Skip to content

[runtime]: Introduce pallet-outbound-proofs#732

Merged
Wizdave97 merged 63 commits intomainfrom
dami/zk-proof-indexer-db
Apr 20, 2026
Merged

[runtime]: Introduce pallet-outbound-proofs#732
Wizdave97 merged 63 commits intomainfrom
dami/zk-proof-indexer-db

Conversation

@dharjeezy
Copy link
Copy Markdown
Contributor

@dharjeezy dharjeezy commented Mar 24, 2026

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 poll
and submit to EVM chains via HandlerV2.batchCall().

#711

Comment thread sdk/packages/indexer/src/configs/schema.graphql Outdated
Comment thread sdk/packages/indexer/src/configs/schema.graphql Outdated
Comment thread sdk/packages/indexer/src/configs/schema.graphql Outdated
@dharjeezy dharjeezy changed the title Index ZK consensus proofs in the indexer PostgreSQL database [tesseract]: Index ZK consensus proofs in the indexer PostgreSQL database Mar 24, 2026
@dharjeezy dharjeezy requested a review from seunlanlege March 25, 2026 10:22
Comment thread tesseract/consensus/beefy/src/host.rs Outdated
@dharjeezy dharjeezy marked this pull request as ready for review April 18, 2026 17:11
dharjeezy and others added 8 commits April 18, 2026 18:13
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`.
@seunlanlege seunlanlege marked this pull request as draft April 20, 2026 07:09
@seunlanlege seunlanlege marked this pull request as ready for review April 20, 2026 07:35
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`.
Comment thread modules/pallets/beefy-consensus-proofs/src/lib.rs
Comment thread modules/pallets/beefy-consensus-proofs/src/lib.rs Outdated
Co-authored-by: David Salami <31099392+Wizdave97@users.noreply.github.com>
Comment thread modules/pallets/beefy-consensus-proofs/src/lib.rs Outdated
Comment thread modules/pallets/beefy-consensus-proofs/src/benchmarking.rs Outdated
Comment thread modules/pallets/beefy-consensus-proofs/src/benchmarking.rs Outdated
Comment thread modules/pallets/beefy-consensus-proofs/src/lib.rs Outdated
Comment thread modules/pallets/beefy-consensus-proofs/src/types.rs Outdated
Comment thread modules/pallets/beefy-consensus-proofs/src/types.rs Outdated
…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.
@Wizdave97 Wizdave97 merged commit f2f1881 into main Apr 20, 2026
9 checks passed
@Wizdave97 Wizdave97 deleted the dami/zk-proof-indexer-db branch April 20, 2026 12:01
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.

3 participants