From e64c8d8e2651233da3b3de7ab8bd6a756288293a Mon Sep 17 00:00:00 2001 From: Forostovec Date: Fri, 6 Feb 2026 14:10:30 +0200 Subject: [PATCH] core/rawdb: check pruning tail in HasBody and HasReceipts (#33747) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Problem `HasBody` and `HasReceipts` returned `true` for pruned blocks because they only checked `isCanon()` which verifies the hash table — but hash/header tables have `prunable: false` while body/receipt tables have `prunable: true`. After `TruncateTail()`, hashes still exist but bodies/receipts are gone. This caused inconsistency: `HasBody()` returns `true`, but `ReadBody()` returns `nil`. ### Changes Both functions now check `db.Tail()` when the block is in ancient store. If `number < tail`, the data has been pruned and the function correctly returns `false`. This aligns `HasBody`/`HasReceipts` behavior with `ReadBody`/`ReadReceipts` and fixes potential issues in `skeleton.linked()` which relies on these checks during sync. --- core/rawdb/accessors_chain.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 6ae64fb2fd..14308dd698 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -424,7 +424,13 @@ func WriteBodyRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rlp // HasBody verifies the existence of a block body corresponding to the hash. func HasBody(db ethdb.Reader, hash common.Hash, number uint64) bool { if isCanon(db, number, hash) { - return true + // Block is in ancient store, but bodies can be pruned. + // Check if the block number is above the pruning tail. + tail, _ := db.Tail() + if number >= tail { + return true + } + return false } if has, err := db.Has(blockBodyKey(number, hash)); !has || err != nil { return false @@ -466,7 +472,13 @@ func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { // to a block. func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool { if isCanon(db, number, hash) { - return true + // Block is in ancient store, but receipts can be pruned. + // Check if the block number is above the pruning tail. + tail, _ := db.Tail() + if number >= tail { + return true + } + return false } if has, err := db.Has(blockReceiptsKey(number, hash)); !has || err != nil { return false