Conversation
Morph disables EIP-7623 during execution — `CfgEnv::disable_eip7623` is set at every construction site in `morph_evm::MorphEvmConfig` — matching morph-geth, whose `IntrinsicGas` has no floor term at all (core/state_transition.go:152-200). The transaction pool did not follow: reth's `ensure_intrinsic_gas` derives its `SpecId` from the Prague fork flag, and `MorphChainSpec` activates Prague at Viridian time so EIP-7702 works, so from Viridian onward the pool rejected transactions whose gas limit fell below the EIP-7623 floor. Both clients execute those transactions fine, and morph-geth's pool accepts them, so any reth node taking user transactions rejected traffic the network considers valid. A transaction with 4 KiB of non-zero calldata needs 86_536 gas to execute but 184_840 to clear the floor, which covers data-availability style transactions and any wallet sizing gas from `eth_estimateGas`. Run the inner pipeline through Morph's own `validate_inner_with_state`, which mirrors reth's `validate_one_with_provider` and re-adjudicates exactly one verdict: a stateless `IntrinsicGasTooLow` is re-checked without the floor term. `initial_total_gas` is recomputed with the same revm helper and the same `SpecId` selection reth uses, so genuinely underfunded transactions are still rejected with the same error.
There was a problem hiding this comment.
Claude Code Review
Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.
Tip: disable this comment in your organization's Code Review settings.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe transaction pool now re-adjudicates ChangesIntrinsic gas validation
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant TransactionPool
participant MorphValidator
participant RethValidator
participant StateProvider
TransactionPool->>MorphValidator: Validate transaction
MorphValidator->>RethValidator: Run stateless checks
RethValidator-->>MorphValidator: Return IntrinsicGasTooLow
MorphValidator->>MorphValidator: Recompute intrinsic gas without EIP-7623 floor
MorphValidator->>StateProvider: Fetch latest state
StateProvider-->>MorphValidator: Return state
MorphValidator->>RethValidator: Run stateful checks
RethValidator-->>TransactionPool: Return validation result
Merge Risk: ⚪ Minimal · up to The transaction pool now admits valid Morph transactions that were incorrectly rejected by the EIP-7623 calldata floor while continuing to reject genuinely underfunded transactions. The change is ready to merge. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…k gate morph-geth implements EIP-7623 on its unmerged `eip7623` branch by introducing a new hardfork ordered after Jade, so the floor becomes correct behaviour once that fork activates. Record that this carve-out must then be converted to a fork gate rather than reverted, alongside the matching `disable_eip7623` change in morph-evm and geth's L1-message exemption. Tracked in #201.
…tch to it Block number and timestamp, base fee, L1 fee parameters and the EVM environment were written and read as separate fields. A canonical update racing a validation batch could pair the new block's state with the previous block's environment, and the state provider was opened by block *number*, which a same-height reorg cannot disambiguate. `MorphValidationHead` bundles hash, number, timestamp, base fee, L1 info and the `EvmEnv`, published atomically behind one `RwLock<Option<Arc<_>>>`. A batch pins one head and opens its provider by hash. The inner validator's stateful checks now run against that pinned provider too, so the account read, the token read and the environment agree on a block — previously the inner validator used `latest()` while the token read used the cached head number. Until a head has been published, validation returns `Error` (retry) rather than validating against a zero head. Note for #199: its `validate_inner_with_state` covers the same stateless/stateful split; the EIP-7623 carve-out slots in at the `validate_stateless` call here when that branch is rebased. Found by the second external review of #200. Claude-Session: https://claude.ai/code/session_01PiUjd47Da71WG2BFkDQz9q
Problem
Morph disables EIP-7623 during execution —
CfgEnv::disable_eip7623is set at every construction site inmorph_evm::MorphEvmConfig(crates/evm/src/config.rs:39,90) — matching production morph-geth5744b8f66, whoseIntrinsicGashas no floor term at all (core/state_transition.go:152-200).The transaction pool did not follow. reth's
ensure_intrinsic_gasderives itsSpecIdfrom the Prague fork flag (transaction-pool/src/validate/eth.rs:1499-1543), andMorphChainSpecactivates Prague at Viridian time so EIP-7702 works (crates/chainspec/src/spec.rs:121-126). So from Viridian onward the pool rejected any transaction whose gas limit fell below21_000 + 10 * tokens.Both clients execute those transactions fine and morph-geth's pool accepts them, so a reth node taking user traffic rejects transactions the network considers valid:
Anything in that window is refused with
intrinsic gas too low. It covers data-availability style transactions and any wallet sizing gas frometh_estimateGas, which also has no floor.Block import is unaffected — the engine path does not run pool validation — so this is an admission and propagation problem on any reth node exposing
eth_sendRawTransaction, not a consensus one.Found by the pre-migration cross-client audit (POOL-01), reproduced with a
MockEthProviderprobe before and after the Viridian timestamp.Fix
Route the inner pipeline through
MorphTransactionValidator::validate_inner_with_state, which mirrors reth'svalidate_one_with_provider— stateless checks, fetch and cache a state provider, stateful checks — and re-adjudicates exactly one verdict: a statelessIntrinsicGasTooLowis re-checked byintrinsic_gas_is_sufficient, which omits the floor term.initial_total_gasis recomputed with the same revm helper and the sameSpecIdselection reth uses, so the part Morph does enforce cannot drift from upstream, and a genuinely underfunded transaction is still rejected with the same error.This is an interim measure — see #201
morph-geth has EIP-7623 implemented on its unmerged
eip7623branch (morph-l2/go-ethereum, tipc685e19d3). That branch does not flip a flag on an existing fork: it introduces a new hardforkNextFork(nextForkTime, ordered after Jade) and enables the floor atIsNextForkin both execution and its own pool.NextForkTimeis not scheduled on any network yet.So the two clients gate the same rule at different points, and this PR only closes the interval between them:
eip7623branchWhen that fork lands, do not revert this — the pool would go back to applying the floor one hardfork early. Convert
intrinsic_gas_is_sufficientto a fork gate instead, alongside the matchingdisable_eip7623change inmorph_evmand geth's L1-message exemption. #201 carries the checklist and is linked from the code.The copied
validate_one_with_providerskeleton is the cost of upstream having no knob here:ensure_intrinsic_gastakes only theForkTracker, even thoughmax_initcode_sizeandtx_gas_limit_capon that same tracker already come from the EVM config. If upstream gains an equivalent switch this method collapses to one builder call; until then the doc comment tells the next reth upgrade to re-check both upstream functions.Tests
Two regression tests on a validator whose fork tracker reports Prague:
viridian_pool_accepts_calldata_below_eip7623_floor— 4096 non-zero calldata bytes at exactly 86_536 gas is admitted.viridian_pool_still_rejects_underfunded_intrinsic_gas— the same transaction at 86_535 gas is still rejected asIntrinsicGasTooLow.cargo test --all,cargo fmt --all -- --checkandcargo clippy --all --all-targets -- -D warningspass.