hotshot_types/traits/
election.rs1use std::{fmt::Debug, sync::Arc};
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::{one_honest_threshold, supermajority_threshold, upgrade_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) -> 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 supermajority_threshold(self.total_da_stake())
133 }
134
135 fn failure_threshold(&self) -> U256 {
137 one_honest_threshold(self.total_stake())
138 }
139
140 fn upgrade_threshold(&self) -> U256 {
142 upgrade_threshold(self.total_stake())
143 }
144
145 fn leader(&self, view: ViewNumber) -> anytrace::Result<T::SignatureKey> {
148 use hotshot_utils::anytrace::*;
149 let epoch = self.epoch();
150 self.lookup_leader(view).wrap().context(info!(
151 "Failed to get leader for view {view} in epoch {epoch}"
152 ))
153 }
154}
155
156#[derive(Debug)]
162pub struct GenesisStakeTables<T: NodeType> {
163 stake_table: Vec<PeerConfig<T>>,
164 da_stake_table: Vec<PeerConfig<T>>,
165 committee_members: Vec<T::SignatureKey>,
167 da_committee_members: Vec<T::SignatureKey>,
169}
170
171impl<T: NodeType> GenesisStakeTables<T> {
172 pub fn new(stake_table: Vec<PeerConfig<T>>, da_stake_table: Vec<PeerConfig<T>>) -> Self {
174 Self {
175 committee_members: keys(&stake_table),
176 da_committee_members: keys(&da_stake_table),
177 stake_table,
178 da_stake_table,
179 }
180 }
181
182 pub fn stake_table(&self) -> impl ExactSizeIterator<Item = &PeerConfig<T>> + Send + '_ {
183 self.stake_table.iter()
184 }
185
186 pub fn da_stake_table(&self) -> impl ExactSizeIterator<Item = &PeerConfig<T>> + Send + '_ {
187 self.da_stake_table.iter()
188 }
189
190 pub fn committee_members(&self) -> impl ExactSizeIterator<Item = &T::SignatureKey> + Send + '_ {
191 self.committee_members.iter()
192 }
193
194 pub fn da_committee_members(
195 &self,
196 ) -> impl ExactSizeIterator<Item = &T::SignatureKey> + Send + '_ {
197 self.da_committee_members.iter()
198 }
199
200 pub fn stake(&self, key: &T::SignatureKey) -> Option<PeerConfig<T>> {
201 entry(&self.committee_members, &self.stake_table, key)
202 }
203
204 pub fn da_stake(&self, key: &T::SignatureKey) -> Option<PeerConfig<T>> {
205 entry(&self.da_committee_members, &self.da_stake_table, key)
206 }
207
208 pub fn has_stake(&self, key: &T::SignatureKey) -> bool {
209 self.stake(key)
210 .is_some_and(|p| p.stake_table_entry.stake() > U256::ZERO)
211 }
212
213 pub fn has_da_stake(&self, key: &T::SignatureKey) -> bool {
214 self.da_stake(key)
215 .is_some_and(|p| p.stake_table_entry.stake() > U256::ZERO)
216 }
217
218 pub fn leader(&self, view: ViewNumber) -> anytrace::Result<T::SignatureKey> {
224 if self.committee_members.is_empty() {
225 return Err(anytrace::info!(
226 "Failed to get leader for view {view}: the genesis stake table is empty"
227 ));
228 }
229 let index = *view as usize % self.committee_members.len();
230 Ok(self.committee_members[index].clone())
231 }
232
233 pub fn total_nodes(&self) -> usize {
234 self.stake_table.len()
235 }
236
237 pub fn da_total_nodes(&self) -> usize {
238 self.da_stake_table.len()
239 }
240
241 pub fn success_threshold(&self) -> U256 {
242 supermajority_threshold(total_stake(&self.stake_table))
243 }
244
245 pub fn da_success_threshold(&self) -> U256 {
246 supermajority_threshold(total_stake(&self.da_stake_table))
247 }
248
249 pub fn failure_threshold(&self) -> U256 {
250 one_honest_threshold(total_stake(&self.stake_table))
251 }
252
253 pub fn upgrade_threshold(&self) -> U256 {
254 upgrade_threshold(total_stake(&self.stake_table))
255 }
256}
257
258fn keys<T: NodeType>(table: &[PeerConfig<T>]) -> Vec<T::SignatureKey> {
259 table
260 .iter()
261 .map(|p| p.stake_table_entry.public_key())
262 .collect()
263}
264
265fn entry<T: NodeType>(
267 keys: &[T::SignatureKey],
268 table: &[PeerConfig<T>],
269 key: &T::SignatureKey,
270) -> Option<PeerConfig<T>> {
271 let index = keys.iter().position(|k| k == key)?;
272 Some(table[index].clone())
273}
274
275fn total_stake<T: NodeType>(table: &[PeerConfig<T>]) -> U256 {
276 table
277 .iter()
278 .fold(U256::ZERO, |acc, p| acc + p.stake_table_entry.stake())
279}
280
281pub trait Membership<T: NodeType>: Debug + Send + Sync {
289 type Error: std::error::Error + Send + Sync + 'static;
290
291 type Snapshot: MembershipSnapshot<T, Error = Self::Error>;
293
294 fn snapshot(&self, epoch: EpochNumber) -> Option<Self::Snapshot>;
298
299 fn genesis_stake_tables(&self) -> Arc<GenesisStakeTables<T>>;
301
302 fn first_epoch(&self) -> EpochNumber;
304
305 fn highest_known_epoch(&self) -> Option<EpochNumber>;
310
311 fn get_epoch_root(
314 &self,
315 e: EpochNumber,
316 coordinator: &EpochMembershipCoordinator<T>,
317 ) -> impl Future<Output = Result<Leaf2<T>, Self::Error>> + Send;
318
319 fn get_epoch_drb(
321 &self,
322 e: EpochNumber,
323 coordinator: &EpochMembershipCoordinator<T>,
324 ) -> impl Future<Output = Result<DrbResult, Self::Error>> + Send;
325
326 fn add_epoch_root(
328 &self,
329 h: T::BlockHeader,
330 coordinator: &EpochMembershipCoordinator<T>,
331 ) -> impl Future<Output = Result<(), Self::Error>> + Send;
332
333 fn add_drb_result(&self, e: EpochNumber, d: DrbResult);
335
336 fn load_stake_table(&self, epoch: EpochNumber) -> impl Future<Output = bool> + Send;
340
341 fn add_da_committee(&self, first_epoch: EpochNumber, da_committee: Vec<PeerConfig<T>>);
343}