//! Sapling shielded data for `V4` and `V5` `Transaction`s. //! //! Zebra uses a generic shielded data type for `V4` and `V5` transactions. //! The `value_balance` change is handled using the default zero value. //! The anchor change is handled using the `AnchorVariant` type trait. use futures::future::Either; use crate::{ amount::Amount, primitives::redjubjub::{Binding, Signature}, sapling::{tree, Nullifier, Output, Spend, ValueCommitment}, serialization::serde_helpers, }; use serde::{de::DeserializeOwned, Serialize}; use std::{ cmp::{Eq, PartialEq}, fmt::Debug, }; /// Per-Spend Sapling anchors, used in Transaction V4 and the /// `spends_per_anchor` method. #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub struct PerSpendAnchor {} /// Shared Sapling anchors, used in Transaction V5. #[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub struct SharedAnchor {} impl AnchorVariant for PerSpendAnchor { type Shared = (); type PerSpend = tree::Root; } impl AnchorVariant for SharedAnchor { type Shared = tree::Root; type PerSpend = (); } /// A type trait to handle structural differences between V4 and V5 Sapling /// Transaction anchors. /// /// In Transaction V4, anchors are per-Spend. In Transaction V5, there is a /// single transaction anchor for all Spends in a transaction. pub trait AnchorVariant { /// The type of the shared anchor. /// /// `()` means "not present in this transaction version". type Shared: Clone + Debug + DeserializeOwned + Serialize + Eq + PartialEq; /// The type of the per-spend anchor. /// /// `()` means "not present in this transaction version". type PerSpend: Clone + Debug + DeserializeOwned + Serialize + Eq + PartialEq; } /// 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` correctly models the presence or absence of any /// shielded data. /// /// # Differences between Transaction Versions /// /// The Sapling `value_balance` field is optional in `Transaction::V5`, but /// required in `Transaction::V4`. In both cases, if there is no `ShieldedData`, /// then the field value must be zero. Therefore, only need to store /// `value_balance` when there is some Sapling `ShieldedData`. /// /// In `Transaction::V4`, each `Spend` has its own anchor. In `Transaction::V5`, /// there is a single `shared_anchor` for the entire transaction. This /// structural difference is modeled using the `AnchorVariant` type trait. /// A type of `()` means "not present in this transaction version". #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ShieldedData where AnchorV: AnchorVariant + Clone, { /// The net value of Sapling spend transfers minus output transfers. pub value_balance: Amount, /// The shared anchor for all `Spend`s in this transaction. /// /// A type of `()` means "not present in this transaction version". pub shared_anchor: AnchorV::Shared, /// 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, 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>, /// 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, /// A signature on the transaction hash. pub binding_sig: Signature, } impl ShieldedData where AnchorV: AnchorVariant + Clone, Spend: From<(Spend, AnchorV::Shared)>, { /// Iterate over the [`Spend`]s for this transaction. /// /// Returns `Spend` regardless of the underlying transaction /// version, to allow generic verification over V4 and V5 transactions. /// /// # Correctness /// /// Do not use this function for serialization. pub fn spends_per_anchor(&self) -> impl Iterator> + '_ { self.spends() .cloned() .map(move |spend| Spend::::from((spend, self.shared_anchor.clone()))) } } impl ShieldedData where AnchorV: AnchorVariant + Clone, { /// Iterate over the [`Spend`]s for this transaction, returning them as /// their generic type. /// /// # Correctness /// /// Use this function for serialization. pub fn spends(&self) -> impl Iterator> { 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 { 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 { 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 { 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) -> redjubjub::VerificationKeyBytes { 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(), self.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. // // A `ShieldedData` can never be equal to a // `ShieldedData`, even if they have the same effects. impl std::cmp::PartialEq for ShieldedData where AnchorV: AnchorVariant + Clone + PartialEq, { 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 all the fields match self.value_balance == other.value_balance && self.shared_anchor == other.shared_anchor && 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 where AnchorV: AnchorVariant + Clone + PartialEq {}