zebra/zebra-chain/src/primitives/zcash_primitives.rs

149 lines
4.9 KiB
Rust

//! 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::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<Self, Self::Error> {
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<zcash_primitives::transaction::Transaction, io::Error> {
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<Amount<NonNegative>> for zcash_primitives::transaction::components::Amount {
type Error = ();
fn try_from(amount: Amount<NonNegative>) -> Result<Self, Self::Error> {
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,
input: Option<(&transparent::Output, &transparent::Input, usize)>,
) -> 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 {
Some((output, _, idx)) => {
script = (&output.lock_script).into();
zcash_primitives::transaction::sighash::SignableInput::Transparent(
zcash_primitives::transaction::sighash::TransparentInput::new(
idx,
&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)
}