sapling_crypto/pczt.rs
1//! PCZT support for Sapling.
2
3use alloc::collections::BTreeMap;
4use alloc::string::String;
5use alloc::vec::Vec;
6use core::fmt;
7
8use getset::Getters;
9use redjubjub::{Binding, SpendAuth};
10use zcash_note_encryption::{
11 EphemeralKeyBytes, OutgoingCipherKey, ENC_CIPHERTEXT_SIZE, OUT_CIPHERTEXT_SIZE,
12};
13use zip32::ChildIndex;
14
15use crate::{
16 bundle::GrothProofBytes,
17 keys::SpendAuthorizingKey,
18 note::ExtractedNoteCommitment,
19 value::{NoteValue, ValueCommitTrapdoor, ValueCommitment, ValueSum},
20 Anchor, MerklePath, Nullifier, PaymentAddress, ProofGenerationKey, Rseed,
21};
22
23mod parse;
24pub use parse::ParseError;
25
26mod verify;
27pub use verify::VerifyError;
28
29mod io_finalizer;
30pub use io_finalizer::IoFinalizerError;
31
32mod updater;
33pub use updater::{OutputUpdater, SpendUpdater, Updater, UpdaterError};
34
35#[cfg(feature = "circuit")]
36mod prover;
37#[cfg(feature = "circuit")]
38pub use prover::ProverError;
39
40mod signer;
41pub use signer::SignerError;
42
43mod tx_extractor;
44pub use tx_extractor::{TxExtractorError, Unbound};
45
46/// PCZT fields that are specific to producing the transaction's Sapling bundle (if any).
47///
48/// This struct is for representing Sapling in a partially-created transaction. If you
49/// have a fully-created transaction, use [the regular `Bundle` struct].
50///
51/// [the regular `Bundle` struct]: crate::Bundle
52#[derive(Debug, Getters)]
53#[getset(get = "pub")]
54pub struct Bundle {
55 /// The Sapling spends in this bundle.
56 ///
57 /// Entries are added by the Constructor, and modified by an Updater, IO Finalizer,
58 /// Prover, Signer, Combiner, or Spend Finalizer.
59 pub(crate) spends: Vec<Spend>,
60
61 /// The Sapling outputs in this bundle.
62 ///
63 /// Entries are added by the Constructor, and modified by an Updater, IO Finalizer,
64 /// Prover, Signer, Combiner, or Spend Finalizer.
65 pub(crate) outputs: Vec<Output>,
66
67 /// The net value of Sapling `spends` minus `outputs`.
68 ///
69 /// This is initialized by the Creator, and updated by the Constructor as spends or
70 /// outputs are added to the PCZT. It enables per-spend and per-output values to be
71 /// redacted from the PCZT after they are no longer necessary.
72 pub(crate) value_sum: ValueSum,
73
74 /// The Sapling anchor for this transaction.
75 ///
76 /// Set by the Creator.
77 pub(crate) anchor: Anchor,
78
79 /// The Sapling binding signature signing key.
80 ///
81 /// - This is `None` until it is set by the IO Finalizer.
82 /// - The Transaction Extractor uses this to produce the binding signature.
83 pub(crate) bsk: Option<redjubjub::SigningKey<Binding>>,
84}
85
86impl Bundle {
87 /// Returns a mutable reference to the spends in this bundle.
88 ///
89 /// This is used by Signers to apply signatures with [`Spend::sign`].
90 pub fn spends_mut(&mut self) -> &mut [Spend] {
91 &mut self.spends
92 }
93}
94
95/// Information about a Sapling spend within a transaction.
96///
97/// This struct is for representing Sapling spends in a partially-created transaction. If
98/// you have a fully-created transaction, use [the regular `SpendDescription` struct].
99///
100/// [the regular `SpendDescription` struct]: crate::bundle::SpendDescription
101#[derive(Debug, Getters)]
102#[getset(get = "pub")]
103pub struct Spend {
104 /// A commitment to the value consumed by this spend.
105 pub(crate) cv: ValueCommitment,
106
107 /// The nullifier of the note being spent.
108 pub(crate) nullifier: Nullifier,
109
110 /// The randomized verification key for the note being spent.
111 pub(crate) rk: redjubjub::VerificationKey<SpendAuth>,
112
113 /// The Spend proof.
114 ///
115 /// This is set by the Prover.
116 pub(crate) zkproof: Option<GrothProofBytes>,
117
118 /// The spend authorization signature.
119 ///
120 /// This is set by the Signer.
121 pub(crate) spend_auth_sig: Option<redjubjub::Signature<SpendAuth>>,
122
123 /// The address that received the note being spent.
124 ///
125 /// - This is set by the Constructor (or Updater?).
126 /// - This is required by the Prover.
127 pub(crate) recipient: Option<PaymentAddress>,
128
129 /// The value of the input being spent.
130 ///
131 /// This may be used by Signers to verify that the value matches `cv`, and to confirm
132 /// the values and change involved in the transaction.
133 ///
134 /// This exposes the input value to all participants. For Signers who don't need this
135 /// information, or after signatures have been applied, this can be redacted.
136 pub(crate) value: Option<NoteValue>,
137
138 /// The seed randomness for the note being spent.
139 ///
140 /// - This is set by the Constructor.
141 /// - This is required by the Prover.
142 pub(crate) rseed: Option<Rseed>,
143
144 /// The value commitment randomness.
145 ///
146 /// - This is set by the Constructor.
147 /// - The IO Finalizer compresses it into the `bsk`.
148 /// - This is required by the Prover.
149 /// - This may be used by Signers to verify that the value correctly matches `cv`.
150 ///
151 /// This opens `cv` for all participants. For Signers who don't need this information,
152 /// or after proofs / signatures have been applied, this can be redacted.
153 pub(crate) rcv: Option<ValueCommitTrapdoor>,
154
155 /// The proof generation key `(ak, nsk)` corresponding to the recipient that received
156 /// the note being spent.
157 ///
158 /// - This is set by the Updater.
159 /// - This is required by the Prover.
160 pub(crate) proof_generation_key: Option<ProofGenerationKey>,
161
162 /// A witness from the note to the bundle's anchor.
163 ///
164 /// - This is set by the Updater.
165 /// - This is required by the Prover.
166 pub(crate) witness: Option<MerklePath>,
167
168 /// The spend authorization randomizer.
169 ///
170 /// - This is chosen by the Constructor.
171 /// - This is required by the Signer for creating `spend_auth_sig`, and may be used to
172 /// validate `rk`.
173 /// - After`zkproof` / `spend_auth_sig` has been set, this can be redacted.
174 pub(crate) alpha: Option<jubjub::Scalar>,
175
176 /// The ZIP 32 derivation path at which the spending key can be found for the note
177 /// being spent.
178 pub(crate) zip32_derivation: Option<Zip32Derivation>,
179
180 /// The spend authorizing key for this spent note, if it is a dummy note.
181 ///
182 /// - This is chosen by the Constructor.
183 /// - This is required by the IO Finalizer, and is cleared by it once used.
184 /// - Signers MUST reject PCZTs that contain `dummy_ask` values.
185 pub(crate) dummy_ask: Option<SpendAuthorizingKey>,
186
187 /// Proprietary fields related to the note being spent.
188 pub(crate) proprietary: BTreeMap<String, Vec<u8>>,
189}
190
191/// Information about a Sapling output within a transaction.
192///
193/// This struct is for representing Sapling outputs in a partially-created transaction. If
194/// you have a fully-created transaction, use [the regular `OutputDescription` struct].
195///
196/// [the regular `OutputDescription` struct]: crate::bundle::OutputDescription
197#[derive(Getters)]
198#[getset(get = "pub")]
199pub struct Output {
200 /// A commitment to the value created by this output.
201 pub(crate) cv: ValueCommitment,
202
203 /// A commitment to the new note being created.
204 pub(crate) cmu: ExtractedNoteCommitment,
205
206 /// The ephemeral key used to encrypt the note plaintext.
207 pub(crate) ephemeral_key: EphemeralKeyBytes,
208
209 /// The encrypted note plaintext for the output.
210 ///
211 /// Once we have memo bundles, we will be able to set memos independently of Outputs.
212 /// For now, the Constructor sets both at the same time.
213 pub(crate) enc_ciphertext: [u8; ENC_CIPHERTEXT_SIZE],
214
215 /// The encrypted output plaintext for the output.
216 pub(crate) out_ciphertext: [u8; OUT_CIPHERTEXT_SIZE],
217
218 /// The Output proof.
219 ///
220 /// This is set by the Prover.
221 pub(crate) zkproof: Option<GrothProofBytes>,
222
223 /// The address that will receive the output.
224 ///
225 /// - This is set by the Constructor.
226 /// - This is required by the Prover.
227 /// - The Signer can use `recipient` and `rseed` (if present) to verify that
228 /// `enc_ciphertext` is correctly encrypted (and contains a note plaintext matching
229 /// the public commitments), and to confirm the value of the memo.
230 pub(crate) recipient: Option<PaymentAddress>,
231
232 /// The value of the output.
233 ///
234 /// This may be used by Signers to verify that the value matches `cv`, and to confirm
235 /// the values and change involved in the transaction.
236 ///
237 /// This exposes the output value to all participants. For Signers who don't need this
238 /// information, or after signatures have been applied, this can be redacted.
239 pub(crate) value: Option<NoteValue>,
240
241 /// The seed randomness for the output.
242 ///
243 /// - This is set by the Constructor.
244 /// - This is required by the Prover.
245 /// - The Signer can use `recipient` and `rseed` (if present) to verify that
246 /// `enc_ciphertext` is correctly encrypted (and contains a note plaintext matching
247 /// the public commitments), and to confirm the value of the memo.
248 pub(crate) rseed: Option<[u8; 32]>,
249
250 /// The value commitment randomness.
251 ///
252 /// - This is set by the Constructor.
253 /// - The IO Finalizer compresses it into the bsk.
254 /// - This is required by the Prover.
255 /// - This may be used by Signers to verify that the value correctly matches `cv`.
256 ///
257 /// This opens `cv` for all participants. For Signers who don't need this information,
258 /// or after proofs / signatures have been applied, this can be redacted.
259 pub(crate) rcv: Option<ValueCommitTrapdoor>,
260
261 /// The `ock` value used to encrypt `out_ciphertext`.
262 ///
263 /// This enables Signers to verify that `out_ciphertext` is correctly encrypted.
264 ///
265 /// This may be `None` if the Constructor added the output using an OVK policy of
266 /// "None", to make the output unrecoverable from the chain by the sender.
267 pub(crate) ock: Option<OutgoingCipherKey>,
268
269 /// The ZIP 32 derivation path at which the spending key can be found for the output.
270 pub(crate) zip32_derivation: Option<Zip32Derivation>,
271
272 /// The user-facing address to which this output is being sent, if any.
273 ///
274 /// - This is set by an Updater.
275 /// - Signers must parse this address (if present) and confirm that it contains
276 /// `recipient` (either directly, or e.g. as a receiver within a Unified Address).
277 pub(crate) user_address: Option<String>,
278
279 /// Proprietary fields related to the note being created.
280 pub(crate) proprietary: BTreeMap<String, Vec<u8>>,
281}
282
283impl fmt::Debug for Output {
284 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
285 f.debug_struct("Output")
286 .field("cv", &self.cv)
287 .field("cmu", &self.cmu)
288 .field("ephemeral_key", &self.ephemeral_key)
289 .field("enc_ciphertext", &self.enc_ciphertext)
290 .field("out_ciphertext", &self.out_ciphertext)
291 .field("zkproof", &self.zkproof)
292 .field("recipient", &self.recipient)
293 .field("value", &self.value)
294 .field("rseed", &self.rseed)
295 .field("rcv", &self.rcv)
296 .field("zip32_derivation", &self.zip32_derivation)
297 .field("user_address", &self.user_address)
298 .field("proprietary", &self.proprietary)
299 .finish_non_exhaustive()
300 }
301}
302
303/// The ZIP 32 derivation path at which a key can be found.
304#[derive(Debug, Getters, PartialEq, Eq)]
305#[getset(get = "pub")]
306pub struct Zip32Derivation {
307 /// The [ZIP 32 seed fingerprint](https://zips.z.cash/zip-0032#seed-fingerprints).
308 seed_fingerprint: [u8; 32],
309
310 /// The sequence of indices corresponding to the shielded HD path.
311 derivation_path: Vec<ChildIndex>,
312}
313
314impl Zip32Derivation {
315 /// Extracts the ZIP 32 account index from this derivation path.
316 ///
317 /// Returns `None` if the seed fingerprints don't match, or if this is a non-standard
318 /// derivation path.
319 pub fn extract_account_index(
320 &self,
321 seed_fp: &zip32::fingerprint::SeedFingerprint,
322 expected_coin_type: zip32::ChildIndex,
323 ) -> Option<zip32::AccountId> {
324 if self.seed_fingerprint == seed_fp.to_bytes() {
325 match &self.derivation_path[..] {
326 [purpose, coin_type, account_index]
327 if purpose == &zip32::ChildIndex::hardened(32)
328 && coin_type == &expected_coin_type =>
329 {
330 Some(
331 zip32::AccountId::try_from(account_index.index() - (1 << 31))
332 .expect("zip32::ChildIndex only supports hardened"),
333 )
334 }
335 _ => None,
336 }
337 } else {
338 None
339 }
340 }
341}