Skip to content

Commit 09ac7f9

Browse files
committed
Merge pull request #5427
1577df9 Port of walletbackup.sh to Python. (mrbandrews)
2 parents 7f76dda + 1577df9 commit 09ac7f9

File tree

2 files changed

+200
-297
lines changed

2 files changed

+200
-297
lines changed

qa/rpc-tests/walletbackup.py

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

0 commit comments

Comments
 (0)