Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion db/rawdb/accessors_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,10 @@ func ReadBlockAccessListBytes(db kv.Getter, hash common.Hash, number uint64) ([]
return data, nil
}

// WriteBlockAccessListBytes stores the RLP-encoded block access list sidecar for a block.
// WriteBlockAccessListBytes stores the RLP-encoded block access list sidecar for
// a block. This is secondary storage (serving, backfill, unwind); the primary
// carry into execution is Block.BlockAccessList(), written via the block overlay
// in InsertBlocks and flushed at commit.
func WriteBlockAccessListBytes(db kv.Putter, hash common.Hash, number uint64, data []byte) error {
if err := db.Put(kv.BlockAccessList, dbutils.BlockBodyKey(number, hash), data); err != nil {
return fmt.Errorf("failed to store block access list: %w", err)
Expand Down Expand Up @@ -807,6 +810,13 @@ func ReadBlock(tx kv.Getter, hash common.Hash, number uint64) *types.Block {
return nil
}
block := types.NewBlockFromStorage(hash, header, body.Transactions, body.Uncles, body.Withdrawals)
// Carry the BAL sidecar (secondary storage) so a block reconstructed from the
// DB carries its BAL like its header/body. Only Amsterdam+ blocks have one.
if header.BlockAccessListHash != nil {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

header.HasBAL()

if bal, err := ReadBlockAccessListBytes(tx, hash, number); err == nil && len(bal) > 0 {
block.SetBlockAccessList(bal)
}
}
return block
}

Expand Down
4 changes: 4 additions & 0 deletions execution/engineapi/engine_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,10 @@ func (s *EngineServer) newPayload(ctx context.Context, req *engine_types.Executi
// via rlp.EncodeToBytes. Both slices reference the same underlying
// byte buffers from req.Transactions.
block := types.NewBlockFromStorageWithBinaryTxs(blockHash, &header, transactions, txs, nil /* uncles */, withdrawals)
// Carry the payload's BAL on the block so execution consumes it from the
// in-memory payload; InsertBlocks still stores it in the overlay (flushed at
// commit) as secondary storage.
block.SetBlockAccessList(blockAccessListBytes)
payloadStatus, err := s.HandleNewPayload(ctx, "NewPayload", block, expectedBlobHashes, blockAccessListBytes)
if err != nil {
if errors.Is(err, rules.ErrInvalidBlock) {
Expand Down
19 changes: 13 additions & 6 deletions execution/stagedsync/exec3.go
Original file line number Diff line number Diff line change
Expand Up @@ -612,12 +612,19 @@ func (te *txExecutor) executeBlocks(ctx context.Context, startBlockNum uint64, m
go warmTxsHashes(b)

var dbBAL types.BlockAccessList
// Read BAL through blockTx (overlay or execRoTx) — do NOT open
// a separate db.View() as it can deadlock with the stageloop's
// RW transaction when BlockOverlay is active.
data, err := rawdb.ReadBlockAccessListBytes(blockTx, b.Hash(), blockNum)
if err != nil {
return err
// Prefer the BAL carried on the block (the payload) — the newPayload /
// backward-sync paths attach it, so no read is needed. Fall back to the
// BAL sidecar in the DB (via blockTx: overlay or execRoTx) for blocks
// that don't carry it (snapshot / forward-sync); do NOT open a separate
// db.View() as it can deadlock with the stageloop's RW transaction when
// BlockOverlay is active. ProcessBAL still computes+validates the BAL
// from the write-set as the ultimate fallback.
data := b.BlockAccessList()
if len(data) == 0 {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if len(data) == 0 && header.HasBAL() to save an unnecessary call to DB

data, err = rawdb.ReadBlockAccessListBytes(blockTx, b.Hash(), blockNum)
if err != nil {
return err
}
}
if len(data) > 0 && !dbg.IgnoreBAL {
dbBAL, err = types.DecodeBlockAccessListBytes(data)
Expand Down
33 changes: 25 additions & 8 deletions execution/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,7 @@ func (r RawBlock) AsBlock() (*Block, error) {
}
}
b.transactions = txs
b.blockAccessList = r.BlockAccessList

return b, nil
}
Expand All @@ -819,6 +820,12 @@ type Block struct {
transactions Transactions
withdrawals []*Withdrawal

// blockAccessList is the RLP-encoded EIP-7928 Block Access List sidecar
// carried with the payload (nil pre-Amsterdam). It is NOT part of the block's
// RLP/consensus encoding or hash — never add it to EncodeRLP/DecodeRLP/
// payloadSize. The header's BlockAccessListHash is the consensus commitment.
blockAccessList []byte

// binaryTransactions optionally caches the transactions' encodings (e.g. from
// an engine_newPayload payload) so RawBody() can skip re-encoding them.
binaryTransactions BinaryTransactions
Expand Down Expand Up @@ -1380,6 +1387,14 @@ func (b *Block) ParentBeaconBlockRoot() *common.Hash { return b.header.ParentBea
func (b *Block) RequestsHash() *common.Hash { return b.header.RequestsHash }
func (b *Block) BlockAccessListHash() *common.Hash { return b.header.BlockAccessListHash }

// BlockAccessList returns the RLP-encoded EIP-7928 BAL sidecar carried with the
// payload (nil when absent). It is not part of the block's RLP encoding or hash.
func (b *Block) BlockAccessList() []byte { return b.blockAccessList }

// SetBlockAccessList attaches the RLP-encoded BAL sidecar to the block, copying
// the input so a transaction-owned or later-mutated source cannot alias it.
func (b *Block) SetBlockAccessList(bal []byte) { b.blockAccessList = bytes.Clone(bal) }

// Header returns a deep-copy of the entire block header using CopyHeader()
func (b *Block) Header() *Header { return CopyHeader(b.header) }
func (b *Block) HeaderNoCopy() *Header { return b.header }
Expand Down Expand Up @@ -1540,10 +1555,11 @@ func (b *Block) Copy() *Block {
}

newB := &Block{
header: CopyHeader(b.header),
uncles: uncles,
transactions: CopyTxs(b.transactions),
withdrawals: withdrawals,
header: CopyHeader(b.header),
uncles: uncles,
transactions: CopyTxs(b.transactions),
withdrawals: withdrawals,
blockAccessList: bytes.Clone(b.blockAccessList),
}
szCopy := b.size.Load()
newB.size.Store(szCopy)
Expand All @@ -1557,10 +1573,11 @@ func (b *Block) WithSeal(header *Header) *Block {
headerCopy.mutable = false
headerCopy.hash.Store(nil) // invalidate cached hash
return &Block{
header: headerCopy,
transactions: b.transactions,
uncles: b.uncles,
withdrawals: b.withdrawals,
header: headerCopy,
transactions: b.transactions,
uncles: b.uncles,
withdrawals: b.withdrawals,
blockAccessList: b.blockAccessList,
}
}

Expand Down
32 changes: 32 additions & 0 deletions execution/types/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,38 @@ func TestBlockEncoding(t *testing.T) {
}
}

// TestBlockAccessListNotInEncoding pins the invariant that the BAL sidecar is
// carried out-of-band: it must never enter the block's RLP encoding or hash.
func TestBlockAccessListNotInEncoding(t *testing.T) {
t.Parallel()
blockEnc := common.FromHex("f90260f901f9a083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1c0")
var block Block
if err := rlp.DecodeBytes(blockEnc, &block); err != nil {
t.Fatal("decode error: ", err)
}

hashBefore := block.Hash()
block.SetBlockAccessList([]byte{0x01, 0x02, 0x03})
if got := block.Hash(); got != hashBefore {
t.Errorf("BAL changed block hash: got %x want %x", got, hashBefore)
}
enc, err := rlp.EncodeToBytes(&block)
if err != nil {
t.Fatal("encode error: ", err)
}
if !bytes.Equal(enc, blockEnc) {
t.Errorf("BAL leaked into block RLP:\ngot: %x\nwant: %x", enc, blockEnc)
}

var decoded Block
if err := rlp.DecodeBytes(enc, &decoded); err != nil {
t.Fatal("decode error: ", err)
}
if decoded.BlockAccessList() != nil {
t.Errorf("BAL survived RLP round-trip (must be a non-encoded sidecar): %x", decoded.BlockAccessList())
}
}

func TestEIP1559BlockEncoding(t *testing.T) {
t.Parallel()
blockEnc := common.FromHex("f9030bf901fea083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4843b9aca00f90106f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1b8a302f8a0018080843b9aca008301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8c0")
Expand Down
Loading