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