public function testBIP32Key() { $this->assertEquals(new BIP32Key("xpub1", "m"), new BIP32Key(["xpub1", "m"])); $this->assertEquals(new BIP32Key("xpub1", "m"), BIP32Key::create("xpub1", "m")); $this->assertEquals(new BIP32Key("xpub1", "m"), BIP32Key::create(["xpub1", "m"])); $e = null; try { BIP32Key::create("xpub1"); } catch (\Exception $e) { } $this->assertTrue(!!$e, "an exception should be thrown"); $k = BIP32Key::create("xprv9s21ZrQH143K44ed3A1NBn3udjmm6qHRpX4Da47ZpRdhqxpkhCWwMFWNpFbSkxAtkZ2s2345tyX5GdTuDWQYZ9jZPuTbkkBeHx3h6RmzL8J", "m"); $this->assertEquals("xprv9s21ZrQH143K44ed3A1NBn3udjmm6qHRpX4Da47ZpRdhqxpkhCWwMFWNpFbSkxAtkZ2s2345tyX5GdTuDWQYZ9jZPuTbkkBeHx3h6RmzL8J", $k->key()); $this->assertEquals("m", $k->path()); $this->assertEquals(["xprv9s21ZrQH143K44ed3A1NBn3udjmm6qHRpX4Da47ZpRdhqxpkhCWwMFWNpFbSkxAtkZ2s2345tyX5GdTuDWQYZ9jZPuTbkkBeHx3h6RmzL8J", "m"], $k->tuple()); $c1 = $k->buildKey("m/1"); $this->assertEquals("xprv9uyqGTZ6imvYQgfExoMn4ja1cD8DhHqhTxZVDBKSeETMqd87Nx5vexxEnuXYFMFi5ViQFdvw7AQt3RovuTivKdSFmygPBadBmuCVLszfHDc", $c1->key()); $this->assertEquals("m/1", $c1->path()); $this->assertEquals(["xprv9uyqGTZ6imvYQgfExoMn4ja1cD8DhHqhTxZVDBKSeETMqd87Nx5vexxEnuXYFMFi5ViQFdvw7AQt3RovuTivKdSFmygPBadBmuCVLszfHDc", "m/1"], $c1->tuple()); $p = $c1->bip32Path(); $this->assertTrue($p instanceof BIP32Path); $this->assertEquals("m/1", (string) $p); $this->assertEquals("039e3650a99c00105dee96b0ceee7ace9ad6f898fc728275937b9f58e831570210", $c1->publicKey()); }
/** * @param $primaryMnemonic * @param $backupMnemonic * @param $blocktrailPublicKeys */ public function __construct($primaryMnemonic, $backupMnemonic, $blocktrailPublicKeys) { /* * if DOMPDF is not already loaded we have to do it * they require a config file to be loaded, no autoloading :/ */ if (!defined('DOMPDF_ENABLE_AUTOLOAD')) { // disable DOMPDF's internal autoloader if you are using Composer define('DOMPDF_ENABLE_AUTOLOAD', false); //try the different possible locations for the config file, depending on if the sdk is included as a dependency or is the main project itself @(include_once __DIR__ . '/../../../dompdf/dompdf/dompdf_config.inc.php') || @(include_once __DIR__ . '/../vendor/dompdf/dompdf/dompdf_config.inc.php'); } //set the fonts path $this->fontsPath = dirname(__FILE__) . '/../resources/fonts'; $this->primaryMnemonic = $primaryMnemonic; $this->backupMnemonic = $backupMnemonic; $this->blocktrailPublicKeys = array_map(function ($key) { return BIP32Key::create($key); // M/9999' or M/0' or M/1' }, $blocktrailPublicKeys); }
/** * @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)); }
/** * upgrade wallet to different blocktrail cosign key * * @param $keyIndex * @return bool * @throws \Exception */ public function upgradeKeyIndex($keyIndex) { if ($this->locked) { throw new \Exception("Wallet needs to be unlocked to upgrade key index"); } $walletPath = WalletPath::create($keyIndex); // do the upgrade to the new 'key_index' $primaryPublicKey = BIP32::extended_private_to_public(BIP32::build_key($this->primaryPrivateKey->tuple(), (string) $walletPath->keyIndexPath())); $result = $this->sdk->upgradeKeyIndex($this->identifier, $keyIndex, $primaryPublicKey); $this->primaryPublicKeys[$keyIndex] = BIP32Key::create($primaryPublicKey); $this->keyIndex = $keyIndex; $this->walletPath = $walletPath; // update the blocktrail public keys foreach ($result['blocktrail_public_keys'] as $keyIndex => $pubKey) { if (!isset($this->blocktrailPublicKeys[$keyIndex])) { $this->blocktrailPublicKeys[$keyIndex] = BIP32Key::create($pubKey); } } return true; }