From 7af5c8a8f8c0eb2bcce3da1437ef82798b29bc07 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 15 Jul 2019 19:49:52 -0400 Subject: [PATCH 1/4] merkle impl --- tendermint-rs/src/lib.rs | 1 + tendermint-rs/src/merkle.rs | 85 +++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 tendermint-rs/src/merkle.rs diff --git a/tendermint-rs/src/lib.rs b/tendermint-rs/src/lib.rs index 32104ce..7e88d95 100644 --- a/tendermint-rs/src/lib.rs +++ b/tendermint-rs/src/lib.rs @@ -38,6 +38,7 @@ pub mod evidence; #[cfg(feature = "rpc")] pub mod genesis; pub mod hash; +pub mod merkle; mod moniker; pub mod net; pub mod node; diff --git a/tendermint-rs/src/merkle.rs b/tendermint-rs/src/merkle.rs new file mode 100644 index 0000000..817c6b4 --- /dev/null +++ b/tendermint-rs/src/merkle.rs @@ -0,0 +1,85 @@ +//! Merkle tree used in Tendermint networks + +use sha2::{Digest, Sha256}; + +/// Size of Merkle root hash +pub const HASH_SIZE: usize = 32; + +/// Compute a simple Merkle root from the arbitrary sized byte slices +pub fn simple_hash_from_byte_slices(byte_slices: &[&[u8]]) -> [u8; HASH_SIZE] { + let length = byte_slices.len(); + match length { + 0 => [0; HASH_SIZE], + 1 => leaf_hash(byte_slices[0]), + _ => { + let k = get_split_point(length); + let left = simple_hash_from_byte_slices(&byte_slices[..k]); + let right = simple_hash_from_byte_slices(&byte_slices[k..]); + inner_hash(&left, &right) + } + } +} + +// returns the largest power of 2 less than length +fn get_split_point(length: usize) -> usize { + match length { + 0 => panic!("tree is empty!"), + 1 => panic!("tree has only one element!"), + 2 => 1, + _ => length.next_power_of_two() / 2, + } +} + +// tmhash(0x00 || leaf) +fn leaf_hash(bytes: &[u8]) -> [u8; HASH_SIZE] { + // make a new array starting with 0 and copy in the bytes + let mut leaf_bytes = [0u8; HASH_SIZE + 1]; + leaf_bytes[1..].copy_from_slice(bytes); + + // hash it ! + let digest = Sha256::digest(&leaf_bytes); + + // copy the GenericArray out + let mut hash_bytes = [0u8; HASH_SIZE]; + hash_bytes.copy_from_slice(&digest); + hash_bytes +} + +// tmhash(0x01 || left || right) +fn inner_hash(left: &[u8], right: &[u8]) -> [u8; HASH_SIZE] { + // make a new array starting with 0x1 and copy in the bytes + let mut inner_bytes = [0u8; HASH_SIZE * 2 + 1]; + inner_bytes[0] = 0x01; + inner_bytes[1..].copy_from_slice(left); + inner_bytes[HASH_SIZE + 1..].copy_from_slice(right); + + // hash it ! + let digest = Sha256::digest(&inner_bytes); + + // copy the GenericArray out + let mut hash_bytes = [0u8; HASH_SIZE]; + hash_bytes.copy_from_slice(&digest); + hash_bytes +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_split_point() { + assert_eq!(get_split_point(2), 1); + assert_eq!(get_split_point(3), 2); + assert_eq!(get_split_point(4), 2); + assert_eq!(get_split_point(5), 4); + assert_eq!(get_split_point(10), 8); + assert_eq!(get_split_point(20), 16); + assert_eq!(get_split_point(100), 64); + assert_eq!(get_split_point(255), 128); + assert_eq!(get_split_point(256), 128); + assert_eq!(get_split_point(257), 256); + } + + #[test] + fn test_empty_leaf() {} +} From 352c1e2fc6c7e3cfe9c8c68ebc65ccaa92787dda Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 15 Jul 2019 20:21:12 -0400 Subject: [PATCH 2/4] tests and fixes --- tendermint-rs/src/merkle.rs | 44 +++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/tendermint-rs/src/merkle.rs b/tendermint-rs/src/merkle.rs index 817c6b4..9274834 100644 --- a/tendermint-rs/src/merkle.rs +++ b/tendermint-rs/src/merkle.rs @@ -33,8 +33,9 @@ fn get_split_point(length: usize) -> usize { // tmhash(0x00 || leaf) fn leaf_hash(bytes: &[u8]) -> [u8; HASH_SIZE] { // make a new array starting with 0 and copy in the bytes - let mut leaf_bytes = [0u8; HASH_SIZE + 1]; - leaf_bytes[1..].copy_from_slice(bytes); + let mut leaf_bytes = Vec::with_capacity(bytes.len() + 1); + leaf_bytes.push(0x00); + leaf_bytes.extend_from_slice(bytes); // hash it ! let digest = Sha256::digest(&leaf_bytes); @@ -48,10 +49,10 @@ fn leaf_hash(bytes: &[u8]) -> [u8; HASH_SIZE] { // tmhash(0x01 || left || right) fn inner_hash(left: &[u8], right: &[u8]) -> [u8; HASH_SIZE] { // make a new array starting with 0x1 and copy in the bytes - let mut inner_bytes = [0u8; HASH_SIZE * 2 + 1]; - inner_bytes[0] = 0x01; - inner_bytes[1..].copy_from_slice(left); - inner_bytes[HASH_SIZE + 1..].copy_from_slice(right); + let mut inner_bytes = Vec::with_capacity(left.len() + right.len() + 1); + inner_bytes.push(0x01); + inner_bytes.extend_from_slice(left); + inner_bytes.extend_from_slice(right); // hash it ! let digest = Sha256::digest(&inner_bytes); @@ -65,6 +66,7 @@ fn inner_hash(left: &[u8], right: &[u8]) -> [u8; HASH_SIZE] { #[cfg(test)] mod tests { use super::*; + use subtle_encoding::hex; // TODO: use non-subtle ? #[test] fn test_get_split_point() { @@ -81,5 +83,33 @@ mod tests { } #[test] - fn test_empty_leaf() {} + fn test_RFC6962_empty_leaf() { + let empty_leaf_root_hex = "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"; + let empty_leaf_root = &hex::decode(empty_leaf_root_hex).unwrap(); + let empty_tree: &[&[u8]] = &[&[]]; + let root = simple_hash_from_byte_slices(empty_tree); + assert_eq!(empty_leaf_root, &root); + } + + #[test] + fn test_RFC6962_leaf() { + let leaf_root_hex = "395aa064aa4c29f7010acfe3f25db9485bbd4b91897b6ad7ad547639252b4d56"; + let leaf_string = "L123456"; + + let leaf_root = &hex::decode(leaf_root_hex).unwrap(); + let leaf_tree: &[&[u8]] = &[ leaf_string.as_bytes() ]; + let root = simple_hash_from_byte_slices(leaf_tree); + assert_eq!(leaf_root, &root); + } + + #[test] + fn test_RFC6962_node() { + let node_hash_hex = "aa217fe888e47007fa15edab33c2b492a722cb106c64667fc2b044444de66bbb"; + let left_string = "N123"; + let right_string = "N456"; + + let node_hash = &hex::decode(node_hash_hex).unwrap(); + let hash = inner_hash(left_string.as_bytes(), right_string.as_bytes()); + assert_eq!(node_hash, &hash); + } } From 2e6217c072d6e6c6660f1d48407323b455ffcdb0 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 16 Jul 2019 08:21:13 -0400 Subject: [PATCH 3/4] lint --- tendermint-rs/src/merkle.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tendermint-rs/src/merkle.rs b/tendermint-rs/src/merkle.rs index 9274834..cab85a0 100644 --- a/tendermint-rs/src/merkle.rs +++ b/tendermint-rs/src/merkle.rs @@ -84,7 +84,8 @@ mod tests { #[test] fn test_RFC6962_empty_leaf() { - let empty_leaf_root_hex = "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"; + let empty_leaf_root_hex = + "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"; let empty_leaf_root = &hex::decode(empty_leaf_root_hex).unwrap(); let empty_tree: &[&[u8]] = &[&[]]; let root = simple_hash_from_byte_slices(empty_tree); @@ -97,7 +98,7 @@ mod tests { let leaf_string = "L123456"; let leaf_root = &hex::decode(leaf_root_hex).unwrap(); - let leaf_tree: &[&[u8]] = &[ leaf_string.as_bytes() ]; + let leaf_tree: &[&[u8]] = &[leaf_string.as_bytes()]; let root = simple_hash_from_byte_slices(leaf_tree); assert_eq!(leaf_root, &root); } From 2e8d419f8280cd9224f17684a556cb528bfd83f7 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 16 Jul 2019 13:05:59 -0400 Subject: [PATCH 4/4] lowercase --- tendermint-rs/src/merkle.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tendermint-rs/src/merkle.rs b/tendermint-rs/src/merkle.rs index cab85a0..ba6d9ed 100644 --- a/tendermint-rs/src/merkle.rs +++ b/tendermint-rs/src/merkle.rs @@ -83,7 +83,7 @@ mod tests { } #[test] - fn test_RFC6962_empty_leaf() { + fn test_rfc6962_empty_leaf() { let empty_leaf_root_hex = "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"; let empty_leaf_root = &hex::decode(empty_leaf_root_hex).unwrap(); @@ -93,7 +93,7 @@ mod tests { } #[test] - fn test_RFC6962_leaf() { + fn test_rfc6962_leaf() { let leaf_root_hex = "395aa064aa4c29f7010acfe3f25db9485bbd4b91897b6ad7ad547639252b4d56"; let leaf_string = "L123456"; @@ -104,7 +104,7 @@ mod tests { } #[test] - fn test_RFC6962_node() { + fn test_rfc6962_node() { let node_hash_hex = "aa217fe888e47007fa15edab33c2b492a722cb106c64667fc2b044444de66bbb"; let left_string = "N123"; let right_string = "N456";