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) 2021-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 scanblocks RPC call."""6from test_framework.address import address_to_scriptpubkey7from test_framework.blockfilter import (8bip158_basic_element_hash,9bip158_relevant_scriptpubkeys,10)11from test_framework.messages import COIN12from test_framework.test_framework import BitcoinTestFramework13from test_framework.util import (14assert_equal,15assert_raises_rpc_error,16)17from test_framework.wallet import (18MiniWallet,19getnewdestination,20)23class ScanblocksTest(BitcoinTestFramework):24def set_test_params(self):25self.num_nodes = 226self.extra_args = [["-blockfilterindex=1"], []]28def run_test(self):29node = self.nodes[0]30wallet = MiniWallet(node)32# send 1.0, mempool only33_, spk_1, addr_1 = getnewdestination()34wallet.send_to(from_node=node, scriptPubKey=spk_1, amount=1 * COIN)36parent_key = "tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B"37# send 1.0, mempool only38# childkey 5 of `parent_key`39wallet.send_to(from_node=node,40scriptPubKey=address_to_scriptpubkey("mkS4HXoTYWRTescLGaUTGbtTTYX5EjJyEE"),41amount=1 * COIN)43# mine a block and assure that the mined blockhash is in the filterresult44blockhash = self.generate(node, 1)[0]45height = node.getblockheader(blockhash)['height']46self.wait_until(lambda: all(i["synced"] for i in node.getindexinfo().values()))48out = node.scanblocks("start", [f"addr({addr_1})"])49assert blockhash in out['relevant_blocks']50assert_equal(height, out['to_height'])51assert_equal(0, out['from_height'])52assert_equal(True, out['completed'])54# mine another block55blockhash_new = self.generate(node, 1)[0]56height_new = node.getblockheader(blockhash_new)['height']58# make sure the blockhash is not in the filter result if we set the start_height59# to the just mined block (unlikely to hit a false positive)60assert blockhash not in node.scanblocks(61"start", [f"addr({addr_1})"], height_new)['relevant_blocks']63# make sure the blockhash is present when using the first mined block as start_height64assert blockhash in node.scanblocks(65"start", [f"addr({addr_1})"], height)['relevant_blocks']66for v in [False, True]:67assert blockhash in node.scanblocks(68action="start",69scanobjects=[f"addr({addr_1})"],70start_height=height,71options={"filter_false_positives": v})['relevant_blocks']73# also test the stop height74assert blockhash in node.scanblocks(75"start", [f"addr({addr_1})"], height, height)['relevant_blocks']77# use the stop_height to exclude the relevant block78assert blockhash not in node.scanblocks(79"start", [f"addr({addr_1})"], 0, height - 1)['relevant_blocks']81# make sure the blockhash is present when using the first mined block as start_height82assert blockhash in node.scanblocks(83"start", [{"desc": f"pkh({parent_key}/*)", "range": [0, 100]}], height)['relevant_blocks']85# check that false-positives are included in the result now; note that86# finding a false-positive at runtime would take too long, hence we simply87# use a pre-calculated one that collides with the regtest genesis block's88# coinbase output and verify that their BIP158 ranged hashes match89genesis_blockhash = node.getblockhash(0)90genesis_spks = bip158_relevant_scriptpubkeys(node, genesis_blockhash)91assert_equal(len(genesis_spks), 1)92genesis_coinbase_spk = list(genesis_spks)[0]93false_positive_spk = bytes.fromhex("001400000000000000000000000000000000000cadcb")95genesis_coinbase_hash = bip158_basic_element_hash(genesis_coinbase_spk, 1, genesis_blockhash)96false_positive_hash = bip158_basic_element_hash(false_positive_spk, 1, genesis_blockhash)97assert_equal(genesis_coinbase_hash, false_positive_hash)99assert genesis_blockhash in node.scanblocks(100"start", [{"desc": f"raw({genesis_coinbase_spk.hex()})"}], 0, 0)['relevant_blocks']101assert genesis_blockhash in node.scanblocks(102"start", [{"desc": f"raw({false_positive_spk.hex()})"}], 0, 0)['relevant_blocks']104# check that the filter_false_positives option works105assert genesis_blockhash in node.scanblocks(106"start", [{"desc": f"raw({genesis_coinbase_spk.hex()})"}], 0, 0, "basic", {"filter_false_positives": True})['relevant_blocks']107assert genesis_blockhash not in node.scanblocks(108"start", [{"desc": f"raw({false_positive_spk.hex()})"}], 0, 0, "basic", {"filter_false_positives": True})['relevant_blocks']110# test node with disabled blockfilterindex111assert_raises_rpc_error(-1, "Index is not enabled for filtertype basic",112self.nodes[1].scanblocks, "start", [f"addr({addr_1})"])114# test unknown filtertype115assert_raises_rpc_error(-5, "Unknown filtertype",116node.scanblocks, "start", [f"addr({addr_1})"], 0, 10, "extended")118# test invalid start_height119assert_raises_rpc_error(-1, "Invalid start_height",120node.scanblocks, "start", [f"addr({addr_1})"], 100000000)122# test invalid stop_height123assert_raises_rpc_error(-1, "Invalid stop_height",124node.scanblocks, "start", [f"addr({addr_1})"], 10, 0)125assert_raises_rpc_error(-1, "Invalid stop_height",126node.scanblocks, "start", [f"addr({addr_1})"], 10, 100000000)128# test accessing the status (must be empty)129assert_equal(node.scanblocks("status"), None)131# test aborting the current scan (there is no, must return false)132assert_equal(node.scanblocks("abort"), False)134# test invalid command135assert_raises_rpc_error(-8, "Invalid action 'foobar'", node.scanblocks, "foobar")137# test that null scanobjects is rejected for start138assert_raises_rpc_error(-1, "scanobjects argument is required for the start action", node.scanblocks, "start", None)141if __name__ == '__main__':142ScanblocksTest(__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.