From b213a5ade4799db678afff5190a6adc678ea08b9 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Fri, 26 Apr 2019 09:55:03 +1000 Subject: [PATCH] Create `cached_tree_hash` crate. --- Cargo.toml | 1 + eth2/types/src/tree_hash_vector.rs | 2 +- eth2/utils/cached_tree_hash/Cargo.toml | 14 + eth2/utils/cached_tree_hash/README.md | 76 + .../src}/btree_overlay.rs | 4 + eth2/utils/cached_tree_hash/src/errors.rs | 18 + .../src}/impls.rs | 9 +- .../src}/impls/vec.rs | 1 + eth2/utils/cached_tree_hash/src/lib.rs | 66 + eth2/utils/cached_tree_hash/src/merkleize.rs | 78 + .../src}/resize.rs | 0 .../src}/tree_hash_cache.rs | 6 + eth2/utils/cached_tree_hash/tests/tests.rs | 437 +++++ eth2/utils/tree_hash/src/cached_tree_hash.rs | 156 -- .../src/{standard_tree_hash => }/impls.rs | 1 + eth2/utils/tree_hash/src/lib.rs | 84 +- eth2/utils/tree_hash/src/signed_root.rs | 5 - .../utils/tree_hash/src/standard_tree_hash.rs | 75 - eth2/utils/tree_hash/tests/tests.rs | 1514 ----------------- eth2/utils/tree_hash_derive/Cargo.toml | 1 + eth2/utils/tree_hash_derive/src/lib.rs | 16 +- eth2/utils/tree_hash_derive/tests/tests.rs | 14 +- 22 files changed, 800 insertions(+), 1778 deletions(-) create mode 100644 eth2/utils/cached_tree_hash/Cargo.toml create mode 100644 eth2/utils/cached_tree_hash/README.md rename eth2/utils/{tree_hash/src/cached_tree_hash => cached_tree_hash/src}/btree_overlay.rs (98%) create mode 100644 eth2/utils/cached_tree_hash/src/errors.rs rename eth2/utils/{tree_hash/src/cached_tree_hash => cached_tree_hash/src}/impls.rs (92%) rename eth2/utils/{tree_hash/src/cached_tree_hash => cached_tree_hash/src}/impls/vec.rs (99%) create mode 100644 eth2/utils/cached_tree_hash/src/lib.rs create mode 100644 eth2/utils/cached_tree_hash/src/merkleize.rs rename eth2/utils/{tree_hash/src/cached_tree_hash => cached_tree_hash/src}/resize.rs (100%) rename eth2/utils/{tree_hash/src/cached_tree_hash => cached_tree_hash/src}/tree_hash_cache.rs (98%) create mode 100644 eth2/utils/cached_tree_hash/tests/tests.rs delete mode 100644 eth2/utils/tree_hash/src/cached_tree_hash.rs rename eth2/utils/tree_hash/src/{standard_tree_hash => }/impls.rs (99%) delete mode 100644 eth2/utils/tree_hash/src/signed_root.rs delete mode 100644 eth2/utils/tree_hash/src/standard_tree_hash.rs delete mode 100644 eth2/utils/tree_hash/tests/tests.rs diff --git a/Cargo.toml b/Cargo.toml index b419d32e4..c05e22286 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "eth2/types", "eth2/utils/bls", "eth2/utils/boolean-bitfield", + "eth2/utils/cached_tree_hash", "eth2/utils/hashing", "eth2/utils/honey-badger-split", "eth2/utils/merkle_proof", diff --git a/eth2/types/src/tree_hash_vector.rs b/eth2/types/src/tree_hash_vector.rs index 1cc8e40a5..c90a77c8d 100644 --- a/eth2/types/src/tree_hash_vector.rs +++ b/eth2/types/src/tree_hash_vector.rs @@ -50,7 +50,7 @@ where } fn tree_hash_root(&self) -> Vec { - tree_hash::standard_tree_hash::vec_tree_hash_root(self) + tree_hash::impls::vec_tree_hash_root(self) } } diff --git a/eth2/utils/cached_tree_hash/Cargo.toml b/eth2/utils/cached_tree_hash/Cargo.toml new file mode 100644 index 000000000..c8881eb0f --- /dev/null +++ b/eth2/utils/cached_tree_hash/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "cached_tree_hash" +version = "0.1.0" +authors = ["Paul Hauner "] +edition = "2018" + +[dev-dependencies] +tree_hash_derive = { path = "../tree_hash_derive" } + +[dependencies] +tree_hash = { path = "../tree_hash" } +ethereum-types = "0.5" +hashing = { path = "../hashing" } +int_to_bytes = { path = "../int_to_bytes" } diff --git a/eth2/utils/cached_tree_hash/README.md b/eth2/utils/cached_tree_hash/README.md new file mode 100644 index 000000000..0498bfc3e --- /dev/null +++ b/eth2/utils/cached_tree_hash/README.md @@ -0,0 +1,76 @@ +# Tree hashing + +Provides both cached and non-cached tree hashing methods. + +## Standard Tree Hash + +```rust +use tree_hash_derive::TreeHash; + +#[derive(TreeHash)] +struct Foo { + a: u64, + b: Vec, +} + +fn main() { + let foo = Foo { + a: 42, + b: vec![1, 2, 3] + }; + + println!("root: {}", foo.tree_hash_root()); +} +``` + +## Cached Tree Hash + + +```rust +use tree_hash_derive::{TreeHash, CachedTreeHash}; + +#[derive(TreeHash, CachedTreeHash)] +struct Foo { + a: u64, + b: Vec, +} + +#[derive(TreeHash, CachedTreeHash)] +struct Bar { + a: Vec, + b: u64, +} + +fn main() { + let bar = Bar { + a: vec![ + Foo { + a: 42, + b: vec![1, 2, 3] + } + ], + b: 42 + }; + + let modified_bar = Bar { + a: vec![ + Foo { + a: 100, + b: vec![1, 2, 3, 4, 5, 6] + }, + Foo { + a: 42, + b: vec![] + } + ], + b: 99 + }; + + + let mut hasher = CachedTreeHasher::new(&bar).unwrap(); + hasher.update(&modified_bar).unwrap(); + + // Assert that the cached tree hash matches a standard tree hash. + assert_eq!(hasher.tree_hash_root(), modified_bar.tree_hash_root()); +} +``` diff --git a/eth2/utils/tree_hash/src/cached_tree_hash/btree_overlay.rs b/eth2/utils/cached_tree_hash/src/btree_overlay.rs similarity index 98% rename from eth2/utils/tree_hash/src/cached_tree_hash/btree_overlay.rs rename to eth2/utils/cached_tree_hash/src/btree_overlay.rs index 9fd1251d7..1a8fde3c1 100644 --- a/eth2/utils/tree_hash/src/cached_tree_hash/btree_overlay.rs +++ b/eth2/utils/cached_tree_hash/src/btree_overlay.rs @@ -168,6 +168,10 @@ impl BTreeOverlay { } } +fn children(parent: usize) -> (usize, usize) { + ((2 * parent + 1), (2 * parent + 2)) +} + #[cfg(test)] mod test { use super::*; diff --git a/eth2/utils/cached_tree_hash/src/errors.rs b/eth2/utils/cached_tree_hash/src/errors.rs new file mode 100644 index 000000000..9045d0409 --- /dev/null +++ b/eth2/utils/cached_tree_hash/src/errors.rs @@ -0,0 +1,18 @@ +use tree_hash::TreeHashType; + +#[derive(Debug, PartialEq, Clone)] +pub enum Error { + ShouldNotProduceBTreeOverlay, + NoFirstNode, + NoBytesForRoot, + UnableToObtainSlices, + UnableToGrowMerkleTree, + UnableToShrinkMerkleTree, + TreeCannotHaveZeroNodes, + ShouldNeverBePacked(TreeHashType), + BytesAreNotEvenChunks(usize), + NoModifiedFieldForChunk(usize), + NoBytesForChunk(usize), + NoOverlayForIndex(usize), + NotLeafNode(usize), +} diff --git a/eth2/utils/tree_hash/src/cached_tree_hash/impls.rs b/eth2/utils/cached_tree_hash/src/impls.rs similarity index 92% rename from eth2/utils/tree_hash/src/cached_tree_hash/impls.rs rename to eth2/utils/cached_tree_hash/src/impls.rs index 74ab986cb..80259632d 100644 --- a/eth2/utils/tree_hash/src/cached_tree_hash/impls.rs +++ b/eth2/utils/cached_tree_hash/src/impls.rs @@ -1,4 +1,5 @@ use super::*; +use crate::merkleize::merkleize; mod vec; @@ -17,8 +18,8 @@ impl CachedTreeHash for u64 { fn tree_hash_cache_overlay( &self, - chunk_offset: usize, - depth: usize, + _chunk_offset: usize, + _depth: usize, ) -> Result { panic!("Basic should not produce overlay"); // BTreeOverlay::from_lengths(chunk_offset, 1, depth, vec![1]) @@ -50,8 +51,8 @@ impl CachedTreeHash for usize { fn tree_hash_cache_overlay( &self, - chunk_offset: usize, - depth: usize, + _chunk_offset: usize, + _depth: usize, ) -> Result { panic!("Basic should not produce overlay"); // BTreeOverlay::from_lengths(chunk_offset, 1, depth, vec![1]) diff --git a/eth2/utils/tree_hash/src/cached_tree_hash/impls/vec.rs b/eth2/utils/cached_tree_hash/src/impls/vec.rs similarity index 99% rename from eth2/utils/tree_hash/src/cached_tree_hash/impls/vec.rs rename to eth2/utils/cached_tree_hash/src/impls/vec.rs index c92077e94..8c58e022a 100644 --- a/eth2/utils/tree_hash/src/cached_tree_hash/impls/vec.rs +++ b/eth2/utils/cached_tree_hash/src/impls/vec.rs @@ -1,4 +1,5 @@ use super::*; +use crate::merkleize::{merkleize, num_sanitized_leaves, sanitise_bytes}; impl CachedTreeHash> for Vec where diff --git a/eth2/utils/cached_tree_hash/src/lib.rs b/eth2/utils/cached_tree_hash/src/lib.rs new file mode 100644 index 000000000..539519611 --- /dev/null +++ b/eth2/utils/cached_tree_hash/src/lib.rs @@ -0,0 +1,66 @@ +use hashing::hash; +use std::ops::Range; +use tree_hash::{TreeHash, TreeHashType, BYTES_PER_CHUNK, HASHSIZE}; + +mod btree_overlay; +mod errors; +mod impls; +pub mod merkleize; +mod resize; +mod tree_hash_cache; + +pub use btree_overlay::BTreeOverlay; +pub use errors::Error; +pub use tree_hash_cache::TreeHashCache; + +pub trait CachedTreeHash: TreeHash { + fn tree_hash_cache_overlay( + &self, + chunk_offset: usize, + depth: usize, + ) -> Result; + + fn num_tree_hash_cache_chunks(&self) -> usize; + + fn new_tree_hash_cache(&self, depth: usize) -> Result; + + fn update_tree_hash_cache(&self, cache: &mut TreeHashCache) -> Result<(), Error>; +} + +#[derive(Debug, PartialEq)] +pub struct CachedTreeHasher { + cache: TreeHashCache, +} + +impl CachedTreeHasher { + pub fn new(item: &T) -> Result + where + T: CachedTreeHash, + { + Ok(Self { + cache: TreeHashCache::new(item, 0)?, + }) + } + + pub fn update(&mut self, item: &T) -> Result<(), Error> + where + T: CachedTreeHash, + { + // Reset the per-hash counters. + self.cache.chunk_index = 0; + self.cache.overlay_index = 0; + + // Reset the "modified" flags for the cache. + self.cache.reset_modifications(); + + // Update the cache with the (maybe) changed object. + item.update_tree_hash_cache(&mut self.cache)?; + + Ok(()) + } + + pub fn tree_hash_root(&self) -> Result, Error> { + // Return the root of the cache -- the merkle root. + Ok(self.cache.root()?.to_vec()) + } +} diff --git a/eth2/utils/cached_tree_hash/src/merkleize.rs b/eth2/utils/cached_tree_hash/src/merkleize.rs new file mode 100644 index 000000000..6bfa73888 --- /dev/null +++ b/eth2/utils/cached_tree_hash/src/merkleize.rs @@ -0,0 +1,78 @@ +use hashing::hash; +use tree_hash::{BYTES_PER_CHUNK, HASHSIZE, MERKLE_HASH_CHUNK}; + +/// Split `values` into a power-of-two, identical-length chunks (padding with `0`) and merkleize +/// them, returning the entire merkle tree. +/// +/// The root hash is `merkleize(values)[0..BYTES_PER_CHUNK]`. +pub fn merkleize(values: Vec) -> Vec { + let values = sanitise_bytes(values); + + let leaves = values.len() / HASHSIZE; + + if leaves == 0 { + panic!("No full leaves"); + } + + if !leaves.is_power_of_two() { + panic!("leaves is not power of two"); + } + + let mut o: Vec = vec![0; (num_nodes(leaves) - leaves) * HASHSIZE]; + o.append(&mut values.to_vec()); + + let mut i = o.len(); + let mut j = o.len() - values.len(); + + while i >= MERKLE_HASH_CHUNK { + i -= MERKLE_HASH_CHUNK; + let hash = hash(&o[i..i + MERKLE_HASH_CHUNK]); + + j -= HASHSIZE; + o[j..j + HASHSIZE].copy_from_slice(&hash); + } + + o +} + +pub fn sanitise_bytes(mut bytes: Vec) -> Vec { + let present_leaves = num_unsanitized_leaves(bytes.len()); + let required_leaves = present_leaves.next_power_of_two(); + + if (present_leaves != required_leaves) | last_leaf_needs_padding(bytes.len()) { + bytes.resize(num_bytes(required_leaves), 0); + } + + bytes +} + +pub fn pad_for_leaf_count(num_leaves: usize, bytes: &mut Vec) { + let required_leaves = num_leaves.next_power_of_two(); + + bytes.resize( + bytes.len() + (required_leaves - num_leaves) * BYTES_PER_CHUNK, + 0, + ); +} + +fn last_leaf_needs_padding(num_bytes: usize) -> bool { + num_bytes % HASHSIZE != 0 +} + +/// Rounds up +fn num_unsanitized_leaves(num_bytes: usize) -> usize { + (num_bytes + HASHSIZE - 1) / HASHSIZE +} + +fn num_bytes(num_leaves: usize) -> usize { + num_leaves * HASHSIZE +} + +fn num_nodes(num_leaves: usize) -> usize { + 2 * num_leaves - 1 +} + +pub fn num_sanitized_leaves(num_bytes: usize) -> usize { + let leaves = (num_bytes + HASHSIZE - 1) / HASHSIZE; + leaves.next_power_of_two() +} diff --git a/eth2/utils/tree_hash/src/cached_tree_hash/resize.rs b/eth2/utils/cached_tree_hash/src/resize.rs similarity index 100% rename from eth2/utils/tree_hash/src/cached_tree_hash/resize.rs rename to eth2/utils/cached_tree_hash/src/resize.rs diff --git a/eth2/utils/tree_hash/src/cached_tree_hash/tree_hash_cache.rs b/eth2/utils/cached_tree_hash/src/tree_hash_cache.rs similarity index 98% rename from eth2/utils/tree_hash/src/cached_tree_hash/tree_hash_cache.rs rename to eth2/utils/cached_tree_hash/src/tree_hash_cache.rs index 169edb4d1..d93278d30 100644 --- a/eth2/utils/tree_hash/src/cached_tree_hash/tree_hash_cache.rs +++ b/eth2/utils/cached_tree_hash/src/tree_hash_cache.rs @@ -1,4 +1,6 @@ use super::*; +use crate::merkleize::{merkleize, pad_for_leaf_count}; +use int_to_bytes::int_to_bytes32; #[derive(Debug, PartialEq, Clone)] pub struct TreeHashCache { @@ -328,3 +330,7 @@ impl TreeHashCache { (self.cache, self.chunk_modified, self.overlays) } } + +fn node_range_to_byte_range(node_range: &Range) -> Range { + node_range.start * HASHSIZE..node_range.end * HASHSIZE +} diff --git a/eth2/utils/cached_tree_hash/tests/tests.rs b/eth2/utils/cached_tree_hash/tests/tests.rs new file mode 100644 index 000000000..8837fa1da --- /dev/null +++ b/eth2/utils/cached_tree_hash/tests/tests.rs @@ -0,0 +1,437 @@ +use cached_tree_hash::{merkleize::merkleize, *}; +use int_to_bytes::int_to_bytes32; +use tree_hash_derive::{CachedTreeHash, TreeHash}; + +#[derive(Clone, Debug, TreeHash, CachedTreeHash)] +pub struct NestedStruct { + pub a: u64, + pub b: Inner, +} + +fn test_routine(original: T, modified: Vec) +where + T: CachedTreeHash, +{ + let mut hasher = CachedTreeHasher::new(&original).unwrap(); + + let standard_root = original.tree_hash_root(); + let cached_root = hasher.tree_hash_root().unwrap(); + assert_eq!(standard_root, cached_root, "Initial cache build failed."); + + for (i, modified) in modified.iter().enumerate() { + println!("-- Start of modification {} --", i); + // Test after a modification + hasher + .update(modified) + .expect(&format!("Modification {}", i)); + let standard_root = modified.tree_hash_root(); + let cached_root = hasher + .tree_hash_root() + .expect(&format!("Modification {}", i)); + assert_eq!( + standard_root, cached_root, + "Modification {} failed. \n Cache: {:?}", + i, hasher + ); + } +} + +#[test] +fn test_nested_struct() { + let original = NestedStruct { + a: 42, + b: Inner { + a: 12, + b: 13, + c: 14, + d: 15, + }, + }; + let modified = vec![NestedStruct { + a: 99, + ..original.clone() + }]; + + test_routine(original, modified); +} + +#[test] +fn test_inner() { + let original = Inner { + a: 12, + b: 13, + c: 14, + d: 15, + }; + + let modified = vec![Inner { + a: 99, + ..original.clone() + }]; + + test_routine(original, modified); +} + +#[test] +fn test_vec() { + let original: Vec = vec![1, 2, 3, 4, 5]; + + let modified: Vec> = vec![ + vec![1, 2, 3, 4, 42], + vec![1, 2, 3, 4], + vec![], + vec![42; 2_usize.pow(4)], + vec![], + vec![], + vec![1, 2, 3, 4, 42], + vec![1, 2, 3], + vec![1], + ]; + + test_routine(original, modified); +} + +#[test] +fn test_nested_list_of_u64() { + let original: Vec> = vec![vec![42]]; + + let modified = vec![ + vec![vec![1]], + vec![vec![1], vec![2]], + vec![vec![1], vec![3], vec![4]], + vec![], + vec![vec![1], vec![3], vec![4]], + vec![], + vec![vec![1, 2], vec![3], vec![4, 5, 6, 7, 8]], + vec![], + vec![vec![1], vec![2], vec![3]], + vec![vec![1, 2, 3, 4, 5, 6], vec![1, 2, 3, 4, 5, 6, 7]], + vec![vec![], vec![], vec![]], + vec![vec![0, 0, 0], vec![0], vec![0]], + ]; + + test_routine(original, modified); +} + +#[derive(Clone, Debug, TreeHash, CachedTreeHash)] +pub struct StructWithVec { + pub a: u64, + pub b: Inner, + pub c: Vec, +} + +#[test] +fn test_struct_with_vec() { + let original = StructWithVec { + a: 42, + b: Inner { + a: 12, + b: 13, + c: 14, + d: 15, + }, + c: vec![1, 2, 3, 4, 5], + }; + + let modified = vec![ + StructWithVec { + a: 99, + ..original.clone() + }, + StructWithVec { + a: 100, + ..original.clone() + }, + StructWithVec { + c: vec![1, 2, 3, 4, 5], + ..original.clone() + }, + StructWithVec { + c: vec![1, 3, 4, 5, 6], + ..original.clone() + }, + StructWithVec { + c: vec![1, 3, 4, 5, 6, 7, 8, 9], + ..original.clone() + }, + StructWithVec { + c: vec![1, 3, 4, 5], + ..original.clone() + }, + StructWithVec { + b: Inner { + a: u64::max_value(), + b: u64::max_value(), + c: u64::max_value(), + d: u64::max_value(), + }, + c: vec![], + ..original.clone() + }, + StructWithVec { + b: Inner { + a: 0, + b: 1, + c: 2, + d: 3, + }, + ..original.clone() + }, + ]; + + test_routine(original, modified); +} + +#[test] +fn test_vec_of_struct_with_vec() { + let a = StructWithVec { + a: 42, + b: Inner { + a: 12, + b: 13, + c: 14, + d: 15, + }, + c: vec![1, 2, 3, 4, 5], + }; + let b = StructWithVec { + c: vec![], + ..a.clone() + }; + let c = StructWithVec { + b: Inner { + a: 99, + b: 100, + c: 101, + d: 102, + }, + ..a.clone() + }; + let d = StructWithVec { a: 0, ..a.clone() }; + + // let original: Vec = vec![a.clone(), c.clone()]; + let original: Vec = vec![a.clone()]; + + let modified = vec![ + vec![a.clone(), c.clone()], + vec![a.clone(), b.clone(), c.clone(), d.clone()], + vec![b.clone(), a.clone(), c.clone(), d.clone()], + vec![], + vec![a.clone()], + vec![a.clone(), b.clone(), c.clone(), d.clone()], + ]; + + test_routine(original, modified); +} + +#[derive(Clone, Debug, TreeHash, CachedTreeHash)] +pub struct StructWithVecOfStructs { + pub a: u64, + pub b: Inner, + pub c: Vec, +} + +fn get_struct_with_vec_of_structs() -> Vec { + let inner_a = Inner { + a: 12, + b: 13, + c: 14, + d: 15, + }; + + let inner_b = Inner { + a: 99, + b: 100, + c: 101, + d: 102, + }; + + let inner_c = Inner { + a: 255, + b: 256, + c: 257, + d: 0, + }; + + let a = StructWithVecOfStructs { + a: 42, + b: inner_a.clone(), + c: vec![inner_a.clone(), inner_b.clone(), inner_c.clone()], + }; + + let b = StructWithVecOfStructs { + c: vec![], + ..a.clone() + }; + + let c = StructWithVecOfStructs { + a: 800, + ..a.clone() + }; + + let d = StructWithVecOfStructs { + b: inner_c.clone(), + ..a.clone() + }; + + let e = StructWithVecOfStructs { + c: vec![inner_a.clone(), inner_b.clone()], + ..a.clone() + }; + + let f = StructWithVecOfStructs { + c: vec![inner_a.clone()], + ..a.clone() + }; + + vec![a, b, c, d, e, f] +} + +#[test] +fn test_struct_with_vec_of_structs() { + let variants = get_struct_with_vec_of_structs(); + + test_routine(variants[0].clone(), variants.clone()); + test_routine(variants[1].clone(), variants.clone()); + test_routine(variants[2].clone(), variants.clone()); + test_routine(variants[3].clone(), variants.clone()); + test_routine(variants[4].clone(), variants.clone()); + test_routine(variants[5].clone(), variants.clone()); +} + +#[derive(Clone, Debug, TreeHash, CachedTreeHash)] +pub struct StructWithVecOfStructWithVecOfStructs { + pub a: Vec, + pub b: u64, +} + +#[test] +fn test_struct_with_vec_of_struct_with_vec_of_structs() { + let structs = get_struct_with_vec_of_structs(); + + let variants = vec![ + StructWithVecOfStructWithVecOfStructs { + a: structs[..].to_vec(), + b: 99, + }, + StructWithVecOfStructWithVecOfStructs { a: vec![], b: 99 }, + StructWithVecOfStructWithVecOfStructs { + a: structs[0..2].to_vec(), + b: 99, + }, + StructWithVecOfStructWithVecOfStructs { + a: structs[0..2].to_vec(), + b: 100, + }, + StructWithVecOfStructWithVecOfStructs { + a: structs[0..1].to_vec(), + b: 100, + }, + StructWithVecOfStructWithVecOfStructs { + a: structs[0..4].to_vec(), + b: 100, + }, + StructWithVecOfStructWithVecOfStructs { + a: structs[0..5].to_vec(), + b: 8, + }, + ]; + + for v in &variants { + test_routine(v.clone(), variants.clone()); + } +} + +#[derive(Clone, Debug, TreeHash, CachedTreeHash)] +pub struct Inner { + pub a: u64, + pub b: u64, + pub c: u64, + pub d: u64, +} + +fn generic_test(index: usize) { + let inner = Inner { + a: 1, + b: 2, + c: 3, + d: 4, + }; + + let mut cache = TreeHashCache::new(&inner, 0).unwrap(); + + let changed_inner = match index { + 0 => Inner { + a: 42, + ..inner.clone() + }, + 1 => Inner { + b: 42, + ..inner.clone() + }, + 2 => Inner { + c: 42, + ..inner.clone() + }, + 3 => Inner { + d: 42, + ..inner.clone() + }, + _ => panic!("bad index"), + }; + + changed_inner.update_tree_hash_cache(&mut cache).unwrap(); + + let data1 = int_to_bytes32(1); + let data2 = int_to_bytes32(2); + let data3 = int_to_bytes32(3); + let data4 = int_to_bytes32(4); + + let mut data = vec![data1, data2, data3, data4]; + + data[index] = int_to_bytes32(42); + + let expected = merkleize(join(data)); + + let cache_bytes: Vec = cache.into(); + + assert_eq!(expected, cache_bytes); +} + +#[test] +fn cached_hash_on_inner() { + generic_test(0); + generic_test(1); + generic_test(2); + generic_test(3); +} + +#[test] +fn inner_builds() { + let data1 = int_to_bytes32(1); + let data2 = int_to_bytes32(2); + let data3 = int_to_bytes32(3); + let data4 = int_to_bytes32(4); + + let data = join(vec![data1, data2, data3, data4]); + let expected = merkleize(data); + + let inner = Inner { + a: 1, + b: 2, + c: 3, + d: 4, + }; + + let cache: Vec = TreeHashCache::new(&inner, 0).unwrap().into(); + + assert_eq!(expected, cache); +} + +fn join(many: Vec>) -> Vec { + let mut all = vec![]; + for one in many { + all.extend_from_slice(&mut one.clone()) + } + all +} diff --git a/eth2/utils/tree_hash/src/cached_tree_hash.rs b/eth2/utils/tree_hash/src/cached_tree_hash.rs deleted file mode 100644 index 0183f5c84..000000000 --- a/eth2/utils/tree_hash/src/cached_tree_hash.rs +++ /dev/null @@ -1,156 +0,0 @@ -use super::*; -use hashing::hash; -use int_to_bytes::int_to_bytes32; -use std::ops::Range; - -pub mod btree_overlay; -pub mod impls; -pub mod resize; -pub mod tree_hash_cache; - -pub use btree_overlay::BTreeOverlay; -pub use tree_hash_cache::TreeHashCache; - -#[derive(Debug, PartialEq)] -pub struct CachedTreeHasher { - cache: TreeHashCache, -} - -impl CachedTreeHasher { - pub fn new(item: &T) -> Result - where - T: CachedTreeHash, - { - Ok(Self { - cache: TreeHashCache::new(item, 0)?, - }) - } - - pub fn update(&mut self, item: &T) -> Result<(), Error> - where - T: CachedTreeHash, - { - // Reset the per-hash counters. - self.cache.chunk_index = 0; - self.cache.overlay_index = 0; - - // Reset the "modified" flags for the cache. - self.cache.reset_modifications(); - - // Update the cache with the (maybe) changed object. - item.update_tree_hash_cache(&mut self.cache)?; - - Ok(()) - } - - pub fn tree_hash_root(&self) -> Result, Error> { - // Return the root of the cache -- the merkle root. - Ok(self.cache.root()?.to_vec()) - } -} - -#[derive(Debug, PartialEq, Clone)] -pub enum Error { - ShouldNotProduceBTreeOverlay, - NoFirstNode, - NoBytesForRoot, - UnableToObtainSlices, - UnableToGrowMerkleTree, - UnableToShrinkMerkleTree, - TreeCannotHaveZeroNodes, - ShouldNeverBePacked(TreeHashType), - BytesAreNotEvenChunks(usize), - NoModifiedFieldForChunk(usize), - NoBytesForChunk(usize), - NoOverlayForIndex(usize), - NotLeafNode(usize), -} - -pub trait CachedTreeHash: TreeHash { - fn tree_hash_cache_overlay( - &self, - chunk_offset: usize, - depth: usize, - ) -> Result; - - fn num_tree_hash_cache_chunks(&self) -> usize; - - fn new_tree_hash_cache(&self, depth: usize) -> Result; - - fn update_tree_hash_cache(&self, cache: &mut TreeHashCache) -> Result<(), Error>; -} - -fn children(parent: usize) -> (usize, usize) { - ((2 * parent + 1), (2 * parent + 2)) -} - -fn node_range_to_byte_range(node_range: &Range) -> Range { - node_range.start * HASHSIZE..node_range.end * HASHSIZE -} - -/// Split `values` into a power-of-two, identical-length chunks (padding with `0`) and merkleize -/// them, returning the entire merkle tree. -/// -/// The root hash is `merkleize(values)[0..BYTES_PER_CHUNK]`. -pub fn merkleize(values: Vec) -> Vec { - let values = sanitise_bytes(values); - - let leaves = values.len() / HASHSIZE; - - if leaves == 0 { - panic!("No full leaves"); - } - - if !leaves.is_power_of_two() { - panic!("leaves is not power of two"); - } - - let mut o: Vec = vec![0; (num_nodes(leaves) - leaves) * HASHSIZE]; - o.append(&mut values.to_vec()); - - let mut i = o.len(); - let mut j = o.len() - values.len(); - - while i >= MERKLE_HASH_CHUNCK { - i -= MERKLE_HASH_CHUNCK; - let hash = hash(&o[i..i + MERKLE_HASH_CHUNCK]); - - j -= HASHSIZE; - o[j..j + HASHSIZE].copy_from_slice(&hash); - } - - o -} - -pub fn sanitise_bytes(mut bytes: Vec) -> Vec { - let present_leaves = num_unsanitized_leaves(bytes.len()); - let required_leaves = present_leaves.next_power_of_two(); - - if (present_leaves != required_leaves) | last_leaf_needs_padding(bytes.len()) { - bytes.resize(num_bytes(required_leaves), 0); - } - - bytes -} - -fn pad_for_leaf_count(num_leaves: usize, bytes: &mut Vec) { - let required_leaves = num_leaves.next_power_of_two(); - - bytes.resize( - bytes.len() + (required_leaves - num_leaves) * BYTES_PER_CHUNK, - 0, - ); -} - -fn last_leaf_needs_padding(num_bytes: usize) -> bool { - num_bytes % HASHSIZE != 0 -} - -/// Rounds up -fn num_unsanitized_leaves(num_bytes: usize) -> usize { - (num_bytes + HASHSIZE - 1) / HASHSIZE -} - -fn num_bytes(num_leaves: usize) -> usize { - num_leaves * HASHSIZE -} diff --git a/eth2/utils/tree_hash/src/standard_tree_hash/impls.rs b/eth2/utils/tree_hash/src/impls.rs similarity index 99% rename from eth2/utils/tree_hash/src/standard_tree_hash/impls.rs rename to eth2/utils/tree_hash/src/impls.rs index be6b4ba07..01b165150 100644 --- a/eth2/utils/tree_hash/src/standard_tree_hash/impls.rs +++ b/eth2/utils/tree_hash/src/impls.rs @@ -1,5 +1,6 @@ use super::*; use ethereum_types::H256; +use int_to_bytes::int_to_bytes32; macro_rules! impl_for_bitsize { ($type: ident, $bit_size: expr) => { diff --git a/eth2/utils/tree_hash/src/lib.rs b/eth2/utils/tree_hash/src/lib.rs index 5aaf2d585..6ed0247f1 100644 --- a/eth2/utils/tree_hash/src/lib.rs +++ b/eth2/utils/tree_hash/src/lib.rs @@ -1,14 +1,10 @@ -pub mod cached_tree_hash; -pub mod signed_root; -pub mod standard_tree_hash; +use hashing::hash; + +pub mod impls; pub const BYTES_PER_CHUNK: usize = 32; pub const HASHSIZE: usize = 32; -pub const MERKLE_HASH_CHUNCK: usize = 2 * BYTES_PER_CHUNK; - -pub use cached_tree_hash::{BTreeOverlay, CachedTreeHash, Error, TreeHashCache}; -pub use signed_root::SignedRoot; -pub use standard_tree_hash::{merkle_root, TreeHash}; +pub const MERKLE_HASH_CHUNK: usize = 2 * BYTES_PER_CHUNK; #[derive(Debug, PartialEq, Clone)] pub enum TreeHashType { @@ -18,6 +14,78 @@ pub enum TreeHashType { Container, } +pub trait TreeHash { + fn tree_hash_type() -> TreeHashType; + + fn tree_hash_packed_encoding(&self) -> Vec; + + fn tree_hash_packing_factor() -> usize; + + fn tree_hash_root(&self) -> Vec; +} + +pub trait SignedRoot: TreeHash { + fn signed_root(&self) -> Vec; +} + +pub fn merkle_root(bytes: &[u8]) -> Vec { + // TODO: replace this with a more memory efficient method. + efficient_merkleize(&bytes)[0..32].to_vec() +} + +pub fn efficient_merkleize(bytes: &[u8]) -> Vec { + // If the bytes are just one chunk (or less than one chunk) just return them. + if bytes.len() <= HASHSIZE { + let mut o = bytes.to_vec(); + o.resize(HASHSIZE, 0); + return o; + } + + let leaves = num_sanitized_leaves(bytes.len()); + let nodes = num_nodes(leaves); + let internal_nodes = nodes - leaves; + + let num_bytes = std::cmp::max(internal_nodes, 1) * HASHSIZE + bytes.len(); + + let mut o: Vec = vec![0; internal_nodes * HASHSIZE]; + + o.append(&mut bytes.to_vec()); + + assert_eq!(o.len(), num_bytes); + + let empty_chunk_hash = hash(&[0; MERKLE_HASH_CHUNK]); + + let mut i = nodes * HASHSIZE; + let mut j = internal_nodes * HASHSIZE; + + while i >= MERKLE_HASH_CHUNK { + i -= MERKLE_HASH_CHUNK; + + j -= HASHSIZE; + let hash = match o.get(i..i + MERKLE_HASH_CHUNK) { + // All bytes are available, hash as ususal. + Some(slice) => hash(slice), + // Unable to get all the bytes. + None => { + match o.get(i..) { + // Able to get some of the bytes, pad them out. + Some(slice) => { + let mut bytes = slice.to_vec(); + bytes.resize(MERKLE_HASH_CHUNK, 0); + hash(&bytes) + } + // Unable to get any bytes, use the empty-chunk hash. + None => empty_chunk_hash.clone(), + } + } + }; + + o[j..j + HASHSIZE].copy_from_slice(&hash); + } + + o +} + fn num_sanitized_leaves(num_bytes: usize) -> usize { let leaves = (num_bytes + HASHSIZE - 1) / HASHSIZE; leaves.next_power_of_two() diff --git a/eth2/utils/tree_hash/src/signed_root.rs b/eth2/utils/tree_hash/src/signed_root.rs deleted file mode 100644 index f7aeca4af..000000000 --- a/eth2/utils/tree_hash/src/signed_root.rs +++ /dev/null @@ -1,5 +0,0 @@ -use crate::TreeHash; - -pub trait SignedRoot: TreeHash { - fn signed_root(&self) -> Vec; -} diff --git a/eth2/utils/tree_hash/src/standard_tree_hash.rs b/eth2/utils/tree_hash/src/standard_tree_hash.rs deleted file mode 100644 index 812a2c352..000000000 --- a/eth2/utils/tree_hash/src/standard_tree_hash.rs +++ /dev/null @@ -1,75 +0,0 @@ -use super::*; -use hashing::hash; -use int_to_bytes::int_to_bytes32; - -pub use impls::vec_tree_hash_root; - -mod impls; - -pub trait TreeHash { - fn tree_hash_type() -> TreeHashType; - - fn tree_hash_packed_encoding(&self) -> Vec; - - fn tree_hash_packing_factor() -> usize; - - fn tree_hash_root(&self) -> Vec; -} - -pub fn merkle_root(bytes: &[u8]) -> Vec { - // TODO: replace this with a more memory efficient method. - efficient_merkleize(&bytes)[0..32].to_vec() -} - -pub fn efficient_merkleize(bytes: &[u8]) -> Vec { - // If the bytes are just one chunk (or less than one chunk) just return them. - if bytes.len() <= HASHSIZE { - let mut o = bytes.to_vec(); - o.resize(HASHSIZE, 0); - return o; - } - - let leaves = num_sanitized_leaves(bytes.len()); - let nodes = num_nodes(leaves); - let internal_nodes = nodes - leaves; - - let num_bytes = std::cmp::max(internal_nodes, 1) * HASHSIZE + bytes.len(); - - let mut o: Vec = vec![0; internal_nodes * HASHSIZE]; - - o.append(&mut bytes.to_vec()); - - assert_eq!(o.len(), num_bytes); - - let empty_chunk_hash = hash(&[0; MERKLE_HASH_CHUNCK]); - - let mut i = nodes * HASHSIZE; - let mut j = internal_nodes * HASHSIZE; - - while i >= MERKLE_HASH_CHUNCK { - i -= MERKLE_HASH_CHUNCK; - - j -= HASHSIZE; - let hash = match o.get(i..i + MERKLE_HASH_CHUNCK) { - // All bytes are available, hash as ususal. - Some(slice) => hash(slice), - // Unable to get all the bytes. - None => { - match o.get(i..) { - // Able to get some of the bytes, pad them out. - Some(slice) => { - let mut bytes = slice.to_vec(); - bytes.resize(MERKLE_HASH_CHUNCK, 0); - hash(&bytes) - } - // Unable to get any bytes, use the empty-chunk hash. - None => empty_chunk_hash.clone(), - } - } - }; - - o[j..j + HASHSIZE].copy_from_slice(&hash); - } - - o -} diff --git a/eth2/utils/tree_hash/tests/tests.rs b/eth2/utils/tree_hash/tests/tests.rs deleted file mode 100644 index e3d8701bd..000000000 --- a/eth2/utils/tree_hash/tests/tests.rs +++ /dev/null @@ -1,1514 +0,0 @@ -use int_to_bytes::int_to_bytes32; -use tree_hash::cached_tree_hash::*; -use tree_hash_derive::{CachedTreeHash, TreeHash}; - -#[derive(Clone, Debug, TreeHash, CachedTreeHash)] -pub struct NestedStruct { - pub a: u64, - pub b: Inner, -} - -fn test_routine(original: T, modified: Vec) -where - T: CachedTreeHash, -{ - let mut hasher = CachedTreeHasher::new(&original).unwrap(); - - let standard_root = original.tree_hash_root(); - let cached_root = hasher.tree_hash_root().unwrap(); - assert_eq!(standard_root, cached_root, "Initial cache build failed."); - - for (i, modified) in modified.iter().enumerate() { - println!("-- Start of modification {} --", i); - // Test after a modification - hasher - .update(modified) - .expect(&format!("Modification {}", i)); - let standard_root = modified.tree_hash_root(); - let cached_root = hasher - .tree_hash_root() - .expect(&format!("Modification {}", i)); - assert_eq!( - standard_root, cached_root, - "Modification {} failed. \n Cache: {:?}", - i, hasher - ); - } -} - -#[test] -fn test_nested_struct() { - let original = NestedStruct { - a: 42, - b: Inner { - a: 12, - b: 13, - c: 14, - d: 15, - }, - }; - let modified = vec![NestedStruct { - a: 99, - ..original.clone() - }]; - - test_routine(original, modified); -} - -#[test] -fn test_inner() { - let original = Inner { - a: 12, - b: 13, - c: 14, - d: 15, - }; - - let modified = vec![Inner { - a: 99, - ..original.clone() - }]; - - test_routine(original, modified); -} - -#[test] -fn test_vec() { - let original: Vec = vec![1, 2, 3, 4, 5]; - - let modified: Vec> = vec![ - vec![1, 2, 3, 4, 42], - vec![1, 2, 3, 4], - vec![], - vec![42; 2_usize.pow(4)], - vec![], - vec![], - vec![1, 2, 3, 4, 42], - vec![1, 2, 3], - vec![1], - ]; - - test_routine(original, modified); -} - -#[test] -fn test_nested_list_of_u64() { - let original: Vec> = vec![vec![42]]; - - let modified = vec![ - vec![vec![1]], - vec![vec![1], vec![2]], - vec![vec![1], vec![3], vec![4]], - vec![], - vec![vec![1], vec![3], vec![4]], - vec![], - vec![vec![1, 2], vec![3], vec![4, 5, 6, 7, 8]], - vec![], - vec![vec![1], vec![2], vec![3]], - vec![vec![1, 2, 3, 4, 5, 6], vec![1, 2, 3, 4, 5, 6, 7]], - vec![vec![], vec![], vec![]], - vec![vec![0, 0, 0], vec![0], vec![0]], - ]; - - test_routine(original, modified); -} - -#[derive(Clone, Debug, TreeHash, CachedTreeHash)] -pub struct StructWithVec { - pub a: u64, - pub b: Inner, - pub c: Vec, -} - -#[test] -fn test_struct_with_vec() { - let original = StructWithVec { - a: 42, - b: Inner { - a: 12, - b: 13, - c: 14, - d: 15, - }, - c: vec![1, 2, 3, 4, 5], - }; - - let modified = vec![ - StructWithVec { - a: 99, - ..original.clone() - }, - StructWithVec { - a: 100, - ..original.clone() - }, - StructWithVec { - c: vec![1, 2, 3, 4, 5], - ..original.clone() - }, - StructWithVec { - c: vec![1, 3, 4, 5, 6], - ..original.clone() - }, - StructWithVec { - c: vec![1, 3, 4, 5, 6, 7, 8, 9], - ..original.clone() - }, - StructWithVec { - c: vec![1, 3, 4, 5], - ..original.clone() - }, - StructWithVec { - b: Inner { - a: u64::max_value(), - b: u64::max_value(), - c: u64::max_value(), - d: u64::max_value(), - }, - c: vec![], - ..original.clone() - }, - StructWithVec { - b: Inner { - a: 0, - b: 1, - c: 2, - d: 3, - }, - ..original.clone() - }, - ]; - - test_routine(original, modified); -} - -#[test] -fn test_vec_of_struct_with_vec() { - let a = StructWithVec { - a: 42, - b: Inner { - a: 12, - b: 13, - c: 14, - d: 15, - }, - c: vec![1, 2, 3, 4, 5], - }; - let b = StructWithVec { - c: vec![], - ..a.clone() - }; - let c = StructWithVec { - b: Inner { - a: 99, - b: 100, - c: 101, - d: 102, - }, - ..a.clone() - }; - let d = StructWithVec { a: 0, ..a.clone() }; - - // let original: Vec = vec![a.clone(), c.clone()]; - let original: Vec = vec![a.clone()]; - - let modified = vec![ - vec![a.clone(), c.clone()], - vec![a.clone(), b.clone(), c.clone(), d.clone()], - vec![b.clone(), a.clone(), c.clone(), d.clone()], - vec![], - vec![a.clone()], - vec![a.clone(), b.clone(), c.clone(), d.clone()], - ]; - - test_routine(original, modified); -} - -#[derive(Clone, Debug, TreeHash, CachedTreeHash)] -pub struct StructWithVecOfStructs { - pub a: u64, - pub b: Inner, - pub c: Vec, -} - -fn get_struct_with_vec_of_structs() -> Vec { - let inner_a = Inner { - a: 12, - b: 13, - c: 14, - d: 15, - }; - - let inner_b = Inner { - a: 99, - b: 100, - c: 101, - d: 102, - }; - - let inner_c = Inner { - a: 255, - b: 256, - c: 257, - d: 0, - }; - - let a = StructWithVecOfStructs { - a: 42, - b: inner_a.clone(), - c: vec![inner_a.clone(), inner_b.clone(), inner_c.clone()], - }; - - let b = StructWithVecOfStructs { - c: vec![], - ..a.clone() - }; - - let c = StructWithVecOfStructs { - a: 800, - ..a.clone() - }; - - let d = StructWithVecOfStructs { - b: inner_c.clone(), - ..a.clone() - }; - - let e = StructWithVecOfStructs { - c: vec![inner_a.clone(), inner_b.clone()], - ..a.clone() - }; - - let f = StructWithVecOfStructs { - c: vec![inner_a.clone()], - ..a.clone() - }; - - vec![a, b, c, d, e, f] -} - -#[test] -fn test_struct_with_vec_of_structs() { - let variants = get_struct_with_vec_of_structs(); - - test_routine(variants[0].clone(), variants.clone()); - test_routine(variants[1].clone(), variants.clone()); - test_routine(variants[2].clone(), variants.clone()); - test_routine(variants[3].clone(), variants.clone()); - test_routine(variants[4].clone(), variants.clone()); - test_routine(variants[5].clone(), variants.clone()); -} - -#[derive(Clone, Debug, TreeHash, CachedTreeHash)] -pub struct StructWithVecOfStructWithVecOfStructs { - pub a: Vec, - pub b: u64, -} - -#[test] -fn test_struct_with_vec_of_struct_with_vec_of_structs() { - let structs = get_struct_with_vec_of_structs(); - - let variants = vec![ - StructWithVecOfStructWithVecOfStructs { - a: structs[..].to_vec(), - b: 99, - }, - StructWithVecOfStructWithVecOfStructs { a: vec![], b: 99 }, - StructWithVecOfStructWithVecOfStructs { - a: structs[0..2].to_vec(), - b: 99, - }, - StructWithVecOfStructWithVecOfStructs { - a: structs[0..2].to_vec(), - b: 100, - }, - StructWithVecOfStructWithVecOfStructs { - a: structs[0..1].to_vec(), - b: 100, - }, - StructWithVecOfStructWithVecOfStructs { - a: structs[0..4].to_vec(), - b: 100, - }, - StructWithVecOfStructWithVecOfStructs { - a: structs[0..5].to_vec(), - b: 8, - }, - ]; - - for v in &variants { - test_routine(v.clone(), variants.clone()); - } -} - -#[derive(Clone, Debug, TreeHash, CachedTreeHash)] -pub struct Inner { - pub a: u64, - pub b: u64, - pub c: u64, - pub d: u64, -} - -fn generic_test(index: usize) { - let inner = Inner { - a: 1, - b: 2, - c: 3, - d: 4, - }; - - let mut cache = TreeHashCache::new(&inner, 0).unwrap(); - - let changed_inner = match index { - 0 => Inner { - a: 42, - ..inner.clone() - }, - 1 => Inner { - b: 42, - ..inner.clone() - }, - 2 => Inner { - c: 42, - ..inner.clone() - }, - 3 => Inner { - d: 42, - ..inner.clone() - }, - _ => panic!("bad index"), - }; - - changed_inner.update_tree_hash_cache(&mut cache).unwrap(); - - let data1 = int_to_bytes32(1); - let data2 = int_to_bytes32(2); - let data3 = int_to_bytes32(3); - let data4 = int_to_bytes32(4); - - let mut data = vec![data1, data2, data3, data4]; - - data[index] = int_to_bytes32(42); - - let expected = merkleize(join(data)); - - let cache_bytes: Vec = cache.into(); - - assert_eq!(expected, cache_bytes); -} - -#[test] -fn cached_hash_on_inner() { - generic_test(0); - generic_test(1); - generic_test(2); - generic_test(3); -} - -#[test] -fn inner_builds() { - let data1 = int_to_bytes32(1); - let data2 = int_to_bytes32(2); - let data3 = int_to_bytes32(3); - let data4 = int_to_bytes32(4); - - let data = join(vec![data1, data2, data3, data4]); - let expected = merkleize(data); - - let inner = Inner { - a: 1, - b: 2, - c: 3, - d: 4, - }; - - let cache: Vec = TreeHashCache::new(&inner, 0).unwrap().into(); - - assert_eq!(expected, cache); -} - -fn join(many: Vec>) -> Vec { - let mut all = vec![]; - for one in many { - all.extend_from_slice(&mut one.clone()) - } - all -} - -/* -#[derive(Clone, Debug)] -pub struct InternalCache { - pub a: u64, - pub b: u64, - pub cache: Option, -} - -impl TreeHash for InternalCache { - fn tree_hash_type() -> TreeHashType { - TreeHashType::Container - } - - fn tree_hash_packed_encoding(&self) -> Vec { - unreachable!("Struct should never be packed.") - } - - fn tree_hash_packing_factor() -> usize { - unreachable!("Struct should never be packed.") - } - - fn tree_hash_root(&self) -> Vec { - let mut leaves = Vec::with_capacity(4 * HASHSIZE); - - leaves.append(&mut self.a.tree_hash_root()); - leaves.append(&mut self.b.tree_hash_root()); - - efficient_merkleize(&leaves)[0..32].to_vec() - } -} - -impl CachedTreeHash for InternalCache { - fn update_internal_tree_hash_cache(mut self, mut old: Self) -> Result<(Self, Self), Error> { - let mut local_cache = old.cache; - old.cache = None; - - if let Some(ref mut local_cache) = local_cache { - self.update_tree_hash_cache(&old, local_cache, 0)?; - } else { - local_cache = Some(self.new_tree_hash_cache()?) - } - - self.cache = local_cache; - - Ok((old, self)) - } - - fn cached_tree_hash_root(&self) -> Option> { - match &self.cache { - None => None, - Some(c) => Some(c.root()?.to_vec()), - } - } - - fn clone_without_tree_hash_cache(&self) -> Self { - Self { - a: self.a, - b: self.b, - cache: None, - } - } -} - -#[test] -fn works_when_embedded() { - let old = InternalCache { - a: 99, - b: 99, - cache: None, - }; - - let mut new = old.clone_without_tree_hash_cache(); - new.a = 1; - new.b = 2; - - let (_old, new) = new.update_internal_tree_hash_cache(old).unwrap(); - - let root = new.cached_tree_hash_root().unwrap(); - - let leaves = vec![int_to_bytes32(1), int_to_bytes32(2)]; - let merkle = merkleize(join(leaves)); - - assert_eq!(&merkle[0..32], &root[..]); -} - -impl CachedTreeHash for InternalCache { - fn new_tree_hash_cache(&self) -> Result { - let tree = TreeHashCache::from_leaves_and_subtrees( - self, - vec![self.a.new_tree_hash_cache()?, self.b.new_tree_hash_cache()?], - )?; - - Ok(tree) - } - - fn tree_hash_cache_overlay(&self, chunk_offset: usize) -> Result { - let mut lengths = vec![]; - - lengths.push(BTreeOverlay::new(&self.a, 0)?.num_nodes()); - lengths.push(BTreeOverlay::new(&self.b, 0)?.num_nodes()); - - BTreeOverlay::from_lengths(chunk_offset, lengths) - } - - fn update_tree_hash_cache( - &self, - other: &Self, - cache: &mut TreeHashCache, - chunk: usize, - ) -> Result { - let offset_handler = BTreeOverlay::new(self, chunk)?; - - // Skip past the internal nodes and update any changed leaf nodes. - { - let chunk = offset_handler.first_leaf_node()?; - let chunk = self.a.update_tree_hash_cache(&other.a, cache, chunk)?; - let _chunk = self.b.update_tree_hash_cache(&other.b, cache, chunk)?; - } - - for (&parent, children) in offset_handler.iter_internal_nodes().rev() { - if cache.either_modified(children)? { - cache.modify_chunk(parent, &cache.hash_children(children)?)?; - } - } - - Ok(offset_handler.next_node) - } -} - -fn num_nodes(num_leaves: usize) -> usize { - 2 * num_leaves - 1 -} - -#[derive(Clone, Debug)] -pub struct Inner { - pub a: u64, - pub b: u64, - pub c: u64, - pub d: u64, -} - -impl TreeHash for Inner { - fn tree_hash_type() -> TreeHashType { - TreeHashType::Container - } - - fn tree_hash_packed_encoding(&self) -> Vec { - unreachable!("Struct should never be packed.") - } - - fn tree_hash_packing_factor() -> usize { - unreachable!("Struct should never be packed.") - } - - fn tree_hash_root(&self) -> Vec { - let mut leaves = Vec::with_capacity(4 * HASHSIZE); - - leaves.append(&mut self.a.tree_hash_root()); - leaves.append(&mut self.b.tree_hash_root()); - leaves.append(&mut self.c.tree_hash_root()); - leaves.append(&mut self.d.tree_hash_root()); - - efficient_merkleize(&leaves)[0..32].to_vec() - } -} - -impl CachedTreeHash for Inner { - fn new_tree_hash_cache(&self) -> Result { - let tree = TreeHashCache::from_leaves_and_subtrees( - self, - vec![ - self.a.new_tree_hash_cache()?, - self.b.new_tree_hash_cache()?, - self.c.new_tree_hash_cache()?, - self.d.new_tree_hash_cache()?, - ], - )?; - - Ok(tree) - } - - fn tree_hash_cache_overlay(&self, chunk_offset: usize) -> Result { - let mut lengths = vec![]; - - lengths.push(BTreeOverlay::new(&self.a, 0)?.num_nodes()); - lengths.push(BTreeOverlay::new(&self.b, 0)?.num_nodes()); - lengths.push(BTreeOverlay::new(&self.c, 0)?.num_nodes()); - lengths.push(BTreeOverlay::new(&self.d, 0)?.num_nodes()); - - BTreeOverlay::from_lengths(chunk_offset, lengths) - } - - fn update_tree_hash_cache( - &self, - other: &Self, - cache: &mut TreeHashCache, - chunk: usize, - ) -> Result { - let offset_handler = BTreeOverlay::new(self, chunk)?; - - // Skip past the internal nodes and update any changed leaf nodes. - { - let chunk = offset_handler.first_leaf_node()?; - let chunk = self.a.update_tree_hash_cache(&other.a, cache, chunk)?; - let chunk = self.b.update_tree_hash_cache(&other.b, cache, chunk)?; - let chunk = self.c.update_tree_hash_cache(&other.c, cache, chunk)?; - let _chunk = self.d.update_tree_hash_cache(&other.d, cache, chunk)?; - } - - for (&parent, children) in offset_handler.iter_internal_nodes().rev() { - if cache.either_modified(children)? { - cache.modify_chunk(parent, &cache.hash_children(children)?)?; - } - } - - Ok(offset_handler.next_node) - } -} - -#[derive(Clone, Debug)] -pub struct Outer { - pub a: u64, - pub b: Inner, - pub c: u64, -} - -impl TreeHash for Outer { - fn tree_hash_type() -> TreeHashType { - TreeHashType::Container - } - - fn tree_hash_packed_encoding(&self) -> Vec { - unreachable!("Struct should never be packed.") - } - - fn tree_hash_packing_factor() -> usize { - unreachable!("Struct should never be packed.") - } - - fn tree_hash_root(&self) -> Vec { - let mut leaves = Vec::with_capacity(4 * HASHSIZE); - - leaves.append(&mut self.a.tree_hash_root()); - leaves.append(&mut self.b.tree_hash_root()); - leaves.append(&mut self.c.tree_hash_root()); - - efficient_merkleize(&leaves)[0..32].to_vec() - } -} - -impl CachedTreeHash for Outer { - fn new_tree_hash_cache(&self) -> Result { - let tree = TreeHashCache::from_leaves_and_subtrees( - self, - vec![ - self.a.new_tree_hash_cache()?, - self.b.new_tree_hash_cache()?, - self.c.new_tree_hash_cache()?, - ], - )?; - - Ok(tree) - } - - fn tree_hash_cache_overlay(&self, chunk_offset: usize) -> Result { - let mut lengths = vec![]; - - lengths.push(BTreeOverlay::new(&self.a, 0)?.num_nodes()); - lengths.push(BTreeOverlay::new(&self.b, 0)?.num_nodes()); - lengths.push(BTreeOverlay::new(&self.c, 0)?.num_nodes()); - - BTreeOverlay::from_lengths(chunk_offset, lengths) - } - - fn update_tree_hash_cache( - &self, - other: &Self, - cache: &mut TreeHashCache, - chunk: usize, - ) -> Result { - let offset_handler = BTreeOverlay::new(self, chunk)?; - - // Skip past the internal nodes and update any changed leaf nodes. - { - let chunk = offset_handler.first_leaf_node()?; - let chunk = self.a.update_tree_hash_cache(&other.a, cache, chunk)?; - let chunk = self.b.update_tree_hash_cache(&other.b, cache, chunk)?; - let _chunk = self.c.update_tree_hash_cache(&other.c, cache, chunk)?; - } - - for (&parent, children) in offset_handler.iter_internal_nodes().rev() { - if cache.either_modified(children)? { - cache.modify_chunk(parent, &cache.hash_children(children)?)?; - } - } - - Ok(offset_handler.next_node) - } -} - -fn join(many: Vec>) -> Vec { - let mut all = vec![]; - for one in many { - all.extend_from_slice(&mut one.clone()) - } - all -} - -#[test] -fn partial_modification_to_inner_struct() { - let original_inner = Inner { - a: 1, - b: 2, - c: 3, - d: 4, - }; - - let original_outer = Outer { - a: 0, - b: original_inner.clone(), - c: 5, - }; - - let modified_inner = Inner { - a: 42, - ..original_inner.clone() - }; - - // Modify outer - let modified_outer = Outer { - b: modified_inner.clone(), - ..original_outer.clone() - }; - - // Perform a differential hash - let mut cache_struct = TreeHashCache::new(&original_outer).unwrap(); - - modified_outer - .update_tree_hash_cache(&original_outer, &mut cache_struct, 0) - .unwrap(); - - let modified_cache: Vec = cache_struct.into(); - - // Generate reference data. - let mut data = vec![]; - data.append(&mut int_to_bytes32(0)); - let inner_bytes: Vec = TreeHashCache::new(&modified_inner).unwrap().into(); - data.append(&mut int_to_bytes32(5)); - - let leaves = vec![ - int_to_bytes32(0), - inner_bytes[0..32].to_vec(), - int_to_bytes32(5), - vec![0; 32], // padding - ]; - let mut merkle = merkleize(join(leaves)); - merkle.splice(4 * 32..5 * 32, inner_bytes); - - assert_eq!(merkle.len() / HASHSIZE, 13); - assert_eq!(modified_cache.len() / HASHSIZE, 13); - - assert_eq!(merkle, modified_cache); -} - -#[test] -fn partial_modification_to_outer() { - let inner = Inner { - a: 1, - b: 2, - c: 3, - d: 4, - }; - - let original_outer = Outer { - a: 0, - b: inner.clone(), - c: 5, - }; - - // Build the initial cache. - // let original_cache = original_outer.build_cache_bytes(); - - // Modify outer - let modified_outer = Outer { - c: 42, - ..original_outer.clone() - }; - - // Perform a differential hash - let mut cache_struct = TreeHashCache::new(&original_outer).unwrap(); - - modified_outer - .update_tree_hash_cache(&original_outer, &mut cache_struct, 0) - .unwrap(); - - let modified_cache: Vec = cache_struct.into(); - - // Generate reference data. - let mut data = vec![]; - data.append(&mut int_to_bytes32(0)); - let inner_bytes: Vec = TreeHashCache::new(&inner).unwrap().into(); - data.append(&mut int_to_bytes32(5)); - - let leaves = vec![ - int_to_bytes32(0), - inner_bytes[0..32].to_vec(), - int_to_bytes32(42), - vec![0; 32], // padding - ]; - let mut merkle = merkleize(join(leaves)); - merkle.splice(4 * 32..5 * 32, inner_bytes); - - assert_eq!(merkle.len() / HASHSIZE, 13); - assert_eq!(modified_cache.len() / HASHSIZE, 13); - - assert_eq!(merkle, modified_cache); -} - -#[test] -fn outer_builds() { - let inner = Inner { - a: 1, - b: 2, - c: 3, - d: 4, - }; - - let outer = Outer { - a: 0, - b: inner.clone(), - c: 5, - }; - - // Build the function output. - let cache: Vec = TreeHashCache::new(&outer).unwrap().into(); - - // Generate reference data. - let mut data = vec![]; - data.append(&mut int_to_bytes32(0)); - let inner_bytes: Vec = TreeHashCache::new(&inner).unwrap().into(); - data.append(&mut int_to_bytes32(5)); - - let leaves = vec![ - int_to_bytes32(0), - inner_bytes[0..32].to_vec(), - int_to_bytes32(5), - vec![0; 32], // padding - ]; - let mut merkle = merkleize(join(leaves)); - merkle.splice(4 * 32..5 * 32, inner_bytes); - - assert_eq!(merkle.len() / HASHSIZE, 13); - assert_eq!(cache.len() / HASHSIZE, 13); - - assert_eq!(merkle, cache); -} - -fn mix_in_length(root: &mut [u8], len: usize) { - let mut bytes = root.to_vec(); - bytes.append(&mut int_to_bytes32(len as u64)); - - root.copy_from_slice(&hash(&bytes)); -} - -/// Generic test that covers: -/// -/// 1. Produce a new cache from `original`. -/// 2. Do a differential hash between `original` and `modified`. -/// 3. Test that the cache generated matches the one we generate manually. -/// -/// In effect it ensures that we can do a differential hash between two `Vec`. -fn test_u64_vec_modifications(original: Vec, modified: Vec) { - // Generate initial cache. - let original_cache: Vec = TreeHashCache::new(&original).unwrap().into(); - - // Perform a differential hash - let mut cache_struct = TreeHashCache::from_bytes(original_cache.clone(), false).unwrap(); - modified - .update_tree_hash_cache(&original, &mut cache_struct, 0) - .unwrap(); - let modified_cache: Vec = cache_struct.into(); - - // Generate reference data. - let mut data = vec![]; - for i in &modified { - data.append(&mut int_to_bytes8(*i)); - } - let data = sanitise_bytes(data); - let mut expected = merkleize(data); - - mix_in_length(&mut expected[0..HASHSIZE], modified.len()); - - assert_eq!(expected, modified_cache); - assert_eq!(&expected[0..32], &modified.tree_hash_root()[..]); -} - -#[test] -fn partial_modification_u64_vec() { - let n: u64 = 2_u64.pow(5); - - let original_vec: Vec = (0..n).collect(); - - let mut modified_vec = original_vec.clone(); - modified_vec[n as usize - 1] = 42; - - test_u64_vec_modifications(original_vec, modified_vec); -} - -#[test] -fn shortened_u64_vec_len_within_pow_2_boundary() { - let n: u64 = 2_u64.pow(5) - 1; - - let original_vec: Vec = (0..n).collect(); - - let mut modified_vec = original_vec.clone(); - modified_vec.pop(); - - test_u64_vec_modifications(original_vec, modified_vec); -} - -#[test] -fn shortened_u64_vec_len_outside_pow_2_boundary() { - let original_vec: Vec = (0..2_u64.pow(6)).collect(); - - let modified_vec: Vec = (0..2_u64.pow(5)).collect(); - - test_u64_vec_modifications(original_vec, modified_vec); -} - -#[test] -fn extended_u64_vec_len_within_pow_2_boundary() { - let n: u64 = 2_u64.pow(5) - 2; - - let original_vec: Vec = (0..n).collect(); - - let mut modified_vec = original_vec.clone(); - modified_vec.push(42); - - test_u64_vec_modifications(original_vec, modified_vec); -} - -#[test] -fn extended_u64_vec_len_outside_pow_2_boundary() { - let original_vec: Vec = (0..2_u64.pow(5)).collect(); - - let modified_vec: Vec = (0..2_u64.pow(6)).collect(); - - test_u64_vec_modifications(original_vec, modified_vec); -} - -#[test] -fn large_vec_of_u64_builds() { - let n: u64 = 50; - - let my_vec: Vec = (0..n).collect(); - - // Generate function output. - let cache: Vec = TreeHashCache::new(&my_vec).unwrap().into(); - - // Generate reference data. - let mut data = vec![]; - for i in &my_vec { - data.append(&mut int_to_bytes8(*i)); - } - let data = sanitise_bytes(data); - let expected = merkleize(data); - - assert_eq!(expected, cache); -} - -/// Generic test that covers: -/// -/// 1. Produce a new cache from `original`. -/// 2. Do a differential hash between `original` and `modified`. -/// 3. Test that the cache generated matches the one we generate manually. -/// -/// The `reference` vec is used to build the tree hash cache manually. `Inner` is just 4x `u64`, so -/// you can represent 2x `Inner` with a `reference` vec of len 8. -/// -/// In effect it ensures that we can do a differential hash between two `Vec`. -fn test_inner_vec_modifications(original: Vec, modified: Vec, reference: Vec) { - let mut cache = TreeHashCache::new(&original).unwrap(); - - modified - .update_tree_hash_cache(&original, &mut cache, 0) - .unwrap(); - let modified_cache: Vec = cache.into(); - - // Build the reference vec. - - let mut leaves = vec![]; - let mut full_bytes = vec![]; - - for n in reference.chunks(4) { - let mut merkle = merkleize(join(vec![ - int_to_bytes32(n[0]), - int_to_bytes32(n[1]), - int_to_bytes32(n[2]), - int_to_bytes32(n[3]), - ])); - leaves.append(&mut merkle[0..HASHSIZE].to_vec()); - full_bytes.append(&mut merkle); - } - - let num_leaves = leaves.len() / HASHSIZE; - let mut expected = merkleize(leaves); - - let num_internal_nodes = num_leaves.next_power_of_two() - 1; - expected.splice(num_internal_nodes * HASHSIZE.., full_bytes); - - for _ in num_leaves..num_leaves.next_power_of_two() { - expected.append(&mut vec![0; HASHSIZE]); - } - - mix_in_length(&mut expected[0..HASHSIZE], modified.len()); - - // Compare the cached tree to the reference tree. - assert_trees_eq(&expected, &modified_cache); - assert_eq!(&expected[0..32], &modified.tree_hash_root()[..]); -} - -#[test] -fn partial_modification_of_vec_of_inner() { - let original = vec![ - Inner { - a: 0, - b: 1, - c: 2, - d: 3, - }, - Inner { - a: 4, - b: 5, - c: 6, - d: 7, - }, - Inner { - a: 8, - b: 9, - c: 10, - d: 11, - }, - ]; - - let mut modified = original.clone(); - modified[1].a = 42; - - let mut reference_vec: Vec = (0..12).collect(); - reference_vec[4] = 42; - - test_inner_vec_modifications(original, modified, reference_vec); -} - -#[test] -fn shortened_vec_of_inner_within_power_of_two_boundary() { - let original = vec![ - Inner { - a: 0, - b: 1, - c: 2, - d: 3, - }, - Inner { - a: 4, - b: 5, - c: 6, - d: 7, - }, - Inner { - a: 8, - b: 9, - c: 10, - d: 11, - }, - Inner { - a: 12, - b: 13, - c: 14, - d: 15, - }, - ]; - - let mut modified = original.clone(); - modified.pop(); // remove the last element from the list. - - let reference_vec: Vec = (0..12).collect(); - - test_inner_vec_modifications(original, modified, reference_vec); -} - -#[test] -fn shortened_vec_of_inner_outside_power_of_two_boundary() { - let original = vec![ - Inner { - a: 0, - b: 1, - c: 2, - d: 3, - }, - Inner { - a: 4, - b: 5, - c: 6, - d: 7, - }, - Inner { - a: 8, - b: 9, - c: 10, - d: 11, - }, - Inner { - a: 12, - b: 13, - c: 14, - d: 15, - }, - Inner { - a: 16, - b: 17, - c: 18, - d: 19, - }, - ]; - - let mut modified = original.clone(); - modified.pop(); // remove the last element from the list. - - let reference_vec: Vec = (0..16).collect(); - - test_inner_vec_modifications(original, modified, reference_vec); -} - -#[test] -fn lengthened_vec_of_inner_within_power_of_two_boundary() { - let original = vec![ - Inner { - a: 0, - b: 1, - c: 2, - d: 3, - }, - Inner { - a: 4, - b: 5, - c: 6, - d: 7, - }, - Inner { - a: 8, - b: 9, - c: 10, - d: 11, - }, - ]; - - let mut modified = original.clone(); - modified.push(Inner { - a: 12, - b: 13, - c: 14, - d: 15, - }); - - let reference_vec: Vec = (0..16).collect(); - - test_inner_vec_modifications(original, modified, reference_vec); -} - -#[test] -fn lengthened_vec_of_inner_outside_power_of_two_boundary() { - let original = vec![ - Inner { - a: 0, - b: 1, - c: 2, - d: 3, - }, - Inner { - a: 4, - b: 5, - c: 6, - d: 7, - }, - Inner { - a: 8, - b: 9, - c: 10, - d: 11, - }, - Inner { - a: 12, - b: 13, - c: 14, - d: 15, - }, - ]; - - let mut modified = original.clone(); - modified.push(Inner { - a: 16, - b: 17, - c: 18, - d: 19, - }); - - let reference_vec: Vec = (0..20).collect(); - - test_inner_vec_modifications(original, modified, reference_vec); -} - -#[test] -fn vec_of_inner_builds() { - let numbers: Vec = (0..12).collect(); - - let mut leaves = vec![]; - let mut full_bytes = vec![]; - - for n in numbers.chunks(4) { - let mut merkle = merkleize(join(vec![ - int_to_bytes32(n[0]), - int_to_bytes32(n[1]), - int_to_bytes32(n[2]), - int_to_bytes32(n[3]), - ])); - leaves.append(&mut merkle[0..HASHSIZE].to_vec()); - full_bytes.append(&mut merkle); - } - - let mut expected = merkleize(leaves); - expected.splice(3 * HASHSIZE.., full_bytes); - expected.append(&mut vec![0; HASHSIZE]); - - let my_vec = vec![ - Inner { - a: 0, - b: 1, - c: 2, - d: 3, - }, - Inner { - a: 4, - b: 5, - c: 6, - d: 7, - }, - Inner { - a: 8, - b: 9, - c: 10, - d: 11, - }, - ]; - - let cache: Vec = TreeHashCache::new(&my_vec).unwrap().into(); - - assert_trees_eq(&expected, &cache); -} - -/// Provides detailed assertions when comparing merkle trees. -fn assert_trees_eq(a: &[u8], b: &[u8]) { - assert_eq!(a.len(), b.len(), "Byte lens different"); - for i in (0..a.len() / HASHSIZE).rev() { - let range = i * HASHSIZE..(i + 1) * HASHSIZE; - assert_eq!( - a[range.clone()], - b[range], - "Chunk {}/{} different \n\n a: {:?} \n\n b: {:?}", - i, - a.len() / HASHSIZE, - a, - b, - ); - } -} - -#[test] -fn vec_of_u64_builds() { - let data = join(vec![ - int_to_bytes8(1), - int_to_bytes8(2), - int_to_bytes8(3), - int_to_bytes8(4), - int_to_bytes8(5), - vec![0; 32 - 8], // padding - ]); - - let expected = merkleize(data); - - let my_vec = vec![1, 2, 3, 4, 5]; - - // - // Note: the length is not mixed-in in this example. The user must ensure the length is - // mixed-in. - // - - let cache: Vec = TreeHashCache::new(&my_vec).unwrap().into(); - - assert_eq!(expected, cache); -} - -#[test] -fn vec_does_mix_in_len() { - let data = join(vec![ - int_to_bytes8(1), - int_to_bytes8(2), - int_to_bytes8(3), - int_to_bytes8(4), - int_to_bytes8(5), - vec![0; 32 - 8], // padding - ]); - - let tree = merkleize(data); - - let my_vec: Vec = vec![1, 2, 3, 4, 5]; - - let mut expected = vec![0; 32]; - expected.copy_from_slice(&tree[0..HASHSIZE]); - expected.append(&mut int_to_bytes32(my_vec.len() as u64)); - let expected = hash(&expected); - - assert_eq!(&expected[0..HASHSIZE], &my_vec.tree_hash_root()[..]); -} - -#[test] -fn merkleize_odd() { - let data = join(vec![ - int_to_bytes32(1), - int_to_bytes32(2), - int_to_bytes32(3), - int_to_bytes32(4), - int_to_bytes32(5), - ]); - - let merkle = merkleize(sanitise_bytes(data)); - - let expected_len = num_nodes(8) * BYTES_PER_CHUNK; - - assert_eq!(merkle.len(), expected_len); -} - -fn generic_test(index: usize) { - let inner = Inner { - a: 1, - b: 2, - c: 3, - d: 4, - }; - - let cache: Vec = TreeHashCache::new(&inner).unwrap().into(); - - let changed_inner = match index { - 0 => Inner { - a: 42, - ..inner.clone() - }, - 1 => Inner { - b: 42, - ..inner.clone() - }, - 2 => Inner { - c: 42, - ..inner.clone() - }, - 3 => Inner { - d: 42, - ..inner.clone() - }, - _ => panic!("bad index"), - }; - - let mut cache_struct = TreeHashCache::from_bytes(cache.clone(), false).unwrap(); - - changed_inner - .update_tree_hash_cache(&inner, &mut cache_struct, 0) - .unwrap(); - - // assert_eq!(*cache_struct.hash_count, 3); - - let new_tree_hash_cache: Vec = cache_struct.into(); - - let data1 = int_to_bytes32(1); - let data2 = int_to_bytes32(2); - let data3 = int_to_bytes32(3); - let data4 = int_to_bytes32(4); - - let mut data = vec![data1, data2, data3, data4]; - - data[index] = int_to_bytes32(42); - - let expected = merkleize(join(data)); - - assert_eq!(expected, new_tree_hash_cache); -} - -#[test] -fn cached_hash_on_inner() { - generic_test(0); - generic_test(1); - generic_test(2); - generic_test(3); -} - -#[test] -fn inner_builds() { - let data1 = int_to_bytes32(1); - let data2 = int_to_bytes32(2); - let data3 = int_to_bytes32(3); - let data4 = int_to_bytes32(4); - - let data = join(vec![data1, data2, data3, data4]); - let expected = merkleize(data); - - let inner = Inner { - a: 1, - b: 2, - c: 3, - d: 4, - }; - - let cache: Vec = TreeHashCache::new(&inner).unwrap().into(); - - assert_eq!(expected, cache); -} - -#[test] -fn merkleize_4_leaves() { - let data1 = hash(&int_to_bytes32(1)); - let data2 = hash(&int_to_bytes32(2)); - let data3 = hash(&int_to_bytes32(3)); - let data4 = hash(&int_to_bytes32(4)); - - let data = join(vec![ - data1.clone(), - data2.clone(), - data3.clone(), - data4.clone(), - ]); - - let cache = merkleize(data); - - let hash_12 = { - let mut joined = vec![]; - joined.append(&mut data1.clone()); - joined.append(&mut data2.clone()); - hash(&joined) - }; - let hash_34 = { - let mut joined = vec![]; - joined.append(&mut data3.clone()); - joined.append(&mut data4.clone()); - hash(&joined) - }; - let hash_hash12_hash_34 = { - let mut joined = vec![]; - joined.append(&mut hash_12.clone()); - joined.append(&mut hash_34.clone()); - hash(&joined) - }; - - for (i, chunk) in cache.chunks(HASHSIZE).enumerate().rev() { - let expected = match i { - 0 => hash_hash12_hash_34.clone(), - 1 => hash_12.clone(), - 2 => hash_34.clone(), - 3 => data1.clone(), - 4 => data2.clone(), - 5 => data3.clone(), - 6 => data4.clone(), - _ => vec![], - }; - - assert_eq!(chunk, &expected[..], "failed at {}", i); - } -} -*/ diff --git a/eth2/utils/tree_hash_derive/Cargo.toml b/eth2/utils/tree_hash_derive/Cargo.toml index f227d7954..8544108a7 100644 --- a/eth2/utils/tree_hash_derive/Cargo.toml +++ b/eth2/utils/tree_hash_derive/Cargo.toml @@ -10,6 +10,7 @@ proc-macro = true [dev-dependencies] tree_hash = { path = "../tree_hash" } +cached_tree_hash = { path = "../cached_tree_hash" } [dependencies] syn = "0.15" diff --git a/eth2/utils/tree_hash_derive/src/lib.rs b/eth2/utils/tree_hash_derive/src/lib.rs index 9b35512a9..29db95509 100644 --- a/eth2/utils/tree_hash_derive/src/lib.rs +++ b/eth2/utils/tree_hash_derive/src/lib.rs @@ -58,9 +58,9 @@ pub fn subtree_derive(input: TokenStream) -> TokenStream { let num_items = idents_a.len(); let output = quote! { - impl tree_hash::CachedTreeHash<#name> for #name { - fn new_tree_hash_cache(&self, depth: usize) -> Result { - let tree = tree_hash::TreeHashCache::from_leaves_and_subtrees( + impl cached_tree_hash::CachedTreeHash<#name> for #name { + fn new_tree_hash_cache(&self, depth: usize) -> Result { + let tree = cached_tree_hash::TreeHashCache::from_leaves_and_subtrees( self, vec![ #( @@ -74,23 +74,23 @@ pub fn subtree_derive(input: TokenStream) -> TokenStream { } fn num_tree_hash_cache_chunks(&self) -> usize { - tree_hash::BTreeOverlay::new(self, 0, 0) + cached_tree_hash::BTreeOverlay::new(self, 0, 0) .and_then(|o| Ok(o.num_chunks())) .unwrap_or_else(|_| 1) } - fn tree_hash_cache_overlay(&self, chunk_offset: usize, depth: usize) -> Result { + fn tree_hash_cache_overlay(&self, chunk_offset: usize, depth: usize) -> Result { let mut lengths = vec![]; #( lengths.push(self.#idents_b.num_tree_hash_cache_chunks()); )* - tree_hash::BTreeOverlay::from_lengths(chunk_offset, #num_items, depth, lengths) + cached_tree_hash::BTreeOverlay::from_lengths(chunk_offset, #num_items, depth, lengths) } - fn update_tree_hash_cache(&self, cache: &mut TreeHashCache) -> Result<(), Error> { - let overlay = BTreeOverlay::new(self, cache.chunk_index, 0)?; + fn update_tree_hash_cache(&self, cache: &mut cached_tree_hash::TreeHashCache) -> Result<(), cached_tree_hash::Error> { + let overlay = cached_tree_hash::BTreeOverlay::new(self, cache.chunk_index, 0)?; // Skip the chunk index to the first leaf node of this struct. cache.chunk_index = overlay.first_leaf_node(); diff --git a/eth2/utils/tree_hash_derive/tests/tests.rs b/eth2/utils/tree_hash_derive/tests/tests.rs index 10d0aa853..5b1bb481f 100644 --- a/eth2/utils/tree_hash_derive/tests/tests.rs +++ b/eth2/utils/tree_hash_derive/tests/tests.rs @@ -1,4 +1,5 @@ -use tree_hash::{CachedTreeHash, SignedRoot, TreeHash}; +use cached_tree_hash::{CachedTreeHash, CachedTreeHasher}; +use tree_hash::{SignedRoot, TreeHash}; use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; #[derive(Clone, Debug, TreeHash, CachedTreeHash)] @@ -13,18 +14,17 @@ fn test_standard_and_cached(original: &T, modified: &T) where T: CachedTreeHash, { - let mut cache = original.new_tree_hash_cache().unwrap(); + // let mut cache = original.new_tree_hash_cache().unwrap(); + let mut hasher = CachedTreeHasher::new(original).unwrap(); let standard_root = original.tree_hash_root(); - let cached_root = cache.root().unwrap().to_vec(); + let cached_root = hasher.tree_hash_root().unwrap(); assert_eq!(standard_root, cached_root); // Test after a modification - modified - .update_tree_hash_cache(&original, &mut cache, 0) - .unwrap(); + hasher.update(modified).unwrap(); let standard_root = modified.tree_hash_root(); - let cached_root = cache.root().unwrap().to_vec(); + let cached_root = hasher.tree_hash_root().unwrap(); assert_eq!(standard_root, cached_root); }