Skip to main content

hbbft/
traits.rs

1//! Common supertraits for consensus protocols.
2
3use std::collections::BTreeMap;
4use std::fmt::{Debug, Display};
5use std::hash::Hash;
6use std::iter::once;
7
8use failure::Fail;
9use rand::Rng;
10use serde::{de::DeserializeOwned, Serialize};
11
12use crate::fault_log::{Fault, FaultLog};
13use crate::sender_queue::SenderQueueableMessage;
14use crate::{Target, TargetedMessage};
15
16/// A transaction, user message, or other user data.
17pub trait Contribution: Eq + Debug + Hash + Send + Sync {}
18impl<C> Contribution for C where C: Eq + Debug + Hash + Send + Sync {}
19
20/// A peer node's unique identifier.
21pub trait NodeIdT: Eq + Ord + Clone + Debug + Hash + Send + Sync {}
22impl<N> NodeIdT for N where N: Eq + Ord + Clone + Debug + Hash + Send + Sync {}
23
24/// A consensus protocol fault.
25pub trait FaultT: Clone + Debug + Fail + PartialEq {}
26impl<N> FaultT for N where N: Clone + Debug + Fail + PartialEq {}
27
28/// Messages.
29pub trait Message: Debug + Send + Sync {}
30impl<M> Message for M where M: Debug + Send + Sync {}
31
32/// Session identifiers.
33pub trait SessionIdT: Display + Serialize + Send + Sync + Clone + Debug {}
34impl<S> SessionIdT for S where S: Display + Serialize + Send + Sync + Clone + Debug {}
35
36/// Epochs.
37pub trait EpochT: Copy + Message + Default + Eq + Ord + Serialize + DeserializeOwned {}
38impl<E> EpochT for E where E: Copy + Message + Default + Eq + Ord + Serialize + DeserializeOwned {}
39
40/// Single algorithm step outcome.
41///
42/// Each time input (typically in the form of user input or incoming network messages) is provided
43/// to an instance of an algorithm, a `Step` is produced, potentially containing output values,
44/// a fault log, and network messages.
45///
46/// Any `Step` **must always be used** by the client application; at the very least the resulting
47/// messages must be queued.
48///
49/// ## Handling unused Steps
50///
51/// In the (rare) case of a `Step` not being of any interest at all, instead of discarding it
52/// through `let _ = ...` or similar constructs, the implicit assumption should explicitly be
53/// checked instead:
54///
55/// ```ignore
56/// assert!(alg.propose(123).expect("Could not propose value").is_empty(),
57///         "Algorithm will never output anything on first proposal");
58/// ```
59///
60/// If an edge case occurs and outgoing messages are generated as a result, the `assert!` will
61/// catch it, instead of potentially stalling the algorithm.
62#[must_use = "The algorithm step result must be used."]
63#[derive(Debug)]
64pub struct Step<M, O, N, F: Fail> {
65    /// The algorithm's output, after consensus has been reached. This is guaranteed to be the same
66    /// in all nodes.
67    pub output: Vec<O>,
68    /// A list of nodes that are not following consensus, together with information about the
69    /// detected misbehavior.
70    pub fault_log: FaultLog<N, F>,
71    /// A list of messages that must be sent to other nodes. Each entry contains a message and a
72    /// `Target`.
73    pub messages: Vec<TargetedMessage<M, N>>,
74}
75
76impl<M, O, N, F> Default for Step<M, O, N, F>
77where
78    F: Fail,
79{
80    fn default() -> Self {
81        Step {
82            output: Vec::default(),
83            fault_log: FaultLog::default(),
84            messages: Vec::default(),
85        }
86    }
87}
88
89impl<M, O, N, F> Step<M, O, N, F>
90where
91    F: Fail,
92{
93    /// Creates a new `Step` from the given collections.
94    pub fn new(
95        output: Vec<O>,
96        fault_log: FaultLog<N, F>,
97        messages: Vec<TargetedMessage<M, N>>,
98    ) -> Self {
99        Step {
100            output,
101            fault_log,
102            messages,
103        }
104    }
105
106    /// Returns the same step, with the given additional output.
107    pub fn with_output<T: Into<Option<O>>>(mut self, output: T) -> Self {
108        self.output.extend(output.into());
109        self
110    }
111
112    /// Converts `self` into a step of another type, given conversion methods for output, faults,
113    /// and messages.
114    pub fn map<M2, O2, F2, FO, FF, FM>(
115        self,
116        f_out: FO,
117        f_fail: FF,
118        f_msg: FM,
119    ) -> Step<M2, O2, N, F2>
120    where
121        F2: Fail,
122        FO: Fn(O) -> O2,
123        FF: Fn(F) -> F2,
124        FM: Fn(M) -> M2,
125    {
126        Step {
127            output: self.output.into_iter().map(f_out).collect(),
128            fault_log: self.fault_log.map(f_fail),
129            messages: self.messages.into_iter().map(|tm| tm.map(&f_msg)).collect(),
130        }
131    }
132
133    /// Extends `self` with `other`s messages and fault logs, and returns `other.output`.
134    #[must_use]
135    pub fn extend_with<M2, O2, F2, FF, FM>(
136        &mut self,
137        other: Step<M2, O2, N, F2>,
138        f_fail: FF,
139        f_msg: FM,
140    ) -> Vec<O2>
141    where
142        F2: Fail,
143        FF: Fn(F2) -> F,
144        FM: Fn(M2) -> M,
145    {
146        let fails = other.fault_log.map(f_fail);
147        self.fault_log.extend(fails);
148        let msgs = other.messages.into_iter().map(|tm| tm.map(&f_msg));
149        self.messages.extend(msgs);
150        other.output
151    }
152
153    /// Adds the outputs, fault logs and messages of `other` to `self`.
154    pub fn extend(&mut self, other: Self) {
155        self.output.extend(other.output);
156        self.fault_log.extend(other.fault_log);
157        self.messages.extend(other.messages);
158    }
159
160    /// Extends this step with `other` and returns the result.
161    pub fn join(mut self, other: Self) -> Self {
162        self.extend(other);
163        self
164    }
165
166    /// Returns `true` if there are no messages, faults or outputs.
167    pub fn is_empty(&self) -> bool {
168        self.output.is_empty() && self.fault_log.is_empty() && self.messages.is_empty()
169    }
170}
171
172impl<M, O, N, F> From<FaultLog<N, F>> for Step<M, O, N, F>
173where
174    F: Fail,
175{
176    fn from(fault_log: FaultLog<N, F>) -> Self {
177        Step {
178            fault_log,
179            ..Step::default()
180        }
181    }
182}
183
184impl<M, O, N, F> From<Fault<N, F>> for Step<M, O, N, F>
185where
186    F: Fail,
187{
188    fn from(fault: Fault<N, F>) -> Self {
189        Step {
190            fault_log: fault.into(),
191            ..Step::default()
192        }
193    }
194}
195
196impl<M, O, N, F> From<TargetedMessage<M, N>> for Step<M, O, N, F>
197where
198    F: Fail,
199{
200    fn from(msg: TargetedMessage<M, N>) -> Self {
201        Step {
202            messages: once(msg).collect(),
203            ..Step::default()
204        }
205    }
206}
207
208impl<I, M, O, N, F> From<I> for Step<M, O, N, F>
209where
210    I: IntoIterator<Item = TargetedMessage<M, N>>,
211    F: Fail,
212{
213    fn from(msgs: I) -> Self {
214        Step {
215            messages: msgs.into_iter().collect(),
216            ..Step::default()
217        }
218    }
219}
220
221/// An interface to objects with epoch numbers. Different algorithms may have different internal
222/// notion of _epoch_. This interface summarizes the properties that are essential for the message
223/// sender queue.
224pub trait Epoched {
225    /// Type of epoch.
226    type Epoch: EpochT;
227
228    /// Returns the object's epoch number.
229    fn epoch(&self) -> Self::Epoch;
230}
231
232/// An alias for the type of `Step` returned by `D`'s methods.
233pub type CpStep<D> = Step<
234    <D as ConsensusProtocol>::Message,
235    <D as ConsensusProtocol>::Output,
236    <D as ConsensusProtocol>::NodeId,
237    <D as ConsensusProtocol>::FaultKind,
238>;
239
240impl<'i, M, O, N, F> Step<M, O, N, F>
241where
242    N: NodeIdT,
243    M: 'i + Clone + SenderQueueableMessage,
244    F: Fail,
245{
246    /// Removes and returns any messages that are not yet accepted by remote nodes according to the
247    /// mapping `remote_epochs`. This way the returned messages are postponed until later, and the
248    /// remaining messages can be sent to remote nodes without delay.
249    pub fn defer_messages(
250        &mut self,
251        peer_epochs: &BTreeMap<N, M::Epoch>,
252        max_future_epochs: u64,
253    ) -> Vec<(N, M)> {
254        let mut deferred_msgs: Vec<(N, M)> = Vec::new();
255        let mut passed_msgs: Vec<_> = Vec::new();
256        for msg in self.messages.drain(..) {
257            match msg.target.clone() {
258                Target::Node(id) => {
259                    if let Some(&them) = peer_epochs.get(&id) {
260                        if msg.message.is_premature(them, max_future_epochs) {
261                            deferred_msgs.push((id, msg.message));
262                        } else if !msg.message.is_obsolete(them) {
263                            passed_msgs.push(msg);
264                        }
265                    }
266                }
267                Target::All => {
268                    let is_accepted = |&them| msg.message.is_accepted(them, max_future_epochs);
269                    let is_premature = |&them| msg.message.is_premature(them, max_future_epochs);
270                    let is_obsolete = |&them| msg.message.is_obsolete(them);
271                    if peer_epochs.values().all(is_accepted) {
272                        passed_msgs.push(msg);
273                    } else {
274                        // The `Target::All` message is split into two sets of point messages: those
275                        // which can be sent without delay and those which should be postponed.
276                        for (id, them) in peer_epochs {
277                            if is_premature(them) {
278                                deferred_msgs.push((id.clone(), msg.message.clone()));
279                            } else if !is_obsolete(them) {
280                                passed_msgs
281                                    .push(Target::Node(id.clone()).message(msg.message.clone()));
282                            }
283                        }
284                    }
285                }
286            }
287        }
288        self.messages.extend(passed_msgs);
289        deferred_msgs
290    }
291}
292
293/// A consensus protocol that defines a message flow.
294///
295/// Many algorithms require an RNG which must be supplied on each call. It is up to the caller to
296/// ensure that this random number generator is cryptographically secure.
297pub trait ConsensusProtocol: Send + Sync {
298    /// Unique node identifier.
299    type NodeId: NodeIdT;
300    /// The input provided by the user.
301    type Input;
302    /// The output type. Some algorithms return an output exactly once, others return multiple
303    /// times.
304    type Output;
305    /// The messages that need to be exchanged between the instances in the participating nodes.
306    type Message: Message;
307    /// The errors that can occur during execution.
308    type Error: Fail;
309    /// The kinds of message faults that can be detected during execution.
310    type FaultKind: FaultT;
311
312    /// Handles an input provided by the user, and returns
313    fn handle_input<R: Rng>(
314        &mut self,
315        input: Self::Input,
316        rng: &mut R,
317    ) -> Result<CpStep<Self>, Self::Error>
318    where
319        Self: Sized;
320
321    /// Handles a message received from node `sender_id`.
322    fn handle_message<R: Rng>(
323        &mut self,
324        sender_id: &Self::NodeId,
325        message: Self::Message,
326        rng: &mut R,
327    ) -> Result<CpStep<Self>, Self::Error>
328    where
329        Self: Sized;
330
331    /// Returns `true` if execution has completed and this instance can be dropped.
332    fn terminated(&self) -> bool;
333
334    /// Returns this node's own ID.
335    fn our_id(&self) -> &Self::NodeId;
336}