-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathmultihasher.rs
171 lines (147 loc) · 5.49 KB
/
multihasher.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//! Module responsible for calculating hashes for data received
//!
//! For interoperability `StandardMultihasher` is registered by default, which uses hashes
//! provided by [`multihash_codetable::Code`]. If you need to register your own multihashes,
//! you can implement [`Multihasher`] trait and then register the struct with
//! [`BehaviourBuilder::register_multihasher`] when creating the behaviour.
//!
//! [`BehaviourBuilder::register_multihasher`]:
//! crate::builder::BehaviourBuilder::register_multihasher
use std::collections::VecDeque;
use std::fmt::{self, Display};
use std::future::Future;
use std::pin::Pin;
use futures_util::future::FutureExt;
use libp2p_core::multihash::Multihash;
use multihash_codetable::MultihashDigest;
use crate::utils::convert_multihash;
/// Errors that can be produced by [`Multihasher`] trait.
#[derive(Debug, thiserror::Error)]
pub enum MultihasherError {
/// [`Multihasher`] can not handle the specified multihash code.
#[error("Unknown multihash code")]
UnknownMultihashCode,
/// Bigger [`Multihash`] is needed.
///
/// Maximum allowed size of [`Multihash`] is specified as generic const when
/// [`Behaviour`] is constructed.
///
/// [`Behaviour`]: crate::Behaviour
#[error("Invalid multihash size")]
InvalidMultihashSize,
/// Custom error.
///
/// This error will be logged and the specified `input` will be ignored.
#[error("Hashing failure: {0}")]
Custom(String),
/// Custom error that causes [`Stream`] to close.
///
/// This error will be logged and the [`Stream`] which delivered the
/// `input` will be closed.
///
/// [`Stream`]: libp2p_swarm::Stream
#[error("Fatal hashing failure: {0}")]
CustomFatal(String),
}
impl MultihasherError {
/// Custom error, causes block to be ignored
pub fn custom(e: impl Display) -> MultihasherError {
MultihasherError::Custom(e.to_string())
}
/// Custom fatal error, causes block to be ignored and stream from which it was received to
/// close
pub fn custom_fatal(e: impl Display) -> MultihasherError {
MultihasherError::CustomFatal(e.to_string())
}
}
/// Trait for producing a custom [`Multihash`].
pub trait Multihasher<const S: usize> {
/// Hash the `input` based on the `multihash_code`.
///
/// If this `Multihasher` can not handle the specified `multihash_code`, then
/// [`MultihasherError::UnknownMultihashCode`] must be returned. In this
/// case hashing will be re-tried with the next `Multihasher`. For more info check
/// [`BehaviourBuilder::register_multihasher`].
///
/// [`BehaviourBuilder::register_multihasher`]: crate::BehaviourBuilder::register_multihasher
fn hash(
&self,
multihash_code: u64,
input: &[u8],
) -> impl Future<Output = Result<Multihash<S>, MultihasherError>> + Send;
}
/// Workaround for having dynamic dispatch for `Multihasher` internally.
trait DispatchableMultihasher<const S: usize> {
fn hash<'a>(
&'a self,
multihash_code: u64,
input: &'a [u8],
) -> Pin<Box<dyn Future<Output = Result<Multihash<S>, MultihasherError>> + Send + 'a>>;
}
impl<const S: usize, T> DispatchableMultihasher<S> for T
where
T: Multihasher<S>,
{
fn hash<'a>(
&'a self,
multihash_code: u64,
input: &'a [u8],
) -> Pin<Box<dyn Future<Output = Result<Multihash<S>, MultihasherError>> + Send + 'a>> {
Multihasher::<S>::hash(self, multihash_code, input).boxed()
}
}
/// [`Multihasher`] that uses [`multihash_codetable::Code`]
pub struct StandardMultihasher;
impl<const S: usize> Multihasher<S> for StandardMultihasher {
async fn hash(
&self,
multihash_code: u64,
input: &[u8],
) -> Result<Multihash<S>, MultihasherError> {
let hasher = multihash_codetable::Code::try_from(multihash_code)
.map_err(|_| MultihasherError::UnknownMultihashCode)?;
let hash = hasher.digest(input);
convert_multihash(&hash).ok_or(MultihasherError::InvalidMultihashSize)
}
}
pub(crate) struct MultihasherTable<const S: usize> {
multihashers: VecDeque<Box<dyn DispatchableMultihasher<S> + Send + Sync + 'static>>,
}
impl<const S: usize> fmt::Debug for MultihasherTable<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("MultihasherTable { .. }")
}
}
impl<const S: usize> MultihasherTable<S> {
pub(crate) fn new() -> Self {
let mut table = MultihasherTable {
multihashers: VecDeque::new(),
};
table.register(StandardMultihasher);
table
}
pub(crate) fn register<M>(&mut self, multihasher: M)
where
M: Multihasher<S> + Send + Sync + 'static,
{
self.multihashers.push_front(Box::new(multihasher));
}
pub(crate) async fn hash(
&self,
multihash_code: u64,
input: &[u8],
) -> Result<Multihash<S>, MultihasherError> {
for multihasher in &self.multihashers {
match multihasher.hash(multihash_code, input).await {
Ok(hash) => return Ok(hash),
// `multihash_code` cannot be handled by this multihasher
// so we move to the next one.
Err(MultihasherError::UnknownMultihashCode) => continue,
Err(e) => return Err(e),
}
}
// Reaching this point means there isn't any registered multihasher
// that can handle `multihash_code`.
Err(MultihasherError::UnknownMultihashCode)
}
}