public function signMessage($address) { $input = Input::all(); $output = array(); if (!isset($input['message']) or trim($input['message']) == '') { $output['error'] = 'Message required'; $output['result'] = false; return new Response($output, 400); } $get = PaymentAddress::where('uuid', $address)->orWhere('address', $address)->first(); $found = false; if (!$get) { $output['error'] = 'Bitcoin address does not belong to server'; $output['result'] = false; return new Response($output, 400); } $address = $get->address; $address_generator = app('Tokenly\\BitcoinAddressLib\\BitcoinAddressGenerator'); $lib = new BitcoinLib(); $priv_key = $address_generator->WIFPrivateKey($get->private_key_token); $priv_key = BitcoinLib::WIF_to_private_key($priv_key); $sign = $priv_key; try { $sign = $lib->signMessage($input['message'], $priv_key); } catch (Exception $e) { $sign = false; } if (!$sign) { $output['error'] = 'Error signing message'; $output['result'] = false; return new Response($output, 500); } $output['result'] = $sign; return new Response($output); }
/** * @param string $address * @param int $value * @return $this * @throws \Exception */ public function addRecipient($address, $value) { if (!BitcoinLib::validate_address($address)) { throw new \Exception("Invalid address [{$address}]"); } // using this 'dirty' way of checking for a float since there's no other reliable way in PHP if (!is_int($value)) { throw new \Exception("Values should be in Satoshis (int)"); } if ($value <= Blocktrail::DUST) { throw new \Exception("Values should be more than dust (" . Blocktrail::DUST . ")"); } $this->addOutput(['address' => $address, 'value' => $value]); return $this; }
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']); } }
/** * @param $primaryMnemonic * @param $primaryPassphrase * @param $backupMnemonic * @param array $blocktrailPublicKeys * @param BlockchainDataServiceInterface $bitcoinClient * @param string $network * @param bool $testnet * @throws \Exception */ public function __construct($primaryMnemonic, $primaryPassphrase, $backupMnemonic, array $blocktrailPublicKeys, BlockchainDataServiceInterface $bitcoinClient, $network = 'btc', $testnet = false) { // normalize network and set bitcoinlib to the right magic-bytes list($this->network, $this->testnet) = $this->normalizeNetwork($network, $testnet); BitcoinLib::setMagicByteDefaults($this->network . ($this->testnet ? '-testnet' : '')); //create BIP32 keys for the Blocktrail public keys foreach ($blocktrailPublicKeys as $blocktrailKey) { $this->blocktrailPublicKeys[$blocktrailKey['keyIndex']] = BIP32Key::create($blocktrailKey['pubkey'], $blocktrailKey['path']); } //set the unspent output finder, using the given bitcoin data service provider $this->bitcoinClient = $bitcoinClient; $this->utxoFinder = new UnspentOutputFinder($this->bitcoinClient); // cleanup copy paste errors from mnemonics $primaryMnemonic = str_replace(" ", " ", str_replace("\r\n", " ", str_replace("\n", " ", trim($primaryMnemonic)))); $backupMnemonic = str_replace(" ", " ", str_replace("\r\n", " ", str_replace("\n", " ", trim($backupMnemonic)))); // convert the primary and backup mnemonics to seeds (using BIP39), then create private keys (using BIP32) $primarySeed = BIP39::mnemonicToSeedHex($primaryMnemonic, $primaryPassphrase); $backupSeed = BIP39::mnemonicToSeedHex($backupMnemonic, ""); $this->primaryPrivateKey = BIP32Key::create(BIP32::master_key($primarySeed, $this->network, $this->testnet)); $this->backupPrivateKey = BIP32Key::create(BIP32::master_key($backupSeed, $this->network, $this->testnet)); }
public function testMnemonicDecode() { $mnemonic = trim('teach start paradise collect blade chill gay childhood creek picture creator branch'); $known_seed = 'dcb85458ec2fcaaac54b71fba90bd4a5'; $known_secexp = '74b1f6c0caae485b4aeb2f26bab3cabdec4f0b432751bd454fe11b2d2907cbda'; $known_mpk = '819519e966729f31e1855eb75133d9e7f0c31abaadd8f184870d62771c62c2e759406ace1dee933095d15e4c719617e252f32dc0465393055f867aee9357cd52'; $known_addresses = ["", "", "", "", ""]; $seed = Electrum::decode_mnemonic($mnemonic); $this->assertEquals($seed, $known_seed); $mpk = Electrum::generate_mpk($seed); $this->assertEquals($mpk, $known_mpk); $secexp = Electrum::stretch_seed($seed); $secexp = $secexp['seed']; $this->assertEquals($secexp, $known_secexp); $count_known_addresses = count($known_addresses); for ($i = 0; $i < $count_known_addresses; $i++) { $privkey = Electrum::generate_private_key($secexp, $i, 0); $address_private_deriv = BitcoinLib::private_key_to_address($privkey, $this->magic_byte); $public_deriv = Electrum::public_key_from_mpk($mpk, $i); $address_private_deriv = BitcoinLib::public_key_to_address($public_deriv, $this->magic_byte); } }
/** * Details * Buyer URI: purchases/details/<order_id> * Vendor URI: orders/details/<order_id> * Admin URI: admin/orders/<order_id> * * Order details page. Shows buyer/vendor/admin the details of the * the order. * * @param int $order_id */ public function details($order_id) { if (!(is_numeric($order_id) && $order_id >= 0)) { redirect(''); } $data['order'] = $this->order_model->get($order_id); // no restriction on buyer/vendor if ($data['order'] == FALSE) { redirect(''); } // Work out if the user is allowed to view this order. if (!$this->current_user->user_role == 'Admin' && !($this->current_user->user_id == $data['order']['buyer']['id']) && !($this->current_user->user_id == $data['order']['vendor']['id'])) { redirect(''); } // Only allow access when the order is confirmed by the buyer. if ($data['order']['progress'] == '0') { redirect(''); } if ($this->current_user->user_role == 'Buyer') { $data['action_page'] = 'purchases/details/' . $order_id; $data['cancel_page'] = 'purchases'; } else { if ($this->current_user->user_role == 'Vendor') { $data['action_page'] = 'orders/details/' . $order_id; $data['cancel_page'] = 'orders'; } else { if ($this->current_user->user_role == 'Admin') { $data['action_page'] = 'admin/order/' . $order_id; $data['cancel_page'] = 'admin/orders'; } } } $this->load->model('transaction_cache_model'); $this->load->model('bip32_model'); $this->load->model('review_model'); $child_key_id = $data['order'][strtolower($this->current_user->user_role) . '_public_key']; $data['my_multisig_key'] = $this->bip32_model->get_child_key($child_key_id); $data['wallet_salt'] = $this->users_model->wallet_salt($this->current_user->user_id); $this->load->library('bw_bitcoin'); $html = $this->bw_bitcoin->js_html($data['order']['redeemScript'], $data['order']['unsigned_transaction']); if (is_array($data['my_multisig_key'])) { $data['signing_info'] = array('parent_extended_public_key' => $data['my_multisig_key']['parent_extended_public_key'], 'extended_public_key' => $data['my_multisig_key']['extended_public_key'], 'bip32_provider' => $data['my_multisig_key']['provider'], 'full_key_index' => "m/0'/0/" . $data['my_multisig_key']['key_index'], 'key_index' => "m/0'/0/" . $data['my_multisig_key']['key_index'], 'public_key' => $data['my_multisig_key']['public_key'], 'partially_signed_transaction' => $data['order']['partially_signed_transaction'], 'unsigned_transaction' => $data['order']['unsigned_transaction'], 'wallet_salt' => $data['wallet_salt'], 'crafted_html' => $html); $ident = strtolower($data['my_multisig_key']['provider']); $this->_partial("sign_form_output", "orders/details_form_" . $ident); if ($ident == 'js') { $data['header_meta'] = $this->load->view('orders/add_signature_js', $data['signing_info'], TRUE); $this->load->model('users_model'); } elseif ($ident == 'onchain') { $this->load->library('onchainlib'); $tx_crc = substr(hash('sha256', $data['order']['partially_signed_transaction'] == '' ? $data['order']['unsigned_transaction'] : $data['order']['partially_signed_transaction']), 0, 8); $data['onchain_sign'] = $this->onchainlib->sign_request($data['order']['id'], $tx_crc); } } // Only allow a vendor to refund, if the progress is either 3 or 4 (not yet dispatched) $data['can_refund'] = ($this->current_user->user_role == 'Vendor' and in_array($data['order']['progress'], array('3', '4'))); $data['display_sign_form'] = (bool) (($data['order']['vendor_selected_upfront'] == '1' or $data['order']['vendor_selected_escrow'] == '0') and $data['order']['progress'] == '3' && $this->current_user->user_role == 'Buyer' or $data['order']['progress'] == '4' && $this->current_user->user_role == 'Vendor' or ($data['order']['vendor_selected_escrow'] == '1' and $data['order']['vendor_selected_upfront'] == '0' and $data['order']['progress'] == '4' && $this->current_user->user_role == 'Vendor' or $data['order']['progress'] == '5' && $this->current_user->user_role == 'Buyer') or ($data['order']['progress'] == '6' and $data['order']['partially_signed_transaction'] == '' or $data['order']['partially_signed_transaction'] !== '' and $data['order']['partially_signing_user_id'] !== $this->current_user->user_id) or ($data['order']['progress'] == '8' and $data['order']['partially_signed_transaction'] == '' or $data['order']['partially_signed_transaction'] !== '' and $data['order']['partially_signing_user_id'] !== $this->current_user->user_id)); $data['display_sign_msg'] = $data['order']['partially_signed_transaction'] == '' ? 'Please add first signature.' : 'Please sign to complete transaction.'; // Only allow access to the form handling script if the form is allowed to be displayed. if ($data['display_sign_form'] == TRUE) { // JS if ($this->input->post('submit_js_signed_transaction') == 'Submit Transaction') { if ($this->form_validation->run('submit_js_signed_transaction') == TRUE) { $check = $this->bw_bitcoin->handle_order_tx_submission($data['order'], $this->input->post('js_transaction'), $data['my_multisig_key']); if (is_string($check)) { $data['invalid_transaction_error'] = $check; } else { if ($check == TRUE) { if (strlen($data['order']['partially_signed_transaction']) > 0) { $this->current_user->set_return_message('Transaction has been submitted, and will be processed shortly.', 'success'); redirect($data['action_page']); } else { if ($data['order']['progress'] == '3') { // Buyer must sign early before vendor dispatches. $update = array('partially_signed_transaction' => $this->input->post('js_transaction'), 'partially_signing_user_id' => $this->current_user->user_id, 'partially_signed_time' => time()); $this->order_model->progress_order($order_id, '3', '4', $update); } else { if ($data['order']['progress'] == '4') { // Vendor indicates they have dispatched. $update = array('partially_signed_transaction' => $this->input->post('js_transaction'), 'partially_signing_user_id' => $this->current_user->user_id, 'partially_signed_time' => time(), 'dispatched_time' => time(), 'dispatched' => '1'); $this->order_model->progress_order($order_id, '4', '5', $update); } else { if ($data['order']['progress'] == '6') { $update = array('partially_signed_transaction' => $this->input->post('js_transaction'), 'partially_signing_user_id' => $this->current_user->user_id, 'partially_signed_time' => time()); $this->order_model->update_order($order_id, $update); } else { if ($data['order']['progress'] == '8') { $update = array('partially_signed_transaction' => $this->input->post('js_transaction'), 'partially_signing_user_id' => $this->current_user->user_id, 'partially_signed_time' => time()); $this->order_model->update_order($order_id, $update); } } } } $this->current_user->set_return_message('Your partially signed transaction has been saved!', 'success'); redirect($data['action_page']); } } } } } // Manual if ($this->input->post('submit_signed_transaction') == 'Submit Transaction') { if ($this->form_validation->run('input_transaction') == TRUE) { $validate = $this->bw_bitcoin->handle_order_tx_submission($data['order'], $this->input->post('partially_signed_transaction'), $data['my_multisig_key']); if (is_string($validate)) { $data['invalid_transaction_error'] = $validate; } else { if ($validate == TRUE) { if (strlen($data['order']['partially_signed_transaction']) > 0) { $this->current_user->set_return_message('Transaction has been submitted, and will be processed shortly.', 'success'); redirect($data['action_page']); } else { if ($data['order']['progress'] == '3') { // Buyer must sign early before vendor dispatches. $update = array('partially_signed_transaction' => $this->input->post('partially_signed_transaction'), 'partially_signing_user_id' => $this->current_user->user_id, 'partially_signed_time' => time()); $this->order_model->progress_order($order_id, '3', '4', $update); } else { if ($data['order']['progress'] == '4') { // Vendor indicates they have dispatched. $update = array('partially_signed_transaction' => $this->input->post('partially_signed_transaction'), 'partially_signing_user_id' => $this->current_user->user_id, 'partially_signed_time' => time(), 'dispatched_time' => time(), 'dispatched' => '1'); $this->order_model->progress_order($order_id, '4', '5', $update); } else { if ($data['order']['progress'] == '6') { $update = array('partially_signed_transaction' => $this->input->post('partially_signed_transaction'), 'partially_signing_user_id' => $this->current_user->user_id, 'partially_signed_time' => time()); $this->order_model->update_order($order_id, $update); } else { if ($data['order']['progress'] == '8') { $update = array('partially_signed_transaction' => $this->input->post('partially_signed_transaction'), 'partially_signing_user_id' => $this->current_user->user_id, 'partially_signed_time' => time()); $this->order_model->update_order($order_id, $update); } } } } $this->current_user->set_return_message('Your partially signed transaction has been saved!', 'success'); redirect($data['action_page']); } } } } } } $data['redeem_script'] = RawTransaction::decode_redeem_script($data['order']['redeemScript']); $data['addrs'] = array($data['order']['buyer_payout'] => 'buyer', $data['order']['vendor_payout'] => 'vendor'); if (isset($data['order']['public_keys']['admin'])) { $data['addrs'][BitcoinLib::public_key_to_address($data['order']['public_keys']['admin']['public_key'], $this->bw_config->currencies[0]['crypto_magic_byte'])] = 'admin'; } if (strlen($data['order']['partially_signed_transaction']) > 0) { $data['raw_tx'] = RawTransaction::decode($data['order']['partially_signed_transaction']); $data['signer'] = $this->accounts_model->get(array('id' => $data['order']['partially_signing_user_id'])); } else { if (strlen($data['order']['unsigned_transaction']) > 0) { $data['raw_tx'] = RawTransaction::decode($data['order']['unsigned_transaction']); } } $checkStrangeAddress = function () use($data) { $tx_addrs = array(); foreach ($data['raw_tx']['vout'] as $vout) { $tx_addrs[] = $vout['scriptPubKey']['addresses'][0]; } return count($tx_addrs) != count(array_intersect($tx_addrs, array_keys($data['addrs']))); }; $data['strange_address'] = isset($data['raw_tx']) ? $checkStrangeAddress() : FALSE; $data['fees']['shipping_cost'] = $data['order']['shipping_costs']; $data['fees']['fee'] = $data['order']['fees']; $data['fees']['escrow_fees'] = $data['order']['extra_fees']; $data['fees']['total'] = $data['order']['shipping_costs'] + $data['order']['fees']; if ($this->current_user->user_role == 'Buyer' && $data['order']['paid_time'] == '') { $this->load->library('ciqrcode'); $data['payment_url'] = "bitcoin:{$data['order']['address']}?amount={$data['order']['order_price']}&message=Order+{$data['order']['id']}&label=Order+{$data['order']['id']}"; $data['qr'] = $this->ciqrcode->generate_base64(array('data' => $data['payment_url'])); } $data['page'] = 'orders/details'; $data['title'] = 'Order Details: #' . $data['order']['id']; $this->_render($data['page'], $data); }
public function get_user_key_usage($user_id) { $query = $this->db->get_where('bip32_user_keys', array('user_id' => $user_id))->result_array(); if (count($query) > 0) { foreach ($query as &$row) { $row['address'] = \BitWasp\BitcoinLib\BitcoinLib::public_key_to_address($row['public_key'], $this->bw_config->currencies[0]['crypto_magic_byte']); } } return $query; }
/** * Key To Address * * This function accepts a bip32 extended key, and converts it to a * bitcoin address. * * @param string $extended_key * @return string */ public static function key_to_address($extended_key) { $import = self::import($extended_key); if ($import['type'] == 'public') { $public = $import['key']; } else { $public = BitcoinLib::private_key_to_public_key($import['key'], true); } // Convert the public key to the address. return BitcoinLib::public_key_to_address($public, $import['version']); }
public function testSatoshiConversion() { $toSatoshi = [["0.00000001", "1", 1], [1.0E-8, "1", 1], ["0.29560000", "29560000", 29560000], [0.2956, "29560000", 29560000], ["1.0000009", "100000090", 100000090], [1.0000009, "100000090", 100000090], ["1.00000009", "100000009", 100000009], [1.00000009, "100000009", 100000009], ["21000000.00000001", "2100000000000001", 2100000000000001], [21000000.00000001, "2100000000000001", 2100000000000001], ["21000000.0000009", "2100000000000090", 2100000000000090], [21000000.0000009, "2100000000000090", 2100000000000090], ["21000000.00000009", "2100000000000009", 2100000000000009], [21000000.00000009, "2100000000000009", 2100000000000009], ["210000000.00000009", "21000000000000009", 21000000000000009], [210000000.0000001, "21000000000000009", 21000000000000009]]; $toBTC = [["1", "0.00000001"], [1, "0.00000001"], ["29560000", "0.29560000"], [29560000, "0.29560000"], ["100000090", "1.00000090"], [100000090, "1.00000090"], ["100000009", "1.00000009"], [100000009, "1.00000009"], ["2100000000000001", "21000000.00000001"], [2100000000000001, "21000000.00000001"], ["2100000000000090", "21000000.00000090"], [2100000000000090, "21000000.00000090"], ["2100000000000009", "21000000.00000009"], [2100000000000009, "21000000.00000009"], ["21000000000000009", "210000000.00000009"], [21000000000000009, "210000000.00000009"], ["210000000000000009", "2100000000.00000009"], [210000000000000009, "2100000000.00000009"], ["2100000000000000009", "21000000000.00000009"], [2100000000000000009, "21000000000.00000009"]]; foreach ($toSatoshi as $i => $test) { $btc = $test[0]; $satoshiString = $test[1]; $satoshiInt = $test[2]; $string = BitcoinLib::toSatoshiString($btc); $this->assertEquals($satoshiString, $string, "[{$i}] {$btc} => {$satoshiString} =? {$string}"); $this->assertTrue($satoshiString === $string, "[{$i}] {$btc} => {$satoshiString} ==? {$string}"); $int = BitcoinLib::toSatoshi($btc); $this->assertEquals($satoshiInt, $int, "[{$i}] {$btc} => {$satoshiInt} =? {$int}"); $this->assertTrue($satoshiInt === $int, "[{$i}] {$btc} => {$satoshiInt} ==? {$int}"); } foreach ($toBTC as $i => $test) { $satoshi = $test[0]; $btc = $test[1]; $this->assertEquals($btc, BitcoinLib::toBTC($satoshi), "[{$i}] {$satoshi} => {$btc}"); $this->assertTrue($btc === BitcoinLib::toBTC($satoshi), "[{$i}] {$satoshi} => {$btc}"); } }
/** * this test requires / asumes that the test wallet it uses contains a balance * * we keep the wallet topped off with some coins, * but if some funny guy ever empties it or if you use your own API key to run the test then it needs to be topped off again * * @throws \Exception */ public function testWalletTransactionWithoutMnemonics() { $client = $this->setupBlocktrailSDK(); $primaryPrivateKey = BIP32::master_key(BIP39::mnemonicToSeedHex("give pause forget seed dance crawl situate hole keen", "password"), 'bitcoin', true); $wallet = $client->initWallet(["identifier" => "unittest-transaction", "primary_private_key" => $primaryPrivateKey, "primary_mnemonic" => false]); $this->assertEquals("unittest-transaction", $wallet->getIdentifier()); $this->assertEquals("M/9999'", $wallet->getBlocktrailPublicKeys()[9999][1]); $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKeys()[9999][0]); $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("m/9999'")->key()); $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("M/9999'")->key()); list($confirmed, $unconfirmed) = $wallet->getBalance(); $this->assertGreaterThan(0, $confirmed + $unconfirmed, "positive unconfirmed balance"); $this->assertGreaterThan(0, $confirmed, "positive confirmed balance"); list($path, $address) = $wallet->getNewAddressPair(); $this->assertTrue(strpos($path, "M/9999'/0/") === 0); $this->assertTrue(BitcoinLib::validate_address($address, false, null)); $value = BlocktrailSDK::toSatoshi(0.0002); $txHash = $wallet->pay([$address => $value]); $this->assertTrue(!!$txHash); sleep(1); // sleep to wait for the TX to be processed try { $tx = $client->transaction($txHash); } catch (ObjectNotFound $e) { $this->fail("404 for tx[{$txHash}] [" . gmdate('Y-m-d H:i:s') . "]"); } $this->assertTrue(!!$tx, "check for tx[{$txHash}] [" . gmdate('Y-m-d H:i:s') . "]"); $this->assertEquals($txHash, $tx['hash']); $this->assertTrue(count($tx['outputs']) <= 2); $this->assertTrue(in_array($value, array_column($tx['outputs'], 'value'))); }
$line = trim(fgets(STDIN)); $decode_redeem_script = RawTransaction::decode_redeem_script($line); if ($decode_redeem_script == FALSE) { echo "[ERROR]- Not a valid script!\n"; unset($decode_redeem_script); } else { $redeem_script = $line; echo "Learned about {$decode_redeem_script['m']} of {$decode_redeem_script['n']} address: " . BitcoinLib::public_key_to_address($redeem_script, '05') . "\n"; } } echo "Enter WIF encoded private keys: \n (1): "; $private_keys = array(); while ("\n" != ($line = fgets(STDIN))) { $line = trim($line); $t = BitcoinLib::validate_WIF($line, '80'); var_dump($t); if (BitcoinLib::validate_WIF($line, '80') == TRUE) { $private_keys[] = $line; } else { echo "Not a valid private key.\n"; } echo " (" . (count($private_keys) + 1) . "): "; } // Initialize wallet $wallet = array(); RawTransaction::private_keys_to_wallet($wallet, $private_keys, '00'); RawTransaction::redeem_scripts_to_wallet($wallet, array($redeem_script), '05'); $raw_transaction = '01000000018240b84b90a3ae326e13219dc8a2781661fa28e23129b26fea848dd8e01a0c520000000000ffffffff02d8270000000000001976a914b7119dfb9b5c8aa7157fec48fbe640ea347dc92b88ac584d0000000000001976a914592fb6dc8cf6cd561ec86fd5fbc2a140e5ac7bc988ac00000000'; $json = '[{"txid":"520c1ae0d88d84ea6fb22931e228fa611678a2c89d21136e32aea3904bb84082","vout":0,"scriptPubKey":"a914fb56f0d4845487cc9da00c3e91e63503245f151787","redeemScript":"52210385fae44cb9f0cf858e0d404baecf78d026fae4cc9dd4343b8562059473c2af7b2102f34a1b64155db258d3a910625bd80fae6adf67d7e5b5f3de03265a4208b552d841040fa9a86f3237237423dd8331dc481c0a949fe11594c5dfa0b54bdc105daa319f9de6547d97c22296d4211073e7cffa71c8d6cd4da639607ca64fca2705e562a353ae"}]'; $sign = RawTransaction::sign($wallet, $raw_transaction, $json); print_r($sign);
<?php use BitWasp\BitcoinLib\BitcoinLib; require_once __DIR__ . '/../vendor/autoload.php'; $usage = "Usage: php {$argv[0]} <magic byte>\n\n"; $usage .= "Some sample bytes are on this list, but you can chose any 2 character byte.\n"; $usage .= "Bitcoin: 00\t\tTestnet: 6f \n"; $usage .= "Litecoin: 48 \n"; $usage .= "Namecoin: 52 \n"; $usage .= "Auroracoin: 17 \n"; if (count($argv) !== 2) { die($usage); } $magic_byte = $argv[1]; echo "Generated keypair: (this will not be saved, do not lose this data!)\n"; $keypair = BitcoinLib::get_new_key_set($magic_byte); echo "Key pair: \n"; print_r($keypair); echo "\n"; echo "\n";
<?php use BitWasp\BitcoinLib\BitcoinLib; require_once __DIR__ . '/../vendor/autoload.php'; $magic_byte = '00'; $keypair = BitcoinLib::get_new_key_set($magic_byte); echo "Key pair: \n"; print_r($keypair); echo "\n"; $compress = BitcoinLib::compress_public_key($keypair['pubKey']); echo "Compressed public key: {$compress} \n"; $decompress = BitcoinLib::decompress_public_key($compress); echo "Decompressed key info: \n"; print_r($decompress); echo "\n"; $address = BitcoinLib::public_key_to_address($compress, $magic_byte); echo "decoding {$address}\n"; echo BitcoinLib::base58_decode($address); echo "\n\n"; $sc = '5357'; $ad = BitcoinLib::public_key_to_address($sc, '05'); echo $ad . "\n";
/** * Log Key Usage * * @param string $usage * @param string $mpk * @param int $iteration * @param string $public_key * @param int $order_id * @param string $user_hash * @return boolean */ public function log_key_usage($usage, $mpk, $iteration, $public_key, $order_id = FALSE, $user_hash = FALSE) { if (!in_array($usage, array('fees', 'order'))) { return FALSE; } if ($usage == 'order' && $order_id == FALSE) { return FALSE; } if ($usage == 'fees' && $user_hash == FALSE) { return FALSE; } $coin = $this->bw_config->currencies[0]; $address = BitcoinLib::public_key_to_address($public_key, $coin['crypto_magic_byte']); $order_id = $usage == 'order' ? $order_id : ''; $user_hash = $usage == 'fees' ? $user_hash : ''; $log = array('usage' => $usage, 'mpk' => $mpk, 'iteration' => $iteration, 'public_key' => $public_key, 'address' => $address, 'order_id' => $order_id, 'fees_user_hash' => $user_hash); return $this->db->insert('key_usage', $log) == TRUE ? TRUE : FALSE; }
public function setup() { parent::setup(); // ensure we're set to bitcoin-testnet and not bitcoin BitcoinLib::setMagicByteDefaults('bitcoin-testnet'); }
/** * create checksum to verify ownership of the master primary key * * @return string[] [address, signature] */ protected function createChecksumVerificationSignature() { $import = BIP32::import($this->primaryPrivateKey->key()); $public = $this->primaryPrivateKey->publicKey(); $address = BitcoinLib::public_key_to_address($public, $import['version']); return [$address, BitcoinLib::signMessage($address, $import)]; }
public function a() { print_r(BitcoinLib::get_new_key_set('00')); }
/** * Redeem Scripts To Wallet * * This function extends on whatever data is in the $wallet array, by * adding script hash addresses to the wallet, and linking keys in the * multisignature address with keys in the wallet. * Adds each redeemScript to the referenced $wallet. * * @param array $wallet * @param array $redeem_scripts * @param null $magic_p2sh_byte */ public static function redeem_scripts_to_wallet(&$wallet, array $redeem_scripts = array(), $magic_p2sh_byte = null) { $magic_p2sh_byte = BitcoinLib::magicP2SHByte($magic_p2sh_byte); if (count($redeem_scripts) > 0) { foreach ($redeem_scripts as $script) { $decode = self::decode_redeem_script($script); if ($decode == false) { continue; } $scripthash = BitcoinLib::hash160($script); $keys = array(); foreach ($decode['keys'] as $key) { $keyhash = BitcoinLib::hash160($key); if (isset($wallet[$keyhash])) { $keys[] = $wallet[$keyhash]; } } $wallet[$scripthash] = array('type' => 'scripthash', 'script' => $script, 'required_signature_count' => $decode['m'], 'address' => BitcoinLib::hash160_to_address($scripthash, $magic_p2sh_byte), 'public_keys' => $decode['keys'], 'keys' => $keys); } } }
public function check_bitcoin_address($str) { return \BitWasp\BitcoinLib\BitcoinLib::validate_address($str, '05'); }
/** * Dispute * * This controller shows either the disputes list (if $order_id is unset) * or a specified disputed order (set by $order_id). * * @param int $order_id */ public function dispute($order_id = NULL) { $this->load->library('form_validation'); $this->load->model('order_model'); $this->load->model('disputes_model'); // If no order is specified, load the list of disputes. if ($order_id == NULL) { $data['page'] = 'admin/disputes_list'; $data['title'] = 'Active Disputes'; $data['disputes'] = $this->disputes_model->disputes_list(); } else { $data['dispute'] = $this->disputes_model->get_by_order_id($order_id); // If the dispute cannot be found, redirect to the dispute list. if ($data['dispute'] == FALSE) { redirect('admin/disputes'); } $data['page'] = 'admin/dispute'; $data['title'] = "Disputed Order #{$order_id}"; // Load the order information. $data['current_order'] = $this->order_model->get($order_id); // Work out whether the vendor or buyer is disputing. $data['disputing_user'] = $data['dispute']['disputing_user_id'] == $data['current_order']['buyer']['id'] ? $data['current_order']['buyer'] : $data['current_order']['vendor']; $data['other_user'] = $data['dispute']['other_user_id'] == $data['current_order']['buyer']['id'] ? $data['current_order']['buyer'] : $data['current_order']['vendor']; // If the message is updated: if ($this->input->post('post_dispute_message') == 'Post Message') { if ($this->form_validation->run('add_dispute_update') == TRUE) { // Update the dispute record. $update = array('posting_user_id' => $this->current_user->user_id, 'order_id' => $order_id, 'dispute_id' => $data['dispute']['id'], 'message' => $this->input->post('update_message')); if ($this->disputes_model->post_dispute_update($update) == TRUE) { redirect('admin/dispute/' . $order_id); } } } // Resolution: $data['transaction_fee'] = 0.0001; $data['admin_fee'] = $data['current_order']['fees'] + $data['current_order']['extra_fees'] - $data['transaction_fee']; $data['user_funds'] = (double) ($data['current_order']['order_price'] - $data['admin_fee'] - $data['transaction_fee']); if ($this->input->post('resolve_dispute') !== null) { if ($this->form_validation->run('admin_resolve_dispute') == TRUE) { if ($this->input->post('resolve_dispute_id') == $data['current_order']['id']) { if ($this->input->post('relinquish_fee') == '1') { $data['admin_fee'] = 0; $data['user_funds'] = (double) ($data['current_order']['order_price'] - $data['admin_fee'] - $data['transaction_fee']); } if ($data['current_order']['vendor_selected_escrow'] == '1') { $pay_buyer_amount = $this->input->post('pay_buyer'); $pay_vendor_amount = $this->input->post('pay_vendor'); $sum = $pay_buyer_amount + $pay_vendor_amount; $epsilon = 1.0E-8; // Must total user funds available if (abs($sum - $data['user_funds']) < $epsilon) { $tx_outs = array(); // Add outputs for the sites fee, buyer, and vendor. if ($data['admin_fee'] > 0) { $admin_address = BitcoinLib::public_key_to_address($data['current_order']['public_keys']['admin']['public_key'], $this->bw_config->currencies[0]['crypto_magic_byte']); $tx_outs[$admin_address] = (double) $data['admin_fee']; } if ($pay_buyer_amount > 0) { $tx_outs[$data['current_order']['buyer_payout']] = (double) $pay_buyer_amount; } if ($pay_vendor_amount > 0) { $tx_outs[$data['current_order']['vendor_payout']] = (double) $pay_vendor_amount; } // Create spend transaction and redirect, otherwise display an error $create_spend_transaction = $this->order_model->create_spend_transaction($data['current_order']['address'], $tx_outs, $data['current_order']['redeemScript']); if ($create_spend_transaction == TRUE) { // Notify users by way of a dispute update $this->disputes_model->post_dispute_update(array('posting_user_id' => '', 'order_id' => $order_id, 'dispute_id' => $data['dispute']['id'], 'message' => 'New transaction on order page.')); redirect('admin/dispute/' . $order_id); } else { $data['returnMessage'] = $create_spend_transaction; } } else { $data['amount_error'] = 'The User Funds amount must be completely spread between both users.'; } } else { if ($this->order_model->progress_order($data['current_order']['id'], '6') == TRUE) { $update = array('posting_user_id' => '', 'order_id' => $order_id, 'dispute_id' => $data['dispute']['id'], 'message' => 'Dispute closed by admin.'); $this->disputes_model->post_dispute_update($update); $this->disputes_model->set_final_response($data['current_order']['id']); redirect('admin/dispute/' . $order_id); } } } } } } $this->_render($data['page'], $data); }
/** * Decode Mnemonic * * This function decodes a string of 12 words to convert to the electrum * seed. This is an implementation of http://tools.ietf.org/html/rfc1751, * which is how electrum generates a 128-bit key from 12 words. * * @param string $words * @return string */ public static function decode_mnemonic($words) { $math = EccFactory::getAdapter(); $words = explode(" ", $words); $out = ''; $n = 1626; for ($i = 0; $i < count($words) / 3; $i++) { $a = 3 * $i; list($word1, $word2, $word3) = array($words[$a], $words[$a + 1], $words[$a + 2]); $index_w1 = array_search($word1, self::$words); $index_w2 = array_search($word2, self::$words) % $n; $index_w3 = array_search($word3, self::$words) % $n; $x = $index_w1 + $n * $math->mod($index_w2 - $index_w1, $n) + $n * $n * $math->mod($index_w3 - $index_w2, $n); $out .= BitcoinLib::hex_encode($x); } return $out; }
public function testSignMessageDataSetAgainstRPC() { if (!getenv('BITCOINLIB_TEST_AGAINST_RPC')) { return $this->markTestSkipped("Not testing against RPC"); } // special case, when undefined we do 1, otherwise we do ENV * 5 (50 on travis) $cnt = getenv('BITCOINLIB_EXTENSIVE_TESTING') ? getenv('BITCOINLIB_EXTENSIVE_TESTING') * 5 : 1; $rpcHost = getenv('BITCOINLIB_RPC_HOST') ?: '127.0.0.1'; $rpcUser = getenv('BITCOINLIB_RPC_USER') ?: 'bitcoinrpc'; $rpcPassword = getenv('BITCOINLIB_RPC_PASSWORD') ?: '6Wk1SYL7JmPYoUeWjYRSdqij4xrM5rGBvC4kbJipLVJK'; $rpc = new Jsonrpcclient(['url' => "http://{$rpcUser}:{$rpcPassword}@{$rpcHost}:8332"]); if ($rpc->getinfo() == null) { $this->fail("Can't connect to bitcoind"); } $data = json_decode(file_get_contents(__DIR__ . "/data/signverify.json"), true); $data = array_map(function ($k) use($data) { return $data[$k]; }, (array) array_rand($data, $cnt)); foreach ($data as $row) { $privKey = BitcoinLib::WIF_to_private_key($row['wif']); $signature = BitcoinLib::signMessage($row['address'], $privKey); $this->assertTrue(!!$signature); $this->assertTrue(BitcoinLib::verifyMessage($row['address'], $signature, $row['address'])); $this->assertTrue($rpc->verifymessage($row['address'], $signature, $row['address'])); } }
// Create derived key from master key + derivation $key = BIP32::build_key($master, $def); // Display private extended key and the address that's derived from it. echo "Generated key: note that all depth=1 keys are hardened. \n {$key[1]} : {$key[0]}\n"; echo " : " . BIP32::key_to_address($key[0]) . "\n"; // Convert the extended private key to the public key, and display the address that's derived from it. $pub = BIP32::extended_private_to_public($key); echo "Public key\n {$pub[1]} : {$pub[0]}\n"; echo " : " . BIP32::key_to_address($pub[0]) . "\n"; ///////////////////////////// // We're gonna spent the first txout from this tx: // https://www.blocktrail.com/BTC/tx/4a2231e13182cdb64fa2f9aae38fca46549891e9dc15e8aaf484d82fc6e0a1d8 // Set up inputs here $inputs = array(array('txid' => '4a2231e13182cdb64fa2f9aae38fca46549891e9dc15e8aaf484d82fc6e0a1d8', 'vout' => 0)); // Set up outputs here $outputs = array('1KuE17Fbcdsn3Ns5T9Wzi1epurRnKC9qVr' => BitcoinLib::toSatoshi(0.0004)); //////////////////////////// // Parameters for signing. // Create JSON inputs parameter // - These can come from bitcoind, or just knowledge of the txid/vout/scriptPubKey, // and redeemScript if needed. $json_inputs = json_encode(array(array('txid' => '4a2231e13182cdb64fa2f9aae38fca46549891e9dc15e8aaf484d82fc6e0a1d8', 'vout' => 0, 'scriptPubKey' => '76a914' . 'bf012bde5bd12eb7f9a66de5697b241b65a9a3c9' . '88ac'))); // build wallet from private key(s) $wallet = array(); BIP32::bip32_keys_to_wallet($wallet, array($key), '00'); // Create raw transaction $raw_transaction = RawTransaction::create($inputs, $outputs); // Sign the transaction $signed = RawTransaction::sign($wallet, $raw_transaction, $json_inputs); print_r($signed); echo "\n";
<?php use BitWasp\BitcoinLib\BitcoinLib; use BitWasp\BitcoinLib\RawTransaction; require_once __DIR__ . '/../vendor/autoload.php'; $m = 2; $public_keys = array('0379ddc228d8c44a85ae30c877a6b037ec3d627e0507f223a0412790a83a46cd5f', '024d1cf2ca917f4d679fc02df2a39c0a8110a1b6935b27ae6762a0ceeec7752801', '0258f70f6400aa6f60ff0d21c3aaf1ca236d177877d2b9ad9d2c55280e375ab2d2'); // Create redeem script $redeem_script = RawTransaction::create_redeem_script($m, $public_keys); // Obtain 20-byte hash of script $hash160 = BitcoinLib::hash160($redeem_script); // Convert to address with version 0x05. $address = BitcoinLib::hash160_to_address($hash160, '05'); // Display data $c = 0; echo "Public Keys\n"; for ($i = 0; $i < count($public_keys); $i++) { echo "{$i} : " . $public_keys[$i] . "\n"; } echo "\nRedeem Script\n"; echo "{$redeem_script}\n\n"; echo "Hash160\n"; echo "{$hash160}\n\n"; echo "Address\n"; echo "{$address}\n\n";
$inputs = array(array('txid' => '6737e1355be0566c583eecd48bf8a5e1fcdf2d9f51cc7be82d4393ac9555611c', 'vout' => 0, 'value' => 0.0002, 'scriptPubKey' => '76a9147e3f939e8ded8c0d93695310d6d481ae5da3961688ac')); // sum up the total amount of coins we're spending $inputsTotal = 0; foreach ($inputs as $input) { $inputsTotal += $input['value']; } // fixed fee $fee = 0.0001; // information of who we're sending coins to and how much $to = '1PGa6cMAzzrBpTtfvQTzX5PmUxsDiFzKyW'; $send = 5.0E-5; // calculate change $change = $inputsTotal - $send - $fee; // this is our own address $changeAddress = "1CWYJZ4vSoemSCrfBvXreqEtojEeCUeKw3"; // create ouputs, one to recipient and one to change $outputs = array($to => BitcoinLib::toSatoshi($send), $changeAddress => BitcoinLib::toSatoshi($change)); // import private key $wallet = array(); RawTransaction::private_keys_to_wallet($wallet, array('L2V4QgXVUyWVoMGejTj7PrRUUCEi9D9Y1AhUM8E6f5yJm7gemgN6'), '00'); // crate unsigned raw transaction $raw_transaction = RawTransaction::create($inputs, $outputs); // sign the transaction // to broadcast transaction take this value and `bitcoin-cli sendrawtransaction <hex>` $sign = RawTransaction::sign($wallet, $raw_transaction, json_encode($inputs)); print_r($sign); echo "\n"; // set the transaction hash from the raw transaction $txid = RawTransaction::txid_from_raw($sign['hex']); print_r($txid); echo "\n";
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']); }
<?php use BitWasp\BitcoinLib\BitcoinLib; use BitWasp\BitcoinLib\Electrum; require_once __DIR__ . '/../vendor/autoload.php'; $magic_byte = '00'; $string = trim('teach start paradise collect blade chill gay childhood creek picture creator branch'); $seed = Electrum::decode_mnemonic($string); echo "Words: {$string}\n"; echo "Seed: {$seed}\n"; $secexp = Electrum::stretch_seed($seed); $secexp = $secexp['seed']; echo "Secret Exponent: {$secexp}\n"; $mpk = Electrum::generate_mpk($seed); echo "MPK: {$mpk}\n"; for ($i = 0; $i < 5; $i++) { $privkey = Electrum::generate_private_key($secexp, $i, 0); echo "Private key: {$privkey}\n"; echo "Private WIF: " . BitcoinLib::private_key_to_WIF($privkey, FALSE, $magic_byte) . "\n"; $public_key = Electrum::public_key_from_mpk($mpk, $i); echo "Public Key: {$public_key}\n"; $address = BitcoinLib::public_key_to_address($public_key, $magic_byte); echo "Public derivation: {$address}.\n"; $address = BitcoinLib::private_key_to_address($privkey, $magic_byte); echo "Private derivation: {$address}.\n"; echo "-----------\n"; }
public function setup() { // ensure we're set to bitcoin and not bitcoin-testnet BitcoinLib::setMagicByteDefaults('bitcoin'); }
/** * verify a message signed bitcoin-core style * * @param string $message * @param string $address * @param string $signature * @return boolean */ public function verifyMessage($message, $address, $signature) { // we could also use the API instead of the using BitcoinLib to verify // $this->client->post("verify_message", null, ['message' => $message, 'address' => $address, 'signature' => $signature])['result']; try { return BitcoinLib::verifyMessage($address, $signature, $message); } catch (\Exception $e) { return false; } }
* address: mhsywR248h21gCB8oSwse5tmFSPvo9d5ML * priv: cMps8Dg4Z1ThcwvPiPpshR6cbosYoTrgUwgLcFasBSxsdLHwzoUK * pub: 02ab1fae8dacd465460ad8e0c08cb9c25871782aa539a58b65f9bf1264c355d098 * * address: mh7gsCxi4pcuNyHU9aWD9pGogHNJJZcCta * priv: cNn72iUvQhuzZCWg3TC31fvyNDYttL8emHgMcFJzhF4xnFo8LYCk * pub: 02dc43b58ee5313d1969b939718d2c8104a3365d45f12f91753bfc950d16d3e82e * * 2of3 address: 2N1zEScjXeBDX2Gy4c6ojLTfqjRjSvf7iEC * 2of3 redeem: 522103c0b1fd07752ebdd43c75c0a60d67958eeac8d4f5245884477eae094c4361418d2102ab1fae8dacd465460ad8e0c08cb9c25871782aa539a58b65f9bf1264c355d0982102dc43b58ee5313d1969b939718d2c8104a3365d45f12f91753bfc950d16d3e82e53ae * * funded in TX: 83c5c88e94d9c518f314e30ca0529ab3f8e5e4f14a8936db4a32070005e3b61f */ $redeem_script = "522103c0b1fd07752ebdd43c75c0a60d67958eeac8d4f5245884477eae094c4361418d2102ab1fae8dacd465460ad8e0c08cb9c25871782aa539a58b65f9bf1264c355d0982102dc43b58ee5313d1969b939718d2c8104a3365d45f12f91753bfc950d16d3e82e53ae"; $inputs = array(array("txid" => "83c5c88e94d9c518f314e30ca0529ab3f8e5e4f14a8936db4a32070005e3b61f", "vout" => 0, "scriptPubKey" => "a9145fe34588f475c5251ff994eafb691a5ce197d18b87", "redeemScript" => $redeem_script)); $outputs = array("n3P94USXs7LzfF4BKJVyGv2uCfBQRbvMZJ" => BitcoinLib::toSatoshi(0.0001)); $raw_transaction = RawTransaction::create($inputs, $outputs); /* * sign with first key */ $wallet = array(); RawTransaction::private_keys_to_wallet($wallet, array("cV2BRcdtWoZMSovYCpoY9gyvjiVK5xufpAwdAFk1jdonhGZq1cCm")); RawTransaction::redeem_scripts_to_wallet($wallet, array($redeem_script)); $sign = RawTransaction::sign($wallet, $raw_transaction, json_encode($inputs)); print_r($sign); var_dump(2 == $sign['req_sigs'], 1 == $sign['sign_count'], 'false' === $sign['complete']); /* * sign with second key */ $wallet = array(); RawTransaction::private_keys_to_wallet($wallet, array("cMps8Dg4Z1ThcwvPiPpshR6cbosYoTrgUwgLcFasBSxsdLHwzoUK"));