Repository navigation
execution: system calls do not touch SystemAddress, drop its BAL filter - #24504
Conversation
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The remaining witness filter can omit SystemAddress proofs required for zero-amount withdrawal replay.
Review effort: Balanced
Findings: 1
Open (2)
What changed in this PR
Updates EIP-7928 handling so system-call callers are not treated as state accesses, while preserving AuRa behavior.
Changes:
- Skip caller touches for non-AuRa zero-value system calls.
- Remove the SystemAddress-specific BAL filter.
- Add BAL regressions for withdrawals and EIP-7702 delegation.
| File | Description |
|---|---|
rpc/jsonrpc/eth_call.go |
Updates SystemAddress witness rationale. |
execution/vm/runtime/runtime_test.go |
Tests standard and AuRa system calls. |
execution/vm/instructions.go |
Generalizes BALANCE documentation. |
execution/vm/evm.go |
Restricts caller touching to AuRa. |
execution/state/versionedio.go |
Removes the SystemAddress BAL exception. |
execution/state/versionedio_test.go |
Tests ordinary BAL treatment. |
execution/state/intra_block_state.go |
Clarifies AuRa touch behavior. |
execution/engineapi/engine_api_bal_test.go |
Adds withdrawal and delegation regressions. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| // On AuRa the stateless verifier navigates the system address (system-call msg.sender), | ||
| // so its path must be in the witness. |
| // On AuRa the stateless verifier navigates the system address (system-call msg.sender), | ||
| // so its path must be in the witness. | ||
| // collectAccessedState drops it per EIP-7928 — a witness-content rule for the | ||
| // debug_executionWitness format that does not apply to this op-stream witness. | ||
| accessed.Addresses[common.Address(params.SystemAddress.Value())] = struct{}{} |
|
also passes ethereum/execution-specs#3681 |
|
@chfast fyi looks like CI has some issues |
A zero-amount withdrawal to SystemAddress and a call to an EOA delegated (EIP-7702) to SystemAddress are state accesses, so by the EIP-7928 spec SystemAddress must be in the block access list. Both are omitted today.
Every zero-value system call touched its caller, which put SystemAddress into the block access list, and AsBlockAccessList then dropped it unless a user tx had a non-revertable access to it. That heuristic also dropped real accesses: a zero-amount withdrawal to SystemAddress and a delegation to it. By the EIP-7928 spec, being the caller of a system call is not a state access, so a system call no longer touches its caller (AuRa keeps the touch for its empty system account) and SystemAddress follows the same BAL rules as any other address. IntraBlockState.SubBalance still turns a zero debit of SystemAddress into a touch on every chain. Block execution never debits SystemAddress (every caller passes a recovered sender or a contract address), so only RPC calls from 0xff..fe (eth_call, eth_simulateV1, trace_call) reach it; it is left as is and its comment corrected.
4566a68 to
524670b
Compare
|
Hi, Erigon v3.7.1 (the Sepolia Glamsterdam release) doesn't include #24504. After the fork (Oct 6, 13:53 UTC), any Sepolia user can split it with one 7702 tx that delegates an EOA to 0xff…fe and calls it, because v3.7.1 leaves SYSTEM_ADDRESS out of the BAL. I tested it on the releases: v3.7.1 rejects the geth-built block (BAL mismatch), and reth 2.7.0, geth 1.17.7 and Nethermind 2.1.0 all reject the block v3.7.1 builds. With main (6922d4d), all four agree. The execution-specs #3681 test test_bal_7702_delegation_to_system_address fails on v3.7.1 and passes on main. Could #24504 be backported for a 3.7.2 before the fork? |
Reconciles the dependency-wait Block-STM executor and write-side validation onto current main, on top of Alex's #24456 (refold/write-only-moved-slots) which is the current branch HEAD. 23 conflicts resolved keeping the branch architecture and folding main's orthogonal, non-validation changes. Branch architecture kept (the substantive change): - dependency-wait scheduler: a worker parks mid-EVM on its read dependency and resumes, instead of aborting and re-queueing (exec3_parallel, runDepOrderValidation) - write-side validation: validity stamped at write time (valStatus, noValueRead) rather than re-derived on the read side main's orthogonal changes folded in: - domain_shared: no-ctx FlushPendingUpdates, kv.UnderlyingTx, InitBlockOverlay/NewMemoryBatch - state API drift for main's vm: ResolveCode -> accounts.Code, StopAccessRecording (#24555), asOfStateReader.BindsWorkerTx (#23722), trie.EmptyRoot -> empty.RootHash - FlushVersionedWrites kept 3-arg (branch), normalized tree-wide - #24504: drop the stale SystemAddress BAL filter in versionedio.AsBlockAccessList to match main's auto-merged evm.go (system calls no longer touch their caller); without this the auto-merged engineapi SystemAddress BAL tests fail Removed tests for branch-dropped code (WriteSet.Normalize/MergeInto, revival reader), main's committer-level prefetch test (#24479, branch wires prefetch differently), and the two obsolete SystemAddress-filter tests. ReadStamp reports !ok so the vm frame slot-cache (#24551) stays off: the dep-wait path parks mid-frame and a dependency can change without a journal bump, which a journal-stamped cache would miss. Deferred to follow-ups (main perf coupled to its read-side design): BAL branch-prefetch (#24479), vm frame slot-cache (#24551), committed-base/warm-read path.
Reconciles the dependency-wait Block-STM executor and write-side validation onto current main (46 commits since merge-base 48d3a16). 23 conflicts resolved keeping the branch architecture and folding main's orthogonal, non-validation changes. Branch architecture kept (the substantive change): - dependency-wait scheduler: a worker parks mid-EVM on its read dependency and resumes, instead of aborting and re-queueing (exec3_parallel, runDepOrderValidation) - write-side validation: validity stamped at write time (valStatus, noValueRead) rather than re-derived on the read side main's orthogonal changes folded in: - domain_shared: no-ctx FlushPendingUpdates, kv.UnderlyingTx, InitBlockOverlay/NewMemoryBatch - state API drift for main's vm: ResolveCode -> accounts.Code, StopAccessRecording (#24555), asOfStateReader.BindsWorkerTx (#23722), trie.EmptyRoot -> empty.RootHash - FlushVersionedWrites kept 3-arg (branch), normalized tree-wide - #24504: drop the stale SystemAddress BAL filter in versionedio.AsBlockAccessList to match main's auto-merged evm.go (system calls no longer touch their caller); without this the auto-merged engineapi SystemAddress BAL tests fail Removed tests for branch-dropped code (WriteSet.Normalize/MergeInto, revival reader), main's prefetch test (feature deferred), and the two obsolete SystemAddress-filter tests. Deferred to follow-ups (main perf coupled to its read-side design, not folded into this consensus merge): BAL branch-prefetch (#24479), vm frame slot-cache (#24551, ReadStamp reports !ok so the cache stays off), committed-base/warm-read path.
… (valueStatus) Reconciles the dependency-wait Block-STM executor + write-side validation onto current main, keeping the write-time valueStatus net-zero correctness mechanism (WriteCell.valStatus: ValueUnchanged is the only state skipped from the domain/commitment projection) rather than the consumer-side StoragesChanged re-derivation of #24456. Branch architecture kept: dependency-wait scheduler (park mid-EVM on a read dependency, not abort+re-queue) and write-side validation (valueStatus stamped at write, noValueRead). main's orthogonal changes folded in (no-ctx FlushPendingUpdates, ResolveCode->accounts.Code, StopAccessRecording, BindsWorkerTx, empty.RootHash, 3-arg FlushVersionedWrites, #24504 SystemAddress BAL filter removal, writeBalanceVersioned prev param, CreateAccount no-heap-alloc, memreader multi-block BodyWithRawTransactions).
…AL filter (erigontech#24583) Cherry-pick of erigontech#24504 to release/3.7. The upstream BAL and zero-value system-call regressions fail on the release base and pass with this backport. Resolved a blank-line conflict in removed BAL-filter tests; the code changes match upstream. Co-authored-by: Paweł Bylica <pawel@ethereum.org>


Every zero-value system call touched its caller, which put SystemAddress
into the block access list, and AsBlockAccessList then dropped it unless a
user tx had a non-revertable access to it. That heuristic also dropped real
accesses: a zero-amount withdrawal to SystemAddress and a delegation to it.
By the EIP-7928 spec, being the caller of a system call is not a state
access, so a system call no longer touches its caller (AuRa keeps the touch
for its empty system account) and SystemAddress follows the same BAL rules
as any other address.
IntraBlockState.SubBalance still turns a zero debit of SystemAddress into a
touch on every chain. Block execution never debits SystemAddress (every
caller passes a recovered sender or a contract address), so only RPC calls
from 0xff..fe (eth_call, eth_simulateV1, trace_call) reach it; it is left as
is and its comment corrected.
Open question (Gnosis): AuRa keeps the system-call touch of SystemAddress while the BAL filter is gone, so an AuRa chain activating Amsterdam would carry SystemAddress as an access-only entry in every BAL. No AuRa chain has Amsterdam scheduled; whether Gnosis BALs should include it needs to be agreed with Nethermind.