diff --git a/src/utils/multihash.rs b/src/utils/multihash.rs index d788a5244498..d2f613f8528f 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)?)? + } + _ => { + anyhow::bail!("`digest_byte_stream` is unimplemented for {self:?}"); + } + }) + } +} + /// Identity hasher with a maximum size. /// /// # Panics @@ -113,3 +213,36 @@ 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::*; + + 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); + } + + cursor.set_position(0); + Identity.digest_byte_stream(&mut cursor).unwrap_err(); + } + } +}