fedimint_hbbft/
fault_log.rs

1//! Functionality for logging faulty node behavior encountered by each
2//! algorithm.
3//!
4//! Each algorithm can propagate their faulty node logs upwards to a calling algorithm via
5//! `ConsensusProtocol`'s `.handle_input()` and `.handle_message()` trait methods.
6
7use std::error::Error;
8
9/// A structure representing the context of a faulty node. This structure
10/// describes which node is faulty (`node_id`) and which faulty behavior
11/// that the node exhibited (`kind`).
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct Fault<N, F: Error> {
14    /// The faulty node's ID.
15    pub node_id: N,
16    /// The kind of fault the node is blamed for.
17    pub kind: F,
18}
19
20impl<N, F> Fault<N, F>
21where
22    F: Error,
23{
24    /// Creates a new fault, blaming `node_id` for the `kind`.
25    pub fn new(node_id: N, kind: F) -> Self {
26        Fault { node_id, kind }
27    }
28
29    /// Applies `f_fault` to `kind`, leaves `node_id` unchanged
30    pub fn map<F2, FF>(self, f_fault: FF) -> Fault<N, F2>
31    where
32        F2: Error,
33        FF: FnOnce(F) -> F2,
34    {
35        Fault {
36            node_id: self.node_id,
37            kind: f_fault(self.kind),
38        }
39    }
40}
41
42/// Creates a new `FaultLog` where `self` is the first element in the log
43/// vector.
44#[allow(clippy::from_over_into)]
45impl<N, F> Into<FaultLog<N, F>> for Fault<N, F>
46where
47    F: Error,
48{
49    fn into(self) -> FaultLog<N, F> {
50        FaultLog(vec![self])
51    }
52}
53
54/// A structure used to contain reports of faulty node behavior.
55#[derive(Debug, PartialEq, Eq)]
56pub struct FaultLog<N, F: Error>(pub Vec<Fault<N, F>>);
57
58impl<N, F> FaultLog<N, F>
59where
60    F: Error,
61{
62    /// Creates an empty `FaultLog`.
63    pub fn new() -> Self {
64        FaultLog::default()
65    }
66
67    /// Creates a new `FaultLog` initialized with a single log.
68    pub fn init(node_id: N, kind: F) -> Self {
69        Fault::new(node_id, kind).into()
70    }
71
72    /// Creates a new `Fault` and pushes it onto the fault log.
73    pub fn append(&mut self, node_id: N, kind: F) {
74        self.0.push(Fault::new(node_id, kind));
75    }
76
77    /// Consumes a `Fault` and pushes it onto the fault log.
78    pub fn append_fault(&mut self, fault: Fault<N, F>) {
79        self.0.push(fault);
80    }
81
82    /// Consumes `new_logs`, appending its logs onto the end of `self`.
83    pub fn extend(&mut self, new_logs: FaultLog<N, F>) {
84        self.0.extend(new_logs.0);
85    }
86
87    /// Consumes `self`, appending its logs onto the end of `logs`.
88    pub fn merge_into(self, logs: &mut FaultLog<N, F>) {
89        logs.extend(self);
90    }
91
92    /// Returns `true` if there are no fault entries in the log.
93    pub fn is_empty(&self) -> bool {
94        self.0.is_empty()
95    }
96
97    /// Applies `f_fault` to each element in log, modifying its `kind` only
98    pub fn map<F2, FF>(self, mut f_fault: FF) -> FaultLog<N, F2>
99    where
100        F2: Error,
101        FF: FnMut(F) -> F2,
102    {
103        FaultLog(self.into_iter().map(|f| f.map(&mut f_fault)).collect())
104    }
105}
106
107impl<N, F> Default for FaultLog<N, F>
108where
109    F: Error,
110{
111    fn default() -> Self {
112        FaultLog(vec![])
113    }
114}
115
116impl<N, F> IntoIterator for FaultLog<N, F>
117where
118    F: Error,
119{
120    type Item = Fault<N, F>;
121    type IntoIter = std::vec::IntoIter<Fault<N, F>>;
122
123    fn into_iter(self) -> Self::IntoIter {
124        self.0.into_iter()
125    }
126}
127
128impl<N, F> std::iter::FromIterator<Fault<N, F>> for FaultLog<N, F>
129where
130    F: Error,
131{
132    fn from_iter<I: IntoIterator<Item = Fault<N, F>>>(iter: I) -> Self {
133        let mut log = FaultLog::new();
134        for i in iter {
135            log.append_fault(i);
136        }
137        log
138    }
139}