diff --git a/zcash_primitives/src/transaction/components.rs b/zcash_primitives/src/transaction/components.rs index e355eb1ec..7699681d0 100644 --- a/zcash_primitives/src/transaction/components.rs +++ b/zcash_primitives/src/transaction/components.rs @@ -159,17 +159,22 @@ impl TzeIn { }) } - pub fn write(&self, mut writer: W) -> io::Result<()> { + pub fn write_without_witness(&self, mut writer: W) -> io::Result<()> { self.prevout.write(&mut writer)?; CompactSize::write( &mut writer, usize::try_from(self.witness.extension_id).map_err(|e| to_io_error(e))?, )?; + CompactSize::write( &mut writer, usize::try_from(self.witness.mode).map_err(|e| to_io_error(e))?, - )?; + ) + } + + pub fn write(&self, mut writer: W) -> io::Result<()> { + self.write_without_witness(&mut writer)?; Vector::write(&mut writer, &self.witness.payload, |w, b| w.write_u8(*b)) } } diff --git a/zcash_primitives/src/transaction/sighash.rs b/zcash_primitives/src/transaction/sighash.rs index bdcb70523..28a686554 100644 --- a/zcash_primitives/src/transaction/sighash.rs +++ b/zcash_primitives/src/transaction/sighash.rs @@ -17,6 +17,9 @@ 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"; +const ZCASH_TZE_INPUTS_HASH_PERSONALIZATION: &[u8; 16] = b"Zcash_TzeInsHash"; +const ZCASH_TZE_OUTPUTS_HASH_PERSONALIZATION: &[u8; 16] = b"ZcashTzeOutsHash"; + const ZCASH_TZE_SIGNED_INPUT_DOMAIN_SEPARATOR: &[u8; 16] = b"ZcashTZESigInput"; const ZCASH_TRANSPARENT_SIGNED_INPUT_DOMAIN_SEPARATOR: &[u8; 16] = b"Zcash_TransInput"; @@ -155,6 +158,28 @@ fn shielded_outputs_hash(tx: &TransactionData) -> Blake2bHash { .hash(&data) } +fn tze_inputs_hash(tx: &TransactionData) -> Blake2bHash { + let mut data = vec![]; + for tzein in &tx.tze_inputs { + tzein.write_without_witness(&mut data).unwrap(); + } + Blake2bParams::new() + .hash_length(32) + .personal(ZCASH_TZE_INPUTS_HASH_PERSONALIZATION) + .hash(&data) +} + +fn tze_outputs_hash(tx: &TransactionData) -> Blake2bHash { + let mut data = vec![]; + for tzeout in &tx.tze_outputs { + tzeout.write(&mut data).unwrap(); + } + Blake2bParams::new() + .hash_length(32) + .personal(ZCASH_TZE_OUTPUTS_HASH_PERSONALIZATION) + .hash(&data) +} + pub enum SignableInput<'a> { Shielded, Transparent { @@ -233,8 +258,12 @@ pub fn signature_hash_data<'a>( } else { h.update(&[0; 32]); }; + if sigversion == SigHashVersion::Future { + update_hash!(h, !tx.tze_inputs.is_empty(), tze_inputs_hash(tx)); + update_hash!(h, !tx.tze_outputs.is_empty(), tze_outputs_hash(tx)); + } update_hash!(h, !tx.joinsplits.is_empty(), joinsplits_hash(tx)); - if sigversion == SigHashVersion::Sapling { + if sigversion == SigHashVersion::Sapling || sigversion == SigHashVersion::Future { update_hash!(h, !tx.shielded_spends.is_empty(), shielded_spends_hash(tx)); update_hash!( h, @@ -244,7 +273,7 @@ pub fn signature_hash_data<'a>( } update_u32!(h, tx.lock_time, tmp); update_u32!(h, tx.expiry_height, tmp); - if sigversion == SigHashVersion::Sapling { + if sigversion == SigHashVersion::Sapling || sigversion == SigHashVersion::Future { h.update(&tx.value_balance.to_i64_le_bytes()); } update_u32!(h, hash_type, tmp);