diff --git a/Cargo.toml b/Cargo.toml index def0d03..55d7d59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ borsh = { version = "0.10.0", optional = true } borsh = { version = "0.10.0" } serde_json = "1.0.96" postcard = { version = "1.0.4" } +tendermint = {version = "0.35.0"} [features] default = ["std"] diff --git a/src/lib.rs b/src/lib.rs index 052ace6..fbca929 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,11 +18,15 @@ extern crate alloc; mod maybestd { #[cfg(not(feature = "std"))] - pub use alloc::{boxed, collections, format, string, vec}; + pub use alloc::{boxed, vec}; + #[cfg(all(not(feature = "std"), feature = "serde"))] + pub use alloc::{format, string}; #[cfg(not(feature = "std"))] pub use core::{cmp, fmt, hash, marker, mem, ops}; #[cfg(feature = "std")] - pub use std::{boxed, cmp, collections, fmt, format, hash, marker, mem, ops, string, vec}; + pub use std::{boxed, cmp, fmt, hash, marker, mem, ops, vec}; + #[cfg(all(feature = "std", feature = "serde"))] + pub use std::{format, string}; pub mod hash_or_btree_map { #[cfg(not(feature = "std"))] @@ -46,6 +50,9 @@ use simple_merkle::{ mod namespaced_hash; pub use namespaced_hash::*; +mod tendermint_hash; +pub use tendermint_hash::*; + // pub mod db; pub mod nmt_proof; pub mod simple_merkle; @@ -414,7 +421,7 @@ pub enum RangeProofType { #[cfg(test)] mod tests { - use crate::maybestd::{format, vec::Vec}; + use crate::maybestd::vec::Vec; use crate::NamespaceMerkleHasher; use crate::{ namespaced_hash::{NamespaceId, NamespacedSha2Hasher}, diff --git a/src/tendermint_hash.rs b/src/tendermint_hash.rs new file mode 100644 index 0000000..9657080 --- /dev/null +++ b/src/tendermint_hash.rs @@ -0,0 +1,72 @@ +use sha2::{Digest, Sha256}; + +use crate::simple_merkle::tree::MerkleHash; + +const LEAF_PREFIX: &[u8] = &[0]; +const INNER_PREFIX: &[u8] = &[1]; + +fn leaf_hash(bytes: &[u8]) -> [u8; 32] { + //hash([LEAF_PREFIX, bytes].concat().as_slice()) + let mut hasher = Sha256::new(); + hasher.update(LEAF_PREFIX); + hasher.update(bytes); + hasher.finalize().into() +} + +fn inner_hash(left: &[u8], right: &[u8]) -> [u8; 32] { + let mut hasher = Sha256::new(); + hasher.update(INNER_PREFIX); + hasher.update(left); + hasher.update(right); + hasher.finalize().into() +} + +/// A sha256 hasher, compatible with [Tendermint merkle hash](https://github.com/informalsystems/tendermint-rs/blob/979456c9f33463944f97f7ea3900640e59f7ea6d/tendermint/src/merkle.rs) +pub struct TmSha2Hasher; + +impl Default for TmSha2Hasher { + fn default() -> Self { + Self::new() + } +} + +impl TmSha2Hasher { + /// Create a new instance of the hasher + pub fn new() -> Self { + TmSha2Hasher + } +} + +impl MerkleHash for TmSha2Hasher { + type Output = [u8; 32]; + + const EMPTY_ROOT: Self::Output = [ + 227, 176, 196, 66, 152, 252, 28, 20, 154, 251, 244, 200, 153, 111, 185, 36, 39, 174, 65, + 228, 100, 155, 147, 76, 164, 149, 153, 27, 120, 82, 184, 85, + ]; + + fn hash_leaf(&self, data: &[u8]) -> Self::Output { + leaf_hash(data) + } + fn hash_nodes(&self, left: &Self::Output, right: &Self::Output) -> Self::Output { + inner_hash(left, right) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{MemDb, MerkleTree}; + use tendermint::merkle::simple_hash_from_byte_vectors; + #[test] + fn test_tm_hash_matches_upstream() { + let leaves: Vec<&[u8]> = vec![b"leaf_1", b"leaf_2", b"leaf_3", b"leaf_4"]; + let hasher = TmSha2Hasher {}; + let mut tree: MerkleTree, TmSha2Hasher> = MerkleTree::with_hasher(hasher); + leaves.iter().for_each(|leaf| { + tree.push_raw_leaf(leaf); + }); + let hash_from_byte_slices = simple_hash_from_byte_vectors::(leaves.as_slice()); + assert_eq!(tree.root().as_ref(), &hash_from_byte_slices); + } +}