Notes and Nullifiers
The pool keeps its books the way Zcash’s Orchard does: notes and nullifiers. The hashes are
different. Cheshire’s proofs work over the M31 field (numbers modulo 2³¹ − 1), so it uses
Poseidon2, a hash built to be cheap inside such proofs. Below, H(a, b, …) means one
Poseidon2 hash of the inputs, with a domain tag in front so that hashes for different
purposes never collide.
A wallet has one secret, sk: 8 field elements (about 248 bits), derived from the
wallet’s twelve words. Its public key is pk = H(sk). A wallet’s address (its payment
code, tchs1… on test networks) carries pk and a second key used to find payments
(see payment codes).
A note is four values: (amount, pk, rho, r).
amountis in satoshis, below 2²⁷ (about 1.34 BTC). A larger balance is several notes.pkis the owner’s public key.rhoandrare random-looking values that make every note unique and hide it.
The note’s key and commitment are
K = H(pk, rho, r)C = H(amount, K)C is what goes into the tree. Nobody can tell from C what the amount is or whose it is.
The owner, who knows (amount, rho, r), can recompute C and find it.
The notes tree is a Merkle tree of depth 48. It only grows: each new note goes in at the next free position, and that position is part of the pool’s state. There is room for 2⁴⁸ notes, so block space, not the tree, is the limit.
Nullifiers
Section titled “Nullifiers”Spending the note at position i publishes its nullifier:
nf = H(sk, rho, i)- Only the owner can compute it, because it needs
sk. - It reveals nothing about which note it is.
- It is the same every time for the same note, so a second spend is caught.
The position is in the hash for a reason. The payer chooses rho. Without the position,
a malicious payer could send you two notes with the same rho; spending one would make the
other unspendable. Zcash calls this “faerie gold”. Positions are unique, so nullifiers are.
The nullifier tree is an indexed Merkle tree: a sorted linked list in Merkle form.
Each leaf holds a nullifier key and the next larger one. To add nf, the proof shows the
leaf just below nf, whose “next” is just above it. That proves nf is not there yet.
Then it updates that leaf and appends the new one. The key stored is the first 3 field
elements of nf, about 93 bits.
Two in, two out
Section titled “Two in, two out”Every contribution has the same shape: it spends up to 2 notes and creates exactly 2.
- A payment spends one or two of your notes, and creates one note for the payee and one change note for you.
- A deposit spends nothing and creates your new note.
- A withdrawal spends your notes and pays out to a Bitcoin address, with change.
Unused input slots spend a dummy (a nullifier that marks nothing real), and unused outputs are zero-value notes. So an observer cannot tell a one-input payment from a two-input one. This is count hiding. Orchard shows how many actions a transaction has; Cheshire does not.
What one proof says
Section titled “What one proof says”Your wallet’s proof for a contribution says, without revealing any of the values:
- each real input note is in the notes tree under a recent root (an anchor);
- you know its
sk; - its nullifier is correctly formed;
- inputs plus any deposit equal outputs plus any payout plus the fee;
- every amount is in range, so sums cannot wrap around the field.
That is the whole statement. The pool checks it through the recursion described in the proof.
Anchors
Section titled “Anchors”Your wallet proves against the notes root it last saw. By the time the contribution settles, other settlements may have moved the root. So the pool accepts a contribution anchored to any of the last 16 settlement roots. A contribution older than that must be proved again against a fresh root.
Capacity, in numbers
Section titled “Capacity, in numbers”| tree depth | 48 (2⁴⁸ positions per tree) |
| amount per note | below 2²⁷ sat |
| nullifier key | 3 field elements, ~93 bits |
| anchor window | 16 settlements |
A nullifier-key collision would make the later note unspendable: a freeze, not new money. By calculation, the chance of an accidental one is about 5·10⁻⁵ after 10¹² nullifiers, and forcing one on purpose costs about 2⁹³ work. These are calculations, not measurements.