Skip to content

test(e2e): regression for self-sponsored EIP-7702 chain halt (audit #677)#741

Closed
keanji-x wants to merge 1 commit into
mainfrom
test/e2e-7702-selfsponsor-halt-677
Closed

test(e2e): regression for self-sponsored EIP-7702 chain halt (audit #677)#741
keanji-x wants to merge 1 commit into
mainfrom
test/e2e-7702-selfsponsor-halt-677

Conversation

@keanji-x

Copy link
Copy Markdown
Contributor

What

Adds a single-node e2e regression suite (gravity_e2e/cluster_test_cases/prague_7702_selfsponsor/) for gravity-audit #677 — a CRITICAL, externally-triggerable, deterministic chain halt.

The bug

A self-sponsored EIP-7702 (type-4) transaction — sender is also the authority of an authorization tuple in its own authorization_list — bumps the sender's nonce twice during execution:

  • once in deduct_caller (revm pre_execution.rs:170)
  • once in apply_eip7702_auth_list (revm pre_execution.rs:251), because authority == caller

so the caller's post-state nonce is expect + 2. The currently-pinned grevm (v2.2.4 / 26b586c) hard-asserts assert_eq!(change.info.nonce, expect + 1) at async_commit.rs:78 and panics.

The tx is spec-valid and pool-valid, so any funded EOA can submit it over public JSON-RPC; every validator then panics deterministically on the ordered block (and re-panics on replay) → permanent network halt. This is the bug that halted the testnet on 2026-06-09 (block 1400867 → 1400868).

The fix exists in a newer grevm rev (>= 3c09e7c, which allows a post-nonce in [expect+1, expect+1+self_auth_count]) but is not pinned by gravity-sdk, so the deployed chain remains exposed even though #677 was closed.

Reproduced locally (gate evidence)

Against the pinned build, the self-sponsored 7702 tx kills the node — process exits, 14 RPC failures, zero block progress:

panicked at .../grevm/26b586c/src/async_commit.rs:78:29:
assertion `left == right` failed
  left: 2
 right: 1

→ test reports XFAIL (expected failure: bug present).

Test design / why it's trustworthy

  • Prague coverage gap: the shared devnet genesis doesn't set pragueTime, so a type-4 tx is rejected by the reth pool (transaction type not supported) before execution — the very gap that let fix(e2e): adapt staking tests for 2-step timelock role changes and fix cross-platform compat #677 ship. hooks.py:pre_start enables Prague for this suite only.
  • Ordering fix: deploy.sh copies the suite genesis to <base_dir>/genesis.json and boots the node from it before pre_start runs, so patching only the source artifact is too late and yields a Prague-disabled node + false-negative pass. The hook patches the deployed copy.
  • No false negatives: the test fails hard (not xfail) if the tx is rejected pre-execution, and requires positive proof of execution (receipt present + sender nonce advances by exactly 2) before ever concluding the node is healthy. A Prague-disabled or tx-dropping harness can never masquerade as "fixed."
  • xfail (strict=False): CI stays green until the grevm bump lands; it then flips to xpass to prompt removal of the marker.

Scope

Test-only — adds one e2e suite, no production-code changes. Remove the xfail once grevm is bumped to >= 3c09e7c and relocked.

)

Adds a single-node e2e suite that exercises a self-sponsored EIP-7702
(type-4) transaction — one whose sender is also the `authority` of an
authorization tuple in its own `authorization_list`.

Such a tx bumps the sender's nonce TWICE during execution (once in
`deduct_caller`, once in `apply_eip7702_auth_list`, because authority ==
caller), so the caller's post-state nonce is `expect + 2`. The pinned grevm
(v2.2.4 / 26b586c) hard-asserts `assert_eq!(change.info.nonce, expect + 1)`
in `async_commit.rs:78` and panics. Because the tx is spec-valid and
pool-valid, any funded EOA can submit it; every validator then panics
deterministically on the ordered block (and re-panics on replay) -> permanent
network halt. This is the bug that halted the testnet on 2026-06-09
(block 1400867 -> 1400868). The fix exists in a newer grevm rev (>= 3c09e7c,
which allows a post-nonce in [expect+1, expect+1+self_auth_count]) but is NOT
pinned by gravity-sdk, so the deployed chain remains exposed.

Prague (EIP-7702) is off in the shared devnet genesis, so a type-4 tx is
rejected by the reth pool before it can reach execution — the very coverage
gap that let #677 ship. hooks.py:pre_start enables Prague for THIS suite only
by patching pragueTime=0 into the DEPLOYED <base_dir>/genesis.json (deploy.sh
copies the genesis and boots the node from it BEFORE pre_start runs, so
patching only the source artifact is too late and silently yields a Prague-
disabled node + false-negative pass).

The test asserts the CORRECT (post-fix) behavior — the node survives the tx,
keeps producing blocks, AND actually applies it (receipt present, sender nonce
advances by exactly 2, proving the authority==caller path was exercised) — and
is marked xfail referencing gravity-audit #677 so CI stays green until the
grevm bump lands, then flips to xpass to prompt removal of the marker. It fails
hard (not xfail) if the tx is rejected pre-execution, so a Prague-disabled
harness can never masquerade as a healthy node.

Reproduced locally against the pinned build: the self-sponsored 7702 tx kills
the node (process exits, 14 RPC failures, zero block progress) with
  panicked at .../grevm/26b586c/src/async_commit.rs:78:29:
  assertion `left == right` failed
    left: 2
   right: 1
-> reported XFAIL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@keanji-x

Copy link
Copy Markdown
Contributor Author

Closing as redundant — built on a stale local checkout.

After pulling latest main I confirmed:

My suite only "reproduced" the panic because my checked-out Cargo.lock and prebuilt binary predated #738. There's no need for a duplicate xfail suite for an already-fixed, already-covered bug. Apologies for the noise — I should have pulled latest main and checked merged PRs before filing.

#740 (BLS PoP precompile gas-limit, audit #678) is unaffected: I re-verified that bug is still unfixed at the currently-pinned greth 0adbb4c (bls_precompile.rs:102 returns flat POP_VERIFY_GAS with no gas-limit guard; alloy-evm precompiles.rs:425 still asserts on underflow).

@keanji-x keanji-x closed this Jun 10, 2026
@keanji-x keanji-x deleted the test/e2e-7702-selfsponsor-halt-677 branch June 10, 2026 08:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant