diff --git a/zcash_primitives/src/transaction/mod.rs b/zcash_primitives/src/transaction/mod.rs index 17acacdea..1c8f2c4af 100644 --- a/zcash_primitives/src/transaction/mod.rs +++ b/zcash_primitives/src/transaction/mod.rs @@ -1,6 +1,7 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use sapling_crypto::redjubjub::Signature; use std::io::{self, Read, Write}; +use std::ops::Deref; use serialize::Vector; @@ -10,6 +11,8 @@ mod sighash; #[cfg(test)] mod tests; +pub use self::sighash::{signature_hash, signature_hash_data, SIGHASH_ALL}; + use self::components::{Amount, JSDescription, OutputDescription, SpendDescription, TxIn, TxOut}; const OVERWINTER_VERSION_GROUP_ID: u32 = 0x03C48270; @@ -18,21 +21,64 @@ const SAPLING_VERSION_GROUP_ID: u32 = 0x892F2085; const SAPLING_TX_VERSION: u32 = 4; /// A Zcash transaction. -pub struct Transaction { - overwintered: bool, - version: u32, - version_group_id: u32, - vin: Vec, - vout: Vec, - lock_time: u32, - expiry_height: u32, - value_balance: Amount, - shielded_spends: Vec, - shielded_outputs: Vec, - joinsplits: Vec, - joinsplit_pubkey: [u8; 32], - joinsplit_sig: [u8; 64], - binding_sig: Option, +pub struct Transaction(TransactionData); + +impl Deref for Transaction { + type Target = TransactionData; + + fn deref(&self) -> &TransactionData { + &self.0 + } +} + +pub struct TransactionData { + pub overwintered: bool, + pub version: u32, + pub version_group_id: u32, + pub vin: Vec, + pub vout: Vec, + pub lock_time: u32, + pub expiry_height: u32, + pub value_balance: Amount, + pub shielded_spends: Vec, + pub shielded_outputs: Vec, + pub joinsplits: Vec, + pub joinsplit_pubkey: [u8; 32], + pub joinsplit_sig: [u8; 64], + pub binding_sig: Option, +} + +impl TransactionData { + pub fn new() -> Self { + TransactionData { + overwintered: true, + version: SAPLING_TX_VERSION, + version_group_id: SAPLING_VERSION_GROUP_ID, + vin: vec![], + vout: vec![], + lock_time: 0, + expiry_height: 0, + value_balance: Amount(0), + shielded_spends: vec![], + shielded_outputs: vec![], + joinsplits: vec![], + joinsplit_pubkey: [0u8; 32], + joinsplit_sig: [0u8; 64], + binding_sig: None, + } + } + + fn header(&self) -> u32 { + let mut header = self.version; + if self.overwintered { + header |= 1 << 31; + } + header + } + + pub fn freeze(self) -> Transaction { + Transaction(self) + } } impl Transaction { @@ -97,7 +143,7 @@ impl Transaction { false => None, }; - Ok(Transaction { + Ok(Transaction(TransactionData { overwintered, version, version_group_id, @@ -112,7 +158,7 @@ impl Transaction { joinsplit_pubkey, joinsplit_sig, binding_sig, - }) + })) } pub fn write(&self, mut writer: W) -> io::Result<()> { @@ -169,12 +215,4 @@ impl Transaction { Ok(()) } - - fn header(&self) -> u32 { - let mut header = self.version; - if self.overwintered { - header |= 1 << 31; - } - header - } } diff --git a/zcash_primitives/src/transaction/sighash.rs b/zcash_primitives/src/transaction/sighash.rs index 9018cccb9..b835aa829 100644 --- a/zcash_primitives/src/transaction/sighash.rs +++ b/zcash_primitives/src/transaction/sighash.rs @@ -4,7 +4,8 @@ use pairing::{PrimeField, PrimeFieldRepr}; use super::{ components::{Amount, Script}, - Transaction, OVERWINTER_VERSION_GROUP_ID, SAPLING_TX_VERSION, SAPLING_VERSION_GROUP_ID, + Transaction, TransactionData, OVERWINTER_VERSION_GROUP_ID, SAPLING_TX_VERSION, + SAPLING_VERSION_GROUP_ID, }; const ZCASH_SIGHASH_PERSONALIZATION_PREFIX: &'static [u8; 12] = b"ZcashSigHash"; @@ -53,7 +54,7 @@ enum SigHashVersion { } impl SigHashVersion { - fn from_tx(tx: &Transaction) -> Self { + fn from_tx(tx: &TransactionData) -> Self { if tx.overwintered { match tx.version_group_id { OVERWINTER_VERSION_GROUP_ID => SigHashVersion::Overwinter, @@ -66,7 +67,7 @@ impl SigHashVersion { } } -fn prevout_hash(tx: &Transaction) -> Vec { +fn prevout_hash(tx: &TransactionData) -> Vec { let mut data = Vec::with_capacity(tx.vin.len() * 36); for t_in in &tx.vin { t_in.prevout.write(&mut data).unwrap(); @@ -76,7 +77,7 @@ fn prevout_hash(tx: &Transaction) -> Vec { h.finalize().as_ref().to_vec() } -fn sequence_hash(tx: &Transaction) -> Vec { +fn sequence_hash(tx: &TransactionData) -> Vec { let mut data = Vec::with_capacity(tx.vin.len() * 4); for t_in in &tx.vin { (&mut data) @@ -88,7 +89,7 @@ fn sequence_hash(tx: &Transaction) -> Vec { h.finalize().as_ref().to_vec() } -fn outputs_hash(tx: &Transaction) -> Vec { +fn outputs_hash(tx: &TransactionData) -> Vec { let mut data = Vec::with_capacity(tx.vout.len() * (4 + 1)); for t_out in &tx.vout { t_out.write(&mut data).unwrap(); @@ -98,7 +99,7 @@ fn outputs_hash(tx: &Transaction) -> Vec { h.finalize().as_ref().to_vec() } -fn joinsplits_hash(tx: &Transaction) -> Vec { +fn joinsplits_hash(tx: &TransactionData) -> Vec { let mut data = Vec::with_capacity( tx.joinsplits.len() * if tx.version < SAPLING_TX_VERSION { 1802 // JSDescription with PHGR13 proof @@ -115,7 +116,7 @@ fn joinsplits_hash(tx: &Transaction) -> Vec { h.finalize().as_ref().to_vec() } -fn shielded_spends_hash(tx: &Transaction) -> Vec { +fn shielded_spends_hash(tx: &TransactionData) -> Vec { let mut data = Vec::with_capacity(tx.shielded_spends.len() * 384); for s_spend in &tx.shielded_spends { s_spend.cv.write(&mut data).unwrap(); @@ -129,7 +130,7 @@ fn shielded_spends_hash(tx: &Transaction) -> Vec { h.finalize().as_ref().to_vec() } -fn shielded_outputs_hash(tx: &Transaction) -> Vec { +fn shielded_outputs_hash(tx: &TransactionData) -> Vec { let mut data = Vec::with_capacity(tx.shielded_outputs.len() * 948); for s_out in &tx.shielded_outputs { s_out.write(&mut data).unwrap(); @@ -139,8 +140,8 @@ fn shielded_outputs_hash(tx: &Transaction) -> Vec { h.finalize().as_ref().to_vec() } -pub fn signature_hash( - tx: &Transaction, +pub fn signature_hash_data( + tx: &TransactionData, consensus_branch_id: u32, hash_type: u32, transparent_input: Option<(usize, Script, Amount)>, @@ -219,3 +220,12 @@ pub fn signature_hash( SigHashVersion::Sprout => unimplemented!(), } } + +pub fn signature_hash( + tx: &Transaction, + consensus_branch_id: u32, + hash_type: u32, + transparent_input: Option<(usize, Script, Amount)>, +) -> Vec { + signature_hash_data(tx, consensus_branch_id, hash_type, transparent_input) +}