pczt/
orchard.rs

1//! The Orchard fields of a PCZT.
2
3use alloc::collections::BTreeMap;
4use alloc::string::String;
5use alloc::vec::Vec;
6use core::cmp::Ordering;
7
8#[cfg(feature = "orchard")]
9use ff::PrimeField;
10use getset::Getters;
11use serde::{Deserialize, Serialize};
12use serde_with::serde_as;
13
14use crate::{
15    common::{Global, Zip32Derivation},
16    roles::combiner::{merge_map, merge_optional},
17};
18
19/// PCZT fields that are specific to producing the transaction's Orchard bundle (if any).
20#[derive(Clone, Debug, Serialize, Deserialize, Getters)]
21pub struct Bundle {
22    /// The Orchard actions in this bundle.
23    ///
24    /// Entries are added by the Constructor, and modified by an Updater, IO Finalizer,
25    /// Signer, Combiner, or Spend Finalizer.
26    #[getset(get = "pub")]
27    pub(crate) actions: Vec<Action>,
28
29    /// The flags for the Orchard bundle.
30    ///
31    /// Contains:
32    /// - `enableSpendsOrchard` flag (bit 0)
33    /// - `enableOutputsOrchard` flag (bit 1)
34    /// - Reserved, zeros (bits 2..=7)
35    ///
36    /// This is set by the Creator. The Constructor MUST only add spends and outputs that
37    /// are consistent with these flags (i.e. are dummies as appropriate).
38    #[getset(get = "pub")]
39    pub(crate) flags: u8,
40
41    /// The net value of Orchard spends minus outputs.
42    ///
43    /// This is initialized by the Creator, and updated by the Constructor as spends or
44    /// outputs are added to the PCZT. It enables per-spend and per-output values to be
45    /// redacted from the PCZT after they are no longer necessary.
46    #[getset(get = "pub")]
47    pub(crate) value_sum: (u64, bool),
48
49    /// The Orchard anchor for this transaction.
50    ///
51    /// Set by the Creator.
52    #[getset(get = "pub")]
53    pub(crate) anchor: [u8; 32],
54
55    /// The Orchard bundle proof.
56    ///
57    /// This is `None` until it is set by the Prover.
58    pub(crate) zkproof: Option<Vec<u8>>,
59
60    /// The Orchard binding signature signing key.
61    ///
62    /// - This is `None` until it is set by the IO Finalizer.
63    /// - The Transaction Extractor uses this to produce the binding signature.
64    pub(crate) bsk: Option<[u8; 32]>,
65}
66
67/// Information about an Orchard action within a transaction.
68#[derive(Clone, Debug, Serialize, Deserialize, Getters)]
69pub struct Action {
70    //
71    // Action effecting data.
72    //
73    // These are required fields that are part of the final transaction, and are filled in
74    // by the Constructor when adding an output.
75    //
76    #[getset(get = "pub")]
77    pub(crate) cv_net: [u8; 32],
78    #[getset(get = "pub")]
79    pub(crate) spend: Spend,
80    #[getset(get = "pub")]
81    pub(crate) output: Output,
82
83    /// The value commitment randomness.
84    ///
85    /// - This is set by the Constructor.
86    /// - The IO Finalizer compresses it into the bsk.
87    /// - This is required by the Prover.
88    /// - This may be used by Signers to verify that the value correctly matches `cv`.
89    ///
90    /// This opens `cv` for all participants. For Signers who don't need this information,
91    /// or after proofs / signatures have been applied, this can be redacted.
92    pub(crate) rcv: Option<[u8; 32]>,
93}
94
95/// Information about the spend part of an Orchard action.
96#[serde_as]
97#[derive(Clone, Debug, Serialize, Deserialize, Getters)]
98pub struct Spend {
99    //
100    // Spend-specific Action effecting data.
101    //
102    // These are required fields that are part of the final transaction, and are filled in
103    // by the Constructor when adding a spend.
104    //
105    #[getset(get = "pub")]
106    pub(crate) nullifier: [u8; 32],
107    #[getset(get = "pub")]
108    pub(crate) rk: [u8; 32],
109
110    /// The spend authorization signature.
111    ///
112    /// This is set by the Signer.
113    #[serde_as(as = "Option<[_; 64]>")]
114    pub(crate) spend_auth_sig: Option<[u8; 64]>,
115
116    /// The [raw encoding] of the Orchard payment address that received the note being spent.
117    ///
118    /// - This is set by the Constructor.
119    /// - This is required by the Prover.
120    ///
121    /// [raw encoding]: https://zips.z.cash/protocol/protocol.pdf#orchardpaymentaddrencoding
122    #[serde_as(as = "Option<[_; 43]>")]
123    pub(crate) recipient: Option<[u8; 43]>,
124
125    /// The value of the input being spent.
126    ///
127    /// - This is required by the Prover.
128    /// - This may be used by Signers to verify that the value matches `cv`, and to
129    ///   confirm the values and change involved in the transaction.
130    ///
131    /// This exposes the input value to all participants. For Signers who don't need this
132    /// information, or after signatures have been applied, this can be redacted.
133    pub(crate) value: Option<u64>,
134
135    /// The rho value for the note being spent.
136    ///
137    /// - This is set by the Constructor.
138    /// - This is required by the Prover.
139    pub(crate) rho: Option<[u8; 32]>,
140
141    /// The seed randomness for the note being spent.
142    ///
143    /// - This is set by the Constructor.
144    /// - This is required by the Prover.
145    pub(crate) rseed: Option<[u8; 32]>,
146
147    /// The full viewing key that received the note being spent.
148    ///
149    /// - This is set by the Updater.
150    /// - This is required by the Prover.
151    #[serde_as(as = "Option<[_; 96]>")]
152    pub(crate) fvk: Option<[u8; 96]>,
153
154    /// A witness from the note to the bundle's anchor.
155    ///
156    /// - This is set by the Updater.
157    /// - This is required by the Prover.
158    pub(crate) witness: Option<(u32, [[u8; 32]; 32])>,
159
160    /// The spend authorization randomizer.
161    ///
162    /// - This is chosen by the Constructor.
163    /// - This is required by the Signer for creating `spend_auth_sig`, and may be used to
164    ///   validate `rk`.
165    /// - After `zkproof` / `spend_auth_sig` has been set, this can be redacted.
166    pub(crate) alpha: Option<[u8; 32]>,
167
168    /// The ZIP 32 derivation path at which the spending key can be found for the note
169    /// being spent.
170    pub(crate) zip32_derivation: Option<Zip32Derivation>,
171
172    /// The spending key for this spent note, if it is a dummy note.
173    ///
174    /// - This is chosen by the Constructor.
175    /// - This is required by the IO Finalizer, and is cleared by it once used.
176    /// - Signers MUST reject PCZTs that contain `dummy_sk` values.
177    pub(crate) dummy_sk: Option<[u8; 32]>,
178
179    /// Proprietary fields related to the note being spent.
180    #[getset(get = "pub")]
181    pub(crate) proprietary: BTreeMap<String, Vec<u8>>,
182}
183
184/// Information about the output part of an Orchard action.
185#[serde_as]
186#[derive(Clone, Debug, Serialize, Deserialize, Getters)]
187pub struct Output {
188    //
189    // Output-specific Action effecting data.
190    //
191    // These are required fields that are part of the final transaction, and are filled in
192    // by the Constructor when adding an output.
193    //
194    #[getset(get = "pub")]
195    pub(crate) cmx: [u8; 32],
196    #[getset(get = "pub")]
197    pub(crate) ephemeral_key: [u8; 32],
198    /// The encrypted note plaintext for the output.
199    ///
200    /// Encoded as a `Vec<u8>` because its length depends on the transaction version.
201    ///
202    /// Once we have memo bundles, we will be able to set memos independently of Outputs.
203    /// For now, the Constructor sets both at the same time.
204    #[getset(get = "pub")]
205    pub(crate) enc_ciphertext: Vec<u8>,
206    /// The encrypted note plaintext for the output.
207    ///
208    /// Encoded as a `Vec<u8>` because its length depends on the transaction version.
209    #[getset(get = "pub")]
210    pub(crate) out_ciphertext: Vec<u8>,
211
212    /// The [raw encoding] of the Orchard payment address that will receive the output.
213    ///
214    /// - This is set by the Constructor.
215    /// - This is required by the Prover.
216    ///
217    /// [raw encoding]: https://zips.z.cash/protocol/protocol.pdf#orchardpaymentaddrencoding
218    #[serde_as(as = "Option<[_; 43]>")]
219    #[getset(get = "pub")]
220    pub(crate) recipient: Option<[u8; 43]>,
221
222    /// The value of the output.
223    ///
224    /// This may be used by Signers to verify that the value matches `cv`, and to confirm
225    /// the values and change involved in the transaction.
226    ///
227    /// This exposes the value to all participants. For Signers who don't need this
228    /// information, we can drop the values and compress the rcvs into the bsk global.
229    #[getset(get = "pub")]
230    pub(crate) value: Option<u64>,
231
232    /// The seed randomness for the output.
233    ///
234    /// - This is set by the Constructor.
235    /// - This is required by the Prover, instead of disclosing `shared_secret` to them.
236    #[getset(get = "pub")]
237    pub(crate) rseed: Option<[u8; 32]>,
238
239    /// The `ock` value used to encrypt `out_ciphertext`.
240    ///
241    /// This enables Signers to verify that `out_ciphertext` is correctly encrypted.
242    ///
243    /// This may be `None` if the Constructor added the output using an OVK policy of
244    /// "None", to make the output unrecoverable from the chain by the sender.
245    pub(crate) ock: Option<[u8; 32]>,
246
247    /// The ZIP 32 derivation path at which the spending key can be found for the output.
248    pub(crate) zip32_derivation: Option<Zip32Derivation>,
249
250    /// The user-facing address to which this output is being sent, if any.
251    ///
252    /// - This is set by an Updater.
253    /// - Signers must parse this address (if present) and confirm that it contains
254    ///   `recipient` (either directly, or e.g. as a receiver within a Unified Address).
255    #[getset(get = "pub")]
256    pub(crate) user_address: Option<String>,
257
258    /// Proprietary fields related to the note being created.
259    #[getset(get = "pub")]
260    pub(crate) proprietary: BTreeMap<String, Vec<u8>>,
261}
262
263impl Bundle {
264    /// Merges this bundle with another.
265    ///
266    /// Returns `None` if the bundles have conflicting data.
267    pub(crate) fn merge(
268        mut self,
269        other: Self,
270        self_global: &Global,
271        other_global: &Global,
272    ) -> Option<Self> {
273        // Destructure `other` to ensure we handle everything.
274        let Self {
275            mut actions,
276            flags,
277            value_sum,
278            anchor,
279            zkproof,
280            bsk,
281        } = other;
282
283        if self.flags != flags {
284            return None;
285        }
286
287        // If `bsk` is set on either bundle, the IO Finalizer has run, which means we
288        // cannot have differing numbers of actions, and the value sums must match.
289        match (self.bsk.as_mut(), bsk) {
290            (Some(lhs), Some(rhs)) if lhs != &rhs => return None,
291            (Some(_), _) | (_, Some(_))
292                if self.actions.len() != actions.len() || self.value_sum != value_sum =>
293            {
294                return None
295            }
296            // IO Finalizer has run, and neither bundle has excess spends or outputs.
297            (Some(_), _) | (_, Some(_)) => (),
298            // IO Finalizer has not run on either bundle.
299            (None, None) => match (
300                self_global.shielded_modifiable(),
301                other_global.shielded_modifiable(),
302                self.actions.len().cmp(&actions.len()),
303            ) {
304                // Fail if the merge would add actions to a non-modifiable bundle.
305                (false, _, Ordering::Less) | (_, false, Ordering::Greater) => return None,
306                // If the other bundle has more actions than us, move them over; these
307                // cannot conflict by construction.
308                (true, _, Ordering::Less) => {
309                    self.actions.extend(actions.drain(self.actions.len()..));
310
311                    // We check below that the overlapping actions match. Assuming here
312                    // that they will, we can take the other bundle's value sum.
313                    self.value_sum = value_sum;
314                }
315                // Do nothing otherwise.
316                (_, _, Ordering::Equal) | (_, true, Ordering::Greater) => (),
317            },
318        }
319
320        if self.anchor != anchor {
321            return None;
322        }
323
324        if !merge_optional(&mut self.zkproof, zkproof) {
325            return None;
326        }
327
328        // Leverage the early-exit behaviour of zip to confirm that the remaining data in
329        // the other bundle matches this one.
330        for (lhs, rhs) in self.actions.iter_mut().zip(actions.into_iter()) {
331            // Destructure `rhs` to ensure we handle everything.
332            let Action {
333                cv_net,
334                spend:
335                    Spend {
336                        nullifier,
337                        rk,
338                        spend_auth_sig,
339                        recipient,
340                        value,
341                        rho,
342                        rseed,
343                        fvk,
344                        witness,
345                        alpha,
346                        zip32_derivation: spend_zip32_derivation,
347                        dummy_sk,
348                        proprietary: spend_proprietary,
349                    },
350                output:
351                    Output {
352                        cmx,
353                        ephemeral_key,
354                        enc_ciphertext,
355                        out_ciphertext,
356                        recipient: output_recipient,
357                        value: output_value,
358                        rseed: output_rseed,
359                        ock,
360                        zip32_derivation: output_zip32_derivation,
361                        user_address,
362                        proprietary: output_proprietary,
363                    },
364                rcv,
365            } = rhs;
366
367            if lhs.cv_net != cv_net
368                || lhs.spend.nullifier != nullifier
369                || lhs.spend.rk != rk
370                || lhs.output.cmx != cmx
371                || lhs.output.ephemeral_key != ephemeral_key
372                || lhs.output.enc_ciphertext != enc_ciphertext
373                || lhs.output.out_ciphertext != out_ciphertext
374            {
375                return None;
376            }
377
378            if !(merge_optional(&mut lhs.spend.spend_auth_sig, spend_auth_sig)
379                && merge_optional(&mut lhs.spend.recipient, recipient)
380                && merge_optional(&mut lhs.spend.value, value)
381                && merge_optional(&mut lhs.spend.rho, rho)
382                && merge_optional(&mut lhs.spend.rseed, rseed)
383                && merge_optional(&mut lhs.spend.fvk, fvk)
384                && merge_optional(&mut lhs.spend.witness, witness)
385                && merge_optional(&mut lhs.spend.alpha, alpha)
386                && merge_optional(&mut lhs.spend.zip32_derivation, spend_zip32_derivation)
387                && merge_optional(&mut lhs.spend.dummy_sk, dummy_sk)
388                && merge_map(&mut lhs.spend.proprietary, spend_proprietary)
389                && merge_optional(&mut lhs.output.recipient, output_recipient)
390                && merge_optional(&mut lhs.output.value, output_value)
391                && merge_optional(&mut lhs.output.rseed, output_rseed)
392                && merge_optional(&mut lhs.output.ock, ock)
393                && merge_optional(&mut lhs.output.zip32_derivation, output_zip32_derivation)
394                && merge_optional(&mut lhs.output.user_address, user_address)
395                && merge_map(&mut lhs.output.proprietary, output_proprietary)
396                && merge_optional(&mut lhs.rcv, rcv))
397            {
398                return None;
399            }
400        }
401
402        Some(self)
403    }
404}
405
406#[cfg(feature = "orchard")]
407impl Bundle {
408    pub(crate) fn into_parsed(self) -> Result<orchard::pczt::Bundle, orchard::pczt::ParseError> {
409        let actions = self
410            .actions
411            .into_iter()
412            .map(|action| {
413                let spend = orchard::pczt::Spend::parse(
414                    action.spend.nullifier,
415                    action.spend.rk,
416                    action.spend.spend_auth_sig,
417                    action.spend.recipient,
418                    action.spend.value,
419                    action.spend.rho,
420                    action.spend.rseed,
421                    action.spend.fvk,
422                    action.spend.witness,
423                    action.spend.alpha,
424                    action
425                        .spend
426                        .zip32_derivation
427                        .map(|z| {
428                            orchard::pczt::Zip32Derivation::parse(
429                                z.seed_fingerprint,
430                                z.derivation_path,
431                            )
432                        })
433                        .transpose()?,
434                    action.spend.dummy_sk,
435                    action.spend.proprietary,
436                )?;
437
438                let output = orchard::pczt::Output::parse(
439                    *spend.nullifier(),
440                    action.output.cmx,
441                    action.output.ephemeral_key,
442                    action.output.enc_ciphertext,
443                    action.output.out_ciphertext,
444                    action.output.recipient,
445                    action.output.value,
446                    action.output.rseed,
447                    action.output.ock,
448                    action
449                        .output
450                        .zip32_derivation
451                        .map(|z| {
452                            orchard::pczt::Zip32Derivation::parse(
453                                z.seed_fingerprint,
454                                z.derivation_path,
455                            )
456                        })
457                        .transpose()?,
458                    action.output.user_address,
459                    action.output.proprietary,
460                )?;
461
462                orchard::pczt::Action::parse(action.cv_net, spend, output, action.rcv)
463            })
464            .collect::<Result<_, _>>()?;
465
466        orchard::pczt::Bundle::parse(
467            actions,
468            self.flags,
469            self.value_sum,
470            self.anchor,
471            self.zkproof,
472            self.bsk,
473        )
474    }
475
476    pub(crate) fn serialize_from(bundle: orchard::pczt::Bundle) -> Self {
477        let actions = bundle
478            .actions()
479            .iter()
480            .map(|action| {
481                let spend = action.spend();
482                let output = action.output();
483
484                Action {
485                    cv_net: action.cv_net().to_bytes(),
486                    spend: Spend {
487                        nullifier: spend.nullifier().to_bytes(),
488                        rk: spend.rk().into(),
489                        spend_auth_sig: spend.spend_auth_sig().as_ref().map(|s| s.into()),
490                        recipient: action
491                            .spend()
492                            .recipient()
493                            .map(|recipient| recipient.to_raw_address_bytes()),
494                        value: spend.value().map(|value| value.inner()),
495                        rho: spend.rho().map(|rho| rho.to_bytes()),
496                        rseed: spend.rseed().map(|rseed| *rseed.as_bytes()),
497                        fvk: spend.fvk().as_ref().map(|fvk| fvk.to_bytes()),
498                        witness: spend.witness().as_ref().map(|witness| {
499                            (
500                                u32::try_from(u64::from(witness.position()))
501                                    .expect("Sapling positions fit in u32"),
502                                witness
503                                    .auth_path()
504                                    .iter()
505                                    .map(|node| node.to_bytes())
506                                    .collect::<Vec<_>>()[..]
507                                    .try_into()
508                                    .expect("path is length 32"),
509                            )
510                        }),
511                        alpha: spend.alpha().map(|alpha| alpha.to_repr()),
512                        zip32_derivation: spend.zip32_derivation().as_ref().map(|z| {
513                            Zip32Derivation {
514                                seed_fingerprint: *z.seed_fingerprint(),
515                                derivation_path: z
516                                    .derivation_path()
517                                    .iter()
518                                    .map(|i| i.index())
519                                    .collect(),
520                            }
521                        }),
522                        dummy_sk: action
523                            .spend()
524                            .dummy_sk()
525                            .map(|dummy_sk| *dummy_sk.to_bytes()),
526                        proprietary: spend.proprietary().clone(),
527                    },
528                    output: Output {
529                        cmx: output.cmx().to_bytes(),
530                        ephemeral_key: output.encrypted_note().epk_bytes,
531                        enc_ciphertext: output.encrypted_note().enc_ciphertext.to_vec(),
532                        out_ciphertext: output.encrypted_note().out_ciphertext.to_vec(),
533                        recipient: action
534                            .output()
535                            .recipient()
536                            .map(|recipient| recipient.to_raw_address_bytes()),
537                        value: output.value().map(|value| value.inner()),
538                        rseed: output.rseed().map(|rseed| *rseed.as_bytes()),
539                        ock: output.ock().as_ref().map(|ock| ock.0),
540                        zip32_derivation: output.zip32_derivation().as_ref().map(|z| {
541                            Zip32Derivation {
542                                seed_fingerprint: *z.seed_fingerprint(),
543                                derivation_path: z
544                                    .derivation_path()
545                                    .iter()
546                                    .map(|i| i.index())
547                                    .collect(),
548                            }
549                        }),
550                        user_address: output.user_address().clone(),
551                        proprietary: output.proprietary().clone(),
552                    },
553                    rcv: action.rcv().as_ref().map(|rcv| rcv.to_bytes()),
554                }
555            })
556            .collect();
557
558        let value_sum = {
559            let (magnitude, sign) = bundle.value_sum().magnitude_sign();
560            (magnitude, matches!(sign, orchard::value::Sign::Negative))
561        };
562
563        Self {
564            actions,
565            flags: bundle.flags().to_byte(),
566            value_sum,
567            anchor: bundle.anchor().to_bytes(),
568            zkproof: bundle
569                .zkproof()
570                .as_ref()
571                .map(|zkproof| zkproof.as_ref().to_vec()),
572            bsk: bundle.bsk().as_ref().map(|bsk| bsk.into()),
573        }
574    }
575}