1#!/usr/bin/env python3
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
6from test_framework.test_framework import BitcoinTestFramework
7from test_framework.util import (start_nodes, start_node, assert_equal, bitcoind_processes)
8
9
10def read_dump(file_name, addrs, hd_master_addr_old):
11    """
12    Read the given dump, count the addrs that match, count change and reserve.
13    Also check that the old hd_master is inactive
14    """
15    with open(file_name, encoding='utf8') as inputfile:
16        found_addr = 0
17        found_addr_chg = 0
18        found_addr_rsv = 0
19        hd_master_addr_ret = None
20        for line in inputfile:
21            # only read non comment lines
22            if line[0] != "#" and len(line) > 10:
23                # split out some data
24                key_label, comment = line.split("#")
25                # key = key_label.split(" ")[0]
26                keytype = key_label.split(" ")[2]
27                if len(comment) > 1:
28                    addr_keypath = comment.split(" addr=")[1]
29                    addr = addr_keypath.split(" ")[0]
30                    keypath = None
31                    if keytype == "inactivehdmaster=1":
32                        # ensure the old master is still available
33                        assert(hd_master_addr_old == addr)
34                    elif keytype == "hdmaster=1":
35                        # ensure we have generated a new hd master key
36                        assert(hd_master_addr_old != addr)
37                        hd_master_addr_ret = addr
38                    else:
39                        keypath = addr_keypath.rstrip().split("hdkeypath=")[1]
40
41                    # count key types
42                    for addrObj in addrs:
43                        if addrObj['address'] == addr and addrObj['hdkeypath'] == keypath and keytype == "label=":
44                            found_addr += 1
45                            break
46                        elif keytype == "change=1":
47                            found_addr_chg += 1
48                            break
49                        elif keytype == "reserve=1":
50                            found_addr_rsv += 1
51                            break
52        return found_addr, found_addr_chg, found_addr_rsv, hd_master_addr_ret
53
54
55class WalletDumpTest(BitcoinTestFramework):
56
57    def __init__(self):
58        super().__init__()
59        self.setup_clean_chain = False
60        self.num_nodes = 1
61        self.extra_args = [["-keypool=90"]]
62
63    def setup_network(self, split=False):
64        # Use 1 minute timeout because the initial getnewaddress RPC can take
65        # longer than the default 30 seconds due to an expensive
66        # CWallet::TopUpKeyPool call, and the encryptwallet RPC made later in
67        # the test often takes even longer.
68        self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args, timewait=60)
69
70    def run_test (self):
71        tmpdir = self.options.tmpdir
72
73        # generate 20 addresses to compare against the dump
74        test_addr_count = 20
75        addrs = []
76        for i in range(0,test_addr_count):
77            addr = self.nodes[0].getnewaddress()
78            vaddr= self.nodes[0].validateaddress(addr) #required to get hd keypath
79            addrs.append(vaddr)
80        # Should be a no-op:
81        self.nodes[0].keypoolrefill()
82
83        # dump unencrypted wallet
84        self.nodes[0].dumpwallet(tmpdir + "/node0/wallet.unencrypted.dump")
85
86        found_addr, found_addr_chg, found_addr_rsv, hd_master_addr_unenc = \
87            read_dump(tmpdir + "/node0/wallet.unencrypted.dump", addrs, None)
88        assert_equal(found_addr, test_addr_count)  # all keys must be in the dump
89        assert_equal(found_addr_chg, 50)  # 50 blocks where mined
90        assert_equal(found_addr_rsv, 90 + 1)  # keypool size (TODO: fix off-by-one)
91
92        #encrypt wallet, restart, unlock and dump
93        self.nodes[0].encryptwallet('test')
94        bitcoind_processes[0].wait()
95        self.nodes[0] = start_node(0, self.options.tmpdir, self.extra_args[0])
96        self.nodes[0].walletpassphrase('test', 10)
97        # Should be a no-op:
98        self.nodes[0].keypoolrefill()
99        self.nodes[0].dumpwallet(tmpdir + "/node0/wallet.encrypted.dump")
100
101        found_addr, found_addr_chg, found_addr_rsv, hd_master_addr_enc = \
102            read_dump(tmpdir + "/node0/wallet.encrypted.dump", addrs, hd_master_addr_unenc)
103        assert_equal(found_addr, test_addr_count)
104        assert_equal(found_addr_chg, 90 + 1 + 50)  # old reserve keys are marked as change now
105        assert_equal(found_addr_rsv, 90 + 1)  # keypool size (TODO: fix off-by-one)
106
107if __name__ == '__main__':
108    WalletDumpTest().main ()
109