Privacy design

Profile A — Shielded UTXO pool

The full-privacy value model: notes as Poseidon commitments, a 2-in/2-out join-split transaction circuit, value conservation in the field, and double-spend protection by nullifier.

Profile A follows the UTXO model (after Tornado Nova): arbitrary amounts, join-split transfers. A single transaction circuit covers deposit, private transfer, and withdraw via a signed external amount.

Notes & keys

NameDefinition
privKeyrandom F_p
pubKeyH(privKey)
note commitmentH(amount, pubKey, blinding)
signatureH(privKey, commitment, merklePathIndices)
nullifierH(commitment, merklePathIndices, signature)

blinding is a random F_p per note. amount is constrained to 248 bits to prevent field overflow during summation.

The transaction circuit

shielded/transaction.circom, instantiated 2-in / 2-out. Public signals, in order:

text
[ root, publicAmount, extDataHash,
  inputNullifier[nIns], outputCommitment[nOuts] ]

Private inputs: per-input inAmount, inPrivateKey, inBlinding, inPathIndices, inPathElements[LEVELS]; per-output outAmount, outPubkey, outBlinding.

Constraints

  1. For each input: recompute commitment, signature, nullifier; require nullifier === inputNullifier[i].
  2. For each input with non-zero amount: MerkleProof(commitment, …) === root (zero-amount inputs are dummies and skip the root check).
  3. For each output: recompute commitment; require === outputCommitment[i]; amount fits in 248 bits.
  4. All input nullifiers are pairwise distinct.
  5. Value conservation: Σ inAmount + publicAmount === Σ outAmount (mod p).
  6. extDataHash is bound (squared) — committing to recipient, relayer, fee, and the encrypted output ciphertexts the contract emits for note discovery.

publicAmount — one circuit, three actions

The contract computes publicAmount = (extAmount − fee) mod p. The sign of extAmount selects the action:

extAmountActionEffect
> 0Depositcaller sends extAmount
< 0Withdrawalcontract pays −extAmount to recipient
= 0Private transfervalue stays inside the shielded set

Spending & double-spend protection

On transact, the contract verifies the proof against a known root, checks each inputNullifier is unused and marks it spent, inserts each outputCommitment into the tree, emits the encrypted outputs, and settles publicAmount against the bridge/escrow.

Build these proofs with the SDK — see SDK: Shielded UTXO.