1use std::{
8 collections::{BTreeMap, HashMap},
9 sync::{
10 Arc,
11 atomic::{AtomicBool, Ordering},
12 },
13};
14
15use anyhow::{Result, anyhow, bail};
16use async_lock::RwLock;
17use async_trait::async_trait;
18use hotshot_types::{
19 data::{
20 DaProposal, DaProposal2, EpochNumber, Leaf2, QuorumProposal, QuorumProposal2,
21 QuorumProposalWrapper, VidCommitment, VidDisperseShare, ViewNumber,
22 },
23 drb::{DrbInput, DrbResult},
24 event::HotShotAction,
25 message::{Proposal, convert_proposal},
26 simple_certificate::{
27 LightClientStateUpdateCertificateV2, NextEpochQuorumCertificate2, QuorumCertificate2,
28 UpgradeCertificate,
29 },
30 traits::{node_implementation::NodeType, storage::Storage},
31 vote::HasViewNumber,
32};
33
34use crate::testable_delay::{DelayConfig, SupportedTraitTypesForAsyncDelay, TestableDelay};
35
36type VidShares<TYPES> = BTreeMap<
37 ViewNumber,
38 HashMap<<TYPES as NodeType>::SignatureKey, Proposal<TYPES, VidDisperseShare<TYPES>>>,
39>;
40#[derive(Clone, Debug)]
41pub struct TestStorageState<TYPES: NodeType> {
42 vids: VidShares<TYPES>,
43 das: HashMap<ViewNumber, Proposal<TYPES, DaProposal<TYPES>>>,
44 da2s: HashMap<ViewNumber, Proposal<TYPES, DaProposal2<TYPES>>>,
45 pub proposals: BTreeMap<ViewNumber, Proposal<TYPES, QuorumProposal<TYPES>>>,
46 pub proposals2: BTreeMap<ViewNumber, Proposal<TYPES, QuorumProposal2<TYPES>>>,
47 pub proposals_wrapper: BTreeMap<ViewNumber, Proposal<TYPES, QuorumProposalWrapper<TYPES>>>,
48 high_qc: Option<hotshot_types::simple_certificate::QuorumCertificate<TYPES>>,
49 high_qc2: Option<hotshot_types::simple_certificate::QuorumCertificate2<TYPES>>,
50 eqc: Option<(
51 hotshot_types::simple_certificate::QuorumCertificate2<TYPES>,
52 hotshot_types::simple_certificate::NextEpochQuorumCertificate2<TYPES>,
53 )>,
54 next_epoch_high_qc2:
55 Option<hotshot_types::simple_certificate::NextEpochQuorumCertificate2<TYPES>>,
56 action: ViewNumber,
57 action_log: Vec<(ViewNumber, HotShotAction)>,
58 epoch: Option<EpochNumber>,
59 state_certs: BTreeMap<EpochNumber, LightClientStateUpdateCertificateV2<TYPES>>,
60 drb_results: BTreeMap<EpochNumber, DrbResult>,
61 drb_inputs: BTreeMap<u64, DrbInput>,
62 epoch_roots: BTreeMap<EpochNumber, TYPES::BlockHeader>,
63 restart_view: ViewNumber,
64 anchor_leaf: Option<(Leaf2<TYPES>, QuorumCertificate2<TYPES>)>,
65}
66
67impl<TYPES: NodeType> Default for TestStorageState<TYPES> {
68 fn default() -> Self {
69 Self {
70 vids: BTreeMap::new(),
71 das: HashMap::new(),
72 da2s: HashMap::new(),
73 proposals: BTreeMap::new(),
74 proposals2: BTreeMap::new(),
75 proposals_wrapper: BTreeMap::new(),
76 high_qc: None,
77 high_qc2: None,
78 eqc: None,
79 next_epoch_high_qc2: None,
80 action: ViewNumber::genesis(),
81 action_log: Vec::new(),
82 epoch: None,
83 state_certs: BTreeMap::new(),
84 drb_results: BTreeMap::new(),
85 drb_inputs: BTreeMap::new(),
86 epoch_roots: BTreeMap::new(),
87 restart_view: ViewNumber::genesis(),
88 anchor_leaf: None,
89 }
90 }
91}
92
93#[derive(Clone, Debug)]
94pub struct TestStorage<TYPES: NodeType> {
95 pub inner: Arc<RwLock<TestStorageState<TYPES>>>,
96 pub should_return_err: Arc<AtomicBool>,
98 pub delay_config: DelayConfig,
99 pub decided_upgrade_certificate: Arc<RwLock<Option<UpgradeCertificate<TYPES>>>>,
100}
101
102impl<TYPES: NodeType> Default for TestStorage<TYPES> {
103 fn default() -> Self {
104 Self {
105 inner: Arc::new(RwLock::new(TestStorageState::default())),
106 should_return_err: Arc::new(AtomicBool::new(false)),
107 delay_config: DelayConfig::default(),
108 decided_upgrade_certificate: Arc::new(RwLock::new(None)),
109 }
110 }
111}
112
113#[async_trait]
114impl<TYPES: NodeType> TestableDelay for TestStorage<TYPES> {
115 async fn run_delay_settings_from_config(delay_config: &DelayConfig) {
116 if let Some(settings) = delay_config.get_setting(&SupportedTraitTypesForAsyncDelay::Storage)
117 {
118 Self::handle_async_delay(settings).await;
119 }
120 }
121}
122
123impl<TYPES: NodeType> TestStorage<TYPES> {
124 pub async fn proposals_cloned(
125 &self,
126 ) -> BTreeMap<ViewNumber, Proposal<TYPES, QuorumProposalWrapper<TYPES>>> {
127 self.inner.read().await.proposals_wrapper.clone()
128 }
129
130 pub async fn high_qc_cloned(&self) -> Option<QuorumCertificate2<TYPES>> {
131 self.inner.read().await.high_qc2.clone()
132 }
133
134 pub async fn next_epoch_high_qc_cloned(&self) -> Option<NextEpochQuorumCertificate2<TYPES>> {
135 self.inner.read().await.next_epoch_high_qc2.clone()
136 }
137
138 pub async fn decided_upgrade_certificate(&self) -> Option<UpgradeCertificate<TYPES>> {
139 self.decided_upgrade_certificate.read().await.clone()
140 }
141
142 pub async fn last_actioned_view(&self) -> ViewNumber {
143 self.inner.read().await.action
144 }
145
146 pub async fn action_log(&self) -> Vec<(ViewNumber, HotShotAction)> {
148 self.inner.read().await.action_log.clone()
149 }
150
151 pub async fn restart_view(&self) -> ViewNumber {
152 self.inner.read().await.restart_view
153 }
154
155 pub async fn update_anchor_leaf(&self, leaf: Leaf2<TYPES>, qc: QuorumCertificate2<TYPES>) {
160 let mut inner = self.inner.write().await;
161 if inner
162 .anchor_leaf
163 .as_ref()
164 .is_none_or(|(anchor, _)| leaf.view_number() > anchor.view_number())
165 {
166 inner.anchor_leaf = Some((leaf, qc));
167 }
168 }
169
170 pub async fn anchor_leaf(&self) -> Option<(Leaf2<TYPES>, QuorumCertificate2<TYPES>)> {
171 self.inner.read().await.anchor_leaf.clone()
172 }
173
174 pub async fn last_actioned_epoch(&self) -> Option<EpochNumber> {
175 self.inner.read().await.epoch
176 }
177 pub async fn vids_cloned(&self) -> VidShares<TYPES> {
178 self.inner.read().await.vids.clone()
179 }
180
181 pub async fn state_cert_cloned(&self) -> Option<LightClientStateUpdateCertificateV2<TYPES>> {
182 self.inner
183 .read()
184 .await
185 .state_certs
186 .iter()
187 .next_back()
188 .map(|(_, cert)| cert.clone())
189 }
190}
191
192#[async_trait]
193impl<TYPES: NodeType> Storage<TYPES> for TestStorage<TYPES> {
194 async fn append_vid(&self, proposal: &Proposal<TYPES, VidDisperseShare<TYPES>>) -> Result<()> {
195 if self.should_return_err.load(Ordering::Relaxed) {
196 bail!("Failed to append VID proposal to storage");
197 }
198 Self::run_delay_settings_from_config(&self.delay_config).await;
199 let mut inner = self.inner.write().await;
200 inner
201 .vids
202 .entry(proposal.data.view_number())
203 .or_default()
204 .insert(proposal.data.recipient_key().clone(), proposal.clone());
205 Ok(())
206 }
207
208 async fn append_da(
209 &self,
210 proposal: &Proposal<TYPES, DaProposal<TYPES>>,
211 _vid_commit: VidCommitment,
212 ) -> Result<()> {
213 if self.should_return_err.load(Ordering::Relaxed) {
214 bail!("Failed to append DA proposal to storage");
215 }
216 Self::run_delay_settings_from_config(&self.delay_config).await;
217 let mut inner = self.inner.write().await;
218 inner
219 .das
220 .insert(proposal.data.view_number, proposal.clone());
221 Ok(())
222 }
223
224 async fn append_da2(
225 &self,
226 proposal: &Proposal<TYPES, DaProposal2<TYPES>>,
227 _vid_commit: VidCommitment,
228 ) -> Result<()> {
229 if self.should_return_err.load(Ordering::Relaxed) {
230 bail!("Failed to append DA proposal (2) to storage");
231 }
232 Self::run_delay_settings_from_config(&self.delay_config).await;
233 let mut inner = self.inner.write().await;
234 inner
235 .da2s
236 .insert(proposal.data.view_number, proposal.clone());
237 Ok(())
238 }
239
240 async fn append_proposal(
241 &self,
242 proposal: &Proposal<TYPES, QuorumProposal<TYPES>>,
243 ) -> Result<()> {
244 if self.should_return_err.load(Ordering::Relaxed) {
245 bail!("Failed to append Quorum proposal (1) to storage");
246 }
247 Self::run_delay_settings_from_config(&self.delay_config).await;
248 let mut inner = self.inner.write().await;
249 inner
250 .proposals
251 .insert(proposal.data.view_number, proposal.clone());
252 Ok(())
253 }
254
255 async fn append_proposal2(
256 &self,
257 proposal: &Proposal<TYPES, QuorumProposal2<TYPES>>,
258 ) -> Result<()> {
259 if self.should_return_err.load(Ordering::Relaxed) {
260 bail!("Failed to append Quorum proposal (2) to storage");
261 }
262 Self::run_delay_settings_from_config(&self.delay_config).await;
263 let mut inner = self.inner.write().await;
264 inner
265 .proposals2
266 .insert(proposal.data.view_number, proposal.clone());
267 Ok(())
268 }
269
270 async fn append_proposal_wrapper(
271 &self,
272 proposal: &Proposal<TYPES, QuorumProposalWrapper<TYPES>>,
273 ) -> Result<()> {
274 if self.should_return_err.load(Ordering::Relaxed) {
275 bail!("Failed to append Quorum proposal (wrapped) to storage");
276 }
277 Self::run_delay_settings_from_config(&self.delay_config).await;
278 let mut inner = self.inner.write().await;
279 inner
280 .proposals_wrapper
281 .insert(proposal.data.view_number(), proposal.clone());
282 Ok(())
283 }
284
285 async fn record_action(
286 &self,
287 view: ViewNumber,
288 epoch: Option<EpochNumber>,
289 action: hotshot_types::event::HotShotAction,
290 ) -> Result<()> {
291 if self.should_return_err.load(Ordering::Relaxed) {
292 bail!("Failed to append Action to storage");
293 }
294 let mut inner = self.inner.write().await;
295 inner.action_log.push((view, action));
296 if matches!(
297 action,
298 HotShotAction::Vote | HotShotAction::Propose | HotShotAction::TimeoutVote
299 ) {
300 if view > inner.action {
301 inner.action = view;
302 }
303 if epoch > inner.epoch {
304 inner.epoch = epoch;
305 }
306 }
307 if matches!(action, HotShotAction::Vote) {
308 inner.restart_view = view + 1;
309 }
310 Self::run_delay_settings_from_config(&self.delay_config).await;
311 Ok(())
312 }
313
314 async fn update_high_qc(
315 &self,
316 new_high_qc: hotshot_types::simple_certificate::QuorumCertificate<TYPES>,
317 ) -> Result<()> {
318 if self.should_return_err.load(Ordering::Relaxed) {
319 bail!("Failed to update high qc to storage");
320 }
321 Self::run_delay_settings_from_config(&self.delay_config).await;
322 let mut inner = self.inner.write().await;
323 if let Some(ref current_high_qc) = inner.high_qc {
324 if new_high_qc.view_number() > current_high_qc.view_number() {
325 inner.high_qc = Some(new_high_qc);
326 }
327 } else {
328 inner.high_qc = Some(new_high_qc);
329 }
330 Ok(())
331 }
332
333 async fn update_eqc(
335 &self,
336 high_qc: QuorumCertificate2<TYPES>,
337 next_epoch_high_qc: NextEpochQuorumCertificate2<TYPES>,
338 ) -> Result<()> {
339 if self.should_return_err.load(Ordering::Relaxed) {
340 bail!("Failed to update eqc in storage");
341 }
342 Self::run_delay_settings_from_config(&self.delay_config).await;
343 let mut inner = self.inner.write().await;
344 if let Some((ref current_high_qc, _)) = inner.eqc {
345 if high_qc.view_number() > current_high_qc.view_number() {
346 inner.eqc = Some((high_qc, next_epoch_high_qc));
347 }
348 } else {
349 inner.eqc = Some((high_qc, next_epoch_high_qc));
350 }
351 Ok(())
352 }
353
354 async fn update_high_qc2(
355 &self,
356 new_high_qc: hotshot_types::simple_certificate::QuorumCertificate2<TYPES>,
357 ) -> Result<()> {
358 if self.should_return_err.load(Ordering::Relaxed) {
359 bail!("Failed to update high qc to storage");
360 }
361 Self::run_delay_settings_from_config(&self.delay_config).await;
362 let mut inner = self.inner.write().await;
363 if let Some(ref current_high_qc) = inner.high_qc2 {
364 if new_high_qc.view_number() > current_high_qc.view_number() {
365 inner.high_qc2 = Some(new_high_qc);
366 }
367 } else {
368 inner.high_qc2 = Some(new_high_qc);
369 }
370 Ok(())
371 }
372
373 async fn update_state_cert(
374 &self,
375 state_cert: LightClientStateUpdateCertificateV2<TYPES>,
376 ) -> Result<()> {
377 if self.should_return_err.load(Ordering::Relaxed) {
378 bail!("Failed to update state_cert to storage");
379 }
380 Self::run_delay_settings_from_config(&self.delay_config).await;
381 self.inner
382 .write()
383 .await
384 .state_certs
385 .insert(state_cert.epoch, state_cert);
386 Ok(())
387 }
388
389 async fn update_next_epoch_high_qc2(
390 &self,
391 new_next_epoch_high_qc: hotshot_types::simple_certificate::NextEpochQuorumCertificate2<
392 TYPES,
393 >,
394 ) -> Result<()> {
395 if self.should_return_err.load(Ordering::Relaxed) {
396 bail!("Failed to update next epoch high qc to storage");
397 }
398 Self::run_delay_settings_from_config(&self.delay_config).await;
399 let mut inner = self.inner.write().await;
400 if let Some(ref current_next_epoch_high_qc) = inner.next_epoch_high_qc2 {
401 if new_next_epoch_high_qc.view_number() > current_next_epoch_high_qc.view_number() {
402 inner.next_epoch_high_qc2 = Some(new_next_epoch_high_qc);
403 }
404 } else {
405 inner.next_epoch_high_qc2 = Some(new_next_epoch_high_qc);
406 }
407 Ok(())
408 }
409
410 async fn update_decided_upgrade_certificate(
411 &self,
412 decided_upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
413 ) -> Result<()> {
414 *self.decided_upgrade_certificate.write().await = decided_upgrade_certificate;
415
416 Ok(())
417 }
418
419 async fn migrate_storage(&self) -> Result<()> {
420 let mut storage_writer = self.inner.write().await;
421
422 for (view, proposal) in storage_writer.proposals.clone().iter() {
423 storage_writer
424 .proposals2
425 .insert(*view, convert_proposal(proposal.clone()));
426 }
427
428 Ok(())
429 }
430
431 async fn store_drb_result(&self, epoch: EpochNumber, drb_result: DrbResult) -> Result<()> {
432 let mut inner = self.inner.write().await;
433
434 inner.drb_results.insert(epoch, drb_result);
435
436 Ok(())
437 }
438
439 async fn store_epoch_root(
440 &self,
441 epoch: EpochNumber,
442 block_header: TYPES::BlockHeader,
443 ) -> Result<()> {
444 let mut inner = self.inner.write().await;
445
446 inner.epoch_roots.insert(epoch, block_header);
447
448 Ok(())
449 }
450
451 async fn store_drb_input(&self, drb_input: DrbInput) -> Result<()> {
452 let mut inner = self.inner.write().await;
453
454 inner.drb_inputs.insert(drb_input.epoch, drb_input);
455
456 Ok(())
457 }
458
459 async fn load_drb_input(&self, epoch: u64) -> Result<DrbInput> {
460 let inner = self.inner.read().await;
461
462 match inner.drb_inputs.get(&epoch) {
463 Some(drb_input) => Ok(drb_input.clone()),
464 None => Err(anyhow!("Missing DrbInput for epoch {}", epoch)),
465 }
466 }
467}