hotshot_types/traits/
election.rs1use std::fmt::Debug;
17
18use alloy_primitives::U256;
19use committable::{Commitment, Committable};
20use hotshot_utils::anytrace;
21
22use super::node_implementation::NodeType;
23use crate::{
24 PeerConfig,
25 data::{EpochNumber, Leaf2, ViewNumber},
26 drb::DrbResult,
27 epoch_membership::EpochMembershipCoordinator,
28 stake_table::supermajority_threshold,
29 traits::signature_key::StakeTableEntryType,
30};
31
32pub struct NoStakeTableHash;
33
34impl Committable for NoStakeTableHash {
35 fn commit(&self) -> Commitment<Self> {
36 Commitment::from_raw([0u8; 32])
37 }
38}
39
40pub trait MembershipSnapshot<T: NodeType>: Clone + Send + Sync {
42 type Error: std::error::Error + Send + Sync + 'static;
43 type StakeTableHash: Committable;
44
45 fn epoch(&self) -> EpochNumber;
47
48 fn first_epoch(&self) -> Option<EpochNumber>;
50
51 fn has_drb(&self) -> bool;
54
55 fn stake_table(&self) -> impl ExactSizeIterator<Item = &PeerConfig<T>> + Send;
62
63 fn da_stake_table(&self) -> impl ExactSizeIterator<Item = &PeerConfig<T>> + Send;
65
66 fn committee_members(
68 &self,
69 view: ViewNumber,
70 ) -> impl ExactSizeIterator<Item = &T::SignatureKey> + Send;
71
72 fn da_committee_members(
74 &self,
75 view: ViewNumber,
76 ) -> impl ExactSizeIterator<Item = &T::SignatureKey> + Send;
77
78 fn stake(&self, key: &T::SignatureKey) -> Option<PeerConfig<T>>;
81
82 fn da_stake(&self, key: &T::SignatureKey) -> Option<PeerConfig<T>>;
84
85 fn has_stake(&self, key: &T::SignatureKey) -> bool;
87
88 fn has_da_stake(&self, key: &T::SignatureKey) -> bool;
90
91 fn lookup_leader(&self, view: ViewNumber) -> Result<T::SignatureKey, Self::Error>;
97
98 fn stake_table_hash(&self) -> Option<Commitment<Self::StakeTableHash>> {
100 None
101 }
102
103 fn total_nodes(&self) -> usize {
105 self.stake_table().len()
106 }
107
108 fn da_total_nodes(&self) -> usize {
110 self.da_stake_table().len()
111 }
112
113 fn total_stake(&self) -> U256 {
115 self.stake_table()
116 .fold(U256::ZERO, |acc, e| acc + e.stake_table_entry.stake())
117 }
118
119 fn total_da_stake(&self) -> U256 {
121 self.da_stake_table()
122 .fold(U256::ZERO, |acc, e| acc + e.stake_table_entry.stake())
123 }
124
125 fn success_threshold(&self) -> U256 {
127 supermajority_threshold(self.total_stake())
128 }
129
130 fn da_success_threshold(&self) -> U256 {
132 let total = self.total_da_stake();
133 let one = U256::ONE;
134 let two = U256::from(2);
135 let three = U256::from(3);
136 if total < U256::MAX / two {
137 ((total * two) / three) + one
138 } else {
139 ((total / three) * two) + two
140 }
141 }
142
143 fn failure_threshold(&self) -> U256 {
145 let total = self.total_stake();
146 (total / U256::from(3)) + U256::ONE
147 }
148
149 fn upgrade_threshold(&self) -> U256 {
151 let total = self.total_stake();
152 let nine = U256::from(9);
153 let ten = U256::from(10);
154 let normal = self.success_threshold();
155 let higher = if total < U256::MAX / nine {
156 (total * nine) / ten
157 } else {
158 (total / ten) * nine
159 };
160 std::cmp::max(higher, normal)
161 }
162
163 fn leader(&self, view: ViewNumber) -> anytrace::Result<T::SignatureKey> {
166 use hotshot_utils::anytrace::*;
167 let epoch = self.epoch();
168 self.lookup_leader(view).wrap().context(info!(
169 "Failed to get leader for view {view} in epoch {epoch}"
170 ))
171 }
172}
173
174pub trait NonEpochMembershipSnapshot<T: NodeType>: Clone + Send + Sync {
179 type Error: std::error::Error + Send + Sync + 'static;
180
181 fn stake_table(&self) -> impl ExactSizeIterator<Item = &PeerConfig<T>> + Send + '_;
182 fn da_stake_table(&self) -> impl ExactSizeIterator<Item = &PeerConfig<T>> + Send + '_;
183 fn committee_members(
184 &self,
185 view: ViewNumber,
186 ) -> impl ExactSizeIterator<Item = &T::SignatureKey> + Send + '_;
187 fn da_committee_members(
188 &self,
189 view: ViewNumber,
190 ) -> impl ExactSizeIterator<Item = &T::SignatureKey> + Send + '_;
191 fn stake(&self, key: &T::SignatureKey) -> Option<PeerConfig<T>>;
192 fn da_stake(&self, key: &T::SignatureKey) -> Option<PeerConfig<T>>;
193 fn has_stake(&self, key: &T::SignatureKey) -> bool;
194 fn has_da_stake(&self, key: &T::SignatureKey) -> bool;
195 fn lookup_leader(&self, view: ViewNumber) -> Result<T::SignatureKey, Self::Error>;
196
197 fn total_nodes(&self) -> usize {
198 self.stake_table().len()
199 }
200
201 fn da_total_nodes(&self) -> usize {
202 self.da_stake_table().len()
203 }
204
205 fn total_stake(&self) -> U256 {
206 self.stake_table()
207 .fold(U256::ZERO, |acc, e| acc + e.stake_table_entry.stake())
208 }
209
210 fn total_da_stake(&self) -> U256 {
211 self.da_stake_table()
212 .fold(U256::ZERO, |acc, e| acc + e.stake_table_entry.stake())
213 }
214
215 fn success_threshold(&self) -> U256 {
216 supermajority_threshold(self.total_stake())
217 }
218
219 fn da_success_threshold(&self) -> U256 {
220 let total = self.total_da_stake();
221 let one = U256::ONE;
222 let two = U256::from(2);
223 let three = U256::from(3);
224 if total < U256::MAX / two {
225 ((total * two) / three) + one
226 } else {
227 ((total / three) * two) + two
228 }
229 }
230
231 fn failure_threshold(&self) -> U256 {
232 let total = self.total_stake();
233 (total / U256::from(3)) + U256::ONE
234 }
235
236 fn upgrade_threshold(&self) -> U256 {
237 let total = self.total_stake();
238 let nine = U256::from(9);
239 let ten = U256::from(10);
240 let normal = self.success_threshold();
241 let higher = if total < U256::MAX / nine {
242 (total * nine) / ten
243 } else {
244 (total / ten) * nine
245 };
246 std::cmp::max(higher, normal)
247 }
248
249 fn leader(&self, view: ViewNumber) -> anytrace::Result<T::SignatureKey> {
250 use hotshot_utils::anytrace::*;
251 self.lookup_leader(view)
252 .wrap()
253 .context(info!("Failed to get leader for view {view} (non-epoch)"))
254 }
255}
256
257pub trait Membership<T: NodeType>: Debug + Send + Sync {
266 type Error: std::error::Error + Send + Sync + 'static;
267
268 type Snapshot: MembershipSnapshot<T, Error = Self::Error>;
270
271 type NonEpochSnapshot: NonEpochMembershipSnapshot<T, Error = Self::Error>;
273
274 fn snapshot(&self, epoch: EpochNumber) -> Option<Self::Snapshot>;
278
279 fn non_epoch_snapshot(&self) -> Self::NonEpochSnapshot;
281
282 fn first_epoch(&self) -> Option<EpochNumber>;
284
285 fn highest_known_epoch(&self) -> Option<EpochNumber> {
289 None
290 }
291
292 fn get_epoch_root(
295 &self,
296 e: EpochNumber,
297 coordinator: &EpochMembershipCoordinator<T>,
298 ) -> impl Future<Output = Result<Leaf2<T>, Self::Error>> + Send;
299
300 fn get_epoch_drb(
302 &self,
303 e: EpochNumber,
304 coordinator: &EpochMembershipCoordinator<T>,
305 ) -> impl Future<Output = Result<DrbResult, Self::Error>> + Send;
306
307 fn add_epoch_root(
309 &self,
310 h: T::BlockHeader,
311 coordinator: &EpochMembershipCoordinator<T>,
312 ) -> impl Future<Output = Result<(), Self::Error>> + Send;
313
314 fn add_drb_result(&self, e: EpochNumber, d: DrbResult);
316
317 fn set_first_epoch(&self, e: EpochNumber, r: DrbResult);
322
323 fn add_da_committee(&self, first_epoch: EpochNumber, da_committee: Vec<PeerConfig<T>>);
325}