Pin confirmed-tx API reads to a chain view and stamp the tip - #200
Merged
Conversation
added 7 commits
August 22, 2026 16:14
Confirmed-tx API reads need a snapshot token that is the tip hash, not only height. Capture height+hash+header_fk; still_live is true while confirmed[height] is that fk so extension keeps a prefix pin and same-height replace does not.
Same-height reorg reused the connection join because the slot keyed on height. Store the pin hash; is_confirmed_strong_at(view.height) hides creates above the pin so a prefix view cannot leak the live tip.
A request that straddles a disconnect must not return a torn mix. run_at_chain_view pins, runs the body, and retries up to 8 times if confirmed[height] moved. Exhaustion is StoreError::Stale, not Corrupt.
Sequential fetches need a snapshot token that is the tip hash, not height. Pin before the handler; if that prefix is still published, stamp hash+height and CORS-expose them. A same-height replace kills the pin and yields 503 so the client retries an honest body.
Electrum is TCP, not HTTP, so the snapshot token is an extra JSON-RPC member next to result. get_history / get_balance / listunspent / transaction.get / get_merkle retry while the pin stays live. ping and version omit the fields. server.features.chain_tip advertises it.
Legacy status is SHA256 of txid:height, so a same-height replace is silent. Include the confirming block hash in the preimage. TipNotify carries reorg_from_height so a reorg restatuses every watch even when the new block does not touch the script.
Document reader pin+retry (no pause-queries), Esplora tip headers, and Electrum chain_tip + status blockhash. Credit Yuval for raising A-B-A; cite mempool/mempool#6584 and spesmilo/electrum-protocol#2 (#17 revert).
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Yuval pointed out that Electrum and Esplora have an A-B-A hole: a
same-height reorg can leave
txid:height(and a height-keyed join cache)unchanged while merkle proofs and confirming block hashes moved.
We researched:
chaintip, then reverted it in #17 because ElectrumX is bitcoind middleware and cannot pin. That argument does not apply here.What
Confirmed-tx reads pin
{height, hash, header_fk}of the published tip(
Query::pin_chain_view). Extension leaves a prefix pin live. Disconnector same-height replace dies the pin;
run_at_chain_viewretries (bound 8)then
StoreError::Stale— not a writer lock and not MVCC.X-Bitcoin-Chain-Tip+X-Bitcoin-Chain-Tip-Height(CORS-exposed). Pin death → 503.chain_tip/chain_tip_heightnext toresult(ping/version omit).server.features.chain_tip.txid:height:blockhash:for confirmed rows so same-height replace changes status. Reorg restatuses every watch.We stamp tip, not only the last relevant history tx. We do not serve
“as of hash H” after H is disconnected.
Docs:
COMPAT.md,docs/concurrency.md(reader pin+retry),docs/crash-recovery.md.Test plan
/blocks/tip/hash; omitted on empty chain; changes after same-height replace.get_historystampschain_tip; ping omits it; status includes blockhash; reorg notifies a dropped scripthash.Do not merge unless asked.