fix(rpc): register BLS pop-verify precompile unconditionally (split from #367)#370
Open
nekomoto911 wants to merge 6 commits into
Open
fix(rpc): register BLS pop-verify precompile unconditionally (split from #367)#370nekomoto911 wants to merge 6 commits into
nekomoto911 wants to merge 6 commits into
Conversation
…-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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
cargo checkagainst the currentGalxe/gravity-reth:mainWhat
1.
fix(rpc): register BLS pop-verify precompile unconditionally— the pipe execution layer registers BLS on every block (pre-Alpha viapre_alpha_precompiles, post-Alpha alongside randomness); the RPC side registered nothing for BLS. Anydebug_trace*/trace_*replay of a historical block calling0x…625f5001therefore 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 ofregister_custom_precompilesincrates/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-rpctook a prod dep onreth-pipe-exec-layer-ext-v2for the BLS helper, which is philosophically backwards (pipe sits closer to consensus). Move the helper togravity-precompiles::bls_pop_verify(whererandomness_by_heightalready lives); the pipe-exec-layer dep onreth-rpcdrops 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 samecallTraceroutput 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 Alphatest_rpc_bls_call_debug_trace_transaction_byte_equal— single-tx trace path (`debug_traceTransaction`)Test plan
cargo check -p reth-pipe-exec-layer-ext-v2 -p reth-rpc -p gravity-precompiles --tests --benchesCompatibility
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_000flat charge andregister_custom_precompilessemantics preserved verbatim.Coordination