Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Made Hash store a byte array, so Node can store a Hash, rather than a byte slice #67

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ categories = [
[dependencies]
blake2-rfc = "0.2.18"
byteorder = "1.2.6"
constant_time_eq = "0.1.3"
ed25519-dalek = "0.8.1"
failure = "0.1.2"
flat-tree = "4.0.1"
Expand Down
78 changes: 61 additions & 17 deletions src/crypto/hash.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
pub use blake2_rfc::blake2b::Blake2bResult;

use crate::storage::Node;
use blake2_rfc::blake2b::Blake2b;
use byteorder::{BigEndian, WriteBytesExt};
// use ed25519_dalek::PublicKey;
use crate::storage::Node;
use constant_time_eq::constant_time_eq;
use merkle_tree_stream::Node as NodeTrait;
use pretty_hash::fmt as pretty_fmt;
use std::convert::AsRef;
use std::fmt;
use std::mem;
use std::ops::{Deref, DerefMut};

Expand All @@ -14,53 +17,57 @@ const LEAF_TYPE: [u8; 1] = [0x00];
const PARENT_TYPE: [u8; 1] = [0x01];
const ROOT_TYPE: [u8; 1] = [0x02];
//const HYPERCORE: [u8; 9] = *b"hypercore";
const BLAKE_2_HASH_SIZE: usize = 32;

type StoredHash = [u8; BLAKE_2_HASH_SIZE];
/// `BLAKE2b` hash.
#[derive(Debug, Clone, PartialEq)]
/// uses [blake2_rfc::blake2b::Blake2bResult] to hash its inputs on initalisation, the calculated
/// hash is then constant and can not be changed.
#[derive(Debug, Clone)]
pub struct Hash {
hash: Blake2bResult,
hash: StoredHash,
}

impl Hash {
/// Hash a `Leaf` node.
pub fn from_leaf(data: &[u8]) -> Self {
let size = u64_as_be(data.len() as u64);

let mut hasher = Blake2b::new(32);
let mut hasher = Blake2b::new(BLAKE_2_HASH_SIZE);
hasher.update(&LEAF_TYPE);
hasher.update(&size);
hasher.update(data);

Self {
hash: hasher.finalize(),
hash: hasher_to_stored_hash(hasher),
}
}

/// Hash two `Leaf` nodes hashes together to form a `Parent` hash.
pub fn from_hashes(left: &Node, right: &Node) -> Self {
let (node1, node2) = if left.index <= right.index {
let (node1, node2) = if left <= right {
(left, right)
} else {
(right, left)
};

let size = u64_as_be((node1.length + node2.length) as u64);

let mut hasher = Blake2b::new(32);
let mut hasher = Blake2b::new(BLAKE_2_HASH_SIZE);
hasher.update(&PARENT_TYPE);
hasher.update(&size);
hasher.update(node1.hash());
hasher.update(node2.hash());

Self {
hash: hasher.finalize(),
hash: hasher_to_stored_hash(hasher),
}
}

// /// Hash a public key. Useful to find the key you're looking for on a public
// /// network without leaking the key itself.
// pub fn from_key(public_key: PublicKey) -> Self {
// let mut hasher = Blake2b::new(32);
// let mut hasher = Blake2b::new(BLAKE_2_HASH_SIZE);
// hasher.update(*HYPERCORE);
// hasher.update(public_key.as_bytes());
// Self {
Expand All @@ -71,7 +78,7 @@ impl Hash {
/// Hash a vector of `Root` nodes.
// Called `crypto.tree()` in the JS implementation.
pub fn from_roots(roots: &[impl AsRef<Node>]) -> Self {
let mut hasher = Blake2b::new(32);
let mut hasher = Blake2b::new(BLAKE_2_HASH_SIZE);
hasher.update(&ROOT_TYPE);

for node in roots {
Expand All @@ -82,14 +89,37 @@ impl Hash {
}

Self {
hash: hasher.finalize(),
hash: hasher_to_stored_hash(hasher),
}
}

pub fn from_bytes(bytes: &[u8]) -> Self {
Self {
hash: slice_to_stored_hash(bytes),
}
}

/// Returns a byte slice of this `Hash`'s contents.
pub fn as_bytes(&self) -> &[u8] {
self.hash.as_bytes()
&self.hash[..]
}
}

fn slice_to_stored_hash(slice: &[u8]) -> StoredHash {
assert!(slice.len() == BLAKE_2_HASH_SIZE);

let mut stored_hash: StoredHash = [0; BLAKE_2_HASH_SIZE];
let mut i = 0;
for byte in slice.iter() {
stored_hash[i] = *byte;
i = i + 1;
}

stored_hash
}

fn hasher_to_stored_hash(hasher: Blake2b) -> StoredHash {
slice_to_stored_hash(hasher.finalize().as_bytes())
}

fn u64_as_be(n: u64) -> [u8; 8] {
Expand All @@ -99,7 +129,7 @@ fn u64_as_be(n: u64) -> [u8; 8] {
}

impl Deref for Hash {
type Target = Blake2bResult;
type Target = StoredHash;

fn deref(&self) -> &Self::Target {
&self.hash
Expand All @@ -112,6 +142,20 @@ impl DerefMut for Hash {
}
}

impl fmt::Display for Hash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", pretty_fmt(&self.hash[..]).unwrap())
}
}

impl PartialEq for Hash {
fn eq(&self, other: &Self) -> bool {
constant_time_eq(&self.hash[..], &other.hash[..])
}
}

impl Eq for Hash {}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -143,8 +187,8 @@ mod tests {
fn parent_hash() {
let d1: &[u8] = &[0, 1, 2, 3, 4];
let d2: &[u8] = &[42, 43, 44, 45, 46, 47, 48];
let node1 = Node::new(0, Hash::from_leaf(d1).as_bytes().to_vec(), d1.len());
let node2 = Node::new(1, Hash::from_leaf(d2).as_bytes().to_vec(), d2.len());
let node1 = Node::new(0, Hash::from_leaf(d1), d1.len());
let node2 = Node::new(1, Hash::from_leaf(d2), d2.len());
check_hash(
Hash::from_hashes(&node1, &node2),
"6fac58578fa385f25a54c0637adaca71fdfddcea885d561f33d80c4487149a14",
Expand All @@ -159,8 +203,8 @@ mod tests {
fn root_hash() {
let d1: &[u8] = &[0, 1, 2, 3, 4];
let d2: &[u8] = &[42, 43, 44, 45, 46, 47, 48];
let node1 = Node::new(0, Hash::from_leaf(d1).as_bytes().to_vec(), d1.len());
let node2 = Node::new(1, Hash::from_leaf(d2).as_bytes().to_vec(), d2.len());
let node1 = Node::new(0, Hash::from_leaf(d1), d1.len());
let node2 = Node::new(1, Hash::from_leaf(d2), d2.len());
check_hash(
Hash::from_roots(&[&node1, &node2]),
"2d117e0bb15c6e5236b6ce764649baed1c41890da901a015341503146cc20bcd",
Expand Down
10 changes: 5 additions & 5 deletions src/crypto/merkle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use merkle_tree_stream::{
use std::rc::Rc;

#[derive(Debug)]
struct H;
struct Hasher;

impl HashMethods for H {
impl HashMethods for Hasher {
type Node = Node;
type Hash = Hash;

Expand All @@ -33,7 +33,7 @@ impl HashMethods for H {
index: partial.index(),
parent: partial.parent,
length: partial.len(),
hash: hash.as_bytes().into(),
hash: hash,
data,
}
}
Expand All @@ -42,7 +42,7 @@ impl HashMethods for H {
/// Merkle Tree Stream
#[derive(Debug)]
pub struct Merkle {
stream: MerkleTreeStream<H>,
stream: MerkleTreeStream<Hasher>,
nodes: Vec<Rc<Node>>,
}

Expand All @@ -58,7 +58,7 @@ impl Merkle {
pub fn new() -> Self {
Self {
nodes: vec![],
stream: MerkleTreeStream::new(H, vec![]),
stream: MerkleTreeStream::new(Hasher, vec![]),
}
}

Expand Down
12 changes: 5 additions & 7 deletions src/feed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,11 +287,9 @@ where

let mut visited = vec![];
let mut top = match data {
Some(data) => Node::new(
tree_index(index),
Hash::from_leaf(&data).as_bytes().to_owned(),
data.len(),
),
Some(data) => {
Node::new(tree_index(index), Hash::from_leaf(&data), data.len())
}
None => proof.nodes.remove(0),
};

Expand Down Expand Up @@ -322,7 +320,7 @@ where
visited.push(top.clone());
let hash = Hash::from_hashes(&top, &node);
let len = top.len() + node.len();
top = Node::new(flat::parent(top.index), hash.as_bytes().into(), len);
top = Node::new(flat::parent(top.index), hash, len);

if verify_node(&trusted_node, &top) {
self.write(index, data, &visited, None)?;
Expand Down Expand Up @@ -526,7 +524,7 @@ where
let node = self.storage.get_node(2 * index)?;
let data = self.storage.get_data(index)?;
let data_hash = Hash::from_leaf(&data);
if node.hash == data_hash.as_bytes() {
if node.hash == data_hash {
valid_blocks += 1;
} else {
invalid_blocks += 1;
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ extern crate failure;

extern crate blake2_rfc;
extern crate byteorder;
extern crate constant_time_eq;
extern crate ed25519_dalek;
extern crate flat_tree;
extern crate merkle_tree_stream;
Expand Down
25 changes: 11 additions & 14 deletions src/storage/node.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::crypto::Hash;
use crate::Result;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use flat_tree;
use merkle_tree_stream::Node as NodeTrait;
use pretty_hash::fmt as pretty_fmt;
use std::cmp::Ordering;
use std::convert::AsRef;
use std::fmt::{self, Display};
use std::io::Cursor;
use std::io::{Cursor, Seek, SeekFrom};

/// Nodes that are persisted to disk.
// TODO: replace `hash: Vec<u8>` with `hash: Hash`. This requires patching /
Expand All @@ -15,7 +15,7 @@ use std::io::Cursor;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Node {
pub(crate) index: usize,
pub(crate) hash: Vec<u8>,
pub(crate) hash: Hash,
pub(crate) length: usize,
pub(crate) parent: usize,
pub(crate) data: Option<Vec<u8>>,
Expand All @@ -24,7 +24,7 @@ pub struct Node {
impl Node {
/// Create a new instance.
// TODO: ensure sizes are correct.
pub fn new(index: usize, hash: Vec<u8>, length: usize) -> Self {
pub fn new(index: usize, hash: Hash, length: usize) -> Self {
Self {
index,
hash,
Expand All @@ -41,16 +41,15 @@ impl Node {
ensure!(buffer.len() == 40, "buffer should be 40 bytes");

let parent = flat_tree::parent(index);
let mut reader = Cursor::new(buffer);

// TODO: subslice directly, move cursor forward.
let capacity = 32;
let mut hash = Vec::with_capacity(capacity);
for _ in 0..capacity {
hash.push(reader.read_u8()?);
}
let hash = Hash::from_bytes(&buffer[..capacity]);

let mut reader = Cursor::new(buffer);
reader.seek(SeekFrom::Start(capacity as u64))?;
// TODO: This will blow up on 32 bit systems, because usize can be 32 bits.
// Note: we could stop using usize on any protocol specific parts of code?
let length = reader.read_u64::<BigEndian>()? as usize;
Ok(Self {
hash,
Expand All @@ -64,7 +63,7 @@ impl Node {
/// Convert to a buffer that can be written to disk.
pub fn to_bytes(&self) -> Result<Vec<u8>> {
let mut writer = Vec::with_capacity(40);
writer.extend_from_slice(&self.hash);
writer.extend_from_slice(&self.hash.as_bytes());
writer.write_u64::<BigEndian>(self.length as u64)?;
Ok(writer)
}
Expand All @@ -78,7 +77,7 @@ impl NodeTrait for Node {

#[inline]
fn hash(&self) -> &[u8] {
&self.hash
&self.hash.as_bytes()
}

#[inline]
Expand Down Expand Up @@ -109,9 +108,7 @@ impl Display for Node {
write!(
f,
"Node {{ index: {}, hash: {}, length: {} }}",
self.index,
pretty_fmt(&self.hash).unwrap(),
self.length
self.index, self.hash, self.length
)
}
}
Expand Down