Skip to main content

hotshot_new_protocol/consensus/
proposals.rs

1use std::{collections::BTreeMap, ops::RangeBounds};
2
3use committable::Commitment;
4use hotshot_types::{
5    data::{Leaf2, ViewNumber},
6    traits::node_implementation::NodeType,
7    vote::HasViewNumber,
8};
9
10use crate::message::Proposal;
11
12/// The proposals a node holds, by view and commitment.
13///
14/// A view normally holds one. An equivocating leader can send different
15/// proposals for its view to different nodes, and at an epoch boundary two
16/// leaders can propose for one view, so a node can hold several. Every
17/// certificate names its proposal by commitment, and whatever a node does on a
18/// certificate's behalf looks the proposal up by that commitment. So no proposal
19/// ever has to give way to another, whatever order they arrive in.
20///
21/// `live` is, per view, the proposal the node paired with its own VID share.
22pub struct Proposals<T: NodeType> {
23    live: BTreeMap<ViewNumber, Commitment<Leaf2<T>>>,
24    all: BTreeMap<ViewNumber, BTreeMap<Commitment<Leaf2<T>>, Proposal<T>>>,
25}
26
27impl<T: NodeType> Proposals<T> {
28    pub fn new() -> Self {
29        Self {
30            live: BTreeMap::new(),
31            all: BTreeMap::new(),
32        }
33    }
34
35    pub fn live(&self, v: ViewNumber) -> Option<&Proposal<T>> {
36        self.get(v, *self.live.get(&v)?)
37    }
38
39    pub fn at(&self, v: ViewNumber) -> impl Iterator<Item = &Proposal<T>> {
40        self.all.get(&v).into_iter().flat_map(|a| a.values())
41    }
42
43    pub fn get(&self, v: ViewNumber, c: Commitment<Leaf2<T>>) -> Option<&Proposal<T>> {
44        self.all.get(&v)?.get(&c)
45    }
46
47    pub fn contains(&self, v: ViewNumber, c: Commitment<Leaf2<T>>) -> bool {
48        self.get(v, c).is_some()
49    }
50
51    pub fn range<R>(&self, r: R) -> impl Iterator<Item = &Proposal<T>>
52    where
53        R: RangeBounds<ViewNumber>,
54    {
55        self.all.range(r).flat_map(|(_, a)| a.values())
56    }
57
58    /// The proposal held under `c` at the latest view before `v`.
59    pub fn latest_before(&self, v: ViewNumber, c: Commitment<Leaf2<T>>) -> Option<&Proposal<T>> {
60        self.all.range(..v).rev().find_map(|(_, a)| a.get(&c))
61    }
62
63    pub fn last(&self) -> Option<&Proposal<T>> {
64        let (_, all) = self.all.last_key_value()?;
65        all.values().max_by_key(|p| p.justify_qc.view_number())
66    }
67
68    pub fn insert_under(&mut self, p: Proposal<T>, c: Commitment<Leaf2<T>>) {
69        self.all.entry(p.view_number).or_default().insert(c, p);
70    }
71
72    pub fn set_live(&mut self, v: ViewNumber, c: Commitment<Leaf2<T>>) {
73        self.live.insert(v, c);
74    }
75
76    pub fn retain_from(&mut self, view: ViewNumber) {
77        self.all = self.all.split_off(&view);
78        self.live = self.live.split_off(&view);
79    }
80
81    #[cfg(test)]
82    pub fn insert(&mut self, p: Proposal<T>) -> Commitment<Leaf2<T>> {
83        let c = crate::helpers::proposal_commitment(&p);
84        self.insert_under(p, c);
85        c
86    }
87
88    #[cfg(test)]
89    pub(crate) fn replace_view(&mut self, proposal: Proposal<T>) {
90        let view = proposal.view_number;
91        self.all.remove(&view);
92        self.live.remove(&view);
93        self.insert(proposal);
94    }
95}