1use std::{
8 cmp::max,
9 collections::{BTreeMap, BTreeSet},
10};
11
12use alloy::primitives::U256;
13use hotshot_types::{
14 drb::DrbResult,
15 stake_table::HSStakeTable,
16 traits::{
17 election::Membership,
18 node_implementation::NodeType,
19 signature_key::{SignatureKey, StakeTableEntryType},
20 },
21 PeerConfig,
22};
23use hotshot_utils::anytrace::Result;
24
25type EligibleLeaders<T> = (Vec<PeerConfig<T>>, Vec<PeerConfig<T>>);
27
28type StakeTables<T> = (HSStakeTable<T>, HSStakeTable<T>);
30
31type IndexedStakeTables<T> = (
33 BTreeMap<<T as NodeType>::SignatureKey, PeerConfig<T>>,
34 BTreeMap<<T as NodeType>::SignatureKey, PeerConfig<T>>,
35);
36
37#[derive(Clone, Debug, Eq, PartialEq, Hash)]
38pub struct TwoStaticCommittees<T: NodeType> {
40 eligible_leaders: EligibleLeaders<T>,
44
45 stake_table: StakeTables<T>,
47
48 da_stake_table: StakeTables<T>,
50
51 indexed_stake_table: IndexedStakeTables<T>,
53
54 indexed_da_stake_table: IndexedStakeTables<T>,
56
57 first_epoch: Option<T::Epoch>,
60}
61
62impl<TYPES: NodeType> Membership<TYPES> for TwoStaticCommittees<TYPES> {
63 type Error = hotshot_utils::anytrace::Error;
64 fn new(committee_members: Vec<PeerConfig<TYPES>>, da_members: Vec<PeerConfig<TYPES>>) -> Self {
66 let eligible_leaders: Vec<PeerConfig<TYPES>> = committee_members
68 .clone()
69 .into_iter()
70 .filter(|member| member.stake_table_entry.stake() > U256::ZERO)
71 .collect();
72
73 let eligible_leaders1 = eligible_leaders
74 .iter()
75 .enumerate()
76 .filter(|(idx, _)| idx % 2 == 0)
77 .map(|(_, leader)| leader.clone())
78 .collect();
79 let eligible_leaders2 = eligible_leaders
80 .iter()
81 .enumerate()
82 .filter(|(idx, _)| idx % 2 == 1)
83 .map(|(_, leader)| leader.clone())
84 .collect();
85
86 let members: Vec<PeerConfig<TYPES>> = committee_members
88 .clone()
89 .into_iter()
90 .filter(|member| member.stake_table_entry.stake() > U256::ZERO)
91 .collect();
92
93 let members1: Vec<PeerConfig<TYPES>> = members
94 .iter()
95 .enumerate()
96 .filter(|(idx, _)| idx % 2 == 0)
97 .map(|(_, leader)| leader.clone())
98 .collect();
99 let members2: Vec<PeerConfig<TYPES>> = members
100 .iter()
101 .enumerate()
102 .filter(|(idx, _)| idx % 2 == 1)
103 .map(|(_, leader)| leader.clone())
104 .collect();
105
106 let da_members: Vec<PeerConfig<TYPES>> = da_members
108 .clone()
109 .into_iter()
110 .filter(|member| member.stake_table_entry.stake() > U256::ZERO)
111 .collect();
112
113 let da_members1: Vec<PeerConfig<TYPES>> = da_members
114 .iter()
115 .enumerate()
116 .filter(|(idx, _)| idx % 2 == 0)
117 .map(|(_, leader)| leader.clone())
118 .collect();
119 let da_members2: Vec<PeerConfig<TYPES>> = da_members
120 .iter()
121 .enumerate()
122 .filter(|(idx, _)| idx % 2 == 1)
123 .map(|(_, leader)| leader.clone())
124 .collect();
125
126 let indexed_stake_table1: BTreeMap<TYPES::SignatureKey, _> = members1
128 .iter()
129 .map(|member| {
130 (
131 TYPES::SignatureKey::public_key(&member.stake_table_entry),
132 member.clone(),
133 )
134 })
135 .collect();
136
137 let indexed_stake_table2: BTreeMap<TYPES::SignatureKey, _> = members2
138 .iter()
139 .map(|member| {
140 (
141 TYPES::SignatureKey::public_key(&member.stake_table_entry),
142 member.clone(),
143 )
144 })
145 .collect();
146
147 let indexed_da_stake_table1: BTreeMap<TYPES::SignatureKey, _> = da_members1
149 .iter()
150 .map(|member| {
151 (
152 TYPES::SignatureKey::public_key(&member.stake_table_entry),
153 member.clone(),
154 )
155 })
156 .collect();
157
158 let indexed_da_stake_table2: BTreeMap<TYPES::SignatureKey, _> = da_members2
159 .iter()
160 .map(|member| {
161 (
162 TYPES::SignatureKey::public_key(&member.stake_table_entry),
163 member.clone(),
164 )
165 })
166 .collect();
167
168 Self {
169 eligible_leaders: (eligible_leaders1, eligible_leaders2),
170 stake_table: (members1.into(), members2.into()),
171 da_stake_table: (da_members1.into(), da_members2.into()),
172 indexed_stake_table: (indexed_stake_table1, indexed_stake_table2),
173 indexed_da_stake_table: (indexed_da_stake_table1, indexed_da_stake_table2),
174 first_epoch: None,
175 }
176 }
177
178 fn stake_table(&self, epoch: Option<<TYPES as NodeType>::Epoch>) -> HSStakeTable<TYPES> {
180 let epoch = epoch.expect("epochs cannot be disabled with TwoStaticCommittees");
181 if *epoch != 0 && *epoch % 2 == 0 {
182 self.stake_table.0.clone()
183 } else {
184 self.stake_table.1.clone()
185 }
186 }
187
188 fn da_stake_table(&self, epoch: Option<<TYPES as NodeType>::Epoch>) -> HSStakeTable<TYPES> {
190 let epoch = epoch.expect("epochs cannot be disabled with TwoStaticCommittees");
191 if *epoch != 0 && *epoch % 2 == 0 {
192 self.da_stake_table.0.clone()
193 } else {
194 self.da_stake_table.1.clone()
195 }
196 }
197
198 fn committee_members(
200 &self,
201 _view_number: <TYPES as NodeType>::View,
202 epoch: Option<<TYPES as NodeType>::Epoch>,
203 ) -> BTreeSet<<TYPES as NodeType>::SignatureKey> {
204 let epoch = epoch.expect("epochs cannot be disabled with TwoStaticCommittees");
205 if *epoch != 0 && *epoch % 2 == 0 {
206 self.stake_table
207 .0
208 .iter()
209 .map(|sc| TYPES::SignatureKey::public_key(&sc.stake_table_entry))
210 .collect()
211 } else {
212 self.stake_table
213 .1
214 .iter()
215 .map(|sc| TYPES::SignatureKey::public_key(&sc.stake_table_entry))
216 .collect()
217 }
218 }
219
220 fn da_committee_members(
222 &self,
223 _view_number: <TYPES as NodeType>::View,
224 epoch: Option<<TYPES as NodeType>::Epoch>,
225 ) -> BTreeSet<<TYPES as NodeType>::SignatureKey> {
226 let epoch = epoch.expect("epochs cannot be disabled with TwoStaticCommittees");
227 if *epoch != 0 && *epoch % 2 == 0 {
228 self.da_stake_table
229 .0
230 .iter()
231 .map(|da| TYPES::SignatureKey::public_key(&da.stake_table_entry))
232 .collect()
233 } else {
234 self.da_stake_table
235 .1
236 .iter()
237 .map(|da| TYPES::SignatureKey::public_key(&da.stake_table_entry))
238 .collect()
239 }
240 }
241
242 fn stake(
244 &self,
245 pub_key: &<TYPES as NodeType>::SignatureKey,
246 epoch: Option<<TYPES as NodeType>::Epoch>,
247 ) -> Option<PeerConfig<TYPES>> {
248 let epoch = epoch.expect("epochs cannot be disabled with TwoStaticCommittees");
250 if *epoch != 0 && *epoch % 2 == 0 {
251 self.indexed_stake_table.0.get(pub_key).cloned()
252 } else {
253 self.indexed_stake_table.1.get(pub_key).cloned()
254 }
255 }
256
257 fn da_stake(
259 &self,
260 pub_key: &<TYPES as NodeType>::SignatureKey,
261 epoch: Option<<TYPES as NodeType>::Epoch>,
262 ) -> Option<PeerConfig<TYPES>> {
263 let epoch = epoch.expect("epochs cannot be disabled with TwoStaticCommittees");
265 if *epoch != 0 && *epoch % 2 == 0 {
266 self.indexed_da_stake_table.0.get(pub_key).cloned()
267 } else {
268 self.indexed_da_stake_table.1.get(pub_key).cloned()
269 }
270 }
271
272 fn has_stake(
274 &self,
275 pub_key: &<TYPES as NodeType>::SignatureKey,
276 epoch: Option<<TYPES as NodeType>::Epoch>,
277 ) -> bool {
278 let epoch = epoch.expect("epochs cannot be disabled with TwoStaticCommittees");
279 if *epoch != 0 && *epoch % 2 == 0 {
280 self.indexed_stake_table
281 .0
282 .get(pub_key)
283 .is_some_and(|x| x.stake_table_entry.stake() > U256::ZERO)
284 } else {
285 self.indexed_stake_table
286 .1
287 .get(pub_key)
288 .is_some_and(|x| x.stake_table_entry.stake() > U256::ZERO)
289 }
290 }
291 fn has_da_stake(
293 &self,
294 pub_key: &<TYPES as NodeType>::SignatureKey,
295 epoch: Option<<TYPES as NodeType>::Epoch>,
296 ) -> bool {
297 let epoch = epoch.expect("epochs cannot be disabled with TwoStaticCommittees");
298 if *epoch != 0 && *epoch % 2 == 0 {
299 self.indexed_da_stake_table
300 .0
301 .get(pub_key)
302 .is_some_and(|x| x.stake_table_entry.stake() > U256::ZERO)
303 } else {
304 self.indexed_da_stake_table
305 .1
306 .get(pub_key)
307 .is_some_and(|x| x.stake_table_entry.stake() > U256::ZERO)
308 }
309 }
310
311 fn lookup_leader(
313 &self,
314 view_number: <TYPES as NodeType>::View,
315 epoch: Option<<TYPES as NodeType>::Epoch>,
316 ) -> Result<TYPES::SignatureKey> {
317 let epoch = epoch.expect("epochs cannot be disabled with TwoStaticCommittees");
318 if *epoch != 0 && *epoch % 2 == 0 {
319 #[allow(clippy::cast_possible_truncation)]
320 let index = *view_number as usize % self.eligible_leaders.0.len();
321 let res = self.eligible_leaders.0[index].clone();
322 Ok(TYPES::SignatureKey::public_key(&res.stake_table_entry))
323 } else {
324 #[allow(clippy::cast_possible_truncation)]
325 let index = *view_number as usize % self.eligible_leaders.1.len();
326 let res = self.eligible_leaders.1[index].clone();
327 Ok(TYPES::SignatureKey::public_key(&res.stake_table_entry))
328 }
329 }
330
331 fn total_nodes(&self, epoch: Option<<TYPES as NodeType>::Epoch>) -> usize {
333 let epoch = epoch.expect("epochs cannot be disabled with TwoStaticCommittees");
334 if *epoch != 0 && *epoch % 2 == 0 {
335 self.stake_table.0.len()
336 } else {
337 self.stake_table.1.len()
338 }
339 }
340
341 fn da_total_nodes(&self, epoch: Option<<TYPES as NodeType>::Epoch>) -> usize {
343 let epoch = epoch.expect("epochs cannot be disabled with TwoStaticCommittees");
344 if *epoch != 0 && *epoch % 2 == 0 {
345 self.da_stake_table.0.len()
346 } else {
347 self.da_stake_table.1.len()
348 }
349 }
350
351 fn success_threshold(&self, epoch: Option<<TYPES as NodeType>::Epoch>) -> U256 {
353 let epoch = epoch.expect("epochs cannot be disabled with TwoStaticCommittees");
354 if *epoch != 0 && *epoch % 2 == 0 {
355 U256::from(((self.stake_table.0.len() as u64 * 2) / 3) + 1)
356 } else {
357 U256::from(((self.stake_table.1.len() as u64 * 2) / 3) + 1)
358 }
359 }
360
361 fn da_success_threshold(&self, epoch: Option<TYPES::Epoch>) -> U256 {
363 let epoch = epoch.expect("epochs cannot be disabled with TwoStaticCommittees");
364 if *epoch != 0 && *epoch % 2 == 0 {
365 U256::from(((self.da_stake_table.0.len() as u64 * 2) / 3) + 1)
366 } else {
367 U256::from(((self.da_stake_table.1.len() as u64 * 2) / 3) + 1)
368 }
369 }
370
371 fn failure_threshold(&self, epoch: Option<<TYPES as NodeType>::Epoch>) -> U256 {
373 let epoch = epoch.expect("epochs cannot be disabled with TwoStaticCommittees");
374 if *epoch != 0 && *epoch % 2 == 0 {
375 U256::from(((self.stake_table.0.len() as u64) / 3) + 1)
376 } else {
377 U256::from(((self.stake_table.1.len() as u64) / 3) + 1)
378 }
379 }
380
381 fn upgrade_threshold(&self, epoch: Option<<TYPES as NodeType>::Epoch>) -> U256 {
383 let epoch = epoch.expect("epochs cannot be disabled with TwoStaticCommittees");
384 if *epoch != 0 && *epoch % 2 == 0 {
385 U256::from(max(
386 (self.stake_table.0.len() as u64 * 9) / 10,
387 ((self.stake_table.0.len() as u64 * 2) / 3) + 1,
388 ))
389 } else {
390 U256::from(max(
391 (self.stake_table.1.len() as u64 * 9) / 10,
392 ((self.stake_table.1.len() as u64 * 2) / 3) + 1,
393 ))
394 }
395 }
396 fn has_stake_table(&self, _epoch: TYPES::Epoch) -> bool {
397 true
398 }
399 fn has_randomized_stake_table(&self, _epoch: TYPES::Epoch) -> anyhow::Result<bool> {
400 Ok(true)
401 }
402
403 fn add_drb_result(&mut self, _epoch: <TYPES as NodeType>::Epoch, _drb_result: DrbResult) {}
404
405 fn set_first_epoch(&mut self, epoch: TYPES::Epoch, _initial_drb_result: DrbResult) {
406 self.first_epoch = Some(epoch);
407 }
408
409 fn first_epoch(&self) -> Option<TYPES::Epoch> {
410 self.first_epoch
411 }
412}