A circuit-agnostic verifier for Noir proofs on Solana, using Solana's native BN254 syscalls.
This project enables verification of Noir zero-knowledge proofs on Solana. It targets Noir's Barretenberg backend (UltraHonk) and uses Solana's alt_bn128 syscalls for efficient on-chain verification.
- Circuit-agnostic: One deployed verifier supports ANY UltraHonk circuit
- VK as account: Upload your VK once, reuse for all proofs
- CLI & SDKs: Easy integration via Rust CLI, TypeScript SDK, or Rust SDK
- Verification receipts: On-chain proof that verification succeeded (for CPI)
- Native syscalls: Uses
solana-bn254for BN254 curve operations
| Tool | Version | Notes |
|---|---|---|
| Noir (nargo) | 1.0.0-beta.8 | UltraHonk/Keccak support |
| Barretenberg (bb) | 0.87.x | Auto-installed by bbup |
| Rust | 1.75+ | Stable |
| Solana SDK | 3.0+ | BN254 syscalls |
Important: bbup auto-detects your nargo version and installs the compatible bb. Install nargo first:
# Install Noir (pinned version)
curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash
noirup -v 1.0.0-beta.8
# Install Barretenberg (auto-detects compatible version)
curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/next/barretenberg/bbup/install | bash
bbup
# Install Solana CLI
sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)"cargo install --path crates/rust-sdk --features cli# 1. Start local validator
surfpool # or: solana-test-validator
# 2. Deploy verifier (one-time, circuit-agnostic)
noir-solana deploy --network localnet
# β Program ID: 7sfMWfVs6P1ACjouyvRwWHjiAj6AsFkYARP2v9RBSSoe
# 3. Upload your circuit's VK
noir-solana upload-vk \
--vk ./target/keccak/vk \
--program-id <PROGRAM_ID> \
--network localnet
# β VK Account: 3WzRvunVbZMFwroHGSi9kEcwPhWyreFM4FNrdmF9TAmd
# 4. Verify a proof
noir-solana verify \
--proof ./target/keccak/proof \
--public-inputs ./target/keccak/public_inputs \
--vk-account <VK_ACCOUNT> \
--program-id <PROGRAM_ID> \
--network localnet
# β β
Proof verified successfully!noir-solana deploy # Deploy verifier program
noir-solana upload-vk # Upload VK to account
noir-solana verify # Verify a proof (full E2E)
noir-solana status # Check verification state
noir-solana receipt create # Create verification receipt
noir-solana receipt check # Check if receipt exists
noir-solana close # Close accounts, reclaim rentimport { SolanaNoirVerifier } from '@solana-noir-verifier/sdk';
const verifier = new SolanaNoirVerifier(connection, programId);
// Upload VK (once per circuit)
const { vkAccount } = await verifier.uploadVK(payer, vkBytes);
// Verify proof
const result = await verifier.verify(payer, proof, publicInputs, vkAccount);
console.log(`Verified in ${result.totalCUs} CUs`);
// Create receipt for CPI
await verifier.createReceipt(payer, stateAccount, proofAccount, vkAccount, publicInputs);use solana_noir_verifier_sdk::SolanaNoirVerifier;
let verifier = SolanaNoirVerifier::new(rpc_client, program_id);
// Upload VK
let vk_result = verifier.upload_vk(&payer, &vk_bytes).await?;
// Verify proof
let result = verifier.verify(
&payer, &proof, &public_inputs, vk_result.vk_account, None
).await?;
// Create receipt for CPI
verifier.create_receipt(&payer, state, proof_acc, vk_acc, &public_inputs).await?;For Solana programs that need to check if a proof was verified:
use solana_noir_verifier_cpi::{is_verified, get_verified_slot};
// Check if proof was verified
let receipt_pda = derive_receipt_pda(vk_account, &public_inputs, program_id);
if is_verified(&receipt_account)? {
let slot = get_verified_slot(&receipt_account)?;
// Proceed with application logic...
}See examples/sample-integrator/ for a complete example.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β CIRCUIT DEPLOYMENT (once per circuit) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β 1. Create VK account β
β 2. Upload VK (2 chunks for 1,760 bytes) β
β β VK Account pubkey (save for proof verification) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PROOF VERIFICATION (per proof) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β 1. Create proof + state accounts (1 TX) β
β 2. Upload proof (16 chunks in parallel) β
β 3. Run verification phases (8 TXs, ~5.4M CUs) β
β 4. (Optional) Create verification receipt for CPI β
β β Verification result + accounts closed (rent reclaimed) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Solana's 1.4M CU per-transaction limit requires splitting UltraHonk verification across multiple transactions:
| Phase | Description | TXs | CUs |
|---|---|---|---|
| 1 | Challenge generation | 1 | ~287K |
| 2 | Sumcheck (rounds+relations) | 3 | ~3.8M |
| 3 | MSM (weights+fold+gemini) | 3 | ~2.1M |
| 4 | Pairing check | 1 | ~55K |
| Total | 8 | ~5.4M |
State is stored in a verification account between transactions.
| Account | Size | Purpose |
|---|---|---|
| VK Buffer | 1,763 bytes | Header (3) + VK (1,760) |
| Proof Buffer | ~16,261 bytes | Header (9) + PI (32Γn) + Proof |
| State Buffer | 6,408 bytes | Verification state between TXs |
| Mode | Proof Size | VK Size | Use Case |
|---|---|---|---|
| Poseidon2 | ~16 KB | ~3.6 KB | Recursive |
| Keccak + ZK | 16,224 B | 1,760 | Solana β |
Always use --oracle_hash keccak --zk for Solana verification.
Note: bb 0.87 produces fixed-size proofs (16,224 bytes for ZK) regardless of circuit complexity due to CONST_PROOF_SIZE_LOG_N=28 padding.
| Circuit | log_n | Public Inputs | Transactions | CUs |
|---|---|---|---|---|
| simple_square | 12 | 1 | 24 | 5.44M |
| fib_chain_100 | 12 | 1 | 24 | 5.44M |
| iterated_square_100 | 12 | 1 | 24 | 5.44M |
| iterated_square_1000 | 13 | 1 | 25 | 5.70M |
| iterated_square_10k | 14 | 1 | 25 | 5.96M |
| iterated_square_100k | 16 | 1 | 25 | 6.72M |
| hash_batch | 17 | 32 | 26 | 6.99M |
| merkle_membership | 18 | 32 | 26 | 7.25M |
| sapling_spend | 16 | 4 | 25 | 6.49M |
All proofs are 16,224 bytes (fixed size in ZK mode). Verification takes ~10s on localnet.
| Component | Cost |
|---|---|
| Per proof verification | ~$1.10 (8 TXs, priority fees) |
| Circuit deployment (VK upload) | ~$0.003 (one-time) |
| Rent deposits (recoverable) | ~$33 per proof |
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Install Noir and Barretenberg (see Version Compatibility above)
noirup -v 1.0.0-beta.8
bbup# Build all test circuits first (generates proofs + VKs)
cd test-circuits && ./build_all.sh && cd ..
# Run all tests (58+ passing)
cargo test
# Core library tests only
cargo test -p plonk-solana-core
# Build the verifier program
cd programs/ultrahonk-verifier && cargo build-sbfcd test-circuits/simple_square
# Compile and execute
nargo compile && nargo execute
# Generate proof (ALWAYS use keccak + zk for Solana)
~/.bb/bb prove \
-b ./target/simple_square.json \
-w ./target/simple_square.gz \
--oracle_hash keccak --zk \
-o ./target/keccak
# Generate VK
~/.bb/bb write_vk \
-b ./target/simple_square.json \
--oracle_hash keccak \
-o ./target/keccak
# Verify locally (sanity check)
~/.bb/bb verify -p ./target/keccak/proof -k ./target/keccak/vk \
--oracle_hash keccak --zkOr use the helper script:
cd test-circuits && ./build_all.sh simple_square# 1. Start Surfpool (in separate terminal)
surfpool
# 2. Build & deploy
cd programs/ultrahonk-verifier
cargo build-sbf
solana program deploy target/deploy/ultrahonk_verifier.so \
--url http://127.0.0.1:8899 --use-rpc
# β Note the Program ID
# 3. Test with CLI
noir-solana upload-vk --vk ../../test-circuits/simple_square/target/keccak/vk \
--program-id <PROGRAM_ID> --network localnet
noir-solana verify \
--proof ../../test-circuits/simple_square/target/keccak/proof \
--public-inputs ../../test-circuits/simple_square/target/keccak/public_inputs \
--vk-account <VK_ACCOUNT> \
--program-id <PROGRAM_ID> --network localnet
# Or use the JS test script
PROGRAM_ID=<id> node scripts/solana/test_phased.mjssolana-noir-verifier/
βββ crates/
β βββ plonk-core/ # Core verifier library (58 tests)
β β βββ ops.rs # BN254 ops via syscalls
β β βββ transcript.rs # Fiat-Shamir (Keccak256)
β β βββ key.rs # VK parsing (1,760 bytes)
β β βββ proof.rs # Proof parsing (16,224 bytes)
β β βββ sumcheck.rs # Sumcheck protocol
β β βββ relations.rs # 26 subrelations
β β βββ shplemini.rs # Batch opening verification
β β βββ verifier.rs # Main verification logic
β βββ rust-sdk/ # Rust SDK + CLI
β β βββ src/
β β β βββ client.rs # SolanaNoirVerifier
β β β βββ instructions.rs # Instruction builders
β β β βββ bin/noir-solana/ # CLI binary
β β βββ examples/
β β βββ test_phased.rs # E2E example
β βββ verifier-cpi/ # CPI helper for integrators
β βββ vk-codegen/ # VK β Rust constants (legacy)
βββ programs/
β βββ ultrahonk-verifier/ # Main Solana verifier program
β βββ src/
β β βββ lib.rs # Entry point + instructions
β β βββ phased.rs # Verification state machine
β βββ tests/
β βββ integration_test.rs
βββ sdk/ # TypeScript SDK
β βββ src/
β βββ client.ts # SolanaNoirVerifier class
β βββ instructions.ts # Instruction builders
β βββ types.ts # TypeScript interfaces
βββ examples/
β βββ sample-integrator/ # CPI integration example
βββ test-circuits/ # 9 verified test circuits
β βββ simple_square/ # Basic xΒ² = y
β βββ iterated_square_*/ # Scalability tests
β βββ hash_batch/ # Blake3 hashing
β βββ merkle_membership/ # Merkle proofs
β βββ sapling_spend/ # Zcash-style circuit
βββ scripts/
β βββ solana/
β βββ test_phased.mjs # E2E verification script
β βββ verify.mjs # Simple verification
βββ docs/
βββ theory.md # UltraHonk protocol docs
βββ knowledge.md # Implementation notes
βββ bpf-limitations.md # Solana constraints
- BN254 operations via syscalls
- Proof/VK parsing (bb 0.87 format with limbed G1 points)
- Fiat-Shamir transcript (Keccak256)
- All 25 alpha challenges generation
- Gate challenges (CONST_PROOF_SIZE_LOG_N iterations)
- Sumcheck verification (all rounds)
- All 26 subrelations (arithmetic, permutation, lookup, range, elliptic, aux, poseidon)
- Shplemini batch opening verification
- KZG pairing check
- Multi-transaction phased verification
- Zero-copy proof parsing
- VK account support (circuit-agnostic)
- Verification receipts for CPI
- TypeScript SDK
- Rust SDK + CLI
- 9 test circuits verified
| Optimization | Improvement |
|---|---|
| Montgomery multiplication | -87% CUs (7x faster) |
| Batch inversion (sumcheck) | -38% CUs sumcheck |
| Batch inversion (fold denoms) | -60% CUs phase 3b1 |
| FrLimbs in sumcheck | -24% Phase 2 |
| FrLimbs in shplemini | -16% Phase 3 |
| Zero-copy Proof struct | -47% transactions |
| Parallel proof upload | 16 chunks in ~0.8s |
SPEC.md- Detailed specificationtasks.md- Implementation progressdocs/knowledge.md- Implementation notesdocs/theory.md- UltraHonk protocolcrates/rust-sdk/README.md- Rust SDK & CLI docs
- Noir Documentation
- Barretenberg
- groth16-solana - Groth16 verifier pattern
- ultraplonk_verifier - UltraPlonk reference
MIT OR Apache-2.0