sp_multihash/
multihash.rs

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