core/rawdb: check pruning tail in HasBody and HasReceipts (#33747)

### 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.
This commit is contained in:
Forostovec 2026-02-06 14:10:30 +02:00 committed by GitHub
parent 9967fb7c5c
commit e64c8d8e26
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

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