diff --git a/README.md b/README.md index 5afd9eba..25c682d7 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ use multihash::Multihash; const SHA2_256: u64 = 0x12; fn main() { - let hash = Multihash::<64>::wrap(SHA2_256, b"my digest"); + let hash = Multihash::<64>::new(SHA2_256, b"my digest"); println!("{:?}", hash); } ``` diff --git a/codetable/tests/lib.rs b/codetable/tests/lib.rs index 05ff27f7..1b2d2c63 100644 --- a/codetable/tests/lib.rs +++ b/codetable/tests/lib.rs @@ -228,7 +228,7 @@ where let mut expected_cursor = Cursor::new(&expected_bytes); let multihash = code.digest(b"hello world"); - assert_eq!(Multihash::wrap(code.into(), &digest).unwrap(), multihash); + assert_eq!(Multihash::new(code.into(), &digest).unwrap(), multihash); assert_eq!(multihash.code(), u64::from(code)); assert_eq!(multihash.size() as usize, digest.len()); assert_eq!(multihash.digest(), digest); diff --git a/derive-impl/src/multihash.rs b/derive-impl/src/multihash.rs index 57161c31..1d3606d3 100644 --- a/derive-impl/src/multihash.rs +++ b/derive-impl/src/multihash.rs @@ -87,7 +87,7 @@ impl Hash { quote!(Self::#ident => { let mut hasher = #hasher::default(); hasher.update(input); - Multihash::wrap(#code, hasher.finalize()).unwrap() + Multihash::new(#code, hasher.finalize()).unwrap() }) } } @@ -233,7 +233,7 @@ pub fn multihash(s: Structure) -> TokenStream { } fn wrap(&self, digest: &[u8]) -> Result { - Multihash::wrap((*self).into(), digest) + Multihash::new((*self).into(), digest).map_err(#mh_crate::Error::from) } } diff --git a/derive/tests/multihash.rs b/derive/tests/multihash.rs index f723ef5e..31028263 100644 --- a/derive/tests/multihash.rs +++ b/derive/tests/multihash.rs @@ -23,7 +23,7 @@ fn uses_correct_hasher() { hasher.update(b"foobar"); let digest = hasher.finalize(); - let multihash2 = Multihash::wrap(0x38b64f, digest).unwrap(); + let multihash2 = Multihash::new(0x38b64f, digest).unwrap(); assert_eq!(multihash1, multihash2) } diff --git a/examples/identity.rs b/examples/identity.rs index 70983e62..730359bd 100644 --- a/examples/identity.rs +++ b/examples/identity.rs @@ -3,7 +3,7 @@ //! Identity hashing means we don't actually perform any hashing. //! Instead, we just store data directly in place of the "digest". //! -//! [`Multihash::wrap`] returns an error in case the provided digest is too big for the available space. +//! [`Multihash::new`] returns an error in case the provided digest is too big for the available space. //! Make sure you construct a [`Multihash`] with a large enough buffer for your data. //! //! Typically, the way you want to use the "identity" hash is: @@ -17,8 +17,8 @@ use multihash::Multihash; const IDENTITY_HASH_CODE: u64 = 0; fn main() { - let identity_hash = Multihash::<64>::wrap(IDENTITY_HASH_CODE, b"foobar").unwrap(); - let wrap_err = Multihash::<2>::wrap(IDENTITY_HASH_CODE, b"foobar").unwrap_err(); + let identity_hash = Multihash::<64>::new(IDENTITY_HASH_CODE, b"foobar").unwrap(); + let wrap_err = Multihash::<2>::new(IDENTITY_HASH_CODE, b"foobar").unwrap_err(); assert_eq!(identity_hash.digest(), b"foobar"); assert_eq!(wrap_err.to_string(), "Invalid multihash size 6."); diff --git a/src/arb.rs b/src/arb.rs index c279484a..00d69570 100644 --- a/src/arb.rs +++ b/src/arb.rs @@ -31,7 +31,7 @@ impl quickcheck::Arbitrary for Multihash { let size = rng.gen_range(0..S); let mut data = [0; S]; rng.fill_bytes(&mut data); - Multihash::wrap(code, &data[..size]).unwrap() + Multihash::new(code, &data[..size]).unwrap() } } @@ -58,7 +58,7 @@ impl<'a, const S: usize> arbitrary::Arbitrary<'a> for Multihash { let size = u.int_in_range(0..=S)?; let data = u.bytes(size)?; - Ok(Multihash::wrap(code, data).unwrap()) + Ok(Multihash::new(code, data).unwrap()) } fn size_hint(depth: usize) -> (usize, Option) { @@ -76,7 +76,7 @@ mod tests { let mut u = Unstructured::new(&[2, 4, 13, 5, 6, 7, 8, 9, 6]); let mh = as Arbitrary>::arbitrary(&mut u).unwrap(); - let mh2 = Multihash::<16>::wrap(1037, &[6, 7, 8, 9, 6]).unwrap(); + let mh2 = Multihash::<16>::new(1037, &[6, 7, 8, 9, 6]).unwrap(); assert_eq!(mh, mh2); } } diff --git a/src/multihash.rs b/src/multihash.rs index a18a6f21..6d273d76 100644 --- a/src/multihash.rs +++ b/src/multihash.rs @@ -3,7 +3,7 @@ use crate::Error; use alloc::vec::Vec; use core::convert::TryInto; -use core::fmt::Debug; +use core::fmt::{self, Debug}; use unsigned_varint::encode as varint_encode; @@ -54,13 +54,49 @@ impl Default for Multihash { } } +/// Returned from [`Multihash::new`] if the digest cannot fit inside the multihash. +#[derive(Debug)] +pub struct DigestTooBig(usize); + +impl fmt::Display for DigestTooBig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Must match the error message for `crate::Error` + f.write_fmt(format_args!("Invalid multihash size {}.", self.0)) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for DigestTooBig {} + +// Allows implementations of `multihash_derive::MultihashDigest` to construct the +// error required by that trait without using the deprecated `Multihash::wrap` +// +// (This includes the derive macro for that trait) +impl From for crate::Error { + fn from(value: DigestTooBig) -> Self { + Error::invalid_size(value.0 as _) + } +} + impl Multihash { - /// Wraps the digest in a multihash. - pub const fn wrap(code: u64, input_digest: &[u8]) -> Result { - if input_digest.len() > S { - return Err(Error::invalid_size(input_digest.len() as _)); - } + /// Constructs a new [`Multihash`]. + /// + /// The provided digest must be created from a hash-function with the registered code. + /// + /// This function may be called in const contexts. + /// ```rust + /// # use multihash::Multihash; + /// const MULTIHASH: Multihash<64> = match Multihash::new(0, &[]) { + /// Ok(ok) => ok, + /// Err(_) => panic!(), + /// }; + /// ``` + pub const fn new(code: u64, input_digest: &[u8]) -> Result { let size = input_digest.len(); + if size > S { + return Err(DigestTooBig(size)); + } + let mut digest = [0; S]; let mut i = 0; while i < size { @@ -74,6 +110,16 @@ impl Multihash { }) } + /// Wraps the digest in a multihash. + #[deprecated = "Use Multihash::new instead, which allows accessing a Multihash in const contexts"] + #[doc(hidden)] + pub const fn wrap(code: u64, input_digest: &[u8]) -> Result { + match Self::new(code, input_digest) { + Ok(ok) => Ok(ok), + Err(DigestTooBig(u)) => Err(Error::invalid_size(u as _)), + } + } + /// Returns the code of the multihash. pub const fn code(&self) -> u64 { self.code @@ -321,14 +367,14 @@ mod tests { fn test_scale() { use parity_scale_codec::{Decode, Encode}; - let mh1 = Multihash::<32>::wrap(0, b"hello world").unwrap(); + let mh1 = Multihash::<32>::new(0, b"hello world").unwrap(); // println!("mh1: code = {}, size = {}, digest = {:?}", mh1.code(), mh1.size(), mh1.digest()); let mh1_bytes = mh1.encode(); // println!("Multihash<32>: {}", hex::encode(&mh1_bytes)); let mh2: Multihash<32> = Decode::decode(&mut &mh1_bytes[..]).unwrap(); assert_eq!(mh1, mh2); - let mh3 = Multihash::<64>::wrap(0, b"hello world").unwrap(); + let mh3 = Multihash::<64>::new(0, b"hello world").unwrap(); // println!("mh3: code = {}, size = {}, digest = {:?}", mh3.code(), mh3.size(), mh3.digest()); let mh3_bytes = mh3.encode(); // println!("Multihash<64>: {}", hex::encode(&mh3_bytes)); diff --git a/src/serde.rs b/src/serde.rs index ba642e55..d80b40f2 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -75,7 +75,7 @@ mod tests { // This is a concatenation of `SHA2_256_CODE + DIGEST_LENGTH + DIGEST`. let expected_json = format!("[{},{},159,228,204,198,222,22,114,79,58,48,199,232,242,84,243,198,71,25,134,172,177,248,216,207,142,150,206,42,215,219,231,251]", SHA2_256_CODE as u8, DIGEST.len() as u8); - let mh = Multihash::<32>::wrap(SHA2_256_CODE, &DIGEST).unwrap(); + let mh = Multihash::<32>::new(SHA2_256_CODE, &DIGEST).unwrap(); let json = serde_json::to_string(&mh).unwrap(); assert_eq!(json, expected_json); @@ -124,7 +124,7 @@ mod tests { 251, ]; - let mh = Multihash::<32>::wrap(SHA2_256_CODE, &DIGEST).unwrap(); + let mh = Multihash::<32>::new(SHA2_256_CODE, &DIGEST).unwrap(); // As bytes. assert_tokens(&mh, &[Token::Bytes(&ENCODED_MULTIHASH_BYTES)]);