Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1#!/usr/bin/env python32# Copyright (c) 2014-present The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or http://www.opensource.org/licenses/mit-license.php.5"""Test the importprunedfunds and removeprunedfunds RPCs."""6from decimal import Decimal8from test_framework.address import key_to_p2wpkh9from test_framework.blocktools import COINBASE_MATURITY10from test_framework.messages import (11CMerkleBlock,12from_hex,13)14from test_framework.test_framework import BitcoinTestFramework15from test_framework.util import (16assert_equal,17assert_not_equal,18assert_raises_rpc_error,19wallet_importprivkey,20)21from test_framework.wallet_util import generate_keypair24class ImportPrunedFundsTest(BitcoinTestFramework):25def set_test_params(self):26self.setup_clean_chain = True27self.num_nodes = 228self.extra_args = [["-deprecatedrpc=removeprunedfunds"]] * 230def skip_test_if_missing_module(self):31self.skip_if_no_wallet()33def run_test(self):34self.log.info("Mining blocks...")35self.generate(self.nodes[0], COINBASE_MATURITY + 1)37# address38address1 = self.nodes[0].getnewaddress()39# pubkey40address2 = self.nodes[0].getnewaddress()41# privkey42address3_privkey, address3_pubkey = generate_keypair(wif=True)43address3 = key_to_p2wpkh(address3_pubkey)44wallet_importprivkey(self.nodes[0], address3_privkey, "now")46# Check only one address47address_info = self.nodes[0].getaddressinfo(address1)48assert_equal(address_info['ismine'], True)50self.sync_all()52# Node 1 sync test53assert_equal(self.nodes[1].getblockcount(), COINBASE_MATURITY + 1)55# Address Test - before import56address_info = self.nodes[1].getaddressinfo(address1)57assert_equal(address_info['ismine'], False)59address_info = self.nodes[1].getaddressinfo(address2)60assert_equal(address_info['ismine'], False)62address_info = self.nodes[1].getaddressinfo(address3)63assert_equal(address_info['ismine'], False)65# Send funds to self66txnid1 = self.nodes[0].sendtoaddress(address1, 0.1)67self.generate(self.nodes[0], 1)68rawtxn1 = self.nodes[0].gettransaction(txnid1)['hex']69proof1 = self.nodes[0].gettxoutproof([txnid1])71txnid2 = self.nodes[0].sendtoaddress(address2, 0.05)72self.generate(self.nodes[0], 1)73rawtxn2 = self.nodes[0].gettransaction(txnid2)['hex']74proof2 = self.nodes[0].gettxoutproof([txnid2])76txnid3 = self.nodes[0].sendtoaddress(address3, 0.025)77self.generate(self.nodes[0], 1)78rawtxn3 = self.nodes[0].gettransaction(txnid3)['hex']79proof3 = self.nodes[0].gettxoutproof([txnid3])81self.sync_all()83# Import with no affiliated address84assert_raises_rpc_error(-5, "No addresses", self.nodes[1].importprunedfunds, rawtxn1, proof1)86balance1 = self.nodes[1].getbalance()87assert_equal(balance1, Decimal(0))89# Import with affiliated address with no rescan90self.nodes[1].createwallet('wwatch', disable_private_keys=True)91wwatch = self.nodes[1].get_wallet_rpc('wwatch')92wwatch.importdescriptors([{"desc": self.nodes[0].getaddressinfo(address2)["desc"], "timestamp": "now"}])93wwatch.importprunedfunds(rawtransaction=rawtxn2, txoutproof=proof2)94assert txnid2 in [tx['txid'] for tx in wwatch.listtransactions()]96# Import with private key with no rescan97w1 = self.nodes[1].get_wallet_rpc(self.default_wallet_name)98wallet_importprivkey(w1, address3_privkey, "now")99w1.importprunedfunds(rawtxn3, proof3)100assert txnid3 in [tx['txid'] for tx in w1.listtransactions()]101balance3 = w1.getbalance()102assert_equal(balance3, Decimal('0.025'))104# Addresses Test - after import105address_info = w1.getaddressinfo(address1)106assert_equal(address_info['ismine'], False)107address_info = wwatch.getaddressinfo(address2)108assert_equal(address_info['ismine'], True)109address_info = w1.getaddressinfo(address3)110assert_equal(address_info['ismine'], True)112# Remove transactions113assert_raises_rpc_error(-4, f'Transaction {txnid1} does not belong to this wallet', w1.removeprunedfunds, txnid1)114assert txnid1 not in [tx['txid'] for tx in w1.listtransactions()]116wwatch.removeprunedfunds(txnid2)117assert txnid2 not in [tx['txid'] for tx in wwatch.listtransactions()]119w1.removeprunedfunds(txnid3)120assert txnid3 not in [tx['txid'] for tx in w1.listtransactions()]122# Check various RPC parameter validation errors123assert_raises_rpc_error(-22, "TX decode failed", w1.importprunedfunds, b'invalid tx'.hex(), proof1)124assert_raises_rpc_error(-5, "Transaction given doesn't exist in proof", w1.importprunedfunds, rawtxn2, proof1)126mb = from_hex(CMerkleBlock(), proof1)127mb.header.hashMerkleRoot = 0xdeadbeef # cause mismatch between merkle root and merkle block128assert_raises_rpc_error(-5, "Something wrong with merkleblock", w1.importprunedfunds, rawtxn1, mb.serialize().hex())130mb = from_hex(CMerkleBlock(), proof1)131mb.header.nTime += 1 # modify arbitrary block header field to change block hash132assert_raises_rpc_error(-5, "Block not found in chain", w1.importprunedfunds, rawtxn1, mb.serialize().hex())134self.log.info("Test removeprunedfunds with conflicting transactions")135node = self.nodes[0]137# Create a transaction138utxo = node.listunspent()[0]139addr = node.getnewaddress()140tx1_id = node.send(outputs=[{addr: 1}], inputs=[utxo])["txid"]141tx1_fee = node.gettransaction(tx1_id)["fee"]143# Create a conflicting tx with a larger fee (tx1_fee is negative)144output_value = utxo["amount"] + tx1_fee - Decimal("0.00001")145raw_tx2 = node.createrawtransaction(inputs=[utxo], outputs=[{addr: output_value}])146signed_tx2 = node.signrawtransactionwithwallet(raw_tx2)147tx2_id = node.sendrawtransaction(signed_tx2["hex"])148assert_not_equal(tx2_id, tx1_id)150# Both txs should be in the wallet, tx2 replaced tx1 in mempool151assert tx1_id in [tx["txid"] for tx in node.listtransactions()]152assert tx2_id in [tx["txid"] for tx in node.listtransactions()]154# Remove the replaced tx from wallet155node.removeprunedfunds(tx1_id)157# The UTXO should still be considered spent (by tx2)158available_utxos = [u["txid"] for u in node.listunspent(minconf=0)]159assert utxo["txid"] not in available_utxos, "UTXO should still be spent by conflicting tx"162if __name__ == '__main__':163ImportPrunedFundsTest(__file__).main()
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.