Write reorg data for blocks near the tip during initial block download - #14
Merged
Merged
Conversation
|
Concept ACK There is an issue with esplora backend where |
`chain_info` is not implemented for the esplora backend, so the early exit from IBD never triggers there and catch-up blocks after a restart remain rollback-incapable. Note this on the helper and on the CLI flag.
Author
|
Thanks. Added the esplora-mode note on |
DeviaVir
marked this pull request as ready for review
September 16, 2026 16:50
…ar-tip-during-ibd
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 free
to 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.
Problem
Blocks indexed while
ibd == trueget noREORG_CFundo record (src/store/db.rs, theibdgate inStore::update).ibdstartstrueon every process start and only flips tofalseon the firstChainStatus::Tip, so every block indexed while catching up after a restart is permanently rollback-incapable. When the chain later reorgs across one of them,store.reorg()fails withNo reorg data found for height N, the blocks task panics (the HTTP listener survives, so the process keeps answering 503), and restarting reproduces the same panic because the reorged block is still the last entry inHASHES_CF.--reorg-data-keep-heightsdoes not help: it only controls how long a written record is kept, and here the record was never written. Observed on testnet4 with--reorg-data-keep-heights 144after a 7-block reorg; the only recovery was a reindex or a restore from an older snapshot.Fix
Gate the undo write on block age instead of process state:
threads::blocksreads the node tip (chain_info) at startup and every 60 s while catching up, and callsibd_finished()as soon as the block about to be indexed satisfiesnode_tip - height < reorg_data_keep_heights. From then on every block gets reorg data, exactly as after reaching the tip. Genuine initial sync of old blocks still skips the write. A stale (lower) tip only enables writes earlier, never later.ibd_finished()itself when the tip is reached, before the next block is indexed, instead of the mempool task that receives the initial-sync signal. This removes a small race where a block could be indexed between the signal and the flag flip.DBStore::ibd_finishedis idempotent (swap) and logs only on the transition.reorg_data_keep_heightsis threaded to the blocks task; the default6is now a named constant.Esplora mode (
chain_inforeturnsNone) keeps the previous behaviour: reorg data starts at the first tip.Tests
test_bitcoin_reorg_after_restart(integration, regtest): funds a UTXO, broadcasts tx A, stops the server, mines block A and one more block, restarts the server on the same database, then invalidates block A and mines a longer chain double-spending the UTXO. Asserts the server follows the new tip and the histories are corrected.masterthis fails exactly like production:reorg failed: No reorg data found for height 105→ tip never advances →no tip hash after 10s.test_reorg_data_written_only_after_ibd_finished(unit): no reorg data and_reorgfails while in IBD; reorg data written andreorgsucceeds afteribd_finished;ibd_finishedis idempotent.reorg_window(unit): boundary cases of the window check, including saturating behaviour past a stale tip.TestEnv::shutdown_server()(stops the server, waits for it, returns the node) andgenerate_blocks(&node, n)for mining while no server is running.Ran locally:
cargo test --features test_env(unit + regtest integration), thereorg_crash_testexact test, thecargo checkmatrix from CI (--benches,--tests --features test_env,--no-default-featureswith and withouttest_env),cargo fmt,cargo clippy(only warning is pre-existing insrc/fetch.rs).Not in this PR
_reorg()still leaves the rolled-back height inHASHES_CF(it only mutates UTXO/HISTORY and deletes theREORG_CFentry); a crash between a successful rollback and the re-index of that height would replay the reorg against an already-consumed undo record. Deleting the row in the same batch is a two-line follow-up.chain_inforeturnsNonethere, so catch-up blocks after a restart still get no reorg data and behaviour is unchanged from before this PR. Documented onnode_tip_heightand on the CLI flag. Fixing it needs a tip-height lookup for esplora (GET /blocks/tip/height) if that backend stays.