wallet_importprunedfunds.py
1 #!/usr/bin/env python3 2 # Copyright (c) 2014-2022 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 the importprunedfunds and removeprunedfunds RPCs.""" 6 from decimal import Decimal 7 8 from test_framework.address import key_to_p2wpkh 9 from test_framework.blocktools import COINBASE_MATURITY 10 from test_framework.messages import ( 11 CMerkleBlock, 12 from_hex, 13 ) 14 from test_framework.test_framework import BitcoinTestFramework 15 from test_framework.util import ( 16 assert_equal, 17 assert_raises_rpc_error, 18 ) 19 from test_framework.wallet_util import generate_keypair 20 21 22 class ImportPrunedFundsTest(BitcoinTestFramework): 23 def set_test_params(self): 24 self.setup_clean_chain = True 25 self.num_nodes = 2 26 27 def skip_test_if_missing_module(self): 28 self.skip_if_no_wallet() 29 30 def run_test(self): 31 self.log.info("Mining blocks...") 32 self.generate(self.nodes[0], COINBASE_MATURITY + 1) 33 34 # address 35 address1 = self.nodes[0].getnewaddress() 36 # pubkey 37 address2 = self.nodes[0].getnewaddress() 38 # privkey 39 address3_privkey, address3_pubkey = generate_keypair(wif=True) 40 address3 = key_to_p2wpkh(address3_pubkey) 41 self.nodes[0].importprivkey(address3_privkey) 42 43 # Check only one address 44 address_info = self.nodes[0].getaddressinfo(address1) 45 assert_equal(address_info['ismine'], True) 46 47 self.sync_all() 48 49 # Node 1 sync test 50 assert_equal(self.nodes[1].getblockcount(), COINBASE_MATURITY + 1) 51 52 # Address Test - before import 53 address_info = self.nodes[1].getaddressinfo(address1) 54 assert_equal(address_info['iswatchonly'], False) 55 assert_equal(address_info['ismine'], False) 56 57 address_info = self.nodes[1].getaddressinfo(address2) 58 assert_equal(address_info['iswatchonly'], False) 59 assert_equal(address_info['ismine'], False) 60 61 address_info = self.nodes[1].getaddressinfo(address3) 62 assert_equal(address_info['iswatchonly'], False) 63 assert_equal(address_info['ismine'], False) 64 65 # Send funds to self 66 txnid1 = self.nodes[0].sendtoaddress(address1, 0.1) 67 self.generate(self.nodes[0], 1) 68 rawtxn1 = self.nodes[0].gettransaction(txnid1)['hex'] 69 proof1 = self.nodes[0].gettxoutproof([txnid1]) 70 71 txnid2 = self.nodes[0].sendtoaddress(address2, 0.05) 72 self.generate(self.nodes[0], 1) 73 rawtxn2 = self.nodes[0].gettransaction(txnid2)['hex'] 74 proof2 = self.nodes[0].gettxoutproof([txnid2]) 75 76 txnid3 = self.nodes[0].sendtoaddress(address3, 0.025) 77 self.generate(self.nodes[0], 1) 78 rawtxn3 = self.nodes[0].gettransaction(txnid3)['hex'] 79 proof3 = self.nodes[0].gettxoutproof([txnid3]) 80 81 self.sync_all() 82 83 # Import with no affiliated address 84 assert_raises_rpc_error(-5, "No addresses", self.nodes[1].importprunedfunds, rawtxn1, proof1) 85 86 balance1 = self.nodes[1].getbalance() 87 assert_equal(balance1, Decimal(0)) 88 89 # Import with affiliated address with no rescan 90 self.nodes[1].createwallet('wwatch', disable_private_keys=True) 91 wwatch = self.nodes[1].get_wallet_rpc('wwatch') 92 wwatch.importaddress(address=address2, rescan=False) 93 wwatch.importprunedfunds(rawtransaction=rawtxn2, txoutproof=proof2) 94 assert [tx for tx in wwatch.listtransactions(include_watchonly=True) if tx['txid'] == txnid2] 95 96 # Import with private key with no rescan 97 w1 = self.nodes[1].get_wallet_rpc(self.default_wallet_name) 98 w1.importprivkey(privkey=address3_privkey, rescan=False) 99 w1.importprunedfunds(rawtxn3, proof3) 100 assert [tx for tx in w1.listtransactions() if tx['txid'] == txnid3] 101 balance3 = w1.getbalance() 102 assert_equal(balance3, Decimal('0.025')) 103 104 # Addresses Test - after import 105 address_info = w1.getaddressinfo(address1) 106 assert_equal(address_info['iswatchonly'], False) 107 assert_equal(address_info['ismine'], False) 108 address_info = wwatch.getaddressinfo(address2) 109 assert_equal(address_info['iswatchonly'], False) 110 assert_equal(address_info['ismine'], True) 111 address_info = w1.getaddressinfo(address3) 112 assert_equal(address_info['iswatchonly'], False) 113 assert_equal(address_info['ismine'], True) 114 115 # Remove transactions 116 assert_raises_rpc_error(-4, f'Transaction {txnid1} does not belong to this wallet', w1.removeprunedfunds, txnid1) 117 assert not [tx for tx in w1.listtransactions(include_watchonly=True) if tx['txid'] == txnid1] 118 119 wwatch.removeprunedfunds(txnid2) 120 assert not [tx for tx in wwatch.listtransactions(include_watchonly=True) if tx['txid'] == txnid2] 121 122 w1.removeprunedfunds(txnid3) 123 assert not [tx for tx in w1.listtransactions(include_watchonly=True) if tx['txid'] == txnid3] 124 125 # Check various RPC parameter validation errors 126 assert_raises_rpc_error(-22, "TX decode failed", w1.importprunedfunds, b'invalid tx'.hex(), proof1) 127 assert_raises_rpc_error(-5, "Transaction given doesn't exist in proof", w1.importprunedfunds, rawtxn2, proof1) 128 129 mb = from_hex(CMerkleBlock(), proof1) 130 mb.header.hashMerkleRoot = 0xdeadbeef # cause mismatch between merkle root and merkle block 131 assert_raises_rpc_error(-5, "Something wrong with merkleblock", w1.importprunedfunds, rawtxn1, mb.serialize().hex()) 132 133 mb = from_hex(CMerkleBlock(), proof1) 134 mb.header.nTime += 1 # modify arbitrary block header field to change block hash 135 assert_raises_rpc_error(-5, "Block not found in chain", w1.importprunedfunds, rawtxn1, mb.serialize().hex()) 136 137 138 if __name__ == '__main__': 139 ImportPrunedFundsTest(__file__).main()