Skip to content

Gap analysis, implement execution-spec-tests#10

Open
garyschulte wants to merge 39 commits intomainfrom
feat/gap-analysis
Open

Gap analysis, implement execution-spec-tests#10
garyschulte wants to merge 39 commits intomainfrom
feat/gap-analysis

Conversation

@garyschulte
Copy link
Copy Markdown
Collaborator

This PR is the result of gap-analysis necessary to have a mainnet-friendly evm interpreter

at a high level this PR:

  • implements an interpreter dispatch handler
  • creates a shared protocol schedule definition
  • implements host operations
  • reverts back to Vec based stack, in order to avoid recursive sub-interpreter initialization costs
  • addresses boundary cases for gas accounting
  • boundary cases for precompiles
  • implements state-tests portion of execution-spec-tests

Some things noted for future improvements but not included in this PR:

  • systematic treatment of alloc rather than explicit std.* usage
  • precompile spec wrappers, allowing clean implementation overrides
  • probably many more things

@garyschulte garyschulte force-pushed the feat/gap-analysis branch 3 times, most recently from 7fe8bd0 to 6133bc2 Compare March 5, 2026 22:48
daniellehrner and others added 27 commits March 5, 2026 15:00
Signed-off-by: garyschulte <garyschulte@gmail.com>
protocol schedule refactor (may remove in favor of namespace specific specId impls)
initial implementation of host operations (storage ops) - they probably need work

Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Phase 1 — Caller state changes (enables first valid tx)
        1. validateAgainstStateAndDeductCaller: create journal entries to actually deduct max_fee * gas_limit + value from caller (via journal.balanceTransfer or direct balance
        change entry)
        2. Pre-execution: increment caller nonce via journal
        3. Pre-execution: warm access list addresses and storage slots, warm coinbase (EIP-3651)
        4. Fix calculateInitialGas: zero bytes at 4 gas, non-zero at 16; access list: 2400/address + 1900/storage slot

Signed-off-by: garyschulte <garyschulte@gmail.com>
        Phase 2 — Post-execution correctness (produces correct state root)
        5. Implement gas refund accumulation from SSTORE (journal already tracks old values, need to compute refund from them)
        6. reimburse_caller: effective_gas_price * (gas_limit - gas_used - refund) credited back to caller balance
        7. reward_beneficiary: (effective_gas_price - base_fee) * gas_used credited to coinbase

Signed-off-by: garyschulte <garyschulte@gmail.com>
        Phase 3 — State commitment
        8. On success: call journal.commitTx(), then journal.finalize() to return State diff
        9. On error/revert: call journal.discardTx()
        10. Wire ExecuteCommitEvm.executeAndCommit() to apply the State diff to the underlying DB

Signed-off-by: garyschulte <garyschulte@gmail.com>
        Phase 4 — Nested calls (needed for any non-trivial contract)
        11. Implement frame loop in MainnetHandler.execute() with a call stack
        12. Wire host.call() to push a child frame, execute it, and return result to parent's stack

Signed-off-by: garyschulte <garyschulte@gmail.com>
        Phase 5 — EIP completeness
        13. EIP-155 chain ID validation
        14. EIP-3607 (reject tx from accounts with code)
        15. EIP-4844 blob validation / excess blob gas
        16. EIP-7702 authorization list

Signed-off-by: garyschulte <garyschulte@gmail.com>
  Group 1 — Bytecode.default() fix (journal.zig:519)
  - Bytecode.default() → Bytecode.new() (method didn't exist)
  - Also fixed journal.zig:536: code.eip7702.address().isZero() → std.mem.eql(u8, &code.eip7702.address, &[_]u8{0} ** 20) (field access, not method; arrays have no
  isZero())

  Group 2 — Protocol schedule hardfork registration (protocol_schedule.zig)
  - Removed RETURNDATASIZE/RETURNDATACOPY from Frontier → added to applyByzantiumChanges
  - Removed SHL/SHR/SAR from applyByzantiumChanges → added to applyConstantinopleChanges
  - Removed CREATE2 from Frontier → added to applyConstantinopleChanges

  Group 3 — CREATE collision balance check (journal.zig:628)
  - Added || balance != 0 to EIP-161 collision check

  Group 4 — EXP gas spec-gating EIP-160 (gas_costs.zig, arithmetic.zig)
  - Added G_EXPBYTE_FRONTIER = 10
  - opExp now uses 10 pre-Spurious Dragon, 50 post

  Group 5 — LOG topic memory leak (journal.zig)
  - discardTx, commitTx, and deinit now free heap-allocated topic slices before clearing the log list

  Group 6 — EIP-4844 blob validation (primitives/main.zig, validation.zig, mainnet_builder.zig)
  - Added GAS_PER_BLOB, MAX_BLOB_NUMBER_PER_BLOCK, VERSIONED_HASH_VERSION_KZG constants
  - calculateInitialGas adds blob intrinsic gas (131072 × blob_count)
  - New validateBlobTx checks count, hash version, and max_fee_per_blob_gas ≥ blob_basefee

  Group 7 — EIP-7702 authorization list (mainnet_builder.zig)
  - preExecution now iterates authorization list (Prague+), validates chain_id and nonce, bumps authority nonce, and sets EIP-7702 delegation bytecode (or clears it for
  zero-address authorizations)

Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
…aults and panics

Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
… leak

Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
…allout

Signed-off-by: garyschulte <garyschulte@gmail.com>
…, balance and code checks

Signed-off-by: garyschulte <garyschulte@gmail.com>
garyschulte and others added 9 commits March 5, 2026 15:01
…rking

Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
…or vec/static stack until we are zk proving to back up our cost expectations

Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
…journal workarounds required by static stack allocation and recursion

Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
…0.15 syntax

host.zig: touch precompile callee on successful call so it appears in
post-state for pre-EIP-161 forks (Frontier/Homestead). The callee was
loaded into evm_state by accountInfo() in the CALL opcode handler but
never marked touched, causing it to be excluded from the post-state.
For EIP-161+ forks the empty account is cleaned up by state-clear logic.

mainnet_builder.zig: detect EIP-7702 delegation designators stored as
legacy_analyzed bytecode by checking the raw 0xEF 0x01 0x00 prefix,
so pre-state fixtures with already-delegated accounts are re-delegatable.

validation.zig + primitives/main.zig: apply correct per-fork blob count
limits — Osaka uses per-tx limit of 6 (EIP-7594), Prague uses per-block
limit of 9 (EIP-7691), Cancun keeps 6.

mcl_wrapper.zig: fix array initializer syntax for Zig 0.15 compatibility
([1]T{.{}} ** N instead of .{.{}} ** N).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ntirely, but that is something to consider since EOF is dead.

Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
Signed-off-by: garyschulte <garyschulte@gmail.com>
garyschulte pushed a commit to garyschulte/zevm that referenced this pull request Mar 24, 2026
* feat: EIP-7843 — SLOTNUM opcode (Amsterdam)

Adds SLOTNUM (0x4B): pushes the beacon chain slot number from the block
environment. Gas: G_BASE (2). Adds slot_number field to BlockEnv and
BlockEnvBuilder (defaults to 0).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(eip-7843): make slot_number optional everywhere per review feedback

- `BlockEnv.slot_number`: `u64` → `?u64`; default is `null` (absent on
  pre-Amsterdam / non-beacon chains)
- `BlockEnvBuilder`: add `setSlotNumber(?u64)`; propagate `slot_number`
  through all existing setter methods (previously silently dropped)
- `Host.slotNumber()`: returns `?u64`
- `opSlotnum`: halt with `invalid_opcode` when slot number is absent
- `spec_test/runner.zig`: initialize `slot_number` to `null`

Addresses review comment from @garyschulte on PR 10d9e#10.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
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