indexes: set prune lock to genesis before first block #36150

pull andrewtoth wants to merge 2 commits into bitcoin:master from andrewtoth:fix_prune changing 2 files +10 −2
  1. andrewtoth commented at 4:47 PM on September 2, 2026: contributor

    When setting both a new index and prune size and restarting an unpruned node, the node will prune the block store first and then the index will fail to start syncing.

    Fix this by setting the prune lock to 0 if the index does not yet have a best block.

  2. DrahtBot added the label UTXO Db and Indexes on Sep 2, 2026
  3. DrahtBot commented at 4:47 PM on September 2, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/36150.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process.

    Type Reviewers
    Concept ACK l0rinc, mzumsande
    Stale ACK sedited

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  4. l0rinc commented at 5:47 PM on September 2, 2026: contributor

    Concept ACK! Since #34897 the locator can lag the in-memory tip while SetBestBlockIndex() still advances the lock on every update, so should we maybe derive the lock from the committed locator instead (initialize in Init(), advance in Commit(), cap at the chain height on reorg, flush and drain before pruneblockchain) and cover it with a prune-mode index test?

  5. andrewtoth commented at 5:51 PM on September 2, 2026: contributor

    Since #34897 the locator can lag the in-memory tip while SetBestBlockIndex() still advances the lock on every update, so should we maybe derive the lock from the committed locator instead (initialize in Init(), advance in Commit(), cap at the chain height on reorg, flush and drain before pruneblockchain) and cover it with a prune-mode index test?

    That seems out of scope for what this is fixing. If you have an unpruned node, and then add both a new index and a prune value to the config and restart, it will break on indexing because it will prune first before syncing the index. This is not about a lagging locator, but startup failing to lock the prune height for an empty index.

  6. mzumsande commented at 3:47 PM on September 3, 2026: contributor

    Concept ACK

  7. sedited approved
  8. sedited commented at 7:05 PM on September 3, 2026: contributor

    ACK 39e4a6bb41749547c983456ce05281ca9eae16fe

  9. DrahtBot requested review from mzumsande on Sep 3, 2026
  10. DrahtBot requested review from l0rinc on Sep 3, 2026
  11. andrewtoth commented at 4:36 PM on September 8, 2026: contributor

    I hope this can get in before v32 cutoff. If you restart your non-pruned node with a prune target and a new index, you will first prune then crash on starting to index. Then you are S.O.L. with your new index and your only recourse is to do a full -reindex. I came across this while testing #36002.

    It's a very simple fix. I assume the original check for block was intended as a null guard, but it should really set it to genesis instead of skipping the prune lock.

    Here's a reproducer. I'm not sure it's worth putting it in the codebase though.

    #!/usr/bin/env python3
    from feature_pruning import mine_large_blocks
    from test_framework.test_framework import BitcoinTestFramework
    from test_framework.util import assert_greater_than
    
    
    class IndexPruneStartupTest(BitcoinTestFramework):
        def set_test_params(self):
            self.setup_clean_chain = True
            self.num_nodes = 1
    
        def run_test(self):
            node = self.nodes[0]
            self.log.info("Build an unpruned chain exceeding the minimum automatic prune target")
            mine_large_blocks(node, 1020)
            height = node.getblockcount()
    
            self.log.info("Enable automatic pruning and a fresh blockfilterindex on the same restart")
            self.restart_node(0, extra_args=["-prune=550", "-blockfilterindex=1"])
            self.wait_until(lambda: node.getindexinfo() == {
                "basic block filter index": {"synced": True, "best_block_height": height},
            })
    
    
    if __name__ == "__main__":
        IndexPruneStartupTest(__file__).main()
    
  12. sedited added this to the milestone 32.0 on Sep 8, 2026
  13. l0rinc changes_requested
  14. l0rinc commented at 11:09 PM on September 8, 2026: contributor

    This is not about a lagging locator, but startup failing to lock the prune height for an empty index.

    Sure, let’s fix startup here and address the lock advancing beyond the committed index state separately.


    The problem we're fixing here was introduced in #21726, which included the genesis fallback at one stage, but lost it without a regression test catching it, so we should learn from that and add one here.

    Luckily, DeletePruneLock() already returns whether the lock existed, so we can check that and just reinitialize the index in blockfilter_index_initial_sync. I’ve prepared characterization + fix commits in l0rinc/bitcoin#296 as a demo.

  15. DrahtBot requested review from l0rinc on Sep 8, 2026
  16. test: characterize startup with newly added prune and index 0ae3b40c27
  17. indexes: set prune lock to genesis before first block
    When setting both a new index and prune size and restarting
    an unpruned node, the node will prune the block store first
    and then the index will fail to start syncing.
    
    Fix this by setting the prune lock to 0 if the index does not
    yet have a best block.
    6d7caa1c23
  18. andrewtoth force-pushed on Sep 9, 2026
  19. andrewtoth commented at 1:32 AM on September 9, 2026: contributor

    Thanks @l0rinc, I added the characterization test in the first commit and updated to the regression test in the second commit. I think the functional test suite is more appropriate for this, since we want to make sure we can startup and sync a new index when we introduce pruning at the same time. This functionality could possibly fail in other ways in the future, even if the underlying prune locks are correctly applied.

  20. in test/functional/feature_pruning.py:474 in 6d7caa1c23
     470 | @@ -472,15 +471,12 @@ def run_test(self):
     471 |          self.sync_blocks([self.nodes[0], self.nodes[5]], wait=5, timeout=300)
     472 |  
     473 |          self.log.info("Test prune with a new index")
     474 | -        self.stop_node(0)
     475 | +        self.restart_node(0, extra_args=["-prune=550", "-blockfilterindex=1"])
    


    l0rinc commented at 1:34 AM on September 9, 2026:

    6d7caa1 indexes: set prune lock to genesis before first block:

    We're changing the test conditions in the fix commit, so it's not obvious what the behavior was before the fix. Can you adjust the characterization commit so that ideally only the assertions change while the conditions stay the same (so that we're actually exercising the same use case)?


    andrewtoth commented at 1:38 AM on September 9, 2026:

    it's not obvious what the behavior was before the fix.

    What specifically is not obvious about it? I'd prefer to leave it like this.


    l0rinc commented at 1:43 AM on September 9, 2026:

    you're changing the conditions, this is a different test, so you haven't characterized the behavior before the fix


    andrewtoth commented at 1:44 AM on September 9, 2026:

    But which part is not obvious to you?


mzumsande

Milestone
32.0


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-09-09 07:56 UTC