Repository navigation
feat!: meter Satin's writes and frame starts in the revm fork's hooks, on a per-frame byte pool - #407
Conversation
# Conflicts: # AGENTS.md # crates/mega-evm/tests/_pending/README.md
…write on both paths
…s, and the creation's depth
…cells of the matrix
…s a transaction's
…e and check both inspector columns
A transaction of the system shape (a legacy call to a whitelisted
contract) reads the SequencerRegistry's current system address from the
journal, without warming it, when it is validated. Every other
transaction reads nothing and is never a system-address transaction.
The block executor no longer reads or plumbs the address. Removed:
MegaContext::{system_address, set_system_address, with_system_address},
system::resolve_system_address, system::sent_from_system_address,
PreBlockStateSource::SystemAddress and the three MegaBlockExecutionError
variants only that read produced. A transaction from the system address
of another shape now runs as an ordinary transaction instead of being
refused.
…nds it what no check held
Claude review status
🛠️ Review did not finish Attempted This round did not publish: MODEL_ACTION_FAILED in phase review_retry. Anything listed below is from the last round that did. Re-run the workflow or push a new commit to try again. |
Codecov Report❌ Patch coverage is ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Merging this PR will degrade performance by 11.58%2 warnings
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 720ad2435d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if is_satin_key(&key) { | ||
| let entry = map.next_value::<Entry>()?; | ||
| if entries.contains_key(&key) { | ||
| duplicate.get_or_insert_with(|| key.clone()); | ||
| } | ||
| entries.insert(key, entry); | ||
| entries.insert(key, map.next_value::<Entry>()?); |
There was a problem hiding this comment.
Ignore superseded duplicate values before decoding them
When duplicate satinTime keys are streamed, every occurrence is deserialized as Entry before the last one replaces it. Consequently, a superseded value such as {"$serde_json::private::Number": 1} fails at next_value::<String>(), while parsing the same config through serde_json::Value discards that occurrence and accepts a later valid satinTime. This defeats the intended last-copy semantics and makes readers of the same genesis disagree; buffer or skip earlier duplicates so only the final value is interpreted.
AGENTS.md reference: AGENTS.md:L224-L224
Useful? React with 👍 / 👎.
(cherry picked from commit 821577d) Conflict resolution: - .github/workflows/claude.yml: main's version, unchanged. - .github/workflows/coverage.yml: this branch's version, which already uploads over OIDC with fail_ci_if_error, continue-on-error and a warning step, and triggers on satin; main's persist-credentials: false is added to its checkout step.
🧬 Mutation testing — ❌ FAILDiff mutation score: 93.7% (283/302 viable mutants killed)
Survivors needing attentionEach mutation below changed the code but no test failed. Add a test that kills it, or — if it is provably equivalent/dead — add a justified entry to
Timed-out mutants (inconclusive)Each mutation below never finished within the timeout, so it was not proven caught. Make it terminate (a faster test), kill it, or — if it is a genuine non-terminating/equivalent case — add a justified entry to
Tip: run |
There was a problem hiding this comment.
0 blocking · 0 should-fix · 1 suggestion(s) · 0 open question(s)
Reviewed head 9c1909a6.
Findings without inline anchors:
crates/mega-evm/src/block/genesis.rs:308— [Minor] satinTime readers diverge when the first duplicate value is a malformed arbitrary-precision form Two honest readers of the same genesis file disagree on validity — the streamed path refuses it, the Value path accepts the last satinTime. The trigger is contrived (serde_json never emits a non-string arbitrary-precision form), so natural files are unaffected, but it violates the newly stated spec rule (PR §10) that every reader reads the last copy. The existing testtest_the_last_copy_of_a_key_given_twice_is_readonly exercises number/string first copies and does not catch this. Suggested fix: Buffer or skip earlier satinTime copies before decoding them in EntriesVisitor so only the final occurrence drives the type check. One shape: iterate withIgnoredAnyand record the lastserde_json::Valuefor each key, then decode only that intoEntryafter the loop. Add regression tests with{"$serde_json::private::Number": 1}and other malformed objects (e.g.[1]) as the first duplicate so the streamed and Value paths are pinned to agree.
…hanges The stack merged down into #386, and the skeleton (#385) merged into `satin` first, so the guide now names #386 as the pull request under review and `a8f8c7c9..<head>` as the range that still holds the skeleton. - A table of the engine's modules says which are stable and which #407 still changes, derived from the diff of `cz/feat/satin-w9` against this branch and the work still to land in it; it replaces the old "What follows" section. - The fork bullet keeps this branch's tag pins and names the fork pull requests that review the commit pins #407 moves to. - Module headings, three checks and the decided items #407 revisits say so.
Brings the review guide's update for #386. The guide's four conflicting passages keep this branch's facts (the forks pinned by rev, block execution admitting any inspector); the rest of the update merges as is and is refreshed for this branch's code when it merges into cz/feat/satin-w2.
Brings the review guide's revert-or-halt section.
…cles, count the creator record at the return and add the destruction table - Restore the KV-update and state-growth limits per transaction, per frame (98/100 shares) and per block; a crossing is a revert-class limit stop of the frame that crosses it, ordered data size, KV updates, state growth, the frame budget named first. - Forward the reservoir and regular gas at 98/100 on the forks' pools, and replace the storage-gas stipend with a 160-byte history allowance. - Run frames on revm's own plain and inspected lifecycles through the forks' loop hooks; no inspector-specific code remains in the engine. - Count a creator's nonce record on the creator when the creation returns with the nonce bumped; the creation's budget is the share taken before it. - Give back what a transaction made of an account its first effective destruction removes (the same-transaction destruction table), read from the journal. - Read an account's absence at a transaction's start from that transaction's journal. - Pin the forks at revm 2fc8edf33b2ffddb195cebc17be9adabfd95417c and op-revm f599efdabc3e5b36a280f1aa72978751bd64c160. - Accept a limits file of the first published shape; pack mega-evme's recorded block fixtures into one archive; specification, implementation notes and the review guide updated to the final engine.
Summary
This pull request moves Satin's resource metering into the MegaETH fork of revm and finishes the engine's resource rules. Its base is
cz/feat/satin-w2, the branch of #386, which holds the stacked pull requests #387, #388, #390, #399 and #400 (with #404). This pull request merges into it before #386 merges intosatin.Writes and frame starts are charged inside revm's own instructions, through two hooks. A frame's data-size, KV-update and state-growth budgets are pools on revm's gas tracker, forwarded to a child and settled on its return as the EIP-8037 reservoir is. What a halting frame burned is read off a record the fork keeps. Inspectors run on revm's own inspected lifecycle, and the engine's per-frame work sits only in the frame start, run result and return that revm's plain and inspected loops share. Satin no longer rebuilds revm's state from outside: the instruction wrappers for writes and frame starts, the per-frame record lanes, a journal wrapper with its ledger, the engine's inspected copies of the frame lifecycle and the inspector admission gate for block execution are all deleted.
Several Satin rules change with it (see "Satin rule changes"):
SELFDESTRUCTof an account created in the same transaction gives back what the transaction made of it;The state-test runner gains a whole-outcome digest. Every step of this work was held to the step before it with that digest, and each change it showed is explained under "Verification".
write_completedandframe_startingreplace the write and start wrappers. The fork's pools replace the per-frame lanes. The fork's failed-charge record replaces the code that noted halts. The context's journal is revm's own again.src/evm/inspector.rs. A result an inspector rewrites, or an answer it gives, is taken as revm takes it. Block execution admits any inspector.PRE_BLOCK_CALL_GAS_LIMIT(31,566,720), whatever the block's gas limit.satinTimeis also read from the object formserde_jsonproduces. A witness replay's exports may be a subset of the building node's. The specification states the revert-or-halt rule, a value call's own charges under detention and the EIP-2935 contract's read.revat feat: instruction, frame-loop and start-lifecycle hooks, and a borrowed original-info accessor revm#76 and the op-revm fork at feat: settle the first frame through the revm fork's shared settlement and call its start lifecycle op-revm#8, on their public repositories.Review the diff against the base as a whole: a later commit may revise an earlier one, and the history explains steps, not the final rules.
Terms
MegaLimitExceeded(uint8 kind, uint64 limit), of the frame that crosses a limit: kind 0 data size, 1 KV updates, 2 compute, 3 state growth. Its caller continues as after any revert.GasTracker), for data size, KV updates and state growth. Each is forwarded to a child at a frame start and settled on the child's return, as the EIP-8037 state-gas reservoir is.SELFDESTRUCTof an account created in the same transaction: each part the transaction made, absent at its start and present before the destruction, is refilled and taken back, and each part present at its start is an ordinary deletion.Changes
Gas detention and frame starts
DELEGATECALL's apparent value counted too: aDELEGATECALLfrom an underfunded sender ran a priced precompile past the cap.NUMBERits code runs before it loads the slot; anSLOADof the storage the pre-block calls wrote is no read. The engine already did this; the specification now says so, and a test pins both contracts.The fork's failed-charge record
SYSTEM_CALL_REGULAR_GAS_LIMIT, 30,000,000 regular, the rest reservoir, inMegaHandler::tx_gas), and reading the reservoir a result settled with (AdditionalLimit::reservoir_remaining). The op-revm baselines run op-revm through a reference handler that does the same.The fork's instruction hooks
SSTORE,LOG0–LOG4andSELFDESTRUCTcallwrite_completedonce the write is in the journal and every charge they make has been made. The engine counts the record, holds it to the limits, and then charges the record's history. A write that the opcode's own failure takes back is never counted.frame_startingafter the forward is taken. The engine charges the caller, out of what it kept, the history of the records the start makes, and declares the frame's own part on the start (FrameStart::charge), which the fork refunds when the frame fails. The hook counts nothing.GasTracker.The fork's loop hooks
step, and the inspected frame start hands a frame built with one to noinitialize_interp. ThekeylessDeploycall's creation and answer, and the stop of a caller its child left over a limit, are set that way, soMegaEvm::frame_runonly forwards to revm's.EvmTr::frame_run_result) once a frame's run returns, before the inspector'scall_end,create_endandframe_endand beforeframe_return_result. Gas detention turns a crossing into the stop there, so an inspector and a tracer see the frame end withMegaLimitExceeded(2, limit), as the receipt bills it. A frame that ends before it runs is classified where it ends.keylessDeploycall start, its creation and the call end, with noinitialize_interporstepfor the call's own frame. The keyless differential digest and the tracer snapshots follow.Inspectors on revm's own lifecycle
inspect_frame_init,inspect_frame_run), its step guard (StepGuard) andsrc/evm/inspector.rsare removed.MegaEvm'sInspectorEvmTris revm's inspected lifecycle unchanged, and it starts, runs and returns frames through the sameframe_init,frame_run_resultandframe_return_resultthe plain loop uses.settle_frame_resultandwith_pools_ofare removed.AGENTS.mdholds the engine's contract with them.Outcome-digest tooling
state-test --json-digestprints one line per executed transaction, carrying the hash of the canonical record of its whole outcome;--json-digest-fulladds the record itself.mega-state-test::digestexposes the canonical record and the hash. The record carries the state growth with the other usage counts.--limit-data-size,--limit-frame-data-size,--limit-kv-updates,--limit-state-growthand--limit-compute-gasrun Satin mode under limits tighter than the spec's, so the stop paths run across the fixtures.--json-outcome) did not.The pools, after a journal-centred ledger
MegaJournal) and kept beside it a ledger of what the transaction keeps. The ledger first ran as a shadow, checked against the per-frame record lanes; then it decided, and the lanes were deleted.Journal<DB>again.pool_kept_percent= 2 for each of the three pools,reservoir_kept_percent= 2,call_stipend_reduction= 50 andcall_history_allowance= 14,080 gas. The pricing table lists them.KV updates and state growth
TX_KV_UPDATE_LIMIT= 500,000 write records andTX_STATE_GROWTH_LIMIT= 1,000 new accounts and slots, and each frame to 98/100 of what its parent has left in each, on two more pools of the gas tracker (limit::KV_UPDATE_POOL,limit::STATE_GROWTH_POOL). A crossing is a limit stop of kind 1 or 3 (LimitKind::KvUpdate,LimitKind::StateGrowth, Rex6's discriminants).cap_the_shares). A transaction reportsmax(net, 0)(LimitUsage::state_growth).limit::COUNTED). Inside each, the frame budget is tested before the transaction limit, and the stop names the budget whenever the budget is crossed.hold_authorization_list). A database error in the simulation leaves the transaction without an outcome: an internal error when the database calls it fatal, a block verdict otherwise.BLOCK_KV_UPDATE_LIMIT= 500,000 andBLOCK_STATE_GROWTH_LIMIT= 1,000, checked after the data size and in that order: the transaction that reaches one is included, and every later ordinary transaction is refused before it runs. Deposits and Mega System Transactions are never refused by them and count towards them; the pre-block calls count towards none.BlockLimitsmay hold either below the protocol's value, never above.ProtocolLimitsandEvmTxRuntimeLimitsread a limits file of the first published shape, such as one given tomega-evme --override.limits, with the default spec's values for the four newer fields. Every field that shape had stays required, and an unknown field is refused.The 98/100 forwards
remaining − remaining × 2 / 100of its caller's regular gas, in place of EIP-150's 63/64 (GasId::call_stipend_reduction= 50). A value call's 2,300 stipend is added after the cap.GasId::reservoir_kept_percent= 2), whatever regular gas the call asks for. The transaction's own frame and a system call's outermost frame take the whole reservoir, and a keyless deployment's creation takes the share of what the call has left.The destruction table
SELFDESTRUCTof an account created in the same transaction applies the specification's destruction table to every part of that account.usagealone.NonefromDatabase::basic), never as an empty account: the table gives a leaf back only when no account stood at the address at the transaction's start.Block execution and inspectors
TrustedObserverdeclaration, itsDeclaredObservercarrier,MegaEvm::with_trusted_inspector,MegaEvm::has_rewriting_inspector,MegaBlockExecutorFactory::create_executor_with_trusted_inspectorandMegaBlockExecutionError::RewritingInspector.test_utils::ReadOnlyObserverremains for tests: in debug builds it checks that an inspector writes nothing back.Pre-block calls
applyPendingChanges()) runs onPRE_BLOCK_CALL_GAS_LIMIT= 31,566,720, whatever the block's gas limit: 30,000,000 of regular gas and a reservoir of sixteen fresh slots at the minimum bucket. One helper runs the three, and holds a fatal database error to an internal error and any other failure to a block verdict.pre_block_call_gas_limitandMegaEvm::transact_system_call_with_gas_limitare removed.Write records
SELFDESTRUCTrecords the account it clears and its beneficiary.Spec and engine aligned
Two engine changes and several specification changes.
CREATEorCREATE2loads its creator and its created address for its own checks. Those loads are not volatile reads, and are not refused in a frame whose volatile-data access is off.configobject is read as its last copy by every reader.satinTimeis also read from the object{"$serde_json::private::Number": "<digits>"}, decimal digits alone, with no sign. The parser already accepted the object, and a test pins it; this states it in the spec.MegaLimitExceeded, and each sentence that returns unspent gas names the failed deposit as its exception.The body's history in
tx_gasNames and public text
STORAGE_CALL_STIPEND_BYTESandstorage_call_stipend()are renamedHISTORY_ALLOWANCE_BYTESandhistory_allowance_gas(), as the specification names the allowance.tests/satin/limit_matrix/, one test per case of the rules..claude/skills/satin-review/SKILL.md, describes the engine at this head. It is removed before feat!: the Satin execution engine on the revm 40 forks #386 merges.mega-evmedata_size,kv_updates,compute_gas,state_growth).tests/fixtures/blocks.tar.xz, extracted once per test binary; each file is moved into place on its own, so test processes that extract it at the same time all succeed.scripts/pack_blocks_fixture.pyrepacks it, and its--checkrequires a repack of the committed archive to be byte-identical. The published crate leaves out the scripts and the test fixtures.The fork pins
https://github.com/megaeth-labs/revmand op-revm athttps://github.com/megaeth-labs/op-revm, both byrev.Cargo.lockchanges in theirsourcelines, and eight packages resolvewindows-sys0.59.0 in place of 0.52.0 or 0.61.2.docs/spec/overview.mdnames the two commits and their pull requests in place of release tags.Cargo.lockwhether it is pinned by tag or byrev.Satin rule changes
Each of these changes what a Satin node computes, accepts or must check. Items 1 to 17 change receipts or state; items 18 to 22 change how a chain configuration is read, what a witness replay must hold, what a node's state must answer, and what a caller's configuration can change.
CALLCODE, and a valueCALLto the calling account itself, record nothing. Why: the record should measure state the transaction keeps, by revm's own notion of a change; the per-frame bookkeeping over-counted, and charged history gas for writes that leave nothing.SELFDESTRUCTrecords the account it clears, as well as its beneficiary. Why: clearing an account is a write the transaction keeps.SELFDESTRUCTpaying it). It is charged its creator's nonce record only when the bump changes the creator's account from what the transaction loaded. A keyless deployment charges by the same rule. Why: the net-change rule applied to creations; the old upper-bound charge made a creator that held enough for one record halt out of gas, and two Ethereum fixtures take a different path because of it.CREATEorCREATE2is neither marked as a volatile read nor refused in a frame whose volatile-data access is off, even when its creator or its created address is the block beneficiary. Before, such a creation in a frame with access off halted out of gas. Why: these loads are the creation opcode's own checks under EIP-8037, just as a value call's check of its caller's balance is not a read; Rex6 neither marked nor refused a creation.remaining − remaining × 2 / 100of its caller's regular gas, and every child receives its caller's reservoir less 2 per cent, whatever gas the call asks for. The transaction's own frame and a system call's outermost frame take the whole reservoir. This departs from EIP-150's 63/64 and from EIP-8037, which passes the reservoir whole. Why: the regular forward is Rex6's, and state and history gas draw the reservoir first, so a callee forwarded the whole reservoir could spend all of it and leave its caller, which can catch its stop, nothing to carry on with.SELFDESTRUCTof an account created in the same transaction refills the state gas of each part the transaction made, to the destroying frame, lowers state growth for the leaf and each new slot, and takes back their write records and the code's data size and history gas; from the mark the account adds nothing and its later state charges are priced at nothing. Why: a part the transaction made does not remain for a node to store, so the count and the charge stay aligned; EIP-8037 refills nothing for this destruction and Rex6 refunded no storage gas, so this departs from both on purpose.max(block gas limit, 30,000,000). Why: a pre-block call does not draw on the block's gas, the old budget was widened for storage gas a crowded bucket multiplied, which a system call no longer pays, a pre-block call on MegaETH's chains writes at most six fresh slots, and none of the three contracts readsGAS.configobject is read as its last copy by every reader. Before, a reader that saw both copies refused the file. Why: a node and the reference CLI parse the file into a JSON value, which keeps only the last copy, so refusing in one reader made readers disagree on the same file.satinTimeis also accepted as{"$serde_json::private::Number": "<digits>"}, decimal digits alone, with the same range. The parser already accepted the object; the spec now says so, and a sign is refused. Why: that is the formserde_jsonhands a number over in when itsarbitrary_precisionfeature is on, and a reader cannot tell it from the same object written in the file.Breaking API changes
TrustedObserver,DeclaredObserver,MegaEvm::with_trusted_inspector(usewith_inspector),MegaEvm::has_rewriting_inspector,MegaBlockExecutorFactory::create_executor_with_trusted_inspector(usecreate_executorwith an EVM fromcreate_evm_with_inspector, orMegaBlockExecutor::new), andMegaBlockExecutionError::RewritingInspector.settle_frame_resultandwith_pools_of.pre_block_call_gas_limitandMegaEvm::transact_system_call_with_gas_limit.constants::PRE_BLOCK_CALL_GAS_LIMITis new.STORAGE_CALL_STIPEND_BYTEStoHISTORY_ALLOWANCE_BYTES, andstorage_call_stipendtohistory_allowance_gas.StagedRecordandAdditionalLimit::staged_recordare removed.AdditionalLimit::usageis nowMegaContext::limit_usage.LimitKindgainsKvUpdateandStateGrowth, andLimitKind::from_u8decodes 1 and 3.LimitUsagegainsstate_growth.EvmTxRuntimeLimitsgainstx_kv_update_limitandtx_state_growth_limit,ProtocolLimitsandBlockLimitsgainblock_kv_update_limitandblock_state_growth_limit, each with itswith_*builder.MegaBlockLimitExceededErrorgainsKvUpdateLimitandStateGrowthLimit.constantsgainsTX_KV_UPDATE_LIMIT,TX_STATE_GROWTH_LIMIT,BLOCK_KV_UPDATE_LIMITandBLOCK_STATE_GROWTH_LIMIT.MegaGasUsage::newtakes the reservoir remaining as an argument, since revm'sResultGasno longer carries it.AdditionalLimit::reservoir_remainingis new.SYSTEM_CALL_REGULAR_GAS_LIMIT(30,000,000) is exported, now that Satin splits a system call's gas itself.HISTORY_GAS_PRICEDgainsGasId::call_history_allowance.SatinChainConfigError::DuplicateKeyis removed.test_utils::ReadOnlyObserveris new (test utilities only).mega-state-test: thedigestmodule (DigestForm,canonical,hash,text,panicked,Executed) and a re-export ofEvmTxRuntimeLimits.state-test: the--json-digest,--json-digest-full,--limit-data-size,--limit-frame-data-size,--limit-kv-updates,--limit-state-growthand--limit-compute-gasoptions.mega-evme:SatinReportgainsstate_growth, andLimitStop::kindmay bekv_updatesorstate_growth.Hostinstruction hooks, its pools onGasTracker(PoolId), the reservoir share, theFrameStart/StartChargecarriers, the action a frame's run returns before it runs and the run-result hook: a downstream crate must build against the two fork commits below.Dependencies
2fc8edf33b2ffddb195cebc17be9adabfd95417c, that pull request's head.f599efdabc3e5b36a280f1aa72978751bd64c160, that pull request's head, which itself pins revm at the commit above.rev, with no tag until the line is final. Merge order: revm#75, revm#76, then op-revm#8. This PR then moves its pins to the merged commits.Verification
Run on macOS (aarch64).
test_dynamic_create2_selfdestruct_collision. ACREATE2onto an address the transaction had funded no longer halts out of gas at its start.GASreading stored another, or a call that fit its forward under one rule and not the other; nothing else.limitword and the reported stop. Two oracle builds attribute each to the order alone.CREATE2from the EIP-7997 factory that the creator's record used to stop at its start: it now runs and returns, its creator stops at the return, and the transaction keeps the same usage and state but for fee balances, its gas differing by what the init code ran.usagealone: the neutral configuration counts the table and prices none of it, so no result, ledger, log or state moves, and no fixture checks the counts. It changed 46 Osaka and 151 Amsterdam transactions in Satin mode, and 11 and 35 under tight limits (600 bytes, compute cap 100). A build with the table's three paths switched off reproduces the base on all six runs, so every difference is the table's.cargo test --workspace --release --locked: 1,843 passed, 0 failed. The three tests the last commits add, and the two they extend, pass at all 73 points ofscripts/price_grid.sh --full. fmt, clippy (--workspace --lib --examples --tests --benches --all-features --locked, warnings denied) and Prettier on*.md,docs/**/*.mdand the guide are clean.cargo insta test --workspace --check --unreferenced=reject --test-runner cargo-test --no-quiet --fail-fast: 1,849 passed, 0 failed, no pending or unreferenced snapshot.cargo test --workspace --release --locked: 1,840 passed, 0 failed.mega-evme: 208 passed, 0 failed.scripts/price_grid.sh --full: the suite passes at all 73 points, 1,555 tests each.--test: all pass.state-testin equivalence mode on the Osaka state tests: 19,465 executed, 19,463 passed, the 2 failures the two listed deviations, gate passed, no panic. In Satin mode: 19,465 executed, no panic.no_stdcheck are clean.cargo-mutantsrun was made for this head.SELFDESTRUCT's moved balance ignored, the body's history rebuild dropped, a creation's loads marked or refused as reads, the genesis parser's duplicate refusal put back, the forward quotient, the reservoir share, the three pool shares, the negative-net clamp, the frame-budget-first order, the authorization-list skip, the deposit's created caller, aSELFDESTRUCTbeneficiary's rise, a value transfer's rise, the keyless signer's rise, the block KV budget, the exempt pools, the resumed caller's hold removed, the absent leaf's empty-original condition dropped and the applied authority's touch check dropped. Most of the creator-record and destruction-table tests were written red first, failing on the engine before the change; the rest pin behaviour the change keeps or creates.Cargo.lockresolves revm 40.0.3 and op-revm 20.0.0 once each, from the two fork commits.op_revmrows run op-revm's own EVM on the same workloads, and their code did not change. Their +3.5% to +6% on call-heavy benchmarks is the fork's new call path: larger frame results, the forward share and the start charge.op_revmrows of the frame-heavy benchmarks (deep_calls,intercepted_calls,system_address_misses,remaining_compute_gas,value_calls) by +8.5% to +13%, the fork's cost of forwarding and settling three pools and the reservoir share at every start, and the same Satin rows by +17% to +20%;storage_writes,logs,data_size_limitandsalt_new_accountsmoved +12% to +18% on the Satin side.Follow-ups
These stay out of this PR.
Labels
spec:unstable,api:breaking,comp:core,comp:mega-evme,comp:misc,comp:doc,dependencies