Skip to content

fix(rpc): register BLS pop-verify precompile unconditionally (split from #367)#370

Open
nekomoto911 wants to merge 6 commits into
Galxe:mainfrom
nekomoto911:fix/bls-pop-verify-rpc-register
Open

fix(rpc): register BLS pop-verify precompile unconditionally (split from #367)#370
nekomoto911 wants to merge 6 commits into
Galxe:mainfrom
nekomoto911:fix/bls-pop-verify-rpc-register

Conversation

@nekomoto911

Copy link
Copy Markdown
Collaborator

Summary

Three orthogonal commits factored out of #367 — fix the pre-existing RPC↔canonical divergence for the BLS pop-verify precompile, fold in the hygiene move that comes with it, and pin both with a replay test. None depends on Gravity Alpha.

Why split out of #367

The Alpha hardfork PR (#367) bundled this BLS RPC gap as a "fold-in orthogonal pre-existing fix" (explicitly noted in its body under "Orthogonal pre-existing fixes folded in"). Surfacing it on its own:

What

1. fix(rpc): register BLS pop-verify precompile unconditionally — the pipe execution layer registers BLS on every block (pre-Alpha via pre_alpha_precompiles, post-Alpha alongside randomness); the RPC side registered nothing for BLS. Any debug_trace* / trace_* replay of a historical block calling 0x…625f5001 therefore diverged from canonical (the precompile address has no code on the RPC side, so the CALL returned 0/empty). Fix: unconditional registration at the top of register_custom_precompiles in crates/rpc/rpc/src/eth/helpers/call.rs, before any Alpha gate.

2. refactor: move BLS pop-verify precompile to gravity-precompiles — hygiene: after the registration fix above, reth-rpc took a prod dep on reth-pipe-exec-layer-ext-v2 for the BLS helper, which is philosophically backwards (pipe sits closer to consensus). Move the helper to gravity-precompiles::bls_pop_verify (where randomness_by_height already lives); the pipe-exec-layer dep on reth-rpc drops to dev-dependency status.

3. test(rpc): BLS pop-verify precompile replay byte-equal canonical (§3.5) — three test functions, each booting a full pipe execution layer harness and asserting byte-equal canonical via the same callTracer output downstream consumers see:

  • test_rpc_bls_call_replay_byte_equal_canonical — post-Alpha block-family path (`debug_traceBlock` + `trace_block` parity)
  • test_rpc_bls_call_pre_alpha_replay_unchanged — long-term regression guard against accidentally re-gating BLS RPC registration behind Alpha
  • test_rpc_bls_call_debug_trace_transaction_byte_equal — single-tx trace path (`debug_traceTransaction`)

Test plan

  • Local cargo check -p reth-pipe-exec-layer-ext-v2 -p reth-rpc -p gravity-precompiles --tests --benches
  • CI: BLS replay byte-equal canonical tests pass under both grevm + disable-grevm executors

Compatibility

Zero behaviour change on the pipe (canonical) side. RPC path now matches canonical for BLS calls in any block — pre- or post-Alpha. The hygiene refactor is a pure path/import change with POP_VERIFY_GAS = 110_000 flat charge and register_custom_precompiles semantics preserved verbatim.

Coordination

…-audit§3.5.0)

The pipe execution layer registers the BLS12-381 pop-verify precompile
(0x…625f5001) on **every** block:
  - pre-Alpha via `pre_alpha_precompiles`
    (`pipe-exec-layer-ext-v2/execute/src/lib.rs:1495-1497`)
  - post-Alpha alongside randomness
    (`pipe-exec-layer-ext-v2/execute/src/lib.rs:393-396`)

The RPC side, however, only registered the randomness-by-height
precompile, and only when Alpha is active — BLS was never registered at
all. Any `debug_traceBlock` / `debug_traceTransaction` / `trace_block` /
`trace_replayTransaction` replay of a historical block (pre-Alpha **or**
post-Alpha) that calls `0x…625f5001` therefore diverges from canonical
(the precompile address has no code on the RPC side, so the CALL
returns 0/empty and any receipt-log assertions in the user contract
fail).

Fix: register BLS unconditionally at the top of
`register_custom_precompiles`, before the Alpha gate. Randomness
registration remains Alpha-gated to match the pipe's post-Alpha branch.
BLS helper + addr are re-used from `reth_pipe_exec_layer_ext_v2`
(`bls_precompile::create_bls_pop_verify_precompile` /
`onchain_config::BLS_PRECOMPILE_ADDR`); no cycle is introduced because
`reth-pipe-exec-layer-ext-v2` depends only on `reth-rpc-eth-api`, not on
`reth-rpc`.

No tests in this commit — RPC replay tests for BLS require trace-mock
infrastructure and are tracked separately per
`_local/drafts/system-tx-gas-exempt/acceptance-tests-2026-06-26.md §5.4`.
Hygiene refactor: BLS pop-verify precompile lived in
`crates/pipe-exec-layer-ext-v2/execute/src/bls_precompile.rs` and the
`BLS_PRECOMPILE_ADDR` constant in `.../onchain_config/mod.rs`. After the
BLS RPC registration fix (23a5558), `reth-rpc` started using
`reth-pipe-exec-layer-ext-v2` as a prod dep to reach the BLS helper —
philosophically backwards since the pipe layer sits closer to consensus.

`gravity-precompiles` is the existing home for chain-specific precompiles
(`randomness_by_height` already lives there). Move the BLS helper to
`gravity-precompiles::bls_pop_verify`, switch both pipe-exec-layer and
reth-rpc to import from there. The pipe-exec-layer dep on reth-rpc is
no longer needed for BLS — it drops to dev-dependency status (still
needed for the R9 randomness-provider equivalence test).

Zero behaviour change: the precompile implementation, gas pricing
(POP_VERIFY_GAS = 110_000), and the `register_custom_precompiles`
unconditional-registration semantics are preserved verbatim.
Adds the §3.5 BLS RPC replay must-pass row from
`acceptance-tests-2026-06-26.md` — the gate that has been outstanding since
commit `23a55587c4` ("fix(rpc): register BLS pop-verify precompile
unconditionally"), whose commit body explicitly noted "RPC replay tests for
BLS … are tracked separately".

Three test functions, each booting a full pipe execution layer harness and
asserting byte-equal canonical via the same callTracer output that downstream
consumers see:

  1. `test_rpc_bls_call_replay_byte_equal_canonical`
     post-Alpha block-family path. Block 5 ts is Alpha-active, contains a
     user tx calling `0x…625f5001` (144-byte input → 32-byte 0x00..00 output,
     `POP_VERIFY_GAS = 110_000` flat charge). `debug_traceBlock` + callTracer
     and `trace_block` (parity) are exercised; the BLS frame `to`, `gas_used`,
     `output.len`, and absence of error are byte-equal to canonical receipt.

  2. `test_rpc_bls_call_pre_alpha_replay_unchanged`
     Same fixture shape, but `alphaTime` is set to a far-future value so every
     block timestamp is strictly pre-Alpha. This is the long-term regression
     guard against accidentally re-gating BLS RPC registration behind Alpha
     — pre-fix, RPC routed BLS calls in pre-Alpha blocks to an empty account
     (no 110_000 gas charge), diverging from canonical for the entire pre-
     Alpha history. The fix is `call.rs:117-123` (`register_custom_precompiles`)
     where BLS is now registered *before* the Alpha gate.

  3. `test_rpc_bls_call_debug_trace_transaction_byte_equal`
     Single-tx path. `debug_traceTransaction` is invoked on the BLS tx hash
     and the call frame is walked to locate the BLS-targeted frame; the top
     frame's `gas_used` matches canonical, the BLS frame is error-free and
     emits a 32-byte output. Covers the `call.rs:733-807`
     `replay_transactions_until` family.

Coverage matrix:

  | dim                | test_1 | test_2 | test_3 |
  | endpoint family    | block  | block  | tx     |
  | Alpha regime       | post   | pre    | post   |
  | byte-equal channel | gas_used, output.len, frame.to, no error  |

BLS fixture: 144 zero bytes. The byte-equality holds regardless of input
validity — both pipe and RPC run the same handler against the same input, so
any valid 144-byte buffer drives the precompile through its full code path
and produces deterministic gas + output. This reuses the unit-test fixture
style from `gravity_precompiles::bls_pop_verify::tests` (no fresh blst
keypair generated). Re-declares `BLS_PRECOMPILE_ADDR` as a local const to
avoid pulling `gravity-precompiles` into the test crate's dev-deps (sibling
`gravity_bls_precompile_test.rs:73` does the same).

Run:
  cargo nextest run -p reth-pipe-exec-layer-ext-v2 --test gravity_system_tx_bls_replay_test
  → 3 passed.
`clippy::doc-markdown` (`-D warnings`) flagged eight bare `PoP` references
in module/item-level doc comments of the BLS pop-verify precompile. Wrap
them in backticks so they're treated as inline code, matching the existing
treatment of `POP_VERIFY_GAS`, `PrecompileInput`, etc.

No behavior change.
`clippy::useless-conversion` (`-D warnings`) flagged the `.into()` after
`format!(...)` since `PrecompileError::Other` already takes `String`. The
conversion is a no-op `String -> String`. Drop it.

No behavior change.
The two CI hygiene cherry-picks (`f30bf2b37c` PoP backticks +
`d6caea4d72` useless `.into()`) covered the prod doc paths of
`bls_pop_verify.rs`, but a sibling doc comment inside the file's
`#[cfg(test)] mod tests` ("Generate a BLS12-381 keypair and PoP for
testing") still trips `clippy::doc-markdown` once the code moved from
`pipe-exec-layer-ext-v2` (which had a permissive module-level allow) to
`gravity-precompiles`. Backtick `PoP` to match the eight prod sites.
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