SIP Circuits

Live Cryptography Started November 2025 Launched December 2025
Noir Zero-Knowledge Proofs Barretenberg Pedersen Commitments ECDSA

SIP Circuits: When Math Becomes Privacy

Heres the thing about zero-knowledge proofs: everyone talks about them, few people actually build them. Theyre the cryptographic equivalent of "I could totally run a marathon" — easy to claim, hard to execute.

When I built SIP Protocol for the Zypherpunk hackathon, the SDK handled the high-level privacy logic. But underneath? Actual ZK circuits doing actual math. This is the story of those circuits.

The Three Proofs

SIP needs to prove three things without revealing anything:

  1. Funding Proof — "I have enough money" without showing your balance
  2. Validity Proof — "I authorized this intent" without revealing your identity
  3. Fulfillment Proof — "I delivered the swap correctly" without exposing amounts

Each proof is a separate circuit. Each circuit is a carefully constructed set of constraints. Each constraint is math that either passes or fails. No in-between.

Why Noir?

I chose Noir over Circom or Halo2 for one reason: developer experience.

Noir reads like Rust. Variables, functions, structs — familiar patterns. Circom feels like assembly. Halo2 requires a PhD in elliptic curves. When youre building on a hackathon deadline, syntax matters.

fn main(
    balance: Field,
    minimum_required: pub Field,
    commitment_hash: pub Field,
    blinding: Field
) {
    // Prove balance >= minimum without revealing balance
    assert(balance >= minimum_required);

    // Prove we know the preimage of the commitment
    let computed = pedersen_commitment(balance, blinding);
    assert(computed == commitment_hash);
}

Thats the funding proof. Read it once, understand it immediately. Try doing that with R1CS constraints.

Pedersen Commitments Are Magic

The core primitive: Pedersen commitments. You commit to a value without revealing it. Later, you prove properties about that value without opening the commitment.

Commit to your balance: C = balance * G + blinding * H

The commitment C goes on-chain. Your actual balance stays private. The ZK proof shows that the balance behind C is greater than some minimum. Verifiers learn only: "yes, they have enough" — nothing more.

The blinding factor is crucial. Without it, someone could brute-force your balance by trying all possible values. With it, the commitment is perfectly hiding. Information-theoretic security.

Validity Proof: The Hard One

Funding proof was straightforward. Validity proof made me question my career choices.

You need to prove you authorized an intent without revealing your address. This requires:

  • Commitment to sender identity (Pedersen again)
  • Nullifier derivation (prevents double-spending)
  • ECDSA signature verification (proves authorization)
  • All inside ZK constraints

ECDSA in ZK is notoriously expensive. Secp256k1 curve operations explode into thousands of constraints. The validity proof hit 1,113 ACIR opcodes — not huge by ZK standards, but meaningful.

The nullifier is elegant: hash(sendersecret, intenthash). Same secret + same intent = same nullifier. Use an intent twice, the duplicate nullifier exposes you. Privacy with accountability.

Fulfillment Proof: Oracle Games

The final circuit proves a solver correctly fulfilled a swap. This involves:

  • Output amount commitment (hide exact amounts)
  • Stealth address verification (hide recipient)
  • Oracle attestation (trusted price data)
  • Time bounds (prevent stale fulfillment)

Oracles in ZK are weird. You cant query an API from inside a circuit. Instead, the oracle signs attestation data off-chain, and the circuit verifies that signature. The proof confirms: "yes, the oracle attested this price at this time."

Nineteen tests across three circuits. Every edge case covered. What happens with zero balance? Expired timestamps? Invalid signatures? Each test caught bugs that wouldve been exploits in production.

The Compilation Dance

Noir compiles to ACIR (Abstract Circuit Intermediate Representation), then to Barretenberg constraints. The final artifacts are JSON files consumed by the TypeScript SDK.

cd funding_proof && nargo compile
# outputs: target/funding_proof.json

That JSON contains everything: circuit structure, verification key, proving parameters. The SDK loads it, generates proofs client-side, and submits them on-chain. Users never see the complexity.

What I Learned

ZK development is constraint thinking. Every operation has a cost. Comparisons are cheap. Hashing is medium. Signature verification is expensive. You budget constraints like memory.

Noir abstracts the pain but not the fundamentals. You still need to understand fields, curves, and commitment schemes. The language makes it accessible; the cryptography remains deep.

The biggest insight: ZK circuits are functional programming taken to extremes. No side effects. No I/O. Pure computation from inputs to outputs. If it compiles and tests pass, it works. Theres something beautiful about that certainty.


Result: 🏆 Part of Zypherpunk Winner (NEAR Track, $4,000)

Circuits: Funding Proof (972 opcodes) • Validity Proof (1,113 opcodes) • Fulfillment Proof (1,691 opcodes)

Stack: Noir 1.0.0-beta.15, Barretenberg (UltraHonk), Nargo CLI

Tests: 19/19 passing

Links: GitHubSpecs

See also: SIP Protocol (main SDK story)