1#!/usr/bin/env python3
2# Copyright (c) 2014-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
6"""
7Exercise the wallet backup code.  Ported from walletbackup.sh.
8
9Test case is:
104 nodes. 1 2 and 3 send transactions between each other,
11fourth node is a miner.
121 2 3 each mine a block to start, then
13Miner creates 100 blocks so 1 2 3 each have 50 mature
14coins to spend.
15Then 5 iterations of 1/2/3 sending coins amongst
16themselves to get transactions in the wallets,
17and the miner mining one block.
18
19Wallets are backed up using dumpwallet/backupwallet.
20Then 5 more iterations of transactions and mining a block.
21
22Miner then generates 101 more blocks, so any
23transaction fees paid mature.
24
25Sanity check:
26  Sum(1,2,3,4 balances) == 114*50
27
281/2/3 are shutdown, and their wallets erased.
29Then restore using wallet.dat backup. And
30confirm 1/2/3/4 balances are same as before.
31
32Shutdown again, restore using importwallet,
33and confirm again balances are correct.
34"""
35
36from test_framework.test_framework import BitcoinTestFramework
37from test_framework.util import *
38from random import randint
39import logging
40logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO, stream=sys.stdout)
41
42class WalletBackupTest(BitcoinTestFramework):
43
44    def __init__(self):
45        super().__init__()
46        self.setup_clean_chain = True
47        self.num_nodes = 4
48        # nodes 1, 2,3 are spenders, let's give them a keypool=100
49        self.extra_args = [["-keypool=100"], ["-keypool=100"], ["-keypool=100"], []]
50
51    # This mirrors how the network was setup in the bash test
52    def setup_network(self, split=False):
53        self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)
54        connect_nodes(self.nodes[0], 3)
55        connect_nodes(self.nodes[1], 3)
56        connect_nodes(self.nodes[2], 3)
57        connect_nodes(self.nodes[2], 0)
58        self.is_network_split=False
59        self.sync_all()
60
61    def one_send(self, from_node, to_address):
62        if (randint(1,2) == 1):
63            amount = Decimal(randint(1,10)) / Decimal(10)
64            self.nodes[from_node].sendtoaddress(to_address, amount)
65
66    def do_one_round(self):
67        a0 = self.nodes[0].getnewaddress()
68        a1 = self.nodes[1].getnewaddress()
69        a2 = self.nodes[2].getnewaddress()
70
71        self.one_send(0, a1)
72        self.one_send(0, a2)
73        self.one_send(1, a0)
74        self.one_send(1, a2)
75        self.one_send(2, a0)
76        self.one_send(2, a1)
77
78        # Have the miner (node3) mine a block.
79        # Must sync mempools before mining.
80        sync_mempools(self.nodes)
81        self.nodes[3].generate(1)
82        sync_blocks(self.nodes)
83
84    # As above, this mirrors the original bash test.
85    def start_three(self):
86        self.nodes[0] = start_node(0, self.options.tmpdir)
87        self.nodes[1] = start_node(1, self.options.tmpdir)
88        self.nodes[2] = start_node(2, self.options.tmpdir)
89        connect_nodes(self.nodes[0], 3)
90        connect_nodes(self.nodes[1], 3)
91        connect_nodes(self.nodes[2], 3)
92        connect_nodes(self.nodes[2], 0)
93
94    def stop_three(self):
95        stop_node(self.nodes[0], 0)
96        stop_node(self.nodes[1], 1)
97        stop_node(self.nodes[2], 2)
98
99    def erase_three(self):
100        os.remove(self.options.tmpdir + "/node0/regtest/wallet.dat")
101        os.remove(self.options.tmpdir + "/node1/regtest/wallet.dat")
102        os.remove(self.options.tmpdir + "/node2/regtest/wallet.dat")
103
104    def run_test(self):
105        logging.info("Generating initial blockchain")
106        self.nodes[0].generate(1)
107        sync_blocks(self.nodes)
108        self.nodes[1].generate(1)
109        sync_blocks(self.nodes)
110        self.nodes[2].generate(1)
111        sync_blocks(self.nodes)
112        self.nodes[3].generate(100)
113        sync_blocks(self.nodes)
114
115        assert_equal(self.nodes[0].getbalance(), 50)
116        assert_equal(self.nodes[1].getbalance(), 50)
117        assert_equal(self.nodes[2].getbalance(), 50)
118        assert_equal(self.nodes[3].getbalance(), 0)
119
120        logging.info("Creating transactions")
121        # Five rounds of sending each other transactions.
122        for i in range(5):
123            self.do_one_round()
124
125        logging.info("Backing up")
126        tmpdir = self.options.tmpdir
127        self.nodes[0].backupwallet(tmpdir + "/node0/wallet.bak")
128        self.nodes[0].dumpwallet(tmpdir + "/node0/wallet.dump")
129        self.nodes[1].backupwallet(tmpdir + "/node1/wallet.bak")
130        self.nodes[1].dumpwallet(tmpdir + "/node1/wallet.dump")
131        self.nodes[2].backupwallet(tmpdir + "/node2/wallet.bak")
132        self.nodes[2].dumpwallet(tmpdir + "/node2/wallet.dump")
133
134        logging.info("More transactions")
135        for i in range(5):
136            self.do_one_round()
137
138        # Generate 101 more blocks, so any fees paid mature
139        self.nodes[3].generate(101)
140        self.sync_all()
141
142        balance0 = self.nodes[0].getbalance()
143        balance1 = self.nodes[1].getbalance()
144        balance2 = self.nodes[2].getbalance()
145        balance3 = self.nodes[3].getbalance()
146        total = balance0 + balance1 + balance2 + balance3
147
148        # At this point, there are 214 blocks (103 for setup, then 10 rounds, then 101.)
149        # 114 are mature, so the sum of all wallets should be 114 * 50 = 5700.
150        assert_equal(total, 5700)
151
152        ##
153        # Test restoring spender wallets from backups
154        ##
155        logging.info("Restoring using wallet.dat")
156        self.stop_three()
157        self.erase_three()
158
159        # Start node2 with no chain
160        shutil.rmtree(self.options.tmpdir + "/node2/regtest/blocks")
161        shutil.rmtree(self.options.tmpdir + "/node2/regtest/chainstate")
162
163        # Restore wallets from backup
164        shutil.copyfile(tmpdir + "/node0/wallet.bak", tmpdir + "/node0/regtest/wallet.dat")
165        shutil.copyfile(tmpdir + "/node1/wallet.bak", tmpdir + "/node1/regtest/wallet.dat")
166        shutil.copyfile(tmpdir + "/node2/wallet.bak", tmpdir + "/node2/regtest/wallet.dat")
167
168        logging.info("Re-starting nodes")
169        self.start_three()
170        sync_blocks(self.nodes)
171
172        assert_equal(self.nodes[0].getbalance(), balance0)
173        assert_equal(self.nodes[1].getbalance(), balance1)
174        assert_equal(self.nodes[2].getbalance(), balance2)
175
176        logging.info("Restoring using dumped wallet")
177        self.stop_three()
178        self.erase_three()
179
180        #start node2 with no chain
181        shutil.rmtree(self.options.tmpdir + "/node2/regtest/blocks")
182        shutil.rmtree(self.options.tmpdir + "/node2/regtest/chainstate")
183
184        self.start_three()
185
186        assert_equal(self.nodes[0].getbalance(), 0)
187        assert_equal(self.nodes[1].getbalance(), 0)
188        assert_equal(self.nodes[2].getbalance(), 0)
189
190        self.nodes[0].importwallet(tmpdir + "/node0/wallet.dump")
191        self.nodes[1].importwallet(tmpdir + "/node1/wallet.dump")
192        self.nodes[2].importwallet(tmpdir + "/node2/wallet.dump")
193
194        sync_blocks(self.nodes)
195
196        assert_equal(self.nodes[0].getbalance(), balance0)
197        assert_equal(self.nodes[1].getbalance(), balance1)
198        assert_equal(self.nodes[2].getbalance(), balance2)
199
200
201if __name__ == '__main__':
202    WalletBackupTest().main()
203