referrerpolicy=no-referrer-when-downgrade

sc_network_types/
multihash.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! [`Multihash`] implemenattion used by substrate. Currently it's a wrapper over
20//! multihash used by litep2p, but it can be switched to other implementation if needed.
21
22use litep2p::types::multihash::{
23	Code as LiteP2pCode, Error as LiteP2pError, Multihash as LiteP2pMultihash, MultihashDigest as _,
24};
25use std::fmt::{self, Debug};
26
27/// Default [`Multihash`] implementations. Only hashes used by substrate are defined.
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub enum Code {
30	/// Identity hasher.
31	Identity,
32	/// SHA-256 (32-byte hash size).
33	Sha2_256,
34}
35
36impl Code {
37	/// Calculate digest using this [`Code`]'s hashing algorithm.
38	pub fn digest(&self, input: &[u8]) -> Multihash {
39		LiteP2pCode::from(*self).digest(input).into()
40	}
41}
42
43/// Error generated when converting to [`Code`].
44#[derive(Debug, thiserror::Error)]
45pub enum Error {
46	/// Invalid multihash size.
47	#[error("invalid multihash size '{0}'")]
48	InvalidSize(u64),
49	/// The multihash code is not supported.
50	#[error("unsupported multihash code '{0:x}'")]
51	UnsupportedCode(u64),
52	/// Catch-all for other errors emitted when converting `u64` code to enum or parsing multihash
53	/// from bytes. Never generated as of multihash-0.17.0.
54	#[error("other error: {0}")]
55	Other(Box<dyn std::error::Error + Send + Sync>),
56}
57
58impl From<LiteP2pError> for Error {
59	fn from(error: LiteP2pError) -> Self {
60		match error {
61			LiteP2pError::InvalidSize(s) => Self::InvalidSize(s),
62			LiteP2pError::UnsupportedCode(c) => Self::UnsupportedCode(c),
63			e => Self::Other(Box::new(e)),
64		}
65	}
66}
67
68impl From<Code> for LiteP2pCode {
69	fn from(code: Code) -> Self {
70		match code {
71			Code::Identity => LiteP2pCode::Identity,
72			Code::Sha2_256 => LiteP2pCode::Sha2_256,
73		}
74	}
75}
76
77impl TryFrom<LiteP2pCode> for Code {
78	type Error = Error;
79
80	fn try_from(code: LiteP2pCode) -> Result<Self, Self::Error> {
81		match code {
82			LiteP2pCode::Identity => Ok(Code::Identity),
83			LiteP2pCode::Sha2_256 => Ok(Code::Sha2_256),
84			_ => Err(Error::UnsupportedCode(code.into())),
85		}
86	}
87}
88
89impl TryFrom<u64> for Code {
90	type Error = Error;
91
92	fn try_from(code: u64) -> Result<Self, Self::Error> {
93		match LiteP2pCode::try_from(code) {
94			Ok(code) => code.try_into(),
95			Err(e) => Err(e.into()),
96		}
97	}
98}
99
100impl From<Code> for u64 {
101	fn from(code: Code) -> Self {
102		LiteP2pCode::from(code).into()
103	}
104}
105
106#[derive(Clone, Copy, Hash, PartialEq, Eq, Ord, PartialOrd)]
107pub struct Multihash {
108	multihash: LiteP2pMultihash,
109}
110
111impl Multihash {
112	/// Multihash code.
113	pub fn code(&self) -> u64 {
114		self.multihash.code()
115	}
116
117	/// Multihash digest.
118	pub fn digest(&self) -> &[u8] {
119		self.multihash.digest()
120	}
121
122	/// Wraps the digest in a multihash.
123	pub fn wrap(code: u64, input_digest: &[u8]) -> Result<Self, Error> {
124		LiteP2pMultihash::wrap(code, input_digest).map(Into::into).map_err(Into::into)
125	}
126
127	/// Parses a multihash from bytes.
128	///
129	/// You need to make sure the passed in bytes have the length of 64.
130	pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
131		LiteP2pMultihash::from_bytes(bytes).map(Into::into).map_err(Into::into)
132	}
133
134	/// Returns the bytes of a multihash.
135	pub fn to_bytes(&self) -> Vec<u8> {
136		self.multihash.to_bytes()
137	}
138}
139
140/// Remove extra layer of nestedness by deferring to the wrapped value's [`Debug`].
141impl Debug for Multihash {
142	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143		Debug::fmt(&self.multihash, f)
144	}
145}
146
147impl From<LiteP2pMultihash> for Multihash {
148	fn from(multihash: LiteP2pMultihash) -> Self {
149		Multihash { multihash }
150	}
151}
152
153impl From<Multihash> for LiteP2pMultihash {
154	fn from(multihash: Multihash) -> Self {
155		multihash.multihash
156	}
157}
158
159impl From<multihash::Multihash<64>> for Multihash {
160	fn from(generic: multihash::Multihash<64>) -> Self {
161		LiteP2pMultihash::wrap(generic.code(), generic.digest())
162			.expect("both have size 64; qed")
163			.into()
164	}
165}
166
167impl From<Multihash> for multihash::Multihash<64> {
168	fn from(multihash: Multihash) -> Self {
169		multihash::Multihash::<64>::wrap(multihash.code(), multihash.digest())
170			.expect("both have size 64; qed")
171	}
172}
173
174#[cfg(test)]
175mod tests {
176	use super::*;
177
178	#[test]
179	fn code_from_u64() {
180		assert_eq!(Code::try_from(0x00).unwrap(), Code::Identity);
181		assert_eq!(Code::try_from(0x12).unwrap(), Code::Sha2_256);
182		assert!(matches!(Code::try_from(0x01).unwrap_err(), Error::UnsupportedCode(0x01)));
183	}
184
185	#[test]
186	fn code_into_u64() {
187		assert_eq!(u64::from(Code::Identity), 0x00);
188		assert_eq!(u64::from(Code::Sha2_256), 0x12);
189	}
190}