tiny_multihash/
multihash.rs

1use crate::hasher::{Digest, Size};
2use crate::Error;
3use core::convert::TryFrom;
4#[cfg(feature = "std")]
5use core::convert::TryInto;
6use core::fmt::Debug;
7use generic_array::{ArrayLength, GenericArray};
8
9/// Trait that implements hashing.
10///
11/// It is usually implemented by a custom code table enum that derives the [`Multihash` derive].
12///
13/// [`Multihash` derive]: crate::derive
14pub trait MultihashCode:
15    TryFrom<u64> + Into<u64> + Send + Sync + Unpin + Copy + Eq + Debug + 'static
16{
17    /// The maximum size a hash will allocate.
18    type AllocSize: Size;
19
20    /// Calculate the hash of some input data.
21    ///
22    /// # Example
23    ///
24    /// ```
25    /// // `Code` implements `MultihashCode`
26    /// use tiny_multihash::{Code, MultihashCode};
27    ///
28    /// let hash = Code::Sha3_256.digest(b"Hello world!");
29    /// println!("{:02x?}", hash);
30    /// ```
31    fn digest(&self, input: &[u8]) -> Multihash<Self::AllocSize>;
32
33    /// Create a multihash from an existing [`Digest`].
34    ///
35    /// # Example
36    ///
37    /// ```
38    /// use tiny_multihash::{Code, MultihashCode, Sha3_256, StatefulHasher};
39    ///
40    /// let mut hasher = Sha3_256::default();
41    /// hasher.update(b"Hello world!");
42    /// let hash = Code::multihash_from_digest(&hasher.finalize());
43    /// println!("{:02x?}", hash);
44    /// ```
45    fn multihash_from_digest<'a, S, D>(digest: &'a D) -> Multihash<Self::AllocSize>
46    where
47        S: Size,
48        D: Digest<S>,
49        Self: From<&'a D>;
50}
51
52/// A Multihash instance that only supports the basic functionality and no hashing.
53///
54/// With this Multihash implementation you can operate on Multihashes in a generic way, but
55/// no hasher implementation is associated with the code.
56///
57/// # Example
58///
59/// ```
60/// use tiny_multihash::{Multihash, U64};
61///
62/// const Sha3_256: u64 = 0x16;
63/// let digest_bytes = [
64///     0x16, 0x20, 0x64, 0x4b, 0xcc, 0x7e, 0x56, 0x43, 0x73, 0x04, 0x09, 0x99, 0xaa, 0xc8, 0x9e,
65///     0x76, 0x22, 0xf3, 0xca, 0x71, 0xfb, 0xa1, 0xd9, 0x72, 0xfd, 0x94, 0xa3, 0x1c, 0x3b, 0xfb,
66///     0xf2, 0x4e, 0x39, 0x38,
67/// ];
68/// let mh = Multihash::<U64>::from_bytes(&digest_bytes).unwrap();
69/// assert_eq!(mh.code(), Sha3_256);
70/// assert_eq!(mh.size(), 32);
71/// assert_eq!(mh.digest(), &digest_bytes[2..]);
72/// ```
73#[cfg_attr(feature = "serde-codec", derive(serde::Deserialize))]
74#[cfg_attr(feature = "serde-codec", derive(serde::Serialize))]
75#[cfg_attr(feature = "serde-codec", serde(bound = "S: Size"))]
76#[derive(Clone, Debug, Default, Eq, PartialEq)]
77pub struct Multihash<S: Size> {
78    /// The code of the Multihash.
79    code: u64,
80    /// The actual size of the digest in bytes (not the allocated size).
81    size: u8,
82    /// The digest.
83    digest: GenericArray<u8, S>,
84}
85
86impl<S: Size> Copy for Multihash<S> where <S as ArrayLength<u8>>::ArrayType: Copy {}
87
88impl<S: Size> Multihash<S> {
89    /// Wraps the digest in a multihash.
90    pub fn wrap(code: u64, input_digest: &[u8]) -> Result<Self, Error> {
91        if input_digest.len() > S::to_usize() {
92            return Err(Error::InvalidSize(input_digest.len() as _));
93        }
94        let size = input_digest.len();
95        let mut digest = GenericArray::default();
96        digest[..size].copy_from_slice(input_digest);
97        Ok(Self {
98            code,
99            size: size as u8,
100            digest,
101        })
102    }
103
104    /// Returns the code of the multihash.
105    pub fn code(&self) -> u64 {
106        self.code
107    }
108
109    /// Returns the size of the digest.
110    pub fn size(&self) -> u8 {
111        self.size
112    }
113
114    /// Returns the digest.
115    pub fn digest(&self) -> &[u8] {
116        &self.digest[..self.size as usize]
117    }
118
119    /// Reads a multihash from a byte stream.
120    #[cfg(feature = "std")]
121    pub fn read<R: std::io::Read>(r: R) -> Result<Self, Error>
122    where
123        Self: Sized,
124    {
125        let (code, size, digest) = read_multihash(r)?;
126        Ok(Self { code, size, digest })
127    }
128
129    /// Parses a multihash from a bytes.
130    ///
131    /// You need to make sure the passed in bytes have the correct length. The digest length
132    /// needs to match the `size` value of the multihash.
133    #[cfg(feature = "std")]
134    pub fn from_bytes(mut bytes: &[u8]) -> Result<Self, Error>
135    where
136        Self: Sized,
137    {
138        let result = Self::read(&mut bytes)?;
139        // There were more bytes supplied than read
140        if !bytes.is_empty() {
141            return Err(Error::InvalidSize(bytes.len().try_into().expect(
142                "Currently the maximum size is 255, therefore always fits into usize",
143            )));
144        }
145
146        Ok(result)
147    }
148
149    /// Writes a multihash to a byte stream.
150    #[cfg(feature = "std")]
151    pub fn write<W: std::io::Write>(&self, w: W) -> Result<(), Error> {
152        write_multihash(w, self.code(), self.size(), self.digest())
153    }
154
155    /// Returns the bytes of a multihash.
156    #[cfg(feature = "std")]
157    pub fn to_bytes(&self) -> Vec<u8> {
158        let mut bytes = vec![];
159        self.write(&mut bytes)
160            .expect("writing to a vec should never fail");
161        bytes
162    }
163}
164
165#[cfg(feature = "scale-codec")]
166impl parity_scale_codec::Encode for Multihash<crate::U32> {
167    fn encode_to<EncOut: parity_scale_codec::Output>(&self, dest: &mut EncOut) {
168        let mut digest = [0; 32];
169        digest.copy_from_slice(&self.digest);
170        dest.push(&self.code);
171        dest.push(&self.size);
172        dest.push(&digest);
173    }
174}
175
176#[cfg(feature = "scale-codec")]
177impl parity_scale_codec::EncodeLike for Multihash<crate::U32> {}
178
179#[cfg(feature = "scale-codec")]
180impl parity_scale_codec::Decode for Multihash<crate::U32> {
181    fn decode<DecIn: parity_scale_codec::Input>(
182        input: &mut DecIn,
183    ) -> Result<Self, parity_scale_codec::Error> {
184        Ok(Multihash {
185            code: parity_scale_codec::Decode::decode(input)?,
186            size: parity_scale_codec::Decode::decode(input)?,
187            digest: {
188                let digest = <[u8; 32]>::decode(input)?;
189                GenericArray::clone_from_slice(&digest)
190            },
191        })
192    }
193}
194
195#[cfg(feature = "scale-codec")]
196impl parity_scale_codec::Encode for Multihash<crate::U64> {
197    fn encode_to<EncOut: parity_scale_codec::Output>(&self, dest: &mut EncOut) {
198        let mut digest = [0; 64];
199        digest.copy_from_slice(&self.digest);
200        dest.push(&self.code);
201        dest.push(&self.size);
202        dest.push(&digest);
203    }
204}
205
206#[cfg(feature = "scale-codec")]
207impl parity_scale_codec::EncodeLike for Multihash<crate::U64> {}
208
209#[cfg(feature = "scale-codec")]
210impl parity_scale_codec::Decode for Multihash<crate::U64> {
211    fn decode<DecIn: parity_scale_codec::Input>(
212        input: &mut DecIn,
213    ) -> Result<Self, parity_scale_codec::Error> {
214        Ok(Multihash {
215            code: parity_scale_codec::Decode::decode(input)?,
216            size: parity_scale_codec::Decode::decode(input)?,
217            digest: {
218                let digest = <[u8; 64]>::decode(input)?;
219                GenericArray::clone_from_slice(&digest)
220            },
221        })
222    }
223}
224
225/// Writes the multihash to a byte stream.
226#[cfg(feature = "std")]
227pub fn write_multihash<W>(mut w: W, code: u64, size: u8, digest: &[u8]) -> Result<(), Error>
228where
229    W: std::io::Write,
230{
231    use unsigned_varint::encode as varint_encode;
232
233    let mut code_buf = varint_encode::u64_buffer();
234    let code = varint_encode::u64(code, &mut code_buf);
235
236    let mut size_buf = varint_encode::u8_buffer();
237    let size = varint_encode::u8(size, &mut size_buf);
238
239    w.write_all(code)?;
240    w.write_all(size)?;
241    w.write_all(digest)?;
242    Ok(())
243}
244
245/// Reads a multihash from a byte stream that contains a full multihash (code, size and the digest)
246///
247/// Returns the code, size and the digest. The size is the actual size and not the
248/// maximum/allocated size of the digest.
249///
250/// Currently the maximum size for a digest is 255 bytes.
251#[cfg(feature = "std")]
252pub fn read_multihash<R, S>(mut r: R) -> Result<(u64, u8, GenericArray<u8, S>), Error>
253where
254    R: std::io::Read,
255    S: Size,
256{
257    use unsigned_varint::io::read_u64;
258
259    let code = read_u64(&mut r)?;
260    let size = read_u64(&mut r)?;
261
262    if size > S::to_u64() || size > u8::MAX as u64 {
263        return Err(Error::InvalidSize(size));
264    }
265
266    let mut digest = GenericArray::default();
267    r.read_exact(&mut digest[..size as usize])?;
268    Ok((code, size as u8, digest))
269}
270
271#[cfg(test)]
272mod tests {
273    use super::*;
274    use crate::multihash_impl::Code;
275
276    #[test]
277    fn roundtrip() {
278        let hash = Code::Sha2_256.digest(b"hello world");
279        let mut buf = [0u8; 35];
280        hash.write(&mut buf[..]).unwrap();
281        let hash2 = Multihash::read(&buf[..]).unwrap();
282        assert_eq!(hash, hash2);
283    }
284
285    #[test]
286    #[cfg(feature = "scale-codec")]
287    fn test_scale() {
288        use parity_scale_codec::{Decode, Encode};
289
290        let mh = Multihash::<crate::U32>::default();
291        let bytes = mh.encode();
292        let mh2: Multihash<crate::U32> = Decode::decode(&mut &bytes[..]).unwrap();
293        assert_eq!(mh, mh2);
294    }
295
296    #[test]
297    #[cfg(feature = "serde-codec")]
298    fn test_serde() {
299        let mh = Multihash::<crate::U32>::default();
300        let bytes = serde_json::to_string(&mh).unwrap();
301        let mh2 = serde_json::from_str(&bytes).unwrap();
302        assert_eq!(mh, mh2);
303    }
304}