public function handle_order_tx_submission($order, $incoming_tx, $user_bip32_key) { $this->CI->load->model('transaction_cache_model'); $currently_unsigned = strlen($order['partially_signed_transaction']) == 0; if ($currently_unsigned) { $start_tx = trim($order['unsigned_transaction']); } else { $start_tx = trim($order['partially_signed_transaction']); } $json = str_replace("'", '', $order['json_inputs']); $decode_current_tx = \BitWasp\BitcoinLib\RawTransaction::decode($start_tx); $decode_incoming_tx = \BitWasp\BitcoinLib\RawTransaction::decode($incoming_tx); // Does incoming tx match expected spend? $check = $this->CI->transaction_cache_model->check_if_expected_spend($decode_incoming_tx['vout'], $order['id']); if ($check !== $order['address']) { return 'Invalid transaction.'; } // General check that signatures match tx $validate = \BitWasp\BitcoinLib\RawTransaction::validate_signed_transaction($incoming_tx, $json); if ($validate == FALSE) { return 'Invalid signature.'; } $decode_redeem_script = \BitWasp\BitcoinLib\RawTransaction::decode_redeem_script($order['redeemScript']); if (!$currently_unsigned and $user_bip32_key['provider'] == 'JS') { // Need to build the sig from this tx into the last. $copy = $decode_incoming_tx; foreach ($copy['vin'] as $i => &$input) { $script = explode(" ", $input['scriptSig']['asm']); $sig1 = \BitWasp\BitcoinLib\RawTransaction::_encode_vint(strlen($script[1]) / 2) . $script[1]; $old_script = explode(" ", $decode_current_tx['vin'][$i]['scriptSig']['asm']); $sig2 = \BitWasp\BitcoinLib\RawTransaction::_encode_vint(strlen($old_script[1]) / 2) . $old_script[1]; $redeem_script = '4c' . \BitWasp\BitcoinLib\RawTransaction::_encode_vint(strlen($order['redeemScript']) / 2) . $order['redeemScript']; $input['scriptSig']['hex'] = '00' . $sig1 . $sig2 . $redeem_script; } $incoming_tx = \BitWasp\BitcoinLib\RawTransaction::encode($copy); // Now need to reorder sigs! $assoc = $this->associate_sigs_with_keys($incoming_tx, $json, $this->CI->bw_config->currencies[0]['crypto_magic_byte']); foreach ($copy['vin'] as $i => &$input) { $input['scriptSig']['hex'] = \BitWasp\BitcoinLib\RawTransaction::_apply_sig_scripthash_multisig($assoc[$i], array('public_keys' => $decode_redeem_script['keys'], 'script' => $order['redeemScript'])); } $incoming_tx = \BitWasp\BitcoinLib\RawTransaction::encode($copy); $decode_incoming_tx = \BitWasp\BitcoinLib\RawTransaction::decode($incoming_tx); } // Compare signatures! $old_sig_map = $this->associate_sigs_with_keys($start_tx, $json, $this->CI->bw_config->currencies[0]['crypto_magic_byte']); // Now check current signatures against users key. submittee must have signed. $key_sig_map = $this->associate_sigs_with_keys($incoming_tx, $json, $this->CI->bw_config->currencies[0]['crypto_magic_byte']); foreach ($key_sig_map as $i => $input_sig_map) { // If the number of sigs hasn't increased, or no sig from the current user exists.. if (count($old_sig_map) > 0 && count($input_sig_map) <= count($old_sig_map[$i]) or !isset($input_sig_map[$user_bip32_key['public_key']])) { return 'Incorrect signature!'; } } // Broadcast tx if fully signed! if (!$currently_unsigned) { $this->CI->transaction_cache_model->to_broadcast($incoming_tx); $this->sendrawtransaction($incoming_tx); } return TRUE; }
public function testTxDecodeNonStandard() { $txId = "e0c354620324422f59c9d62464a62240879cfb63856ebc60601da051522865ed"; $raw = "0000000001e58a2b3e662984ec5a26c9d3bfbeab17c8bcb144d7b841ca2779b43f57424e23010000006b48304502210097166be10ed2660070929b68ea975e60ba4701bd5dbc2eb53290ce9ab6e5f9c502200222033604f63a4a7f97a29cd2039e97b4f9407544ced5ea6ec6371a223f2e98012103619344fbff8e9e203909cf9912c699f9d7ede4f3aa18b7545bfae16dd4c6df2effffffff020000000000000000216a1f4343484e01017b226d223a22746c222c2264223a7b2263223a223f63227d7d4847f50200000000226d784b4e7a3667583675627253457872326b66387556614539396d7a43536d78756900000000"; $tx = RawTransaction::decode($raw); $this->assertEquals($raw, RawTransaction::encode($tx)); $this->assertEquals($txId, RawTransaction::txid_from_raw(RawTransaction::encode($tx))); }
/** * Create Spend Transaction * * This function takes a $from_address, an order address, and a $tx_outs array, * specifying who the transaction should pay. * * Returns TRUE if the transaction is successfully created, and previous details * removed, or else a string containing an error if it fails. * * @param string $from_address * @param array $tx_outs * @param string $script * @return bool|string */ public function create_spend_transaction($from_address, array $tx_outs = array(), $script) { if (count($tx_outs) < 1) { return 'No outputs specified in transaction.'; } $this->load->model('transaction_cache_model'); // Add the inputs at the multisig address. $payments = $this->transaction_cache_model->payments_to_address($from_address); if (count($payments) == 0) { return 'No spendable outputs found for this address'; } $order_id = $payments[0]['order_id']; // Create the transaction inputs $tx_ins = array(); $tx_pkScripts = array(); foreach ($payments as $pmt) { $tx_ins[] = array('txid' => $pmt['tx_id'], 'vout' => $pmt['vout']); $tx_pkScripts[] = array('txid' => $pmt['tx_id'], 'vout' => (int) $pmt['vout'], 'scriptPubKey' => $pmt['pkScript'], 'redeemScript' => $script); } $json = json_encode($tx_pkScripts); $tx_outs = array_map('strval', $tx_outs); $raw_transaction = RawTransaction::create($tx_ins, $tx_outs); if ($raw_transaction == FALSE) { return 'An error occurred creating the transaction!'; } else { // Embed redeem script into all tx's $new_tx = RawTransaction::decode($raw_transaction); foreach ($new_tx['vin'] as &$input_ref) { //$empty_input = $script; $input_ref['scriptSig']['hex'] = $script; } $raw_transaction = RawTransaction::encode($new_tx); $decoded_transaction = RawTransaction::decode($raw_transaction); if ($this->update_order($order_id, array('unsigned_transaction' => $raw_transaction . " ", 'json_inputs' => "'{$json}'", 'partially_signed_transaction' => '', 'partially_signed_time' => '', 'partially_signing_user_id' => ''))) { $this->transaction_cache_model->clear_expected_for_address($from_address); $this->transaction_cache_model->log_transaction($decoded_transaction['vout'], $from_address, $order_id); return TRUE; } return 'An error occured updating the order!'; } }