04 - Sprout and Sapling
1. Why this chapter exists
Sprout is the original Zerocash protocol embedded in Zcash at
launch; it is historically important but largely frozen. Sapling
is the production shielded pool and the locus of almost everything
in
zcash_primitives,
zcash_proofs,
and
zcash_keys.
A reader who cannot state the Spend statement from memory will not
be able to follow the builder code in
zcash_primitives/src/transaction/builder.rs
or audit any change to it. By the end of this chapter you will be
able to map every field of a SpendDescription and OutputDescription
to its mathematical role and locate it in
zcash_primitives/src/transaction/components/sapling.rs.
This chapter is the narrative introduction. For the authoritative symbol-by-symbol reference of every key (), see chapter 23 - The complete key catalog. For the clause-by-clause walk of the Spend and Output circuits with constraint counts, see chapter 24 - Circuits, constraint by constraint.
2. Definitions
Sprout
Definition (Sprout note). The tuple
where is the recipient paying key, is the value in zatoshis, is a uniqueness nonce, and is commitment randomness. The commitment is
Definition (JoinSplit). A constant-shape gadget with 2 inputs, 2 outputs, a public scalar moving from transparent into the shielded side, a public scalar moving the other way, a public Merkle root (anchor), and a public per-JoinSplit signature digest .
Sapling
Definition (Pedersen hash, ). Algebraic hash on Jubjub: pad input bits to a multiple of three, group bits in chunks of 3 then segments of chunks (189 bits per segment), encode each segment as a signed integer via the windowed encoding , multiply each segment by an independent generator derived deterministically from a domain-separation string, and sum.
Definition (Sapling note commitment).
The randomness term makes the commitment perfectly hiding.
Definition (Sapling Merkle hash).
where is the layer index and takes the -coordinate of the resulting Jubjub point modulo the field. The layer-indexed domain separation prevents tree-rotation attacks.
Definition (Sapling key tree).
The full viewing key is sufficient to decrypt any incoming or outgoing-tagged note.
Definition (Sapling nullifier). For a note with commitment , position in the tree, and ,
Invariant (Sapling binding equation). For any valid Sapling bundle,
with . The binding signature certifies that the spender knows , which is feasible only when the equation holds.
3. The code
3.1 Sprout: a one-page tour
The Sprout circuit lives in
zcash_proofs/src/circuit/sprout/mod.rs
(the "hybrid Sprout" implementation re-encoded for Groth16 instead
of the original BCTV14 system). Its top-level types pin the shape:
loading...
The synthesize method enforces, for each input :
- Recompute from the witnessed note.
- Verify a Merkle path of depth 29 from to the public anchor (with the witnessed authentication path).
- Derive the paying key .
- Compute the nullifier .
- Compute the " tag" that binds the JoinSplit to a specific .
For each output it derives a fresh from and indices and recomputes the commitment.
Finally it enforces the balance equation
with each enforced via boolean range constraints.
The PRFs are all "SHA-256 with a tag prefix", for example
where 1110 is a 4-bit tag.
The Sprout-Groth16 proving key is 64 MB
(sprout-groth16.params, SHA-256 in
zcash_proofs/src/lib.rs#L52).
Verification is a single Groth16 pairing equation.
Sprout is closed to new outputs since NU5. The code remains for historical sweeps from old Sprout balances; treat it as legacy.
3.2 Sapling: curves and parameters
-
: the scalar field of BLS12-381, , a 255-bit prime.
-
: the base field of Jubjub. Equal to .
-
Jubjub is a twisted Edwards curve over ,
with a prime-order subgroup of order
-
is the fixed generator of that subgroup.
Why this curve? Twisted Edwards arithmetic is strongly unified (the same formula for addition and doubling), which keeps the in-circuit constraint count small. Because is the SNARK scalar field, a Jubjub scalar mul costs only roughly 750 R1CS constraints per operation.
3.3 The Sapling key tree
From a 32-byte spending key :
-
Derive
where reduces a 64-byte string modulo .
-
Compute the public points
with and distinct fixed generators on Jubjub.
-
Compute the incoming viewing key
where is BLAKE2s with personalisation
"Zcashivk". -
For each 11-byte diversifier , compute , a hash-to-curve into Jubjub. Not every yields a valid prime-order point; if it does not, the diversifier is invalid and skipped. The diversified transmission key is
-
A Sapling payment address is the pair encoded as 43 plaintext bytes then bech32 with HRP
zs.
Diversified addresses follow: each generates infinitely many payment addresses sharing the same viewing key. A wallet can hand out a fresh to every counterparty without revealing the common .
3.4 Spend description (math)
A Sapling SpendDescription is the tuple
- is the value commitment to the spent note's value with fresh randomness .
- is the Merkle root used for membership.
- is the nullifier.
- is the re-randomised spend-authority public key.
- is the Groth16 proof.
- is a RedJubjub signature under over the sighash of the transaction.
The wire format is implemented by read_spend_v4 (Sapling v4
transactions, full per-spend signature and proof) and
read_spend_v5 (Sapling v5 transactions, sigs and proofs are
factored to the end of the bundle):
loading...
The Spend statement (what the circuit enforces): the prover knows such that
- .
- The Merkle path proves is in the tree with root . Special case: if the path check is skipped, allowing "dummy" spends used to mask the input count.
- where .
- (the spender owns this address).
- .
- .
- for known .
- .
Public inputs: .
3.5 Output description (math)
An OutputDescription is
- : value commitment of the new note.
- : the -coordinate of the new note's commitment. (The full commitment is recoverable; only the -coordinate is published to save space.)
- : ephemeral public key for ECDH note encryption.
- : the encrypted note plaintext (recipient, value, , memo).
- : the outgoing ciphertext that lets the sender recover the plaintext using .
- : the Groth16 proof.
The Output statement: the prover knows such that
- and is its -coordinate.
- .
- .
- .
- is a valid prime-order subgroup element (non-zero).
Public inputs: .
3.6 The bundle and binding signature
A Sapling bundle is
The binding equation from Section 2 holds because each is Pedersen and the proofs internally certify well-formedness. The binding signature is a RedJubjub signature over the sighash whose verification key is
If the equation holds, , so the spender holds the secret key to that point. If anything is off by even a single zatoshi or one randomness off, is a random-looking point and the signature cannot be forged. Balance is enforced by a signature whose key is a function of the commitments.
3.7 Groth16 specifics
A Groth16 proof is
Verification given public inputs and verifying key :
The vector is the input key: one point per public input, plus a constant. For Sapling Spend (witness encoding of ); for Sapling Output .
The Sapling trusted setup was performed in two MPC ceremonies in
2017-2018 ("Powers of Tau" then per-circuit). The verifying-key
hashes are hardcoded in
zcash_proofs/src/lib.rs
(SAPLING_SPEND_HASH, SAPLING_OUTPUT_HASH). The wallet downloads
the proving keys with download-params and verifies them by
SHA-256.
3.8 End-to-end
To spend from a note and create a new output of value (plus a change output) with fee , a Sapling transaction:
- Picks anchor from a recent block.
- Constructs SpendDescriptions for each input, sampling and , generating the Groth16 proof and the spend-auth signature.
- Constructs OutputDescriptions, sampling fresh , , and , encrypting the note plaintext.
- Sets
$v_{\text{bal}} = v_{\text{in}} - v_{\text{out}}
- v_{\text{change}}$ (sign convention from the spec).
- Computes under the implicit key
$\mathsf{bvk} = \sum \mathsf{cv}_i^{\text{in}}
- \sum \mathsf{cv}j^{\text{out}} - [v{\text{bal}}] V$.
A node verifies each proof against its , verifies the spend-auth signatures, verifies the binding signature, and checks nullifier non-membership.
The Sapling protocol implementation moved out of this workspace
into the
sapling-crypto crate.
What remains here:
zcash_primitives/src/transaction/components/sapling.rs: serialization, theBundle<A, Amount>type, authorisation states.zcash_primitives/src/transaction/builder.rs: the high-level Sapling builder shim that delegates intosapling_builderfromsapling-crypto.zcash_proofs/src/lib.rs: parameter loading, verifying-key hashes, prover bindings.zcash_keys/src/keys.rs: spending-key derivation glue, much delegated tozip32andsapling-crypto::zip32.
4. Failure modes
- Sprout counterfeiting CVE-2019-7167. The original Sprout proving system was BCTV14. A soundness flaw in BCTV14 allowed a prover to forge a JoinSplit proof under a weaker assumption. Mitigation: migration to Groth16 with a fresh MPC and the "hybrid Sprout" wrapper that this repo still ships. See chapter 12 for the full timeline and the ECC remediation post. Any change to the Sprout circuit must preserve the boolean range constraints; removing them silently restores the BCTV14 failure mode at the application layer.
- Sapling
InternalHissue andcm-vs-cm^uconfusion. Early Sapling implementations conflated the full commitment with its extracted -coordinate. Any code change that publishes a full where the spec asks for (or the reverse) leaks information and breaks downstream wallets. - Dummy-spend value drift. A Sapling bundle may include dummy spends with to mask the input count. Builders must enforce for dummies; non-zero dummies silently corrupt and break the binding signature.
- Wrong reduction. reduces a 64-byte string modulo . Substituting a 32-byte truncation biases the key distribution and silently breaks unlinkability proofs.
- Re-randomisation reuse. Each spend must sample a fresh . Reusing across two spends links their to the same underlying , defeating the entire point of RedJubjub re-randomisation.
Tests under
zcash_primitives/src/transaction/tests.rs
exercise the full v4 / v5 serialization round-trip; the
sapling-crypto crate carries the circuit-level tests for the
Spend and Output statements.
5. Spec pointers
- Zcash Protocol Specification, section 4 (Abstract Protocol): the high-level Sapling protocol definitions cited throughout this chapter.
- Zcash Protocol Specification, section 5 (Concrete Protocol): the concrete formulas for , , , and the key tree.
- Zcash Protocol Specification, section 7 (Encodings):
the wire-format encoding that
read_spend_v4andread_spend_v5implement. - ZIP 32: HD derivation for the spending key that seeds the key tree above.
- Ben-Sasson et al., Zerocash, IEEE S&P 2014: the Sprout-era protocol. Background only; Sapling diverges.
- Groth, 2016: the Groth16 paper. Read section 3 for the pairing equation cited in Section 3.7.
6. Exercises
- Map a field. Open
SpendDescriptionand identify the source line of each component of the tuple . - Predict the dispatch. For a v4 transaction, which of
read_spend_v4orread_spend_v5does the parser call? What about a v5 transaction? Cite the call site inzcash_primitives/src/transaction/components/sapling.rs. - Modify and test. In a checkout, add a unit test under
zcash_primitivesthat constructs aSpendDescriptionwith a deliberately wrong (e.g. negate it) and confirms that the spend-auth signature verification fails. The test should pass (i.e. the assertion that verification returns an error must hold). Cite the public verification entry point from theredjubjubcrate as your reference.
Answers in the code
- Sprout JoinSplit shape:
zcash_proofs/src/circuit/sprout/mod.rs#L25-L54. - Sapling Spend v4/v5 readers:
zcash_primitives/src/transaction/components/sapling.rs#L168-L213. - Sapling proving / verifying key hashes:
zcash_proofs/src/lib.rs#L40-L52.
7. Further reading
- chapter 05: Orchard and Halo 2, which replace the trusted-setup Groth16 stack with a transparent IPA-based system.
- chapter 16: the windowed encoding for Pedersen hashes, in-circuit cost, generator derivation.
- chapter 24: clause-by-clause walk of the Spend and Output circuits with constraint counts.
- Hopwood, Bowe, Hornby, Wilcox. Sapling design notes.