2 # Copyright (c) 2016 The Bitcoin Core developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Test Hierarchical Deterministic wallet function."""
7 from test_framework
.test_framework
import BitcoinTestFramework
8 from test_framework
.util
import (
14 class WalletHDTest(BitcoinTestFramework
):
15 def set_test_params(self
):
16 self
.setup_clean_chain
= True
18 self
.extra_args
= [[], ['-keypool=0']]
21 tmpdir
= self
.options
.tmpdir
23 # Make sure can't switch off usehd after wallet creation
25 self
.assert_start_raises_init_error(1, ['-usehd=0'], 'already existing HD wallet')
27 connect_nodes_bi(self
.nodes
, 0, 1)
29 # Make sure we use hd, keep masterkeyid
30 masterkeyid
= self
.nodes
[1].getwalletinfo()['hdmasterkeyid']
31 assert_equal(len(masterkeyid
), 40)
33 # create an internal key
34 change_addr
= self
.nodes
[1].getrawchangeaddress()
35 change_addrV
= self
.nodes
[1].validateaddress(change_addr
)
36 assert_equal(change_addrV
["hdkeypath"], "m/0'/1'/0'") #first internal child key
38 # Import a non-HD private key in the HD wallet
39 non_hd_add
= self
.nodes
[0].getnewaddress()
40 self
.nodes
[1].importprivkey(self
.nodes
[0].dumpprivkey(non_hd_add
))
42 # This should be enough to keep the master key and the non-HD key
43 self
.nodes
[1].backupwallet(tmpdir
+ "/hd.bak")
44 #self.nodes[1].dumpwallet(tmpdir + "/hd.dump")
46 # Derive some HD addresses and remember the last
47 # Also send funds to each add
48 self
.nodes
[0].generate(101)
51 for i
in range(num_hd_adds
):
52 hd_add
= self
.nodes
[1].getnewaddress()
53 hd_info
= self
.nodes
[1].validateaddress(hd_add
)
54 assert_equal(hd_info
["hdkeypath"], "m/0'/0'/"+str(i
)+"'")
55 assert_equal(hd_info
["hdmasterkeyid"], masterkeyid
)
56 self
.nodes
[0].sendtoaddress(hd_add
, 1)
57 self
.nodes
[0].generate(1)
58 self
.nodes
[0].sendtoaddress(non_hd_add
, 1)
59 self
.nodes
[0].generate(1)
61 # create an internal key (again)
62 change_addr
= self
.nodes
[1].getrawchangeaddress()
63 change_addrV
= self
.nodes
[1].validateaddress(change_addr
)
64 assert_equal(change_addrV
["hdkeypath"], "m/0'/1'/1'") #second internal child key
67 assert_equal(self
.nodes
[1].getbalance(), num_hd_adds
+ 1)
69 self
.log
.info("Restore backup ...")
71 # we need to delete the complete regtest directory
72 # otherwise node1 would auto-recover all funds in flag the keypool keys as used
73 shutil
.rmtree(tmpdir
+ "/node1/regtest/blocks")
74 shutil
.rmtree(tmpdir
+ "/node1/regtest/chainstate")
75 shutil
.copyfile(tmpdir
+ "/hd.bak", tmpdir
+ "/node1/regtest/wallet.dat")
78 # Assert that derivation is deterministic
80 for _
in range(num_hd_adds
):
81 hd_add_2
= self
.nodes
[1].getnewaddress()
82 hd_info_2
= self
.nodes
[1].validateaddress(hd_add_2
)
83 assert_equal(hd_info_2
["hdkeypath"], "m/0'/0'/"+str(_
)+"'")
84 assert_equal(hd_info_2
["hdmasterkeyid"], masterkeyid
)
85 assert_equal(hd_add
, hd_add_2
)
86 connect_nodes_bi(self
.nodes
, 0, 1)
91 self
.start_node(1, extra_args
=self
.extra_args
[1] + ['-rescan'])
92 assert_equal(self
.nodes
[1].getbalance(), num_hd_adds
+ 1)
94 # send a tx and make sure its using the internal chain for the changeoutput
95 txid
= self
.nodes
[1].sendtoaddress(self
.nodes
[0].getnewaddress(), 1)
96 outs
= self
.nodes
[1].decoderawtransaction(self
.nodes
[1].gettransaction(txid
)['hex'])['vout']
100 keypath
= self
.nodes
[1].validateaddress(out
['scriptPubKey']['addresses'][0])['hdkeypath']
102 assert_equal(keypath
[0:7], "m/0'/1'")
104 if __name__
== '__main__':
105 WalletHDTest().main ()