//! Contains code that interfaces with the zcash_primitives crate from //! librustzcash. use std::{ convert::{TryFrom, TryInto}, io, ops::Deref, }; use crate::{ amount::{Amount, NonNegative}, parameters::{Network, NetworkUpgrade}, serialization::ZcashSerialize, transaction::{AuthDigest, HashType, SigHash, Transaction}, transparent::{self, Script}, }; impl TryFrom<&Transaction> for zcash_primitives::transaction::Transaction { type Error = io::Error; /// Convert a Zebra transaction into a librustzcash one. /// /// # Panics /// /// If the transaction is not V5. (Currently there is no need for this /// conversion for other versions.) fn try_from(trans: &Transaction) -> Result { let network_upgrade = match trans { Transaction::V5 { network_upgrade, .. } => network_upgrade, Transaction::V1 { .. } | Transaction::V2 { .. } | Transaction::V3 { .. } | Transaction::V4 { .. } => panic!("Zebra only uses librustzcash for V5 transactions"), }; convert_tx_to_librustzcash(trans, *network_upgrade) } } pub(crate) fn convert_tx_to_librustzcash( trans: &Transaction, network_upgrade: NetworkUpgrade, ) -> Result { let serialized_tx = trans.zcash_serialize_to_vec()?; let branch_id: u32 = network_upgrade .branch_id() .expect("Network upgrade must have a Branch ID") .into(); // We've already parsed this transaction, so its network upgrade must be valid. let branch_id: zcash_primitives::consensus::BranchId = branch_id .try_into() .expect("zcash_primitives and Zebra have the same branch ids"); let alt_tx = zcash_primitives::transaction::Transaction::read(&serialized_tx[..], branch_id)?; Ok(alt_tx) } /// Convert a Zebra Amount into a librustzcash one. impl TryFrom> for zcash_primitives::transaction::components::Amount { type Error = (); fn try_from(amount: Amount) -> Result { zcash_primitives::transaction::components::Amount::from_u64(amount.into()) } } /// Convert a Zebra Script into a librustzcash one. impl From<&Script> for zcash_primitives::legacy::Script { fn from(script: &Script) -> Self { zcash_primitives::legacy::Script(script.as_raw_bytes().to_vec()) } } /// Compute a signature hash using librustzcash. /// /// # Inputs /// /// - `transaction`: the transaction whose signature hash to compute. /// - `hash_type`: the type of hash (SIGHASH) being used. /// - `network_upgrade`: the network upgrade of the block containing the transaction. /// - `input`: information about the transparent input for which this signature /// hash is being computed, if any. A tuple with the matching output of the /// previous transaction, the input itself, and the index of the input in /// the transaction. pub(crate) fn sighash( trans: &Transaction, hash_type: HashType, network_upgrade: NetworkUpgrade, all_previous_outputs: &[transparent::Output], input_index: Option, ) -> SigHash { let alt_tx = convert_tx_to_librustzcash(trans, network_upgrade) .expect("zcash_primitives and Zebra transaction formats must be compatible"); let script: zcash_primitives::legacy::Script; let signable_input = match input_index { Some(input_index) => { let output = all_previous_outputs[input_index].clone(); script = (&output.lock_script).into(); zcash_primitives::transaction::sighash::SignableInput::Transparent( zcash_primitives::transaction::sighash::TransparentInput::new( input_index, &script, output .value .try_into() .expect("amount was previously validated"), ), ) } None => zcash_primitives::transaction::sighash::SignableInput::Shielded, }; let txid_parts = alt_tx .deref() .digest(zcash_primitives::transaction::txid::TxIdDigester); SigHash( *zcash_primitives::transaction::sighash::signature_hash( alt_tx.deref(), hash_type.bits(), &signable_input, &txid_parts, ) .as_ref(), ) } /// Compute the authorizing data commitment of this transaction as specified /// in [ZIP-244]. /// /// # Panics /// /// If passed a pre-v5 transaction. /// /// [ZIP-244]: https://zips.z.cash/zip-0244. pub(crate) fn auth_digest(trans: &Transaction) -> AuthDigest { let alt_tx: zcash_primitives::transaction::Transaction = trans .try_into() .expect("zcash_primitives and Zebra transaction formats must be compatible"); let digest_bytes: [u8; 32] = alt_tx .auth_commitment() .as_ref() .try_into() .expect("digest has the correct size"); AuthDigest(digest_bytes) } /// Return the destination address from a transparent output. /// /// Returns None if the address type is not valid or unrecognized. pub(crate) fn transparent_output_address( output: &transparent::Output, network: Network, ) -> Option { let script = zcash_primitives::legacy::Script::from(&output.lock_script); let alt_addr = script.address(); match alt_addr { Some(zcash_primitives::legacy::TransparentAddress::PublicKey(pub_key_hash)) => Some( transparent::Address::from_pub_key_hash(network, pub_key_hash), ), Some(zcash_primitives::legacy::TransparentAddress::Script(script_hash)) => { Some(transparent::Address::from_script_hash(network, script_hash)) } None => None, } }