/** * generate multisig address for given path * * @param $path * @return array * @throws \Exception */ protected function createAddress($path) { $path = BIP32Path::path($path); //build public keys for this path $primaryPubKey = $this->primaryPrivateKey->buildKey($path)->publicKey(); $backupPubKey = $this->backupPrivateKey->buildKey($path->unhardenedPath())->publicKey(); $blocktrailPubKey = $this->getBlocktrailPublicKey($path)->buildKey($path)->publicKey(); //sort the keys $multisigKeys = BlocktrailSDK::sortMultisigKeys([$primaryPubKey, $backupPubKey, $blocktrailPubKey]); //create the multisig address $multiSig = RawTransaction::create_multisig(2, $multisigKeys); return [$multiSig['address'], $multiSig['redeemScript']]; }
/** * @param BIP32Key $key * @param string|BIP32Path $path * @return string[] [address, redeemScript] * @throws \Exception */ protected function getRedeemScriptFromKey(BIP32Key $key, $path) { $path = BIP32Path::path($path)->publicPath(); $blocktrailPublicKey = $this->getBlocktrailPublicKey($path); $multiSig = RawTransaction::create_multisig(2, BlocktrailSDK::sortMultisigKeys([$key->buildKey($path)->publicKey(), $this->backupPublicKey->buildKey($path->unhardenedPath())->publicKey(), $blocktrailPublicKey->buildKey($path)->publicKey()])); return [$multiSig['address'], $multiSig['redeemScript']]; }
public function testP2SHMultisig() { $j = 0; for ($i = 0; $i < 5; $i++) { $n = rand(1, 20); $m = rand(1, $n); $k = []; $pk_list = []; for ($i = 0; $i < $n; $i++) { $k[$i] = BitcoinLib::get_new_key_set(null, (bool) ($j++ % 2)); $pk_list[] = $k[$i]['pubKey']; } $multisig = RawTransaction::create_multisig($m, $pk_list); $real = $this->client->createmultisig($m, $pk_list); $this->assertEquals($real['address'], $multisig['address']); $this->assertEquals($real['redeemScript'], $multisig['redeemScript']); } }
public function testP2SHMultisig2() { $n = 3; $m = 2; $privKeys = ["a56a29f79648d95c5666989c9b2b8d40bfe29c4f65b6fbc3e28ed15f8bc46691", "df3fa8db488c6ab6eb31f6b8979dcffd9a7c334196db88b1705bf8bfada41bb2", "2c44e5a2b83abded4e02aae1c3c02a95bf68a4ca56b5473c7f55b8940a5dcfa6"]; $pubKeys = array_map(function ($privKey) { return BitcoinLib::private_key_to_public_key($privKey, true); }, $privKeys); $pubKeys = RawTransaction::sort_multisig_keys($pubKeys); $multisig = RawTransaction::create_multisig($m, $pubKeys); $this->assertEquals("3BMH67dedFZTbbtMQ3e7nnKEzHfkwB6VpU", $multisig['address']); }
public function js_html($redeem_script, $raw_transaction) { if (strlen($redeem_script) == '0' || strlen($raw_transaction) == '0') { return ''; } $this->CI->load->model('transaction_cache_model'); $decode_rs = \BitWasp\BitcoinLib\RawTransaction::decode_redeem_script($redeem_script); $pubkey_list = ''; foreach ($decode_rs['keys'] as $i => $key) { $pubkey_list .= ' "' . $key . '"' . ($i < 2 ? ',' : '') . "\n"; } $p2sh_info = \BitWasp\BitcoinLib\RawTransaction::create_multisig(2, $decode_rs['keys']); $decode_tx = \BitWasp\BitcoinLib\RawTransaction::decode($raw_transaction); $utxos = ""; foreach ($decode_tx['vin'] as $vin => $input) { $tx_info = $this->CI->transaction_cache_model->get_payment($input['txid']); $utxos .= "\n{\n address: '{$p2sh_info['address']}',\n txid: '{$tx_info['tx_id']}',\n vout: {$tx_info['vout']},\n scriptPubKey: '{$tx_info['pkScript']}',\n confirmations: 10,\n amount: " . $tx_info['value'] . "\n}" . ($vin < count($decode_tx['vin']) - 1 ? ',' : '') . "\n"; //".($current_block-$tx_info['block_height']).", } $outs = ''; foreach ($decode_tx['vout'] as $vout => $output) { $outs .= '{ address: "' . $output['scriptPubKey']['addresses'][0] . '", amount: ' . $output['value'] . "\n}" . ($vout < count($decode_tx['vout']) - 1 ? ',' : ''); } $json = "\nvar pubkeys = [\n{$pubkey_list}\n];\n\nvar opts = {\n nreq: 2,\n pubkeys: pubkeys\n};\n\nvar serialized_pubkeys = [];\nfor (var i=0; i<3; i++) {\n serialized_pubkeys.push(new bitcore.buffertools.Buffer(opts.pubkeys[i],'hex'));\n}\n\nvar script = bitcore.Script.createMultisig(opts.nreq, serialized_pubkeys, {noSorting: true});\nvar hash = bitcore.util.sha256ripe160(script.getBuffer());\n\n\nvar p2shScript = script.serialize().toString('hex');\nvar p2shAddress = new bitcore.Address.fromScript(p2shScript).toString();\nconsole.log('got address: '+p2shAddress);\n\nvar utxos = [\n{$utxos}\n];\n\nvar outs = [\n{$outs}\n];\n\nvar hashMap = {};\nhashMap[p2shAddress] = p2shScript;\n "; return $json; }
<?php use BitWasp\BitcoinLib\BitcoinLib; use BitWasp\BitcoinLib\RawTransaction; require_once __DIR__ . '/../vendor/autoload.php'; $m = 2; $publicKeys = ['0379ddc228d8c44a85ae30c877a6b037ec3d627e0507f223a0412790a83a46cd5f', '024d1cf2ca917f4d679fc02df2a39c0a8110a1b6935b27ae6762a0ceeec7752801', '0258f70f6400aa6f60ff0d21c3aaf1ca236d177877d2b9ad9d2c55280e375ab2d2']; // It's recommended that you sort the public keys before creating multisig // Someday this might be standardized in BIP67; https://github.com/bitcoin/bips/pull/146 // Many other libraries already do this too! RawTransaction::sort_multisig_keys($publicKeys); // Create redeem script $redeemScript = RawTransaction::create_multisig($m, $public_keys); // Display data echo "Public Keys: \n"; foreach ($publicKeys as $i => $publicKey) { echo "{$i} : {$publicKey} \n"; } echo "\n"; echo "Redeem Script: \n"; echo "{$redeemScript['redeem_script']} \n\n"; echo "Address: \n"; echo "{$redeemScript['address']} \n\n";
$wallet[1] = BIP32::master_key('b861e093a58718e145b9791af35fb222'); $wallet[2] = BIP32::master_key('b861e093a58718e145b9791af35fb333'); print_r($wallet); echo "Now we will generate a m/0' extended key. These will yield a private key\n"; $user[0] = BIP32::build_key($wallet[0][0], "3'"); $user[1] = BIP32::build_key($wallet[1][0], "23'"); $user[2] = BIP32::build_key($wallet[2][0], "9'"); print_r($user); // As the previous is a private key, we should convert to the corresponding // public key: M/0' echo "As the previous is a private key, we should convert it to the corresponding\n"; echo "public key: M/0' \n"; $pub[0] = BIP32::extended_private_to_public($user[0]); $pub[1] = BIP32::extended_private_to_public($user[1]); $pub[2] = BIP32::extended_private_to_public($user[2]); print_r($pub); echo "This is the key you will ask your users for. For repeated transactions\n"; echo "BIP32 allows you to deterministically generate public keys, meaning less\n"; echo "effort for everyone involved\n\n"; echo "Now we can generate many multisignature addresses from what we have here: \n"; for ($i = 0; $i < 3; $i++) { $bip32key[0] = BIP32::build_key($pub[0], "0/{$i}"); $bip32key[1] = BIP32::build_key($pub[1], "0/{$i}"); $bip32key[2] = BIP32::build_key($pub[2], "0/{$i}"); print_r($bip32key); $pubkey[0] = BIP32::extract_public_key($bip32key[0]); $pubkey[1] = BIP32::extract_public_key($bip32key[1]); $pubkey[2] = BIP32::extract_public_key($bip32key[2]); print_r($pubkey); print_r(RawTransaction::create_multisig(2, $pubkey)); }
/** * Vendor Accept Order * * Pass info generated at either buyer_confirm or vendor_accept page * via $info, and then create the order/address details. * * $info = array('vendor_public_keys' => array, * 'order_type' => array, * 'order' => array * 'initiating_user' => array * 'update_fields' => array * * ); * @param array $info * @return string/TRUE */ public function vendor_accept_order($info) { $this->load->model('bitcoin_model'); $this->load->model('bip32_model'); $this->load->model('accounts_model'); if ($info['initiating_user'] == 'buyer') { // Buyer public key is in $info.buyerpubkey array, also ID in update fields. $buyer_public_key = $info['buyer_public_key']; $this->update_order($info['order']['id'], $info['update_fields']); foreach ($info['update_fields'] as $key => $field) { $info['order'][$key] = $field; } $info['update_fields'] = array(); } else { $buyer_public_key = $info['order']['public_keys']['buyer']; } // Add vendors public key no matter what we're doing! $vendor_public_key = $this->bip32_model->add_child_key(array('user_id' => $info['order']['vendor']['id'], 'user_role' => 'Vendor', 'order_id' => $info['order']['id'], 'order_hash' => '', 'parent_extended_public_key' => $info['vendor_public_key']['parent_extended_public_key'], 'provider' => $info['vendor_public_key']['provider'], 'extended_public_key' => $info['vendor_public_key']['extended_public_key'], 'public_key' => $info['vendor_public_key']['public_key'], 'key_index' => $info['vendor_public_key']['key_index'])); // Get vendors public key, stored by that function. $admin_public_key = $this->bip32_model->get_next_admin_child(); if ($admin_public_key == FALSE) { return 'An error occured, which prevented your order being created. Please notify an administrator.'; } else { $admin_public_key = $this->bip32_model->add_child_key(array('user_id' => '0', 'user_role' => 'Admin', 'order_id' => $info['order']['id'], 'order_hash' => '', 'parent_extended_public_key' => $admin_public_key['parent_extended_public_key'], 'provider' => 'Manual', 'extended_public_key' => $admin_public_key['extended_public_key'], 'public_key' => $admin_public_key['public_key'], 'key_index' => $admin_public_key['key_index'])); $public_keys = array($buyer_public_key['public_key'], $vendor_public_key['public_key'], $admin_public_key['public_key']); $sorted_keys = RawTransaction::sort_multisig_keys($public_keys); $multisig_details = RawTransaction::create_multisig('2', $sorted_keys); // If no errors, we're good to create the order! if ($multisig_details !== FALSE) { $this->bitcoin_model->log_key_usage('order', $this->bw_config->bip32_mpk, $admin_public_key['key_index'], $admin_public_key['public_key'], $info['order']['id']); $info['update_fields']['vendor_public_key'] = $vendor_public_key['id']; $info['update_fields']['admin_public_key'] = $admin_public_key['id']; $info['update_fields']['buyer_public_key'] = $buyer_public_key['id']; $info['update_fields']['address'] = $multisig_details['address']; $info['update_fields']['redeemScript'] = $multisig_details['redeemScript']; $info['update_fields']['selected_payment_type_time'] = time(); $info['update_fields']['progress'] = 2; $info['update_fields']['time'] = time(); if ($info['order_type'] == 'escrow') { $info['update_fields']['vendor_selected_escrow'] = '1'; $info['update_fields']['extra_fees'] = ($info['order']['price'] + $info['order']['shipping_costs']) / 100 * $this->bw_config->escrow_rate; } else { $info['update_fields']['vendor_selected_escrow'] = '0'; $info['update_fields']['vendor_selected_upfront'] = '1'; $info['update_fields']['extra_fees'] = ($info['order']['price'] + $info['order']['shipping_costs']) / 100 * $this->bw_config->upfront_rate; } if ($this->update_order($info['order']['id'], $info['update_fields']) == TRUE) { $this->bitcoin_model->add_watch_address($multisig_details['address'], 'order'); $subject = 'Confirmed Order #' . $info['order']['id']; $message = "Your order with {$info['order']['vendor']['user_name']} has been confirmed.\n" . ($info['order_type'] == 'escrow' ? "Escrow payment was chosen. Once you pay to the address, the vendor will ship the goods. You can raise a dispute if you have any issues." : "You must make payment up-front to complete this order. Once the full amount is sent to the address, you must sign a transaction paying the vendor."); $this->order_model->send_order_message($info['order']['id'], $info['order']['buyer']['user_name'], $subject, $message); $subject = 'New Order #' . $info['order']['id']; $message = "A new order from {$info['order']['buyer']['user_name']} has been confirmed.\n" . ($info['order_type'] == 'escrow' ? "Escrow was chosen for this order. Once paid, you will be asked to sign the transaction to indicate the goods have been dispatched." : "Up-front payment was chosen for this order based on your settings for one of the items. The buyer will be asked to sign the transaction paying you immediately after payment, which you can sign and broadcast to mark the order as dispatched."); $this->order_model->send_order_message($info['order']['id'], $info['order']['vendor']['user_name'], $subject, $message); $msg = $info['initiating_user'] == 'buyer' ? 'This order has been automatically accepted, visit the orders page to see the payment address!' : 'You have accepted this order! Visit the orders page to see the bitcoin address!'; $this->current_user->set_return_message($msg, 'success'); return TRUE; } else { return 'There was an error creating your order.'; } } else { return 'Unable to create address.'; } } }