hotshot_new_protocol/vote/
accumulate.rs1use std::{any::type_name, mem};
2
3use committable::Committable;
4use hotshot::types::SignatureKey;
5use hotshot_types::{
6 epoch_membership::EpochMembership,
7 message::UpgradeLock,
8 simple_vote::{HasEpoch, VersionedVoteData},
9 stake_table::StakeTableEntries,
10 traits::node_implementation::NodeType,
11 vote::{Certificate, Vote, VoteAccumulator},
12};
13use hotshot_utils::anytrace;
14use tracing::{info, warn};
15
16pub struct CheckedAccumulator<T, V, C>
23where
24 T: NodeType,
25 V: Vote<T>,
26 C: Certificate<T, V::Commitment, Voteable = V::Commitment> + HasEpoch,
27{
28 accumulator: VoteAccumulator<T, V, C>,
29
30 votes: Vec<V>,
32
33 verify_votes: bool,
37
38 membership: EpochMembership<T>,
39
40 upgrade_lock: UpgradeLock<T>,
41}
42
43impl<T, V, C> CheckedAccumulator<T, V, C>
44where
45 T: NodeType,
46 V: Vote<T>,
47 C: Certificate<T, V::Commitment, Voteable = V::Commitment> + HasEpoch,
48{
49 pub fn new(membership: EpochMembership<T>, lock: UpgradeLock<T>) -> Self {
50 Self {
51 accumulator: VoteAccumulator::new(lock.clone()),
52 votes: Vec::new(),
53 verify_votes: false,
54 membership,
55 upgrade_lock: lock,
56 }
57 }
58
59 pub fn add(&mut self, vote: V) -> Option<C> {
63 if self.verify_votes {
64 if !self.is_valid_vote(&vote) {
65 return None;
66 }
67 let cert = self
68 .accumulator
69 .accumulate(&vote, self.membership.clone())?;
70 debug_assert!(self.validate(&cert).is_ok());
71 info!(view = %cert.view_number(), cert = type_name::<C>(), "certificate formed");
72 return Some(cert);
73 }
74
75 let Some(cert) = self.accumulator.accumulate(&vote, self.membership.clone()) else {
76 self.votes.push(vote);
77 return None;
78 };
79
80 match self.validate(&cert) {
81 Ok(()) => {
82 info!(view = %cert.view_number(), cert = type_name::<C>(), "certificate formed");
83 Some(cert)
84 },
85 Err(err) => {
86 warn!(view = %cert.view_number(), %err, "invalid certificate formed");
87 self.votes.push(vote);
88 self.recover()
89 },
90 }
91 }
92
93 fn recover(&mut self) -> Option<C> {
95 self.verify_votes = true;
96 self.accumulator.clear();
97 for vote in mem::take(&mut self.votes) {
98 if !self.is_valid_vote(&vote) {
99 continue;
100 }
101 if let Some(cert) = self.accumulator.accumulate(&vote, self.membership.clone()) {
102 debug_assert!(self.validate(&cert).is_ok());
103 info!(view = %cert.view_number(), cert = type_name::<C>(), "certificate formed");
104 return Some(cert);
105 }
106 }
107 None
108 }
109
110 fn validate(&self, cert: &C) -> anytrace::Result<()> {
112 let table = StakeTableEntries::from(C::stake_table(&self.membership));
113 let thresh = C::threshold(&self.membership);
114 cert.is_valid_cert(&table.0, thresh, &self.upgrade_lock)
115 }
116
117 fn is_valid_vote(&self, vote: &V) -> bool {
119 let commit = match VersionedVoteData::new(
120 vote.date().clone(),
121 vote.view_number(),
122 &self.upgrade_lock,
123 ) {
124 Ok(data) => data.commit(),
125 Err(err) => {
126 warn!(%err, "failed to generate versioned vote data");
127 return false;
128 },
129 };
130 let valid = vote
131 .signing_key()
132 .validate(&vote.signature(), commit.as_ref());
133 if !valid {
134 warn!(
135 view = %vote.view_number(),
136 cert = type_name::<C>(),
137 signer = %vote.signing_key(),
138 "invalid vote"
139 );
140 }
141 valid
142 }
143}