Skip to content

execution/vm: preserve EIP-4788 no-op-when-not-deployed syscall semantics#20691

Merged
yperbasis merged 3 commits intomainfrom
yperbasis/more_bal
Apr 21, 2026
Merged

execution/vm: preserve EIP-4788 no-op-when-not-deployed syscall semantics#20691
yperbasis merged 3 commits intomainfrom
yperbasis/more_bal

Conversation

@yperbasis
Copy link
Copy Markdown
Member

Summary

#19277 added && !syscall to the Spurious Dragon zero-value short-circuit in evm.call, causing system calls to a non-deployed target to run CreateAccount(addr, false) instead of returning a no-op. That violates EIP-4788's rule that "if the contract code is empty, the system call is a no-op", specifically at the fork-transition block where the beacon-root contract is deployed later in the same block by a CREATE tx.

Repro

On #20606 (bal-devnet-4), which flips ExperimentalBAL + Exec3Parallel on by default, the eest_blockchain suite fails:

--- FAIL: TestExecutionSpecBlockchain/cancun/eip4788_beacon_root/
          test_beacon_root_contract_deploy.json/.../deploy_on_cancun
block_test.go:62: block #[2 0 0 0] insertion into chain failed:
  BadBlock err: updateForkChoice: [4/6 Execution] invalid block,
  block=2, gas used by execution: 52529, in header: 116552

Block 1 is the first Cancun block (timestamp=15000, fork point). The EIP-4788 pre-block syscall fires before the beacon-root contract is deployed later in block 1's tx 0. Post-#19277, the syscall calls CreateAccount(0x000f3df6…, false) at the beacon-root address, diverting state so block 2's verifier tx underuses gas by ~64k.

Reverting only the !syscall clause from the short-circuit — while keeping the rest of #19277 — makes the test green without regressing TestSystemCallZeroValueSkipsTransferChecks (its target already exists, so this branch isn't exercised).

Change

execution/vm/evm.go, one hunk:

 if !exist {
-    if !isPrecompile && evm.chainRules.IsSpuriousDragon && value.IsZero() && !syscall {
+    // EIP-4788/6110/7002/7251 system calls to a non-deployed target are
+    // no-ops ... short-circuit to preserve that at the fork-transition block.
+    if !isPrecompile && evm.chainRules.IsSpuriousDragon && value.IsZero() {
         return nil, gas, nil
     }
     evm.intraBlockState.CreateAccount(addr, false)
 }

Everything else from #19277 is preserved: the zero-value CanTransfer skip, TouchAccount(caller) instead of Transfer for syscalls, and the intra_block_state.go refactor.

Validation

Local:

  • cancun/eip4788_beacon_root/* — all pass (8.5s)
  • Full cancun eest_blockchain suite — pass (167s)
  • Full prague eest_blockchain suite — pass (85s)
  • TestSystemCallZeroValueSkipsTransferChecks — pass (still green)
  • execution/state + execution/stagedsync unit tests — pass
  • make lint — clean

Test plan

Related

🤖 Generated with Claude Code

…tics

#19277 added `&& !syscall` to the Spurious Dragon zero-value short-circuit
in evm.call, so system calls to a non-deployed target now run
CreateAccount(addr, false) instead of returning a no-op. That breaks
EIP-4788's rule that the beacon-root syscall is a no-op when the
contract code is empty — specifically at the fork-transition block,
where the beacon-root contract is deployed later in the same block by a
CREATE tx. The pre-tx CreateAccount diverts state enough that a later
verifier tx underuses gas and the block is rejected with a gas mismatch.

Surfaced once #20606 (bal-devnet-4) flipped ExperimentalBAL + Exec3Parallel
on by default: eest_blockchain's
cancun/eip4788_beacon_root/test_beacon_root_contract_deploy[deploy_on_cancun]
goes red with "gas used by execution: 52529, in header: 116552" on
block 2. Dropping the `!syscall` clause restores the no-op short-circuit.
The rest of #19277 (skip CanTransfer on zero-value calls, TouchAccount(caller)
instead of Transfer for syscalls, intra_block_state refactor) is preserved.

TestSystemCallZeroValueSkipsTransferChecks still passes (its target
already exists, so this path isn't exercised).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@yperbasis yperbasis added the Glamsterdam https://eips.ethereum.org/EIPS/eip-7773 label Apr 20, 2026
@yperbasis yperbasis requested a review from Copilot April 20, 2026 14:51
@yperbasis yperbasis marked this pull request as ready for review April 20, 2026 14:51
@yperbasis yperbasis requested a review from mh0lt as a code owner April 20, 2026 14:51
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR restores the “no-op when target is not deployed and value is zero” behavior for system calls (notably EIP-4788) by removing the && !syscall restriction from the Spurious Dragon short-circuit in EVM.call, preventing unintended account creation during fork-transition blocks.

Changes:

  • Re-enable the Spurious Dragon zero-value short-circuit for system calls to non-deployed targets.
  • Add an explanatory comment describing the fork-transition/system-call motivation for the short-circuit.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread execution/vm/evm.go Outdated
The previous comment framed the Spurious Dragon zero-value short-circuit
as EIP-4788/6110/7002/7251 syscall-specific, but:

- the condition is not gated on `syscall` — it fires for any zero-value
  CALL to a non-existent non-precompile account (standard EIP-161);
- EIP-6110 parses deposit logs and does not issue a system call;
- EIP-7002 and EIP-7251 guard the syscall with a codeSize==0 pre-check
  in misc/eip7002.go and misc/eip7251.go, so they error out before
  reaching this branch.

Only EIP-4788 relies on this short-circuit today (it has no pre-check
and depends on the VM returning a no-op when the beacon-root contract
has not yet been deployed at the fork-transition block). Describe the
general condition first, then the EIP-4788 motivation for preserving it.

Addresses Copilot review feedback on #20691.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@yperbasis yperbasis requested a review from taratorio April 20, 2026 15:02
@yperbasis yperbasis enabled auto-merge April 20, 2026 18:45
@yperbasis yperbasis added this pull request to the merge queue Apr 21, 2026
Merged via the queue into main with commit 139e4e3 Apr 21, 2026
36 checks passed
@yperbasis yperbasis deleted the yperbasis/more_bal branch April 21, 2026 04:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Glamsterdam https://eips.ethereum.org/EIPS/eip-7773

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants