From d359939799c3dcb113d249cb8a764fe17d4ddbf6 Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Wed, 9 Jul 2025 20:50:47 +0800 Subject: [PATCH 1/3] feat: util for calculating multihash from byte stream --- src/utils/multihash.rs | 130 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 1 deletion(-) diff --git a/src/utils/multihash.rs b/src/utils/multihash.rs index d788a5244498..3c5ecda8e8d6 100644 --- a/src/utils/multihash.rs +++ b/src/utils/multihash.rs @@ -12,7 +12,7 @@ pub mod prelude { pub use multihash_codetable::MultihashDigest as _; } -use multihash_derive::MultihashDigest; +use multihash_derive::{Hasher, MultihashDigest}; /// Extends [`multihash_codetable::Code`] with `Identity` #[derive(Clone, Copy, Debug, Eq, MultihashDigest, PartialEq)] @@ -76,6 +76,106 @@ pub enum MultihashCode { Ripemd320, } +impl MultihashCode { + /// Calculate the [`Multihash`] of the input byte stream. + pub fn digest_byte_stream(&self, bytes: &mut R) -> anyhow::Result { + fn hash<'a, H: Hasher, R: std::io::Read>( + hasher: &'a mut H, + bytes: &'a mut R, + ) -> anyhow::Result<&'a [u8]> { + let mut buf = [0; 1024]; + loop { + let n = bytes.read(&mut buf)?; + if n == 0 { + break; + } + if let Some(b) = buf.get(0..n) { + hasher.update(b); + } + } + Ok(hasher.finalize()) + } + + Ok(match self { + Self::Sha2_256 => { + let mut hasher = multihash_codetable::Sha2_256::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Sha2_512 => { + let mut hasher = multihash_codetable::Sha2_512::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Sha3_224 => { + let mut hasher = multihash_codetable::Sha3_224::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Sha3_256 => { + let mut hasher = multihash_codetable::Sha3_256::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Sha3_384 => { + let mut hasher = multihash_codetable::Sha3_384::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Sha3_512 => { + let mut hasher = multihash_codetable::Sha3_512::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Keccak224 => { + let mut hasher = multihash_codetable::Keccak224::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Keccak256 => { + let mut hasher = multihash_codetable::Keccak256::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Keccak384 => { + let mut hasher = multihash_codetable::Keccak384::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Keccak512 => { + let mut hasher = multihash_codetable::Keccak512::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Blake2b256 => { + let mut hasher = multihash_codetable::Blake2b256::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Blake2b512 => { + let mut hasher = multihash_codetable::Blake2b512::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Blake2s128 => { + let mut hasher = multihash_codetable::Blake2s128::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Blake2s256 => { + let mut hasher = multihash_codetable::Blake2s256::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Blake3_256 => { + let mut hasher = multihash_codetable::Blake3_256::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Ripemd160 => { + let mut hasher = multihash_codetable::Ripemd160::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Ripemd256 => { + let mut hasher = multihash_codetable::Ripemd256::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + Self::Ripemd320 => { + let mut hasher = multihash_codetable::Ripemd320::default(); + self.wrap(hash(&mut hasher, bytes)?)? + } + _ => { + unimplemented!("{self:?}") + } + }) + } +} + /// Identity hasher with a maximum size. /// /// # Panics @@ -113,3 +213,31 @@ impl multihash_derive::Hasher for IdentityHasher { self.i = 0 } } + +#[cfg(test)] +mod tests { + use std::io::Cursor; + + use super::*; + use crate::utils::rand::forest_rng; + use rand::RngCore as _; + + #[test] + fn test_digest_byte_stream() { + use MultihashCode::*; + + let mut bytes = vec![0; 10000]; + forest_rng().fill_bytes(&mut bytes); + let mut cursor = Cursor::new(bytes.clone()); + for code in [ + Sha2_256, Sha2_512, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Keccak224, Keccak256, + Keccak384, Keccak512, Blake2b256, Blake2b512, Blake2s128, Blake2s256, Blake3_256, + Ripemd160, Ripemd256, Ripemd320, + ] { + cursor.set_position(0); + let mh1 = code.digest(&bytes); + let mh2 = code.digest_byte_stream(&mut cursor).unwrap(); + assert_eq!(mh1, mh2); + } + } +} From caeeb75f3778785b431d0b24e84fb915f09c41b5 Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Thu, 10 Jul 2025 18:26:44 +0800 Subject: [PATCH 2/3] resolve comments --- src/utils/multihash.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/utils/multihash.rs b/src/utils/multihash.rs index 3c5ecda8e8d6..fa3ac841bbc4 100644 --- a/src/utils/multihash.rs +++ b/src/utils/multihash.rs @@ -170,7 +170,7 @@ impl MultihashCode { self.wrap(hash(&mut hasher, bytes)?)? } _ => { - unimplemented!("{self:?}") + anyhow::bail!("`digest_byte_stream` is unimplemented for {self:?}"); } }) } @@ -226,18 +226,20 @@ mod tests { fn test_digest_byte_stream() { use MultihashCode::*; - let mut bytes = vec![0; 10000]; - forest_rng().fill_bytes(&mut bytes); - let mut cursor = Cursor::new(bytes.clone()); - for code in [ - Sha2_256, Sha2_512, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Keccak224, Keccak256, - Keccak384, Keccak512, Blake2b256, Blake2b512, Blake2s128, Blake2s256, Blake3_256, - Ripemd160, Ripemd256, Ripemd320, - ] { - cursor.set_position(0); - let mh1 = code.digest(&bytes); - let mh2 = code.digest_byte_stream(&mut cursor).unwrap(); - assert_eq!(mh1, mh2); + for len in [0, 1, 100, 1024, 10000] { + let mut bytes = vec![0; len]; + forest_rng().fill_bytes(&mut bytes); + let mut cursor = Cursor::new(bytes.clone()); + for code in [ + Sha2_256, Sha2_512, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Keccak224, Keccak256, + Keccak384, Keccak512, Blake2b256, Blake2b512, Blake2s128, Blake2s256, Blake3_256, + Ripemd160, Ripemd256, Ripemd320, + ] { + cursor.set_position(0); + let mh1 = code.digest(&bytes); + let mh2 = code.digest_byte_stream(&mut cursor).unwrap(); + assert_eq!(mh1, mh2); + } } } } From 8d77e661e61b1eccac92b28d9a4999172cc35a1f Mon Sep 17 00:00:00 2001 From: hanabi1224 Date: Thu, 10 Jul 2025 21:57:05 +0800 Subject: [PATCH 3/3] test Identity code --- src/utils/multihash.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utils/multihash.rs b/src/utils/multihash.rs index fa3ac841bbc4..d2f613f8528f 100644 --- a/src/utils/multihash.rs +++ b/src/utils/multihash.rs @@ -240,6 +240,9 @@ mod tests { let mh2 = code.digest_byte_stream(&mut cursor).unwrap(); assert_eq!(mh1, mh2); } + + cursor.set_position(0); + Identity.digest_byte_stream(&mut cursor).unwrap_err(); } } }