1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
use futures::future::Either; use crate::{ amount::Amount, primitives::redjubjub::{Binding, Signature}, sapling::{Nullifier, Output, Spend, ValueCommitment}, serialization::serde_helpers, }; /// A bundle of [`Spend`] and [`Output`] descriptions and signature data. /// /// Spend and Output descriptions are optional, but Zcash transactions must /// include a binding signature if and only if there is at least one Spend *or* /// Output description. This wrapper type bundles at least one Spend or Output /// description with the required signature data, so that an /// `Option<ShieldedData>` correctly models the presence or absence of any /// shielded data. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ShieldedData { /// Either a spend or output description. /// /// Storing this separately ensures that it is impossible to construct /// an invalid `ShieldedData` with no spends or outputs. /// /// However, it's not necessary to access or process `first` and `rest` /// separately, as the [`ShieldedData::spends`] and [`ShieldedData::outputs`] /// methods provide iterators over all of the [`Spend`]s and /// [`Output`]s. #[serde(with = "serde_helpers::Either")] pub first: Either<Spend, Output>, /// The rest of the [`Spend`]s for this transaction. /// /// Note that the [`ShieldedData::spends`] method provides an iterator /// over all spend descriptions. pub rest_spends: Vec<Spend>, /// The rest of the [`Output`]s for this transaction. /// /// Note that the [`ShieldedData::outputs`] method provides an iterator /// over all output descriptions. pub rest_outputs: Vec<Output>, /// A signature on the transaction hash. pub binding_sig: Signature<Binding>, } impl ShieldedData { /// Iterate over the [`Spend`]s for this transaction. pub fn spends(&self) -> impl Iterator<Item = &Spend> { match self.first { Either::Left(ref spend) => Some(spend), Either::Right(_) => None, } .into_iter() .chain(self.rest_spends.iter()) } /// Iterate over the [`Output`]s for this transaction. pub fn outputs(&self) -> impl Iterator<Item = &Output> { match self.first { Either::Left(_) => None, Either::Right(ref output) => Some(output), } .into_iter() .chain(self.rest_outputs.iter()) } /// Collect the [`Nullifier`]s for this transaction, if it contains /// [`Spend`]s. pub fn nullifiers(&self) -> impl Iterator<Item = &Nullifier> { self.spends().map(|spend| &spend.nullifier) } /// Collect the cm_u's for this transaction, if it contains [`Output`]s. pub fn note_commitments(&self) -> impl Iterator<Item = &jubjub::Fq> { self.outputs().map(|output| &output.cm_u) } /// Calculate the Spend/Output binding verification key. /// /// Getting the binding signature validating key from the Spend and Output /// description value commitments and the balancing value implicitly checks /// that the balancing value is consistent with the value transfered in the /// Spend and Output descriptions but also proves that the signer knew the /// randomness used for the Spend and Output value commitments, which /// prevents replays of Output descriptions. /// /// The net value of Spend transfers minus Output transfers in a transaction /// is called the balancing value, measured in zatoshi as a signed integer /// v_balance. /// /// Consistency of v_balance with the value commitments in Spend /// descriptions and Output descriptions is enforced by the binding /// signature. /// /// Instead of generating a key pair at random, we generate it as a function /// of the value commitments in the Spend descriptions and Output /// descriptions of the transaction, and the balancing value. /// /// https://zips.z.cash/protocol/protocol.pdf#saplingbalance pub fn binding_verification_key( &self, value_balance: Amount, ) -> redjubjub::VerificationKeyBytes<Binding> { let cv_old: ValueCommitment = self.spends().map(|spend| spend.cv).sum(); let cv_new: ValueCommitment = self.outputs().map(|output| output.cv).sum(); let cv_balance: ValueCommitment = ValueCommitment::new(jubjub::Fr::zero(), value_balance); let key_bytes: [u8; 32] = (cv_old - cv_new - cv_balance).into(); key_bytes.into() } } // Technically, it's possible to construct two equivalent representations // of a ShieldedData with at least one spend and at least one output, depending // on which goes in the `first` slot. This is annoying but a smallish price to // pay for structural validity. impl std::cmp::PartialEq for ShieldedData { fn eq(&self, other: &Self) -> bool { // First check that the lengths match, so we know it is safe to use zip, // which truncates to the shorter of the two iterators. if self.spends().count() != other.spends().count() { return false; } if self.outputs().count() != other.outputs().count() { return false; } // Now check that the binding_sig, spends, outputs match. self.binding_sig == other.binding_sig && self.spends().zip(other.spends()).all(|(a, b)| a == b) && self.outputs().zip(other.outputs()).all(|(a, b)| a == b) } } impl std::cmp::Eq for ShieldedData {}