Changeset View
Changeset View
Standalone View
Standalone View
test/functional/wallet_hd.py
Show All 21 Lines | def set_test_params(self): | ||||
self.extra_args = [[], ['-keypool=0']] | self.extra_args = [[], ['-keypool=0']] | ||||
self.supports_cli = False | self.supports_cli = False | ||||
def skip_test_if_missing_module(self): | def skip_test_if_missing_module(self): | ||||
self.skip_if_no_wallet() | self.skip_if_no_wallet() | ||||
def run_test(self): | def run_test(self): | ||||
# Make sure we use hd, keep masterkeyid | # Make sure we use hd, keep masterkeyid | ||||
masterkeyid = self.nodes[1].getwalletinfo()['hdseedid'] | hd_fingerprint = self.nodes[1].getaddressinfo( | ||||
assert_equal(len(masterkeyid), 40) | self.nodes[1].getnewaddress())['hdmasterfingerprint'] | ||||
assert_equal(len(hd_fingerprint), 8) | |||||
# create an internal key | # create an internal key | ||||
change_addr = self.nodes[1].getrawchangeaddress() | change_addr = self.nodes[1].getrawchangeaddress() | ||||
change_addrV = self.nodes[1].getaddressinfo(change_addr) | change_addrV = self.nodes[1].getaddressinfo(change_addr) | ||||
if self.options.descriptors: | |||||
assert_equal(change_addrV["hdkeypath"], "m/44'/1'/0'/1/0") | |||||
else: | |||||
# first internal child key | # first internal child key | ||||
assert_equal(change_addrV["hdkeypath"], "m/0'/1'/0'") | assert_equal(change_addrV["hdkeypath"], "m/0'/1'/0'") | ||||
# Import a non-HD private key in the HD wallet | # Import a non-HD private key in the HD wallet | ||||
non_hd_add = self.nodes[0].getnewaddress() | non_hd_add = 'bchreg:qr09jgufyeae4s97nqp6mv0tv6eymfunygk6kt4p5e' | ||||
self.nodes[1].importprivkey(self.nodes[0].dumpprivkey(non_hd_add)) | non_hd_key = 'cS9umN9w6cDMuRVYdbkfE4c7YUFLJRoXMfhQ569uY4odiQbVN8Rt' | ||||
self.nodes[1].importprivkey(non_hd_key) | |||||
# This should be enough to keep the master key and the non-HD key | # This should be enough to keep the master key and the non-HD key | ||||
self.nodes[1].backupwallet( | self.nodes[1].backupwallet( | ||||
os.path.join(self.nodes[1].datadir, "hd.bak")) | os.path.join(self.nodes[1].datadir, "hd.bak")) | ||||
# self.nodes[1].dumpwallet(os.path.join(self.nodes[1].datadir, "hd.dump")) | # self.nodes[1].dumpwallet(os.path.join(self.nodes[1].datadir, "hd.dump")) | ||||
# Derive some HD addresses and remember the last | # Derive some HD addresses and remember the last | ||||
# Also send funds to each add | # Also send funds to each add | ||||
self.nodes[0].generate(101) | self.nodes[0].generate(101) | ||||
hd_add = None | hd_add = None | ||||
NUM_HD_ADDS = 10 | NUM_HD_ADDS = 10 | ||||
for i in range(NUM_HD_ADDS): | for i in range(1, NUM_HD_ADDS + 1): | ||||
hd_add = self.nodes[1].getnewaddress() | hd_add = self.nodes[1].getnewaddress() | ||||
hd_info = self.nodes[1].getaddressinfo(hd_add) | hd_info = self.nodes[1].getaddressinfo(hd_add) | ||||
if self.options.descriptors: | |||||
assert_equal(hd_info["hdkeypath"], "m/44'/1'/0'/0/" + str(i)) | |||||
else: | |||||
assert_equal(hd_info["hdkeypath"], "m/0'/0'/" + str(i) + "'") | assert_equal(hd_info["hdkeypath"], "m/0'/0'/" + str(i) + "'") | ||||
assert_equal(hd_info["hdseedid"], masterkeyid) | assert_equal(hd_info["hdmasterfingerprint"], hd_fingerprint) | ||||
self.nodes[0].sendtoaddress(hd_add, 1) | self.nodes[0].sendtoaddress(hd_add, 1) | ||||
self.nodes[0].generate(1) | self.nodes[0].generate(1) | ||||
self.nodes[0].sendtoaddress(non_hd_add, 1) | self.nodes[0].sendtoaddress(non_hd_add, 1) | ||||
self.nodes[0].generate(1) | self.nodes[0].generate(1) | ||||
# create an internal key (again) | # create an internal key (again) | ||||
change_addr = self.nodes[1].getrawchangeaddress() | change_addr = self.nodes[1].getrawchangeaddress() | ||||
change_addrV = self.nodes[1].getaddressinfo(change_addr) | change_addrV = self.nodes[1].getaddressinfo(change_addr) | ||||
if self.options.descriptors: | |||||
assert_equal(change_addrV["hdkeypath"], "m/44'/1'/0'/1/1") | |||||
else: | |||||
# second internal child key | # second internal child key | ||||
assert_equal(change_addrV["hdkeypath"], "m/0'/1'/1'") | assert_equal(change_addrV["hdkeypath"], "m/0'/1'/1'") | ||||
self.sync_all() | self.sync_all() | ||||
assert_equal(self.nodes[1].getbalance(), NUM_HD_ADDS + 1) | assert_equal(self.nodes[1].getbalance(), NUM_HD_ADDS + 1) | ||||
self.log.info("Restore backup ...") | self.log.info("Restore backup ...") | ||||
self.stop_node(1) | self.stop_node(1) | ||||
# we need to delete the complete regtest directory | # we need to delete the complete chain directory | ||||
# otherwise node1 would auto-recover all funds in flag the keypool keys | # otherwise node1 would auto-recover all funds in flag the keypool keys | ||||
# as used | # as used | ||||
shutil.rmtree(os.path.join(self.nodes[1].datadir, "regtest", "blocks")) | shutil.rmtree( | ||||
shutil.rmtree(os.path.join( | os.path.join( | ||||
self.nodes[1].datadir, "regtest", "chainstate")) | self.nodes[1].datadir, | ||||
shutil.copyfile(os.path.join(self.nodes[1].datadir, "hd.bak"), os.path.join( | self.chain, | ||||
self.nodes[1].datadir, "regtest", "wallets", "wallet.dat")) | "blocks")) | ||||
shutil.rmtree( | |||||
os.path.join( | |||||
self.nodes[1].datadir, | |||||
self.chain, | |||||
"chainstate")) | |||||
shutil.copyfile( | |||||
os.path.join( | |||||
self.nodes[1].datadir, | |||||
"hd.bak"), | |||||
os.path.join( | |||||
self.nodes[1].datadir, | |||||
self.chain, | |||||
'wallets', | |||||
"wallet.dat")) | |||||
self.start_node(1) | self.start_node(1) | ||||
# Assert that derivation is deterministic | # Assert that derivation is deterministic | ||||
hd_add_2 = None | hd_add_2 = None | ||||
for i in range(NUM_HD_ADDS): | for i in range(1, NUM_HD_ADDS + 1): | ||||
hd_add_2 = self.nodes[1].getnewaddress() | hd_add_2 = self.nodes[1].getnewaddress() | ||||
hd_info_2 = self.nodes[1].getaddressinfo(hd_add_2) | hd_info_2 = self.nodes[1].getaddressinfo(hd_add_2) | ||||
if self.options.descriptors: | |||||
assert_equal(hd_info_2["hdkeypath"], "m/44'/1'/0'/0/" + str(i)) | |||||
else: | |||||
assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/" + str(i) + "'") | assert_equal(hd_info_2["hdkeypath"], "m/0'/0'/" + str(i) + "'") | ||||
assert_equal(hd_info_2["hdseedid"], masterkeyid) | assert_equal(hd_info_2["hdmasterfingerprint"], hd_fingerprint) | ||||
assert_equal(hd_add, hd_add_2) | assert_equal(hd_add, hd_add_2) | ||||
connect_nodes(self.nodes[0], self.nodes[1]) | connect_nodes(self.nodes[0], self.nodes[1]) | ||||
self.sync_all() | self.sync_all() | ||||
# Needs rescan | # Needs rescan | ||||
self.stop_node(1) | self.stop_node(1) | ||||
self.start_node(1, extra_args=self.extra_args[1] + ['-rescan']) | self.start_node(1, extra_args=self.extra_args[1] + ['-rescan']) | ||||
assert_equal(self.nodes[1].getbalance(), NUM_HD_ADDS + 1) | assert_equal(self.nodes[1].getbalance(), NUM_HD_ADDS + 1) | ||||
Show All 30 Lines | def run_test(self): | ||||
outs = self.nodes[1].decoderawtransaction( | outs = self.nodes[1].decoderawtransaction( | ||||
self.nodes[1].gettransaction(txid)['hex'])['vout'] | self.nodes[1].gettransaction(txid)['hex'])['vout'] | ||||
keypath = "" | keypath = "" | ||||
for out in outs: | for out in outs: | ||||
if out['value'] != 1: | if out['value'] != 1: | ||||
keypath = self.nodes[1].getaddressinfo( | keypath = self.nodes[1].getaddressinfo( | ||||
out['scriptPubKey']['addresses'][0])['hdkeypath'] | out['scriptPubKey']['addresses'][0])['hdkeypath'] | ||||
if self.options.descriptors: | |||||
assert_equal(keypath[0:14], "m/44'/1'/0'/1/") | |||||
else: | |||||
assert_equal(keypath[0:7], "m/0'/1'") | assert_equal(keypath[0:7], "m/0'/1'") | ||||
if not self.options.descriptors: | |||||
# Generate a new HD seed on node 1 and make sure it is set | # Generate a new HD seed on node 1 and make sure it is set | ||||
orig_masterkeyid = self.nodes[1].getwalletinfo()['hdseedid'] | orig_masterkeyid = self.nodes[1].getwalletinfo()['hdseedid'] | ||||
self.nodes[1].sethdseed() | self.nodes[1].sethdseed() | ||||
new_masterkeyid = self.nodes[1].getwalletinfo()['hdseedid'] | new_masterkeyid = self.nodes[1].getwalletinfo()['hdseedid'] | ||||
assert orig_masterkeyid != new_masterkeyid | assert orig_masterkeyid != new_masterkeyid | ||||
addr = self.nodes[1].getnewaddress() | addr = self.nodes[1].getnewaddress() | ||||
# Make sure the new address is the first from the keypool | # Make sure the new address is the first from the keypool | ||||
assert_equal(self.nodes[1].getaddressinfo( | assert_equal(self.nodes[1].getaddressinfo( | ||||
addr)['hdkeypath'], 'm/0\'/0\'/0\'') | addr)['hdkeypath'], 'm/0\'/0\'/0\'') | ||||
self.nodes[1].keypoolrefill(1) # Fill keypool with 1 key | # Fill keypool with 1 key | ||||
self.nodes[1].keypoolrefill(1) | |||||
# Set a new HD seed on node 1 without flushing the keypool | # Set a new HD seed on node 1 without flushing the keypool | ||||
new_seed = self.nodes[0].dumpprivkey(self.nodes[0].getnewaddress()) | new_seed = self.nodes[0].dumpprivkey(self.nodes[0].getnewaddress()) | ||||
orig_masterkeyid = new_masterkeyid | orig_masterkeyid = new_masterkeyid | ||||
self.nodes[1].sethdseed(False, new_seed) | self.nodes[1].sethdseed(False, new_seed) | ||||
new_masterkeyid = self.nodes[1].getwalletinfo()['hdseedid'] | new_masterkeyid = self.nodes[1].getwalletinfo()['hdseedid'] | ||||
assert orig_masterkeyid != new_masterkeyid | assert orig_masterkeyid != new_masterkeyid | ||||
addr = self.nodes[1].getnewaddress() | addr = self.nodes[1].getnewaddress() | ||||
assert_equal(orig_masterkeyid, self.nodes[1].getaddressinfo( | assert_equal( | ||||
addr)['hdseedid']) | orig_masterkeyid, | ||||
self.nodes[1].getaddressinfo(addr)['hdseedid']) | |||||
# Make sure the new address continues previous keypool | # Make sure the new address continues previous keypool | ||||
assert_equal(self.nodes[1].getaddressinfo( | assert_equal(self.nodes[1].getaddressinfo( | ||||
addr)['hdkeypath'], 'm/0\'/0\'/1\'') | addr)['hdkeypath'], 'm/0\'/0\'/1\'') | ||||
# Check that the next address is from the new seed | # Check that the next address is from the new seed | ||||
self.nodes[1].keypoolrefill(1) | self.nodes[1].keypoolrefill(1) | ||||
next_addr = self.nodes[1].getnewaddress() | next_addr = self.nodes[1].getnewaddress() | ||||
assert_equal(new_masterkeyid, self.nodes[1].getaddressinfo( | assert_equal( | ||||
next_addr)['hdseedid']) | new_masterkeyid, | ||||
self.nodes[1].getaddressinfo(next_addr)['hdseedid']) | |||||
# Make sure the new address is not from previous keypool | # Make sure the new address is not from previous keypool | ||||
assert_equal(self.nodes[1].getaddressinfo( | assert_equal(self.nodes[1].getaddressinfo( | ||||
next_addr)['hdkeypath'], 'm/0\'/0\'/0\'') | next_addr)['hdkeypath'], 'm/0\'/0\'/0\'') | ||||
assert next_addr != addr | assert next_addr != addr | ||||
# Sethdseed parameter validity | # Sethdseed parameter validity | ||||
assert_raises_rpc_error(-1, 'sethdseed', | assert_raises_rpc_error(-1, | ||||
self.nodes[0].sethdseed, False, new_seed, 0) | 'sethdseed', | ||||
assert_raises_rpc_error(-5, "Invalid private key", | self.nodes[0].sethdseed, | ||||
self.nodes[1].sethdseed, False, "not_wif") | False, | ||||
assert_raises_rpc_error(-1, "JSON value is not a boolean as expected", | new_seed, | ||||
self.nodes[1].sethdseed, "Not_bool") | 0) | ||||
assert_raises_rpc_error(-1, "JSON value is not a string as expected", | assert_raises_rpc_error(-5, | ||||
self.nodes[1].sethdseed, False, True) | "Invalid private key", | ||||
assert_raises_rpc_error(-5, "Already have this key", | self.nodes[1].sethdseed, | ||||
self.nodes[1].sethdseed, False, new_seed) | False, | ||||
assert_raises_rpc_error(-5, "Already have this key", | "not_wif") | ||||
self.nodes[1].sethdseed, False, self.nodes[1].dumpprivkey(self.nodes[1].getnewaddress())) | assert_raises_rpc_error(-1, | ||||
"JSON value is not a boolean as expected", | |||||
self.nodes[1].sethdseed, | |||||
"Not_bool") | |||||
assert_raises_rpc_error(-1, | |||||
"JSON value is not a string as expected", | |||||
self.nodes[1].sethdseed, | |||||
False, | |||||
True) | |||||
assert_raises_rpc_error(-5, | |||||
"Already have this key", | |||||
self.nodes[1].sethdseed, | |||||
False, | |||||
new_seed) | |||||
assert_raises_rpc_error(-5, | |||||
"Already have this key", | |||||
self.nodes[1].sethdseed, | |||||
False, | |||||
self.nodes[1].dumpprivkey(self.nodes[1].getnewaddress())) | |||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
WalletHDTest().main() | WalletHDTest().main() |