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) 2017-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 various fingerprinting protections.7If a stale block more than a month old or its header are requested by a peer,8the node should pretend that it does not have it to avoid fingerprinting.9"""11import time13from test_framework.blocktools import create_block14from test_framework.messages import CInv, MSG_BLOCK15from test_framework.p2p import (16P2PInterface,17msg_headers,18msg_block,19msg_getdata,20msg_getheaders,21p2p_lock,22)23from test_framework.test_framework import BitcoinTestFramework24from test_framework.util import (25assert_equal,26)29class P2PFingerprintTest(BitcoinTestFramework):30def set_test_params(self):31self.setup_clean_chain = True32self.num_nodes = 134# Build a chain of blocks on top of given one35def build_chain(self, nblocks, prev_hash, prev_height, prev_median_time):36blocks = []37for _ in range(nblocks):38block = create_block(int(prev_hash, 16), height=prev_height + 1, ntime=prev_median_time + 1)39block.solve()41blocks.append(block)42prev_hash = block.hash_hex43prev_height += 144prev_median_time += 145return blocks47# Send a getdata request for a given block hash48def send_block_request(self, block_hash, node):49msg = msg_getdata()50msg.inv.append(CInv(MSG_BLOCK, block_hash))51node.send_without_ping(msg)53# Send a getheaders request for a given single block hash54def send_header_request(self, block_hash, node):55msg = msg_getheaders()56msg.hashstop = block_hash57node.send_without_ping(msg)59# Checks that stale blocks timestamped more than a month ago are not served60# by the node while recent stale blocks and old active chain blocks are.61# This does not currently test that stale blocks timestamped within the62# last month but that have over a month's worth of work are also withheld.63def run_test(self):64node0 = self.nodes[0].add_p2p_connection(P2PInterface())66# Set node time to 60 days ago67self.nodes[0].setmocktime(int(time.time()) - 60 * 24 * 60 * 60)69# Generating a chain of 10 blocks70block_hashes = self.generatetoaddress(self.nodes[0], 10, self.nodes[0].get_deterministic_priv_key().address)72# Create longer chain starting 2 blocks before current tip73height = len(block_hashes) - 274block_hash = block_hashes[height - 1]75block_time = self.nodes[0].getblockheader(block_hash)["mediantime"] + 176new_blocks = self.build_chain(5, block_hash, height, block_time)78# Force reorg to a longer chain79node0.send_without_ping(msg_headers(new_blocks))80node0.wait_for_getdata([x.hash_int for x in new_blocks])81for block in new_blocks:82node0.send_and_ping(msg_block(block))84# Check that reorg succeeded85assert_equal(self.nodes[0].getblockcount(), 13)87stale_hash = int(block_hashes[-1], 16)89# Check that getdata request for stale block succeeds90self.send_block_request(stale_hash, node0)91node0.wait_for_block(stale_hash, timeout=3)93# Check that getheader request for stale block header succeeds94self.send_header_request(stale_hash, node0)95node0.wait_for_header(hex(stale_hash), timeout=3)97# Longest chain is extended so stale is much older than chain tip98self.nodes[0].setmocktime(0)99block_hash = int(self.generatetoaddress(self.nodes[0], 1, self.nodes[0].get_deterministic_priv_key().address)[-1], 16)100assert_equal(self.nodes[0].getblockcount(), 14)101node0.wait_for_block(block_hash, timeout=3)103# Request for very old stale block should now fail104with p2p_lock:105node0.last_message.pop("block", None)106self.send_block_request(stale_hash, node0)107node0.sync_with_ping()108assert "block" not in node0.last_message110# Request for very old stale block header should now fail111with p2p_lock:112node0.last_message.pop("headers", None)113self.send_header_request(stale_hash, node0)114node0.sync_with_ping()115assert "headers" not in node0.last_message117# Verify we can fetch very old blocks and headers on the active chain118block_hash = int(block_hashes[2], 16)119self.send_block_request(block_hash, node0)120self.send_header_request(block_hash, node0)121node0.sync_with_ping()123self.send_block_request(block_hash, node0)124node0.wait_for_block(block_hash, timeout=3)126self.send_header_request(block_hash, node0)127node0.wait_for_header(hex(block_hash), timeout=3)130if __name__ == '__main__':131P2PFingerprintTest(__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.