From 77b6a092b84df361886b4a13f60c22a3f0609041 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 17 Sep 2026 01:51:22 -0400 Subject: [PATCH] format: resolve external-call invoke operands before the instruction #281 made instruction contexts postconditions, pointers included, but the invoke schema kept the sentence that placed pointers "at the instruction's trace step", and its CALL, DELEGATECALL and CREATE2 examples point at stack operands that those instructions consume. The invoke context stays on the call instruction: the invocation occurs when that instruction executes. The pointer fields of a `message` or `create` invocation (target, gas, value, input, salt) describe the operands of the marked instruction, so they resolve against the state immediately before it executes. The instruction schema names this as the one exception to the postcondition rule. Internal calls need no exception: the entry JUMPDEST leaves the stack unchanged. The external-call return example no longer leans on the pre-state reading: its pointers resolve after the marked instruction, as with any context. No schema keyword changes. No producer emits `message` or `create` invokes today, and no consumer resolves their pointers. --- CHANGELOG.md | 25 +++++++- .../spec/program/context/function/invoke.mdx | 31 ++++++---- .../spec/program/context/function/return.mdx | 11 ++-- .../context/function/invoke.schema.yaml | 59 ++++++++++++------- .../context/function/return.schema.yaml | 9 +-- schemas/program/instruction.schema.yaml | 6 +- 6 files changed, 96 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88fd8b26c1..73ce662c1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,9 +25,9 @@ Each `Producers:` and `Consumers:` sub-item starts with one of three prefixes: sub-item says what the party may now do. - `required:` Output that was valid in the previous published version no longer validates, or the specification adds or changes a normative **must**, or the - meaning of data that was already valid changes. The sub-item names the schema keyword, or - quotes the specification prose, that imposes the obligation. Prose that says - **should** or "preferred" is never `required:`. + meaning of data that was already valid changes. The sub-item names the schema + keyword, or quotes the specification prose, that imposes the obligation. Prose + that says **should** or "preferred" is never `required:`. A consumer has an obligation only where producers can now emit something that a conforming consumer would otherwise misread or reject, or where the meaning of @@ -55,6 +55,24 @@ The sections do not signal obligations; the prefixes do. ### Changed +- The pointer fields of an external call or contract creation `invoke` + (`target`, `gas`, `value`, `input`, `salt`) describe the operands that the + marked CALL or CREATE instruction consumes, so they resolve against the state + immediately before that instruction executes. This is the one exception to + the rule from [#281] that a context's pointers resolve after its instruction. + The `invoke` description had kept the earlier "trace step" sentence, which + contradicted that rule; the context itself stays on the call instruction + ([#303]). + - Schemas: **ethdebug/format/program/context/function/invoke**, + **ethdebug/format/program/instruction**, + **ethdebug/format/program/context/function/return** + - Producers: no change needed. The `invoke` examples already placed the + context on the call instruction with pointers to its operands. + - Consumers: required: resolve the pointer fields of a `message` or `create` + `invoke` against the state before the marked instruction executes (a new + exception in the `context` description). A consumer that followed the + `invoke` examples already does this. + - A segment's `offset` is no longer limited to a value below `$wordsize`: an offset at or past `$wordsize` now carries into later slots. The default `length` is now `$wordsize - (offset mod $wordsize)`, which runs to the end of @@ -590,3 +608,4 @@ First published version of the specification. [#284]: https://github.com/ethdebug/format/pull/284 [#285]: https://github.com/ethdebug/format/pull/285 [#286]: https://github.com/ethdebug/format/pull/286 +[#303]: https://github.com/ethdebug/format/pull/303 diff --git a/packages/web/spec/program/context/function/invoke.mdx b/packages/web/spec/program/context/function/invoke.mdx index b04f44b2de..4ebfe04eb6 100644 --- a/packages/web/spec/program/context/function/invoke.mdx +++ b/packages/web/spec/program/context/function/invoke.mdx @@ -21,23 +21,30 @@ contexts to reconstruct call stacks. ## Pointer evaluation and instruction placement -An instruction's context describes what is known _following_ -that instruction's execution: the fact that a function was -invoked holds from that point forward. Pointers within the -context reference the machine state at the instruction's trace -step — the state a debugger observes when it encounters the -instruction. +An instruction's context holds _following_ that instruction's +execution: the fact that a function was invoked holds from that +point forward, and pointers within the context resolve against +the machine state after the instruction has executed (see +[instruction](/spec/program/instruction)). The operand pointers +of an external call or contract creation are the one exception. For **internal calls**, this context is typically placed on the -callee's entry JUMPDEST rather than the caller's JUMP. JUMP -consumes its destination operand from the stack; at the entry -JUMPDEST, the remaining stack (return address followed by -arguments) is stable and directly addressable. +callee's entry JUMPDEST. The caller's JUMP has consumed its +destination operand by then, and JUMPDEST leaves the stack +unchanged, so after it executes the remaining stack (return +address followed by arguments) is stable and directly +addressable. For **external calls** and **contract creations**, this context marks the CALL/DELEGATECALL/STATICCALL/CREATE/CREATE2 -instruction itself, where the call parameters are visible on -the stack. +instruction itself: the invocation occurs when that instruction +executes. The pointer fields of a `message` or `create` +invocation (`target`, `gas`, `value`, `input`, `salt`) describe +the operands of the marked instruction, which the instruction +consumes. These pointers therefore resolve against the machine +state immediately **before** the marked instruction executes. A +debugger that is paused about to execute the CALL can resolve +them against the state it observes.