diff --git a/Cargo.toml b/Cargo.toml index 4600b694..580f7fac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] -members = ["derive", "."] +members = ["derive", "derive-impl", ".", "codetable"] +resolver = "2" [package] name = "multihash" @@ -15,24 +16,13 @@ edition = "2021" rust-version = "1.59" [features] -default = ["std", "derive", "multihash-impl", "secure-hashes"] -std = ["unsigned-varint/std", "multihash-derive/std", "alloc"] -alloc = ["core2/alloc"] -multihash-impl = ["derive"] -derive = ["multihash-derive"] -arb = ["quickcheck", "rand", "arbitrary"] -secure-hashes = ["blake2b", "blake2s", "blake3", "sha2", "sha3"] -scale-codec = ["parity-scale-codec"] -serde-codec = ["serde", "serde-big-array"] - -blake2b = ["blake2b_simd"] -blake2s = ["blake2s_simd"] -identity = [] -sha1 = ["digest", "sha-1"] -sha2 = ["digest", "sha-2"] -sha3 = ["digest", "sha-3"] -strobe = ["strobe-rs"] -ripemd = ["ripemd-rs"] +default = ["std"] +std = ["unsigned-varint/std", "alloc"] +alloc = [] +arb = ["dep:quickcheck", "dep:rand", "dep:arbitrary"] +scale-codec = ["dep:parity-scale-codec"] +serde-codec = ["serde"] # Deprecated, don't use. +serde = ["dep:serde", "dep:serde-big-array"] [dependencies] parity-scale-codec = { version = "3.0.0", default-features = false, features = ["derive"], optional = true } @@ -40,31 +30,14 @@ quickcheck = { version = "1.0.3", optional = true } rand = { version = "0.8.5", optional = true, features = ["small_rng"] } serde = { version = "1.0.116", optional = true, default-features = false, features = ["derive"] } serde-big-array = { version = "0.3.2", optional = true, features = ["const-generics"] } -multihash-derive = { version = "0.8.0", path = "derive", default-features = false, optional = true } unsigned-varint = { version = "0.7.1", default-features = false } arbitrary = {version = "1.1.0", optional = true } -blake2b_simd = { version = "1.0.0", default-features = false, optional = true } -blake2s_simd = { version = "1.0.0", default-features = false, optional = true } -blake3 = { version = "1.2.0", default-features = false, optional = true } -digest = { version = "0.10.1", default-features = false, optional = true } -sha-1 = { version = "0.10.0", default-features = false, optional = true } -sha-2 = { version = "0.10.0", default-features = false, optional = true, package = "sha2" } -sha-3 = { version = "0.10.0", default-features = false, optional = true, package = "sha3" } -strobe-rs = { version = "0.7.0", default-features = false, optional = true } -ripemd-rs = { package = "ripemd", version = "0.1.1", optional = true} - core2 = { version = "0.4.0", default-features = false } [dev-dependencies] -criterion = "0.3.3" hex = "0.4.2" serde_json = "1.0.58" quickcheck = "1.0.3" rand = "0.8.5" arbitrary = "1.1.0" -multihash = { path = ".", features = ["sha1", "strobe"] } - -[[bench]] -name = "multihash" -harness = false diff --git a/codetable/Cargo.toml b/codetable/Cargo.toml new file mode 100644 index 00000000..713747ba --- /dev/null +++ b/codetable/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "multihash-codetable" +description = "Default multihash code-table with cryptographically secure hash implementations" +version = "0.1.0" +repository = "https://github.com/multiformats/rust-multihash" +license = "MIT" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = ["std"] +std = ["blake2b_simd?/std", "blake2s_simd?/std", "blake3?/std", "digest?/std", "sha-1?/std", "sha2?/std", "sha3?/std", "strobe-rs?/std", "ripemd?/std", "multihash-derive/std", "core2/std"] +sha1 = ["dep:sha-1"] +strobe = ["dep:strobe-rs"] +blake2b = ["dep:blake2b_simd"] +blake2s = ["dep:blake2s_simd"] +identity = [] + +[dependencies] +blake2b_simd = { version = "1.0.0", default-features = false, optional = true } +blake2s_simd = { version = "1.0.0", default-features = false, optional = true } +blake3 = { version = "1.2.0", default-features = false, optional = true } +digest = { version = "0.10.1", default-features = false, optional = true } +sha-1 = { version = "0.10.0", default-features = false, optional = true } +sha2 = { version = "0.10.0", default-features = false, optional = true } +sha3 = { version = "0.10.0", default-features = false, optional = true } +strobe-rs = { version = "0.7.0", default-features = false, optional = true } +ripemd = { version = "0.1.1", default-features = false, optional = true } +multihash-derive = { version = "0.8.0", path = "../derive", default-features = false } +core2 = { version = "0.4.0", default-features = false } +serde = { version = "1.0.158", features = ["derive"], optional = true } + +[dev-dependencies] +hex = "0.4.2" +unsigned-varint = { version = "0.7.1", default-features = false } +criterion = "0.3.3" +rand = "0.8.5" + +[[bench]] +name = "multihash" +harness = false + +[[test]] +name = "lib" +required-features = ["sha1", "sha2", "sha3", "ripemd", "strobe", "blake2b", "blake2s", "blake3"] diff --git a/benches/multihash.rs b/codetable/benches/multihash.rs similarity index 94% rename from benches/multihash.rs rename to codetable/benches/multihash.rs index 0b75ea15..735926ac 100644 --- a/benches/multihash.rs +++ b/codetable/benches/multihash.rs @@ -1,11 +1,12 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use rand::Rng; -use multihash::{ - Blake2b256, Blake2b512, Blake2s128, Blake2s256, Blake3_256, Hasher, Keccak224, Keccak256, - Keccak384, Keccak512, Sha1, Sha2_256, Sha2_512, Sha3_224, Sha3_256, Sha3_384, Sha3_512, - Strobe256, Strobe512, +use multihash_codetable::{ + Blake2b256, Blake2b512, Blake2s128, Blake2s256, Blake3_256, Keccak224, Keccak256, Keccak384, + Keccak512, Sha1, Sha2_256, Sha2_512, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Strobe256, + Strobe512, }; +use multihash_derive::Hasher; macro_rules! group_digest { ($criterion:ident, $( $id:expr => $hash:ident, $input:expr)* ) => {{ diff --git a/examples/custom_table.rs b/codetable/examples/custom_table.rs similarity index 88% rename from examples/custom_table.rs rename to codetable/examples/custom_table.rs index a7f6a92f..40235d0d 100644 --- a/examples/custom_table.rs +++ b/codetable/examples/custom_table.rs @@ -1,7 +1,7 @@ use std::convert::TryFrom; -use multihash::derive::Multihash; -use multihash::{Error, Hasher, MultihashDigest, MultihashGeneric, Sha2_256}; +use multihash_codetable::Sha2_256; +use multihash_derive::{Hasher, MultihashDigest}; // You can implement a custom hasher. This is a SHA2 256-bit hasher that returns a hash that is // truncated to 160 bits. @@ -19,14 +19,14 @@ impl Hasher for Sha2_256Truncated20 { } } -#[derive(Clone, Copy, Debug, Eq, Multihash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, MultihashDigest, PartialEq)] #[mh(alloc_size = 64)] pub enum Code { /// Example for using a custom hasher which returns truncated hashes #[mh(code = 0x12, hasher = Sha2_256Truncated20)] Sha2_256Truncated20, /// Example for using a hasher with a bit size that is not exported by default - #[mh(code = 0xb219, hasher = multihash::Blake2bHasher::<25>)] + #[mh(code = 0xb219, hasher = multihash_codetable::Blake2bHasher::<25>)] Blake2b200, } diff --git a/examples/manual_mh.rs b/codetable/examples/manual_mh.rs similarity index 91% rename from examples/manual_mh.rs rename to codetable/examples/manual_mh.rs index 95414b85..c3905591 100644 --- a/examples/manual_mh.rs +++ b/codetable/examples/manual_mh.rs @@ -1,4 +1,5 @@ -use multihash::{Code, MultihashDigest}; +use multihash_codetable::Code; +use multihash_derive::MultihashDigest; /// prefix/multihash generating tool to aid when adding new tests fn prefix_util() { diff --git a/src/hasher_impl.rs b/codetable/src/hasher_impl.rs similarity index 78% rename from src/hasher_impl.rs rename to codetable/src/hasher_impl.rs index 4e24439a..d7e9bf36 100644 --- a/src/hasher_impl.rs +++ b/codetable/src/hasher_impl.rs @@ -1,27 +1,22 @@ -use crate::hasher::Hasher; - -#[cfg(feature = "std")] -use std::io; - -#[cfg(not(feature = "std"))] -use core2::io; - +#[cfg(any(feature = "strobe", feature = "identity", feature = "blake3"))] macro_rules! derive_write { ($name:ident) => { - impl io::Write for $name { - fn write(&mut self, buf: &[u8]) -> io::Result { + impl core2::io::Write for $name { + fn write(&mut self, buf: &[u8]) -> core2::io::Result { + use multihash_derive::Hasher as _; + self.update(buf); Ok(buf.len()) } - fn flush(&mut self) -> io::Result<()> { + fn flush(&mut self) -> core2::io::Result<()> { Ok(()) } } }; } -#[cfg(any(feature = "blake2b", feature = "blake2s"))] +#[cfg(any(feature = "blake2b", feature = "blake2s", feature = "blake3"))] macro_rules! derive_hasher_blake { ($module:ident, $name:ident) => { /// Multihash hasher. @@ -42,7 +37,7 @@ macro_rules! derive_hasher_blake { } } - impl Hasher for $name { + impl multihash_derive::Hasher for $name { fn update(&mut self, input: &[u8]) { self.state.update(input); } @@ -67,8 +62,6 @@ macro_rules! derive_hasher_blake { #[cfg(feature = "blake2b")] pub mod blake2b { - use super::*; - derive_hasher_blake!(blake2b_simd, Blake2bHasher); /// 256 bit blake2b hasher. @@ -80,8 +73,6 @@ pub mod blake2b { #[cfg(feature = "blake2s")] pub mod blake2s { - use super::*; - derive_hasher_blake!(blake2s_simd, Blake2sHasher); /// 256 bit blake2b hasher. @@ -93,8 +84,6 @@ pub mod blake2s { #[cfg(feature = "blake3")] pub mod blake3 { - use super::*; - /// Multihash hasher. #[derive(Debug)] pub struct Blake3Hasher { @@ -121,7 +110,7 @@ pub mod blake3 { } } - impl Hasher for Blake3Hasher { + impl multihash_derive::Hasher for Blake3Hasher { fn update(&mut self, input: &[u8]) { self.hasher.update(input); } @@ -143,7 +132,12 @@ pub mod blake3 { pub type Blake3_256 = Blake3Hasher<32>; } -#[cfg(feature = "digest")] +#[cfg(any( + feature = "sha1", + feature = "sha2", + feature = "sha3", + feature = "ripemd" +))] macro_rules! derive_rustcrypto_hasher { ($module:ty, $name:ident, $size:expr) => { /// Multihash hasher. @@ -162,7 +156,7 @@ macro_rules! derive_rustcrypto_hasher { } } - impl $crate::hasher::Hasher for $name { + impl ::multihash_derive::Hasher for $name { fn update(&mut self, input: &[u8]) { use digest::Digest; self.state.update(input) @@ -183,13 +177,15 @@ macro_rules! derive_rustcrypto_hasher { } } - impl io::Write for $name { - fn write(&mut self, buf: &[u8]) -> io::Result { + impl core2::io::Write for $name { + fn write(&mut self, buf: &[u8]) -> core2::io::Result { + use multihash_derive::Hasher as _; + self.update(buf); Ok(buf.len()) } - fn flush(&mut self) -> io::Result<()> { + fn flush(&mut self) -> core2::io::Result<()> { Ok(()) } } @@ -198,47 +194,37 @@ macro_rules! derive_rustcrypto_hasher { #[cfg(feature = "sha1")] pub mod sha1 { - use super::*; - derive_rustcrypto_hasher!(::sha1::Sha1, Sha1, 20); } #[cfg(feature = "sha2")] pub mod sha2 { - use super::*; - - derive_rustcrypto_hasher!(sha_2::Sha256, Sha2_256, 32); - derive_rustcrypto_hasher!(sha_2::Sha512, Sha2_512, 64); + derive_rustcrypto_hasher!(::sha2::Sha256, Sha2_256, 32); + derive_rustcrypto_hasher!(::sha2::Sha512, Sha2_512, 64); } #[cfg(feature = "sha3")] pub mod sha3 { - use super::*; - - derive_rustcrypto_hasher!(sha_3::Sha3_224, Sha3_224, 28); - derive_rustcrypto_hasher!(sha_3::Sha3_256, Sha3_256, 32); - derive_rustcrypto_hasher!(sha_3::Sha3_384, Sha3_384, 48); - derive_rustcrypto_hasher!(sha_3::Sha3_512, Sha3_512, 64); - - derive_rustcrypto_hasher!(sha_3::Keccak224, Keccak224, 28); - derive_rustcrypto_hasher!(sha_3::Keccak256, Keccak256, 32); - derive_rustcrypto_hasher!(sha_3::Keccak384, Keccak384, 48); - derive_rustcrypto_hasher!(sha_3::Keccak512, Keccak512, 64); + derive_rustcrypto_hasher!(::sha3::Sha3_224, Sha3_224, 28); + derive_rustcrypto_hasher!(::sha3::Sha3_256, Sha3_256, 32); + derive_rustcrypto_hasher!(::sha3::Sha3_384, Sha3_384, 48); + derive_rustcrypto_hasher!(::sha3::Sha3_512, Sha3_512, 64); + + derive_rustcrypto_hasher!(::sha3::Keccak224, Keccak224, 28); + derive_rustcrypto_hasher!(::sha3::Keccak256, Keccak256, 32); + derive_rustcrypto_hasher!(::sha3::Keccak384, Keccak384, 48); + derive_rustcrypto_hasher!(::sha3::Keccak512, Keccak512, 64); } #[cfg(feature = "ripemd")] pub mod ripemd { - - use super::*; - - derive_rustcrypto_hasher!(ripemd_rs::Ripemd160, Ripemd160, 20); - derive_rustcrypto_hasher!(ripemd_rs::Ripemd256, Ripemd256, 32); - derive_rustcrypto_hasher!(ripemd_rs::Ripemd320, Ripemd320, 40); + derive_rustcrypto_hasher!(::ripemd::Ripemd160, Ripemd160, 20); + derive_rustcrypto_hasher!(::ripemd::Ripemd256, Ripemd256, 32); + derive_rustcrypto_hasher!(::ripemd::Ripemd320, Ripemd320, 40); } +#[cfg(feature = "identity")] pub mod identity { - use super::*; - /// Identity hasher with a maximum size. /// /// # Panics @@ -259,7 +245,7 @@ pub mod identity { } } - impl Hasher for IdentityHasher { + impl multihash_derive::Hasher for IdentityHasher { fn update(&mut self, input: &[u8]) { let start = self.i.min(self.bytes.len()); let end = (self.i + input.len()).min(self.bytes.len()); @@ -288,7 +274,6 @@ pub mod identity { #[cfg(feature = "strobe")] pub mod strobe { - use super::*; use strobe_rs::{SecParam, Strobe}; /// Strobe hasher. @@ -308,7 +293,7 @@ pub mod strobe { } } - impl Hasher for StrobeHasher { + impl multihash_derive::Hasher for StrobeHasher { fn update(&mut self, input: &[u8]) { self.strobe.ad(input, self.initialized); self.initialized = true; diff --git a/src/multihash_impl.rs b/codetable/src/lib.rs similarity index 58% rename from src/multihash_impl.rs rename to codetable/src/lib.rs index 29face0b..8eade693 100644 --- a/src/multihash_impl.rs +++ b/codetable/src/lib.rs @@ -1,14 +1,38 @@ -pub use multihash_derive::Multihash; +#![cfg_attr(not(feature = "std"), no_std)] + +mod hasher_impl; + +use multihash_derive::MultihashDigest; + +#[cfg(feature = "blake2b")] +pub use crate::hasher_impl::blake2b::{Blake2b256, Blake2b512, Blake2bHasher}; +#[cfg(feature = "blake2s")] +pub use crate::hasher_impl::blake2s::{Blake2s128, Blake2s256, Blake2sHasher}; +#[cfg(feature = "blake3")] +pub use crate::hasher_impl::blake3::{Blake3Hasher, Blake3_256}; +#[cfg(feature = "identity")] +pub use crate::hasher_impl::identity::{Identity256, IdentityHasher}; +#[cfg(feature = "ripemd")] +pub use crate::hasher_impl::ripemd::{Ripemd160, Ripemd256, Ripemd320}; +#[cfg(feature = "sha1")] +pub use crate::hasher_impl::sha1::Sha1; +#[cfg(feature = "sha2")] +pub use crate::hasher_impl::sha2::{Sha2_256, Sha2_512}; +#[cfg(feature = "sha3")] +pub use crate::hasher_impl::sha3::{ + Keccak224, Keccak256, Keccak384, Keccak512, Sha3_224, Sha3_256, Sha3_384, Sha3_512, +}; +#[cfg(feature = "strobe")] +pub use crate::hasher_impl::strobe::{Strobe256, Strobe512, StrobeHasher}; /// Default (cryptographically secure) Multihash implementation. /// /// This is a default set of hashing algorithms. Usually applications would use their own subset of -/// algorithms. See the [`Multihash` derive] for more information. +/// algorithms. See the [`multihash-derive`] crate for more information. /// -/// [`Multihash` derive]: crate::derive -#[cfg_attr(feature = "serde-codec", derive(serde::Deserialize))] -#[cfg_attr(feature = "serde-codec", derive(serde::Serialize))] -#[derive(Copy, Clone, Debug, Eq, Multihash, PartialEq)] +/// [`multihash-derive`]: https://docs.rs/multihash-derive +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Copy, Clone, Debug, Eq, MultihashDigest, PartialEq)] #[mh(alloc_size = 64)] pub enum Code { /// SHA-256 (32-byte hash size) @@ -94,11 +118,11 @@ pub enum Code { #[cfg(test)] mod tests { use super::*; - use crate::hasher::Hasher; use crate::hasher_impl::sha3::{Sha3_256, Sha3_512}; - use crate::multihash::MultihashDigest; + use multihash_derive::{Hasher, Multihash, MultihashDigest}; #[test] + #[cfg(feature = "sha3")] fn test_hasher_256() { let mut hasher = Sha3_256::default(); hasher.update(b"hello world"); @@ -112,6 +136,7 @@ mod tests { } #[test] + #[cfg(feature = "sha3")] fn test_hasher_512() { let mut hasher = Sha3_512::default(); hasher.update(b"hello world"); @@ -123,4 +148,52 @@ mod tests { assert_eq!(hash.digest(), digest); assert_eq!(hash, hash2); } + + #[test] + #[cfg(feature = "sha2")] + fn roundtrip() { + let hash = Code::Sha2_256.digest(b"hello world"); + let mut buf = [0u8; 35]; + let written = hash.write(&mut buf[..]).unwrap(); + let hash2 = Multihash::<32>::read(&buf[..]).unwrap(); + assert_eq!(hash, hash2); + assert_eq!(hash.encoded_len(), written); + } + + #[test] + #[cfg(feature = "sha2")] + fn test_truncate_down() { + let hash = Code::Sha2_256.digest(b"hello world"); + let small = hash.truncate(20); + assert_eq!(small.size(), 20); + } + + #[test] + #[cfg(feature = "sha2")] + fn test_truncate_up() { + let hash = Code::Sha2_256.digest(b"hello world"); + let small = hash.truncate(100); + assert_eq!(small.size(), 32); + } + + #[test] + #[cfg(feature = "sha2")] + fn test_resize_fits() { + let hash = Code::Sha2_256.digest(b"hello world"); + let _: Multihash<32> = hash.resize().unwrap(); + } + + #[test] + #[cfg(feature = "sha2")] + fn test_resize_up() { + let hash = Code::Sha2_256.digest(b"hello world"); + let _: Multihash<100> = hash.resize().unwrap(); + } + + #[test] + #[cfg(feature = "sha2")] + fn test_resize_truncate() { + let hash = Code::Sha2_256.digest(b"hello world"); + hash.resize::<20>().unwrap_err(); + } } diff --git a/tests/lib.rs b/codetable/tests/lib.rs similarity index 96% rename from tests/lib.rs rename to codetable/tests/lib.rs index 906d3d00..7fc63b83 100644 --- a/tests/lib.rs +++ b/codetable/tests/lib.rs @@ -1,15 +1,13 @@ use std::io::{Cursor, Write}; -use multihash::{ - derive::Multihash, Blake2b256, Blake2b512, Blake2s128, Blake2s256, Blake3_256, Hasher, - Identity256, Keccak224, Keccak256, Keccak384, Keccak512, MultihashDigest, Sha1, Sha2_256, - Sha2_512, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Strobe256, Strobe512, +use multihash_codetable::{ + Blake2b256, Blake2b512, Blake2s128, Blake2s256, Blake3_256, Identity256, Keccak224, Keccak256, + Keccak384, Keccak512, Ripemd160, Ripemd256, Ripemd320, Sha1, Sha2_256, Sha2_512, Sha3_224, + Sha3_256, Sha3_384, Sha3_512, Strobe256, Strobe512, }; +use multihash_derive::{Hasher, MultihashDigest}; -#[cfg(feature = "ripemd")] -use multihash::{Ripemd160, Ripemd256, Ripemd320}; - -#[derive(Clone, Copy, Debug, Eq, Multihash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, MultihashDigest, PartialEq)] #[mh(alloc_size = 64)] pub enum Code { #[mh(code = 0x00, hasher = Identity256)] @@ -388,7 +386,8 @@ fn multihash_errors() { #[test] fn blak3_non_default_digest() { - use multihash::Blake3Hasher; + use multihash_codetable::Blake3Hasher; + use multihash_derive::MultihashDigest; const DIGEST_SIZE: usize = 16; pub struct ContentHasher(Blake3Hasher); @@ -404,7 +403,9 @@ fn blak3_non_default_digest() { } fn finish(&mut self) -> ContentHash { - let hash = multihash::Code::Blake3_256.wrap(self.0.finalize()).unwrap(); + let hash = multihash_codetable::Code::Blake3_256 + .wrap(self.0.finalize()) + .unwrap(); let resized_hash = hash.resize::().unwrap(); let mut content = ContentHash([0u8; DIGEST_SIZE]); diff --git a/derive-impl/Cargo.toml b/derive-impl/Cargo.toml new file mode 100644 index 00000000..c8dd27a9 --- /dev/null +++ b/derive-impl/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "multihash-derive-impl" +version = "0.1.0" +authors = ["David Craven "] +edition = "2018" +description = "Internal proc-macro crate for the MultihashDigest derive" +license = "MIT" +repository = "https://github.com/multiformats/rust-multihash" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = { version = "1.0.24", features = ["span-locations"] } +proc-macro-crate = "~1.1.0" +proc-macro-error = "1.0.4" +quote = "1.0.7" +syn = "1.0.42" +synstructure = "0.12.4" diff --git a/derive-impl/src/lib.rs b/derive-impl/src/lib.rs new file mode 100644 index 00000000..7fd1c3c4 --- /dev/null +++ b/derive-impl/src/lib.rs @@ -0,0 +1,38 @@ +//! This is an internal crate that implements the actual `MultihashDigest` derive. +//! +//! The `multihash-derive` crate acts as a facade and defines additional symbols that our derive depends on. +//! For example, the actual trait that we are deriving `MultihashDigest`, as well as the `Hasher` trait and +//! the `UnsupportedCode` error type. + +extern crate proc_macro; + +mod multihash; +mod utils; + +use proc_macro::TokenStream; +use proc_macro_error::proc_macro_error; +use synstructure::macros::{parse, DeriveInput}; +use synstructure::{MacroResult, Structure}; + +#[proc_macro_derive(Multihash, attributes(mh))] +#[allow(non_snake_case)] +#[proc_macro_error] +#[deprecated(since = "0.8.1", note = "Use `MultihashDigest` derive instead.")] +pub fn Multihash(i: TokenStream) -> TokenStream { + match parse::(i) { + Ok(p) => match Structure::try_new(&p) { + Ok(s) => multihash::multihash(s).into_stream(), + Err(e) => e.to_compile_error().into(), + }, + Err(e) => e.to_compile_error().into(), + } +} + +/// Custom derive for the `MultihashDigest` trait. +#[proc_macro_derive(MultihashDigest, attributes(mh))] +#[allow(non_snake_case)] +#[proc_macro_error] +pub fn MultihashDigest(i: TokenStream) -> TokenStream { + #[allow(deprecated)] + Multihash(i) +} diff --git a/derive/src/multihash.rs b/derive-impl/src/multihash.rs similarity index 63% rename from derive/src/multihash.rs rename to derive-impl/src/multihash.rs index d03dcec2..57161c31 100644 --- a/derive/src/multihash.rs +++ b/derive-impl/src/multihash.rs @@ -198,7 +198,7 @@ fn error_code_duplicates(hashes: &[Hash]) { struct ParseError(Span); pub fn multihash(s: Structure) -> TokenStream { - let mh_crate = match utils::use_crate("multihash") { + let mh_crate = match utils::use_crate("multihash-derive") { Ok(ident) => ident, Err(e) => { let err = syn::Error::new(Span::call_site(), e).to_compile_error(); @@ -221,7 +221,7 @@ pub fn multihash(s: Structure) -> TokenStream { quote! { /// A Multihash with the same allocated size as the Multihashes produces by this derive. - pub type Multihash = #mh_crate::MultihashGeneric<#alloc_size>; + pub type Multihash = #mh_crate::Multihash<#alloc_size>; impl #mh_crate::MultihashDigest<#alloc_size> for #code_enum { fn digest(&self, input: &[u8]) -> Multihash { @@ -247,125 +247,14 @@ pub fn multihash(s: Structure) -> TokenStream { } impl core::convert::TryFrom for #code_enum { - type Error = #mh_crate::Error; + type Error = #mh_crate::UnsupportedCode; fn try_from(code: u64) -> Result { match code { #(#code_from_u64,)* - _ => Err(#mh_crate::Error::UnsupportedCode(code)) + _ => Err(#mh_crate::UnsupportedCode(code)) } } } } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_multihash_derive() { - let input = quote! { - #[derive(Clone, Multihash)] - #[mh(alloc_size = 32)] - pub enum Code { - #[mh(code = multihash::IDENTITY, hasher = multihash::Identity256)] - Identity256, - /// Multihash array for hash function. - #[mh(code = 0x38b64f, hasher = multihash::Strobe256)] - Strobe256, - } - }; - let expected = quote! { - /// A Multihash with the same allocated size as the Multihashes produces by this derive. - pub type Multihash = multihash::MultihashGeneric<32>; - - impl multihash::MultihashDigest<32> for Code { - fn digest(&self, input: &[u8]) -> Multihash { - use multihash::Hasher; - match self { - Self::Identity256 => { - let mut hasher = multihash::Identity256::default(); - hasher.update(input); - Multihash::wrap(multihash::IDENTITY, hasher.finalize()).unwrap() - }, - Self::Strobe256 => { - let mut hasher = multihash::Strobe256::default(); - hasher.update(input); - Multihash::wrap(0x38b64f, hasher.finalize()).unwrap() - }, - _ => unreachable!(), - } - } - - fn wrap(&self, digest: &[u8]) -> Result { - Multihash::wrap((*self).into(), digest) - } - } - - impl From for u64 { - fn from(code: Code) -> Self { - match code { - Code::Identity256 => multihash::IDENTITY, - Code::Strobe256 => 0x38b64f, - _ => unreachable!(), - } - } - } - - impl core::convert::TryFrom for Code { - type Error = multihash::Error; - - fn try_from(code: u64) -> Result { - match code { - multihash::IDENTITY => Ok(Self::Identity256), - 0x38b64f => Ok(Self::Strobe256), - _ => Err(multihash::Error::UnsupportedCode(code)) - } - } - } - }; - let derive_input = syn::parse2(input).unwrap(); - let s = Structure::new(&derive_input); - let result = multihash(s); - utils::assert_proc_macro(result, expected); - } - - #[test] - #[should_panic( - expected = "the #mh(code) attribute `multihash :: SHA2_256` is defined multiple times" - )] - fn test_multihash_error_code_duplicates() { - let input = quote! { - #[derive(Clone, Multihash)] - #[mh(alloc_size = 64)] - pub enum Multihash { - #[mh(code = multihash::SHA2_256, hasher = multihash::Sha2_256)] - Identity256, - #[mh(code = multihash::SHA2_256, hasher = multihash::Sha2_256)] - Identity256, - } - }; - let derive_input = syn::parse2(input).unwrap(); - let s = Structure::new(&derive_input); - multihash(s); - } - - #[test] - #[should_panic(expected = "the #mh(code) attribute `0x14` is defined multiple times")] - fn test_multihash_error_code_duplicates_numbers() { - let input = quote! { - #[derive(Clone, Multihash)] - #[mh(alloc_size = 32)] - pub enum Code { - #[mh(code = 0x14, hasher = multihash::Sha2_256)] - Identity256, - #[mh(code = 0x14, hasher = multihash::Sha2_256)] - Identity256, - } - }; - let derive_input = syn::parse2(input).unwrap(); - let s = Structure::new(&derive_input); - multihash(s); - } -} diff --git a/derive/src/utils.rs b/derive-impl/src/utils.rs similarity index 82% rename from derive/src/utils.rs rename to derive-impl/src/utils.rs index b21edb8a..bf3cf427 100644 --- a/derive/src/utils.rs +++ b/derive-impl/src/utils.rs @@ -43,13 +43,3 @@ impl Parse for Attr { }) } } - -#[cfg(test)] -pub(crate) fn assert_proc_macro( - result: proc_macro2::TokenStream, - expected: proc_macro2::TokenStream, -) { - let result = result.to_string(); - let expected = expected.to_string(); - pretty_assertions::assert_eq!(result, expected); -} diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 4f3aa927..983f072f 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -1,28 +1,20 @@ [package] name = "multihash-derive" version = "0.8.1" -authors = ["David Craven "] edition = "2018" description = "Proc macro for deriving custom multihash tables." license = "MIT" -repository = "https://github.com/multiformats/multihash" -resolver = "2" - -[lib] -proc-macro = true - -[dependencies] -proc-macro2 = { version = "1.0.24", features = ["span-locations"] } -proc-macro-crate = "~1.1.0" -proc-macro-error = "1.0.4" -quote = "1.0.7" -syn = "1.0.42" -synstructure = "0.12.4" +repository = "https://github.com/multiformats/rust-multihash" [features] default = ["std"] -std = [] +std = ["multihash/std", "core2/std"] + +[dependencies] +multihash-derive-impl = { version = "0.1.0", path = "../derive-impl" } +multihash = { version = "0.18.0", path = "../", default-features = false } +core2 = { version = "0.4.0", default-features = false } [dev-dependencies] -pretty_assertions = "1.0.0" -multihash = { path = "..", default-features = false, features = ["derive", "sha2"] } +trybuild = "1.0.80" +multihash-codetable = { path = "../codetable", features = ["strobe", "identity"] } diff --git a/src/hasher.rs b/derive/src/hasher.rs similarity index 100% rename from src/hasher.rs rename to derive/src/hasher.rs diff --git a/derive/src/lib.rs b/derive/src/lib.rs index a9c633ec..062ce43e 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,3 +1,7 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +//! A procedural macro for custom Multihash code tables. +//! //! This proc macro derives a custom Multihash code table from a list of hashers. It also //! generates a public type called `Multihash` which corresponds to the specified `alloc_size`. //! @@ -17,32 +21,79 @@ //! //! # Example //! -//! ``` -//! use multihash::derive::Multihash; -//! use multihash::MultihashDigest; +//! ```ignore : `proc-macro-crate` does not work in docs, see https://github.com/bkchr/proc-macro-crate/issues/14 +//! use multihash_derive::{Hasher, MultihashDigest}; +//! +//! struct FooHasher; //! -//! #[derive(Clone, Copy, Debug, Eq, Multihash, PartialEq)] +//! impl Hasher for FooHasher { +//! // Implement hasher ... +//! # fn update(&mut self, input: &[u8]) { +//! # +//! # } +//! # +//! # fn finalize(&mut self) -> &[u8] { +//! # &[] +//! # } +//! # +//! # fn reset(&mut self) { +//! # +//! # } +//! } +//! +//! #[derive(Clone, Copy, Debug, Eq, MultihashDigest, PartialEq)] //! #[mh(alloc_size = 64)] //! pub enum Code { -//! #[mh(code = 0x01, hasher = multihash::Sha2_256)] -//! Foo, -//! #[mh(code = 0x02, hasher = multihash::Sha2_512)] -//! Bar, +//! #[mh(code = 0x01, hasher = FooHasher)] +//! Foo //! } //! //! let hash = Code::Foo.digest(b"hello world!"); +//! //! println!("{:02x?}", hash); //! ``` -extern crate proc_macro; -mod multihash; -mod utils; +mod hasher; + +use core::convert::TryFrom; +use core::fmt; + +pub use hasher::Hasher; +pub use multihash::Error; +pub use multihash::Multihash; +#[doc(inline)] +pub use multihash_derive_impl::Multihash; // This one is deprecated. +pub use multihash_derive_impl::MultihashDigest; + +/// The given code is not supported by this codetable. +#[derive(Debug)] +pub struct UnsupportedCode(pub u64); + +impl fmt::Display for UnsupportedCode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "the code {} is not supported by this codetable", self.0) + } +} + +impl core2::error::Error for UnsupportedCode {} -use proc_macro::TokenStream; -use proc_macro_error::proc_macro_error; -use synstructure::{decl_derive, Structure}; +/// Trait that implements hashing. +/// +/// Typically, you won't implement this yourself but use the [`MultihashDigest`](multihash_derive_impl::MultihashDigest) custom-derive. +pub trait MultihashDigest: + TryFrom + + Into + + Send + + Sync + + Unpin + + Copy + + Eq + + fmt::Debug + + 'static +{ + /// Calculate the hash of some input data. + fn digest(&self, input: &[u8]) -> Multihash; -decl_derive!([Multihash, attributes(mh)] => #[proc_macro_error] multihash); -fn multihash(s: Structure) -> TokenStream { - multihash::multihash(s).into() + /// Create a multihash from an existing multihash digest. + fn wrap(&self, digest: &[u8]) -> Result, Error>; } diff --git a/derive/tests/fail/no_allow_same_code_twice.rs b/derive/tests/fail/no_allow_same_code_twice.rs new file mode 100644 index 00000000..866ecaa7 --- /dev/null +++ b/derive/tests/fail/no_allow_same_code_twice.rs @@ -0,0 +1,27 @@ +#[derive(Default)] +struct FooHasher { + +} + +impl multihash_derive::Hasher for FooHasher { + fn update(&mut self, input: &[u8]) { } + + fn finalize(&mut self) -> &[u8] { + todo!() + } + + fn reset(&mut self) { } +} + +#[derive(Clone, Debug, Eq, PartialEq, Copy, multihash_derive::MultihashDigest)] +#[mh(alloc_size = 32)] +pub enum Code { + #[mh(code = 0x0, hasher = FooHasher)] + Foo1, + #[mh(code = 0x0, hasher = FooHasher)] + Foo2, +} + +fn main() { + +} diff --git a/derive/tests/fail/no_allow_same_code_twice.stderr b/derive/tests/fail/no_allow_same_code_twice.stderr new file mode 100644 index 00000000..b4688a76 --- /dev/null +++ b/derive/tests/fail/no_allow_same_code_twice.stderr @@ -0,0 +1,8 @@ +error: the #mh(code) attribute `0x0` is defined multiple times + + = note: previous definition of `0x0` at line 0 + + --> tests/fail/no_allow_same_code_twice.rs:21:17 + | +21 | #[mh(code = 0x0, hasher = FooHasher)] + | ^^^ diff --git a/derive/tests/fail/no_allow_same_name_twice.rs b/derive/tests/fail/no_allow_same_name_twice.rs new file mode 100644 index 00000000..73a89858 --- /dev/null +++ b/derive/tests/fail/no_allow_same_name_twice.rs @@ -0,0 +1,27 @@ +#[derive(Default)] +struct FooHasher { + +} + +impl multihash_derive::Hasher for FooHasher { + fn update(&mut self, input: &[u8]) { } + + fn finalize(&mut self) -> &[u8] { + todo!() + } + + fn reset(&mut self) { } +} + +#[derive(Clone, Debug, Eq, PartialEq, Copy, multihash_derive::MultihashDigest)] +#[mh(alloc_size = 32)] +pub enum Code { + #[mh(code = 0x0, hasher = FooHasher)] + Foo, + #[mh(code = 0x1, hasher = FooHasher)] + Foo, +} + +fn main() { + +} diff --git a/derive/tests/fail/no_allow_same_name_twice.stderr b/derive/tests/fail/no_allow_same_name_twice.stderr new file mode 100644 index 00000000..9ba4e34b --- /dev/null +++ b/derive/tests/fail/no_allow_same_name_twice.stderr @@ -0,0 +1,10 @@ +error[E0428]: the name `Foo` is defined multiple times + --> tests/fail/no_allow_same_name_twice.rs:22:5 + | +20 | Foo, + | --- previous definition of the type `Foo` here +21 | #[mh(code = 0x1, hasher = FooHasher)] +22 | Foo, + | ^^^ `Foo` redefined here + | + = note: `Foo` must be defined only once in the type namespace of this enum diff --git a/derive/tests/multihash.rs b/derive/tests/multihash.rs new file mode 100644 index 00000000..f723ef5e --- /dev/null +++ b/derive/tests/multihash.rs @@ -0,0 +1,29 @@ +use multihash_derive::{Hasher, MultihashDigest}; + +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.pass("tests/pass/*.rs"); + t.compile_fail("tests/fail/*.rs"); +} + +#[test] +fn uses_correct_hasher() { + #[derive(Clone, Debug, Eq, PartialEq, Copy, MultihashDigest)] + #[mh(alloc_size = 32)] + pub enum Code { + /// Multihash array for hash function. + #[mh(code = 0x38b64f, hasher = multihash_codetable::Strobe256)] + Strobe256, + } + + let multihash1 = Code::Strobe256.digest(b"foobar"); + + let mut hasher = multihash_codetable::Strobe256::default(); + hasher.update(b"foobar"); + let digest = hasher.finalize(); + + let multihash2 = Multihash::wrap(0x38b64f, digest).unwrap(); + + assert_eq!(multihash1, multihash2) +} diff --git a/derive/tests/pass/derive.rs b/derive/tests/pass/derive.rs new file mode 100644 index 00000000..48818b74 --- /dev/null +++ b/derive/tests/pass/derive.rs @@ -0,0 +1,20 @@ +use multihash_derive::MultihashDigest; + +#[derive(Clone, Debug, Eq, PartialEq, Copy, MultihashDigest)] +#[mh(alloc_size = 32)] +pub enum Code { + #[mh(code = 0x0, hasher = multihash_codetable::Identity256)] + Identity256, + /// Multihash array for hash function. + #[mh(code = 0x38b64f, hasher = multihash_codetable::Strobe256)] + Strobe256, +} + +fn main() { + assert_multihash_size_32(Code::Identity256.digest(&[])); + assert_multihash_size_32(Code::Strobe256.digest(&[])); +} + +fn assert_multihash_size_32(_mh: multihash_derive::Multihash<32>) { + +} diff --git a/src/arb.rs b/src/arb.rs index 2768a778..c279484a 100644 --- a/src/arb.rs +++ b/src/arb.rs @@ -4,13 +4,12 @@ use rand::{ Rng, RngCore, SeedableRng, }; +use crate::Multihash; use arbitrary::{size_hint, Unstructured}; -use crate::MultihashGeneric; - /// Generates a random valid multihash. -impl quickcheck::Arbitrary for MultihashGeneric { - fn arbitrary(g: &mut Gen) -> MultihashGeneric { +impl quickcheck::Arbitrary for Multihash { + fn arbitrary(g: &mut Gen) -> Multihash { // In real world lower multihash codes are more likely to happen, hence distribute them // with bias towards smaller values. let weights = [128, 64, 32, 16, 8, 4, 2, 1]; @@ -32,11 +31,11 @@ impl quickcheck::Arbitrary for MultihashGeneric { let size = rng.gen_range(0..S); let mut data = [0; S]; rng.fill_bytes(&mut data); - MultihashGeneric::wrap(code, &data[..size]).unwrap() + Multihash::wrap(code, &data[..size]).unwrap() } } -impl<'a, const S: usize> arbitrary::Arbitrary<'a> for MultihashGeneric { +impl<'a, const S: usize> arbitrary::Arbitrary<'a> for Multihash { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { let mut code = 0u64; let mut len_choice = u.arbitrary::()? | 1; @@ -59,7 +58,7 @@ impl<'a, const S: usize> arbitrary::Arbitrary<'a> for MultihashGeneric { let size = u.int_in_range(0..=S)?; let data = u.bytes(size)?; - Ok(MultihashGeneric::wrap(code, data).unwrap()) + Ok(Multihash::wrap(code, data).unwrap()) } fn size_hint(depth: usize) -> (usize, Option) { @@ -69,15 +68,15 @@ impl<'a, const S: usize> arbitrary::Arbitrary<'a> for MultihashGeneric { #[cfg(test)] mod tests { - use crate::MultihashGeneric; + use crate::Multihash; use arbitrary::{Arbitrary, Unstructured}; #[test] fn arbitrary() { let mut u = Unstructured::new(&[2, 4, 13, 5, 6, 7, 8, 9, 6]); - let mh = as Arbitrary>::arbitrary(&mut u).unwrap(); - let mh2 = MultihashGeneric::<16>::wrap(1037, &[6, 7, 8, 9, 6]).unwrap(); + let mh = as Arbitrary>::arbitrary(&mut u).unwrap(); + let mh2 = Multihash::<16>::wrap(1037, &[6, 7, 8, 9, 6]).unwrap(); assert_eq!(mh, mh2); } } diff --git a/src/error.rs b/src/error.rs index ddf25fbd..61dc29a9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,54 +1,87 @@ #[cfg(not(feature = "std"))] -use core2::{error::Error as StdError, io::Error as IoError}; +use core2::{error::Error as StdError, io}; #[cfg(feature = "std")] -use std::{error::Error as StdError, io::Error as IoError}; +use std::{error::Error as StdError, io}; -use unsigned_varint::decode::Error as DecodeError; -#[cfg(feature = "std")] -use unsigned_varint::io::ReadError; +use unsigned_varint::decode; -/// Multihash error. +/// Opaque error struct for operations involving a [`Multihash`](crate::Multihash). #[derive(Debug)] -pub enum Error { +pub struct Error { + kind: Kind, +} + +impl Error { + pub(crate) const fn invalid_size(size: u64) -> Self { + Self { + kind: Kind::InvalidSize(size), + } + } + + #[cfg(not(feature = "std"))] + pub(crate) const fn insufficient_varint_bytes() -> Self { + Self { + kind: Kind::Varint(decode::Error::Insufficient), + } + } + + #[cfg(not(feature = "std"))] + pub(crate) const fn varint_overflow() -> Self { + Self { + kind: Kind::Varint(decode::Error::Overflow), + } + } +} + +impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + self.kind.fmt(f) + } +} + +#[derive(Debug)] +enum Kind { /// Io error. - Io(IoError), - /// Unsupported multihash code. - UnsupportedCode(u64), + Io(io::Error), /// Invalid multihash size. InvalidSize(u64), /// Invalid varint. - Varint(DecodeError), + Varint(decode::Error), } -impl core::fmt::Display for Error { +#[cfg(feature = "std")] +pub(crate) fn unsigned_variant_to_multihash_error(err: unsigned_varint::io::ReadError) -> Error { + match err { + unsigned_varint::io::ReadError::Io(err) => io_to_multihash_error(err), + unsigned_varint::io::ReadError::Decode(err) => Error { + kind: Kind::Varint(err), + }, + _ => unreachable!(), + } +} + +pub(crate) fn io_to_multihash_error(err: io::Error) -> Error { + Error { + kind: Kind::Io(err), + } +} + +impl core::fmt::Display for Kind { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { match self { Self::Io(err) => write!(f, "{err}"), - Self::UnsupportedCode(code) => write!(f, "Unsupported multihash code {code}."), Self::InvalidSize(size) => write!(f, "Invalid multihash size {size}."), Self::Varint(err) => write!(f, "{err}"), } } } -impl StdError for Error {} - -impl From for Error { - fn from(err: IoError) -> Self { - Self::Io(err) - } -} - -#[cfg(feature = "std")] -impl From for Error { - fn from(err: ReadError) -> Self { - match err { - ReadError::Io(err) => Self::Io(err), - ReadError::Decode(err) => Self::Varint(err), - _ => unreachable!(), +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match &self.kind { + Kind::Io(inner) => Some(inner), + Kind::InvalidSize(_) => None, + Kind::Varint(_) => None, // FIXME: Does not implement `core2::Error`. } } } - -/// Multihash result. -pub type Result = core::result::Result; diff --git a/src/lib.rs b/src/lib.rs index d561c27b..3c2b53e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,42 +1,12 @@ -//! Multihash implementation. +//! Bare-minimum multihash data structure. //! -//! Feature Flags -//! ------------- +//! This crate defines a `no_std` compatible data structures for representing a `Multihash`. //! -//! Multihash has lots of [feature flags], by default a table with cryptographically secure hashers -//! is created. +//! It does not offer any hashing, instead you are encouraged to either do the hashing yourself. +//! Alternatively, you can use an existing code table or make your own code table. //! -//! Some of the features are about specific hash functions, these are ("default" marks the hashers -//! that are enabled by default): -//! -//! - `blake2b`: (default) Enable Blake2b hashers -//! - `blake2s`: (default) Enable Blake2s hashers -//! - `identity`: Enable the Identity hashers (using it is discouraged as it's not a hash function -//! in the sense that it produces a fixed sized output independent of the input size) -//! - `sha1`: Enable SHA-1 hasher -//! - `sha2`: (default) Enable SHA-2 hashers -//! - `sha3`: (default) Enable SHA-3 hashers -//! - `strobe`: Enable Strobe hashers -//! -//! In order to enable all cryptographically secure hashers, you can set the `secure-hashes` -//! feature flag (enabled by default). -//! -//! The library has support for `no_std`, if you disable the `std` feature flag. -//! -//! The `multihash-impl` feature flag (enabled by default) enables a default Multihash -//! implementation that contains some of the bundled hashers. If you want a different set of hash -//! algorithms you can change this with enabled the corresponding features. -//! -//! For example if you only need SHA2 hasher, you could set the features in the `multihash` -//! dependency like this: -//! -//! ```toml -//! multihash = { version = …, default-features = false, features = ["std", "multihash-impl", "sha2"] } -//! ``` -//! -//! If you want to customize your code table even more, for example you want only one specific hash -//! digest size and not whole family, you would only enable the `derive` feature (enabled by -//! default), which enables the [`Multihash` derive], together with the hashers you want. +//! The [`multihash-codetable`] crate defines a set of hashes to get started quickly. +//! To make your own codetable, use the [`multihash-derive`] crate. //! //! The `arb` feature flag enables the quickcheck arbitrary implementation for property based //! testing. @@ -44,10 +14,10 @@ //! For serializing the multihash there is support for [Serde] via the `serde-codec` feature and //! the [SCALE Codec] via the `scale-codec` feature. //! -//! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section -//! [`Multihash` derive]: crate::derive //! [Serde]: https://serde.rs //! [SCALE Codec]: https://github.com/paritytech/parity-scale-codec +//! [`multihash-derive`]: https://docs.rs/multihash-derive +//! [`multihash-codetable`]: https://docs.rs/multihash-codetable #![deny(missing_docs, unsafe_code)] #![cfg_attr(not(feature = "std"), no_std)] @@ -58,37 +28,15 @@ extern crate alloc; #[cfg(any(test, feature = "arb"))] mod arb; mod error; -mod hasher; -mod hasher_impl; mod multihash; -#[cfg(feature = "multihash-impl")] -mod multihash_impl; -pub use crate::error::{Error, Result}; -pub use crate::hasher::Hasher; -pub use crate::multihash::{Multihash as MultihashGeneric, MultihashDigest}; -#[cfg(feature = "derive")] -pub use multihash_derive as derive; +/// Multihash result. +#[deprecated(note = "Use `Result` instead")] +pub type Result = core::result::Result; -#[cfg(feature = "multihash-impl")] -pub use crate::multihash_impl::{Code, Multihash}; +pub use crate::error::Error; +pub use crate::multihash::Multihash; -#[cfg(feature = "blake2b")] -pub use crate::hasher_impl::blake2b::{Blake2b256, Blake2b512, Blake2bHasher}; -#[cfg(feature = "blake2s")] -pub use crate::hasher_impl::blake2s::{Blake2s128, Blake2s256, Blake2sHasher}; -#[cfg(feature = "blake3")] -pub use crate::hasher_impl::blake3::{Blake3Hasher, Blake3_256}; -pub use crate::hasher_impl::identity::{Identity256, IdentityHasher}; -#[cfg(feature = "ripemd")] -pub use crate::hasher_impl::ripemd::{Ripemd160, Ripemd256, Ripemd320}; -#[cfg(feature = "sha1")] -pub use crate::hasher_impl::sha1::Sha1; -#[cfg(feature = "sha2")] -pub use crate::hasher_impl::sha2::{Sha2_256, Sha2_512}; -#[cfg(feature = "sha3")] -pub use crate::hasher_impl::sha3::{Keccak224, Keccak256, Keccak384, Keccak512}; -#[cfg(feature = "sha3")] -pub use crate::hasher_impl::sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512}; -#[cfg(feature = "strobe")] -pub use crate::hasher_impl::strobe::{Strobe256, Strobe512, StrobeHasher}; +/// Deprecated type-alias for the [`Multihash`] type. +#[deprecated(since = "0.18.0", note = "Use `multihash::Multihash instead.")] +pub type MultihashGeneric = Multihash; diff --git a/src/multihash.rs b/src/multihash.rs index e9aac9c5..cd2b3ab0 100644 --- a/src/multihash.rs +++ b/src/multihash.rs @@ -1,12 +1,9 @@ use crate::Error; #[cfg(feature = "alloc")] use alloc::vec::Vec; -use core::convert::TryFrom; use core::convert::TryInto; use core::fmt::Debug; -#[cfg(feature = "serde-codec")] -use serde_big_array::BigArray; use unsigned_varint::encode as varint_encode; @@ -16,42 +13,6 @@ use std::io; #[cfg(not(feature = "std"))] use core2::io; -/// Trait that implements hashing. -/// -/// It is usually implemented by a custom code table enum that derives the [`Multihash` derive]. -/// -/// [`Multihash` derive]: crate::derive -pub trait MultihashDigest: - TryFrom + Into + Send + Sync + Unpin + Copy + Eq + Debug + 'static -{ - /// Calculate the hash of some input data. - /// - /// # Example - /// - /// ``` - /// // `Code` implements `MultihashDigest` - /// use multihash::{Code, MultihashDigest}; - /// - /// let hash = Code::Sha3_256.digest(b"Hello world!"); - /// println!("{:02x?}", hash); - /// ``` - fn digest(&self, input: &[u8]) -> Multihash; - - /// Create a multihash from an existing multihash digest. - /// - /// # Example - /// - /// ``` - /// use multihash::{Code, Hasher, MultihashDigest, Sha3_256}; - /// - /// let mut hasher = Sha3_256::default(); - /// hasher.update(b"Hello world!"); - /// let hash = Code::Sha3_256.wrap(&hasher.finalize()).unwrap(); - /// println!("{:02x?}", hash); - /// ``` - fn wrap(&self, digest: &[u8]) -> Result, Error>; -} - /// A Multihash instance that only supports the basic functionality and no hashing. /// /// With this Multihash implementation you can operate on Multihashes in a generic way, but @@ -68,13 +29,13 @@ pub trait MultihashDigest: /// 0x76, 0x22, 0xf3, 0xca, 0x71, 0xfb, 0xa1, 0xd9, 0x72, 0xfd, 0x94, 0xa3, 0x1c, 0x3b, 0xfb, /// 0xf2, 0x4e, 0x39, 0x38, /// ]; -/// let mh = Multihash::from_bytes(&digest_bytes).unwrap(); +/// let mh = Multihash::<32>::from_bytes(&digest_bytes).unwrap(); /// assert_eq!(mh.code(), Sha3_256); /// assert_eq!(mh.size(), 32); /// assert_eq!(mh.digest(), &digest_bytes[2..]); /// ``` -#[cfg_attr(feature = "serde-codec", derive(serde::Deserialize))] -#[cfg_attr(feature = "serde-codec", derive(serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Clone, Copy, Debug, Eq, Ord, PartialOrd)] pub struct Multihash { /// The code of the Multihash. @@ -82,7 +43,7 @@ pub struct Multihash { /// The actual size of the digest in bytes (not the allocated size). size: u8, /// The digest. - #[cfg_attr(feature = "serde-codec", serde(with = "BigArray"))] + #[cfg_attr(feature = "serde", serde(with = "serde_big_array::BigArray"))] digest: [u8; S], } @@ -100,7 +61,7 @@ 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::InvalidSize(input_digest.len() as _)); + return Err(Error::invalid_size(input_digest.len() as _)); } let size = input_digest.len(); let mut digest = [0; S]; @@ -151,7 +112,7 @@ impl Multihash { let result = Self::read(&mut bytes)?; // There were more bytes supplied than read if !bytes.is_empty() { - return Err(Error::InvalidSize(bytes.len().try_into().expect( + return Err(Error::invalid_size(bytes.len().try_into().expect( "Currently the maximum size is 255, therefore always fits into usize", ))); } @@ -190,31 +151,19 @@ impl Multihash { /// is secure (cryptographically) to use. /// /// If the new size is larger than the current size, this method does nothing. - /// - /// ``` - /// use multihash::{Code, MultihashDigest}; - /// - /// let hash = Code::Sha3_256.digest(b"Hello world!").truncate(20); - /// ``` pub fn truncate(&self, size: u8) -> Self { let mut mh = *self; mh.size = mh.size.min(size); mh } - /// Resizes the backing multihash buffer. This function fails if the hash digest is larger than - /// the target size. + /// Resizes the backing multihash buffer. /// - /// ``` - /// use multihash::{Code, MultihashDigest, MultihashGeneric}; - /// - /// let hash = Code::Sha3_256.digest(b"Hello world!"); - /// let large_hash: MultihashGeneric<32> = hash.resize().unwrap(); - /// ``` + /// This function fails if the hash digest is larger than the target size. pub fn resize(&self) -> Result, Error> { let size = self.size as usize; if size > R { - return Err(Error::InvalidSize(self.size as u64)); + return Err(Error::invalid_size(self.size as u64)); } let mut mh = Multihash { code: self.code, @@ -227,22 +176,7 @@ impl Multihash { /// Decomposes struct, useful when needing a `Sized` array or moving all the data into another type /// - /// It is recommended to use `digest()` `code()` and `size()` for most cases - /// - /// ``` - /// use multihash::{Code, MultihashDigest}; - /// struct Foo { - /// arr: [u8; S], - /// len: usize, - /// } - /// - /// let hash = Code::Sha3_256.digest(b"Hello world!"); - /// let (.., arr, size) = hash.into_inner(); - /// let foo = Foo { - /// arr, - /// len: size as usize, - /// }; - /// ``` + /// It is recommended to use `digest()` `code()` and `size()` for most cases. pub fn into_inner(self) -> (u64, [u8; S], u8) { let Self { code, digest, size } = self; (code, digest, size) @@ -250,7 +184,7 @@ impl Multihash { } // Don't hash the whole allocated space, but just the actual digest -#[allow(clippy::derive_hash_xor_eq)] +#[allow(clippy::derived_hash_with_manual_eq)] impl core::hash::Hash for Multihash { fn hash(&self, state: &mut T) { self.code.hash(state); @@ -309,7 +243,7 @@ impl parity_scale_codec::Decode for Multihash { } /// Writes the multihash to a byte stream. -pub fn write_multihash(mut w: W, code: u64, size: u8, digest: &[u8]) -> Result +fn write_multihash(mut w: W, code: u64, size: u8, digest: &[u8]) -> Result where W: io::Write, { @@ -321,9 +255,12 @@ where let written = code.len() + size.len() + digest.len(); - w.write_all(code)?; - w.write_all(size)?; - w.write_all(digest)?; + w.write_all(code) + .map_err(crate::error::io_to_multihash_error)?; + w.write_all(size) + .map_err(crate::error::io_to_multihash_error)?; + w.write_all(digest) + .map_err(crate::error::io_to_multihash_error)?; Ok(written) } @@ -334,7 +271,7 @@ where /// maximum/allocated size of the digest. /// /// Currently the maximum size for a digest is 255 bytes. -pub fn read_multihash(mut r: R) -> Result<(u64, u8, [u8; S]), Error> +fn read_multihash(mut r: R) -> Result<(u64, u8, [u8; S]), Error> where R: io::Read, { @@ -342,16 +279,19 @@ where let size = read_u64(&mut r)?; if size > S as u64 || size > u8::MAX as u64 { - return Err(Error::InvalidSize(size)); + return Err(Error::invalid_size(size)); } let mut digest = [0; S]; - r.read_exact(&mut digest[..size as usize])?; + r.read_exact(&mut digest[..size as usize]) + .map_err(crate::error::io_to_multihash_error)?; Ok((code, size as u8, digest)) } #[cfg(feature = "std")] -pub(crate) use unsigned_varint::io::read_u64; +pub(crate) fn read_u64(r: R) -> Result { + unsigned_varint::io::read_u64(r).map_err(crate::error::unsigned_variant_to_multihash_error) +} /// Reads 64 bits from a byte array into a u64 /// Adapted from unsigned-varint's generated read_u64 function at @@ -361,76 +301,35 @@ pub(crate) fn read_u64(mut r: R) -> Result { use unsigned_varint::decode; let mut b = varint_encode::u64_buffer(); for i in 0..b.len() { - let n = r.read(&mut (b[i..i + 1]))?; + let n = r + .read(&mut (b[i..i + 1])) + .map_err(crate::error::io_to_multihash_error)?; if n == 0 { - return Err(Error::Varint(decode::Error::Insufficient)); + return Err(Error::insufficient_varint_bytes()); } else if decode::is_last(b[i]) { return Ok(decode::u64(&b[..=i]).unwrap().0); } } - Err(Error::Varint(decode::Error::Overflow)) + Err(Error::varint_overflow()) } #[cfg(test)] mod tests { use super::*; - use crate::multihash_impl::Code; - - #[test] - fn roundtrip() { - let hash = Code::Sha2_256.digest(b"hello world"); - let mut buf = [0u8; 35]; - let written = hash.write(&mut buf[..]).unwrap(); - let hash2 = Multihash::<32>::read(&buf[..]).unwrap(); - assert_eq!(hash, hash2); - assert_eq!(hash.encoded_len(), written); - } - - #[test] - fn test_truncate_down() { - let hash = Code::Sha2_256.digest(b"hello world"); - let small = hash.truncate(20); - assert_eq!(small.size(), 20); - } - - #[test] - fn test_truncate_up() { - let hash = Code::Sha2_256.digest(b"hello world"); - let small = hash.truncate(100); - assert_eq!(small.size(), 32); - } - - #[test] - fn test_resize_fits() { - let hash = Code::Sha2_256.digest(b"hello world"); - let _: Multihash<32> = hash.resize().unwrap(); - } - - #[test] - fn test_resize_up() { - let hash = Code::Sha2_256.digest(b"hello world"); - let _: Multihash<100> = hash.resize().unwrap(); - } - - #[test] - fn test_resize_truncate() { - let hash = Code::Sha2_256.digest(b"hello world"); - hash.resize::<20>().unwrap_err(); - } #[test] #[cfg(feature = "scale-codec")] fn test_scale() { use parity_scale_codec::{Decode, Encode}; - let mh1 = Code::Sha2_256.digest(b"hello world"); + let mh1 = Multihash::<32>::wrap(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> = Code::Sha2_256.digest(b"hello world"); + let mh3 = Multihash::<64>::wrap(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)); @@ -441,7 +340,7 @@ mod tests { } #[test] - #[cfg(feature = "serde-codec")] + #[cfg(feature = "serde")] fn test_serde() { let mh = Multihash::<32>::default(); let bytes = serde_json::to_string(&mh).unwrap();