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) 2024-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.6from decimal import Decimal8from test_framework.test_framework import BitcoinTestFramework9from test_framework.util import assert_equal, assert_raises_rpc_error10from test_framework.messages import COIN11from test_framework.wallet import MiniWallet, MiniWalletMode, getnewdestination14class GetBlocksActivityTest(BitcoinTestFramework):15def set_test_params(self):16self.num_nodes = 117self.setup_clean_chain = True19def run_test(self):20node = self.nodes[0]21wallet = MiniWallet(node)22node.setmocktime(node.getblockheader(node.getbestblockhash())['time'])23self.generate(wallet, 200)25self.test_no_activity(node)26self.test_activity_in_block(node, wallet)27self.test_no_mempool_inclusion(node, wallet)28self.test_multiple_addresses(node, wallet)29self.test_invalid_blockhash(node, wallet)30self.test_invalid_descriptor(node, wallet)31self.test_confirmed_and_unconfirmed(node, wallet)32self.test_receive_then_spend(node, wallet)33self.test_no_address(node, wallet)34self.test_required_args(node)36def test_no_activity(self, node):37self.log.info("Test that no activity is found for an unused address")38_, _, addr_1 = getnewdestination()39result = node.getdescriptoractivity([], [f"addr({addr_1})"], True)40assert_equal(len(result['activity']), 0)42def test_activity_in_block(self, node, wallet):43self.log.info("Test that receive activity is correctly reported in a mined block")44_, spk_1, addr_1 = getnewdestination(address_type='bech32m')45txid = wallet.send_to(from_node=node, scriptPubKey=spk_1, amount=1 * COIN)['txid']46blockhash = self.generate(node, 1)[0]48# Test getdescriptoractivity with the specific blockhash49result = node.getdescriptoractivity([blockhash], [f"addr({addr_1})"], True)50assert_equal(list(result.keys()), ['activity'])51[activity] = result['activity']53for k, v in {54'amount': Decimal('1.00000000'),55'blockhash': blockhash,56'height': 201,57'txid': txid,58'type': 'receive',59'vout': 1,60}.items():61assert_equal(activity[k], v)63outspk = activity['output_spk']65assert_equal(outspk['asm'][:2], '1 ')66assert_equal(outspk['desc'].split('(')[0], 'rawtr')67assert_equal(outspk['hex'], spk_1.hex())68assert_equal(outspk['address'], addr_1)69assert_equal(outspk['type'], 'witness_v1_taproot')72def test_no_mempool_inclusion(self, node, wallet):73self.log.info("Test that mempool transactions are not included when include_mempool argument is False")74_, spk_1, addr_1 = getnewdestination()75wallet.send_to(from_node=node, scriptPubKey=spk_1, amount=1 * COIN)77_, spk_2, addr_2 = getnewdestination()78wallet.send_to(79from_node=node, scriptPubKey=spk_2, amount=1 * COIN)81# Do not generate a block to keep the transaction in the mempool83result = node.getdescriptoractivity([], [f"addr({addr_1})", f"addr({addr_2})"], False)85assert_equal(len(result['activity']), 0)87def test_multiple_addresses(self, node, wallet):88self.log.info("Test querying multiple addresses returns all activity correctly")89_, spk_1, addr_1 = getnewdestination()90_, spk_2, addr_2 = getnewdestination()91wallet.send_to(from_node=node, scriptPubKey=spk_1, amount=1 * COIN)92wallet.send_to(from_node=node, scriptPubKey=spk_2, amount=2 * COIN)94blockhash = self.generate(node, 1)[0]96result = node.getdescriptoractivity([blockhash], [f"addr({addr_1})", f"addr({addr_2})"], True)98assert_equal(len(result['activity']), 2)100# Duplicate address specification is fine.101assert_equal(102result,103node.getdescriptoractivity([blockhash], [104f"addr({addr_1})", f"addr({addr_1})", f"addr({addr_2})"], True))106# Flipping descriptor order doesn't affect results.107result_flipped = node.getdescriptoractivity(108[blockhash], [f"addr({addr_2})", f"addr({addr_1})"], True)109assert_equal(result, result_flipped)111[a1] = [a for a in result['activity'] if a['output_spk']['address'] == addr_1]112[a2] = [a for a in result['activity'] if a['output_spk']['address'] == addr_2]114assert_equal(a1['blockhash'], blockhash)115assert_equal(a1['amount'], 1.0)117assert_equal(a2['blockhash'], blockhash)118assert_equal(a2['amount'], 2.0)120def test_invalid_blockhash(self, node, wallet):121self.log.info("Test that passing an invalid blockhash raises appropriate RPC error")122self.generate(node, 20) # Generate to get more fees124_, spk_1, addr_1 = getnewdestination()125wallet.send_to(from_node=node, scriptPubKey=spk_1, amount=1 * COIN)127invalid_blockhash = "0000000000000000000000000000000000000000000000000000000000000000"129assert_raises_rpc_error(130-5, "Block not found",131node.getdescriptoractivity, [invalid_blockhash], [f"addr({addr_1})"], True)133def test_invalid_descriptor(self, node, wallet):134self.log.info("Test that an invalid descriptor raises the correct RPC error")135blockhash = self.generate(node, 1)[0]136_, _, addr_1 = getnewdestination()138assert_raises_rpc_error(139-5, "is not a valid descriptor",140node.getdescriptoractivity, [blockhash], [f"addrx({addr_1})"], True)142def test_confirmed_and_unconfirmed(self, node, wallet):143self.log.info("Test that both confirmed and unconfirmed transactions are reported correctly")144self.generate(node, 20) # Generate to get more fees146_, spk_1, addr_1 = getnewdestination()147txid_1 = wallet.send_to(148from_node=node, scriptPubKey=spk_1, amount=1 * COIN)['txid']149blockhash = self.generate(node, 1)[0]151_, spk_2, to_addr = getnewdestination()152txid_2 = wallet.send_to(153from_node=node, scriptPubKey=spk_2, amount=1 * COIN)['txid']155result = node.getdescriptoractivity(156[blockhash], [f"addr({addr_1})", f"addr({to_addr})"], True)158activity = result['activity']159assert_equal(len(activity), 2)161[confirmed] = [a for a in activity if a.get('blockhash') == blockhash]162assert_equal(confirmed['txid'], txid_1)163assert_equal(confirmed['height'], node.getblockchaininfo()['blocks'])165[unconfirmed] = [a for a in activity if not a.get('blockhash')]166assert 'blockhash' not in unconfirmed167assert 'height' not in unconfirmed169assert txid_2 in [a['txid'] for a in activity if not a.get('blockhash')]171def test_receive_then_spend(self, node, wallet):172"""Also important because this tests multiple blockhashes."""173self.log.info("Test receive and spend activities across different blocks are reported consistently")174self.generate(node, 20) # Generate to get more fees176sent1 = wallet.send_self_transfer(from_node=node)177utxo = sent1['new_utxo']178blockhash_1 = self.generate(node, 1)[0]180sent2 = wallet.send_self_transfer(from_node=node, utxo_to_spend=utxo)181blockhash_2 = self.generate(node, 1)[0]183result = node.getdescriptoractivity(184[blockhash_1, blockhash_2], [wallet.get_descriptor()], True)186assert_equal(len(result['activity']), 4)188assert_equal(result['activity'][1]['type'], 'receive')189assert_equal(result['activity'][1]['txid'], sent1['txid'])190assert_equal(result['activity'][1]['blockhash'], blockhash_1)192assert_equal(result['activity'][2]['type'], 'spend')193assert_equal(result['activity'][2]['spend_txid'], sent2['txid'])194assert_equal(result['activity'][2]['spend_vin'], 0)195assert_equal(result['activity'][2]['prevout_txid'], sent1['txid'])196assert_equal(result['activity'][2]['blockhash'], blockhash_2)198# Test that reversing the blockorder yields the same result.199assert_equal(result, node.getdescriptoractivity(200[blockhash_1, blockhash_2], [wallet.get_descriptor()], True))202self.log.info("Test that duplicated blockhash request does not report duplicated results")203# Test that duplicating a blockhash yields the same result.204assert_equal(result, node.getdescriptoractivity(205[blockhash_1, blockhash_2, blockhash_2], [wallet.get_descriptor()], True))207def test_no_address(self, node, wallet):208self.log.info("Test that activity is still reported for scripts without an associated address")209raw_wallet = MiniWallet(self.nodes[0], mode=MiniWalletMode.RAW_P2PK)210self.generate(raw_wallet, 100)212no_addr_tx = raw_wallet.send_self_transfer(from_node=node)213raw_desc = raw_wallet.get_descriptor()215blockhash = self.generate(node, 1)[0]217result = node.getdescriptoractivity([blockhash], [raw_desc], False)219assert_equal(len(result['activity']), 2)221a1 = result['activity'][0]222a2 = result['activity'][1]224assert_equal(a1['type'], "spend")225assert_equal(a1['blockhash'], blockhash)226# sPK lacks address.227assert_equal(list(a1['prevout_spk'].keys()), ['asm', 'desc', 'hex', 'type'])228assert_equal(a1['amount'], no_addr_tx["fee"] + Decimal(no_addr_tx["tx"].vout[0].nValue) / COIN)230assert_equal(a2['type'], "receive")231assert_equal(a2['blockhash'], blockhash)232# sPK lacks address.233assert_equal(list(a2['output_spk'].keys()), ['asm', 'desc', 'hex', 'type'])234assert_equal(a2['amount'], Decimal(no_addr_tx["tx"].vout[0].nValue) / COIN)236def test_required_args(self, node):237self.log.info("Test that required arguments must be passed")238assert_raises_rpc_error(-1, "getdescriptoractivity", node.getdescriptoractivity)239assert_raises_rpc_error(-1, "getdescriptoractivity", node.getdescriptoractivity, [])242if __name__ == '__main__':243GetBlocksActivityTest(__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.