From 1c09fdc74e63eda83c5802207150184c399c2bf8 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sat, 19 Dec 2015 18:37:23 -0700 Subject: [PATCH] Initial commit. --- .gitignore | 2 + Cargo.toml | 7 + src/lib.rs | 362 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/sha256.rs | 68 ++++++++++ 4 files changed, 439 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs create mode 100644 src/sha256.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9d37c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8329b7c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "incrementalmerkletree" +version = "0.1.0" +authors = ["Sean Bowe "] + +[dependencies] +rand = "0.3" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..bfa1983 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,362 @@ +#![feature(rustc_private)] +extern crate rustc; +extern crate rand; + +mod sha256; + +trait Hashable: Clone + Copy { + fn combine(&Self, &Self) -> Self; + fn blank() -> Self; +} + +#[derive(Clone)] +struct IncrementalMerkleTree { + cursor: Leaf, + depth: usize +} + +#[derive(Clone)] +enum Leaf { + Left{parent: Parent, content: T}, + Right{parent: Parent, left: T, content: T} +} + +#[derive(Clone)] +enum Parent { + Empty, + Left{parent: Box>}, + Right{left: T, parent: Box>} +} + +impl Parent { + fn ascend<'a, F: FnMut(Option<&'a T>) -> bool>(&'a self, mut cb: F) { + match *self { + Parent::Empty => { + if cb(None) { + self.ascend(cb); + } + }, + Parent::Left{ref parent} => { + if cb(None) { + parent.ascend(cb); + } + }, + Parent::Right{ref left, ref parent} => { + if cb(Some(left)) { + parent.ascend(cb); + } + } + } + } + + fn advance(self, hash: T) -> Parent { + match self { + Parent::Empty => { + Parent::Right { + left: hash, + parent: Box::new(Parent::Empty) + } + }, + Parent::Left{parent} => { + Parent::Right { + left: hash, + parent: parent + } + }, + Parent::Right{left, parent} => { + Parent::Left{ + parent: Box::new(parent.advance(T::combine(&left, &hash))) + } + } + } + } +} + +impl IncrementalMerkleTree { + fn new(d: usize, initial: T) -> IncrementalMerkleTree { + assert!(d != 0); + + IncrementalMerkleTree { + cursor: Leaf::Left{parent: Parent::Empty, content: initial}, + depth: d + } + } + + fn completed(&self) -> bool { + match self.cursor { + Leaf::Left{..} => false, + Leaf::Right{ref parent, ..} => { + let complete = &mut true; + let depth = &mut (self.depth - 1); + + parent.ascend(|left| { + if *depth == 0 { + return false; + } + + if left.is_none() { + *complete = false; + return false; + } + + *depth -= 1; + + true + }); + + *complete + } + } + } + + fn append(self, obj: T) -> IncrementalMerkleTree { + match self.cursor { + Leaf::Left{parent, content} => { + IncrementalMerkleTree { + cursor: Leaf::Right{ + parent: parent, + left: content, + content: obj + }, + depth: self.depth + } + }, + Leaf::Right{parent, left, content} => { + IncrementalMerkleTree { + cursor: Leaf::Left{ + parent: parent.advance(T::combine(&left, &content)), + content: obj + }, + depth: self.depth + } + } + } + } + + fn unfilled(&self, mut skip: usize) -> usize { + let parent = match self.cursor { + Leaf::Left{ref parent, ..} => { + if skip == 0 { + return 0; + } else { + skip -= 1; + + parent + } + }, + Leaf::Right{ref parent, ..} => { + parent + } + }; + + let mut depth = &mut 0; + parent.ascend(|left| { + *depth += 1; + + match left { + Some(_) => { + return true; + }, + None => { + if skip == 0 { + return false; + } else { + skip -= 1; + return true; + } + } + } + }); + + return *depth; + } + + fn root(&self) -> T { + self.root_advanced(Some(T::blank()).iter().cycle()) + } + + fn root_advanced<'a, I: Iterator>(&self, mut it: I) -> T where T: 'a { + let (parent, mut child) = match self.cursor { + Leaf::Left{ref parent, ref content} => { + (parent, T::combine(content, it.next().unwrap())) + }, + Leaf::Right{ref parent, ref left, ref content} => { + (parent, T::combine(left, content)) + } + }; + + let mut depth = self.depth - 1; + { + let child = &mut child; + + parent.ascend(move |left| { + if depth == 0 { + return false; + } + + match left { + Some(left) => { + *child = T::combine(left, &*child); + }, + None => { + *child = T::combine(&*child, it.next().unwrap()); + } + } + + depth = depth - 1; + + true + }); + } + + return child; + } +} + +#[derive(Clone)] +struct IncrementalWitness { + tree: IncrementalMerkleTree, + delta: IncrementalDelta +} + +#[derive(Clone)] +struct IncrementalDelta { + filled: Vec, + active: Option> +} + +impl IncrementalWitness { + fn new(from: &IncrementalMerkleTree) -> IncrementalWitness { + IncrementalWitness { + tree: from.clone(), + delta: IncrementalDelta { + filled: vec![], + active: None + } + } + } + + fn append(&mut self, object: T) { + match self.delta.active.take() { + Some(active) => { + let active = active.append(object); + + if active.completed() { + self.delta.filled.push(active.root()); + } else { + self.delta.active = Some(active); + } + }, + None => { + match self.tree.unfilled(self.delta.filled.len()) { + 0 => { + self.delta.filled.push(object); + }, + i => { + self.delta.active = Some(IncrementalMerkleTree::new(i, object)); + } + } + } + } + } + + fn root(&self) -> T { + self.tree.root_advanced(self.delta.filled.iter() // use filled values + .chain(self.delta.active.as_ref().map(|x| x.root()).as_ref()) // then use the active root + .chain(Some(T::blank()).iter().cycle())) // then fill in with blanks + } +} + + +mod test { + use super::{IncrementalMerkleTree, IncrementalWitness}; + use super::sha256::*; + + #[test] + fn test_root() { + let a = Sha256Digest::rand(0); + + let tree = IncrementalMerkleTree::new(3, a); + + assert_eq!(tree.root(), Sha256Digest([94, 162, 216, 229, 230, 128, 153, 35, 89, 40, 180, 159, 125, 27, 48, 80, 181, 73, 7, 195, 182, 223, 83, 165, 59, 200, 234, 181, 106, 3, 243, 228])); + + let b = Sha256Digest::rand(1); + + let tree = tree.append(b); + + assert_eq!(tree.root(), Sha256Digest([222, 23, 196, 222, 130, 80, 115, 139, 134, 72, 108, 150, 235, 75, 216, 5, 63, 101, 2, 237, 51, 47, 165, 216, 40, 15, 209, 176, 10, 192, 224, 26])); + } + + #[test] + fn test_unfilled() { + let a = Sha256Digest::rand(0); + let mut tree = IncrementalMerkleTree::new(3, a); + + for i in 0..4 { + let b = Sha256Digest::rand(i+1); + tree = tree.append(b); + } + + assert_eq!(tree.unfilled(0), 0); + assert_eq!(tree.unfilled(1), 1); + assert_eq!(tree.unfilled(2), 3); + } + + #[test] + fn test_complete() { + let a = Sha256Digest::rand(0); + let mut tree = IncrementalMerkleTree::new(3, a); + + for i in 0..7 { + assert_eq!(tree.completed(), false); + let b = Sha256Digest::rand(i+1); + tree = tree.append(b); + } + + assert_eq!(tree.completed(), true); + } + + #[test] + fn test_witness() { + let a = Sha256Digest::rand(0); + let mut tree = IncrementalMerkleTree::new(3, a); + + let mut witness = IncrementalWitness::new(&tree); + + assert_eq!(tree.root(), witness.root()); + assert_eq!(witness.delta.filled.len(), 0); + assert!(witness.delta.active.is_none()); + + for i in 1..8 { + let b = Sha256Digest::rand(i); + witness.append(b); + tree = tree.append(b); + + match i { + 1 => { + assert_eq!(witness.delta.filled.len(), 1); + assert!(witness.delta.active.is_none()); + }, + i if i <= 2 => { + assert_eq!(witness.delta.filled.len(), 1); + assert!(witness.delta.active.is_some()); + }, + i if i == 3 => { + assert_eq!(witness.delta.filled.len(), 2); + assert!(witness.delta.active.is_none()); + }, + i if i < 7 => { + assert_eq!(witness.delta.filled.len(), 2); + assert!(witness.delta.active.is_some()); + }, + a @ _ => { + assert_eq!(a, 7); + assert_eq!(witness.delta.filled.len(), 3); + assert!(witness.delta.active.is_none()); + } + } + + assert_eq!(tree.root(), witness.root()); + } + } +} \ No newline at end of file diff --git a/src/sha256.rs b/src/sha256.rs new file mode 100644 index 0000000..b5e1830 --- /dev/null +++ b/src/sha256.rs @@ -0,0 +1,68 @@ +use rustc::util::sha2::{Digest,Sha256}; +use std::u8; + +use super::Hashable; + +impl Hashable for Sha256Digest { + fn combine(left: &Self, right: &Self) -> Sha256Digest { + sha256_compression_function(Sha256Block::new_from_digests(left, right)) + } + + fn blank() -> Sha256Digest { + Sha256Digest([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + } +} + +struct Sha256Block(pub [u8; 64]); +impl Sha256Block { + fn new_from_digests(left: &Sha256Digest, right: &Sha256Digest) -> Sha256Block { + use std::mem; + + struct CompoundDigest { + left: Sha256Digest, + right: Sha256Digest + } + + let compound = CompoundDigest { left: *left, right: *right }; + + unsafe { mem::transmute(compound) } + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct Sha256Digest(pub [u8; 32]); + +impl Sha256Digest { + pub fn rand(seed: usize) -> Sha256Digest { + use rand::{self,Rng,SeedableRng,StdRng}; + + let seed: [usize; 1] = [seed]; + + let mut rng = StdRng::from_seed(&seed); + + Sha256Digest([rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen(), + rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen(), + rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen(), + rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen() + ]) + } +} + +// todo: this is not a compression function +fn sha256_compression_function(block: Sha256Block) -> Sha256Digest { + let mut hash = Sha256::new(); + + hash.input(&block.0); + + let res = hash.result_bytes(); + + let mut s = Sha256Digest([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + + unsafe { + use std::ptr; + + ptr::copy_nonoverlapping::(&res[0], &mut (s.0)[0], 32); + } + + s +} \ No newline at end of file