Skip to main content

espresso_types/v0/
traits.rs

1//! This module contains all the traits used for building the sequencer types.
2//! It also includes some trait implementations that cannot be implemented in an external crate.
3#[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    /// Fetch the leaf at the given height without retrying on transient errors.
66    ///
67    /// `coordinator` resolves the stake tables used to verify the fetched leaf
68    /// chain, triggering catchup for epochs whose stake table is not yet
69    /// available.
70    async fn try_fetch_leaf(
71        &self,
72        retry: usize,
73        coordinator: EpochMembershipCoordinator<SeqTypes>,
74        height: u64,
75    ) -> anyhow::Result<Leaf2>;
76
77    /// Fetch the leaf at the given height, retrying on transient errors.
78    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    /// Fetch the given list of accounts without retrying on transient errors.
92    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    /// Fetch the given list of accounts, retrying on transient errors.
103    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    /// Fetch and remember the blocks frontier without retrying on transient errors.
137    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    /// Fetch and remember the blocks frontier, retrying on transient errors.
147    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    /// Fetch the chain config without retrying on transient errors.
164    async fn try_fetch_chain_config(
165        &self,
166        retry: usize,
167        commitment: Commitment<ChainConfig>,
168    ) -> anyhow::Result<ChainConfig>;
169
170    /// Fetch the chain config, retrying on transient errors.
171    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    /// Fetch the given reward merkle tree without retrying on transient errors.
186    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    /// Fetch the given list of reward accounts without retrying on transient errors.
225    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    /// Fetch the given list of reward accounts, retrying on transient errors.
236    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    /// Fetch the state certificate for a given epoch without retrying on transient errors.
271    async fn try_fetch_state_cert(
272        &self,
273        retry: usize,
274        epoch: u64,
275    ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>>;
276
277    /// Fetch the state certificate for a given epoch, retrying on transient errors.
278    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    /// Returns true if the catchup provider is local (e.g. does not make calls to remote resources).
293    fn is_local(&self) -> bool;
294
295    /// Returns the backoff parameters for the catchup provider.
296    fn backoff(&self) -> &BackoffParams;
297
298    /// Returns the name of the catchup provider.
299    fn name(&self) -> String;
300
301    /// A copy of this provider that waits up to `local_timeout` on its local providers, or `None`
302    /// if it has no such timeout.
303    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/// Determine the read state based on the queried block range.
504// - If the persistence returned events up to the requested block, the read is complete.
505/// - Otherwise, indicate that the read is up to the last processed block.
506#[derive(Clone, Copy, Debug, PartialEq, Eq)]
507pub enum EventsPersistenceRead {
508    Complete,
509    UntilL1Block(u64),
510}
511
512/// Tuple type for stake table data: (validators, block_reward, stake_table_hash)
513pub type StakeTuple = (
514    AuthenticatedValidatorMap,
515    Option<RewardAmount>,
516    Option<StakeTableHash>,
517);
518
519#[async_trait]
520/// Trait used by `Memberships` implementations to interact with persistence layer.
521pub trait MembershipPersistence: Send + Sync + 'static {
522    /// Load stake table for epoch from storage
523    async fn load_stake(&self, epoch: EpochNumber) -> anyhow::Result<Option<StakeTuple>>;
524
525    /// Load stake tables for storage for latest `n` known epochs
526    async fn load_latest_stake(&self, limit: u64) -> anyhow::Result<Option<Vec<IndexedStake>>>;
527
528    /// Load the DRB result for `epoch`.
529    async fn load_drb_result(&self, epoch: EpochNumber) -> anyhow::Result<Option<DrbResult>>;
530
531    /// Load the epoch root block header for `epoch`.
532    async fn load_epoch_root(&self, epoch: EpochNumber) -> anyhow::Result<Option<Header>>;
533
534    /// Store the epoch root block header for `epoch`.
535    async fn store_epoch_root(
536        &self,
537        epoch: EpochNumber,
538        block_header: Header,
539    ) -> anyhow::Result<()>;
540
541    /// Store stake table at `epoch` in the persistence layer
542    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    /// Delete all stake table events, the L1 block tracker, and the epoch DRB and root data.
565    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/// State certificates by epoch, apart from [`SequencerPersistence`] so that the API can hold them as
582/// a trait object.
583#[cfg(feature = "node")]
584#[async_trait]
585pub trait StateCertPersistence: Send + Sync + 'static {
586    /// Get a state certificate for an epoch.
587    async fn get_state_cert_by_epoch(
588        &self,
589        epoch: u64,
590    ) -> anyhow::Result<Option<LightClientStateUpdateCertificateV2<SeqTypes>>>;
591
592    /// Insert a state certificate for a given epoch.
593    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    /// Use this storage as a state catchup backend, if supported.
606    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    /// Load the orchestrator config from storage.
614    ///
615    /// Returns `None` if no config exists (we are joining a network for the first time). Fails with
616    /// `Err` if it could not be determined whether a config exists or not.
617    async fn load_config(&self) -> anyhow::Result<Option<NetworkConfig>>;
618
619    /// Save the orchestrator config to storage.
620    async fn save_config(&self, cfg: &NetworkConfig) -> anyhow::Result<()>;
621
622    /// Load the highest view saved with [`save_voted_view`](Self::save_voted_view).
623    async fn load_latest_acted_view(&self) -> anyhow::Result<Option<ViewNumber>>;
624
625    /// Load the view to restart from.
626    async fn load_restart_view(&self) -> anyhow::Result<Option<ViewNumber>>;
627
628    /// Load the proposals saved by consensus
629    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    /// Load the latest known consensus state.
655    ///
656    /// Returns an initializer to resume HotShot from the latest saved state (or start from genesis,
657    /// if there is no saved state). Also returns the anchor view number, which can be used as a
658    /// reference point to process any events which were not processed before a previous shutdown,
659    /// if applicable,.
660    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        // Use epoch height from node state. Node state gets the epoch height from the genesis file.
697        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            // If we are starting from genesis, we can provide the full state.
771            genesis_validated_state
772        } else {
773            // Otherwise, we will have to construct a sparse state and fetch missing data during
774            // catchup.
775            ValidatedState::from_header(leaf.block_header())
776        };
777
778        // If we are not starting from genesis, we start from the view following the maximum view
779        // between `highest_voted_view` and `leaf.view_number`. This prevents double votes from
780        // starting in a view in which we had already voted before the restart, and prevents
781        // unnecessary catchup from starting in a view earlier than the anchor leaf.
782        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    /// Decode a consensus decide event and persist its leaves, for the consensus event loop.
831    /// Returns the decided view on a decide so the caller can wake a background task to run
832    /// [`process_decided_events`](Self::process_decided_events); `None` otherwise.
833    ///
834    /// This is the persist-only half of a decide: query-service ingestion and GC are deferred to
835    /// [`process_decided_events`](Self::process_decided_events). Tests that want the synchronous
836    /// persist-then-process behavior use [`append_decided_leaves`](Self::append_decided_leaves).
837    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                // `cert1` certifies the newest leaf; each newer leaf's justify_qc certifies the
852                // next older leaf.
853                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    /// Append decided leaves to persistent storage and emit a corresponding event.
879    ///
880    /// `consumer` will be sent a `Decide` event containing all decided leaves in persistent storage
881    /// up to and including `view`. If available in persistent storage, full block payloads and VID
882    /// info will also be included for each leaf.
883    ///
884    /// Once the new decided leaves have been processed, old data up to `view` will be garbage
885    /// collected The consumer's handling of this event is a prerequisite for the completion of
886    /// garbage collection: if the consumer fails to process the event, no data is deleted. This
887    /// ensures that, if called repeatedly, all decided leaves ever recorded in consensus storage
888    /// will eventually be passed to the consumer.
889    ///
890    /// Note that the converse is not true: if garbage collection fails, it is not guaranteed that
891    /// the consumer hasn't processed the decide event. Thus, in rare cases, some events may be
892    /// processed twice, or the consumer may get two events which share a subset of their data.
893    /// Thus, it is the consumer's responsibility to make sure its handling of each leaf is
894    /// idempotent.
895    ///
896    /// If the consumer fails to handle the new decide event, it may be retried, or simply postponed
897    /// until the next decide, at which point all persisted leaves from the failed GC run will be
898    /// included in the event along with subsequently decided leaves.
899    ///
900    /// This functionality is useful for keeping a separate view of the blockchain in sync with the
901    /// consensus storage. For example, the `consumer` could be used for moving data from consensus
902    /// storage to long-term archival storage.
903    ///
904    /// Convenience combinator: [`persist_decided_leaves`](Self::persist_decided_leaves) then
905    /// [`process_decided_events`](Self::process_decided_events). Production drives the two halves on
906    /// separate tasks; tests and back-compat callers use this synchronous form.
907    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        // Leaves are persisted; processing failures are non-fatal here and retried in production.
916        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    /// Persist decided leaves only (the critical, must-not-lag half of a decide; also the
923    /// anchor for restart recovery). Query-service ingestion and GC are deferred to
924    /// [`process_decided_events`](Self::process_decided_events). Backends with no replayable storage
925    /// (e.g. `NoStorage`) may instead forward decide events to `consumer` here.
926    ///
927    /// `cert2` finalizes the newest leaf of `leaf_chain`. It is persisted atomically with the
928    /// leaves, so [`process_decided_events`](Self::process_decided_events) never sees that leaf
929    /// without it.
930    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    /// Generate decide events for `consumer` from persisted leaves, then GC processed data.
938    /// Cursor-driven (e.g. `last_processed_view`): advances only on success, so it may lag
939    /// consensus without losing data.
940    ///
941    /// Returns the highest view confirmed processed (the cursor), or `None` if nothing was
942    /// processed, so the caller can track real progress. Errors are propagated; the failed range
943    /// is retried on the next call.
944    ///
945    /// Default returns `Some(decided_view)`: backends with no replayable storage (e.g. `NoStorage`)
946    /// forward events synchronously in `persist_decided_leaves` and are always caught up here.
947    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    /// Persist cert2 for the given view.
973    async fn append_cert2(
974        &self,
975        _view: ViewNumber,
976        _cert2: Certificate2<SeqTypes>,
977    ) -> anyhow::Result<()> {
978        Ok(())
979    }
980
981    /// Load a persisted cert2 by view, if any.
982    async fn load_cert2(
983        &self,
984        _view: ViewNumber,
985    ) -> anyhow::Result<Option<Certificate2<SeqTypes>>> {
986        Ok(None)
987    }
988
989    /// Persist the new protocol's locked QC, written before each phase-2 vote.
990    ///
991    /// Implementations must apply this as an atomic monotonic compare-and-set: a
992    /// write whose view is not newer than the stored one is a no-op. This lets
993    /// concurrent, retried writes race without ever regressing the persisted lock.
994    async fn append_high_qc2(&self, _high_qc: QuorumCertificate2<SeqTypes>) -> anyhow::Result<()> {
995        Ok(())
996    }
997
998    /// Load the persisted locked QC, if any.
999    async fn load_high_qc2(&self) -> anyhow::Result<Option<QuorumCertificate2<SeqTypes>>> {
1000        Ok(None)
1001    }
1002
1003    /// Persist the new protocol's latest epoch boundary certificate: the own
1004    /// `Certificate1` of the last block of an epoch, at the block's view.
1005    ///
1006    /// Implementations must apply this as an atomic monotonic compare-and-set,
1007    /// ordered by epoch, then view, like [`Self::append_high_qc2`].
1008    async fn append_boundary_qc2(&self, _qc: QuorumCertificate2<SeqTypes>) -> anyhow::Result<()> {
1009        Ok(())
1010    }
1011
1012    /// Load the persisted boundary certificate, if any.
1013    async fn load_boundary_qc2(&self) -> anyhow::Result<Option<QuorumCertificate2<SeqTypes>>> {
1014        Ok(None)
1015    }
1016
1017    /// Update the current eQC in storage.
1018    async fn store_eqc(
1019        &self,
1020        _high_qc: QuorumCertificate2<SeqTypes>,
1021        _next_epoch_high_qc: NextEpochQuorumCertificate2<SeqTypes>,
1022    ) -> anyhow::Result<()>;
1023
1024    /// Load the current eQC from storage.
1025    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        // Writes are spawned concurrently and retried, so a stale write can land
1180        // after a newer one. The backend applies a monotonic compare-and-set
1181        // atomically, so such a stale write is a no-op and never regresses the
1182        // persisted view.
1183        (**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
1199/// Data that can be deserialized from a subslice of namespace payload bytes.
1200///
1201/// Companion trait for [`NsPayloadBytesRange`], which specifies the subslice of
1202/// namespace payload bytes to read.
1203pub trait FromNsPayloadBytes<'a> {
1204    /// Deserialize `Self` from namespace payload bytes.
1205    fn from_payload_bytes(bytes: &'a [u8]) -> Self;
1206}
1207
1208/// Specifies a subslice of namespace payload bytes to read.
1209///
1210/// Companion trait for [`FromNsPayloadBytes`], which holds data that can be
1211/// deserialized from that subslice of bytes.
1212pub trait NsPayloadBytesRange<'a> {
1213    type Output: FromNsPayloadBytes<'a>;
1214
1215    /// Range relative to this ns payload
1216    fn ns_payload_range(&self) -> Range<usize>;
1217}
1218
1219/// Types which can be deserialized from either integers or strings.
1220///
1221/// Some types can be represented as an integer or a string in human-readable formats like JSON or
1222/// TOML. For example, 1 GWEI might be represented by the integer `1000000000` or the string `"1
1223/// gwei"`. Such types can implement `FromStringOrInteger` and then use [`impl_string_or_integer`]
1224/// to derive this user-friendly serialization.
1225///
1226/// These types are assumed to have an efficient representation as an integral type in Rust --
1227/// [`Self::Binary`] -- and will be serialized to and from this type when using a non-human-readable
1228/// encoding. With human readable encodings, serialization is always to a string.
1229pub 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}