hotshot_new_protocol/vote/
accumulate.rs1use std::{any::type_name, mem};
2
3use committable::Committable;
4use hotshot_types::{
5 epoch_membership::EpochMembership,
6 message::UpgradeLock,
7 simple_certificate::{SimpleCertificate, Threshold},
8 simple_vote::{HasEpoch, VersionedVoteData},
9 stake_table::StakeTableEntries,
10 traits::{
11 node_implementation::NodeType,
12 signature_key::{SignatureKey, StakeTableEntryType},
13 },
14 vote::{Certificate, HasViewNumber, Vote, VoteAccumulator},
15};
16use hotshot_utils::anytrace;
17use tracing::{info, warn};
18
19use crate::cert_verifier::verify_signatures;
20
21pub type Cert<T, V, Th> = SimpleCertificate<T, <V as Vote<T>>::Commitment, Th>;
23
24pub struct CheckedAccumulator<T, V, Th>
31where
32 T: NodeType,
33 V: Vote<T>,
34 Th: Threshold<T>,
35 Cert<T, V, Th>: Certificate<T, V::Commitment, Voteable = V::Commitment> + HasEpoch,
36{
37 accumulator: VoteAccumulator<T, V, Cert<T, V, Th>>,
38
39 votes: Vec<V>,
41
42 verify_votes: bool,
46
47 membership: EpochMembership<T>,
48
49 upgrade_lock: UpgradeLock<T>,
50}
51
52impl<T, V, Th> CheckedAccumulator<T, V, Th>
53where
54 T: NodeType,
55 V: Vote<T>,
56 V::Commitment: 'static,
57 Th: Threshold<T>,
58 Cert<T, V, Th>: Certificate<T, V::Commitment, Voteable = V::Commitment> + HasEpoch,
59{
60 pub fn new(membership: EpochMembership<T>, lock: UpgradeLock<T>) -> Self {
61 Self {
62 accumulator: VoteAccumulator::new(lock.clone()),
63 votes: Vec::new(),
64 verify_votes: false,
65 membership,
66 upgrade_lock: lock,
67 }
68 }
69
70 pub fn add(&mut self, vote: V) -> Option<Cert<T, V, Th>> {
74 if self.verify_votes {
75 if !self.is_valid_vote(&vote) {
76 return None;
77 }
78 let cert = self
79 .accumulator
80 .accumulate(&vote, self.membership.clone())?;
81 debug_assert!(self.validate(&cert).is_ok());
82 info!(view = %cert.view_number(), cert = type_name::<Cert<T, V, Th>>(), "certificate formed");
83 return Some(cert);
84 }
85
86 let Some(cert) = self.accumulator.accumulate(&vote, self.membership.clone()) else {
87 self.votes.push(vote);
88 return None;
89 };
90
91 match self.validate(&cert) {
92 Ok(()) => {
93 info!(view = %cert.view_number(), cert = type_name::<Cert<T, V, Th>>(), "certificate formed");
94 Some(cert)
95 },
96 Err(err) => {
97 warn!(view = %cert.view_number(), %err, "invalid certificate formed");
98 self.votes.push(vote);
99 self.recover()
100 },
101 }
102 }
103
104 pub fn min_votes(&self) -> usize {
112 let table = Cert::<T, V, Th>::stake_table(&self.membership);
113 let largest = table
114 .0
115 .iter()
116 .map(|peer| peer.stake_table_entry.stake())
117 .max()
118 .unwrap_or_default();
119 if largest.is_zero() {
120 return 1;
121 }
122 let nodes = table.0.len().max(1);
123 let votes = Cert::<T, V, Th>::threshold(&self.membership).div_ceil(largest);
124 usize::try_from(votes).unwrap_or(nodes).clamp(1, nodes)
125 }
126
127 fn recover(&mut self) -> Option<Cert<T, V, Th>> {
129 self.verify_votes = true;
130 self.accumulator.clear();
131 for vote in mem::take(&mut self.votes) {
132 if !self.is_valid_vote(&vote) {
133 continue;
134 }
135 if let Some(cert) = self.accumulator.accumulate(&vote, self.membership.clone()) {
136 debug_assert!(self.validate(&cert).is_ok());
137 info!(view = %cert.view_number(), cert = type_name::<Cert<T, V, Th>>(), "certificate formed");
138 return Some(cert);
139 }
140 }
141 None
142 }
143
144 fn validate(&self, cert: &Cert<T, V, Th>) -> anytrace::Result<()> {
146 let table = StakeTableEntries::from(Cert::<T, V, Th>::stake_table(&self.membership));
147 let thresh = Cert::<T, V, Th>::threshold(&self.membership);
148 verify_signatures(cert, &table.0, thresh, &self.upgrade_lock)
149 }
150
151 fn is_valid_vote(&self, vote: &V) -> bool {
153 let commit = match VersionedVoteData::new(
154 vote.date().clone(),
155 vote.view_number(),
156 &self.upgrade_lock,
157 ) {
158 Ok(data) => data.commit(),
159 Err(err) => {
160 warn!(%err, "failed to generate versioned vote data");
161 return false;
162 },
163 };
164 let valid = vote
165 .signing_key()
166 .validate(&vote.signature(), commit.as_ref());
167 if !valid {
168 warn!(
169 view = %vote.view_number(),
170 cert = type_name::<Cert<T, V, Th>>(),
171 signer = %vote.signing_key(),
172 "invalid vote"
173 );
174 }
175 valid
176 }
177}