From 9e303edaabaa7ee474cbfcd1d13cba3ca977bdd6 Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Wed, 19 Jul 2023 16:21:53 +0100 Subject: [PATCH 1/8] feat: usable const constructor --- src/multihash.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/multihash.rs b/src/multihash.rs index a18a6f21..6b019640 100644 --- a/src/multihash.rs +++ b/src/multihash.rs @@ -54,7 +54,41 @@ impl Default for Multihash { } } +/// Returned from [`Multihash::new`] +#[non_exhaustive] +pub enum CreationError { + /// The digest exceeds the capacity of the multihash + DigestTooBig, +} + impl Multihash { + /// Wrap the digest in a multihash + /// ```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 { + if input_digest.len() > S { + return Err(CreationError::DigestTooBig); + } + + let size = input_digest.len(); + let mut digest = [0; S]; + let mut i = 0; + while i < size { + digest[i] = input_digest[i]; + i += 1; + } + Ok(Self { + code, + size: size as u8, + digest, + }) + } + /// Wraps the digest in a multihash. pub const fn wrap(code: u64, input_digest: &[u8]) -> Result { if input_digest.len() > S { From 349423c2c1ca15991b8b68ddd23619395e8aa443 Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Wed, 19 Jul 2023 16:24:40 +0100 Subject: [PATCH 2/8] feat: CreationError implements std::error::Error --- src/multihash.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/multihash.rs b/src/multihash.rs index 6b019640..46284620 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; @@ -55,12 +55,26 @@ impl Default for Multihash { } /// Returned from [`Multihash::new`] +#[derive(Debug)] #[non_exhaustive] pub enum CreationError { /// The digest exceeds the capacity of the multihash DigestTooBig, } +impl fmt::Display for CreationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CreationError::DigestTooBig => { + f.write_str("The digest exceeds the capacity of the multihash") + } + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for CreationError {} + impl Multihash { /// Wrap the digest in a multihash /// ```rust From f3ac22ed8053293d9009ac0012d5b6225071714e Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Wed, 19 Jul 2023 16:27:11 +0100 Subject: [PATCH 3/8] refactor: Multihash::wrap uses Multihash::new internally --- src/multihash.rs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/src/multihash.rs b/src/multihash.rs index 46284620..08b918d1 100644 --- a/src/multihash.rs +++ b/src/multihash.rs @@ -105,21 +105,10 @@ 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 _)); + match Self::new(code, input_digest) { + Ok(ok) => Ok(ok), + Err(CreationError::DigestTooBig) => Err(Error::invalid_size(input_digest.len() as _)), } - let size = input_digest.len(); - let mut digest = [0; S]; - let mut i = 0; - while i < size { - digest[i] = input_digest[i]; - i += 1; - } - Ok(Self { - code, - size: size as u8, - digest, - }) } /// Returns the code of the multihash. From f501ff37f98550983a86f0ee37aa64b72c38dcde Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Wed, 19 Jul 2023 16:28:37 +0100 Subject: [PATCH 4/8] chore: mark Multihash::wrap as deprecated, and hide it --- src/multihash.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/multihash.rs b/src/multihash.rs index 08b918d1..b800a893 100644 --- a/src/multihash.rs +++ b/src/multihash.rs @@ -104,6 +104,8 @@ impl Multihash { } /// Wraps the digest in a multihash. + #[deprecated] + #[doc(hidden)] pub const fn wrap(code: u64, input_digest: &[u8]) -> Result { match Self::new(code, input_digest) { Ok(ok) => Ok(ok), From 82df0eae83d4af614ba6f95fc8d469ff4a96131d Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Wed, 19 Jul 2023 16:35:58 +0100 Subject: [PATCH 5/8] fix: clippy linting use of deprecated Multihash::wrap --- codetable/tests/lib.rs | 2 +- derive/tests/multihash.rs | 2 +- examples/identity.rs | 4 ++-- src/arb.rs | 6 +++--- src/multihash.rs | 4 ++-- src/serde.rs | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) 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/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..58db7c60 100644 --- a/examples/identity.rs +++ b/examples/identity.rs @@ -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 b800a893..3f2efb4e 100644 --- a/src/multihash.rs +++ b/src/multihash.rs @@ -360,14 +360,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)]); From 372a05715f85491b74f6d2271c55c57008af9a5c Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Wed, 19 Jul 2023 16:36:57 +0100 Subject: [PATCH 6/8] doc: add note to deprecation --- src/multihash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/multihash.rs b/src/multihash.rs index 3f2efb4e..fd650abf 100644 --- a/src/multihash.rs +++ b/src/multihash.rs @@ -104,7 +104,7 @@ impl Multihash { } /// Wraps the digest in a multihash. - #[deprecated] + #[deprecated = "use Multihash::new insteads"] #[doc(hidden)] pub const fn wrap(code: u64, input_digest: &[u8]) -> Result { match Self::new(code, input_digest) { From 36468405234768562d696c0e41f10e2639588dfb Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Wed, 19 Jul 2023 16:40:29 +0100 Subject: [PATCH 7/8] chore: cleanup stray uses of Multihash::wrap --- README.md | 2 +- derive-impl/src/multihash.rs | 2 +- examples/identity.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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/derive-impl/src/multihash.rs b/derive-impl/src/multihash.rs index 57161c31..19780b52 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() }) } } diff --git a/examples/identity.rs b/examples/identity.rs index 58db7c60..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: From 24a433b4ee2930fb0e0c1a1364ffb94b5733c9ec Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Wed, 19 Jul 2023 19:45:53 +0100 Subject: [PATCH 8/8] review: markups --- derive-impl/src/multihash.rs | 2 +- src/multihash.rs | 47 +++++++++++++++++++++--------------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/derive-impl/src/multihash.rs b/derive-impl/src/multihash.rs index 19780b52..1d3606d3 100644 --- a/derive-impl/src/multihash.rs +++ b/derive-impl/src/multihash.rs @@ -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/src/multihash.rs b/src/multihash.rs index fd650abf..6d273d76 100644 --- a/src/multihash.rs +++ b/src/multihash.rs @@ -54,29 +54,36 @@ impl Default for Multihash { } } -/// Returned from [`Multihash::new`] +/// Returned from [`Multihash::new`] if the digest cannot fit inside the multihash. #[derive(Debug)] -#[non_exhaustive] -pub enum CreationError { - /// The digest exceeds the capacity of the multihash - DigestTooBig, -} +pub struct DigestTooBig(usize); -impl fmt::Display for CreationError { +impl fmt::Display for DigestTooBig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - CreationError::DigestTooBig => { - f.write_str("The digest exceeds the capacity of the multihash") - } - } + // 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 CreationError {} +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 { - /// Wrap the digest in a multihash + /// 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, &[]) { @@ -84,12 +91,12 @@ impl Multihash { /// Err(_) => panic!(), /// }; /// ``` - pub const fn new(code: u64, input_digest: &[u8]) -> Result { - if input_digest.len() > S { - return Err(CreationError::DigestTooBig); + pub const fn new(code: u64, input_digest: &[u8]) -> Result { + let size = input_digest.len(); + if size > S { + return Err(DigestTooBig(size)); } - let size = input_digest.len(); let mut digest = [0; S]; let mut i = 0; while i < size { @@ -104,12 +111,12 @@ impl Multihash { } /// Wraps the digest in a multihash. - #[deprecated = "use Multihash::new insteads"] + #[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(CreationError::DigestTooBig) => Err(Error::invalid_size(input_digest.len() as _)), + Err(DigestTooBig(u)) => Err(Error::invalid_size(u as _)), } }