public function testValidateAddresses() { for ($i = 0; $i < 5; $i++) { $set = BitcoinLib::get_new_key_set(null, (bool) ($i % 2)); $validate = $this->client->validateaddress($set['pubAdd']); // Ensure that the Bitcoind believes the address is valid, and that this matches what we think $this->assertTrue($validate['isvalid']); $this->assertEquals($validate['isvalid'], BitcoinLib::validate_address($set['pubAdd'], null, null)); } }
/** * @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 validateAddress($address) { $bitcoin = new BitcoinLib(); $output = array(); try { $validate = $bitcoin->validate_address($address); } catch (Exception $e) { $validate = false; } if (!$validate) { $output['result'] = false; $output['is_mine'] = false; } else { $output['result'] = true; $get = PaymentAddress::where('address', $address)->first(); if ($get) { $output['is_mine'] = true; } else { $output['is_mine'] = false; } } return new Response($output); }
/** * 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'))); }
public function check_bitcoin_address($str) { return \BitWasp\BitcoinLib\BitcoinLib::validate_address($str, '05'); }
public function testP2SHMultisig() { $n = 3; $m = 2; $k = []; $pk_list = []; for ($i = 0; $i < $n; $i++) { $k[$i] = BitcoinLib::get_new_key_set(); $pk_list[] = $k[$i]['pubKey']; } $multisig = RawTransaction::create_multisig($m, RawTransaction::sort_multisig_keys($pk_list)); $this->assertTrue(!!$multisig['address']); $this->assertTrue(BitcoinLib::validate_address($multisig['address'])); }
/** * build inputs and outputs lists for TransactionBuilder * * @param TransactionBuilder $txBuilder * @return array * @throws \Exception */ public function buildTx(TransactionBuilder $txBuilder) { $send = $txBuilder->getOutputs(); $utxos = $txBuilder->getUtxos(); foreach ($utxos as $utxo) { if (!$utxo->address || !$utxo->value || !$utxo->scriptPubKeyHex) { $tx = $this->sdk->transaction($utxo->hash); if (!$tx || !isset($tx['outputs'][$utxo->index])) { throw new \Exception("Invalid output [{$utxo->hash}][{$utxo->index}]"); } $output = $tx['outputs'][$utxo->index]; if (!$utxo->address) { $utxo->address = $output['address']; } if (!$utxo->value) { $utxo->value = $output['value']; } if (!$utxo->scriptPubKeyHex) { $utxo->scriptPubKeyHex = $output['script_hex']; } } if (!$utxo->path) { $address = $utxo->address; if (!BitcoinLib::validate_address($address)) { throw new \Exception("Invalid address [{$address}]"); } $utxo->path = $this->getPathForAddress($address); } if (!$utxo->redeemScript) { list(, $redeemScript) = $this->getRedeemScriptByPath($utxo->path); $utxo->redeemScript = $redeemScript; } } if (array_sum(array_map(function (UTXO $utxo) { return $utxo->value; }, $utxos)) < array_sum(array_column($send, 'value'))) { throw new \Exception("Atempting to spend more than sum of UTXOs"); } list($fee, $change) = $this->determineFeeAndChange($txBuilder); if ($txBuilder->getValidateChange() !== null && $txBuilder->getValidateChange() != $change) { throw new \Exception("the amount of change suggested by the coin selection seems incorrect"); } if ($txBuilder->getValidateFee() !== null && $txBuilder->getValidateFee() != $fee) { throw new \Exception("the fee suggested by the coin selection ({$txBuilder->getValidateFee()}) seems incorrect ({$fee})"); } if ($change > 0) { $send[] = ['address' => $txBuilder->getChangeAddress() ?: $this->getNewAddress(), 'value' => $change]; } // create raw transaction $inputs = array_map(function (UTXO $utxo) { return ['txid' => $utxo->hash, 'vout' => (int) $utxo->index, 'address' => $utxo->address, 'scriptPubKey' => $utxo->scriptPubKeyHex, 'value' => $utxo->value, 'path' => $utxo->path, 'redeemScript' => $utxo->redeemScript]; }, $utxos); // outputs should be randomized to make the change harder to detect if ($txBuilder->shouldRandomizeChangeOuput()) { shuffle($send); } return [$inputs, $send]; }
/** * Create * * This function creates a raw transaction from an array of inputs, * and an array of outputs. It takes essentially the same data is * bitcoind's createrawtransaction function. * * Inputs: Each input is a child array of [txid, vout, and optionally a sequence number.] * Outputs: Each output is a key in the array: address => $value. * * @param array $inputs * @param array $outputs [address => value, ] or [[address, value], ] * or [['address' => address, 'value' => value], ] * or [['scriptPubKey' => scriptPubKey, 'value' => value], ] * @param string $magic_byte * @param string $magic_p2sh_byte * @return string|FALSE */ public static function create($inputs, $outputs, $magic_byte = null, $magic_p2sh_byte = null) { $magic_byte = BitcoinLib::magicByte($magic_byte); $magic_p2sh_byte = BitcoinLib::magicP2SHByte($magic_p2sh_byte); $tx_array = array('version' => '1'); // Inputs is the set of [txid/vout/scriptPubKey] $tx_array['vin'] = array(); foreach ($inputs as $i => $input) { if (!isset($input['txid']) || strlen($input['txid']) !== 64 || !isset($input['vout']) || !is_numeric($input['vout'])) { throw new \InvalidArgumentException("Invalid input [{$i}]"); } $tx_array['vin'][] = ['txid' => $input['txid'], 'vout' => $input['vout'], 'sequence' => isset($input['sequence']) ? $input['sequence'] : 4294967295, 'scriptSig' => ['hex' => '']]; } // Outputs is the set of [address/amount] $tx_array['vout'] = array(); foreach ($outputs as $k => $v) { $address = null; $scriptPubKey = null; if (is_numeric($k)) { if (!is_array($v)) { throw new \InvalidArgumentException("outputs should be [address => value, ] or [[address, value], ]" . "or [['address' => address, 'value' => value], ] or [['scriptPubKey' => scriptPubKey, 'value' => value], ]"); } if (isset($v['scriptPubKey']) && isset($v['value'])) { $scriptPubKey = $v['scriptPubKey']; $value = $v['value']; } else { if (isset($v['address']) && isset($v['value'])) { $address = $v['address']; $value = $v['value']; } else { if (count($v) == 2 && isset($v[0]) && isset($v[1])) { $address = $v[0]; $value = $v[1]; } else { throw new \InvalidArgumentException("outputs should be [address => value, ] or [[address, value], ]" . "or [['address' => address, 'value' => value], ] or [['scriptPubKey' => scriptPubKey, 'value' => value], ]"); } } } } else { $address = $k; $value = $v; } if (!is_int($value)) { throw new \InvalidArgumentException("Values should be in Satoshis [{$value}]"); } if (!$scriptPubKey) { if (!BitcoinLib::validate_address($address, $magic_byte, $magic_p2sh_byte)) { throw new \InvalidArgumentException("Invalid address [{$address}]"); } $decode_address = BitcoinLib::base58_decode($address); $version = substr($decode_address, 0, 2); $hash = substr($decode_address, 2, 40); if ($version == $magic_p2sh_byte) { // OP_HASH160 <scriptHash> OP_EQUAL $scriptPubKey = "a914{$hash}87"; } else { // OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG $scriptPubKey = "76a914{$hash}88ac"; } } $tx_array['vout'][] = ['value' => $value, 'scriptPubKey' => ['hex' => $scriptPubKey]]; } $tx_array['locktime'] = 0; return self::encode($tx_array); }
public function testBase58CheckEncodeP2SH() { $cnt = (getenv('BITCOINLIB_EXTENSIVE_TESTING') ?: 1) * 50; for ($i = 0; $i < $cnt; $i++) { // random, 20-byte string. $hex = (string) bin2hex(mcrypt_create_iv(20, \MCRYPT_DEV_URANDOM)); // 'manually' create address $encode = BitcoinLib::base58_encode_checksum($this->p2shAddressVersion . $hex); $decode = BitcoinLib::base58_decode_checksum($encode); // validate 'manually' created address $this->assertTrue(BitcoinLib::validate_address($encode, $this->addressVersion, $this->p2shAddressVersion)); // validate 'manually' created address without specifying the address version // relying on the defaults $this->assertTrue(BitcoinLib::validate_address($encode)); // validate 'manually' created address // disable address version and P2S address version specifically $this->assertTrue(BitcoinLib::validate_address($encode, false, null)); $this->assertFalse(BitcoinLib::validate_address($encode, null, false)); // validate 'manually' $this->assertTrue($hex == $decode); // create address $check2 = BitcoinLib::hash160_to_address($hex, $this->p2shAddressVersion); // validate created address $this->assertTrue(BitcoinLib::validate_address($check2, $this->addressVersion, $this->p2shAddressVersion)); // validate created address without specifying the address version // relying on the defaults $this->assertTrue(BitcoinLib::validate_address($check2)); // validate created address // disable address version and P2S address version specifically $this->assertTrue(BitcoinLib::validate_address($check2, false, null)); $this->assertFalse(BitcoinLib::validate_address($check2, null, false)); // validate 'manually' $this->assertTrue($check2 == $encode); // create address, without specifying the address version // relying on the defaults $check3 = BitcoinLib::hash160_to_address($hex, 'p2sh'); // validate created address $this->assertTrue(BitcoinLib::validate_address($check3, $this->addressVersion, $this->p2shAddressVersion)); // validate created address without specifying the address version // relying on the defaults $this->assertTrue(BitcoinLib::validate_address($check3)); // validate created address // disable address version and P2S address version specifically $this->assertTrue(BitcoinLib::validate_address($check3, false, null)); $this->assertFalse(BitcoinLib::validate_address($check3, null, false)); // validate 'manually' $this->assertTrue($check3 == $encode); } }