Skip to main content

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)

AspectValue
CryptoRSA accumulator + double-discrete-log proofs
Trusted setupRSA modulus (later replaced by class groups in follow-up work)
What is hiddenLinkage between mint and spend
What is not hiddenAmount (fixed denomination), and that a shielded op took place
Proof size~25 KiB
ReferenceMiers 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)

AspectValue
CryptoGroth16-style zk-SNARK with R1CS arithmetisation
Trusted setupPer-circuit, public; produced via a multi-party computation ceremony
What is hiddenAmount, sender, receiver, linkage
What is not hiddenThe fact a shielded transaction occurred; net value transferred to/from transparent
Proof size~300 bytes
Verification time< 10 ms
ReferenceBen-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).

AspectValue
Curvealt_bn128 (Barreto-Naehrig, 254-bit, pairing-friendly)
Proof systemBCTV14, later Groth16-backport
Note commitment hashSHA-256 truncated
Merkle tree hashSHA-256 PRF
Trusted setup"Sprout MPC", 6 participants
Transaction formatJoinSplit (2-in / 2-out per JoinSplit, multiple per tx)
StatusDeprecated; new value cannot enter the pool
SpecZcash 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:

  • voldpubv^{\mathsf{pub}}_{\mathsf{old}} and vnewpubv^{\mathsf{pub}}_{\mathsf{new}}: 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 nf1,nf2\mathsf{nf}_1, \mathsf{nf}_2 (one per consumed note).
  • Two output commitments cm1,cm2\mathsf{cm}_1, \mathsf{cm}_2 (one per produced note).
  • An ephemeral public key plus two ciphertexts encrypting the new notes to their recipients.
  • A randomSeed and two MACs h_1, h_2 binding the description to the spending keys, plus a joinSplitPubKey whose 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 mm inputs and nn 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:

3.5 Sapling (Network Upgrade 1, October 2018)

Sapling redesigned the in-circuit primitives for efficiency and introduced viewing keys and diversified addresses.

AspectValue
CurvesBLS12-381 (proving) + Jubjub (in-circuit, twisted Edwards)
Proof systemGroth16
Note commitment hashPedersen hash on Jubjub
Merkle tree hashPedersen hash
Nullifier PRFBlake2s
Trusted setup"Sapling MPC", ~90 participants
Transaction formatSpendDescription (per input) + OutputDescription (per output)
Notable additionsDiversified addresses; full viewing keys; spend-authority signatures (RedJubjub)
Implementationzcash/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 (d,pkd)(d,\, \mathsf{pk_d}). The component domains are:

    • d{0,1}88d \in \{0, 1\}^{88}: a fixed-length bit string, the diversifier.
    • gd=DiversifyHash(d)J(r)g_d = \mathsf{DiversifyHash}(d) \in \mathbb{J}^{(r)*}: a non-identity Jubjub point (the prime-order subgroup), derived deterministically from dd 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.
    • ivkFrJ\mathsf{ivk} \in \mathbb{F}_{r_{\mathbb{J}}}: the incoming viewing key, a Jubjub scalar derived from the full viewing key.
    • pkd=[ivk]gdJ(r)\mathsf{pk_d} = [\mathsf{ivk}]\, g_d \in \mathbb{J}^{(r)*}: another non-identity Jubjub point, the diversified transmission key.

    Reasoning. The map dgdd \mapsto g_d is the only step that consumes the diversifier, so changing dd produces a completely different base point, and the scalar multiplication [ivk]gd[\mathsf{ivk}]\, g_d then yields a completely different pkd\mathsf{pk_d}. Two addresses from the same spending key share no on-chain identifier.

    Security. Recovering ivk\mathsf{ivk} from (gd,pkd)(g_d, \mathsf{pk_d}) is the discrete-log problem on Jubjub. Recognising two addresses as belonging to the same spending key is the decisional Diffie-Hellman problem with ivk\mathsf{ivk} as the secret, equivalent in hardness to discrete-log on Jubjub under standard assumptions. A holder of ivk\mathsf{ivk} can detect notes addressed to any of these addresses by computing [ivk]gd[\mathsf{ivk}]\, g_d and comparing against the published pkd\mathsf{pk_d}.

    See protocol specification Section 4.2.2 ("Sapling Key Components"), Section 5.4.1.6 ("Group Hash into Jubjub" for DiversifyHash\mathsf{DiversifyHash}), and Section 5.6 ("Encodings of Addresses"). Orchard re-uses the same construction over Pallas with ivkFrP\mathsf{ivk} \in \mathbb{F}_{r_{\mathbb{P}}}, gdPg_d \in \mathbb{P}^* (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 (ak,nk,ovk)(\mathsf{ak},\, \mathsf{nk},\, \mathsf{ovk}). The component domains are:

    • akJ(r)\mathsf{ak} \in \mathbb{J}^{(r)*}: the spend-authorising public key, a non-identity Jubjub point of prime order. It is the public component of the RedJubjub signing key pair, ak=[ask]G\mathsf{ak} = [\mathsf{ask}]\, G for the per-pool generator GG.
    • nkFrJ\mathsf{nk} \in \mathbb{F}_{r_{\mathbb{J}}}: the nullifier-deriving key, a Jubjub scalar derived from the spending key by the PRF PRFnk\mathsf{PRF}^{\mathsf{nk}}.
    • ovk{0,1}256\mathsf{ovk} \in \{0, 1\}^{256}: the outgoing viewing key, a 256-bit byte string derived from the spending key by the PRF PRFovk\mathsf{PRF}^{\mathsf{ovk}}. 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 ivkFrJ\mathsf{ivk} \in \mathbb{F}_{r_{\mathbb{J}}}, a Jubjub scalar, computed as ivk=CRHivk(ak,nk)\mathsf{ivk} = \mathsf{CRH}^{\mathsf{ivk}}(\mathsf{ak},\, \mathsf{nk}).

    Reasoning. The split into three components mirrors the three read-only capabilities a wallet needs without spend power: ivk\mathsf{ivk} for detecting and decrypting incoming notes (Diffie-Hellman with epk\mathsf{epk}), ovk\mathsf{ovk} for decrypting the sender-side memo of outgoing notes that the wallet has authored, and nk\mathsf{nk} for recomputing the nullifiers of the wallet's notes so that wallet software can mark notes as spent. None of the three components reveals ask\mathsf{ask}, so a viewing-key holder cannot forge a spend-authorising signature.

    Security. Recovering ask\mathsf{ask} from ak\mathsf{ak} is the discrete-log problem on Jubjub. Recovering nk\mathsf{nk} from ivk\mathsf{ivk} alone is at least as hard as inverting CRHivk\mathsf{CRH}^{\mathsf{ivk}} on a fixed first argument. Recovering ovk\mathsf{ovk} from outgoing ciphertexts is the pre-image resistance of the underlying PRF used to expand the sender ciphertext key from ovk\mathsf{ovk}. The capability hierarchy is documented as a lattice in the protocol specification: skfvk{ivk,ovk,nk}ivk\mathsf{sk} \to \mathsf{fvk} \to \{\mathsf{ivk}, \mathsf{ovk}, \mathsf{nk}\} \to \mathsf{ivk}. 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 PRFnk\mathsf{PRF}^{\mathsf{nk}}, PRFovk\mathsf{PRF}^{\mathsf{ovk}}), 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 rivk\mathsf{rivk} trapdoor that randomises the CRHivk\mathsf{CRH}^{\mathsf{ivk}} step.

  • Spend-authority signatures (RedJubjub). A re-randomisable Schnorr-style signature scheme on the Jubjub curve, denoted RedJubjub\mathsf{RedJubjub} in the specification. Each Sapling spend signs the transaction under a fresh re-randomisation rk=ak+[α]G\mathsf{rk} = \mathsf{ak} + [\alpha]\, G of the long-term key ak\mathsf{ak}, where α\alpha is a per-spend secret. The transaction publishes rk\mathsf{rk} and the signature; verifiers check the signature against rk\mathsf{rk} without learning ak\mathsf{ak}. See protocol specification Section 5.4.6 ("RedDSA, RedJubjub, and RedPallas"). Orchard inherits the same construction over Pallas as RedPallas\mathsf{RedPallas}; 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.

AspectValue
CurvesPallas + Vesta (Pasta cycle)
Proof systemHalo 2 (PLONKish arithmetisation + IPA)
Note commitment hashSinsemilla
Merkle tree hashMerkleCRH^Orchard (Sinsemilla with depth tag)
Nullifier PRFPoseidon over Pallas
Trusted setupNone (transparent setup; the IPA SRS is derived from a published seed)
Transaction formatActionDescription (one per fused spend+output, see Chapter 12)
SpecZIP 224, ZIP 225
Implementationzcash/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

PropertyZerocoinZerocashSproutSaplingOrchard
SNARKnoyesyes (BCTV14)yes (Groth16)yes (Halo 2 + IPA)
Trusted setupno (RSA)yes (per-pair)yes (MPC)yes (MPC)no
Amounts hiddennoyesyesyesyes
Addresses hiddennoyesyesyesyes
CurveRSAvariousalt_bn128BLS12-381Pallas / Vesta
Embedded curven/an/an/aJubjubPallas
Note hashn/aSHA-256SHA-256PedersenSinsemilla
Nullifier PRFRSA accum.SHA-256SHA-256Blake2sPoseidon
Year20132014201620182022

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

note=(d,pkd,v,ρ,ψ,rcm)\mathsf{note} = (d,\, \mathsf{pk_d},\, v,\, \rho,\, \psi,\, \mathsf{rcm})

with:

  • dd: 88-bit diversifier (an opaque randomisation of the recipient's address).
  • pkd\mathsf{pk_d}: the recipient's diversified transmission key, a Pallas point pkd=[ivk]gd\mathsf{pk_d} = [\mathsf{ivk}] g_d where gd=DiversifyHash(d)g_d = \mathsf{DiversifyHash}(d).
  • vv: the note value, a 64-bit unsigned integer.
  • ρ\rho: a Pallas base-field element binding the note to a specific predecessor nullifier (the rho chain; see Chapter 9).
  • ψ\psi: a 255-bit auxiliary randomness derived from ρ\rho and the random seed rseed.
  • rcm\mathsf{rcm}: the commitment trapdoor, a Pallas scalar.

Source: src/note.rs defines Note. Sapling's note carries (d,pkd,v,rcm)(d, \mathsf{pk_d}, v,\, \mathsf{rcm}) only; the Zerocash paper uses the symbol c\mathsf{c} where Zcash uses note\mathsf{note}.

Why Orchard Carries Explicit ρ\rho and ψ\psi

Two structural changes between Sapling and Orchard force the ρ\rho and ψ\psi 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 ρ\rho is cm\mathsf{cm}, not a separate field. Sapling computes the nullifier as nfSapling=PRFnknfSapling(cm)\mathsf{nf}^{\mathsf{Sapling}} = \mathsf{PRF}^{\mathsf{nfSapling}}_{\mathsf{nk}}(\mathsf{cm}) (Section 4.16.1 of the spec). Because the role of "ρ\rho" is played by the commitment cm\mathsf{cm} itself, no separate storage is needed; once the wallet has cm\mathsf{cm} it has everything required to derive the nullifier. Orchard's nullifier construction (Section 4.16.2, see also Chapter 9) feeds ρ\rho to Poseidon and adds ψ\psi and cm\mathsf{cm} as separate algebraic terms, so ρ\rho is no longer derivable from cm\mathsf{cm} and must travel with the note.
  • Orchard sets ρ\rho to the nullifier of a spent note. The source-level helper that constructs the ρ\rho of a new note reads Rho::from_nf_old(nf: Nullifier) -> Self and just stores the nullifier's inner field element. This is the rho chain: every output note's ρ\rho is the input note's published nfold\mathsf{nf}_{\mathsf{old}}. Sapling does not have an analogous chain because ρ=cm\rho = \mathsf{cm} 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.

ψ\psi is derived from the random seed rseed and ρ\rho, again in src/note.rs:

src/note.rs (psi derivation)
loading...

The wallet stores ρ\rho explicitly (it cannot be recovered from cm\mathsf{cm}) and re-derives ψ\psi from rseed and ρ\rho whenever it needs the note in full. Sapling stores rcm\mathsf{rcm} 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:

cm=NoteCommitrcmOrchard(gd,pkd,v,ρ,ψ)\mathsf{cm} = \mathsf{NoteCommit}^{\mathsf{Orchard}}_{\mathsf{rcm}}(g_d,\, \mathsf{pk_d},\, v,\, \rho,\, \psi)

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:

src/constants/fixed_bases.rs (note-commit personalisation)
loading...
src/note/commitment.rs (CommitDomain construction)
loading...

The extracted form cm=ExtractP(cm)\mathsf{cm}^\star = \mathsf{Extract}_{\mathbb{P}}(\mathsf{cm}) (the xx-coordinate of the commitment as a Pallas point) is what is inserted into the Merkle tree.

In Zerocash this is "cm" or cm\mathsf{cm} 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 MERKLE_DEPTH_ORCHARD=32\mathsf{MERKLE\_DEPTH\_ORCHARD} = 32. 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 UncommittedOrchard=2Fp\mathsf{Uncommitted}_{\mathsf{Orchard}} = 2 \in \mathbb{F}_p 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 is zebra_chain::orchard::tree::Root and is re-exported as the pool's Anchor type. Persistence and the rolling window of recent anchors are owned by the zebra-state service. Browse the generated documentation at dannywillems.github.io/zebra, starting at the zebra_chain::orchard::tree module and the zebra_state crate's service::finalized_state module.
  • zcashd (C++, in maintenance). The same role is filled by the OrchardMerkleFrontier and the coin/state caches around CCoinsViewCache and CChainState. Browse the doxygen at dannywillems.github.io/zcashd, starting at OrchardMerkleFrontier and following the call sites in the block-connect path (ConnectBlock, WriteCoinsViewCache). zcashd is in maintenance in favour of zebrad, 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:

nf=ExtractP([PRFnknfOrchard(ρ)]K  +  ψ  +  cm)\mathsf{nf} = \mathsf{Extract}_{\mathbb{P}}\Big( \big[\mathsf{PRF}^{\mathsf{nfOrchard}}_{\mathsf{nk}}(\rho)\big] \mathcal{K} \;+\; \psi \;+\; \mathsf{cm} \Big)

where K\mathcal{K} is the nullifier base point, PRFnfOrchard\mathsf{PRF}^{\mathsf{nfOrchard}} is implemented as Poseidon P128PastaP_{128}^{\mathrm{Pasta}}, and nk\mathsf{nk} 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 nk\mathsf{nk} and ρ\rho; 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:

cv=[v]VOrchard+[rcv]ROrchard.\mathsf{cv} = [v]\, \mathcal{V}^{\mathsf{Orchard}} + [\mathsf{rcv}]\, \mathcal{R}^{\mathsf{Orchard}}.

A bundle publishes cvnet\mathsf{cv^{\mathsf{net}}} 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 dd that randomises the address without changing the underlying spending key. The incoming viewing key is

ivk=Commitrivkivk(ak,nk)\mathsf{ivk} = \mathsf{Commit}^{\mathsf{ivk}}_{\mathsf{rivk}}(\mathsf{ak},\, \mathsf{nk})

where ak\mathsf{ak} is the spend-authorising public key and Commitivk\mathsf{Commit}^{\mathsf{ivk}} is a SinsemillaCommit whose output is a Pallas scalar. The payment address is (d,pkd)(\mathsf{d},\, \mathsf{pk_d}).

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 nfold\mathsf{nf}_{\mathsf{old}}.
  • The output note's commitment cmnew\mathsf{cm}^\star_{\mathsf{new}}.
  • A net value commitment cvnet\mathsf{cv^{\mathsf{net}}}.
  • A randomised spend-authorising key rk\mathsf{rk}.
  • An ephemeral key epk\mathsf{epk} and the encrypted note ciphertexts.

A bundle of N2N \geq 2 Actions is proved jointly by one Halo 2 proof and signed by one binding signature plus NN 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 vv 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 cvnet\mathsf{cv^{\mathsf{net}}} 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 Bundle with 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 vv plus fee, no shielded inputs (the spend half of each Action is a dummy with value 00), and an Orchard output containing Bob's note. The vv 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.

FieldVisible to allMutates state
nf_oldyesinserted into NS
cm*_newyesappended to commitment tree
cv_netyessummed for binding signature
anchoryeschecked against recent roots
epk, c_encyesnone
rk (rand. spend)yessignature check
zk-proofyesverified, 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.

StepSprout (2016)Sapling (2018)Orchard (2022)
Note commitment hashSHA-256 (truncated)Pedersen on JubjubSinsemilla on Pallas
Merkle inner hashSHA-256 + depth tagPedersen on JubjubMerkleCRH_Orchard (Sinsemilla)
Nullifier functionSHA-256 PRFBlake2s PRFPoseidon + Pallas point ops
zk-SNARKBCTV14 / Groth16Groth16Halo 2 + IPA
Proving curvealt_bn128BLS12-381Pallas / Vesta
In-circuit curven/aJubjubPallas
Tx primitiveJoinSplit (2-in 2-out)Spend + OutputAction (fused spend+output)
Trusted setupyes (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

ConceptFileChapter
Notesrc/note.rs9
NoteCommitment, cm, cm*src/note/commitment.rs9
MerkleHashOrchard, Anchorsrc/tree.rs11
Nullifiersrc/note/nullifier.rs9
ValueCommitment, value_balancesrc/value.rs13
SpendingKey, FVK, ivk, ovksrc/keys.rs8
Actionsrc/action.rs5
Bundle, binding signaturesrc/bundle.rs12
Action circuit (the SNARK predicate)src/circuit.rs5
Note encryptionsrc/note_encryption.rs10

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

AssumptionSapling instantiationOrchard instantiationUsed for
Discrete-log (DL)J(r)\mathbb{J}^{(r)} (Jubjub prime-order subgroup)P\mathbb{P}^* (Pallas, embedded curve)Recovering ask\mathsf{ask} from ak\mathsf{ak}; secrecy of all secret keys
Discrete-log (DL)n/aEp,EqE_p,\, E_q (Pallas / Vesta, proving curves)Halo 2 / IPA polynomial-commitment binding
Decisional Diffie-Hellman (DDH)J(r)\mathbb{J}^{(r)} with ivk\mathsf{ivk} as secretP\mathbb{P}^* with ivk\mathsf{ivk} as secretUnlinkability of diversified addresses; IND-CPA of the Orchard KEM
Gap Diffie-Hellman (Gap-DH)n/aP\mathbb{P}^*IK-CCA of the Orchard KEM (with the AEAD)
Strong-unforgeability of RedDSARedJubjub\mathsf{RedJubjub} on J(r)\mathbb{J}^{(r)}RedPallas\mathsf{RedPallas} on P\mathbb{P}^*Spend-authorising signature; binding signature

8.2 Hash and PRF Assumptions

AssumptionPrimitiveUsed for
Collision and pre-image resistanceSinsemilla\mathsf{Sinsemilla} on PallasNoteCommitOrchard\mathsf{NoteCommit}^{\mathsf{Orchard}}; MerkleCRH\mathsf{MerkleCRH}
Collision and pre-image resistancePedersen\mathsf{Pedersen} on JubjubSapling note commitment and Merkle CRH
Collision and pre-image resistancePoseidonP128Pasta\mathsf{Poseidon}\, P_{128}^{\mathrm{Pasta}}PRFnfOrchard\mathsf{PRF}^{\mathsf{nfOrchard}} inner permutation
Collision and pre-image resistance (truncated)SHA-256Sprout NoteCommit\mathsf{NoteCommit}, Sprout Merkle CRH, Sprout nullifier
PRF securityBlake2b\mathsf{Blake2b} (256- and 512-bit personalised)KDFOrchard\mathsf{KDF}^{\mathsf{Orchard}}, PRFexpand\mathsf{PRF}^{\mathsf{expand}}, PRFovk\mathsf{PRF}^{\mathsf{ovk}}, PRFnk\mathsf{PRF}^{\mathsf{nk}}
PRF securityBlake2sSapling nullifier PRF
Knowledge soundness in the random oracle modelBlake2b\mathsf{Blake2b} Fiat-Shamir transcriptHalo 2 proof soundness

8.3 Symmetric and AEAD Assumptions

AssumptionPrimitiveUsed for
IND-CCA security of the AEADChaCha20-Poly1305Note encryption (enc, out ciphertexts in Sapling and Orchard)
Block-cipher pseudorandomness (PRP)AES-256FF1 format-preserving encryption of diversifier indices in ZIP 32

8.4 Knowledge-Soundness Assumptions of the Proof System

PoolProof systemModel
SproutBCTV14 / Groth16 over alt_bn128Knowledge of exponent (KoE) and qq-PKE in the trusted-setup CRS
SaplingGroth16 over BLS12-381Knowledge of exponent (KoE) and qq-PKE in the trusted-setup CRS
OrchardHalo 2 (PLONKish + IPA) over Pallas / VestaAlgebraic group model (AGM) reduction to DL on Ep,EqE_p,\, E_q; 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.

10. Where to Go Next

After this page, the natural reading order is:

  1. Chapter 1 (Crate and Module Map) to see how the vocabulary above translates to the file tree.
  2. Chapter 5 (Action Circuit) and Chapter 18 (Shielded Transfers) for how the vocabulary composes into a real transaction.
  3. Chapter 19 (Research Frontier) if you are reading as a cryptographer interested in what would come next.