Wargo is a privacy-preserving application that verifies if a number is a perfect square without revealing the number itself. It combines the power of Zero-Knowledge Proofs (ZKPs) with Starknet's scalable Layer 2 infrastructure to create a privacy-focused, efficient, and cost-effective application.
This project demonstrates how to:
- Create zero-knowledge circuits using Noir
- Generate and verify proofs using Barretenberg's UltraHonk proving system
- Deploy and interact with Cairo smart contracts on Starknet
- Build a React frontend that interfaces with ZK proofs and blockchain contracts
The project consists of three main components:
The Noir circuit implements the cryptographic logic that:
- Verifies an input is a perfect square using an unconstrained binary search algorithm
- Maintains privacy by keeping the actual input hidden
- Prevents replay attacks by tracking previously used inputs via Poseidon hashes
- Generates nullifiers to prove uniqueness without revealing the input
wargo/circuit/src/main.nr
Two Cairo contracts handle the on-chain verification:
Verifier Contract: Auto-generated by Garaga, this contract implements the UltraKeccakHonk verification algorithm that checks proof validity.
Main Contract: Custom contract that:
- Acts as the entry point for proof verification
- Makes library calls to the verifier contract
- Maintains on-chain state to prevent duplicate submissions
- Validates public keys and nullifiers
wargo/contracts/verifier/src/honk_verifier.cairo
wargo/contracts/main/src/lib.cairo
A user-friendly interface that:
- Allows users to input a number to verify
- Generates a zero-knowledge proof using Noir and Barretenberg
- Connects to Starknet wallets
- Submits the proof to the smart contract for verification
- Displays the verification status
wargo/app/src/App.tsx
- Circuit Language: Noir (v1.0.0-beta.3)
- Proving Backend: Barretenberg UltraHonk (v0.86.0-starknet.1)
- Smart Contract Language: Cairo (Starknet's native language)
- ZK Tooling: Garaga (Cairo verifier generator)
- Frontend: React, TypeScript
- Libraries:
@noir-lang/noir_js
- JavaScript bindings for Noir@aztec/bb.js
- JavaScript interface to Barretenberg@garaga/honk
- Utilities for handling UltraHonk proofsstarknet
- Starknet JavaScript SDK
The circuit verifies that an input is a perfect square by:
- Computing the square root using an unconstrained function
- Verifying that squaring this result equals the original input
- Producing a proof of this verification without revealing the input
The circuit uses Poseidon hash (a ZK-friendly hash function) to:
- Create a public key derived from the secret key
- Generate a unique nullifier for each input to prevent double-counting
- Allow public verification without compromising privacy
This project uses Barretenberg's UltraHonk proving system because:
- It offers faster proving times than UltraPlonk
- It uses less RAM during proof generation
- The higher verification costs are mitigated by Starknet's efficient execution model
- Node.js (>= 16.0.0)
- Noir (v1.0.0-beta.3):
noirup --version 1.0.0-beta.3
- Barretenberg (v0.86.0-starknet.1):
bbup --version 0.86.0-starknet.1
- Garaga CLI (v0.18.0):
pip install garaga==0.18.0
- Starknet CLI (for deployment)
Ensure you have:
- Node.js >= 20
- Python 3.10 (for Garaga)
- Bun (recommended for package management)
-
Install required tools
# Install Bun curl -fsSL https://bun.sh/install | bash # Install Noir (specific version required) curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash noirup --version 1.0.0-beta.3 # Install Barretenberg curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/refs/heads/master/barretenberg/bbup/install | bash bbup --version 0.86.0-starknet.1 # Install Starknet tools curl --proto '=https' --tlsv1.2 -sSf https://sh.starkup.dev | sh # Install Starknet devnet asdf plugin add starknet-devnet asdf install starknet-devnet 0.4.1 # Install Garaga (you may need to use a Python virtual environment) python3.10 -m venv garaga-venv && source garaga-venv/bin/activate pip install garaga==0.18.0
-
Build and prepare the circuit
# Build the Noir circuit cd circuit nargo build # Generate witness from sample inputs in Prover.toml nargo execute witness # Generate verification key bb write_vk --scheme ultra_honk --oracle_hash starknet -b ./target/circuit.json -o ./target
-
Generate and build the verifier contract
# Generate Cairo verifier contract using Garaga cd ../contracts garaga gen --system ultra_starknet_honk --vk ../circuit/target/vk --project-name verifier # Build the verifier contract cd verifier scarb build
-
Start local Starknet devnet
In a separate terminal:
starknet-devnet --accounts=2 --seed=0 --initial-balance=100000000000000000000000
-
Deploy contracts to Starknet
In a new terminal:
# Generate accounts file for deployment curl -s http://localhost:5050/predeployed_accounts | jq '{"alpha-sepolia": {"devnet0": {address: .[0].address, private_key: .[0].private_key, public_key: .[0].public_key, class_hash: "0xe2eb8f5672af4e6a4e8a8f1b44989685e668489b0a25437733756c5a34a1d6", deployed: true, legacy: false, salt: "0x14", type: "open_zeppelin"}}}' > ./contracts/accounts.json # Declare the verifier contract cd contracts sncast declare --contract-name UltraStarknetHonkVerifier # Deploy the verifier contract (update the class hash with result from declare command) sncast deploy --salt 0x00 --class-hash <CLASS_HASH_FROM_DECLARE_STEP>
-
Prepare and run the frontend application
# Copy necessary artifacts cp ./circuit/target/circuit.json ./app/src/assets/circuit.json cp ./circuit/target/vk ./app/src/assets/vk.bin cp ./contracts/target/release/verifier_UltraStarknetHonkVerifier.contract_class.json ./app/src/assets/verifier.json # Update contract address in App.tsx with the deployed contract address # Install app dependencies and run cd app bun install bun run dev
- User enters a secret key and input number (which should be a perfect square)
- The application computes the public key and nullifier using Poseidon hashes
- It generates a zero-knowledge proof that:
- The input is a perfect square
- The public key and nullifier were correctly derived
- The proof and public outputs (public key and nullifier) are sent to the Starknet contract
- The contract verifies the proof and checks that the nullifier hasn't been used before
- If verification succeeds, the nullifier is recorded to prevent reuse
- The circuit is compiled to ACIR (Aztec Circuit Intermediate Representation)
- The ACIR is transformed into an arithmetic circuit compatible with UltraHonk
- Witness generation occurs based on the private and public inputs
- The UltraHonk proving system creates a succinct proof of knowledge
- The proof and public inputs are prepared as calldata for the Starknet contract
- The
MainContract
receives the proof as a serialized array of field elements - It calls the
UltraKeccakHonkVerifier
contract via a library call - The verifier performs complex cryptographic checks including:
- Multi-scalar multiplication (MSM)
- Pairing checks on the BN254 elliptic curve
- Keccak hash function evaluations
- If verification succeeds, the public inputs are returned
- The main contract validates the public key and records the nullifier
- Noir: Provides a developer-friendly language for writing ZK circuits with strong safety guarantees
- UltraHonk: Offers faster proving times than alternative systems, making it suitable for interactive applications
- Starknet: Provides a scalable, low-cost platform for running the verification logic
- Cairo: As Starknet's native language, it offers optimal performance for on-chain verification
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
- Aztec Protocol for Noir and Barretenberg
- Starkware for Starknet and Cairo
- The Garaga team for the verifier generation tools