core/rawdb: verify canonical hash before reading ancient BAL (#35471)
Some checks are pending
/ Keeper Build (push) Waiting to run
/ Windows Build (push) Waiting to run
/ Docker Image (push) Waiting to run
/ Linux Build (arm) (push) Waiting to run
/ Linux Build (push) Waiting to run

This commit is contained in:
0xoasis 2026-08-08 21:14:04 +08:00 committed by GitHub
parent 7e520c4310
commit cae76d5a3c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 53 additions and 4 deletions

View file

@ -618,11 +618,14 @@ func HasAccessList(db ethdb.Reader, hash common.Hash, number uint64) bool {
func ReadAccessListRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
var data []byte
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
data, _ = reader.Ancient(ChainFreezerBALTable, number)
if len(data) > 0 {
return nil
// Ancients maintain only canonical data. For a non-canonical hash at an
// ancient height, fall back to the key-value store.
if isCanon(reader, number, hash) {
data, _ = reader.Ancient(ChainFreezerBALTable, number)
if len(data) > 0 {
return nil
}
}
// Block is not in ancients, read from key-value store by hash and number.
data, _ = db.Get(accessListKey(number, hash))
return nil
})

View file

@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/core/types/bal"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/keccak"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
@ -967,6 +968,51 @@ func TestWriteAncientBlocksNilBAL(t *testing.T) {
}
}
func TestReadAccessListRLPAncientNonCanonical(t *testing.T) {
db, err := Open(NewMemoryDatabase(), OpenOptions{Ancient: t.TempDir()})
if err != nil {
t.Fatalf("failed to create database with ancient backend: %v", err)
}
defer db.Close()
encoded, accessList := makeTestBAL(t)
accessListHash := accessList.Hash()
block := types.NewBlockWithHeader(&types.Header{
Number: big.NewInt(0),
Extra: []byte("canonical-bal block"),
UncleHash: types.EmptyUncleHash,
TxHash: types.EmptyTxsHash,
ReceiptHash: types.EmptyReceiptsHash,
BlockAccessListHash: &accessListHash,
}).WithAccessList(accessList)
if _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
if err := op.AppendRaw(ChainFreezerHashTable, 0, block.Hash().Bytes()); err != nil {
return err
}
if err := op.Append(ChainFreezerHeaderTable, 0, block.Header()); err != nil {
return err
}
if err := op.Append(ChainFreezerBodiesTable, 0, block.Body()); err != nil {
return err
}
if err := op.AppendRaw(ChainFreezerReceiptTable, 0, types.EncodeBlockReceiptLists([]types.Receipts{nil})[0]); err != nil {
return err
}
return op.AppendRaw(ChainFreezerBALTable, 0, encoded)
}); err != nil {
t.Fatalf("failed to write ancient block: %v", err)
}
if blob := ReadAccessListRLP(db, block.Hash(), block.NumberU64()); len(blob) == 0 {
t.Fatal("canonical block access list not found in ancients")
}
// Ancients contain data only for the canonical hash. Looking up another
// hash at the same height must not return the canonical block's access list.
nonCanonical := common.HexToHash("0xdeadbeef")
if blob := ReadAccessListRLP(db, nonCanonical, block.NumberU64()); len(blob) != 0 {
t.Fatalf("ReadAccessListRLP returned canonical data for non-canonical hash: %x", blob)
}
}
// TestBALStorage tests write/read/delete of BALs in the KV store.
func TestBALStorage(t *testing.T) {
db := NewMemoryDatabase()