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 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}