fix(evm): make EnrichedMegaTx's cached tx_size/da_size reachable#331
fix(evm): make EnrichedMegaTx's cached tx_size/da_size reachable#331RealiCZ wants to merge 5 commits into
Conversation
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.
🧬 Mutation testing — ✅ PASSNothing to test — no mutants were generated on the changed lines. |
Codecov Report❌ Patch coverage is
☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Label checkThe spec:stable and api:compatible labels look correct now. Missing comp:misc |
🧬 Mutation testing — ✅ PASSDiff mutation score: 100.0% (1/1 viable mutants killed)
No new test gaps introduced by this change. 🎉 |
There was a problem hiding this comment.
- Summary: focused re-review of the
EnrichedMegaTxcache-reachability fix, the newrun_transaction_enrichedentry point, and its test/bench surface. - Nice: the
debug_assertcross-check onrun_transaction_enrichedcloses the trust-boundary risk ofEnrichedMegaTx::newaccepting 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_sizessplit cleanly separates the two size-resolution paths without changing the existingBlockExecutor-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_flagdelegation is incomplete). - No still-open prior review threads.
…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.
|
Thanks for the re-review. Addressed the inline Also went back and closed the codecov patch-coverage gap (14 lines, 67.4%) that was still open at that point:
|
There was a problem hiding this comment.
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.
Summary
EnrichedMegaTx<T>'sMegaTransactionExtoverride fortx_size/estimated_da_sizecould never dispatch — the trait methods carry awhere Self: Encodable2718bound thatEnrichedMegaTxnever 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 inEnrichedMegaTxexpecting it to be reused, butMegaBlockExecutor::run_transactionignored that and recomputed every time.Closes #324.
Fix:
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 standardBlockExecutor::execute_transaction_with_commit_conditionpath, which is constrained byalloy_evm'sExecutableTxand can't requireMegaTransactionExt.run_transaction_enriched/execute_mega_transaction_enrichedentry point that readstx_size/da_sizefrom the outerTx(hittingEnrichedMegaTx's cache) instead of unwrapping to the raw inner transaction.crates/mega-evm/benches/enriched_tx.rs).Trust boundary hardening:
run_transaction_enrichedfeedsTx-reportedtx_size/da_sizedirectly intopre_execution_check's consensus-relevant limit checks with no validation, andEnrichedMegaTx::newis a public constructor that accepts arbitrary values. Added adebug_assertcross-check against a fresh recompute (compiled out in release, so it costs nothing in production) and documented the caller contract on bothrun_transaction_enrichedandEnrichedMegaTx::new.Test plan
EnrichedMegaTx's trait-dispatchedtx_size/estimated_da_size/tx_hashreturn the stored fields, not a recomputed value.MegaBlockExecutorintegration test proves the fix changes real behavior: with atx_da_size_limitset between a deliberately understated storedda_sizeand the real recomputed value, the same transaction is rejected underrun_transactionand — before thedebug_assertwas added — was accepted underrun_transaction_enriched.#[cfg(debug_assertions)],#[should_panic]) proves thedebug_assertsafety net actually catches that exact mismatch in debug/test builds. Verified it correctly does not run undercargo test --release(the assert compiles out there by design — documented as the intended profile-dependent trade-off, not an oversight).cargo mutants --in-diffgate locally against the full change: 0 missed mutants.cargo test -p mega-evm(debug and--release) andcargo test -p mega-evmepass.cargo fmt --all --check,cargo clippy --workspace --lib --examples --tests --benches --all-features --locked(debug and--release, checked for unused-import regressions from the newcfg(debug_assertions)gating),cargo sort --check,cargo doc -p mega-evm --no-deps(no new warnings), and theno_std/riscv64 target check all pass.cargo bench -p mega-evm --bench enriched_txrun locally to verify the new benchmark actually executes (not just compiles).