hotshot/traits/election/
static_committee_leader_two_views.rs

1// Copyright (c) 2021-2024 Espresso Systems (espressosys.com)
2// This file is part of the HotShot repository.
3
4// You should have received a copy of the MIT License
5// along with the HotShot repository. If not, see <https://mit-license.org/>.
6
7use std::collections::{BTreeMap, BTreeSet};
8
9use alloy::primitives::U256;
10use hotshot_types::{
11    drb::DrbResult,
12    stake_table::HSStakeTable,
13    traits::{
14        election::Membership,
15        node_implementation::NodeType,
16        signature_key::{SignatureKey, StakeTableEntryType},
17    },
18    PeerConfig,
19};
20use hotshot_utils::anytrace::Result;
21
22#[derive(Clone, Debug, Eq, PartialEq, Hash)]
23
24/// The static committee election
25pub struct StaticCommitteeLeaderForTwoViews<T: NodeType> {
26    /// The nodes eligible for leadership.
27    /// NOTE: This is currently a hack because the DA leader needs to be the quorum
28    /// leader but without voting rights.
29    eligible_leaders: Vec<PeerConfig<T>>,
30
31    /// The nodes on the committee and their stake
32    stake_table: HSStakeTable<T>,
33
34    /// The nodes on the committee and their stake
35    da_stake_table: HSStakeTable<T>,
36
37    /// The nodes on the committee and their stake, indexed by public key
38    indexed_stake_table: BTreeMap<T::SignatureKey, PeerConfig<T>>,
39
40    /// The nodes on the committee and their stake, indexed by public key
41    indexed_da_stake_table: BTreeMap<T::SignatureKey, PeerConfig<T>>,
42}
43
44impl<TYPES: NodeType> Membership<TYPES> for StaticCommitteeLeaderForTwoViews<TYPES> {
45    type Error = hotshot_utils::anytrace::Error;
46    /// Create a new election
47    fn new(committee_members: Vec<PeerConfig<TYPES>>, da_members: Vec<PeerConfig<TYPES>>) -> Self {
48        // For each eligible leader, get the stake table entry
49        let eligible_leaders: Vec<PeerConfig<TYPES>> = committee_members
50            .iter()
51            .filter(|&member| member.stake_table_entry.stake() > U256::ZERO)
52            .cloned()
53            .collect();
54
55        // For each member, get the stake table entry
56        let members: Vec<PeerConfig<TYPES>> = committee_members
57            .iter()
58            .filter(|&member| member.stake_table_entry.stake() > U256::ZERO)
59            .cloned()
60            .collect();
61
62        // For each member, get the stake table entry
63        let da_members: Vec<PeerConfig<TYPES>> = da_members
64            .iter()
65            .filter(|&member| member.stake_table_entry.stake() > U256::ZERO)
66            .cloned()
67            .collect();
68
69        // Index the stake table by public key
70        let indexed_stake_table: BTreeMap<TYPES::SignatureKey, PeerConfig<TYPES>> = members
71            .iter()
72            .map(|member| {
73                (
74                    TYPES::SignatureKey::public_key(&member.stake_table_entry),
75                    member.clone(),
76                )
77            })
78            .collect();
79
80        // Index the stake table by public key
81        let indexed_da_stake_table: BTreeMap<TYPES::SignatureKey, PeerConfig<TYPES>> = da_members
82            .iter()
83            .map(|member| {
84                (
85                    TYPES::SignatureKey::public_key(&member.stake_table_entry),
86                    member.clone(),
87                )
88            })
89            .collect();
90
91        Self {
92            eligible_leaders,
93            stake_table: members.into(),
94            da_stake_table: da_members.into(),
95            indexed_stake_table,
96            indexed_da_stake_table,
97        }
98    }
99
100    /// Get the stake table for the current view
101    fn stake_table(&self, _epoch: Option<<TYPES as NodeType>::Epoch>) -> HSStakeTable<TYPES> {
102        self.stake_table.clone()
103    }
104
105    /// Get the stake table for the current view
106    fn da_stake_table(&self, _epoch: Option<<TYPES as NodeType>::Epoch>) -> HSStakeTable<TYPES> {
107        self.da_stake_table.clone()
108    }
109
110    /// Get all members of the committee for the current view
111    fn committee_members(
112        &self,
113        _view_number: <TYPES as NodeType>::View,
114        _epoch: Option<<TYPES as NodeType>::Epoch>,
115    ) -> BTreeSet<<TYPES as NodeType>::SignatureKey> {
116        self.stake_table
117            .iter()
118            .map(|sc| TYPES::SignatureKey::public_key(&sc.stake_table_entry))
119            .collect()
120    }
121
122    /// Get all members of the committee for the current view
123    fn da_committee_members(
124        &self,
125        _view_number: <TYPES as NodeType>::View,
126        _epoch: Option<<TYPES as NodeType>::Epoch>,
127    ) -> BTreeSet<<TYPES as NodeType>::SignatureKey> {
128        self.da_stake_table
129            .iter()
130            .map(|da| TYPES::SignatureKey::public_key(&da.stake_table_entry))
131            .collect()
132    }
133
134    /// Get the stake table entry for a public key
135    fn stake(
136        &self,
137        pub_key: &<TYPES as NodeType>::SignatureKey,
138        _epoch: Option<<TYPES as NodeType>::Epoch>,
139    ) -> Option<PeerConfig<TYPES>> {
140        // Only return the stake if it is above zero
141        self.indexed_stake_table.get(pub_key).cloned()
142    }
143
144    /// Get DA the stake table entry for a public key
145    fn da_stake(
146        &self,
147        pub_key: &<TYPES as NodeType>::SignatureKey,
148        _epoch: Option<<TYPES as NodeType>::Epoch>,
149    ) -> Option<PeerConfig<TYPES>> {
150        // Only return the stake if it is above zero
151        self.indexed_da_stake_table.get(pub_key).cloned()
152    }
153
154    /// Check if a node has stake in the committee
155    fn has_stake(
156        &self,
157        pub_key: &<TYPES as NodeType>::SignatureKey,
158        _epoch: Option<<TYPES as NodeType>::Epoch>,
159    ) -> bool {
160        self.indexed_stake_table
161            .get(pub_key)
162            .is_some_and(|x| x.stake_table_entry.stake() > U256::ZERO)
163    }
164
165    /// Check if a node has stake in the committee
166    fn has_da_stake(
167        &self,
168        pub_key: &<TYPES as NodeType>::SignatureKey,
169        _epoch: Option<<TYPES as NodeType>::Epoch>,
170    ) -> bool {
171        self.indexed_da_stake_table
172            .get(pub_key)
173            .is_some_and(|x| x.stake_table_entry.stake() > U256::ZERO)
174    }
175
176    /// Index the vector of public keys with the current view number
177    fn lookup_leader(
178        &self,
179        view_number: <TYPES as NodeType>::View,
180        _epoch: Option<<TYPES as NodeType>::Epoch>,
181    ) -> Result<TYPES::SignatureKey> {
182        let index =
183            usize::try_from((*view_number / 2) % self.eligible_leaders.len() as u64).unwrap();
184        let res = self.eligible_leaders[index].clone();
185
186        Ok(TYPES::SignatureKey::public_key(&res.stake_table_entry))
187    }
188
189    /// Get the total number of nodes in the committee
190    fn total_nodes(&self, _epoch: Option<<TYPES as NodeType>::Epoch>) -> usize {
191        self.stake_table.len()
192    }
193
194    /// Get the total number of DA nodes in the committee
195    fn da_total_nodes(&self, _epoch: Option<<TYPES as NodeType>::Epoch>) -> usize {
196        self.da_stake_table.len()
197    }
198
199    /// Get the voting success threshold for the committee
200    fn success_threshold(&self, _epoch: Option<<TYPES as NodeType>::Epoch>) -> U256 {
201        U256::from(((self.stake_table.len() as u64 * 2) / 3) + 1)
202    }
203
204    /// Get the voting success threshold for the committee
205    fn da_success_threshold(&self, _epoch: Option<<TYPES as NodeType>::Epoch>) -> U256 {
206        U256::from(((self.da_stake_table.len() as u64 * 2) / 3) + 1)
207    }
208
209    /// Get the voting failure threshold for the committee
210    fn failure_threshold(&self, _epoch: Option<<TYPES as NodeType>::Epoch>) -> U256 {
211        U256::from(((self.stake_table.len() as u64) / 3) + 1)
212    }
213
214    /// Get the voting upgrade threshold for the committee
215    fn upgrade_threshold(&self, _epoch: Option<<TYPES as NodeType>::Epoch>) -> U256 {
216        U256::from(((self.stake_table.len() as u64 * 9) / 10) + 1)
217    }
218    fn has_stake_table(&self, _epoch: TYPES::Epoch) -> bool {
219        true
220    }
221    fn has_randomized_stake_table(&self, _epoch: TYPES::Epoch) -> anyhow::Result<bool> {
222        Ok(true)
223    }
224
225    fn add_drb_result(&mut self, _epoch: <TYPES as NodeType>::Epoch, _drb_result: DrbResult) {}
226}