1#[cfg(feature = "node")]
4use std::{cmp::max, collections::BTreeMap};
5use std::{fmt::Debug, ops::Range, sync::Arc, time::Duration};
6
7use alloy::primitives::Address;
8#[cfg(feature = "node")]
9use anyhow::{Context, bail, ensure};
10use async_trait::async_trait;
11use committable::Commitment;
12use futures::{FutureExt, TryFutureExt};
13#[cfg(feature = "node")]
14use hotshot_new_protocol::storage::NewProtocolStorage;
15#[cfg(feature = "node")]
16use hotshot_types::simple_certificate::{Certificate1, Certificate2};
17#[cfg(feature = "node")]
18use hotshot_types::{
19 data::{DaProposal, DaProposal2, QuorumProposalWrapper, VidCommitment, VidDisperseShare},
20 drb::DrbInput,
21 event::{HotShotAction, LeafInfo},
22 initializer::{HotShotInitializer, InitializerEpochInfo},
23 message::Proposal,
24 simple_certificate::{
25 CertificatePair, NextEpochQuorumCertificate2, QuorumCertificate2, UpgradeCertificate,
26 },
27 traits::{
28 ValidatedState as HotShotState, metrics::Metrics, node_implementation::NodeType,
29 storage::Storage,
30 },
31 vote::HasViewNumber,
32};
33use hotshot_types::{
34 data::{EpochNumber, ViewNumber},
35 drb::DrbResult,
36 epoch_membership::EpochMembershipCoordinator,
37 new_protocol::CoordinatorEvent,
38 simple_certificate::LightClientStateUpdateCertificateV2,
39};
40use indexmap::IndexMap;
41use serde::{Serialize, de::DeserializeOwned};
42#[cfg(feature = "node")]
43use versions::{NEW_PROTOCOL_VERSION, Upgrade};
44
45use super::{
46 impls::NodeState,
47 utils::BackoffParams,
48 v0_3::{EventKey, IndexedStake, StakeTableEvent},
49};
50use crate::{
51 AuthenticatedValidatorMap, BlockMerkleTree, FeeAccount, FeeAccountProof, FeeMerkleCommitment,
52 Header, Leaf2, PubKey, SeqTypes,
53 v0::impls::StakeTableHash,
54 v0_3::{
55 ChainConfig, RegisteredValidator, RewardAccountProofV1, RewardAccountV1, RewardAmount,
56 RewardMerkleCommitmentV1,
57 },
58 v0_4::{PermittedRewardMerkleTreeV2, RewardAccountV2, RewardMerkleCommitmentV2},
59};
60#[cfg(feature = "node")]
61use crate::{NetworkConfig, v0::impls::ValidatedState};
62
63#[async_trait]
64pub trait StateCatchup: Send + Sync {
65 async fn try_fetch_leaf(
71 &self,
72 retry: usize,
73 coordinator: EpochMembershipCoordinator<SeqTypes>,
74 height: u64,
75 ) -> anyhow::Result<Leaf2>;
76
77 async fn fetch_leaf(
79 &self,
80 coordinator: EpochMembershipCoordinator<SeqTypes>,
81 height: u64,
82 ) -> anyhow::Result<Leaf2> {
83 self.backoff()
84 .retry(self, |provider, retry| {
85 let coordinator = coordinator.clone();
86 async move { provider.try_fetch_leaf(retry, coordinator, height).await }.boxed()
87 })
88 .await
89 }
90
91 async fn try_fetch_accounts(
93 &self,
94 retry: usize,
95 instance: &NodeState,
96 height: u64,
97 view: ViewNumber,
98 fee_merkle_tree_root: FeeMerkleCommitment,
99 accounts: &[FeeAccount],
100 ) -> anyhow::Result<Vec<FeeAccountProof>>;
101
102 async fn fetch_accounts(
104 &self,
105 instance: &NodeState,
106 height: u64,
107 view: ViewNumber,
108 fee_merkle_tree_root: FeeMerkleCommitment,
109 accounts: Vec<FeeAccount>,
110 ) -> anyhow::Result<Vec<FeeAccountProof>> {
111 self.backoff()
112 .retry(self, |provider, retry| {
113 let accounts = &accounts;
114 async move {
115 provider
116 .try_fetch_accounts(
117 retry,
118 instance,
119 height,
120 view,
121 fee_merkle_tree_root,
122 accounts,
123 )
124 .await
125 .map_err(|err| {
126 err.context(format!(
127 "fetching accounts {accounts:?}, height {height}, view {view}"
128 ))
129 })
130 }
131 .boxed()
132 })
133 .await
134 }
135
136 async fn try_remember_blocks_merkle_tree(
138 &self,
139 retry: usize,
140 instance: &NodeState,
141 height: u64,
142 view: ViewNumber,
143 mt: &mut BlockMerkleTree,
144 ) -> anyhow::Result<()>;
145
146 async fn remember_blocks_merkle_tree(
148 &self,
149 instance: &NodeState,
150 height: u64,
151 view: ViewNumber,
152 mt: &mut BlockMerkleTree,
153 ) -> anyhow::Result<()> {
154 self.backoff()
155 .retry(mt, |mt, retry| {
156 self.try_remember_blocks_merkle_tree(retry, instance, height, view, mt)
157 .map_err(|err| err.context(format!("fetching frontier using {}", self.name())))
158 .boxed()
159 })
160 .await
161 }
162
163 async fn try_fetch_chain_config(
165 &self,
166 retry: usize,
167 commitment: Commitment<ChainConfig>,
168 ) -> anyhow::Result<ChainConfig>;
169
170 async fn fetch_chain_config(
172 &self,
173 commitment: Commitment<ChainConfig>,
174 ) -> anyhow::Result<ChainConfig> {
175 self.backoff()
176 .retry(self, |provider, retry| {
177 provider
178 .try_fetch_chain_config(retry, commitment)
179 .map_err(|err| err.context("fetching chain config"))
180 .boxed()
181 })
182 .await
183 }
184
185 async fn try_fetch_reward_merkle_tree_v2(
187 &self,
188 retry: usize,
189 height: u64,
190 view: ViewNumber,
191 reward_merkle_tree_root: RewardMerkleCommitmentV2,
192 accounts: Arc<Vec<RewardAccountV2>>,
193 ) -> anyhow::Result<PermittedRewardMerkleTreeV2>;
194
195 async fn fetch_reward_merkle_tree_v2(
196 &self,
197 height: u64,
198 view: ViewNumber,
199 reward_merkle_tree_root: RewardMerkleCommitmentV2,
200 accounts: Arc<Vec<RewardAccountV2>>,
201 ) -> anyhow::Result<PermittedRewardMerkleTreeV2> {
202 self.backoff()
203 .retry(self, |provider, retry| {
204 let accounts = accounts.clone();
205 async move {
206 provider
207 .try_fetch_reward_merkle_tree_v2(
208 retry,
209 height,
210 view,
211 reward_merkle_tree_root,
212 accounts,
213 )
214 .await
215 .map_err(|err| {
216 err.context(format!("fetching reward merkle tree for height {height}"))
217 })
218 }
219 .boxed()
220 })
221 .await
222 }
223
224 async fn try_fetch_reward_accounts_v1(
226 &self,
227 retry: usize,
228 instance: &NodeState,
229 height: u64,
230 view: ViewNumber,
231 reward_merkle_tree_root: RewardMerkleCommitmentV1,
232 accounts: &[RewardAccountV1],
233 ) -> anyhow::Result<Vec<RewardAccountProofV1>>;
234
235 async fn fetch_reward_accounts_v1(
237 &self,
238 instance: &NodeState,
239 height: u64,
240 view: ViewNumber,
241 reward_merkle_tree_root: RewardMerkleCommitmentV1,
242 accounts: Vec<RewardAccountV1>,
243 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
244 self.backoff()
245 .retry(self, |provider, retry| {
246 let accounts = &accounts;
247 async move {
248 provider
249 .try_fetch_reward_accounts_v1(
250 retry,
251 instance,
252 height,
253 view,
254 reward_merkle_tree_root,
255 accounts,
256 )
257 .await
258 .map_err(|err| {
259 err.context(format!(
260 "fetching v1 reward accounts {accounts:?}, height {height}, view \
261 {view}"
262 ))
263 })
264 }
265 .boxed()
266 })
267 .await
268 }
269
270 async fn try_fetch_state_cert(
272 &self,
273 retry: usize,
274 epoch: u64,
275 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>>;
276
277 async fn fetch_state_cert(
279 &self,
280 epoch: u64,
281 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
282 self.backoff()
283 .retry(self, |provider, retry| {
284 provider
285 .try_fetch_state_cert(retry, epoch)
286 .map_err(|err| err.context(format!("fetching state cert for epoch {epoch}")))
287 .boxed()
288 })
289 .await
290 }
291
292 fn is_local(&self) -> bool;
294
295 fn backoff(&self) -> &BackoffParams;
297
298 fn name(&self) -> String;
300
301 fn with_local_timeout(&self, _local_timeout: Duration) -> Option<Arc<dyn StateCatchup>> {
304 None
305 }
306}
307
308#[async_trait]
309impl<T: StateCatchup + ?Sized> StateCatchup for Arc<T> {
310 async fn try_fetch_leaf(
311 &self,
312 retry: usize,
313 coordinator: EpochMembershipCoordinator<SeqTypes>,
314 height: u64,
315 ) -> anyhow::Result<Leaf2> {
316 (**self).try_fetch_leaf(retry, coordinator, height).await
317 }
318
319 async fn fetch_leaf(
320 &self,
321 coordinator: EpochMembershipCoordinator<SeqTypes>,
322 height: u64,
323 ) -> anyhow::Result<Leaf2> {
324 (**self).fetch_leaf(coordinator, height).await
325 }
326
327 async fn try_fetch_accounts(
328 &self,
329 retry: usize,
330 instance: &NodeState,
331 height: u64,
332 view: ViewNumber,
333 fee_merkle_tree_root: FeeMerkleCommitment,
334 accounts: &[FeeAccount],
335 ) -> anyhow::Result<Vec<FeeAccountProof>> {
336 (**self)
337 .try_fetch_accounts(
338 retry,
339 instance,
340 height,
341 view,
342 fee_merkle_tree_root,
343 accounts,
344 )
345 .await
346 }
347
348 async fn fetch_accounts(
349 &self,
350 instance: &NodeState,
351 height: u64,
352 view: ViewNumber,
353 fee_merkle_tree_root: FeeMerkleCommitment,
354 accounts: Vec<FeeAccount>,
355 ) -> anyhow::Result<Vec<FeeAccountProof>> {
356 (**self)
357 .fetch_accounts(instance, height, view, fee_merkle_tree_root, accounts)
358 .await
359 }
360
361 async fn try_remember_blocks_merkle_tree(
362 &self,
363 retry: usize,
364 instance: &NodeState,
365 height: u64,
366 view: ViewNumber,
367 mt: &mut BlockMerkleTree,
368 ) -> anyhow::Result<()> {
369 (**self)
370 .try_remember_blocks_merkle_tree(retry, instance, height, view, mt)
371 .await
372 }
373
374 async fn remember_blocks_merkle_tree(
375 &self,
376 instance: &NodeState,
377 height: u64,
378 view: ViewNumber,
379 mt: &mut BlockMerkleTree,
380 ) -> anyhow::Result<()> {
381 (**self)
382 .remember_blocks_merkle_tree(instance, height, view, mt)
383 .await
384 }
385
386 async fn try_fetch_chain_config(
387 &self,
388 retry: usize,
389 commitment: Commitment<ChainConfig>,
390 ) -> anyhow::Result<ChainConfig> {
391 (**self).try_fetch_chain_config(retry, commitment).await
392 }
393
394 async fn fetch_chain_config(
395 &self,
396 commitment: Commitment<ChainConfig>,
397 ) -> anyhow::Result<ChainConfig> {
398 (**self).fetch_chain_config(commitment).await
399 }
400
401 async fn try_fetch_reward_merkle_tree_v2(
402 &self,
403 retry: usize,
404 height: u64,
405 view: ViewNumber,
406 reward_merkle_tree_root: RewardMerkleCommitmentV2,
407 accounts: Arc<Vec<RewardAccountV2>>,
408 ) -> anyhow::Result<PermittedRewardMerkleTreeV2> {
409 (**self)
410 .try_fetch_reward_merkle_tree_v2(retry, height, view, reward_merkle_tree_root, accounts)
411 .await
412 }
413
414 async fn fetch_reward_merkle_tree_v2(
415 &self,
416 height: u64,
417 view: ViewNumber,
418 reward_merkle_tree_root: RewardMerkleCommitmentV2,
419 accounts: Arc<Vec<RewardAccountV2>>,
420 ) -> anyhow::Result<PermittedRewardMerkleTreeV2> {
421 (**self)
422 .fetch_reward_merkle_tree_v2(height, view, reward_merkle_tree_root, accounts)
423 .await
424 }
425
426 async fn try_fetch_reward_accounts_v1(
427 &self,
428 retry: usize,
429 instance: &NodeState,
430 height: u64,
431 view: ViewNumber,
432 reward_merkle_tree_root: RewardMerkleCommitmentV1,
433 accounts: &[RewardAccountV1],
434 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
435 (**self)
436 .try_fetch_reward_accounts_v1(
437 retry,
438 instance,
439 height,
440 view,
441 reward_merkle_tree_root,
442 accounts,
443 )
444 .await
445 }
446
447 async fn fetch_reward_accounts_v1(
448 &self,
449 instance: &NodeState,
450 height: u64,
451 view: ViewNumber,
452 reward_merkle_tree_root: RewardMerkleCommitmentV1,
453 accounts: Vec<RewardAccountV1>,
454 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
455 (**self)
456 .fetch_reward_accounts_v1(instance, height, view, reward_merkle_tree_root, accounts)
457 .await
458 }
459
460 async fn try_fetch_state_cert(
461 &self,
462 retry: usize,
463 epoch: u64,
464 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
465 (**self).try_fetch_state_cert(retry, epoch).await
466 }
467
468 async fn fetch_state_cert(
469 &self,
470 epoch: u64,
471 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
472 (**self).fetch_state_cert(epoch).await
473 }
474
475 fn backoff(&self) -> &BackoffParams {
476 (**self).backoff()
477 }
478
479 fn name(&self) -> String {
480 (**self).name()
481 }
482
483 fn is_local(&self) -> bool {
484 (**self).is_local()
485 }
486
487 fn with_local_timeout(&self, local_timeout: Duration) -> Option<Arc<dyn StateCatchup>> {
488 (**self).with_local_timeout(local_timeout)
489 }
490}
491
492#[cfg(feature = "node")]
493#[async_trait]
494pub trait PersistenceOptions: Clone + Send + Sync + Debug + 'static {
495 type Persistence: SequencerPersistence + MembershipPersistence;
496
497 fn set_view_retention(&mut self, view_retention: u64);
498 fn set_consensus_only(&mut self);
499 async fn create(&mut self) -> anyhow::Result<Self::Persistence>;
500 async fn reset(self) -> anyhow::Result<()>;
501}
502
503#[derive(Clone, Copy, Debug, PartialEq, Eq)]
507pub enum EventsPersistenceRead {
508 Complete,
509 UntilL1Block(u64),
510}
511
512pub type StakeTuple = (
514 AuthenticatedValidatorMap,
515 Option<RewardAmount>,
516 Option<StakeTableHash>,
517);
518
519#[async_trait]
520pub trait MembershipPersistence: Send + Sync + 'static {
522 async fn load_stake(&self, epoch: EpochNumber) -> anyhow::Result<Option<StakeTuple>>;
524
525 async fn load_latest_stake(&self, limit: u64) -> anyhow::Result<Option<Vec<IndexedStake>>>;
527
528 async fn load_drb_result(&self, epoch: EpochNumber) -> anyhow::Result<Option<DrbResult>>;
530
531 async fn load_epoch_root(&self, epoch: EpochNumber) -> anyhow::Result<Option<Header>>;
533
534 async fn store_epoch_root(
536 &self,
537 epoch: EpochNumber,
538 block_header: Header,
539 ) -> anyhow::Result<()>;
540
541 async fn store_stake(
543 &self,
544 epoch: EpochNumber,
545 stake: AuthenticatedValidatorMap,
546 block_reward: Option<RewardAmount>,
547 stake_table_hash: Option<StakeTableHash>,
548 ) -> anyhow::Result<()>;
549
550 async fn store_events(
551 &self,
552 l1_finalized: u64,
553 events: Vec<(EventKey, StakeTableEvent)>,
554 ) -> anyhow::Result<()>;
555 async fn load_events(
556 &self,
557 from_l1_block: u64,
558 l1_finalized: u64,
559 ) -> anyhow::Result<(
560 Option<EventsPersistenceRead>,
561 Vec<(EventKey, StakeTableEvent)>,
562 )>;
563
564 async fn delete_stake_tables(&self) -> anyhow::Result<()>;
566
567 async fn store_all_validators(
568 &self,
569 epoch: EpochNumber,
570 all_validators: IndexMap<Address, RegisteredValidator<PubKey>>,
571 ) -> anyhow::Result<()>;
572
573 async fn load_all_validators(
574 &self,
575 epoch: EpochNumber,
576 offset: u64,
577 limit: u64,
578 ) -> anyhow::Result<Vec<RegisteredValidator<PubKey>>>;
579}
580
581#[cfg(feature = "node")]
584#[async_trait]
585pub trait StateCertPersistence: Send + Sync + 'static {
586 async fn get_state_cert_by_epoch(
588 &self,
589 epoch: u64,
590 ) -> anyhow::Result<Option<LightClientStateUpdateCertificateV2<SeqTypes>>>;
591
592 async fn insert_state_cert(
594 &self,
595 epoch: u64,
596 cert: LightClientStateUpdateCertificateV2<SeqTypes>,
597 ) -> anyhow::Result<()>;
598}
599
600#[cfg(feature = "node")]
601#[async_trait]
602pub trait SequencerPersistence:
603 Sized + Send + Sync + Clone + 'static + MembershipPersistence + StateCertPersistence
604{
605 fn into_catchup_provider(
607 self,
608 _backoff: BackoffParams,
609 ) -> anyhow::Result<Arc<dyn StateCatchup>> {
610 bail!("state catchup is not implemented for this persistence type");
611 }
612
613 async fn load_config(&self) -> anyhow::Result<Option<NetworkConfig>>;
618
619 async fn save_config(&self, cfg: &NetworkConfig) -> anyhow::Result<()>;
621
622 async fn load_latest_acted_view(&self) -> anyhow::Result<Option<ViewNumber>>;
624
625 async fn load_restart_view(&self) -> anyhow::Result<Option<ViewNumber>>;
627
628 async fn load_quorum_proposals(
630 &self,
631 ) -> anyhow::Result<BTreeMap<ViewNumber, Proposal<SeqTypes, QuorumProposalWrapper<SeqTypes>>>>;
632
633 async fn load_quorum_proposal(
634 &self,
635 view: ViewNumber,
636 ) -> anyhow::Result<Proposal<SeqTypes, QuorumProposalWrapper<SeqTypes>>>;
637
638 async fn load_vid_share(
639 &self,
640 view: ViewNumber,
641 ) -> anyhow::Result<Option<Proposal<SeqTypes, VidDisperseShare<SeqTypes>>>>;
642 async fn load_da_proposal(
643 &self,
644 view: ViewNumber,
645 ) -> anyhow::Result<Option<Proposal<SeqTypes, DaProposal2<SeqTypes>>>>;
646 async fn load_upgrade_certificate(
647 &self,
648 ) -> anyhow::Result<Option<UpgradeCertificate<SeqTypes>>>;
649 async fn load_start_epoch_info(&self) -> anyhow::Result<Vec<InitializerEpochInfo<SeqTypes>>>;
650 async fn load_state_cert(
651 &self,
652 ) -> anyhow::Result<Option<LightClientStateUpdateCertificateV2<SeqTypes>>>;
653
654 async fn load_consensus_state(
661 &self,
662 state: NodeState,
663 upgrade: Upgrade,
664 ) -> anyhow::Result<(HotShotInitializer<SeqTypes>, Option<ViewNumber>)> {
665 let genesis_validated_state = ValidatedState::genesis(&state).0;
666 let highest_voted_view = match self
667 .load_latest_acted_view()
668 .await
669 .context("loading last voted view")?
670 {
671 Some(view) => {
672 tracing::info!(?view, "starting with last actioned view");
673 view
674 },
675 None => {
676 tracing::info!("no saved view, starting from genesis");
677 ViewNumber::genesis()
678 },
679 };
680
681 let restart_view = match self
682 .load_restart_view()
683 .await
684 .context("loading restart view")?
685 {
686 Some(view) => {
687 tracing::info!(?view, "starting from saved view");
688 view
689 },
690 None => {
691 tracing::info!("no saved view, starting from genesis");
692 ViewNumber::genesis()
693 },
694 };
695 let config = self.load_config().await.context("loading config")?;
696 let epoch_height = state.epoch_height.unwrap_or_else(|| {
698 config
699 .as_ref()
700 .map(|c| c.config.epoch_height)
701 .unwrap_or_default()
702 });
703 let (leaf, cert_pair, anchor_view) = match self
704 .load_anchor_leaf()
705 .await
706 .context("loading anchor leaf")?
707 {
708 Some((leaf, cert_pair)) => {
709 tracing::info!(?leaf, ?cert_pair, "starting from saved leaf");
710 let high_qc = cert_pair.qc().clone();
711 let leaf_view = leaf.view_number();
712 ensure!(
713 leaf_view == high_qc.view_number,
714 format!(
715 "loaded anchor leaf from view {}, but high QC is from view {}",
716 leaf_view, high_qc.view_number
717 )
718 );
719 if leaf.block_header().version() < NEW_PROTOCOL_VERSION {
720 ensure!(
721 epoch_height == 0
722 || cert_pair.block_number().is_none()
723 || cert_pair.verify_next_epoch_qc(epoch_height).is_ok(),
724 format!(
725 "Next epoch QC is required but it's not present or doesn't match \
726 primary QC\nPrimary QC: {:?}\nNext epoch QC: {:?}",
727 cert_pair.qc(),
728 cert_pair.next_epoch_qc()
729 )
730 );
731 }
732
733 let anchor_view = leaf.view_number();
734 (leaf, cert_pair, Some(anchor_view))
735 },
736 None => {
737 tracing::info!("no saved leaf, starting from genesis leaf");
738 let genesis_qc =
739 QuorumCertificate2::genesis(&genesis_validated_state, &state, upgrade).await;
740 (
741 hotshot_types::data::Leaf2::genesis(
742 &genesis_validated_state,
743 &state,
744 upgrade.base,
745 )
746 .await,
747 CertificatePair::new(genesis_qc, None),
748 None,
749 )
750 },
751 };
752
753 let mut high_qc = cert_pair.qc().clone();
754 if let Some((extended_high_qc, _)) = self.load_eqc().await
755 && extended_high_qc.view_number() > high_qc.view_number()
756 {
757 high_qc = extended_high_qc;
758 }
759
760 if let Some(running_high_qc) = self
761 .load_high_qc2()
762 .await
763 .context("loading persisted high qc")?
764 && running_high_qc.view_number() > high_qc.view_number()
765 {
766 high_qc = running_high_qc;
767 }
768
769 let validated_state = if leaf.block_header().height() == 0 {
770 genesis_validated_state
772 } else {
773 ValidatedState::from_header(leaf.block_header())
776 };
777
778 let restart_view = max(restart_view, leaf.view_number());
783
784 let saved_proposals = self
785 .load_quorum_proposals()
786 .await
787 .context("loading saved proposals")?;
788
789 let upgrade_certificate = self
790 .load_upgrade_certificate()
791 .await
792 .context("loading upgrade certificate")?;
793
794 let start_epoch_info = self
795 .load_start_epoch_info()
796 .await
797 .context("loading start epoch info")?;
798
799 let state_cert = self
800 .load_state_cert()
801 .await
802 .context("loading light client state update certificate")?;
803
804 tracing::warn!(
805 ?leaf,
806 ?restart_view,
807 ?high_qc,
808 ?validated_state,
809 ?state_cert,
810 "loaded consensus state"
811 );
812
813 Ok((
814 HotShotInitializer::load(
815 state,
816 epoch_height,
817 start_epoch_info,
818 (leaf, Some(Arc::new(validated_state))),
819 restart_view,
820 high_qc,
821 highest_voted_view,
822 saved_proposals,
823 upgrade_certificate,
824 state_cert,
825 ),
826 anchor_view,
827 ))
828 }
829
830 async fn persist_event(
838 &self,
839 event: &CoordinatorEvent<SeqTypes>,
840 consumer: &(impl EventConsumer + 'static),
841 ) -> Option<ViewNumber> {
842 match event {
843 CoordinatorEvent::NewDecide {
844 leaf_infos,
845 cert1,
846 cert2,
847 } => {
848 let first = leaf_infos.first()?;
849 let decided_view = first.leaf.view_number();
850
851 let certifying_qcs = std::iter::once(cert1.clone())
854 .chain(leaf_infos.iter().map(|info| info.leaf.justify_qc().clone()))
855 .take(leaf_infos.len())
856 .map(CertificatePair::non_epoch_change);
857
858 if let Err(err) = self
859 .persist_decided_leaves(
860 leaf_infos.iter().zip(certifying_qcs),
861 cert2.as_ref(),
862 consumer,
863 )
864 .await
865 {
866 tracing::error!(
867 "failed to save decided leaves from new protocol, chain may not be up to \
868 date: {err:#}"
869 );
870 return None;
871 }
872 Some(decided_view)
873 },
874 _ => None,
875 }
876 }
877
878 async fn append_decided_leaves(
908 &self,
909 decided_view: ViewNumber,
910 leaf_chain: impl IntoIterator<Item = (&LeafInfo<SeqTypes>, CertificatePair<SeqTypes>)> + Send,
911 consumer: &(impl EventConsumer + 'static),
912 ) -> anyhow::Result<()> {
913 self.persist_decided_leaves(leaf_chain, None, consumer)
914 .await?;
915 if let Err(err) = self.process_decided_events(decided_view, consumer).await {
917 tracing::warn!(?decided_view, "decide event processing failed: {err:#}");
918 }
919 Ok(())
920 }
921
922 async fn persist_decided_leaves(
931 &self,
932 leaf_chain: impl IntoIterator<Item = (&LeafInfo<SeqTypes>, CertificatePair<SeqTypes>)> + Send,
933 cert2: Option<&Certificate2<SeqTypes>>,
934 consumer: &(impl EventConsumer + 'static),
935 ) -> anyhow::Result<()>;
936
937 async fn process_decided_events(
948 &self,
949 decided_view: ViewNumber,
950 _consumer: &(impl EventConsumer + 'static),
951 ) -> anyhow::Result<Option<ViewNumber>> {
952 Ok(Some(decided_view))
953 }
954
955 async fn load_anchor_leaf(&self) -> anyhow::Result<Option<(Leaf2, CertificatePair<SeqTypes>)>>;
956 async fn append_vid(
957 &self,
958 proposal: &Proposal<SeqTypes, VidDisperseShare<SeqTypes>>,
959 ) -> anyhow::Result<()>;
960 async fn append_da(
961 &self,
962 proposal: &Proposal<SeqTypes, DaProposal<SeqTypes>>,
963 vid_commit: VidCommitment,
964 ) -> anyhow::Result<()>;
965 async fn record_action(&self, view: ViewNumber, action: HotShotAction) -> anyhow::Result<()>;
966
967 async fn append_quorum_proposal2(
968 &self,
969 proposal: &Proposal<SeqTypes, QuorumProposalWrapper<SeqTypes>>,
970 ) -> anyhow::Result<()>;
971
972 async fn append_cert2(
974 &self,
975 _view: ViewNumber,
976 _cert2: Certificate2<SeqTypes>,
977 ) -> anyhow::Result<()> {
978 Ok(())
979 }
980
981 async fn load_cert2(
983 &self,
984 _view: ViewNumber,
985 ) -> anyhow::Result<Option<Certificate2<SeqTypes>>> {
986 Ok(None)
987 }
988
989 async fn append_high_qc2(&self, _high_qc: QuorumCertificate2<SeqTypes>) -> anyhow::Result<()> {
995 Ok(())
996 }
997
998 async fn load_high_qc2(&self) -> anyhow::Result<Option<QuorumCertificate2<SeqTypes>>> {
1000 Ok(None)
1001 }
1002
1003 async fn append_boundary_qc2(&self, _qc: QuorumCertificate2<SeqTypes>) -> anyhow::Result<()> {
1009 Ok(())
1010 }
1011
1012 async fn load_boundary_qc2(&self) -> anyhow::Result<Option<QuorumCertificate2<SeqTypes>>> {
1014 Ok(None)
1015 }
1016
1017 async fn store_eqc(
1019 &self,
1020 _high_qc: QuorumCertificate2<SeqTypes>,
1021 _next_epoch_high_qc: NextEpochQuorumCertificate2<SeqTypes>,
1022 ) -> anyhow::Result<()>;
1023
1024 async fn load_eqc(
1026 &self,
1027 ) -> Option<(
1028 QuorumCertificate2<SeqTypes>,
1029 NextEpochQuorumCertificate2<SeqTypes>,
1030 )>;
1031
1032 async fn store_upgrade_certificate(
1033 &self,
1034 decided_upgrade_certificate: Option<UpgradeCertificate<SeqTypes>>,
1035 ) -> anyhow::Result<()>;
1036
1037 async fn load_anchor_view(&self) -> anyhow::Result<ViewNumber> {
1038 match self.load_anchor_leaf().await? {
1039 Some((leaf, _)) => Ok(leaf.view_number()),
1040 None => Ok(ViewNumber::genesis()),
1041 }
1042 }
1043
1044 async fn append_next_epoch_high_qc2(
1045 &self,
1046 _next_epoch_high_qc: NextEpochQuorumCertificate2<SeqTypes>,
1047 ) -> anyhow::Result<()> {
1048 Ok(())
1049 }
1050
1051 async fn append_da2(
1052 &self,
1053 proposal: &Proposal<SeqTypes, DaProposal2<SeqTypes>>,
1054 vid_commit: VidCommitment,
1055 ) -> anyhow::Result<()>;
1056
1057 async fn store_drb_result(
1058 &self,
1059 epoch: EpochNumber,
1060 drb_result: DrbResult,
1061 ) -> anyhow::Result<()>;
1062 async fn store_drb_input(&self, drb_input: DrbInput) -> anyhow::Result<()>;
1063 async fn load_drb_input(&self, epoch: u64) -> anyhow::Result<DrbInput>;
1064 async fn add_state_cert(
1065 &self,
1066 state_cert: LightClientStateUpdateCertificateV2<SeqTypes>,
1067 ) -> anyhow::Result<()>;
1068
1069 fn enable_metrics(&mut self, metrics: &dyn Metrics);
1070}
1071
1072#[async_trait]
1073pub trait EventConsumer: Debug + Send + Sync {
1074 async fn handle_event(&self, event: &CoordinatorEvent<SeqTypes>) -> anyhow::Result<()>;
1075}
1076
1077#[async_trait]
1078impl<T> EventConsumer for Box<T>
1079where
1080 T: EventConsumer + ?Sized,
1081{
1082 async fn handle_event(&self, event: &CoordinatorEvent<SeqTypes>) -> anyhow::Result<()> {
1083 (**self).handle_event(event).await
1084 }
1085}
1086
1087#[derive(Clone, Copy, Debug)]
1088pub struct NullEventConsumer;
1089
1090#[async_trait]
1091impl EventConsumer for NullEventConsumer {
1092 async fn handle_event(&self, _event: &CoordinatorEvent<SeqTypes>) -> anyhow::Result<()> {
1093 Ok(())
1094 }
1095}
1096
1097#[cfg(feature = "node")]
1098#[async_trait]
1099impl<P: SequencerPersistence> Storage<SeqTypes> for Arc<P> {
1100 async fn append_vid(
1101 &self,
1102 proposal: &Proposal<SeqTypes, VidDisperseShare<SeqTypes>>,
1103 ) -> anyhow::Result<()> {
1104 (**self).append_vid(proposal).await
1105 }
1106
1107 async fn append_da(
1108 &self,
1109 proposal: &Proposal<SeqTypes, DaProposal<SeqTypes>>,
1110 vid_commit: VidCommitment,
1111 ) -> anyhow::Result<()> {
1112 (**self).append_da(proposal, vid_commit).await
1113 }
1114
1115 async fn append_da2(
1116 &self,
1117 proposal: &Proposal<SeqTypes, DaProposal2<SeqTypes>>,
1118 vid_commit: VidCommitment,
1119 ) -> anyhow::Result<()> {
1120 (**self).append_da2(proposal, vid_commit).await
1121 }
1122
1123 async fn record_action(&self, view: ViewNumber, action: HotShotAction) -> anyhow::Result<()> {
1124 (**self).record_action(view, action).await
1125 }
1126
1127 async fn append_proposal_wrapper(
1128 &self,
1129 proposal: &Proposal<SeqTypes, QuorumProposalWrapper<SeqTypes>>,
1130 ) -> anyhow::Result<()> {
1131 (**self).append_quorum_proposal2(proposal).await
1132 }
1133
1134 async fn update_decided_upgrade_certificate(
1135 &self,
1136 decided_upgrade_certificate: Option<UpgradeCertificate<SeqTypes>>,
1137 ) -> anyhow::Result<()> {
1138 (**self)
1139 .store_upgrade_certificate(decided_upgrade_certificate)
1140 .await
1141 }
1142
1143 async fn store_drb_result(
1144 &self,
1145 epoch: EpochNumber,
1146 drb_result: DrbResult,
1147 ) -> anyhow::Result<()> {
1148 (**self).store_drb_result(epoch, drb_result).await
1149 }
1150
1151 async fn store_epoch_root(
1152 &self,
1153 epoch: EpochNumber,
1154 block_header: <SeqTypes as NodeType>::BlockHeader,
1155 ) -> anyhow::Result<()> {
1156 (**self).store_epoch_root(epoch, block_header).await
1157 }
1158
1159 async fn store_drb_input(&self, drb_input: DrbInput) -> anyhow::Result<()> {
1160 (**self).store_drb_input(drb_input).await
1161 }
1162
1163 async fn load_drb_input(&self, epoch: u64) -> anyhow::Result<DrbInput> {
1164 (**self).load_drb_input(epoch).await
1165 }
1166
1167 async fn update_state_cert(
1168 &self,
1169 state_cert: LightClientStateUpdateCertificateV2<SeqTypes>,
1170 ) -> anyhow::Result<()> {
1171 (**self).add_state_cert(state_cert).await
1172 }
1173}
1174
1175#[cfg(feature = "node")]
1176#[async_trait]
1177impl<P: SequencerPersistence> NewProtocolStorage<SeqTypes> for Arc<P> {
1178 async fn append_high_qc2(&self, high_qc: Certificate1<SeqTypes>) -> anyhow::Result<()> {
1179 (**self).append_high_qc2(high_qc).await
1184 }
1185
1186 async fn load_high_qc2(&self) -> anyhow::Result<Option<Certificate1<SeqTypes>>> {
1187 (**self).load_high_qc2().await
1188 }
1189
1190 async fn append_boundary_qc2(&self, qc: Certificate1<SeqTypes>) -> anyhow::Result<()> {
1191 (**self).append_boundary_qc2(qc).await
1192 }
1193
1194 async fn load_boundary_qc2(&self) -> anyhow::Result<Option<Certificate1<SeqTypes>>> {
1195 (**self).load_boundary_qc2().await
1196 }
1197}
1198
1199pub trait FromNsPayloadBytes<'a> {
1204 fn from_payload_bytes(bytes: &'a [u8]) -> Self;
1206}
1207
1208pub trait NsPayloadBytesRange<'a> {
1213 type Output: FromNsPayloadBytes<'a>;
1214
1215 fn ns_payload_range(&self) -> Range<usize>;
1217}
1218
1219pub trait FromStringOrInteger: Sized {
1230 type Binary: Serialize + DeserializeOwned;
1231 type Integer: Serialize + DeserializeOwned;
1232
1233 fn from_binary(b: Self::Binary) -> anyhow::Result<Self>;
1234 fn from_string(s: String) -> anyhow::Result<Self>;
1235 fn from_integer(i: Self::Integer) -> anyhow::Result<Self>;
1236
1237 fn to_binary(&self) -> anyhow::Result<Self::Binary>;
1238 fn to_string(&self) -> anyhow::Result<String>;
1239}