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 coin_selection:* tracepoint API interface.7See https://github.com/bitcoin/bitcoin/blob/master/doc/tracing.md#context-coin_selection8"""10# Test will be skipped if we don't have bcc installed11try:12from bcc import BPF, USDT # type: ignore[import]13except ImportError:14pass15from test_framework.test_framework import BitcoinTestFramework16from test_framework.util import (17assert_equal,18assert_greater_than,19assert_raises_rpc_error,20bpf_cflags,21)23coinselection_tracepoints_program = """24#include <uapi/linux/ptrace.h>26#define WALLET_NAME_LENGTH 1627#define ALGO_NAME_LENGTH 1629struct event_data30{31u8 type;32char wallet_name[WALLET_NAME_LENGTH];34// selected coins event35char algo[ALGO_NAME_LENGTH];36s64 target;37s64 waste;38s64 selected_value;40// create tx event41bool success;42s64 fee;43s32 change_pos;45// aps create tx event46bool use_aps;47};49BPF_QUEUE(coin_selection_events, struct event_data, 1024);51int trace_selected_coins(struct pt_regs *ctx) {52struct event_data data;53void *pwallet_name = NULL, *palgo = NULL;54__builtin_memset(&data, 0, sizeof(data));55data.type = 1;56bpf_usdt_readarg(1, ctx, &pwallet_name);57bpf_probe_read_user_str(&data.wallet_name, WALLET_NAME_LENGTH, pwallet_name);58bpf_usdt_readarg(2, ctx, &palgo);59bpf_probe_read_user_str(&data.algo, ALGO_NAME_LENGTH, palgo);60bpf_usdt_readarg(3, ctx, &data.target);61bpf_usdt_readarg(4, ctx, &data.waste);62bpf_usdt_readarg(5, ctx, &data.selected_value);63coin_selection_events.push(&data, 0);64return 0;65}67int trace_normal_create_tx(struct pt_regs *ctx) {68struct event_data data;69void *pwallet_name = NULL;70__builtin_memset(&data, 0, sizeof(data));71data.type = 2;72bpf_usdt_readarg(1, ctx, &pwallet_name);73bpf_probe_read_user_str(&data.wallet_name, WALLET_NAME_LENGTH, pwallet_name);74bpf_usdt_readarg(2, ctx, &data.success);75bpf_usdt_readarg(3, ctx, &data.fee);76bpf_usdt_readarg(4, ctx, &data.change_pos);77coin_selection_events.push(&data, 0);78return 0;79}81int trace_attempt_aps(struct pt_regs *ctx) {82struct event_data data;83void *pwallet_name = NULL;84__builtin_memset(&data, 0, sizeof(data));85data.type = 3;86bpf_usdt_readarg(1, ctx, &pwallet_name);87bpf_probe_read_user_str(&data.wallet_name, WALLET_NAME_LENGTH, pwallet_name);88coin_selection_events.push(&data, 0);89return 0;90}92int trace_aps_create_tx(struct pt_regs *ctx) {93struct event_data data;94void *pwallet_name = NULL;95__builtin_memset(&data, 0, sizeof(data));96data.type = 4;97bpf_usdt_readarg(1, ctx, &pwallet_name);98bpf_probe_read_user_str(&data.wallet_name, WALLET_NAME_LENGTH, pwallet_name);99bpf_usdt_readarg(2, ctx, &data.use_aps);100bpf_usdt_readarg(3, ctx, &data.success);101bpf_usdt_readarg(4, ctx, &data.fee);102bpf_usdt_readarg(5, ctx, &data.change_pos);103coin_selection_events.push(&data, 0);104return 0;105}106"""109class CoinSelectionTracepointTest(BitcoinTestFramework):110def set_test_params(self):111self.num_nodes = 1112self.setup_clean_chain = True114def skip_test_if_missing_module(self):115self.skip_if_platform_not_linux()116self.skip_if_no_bitcoind_tracepoints()117self.skip_if_no_python_bcc()118self.skip_if_no_bpf_permissions()119self.skip_if_no_wallet()120self.skip_if_running_under_valgrind()122def get_tracepoints(self, expected_types):123events = []124try:125for i in range(0, len(expected_types) + 1):126event = self.bpf["coin_selection_events"].pop()127assert_equal(event.wallet_name.decode(), self.default_wallet_name)128assert_equal(event.type, expected_types[i])129events.append(event)130else:131# If the loop exits successfully instead of throwing a KeyError, then we have had132# more events than expected. There should be no more than len(expected_types) events.133assert False134except KeyError:135assert_equal(len(events), len(expected_types))136return events139def determine_selection_from_usdt(self, events):140success = None141use_aps = None142algo = None143waste = None144change_pos = None146is_aps = False147sc_events = []148for event in events:149if event.type == 1:150if not is_aps:151algo = event.algo.decode()152waste = event.waste153sc_events.append(event)154elif event.type == 2:155success = event.success156if not is_aps:157change_pos = event.change_pos158elif event.type == 3:159is_aps = True160elif event.type == 4:161assert is_aps162if event.use_aps:163use_aps = True164assert_equal(len(sc_events), 2)165algo = sc_events[1].algo.decode()166waste = sc_events[1].waste167change_pos = event.change_pos168return success, use_aps, algo, waste, change_pos170def run_test(self):171self.log.info("hook into the coin_selection tracepoints")172ctx = USDT(pid=self.nodes[0].process.pid)173ctx.enable_probe(probe="coin_selection:selected_coins", fn_name="trace_selected_coins")174ctx.enable_probe(probe="coin_selection:normal_create_tx_internal", fn_name="trace_normal_create_tx")175ctx.enable_probe(probe="coin_selection:attempting_aps_create_tx", fn_name="trace_attempt_aps")176ctx.enable_probe(probe="coin_selection:aps_create_tx_internal", fn_name="trace_aps_create_tx")177self.bpf = BPF(text=coinselection_tracepoints_program, usdt_contexts=[ctx], debug=0, cflags=bpf_cflags())179self.log.info("Prepare wallets")180self.generate(self.nodes[0], 101)181wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)183self.log.info("Sending a transaction should result in all tracepoints")184# We should have 5 tracepoints in the order:185# 1. selected_coins (type 1)186# 2. normal_create_tx_internal (type 2)187# 3. attempting_aps_create_tx (type 3)188# 4. selected_coins (type 1)189# 5. aps_create_tx_internal (type 4)190wallet.sendtoaddress(wallet.getnewaddress(), 10)191events = self.get_tracepoints([1, 2, 3, 1, 4])192success, use_aps, _algo, _waste, change_pos = self.determine_selection_from_usdt(events)193assert_equal(success, True)194assert_greater_than(change_pos, -1)196self.log.info("Failing to fund results in 1 tracepoint")197# We should have 1 tracepoints in the order198# 1. normal_create_tx_internal (type 2)199assert_raises_rpc_error(-6, "Insufficient funds", wallet.sendtoaddress, wallet.getnewaddress(), 102 * 50)200events = self.get_tracepoints([2])201success, use_aps, _algo, _waste, change_pos = self.determine_selection_from_usdt(events)202assert_equal(success, False)204self.log.info("Explicitly enabling APS results in 2 tracepoints")205# We should have 2 tracepoints in the order206# 1. selected_coins (type 1)207# 2. normal_create_tx_internal (type 2)208wallet.setwalletflag("avoid_reuse")209wallet.sendtoaddress(address=wallet.getnewaddress(), amount=10, avoid_reuse=True)210events = self.get_tracepoints([1, 2])211success, use_aps, _algo, _waste, change_pos = self.determine_selection_from_usdt(events)212assert_equal(success, True)213assert_equal(use_aps, None)215self.log.info("Change position is -1 if no change is created with APS when APS was initially not used")216# We should have 2 tracepoints in the order:217# 1. selected_coins (type 1)218# 2. normal_create_tx_internal (type 2)219# 3. attempting_aps_create_tx (type 3)220# 4. selected_coins (type 1)221# 5. aps_create_tx_internal (type 4)222wallet.sendtoaddress(address=wallet.getnewaddress(), amount=wallet.getbalance(), subtractfeefromamount=True, avoid_reuse=False)223events = self.get_tracepoints([1, 2, 3, 1, 4])224success, use_aps, _algo, _waste, change_pos = self.determine_selection_from_usdt(events)225assert_equal(success, True)226assert_equal(change_pos, -1)228self.log.info("Change position is -1 if no change is created normally and APS is not used")229# We should have 2 tracepoints in the order:230# 1. selected_coins (type 1)231# 2. normal_create_tx_internal (type 2)232wallet.sendtoaddress(address=wallet.getnewaddress(), amount=wallet.getbalance(), subtractfeefromamount=True)233events = self.get_tracepoints([1, 2])234success, use_aps, _algo, _waste, change_pos = self.determine_selection_from_usdt(events)235assert_equal(success, True)236assert_equal(change_pos, -1)238self.bpf.cleanup()241if __name__ == '__main__':242CoinSelectionTracepointTest(__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.