Merge circuit
A circuit that combines up to eight of one owner's notes into one, written for the EVM port of the pool and not used by the Solana program.
The merge circuit burns two to eight notes of one owner and mints their sum as a single note to the same owner. The Solana shielded pool program has no instruction that verifies it: it compiles in no key for it, and none of its handlers is a merge. The circuit exists for the EVM port of the pool, which has a merge contract.
The diagram below shows what a merge does.
What it proves
The circuit is Merge(8, 10, 8, 1, 4) and has 54,588 non-linear constraints. It proves the following.
The owner is registered. The owner's identity commitment (sometimes called the user commitment), rebuilt from their keys, is in the identity tree.
Two to eight real inputs. Every real input is identity-owned by that owner, real inputs come first, and at least two are real. Address-owned notes cannot be merged.
One token, zone and version. All real inputs share them, because the merged note carries one of each.
No expiring inputs. Every real input has no valid-before slot, and each is past its valid-after slot and in the note tree.
Distinct nullifiers. Each input's nullifier is correctly derived, and all eight are pairwise distinct, so one note cannot be counted twice.
One output. The output note is owned by the same identity, holds the sum of the inputs as a 64-bit amount, has the same token, zone and version, no validity window and no memo, and names the owner as its funder. Its two blinding factors are derived from the owner's outgoing viewing key and the nullifiers, and must be non-zero. The owner can therefore recompute them from its own key and the public nullifiers.
It has 15 public values: the note-tree root, the identity-tree root, the execution slot, the context digest's two halves, the eight nullifiers, the real count and the output commitment.
What it leaves out
A merge has no attester signature, no fee, no deposit or withdrawal lane, no zone signature and no viewing-key sharing: nothing about it is authorised by anyone but the owner. Value never enters or leaves the pool, and the owner and token do not change.
Its nullifiers' absence from the nullifier tree is proven by a companion circuit, nullifier_non_membership_8, the eight-nullifier form of the nullifier-freshness circuit, with 58,849 non-linear constraints. Keeping it separate gives it its own verifying key, so a freshness proof made over three nullifiers can never verify a statement about eight. Both circuits fit a setup of 2^16.
Neither circuit has a test key in the repository or a key compiled into the program. The test-key tool leaves both out on purpose, because no Solana handler can settle a merge. They are documented here because they are in the circuit manifest. On Solana, the way to combine notes is an ordinary private transfer of up to three notes to yourself.
Reference
| Entry | circuits/zk/merge.circom |
|---|---|
| Instantiation | Merge(8, 10, 8, 1, 4) |
| AGGREGATE_SLOT_COUNT | 15 |
| GROUP_CONTEXT | 3 |
| GROUP_NOTE_EFFECTS | 5 |
| GROUP_ROOTS_AND_CLOCK | 0 |
| NULLIFIER_PAIR_COUNT | 28 |
| SLOT_BITS | 48 |
| SLOT_INPUT_NULLIFIER | 5 |
| SLOT_OUTPUT_COMMITMENT | 14 |
| SLOT_REAL_COUNT | 13 |
| nullifierPair | 28 |
circuits/zk/circom-manifest.json.| Entry | circuits/zk/nullifier_non_membership_8.circom |
|---|---|
| Instantiation | NullifierNonMembership(8, 11, 8, 1, 8) |
circuits/zk/circom-manifest.json.Related
Last updated
Fee collection circuit
The circuit that lets the treasury identity collect up to twenty protocol fee notes at a time, what its proof shows, and what the claim_fee_20 handler checks and pays.
Formal verification
What the Lean models of the circuits prove, what they assume, what they show cannot be proved, and what the Kani harnesses and Lean models check on the chain side.