Skip to content

fix(electrum): refresh header cache with headers fetched for the chain update - #2319

Merged
evanlinjin merged 3 commits into
bitcoindevkit:release/chain-0.23.xfrom
evanlinjin:fix/electrum-stale-header-cache
Oct 12, 2026
Merged

evanlinjin merged 3 commits into
bitcoindevkit:release/chain-0.23.xfrom
evanlinjin:fix/electrum-stale-header-cache

Conversation

@evanlinjin

Copy link
Copy Markdown
Member

Description

block_header_cache was only written by batch_fetch_anchors, so after a reorg it kept the pre-reorg header for that height. A tx re-mined at the same height then hit the anchor cache under the stale block hash, skipped proof validation, and was never re-anchored to the replacement block. This happens even for a 1-block reorg.

fetch_tip_and_latest_blocks now writes the headers it fetches (the chain suffix and the agreement-walk checkpoints) into the header cache.

Notes to the reviewers

The test covers two cases: syncing right after the reorg (header is in the suffix), and syncing after 20 more blocks (header comes from the agreement walk). Both fail on the base branch. The 20-block case also fails if only the agreement-walk insert is removed.

Related: #2312. That issue points at chain_update, but checkpoints are already corrected by the agreement walk. The stale header cache is what leaves the anchor wrong.

Changelog notice

  • Fixed bdk_electrum returning a stale anchor for a transaction re-mined at the same height after a reorg.

Checklists

All Submissions:

Bugfixes:

  • This pull request breaks the existing API
  • I've added tests to reproduce the issue which are now passing
  • I'm linking the issue being fixed by this PR

🤖 Generated with Claude Code

@noahjoeris noahjoeris 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.

tACK a518204

`powerfmt v0.2.1` requires rustc 1.79, which breaks the 1.75 CI jobs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@evanlinjin
evanlinjin force-pushed the fix/electrum-stale-header-cache branch from a518204 to e3cb6fc Compare October 12, 2026 01:48
evanlinjin and others added 2 commits October 12, 2026 04:15
…n update

`block_header_cache` was only written by `batch_fetch_anchors`, so after a
reorg it kept serving the pre-reorg header for that height. The anchor
cache is keyed by `(txid, hash)` from that header, so a tx re-mined at the
same height hit the anchor cache with the stale hash, skipped proof
validation, and was never re-anchored to the replacement block.

`fetch_tip_and_latest_blocks` now writes the headers it fetches (the
chain suffix and the agreement-walk checkpoints) into the header cache.
Checks the tx is re-anchored to the replacement block both when the
reorged height is still in the synced chain suffix and when 20 blocks are
mined before the next sync.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@evanlinjin
evanlinjin force-pushed the fix/electrum-stale-header-cache branch from e3cb6fc to c04f487 Compare October 12, 2026 04:15
@evanlinjin

Copy link
Copy Markdown
Member Author

I added a commit to fix MSRV.

@evanlinjin evanlinjin left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

self-ACK c04f487

@evanlinjin
evanlinjin merged commit 77b0e74 into bitcoindevkit:release/chain-0.23.x Oct 12, 2026
19 checks passed
evanlinjin added a commit that referenced this pull request Oct 12, 2026
… for the chain update

6c5e977 test(electrum): cover tx re-mined at the same height after a reorg (志宇)
b434370 fix(electrum): refresh header cache with headers fetched for the chain update (志宇)

Pull request description:

  ### Description

  Forward-port of #2319 to `master`.

  `block_header_cache` was only written by `batch_fetch_anchors`, so after a reorg it kept the pre-reorg header for that height. A tx re-mined at the same height then hit the anchor cache under the stale block hash, skipped proof validation, and was never re-anchored to the replacement block. This happens even for a 1-block reorg.

  `fetch_tip_and_latest_blocks` now writes the headers it fetches (the chain suffix and the agreement-walk checkpoints) into the header cache.

  ### Notes to the reviewers

  Only differences from #2319: the `fetch_tip_and_latest_blocks` signature keeps master's `CheckPoint<BlockHash>`, and the test uses master's `TestEnv` / `LocalChain` APIs. The test fails on `master` without the fix.

  Related: #2312.

  ### Changelog notice

  - Fixed `bdk_electrum` returning a stale anchor for a transaction re-mined at the same height after a reorg.

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  #### Bugfixes:

  * [ ] This pull request breaks the existing API
  * [x] I've added tests to reproduce the issue which are now passing
  * [ ] I'm linking the issue being fixed by this PR

  🤖 Generated with [Claude Code](https://claude.com/claude-code)

ACKs for top commit:
  noahjoeris:
    tACK 6c5e977

Tree-SHA512: 9e9aa3c654a9898dfefa27acf99de13a7563802d7d22577437cf8ac064542074e8ee893646b1f22580da9cccfd444be7bdd348bcc4b760f5991d00cca1d41eee
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants