hotshot_types/traits/
election.rs1use std::{collections::BTreeSet, fmt::Debug, sync::Arc};
9
10use alloy::primitives::U256;
11use async_broadcast::Receiver;
12use async_lock::RwLock;
13use committable::{Commitment, Committable};
14use hotshot_utils::anytrace::Result;
15
16use super::node_implementation::NodeType;
17use crate::{
18 data::Leaf2,
19 drb::DrbResult,
20 event::Event,
21 stake_table::HSStakeTable,
22 traits::{node_implementation::NodeImplementation, signature_key::StakeTableEntryType},
23 PeerConfig,
24};
25
26pub struct NoStakeTableHash;
27
28impl Committable for NoStakeTableHash {
29 fn commit(&self) -> Commitment<Self> {
30 Commitment::from_raw([0u8; 32])
31 }
32}
33
34pub trait Membership<TYPES: NodeType>: Debug + Send + Sync {
36 type Error: std::fmt::Display;
38
39 type Storage;
41
42 type StakeTableHash: Committable;
43
44 fn new<I: NodeImplementation<TYPES>>(
46 stake_committee_members: Vec<PeerConfig<TYPES>>,
49 da_committee_members: Vec<PeerConfig<TYPES>>,
50 storage: Self::Storage,
51 network: Arc<<I as NodeImplementation<TYPES>>::Network>,
52 public_key: TYPES::SignatureKey,
53 epoch_height: u64,
54 ) -> Self;
55
56 fn set_external_channel(
57 &mut self,
58 _external_channel: Receiver<Event<TYPES>>,
59 ) -> impl std::future::Future<Output = ()> + Send {
60 async {}
61 }
62
63 fn total_stake(&self, epoch: Option<TYPES::Epoch>) -> U256 {
64 self.stake_table(epoch)
65 .iter()
66 .fold(U256::ZERO, |acc, entry| {
67 acc + entry.stake_table_entry.stake()
68 })
69 }
70
71 fn total_da_stake(&self, epoch: Option<TYPES::Epoch>) -> U256 {
72 self.da_stake_table(epoch)
73 .iter()
74 .fold(U256::ZERO, |acc, entry| {
75 acc + entry.stake_table_entry.stake()
76 })
77 }
78
79 fn stake_table(&self, epoch: Option<TYPES::Epoch>) -> HSStakeTable<TYPES>;
81
82 fn da_stake_table(&self, epoch: Option<TYPES::Epoch>) -> HSStakeTable<TYPES>;
84
85 fn committee_members(
87 &self,
88 view_number: TYPES::View,
89 epoch: Option<TYPES::Epoch>,
90 ) -> BTreeSet<TYPES::SignatureKey>;
91
92 fn da_committee_members(
94 &self,
95 view_number: TYPES::View,
96 epoch: Option<TYPES::Epoch>,
97 ) -> BTreeSet<TYPES::SignatureKey>;
98
99 fn stake(
102 &self,
103 pub_key: &TYPES::SignatureKey,
104 epoch: Option<TYPES::Epoch>,
105 ) -> Option<PeerConfig<TYPES>>;
106
107 fn da_stake(
110 &self,
111 pub_key: &TYPES::SignatureKey,
112 epoch: Option<TYPES::Epoch>,
113 ) -> Option<PeerConfig<TYPES>>;
114
115 fn has_stake(&self, pub_key: &TYPES::SignatureKey, epoch: Option<TYPES::Epoch>) -> bool;
117
118 fn has_da_stake(&self, pub_key: &TYPES::SignatureKey, epoch: Option<TYPES::Epoch>) -> bool;
120
121 fn leader(
129 &self,
130 view: TYPES::View,
131 epoch: Option<TYPES::Epoch>,
132 ) -> Result<TYPES::SignatureKey> {
133 use hotshot_utils::anytrace::*;
134
135 self.lookup_leader(view, epoch).wrap().context(info!(
136 "Failed to get leader for view {view} in epoch {epoch:?}"
137 ))
138 }
139
140 fn lookup_leader(
148 &self,
149 view: TYPES::View,
150 epoch: Option<TYPES::Epoch>,
151 ) -> std::result::Result<TYPES::SignatureKey, Self::Error>;
152
153 fn total_nodes(&self, epoch: Option<TYPES::Epoch>) -> usize;
155
156 fn da_total_nodes(&self, epoch: Option<TYPES::Epoch>) -> usize;
158
159 fn success_threshold(&self, epoch: Option<<TYPES as NodeType>::Epoch>) -> U256 {
161 let total_stake = self.total_stake(epoch);
162 let one = U256::ONE;
163 let two = U256::from(2);
164 let three = U256::from(3);
165 if total_stake < U256::MAX / two {
166 ((total_stake * two) / three) + one
167 } else {
168 ((total_stake / three) * two) + two
169 }
170 }
171
172 fn da_success_threshold(&self, epoch: Option<<TYPES as NodeType>::Epoch>) -> U256 {
174 let total_stake = self.total_da_stake(epoch);
175 let one = U256::ONE;
176 let two = U256::from(2);
177 let three = U256::from(3);
178
179 if total_stake < U256::MAX / two {
180 ((total_stake * two) / three) + one
181 } else {
182 ((total_stake / three) * two) + two
183 }
184 }
185
186 fn failure_threshold(&self, epoch: Option<<TYPES as NodeType>::Epoch>) -> U256 {
188 let total_stake = self.total_stake(epoch);
189 let one = U256::ONE;
190 let three = U256::from(3);
191
192 (total_stake / three) + one
193 }
194
195 fn upgrade_threshold(&self, epoch: Option<<TYPES as NodeType>::Epoch>) -> U256 {
197 let total_stake = self.total_stake(epoch);
198 let nine = U256::from(9);
199 let ten = U256::from(10);
200
201 let normal_threshold = self.success_threshold(epoch);
202 let higher_threshold = if total_stake < U256::MAX / nine {
203 (total_stake * nine) / ten
204 } else {
205 (total_stake / ten) * nine
206 };
207
208 std::cmp::max(higher_threshold, normal_threshold)
209 }
210
211 fn has_stake_table(&self, epoch: TYPES::Epoch) -> bool;
213
214 fn has_randomized_stake_table(&self, epoch: TYPES::Epoch) -> anyhow::Result<bool>;
216
217 fn get_epoch_root(
220 _membership: Arc<RwLock<Self>>,
221 _block_height: u64,
222 _epoch: TYPES::Epoch,
223 ) -> impl std::future::Future<Output = anyhow::Result<Leaf2<TYPES>>> + Send;
224
225 fn get_epoch_drb(
227 _membership: Arc<RwLock<Self>>,
228 _epoch: TYPES::Epoch,
229 ) -> impl std::future::Future<Output = anyhow::Result<DrbResult>> + Send;
230
231 fn add_epoch_root(
233 _membership: Arc<RwLock<Self>>,
234 _epoch: TYPES::Epoch,
235 _block_header: TYPES::BlockHeader,
236 ) -> impl std::future::Future<Output = anyhow::Result<()>> + Send;
237
238 fn add_drb_result(&mut self, _epoch: TYPES::Epoch, _drb_result: DrbResult);
241
242 fn set_first_epoch(&mut self, _epoch: TYPES::Epoch, _initial_drb_result: DrbResult);
247
248 fn first_epoch(&self) -> Option<TYPES::Epoch>;
250
251 fn stake_table_hash(&self, _epoch: TYPES::Epoch) -> Option<Commitment<Self::StakeTableHash>> {
254 None
255 }
256
257 fn add_da_committee(&mut self, _first_epoch: u64, _da_committee: Vec<PeerConfig<TYPES>>) {}
258}
259
260pub fn membership_spawn_add_epoch_root<TYPES: NodeType>(
261 membership: Arc<RwLock<impl Membership<TYPES> + 'static>>,
262 epoch: TYPES::Epoch,
263 block_header: TYPES::BlockHeader,
264) {
265 tokio::spawn(async move {
266 if let Err(e) = Membership::<TYPES>::add_epoch_root(membership, epoch, block_header).await {
267 tracing::error!("Failed to add epoch root for epoch {epoch}: {e}");
268 }
269 });
270}