Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion site/src/data/skills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export const SKILL_CARD_SOURCES: readonly SkillCardSource[] = [
category: "ZK",
title: "ZK Proofs",
description:
"Verify Groth16 proofs on-chain via BLS12-381, with Circom, Noir, and RISC Zero toolchain walkthroughs.",
"Verify Groth16 and UltraHonk proofs on-chain via BLS12-381 and BN254, with Circom, Noir, and RISC Zero toolchain walkthroughs.",
},
{
source: "skills/standards/SKILL.md",
Expand Down
10 changes: 6 additions & 4 deletions skills/standards/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,10 @@ Treat this file as a routing map, not a source of final governance/status truth.
- [CAP-0067](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0067.md): protocol/runtime improvements including asset/event model changes

### Newer and draft crypto/features
- [CAP-0074](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0074.md): BN254 host functions proposal
- [CAP-0075](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0075.md): Poseidon/Poseidon2 proposal
- [CAP-0074](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0074.md): BN254 host functions (G1 add/mul, pairing check) — Final, Protocol 25+
- [CAP-0075](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0075.md): Poseidon/Poseidon2 permutation primitives — Final, Protocol 25+
- [CAP-0079](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0079.md): muxed-address strkey conversion proposal
- [CAP-0080](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0080.md): BN254 G1 MSM, Fr arithmetic, on-curve checks — Implemented, Protocol 26+

Use the CAP preamble status fields as the source of truth for implementation readiness.

Expand Down Expand Up @@ -629,8 +630,9 @@ Always verify CAP status and network support before treating any ZK primitive as

### Protocol & Specifications
- [Protocol upgrades](https://stellar.org/protocol-upgrades) - Upgrade timeline and network context
- [CAP-0074](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0074.md) - BN254 host functions proposal
- [CAP-0075](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0075.md) - Poseidon/Poseidon2 host functions proposal
- [CAP-0074](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0074.md) - BN254 host functions (G1 add/mul, pairing check) — Final, Protocol 25+
- [CAP-0075](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0075.md) - Poseidon/Poseidon2 permutation primitives — Final, Protocol 25+
- [CAP-0080](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0080.md) - BN254 G1 MSM, Fr arithmetic, on-curve checks — Implemented, Protocol 26+

### SDK Documentation
- [Soroban SDK BN254 module](https://docs.rs/soroban-sdk/latest/soroban_sdk/crypto/bn254/) - Verify availability in your pinned SDK version
Expand Down
53 changes: 29 additions & 24 deletions skills/zk-proofs/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: zk-proofs
description: Zero-knowledge proofs and privacy patterns on Stellar. Covers Groth16 verification in smart contracts via BLS12-381 host functions (CAP-0059, available), the BN254 + Poseidon proposals (CAP-0074/0075, status-sensitive), and concrete toolchain walkthroughs for Circom (on-chain verifiable today), Noir, and RISC Zero (attestation pattern until BN254 lands). Use when building privacy-preserving applications, ZK-verifier contracts, or wiring a proving toolchain to Stellar.
description: Zero-knowledge proofs and privacy patterns on Stellar. Covers Groth16 verification in smart contracts via BLS12-381 (CAP-0059, Protocol 22+) and BN254 (CAP-0074, Protocol 25+; MSM and Fr arithmetic via CAP-0080, Protocol 26+) host functions, Poseidon/Poseidon2 permutation primitives (CAP-0075, Protocol 25+), and concrete toolchain walkthroughs for Circom, Noir (UltraHonk verifiable on-chain via a community verifier, Protocol 26+), and RISC Zero (Groth16-wrapped receipts verify natively). Use when building privacy-preserving applications, ZK-verifier contracts, or wiring a proving toolchain to Stellar.
user-invocable: true
argument-hint: "[zk task]"
---
Expand All @@ -9,11 +9,13 @@ argument-hint: "[zk task]"

ZK verification on Stellar. Capability is protocol- and SDK-version dependent — always verify CAP status, network version, and `soroban-sdk` host-function support before relying on a primitive.

> **Last verified against Protocol 27** (mainnet, August 2026). CAP-0074/0075 shipped in Protocol 25 "X-Ray" and CAP-0080 (BN254 MSM, Fr arithmetic, on-curve checks) in Protocol 26 — if a doc tells you BN254 or Poseidon is "proposed", it predates that.

## When to use this skill
- Implementing a Groth16 (or other SNARK) verifier as a Stellar smart contract
- Wiring Circom, Noir, or RISC Zero output to on-chain verification
- Building privacy pools, confidential tokens, or Merkle-tree-backed commitments
- Planning for BN254 / Poseidon availability
- Using the BN254 / Poseidon host functions (Protocol 25+)

## Related skills
- Contract patterns and deployment → `../smart-contracts/development.md`
Expand All @@ -25,22 +27,23 @@ ZK verification on Stellar. Capability is protocol- and SDK-version dependent
| Primitive | CAP | Status |
|-----------|-----|--------|
| BLS12-381 ops (G1/G2 add, mul, MSM, pairing check, hash-to-curve, Fr arithmetic) | [CAP-0059](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0059.md) | **Available** (Protocol 22+) |
| BN254 host functions | [CAP-0074](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0074.md) | Proposed — check current status |
| Poseidon/Poseidon2 hash | [CAP-0075](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0075.md) | Proposed — check current status |
| BN254 base ops (G1 add/mul, pairing check — `crypto::bn254`) | [CAP-0074](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0074.md) | **Available** (Protocol 25+) |
| BN254 G1 MSM, Fr arithmetic (`fr_add/sub/mul/pow/inv`), on-curve checks | [CAP-0080](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0080.md) | **Available** (Protocol 26+) |
| Poseidon/Poseidon2 **permutation** primitives (not complete hash functions) | [CAP-0075](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0075.md) | **Available** (Protocol 25+) |

Before implementation, always confirm:
1. CAP status in the preamble (`Accepted`/`Implemented` vs draft)
2. Target network protocol version ([software versions](https://developers.stellar.org/docs/networks/software-versions))
3. `soroban-sdk` release support for the host functions you need

**The curve decides everything.** BLS12-381 proofs verify natively on-chain today; BN254 proofs (Circom's default, Barretenberg, RISC Zero's Groth16 wrapper) are gated on CAP-0074.
**Both curves have base host functions since Protocol 25** — the curve no longer decides everything. What still matters is the proof *system*: Groth16 verifies with a small contract on either curve, while UltraHonk needs a dedicated in-contract verifier (one exists — see the Noir walkthrough — but it relies on CAP-0080 host functions, so Protocol 26+).

| Toolchain | Proof system | Curve | On-chain on Stellar |
|-----------|--------------|-------|---------------------|
| Circom + snarkjs (`-p bls12381`) | Groth16 | BLS12-381 | ✅ Today, via CAP-0059 |
| Circom + snarkjs (default) | Groth16 | BN254 | Gated on CAP-0074 |
| Noir + Barretenberg | UltraHonk | BN254 | Not yet — attest off-chain verification |
| RISC Zero (STARK → Groth16 wrap) | Groth16 | BN254 | Gated on CAP-0074 — attest meanwhile |
| Circom + snarkjs (`-p bls12381`) | Groth16 | BLS12-381 | ✅ via CAP-0059 (Protocol 22+) |
| Circom + snarkjs (default `bn128`) | Groth16 | BN254 | ✅ via CAP-0074 (Protocol 25+) |
| Noir + Barretenberg | UltraHonk | BN254 | ✅ via CAP-0074 + CAP-0080 (**Protocol 26+**) — needs the [rs-soroban-ultrahonk](https://github.com/NethermindEth/rs-soroban-ultrahonk) verifier contract |
| RISC Zero (STARK → Groth16 wrap) | Groth16 | BN254 | ✅ via CAP-0074 (Protocol 25+) |

## The on-chain verifier (Groth16 over BLS12-381)

Expand Down Expand Up @@ -116,9 +119,9 @@ Point encodings are uncompressed big-endian: `G1Affine` wraps 96 bytes, `G2Affin

In production, wrap this verifier with application logic: fix the `VerificationKey` at deploy time (constructor) instead of taking it as a call argument, and bind proofs to context (see [Pitfalls](#pitfalls)).

## Walkthrough: Circom → on-chain verification (works today)
## Walkthrough: Circom → on-chain verification

Circom supports BLS12-381 as a target field — this makes it the toolchain that verifies natively on Stellar right now.
Circom compiles for either curve. This walkthrough targets BLS12-381 to match the canonical verifier contract above; since Protocol 25 the default `bn128` (BN254) output is equally verifiable via `crypto::bn254` — just make sure circuit curve and verifier contract agree.

```bash
# 1. Circuit
Expand All @@ -133,7 +136,8 @@ template Multiplier() {
component main = Multiplier();
EOF

# 2. Compile for BLS12-381 (NOT the default bn128 — that's gated on CAP-0074)
# 2. Compile for BLS12-381 to match the verifier contract above
# (the default bn128 also verifies on-chain since Protocol 25 — via a BN254 verifier)
circom multiplier.circom --r1cs --wasm -p bls12381

# 3. Trusted setup (powers of tau on bls12-381, then circuit-specific phase 2)
Expand All @@ -156,9 +160,9 @@ Then convert `proof.json` / `verification_key.json` (decimal-string coordinates)

For real applications the per-proof flow is: client proves locally (WASM prover or native), submits `(proof, public_signals)` in a contract invocation, contract verifies + applies policy + updates state.

## Walkthrough: Noir (off-chain verify + attestation, for now)
## Walkthrough: Noir (UltraHonk, on-chain verifiable since Protocol 26)

Noir's standard backend (Barretenberg) produces UltraHonk proofs over BN254 — neither the proof system nor the curve is on-chain verifiable on Stellar today.
Noir's standard backend (Barretenberg) produces UltraHonk proofs over BN254. The curve's base operations are native since Protocol 25 (CAP-0074), and UltraHonk verification runs on-chain through [Nethermind's rs-soroban-ultrahonk](https://github.com/NethermindEth/rs-soroban-ultrahonk) verifier contract, whose host calls include `env.crypto().bn254()`'s `g1_msm`, Fr arithmetic (`fr_add/sub/mul/pow/inv`) and `pairing_check` — the MSM and Fr functions come from CAP-0080, so this verifier needs **Protocol 26+** and will fail to link on a Protocol-25 network. This is the stack OpenZeppelin's [Confidential Tokens developer preview](https://stellar.org/blog/developers/developer-preview-confidential-tokens-on-stellar) runs on (testnet). It is a young, community-maintained verifier: check its audit status and maturity before mainnet use.

```bash
# Local proving workflow
Expand All @@ -174,14 +178,13 @@ bb prove -b target/age_check.json -w target/witness.gz -o target/proof
bb verify -k target/vk -p target/proof # off-chain verification
```

On Stellar, two patterns until the curve/system gap closes:

1. **Attestation oracle**: a verifier service runs `bb verify` (or the Noir JS verifier) off-chain and submits a signed attestation; the contract `require_auth()`s the attester address and applies policy. The trust assumption (the attester) must be explicit and documented — this is *not* trustless ZK, it's a verifiable-computation oracle.
2. **Switch the proving stack for on-chain parts**: express the on-chain-critical statement as a Circom/Groth16-BLS12-381 circuit (walkthrough above) and keep Noir for off-chain components.
Three ways to get a Noir statement on-chain, in order of preference:

Track CAP-0074 (BN254): when implemented, BN254 Groth16 verification becomes possible — but UltraHonk would additionally need a verifier implementation in-contract, so Groth16-based paths will land first.
1. **On-chain UltraHonk verification** (Protocol 26+, CAP-0074 + CAP-0080): deploy or call an [rs-soroban-ultrahonk](https://github.com/NethermindEth/rs-soroban-ultrahonk)-based verifier and submit `(proof, public inputs)` directly — trustless, no oracle.
2. **Switch the proving stack for on-chain parts**: express the on-chain-critical statement as a Circom/Groth16 circuit (walkthrough above) and keep Noir for off-chain components — smaller, battle-tested verifier at the cost of a second toolchain.
3. **Attestation oracle** (fallback for older protocol targets or unaudited-verifier concerns): a service runs `bb verify` off-chain and submits a signed attestation; the contract `require_auth()`s the attester and applies policy. The trust assumption (the attester) must be explicit and documented — this is *not* trustless ZK, it's a verifiable-computation oracle.

## Walkthrough: RISC Zero (same gate, clear path)
## Walkthrough: RISC Zero (Groth16 wrap, on-chain verifiable since Protocol 25)

RISC Zero proves arbitrary Rust execution (zkVM) and can wrap its STARK receipts into a Groth16 proof over BN254 ("stark-to-snark") — small enough for on-chain verification where BN254 is supported.

Expand All @@ -202,21 +205,21 @@ let receipt = prover.prove(env, ELF)?.receipt;
receipt.verify(IMAGE_ID)?; // off-chain verification
```

On Stellar today, use the **attestation pattern** (as with Noir): verify the receipt off-chain — locally or via a proving service — and have an authorized attester submit the journal + attestation to your contract. Once CAP-0074 (BN254) is implemented, the Groth16-wrapped receipt becomes verifiable natively with a BN254 verifier contract mirroring the BLS12-381 one above; the `IMAGE_ID` (which program ran) and journal digest become public inputs. See the [RISC Zero docs](https://dev.risczero.com/api) for the wrapping workflow.
Since Protocol 25 (CAP-0074), the Groth16-wrapped receipt verifies **natively on-chain**: a BN254 verifier contract mirroring the BLS12-381 one above. Its public inputs are *not* the `IMAGE_ID` and journal digest directly — RISC Zero's Groth16 verifier takes five field elements encoding the control root (split into two halves), the receipt-claim digest (also split in two), and the BN254 control ID. The image ID and journal are inputs to reconstructing that receipt claim off-chain (or in contract code) before the pairing check; binding them incorrectly rejects valid seals or proves the wrong statement. See the [RISC Zero docs](https://dev.risczero.com/api) for the wrapping workflow and claim-digest construction. The attestation pattern remains a fallback for STARK-only receipts (no Groth16 wrap) or pre-Protocol-25 targets.

## Architecture patterns

- **Verification gateway**: isolate cryptographic checks in a dedicated verifier contract/module — normalize inputs, verify, emit explicit success/failure events. Smaller audit surface, cleaner upgrades.
- **Policy-and-proof split**: `Verifier` (cryptographic validity) → `Policy` (business/compliance rules) → `Application` (state transition). Each independently testable and upgradeable.
- **Capability gating**: enable ZK flows only where required primitives are confirmed available; keep deterministic fallbacks and document the supported network/protocol matrix.

For Merkle-tree commitments (privacy pools, allowlists): until Poseidon (CAP-0075) lands, in-circuit-friendly hashing on-chain is expensive — design trees so the contract only needs root comparisons and membership proofs verified inside the SNARK.
For Merkle-tree commitments (privacy pools, allowlists): CAP-0075 (Protocol 25+) exposes Poseidon/Poseidon2 **permutations**, not ready-made hash functions. To recompute Poseidon Merkle roots and commitments on-chain you must build the sponge/compression construction in guest code and pass the same field, state size `t`, S-box degree `d`, round counts, MDS/diagonal matrix, and round constants your circuit uses — plus matching padding and domain separation. Mismatched parameters silently produce roots that differ from the circuit's, and an ad-hoc construction can be insecure. On pre-Protocol-25 targets, design trees so the contract only needs root comparisons and membership proofs verified inside the SNARK.

## Pitfalls

- **Verifying the proof but not the statement.** A valid proof only shows *some* witness satisfies the circuit. The contract must validate the public inputs' semantics: who is this proof for, which Merkle root, which action, which amount.
- **Missing anti-replay binding.** Valid proofs can be replayed. Bind a nonce/session/action into the public inputs and persist a replay guard (nullifier set) on-chain.
- **Curve mismatch.** Circom defaults to bn128; on Stellar compile with `-p bls12381` or your proof will be unverifiable on-chain.
- **Curve/verifier mismatch.** The proof's curve must match the verifier contract's host functions: the canonical example verifier is BLS12-381, so a default-`bn128` Circom proof fails against it (and vice versa for a BN254 verifier). Since Protocol 25 both curves work — just keep circuit and verifier on the same one.
- **Trusted-setup hygiene.** Groth16 needs a circuit-specific phase-2 setup; for production use a real multi-party ceremony, not a single-contributor dev setup.
- **Hardcoded protocol assumptions.** Capability-gate; don't assume draft CAPs are live on the target network.

Expand All @@ -230,5 +233,7 @@ For Merkle-tree commitments (privacy pools, allowlists): until Poseidon (CAP-007

- [groth16_verifier example](https://github.com/stellar/soroban-examples/tree/main/groth16_verifier) — canonical verifier + arkworks test fixtures
- [soroban-examples](https://github.com/stellar/soroban-examples)
- [BLS12-381 SDK docs](https://docs.rs/soroban-sdk/latest/soroban_sdk/crypto/bls12_381/index.html)
- [ZK Proofs on Stellar](https://developers.stellar.org/docs/build/apps/zk) · [Privacy on Stellar](https://developers.stellar.org/docs/build/apps/privacy) — official docs
- [rs-soroban-ultrahonk](https://github.com/NethermindEth/rs-soroban-ultrahonk) — UltraHonk/BN254 verifier contract
- [BLS12-381 SDK docs](https://docs.rs/soroban-sdk/latest/soroban_sdk/crypto/bls12_381/index.html) · [BN254 SDK docs](https://docs.rs/soroban-sdk/latest/soroban_sdk/crypto/bn254/index.html)
- [Circom docs](https://docs.circom.io) · [snarkjs](https://github.com/iden3/snarkjs) · [Noir docs](https://noir-lang.org/docs) · [RISC Zero docs](https://dev.risczero.com)
Loading