bitcoin_node_tests package¶
Submodules¶
bitcoin_node_tests.bitcoind module¶
BitcoindAdapter: the oracle, bitcoind on any chain it runs.
The release is 31.1, fetched from bitcoincore.org and verified against its published sha256, the same one [btclib’s integration-bitcoind.yml](https://github.com/btclib-org/btclib/blob/main/.github/workflows/integration-bitcoind.yml) pins – node-integration.yml’s core-master job builds Core’s master from source. This module holds none of that fetch: it is handed the daemon’s own path, already installed – btclib-org/.github’s install_bitcoind.py is what installs it in CI, and tests/integration/conftest.py is what hands it over.
- class bitcoin_node_tests.bitcoind.BitcoindAdapter(executable: str, datadir: Path, rpc_port: int, p2p_port: int, extra_args: Sequence[str] = (), rpc_auth: tuple[str, str] | None = None, *, trace_rpc: bool = False, chain: str = 'regtest')[source]¶
Bases:
NodeAdapterA bitcoind: cookie authentication, and every capability.
RPC authenticates by cookie, the file -datadir writes once the node is listening – rule 1’s “how RPC authenticates”, answered by a file rather than a credential this adapter invents.
Capability.MINE is generatetoaddress over a wallet this adapter loads or creates on use, and it is not a fact fixed for the whole class: mine needs a build with wallet support compiled in, which every release this repository fetches has, but a Core developer’s own build tree can configure out (cmake -DENABLE_WALLET=OFF, or –disable-wallet under autotools) – ISS 35’s own rule (capability.py’s module docstring), a fact read from the running build rather than assumed for the whole class. _has_wallet above is the probe, and __init__ below is what drops the capability from an instance built against a bitcoind lacking it, rather than declaring it and failing mine’s own wallet calls once a test actually calls it. Capability.NODE_WALLET is bitcoind’s own built-in wallet, dropped by the same probe (_WALLET_ONLY): _command passes no -disablewallet, so a build with wallet support serves every wallet RPC. This adapter adds no method for it. A test creates the wallets it names and reaches each through self.rpc.for_wallet(name), bitcoin_core_rpc’s own client for /wallet/<name>, which keeps this client’s credential and transport, –tracerpc included. It names the wallet on every call, never the node’s own endpoint: once mine has loaded _MINER_WALLET beside a test’s own, bitcoind refuses a wallet method there that names no wallet (RPC_WALLET_NOT_SPECIFIED, src/wallet/rpc/util.cpp). Capability.CONNECT is node.connect_nodes (node.py), unconditional here since bitcoind answers addnode and getnetworkinfo the way every Core-compatible node does. Capability.DISCONNECT is node.disconnect_nodes’s own disconnectnode, and Capability.BAN is setban/listbanned/ clearbanned: all three, like addnode, are in this release’s own help listing with no argument, unconditional the same way (measured against the pinned 31.1, [ISS bitcoin-node-tests#43](https://github.com/btclib-org/bitcoin-node-tests/issues/43)). Capability.RAW_MESSAGE is sendmsgtopeer, a debug RPC this release answers (measured against the pinned 31.1: not in help’s own listing, which is for discoverability rather than availability, but help sendmsgtopeer answers its own signature) and which no other adapter’s node offers yet. Capability.BLK_FILES is unconditional too: this is Core’s own binary, so blk*.dat under blocks/ is the format it already writes, not one this adapter has to add anything for. Capability.DEBUG_LOG is debug_log_path below, over the -debug categories _command enables: bitcoind’s own binary is what writes Core’s own wording, which is the fact this capability names. Capability.UA_COMMENT is unconditional too: -uacomment is Core’s own flag, so this is the one node under test that always has it, whatever a later option capability turns out to name. Capability.CLOCK is setmocktime, wrapped by NodeAdapter.set_mock_time (node.py) – a regtest-only RPC in the same standing as sendmsgtopeer above (measured against the pinned 31.1: also absent from help’s own listing, and also answering help setmocktime directly). Capability.RPC_AUTH_CONFIG is unconditional too: rpcauth, rpcwhitelist and rpcwhitelistdefault are this binary’s own bitcoin.conf keys, read the same way regardless of which adapter wrote the file. Capability.RPC_AUTH_NEGATION is unconditional too: -norpcauth disabling every -rpcauth given before it is ArgsManager’s own generic negation of a list-type setting, not a fact -rpcauth itself carries – measured live against the pinned 31.1, a request authenticated with a credential named on the command line before -norpcauth gets 401 once the node has started. Capability.TEST_ACTIVATION_HEIGHT is unconditional too: -testactivationheight is a debug-only flag of this binary’s own, read by ArgsManager before any deployment is checked, so nothing about which deployment or which height is named changes whether the flag itself is recognised. Capability.V2TRANSPORT is unconditional too: -v2transport is this binary’s own flag, defaulting to 1 on the pinned 31.1 (measured against -help’s own listing) – node-to-node connections already report transport_protocol_type: v2 in getpeerinfo with no argument at all, and -v2transport=0 on either side falls back to v1. This is a fact about a connection between two adapters of this kind, not about a Peer (peer.py): that class speaks only the plaintext v1 wire format, so a Peer reaches this node over v1 regardless of -v2transport, BIP324’s own detection accepting a v1 handshake from either side. Capability.DATACARRIER, Capability.PERMIT_BARE_MULTISIG, Capability.DUST_RELAY_FEE, Capability.BYTES_PER_SIGOP and Capability.MAXMEMPOOL are unconditional too: -datacarrier, -datacarriersize, -permitbaremultisig, -dustrelayfee, -bytespersigop and -maxmempool are all ordinary mempool/relay-policy flags of this binary’s own, recognised regardless of what a caller sets them to ([ISS bitcoin-node-tests#14](https://github.com/btclib-org/bitcoin-node-tests/issues/14)). Capability.DESCRIPTOR_ACTIVITY and Capability.BLOCK_STATS are unconditional too: getdescriptoractivity and getblockstats are both RPCs of this binary’s own, answered regardless of which descriptor, block or statistic a caller names. Capability.FASTPRUNE, Capability.INBOUND_EVICTION and Capability.BLOCK_FILTER_INDEX are unconditional too: -fastprune, -maxconnections and -blockfilterindex are this binary’s own flags, and inbound eviction and scanblocks are its own behaviour behind the second and the third. Capability.VALIDATE_ADDRESS is unconditional too: validateaddress is this binary’s own RPC (src/rpc/output_script.cpp), with no wallet behind it. Capability.TYPED_OUTBOUND is addconnection, wrapped by NodeAdapter.add_outbound_connection (node.py): a regtest-only RPC in the same standing as setmocktime above (measured against the pinned 31.1: absent from help’s own listing, answering help addconnection directly). Capability.RPC_WORK_QUEUE is unconditional too: -rpcthreads and -rpcworkqueue are this binary’s own flags, and the refusal of a request past the queue’s bound is its own HTTP server’s (src/httpserver.cpp). Capability.BLOCKS_ONLY and Capability.BLOCK_FROM_PEER are unconditional too: -blocksonly is this binary’s own flag, and getblockfrompeer its own RPC (src/rpc/blockchain.cpp). Capability.ACCEPT_NON_STANDARD is -acceptnonstdtxn, this binary’s own debug-only flag, which it refuses on main alone. Capability.SUSPEND_NETWORK is unconditional: setnetworkactive is this binary’s own RPC (src/rpc/net.cpp). Capability.DESCRIPTOR_INFO is unconditional too: getdescriptorinfo is this binary’s own RPC (src/rpc/output_script.cpp), with no wallet behind it. Capability.PEER_TIMEOUT and Capability.MEMPOOL_EXPIRY are unconditional too: -peertimeout and -mempoolexpiry are this binary’s own flags (src/init.cpp). Capability.SIGN_RAW_TRANSACTION is unconditional: signrawtransactionwithkey and combinerawtransaction are this binary’s own RPCs (src/rpc/rawtransaction.cpp), with no wallet behind them. Capability.INVALIDATE_BLOCK is unconditional too: invalidateblock and reconsiderblock are this binary’s own RPCs (src/rpc/blockchain.cpp), and so is scantxoutset, Capability.SCAN_UTXO_SET’s. Capability.GENERATE is generatetoaddress and generateblock (src/rpc/mining.cpp), which need no wallet, unlike mine’s own generatetoaddress over one: it is declared on regtest alone, not narrowed by _has_wallet. Capability.REINDEX_AFTER_FAILURE is -test’s own reindex_after_failure_noninteractive_yes, a debug-only flag of this binary’s own (src/init.cpp), which it refuses off regtest. It is declared only by a build whose -help-debug lists it, _has_reindex_after_failure being the probe. Capability.PROXY, Capability.CJDNS, Capability.I2P_SAM and Capability.ONLYNET are unconditional too: -proxy, -onion, -proxyrandomize, -cjdnsreachable, -i2psam, -i2pacceptincoming and -onlynet are this binary’s own flags (src/init.cpp). Capability.PROXY_PER_NETWORK is -proxy’s =<network> suffix, this binary’s own (src/init.cpp), declared only by a build whose -help shows it, _has_proxy_per_network being the probe. Capability.TX_RECONCILIATION and Capability.PEER_BLOOM_FILTERS are unconditional: -txreconciliation and -peerbloomfilters are this binary’s own flags (src/init.cpp). Capability.REINDEX is unconditional: -reindex and -reindex-chainstate are this binary’s own flags (src/init.cpp). Capability.CAPTURE_MESSAGES and Capability.BLOCKS_XOR are unconditional: -capturemessages and -blocksxor are this binary’s own flags (src/init.cpp). Capability.ORPHANAGE is unconditional too: getorphantxs is this binary’s own RPC (src/rpc/mempool.cpp), hidden from help’s own listing and answered on every chain. Capability.BLOCK_PROPOSAL is unconditional too: getblocktemplate is this binary’s own RPC (src/rpc/mining.cpp), and its proposal mode answers ahead of the checks its template mode makes of the chain and the peers. Capability.DNS_SEED is -dnsseed and -forcednsseed, this binary’s own flags (src/init.cpp), declared on regtest alone: on any other chain _command turns -dnsseed off itself. Capability.ADDRESS_FETCH is -seednode, this binary’s own flag (src/init.cpp), declared on regtest alone: on any other chain _command passes -connect=0, under which no seed node is asked. Capability.KNOWN_ADDRESSES is unconditional: addpeeraddress and getnodeaddresses are this binary’s own RPCs (src/rpc/net.cpp), the first hidden from help’s own listing. Capability.EXTERNAL_IP is unconditional too: -externalip is this binary’s own flag (src/init.cpp). Capability.PRIVATE_BROADCAST is -privatebroadcast, this binary’s own flag (src/init.cpp), with its getprivatebroadcastinfo and abortprivatebroadcast (src/rpc/mempool.cpp) and mockscheduler (src/rpc/node.cpp), declared on regtest alone, where mockscheduler answers, and only by a build whose -help lists the option, _has_private_broadcast being the probe. Capability.STARTUP_NOTIFY is unconditional too: -startupnotify is this binary’s own flag (src/init.cpp). Capability.DUMP_UTXO_SET is unconditional too: dumptxoutset is this binary’s own RPC (src/rpc/blockchain.cpp). Capability.LOAD_BLOCK is unconditional too: -loadblock is this binary’s own flag (src/init.cpp). Capability.LISTEN_ADDRESS is unconditional too: -port and -bind are this binary’s own flags (src/init.cpp). _command names a -bind of its own, so a test reaching the capability starts a subclass whose _command leaves that entry out. Capability.MAX_TIP_AGE is unconditional too: -maxtipage is this binary’s own flag (src/init.cpp). Capability.PEER_BLOCK_FILTERS is unconditional too: -peerblockfilters is this binary’s own flag (src/init.cpp), off unless a test’s own arguments pass it. Capability.RPC_INFO is unconditional too: getrpcinfo is this binary’s own RPC (src/rpc/server.cpp), its logpath naming debug_log_path below. Capability.MIN_RELAY_TX_FEE is unconditional too: -minrelaytxfee is this binary’s own flag (src/init.cpp). Capability.CLUSTER_LINEARIZATION, Capability.LIMIT_CLUSTER_COUNT and Capability.LIMIT_CLUSTER_SIZE are the cluster mempool’s: getmempoolcluster and getmempoolfeeratediagram are RPCs (src/rpc/mempool.cpp), optimal a field of getmempoolinfo, and -limitclustercount and -limitclustersize flags (src/init.cpp). They are declared only by a build whose -help-debug lists -limitclustercount, _has_cluster_mempool being the probe. Capability.MINIMUM_CHAIN_WORK is unconditional too: -minimumchainwork is this binary’s own debug-only flag (src/init.cpp). Capability.MEMPOOL_GRAPH is unconditional too: getmempoolancestors, getmempooldescendants and gettxspendingprevout are this binary’s own RPCs (src/rpc/mempool.cpp), the last reading the mempool where no -txospenderindex is given. Capability.ALERT_NOTIFY is unconditional too: -alertnotify is this binary’s own flag (src/init.cpp). Capability.PACKAGE_ACCEPTANCE is unconditional too: submitpackage and testmempoolaccept, each taking a package, are this binary’s own RPCs (src/rpc/mempool.cpp), on every chain. Capability.BLOCK_NOTIFY and Capability.SHUTDOWN_NOTIFY are unconditional too: -blocknotify and -shutdownnotify are this binary’s own flags (src/init.cpp). Capability.SETTINGS_FILE is unconditional too: -settings is this binary’s own flag (src/init.cpp), its file read and written by ArgsManager (src/common/args.cpp). Capability.PRECIOUS_BLOCK is unconditional too: preciousblock is this binary’s own RPC (src/rpc/blockchain.cpp). Capability.SIGN_MESSAGE_WITH_PRIVKEY is unconditional too: signmessagewithprivkey and verifymessage are this binary’s own RPCs (src/rpc/signmessage.cpp), with no wallet behind them. Capability.ESTIMATE_SMART_FEE is unconditional too: estimatesmartfee and estimaterawfee are this binary’s own RPCs (src/rpc/fees.cpp). Capability.CHAIN_TIPS is unconditional too: getchaintips is this binary’s own RPC (src/rpc/blockchain.cpp). Capability.ASSUME_VALID is unconditional too: -assumevalid is this binary’s own flag (src/init.cpp). Capability.INCREMENTAL_RELAY_FEE is unconditional too: -incrementalrelayfee is this binary’s own flag (src/node/mempool_args.cpp). Capability.PEER_PERMISSIONS is unconditional too: -whitelist and -whitebind are this binary’s own flags (src/init.cpp).
Every chain the release runs is in chains. On any chain but regtest an instance drops _REGTEST_ONLY’s capabilities, which only regtest answers or which _command turns off elsewhere, and on main _TEST_CHAIN_ONLY’s too.
- property debug_log_path: Path¶
Return this node’s own debug.log, Capability.DEBUG_LOG’s fact.
debug.log in _chain_dir, the directory the cookie file above is read from – bitcoind’s own convention, not a name this adapter invents.
- mine(count: int = 1) list[str][source]¶
Mine count blocks to this adapter’s wallet, return their hashes.
generatetoaddress, over the wallet _load_miner_wallet makes loaded first: Core’s own regtest mining needs an address to pay, and a wallet answers getnewaddress with one that this same client can later spend from, which is Capability.MINE’s whole promise rather than only a taller chain.
- Parameters:
count – how many blocks to mine.
- Returns:
the mined blocks’ own hashes, Core’s own generatetoaddress return value.
bitcoin_node_tests.btclib_node module¶
BtclibNodeAdapter: the first target, btclib-node over python -m.
[btclib-node PR 1012](https://github.com/btclib-org/btclib-node/pull/1012) is what this adapter needs already served: getblock, submitblock, addnode and getnetworkinfo, measured present in src/btclib_node/rpc/callbacks.py’s own dispatch table before this module was written.
Capability.MINE is declared per instance, by _connects_alone’s own probe. mine below builds and solves each block client-side and hands it to submitblock, and a node with no peer never leaves NodeStatus.SyncingHeaders, so what decides is whether the build’s own main.update_chain connects a block at that status: main from btclib-node PR 1152 (84277406) on, the fix [ISS btclib-node#1071](https://github.com/btclib-org/btclib-node/issues/1071) asked for. Measured at main (35b26d2e): a solo node accepts the block and getblockcount/getbestblockhash move onto it. The released 2026.9.24 (422d2640) answers the same submitblock None (accepted) and leaves both at genesis, so an instance built against it does not gain the capability. Neither build names generatetoaddress, generateblock or getblocktemplate in src/btclib_node/rpc/callbacks.py’s own dispatch table, which is why mine builds the block itself.
Independently, [ISS btclib-node#1072](https://github.com/btclib-org/btclib-node/issues/1072) is what makes p2p_getdata itself fail on the released build: block_db never holds the genesis block, so neither getblock nor a p2p getdata can serve the one block a fresh regtest node – mined or not – starts at.
Capability.BLK_FILES is not declared either, and is not a gap this adapter is waiting on: block_db.BlockDB is its own on-disk format, not Core’s blk*.dat, by the decision [ISS btclib-node#573](https://github.com/btclib-org/btclib-node/issues/573) already made and closed on – reading Core’s own files was refused in favour of -connect/-addnode delivering the same blocks over loopback p2p, which this repository’s own Capability.CONNECT already reaches.
Capability.DISCONNECT is declared per instance, by _serves_disconnect’s own probe: a build whose rpc/callbacks.py names disconnectnode in its public callbacks, as main (053d5d83) does from 24de126d on ([ISS btclib-node#1193](https://github.com/btclib-org/btclib-node/issues/1193)). The released 2026.9.24 (422d2640) names no such callback, so an instance built against it does not gain the capability.
Capability.BAN is declared per instance, by _serves_ban_list’s own probe: a build whose rpc/callbacks.py names setban, listbanned and clearbanned in its public callbacks, the table rpc/main.py resolves every request’s method through – main from btclib-node PR 1275 (65510d56) on, the ban list [ISS btclib-node#1088](https://github.com/btclib-org/btclib-node/issues/1088) asked for. The released 2026.9.24 (422d2640) names none of the three, so an instance built against it does not gain the capability.
Capability.UA_COMMENT is not declared: measured against cli.py’s own _build_parser at the released 2026.9.24 (422d2640) and its _OPTIONS at main (8ded5494) alike, -uacomment is registered by neither.
Capability.CLOCK is not declared: setmocktime names no callback in src/btclib_node/rpc/callbacks.py’s own dispatch table, measured at btclib-node 18b6ae1e2c74.
Capability.RPC_AUTH_CONFIG is not a class-level fact the way BLK_FILES, UA_COMMENT and CLOCK above are, and unlike them this is not a fact about btclib-node itself: cli.py’s own _RECOGNIZED_KEYS at 18b6ae1e2c74 already names rpcauth, rpcwhitelist and rpcwhitelistdefault, landed by [ISS btclib-node#1070](https://github.com/btclib-org/btclib-node/issues/1070) alongside the cookie authentication _writes_auth_cookie above already probes for – so it is a fact about which build the executable an instance is constructed with names, and __init__ below declares it on that instance by riding on the same probe rather than by a second one: a build whose btclib_node.rpc.auth imports (_writes_auth_cookie returns True) also recognises those three keys, both having landed in the same commit. The class-level capabilities stays frozenset({Capability.CONNECT}), the fact true of every build; an instance built with an executable carrying rpc.auth gains Capability.RPC_AUTH_CONFIG on top of it. PyPI’s 2026.9.24 release, what this repository’s own TF2_BTCLIB_NODE_PYTHON names, predates that issue – measured live to warn ignoring unknown configuration value rpcauth and start anyway rather than to enforce it – so an instance built against it does not gain the capability; one built against a main carrying #1070 does.
Capability.RPC_AUTH_NEGATION is declared per instance too, by _negates_rpcauth’s own probe: a build whose cli.py reads -noname as -name negated, rpcauth among its _OPTIONS, discards every -rpcauth given before -norpcauth – main from btclib-node PR 1165 (995dd1d0) on, the behaviour [ISS btclib-node#1176](https://github.com/btclib-org/btclib-node/issues/1176) asks for. _writes_auth_cookie does not answer it: 995dd1d0’s parent b1184d7c imports btclib_node.rpc.auth and still refuses -norpcauth as an “Invalid parameter”. The released 2026.9.24 (422d2640) refuses it as argparse’s “unrecognized arguments”, -nolisten being the one negated spelling its _build_parser registers, so an instance built against it does not gain the capability.
Capability.V2TRANSPORT is declared per instance too, by _speaks_v2’s own probe: a build whose cli.build_config reads -nov2transport as v2transport off – main from btclib-node PR 1675 (3f7d2b19) on, step D2 of [ISS btclib-node#1190](https://github.com/btclib-org/btclib-node/issues/1190) – has the flag, the BIP324 codec behind it, and getpeerinfo’s own transport_protocol_type and session_id. The released 2026.9.24 (422d2640) refuses the argument as argparse’s “unrecognized arguments”, so an instance built against it does not gain the capability.
BtclibNodeAdapter._command adds -v1transport=1 where _accepts_v1transport’s probe holds: Peer speaks v1 only, and btclib-node refuses v1 under -v1transport=0 ([ISS btclib-node#1190](https://github.com/btclib-org/btclib-node/issues/1190)). A build without the flag, such as the PyPI 2026.9.24, gets no -v1transport.
Capability.INBOUND_EVICTION is declared per instance too, by _evicts_inbound’s own probe: a build carrying btclib_node.p2p.eviction, a port of Core’s SelectNodeToEvict landed by [ISS btclib-node#1064](https://github.com/btclib-org/btclib-node/issues/1064), disconnects an unprotected inbound peer once its inbound slots are full, and registers -maxconnections to bound them. PyPI’s 2026.9.24 release carries neither, so an instance built against it does not gain the capability.
Capability.DESCRIPTOR_ACTIVITY and Capability.BLOCK_STATS are never declared, on either build: neither getdescriptoractivity nor getblockstats names a callback in src/btclib_node/rpc/callbacks.py’s own dispatch table, measured at the released 2026.9.24 (422d2640) and at main (d7693b2f5a16) alike.
Capability.VALIDATE_ADDRESS is never declared either: validateaddress names no callback in that same dispatch table, measured at the released 2026.9.24 (422d2640) and at main (d0ead5f0) alike.
Capability.TYPED_OUTBOUND is never declared, on either build: addconnection names no callback in that same dispatch table, measured at the released 2026.9.24 (422d2640) and at main (25776772) alike, and addnode, the one RPC there that dials, takes Core’s own arguments, none of them a connection type.
Capability.RPC_WORK_QUEUE is never declared either: cli.py registers neither -rpcthreads nor -rpcworkqueue, measured at the released 2026.9.24 and at main (25776772) alike.
Capability.BLOCKS_ONLY and Capability.BLOCK_FROM_PEER are never declared either: cli.py registers no -blocksonly, and getblockfrompeer names no callback in that same dispatch table, measured at the released 2026.9.24 and at main (25776772) alike.
Capability.ACCEPT_NON_STANDARD is never declared either: -acceptnonstdtxn is not among the flags -help -noconf prints, measured at the released 2026.9.24 and at main (19c5661e) alike.
Capability.SUSPEND_NETWORK is never declared either: setnetworkactive names no callback in src/btclib_node/rpc/callbacks.py’s own dispatch table, measured at the released 2026.9.24 and at main (19c5661e) alike ([ISS btclib-node#1392](https://github.com/btclib-org/btclib-node/issues/1392)).
Capability.DESCRIPTOR_INFO is never declared either: getdescriptorinfo names no callback in src/btclib_node/rpc/callbacks.py’s own dispatch table, measured at the released 2026.9.24 (422d2640) and at main (4e155386) alike.
Capability.PEER_TIMEOUT and Capability.MEMPOOL_EXPIRY are never declared either: cli.py registers neither -peertimeout nor -mempoolexpiry, measured at the released 2026.9.24 (422d2640) and at main (4e155386) alike.
Capability.SIGN_RAW_TRANSACTION is never declared either: neither signrawtransactionwithkey nor combinerawtransaction names a callback in src/btclib_node/rpc/callbacks.py’s own dispatch table, measured at the released 2026.9.24 (422d2640) and at main (4e155386) alike ([ISS btclib-node#1400](https://github.com/btclib-org/btclib-node/issues/1400)).
Capability.INVALIDATE_BLOCK is never declared either: neither invalidateblock nor reconsiderblock names a callback in src/btclib_node/rpc/callbacks.py’s own dispatch table, measured at the released 2026.9.24 (422d2640) and at main (59618462) alike ([ISS btclib-node#1480](https://github.com/btclib-org/btclib-node/issues/1480), [ISS btclib-node#1536](https://github.com/btclib-org/btclib-node/issues/1536)).
Capability.GENERATE and Capability.SCAN_UTXO_SET are never declared either: none of generatetoaddress, generateblock, help and scantxoutset names a callback in that same dispatch table, measured at the released 2026.9.24 (422d2640) and at main (d2b4efa5) alike ([ISS btclib-node#1404](https://github.com/btclib-org/btclib-node/issues/1404), [ISS btclib-node#1396](https://github.com/btclib-org/btclib-node/issues/1396), [ISS btclib-node#1405](https://github.com/btclib-org/btclib-node/issues/1405), [ISS btclib-node#1406](https://github.com/btclib-org/btclib-node/issues/1406)).
Capability.REINDEX_AFTER_FAILURE is never declared either: cli.py registers no -test, measured at the released 2026.9.24 and at main (4e155386) alike, each refusing -test=reindex_after_failure_noninteractive_yes as an unknown argument.
Capability.PROXY is never declared either: cli.py registers none of -proxy, -onion and -proxyrandomize, measured against its _build_parser at the released 2026.9.24 (422d2640) and its _OPTIONS at main (d2b4efa5) alike. Nor is Capability.PROXY_PER_NETWORK, the suffix of an option it does not register. Nor are Capability.CJDNS, Capability.I2P_SAM and Capability.ONLYNET: it registers none of -cjdnsreachable, -i2psam, -i2pacceptincoming and -onlynet, measured the same way at the released 2026.9.24 and at main (1aeebc67).
Capability.NODE_WALLET is never declared either: btclib-node keeps no wallet, src/btclib_node/rpc/callbacks.py’s own dispatch table naming no wallet RPC – no createwallet, getnewaddress or signmessage – measured at the released 2026.9.24 and at main (d2b4efa5) alike. A wallet kept beside the node is [ISS 199](https://github.com/btclib-org/bitcoin-node-tests/issues/199)’s to reach.
Capability.TX_RECONCILIATION and Capability.PEER_BLOOM_FILTERS are never declared either: cli.py registers neither -txreconciliation nor -peerbloomfilters, measured at the released 2026.9.24 (422d2640) and at main (dca9c2be) alike, txreconciliation being a -debug category at main.
Capability.REINDEX is never declared either: each build refuses -reindex and -reindex-chainstate as unknown arguments, measured at the released 2026.9.24 and at main (338f64c5) alike ([ISS btclib-node#1415](https://github.com/btclib-org/btclib-node/issues/1415)).
Capability.CAPTURE_MESSAGES and Capability.BLOCKS_XOR are never declared either: cli.py registers neither -capturemessages nor -blocksxor, measured against its _build_parser at the released 2026.9.24 (422d2640) and its _OPTIONS at main (98448c4d) alike.
Capability.ORPHANAGE is never declared either: getorphantxs names no callback in src/btclib_node/rpc/callbacks.py’s own dispatch table, and no source file names an orphan, measured at the released 2026.9.24 (422d2640) and at main (1aeebc67) alike ([ISS btclib-node#1420](https://github.com/btclib-org/btclib-node/issues/1420)).
Capability.BLOCK_PROPOSAL is never declared either: getblocktemplate names no callback in src/btclib_node/rpc/callbacks.py’s own dispatch table, measured at the released 2026.9.24 (422d2640) and at main (503edaec) alike, each answering it Method not found ([ISS btclib-node#1427](https://github.com/btclib-org/btclib-node/issues/1427)).
Capability.ADDRESS_FETCH is never declared either: cli.py registers no -seednode, measured against its _build_parser at the released 2026.9.24 (422d2640) and its _OPTIONS at main (d4559960) alike ([ISS btclib-node#1192](https://github.com/btclib-org/btclib-node/issues/1192)).
Capability.DNS_SEED is never declared either: cli.py registers neither -dnsseed nor -forcednsseed, measured against its _build_parser at the released 2026.9.24 (422d2640) and its _OPTIONS at main (d4559960) alike ([ISS btclib-node#1192](https://github.com/btclib-org/btclib-node/issues/1192), [ISS btclib-node#1265](https://github.com/btclib-org/btclib-node/issues/1265)). Nor is Capability.KNOWN_ADDRESSES: neither addpeeraddress nor getnodeaddresses names a callback in src/btclib_node/rpc/callbacks.py’s own dispatch table, measured at the same two commits ([ISS btclib-node#1443](https://github.com/btclib-org/btclib-node/issues/1443)).
Capability.EXTERNAL_IP is never declared either: cli.py registers no -externalip, measured at the released 2026.9.24 (422d2640) and at main (d4559960) alike ([ISS btclib-node#1445](https://github.com/btclib-org/btclib-node/issues/1445)).
Capability.PRIVATE_BROADCAST is never declared either: cli.py registers no -privatebroadcast, and neither getprivatebroadcastinfo nor abortprivatebroadcast names a callback in src/btclib_node/rpc/callbacks.py’s own dispatch table, measured at the released 2026.9.24 (422d2640) and at main (d4559960) alike, privatebroadcast being a -debug category at main.
Capability.STARTUP_NOTIFY is never declared either: cli.py registers no -startupnotify, measured at the released 2026.9.24 (422d2640) and at main (d4559960) alike ([ISS btclib-node#1449](https://github.com/btclib-org/btclib-node/issues/1449)).
Capability.DUMP_UTXO_SET is never declared either: no source file names dumptxoutset, measured at the released 2026.9.24 (422d2640) and at main (ecb9b190) alike ([ISS btclib-node#1471](https://github.com/btclib-org/btclib-node/issues/1471)).
Capability.LOAD_BLOCK is never declared either: no source file names loadblock, measured at the released 2026.9.24 (422d2640) and at main (ecb9b190) alike. Reading Core’s block files is left out by decision, the node taking the same blocks over p2p ([ISS btclib-node#573](https://github.com/btclib-org/btclib-node/issues/573)).
Capability.LISTEN_ADDRESS is never declared either: cli.py registers -port and no -bind, and the listener binds 0.0.0.0 and :: alone, measured at the released 2026.9.24 (422d2640) and at main (ecb9b190) alike ([ISS btclib-node#1257](https://github.com/btclib-org/btclib-node/issues/1257)).
Capability.MAX_TIP_AGE is never declared either: cli.py registers no -maxtipage, the age being constants.py’s own MAX_TIP_AGE of a day, measured at the released 2026.9.24 (422d2640) and at main (9ae620c2) alike ([ISS btclib-node#1474](https://github.com/btclib-org/btclib-node/issues/1474)).
Capability.PEER_BLOCK_FILTERS is never declared either: cli.py registers no -peerblockfilters, and p2p/callbacks.py answers getcfilters, getcfheaders and getcfcheckpt for every peer while p2p/connection.py signals NODE_COMPACT_FILTERS to every one, with no option to turn either off, measured at the released 2026.9.24 (422d2640) and at main (9ae620c2) alike ([ISS btclib-node#1395](https://github.com/btclib-org/btclib-node/issues/1395)).
Capability.RPC_INFO is never declared either: getrpcinfo names no callback in src/btclib_node/rpc/callbacks.py’s own dispatch table, measured at the released 2026.9.24 (422d2640) and at main (76d7daa4) alike ([ISS btclib-node#1486](https://github.com/btclib-org/btclib-node/issues/1486)).
Capability.CLUSTER_LINEARIZATION is never declared either: getmempoolcluster and getmempoolfeeratediagram name no callback in src/btclib_node/rpc/callbacks.py’s own dispatch table, measured at the released 2026.9.24 (422d2640) and at main (93c1d066) alike ([ISS btclib-node#1499](https://github.com/btclib-org/btclib-node/issues/1499)).
Capability.MINIMUM_CHAIN_WORK is never declared either: cli.py registers no -minimumchainwork, the floor being the chain’s own minimum_chain_work (btclib.consensus), measured at the released 2026.9.24 (422d2640) and at main (93c1d066) alike ([ISS btclib-node#1500](https://github.com/btclib-org/btclib-node/issues/1500)).
Capability.MEMPOOL_GRAPH is never declared either: getmempoolancestors, getmempooldescendants and gettxspendingprevout name no callback in src/btclib_node/rpc/callbacks.py’s own dispatch table, measured at the released 2026.9.24 (422d2640) and at main (93c1d066) alike ([ISS btclib-node#1501](https://github.com/btclib-org/btclib-node/issues/1501)).
Capability.PACKAGE_ACCEPTANCE is never declared either: no file under src/ names submitpackage, measured at the released 2026.9.24 (422d2640) and at main (93c1d066) alike ([ISS btclib-node#1494](https://github.com/btclib-org/btclib-node/issues/1494)).
Capability.MIN_RELAY_TX_FEE is declared per instance, by _sets_min_relay_fee’s own probe: a build whose cli.py registers -minrelaytxfee – main from btclib-node PR 1452 (88f5c894) on, the option [ISS btclib-node#1332](https://github.com/btclib-org/btclib-node/issues/1332) asked for. The released 2026.9.24 (422d2640) registers none, its Config.min_relay_feerate taking no flag, so an instance built against it does not gain the capability.
Capability.ALERT_NOTIFY is never declared either: no source file names alertnotify, measured at the released 2026.9.24 (422d2640) and at main (93c1d066) alike ([ISS btclib-node#1475](https://github.com/btclib-org/btclib-node/issues/1475)).
Capability.BLOCK_NOTIFY and Capability.SHUTDOWN_NOTIFY are never declared either: no file names blocknotify or shutdownnotify, measured at the released 2026.9.24 (422d2640) and at main (84677942) alike ([ISS btclib-node#1519](https://github.com/btclib-org/btclib-node/issues/1519)).
Capability.SETTINGS_FILE is never declared either: cli.py registers no -settings, and no file under src/ reads or writes a settings.json, measured at the released 2026.9.24 (422d2640) and at main (84677942) alike ([ISS btclib-node#1523](https://github.com/btclib-org/btclib-node/issues/1523)).
Capability.PRECIOUS_BLOCK is never declared either: preciousblock names no callback in src/btclib_node/rpc/callbacks.py’s own dispatch table, measured at the released 2026.9.24 (422d2640) and at main (d0943a37) alike ([ISS btclib-node#1534](https://github.com/btclib-org/btclib-node/issues/1534)).
Capability.SIGN_MESSAGE_WITH_PRIVKEY is never declared either: no file names signmessagewithprivkey or verifymessage, measured at the released 2026.9.24 (422d2640) and at main (59618462) alike ([ISS btclib-node#1538](https://github.com/btclib-org/btclib-node/issues/1538)).
Capability.ESTIMATE_SMART_FEE is never declared either: no file names estimatesmartfee or estimaterawfee, measured at the released 2026.9.24 (422d2640) and at main (02b2ed6e) alike ([ISS btclib-node#1543](https://github.com/btclib-org/btclib-node/issues/1543)).
Capability.CHAIN_TIPS is declared per instance, by _serves_chain_tips’s own probe: a build whose rpc/callbacks.py names getchaintips in its public callbacks, as main (e471c576) does. The released 2026.9.24 (422d2640) names no such callback, so an instance built against it does not gain the capability.
Capability.ASSUME_VALID is never declared: no file names assumevalid, measured at the released 2026.9.24 (422d2640) and at main (26ec9ec5) alike ([ISS btclib-node#1576](https://github.com/btclib-org/btclib-node/issues/1576)).
Capability.INCREMENTAL_RELAY_FEE is never declared: cli.py registers no -incrementalrelayfee, measured at the released 2026.9.24 (422d2640) and at main (eb18985c) alike ([ISS btclib-node#1596](https://github.com/btclib-org/btclib-node/issues/1596)).
Capability.PEER_PERMISSIONS is never declared: cli.py registers no -whitelist and no -whitebind, measured at the released 2026.9.24 (422d2640) and at main (f2b326d6) alike, and getpeerinfo lists an empty permissions on main and none on the release ([ISS btclib-node#1320](https://github.com/btclib-org/btclib-node/issues/1320)).
- class bitcoin_node_tests.btclib_node.BtclibNodeAdapter(executable: str, datadir: Path, rpc_port: int, p2p_port: int, extra_args: Sequence[str] = (), rpc_auth: tuple[str, str] | None = None, *, trace_rpc: bool = False, chain: str = 'regtest')[source]¶
Bases:
NodeAdapterA btclib-node, run as python -m btclib_node.
Not the console script pip install btclib-node also provides: cli.py’s own module docstring is where the reason not to run that entry point directly from a spawning process is argued – ReimportedMainProcessError reaching every path but the one python -m and its own __main__.py exempt.
chains is every chain _CHAIN_DIRS names, testnet4 not among them. On any chain but regtest an instance drops Capability.MINE: MiniWallet builds regtest’s own blocks alone.
- property log_path: Path¶
Return this node’s own history.log, the disk family’s own fact.
Not debug_log_path: BitcoindAdapter’s own name is Core’s file, and this node writes no file of that name. history.log is btclib_node’s own, in _chain_dir, the directory the cookie file above is read from.
- mine(count: int = 1) list[str][source]¶
Mine count blocks client-side, return their hashes once connected.
MiniWallet.generate (mini_wallet.py) builds, solves and submits each block, extending whatever tip the node answers now, and pays its coinbase to that class’s own anyone-can-spend script: a caller that means to spend what it mines holds a MiniWallet of its own instead. submitblock answering None says the block was stored, not that it became the tip: connecting it is main.update_chain’s job rather than rpc/callbacks.py’s own submit_block, so this polls getbestblockhash until it names the last block, where BitcoindAdapter.mine’s own generatetoaddress answers only once connected.
- Parameters:
count – how many blocks to mine.
- Returns:
the mined blocks’ own hashes, oldest first, the shape of BitcoindAdapter.mine’s own.
- Raises:
TimeoutError – the node stored the last block and never made it its tip.
bitcoin_node_tests.capability module¶
What a node can do, and how many tests skipped for lack of it.
Rule 4 of [ISS 2220](https://github.com/btclib-org/btclib/issues/2220): a node declares its capabilities, a test needing one the node lacks skips, and the run prints a skip count per capability – a number the node’s own tracker can read, never a silent pass. A capability names what a node can do, not how it spells the call that does it: Capability.CONNECT covers what node.connect_nodes does today because that is the one way both adapters answer it alike, and a node reaching it another way would still declare the same member.
A fact that differs between two builds of one node is read from the build under test, never fixed as a class-wide constant ([ISS bitcoin-node-tests#35](https://github.com/btclib-org/bitcoin-node-tests/issues/35)): the class says what every build of that node can do, and the running build says what this one can. Where the fact is whether a capability is there at all, it is still a Capability, declared per instance from a probe rather than fixed for the whole class: BtclibNodeAdapter.__init__’s own _writes_auth_cookie decides, per instance, whether Capability.RPC_AUTH_CONFIG is declared at all, and BitcoindAdapter.__init__’s own _has_wallet narrows Capability.MINE and Capability.NODE_WALLET off an instance built against a bitcoind without wallet support, widening or narrowing the class’s own frozen set rather than replacing it outright. Where the fact is not whether a capability exists but how a capability every build declares alike behaves once exercised – whether bitcoind’s own ADD_ONION negotiates BIP434’s proof-of-work defenses (tests/integration/feature_torcontrol_bitcoind_test.py), which p2p protocol version a build speaks (tests/integration/p2p_bip434_feature_bitcoind_test.py) – there is no skip to gate, so nothing is added to this enum: the test itself reads the fact off the running node’s own RPC or its own wire behaviour and asserts whichever shape that build produces. The enum is for what SkipCounts.report’s own count is over – an instance either declares a member or it does not, and a run either skips a test for lacking it or does not – and an expectation with no skip attached to it is not that.
p2p_getdata needs none of the members below: it asks only for what every adapter provides unconditionally – a running node, its RPC and its p2p port – so require is exercised by tests/capability_test.py rather than by that test. tests/integration/ is where the rest of the members are exercised, MINE, CONNECT and RAW_MESSAGE among them.
UA_COMMENT is the first of the option family ([ISS bitcoin-node-tests#3](https://github.com/btclib-org/bitcoin-node-tests/issues/3), whose own body already answers the design question this way), and it sets the shape every later option takes: one member per Core option a ported test actually asks for, added the moment that test is ported rather than declared for the whole of Core’s option surface up front, which is large and mostly untouched by any test this suite has ported. The rejected alternative is a single parameterized Capability.OPTION, keyed on the option’s own name, with one node-side mapping of names to “has it”; rule 4’s own count is what this file already prints one line per member of, sorted by value, and a parameterized capability would need to fold that back to one line itself rather than getting it from SkipCounts.report unchanged. One member per option is what keeps a name in the enum a name in the printed summary, at the cost this module already carries: a member added by hand, per option, per test ported. A member’s own value is what that summary prints, so it is spelled the same as the member rather than shortened on its own, keeping the summary’s own name for a capability the same as the code’s.
Not every Core option a test names earns a member here. Step 5’s own charter carries the narrower rule first: “wallet and USDT tests stay out” and “a test of bitcoind’s own options runs against bitcoind alone”. The first rule’s wallet half is overridden by [ISS bitcoin-node-tests#45](https://github.com/btclib-org/bitcoin-node-tests/issues/45), which brings Core’s node-wallet tests in, one body over both nodes behind NODE_WALLET, which BitcoindAdapter alone declares. An option only bitcoind has a reason to carry – -disablewallet, -torcontrol – is never declared or skipped by another node under this mechanism; it is a bitcoind-only test’s subject, a shape this module does not build. [ISS bitcoin-node-tests#23](https://github.com/btclib-org/bitcoin-node-tests/issues/23) gives that shape a place of its own, and this paragraph is the one rule that decides which option qualifies, rather than a decision made test by test: a *_bitcoind_test.py module with no *_btclib_node_test.py counterpart, asking require for nothing, is a bitcoind-only test. TF2.md’s own per-test ledger spells such a row bitcoind only in its btclib-node column rather than any skip (…) – a cell nothing will ever turn into a pass or a fail, unlike an ordinary skip.
This module imports no test runner. pyproject.toml’s own [project] dependencies name two packages and no third (this package’s own __init__.py says so), and Core’s own test framework runs under no pytest at all – objective 2 of ISS btclib-org/btclib#2220 is that Core can adopt this suite, which a hard runtime dependency on somebody else’s test runner would work against. require raises MissingCapabilityError, its own exception, rather than calling pytest.skip; tests/conftest.py’s own pytest_runtest_call hookwrapper is what translates that into an actual skip, in the one tree that ever runs these tests under pytest. A prior version imported pytest here directly, which sphinx-build’s own autodoc – run from the docs dependency group, which does not install pytest – failed to import with ModuleNotFoundError: No module named ‘pytest’, cascading into every module that imports this one.
- class bitcoin_node_tests.capability.Capability(*values)[source]¶
Bases:
EnumWhat a test may ask a node adapter for, named by what it does.
MINE – produce a block and have the node accept it as its own new tip, however it gets there: generatetoaddress for a node with a wallet, a client-built block over submitblock for one without. CONNECT – accept a second node of its own kind as a peer, the way node.connect_nodes dials and waits for one. DISCONNECT – drop an already-connected peer on request, the way node.disconnect_nodes asks over disconnectnode. Not implied by CONNECT: addnode and disconnectnode are two different RPCs, and a node answering the first need not answer the second (measured of btclib-node, [ISS bitcoin-node-tests#43](https://github.com/btclib-org/bitcoin-node-tests/issues/43)’s own finding). BAN – record and enforce a setban/listbanned/clearbanned ban list, the way rpc_setban’s own subject does: an address already connected drops the moment it is banned. Node-linking’s own third capability, beside CONNECT and DISCONNECT ([ISS bitcoin-node-tests#43](https://github.com/btclib-org/bitcoin-node-tests/issues/43)). RAW_MESSAGE – send an arbitrary p2p message to an already-connected peer, named by that peer’s own index, the way Core’s sendmsgtopeer does on the node under test’s behalf; p2p_net_deadlock’s own subject needs a node that offers it, and today only bitcoind does. BLK_FILES – write its chain to disk the way Core does, blk*.dat files under a blocks/ directory that a caller may read directly: a fact the wire has no call for, unlike where the option that names the directory lives, which a node without this capability may still accept. DEBUG_LOG – write a debug log a caller can read and match Core’s own wording against, the way assert_debug_log (debug_log.py) does. The log family (the third of step 5’s five, [ISS 5](https://github.com/btclib-org/bitcoin-node-tests/issues/5)) is what this names: where the fact an assert_debug_log call in Core asks about is also observable on the wire – a disconnect, getpeerinfo – the ported assertion reads the wire instead and needs no capability at all; where only the log carries it, a test needs this one. A node’s own log is truthful about what it did, not about what Core would have called it, so a byte-for-byte match against Core’s own wording is a fact only bitcoind’s own binary can supply. UA_COMMENT – append a caller-chosen comment to the subversion string getnetworkinfo reports, the fact Core’s own -uacomment asks for. The first of the option family (rule 4, [ISS bitcoin-node-tests#3](https://github.com/btclib-org/bitcoin-node-tests/issues/3)). CLOCK – accept a caller-set wall clock, Core’s setmocktime (test/functional/test_framework/test_node.py’s own TestNode.setmocktime): every RPC and every p2p timeout this node reads the time through sees the caller’s clock instead of the real one, until 0 is set to release it back. RPC_AUTH_CONFIG – recognise rpcauth, rpcwhitelist and rpcwhitelistdefault written into bitcoin.conf, the way Core’s own rpc_users and rpc_whitelist add a credential or restrict its RPC surface through the config file rather than the command line. The disk family’s own second capability ([ISS bitcoin-node-tests#7](https://github.com/btclib-org/bitcoin-node-tests/issues/7)): the fact is bitcoin.conf itself, datadir_path’s own file, not a fact the wire has a call for. RPC_AUTH_NEGATION – recognise -norpcauth on the command line, disabling every -rpcauth value given before it, the way Core’s own rpc_users checks it. Not RPC_AUTH_CONFIG itself: a node can parse -rpcauth and still have no -no<name> negation of any kind, which is the case of some btclib-node builds and not others (btclib_node.py’s own module docstring names which), so a test asking for the negation needs its own capability rather than riding on the one for the value it negates. TEST_ACTIVATION_HEIGHT – hold one buried soft fork’s own deployment inactive until a caller-chosen height, Core’s own debug-only -testactivationheight=<deployment>@<height>. Regtest’s own chain parameters activate every buried deployment otherwise – BIP34, BIP66, BIP65 and CSV from height 1, segwit from genesis (src/kernel/chainparams.cpp’s own comment on each, “Always active unless overridden”, measured against the pinned 31.1), so this is what lets a test hold one of them back long enough to observe the boundary at all ([ISS bitcoin-node-tests#14](https://github.com/btclib-org/bitcoin-node-tests/issues/14)). V2TRANSPORT – accept BIP324 v2 connections from another node of this kind, the way Core’s own -v2transport does: getpeerinfo’s transport_protocol_type reads v2 on such a connection. Not a fact about a Peer (peer.py): that class speaks only the plaintext v1 wire format, so this capability is unconditional on node-to-node connections alone – issue [bitcoin-node-tests#36](https://github.com/btclib-org/bitcoin-node-tests/issues/36). DATACARRIER – recognise -datacarrier and -datacarriersize, Core’s own pair of relay-policy knobs for an OP_RETURN output: the first turns its relay on or off, the second bounds how large one may be. One member for the pair rather than two: neither flag is ever tested apart from the other in [ISS bitcoin-node-tests#14](https://github.com/btclib-org/bitcoin-node-tests/issues/14)’s own mempool_datacarrier.py, both landing at once. PERMIT_BARE_MULTISIG – recognise -permitbaremultisig, Core’s own switch for whether a bare OP_CHECKMULTISIG output is relayed at all, checked apart from DUST_RELAY_FEE because a test can ask for either alone. DUST_RELAY_FEE – recognise -dustrelayfee, Core’s own per-kilobyte rate an output’s own value is measured against to call it dust. BYTES_PER_SIGOP – recognise -bytespersigop, Core’s own conversion rate from a sigop to the virtual bytes a transaction’s own mempool footprint is billed for. LIMIT_CLUSTER_COUNT – recognise -limitclustercount, Core’s own cap on how many transactions, in-mempool and in-package together, one mempool cluster may hold ([ISS bitcoin-node-tests#14](https://github.com/btclib-org/bitcoin-node-tests/issues/14)’s own mempool_package_limits.py). Checked apart from LIMIT_CLUSTER_SIZE: a test can ask for either alone. LIMIT_CLUSTER_SIZE – recognise -limitclustersize, Core’s own cap on one cluster’s own total virtual size (mempool_updatefromblock.py). MAXMEMPOOL – recognise -maxmempool, Core’s own cap, in megabytes, on the mempool’s own total size – what mempool_util.fill_mempool needs a node started small enough under to reach eviction at all ([ISS bitcoin-node-tests#70](https://github.com/btclib-org/bitcoin-node-tests/issues/70)). DESCRIPTOR_ACTIVITY – answer getdescriptoractivity, Core’s own RPC pairing spend and receive events with the descriptors and blocks a caller names. Named for the RPC rather than for an option, the way MINE/CONNECT/DISCONNECT/BAN/RAW_MESSAGE already are: no flag gates it, so what a node either answers or does not is the method itself ([ISS bitcoin-node-tests#14](https://github.com/btclib-org/bitcoin-node-tests/issues/14)). BLOCK_STATS – answer getblockstats, Core’s own per-block statistics RPC, named the same way and for the same reason. FASTPRUNE – recognise -fastprune, Core’s own debug-only switch to block files far smaller than a real node’s, so that a test reaches a block file’s size limit with a single large block ([ISS bitcoin-node-tests#14](https://github.com/btclib-org/bitcoin-node-tests/issues/14)’s own feature_fastprune.py). INBOUND_EVICTION – make room for a new inbound peer, once its inbound slots are full, by disconnecting an existing one that none of Core’s own protections covers, the slots being what -maxconnections bounds (p2p_eviction.py). Named for the eviction rather than for the option: a node can accept -maxconnections and refuse the new peer instead of evicting an old one. BLOCK_FILTER_INDEX – keep BIP158’s basic block filter for every block once -blockfilterindex asks for it, and answer scanblocks (rpc_scanblocks.py) and getblockfilter (rpc_getblockfilter.py) from that index. VALIDATE_ADDRESS – answer validateaddress, Core’s own RPC decoding an address for the chain the node runs: the scriptPubKey a valid one decodes to, and the error and error_locations an invalid one earns (rpc_validateaddress.py). Named for the RPC, as BLOCK_STATS is. TYPED_OUTBOUND – dial an address the caller names as the outbound connection type the caller chooses, outbound-full-relay, block-relay-only, addr-fetch or feeler, the way Core’s TestNode.add_outbound_p2p_connection asks addconnection to ([ISS bitcoin-node-tests#44](https://github.com/btclib-org/bitcoin-node-tests/issues/44)). Not CONNECT: addnode dials a manual connection, one type only. NodeAdapter.add_outbound_connection (node.py) is the call, and peer.Listener what a test has the node dial. RPC_WORK_QUEUE – serve RPC on as few worker threads as a caller names, queueing at most as many requests as it names for them and refusing the rest, the facts Core’s own -rpcthreads and -rpcworkqueue set (rpc_echo_payload.py). One member for the pair, as DATACARRIER is: the one ported test asking for either sets both. BLOCKS_ONLY – recognise -blocksonly, Core’s own switch to a node relaying no transactions, which also selects no BIP152 high-bandwidth peer and asks for a full block rather than a compact one (p2p_compactblocks_blocksonly.py). BLOCK_FROM_PEER – answer getblockfrompeer, Core’s own RPC asking a named peer for a block whose header the node already has (rpc_getblockfrompeer.py). Named for the RPC, as BLOCK_STATS is. ACCEPT_NON_STANDARD – admit to its mempool, on request, a transaction its standardness rules refuse while its script checks still run, the fact Core’s own -acceptnonstdtxn sets (feature_cltv.py, whose spends prepend opcodes to a scriptSig). SUSPEND_NETWORK – stop all p2p activity on request, dropping every peer, and resume it on request, the way Core’s own setnetworkactive does (p2p_node_network_limited.py). DESCRIPTOR_INFO – answer getdescriptorinfo, Core’s own RPC analysing an output descriptor: its checksummed form, the single-path descriptors a multipath one expands to, whether it is ranged, solvable and carries a private key, and the error a malformed one earns (rpc_getdescriptorinfo.py). Named for the RPC, as BLOCK_STATS is. PEER_TIMEOUT – recognise -peertimeout, Core’s own bound on how long a new connection may go without finishing its handshake, and before which no inactivity check reaches it at all (p2p_timeouts.py, p2p_ping.py). MEMPOOL_EXPIRY – recognise -mempoolexpiry, Core’s own age, in hours, past which a transaction leaves the mempool (mempool_expiry.py). SIGN_RAW_TRANSACTION – sign a raw transaction’s inputs with private keys the caller hands it, and merge copies of one transaction each carrying some of the signatures into one: Core’s own signrawtransactionwithkey and combinerawtransaction, which need no wallet (rpc_createmultisig.py’s do_multisig, rpc_signrawtransactionwithkey.py). One member for the pair, as DATACARRIER is. INVALIDATE_BLOCK – mark a block invalid on request, and go back to the best chain not holding it, Core’s own invalidateblock (feature_csv_activation.py, which takes each accepted block back off); and take that mark off a block, its ancestors and its descendants on request, going back to the best chain again, Core’s own reconsiderblock (rpc_invalidateblock.py). Named for the first RPC, as BLOCK_STATS is, and one member for the pair, as SIGN_RAW_TRANSACTION is. REINDEX_AFTER_FAILURE – refuse to start over a block index missing from blocks/index, advising a reindex, and reindex from its block files instead on a start given Core’s own debug-only -test=reindex_after_failure_noninteractive_yes (feature_reindex_init.py). GENERATE – build and solve a block itself, on request, paying the output an address or a descriptor names and carrying the transactions the caller names, and take it as its new tip: Core’s own generatetoaddress and generateblock, and the help naming the -generate option that replaces its hidden generate (rpc_generate.py). Not MINE: a node can take a block a client built over submitblock, which MINE names, and build none itself. SCAN_UTXO_SET – search its own UTXO set for the outputs a list of descriptors matches, Core’s own scantxoutset (rpc_scantxoutset.py). Named for what it reads rather than for the RPC’s own spelling. PROXY – dial a peer through the SOCKS5 proxy -proxy names, on a TCP address or a unix: socket path, for every network, an onion one through -onion’s where that is given, sending a proxy that accepts username/password credentials of each connection’s own under -proxyrandomize; report each network’s proxy in getnetworkinfo; and refuse to start on a -proxy or -onion naming no usable proxy (feature_proxy.py, [ISS bitcoin-node-tests#47](https://github.com/btclib-org/bitcoin-node-tests/issues/47)). One member for the three options, as DATACARRIER is for its pair: -onion names a SOCKS5 proxy as -proxy does, for onion alone, and -proxyrandomize qualifies whichever of the two is given. PROXY_PER_NETWORK – take -proxy’s =<network> suffix, setting the proxy of that network alone or, given 0, removing it, and refuse to start on a suffix that is empty or names an unknown network (feature_proxy.py). Not PROXY’s own: a node can take -proxy and read the suffix as part of the port. CJDNS – take a CJDNS address, one in fc00::/8, as CJDNS once -cjdnsreachable says the network is reachable, dial it through the proxy -proxy names, and report CJDNS reachable in getnetworkinfo (feature_proxy.py). I2P_SAM – recognise -i2psam, the I2P router’s SAM endpoint, and -i2pacceptincoming, report that endpoint as I2P’s proxy in getnetworkinfo with I2P reachable, and refuse to start on one naming no usable address (feature_proxy.py); dial an I2P address through that endpoint on port 0 alone, refusing any other port (p2p_i2p_ports.py), over a SAM session whose key it keeps on disk where -i2pacceptincoming asks it to accept I2P connections, and over one with a key of its own, never saved, where it does not (p2p_i2p_sessions.py). Named for the SAM bridge rather than for I2P: no ported test has an I2P router answer at the endpoint. ONLYNET – recognise -onlynet, Core’s own restriction of outbound connections to the networks it names, refusing to start on a network it does not know or on one it has no way to reach (feature_proxy.py). NODE_WALLET – hold wallets of its own and serve Core’s wallet RPCs over them: createwallet at the node’s own endpoint, and every method a wallet answers – getnewaddress, signmessage, importdescriptors, listtransactions among them – at /wallet/<name>, the endpoint bitcoin_core_rpc’s own for_wallet addresses. The subject of Core’s own wallet_*.py, which drive bitcoind’s built-in wallet ([ISS bitcoin-node-tests#45](https://github.com/btclib-org/bitcoin-node-tests/issues/45)). Not MINE: that names a block the node accepts as its tip, which a node without a wallet still offers over submitblock, where this names the wallet itself. A wallet a separate program keeps for a node is not this member either ([ISS bitcoin-node-tests#199](https://github.com/btclib-org/bitcoin-node-tests/issues/199)). TX_RECONCILIATION – offer and accept BIP330’s transaction reconciliation once -txreconciliation asks for it, Core’s own switch: sendtxrcncl sent to a peer that relays transactions, and a peer’s own sendtxrcncl registered (p2p_sendtxrcncl.py). PEER_BLOOM_FILTERS – recognise -peerbloomfilters, Core’s own switch to serving BIP37 bloom filters, which offers NODE_BLOOM in the node’s own version (p2p_sendtxrcncl.py). REINDEX – rebuild its block index and its chainstate from the block files it already stored, on a start given Core’s own -reindex, and its chainstate alone on one given -reindex-chainstate (feature_reindex.py, feature_reindex_readonly.py). One member for the pair, as DATACARRIER is: feature_reindex.py alternates them on one node. CAPTURE_MESSAGES – write every p2p message it sends a peer and every one it processes from that peer to files of that peer’s own, under the chain directory’s message_capture/, once Core’s own debug-only -capturemessages asks for it (p2p_message_capture.py). BLOCKS_XOR – recognise -blocksxor, Core’s own switch for whether the block and undo files are obfuscated with the key blocks/xor.dat holds, and refuse a start disabling it where the stored key is not all zeros (feature_blocksxor.py). ORPHANAGE – keep a transaction a peer sent whose inputs it cannot find yet, admit it to the mempool once its parent arrives, with that parent where the parent pays too little alone and the two pay enough together, Core’s 1p1c (p2p_opportunistic_1p1c.py, p2p_1p1c_network.py), and report what it keeps, with the peers that announced each, over Core’s own getorphantxs (rpc_orphans.py). Named for what it keeps rather than for the RPC’s own spelling, as SCAN_UTXO_SET is. BLOCK_PROPOSAL – check a block a client proposes on top of its own tip without storing it or asking for its proof-of-work, and answer null where it is valid or BIP22’s reason for the first rule it breaks: Core’s own getblocktemplate in BIP23’s proposal mode (mining_template_verification.py). Not MINE: a node can take a client’s block over submitblock and check none it is not asked to store. DNS_SEED – ask its chain’s DNS seeds for peer addresses once it starts, the way Core’s own -dnsseed does: by default, but not where -connect names its peers unless -dnsseed asks for it; at once under -forcednsseed, which it refuses beside -dnsseed off; and otherwise, with addresses already known, only after a wait, longer where it knows many, and not at all where enough outbound full-relay peers connect during it, block-relay-only ones not counting (p2p_dns_seeds.py). ADDRESS_FETCH – dial the addresses it keeps on its own, and ask a node a caller names for more, the way Core’s own -seednode does: at once where it keeps no address, and otherwise only once a wait passes without enough outbound full-relay peers (p2p_seednode.py). KNOWN_ADDRESSES – take a peer’s address a caller hands it into the addresses it keeps for finding peers, and list those back: Core’s own test-only addpeeraddress and its getnodeaddresses (p2p_dns_seeds.py, p2p_addr_selfannouncement.py, p2p_seednode.py). One member for the table’s writer and its reader, as BAN is for its ban list’s. EXTERNAL_IP – recognise -externalip, Core’s own option naming an address the node advertises to its peers as its own (p2p_addr_selfannouncement.py). PRIVATE_BROADCAST – send a transaction submitted over sendrawtransaction to peers over short-lived connections of its own through Tor or I2P, without putting it in its mempool first, until a peer sends it back; list what it is sending, and stop sending one on request; and send one again once it goes stale: Core’s own -privatebroadcast, getprivatebroadcastinfo and abortprivatebroadcast, with the mockscheduler that moves a stale transaction’s resend forward (p2p_private_broadcast.py). STARTUP_NOTIFY – run a shell command a caller names once it has started, the way Core’s own -startupnotify does (feature_startupnotify.py). DUMP_UTXO_SET – write its own UTXO set to a file a caller names, at its tip or rolled back to an earlier block of its own chain, Core’s own dumptxoutset (rpc_dumptxoutset.py). Named for what it writes rather than for the RPC’s own spelling, as SCAN_UTXO_SET is. LOAD_BLOCK – import, on starting, the blocks a file a caller names holds, each record the network’s message start, the block’s length little-endian and the block, the way Core’s own -loadblock does (feature_loadblock.py). LISTEN_ADDRESS – listen for peers where a caller’s own options say rather than where its adapter binds it, the way Core’s own -port and -bind do: on every address at the port the last -port names, and on 127.0.0.1 at the port after it for Tor’s inbound connections; only on the addresses -bind names where one is given, a bind naming no port taking -port’s, or the port after it where the bind is an onion one; and refusing to start on a -port outside 1 to 65535 (feature_port.py). MAX_TIP_AGE – recognise -maxtipage, Core’s own bound, in seconds, on how old its tip may be, by its own clock, for the node to leave initial block download (feature_maxtipage.py). PEER_BLOCK_FILTERS – serve BIP157’s compact block filters to peers once -peerblockfilters asks for it, Core’s own switch, and not otherwise: signal NODE_COMPACT_FILTERS and answer getcfilters, getcfheaders and getcfcheckpt where it is on, and drop a peer asking for any of them where it is off (p2p_blockfilters.py). Beside BLOCK_FILTER_INDEX, which keeps the filters this serves: a node can keep them for its own RPC and serve none. RPC_INFO – answer getrpcinfo, Core’s own RPC listing the calls it is running, each with how long it has run, and the full path of its debug log (interface_rpc.py). Named for the RPC, as BLOCK_STATS is. MIN_RELAY_TX_FEE – recognise -minrelaytxfee, Core’s own rate, in BTC/kvB, under which a fee counts as zero for relay, and the floor of every feefilter the node sends a peer (p2p_ibd_txrelay.py). CLUSTER_LINEARIZATION – keep its mempool grouped into clusters, the transactions its spends connect, each ordered into chunks of falling fee rate, and report them: Core’s own getmempoolcluster, its hidden getmempoolfeeratediagram and getmempoolinfo’s own optimal (mempool_cluster.py). Named for what it keeps rather than for an RPC’s own spelling, as ORPHANAGE is. MINIMUM_CHAIN_WORK – recognise -minimumchainwork, Core’s own debug-only floor, in hex, on chain work: a node whose tip is below it stays in initial block download and answers getheaders empty to a peer not holding the download permission, which noban implies, it downloads no block from a peer whose best known block is below it, and a value that is not hex, or that runs past 64 hex digits once an optional 0x is dropped, refuses the start (feature_minchainwork.py); it stores no header of a block a peer sends unasked on a chain below it (p2p_unrequested_blocks.py). MEMPOOL_GRAPH – walk the spends connecting its mempool’s transactions and report them: every mempool transaction a given one descends from and every one descending from it, bare or with each one’s own entry, Core’s own getmempoolancestors and getmempooldescendants, and the mempool transaction spending a given output, its gettxspendingprevout with no index behind it (mempool_packages.py). Named for what it walks rather than for an RPC’s own spelling, as SCAN_UTXO_SET is. ALERT_NOTIFY – run a shell command a caller names whenever it raises an alert, the alert’s message in place of the command’s %s, the way Core’s own -alertnotify does (feature_versionbits_warning.py). PACKAGE_ACCEPTANCE – evaluate a package a client hands it, a child with its unconfirmed parents, as one against its mempool and take it in: a parent paying too little alone entering with the child paying for it, and a package replacing what its parent conflicts with where it pays for that under package RBF’s own rules, each refusal answered with Core’s own reason. Core’s own submitpackage, and its testmempoolaccept handed a package it evaluates as one, answering a package-error for the whole (mempool_package_rbf.py). A package a peer relays, Core’s 1p1c, is not this capability’s. Named for what it takes rather than for an RPC’s own spelling, as ORPHANAGE is. BLOCK_NOTIFY – run a shell command a caller names whenever its tip changes outside initial block download, the new tip’s hash in place of the command’s %s, the way Core’s own -blocknotify does (feature_notifications.py). SHUTDOWN_NOTIFY – run a shell command a caller names once it begins shutting down, the way Core’s own -shutdownnotify does (feature_notifications.py). SETTINGS_FILE – keep a settings file in its chain’s data directory the way Core’s own settings.json is kept: written at start with a _warning_ key, each value logged at the next start and left in the file at shutdown, a file that is not valid JSON, not a JSON object or that holds a key twice refusing the start, -nosettings on the command line or in bitcoin.conf turning it off, and -settings=<path> naming another file (feature_settings.py). PRECIOUS_BLOCK – take a block a caller names as if it had been received before every other block of the same work, moving its tip to that block where the two tie, until a later call names another or the node restarts: Core’s own preciousblock (rpc_preciousblock.py). Named for the RPC, as BLOCK_STATS is. SIGN_MESSAGE_WITH_PRIVKEY – sign a message with a private key the caller hands it, and verify a message’s signature under a P2PKH address: Core’s own signmessagewithprivkey and verifymessage, which need no wallet (rpc_signmessagewithprivkey.py, wallet_signmessagewithaddress.py). Named for the first RPC, as BLOCK_STATS is, and one member for the pair, as SIGN_RAW_TRANSACTION is. ESTIMATE_SMART_FEE – answer a fee rate estimate for a confirmation target, and refuse a malformed request for one with Core’s own codes and messages: Core’s own estimatesmartfee and estimaterawfee (rpc_estimatefee.py). Named for the first RPC, as BLOCK_STATS is, and one member for the pair, as SIGN_RAW_TRANSACTION is. CHAIN_TIPS – report every tip of the blocks and headers it knows, each with its height, how far it branches off the active chain and a status, active, valid-fork, headers-only or invalid among them: Core’s own getchaintips (rpc_getchaintips.py). Named for the RPC, as BLOCK_STATS is. ASSUME_VALID – recognise -assumevalid=<hash>, Core’s own hex hash of a block externally verified valid. A block skips script verification where every one of these holds: the hash is not all zeros, it names a block the node already has a header for, the candidate is on the chain leading to that block, the candidate is also on the chain leading to the node’s own best header, the best header’s chain work is at least -minimumchainwork, and the candidate is buried under more than two weeks’ worth of work of the best header; a candidate failing any of those is verified in full (feature_assumevalid.py). INCREMENTAL_RELAY_FEE – recognise -incrementalrelayfee, Core’s own rate, in BTC/kvB, that a replacement must add to the fee of what it replaces, per virtual byte of its own size (feature_rbf.py). Where -minrelaytxfee is not given, Core raises the minimum relay fee rate to it. PEER_PERMISSIONS – grant the permissions Core’s own -whitelist and -whitebind name to the peers at an address or on a bind, and list them under permissions in getpeerinfo: the defaults of a bare address, the flags a bare permission list replaces them with, all, the merge of a whitelisted address with a whitebind’s own flags, the in and out directions, and the start refused on a malformed list (p2p_permissions.py).
- exception bitcoin_node_tests.capability.MissingCapabilityError[source]¶
Bases:
ExceptionRaised by require when the node under test does not declare it.
Not pytest.skip.Exception: this module’s own docstring has why.
- class bitcoin_node_tests.capability.SkipCounts[source]¶
Bases:
objectOne session’s own tally of skips, one count per capability asked for.
Session-scoped in tests/integration/conftest.py, so that one run against however many nodes prints one total per capability rather than one line per test – the number rule 4 asks for is a count the node’s tracker can read, not a running commentary.
- add_mapping(mapping: Mapping[str, int]) None[source]¶
Add counts from another tally’s own as_mapping into this one.
- Parameters:
mapping – a {capability.value: count} mapping, as as_mapping returns.
- as_mapping() dict[str, int][source]¶
Return this tally as a plain {capability.value: count} mapping.
tests/integration/conftest.py is what this is for: an xdist worker’s own tally has to cross to the controller through workeroutput, and the channel that carries it serializes plain data, never an Enum member – add_mapping is this method’s own inverse, on the controller’s own tally.
- record(capability: Capability) None[source]¶
Count one more skip against capability.
- bitcoin_node_tests.capability.require(capability: Capability, capabilities: Set[Capability], counts: SkipCounts) None[source]¶
Raise MissingCapabilityError unless capability is in capabilities.
- Parameters:
capability – what the test about to run needs.
capabilities – what the node under test declares, an adapter’s own capabilities.
counts – the session’s own tally, credited before the raise so that a node lacking a capability still has that asked for it counted – rule 4’s “never a silent pass” reaching the count itself, not only the individual test.
- Raises:
MissingCapabilityError – where capability is missing.
bitcoin_node_tests.debug_log module¶
assert_debug_log: wait for a node’s own log to carry a fact.
Core’s own TestNode.assert_debug_log (test/functional/test_framework/test_node.py): a context manager recording the log file’s own size at entry, and waiting up to timeout after the block exits for every expected substring to have appeared in whatever was appended since, failing at once on any read that finds an unexpected one. Capability.DEBUG_LOG (capability.py) is what gates a caller reaching this at all – only BitcoindAdapter (bitcoind.py) names a debug_log_path today, so a test importing this module is one whose own require call has already run, rather than this function gating the same capability a second time.
- bitcoin_node_tests.debug_log.assert_debug_log(log_path: Path, expected_substrings: Sequence[str], unexpected_substrings: Sequence[str] = (), *, timeout: float = 10.0) Iterator[None][source]¶
Yield, then wait for every one of expected_substrings to appear.
- Parameters:
log_path – the node’s own debug log, BitcoindAdapter.debug_log_path.
expected_substrings – every substring the block’s own action is expected to have caused; checked against what the log gained since this context was entered, not against the whole file, so an earlier, unrelated line carrying the same words does not pass this.
unexpected_substrings – substrings the block’s own action must not have caused, Core’s own unexpected_msgs: checked on each read ahead of expected_substrings, against the same appended text, so a line appended after the wait’s last read is not seen, and with expected_substrings empty there is one read.
timeout – how long to keep polling after the block exits, before –timeout-factor’s own scaling (timeout_factor.scaled). The log is read once however short it is, so 0 is Core’s own default: one read, as soon as the block exits.
- Raises:
AssertionError – some substring of unexpected_substrings appeared, or some of expected_substrings never appeared within timeout.
bitcoin_node_tests.mempool_util module¶
fill_mempool: push a node’s own mempool past its size-based eviction.
Core’s own fill_mempool (test/functional/test_framework/mempool_util.py): a throwaway MiniWallet sends a run of disposable self-transfers at a steadily rising fee rate, so that a node started with a small enough -maxmempool runs out of room and starts evicting its own lowest-feerate transaction – the precondition several of Core’s own fee-bumping and eviction tests build before their own scenario, rather than a mechanism this module’s own callers ask questions about.
A smaller claim than Core’s own function: Core’s tx_sync_fun (or, absent one, test_framework.sync_mempools()) is dropped – every RPC this function makes targets the one node it was given, so nothing needs propagating to a second one first, and Core’s own two callers confirm this rather than needing the parameter, mempool_package_rbf.py passing an explicit no-op and rpc_packages.py running a single-node test where sync_mempools already has nothing to do. tx_batch_size, always 1 at both call sites, is dropped for the same reason: a generality neither caller exercises. The large low-priority output each transaction carries is create_self_transfer’s own target_vsize padding rather than Core’s own literal gen_return_txouts payload, a different number of padding bytes reaching the identical role – a transaction whose own fee rate, not its absolute fee, decides where a size-based mempool ranks it.
TF2_INTEGRATION=1 uv run pytest tests/integration
- bitcoin_node_tests.mempool_util.fill_mempool(node: NodeAdapter) None[source]¶
Fill node’s own mempool with disposable transactions until eviction.
A throwaway MiniWallet mines its own coins, then sends one unpadded, minimum-fee-rate self-transfer – the one this function expects back out of the mempool by the time it returns – followed by _NUM_TXS padded self-transfers, each at _TARGET_VSIZE and at a fee a further multiple of base_fee (twice the node’s own relayfee over _TARGET_VSIZE) than the one before it. The mempool is read once after the first _NUM_TXS - _HIGH_FEE_TXS of them, confirming eviction has not started early, and once more after the rest, confirming it has: mempoolminfee above minrelaytxfee, and the first, minimum-fee-rate transaction gone.
- Parameters:
node – the node whose own mempool this fills, started with a small enough -maxmempool (Core’s own docstring names 5, megabytes) that _NUM_TXS transactions at _TARGET_VSIZE each exhaust it.
- Raises:
AssertionError – eviction started before the last _HIGH_FEE_TXS transactions were sent, or never started at all – mempoolminfee still at minrelaytxfee afterwards, the low fee-rate transaction still in the mempool, or the mempool holding as many transactions as this function sent.
LookupError – forwarded from MiniWallet.get_utxo – the wallet did not mine _NUM_TXS + 1 coins to spend.
TypeError – forwarded from MiniWallet.generate or MiniWallet.send_self_transfer – the node’s RPC answered something one of those calls cannot use.
ValueError – forwarded from MiniWallet.send_self_transfer.
bitcoin_node_tests.mini_wallet module¶
MiniWallet: coins without a wallet, cached from blocks it mines itself.
[ISS 4](https://github.com/btclib-org/bitcoin-node-tests/issues/4), step 5 of [ISS btclib-org/btclib#2220](https://github.com/btclib-org/btclib/issues/2220): Core’s own MiniWallet (test/functional/test_framework/wallet.py) feeds its own cache by calling generatetodescriptor and then scantxoutset – mining through the node’s descriptor-wallet machinery and asking the node to find its own coins back. This one needs neither RPC: generate builds the coinbase itself (btclib.block.build.build_coinbase), mines it (btclib.block.mining.mine) and delivers it over submitblock, so the txid and the vout its own coin sits at are known before the block is ever submitted rather than scanned back out of the node afterwards.
The scriptPubKey every coin of this class pays is Core’s own default MiniWalletMode.ADDRESS_OP_TRUE (wallet.py’s own docstring table): a P2TR output whose internal key is the x-only integer 1 and whose single tapscript leaf, at leaf version 0xc0, is bare OP_TRUE – the same pair test_framework/address.py’s own create_deterministic_address_bcrt1_p2tr_op_true and test_framework/script.py’s own taproot_construct build. btclib.script.taproot.output_pubkey and input_script_sig build the same output key and the same control block from that pair, byte for byte: the address this module’s own scriptPubKey serializes to under regtest is Core’s own published constant, bcrt1p9yfmy5h72durp7zrhlw9lf7jpwjgvwdg0jr0lqmmjtgg83266lqsekaqka, and the spend it proves is standard under bitcoind’s default mempool policy, needing neither a scriptSig long enough to clear a minimum size nor a policy flag to admit a scriptPubKey no standard template matches. OP_TRUE alone is still the smaller claim than a real signature (RAW_P2PK, wallet.py’s third mode): it proves the mechanism this issue is about – a cache fed from mined blocks – without also proving btclib’s own signing surface.
RAW_P2PK_SCRIPT_PUB_KEY and raw_p2pk_script_sig, alongside the class, are that third mode’s output and its signature, for a caller whose subject needs a coin spent under a real signature ([ISS 167](https://github.com/btclib-org/bitcoin-node-tests/issues/167)): the class never pays one, and the caller pays it – a coinbase it builds, typically – and spends it itself.
Capability.MINE is what a caller checks before constructing one, the same capability the first family already skips on for a node not mining: not a new one, since this class produces the fact MINE already names – “a block the node accepts as its own new tip, however it gets there” – by client-side construction over submitblock rather than a node’s own wallet, exactly the second half node.py’s own docstring already draws. BtclibNodeAdapter declares it only on a build that connects a submitted block with no peer, and its own mine is this class’s generate (btclib_node.py’s own docstring has both).
[ISS 4](https://github.com/btclib-org/bitcoin-node-tests/issues/4)’s own remaining ports add several things beyond the mechanism above. Two are read off Core’s own wallet.py rather than invented: get_utxo finds a specific cached coin by its own txid, with no maturity filter – a caller naming one by hand is presumed to know what it is asking for, mempool_spend_coinbase.py’s own subject being what a node does with an immature one; create_self_transfer and send_self_transfer both take an optional utxo_to_spend, spending the named coin in place of the one get_utxo would pick. The other two have no counterpart there. resync re-reads this wallet’s own tip, height and median-time the way __init__ does, for a chain an invalidateblock or a build_fork submission moved without this wallet’s own generate doing it. rescan_utxos (wallet.py) is the shape not taken: it also rebuilds the coin cache from scantxoutset, which this module’s own docstring already has why nothing here ever calls; resync leaves the cache’s membership for the caller to reconcile against whatever the RPC that exposed the reorg already told it.
A cached coin’s own height is the fact confirmed_only filters on, get_utxo’s and get_utxos’ alike (wallet.py): the block that holds it, or 0 for a coin no block holds as far as this wallet knows. wallet.py keeps a separate confirmations beside a height that is 0 exactly when that count is, and filters on the count being positive; Utxo.confirmed is that same test read off the height. generate sets it for the cached coins a transaction its own confirm carries pays. A block mined anywhere else – by another node, or by generatetoaddress on this one, each filling it from a mempool – confirms a coin this wallet cannot see from here, so resync also asks the node: gettxout with include_mempool false, for every cached coin that is not a coinbase, rescan_utxos’ own role in the one respect a confirmed_only caller needs. gettxout rather than getrawtransaction: it answers from the chainstate’s own UTXO set, where getrawtransaction finds a transaction outside the mempool only with -txindex or its block’s hash, neither of which this wallet has.
generate’s own confirm is the second: no Core file has it, because Core’s own node-side mining (generatetodescriptor) pulls the whole mempool into whatever it mines, and this class’s mining reads no mempool back to do the same (this module’s own docstring already has why). A caller that broadcast a transaction and wants it mined names it here, already knowing it – send_self_transfer’s own return value, typically – rather than this class fetching getrawmempool and getrawtransaction back to rediscover what it already handed the node.
[ISS 247](https://github.com/btclib-org/bitcoin-node-tests/issues/247): a confirm transaction this wallet never sent – built by a create_* method and broadcast some other way, over submitpackage, p2p relay or a raw sendrawtransaction – is scanned into the cache once its block is accepted, where Core’s own generate (wallet.py) finds the same coins by ending in rescan_utxos. The scan is _scan_tx’s, in block order: a cached coin the transaction spends is dropped, and every output paying this wallet is cached at that block’s height. A transaction is scanned once, by _send or by generate, so a coin a caller already took from one is not cached again by a later confirm naming it; one already scanned still drops the cached coins it spends, which an unsent parent carried in the same block may just have cached. rescan_utxos rebuilds the whole cache from scantxoutset instead, an RPC BtclibNodeAdapter does not serve (it never declares Capability.SCAN_UTXO_SET), and one that would also cache every coin of this script that any other MiniWallet on the same node holds. Its mempool pass is kept in the one respect that decides a scanned coin: a coin the scan adds is left out where a transaction in the node’s mempool already spends it – a spend of one of new_utxos’ own coins, broadcast before its parent was mined – read off getrawmempool and getrawtransaction, only when the scan added a coin. What rescan_utxos caches and this does not: a coin a block mined elsewhere pays, a coin only a mempool transaction pays, and a coin a caller took from the cache and never spent.
build_fork, alongside the class, is create_empty_fork (test/functional/test_framework/blocktools.py): unsubmitted blocks extending whatever tip the node it is given actually has, for a caller that wants to hold them back and submit them later, mempool_resurrect.py’s own subject. It shares no wallet state – a fork is disposable by construction, spent by nobody – so it reads the node fresh rather than a MiniWallet instance’s own cache, and pays whichever script_pub_key its caller names, that caller’s own wallet’s script_pub_key ordinarily.
build_next_block is one block rather than a fork: solved, unsubmitted, on the node’s own current tip, carrying the transactions its caller names beside a coinbase paying the script_pub_key it names, at a header version and a time it may choose – Core’s own create_block (blocktools.py), whose coinbase is create_coinbase’s. Submitting it is left to the caller, which reads submitblock’s own answer, a refusal as much as an acceptance, or submits it inside assert_debug_log to read bitcoind’s own log line instead.
[ISS 14](https://github.com/btclib-org/bitcoin-node-tests/issues/14)’s own mempool_package_limits.py and mempool_updatefromblock.py need a coin cache several transactions deep rather than one spend at a time: create_self_transfer_multi spends one or several cached coins into several outputs at once, Core’s own create_self_transfer_multi (wallet.py), and create_self_transfer_chain spends the output each call just created into the next, Core’s own create_self_transfer_chain. Both, and create_self_transfer alongside them, take an optional target_vsize: an OP_RETURN output of literal OP_1 opcodes (no PUSHDATA of its own to size around) pads the transaction to exactly that many virtual bytes, _pad_to_vsize mirroring Core’s own bulk_vout (test_framework/script_util.py) byte for byte. get_utxo gains a vout beside txid for the same reason Core’s own carries one: a multi-output send caches more than one coin under the same txid, and a caller spending them in an order its own loop chooses rather than the order they were cached needs the pair rather than the first match alone.
[ISS 103](https://github.com/btclib-org/bitcoin-node-tests/issues/103): an RBF or TRUC test’s subject is the fee, the sequence or the version, so the self-transfers take what Core’s own take – fee_rate, fee, version, locktime and sequence on create_self_transfer, the last three on create_self_transfer_multi – with Core’s own defaults and fee arithmetic. Where Core’s own take BTC Decimal`s these take satoshis, and satoshis per 1000 virtual bytes for a rate: the unit `fee_per_output already has in Core, and create_self_transfer’s own docstring has what else differs.
[ISS 242](https://github.com/btclib-org/bitcoin-node-tests/issues/242): the cache learns of a transaction when this class sends it, as Core’s own scan_tx runs in its sendrawtransaction (wallet.py), or when generate confirms it (ISS 247, above). A create_* method adds nothing to the cache, get_utxo taking out the coin it picks for one, so a transaction that is refused, or is edited after it is built, leaves no coin behind for the next spend to pick, and neither does one that is never sent until a confirm names it. new_utxos is Core’s own new_utxos, the coins a transaction pays this wallet, less the padding output target_vsize adds: a caller spending one before sending it, or broadcasting it some other way, names that coin by hand until a block confirms it.
- class bitcoin_node_tests.mini_wallet.MiniWallet(node: NodeAdapter)[source]¶
Bases:
objectCoins without a wallet: a UTXO cache fed from blocks this class mines.
This module’s own docstring is the design; Capability.MINE (capability.py) is what a caller checks before constructing one.
- Parameters:
node – the node this wallet mines into and spends against, its rpc the whole of how this class ever reaches it – no in-process import of anything the node itself runs.
- create_self_transfer(*, fee_rate: int = 300000, fee: int = 0, utxo_to_spend: Utxo | None = None, target_vsize: int = 0, confirmed_only: bool = False, version: int = 2, locktime: int = 0, sequence: int | Sequence[int] = 0) Tx[source]¶
Return an unbroadcast tx spending one coin, paid to itself.
create_self_transfer (wallet.py), its fee arithmetic included: fee where nonzero, otherwise fee_rate over the tx’s own vsize, or over target_vsize where that is nonzero, rounded up to the next satoshi – the result wallet.py reaches both through get_fee and through truncating the output’s own value. Core’s own method takes BTC Decimal`s; this one takes satoshis and satoshis per 1000 virtual bytes, the unit `CFeeRate’s own integer constructor takes, so a Core rate of at most eight decimals converts exactly. Core prices the fee at a fixed 104 virtual bytes for ADDRESS_OP_TRUE and asserts the tx has them; this one reads the vsize off the tx it built, the same 104 for the one coin shape this class spends. send_self_transfer is the caller wanting it broadcast too, and cached; new_utxos is the coin it creates.
- Parameters:
fee_rate – satoshis per 1000 virtual bytes, used where fee is 0; DEFAULT_FEE_RATE, Core’s own default, where not given.
fee – the absolute fee in satoshis; 0 defers to fee_rate.
utxo_to_spend – the coin to spend, get_utxo’s own answer typically; get_utxo()’s own largest matured coin where None, its maturity check applying only then – a caller naming one by hand, immature or not, gets exactly that one, create_self_transfer (wallet.py)’s own shape.
target_vsize – where nonzero, an OP_RETURN output padding the tx to exactly this many virtual bytes – _pad_to_vsize, beyond the single spendable output this method still returns exactly one of – and the size fee_rate is priced at.
confirmed_only – forwarded to get_utxo where utxo_to_spend is None.
version – the tx’s own version; 3 makes it TRUC (BIP431).
locktime – the tx’s own nLockTime.
sequence – the input’s own nSequence, or a one-element sequence of it, as Core’s own forwarding to create_self_transfer_multi takes either.
- Raises:
LookupError – utxo_to_spend is None and no coin of this wallet has matured yet (or is confirmed, with confirmed_only).
ValueError – fee_rate or fee is negative, refused before any coin is taken; the fee leaves the coin nothing to send, Core’s own RuntimeError; sequence is a sequence of other than one element; or target_vsize is smaller than this tx’s own vsize before the padding output is even added.
- create_self_transfer_chain(*, chain_length: int, utxo_to_spend: Utxo | None = None) list[Tx][source]¶
Return chain_length unbroadcast txs, each spending the last.
create_self_transfer_chain (wallet.py): the first spends utxo_to_spend, or get_utxo()’s own largest matured coin where None; each of the rest spends the single output the one before it created, new_utxos’ own answer, as Core’s spends new_utxo. send_self_transfer_chain is the caller wanting every one of them broadcast too, and cached.
- Parameters:
chain_length – how many transactions the chain carries.
utxo_to_spend – the coin the first transaction spends; get_utxo()’s own largest matured coin where None.
- Raises:
LookupError – utxo_to_spend is None and no coin of this wallet has matured yet.
- create_self_transfer_multi(*, utxos_to_spend: Sequence[Utxo] | None = None, num_outputs: int = 1, version: int = 2, locktime: int = 0, sequence: int | Sequence[int] = 0, fee_per_output: int = 1000, target_vsize: int = 0, confirmed_only: bool = False) Tx[source]¶
Return an unbroadcast tx spending several coins into several.
create_self_transfer_multi (wallet.py): every input the coins utxos_to_spend names, every output the same size, fee_per_output satoshis short of an equal share of the inputs’ own total, Core’s own unit for this one parameter. It takes no fee_rate, as Core’s does not. send_self_transfer_multi is the caller wanting it broadcast too, and cached; new_utxos is the coins it creates. A single new coin wants create_self_transfer instead, num_outputs fixed at one there rather than a parameter of it.
- Parameters:
utxos_to_spend – the coins to spend; get_utxo()’s own largest matured coin alone where None.
num_outputs – how many equal-sized outputs to create.
version – the tx’s own version; 3 makes it TRUC (BIP431).
locktime – the tx’s own nLockTime.
sequence – every input’s own nSequence, or one per input, in the order of utxos_to_spend.
fee_per_output – satoshis short of an equal share of the inputs’ own total value, per output.
target_vsize – where nonzero, an OP_RETURN output padding the tx to exactly this many virtual bytes, beyond the num_outputs spendable ones this method still creates.
confirmed_only – forwarded to get_utxo where utxos_to_spend is None.
- Raises:
LookupError – utxos_to_spend is None and no coin of this wallet has matured yet (or is confirmed, with confirmed_only).
ValueError – the inputs’ own total, less fee_per_output times num_outputs, does not divide into num_outputs positive shares; sequence is a sequence whose length is not the number of coins spent; or target_vsize is smaller than this tx’s own vsize before the padding output is even added.
- generate(count: int, *, confirm: Sequence[Tx] = ()) list[bytes][source]¶
Mine count blocks paying this wallet’s own script, and cache them.
Client-side start to finish: build_coinbase and build_block (btclib.block.build) build the block, mine (btclib.block.mining) solves its nonce, and submitblock delivers it – this module’s own docstring has why nothing here ever calls scantxoutset. The block’s own time is strictly later than the last one this call or a previous one produced, and than the chain’s own median-time-past at construction: bitcoind refuses time-too-old for a block whose own time does not exceed its chain’s own median-time-past, which two blocks minted in the same wall-clock second otherwise both carry, and which a second wallet built on top of a chain it did not itself mine otherwise carries from its very first block.
- Parameters:
count – how many blocks to mine.
confirm – already-broadcast transactions to carry in the first of the count blocks – send_self_transfer’s own return value, typically. Bitcoind’s own node-side mining pulls the whole mempool in on every block; this class reads no mempool back to fill one, so a caller names exactly what it wants confirmed, in an order where a spend of one of them already sits after it. One this wallet has not scanned yet – never sent through it – is scanned once the block is accepted, as _scan_tx scans a send, except that a coin the scan adds is left out where a transaction in the node’s mempool already spends it: this module’s own docstring has the choice and its limits. Every cached coin one of them pays takes that block’s own height, which is what confirmed_only reads.
- Returns:
the mined blocks’ own header hashes, display order, oldest first.
- Raises:
RuntimeError – mine exhausted its own search bound without solving one – regtest’s own target is wide enough that this is not expected to happen.
TypeError – submitblock answered anything but acceptance (None), or getrawmempool or getrawtransaction something _mempool_spends cannot use.
- get_utxo(*, txid: str | None = None, vout: int | None = None, mark_as_spent: bool = True, confirmed_only: bool = False) Utxo[source]¶
Return a cached coin, forgetting it unless mark_as_spent is false.
get_utxo (wallet.py), in its order: the cache is first sorted in place by value, then by descending height, so the largest coin sits last and, among equal values, the lowest height after the higher. Without txid the answer is the largest matured coin, the lowest height of equal ones – a coin no block holds, at 0, ahead of a confirmed one. With it, the answer is the first coin in that order txid paid, maturity aside: a caller naming a coin by its own txid is presumed to already know what it is, mempool_spend_coinbase.py’s own subject being what the node does when handed a spend of one that has not cleared COINBASE_MATURITY yet.
- Parameters:
txid – the hex txid of the coin’s own transaction; the largest matured coin where None.
vout – the coin’s own output index, where more than one cached coin shares txid – send_self_transfer_multi’s own several outputs – and a caller wants a specific one rather than whichever the order above puts first.
mark_as_spent – where false, the coin is returned and stays cached, for a caller that will spend it itself; sending the transaction that spends it drops it then.
confirmed_only – only a coin a block holds (Utxo.confirmed).
- Raises:
LookupError – no cached coin answers every filter given – wallet.py’s own next raising StopIteration instead.
- get_utxos(*, include_immature_coinbase: bool = False, mark_as_spent: bool = True, confirmed_only: bool = False) list[Utxo][source]¶
Return every cached coin the filters keep, in the cache’s order.
get_utxos (wallet.py): no sort of its own, so the order is whatever the last get_utxo sorted the cache into, followed by what was cached after it. mark_as_spent forgets the whole cache, not only the coins returned – an immature coinbase and a coin no block holds are dropped with the rest, as wallet.py’s own self._utxos = [] drops them.
- Parameters:
include_immature_coinbase – keep a coinbase that has not matured yet (_is_mature).
mark_as_spent – where true, empty the cache.
confirmed_only – only coins a block holds (Utxo.confirmed).
- new_utxos(tx: Tx) list[Utxo][source]¶
Return the coins tx pays this wallet, in output order, uncached.
new_utxos (wallet.py), which Core’s own create_* return beside the transaction, and whose first entry is its new_utxo. Core’s lists every output, a target_vsize padding one included; this one lists the outputs paying script_pub_key, the ones _witness() spends, which lead every transaction a create_* method builds. Each is at height 0, and the cache is neither read nor changed.
- Parameters:
tx – the transaction, sent or not.
- resync() None[source]¶
Re-read the tip from the node, and which cached coins a block holds.
For a chain move this wallet did not itself make – an invalidateblock, or a build_fork a caller has just submitted – so that the next generate extends the chain that is actually there instead of a tip this wallet last saw before it moved. No coin is added to the cache or dropped from it: this class has no scantxoutset to rebuild it from (this module’s own docstring has why), so a coin the move spent or unspent again is the caller’s own to reconcile, from whatever RPC told it the move happened.
What changes is each cached non-coinbase coin’s own height, as gettxout answers it with include_mempool false: the block holding the coin where the chainstate’s UTXO set has it, 0 where it does not – a coin only the mempool holds, or one a block has spent. A coinbase is left as it is, generate having cached it at the height of the block that created it.
- Raises:
TypeError – getbestblockhash, getblockcount, getblockchaininfo or gettxout answered something this call cannot use.
- property script_pub_key: ScriptPubKey¶
Return the anyone-can-spend script every coin of this wallet pays.
- send_self_transfer(*, fee_rate: int = 300000, fee: int = 0, utxo_to_spend: Utxo | None = None, target_vsize: int = 0, confirmed_only: bool = False, version: int = 2, locktime: int = 0, sequence: int | Sequence[int] = 0) Tx[source]¶
Create, broadcast and cache a self-transfer; return the sent tx.
Every parameter is forwarded to create_self_transfer, as Core’s own send_self_transfer (wallet.py) forwards its kwargs, and the broadcast passes maxfeerate 0, as Core’s own does, so no fee is refused for exceeding the node’s default ceiling. The new coin is cached once the node accepts the tx, and is spendable the moment this returns: unlike a coinbase, get_utxo never holds a non-coinbase coin back as immature.
- Parameters:
fee_rate – forwarded to create_self_transfer.
fee – forwarded to create_self_transfer.
utxo_to_spend – forwarded to create_self_transfer.
target_vsize – forwarded to create_self_transfer.
confirmed_only – forwarded to create_self_transfer.
version – forwarded to create_self_transfer.
locktime – forwarded to create_self_transfer.
sequence – forwarded to create_self_transfer.
- Raises:
LookupError – utxo_to_spend is None and no coin of this wallet has matured yet (or is confirmed, with confirmed_only).
ValueError – forwarded from create_self_transfer.
TypeError – sendrawtransaction answered something other than the sent tx’s own id.
- send_self_transfer_chain(*, chain_length: int, utxo_to_spend: Utxo | None = None) list[Tx][source]¶
Create, broadcast and cache a chain of self-transfers.
Each broadcast caches its own transaction’s output and drops the one before it, which that transaction spends, so the last transaction’s own output is the one the chain leaves cached.
- Parameters:
chain_length – forwarded to create_self_transfer_chain.
utxo_to_spend – forwarded to create_self_transfer_chain.
- Raises:
LookupError – utxo_to_spend is None and no coin of this wallet has matured yet.
TypeError – a sendrawtransaction answered something other than its own tx’s id.
- send_self_transfer_multi(*, utxos_to_spend: Sequence[Utxo] | None = None, num_outputs: int = 1, version: int = 2, locktime: int = 0, sequence: int | Sequence[int] = 0, fee_per_output: int = 1000, target_vsize: int = 0, confirmed_only: bool = False) Tx[source]¶
Create, broadcast and cache a multi-output self-transfer.
The broadcast passes maxfeerate 0, as send_self_transfer’s does.
- Parameters:
utxos_to_spend – forwarded to create_self_transfer_multi.
num_outputs – forwarded to create_self_transfer_multi.
version – forwarded to create_self_transfer_multi.
locktime – forwarded to create_self_transfer_multi.
sequence – forwarded to create_self_transfer_multi.
fee_per_output – forwarded to create_self_transfer_multi.
target_vsize – forwarded to create_self_transfer_multi.
confirmed_only – forwarded to create_self_transfer_multi.
- Raises:
LookupError – utxos_to_spend is None and no coin of this wallet has matured yet (or is confirmed, with confirmed_only).
ValueError – forwarded from create_self_transfer_multi.
TypeError – sendrawtransaction answered something other than the sent tx’s own id.
- send_to(script_pub_key: ScriptPubKey, value: int) Tx[source]¶
Spend one matured coin, broadcast, paying script_pub_key too.
send_to (wallet.py): a second output pays script_pub_key, and the first keeps FEE sat back for the fee and returns the rest to this wallet as change, so the spent coin’s own value beyond value and FEE is not simply burned as a fee the way it would be paying a single output alone. The coin spent is get_utxo()’s own, the largest matured one, and the tx around it is the one create_self_transfer(fee_rate=0) builds – version 2, nLockTime and nSequence 0 – as wallet.py’s own send_to takes both.
- Parameters:
script_pub_key – what the new, second output pays.
value – the new output’s own satoshi value.
- Raises:
LookupError – no coin of this wallet has matured yet.
ValueError – the spent coin cannot cover value and this class’s own FEE together.
TypeError – sendrawtransaction answered something other than the sent tx’s own id.
- class bitcoin_node_tests.mini_wallet.Utxo(outpoint: OutPoint, value: int, height: int, coinbase: bool)[source]¶
Bases:
objectOne coin MiniWallet knows about, spendable with _witness() alone.
height is the block holding the coin, 0 where no block does as far as the wallet knows – this module’s own docstring has how it learns.
- bitcoin_node_tests.mini_wallet.build_fork(node: NodeAdapter, script_pub_key: ScriptPubKey, length: int) list[Block][source]¶
Return length unsubmitted blocks extending node’s own current tip.
create_empty_fork (test/functional/test_framework/blocktools.py): each block pays script_pub_key and carries no other transaction, mined client-side the same way MiniWallet.generate mines one, and none of them is submitted – the caller does that, in order, once it wants to test what happens when this fork turns out to carry more work than whatever the node accepted in the meantime.
Independent of any MiniWallet instance’s own cache: a fork built this way is spent by nobody, so nothing here needs one.
- Parameters:
node – the node whose own current tip this fork extends.
script_pub_key – what every block of the fork pays.
length – how many blocks to build.
- Raises:
RuntimeError – _mine_one could not solve one of them.
TypeError – node’s own RPC answered something _read_tip cannot use.
- bitcoin_node_tests.mini_wallet.build_next_block(node: NodeAdapter, script_pub_key: ScriptPubKey, transactions: Sequence[Tx] = (), *, version: int = 536870912, extra_output_script: ScriptPubKey | None = None, time: int | None = None) Block[source]¶
Return one solved, unsubmitted block extending node’s own current tip.
create_block over create_coinbase (test/functional/test_framework/blocktools.py), mined client-side by _mine_one, as MiniWallet.generate mines one. The tip, the height and the median-time-past are read off node on every call, and the block’s own time is time where given, otherwise the wall clock or one second past that median-time-past, whichever is later. Submitting it is the caller’s, and so is reading submitblock’s own answer.
- Parameters:
node – the node whose own current tip this block extends.
script_pub_key – what the coinbase pays.
transactions – what the block carries after its coinbase, in the order given.
version – the block header’s own version; btclib.block.mining.VERSION where not given, where Core’s own create_block defaults to 4.
extra_output_script – where given, the coinbase carries a second, zero-valued output paying it, Core’s own create_coinbase parameter of the same name.
time – the block header’s own time, in seconds since the epoch, taken as given rather than checked against the median-time-past: Core’s own create_block ntime, for a caller whose subject is the time a block carries.
- Raises:
RuntimeError – mine exhausted its own search bound without solving it – regtest’s own target is wide enough that this is not expected to happen.
TypeError – node’s own RPC answered something _read_tip cannot use.
- bitcoin_node_tests.mini_wallet.nulldata_script_pub_key(data: bytes) ScriptPubKey[source]¶
Return an OP_RETURN scriptPubKey carrying data, of any length.
CScript([OP_RETURN, data]) (test_framework/script.py), not ScriptPubKey.nulldata: that classmethod’s own 80-byte refusal is the historical “standard” bound, which a -datacarriersize test asks a node about rather than a fact this library should enforce before the request ever leaves this process ([ISS bitcoin-node-tests#14](https://github.com/btclib-org/bitcoin-node-tests/issues/14)). check_validity=False: ScriptPubKey.assert_valid runs no length check of its own on a nulldata script, so this only ever skips the network-name check Script.__init__ would otherwise repeat.
- Parameters:
data – the payload to push after OP_RETURN; any length, including one ScriptPubKey.nulldata would refuse.
- bitcoin_node_tests.mini_wallet.raw_p2pk_script_sig(tx: Tx, vin_i: int) bytes[source]¶
Return the scriptSig spending a RAW_P2PK_SCRIPT_PUB_KEY coin.
sign_tx (wallet.py) in its RAW_P2PK mode, sign_input_legacy (test_framework/script_util.py): the one push of a SIGHASH_ALL ECDSA signature over btclib.script.sig_hash.legacy. That sighash blanks every input’s scriptSig, so tx may carry any when this is called, and the caller puts the answer in place.
- Parameters:
tx – the spending transaction.
vin_i – the index of the input spending the coin.
bitcoin_node_tests.node module¶
NodeAdapter: how a node starts, stops and restarts, and its RPC.
Step 3 of [ISS 2220](https://github.com/btclib-org/btclib/issues/2220): one base class over what bitcoind.py and btclib_node.py share – spawning a process, waiting for its RPC to answer, and tearing it down again – with each subclass supplying only what differs: the command line an option is spelled with, and how RPC authenticates (rule 1 of that issue names both as the adapter’s own).
Reached only over a process and an RPC socket, never in-process: this module imports no node, subprocess.Popen is the whole of how one starts, and bitcoin_core_rpc.BitcoinCoreRpcClient is the whole of how one answers.
- class bitcoin_node_tests.node.NodeAdapter(executable: str, datadir: Path, rpc_port: int, p2p_port: int, extra_args: Sequence[str] = (), rpc_auth: tuple[str, str] | None = None, *, trace_rpc: bool = False, chain: str = 'regtest')[source]¶
Bases:
ABCOne running node: how it starts, stops, restarts, and answers RPC.
A subclass names its own command line (_command), its own RPC client (_rpc_client) and its own capabilities; this class is the process and RPC plumbing every node needs regardless – rule 1’s “how it starts, stops and restarts” and “how RPC authenticates”, answered once here and specialised twice.
datadir, rpc_port and p2p_port are the caller’s to allocate – free_port above, and a fixture’s own tmp_path – rather than this class reaching for a default any other instance on the same machine could collide on.
extra_args is appended after _command’s own argv: a caller asking for a fact _command does not already name – a non-default -blocksdir, a -conf naming a file this same caller wrote – passes it here rather than a subclass growing a parameter for every option a test happens to need. An entry naming an option _command already sets is refused at construction, and in the replacement restart takes, rather than silently overriding it the way bitcoind’s own last-one-wins parsing would.
rpc_auth is the credential a subclass’s own _rpc_client builds its readiness and its ordinary RPC client from instead of its own default (a cookie, BitcoindAdapter’s and, where the build writes one, BtclibNodeAdapter’s): a node started with -rpcuser/ -rpcpassword or -norpccookiefile writes no cookie at all ([ISS bitcoin-node-tests#34](https://github.com/btclib-org/bitcoin-node-tests/issues/34)), so a client waiting on one never sees it and start times out rather than the node ever answering. The caller who put such a flag in extra_args already knows the plaintext credential it configured – Core’s own rpc_users.py builds its own -rpcauth lines the same way, rpcauth.py’s own hash of a password it also keeps – so it is the caller’s to pass here too, rather than something this class could derive from the command line after the fact: a -rpcauth value is a salted hash, and the plaintext behind it exists only where it was chosen.
trace_rpc is Core’s own –tracerpc, restated per adapter rather than as a global: _rpc_transport wraps this adapter’s transport in traced_transport where it is set, so whichever client a subclass’s own _rpc_client builds over it – rpc_auth’s credential one included – prints, rather than this base class building the client itself.
The RPC connections are this adapter’s, kept across calls and closed by stop (_ThreadSessions), so a restarted node is never asked over a connection its previous process accepted.
chain is the chain the node runs, in Core’s own -chain= vocabulary (main, test, testnet4, signet, regtest), regtest where a caller names none. A subclass’s own chains names the ones its node can start; any other is refused at construction. The chain decides the option _command spells it with, and the subdirectory of datadir a subclass reads its cookie and its log from; it is not an extra_args entry, _check_extra_args refusing every chain selector once _command names one.
- add_outbound_connection(address: tuple[str, int], connection_type: str) None[source]¶
Have this node dial address as an outbound connection_type.
Over addconnection, the RPC Core’s own TestNode.add_outbound_p2p_connection (test/functional/test_framework/test_node.py) makes the node dial a listening P2PInterface with; peer.Listener is what this suite listens with. Capability.TYPED_OUTBOUND (capability.py) is what a caller checks before calling this.
v2transport is passed False: Peer speaks the v1 wire alone, the reason connect_nodes above gives for its own default.
- Parameters:
address – (host, port) to dial, a Listener.address.
connection_type – outbound-full-relay, block-relay-only, addr-fetch, feeler or manual, the last where the build’s own help addconnection names it.
- restart(extra_args: Sequence[str] | None = None) None[source]¶
Stop and start again, over the same data directory.
The one of the six parts every adapter answers identically: the data directory is the caller’s, named once in __init__, so a restart resumes the same chain rather than a fresh one.
extra_args, where given, replaces the constructor’s own for this start alone, and a later start or restart without it goes back to the constructor’s: Core’s own restart_node(i, extra_args) (test_framework.py), whose TestNode.start falls back to the node’s own extra_args wherever none is passed. An empty sequence is a start with no extra argument at all.
- Parameters:
extra_args – what to append after _command’s own argv for this start, in place of the constructor’s.
- Raises:
ValueError – an entry of extra_args names an option _command already sets, refused the way __init__ refuses one and before the running node is stopped.
- property rpc: BitcoinCoreRpcClient¶
Return a fresh RPC client for this node, over its kept connections.
Each access builds a client of its own, so what one caller sets on it reaches no other; the connection underneath is the adapter’s, one per calling thread, and stop closes it.
- set_mock_time(timestamp: int) None[source]¶
Set this node’s own clock, over setmocktime.
Core’s own TestNode.setmocktime (test/functional/test_framework/test_node.py) wraps the same RPC; Capability.CLOCK (capability.py) is what a caller checks before calling this, a node not declaring it having no setmocktime to wrap.
- Parameters:
timestamp – the unix time this node’s own clock reads from now on; 0 releases it back to the wall clock.
- start() None[source]¶
Spawn the process and wait for its RPC to answer.
The process’s own stderr is captured into a file under datadir rather than an unread subprocess.PIPE: a pipe nobody drains fills its kernel buffer once a long-running node writes past it, and then blocks the node on every write past that, where a file never blocks the writer regardless of how much it writes.
Each start creates a file of its own under _STDERR_DIR, the way Core’s own TestNode.start opens a tempfile.NamedTemporaryFile in its stderr_dir, and stop reads back the file of the start it ends: a second adapter over the same datadir writes into a file of its own rather than over the one a running node still writes to ([ISS 105](https://github.com/btclib-org/bitcoin-node-tests/issues/105)).
A start that raises leaves nothing running: the process is killed and forgotten before the error propagates, the way Core’s own TestNode.assert_start_raises_init_error ends one, so a caller’s own teardown has no process to lose track of and stop stays a no-op after it ([ISS 79](https://github.com/btclib-org/bitcoin-node-tests/issues/79)).
A process this adapter already holds is refused rather than replaced: the replaced one would keep running past stop, holding its datadir’s lock and its ports ([ISS 92](https://github.com/btclib-org/bitcoin-node-tests/issues/92)). Core’s own TestNode.assert_start_raises_init_error asserts the same of its node before spawning one.
- Raises:
RuntimeError – this adapter already holds a process, which stop ends; or the process exited before answering, the message carrying what it wrote to stderr.
TimeoutError – the RPC never answered; the message carries the failures waited out, what the process wrote to stderr and, where _log_path names one, to its log since this start.
- stop() None[source]¶
Terminate the process, wait for it to exit, and read how it did.
A no-op where nothing was ever started, which is what lets a fixture’s own teardown call this unconditionally rather than track whether start succeeded.
The adapter’s RPC connections are closed once the process has exited, whichever way this returns or raises: a call another thread still has in flight holds its connection until the node answers it or goes, so closing any earlier would wait on that call rather than on the node.
A process still running once the wait expires is killed rather than left behind holding its datadir and ports, then waited for over the same bound, the way Core’s own TestNode.kill_process ends one; the slow shutdown is then raised, not hidden ([ISS 76](https://github.com/btclib-org/bitcoin-node-tests/issues/76)).
An exit code other than _CLEAN_EXIT is raised too, whether the process had already exited before this call – a crash during the test, which a test’s own last RPC call would not have seen – or exited so on terminate: Core’s own TestNode.stop_node checks the code the same way ([ISS 84](https://github.com/btclib-org/bitcoin-node-tests/issues/84)). A process that had already exited with _CLEAN_EXIT, the way an RPC stop ends one, is a clean stop.
Stderr is carried in the error and does not fail a clean exit on its own, unlike Core’s expected_stderr=’’.
Each of these raises only once the process has exited and been forgotten, so nothing is left running and a second stop is a no-op.
- Raises:
TimeoutError – the process ignored the termination for the whole wait and was killed; the message carries what it wrote to stderr.
RuntimeError – the process exited with a code other than _CLEAN_EXIT; the message carries the code, whether it had already exited before this call, and what it wrote to stderr.
- wait_until_stopped(*, timeout: float = 60.0) tuple[int, str][source]¶
Wait for the process to exit on its own, and return how it did.
Core’s own TestNode.wait_until_stopped (test_framework/test_node.py), for a node that something other than stop ends: an RPC stop, or a fault a test provokes. Core checks the exit code and stderr itself; here the caller does.
The process is forgotten once it has exited, and the adapter’s RPC connections closed, so a later stop is a no-op. A process that does not exit in time is left running, and stop still ends it.
- Parameters:
timeout – how long to wait, before –timeout-factor’s own scaling (timeout_factor.scaled).
- Returns:
the exit code, negative for a signal as subprocess reports it, and what the process wrote to stderr, stripped.
- Raises:
RuntimeError – this adapter holds no process.
TimeoutError – the process did not exit within timeout; the message carries what it wrote to stderr.
- bitcoin_node_tests.node.connect_nodes(first: NodeAdapter, second: NodeAdapter, *, timeout: float = 30.0, v2transport: bool = False) None[source]¶
Connect first to second, over addnode onetry and getpeerinfo.
Core’s own connect_nodes (test/functional/test_framework.py): addnode … “onetry” asks first to dial second’s own p2p address once, immediately. Core then waits for getpeerinfo to show the connection on both sides, matched by subversion, before waiting for each side’s own pong to confirm the handshake is actually done; this matches the same three waits, by a criterion each side can actually be read off rather than by subversion, since this adapter’s own nodes carry no per-node subversion tag to tell one apart from another. first’s own outbound entry is matched by address, second’s own p2p address being known and unique to it; second’s own new inbound entry cannot be matched the same way – its addr is first’s ephemeral outbound port, not the address first itself is reachable at – so it is matched by an id absent from a getpeerinfo snapshot taken before the dial, the one entry a single connect_nodes call can have added since (rule 4’s “the adapter translates a spelling” is why _wait_for_handshake reads bytesrecv_per_msg rather than assuming every adapter answers it: btclib-node’s own getpeerinfo already withholds a peer until its handshake is done, so nothing here is adapter-specific except which of these waits is a no-op).
Checking only first’s own view is racy under load: second can take longer to accept and register the same socket than first takes to see its own outbound half of it, which is what lets sendmsgtopeer on second answer “Could not send message to peer” moments after first alone would already report the connection.
addnode’s own third argument, v2transport, is passed explicitly, False unless the caller asks otherwise, rather than left to first’s own default: bitcoind’s own default is True since the pinned 31.1 (measured live – a bare addnode … “onetry” against a BtclibNodeAdapter never completes a handshake, bitcoind’s own debug.log reading “start sending v2 handshake to peer=0” immediately followed by “socket closed, disconnecting peer=0”), and Peer speaks no BIP324 – peer.py’s own module docstring states this suite’s own wire is v1 only, and a btclib-node build without -v2transport reads and type-checks the argument without acting on it (rpc/callbacks.py’s own docstring). bitcoind itself never falls back to v1 once a v2 attempt is reset ([ISS btclib-node#1197](https://github.com/btclib-org/btclib-node/issues/1197)), which is why it is stated rather than left to a fallback. Core’s own connect_nodes (test_framework.py) makes the identical choice through its own peer_advertises_v2 parameter, defaulting to whichever side is dialling; here the default is v1, the one wire every adapter speaks, and a test whose subject is BIP324 between two nodes declaring Capability.V2TRANSPORT passes v2transport=True.
- Parameters:
first – the node asked to dial.
second – the node dialled.
timeout – how long to wait for both sides to report the connection and its handshake, before –timeout-factor’s own scaling (timeout_factor.scaled).
v2transport – addnode’s own v2transport argument, whether first dials over BIP324.
- Raises:
TimeoutError – either side never reported the connection, or its handshake, in time.
- bitcoin_node_tests.node.disconnect_nodes(first: NodeAdapter, second: NodeAdapter, *, timeout: float = 30.0) None[source]¶
Disconnect first from second, over disconnectnode.
Core’s own disconnect_nodes (test/functional/test_framework/test_node.py): disconnectnode asks first to drop second’s own p2p address, and wait_until_disconnected above is how this waits for the drop to land, the same wait connect_nodes above makes for a connection appearing rather than vanishing.
Matched against connect_nodes(first, second): first is the side that dialled, so second’s address is what its own outbound entry was recorded under, and disconnectnode’s address form is what asks for exactly that entry rather than one among several a node with other peers also carries.
- Parameters:
first – the node asked to drop the connection.
second – the node dropped.
timeout – how long to wait for first to stop reporting it, before –timeout-factor’s own scaling.
- Raises:
TimeoutError – first still reports the peer after timeout.
- bitcoin_node_tests.node.free_port() int[source]¶
Return one port a bind on 127.0.0.1 accepts, as free_ports does.
- bitcoin_node_tests.node.free_ports(count: int) tuple[int, ...][source]¶
Return count ports a bind on 127.0.0.1 accepts, pairwise distinct.
Drawn from _PORTS rather than from the kernel’s own pick: a port the kernel hands out is one it can hand out again, to any other process’s bind to port 0 or outgoing connection, between this call returning and the caller’s node binding it ([ISS 192](https://github.com/btclib-org/bitcoin-node-tests/issues/192)).
Each process draws from a block of its own, _worker_block’s, the way Core’s own p2p_port (test_framework/util.py) offsets each test process by its PortSeed, so no two of the numbered workers pytest-xdist -n starts together draw the same port. Within the block, _PortCursor walks on from where its last draw stopped, and a candidate another process holds on 127.0.0.1 or on every IPv4 address – another run of this suite included – fails its probe bind and is skipped; one held on IPv6 alone does not.
- Parameters:
count – how many ports to return.
- Raises:
RuntimeError – fewer than count ports of this process’s block were free.
- bitcoin_node_tests.node.sync_all(nodes: Sequence[NodeAdapter], *, timeout: float = 30.0) None[source]¶
Wait for every node’s tip, then every node’s mempool, to agree.
Core’s own sync_all (test_framework.py): wait_until_tips_agree first, since a mempool’s own transactions are commonly what a block just mined is meant to clear – checking the mempool first could observe an old one, on a node whose new tip has not landed yet.
- Parameters:
nodes – the nodes to wait on, at least one.
timeout – forwarded to each of the two waits in turn, each scaling it by –timeout-factor, so the call bounds at twice the scaled value rather than once.
- bitcoin_node_tests.node.traced_transport(transport: Callable[[Request, float], tuple[int, bytes]]) Callable[[Request, float], tuple[int, bytes]][source]¶
Wrap transport, printing every RPC exchange it carries.
Core’s own –tracerpc (test_framework.py): “Print out all RPC calls as they are made”. bitcoin_core_rpc.BitcoinCoreRpcClient’s own transport= is exactly the two-argument callable (bitcoin_core_rpc.transport.HttpTransport) this wraps, an already-built Request and a timeout answered with a status and a body, so tracing needs no change to the client itself.
- Parameters:
transport – the transport to wrap, an adapter’s own _ThreadSessions where NodeAdapter._rpc_transport is what asks.
- bitcoin_node_tests.node.wait_until(predicate: Callable[[], bool], *, timeout: float = 30.0) None[source]¶
Poll predicate every 0.1 s until it returns True, or raise.
Core’s own TestNode.wait_until (test_framework/test_node.py) calls wait_until_helper_internal (test_framework/util.py) with that node’s –timeout-factor; this package spawns no TestNode for a method like that to live on, so a free function serves in its place, and a test-local wait that needs nothing beyond a predicate calls it instead of reimplementing this loop, unscaled, beside it ([ISS 90](https://github.com/btclib-org/bitcoin-node-tests/issues/90)).
- Parameters:
predicate – checked every 0.1 s until it returns True.
timeout – how long to keep polling, before –timeout-factor’s own scaling (timeout_factor.scaled).
- Raises:
TimeoutError – predicate never returned True within timeout.
- bitcoin_node_tests.node.wait_until_disconnected(node: NodeAdapter, peer: NodeAdapter, *, timeout: float = 30.0) None[source]¶
Wait until node’s own getpeerinfo no longer names peer’s address.
Core’s own is_connected_to (test/functional/test_framework/test_node.py) is self.wait_until wrapped around it, matched by getnetworkinfo’s own subversion string; this matches by p2p address instead, for the same reason connect_nodes above does – this adapter’s own nodes carry no per-node subversion tag. So node is the side that dialled: its outbound entry carries peer’s own listening address, where peer’s inbound entry for node carries an ephemeral port no p2p_address names, and a call with the two swapped returns at once.
Unlike disconnect_nodes below, this makes no RPC call of its own: the drop it waits for is triggered some other way – rpc_setban’s own subject, a setban on peer’s own side dropping the connection it matches – and disconnect_nodes is first.rpc.call (“disconnectnode”, …) followed by exactly this wait, first for node and second for peer.
- Parameters:
node – the node whose own getpeerinfo is polled.
peer – the peer whose address must disappear from it.
timeout – how long to wait, before –timeout-factor’s own scaling.
- Raises:
TimeoutError – node still reports peer after timeout.
- bitcoin_node_tests.node.wait_until_mempools_agree(nodes: Sequence[NodeAdapter], *, timeout: float = 30.0) None[source]¶
Poll every node’s getrawmempool until they all hold the same set.
Core’s own sync_mempools (test_framework.py): a transaction relayed over p2p reaches every node in a topology on its own schedule, exactly as a block does, which is wait_until_tips_agree above’s own reason restated for the mempool rather than the chain. Core’s own version also calls syncwithvalidationinterfacequeue on every node once they agree, flushing a background validation queue bitcoind’s own tests care about; this drops it; it is a call this adapter’s own node has no equivalent of, and no capability declares.
- Parameters:
nodes – the nodes to poll, at least one.
timeout – how long to wait for every mempool to match, before –timeout-factor’s own scaling.
- Raises:
TimeoutError – the nodes never agreed within timeout.
- bitcoin_node_tests.node.wait_until_tips_agree(nodes: Sequence[NodeAdapter], *, timeout: float = 30.0) None[source]¶
Poll every node’s getbestblockhash until they all agree.
Core’s own sync_blocks (test_framework.py): propagation over p2p relay is asynchronous, so a block one node just mined or received reaches the rest of a topology on their own schedule, and this is the wait that turns “eventually” into a deadline this suite holds to rather than a race the assertion after it would otherwise be.
- Parameters:
nodes – the nodes to poll, at least one.
timeout – how long to wait for every hash to match, before –timeout-factor’s own scaling.
- Raises:
TimeoutError – the nodes never agreed within timeout.
bitcoin_node_tests.peer module¶
Peer: the p2p connection a test drives, over btclib.p2p’s codecs.
Rule 5 of [ISS 2220](https://github.com/btclib-org/btclib/issues/2220): btclib’s own tests/integration/ fixture is not shared, and this is tf2’s own peer rather than a port of Core’s P2PInterface (test/functional/test_framework/p2p.py) – a synchronous socket and a receive loop over btclib.p2p.Message, Version and the rest, where Core’s own class is asyncio.Protocol driven from a background thread. What is read from Core’s own class, rather than copied, is the shape of the handshake and the convenience a test wants: handshake sends and waits for both sides’ version/verack, and wait_for is wait_until narrowed to “the next message of this command”, auto-answering a ping along the way so a caller waiting on anything else is not the one that has to remember to.
Listener is the other direction, Core’s peer_accept_connection: a socket the node is made to dial, the node’s own outbound connection arriving there as a Peer like any other.
- class bitcoin_node_tests.peer.Listener(magic: bytes, *, timeout: float = 30.0)[source]¶
Bases:
objectA loopback socket a node is made to dial, each connection a Peer.
Core’s P2PConnection.peer_accept_connection, what its TestNode.add_outbound_p2p_connection listens with, over one blocking socket rather than an event loop. The contract:
bound at construction to 127.0.0.1 and a port the OS chooses, and listening from then on, so address is dialable before the caller asks the node to dial it;
a backlog of one: the kernel completes the node’s own connection and queues it until accept, so the call that makes the node dial and the accept after it run in sequence on the caller’s thread;
accept returns the next queued connection as a Peer whose handshake waits for the node’s version before sending its own, and whose waits are this listener’s timeout;
close stops listening and leaves every accepted Peer open.
- Parameters:
magic – the four octets of the message start, as Peer’s.
timeout – how long accept waits, and every accepted Peer’s default wait, before –timeout-factor’s own scaling (timeout_factor.scaled).
- accept() Peer[source]¶
Return the next connection a node made to address, as a Peer.
- Raises:
TimeoutError – no connection arrived within the wait.
- class bitcoin_node_tests.peer.Peer(address: tuple[str, int], magic: bytes, *, timeout: float = 30.0)[source]¶
Bases:
objectOne p2p connection to a node, driven by hand rather than by a peer.
- Parameters:
address – (host, port) to dial, a NodeAdapter.p2p_address.
magic – the four octets of the message start, btclib.p2p.magic_from_chain(“regtest”) for every node this step drives.
timeout – the wait of the dial, and of every call on this peer given no timeout of its own, send and a bare receive included, before –timeout-factor’s own scaling (timeout_factor.scaled).
message_count is Core’s P2PInterface.message_count: how many messages of each command receive has returned, those wait_for and handshake read and drop included. last_message is Core’s P2PInterface.last_message: the latest message of each command receive has returned, kept whole for the caller to parse.
- handshake(*, services: ServiceFlags = <ServiceFlags.NODE_NETWORK|NODE_WITNESS: 9>, addrv2: bool = False, wtxidrelay: bool = True) Version[source]¶
Exchange version/verack, and return the node’s own version.
Core’s own handshake. The side that dialled sends its version first: this peer where it dialled the node, the node where it dialled a Listener, this peer then answering the node’s own version with its own, as Core’s P2PInterface.on_version does. Then, once both version`s are out, BIP339’s `wtxidrelay goes ahead of this peer’s verack – a btclib-node build before btclib-org/btclib-node#1183 refuses a verack that arrives without it first (measured against 382a29fb’s p2p.callbacks.verack, “a verack ahead of the version/wtxidrelay it depends on”), and bitcoind accepts one either way, so sending it is what one peer can offer both nodes.
- Parameters:
services – the services this peer’s own version offers, NODE_NETWORK | NODE_WITNESS where not given. Core’s add_p2p_connection takes the same services, and rpc_getblockfrompeer drops NODE_WITNESS from it to be a pre-segwit peer.
addrv2 – send BIP155’s sendaddrv2 between the wtxidrelay and the verack, asking the node for addrv2 rather than addr: Core’s own P2PInterface(support_addrv2=True), whose on_version sends it there. BIP155 has it sent before verack, and bitcoind drops a peer sending it after.
wtxidrelay – send BIP339’s wtxidrelay, True where not given. False is Core’s own P2PInterface(wtxidrelay=False), a peer announcing and asked for transactions by txid, which a btclib-node build before btclib-org/btclib-node#1183 refuses, as above.
- Returns:
the node’s own Version, nServices and all – p2p_getdata’s own P2PStoreBlock reads nothing off it, but a later family well might.
- property is_connected: bool¶
Whether the connection is still open, read without waiting.
Core’s P2PInterface.is_connected, which reads the state its background thread keeps. With no such thread here, this reads whatever the socket already holds without blocking, keeping the octets for receive, and answers False where that read ends in the node’s close or reset, or where close has run.
- receive(*, timeout: float | None = None) Message[source]¶
Return the next whole message, reading more off the socket as needed.
- Parameters:
timeout – applied to the underlying socket for every read this call makes, and for this call only: the socket is back at this peer’s own default wait when it returns or raises. That default wait where None.
- Raises:
ConnectionError – the peer closed the connection.
TimeoutError – no octet arrived within timeout.
- send(payload: Payload, *, check_validity: bool = True) None[source]¶
Frame payload for this connection’s own network, and send it.
- Parameters:
payload – the message to send.
check_validity – forwarded to Payload.to_message. False is what p2p_invalid_locator’s own subject needs: a locator over MAX_LOCATOR_SZ is exactly the octets a node’s own refusal is asked about, and assert_valid refuses to build one at all where this stayed True.
- send_raw(data: bytes) None[source]¶
Send already-serialized bytes, bypassing this peer’s own magic.
send above always frames for self._magic, this connection’s own network; a caller building a message with a different magic on purpose – the log family’s own tests, provoking the node’s “wrong network” refusal – serializes it directly (payload.to_message(bad_magic).serialize()) and hands the octets here instead.
- sync_with_ping(*, timeout: float | None = None) None[source]¶
Send two ping`s, and wait for the `pong answering the second.
A node processing a connection’s messages in the order they arrive, as Core’s does, answers the second ping only once every message sent ahead of the pings has been processed, and processes the first ping as a message of its own before it, so it runs its own send loop for this connection at least once in between. A node answering a ping ahead of the messages sent before it ([ISS btclib-node#1410](https://github.com/btclib-org/btclib-node/issues/1410)) guarantees neither. Core’s P2PInterface.sync_with_ping is the same barrier, built the same way, and its TestNode.add_p2p_connection runs it after the handshake: handshake returns on the node’s own verack, which the node sends before it has processed this peer’s.
- Parameters:
timeout – how long to wait, scaled by –timeout-factor like every other explicit wait; self._timeout (scaled already, at construction) where None.
- Raises:
TimeoutError – no matching pong arrived in time.
- wait_for(command: str, *, predicate: Callable[[Message], bool] | None = None, timeout: float | None = None) Message[source]¶
Return the next command message, answering a ping meanwhile.
Every other message received while waiting is read and dropped, ping excepted: it is answered with the nonce it carried, so a caller waiting on anything else is never the one that has to keep the connection’s own keepalive alive.
- Parameters:
command – the message name to wait for, “version” or “block”.
predicate – an additional test the message must pass, beyond naming command – matching a pong’s own nonce, say. Every message failing it is dropped exactly as one of the wrong command is.
timeout – how long to wait, scaled by –timeout-factor like every other explicit wait; self._timeout (scaled already, at construction) where None.
- Raises:
TimeoutError – no matching message arrived in time.
- wait_for_disconnect(*, timeout: float | None = None) None[source]¶
Block until the node closes this connection, or raise.
The log family’s own wire-observable half: a disconnect is a ConnectionError out of receive, so this drains and drops whatever the node still sends first – a ping, an addr – exactly as wait_for does, until either the socket closes or the deadline passes with it still open.
- Parameters:
timeout – how long to wait, scaled by –timeout-factor like every other explicit wait; self._timeout (scaled already, at construction) where None.
- Raises:
AssertionError – the connection was still open at the deadline.
bitcoin_node_tests.socks5 module¶
Socks5Proxy: a SOCKS5 server a node is pointed at, recording what it asks.
Core’s own Socks5Server (test/functional/test_framework/socks5.py), read rather than copied: a listener on IPv4 or IPv6 loopback, or on a unix socket, speaking the server half of SOCKS5 (RFC 1928) and of its username/password method (RFC 1929), which answers every CONNECT with success and records it as a Socks5Request – the address type, the host, the port and the credentials the node sent – for the test to read back with next_request. btclib names SOCKS nowhere, so this is tf2’s harness rather than a covering module. It is a module of the adapter rather than a class inside one test, as the mock Tor control server of tests/integration/feature_torcontrol_bitcoind_test.py is, because Core’s own is framework code several of its tests share – feature_proxy.py, feature_anchors.py and p2p_private_broadcast.py among them.
A connection whose request is recorded is held open, unread, until close: what Core’s own server does only under its keep_alive setting, which feature_anchors.py sets, closing it otherwise. The node keeps the peer, and getpeerinfo lists it, until close or until the node’s own -peertimeout drops a peer that never answered – bitcoind’s default is DEFAULT_PEER_CONNECT_TIMEOUT (src/net.h), a wait –timeout-factor does not scale.
Given a destinations_factory, the proxy forwards instead, as Core’s own server does under its setting of that name: the factory is handed each request, and the client’s own address as the proxy sees it, and names where the connection goes, the octets then copied both ways until either end closes. p2p_private_broadcast.py is what forwards its node’s connections to peers of its own choosing.
A protocol violation is not a request: it is queued in the request’s place and raised by the next_request that reaches it, rather than leaving the test to wait out a timeout for a request that never comes.
- class bitcoin_node_tests.socks5.AddressType(*values)[source]¶
Bases:
IntEnumRFC 1928’s ATYP: how the host of a request is spelled.
- class bitcoin_node_tests.socks5.Socks5Proxy(*, authentication: bool = False, family: int = AddressFamily.AF_INET, timeout: float = 30.0, destinations_factory: Callable[[Socks5Request, str], tuple[str, int] | None] | None = None)[source]¶
Bases:
objectA local SOCKS5 server, recording each CONNECT it answers.
The contract:
bound at construction to family’s loopback address and a port the OS chooses – or, for AF_UNIX, to a socket file in a directory of its own under the system’s temporary directory – and accepting from then on, on a thread of its own, so endpoint is dialable before the caller starts the node that dials it;
one connection at a time: each is negotiated to its request on that thread, then held open, unread, until close – or, given a destinations_factory, handed to a thread of its own that forwards it;
the username/password method is chosen where authentication is set and the client offers it, and no authentication otherwise, where the client offers that – the choice Core’s own server makes between its auth and unauth settings, unauth always on;
close stops accepting, closes every connection it holds or forwards, and removes a unix socket’s file and directory.
The temporary directory is the system’s rather than a test’s own tmp_path, as Core’s feature_proxy.py takes its path from tempfile: a unix socket’s path is bounded by the size of sockaddr_un’s own sun_path, which a test’s own directory can outgrow.
- Parameters:
authentication – offer RFC 1929’s username/password method.
family – AF_INET, AF_INET6 or AF_UNIX.
timeout – how long next_request waits, and how long one client is given to finish its negotiation, before –timeout-factor’s own scaling (timeout_factor.scaled).
destinations_factory – where given, called with each request once its success is sent, and with the client’s own address as the proxy sees it – host:port, what the client’s own getpeerinfo reports as that connection’s addrbind. It returns the (host, port) the connection is forwarded to, or None, which closes it. Core’s own Socks5Configuration’s destinations_factory, which is handed the host decoded, the port and that address, and returns them as a dict. What it raises, and a destination refusing the connection, is queued for next_request behind the request, as Core’s own server queues it.
- property address: tuple[str, int] | str¶
Return the (host, port) a client connects to, or a unix path.
- close() None[source]¶
Stop accepting, and close every connection held open.
A connection to the proxy’s own address, in its own family, is what wakes the thread blocked in accept, the way Core’s own Socks5Server.stop does. A client still negotiating is shut down first, so the thread returns to accept without waiting out that client’s timeout; the join is bounded by the same timeout all the same, as Core’s own stop bounds its handlers’ joins. A unix socket’s file goes with the directory holding it. A forwarded connection is shut down, which ends its thread’s copying and closes its destination’s end, and that join is bounded the same way.
- property endpoint: str¶
Return this proxy as -proxy and getnetworkinfo spell it.
host:port, an IPv6 host in brackets, or a socket’s path behind Core’s own unix: prefix (IsUnixSocketPath, src/netbase.cpp).
- next_request() Socks5Request[source]¶
Return the oldest request not yet returned.
- Raises:
TimeoutError – none arrived within the wait, or a client stalled mid-negotiation past it, raised in its request’s place.
ConnectionError – a client closed its connection before its request was complete.
ValueError – a client broke the protocol; the message says where.
- class bitcoin_node_tests.socks5.Socks5Request(address_type: AddressType, host: bytes, port: int, username: bytes | None, password: bytes | None)[source]¶
Bases:
objectOne CONNECT a client made, as the proxy received it.
- Parameters:
address_type – how host is spelled.
host – the host octets as sent: four for IPV4, sixteen for IPV6, and the name itself for DOMAINNAME.
port – the port asked for.
username – the RFC 1929 username, None where the client authenticated with no method.
password – the RFC 1929 password, None likewise.
bitcoin_node_tests.timeout_factor module¶
How every default wait and the adapters’ RPC client timeout are scaled.
Core’s own –timeout-factor (test_framework.py) multiplies every wait_until call by one value read off a BitcoinTestFramework instance; this package spawns no such object for a value like that to live on, so a small mutable singleton serves in its place – set once per process by tests/integration/conftest.py’s own –timeout-factor option, before any node starts. Under -n auto this module loads once per xdist worker and once in the controller, each with its own _factor; every worker receives the same command line, so every process scales the same way, matching the “one tally per process” scope tests/integration/conftest.py’s own SkipCounts already carries for the identical reason.
- bitcoin_node_tests.timeout_factor.factor_from_option(value: str) float[source]¶
Return the multiplier –timeout-factor value asks for.
Core’s own reading of the option: its help says “Setting it to 0 disables all timeouts”, and BitcoinTestFramework.parse_args (test_framework.py) does that by putting 999 in place of 0, so every wait is still a bound, only a very long one. Any other value, a negative one included, is kept as given, as Core keeps it.
- Parameters:
value – the option’s own text, as the command line gives it.
- Raises:
ValueError – value is not a number.
- bitcoin_node_tests.timeout_factor.rpc_client_timeout() int[source]¶
Return the seconds an adapter’s RPC client may hold one call.
Core’s own bound, scaled the same way: BitcoinTestFramework.__init__ sets rpc_timeout to 60 and then to int(self.rpc_timeout * self.options.timeout_factor) (test_framework.py), and TestNode.create_new_rpc_connection gives a connection whose caller names no client_timeout rpc_timeout // 2, “to allow for one retry in case of ETIMEDOUT” (test_node.py) – 30 at the default factor. A factor below 1/30 makes it 0, which bitcoin_core_rpc.BitcoinCoreRpcClient refuses to be built with.
- bitcoin_node_tests.timeout_factor.scaled(seconds: float) float[source]¶
Return seconds, multiplied by whatever set_factor last set.
- Parameters:
seconds – a wait this package would otherwise use unscaled.
- bitcoin_node_tests.timeout_factor.set_factor(factor: float) None[source]¶
Set the multiplier scaled applies from now on, in this process.
- Parameters:
factor – the multiplier itself, taken as given: –timeout-factor reaches it through factor_from_option, so a 0 here is the caller’s own and makes every wait 0. 1.0 leaves every wait exactly as written, matching Core’s own default.
Module contents¶
The bitcoin_node_tests package: tf2, and the version metadata.
Core’s functional tests, rewritten on btclib, run against any node that speaks bitcoin’s RPC and p2p (issue btclib-org/btclib#2220). It imports btclib and bitcoin-core-rpc; it imports no node, reaching one only over a process, an RPC socket or a p2p socket.
The adapter under this package – CONTRIBUTING.md’s The public surface names its modules – is each a submodule with its own __all__. This package’s own root re-exports none of them: a caller imports the submodule it needs, from bitcoin_node_tests.bitcoind import BitcoindAdapter rather than a name off the root, so __all__ here stays empty rather than absent – a decision, not a placeholder for anything to fill.
name and the metadata dunders are not in __all__: each is still an attribute here, bitcoin_node_tests.__version__ being how a caller reads the version.