Changeset View
Changeset View
Standalone View
Standalone View
test/functional/wallet_importmulti.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2014-2019 The Bitcoin Core developers | # Copyright (c) 2014-2019 The Bitcoin Core developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
"""Test the importmulti RPC.""" | """Test the importmulti RPC. | ||||
Test importmulti by generating keys on node0, importing the scriptPubKeys and | |||||
addresses on node1 and then testing the address info for the different address | |||||
variants. | |||||
- `get_key()` and `get_multisig()` are called to generate keys on node0 and | |||||
return the privkeys, pubkeys and all variants of scriptPubKey and address.""" | |||||
from collections import namedtuple | from collections import namedtuple | ||||
from test_framework.address import ( | from test_framework.address import ( | ||||
key_to_p2pkh, | key_to_p2pkh, | ||||
script_to_p2sh, | |||||
) | ) | ||||
from test_framework.script import ( | from test_framework.script import ( | ||||
CScript, | CScript, | ||||
OP_2, | |||||
OP_3, | |||||
OP_CHECKMULTISIG, | |||||
OP_CHECKSIG, | OP_CHECKSIG, | ||||
OP_DUP, | OP_DUP, | ||||
OP_EQUAL, | |||||
OP_EQUALVERIFY, | OP_EQUALVERIFY, | ||||
OP_HASH160, | OP_HASH160, | ||||
OP_NOP, | OP_NOP, | ||||
hash160, | hash160, | ||||
) | ) | ||||
from test_framework.test_framework import BitcoinTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
from test_framework.util import ( | from test_framework.util import ( | ||||
assert_equal, | assert_equal, | ||||
assert_greater_than, | assert_greater_than, | ||||
assert_raises_rpc_error, | assert_raises_rpc_error, | ||||
hex_str_to_bytes, | hex_str_to_bytes, | ||||
) | ) | ||||
Key = namedtuple('Key', ['privkey', | Key = namedtuple('Key', ['privkey', | ||||
'pubkey', | 'pubkey', | ||||
'p2pkh_script', | 'p2pkh_script', | ||||
'p2pkh_addr']) | 'p2pkh_addr']) | ||||
Multisig = namedtuple('Multisig', ['privkeys', | |||||
'pubkeys', | |||||
'p2sh_script', | |||||
'p2sh_addr', | |||||
'redeem_script']) | |||||
class ImportMultiTest(BitcoinTestFramework): | class ImportMultiTest(BitcoinTestFramework): | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.num_nodes = 2 | self.num_nodes = 2 | ||||
self.setup_clean_chain = True | self.setup_clean_chain = True | ||||
def skip_test_if_missing_module(self): | def skip_test_if_missing_module(self): | ||||
self.skip_if_no_wallet() | self.skip_if_no_wallet() | ||||
Show All 11 Lines | def get_key(self): | ||||
return Key(self.nodes[0].dumpprivkey(addr), | return Key(self.nodes[0].dumpprivkey(addr), | ||||
pubkey, | pubkey, | ||||
# p2pkh | # p2pkh | ||||
CScript([OP_DUP, OP_HASH160, pkh, | CScript([OP_DUP, OP_HASH160, pkh, | ||||
OP_EQUALVERIFY, OP_CHECKSIG]).hex(), | OP_EQUALVERIFY, OP_CHECKSIG]).hex(), | ||||
# p2pkh addr | # p2pkh addr | ||||
key_to_p2pkh(pubkey)) | key_to_p2pkh(pubkey)) | ||||
def get_multisig(self): | |||||
"""Generate a fresh multisig on node0 | |||||
Returns a named tuple of privkeys, pubkeys and all address and scripts.""" | |||||
addrs = [] | |||||
pubkeys = [] | |||||
for _ in range(3): | |||||
addr = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) | |||||
addrs.append(addr['address']) | |||||
pubkeys.append(addr['pubkey']) | |||||
script_code = CScript([OP_2] + [hex_str_to_bytes(pubkey) | |||||
for pubkey in pubkeys] + [OP_3, OP_CHECKMULTISIG]) | |||||
return Multisig([self.nodes[0].dumpprivkey(addr) for addr in addrs], | |||||
pubkeys, | |||||
# p2sh | |||||
CScript([OP_HASH160, hash160( | |||||
script_code), OP_EQUAL]).hex(), | |||||
# p2sh addr | |||||
script_to_p2sh(script_code), | |||||
# redeem script | |||||
script_code.hex()) | |||||
def run_test(self): | def run_test(self): | ||||
self.log.info("Mining blocks...") | self.log.info("Mining blocks...") | ||||
self.nodes[0].generate(1) | self.nodes[0].generate(1) | ||||
self.nodes[1].generate(1) | self.nodes[1].generate(1) | ||||
timestamp = self.nodes[1].getblock( | timestamp = self.nodes[1].getblock( | ||||
self.nodes[1].getbestblockhash())['mediantime'] | self.nodes[1].getbestblockhash())['mediantime'] | ||||
node0_address1 = self.nodes[0].getaddressinfo( | node0_address1 = self.nodes[0].getaddressinfo( | ||||
▲ Show 20 Lines • Show All 236 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
result[0]['error']['message'], | result[0]['error']['message'], | ||||
'Internal must be set to true for nonstandard scriptPubKey imports.') | 'Internal must be set to true for nonstandard scriptPubKey imports.') | ||||
address_assert = self.nodes[1].getaddressinfo(address) | address_assert = self.nodes[1].getaddressinfo(address) | ||||
assert_equal(address_assert['iswatchonly'], False) | assert_equal(address_assert['iswatchonly'], False) | ||||
assert_equal(address_assert['ismine'], False) | assert_equal(address_assert['ismine'], False) | ||||
assert_equal('timestamp' in address_assert, False) | assert_equal('timestamp' in address_assert, False) | ||||
# P2SH address | # P2SH address | ||||
sig_address_1 = self.nodes[0].getaddressinfo( | multisig = self.get_multisig() | ||||
self.nodes[0].getnewaddress()) | |||||
sig_address_2 = self.nodes[0].getaddressinfo( | |||||
self.nodes[0].getnewaddress()) | |||||
sig_address_3 = self.nodes[0].getaddressinfo( | |||||
self.nodes[0].getnewaddress()) | |||||
multi_sig_script = self.nodes[0].createmultisig( | |||||
2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']]) | |||||
self.nodes[1].generate(100) | self.nodes[1].generate(100) | ||||
self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) | self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00) | ||||
self.nodes[1].generate(1) | self.nodes[1].generate(1) | ||||
timestamp = self.nodes[1].getblock( | timestamp = self.nodes[1].getblock( | ||||
self.nodes[1].getbestblockhash())['mediantime'] | self.nodes[1].getbestblockhash())['mediantime'] | ||||
self.log.info("Should import a p2sh") | self.log.info("Should import a p2sh") | ||||
result = self.nodes[1].importmulti([{ | result = self.nodes[1].importmulti([{ | ||||
"scriptPubKey": { | "scriptPubKey": { | ||||
"address": multi_sig_script['address'] | "address": multisig.p2sh_addr | ||||
}, | }, | ||||
"timestamp": "now", | "timestamp": "now", | ||||
}]) | }]) | ||||
assert_equal(result[0]['success'], True) | assert_equal(result[0]['success'], True) | ||||
address_assert = self.nodes[1].getaddressinfo( | address_assert = self.nodes[1].getaddressinfo(multisig.p2sh_addr) | ||||
multi_sig_script['address']) | |||||
assert_equal(address_assert['isscript'], True) | assert_equal(address_assert['isscript'], True) | ||||
assert_equal(address_assert['iswatchonly'], True) | assert_equal(address_assert['iswatchonly'], True) | ||||
assert_equal(address_assert['timestamp'], timestamp) | assert_equal(address_assert['timestamp'], timestamp) | ||||
p2shunspent = self.nodes[1].listunspent( | p2shunspent = self.nodes[1].listunspent( | ||||
0, 999999, [multi_sig_script['address']])[0] | 0, 999999, [multisig.p2sh_addr])[0] | ||||
assert_equal(p2shunspent['spendable'], False) | assert_equal(p2shunspent['spendable'], False) | ||||
assert_equal(p2shunspent['solvable'], False) | assert_equal(p2shunspent['solvable'], False) | ||||
# P2SH + Redeem script | # P2SH + Redeem script | ||||
sig_address_1 = self.nodes[0].getaddressinfo( | multisig = self.get_multisig() | ||||
self.nodes[0].getnewaddress()) | |||||
sig_address_2 = self.nodes[0].getaddressinfo( | |||||
self.nodes[0].getnewaddress()) | |||||
sig_address_3 = self.nodes[0].getaddressinfo( | |||||
self.nodes[0].getnewaddress()) | |||||
multi_sig_script = self.nodes[0].createmultisig( | |||||
2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']]) | |||||
self.nodes[1].generate(100) | self.nodes[1].generate(100) | ||||
self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) | self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00) | ||||
self.nodes[1].generate(1) | self.nodes[1].generate(1) | ||||
timestamp = self.nodes[1].getblock( | timestamp = self.nodes[1].getblock( | ||||
self.nodes[1].getbestblockhash())['mediantime'] | self.nodes[1].getbestblockhash())['mediantime'] | ||||
self.log.info("Should import a p2sh with respective redeem script") | self.log.info("Should import a p2sh with respective redeem script") | ||||
result = self.nodes[1].importmulti([{ | result = self.nodes[1].importmulti([{ | ||||
"scriptPubKey": { | "scriptPubKey": { | ||||
"address": multi_sig_script['address'] | "address": multisig.p2sh_addr | ||||
}, | }, | ||||
"timestamp": "now", | "timestamp": "now", | ||||
"redeemscript": multi_sig_script['redeemScript'] | "redeemscript": multisig.redeem_script | ||||
}]) | }]) | ||||
assert_equal(result[0]['success'], True) | assert_equal(result[0]['success'], True) | ||||
address_assert = self.nodes[1].getaddressinfo( | address_assert = self.nodes[1].getaddressinfo(multisig.p2sh_addr) | ||||
multi_sig_script['address']) | |||||
assert_equal(address_assert['timestamp'], timestamp) | assert_equal(address_assert['timestamp'], timestamp) | ||||
p2shunspent = self.nodes[1].listunspent( | p2shunspent = self.nodes[1].listunspent( | ||||
0, 999999, [multi_sig_script['address']])[0] | 0, 999999, [multisig.p2sh_addr])[0] | ||||
assert_equal(p2shunspent['spendable'], False) | assert_equal(p2shunspent['spendable'], False) | ||||
assert_equal(p2shunspent['solvable'], True) | assert_equal(p2shunspent['solvable'], True) | ||||
# P2SH + Redeem script + Private Keys + !Watchonly | # P2SH + Redeem script + Private Keys + !Watchonly | ||||
sig_address_1 = self.nodes[0].getaddressinfo( | multisig = self.get_multisig() | ||||
self.nodes[0].getnewaddress()) | |||||
sig_address_2 = self.nodes[0].getaddressinfo( | |||||
self.nodes[0].getnewaddress()) | |||||
sig_address_3 = self.nodes[0].getaddressinfo( | |||||
self.nodes[0].getnewaddress()) | |||||
multi_sig_script = self.nodes[0].createmultisig( | |||||
2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']]) | |||||
self.nodes[1].generate(100) | self.nodes[1].generate(100) | ||||
self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) | self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00) | ||||
self.nodes[1].generate(1) | self.nodes[1].generate(1) | ||||
timestamp = self.nodes[1].getblock( | timestamp = self.nodes[1].getblock( | ||||
self.nodes[1].getbestblockhash())['mediantime'] | self.nodes[1].getbestblockhash())['mediantime'] | ||||
self.log.info( | self.log.info( | ||||
"Should import a p2sh with respective redeem script and private keys") | "Should import a p2sh with respective redeem script and private keys") | ||||
result = self.nodes[1].importmulti([{ | result = self.nodes[1].importmulti([{ | ||||
"scriptPubKey": { | "scriptPubKey": { | ||||
"address": multi_sig_script['address'] | "address": multisig.p2sh_addr | ||||
}, | }, | ||||
"timestamp": "now", | "timestamp": "now", | ||||
"redeemscript": multi_sig_script['redeemScript'], | "redeemscript": multisig.redeem_script, | ||||
"keys": [self.nodes[0].dumpprivkey(sig_address_1['address']), self.nodes[0].dumpprivkey(sig_address_2['address'])] | "keys": multisig.privkeys[0:2] | ||||
}]) | }]) | ||||
assert_equal(result[0]['success'], True) | assert_equal(result[0]['success'], True) | ||||
address_assert = self.nodes[1].getaddressinfo( | address_assert = self.nodes[1].getaddressinfo(multisig.p2sh_addr) | ||||
multi_sig_script['address']) | |||||
assert_equal(address_assert['timestamp'], timestamp) | assert_equal(address_assert['timestamp'], timestamp) | ||||
p2shunspent = self.nodes[1].listunspent( | p2shunspent = self.nodes[1].listunspent( | ||||
0, 999999, [multi_sig_script['address']])[0] | 0, 999999, [multisig.p2sh_addr])[0] | ||||
assert_equal(p2shunspent['spendable'], False) | assert_equal(p2shunspent['spendable'], False) | ||||
assert_equal(p2shunspent['solvable'], True) | assert_equal(p2shunspent['solvable'], True) | ||||
# P2SH + Redeem script + Private Keys + Watchonly | # P2SH + Redeem script + Private Keys + Watchonly | ||||
sig_address_1 = self.nodes[0].getaddressinfo( | multisig = self.get_multisig() | ||||
self.nodes[0].getnewaddress()) | |||||
sig_address_2 = self.nodes[0].getaddressinfo( | |||||
self.nodes[0].getnewaddress()) | |||||
sig_address_3 = self.nodes[0].getaddressinfo( | |||||
self.nodes[0].getnewaddress()) | |||||
multi_sig_script = self.nodes[0].createmultisig( | |||||
2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']]) | |||||
self.nodes[1].generate(100) | self.nodes[1].generate(100) | ||||
self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) | self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00) | ||||
self.nodes[1].generate(1) | self.nodes[1].generate(1) | ||||
timestamp = self.nodes[1].getblock( | timestamp = self.nodes[1].getblock( | ||||
self.nodes[1].getbestblockhash())['mediantime'] | self.nodes[1].getbestblockhash())['mediantime'] | ||||
self.log.info( | self.log.info( | ||||
"Should import a p2sh with respective redeem script and private keys") | "Should import a p2sh with respective redeem script and private keys") | ||||
result = self.nodes[1].importmulti([{ | result = self.nodes[1].importmulti([{ | ||||
"scriptPubKey": { | "scriptPubKey": { | ||||
"address": multi_sig_script['address'] | "address": multisig.p2sh_addr | ||||
}, | }, | ||||
"timestamp": "now", | "timestamp": "now", | ||||
"redeemscript": multi_sig_script['redeemScript'], | "redeemscript": multisig.redeem_script, | ||||
"keys": [self.nodes[0].dumpprivkey(sig_address_1['address']), self.nodes[0].dumpprivkey(sig_address_2['address'])], | "keys": multisig.privkeys[0:2], | ||||
"watchonly": True | "watchonly": True | ||||
}]) | }]) | ||||
assert_equal(result[0]['success'], False) | assert_equal(result[0]['success'], False) | ||||
assert_equal(result[0]['error']['code'], -8) | assert_equal(result[0]['error']['code'], -8) | ||||
assert_equal( | assert_equal( | ||||
result[0]['error']['message'], | result[0]['error']['message'], | ||||
'Watch-only addresses should not include private keys') | 'Watch-only addresses should not include private keys') | ||||
▲ Show 20 Lines • Show All 128 Lines • Show Last 20 Lines |