Unify tree hash methods

This commit is contained in:
Paul Hauner 2019-04-16 09:34:23 +10:00
parent 93f3fc858d
commit d311b48a9f
No known key found for this signature in database
GPG Key ID: D362883A9218FCC6
6 changed files with 101 additions and 102 deletions

View File

@ -17,7 +17,7 @@ pub enum Error {
UnableToObtainSlices, UnableToObtainSlices,
UnableToGrowMerkleTree, UnableToGrowMerkleTree,
UnableToShrinkMerkleTree, UnableToShrinkMerkleTree,
ShouldNeverBePacked(ItemType), ShouldNeverBePacked(TreeHashType),
BytesAreNotEvenChunks(usize), BytesAreNotEvenChunks(usize),
NoModifiedFieldForChunk(usize), NoModifiedFieldForChunk(usize),
NoBytesForChunk(usize), NoBytesForChunk(usize),
@ -31,15 +31,9 @@ pub trait CachedTreeHash<T>: CachedTreeHashSubTree<T> + Sized {
fn clone_without_tree_hash_cache(&self) -> Self; fn clone_without_tree_hash_cache(&self) -> Self;
} }
pub trait CachedTreeHashSubTree<Item> { pub trait CachedTreeHashSubTree<Item>: TreeHash {
fn item_type() -> ItemType;
fn btree_overlay(&self, chunk_offset: usize) -> Result<BTreeOverlay, Error>; fn btree_overlay(&self, chunk_offset: usize) -> Result<BTreeOverlay, Error>;
fn packed_encoding(&self) -> Result<Vec<u8>, Error>;
fn packing_factor() -> usize;
fn new_cache(&self) -> Result<TreeHashCache, Error>; fn new_cache(&self) -> Result<TreeHashCache, Error>;
fn update_cache( fn update_cache(

View File

@ -5,10 +5,6 @@ use ssz::ssz_encode;
mod vec; mod vec;
impl CachedTreeHashSubTree<u64> for u64 { impl CachedTreeHashSubTree<u64> for u64 {
fn item_type() -> ItemType {
ItemType::Basic
}
fn new_cache(&self) -> Result<TreeHashCache, Error> { fn new_cache(&self) -> Result<TreeHashCache, Error> {
Ok(TreeHashCache::from_bytes( Ok(TreeHashCache::from_bytes(
merkleize(ssz_encode(self)), merkleize(ssz_encode(self)),
@ -20,14 +16,6 @@ impl CachedTreeHashSubTree<u64> for u64 {
BTreeOverlay::from_lengths(chunk_offset, vec![1]) BTreeOverlay::from_lengths(chunk_offset, vec![1])
} }
fn packed_encoding(&self) -> Result<Vec<u8>, Error> {
Ok(ssz_encode(self))
}
fn packing_factor() -> usize {
HASHSIZE / 8
}
fn update_cache( fn update_cache(
&self, &self,
other: &Self, other: &Self,

View File

@ -2,18 +2,14 @@ use super::*;
impl<T> CachedTreeHashSubTree<Vec<T>> for Vec<T> impl<T> CachedTreeHashSubTree<Vec<T>> for Vec<T>
where where
T: CachedTreeHashSubTree<T>, T: CachedTreeHashSubTree<T> + TreeHash,
{ {
fn item_type() -> ItemType {
ItemType::List
}
fn new_cache(&self) -> Result<TreeHashCache, Error> { fn new_cache(&self) -> Result<TreeHashCache, Error> {
match T::item_type() { match T::tree_hash_type() {
ItemType::Basic => { TreeHashType::Basic => {
TreeHashCache::from_bytes(merkleize(get_packed_leaves(self)?), false) TreeHashCache::from_bytes(merkleize(get_packed_leaves(self)?), false)
} }
ItemType::Composite | ItemType::List => { TreeHashType::Composite | TreeHashType::List => {
let subtrees = self let subtrees = self
.iter() .iter()
.map(|item| TreeHashCache::new(item)) .map(|item| TreeHashCache::new(item))
@ -25,9 +21,9 @@ where
} }
fn btree_overlay(&self, chunk_offset: usize) -> Result<BTreeOverlay, Error> { fn btree_overlay(&self, chunk_offset: usize) -> Result<BTreeOverlay, Error> {
let lengths = match T::item_type() { let lengths = match T::tree_hash_type() {
ItemType::Basic => vec![1; self.len() / T::packing_factor()], TreeHashType::Basic => vec![1; self.len() / T::tree_hash_packing_factor()],
ItemType::Composite | ItemType::List => { TreeHashType::Composite | TreeHashType::List => {
let mut lengths = vec![]; let mut lengths = vec![];
for item in self { for item in self {
@ -41,14 +37,6 @@ where
BTreeOverlay::from_lengths(chunk_offset, lengths) BTreeOverlay::from_lengths(chunk_offset, lengths)
} }
fn packed_encoding(&self) -> Result<Vec<u8>, Error> {
Err(Error::ShouldNeverBePacked(Self::item_type()))
}
fn packing_factor() -> usize {
1
}
fn update_cache( fn update_cache(
&self, &self,
other: &Vec<T>, other: &Vec<T>,
@ -93,8 +81,8 @@ where
cache.splice(old_offset_handler.chunk_range(), modified_cache); cache.splice(old_offset_handler.chunk_range(), modified_cache);
} }
match T::item_type() { match T::tree_hash_type() {
ItemType::Basic => { TreeHashType::Basic => {
let leaves = get_packed_leaves(self)?; let leaves = get_packed_leaves(self)?;
for (i, chunk) in offset_handler.iter_leaf_nodes().enumerate() { for (i, chunk) in offset_handler.iter_leaf_nodes().enumerate() {
@ -109,7 +97,7 @@ where
TreeHashCache::from_bytes(leaves, true)?, TreeHashCache::from_bytes(leaves, true)?,
); );
} }
ItemType::Composite | ItemType::List => { TreeHashType::Composite | TreeHashType::List => {
let mut i = offset_handler.num_leaf_nodes; let mut i = offset_handler.num_leaf_nodes;
for &start_chunk in offset_handler.iter_leaf_nodes().rev() { for &start_chunk in offset_handler.iter_leaf_nodes().rev() {
i -= 1; i -= 1;
@ -170,13 +158,13 @@ fn get_packed_leaves<T>(vec: &Vec<T>) -> Result<Vec<u8>, Error>
where where
T: CachedTreeHashSubTree<T>, T: CachedTreeHashSubTree<T>,
{ {
let num_packed_bytes = (BYTES_PER_CHUNK / T::packing_factor()) * vec.len(); let num_packed_bytes = (BYTES_PER_CHUNK / T::tree_hash_packing_factor()) * vec.len();
let num_leaves = num_sanitized_leaves(num_packed_bytes); let num_leaves = num_sanitized_leaves(num_packed_bytes);
let mut packed = Vec::with_capacity(num_leaves * HASHSIZE); let mut packed = Vec::with_capacity(num_leaves * HASHSIZE);
for item in vec { for item in vec {
packed.append(&mut item.packed_encoding()?); packed.append(&mut item.tree_hash_packed_encoding());
} }
Ok(sanitise_bytes(packed)) Ok(sanitise_bytes(packed))

View File

@ -5,8 +5,11 @@ pub const BYTES_PER_CHUNK: usize = 32;
pub const HASHSIZE: usize = 32; pub const HASHSIZE: usize = 32;
pub const MERKLE_HASH_CHUNCK: usize = 2 * BYTES_PER_CHUNK; pub const MERKLE_HASH_CHUNCK: usize = 2 * BYTES_PER_CHUNK;
pub use cached_tree_hash::CachedTreeHashSubTree;
pub use standard_tree_hash::TreeHash;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum ItemType { pub enum TreeHashType {
Basic, Basic,
List, List,
Composite, Composite,

View File

@ -4,23 +4,29 @@ use int_to_bytes::int_to_bytes32;
use ssz::ssz_encode; use ssz::ssz_encode;
pub trait TreeHash { pub trait TreeHash {
fn tree_hash_item_type() -> ItemType; fn tree_hash_type() -> TreeHashType;
fn tree_hash_packed_encoding(&self) -> Vec<u8>; fn tree_hash_packed_encoding(&self) -> Vec<u8>;
fn hash_tree_root(&self) -> Vec<u8>; fn tree_hash_packing_factor() -> usize;
fn tree_hash_root(&self) -> Vec<u8>;
} }
impl TreeHash for u64 { impl TreeHash for u64 {
fn tree_hash_item_type() -> ItemType { fn tree_hash_type() -> TreeHashType {
ItemType::Basic TreeHashType::Basic
} }
fn tree_hash_packed_encoding(&self) -> Vec<u8> { fn tree_hash_packed_encoding(&self) -> Vec<u8> {
ssz_encode(self) ssz_encode(self)
} }
fn hash_tree_root(&self) -> Vec<u8> { fn tree_hash_packing_factor() -> usize {
HASHSIZE / 8
}
fn tree_hash_root(&self) -> Vec<u8> {
int_to_bytes32(*self) int_to_bytes32(*self)
} }
} }
@ -29,18 +35,23 @@ impl<T> TreeHash for Vec<T>
where where
T: TreeHash, T: TreeHash,
{ {
fn tree_hash_item_type() -> ItemType { fn tree_hash_type() -> TreeHashType {
ItemType::List TreeHashType::List
} }
fn tree_hash_packed_encoding(&self) -> Vec<u8> { fn tree_hash_packed_encoding(&self) -> Vec<u8> {
unreachable!("List should never be packed.") unreachable!("List should never be packed.")
} }
fn hash_tree_root(&self) -> Vec<u8> { fn tree_hash_packing_factor() -> usize {
let leaves = match T::tree_hash_item_type() { unreachable!("List should never be packed.")
ItemType::Basic => { }
let mut leaves = vec![];
fn tree_hash_root(&self) -> Vec<u8> {
let leaves = match T::tree_hash_type() {
TreeHashType::Basic => {
let mut leaves =
Vec::with_capacity((HASHSIZE / T::tree_hash_packing_factor()) * self.len());
for item in self { for item in self {
leaves.append(&mut item.tree_hash_packed_encoding()); leaves.append(&mut item.tree_hash_packed_encoding());
@ -48,11 +59,11 @@ where
leaves leaves
} }
ItemType::Composite | ItemType::List => { TreeHashType::Composite | TreeHashType::List => {
let mut leaves = Vec::with_capacity(self.len() * HASHSIZE); let mut leaves = Vec::with_capacity(self.len() * HASHSIZE);
for item in self { for item in self {
leaves.append(&mut item.hash_tree_root()) leaves.append(&mut item.tree_hash_root())
} }
leaves leaves

View File

@ -11,6 +11,29 @@ pub struct InternalCache {
pub cache: Option<TreeHashCache>, pub cache: Option<TreeHashCache>,
} }
impl TreeHash for InternalCache {
fn tree_hash_type() -> TreeHashType {
TreeHashType::Composite
}
fn tree_hash_packed_encoding(&self) -> Vec<u8> {
unreachable!("Struct should never be packed.")
}
fn tree_hash_packing_factor() -> usize {
unreachable!("Struct should never be packed.")
}
fn tree_hash_root(&self) -> Vec<u8> {
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<InternalCache> for InternalCache { impl CachedTreeHash<InternalCache> for InternalCache {
fn update_internal_tree_hash_cache(mut self, mut old: Self) -> Result<(Self, Self), Error> { fn update_internal_tree_hash_cache(mut self, mut old: Self) -> Result<(Self, Self), Error> {
let mut local_cache = old.cache; let mut local_cache = old.cache;
@ -66,10 +89,6 @@ fn works_when_embedded() {
} }
impl CachedTreeHashSubTree<InternalCache> for InternalCache { impl CachedTreeHashSubTree<InternalCache> for InternalCache {
fn item_type() -> ItemType {
ItemType::Composite
}
fn new_cache(&self) -> Result<TreeHashCache, Error> { fn new_cache(&self) -> Result<TreeHashCache, Error> {
let tree = TreeHashCache::from_leaves_and_subtrees( let tree = TreeHashCache::from_leaves_and_subtrees(
self, self,
@ -88,14 +107,6 @@ impl CachedTreeHashSubTree<InternalCache> for InternalCache {
BTreeOverlay::from_lengths(chunk_offset, lengths) BTreeOverlay::from_lengths(chunk_offset, lengths)
} }
fn packed_encoding(&self) -> Result<Vec<u8>, Error> {
Err(Error::ShouldNeverBePacked(Self::item_type()))
}
fn packing_factor() -> usize {
1
}
fn update_cache( fn update_cache(
&self, &self,
other: &Self, other: &Self,
@ -134,31 +145,31 @@ pub struct Inner {
} }
impl TreeHash for Inner { impl TreeHash for Inner {
fn tree_hash_item_type() -> ItemType { fn tree_hash_type() -> TreeHashType {
ItemType::Composite TreeHashType::Composite
} }
fn tree_hash_packed_encoding(&self) -> Vec<u8> { fn tree_hash_packed_encoding(&self) -> Vec<u8> {
unreachable!("Struct should never be packed.") unreachable!("Struct should never be packed.")
} }
fn hash_tree_root(&self) -> Vec<u8> { fn tree_hash_packing_factor() -> usize {
unreachable!("Struct should never be packed.")
}
fn tree_hash_root(&self) -> Vec<u8> {
let mut leaves = Vec::with_capacity(4 * HASHSIZE); let mut leaves = Vec::with_capacity(4 * HASHSIZE);
leaves.append(&mut self.a.hash_tree_root()); leaves.append(&mut self.a.tree_hash_root());
leaves.append(&mut self.b.hash_tree_root()); leaves.append(&mut self.b.tree_hash_root());
leaves.append(&mut self.c.hash_tree_root()); leaves.append(&mut self.c.tree_hash_root());
leaves.append(&mut self.d.hash_tree_root()); leaves.append(&mut self.d.tree_hash_root());
efficient_merkleize(&leaves)[0..32].to_vec() efficient_merkleize(&leaves)[0..32].to_vec()
} }
} }
impl CachedTreeHashSubTree<Inner> for Inner { impl CachedTreeHashSubTree<Inner> for Inner {
fn item_type() -> ItemType {
ItemType::Composite
}
fn new_cache(&self) -> Result<TreeHashCache, Error> { fn new_cache(&self) -> Result<TreeHashCache, Error> {
let tree = TreeHashCache::from_leaves_and_subtrees( let tree = TreeHashCache::from_leaves_and_subtrees(
self, self,
@ -184,14 +195,6 @@ impl CachedTreeHashSubTree<Inner> for Inner {
BTreeOverlay::from_lengths(chunk_offset, lengths) BTreeOverlay::from_lengths(chunk_offset, lengths)
} }
fn packed_encoding(&self) -> Result<Vec<u8>, Error> {
Err(Error::ShouldNeverBePacked(Self::item_type()))
}
fn packing_factor() -> usize {
1
}
fn update_cache( fn update_cache(
&self, &self,
other: &Self, other: &Self,
@ -226,11 +229,31 @@ pub struct Outer {
pub c: u64, pub c: u64,
} }
impl CachedTreeHashSubTree<Outer> for Outer { impl TreeHash for Outer {
fn item_type() -> ItemType { fn tree_hash_type() -> TreeHashType {
ItemType::Composite TreeHashType::Composite
} }
fn tree_hash_packed_encoding(&self) -> Vec<u8> {
unreachable!("Struct should never be packed.")
}
fn tree_hash_packing_factor() -> usize {
unreachable!("Struct should never be packed.")
}
fn tree_hash_root(&self) -> Vec<u8> {
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 CachedTreeHashSubTree<Outer> for Outer {
fn new_cache(&self) -> Result<TreeHashCache, Error> { fn new_cache(&self) -> Result<TreeHashCache, Error> {
let tree = TreeHashCache::from_leaves_and_subtrees( let tree = TreeHashCache::from_leaves_and_subtrees(
self, self,
@ -254,14 +277,6 @@ impl CachedTreeHashSubTree<Outer> for Outer {
BTreeOverlay::from_lengths(chunk_offset, lengths) BTreeOverlay::from_lengths(chunk_offset, lengths)
} }
fn packed_encoding(&self) -> Result<Vec<u8>, Error> {
Err(Error::ShouldNeverBePacked(Self::item_type()))
}
fn packing_factor() -> usize {
1
}
fn update_cache( fn update_cache(
&self, &self,
other: &Self, other: &Self,
@ -481,7 +496,7 @@ fn test_u64_vec_modifications(original: Vec<u64>, modified: Vec<u64>) {
mix_in_length(&mut expected[0..HASHSIZE], modified.len()); mix_in_length(&mut expected[0..HASHSIZE], modified.len());
assert_eq!(expected, modified_cache); assert_eq!(expected, modified_cache);
assert_eq!(&expected[0..32], &modified.hash_tree_root()[..]); assert_eq!(&expected[0..32], &modified.tree_hash_root()[..]);
} }
#[test] #[test]
@ -604,7 +619,7 @@ fn test_inner_vec_modifications(original: Vec<Inner>, modified: Vec<Inner>, refe
// Compare the cached tree to the reference tree. // Compare the cached tree to the reference tree.
assert_trees_eq(&expected, &modified_cache); assert_trees_eq(&expected, &modified_cache);
assert_eq!(&expected[0..32], &modified.hash_tree_root()[..]); assert_eq!(&expected[0..32], &modified.tree_hash_root()[..]);
} }
#[test] #[test]