Skip to content

rpc: return bip125-replaceable and getblockstats utxo_size_inc so typed Rust clients decode - #1330

Merged
reardencode merged 2 commits into
reardencode:masterfrom
smolcars:rpc/typed-client-fields
Oct 30, 2026
Merged

reardencode merged 2 commits into
reardencode:masterfrom
smolcars:rpc/typed-client-fields

Conversation

@niteshbalusu11

@niteshbalusu11 niteshbalusu11 commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Closes #1255.

bitcoincore-rpc 0.19 failed to decode getmempoolentry / verbose getrawmempool (missing bip125-replaceable) and getblockstats (missing utxo_size_inc). Both fields are now returned.

  • bip125-replaceable on getmempoolentry, verbose getrawmempool, and verbose getmempoolancestors / getmempooldescendants. It is Core's IsRBFOptIn: true when the tx or an in-mempool ancestor has an input sequence at or below 0xfffffffd. It is not hardcoded to true as the issue suggests: Core 32.1 still reports false for an opt-out tx under full RBF (feature_rbf.py test_fullrbf), and full RBF here still replaces it. MempoolHub::signals_bip125 checks the tx and its ancestor set under one read lock. This overlaps the bip125 part of draft rpc: answers Core clients rely on (captaind / bark on rbitcoin) #1191, which has been idle.
  • getblockstats utxo_size_inc / utxo_size_inc_actual. Each output adds its serialized CTxOut plus PER_UTXO_OVERHEAD, each spent prevout subtracts the same, and _actual adds only outputs that enter the UTXO set. The overhead is 40, Core v32.1's sizeof(COutPoint) + sizeof(uint32_t). The issue cites Core 31.1, which counted a spent-flag byte (41). This repo pins v32.1. A stamped block reads its input edges in one input.body span read and each parent's outputs once, only when the selected stats include a size stat. The stamped outputs pass also sums total_out now, so Query::non_coinbase_total_out is gone.
  • The Core functional proxy no longer computes the two size stats from Esplora. rpc_blockstats.py and its self-test are deleted, and rpc_getblockstats.py runs against the node alone.

Not in this PR: gettxoutsetinfo stays -32601. COMPAT.md lists it as never: the node keeps no UTXO set to hash or count.

Proof: the new assertions fail before each fix and pass after. The mempool journey covers a signaling parent, an opt-out child that inherits true, an opt-out local tx at false, and full RBF replacing that tx. getblockstats pins regtest genesis at 116/0, and the stamped and reconstruct paths both match a tally from getblock verbosity 3 prevouts for a block with an OP_RETURN output. Core 32.1's rpc_getblockstats.py passes locally against the node with the shim removed. My first cut used 41, and this script caught it at height 101 (163/75 instead of 161/74). Local fmt, clippy, deny, ast-grep, ratchets, ci-os-smoke.sh, the harness self-tests, and the workspace tests pass. The exceptions are outbound_refill_journey, which needs sudo for a netns locally, and tip_accept::two_full_senders_both_enqueue, which failed once under workspace load and passed 8/8 alone and in a full rbitcoin-net suite run.

This changes the Core functional harness, so it needs the core-functional label. I cannot add labels on this repo.

🤖 Generated with Claude Code

niteshbalusu11 and others added 2 commits October 30, 2026 18:49
getmempoolentry, verbose getrawmempool, and verbose getmempoolancestors /
getmempooldescendants left out Core's bip125-replaceable. Typed clients
such as bitcoincore-rpc require it, so decoding an entry failed with
"missing field `bip125-replaceable`" (reardencode#1255).

The field is Core's IsRBFOptIn: true when the transaction or an
in-mempool ancestor has an input sequence at or below 0xfffffffd.
MempoolHub::signals_bip125 checks the tx and its ancestor set under one
read lock. Full RBF still replaces an opt-out transaction, as in Core's
feature_rbf.py test_fullrbf, so the field is the signal only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
getblockstats omitted Core's two coins-DB size stats, and selecting
either name was an invalid statistic. Typed clients such as
bitcoincore-rpc require both, so get_block_stats failed with "missing
field `utxo_size_inc`" (reardencode#1255). The Core functional proxy filled them in
from Esplora for rpc_getblockstats.py.

Each output adds its serialized CTxOut plus Core v32.1's
PER_UTXO_OVERHEAD (40: the outpoint and the packed height and coinbase
flag), and each spent prevout subtracts the same. utxo_size_inc_actual
adds only outputs that enter the UTXO set, the rule
utxo_increase_actual already uses. The reconstruct path has the
prevouts in hand. A stamped block reads its input edges in one
input.body span and each parent's outputs once, and only when the
selected stats include a size stat.

The stamped outputs pass now also sums total_out, so each tx's outputs
are read once and Query::non_coinbase_total_out loses its last caller.
The proxy shim (rpc_blockstats.py) and its self-test are deleted;
rpc_getblockstats.py passes against the node alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@niteshbalusu11
niteshbalusu11 force-pushed the rpc/typed-client-fields branch from 7b2493e to bda5015 Compare October 30, 2026 23:07

@rearden-grok rearden-grok Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE

0 bugs, 0 suggestions, 0 nits.

@reardencode

Copy link
Copy Markdown
Owner

I hate this PR because we don't have a UTXO set, but agreed it's the right compatibility thing to do. Thanks!

@reardencode
reardencode merged commit 8381e48 into reardencode:master Oct 30, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

getmempoolentry / getrawmempool(verbose) omit bip125-replaceable and getblockstats omits utxo_size_inc: typed Rust clients fail

2 participants