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) 2022-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.6""" Tests the mempool:* tracepoint API interface.7See https://github.com/bitcoin/bitcoin/blob/master/doc/tracing.md#context-mempool8"""10import ctypes11from decimal import Decimal13# Test will be skipped if we don't have bcc installed14try:15from bcc import BPF, USDT # type: ignore[import]16except ImportError:17pass19from test_framework.blocktools import COINBASE_MATURITY20from test_framework.messages import COIN, DEFAULT_MEMPOOL_EXPIRY_HOURS21from test_framework.p2p import P2PDataStore22from test_framework.test_framework import BitcoinTestFramework23from test_framework.util import (24assert_equal,25bpf_cflags,26)27from test_framework.wallet import MiniWallet29MEMPOOL_TRACEPOINTS_PROGRAM = """30# include <uapi/linux/ptrace.h>32// The longest rejection reason is 118 chars and is generated in case of SCRIPT_ERR_EVAL_FALSE by33// strprintf("block-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))34#define MAX_REJECT_REASON_LENGTH 11835// The longest string returned by RemovalReasonToString() is 'sizelimit'36#define MAX_REMOVAL_REASON_LENGTH 937#define HASH_LENGTH 3239struct added_event40{41u8 hash[HASH_LENGTH];42s32 vsize;43s64 fee;44};46struct removed_event47{48u8 hash[HASH_LENGTH];49char reason[MAX_REMOVAL_REASON_LENGTH];50s32 vsize;51s64 fee;52u64 entry_time;53};55struct rejected_event56{57u8 hash[HASH_LENGTH];58char reason[MAX_REJECT_REASON_LENGTH];59};61struct replaced_event62{63u8 replaced_hash[HASH_LENGTH];64s32 replaced_vsize;65s64 replaced_fee;66u64 replaced_entry_time;67u8 replacement_hash[HASH_LENGTH];68s32 replacement_vsize;69s64 replacement_fee;70bool replaced_by_transaction;71};73// BPF perf buffer to push the data to user space.74BPF_PERF_OUTPUT(added_events);75BPF_PERF_OUTPUT(removed_events);76BPF_PERF_OUTPUT(rejected_events);77BPF_PERF_OUTPUT(replaced_events);79int trace_added(struct pt_regs *ctx) {80struct added_event added = {};81void *phash = NULL;82bpf_usdt_readarg(1, ctx, &phash);83bpf_probe_read_user(&added.hash, sizeof(added.hash), phash);84bpf_usdt_readarg(2, ctx, &added.vsize);85bpf_usdt_readarg(3, ctx, &added.fee);87added_events.perf_submit(ctx, &added, sizeof(added));88return 0;89}91int trace_removed(struct pt_regs *ctx) {92struct removed_event removed = {};93void *phash = NULL, *preason = NULL;94bpf_usdt_readarg(1, ctx, &phash);95bpf_probe_read_user(&removed.hash, sizeof(removed.hash), phash);96bpf_usdt_readarg(2, ctx, &preason);97bpf_probe_read_user_str(&removed.reason, sizeof(removed.reason), preason);98bpf_usdt_readarg(3, ctx, &removed.vsize);99bpf_usdt_readarg(4, ctx, &removed.fee);100bpf_usdt_readarg(5, ctx, &removed.entry_time);102removed_events.perf_submit(ctx, &removed, sizeof(removed));103return 0;104}106int trace_rejected(struct pt_regs *ctx) {107struct rejected_event rejected = {};108void *phash = NULL, *preason = NULL;109bpf_usdt_readarg(1, ctx, &phash);110bpf_probe_read_user(&rejected.hash, sizeof(rejected.hash), phash);111bpf_usdt_readarg(2, ctx, &preason);112bpf_probe_read_user_str(&rejected.reason, sizeof(rejected.reason), preason);113rejected_events.perf_submit(ctx, &rejected, sizeof(rejected));114return 0;115}117int trace_replaced(struct pt_regs *ctx) {118struct replaced_event replaced = {};119void *preplaced_hash = NULL, *preplacement_hash = NULL;120bpf_usdt_readarg(1, ctx, &preplaced_hash);121bpf_probe_read_user(&replaced.replaced_hash, sizeof(replaced.replaced_hash), preplaced_hash);122bpf_usdt_readarg(2, ctx, &replaced.replaced_vsize);123bpf_usdt_readarg(3, ctx, &replaced.replaced_fee);124bpf_usdt_readarg(4, ctx, &replaced.replaced_entry_time);125bpf_usdt_readarg(5, ctx, &preplacement_hash);126bpf_probe_read_user(&replaced.replacement_hash, sizeof(replaced.replacement_hash), preplacement_hash);127bpf_usdt_readarg(6, ctx, &replaced.replacement_vsize);128bpf_usdt_readarg(7, ctx, &replaced.replacement_fee);129bpf_usdt_readarg(8, ctx, &replaced.replaced_by_transaction);131replaced_events.perf_submit(ctx, &replaced, sizeof(replaced));132return 0;133}135"""138class MempoolReplaced(ctypes.Structure):139_fields_ = [140("replaced_hash", ctypes.c_ubyte * 32),141("replaced_vsize", ctypes.c_int32),142("replaced_fee", ctypes.c_int64),143("replaced_entry_time", ctypes.c_uint64),144("replacement_hash", ctypes.c_ubyte * 32),145("replacement_vsize", ctypes.c_int32),146("replacement_fee", ctypes.c_int64),147("replaced_by_transaction", ctypes.c_bool),148]151class MempoolTracepointTest(BitcoinTestFramework):152def set_test_params(self):153self.num_nodes = 1154self.setup_clean_chain = True156def skip_test_if_missing_module(self):157self.skip_if_platform_not_linux()158self.skip_if_no_bitcoind_tracepoints()159self.skip_if_no_python_bcc()160self.skip_if_no_bpf_permissions()161self.skip_if_running_under_valgrind()163def added_test(self):164"""Add a transaction to the mempool and make sure the tracepoint returns165the expected txid, vsize, and fee."""167events = []169self.log.info("Hooking into mempool:added tracepoint...")170node = self.nodes[0]171ctx = USDT(pid=node.process.pid)172ctx.enable_probe(probe="mempool:added", fn_name="trace_added")173bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0, cflags=bpf_cflags())175def handle_added_event(_, data, __):176events.append(bpf["added_events"].event(data))178bpf["added_events"].open_perf_buffer(handle_added_event)180self.log.info("Sending transaction...")181fee = Decimal(31200)182tx = self.wallet.send_self_transfer(from_node=node, fee=fee / COIN)184self.log.info("Polling buffer...")185bpf.perf_buffer_poll(timeout=200)187self.log.info("Cleaning up mempool...")188self.generate(node, 1)190self.log.info("Ensuring mempool:added event was handled successfully...")191assert_equal(1, len(events))192event = events[0]193assert_equal(bytes(event.hash)[::-1].hex(), tx["txid"])194assert_equal(event.vsize, tx["tx"].get_vsize())195assert_equal(event.fee, fee)197bpf.cleanup()198self.generate(self.wallet, 1)200def removed_test(self):201"""Expire a transaction from the mempool and make sure the tracepoint returns202the expected txid, expiry reason, vsize, and fee."""204events = []206self.log.info("Hooking into mempool:removed tracepoint...")207node = self.nodes[0]208ctx = USDT(pid=node.process.pid)209ctx.enable_probe(probe="mempool:removed", fn_name="trace_removed")210bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0, cflags=bpf_cflags())212def handle_removed_event(_, data, __):213events.append(bpf["removed_events"].event(data))215bpf["removed_events"].open_perf_buffer(handle_removed_event)217self.log.info("Sending transaction...")218fee = Decimal(31200)219tx = self.wallet.send_self_transfer(from_node=node, fee=fee / COIN)220txid = tx["txid"]222self.log.info("Fast-forwarding time to mempool expiry...")223entry_time = node.getmempoolentry(txid)["time"]224expiry_time = entry_time + 60 * 60 * DEFAULT_MEMPOOL_EXPIRY_HOURS + 5225node.setmocktime(expiry_time)227self.log.info("Triggering expiry...")228self.wallet.get_utxo(txid=txid)229self.wallet.send_self_transfer(from_node=node)231self.log.info("Polling buffer...")232bpf.perf_buffer_poll(timeout=200)234self.log.info("Ensuring mempool:removed event was handled successfully...")235assert_equal(1, len(events))236event = events[0]237assert_equal(bytes(event.hash)[::-1].hex(), txid)238assert_equal(event.reason.decode("UTF-8"), "expiry")239assert_equal(event.vsize, tx["tx"].get_vsize())240assert_equal(event.fee, fee)241assert_equal(event.entry_time, entry_time)243bpf.cleanup()244self.generate(self.wallet, 1)246def replaced_test(self):247"""Replace one and two transactions in the mempool and make sure the tracepoint248returns the expected txids, vsizes, and fees."""250events = []
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.