Motivation
Several RPC methods issue multiple sequential ReadStateService queries and combine the results into a single response. Each oneshot call independently samples the latest best chain, so a reorg or tip advance between queries can produce internally inconsistent responses. The getrawtransaction instance of this pattern was fixed in PR #10523; three sibling sites remain.
Affected methods
getblock (verbosity 1 and 2) — resolves the header by height, then fetches transactions using the original height instead of the resolved hash. A reorg between the two reads can mix block A's header with block B's transactions. For verbosity 2, transactions from the wrong block are labeled with the resolved block's hash.
getblockheader (verbose) — resolves the header, then fetches SaplingTree and Depth in separate queries. The confirmations field derived from Depth can be stale if the tip advances between queries. The maintainer comment at methods.rs:1494 already acknowledges the reorg race for SaplingTree.
gettxout — issues three separate queries: Tip, Transaction, and IsTransparentOutputSpent. A block landing between the transaction fetch and the spent check can produce a false "unspent" response for an already-spent output. The maintainer TODO at methods.rs:3113-3114 acknowledges this gap.
Fix
Apply the same pattern as the patched getrawtransaction (PR #10523): after the first state query resolves a concrete block hash, use that hash for all subsequent queries. For gettxout, capture the tip from the transaction query rather than issuing a separate Tip request.
Reported by x15-eth (getblock) and lynchmobb (getblockheader, gettxout).
Motivation
Several RPC methods issue multiple sequential
ReadStateServicequeries and combine the results into a single response. Eachoneshotcall independently samples the latest best chain, so a reorg or tip advance between queries can produce internally inconsistent responses. Thegetrawtransactioninstance of this pattern was fixed in PR #10523; three sibling sites remain.Affected methods
getblock(verbosity 1 and 2) — resolves the header by height, then fetches transactions using the original height instead of the resolved hash. A reorg between the two reads can mix block A's header with block B's transactions. For verbosity 2, transactions from the wrong block are labeled with the resolved block's hash.getblockheader(verbose) — resolves the header, then fetches SaplingTree and Depth in separate queries. Theconfirmationsfield derived from Depth can be stale if the tip advances between queries. The maintainer comment atmethods.rs:1494already acknowledges the reorg race for SaplingTree.gettxout— issues three separate queries: Tip, Transaction, and IsTransparentOutputSpent. A block landing between the transaction fetch and the spent check can produce a false "unspent" response for an already-spent output. The maintainer TODO atmethods.rs:3113-3114acknowledges this gap.Fix
Apply the same pattern as the patched
getrawtransaction(PR #10523): after the first state query resolves a concrete block hash, use that hash for all subsequent queries. Forgettxout, capture the tip from the transaction query rather than issuing a separate Tip request.Reported by x15-eth (
getblock) and lynchmobb (getblockheader,gettxout).