Depends on #2200 — the on-demand height fetch primitive. The opt-in mode described here can be implemented in terms of that primitive.
Context
LocalChain<Header>::canonicalize_with_mtp computes median-time-past (MTP) for each confirmed transaction and for the chain tip (Canonical::tip_mtp). MTP at height N is the median of timestamps from the 11 blocks preceding N, so the local chain must contain headers for heights N-11..N at minimum. If those headers aren't present, MTP can't be computed for that block.
Today, the chain source crates only emit the confirmation block when reporting a confirmed tx, and only the tip itself for the chain tip. Downstream callers wanting MTP have to fetch the preceding 11 headers themselves, which is awkward and easy to get wrong.
Goal
Add an opt-in mode to each chain source that also emits the 11 blocks preceding:
- Every confirmation block (so per-tx MTP is computable), and
- The chain tip (so
tip_mtp is computable).
A LocalChain<Header> populated from the source should then have everything canonicalize_with_mtp needs without extra caller-side fetches.
Scope
bdk_electrum
bdk_esplora
bdk_bitcoind_rpc
Design notes
- Opt-in via builder/constructor flag. Most callers don't need MTP and shouldn't pay the extra round-trips. Each crate should expose this through whatever builder or constructor API it already has.
- Dedup overlapping windows. A confirmation near the tip will share most of its prev-11 window with the tip's prev-11. Ditto two confirmations close in height. The source should fetch and emit each prev header at most once per sync.
- Already-known headers. If a header is already in the caller's
LocalChain, it shouldn't be refetched — accept a "known heights" filter or expose enough that the caller can dedupe.
- Reorg safety. Prev-11 headers must be tied to the same chain as the block they're preceding; if the confirmation or tip reorgs, the prev-11 emission for the old branch must be invalidated too.
Acceptance criteria
- Each crate exposes a builder/constructor flag to enable prev-11 emission.
- With it enabled, populating a
LocalChain<Header> from the source is sufficient to call canonicalize_with_mtp and get Some(_) MTPs for all confirmed txs and for the tip.
- Test covering: a confirmation at height
N results in headers N-11..N present in the resulting chain.
- Test covering: a chain tip at height
T results in headers T-11..T present in the resulting chain.
- Test covering: two confirmations within 11 blocks of each other (or a confirmation within 11 blocks of the tip) don't trigger duplicate header fetches.
Depends on #2200 — the on-demand height fetch primitive. The opt-in mode described here can be implemented in terms of that primitive.
Context
LocalChain<Header>::canonicalize_with_mtpcomputes median-time-past (MTP) for each confirmed transaction and for the chain tip (Canonical::tip_mtp). MTP at heightNis the median of timestamps from the 11 blocks precedingN, so the local chain must contain headers for heightsN-11..Nat minimum. If those headers aren't present, MTP can't be computed for that block.Today, the chain source crates only emit the confirmation block when reporting a confirmed tx, and only the tip itself for the chain tip. Downstream callers wanting MTP have to fetch the preceding 11 headers themselves, which is awkward and easy to get wrong.
Goal
Add an opt-in mode to each chain source that also emits the 11 blocks preceding:
tip_mtpis computable).A
LocalChain<Header>populated from the source should then have everythingcanonicalize_with_mtpneeds without extra caller-side fetches.Scope
bdk_electrumbdk_esplorabdk_bitcoind_rpcDesign notes
LocalChain, it shouldn't be refetched — accept a "known heights" filter or expose enough that the caller can dedupe.Acceptance criteria
LocalChain<Header>from the source is sufficient to callcanonicalize_with_mtpand getSome(_)MTPs for all confirmed txs and for the tip.Nresults in headersN-11..Npresent in the resulting chain.Tresults in headersT-11..Tpresent in the resulting chain.