Orchard Transaction Format
1. Why this chapter exists
Knowing the halo2 chips that build Orchard is half the story. The other half is what those chips' outputs become on chain. The Orchard transaction format is a deliberate redesign of Sapling: it merges spends and outputs into symmetric "actions", pulls the anchor up to the bundle level, and replaces the per-description Groth16 proofs with a single halo2 proof for the entire bundle. These choices are visible at the byte level (the V5 transaction serialization in ZIP 225) and at the consensus level (one zk-proof verification per bundle instead of one per description).
This chapter is the field-by-field reference for that format and the diff against Sapling and Sprout. If you ever need to write parser code, debug a transaction that fails to deserialize, or reason about which fields the halo2 proof actually binds, this is the page to come back to.
The protocol-context chapter (read it first) introduced the term action; this chapter is its expansion.
2. Definitions
Definition (Bundle). The per-pool subset of a transaction.
A V5 transaction carries up to three bundles: transparent (legacy
Bitcoin-style inputs/outputs), Sapling (SaplingBundle), and
Orchard (OrchardBundle). Each pool's bundle is self-contained:
its proofs, anchors, and binding signatures verify independently
of the others. Pool-to-pool value transfer is the only coupling,
and it goes through the per-bundle valueBalance field.
Definition (Action). Orchard's unit of work. An action combines exactly one note spend and one note output, both possibly dummies (with flags signalling which sides are real). The symmetric structure means a wallet that wants to make a 1-input, 3-output payment uses 3 actions with one real spend in the first action and dummy spends in the other two; the dummy notes are indistinguishable from real ones at the consensus layer. Defined in ZIP 224 section "Actions and Bundles".
Definition (Dummy note). A note generated by the prover for
balance / unlinkability padding. Its value commitment is real
(otherwise valueBalance would not sum), but its underlying note
value is zero and its address is randomly generated. Dummies are
indistinguishable from real notes on chain. Used to round out the
number of actions to whatever the wallet wants for privacy
reasons.
Definition (enableSpends, enableOutputs). Two bits in the
bundle-level flagsOrchard byte. If enableSpends is 0, every
action's spend side is forced to be a dummy (the bundle is
"output-only", useful for shielding). If enableOutputs is 0,
every action's output side is forced to be a dummy
("deshielding-only"). Both bits set is the normal case. Both
clear means an Orchard bundle that does nothing, which is
forbidden by consensus.
Definition (valueBalanceOrchard). Signed 64-bit integer
giving the net value moving out of the Orchard pool. Positive
means Orchard value flows to the transparent or Sapling pools;
negative means the reverse. The binding signature certifies that
the sum of input-action value commitments minus output-action
value commitments equals
, which
forces value conservation across the pool boundary without
revealing per-action amounts.
3. The code
3.1 Bundle and action structure (in zcash/orchard)
The Orchard bundle is a Rust type in
zcash/orchard/src/bundle.rs;
the per-action description is in
zcash/orchard/src/action.rs.
Both are outside this repository; halo2 only provides the chips
that the bundle's proof field certifies.
In the spec (and on the wire), the Orchard bundle has these fields:
OrchardBundle = {
actions: vector of ActionDescription (>= 1 if present),
flags: 1 byte (enableSpends | enableOutputs | reserved),
valueBalance: int64 (signed, in zatoshi),
anchorOrchard: 32 bytes (root of the note commitment tree),
sizeProofsOrchard: CompactSize,
proofsOrchard: halo2 proof (variable length),
vSpendAuthSigsOrchard: vector of 64-byte RedPallas signatures,
one per action,
bindingSigOrchard: 64 bytes (RedPallas binding signature),
}
Each ActionDescription is exactly 820 bytes serialized:
ActionDescription = {
cv: 32 bytes (value commitment, point compressed to x),
nullifier: 32 bytes (Pallas base field, the nullifier nf),
rk: 32 bytes (randomized spend-auth verification key),
cmx: 32 bytes (x-coord of new note commitment cm_new),
ephemeralKey: 32 bytes (DH public key for note encryption),
encCiphertext: 580 bytes (ChaCha20-Poly1305 of the new note + memo),
outCiphertext: 80 bytes (recipient-detection ciphertext for the OVK),
}
spendAuthSig[i] and bindingSigOrchard live at the bundle
level, not in the action. The proofsOrchard field is one halo2
proof that simultaneously certifies all of the actions' predicates.
3.2 What the halo2 proof binds
The Orchard circuit's public input vector has one block per action plus a small bundle-level block. The action block, in order:
| Field | Source | Field type | What constrains it |
|---|---|---|---|
anchor | bundle-level | Pallas base field | shared across all actions; one anchor per bundle |
cv | per action | Pallas base field (x of P) | |
nf_old | per action | Pallas base field | |
rk | per action | Pallas base field (x of P) | |
cmx_new | per action | Pallas base field | |
enableSpend | bundle-level | forces the spend side to a dummy when 0 | |
enableOutput | bundle-level | forces the output side to a dummy when 0 |
The mapping from chip to predicate:
cvand the value-conservation equation: ECC chip (mul_fixed_shortfor the value,mul_fixedfor the randomness,add), see chapter 14 section 3.7.nf_old: Poseidon for the scalar, then ECC for the curve arithmetic, thenExtract_P. Chapter 15 section 3.7.cmx_new: SinsemillaNoteCommit, thenExtract_P. Chapter 15.rk: ECCmul(variable-base) plusadd.enableSpend/enableOutput: bound to the dummy-handling selectors inside the Orchard circuit.
Everything else in ActionDescription (the ciphertexts, the
ephemeral key, the spend-auth signature) is not a public input
to the halo2 proof: it is verified by separate consensus rules
(decryption, signature verification, format checks). The proof
only certifies the in-circuit predicates above.
3.3 Bundle-level proof verification
A V5 transaction with an Orchard bundle is accepted only if all of the following hold (Zcash Protocol Spec section 5.4.7 "RedDSA" and section 7.1.2 "Transaction Consensus Rules"):
- The single halo2 proof in
proofsOrchardverifies against the per-action public-input blocks and the bundle-level public inputs. This is one call tohalo2_proofs::plonk::verify_proof, no matter how many actions are in the bundle. - Every
spendAuthSig[i]is a valid RedPallas signature over the SIGHASH transaction digest with public keyactions[i].rk. bindingSigOrchardis a valid RedPallas signature over the same digest, with a public key derived from the sum ofactions[i].cvminus .anchorOrchardmatches a historical note-commitment-tree root at the consensus-required age window.- Every
actions[i].nullifieris absent from the historical nullifier set (no double-spend).
The fact that a single halo2 proof covers all actions is the single largest verifier-cost saving over Sapling.
3.4 The differences, field by field
The most useful comparison is to lay the three pool formats side by side.
Per-input description
| Concept | Sprout (JoinSplit) | Sapling (SpendDescription) | Orchard (ActionDescription) |
|---|---|---|---|
| Inputs per description | 2 | 1 | 1 (spend side) |
| Outputs per description | 2 | 0 | 1 (output side) |
| Anchor | per JoinSplit | per spend (every spend carries its own) | per bundle (one shared anchor) |
| Nullifier | per input (2) | one | one (or dummy) |
| Value commitment | n/a | one | one (covers spend - output) |
| Randomized auth pubkey | n/a | rk | rk |
| Output commitment | 2 (in same) | (in separate OutputDescription) | cmx (x-coord only) |
| Encrypted note + memo | 2 (in same) | (separate) | encCiphertext (580 B) |
| Per-description zk-proof | yes (BCTV14/Groth16) | yes (Groth16) | no (one proof per bundle) |
| Spend-auth signature | n/a | per spend | per action |
| Serialized size (typical) | ~1700 B | 384 B + 948 B output | 820 B (combined) |
Per-bundle metadata
| Concept | Sprout (vJoinSplit array) | Sapling | Orchard |
|---|---|---|---|
| Value balance | per JoinSplit (vpub_old/new) | per bundle (valueBalanceSapling) | per bundle (valueBalanceOrchard) |
| Binding signature | per JoinSplit (signs randomSeed) | per bundle (Pedersen-based) | per bundle (RedPallas) |
| Single proof per bundle? | no (one per JoinSplit) | no (one per Spend, one per Output) | yes (one halo2 proof) |
| Pool-level proof system | BCTV14/Groth16 (BN254) | Groth16 (BLS12-381) | halo2 + IPA (Pallas) |
| Trusted setup needed | yes | yes | no |
| Enable spends/outputs flags | n/a | n/a (always both) | bundle flag byte |
Why these choices
- Per-bundle anchor (Orchard) vs per-spend anchor (Sapling). In Sapling, each spend independently selects which historical anchor to use, which complicates wallet logic and bloats the spend description. Orchard sharing a single anchor across the whole bundle is simpler, slightly smaller on the wire, and no less private (the wallet can always split into multiple bundles if it wants different anchors).
- Symmetric actions (Orchard) vs split spend/output (Sapling). Sapling's separation leaked the spend / output count of a transaction; Orchard's symmetric structure with mandatory dummies hides it. The wallet always pads to the max(inputs, outputs).
- Single proof (Orchard) vs many proofs (Sapling). Each Groth16 verification is ~3-4 pairings. Sapling transactions with 5 spends and 5 outputs cost ~10 verifier units; an Orchard bundle with 5 actions costs one halo2 verification (with IPA-batched MSM). The verifier-cost gap grows linearly with the number of descriptions.
- No trusted setup (Orchard) vs MPC ceremony (Sapling). halo2's IPA does not need a structured reference string. This was the original motivation for the Pasta cycle and for halo2 itself (the Halo paper).
3.5 Real-world examples (mainnet)
Two real V5 transactions, both mined in mainnet block 3357450,
make the format above concrete. Both happen to have exactly two
actions in their Orchard bundle. The full raw JSON for each is
archived under
onboarding/static/orchard-tx-examples/
so the examples remain reproducible even if the explorer is
unavailable.
Example A: pure shielded transfer (d92c8b0d...a25f)
- Explorer:
mainnet.zcashexplorer.app/transactions/d92c8b0d...a25f - Archived raw JSON:
/orchard-tx-examples/d92c...a25f.json - Total tx size: 9165 bytes
Bundle-level fields:
| Field | Value |
|---|---|
version | 5 |
vin / vout count | 0 / 0 (no transparent component) |
vShieldedSpend / Output | 0 / 0 (no Sapling component) |
actions count | 2 |
flagsOrchard | enableSpends = true, enableOutputs = true |
valueBalanceOrchard | +10000 zat (+0.0001 ZEC, the fee leaving the Orchard pool) |
anchorOrchard | 5255c4c7e2fbb24ae185caae08177cfdb097d496d8d81f95a5c9cdfcde81f416 |
proofsOrchard length | 7264 bytes (14528 hex chars) for the single halo2 proof |
First action's serialized fields:
| Field | Bytes | Value |
|---|---|---|
cv | 32 | d493c81198c6a7e2c8b2d2570780bea2656b5d68078f9f655117c21e250cbb15 |
nullifier | 32 | d07befbcf98f4c0e5da783e1ff29d892bd7073faea0d44f87317c23ae875093a |
rk | 32 | c1095f96b593f06a8d9720e5acec432194885677928eb182501139037ca674bc |
cmx | 32 | 43e1807748ad1f27c44bbfd8544545c3ccecba2269ae20fe95f1c5a6a43a8f3a |
ephemeralKey | 32 | a142f7870ceec45203a78f4eedf3a09f95e28e3195a0bc5e0a864060661cddbd |
encCiphertext | 580 | 94d5dedc... (Note + memo, ChaCha20-Poly1305) |
outCiphertext | 80 | e20d73b6... (OVK-encrypted decryption material) |
Each action contributes
bytes to the bundle, matching the spec exactly. The
spendAuthSig for each action (64 bytes) lives at the bundle
level in vSpendAuthSigsOrchard.
What this transaction proves: valueBalanceOrchard = +10000 says
"10,000 zat is moving out of the Orchard pool" — and since the
transaction has no transparent or Sapling outputs, that 10,000
zat is the miner fee. The two actions otherwise net to zero (one
spend equals one output), so the bundle is consistent with "spend
two Orchard notes, create two new Orchard notes, total value
equals input minus 10000".
Example B: shielding (T->Z) transaction (714c7b48...7685)
Same block 3357450.
- Explorer:
mainnet.zcashexplorer.app/transactions/714c7b48...7685 - Archived raw JSON:
/orchard-tx-examples/714c...7685.json - Total tx size: 9313 bytes
| Field | Value |
|---|---|
version | 5 |
vin count | 1 (transparent input t1a4eKkm768xAH6PKmC1goHys3rh7MiZcea, value 0.12006222 ZEC = 12 006 222 zat) |
vout count | 0 |
actions count | 2 |
flagsOrchard | enableSpends = true, enableOutputs = true |
valueBalanceOrchard | -11991222 zat (negative = value flowing into the Orchard pool) |
anchorOrchard | ae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f |
First action:
| Field | Value |
|---|---|
cv | e59a67c425fba74cccc3a2d1e1716c971cbcefa1209933889c363612211c69ac |
nullifier | 7c55b6fc9a9061cf1e0a657434b1a0e8e4cc9bc478744f23a996800e37a94321 |
rk | 3cd24d54bc8f6f64d89b32d7125f05f0e952858d88fecd0d50143d1e0b855487 |
cmx | b80db92198058a36273dab3543819bf648fe8f7a08c1a824613ff68b1b957c3b |
ephemeralKey | ee006dc5904320c8c446ef898b9473c0313e3e8851e44c9f671dd01dc366ed08 |
Consensus-balance check, in zat:
transparent_in = 12 006 222
transparent_out = 0
valueBalanceOrchard = -11 991 222 (negative: pool absorbs value)
fee = transparent_in - transparent_out - (- valueBalanceOrchard)
= 12 006 222 - 0 - 11 991 222
= 15 000 zat (0.00015 ZEC)
enableSpends = true even though there are no real Orchard
inputs: the two actions still have dummy spend sides. The
prover witnesses those dummies and proves the dummy predicate
(value zero, random key); on the wire the bundle is
indistinguishable from one that spends two real notes. This is
how a transaction that is "only" a shielding still produces
spends and nullifiers indistinguishable from real activity.
Example C: t->z consolidation, four transparent inputs (b79955d9...9969)
Block 3357100.
- Explorer:
mainnet.zcashexplorer.app/transactions/b79955d9...9969 - Archived raw JSON:
/orchard-tx-examples/b799...9969.json - Total tx size: 9790 bytes
| Field | Value |
|---|---|
version | 5 |
vin count | 4 (consolidating four prior transparent UTXOs) |
vout count | 1 (transparent change to t1Ku2KLyndDPsR32jwnrTMd3yvi9tfFP8ML) |
actions count | 2 |
valueBalanceOrchard | -1 309 449 925 zat (negative: pool absorbs ~13.09 ZEC) |
anchorOrchard | ae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f |
The four transparent inputs have values 1.5001, 1.5412,
2.28319145 and 232.68922473 ZEC, totalling 238.01371318 ZEC.
The single transparent output returns 224.91891693 ZEC to the
same wallet. The remaining ~13.09 ZEC enters the Orchard pool
as two new shielded notes.
Why this example matters: it shows that the Orchard bundle size is independent of how many transparent inputs the transaction has. The transparent-side fan-in is whatever Bitcoin-style script logic dictates; the Orchard side stays exactly two actions (which is the wallet's privacy-padding choice, not a consequence of the transparent count).
Action 0:
| Field | Value |
|---|---|
cv | 428b36c3c06f08d9470b98aef3ab4e105971183a228367d938daeb2be2d1f584 |
nullifier | 616ef3b62330c64cb378bf245868714e857d9a8d5687ee23e4d7d580e6f6ba36 |
rk | 7ef26ff3327584acdae27f87e9c77e055b3016e04347b1ccb369f2e8fdb2b51d |
cmx | f698fa99b0135def37f4eda1c7705859301d246dd482d477d9fba2cb8494aa15 |
ephemeralKey | 61a50a832f09721ff03cea426804d1013eb3b727c38135fce055ec8236d2b39e |
Note that anchorOrchard here matches example B's anchor
exactly: both bundles selected the same historical
note-commitment-tree root. This is allowed by consensus (any
anchor within the rolling age window is accepted) and is mildly
useful as a verifier-side cache hit, but it carries no
cryptographic significance.
Example D: z->t deshielding, four actions (8ca808cd...1cbe)
Block 3357000.
- Explorer:
mainnet.zcashexplorer.app/transactions/8ca808cd...1cbe - Archived raw JSON:
/orchard-tx-examples/8ca8...1cbe.json - Total tx size: 15511 bytes
| Field | Value |
|---|---|
version | 5 |
vin count | 0 |
vout count | 1 (transparent output t1ZQEGDvsfecozh3LvWGdUrcL8idozig9K9, 3.2495003 ZEC) |
actions count | 4 |
valueBalanceOrchard | +324 975 030 zat (~+3.25 ZEC leaving the pool) |
anchorOrchard | aad846cc74c19db033b625533056ab47bcda1adebac77d5060caf922fb24782c |
proofsOrchard length | 11808 bytes (vs 7264 bytes for 2-action bundles) |
Action 0:
| Field | Value |
|---|---|
cv | 1429d2ff83c5a938d21f0c678ca9006ff61940b148a3744b9ddfb2aacde1d49f |
nullifier | cfce1e109fa2902b9a1942a07e890fa530ff39ae50815e696614ff6a1e03ab19 |
rk | 128afa79f1fd183706eebd53e7972131d580e950338a2affa8bab95d6658c726 |
cmx | 302af08932772b3113f134947393f2f2fe907aafd1f4330720e97451a4b6290e |
ephemeralKey | 09ec3c3c6fd34f4c58d2051dda0e2c99348e433672df52ddabcc6e2fa13706b8 |
This bundle has four actions and pulls 3.25 ZEC out of the pool into a single transparent address. Four actions means the wallet spent something like 2 real notes (the others are dummies, or it spent 3 real notes with one dummy output; the bundle does not distinguish). The output side similarly hides whether the 3.25 ZEC came from one note, two notes, or four.
Example E: pure shielded transfer with three actions (538553e4...fd6a)
Block 3357150.
- Explorer:
mainnet.zcashexplorer.app/transactions/538553e4...fd6a - Archived raw JSON:
/orchard-tx-examples/5385...fd6a.json - Total tx size: 12321 bytes
| Field | Value |
|---|---|
version | 5 |
vin / vout count | 0 / 0 |
actions count | 3 |
valueBalanceOrchard | +15 000 zat (the fee) |
proofsOrchard length | 9536 bytes |
Same shape as example A (pure z->z, no transparent or Sapling component) but with three actions instead of two. The wallet that built this transaction picked three for unlinkability reasons (probably one spend plus two outputs, or one real plus two dummies). The on-chain observer sees three nullifiers and three commitments without learning how many of each are real.
Proof-size scaling
Pulling the proof length and action count from all five examples:
| Example | Actions | proofsOrchard (bytes) | Per-action delta vs base |
|---|---|---|---|
| A, B, C | 2 | 7264 | baseline |
| E | 3 | 9536 | +2272 |
| D | 4 | 11808 | +2272 (relative to E) |
Each additional action adds exactly 2272 bytes to the halo2 proof on mainnet (at this protocol version). This is the per-action evaluation and commitment overhead the prover writes to the transcript; the constant 7264-byte base covers the gate, permutation, lookup, and IPA tail. Note that this is the proof size, not the verifier cost: the per-action verifier work is dominated by the multi-scalar multiplication, which IPA can amortize when batched (see chapter 09).
What these five examples confirm about the format
- Per-action body is exactly 820 bytes (32 + 32 + 32 + 32 + 32 + 580 + 80), matching the table in section 3.1.
- The Orchard bundle size is independent of how many transparent inputs and outputs the surrounding transaction has (example C has 4 vin, example A has 0).
- A single halo2 proof covers all actions in the bundle; the proof grows by ~2272 bytes per additional action.
- An equivalent Sapling bundle would carry one ~192-byte Groth16 proof per spend and per output, plus per-description overheads, so the cross-over point in proof size between Orchard and Sapling is around 30+ descriptions; below that Sapling is smaller, but every action's verifier cost is one pairing-set in Sapling versus an amortizable MSM in Orchard.
- The anchor is bundle-scoped: examples B and C share the same anchor across different bundles, which is allowed and expected.
enableSpendsandenableOutputsare bothtruein every example above; their existence in the byte format is what makes output-only and spend-only bundles encodable at all, but mainnet wallets do not seem to use that capability in practice.
3.6 Where to look in the code
The transaction format lives in zcash/librustzcash, not in this
repository. Pointers:
zcash/librustzcash/zcash_primitives/src/transaction/components/orchard.rs: the parser and serializer for the V5 Orchard bundle.zcash/orchard/src/bundle.rs: the in-memoryBundle<Authorization, V>type that wraps the on-wire format.zcash/orchard/src/action.rs: the in-memoryAction<A>type.zcash/orchard/src/circuit.rs: the halo2Circuitimplementation; itsConfigand public input layout are what bind the table in section 3.2.
For halo2-side code that the format ultimately reaches:
/// SWU hash-to-curve personalization for the Merkle CRH generator
pub const MERKLE_CRH_PERSONALIZATION: &str = "z.cash:Orchard-MerkleCRH";
/// Instructions to check the validity of a Merkle path of a given `PATH_LENGTH`.
MERKLE_DEPTH_ORCHARD = 32 (in
halo2_gadgets/src/sinsemilla/merkle.rs)
is exactly the depth that anchorOrchard is the root of. The two
anchor values quoted in section 3.5
(5255c4c7... and ae2935f1...) are each 32-byte outputs of
MerkleCRH^Orchard applied 32 times up from a leaf produced by
this chip.
4. Failure modes
- Mixing bundle flags with action contents.
enableSpends = 0does not allow you to skip the spend-side witness in the action; the witness is still required (and is for a dummy note). The flag only changes the predicate the proof must satisfy. Wallet code that conflates the two will produce unprovable bundles. - Per-action anchors. Porting a Sapling parser to Orchard and assuming each action carries its own anchor is a frequent source of bugs. Anchor is bundle-level; each action shares it.
- Forgetting that
cvis a Pallas point, not a value.cvis the value commitment, not the value; sum ofcvs minus[valueBalance] Vshould be a commitment to zero modulo the binding-signature pubkey transformation. Treatingcvas an int64 is the classic format-parsing mistake. - Encrypting before signing. SIGHASH-style coverage in V5 transactions includes the ciphertexts; signing first and encrypting afterwards produces invalid signatures.
- Re-using the bundle proof in another bundle. The proof is
bound to the exact public-input vector, which includes the
per-action
cv,nf,cmx, andrk. Reuse is detected by consensus, but if you are writing batching infrastructure on top, do not share proofs across bundles.
5. Spec pointers
- ZIP 225 "Version 5 Transaction Format": the normative byte layout of the V5 transaction, including all three bundle types. The single most important reference for this chapter.
- ZIP 224 "Orchard Shielded Protocol":
defines
Action,Bundle,Flags,valueBalanceOrchard, binding signature, and the consensus rules. - Zcash Protocol Specification, section 4 "Abstract Protocol": the conceptual layer that the byte format implements.
- Zcash Protocol Specification, section 7.1 "Transaction Encoding and Consensus": the consensus rules a V5 transaction must satisfy.
- Zcash Protocol Specification, section 4.6 "Sapling Spend Description Verification": for the Sapling side of the comparison.
- ZIP 213 "Shielded Coinbase" and ZIP 216 "Require Canonical Jubjub Point Encodings": Sapling-era format ZIPs; useful for understanding what Orchard deliberately changed.
- zcash/orchard README for the high-level summary of the bundle and action types.
6. Exercises
- From the table in section 3.4, identify the three differences
between a Sapling
SpendDescriptionand an OrchardActionDescriptionthat change the on-wire serialized size. Compute the per-input size delta in bytes. - Read
zcash/orchard/src/action.rsand identify which Rust struct field corresponds to each row of the action format table in section 3.1. - Suppose a wallet wants to make a 0-input, 1-output Orchard
transaction (a pure shielding from the transparent pool).
How many actions does the bundle need, and what does the
flagsOrchardbyte contain? - Why is
anchorOrchard32 bytes (instead of, say, a varint)? Hint: it is the output ofExtract_Papplied to a Pallas point. - The halo2 proof in
proofsOrchardis the only zk-proof in the Orchard bundle. List the fields inActionDescriptionthat the proof does not bind (i.e. that are checked by separate consensus rules).
Answers in the code
- Exercise 3: one action, with
enableSpends = 0andenableOutputs = 1. The spend side of the action is a dummy; the output side is the real new note.valueBalanceOrchardequals the negation of the transparent value being shielded (it is signed: negative means value flowing into the pool). - Exercise 4:
Extract_Preturns a Pallas base-field element, which fits in a fixed 32-byte little-endian encoding (the field modulus is ~255 bits). - Exercise 5:
ephemeralKey,encCiphertext,outCiphertext, andspendAuthSigare checked outside the halo2 proof; onlyanchor,cv,nf,rk,cmx,enableSpend,enableOutputare public inputs to the circuit.
7. Further reading
- zcash/orchard/src/builder.rs
for how a wallet constructs a bundle from a list of
Spends andRecipients; the builder handles dummy generation and action packing. - zcash/zcash protocol blog: "Orchard Shielded Protocol" for a user-level walkthrough of the format choices.
- Daira Hopwood's analysis of action-symmetry vs split spend/output for the original design discussion that led to actions.