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:
parent
d1ab8a8946
commit
40e350c342
|
@ -2,35 +2,11 @@
|
||||||
|
|
||||||
use super::Transaction;
|
use super::Transaction;
|
||||||
|
|
||||||
use crate::{
|
use crate::{parameters::NetworkUpgrade, transparent};
|
||||||
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::primitives::zcash_primitives::sighash;
|
use crate::primitives::zcash_primitives::sighash;
|
||||||
|
|
||||||
static ZIP143_EXPLANATION: &str = "Invalid transaction version: after Overwinter activation transaction versions 1 and 2 are rejected";
|
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! {
|
bitflags::bitflags! {
|
||||||
/// The different SigHash types, as defined in https://zips.z.cash/zip-0143
|
/// 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
|
/// A Signature Hash (or SIGHASH) as specified in
|
||||||
/// https://zips.z.cash/protocol/protocol.pdf#sighash
|
/// https://zips.z.cash/protocol/protocol.pdf#sighash
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||||
|
@ -102,458 +72,17 @@ impl<'a> SigHasher<'a> {
|
||||||
|
|
||||||
pub(super) fn sighash(self) -> SigHash {
|
pub(super) fn sighash(self) -> SigHash {
|
||||||
use NetworkUpgrade::*;
|
use NetworkUpgrade::*;
|
||||||
let mut hash = blake2b_simd::Params::new()
|
|
||||||
.hash_length(32)
|
|
||||||
.personal(&self.personal())
|
|
||||||
.to_state();
|
|
||||||
|
|
||||||
match self.network_upgrade {
|
match self.network_upgrade {
|
||||||
Genesis | BeforeOverwinter => unreachable!(ZIP143_EXPLANATION),
|
Genesis | BeforeOverwinter => unreachable!(ZIP143_EXPLANATION),
|
||||||
Overwinter => self
|
Overwinter | Sapling | Blossom | Heartwood | Canopy | Nu5 => {
|
||||||
.hash_sighash_zip143(&mut hash)
|
self.hash_sighash_librustzcash()
|
||||||
.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")
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
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> {
|
/// Compute a signature hash using librustzcash.
|
||||||
self.trans.lock_time().zcash_serialize(&mut writer)
|
fn hash_sighash_librustzcash(&self) -> SigHash {
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
let input = self
|
let input = self
|
||||||
.input
|
.input
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::convert::{TryFrom, TryInto};
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use zebra_test::{zip0243, zip0244};
|
use zebra_test::{zip0143, zip0243, zip0244};
|
||||||
|
|
||||||
use super::super::*;
|
use super::super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -18,27 +18,6 @@ use crate::{amount::Amount, transaction::Transaction};
|
||||||
use transparent::Script;
|
use transparent::Script;
|
||||||
use zebra_test::vectors::{ZIP143_1, ZIP143_2, ZIP243_1, ZIP243_2, ZIP243_3};
|
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! {
|
lazy_static! {
|
||||||
pub static ref EMPTY_V5_TX: Transaction = Transaction::V5 {
|
pub static ref EMPTY_V5_TX: Transaction = Transaction::V5 {
|
||||||
network_upgrade: NetworkUpgrade::Nu5,
|
network_upgrade: NetworkUpgrade::Nu5,
|
||||||
|
@ -501,46 +480,6 @@ fn test_vec143_1() -> Result<()> {
|
||||||
None,
|
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 hash = hasher.sighash();
|
||||||
let expected = "a1f1a4e5cd9bd522322d661edd2af1bf2a7019cfab94ece18f4ba935b0a19073";
|
let expected = "a1f1a4e5cd9bd522322d661edd2af1bf2a7019cfab94ece18f4ba935b0a19073";
|
||||||
let result = hex::encode(hash);
|
let result = hex::encode(hash);
|
||||||
|
@ -573,52 +512,6 @@ fn test_vec143_2() -> Result<()> {
|
||||||
Some((input_ind, transparent::Output { value, lock_script })),
|
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 hash = hasher.sighash();
|
||||||
let expected = "23652e76cb13b85a0e3363bb5fca061fa791c40c533eccee899364e6e60bb4f7";
|
let expected = "23652e76cb13b85a0e3363bb5fca061fa791c40c533eccee899364e6e60bb4f7";
|
||||||
let result: &[u8] = hash.as_ref();
|
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);
|
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 hash = hasher.sighash();
|
||||||
let expected = "63d18534de5f2d1c9e169b73f9c783718adbef5c8a7d55b5e7a37affa1dd3ff3";
|
let expected = "63d18534de5f2d1c9e169b73f9c783718adbef5c8a7d55b5e7a37affa1dd3ff3";
|
||||||
let result = hex::encode(hash);
|
let result = hex::encode(hash);
|
||||||
|
@ -738,72 +577,6 @@ fn test_vec243_2() -> Result<()> {
|
||||||
Some((input_ind, transparent::Output { value, lock_script })),
|
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 hash = hasher.sighash();
|
||||||
let expected = "bbe6d84f57c56b29b914c694baaccb891297e961de3eb46c68e3c89c47b1a1db";
|
let expected = "bbe6d84f57c56b29b914c694baaccb891297e961de3eb46c68e3c89c47b1a1db";
|
||||||
let result = hex::encode(hash);
|
let result = hex::encode(hash);
|
||||||
|
@ -853,76 +626,6 @@ fn test_vec243_3() -> Result<()> {
|
||||||
Some((input_ind, transparent::Output { value, lock_script })),
|
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 hash = hasher.sighash();
|
||||||
let expected = "f3148f80dfab5e573d5edfe7a850f5fd39234f80b5429d3a57edcc11e34c585b";
|
let expected = "f3148f80dfab5e573d5edfe7a850f5fd39234f80b5429d3a57edcc11e34c585b";
|
||||||
let result = hex::encode(hash);
|
let result = hex::encode(hash);
|
||||||
|
@ -955,6 +658,37 @@ fn test_vec243_3() -> Result<()> {
|
||||||
Ok(())
|
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]
|
#[test]
|
||||||
fn zip243_sighash() -> Result<()> {
|
fn zip243_sighash() -> Result<()> {
|
||||||
zebra_test::init();
|
zebra_test::init();
|
||||||
|
@ -1015,53 +749,6 @@ fn zip244_sighash() -> Result<()> {
|
||||||
Ok(())
|
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]
|
#[test]
|
||||||
fn binding_signatures() {
|
fn binding_signatures() {
|
||||||
zebra_test::init();
|
zebra_test::init();
|
||||||
|
|
|
@ -201,8 +201,6 @@ fn v5_coinbase_transaction_with_enable_spends_flag_fails_validation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[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() {
|
async fn v5_transaction_is_rejected_before_nu5_activation() {
|
||||||
const V5_TRANSACTION_VERSION: u32 = 5;
|
const V5_TRANSACTION_VERSION: u32 = 5;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ pub mod net;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod transcript;
|
pub mod transcript;
|
||||||
pub mod vectors;
|
pub mod vectors;
|
||||||
|
pub mod zip0143;
|
||||||
pub mod zip0243;
|
pub mod zip0243;
|
||||||
pub mod zip0244;
|
pub mod zip0244;
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue