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:
commitment_old = Poseidon(balance_old, randomness_old)andcommitment_new = Poseidon(balance_new, randomness_new).balance_old - amount = balance_new— balance conservation.balance_new ∈ [0, 2^64)— a range check that prevents a field-underflowed balance.commitment_oldis a member of the Merkle tree with rootmerkle_root.nullifier = Poseidon(Poseidon(randomness_old, balance_old), NULLIFIER_DOMAIN)— deterministic per spent note, unlinkable tocommitment_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
| Field | Type | Description |
|---|---|---|
balance_old | u64 | Sender balance before the transfer |
balance_new | u64 | Sender balance after the transfer |
randomness_old | field | Commitment randomness for balance_old |
randomness_new | field | Commitment randomness for balance_new |
amount | u64 | Transfer 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
| Input | Description |
|---|---|
commitment_old | Poseidon(balance_old, randomness_old) |
commitment_new | Poseidon(balance_new, randomness_new) |
merkle_root | Account-set Merkle root containing commitment_old |
nullifier | Double-spend marker (unlinkable to commitment_old) |