Skip to content

feat!: Satin review follow-ups: Amsterdam gas table, per-spec limits, catchable stops, validation rules, tools and cleanup - #404

Merged
RealiCZ merged 226 commits into
cz/feat/satin-w7from
cz/feat/satin-w8
Oct 20, 2026
Merged

RealiCZ merged 226 commits into
cz/feat/satin-w7from
cz/feat/satin-w8

Conversation

@RealiCZ

@RealiCZ RealiCZ commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Summary

W8 of the Satin line, on top of #400. It carries the follow-ups from reviewing the stack, plus tool hardening and a test cleanup:

  • Gas schedule: the Amsterdam regular gas table.
  • Limits: fixed per spec; the KV and state-gas limits and the block execution-gas budget are removed.
  • Validation: the live system address and its shape, the chain id, oversized bodies, and stale delayed commits.
  • System transactions and contracts: Mega System Transaction packing, in-place system-contract upgrades, and pre-block Oracle reads.
  • Keyless deploys and inspectors: keyless and inspector fixes.
  • Limit crossings: they no longer latch. A crossing stops only the frame that crosses, and its caller may catch it.

The full description will be written together while the stack is reviewed. The stack merges down into #385, which then holds the whole Satin engine at its final state.

…s, keyless errors, the base-fork schedule check, post-Karst receipts
A matching grep -q closed the diff pipe, so the change check exited 141 and skipped the gate. The same happened when git diff itself failed.
The mutation driver defaults its job count to nproc, which stock macOS does not have. The count now comes from sysctl, then from getconf.
The check watched the five contract files and missed the interfaces they import, so an interface-only edit left the checked-in bytecode unexamined.
Refuse MEGA_FUZZ_CASES=0 and a price comparison that shares no path. Require each counted conditional class to be reached, and pin the first-frame history refund against a non-zero operator fee.
The pre-block calls were hitting empty accounts. Seed their code and check, outside the measured section, that both wrote storage.
The seventeen regular-gas entries pressed back to Osaka are gone: every
regular entry is Amsterdam's (EIP-8038 and EIP-8037's creation split), so
creating state costs its access and write in regular gas and the new
state in state gas alone. State gas and history gas do not move.

Tests that summed Osaka prices by hand now read them off the schedule.
Pins of the spec's own numbers move with the table: the fresh-SSTORE
boundary 162,306 -> 152,306, a value CALL's start 713,055 -> 690,755,
the keyless CREATE charge 32,000 -> 12,000, the Satin pools' cold call
and CREATE in deposit_charge, the dump fixture's 165,826 -> 155,826.
Premises that held only at Osaka prices are re-derived: the writes that
cross detention's cap (1,000 -> 1,653 slots, and a reservoir for them),
the overshoot and tracer caps (+400 per cold access), the refund-cap
settlement (8 -> 16 slots), a failed load short of the value transfer.
The recorded-block replays now refuse a transaction only for the block
gas it declared. Regenerated and reviewed: the pricing table,
satin-differences.md, 28 tracer views, the keyless differential record.
No member inherits those workspace dependencies. Nothing reads the hex or attack-deploy inputs, and the disabled replay-bench workflow has no Satin bench mode.
EIP-8038 also gives EXTCODESIZE and EXTCODECOPY a static base of two
warm reads (200), which revm keeps in the instruction table rather than
the schedule. Satin's instruction table now carries Amsterdam's static
prices (satin_gas_table), which part from the Osaka base's in those two
opcodes alone. The neutral configuration still installs the fixture
fork's own table, so an Osaka fixture runs at 100.

The refused EXTCODECOPY read pays its 200 static gas, and one recorded
transaction of satin-differences.md moves by 100.
A bare line suppression matches every mutant with that text. The gate now fails when one of them matches more than one.
Clippy rejects a panic that only checks the count is zero.
…fuzz passes, seeded EIP contracts in block benches, unused workspace dependencies and the Rex-era replay bench removed
…DESIZE / EXTCODECOPY at Amsterdam's static gas, and section 5 restated
The per-transaction, per-frame and per-block KV limits go, with the KV stop kind,
the data-size-before-records check order, block KV packing and their genesis keys.
Write records are still counted for data size and history gas, and the KV count is
still reported. LimitKind keeps its encoding: kind 1 decodes to nothing.
A rejected transaction was the only setup check, so a halt or a revert was still measured. Each transaction is now pinned to an outcome class and to its gas used at the spec's byte prices, and the bench checks the pins before it measures.

UPDATE_CORPUS_EXPECTATIONS=1 rewrites the pins from a run at the constants. Other byte prices skip the gas pin and still check the outcome class.
A measurement build can leave a counted class unreachable. The keyless tally and the differential coverage counts stay printed, and those minimums bind only at the constants. A zero case count is still refused.
…zz coverage minimums bound to the spec's byte prices
The per-transaction and per-block state-gas limits go, with every check of them:
the state-gas holds before the first frame, at the opcode, at frame start and at
the code deposit, the lanes' state-gas tracking, the block's post-execution check
and the StateGrowth stop kind. The state ledger and the block's state gas counter
stay. Kind 3 now decodes to nothing. The crate README and the runner's docs drop
the KV limit too.
The Satin spec, the agent guides, the crate READMEs, the mega-evme docs and the
review skill state that the registry is a precondition the legacy forks left
(no seed, no v1 upgrade, a block without v2.0.0 is invalid) and that the genesis
configuration is satinTime alone.
…e holds

SequencerRegistryRoles::account_info is the v2.0.0 account (code, nonce 1) that
MemoryDatabase::sequencer_registry installs and the blockchain harness puts in
its pre-state and takes out of the root.
Satin deploys and upgrades no predeploy. The block in which Satin activates
(MegaBlockExecutionCtx::satin_activation_block, set by the caller from
MegaHardforks::is_satin_activation_block and the parent's timestamp) reads the
six system contracts and the EIP-7997 factory and refuses the block with one
error, UnexpectedSystemContractCode, when one does not hold Satin's code; no
other block reads them. Removed: the create path, nonce 1, the Oracle and
registry upgrades, ZeroFactoryNonce, UsedEmptyAccount, CREATE2_FACTORY_NONCE,
check_genesis_account. Tests, the blockchain harness, the block bench and
mega-evme start from a Rex6-shaped state (test_utils::rex6_predeploys); mega-evme
run/tx installs a predeploy only where the state lacks its code.
Remove FALLBACK_RUNG and all_activated_hardforks; hardfork_schedule returns None
for an unknown chain. mega-evme refuses to pick an engine for such a chain
unless --genesis or --override.spec is given, and builds a Satin
counterfactual's schedule locally: the chain's own with Satin from genesis, or
the engine's base forks with Satin from genesis.
@codspeed

codspeed Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Merging this PR will regress 1 benchmark

⚠️ 17 benchmarks spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 5 improved benchmarks
❌ 1 regressed benchmark
✅ 50 untouched benchmarks
⏩ 388 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
❌ ⚠️ empty_transaction/satin 50.1 µs 52.8 µs -5.23%
⚡ storage_reads/op_revm 166 µs 153.4 µs +8.23%
⚡ storage_reads/satin 177.6 µs 165.7 µs +7.18%
⚡ value_calls/satin 526.7 µs 499.6 µs +5.44%
⚡ ⚠️ deep_calls/satin 468.6 µs 445.9 µs +5.09%
⚡ remaining_compute_gas/satin 309.7 µs 294.8 µs +5.08%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing cz/feat/satin-w8 (9617c90) with cz/feat/satin-w7 (798701a)2

Open in CodSpeed

Footnotes

  1. 388 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

  2. No successful run was found on cz/feat/satin-w7 (c04aa53) during the generation of this report, so 5fe3a41 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

@RealiCZ
RealiCZ merged commit 22940bd into cz/feat/satin-w7 Oct 20, 2026
25 of 28 checks passed
@RealiCZ
RealiCZ deleted the cz/feat/satin-w8 branch October 20, 2026 07:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant