execution: treat empty BAL as valid access list (EIP-7928)#20776
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates Erigon’s execution/engine API and storage paths to treat an empty Block Access List (BAL) as a present and valid sidecar (per EIP-7928), ensuring it round-trips as canonical RLP 0xc0 rather than being dropped as “missing”.
Changes:
- Persist BAL sidecars even when the BAL is empty (based on the empty BAL commitment hash).
- Serve canonical empty BAL bytes (
0xc0) when legacy data stored the empty hash but omitted the sidecar bytes. - Update engine payload assembly/newPayload handling and add a test to ensure empty BAL round-trips through getPayload.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
execution/execmodule/inserters.go |
Persists BAL sidecar when the header commitment indicates an empty BAL, normalizing empty bytes to canonical 0xc0. |
execution/execmodule/getters.go |
Adds BAL read helper that recovers canonical empty BAL bytes when legacy inserts stored only the empty-hash commitment. |
execution/engineapi/engine_server.go |
Normalizes empty BAL input to canonical bytes in newPayload, and always encodes BAL in payload responses when a commitment exists. |
execution/engineapi/engine_server_getpayload_test.go |
Adds test asserting payload responses include canonical empty BAL bytes when header carries the empty commitment hash. |
Comments suppressed due to low confidence (2)
execution/engineapi/engine_server.go:1079
assembledBlockToPayloadResponsenow encodes and returns a BAL wheneverheader.BlockAccessListHashis non-nil, even ifbr.BlockAccessListis nil. If a non-empty BAL commitment is present but the in-memoryBlockAccessListis missing (nil), this will incorrectly return the canonical empty bytes0xc0, causing a commitment mismatch for the payload. Consider only encoding whenbr.BlockAccessListis non-nil/len>0, or when the header commitment equalsempty.BlockAccessListHash(the one case where nil/empty is still valid).
if header.BlockAccessListHash != nil {
// NOTE(EIP-7928): an empty BAL still round-trips as the RLP bytes 0xc0, so
// we encode even when br.BlockAccessList is nil/empty as long as the header
// carries a BAL commitment.
encoded, encErr := types.EncodeBlockAccessListBytes(br.BlockAccessList)
if encErr == nil {
ep.BlockAccessList = encoded
}
execution/execmodule/inserters.go:129
- The BAL sidecar write is still skipped when
header.BlockAccessListHashis non-nil/non-empty butblock.BlockAccessListis empty. That silently persists a header commitment without the corresponding sidecar bytes (similar to the empty-BAL issue, but for non-empty commitments). Consider explicitly erroring whenheader.BlockAccessListHash != nilandlen(block.BlockAccessList)==0unless the hash equalsempty.BlockAccessListHash(in which case you can normalize to0xc0).
if len(block.BlockAccessList) > 0 || (header.BlockAccessListHash != nil && *header.BlockAccessListHash == empty.BlockAccessListHash) {
if header.BlockAccessListHash == nil {
return 0, fmt.Errorf("ethereumExecutionModule.InsertBlocks: block access list provided without hash for block %d", height)
}
balBytes := block.BlockAccessList
if len(balBytes) == 0 {
balBytes, err = types.EncodeBlockAccessListBytes(nil)
if err != nil {
return 0, fmt.Errorf("ethereumExecutionModule.InsertBlocks: encode empty block access list, block %d: %s", height, err)
}
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
acb4fad to
20b6db3
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
execution/engineapi/engine_server.go:1072
assembledBlockToPayloadResponsesilently dropsBlockAccessListifEncodeBlockAccessListBytesreturns an error (it just leaves the field unset). For Gloas/Amsterdam+ payloads whereBlockAccessListHashis present, returning a payload withoutblockAccessListcan violate the Engine API expectations and makes debugging harder. Consider propagating the encoding error (return it from this function) or at least logging and failing the request when encoding/validation fails, since it indicates an inconsistent block state.
if header.BlockAccessListHash != nil {
// EIP-7928: encode even when br.BlockAccessList is nil/empty — an empty
// BAL serializes to 0xc0 via standard RLP rules.
encoded, encErr := types.EncodeBlockAccessListBytes(br.BlockAccessList)
if encErr == nil {
ep.BlockAccessList = encoded
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
20b6db3 to
a5121ec
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
0388a10 to
3142328
Compare
…h nil-vs-empty BAL semantics
3142328 to
613d4d6
Compare
This update aligns BAL handling with the requirements described in EIP-7928, which requires that even an empty BAL be recognized as a legitimate access list. According to the specification, the header should include the
rlp.encode([])hash, while the payload should have the standard empty RLP list (0xc0). Previously, even though the header hash was established correctly, the BAL bytes themselves were not saved. This effectively regarded an empty BAL as if it did not exist, contradicting the desired behavior indicated in the EIP.With this change, empty BALs are now handled consistently like any other BAL. The RLP decoder now returns an initialized empty slice instead of nil, so
0xc0naturally round-trips through the standard encode/decode path without special-case logic. Thelen(block.BlockAccessList) > 0guard in the DB inserter correctly persists the[]byte{0xc0}sidecar (length 1), and the engine API response encodes it back via standard RLP rules. The payload response path also retains thebr.BlockAccessList != nilguard so that a genuinely missing BAL is never silently emitted as0xc0. This removes any ambiguity between a valid empty BAL and cases where BAL data is missing or pruned.