Convert Transaction into a wrapping struct with impl Deref

Users who have a Transaction can now only obtain an immutable reference
to its underlying data.
This commit is contained in:
Jack Grigg 2018-09-11 21:36:42 +01:00
parent 0c81695731
commit 1f11c404dc
No known key found for this signature in database
GPG Key ID: 1B8D649257DB0829
2 changed files with 83 additions and 35 deletions

View File

@ -1,6 +1,7 @@
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use sapling_crypto::redjubjub::Signature; use sapling_crypto::redjubjub::Signature;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use std::ops::Deref;
use serialize::Vector; use serialize::Vector;
@ -10,6 +11,8 @@ mod sighash;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
pub use self::sighash::{signature_hash, signature_hash_data, SIGHASH_ALL};
use self::components::{Amount, JSDescription, OutputDescription, SpendDescription, TxIn, TxOut}; use self::components::{Amount, JSDescription, OutputDescription, SpendDescription, TxIn, TxOut};
const OVERWINTER_VERSION_GROUP_ID: u32 = 0x03C48270; const OVERWINTER_VERSION_GROUP_ID: u32 = 0x03C48270;
@ -18,21 +21,64 @@ const SAPLING_VERSION_GROUP_ID: u32 = 0x892F2085;
const SAPLING_TX_VERSION: u32 = 4; const SAPLING_TX_VERSION: u32 = 4;
/// A Zcash transaction. /// A Zcash transaction.
pub struct Transaction { pub struct Transaction(TransactionData);
overwintered: bool,
version: u32, impl Deref for Transaction {
version_group_id: u32, type Target = TransactionData;
vin: Vec<TxIn>,
vout: Vec<TxOut>, fn deref(&self) -> &TransactionData {
lock_time: u32, &self.0
expiry_height: u32, }
value_balance: Amount, }
shielded_spends: Vec<SpendDescription>,
shielded_outputs: Vec<OutputDescription>, pub struct TransactionData {
joinsplits: Vec<JSDescription>, pub overwintered: bool,
joinsplit_pubkey: [u8; 32], pub version: u32,
joinsplit_sig: [u8; 64], pub version_group_id: u32,
binding_sig: Option<Signature>, pub vin: Vec<TxIn>,
pub vout: Vec<TxOut>,
pub lock_time: u32,
pub expiry_height: u32,
pub value_balance: Amount,
pub shielded_spends: Vec<SpendDescription>,
pub shielded_outputs: Vec<OutputDescription>,
pub joinsplits: Vec<JSDescription>,
pub joinsplit_pubkey: [u8; 32],
pub joinsplit_sig: [u8; 64],
pub binding_sig: Option<Signature>,
}
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 { impl Transaction {
@ -97,7 +143,7 @@ impl Transaction {
false => None, false => None,
}; };
Ok(Transaction { Ok(Transaction(TransactionData {
overwintered, overwintered,
version, version,
version_group_id, version_group_id,
@ -112,7 +158,7 @@ impl Transaction {
joinsplit_pubkey, joinsplit_pubkey,
joinsplit_sig, joinsplit_sig,
binding_sig, binding_sig,
}) }))
} }
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> { pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
@ -169,12 +215,4 @@ impl Transaction {
Ok(()) Ok(())
} }
fn header(&self) -> u32 {
let mut header = self.version;
if self.overwintered {
header |= 1 << 31;
}
header
}
} }

View File

@ -4,7 +4,8 @@ use pairing::{PrimeField, PrimeFieldRepr};
use super::{ use super::{
components::{Amount, Script}, 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"; const ZCASH_SIGHASH_PERSONALIZATION_PREFIX: &'static [u8; 12] = b"ZcashSigHash";
@ -53,7 +54,7 @@ enum SigHashVersion {
} }
impl SigHashVersion { impl SigHashVersion {
fn from_tx(tx: &Transaction) -> Self { fn from_tx(tx: &TransactionData) -> Self {
if tx.overwintered { if tx.overwintered {
match tx.version_group_id { match tx.version_group_id {
OVERWINTER_VERSION_GROUP_ID => SigHashVersion::Overwinter, OVERWINTER_VERSION_GROUP_ID => SigHashVersion::Overwinter,
@ -66,7 +67,7 @@ impl SigHashVersion {
} }
} }
fn prevout_hash(tx: &Transaction) -> Vec<u8> { fn prevout_hash(tx: &TransactionData) -> Vec<u8> {
let mut data = Vec::with_capacity(tx.vin.len() * 36); let mut data = Vec::with_capacity(tx.vin.len() * 36);
for t_in in &tx.vin { for t_in in &tx.vin {
t_in.prevout.write(&mut data).unwrap(); t_in.prevout.write(&mut data).unwrap();
@ -76,7 +77,7 @@ fn prevout_hash(tx: &Transaction) -> Vec<u8> {
h.finalize().as_ref().to_vec() h.finalize().as_ref().to_vec()
} }
fn sequence_hash(tx: &Transaction) -> Vec<u8> { fn sequence_hash(tx: &TransactionData) -> Vec<u8> {
let mut data = Vec::with_capacity(tx.vin.len() * 4); let mut data = Vec::with_capacity(tx.vin.len() * 4);
for t_in in &tx.vin { for t_in in &tx.vin {
(&mut data) (&mut data)
@ -88,7 +89,7 @@ fn sequence_hash(tx: &Transaction) -> Vec<u8> {
h.finalize().as_ref().to_vec() h.finalize().as_ref().to_vec()
} }
fn outputs_hash(tx: &Transaction) -> Vec<u8> { fn outputs_hash(tx: &TransactionData) -> Vec<u8> {
let mut data = Vec::with_capacity(tx.vout.len() * (4 + 1)); let mut data = Vec::with_capacity(tx.vout.len() * (4 + 1));
for t_out in &tx.vout { for t_out in &tx.vout {
t_out.write(&mut data).unwrap(); t_out.write(&mut data).unwrap();
@ -98,7 +99,7 @@ fn outputs_hash(tx: &Transaction) -> Vec<u8> {
h.finalize().as_ref().to_vec() h.finalize().as_ref().to_vec()
} }
fn joinsplits_hash(tx: &Transaction) -> Vec<u8> { fn joinsplits_hash(tx: &TransactionData) -> Vec<u8> {
let mut data = Vec::with_capacity( let mut data = Vec::with_capacity(
tx.joinsplits.len() * if tx.version < SAPLING_TX_VERSION { tx.joinsplits.len() * if tx.version < SAPLING_TX_VERSION {
1802 // JSDescription with PHGR13 proof 1802 // JSDescription with PHGR13 proof
@ -115,7 +116,7 @@ fn joinsplits_hash(tx: &Transaction) -> Vec<u8> {
h.finalize().as_ref().to_vec() h.finalize().as_ref().to_vec()
} }
fn shielded_spends_hash(tx: &Transaction) -> Vec<u8> { fn shielded_spends_hash(tx: &TransactionData) -> Vec<u8> {
let mut data = Vec::with_capacity(tx.shielded_spends.len() * 384); let mut data = Vec::with_capacity(tx.shielded_spends.len() * 384);
for s_spend in &tx.shielded_spends { for s_spend in &tx.shielded_spends {
s_spend.cv.write(&mut data).unwrap(); s_spend.cv.write(&mut data).unwrap();
@ -129,7 +130,7 @@ fn shielded_spends_hash(tx: &Transaction) -> Vec<u8> {
h.finalize().as_ref().to_vec() h.finalize().as_ref().to_vec()
} }
fn shielded_outputs_hash(tx: &Transaction) -> Vec<u8> { fn shielded_outputs_hash(tx: &TransactionData) -> Vec<u8> {
let mut data = Vec::with_capacity(tx.shielded_outputs.len() * 948); let mut data = Vec::with_capacity(tx.shielded_outputs.len() * 948);
for s_out in &tx.shielded_outputs { for s_out in &tx.shielded_outputs {
s_out.write(&mut data).unwrap(); s_out.write(&mut data).unwrap();
@ -139,8 +140,8 @@ fn shielded_outputs_hash(tx: &Transaction) -> Vec<u8> {
h.finalize().as_ref().to_vec() h.finalize().as_ref().to_vec()
} }
pub fn signature_hash( pub fn signature_hash_data(
tx: &Transaction, tx: &TransactionData,
consensus_branch_id: u32, consensus_branch_id: u32,
hash_type: u32, hash_type: u32,
transparent_input: Option<(usize, Script, Amount)>, transparent_input: Option<(usize, Script, Amount)>,
@ -219,3 +220,12 @@ pub fn signature_hash(
SigHashVersion::Sprout => unimplemented!(), SigHashVersion::Sprout => unimplemented!(),
} }
} }
pub fn signature_hash(
tx: &Transaction,
consensus_branch_id: u32,
hash_type: u32,
transparent_input: Option<(usize, Script, Amount)>,
) -> Vec<u8> {
signature_hash_data(tx, consensus_branch_id, hash_type, transparent_input)
}