Skip to content

Commit 46c91f9

Browse files
authored
fix(eip-7702): filter intrinsic gas + Gravity acceptance tests (#343)
* fix(eip-7702): filter_invalid_txs intrinsic gas check (gravity-audit#668) The pre-execution filter now mirrors the pool's ensure_intrinsic_gas so a TxEip7702 with gas_limit < 21000 + 25000·N — injected outside the pool's fork-gate — is discarded instead of reaching grevm and panicking the executor with IntrinsicGasTooLow. Free filter_invalid_txs and its tests move into a new tx_filter module per _local/drafts/pipe-exec-layer-lib-refactor.md step 3, shrinking lib.rs from 1829 to 1448 lines. Two regression tests added for intrinsic-gas-too-low and at-the-floor 7702 cases under Prague. * test(eip-7702): acceptance tests for #668 fix + designator e2e Implements the focused subset of the EIP-7702 acceptance design (`_local/drafts/eip-7702/EIP-7702-acceptance-tests-2026-06-01.md`) that exercises Gravity-side integration of the gravity-audit#668 fix. Unit coverage: - §3.1 U-1..U-3 in `tx_filter::tests`: two-auth 7702 tx with sufficient gas passes the filter; three-auth 7702 tx at 21k gas is discarded (the #668 regression); a Prague-spec legacy tx is not regressed. - §3.2 U-4 / U-5 in `eth::tests`: `ensure_intrinsic_gas` rejects a TxEip7702 one wei below the Prague intrinsic floor; `validate_one` short-circuits the same tx with `TxTypeNotSupported` when the pool's ForkTracker has Prague off, so the intrinsic path is never reached. E2E coverage in new `gravity_eip7702_test.rs` (drives MockConsensus + PipeExecLayerApi under `gravity_prague_p3.json`, Prague at block 100): - P-12: a low-gas TxEip7702 injected through OrderedBlock is discarded by `filter_invalid_txs` instead of panicking the executor at `lib.rs:1072`. Pins the #668 fix at the integration boundary. - P-13: a sufficient-gas TxEip7702 with one valid Authorization lands on chain; the authority's account code becomes the 23-byte `0xef0100 || target` designator. - P-14: P-13 runs under both `disable_grevm=false` (`GrevmExecutor`) and `disable_grevm=true` (`WrapExecutor<BasicBlockExecutor>`); designator deployment is observably equivalent in both modes. The spec's stronger "byte-identical state roots" sub-claim is not asserted — this codebase intentionally delegates cross-executor equivalence to unit-level diff tests (`crates/ethereum/evm/src/parallel_execute.rs:446-449`); both state roots are printed for forensic inspection. Adds `alloy-signer` / `alloy-signer-local` as dev-deps of the execute crate to sign TxEip7702 + SignedAuthorization in tests. Anvil account 0 (pre-funded in `gravity_prague_p3.json`) is the relayer; authority is a deterministic non-funded EOA so failures reproduce. Out of scope, documented in the test header as follow-ups: P-1 / P-3 (pool fork-gate via RPC), P-5..P-10 (multi-auth, chain_id==0, re-delegation, CALL/DELEGATECALL/STATICCALL → designator — exercise upstream revm semantics), P-15..P-18 (grevm BlockSTM concurrency), N-1 (manual smoke check). * fix(eip-7702): reject pre-Prague 7702 txs at filter + drop dead test code Address PR #343 review findings. 1. Pre-Prague boundary guard (acceptance design P-2). When `spec_id < PRAGUE`, revm-interpreter's `calculate_initial_tx_gas` ignores `authorization_list_num` entirely (gas/calc.rs:417), so a TxEip7702 with `gas_limit == 21000` would have passed the new intrinsic check added in 3877eab and reached the executor — where revm-handler's `validation.rs:181-182` rejects it with `Eip7702NotSupported` and the `unwrap_or_else(|err| panic!(...))` at `lib.rs:1067-1073` panics the node, which is exactly the failure mode gravity-audit#668 reported. Add an explicit `tx.is_eip7702() && !spec_id.is_enabled_in(PRAGUE)` short-circuit in `filter_invalid_txs::is_tx_valid` plus a unit test that pins the SHANGHAI-spec rejection path with a 100k-gas tx (well above the pre-Prague flat 21k intrinsic) so the guard, not the intrinsic check, is what kicks in. 2. Drop the dead `now_us` helper and its `_silence_unused` shim from `gravity_eip7702_test.rs`. All four e2e scenarios use `p3_ts_us`; `now_us` was left over from an earlier iteration. Also drop the now unused `SystemTime` import. * refactor(eip-7702): derive spec_id inside filter_invalid_txs The caller's MERGE/SHANGHAI/PRAGUE ladder at lib.rs:644-650 mislabelled CANCUN-active blocks as SHANGHAI. The code happened to be correct for the filter's purposes only because CANCUN and SHANGHAI behave identically for `calculate_initial_tx_gas` — but that's an implicit assumption the caller had no way to verify, and a future hardfork that *did* change intrinsic gas semantics could silently break the filter without anyone noticing. The PR review surfaced this as a duplication nit; in fact it was a latent correctness bug. Move spec_id derivation inside `filter_invalid_txs` using the canonical `revm_spec_by_timestamp_and_block_number` helper (alloy-evm) — the same mapping the executor itself uses, so the two cannot drift apart on a future hardfork. The function now takes `&ChainSpec, block_timestamp, block_number` instead of a pre-built `SpecId`; the lib.rs ladder and its truncated PRAGUE/SHANGHAI/MERGE branches are gone. Unit tests switch from passing `SpecId::PRAGUE` / `SpecId::SHANGHAI` literals to passing `&prague_chain_spec()` / `&shanghai_chain_spec()` built via `ChainSpecBuilder::from(&*MAINNET).{prague,shanghai}_activated()` — same semantic intent, but the test now exercises the same spec resolution path the production caller does. * ci(integration): cover EIP-7702 / EIP-2935 tests in gravity-pipe-test job The integration tests for EIP-7702 (gravity_eip7702_test.rs) and EIP-2935 (gravity_eip2935_test.rs) were silently uncovered by CI: unit.yml excludes the whole `reth-pipe-exec-layer-*` family and also filters out `kind(test)` integration binaries, and the existing gravity-pipe-test job used `cargo test --exact test`, which only matches the single `fn test()` in gravity_pipe_test.rs. Switch that step to `cargo nextest run` with a binary filterset covering gravity_pipe_test, gravity_eip2935_test, gravity_eip7702_test. All three share the same MockConsensus + PipeExecLayerApi harness, so one cold build covers them. Drop --show-output --nocapture: nextest prints failed-test output by default, which is cleaner for CI logs. GRETH_DISABLE_PIPE_EXECUTION is intentionally left unset here (commented inline) since these tests assert the real fork-gated pipe-exec path. * ci(integration): restrict nextest compile scope with --test to avoid latent breakage The previous commit (64a2617) extended the gravity-pipe-test job to cover gravity_eip2935_test and gravity_eip7702_test via `cargo nextest run -p reth-pipe-exec-layer-ext-v2 -E 'binary(...)'`. That filterset only filters at *run* time — `-p <crate>` still builds every integration test binary in the crate, which surfaced a latent breakage: gravity_hardfork_test.rs imports `reth_ethereum_forks::Hardforks` but `reth-ethereum-forks` is not declared in execute/Cargo.toml's [dev-dependencies], so compilation fails with E0432. That test file (and its missing dep) pre-dates this PR — it shipped via #307 and was silently uncovered by the original `cargo test --test gravity_pipe_test` command, which only compiled one binary. Fixing the underlying defect is out of scope for the EIP-7702 fix branch; track it separately. Switch to explicit `--test <name>` flags per binary, which restricts both compile and run to exactly the three intended targets. CI scope stays where intended (gravity_pipe_test + EIP-7702 + EIP-2935); the latent gravity_hardfork_test.rs breakage is left for a follow-up audit-tracked fix. Verified locally with `cargo nextest run --no-run --locked -p reth-pipe-exec-layer-ext-v2 --test gravity_pipe_test --test gravity_eip2935_test --test gravity_eip7702_test` (exit 0). * ci(nextest): bump slow-timeout for gravity pipe-exec integration tests The previous commit (7f076ae) made these three integration binaries run in CI for the first time under nextest. The default slow-timeout of 30s × terminate-after=4 (= 120s hard kill) is too tight: gravity_pipe_test pushes 1671 blocks through MockConsensus + PipeExecLayerApi, and under runner contention (sharing a 2-core ubuntu-latest box with 24 other gravity_eip* scenarios) routinely hits ~122s — 2s past the kill threshold. Combined with the workspace-default `retries = { count = 2 }`, nextest then burns ~6 min retrying a fundamentally fine test before declaring failure. Add an override mirroring the existing `binary(e2e_testsuite)` entry, granting these three binaries 2m × terminate-after=3 (6 min ceiling per test). Other 24 gravity_eip2935/7702 scenarios already passed comfortably under the default — they fit in well under 30s each — so the override is scoped narrowly and doesn't slacken correctness signal for the rest of the workspace. * ci(nextest): bump gravity pipe-exec timeout to 30m and disable retries Mirrors the contract of the pre-nextest CI command (`cargo test --test gravity_pipe_test`), which had no per-test timeout — only the 60-min job-level cap. The previous 2m × 3 (= 6 min) override was tight enough to risk false-positive kills under runner contention; bump to 5m × 6 (= 30 min absolute ceiling) so the kill threshold is dominated by the job timeout, not nextest's per-test policy. Set retries = 0 for these binaries: - These are deterministic state-machine tests (boot a reth node, push a fixed block sequence, assert end state); a fail-once test will fail-again, so retry burns runner time without adding signal. - Default retries=2 combined with 30m per-test would be 90m worst case, exceeding the 60-min job timeout-minutes.
1 parent ba7e949 commit 46c91f9

8 files changed

Lines changed: 1580 additions & 366 deletions

File tree

.config/nextest.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,17 @@ slow-timeout = { period = "2m", terminate-after = 10 }
1515
[[profile.default.overrides]]
1616
filter = "binary(e2e_testsuite)"
1717
slow-timeout = { period = "2m", terminate-after = 3 }
18+
19+
# Gravity pipe-exec-layer integration tests: each test boots a full reth node
20+
# and pushes 100s–1000s of blocks through MockConsensus + PipeExecLayerApi.
21+
# Mirror the effective contract of the pre-nextest CI command (plain
22+
# `cargo test --test gravity_pipe_test`, which had no per-test timeout and
23+
# relied solely on the 60-min job-level cap). Grant a generous 30-minute
24+
# per-test ceiling so contention-induced slowness doesn't cause flake kills.
25+
# Retries are forced to 0: (a) these are deterministic state-machine tests
26+
# where retry adds no signal, (b) 30m × default 3 attempts = 90m would blow
27+
# past the 60-min job timeout-minutes.
28+
[[profile.default.overrides]]
29+
filter = "binary(gravity_pipe_test) + binary(gravity_eip2935_test) + binary(gravity_eip7702_test)"
30+
slow-timeout = { period = "5m", terminate-after = 6 }
31+
retries = 0

.github/workflows/integration.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ jobs:
7272
env:
7373
RUST_BACKTRACE: 1
7474
RUST_MIN_STACK: 16777216
75+
# Intentionally NOT setting GRETH_DISABLE_PIPE_EXECUTION: these tests
76+
# drive PipeExecLayerApi end-to-end and assert the real fork-gated path.
7577
timeout-minutes: 60
7678
steps:
7779
- name: Free Disk Space
@@ -90,7 +92,21 @@ jobs:
9092
- uses: Swatinem/rust-cache@v2
9193
with:
9294
cache-on-failure: true
93-
- run: cargo test --package reth-pipe-exec-layer-ext-v2 --test gravity_pipe_test -- --exact test --show-output --nocapture
95+
- uses: taiki-e/install-action@nextest
96+
- name: Run gravity pipe + EIP-7702 / EIP-2935 integration tests
97+
# `--test <name>` is required (vs. just `-E 'binary(...)'`) because
98+
# nextest's filterset only filters at *run* time — without `--test`,
99+
# `-p <crate>` still tries to compile every integration binary in the
100+
# crate, including ones that don't compile in this workspace (e.g.
101+
# gravity_hardfork_test.rs depends on reth-ethereum-forks which is not
102+
# in dev-dependencies). `--test` restricts both compile and run.
103+
run: |
104+
cargo nextest run \
105+
--locked \
106+
-p reth-pipe-exec-layer-ext-v2 \
107+
--test gravity_pipe_test \
108+
--test gravity_eip2935_test \
109+
--test gravity_eip7702_test
94110
95111
integration-success:
96112
name: integration success

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/pipe-exec-layer-ext-v2/execute/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ reth-trie-parallel.workspace = true
2727
reth-provider.workspace = true
2828
reth-rpc-eth-api.workspace = true
2929
revm-primitives.workspace = true
30+
revm-interpreter.workspace = true
3031
gravity-storage.workspace = true
3132
gravity-primitives.workspace = true
3233
grevm.workspace = true
@@ -75,6 +76,8 @@ eyre.workspace = true
7576
rand.workspace = true
7677
criterion.workspace = true
7778
hex-literal = "0.4"
79+
alloy-signer.workspace = true
80+
alloy-signer-local.workspace = true
7881

7982
[features]
8083
pipe_test = ["reth-provider/pipe_test"]

0 commit comments

Comments
 (0)