core/rawdb: introduce freezer table for BALs

This commit is contained in:
Gary Rong 2026-05-15 13:25:09 +08:00
parent ff9c04e4c8
commit da877346c6
3 changed files with 44 additions and 5 deletions

View file

@ -614,9 +614,18 @@ func HasAccessList(db ethdb.Reader, hash common.Hash, number uint64) bool {
return has
}
// ReadAccessListRLP retrieves the RLP-encoded block access list for a block from KV.
// ReadAccessListRLP retrieves the RLP-encoded block access list for a block.
func ReadAccessListRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
data, _ := db.Get(accessListKey(number, hash))
var data []byte
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
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
})
return data
}
@ -759,6 +768,13 @@ func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *type
if err := op.Append(ChainFreezerReceiptTable, num, receipts); err != nil {
return fmt.Errorf("can't append block %d receipts: %v", num, err)
}
// The assumption is held that BAL of ancient block is no longer available
// (it may still reachable, but it's not worthwhile to even retrieve it
// from the network). A nil entry is stored in the BAL table as the absence
// placeholder.
if err := op.AppendRaw(ChainFreezerBALTable, num, nil); err != nil {
return fmt.Errorf("can't append block %d bals: %v", num, err)
}
return nil
}
@ -791,6 +807,7 @@ func DeleteBlock(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
DeleteReceipts(db, hash, number)
DeleteHeader(db, hash, number)
DeleteBody(db, hash, number)
DeleteAccessList(db, hash, number)
}
// DeleteBlockWithoutNumber removes all block data associated with a hash, except
@ -799,6 +816,7 @@ func DeleteBlockWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number
DeleteReceipts(db, hash, number)
deleteHeaderWithoutNumber(db, hash, number)
DeleteBody(db, hash, number)
DeleteAccessList(db, hash, number)
}
const badBlockToKeep = 10

View file

@ -35,6 +35,10 @@ const (
// ChainFreezerReceiptTable indicates the name of the freezer receipts table.
ChainFreezerReceiptTable = "receipts"
// ChainFreezerBALTable indicates the name of the freezer block access list
// table introduced by EIP-7928.
ChainFreezerBALTable = "bals"
)
// Identifiers of tail groups used by the chain freezer.
@ -43,6 +47,11 @@ const (
// receipt tables. The two tables are pruned together and therefore have
// the same tail position.
ChainFreezerBlockDataGroup = "blockdata"
// ChainFreezerBALGroup is the tail group for the block access list table.
// BAL is only populated after EIP-7928 activates, so it generally has a
// higher tail than the block-data group and is pruned independently.
ChainFreezerBALGroup = "bal"
)
// chainFreezerTableConfigs configures the settings for tables in the chain freezer.
@ -54,6 +63,7 @@ var chainFreezerTableConfigs = map[string]freezerTableConfig{
ChainFreezerHashTable: {noSnappy: true},
ChainFreezerBodiesTable: {noSnappy: false, tailGroup: ChainFreezerBlockDataGroup},
ChainFreezerReceiptTable: {noSnappy: false, tailGroup: ChainFreezerBlockDataGroup},
ChainFreezerBALTable: {noSnappy: false, tailGroup: ChainFreezerBALGroup},
}
// freezerTableConfig contains the settings for a freezer table.

View file

@ -45,9 +45,7 @@ const (
// key-value database to flat files for saving space on live database.
type chainFreezer struct {
ancients ethdb.AncientStore // Ancient store for storing cold chain segment
// Optional Era database used as a backup for the pruned chain.
eradb *eradb.Store
eradb *eradb.Store // Optional Era database used as a backup for the pruned chain
quit chan struct{}
wg sync.WaitGroup
@ -327,6 +325,16 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash
if len(receipts) == 0 {
return fmt.Errorf("block receipts missing, can't freeze block %d", number)
}
// An empty block access list is allowed and may occur in multiple
// scenarios, such as:
// - pre-Amsterdam blocks
// - post-Amsterdam blocks with the BAL absent (e.g. pruned by network)
// - post-Amsterdam blocks with an explicitly empty BAL
//
// In these cases, a nil entry will be stored in the BAL table as the
// absence placeholder.
bals := ReadAccessListRLP(nfdb, hash, number)
// Write to the batch.
if err := op.AppendRaw(ChainFreezerHashTable, number, hash[:]); err != nil {
return fmt.Errorf("can't write hash to Freezer: %v", err)
@ -340,6 +348,9 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash
if err := op.AppendRaw(ChainFreezerReceiptTable, number, receipts); err != nil {
return fmt.Errorf("can't write receipts to Freezer: %v", err)
}
if err := op.AppendRaw(ChainFreezerBALTable, number, bals); err != nil {
return fmt.Errorf("can't write bals to Freezer: %v", err)
}
hashes = append(hashes, hash)
}
return nil