Skip to content

fix: distinguish static-mode violations from INVALID (HaltKind.staticViolation) - #37

Merged
leonardoalt merged 2 commits into
mainfrom
fix/static-violation-haltkind
Jul 16, 2026
Merged

fix: distinguish static-mode violations from INVALID (HaltKind.staticViolation)#37
leonardoalt merged 2 commits into
mainfrom
fix/static-violation-haltkind

Conversation

@leonardoalt

Copy link
Copy Markdown
Member

Why

State-modifying built-ins attempted in a static frame (env.static = true) halted with HaltKind.invalid, conflating them with the INVALID opcode. The EVM raises a dedicated StaticModeViolation exception on this path, so a Yul→EVM compiler proved against this semantics cannot match the exact exception for static-context writes — it blocks extending compiler correctness to static (STATICCALL) frames.

What

  • Add HaltKind.staticViolation, distinct from invalid.
  • Route every static write-protection halt to it: guardStatic (covers sstore/tstore/log0log4/selfdestruct) and the value-bearing call/callcode and create/create2 branches of builtinWithExternal.
  • Classify it non-committing in HaltKind.commits (a static violation reverts the frame).
  • The genuine INVALID opcode is unchanged; opName/parse/effects untouched.
  • Guards, examples, and docs (DESIGN.md, Observation.lean) updated.

Checks

lake build green (8576 jobs); lake exe runLinter YulSemantics clean. This is a prerequisite for full static-context support in the yul-compiler correctness theorem (a companion PR pins this commit).

🤖 Generated with Claude Code

…tion)

State-modifying built-ins attempted in a static frame previously halted with
`HaltKind.invalid`, conflating them with the INVALID opcode. The EVM raises a
dedicated `StaticModeViolation` exception on this path, so a Yul→EVM compiler
proved against this semantics could not match the exact exception for
static-context writes.

Add a distinct `HaltKind.staticViolation` and route every static write-protection
halt to it (`guardStatic` for sstore/tstore/log0-4/selfdestruct; the
value-`call`/`callcode` and `create`/`create2` branches of `builtinWithExternal`).
Classify it non-committing in `HaltKind.commits`. The genuine INVALID opcode is
unchanged. Guards, examples, and docs updated.

lake build green (8576 jobs); runLinter clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A value-bearing `callcode` transfers value from the executing account to itself
(callcode runs the target's code in the current account's context), which is a
no-op on world state. Per EIP-214 and the EVM (geth has no static gate on
CALLCODE; the pinned evm-semantics likewise has no `callcodeStatic` rule), it is
NOT rejected in a static frame — unlike a value-bearing `call`.

The previous static guard on `callcode` was an over-restriction: it made the Yul
semantics diverge from the EVM (a Yul→EVM compiler cannot match a nonexistent
target rejection). Remove it so `callcode` behaves like `delegatecall`/`staticcall`
under static context. `call` (which transfers to another account) stays rejected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@leonardoalt
leonardoalt marked this pull request as ready for review July 16, 2026 12:34
@leonardoalt
leonardoalt merged commit 0b1a2c0 into main Jul 16, 2026
1 check passed
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.

1 participant