Skip to main content

tx_privacy Circuit

tx_privacy proves a private token transfer: balance conservation, membership of the spent note in an account-set Merkle tree, and an unlinkable nullifier — without revealing balances or the amount. Size: k=14.


What it proves

For the witnesses below, the circuit proves:

  1. commitment_old = Poseidon(balance_old, randomness_old) and commitment_new = Poseidon(balance_new, randomness_new).
  2. balance_old - amount = balance_new — balance conservation.
  3. balance_new ∈ [0, 2^64) — a range check that prevents a field-underflowed balance.
  4. commitment_old is a member of the Merkle tree with root merkle_root.
  5. nullifier = Poseidon(Poseidon(randomness_old, balance_old), NULLIFIER_DOMAIN) — deterministic per spent note, unlinkable to commitment_old, and folded with a domain constant so it never collides with the commitment. The on-chain contract consumes it to prevent double-spends.

Witness

FieldTypeDescription
balance_oldu64Sender balance before the transfer
balance_newu64Sender balance after the transfer
randomness_oldfieldCommitment randomness for balance_old
randomness_newfieldCommitment randomness for balance_new
amountu64Transfer amount; must equal balance_old - balance_new
merkle_siblings[field; 32]Merkle authentication path siblings
merkle_indices[bool; 32]Path direction bits (false = left, true = right)

Arrays are JSON arrays of strings. Run zerostyl-prove info tx_privacy for a full template.

Public inputs

InputDescription
commitment_oldPoseidon(balance_old, randomness_old)
commitment_newPoseidon(balance_new, randomness_new)
merkle_rootAccount-set Merkle root containing commitment_old
nullifierDouble-spend marker (unlinkable to commitment_old)