🗺️Nonce Architecture

Circuit Design and Optimization

The zero-knowledge circuits in Nonce must balance several competing goals: complete privacy guarantees, efficient proof generation, small proof sizes, and low verification costs. Circuit design requires careful optimization because each additional constraint increases proving time and the size of intermediate values during proof generation.

The transfer circuit serves as a representative example. It takes as inputs up to two note commitments to spend, their Merkle paths, the private key, and parameters for up to two output notes. The circuit first verifies Merkle membership by starting with each input commitment and iteratively hashing with path siblings for 32 levels to reconstruct the root. This requires 32 Poseidon hashes per input note. Next, it verifies ownership through signature verification, checking that the provided private key corresponds to each note's public key. Then it computes nullifiers by hashing the commitment with the private key. Finally, it creates output commitments and verifies value conservation.

The total constraint count for this circuit is approximately 85,000 R1CS constraints. The Merkle path verification dominates with about 50,000 constraints (625 constraints per Poseidon hash × 32 levels × 2 input notes). Signature verification adds roughly 25,000 constraints using efficient elliptic curve operations. Nullifier and output commitment computation add another 5,000 constraints. The remaining 5,000 handle value arithmetic and conditional logic.

Optimization techniques include using lookup tables for repeated operations, performing batching where multiple values are hashed together, and using efficient field arithmetic implementations. The circuit compiler optimizes the final R1CS form through constraint reduction passes that eliminate redundant constraints and merge compatible operations.

Smart Contract Architecture

The smart contract system divides functionality across multiple contracts for modularity, upgradeability of non-critical components, and gas optimization. The core PrivacyPool contract is non-upgradeable to ensure privacy guarantees cannot be altered, while adapter contracts use a proxy pattern allowing protocol integrations to be updated as DeFi protocols evolve.

The PrivacyPool contract maintains two critical data structures. First, a mapping from tree index to note commitment stores the Merkle tree leaves. The contract tracks the current number of leaves and computes internal nodes on-demand during verification rather than storing the entire tree on-chain, saving significant storage costs. Second, a mapping from nullifier to boolean tracks whether each nullifier has been used, preventing double-spends.

When a transaction is submitted, the contract first calls the appropriate verifier to check the proof. The Groth16 verification involves computing a pairing equation that takes about 300,000 gas on Base. If verification succeeds, the contract checks that all input nullifiers are unused, marks them as used, and adds output commitments to the tree. The entire operation costs approximately 400,000-500,000 gas depending on the number of inputs and outputs.

Adapter contracts inherit from a base AdapterContract class that handles common functionality like proof verification routing and token transfers. Each specific adapter (SwapAdapter, AaveAdapter, etc.) implements the protocol-specific logic. These adapters have controlled permissions: they can only operate when called as part of a verified transaction, they cannot access notes belonging to other users, and they must return all funds to the privacy pool.

Client-Side Proof Generation

Proof generation happens entirely client-side in the user's browser or mobile app, ensuring private inputs never leave the user's device. The proving process begins by compiling the circuit witness: a vector containing all circuit inputs including private and public values. For a transfer, the witness includes note values, tokens, public keys, blinding factors, the private key, Merkle paths, and output note parameters.

The witness generation phase computes all intermediate circuit values. Starting from inputs, it evaluates each constraint in sequence, computing the wire values that satisfy the R1CS system. This takes approximately 2-3 seconds for a transfer circuit on a modern mobile device. The witness vector for the transfer circuit contains about 120,000 field elements, each being a 254-bit number.

Next comes the actual proof generation using the Groth16 algorithm. The prover computes three curve points A ∈ G₁, B ∈ G₂, C ∈ G₁ through a series of elliptic curve operations. This involves multi-scalar multiplication where the prover computes combinations like Σᵢ wᵢ·pkᵢ where wᵢ are witness values and pkᵢ are proving key elements. For a circuit with 85,000 constraints, this requires about 15-30 seconds on a modern device, with the duration depending heavily on the device's computational power and the quality of the WebAssembly implementation.

The proving algorithm uses the Fast Fourier Transform (FFT) to efficiently evaluate polynomials at the roots of unity. The witness values form coefficient vectors that are transformed into evaluation form through FFT, allowing polynomial multiplication and division to be computed in O(n log n) time rather than O(n²). These polynomial operations are necessary because the R1CS constraints are ultimately encoded as polynomial equations that must be satisfied.

After proof generation completes, the resulting proof consists of just three elliptic curve points totaling about 200 bytes. The client packages this proof with the public inputs (Merkle root, nullifiers, output commitments, any revealed values like swap amounts) and submits the transaction to a relayer. The relayer forwards the transaction to the privacy pool contract, which verifies the proof and executes the operation.

Relayer Network and Transaction Privacy

The relayer network serves as an anonymization layer that prevents correlation of blockchain transactions with user IP addresses. Without relayers, an observer monitoring network traffic could potentially link a user's IP address to their transaction, compromising privacy even though the transaction content remains hidden. The relayer architecture ensures that transactions appear to originate from the relayer's address rather than the user's network location.

Users connect to relayers through Tor or a VPN for additional network-level privacy. The user's client generates a proof and signs a relayer request containing the proof, public inputs, and a fee payment authorization. This request is sent to a randomly selected relayer from the network. The relayer validates the proof locally (to avoid wasting gas on invalid proofs), submits the transaction from their address, and gets reimbursed from the user's shielded balance.

The relayer fee mechanism works through a special fee note included in each transaction. When a user wants to pay a 0.1% fee on a 100 USDC transfer, they create three output notes instead of two: one for the recipient, one change note for themselves, and one fee note for the relayer. The fee note is created with the relayer's public key, allowing only that relayer to spend it. This ensures relayers get paid without requiring them to trust users or vice versa.

Relayer decentralization is critical for censorship resistance and privacy. If all transactions go through a single relayer operator, that operator could selectively censor transactions or build a database correlating users with their transactions. Nonce incentivizes relayer diversity through the fee structure and maintains a registry of relayers with reputation scores. Users' clients automatically select relayers based on uptime, response time, and geographic diversity.

Last updated