[runtime]: Introduce pallet-outbound-proofs#732
Merged
Conversation
…/beefy-consensus-client
…/beefy-consensus-client # Conflicts: # Cargo.toml # modules/pallets/testsuite/Cargo.toml # modules/pallets/testsuite/src/runtime.rs
verify fraud proof fix tests
verify parachain headers
…/beefy-consensus-client # Conflicts: # modules/pallets/testsuite/Cargo.toml
seunlanlege
reviewed
Mar 24, 2026
seunlanlege
reviewed
Mar 24, 2026
seunlanlege
reviewed
Mar 24, 2026
seunlanlege
reviewed
Mar 25, 2026
The weights file was autogenerated locally but never added to git, so CI failed with E0583 on `pub mod pallet_outbound_proofs;` in weights/mod.rs.
Add real benchmarks for the two extrinsics that were previously missing, using the SP1 Groth16 fixture from SP1BeefyTest.sol. The SCALE-encoded trusted state and wire proof are precomputed (see the ignored helper `dump_sp1_fixture_scale_bytes` in beefy-verifier) and embedded as hex constants so the benchmark stays no_std-friendly (avoids pulling solidity-abi into the wasm runtime build). The fixture encodes a mandatory update (next authority set id advances), which exercises the pallet's mandatory path regardless of host parachain id. Weights still need to be regenerated on the Threadripper box — see bench-outbound-proofs.txt for the commands.
…arks Ran the three benchmarks on the Threadripper (AMD Ryzen Threadripper PRO 5995WX, wasm-execution=compiled, steps=50, repeat=20): submit_proof ~669ms 5r/2w (dominated by SP1 verification) set_proof_reward ~8.7µs 0r/1w set_sp1_vkey_hash ~4.8µs 0r/1w The submit_proof benchmark rewrites the fixture's next_authorities.id to force the mandatory path (avoids HostStateMachine para-id matching under the development genesis preset). SP1 public inputs only commit to authority.keyset_commitment and authority.len, so the proof still verifies.
Renames pallet-outbound-proofs and rewrites it on top of the PR's rebase onto main. Accepts solidity-ABI encoded SP1 BEEFY consensus proofs via authenticated unsigned extrinsics, hands them to pallet-ismp's consensus handler, and rewards submitters from the treasury when a proof advances the proven parachain height past a block with new ISMP dispatches or carries an authority-set rotation. Design notes: - Unsigned extrinsic + SR25519 signature in the payload so the submitter is the reward payee. Replay prevention comes from monotonic state progression (no nonce needed). - A single verify_and_apply helper runs signature check and handle_incoming_message; both validate_unsigned and submit_proof call it so invalid proofs never enter the tx pool. - Tx-pool dedup keeps at most one messaging proof (highest-height wins) and one rotation proof in the pool at a time. - initialize_state dispatches pallet_ismp::create_consensus_client instead of poking IsmpHost directly. - Authority-set id is read straight from pallet-ismp's consensus state rather than mirrored locally. Also refactors evm/abi to no_std so the pallet can reuse its sol! bindings and From impls: #[sol(rpc)] is gated on std only, conversions use core::str::FromStr + alloc, the beefy feature flag is removed, and the unused mmr-primitives / merkle-mountain-range deps are dropped. Wires the pallet into the gargantua runtime at pallet index 90 with a weights stub. Nexus is untouched per plan.
The hook was added for this PR's original reward-on-dispatch design. Now that pallet-beefy-consensus-proofs uses ChildTrieRoot comparison to detect new dispatches, nothing in the tree needs the hook — every runtime set `type OnDispatch = ();`. Remove the trait, the associated type, and the no-op call site in `on_finalize`.
Accepted proofs now land in node-local offchain storage keyed by the identifier relayers care about (set_id for rotation proofs, proven_height for messaging proofs). On-chain we keep only a bounded BTreeMap from that identifier to the block number the proof was accepted in — the key is the offchain-storage lookup, so no per-proof metadata struct is needed. Rotation and messaging are disjoint streams. A proof that rotates the authority set is recorded only on the rotation stream even if it also advances proven height; this matches the validate_unsigned classification (rotation preempts messaging in the pool) and avoids storing the same bytes under two keys. Caps: T::MaxStoredProofs (512 in gargantua) per stream, in both the on-chain map and in offchain storage — when the map reaches capacity we evict the smallest key (oldest entry, monotone) and clear the matching offchain blob so on-chain and offchain stay in lock-step. Public helpers `rotation_offchain_key(set_id)` and `messaging_offchain_key(proven_height)` exposed so relayers can reconstruct keys without copy-pasting concatenation logic.
The helper was deleted when evm/abi gained a proper `impl From<SP1BeefyProof> for Sp1BeefyProof`. Both `dump_sp1_fixture_scale_bytes` and `test_sp1_verify_consensus_accepts_solidity_fixture` now decode the SP1BeefyProof struct directly and `.into()` — fixes `cargo check -p beefy-verifier --tests`.
Wizdave97
reviewed
Apr 20, 2026
Wizdave97
reviewed
Apr 20, 2026
Co-authored-by: David Salami <31099392+Wizdave97@users.noreply.github.com>
Wizdave97
approved these changes
Apr 20, 2026
Wizdave97
reviewed
Apr 20, 2026
Wizdave97
reviewed
Apr 20, 2026
Wizdave97
reviewed
Apr 20, 2026
Wizdave97
reviewed
Apr 20, 2026
Wizdave97
reviewed
Apr 20, 2026
Wizdave97
reviewed
Apr 20, 2026
…k256 The review suggestions used `keccak256` (no underscore), but `sp_io` exports it as `keccak_256`. Also swap blake2_256 → keccak_256 in the benchmark's signature construction and in the two lingering doc comments that still referenced blake2_256, so the pallet, bench, and docs agree on the canonical hash.
SP1Beefy.sol::verifyConsensus uses `abi.decode(proof, (MiniCommitment, PartialBeefyMmrLeaf, ParachainHeader[], bytes))` — a top-level tuple, not a `SP1BeefyProof` struct. My earlier test fix decoded the payload as a struct via `SP1BeefyProof::abi_decode`, which silently misparses (the two encodings differ by an outer offset) and panicked with "Overrun" at runtime. Swap both tests back to `abi_decode_sequence` of the four-tuple and assemble the `Sp1BeefyProof` from parts using the existing `From` impls on `PartialBeefyMmrLeaf` → `MmrLeaf` and `ParachainHeader` → `beefy_verifier_primitives::ParachainHeader`. Matches what the deleted `sp1_beefy_proof_from_solidity` helper used to do.
seunlanlege
approved these changes
Apr 20, 2026
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.
This PR Introduces
pallet-outbound-proofs, a pallet that allows provers to submit ZK BEEFY consensus proofs to Hyperbridge, where they are verified, stored in offchain storage, and made available for outbound relayers to polland submit to EVM chains via
HandlerV2.batchCall().#711