Skip to Content
Technical ReferenceCircuit Specification

Circuit Specification

Overview

Lateo uses a single Circom circuit: policy_tx_2_2 (PolicyTransaction with 2 inputs and 2 outputs).

ParameterValue
Circuitpolicy_tx_2_2
Inputs2 (max UTXOs consumed per transaction)
Outputs2 (max UTXOs created per transaction)
Merkle tree depth10 levels (1,024 max commitments)
ASP membership tree10 levels
ASP non-membership tree10 levels (sparse)
Constraints37,616
Public inputs11
Proving systemGroth16
CurveBN254

Transaction Types

The same circuit handles deposits, withdrawals, and transfers. The publicAmount (ext_amount) determines the type:

TypepublicAmountReal inputsReal outputs
DepositPositive0 (2 dummies)1 real + 1 dummy
WithdrawalNegative1-2 real1 change + 1 dummy
Transfer01-2 real1-2 real

Public Inputs (11 total)

root — Pool Merkle tree root (validates input commitment existence) publicAmount — Net amount entering/leaving the pool (signed) extDataHash — Poseidon2 hash of external data (recipient, amount, encrypted outputs) inputNullifiers[2] — Nullifiers for spent inputs (prevents double-spend) outputCommitments[2] — Commitments for new outputs (inserted into Merkle tree) membershipRoots[1][1] — ASP membership tree root nonMembershipRoots[1][1] — ASP non-membership tree root

Constraints

The circuit enforces:

  1. Balance conservation: sum(input_amounts) + publicAmount = sum(output_amounts)
  2. Commitment validity: commitment = Poseidon2(amount, pubKey, blinding, 0x01)
  3. Nullifier correctness: nullifier = Poseidon2(commitment, pathIndices, privKey, 0x02)
  4. Merkle inclusion: Each input commitment has a valid Merkle path to root
  5. ASP membership: Prover’s leaf is in the membership tree
  6. ASP non-membership: Prover is NOT in the exclusion tree
  7. Signature: Prover knows the private key corresponding to the public key in the commitment
  8. ExtData binding: extDataHash commits to the recipient and encrypted outputs (prevents front-running)

Domain Separators

All Poseidon2 hashes use domain separators to prevent cross-domain attacks:

DomainSeparatorUsage
Commitment0x01Poseidon2(amount, pubKey, blinding, 0x01)
Nullifier0x02Poseidon2(commitment, pathIndices, privKey, 0x02)
Public key0x03Poseidon2(privKey, 0x03)
Signature0x04Poseidon2(commitment, privKey, 0x04)

Proof Format

Groth16 proof = 3 uncompressed BN254 points:

PointSizeDescription
A (G1)64 bytesY-coordinate negated for verification equation
B (G2)128 bytesTwo Fp2 elements (c1 then c0)
C (G1)64 bytesStandard G1 point
Total256 bytesConstant regardless of circuit size
Last updated on