diff --git a/examples/merkle-proofs/src/lib.rs b/examples/merkle-proofs/src/lib.rs index 51c40acb1..19641b79e 100644 --- a/examples/merkle-proofs/src/lib.rs +++ b/examples/merkle-proofs/src/lib.rs @@ -4,7 +4,10 @@ extern crate alloc; use alloc::vec::Vec; use alloy_primitives::B256; -use crypto::merkle::{self, KeccakBuilder, Verifier}; +use crypto::{ + merkle::{self, Verifier}, + KeccakBuilder, +}; use stylus_proc::SolidityError; use stylus_sdk::{ alloy_sol_types::sol, diff --git a/lib/crypto/Cargo.toml b/lib/crypto/Cargo.toml index fbd7afdbe..76029268a 100644 --- a/lib/crypto/Cargo.toml +++ b/lib/crypto/Cargo.toml @@ -17,8 +17,7 @@ hex-literal = "0.4.1" rand = "0.8.5" [features] -default = [] +std = [] + merkle = [] multi-proof = ["merkle"] - -std = [] diff --git a/lib/crypto/src/merkle/hash.rs b/lib/crypto/src/hash.rs similarity index 68% rename from lib/crypto/src/merkle/hash.rs rename to lib/crypto/src/hash.rs index 77ebf12b7..7820d3106 100644 --- a/lib/crypto/src/merkle/hash.rs +++ b/lib/crypto/src/hash.rs @@ -51,8 +51,8 @@ pub trait Hasher { /// # Examples /// /// ```rust -/// use crypto::merkle::KeccakBuilder; -/// use crypto::merkle::hash::{BuildHasher, Hash, Hasher}; +/// use crypto::KeccakBuilder; +/// use crypto::hash::{BuildHasher, Hash, Hasher}; /// /// let b = KeccakBuilder; /// let mut hasher_1 = b.build_hasher(); @@ -78,8 +78,8 @@ pub trait BuildHasher { /// # Examples /// /// ```rust - /// use crypto::merkle::KeccakBuilder; - /// use crypto::merkle::hash::BuildHasher; + /// use crypto::KeccakBuilder; + /// use crypto::hash::BuildHasher; /// /// let b = KeccakBuilder; /// let hasher = b.build_hasher(); @@ -100,8 +100,8 @@ pub trait BuildHasher { /// # Examples /// /// ```rust - /// use crypto::merkle::KeccakBuilder; - /// use crypto::merkle::hash::{BuildHasher, Hash}; + /// use crypto::KeccakBuilder; + /// use crypto::hash::{BuildHasher, Hash}; /// /// let b = KeccakBuilder; /// let hash_1 = b.hash_one([0u8; 32]); @@ -125,3 +125,67 @@ pub trait BuildHasher { hasher.finalize() } } + +/// Hash the pair `(a, b)` with `state`. +#[inline] +pub fn hash_pair(a: H, b: H, mut state: S) -> S::Output +where + H: Hash, + S: Hasher, +{ + a.hash(&mut state); + b.hash(&mut state); + state.finalize() +} + +/// Sort the pair `(a, b)` and hash the result with `state`. Frequently used +/// when working with merkle proofs. +#[inline] +pub fn commutative_hash_pair(mut a: H, mut b: H, state: S) -> S::Output +where + H: Hash + PartialOrd, + S: Hasher, +{ + if a > b { + core::mem::swap(&mut a, &mut b); + } + + hash_pair(a, b, state) +} + +#[cfg(all(test, feature = "std"))] +mod tests { + use super::{commutative_hash_pair, hash_pair, BuildHasher, Hash, Hasher}; + use crate::KeccakBuilder; + + impl Hash for &[u8] { + fn hash(&self, state: &mut H) { + state.update(self); + } + } + + #[test] + fn hashes_pairs() { + let builder = KeccakBuilder; + let a = [1u8].as_slice(); + let b = [2u8].as_slice(); + + let r1 = hash_pair(a, b, builder.build_hasher()); + let r2 = hash_pair(a, b, builder.build_hasher()); + assert_eq!(r1, r2); + + let r3 = hash_pair(b, a, builder.build_hasher()); + assert_ne!(r1, r3); + } + + #[test] + fn commutatively_hashes_pairs() { + let builder = KeccakBuilder; + let a = [1u8].as_slice(); + let b = [2u8].as_slice(); + + let r1 = commutative_hash_pair(a, b, builder.build_hasher()); + let r2 = commutative_hash_pair(b, a, builder.build_hasher()); + assert_eq!(r1, r2); + } +} diff --git a/lib/crypto/src/merkle/keccak.rs b/lib/crypto/src/keccak.rs similarity index 92% rename from lib/crypto/src/merkle/keccak.rs rename to lib/crypto/src/keccak.rs index fd07d1082..136d4afb7 100644 --- a/lib/crypto/src/merkle/keccak.rs +++ b/lib/crypto/src/keccak.rs @@ -1,11 +1,11 @@ //! An interface to the default hashing algorithm used in this library's [merkle -//! proofs][super]. +//! proofs][crate]. use tiny_keccak::{Hasher as TinyHasher, Keccak}; -use super::hash::{BuildHasher, Hash, Hasher}; +use crate::hash::{BuildHasher, Hash, Hasher}; /// The default [`Hasher`] builder used in this library's [merkle -/// proofs][super]. +/// proofs][crate]. /// /// It instantiates a [`Keccak256`] hasher. #[allow(clippy::module_name_repetitions)] @@ -20,7 +20,7 @@ impl BuildHasher for KeccakBuilder { } } -/// The default [`Hasher`] used in this library's [merkle proofs][super]. +/// The default [`Hasher`] used in this library's [merkle proofs][crate]. /// /// The underlying implementation is guaranteed to match that of the /// `keccak256` algorithm, commonly used in Ethereum. diff --git a/lib/crypto/src/lib.rs b/lib/crypto/src/lib.rs index b250c62f6..75fbad7b1 100644 --- a/lib/crypto/src/lib.rs +++ b/lib/crypto/src/lib.rs @@ -4,5 +4,9 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] extern crate alloc; +pub mod hash; #[cfg(any(feature = "std", feature = "merkle"))] pub mod merkle; + +pub mod keccak; +pub use keccak::KeccakBuilder; diff --git a/lib/crypto/src/merkle/mod.rs b/lib/crypto/src/merkle.rs similarity index 96% rename from lib/crypto/src/merkle/mod.rs rename to lib/crypto/src/merkle.rs index c8910d5e8..b841d265f 100644 --- a/lib/crypto/src/merkle/mod.rs +++ b/lib/crypto/src/merkle.rs @@ -13,33 +13,13 @@ use alloc::vec::Vec; use core::marker::PhantomData; -use self::hash::{BuildHasher, Hash, Hasher}; - -pub mod hash; -pub mod keccak; -pub use keccak::KeccakBuilder; +use crate::{ + hash::{commutative_hash_pair, BuildHasher, Hasher}, + KeccakBuilder, +}; type Bytes32 = [u8; 32]; -/// Sort the pair `(a, b)` and hash the result with `hasher`. -#[inline] -fn hash_sorted_pair( - mut a: Bytes32, - mut b: Bytes32, - mut state: S, -) -> S::Output -where - S: Hasher, -{ - if a >= b { - core::mem::swap(&mut a, &mut b); - } - - a.hash(&mut state); - b.hash(&mut state); - state.finalize() -} - /// Verify merkle proofs. pub struct Verifier(PhantomData) where @@ -188,7 +168,7 @@ where /// # Examples /// /// ``` - /// use crypto::merkle::{KeccakBuilder, Verifier}; + /// use crypto::{merkle::Verifier, KeccakBuilder}; /// use hex_literal::hex; /// /// let root = hex!("0000000000000000000000000000000000000000000000000000000000000000"); @@ -205,7 +185,7 @@ where builder: &B, ) -> bool { for &hash in proof { - leaf = hash_sorted_pair(leaf, hash, builder.build_hasher()); + leaf = commutative_hash_pair(leaf, hash, builder.build_hasher()); } leaf == root @@ -263,7 +243,7 @@ where /// # Examples /// /// ```rust - /// use crypto::merkle::{KeccakBuilder, Verifier}; + /// use crypto::{merkle::Verifier, KeccakBuilder}; /// use hex_literal::hex; /// /// let root = hex!("6deb52b5da8fd108f79fab00341f38d2587896634c646ee52e49f845680a70c8"); @@ -328,7 +308,7 @@ where proof_pos += 1; }; - let hash = hash_sorted_pair(a, *b, builder.build_hasher()); + let hash = commutative_hash_pair(a, *b, builder.build_hasher()); hashes.push(hash); } @@ -382,10 +362,11 @@ mod tests { use hex_literal::hex; use rand::{thread_rng, RngCore}; - use super::{hash_sorted_pair, Bytes32, KeccakBuilder, Verifier}; - use crate::merkle::hash::BuildHasher; + use super::{Bytes32, KeccakBuilder, Verifier}; + use crate::hash::{commutative_hash_pair, BuildHasher}; - /// Shorthand for converting from a hex str to a fixed 32-bytes array. + /// Shorthand for declaring variables converted from a hex literal to a + /// fixed 32-byte slice. macro_rules! bytes { ($($var:ident = $hex:literal);* $(;)?) => { $( @@ -395,8 +376,8 @@ mod tests { }; } - /// Shorthand for converting from a string containing several addresses to - /// a fixed 32-bytes collection. + /// Shorthand for converting from an array of hex literals to an array of + /// fixed 32-bytes slices. macro_rules! bytes_array { ($($s:literal),* $(,)?) => { [ @@ -435,7 +416,7 @@ mod tests { assert!(verification); let builder = KeccakBuilder.build_hasher(); - let no_such_leaf = hash_sorted_pair(leaf_a, leaf_b, builder); + let no_such_leaf = commutative_hash_pair(leaf_a, leaf_b, builder); let proof = &proof[1..]; let verification = Verifier::verify(proof, root, no_such_leaf); assert!(verification);