diff --git a/examples/custom_tx_payment_api_example_force_from.php b/examples/custom_tx_payment_api_example_force_from.php index c7f80b3..0c804f0 100644 --- a/examples/custom_tx_payment_api_example_force_from.php +++ b/examples/custom_tx_payment_api_example_force_from.php @@ -58,8 +58,8 @@ // add UTXOs to txbuilder foreach ($utxos['data'] as $utxo) { $scriptPubKey = ScriptFactory::fromHex($utxo['script_hex']); - $address = AddressFactory::fromString($utxo['address']); - $path = $wallet->getPathForAddress($address->getAddress()); + $address = $utxo['address']; + $path = $wallet->getPathForAddress($address); $scripts = $wallet->getWalletScriptByPath($path); $redeemScript = $scripts->getRedeemScript(); $witnessScript = null; diff --git a/examples/wallet_cpfp.php b/examples/wallet_cpfp.php index 6fe24b8..fccc22c 100644 --- a/examples/wallet_cpfp.php +++ b/examples/wallet_cpfp.php @@ -90,8 +90,8 @@ // add UTXOs to txbuilder foreach ($utxos as $utxo) { $scriptPubKey = ScriptFactory::fromHex($utxo['scriptpubkey_hex']); - $address = AddressFactory::fromString($utxo['address']); - $path = $wallet->getPathForAddress($address->getAddress()); + $address = $utxo['address']; + $path = $wallet->getPathForAddress($address); $scripts = $wallet->getWalletScriptByPath($path); $redeemScript = $scripts->getRedeemScript(); $witnessScript = null; diff --git a/src/BackupGenerator.php b/src/BackupGenerator.php index c1b7ed9..74a03b6 100644 --- a/src/BackupGenerator.php +++ b/src/BackupGenerator.php @@ -3,6 +3,7 @@ namespace Blocktrail\SDK; use BitWasp\Bitcoin\Key\Deterministic\HierarchicalKeyFactory; +use BitWasp\Bitcoin\Network\NetworkInterface; use Blocktrail\SDK\Bitcoin\BIP32Key; use Endroid\QrCode\QrCode; @@ -27,6 +28,11 @@ class BackupGenerator { */ protected $blocktrailPubKeyQRs = []; + /** + * @var NetworkInterface + */ + protected $network; + protected $identifier; protected $backupInfo; @@ -45,7 +51,7 @@ class BackupGenerator { * @param array $extra * @param null $options */ - public function __construct($identifier, $backupInfo, $extra = null, $options = null) { + public function __construct(NetworkInterface $network, $identifier, $backupInfo, $extra = null, $options = null) { /* * if DOMPDF is not already loaded we have to do it * they require a config file to be loaded, no autoloading :/ @@ -61,6 +67,7 @@ public function __construct($identifier, $backupInfo, $extra = null, $options = //set the fonts path $this->fontsPath = dirname(__FILE__) . '/../resources/fonts'; + $this->network = $network; $this->identifier = $identifier; $this->backupInfo = $backupInfo; $this->extra = $extra ?: []; @@ -77,7 +84,7 @@ protected function processBlocktrailPubKeys() { //create QR codes for each blocktrail pub key foreach ($this->backupInfo['blocktrail_public_keys'] as $keyIndex => $key) { - $key = $key instanceof BIP32Key ? $key : BIP32Key::create(HierarchicalKeyFactory::fromExtended($key[0]), $key[1]); + $key = $key instanceof BIP32Key ? $key : BIP32Key::create(HierarchicalKeyFactory::fromExtended($key[0], $this->network), $key[1]); $qrCode = new QrCode(); $qrCode diff --git a/src/Bitcoin/BIP32Key.php b/src/Bitcoin/BIP32Key.php index 1cb7ba3..f8dd24d 100644 --- a/src/Bitcoin/BIP32Key.php +++ b/src/Bitcoin/BIP32Key.php @@ -2,9 +2,10 @@ namespace Blocktrail\SDK\Bitcoin; -use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Key\PublicKey; use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface; use BitWasp\Bitcoin\Key\Deterministic\HierarchicalKey; +use BitWasp\Bitcoin\Key\Deterministic\HierarchicalKeyFactory; +use BitWasp\Bitcoin\Network\NetworkInterface; /** * Class BIP32Key @@ -22,6 +23,11 @@ class BIP32Key { */ private $path; + /** + * @var NetworkInterface + */ + private $network; + /** * @var string|null */ @@ -33,13 +39,15 @@ class BIP32Key { private $derivations = []; /** - * @param HierarchicalKey $key - * @param string|null $path + * @param HierarchicalKey $key + * @param NetworkInterface $network + * @param string|null $path * @throws \Exception */ - public function __construct(HierarchicalKey $key, $path = null) { + public function __construct(NetworkInterface $network, HierarchicalKey $key, $path = null) { $this->key = $key; $this->path = BIP32Path::path($path); + $this->network = $network; return; @@ -57,12 +65,30 @@ public function __construct(HierarchicalKey $key, $path = null) { /** * static method to initialize class * - * @param HierarchicalKey $key - * @param string|null $path + * @param NetworkInterface $network + * @param HierarchicalKey $key + * @param string|null $path + * @return BIP32Key + */ + public static function create(NetworkInterface $network, HierarchicalKey $key, $path = null) { + return new BIP32Key($network, $key, $path); + } + + /** + * @param NetworkInterface $network + * @param string $key + * @param string|null $path * @return BIP32Key */ - public static function create(HierarchicalKey $key, $path = null) { - return new BIP32Key($key, $path); + public static function fromString(NetworkInterface $network, $key, $path = null) { + return static::create($network, HierarchicalKeyFactory::fromExtended($key, $network), $path); + } + + /** + * @return NetworkInterface + */ + public function network() { + return $this->network; } /** @@ -113,7 +139,7 @@ public function bip32Path() { } public function tuple() { - return [$this->key->toExtendedKey(), (string)$this->path]; + return [$this->key->toExtendedKey($this->network), (string)$this->path]; } /** @@ -151,7 +177,7 @@ public function buildKey($path) { $key = $key->toPublic(); } - $this->derivations[$originalPath] = BIP32Key::create($key, $originalPath); + $this->derivations[$originalPath] = BIP32Key::create($this->network, $key, $originalPath); } return $this->derivations[$originalPath]; diff --git a/src/BlocktrailSDK.php b/src/BlocktrailSDK.php index 7c7b6dc..971a15f 100644 --- a/src/BlocktrailSDK.php +++ b/src/BlocktrailSDK.php @@ -15,7 +15,7 @@ use BitWasp\Bitcoin\MessageSigner\SignedMessage; use BitWasp\Bitcoin\Mnemonic\Bip39\Bip39SeedGenerator; use BitWasp\Bitcoin\Mnemonic\MnemonicFactory; -use BitWasp\Bitcoin\Network\NetworkFactory; +use BitWasp\Bitcoin\Network\NetworkInterface; use BitWasp\Bitcoin\Transaction\TransactionFactory; use BitWasp\Buffertools\Buffer; use BitWasp\Buffertools\BufferInterface; @@ -46,6 +46,11 @@ class BlocktrailSDK implements BlocktrailSDKInterface { */ protected $testnet; + /** + * @var NetworkParams + */ + protected $networkParams; + /** * @param string $apiKey the API_KEY to use for authentication * @param string $apiSecret the API_SECRET to use for authentication @@ -56,7 +61,6 @@ class BlocktrailSDK implements BlocktrailSDKInterface { * this will cause the $network, $testnet and $apiVersion to be ignored! */ public function __construct($apiKey, $apiSecret, $network = 'BTC', $testnet = false, $apiVersion = 'v1', $apiEndpoint = null) { - list ($apiNetwork, $testnet) = Util::parseApiNetwork($network, $testnet); if (is_null($apiEndpoint)) { @@ -65,35 +69,13 @@ public function __construct($apiKey, $apiSecret, $network = 'BTC', $testnet = fa } // normalize network and set bitcoinlib to the right magic-bytes - list($this->network, $this->testnet) = $this->normalizeNetwork($network, $testnet); - $this->setBitcoinLibMagicBytes($this->network, $this->testnet); + $params = Util::normalizeNetwork($apiNetwork, $testnet); + assert($params->isNetwork("bitcoin") || $params->isNetwork("bitcoincash")); + $this->networkParams = $params; $this->client = new RestClient($apiEndpoint, $apiVersion, $apiKey, $apiSecret); } - /** - * normalize network string - * - * @param $network - * @param $testnet - * @return array - * @throws \Exception - */ - protected function normalizeNetwork($network, $testnet) { - return Util::normalizeNetwork($network, $testnet); - } - - /** - * set BitcoinLib to the correct magic-byte defaults for the selected network - * - * @param $network - * @param $testnet - */ - protected function setBitcoinLibMagicBytes($network, $testnet) { - assert($network == "bitcoin" || $network == "bitcoincash"); - Bitcoin::setNetwork($testnet ? NetworkFactory::bitcoinTestnet() : NetworkFactory::bitcoin()); - } - /** * enable CURL debugging output * @@ -128,12 +110,19 @@ public function setCurlDefaultOption($key, $value) { } /** - * @return RestClient + * @return RestClient */ public function getRestClient() { return $this->client; } + /** + * @return NetworkParams + */ + public function getNetworkParams() { + return $this->networkParams; + } + /** * get a single address * @param string $address address hash @@ -552,6 +541,12 @@ public function createNewWallet($options) { } } + private function formatBlocktrailKeys(NetworkInterface $network, array $blocktrailPublicKeys) { + return Util::arrayMapWithIndex(function ($keyIndex, $pubKeyTuple) use ($network) { + return [$keyIndex, BIP32Key::create($network, HierarchicalKeyFactory::fromExtended($pubKeyTuple[0], $network), $pubKeyTuple[1])]; + }, $blocktrailPublicKeys); + } + protected function createNewWalletV1($options) { $walletPath = WalletPath::create($options['key_index']); @@ -597,8 +592,9 @@ protected function createNewWalletV1($options) { } // create primary public key from the created private key + $network = $this->networkParams->getNetwork(); $path = $walletPath->keyIndexPath()->publicPath(); - $primaryPublicKey = BIP32Key::create($primaryPrivateKey, "m")->buildKey($path); + $primaryPublicKey = BIP32Key::create($network, $primaryPrivateKey, "m")->buildKey($path); if (isset($options['backup_mnemonic']) && $options['backup_public_key']) { throw new \InvalidArgumentException("Can't specify Backup Mnemonic and Backup PublicKey"); @@ -621,11 +617,11 @@ protected function createNewWalletV1($options) { } } else { $backupPrivateKey = HierarchicalKeyFactory::fromEntropy((new Bip39SeedGenerator())->getSeed($backupMnemonic, "")); - $backupPublicKey = BIP32Key::create($backupPrivateKey->toPublic(), "M"); + $backupPublicKey = BIP32Key::create($network, $backupPrivateKey->toPublic(), "M"); } // create a checksum of our private key which we'll later use to verify we used the right password - $checksum = $primaryPrivateKey->getPublicKey()->getAddress()->getAddress(); + $checksum = $primaryPrivateKey->getPublicKey()->getAddress()->getAddress($network); // send the public keys to the server to store them // and the mnemonic, which is safe because it's useless without the password @@ -640,9 +636,7 @@ protected function createNewWalletV1($options) { ); // received the blocktrail public keys - $blocktrailPublicKeys = Util::arrayMapWithIndex(function ($keyIndex, $pubKeyTuple) { - return [$keyIndex, BIP32Key::create(HierarchicalKeyFactory::fromExtended($pubKeyTuple[0]), $pubKeyTuple[1])]; - }, $data['blocktrail_public_keys']); + $blocktrailPublicKeys = $this->formatBlocktrailKeys($network, $data['blocktrail_public_keys']); $wallet = new WalletV1( $this, @@ -652,8 +646,6 @@ protected function createNewWalletV1($options) { $backupPublicKey, $blocktrailPublicKeys, $options['key_index'], - $this->network, - $this->testnet, array_key_exists('segwit', $data) ? $data['segwit'] : false, $checksum ); @@ -730,21 +722,22 @@ protected function createNewWalletV2($options) { $backupSeed = isset($options['backup_seed']) ? $options['backup_seed'] : self::randomBits(256); } + $network = $this->networkParams->getNetwork(); if (isset($options['primary_private_key'])) { - $options['primary_private_key'] = BlocktrailSDK::normalizeBIP32Key($options['primary_private_key']); + $options['primary_private_key'] = BlocktrailSDK::normalizeBIP32Key($options['primary_private_key'], $network); } else { - $options['primary_private_key'] = BIP32Key::create(HierarchicalKeyFactory::fromEntropy(new Buffer($primarySeed)), "m"); + $options['primary_private_key'] = BIP32Key::create($network, HierarchicalKeyFactory::fromEntropy(new Buffer($primarySeed)), "m"); } // create primary public key from the created private key $options['primary_public_key'] = $options['primary_private_key']->buildKey($walletPath->keyIndexPath()->publicPath()); if (!isset($options['backup_public_key'])) { - $options['backup_public_key'] = BIP32Key::create(HierarchicalKeyFactory::fromEntropy(new Buffer($backupSeed)), "m")->buildKey("M"); + $options['backup_public_key'] = BIP32Key::create($network, HierarchicalKeyFactory::fromEntropy(new Buffer($backupSeed)), "m")->buildKey("M"); } // create a checksum of our private key which we'll later use to verify we used the right password - $checksum = $options['primary_private_key']->publicKey()->getAddress()->getAddress(); + $checksum = $options['primary_private_key']->publicKey()->getAddress()->getAddress($network); // send the public keys and encrypted data to server $data = $this->storeNewWalletV2( @@ -760,9 +753,7 @@ protected function createNewWalletV2($options) { ); // received the blocktrail public keys - $blocktrailPublicKeys = Util::arrayMapWithIndex(function ($keyIndex, $pubKeyTuple) { - return [$keyIndex, BIP32Key::create(HierarchicalKeyFactory::fromExtended($pubKeyTuple[0]), $pubKeyTuple[1])]; - }, $data['blocktrail_public_keys']); + $blocktrailPublicKeys = $this->formatBlocktrailKeys($network, $data['blocktrail_public_keys']); $wallet = new WalletV2( $this, @@ -773,8 +764,6 @@ protected function createNewWalletV2($options) { $options['backup_public_key'], $blocktrailPublicKeys, $options['key_index'], - $this->network, - $this->testnet, array_key_exists('segwit', $data) ? $data['segwit'] : false, $checksum ); @@ -870,21 +859,22 @@ protected function createNewWalletV3($options) { } } + $network = $this->networkParams->getNetwork(); if (isset($options['primary_private_key'])) { - $options['primary_private_key'] = BlocktrailSDK::normalizeBIP32Key($options['primary_private_key']); + $options['primary_private_key'] = BlocktrailSDK::normalizeBIP32Key($options['primary_private_key'], $network); } else { - $options['primary_private_key'] = BIP32Key::create(HierarchicalKeyFactory::fromEntropy($primarySeed), "m"); + $options['primary_private_key'] = BIP32Key::create($network, HierarchicalKeyFactory::fromEntropy($primarySeed), "m"); } // create primary public key from the created private key $options['primary_public_key'] = $options['primary_private_key']->buildKey($walletPath->keyIndexPath()->publicPath()); if (!isset($options['backup_public_key'])) { - $options['backup_public_key'] = BIP32Key::create(HierarchicalKeyFactory::fromEntropy($backupSeed), "m")->buildKey("M"); + $options['backup_public_key'] = BIP32Key::create($network, HierarchicalKeyFactory::fromEntropy($backupSeed), "m")->buildKey("M"); } // create a checksum of our private key which we'll later use to verify we used the right password - $checksum = $options['primary_private_key']->publicKey()->getAddress()->getAddress(); + $checksum = $options['primary_private_key']->publicKey()->getAddress()->getAddress($network); // send the public keys and encrypted data to server $data = $this->storeNewWalletV3( @@ -900,9 +890,7 @@ protected function createNewWalletV3($options) { ); // received the blocktrail public keys - $blocktrailPublicKeys = Util::arrayMapWithIndex(function ($keyIndex, $pubKeyTuple) { - return [$keyIndex, BIP32Key::create(HierarchicalKeyFactory::fromExtended($pubKeyTuple[0]), $pubKeyTuple[1])]; - }, $data['blocktrail_public_keys']); + $blocktrailPublicKeys = $this->formatBlocktrailKeys($network, $data['blocktrail_public_keys']); $wallet = new WalletV3( $this, @@ -913,8 +901,6 @@ protected function createNewWalletV3($options) { $options['backup_public_key'], $blocktrailPublicKeys, $options['key_index'], - $this->network, - $this->testnet, array_key_exists('segwit', $data) ? $data['segwit'] : false, $checksum ); @@ -946,7 +932,7 @@ protected function createNewWalletV3($options) { * @throws BlocktrailSDKException */ private function verifyPublicBIP32Key(array $bip32Key) { - $hk = HierarchicalKeyFactory::fromExtended($bip32Key[0]); + $hk = HierarchicalKeyFactory::fromExtended($bip32Key[0], $this->networkParams->getNetwork()); if ($hk->isPrivate()) { throw new BlocktrailSDKException('Private key was included in request, abort'); } @@ -1142,8 +1128,6 @@ public function initWallet($options) { $data['backup_public_key'], $data['blocktrail_public_keys'], isset($options['key_index']) ? $options['key_index'] : $data['key_index'], - $this->network, - $this->testnet, array_key_exists('segwit', $data) ? $data['segwit'] : false, $data['checksum'] ); @@ -1158,8 +1142,6 @@ public function initWallet($options) { $data['backup_public_key'], $data['blocktrail_public_keys'], isset($options['key_index']) ? $options['key_index'] : $data['key_index'], - $this->network, - $this->testnet, array_key_exists('segwit', $data) ? $data['segwit'] : false, $data['checksum'] ); @@ -1193,8 +1175,6 @@ public function initWallet($options) { $data['backup_public_key'], $data['blocktrail_public_keys'], isset($options['key_index']) ? $options['key_index'] : $data['key_index'], - $this->network, - $this->testnet, array_key_exists('segwit', $data) ? $data['segwit'] : false, $data['checksum'] ); @@ -1715,7 +1695,7 @@ public function verifyMessage($message, $address, $signature) { // $this->client->post("verify_message", null, ['message' => $message, 'address' => $address, 'signature' => $signature])['result']; $adapter = Bitcoin::getEcAdapter(); - $addr = AddressFactory::fromString($address); + $addr = AddressFactory::fromString($address, $this->networkParams->getNetwork()); if (!$addr instanceof PayToPubKeyHashAddress) { throw new \RuntimeException('Can only verify a message with a pay-to-pubkey-hash address'); } @@ -1823,18 +1803,19 @@ public static function getWebhookPayload($returnObject = false) { } } - public static function normalizeBIP32KeyArray($keys) { - return Util::arrayMapWithIndex(function ($idx, $key) { - return [$idx, self::normalizeBIP32Key($key)]; + public static function normalizeBIP32KeyArray($keys, NetworkInterface $network) { + return Util::arrayMapWithIndex(function ($idx, $key) use ($network) { + return [$idx, self::normalizeBIP32Key($key, $network)]; }, $keys); } /** - * @param array|BIP32Key $key + * @param BIP32Key|array $key + * @param NetworkInterface $network * @return BIP32Key * @throws \Exception */ - public static function normalizeBIP32Key($key) { + public static function normalizeBIP32Key($key, NetworkInterface $network) { if ($key instanceof BIP32Key) { return $key; } @@ -1844,10 +1825,10 @@ public static function normalizeBIP32Key($key) { $hk = $key[0]; if (!($hk instanceof HierarchicalKey)) { - $hk = HierarchicalKeyFactory::fromExtended($hk); + $hk = HierarchicalKeyFactory::fromExtended($hk, $network); } - return BIP32Key::create($hk, $path); + return BIP32Key::create($network, $hk, $path); } else { throw new \Exception("Bad Input"); } diff --git a/src/BlocktrailSDKInterface.php b/src/BlocktrailSDKInterface.php index 0a2d7f1..4778a40 100644 --- a/src/BlocktrailSDKInterface.php +++ b/src/BlocktrailSDKInterface.php @@ -2,6 +2,7 @@ namespace Blocktrail\SDK; +use BitWasp\Bitcoin\Network\NetworkInterface; use Blocktrail\SDK\Connection\RestClient; /** @@ -43,6 +44,11 @@ public function setCurlDefaultOption($key, $value); */ public function getRestClient(); + /** + * @return NetworkParams + */ + public function getNetworkParams(); + /** * get a single address * @param string $address address hash @@ -295,7 +301,7 @@ public function storeNewWalletV1($identifier, $primaryPublicKey, $backupPublicKe * * @param string $identifier the wallet identifier to create * @param array $primaryPublicKey BIP32 extended public key - [key, path] - * @param string $backupPublicKey plain public key + * @param array $backupPublicKey plain public key * @param $encryptedPrimarySeed * @param $encryptedSecret * @param $recoverySecret diff --git a/src/NetworkParams.php b/src/NetworkParams.php new file mode 100644 index 0000000..dc06306 --- /dev/null +++ b/src/NetworkParams.php @@ -0,0 +1,79 @@ +network = $network; + $this->name = $name; + $this->testnet = $testnet; + $this->shortCode = $shortCode; + } + + /** + * @return string + */ + public function getName() { + return $this->name; + } + + /** + * @return string + */ + public function getShortCode() { + return $this->shortCode; + } + + /** + * @param $network + * @return bool + */ + public function isNetwork($network) { + return $this->name === $network; + } + + /** + * @return bool + */ + public function isTestnet() { + return $this->testnet; + } + + /** + * @return NetworkInterface + */ + public function getNetwork() { + return $this->network; + } +} diff --git a/src/TransactionBuilder.php b/src/TransactionBuilder.php index 712021a..7e323ff 100644 --- a/src/TransactionBuilder.php +++ b/src/TransactionBuilder.php @@ -5,6 +5,7 @@ use BitWasp\Bitcoin\Address\AddressFactory; use BitWasp\Bitcoin\Address\AddressInterface; use BitWasp\Bitcoin\Address\SegwitAddress; +use BitWasp\Bitcoin\Network\NetworkInterface; use BitWasp\Bitcoin\Script\Script; use BitWasp\Bitcoin\Script\ScriptFactory; use BitWasp\Bitcoin\Script\ScriptInterface; @@ -39,7 +40,13 @@ class TransactionBuilder { private $feeStrategy = Wallet::FEE_STRATEGY_OPTIMAL; - public function __construct() { + /** + * @var NetworkInterface + */ + private $network; + + public function __construct(NetworkInterface $network) { + $this->network = $network; } /** @@ -54,7 +61,7 @@ public function __construct() { * @return $this */ public function spendOutput($txId, $index, $value = null, $address = null, $scriptPubKey = null, $path = null, $redeemScript = null, $witnessScript = null, $signMode = SignInfo::MODE_SIGN) { - $address = $address instanceof AddressInterface ? $address : AddressFactory::fromString($address); + $address = $address instanceof AddressInterface ? $address : AddressFactory::fromString($address, $this->network); $scriptPubKey = ($scriptPubKey instanceof ScriptInterface) ? $scriptPubKey : (ctype_xdigit($scriptPubKey) ? ScriptFactory::fromHex($scriptPubKey) : null); @@ -96,8 +103,8 @@ public function setUtxos(array $utxos) { * @throws \Exception */ public function addRecipient($address, $value) { - $object = AddressFactory::fromString($address); - if ($object->getAddress() != $address) { + $object = AddressFactory::fromString($address, $this->network); + if ($object->getAddress($this->network) != $address) { throw new \Exception("Invalid address [{$address}]"); } diff --git a/src/Util.php b/src/Util.php index 341e055..38f8e32 100644 --- a/src/Util.php +++ b/src/Util.php @@ -2,7 +2,15 @@ namespace Blocktrail\SDK; +use BitWasp\Bitcoin\Network\NetworkFactory; + abstract class Util { + + /** + * @param callable $fn + * @param array $arr + * @return bool + */ public static function all(callable $fn, array $arr) { $allvalues = array_map($fn, $arr); return count(array_unique($allvalues)) === 1 && end($allvalues) === true; @@ -49,37 +57,40 @@ public static function parseApiNetwork($network, $testnet) { /** * normalize network string * - * @param $network - * @param $testnet - * @return array + * @param string $network + * @param bool $testnet + * @return NetworkParams * @throws \Exception */ public static function normalizeNetwork($network, $testnet) { + $network = strtolower($network); + switch (strtolower($network)) { case 'btc': - case 'bitcoin': - $network = 'bitcoin'; + $name = 'bitcoin'; + $params = NetworkFactory::bitcoin(); break; case 'tbtc': - case 'bitcoin-testnet': - $network = 'bitcoin'; + $name = 'bitcoin'; + $params = NetworkFactory::bitcoinTestnet(); $testnet = true; break; + case 'bcc': - case 'bitcoincash': - $network = 'bitcoincash'; + $name = 'bitcoincash'; + $params = NetworkFactory::bitcoin(); break; case 'tbcc': - case 'bitcoincash-testnet': - $network = 'bitcoincash'; + $name = 'bitcoincash'; + $params = NetworkFactory::bitcoinTestnet(); $testnet = true; break; case 'rbtc': - case 'bitcoin-regtest': - $network = 'bitcoin'; + $name = 'bitcoin'; + $params = NetworkFactory::bitcoinRegtest(); $testnet = true; break; @@ -88,7 +99,7 @@ public static function normalizeNetwork($network, $testnet) { // this comment silences a phpcs error. } - return [$network, $testnet]; + return new NetworkParams($network, $name, $testnet, $params); } public static function arrayMapWithIndex(callable $fn, $arr) { diff --git a/src/Wallet.php b/src/Wallet.php index a5a5b6a..2010904 100644 --- a/src/Wallet.php +++ b/src/Wallet.php @@ -6,6 +6,7 @@ use BitWasp\Bitcoin\Bitcoin; use BitWasp\Bitcoin\Key\Deterministic\HierarchicalKeyFactory; use BitWasp\Bitcoin\MessageSigner\MessageSigner; +use BitWasp\Bitcoin\Network\NetworkInterface; use BitWasp\Bitcoin\Script\P2shScript; use BitWasp\Bitcoin\Script\ScriptFactory; use BitWasp\Bitcoin\Script\ScriptInterface; @@ -96,6 +97,11 @@ abstract class Wallet implements WalletInterface { */ protected $network; + /** + * @var NetworkInterface + */ + protected $networkParams; + /** * testnet yes / no * @@ -158,26 +164,23 @@ abstract class Wallet implements WalletInterface { * @param BIP32Key $backupPublicKey should be BIP32 master public key M/ * @param BIP32Key[] $blocktrailPublicKeys * @param int $keyIndex - * @param string $network - * @param bool $testnet * @param bool $segwit * @param string $checksum * @throws BlocktrailSDKException */ - public function __construct(BlocktrailSDKInterface $sdk, $identifier, array $primaryPublicKeys, $backupPublicKey, array $blocktrailPublicKeys, $keyIndex, $network, $testnet, $segwit, $checksum) { + public function __construct(BlocktrailSDKInterface $sdk, $identifier, array $primaryPublicKeys, $backupPublicKey, array $blocktrailPublicKeys, $keyIndex, $segwit, $checksum) { $this->sdk = $sdk; + $this->networkParams = $sdk->getNetworkParams(); $this->identifier = $identifier; - $this->backupPublicKey = BlocktrailSDK::normalizeBIP32Key($backupPublicKey); - $this->primaryPublicKeys = BlocktrailSDK::normalizeBIP32KeyArray($primaryPublicKeys); - $this->blocktrailPublicKeys = BlocktrailSDK::normalizeBIP32KeyArray($blocktrailPublicKeys); + $this->backupPublicKey = BlocktrailSDK::normalizeBIP32Key($backupPublicKey, $this->networkParams->getNetwork()); + $this->primaryPublicKeys = BlocktrailSDK::normalizeBIP32KeyArray($primaryPublicKeys, $this->networkParams->getNetwork()); + $this->blocktrailPublicKeys = BlocktrailSDK::normalizeBIP32KeyArray($blocktrailPublicKeys, $this->networkParams->getNetwork()); - $this->network = $network; - $this->testnet = $testnet; $this->keyIndex = $keyIndex; $this->checksum = $checksum; - if ($network === "bitcoin") { + if ($this->networkParams->isNetwork("bitcoin")) { if ($segwit) { $chainIdx = self::CHAIN_BTC_DEFAULT; $changeIdx = self::CHAIN_BTC_SEGWIT; @@ -186,7 +189,7 @@ public function __construct(BlocktrailSDKInterface $sdk, $identifier, array $pri $changeIdx = self::CHAIN_BTC_DEFAULT; } } else { - if ($segwit && $network === "bitcoincash") { + if ($segwit && $this->networkParams->isNetwork("bitcoincash")) { throw new BlocktrailSDKException("Received segwit flag for bitcoincash - abort"); } $chainIdx = self::CHAIN_BCC_DEFAULT; @@ -287,7 +290,7 @@ public function upgradeKeyIndex($keyIndex) { if (!isset($this->blocktrailPublicKeys[$keyIndex])) { $path = $pubKey[1]; $pubKey = $pubKey[0]; - $this->blocktrailPublicKeys[$keyIndex] = BIP32Key::create(HierarchicalKeyFactory::fromExtended($pubKey), $path); + $this->blocktrailPublicKeys[$keyIndex] = BIP32Key::fromString($this->networkParams->getNetwork(), $pubKey, $path); } } @@ -406,7 +409,7 @@ public function getRedeemScriptByPath($path) { $redeemScript = $walletScript->isP2SH() ? $walletScript->getRedeemScript() : null; $witnessScript = $walletScript->isP2WSH() ? $walletScript->getWitnessScript() : null; - return [$walletScript->getAddress()->getAddress(), $redeemScript, $witnessScript]; + return [$walletScript->getAddress()->getAddress($this->networkParams->getNetwork()), $redeemScript, $witnessScript]; } /** @@ -415,7 +418,7 @@ public function getRedeemScriptByPath($path) { * @return string */ protected function getAddressFromKey(BIP32Key $key, $path) { - return $this->getWalletScriptFromKey($key, $path)->getAddress()->getAddress(); + return $this->getWalletScriptFromKey($key, $path)->getAddress()->getAddress($this->networkParams->getNetwork()); } /** @@ -533,6 +536,13 @@ public function doDiscovery($gap = 200) { return [$balanceInfo['confirmed'], $balanceInfo['unconfirmed']]; } + /** + * @return TransactionBuilder + */ + public function createTransaction() { + return new TransactionBuilder($this->networkParams->getNetwork()); + } + /** * create, sign and send a transaction * @@ -553,7 +563,7 @@ public function pay(array $outputs, $changeAddress = null, $allowZeroConf = fals $outputs = self::normalizeOutputsStruct($outputs); - $txBuilder = new TransactionBuilder(); + $txBuilder = $this->createTransaction(); $txBuilder->randomizeChangeOutput($randomizeChangeIdx); $txBuilder->setFeeStrategy($feeStrategy); $txBuilder->setChangeAddress($changeAddress); @@ -693,7 +703,7 @@ public function buildTx(TransactionBuilder $txBuilder) { if (SignInfo::MODE_SIGN === $utxo->signMode) { if (!$utxo->path) { - $utxo->path = $this->getPathForAddress($utxo->address->getAddress()); + $utxo->path = $this->getPathForAddress($utxo->address->getAddress($this->networkParams->getNetwork())); } if (!$utxo->redeemScript || !$utxo->witnessScript) { @@ -744,7 +754,7 @@ public function buildTx(TransactionBuilder $txBuilder) { if (isset($out['scriptPubKey'])) { $txb->output($out['value'], $out['scriptPubKey']); } elseif (isset($out['address'])) { - $txb->payToAddress($out['value'], AddressFactory::fromString($out['address'])); + $txb->payToAddress($out['value'], AddressFactory::fromString($out['address'], $this->networkParams->getNetwork())); } else { throw new \Exception(); } @@ -1011,7 +1021,7 @@ protected function signTransaction(Transaction $tx, array $signInfo) { }, $signInfo), '$signInfo should be SignInfo[]'); $sigHash = SigHash::ALL; - if ($this->network === "bitcoincash") { + if ($this->networkParams->isNetwork("bitcoincash")) { $sigHash |= SigHash::BITCOINCASH; $signer->redeemBitcoinCash(true); } @@ -1117,7 +1127,7 @@ protected function createChecksumVerificationSignature() { $privKey = $this->primaryPrivateKey->key(); $pubKey = $this->primaryPrivateKey->publicKey(); - $address = $pubKey->getAddress()->getAddress(); + $address = $pubKey->getAddress()->getAddress($this->networkParams->getNetwork()); $signer = new MessageSigner(Bitcoin::getEcAdapter()); $signed = $signer->sign($address, $privKey->getPrivateKey()); diff --git a/src/WalletInterface.php b/src/WalletInterface.php index c3d2040..7a2a1d1 100644 --- a/src/WalletInterface.php +++ b/src/WalletInterface.php @@ -284,4 +284,9 @@ public function addresses($page = 1, $limit = 20, $sortDir = 'asc'); * @return array associative array containing the response */ public function utxos($page = 1, $limit = 20, $sortDir = 'asc'); + + /** + * @return TransactionBuilder + */ + public function createTransaction(); } diff --git a/src/WalletSweeper.php b/src/WalletSweeper.php index 1d12079..c8c700b 100644 --- a/src/WalletSweeper.php +++ b/src/WalletSweeper.php @@ -6,6 +6,7 @@ use BitWasp\Bitcoin\Bitcoin; use BitWasp\Bitcoin\Key\Deterministic\HierarchicalKeyFactory; use BitWasp\Bitcoin\Network\NetworkFactory; +use BitWasp\Bitcoin\Network\NetworkInterface; use BitWasp\Bitcoin\Script\P2shScript; use BitWasp\Bitcoin\Script\ScriptFactory; use BitWasp\Bitcoin\Script\WitnessScript; @@ -25,15 +26,9 @@ abstract class WalletSweeper { /** * network to use - currently only supporting 'bitcoin' - * @var string + * @var NetworkParams */ - protected $network; - - /** - * using testnet or not - * @var bool - */ - protected $testnet; + protected $networkParams; /** * backup private key @@ -76,26 +71,24 @@ abstract class WalletSweeper { * @param BufferInterface $backupSeed * @param array $blocktrailPublicKeys = * @param UnspentOutputFinder $utxoFinder - * @param string $network - * @param bool $testnet + * @param string $network + * @param bool $testnet * @throws \Exception */ - public function __construct(BufferInterface $primarySeed, BufferInterface $backupSeed, array $blocktrailPublicKeys, UnspentOutputFinder $utxoFinder, $network = 'btc', $testnet = false) { - // normalize network and set bitcoinlib to the right magic-bytes - list($this->network, $this->testnet) = $this->normalizeNetwork($network, $testnet); + public function __construct(BufferInterface $primarySeed, BufferInterface $backupSeed, array $blocktrailPublicKeys, UnspentOutputFinder $utxoFinder, $network, $tesnet = false) { - assert($this->network == "bitcoin"); - Bitcoin::setNetwork($this->testnet ? NetworkFactory::bitcoinTestnet() : NetworkFactory::bitcoin()); + $params = Util::normalizeNetwork($network, $tesnet); + assert($params->isNetwork("bitcoin") || $params->isNetwork("bitcoincash")); + $network = $params->getNetwork(); //create BIP32 keys for the Blocktrail public keys foreach ($blocktrailPublicKeys as $blocktrailKey) { - $this->blocktrailPublicKeys[$blocktrailKey['keyIndex']] = BlocktrailSDK::normalizeBIP32Key([$blocktrailKey['pubkey'], $blocktrailKey['path']]); + $this->blocktrailPublicKeys[$blocktrailKey['keyIndex']] = BlocktrailSDK::normalizeBIP32Key([$blocktrailKey['pubkey'], $blocktrailKey['path']], $network); } $this->utxoFinder = $utxoFinder; - - $this->primaryPrivateKey = BIP32Key::create(HierarchicalKeyFactory::fromEntropy($primarySeed), "m"); - $this->backupPrivateKey = BIP32Key::create(HierarchicalKeyFactory::fromEntropy($backupSeed), "m"); + $this->primaryPrivateKey = BIP32Key::create($network, HierarchicalKeyFactory::fromEntropy($primarySeed), "m"); + $this->backupPrivateKey = BIP32Key::create($network, HierarchicalKeyFactory::fromEntropy($backupSeed), "m"); } /** @@ -114,38 +107,6 @@ public function disableLogging() { $this->utxoFinder->disableLogging(); } - - /** - * normalize network string - * - * @param $network - * @param $testnet - * @return array - * @throws \Exception - */ - protected function normalizeNetwork($network, $testnet) { - switch (strtolower($network)) { - case 'btc': - case 'bitcoin': - $network = 'bitcoin'; - - break; - - case 'tbtc': - case 'bitcoin-testnet': - $network = 'bitcoin'; - $testnet = true; - - break; - - default: - throw new \Exception("Unknown network [{$network}]"); - } - - return [$network, $testnet]; - } - - /** * generate multisig address for given path * @@ -162,14 +123,14 @@ protected function createAddress($path) { $this->getBlocktrailPublicKey($path)->buildKey($path)->publicKey() ]), false); - if ($this->network !== "bitcoincash" && (int) $path[2] === 2) { + if ($this->networkParams->getName() !== "bitcoincash" && (int) $path[2] === 2) { $witnessScript = new WitnessScript($multisig); $redeemScript = new P2shScript($witnessScript); - $address = $redeemScript->getAddress()->getAddress(); + $address = $redeemScript->getAddress()->getAddress($this->networkParams->getNetwork()); } else { $witnessScript = null; $redeemScript = new P2shScript($multisig); - $address = $redeemScript->getAddress()->getAddress(); + $address = $redeemScript->getAddress()->getAddress($this->networkParams->getNetwork()); } return [$address, $redeemScript, $witnessScript]; diff --git a/src/WalletV1.php b/src/WalletV1.php index 21e1d40..797264b 100644 --- a/src/WalletV1.php +++ b/src/WalletV1.php @@ -25,14 +25,12 @@ class WalletV1 extends Wallet { * @param BIP32Key $backupPublicKey should be BIP32 master public key M/ * @param BIP32Key[] $blocktrailPublicKeys * @param int $keyIndex - * @param string $network - * @param bool $testnet * @param string $checksum */ - public function __construct(BlocktrailSDKInterface $sdk, $identifier, $primaryMnemonic, array $primaryPublicKeys, $backupPublicKey, array $blocktrailPublicKeys, $keyIndex, $network, $testnet, $segwit, $checksum) { + public function __construct(BlocktrailSDKInterface $sdk, $identifier, $primaryMnemonic, array $primaryPublicKeys, $backupPublicKey, array $blocktrailPublicKeys, $keyIndex, $segwit, $checksum) { $this->primaryMnemonic = $primaryMnemonic; - parent::__construct($sdk, $identifier, $primaryPublicKeys, $backupPublicKey, $blocktrailPublicKeys, $keyIndex, $network, $testnet, $segwit, $checksum); + parent::__construct($sdk, $identifier, $primaryPublicKeys, $backupPublicKey, $blocktrailPublicKeys, $keyIndex, $segwit, $checksum); } /** @@ -72,10 +70,11 @@ public function unlock($options, callable $fn = null) { $primaryPrivateKey = HierarchicalKeyFactory::fromEntropy($primarySeed); } - $this->primaryPrivateKey = BIP32Key::create($primaryPrivateKey, "m"); + $network = $this->networkParams->getNetwork(); + $this->primaryPrivateKey = BIP32Key::create($network, $primaryPrivateKey, "m"); // create checksum (address) of the primary privatekey to compare to the stored checksum - $checksum = $this->primaryPrivateKey->key()->getPublicKey()->getAddress()->getAddress(); + $checksum = $this->primaryPrivateKey->key()->getPublicKey()->getAddress()->getAddress($network); if ($checksum != $this->checksum) { throw new \Exception("Checksum [{$checksum}] does not match [{$this->checksum}], most likely due to incorrect password"); } diff --git a/src/WalletV2.php b/src/WalletV2.php index f461452..1dffa7b 100644 --- a/src/WalletV2.php +++ b/src/WalletV2.php @@ -13,33 +13,43 @@ class WalletV2 extends Wallet { + /** + * @var string + */ protected $encryptedPrimarySeed; + /** + * @var string + */ protected $encryptedSecret; + /** + * @var string|null + */ protected $secret = null; + /** + * @var string|null + */ protected $primarySeed = null; /** - * @param BlocktrailSDKInterface $sdk SDK instance used to do requests - * @param string $identifier identifier of the wallet + * @param BlocktrailSDKInterface $sdk SDK instance used to do requests + * @param string $identifier identifier of the wallet * @param string $encryptedPrimarySeed - * @param $encryptedSecret + * @param string $encryptedSecret * @param BIP32Key[] $primaryPublicKeys * @param BIP32Key $backupPublicKey * @param BIP32Key[] $blocktrailPublicKeys * @param int $keyIndex - * @param string $network - * @param bool $testnet * @param bool $segwit * @param string $checksum */ - public function __construct(BlocktrailSDKInterface $sdk, $identifier, $encryptedPrimarySeed, $encryptedSecret, $primaryPublicKeys, $backupPublicKey, $blocktrailPublicKeys, $keyIndex, $network, $testnet, $segwit, $checksum) { + public function __construct(BlocktrailSDKInterface $sdk, $identifier, $encryptedPrimarySeed, $encryptedSecret, $primaryPublicKeys, $backupPublicKey, $blocktrailPublicKeys, $keyIndex, $segwit, $checksum) { $this->encryptedPrimarySeed = $encryptedPrimarySeed; $this->encryptedSecret = $encryptedSecret; - parent::__construct($sdk, $identifier, $primaryPublicKeys, $backupPublicKey, $blocktrailPublicKeys, $keyIndex, $network, $testnet, $segwit, $checksum); + parent::__construct($sdk, $identifier, $primaryPublicKeys, $backupPublicKey, $blocktrailPublicKeys, $keyIndex, $segwit, $checksum); } /** @@ -92,10 +102,11 @@ public function unlock($options, callable $fn = null) { $primaryPrivateKey = HierarchicalKeyFactory::fromEntropy($seedBuffer); } - $this->primaryPrivateKey = $primaryPrivateKey instanceof BIP32Key ? $primaryPrivateKey : BIP32Key::create($primaryPrivateKey, "m"); + $network = $this->networkParams->getNetwork(); + $this->primaryPrivateKey = $primaryPrivateKey instanceof BIP32Key ? $primaryPrivateKey : BIP32Key::create($network, $primaryPrivateKey, "m"); // create checksum (address) of the primary privatekey to compare to the stored checksum - $checksum = $this->primaryPrivateKey->publicKey()->getAddress()->getAddress(); + $checksum = $this->primaryPrivateKey->publicKey()->getAddress()->getAddress($network); if ($checksum != $this->checksum) { throw new \Exception("Checksum [{$checksum}] does not match [{$this->checksum}], most likely due to incorrect password"); } diff --git a/src/WalletV3.php b/src/WalletV3.php index a33e73a..11ad359 100644 --- a/src/WalletV3.php +++ b/src/WalletV3.php @@ -35,19 +35,17 @@ class WalletV3 extends Wallet protected $primarySeed = null; /** - * @param BlocktrailSDKInterface $sdk SDK instance used to do requests - * @param string $identifier identifier of the wallet + * @param BlocktrailSDKInterface $sdk SDK instance used to do requests + * @param string $identifier identifier of the wallet * @param BufferInterface $encryptedPrimarySeed * @param BufferInterface $encryptedSecret * @param BIP32Key[] $primaryPublicKeys * @param BIP32Key $backupPublicKey * @param BIP32Key[] $blocktrailPublicKeys * @param int $keyIndex - * @param string $network - * @param bool $testnet * @param string $checksum */ - public function __construct(BlocktrailSDKInterface $sdk, $identifier, $encryptedPrimarySeed, $encryptedSecret, $primaryPublicKeys, $backupPublicKey, $blocktrailPublicKeys, $keyIndex, $network, $testnet, $segwit, $checksum) { + public function __construct(BlocktrailSDKInterface $sdk, $identifier, $encryptedPrimarySeed, $encryptedSecret, $primaryPublicKeys, $backupPublicKey, $blocktrailPublicKeys, $keyIndex, $segwit, $checksum) { if ($encryptedPrimarySeed !== null && !($encryptedPrimarySeed instanceof Buffer)) { throw new \InvalidArgumentException('Encrypted Primary Seed must be a Buffer or null'); } @@ -57,7 +55,7 @@ public function __construct(BlocktrailSDKInterface $sdk, $identifier, $encrypted $this->encryptedPrimarySeed = $encryptedPrimarySeed; $this->encryptedSecret = $encryptedSecret; - parent::__construct($sdk, $identifier, $primaryPublicKeys, $backupPublicKey, $blocktrailPublicKeys, $keyIndex, $network, $testnet, $segwit, $checksum); + parent::__construct($sdk, $identifier, $primaryPublicKeys, $backupPublicKey, $blocktrailPublicKeys, $keyIndex, $segwit, $checksum); } /** @@ -100,10 +98,11 @@ public function unlock($options, callable $fn = null) { throw new WalletDecryptException("Failed to decrypt primary seed with secret"); } } - $this->primaryPrivateKey = BIP32Key::create(HierarchicalKeyFactory::fromEntropy($this->primarySeed), "m"); + $network = $this->networkParams->getNetwork(); + $this->primaryPrivateKey = BIP32Key::create($network, HierarchicalKeyFactory::fromEntropy($this->primarySeed), "m"); // create checksum (address) of the primary privatekey to compare to the stored checksum - $checksum = $this->primaryPrivateKey->publicKey()->getAddress()->getAddress(); + $checksum = $this->primaryPrivateKey->publicKey()->getAddress()->getAddress($network); if ($checksum != $this->checksum) { throw new \Exception("Checksum [{$checksum}] does not match [{$this->checksum}], most likely due to incorrect password"); } diff --git a/tests/BIP32KeyTest.php b/tests/BIP32KeyTest.php index e2dd76f..29722f2 100644 --- a/tests/BIP32KeyTest.php +++ b/tests/BIP32KeyTest.php @@ -3,28 +3,34 @@ namespace Blocktrail\SDK\Tests; use BitWasp\Bitcoin\Key\Deterministic\HierarchicalKeyFactory; +use BitWasp\Bitcoin\Network\NetworkFactory; use Blocktrail\SDK\Bitcoin\BIP32Key; use Blocktrail\SDK\Bitcoin\BIP32Path; class BIP32KeyTest extends \PHPUnit_Framework_TestCase { public function testBIP32Key() { + $network = NetworkFactory::bitcoin(); + $e = null; try { - BIP32Key::create(HierarchicalKeyFactory::fromExtended("xpub1")); + HierarchicalKeyFactory::fromExtended("xpub1", $network); } catch (\Exception $e) { + // key was invalid } $this->assertTrue(!!$e, "an exception should be thrown"); - $k = BIP32Key::create(HierarchicalKeyFactory::fromExtended("xprv9s21ZrQH143K44ed3A1NBn3udjmm6qHRpX4Da47ZpRdhqxpkhCWwMFWNpFbSkxAtkZ2s2345tyX5GdTuDWQYZ9jZPuTbkkBeHx3h6RmzL8J"), "m"); + $xprv = "xprv9s21ZrQH143K44ed3A1NBn3udjmm6qHRpX4Da47ZpRdhqxpkhCWwMFWNpFbSkxAtkZ2s2345tyX5GdTuDWQYZ9jZPuTbkkBeHx3h6RmzL8J"; + + $k = BIP32Key::create($network, HierarchicalKeyFactory::fromExtended($xprv, $network), "m"); - $this->assertEquals("xprv9s21ZrQH143K44ed3A1NBn3udjmm6qHRpX4Da47ZpRdhqxpkhCWwMFWNpFbSkxAtkZ2s2345tyX5GdTuDWQYZ9jZPuTbkkBeHx3h6RmzL8J", $k->key()->toExtendedKey()); + $this->assertEquals($xprv, $k->key()->toExtendedKey($network)); $this->assertEquals("m", $k->path()); - $this->assertEquals(["xprv9s21ZrQH143K44ed3A1NBn3udjmm6qHRpX4Da47ZpRdhqxpkhCWwMFWNpFbSkxAtkZ2s2345tyX5GdTuDWQYZ9jZPuTbkkBeHx3h6RmzL8J", "m"], $k->tuple()); + $this->assertEquals([$xprv, "m"], $k->tuple()); $c1 = $k->buildKey("m/1"); - $this->assertEquals("xprv9uyqGTZ6imvYQgfExoMn4ja1cD8DhHqhTxZVDBKSeETMqd87Nx5vexxEnuXYFMFi5ViQFdvw7AQt3RovuTivKdSFmygPBadBmuCVLszfHDc", $c1->key()->toExtendedKey()); + $this->assertEquals("xprv9uyqGTZ6imvYQgfExoMn4ja1cD8DhHqhTxZVDBKSeETMqd87Nx5vexxEnuXYFMFi5ViQFdvw7AQt3RovuTivKdSFmygPBadBmuCVLszfHDc", $c1->key()->toExtendedKey($network)); $this->assertEquals("m/1", $c1->path()); $this->assertEquals(["xprv9uyqGTZ6imvYQgfExoMn4ja1cD8DhHqhTxZVDBKSeETMqd87Nx5vexxEnuXYFMFi5ViQFdvw7AQt3RovuTivKdSFmygPBadBmuCVLszfHDc", "m/1"], $c1->tuple()); diff --git a/tests/BlocktrailTestCase.php b/tests/BlocktrailTestCase.php index e6fd1a1..ef2875b 100644 --- a/tests/BlocktrailTestCase.php +++ b/tests/BlocktrailTestCase.php @@ -33,6 +33,7 @@ protected function tearDown() { //called after each test $this->cleanUp(); } + protected function onNotSuccessfulTest(\Exception $e) { //called when a test fails $this->cleanUp(); @@ -41,7 +42,7 @@ protected function onNotSuccessfulTest(\Exception $e) { protected function cleanUp() { //cleanup any records that were created - $client = $this->setupBlocktrailSDK(); + $client = $this->setupBlocktrailSDK("BTC", true); if (array_key_exists('wallets', $this->cleanupData)) { $count = 0; diff --git a/tests/SizeEstimationTest.php b/tests/SizeEstimationTest.php index 2688f2f..76ac1e1 100644 --- a/tests/SizeEstimationTest.php +++ b/tests/SizeEstimationTest.php @@ -6,7 +6,6 @@ use BitWasp\Bitcoin\Network\NetworkFactory; use BitWasp\Bitcoin\Script\ScriptInterface; use BitWasp\Bitcoin\Script\ScriptFactory; - use BitWasp\Bitcoin\Script\ScriptInfo\Multisig; use BitWasp\Bitcoin\Script\WitnessProgram; use BitWasp\Bitcoin\Script\WitnessScript; @@ -17,7 +16,6 @@ use BitWasp\Bitcoin\Transaction\TransactionOutput; use Blocktrail\SDK\SizeEstimation; use \BitWasp\Bitcoin\Key\PrivateKeyFactory; -use Blocktrail\SDK\TransactionBuilder; use Blocktrail\SDK\UTXO; use Blocktrail\SDK\Bitcoin\BIP32Path; use Blocktrail\SDK\Wallet; @@ -99,15 +97,16 @@ public function multisigProvider() { 'Kz2Lm2hzjPWhv3WW9Na5HUKi4qBxoTfv8fNYAU6KV6TZYVGdK5HW', ]; + $network = NetworkFactory::bitcoin(); /** * @var PrivateKeyInterface[] $uncompressed * @var PrivateKeyInterface[] $compressed */ - $uncompressed = array_map(function ($wif) { - return PrivateKeyFactory::fromWif($wif, null); + $uncompressed = array_map(function ($wif) use ($network) { + return PrivateKeyFactory::fromWif($wif, null, $network); }, $u); - $compressed = array_map(function ($wif) { - return PrivateKeyFactory::fromWif($wif, null); + $compressed = array_map(function ($wif) use ($network) { + return PrivateKeyFactory::fromWif($wif, null, $network); }, $c); $fixtures = []; @@ -155,8 +154,9 @@ public function multisigFormProvider() { 'Kz2Lm2hzjPWhv3WW9Na5HUKi4qBxoTfv8fNYAU6KV6TZYVGdK5HW', ]; - $pubs = array_map(function ($wif) { - return PrivateKeyFactory::fromWif($wif)->getPublicKey(); + $network = NetworkFactory::bitcoin(); + $pubs = array_map(function ($wif) use ($network) { + return PrivateKeyFactory::fromWif($wif, null, $network)->getPublicKey(); }, $c); $multisig = ScriptFactory::scriptPubKey()->multisig(2, $pubs); @@ -212,8 +212,9 @@ public function multisigUtxoProvider() { 'Kz2Lm2hzjPWhv3WW9Na5HUKi4qBxoTfv8fNYAU6KV6TZYVGdK5HW', ]; - $pubs = array_map(function ($wif) { - return PrivateKeyFactory::fromWif($wif)->getPublicKey(); + $network = NetworkFactory::bitcoin(); + $pubs = array_map(function ($wif) use ($network) { + return PrivateKeyFactory::fromWif($wif, null, $network)->getPublicKey(); }, $c); $multisig = ScriptFactory::scriptPubKey()->multisig(2, $pubs); @@ -270,8 +271,9 @@ public function testEquivalentWithOld() { 'Kz2Lm2hzjPWhv3WW9Na5HUKi4qBxoTfv8fNYAU6KV6TZYVGdK5HW', ]; - $pubs = array_map(function ($wif) { - return PrivateKeyFactory::fromWif($wif)->getPublicKey(); + $network = NetworkFactory::bitcoin(); + $pubs = array_map(function ($wif) use ($network) { + return PrivateKeyFactory::fromWif($wif, null, $network)->getPublicKey(); }, $c); $multisig = ScriptFactory::scriptPubKey()->multisig(2, $pubs); diff --git a/tests/UtilTest.php b/tests/UtilTest.php index c68e2f6..6af02f1 100644 --- a/tests/UtilTest.php +++ b/tests/UtilTest.php @@ -28,6 +28,41 @@ public function parseApiNetworkProvider() { ]; } + private function checkTestnetNormalize($network, $name) + { + $res = Util::normalizeNetwork($network, true); + $this->assertEquals($name, $res->getName()); + $this->assertEquals($network, $res->getShortCode()); + $this->assertTrue($res->isTestnet()); + + $res = Util::normalizeNetwork($network, false); + $this->assertEquals($name, $res->getName()); + $this->assertEquals($network, $res->getShortCode()); + $this->assertTrue($res->isTestnet()); + } + + private function checkTestnetToggle($network, $name) + { + $res = Util::normalizeNetwork($network, false); + $this->assertEquals($name, $res->getName()); + $this->assertEquals($network, $res->getShortCode()); + $this->assertFalse($res->isTestnet()); + + $res = Util::normalizeNetwork($network, true); + $this->assertEquals($name, $res->getName()); + $this->assertEquals($network, $res->getShortCode()); + $this->assertTrue($res->isTestnet()); + } + + public function testNormalizeNetwork() { + $this->checkTestnetNormalize("tbtc", "bitcoin"); + $this->checkTestnetNormalize("tbcc", "bitcoincash"); + $this->checkTestnetNormalize("rbtc", "bitcoin"); + + $this->checkTestnetToggle("btc", "bitcoin"); + $this->checkTestnetToggle("bcc", "bitcoincash"); + } + /** * @param $network * @param $testnet diff --git a/tests/V3Crypt/FullTest.php b/tests/V3Crypt/FullTest.php index 4fa7582..4194709 100644 --- a/tests/V3Crypt/FullTest.php +++ b/tests/V3Crypt/FullTest.php @@ -3,6 +3,7 @@ namespace Blocktrail\SDK\Tests\V3Crypt; use BitWasp\Bitcoin\Key\Deterministic\HierarchicalKeyFactory; +use BitWasp\Bitcoin\Network\NetworkFactory; use BitWasp\Buffertools\Buffer; use BitWasp\Buffertools\BufferInterface; use Blocktrail\SDK\V3Crypt\Encryption; @@ -50,8 +51,9 @@ public function testDecryptionOnly(BufferInterface $password, $encryptedPrimaryS $decodedPrimarySeed = EncryptionMnemonic::decode($encryptedPrimarySeedMnemonic); $decryptedPrimarySeed = Encryption::decrypt($decodedPrimarySeed, $decryptedSecret); + $network = NetworkFactory::bitcoin(); $hdnode = HierarchicalKeyFactory::fromEntropy($decryptedPrimarySeed); - $this->assertEquals($checksum, $hdnode->getPublicKey()->getAddress()->getAddress()); + $this->assertEquals($checksum, $hdnode->getPublicKey()->getAddress()->getAddress($network)); } /** diff --git a/tests/WalletTest.php b/tests/WalletTest.php index 3af76a8..22ec564 100644 --- a/tests/WalletTest.php +++ b/tests/WalletTest.php @@ -10,6 +10,7 @@ use BitWasp\Bitcoin\Address\SegwitAddress; use BitWasp\Bitcoin\Key\Deterministic\HierarchicalKeyFactory; use BitWasp\Bitcoin\Mnemonic\Bip39\Bip39SeedGenerator; +use BitWasp\Bitcoin\Network\NetworkFactory; use BitWasp\Bitcoin\Script\Classifier\OutputClassifier; use BitWasp\Bitcoin\Script\ScriptInterface; use BitWasp\Bitcoin\Script\ScriptType; @@ -27,7 +28,6 @@ use Blocktrail\SDK\Connection\Exceptions\ObjectNotFound; use Blocktrail\SDK\Exceptions\BlocktrailSDKException; use Blocktrail\SDK\SignInfo; -use Blocktrail\SDK\TransactionBuilder; use Blocktrail\SDK\Wallet; use Blocktrail\SDK\WalletInterface; use Blocktrail\SDK\WalletPath; @@ -132,19 +132,18 @@ protected function _createTestWallet(BlocktrailSDKInterface $client, $identifier $encryptedSecret = CryptoJSAES::encrypt($secret, $passphrase); // still using BIP39 to get seedhex to keep all fixtures the same + $network = $client->getNetworkParams()->getNetwork(); $seed = (new Bip39SeedGenerator())->getSeed($primaryMnemonic, $passphrase); - $primaryPrivateKey = BIP32Key::create(HierarchicalKeyFactory::fromEntropy($seed), "m"); + $primaryPrivateKey = BIP32Key::create($network, HierarchicalKeyFactory::fromEntropy($seed), "m"); $primaryPublicKey = $primaryPrivateKey->buildKey((string)$walletPath->keyIndexPath()->publicPath()); $encryptedPrimarySeed = CryptoJSAES::encrypt(base64_encode($seed->getBinary()), $secret); // still using BIP39 to get seedhex to keep all fixtures the same - $backupPrivateKey = BIP32Key::create(HierarchicalKeyFactory::fromEntropy((new Bip39SeedGenerator())->getSeed($backupMnemonic, "")), "m"); + $backupPrivateKey = BIP32Key::create($network, HierarchicalKeyFactory::fromEntropy((new Bip39SeedGenerator())->getSeed($backupMnemonic, "")), "m"); $backupPublicKey = $backupPrivateKey->buildKey("M"); - $testnet = true; - - $checksum = $primaryPrivateKey->publicKey()->getAddress()->getAddress(); + $checksum = $primaryPrivateKey->publicKey()->getAddress()->getAddress($network); $result = $client->storeNewWalletV2( $identifier, @@ -169,8 +168,6 @@ protected function _createTestWallet(BlocktrailSDKInterface $client, $identifier $backupPublicKey, $blocktrailPublicKeys, $keyIndex, - 'bitcoin', - $testnet, false, $checksum ); @@ -183,15 +180,16 @@ protected function _createTestWallet(BlocktrailSDKInterface $client, $identifier } public function testBIP32() { - $masterkey = HierarchicalKeyFactory::fromExtended("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ"); + $network = NetworkFactory::bitcoinTestnet(); + $masterkey = HierarchicalKeyFactory::fromExtended("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $network); $this->assertEquals("022f6b9339309e89efb41ecabae60e1d40b7809596c68c03b05deb5a694e33cd26", $masterkey->getPublicKey()->getHex()); - $this->assertEquals("tpubDAtJthHcm9MJwmHp4r2UwSTmiDYZWHbQUMqySJ1koGxQpRNSaJdyL2Ab8wwtMm5DsMMk3v68299LQE6KhT8XPQWzxPLK5TbTHKtnrmjV8Gg", $masterkey->derivePath("0")->toExtendedKey()); - $this->assertEquals("tpubDDfqpEKGqEVa5FbdLtwezc6Xgn81teTFFVA69ZfJBHp4UYmUmhqVZMmqXeJBDahvySZrPjpwMy4gKfNfrxuFHmzo1r6srB4MrsDKWbwEw3d", $masterkey->derivePath("0/0")->toExtendedKey()); + $this->assertEquals("tpubDAtJthHcm9MJwmHp4r2UwSTmiDYZWHbQUMqySJ1koGxQpRNSaJdyL2Ab8wwtMm5DsMMk3v68299LQE6KhT8XPQWzxPLK5TbTHKtnrmjV8Gg", $masterkey->derivePath("0")->toExtendedKey($network)); + $this->assertEquals("tpubDDfqpEKGqEVa5FbdLtwezc6Xgn81teTFFVA69ZfJBHp4UYmUmhqVZMmqXeJBDahvySZrPjpwMy4gKfNfrxuFHmzo1r6srB4MrsDKWbwEw3d", $masterkey->derivePath("0/0")->toExtendedKey($network)); $this->assertEquals( "tpubDHNy3kAG39ThyiwwsgoKY4iRenXDRtce8qdCFJZXPMCJg5dsCUHayp84raLTpvyiNA9sXPob5rgqkKvkN8S7MMyXbnEhGJMW64Cf4vFAoaF", - HierarchicalKeyFactory::fromEntropy(Buffer::hex("000102030405060708090a0b0c0d0e0f"))->derivePath("M/0'/1/2'/2/1000000000")->toExtendedPublicKey() + HierarchicalKeyFactory::fromEntropy(Buffer::hex("000102030405060708090a0b0c0d0e0f"))->derivePath("M/0'/1/2'/2/1000000000")->toExtendedPublicKey($network) ); } @@ -205,11 +203,12 @@ public function testCreateWallet() { $wallets = $client->allWallets(); $this->assertTrue(count($wallets) > 0); + $network = $client->getNetworkParams()->getNetwork(); $this->assertEquals($identifier, $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()->toExtendedKey()); - $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("M/9999'")->key()->toExtendedKey()); + $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("m/9999'")->key()->toExtendedKey($network)); + $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("M/9999'")->key()->toExtendedKey($network)); // get a new pair list($path, $address) = $wallet->getNewAddressPair(); @@ -233,8 +232,7 @@ public function testCreateWallet() { list($path, $address) = $wallet->getNewAddressPair(); $this->assertTrue(strpos($path, "M/9999'/0/") === 0); - $this->assertEquals($address, AddressFactory::fromString($address)->getAddress()); - + $this->assertEquals($address, AddressFactory::fromString($address, $network)->getAddress($network)); } private function checkP2sh(ScriptInterface $spk, ScriptInterface $rs) { @@ -289,8 +287,9 @@ public function testChecksBackupKey() { $client = $this->setupBlocktrailSDK(); $backupMnemonic = "give pause forget seed dance crawl situate hole give"; - $backupPrivateKey = BIP32Key::create(HierarchicalKeyFactory::fromEntropy((new Bip39SeedGenerator())->getSeed($backupMnemonic, "")), "m"); - $backupKey = $backupPrivateKey->buildKey("M")->key()->toExtendedPublicKey(); + $network = $client->getNetworkParams()->getNetwork(); + $backupPrivateKey = BIP32Key::create($network, HierarchicalKeyFactory::fromEntropy((new Bip39SeedGenerator())->getSeed($backupMnemonic, "")), "m"); + $backupKey = $backupPrivateKey->buildKey("M")->key()->toExtendedPublicKey($network); try { $client->initWallet([ @@ -359,14 +358,18 @@ public function testSegwitWalletTransaction() $this->assertTrue($segwitwallet->isSegwit()); + $network = $client->getNetworkParams()->getNetwork(); list ($path, $address) = $segwitwallet->getNewAddressPair(); - $addrObj = AddressFactory::fromString($address); + $addrObj = AddressFactory::fromString($address, $network)->getAddress($network); + + $segwitScript = $segwitwallet->getWalletScriptByPath($path); + $this->assertEquals($segwitScript->getAddress()->getAddress($network), $address); // Send back to unittest-transaction-sw $rand = random_int(1, 3); - $builder = (new TransactionBuilder()) + $builder = $segwitwallet->createTransaction() ->addRecipient($address, BlocktrailSDK::toSatoshi(0.015) * $rand) ->setFeeStrategy(Wallet::FEE_STRATEGY_BASE_FEE); @@ -404,7 +407,7 @@ public function testSegwitWalletTransaction() $utxo = $tx['outputs'][$utxoIdx]; - $spendSegwit = (new TransactionBuilder()) + $spendSegwit = $segwitwallet->createTransaction() ->addRecipient($segwitwallet->getNewAddress(), BlocktrailSDK::toSatoshi(0.010) * $rand) ->spendOutput($tx['hash'], $utxoIdx, $utxo['value'], $utxo['address'], $utxo['script_hex'], $path) ->setFeeStrategy(Wallet::FEE_STRATEGY_BASE_FEE); @@ -466,8 +469,9 @@ public function testSendToBech32() $random = random_int(1, 4); $receiveAmount = BlocktrailSDK::toSatoshi(0.0001) * $random; - $builder = (new TransactionBuilder()) - ->addRecipient($addr->getAddress(), $receiveAmount) + $network = $client->getNetworkParams()->getNetwork(); + $builder = $unittestWallet->createTransaction() + ->addRecipient($addr->getAddress($network), $receiveAmount) ->setFeeStrategy(Wallet::FEE_STRATEGY_OPTIMAL); $builder = $unittestWallet->coinSelectionForTxBuilder($builder, false, true); @@ -532,7 +536,7 @@ public function testSpendMixedUtxoTypes() $fundTx = []; $fundTxOutIdx = []; - $builder = (new TransactionBuilder()) + $builder = $segwitwallet->createTransaction() ->addRecipient($unittestAddress, BlocktrailSDK::toSatoshi(0.0003)) ->setFeeStrategy(Wallet::FEE_STRATEGY_OPTIMAL); @@ -553,7 +557,15 @@ public function testSpendMixedUtxoTypes() $outIdx = $j; } } + + if ($outIdx === -1) { + var_dump($fundTxHash[$i]); + var_dump($value); + var_dump($fundTx[$i]); + } + $this->assertNotEquals(-1, $outIdx, "should find the output we created"); + $builder->spendOutput($fundTxHash[$i], $outIdx, $value, $address, null, $path, null, null); $fundTxOutIdx[$i] = $outIdx; } @@ -576,13 +588,13 @@ public function testSpendMixedUtxoTypes() $this->assertInstanceOf(TransactionInterface::class, $tx); } - private function checkWalletScriptAgainstAddressPair(WalletInterface $wallet, $chainIdx) + private function checkWalletScriptAgainstAddressPair(BlocktrailSDKInterface $sdk, WalletInterface $wallet, $chainIdx) { list ($path, $address) = $wallet->getNewAddressPair($chainIdx); $this->assertTrue(strpos("M/9999'/{$chainIdx}/", $path) !== -1); $defaultScript = $wallet->getWalletScriptByPath($path); - $this->assertEquals($defaultScript->getAddress()->getAddress(), $address); + $this->assertEquals($defaultScript->getAddress()->getAddress($sdk->getNetworkParams()->getNetwork()), $address); $classifier = new OutputClassifier(); @@ -629,9 +641,9 @@ public function testWalletGetNewAddressPair() { $this->assertTrue($wallet->isSegwit()); - $this->checkWalletScriptAgainstAddressPair($wallet, Wallet::CHAIN_BTC_DEFAULT); + $this->checkWalletScriptAgainstAddressPair($client, $wallet, Wallet::CHAIN_BTC_DEFAULT); - $this->checkWalletScriptAgainstAddressPair($wallet, Wallet::CHAIN_BTC_SEGWIT); + $this->checkWalletScriptAgainstAddressPair($client, $wallet, Wallet::CHAIN_BTC_SEGWIT); $nestedP2wshPath = "M/9999'/2/0"; $script = $wallet->getWalletScriptByPath($nestedP2wshPath); @@ -643,7 +655,8 @@ public function testWalletGetNewAddressPair() { $this->checkP2sh($script->getScriptPubKey(), $script->getRedeemScript()); $this->checkP2wsh($script->getRedeemScript(), $script->getWitnessScript()); - $this->assertEquals("2N3j4Vx3D9LPumjtRbRe2RJpwVocvCCkHKh", $script->getAddress()->getAddress()); + $network = $client->getNetworkParams()->getNetwork(); + $this->assertEquals("2N3j4Vx3D9LPumjtRbRe2RJpwVocvCCkHKh", $script->getAddress()->getAddress($network)); $this->assertEquals("a91472f4fbf13b171d3acfe3316264835cc4767549a187", $script->getScriptPubKey()->getHex()); $this->assertEquals("0020bbb712fe7c81544b588b6f6d8d915b4e6f485ba2b43a70761e1dd9c68e391094", $script->getRedeemScript()->getHex()); $this->assertEquals("5221020c9855979a83bedd4f45f47938f1008038b703506dd097bf81b76c4c8127482e2102381a4cf140c24080523b5e63082496b514e99657d3506444b7f77c12176635302102ff3475471c1f6caa27def90b97ceee72e2e9c569ebe59232fc19ef3db9e7ecbc53ae", $script->getWitnessScript()->getHex()); @@ -669,16 +682,17 @@ public function testWalletTransaction() { $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()->toExtendedKey()); - $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("M/9999'")->key()->toExtendedKey()); + $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("m/9999'")->tuple()[0]); + $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("M/9999'")->tuple()[0]); 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(); + $network = $client->getNetworkParams()->getNetwork(); $this->assertTrue(strpos($path, "M/9999'/0/") === 0); - $this->assertTrue(AddressFactory::fromString($address)->getAddress() == $address); + $this->assertTrue(AddressFactory::fromString($address, $network)->getAddress($network) == $address); ///* $value = BlocktrailSDK::toSatoshi(0.0002); @@ -730,7 +744,7 @@ public function testWalletTransaction() { */ $value = BlocktrailSDK::toSatoshi(0.0002); $moon = "MOOOOOOOOOOOOON!"; - $txBuilder = new TransactionBuilder(); + $txBuilder = $wallet->createTransaction(); $txBuilder->randomizeChangeOutput(false); $txBuilder->addRecipient($address, $value); $txBuilder->addOpReturn($moon); @@ -773,8 +787,8 @@ public function testWalletTransactionWithoutMnemonics() { $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()->toExtendedKey()); - $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("M/9999'")->key()->toExtendedKey()); + $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("m/9999'")->tuple()[0]); + $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("M/9999'")->tuple()[0]); list($confirmed, $unconfirmed) = $wallet->getBalance(); $this->assertGreaterThan(0, $confirmed + $unconfirmed, "positive unconfirmed balance"); @@ -783,7 +797,8 @@ public function testWalletTransactionWithoutMnemonics() { list($path, $address) = $wallet->getNewAddressPair(); $this->assertTrue(strpos($path, "M/9999'/0/") === 0); - $this->assertTrue(AddressFactory::fromString($address)->getAddress() == $address); + $network = $client->getNetworkParams()->getNetwork(); + $this->assertEquals($address, AddressFactory::fromString($address, $network)->getAddress($network)); $value = BlocktrailSDK::toSatoshi(0.0002); $txHash = $wallet->pay([ @@ -809,8 +824,8 @@ public function testDiscoveryAndKeyIndexUpgrade() { $this->assertEquals($identifier, $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()->toExtendedKey()); - $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("M/9999'")->key()->toExtendedKey()); + $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("m/9999'")->tuple()[0]); + $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("M/9999'")->tuple()[0]); // get a new pair list($path, $address) = $wallet->getNewAddressPair(); @@ -827,7 +842,7 @@ public function testDiscoveryAndKeyIndexUpgrade() { $wallet->upgradeKeyIndex(10000); - $this->assertEquals("tpubD9m9hziKhYQExWgzMUNXdYMNUtourv96sjTUS9jJKdo3EDJAnCBJooMPm6vGSmkNTNAmVt988dzNfNY12YYzk9E6PkA7JbxYeZBFy4XAaCp", $wallet->getBlocktrailPublicKey("m/10000")->key()->toExtendedKey()); + $this->assertEquals("tpubD9m9hziKhYQExWgzMUNXdYMNUtourv96sjTUS9jJKdo3EDJAnCBJooMPm6vGSmkNTNAmVt988dzNfNY12YYzk9E6PkA7JbxYeZBFy4XAaCp", $wallet->getBlocktrailPublicKey("m/10000")->tuple()[0]); $this->assertEquals("2N9ZLKXgs12JQKXvLkngn7u9tsYaQ5kXJmk", $wallet->getAddressByPath("M/10000'/0/0")); @@ -872,8 +887,8 @@ public function testBadPasswordWallet() { $this->assertEquals($identifier, $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()->toExtendedKey()); - $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("M/9999'")->key()->toExtendedKey()); + $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("m/9999'")->tuple()[0]); + $this->assertEquals("tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ", $wallet->getBlocktrailPublicKey("M/9999'")->tuple()[0]); // get a new pair list($path, $address) = $wallet->getNewAddressPair(); @@ -1002,8 +1017,10 @@ public function testNewBlankWithoutMnemonicsWalletV2() { $client = $this->setupBlocktrailSDK(); $identifier = $this->getRandomTestIdentifier(); - $primaryPrivateKey = BIP32Key::create(HierarchicalKeyFactory::generateMasterKey(), 'm'); - $backupPublicKey = BIP32Key::create(HierarchicalKeyFactory::generateMasterKey()->toPublic(), 'M'); + + $network = $client->getNetworkParams()->getNetwork(); + $primaryPrivateKey = BIP32Key::create($network, HierarchicalKeyFactory::generateMasterKey(), 'm'); + $backupPublicKey = BIP32Key::create($network, HierarchicalKeyFactory::generateMasterKey()->toPublic(), 'M'); /** * @var $wallet \Blocktrail\SDK\Wallet @@ -1328,7 +1345,7 @@ public function testSegwitBuildTx() { /** @var Transaction $tx */ /** @var SignInfo[] $signInfo */ list($tx, $signInfo) = $wallet->buildTx( - (new TransactionBuilder()) + $wallet->createTransaction() ->spendOutput( $txid, $vout, @@ -1357,11 +1374,12 @@ public function testSegwitBuildTx() { $this->assertEquals($outValue, $outputTotal); $this->assertEquals($expectfee, $fee); + $network = $client->getNetworkParams()->getNetwork(); // assert the input(s) $this->assertEquals(1, count($tx->getInputs())); $this->assertEquals($txid, $tx->getInput(0)->getOutPoint()->getTxId()->getHex()); $this->assertEquals(0, $tx->getInput(0)->getOutPoint()->getVout()); - $this->assertEquals($address, AddressFactory::fromOutputScript($signInfo[0]->output->getScript())->getAddress()); + $this->assertEquals($address, AddressFactory::fromOutputScript($signInfo[0]->output->getScript())->getAddress($network)); $this->assertEquals($scriptPubKey, $signInfo[0]->output->getScript()->getHex()); $this->assertEquals($value, $signInfo[0]->output->getValue()); $this->assertEquals($path, $signInfo[0]->path); @@ -1376,7 +1394,7 @@ public function testSegwitBuildTx() { // assert the output(s) $this->assertEquals(1, count($tx->getOutputs())); - $this->assertEquals("2N6DJMnoS3xaxpCSDRMULgneCghA1dKJBmT", AddressFactory::fromOutputScript($tx->getOutput(0)->getScript())->getAddress()); + $this->assertEquals("2N6DJMnoS3xaxpCSDRMULgneCghA1dKJBmT", AddressFactory::fromOutputScript($tx->getOutput(0)->getScript())->getAddress($network)); $this->assertEquals($outValue, $tx->getOutput(0)->getValue()); } @@ -1398,7 +1416,7 @@ public function testBuildTx() { /** @var Transaction $tx */ /** @var SignInfo[] $signInfo */ list($tx, $signInfo) = $wallet->buildTx( - (new TransactionBuilder()) + $wallet->createTransaction() ->spendOutput( "0d8703ab259b03a757e37f3cdba7fc4543e8d47f7cc3556e46c0aeef6f5e832b", 0, @@ -1435,11 +1453,13 @@ public function testBuildTx() { $this->assertEquals(BlocktrailSDK::toSatoshi(0.001), $outputTotal); $this->assertEquals(BlocktrailSDK::toSatoshi(0.0001), $fee); + $network = $client->getNetworkParams()->getNetwork(); + // assert the input(s) $this->assertEquals(2, count($tx->getInputs())); $this->assertEquals("0d8703ab259b03a757e37f3cdba7fc4543e8d47f7cc3556e46c0aeef6f5e832b", $tx->getInput(0)->getOutPoint()->getTxId()->getHex()); $this->assertEquals(0, $tx->getInput(0)->getOutPoint()->getVout()); - $this->assertEquals("2N9os1eAZXrWwKWgo7ppDRsY778PyxbScYH", AddressFactory::fromOutputScript($signInfo[0]->output->getScript())->getAddress()); + $this->assertEquals("2N9os1eAZXrWwKWgo7ppDRsY778PyxbScYH", AddressFactory::fromOutputScript($signInfo[0]->output->getScript())->getAddress($network)); $this->assertEquals("a914b5ae3a9950fa66efa4aab2c21ce4a4275e7c95b487", $signInfo[0]->output->getScript()->getHex()); $this->assertEquals(10000, $signInfo[0]->output->getValue()); $this->assertEquals("M/9999'/0/5", $signInfo[0]->path); @@ -1450,7 +1470,7 @@ public function testBuildTx() { $this->assertEquals("be837cd8f04911f3ee10d010823a26665980f7bb6c9ed307d798cb968ca00128", $tx->getInput(1)->getOutPoint()->getTxId()->getHex()); $this->assertEquals(0, $tx->getInput(1)->getOutPoint()->getVout()); - $this->assertEquals("2NBV4sxQMYNyBbUeZkmPTZYtpdmKcuZ4Cyw", AddressFactory::fromOutputScript($signInfo[1]->output->getScript())->getAddress()); + $this->assertEquals("2NBV4sxQMYNyBbUeZkmPTZYtpdmKcuZ4Cyw", AddressFactory::fromOutputScript($signInfo[1]->output->getScript())->getAddress($network)); $this->assertEquals("a914c8107bd24bae2c521a5a9f56c9b72e047eafa1f587", $signInfo[1]->output->getScript()->getHex()); $this->assertEquals(100000, $signInfo[1]->output->getValue()); $this->assertEquals("M/9999'/0/12", $signInfo[1]->path); @@ -1461,7 +1481,7 @@ public function testBuildTx() { // assert the output(s) $this->assertEquals(1, count($tx->getOutputs())); - $this->assertEquals("2N7C5Jn1LasbEK9mvHetBYXaDnQACXkarJe", AddressFactory::fromOutputScript($tx->getOutput(0)->getScript())->getAddress()); + $this->assertEquals("2N7C5Jn1LasbEK9mvHetBYXaDnQACXkarJe", AddressFactory::fromOutputScript($tx->getOutput(0)->getScript())->getAddress($network)); $this->assertEquals(100000, $tx->getOutput(0)->getValue()); /* @@ -1474,7 +1494,7 @@ public function testBuildTx() { try { /** @var Transaction $tx */ list($tx, $signInfo) = $wallet->buildTx( - (new TransactionBuilder()) + $wallet->createTransaction() ->spendOutput( "ed6458f2567c3a6847e96ca5244c8eb097efaf19fd8da2d25ec33d54a49b4396", 0, @@ -1501,7 +1521,7 @@ public function testBuildTx() { ]; /** @var Transaction $tx */ list($tx, $signInfo) = $wallet->buildTx( - (new TransactionBuilder()) + $wallet->createTransaction() ->spendOutput( "ed6458f2567c3a6847e96ca5244c8eb097efaf19fd8da2d25ec33d54a49b4396", 0, @@ -1543,7 +1563,7 @@ public function testBuildTx() { $this->assertEquals(BlocktrailSDK::toSatoshi(0.9999), $outputTotal); $this->assertEquals(BlocktrailSDK::toSatoshi(0.0001), $fee); $this->assertEquals(14, count($tx->getOutputs())); - $this->assertEquals("2N6DJMnoS3xaxpCSDRMULgneCghA1dKJBmT", AddressFactory::fromOutputScript($tx->getOutput(13)->getScript())->getAddress()); + $this->assertEquals("2N6DJMnoS3xaxpCSDRMULgneCghA1dKJBmT", AddressFactory::fromOutputScript($tx->getOutput(13)->getScript())->getAddress($network)); $this->assertEquals(99860000, $tx->getOutput(13)->getValue()); /* @@ -1563,7 +1583,7 @@ public function testBuildTx() { ]; /** @var Transaction $tx */ list($tx, $signInfo) = $wallet->buildTx( - (new TransactionBuilder()) + $wallet->createTransaction() ->spendOutput( "ed6458f2567c3a6847e96ca5244c8eb097efaf19fd8da2d25ec33d54a49b4396", 0, @@ -1611,7 +1631,7 @@ public function testBuildTx() { $this->assertEquals(BlocktrailSDK::toSatoshi(0.9999), $outputTotal); $this->assertEquals(BlocktrailSDK::toSatoshi(0.0001), $fee); $this->assertEquals(20, count($tx->getOutputs())); - $this->assertEquals("2N6DJMnoS3xaxpCSDRMULgneCghA1dKJBmT", AddressFactory::fromOutputScript($tx->getOutput(19)->getScript())->getAddress()); + $this->assertEquals("2N6DJMnoS3xaxpCSDRMULgneCghA1dKJBmT", AddressFactory::fromOutputScript($tx->getOutput(19)->getScript())->getAddress($network)); $this->assertEquals(BlocktrailSDK::toSatoshi(0.9980), $tx->getOutput(19)->getValue()); /* @@ -1633,7 +1653,7 @@ public function testBuildTx() { ]; /** @var Transaction $tx */ list($tx, $signInfo) = $wallet->buildTx( - (new TransactionBuilder()) + $wallet->createTransaction() ->spendOutput( "ed6458f2567c3a6847e96ca5244c8eb097efaf19fd8da2d25ec33d54a49b4396", 0, @@ -1682,7 +1702,7 @@ public function testBuildTx() { $this->assertEquals(BlocktrailSDK::toSatoshi(0.9998), $outputTotal); $this->assertEquals(BlocktrailSDK::toSatoshi(0.0002), $fee); $this->assertEquals(21, count($tx->getOutputs())); - $this->assertEquals("2N6DJMnoS3xaxpCSDRMULgneCghA1dKJBmT", AddressFactory::fromOutputScript($tx->getOutput(20)->getScript())->getAddress()); + $this->assertEquals("2N6DJMnoS3xaxpCSDRMULgneCghA1dKJBmT", AddressFactory::fromOutputScript($tx->getOutput(20)->getScript())->getAddress($network)); $this->assertEquals(BlocktrailSDK::toSatoshi(0.9978), $tx->getOutput(20)->getValue()); /* @@ -1703,7 +1723,7 @@ public function testBuildTx() { ]; /** @var Transaction $tx */ list($tx, $signInfo) = $wallet->buildTx( - (new TransactionBuilder()) + $wallet->createTransaction() ->spendOutput( "ed6458f2567c3a6847e96ca5244c8eb097efaf19fd8da2d25ec33d54a49b4396", 0, @@ -1775,7 +1795,7 @@ public function testBuildTx() { ]; /** @var Transaction $tx */ list($tx, $signInfo) = $wallet->buildTx( - (new TransactionBuilder()) + $wallet->createTransaction() ->spendOutput( "ed6458f2567c3a6847e96ca5244c8eb097efaf19fd8da2d25ec33d54a49b4396", 0, @@ -1833,7 +1853,7 @@ public function testBuildTx() { ]; /** @var Transaction $tx */ list($tx, $signInfo) = $wallet->buildTx( - (new TransactionBuilder()) + $wallet->createTransaction() ->spendOutput( "ed6458f2567c3a6847e96ca5244c8eb097efaf19fd8da2d25ec33d54a49b4396", 0, @@ -1870,7 +1890,7 @@ public function testBuildTx() { ]; /** @var Transaction $tx */ list($tx, $signInfo) = $wallet->buildTx( - (new TransactionBuilder()) + $wallet->createTransaction() ->spendOutput( "ed6458f2567c3a6847e96ca5244c8eb097efaf19fd8da2d25ec33d54a49b4396", 0, @@ -1901,8 +1921,9 @@ public function testBuildTx() { $this->assertEquals(BlocktrailSDK::toSatoshi(0.0019), $outputTotal); $this->assertEquals(BlocktrailSDK::toSatoshi(0.0001), $fee); - $this->assertEquals("2NAUFsSps9S2mEnhaWZoaufwyuCaVPUv8op", AddressFactory::fromOutputScript($tx->getOutput(0)->getScript())->getAddress()); - $this->assertEquals("2NAUFsSps9S2mEnhaWZoaufwyuCaVPUv8op", AddressFactory::fromOutputScript($tx->getOutput(1)->getScript())->getAddress()); + $network = $client->getNetworkParams()->getNetwork(); + $this->assertEquals("2NAUFsSps9S2mEnhaWZoaufwyuCaVPUv8op", AddressFactory::fromOutputScript($tx->getOutput(0)->getScript())->getAddress($network)); + $this->assertEquals("2NAUFsSps9S2mEnhaWZoaufwyuCaVPUv8op", AddressFactory::fromOutputScript($tx->getOutput(1)->getScript())->getAddress($network)); } protected function getTx(BlocktrailSDK $client, $txId, $retries = 3) {