public function testWalletPath()
 {
     $this->assertEquals(new WalletPath(), WalletPath::create());
     $w = WalletPath::create();
     $this->assertTrue($w->path() instanceof BIP32Path);
     $this->assertTrue($w->keyIndexPath() instanceof BIP32Path);
     $this->assertTrue($w->backupPath() instanceof BIP32Path);
     $this->assertTrue($w->keyIndexBackupPath() instanceof BIP32Path);
     $w = WalletPath::create();
     $this->assertEquals("m/0'/0/0", (string) $w->path());
     $this->assertEquals("m/0'", (string) $w->keyIndexPath());
     $this->assertEquals("m/0/0/0", (string) $w->backupPath());
     $this->assertEquals("m/0", (string) $w->keyIndexBackupPath());
     $w = $w->address(1);
     $this->assertEquals("m/0'/0/1", (string) $w->path());
     $this->assertEquals("m/0'", (string) $w->keyIndexPath());
     $this->assertEquals("m/0/0/1", (string) $w->backupPath());
     $this->assertEquals("m/0", (string) $w->keyIndexBackupPath());
     $w = WalletPath::create(1, 1, 1);
     $this->assertEquals("m/1'/1/1", (string) $w->path());
     $this->assertEquals("m/1'", (string) $w->keyIndexPath());
     $this->assertEquals("m/1/1/1", (string) $w->backupPath());
     $this->assertEquals("m/1", (string) $w->keyIndexBackupPath());
     $w = $w->address(2);
     $this->assertEquals("m/1'/1/2", (string) $w->path());
     $this->assertEquals("m/1'", (string) $w->keyIndexPath());
     $this->assertEquals("m/1/1/2", (string) $w->backupPath());
     $this->assertEquals("m/1", (string) $w->keyIndexBackupPath());
 }
 /**
  * create a new wallet
  *   - will generate a new primary seed (with password) and backup seed (without password)
  *   - send the primary seed (BIP39 'encrypted') and backup public key to the server
  *   - receive the blocktrail co-signing public key from the server
  *
  * Either takes one argument:
  * @param array $options
  *
  * Or takes three arguments (old, deprecated syntax):
  * (@nonPHP-doc) @param      $identifier
  * (@nonPHP-doc) @param      $password
  * (@nonPHP-doc) @param int  $keyIndex         override for the blocktrail cosigning key to use
  *
  * @return array[WalletInterface, (string)primaryMnemonic, (string)backupMnemonic]
  * @throws \Exception
  */
 public function createNewWallet($options)
 {
     if (!is_array($options)) {
         $args = func_get_args();
         $options = ["identifier" => $args[0], "password" => $args[1], "key_index" => isset($args[2]) ? $args[2] : null];
     }
     $identifier = $options['identifier'];
     $password = isset($options['passphrase']) ? $options['passphrase'] : (isset($options['password']) ? $options['password'] : null);
     $keyIndex = isset($options['key_index']) ? $options['key_index'] : 0;
     $walletPath = WalletPath::create($keyIndex);
     $storePrimaryMnemonic = isset($options['store_primary_mnemonic']) ? $options['store_primary_mnemonic'] : null;
     if (isset($options['primary_mnemonic']) && $options['primary_private_key']) {
         throw new \InvalidArgumentException("Can't specify Primary Mnemonic and Primary PrivateKey");
     }
     $primaryMnemonic = null;
     $primaryPrivateKey = null;
     if (!isset($options['primary_mnemonic']) && !isset($options['primary_private_key'])) {
         if (!$password) {
             throw new \InvalidArgumentException("Can't generate Primary Mnemonic without a passphrase");
         } else {
             // create new primary seed
             list($primaryMnemonic, $primarySeed, $primaryPrivateKey) = $this->newPrimarySeed($password);
             if ($storePrimaryMnemonic !== false) {
                 $storePrimaryMnemonic = true;
             }
         }
     } else {
         if (isset($options['primary_mnemonic'])) {
             $primaryMnemonic = $options['primary_mnemonic'];
         } else {
             if (isset($options['primary_private_key'])) {
                 $primaryPrivateKey = $options['primary_private_key'];
             }
         }
     }
     if ($storePrimaryMnemonic && $primaryMnemonic && !$password) {
         throw new \InvalidArgumentException("Can't store Primary Mnemonic on server without a passphrase");
     }
     if ($primaryPrivateKey) {
         if (is_string($primaryPrivateKey)) {
             $primaryPrivateKey = [$primaryPrivateKey, "m"];
         }
     } else {
         $primaryPrivateKey = BIP32::master_key(BIP39::mnemonicToSeedHex($primaryMnemonic, $password), 'bitcoin', $this->testnet);
     }
     if (!$storePrimaryMnemonic) {
         $primaryMnemonic = false;
     }
     // create primary public key from the created private key
     $primaryPublicKey = BIP32::build_key($primaryPrivateKey, (string) $walletPath->keyIndexPath()->publicPath());
     if (isset($options['backup_mnemonic']) && $options['backup_public_key']) {
         throw new \InvalidArgumentException("Can't specify Backup Mnemonic and Backup PublicKey");
     }
     $backupMnemonic = null;
     $backupPublicKey = null;
     if (!isset($options['backup_mnemonic']) && !isset($options['backup_public_key'])) {
         list($backupMnemonic, $backupSeed, $backupPrivateKey) = $this->newBackupSeed();
     } else {
         if (isset($options['backup_mnemonic'])) {
             $backupMnemonic = $options['backup_mnemonic'];
         } else {
             if (isset($options['backup_public_key'])) {
                 $backupPublicKey = $options['backup_public_key'];
             }
         }
     }
     if ($backupPublicKey) {
         if (is_string($backupPublicKey)) {
             $backupPublicKey = [$backupPublicKey, "m"];
         }
     } else {
         $backupPublicKey = BIP32::extended_private_to_public(BIP32::master_key(BIP39::mnemonicToSeedHex($backupMnemonic, ""), 'bitcoin', $this->testnet));
     }
     // create a checksum of our private key which we'll later use to verify we used the right password
     $checksum = BIP32::key_to_address($primaryPrivateKey[0]);
     // send the public keys to the server to store them
     //  and the mnemonic, which is safe because it's useless without the password
     $data = $this->_createNewWallet($identifier, $primaryPublicKey, $backupPublicKey, $primaryMnemonic, $checksum, $keyIndex);
     // received the blocktrail public keys
     $blocktrailPublicKeys = $data['blocktrail_public_keys'];
     $wallet = new Wallet($this, $identifier, $primaryMnemonic, [$keyIndex => $primaryPublicKey], $backupPublicKey, $blocktrailPublicKeys, $keyIndex, $this->network, $this->testnet, $checksum);
     $wallet->unlock($options);
     // return wallet and backup mnemonic
     return [$wallet, $primaryMnemonic, $backupMnemonic, $blocktrailPublicKeys];
 }
 /**
  * create a batch of multisig addresses
  *
  * @param $start
  * @param $count
  * @param $keyIndex
  * @return array
  */
 protected function createBatchAddresses($start, $count, $keyIndex)
 {
     $addresses = array();
     for ($i = 0; $i < $count; $i++) {
         //create a path subsequent address
         $path = (string) WalletPath::create($keyIndex, $_chain = 0, $start + $i)->path()->publicPath();
         list($address, $redeem) = $this->createAddress($path);
         $addresses[$address] = array('redeem' => $redeem, 'path' => $path);
         if ($this->debug) {
             echo ".";
         }
     }
     return $addresses;
 }
예제 #4
0
 protected function _createTestWallet(BlocktrailSDKInterface $client, $identifier, $passphrase, $primaryMnemonic, $backupMnemonic, $readOnly = false)
 {
     $walletPath = WalletPath::create(9999);
     $seed = BIP39::mnemonicToSeedHex($primaryMnemonic, $passphrase);
     $primaryPrivateKey = BIP32::master_key($seed, 'bitcoin', true);
     $primaryPublicKey = BIP32::build_key($primaryPrivateKey, (string) $walletPath->keyIndexPath()->publicPath());
     $seed = BIP39::mnemonicToSeedHex($backupMnemonic, "");
     $backupPrivateKey = BIP32::master_key($seed, 'bitcoin', true);
     $backupPublicKey = BIP32::build_key($backupPrivateKey, (string) "M");
     $testnet = true;
     $checksum = BIP32::key_to_address($primaryPrivateKey[0]);
     $result = $client->_createNewWallet($identifier, $primaryPublicKey, $backupPublicKey, $primaryMnemonic, $checksum, 9999);
     $blocktrailPublicKeys = $result['blocktrail_public_keys'];
     $keyIndex = $result['key_index'];
     $wallet = new Wallet($client, $identifier, $primaryMnemonic, [$keyIndex => $primaryPublicKey], $backupPublicKey, $blocktrailPublicKeys, $keyIndex, 'bitcoin', $testnet, $checksum);
     if (!$readOnly) {
         $wallet->unlock(['password' => $passphrase]);
     }
     return $wallet;
 }
예제 #5
0
 /**
  * get a new BIP32 derivation for the next (unused) address
  *  by requesting it from the API
  *
  * @return string
  * @throws \Exception
  */
 protected function getNewDerivation()
 {
     $path = $this->walletPath->path()->last("*");
     if (self::VERIFY_NEW_DERIVATION) {
         $new = $this->sdk->_getNewDerivation($this->identifier, (string) $path);
         $path = $new['path'];
         $address = $new['address'];
         $redeemScript = $new['redeem_script'];
         list($checkAddress, $checkRedeemScript) = $this->getRedeemScriptByPath($path);
         if ($checkAddress != $address) {
             throw new \Exception("Failed to verify that address from API [{$address}] matches address locally [{$checkAddress}]");
         }
         if ($checkRedeemScript != $redeemScript) {
             throw new \Exception("Failed to verify that redeemScript from API [{$redeemScript}] matches address locally [{$checkRedeemScript}]");
         }
     } else {
         $path = $this->sdk->getNewDerivation($this->identifier, (string) $path);
     }
     return (string) $path;
 }