fix(pruner): preserve genesis in kv store during prune-block#139
Merged
viaweb3 merged 4 commits intoApr 21, 2026
Merged
Conversation
The original prune-block implementation calls TruncateTail(newTail)
without first copying the genesis block to the kv store. Since the
freezer's truncate hides every item below newTail (including block 0),
a pruned node fails to restart with:
Fatal: Failed to register the Ethereum service:
failed to retrieve genesis from ancient out of bounds
Fix by caching canonical hash, header, body, and total difficulty for
block 0 before truncation, and writing them back to the kv tables after
truncation. The kv-store path is the natural fallback for rawdb readers,
so subsequent ReadBlock(hash, 0) calls from geth startup succeed.
Receipts are omitted because the genesis block has no transactions.
Change is idempotent: re-running the pruner with the same dataset simply
re-writes identical keys. Verified via go build + go vet.
Reflects the genesis-preservation fix to snapshot prune-block introduced in commit 0cd3072 on this branch. Marked -unstable since this has not been tagged as a formal release.
After snapshot prune-block truncates the freezer tail past block 0,
the ancient store can no longer return genesis. The startup consistency
check in NewDatabaseWithFreezer tried to read chainFreezerHashTable at
position 0 unconditionally and failed with:
Fatal: Failed to register the Ethereum service:
failed to retrieve genesis from ancient out of bounds
Guard the cross-check with tail == 0. When the tail has moved, the
kv-store copy written by prune-block is the authoritative source;
no mismatch is possible because prune-block copies verbatim bytes
read from ancient moments before truncation.
dbHasLegacyReceipts scanned from firstIdx=0, calling db.Ancient(
"receipts", 0) on mainnets where the mainnet hash heuristic does not
apply (KCC NetworkId != 1). After snapshot prune-block advances the
freezer tail, any read below the tail fails with:
ERROR Failed to check db for legacy receipts err="out of bounds"
The check is non-fatal (logged as ERROR, startup continues), but the
noise obscures real issues and the check is effectively broken for
pruned nodes. Clamp firstIdx up to db.Tail() so the scan only covers
the accessible range. A pruned KCC mainnet is post-Byzantium for its
entire retained window anyway, so there cannot be any legacy receipts
in that range.
e7c6594 to
a1b7111
Compare
37ea7a4
into
kcc-community:claude/add-data-pruning-vgbsY
1 check passed
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.
The original prune-block implementation calls TruncateTail(newTail) without first copying the genesis block to the kv store. Since the freezer's truncate hides every item below newTail (including block 0), a pruned node fails to restart with:
Fix by caching canonical hash, header, body, and total difficulty for block 0 before truncation, and writing them back to the kv tables after truncation. The kv-store path is the natural fallback for rawdb readers, so subsequent ReadBlock(hash, 0) calls from geth startup succeed.
Receipts are omitted because the genesis block has no transactions.
Change is idempotent: re-running the pruner with the same dataset simply re-writes identical keys. Verified via go build + go vet.