ipld_nostd/multihash/
mod.rs

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