refactor(machine_state): split into Frame / Env / Substate#132
Merged
Conversation
Move the seven transaction-scoped accumulator fields out of MachineState into a dedicated Substate struct: touched_addresses, accessed_addresses, accessed_storage_keys, created_addresses, original_storage, transient_storage, logs. These all share the same lifecycle — accumulate across successful child frames, revert together on failure — so grouping them simplifies Journal.merge_child_result and clarifies what is and isn't substate per the Yellow Paper. No behavioural change: every field keeps its name and semantics; only the access path moves from state.X to state.substate.X. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Group the three immutable per-call inputs — tx, block, config — under a single `env` field. They share a lifecycle (set on frame construction, never mutated, inherited unchanged by children), so collapsing them makes the distinction between "what evolves" (frame, substate) and "what the call observes" (env) explicit at every access site. Public API unchanged: `MachineState.new/2` still takes `tx:`, `block:`, `config:` as flat keyword opts. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Splits the per-call execution frame out of MachineState into its own struct at lib/eevm/interpreter/machine_state/frame.ex, completing the three-way split of the envelope into frame / env / substate. Frame holds the per-call mutable execution context — pc, stack, memory, gas, refund, code, return_data, contract, depth, is_static, plus the parent-memory return write-back location. The active frame lives at state.frame; suspended parent frames use the same struct in state.call_stack. CallFrame is removed and its single usage replaced. All opcode modules, handlers, and tests now read these fields through state.frame.X and write via MachineState.update_frame/2 (which threads the closure over the active frame). pop_frame now also carries the child's refund forward into the restored parent frame, preserving the old envelope-level refund accumulation semantic. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Splits the monolithic
MachineStateenvelope into three sub-structs that match the three lifecycles already implicit in the EVM:state.frame) — per-call mutable: pc, stack, memory, gas, refund, code, return_data, contract, depth, is_static, parent return write-back location. Replaces and unifies the now-removedCallFrame.state.env) — per-call immutable: tx, block, hardfork config.state.substate) — transaction-scoped accumulators: touched / accessed / created addresses, original storage, transient storage, logs.Top-level on
MachineStatekeeps only what crosses these boundaries:frame,env,substate,db,call_stack,status,tracer.Stacked as three commits, one per phase:
af4d6ac— phase 1: extractSubstate09a1110— phase 2: extractEnv9dbbf9c— phase 3: extractFrame, removeCallFrameAll opcode modules, handlers, and helpers now read fields through
state.frame.X/state.env.X/state.substate.Xand write viaMachineState.update_frame/2.pop_framecarries child refund forward into the restored parent frame so the old envelope-level refund accumulation semantic is preserved.Test plan
mix compile— cleanmix credo --strict— no issuesmix test— 4 doctests + 613 tests, 0 failures🤖 Generated with Claude Code