The Pool Coin
All private value sits in one Bitcoin output: a pay-to-taproot (P2TR) output whose
internal key is a “nothing up my sleeve” point that nobody can sign for. So there is no
key path, and no admin key. The only way to spend the pool coin is to satisfy one of its
script leaves, and every leaf is a version 0xc2 tapscript, the leaf version Great Script
Restoration adds.
A script cannot remember anything. So the state rides along in the transactions:
- Each settlement writes a hash of the new state into a small output called the
caboose: an
OP_RETURNwrapped in a P2WSH script, 330 sat. - The next settlement’s leaf rebuilds the previous transaction from data in its witness, checks that it hashes to the coin it is spending, and so reads the old state, the pool’s value and the epoch counter.
The state is the pool’s roots:
roots = H(notes_root, nullifier_root, next_note, next_nullifier) with the window of the last 16 roots and the payroll queue folded inroots_h = SHA256(roots)(The window lets a wallet prove against a slightly old root. The payroll queue belongs to the payroll layer.)
The pool’s value V is an ordinary amount on an ordinary output. Anyone can read it. The
settlement leaf requires
V' = V + deposits − payouts − feeswhere the three totals come from the proof. fees here is what the miner receives: there
is no fee output, so the difference between inputs and outputs pays the miner, as in any
Bitcoin transaction. An overdraw fails in script.
Bitcoin’s own rule, that a transaction cannot create value, is the outer wall. Whatever a
proof says, the pool can never pay out more than V.
Lineage
Section titled “Lineage”Every leaf checks that the coin it spends descends, through a chain of pool transactions, from one genesis transaction. A coin someone sends to the pool’s address from anywhere else is not the pool and cannot act as it. This matters because deposits come in through an inbox that pays to the pool’s address: without the check, a fake pool coin at the same address could try to claim them.
Epochs and drains
Section titled “Epochs and drains”Each settlement adds one to an epoch counter. Every sixth epoch is a drain: that
settlement must also spend the exit inbox and process every queued exit request (see
exits). The leaf checks this with OP_MOD, so a settlement that skips a drain
is invalid. A drain happens even when no request is waiting. Since 2026-11-06 a drain can
also take the deposit inbox, so waiting deposits’ fees pay for it.
One leaf per shape
Section titled “One leaf per shape”Script has no loops. So the pool has one leaf per transaction shape:
- how many records the settlement publishes (from 1 to the pool’s bound, fixed when the pool is made: 1,750 by default, the prover’s cap);
- what the previous transaction was (the genesis, a settlement or a solo exit);
- whether a deposit inbox is spent;
- whether it is a drain, with or without the deposit inbox;
- whether it migrates notes to a successor pool.
At the default bound a pool has about 21,000 leaves (12 shapes × 1,750 record counts,
plus the exits). A node builds them once when it starts, in parallel (57 s on four shared
cores, measured 2026-11-09), and keeps only their hashes. Only the leaf a settlement uses is
revealed on chain. The leaves sit in a balanced tree, so the proof that a leaf belongs to the
pool grows by one 32-byte hash each time the leaf count doubles: 21,000 leaves make the tree
15 levels deep where a 64-record pool’s 1,687 make it 11, so the full bound costs about
32 vB more per settlement (four hashes, at the witness discount). Every leaf is checked
for forbidden opcodes by the repository’s own BIP-440/441 interpreter as it is built. The
GSR authors’ gsr check tool is run at a 64-record bound, since a leaf for more records
repeats the same per-record block and adds no new opcode sequences. Its last recorded run,
on 2026-11-03, passed 3,364 distinct leaves with no errors, warnings or lints. Retired pool
kinds were removed after that, and the same bound now gives 1,687 distinct leaves; no run on
that set is recorded yet.
A changed leaf is a changed address
Section titled “A changed leaf is a changed address”The pool’s address is a hash of all its leaves. Change one byte of one leaf and you get a different pool. That is why a pool can never be changed in place: new rules mean a new pool, and moving to it is opt-in (see upgrades). It is also why the repository’s refactors are checked to keep every leaf byte-identical.