Skip to content

refactor(satin): keep no copies of revm's figures, account for frame starts in the engine, and start clean after an error - #409

Open
RealiCZ wants to merge 83 commits into
cz/feat/satin-w2from
cz/feat/satin-w10
Open

RealiCZ wants to merge 83 commits into
cz/feat/satin-w2from
cz/feat/satin-w10

Conversation

@RealiCZ

@RealiCZ RealiCZ commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This pull request simplifies the Satin engine by deleting the engine's own copies of figures that revm, the journal or the transaction already hold: each one is now computed where it is read, read off the journal, or decided once when the layer prepares an execution. It changes no consensus outcome: the whole-outcome digest of every execution-spec fixture is equal to its base in equivalence mode, Satin mode and Satin under tight limits, and no insta snapshot changes. One tool-facing behaviour changes on purpose (see "Behaviour").

It also moves the accounting of a frame's start into the engine: the three resource budgets a child starts with, the part of the reservoir its caller keeps, the start charge a failed start gives back and the history allowance of a value call are now decided by the engine on a ledger of its own, driven by the revm fork's start-lifecycle hooks, and the forks carry none of them. And it makes an EVM used again after a call that returned an error start as a fresh one would. Neither changes a transaction's outcome; the inspector shapes whose behaviour changes are listed under "Behaviour".

It is the first pull request of the work after #407: simplification first, then the performance work, on this branch. The performance part takes back about a third of the per-transaction instruction count #407 added on realistic workloads, again with every outcome unchanged. It merges into cz/feat/satin-w2 (#386).

Changes

  • The reservoir report. The reservoir a transaction or a system call leaves is reported where its result settles, instead of through a handler latch. A system call run through the public handler entry points without the crate's preparation reports its own reservoir.
  • The body's history and the first frame's record price. Both are computed where they are read, from the body and the first frame's record, and only when history is priced; the stored copies and their accessors are gone. MegaContext::intrinsic_history_gas() gives the body's history once validation admitted it, and 0 otherwise.
  • One class per execution. The layer decides once, when it prepares a transaction or a system call, whether it is the protocol's own work, a deposit or an ordinary transaction. The exemption from the limits, whether history is priced and gas detention's caps are read off that class; the copies in the context and in gas detention are gone. Replacing the transaction without preparing it changes none of them.
  • The EIP-7708 switch is read off the journal's own configuration.
  • The access switch-off that an answer of the access-control contract asks for is carried on the interceptor's answer and applied when the answer settles as a success, instead of being staged.
  • The keyless signer's give-backs are priced again where they are given back, instead of being stored.
  • The destruction table's pricing switch reduces to the neutral configuration's check: Satin's configuration always enables EIP-8037.

Frame-start accounting in the engine

  • A start ledger. limit/ledger.rs keeps one entry per open frame start: the start's three budgets with their nets, its start charge and its history allowance. revm's start lifecycle drives it alone (Host::frame_split, frame_settle, frame_lifecycle_end), so the engine holds no pairing logic of its own: the ledger begins at the transaction's start, takes each child's start as revm opens it, settles it as revm settles the frame, and is dropped at the lifecycle's end, settled or discarded.
  • What it decides. A child starts with 98% of each of its caller's budgets, held to the frame cap and, when the caller's net is negative, to its share of the caller's budget; the caller keeps 2% of its reservoir, and the child's share is named to revm through FrameStart::reservoir. The limit checks and every draw read the running start's entry. A start that did not succeed gives back its start charge (a creation also when it took no address), except for an inspector's answer or an outcome an inspector rebuilt; a value CALL/CALLCODE gives its frame the history allowance, which a log draws first. A precompile's draws on a start's budgets would be adopted fail-closed against the start's share; no provider draws them today.
  • What the forks carry. None of the four figures: no budget, kept share, start charge or allowance on the gas tracker, the frame inputs or the outcomes, and no schedule entry for them. revm forwards and settles the reservoir as upstream does: the frame's input copies the caller's reservoir, or carries the one the engine names through FrameStart::reservoir, and the settlement replaces the caller's reservoir with what the frame returns. The engine gives the caller back the part it kept (see "Running on forks that carry none of the four figures").
  • A halted transaction. When the transaction's own frame halts and its start charge is given back, the engine burns every regular gas the transaction's gas holds after the refund; revm then consumes only what the hook credited.
  • How it was proved. The ledger was first built beside the fork's figures and compared with them at every settlement in debug builds, over the whole test suite, the fuzz suite and every execution-spec fixture, before the engine switched to it. After the switch, a seeded two-revision run of 40,000 scenarios (nested calls of every scheme, the system contracts, keyless creation, detention, precompiles, budgets at their limits, the pre-block calls, the inner system-call entry points and a nested EVM, plain and under thirteen inspector shapes) was equal to the previous head on every run outside the inspector shapes listed under "Behaviour". The same run, 8 seeds of 5,000 scenarios (360,000 lines per revision), was repeated between ed2d8d95 and the head on the forks that carry none of the figures: every line is equal, and no inspector shape moved again.

Running on forks that carry none of the four figures

  • The engine runs on them. The revm and op-revm forks no longer carry the three budgets, the reservoir share the caller keeps, the start charge or the history allowance; the engine had decided all four since the start ledger, so the fields the forks held were already ignored. The struct literals, test helpers and synthetic outcomes that named them are gone, and the pricing table loses the five rows the zero schedule entries rendered.
  • A child's caller gets back the reservoir it kept. revm's settlement replaces the caller's reservoir with what the child returns, as upstream's does. Host::frame_settle adds FrameSettle::held_reservoir, the reservoir the caller held while the child ran, back on top, at depth 1 and below; the caller then holds what it kept and what the child handed back, on every path that settles a child: an instruction's start, a precompile's answer, a start revm refuses, an inspector's answer and a keylessDeploy creation. Nothing is added back at the transaction's start: revm hands the transaction's frame a copy of the transaction's reservoir, which its settlement replaces, so adding it would count it twice. The result equals the additive settlement the forks had on every amount.
  • The engine owns the pool types. PoolId and PoolAmounts left the fork with the pools; they are in limit/pool.rs with the API and Debug shape they had, and exported from the crate root beside LimitKind.
  • Fewer pieces. A precompile's answer is no longer read back for pool draws, since no provider draws and the read found nothing; the ledger's fail-closed adoption rule and its unit test stay, for a provider that does. The admission hook's override takes the deposit alone again, and the schedule test that only restated two constants other tests pin is gone.
  • Mutation survivors resolved. The gate at ed2d8d95 left five survivors in limit/ledger.rs. Two are killed by new unit tests (a caller's negative net holds its child's share to its budget, and an answer's rebuild puts its start back at its shares). Three are written away: two comparisons no mutant can tell apart, and a third, the signed conversion, written without a comparison.
  • A new test file. tests/satin/reservoir_settlement.rs pins, for a child that writes and stops, reverts, halts, spills past its share and unwinds, what its caller holds after the settlement, and the transaction's own frame, which halts above and below the cap with nothing added back. It is red without the add-back and red with the add-back at the transaction's start too.
  • The keyless differential's recorded snapshot changes only in the removed tracker fields.

Reuse after an error

  • The handler's fatal cleanup (MegaHandler::catch_error) now clears a database error a load stored in the context and the handler never took (post-execution failed first, on a read of its own), takes back the failed call's journal transaction (its nonce bump and fee, and the writes of frames that completed before the error), and clears the per-transaction caches and the frame stack. A failed deposit is a result, not an error: its nonce bump and mint stay.
  • Every entry point that finalizes the journal on a success finalizes it on an error too: MegaEvm::replay, system_call_with_caller and the pre-block calls now do, as transact and the inspected entry points already did. The _one entry points leave the journal to the caller on both outcomes, without the failed call's writes.
  • Before, the next call on the same EVM could fail with the previous call's stored error, or run on its writes and nonce. A table test runs every public entry point, plain and inspected, failing before the first frame, with frames open and after the frame settled, and holds the next call to a fresh EVM's outcome. A panic is not an error return: an EVM a panic unwound out of is not reused.

Performance

  • The first frame's write record is priced once. The pre-execution charge keeps the price it charged and the first frame takes it, instead of pricing the same record again; a debug build prices it again and asserts the two agree.
  • A call that moves no value skips its start's account reads. A STATICCALL, a DELEGATECALL or a zero-value CALL/CALLCODE whose target is not a precompile changes no account at its start, so the before-and-after reads find nothing; they are skipped, and the opcode's start pricing takes the same short cut by a rule of its own. A precompile call, a node's stateful precompile included, and a CALLCODE with value are still read. A debug build makes the skipped reads and asserts they find nothing.
  • One state lookup per account when a start's or a transfer's records are read, instead of two to six.
  • A completed write visits only the pools its count touches, pinned by a test that holds it to the previous walk over every pool; the EIP-7708 rule is read only where a SELFDESTRUCT needs it.
  • An account is compared with its original info by reference, through the revm fork's new Account::original_info_ref, instead of a clone of the original info for every comparison. Measured on its own before the changes above, it saved about 330 instructions per transfer.

Per transaction, on arm64 callgrind with CodSpeed's simulation flags (the CodSpeed report on this pull request is the reference):

workload instructions saved
a transfer in a 64-transaction block −1,013 (−2.27%)
a contract call writing one slot, in a block −1,052 (−3.43%)
an ERC-20 transfer in a block −1,183 (−1.64%)
an ether transfer −968 (−3.58%)

The op-revm control arms are unchanged at every commit. The table predates the last item, whose saving on top of the others the CodSpeed report shows.

The start ledger's cost, measured the same way on the forks that carry none of the four figures, against ed2d8d95 (the head with the ledger on forks that still carried them) and against 98a1848d (the head before the ledger):

workload (Satin arm) against ed2d8d95 against 98a1848d
a value CALL −636 (−10.6%) −311 (−5.5%)
a level of deep_calls −786 −304
a transfer in a 64-transaction block −491 −333
a contract call writing one slot, in a block −554 −362
an ERC-20 transfer in a block −508 −293
an SSTORE −2 +55
a LOG2 −3 +20

The op-revm arms of the call benchmarks drop by 529 to 531 per CALL against ed2d8d95. Most of it is the size of the frame results: at 208 bytes they are copied inline again, where at 296 every move through the handler's wrappers was a library memcpy call, which the memcpy figure of a value-call benchmark shows (−527 per CALL on the Satin arm). The ledger's own cost per write is what remains against 98a1848d (+55 per SSTORE, +20 per LOG2). It is measured, not tuned: the optimisation candidates are listed and are taken together with the rest of the performance work at the end of the line.

Tools and benches

  • state-test --json-witness (and --json-witness-full) prints, per transaction and per block, the set of everything the engine read below the journal: accounts, code, storage, block hashes, SALT buckets and oracle slots. It runs in the same pass as --json-digest, whose output is unchanged. A change that keeps every outcome but reads different state now shows up.
  • block/erc20_transfers: a block of 64 ERC-20 transfers, the token's source and build command beside the bench, with an unmeasured preflight that checks each transfer's event, return value and balances.

Toolchain

Forks

  • The revm and op-revm fork pins move to the heads of feat: instruction, frame-loop and start-lifecycle hooks, and a borrowed original-info accessor revm#76 (4c8f0d43) and feat: settle the first frame through the revm fork's shared settlement and call its start lifecycle op-revm#8 (8c0db939). Both forks are checked with Rust 1.97.0.
  • The forks carry none of a frame start's four figures (see "Running on forks that carry none of the four figures"), and forward and settle the reservoir as upstream does.
  • revm#76 adds the original-info accessor this PR uses, and a start lifecycle: revm records every frame start and its settlement and reports them through hooks that default to doing nothing; the engine's start ledger is driven by them. op-revm#8 calls the lifecycle from its handler's boundaries.
  • The engine names the outcomes' start mark in its literals, and a test that drives a transaction's frame by hand opens the transaction's start first.
  • Under these pins the outcome digest and the read-set comparison are equal to the previous head of this pull request (see Verification).

Spec

  • satin.md names Satin's EIP-8037 profile with its freeze point: the base commit, the later commits taken and declined, and Satin's own departures. A later change to EIP-8037 is an input to a later spec, not to Satin.

Behaviour

  • Accepted change (tools only). When a caller or an inspector turns the journal's EIP-7708 switch off through the public journal, the data-size count follows the logs the journal actually emits; before, a transfer log the journal never emitted was counted. Pinned by a test.
  • Accepted changes (inspectors only). The engine's start ledger decides what an inspector's shape used to decide through the fork's figures on the gas tracker:
    • an inspector that answers a start (for example with a result built from untouched_call_gas or untouched_create_gas) is settled as an answer: unlimited budgets for the caller and no start-charge refund;
    • a gas tracker restored from a snapshot mid-frame (a gas-metering pause) no longer restores the frame's budgets or allowance, which live on the ledger;
    • an inspector that replaces a frame's interpreter keeps the frame's budgets and its history allowance;
    • a frame start an inspector builds itself, for which no instruction reserved anything, starts with zero budgets (in a limited execution a write in it is refused).
      None of these is on a block-building, validation or replay path.
  • Reuse after an error. A call made on an EVM after a call that returned an error now runs as on a fresh EVM (see "Reuse after an error"); only such a call's result changes.
  • Unchanged elsewhere. Every reported figure (gas ledgers, usage, reservoir, history bytes) and every outcome are the base's on the plain, inspected, intercepted, answered, failed and halted paths, for transactions, deposits, system calls and pre-block calls, on a reused EVM and on an unfinalized journal.
  • One error-path figure. On the two error paths that run before any preparation (a failed read of the live system address, an error in an unprepared direct system call), the reported reservoir is 0 where the base kept the previous execution's figure; no outcome carries it.

Public API

  • AdditionalLimit::intrinsic_history_gas() is replaced by MegaContext::intrinsic_history_gas(), with the same figure.
  • MegaContext::prices_history() is no longer const.
  • PoolId and PoolAmounts are exported from the crate root.
  • HISTORY_GAS_PRICED no longer lists the call's history allowance entry: the engine grants the allowance itself.
  • untouched_call_gas / untouched_create_gas no longer put budgets on the gas they build.
  • With test-utils: record_start_ledger_settlements, take_start_ledger_settlements, LedgerSettlement and LedgerPools let a test read what the start ledger settled.

Not in this pull request

  • Restoring a failed start's kept counts from the child's own pool spending instead of a snapshot, and counting kept transfer logs off the journal's logs at settlement: both would change usage under an inspector that rewrites a failed call's gas (as a gas-metering pause does), so they stay as they are.

Verification

  • The frame-budget boundary of a completed write's count (a count equal to the frame's remaining pool is within it) is pinned by a test, and a redundant guard the comparison already implied is gone; the outcome digest of that change is equal to its base.

  • The accessor commit, built against the two fork heads: outcome digest and read-set comparison equal to its parent (six runs, and 20,271 blocks of blockchain tests).

  • Performance commits: outcome digest against edf3fa1e (equivalence, Satin and tight runs on Osaka and Amsterdam), all equal; the read-set comparison against the commit that added it, all six runs and the blockchain tests (20,271 blocks) equal; cargo test --workspace --release --locked with warnings denied: 1,867 passed, 0 failed.

  • Outcome digest against 2ec47583 (the base): equivalence, Satin and tight runs on Osaka (19,465 transactions) and Amsterdam (15,754), all equal, no panic.

  • cargo test --workspace --release --locked with warnings denied: 1,853 passed, 0 failed. The CI insta run: no snapshot changed. Fuzz seeds 0 to 8, mega-evme, scripts/price_grid.sh --pr, the benches in test mode, and the debug state-test in equivalence and Satin modes with no panic.

  • fmt, clippy (CI flags), cargo sort, the no_std check and Prettier pass.

  • New tests pin the reservoir of an unprepared direct system call, the prepared history policy and the validated intrinsic history, a context switched back from the neutral configuration, the journal's EIP-7708 switch, and the keyless signer's re-priced give-back in a crowded SALT bucket; each fails without what it pins.

  • Start ledger, reuse after an error (head ed2d8d95): outcome digest and read-set comparison against 98a1848d (the previous head of this pull request), all six runs and the blockchain tests (20,271 blocks) equal; the same runs with debug assertions compiled in, equal and without a panic; the engine's counterfactual replay of three recorded mainnet blocks byte-identical; cargo test --workspace --release --locked with warnings denied: 1,898 passed; the long fuzz run, clippy, fmt, no_std in debug and release.

  • The mutation gate runs in CI; the survivors it reported at ed2d8d95 are resolved (see "Running on forks that carry none of the four figures").

  • The forks without the four figures. Every gate below ran on a tree identical to dd007b9a except that its fork pins pointed at local clones of the same fork commits:

    • cargo test --workspace: 1,912 passed, 0 failed, 4 ignored; the long fuzz run (MEGA_FUZZ_LONG=1): 23 passed; cargo test --workspace --release --locked with warnings denied: 1,901 passed, 0 failed, 4 ignored;
    • clippy with the CI flags, fmt, cargo sort, no_std on riscv64 in debug and release with warnings denied, Prettier on the documents, and the benches in test mode (57 Success);
    • outcome digest from ed2d8d95 and from 4f431170 to that tree: all six runs equal (19,465 and 15,754 transactions, equivalence and Satin modes, and tight limits), no panic; the read-set comparison: all seven runs equal, 20,271 blocks of blockchain tests;
    • the digest with debug assertions compiled in against the plain release build at ed2d8d95: lines and summaries equal in all seven runs, no panic, no start-ledger message;
    • the counterfactual replay of three recorded mainnet blocks (26,400,001, 26,400,007 and 26,400,110) byte-identical;
    • the two-revision differential between ed2d8d95 and that tree: 360,000 lines per side, all equal.
  • The legacy execution-spec suites (Cancun, Constantinople) were not run for this head; CI's run-tests.sh runs them.

…he handler unprepared

The reported reservoir is forgotten where every execution builds its gas, and the result
reports its own unless post-execution already reported it, so a system call run through
revm's public handler entry points without the crate's preparation no longer reports the
reservoir of the execution before it.
The layer keeps one value, set when it begins a transaction or a system call, that says
whether it is the protocol's own work, a deposit or an ordinary transaction; the exemption
and prices_history derive from it, so a transaction set on the context and not run yet
changes neither. prices_history is const again.
…tted it

The layer records the body's bytes when validation finds the gas limit covers their
history, and the intrinsic history gas and the settled history bytes read that record, so
a transaction validation refuses reports none, as before the history figures were
recomputed.
… counts no transfer log

The count of a value movement's transfer log follows the journal's own EIP-7708 switch, so
a caller or an inspector that turns it off through the context's mutable journal gets no
log and no 160 bytes; a SELFDESTRUCT moving value is held to a data-size limit of its body
and records alone. Documented in AGENTS.md and the record module.
… run the unprepared system call above the cap
… unpriced for history until it is prepared

Switching to the neutral configuration records, in the class the layer keeps, that an
ordinary execution pays no history, and the next preparation decides again; with_cfg alone
no longer prices history, so a system call run straight through the handler's public entry
points on a context switched to the neutral configuration and back pays none, as before the
history policy was derived.
@RealiCZ RealiCZ added spec:unstable Changes to the unstable spec (currently REX5) comp:core Changes to the `mega-evm` core crate comp:doc Changes in the documentation api:breaking Crate interface change — downstream users must update labels Oct 23, 2026
@mega-maxwell

mega-maxwell Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Claude review status

Living comment — rewritten in place. The review workflow keeps this single comment up to date instead of posting a new one each round, so it always describes the latest reviewed commit and the earlier text is intentionally gone. No reply is needed here; reply to a finding in its own review thread, and answer an open question in a reply on this PR. The next review round reconciles your answer.

🛠️ Review did not finish

Attempted head 5e3f30ad · updated 2026-10-29T02:08:38+00:00

This round did not publish: MODEL_NO_OUTPUT: Claude returned no structured review output. Anything listed below is from the last round that did. Workflow run: https://github.com/megaeth-labs/mega-evm/actions/runs/40804560927

@codspeed

codspeed Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Merging this PR will regress 4 benchmarks

2 warnings

⚠️ 18 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

⚠️ Base and head ran on different runner types

Some benchmarks with significant performance changes were compared across different runner types, so the changes might not come from the code. Run base and head on the same runner type, or use CodSpeed Macro Runners for more stable results.

Learn more about Macro Runners

Open the report in CodSpeed to investigate

⚡ 8 improved benchmarks
❌ 4 regressed benchmarks
✅ 44 untouched benchmarks
🆕 1 new benchmark
⏩ 389 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
❌ oracle_reads/op_revm 152.7 µs 167.1 µs -8.65%
❌ storage_reads/op_revm 158.7 µs 168.2 µs -5.63%
❌ volatile_reads/satin 272.5 µs 287.3 µs -5.14%
❌ storage_reads/satin 174.8 µs 184.1 µs -5.06%
⚡ system_address_misses/satin 885.9 µs 785.6 µs +12.76%
⚡ remaining_compute_gas/op_revm 571.7 µs 528.5 µs +8.18%
⚡ intercepted_calls/op_revm 586.1 µs 544 µs +7.76%
⚡ system_address_misses/op_revm 581.9 µs 540 µs +7.75%
⚡ value_calls/satin 745 µs 691.8 µs +7.68%
⚡ deep_calls/op_revm 413.3 µs 386.2 µs +7.02%
⚡ logs/satin 193.5 µs 183.1 µs +5.69%
⚡ value_calls/op_revm 403 µs 382.2 µs +5.45%
🆕 erc20_transfers N/A 3.9 ms N/A

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-w10 (5e3f30a) with cz/feat/satin-w2 (2ec4758)

Open in CodSpeed

Footnotes

  1. 389 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. ↩

@codecov

codecov Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

🧬 Mutation testing — ✅ PASS

Diff mutation score: 100.0% (153/153 viable mutants killed)

  • caught: 153
  • survived (real gaps): 0
  • timed out (inconclusive): 0
  • suppressed (equivalent/dead-code): 0
  • unviable: 189 · timeout total: 0

No new test gaps introduced by this change. 🎉

…ver the docs still put them on the gas tracker
…de draws the creation's budget, and the engine has no separate inspected lifecycle
… settlement left, and the halt tests skip a free history byte
…wherever the docs still left it to revm's rule
…hat a failed _one call leaves in the journal
…etry a _one call after a failed deposit recovery
@RealiCZ RealiCZ changed the title refactor(satin): read figures off revm, the journal and the prepared execution instead of keeping copies refactor(satin): keep no copies of revm's figures, account for frame starts in the engine, and start clean after an error Oct 27, 2026
…h what the child hands back, and the transaction's gas holds what its frame hands back
…draws

No provider the engine runs draws a pool, so the draws read back off
revm's answer were none, and adopting none left the start's entry as its
split opened it. The read-back and its wiring test go; the start ledger's
fail-closed adoption stays, with its unit test, as the rule a provider
that draws on its answer is adopted by.
…tart's four figures

Pins the revm fork and the op-revm fork at the commits where the pools,
the start charge, the history allowance and the reservoir share leave
the fork and revm forwards and settles the reservoir as upstream does.
The pins name local commits by full SHA until the forks are pushed.

- Host::frame_settle gives a child's caller back the reservoir it held
  while the child ran (FrameSettle::held_reservoir), the part
  forward_start kept, on top of what revm's settlement put in place of
  the caller's reservoir. The transaction's gas holds a copy of its
  frame's reservoir, which the settlement replaced, so nothing is added
  back at the transaction's start, and a halted transaction's regular
  gas is still burned after the start charge is given back.
- The pool identifier and amounts of a frame's budgets are the engine's
  (limit/pool.rs), exported beside LimitKind for the start ledger's
  test recorder.
- MegaContext::admit_code_deposit takes the deposit alone, the hook's
  signature in the fork; the outcome and input literals lose the
  removed fields, and two hand-settled tests hand the frame a copy of
  the reservoir rather than taking it.
- The schedule test asserts the engine's own constants; the rendered
  pricing table loses the five entries the fork no longer has; the
  reference holding of a child's shares runs on explicit figures; the
  keyless differential record's digests move where the record rendered
  the tracker's removed fields, and only there.
…gives a child's caller back the reservoir it kept
…, and halts the transaction's frame after a value transfer above and below the cap
…for a start's shares, and the module table lists the pool types
…ld are pinned, and two comparisons no mutant can tell apart are gone

- A unit test pins that only a caller's negative net holds its child's
  share to the caller's budget: a net back at zero after a take-back
  and a draw again holds the child to the frame cap alone.
- A unit test pins that an answer rebuilt on its caller's forward puts
  its start back at its shares, undrawn.
- `signed` saturates through `i64::try_from`, with no comparison at
  `i64::MAX`, where `>` and `>=` agreed.
- A precompile's draw is held to its share with `min`: a draw at, past
  or within the share gives what the two branches gave, without the
  comparisons whose `>`/`>=` forms agreed at zero and at the share.
… an inspector has no budget field left to write

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api:breaking Crate interface change — downstream users must update comp:core Changes to the `mega-evm` core crate comp:doc Changes in the documentation spec:unstable Changes to the unstable spec (currently REX5)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant