fix(tn-reth): reject value-bearing calls to the BLS precompile (#1286) - #1297
Open
MavenRain wants to merge 9 commits into
Open
fix(tn-reth): reject value-bearing calls to the BLS precompile (#1286)#1297MavenRain wants to merge 9 commits into
MavenRain wants to merge 9 commits into
Conversation
Registering 0xb151 short-circuits the interpreter, so no compiled CALLVALUE guard runs and revm commits the journaled value transfer when the call succeeds. The dispatcher forwarded only calldata and gas, so a successful blsVerify call with attached value stranded the wei at an address with no outflow path. Gate the entrypoint on zero call value, mirroring the payability gate PR #1201 added to the TEL dispatcher (blsVerify is pure, so the payable list is empty), and add a value-bearing regression test with a zero-value control. Closes #1286. Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
MavenRain
requested a deployment
to
merge-into-main
August 27, 2026 16:33 — with
GitHub Actions
Waiting
MavenRain
requested a deployment
to
merge-into-main
August 27, 2026 16:34 — with
GitHub Actions
Waiting
MavenRain
requested a deployment
to
merge-into-main
August 27, 2026 16:34 — with
GitHub Actions
Waiting
…le-payability-guard Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
…le-payability-guard Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
…le-payability-guard Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
…le-payability-guard Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
…le-payability-guard Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
…le-payability-guard
…le-payability-guard
…le-payability-guard Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
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.
Closes #1286. (Cantina #21)
Problem
Registering an address as a precompile short-circuits the interpreter, so the
CALLVALUEguard a compiled non-payable dispatcher would run never executes (#1190). On the pinnedrevm-handler15.0.0 the caller-to-target balance transfer is journaled before the precompile runs and commits when it succeeds. The BLS precompile entrypoint at0xb151forwarded onlyinput.dataandinput.gas, so a successfulblsVerifycall with attached value both returned the verification result and permanently credited0xb151.Nothing reads or pays out that balance: no selector draws from it, and the genesis account carries only the
INVALID-opcode marker bytecode. Unlike0x7e1there is not even a governanceburnto destroy stray wei, so value that lands there is inert forever. A bare value send already reverts on the short-calldata check; a value-bearingblsVerifycall was the inconsistent survivor. PR #1201 closed the same gap for the TEL dispatcher at0x7e1; this closes the remaining BLS half of the Cantina finding.Fix
crates/tn-reth/src/evm/bls_precompile/mod.rs: reject nonzeroPrecompileInput::valueat the top ofbls_precompile, before dispatch.blsVerifyis pure, so the payable list is empty. The gate keeps fix(tn-reth): enforce payability in the TEL precompile dispatcher #1201's fail-safe classification: a selector added later rejects value unless it is explicitly made payable. Indirect frames cannot land wei here either way (DELEGATECALLvalue is apparent only;CALLCODEtransfers its explicit operand from the executing contract to itself), and rejecting them mirrors a compiled non-payable dispatcher, whoseCALLVALUEcheck fires in those frames too. The rejection surfaces as a precompile-error halt, the same mechanism as every other error from the dispatcher and identical to the TEL gate's.add_bls_precompilepinningDynPrecompile::new_statefulas load-bearing for this gate: reth's engine-tree precompile cache keys entries on calldata alone and caches only pure precompiles, sonew_stateful(is_pure() == false) is what guarantees a cached zero-valueOkcan never be replayed for a value-bearing call with identical calldata.# Payabilitysection on the entrypoint.Scope note: the TEL dispatcher's other two gates do not transfer here.
blsVerifytouches no state, sois_staticneeds no check, and the direct-call guard (#1210) protects caller-based authorization this precompile does not perform. Payability was the one missing gate.Testing
test_value_bearing_verify_rejected(tests/it/bls_precompile_props.rs): a value-bearingblsVerifycall through the real EVM path must halt withHaltReason::PrecompileErrorspecifically (so a harness-level nonce/balance/gas failure cannot satisfy the test by accident),0xb151's balance stays zero, and the caller keeps the wei; a zero-value control with identical calldata verifies. Confirmed by mutation: with the guard reverted the test fails (the call succeeds), with the guard restored it passes.cargo test -p tn-reth --test it bls_precompile_propsgreen under the pinned toolchain. No existing test sends nonzero value to0xb151, so no behavior change for the existing suite.cargo fmt --all -- --checkgreen; scopedcargo clippy --no-depsover the buildplan-emitted crate list. The one pre-existing failure on main (the main fails cargo check --workspace --all-targets: eth_syncing test calls get_rpc_server without the #1235 BaseFeeContainer argument #1283 E0061 in theenv/rpc.rsunit tests, fixed by open PR fix(tn-reth): pass the base-fee argument in the eth_syncing rpc test (#1283) #1285) is untouched by this diff; thetn-rethlib andittest targets were linted as separate invocations so that failure could not mask them.revm/alloy-evmsources (journal/commit path,STATICCALL/DELEGATECALL/CALLCODEinterplay, cache coupling, gas semantics); its findings are folded into the doc comments and the hardened test assertion above.