/** * get the plain public key for the current BIP32 key * * @return string */ public function publicKey() { // if this is a BIP32 Private key then we first build the public key // that way it will be cached nicely if (!$this->path->isPublicPath()) { return $this->buildKey($this->path->publicPath())->publicKey(); } else { if (is_null($this->publicKey)) { $this->publicKey = BIP32::extract_public_key($this->tuple()); } return $this->publicKey; } }
public function testIsParentOf() { $ok = ["M/9999'/0" => ["M/9999'/0/0", "M/9999'/0/1", "M/9999'/0/0", "M/9999'/0/1", "M/9999'/0/0'"], "m/9999'/0" => ["m/9999'/0/0", "m/9999'/0/1", "m/9999'/0/0", "m/9999'/0/1"]]; foreach ($ok as $p => $cs) { foreach ($cs as $c) { $this->assertTrue(BIP32Path::path($p)->isParentOf(BIP32Path::path($c)), "parent[{$p}] child[{$c}]"); } } $fail = ["M/9999'/0" => ["M/9999'/0", "m/9999/0'/0", "M/9999/0'/0", "M/999'/0/1"], "m/999'/0" => ["m/999'/0", "M/999'/0/0", "m/9999'/0/0", "m/9999'/0/1", "m/9999'/0/0", "m/9999'/0/1"]]; foreach ($fail as $p => $cs) { foreach ($cs as $c) { $this->assertFalse(BIP32Path::path($p)->isParentOf(BIP32Path::path($c)), "parent[{$p}] child[{$c}]"); } } }
/** * signs a raw transaction * * @param $rawTransaction * @param $inputs * @return array */ protected function signTransaction($rawTransaction, $inputs) { $wallet = []; $keys = []; $redeemScripts = []; foreach ($inputs as $input) { //create private keys for signing $path = BIP32Path::path($input['path'])->privatePath(); $keys[] = $this->primaryPrivateKey->buildKey($path); $keys[] = $this->backupPrivateKey->buildKey($path->unhardenedPath()); $redeemScripts[] = $input['redeemScript']; } //add the keys and redeem scripts to a wallet to sign the transaction with BIP32::bip32_keys_to_wallet($wallet, array_map(function (BIP32Key $key) { return $key->tuple(); }, $keys)); RawTransaction::redeem_scripts_to_wallet($wallet, $redeemScripts); return RawTransaction::sign($wallet, $rawTransaction, json_encode($inputs)); }
/** * sign a raw transaction with the private keys that we have * * @param string $raw_transaction * @param array[] $inputs * @return array response from RawTransaction::sign * @throws \Exception */ protected function signTransaction($raw_transaction, array $inputs) { $wallet = []; $keys = []; $redeemScripts = []; foreach ($inputs as $input) { $redeemScript = null; $key = null; if (isset($input['redeemScript'], $input['path'])) { $redeemScript = $input['redeemScript']; $path = BIP32Path::path($input['path'])->privatePath(); $key = $this->primaryPrivateKey->buildKey($path); $address = $this->getAddressFromKey($key, $path); if ($address != $input['address']) { throw new \Exception("Generated address does not match expected address!"); } } else { throw new \Exception("No redeemScript/path for input"); } if ($redeemScript && $key) { $keys[] = $key; $redeemScripts[] = $redeemScript; } } BIP32::bip32_keys_to_wallet($wallet, array_map(function (BIP32Key $key) { return $key->tuple(); }, $keys)); RawTransaction::redeem_scripts_to_wallet($wallet, $redeemScripts); return RawTransaction::sign($wallet, $raw_transaction, json_encode($inputs)); }
protected static function BIP32Path(array $path) { return BIP32Path::path(array_values(array_filter($path, function ($v) { return $v !== null; }))); }