hotshot/traits/election/
dummy_catchup_membership.rs

1use std::{collections::HashSet, marker::PhantomData, sync::Arc, time::Duration};
2
3use alloy::primitives::U256;
4use anyhow::Ok;
5use async_lock::RwLock;
6use hotshot_types::{
7    data::Leaf2,
8    drb::DrbResult,
9    stake_table::HSStakeTable,
10    traits::{
11        election::Membership,
12        node_implementation::{NodeType, Versions},
13        signature_key::SignatureKey,
14    },
15    PeerConfig,
16};
17
18#[derive(Clone, Debug, Eq, PartialEq)]
19pub struct DummyCatchupCommittee<TYPES: NodeType, V: Versions, InnerTypes: NodeType> {
20    inner: InnerTypes::Membership,
21    epochs: HashSet<TYPES::Epoch>,
22    drbs: HashSet<TYPES::Epoch>,
23    _phantom: PhantomData<V>,
24}
25
26impl<TYPES: NodeType, V: Versions, InnerTypes: NodeType>
27    DummyCatchupCommittee<TYPES, V, InnerTypes>
28{
29    fn assert_has_stake_table(&self, epoch: Option<TYPES::Epoch>) {
30        let Some(epoch) = epoch else {
31            return;
32        };
33        assert!(
34            self.epochs.contains(&epoch),
35            "Failed stake table check for epoch {epoch}"
36        );
37    }
38    fn assert_has_randomized_stake_table(&self, epoch: Option<TYPES::Epoch>) {
39        let Some(epoch) = epoch else {
40            return;
41        };
42        assert!(
43            self.drbs.contains(&epoch),
44            "Failed drb check for epoch {epoch}"
45        );
46    }
47
48    fn convert_peer_config<FromTypes, IntoTypes>(
49        peer_config: PeerConfig<FromTypes>,
50    ) -> PeerConfig<IntoTypes>
51    where
52        FromTypes: NodeType,
53        IntoTypes: NodeType,
54        <IntoTypes::SignatureKey as SignatureKey>::StakeTableEntry:
55            From<<FromTypes::SignatureKey as SignatureKey>::StakeTableEntry>,
56        IntoTypes::StateSignatureKey: From<FromTypes::StateSignatureKey>,
57    {
58        PeerConfig {
59            stake_table_entry: peer_config.stake_table_entry.into(),
60            state_ver_key: Into::<IntoTypes::StateSignatureKey>::into(peer_config.state_ver_key),
61        }
62    }
63}
64
65impl<TYPES: NodeType, V: Versions, InnerTypes: NodeType> Membership<TYPES>
66    for DummyCatchupCommittee<TYPES, V, InnerTypes>
67where
68    TYPES::BlockHeader: Default,
69    TYPES::InstanceState: Default,
70    InnerTypes::Epoch: From<TYPES::Epoch>,
71    TYPES::Epoch: From<InnerTypes::Epoch>,
72    InnerTypes::View: From<TYPES::View>,
73    TYPES::SignatureKey: From<InnerTypes::SignatureKey>,
74    for<'a> &'a InnerTypes::SignatureKey: From<&'a TYPES::SignatureKey>,
75    <InnerTypes::SignatureKey as SignatureKey>::StakeTableEntry:
76        From<<TYPES::SignatureKey as SignatureKey>::StakeTableEntry>,
77    InnerTypes::StateSignatureKey: From<TYPES::StateSignatureKey>,
78    <TYPES::SignatureKey as SignatureKey>::StakeTableEntry:
79        From<<InnerTypes::SignatureKey as SignatureKey>::StakeTableEntry>,
80    TYPES::StateSignatureKey: From<InnerTypes::StateSignatureKey>,
81{
82    type Error = <InnerTypes::Membership as Membership<InnerTypes>>::Error;
83
84    fn new(
85        // Note: eligible_leaders is currently a haMemck because the DA leader == the quorum leader
86        // but they should not have voting power.
87        stake_committee_members: Vec<hotshot_types::PeerConfig<TYPES>>,
88        da_committee_members: Vec<hotshot_types::PeerConfig<TYPES>>,
89    ) -> Self {
90        Self {
91            inner: Membership::new(
92                stake_committee_members
93                    .into_iter()
94                    .map(Self::convert_peer_config)
95                    .collect(),
96                da_committee_members
97                    .into_iter()
98                    .map(Self::convert_peer_config)
99                    .collect(),
100            ),
101            epochs: HashSet::new(),
102            drbs: HashSet::new(),
103            _phantom: PhantomData,
104        }
105    }
106
107    fn stake_table(&self, epoch: Option<TYPES::Epoch>) -> HSStakeTable<TYPES> {
108        self.assert_has_stake_table(epoch);
109        let peer_configs = self.inner.stake_table(epoch.map(Into::into)).0;
110        HSStakeTable(
111            peer_configs
112                .into_iter()
113                .map(Self::convert_peer_config)
114                .collect(),
115        )
116    }
117
118    fn da_stake_table(&self, epoch: Option<TYPES::Epoch>) -> HSStakeTable<TYPES> {
119        self.assert_has_stake_table(epoch);
120        let peer_configs = self.inner.da_stake_table(epoch.map(Into::into)).0;
121        HSStakeTable(
122            peer_configs
123                .into_iter()
124                .map(Self::convert_peer_config)
125                .collect(),
126        )
127    }
128
129    fn committee_members(
130        &self,
131        view_number: TYPES::View,
132        epoch: Option<TYPES::Epoch>,
133    ) -> std::collections::BTreeSet<TYPES::SignatureKey> {
134        self.assert_has_stake_table(epoch);
135        self.inner
136            .committee_members(view_number.into(), epoch.map(Into::into))
137            .into_iter()
138            .map(Into::<TYPES::SignatureKey>::into)
139            .collect()
140    }
141
142    fn da_committee_members(
143        &self,
144        view_number: TYPES::View,
145        epoch: Option<TYPES::Epoch>,
146    ) -> std::collections::BTreeSet<TYPES::SignatureKey> {
147        self.assert_has_stake_table(epoch);
148        self.inner
149            .da_committee_members(view_number.into(), epoch.map(Into::into))
150            .into_iter()
151            .map(Into::<TYPES::SignatureKey>::into)
152            .collect()
153    }
154
155    fn stake(
156        &self,
157        pub_key: &TYPES::SignatureKey,
158        epoch: Option<TYPES::Epoch>,
159    ) -> Option<hotshot_types::PeerConfig<TYPES>> {
160        self.assert_has_stake_table(epoch);
161        self.inner
162            .stake(pub_key.into(), epoch.map(Into::into))
163            .map(Self::convert_peer_config)
164    }
165
166    fn da_stake(
167        &self,
168        pub_key: &TYPES::SignatureKey,
169        epoch: Option<TYPES::Epoch>,
170    ) -> Option<hotshot_types::PeerConfig<TYPES>> {
171        self.assert_has_stake_table(epoch);
172        self.inner
173            .da_stake(pub_key.into(), epoch.map(Into::into))
174            .map(Self::convert_peer_config)
175    }
176
177    fn has_stake(&self, pub_key: &TYPES::SignatureKey, epoch: Option<TYPES::Epoch>) -> bool {
178        self.assert_has_stake_table(epoch);
179        self.inner.has_stake(pub_key.into(), epoch.map(Into::into))
180    }
181
182    fn has_da_stake(&self, pub_key: &TYPES::SignatureKey, epoch: Option<TYPES::Epoch>) -> bool {
183        self.assert_has_stake_table(epoch);
184        self.inner
185            .has_da_stake(pub_key.into(), epoch.map(Into::into))
186    }
187
188    fn lookup_leader(
189        &self,
190        view: TYPES::View,
191        epoch: Option<TYPES::Epoch>,
192    ) -> std::result::Result<TYPES::SignatureKey, Self::Error> {
193        self.assert_has_randomized_stake_table(epoch);
194        self.inner
195            .lookup_leader(view.into(), epoch.map(Into::into))
196            .map(Into::<TYPES::SignatureKey>::into)
197    }
198
199    fn total_nodes(&self, epoch: Option<TYPES::Epoch>) -> usize {
200        self.assert_has_stake_table(epoch);
201        self.inner.total_nodes(epoch.map(Into::into))
202    }
203
204    fn da_total_nodes(&self, epoch: Option<TYPES::Epoch>) -> usize {
205        self.assert_has_stake_table(epoch);
206        self.inner.da_total_nodes(epoch.map(Into::into))
207    }
208
209    fn success_threshold(&self, epoch: Option<TYPES::Epoch>) -> U256 {
210        self.assert_has_stake_table(epoch);
211        self.inner.success_threshold(epoch.map(Into::into))
212    }
213
214    fn da_success_threshold(&self, epoch: Option<TYPES::Epoch>) -> U256 {
215        self.assert_has_stake_table(epoch);
216        self.inner.da_success_threshold(epoch.map(Into::into))
217    }
218
219    fn failure_threshold(&self, epoch: Option<TYPES::Epoch>) -> U256 {
220        self.assert_has_stake_table(epoch);
221        self.inner.failure_threshold(epoch.map(Into::into))
222    }
223
224    fn upgrade_threshold(&self, epoch: Option<TYPES::Epoch>) -> U256 {
225        self.assert_has_stake_table(epoch);
226        self.inner.upgrade_threshold(epoch.map(Into::into))
227    }
228
229    fn has_stake_table(&self, epoch: TYPES::Epoch) -> bool {
230        self.epochs.contains(&epoch)
231    }
232
233    fn has_randomized_stake_table(&self, epoch: TYPES::Epoch) -> anyhow::Result<bool> {
234        Ok(self.drbs.contains(&epoch))
235    }
236
237    async fn get_epoch_root(
238        _membership: Arc<RwLock<Self>>,
239        _block_height: u64,
240        _epoch: TYPES::Epoch,
241    ) -> anyhow::Result<Leaf2<TYPES>> {
242        tokio::time::sleep(Duration::from_millis(10)).await;
243        let leaf = Leaf2::genesis::<V>(
244            &TYPES::ValidatedState::default(),
245            &TYPES::InstanceState::default(),
246        )
247        .await;
248        Ok(leaf)
249    }
250
251    async fn get_epoch_drb(
252        _membership: Arc<RwLock<Self>>,
253        _block_height: u64,
254        _epoch: TYPES::Epoch,
255    ) -> anyhow::Result<DrbResult> {
256        tokio::time::sleep(Duration::from_millis(10)).await;
257        Ok(DrbResult::default())
258    }
259
260    fn add_drb_result(&mut self, epoch: TYPES::Epoch, drb_result: hotshot_types::drb::DrbResult) {
261        self.drbs.insert(epoch);
262        self.inner.add_drb_result(epoch.into(), drb_result);
263    }
264
265    fn set_first_epoch(
266        &mut self,
267        epoch: TYPES::Epoch,
268        initial_drb_result: hotshot_types::drb::DrbResult,
269    ) {
270        self.epochs.insert(epoch);
271        self.epochs.insert(epoch + 1);
272        self.drbs.insert(epoch);
273        self.drbs.insert(epoch + 1);
274        self.inner.set_first_epoch(epoch.into(), initial_drb_result);
275    }
276
277    async fn add_epoch_root(
278        membership: Arc<RwLock<Self>>,
279        epoch: TYPES::Epoch,
280        _block_header: TYPES::BlockHeader,
281    ) -> anyhow::Result<()> {
282        let mut membership_writer = membership.write().await;
283        tracing::error!("Adding epoch root for {epoch}");
284        membership_writer.epochs.insert(epoch);
285        Ok(())
286    }
287
288    fn first_epoch(&self) -> Option<TYPES::Epoch> {
289        self.inner.first_epoch().map(Into::into)
290    }
291}