Background: From Zerocoin to Orchard
This page is the bridge between the cryptography literature and
the rest of the course. It explains the lineage that produced
the Orchard shielded protocol, defines the vocabulary (note,
commitment, anchor, nullifier, action) that the later chapters
use without ceremony, and maps each term to the file in
zcash/orchard that owns it.
Read this page first if you have never worked on a shielded protocol before. Skim it if you have, but return to Section 4 whenever a later chapter uses a term you cannot place.
1. Why This Page Exists
zcash/orchard does not justify why it exists, who came before
it, or why every note has both an rho and a psi. Those
facts come from a fifteen-year arc of academic and engineering
work: Zerocoin (2013), Zerocash (2014), Sprout (2016), Sapling
(2018), Orchard (2022). Each generation inherits structure from
the previous one and changes exactly the parts that proved
suboptimal. Without that arc, the constants in src/spec.rs
look arbitrary and the choices in src/circuit.rs look like
trivia.
A small but load-bearing clarification up front: Zcash implements Zerocash, not Zerocoin. Zerocoin (Miers, Garman, Green, Rubin, IEEE S&P 2013; PDF) was the SNARK-free precursor that hid the link between mint and spend using an RSA accumulator, but left amounts and addresses public. Zerocash (Ben-Sasson, Chiesa, Garman, Green, Miers, Tromer, Virza, IEEE S&P 2014; PDF; extended on ePrint 2014/349) is the SNARK-based successor that hides amounts, addresses, and the linkage simultaneously by representing payments as commitments to notes and revealing only deterministic nullifiers on spend. Every Zcash shielded protocol descends from Zerocash; none implement Zerocoin.
2. Definitions
The names below recur in every chapter. We use the notation from the Zcash Protocol Specification Section 4, and we use Orchard's parameter choices throughout. Sapling and Sprout differ in the choice of hash and curve but not in this skeleton.
Definition (Anonymous payment scheme). A consensus protocol that lets a payer transfer value to a payee such that an external observer learns no more than the rules require: typically just that value was conserved and no double-spend occurred. The strongest schemes (Zerocash and its successors) hide the amount, the sender, and the receiver of every shielded transaction.
Definition (Zerocoin). The first proposal for an anonymous extension to Bitcoin-style ledgers (Miers et al., IEEE S&P 2013, PDF). Uses an RSA accumulator over fixed-denomination coins and a double-discrete-log proof of accumulator membership. No SNARK, no trusted setup, but proofs are ~25 KiB and only the link between mint and spend is hidden.
Definition (Zerocash). The zk-SNARK-based successor of Zerocoin (Ben-Sasson et al., IEEE S&P 2014, PDF; ePrint 2014/349). Represents payments as commitments to typed records ("notes") that are appended to a global Merkle tree; spends are proofs of membership in that tree plus a deterministic nullifier that prevents double-spending. Hides amounts, sender, receiver, and linkage. Proof size is ~300 bytes; verification is ~10 ms.
Definition (Shielded pool). The set of notes whose commitments have been published on chain and whose nullifiers have not. A wallet shields by spending transparent value and creating shielded notes; it deshields by spending shielded notes and creating transparent outputs. Each pool (Sprout, Sapling, Orchard) has its own commitment tree and its own nullifier set; consensus tracks all three separately.
Definition (Action). Orchard's atomic ledger primitive. An
Action symmetrically combines one note spend and one note
output, with shared randomisation and a single circuit. Sapling
kept them apart (SpendDescription and OutputDescription);
Orchard fused them, both for circuit efficiency and to make the
on-chain shape less informative (see
Chapter 5 "A Note on the Term Action").
3. The Lineage
3.1 Zerocoin (2013)
| Aspect | Value |
|---|---|
| Crypto | RSA accumulator + double-discrete-log proofs |
| Trusted setup | RSA modulus (later replaced by class groups in follow-up work) |
| What is hidden | Linkage between mint and spend |
| What is not hidden | Amount (fixed denomination), and that a shielded op took place |
| Proof size | ~25 KiB |
| Reference | Miers et al. 2013 |
Zerocoin shipped briefly in Zcoin (now Firo) and inspired several follow-ups (Lelantus, Spark). Zcash does not implement Zerocoin.
3.2 Zerocash (2014)
| Aspect | Value |
|---|---|
| Crypto | Groth16-style zk-SNARK with R1CS arithmetisation |
| Trusted setup | Per-circuit, public; produced via a multi-party computation ceremony |
| What is hidden | Amount, sender, receiver, linkage |
| What is not hidden | The fact a shielded transaction occurred; net value transferred to/from transparent |
| Proof size | ~300 bytes |
| Verification time | < 10 ms |
| Reference | Ben-Sasson et al. 2014, ePrint 2014/349 |
Zerocash is the construction that Zcash inherited and that Sprout, Sapling, and Orchard each refine.
3.3 Sprout (Zcash mainnet, October 2016)
The first deployed shielded pool. A direct implementation of Zerocash, with the JoinSplit transaction format and the parameters in the Zcash Protocol Specification Section 4 (Sprout parts).
| Aspect | Value |
|---|---|
| Curve | alt_bn128 (Barreto-Naehrig, 254-bit, pairing-friendly) |
| Proof system | BCTV14, later Groth16-backport |
| Note commitment hash | SHA-256 truncated |
| Merkle tree hash | SHA-256 PRF |
| Trusted setup | "Sprout MPC", 6 participants |
| Transaction format | JoinSplit (2-in / 2-out per JoinSplit, multiple per tx) |
| Status | Deprecated; new value cannot enter the pool |
| Spec | Zcash Protocol Specification, Section 4 (Sprout sections) |
Sprout is interesting historically; no new code is written against it.
3.4 The JoinSplit Primitive
A small clarification up front, since the two terms are easily
confused: JoinSplit is the transaction primitive used inside
the Sprout pool, not an older name for the pool itself. The
pool is Sprout; each Sprout shielded transaction contains one or
more JoinSplit descriptions. Sapling and Orchard later replaced
this primitive with different shapes (SpendDescription
plus OutputDescription in Sapling, ActionDescription in
Orchard) while keeping the pool concept.
Origin. The construction first appears in the Zerocash 2014
paper as the operation Pour (Section 4.5 of
Ben-Sasson et al.).
Pour is a single zk-SNARK statement that consumes two old
coins and produces two new coins, while allowing some net value
to flow to or from a transparent address in the same step. The
Zcash Protocol Specification
(Section 4, Sprout) and the zcashd reference implementation
(JSDescription in
src/primitives/transaction.h)
call the deployed counterpart of Pour a JoinSplit
description. The 2-in / 2-out shape is the same in both
documents; the same single SNARK statement covers both
shielded-only transfers and the mixed transparent / shielded
cases by setting unused notes or public-value fields to zero.
Shape. A single JoinSplit description on the wire contains:
- and : two 64-bit unsigned integers declaring how much transparent value enters the shielded pool and how much leaves it in this operation.
- An anchor: a recent Sprout note commitment tree root.
- Two input nullifiers (one per consumed note).
- Two output commitments (one per produced note).
- An ephemeral public key plus two ciphertexts encrypting the new notes to their recipients.
- A
randomSeedand two MACsh_1, h_2binding the description to the spending keys, plus ajoinSplitPubKeywhose signature ties the JoinSplits in a transaction together. - The Groth16-style zk-SNARK proof itself.
A Zcash transaction can carry several JoinSplit descriptions back to back; consensus checks each one independently and sums their public-value balances against the transparent inputs and outputs of the same transaction. This is how one Sprout transaction can mix shielding, deshielding, and shielded-to- shielded transfers atomically.
Arity. The Zerocash POUR function is fixed at two old
coins in and two new coins out (Section 4.5 of
Ben-Sasson et al.).
The Sprout JoinSplit description inherits that arity directly,
as documented in the
Zcash Protocol Specification,
Section 4 (Sprout). The redesign for Sapling replaced JoinSplit
with the separable SpendDescription and OutputDescription,
so a transaction can have any mix of inputs and outputs;
see the Sapling sections of the same specification document for
the new layout. This page does not attempt to reconstruct the
engineering rationale for either choice; readers interested in
the design history should consult the protocol specification and
the Sapling paper directly.
On chain. A Sprout transaction wire format encodes each JoinSplit description as a fixed-size record. The full layout and field sizes are documented in the Zcash Protocol Specification, Section 7.2 ("JoinSplit Description").
Code, for reference only. No new code is written against Sprout today, but every full node still parses and validates historical Sprout transactions. The relevant entry points are:
- zcashd (C++).
JSDescriptioninsrc/primitives/transaction.hdefines the wire layout; the JoinSplit verifier interface issrc/zcash/JoinSplit.hppand the underlyinglibzcash::ProofVerifier. Validation is driven fromsrc/main.cppviaCheckTransactionWithoutProofVerificationandContextualCheckTransaction. Browse the generated doxygen at dannywillems.github.io/zcashd, starting atJSDescriptionand following the call sites. - librustzcash (Rust). The wire-format type is
zcash_primitives::transaction::components::sprout::JsDescriptionand the supporting Sprout primitives live inzcash_primitives::sprout. Proof verification is inzcash_proofs::sprout. - Zebra (Rust full node). The pool type and parser are in
zebra_chain::sprout::joinsplit; consensus checks live inzebra-consensus. Browse the rustdoc at dannywillems.github.io/zebra, starting atzebra_chain::sprout::JoinSplitand thezebra_chain::sprout::treemodule. - Circuit. The Sprout circuit was first written against
libsnark(inzcashd'ssrc/zcash/circuit/), then ported tobellmanfor the Groth16 back-port (see ZIP 209 and surrounding ZIPs). Neither circuit sees new development; theorchardcrate intentionally inherits none of this code.
3.5 Sapling (Network Upgrade 1, October 2018)
Sapling redesigned the in-circuit primitives for efficiency and introduced viewing keys and diversified addresses.
| Aspect | Value |
|---|---|
| Curves | BLS12-381 (proving) + Jubjub (in-circuit, twisted Edwards) |
| Proof system | Groth16 |
| Note commitment hash | Pedersen hash on Jubjub |
| Merkle tree hash | Pedersen hash |
| Nullifier PRF | Blake2s |
| Trusted setup | "Sapling MPC", ~90 participants |
| Transaction format | SpendDescription (per input) + OutputDescription (per output) |
| Notable additions | Diversified addresses; full viewing keys; spend-authority signatures (RedJubjub) |
| Implementation | zcash/sapling-crypto |
The three "Notable additions" in the table are the features that distinguish Sapling from Sprout. All three are defined in the Zcash Protocol Specification:
-
Diversified addresses. A scheme that lets a single Sapling spending key produce many unlinkable payment addresses. Each address is the pair . The component domains are:
- : a fixed-length bit string, the diversifier.
- : a non-identity Jubjub point (the prime-order subgroup), derived deterministically from by a hash-to-curve. The hash may fail to land on a valid point, in which case the diversifier is rejected; the spec defines an "unusable diversifier" outcome.
- : the incoming viewing key, a Jubjub scalar derived from the full viewing key.
- : another non-identity Jubjub point, the diversified transmission key.
Reasoning. The map is the only step that consumes the diversifier, so changing produces a completely different base point, and the scalar multiplication then yields a completely different . Two addresses from the same spending key share no on-chain identifier.
Security. Recovering from is the discrete-log problem on Jubjub. Recognising two addresses as belonging to the same spending key is the decisional Diffie-Hellman problem with as the secret, equivalent in hardness to discrete-log on Jubjub under standard assumptions. A holder of can detect notes addressed to any of these addresses by computing and comparing against the published .
See protocol specification Section 4.2.2 ("Sapling Key Components"), Section 5.4.1.6 ("Group Hash into Jubjub" for ), and Section 5.6 ("Encodings of Addresses"). Orchard re-uses the same construction over Pallas with , (non-identity Pallas points), and the same scalar-multiplication step.
-
Full viewing keys. A key that grants read access to a wallet without granting spend access. The Sapling full viewing key is the triple . The component domains are:
- : the spend-authorising public key, a non-identity Jubjub point of prime order. It is the public component of the RedJubjub signing key pair, for the per-pool generator .
- : the nullifier-deriving key, a Jubjub scalar derived from the spending key by the PRF .
- : the outgoing viewing key, a 256-bit byte string derived from the spending key by the PRF . Used as a symmetric key to encrypt and recover the recipient address inside the sender-side ciphertext.
- Derived from the triple: the incoming viewing key , a Jubjub scalar, computed as .
Reasoning. The split into three components mirrors the three read-only capabilities a wallet needs without spend power: for detecting and decrypting incoming notes (Diffie-Hellman with ), for decrypting the sender-side memo of outgoing notes that the wallet has authored, and for recomputing the nullifiers of the wallet's notes so that wallet software can mark notes as spent. None of the three components reveals , so a viewing-key holder cannot forge a spend-authorising signature.
Security. Recovering from is the discrete-log problem on Jubjub. Recovering from alone is at least as hard as inverting on a fixed first argument. Recovering from outgoing ciphertexts is the pre-image resistance of the underlying PRF used to expand the sender ciphertext key from . The capability hierarchy is documented as a lattice in the protocol specification: . Possession of any lower element does not let the holder derive any strictly higher element except by breaking one of the reductions above.
See protocol specification Section 4.2.2 ("Sapling Key Components"), Section 5.4.4 ("Pseudo Random Functions" for , ), Section 5.4.8.2 ("Sapling Key Agreement and Key Derivation"), and ZIP 32 "Shielded HD Wallets" for the hardened-only derivation tree. Orchard re-uses the same triple over Pallas, with the additional trapdoor that randomises the step.
-
Spend-authority signatures (RedJubjub). A re-randomisable Schnorr-style signature scheme on the Jubjub curve, denoted in the specification. Each Sapling spend signs the transaction under a fresh re-randomisation of the long-term key , where is a per-spend secret. The transaction publishes and the signature; verifiers check the signature against without learning . See protocol specification Section 5.4.6 ("RedDSA, RedJubjub, and RedPallas"). Orchard inherits the same construction over Pallas as ; see Chapter 14 (RedPallas).
These three additions are kept by Orchard with the curve and hash changes documented above; the structural role of each is unchanged.
The Sapling circuit is not a Halo 2 circuit; it uses
bellman's Groth16 backend.
3.6 Orchard (Network Upgrade 5, May 2022)
Orchard removed the trusted setup, switched to Halo 2 over the
Pasta cycle, and fused Sapling's separate Spend and Output
descriptions into the single Action.
| Aspect | Value |
|---|---|
| Curves | Pallas + Vesta (Pasta cycle) |
| Proof system | Halo 2 (PLONKish arithmetisation + IPA) |
| Note commitment hash | Sinsemilla |
| Merkle tree hash | MerkleCRH^Orchard (Sinsemilla with depth tag) |
| Nullifier PRF | Poseidon over Pallas |
| Trusted setup | None (transparent setup; the IPA SRS is derived from a published seed) |
| Transaction format | ActionDescription (one per fused spend+output, see Chapter 12) |
| Spec | ZIP 224, ZIP 225 |
| Implementation | zcash/orchard (this crate) + zcash/halo2 |
The Orchard circuit lives in
src/circuit.rs
and is the subject of Chapter 5.
3.7 At a Glance
| Property | Zerocoin | Zerocash | Sprout | Sapling | Orchard |
|---|---|---|---|---|---|
| SNARK | no | yes | yes (BCTV14) | yes (Groth16) | yes (Halo 2 + IPA) |
| Trusted setup | no (RSA) | yes (per-pair) | yes (MPC) | yes (MPC) | no |
| Amounts hidden | no | yes | yes | yes | yes |
| Addresses hidden | no | yes | yes | yes | yes |
| Curve | RSA | various | alt_bn128 | BLS12-381 | Pallas / Vesta |
| Embedded curve | n/a | n/a | n/a | Jubjub | Pallas |
| Note hash | n/a | SHA-256 | SHA-256 | Pedersen | Sinsemilla |
| Nullifier PRF | RSA accum. | SHA-256 | SHA-256 | Blake2s | Poseidon |
| Year | 2013 | 2014 | 2016 | 2018 | 2022 |
4. The Shielded-Pool Vocabulary
The terms below are inherited from Zerocash and recurring across every chapter. Each one is mapped to the Orchard implementation and to its Sapling analogue where they differ.
4.1 Note
A record describing a single shielded UTXO. In Orchard, a note is the tuple
with:
- : 88-bit diversifier (an opaque randomisation of the recipient's address).
- : the recipient's diversified transmission key, a Pallas point where .
- : the note value, a 64-bit unsigned integer.
- : a Pallas base-field element binding the note to a specific predecessor nullifier (the rho chain; see Chapter 9).
- : a 255-bit auxiliary randomness derived from and
the random seed
rseed. - : the commitment trapdoor, a Pallas scalar.
Source:
src/note.rs
defines Note. Sapling's note carries
only; the Zerocash paper uses the symbol where Zcash
uses .
Why Orchard Carries Explicit and
Two structural changes between Sapling and Orchard force the and fields to appear in the Orchard note tuple while they have no Sapling analogue. Both are documented in the Zcash Protocol Specification (Section 4.7.3 "Sending Notes (Orchard)" for the derivations; Section 4.16.2 "Nullifiers (Orchard)" and Section 5.4.1.4 for the construction).
- Sapling's is , not a separate field. Sapling computes the nullifier as (Section 4.16.1 of the spec). Because the role of "" is played by the commitment itself, no separate storage is needed; once the wallet has it has everything required to derive the nullifier. Orchard's nullifier construction (Section 4.16.2, see also Chapter 9) feeds to Poseidon and adds and as separate algebraic terms, so is no longer derivable from and must travel with the note.
- Orchard sets to the nullifier of a spent note. The
source-level helper that constructs the of a new note
reads
Rho::from_nf_old(nf: Nullifier) -> Selfand just stores the nullifier's inner field element. This is the rho chain: every output note's is the input note's published . Sapling does not have an analogous chain because already pins the new note to its own commitment; in Orchard the chain links across consecutive Actions and gives domain-separated input to the Poseidon-based nullifier PRF.
is derived from the random seed rseed and , again
in src/note.rs:
loading...
The wallet stores explicitly (it cannot be recovered from
) and re-derives from rseed and
whenever it needs the note in full. Sapling stores
explicitly for the same operational reason but needs nothing more
because its commitment scheme and nullifier function do not
require independent randomness terms.
4.2 Note Commitment
A binding, hiding commitment to a note, published on chain:
implemented in Orchard as a SinsemillaCommit under the
personalisation "z.cash:Orchard-NoteCommit", declared once as a
const &str in
src/constants/fixed_bases.rs
and consumed at the construction of the commit domain in
src/note/commitment.rs:
loading...
loading...
The extracted form (the -coordinate of the commitment as a Pallas point) is what is inserted into the Merkle tree.
In Zerocash this is "cm" or and is computed by a generic commitment scheme; in Sprout it was SHA-256; in Sapling it was a Pedersen commitment on Jubjub. Orchard's choice of Sinsemilla is the one that makes the in-circuit cost manageable in Halo 2 (see Chapter 6).
4.3 Note Commitment Tree
A global incremental Merkle tree of all note commitments ever
inserted. Fixed depth . The
inner hash is MerkleCRH^Orchard, a Sinsemilla hash that
includes the depth as part of its input.
Source:
src/tree.rs;
the empty-leaf value
is
defined there. The frontier maintenance lives in the external
incrementalmerkletree
crate.
In Zerocash this tree is generic; in Sprout the inner hash was
SHA-256 with a fixed 1 byte tag; in Sapling it was a Pedersen
hash; in Orchard it is Sinsemilla.
4.4 Anchor
The root of the note commitment tree at some past block height. An Orchard spend declares an anchor and proves that its input note's commitment is one of the leaves of the tree with that root, without revealing which leaf.
Source: Anchor in
src/tree.rs;
re-exported at the crate root from
src/lib.rs.
In Zerocash this is rt ("Merkle root"). The naming "anchor"
was introduced in the Zcash Protocol Specification to emphasise
its consensus role.
Where the anchor actually lives. The orchard crate only
computes roots; it does not persist them. The anchor is
consensus state, owned by the full node, and every full-node
implementation must store enough recent roots to validate a
spend whose declared anchor is up to AnchorDepth blocks behind
the chain tip (Zcash Protocol Specification, Section 7.6 of
protocol.pdf).
Two reference implementations are publicly documented:
- Zebra (Rust, current). The Orchard commitment tree is
zebra_chain::orchard::tree::NoteCommitmentTree; its root iszebra_chain::orchard::tree::Rootand is re-exported as the pool'sAnchortype. Persistence and the rolling window of recent anchors are owned by thezebra-stateservice. Browse the generated documentation at dannywillems.github.io/zebra, starting at thezebra_chain::orchard::treemodule and thezebra_statecrate'sservice::finalized_statemodule. - zcashd (C++, in maintenance). The same role is filled by
the
OrchardMerkleFrontierand the coin/state caches aroundCCoinsViewCacheandCChainState. Browse the doxygen at dannywillems.github.io/zcashd, starting atOrchardMerkleFrontierand following the call sites in the block-connect path (ConnectBlock,WriteCoinsViewCache).zcashdis in maintenance in favour ofzebrad, but it remains the historical reference for edge-case behaviour.
Both implementations expose the anchor through their respective
RPCs (getrawtransaction / z_getorchardanchor on zcashd,
getbestchaintip / state APIs on zebrad). Wallets read it from
there; they never recompute it from the tree.
4.5 Nullifier
The unique deterministic identifier of a spent note, revealed on spend so the chain can reject double-spends:
where is the nullifier base point, is implemented as Poseidon , and is the nullifier-deriving key (a private key derived from the spending key, see Chapter 8).
Source:
src/note/nullifier.rs.
Sapling computed the nullifier as a Blake2s PRF of and ; the curve point step is Orchard-specific. Zerocash uses a generic PRF.
4.6 Value Commitment
A homomorphic Pedersen commitment to the note value, in Pallas:
A bundle publishes per Action. Consensus checks value conservation by verifying a binding signature on the sum of these commitments (see Chapter 13).
Source:
src/value.rs.
This is conceptually unchanged from Sapling; only the curve differs. Zerocash relies on value commitments inside the SNARK rather than as a separate homomorphic commitment.
4.7 Diversifier, Address, and the Viewing Keys
The full viewing key derives a per-address diversifier that randomises the address without changing the underlying spending key. The incoming viewing key is
where is the spend-authorising public key and
is a SinsemillaCommit whose
output is a Pallas scalar. The payment address is
.
Source:
src/keys.rs
and Chapter 8.
Diversified addresses were Sapling's innovation; Sprout had a single payment address per spending key. Zerocash does not specify a viewing-key model.
4.8 Action
The Orchard transaction primitive (see Section 1.3 of Chapter 5). An Action description on chain bundles:
- The input note's nullifier .
- The output note's commitment .
- A net value commitment .
- A randomised spend-authorising key .
- An ephemeral key and the encrypted note ciphertexts.
A bundle of Actions is proved jointly by one Halo 2 proof and signed by one binding signature plus spend-authorising signatures.
Source:
src/action.rs.
In Sapling, this role was split between SpendDescription and
OutputDescription; in Sprout, between the two halves of a
JoinSplit. The fusion into a single Action is the
Orchard-specific change.
5. Visualising the Note Lifecycle
The diagrams below cover the entire shielded data flow at the abstract level shared by Sprout, Sapling, and Orchard. Only the primitives inside each box differ across the three pools (SHA-256 vs Pedersen vs Sinsemilla for the note commitment; SHA-256 PRF vs Blake2s vs Poseidon for the nullifier; alt_bn128 vs BLS12-381 vs Pallas for the curve). The structural picture, that is the boxes and arrows, is identical.
5.1 The Two Consensus Data Structures
Every shielded pool maintains exactly two pieces of state on chain: an append-only Merkle tree of all note commitments ever inserted, and a write-once set of all nullifiers ever revealed. A shielded transaction inserts one new commitment per output note and reveals one new nullifier per spent note. Nothing else mutates the shielded state.
The Merkle tree's root at any block height is called the anchor of that height. A spend declares the anchor it proves membership against; consensus accepts any recent anchor, which lets wallets be a few blocks behind without re-proving.
Where Both Structures Live in Code
The orchard crate provides the in-memory data types
(MerkleHashOrchard, Anchor, Nullifier) but does not own the
storage; the full-node implementation persists both structures.
Zebra (Rust full node). The commitment-tree type used by
consensus is zebra_chain::orchard::tree::NoteCommitmentTree and
its bundle, zebra_chain::parallel::tree::NoteCommitmentTrees.
Persistence is handled by zebra-state. The on-disk state uses
RocksDB column families declared in
zebra-state/src/service/finalized_state.rs,
named orchard_note_commitment_tree, orchard_anchors, and
orchard_nullifiers. Browse the rustdoc at
dannywillems.github.io/zebra
starting at zebra_chain::orchard::tree and the zebra_state
crate's service::finalized_state module.
zcashd (C++, in maintenance). The commitment tree is
OrchardMerkleFrontier, stored as tree inside
OrchardMerkleFrontierLegacySer and accessed via
CCoinsView::GetOrchardAnchorAt declared in
src/coins.h
and persisted by
src/txdb.h.
The nullifier set lives in the same coins cache as a
CNullifiersMap mapOrchardNullifiers /
cacheOrchardNullifiers (also src/coins.h); duplicates are
rejected by the OrchardDuplicateNullifier validation error
inside ConnectBlock. Browse the doxygen at
dannywillems.github.io/zcashd
starting at OrchardMerkleFrontier, CCoinsViewCache, and
following the call sites of mapOrchardNullifiers and
GetOrchardAnchorAt.
The same structural pair (commitment tree plus nullifier set)
exists for the Sapling and Sprout pools in both
implementations, with Sapling/Sprout substituted for
Orchard in every name.
5.2 Receiving a Shielded Output
Alice sends v zatoshis to Bob. The output flow is the only
mechanism that creates notes; every shielded balance Bob ever
holds enters the wallet through this path.
What changes in the tree when the Action lands. The leaf
cm*_new is appended to the first free slot; all sibling hashes
on the path up to the root are recomputed; the new root is the
new anchor.
The on-chain footprint of one received note is exactly cm*_new,
the ciphertext c_enc, and the ephemeral key epk. Nothing
links cm*_new to Bob's address or to Alice. Bob learns the
note exists by trial-decrypting every output in every block; this
is cheap because ivk is short and decryption fails fast on the
authentication tag.
Where the Value Comes From
Bob's note is by definition a shielded UTXO. For cm*_new to
appear in the commitment tree the value has to be inside
the shielded pool at the moment the Action is verified;
consensus enforces this by checking that every Action's net
value commitment sums to zero
against the transparent inputs and outputs of the same
transaction, using the binding signature
(see Chapter 13).
This does not mean Alice needs a prior shielded balance. A single Zcash transaction can mix three things at once:
- Transparent inputs and outputs (the t-side of the transaction).
- An Orchard
Bundlewith one or more Actions (each Action consumes one shielded input note and creates one shielded output note, with dummies allowed on either side). - Analogous Sapling spend/output descriptions when the Sapling pool is involved.
If Alice is paying Bob purely from a transparent address, the transaction has transparent inputs equal to plus fee, no shielded inputs (the spend half of each Action is a dummy with value ), and an Orchard output containing Bob's note. The that lands in Bob's note enters the Orchard pool inside that same transaction. From an external observer's point of view this looks like a single payment; from the consensus point of view it is one shielding step glued onto one shielded output, both covered by the same Action proof and the same binding signature.
If Alice already holds Orchard notes, the same Action shape is used with a real shielded input on the spend half. No transparent value is needed in that case.
The Orchard protocol does not provide any other path: every shielded output's value either consumed a shielded input note under the same Action or was shielded from transparent in the same transaction. There is no "transparent direct deposit" into the commitment tree that bypasses these checks.
5.3 Spending a Shielded Note
Some time later Bob spends the note. The deterministic nullifier
nf is what consensus checks for double-spends; the proof
guarantees Bob really owns a note whose commitment is in the tree
under the declared anchor, without revealing which leaf it is.
The nullifier set after the spend lands. The tree itself is
not touched by a pure spend; commitments are append-only and
cm*_old stays a leaf forever.
A real Orchard Action always pairs one spend with one output, so
the same Action that reveals nf_new also appends a fresh
cm*_new for the change note. Dummy notes (value zero) fill out
the spend or output side when only one is needed; see
Chapter 12 on padding.
5.4 What an External Observer Sees
For each Action in a block, the chain exposes a small fixed-shape record. Marked in bold are the parts that mutate consensus state.
| Field | Visible to all | Mutates state |
|---|---|---|
nf_old | yes | inserted into NS |
cm*_new | yes | appended to commitment tree |
cv_net | yes | summed for binding signature |
anchor | yes | checked against recent roots |
epk, c_enc | yes | none |
rk (rand. spend) | yes | signature check |
| zk-proof | yes | verified, then discarded |
What is not visible: which leaf was spent, who owns the
output, the value v, the diversifier d, or any link between
cm*_new and any previous nullifier.
5.5 What Changes Between Protocols
The diagrams above are identical for Sprout, Sapling, and Orchard. The primitives inside each box are not.
| Step | Sprout (2016) | Sapling (2018) | Orchard (2022) |
|---|---|---|---|
| Note commitment hash | SHA-256 (truncated) | Pedersen on Jubjub | Sinsemilla on Pallas |
| Merkle inner hash | SHA-256 + depth tag | Pedersen on Jubjub | MerkleCRH_Orchard (Sinsemilla) |
| Nullifier function | SHA-256 PRF | Blake2s PRF | Poseidon + Pallas point ops |
| zk-SNARK | BCTV14 / Groth16 | Groth16 | Halo 2 + IPA |
| Proving curve | alt_bn128 | BLS12-381 | Pallas / Vesta |
| In-circuit curve | n/a | Jubjub | Pallas |
| Tx primitive | JoinSplit (2-in 2-out) | Spend + Output | Action (fused spend+output) |
| Trusted setup | yes (MPC) | yes (MPC) | none |
The words "tree", "nullifier set", "anchor", "commitment", and "nullifier" carry the exact same consensus meaning in every row; only the algorithm that produces the bits changes.
6. How the Vocabulary Maps to the Crate
| Concept | File | Chapter |
|---|---|---|
Note | src/note.rs | 9 |
NoteCommitment, cm, cm* | src/note/commitment.rs | 9 |
MerkleHashOrchard, Anchor | src/tree.rs | 11 |
Nullifier | src/note/nullifier.rs | 9 |
ValueCommitment, value_balance | src/value.rs | 13 |
SpendingKey, FVK, ivk, ovk | src/keys.rs | 8 |
Action | src/action.rs | 5 |
Bundle, binding signature | src/bundle.rs | 12 |
| Action circuit (the SNARK predicate) | src/circuit.rs | 5 |
| Note encryption | src/note_encryption.rs | 10 |
7. Failure Modes (For the Reader)
Three confusions to avoid when moving between the literature and the code.
- Zerocoin vs Zerocash. Zcash implements Zerocash. Zerocoin is the SNARK-free precursor with RSA accumulators and fixed-denomination coins; Zcash does not use any of it. A paper or blog post that conflates the two is unreliable.
- Sprout vs Sapling vs Orchard primitives. The three pools share the abstract note model but differ on the concrete primitives (commitment hash, Merkle hash, nullifier PRF, curve). Reading a Sapling paper while writing Orchard code is fine for structure but dangerous for constants; the Zcash Protocol Specification distinguishes them by section (Sprout-specific text, Sapling-specific text, Orchard-specific text).
- Domain separation across pools. Every hash in the code is
domain-separated by a personalisation string. The string is
pool-specific (
Zcash_OrchardKDF,MerkleCRH^Orchard, ...). Reusing a personalisation across pools would let a malicious prover replay a Sapling commitment as an Orchard commitment or vice versa; the spec is explicit about this.
8. Catalogue of Cryptographic Hardness Assumptions
The definitions and security arguments earlier in this page invoke several standard cryptographic assumptions. The list below catalogues every assumption that the Sapling and Orchard pools rely on, with the curve and primitive on which the assumption is instantiated, so that a reader who is auditing the security of either pool, comparing it to another design, or scoping a future upgrade has a single reference list.
All entries here are stated as the protocol specification states them; the list is descriptive, not prescriptive, and does not recommend any change.
8.1 Group-Theoretic Assumptions
| Assumption | Sapling instantiation | Orchard instantiation | Used for |
|---|---|---|---|
| Discrete-log (DL) | (Jubjub prime-order subgroup) | (Pallas, embedded curve) | Recovering from ; secrecy of all secret keys |
| Discrete-log (DL) | n/a | (Pallas / Vesta, proving curves) | Halo 2 / IPA polynomial-commitment binding |
| Decisional Diffie-Hellman (DDH) | with as secret | with as secret | Unlinkability of diversified addresses; IND-CPA of the Orchard KEM |
| Gap Diffie-Hellman (Gap-DH) | n/a | IK-CCA of the Orchard KEM (with the AEAD) | |
| Strong-unforgeability of RedDSA | on | on | Spend-authorising signature; binding signature |
8.2 Hash and PRF Assumptions
| Assumption | Primitive | Used for |
|---|---|---|
| Collision and pre-image resistance | on Pallas | ; |
| Collision and pre-image resistance | on Jubjub | Sapling note commitment and Merkle CRH |
| Collision and pre-image resistance | inner permutation | |
| Collision and pre-image resistance (truncated) | SHA-256 | Sprout , Sprout Merkle CRH, Sprout nullifier |
| PRF security | (256- and 512-bit personalised) | , , , |
| PRF security | Blake2s | Sapling nullifier PRF |
| Knowledge soundness in the random oracle model | Fiat-Shamir transcript | Halo 2 proof soundness |
8.3 Symmetric and AEAD Assumptions
| Assumption | Primitive | Used for |
|---|---|---|
| IND-CCA security of the AEAD | ChaCha20-Poly1305 | Note encryption (enc, out ciphertexts in Sapling and Orchard) |
| Block-cipher pseudorandomness (PRP) | AES-256 | FF1 format-preserving encryption of diversifier indices in ZIP 32 |
8.4 Knowledge-Soundness Assumptions of the Proof System
| Pool | Proof system | Model |
|---|---|---|
| Sprout | BCTV14 / Groth16 over alt_bn128 | Knowledge of exponent (KoE) and -PKE in the trusted-setup CRS |
| Sapling | Groth16 over BLS12-381 | Knowledge of exponent (KoE) and -PKE in the trusted-setup CRS |
| Orchard | Halo 2 (PLONKish + IPA) over Pallas / Vesta | Algebraic group model (AGM) reduction to DL on ; ROM for Fiat-Shamir |
8.5 Classical vs Post-Quantum Status
All hardness assumptions in Sections 8.1 and 8.4 are discrete-log-based on elliptic curves. Shor's algorithm solves the discrete-log problem in polynomial time on a sufficiently capable quantum computer, so these assumptions belong to the "classical-only" category of post-quantum classification, as do RSA and standard Diffie-Hellman. The same status applies to every other major deployed cryptographic-currency consensus construction at the time of writing.
The symmetric and hash assumptions in Sections 8.2 and 8.3 (ChaCha20-Poly1305, AES, SHA-256, Blake2b/s, Sinsemilla, Pedersen, Poseidon) are not affected by Shor's algorithm. Grover's algorithm gives a quadratic speed-up against pre-image and key-search problems but does not break collision resistance asymptotically; the practical security margin reduces by roughly half in bits.
This page does not recommend any specific upgrade path or post-quantum replacement; consult the protocol team's published plans and the relevant ZIPs.
9. Spec Pointers
Primary sources, in order of relevance to the Orchard crate.
- Zcash Protocol Specification, the normative reference for all three pools: zips.z.cash/protocol/protocol.pdf. Section 4 ("Abstract Protocol") defines the note model; Section 5.4 specialises it to Orchard.
- ZIP 224 "Orchard Shielded Protocol": the activation ZIP, normative for consensus.
- ZIP 225 "Version 5 Transaction Format":
the wire format of
ActionDescription. - ZIP 32 "Shielded HD Wallets": the hardened-only derivation tree shared across Sapling and Orchard.
- ZIP 212 "Allow Recipient to Derive Ephemeral Secret": the deterministic note randomness derivation shared across Sapling and Orchard.
- ZIP 316 "Unified Addresses and Viewing Keys": the cross-pool address format.
- Zerocoin paper (Miers, Garman, Green, Rubin; IEEE S&P 2013): the historical precursor; Zcash does not implement Zerocoin.
- Zerocash paper (Ben-Sasson, Chiesa, Garman, Green, Miers, Tromer, Virza; IEEE S&P 2014), extended on ePrint 2014/349: the construction Sprout, Sapling, and Orchard all inherit.
- Sapling protocol paper, "Cryptographic primitives" appendix: the Pedersen-on-Jubjub commitments that Orchard replaced with Sinsemilla.
- Halo paper (Bowe, Grigg, Hopwood; ePrint 2019/1021): the IPA + accumulation construction that lets Orchard avoid a trusted setup.
- Halo 2 Book: the proof system Orchard uses.
- Names, ZIPs, Issues, and PRs: this course's curated index of every public reference.
10. Where to Go Next
After this page, the natural reading order is:
- Chapter 1 (Crate and Module Map) to see how the vocabulary above translates to the file tree.
- Chapter 5 (Action Circuit) and Chapter 18 (Shielded Transfers) for how the vocabulary composes into a real transaction.
- Chapter 19 (Research Frontier) if you are reading as a cryptographer interested in what would come next.