pczt 0.6.0

Tools for working with partially-created Zcash transactions
Documentation
//! The Transaction Extractor role (anyone can execute).
//!
//! - Creates bindingSig and extracts the final transaction.

use core::marker::PhantomData;
use rand_core::OsRng;

use zcash_primitives::transaction::{
    Authorization, Transaction,
    sighash::{SignableInput, signature_hash},
    txid::TxIdDigester,
};

use crate::Pczt;

mod orchard;
pub use self::orchard::OrchardError;

mod sapling;
pub use self::sapling::SaplingError;

mod transparent;
pub use self::transparent::TransparentError;

pub struct TransactionExtractor<'a> {
    pczt: Pczt,
    sapling_vk: Option<(
        &'a ::sapling::circuit::SpendVerifyingKey,
        &'a ::sapling::circuit::OutputVerifyingKey,
    )>,
    orchard_vk: Option<&'a ::orchard::circuit::VerifyingKey>,
    _unused: PhantomData<&'a ()>,
}

impl<'a> TransactionExtractor<'a> {
    /// Instantiates the Transaction Extractor role with the given PCZT.
    pub fn new(pczt: Pczt) -> Self {
        Self {
            pczt,
            sapling_vk: None,
            orchard_vk: None,
            _unused: PhantomData,
        }
    }

    /// Provides the Sapling Spend and Output verifying keys for validating the Sapling
    /// proofs (if any).
    ///
    /// If not provided, and the PCZT has a Sapling bundle, [`Self::extract`] will return
    /// an error.
    pub fn with_sapling(
        mut self,
        spend_vk: &'a ::sapling::circuit::SpendVerifyingKey,
        output_vk: &'a ::sapling::circuit::OutputVerifyingKey,
    ) -> Self {
        self.sapling_vk = Some((spend_vk, output_vk));
        self
    }

    /// Provides an existing Orchard verifying key for validating the Orchard proof (if
    /// any).
    ///
    /// If not provided, and the PCZT has an Orchard bundle, an Orchard verifying key will
    /// be generated on the fly.
    pub fn with_orchard(mut self, orchard_vk: &'a ::orchard::circuit::VerifyingKey) -> Self {
        self.orchard_vk = Some(orchard_vk);
        self
    }

    /// Attempts to extract a valid transaction from the PCZT.
    pub fn extract(self) -> Result<Transaction, Error> {
        let Self {
            pczt,
            sapling_vk,
            orchard_vk,
            _unused,
        } = self;

        let crate::ParsedPczt { tx_data, .. } = pczt.extract_tx_data::<Unbound, Error>(
            |t| {
                t.extract()
                    .map_err(|e| Error::Transparent(TransparentError::Extract(e)))
            },
            |s| {
                s.extract()
                    .map_err(|e| Error::Sapling(SaplingError::Extract(e)))
            },
            |o| {
                o.extract()
                    .map_err(|e| Error::Orchard(OrchardError::Extract(e)))
            },
        )?;

        // The commitment being signed is shared across all shielded inputs.
        let txid_parts = tx_data.digest(TxIdDigester);
        let shielded_sighash = signature_hash(&tx_data, &SignableInput::Shielded, &txid_parts);

        // Create the binding signatures.
        let tx_data = tx_data.try_map_bundles(
            |t| Ok(t.map(|t| t.map_authorization(transparent::RemoveInputInfo))),
            |s| {
                s.map(|s| {
                    s.apply_binding_signature(*shielded_sighash.as_ref(), OsRng)
                        .ok_or(Error::SighashMismatch)
                })
                .transpose()
            },
            |o| {
                o.map(|o| {
                    o.apply_binding_signature(*shielded_sighash.as_ref(), OsRng)
                        .ok_or(Error::SighashMismatch)
                })
                .transpose()
            },
            #[cfg(zcash_unstable = "zfuture")]
            |_| unimplemented!("PCZT support for TZEs is not implemented."),
        )?;

        let tx = tx_data.freeze().expect("v5 tx can't fail here");

        // Now that we have a supposedly fully-authorized transaction, verify it.
        if let Some(bundle) = tx.sapling_bundle() {
            let (spend_vk, output_vk) = sapling_vk.ok_or(Error::SaplingRequired)?;

            sapling::verify_bundle(bundle, spend_vk, output_vk, *shielded_sighash.as_ref())
                .map_err(Error::Sapling)?;
        }
        if let Some(bundle) = tx.orchard_bundle() {
            orchard::verify_bundle(bundle, orchard_vk, *shielded_sighash.as_ref())
                .map_err(Error::Orchard)?;
        }

        Ok(tx)
    }
}

struct Unbound;

impl Authorization for Unbound {
    type TransparentAuth = ::transparent::pczt::Unbound;
    type SaplingAuth = ::sapling::pczt::Unbound;
    type OrchardAuth = ::orchard::pczt::Unbound;
    #[cfg(zcash_unstable = "zfuture")]
    type TzeAuth = core::convert::Infallible;
}

/// Errors that can occur while extracting a transaction from a PCZT.
#[derive(Debug)]
pub enum Error {
    Extract(crate::ExtractError),
    Orchard(OrchardError),
    Sapling(SaplingError),
    SaplingRequired,
    SighashMismatch,
    Transparent(TransparentError),
}

impl From<crate::ExtractError> for Error {
    fn from(e: crate::ExtractError) -> Self {
        Error::Extract(e)
    }
}