Summary
The join–split circuit validates that up to six input notes are spent correctly into up to six output notes. It proves knowledge of Merkle paths, correct nullifier derivation, and that the total input value matches the total output value.
Template parameters
The circuit is instantiated with fixed parameters for the deployed version:
- nIns = 6. Maximum number of input notes per transaction.
- nOuts = 6. Maximum number of output notes per transaction.
- depth = 26. Merkle tree depth, matching the on-chain tree capacity.
Witness (private) inputs
These inputs remain private and are never revealed on-chain. They encode note contents and Merkle paths.
signal input inBalance[6]; // Input note amounts signal input inSpendNonce[6]; // Per-note spend nonces signal input inNoteNonce[6]; // Commitment nonces for inputs signal input receiverViewPriv; // Secret key used to derive nullifiers signal input inPathElements[6][26]; // Merkle sibling hashes for inputs signal input inPathIndex[6][26]; // Merkle path directions (0 = left, 1 = right) signal input outAmount[6]; // Output note amounts signal input outNoteNonce[6]; // Commitment nonces for outputs
Public inputs
These inputs are provided to the on-chain verifier to validate the proof against the current state.
1. merkleRoot // Global Merkle root 2-7. inputNullifier[6] // Nullifiers of spent notes 8-11. destLimbs[4] // Recipient address, as 4 × u64 limbs 12-17. outputCommitment[6] // Commitments of new output notes 18. publicAmount // Publicly withdrawn amount 19. extAmountIn // Publicly deposited amount
Address packing
Solana addresses are 32 bytes. The circuit packs them into four 64-bit limbs to simplify range checks and avoid variable-length byte processing inside the field arithmetic.