core: unify Hashed and Writeable to simplify things and remove allocations

This commit is contained in:
Merope Riddle 2016-11-01 01:37:06 +00:00
parent 245f0a8b56
commit ca89dae7e1
6 changed files with 90 additions and 66 deletions

View file

@ -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<u8> {
// 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),

View file

@ -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<u8>;
/// Serializer that outputs a hash of the serialized object
pub struct HashWriter {
state: Keccak
}
fn sha3(data: Vec<u8>) -> [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<W: ser::Writeable> 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<u8> {
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)
}
}

View file

@ -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<u8> {
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

View file

@ -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<u8> {
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<u8> {
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<Input>, outputs: &Vec<Output>) -> 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")

View file

@ -85,13 +85,6 @@ impl Writeable for PowHeader {
}
}
impl Hashed for PowHeader {
fn bytes(&self) -> Vec<u8> {
// 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;

View file

@ -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));