/** * @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)); }
/** * generate a new mnemonic from some random entropy (512 bit) * * @param string $forceEntropy forced entropy instead of random entropy for testing purposes * @return string * @throws \Exception */ protected function generateNewMnemonic($forceEntropy = null) { if ($forceEntropy === null) { $entropy = BIP39::generateEntropy(512); } else { $entropy = $forceEntropy; } return BIP39::entropyToMnemonic($entropy); }
public function testNewBlankWithoutMnemonicsWallet() { $client = $this->setupBlocktrailSDK(); $identifier = $this->getRandomTestIdentifier(); $primaryPrivateKey = BIP32::master_key(BIP39::mnemonicToSeedHex(BIP39::entropyToMnemonic(BIP39::generateEntropy(512)), "password"), 'bitcoin', true); $backupPublicKey = BIP32::extended_private_to_public(BIP32::master_key(BIP39::mnemonicToSeedHex(BIP39::entropyToMnemonic(BIP39::generateEntropy(512)), "password"), 'bitcoin', true)); /** * @var $wallet \Blocktrail\SDK\Wallet */ $e = null; try { $wallet = $client->initWallet(["identifier" => $identifier]); } catch (ObjectNotFound $e) { list($wallet, $primaryMnemonic, $backupMnemonic, $blocktrailPublicKeys) = $client->createNewWallet(["identifier" => $identifier, "primary_private_key" => $primaryPrivateKey, "backup_public_key" => $backupPublicKey, "key_index" => 9999]); } $this->assertTrue(!!$e, "New wallet with ID [{$identifier}] already exists..."); $wallet = $client->initWallet(["identifier" => $identifier, "primary_private_key" => $primaryPrivateKey]); $this->wallets[] = $wallet; // store for cleanup $this->assertEquals(0, $wallet->getBalance()[0]); $e = null; try { $wallet->pay(["2N6Fg6T74Fcv1JQ8FkPJMs8mYmbm9kitTxy" => BlocktrailSDK::toSatoshi(0.001)]); } catch (\Exception $e) { } $this->assertTrue(!!$e, "Wallet without balance is able to pay..."); }
/** * unlock wallet so it can be used for payments * * @param $options ['primary_private_key' => key] OR ['passphrase' => pass] * @param callable $fn * @return bool * @throws \Exception */ public function unlock($options, callable $fn = null) { // explode the wallet data $password = isset($options['passphrase']) ? $options['passphrase'] : (isset($options['password']) ? $options['password'] : null); $primaryMnemonic = $this->primaryMnemonic; $primaryPrivateKey = isset($options['primary_private_key']) ? $options['primary_private_key'] : null; if ($primaryMnemonic && $primaryPrivateKey) { throw new \InvalidArgumentException("Can't specify Primary Mnemonic and Primary PrivateKey"); } if (!$primaryMnemonic && !$primaryPrivateKey) { throw new \InvalidArgumentException("Can't init wallet with Primary Mnemonic or Primary PrivateKey"); } if ($primaryMnemonic && !$password) { throw new \InvalidArgumentException("Can't init wallet with Primary Mnemonic without a passphrase"); } if ($primaryPrivateKey) { if (is_string($primaryPrivateKey)) { $primaryPrivateKey = [$primaryPrivateKey, "m"]; } } else { // convert the mnemonic to a seed using BIP39 standard $primarySeed = BIP39::mnemonicToSeedHex($primaryMnemonic, $password); // create BIP32 private key from the seed $primaryPrivateKey = BIP32::master_key($primarySeed, $this->network, $this->testnet); } $this->primaryPrivateKey = BIP32Key::create($primaryPrivateKey); // create checksum (address) of the primary privatekey to compare to the stored checksum $checksum = BIP32::key_to_address($primaryPrivateKey[0]); if ($checksum != $this->checksum) { throw new \Exception("Checksum [{$checksum}] does not match [{$this->checksum}], most likely due to incorrect password"); } $this->locked = false; // if the response suggests we should upgrade to a different blocktrail cosigning key then we should if (isset($data['upgrade_key_index'])) { $this->upgradeKeyIndex($data['upgrade_key_index']); } if ($fn) { $fn($this); $this->lock(); } }
public function testGenerate() { for ($i = 0; $i < 100; $i++) { $entropy = BIP39::generateEntropy(128); $this->assertTrue(!!$entropy); $mnemonic = BIP39::entropyToMnemonic($entropy); $this->assertTrue(!!$entropy); $entropy2 = BIP39::mnemonicToEntropy($mnemonic); $this->assertTrue(!!$entropy2); $this->assertEquals($entropy, $entropy2); $bip32 = BIP32::master_key(BIP39::mnemonicToSeedHex($mnemonic, 'PASSWORD')); $this->assertTrue(!!$bip32); } for ($i = 0; $i < 100; $i++) { $entropy = BIP39::generateEntropy(256); $this->assertTrue(!!$entropy); $mnemonic = BIP39::entropyToMnemonic($entropy); $this->assertTrue(!!$entropy); $entropy2 = BIP39::mnemonicToEntropy($mnemonic); $this->assertTrue(!!$entropy2); $this->assertEquals($entropy, $entropy2); $bip32 = BIP32::master_key(BIP39::mnemonicToSeedHex($mnemonic, 'PASSWORD')); $this->assertTrue(!!$bip32); } for ($i = 0; $i < 100; $i++) { $entropy = BIP39::generateEntropy(512); $this->assertTrue(!!$entropy); $mnemonic = BIP39::entropyToMnemonic($entropy); $this->assertTrue(!!$entropy); $entropy2 = BIP39::mnemonicToEntropy($mnemonic); $this->assertTrue(!!$entropy2); $this->assertEquals($entropy, $entropy2); $bip32 = BIP32::master_key(BIP39::mnemonicToSeedHex($mnemonic, 'PASSWORD')); $this->assertTrue(!!$bip32); } }
<?php use BitWasp\BitcoinLib\BIP32; use BitWasp\BitcoinLib\BIP39\BIP39; require_once __DIR__ . '/../vendor/autoload.php'; $password = "******"; $entropy = BIP39::generateEntropy(256); $mnemonic = BIP39::entropyToMnemonic($entropy); $seed = BIP39::mnemonicToSeedHex($mnemonic, $password); unset($entropy); // ignore, forget about this, don't use it! var_dump($mnemonic); // this is what you print on a piece of paper, etc var_dump($password); // this is secret of course var_dump($seed); // this is what you use to generate a key $key = BIP32::master_key($seed); // enjoy