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) 2020-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"""6Test BIP 377"""9from test_framework.messages import (10CInv,11COIN,12MAX_BLOOM_FILTER_SIZE,13MAX_BLOOM_HASH_FUNCS,14MSG_WTX,15MSG_BLOCK,16MSG_FILTERED_BLOCK,17msg_filteradd,18msg_filterclear,19msg_filterload,20msg_getdata,21msg_mempool,22msg_version,23)24from test_framework.p2p import (25P2PInterface,26P2P_SERVICES,27P2P_SUBVERSION,28P2P_VERSION,29p2p_lock,30)31from test_framework.script import MAX_SCRIPT_ELEMENT_SIZE32from test_framework.test_framework import BitcoinTestFramework33from test_framework.wallet import (34MiniWallet,35getnewdestination,36)39class P2PBloomFilter(P2PInterface):40# This is a P2SH watch-only wallet41watch_script_pubkey = bytes.fromhex('a914ffffffffffffffffffffffffffffffffffffffff87')42# The initial filter (n=10, fp=0.000001) with just the above scriptPubKey added43watch_filter_init = msg_filterload(44data=45b'@\x00\x08\x00\x80\x00\x00 \x00\xc0\x00 \x04\x00\x08$\x00\x04\x80\x00\x00 \x00\x00\x00\x00\x80\x00\x00@\x00\x02@ \x00',46nHashFuncs=19,47nTweak=0,48nFlags=1,49)51def __init__(self):52super().__init__()53self._tx_received = False54self._merkleblock_received = False56def on_inv(self, message):57want = msg_getdata()58for i in message.inv:59# inv messages can only contain TX or BLOCK, so translate BLOCK to FILTERED_BLOCK60if i.type == MSG_BLOCK:61want.inv.append(CInv(MSG_FILTERED_BLOCK, i.hash))62else:63want.inv.append(i)64if len(want.inv):65self.send_without_ping(want)67def on_merkleblock(self, message):68self._merkleblock_received = True70def on_tx(self, message):71self._tx_received = True73@property74def tx_received(self):75with p2p_lock:76return self._tx_received78@tx_received.setter79def tx_received(self, value):80with p2p_lock:81self._tx_received = value83@property84def merkleblock_received(self):85with p2p_lock:86return self._merkleblock_received88@merkleblock_received.setter89def merkleblock_received(self, value):90with p2p_lock:91self._merkleblock_received = value94class FilterTest(BitcoinTestFramework):95def set_test_params(self):96self.num_nodes = 197# whitelist peers to speed up tx relay / mempool sync98self.noban_tx_relay = True99self.extra_args = [[100'-peerbloomfilters',101]]103def generatetoscriptpubkey(self, scriptpubkey):104"""Helper to generate a single block to the given scriptPubKey."""105return self.generatetodescriptor(self.nodes[0], 1, f'raw({scriptpubkey.hex()})')[0]107def test_size_limits(self, filter_peer):108self.log.info('Check that too large filter is rejected')109with self.nodes[0].assert_debug_log(['Misbehaving']):110filter_peer.send_and_ping(msg_filterload(data=b'\xbb'*(MAX_BLOOM_FILTER_SIZE+1)))112self.log.info('Check that max size filter is accepted')113with self.nodes[0].assert_debug_log([], unexpected_msgs=['Misbehaving']):114filter_peer.send_and_ping(msg_filterload(data=b'\xbb'*(MAX_BLOOM_FILTER_SIZE)))115filter_peer.send_and_ping(msg_filterclear())117self.log.info('Check that filter with too many hash functions is rejected')118with self.nodes[0].assert_debug_log(['Misbehaving']):119filter_peer.send_and_ping(msg_filterload(data=b'\xaa', nHashFuncs=MAX_BLOOM_HASH_FUNCS+1))121self.log.info('Check that filter with max hash functions is accepted')122with self.nodes[0].assert_debug_log([], unexpected_msgs=['Misbehaving']):123filter_peer.send_and_ping(msg_filterload(data=b'\xaa', nHashFuncs=MAX_BLOOM_HASH_FUNCS))124# Don't send filterclear until next two filteradd checks are done126self.log.info('Check that max size data element to add to the filter is accepted')127with self.nodes[0].assert_debug_log([], unexpected_msgs=['Misbehaving']):128filter_peer.send_and_ping(msg_filteradd(data=b'\xcc'*(MAX_SCRIPT_ELEMENT_SIZE)))130self.log.info('Check that too large data element to add to the filter is rejected')131with self.nodes[0].assert_debug_log(['Misbehaving']):132filter_peer.send_and_ping(msg_filteradd(data=b'\xcc'*(MAX_SCRIPT_ELEMENT_SIZE+1)))134filter_peer.send_and_ping(msg_filterclear())136def test_msg_mempool(self):137self.log.info("Check that a node with bloom filters enabled services p2p mempool messages")138filter_peer = P2PBloomFilter()140self.log.info("Create two tx before connecting, one relevant to the node another that is not")141rel_txid = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amount=1 * COIN)["txid"]142irr_result = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=2 * COIN)143irr_txid = irr_result["txid"]144irr_wtxid = irr_result["wtxid"]146self.log.info("Send a mempool msg after connecting and check that the relevant tx is announced")147self.nodes[0].add_p2p_connection(filter_peer)148filter_peer.send_and_ping(filter_peer.watch_filter_init)149filter_peer.send_without_ping(msg_mempool())150filter_peer.wait_for_tx(rel_txid)152self.log.info("Request the irrelevant transaction even though it was not announced")153filter_peer.send_without_ping(msg_getdata([CInv(t=MSG_WTX, h=int(irr_wtxid, 16))]))154self.log.info("We should get it anyway because it was in the mempool on connection to peer")155filter_peer.wait_for_tx(irr_txid)157def test_frelay_false(self, filter_peer):158self.log.info("Check that a node with fRelay set to false does not receive invs until the filter is set")159filter_peer.tx_received = False160self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amount=9 * COIN)161# Sync to make sure the reason filter_peer doesn't receive the tx is not p2p delays162filter_peer.sync_with_ping()163assert not filter_peer.tx_received165# Clear the mempool so that this transaction does not impact subsequent tests166self.generate(self.nodes[0], 1)168def test_filter(self, filter_peer):169# Set the bloomfilter using filterload170filter_peer.send_and_ping(filter_peer.watch_filter_init)171# If fRelay is not already True, sending filterload sets it to True172assert self.nodes[0].getpeerinfo()[0]['relaytxes']174self.log.info('Check that we receive merkleblock and tx if the filter matches a tx in a block')175block_hash = self.generatetoscriptpubkey(filter_peer.watch_script_pubkey)176txid = self.nodes[0].getblock(block_hash)['tx'][0]177filter_peer.wait_for_merkleblock(block_hash)178filter_peer.wait_for_tx(txid)180self.log.info('Check that we only receive a merkleblock if the filter does not match a tx in a block')181filter_peer.tx_received = False182block_hash = self.generatetoscriptpubkey(getnewdestination()[1])183filter_peer.wait_for_merkleblock(block_hash)184assert not filter_peer.tx_received186self.log.info('Check that we not receive a tx if the filter does not match a mempool tx')187filter_peer.merkleblock_received = False188filter_peer.tx_received = False189self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=7 * COIN)190filter_peer.sync_with_ping()191assert not filter_peer.merkleblock_received192assert not filter_peer.tx_received194self.log.info('Check that we receive a tx if the filter matches a mempool tx')195filter_peer.merkleblock_received = False196txid = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amount=9 * COIN)["txid"]197filter_peer.wait_for_tx(txid)198assert not filter_peer.merkleblock_received200self.log.info('Check that after deleting filter all txs get relayed again')201filter_peer.send_and_ping(msg_filterclear())202for _ in range(5):203txid = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=7 * COIN)["txid"]204filter_peer.wait_for_tx(txid)206self.log.info('Check that request for filtered blocks is ignored if no filter is set')207filter_peer.merkleblock_received = False208filter_peer.tx_received = False209with self.nodes[0].assert_debug_log(expected_msgs=['received getdata']):210block_hash = self.generatetoscriptpubkey(getnewdestination()[1])211filter_peer.wait_for_inv([CInv(MSG_BLOCK, int(block_hash, 16))])212filter_peer.sync_with_ping()213assert not filter_peer.merkleblock_received214assert not filter_peer.tx_received216self.log.info('Check that sending "filteradd" if no filter is set is treated as misbehavior')217with self.nodes[0].assert_debug_log(['Misbehaving']):218filter_peer.send_and_ping(msg_filteradd(data=b'letsmisbehave'))220self.log.info("Check that division-by-zero remote crash bug [CVE-2013-5700] is fixed")221filter_peer.send_and_ping(msg_filterload(data=b'', nHashFuncs=1))222filter_peer.send_and_ping(msg_filteradd(data=b'letstrytocrashthisnode'))223self.nodes[0].disconnect_p2ps()225def run_test(self):226self.wallet = MiniWallet(self.nodes[0])228filter_peer = self.nodes[0].add_p2p_connection(P2PBloomFilter())229self.log.info('Test filter size limits')230self.test_size_limits(filter_peer)232self.log.info('Test BIP 37 for a node with fRelay = True (default)')233self.test_filter(filter_peer)234self.nodes[0].disconnect_p2ps()236self.log.info('Test BIP 37 for a node with fRelay = False')237# Add peer but do not send version yet238filter_peer_without_nrelay = self.nodes[0].add_p2p_connection(P2PBloomFilter(), send_version=False, wait_for_verack=False)239# Send version with relay=False240version_without_fRelay = msg_version()241version_without_fRelay.nVersion = P2P_VERSION242version_without_fRelay.strSubVer = P2P_SUBVERSION243version_without_fRelay.nServices = P2P_SERVICES244version_without_fRelay.relay = 0245filter_peer_without_nrelay.send_without_ping(version_without_fRelay)246filter_peer_without_nrelay.wait_for_verack()247assert not self.nodes[0].getpeerinfo()[0]['relaytxes']248self.test_frelay_false(filter_peer_without_nrelay)249self.test_filter(filter_peer_without_nrelay)
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.