Crate and Module Map
1. Why this chapter exists
The crate has no CONTRIBUTING.md, no architecture document, and 38 Rust source
files clustered into 9 named modules. A new contributor who clones it cold has
to derive the module graph from scratch by reading lib.rs and grepping for
re-exports. This chapter does that work once. Every later chapter
cross-references it: "the value commitment lives in
value::ValueCommitment and
is consumed by the Spend circuit in
circuit::expose_value_commitment."
Concretely: by the end of this chapter you should know which file to open for any of the six load-bearing concepts (keys, notes, values, commitments, circuits, bundles).
2. Definitions
Definition 1.1 (Sapling bundle). A Sapling bundle is a tuple
where is a
vector of spend descriptions, is a vector of output descriptions,
is the net value transferred out of the shielded pool, and
is the authorisation (binding signature plus, per spend, a spend
authorisation signature and a Groth16 proof). In code this is
bundle::Bundle<A, V>.
Definition 1.2 (authorisation marker). The phantom type A in
Bundle<A, V> tags the state of the authorising data: from
EffectsOnly (no proofs, no signatures, used by light clients
inspecting effects) through several InProgress<P, S> states
inside the builder to Authorized (full proofs and signatures,
ready for consensus). The Authorization trait associates a
SpendProof, OutputProof, and
AuthSig type with each marker.
Invariant 1.3 (one anchor per bundle). Every spend in a single bundle references the same Merkle anchor; the builder rejects heterogeneous anchors at construction time, and the circuit re-derives the anchor from the witnessed authentication path so the verifier re-checks it.
3. The crate root
The library's module set is fixed in lib.rs. Two of them are gated behind the
circuit feature, which is on by default but is turned off by no_std
consumers that only need to verify or construct effects.
loading...
The re-export block at the bottom of lib.rs (pub use ...) is the "true"
public surface most users touch: PaymentAddress,
Bundle, the key types, Note, Nullifier,
Anchor, CommitmentTree,
MerklePath, Node,
NOTE_COMMITMENT_TREE_DEPTH, plus
BatchValidator and
SaplingVerificationContext from the verifier
module when circuit is on.
4. Module-by-module breakdown
4.1 Primitives (frozen)
These modules implement the primitives the Zcash protocol spec names outright. They are essentially frozen; expect to read them, not to modify them.
constants(src/constants.rs): fixed curve generators (the seven Pedersen-hash bases, the value commitment generators, the spending-key generator, the proof-generation-key generator), BLAKE2s personalisations (b"Zcashivk",b"Zcash_nf",b"Zcash_PH", ...), and the Groth16 proof size constantGROTH_PROOF_SIZE= 192bytes.group_hash(src/group_hash.rs): hash-to-curve into the Jubjub prime-order subgroup. One function.pedersen_hash(src/pedersen_hash.rs): the Sapling Pedersen hash, out-of-circuit. The in-circuit gadget is separate:circuit::pedersen_hashlives undersrc/circuit/.spec(src/spec.rs): small functions named by the protocol spec (crh_ivk,diversify_hash,mixing_pedersen_hash,prf_nf,ka_sapling_*,extract_p,windowed_pedersen_commit). Private (pub(crate)).
4.2 Data types (read constantly, rarely modified)
note(src/note.rs plusnote/commitment.rsandnote/nullifier.rs): theNotestruct,Rseed,NoteCommitment/ExtractedNoteCommitment,Nullifier.address(src/address.rs): thePaymentAddresstype (private module, onlyPaymentAddressitself is re-exported).value(src/value.rs plusvalue/sums.rs):NoteValue,ValueCommitment,ValueCommitTrapdoor,ValueSum,CommitmentSum,TrapdoorSum,BalanceError.tree(src/tree.rs): the depth-32 Merkle tree (Node,Anchor,CommitmentTree,IncrementalWitness,MerklePath) on top of theincrementalmerkletreecrate.keys(src/keys.rs): the entire Sapling key tree below ZIP 32.SpendAuthorizingKey,SpendValidatingKey,ExpandedSpendingKey,ProofGenerationKey,NullifierDerivingKey,ViewingKey,FullViewingKey,OutgoingViewingKey,SaplingIvk,PreparedIncomingViewingKey,DiversifiedTransmissionKey,Diversifier,EphemeralSecretKey,EphemeralPublicKey,PreparedEphemeralPublicKey,SharedSecret.
4.3 ZIP 32 and note encryption
zip32(src/zip32.rs, 1876 lines): HD key derivation per ZIP 32. ExposesExtendedSpendingKey,ExtendedFullViewingKey,DiversifiableFullViewingKey,IncomingViewingKey,DiversifierKey. This is the largest single source file in the crate.note_encryption(src/note_encryption.rs, 1514 lines): implements thezcash_note_encryption::Domaintrait for Sapling, definesZip212Enforcement, exposes thetry_sapling_*_decryptionentry points, and contains the bulk of the test vectors intest_vectors/note_encryption.rs.
4.4 Circuit and prover (feature-gated)
These three modules are behind feature = "circuit":
circuit(src/circuit.rs pluscircuit/{ecc,pedersen_hash,constants}.rs): the bellmanCircuitimpls forSpendandOutput, plus the Edwards-curve gadget library and the in-circuit Pedersen-hash gadget.prover(src/prover.rs): theSpendProverandOutputProvertraits, plusmock::{MockSpendProver,MockOutputProver}for tests.verifier(src/verifier.rs plusverifier/{single,batch}.rs):SaplingVerificationContextfor verifying one transaction,BatchValidatorfor verifying many.
4.5 Transaction assembly
bundle(src/bundle.rs, 623 lines): theBundle,SpendDescription,OutputDescription, andAuthorizationtypes. Pure data; no logic beyond accessors andmap_authorization.builder(src/builder.rs, 1387 lines): theBuilder,SpendInfo,OutputInfo,BundleType, and theInProgress<P, S>state machine. The largest behavioural surface in the crate.pczt(src/pczt.rs pluspczt/*.rs): partially-created Zcash transactions. Has its own actors (Creator, Constructor, Updater, IO Finalizer, Prover, Signer, Combiner, Spend Finalizer, Transaction Extractor) and its own error types per actor.
4.6 Re-exports at the crate root
loading...
Anything not in that re-export block is reachable only via
sapling_crypto::module_name::Type. The crate makes that explicit by leaving
mod address, mod spec, mod tree, mod verifier private at the crate root.
5. Failure modes
- Importing a deprecated path. The crate has been moved out of
zcash_primitives(see CHANGELOG entry for 0.1.0); some external documentation still refers to the old paths (zcash_primitives::sapling::*). If you see those, treat the external doc as stale. Caught by: nothing automatic in this workspace; downstream crates discover this at compile time. - Using
circuit::Spendwithout thecircuitfeature. The module is gated behind#[cfg(feature = "circuit")]. Without it, none of the Groth16 types compile. Caught by: thebuild-nostdCI matrix (wasm32-wasip1,thumbv7em-none-eabihf) which builds the synthetic crate--no-default-features. - Confusing
pedersen_hash(out-of-circuit) withcircuit::pedersen_hash(in-circuit gadget). They are named identically but live in different modules and have different signatures. The first takes an iterator ofbool; the second takes abellman::ConstraintSystemand an iterator ofboolean::Boolean. Caught by: the type system.
6. Spec pointers
- Zcash Protocol Specification, sec. 4.1.6 (Sapling)
gives the protocol-level reading of every module above. This is the document
the in-source
//comments most often cite. - The crate's own
README notes
the
no_stdconstraint: downstreamno_stdconsumers must enablelazy_static'sspin_no_stdfeature.
7. Exercises
- Identify the smallest entry point. Open
src/lib.rs
and list every symbol re-exported at the crate root. For each, answer: which
module file does it live in, and is it visible without the
circuitfeature? Compare your list with the re-export block above. - Map module dependencies. Grep the source for
use crate::anduse super::statements. Draw the resulting dependency graph by hand. Hint:keys,note, andvaluesit at the bottom;builderandpcztsit on top of everything else. - Add a missing accessor.
bundle::Bundlehas accessors forshielded_spends,shielded_outputs,value_balance, andauthorization, but not for the total count of spends or outputs. Addpub fn num_spends(&self) -> usizeandpub fn num_outputs(&self) -> usizeonBundle<A, V>. Add a unit test that builds anEffectsOnlybundle and asserts both counts. Runcargo test --all-featuresand verify the test passes.
Answers in the code. For exercise 1, the re-export list is in
src/lib.rs lines 49-68.
For exercise 3, the Bundle::shielded_spends and Bundle::shielded_outputs
accessors already return slices, so the implementation is
self.shielded_spends.len() / self.shielded_outputs.len().