Skip to content

Block access list changes - BAL construction, execution and validation #32263

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 29 commits into
base: master
Choose a base branch
from

Conversation

jwasinger
Copy link
Contributor

@jwasinger jwasinger commented Jul 23, 2025

WIP. currently passes blockchain spec tests (tests that make use of balances >16 bytes are excluded).

Change summary:

  • when --experimentalbal is enabled:
    • insertion of post-Cancun blocks which don't contain access lists will cause them to be created during block execution and embedded into the block body.
    • Insertion of post-cancun blocks that contain access lists will perform transaction execution and post-state root calculation in parallel using the state diffs provided in the access list. The state diffs produced by execution are validated against the entries in the BAL.
  • Introduced new modified blockchain suite test runner which first imports the test chain (generating BALs), and then re-inserts the access-list-containing blocks.

Deviations from EIP-7928:

  • Use RLP for the encoding format instead of SSZ.
  • Modify BAL format to accomodate pre/post-tx execution state changes:
    • entries with txindex=0 correspond to state reads/changes which occur from system contract execution before transactions.
    • entries with txindx=1..len(block.transactions) correspond to state reads/changes occurring for each transaction.
    • entries with txindex=len(block.transactions)+1 correspond to the post-block system contracts and EIP-4895 withdrawals.

TODO:

  • validation of BAL account/storage reads (it's somewhat likely that these will be removed from the spec, so I'm holding off for now).

@jwasinger
Copy link
Contributor Author

A lot of the comments in the code are straight-up wrong/misleading. There's a lot of notes/reminders I made as I was implementing this, and not all of them have been removed/corrected at this point.

@jwasinger
Copy link
Contributor Author

jwasinger commented Jul 29, 2025

I've pushed parallel execution changes here.

Still failing 2 tests (other than modexp repricing ones which will be fixed with a rebase on master):

--- FAIL: TestExecutionSpecBlocktestsBAL/prague/eip7251_consolidations/consolidations_during_fork/consolidation_requests_during_fork.json (0.03s)
        block_test.go:233: test with config {snapshotter:false, scheme:path} failed: mismatch between BAl value and computed value
    --- FAIL: TestExecutionSpecBlocktestsBAL/prague/eip7002_el_triggerable_withdrawals/withdrawal_requests_during_fork/withdrawal_requests_during_fork.json (0.02s)
        block_test.go:233: test with config {snapshotter:false, scheme:path} failed: mismatch between BAl value and computed value

I've been trying to debug these but it's proving to be exceedingly difficult with the parallel execution enabled. The tests seem to relate to behavior of some system contracts on the fork boundaries, so I will just proceed with gathering numbers on mainnet performance for the meantime.

@jwasinger jwasinger force-pushed the bal-execution branch 2 times, most recently from ab1c562 to 8f200db Compare August 4, 2025 08:51
@jwasinger jwasinger marked this pull request as ready for review August 4, 2025 10:13
@jwasinger
Copy link
Contributor Author

INFO [08-04|13:21:15.227] Imported new chain segment number=23,051,616 hash=228379..13abd2 blocks=0 txs=10783 mgas=1137.280 elapsed=8.072s mgasps=140.878 age=2d6h46m triediffs=271.84MiB triedirty=112.48MiB

Broke the block count on the insertion log statement here. Will try to fix tomorrow.

@jwasinger
Copy link
Contributor Author

Getting some empty accounts in the BAL. example:

 {
      address: "0xff00000000000000000000000000000000008453"
  }

rjl493456442 and others added 14 commits August 15, 2025 11:40
…hen enabled, post-Cancun blocks which lack access lists will have them constructed on execution during import. When importing blocks which contain access lists, transaction execution and state root calculation is performed in parallel.
…ved from BALs, there's not much gain from having a BAL state prefetcher
…ulation in statedb across multiple calls to Finalise. instead, Finalise now returns the diff that occurred from that call
Copy link
Member

@lightclient lightclient left a comment

Choose a reason for hiding this comment

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

Looking good so far, just a few comments.

var resCh chan *ProcessResultWithMetrics
resCh, err = bc.processor.ProcessWithAccessList(block, statedb, bc.cfg.VmConfig)
if err != nil {
// TODO: okay to pass nil here as execution result?
Copy link
Member

Choose a reason for hiding this comment

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

Should be fine, we just use it to get out the receipts (if it is non nil).

// Process block using the parent state as reference point
pstart := time.Now()
var resCh chan *ProcessResultWithMetrics
resCh, err = bc.processor.ProcessWithAccessList(block, statedb, bc.cfg.VmConfig)
Copy link
Member

Choose a reason for hiding this comment

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

Why does this need to return a channel? Seems like it could be a blocking operation. Also I would probably do the branching for access list vs. not in the Process function instead of here since it is really more related to the processing of the block than the broader chain. It should also simplify the changes a bit since both branches of this if are quite similar, especially if you then roll the checks from ValidateProcessResult into ValidateState.

return prev
}

func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
s.code = code
s.data.CodeHash = codeHash[:]
}

// setCode sets the code and hash and dirty markers.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// setCode sets the code and hash and dirty markers.
// setCodeModified sets the code and hash and dirty markers.

// performs post-tx state transition (system contracts and withdrawals)
// and calculates the ProcessResult, returning it to be sent on resCh
// by resultHandler
prepareExecResult := func(postTxState *state.StateDB, receipts types.Receipts) *ProcessResult {
Copy link
Member

Choose a reason for hiding this comment

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

It's fairly hard to follow the logic with these larger function variables. I think it's reasonable to separate this logic into separate methods or create a new object the focuses solely on validating blocks with access lists.

CancunTime *uint64 `json:"cancunTime,omitempty"` // Cancun switch time (nil = no fork, 0 = already on cancun)
PragueTime *uint64 `json:"pragueTime,omitempty"` // Prague switch time (nil = no fork, 0 = already on prague)
OsakaTime *uint64 `json:"osakaTime,omitempty"` // Osaka switch time (nil = no fork, 0 = already on osaka)
GlamsterdamTime *uint64 `json:"glamsterdamTime,omitempty"` // Glamsterdam switch time (nil = no fork, 0 = already on glamsterdam)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
GlamsterdamTime *uint64 `json:"glamsterdamTime,omitempty"` // Glamsterdam switch time (nil = no fork, 0 = already on glamsterdam)
AmsterdamTime *uint64 `json:"amsterdamTime,omitempty"` // Aamsterdam switch time (nil = no fork, 0 = already on Amsterdam)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants