Skip to content

fix(evm): make EnrichedMegaTx's cached tx_size/da_size reachable#331

Open
RealiCZ wants to merge 5 commits into
mainfrom
cz/fix/enriched-mega-tx-cached-size
Open

fix(evm): make EnrichedMegaTx's cached tx_size/da_size reachable#331
RealiCZ wants to merge 5 commits into
mainfrom
cz/fix/enriched-mega-tx-cached-size

Conversation

@RealiCZ

@RealiCZ RealiCZ commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

EnrichedMegaTx<T>'s MegaTransactionExt override for tx_size/estimated_da_size could never dispatch — the trait methods carry a where Self: Encodable2718 bound that EnrichedMegaTx never satisfied, so every caller silently recomputed from the raw inner transaction instead of reusing the precomputed fields.
This defeated the entire purpose of the wrapper: mega-reth's sequencer hot path already precomputes da_size/tx_size (often from a cheap mempool-cached value) and wraps it in EnrichedMegaTx expecting it to be reused, but MegaBlockExecutor::run_transaction ignored that and recomputed every time.
Closes #324.

Fix:

  • Add impl Encodable2718 for EnrichedMegaTx<T> (delegating to the inner transaction) so the bound is satisfiable.
  • run_transaction's existing signature is untouched — it's shared with the standard BlockExecutor::execute_transaction_with_commit_condition path, which is constrained by alloy_evm's ExecutableTx and can't require MegaTransactionExt.
  • Add a new opt-in run_transaction_enriched / execute_mega_transaction_enriched entry point that reads tx_size/da_size from the outer Tx (hitting EnrichedMegaTx's cache) instead of unwrapping to the raw inner transaction.
  • Benchmarked the isolated call cost: recompute is ~410-780ns depending on calldata size; the fixed path is ~0.36-0.37ns, matching a raw field read (crates/mega-evm/benches/enriched_tx.rs).

Trust boundary hardening:

  • run_transaction_enriched feeds Tx-reported tx_size/da_size directly into pre_execution_check's consensus-relevant limit checks with no validation, and EnrichedMegaTx::new is a public constructor that accepts arbitrary values. Added a debug_assert cross-check against a fresh recompute (compiled out in release, so it costs nothing in production) and documented the caller contract on both run_transaction_enriched and EnrichedMegaTx::new.
  • This is safe for the current single-sequencer architecture (the cache is always computed by the same trusted process); the doc comments call out that a future validation-path use of this method would need a different guarantee.

Test plan

  • New unit test asserts EnrichedMegaTx's trait-dispatched tx_size/estimated_da_size/tx_hash return the stored fields, not a recomputed value.
  • New MegaBlockExecutor integration test proves the fix changes real behavior: with a tx_da_size_limit set between a deliberately understated stored da_size and the real recomputed value, the same transaction is rejected under run_transaction and — before the debug_assert was added — was accepted under run_transaction_enriched.
  • A second test (#[cfg(debug_assertions)], #[should_panic]) proves the debug_assert safety net actually catches that exact mismatch in debug/test builds. Verified it correctly does not run under cargo test --release (the assert compiles out there by design — documented as the intended profile-dependent trade-off, not an oversight).
  • Ran the diff-scoped cargo mutants --in-diff gate locally against the full change: 0 missed mutants.
  • Full cargo test -p mega-evm (debug and --release) and cargo test -p mega-evme pass.
  • cargo fmt --all --check, cargo clippy --workspace --lib --examples --tests --benches --all-features --locked (debug and --release, checked for unused-import regressions from the new cfg(debug_assertions) gating), cargo sort --check, cargo doc -p mega-evm --no-deps (no new warnings), and the no_std/riscv64 target check all pass.
  • cargo bench -p mega-evm --bench enriched_tx run locally to verify the new benchmark actually executes (not just compiles).

RealiCZ added 4 commits July 1, 2026 10:32
The MegaTransactionExt override on EnrichedMegaTx<T> could never dispatch: the trait's tx_size/estimated_da_size methods carry a `where Self: Encodable2718` bound that EnrichedMegaTx never satisfied, so every caller silently recomputed from the inner transaction instead of reusing the stored fields.

Add an Encodable2718 impl to satisfy the bound, and a new opt-in run_transaction_enriched/execute_mega_transaction_enriched entry point on MegaBlockExecutor that reads tx_size/da_size from the outer transaction (hitting EnrichedMegaTx's cache) instead of unwrapping to the raw inner tx. run_transaction itself is unchanged, so existing callers are unaffected.

Adds a benchmark (crates/mega-evm/benches/enriched_tx.rs) and tests proving the fix changes real limit-enforcement behavior, not just a returned value.
…wrapping

Add a doc comment explaining why run_transaction_with_sizes exists (shared
body for run_transaction/run_transaction_enriched, sizes resolved by the
caller). Also reflows enriched_tx.rs's module doc comment to satisfy
cargo fmt's comment wrapping.
…on_enriched

run_transaction_enriched feeds Tx-reported tx_size/da_size straight into
pre_execution_check's consensus-relevant limit checks with no validation.
EnrichedMegaTx::new is a public, fully trusting constructor, so a stale or
understated cache could let a transaction bypass tx_da_size_limit,
tx_encode_size_limit, or the block cumulative-size limits.

Add a debug_assert cross-check against a fresh recompute in
run_transaction_enriched (compiled out in release builds, so it doesn't cost
the performance this method exists to deliver), and document the caller
contract on both run_transaction_enriched and EnrichedMegaTx::new. Also
documents on run_transaction why the standard BlockExecutor trait path can't
take the enriched shortcut.

Restructures the block-limit integration test to assert the debug_assert
actually fires on a deliberately mismatched cached da_size, proving the
safety net works, since the assert now makes the test's original "cached
value silently wins" scenario panic by design.
… doc link

The should_panic test for run_transaction_enriched's debug_assert cache
cross-check failed under `cargo test --release`: debug_assert compiles out
in release builds, so the deliberately mismatched cached da_size silently
succeeds instead of panicking. Gate the test (and its now debug-only-used
imports/helper) behind #[cfg(debug_assertions)] and document that this is
the intended profile-dependent trade-off, not an oversight.

Also fixes a broken rustdoc intra-doc link on run_transaction: BlockExecutor
isn't in scope in this file (only referenced via the fully qualified
alloy_evm::block::BlockExecutor path), so the unqualified link never
resolved. cargo doc -p mega-evm --no-deps now has no new warnings from this
change.
@RealiCZ RealiCZ added spec:unchanged No change to any `mega-evm`'s behavior comp:core Changes to the `mega-evm` core crate api:compatible Only new interface or API is introduced. Existing software is compatible. labels Jul 1, 2026
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

🧬 Mutation testing — ✅ PASS

Nothing to test — no mutants were generated on the changed lines.

@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.72727% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 94.6%. Comparing base (5a2ea61) to head (446020e).

Files with missing lines Patch % Lines
crates/mega-evm/src/block/helpers.rs 66.6% 1 Missing ⚠️

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@claude

claude Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Label check

The spec:stable and api:compatible labels look correct now.

Missing comp:misc
mutants/suppressions.toml lives in the mutants/ directory at the repo root, outside all component crates, so comp:misc should be added alongside comp:core to accurately reflect that this PR touches non-core repo infrastructure.

@RealiCZ RealiCZ added spec:stable Touches stable spec code — must not change behavior and removed spec:unchanged No change to any `mega-evm`'s behavior labels Jul 1, 2026
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

🧬 Mutation testing — ✅ PASS

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

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

No new test gaps introduced by this change. 🎉

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ 1 finding

  • Summary: focused re-review of the EnrichedMegaTx cache-reachability fix, the new run_transaction_enriched entry point, and its test/bench surface.
  • Nice: the debug_assert cross-check on run_transaction_enriched closes the trust-boundary risk of EnrichedMegaTx::new accepting arbitrary sizes without paying anything in release, and the #[cfg(debug_assertions)] #[should_panic] test proves that safety net actually fires on a mismatch — profile-dependent trade-off is documented on the method rather than left as a footgun.
  • The run_transaction / run_transaction_enriched / run_transaction_with_sizes split cleanly separates the two size-resolution paths without changing the existing BlockExecutor-backed signature, and the mutation-test refresh replaces the suppressed-as-dead entry with a real assertion that the stored fields are what dispatches.
  • Inline comments: 1 anchorable finding on the new Encodable2718 for EnrichedMegaTx<T> impl (type_flag delegation is incomplete).
  • No still-open prior review threads.

Comment thread crates/mega-evm/src/block/helpers.rs
…d path

Delegate Encodable2718::type_flag on EnrichedMegaTx<T> to the inner
transaction instead of relying on the trait default. Verified with a temp
test that the default (computed via the existing Typed2718::ty() delegation)
already returns the correct value for typed transactions -- so this isn't a
behavior fix, just matching this file's own convention of exhaustively
delegating rather than relying on trait defaults (all four other trait impls
on EnrichedMegaTx already do this), and it's more robust against a future T
that overrides type_flag non-trivially.

Also closes a real patch-coverage gap: execute_mega_transaction_enriched (the
alias) had no caller in any test, and run_transaction_enriched's only test
deliberately panics via its debug_assert before ever reaching the success
path. Add a happy-path test that constructs EnrichedMegaTx::new_slow (an
accurate cache) and calls execute_mega_transaction_enriched, asserting it
succeeds and the outcome carries the cached tx_size/da_size. Un-gates the
create_envelope/EnrichedMegaTx/MegaTransactionExt test-only imports from
#[cfg(debug_assertions)] since they're now also used unconditionally.
@RealiCZ RealiCZ added the comp:misc Changes to the miscellaneous part of this repo label Jul 1, 2026
@RealiCZ

RealiCZ commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for the re-review. Addressed the inline type_flag finding directly on the thread (verified it wasn't actually mis-framing today, applied the suggested delegation anyway for consistency with this file's other trait impls — see 446020e).

Also went back and closed the codecov patch-coverage gap (14 lines, 67.4%) that was still open at that point: execute_mega_transaction_enriched had no test caller at all, and run_transaction_enriched's only test deliberately panics via its debug_assert before ever reaching the success path — so there was no happy-path test proving the enriched path actually succeeds and returns the cached values when they're accurate. Added one (446020e), reran scripts/coverage_mega_evm.sh locally to confirm all 14 lines are now covered.

comp:misc label added per the earlier bot comment — mutants/suppressions.toml is repo infra outside any crate, so that was a fair catch.

Comment thread crates/mega-evm/tests/block_executor/block_limits.rs Dismissed

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean

  • Reviewed the EnrichedMegaTx cache-plumbing fix: new Encodable2718 for EnrichedMegaTx impl to satisfy the trait bound, additive run_transaction_enriched / execute_mega_transaction_enriched entry points that read cached sizes, debug_assert cross-check against a fresh recompute, and matching tests + benchmark.
  • run_transaction (shared with BlockExecutor::execute_transaction_with_commit_condition) is left untouched, so existing callers keep the recomputing behavior — the new path is opt-in.
  • Trust-boundary risk (public EnrichedMegaTx::new + no release-mode revalidation before consensus-relevant limit checks) is called out explicitly in the doc comments on both EnrichedMegaTx::new and run_transaction_enriched, and the debug_assert gives a debug/CI safety net at zero release cost.
  • Tests: the debug-only should_panic test proves the safety net actually fires on a mismatched cache, and the release-safe happy-path test proves execute_mega_transaction_enriched returns an outcome carrying the cached values.
  • No new actionable findings.
  • Still open from earlier reviews: none — the prior type_flag delegation thread was addressed in 446020e and has now been resolved.

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

Labels

api:compatible Only new interface or API is introduced. Existing software is compatible. comp:core Changes to the `mega-evm` core crate comp:misc Changes to the miscellaneous part of this repo spec:stable Touches stable spec code — must not change behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: EnrichedMegaTx tx_size/estimated_da_size overrides are unreachable (precompute optimization never runs)

2 participants