diff --git a/.claude/commands/enhance-ported-test.md b/.claude/commands/enhance-ported-test.md new file mode 100644 index 00000000000..f6d3f6852e5 --- /dev/null +++ b/.claude/commands/enhance-ported-test.md @@ -0,0 +1,530 @@ +# Enhance Ported Test + +Future-proof and clean up a test under `tests/ported_static/`. These tests were +machine-ported from the legacy `ethereum/tests` static fillers (YAML/JSON) and +carry a lot of boilerplate, hardcoded values, and weak/incomplete post-state +checks. This skill is the ordered methodology for turning one into idiomatic, +robust Python. + +This skill is a **living document**: it captures the cases we have validated so +far. Real tests will hit shapes not covered here — that is expected. When you +find one, solve it, then add the new case/step to this file. + +## Goal + +The end state is a test that **passes on every fork from its `valid_from` +onward** (not just the baseline), expresses its intent explicitly, and has no +fragile hardcoded constants. "Future-proof" = a later fork that re-prices gas, +adds state costs, or changes account rules should not silently break it. + +## Core loop (subtractive) + +Most of the work is **removing** boilerplate one piece at a time and proving the +test still passes after each removal: + +1. **Baseline first.** Before touching anything, fill the test and confirm it is + green: `uv run fill --fork= -q --clean`. +2. Make **one** change. +3. Fill again (same fast command). Green → keep, move on. +4. **Red → roll back that one change and analyze.** A break is information: it + tells you the thing you removed was load-bearing. Understand *why* before + deciding whether to keep it, replace it with a dynamic equivalent, or leave + it. Never paste a new expected value just to make red go green without + understanding the change (see "Re-pinning" below). + +Do low-risk, independent removals in small batches if you like, but anything +that can plausibly interact (addresses, contracts, gas) goes **one at a time** +so a failure is attributable. + +## Verification cadence + +- **Iterating:** `--fork=` (usually Cancun) — fast. +- **Checkpoint / done:** fill the whole `valid_from` range (omit `--fork`) so all + deployed forks are exercised. +- **Probe the future fork:** explicitly `--fork Amsterdam` (or the latest fork + that enables new EIPs). A ported test listed in `amsterdam_skip_list.txt` will + always show `sss` there — to see its *real* behavior, temporarily remove its + entry from that file, fill, then restore (or, once fixed, remove it for good — + see Finishing). A gas/state-cost change there is the most likely future + breakage. +- **`fill` output:** writes to `./fixtures` (`--clean` resets it), or pass + `--output ` for a scratch location. Do **not** use `-o` — that is + pytest's `--override-ini`, not the output dir. + +## Ordered steps + +Do them roughly in this order. Earlier steps unblock later ones (notably: max +out gas *before* strengthening post-state, so added opcodes don't hit a gas +ceiling). + +### 1. Remove `env` +Delete the `Environment(...)` block, the `env=env` arg to `state_test`, and any +now-orphaned vars (`coinbase`) and the `Environment` import. The framework +supplies sensible defaults. +**Keep `env` only if** the post asserts on the coinbase/`fee_recipient` balance, +or the bytecode reads block fields (`NUMBER`, `TIMESTAMP`, `PREVRANDAO`, +`BASEFEE`, `GASLIMIT`, `COINBASE`). `fee_recipient=sender` alone is not a reason +to keep it. + +### 2. Remove `gas_limit` from the transaction (if gas is not the subject) +This is the common case and belongs early. Omitting `gas_limit` maxes out the +gas the tx receives, so the body executes fully. See `write-test.md` "Transactions". +- **Remove it** when the test is about *behavior* and just needs to run to + completion. This also lets you delete any per-fork gas band-aids (e.g. + `fork.is_eip_enabled(8037)` budget bumps) and often the `fork` param itself. +- **Keep it** only for genuinely gas-sensitive tests (OOG boundaries, + intrinsic-gas, code-deposit limits, or gas metering) — see step 10. +- **Gas-snapshot tests are gas-sensitive.** If the post asserts a stored `GAS` + reading or a `SUB(@gas_before, GAS)` delta (legacy slots `0` / `0x64`), the + test *measures gas* — handle it under step 10 (preserve via `CodeGasMeasure`), + do not just strip `gas_limit`. This is the dominant `amsterdam_skip_list.txt` + shape: the stored gas value is exactly what EIP-8037 re-prices and breaks. +- **EIP-8037 caveat:** when you omit `gas_limit` on a test that *measures* an + operation incurring **state gas** (account creation, storage writes), add + `state_gas_reservoir=0` to the tx, or that state gas is silently dropped from + the measurement on EIP-8037 forks (see step 10). Pure-execution opcodes + (e.g. `PUSH0`, arithmetic) have no state gas and do not need it. +- Do **not** add a comment explaining the absence of `gas_limit`; omission is + the default. + +### 3. Remove hardcoded contract `nonce` +Drop `nonce=0` from `pre.deploy_contract(...)`. If a `compute_create_address(..., +nonce=N)` in the post depends on it, keep them consistent. + +### 4. Remove hardcoded addresses (one contract at a time) +Two sub-cases: +- **Value discarded:** a `contract = Address(0x...)` literal that is immediately + overwritten by `pre.deploy_contract(...)` (no `address=`). Just delete the + literal; the deploy returns a `fill`-generated address. +- **Value passed to `address=`:** remove both the literal *and* the `address=` + argument, per contract, filling after each. +- **No-op case:** `to=None` creation tests often have no hardcoded address at all + (the created address is `compute_create_address(sender, nonce=0)`). Confirm by + grepping for `Address(0x` / `address=`. +- **On break:** some bytecode hardcodes that address as a CALL/CREATE target (or + the tx `to`/`data`). Thread the dynamic address through the caller and the tx + entry point instead. +- **Self-reference:** a contract that hardcodes its *own* deploy address (e.g. + `Op.BALANCE(0xF172…)` where `0xF172…` is its own `address=`). Threading a + `fill`-generated address in is impossible (chicken-and-egg), so replace the + self-reference with the opcode that yields it at runtime — `Op.BALANCE(Op. + ADDRESS)`. Don't substitute a *different* opcode that happens to be shorter + (e.g. `Op.SELFBALANCE`) if it changes what the test exercises. +- **Remove `@pytest.mark.pre_alloc_mutable`** once the test no longer hardcodes + addresses/nonces or assigns `pre[...]` directly — i.e. all allocation now goes + through `fund_eoa` / `deploy_contract` / `nonexistent_account`. Fill to confirm. + +### 5. Remove easy boilerplate values +Independent and usually safe (batchable): `pre.fund_eoa(amount=...)` → `fund_eoa()`; +tx `value`; tx `data` when it is empty (`Bytes("")`); explicit gas price fields. +Keep any of these that the post actually checks or that triggers the behavior +under test. +- **Drop opcode args that just pass their default.** Ported bytecode often spells + out zero operands that are already the default, e.g. `Op.CALL(..., args_offset=0, + args_size=0, ret_offset=0, ret_size=0)` — all four are `0` by default. Removing + them is a no-op on the assembled bytecode (verify once with + `bytes(a) == bytes(b)`) and cuts noise. Applies to any opcode arg equal to its + default. +- **Drop the hardcoded subcall `gas` operand — this is a correctness fix, not + cosmetics.** `Op.CALL`/`CALLCODE`/`DELEGATECALL`/`STATICCALL` default `gas` to + `Op.GAS` (forward all remaining). Ported fillers hardcode a constant + (`gas=0xEA60`, `gas=0x186A0`) that was sized for the *old* gas schedule; once + EIP-8037 inflates the callee's state gas (e.g. a zero→non-zero SSTORE jumps to + ~97920), that fixed budget no longer covers the callee and the subcall OOGs on + Amsterdam — a common reason a pure-behavior test lands on the skip list. Omit + the operand so it forwards everything. **Caveat:** forwarding all gas via + `Op.GAS` misbehaves on **pre-EIP-150 (Homestead)** — the sweep (step 11) fails + only there, so such tests floor at **TangerineWhistle**. Keep an explicit `gas` + operand *only* when the amount forwarded is the subject (an OOG-boundary test). + **Budget vs. subject:** before dropping the operand, ask *why* the constant + has its value. A mid-sized constant (`0xEA60`) is a *budget* sized for the old + schedule — drop it. An absurd or boundary constant (`2**256 - 20`) is the + *subject*: it exercises the 63/64 clamp on an oversized ask (a client that + computed e.g. `requested + stipend` in wrapping arithmetic would forward + almost nothing and fail). Keep it, name it (`OVERSIZED_GAS_ASK`), and state + the intent in a comment. Validated on `test_make_money`. +- **A codeless / absent call target is `pre.nonexistent_account()`**, not + `pre.fund_eoa(amount=0)`. It yields an address guaranteed to hold no code and + no state, which is what "call an empty contract" tests mean. +- **Drop a stale `# noqa: F841`** on `contract = pre.deploy_contract(...)` once the + variable is actually used (in `to=` / the post); leaving it triggers `RUF100`. + +### 6. (Parametrized tests) Analyze what the `data` parameter is +Look at `tx.data` / `tx.to`: +- **Scenario A — data is a target contract address:** the tx lands in a thin + entry-point contract that just `CALL`s the address from calldata. Usually you + can **delete the entry-point** and call the target directly, and the N targets + are near-identical → replace N bytecode copies with a **dynamic generator** + parameterized by the small difference. When the targets are *gas-measurement* + contracts differing only by the measured opcode, the dedup collapses all the + way to a single `CodeGasMeasure(code=opcode)` parametrized on the opcode + (step 10) — the entry-point's `CALL` was only a delivery mechanism. Validated + on `test_push0_gas2` (PUSH0 vs PUSH1 0x00). +- **Scenario B — data is initcode:** spotted by **`to=None`**. Decide whether + running inside initcode is *required* by the test (e.g. the test is about + initcode-context behavior, per its title/docstring) or just an artifact of the + static-filler format (most common — then the logic can move to a normal + deployed contract). If required, convert the `tx_data` array into an + `initcode(d)` **generator function**: even when variants are genuinely + different programs, the function form lets each branch be labeled by intent, + surfacing the one thing that varies. + +### 7. (Parametrized tests) Simplify `expect_entries_` / `resolve_expect_post` +**First, identify which index actually discriminates — it is *not* always `d`.** +Ported tests also key on `g` (gas) or `v` (value); check both the +`expect_entries_` `indexes` (which axis is non-`-1`) and which of +`tx_data[d]`/`tx_gas[g]`/`tx_value[v]` is the list with >1 entry. The other two +indexes are pinned/wildcard. (Example: `test_add_non_const` varies `v` — +`d`/`g` are fixed at 0 and the `indexes` match on `"value"`.) +**Precondition** (to collapse to a per-case form): every entry's `network` is +implied by `valid_from` and there is no `expect_exception`. Then the post is a +pure function of the discriminating index. +- Convert `expect_entries_` into a plain **list of `result` dicts indexed by the + discriminator** — duplicating identical entries (e.g. data `[0,1]` → two + slots) is fine and preferred; an explicit flat list is easiest to reason about. +- **When the discriminator is a real quantity** (the tx `value` or `gas`), + parametrize *directly on that quantity* (`parametrize("tx_value", [0, 1])`) + rather than an opaque index, feed it straight into the `Transaction`, and + express the post as a function of it. A clean closed form is ideal — + e.g. `Account(storage={0: 2 * tx_value})` for a contract that stores + `ADD(BALANCE, BALANCE)` of a balance equal to the sent value (this is the + "encode relationships" idea from step 9 applied to the post). +- Cascade: delete the `resolve_expect_post` import, the `_exc` it returned, and + the tx's `error=_exc`. +- **Optionally merge** the data-generator and the post-list into **one + `if/elif/else` on `d`** that sets both `initcode` and `post` per case. This + co-locates each case's bytecode with its expected state — the strongest + readability win, and it tends to *reveal* incomplete verification. Use a final + `else` so every branch binds both vars; declare `initcode: Bytecode` and + `post: dict` above the switch. Prefer the array form when cases are many or + the switch would be unwieldy; this is a judgment call. +- **Clean up the `parametrize` signature.** The ported `"d, g, v"` triple is + usually overkill: drop the pinned/unused indexes from both the `parametrize` + and the function signature, keep the discriminator, and rename it to something + meaningful (and `fork` too, if no longer used). Parametrize on the renamed axis: + - **String values** (e.g. `parametrize("opcode", ["calldataload", + "calldatacopy", "codecopy"])`) read best when the cases are distinct + programs; pytest derives the test ids straight from the strings (matching the + old `id=`s), and the switch branches become `if opcode == "calldataload"`. + - **`Op` values** (e.g. `parametrize("opcode", [Op.SLOAD, Op.TLOAD])`) are + cleaner *only* when the opcode plugs directly into a shared bytecode template; + avoid forcing it when each case needs structurally different code. + - Drop the verbose `pytest.param(..., id=...)` wrapping when the bare values + already give good ids. + +### 7b. Consolidate near-identical sibling files +Ported fillers often arrive as a fan of files with near-identical names that +differ in one axis — `test_non_zero_value_{call,callcode,delegatecall}` × +`{,_to_empty,_to_one_storage_key,…}`. Once enhanced to the same shape, **join +them into one parametrized test** (`parametrize("opcode, target_kind", …)` with +ids matching the old filenames), set up the varying piece (call op, target +pre-state) from the params, and merge every source into a single `ported_from` +list. One readable file replaces N. Validated: 10 `NonZeroValue_*` files → +`test_non_zero_value.py`. + +### 8. Strengthen post-state verification +Co-locating bytecode and post (step 7) often exposes that the ported test barely +verifies anything. Improve coupling and observability: +- **Couple the expectation to the bytecode.** If a contract returns its own code + (`CODECOPY`+`RETURN`), assert `code=initcode` instead of a hand-copied + `bytes.fromhex(...)` — change the bytecode and the expectation follows. +- **Make no-op results observable.** Storing `0` is indistinguishable from not + storing (and `storage={}` already asserts "all slots zero" — see + `Storage.must_be_equal`). To genuinely prove a read returned zero, store a + derived non-zero value (e.g. `Op.ADD(Op.CALLDATALOAD(0), 1)` → assert `1`). +- **Zero source data makes offset tests vacuous.** A test that asserts an + out-of-bounds read yields zeros proves nothing if the *in-bounds* data is + also all zeros — any offset, right or wrong, reads zero. Supply non-zero + source bytes (e.g. `data=bytes(range(1, 33))` for a CALLDATACOPY test) so a + client reading from a wrong in-bounds offset produces a visible mismatch. + Ported fillers often ship all-zero calldata; the rewrite is the moment to + fix it. Validated on `test_copy_offset`. +- **Preserve every assertion the legacy filler made — count its slots.** A + ported post often pins *two* observables (e.g. the ask fillers stored both + the callee-observed gas *and* the caller's net gas, which proves unused + forwarded gas is credited back). When reframing, it is easy to carry over + the headline assertion and silently drop the second. Diff the old post's + slots against the new one and re-express each dropped slot dynamically (or + justify its removal explicitly). Validated on `test_raw_call_gas_ask` (the + caller reports its remaining gas up the stack as a second return word). +- **Add a canary.** Write a distinctive non-zero sentinel to an extra slot as the + *final* step (e.g. `Op.SSTORE(0x2, 0xC0DE)`), and assert it. If creation + reverts or the code doesn't run to completion, the slot stays zero and the + test fails loudly instead of silently passing on a coincidentally-matching + (often empty) account. +- Adding `SSTORE`s costs gas — this is why step 2 (max out gas) comes first. +- **Spot a *degraded* port and restore its stated intent.** A ported test whose + name/source promises a scenario its values don't actually exercise is a bug in + the port, not something to preserve faithfully. Classic tell: a + `*_after_value_transfer` / `*_with_value` test that sends `value=0`, so the + observable it names (a callee's `CALLVALUE`, a recipient's balance) is + vacuously zero and would pass even if the behavior were broken. Fix it by + supplying the missing ingredient (a non-zero tx `value`) and asserting the + now-meaningful result (`CALLVALUE == transferred`, recipient balance moved) — + note the restoration in the `@manually-enhanced` line. Validated on + `test_deleagate_call_after_value_transfer` (DELEGATECALL preserves the + enclosing frame's value). Read the test's *name and source comment* against + what it actually checks; the gap is the enhancement. + +### 9. Introduce variables that encode relationships +Whenever a literal carries intent or two literals are logically linked, lift them +into named variables that express the *relationship*, not just the value. E.g. +`create_value = 0xB` fed to both `Op.CREATE(value=create_value, ...)` and the tx +`value=create_value - 1` documents an intentional off-by-one (insufficient +balance) and keeps the two coupled so a future edit can't desync them. Same idea +ties a `CREATE`'s `size` operand to the memory/gas math that depends on it. +- **Post-state derived from gas/fees.** When the asserted value is a function of + the gas charge (e.g. an origin `BALANCE` read mid-execution equals + `sender_balance - gas_limit * effective_gas_price`), express it as that formula + rather than a hardcoded number. Such a test is gas-sensitive — keep an explicit + `gas_limit` (step 10), since the observable depends on it, but **derive that + `gas_limit` too** — `fork.transaction_intrinsic_cost_calculator()() + + code.gas_cost(fork) + buffer` (conservative metadata so it can't undershoot) — + so it is neither a magic number nor fork-fragile. Validated on + `test_sender_balance` (EIP-1559 effective-vs-max price). +- **But first ask whether the gas-derived value is the *subject* or just + noise.** A ported test often pins the `sender` balance to `initial − value − + gas_used * price` — pure filler bookkeeping, not what the test is about. If the + real subject is a gas-*independent* fact (a value flow `tx → caller → callee`, + a storage write, a created account), drop the `gas_limit` (step 2), drop the + fragile `sender`-balance assertion, and instead assert the gas-independent + facts, encoding them as a relationship (`caller: INITIAL + tx_value - + call_value`, `callee: INITIAL + call_value`). Only reach for the "derive the + fee formula" machinery above when the fee itself is the observable. Validated + on `test_make_money`. + +### 10. (Gas-subject / gas-snapshot tests) Replace hardcoded gas with dynamic calculation +Covers both tests that *assert* a gas amount and the dominant +`amsterdam_skip_list.txt` shape: a legacy `GAS` snapshot / `SUB(@gas_before, +GAS)` delta stored to slot `0`/`0x64`. That stored value is *why* EIP-8037 +breaks the test, but it is real coverage — **preserve and fork-robustify it, do +not drop it.** + +**The `CodeGasMeasure` workflow:** +- **Isolate** the bytecode under measurement into a variable + (`call_code = Op.CALL(...)`). This often reveals the legacy measured window + bundled extra ops — e.g. it wrapped an `SSTORE`, inflating the value by a cold + `SSTORE` (~22100). Isolating the opcode measures only it (a large but + *explainable* re-pin — see Re-pinning). +- **Wrap** it: `CodeGasMeasure(code=call_code, extra_stack_items=N, sstore_key=K)`. + It self-calibrates (subtracts its own `GAS` ops and `overhead_cost`) so the + stored value is the opcode's real cost. `extra_stack_items` = items the + measured code leaves on the stack (`CREATE`/`CALL` leave 1) — wrong value + corrupts the result. `sstore_key` = the slot the post asserts. +- **`extra_stack_items=1` silently discards a call's success flag — keep it + observable.** `CodeGasMeasure` SWAP/POPs the extra item, and gas alone + cannot replace it: a wrongly *failed* call refunds the child gas + stipend, + so it measures identically to a *success* into an empty callee, and for + `CALLCODE`/`DELEGATECALL` no balance moves either — the whole post-state is + then blind to the failure. When the measured op is a call whose success is + not otherwise observable, fold the flag into the measured window: + `store_code = Op.SSTORE(flag_slot, call_code, key_warm=False, + original_value=0, new_value=1)` with `extra_stack_items=0`, assert + `flag_slot: 1` in the post, and expect `store_code.gas_cost(fork)` (the + SSTORE's cost is now part of the measurement — and a failed call would + store 0, shifting the measured gas too, so the failure is doubly loud). + Validated on `test_non_zero_value`. +- **Apply opcode metadata from the test's context** so `gas_cost(fork)` is + correct (see `docs/writing_tests/opcode_metadata.md`). For `CALL`: + `address_warm` (is the target pre-accessed?), `value_transfer` (value > 0?), + `account_new` (target absent/empty and receiving value → created?). Use + `pre.nonexistent_account()` for a target that must stay **cold + non-existent** + so `account_new` holds — a `fund_eoa()` target already exists (warm/created) and + would change the cost. + - For `CREATE`/`CREATE2`: `new_memory_size` (the init-code window the offset/ + size operands touch, e.g. `size=0x20` → `new_memory_size=0x20`) **and** + `init_code_size` (drives the EIP-3860 per-word cost, Shanghai+). Omitting + `init_code_size` silently under-predicts by `CODE_INIT_PER_WORD * + ceil(size/32)` (2/word) — a small, easily-missed miss. `CREATE` leaves the + created address on the stack → `extra_stack_items=1`. + - **A runtime address threaded via `SLOAD`** (the create-then-call idiom: + store `CREATE`'s result, then `CALL(address=Op.SLOAD(slot))`) must mark that + `SLOAD` `key_warm=True` — the slot was just written so it is warm at runtime, + but the metadata default is cold and `gas_cost(fork)` would over-predict by + `cold − warm` (2000). An account freshly made by `CREATE` is **warm + already + existing**: `address_warm=True, account_new=False` on the following `CALL`. +- **Express the expected value dynamically** from the same metadata-bearing + variable: `call_code.gas_cost(fork)` (add `fork: Fork`). Both the bytecode and + the expectation are now fork-aware. + +**CALL value-transfer stipend.** A value-bearing `CALL` whose callee consumes +nothing (empty account / EOA) measures `gas_cost(fork) - +fork.gas_costs().CALL_STIPEND`: `gas_cost` counts the full value cost, but the +2300 stipend is forwarded to the callee and returned unused. Confirm the +`- CALL_STIPEND` holds on *every* fork (it is a fork-stable relationship, not a +coincidence). + +**EIP-8037 state-gas reservoir — critical.** Omitting `gas_limit` (step 2) on an +EIP-8037 fork *maxes the state-gas reservoir*, so state gas (e.g. account +creation) is **not** charged against what the `GAS` opcode sees — the measurement +silently loses it (observed 192921 → 9321) and only the future fork breaks. Fix: +keep `gas_limit` omitted **and** add an explicit `state_gas_reservoir=0` to the +`Transaction`. That pins the gas limit to exactly the cap (no reservoir) so state +gas is charged and measurable, and is a no-op on pre-EIP-8037 forks (a *positive* +reservoir there raises; `0` does not, and it must be set explicitly — the default +is treated as "unset"). This keeps a `CodeGasMeasure` test clean (no magic +`gas_limit`) yet correct on Amsterdam. + +**Absolute `GAS` readings are unsalvageable — convert to a delta.** A test that +stores a *raw* `GAS` value (not a `SUB(before, GAS)` delta) — e.g. `SSTORE(0, +GAS)` right after entry — pins `gas_limit - intrinsic - overhead`. Amsterdam +re-priced the **intrinsic transaction cost** (EIP-2780: base 21000 → 15000), so +that stored value shifts by a fixed amount (observed 578998 → 584998, a 6000 +jump) *independent of any state gas* — `state_gas_reservoir=0` does **not** fix +it. The only robust move is to stop storing absolute readings: wrap the measured +op in `CodeGasMeasure` (which stores the *delta* between two `GAS` reads, immune +to intrinsic) and assert `code.gas_cost(fork)`. A legacy `[[0]](GAS) … +[[100]](GAS)` snapshot pair *is* such a delta in disguise — the pair brackets one +operation (e.g. a `CREATE`); collapse it to a single `CodeGasMeasure` around that +op and drop both raw slots. Validated on the `CREATE_EmptyContract*` family. + +**Decompose the constant empirically** when no single helper applies (throwaway +script against the fork): pin each term to the known-good number, then assemble. +Map terms to fork-derived helpers: opcode base+pushes → `bytecode.gas_cost(fork)`; +memory growth → `fork.memory_expansion_gas_calculator()(new_bytes=, +previous_bytes=)`; EIP-3860 init-code words → `fork.gas_costs().CODE_INIT_PER_WORD +* ceil(size/32)`. You can also call `.gas_cost` / `.regular_cost` / `.state_cost` +on exactly the measured bytecode. + +**Nested / callee-side measurements.** When the measured op is a `CALL` whose +callee does real work, the measured cost = `call_code.gas_cost(fork) + +callee_code.gas_cost(fork)` (the CALL's own cost plus what the callee consumed). +Attach the callee's opcode metadata (e.g. SSTORE `key_warm`/`original_value`/ +`new_value`) so its `gas_cost` is right, and decompose against the callee's +*actual* bytecode rather than a reconstruction — a value supplied by `GAS` costs +2, not a `PUSH`'s 3, and that off-by-3 is a real trap. A callee-side gas snapshot +(`SSTORE(k, GAS)`) stores `forward_gas - Op.GAS.gas_cost(fork)`. Derive the +forwarded gas dynamically — `forward_gas = callee_store.gas_cost(fork) + buffer` +— rather than a magic number; under EIP-8037 a cold zero->non-zero SSTORE can +cost ~100k, so a fixed value is both fork-fragile and brittle. (Size the SSTORE +with a placeholder `new_value`: its cost depends only on the zero->non-zero +transition, not the magnitude — which also breaks the `forward_gas`/`new_value` +circularity.) Set `state_gas_reservoir=0` so the state gas is captured. +Validated on `test_raw_call_gas`. + +**Measuring forwarded gas / the EIP-150 63/64 rule (the `*_gas_ask` shape).** +Ported fillers probe "how much gas does a subcall receive when it asks for more +than is available" by pinning an absolute forwarded amount — fork-fragile, +because "available" moves with the EIP-2780 intrinsic change. Make it robust +with three moves: (1) **cap the caller frame's gas to a known budget** with an +*outer* call (`entry → CALL(gas=CALLER_GAS) → caller`); because `CALLER_GAS` is +far below the outer frame's 63/64, the caller receives exactly `CALLER_GAS` +independent of the tx gas limit. (2) **Return the observed `GAS` up the stack** +(`MSTORE(0, GAS) + RETURN(0, 32)` in the callee, `RETURN` again in the caller, +`SSTORE` only in the top frame) instead of `SSTORE`-ing in a lower frame — +avoids the EIP-8037 state-gas trap. (3) **Derive the expectation from the fork:** +``` +available = CALLER_GAS - caller_call_code.gas_cost(fork) +forwarded = available - available // 64 # NOT available * 63 // 64 +expected_gas = forwarded + stipend - Op.GAS.gas_cost(fork) +``` +where `stipend = fork.gas_costs().CALL_STIPEND` for a value-bearing call (0 +otherwise). **The `// 64` form is the trap:** `available - available // 64` and +`available * 63 // 64` differ by exactly 1 whenever `available % 64 != 0` (the +EVM uses the former). One parametrize over `(opcode, value, memory)` covers the +whole CALL/CALLCODE/DELEGATECALL family; floor **Berlin** (the call metadata). +Validated on `test_raw_call_gas_ask` (10 RawCall*GasAsk fillers). + +**Error paths charge regular gas only — assert `regular_cost(fork)`.** A failed +`CREATE`/`CALL` still charges its regular costs (base, memory, init-code words) +but creates no account, so **no state gas is charged** under EIP-8037. For a +success/failure parametrize, that is exactly the `gas_cost(fork)` vs +`regular_cost(fork)` split: success measures `code.gas_cost(fork)` (regular + +state), failure measures `code.regular_cost(fork)` (regular only). On pre-8037 +forks `state_cost` is 0 so the two coincide — one expression, correct on every +fork. Drive a `CREATE` down the balance-failure path by funding the creator one +wei short of the transferred `value` (`balance = value - 1`); the created +address is then `Account.NONEXISTENT`. Validated end-to-end on +`test_raw_create_gas` (6 RawCreate*Gas fillers consolidated). + +### 11. Lower `valid_from` to extend coverage +The ported `valid_from` (often `Cancun`) is usually higher than necessary — lower +it to widen coverage. Find the true floor empirically: temporarily delete the +`valid_from` marker and fill with no `--fork` (the framework then runs from +Frontier up); the earliest fork that *passes* is your floor. Set +`@pytest.mark.valid_from("")` — the marker is mandatory, so this is a +lowering, never a true removal. +- **Gas tests floor at the EIP that introduced their metadata.** A test using + `address_warm` / cold-access metadata + `gas_cost(fork)` is only valid from + **Berlin (EIP-2929)**: earlier forks have no warm/cold distinction, so + `gas_cost` over-predicts by `cold − flat` (2600 − 700 = 1900) and every + pre-Berlin fork fails the measurement. Same shape elsewhere — EIP-3860 + init-code metering floors at Shanghai, etc. The floor is whichever EIP the + test's behavior/metadata depends on, which the empirical sweep reveals directly. +- **Behavioral floors show up as non-gas mismatches in the sweep.** A CREATE + test asserting the created account has `nonce=1` floors at **SpuriousDragon + (EIP-161)** — earlier forks start contract nonces at 0, so Frontier/Homestead/ + TangerineWhistle fail on the nonce, not the gas. Read *what* the sweep's + earliest-passing fork is gated on; it is not always a gas-schedule change. +- **A `bad v` / `INVALID_SIGNATURE_VRS` failure is a signature floor, not a + real one — don't raise `valid_from` for it.** The default `Transaction` is + EIP-155-protected, which pre-SpuriousDragon forks reject. Instead set + `protected=fork.supports_protected_txs()` (add `fork: Fork`): it goes + unprotected on Frontier/Homestead/TangerineWhistle and protected from + SpuriousDragon on. This keeps the floor at the *behavior's* real EIP (e.g. + Homestead for `DELEGATECALL`) instead of masking it at SpuriousDragon. + Validated on `test_delegatecall_emptycontract`. + +## Re-pinning expected values + +When a measurement rewrite (step 10) or bytecode change shifts a stored value, +the workflow is: change → `fill` → read the `KeyValueMismatchError` (`want … got +…`) → update the expected value to the `got` → `fill` again. +**Sanity gate:** the shift must be *explainable* — either small (the gas of +removed framing ops) or large-but-precisely-accounted (e.g. isolating an opcode +in `CodeGasMeasure` drops a cold `SSTORE` ~22100 the legacy window had bundled). +A jump you cannot account for means the rewrite changed *what* is being measured +— stop and investigate, don't just paste the number. + +## `@manually-enhanced` markers + +A docstring `@manually-enhanced: Do not overwrite` marks a deliberate prior fix. +Respect it by default. It may be removed only when a *better* enhancement makes +the workaround it documents obsolete (e.g. maxing out gas removes a per-fork gas +budget hack) — and only under explicit direction. +**Add the marker as the closing step** once a test's enhancements are intentional +(genuinely-verifying post, dynamic addresses/gas) so future auto-porting won't +regress them; briefly state what was enhanced. Place it in the **module +docstring**, after the `Ported from:` block (blank line before), as a single +line: `@manually-enhanced: Do not overwrite. .` (keep it ≤79 +chars). + +## Known gaps (extend me) + +Not yet covered by a validated walkthrough; figure out and append when hit: +- Tests where **more than one** parametrize index varies at once (a genuine 2-D + `data` × `value`/`gas` matrix) — single-axis `d`/`g`/`v` discrimination is now + handled (step 7), but a multi-axis post is not yet exercised. +- Multi-block / `blockchain_test` ported tests. + +## Finishing + +**Remove the skip-list entry.** Once the test passes on the future fork, delete +its line from `tests/ported_static/amsterdam_skip_list.txt` and decrement both +its per-directory count header (`# stXxx (N)`) and the `# Total entries:` count. +Confirm with a full-range fill (`--fork` omitted) with the entry gone — that is +the definition of done. + +**Final sweep checklist** — each of these has been missed in practice; check +them one by one before calling the test done: +- `@pytest.mark.pre_alloc_mutable` removed if no hardcoded addresses/ + nonces/`pre[...]` remain (it silently skips the test in execute mode). +- No machine-port placeholder docstrings left (`Test_.`) — the + module and function docstrings say what the test verifies, in + imperative mood ("Verify/Measure ...", not "Gas cost of ..."). +- Docstrings re-read against the *final* architecture: collapsing a + delivery CALL or moving value onto the tx makes "inherited from the + enclosing CALL"-style prose stale. +- Inline magic operands named (`FORWARDED_GAS`, `GAS_SLOT`, ...) — + consistent with sibling files in the same directory. +- Pinned budget constants guarded: anything like + `available = BUDGET - code.gas_cost(fork)` gets an + `assert available > 0, ...` so a future repricing that outgrows the + budget fails loudly at fill time instead of producing a garbage + expectation. +- The old post's slots all accounted for (see step 8's "count its + slots"). + +When done, offer to run `/lint`. Note that pydantic coercion warnings +(`dict→Alloc/Storage`, `Bytecode→Bytes`, unfilled optional `Transaction` params) +are false positives from the type checker, not real issues. diff --git a/CLAUDE.md b/CLAUDE.md index 806edfc565b..e29e0084072 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -42,6 +42,7 @@ When reviewing PRs that implement or test EIPs: ## When to Use Skills - Writing or modifying tests → run `/write-test` first +- Cleaning up or future-proofing a `tests/ported_static/` test → run `/enhance-ported-test` first - Writing or modifying pytester-based plugin tests → run `/pytester` first - Filling test fixtures → run `/fill-tests` first - Implementing an EIP or modifying fork code in `src/` → run `/implement-eip` first @@ -55,6 +56,7 @@ When reviewing PRs that implement or test EIPs: ## Available Skills - `/write-test` — test writing patterns, fixtures, markers, bytecode helpers +- `/enhance-ported-test` — ordered methodology to clean up & future-proof `tests/ported_static/` tests - `/pytester` — pytester execution modes, isolation, output handling for plugin tests - `/fill-tests` — `fill` CLI reference, flags, debugging, benchmark tests - `/implement-eip` — fork structure, import rules, adding opcodes/precompiles/tx types diff --git a/tests/ported_static/amsterdam_skip_list.txt b/tests/ported_static/amsterdam_skip_list.txt index 4423a666f35..7aba6f38bb0 100644 --- a/tests/ported_static/amsterdam_skip_list.txt +++ b/tests/ported_static/amsterdam_skip_list.txt @@ -8,7 +8,7 @@ # Entries are substring-matched against each pytest nodeid (after # stripping the fixture-format suffix in conftest.py). # -# Total entries: 258 +# Total entries: 153 # stAttackTest (1) stAttackTest/test_crashing_transaction.py::test_crashing_transaction[fork_Amsterdam] @@ -73,7 +73,7 @@ stCreate2/test_revert_depth_create_address_collision_berlin.py::test_revert_dept stCreate2/test_revert_depth_create_address_collision_berlin.py::test_revert_depth_create_address_collision_berlin[fork_Amsterdam-d1-g1-v0] stCreate2/test_revert_depth_create_address_collision_berlin.py::test_revert_depth_create_address_collision_berlin[fork_Amsterdam-d1-g1-v1] -# stCreateTest (40) +# stCreateTest (36) stCreateTest/test_create_address_warm_after_fail.py::test_create_address_warm_after_fail[fork_Amsterdam-create-0xef-v1] stCreateTest/test_create_address_warm_after_fail.py::test_create_address_warm_after_fail[fork_Amsterdam-create-contructor-revert-v1] stCreateTest/test_create_address_warm_after_fail.py::test_create_address_warm_after_fail[fork_Amsterdam-create-high-nonce-v1] @@ -90,10 +90,6 @@ stCreateTest/test_create_address_warm_after_fail.py::test_create_address_warm_af stCreateTest/test_create_e_contract_create_ne_contract_in_init_oog_tr.py::test_create_e_contract_create_ne_contract_in_init_oog_tr[fork_Amsterdam--g0] stCreateTest/test_create_e_contract_create_ne_contract_in_init_oog_tr.py::test_create_e_contract_create_ne_contract_in_init_oog_tr[fork_Amsterdam--g1] stCreateTest/test_create_e_contract_then_call_to_non_existent_acc.py::test_create_e_contract_then_call_to_non_existent_acc[fork_Amsterdam] -stCreateTest/test_create_empty_contract.py::test_create_empty_contract[fork_Amsterdam] -stCreateTest/test_create_empty_contract_and_call_it_0wei.py::test_create_empty_contract_and_call_it_0wei[fork_Amsterdam] -stCreateTest/test_create_empty_contract_and_call_it_1wei.py::test_create_empty_contract_and_call_it_1wei[fork_Amsterdam] -stCreateTest/test_create_empty_contract_with_balance.py::test_create_empty_contract_with_balance[fork_Amsterdam] stCreateTest/test_create_empty_contract_with_storage.py::test_create_empty_contract_with_storage[fork_Amsterdam] stCreateTest/test_create_empty_contract_with_storage_and_call_it_0wei.py::test_create_empty_contract_with_storage_and_call_it_0wei[fork_Amsterdam] stCreateTest/test_create_empty_contract_with_storage_and_call_it_1wei.py::test_create_empty_contract_with_storage_and_call_it_1wei[fork_Amsterdam] @@ -115,12 +111,10 @@ stCreateTest/test_transaction_collision_to_empty_but_code.py::test_transaction_c stCreateTest/test_transaction_collision_to_empty_but_nonce.py::test_transaction_collision_to_empty_but_nonce[fork_Amsterdam--g1-v0] stCreateTest/test_transaction_collision_to_empty_but_nonce.py::test_transaction_collision_to_empty_but_nonce[fork_Amsterdam--g1-v1] -# stDelegatecallTestHomestead (6) +# stDelegatecallTestHomestead (4) stDelegatecallTestHomestead/test_call1024_oog.py::test_call1024_oog[fork_Amsterdam--g0] stDelegatecallTestHomestead/test_call1024_oog.py::test_call1024_oog[fork_Amsterdam--g1] -stDelegatecallTestHomestead/test_deleagate_call_after_value_transfer.py::test_deleagate_call_after_value_transfer[fork_Amsterdam] stDelegatecallTestHomestead/test_delegatecall1024_oog.py::test_delegatecall1024_oog[fork_Amsterdam] -stDelegatecallTestHomestead/test_delegatecall_emptycontract.py::test_delegatecall_emptycontract[fork_Amsterdam] stDelegatecallTestHomestead/test_delegatecall_in_initcode_to_existing_contract.py::test_delegatecall_in_initcode_to_existing_contract[fork_Amsterdam] # stEIP150Specific (7) @@ -132,104 +126,13 @@ stEIP150Specific/test_transaction64_rule_d64e0.py::test_transaction64_rule_d64e0 stEIP150Specific/test_transaction64_rule_d64m1.py::test_transaction64_rule_d64m1[fork_Amsterdam] stEIP150Specific/test_transaction64_rule_d64p1.py::test_transaction64_rule_d64p1[fork_Amsterdam] -# stEIP150singleCodeGasPrices (28) +# stEIP150singleCodeGasPrices (2) stEIP150singleCodeGasPrices/test_gas_cost.py::test_gas_cost[fork_Amsterdam-d40] stEIP150singleCodeGasPrices/test_gas_cost_berlin.py::test_gas_cost_berlin[fork_Amsterdam-d40] -stEIP150singleCodeGasPrices/test_raw_call_code_gas.py::test_raw_call_code_gas[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_call_code_gas_ask.py::test_raw_call_code_gas_ask[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_call_code_gas_memory.py::test_raw_call_code_gas_memory[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_call_code_gas_memory_ask.py::test_raw_call_code_gas_memory_ask[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_call_code_gas_value_transfer.py::test_raw_call_code_gas_value_transfer[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_call_code_gas_value_transfer_ask.py::test_raw_call_code_gas_value_transfer_ask[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_call_code_gas_value_transfer_memory.py::test_raw_call_code_gas_value_transfer_memory[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_call_code_gas_value_transfer_memory_ask.py::test_raw_call_code_gas_value_transfer_memory_ask[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_call_gas.py::test_raw_call_gas[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_call_gas_ask.py::test_raw_call_gas_ask[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_call_gas_value_transfer.py::test_raw_call_gas_value_transfer[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_call_gas_value_transfer_ask.py::test_raw_call_gas_value_transfer_ask[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_call_gas_value_transfer_memory.py::test_raw_call_gas_value_transfer_memory[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_call_gas_value_transfer_memory_ask.py::test_raw_call_gas_value_transfer_memory_ask[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_call_memory_gas.py::test_raw_call_memory_gas[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_call_memory_gas_ask.py::test_raw_call_memory_gas_ask[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_create_fail_gas_value_transfer.py::test_raw_create_fail_gas_value_transfer[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_create_fail_gas_value_transfer2.py::test_raw_create_fail_gas_value_transfer2[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_create_gas.py::test_raw_create_gas[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_create_gas_memory.py::test_raw_create_gas_memory[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_create_gas_value_transfer.py::test_raw_create_gas_value_transfer[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_create_gas_value_transfer_memory.py::test_raw_create_gas_value_transfer_memory[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_delegate_call_gas.py::test_raw_delegate_call_gas[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_delegate_call_gas_ask.py::test_raw_delegate_call_gas_ask[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_delegate_call_gas_memory.py::test_raw_delegate_call_gas_memory[fork_Amsterdam] -stEIP150singleCodeGasPrices/test_raw_delegate_call_gas_memory_ask.py::test_raw_delegate_call_gas_memory_ask[fork_Amsterdam] - -# stEIP1559 (1) -stEIP1559/test_sender_balance.py::test_sender_balance[fork_Amsterdam] # stEIP158Specific (1) stEIP158Specific/test_exp_empty.py::test_exp_empty[fork_Amsterdam] -# stEIP3855_push0 (3) -stEIP3855_push0/test_push0_gas.py::test_push0_gas[fork_Amsterdam] -stEIP3855_push0/test_push0_gas2.py::test_push0_gas2[fork_Amsterdam-use_push0] -stEIP3855_push0/test_push0_gas2.py::test_push0_gas2[fork_Amsterdam-use_push1_00] - -# stEIP5656_MCOPY (55) -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src0_size0-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src0_size0-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src0_size1-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src0_size1-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src0_size31-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src0_size31-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src0_size32-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src0_size32-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src0_size33-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src0_size33-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src0_size44767-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src0_size44767-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src0_size44768-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src0_size44768-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src0_size44769-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src0_size44769-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src1_size0-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src1_size0-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src1_size1-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src1_size1-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src1_size31-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src1_size31-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src1_size32-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src1_size32-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src1_size33-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src1_size33-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src1_size44767-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src1_size44768-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src1_size44769-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src31_size0-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src31_size0-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src31_size1-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src31_size1-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src31_size31-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src31_size31-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src31_size32-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src31_size32-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src31_size33-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src31_size33-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src31_size44767-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src31_size44768-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src31_size44769-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src32_size0-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src32_size0-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src32_size1-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src32_size1-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src32_size31-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src32_size31-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src32_size32-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src32_size32-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src32_size33-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src32_size33-g1] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src32_size44767-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src32_size44768-g0] -stEIP5656_MCOPY/test_mcopy_copy_cost.py::test_mcopy_copy_cost[fork_Amsterdam-src32_size44769-g0] - # stHomesteadSpecific (1) stHomesteadSpecific/test_contract_creation_oo_gdont_leave_empty_contract_via_transaction.py::test_contract_creation_oo_gdont_leave_empty_contract_via_transaction[fork_Amsterdam] @@ -248,24 +151,10 @@ stMemExpandingEIP150Calls/test_call_goes_oog_on_second_level_with_mem_expanding_ stMemExpandingEIP150Calls/test_create_and_gas_inside_create_with_mem_expanding_calls.py::test_create_and_gas_inside_create_with_mem_expanding_calls[fork_Amsterdam] stMemExpandingEIP150Calls/test_new_gas_price_for_codes_with_mem_expanding_calls.py::test_new_gas_price_for_codes_with_mem_expanding_calls[fork_Amsterdam] -# stMemoryTest (4) -stMemoryTest/test_call_data_copy_offset.py::test_call_data_copy_offset[fork_Amsterdam] -stMemoryTest/test_code_copy_offset.py::test_code_copy_offset[fork_Amsterdam] +# stMemoryTest (2) stMemoryTest/test_oog.py::test_oog[fork_Amsterdam-success14] stMemoryTest/test_oog.py::test_oog[fork_Amsterdam-success15] -# stNonZeroCallsTest (10) -stNonZeroCallsTest/test_non_zero_value_call.py::test_non_zero_value_call[fork_Amsterdam] -stNonZeroCallsTest/test_non_zero_value_call_to_empty_paris.py::test_non_zero_value_call_to_empty_paris[fork_Amsterdam] -stNonZeroCallsTest/test_non_zero_value_call_to_one_storage_key_paris.py::test_non_zero_value_call_to_one_storage_key_paris[fork_Amsterdam] -stNonZeroCallsTest/test_non_zero_value_callcode.py::test_non_zero_value_callcode[fork_Amsterdam] -stNonZeroCallsTest/test_non_zero_value_callcode_to_empty_paris.py::test_non_zero_value_callcode_to_empty_paris[fork_Amsterdam] -stNonZeroCallsTest/test_non_zero_value_callcode_to_one_storage_key_paris.py::test_non_zero_value_callcode_to_one_storage_key_paris[fork_Amsterdam] -stNonZeroCallsTest/test_non_zero_value_delegatecall.py::test_non_zero_value_delegatecall[fork_Amsterdam] -stNonZeroCallsTest/test_non_zero_value_delegatecall_to_empty_paris.py::test_non_zero_value_delegatecall_to_empty_paris[fork_Amsterdam] -stNonZeroCallsTest/test_non_zero_value_delegatecall_to_non_non_zero_balance.py::test_non_zero_value_delegatecall_to_non_non_zero_balance[fork_Amsterdam] -stNonZeroCallsTest/test_non_zero_value_delegatecall_to_one_storage_key_paris.py::test_non_zero_value_delegatecall_to_one_storage_key_paris[fork_Amsterdam] - # stRefundTest (7) stRefundTest/test_refund50_2.py::test_refund50_2[fork_Amsterdam] stRefundTest/test_refund50percent_cap.py::test_refund50percent_cap[fork_Amsterdam] @@ -300,11 +189,7 @@ stSolidityTest/test_recursive_create_contracts.py::test_recursive_create_contrac stSolidityTest/test_test_contract_interaction.py::test_test_contract_interaction[fork_Amsterdam] stSolidityTest/test_test_contract_suicide.py::test_test_contract_suicide[fork_Amsterdam] -# stSpecialTest (1) -stSpecialTest/test_make_money.py::test_make_money[fork_Amsterdam] - -# stStaticCall (4) -stStaticCall/test_static_call_value_inherit_from_call.py::test_static_call_value_inherit_from_call[fork_Amsterdam] +# stStaticCall (3) stStaticCall/test_static_create_empty_contract_and_call_it_0wei.py::test_static_create_empty_contract_and_call_it_0wei[fork_Amsterdam] stStaticCall/test_static_create_empty_contract_with_storage_and_call_it_0wei.py::test_static_create_empty_contract_with_storage_and_call_it_0wei[fork_Amsterdam] stStaticCall/test_static_execute_call_that_ask_fore_gas_then_trabsaction_has.py::test_static_execute_call_that_ask_fore_gas_then_trabsaction_has[fork_Amsterdam-d0] diff --git a/tests/ported_static/stArgsZeroOneBalance/test_add_non_const.py b/tests/ported_static/stArgsZeroOneBalance/test_add_non_const.py index 0a24f5ffbc1..c0b91379c99 100644 --- a/tests/ported_static/stArgsZeroOneBalance/test_add_non_const.py +++ b/tests/ported_static/stArgsZeroOneBalance/test_add_non_const.py @@ -1,116 +1,67 @@ """ -Test_add_non_const. +Verify ADD over non-constant operands: the contract adds its own balance to +itself, where that balance equals the value sent by the transaction. Ported from: state_tests/stArgsZeroOneBalance/addNonConstFiller.yml + +@manually-enhanced: Do not overwrite. Parametrized on the transaction value +(the real discriminator), the self-referential balance reads use +`BALANCE(ADDRESS)` instead of a hardcoded address, and the post asserts the +`2 * tx_value` result directly; env/gas boilerplate removed. A canary slot +keeps the `tx_value=0` arm observable (its result slot stays zero). """ import pytest from execution_testing import ( Account, - Address, Alloc, - Bytes, - Environment, + Fork, StateTestFiller, Transaction, ) -from execution_testing.forks import Fork -from execution_testing.specs.static_state.expect_section import ( - resolve_expect_post, -) from execution_testing.vm import Op REFERENCE_SPEC_GIT_PATH = "N/A" REFERENCE_SPEC_VERSION = "N/A" +CANARY = 0xC0DE + @pytest.mark.ported_from( ["state_tests/stArgsZeroOneBalance/addNonConstFiller.yml"], ) -@pytest.mark.valid_from("Cancun") -@pytest.mark.parametrize( - "d, g, v", - [ - pytest.param( - 0, - 0, - 0, - id="-v0", - ), - pytest.param( - 0, - 0, - 1, - id="-v1", - ), - ], -) -@pytest.mark.pre_alloc_mutable +@pytest.mark.valid_from("Frontier") +@pytest.mark.parametrize("tx_value", [0, 1]) def test_add_non_const( state_test: StateTestFiller, pre: Alloc, fork: Fork, - d: int, - g: int, - v: int, + tx_value: int, ) -> None: - """Test_add_non_const.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xDE0B6B3A7640000) + """Add the contract's own balance to itself and store the result.""" + sender = pre.fund_eoa() - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=1000000, - ) - - # Source: lll - # { [[ 0 ]](ADD (BALANCE ) (BALANCE )) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 + # ADD with non-constant operands: the contract's own balance added to + # itself. The balance equals the value sent by the transaction. The + # canary proves the code ran even when the stored result is zero. + target = pre.deploy_contract( code=Op.SSTORE( key=0x0, - value=Op.ADD( - Op.BALANCE(address=0xF1722FE346FA35E045DE07E47CF6AF9BAE8ADE0A), - Op.BALANCE(address=0xF1722FE346FA35E045DE07E47CF6AF9BAE8ADE0A), - ), + value=Op.ADD(Op.BALANCE(Op.ADDRESS), Op.BALANCE(Op.ADDRESS)), ) + + Op.SSTORE(key=0x1, value=CANARY) + Op.STOP, - nonce=0, - address=Address(0xF1722FE346FA35E045DE07E47CF6AF9BAE8ADE0A), # noqa: E501 ) - expect_entries_: list[dict] = [ - { - "indexes": {"data": -1, "gas": -1, "value": 0}, - "network": [">=Cancun"], - "result": {target: Account(storage={0: 0})}, - }, - { - "indexes": {"data": -1, "gas": -1, "value": 1}, - "network": [">=Cancun"], - "result": {target: Account(storage={0: 2})}, - }, - ] - - post, _exc = resolve_expect_post(expect_entries_, d, g, v, fork) - - tx_data = [ - Bytes(""), - ] - tx_gas = [400000] - tx_value = [0, 1] + # ADD(BALANCE, BALANCE) over a balance equal to the sent value. + post = {target: Account(storage={0: 2 * tx_value, 1: CANARY})} tx = Transaction( sender=sender, to=target, - data=tx_data[d], - gas_limit=tx_gas[g], - value=tx_value[v], - error=_exc, + value=tx_value, + protected=fork.supports_protected_txs(), ) - state_test(env=env, pre=pre, post=post, tx=tx) + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stCreateTest/test_create_empty_contract.py b/tests/ported_static/stCreateTest/test_create_empty_contract.py index 9790df29d6c..469d1a3a521 100644 --- a/tests/ported_static/stCreateTest/test_create_empty_contract.py +++ b/tests/ported_static/stCreateTest/test_create_empty_contract.py @@ -1,17 +1,20 @@ """ -Test_create_empty_contract. +Test CREATE of an empty contract and measure the CREATE gas cost. Ported from: state_tests/stCreateTest/CREATE_EmptyContractFiller.json +state_tests/stCreateTest/CREATE_EmptyContractWithBalanceFiller.json + +@manually-enhanced: Do not overwrite. CREATE gas via CodeGasMeasure; dynamic +address + fork-derived cost; empty/with-balance folded into one parametrize. """ import pytest from execution_testing import ( Account, - Address, Alloc, - Bytes, - Environment, + CodeGasMeasure, + Fork, StateTestFiller, Transaction, compute_create_address, @@ -21,56 +24,61 @@ REFERENCE_SPEC_GIT_PATH = "N/A" REFERENCE_SPEC_VERSION = "N/A" +GAS_SLOT = 0x64 + @pytest.mark.ported_from( - ["state_tests/stCreateTest/CREATE_EmptyContractFiller.json"], + [ + "state_tests/stCreateTest/CREATE_EmptyContractFiller.json", + "state_tests/stCreateTest/CREATE_EmptyContractWithBalanceFiller.json", + ], +) +@pytest.mark.valid_from("SpuriousDragon") +@pytest.mark.parametrize( + "create_value", + [ + pytest.param(0, id="empty_contract"), + pytest.param(1, id="with_balance"), + ], ) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable def test_create_empty_contract( state_test: StateTestFiller, pre: Alloc, + fork: Fork, + create_value: int, ) -> None: - """Test_create_empty_contract.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - contract_0 = Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, + """CREATE an empty contract (empty init code) and measure its gas.""" + # CREATE with size=0x20 over never-written memory runs 32 zero bytes as + # init code (STOP on the first byte), depositing no code -> an empty + # account with nonce 1 (and the transferred value as balance). + create_code = Op.CREATE( + value=create_value, + offset=0x0, + size=0x20, + new_memory_size=0x20, + init_code_size=0x20, ) - - # Source: lll - # { [[0]](GAS) [[1]] (CREATE 0 0 32) [[100]] (GAS) } - contract_0 = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x0, value=Op.GAS) - + Op.SSTORE(key=0x1, value=Op.CREATE(value=0x0, offset=0x0, size=0x20)) - + Op.SSTORE(key=0x64, value=Op.GAS) - + Op.STOP, - nonce=0, + contract = pre.deploy_contract( + code=CodeGasMeasure( + code=create_code, + extra_stack_items=1, + sstore_key=GAS_SLOT, + ), + balance=create_value, ) tx = Transaction( - sender=sender, - to=contract_0, - data=Bytes(""), - gas_limit=600000, + sender=pre.fund_eoa(), + to=contract, + state_gas_reservoir=0, ) + created = compute_create_address(address=contract, nonce=1) post = { - compute_create_address(address=contract_0, nonce=0): Account(nonce=1), - contract_0: Account( - storage={ - 0: 0x8D5B6, - 1: compute_create_address(address=contract_0, nonce=0), - 100: 0x7ABF8, - }, + contract: Account( + storage={GAS_SLOT: create_code.gas_cost(fork)}, balance=0 ), + created: Account(nonce=1, balance=create_value), } - state_test(env=env, pre=pre, post=post, tx=tx) + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stCreateTest/test_create_empty_contract_and_call_it.py b/tests/ported_static/stCreateTest/test_create_empty_contract_and_call_it.py new file mode 100644 index 00000000000..cb9c8b21fb7 --- /dev/null +++ b/tests/ported_static/stCreateTest/test_create_empty_contract_and_call_it.py @@ -0,0 +1,111 @@ +""" +Test CREATE of an empty contract followed by a CALL to it, measuring the +CALL gas cost. + +Ported from: +state_tests/stCreateTest/CREATE_EmptyContractAndCallIt_0weiFiller.json +state_tests/stCreateTest/CREATE_EmptyContractAndCallIt_1weiFiller.json + +@manually-enhanced: Do not overwrite. CALL gas via CodeGasMeasure; dynamic +address (runtime SLOAD); 0wei/1wei folded into one parametrize. +""" + +import pytest +from execution_testing import ( + Account, + Alloc, + CodeGasMeasure, + Fork, + StateTestFiller, + Transaction, + compute_create_address, +) +from execution_testing.vm import Op + +REFERENCE_SPEC_GIT_PATH = "N/A" +REFERENCE_SPEC_VERSION = "N/A" + +ADDRESS_SLOT = 0x1 +GAS_SLOT = 0x64 + +FORWARDED_GAS = 0xEA60 + + +@pytest.mark.ported_from( + [ + "state_tests/stCreateTest/CREATE_EmptyContractAndCallIt_0weiFiller.json", # noqa: E501 + "state_tests/stCreateTest/CREATE_EmptyContractAndCallIt_1weiFiller.json", # noqa: E501 + ], +) +@pytest.mark.valid_from("Berlin") +@pytest.mark.parametrize( + "call_value", + [ + pytest.param(0, id="0wei"), + pytest.param(1, id="1wei"), + ], +) +def test_create_empty_contract_and_call_it( + state_test: StateTestFiller, + pre: Alloc, + fork: Fork, + call_value: int, +) -> None: + """CREATE an empty contract, then CALL it and measure the CALL gas.""" + # CREATE over never-written memory deposits no code -> an empty account + # with nonce 1. Its address is stored so the CALL can target it at + # runtime (it is not known when the caller code is assembled). + create_code = Op.CREATE( + value=0x0, + offset=0x0, + size=0x20, + new_memory_size=0x20, + init_code_size=0x20, + ) + # The created account already exists (CREATE set its nonce) and is warm + # (CREATE accessed it), so the CALL is a warm call to an existing account. + call_code = Op.CALL( + gas=FORWARDED_GAS, + address=Op.SLOAD(key=ADDRESS_SLOT, key_warm=True), + value=call_value, + args_offset=0x0, + args_size=0x0, + ret_offset=0x0, + ret_size=0x0, + address_warm=True, + value_transfer=call_value > 0, + account_new=False, + ) + contract = pre.deploy_contract( + code=Op.SSTORE(key=ADDRESS_SLOT, value=create_code) + + CodeGasMeasure( + code=call_code, + extra_stack_items=1, + sstore_key=GAS_SLOT, + ), + balance=call_value, + ) + + tx = Transaction( + sender=pre.fund_eoa(), + to=contract, + state_gas_reservoir=0, + ) + + # A value-bearing CALL whose empty callee consumes nothing measures + # gas_cost minus the stipend (forwarded then returned unused). + stipend = fork.gas_costs().CALL_STIPEND if call_value else 0 + created = compute_create_address(address=contract, nonce=1) + post = { + contract: Account( + storage={ + ADDRESS_SLOT: created, + GAS_SLOT: call_code.gas_cost(fork) - stipend, + }, + balance=0, + ), + # The transferred value on the 1wei case proves the CALL executed. + created: Account(nonce=1, balance=call_value), + } + + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stCreateTest/test_create_empty_contract_and_call_it_0wei.py b/tests/ported_static/stCreateTest/test_create_empty_contract_and_call_it_0wei.py deleted file mode 100644 index b8efc72b9a5..00000000000 --- a/tests/ported_static/stCreateTest/test_create_empty_contract_and_call_it_0wei.py +++ /dev/null @@ -1,92 +0,0 @@ -""" -Test_create_empty_contract_and_call_it_0wei. - -Ported from: -state_tests/stCreateTest/CREATE_EmptyContractAndCallIt_0weiFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, - compute_create_address, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - ["state_tests/stCreateTest/CREATE_EmptyContractAndCallIt_0weiFiller.json"], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_create_empty_contract_and_call_it_0wei( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_create_empty_contract_and_call_it_0wei.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - contract_0 = Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[0]](GAS) [[1]] (CREATE 0 0 32) [[2]](GAS) [[3]] (CALL 60000 (SLOAD 1) 0 0 0 0 0) [[100]] (GAS) } # noqa: E501 - contract_0 = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x0, value=Op.GAS) - + Op.SSTORE(key=0x1, value=Op.CREATE(value=0x0, offset=0x0, size=0x20)) - + Op.SSTORE(key=0x2, value=Op.GAS) - + Op.SSTORE( - key=0x3, - value=Op.CALL( - gas=0xEA60, - address=Op.SLOAD(key=0x1), - value=0x0, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ), - ) - + Op.SSTORE(key=0x64, value=Op.GAS) - + Op.STOP, - nonce=0, - address=Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B), # noqa: E501 - ) - - tx = Transaction( - sender=sender, - to=contract_0, - data=Bytes(""), - gas_limit=600000, - ) - - post = { - contract_0: Account( - storage={ - 0: 0x8D5B6, - 1: compute_create_address(address=contract_0, nonce=0), - 2: 0x7ABF8, - 3: 1, - 100: 0x6FE6B, - }, - ), - compute_create_address(address=contract_0, nonce=0): Account(nonce=1), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stCreateTest/test_create_empty_contract_and_call_it_1wei.py b/tests/ported_static/stCreateTest/test_create_empty_contract_and_call_it_1wei.py deleted file mode 100644 index 1b35d16c1b5..00000000000 --- a/tests/ported_static/stCreateTest/test_create_empty_contract_and_call_it_1wei.py +++ /dev/null @@ -1,95 +0,0 @@ -""" -Test_create_empty_contract_and_call_it_1wei. - -Ported from: -state_tests/stCreateTest/CREATE_EmptyContractAndCallIt_1weiFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, - compute_create_address, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - ["state_tests/stCreateTest/CREATE_EmptyContractAndCallIt_1weiFiller.json"], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_create_empty_contract_and_call_it_1wei( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_create_empty_contract_and_call_it_1wei.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - contract_0 = Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[0]](GAS) [[1]] (CREATE 0 0 32) [[2]](GAS) [[3]](CALL 60000 (SLOAD 1) 1 0 0 0 0) [[100]] (GAS) } # noqa: E501 - contract_0 = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x0, value=Op.GAS) - + Op.SSTORE(key=0x1, value=Op.CREATE(value=0x0, offset=0x0, size=0x20)) - + Op.SSTORE(key=0x2, value=Op.GAS) - + Op.SSTORE( - key=0x3, - value=Op.CALL( - gas=0xEA60, - address=Op.SLOAD(key=0x1), - value=0x1, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ), - ) - + Op.SSTORE(key=0x64, value=Op.GAS) - + Op.STOP, - balance=1, - nonce=0, - address=Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B), # noqa: E501 - ) - - tx = Transaction( - sender=sender, - to=contract_0, - data=Bytes(""), - gas_limit=600000, - ) - - post = { - contract_0: Account( - storage={ - 0: 0x8D5B6, - 1: compute_create_address(address=contract_0, nonce=0), - 2: 0x7ABF8, - 3: 1, - 100: 0x6E43F, - }, - ), - compute_create_address(address=contract_0, nonce=0): Account( - balance=1, nonce=1 - ), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stCreateTest/test_create_empty_contract_with_balance.py b/tests/ported_static/stCreateTest/test_create_empty_contract_with_balance.py deleted file mode 100644 index 60862e9022b..00000000000 --- a/tests/ported_static/stCreateTest/test_create_empty_contract_with_balance.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -Test_create_empty_contract_with_balance. - -Ported from: -state_tests/stCreateTest/CREATE_EmptyContractWithBalanceFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, - compute_create_address, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - ["state_tests/stCreateTest/CREATE_EmptyContractWithBalanceFiller.json"], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_create_empty_contract_with_balance( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_create_empty_contract_with_balance.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - contract_0 = Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[0]](GAS) [[1]] (CREATE 1 0 32) [[100]] (GAS) } - contract_0 = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x0, value=Op.GAS) - + Op.SSTORE(key=0x1, value=Op.CREATE(value=0x1, offset=0x0, size=0x20)) - + Op.SSTORE(key=0x64, value=Op.GAS) - + Op.STOP, - balance=1, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=contract_0, - data=Bytes(""), - gas_limit=600000, - ) - - post = { - contract_0: Account( - storage={ - 0: 0x8D5B6, - 1: compute_create_address(address=contract_0, nonce=0), - 100: 0x7ABF8, - }, - ), - compute_create_address(address=contract_0, nonce=0): Account( - balance=1 - ), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stCreateTest/test_create_transaction_call_data.py b/tests/ported_static/stCreateTest/test_create_transaction_call_data.py index 7c5810b6657..3da9a7599e3 100644 --- a/tests/ported_static/stCreateTest/test_create_transaction_call_data.py +++ b/tests/ported_static/stCreateTest/test_create_transaction_call_data.py @@ -1,29 +1,27 @@ """ -Tests if CALLDATALOAD, CALLDATACOPY, CODECOPY and CODESIZE work... - -call data is always empty in initcode context and "code" is initcode. +Verify CALLDATALOAD, CALLDATACOPY, CODECOPY and CODESIZE in the initcode +context of a create transaction: call data is always empty and "code" is the +initcode itself. Ported from: state_tests/stCreateTest/CreateTransactionCallDataFiller.yml -@manually-enhanced: Do not overwrite. tx_gas was raised from 100 000 to -500 000 so the CREATE path can afford its EIP-8037 NEW_ACCOUNT state -gas on Amsterdam (post-state expectations are unchanged on all forks). +@manually-enhanced: Do not overwrite. The post-state now genuinely verifies +each case (observable +1 reads prove empty call data is zero, a slot-2 canary +guards against silent creation failure, and the CODECOPY case asserts +`code=initcode`), and gas/fork boilerplate was removed in favor of maxing out +the transaction gas. """ import pytest from execution_testing import ( Account, Alloc, - Environment, + Bytecode, StateTestFiller, Transaction, compute_create_address, ) -from execution_testing.forks import Fork -from execution_testing.specs.static_state.expect_section import ( - resolve_expect_post, -) from execution_testing.vm import Op REFERENCE_SPEC_GIT_PATH = "N/A" @@ -35,99 +33,70 @@ ) @pytest.mark.valid_from("Cancun") @pytest.mark.parametrize( - "d, g, v", - [ - pytest.param( - 0, - 0, - 0, - id="calldataload", - ), - pytest.param( - 1, - 0, - 0, - id="calldatacopy", - ), - pytest.param( - 2, - 0, - 0, - id="codecopy", - ), - ], + "opcode", + ["calldataload", "calldatacopy", "codecopy"], ) @pytest.mark.pre_alloc_mutable def test_create_transaction_call_data( state_test: StateTestFiller, pre: Alloc, - fork: Fork, - d: int, - g: int, - v: int, + opcode: str, ) -> None: """Tests if CALLDATALOAD, CALLDATACOPY, CODECOPY and CODESIZE work...""" - sender = pre.fund_eoa(amount=0x5AF3107A4000) - - env = Environment( - fee_recipient=sender, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=1000000, - ) + sender = pre.fund_eoa() - expect_entries_: list[dict] = [ - { - "indexes": {"data": [0, 1], "gas": -1, "value": -1}, - "network": [">=Cancun"], - "result": { - compute_create_address(address=sender, nonce=0): Account( - storage={}, code=b"", nonce=1 - ), - }, - }, - { - "indexes": {"data": [2], "gas": -1, "value": -1}, - "network": [">=Cancun"], - "result": { - compute_create_address(address=sender, nonce=0): Account( - storage={}, - code=bytes.fromhex("3860008039386000f3"), - nonce=1, - ), - }, - }, - ] + created_contract = compute_create_address(address=sender, nonce=0) - post, _exc = resolve_expect_post(expect_entries_, d, g, v, fork) + # Sentinel written to storage as the final init-code step. If creation + # reverts or the init code does not run to completion, this slot stays + # zero and the test fails instead of silently passing on an account that + # happens to match the expected (small) values. + canary = 0xC0DE - tx_data = [ - Op.SSTORE(key=0x0, value=Op.CALLDATALOAD(offset=0x0)) - + Op.SSTORE(key=0x1, value=Op.CALLDATALOAD(offset=0x21)) - + Op.STOP, - Op.CALLDATACOPY(dest_offset=Op.DUP1, offset=0x0, size=0x1) - + Op.SSTORE(key=0x0, value=Op.MLOAD(offset=0x0)) - + Op.CALLDATACOPY(dest_offset=0x0, offset=0x1, size=0x20) - + Op.SSTORE(key=0x1, value=Op.MLOAD(offset=0x0)) - + Op.STOP, - Op.CODECOPY(dest_offset=Op.DUP1, offset=0x0, size=Op.CODESIZE) - + Op.RETURN(offset=0x0, size=Op.CODESIZE), - ] - # EIP-8037 NEW_ACCOUNT + per-byte state-gas spill on Amsterdam; - # pre-EIP-8037 keeps the original 100 000 budget. - outer_tx_gas = 100_000 - if fork.is_eip_enabled(8037): - outer_tx_gas = 500_000 - tx_gas = [outer_tx_gas] + # Each case sets the init code to run and the post-state it produces. + # Call data is always empty in init code context, so the calldata reads + # resolve to zero; the only thing that varies is the opcode under test. + initcode: Bytecode + post: dict + if opcode == "calldataload": # empty data reads 0; +1 makes it visible + initcode = ( + Op.SSTORE(key=0x0, value=Op.ADD(Op.CALLDATALOAD(offset=0x0), 1)) + + Op.SSTORE(key=0x1, value=Op.ADD(Op.CALLDATALOAD(offset=0x21), 1)) + + Op.SSTORE(key=0x2, value=canary) + + Op.STOP + ) + post = { + created_contract: Account( + storage={0: 1, 1: 1, 2: canary}, code=b"", nonce=1 + ) + } + elif opcode == "calldatacopy": # empty data reads 0; +1 makes it visible + initcode = ( + Op.CALLDATACOPY(dest_offset=Op.DUP1, offset=0x0, size=0x1) + + Op.SSTORE(key=0x0, value=Op.ADD(Op.MLOAD(offset=0x0), 1)) + + Op.CALLDATACOPY(dest_offset=0x0, offset=0x1, size=0x20) + + Op.SSTORE(key=0x1, value=Op.ADD(Op.MLOAD(offset=0x0), 1)) + + Op.SSTORE(key=0x2, value=canary) + + Op.STOP + ) + post = { + created_contract: Account( + storage={0: 1, 1: 1, 2: canary}, code=b"", nonce=1 + ) + } + else: # "codecopy": CODECOPY/CODESIZE return the init code as the code + initcode = Op.CODECOPY( + dest_offset=Op.DUP1, offset=0x0, size=Op.CODESIZE + ) + Op.RETURN(offset=0x0, size=Op.CODESIZE) + # The init code returns its own bytes, so the deployed code is the + # init code itself; assert against it directly rather than a + # hand-copied hex string. + post = {created_contract: Account(storage={}, code=initcode, nonce=1)} tx = Transaction( sender=sender, to=None, - data=tx_data[d], - gas_limit=tx_gas[g], - error=_exc, + data=initcode, ) - state_test(env=env, pre=pre, post=post, tx=tx) + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stDelegatecallTestHomestead/test_deleagate_call_after_value_transfer.py b/tests/ported_static/stDelegatecallTestHomestead/test_deleagate_call_after_value_transfer.py index a17a34fd41e..72955299b76 100644 --- a/tests/ported_static/stDelegatecallTestHomestead/test_deleagate_call_after_value_transfer.py +++ b/tests/ported_static/stDelegatecallTestHomestead/test_deleagate_call_after_value_transfer.py @@ -1,17 +1,22 @@ """ -Test_deleagate_call_after_value_transfer. +Verify DELEGATECALL propagates the caller frame's context (CALLVALUE, CALLER, +CALLDATA) into the delegate, after a value-bearing transaction. Ported from: state_tests/stDelegatecallTestHomestead/deleagateCallAfterValueTransferFiller.json + +@manually-enhanced: Do not overwrite. DELEGATECALL context propagation +(CALLVALUE/CALLER/CALLDATA) run in the caller's storage; the ported test +transferred zero value (so "after value transfer" was vacuous) -> a non-zero +tx value is now sent so the callee observes it via CALLVALUE. Dynamic +addresses, gas forwarded via the default Op.GAS. """ import pytest from execution_testing import ( Account, - Address, Alloc, - Bytes, - Environment, + Fork, StateTestFiller, Transaction, ) @@ -20,67 +25,59 @@ REFERENCE_SPEC_GIT_PATH = "N/A" REFERENCE_SPEC_VERSION = "N/A" +TRANSFERRED_VALUE = 0xA + @pytest.mark.ported_from( [ "state_tests/stDelegatecallTestHomestead/deleagateCallAfterValueTransferFiller.json" # noqa: E501 ], ) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable +@pytest.mark.valid_from("TangerineWhistle") def test_deleagate_call_after_value_transfer( state_test: StateTestFiller, pre: Alloc, + fork: Fork, ) -> None: - """Test_deleagate_call_after_value_transfer.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0x2386F26FC10000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=1000000, - ) - - # Source: lll - # { (SSTORE 0 (CALLVALUE)) (SSTORE 1 (CALLER)) (SSTORE 2 (CALLDATALOAD 0)) } # noqa: E501 - addr = pre.deploy_contract( # noqa: F841 + """DELEGATECALL runs the callee's code in the caller's context.""" + # Delegated code records the environment it observes: it must see the + # enclosing frame's CALLVALUE (the transferred value), the original CALLER + # (the sender), and the delegate-call args as its calldata (0x1). + delegate = pre.deploy_contract( code=Op.SSTORE(key=0x0, value=Op.CALLVALUE) + Op.SSTORE(key=0x1, value=Op.CALLER) + Op.SSTORE(key=0x2, value=Op.CALLDATALOAD(offset=0x0)) + Op.STOP, - nonce=0, ) - # Source: lll - # { (MSTORE 0 0x01) (DELEGATECALL 100000 0 64 0 64) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 + caller = pre.deploy_contract( code=Op.MSTORE(offset=0x0, value=0x1) + Op.DELEGATECALL( - gas=0x186A0, - address=addr, + address=delegate, args_offset=0x0, args_size=0x40, ret_offset=0x0, ret_size=0x40, ) + Op.STOP, - balance=0x10C8E0, - nonce=0, ) + sender = pre.fund_eoa() tx = Transaction( sender=sender, - to=target, - data=Bytes(""), - gas_limit=453081, + to=caller, + value=TRANSFERRED_VALUE, + protected=fork.supports_protected_txs(), ) post = { - target: Account(storage={0: 0, 1: sender, 2: 1}), - addr: Account(storage={0: 0, 1: 0, 2: 0}), + # DELEGATECALL preserves the enclosing frame's value, so the callee + # sees CALLVALUE == the transferred value; its writes land in the + # caller's storage, not the callee's. + caller: Account( + balance=TRANSFERRED_VALUE, + storage={0: TRANSFERRED_VALUE, 1: sender, 2: 1}, + ), + delegate: Account(storage={0: 0, 1: 0, 2: 0}), } - state_test(env=env, pre=pre, post=post, tx=tx) + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stDelegatecallTestHomestead/test_delegatecall_emptycontract.py b/tests/ported_static/stDelegatecallTestHomestead/test_delegatecall_emptycontract.py index 60b0410f6c4..90c1b8b72bb 100644 --- a/tests/ported_static/stDelegatecallTestHomestead/test_delegatecall_emptycontract.py +++ b/tests/ported_static/stDelegatecallTestHomestead/test_delegatecall_emptycontract.py @@ -1,17 +1,19 @@ """ -Test_delegatecall_emptycontract. +Verify a DELEGATECALL to a codeless, nonexistent account succeeds without +creating or touching the target. Ported from: state_tests/stDelegatecallTestHomestead/delegatecallEmptycontractFiller.json + +@manually-enhanced: Do not overwrite. DELEGATECALL to a codeless account +returns success; dynamic addresses, gas maxed out. """ import pytest from execution_testing import ( Account, - Address, Alloc, - Bytes, - Environment, + Fork, StateTestFiller, Transaction, ) @@ -26,33 +28,20 @@ "state_tests/stDelegatecallTestHomestead/delegatecallEmptycontractFiller.json" # noqa: E501 ], ) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable +@pytest.mark.valid_from("TangerineWhistle") def test_delegatecall_emptycontract( state_test: StateTestFiller, pre: Alloc, + fork: Fork, ) -> None: - """Test_delegatecall_emptycontract.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0x10C8E0) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=1000000, - ) - - # Source: lll - # { [[ 0 ]] (DELEGATECALL 50000 0x945304eb96065b2a98b57a48a06ae28d285a71b5 0 64 0 64 )} # noqa: E501 - target = pre.deploy_contract( # noqa: F841 + """DELEGATECALL to a codeless account succeeds (returns 1).""" + # A DELEGATECALL to an account with no code runs nothing and returns 1. + empty = pre.nonexistent_account() + caller = pre.deploy_contract( code=Op.SSTORE( key=0x0, value=Op.DELEGATECALL( - gas=0xC350, - address=0x945304EB96065B2A98B57A48A06AE28D285A71B5, + address=empty, args_offset=0x0, args_size=0x40, ret_offset=0x0, @@ -60,17 +49,21 @@ def test_delegatecall_emptycontract( ), ) + Op.STOP, - balance=1000, - nonce=0, ) + # DELEGATECALL predates EIP-155, so the tx must go unprotected on + # pre-SpuriousDragon forks or it fails signature validation. tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=105044, + sender=pre.fund_eoa(), + to=caller, + protected=fork.supports_protected_txs(), ) - post = {target: Account(storage={0: 1})} + # DELEGATECALL carries no value, so it must not create (or even touch) + # the target account. + post = { + caller: Account(storage={0: 1}), + empty: Account.NONEXISTENT, + } - state_test(env=env, pre=pre, post=post, tx=tx) + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas.py deleted file mode 100644 index ce267ffe0b9..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas.py +++ /dev/null @@ -1,84 +0,0 @@ -""" -Test_raw_call_code_gas. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - ["state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasFiller.json"], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_call_code_gas( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_call_code_gas.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (CALLCODE 30000 0 0 0 0 0) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.CALLCODE( - gas=0x7530, - address=addr, - value=0x0, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - ) - - post = { - addr: Account(storage={}), - target: Account(storage={1: 24739, 2: 29998}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_ask.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_ask.py deleted file mode 100644 index 4850de47a79..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_ask.py +++ /dev/null @@ -1,84 +0,0 @@ -""" -Test_raw_call_code_gas_ask. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasAskFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - ["state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasAskFiller.json"], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_call_code_gas_ask( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_call_code_gas_ask.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (CALLCODE 3000000 0 0 0 0 0) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.CALLCODE( - gas=0x2DC6C0, - address=addr, - value=0x0, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - ) - - post = { - addr: Account(storage={}), - target: Account(storage={1: 24739, 2: 0x727BB}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_memory.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_memory.py deleted file mode 100644 index d5eecdff81e..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_memory.py +++ /dev/null @@ -1,86 +0,0 @@ -""" -Test_raw_call_code_gas_memory. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasMemoryFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasMemoryFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_call_code_gas_memory( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_call_code_gas_memory.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (CALLCODE 30000 0 0 8000 0 8000) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.CALLCODE( - gas=0x7530, - address=addr, - value=0x0, - args_offset=0x0, - args_size=0x1F40, - ret_offset=0x0, - ret_size=0x1F40, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - ) - - post = { - addr: Account(storage={}), - target: Account(storage={1: 25608, 2: 29998}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_memory_ask.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_memory_ask.py deleted file mode 100644 index dbed9f97250..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_memory_ask.py +++ /dev/null @@ -1,86 +0,0 @@ -""" -Test_raw_call_code_gas_memory_ask. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasMemoryAskFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasMemoryAskFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_call_code_gas_memory_ask( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_call_code_gas_memory_ask.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (CALLCODE 3000000 0 0 8000 0 8000) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.CALLCODE( - gas=0x2DC6C0, - address=addr, - value=0x0, - args_offset=0x0, - args_size=0x1F40, - ret_offset=0x0, - ret_size=0x1F40, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - ) - - post = { - addr: Account(storage={}), - target: Account(storage={1: 25608, 2: 0x72464}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_value_transfer.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_value_transfer.py deleted file mode 100644 index 8082a69671c..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_value_transfer.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Test_raw_call_code_gas_value_transfer. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasValueTransferFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasValueTransferFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_call_code_gas_value_transfer( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_call_code_gas_value_transfer.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (CALLCODE 30000 10 0 0 0 0) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.CALLCODE( - gas=0x7530, - address=addr, - value=0xA, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - value=10, - ) - - post = { - addr: Account(storage={}), - target: Account(storage={1: 31439, 2: 32298}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_value_transfer_ask.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_value_transfer_ask.py deleted file mode 100644 index d69e9eec044..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_value_transfer_ask.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Test_raw_call_code_gas_value_transfer_ask. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasValueTransferAskFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasValueTransferAskFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_call_code_gas_value_transfer_ask( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_call_code_gas_value_transfer_ask.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (CALLCODE 3000000 10 0 0 0 0) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.CALLCODE( - gas=0x2DC6C0, - address=addr, - value=0xA, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - value=10, - ) - - post = { - addr: Account(storage={}), - target: Account(storage={1: 31439, 2: 0x70E1C}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_value_transfer_memory.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_value_transfer_memory.py deleted file mode 100644 index adc2b040768..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_value_transfer_memory.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Test_raw_call_code_gas_value_transfer_memory. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasValueTransferMemoryFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasValueTransferMemoryFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_call_code_gas_value_transfer_memory( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_call_code_gas_value_transfer_memory.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (CALLCODE 30000 10 0 8000 0 8000) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.CALLCODE( - gas=0x7530, - address=addr, - value=0xA, - args_offset=0x0, - args_size=0x1F40, - ret_offset=0x0, - ret_size=0x1F40, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - value=10, - ) - - post = { - addr: Account(storage={}), - target: Account(storage={1: 32308, 2: 32298}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_value_transfer_memory_ask.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_value_transfer_memory_ask.py deleted file mode 100644 index db089d6d3fc..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_code_gas_value_transfer_memory_ask.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Test_raw_call_code_gas_value_transfer_memory_ask. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasValueTransferMemoryAskFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasValueTransferMemoryAskFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_call_code_gas_value_transfer_memory_ask( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_call_code_gas_value_transfer_memory_ask.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (CALLCODE 3000000 10 0 8000 0 8000) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.CALLCODE( - gas=0x2DC6C0, - address=addr, - value=0xA, - args_offset=0x0, - args_size=0x1F40, - ret_offset=0x0, - ret_size=0x1F40, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - value=10, - ) - - post = { - addr: Account(storage={}), - target: Account(storage={1: 32308, 2: 0x70AC4}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas.py index 97bc8524d66..6fe0cbca6de 100644 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas.py +++ b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas.py @@ -1,17 +1,29 @@ """ -Test_raw_call_gas. +Measure the gas cost of CALL / CALLCODE / DELEGATECALL with CodeGasMeasure, +across value-transfer and memory-expansion variants. The callee records the +gas it was forwarded. Ported from: state_tests/stEIP150singleCodeGasPrices/RawCallGasFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCallGasValueTransferFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCallMemoryGasFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCallGasValueTransferMemoryFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasValueTransferFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasMemoryFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasValueTransferMemoryFiller.json +state_tests/stEIP150singleCodeGasPrices/RawDelegateCallGasFiller.json +state_tests/stEIP150singleCodeGasPrices/RawDelegateCallGasMemoryFiller.json + +@manually-enhanced: Do not overwrite. Nested call gas via CodeGasMeasure. """ import pytest from execution_testing import ( Account, - Address, Alloc, - Bytes, - Environment, + CodeGasMeasure, + Fork, StateTestFiller, Transaction, ) @@ -20,65 +32,143 @@ REFERENCE_SPEC_GIT_PATH = "N/A" REFERENCE_SPEC_VERSION = "N/A" +FORWARD_BUFFER = 100 # margin forwarded beyond the callee's own gas cost +MEMORY_SIZE = 0x1F40 # args/ret buffer size for memory variants +CALL_VALUE = 0xA +CALLER_BALANCE = 100 + @pytest.mark.ported_from( - ["state_tests/stEIP150singleCodeGasPrices/RawCallGasFiller.json"], + [ + "state_tests/stEIP150singleCodeGasPrices/RawCallGasFiller.json", + "state_tests/stEIP150singleCodeGasPrices/RawCallGasValueTransferFiller.json", # noqa: E501 + "state_tests/stEIP150singleCodeGasPrices/RawCallMemoryGasFiller.json", + "state_tests/stEIP150singleCodeGasPrices/RawCallGasValueTransferMemoryFiller.json", # noqa: E501 + "state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasFiller.json", + "state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasValueTransferFiller.json", # noqa: E501 + "state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasMemoryFiller.json", + "state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasValueTransferMemoryFiller.json", # noqa: E501 + "state_tests/stEIP150singleCodeGasPrices/RawDelegateCallGasFiller.json", + "state_tests/stEIP150singleCodeGasPrices/RawDelegateCallGasMemoryFiller.json", # noqa: E501 + ], +) +@pytest.mark.valid_from("Berlin") +@pytest.mark.parametrize( + "opcode, value, memory", + [ + pytest.param(Op.CALL, 0, False, id="raw_call_gas"), + pytest.param( + Op.CALL, CALL_VALUE, False, id="raw_call_gas_value_transfer" + ), + pytest.param(Op.CALL, 0, True, id="raw_call_memory_gas"), + pytest.param( + Op.CALL, CALL_VALUE, True, id="raw_call_gas_value_transfer_memory" + ), + pytest.param(Op.CALLCODE, 0, False, id="raw_call_code_gas"), + pytest.param( + Op.CALLCODE, + CALL_VALUE, + False, + id="raw_call_code_gas_value_transfer", + ), + pytest.param(Op.CALLCODE, 0, True, id="raw_call_code_gas_memory"), + pytest.param( + Op.CALLCODE, + CALL_VALUE, + True, + id="raw_call_code_gas_value_transfer_memory", + ), + pytest.param(Op.DELEGATECALL, 0, False, id="raw_delegate_call_gas"), + pytest.param( + Op.DELEGATECALL, 0, True, id="raw_delegate_call_gas_memory" + ), + ], ) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable def test_raw_call_gas( state_test: StateTestFiller, pre: Alloc, + fork: Fork, + opcode: Op, + value: int, + memory: bool, ) -> None: - """Test_raw_call_gas.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) + """Measure call-family gas, with the callee recording forwarded gas.""" + stipend = fork.gas_costs().CALL_STIPEND if value else 0 + mem = MEMORY_SIZE if memory else 0 - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, + # The callee writes a cold (zero->non-zero) slot; SSTORE cost depends only + # on that transition, not the value, so a placeholder new_value suffices to + # size the gas to forward (large under EIP-8037 state gas). + callee_store = Op.SSTORE( + key=0x2, + value=Op.GAS, + key_warm=False, + original_value=0, + new_value=1, ) + forward_gas = callee_store.gas_cost(fork) + FORWARD_BUFFER + callee = pre.deploy_contract(code=callee_store + Op.STOP) - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (CALL 30000 0 0 0 0 0) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.CALL( - gas=0x7530, - address=addr, - value=0x0, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ) + # Callee records the gas it received: forwarded gas plus the value-transfer + # stipend, minus the GAS opcode it executes. + callee_gas_seen = forward_gas + stipend - Op.GAS.gas_cost(fork) + + if opcode == Op.DELEGATECALL: + call_code = Op.DELEGATECALL( + gas=forward_gas, + address=callee, + args_offset=0x0, + args_size=mem, + ret_offset=0x0, + ret_size=mem, + address_warm=False, + new_memory_size=mem, + ) + else: + call_code = opcode( + gas=forward_gas, + address=callee, + value=value, + args_offset=0x0, + args_size=mem, + ret_offset=0x0, + ret_size=mem, + address_warm=False, + value_transfer=value > 0, + account_new=False, + new_memory_size=mem, ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, + caller = pre.deploy_contract( + code=CodeGasMeasure( + code=call_code, + extra_stack_items=1, + sstore_key=0x1, + ), + balance=CALLER_BALANCE, ) tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, + sender=pre.fund_eoa(), + to=caller, + state_gas_reservoir=0, ) + # Measured cost = the call's own cost plus the callee's consumption; the + # value-transfer stipend is forwarded free, not charged to the caller. + call_gas = call_code.gas_cost(fork) + callee_store.gas_cost(fork) - stipend + + # CALL runs the callee in its own context (slot 2 in the callee); CALLCODE + # and DELEGATECALL run it in the caller's context (slot 2 in the caller). + if opcode == Op.CALL: + callee_storage = {0x2: callee_gas_seen} + caller_storage = {0x1: call_gas} + else: + callee_storage = {} + caller_storage = {0x1: call_gas, 0x2: callee_gas_seen} + post = { - addr: Account(storage={2: 29998}), - target: Account(storage={1: 24739}), + callee: Account(storage=callee_storage), + caller: Account(storage=caller_storage), } - state_test(env=env, pre=pre, post=post, tx=tx) + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas_ask.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas_ask.py index 6935ff2a0d7..e16c5b50880 100644 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas_ask.py +++ b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas_ask.py @@ -1,17 +1,35 @@ """ -Test_raw_call_gas_ask. +Verify the EIP-150 "all but one 64th" rule: a subcall asking for more gas +than is available receives 63/64 of it, across CALL / CALLCODE / DELEGATECALL +and their value-transfer and memory-expansion variants. Ported from: state_tests/stEIP150singleCodeGasPrices/RawCallGasAskFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCallGasValueTransferAskFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCallMemoryGasAskFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCallGasValueTransferMemoryAskFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasAskFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasMemoryAskFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasValueTransferAskFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasValueTransferMemoryAskFiller.json +state_tests/stEIP150singleCodeGasPrices/RawDelegateCallGasAskFiller.json +state_tests/stEIP150singleCodeGasPrices/RawDelegateCallGasMemoryAskFiller.json + +@manually-enhanced: Do not overwrite. The ported fillers pinned the forwarded +gas as an absolute number tied to the tx gas limit (fork-fragile via the +intrinsic). Reframed so an outer call caps the caller frame at a known gas +budget, the callee returns its observed GAS up to the top frame (no lower-frame +SSTORE state-gas trap), and the expected value is derived from the fork: +`all_but_one_64th(caller_gas - call.gas_cost(fork))`. The caller also reports +its remaining gas after the subcall, preserving the ported fillers' second +assertion that unused forwarded gas is credited back. """ import pytest from execution_testing import ( Account, - Address, Alloc, - Bytes, - Environment, + Fork, StateTestFiller, Transaction, ) @@ -20,65 +38,156 @@ REFERENCE_SPEC_GIT_PATH = "N/A" REFERENCE_SPEC_VERSION = "N/A" +CALLER_GAS = 100_000 +CALL_VALUE = 0xA +MEMORY_SIZE = 0x1F40 # 8000-byte args/ret buffer for the memory variants + @pytest.mark.ported_from( - ["state_tests/stEIP150singleCodeGasPrices/RawCallGasAskFiller.json"], + [ + "state_tests/stEIP150singleCodeGasPrices/RawCallGasAskFiller.json", + "state_tests/stEIP150singleCodeGasPrices/RawCallGasValueTransferAskFiller.json", # noqa: E501 + "state_tests/stEIP150singleCodeGasPrices/RawCallMemoryGasAskFiller.json", # noqa: E501 + "state_tests/stEIP150singleCodeGasPrices/RawCallGasValueTransferMemoryAskFiller.json", # noqa: E501 + "state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasAskFiller.json", + "state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasMemoryAskFiller.json", # noqa: E501 + "state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasValueTransferAskFiller.json", # noqa: E501 + "state_tests/stEIP150singleCodeGasPrices/RawCallCodeGasValueTransferMemoryAskFiller.json", # noqa: E501 + "state_tests/stEIP150singleCodeGasPrices/RawDelegateCallGasAskFiller.json", # noqa: E501 + "state_tests/stEIP150singleCodeGasPrices/RawDelegateCallGasMemoryAskFiller.json", # noqa: E501 + ], +) +@pytest.mark.valid_from("Berlin") +@pytest.mark.parametrize( + "opcode, value, memory", + [ + pytest.param(Op.CALL, 0, False, id="raw_call_gas_ask"), + pytest.param( + Op.CALL, CALL_VALUE, False, id="raw_call_gas_value_transfer_ask" + ), + pytest.param(Op.CALL, 0, True, id="raw_call_memory_gas_ask"), + pytest.param( + Op.CALL, + CALL_VALUE, + True, + id="raw_call_gas_value_transfer_memory_ask", + ), + pytest.param(Op.CALLCODE, 0, False, id="raw_call_code_gas_ask"), + pytest.param(Op.CALLCODE, 0, True, id="raw_call_code_gas_memory_ask"), + pytest.param( + Op.CALLCODE, + CALL_VALUE, + False, + id="raw_call_code_gas_value_transfer_ask", + ), + pytest.param( + Op.CALLCODE, + CALL_VALUE, + True, + id="raw_call_code_gas_value_transfer_memory_ask", + ), + pytest.param( + Op.DELEGATECALL, 0, False, id="raw_delegate_call_gas_ask" + ), + pytest.param( + Op.DELEGATECALL, 0, True, id="raw_delegate_call_gas_memory_ask" + ), + ], ) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable def test_raw_call_gas_ask( state_test: StateTestFiller, pre: Alloc, + fork: Fork, + opcode: Op, + value: int, + memory: bool, ) -> None: - """Test_raw_call_gas_ask.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, + """A subcall asking for more gas than available receives 63/64 of it.""" + sender = pre.fund_eoa() + + # Callee returns the gas it observed on entry back to the caller. + gas_return_code = Op.MSTORE(0, Op.GAS, new_memory_size=32) + Op.RETURN( + 0, 32 ) + gas_return_contract = pre.deploy_contract(code=gas_return_code) + + mem = MEMORY_SIZE if memory else 0 + ret_size = MEMORY_SIZE if memory else 32 # must fit the 32-byte GAS return + new_memory_size = MEMORY_SIZE if memory else 32 - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, + # The caller asks for "all" gas (the default Op.GAS operand), which exceeds + # what remains after the call's own cost, so the 63/64 cap kicks in. + if opcode == Op.DELEGATECALL: + caller_call_code = Op.DELEGATECALL( + address=gas_return_contract, + args_offset=0, + args_size=mem, + ret_offset=0, + ret_size=ret_size, + address_warm=False, + new_memory_size=new_memory_size, + ) + else: + caller_call_code = opcode( + address=gas_return_contract, + value=value, + args_offset=0, + args_size=mem, + ret_offset=0, + ret_size=ret_size, + address_warm=False, + value_transfer=value > 0, + account_new=False, + new_memory_size=new_memory_size, + ) + # After the subcall returns, the caller appends its own remaining gas to + # the return data, so the top frame can also assert that the unused part + # of the 63/64-forwarded grant was credited back to the caller. + caller = pre.deploy_contract( + code=caller_call_code + Op.MSTORE(32, Op.GAS) + Op.RETURN(0, 64), + balance=value, ) - # Source: lll - # { [0] (GAS) (CALL 3000000 0 0 0 0 0) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.CALL( - gas=0x2DC6C0, - address=addr, - value=0x0, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ) + + # An outer call pins the caller frame's gas to a known budget, so the + # forwarded amount does not depend on the tx gas limit. + entry = pre.deploy_contract( + code=Op.SSTORE(0, 1) + + Op.CALL( + gas=CALLER_GAS, + address=caller, + value=0, + args_offset=0, + args_size=0, + ret_offset=0, + ret_size=64, ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, + + Op.SSTORE(1, Op.MLOAD(0)) + + Op.SSTORE(2, Op.MLOAD(32)), ) - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, + # EIP-150 forwards "all but one 64th" of the gas left after the call's own + # cost; a value-bearing call additionally hands the callee the stipend. + stipend = fork.gas_costs().CALL_STIPEND if value else 0 + available = CALLER_GAS - caller_call_code.gas_cost(fork) + assert available > 0, "CALLER_GAS must exceed the call's own cost" + forwarded = available - available // 64 + expected_gas = forwarded + stipend - Op.GAS.gas_cost(fork) + + # The callee's unconsumed gas returns to the caller: what the caller sees + # after the subcall is its budget minus the call's own cost and the + # callee's consumption (the stipend nets out on value-bearing calls). + expected_caller_gas = ( + CALLER_GAS + - caller_call_code.gas_cost(fork) + + stipend + - gas_return_code.gas_cost(fork) + - Op.GAS.gas_cost(fork) ) + tx = Transaction(sender=sender, to=entry) + post = { - addr: Account(storage={2: 0x727BB}), - target: Account(storage={1: 24739}), + entry: Account(storage={0: 1, 1: expected_gas, 2: expected_caller_gas}) } - state_test(env=env, pre=pre, post=post, tx=tx) + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas_value_transfer.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas_value_transfer.py deleted file mode 100644 index c94dd98e65a..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas_value_transfer.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Test_raw_call_gas_value_transfer. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCallGasValueTransferFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawCallGasValueTransferFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_call_gas_value_transfer( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_call_gas_value_transfer.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (CALL 30000 10 0 0 0 0) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.CALL( - gas=0x7530, - address=addr, - value=0xA, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - value=10, - ) - - post = { - addr: Account(storage={2: 32298}), - target: Account(storage={1: 31439}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas_value_transfer_ask.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas_value_transfer_ask.py deleted file mode 100644 index 46240d39693..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas_value_transfer_ask.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Test_raw_call_gas_value_transfer_ask. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCallGasValueTransferAskFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawCallGasValueTransferAskFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_call_gas_value_transfer_ask( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_call_gas_value_transfer_ask.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (CALL 3000000 10 0 0 0 0) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.CALL( - gas=0x2DC6C0, - address=addr, - value=0xA, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - value=10, - ) - - post = { - addr: Account(storage={2: 0x70E1C}), - target: Account(storage={1: 31439}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas_value_transfer_memory.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas_value_transfer_memory.py deleted file mode 100644 index cd1f9f68c18..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas_value_transfer_memory.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Test_raw_call_gas_value_transfer_memory. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCallGasValueTransferMemoryFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawCallGasValueTransferMemoryFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_call_gas_value_transfer_memory( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_call_gas_value_transfer_memory.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (CALL 30000 10 0 8000 0 8000) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.CALL( - gas=0x7530, - address=addr, - value=0xA, - args_offset=0x0, - args_size=0x1F40, - ret_offset=0x0, - ret_size=0x1F40, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - value=10, - ) - - post = { - addr: Account(storage={2: 32298}), - target: Account(storage={1: 32308}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas_value_transfer_memory_ask.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas_value_transfer_memory_ask.py deleted file mode 100644 index 0939cc07c2a..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_gas_value_transfer_memory_ask.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Test_raw_call_gas_value_transfer_memory_ask. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCallGasValueTransferMemoryAskFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawCallGasValueTransferMemoryAskFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_call_gas_value_transfer_memory_ask( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_call_gas_value_transfer_memory_ask.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (CALL 3000000 10 0 8000 0 8000) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.CALL( - gas=0x2DC6C0, - address=addr, - value=0xA, - args_offset=0x0, - args_size=0x1F40, - ret_offset=0x0, - ret_size=0x1F40, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - value=10, - ) - - post = { - addr: Account(storage={2: 0x70AC4}), - target: Account(storage={1: 32308}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_memory_gas.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_memory_gas.py deleted file mode 100644 index 2626ed1afa2..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_memory_gas.py +++ /dev/null @@ -1,84 +0,0 @@ -""" -Test_raw_call_memory_gas. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCallMemoryGasFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - ["state_tests/stEIP150singleCodeGasPrices/RawCallMemoryGasFiller.json"], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_call_memory_gas( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_call_memory_gas.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (CALL 30000 0 0 8000 0 8000) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.CALL( - gas=0x7530, - address=addr, - value=0x0, - args_offset=0x0, - args_size=0x1F40, - ret_offset=0x0, - ret_size=0x1F40, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - ) - - post = { - addr: Account(storage={2: 29998}), - target: Account(storage={1: 25608}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_memory_gas_ask.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_memory_gas_ask.py deleted file mode 100644 index 1ea4c6eab02..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_call_memory_gas_ask.py +++ /dev/null @@ -1,84 +0,0 @@ -""" -Test_raw_call_memory_gas_ask. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCallMemoryGasAskFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - ["state_tests/stEIP150singleCodeGasPrices/RawCallMemoryGasAskFiller.json"], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_call_memory_gas_ask( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_call_memory_gas_ask.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (CALL 3000000 0 0 8000 0 8000) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.CALL( - gas=0x2DC6C0, - address=addr, - value=0x0, - args_offset=0x0, - args_size=0x1F40, - ret_offset=0x0, - ret_size=0x1F40, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - ) - - post = { - addr: Account(storage={2: 0x72464}), - target: Account(storage={1: 25608}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_fail_gas_value_transfer.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_fail_gas_value_transfer.py deleted file mode 100644 index b1926166116..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_fail_gas_value_transfer.py +++ /dev/null @@ -1,75 +0,0 @@ -""" -Test_raw_create_fail_gas_value_transfer. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCreateFailGasValueTransferFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, - compute_create_address, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawCreateFailGasValueTransferFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_create_fail_gas_value_transfer( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_create_fail_gas_value_transfer.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - contract_0 = Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [0] (GAS) (CREATE 11 0 0) [[1]] (SUB @0 (GAS)) } - contract_0 = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP(Op.CREATE(value=0xB, offset=0x0, size=0x0)) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=contract_0, - data=Bytes(""), - gas_limit=500000, - value=10, - ) - - post = { - contract_0: Account(storage={1: 32022}), - compute_create_address( - address=contract_0, nonce=0 - ): Account.NONEXISTENT, - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_fail_gas_value_transfer2.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_fail_gas_value_transfer2.py deleted file mode 100644 index cff6a4c875c..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_fail_gas_value_transfer2.py +++ /dev/null @@ -1,75 +0,0 @@ -""" -Test_raw_create_fail_gas_value_transfer2. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCreateFailGasValueTransfer2Filler.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, - compute_create_address, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawCreateFailGasValueTransfer2Filler.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_create_fail_gas_value_transfer2( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_create_fail_gas_value_transfer2.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - contract_0 = Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [0] (GAS) (CREATE 11 0 8000) [[1]] (SUB @0 (GAS)) } - contract_0 = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP(Op.CREATE(value=0xB, offset=0x0, size=0x1F40)) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=contract_0, - data=Bytes(""), - gas_limit=500000, - value=10, - ) - - post = { - contract_0: Account(storage={1: 33391}), - compute_create_address( - address=contract_0, nonce=0 - ): Account.NONEXISTENT, - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_gas.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_gas.py index cfcdaf8e86f..e7e0e5962af 100644 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_gas.py +++ b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_gas.py @@ -1,17 +1,25 @@ """ -Test_raw_create_gas. +Measure the gas cost of CREATE with CodeGasMeasure, across value-transfer, +memory-expansion, and insufficient-balance (failure) variants. Ported from: state_tests/stEIP150singleCodeGasPrices/RawCreateGasFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCreateGasMemoryFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCreateGasValueTransferFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCreateGasValueTransferMemoryFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCreateFailGasValueTransferFiller.json +state_tests/stEIP150singleCodeGasPrices/RawCreateFailGasValueTransfer2Filler.json + +@manually-enhanced: Do not overwrite. Six RawCreate*Gas fillers folded into one +CodeGasMeasure parametrize; failure path charges regular_cost (no state gas). """ import pytest from execution_testing import ( Account, - Address, Alloc, - Bytes, - Environment, + CodeGasMeasure, + Fork, StateTestFiller, Transaction, compute_create_address, @@ -21,52 +29,85 @@ REFERENCE_SPEC_GIT_PATH = "N/A" REFERENCE_SPEC_VERSION = "N/A" +GAS_SLOT = 0x1 +MEMORY_SIZE = 0x1F40 # 8000-byte init-code window for the memory variants + @pytest.mark.ported_from( - ["state_tests/stEIP150singleCodeGasPrices/RawCreateGasFiller.json"], + [ + "state_tests/stEIP150singleCodeGasPrices/RawCreateGasFiller.json", + "state_tests/stEIP150singleCodeGasPrices/RawCreateGasMemoryFiller.json", + "state_tests/stEIP150singleCodeGasPrices/RawCreateGasValueTransferFiller.json", # noqa: E501 + "state_tests/stEIP150singleCodeGasPrices/RawCreateGasValueTransferMemoryFiller.json", # noqa: E501 + "state_tests/stEIP150singleCodeGasPrices/RawCreateFailGasValueTransferFiller.json", # noqa: E501 + "state_tests/stEIP150singleCodeGasPrices/RawCreateFailGasValueTransfer2Filler.json", # noqa: E501 + ], +) +@pytest.mark.valid_from("SpuriousDragon") +@pytest.mark.parametrize( + "create_value, size, fails", + [ + pytest.param(0x0, 0x0, False, id="raw_create_gas"), + pytest.param(0x0, MEMORY_SIZE, False, id="raw_create_gas_memory"), + pytest.param(0xA, 0x0, False, id="raw_create_gas_value_transfer"), + pytest.param( + 0xA, MEMORY_SIZE, False, id="raw_create_gas_value_transfer_memory" + ), + pytest.param(0xB, 0x0, True, id="raw_create_fail_gas_value_transfer"), + pytest.param( + 0xB, MEMORY_SIZE, True, id="raw_create_fail_gas_value_transfer2" + ), + ], ) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable def test_raw_create_gas( state_test: StateTestFiller, pre: Alloc, + fork: Fork, + create_value: int, + size: int, + fails: bool, ) -> None: - """Test_raw_create_gas.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - contract_0 = Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, + """Measure CREATE gas; a balance-failure path is cheaper (no state gas).""" + # Init code is never written, so it is `size` zero bytes: the created + # contract STOPs immediately and deposits no code. + create_code = Op.CREATE( + value=create_value, + offset=0x0, + size=size, + new_memory_size=size, + init_code_size=size, ) - - # Source: lll - # { [0] (GAS) (CREATE 0 0 0) [[1]] (SUB @0 (GAS)) } - contract_0 = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP(Op.CREATE(value=0x0, offset=0x0, size=0x0)) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, + # Fund the creator one wei short of `create_value` on the failure cases so + # the CREATE aborts on the balance check; otherwise give it exactly enough. + balance = create_value - 1 if fails else create_value + contract = pre.deploy_contract( + code=CodeGasMeasure( + code=create_code, + extra_stack_items=1, + sstore_key=GAS_SLOT, + ), + balance=balance, ) tx = Transaction( - sender=sender, - to=contract_0, - data=Bytes(""), - gas_limit=500000, + sender=pre.fund_eoa(), + to=contract, + state_gas_reservoir=0, ) + created = compute_create_address(address=contract, nonce=1) + if fails: + # A balance-check failure runs no init code and creates no account, so + # only the regular (execution) gas is charged, never state gas. + expected_gas = create_code.regular_cost(fork) + created_account = Account.NONEXISTENT + else: + expected_gas = create_code.gas_cost(fork) + created_account = Account(balance=create_value) + post = { - contract_0: Account(storage={1: 32022}), - compute_create_address(address=contract_0, nonce=0): Account( - balance=0 - ), + contract: Account(storage={GAS_SLOT: expected_gas}), + created: created_account, } - state_test(env=env, pre=pre, post=post, tx=tx) + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_gas_memory.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_gas_memory.py deleted file mode 100644 index 78b9d6682e6..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_gas_memory.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Test_raw_create_gas_memory. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCreateGasMemoryFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, - compute_create_address, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - ["state_tests/stEIP150singleCodeGasPrices/RawCreateGasMemoryFiller.json"], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_create_gas_memory( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_create_gas_memory.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - contract_0 = Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [0] (GAS) (CREATE 0 0 8000) [[1]] (SUB @0 (GAS)) } - contract_0 = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP(Op.CREATE(value=0x0, offset=0x0, size=0x1F40)) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=contract_0, - data=Bytes(""), - gas_limit=500000, - ) - - post = { - contract_0: Account(storage={1: 33391}), - compute_create_address(address=contract_0, nonce=0): Account( - balance=0 - ), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_gas_value_transfer.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_gas_value_transfer.py deleted file mode 100644 index 355d74d1dfd..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_gas_value_transfer.py +++ /dev/null @@ -1,75 +0,0 @@ -""" -Test_raw_create_gas_value_transfer. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCreateGasValueTransferFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, - compute_create_address, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawCreateGasValueTransferFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_create_gas_value_transfer( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_create_gas_value_transfer.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - contract_0 = Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [0] (GAS) (CREATE 10 0 0) [[1]] (SUB @0 (GAS)) } - contract_0 = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP(Op.CREATE(value=0xA, offset=0x0, size=0x0)) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=contract_0, - data=Bytes(""), - gas_limit=500000, - value=10, - ) - - post = { - contract_0: Account(storage={1: 32022}), - compute_create_address(address=contract_0, nonce=0): Account( - balance=10 - ), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_gas_value_transfer_memory.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_gas_value_transfer_memory.py deleted file mode 100644 index 52578c5b4b9..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_create_gas_value_transfer_memory.py +++ /dev/null @@ -1,75 +0,0 @@ -""" -Test_raw_create_gas_value_transfer_memory. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawCreateGasValueTransferMemoryFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, - compute_create_address, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawCreateGasValueTransferMemoryFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_create_gas_value_transfer_memory( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_create_gas_value_transfer_memory.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - contract_0 = Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [0] (GAS) (CREATE 10 0 8000) [[1]] (SUB @0 (GAS)) } - contract_0 = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP(Op.CREATE(value=0xA, offset=0x0, size=0x1F40)) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=contract_0, - data=Bytes(""), - gas_limit=500000, - value=10, - ) - - post = { - contract_0: Account(storage={1: 33391}), - compute_create_address(address=contract_0, nonce=0): Account( - balance=10 - ), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_delegate_call_gas.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_delegate_call_gas.py deleted file mode 100644 index 9ff87ffdc2a..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_delegate_call_gas.py +++ /dev/null @@ -1,83 +0,0 @@ -""" -Test_raw_delegate_call_gas. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawDelegateCallGasFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - ["state_tests/stEIP150singleCodeGasPrices/RawDelegateCallGasFiller.json"], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_delegate_call_gas( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_delegate_call_gas.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (DELEGATECALL 30000 0 0 0 0) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.DELEGATECALL( - gas=0x7530, - address=addr, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - ) - - post = { - addr: Account(storage={}), - target: Account(storage={1: 24736, 2: 29998}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_delegate_call_gas_ask.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_delegate_call_gas_ask.py deleted file mode 100644 index 64eeacabb25..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_delegate_call_gas_ask.py +++ /dev/null @@ -1,85 +0,0 @@ -""" -Test_raw_delegate_call_gas_ask. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawDelegateCallGasAskFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawDelegateCallGasAskFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_delegate_call_gas_ask( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_delegate_call_gas_ask.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (DELEGATECALL 3000000 0 0 0 0) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.DELEGATECALL( - gas=0x2DC6C0, - address=addr, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - ) - - post = { - addr: Account(storage={}), - target: Account(storage={1: 24736, 2: 0x727BE}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_delegate_call_gas_memory.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_delegate_call_gas_memory.py deleted file mode 100644 index 3db2620bb5a..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_delegate_call_gas_memory.py +++ /dev/null @@ -1,85 +0,0 @@ -""" -Test_raw_delegate_call_gas_memory. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawDelegateCallGasMemoryFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawDelegateCallGasMemoryFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_delegate_call_gas_memory( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_delegate_call_gas_memory.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (DELEGATECALL 30000 0 8000 0 8000) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.DELEGATECALL( - gas=0x7530, - address=addr, - args_offset=0x0, - args_size=0x1F40, - ret_offset=0x0, - ret_size=0x1F40, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - ) - - post = { - addr: Account(storage={}), - target: Account(storage={1: 25605, 2: 29998}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_delegate_call_gas_memory_ask.py b/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_delegate_call_gas_memory_ask.py deleted file mode 100644 index 37333476bf2..00000000000 --- a/tests/ported_static/stEIP150singleCodeGasPrices/test_raw_delegate_call_gas_memory_ask.py +++ /dev/null @@ -1,85 +0,0 @@ -""" -Test_raw_delegate_call_gas_memory_ask. - -Ported from: -state_tests/stEIP150singleCodeGasPrices/RawDelegateCallGasMemoryAskFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stEIP150singleCodeGasPrices/RawDelegateCallGasMemoryAskFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_raw_delegate_call_gas_memory_ask( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_raw_delegate_call_gas_memory_ask.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { [[2]] (GAS) } - addr = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x2, value=Op.GAS) + Op.STOP, - nonce=0, - ) - # Source: lll - # { [0] (GAS) (DELEGATECALL 3000000 0 8000 0 8000) [[1]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.POP( - Op.DELEGATECALL( - gas=0x2DC6C0, - address=addr, - args_offset=0x0, - args_size=0x1F40, - ret_offset=0x0, - ret_size=0x1F40, - ) - ) - + Op.SSTORE(key=0x1, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=500000, - ) - - post = { - addr: Account(storage={}), - target: Account(storage={1: 25605, 2: 0x72467}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP1559/test_sender_balance.py b/tests/ported_static/stEIP1559/test_sender_balance.py index c248718b4df..7ea2c909564 100644 --- a/tests/ported_static/stEIP1559/test_sender_balance.py +++ b/tests/ported_static/stEIP1559/test_sender_balance.py @@ -1,22 +1,20 @@ """ -The execution records the EIP-1559 transaction origin balance to make... - -properly computed based on the effective gas price (not the maximum gas price -as in -the transaction validity check). +The origin balance seen during execution of an EIP-1559 transaction is +computed from the effective gas price, not the maximum gas price used in the +transaction validity check. Ported from: state_tests/stEIP1559/senderBalanceFiller.yml + +@manually-enhanced: Do not overwrite. Balance derived from gas/fee inputs. """ import pytest from execution_testing import ( - EOA, Account, - Address, Alloc, - Bytes, Environment, + Fork, StateTestFiller, Transaction, ) @@ -29,49 +27,58 @@ @pytest.mark.ported_from( ["state_tests/stEIP1559/senderBalanceFiller.yml"], ) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable +@pytest.mark.valid_from("London") def test_sender_balance( state_test: StateTestFiller, pre: Alloc, + fork: Fork, ) -> None: - """The execution records the EIP-1559 transaction origin balance to...""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = EOA( - key=0xE04D1AC7DDDA0C98397D56A0B501E960D4CD325A39286919AC23C1A07009A869 - ) + """Origin balance during execution reflects the effective gas price.""" + base_fee = 11 + priority_fee = 100 + max_fee = 1000 + sender_balance = 0xDE0B6B3A7640000 + + # The effective gas price is base + priority (kept below max_fee, so the + # validity check would reserve more — the point of the test). + effective_gas_price = base_fee + priority_fee + + env = Environment(base_fee_per_gas=base_fee) + sender = pre.fund_eoa(amount=sender_balance) - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=11, - gas_limit=30000000, + # Source: yul: { sstore(0, balance(caller())) } + target_code = ( + Op.SSTORE( + key=0x0, + value=Op.BALANCE(address=Op.CALLER, address_warm=False), + key_warm=False, + original_value=0, + new_value=1, + ) + + Op.STOP ) + target = pre.deploy_contract(code=target_code) - pre[sender] = Account(balance=0xDE0B6B3A7640000) - # Source: yul - # london - # { - # sstore(0, balance(caller())) - # } - target = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x0, value=Op.BALANCE(address=Op.CALLER)) + Op.STOP, - nonce=0, - address=Address(0x420132F96200BA8E5C98298A85633C35C4F052EF), # noqa: E501 + # Size the gas limit to the work done, so the upfront charge (and thus the + # observed balance) tracks the fork's costs rather than a magic number. + gas_limit = ( + fork.transaction_intrinsic_cost_calculator()() + + target_code.gas_cost(fork) + + 1000 ) tx = Transaction( sender=sender, to=target, - data=Bytes(""), - gas_limit=60000, - max_fee_per_gas=1000, - max_priority_fee_per_gas=100, - access_list=[], + gas_limit=gas_limit, + max_fee_per_gas=max_fee, + max_priority_fee_per_gas=priority_fee, ) - post = {target: Account(storage={0: 0xDE0B6B3A6FE6060})} + post = { + target: Account( + storage={0: sender_balance - gas_limit * effective_gas_price} + ) + } state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP3855_push0/test_push0_gas.py b/tests/ported_static/stEIP3855_push0/test_push0_gas.py index e9a870b6600..a3314b55996 100644 --- a/tests/ported_static/stEIP3855_push0/test_push0_gas.py +++ b/tests/ported_static/stEIP3855_push0/test_push0_gas.py @@ -1,17 +1,18 @@ """ -Test_push0_gas. +Measure the gas cost of the PUSH0 instruction. Ported from: state_tests/Shanghai/stEIP3855_push0/push0GasFiller.yml + +@manually-enhanced: Do not overwrite. PUSH0 gas via CodeGasMeasure. """ import pytest from execution_testing import ( Account, - Address, Alloc, - Bytes, - Environment, + CodeGasMeasure, + Fork, StateTestFiller, Transaction, ) @@ -24,42 +25,29 @@ @pytest.mark.ported_from( ["state_tests/Shanghai/stEIP3855_push0/push0GasFiller.yml"], ) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable +@pytest.mark.valid_from("Shanghai") def test_push0_gas( state_test: StateTestFiller, pre: Alloc, + fork: Fork, ) -> None: - """Test_push0_gas.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0x989680) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=89128960, - ) - - # Source: raw - # 0x5a6000555f5a6000540360015500 - target = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE(key=0x0, value=Op.GAS) - + Op.PUSH0 - + Op.SSTORE(key=0x1, value=Op.SUB(Op.SLOAD(key=0x0), Op.GAS)) - + Op.STOP, - nonce=0, + """Measure PUSH0's gas cost against the fork-derived expectation.""" + sender = pre.fund_eoa() + + push0_code = Op.PUSH0 + target = pre.deploy_contract( + code=CodeGasMeasure( + code=push0_code, + extra_stack_items=1, + sstore_key=0x1, + ), ) tx = Transaction( sender=sender, to=target, - data=Bytes(""), - gas_limit=100000, ) - post = {target: Account(storage={0: 0x13496, 1: 22107})} + post = {target: Account(storage={0x1: push0_code.gas_cost(fork)})} - state_test(env=env, pre=pre, post=post, tx=tx) + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP3855_push0/test_push0_gas2.py b/tests/ported_static/stEIP3855_push0/test_push0_gas2.py index 7405bce15a3..85e6c5140be 100644 --- a/tests/ported_static/stEIP3855_push0/test_push0_gas2.py +++ b/tests/ported_static/stEIP3855_push0/test_push0_gas2.py @@ -1,24 +1,23 @@ """ -Test_push0_gas2. +Measure the gas cost of PUSH0 and of PUSH1 0x00: each case asserts its own +fork-derived cost, which together demonstrate PUSH0 is the cheaper encoding. Ported from: state_tests/Shanghai/stEIP3855_push0/push0Gas2Filler.yml + +@manually-enhanced: Do not overwrite. Opcode gas via CodeGasMeasure. """ import pytest from execution_testing import ( - EOA, Account, - Address, Alloc, - Environment, + Bytecode, + CodeGasMeasure, + Fork, StateTestFiller, Transaction, ) -from execution_testing.forks import Fork -from execution_testing.specs.static_state.expect_section import ( - resolve_expect_post, -) from execution_testing.vm import Op REFERENCE_SPEC_GIT_PATH = "N/A" @@ -28,138 +27,34 @@ @pytest.mark.ported_from( ["state_tests/Shanghai/stEIP3855_push0/push0Gas2Filler.yml"], ) -@pytest.mark.valid_from("Cancun") +@pytest.mark.valid_from("Shanghai") @pytest.mark.parametrize( - "d, g, v", - [ - pytest.param( - 0, - 0, - 0, - id="use_push0", - ), - pytest.param( - 1, - 0, - 0, - id="use_push1_00", - ), - ], + "opcode", + [Op.PUSH0, Op.PUSH1[0x00]], + ids=["use_push0", "use_push1_00"], ) -@pytest.mark.pre_alloc_mutable def test_push0_gas2( state_test: StateTestFiller, pre: Alloc, fork: Fork, - d: int, - g: int, - v: int, + opcode: Bytecode, ) -> None: - """Test_push0_gas2.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - contract_0 = Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B) - contract_1 = Address(0x0000000000000000000000000000000000001000) - contract_2 = Address(0x0000000000000000000000000000000000000200) - sender = EOA( - key=0x45A915E4D060149EB4365960E6A7A45F334393093061116B197E3240065FF2D8 - ) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=89128960, - ) - - pre[sender] = Account(balance=0x989680) - # Source: yul - # berlin - # { - # sstore(0, call(100000, shr(96, calldataload(0)), 0, 0, 0, 0, 0)) - # sstore(1, 1) - # } - contract_0 = pre.deploy_contract( # noqa: F841 - code=Op.SSTORE( - key=0x0, - value=Op.CALL( - gas=0x186A0, - address=Op.SHR(0x60, Op.CALLDATALOAD(offset=Op.DUP1)), - value=Op.DUP1, - args_offset=Op.DUP1, - args_size=Op.DUP1, - ret_offset=Op.DUP1, - ret_size=0x0, - ), - ) - + Op.SSTORE(key=Op.DUP1, value=0x1) - + Op.STOP, - nonce=0, - address=Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B), # noqa: E501 - ) - # Source: raw - # 0x5a5f5a9091039055 - contract_1 = pre.deploy_contract( # noqa: F841 - code=Op.GAS - + Op.PUSH0 - + Op.GAS - + Op.SWAP1 - + Op.SWAP2 - + Op.SUB - + Op.SWAP1 - + Op.SSTORE, - nonce=0, - address=Address(0x0000000000000000000000000000000000001000), # noqa: E501 - ) - # Source: raw - # 0x5a60005a9091039055 - contract_2 = pre.deploy_contract( # noqa: F841 - code=Op.GAS - + Op.PUSH1[0x0] - + Op.GAS - + Op.SWAP1 - + Op.SWAP2 - + Op.SUB - + Op.SWAP1 - + Op.SSTORE, - nonce=0, - address=Address(0x0000000000000000000000000000000000000200), # noqa: E501 + """Measure the parametrized push encoding's exact gas cost.""" + sender = pre.fund_eoa() + + measured = pre.deploy_contract( + code=CodeGasMeasure( + code=opcode, + extra_stack_items=1, + sstore_key=0x0, + ), ) - expect_entries_: list[dict] = [ - { - "indexes": {"data": [0], "gas": -1, "value": -1}, - "network": [">=Cancun"], - "result": { - contract_1: Account(storage={0: 4}, balance=0), - contract_0: Account(storage={0: 1, 1: 1}), - }, - }, - { - "indexes": {"data": [1], "gas": -1, "value": -1}, - "network": [">=Cancun"], - "result": { - contract_2: Account(storage={0: 5}, balance=0), - contract_0: Account(storage={0: 1, 1: 1}), - }, - }, - ] - - post, _exc = resolve_expect_post(expect_entries_, d, g, v, fork) - - tx_data = [ - contract_1, - contract_2, - ] - tx_gas = [300000] - tx = Transaction( sender=sender, - to=contract_0, - data=tx_data[d], - gas_limit=tx_gas[g], - error=_exc, + to=measured, ) - state_test(env=env, pre=pre, post=post, tx=tx) + post = {measured: Account(storage={0x0: opcode.gas_cost(fork)})} + + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stEIP5656_MCOPY/test_mcopy_copy_cost.py b/tests/ported_static/stEIP5656_MCOPY/test_mcopy_copy_cost.py index 8cd67806eee..12340a9259e 100644 --- a/tests/ported_static/stEIP5656_MCOPY/test_mcopy_copy_cost.py +++ b/tests/ported_static/stEIP5656_MCOPY/test_mcopy_copy_cost.py @@ -3,604 +3,73 @@ Ported from: state_tests/Cancun/stEIP5656_MCOPY/MCOPY_copy_costFiller.yml + +@manually-enhanced: Do not overwrite. The ported filler probed MCOPY cost via a +tight OOG gas boundary (55697); EIP-8037 reprices the instrumentation SSTORE +into state gas, breaking that boundary. Reframed to measure the MCOPY copy cost +directly with CodeGasMeasure over a pre-expanded memory (so no expansion is +charged), asserting the fork-derived `mcopy.gas_cost(fork)`. """ import pytest from execution_testing import ( Account, - Address, Alloc, - Environment, - Hash, + CodeGasMeasure, + Fork, StateTestFiller, Transaction, ) -from execution_testing.forks import Fork -from execution_testing.specs.static_state.expect_section import ( - resolve_expect_post, -) from execution_testing.vm import Op REFERENCE_SPEC_GIT_PATH = "N/A" REFERENCE_SPEC_VERSION = "N/A" +GAS_SLOT = 0x0 +# MSTORE at this offset grows memory to PREEXPANDED bytes, covering every +# (src, size) copy region below so the measured MCOPY never expands memory. +PREEXPAND_OFFSET = 0xAF00 +PREEXPANDED = PREEXPAND_OFFSET + 0x20 # 44832 bytes = 1401 words + +SRCS = [0x0, 0x1, 0x1F, 0x20] +SIZES = [0x0, 0x1, 0x1F, 0x20, 0x21, 0xAEDF, 0xAEE0, 0xAEE1] + @pytest.mark.ported_from( ["state_tests/Cancun/stEIP5656_MCOPY/MCOPY_copy_costFiller.yml"], ) @pytest.mark.valid_from("Cancun") -@pytest.mark.parametrize( - "d, g, v", - [ - pytest.param( - 0, - 0, - 0, - id="src0_size0-g0", - ), - pytest.param( - 0, - 1, - 0, - id="src0_size0-g1", - ), - pytest.param( - 1, - 0, - 0, - id="src0_size1-g0", - ), - pytest.param( - 1, - 1, - 0, - id="src0_size1-g1", - ), - pytest.param( - 2, - 0, - 0, - id="src0_size31-g0", - ), - pytest.param( - 2, - 1, - 0, - id="src0_size31-g1", - ), - pytest.param( - 3, - 0, - 0, - id="src0_size32-g0", - ), - pytest.param( - 3, - 1, - 0, - id="src0_size32-g1", - ), - pytest.param( - 4, - 0, - 0, - id="src0_size33-g0", - ), - pytest.param( - 4, - 1, - 0, - id="src0_size33-g1", - ), - pytest.param( - 5, - 0, - 0, - id="src0_size44767-g0", - ), - pytest.param( - 5, - 1, - 0, - id="src0_size44767-g1", - ), - pytest.param( - 6, - 0, - 0, - id="src0_size44768-g0", - ), - pytest.param( - 6, - 1, - 0, - id="src0_size44768-g1", - ), - pytest.param( - 7, - 0, - 0, - id="src0_size44769-g0", - ), - pytest.param( - 7, - 1, - 0, - id="src0_size44769-g1", - ), - pytest.param( - 8, - 0, - 0, - id="src1_size0-g0", - ), - pytest.param( - 8, - 1, - 0, - id="src1_size0-g1", - ), - pytest.param( - 9, - 0, - 0, - id="src1_size1-g0", - ), - pytest.param( - 9, - 1, - 0, - id="src1_size1-g1", - ), - pytest.param( - 10, - 0, - 0, - id="src1_size31-g0", - ), - pytest.param( - 10, - 1, - 0, - id="src1_size31-g1", - ), - pytest.param( - 11, - 0, - 0, - id="src1_size32-g0", - ), - pytest.param( - 11, - 1, - 0, - id="src1_size32-g1", - ), - pytest.param( - 12, - 0, - 0, - id="src1_size33-g0", - ), - pytest.param( - 12, - 1, - 0, - id="src1_size33-g1", - ), - pytest.param( - 13, - 0, - 0, - id="src1_size44767-g0", - ), - pytest.param( - 13, - 1, - 0, - id="src1_size44767-g1", - ), - pytest.param( - 14, - 0, - 0, - id="src1_size44768-g0", - ), - pytest.param( - 14, - 1, - 0, - id="src1_size44768-g1", - ), - pytest.param( - 15, - 0, - 0, - id="src1_size44769-g0", - ), - pytest.param( - 15, - 1, - 0, - id="src1_size44769-g1", - ), - pytest.param( - 16, - 0, - 0, - id="src31_size0-g0", - ), - pytest.param( - 16, - 1, - 0, - id="src31_size0-g1", - ), - pytest.param( - 17, - 0, - 0, - id="src31_size1-g0", - ), - pytest.param( - 17, - 1, - 0, - id="src31_size1-g1", - ), - pytest.param( - 18, - 0, - 0, - id="src31_size31-g0", - ), - pytest.param( - 18, - 1, - 0, - id="src31_size31-g1", - ), - pytest.param( - 19, - 0, - 0, - id="src31_size32-g0", - ), - pytest.param( - 19, - 1, - 0, - id="src31_size32-g1", - ), - pytest.param( - 20, - 0, - 0, - id="src31_size33-g0", - ), - pytest.param( - 20, - 1, - 0, - id="src31_size33-g1", - ), - pytest.param( - 21, - 0, - 0, - id="src31_size44767-g0", - ), - pytest.param( - 21, - 1, - 0, - id="src31_size44767-g1", - ), - pytest.param( - 22, - 0, - 0, - id="src31_size44768-g0", - ), - pytest.param( - 22, - 1, - 0, - id="src31_size44768-g1", - ), - pytest.param( - 23, - 0, - 0, - id="src31_size44769-g0", - ), - pytest.param( - 23, - 1, - 0, - id="src31_size44769-g1", - ), - pytest.param( - 24, - 0, - 0, - id="src32_size0-g0", - ), - pytest.param( - 24, - 1, - 0, - id="src32_size0-g1", - ), - pytest.param( - 25, - 0, - 0, - id="src32_size1-g0", - ), - pytest.param( - 25, - 1, - 0, - id="src32_size1-g1", - ), - pytest.param( - 26, - 0, - 0, - id="src32_size31-g0", - ), - pytest.param( - 26, - 1, - 0, - id="src32_size31-g1", - ), - pytest.param( - 27, - 0, - 0, - id="src32_size32-g0", - ), - pytest.param( - 27, - 1, - 0, - id="src32_size32-g1", - ), - pytest.param( - 28, - 0, - 0, - id="src32_size33-g0", - ), - pytest.param( - 28, - 1, - 0, - id="src32_size33-g1", - ), - pytest.param( - 29, - 0, - 0, - id="src32_size44767-g0", - ), - pytest.param( - 29, - 1, - 0, - id="src32_size44767-g1", - ), - pytest.param( - 30, - 0, - 0, - id="src32_size44768-g0", - ), - pytest.param( - 30, - 1, - 0, - id="src32_size44768-g1", - ), - pytest.param( - 31, - 0, - 0, - id="src32_size44769-g0", - ), - pytest.param( - 31, - 1, - 0, - id="src32_size44769-g1", - ), - ], -) +@pytest.mark.parametrize("size", SIZES, ids=lambda s: f"size{s}") +@pytest.mark.parametrize("src", SRCS, ids=lambda s: f"src{s}") def test_mcopy_copy_cost( state_test: StateTestFiller, pre: Alloc, fork: Fork, - d: int, - g: int, - v: int, + src: int, + size: int, ) -> None: - """Test cases for the cost of memory copy in the MCOPY instruction.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0x3B9ACA00) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1687174231, - prev_randao=0x20000, - base_fee_per_gas=10, + """Measure the MCOPY copy cost (linear in size, independent of source).""" + # Memory is pre-expanded past the largest copy region, so the measured + # MCOPY charges only its base + per-word copy cost, never expansion. + mcopy = Op.MCOPY( + dest_offset=0x0, + offset=src, + size=size, + data_size=size, + old_memory_size=PREEXPANDED, + new_memory_size=PREEXPANDED, ) - - # Source: yul - # shanghai optimise { - # function mcopy(dst, src, size) { verbatim_3i_0o(hex"5e", dst, src, size) } # noqa: E501 - # - # // Put a flag in storage indicating successful execution (will be reverted in case of OOG). # noqa: E501 - # sstore(0, 1) - # - # // Expand memory to cover memory expansion cost before MCOPY. - # // The test uses up to 1400 memory words. - # mstore(44800, 1) - # - # // MCOPY using src and size from CALLDATA to 0 destination. - # mcopy(0, calldataload(0), calldataload(32)) - # } - target = pre.deploy_contract( # noqa: F841 - code=Op.JUMP(pc=0xC) - + Op.JUMPDEST - + Op.MCOPY(dest_offset=Op.DUP3, offset=Op.DUP3, size=Op.DUP3) - + Op.POP * 3 - + Op.JUMP - + Op.JUMPDEST - + Op.SSTORE(key=Op.PUSH0, value=0x1) - + Op.MSTORE(offset=0xAF00, value=0x1) - + Op.PUSH1[0x22] - + Op.CALLDATALOAD(offset=0x20) - + Op.CALLDATALOAD(offset=Op.PUSH0) - + Op.PUSH0 - + Op.JUMP(pc=0x3) - + Op.JUMPDEST, - nonce=1, + contract = pre.deploy_contract( + code=Op.MSTORE(offset=PREEXPAND_OFFSET, value=0x1) + + CodeGasMeasure( + code=mcopy, + extra_stack_items=0, + sstore_key=GAS_SLOT, + ), ) - expect_entries_: list[dict] = [ - { - "indexes": { - "data": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - ], - "gas": 0, - "value": -1, - }, - "network": [">=Cancun"], - "result": {target: Account(storage={0: 1})}, - }, - { - "indexes": { - "data": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 16, - 17, - 18, - 19, - 20, - 24, - 25, - 26, - 27, - 28, - ], - "gas": 1, - "value": -1, - }, - "network": [">=Cancun"], - "result": {target: Account(storage={0: 1})}, - }, - { - "indexes": { - "data": [13, 14, 15, 21, 22, 23, 29, 30, 31], - "gas": 1, - "value": -1, - }, - "network": [">=Cancun"], - "result": {target: Account(storage={0: 0})}, - }, - ] - - post, _exc = resolve_expect_post(expect_entries_, d, g, v, fork) + tx = Transaction(sender=pre.fund_eoa(), to=contract) - tx_data = [ - Hash(0x0) + Hash(0x0), - Hash(0x0) + Hash(0x1), - Hash(0x0) + Hash(0x1F), - Hash(0x0) + Hash(0x20), - Hash(0x0) + Hash(0x21), - Hash(0x0) + Hash(0xAEDF), - Hash(0x0) + Hash(0xAEE0), - Hash(0x0) + Hash(0xAEE1), - Hash(0x1) + Hash(0x0), - Hash(0x1) + Hash(0x1), - Hash(0x1) + Hash(0x1F), - Hash(0x1) + Hash(0x20), - Hash(0x1) + Hash(0x21), - Hash(0x1) + Hash(0xAEDF), - Hash(0x1) + Hash(0xAEE0), - Hash(0x1) + Hash(0xAEE1), - Hash(0x1F) + Hash(0x0), - Hash(0x1F) + Hash(0x1), - Hash(0x1F) + Hash(0x1F), - Hash(0x1F) + Hash(0x20), - Hash(0x1F) + Hash(0x21), - Hash(0x1F) + Hash(0xAEDF), - Hash(0x1F) + Hash(0xAEE0), - Hash(0x1F) + Hash(0xAEE1), - Hash(0x20) + Hash(0x0), - Hash(0x20) + Hash(0x1), - Hash(0x20) + Hash(0x1F), - Hash(0x20) + Hash(0x20), - Hash(0x20) + Hash(0x21), - Hash(0x20) + Hash(0xAEDF), - Hash(0x20) + Hash(0xAEE0), - Hash(0x20) + Hash(0xAEE1), - ] - tx_gas = [100000, 55697] - - tx = Transaction( - sender=sender, - to=target, - data=tx_data[d], - gas_limit=tx_gas[g], - error=_exc, - ) + post = {contract: Account(storage={GAS_SLOT: mcopy.gas_cost(fork)})} - state_test(env=env, pre=pre, post=post, tx=tx) + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stMemoryTest/test_call_data_copy_offset.py b/tests/ported_static/stMemoryTest/test_call_data_copy_offset.py deleted file mode 100644 index 2cd7a467e4b..00000000000 --- a/tests/ported_static/stMemoryTest/test_call_data_copy_offset.py +++ /dev/null @@ -1,97 +0,0 @@ -""" -Test_call_data_copy_offset. - -Ported from: -state_tests/stMemoryTest/callDataCopyOffsetFiller.json -""" - -import pytest -from execution_testing import ( - EOA, - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - ["state_tests/stMemoryTest/callDataCopyOffsetFiller.json"], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_call_data_copy_offset( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_call_data_copy_offset.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - contract_0 = Address(0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE) - contract_1 = Address(0x095E7BAEA6A6C7C4C2DFEB977EFAC326AF552D87) - sender = EOA( - key=0x45A915E4D060149EB4365960E6A7A45F334393093061116B197E3240065FF2D8 - ) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=1000000, - ) - - pre[sender] = Account(balance=0xDE0B6B3A7640000) - # Source: lll - # { (MSTORE 0x00 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (CALLDATACOPY 0x00 0xffff 0x10) (SSTORE 0x00 (MLOAD 0x00)) } # noqa: E501 - contract_0 = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE( - offset=0x0, - value=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, # noqa: E501 - ) - + Op.CALLDATACOPY(dest_offset=0x0, offset=0xFFFF, size=0x10) - + Op.SSTORE(key=0x0, value=Op.MLOAD(offset=0x0)) - + Op.STOP, - balance=0xDE0B6B3A7640000, - nonce=1, - address=Address(0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE), # noqa: E501 - ) - # Source: yul - # berlin { mstore(0, 0x0123456789abcdef) pop(call(0xffff,0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee,0, 0,0x0f, 0,0)) } # noqa: E501 - contract_1 = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=0x123456789ABCDEF) - + Op.CALL( - gas=0xFFFF, - address=contract_0, - value=Op.DUP1, - args_offset=Op.DUP2, - args_size=0xF, - ret_offset=Op.DUP1, - ret_size=0x0, - ) - + Op.STOP, - balance=0xDE0B6B3A7640000, - nonce=1, - address=Address(0x095E7BAEA6A6C7C4C2DFEB977EFAC326AF552D87), # noqa: E501 - ) - - tx = Transaction( - sender=sender, - to=contract_1, - data=Bytes(""), - gas_limit=400000, - value=0x186A0, - ) - - post = { - contract_0: Account(storage={0: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF}) - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stMemoryTest/test_code_copy_offset.py b/tests/ported_static/stMemoryTest/test_code_copy_offset.py deleted file mode 100644 index 36b096523aa..00000000000 --- a/tests/ported_static/stMemoryTest/test_code_copy_offset.py +++ /dev/null @@ -1,93 +0,0 @@ -""" -Test_code_copy_offset. - -Ported from: -state_tests/stMemoryTest/codeCopyOffsetFiller.json -""" - -import pytest -from execution_testing import ( - EOA, - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - ["state_tests/stMemoryTest/codeCopyOffsetFiller.json"], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_code_copy_offset( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_code_copy_offset.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = EOA( - key=0xB1F4CBC3A50042184425A6F9E996D0910F7BA879457CE5DAC5C71E498AD3C005 - ) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=1000000, - ) - - pre[sender] = Account(balance=0xDE0B6B3A7640000) - # Source: lll - # { (MSTORE 0x00 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) (CODECOPY 0x00 0xffff 0x10) (SSTORE 0x00 (MLOAD 0x00)) } # noqa: E501 - addr = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE( - offset=0x0, - value=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, # noqa: E501 - ) - + Op.CODECOPY(dest_offset=0x0, offset=0xFFFF, size=0x10) - + Op.SSTORE(key=0x0, value=Op.MLOAD(offset=0x0)) - + Op.STOP, - balance=0xDE0B6B3A7640000, - nonce=1, - address=Address(0x27D16E1D3CC862149F1E7162E612635FCAEF9FF4), # noqa: E501 - ) - # Source: yul - # berlin { mstore(0, 0x0123456789abcdef) pop(call(0xffff, , 0, 0, 0x0f, 0, 0)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=0x123456789ABCDEF) - + Op.CALL( - gas=0xFFFF, - address=addr, - value=Op.DUP1, - args_offset=Op.DUP2, - args_size=0xF, - ret_offset=Op.DUP1, - ret_size=0x0, - ) - + Op.STOP, - balance=0xDE0B6B3A7640000, - nonce=1, - address=Address(0xAF89A7504341A87E1CFDFFD483A00A4688469B3D), # noqa: E501 - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=400000, - value=0x186A0, - ) - - post = {addr: Account(storage={0: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF})} - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stMemoryTest/test_copy_offset.py b/tests/ported_static/stMemoryTest/test_copy_offset.py new file mode 100644 index 00000000000..26f32b060a9 --- /dev/null +++ b/tests/ported_static/stMemoryTest/test_copy_offset.py @@ -0,0 +1,77 @@ +""" +Test CODECOPY / CALLDATACOPY reading from an out-of-bounds source offset, +which yields zeros. + +Ported from: +state_tests/stMemoryTest/codeCopyOffsetFiller.json +state_tests/stMemoryTest/callDataCopyOffsetFiller.json + +@manually-enhanced: Do not overwrite. CODECOPY/CALLDATACOPY OOB-offset +zero-fill folded into one parametrize; delivery-CALL dropped; dynamic +addresses; nonzero tx calldata so a wrong in-bounds offset is observable. +""" + +import pytest +from execution_testing import ( + Account, + Alloc, + Fork, + StateTestFiller, + Transaction, +) +from execution_testing.vm import Op + +REFERENCE_SPEC_GIT_PATH = "N/A" +REFERENCE_SPEC_VERSION = "N/A" + +# Copy 16 bytes from a source offset far past the end of code/calldata; the +# out-of-bounds region reads as zeros, which overwrite memory bytes 0..15 +# (the most-significant half of the word MLOAD reads back), leaving only the +# low 128 bits of the pre-filled word set to 0xFF. +OOB_OFFSET = 0xFFFF +COPY_SIZE = 0x10 +EXPECTED = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +# Nonzero calldata makes the CALLDATACOPY arm discriminate a wrong (in-bounds) +# source offset from the correct out-of-bounds zero-fill; with empty calldata +# every offset would read zeros and the assertion would be vacuous. +TX_DATA = bytes(range(1, 33)) + + +@pytest.mark.ported_from( + [ + "state_tests/stMemoryTest/codeCopyOffsetFiller.json", + "state_tests/stMemoryTest/callDataCopyOffsetFiller.json", + ], +) +@pytest.mark.valid_from("Frontier") +@pytest.mark.parametrize( + "copy_op", + [ + pytest.param(Op.CODECOPY, id="code_copy_offset"), + pytest.param(Op.CALLDATACOPY, id="call_data_copy_offset"), + ], +) +def test_copy_offset( + state_test: StateTestFiller, + pre: Alloc, + fork: Fork, + copy_op: Op, +) -> None: + """Copying from an out-of-bounds source offset yields zeros.""" + contract = pre.deploy_contract( + code=Op.MSTORE(offset=0x0, value=(1 << 256) - 1) + + copy_op(dest_offset=0x0, offset=OOB_OFFSET, size=COPY_SIZE) + + Op.SSTORE(key=0x0, value=Op.MLOAD(offset=0x0)) + + Op.STOP, + ) + + tx = Transaction( + sender=pre.fund_eoa(), + to=contract, + data=TX_DATA, + protected=fork.supports_protected_txs(), + ) + + post = {contract: Account(storage={0: EXPECTED})} + + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value.py b/tests/ported_static/stNonZeroCallsTest/test_non_zero_value.py new file mode 100644 index 00000000000..27f734dbb65 --- /dev/null +++ b/tests/ported_static/stNonZeroCallsTest/test_non_zero_value.py @@ -0,0 +1,185 @@ +""" +Measure the gas cost of CALL / CALLCODE / DELEGATECALL carrying non-zero +value to targets in various pre-states, using CodeGasMeasure. + +Ported from: +state_tests/stNonZeroCallsTest/NonZeroValue_CALLFiller.json +state_tests/stNonZeroCallsTest/NonZeroValue_CALL_ToEmpty_ParisFiller.json +state_tests/stNonZeroCallsTest/NonZeroValue_CALL_ToOneStorageKey_ParisFiller.json +state_tests/stNonZeroCallsTest/NonZeroValue_CALLCODEFiller.json +state_tests/stNonZeroCallsTest/NonZeroValue_CALLCODE_ToEmpty_ParisFiller.json +state_tests/stNonZeroCallsTest/NonZeroValue_CALLCODE_ToOneStorageKey_ParisFiller.json +state_tests/stNonZeroCallsTest/NonZeroValue_DELEGATECALLFiller.json +state_tests/stNonZeroCallsTest/NonZeroValue_DELEGATECALL_ToEmpty_ParisFiller.json +state_tests/stNonZeroCallsTest/NonZeroValue_DELEGATECALL_ToOneStorageKey_ParisFiller.json +state_tests/stNonZeroCallsTest/NonZeroValue_DELEGATECALL_ToNonNonZeroBalanceFiller.json + +@manually-enhanced: Do not overwrite. Call gas via CodeGasMeasure; the call +success flag is stored inside the measured window (a wrongly failed call is +gas-identical to success against an empty callee, so gas alone cannot +discriminate). +""" + +import pytest +from execution_testing import ( + Account, + Alloc, + CodeGasMeasure, + Fork, + StateTestFiller, + Transaction, +) +from execution_testing.vm import Op + +REFERENCE_SPEC_GIT_PATH = "N/A" +REFERENCE_SPEC_VERSION = "N/A" + +CONTRACT_BALANCE = 100 +CALL_VALUE = 1 +EXISTING_BALANCE = 10 +NONZERO_BALANCE = 100 +FORWARDED_GAS = 0xEA60 +GAS_SLOT = 0x64 +SUCCESS_SLOT = 0x1 + + +@pytest.mark.ported_from( + [ + "state_tests/stNonZeroCallsTest/NonZeroValue_CALLFiller.json", + "state_tests/stNonZeroCallsTest/NonZeroValue_CALL_ToEmpty_ParisFiller.json", # noqa: E501 + "state_tests/stNonZeroCallsTest/NonZeroValue_CALL_ToOneStorageKey_ParisFiller.json", # noqa: E501 + "state_tests/stNonZeroCallsTest/NonZeroValue_CALLCODEFiller.json", + "state_tests/stNonZeroCallsTest/NonZeroValue_CALLCODE_ToEmpty_ParisFiller.json", # noqa: E501 + "state_tests/stNonZeroCallsTest/NonZeroValue_CALLCODE_ToOneStorageKey_ParisFiller.json", # noqa: E501 + "state_tests/stNonZeroCallsTest/NonZeroValue_DELEGATECALLFiller.json", + "state_tests/stNonZeroCallsTest/NonZeroValue_DELEGATECALL_ToEmpty_ParisFiller.json", # noqa: E501 + "state_tests/stNonZeroCallsTest/NonZeroValue_DELEGATECALL_ToOneStorageKey_ParisFiller.json", # noqa: E501 + "state_tests/stNonZeroCallsTest/NonZeroValue_DELEGATECALL_ToNonNonZeroBalanceFiller.json", # noqa: E501 + ], +) +@pytest.mark.valid_from("Berlin") +@pytest.mark.parametrize( + "opcode, target_kind", + [ + pytest.param(Op.CALL, "nonexistent", id="call"), + pytest.param(Op.CALL, "empty", id="call_to_empty"), + pytest.param(Op.CALL, "one_storage_key", id="call_to_one_storage_key"), + pytest.param(Op.CALLCODE, "nonexistent", id="callcode"), + pytest.param(Op.CALLCODE, "empty", id="callcode_to_empty"), + pytest.param( + Op.CALLCODE, "one_storage_key", id="callcode_to_one_storage_key" + ), + pytest.param(Op.DELEGATECALL, "nonexistent", id="delegatecall"), + pytest.param(Op.DELEGATECALL, "empty", id="delegatecall_to_empty"), + pytest.param( + Op.DELEGATECALL, + "one_storage_key", + id="delegatecall_to_one_storage_key", + ), + pytest.param( + Op.DELEGATECALL, + "nonzero_balance", + id="delegatecall_to_nonzero_balance", + ), + ], +) +def test_non_zero_value( + state_test: StateTestFiller, + pre: Alloc, + fork: Fork, + opcode: Op, + target_kind: str, +) -> None: + """Measure call-family gas to a cold target of each pre-state.""" + transfers_value = opcode != Op.DELEGATECALL + + # Set up the target account in the requested pre-state. + if target_kind == "nonexistent": + call_target = pre.nonexistent_account() + target_balance = 0 + target_storage: dict = {} + elif target_kind == "one_storage_key": + target_balance = EXISTING_BALANCE + target_storage = {0x0: 0x1} + call_target = pre.deploy_contract( + code=b"", balance=target_balance, storage=target_storage + ) + else: + target_balance = ( + NONZERO_BALANCE + if target_kind == "nonzero_balance" + else EXISTING_BALANCE + ) + target_storage = {} + call_target = pre.fund_eoa(amount=target_balance) + + # Only a plain CALL forwards value to the target (and can create it); + # CALLCODE keeps value in the caller's context, DELEGATECALL has no value. + account_new = opcode == Op.CALL and target_kind == "nonexistent" + received = CALL_VALUE if opcode == Op.CALL else 0 + + if opcode == Op.DELEGATECALL: + call_code = Op.DELEGATECALL( + gas=FORWARDED_GAS, + address=call_target, + address_warm=False, + ) + else: + call_code = opcode( + gas=FORWARDED_GAS, + address=call_target, + value=CALL_VALUE, + address_warm=False, + value_transfer=True, + account_new=account_new, + ) + + # Store the call's success flag inside the measured window: a wrongly + # failed call is otherwise indistinguishable from a success into empty + # code (same gas, balances, and storage for CALLCODE/DELEGATECALL). + store_code = Op.SSTORE( + SUCCESS_SLOT, + call_code, + key_warm=False, + original_value=0, + new_value=1, + ) + + contract = pre.deploy_contract( + code=CodeGasMeasure( + code=store_code, + extra_stack_items=0, + sstore_key=GAS_SLOT, + ), + balance=CONTRACT_BALANCE, + ) + + tx = Transaction( + sender=pre.fund_eoa(), + to=contract, + state_gas_reservoir=0, + ) + + # A value-bearing call whose callee consumes nothing returns the stipend. + measured = store_code.gas_cost(fork) + if transfers_value: + measured -= fork.gas_costs().CALL_STIPEND + + if target_kind == "nonexistent": + target_account = ( + Account(balance=CALL_VALUE) if account_new else Account.NONEXISTENT + ) + else: + target_account = Account( + balance=target_balance + received, storage=target_storage + ) + + post = { + contract: Account( + storage={GAS_SLOT: measured, SUCCESS_SLOT: 1}, + balance=CONTRACT_BALANCE - received, + ), + call_target: target_account, + } + + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_call.py b/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_call.py deleted file mode 100644 index aae4b4903c5..00000000000 --- a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_call.py +++ /dev/null @@ -1,88 +0,0 @@ -""" -Test_non_zero_value_call. - -Ported from: -state_tests/stNonZeroCallsTest/NonZeroValue_CALLFiller.json -""" - -import pytest -from execution_testing import ( - EOA, - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - ["state_tests/stNonZeroCallsTest/NonZeroValue_CALLFiller.json"], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_non_zero_value_call( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_non_zero_value_call.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - contract_0 = Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B) - sender = EOA( - key=0x45A915E4D060149EB4365960E6A7A45F334393093061116B197E3240065FF2D8 - ) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - pre[sender] = Account(balance=0xE8D4A51000) - # Source: lll - # { [0](GAS) [[1]] (CALL 60000 0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b 1 0 0 0 0) [[100]] (SUB @0 (GAS)) } # noqa: E501 - contract_0 = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.SSTORE( - key=0x1, - value=Op.CALL( - gas=0xEA60, - address=0xC94F5374FCE5EDBC8E2A8697C15331677E6EBF0B, - value=0x1, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ), - ) - + Op.SSTORE(key=0x64, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - balance=100, - nonce=0, - address=Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B), # noqa: E501 - ) - - tx = Transaction( - sender=sender, - to=contract_0, - data=Bytes(""), - gas_limit=600000, - ) - - post = { - contract_0: Account(storage={1: 1, 100: 56435}, balance=99), - Address(0xC94F5374FCE5EDBC8E2A8697C15331677E6EBF0B): Account( - balance=1 - ), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_call_to_empty_paris.py b/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_call_to_empty_paris.py deleted file mode 100644 index fcfb431e5b8..00000000000 --- a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_call_to_empty_paris.py +++ /dev/null @@ -1,83 +0,0 @@ -""" -Test_non_zero_value_call_to_empty_paris. - -Ported from: -state_tests/stNonZeroCallsTest/NonZeroValue_CALL_ToEmpty_ParisFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stNonZeroCallsTest/NonZeroValue_CALL_ToEmpty_ParisFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_non_zero_value_call_to_empty_paris( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_non_zero_value_call_to_empty_paris.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - addr = pre.fund_eoa(amount=10) # noqa: F841 - # Source: lll - # { [0](GAS) [[1]] (CALL 60000 1 0 0 0 0) [[100]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.SSTORE( - key=0x1, - value=Op.CALL( - gas=0xEA60, - address=addr, - value=0x1, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ), - ) - + Op.SSTORE(key=0x64, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - balance=1000, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=600000, - ) - - post = { - addr: Account(balance=11), - target: Account(storage={1: 1, 100: 31435}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_call_to_one_storage_key_paris.py b/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_call_to_one_storage_key_paris.py deleted file mode 100644 index 76dd7cdb2ba..00000000000 --- a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_call_to_one_storage_key_paris.py +++ /dev/null @@ -1,89 +0,0 @@ -""" -Test_non_zero_value_call_to_one_storage_key_paris. - -Ported from: -state_tests/stNonZeroCallsTest/NonZeroValue_CALL_ToOneStorageKey_ParisFiller.json -""" - -import pytest -from execution_testing import ( - EOA, - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stNonZeroCallsTest/NonZeroValue_CALL_ToOneStorageKey_ParisFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_non_zero_value_call_to_one_storage_key_paris( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_non_zero_value_call_to_one_storage_key_paris.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - addr = Address(0x4757608F18B70777AE788DD4056EEED52F7AA68F) - sender = EOA( - key=0x4F31B3206FBF0E0E598B9B1A7D8AC86302A0FF1D8930738F1BEBAE9B67173E52 - ) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - pre[sender] = Account(balance=0xE8D4A51000) - pre[addr] = Account(balance=10, storage={0: 1}) - # Source: lll - # { [0](GAS) [[1]] (CALL 60000 1 0 0 0 0) [[100]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.SSTORE( - key=0x1, - value=Op.CALL( - gas=0xEA60, - address=0x4757608F18B70777AE788DD4056EEED52F7AA68F, - value=0x1, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ), - ) - + Op.SSTORE(key=0x64, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - balance=1000, - nonce=0, - address=Address(0xF6029618CF51CA5236AFC14EAD1FBE0739573C23), # noqa: E501 - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=600000, - ) - - post = { - addr: Account(storage={0: 1}, balance=11), - target: Account(storage={1: 1, 100: 31435}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_callcode.py b/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_callcode.py deleted file mode 100644 index 55b2e219e75..00000000000 --- a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_callcode.py +++ /dev/null @@ -1,88 +0,0 @@ -""" -Test_non_zero_value_callcode. - -Ported from: -state_tests/stNonZeroCallsTest/NonZeroValue_CALLCODEFiller.json -""" - -import pytest -from execution_testing import ( - EOA, - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - ["state_tests/stNonZeroCallsTest/NonZeroValue_CALLCODEFiller.json"], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_non_zero_value_callcode( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_non_zero_value_callcode.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - contract_0 = Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B) - sender = EOA( - key=0x45A915E4D060149EB4365960E6A7A45F334393093061116B197E3240065FF2D8 - ) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - pre[sender] = Account(balance=0xE8D4A51000) - # Source: lll - # { [0](GAS) [[1]] (CALLCODE 60000 0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b 1 0 0 0 0) [[100]] (SUB @0 (GAS)) } # noqa: E501 - contract_0 = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.SSTORE( - key=0x1, - value=Op.CALLCODE( - gas=0xEA60, - address=0xC94F5374FCE5EDBC8E2A8697C15331677E6EBF0B, - value=0x1, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ), - ) - + Op.SSTORE(key=0x64, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - balance=100, - nonce=0, - address=Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B), # noqa: E501 - ) - - tx = Transaction( - sender=sender, - to=contract_0, - data=Bytes(""), - gas_limit=600000, - ) - - post = { - contract_0: Account(storage={1: 1, 100: 31435}), - Address( - 0xC94F5374FCE5EDBC8E2A8697C15331677E6EBF0B - ): Account.NONEXISTENT, - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_callcode_to_empty_paris.py b/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_callcode_to_empty_paris.py deleted file mode 100644 index 36f7f5f9c28..00000000000 --- a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_callcode_to_empty_paris.py +++ /dev/null @@ -1,83 +0,0 @@ -""" -Test_non_zero_value_callcode_to_empty_paris. - -Ported from: -state_tests/stNonZeroCallsTest/NonZeroValue_CALLCODE_ToEmpty_ParisFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stNonZeroCallsTest/NonZeroValue_CALLCODE_ToEmpty_ParisFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_non_zero_value_callcode_to_empty_paris( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_non_zero_value_callcode_to_empty_paris.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - addr = pre.fund_eoa(amount=10) # noqa: F841 - # Source: lll - # { [0](GAS) [[1]] (CALLCODE 60000 1 0 0 0 0) [[100]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.SSTORE( - key=0x1, - value=Op.CALLCODE( - gas=0xEA60, - address=addr, - value=0x1, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ), - ) - + Op.SSTORE(key=0x64, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - balance=100, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=600000, - ) - - post = { - addr: Account(storage={}, code=b"", balance=10, nonce=0), - target: Account(storage={1: 1, 100: 31435}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_callcode_to_one_storage_key_paris.py b/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_callcode_to_one_storage_key_paris.py deleted file mode 100644 index 24406f1746d..00000000000 --- a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_callcode_to_one_storage_key_paris.py +++ /dev/null @@ -1,89 +0,0 @@ -""" -Test_non_zero_value_callcode_to_one_storage_key_paris. - -Ported from: -state_tests/stNonZeroCallsTest/NonZeroValue_CALLCODE_ToOneStorageKey_ParisFiller.json -""" - -import pytest -from execution_testing import ( - EOA, - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stNonZeroCallsTest/NonZeroValue_CALLCODE_ToOneStorageKey_ParisFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_non_zero_value_callcode_to_one_storage_key_paris( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_non_zero_value_callcode_to_one_storage_key_paris.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - addr = Address(0x4757608F18B70777AE788DD4056EEED52F7AA68F) - sender = EOA( - key=0x4F31B3206FBF0E0E598B9B1A7D8AC86302A0FF1D8930738F1BEBAE9B67173E52 - ) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - pre[sender] = Account(balance=0xE8D4A51000) - pre[addr] = Account(balance=10, storage={0: 1}) - # Source: lll - # { [0](GAS) [[1]] (CALLCODE 60000 1 0 0 0 0) [[100]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.SSTORE( - key=0x1, - value=Op.CALLCODE( - gas=0xEA60, - address=0x4757608F18B70777AE788DD4056EEED52F7AA68F, - value=0x1, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ), - ) - + Op.SSTORE(key=0x64, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - balance=1000, - nonce=0, - address=Address(0xB7BB61C75BE691459CEF9A8FD7EC074933FA1D1F), # noqa: E501 - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=600000, - ) - - post = { - addr: Account(storage={0: 1}, balance=10), - target: Account(storage={1: 1, 100: 31435}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_delegatecall.py b/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_delegatecall.py deleted file mode 100644 index ca0b9e66a79..00000000000 --- a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_delegatecall.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Test_non_zero_value_delegatecall. - -Ported from: -state_tests/stNonZeroCallsTest/NonZeroValue_DELEGATECALLFiller.json -""" - -import pytest -from execution_testing import ( - EOA, - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - ["state_tests/stNonZeroCallsTest/NonZeroValue_DELEGATECALLFiller.json"], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_non_zero_value_delegatecall( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_non_zero_value_delegatecall.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - contract_0 = Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B) - sender = EOA( - key=0x45A915E4D060149EB4365960E6A7A45F334393093061116B197E3240065FF2D8 - ) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - pre[sender] = Account(balance=0xE8D4A51000) - # Source: lll - # { [0](GAS) [[1]] (DELEGATECALL 60000 0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b 0 0 0 0) [[100]] (SUB @0 (GAS)) } # noqa: E501 - contract_0 = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.SSTORE( - key=0x1, - value=Op.DELEGATECALL( - gas=0xEA60, - address=0xC94F5374FCE5EDBC8E2A8697C15331677E6EBF0B, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ), - ) - + Op.SSTORE(key=0x64, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - balance=1, - nonce=0, - address=Address(0xB94F5374FCE5EDBC8E2A8697C15331677E6EBF0B), # noqa: E501 - ) - - tx = Transaction( - sender=sender, - to=contract_0, - data=Bytes(""), - gas_limit=600000, - ) - - post = { - contract_0: Account(storage={1: 1, 100: 24732}), - Address( - 0xC94F5374FCE5EDBC8E2A8697C15331677E6EBF0B - ): Account.NONEXISTENT, - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_delegatecall_to_empty_paris.py b/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_delegatecall_to_empty_paris.py deleted file mode 100644 index 1c2a832d499..00000000000 --- a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_delegatecall_to_empty_paris.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Test_non_zero_value_delegatecall_to_empty_paris. - -Ported from: -state_tests/stNonZeroCallsTest/NonZeroValue_DELEGATECALL_ToEmpty_ParisFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stNonZeroCallsTest/NonZeroValue_DELEGATECALL_ToEmpty_ParisFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_non_zero_value_delegatecall_to_empty_paris( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_non_zero_value_delegatecall_to_empty_paris.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - addr = pre.fund_eoa(amount=10) # noqa: F841 - # Source: lll - # { [0](GAS) [[1]] (DELEGATECALL 60000 0 0 0 0) [[100]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.SSTORE( - key=0x1, - value=Op.DELEGATECALL( - gas=0xEA60, - address=addr, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ), - ) - + Op.SSTORE(key=0x64, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=600000, - ) - - post = { - addr: Account(storage={}, code=b"", balance=10, nonce=0), - target: Account(storage={1: 1, 100: 24732}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_delegatecall_to_non_non_zero_balance.py b/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_delegatecall_to_non_non_zero_balance.py deleted file mode 100644 index f4716bb2707..00000000000 --- a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_delegatecall_to_non_non_zero_balance.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Test_non_zero_value_delegatecall_to_non_non_zero_balance. - -Ported from: -state_tests/stNonZeroCallsTest/NonZeroValue_DELEGATECALL_ToNonNonZeroBalanceFiller.json -""" - -import pytest -from execution_testing import ( - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stNonZeroCallsTest/NonZeroValue_DELEGATECALL_ToNonNonZeroBalanceFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_non_zero_value_delegatecall_to_non_non_zero_balance( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_non_zero_value_delegatecall_to_non_non_zero_balance.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - addr = pre.fund_eoa(amount=100) # noqa: F841 - # Source: lll - # { [0](GAS) [[1]] (DELEGATECALL 60000 0 0 0 0) [[100]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.SSTORE( - key=0x1, - value=Op.DELEGATECALL( - gas=0xEA60, - address=addr, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ), - ) - + Op.SSTORE(key=0x64, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=600000, - ) - - post = { - addr: Account(balance=100), - target: Account(storage={1: 1, 100: 24732}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_delegatecall_to_one_storage_key_paris.py b/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_delegatecall_to_one_storage_key_paris.py deleted file mode 100644 index 59f545e4ace..00000000000 --- a/tests/ported_static/stNonZeroCallsTest/test_non_zero_value_delegatecall_to_one_storage_key_paris.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Test_non_zero_value_delegatecall_to_one_storage_key_paris. - -Ported from: -state_tests/stNonZeroCallsTest/NonZeroValue_DELEGATECALL_ToOneStorageKey_ParisFiller.json -""" - -import pytest -from execution_testing import ( - EOA, - Account, - Address, - Alloc, - Bytes, - Environment, - StateTestFiller, - Transaction, -) -from execution_testing.vm import Op - -REFERENCE_SPEC_GIT_PATH = "N/A" -REFERENCE_SPEC_VERSION = "N/A" - - -@pytest.mark.ported_from( - [ - "state_tests/stNonZeroCallsTest/NonZeroValue_DELEGATECALL_ToOneStorageKey_ParisFiller.json" # noqa: E501 - ], -) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable -def test_non_zero_value_delegatecall_to_one_storage_key_paris( - state_test: StateTestFiller, - pre: Alloc, -) -> None: - """Test_non_zero_value_delegatecall_to_one_storage_key_paris.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - addr = Address(0x4757608F18B70777AE788DD4056EEED52F7AA68F) - sender = EOA( - key=0x4F31B3206FBF0E0E598B9B1A7D8AC86302A0FF1D8930738F1BEBAE9B67173E52 - ) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - pre[sender] = Account(balance=0xE8D4A51000) - pre[addr] = Account(balance=10, storage={0: 1}) - # Source: lll - # { [0](GAS) [[1]] (DELEGATECALL 60000 0 0 0 0) [[100]] (SUB @0 (GAS)) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE(offset=0x0, value=Op.GAS) - + Op.SSTORE( - key=0x1, - value=Op.DELEGATECALL( - gas=0xEA60, - address=0x4757608F18B70777AE788DD4056EEED52F7AA68F, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ), - ) - + Op.SSTORE(key=0x64, value=Op.SUB(Op.MLOAD(offset=0x0), Op.GAS)) - + Op.STOP, - nonce=0, - address=Address(0x9C1470E9F035F5D8F34D7C0FF2650F9F89DE43FE), # noqa: E501 - ) - - tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=600000, - ) - - post = { - addr: Account(storage={0: 1}, balance=10), - target: Account(storage={1: 1, 100: 24732}), - } - - state_test(env=env, pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stSpecialTest/test_make_money.py b/tests/ported_static/stSpecialTest/test_make_money.py index 9f0678d1dc0..26fd61f9de2 100644 --- a/tests/ported_static/stSpecialTest/test_make_money.py +++ b/tests/ported_static/stSpecialTest/test_make_money.py @@ -1,17 +1,21 @@ """ -Test_make_money. +Verify value flows tx -> caller -> callee when the CALL asks for an absurdly +oversized gas amount (near 2^256), which the EIP-150 63/64 cap must clamp. Ported from: state_tests/stSpecialTest/makeMoneyFiller.json + +@manually-enhanced: Do not overwrite. Value flow tx->caller->callee expressed +as a relationship; dynamic addresses. The oversized CALL gas operand is the +original filler's point (clamping, not wrapping, of a near-2^256 ask) and +must stay explicit. """ import pytest from execution_testing import ( Account, - Address, Alloc, - Bytes, - Environment, + Fork, StateTestFiller, Transaction, ) @@ -20,70 +24,52 @@ REFERENCE_SPEC_GIT_PATH = "N/A" REFERENCE_SPEC_VERSION = "N/A" +INITIAL_BALANCE = 0xDE0B6B3A7640000 +TX_VALUE = 10 +CALL_VALUE = 0x17 +# The ported filler asks for nearly 2^256 gas: a client computing e.g. +# `requested + stipend` in wrapping arithmetic would forward almost nothing +# and OOG the callee, so the 63/64 clamp itself is under test. +OVERSIZED_GAS_ASK = 2**256 - 20 + @pytest.mark.ported_from( ["state_tests/stSpecialTest/makeMoneyFiller.json"], ) -@pytest.mark.valid_from("Cancun") -@pytest.mark.pre_alloc_mutable +@pytest.mark.valid_from("TangerineWhistle") def test_make_money( state_test: StateTestFiller, pre: Alloc, + fork: Fork, ) -> None: - """Test_make_money.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0x3B9ACA00) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=1000000, - ) - - # Source: raw - # 0x600160015532600255 - addr = pre.deploy_contract( # noqa: F841 + """Value forwards tx -> caller -> callee; the callee records ORIGIN.""" + # Callee stores a sentinel and the transaction origin, proving its code + # ran (not merely that value was transferred). + callee = pre.deploy_contract( code=Op.SSTORE(key=0x1, value=0x1) + Op.SSTORE(key=0x2, value=Op.ORIGIN), - balance=0xDE0B6B3A7640000, - nonce=0, + balance=INITIAL_BALANCE, ) - # Source: lll - # { (MSTORE 0 0x601080600c6000396000f20060003554156009570060203560003555) (CALL 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec 23 0 0 0 0) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.MSTORE( - offset=0x0, - value=0x601080600C6000396000F20060003554156009570060203560003555, - ) - + Op.CALL( - gas=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC, # noqa: E501 - address=addr, - value=0x17, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ) + caller = pre.deploy_contract( + code=Op.CALL(gas=OVERSIZED_GAS_ASK, address=callee, value=CALL_VALUE) + Op.STOP, - balance=0xDE0B6B3A7640000, - nonce=0, + balance=INITIAL_BALANCE, ) + sender = pre.fund_eoa() tx = Transaction( sender=sender, - to=target, - data=Bytes(""), - gas_limit=228500, - value=10, + to=caller, + value=TX_VALUE, + protected=fork.supports_protected_txs(), ) post = { - target: Account(balance=0xDE0B6B3A763FFF3), - sender: Account(balance=0x3B8F6A16), - addr: Account(balance=0xDE0B6B3A7640017), + caller: Account(balance=INITIAL_BALANCE + TX_VALUE - CALL_VALUE), + callee: Account( + balance=INITIAL_BALANCE + CALL_VALUE, + storage={1: 1, 2: sender}, + ), } - state_test(env=env, pre=pre, post=post, tx=tx) + state_test(pre=pre, post=post, tx=tx) diff --git a/tests/ported_static/stStaticCall/test_static_call_value_inherit_from_call.py b/tests/ported_static/stStaticCall/test_static_call_value_inherit_from_call.py index 930f7670065..7686c35c607 100644 --- a/tests/ported_static/stStaticCall/test_static_call_value_inherit_from_call.py +++ b/tests/ported_static/stStaticCall/test_static_call_value_inherit_from_call.py @@ -1,17 +1,21 @@ """ -Test_static_call_value_inherit_from_call. +Verify a STATICCALL callee observes CALLVALUE 0, never inheriting the +enclosing frame's non-zero value (delivered here by the transaction). Ported from: state_tests/stStaticCall/static_call_value_inherit_from_callFiller.json + +@manually-enhanced: Do not overwrite. STATICCALL sees CALLVALUE 0 (never +inherited from the enclosing value-bearing frame — the ported filler's +delivery CALL is collapsed into the transaction's own value); dynamic +addresses, gas forwarded via the default Op.GAS. """ import pytest from execution_testing import ( Account, - Address, Alloc, - Bytes, - Environment, + Fork, StateTestFiller, Transaction, ) @@ -20,49 +24,33 @@ REFERENCE_SPEC_GIT_PATH = "N/A" REFERENCE_SPEC_VERSION = "N/A" +CALL_VALUE = 0xA + @pytest.mark.ported_from( [ "state_tests/stStaticCall/static_call_value_inherit_from_callFiller.json" # noqa: E501 ], ) -@pytest.mark.valid_from("Cancun") -@pytest.mark.slow -@pytest.mark.pre_alloc_mutable +@pytest.mark.valid_from("Byzantium") def test_static_call_value_inherit_from_call( state_test: StateTestFiller, pre: Alloc, + fork: Fork, ) -> None: - """Test_static_call_value_inherit_from_call.""" - coinbase = Address(0x2ADC25665018AA1FE0E6BC666DAC8FC2697FF9BA) - sender = pre.fund_eoa(amount=0xE8D4A51000) - - env = Environment( - fee_recipient=coinbase, - number=1, - timestamp=1000, - prev_randao=0x20000, - base_fee_per_gas=10, - gas_limit=10000000, - ) - - # Source: lll - # { (MSTORE 0 (CALLVALUE)) (RETURN 0 32) } - addr_2 = pre.deploy_contract( # noqa: F841 + """A STATICCALL callee observes CALLVALUE 0, not the caller's value.""" + # Callee returns whatever CALLVALUE it sees; under STATICCALL that is 0. + callee = pre.deploy_contract( code=Op.MSTORE(offset=0x0, value=Op.CALLVALUE) - + Op.RETURN(offset=0x0, size=0x20) - + Op.STOP, - balance=1, - nonce=0, + + Op.RETURN(offset=0x0, size=0x20), ) - # Source: lll - # { [[0]] (STATICCALL 50000 0 0 0 32) [[1]] (MLOAD 0) } # noqa: E501 - addr = pre.deploy_contract( # noqa: F841 + # The tx delivers CALL_VALUE to this contract, so its own CALLVALUE is + # non-zero; the STATICCALL must still hand the callee a CALLVALUE of 0. + caller = pre.deploy_contract( code=Op.SSTORE( key=0x0, value=Op.STATICCALL( - gas=0xC350, - address=addr_2, + address=callee, args_offset=0x0, args_size=0x0, ret_offset=0x0, @@ -72,33 +60,16 @@ def test_static_call_value_inherit_from_call( + Op.SSTORE(key=0x1, value=Op.MLOAD(offset=0x0)) + Op.STOP, storage={1: 1}, - balance=1, - nonce=0, - ) - # Source: lll - # { (CALL 100000 10 0 0 0 0) } # noqa: E501 - target = pre.deploy_contract( # noqa: F841 - code=Op.CALL( - gas=0x186A0, - address=addr, - value=0xA, - args_offset=0x0, - args_size=0x0, - ret_offset=0x0, - ret_size=0x0, - ) - + Op.STOP, - nonce=0, ) tx = Transaction( - sender=sender, - to=target, - data=Bytes(""), - gas_limit=460000, - value=10, + sender=pre.fund_eoa(), + to=caller, + value=CALL_VALUE, + protected=fork.supports_protected_txs(), ) - post = {addr: Account(storage={0: 1, 1: 0})} + # slot 0: STATICCALL succeeded (1). slot 1: the returned CALLVALUE (0). + post = {caller: Account(storage={0: 1, 1: 0})} - state_test(env=env, pre=pre, post=post, tx=tx) + state_test(pre=pre, post=post, tx=tx)