diff options
-rwxr-xr-x | test/functional/wallet_importmulti.py | 232 |
1 files changed, 142 insertions, 90 deletions
diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py index b6159063e1..6cf854882e 100755 --- a/test/functional/wallet_importmulti.py +++ b/test/functional/wallet_importmulti.py @@ -3,13 +3,24 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the importmulti RPC.""" +from collections import namedtuple +from test_framework.address import ( + key_to_p2pkh, + key_to_p2sh_p2wpkh, + key_to_p2wpkh, +) from test_framework.messages import sha256 from test_framework.script import ( CScript, OP_0, + OP_CHECKSIG, + OP_DUP, + OP_EQUAL, + OP_EQUALVERIFY, + OP_HASH160, OP_NOP, - hash160 + hash160, ) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( @@ -20,6 +31,16 @@ from test_framework.util import ( hex_str_to_bytes ) +Key = namedtuple('Key', ['privkey', + 'pubkey', + 'p2pkh_script', + 'p2pkh_addr', + 'p2wpkh_script', + 'p2wpkh_addr', + 'p2sh_p2wpkh_script', + 'p2sh_p2wpkh_redeem_script', + 'p2sh_p2wpkh_addr']) + class ImportMultiTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 @@ -32,6 +53,23 @@ class ImportMultiTest(BitcoinTestFramework): def setup_network(self): self.setup_nodes() + def get_key(self): + """Generate a fresh key on node0 + + Returns a named tuple of privkey, pubkey and all address and scripts.""" + addr = self.nodes[0].getnewaddress() + pubkey = self.nodes[0].getaddressinfo(addr)['pubkey'] + pkh = hash160(hex_str_to_bytes(pubkey)) + return Key(self.nodes[0].dumpprivkey(addr), + pubkey, + CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG]).hex(), # p2pkh + key_to_p2pkh(pubkey), # p2pkh addr + CScript([OP_0, pkh]).hex(), # p2wpkh + key_to_p2wpkh(pubkey), # p2wpkh addr + CScript([OP_HASH160, hash160(CScript([OP_0, pkh])), OP_EQUAL]).hex(), # p2sh-p2wpkh + CScript([OP_0, pkh]).hex(), # p2sh-p2wpkh redeem script + key_to_p2sh_p2wpkh(pubkey)) # p2sh-p2wpkh addr + def run_test(self): self.log.info("Mining blocks...") self.nodes[0].generate(1) @@ -55,20 +93,21 @@ class ImportMultiTest(BitcoinTestFramework): # Bitcoin Address (implicit non-internal) self.log.info("Should import an address") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + key = self.get_key() + address = key.p2pkh_addr result = self.nodes[1].importmulti([{ "scriptPubKey": { - "address": address['address'] + "address": address }, "timestamp": "now", }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) assert_equal(address_assert['timestamp'], timestamp) assert_equal(address_assert['ischange'], False) - watchonly_address = address['address'] + watchonly_address = address watchonly_timestamp = timestamp self.log.info("Should not import an invalid address") @@ -84,14 +123,14 @@ class ImportMultiTest(BitcoinTestFramework): # ScriptPubKey + internal self.log.info("Should import a scriptPubKey with internal flag") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + key = self.get_key() result = self.nodes[1].importmulti([{ - "scriptPubKey": address['scriptPubKey'], + "scriptPubKey": key.p2pkh_script, "timestamp": "now", "internal": True }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(key.p2pkh_addr) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) assert_equal(address_assert['timestamp'], timestamp) @@ -99,9 +138,9 @@ class ImportMultiTest(BitcoinTestFramework): # ScriptPubKey + internal + label self.log.info("Should not allow a label to be specified when internal is true") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + key = self.get_key() result = self.nodes[1].importmulti([{ - "scriptPubKey": address['scriptPubKey'], + "scriptPubKey": key.p2pkh_script, "timestamp": "now", "internal": True, "label": "Example label" @@ -112,8 +151,9 @@ class ImportMultiTest(BitcoinTestFramework): # Nonstandard scriptPubKey + !internal self.log.info("Should not import a nonstandard scriptPubKey without internal flag") - nonstandardScriptPubKey = address['scriptPubKey'] + bytes_to_hex_str(CScript([OP_NOP])) - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + nonstandardScriptPubKey = key.p2pkh_script + bytes_to_hex_str(CScript([OP_NOP])) + key = self.get_key() + address = key.p2pkh_addr result = self.nodes[1].importmulti([{ "scriptPubKey": nonstandardScriptPubKey, "timestamp": "now", @@ -121,73 +161,77 @@ class ImportMultiTest(BitcoinTestFramework): assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -8) assert_equal(result[0]['error']['message'], 'Internal must be set to true for nonstandard scriptPubKey imports.') - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) assert_equal('timestamp' in address_assert, False) # Address + Public key + !Internal(explicit) self.log.info("Should import an address with public key") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + key = self.get_key() + address = key.p2pkh_addr result = self.nodes[1].importmulti([{ "scriptPubKey": { - "address": address['address'] + "address": address }, "timestamp": "now", - "pubkeys": [address['pubkey']], + "pubkeys": [key.pubkey], "internal": False }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) assert_equal(address_assert['timestamp'], timestamp) # ScriptPubKey + Public key + internal self.log.info("Should import a scriptPubKey with internal and with public key") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + key = self.get_key() + address = key.p2pkh_addr request = [{ - "scriptPubKey": address['scriptPubKey'], + "scriptPubKey": key.p2pkh_script, "timestamp": "now", - "pubkeys": [address['pubkey']], + "pubkeys": [key.pubkey], "internal": True }] result = self.nodes[1].importmulti(requests=request) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) assert_equal(address_assert['timestamp'], timestamp) # Nonstandard scriptPubKey + Public key + !internal self.log.info("Should not import a nonstandard scriptPubKey without internal and with public key") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + key = self.get_key() + address = key.p2pkh_addr request = [{ "scriptPubKey": nonstandardScriptPubKey, "timestamp": "now", - "pubkeys": [address['pubkey']] + "pubkeys": [key.pubkey] }] result = self.nodes[1].importmulti(requests=request) assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -8) assert_equal(result[0]['error']['message'], 'Internal must be set to true for nonstandard scriptPubKey imports.') - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) assert_equal('timestamp' in address_assert, False) # Address + Private key + !watchonly self.log.info("Should import an address with private key") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + key = self.get_key() + address = key.p2pkh_addr result = self.nodes[1].importmulti([{ "scriptPubKey": { - "address": address['address'] + "address": address }, "timestamp": "now", - "keys": [self.nodes[0].dumpprivkey(address['address'])] + "keys": [key.privkey] }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], True) assert_equal(address_assert['timestamp'], timestamp) @@ -195,10 +239,10 @@ class ImportMultiTest(BitcoinTestFramework): self.log.info("Should not import an address with private key if is already imported") result = self.nodes[1].importmulti([{ "scriptPubKey": { - "address": address['address'] + "address": address }, "timestamp": "now", - "keys": [self.nodes[0].dumpprivkey(address['address'])] + "keys": [key.privkey] }]) assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -4) @@ -206,50 +250,53 @@ class ImportMultiTest(BitcoinTestFramework): # Address + Private key + watchonly self.log.info("Should not import an address with private key and with watchonly") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + key = self.get_key() + address = key.p2pkh_addr result = self.nodes[1].importmulti([{ "scriptPubKey": { - "address": address['address'] + "address": address }, "timestamp": "now", - "keys": [self.nodes[0].dumpprivkey(address['address'])], + "keys": [key.privkey], "watchonly": True }]) assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -8) assert_equal(result[0]['error']['message'], 'Watch-only addresses should not include private keys') - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) assert_equal('timestamp' in address_assert, False) # ScriptPubKey + Private key + internal self.log.info("Should import a scriptPubKey with internal and with private key") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + key = self.get_key() + address = key.p2pkh_addr result = self.nodes[1].importmulti([{ - "scriptPubKey": address['scriptPubKey'], + "scriptPubKey": key.p2pkh_script, "timestamp": "now", - "keys": [self.nodes[0].dumpprivkey(address['address'])], + "keys": [key.privkey], "internal": True }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], True) assert_equal(address_assert['timestamp'], timestamp) # Nonstandard scriptPubKey + Private key + !internal self.log.info("Should not import a nonstandard scriptPubKey without internal and with private key") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + key = self.get_key() + address = key.p2pkh_addr result = self.nodes[1].importmulti([{ "scriptPubKey": nonstandardScriptPubKey, "timestamp": "now", - "keys": [self.nodes[0].dumpprivkey(address['address'])] + "keys": [key.privkey] }]) assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -8) assert_equal(result[0]['error']['message'], 'Internal must be set to true for nonstandard scriptPubKey imports.') - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) assert_equal('timestamp' in address_assert, False) @@ -359,75 +406,79 @@ class ImportMultiTest(BitcoinTestFramework): # Address + Public key + !Internal + Wrong pubkey self.log.info("Should not import an address with a wrong public key") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) - address2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + key = self.get_key() + address = key.p2pkh_addr + wrong_key = self.get_key().pubkey result = self.nodes[1].importmulti([{ "scriptPubKey": { - "address": address['address'] + "address": address }, "timestamp": "now", - "pubkeys": [address2['pubkey']] + "pubkeys": [wrong_key] }]) assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -5) assert_equal(result[0]['error']['message'], 'Key does not match address destination') - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) assert_equal('timestamp' in address_assert, False) # ScriptPubKey + Public key + internal + Wrong pubkey self.log.info("Should not import a scriptPubKey with internal and with a wrong public key") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) - address2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + key = self.get_key() + address = key.p2pkh_addr + wrong_key = self.get_key().pubkey request = [{ - "scriptPubKey": address['scriptPubKey'], + "scriptPubKey": key.p2pkh_script, "timestamp": "now", - "pubkeys": [address2['pubkey']], + "pubkeys": [wrong_key], "internal": True }] result = self.nodes[1].importmulti(request) assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -5) assert_equal(result[0]['error']['message'], 'Key does not match address destination') - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) assert_equal('timestamp' in address_assert, False) # Address + Private key + !watchonly + Wrong private key self.log.info("Should not import an address with a wrong private key") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) - address2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + key = self.get_key() + address = key.p2pkh_addr + wrong_privkey = self.get_key().privkey result = self.nodes[1].importmulti([{ "scriptPubKey": { - "address": address['address'] + "address": address }, "timestamp": "now", - "keys": [self.nodes[0].dumpprivkey(address2['address'])] + "keys": [wrong_privkey] }]) assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -5) assert_equal(result[0]['error']['message'], 'Key does not match address destination') - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) assert_equal('timestamp' in address_assert, False) # ScriptPubKey + Private key + internal + Wrong private key self.log.info("Should not import a scriptPubKey with internal and with a wrong private key") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) - address2 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) + key = self.get_key() + address = key.p2pkh_addr + wrong_privkey = self.get_key().privkey result = self.nodes[1].importmulti([{ - "scriptPubKey": address['scriptPubKey'], + "scriptPubKey": key.p2pkh_script, "timestamp": "now", - "keys": [self.nodes[0].dumpprivkey(address2['address'])], + "keys": [wrong_privkey], "internal": True }]) assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -5) assert_equal(result[0]['error']['message'], 'Key does not match address destination') - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) assert_equal('timestamp' in address_assert, False) @@ -459,54 +510,57 @@ class ImportMultiTest(BitcoinTestFramework): # Bad or missing timestamps self.log.info("Should throw on invalid or missing timestamp values") assert_raises_rpc_error(-3, 'Missing required timestamp field for key', - self.nodes[1].importmulti, [{"scriptPubKey": address['scriptPubKey']}]) + self.nodes[1].importmulti, [{"scriptPubKey": key.p2pkh_script}]) assert_raises_rpc_error(-3, 'Expected number or "now" timestamp value for key. got type string', self.nodes[1].importmulti, [{ - "scriptPubKey": address['scriptPubKey'], + "scriptPubKey": key.p2pkh_script, "timestamp": "" }]) # Import P2WPKH address as watch only self.log.info("Should import a P2WPKH address as watch only") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="bech32")) + key = self.get_key() + address = key.p2wpkh_addr result = self.nodes[1].importmulti([{ "scriptPubKey": { - "address": address['address'] + "address": address }, "timestamp": "now", }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['solvable'], False) # Import P2WPKH address with public key but no private key self.log.info("Should import a P2WPKH address and public key as solvable but not spendable") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="bech32")) + key = self.get_key() + address = key.p2wpkh_addr result = self.nodes[1].importmulti([{ "scriptPubKey": { - "address": address['address'] + "address": address }, "timestamp": "now", - "pubkeys": [address['pubkey']] + "pubkeys": [key.pubkey] }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['ismine'], False) assert_equal(address_assert['solvable'], True) # Import P2WPKH address with key and check it is spendable self.log.info("Should import a P2WPKH address with key") - address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="bech32")) + key = self.get_key() + address = key.p2wpkh_addr result = self.nodes[1].importmulti([{ "scriptPubKey": { - "address": address['address'] + "address": address }, "timestamp": "now", - "keys": [self.nodes[0].dumpprivkey(address['address'])] + "keys": [key.privkey] }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].getaddressinfo(address['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], True) @@ -542,18 +596,17 @@ class ImportMultiTest(BitcoinTestFramework): assert_equal(address_assert['sigsrequired'], 2) # P2SH-P2WPKH address with no redeemscript or public or private key - sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="p2sh-segwit")) - pubkeyhash = hash160(hex_str_to_bytes(sig_address_1['pubkey'])) - pkscript = CScript([OP_0, pubkeyhash]) + key = self.get_key() + address = key.p2sh_p2wpkh_addr self.log.info("Should import a p2sh-p2wpkh without redeem script or keys") result = self.nodes[1].importmulti([{ "scriptPubKey": { - "address": sig_address_1['address'] + "address": address }, "timestamp": "now" }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].getaddressinfo(sig_address_1['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['solvable'], False) assert_equal(address_assert['ismine'], False) @@ -561,32 +614,31 @@ class ImportMultiTest(BitcoinTestFramework): self.log.info("Should import a p2sh-p2wpkh with respective redeem script and pubkey as solvable") result = self.nodes[1].importmulti([{ "scriptPubKey": { - "address": sig_address_1['address'] + "address": address }, "timestamp": "now", - "redeemscript": bytes_to_hex_str(pkscript), - "pubkeys": [sig_address_1['pubkey']] + "redeemscript": key.p2sh_p2wpkh_redeem_script, + "pubkeys": [key.pubkey] }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].getaddressinfo(sig_address_1['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['solvable'], True) assert_equal(address_assert['ismine'], False) # P2SH-P2WPKH address + redeemscript + private key - sig_address_1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress(address_type="p2sh-segwit")) - pubkeyhash = hash160(hex_str_to_bytes(sig_address_1['pubkey'])) - pkscript = CScript([OP_0, pubkeyhash]) + key = self.get_key() + address = key.p2sh_p2wpkh_addr self.log.info("Should import a p2sh-p2wpkh with respective redeem script and private keys") result = self.nodes[1].importmulti([{ "scriptPubKey": { - "address": sig_address_1['address'] + "address": address }, "timestamp": "now", - "redeemscript": bytes_to_hex_str(pkscript), - "keys": [self.nodes[0].dumpprivkey(sig_address_1['address'])] + "redeemscript": key.p2sh_p2wpkh_redeem_script, + "keys": [key.privkey] }]) assert_equal(result[0]['success'], True) - address_assert = self.nodes[1].getaddressinfo(sig_address_1['address']) + address_assert = self.nodes[1].getaddressinfo(address) assert_equal(address_assert['solvable'], True) assert_equal(address_assert['ismine'], True) |