1#[cfg(feature = "node")]
4use std::{cmp::max, collections::BTreeMap};
5use std::{fmt::Debug, ops::Range, sync::Arc};
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::{HotShotInitializer, InitializerEpochInfo, types::EventType};
15#[cfg(feature = "node")]
16use hotshot_libp2p_networking::network::behaviours::dht::store::persistent::DhtPersistentStorage;
17#[cfg(feature = "node")]
18use hotshot_new_protocol::storage::NewProtocolStorage;
19#[cfg(feature = "node")]
20use hotshot_types::simple_certificate::{Certificate1, Certificate2};
21#[cfg(feature = "node")]
22use hotshot_types::{
23 data::{
24 DaProposal, DaProposal2, QuorumProposal, QuorumProposal2, QuorumProposalWrapper,
25 VidCommitment, VidDisperseShare,
26 },
27 drb::{DrbInput, DrbResult},
28 event::{HotShotAction, LeafInfo},
29 message::{Proposal, convert_proposal},
30 simple_certificate::{
31 CertificatePair, NextEpochQuorumCertificate2, QuorumCertificate, QuorumCertificate2,
32 UpgradeCertificate,
33 },
34 traits::{
35 ValidatedState as HotShotState, metrics::Metrics, node_implementation::NodeType,
36 storage::Storage,
37 },
38 utils::genesis_epoch_from_version,
39 vote::HasViewNumber,
40};
41use hotshot_types::{
42 data::{EpochNumber, ViewNumber},
43 epoch_membership::EpochMembershipCoordinator,
44 new_protocol::CoordinatorEvent,
45 simple_certificate::LightClientStateUpdateCertificateV2,
46};
47use indexmap::IndexMap;
48use serde::{Serialize, de::DeserializeOwned};
49#[cfg(feature = "node")]
50use versions::{NEW_PROTOCOL_VERSION, Upgrade};
51
52use super::{
53 impls::NodeState,
54 utils::BackoffParams,
55 v0_3::{EventKey, IndexedStake, StakeTableEvent},
56};
57use crate::{
58 AuthenticatedValidatorMap, BlockMerkleTree, FeeAccount, FeeAccountProof, FeeMerkleCommitment,
59 Leaf2, PubKey, SeqTypes,
60 v0::impls::StakeTableHash,
61 v0_3::{
62 ChainConfig, RegisteredValidator, RewardAccountProofV1, RewardAccountV1, RewardAmount,
63 RewardMerkleCommitmentV1,
64 },
65 v0_4::{PermittedRewardMerkleTreeV2, RewardAccountV2, RewardMerkleCommitmentV2},
66};
67#[cfg(feature = "node")]
68use crate::{NetworkConfig, v0::impls::ValidatedState};
69
70#[async_trait]
71pub trait StateCatchup: Send + Sync {
72 async fn try_fetch_leaf(
78 &self,
79 retry: usize,
80 coordinator: EpochMembershipCoordinator<SeqTypes>,
81 height: u64,
82 ) -> anyhow::Result<Leaf2>;
83
84 async fn fetch_leaf(
86 &self,
87 coordinator: EpochMembershipCoordinator<SeqTypes>,
88 height: u64,
89 ) -> anyhow::Result<Leaf2> {
90 self.backoff()
91 .retry(self, |provider, retry| {
92 let coordinator = coordinator.clone();
93 async move { provider.try_fetch_leaf(retry, coordinator, height).await }.boxed()
94 })
95 .await
96 }
97
98 async fn try_fetch_accounts(
100 &self,
101 retry: usize,
102 instance: &NodeState,
103 height: u64,
104 view: ViewNumber,
105 fee_merkle_tree_root: FeeMerkleCommitment,
106 accounts: &[FeeAccount],
107 ) -> anyhow::Result<Vec<FeeAccountProof>>;
108
109 async fn fetch_accounts(
111 &self,
112 instance: &NodeState,
113 height: u64,
114 view: ViewNumber,
115 fee_merkle_tree_root: FeeMerkleCommitment,
116 accounts: Vec<FeeAccount>,
117 ) -> anyhow::Result<Vec<FeeAccountProof>> {
118 self.backoff()
119 .retry(self, |provider, retry| {
120 let accounts = &accounts;
121 async move {
122 provider
123 .try_fetch_accounts(
124 retry,
125 instance,
126 height,
127 view,
128 fee_merkle_tree_root,
129 accounts,
130 )
131 .await
132 .map_err(|err| {
133 err.context(format!(
134 "fetching accounts {accounts:?}, height {height}, view {view}"
135 ))
136 })
137 }
138 .boxed()
139 })
140 .await
141 }
142
143 async fn try_remember_blocks_merkle_tree(
145 &self,
146 retry: usize,
147 instance: &NodeState,
148 height: u64,
149 view: ViewNumber,
150 mt: &mut BlockMerkleTree,
151 ) -> anyhow::Result<()>;
152
153 async fn remember_blocks_merkle_tree(
155 &self,
156 instance: &NodeState,
157 height: u64,
158 view: ViewNumber,
159 mt: &mut BlockMerkleTree,
160 ) -> anyhow::Result<()> {
161 self.backoff()
162 .retry(mt, |mt, retry| {
163 self.try_remember_blocks_merkle_tree(retry, instance, height, view, mt)
164 .map_err(|err| err.context(format!("fetching frontier using {}", self.name())))
165 .boxed()
166 })
167 .await
168 }
169
170 async fn try_fetch_chain_config(
172 &self,
173 retry: usize,
174 commitment: Commitment<ChainConfig>,
175 ) -> anyhow::Result<ChainConfig>;
176
177 async fn fetch_chain_config(
179 &self,
180 commitment: Commitment<ChainConfig>,
181 ) -> anyhow::Result<ChainConfig> {
182 self.backoff()
183 .retry(self, |provider, retry| {
184 provider
185 .try_fetch_chain_config(retry, commitment)
186 .map_err(|err| err.context("fetching chain config"))
187 .boxed()
188 })
189 .await
190 }
191
192 async fn try_fetch_reward_merkle_tree_v2(
194 &self,
195 retry: usize,
196 height: u64,
197 view: ViewNumber,
198 reward_merkle_tree_root: RewardMerkleCommitmentV2,
199 accounts: Arc<Vec<RewardAccountV2>>,
200 ) -> anyhow::Result<PermittedRewardMerkleTreeV2>;
201
202 async fn fetch_reward_merkle_tree_v2(
203 &self,
204 height: u64,
205 view: ViewNumber,
206 reward_merkle_tree_root: RewardMerkleCommitmentV2,
207 accounts: Arc<Vec<RewardAccountV2>>,
208 ) -> anyhow::Result<PermittedRewardMerkleTreeV2> {
209 self.backoff()
210 .retry(self, |provider, retry| {
211 let accounts = accounts.clone();
212 async move {
213 provider
214 .try_fetch_reward_merkle_tree_v2(
215 retry,
216 height,
217 view,
218 reward_merkle_tree_root,
219 accounts,
220 )
221 .await
222 .map_err(|err| {
223 err.context(format!("fetching reward merkle tree for height {height}"))
224 })
225 }
226 .boxed()
227 })
228 .await
229 }
230
231 async fn try_fetch_reward_accounts_v1(
233 &self,
234 retry: usize,
235 instance: &NodeState,
236 height: u64,
237 view: ViewNumber,
238 reward_merkle_tree_root: RewardMerkleCommitmentV1,
239 accounts: &[RewardAccountV1],
240 ) -> anyhow::Result<Vec<RewardAccountProofV1>>;
241
242 async fn fetch_reward_accounts_v1(
244 &self,
245 instance: &NodeState,
246 height: u64,
247 view: ViewNumber,
248 reward_merkle_tree_root: RewardMerkleCommitmentV1,
249 accounts: Vec<RewardAccountV1>,
250 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
251 self.backoff()
252 .retry(self, |provider, retry| {
253 let accounts = &accounts;
254 async move {
255 provider
256 .try_fetch_reward_accounts_v1(
257 retry,
258 instance,
259 height,
260 view,
261 reward_merkle_tree_root,
262 accounts,
263 )
264 .await
265 .map_err(|err| {
266 err.context(format!(
267 "fetching v1 reward accounts {accounts:?}, height {height}, view \
268 {view}"
269 ))
270 })
271 }
272 .boxed()
273 })
274 .await
275 }
276
277 async fn try_fetch_state_cert(
279 &self,
280 retry: usize,
281 epoch: u64,
282 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>>;
283
284 async fn fetch_state_cert(
286 &self,
287 epoch: u64,
288 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
289 self.backoff()
290 .retry(self, |provider, retry| {
291 provider
292 .try_fetch_state_cert(retry, epoch)
293 .map_err(|err| err.context(format!("fetching state cert for epoch {epoch}")))
294 .boxed()
295 })
296 .await
297 }
298
299 fn is_local(&self) -> bool;
301
302 fn backoff(&self) -> &BackoffParams;
304
305 fn name(&self) -> String;
307}
308
309#[async_trait]
310impl<T: StateCatchup + ?Sized> StateCatchup for Arc<T> {
311 async fn try_fetch_leaf(
312 &self,
313 retry: usize,
314 coordinator: EpochMembershipCoordinator<SeqTypes>,
315 height: u64,
316 ) -> anyhow::Result<Leaf2> {
317 (**self).try_fetch_leaf(retry, coordinator, height).await
318 }
319
320 async fn fetch_leaf(
321 &self,
322 coordinator: EpochMembershipCoordinator<SeqTypes>,
323 height: u64,
324 ) -> anyhow::Result<Leaf2> {
325 (**self).fetch_leaf(coordinator, height).await
326 }
327
328 async fn try_fetch_accounts(
329 &self,
330 retry: usize,
331 instance: &NodeState,
332 height: u64,
333 view: ViewNumber,
334 fee_merkle_tree_root: FeeMerkleCommitment,
335 accounts: &[FeeAccount],
336 ) -> anyhow::Result<Vec<FeeAccountProof>> {
337 (**self)
338 .try_fetch_accounts(
339 retry,
340 instance,
341 height,
342 view,
343 fee_merkle_tree_root,
344 accounts,
345 )
346 .await
347 }
348
349 async fn fetch_accounts(
350 &self,
351 instance: &NodeState,
352 height: u64,
353 view: ViewNumber,
354 fee_merkle_tree_root: FeeMerkleCommitment,
355 accounts: Vec<FeeAccount>,
356 ) -> anyhow::Result<Vec<FeeAccountProof>> {
357 (**self)
358 .fetch_accounts(instance, height, view, fee_merkle_tree_root, accounts)
359 .await
360 }
361
362 async fn try_remember_blocks_merkle_tree(
363 &self,
364 retry: usize,
365 instance: &NodeState,
366 height: u64,
367 view: ViewNumber,
368 mt: &mut BlockMerkleTree,
369 ) -> anyhow::Result<()> {
370 (**self)
371 .try_remember_blocks_merkle_tree(retry, instance, height, view, mt)
372 .await
373 }
374
375 async fn remember_blocks_merkle_tree(
376 &self,
377 instance: &NodeState,
378 height: u64,
379 view: ViewNumber,
380 mt: &mut BlockMerkleTree,
381 ) -> anyhow::Result<()> {
382 (**self)
383 .remember_blocks_merkle_tree(instance, height, view, mt)
384 .await
385 }
386
387 async fn try_fetch_chain_config(
388 &self,
389 retry: usize,
390 commitment: Commitment<ChainConfig>,
391 ) -> anyhow::Result<ChainConfig> {
392 (**self).try_fetch_chain_config(retry, commitment).await
393 }
394
395 async fn fetch_chain_config(
396 &self,
397 commitment: Commitment<ChainConfig>,
398 ) -> anyhow::Result<ChainConfig> {
399 (**self).fetch_chain_config(commitment).await
400 }
401
402 async fn try_fetch_reward_merkle_tree_v2(
403 &self,
404 retry: usize,
405 height: u64,
406 view: ViewNumber,
407 reward_merkle_tree_root: RewardMerkleCommitmentV2,
408 accounts: Arc<Vec<RewardAccountV2>>,
409 ) -> anyhow::Result<PermittedRewardMerkleTreeV2> {
410 (**self)
411 .try_fetch_reward_merkle_tree_v2(retry, height, view, reward_merkle_tree_root, accounts)
412 .await
413 }
414
415 async fn fetch_reward_merkle_tree_v2(
416 &self,
417 height: u64,
418 view: ViewNumber,
419 reward_merkle_tree_root: RewardMerkleCommitmentV2,
420 accounts: Arc<Vec<RewardAccountV2>>,
421 ) -> anyhow::Result<PermittedRewardMerkleTreeV2> {
422 (**self)
423 .fetch_reward_merkle_tree_v2(height, view, reward_merkle_tree_root, accounts)
424 .await
425 }
426
427 async fn try_fetch_reward_accounts_v1(
428 &self,
429 retry: usize,
430 instance: &NodeState,
431 height: u64,
432 view: ViewNumber,
433 reward_merkle_tree_root: RewardMerkleCommitmentV1,
434 accounts: &[RewardAccountV1],
435 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
436 (**self)
437 .try_fetch_reward_accounts_v1(
438 retry,
439 instance,
440 height,
441 view,
442 reward_merkle_tree_root,
443 accounts,
444 )
445 .await
446 }
447
448 async fn fetch_reward_accounts_v1(
449 &self,
450 instance: &NodeState,
451 height: u64,
452 view: ViewNumber,
453 reward_merkle_tree_root: RewardMerkleCommitmentV1,
454 accounts: Vec<RewardAccountV1>,
455 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
456 (**self)
457 .fetch_reward_accounts_v1(instance, height, view, reward_merkle_tree_root, accounts)
458 .await
459 }
460
461 async fn try_fetch_state_cert(
462 &self,
463 retry: usize,
464 epoch: u64,
465 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
466 (**self).try_fetch_state_cert(retry, epoch).await
467 }
468
469 async fn fetch_state_cert(
470 &self,
471 epoch: u64,
472 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
473 (**self).fetch_state_cert(epoch).await
474 }
475
476 fn backoff(&self) -> &BackoffParams {
477 (**self).backoff()
478 }
479
480 fn name(&self) -> String {
481 (**self).name()
482 }
483
484 fn is_local(&self) -> bool {
485 (**self).is_local()
486 }
487}
488
489#[cfg(feature = "node")]
490#[async_trait]
491pub trait PersistenceOptions: Clone + Send + Sync + Debug + 'static {
492 type Persistence: SequencerPersistence + MembershipPersistence;
493
494 fn set_view_retention(&mut self, view_retention: u64);
495 async fn create(&mut self) -> anyhow::Result<Self::Persistence>;
496 async fn reset(self) -> anyhow::Result<()>;
497}
498
499#[derive(Clone, Copy, Debug, PartialEq, Eq)]
503pub enum EventsPersistenceRead {
504 Complete,
505 UntilL1Block(u64),
506}
507
508pub type StakeTuple = (
510 AuthenticatedValidatorMap,
511 Option<RewardAmount>,
512 Option<StakeTableHash>,
513);
514
515#[async_trait]
516pub trait MembershipPersistence: Send + Sync + 'static {
518 async fn load_stake(&self, epoch: EpochNumber) -> anyhow::Result<Option<StakeTuple>>;
520
521 async fn load_latest_stake(&self, limit: u64) -> anyhow::Result<Option<Vec<IndexedStake>>>;
523
524 async fn store_stake(
526 &self,
527 epoch: EpochNumber,
528 stake: AuthenticatedValidatorMap,
529 block_reward: Option<RewardAmount>,
530 stake_table_hash: Option<StakeTableHash>,
531 ) -> anyhow::Result<()>;
532
533 async fn store_events(
534 &self,
535 l1_finalized: u64,
536 events: Vec<(EventKey, StakeTableEvent)>,
537 ) -> anyhow::Result<()>;
538 async fn load_events(
539 &self,
540 from_l1_block: u64,
541 l1_finalized: u64,
542 ) -> anyhow::Result<(
543 Option<EventsPersistenceRead>,
544 Vec<(EventKey, StakeTableEvent)>,
545 )>;
546
547 async fn delete_stake_tables(&self) -> anyhow::Result<()>;
549
550 async fn store_all_validators(
551 &self,
552 epoch: EpochNumber,
553 all_validators: IndexMap<Address, RegisteredValidator<PubKey>>,
554 ) -> anyhow::Result<()>;
555
556 async fn load_all_validators(
557 &self,
558 epoch: EpochNumber,
559 offset: u64,
560 limit: u64,
561 ) -> anyhow::Result<Vec<RegisteredValidator<PubKey>>>;
562}
563
564#[cfg(feature = "node")]
565#[async_trait]
566pub trait SequencerPersistence:
567 Sized + Send + Sync + Clone + 'static + DhtPersistentStorage + MembershipPersistence
568{
569 fn into_catchup_provider(
571 self,
572 _backoff: BackoffParams,
573 ) -> anyhow::Result<Arc<dyn StateCatchup>> {
574 bail!("state catchup is not implemented for this persistence type");
575 }
576
577 async fn load_config(&self) -> anyhow::Result<Option<NetworkConfig>>;
582
583 async fn save_config(&self, cfg: &NetworkConfig) -> anyhow::Result<()>;
585
586 async fn load_latest_acted_view(&self) -> anyhow::Result<Option<ViewNumber>>;
588
589 async fn load_restart_view(&self) -> anyhow::Result<Option<ViewNumber>>;
591
592 async fn load_quorum_proposals(
594 &self,
595 ) -> anyhow::Result<BTreeMap<ViewNumber, Proposal<SeqTypes, QuorumProposalWrapper<SeqTypes>>>>;
596
597 async fn load_quorum_proposal(
598 &self,
599 view: ViewNumber,
600 ) -> anyhow::Result<Proposal<SeqTypes, QuorumProposalWrapper<SeqTypes>>>;
601
602 async fn load_vid_share(
603 &self,
604 view: ViewNumber,
605 ) -> anyhow::Result<Option<Proposal<SeqTypes, VidDisperseShare<SeqTypes>>>>;
606 async fn load_da_proposal(
607 &self,
608 view: ViewNumber,
609 ) -> anyhow::Result<Option<Proposal<SeqTypes, DaProposal2<SeqTypes>>>>;
610 async fn load_upgrade_certificate(
611 &self,
612 ) -> anyhow::Result<Option<UpgradeCertificate<SeqTypes>>>;
613 async fn load_start_epoch_info(&self) -> anyhow::Result<Vec<InitializerEpochInfo<SeqTypes>>>;
614 async fn load_state_cert(
615 &self,
616 ) -> anyhow::Result<Option<LightClientStateUpdateCertificateV2<SeqTypes>>>;
617
618 async fn get_state_cert_by_epoch(
620 &self,
621 epoch: u64,
622 ) -> anyhow::Result<Option<LightClientStateUpdateCertificateV2<SeqTypes>>>;
623
624 async fn insert_state_cert(
626 &self,
627 epoch: u64,
628 cert: LightClientStateUpdateCertificateV2<SeqTypes>,
629 ) -> anyhow::Result<()>;
630
631 async fn load_consensus_state(
638 &self,
639 state: NodeState,
640 upgrade: Upgrade,
641 ) -> anyhow::Result<(HotShotInitializer<SeqTypes>, Option<ViewNumber>)> {
642 let genesis_validated_state = ValidatedState::genesis(&state).0;
643 let highest_voted_view = match self
644 .load_latest_acted_view()
645 .await
646 .context("loading last voted view")?
647 {
648 Some(view) => {
649 tracing::info!(?view, "starting with last actioned view");
650 view
651 },
652 None => {
653 tracing::info!("no saved view, starting from genesis");
654 ViewNumber::genesis()
655 },
656 };
657
658 let restart_view = match self
659 .load_restart_view()
660 .await
661 .context("loading restart view")?
662 {
663 Some(view) => {
664 tracing::info!(?view, "starting from saved view");
665 view
666 },
667 None => {
668 tracing::info!("no saved view, starting from genesis");
669 ViewNumber::genesis()
670 },
671 };
672 let config = self.load_config().await.context("loading config")?;
673 let epoch_height = state.epoch_height.unwrap_or_else(|| {
675 config
676 .as_ref()
677 .map(|c| c.config.epoch_height)
678 .unwrap_or_default()
679 });
680 let (leaf, cert_pair, anchor_view) = match self
681 .load_anchor_leaf()
682 .await
683 .context("loading anchor leaf")?
684 {
685 Some((leaf, cert_pair)) => {
686 tracing::info!(?leaf, ?cert_pair, "starting from saved leaf");
687 let high_qc = cert_pair.qc().clone();
688 let leaf_view = leaf.view_number();
689 ensure!(
690 leaf_view == high_qc.view_number,
691 format!(
692 "loaded anchor leaf from view {}, but high QC is from view {}",
693 leaf_view, high_qc.view_number
694 )
695 );
696 if leaf.block_header().version() < NEW_PROTOCOL_VERSION {
697 ensure!(
698 epoch_height == 0
699 || cert_pair.block_number().is_none()
700 || cert_pair.verify_next_epoch_qc(epoch_height).is_ok(),
701 format!(
702 "Next epoch QC is required but it's not present or doesn't match \
703 primary QC\nPrimary QC: {:?}\nNext epoch QC: {:?}",
704 cert_pair.qc(),
705 cert_pair.next_epoch_qc()
706 )
707 );
708 }
709
710 let anchor_view = leaf.view_number();
711 (leaf, cert_pair, Some(anchor_view))
712 },
713 None => {
714 tracing::info!("no saved leaf, starting from genesis leaf");
715 let genesis_qc =
716 QuorumCertificate2::genesis(&genesis_validated_state, &state, upgrade).await;
717 (
718 hotshot_types::data::Leaf2::genesis(
719 &genesis_validated_state,
720 &state,
721 upgrade.base,
722 )
723 .await,
724 CertificatePair::new(genesis_qc, None),
725 None,
726 )
727 },
728 };
729
730 let mut high_qc = cert_pair.qc().clone();
731 let mut next_epoch_high_qc = cert_pair.next_epoch_qc().cloned();
732 if let Some((extended_high_qc, extended_next_qc)) = self.load_eqc().await
733 && extended_high_qc.view_number() > high_qc.view_number()
734 {
735 high_qc = extended_high_qc;
736 next_epoch_high_qc = Some(extended_next_qc);
737 }
738
739 let validated_state = if leaf.block_header().height() == 0 {
740 genesis_validated_state
742 } else {
743 ValidatedState::from_header(leaf.block_header())
746 };
747
748 let restart_view = max(restart_view, leaf.view_number());
753 let epoch = genesis_epoch_from_version(upgrade.base);
755
756 let epoch_start_block = state.epoch_start_block;
758
759 let saved_proposals = self
760 .load_quorum_proposals()
761 .await
762 .context("loading saved proposals")?;
763
764 let upgrade_certificate = self
765 .load_upgrade_certificate()
766 .await
767 .context("loading upgrade certificate")?;
768
769 let start_epoch_info = self
770 .load_start_epoch_info()
771 .await
772 .context("loading start epoch info")?;
773
774 let state_cert = self
775 .load_state_cert()
776 .await
777 .context("loading light client state update certificate")?;
778
779 tracing::warn!(
780 ?leaf,
781 ?restart_view,
782 ?epoch,
783 ?high_qc,
784 ?validated_state,
785 ?state_cert,
786 "loaded consensus state"
787 );
788
789 Ok((
790 HotShotInitializer {
791 instance_state: state,
792 epoch_height,
793 epoch_start_block,
794 anchor_leaf: leaf,
795 anchor_state: Arc::new(validated_state),
796 anchor_state_delta: None,
797 start_view: restart_view,
798 start_epoch: epoch,
799 last_actioned_view: highest_voted_view,
800 saved_proposals,
801 high_qc,
802 next_epoch_high_qc,
803 decided_upgrade_certificate: upgrade_certificate,
804 undecided_leaves: Default::default(),
805 undecided_state: Default::default(),
806 saved_vid_shares: Default::default(), start_epoch_info,
808 state_cert,
809 },
810 anchor_view,
811 ))
812 }
813
814 async fn persist_event(
822 &self,
823 event: &CoordinatorEvent<SeqTypes>,
824 consumer: &(impl EventConsumer + 'static),
825 ) -> Option<(ViewNumber, Option<Arc<CertificatePair<SeqTypes>>>)> {
826 match event {
827 CoordinatorEvent::LegacyEvent(hotshot_event) => {
828 let EventType::Decide {
829 leaf_chain,
830 committing_qc,
831 deciding_qc,
832 ..
833 } = &hotshot_event.event
834 else {
835 return None;
836 };
837 let LeafInfo { leaf, .. } = leaf_chain.first()?;
838 let decided_view = leaf.view_number();
839
840 let chain = leaf_chain.iter().zip(
841 std::iter::once((**committing_qc).clone()).chain(
842 leaf_chain
843 .iter()
844 .map(|leaf| CertificatePair::for_parent(&leaf.leaf)),
845 ),
846 );
847
848 if let Err(err) = self
849 .persist_decided_leaves(decided_view, chain, deciding_qc.clone(), consumer)
850 .await
851 {
852 tracing::error!(
853 "failed to save decided leaves, chain may not be up to date: {err:#}"
854 );
855 return None;
856 }
857 Some((decided_view, deciding_qc.clone()))
858 },
859 CoordinatorEvent::NewDecide {
860 leaf_infos, cert1, ..
861 } => {
862 let first = leaf_infos.first()?;
863 let decided_view = first.leaf.view_number();
864
865 let certifying_qcs = std::iter::once(cert1.clone())
868 .chain(leaf_infos.iter().map(|info| info.leaf.justify_qc()))
869 .take(leaf_infos.len())
870 .map(CertificatePair::non_epoch_change);
871
872 if let Err(err) = self
873 .persist_decided_leaves(
874 decided_view,
875 leaf_infos.iter().zip(certifying_qcs),
876 None,
877 consumer,
878 )
879 .await
880 {
881 tracing::error!(
882 "failed to save decided leaves from new protocol, chain may not be up to \
883 date: {err:#}"
884 );
885 return None;
886 }
887 Some((decided_view, None))
888 },
889 _ => None,
890 }
891 }
892
893 async fn append_decided_leaves(
923 &self,
924 decided_view: ViewNumber,
925 leaf_chain: impl IntoIterator<Item = (&LeafInfo<SeqTypes>, CertificatePair<SeqTypes>)> + Send,
926 deciding_qc: Option<Arc<CertificatePair<SeqTypes>>>,
927 consumer: &(impl EventConsumer + 'static),
928 ) -> anyhow::Result<()> {
929 self.persist_decided_leaves(decided_view, leaf_chain, deciding_qc.clone(), consumer)
930 .await?;
931 if let Err(err) = self
933 .process_decided_events(decided_view, deciding_qc, consumer)
934 .await
935 {
936 tracing::warn!(?decided_view, "decide event processing failed: {err:#}");
937 }
938 Ok(())
939 }
940
941 async fn persist_decided_leaves(
946 &self,
947 decided_view: ViewNumber,
948 leaf_chain: impl IntoIterator<Item = (&LeafInfo<SeqTypes>, CertificatePair<SeqTypes>)> + Send,
949 deciding_qc: Option<Arc<CertificatePair<SeqTypes>>>,
950 consumer: &(impl EventConsumer + 'static),
951 ) -> anyhow::Result<()>;
952
953 async fn process_decided_events(
964 &self,
965 decided_view: ViewNumber,
966 _deciding_qc: Option<Arc<CertificatePair<SeqTypes>>>,
967 _consumer: &(impl EventConsumer + 'static),
968 ) -> anyhow::Result<Option<ViewNumber>> {
969 Ok(Some(decided_view))
970 }
971
972 async fn load_anchor_leaf(&self) -> anyhow::Result<Option<(Leaf2, CertificatePair<SeqTypes>)>>;
973 async fn append_vid(
974 &self,
975 proposal: &Proposal<SeqTypes, VidDisperseShare<SeqTypes>>,
976 ) -> anyhow::Result<()>;
977 async fn append_da(
978 &self,
979 proposal: &Proposal<SeqTypes, DaProposal<SeqTypes>>,
980 vid_commit: VidCommitment,
981 ) -> anyhow::Result<()>;
982 async fn record_action(
983 &self,
984 view: ViewNumber,
985 epoch: Option<EpochNumber>,
986 action: HotShotAction,
987 ) -> anyhow::Result<()>;
988
989 async fn append_quorum_proposal2(
990 &self,
991 proposal: &Proposal<SeqTypes, QuorumProposalWrapper<SeqTypes>>,
992 ) -> anyhow::Result<()>;
993
994 async fn append_cert2(
996 &self,
997 _view: ViewNumber,
998 _cert2: Certificate2<SeqTypes>,
999 ) -> anyhow::Result<()> {
1000 Ok(())
1001 }
1002
1003 async fn load_cert2(
1005 &self,
1006 _view: ViewNumber,
1007 ) -> anyhow::Result<Option<Certificate2<SeqTypes>>> {
1008 Ok(None)
1009 }
1010
1011 async fn append_high_qc2(&self, _high_qc: QuorumCertificate2<SeqTypes>) -> anyhow::Result<()> {
1017 Ok(())
1018 }
1019
1020 async fn load_high_qc2(&self) -> anyhow::Result<Option<QuorumCertificate2<SeqTypes>>> {
1022 Ok(None)
1023 }
1024
1025 async fn store_eqc(
1027 &self,
1028 _high_qc: QuorumCertificate2<SeqTypes>,
1029 _next_epoch_high_qc: NextEpochQuorumCertificate2<SeqTypes>,
1030 ) -> anyhow::Result<()>;
1031
1032 async fn load_eqc(
1034 &self,
1035 ) -> Option<(
1036 QuorumCertificate2<SeqTypes>,
1037 NextEpochQuorumCertificate2<SeqTypes>,
1038 )>;
1039
1040 async fn store_upgrade_certificate(
1041 &self,
1042 decided_upgrade_certificate: Option<UpgradeCertificate<SeqTypes>>,
1043 ) -> anyhow::Result<()>;
1044
1045 async fn migrate_storage(&self) -> anyhow::Result<()> {
1046 tracing::warn!("migrating consensus data...");
1047
1048 self.migrate_anchor_leaf().await?;
1049 self.migrate_da_proposals().await?;
1050 self.migrate_vid_shares().await?;
1051 self.migrate_quorum_proposals().await?;
1052 self.migrate_quorum_certificates().await?;
1053 self.migrate_x25519_keys().await?;
1054 tracing::warn!("consensus storage has been migrated to new types");
1055
1056 Ok(())
1057 }
1058
1059 async fn migrate_x25519_keys(&self) -> anyhow::Result<()>;
1060
1061 async fn migrate_anchor_leaf(&self) -> anyhow::Result<()>;
1062 async fn migrate_da_proposals(&self) -> anyhow::Result<()>;
1063 async fn migrate_vid_shares(&self) -> anyhow::Result<()>;
1064 async fn migrate_quorum_proposals(&self) -> anyhow::Result<()>;
1065 async fn migrate_quorum_certificates(&self) -> anyhow::Result<()>;
1066
1067 async fn load_anchor_view(&self) -> anyhow::Result<ViewNumber> {
1068 match self.load_anchor_leaf().await? {
1069 Some((leaf, _)) => Ok(leaf.view_number()),
1070 None => Ok(ViewNumber::genesis()),
1071 }
1072 }
1073
1074 async fn store_next_epoch_quorum_certificate(
1075 &self,
1076 high_qc: NextEpochQuorumCertificate2<SeqTypes>,
1077 ) -> anyhow::Result<()>;
1078
1079 async fn load_next_epoch_quorum_certificate(
1080 &self,
1081 ) -> anyhow::Result<Option<NextEpochQuorumCertificate2<SeqTypes>>>;
1082
1083 async fn append_da2(
1084 &self,
1085 proposal: &Proposal<SeqTypes, DaProposal2<SeqTypes>>,
1086 vid_commit: VidCommitment,
1087 ) -> anyhow::Result<()>;
1088
1089 async fn append_proposal2(
1090 &self,
1091 proposal: &Proposal<SeqTypes, QuorumProposalWrapper<SeqTypes>>,
1092 ) -> anyhow::Result<()> {
1093 self.append_quorum_proposal2(proposal).await
1094 }
1095
1096 async fn store_drb_result(
1097 &self,
1098 epoch: EpochNumber,
1099 drb_result: DrbResult,
1100 ) -> anyhow::Result<()>;
1101 async fn store_drb_input(&self, drb_input: DrbInput) -> anyhow::Result<()>;
1102 async fn load_drb_input(&self, epoch: u64) -> anyhow::Result<DrbInput>;
1103 async fn store_epoch_root(
1104 &self,
1105 epoch: EpochNumber,
1106 block_header: <SeqTypes as NodeType>::BlockHeader,
1107 ) -> anyhow::Result<()>;
1108 async fn add_state_cert(
1109 &self,
1110 state_cert: LightClientStateUpdateCertificateV2<SeqTypes>,
1111 ) -> anyhow::Result<()>;
1112
1113 fn enable_metrics(&mut self, metrics: &dyn Metrics);
1114}
1115
1116#[async_trait]
1117pub trait EventConsumer: Debug + Send + Sync {
1118 async fn handle_event(&self, event: &CoordinatorEvent<SeqTypes>) -> anyhow::Result<()>;
1119}
1120
1121#[async_trait]
1122impl<T> EventConsumer for Box<T>
1123where
1124 T: EventConsumer + ?Sized,
1125{
1126 async fn handle_event(&self, event: &CoordinatorEvent<SeqTypes>) -> anyhow::Result<()> {
1127 (**self).handle_event(event).await
1128 }
1129}
1130
1131#[derive(Clone, Copy, Debug)]
1132pub struct NullEventConsumer;
1133
1134#[async_trait]
1135impl EventConsumer for NullEventConsumer {
1136 async fn handle_event(&self, _event: &CoordinatorEvent<SeqTypes>) -> anyhow::Result<()> {
1137 Ok(())
1138 }
1139}
1140
1141#[cfg(feature = "node")]
1142#[async_trait]
1143impl<P: SequencerPersistence> Storage<SeqTypes> for Arc<P> {
1144 async fn append_vid(
1145 &self,
1146 proposal: &Proposal<SeqTypes, VidDisperseShare<SeqTypes>>,
1147 ) -> anyhow::Result<()> {
1148 (**self).append_vid(proposal).await
1149 }
1150
1151 async fn append_da(
1152 &self,
1153 proposal: &Proposal<SeqTypes, DaProposal<SeqTypes>>,
1154 vid_commit: VidCommitment,
1155 ) -> anyhow::Result<()> {
1156 (**self).append_da(proposal, vid_commit).await
1157 }
1158
1159 async fn append_da2(
1160 &self,
1161 proposal: &Proposal<SeqTypes, DaProposal2<SeqTypes>>,
1162 vid_commit: VidCommitment,
1163 ) -> anyhow::Result<()> {
1164 (**self).append_da2(proposal, vid_commit).await
1165 }
1166
1167 async fn record_action(
1168 &self,
1169 view: ViewNumber,
1170 epoch: Option<EpochNumber>,
1171 action: HotShotAction,
1172 ) -> anyhow::Result<()> {
1173 (**self).record_action(view, epoch, action).await
1174 }
1175
1176 async fn update_high_qc(&self, _high_qc: QuorumCertificate<SeqTypes>) -> anyhow::Result<()> {
1177 Ok(())
1178 }
1179
1180 async fn append_proposal(
1181 &self,
1182 proposal: &Proposal<SeqTypes, QuorumProposal<SeqTypes>>,
1183 ) -> anyhow::Result<()> {
1184 (**self)
1185 .append_quorum_proposal2(&convert_proposal(proposal.clone()))
1186 .await
1187 }
1188
1189 async fn append_proposal2(
1190 &self,
1191 proposal: &Proposal<SeqTypes, QuorumProposal2<SeqTypes>>,
1192 ) -> anyhow::Result<()> {
1193 let proposal_qp_wrapper: Proposal<SeqTypes, QuorumProposalWrapper<SeqTypes>> =
1194 convert_proposal(proposal.clone());
1195 (**self).append_quorum_proposal2(&proposal_qp_wrapper).await
1196 }
1197
1198 async fn update_high_qc2(&self, _high_qc: QuorumCertificate2<SeqTypes>) -> anyhow::Result<()> {
1199 Ok(())
1200 }
1201
1202 async fn update_eqc(
1204 &self,
1205 high_qc: QuorumCertificate2<SeqTypes>,
1206 next_epoch_high_qc: NextEpochQuorumCertificate2<SeqTypes>,
1207 ) -> anyhow::Result<()> {
1208 if let Some((existing_high_qc, _)) = (**self).load_eqc().await
1209 && high_qc.view_number() < existing_high_qc.view_number()
1210 {
1211 return Ok(());
1212 }
1213
1214 (**self).store_eqc(high_qc, next_epoch_high_qc).await
1215 }
1216
1217 async fn update_next_epoch_high_qc2(
1218 &self,
1219 _next_epoch_high_qc: NextEpochQuorumCertificate2<SeqTypes>,
1220 ) -> anyhow::Result<()> {
1221 Ok(())
1222 }
1223
1224 async fn update_decided_upgrade_certificate(
1225 &self,
1226 decided_upgrade_certificate: Option<UpgradeCertificate<SeqTypes>>,
1227 ) -> anyhow::Result<()> {
1228 (**self)
1229 .store_upgrade_certificate(decided_upgrade_certificate)
1230 .await
1231 }
1232
1233 async fn store_drb_result(
1234 &self,
1235 epoch: EpochNumber,
1236 drb_result: DrbResult,
1237 ) -> anyhow::Result<()> {
1238 (**self).store_drb_result(epoch, drb_result).await
1239 }
1240
1241 async fn store_epoch_root(
1242 &self,
1243 epoch: EpochNumber,
1244 block_header: <SeqTypes as NodeType>::BlockHeader,
1245 ) -> anyhow::Result<()> {
1246 (**self).store_epoch_root(epoch, block_header).await
1247 }
1248
1249 async fn store_drb_input(&self, drb_input: DrbInput) -> anyhow::Result<()> {
1250 (**self).store_drb_input(drb_input).await
1251 }
1252
1253 async fn load_drb_input(&self, epoch: u64) -> anyhow::Result<DrbInput> {
1254 (**self).load_drb_input(epoch).await
1255 }
1256
1257 async fn update_state_cert(
1258 &self,
1259 state_cert: LightClientStateUpdateCertificateV2<SeqTypes>,
1260 ) -> anyhow::Result<()> {
1261 (**self).add_state_cert(state_cert).await
1262 }
1263}
1264
1265#[cfg(feature = "node")]
1266#[async_trait]
1267impl<P: SequencerPersistence> NewProtocolStorage<SeqTypes> for Arc<P> {
1268 async fn append_cert2(
1269 &self,
1270 view: ViewNumber,
1271 cert: Certificate2<SeqTypes>,
1272 ) -> anyhow::Result<()> {
1273 (**self).append_cert2(view, cert).await
1274 }
1275
1276 async fn append_high_qc2(&self, high_qc: Certificate1<SeqTypes>) -> anyhow::Result<()> {
1277 (**self).append_high_qc2(high_qc).await
1282 }
1283
1284 async fn load_high_qc2(&self) -> anyhow::Result<Option<Certificate1<SeqTypes>>> {
1285 (**self).load_high_qc2().await
1286 }
1287}
1288
1289pub trait FromNsPayloadBytes<'a> {
1294 fn from_payload_bytes(bytes: &'a [u8]) -> Self;
1296}
1297
1298pub trait NsPayloadBytesRange<'a> {
1303 type Output: FromNsPayloadBytes<'a>;
1304
1305 fn ns_payload_range(&self) -> Range<usize>;
1307}
1308
1309pub trait FromStringOrInteger: Sized {
1320 type Binary: Serialize + DeserializeOwned;
1321 type Integer: Serialize + DeserializeOwned;
1322
1323 fn from_binary(b: Self::Binary) -> anyhow::Result<Self>;
1324 fn from_string(s: String) -> anyhow::Result<Self>;
1325 fn from_integer(i: Self::Integer) -> anyhow::Result<Self>;
1326
1327 fn to_binary(&self) -> anyhow::Result<Self::Binary>;
1328 fn to_string(&self) -> anyhow::Result<String>;
1329}