diff --git a/core/src/core/block.rs b/core/src/core/block.rs index 5d3ee716e..3985705e0 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -26,7 +26,7 @@ use core::transaction::merkle_inputs_outputs; use core::{PROOFSIZE, REWARD}; use core::hash::{Hash, Hashed, ZERO_HASH}; use core::transaction::MAX_IN_OUT_LEN; -use ser::{self, Readable, Reader, Writeable, Writer, ser_vec}; +use ser::{self, Readable, Reader, Writeable, Writer}; /// Block header, fairly standard compared to other blockchains. pub struct BlockHeader { @@ -79,13 +79,6 @@ impl Writeable for BlockHeader { } } -impl Hashed for BlockHeader { - fn bytes(&self) -> Vec { - // no serialization errors are applicable in this specific case - ser_vec(self).unwrap() - } -} - /// A block as expressed in the MimbleWimble protocol. The reward is /// non-explicit, assumed to be deductible from block height (similar to /// bitcoin's schedule) and expressed as a global transaction fee (added v.H), diff --git a/core/src/core/hash.rs b/core/src/core/hash.rs index ad3328ec6..543b6ddfa 100644 --- a/core/src/core/hash.rs +++ b/core/src/core/hash.rs @@ -17,10 +17,12 @@ //! Primary hash function used in the protocol //! +use byteorder::{ByteOrder, WriteBytesExt, BigEndian}; use std::fmt; - use tiny_keccak::Keccak; +use ser::{self, AsFixedBytes}; + /// A hash to uniquely (or close enough) identify one of the main blockchain /// constructs. Used pervasively for blocks, transactions and ouputs. #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] @@ -56,27 +58,58 @@ impl Hash { pub const ZERO_HASH: Hash = Hash([0; 32]); -/// A trait for types that get their hash (double SHA256) from their byte -/// serialzation. -pub trait Hashed { - fn hash(&self) -> Hash { - let data = self.bytes(); - Hash(sha3(data)) - } - - fn bytes(&self) -> Vec; +/// Serializer that outputs a hash of the serialized object +pub struct HashWriter { + state: Keccak } -fn sha3(data: Vec) -> [u8; 32] { - let mut sha3 = Keccak::new_sha3_256(); - let mut buf = [0; 32]; - sha3.update(&data); - sha3.finalize(&mut buf); - buf +impl HashWriter { + fn finalize(self, output: &mut [u8]) { + self.state.finalize(output); + } +} + +impl Default for HashWriter { + fn default() -> HashWriter { + HashWriter { + state: Keccak::new_sha3_256() + } + } +} + +impl ser::Writer for HashWriter { + fn serialization_mode(&self) -> ser::SerializationMode { + ser::SerializationMode::Hash + } + + fn write_fixed_bytes(&mut self, b32: &AsFixedBytes) -> Result<(), ser::Error> { + self.state.update(b32.as_fixed_bytes()); + Ok(()) + } +} + +/// A trait for types that have a canonical hash +pub trait Hashed { + fn hash(&self) -> Hash; +} + +impl Hashed for W { + fn hash(&self) -> Hash { + let mut hasher = HashWriter::default(); + ser::Writeable::write(self, &mut hasher).unwrap(); + let mut ret = [0; 32]; + hasher.finalize(&mut ret); + Hash(ret) + } } impl Hashed for [u8] { - fn bytes(&self) -> Vec { - self.to_owned() + fn hash(&self) -> Hash { + let mut hasher = HashWriter::default(); + let mut ret = [0; 32]; + ser::Writer::write_bytes(&mut hasher, &self).unwrap(); + hasher.finalize(&mut ret); + Hash(ret) } } + diff --git a/core/src/core/mod.rs b/core/src/core/mod.rs index 1095c70aa..d1472eed7 100644 --- a/core/src/core/mod.rs +++ b/core/src/core/mod.rs @@ -154,12 +154,12 @@ impl Proof { /// Two hashes that will get hashed together in a Merkle tree to build the next /// level up. struct HPair(Hash, Hash); -impl Hashed for HPair { - fn bytes(&self) -> Vec { - let mut data = Vec::with_capacity(64); - data.extend_from_slice(self.0.to_slice()); - data.extend_from_slice(self.1.to_slice()); - return data; + +impl Writeable for HPair { + fn write(&self, writer: &mut Writer) -> Result<(), Error> { + try!(writer.write_bytes(&self.0.to_slice())); + try!(writer.write_bytes(&self.1.to_slice())); + Ok(()) } } /// An iterator over hashes in a vector that pairs them to build a row in a diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 6884cc858..d1d6bcc02 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -16,7 +16,7 @@ use core::Committed; use core::MerkleRow; -use core::hash::{Hashed, Hash}; +use core::hash::{Hash, Hashed}; use ser::{self, Reader, Writer, Readable, Writeable}; use secp::{self, Secp256k1, Message, Signature}; @@ -283,13 +283,6 @@ impl Input { } } -/// The hash of an input is the hash of the output hash it references. -impl Hashed for Input { - fn bytes(&self) -> Vec { - self.output_hash().to_vec() - } -} - #[derive(Debug, Copy, Clone)] pub enum Output { BlindOutput { @@ -303,8 +296,12 @@ pub enum Output { /// an Output as binary. impl Writeable for Output { fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> { + // The hash of an output is only the hash of its commitment. try!(writer.write_fixed_bytes(&self.commitment().unwrap())); - writer.write_bytes(&self.proof().unwrap().bytes()) + if writer.serialization_mode() == ser::SerializationMode::Full { + try!(writer.write_bytes(&self.proof().unwrap().bytes())) + } + Ok(()) } } @@ -363,17 +360,6 @@ impl Output { } } -/// The hash of an output is the hash of its commitment. -impl Hashed for Output { - fn bytes(&self) -> Vec { - if let &Output::BlindOutput { commit, .. } = self { - return commit.bytes().to_vec(); - } else { - panic!("cannot hash an overt output"); - } - } -} - /// Utility function to calculate the Merkle root of vectors of inputs and /// outputs. pub fn merkle_inputs_outputs(inputs: &Vec, outputs: &Vec) -> Hash { @@ -418,12 +404,12 @@ mod test { let ref secp = new_secp(); let tx = tx2i1o(secp, &mut rng); - let mut btx = tx.blind(&secp).unwrap(); + let btx = tx.blind(&secp).unwrap(); let mut vec = Vec::new(); serialize(&mut vec, &btx).expect("serialization failed"); // let mut dtx = Transaction::read(&mut BinReader { source: &mut &vec[..] // }).unwrap(); - let mut dtx: Transaction = deserialize(&mut &vec[..]).unwrap(); + let dtx: Transaction = deserialize(&mut &vec[..]).unwrap(); assert_eq!(dtx.fee, 1); assert_eq!(dtx.inputs.len(), 2); assert_eq!(dtx.outputs.len(), 1); @@ -437,15 +423,15 @@ mod test { let ref secp = new_secp(); let tx = tx2i1o(secp, &mut rng); - let mut btx = tx.blind(&secp).unwrap(); + let btx = tx.blind(&secp).unwrap(); let mut vec = Vec::new(); assert!(serialize(&mut vec, &btx).is_ok()); - let mut dtx: Transaction = deserialize(&mut &vec[..]).unwrap(); + let dtx: Transaction = deserialize(&mut &vec[..]).unwrap(); let mut vec2 = Vec::new(); assert!(serialize(&mut vec2, &btx).is_ok()); - let mut dtx2: Transaction = deserialize(&mut &vec2[..]).unwrap(); + let dtx2: Transaction = deserialize(&mut &vec2[..]).unwrap(); assert_eq!(btx.hash(), dtx.hash()); assert_eq!(dtx.hash(), dtx2.hash()); @@ -532,10 +518,10 @@ mod test { let mut rng = OsRng::new().unwrap(); let tx1 = tx2i1o(secp, &mut rng); - let mut btx1 = tx1.blind(&secp).unwrap(); + let btx1 = tx1.blind(&secp).unwrap(); let tx2 = tx1i1o(secp, &mut rng); - let mut btx2 = tx2.blind(&secp).unwrap(); + let btx2 = tx2.blind(&secp).unwrap(); if btx1.hash() == btx2.hash() { panic!("diff txs have same hash") diff --git a/core/src/pow/mod.rs b/core/src/pow/mod.rs index d17da511b..7047c10c6 100644 --- a/core/src/pow/mod.rs +++ b/core/src/pow/mod.rs @@ -85,13 +85,6 @@ impl Writeable for PowHeader { } } -impl Hashed for PowHeader { - fn bytes(&self) -> Vec { - // no serialization errors are applicable in this specific case - ser_vec(self).unwrap() - } -} - impl PowHeader { fn from_block(b: &Block) -> PowHeader { let ref h = b.header; diff --git a/core/src/ser.rs b/core/src/ser.rs index 4ffd516ed..54ed99255 100644 --- a/core/src/ser.rs +++ b/core/src/ser.rs @@ -81,9 +81,24 @@ pub trait AsFixedBytes { fn as_fixed_bytes(&self) -> &[u8]; } + +/// Signal to a serializable object how much of its data should be serialized +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum SerializationMode { + /// Serialize everything sufficiently to fully reconstruct the object + Full, + /// Serialize the data that defines the object + Hash, + /// Serialize everything that a signer of the object should know + SigHash +} + /// Implementations defined how different numbers and binary structures are /// written to an underlying stream or container (depending on implementation). pub trait Writer { + /// The mode this serializer is writing in + fn serialization_mode(&self) -> SerializationMode; + /// Writes a u8 as bytes fn write_u8(&mut self, n: u8) -> Result<(), Error> { self.write_fixed_bytes(&[n]) @@ -252,6 +267,10 @@ struct BinWriter<'a> { } impl<'a> Writer for BinWriter<'a> { + fn serialization_mode(&self) -> SerializationMode { + SerializationMode::Full + } + fn write_fixed_bytes(&mut self, fixed: &AsFixedBytes) -> Result<(), Error> { let bs = fixed.as_fixed_bytes(); try!(self.sink.write_all(bs));