fedimint_hbbft/sender_queue/
dynamic_honey_badger.rs

1//! Convenience methods for a `SenderQueue` wrapping a `DynamicHoneyBadger`.
2
3use std::collections::BTreeSet;
4use std::result;
5
6use crate::crypto::PublicKey;
7use rand::Rng;
8use serde::{de::DeserializeOwned, Serialize};
9
10use super::{
11    Error, Message, SenderQueue, SenderQueueableConsensusProtocol, SenderQueueableMessage,
12    SenderQueueableOutput,
13};
14use crate::{Contribution, CpStep, NodeIdT};
15
16use crate::dynamic_honey_badger::{
17    Batch, Change, ChangeState, DynamicHoneyBadger, Error as DhbError, JoinPlan,
18    Message as DhbMessage,
19};
20
21impl<C, N> SenderQueueableOutput<N, (u64, u64)> for Batch<C, N>
22where
23    C: Contribution,
24    N: NodeIdT,
25{
26    fn participant_change(&self) -> Option<BTreeSet<N>> {
27        if let ChangeState::InProgress(Change::NodeChange(pub_keys)) = self.change() {
28            let candidates = pub_keys.keys();
29            let current_validators: BTreeSet<&N> = self.public_keys().keys().collect();
30            let participants = candidates.chain(current_validators).cloned().collect();
31            Some(participants)
32        } else if let ChangeState::Complete(Change::NodeChange(pub_keys)) = self.change() {
33            let next_validators = pub_keys.keys().cloned().collect();
34            Some(next_validators)
35        } else {
36            None
37        }
38    }
39
40    fn output_epoch(&self) -> (u64, u64) {
41        let hb_epoch = self.epoch() - self.era();
42        (self.era(), hb_epoch)
43    }
44}
45
46impl<N: Ord> SenderQueueableMessage for DhbMessage<N> {
47    type Epoch = (u64, u64);
48
49    fn is_premature(&self, (them_era, them): (u64, u64), max_future_epochs: u64) -> bool {
50        match *self {
51            DhbMessage::HoneyBadger(era, ref msg) => {
52                era > them_era || (era == them_era && msg.epoch() > them + max_future_epochs)
53            }
54            DhbMessage::KeyGen(era, _, _) => era > them_era,
55            DhbMessage::SignedVote(ref signed_vote) => signed_vote.era() > them_era,
56        }
57    }
58
59    fn is_obsolete(&self, (them_era, them): (u64, u64)) -> bool {
60        match *self {
61            DhbMessage::HoneyBadger(era, ref msg) => {
62                era < them_era || (era == them_era && msg.epoch() < them)
63            }
64            DhbMessage::KeyGen(era, _, _) => era < them_era,
65            DhbMessage::SignedVote(ref signed_vote) => signed_vote.era() < them_era,
66        }
67    }
68
69    fn first_epoch(&self) -> (u64, u64) {
70        match *self {
71            DhbMessage::HoneyBadger(era, ref msg) => (era, msg.epoch()),
72            DhbMessage::KeyGen(era, _, _) => (era, 0),
73            DhbMessage::SignedVote(ref signed_vote) => (signed_vote.era(), 0),
74        }
75    }
76}
77
78impl<C, N> SenderQueueableConsensusProtocol for DynamicHoneyBadger<C, N>
79where
80    C: Contribution + Serialize + DeserializeOwned,
81    N: NodeIdT + Serialize + DeserializeOwned,
82{
83    fn max_future_epochs(&self) -> u64 {
84        self.max_future_epochs()
85    }
86}
87
88type Result<C, N> = result::Result<CpStep<SenderQueue<DynamicHoneyBadger<C, N>>>, Error<DhbError>>;
89
90impl<C, N> SenderQueue<DynamicHoneyBadger<C, N>>
91where
92    C: Contribution + Serialize + DeserializeOwned,
93    N: NodeIdT + Serialize + DeserializeOwned,
94{
95    /// Proposes a contribution in the current epoch.
96    ///
97    /// Returns an error if we already made a proposal in this epoch.
98    ///
99    /// If we are the only validator, this will immediately output a batch, containing our
100    /// proposal.
101    pub fn propose<R: Rng>(&mut self, rng: &mut R, contrib: C) -> Result<C, N> {
102        self.apply(|algo| algo.propose(contrib, rng))
103    }
104
105    /// Casts a vote to change the set of validators or parameters.
106    ///
107    /// This stores a pending vote for the change. It will be included in some future batch, and
108    /// once enough validators have been voted for the same change, it will take effect.
109    pub fn vote_for(&mut self, change: Change<N>) -> Result<C, N> {
110        self.apply(|algo| algo.vote_for(change))
111    }
112
113    /// Casts a vote to add a node as a validator.
114    ///
115    /// This stores a pending vote for the change. It will be included in some future batch, and
116    /// once enough validators have been voted for the same change, it will take effect.
117    pub fn vote_to_add(&mut self, node_id: N, pub_key: PublicKey) -> Result<C, N> {
118        self.apply(|algo| algo.vote_to_add(node_id, pub_key))
119    }
120
121    /// Casts a vote to demote a validator to observer.
122    ///
123    /// This stores a pending vote for the change. It will be included in some future batch, and
124    /// once enough validators have been voted for the same change, it will take effect.
125    pub fn vote_to_remove(&mut self, node_id: &N) -> Result<C, N> {
126        self.apply(|algo| algo.vote_to_remove(node_id))
127    }
128
129    /// Restarts the managed algorithm with the given join plan with a new list of peers and with
130    /// the same secret key. In order to be restarted, the node should have completed the process of
131    /// removing itself from the network. The node may not output a batch if it were not properly
132    /// removed.
133    pub fn restart<I, R: Rng>(
134        &mut self,
135        join_plan: JoinPlan<N>,
136        peer_ids: I,
137        rng: &mut R,
138    ) -> Result<C, N>
139    where
140        I: Iterator<Item = N>,
141    {
142        if !self.is_removed {
143            return Err(Error::DynamicHoneyBadgerNotRemoved);
144        }
145        let secret_key = self.algo().secret_key().clone();
146        let id = self.algo().netinfo().our_id().clone();
147        let (dhb, dhb_step) =
148            DynamicHoneyBadger::new_joining(id.clone(), secret_key, join_plan, rng)
149                .map_err(Error::DynamicHoneyBadgerNewJoining)?;
150        let (sq, mut sq_step) = SenderQueue::builder(dhb, peer_ids).build(id);
151        sq_step.extend(dhb_step.map(|output| output, |fault| fault, Message::from));
152        *self = sq;
153        Ok(sq_step)
154    }
155}