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