From 1b9666962f5898fbac6418237e095032befb76e4 Mon Sep 17 00:00:00 2001 From: jonny rhea <5555162+jrhea@users.noreply.github.com> Date: Fri, 20 Mar 2026 13:17:40 -0500 Subject: [PATCH] core/rawdb: add BAL storage layer --- core/rawdb/accessors_chain.go | 86 +++++++++++++++++++++++++++++++++++ core/rawdb/ancient_scheme.go | 4 ++ core/rawdb/schema.go | 6 +++ 3 files changed, 96 insertions(+) diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 6ae64fb2fd..a67db4bac1 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/types/bal" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" @@ -605,6 +606,91 @@ func DeleteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { } } +// HasBAL verifies the existence of a block access list for a block. +func HasBAL(db ethdb.Reader, hash common.Hash, number uint64) bool { + if isCanon(db, number, hash) { + return true + } + if has, err := db.Has(balKey(number, hash)); !has || err != nil { + return false + } + return true +} + +// ReadBALRLP retrieves the RLP-encoded block access list for a block. +func ReadBALRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue { + var data []byte + db.ReadAncients(func(reader ethdb.AncientReaderOp) error { + if isCanon(reader, number, hash) { + data, _ = reader.Ancient(ChainFreezerBALTable, number) + return nil + } + data, _ = db.Get(balKey(number, hash)) + return nil + }) + return data +} + +// ReadCanonicalBALRLP retrieves the BAL RLP for the canonical block at number. +// Optionally takes the block hash to avoid looking it up. +func ReadCanonicalBALRLP(db ethdb.Reader, number uint64, hash *common.Hash) rlp.RawValue { + var data []byte + db.ReadAncients(func(reader ethdb.AncientReaderOp) error { + data, _ = reader.Ancient(ChainFreezerBALTable, number) + if len(data) > 0 { + return nil + } + // BAL is not in ancients, read from db by hash and number. + if hash != nil { + data, _ = db.Get(balKey(number, *hash)) + } else { + hashBytes, _ := db.Get(headerHashKey(number)) + data, _ = db.Get(balKey(number, common.BytesToHash(hashBytes))) + } + return nil + }) + return data +} + +// ReadBAL retrieves and decodes the block access list for a block. +func ReadBAL(db ethdb.Reader, hash common.Hash, number uint64) *bal.BlockAccessList { + data := ReadBALRLP(db, hash, number) + if len(data) == 0 { + return nil + } + b := new(bal.BlockAccessList) + if err := rlp.DecodeBytes(data, b); err != nil { + log.Error("Invalid BAL RLP", "hash", hash, "err", err) + return nil + } + return b +} + +// WriteBAL RLP-encodes and stores a block access list in the active KV store. +func WriteBAL(db ethdb.KeyValueWriter, hash common.Hash, number uint64, b *bal.BlockAccessList) { + bytes, err := rlp.EncodeToBytes(b) + if err != nil { + log.Crit("Failed to encode BAL", "err", err) + } + if err := db.Put(balKey(number, hash), bytes); err != nil { + log.Crit("Failed to store BAL", "err", err) + } +} + +// WriteBALRLP stores a pre-encoded block access list in the active KV store. +func WriteBALRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, encoded rlp.RawValue) { + if err := db.Put(balKey(number, hash), encoded); err != nil { + log.Crit("Failed to store BAL", "err", err) + } +} + +// DeleteBAL removes a block access list from the active KV store. +func DeleteBAL(db ethdb.KeyValueWriter, hash common.Hash, number uint64) { + if err := db.Delete(balKey(number, hash)); err != nil { + log.Crit("Failed to delete BAL", "err", err) + } +} + // ReceiptLogs is a barebone version of ReceiptForStorage which only keeps // the list of logs. When decoding a stored receipt into this object we // avoid creating the bloom filter. diff --git a/core/rawdb/ancient_scheme.go b/core/rawdb/ancient_scheme.go index afec7848c8..0fd5435082 100644 --- a/core/rawdb/ancient_scheme.go +++ b/core/rawdb/ancient_scheme.go @@ -35,6 +35,9 @@ const ( // ChainFreezerReceiptTable indicates the name of the freezer receipts table. ChainFreezerReceiptTable = "receipts" + + // ChainFreezerBALTable indicates the name of the freezer block access list table. + ChainFreezerBALTable = "bals" ) // chainFreezerTableConfigs configures the settings for tables in the chain freezer. @@ -46,6 +49,7 @@ var chainFreezerTableConfigs = map[string]freezerTableConfig{ ChainFreezerHashTable: {noSnappy: true, prunable: false}, ChainFreezerBodiesTable: {noSnappy: false, prunable: true}, ChainFreezerReceiptTable: {noSnappy: false, prunable: true}, + ChainFreezerBALTable: {noSnappy: false, prunable: true}, } // freezerTableConfig contains the settings for a freezer table. diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index d9140c5fd6..6aa07220d4 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -112,6 +112,7 @@ var ( blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts + balPrefix = []byte("j") // balPrefix + num (uint64 big endian) + hash -> block access list txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits @@ -214,6 +215,11 @@ func blockReceiptsKey(number uint64, hash common.Hash) []byte { return append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...) } +// balKey = balPrefix + num (uint64 big endian) + hash +func balKey(number uint64, hash common.Hash) []byte { + return append(append(balPrefix, encodeBlockNumber(number)...), hash.Bytes()...) +} + // txLookupKey = txLookupPrefix + hash func txLookupKey(hash common.Hash) []byte { return append(txLookupPrefix, hash.Bytes()...)