Skip to content

ZK Architecture

SIP uses Noir as the primary ZK circuit language for zero-knowledge proofs.

SIP requires ZK proofs for three core operations:

  1. Funding Proof - Prove sufficient funds without revealing balance
  2. Validity Proof - Prove intent authorization without revealing sender
  3. Fulfillment Proof - Prove correct execution without revealing path

Noir provides the best balance of:

  • Developer experience (Rust-like syntax)
  • Backend flexibility (UltraPlonk, Groth16, Halo2)
  • Universal setup (no per-circuit trusted setup)
  • Rich standard library
CriteriaCircomNoirHalo2
Security9/107/107/10
DX5/109/104/10
Performance9/107/108/10
Trust Assumptions5/108/1010/10
Weighted Score7.057.756.55

Proves user has sufficient balance without revealing actual amount.

fn funding_proof(
// Public inputs
commitment_hash: pub Field,
minimum_amount: pub u64,
// Private inputs
actual_balance: u64,
blinding_factor: Field,
) {
// Prove balance is sufficient
assert(actual_balance >= minimum_amount);
// Prove commitment is correctly formed
let computed = pedersen_commit(
actual_balance as Field,
blinding_factor
);
assert(commitment_hash(computed) == commitment_hash);
}

Proves intent is authorized without revealing sender.

fn validity_proof(
// Public inputs
intent_hash: pub Field,
sender_commitment: pub Field,
// Private inputs
sender_address: Field,
signature: [u8; 64],
blinding: Field,
) {
// Verify signature
let is_valid = ecdsa_verify(
sender_address,
intent_hash,
signature
);
assert(is_valid);
// Verify sender commitment
let address_hash = poseidon::hash_1([sender_address]);
let computed = pedersen_commit(address_hash, blinding);
assert(commitment_hash(computed) == sender_commitment);
}

Proves solver correctly executed the swap.

fn fulfillment_proof(
// Public inputs
intent_id: pub Field,
output_commitment: pub Field,
min_output: pub u64,
recipient_stealth: pub Field,
// Private inputs
output_amount: u64,
output_blinding: Field,
tx_hash: Field,
) {
// Prove output meets minimum
assert(output_amount >= min_output);
// Prove commitment correctness
let computed = pedersen_commit(
output_amount as Field,
output_blinding
);
assert(commitment_hash(computed) == output_commitment);
// Prove delivery to stealth address
// (binding to tx_hash proves execution)
}

Noir provides built-in support for:

PrimitiveUsage
Pedersen hashCommitment scheme
SHA256Compatibility
PoseidonField-efficient hashing
ECDSAWallet signatures
EdDSAAlternative signatures
SchnorrStealth derivation
flowchart TB
    subgraph SDK["SDK (TypeScript)"]
        subgraph PP["ProofProvider Interface"]
            Mock["MockProvider"]
            Noir["NoirProvider"]
        end
    end

    SDK --> TestPath
    SDK --> ProdPath

    subgraph TestPath["Testing Path"]
        MockProofs["Mock Proofs<br/>(Testing)"]
    end

    subgraph ProdPath["Production Path"]
        NoirJS["Noir / NoirJS<br/>(Production)"]
    end

    NoirJS --> UP["UltraPlonk Backend"]
    NoirJS --> G16["Groth16 Backend"]

    style SDK fill:#312e81,stroke:#8b5cf6,stroke-width:2px
    style PP fill:#4c1d95,stroke:#a78bfa
    style TestPath fill:#1e1b4b,stroke:#8b5cf6
    style ProdPath fill:#4c1d95,stroke:#a78bfa,stroke-width:2px
    style UP fill:#22c55e,stroke:#86efac
    style G16 fill:#22c55e,stroke:#86efac
interface ProofProvider {
generateFundingProof(params: FundingProofParams): Promise<ProofResult>
generateValidityProof(params: ValidityProofParams): Promise<ProofResult>
generateFulfillmentProof(params: FulfillmentProofParams): Promise<ProofResult>
verifyProof(proof: ZKProof, publicInputs: Field[]): Promise<boolean>
}

For testing and development:

const sip = new SIP({
network: 'testnet',
proofProvider: new MockProofProvider()
})

For production:

const sip = new SIP({
network: 'mainnet',
proofProvider: new NoirProofProvider({
wasmPath: '/circuits/funding.wasm',
circuitPath: '/circuits/'
})
})
OperationMockNoir (estimated)
Funding proof gen<1ms2-5s
Validity proof gen<1ms2-5s
Fulfillment proof gen<1ms2-5s
Verification<1ms~10ms

NoirJS enables client-side proof generation:

import { compile, createProver } from '@noir-lang/noir_js'
// Load circuit
const circuit = await compile('/circuits/funding.nr')
const prover = await createProver(circuit)
// Generate proof in browser
const proof = await prover.prove({
actual_balance: 1000n,
blinding_factor: randomField(),
commitment_hash: commitmentHash,
minimum_amount: 500n
})
RiskMitigation
Less battle-testedSimple circuits, formal verification
Performance gapsStart with UltraPlonk, can switch
Smaller ecosystemCore primitives well-supported
  • MockProofProvider for testing
  • Circuit specifications defined
  • ProofProvider interface stable
  • Noir circuit implementation
  • NoirJS integration
  • Browser proof generation
  • Recursive proofs for batching
  • Alternative backends
  • Formal verification