Add ZIP-244 signature hash support (#2165)

* ZIP-244 sighash implementation using librustzcash

* ZIP-244: fix sighash test; add roundtrip test; update vectors

* Improvements from review; renamed sighash::Hash to SigHash
This commit is contained in:
Conrado Gouvea 2021-07-06 19:27:10 -03:00 committed by GitHub
parent d430e951c2
commit fdfa3cbdc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 803 additions and 508 deletions

View File

@ -15,4 +15,4 @@ pub use x25519_dalek as x25519;
pub use proofs::{Bctv14Proof, Groth16Proof, Halo2Proof, ZkSnarkProof};
pub mod zcash_history;
mod zcash_primitives;
pub(crate) mod zcash_primitives;

View File

@ -4,9 +4,16 @@
use std::{
convert::{TryFrom, TryInto},
io,
ops::Deref,
};
use crate::{serialization::ZcashSerialize, transaction::Transaction};
use crate::{
amount::{Amount, NonNegative},
parameters::NetworkUpgrade,
serialization::ZcashSerialize,
transaction::{HashType, SigHash, Transaction},
transparent::{self, Script},
};
impl TryFrom<&Transaction> for zcash_primitives::transaction::Transaction {
type Error = io::Error;
@ -28,19 +35,92 @@ impl TryFrom<&Transaction> for zcash_primitives::transaction::Transaction {
| Transaction::V4 { .. } => panic!("Zebra only uses librustzcash for V5 transactions"),
};
let serialized_tx = trans.zcash_serialize_to_vec()?;
// The `read` method currently ignores the BranchId for V5 transactions;
// but we use the correct BranchId anyway.
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_tx_to_librustzcash(trans, *network_upgrade)
}
}
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(),
)
}

View File

@ -21,6 +21,7 @@ pub use lock_time::LockTime;
pub use memo::Memo;
pub use sapling::FieldNotPresent;
pub use sighash::HashType;
pub use sighash::SigHash;
use crate::{
amount, block, orchard,
@ -146,7 +147,7 @@ impl Transaction {
network_upgrade: NetworkUpgrade,
hash_type: sighash::HashType,
input: Option<(u32, transparent::Output)>,
) -> blake2b_simd::Hash {
) -> SigHash {
sighash::SigHasher::new(self, hash_type, network_upgrade, input).sighash()
}

View File

@ -12,10 +12,14 @@ use crate::{
transparent,
};
use blake2b_simd::Hash;
use byteorder::{LittleEndian, WriteBytesExt};
use std::io::{self, Write};
use std::{
convert::TryInto,
io::{self, Write},
};
use crate::primitives::zcash_primitives::sighash;
static ZIP143_EXPLANATION: &str = "Invalid transaction version: after Overwinter activation transaction versions 1 and 2 are rejected";
static ZIP243_EXPLANATION: &str = "Invalid transaction version: after Sapling activation transaction versions 1, 2, and 3 are rejected";
@ -48,6 +52,23 @@ impl HashType {
}
}
/// A Signature Hash (or SIGHASH) as specified in
/// https://zips.z.cash/protocol/protocol.pdf#sighash
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct SigHash(pub [u8; 32]);
impl AsRef<[u8; 32]> for SigHash {
fn as_ref(&self) -> &[u8; 32] {
&self.0
}
}
impl AsRef<[u8]> for SigHash {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
pub(super) struct SigHasher<'a> {
trans: &'a Transaction,
hash_type: HashType,
@ -79,7 +100,7 @@ impl<'a> SigHasher<'a> {
}
}
pub(super) fn sighash(self) -> Hash {
pub(super) fn sighash(self) -> SigHash {
use NetworkUpgrade::*;
let mut hash = blake2b_simd::Params::new()
.hash_length(32)
@ -94,12 +115,10 @@ impl<'a> SigHasher<'a> {
Sapling | Blossom | Heartwood | Canopy => self
.hash_sighash_zip243(&mut hash)
.expect("serialization into hasher never fails"),
Nu5 => unimplemented!(
"Nu5 upgrade uses a new transaction digest algorithm, as specified in ZIP-244"
),
Nu5 => return self.hash_sighash_zip244(),
}
hash.finalize()
SigHash(hash.finalize().as_ref().try_into().unwrap())
}
fn consensus_branch_id(&self) -> ConsensusBranchId {
@ -107,7 +126,7 @@ impl<'a> SigHasher<'a> {
}
/// Sighash implementation for the overwinter network upgrade
fn hash_sighash_zip143<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_sighash_zip143<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
self.hash_header(&mut writer)?;
self.hash_groupid(&mut writer)?;
self.hash_prevouts(&mut writer)?;
@ -131,7 +150,7 @@ impl<'a> SigHasher<'a> {
personal
}
fn hash_header<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_header<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
let overwintered_flag = 1 << 31;
writer.write_u32::<LittleEndian>(match &self.trans {
@ -142,7 +161,7 @@ impl<'a> SigHasher<'a> {
})
}
fn hash_groupid<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_groupid<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_u32::<LittleEndian>(match &self.trans {
Transaction::V1 { .. } | Transaction::V2 { .. } => unreachable!(ZIP143_EXPLANATION),
Transaction::V3 { .. } => OVERWINTER_VERSION_GROUP_ID,
@ -151,7 +170,7 @@ impl<'a> SigHasher<'a> {
})
}
fn hash_prevouts<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_prevouts<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
if self.hash_type.contains(HashType::ANYONECANPAY) {
return writer.write_all(&[0; 32]);
}
@ -173,7 +192,7 @@ impl<'a> SigHasher<'a> {
writer.write_all(hash.finalize().as_ref())
}
fn hash_sequence<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_sequence<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
if self.hash_type.contains(HashType::ANYONECANPAY)
|| self.hash_type.masked() == HashType::SINGLE
|| self.hash_type.masked() == HashType::NONE
@ -199,7 +218,7 @@ impl<'a> SigHasher<'a> {
}
/// Writes the u256 hash of the transactions outputs to the provided Writer
fn hash_outputs<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_outputs<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
if self.hash_type.masked() != HashType::SINGLE && self.hash_type.masked() != HashType::NONE
{
self.outputs_hash(writer)
@ -249,7 +268,7 @@ impl<'a> SigHasher<'a> {
writer.write_all(hash.finalize().as_ref())
}
fn hash_joinsplits<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_joinsplits<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
let has_joinsplits = match self.trans {
Transaction::V1 { .. } | Transaction::V2 { .. } => unreachable!(ZIP143_EXPLANATION),
Transaction::V3 { joinsplit_data, .. } => joinsplit_data.is_some(),
@ -318,19 +337,19 @@ impl<'a> SigHasher<'a> {
writer.write_all(hash.finalize().as_ref())
}
fn hash_lock_time<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_lock_time<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
self.trans.lock_time().zcash_serialize(&mut writer)
}
fn hash_expiry_height<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_expiry_height<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_u32::<LittleEndian>(self.trans.expiry_height().expect(ZIP143_EXPLANATION).0)
}
fn hash_hash_type<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_hash_type<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_u32::<LittleEndian>(self.hash_type.bits())
}
fn hash_input<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_input<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
if self.input.is_some() {
self.hash_input_prevout(&mut writer)?;
self.hash_input_script_code(&mut writer)?;
@ -341,7 +360,7 @@ impl<'a> SigHasher<'a> {
Ok(())
}
fn hash_input_prevout<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_input_prevout<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
let (_, input, _) = self
.input
.as_ref()
@ -359,7 +378,10 @@ impl<'a> SigHasher<'a> {
Ok(())
}
fn hash_input_script_code<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_input_script_code<W: io::Write>(
&self,
mut writer: W,
) -> Result<(), io::Error> {
let (transparent::Output { lock_script, .. }, _, _) = self
.input
.as_ref()
@ -370,7 +392,7 @@ impl<'a> SigHasher<'a> {
Ok(())
}
fn hash_input_amount<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_input_amount<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
let (transparent::Output { value, .. }, _, _) = self
.input
.as_ref()
@ -381,7 +403,7 @@ impl<'a> SigHasher<'a> {
Ok(())
}
fn hash_input_sequence<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_input_sequence<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
let (_, input, _) = self
.input
.as_ref()
@ -405,7 +427,7 @@ impl<'a> SigHasher<'a> {
/// Sighash implementation for the sapling network upgrade and every
/// subsequent network upgrade
fn hash_sighash_zip243<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_sighash_zip243<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
self.hash_header(&mut writer)?;
self.hash_groupid(&mut writer)?;
self.hash_prevouts(&mut writer)?;
@ -423,7 +445,10 @@ impl<'a> SigHasher<'a> {
Ok(())
}
fn hash_shielded_spends<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_shielded_spends<W: io::Write>(
&self,
mut writer: W,
) -> Result<(), io::Error> {
use Transaction::*;
let sapling_shielded_data = match self.trans {
@ -462,7 +487,10 @@ impl<'a> SigHasher<'a> {
writer.write_all(hash.finalize().as_ref())
}
fn hash_shielded_outputs<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_shielded_outputs<W: io::Write>(
&self,
mut writer: W,
) -> Result<(), io::Error> {
use Transaction::*;
let sapling_shielded_data = match self.trans {
@ -499,7 +527,7 @@ impl<'a> SigHasher<'a> {
writer.write_all(hash.finalize().as_ref())
}
fn hash_value_balance<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
pub(super) fn hash_value_balance<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
use crate::amount::Amount;
use std::convert::TryFrom;
use Transaction::*;
@ -520,466 +548,13 @@ impl<'a> SigHasher<'a> {
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{amount::Amount, serialization::ZcashDeserializeInto, transaction::Transaction};
use color_eyre::eyre;
use eyre::Result;
use transparent::Script;
use zebra_test::vectors::{ZIP143_1, ZIP143_2, ZIP243_1, ZIP243_2, ZIP243_3};
macro_rules! assert_hash_eq {
($expected:literal, $hasher:expr, $f:ident) => {
let mut buf = vec![];
$hasher
.$f(&mut buf)
.expect("hashing into a vec never fails");
let expected = $expected;
let result = hex::encode(buf);
let span = tracing::span!(
tracing::Level::ERROR,
"compare_vecs",
expected.len = expected.len(),
result.len = result.len(),
hash_fn = stringify!($f)
);
let guard = span.enter();
assert_eq!(expected, result);
drop(guard);
};
}
#[test]
fn test_vec143_1() -> Result<()> {
zebra_test::init();
let transaction = ZIP143_1.zcash_deserialize_into::<Transaction>()?;
let hasher = SigHasher {
trans: &transaction,
hash_type: HashType::ALL,
network_upgrade: NetworkUpgrade::Overwinter,
input: None,
};
assert_hash_eq!("03000080", hasher, hash_header);
assert_hash_eq!("7082c403", hasher, hash_groupid);
assert_hash_eq!(
"d53a633bbecf82fe9e9484d8a0e727c73bb9e68c96e72dec30144f6a84afa136",
hasher,
hash_prevouts
);
assert_hash_eq!(
"a5f25f01959361ee6eb56a7401210ee268226f6ce764a4f10b7f29e54db37272",
hasher,
hash_sequence
);
assert_hash_eq!(
"ab6f7f6c5ad6b56357b5f37e16981723db6c32411753e28c175e15589172194a",
hasher,
hash_outputs
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_joinsplits
);
assert_hash_eq!("481cdd86", hasher, hash_lock_time);
assert_hash_eq!("b3cc4318", hasher, hash_expiry_height);
assert_hash_eq!("01000000", hasher, hash_hash_type);
assert_hash_eq!(
"030000807082c403d53a633bbecf82fe9e9484d8a0e727c73bb9e68c96e72dec30144f6a84afa136a5f25f01959361ee6eb56a7401210ee268226f6ce764a4f10b7f29e54db37272ab6f7f6c5ad6b56357b5f37e16981723db6c32411753e28c175e15589172194a0000000000000000000000000000000000000000000000000000000000000000481cdd86b3cc431801000000",
hasher,
hash_sighash_zip143
);
let hash = hasher.sighash();
let expected = "a1f1a4e5cd9bd522322d661edd2af1bf2a7019cfab94ece18f4ba935b0a19073";
let result = hex::encode(hash.as_bytes());
let span = tracing::span!(
tracing::Level::ERROR,
"compare_final",
expected.len = expected.len(),
buf.len = result.len()
);
let _guard = span.enter();
assert_eq!(expected, result);
Ok(())
}
#[test]
fn test_vec143_2() -> Result<()> {
zebra_test::init();
let transaction = ZIP143_2.zcash_deserialize_into::<Transaction>()?;
let value = hex::decode("2f6e04963b4c0100")?.zcash_deserialize_into::<Amount<_>>()?;
let lock_script = Script::new(&hex::decode("53")?);
let input_ind = 1;
let hasher = SigHasher::new(
&transaction,
HashType::SINGLE,
NetworkUpgrade::Overwinter,
Some((input_ind, transparent::Output { value, lock_script })),
);
assert_hash_eq!("03000080", hasher, hash_header);
assert_hash_eq!("7082c403", hasher, hash_groupid);
assert_hash_eq!(
"92b8af1f7e12cb8de105af154470a2ae0a11e64a24a514a562ff943ca0f35d7f",
hasher,
hash_prevouts
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_sequence
);
assert_hash_eq!(
"edc32cce530f836f7c31c53656f859f514c3ff8dcae642d3e17700fdc6e829a4",
hasher,
hash_outputs
);
assert_hash_eq!(
"f59e41b40f3a60be90bee2be11b0956dfff06a6d8e22668c4f215bd87b20d514",
hasher,
hash_joinsplits
);
assert_hash_eq!("97b0e4e4", hasher, hash_lock_time);
assert_hash_eq!("c705fc05", hasher, hash_expiry_height);
assert_hash_eq!("03000000", hasher, hash_hash_type);
assert_hash_eq!(
"378af1e40f64e125946f62c2fa7b2fecbcb64b6968912a6381ce3dc166d56a1d62f5a8d7",
hasher,
hash_input_prevout
);
assert_hash_eq!("0153", hasher, hash_input_script_code);
assert_hash_eq!("2f6e04963b4c0100", hasher, hash_input_amount);
assert_hash_eq!("e8c7203d", hasher, hash_input_sequence);
let hash = hasher.sighash();
let expected = "23652e76cb13b85a0e3363bb5fca061fa791c40c533eccee899364e6e60bb4f7";
let result = hash.as_bytes();
let result = hex::encode(result);
let span = tracing::span!(
tracing::Level::ERROR,
"compare_final",
expected.len = expected.len(),
buf.len = result.len()
);
let _guard = span.enter();
assert_eq!(expected, result);
Ok(())
}
#[test]
fn test_vec243_1() -> Result<()> {
zebra_test::init();
let transaction = ZIP243_1.zcash_deserialize_into::<Transaction>()?;
let hasher = SigHasher {
trans: &transaction,
hash_type: HashType::ALL,
network_upgrade: NetworkUpgrade::Sapling,
input: None,
};
assert_hash_eq!("04000080", hasher, hash_header);
assert_hash_eq!("85202f89", hasher, hash_groupid);
assert_hash_eq!(
"d53a633bbecf82fe9e9484d8a0e727c73bb9e68c96e72dec30144f6a84afa136",
hasher,
hash_prevouts
);
assert_hash_eq!(
"a5f25f01959361ee6eb56a7401210ee268226f6ce764a4f10b7f29e54db37272",
hasher,
hash_sequence
);
assert_hash_eq!(
"ab6f7f6c5ad6b56357b5f37e16981723db6c32411753e28c175e15589172194a",
hasher,
hash_outputs
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_joinsplits
);
assert_hash_eq!(
"3fd9edb96dccf5b9aeb71e3db3710e74be4f1dfb19234c1217af26181f494a36",
hasher,
hash_shielded_spends
);
assert_hash_eq!(
"dafece799f638ba7268bf8fe43f02a5112f0bb32a84c4a8c2f508c41ff1c78b5",
hasher,
hash_shielded_outputs
);
assert_hash_eq!("481cdd86", hasher, hash_lock_time);
assert_hash_eq!("b3cc4318", hasher, hash_expiry_height);
assert_hash_eq!("442117623ceb0500", hasher, hash_value_balance);
assert_hash_eq!("01000000", hasher, hash_hash_type);
assert_hash_eq!(
"0400008085202f89d53a633bbecf82fe9e9484d8a0e727c73bb9e68c96e72dec30144f6a84afa136a5f25f01959361ee6eb56a7401210ee268226f6ce764a4f10b7f29e54db37272ab6f7f6c5ad6b56357b5f37e16981723db6c32411753e28c175e15589172194a00000000000000000000000000000000000000000000000000000000000000003fd9edb96dccf5b9aeb71e3db3710e74be4f1dfb19234c1217af26181f494a36dafece799f638ba7268bf8fe43f02a5112f0bb32a84c4a8c2f508c41ff1c78b5481cdd86b3cc4318442117623ceb050001000000",
hasher,
hash_sighash_zip243
);
let hash = hasher.sighash();
let expected = "63d18534de5f2d1c9e169b73f9c783718adbef5c8a7d55b5e7a37affa1dd3ff3";
let result = hex::encode(hash.as_bytes());
let span = tracing::span!(
tracing::Level::ERROR,
"compare_final",
expected.len = expected.len(),
buf.len = result.len()
);
let _guard = span.enter();
assert_eq!(expected, result);
Ok(())
}
#[test]
fn test_vec243_2() -> Result<()> {
zebra_test::init();
let transaction = ZIP243_2.zcash_deserialize_into::<Transaction>()?;
let value = hex::decode("adedf02996510200")?.zcash_deserialize_into::<Amount<_>>()?;
let lock_script = Script::new(&[]);
let input_ind = 1;
let hasher = SigHasher::new(
&transaction,
HashType::NONE,
NetworkUpgrade::Sapling,
Some((input_ind, transparent::Output { value, lock_script })),
);
assert_hash_eq!("04000080", hasher, hash_header);
assert_hash_eq!("85202f89", hasher, hash_groupid);
assert_hash_eq!(
"cacf0f5210cce5fa65a59f314292b3111d299e7d9d582753cf61e1e408552ae4",
hasher,
hash_prevouts
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_sequence
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_outputs
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_joinsplits
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_shielded_spends
);
assert_hash_eq!(
"b79530fcec83211d21e3c355db538c138d625784c27370e9d1039a8515a23f87",
hasher,
hash_shielded_outputs
);
assert_hash_eq!("d7034302", hasher, hash_lock_time);
assert_hash_eq!("011b9a07", hasher, hash_expiry_height);
assert_hash_eq!("6620edc067ff0200", hasher, hash_value_balance);
assert_hash_eq!("02000000", hasher, hash_hash_type);
assert_hash_eq!(
"090f47a068e227433f9e49d3aa09e356d8d66d0c0121e91a3c4aa3f27fa1b63396e2b41d",
hasher,
hash_input_prevout
);
assert_hash_eq!("00", hasher, hash_input_script_code);
assert_hash_eq!("adedf02996510200", hasher, hash_input_amount);
assert_hash_eq!("4e970568", hasher, hash_input_sequence);
assert_hash_eq!(
"0400008085202f89cacf0f5210cce5fa65a59f314292b3111d299e7d9d582753cf61e1e408552ae40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b79530fcec83211d21e3c355db538c138d625784c27370e9d1039a8515a23f87d7034302011b9a076620edc067ff020002000000090f47a068e227433f9e49d3aa09e356d8d66d0c0121e91a3c4aa3f27fa1b63396e2b41d00adedf029965102004e970568",
hasher,
hash_sighash_zip243
);
let hash = hasher.sighash();
let expected = "bbe6d84f57c56b29b914c694baaccb891297e961de3eb46c68e3c89c47b1a1db";
let result = hex::encode(hash.as_bytes());
let span = tracing::span!(
tracing::Level::ERROR,
"compare_final",
expected.len = expected.len(),
buf.len = result.len()
);
let _guard = span.enter();
assert_eq!(expected, result);
Ok(())
}
#[test]
fn test_vec243_3() -> Result<()> {
zebra_test::init();
let transaction = ZIP243_3.zcash_deserialize_into::<Transaction>()?;
let value = hex::decode("80f0fa0200000000")?.zcash_deserialize_into::<Amount<_>>()?;
let lock_script = Script::new(&hex::decode(
"76a914507173527b4c3318a2aecd793bf1cfed705950cf88ac",
)?);
let input_ind = 0;
let hasher = SigHasher::new(
&transaction,
HashType::ALL,
NetworkUpgrade::Sapling,
Some((input_ind, transparent::Output { value, lock_script })),
);
assert_hash_eq!("04000080", hasher, hash_header);
assert_hash_eq!("85202f89", hasher, hash_groupid);
assert_hash_eq!(
"fae31b8dec7b0b77e2c8d6b6eb0e7e4e55abc6574c26dd44464d9408a8e33f11",
hasher,
hash_prevouts
);
assert_hash_eq!(
"6c80d37f12d89b6f17ff198723e7db1247c4811d1a695d74d930f99e98418790",
hasher,
hash_sequence
);
assert_hash_eq!(
"d2b04118469b7810a0d1cc59568320aad25a84f407ecac40b4f605a4e6868454",
hasher,
hash_outputs
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_joinsplits
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_shielded_spends
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_shielded_outputs
);
assert_hash_eq!("29b00400", hasher, hash_lock_time);
assert_hash_eq!("48b00400", hasher, hash_expiry_height);
assert_hash_eq!("0000000000000000", hasher, hash_value_balance);
assert_hash_eq!("01000000", hasher, hash_hash_type);
assert_hash_eq!(
"a8c685478265f4c14dada651969c45a65e1aeb8cd6791f2f5bb6a1d9952104d901000000",
hasher,
hash_input_prevout
);
assert_hash_eq!(
"1976a914507173527b4c3318a2aecd793bf1cfed705950cf88ac",
hasher,
hash_input_script_code
);
assert_hash_eq!("80f0fa0200000000", hasher, hash_input_amount);
assert_hash_eq!("feffffff", hasher, hash_input_sequence);
assert_hash_eq!(
"0400008085202f89fae31b8dec7b0b77e2c8d6b6eb0e7e4e55abc6574c26dd44464d9408a8e33f116c80d37f12d89b6f17ff198723e7db1247c4811d1a695d74d930f99e98418790d2b04118469b7810a0d1cc59568320aad25a84f407ecac40b4f605a4e686845400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029b0040048b00400000000000000000001000000a8c685478265f4c14dada651969c45a65e1aeb8cd6791f2f5bb6a1d9952104d9010000001976a914507173527b4c3318a2aecd793bf1cfed705950cf88ac80f0fa0200000000feffffff",
hasher,
hash_sighash_zip243
);
let hash = hasher.sighash();
let expected = "f3148f80dfab5e573d5edfe7a850f5fd39234f80b5429d3a57edcc11e34c585b";
let result = hex::encode(hash.as_bytes());
let span = tracing::span!(
tracing::Level::ERROR,
"compare_final",
expected.len = expected.len(),
buf.len = result.len()
);
let _guard = span.enter();
assert_eq!(expected, result);
Ok(())
/// Compute a signature hash for V5 transactions according to ZIP-244.
fn hash_sighash_zip244(&self) -> SigHash {
let input = self
.input
.as_ref()
.map(|(output, input, idx)| (output, *input, *idx));
sighash(&self.trans, self.hash_type, self.network_upgrade, input)
}
}

View File

@ -10,9 +10,35 @@ use crate::{
block::{Block, Height, MAX_BLOCK_BYTES},
parameters::{Network, NetworkUpgrade},
serialization::{SerializationError, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize},
transaction::txid::TxIdBuilder,
transaction::{sighash::SigHasher, txid::TxIdBuilder},
};
use crate::{amount::Amount, transaction::Transaction};
use transparent::Script;
use zebra_test::vectors::{ZIP143_1, ZIP143_2, ZIP243_1, ZIP243_2, ZIP243_3};
macro_rules! assert_hash_eq {
($expected:literal, $hasher:expr, $f:ident) => {
let mut buf = vec![];
$hasher
.$f(&mut buf)
.expect("hashing into a vec never fails");
let expected = $expected;
let result = hex::encode(buf);
let span = tracing::span!(
tracing::Level::ERROR,
"compare_vecs",
expected.len = expected.len(),
result.len = result.len(),
hash_fn = stringify!($f)
);
let guard = span.enter();
assert_eq!(expected, result);
drop(guard);
};
}
lazy_static! {
pub static ref EMPTY_V5_TX: Transaction = Transaction::V5 {
network_upgrade: NetworkUpgrade::Nu5,
@ -431,6 +457,23 @@ fn fake_v5_librustzcash_round_trip_for_network(network: Network) {
}
}
#[test]
fn zip244_round_trip() -> Result<()> {
zebra_test::init();
for test in zip0244::TEST_VECTORS.iter() {
let transaction = test.tx.zcash_deserialize_into::<Transaction>()?;
let reencoded = transaction.zcash_serialize_to_vec()?;
assert_eq!(test.tx, reencoded);
let _alt_tx: zcash_primitives::transaction::Transaction = (&transaction)
.try_into()
.expect("librustzcash deserialization must work for zebra serialized transactions");
}
Ok(())
}
#[test]
fn zip244_txid() -> Result<()> {
zebra_test::init();
@ -444,3 +487,547 @@ fn zip244_txid() -> Result<()> {
Ok(())
}
#[test]
fn test_vec143_1() -> Result<()> {
zebra_test::init();
let transaction = ZIP143_1.zcash_deserialize_into::<Transaction>()?;
let hasher = SigHasher::new(
&transaction,
HashType::ALL,
NetworkUpgrade::Overwinter,
None,
);
assert_hash_eq!("03000080", hasher, hash_header);
assert_hash_eq!("7082c403", hasher, hash_groupid);
assert_hash_eq!(
"d53a633bbecf82fe9e9484d8a0e727c73bb9e68c96e72dec30144f6a84afa136",
hasher,
hash_prevouts
);
assert_hash_eq!(
"a5f25f01959361ee6eb56a7401210ee268226f6ce764a4f10b7f29e54db37272",
hasher,
hash_sequence
);
assert_hash_eq!(
"ab6f7f6c5ad6b56357b5f37e16981723db6c32411753e28c175e15589172194a",
hasher,
hash_outputs
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_joinsplits
);
assert_hash_eq!("481cdd86", hasher, hash_lock_time);
assert_hash_eq!("b3cc4318", hasher, hash_expiry_height);
assert_hash_eq!("01000000", hasher, hash_hash_type);
assert_hash_eq!(
"030000807082c403d53a633bbecf82fe9e9484d8a0e727c73bb9e68c96e72dec30144f6a84afa136a5f25f01959361ee6eb56a7401210ee268226f6ce764a4f10b7f29e54db37272ab6f7f6c5ad6b56357b5f37e16981723db6c32411753e28c175e15589172194a0000000000000000000000000000000000000000000000000000000000000000481cdd86b3cc431801000000",
hasher,
hash_sighash_zip143
);
let hash = hasher.sighash();
let expected = "a1f1a4e5cd9bd522322d661edd2af1bf2a7019cfab94ece18f4ba935b0a19073";
let result = hex::encode(hash);
let span = tracing::span!(
tracing::Level::ERROR,
"compare_final",
expected.len = expected.len(),
buf.len = result.len()
);
let _guard = span.enter();
assert_eq!(expected, result);
Ok(())
}
#[test]
fn test_vec143_2() -> Result<()> {
zebra_test::init();
let transaction = ZIP143_2.zcash_deserialize_into::<Transaction>()?;
let value = hex::decode("2f6e04963b4c0100")?.zcash_deserialize_into::<Amount<_>>()?;
let lock_script = Script::new(&hex::decode("53")?);
let input_ind = 1;
let hasher = SigHasher::new(
&transaction,
HashType::SINGLE,
NetworkUpgrade::Overwinter,
Some((input_ind, transparent::Output { value, lock_script })),
);
assert_hash_eq!("03000080", hasher, hash_header);
assert_hash_eq!("7082c403", hasher, hash_groupid);
assert_hash_eq!(
"92b8af1f7e12cb8de105af154470a2ae0a11e64a24a514a562ff943ca0f35d7f",
hasher,
hash_prevouts
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_sequence
);
assert_hash_eq!(
"edc32cce530f836f7c31c53656f859f514c3ff8dcae642d3e17700fdc6e829a4",
hasher,
hash_outputs
);
assert_hash_eq!(
"f59e41b40f3a60be90bee2be11b0956dfff06a6d8e22668c4f215bd87b20d514",
hasher,
hash_joinsplits
);
assert_hash_eq!("97b0e4e4", hasher, hash_lock_time);
assert_hash_eq!("c705fc05", hasher, hash_expiry_height);
assert_hash_eq!("03000000", hasher, hash_hash_type);
assert_hash_eq!(
"378af1e40f64e125946f62c2fa7b2fecbcb64b6968912a6381ce3dc166d56a1d62f5a8d7",
hasher,
hash_input_prevout
);
assert_hash_eq!("0153", hasher, hash_input_script_code);
assert_hash_eq!("2f6e04963b4c0100", hasher, hash_input_amount);
assert_hash_eq!("e8c7203d", hasher, hash_input_sequence);
let hash = hasher.sighash();
let expected = "23652e76cb13b85a0e3363bb5fca061fa791c40c533eccee899364e6e60bb4f7";
let result: &[u8] = hash.as_ref();
let result = hex::encode(result);
let span = tracing::span!(
tracing::Level::ERROR,
"compare_final",
expected.len = expected.len(),
buf.len = result.len()
);
let _guard = span.enter();
assert_eq!(expected, result);
Ok(())
}
#[test]
fn test_vec243_1() -> Result<()> {
zebra_test::init();
let transaction = ZIP243_1.zcash_deserialize_into::<Transaction>()?;
let hasher = SigHasher::new(&transaction, HashType::ALL, NetworkUpgrade::Sapling, None);
assert_hash_eq!("04000080", hasher, hash_header);
assert_hash_eq!("85202f89", hasher, hash_groupid);
assert_hash_eq!(
"d53a633bbecf82fe9e9484d8a0e727c73bb9e68c96e72dec30144f6a84afa136",
hasher,
hash_prevouts
);
assert_hash_eq!(
"a5f25f01959361ee6eb56a7401210ee268226f6ce764a4f10b7f29e54db37272",
hasher,
hash_sequence
);
assert_hash_eq!(
"ab6f7f6c5ad6b56357b5f37e16981723db6c32411753e28c175e15589172194a",
hasher,
hash_outputs
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_joinsplits
);
assert_hash_eq!(
"3fd9edb96dccf5b9aeb71e3db3710e74be4f1dfb19234c1217af26181f494a36",
hasher,
hash_shielded_spends
);
assert_hash_eq!(
"dafece799f638ba7268bf8fe43f02a5112f0bb32a84c4a8c2f508c41ff1c78b5",
hasher,
hash_shielded_outputs
);
assert_hash_eq!("481cdd86", hasher, hash_lock_time);
assert_hash_eq!("b3cc4318", hasher, hash_expiry_height);
assert_hash_eq!("442117623ceb0500", hasher, hash_value_balance);
assert_hash_eq!("01000000", hasher, hash_hash_type);
assert_hash_eq!(
"0400008085202f89d53a633bbecf82fe9e9484d8a0e727c73bb9e68c96e72dec30144f6a84afa136a5f25f01959361ee6eb56a7401210ee268226f6ce764a4f10b7f29e54db37272ab6f7f6c5ad6b56357b5f37e16981723db6c32411753e28c175e15589172194a00000000000000000000000000000000000000000000000000000000000000003fd9edb96dccf5b9aeb71e3db3710e74be4f1dfb19234c1217af26181f494a36dafece799f638ba7268bf8fe43f02a5112f0bb32a84c4a8c2f508c41ff1c78b5481cdd86b3cc4318442117623ceb050001000000",
hasher,
hash_sighash_zip243
);
let hash = hasher.sighash();
let expected = "63d18534de5f2d1c9e169b73f9c783718adbef5c8a7d55b5e7a37affa1dd3ff3";
let result = hex::encode(hash);
let span = tracing::span!(
tracing::Level::ERROR,
"compare_final",
expected.len = expected.len(),
buf.len = result.len()
);
let _guard = span.enter();
assert_eq!(expected, result);
let alt_sighash = crate::primitives::zcash_primitives::sighash(
&transaction,
HashType::ALL,
NetworkUpgrade::Sapling,
None,
);
let result = hex::encode(alt_sighash);
assert_eq!(expected, result);
Ok(())
}
#[test]
fn test_vec243_2() -> Result<()> {
zebra_test::init();
let transaction = ZIP243_2.zcash_deserialize_into::<Transaction>()?;
let value = hex::decode("adedf02996510200")?.zcash_deserialize_into::<Amount<_>>()?;
let lock_script = Script::new(&[]);
let input_ind = 1;
let hasher = SigHasher::new(
&transaction,
HashType::NONE,
NetworkUpgrade::Sapling,
Some((input_ind, transparent::Output { value, lock_script })),
);
assert_hash_eq!("04000080", hasher, hash_header);
assert_hash_eq!("85202f89", hasher, hash_groupid);
assert_hash_eq!(
"cacf0f5210cce5fa65a59f314292b3111d299e7d9d582753cf61e1e408552ae4",
hasher,
hash_prevouts
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_sequence
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_outputs
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_joinsplits
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_shielded_spends
);
assert_hash_eq!(
"b79530fcec83211d21e3c355db538c138d625784c27370e9d1039a8515a23f87",
hasher,
hash_shielded_outputs
);
assert_hash_eq!("d7034302", hasher, hash_lock_time);
assert_hash_eq!("011b9a07", hasher, hash_expiry_height);
assert_hash_eq!("6620edc067ff0200", hasher, hash_value_balance);
assert_hash_eq!("02000000", hasher, hash_hash_type);
assert_hash_eq!(
"090f47a068e227433f9e49d3aa09e356d8d66d0c0121e91a3c4aa3f27fa1b63396e2b41d",
hasher,
hash_input_prevout
);
assert_hash_eq!("00", hasher, hash_input_script_code);
assert_hash_eq!("adedf02996510200", hasher, hash_input_amount);
assert_hash_eq!("4e970568", hasher, hash_input_sequence);
assert_hash_eq!(
"0400008085202f89cacf0f5210cce5fa65a59f314292b3111d299e7d9d582753cf61e1e408552ae40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b79530fcec83211d21e3c355db538c138d625784c27370e9d1039a8515a23f87d7034302011b9a076620edc067ff020002000000090f47a068e227433f9e49d3aa09e356d8d66d0c0121e91a3c4aa3f27fa1b63396e2b41d00adedf029965102004e970568",
hasher,
hash_sighash_zip243
);
let hash = hasher.sighash();
let expected = "bbe6d84f57c56b29b914c694baaccb891297e961de3eb46c68e3c89c47b1a1db";
let result = hex::encode(hash);
let span = tracing::span!(
tracing::Level::ERROR,
"compare_final",
expected.len = expected.len(),
buf.len = result.len()
);
let _guard = span.enter();
assert_eq!(expected, result);
let lock_script = Script::new(&[]);
let prevout = transparent::Output { value, lock_script };
let index = input_ind as usize;
let inputs = transaction.inputs();
let input = Some((&prevout, &inputs[index], index));
let alt_sighash = crate::primitives::zcash_primitives::sighash(
&transaction,
HashType::NONE,
NetworkUpgrade::Sapling,
input,
);
let result = hex::encode(alt_sighash);
assert_eq!(expected, result);
Ok(())
}
#[test]
fn test_vec243_3() -> Result<()> {
zebra_test::init();
let transaction = ZIP243_3.zcash_deserialize_into::<Transaction>()?;
let value = hex::decode("80f0fa0200000000")?.zcash_deserialize_into::<Amount<_>>()?;
let lock_script = Script::new(&hex::decode(
"76a914507173527b4c3318a2aecd793bf1cfed705950cf88ac",
)?);
let input_ind = 0;
let hasher = SigHasher::new(
&transaction,
HashType::ALL,
NetworkUpgrade::Sapling,
Some((input_ind, transparent::Output { value, lock_script })),
);
assert_hash_eq!("04000080", hasher, hash_header);
assert_hash_eq!("85202f89", hasher, hash_groupid);
assert_hash_eq!(
"fae31b8dec7b0b77e2c8d6b6eb0e7e4e55abc6574c26dd44464d9408a8e33f11",
hasher,
hash_prevouts
);
assert_hash_eq!(
"6c80d37f12d89b6f17ff198723e7db1247c4811d1a695d74d930f99e98418790",
hasher,
hash_sequence
);
assert_hash_eq!(
"d2b04118469b7810a0d1cc59568320aad25a84f407ecac40b4f605a4e6868454",
hasher,
hash_outputs
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_joinsplits
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_shielded_spends
);
assert_hash_eq!(
"0000000000000000000000000000000000000000000000000000000000000000",
hasher,
hash_shielded_outputs
);
assert_hash_eq!("29b00400", hasher, hash_lock_time);
assert_hash_eq!("48b00400", hasher, hash_expiry_height);
assert_hash_eq!("0000000000000000", hasher, hash_value_balance);
assert_hash_eq!("01000000", hasher, hash_hash_type);
assert_hash_eq!(
"a8c685478265f4c14dada651969c45a65e1aeb8cd6791f2f5bb6a1d9952104d901000000",
hasher,
hash_input_prevout
);
assert_hash_eq!(
"1976a914507173527b4c3318a2aecd793bf1cfed705950cf88ac",
hasher,
hash_input_script_code
);
assert_hash_eq!("80f0fa0200000000", hasher, hash_input_amount);
assert_hash_eq!("feffffff", hasher, hash_input_sequence);
assert_hash_eq!(
"0400008085202f89fae31b8dec7b0b77e2c8d6b6eb0e7e4e55abc6574c26dd44464d9408a8e33f116c80d37f12d89b6f17ff198723e7db1247c4811d1a695d74d930f99e98418790d2b04118469b7810a0d1cc59568320aad25a84f407ecac40b4f605a4e686845400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029b0040048b00400000000000000000001000000a8c685478265f4c14dada651969c45a65e1aeb8cd6791f2f5bb6a1d9952104d9010000001976a914507173527b4c3318a2aecd793bf1cfed705950cf88ac80f0fa0200000000feffffff",
hasher,
hash_sighash_zip243
);
let hash = hasher.sighash();
let expected = "f3148f80dfab5e573d5edfe7a850f5fd39234f80b5429d3a57edcc11e34c585b";
let result = hex::encode(hash);
let span = tracing::span!(
tracing::Level::ERROR,
"compare_final",
expected.len = expected.len(),
buf.len = result.len()
);
let _guard = span.enter();
assert_eq!(expected, result);
let lock_script = Script::new(&hex::decode(
"76a914507173527b4c3318a2aecd793bf1cfed705950cf88ac",
)?);
let prevout = transparent::Output { value, lock_script };
let index = input_ind as usize;
let inputs = transaction.inputs();
let input = Some((&prevout, &inputs[index], index));
let alt_sighash = crate::primitives::zcash_primitives::sighash(
&transaction,
HashType::ALL,
NetworkUpgrade::Sapling,
input,
);
let result = hex::encode(alt_sighash);
assert_eq!(expected, result);
Ok(())
}
#[test]
fn zip244_sighash() -> Result<()> {
zebra_test::init();
for (i, test) in zip0244::TEST_VECTORS.iter().enumerate() {
let transaction = test.tx.zcash_deserialize_into::<Transaction>()?;
let input = match test.amount {
Some(amount) => Some((
test.transparent_input
.expect("test vector must have transparent_input when it has amount"),
transparent::Output {
value: amount.try_into()?,
lock_script: transparent::Script::new(
test.script_code
.as_ref()
.expect("test vector must have script_code when it has amount"),
),
},
)),
None => None,
};
let result = hex::encode(transaction.sighash(NetworkUpgrade::Nu5, HashType::ALL, input));
let expected = hex::encode(test.sighash_all);
assert_eq!(expected, result, "test #{}: sighash does not match", i);
}
Ok(())
}
/// Use librustzcash to compute sighashes and compare with zebra sighashes.
#[test]
fn librustzcash_sighash() {
zebra_test::init();
librustzcash_sighash_for_network(Network::Mainnet);
librustzcash_sighash_for_network(Network::Testnet);
}
fn librustzcash_sighash_for_network(network: Network) {
let block_iter = match network {
Network::Mainnet => zebra_test::vectors::MAINNET_BLOCKS.iter(),
Network::Testnet => zebra_test::vectors::TESTNET_BLOCKS.iter(),
};
for (height, original_bytes) in block_iter {
let original_block = original_bytes
.zcash_deserialize_into::<Block>()
.expect("block is structurally valid");
// skip blocks that are before overwinter as they will not have a valid consensus branch id
if *height
< NetworkUpgrade::Overwinter
.activation_height(network)
.expect("a valid height")
.0
{
continue;
}
let network_upgrade = NetworkUpgrade::current(network, Height(*height));
// Test each transaction. Skip the coinbase transaction
for original_tx in original_block.transactions.iter().skip(1) {
let original_sighash = original_tx.sighash(network_upgrade, HashType::ALL, None);
let alt_sighash = crate::primitives::zcash_primitives::sighash(
original_tx,
HashType::ALL,
network_upgrade,
None,
);
assert_eq!(original_sighash, alt_sighash);
}
}
}

View File

@ -19,7 +19,7 @@ use zebra_chain::{
parameters::{Network, NetworkUpgrade},
primitives::Groth16Proof,
sapling,
transaction::{self, HashType, Transaction},
transaction::{self, HashType, SigHash, Transaction},
transparent,
};
@ -398,7 +398,7 @@ where
/// Verifies a transaction's Sprout shielded join split data.
fn verify_sprout_shielded_data(
joinsplit_data: &Option<transaction::JoinSplitData<Groth16Proof>>,
shielded_sighash: &blake2b_simd::Hash,
shielded_sighash: &SigHash,
) -> AsyncChecks {
let mut checks = AsyncChecks::new();
@ -434,7 +434,7 @@ where
/// Verifies a transaction's Sapling shielded data.
fn verify_sapling_shielded_data<A>(
sapling_shielded_data: &Option<sapling::ShieldedData<A>>,
shielded_sighash: &blake2b_simd::Hash,
shielded_sighash: &SigHash,
) -> Result<AsyncChecks, TransactionError>
where
A: sapling::AnchorVariant + Clone,

View File

@ -620,7 +620,7 @@ fn v4_with_signed_sprout_transfer_is_accepted() {
Transaction::V4 {
joinsplit_data: Some(joinsplit_data),
..
} => joinsplit_data.sig = signing_key.sign(sighash.as_bytes()),
} => joinsplit_data.sig = signing_key.sign(sighash.as_ref()),
_ => unreachable!("Mock transaction was created incorrectly"),
}

View File

@ -7,6 +7,8 @@ pub struct TestVector {
pub tx: Vec<u8>,
/// The expected transaction ID.
pub txid: [u8; 32],
/// The expected auth digest.
pub auth_digest: [u8; 32],
/// Which transparent input the ID refers to, if any.
pub transparent_input: Option<u32>,
/// The script code for the given transparent input, if any.
@ -288,6 +290,11 @@ lazy_static! {
0x8b, 0x3c, 0x87, 0xc7, 0x4a, 0x2a, 0x63, 0xa2, 0x89, 0xd3, 0x00, 0x05, 0xda, 0xd6,
0x98, 0x3d, 0x95, 0x44,
],
auth_digest: [
0x1c, 0xbb, 0xe0, 0xd7, 0x25, 0x4c, 0xa6, 0x52, 0xcd, 0xda, 0xa7, 0xa9, 0xd2, 0x91,
0x5a, 0x60, 0x9e, 0x35, 0x73, 0xc0, 0x3d, 0x27, 0x05, 0xe9, 0xad, 0xd4, 0xe3, 0x2e,
0xec, 0x0d, 0x76, 0x9e,
],
transparent_input: Some(0),
script_code: Some(vec![0x65, 0x00, 0x51]),
amount: Some(570688904498311),
@ -459,6 +466,11 @@ lazy_static! {
0xa5, 0xe4, 0xfc, 0xb7, 0xbf, 0xa7, 0xda, 0x79, 0x29, 0xbf, 0xb7, 0x31, 0xac, 0x10,
0xa5, 0x8a, 0xb0, 0x03,
],
auth_digest: [
0x64, 0xed, 0x51, 0x50, 0x16, 0x96, 0xf1, 0x14, 0x32, 0xb8, 0xa1, 0xe2, 0xe6, 0x87,
0xb8, 0x9e, 0x61, 0x25, 0x97, 0xfd, 0x47, 0x49, 0xf9, 0x2c, 0x0f, 0x5f, 0x51, 0x71,
0xfc, 0xad, 0x78, 0xbe,
],
transparent_input: None,
script_code: None,
amount: None,
@ -483,6 +495,11 @@ lazy_static! {
0xf9, 0x57, 0x6a, 0xb7, 0x6b, 0xff, 0xc7, 0x1b, 0xcd, 0x98, 0x5b, 0x62, 0xdd, 0xd6,
0x9d, 0x29, 0x97, 0xcd,
],
auth_digest: [
0x04, 0xda, 0x78, 0xb6, 0x64, 0x11, 0x1d, 0xe8, 0xe4, 0xfc, 0xfc, 0x14, 0x93, 0x3d,
0x79, 0xf6, 0xd9, 0x60, 0xda, 0xd8, 0xf1, 0x08, 0xf1, 0xe0, 0xb4, 0x26, 0xcc, 0x20,
0x36, 0x29, 0x22, 0xed,
],
transparent_input: None,
script_code: None,
amount: None,
@ -508,6 +525,11 @@ lazy_static! {
0xe3, 0x4f, 0xd5, 0xf9, 0xc7, 0x9c, 0x9f, 0x78, 0xdc, 0x65, 0x05, 0x1a, 0x9a, 0x14,
0xca, 0xc2, 0xb1, 0xbf,
],
auth_digest: [
0x04, 0xda, 0x78, 0xb6, 0x64, 0x11, 0x1d, 0xe8, 0xe4, 0xfc, 0xfc, 0x14, 0x93, 0x3d,
0x79, 0xf6, 0xd9, 0x60, 0xda, 0xd8, 0xf1, 0x08, 0xf1, 0xe0, 0xb4, 0x26, 0xcc, 0x20,
0x36, 0x29, 0x22, 0xed,
],
transparent_input: None,
script_code: None,
amount: None,
@ -544,6 +566,11 @@ lazy_static! {
0x90, 0x93, 0x03, 0xd4, 0x58, 0x0e, 0x72, 0x3a, 0xd3, 0x16, 0x2c, 0x06, 0x09, 0x66,
0x48, 0xa2, 0x5b, 0x1e,
],
auth_digest: [
0x9e, 0x7c, 0x68, 0x39, 0xb3, 0x1f, 0xb3, 0xe9, 0x12, 0xb6, 0x93, 0xd1, 0x87, 0x9f,
0xbb, 0xad, 0xdb, 0xe7, 0xa7, 0x4a, 0x50, 0x8e, 0x7b, 0x6d, 0x1f, 0xe1, 0x93, 0x82,
0x68, 0xc0, 0x4a, 0xb3,
],
transparent_input: Some(1),
script_code: Some(vec![0xac, 0x00, 0x00]),
amount: Some(693972628630138),
@ -800,6 +827,11 @@ lazy_static! {
0x1f, 0x14, 0x12, 0xe3, 0x8f, 0x89, 0x13, 0x07, 0x52, 0x80, 0xcd, 0x2b, 0x38, 0x02,
0xf5, 0xb2, 0x70, 0x4e,
],
auth_digest: [
0xee, 0x3a, 0x4a, 0x6d, 0xd3, 0x89, 0xf2, 0x81, 0xb6, 0xd6, 0xe3, 0xd8, 0xe0, 0xf7,
0x97, 0xa3, 0xd4, 0xfa, 0x01, 0x4e, 0x3f, 0x41, 0x6a, 0x0e, 0x47, 0x68, 0x4f, 0x76,
0x02, 0x15, 0x44, 0x58,
],
transparent_input: None,
script_code: None,
amount: None,
@ -831,6 +863,11 @@ lazy_static! {
0xbc, 0xdf, 0x2d, 0x35, 0xd0, 0x13, 0x55, 0x80, 0x4b, 0xf0, 0xe3, 0xee, 0x1b, 0x17,
0xde, 0x9e, 0x46, 0xcd,
],
auth_digest: [
0x2c, 0xbe, 0xf7, 0x5b, 0x3e, 0x2c, 0xff, 0x88, 0xb6, 0xf1, 0x20, 0xbf, 0x70, 0xcb,
0x3e, 0xc7, 0x57, 0xec, 0x25, 0xd3, 0x11, 0xfa, 0xca, 0xfd, 0xfe, 0x2d, 0xb4, 0x2e,
0xd3, 0x42, 0x04, 0x0f,
],
transparent_input: Some(0),
script_code: Some(vec![0x63, 0x52, 0x51, 0x63, 0x53]),
amount: Some(107504874564564),
@ -1092,6 +1129,11 @@ lazy_static! {
0x2a, 0xd9, 0x06, 0x50, 0x34, 0x22, 0x5d, 0xad, 0x9c, 0x89, 0xbf, 0xcb, 0x73, 0x32,
0x8d, 0x3d, 0x4f, 0xe6,
],
auth_digest: [
0xbb, 0x48, 0xcf, 0x27, 0x71, 0x54, 0x6c, 0x2c, 0x15, 0x57, 0x7d, 0xb2, 0x0d, 0x04,
0x86, 0x5a, 0x8c, 0xc0, 0x0c, 0x9c, 0x02, 0xec, 0xb3, 0x10, 0x24, 0x94, 0x31, 0x0f,
0x18, 0x68, 0x50, 0xcf,
],
transparent_input: None,
script_code: None,
amount: None,
@ -1200,6 +1242,11 @@ lazy_static! {
0x01, 0x54, 0xeb, 0x2d, 0xeb, 0x76, 0x78, 0x74, 0xa3, 0x1b, 0x5d, 0x10, 0xaa, 0xf7,
0x6b, 0xa8, 0x4f, 0xae,
],
auth_digest: [
0xf1, 0x58, 0x29, 0x64, 0xb8, 0xc9, 0xec, 0x20, 0x29, 0xab, 0x97, 0xa2, 0x55, 0x98,
0xdb, 0xff, 0x28, 0x35, 0x23, 0xe6, 0xf3, 0x7a, 0xdb, 0x19, 0xcc, 0x57, 0x69, 0xb5,
0x13, 0xc7, 0x33, 0xc0,
],
transparent_input: None,
script_code: None,
amount: None,
@ -1497,6 +1544,11 @@ lazy_static! {
0xde, 0x07, 0x15, 0x27, 0x5d, 0x15, 0x6c, 0xda, 0xb9, 0x6f, 0x68, 0xdc, 0x70, 0x10,
0x58, 0x3b, 0x02, 0xaa,
],
auth_digest: [
0x87, 0x46, 0xc9, 0x9d, 0x98, 0x57, 0x86, 0xb1, 0xb7, 0x7c, 0x40, 0x5f, 0x6e, 0xe8,
0x31, 0xe2, 0x98, 0x71, 0xca, 0x9d, 0x68, 0xe7, 0x72, 0x4f, 0xb7, 0xf8, 0x78, 0x6f,
0x28, 0x18, 0x42, 0xa6,
],
transparent_input: Some(0),
script_code: Some(vec![]),
amount: Some(1405243945822387),