multihash/
multihash.rs

1use crate::Error;
2#[cfg(feature = "alloc")]
3use alloc::vec::Vec;
4
5use core::convert::TryInto;
6use core::fmt::Debug;
7
8use unsigned_varint::encode as varint_encode;
9
10#[cfg(feature = "std")]
11use std::io;
12
13#[cfg(not(feature = "std"))]
14use core2::io;
15
16/// A Multihash instance that only supports the basic functionality and no hashing.
17///
18/// With this Multihash implementation you can operate on Multihashes in a generic way, but
19/// no hasher implementation is associated with the code.
20///
21/// # Example
22///
23/// ```
24/// use multihash::Multihash;
25///
26/// const Sha3_256: u64 = 0x16;
27/// let digest_bytes = [
28///     0x16, 0x20, 0x64, 0x4b, 0xcc, 0x7e, 0x56, 0x43, 0x73, 0x04, 0x09, 0x99, 0xaa, 0xc8, 0x9e,
29///     0x76, 0x22, 0xf3, 0xca, 0x71, 0xfb, 0xa1, 0xd9, 0x72, 0xfd, 0x94, 0xa3, 0x1c, 0x3b, 0xfb,
30///     0xf2, 0x4e, 0x39, 0x38,
31/// ];
32/// let mh = Multihash::<32>::from_bytes(&digest_bytes).unwrap();
33/// assert_eq!(mh.code(), Sha3_256);
34/// assert_eq!(mh.size(), 32);
35/// assert_eq!(mh.digest(), &digest_bytes[2..]);
36/// ```
37#[derive(Clone, Copy, Debug, Eq, Ord, PartialOrd)]
38pub struct Multihash<const S: usize> {
39    /// The code of the Multihash.
40    code: u64,
41    /// The actual size of the digest in bytes (not the allocated size).
42    size: u8,
43    /// The digest.
44    digest: [u8; S],
45}
46
47impl<const S: usize> Default for Multihash<S> {
48    fn default() -> Self {
49        Self {
50            code: 0,
51            size: 0,
52            digest: [0; S],
53        }
54    }
55}
56
57impl<const S: usize> Multihash<S> {
58    /// Wraps the digest in a multihash.
59    pub const fn wrap(code: u64, input_digest: &[u8]) -> Result<Self, Error> {
60        if input_digest.len() > S {
61            return Err(Error::invalid_size(input_digest.len() as _));
62        }
63        let size = input_digest.len();
64        let mut digest = [0; S];
65        let mut i = 0;
66        while i < size {
67            digest[i] = input_digest[i];
68            i += 1;
69        }
70        Ok(Self {
71            code,
72            size: size as u8,
73            digest,
74        })
75    }
76
77    /// Returns the code of the multihash.
78    pub const fn code(&self) -> u64 {
79        self.code
80    }
81
82    /// Returns the size of the digest.
83    pub const fn size(&self) -> u8 {
84        self.size
85    }
86
87    /// Returns the digest.
88    pub fn digest(&self) -> &[u8] {
89        &self.digest[..self.size as usize]
90    }
91
92    /// Reads a multihash from a byte stream.
93    pub fn read<R: io::Read>(r: R) -> Result<Self, Error>
94    where
95        Self: Sized,
96    {
97        let (code, size, digest) = read_multihash(r)?;
98        Ok(Self { code, size, digest })
99    }
100
101    /// Parses a multihash from a bytes.
102    ///
103    /// You need to make sure the passed in bytes have the correct length. The digest length
104    /// needs to match the `size` value of the multihash.
105    pub fn from_bytes(mut bytes: &[u8]) -> Result<Self, Error>
106    where
107        Self: Sized,
108    {
109        let result = Self::read(&mut bytes)?;
110        // There were more bytes supplied than read
111        if !bytes.is_empty() {
112            return Err(Error::invalid_size(bytes.len().try_into().expect(
113                "Currently the maximum size is 255, therefore always fits into usize",
114            )));
115        }
116
117        Ok(result)
118    }
119
120    /// Writes a multihash to a byte stream, returning the written size.
121    pub fn write<W: io::Write>(&self, w: W) -> Result<usize, Error> {
122        write_multihash(w, self.code(), self.size(), self.digest())
123    }
124
125    /// Returns the length in bytes needed to encode this multihash into bytes.
126    pub fn encoded_len(&self) -> usize {
127        let mut code_buf = varint_encode::u64_buffer();
128        let code = varint_encode::u64(self.code, &mut code_buf);
129
130        let mut size_buf = varint_encode::u8_buffer();
131        let size = varint_encode::u8(self.size, &mut size_buf);
132
133        code.len() + size.len() + usize::from(self.size)
134    }
135
136    #[cfg(feature = "alloc")]
137    /// Returns the bytes of a multihash.
138    pub fn to_bytes(&self) -> Vec<u8> {
139        let mut bytes = Vec::with_capacity(self.size().into());
140        let written = self
141            .write(&mut bytes)
142            .expect("writing to a vec should never fail");
143        debug_assert_eq!(written, bytes.len());
144        bytes
145    }
146
147    /// Truncates the multihash to the given size. It's up to the caller to ensure that the new size
148    /// is secure (cryptographically) to use.
149    ///
150    /// If the new size is larger than the current size, this method does nothing.
151    pub fn truncate(&self, size: u8) -> Self {
152        let mut mh = *self;
153        mh.size = mh.size.min(size);
154        mh
155    }
156
157    /// Resizes the backing multihash buffer.
158    ///
159    /// This function fails if the hash digest is larger than the target size.
160    pub fn resize<const R: usize>(&self) -> Result<Multihash<R>, Error> {
161        let size = self.size as usize;
162        if size > R {
163            return Err(Error::invalid_size(self.size as u64));
164        }
165        let mut mh = Multihash {
166            code: self.code,
167            size: self.size,
168            digest: [0; R],
169        };
170        mh.digest[..size].copy_from_slice(&self.digest[..size]);
171        Ok(mh)
172    }
173
174    /// Decomposes struct, useful when needing a `Sized` array or moving all the data into another type
175    ///
176    /// It is recommended to use `digest()` `code()` and `size()` for most cases.
177    pub fn into_inner(self) -> (u64, [u8; S], u8) {
178        let Self { code, digest, size } = self;
179        (code, digest, size)
180    }
181}
182
183// Don't hash the whole allocated space, but just the actual digest
184#[allow(clippy::derived_hash_with_manual_eq)]
185impl<const S: usize> core::hash::Hash for Multihash<S> {
186    fn hash<T: core::hash::Hasher>(&self, state: &mut T) {
187        self.code.hash(state);
188        self.digest().hash(state);
189    }
190}
191
192#[cfg(feature = "alloc")]
193impl<const S: usize> From<Multihash<S>> for Vec<u8> {
194    fn from(multihash: Multihash<S>) -> Self {
195        multihash.to_bytes()
196    }
197}
198
199impl<const A: usize, const B: usize> PartialEq<Multihash<B>> for Multihash<A> {
200    fn eq(&self, other: &Multihash<B>) -> bool {
201        // NOTE: there's no need to explicitly check the sizes, that's implicit in the digest.
202        self.code == other.code && self.digest() == other.digest()
203    }
204}
205
206#[cfg(feature = "scale-codec")]
207impl<const S: usize> parity_scale_codec::Encode for Multihash<S> {
208    fn encode_to<EncOut: parity_scale_codec::Output + ?Sized>(&self, dest: &mut EncOut) {
209        self.code.encode_to(dest);
210        self.size.encode_to(dest);
211        // **NOTE** We write the digest directly to dest, since we have known the size of digest.
212        //
213        // We do not choose to encode &[u8] directly, because it will add extra bytes (the compact length of digest).
214        // For a valid multihash, the length of digest must equal to `size`.
215        // Therefore, we can only read raw bytes whose length is equal to `size` when decoding.
216        dest.write(self.digest());
217    }
218}
219
220#[cfg(feature = "scale-codec")]
221impl<const S: usize> parity_scale_codec::EncodeLike for Multihash<S> {}
222
223#[cfg(feature = "scale-codec")]
224impl<const S: usize> parity_scale_codec::Decode for Multihash<S> {
225    fn decode<DecIn: parity_scale_codec::Input>(
226        input: &mut DecIn,
227    ) -> Result<Self, parity_scale_codec::Error> {
228        let mut mh = Multihash {
229            code: parity_scale_codec::Decode::decode(input)?,
230            size: parity_scale_codec::Decode::decode(input)?,
231            digest: [0; S],
232        };
233        if mh.size as usize > S {
234            return Err(parity_scale_codec::Error::from("invalid size"));
235        }
236        // For a valid multihash, the length of digest must equal to the size.
237        input.read(&mut mh.digest[..mh.size as usize])?;
238        Ok(mh)
239    }
240}
241
242/// Writes the multihash to a byte stream.
243fn write_multihash<W>(mut w: W, code: u64, size: u8, digest: &[u8]) -> Result<usize, Error>
244where
245    W: io::Write,
246{
247    let mut code_buf = varint_encode::u64_buffer();
248    let code = varint_encode::u64(code, &mut code_buf);
249
250    let mut size_buf = varint_encode::u8_buffer();
251    let size = varint_encode::u8(size, &mut size_buf);
252
253    let written = code.len() + size.len() + digest.len();
254
255    w.write_all(code)
256        .map_err(crate::error::io_to_multihash_error)?;
257    w.write_all(size)
258        .map_err(crate::error::io_to_multihash_error)?;
259    w.write_all(digest)
260        .map_err(crate::error::io_to_multihash_error)?;
261
262    Ok(written)
263}
264
265/// Reads a multihash from a byte stream that contains a full multihash (code, size and the digest)
266///
267/// Returns the code, size and the digest. The size is the actual size and not the
268/// maximum/allocated size of the digest.
269///
270/// Currently the maximum size for a digest is 255 bytes.
271fn read_multihash<R, const S: usize>(mut r: R) -> Result<(u64, u8, [u8; S]), Error>
272where
273    R: io::Read,
274{
275    let code = read_u64(&mut r)?;
276    let size = read_u64(&mut r)?;
277
278    if size > S as u64 || size > u8::MAX as u64 {
279        return Err(Error::invalid_size(size));
280    }
281
282    let mut digest = [0; S];
283    r.read_exact(&mut digest[..size as usize])
284        .map_err(crate::error::io_to_multihash_error)?;
285    Ok((code, size as u8, digest))
286}
287
288#[cfg(feature = "std")]
289pub(crate) fn read_u64<R: io::Read>(r: R) -> Result<u64, Error> {
290    unsigned_varint::io::read_u64(r).map_err(crate::error::unsigned_varint_to_multihash_error)
291}
292
293/// Reads 64 bits from a byte array into a u64
294/// Adapted from unsigned-varint's generated read_u64 function at
295/// https://github.com/paritytech/unsigned-varint/blob/master/src/io.rs
296#[cfg(not(feature = "std"))]
297pub(crate) fn read_u64<R: io::Read>(mut r: R) -> Result<u64, Error> {
298    use unsigned_varint::decode;
299    let mut b = varint_encode::u64_buffer();
300    for i in 0..b.len() {
301        let n = r
302            .read(&mut (b[i..i + 1]))
303            .map_err(crate::error::io_to_multihash_error)?;
304        if n == 0 {
305            return Err(Error::insufficient_varint_bytes());
306        } else if decode::is_last(b[i]) {
307            return decode::u64(&b[..=i])
308                .map(|decoded| decoded.0)
309                .map_err(crate::error::unsigned_varint_decode_to_multihash_error);
310        }
311    }
312    Err(Error::varint_overflow())
313}
314
315#[cfg(test)]
316mod tests {
317    use super::*;
318
319    #[test]
320    #[cfg(feature = "scale-codec")]
321    fn test_scale() {
322        use parity_scale_codec::{Decode, Encode};
323
324        let mh1 = Multihash::<32>::wrap(0, b"hello world").unwrap();
325        // println!("mh1: code = {}, size = {}, digest = {:?}", mh1.code(), mh1.size(), mh1.digest());
326        let mh1_bytes = mh1.encode();
327        // println!("Multihash<32>: {}", hex::encode(&mh1_bytes));
328        let mh2: Multihash<32> = Decode::decode(&mut &mh1_bytes[..]).unwrap();
329        assert_eq!(mh1, mh2);
330
331        let mh3 = Multihash::<64>::wrap(0, b"hello world").unwrap();
332        // println!("mh3: code = {}, size = {}, digest = {:?}", mh3.code(), mh3.size(), mh3.digest());
333        let mh3_bytes = mh3.encode();
334        // println!("Multihash<64>: {}", hex::encode(&mh3_bytes));
335        let mh4: Multihash<64> = Decode::decode(&mut &mh3_bytes[..]).unwrap();
336        assert_eq!(mh3, mh4);
337
338        assert_eq!(mh1_bytes, mh3_bytes);
339    }
340
341    #[test]
342    fn test_eq_sizes() {
343        let mh1 = Multihash::<32>::default();
344        let mh2 = Multihash::<64>::default();
345        assert_eq!(mh1, mh2);
346    }
347
348    #[test]
349    fn decode_non_minimal_error() {
350        // This is a non-minimal varint.
351        let data = [241, 0, 0, 0, 0, 0, 128, 132, 132, 132, 58];
352        let result = read_u64(&data[..]);
353        assert!(result.is_err());
354    }
355}