diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 987b8df392..08e4cc5713 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -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 diff --git a/core/rawdb/ancient_scheme.go b/core/rawdb/ancient_scheme.go index 15bcf8be60..d6097a9193 100644 --- a/core/rawdb/ancient_scheme.go +++ b/core/rawdb/ancient_scheme.go @@ -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. diff --git a/core/rawdb/chain_freezer.go b/core/rawdb/chain_freezer.go index c47ddebf8c..b706f9132c 100644 --- a/core/rawdb/chain_freezer.go +++ b/core/rawdb/chain_freezer.go @@ -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