Always compute sighash with librustzcash (#2469)

* Always use librustzcash for sighash and remove old sighash code

Also added ZIP-143 test vectors

* Remove librustzcash_sighash test that is no longer needed
This commit is contained in:
Conrado Gouvea 2021-07-08 20:55:08 -03:00 committed by GitHub
parent d1ab8a8946
commit 40e350c342
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 1739 additions and 823 deletions

View File

@ -2,35 +2,11 @@
use super::Transaction;
use crate::{
parameters::{
ConsensusBranchId, NetworkUpgrade, OVERWINTER_VERSION_GROUP_ID, SAPLING_VERSION_GROUP_ID,
TX_V5_VERSION_GROUP_ID,
},
sapling,
serialization::{WriteZcashExt, ZcashSerialize},
transparent,
};
use byteorder::{LittleEndian, WriteBytesExt};
use std::{
convert::TryInto,
io::{self, Write},
};
use crate::{parameters::NetworkUpgrade, transparent};
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";
const ZCASH_SIGHASH_PERSONALIZATION_PREFIX: &[u8; 12] = b"ZcashSigHash";
const ZCASH_PREVOUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashPrevoutHash";
const ZCASH_SEQUENCE_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSequencHash";
const ZCASH_OUTPUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashOutputsHash";
const ZCASH_JOINSPLITS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashJSplitsHash";
const ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSSpendsHash";
const ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashSOutputHash";
bitflags::bitflags! {
/// The different SigHash types, as defined in https://zips.z.cash/zip-0143
@ -46,12 +22,6 @@ bitflags::bitflags! {
}
}
impl HashType {
fn masked(self) -> Self {
Self::from_bits_truncate(self.bits & 0b0001_1111)
}
}
/// A Signature Hash (or SIGHASH) as specified in
/// https://zips.z.cash/protocol/protocol.pdf#sighash
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
@ -102,458 +72,17 @@ impl<'a> SigHasher<'a> {
pub(super) fn sighash(self) -> SigHash {
use NetworkUpgrade::*;
let mut hash = blake2b_simd::Params::new()
.hash_length(32)
.personal(&self.personal())
.to_state();
match self.network_upgrade {
Genesis | BeforeOverwinter => unreachable!(ZIP143_EXPLANATION),
Overwinter => self
.hash_sighash_zip143(&mut hash)
.expect("serialization into hasher never fails"),
Sapling | Blossom | Heartwood | Canopy => self
.hash_sighash_zip243(&mut hash)
.expect("serialization into hasher never fails"),
Nu5 => return self.hash_sighash_zip244(),
}
SigHash(hash.finalize().as_ref().try_into().unwrap())
}
fn consensus_branch_id(&self) -> ConsensusBranchId {
self.network_upgrade.branch_id().expect(ZIP143_EXPLANATION)
}
/// Sighash implementation for the overwinter network upgrade
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)?;
self.hash_sequence(&mut writer)?;
self.hash_outputs(&mut writer)?;
self.hash_joinsplits(&mut writer)?;
self.hash_lock_time(&mut writer)?;
self.hash_expiry_height(&mut writer)?;
self.hash_hash_type(&mut writer)?;
self.hash_input(&mut writer)?;
Ok(())
}
pub(super) fn personal(&self) -> [u8; 16] {
let mut personal = [0; 16];
(&mut personal[..12]).copy_from_slice(ZCASH_SIGHASH_PERSONALIZATION_PREFIX);
(&mut personal[12..])
.write_u32::<LittleEndian>(self.consensus_branch_id().into())
.unwrap();
personal
}
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 {
Transaction::V1 { .. } | Transaction::V2 { .. } => unreachable!(ZIP143_EXPLANATION),
Transaction::V3 { .. } => 3 | overwintered_flag,
Transaction::V4 { .. } => 4 | overwintered_flag,
Transaction::V5 { .. } => 5 | overwintered_flag,
})
}
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,
Transaction::V4 { .. } => SAPLING_VERSION_GROUP_ID,
Transaction::V5 { .. } => TX_V5_VERSION_GROUP_ID,
})
}
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]);
}
let mut hash = blake2b_simd::Params::new()
.hash_length(32)
.personal(ZCASH_PREVOUTS_HASH_PERSONALIZATION)
.to_state();
self.trans
.inputs()
.iter()
.map(|input| match input {
transparent::Input::PrevOut { outpoint, .. } => outpoint,
transparent::Input::Coinbase { .. } => &transparent::OutPoint {
hash: crate::transaction::Hash([0; 32]),
index: u32::MAX,
},
})
.try_for_each(|outpoint| outpoint.zcash_serialize(&mut hash))?;
writer.write_all(hash.finalize().as_ref())
}
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
{
return writer.write_all(&[0; 32]);
}
let mut hash = blake2b_simd::Params::new()
.hash_length(32)
.personal(ZCASH_SEQUENCE_HASH_PERSONALIZATION)
.to_state();
self.trans
.inputs()
.iter()
.map(|input| match input {
transparent::Input::PrevOut { sequence, .. } => sequence,
transparent::Input::Coinbase { sequence, .. } => sequence,
})
.try_for_each(|sequence| (&mut hash).write_u32::<LittleEndian>(*sequence))?;
writer.write_all(hash.finalize().as_ref())
}
/// Writes the u256 hash of the transactions outputs to the provided Writer
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)
} else if self.hash_type.masked() == HashType::SINGLE
&& self
.input
.as_ref()
.map(|&(_, _, index)| index)
.map(|index| index < self.trans.outputs().len())
.unwrap_or(false)
{
self.single_output_hash(writer)
} else {
writer.write_all(&[0; 32])
}
}
fn outputs_hash<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
let mut hash = blake2b_simd::Params::new()
.hash_length(32)
.personal(ZCASH_OUTPUTS_HASH_PERSONALIZATION)
.to_state();
self.trans
.outputs()
.iter()
.try_for_each(|output| output.zcash_serialize(&mut hash))?;
writer.write_all(hash.finalize().as_ref())
}
fn single_output_hash<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
let &(_, _, output) = self
.input
.as_ref()
.expect("already checked index is some in `hash_outputs`");
let output = &self.trans.outputs()[output];
let mut hash = blake2b_simd::Params::new()
.hash_length(32)
.personal(ZCASH_OUTPUTS_HASH_PERSONALIZATION)
.to_state();
output.zcash_serialize(&mut hash)?;
writer.write_all(hash.finalize().as_ref())
}
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(),
Transaction::V4 { joinsplit_data, .. } => joinsplit_data.is_some(),
Transaction::V5 { .. } => {
unimplemented!("v5 transaction hash as specified in ZIP-225 and ZIP-244")
Overwinter | Sapling | Blossom | Heartwood | Canopy | Nu5 => {
self.hash_sighash_librustzcash()
}
};
if !has_joinsplits {
return writer.write_all(&[0; 32]);
}
let mut hash = blake2b_simd::Params::new()
.hash_length(32)
.personal(ZCASH_JOINSPLITS_HASH_PERSONALIZATION)
.to_state();
// This code, and the check above for has_joinsplits cannot be combined
// into a single branch because the `joinsplit_data` type of each
// transaction kind has a different type.
//
// For v3 joinsplit_data is a JoinSplitData<Bctv14Proof>
// For v4 joinsplit_data is a JoinSplitData<Groth16Proof>
//
// The type parameter on these types prevents them from being unified,
// which forces us to duplicate the logic in each branch even though the
// code within each branch is identical.
//
// TODO: use a generic function to remove the duplicate code
match self.trans {
Transaction::V3 {
joinsplit_data: Some(jsd),
..
} => {
for joinsplit in jsd.joinsplits() {
joinsplit.zcash_serialize(&mut hash)?;
}
(&mut hash).write_all(&<[u8; 32]>::from(jsd.pub_key)[..])?;
}
Transaction::V4 {
joinsplit_data: Some(jsd),
..
} => {
for joinsplit in jsd.joinsplits() {
joinsplit.zcash_serialize(&mut hash)?;
}
(&mut hash).write_all(&<[u8; 32]>::from(jsd.pub_key)[..])?;
}
Transaction::V5 { .. } => {
unimplemented!("v5 transaction hash as specified in ZIP-225 and ZIP-244")
}
Transaction::V1 { .. }
| Transaction::V2 { .. }
| Transaction::V3 {
joinsplit_data: None,
..
}
| Transaction::V4 {
joinsplit_data: None,
..
} => unreachable!("already checked transaction kind above"),
};
writer.write_all(hash.finalize().as_ref())
}
pub(super) fn hash_lock_time<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
self.trans.lock_time().zcash_serialize(&mut writer)
}
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)
}
pub(super) fn hash_hash_type<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_u32::<LittleEndian>(self.hash_type.bits())
}
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)?;
self.hash_input_amount(&mut writer)?;
self.hash_input_sequence(&mut writer)?;
}
Ok(())
}
pub(super) fn hash_input_prevout<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
let (_, input, _) = self
.input
.as_ref()
.expect("caller verifies input is not none");
let outpoint = match input {
transparent::Input::PrevOut { outpoint, .. } => outpoint,
transparent::Input::Coinbase { .. } => {
unreachable!("sighash should only ever be called for valid Input types")
}
};
outpoint.zcash_serialize(&mut writer)?;
Ok(())
}
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()
.expect("caller verifies input is not none");
lock_script.zcash_serialize(&mut writer)?;
Ok(())
}
pub(super) fn hash_input_amount<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
let (transparent::Output { value, .. }, _, _) = self
.input
.as_ref()
.expect("caller verifies input is not none");
writer.write_all(&value.to_bytes())?;
Ok(())
}
pub(super) fn hash_input_sequence<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
let (_, input, _) = self
.input
.as_ref()
.expect("caller verifies input is not none");
let sequence = match input {
transparent::Input::PrevOut { sequence, .. } => sequence,
transparent::Input::Coinbase { .. } => {
unreachable!("sighash should only ever be called for valid Input types")
}
};
writer.write_u32::<LittleEndian>(*sequence)?;
Ok(())
}
// ********************
// * ZIP243 ADDITIONS *
// ********************
/// Sighash implementation for the sapling network upgrade and every
/// subsequent network upgrade
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)?;
self.hash_sequence(&mut writer)?;
self.hash_outputs(&mut writer)?;
self.hash_joinsplits(&mut writer)?;
self.hash_shielded_spends(&mut writer)?;
self.hash_shielded_outputs(&mut writer)?;
self.hash_lock_time(&mut writer)?;
self.hash_expiry_height(&mut writer)?;
self.hash_value_balance(&mut writer)?;
self.hash_hash_type(&mut writer)?;
self.hash_input(&mut writer)?;
Ok(())
}
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 {
V4 {
sapling_shielded_data: Some(sapling_shielded_data),
..
} => sapling_shielded_data,
V4 {
sapling_shielded_data: None,
..
} => return writer.write_all(&[0; 32]),
V5 { .. } => unimplemented!("v5 transaction hash as specified in ZIP-225 and ZIP-244"),
V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(ZIP243_EXPLANATION),
};
if sapling_shielded_data.spends().next().is_none() {
return writer.write_all(&[0; 32]);
}
let mut hash = blake2b_simd::Params::new()
.hash_length(32)
.personal(ZCASH_SHIELDED_SPENDS_HASH_PERSONALIZATION)
.to_state();
// TODO: make a generic wrapper in `spends.rs` that does this serialization
for spend in sapling_shielded_data.spends() {
// This is the canonical transaction serialization, minus the `spendAuthSig`.
spend.cv.zcash_serialize(&mut hash)?;
// TODO: ZIP-243 Sapling to Canopy only
hash.write_all(&spend.per_spend_anchor.0[..])?;
hash.write_32_bytes(&spend.nullifier.into())?;
hash.write_all(&<[u8; 32]>::from(spend.rk)[..])?;
spend.zkproof.zcash_serialize(&mut hash)?;
}
writer.write_all(hash.finalize().as_ref())
}
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 {
V4 {
sapling_shielded_data: Some(sapling_shielded_data),
..
} => sapling_shielded_data,
V4 {
sapling_shielded_data: None,
..
} => return writer.write_all(&[0; 32]),
V5 { .. } => unimplemented!("v5 transaction hash as specified in ZIP-225 and ZIP-244"),
V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(ZIP243_EXPLANATION),
};
if sapling_shielded_data.outputs().next().is_none() {
return writer.write_all(&[0; 32]);
}
let mut hash = blake2b_simd::Params::new()
.hash_length(32)
.personal(ZCASH_SHIELDED_OUTPUTS_HASH_PERSONALIZATION)
.to_state();
// Correctness: checked for V4 transaction above
for output in sapling_shielded_data
.outputs()
.cloned()
.map(sapling::OutputInTransactionV4)
{
output.zcash_serialize(&mut hash)?;
}
writer.write_all(hash.finalize().as_ref())
}
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::*;
let value_balance = match self.trans {
V4 {
sapling_shielded_data,
..
} => match sapling_shielded_data {
Some(s) => s.value_balance,
None => Amount::try_from(0).unwrap(),
},
V5 { .. } => unimplemented!("v5 transaction hash as specified in ZIP-225 and ZIP-244"),
V1 { .. } | V2 { .. } | V3 { .. } => unreachable!(ZIP243_EXPLANATION),
};
writer.write_all(&value_balance.to_bytes())?;
Ok(())
}
/// Compute a signature hash for V5 transactions according to ZIP-244.
fn hash_sighash_zip244(&self) -> SigHash {
/// Compute a signature hash using librustzcash.
fn hash_sighash_librustzcash(&self) -> SigHash {
let input = self
.input
.as_ref()

View File

@ -3,7 +3,7 @@ use std::convert::{TryFrom, TryInto};
use color_eyre::eyre::Result;
use lazy_static::lazy_static;
use zebra_test::{zip0243, zip0244};
use zebra_test::{zip0143, zip0243, zip0244};
use super::super::*;
use crate::{
@ -18,27 +18,6 @@ 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,
@ -501,46 +480,6 @@ fn test_vec143_1() -> Result<()> {
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);
@ -573,52 +512,6 @@ fn test_vec143_2() -> Result<()> {
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();
@ -643,60 +536,6 @@ fn test_vec243_1() -> Result<()> {
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);
@ -738,72 +577,6 @@ fn test_vec243_2() -> Result<()> {
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);
@ -853,76 +626,6 @@ fn test_vec243_3() -> Result<()> {
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);
@ -955,6 +658,37 @@ fn test_vec243_3() -> Result<()> {
Ok(())
}
#[test]
fn zip143_sighash() -> Result<()> {
zebra_test::init();
for (i, test) in zip0143::TEST_VECTORS.iter().enumerate() {
let transaction = test.tx.zcash_deserialize_into::<Transaction>()?;
let input = match test.transparent_input {
Some(transparent_input) => Some((
transparent_input,
transparent::Output {
value: test.amount.try_into()?,
lock_script: transparent::Script::new(test.script_code.as_ref()),
},
)),
None => None,
};
let result = hex::encode(
transaction.sighash(
NetworkUpgrade::from_branch_id(test.consensus_branch_id)
.expect("must be a valid branch ID"),
HashType::from_bits(test.hash_type).expect("must be a valid HashType"),
input,
),
);
let expected = hex::encode(test.sighash);
assert_eq!(expected, result, "test #{}: sighash does not match", i);
}
Ok(())
}
#[test]
fn zip243_sighash() -> Result<()> {
zebra_test::init();
@ -1015,53 +749,6 @@ fn zip244_sighash() -> Result<()> {
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));
for original_tx in original_block.transactions.iter() {
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);
}
}
}
#[test]
fn binding_signatures() {
zebra_test::init();

View File

@ -201,8 +201,6 @@ fn v5_coinbase_transaction_with_enable_spends_flag_fails_validation() {
}
#[tokio::test]
// TODO: Remove `should_panic` once V5 transaction sighash is implemened by the merge of #2165.
#[should_panic]
async fn v5_transaction_is_rejected_before_nu5_activation() {
const V5_TRANSACTION_VERSION: u32 = 5;

View File

@ -24,6 +24,7 @@ pub mod net;
pub mod prelude;
pub mod transcript;
pub mod vectors;
pub mod zip0143;
pub mod zip0243;
pub mod zip0244;

1701
zebra-test/src/zip0143.rs Normal file

File diff suppressed because it is too large Load Diff