1use core::fmt::Debug;
2use std::{ops::Range, sync::Arc, time::Duration};
3
4use anyhow::{Context, bail, ensure};
5use async_lock::Mutex;
6use committable::Committable;
7use either::Either;
8use espresso_types::{
9 BlockMerkleTree, EpochRewardsCalculator, FeeAccount, FeeMerkleTree, Leaf2, ValidatedState,
10 traits::StateCatchup,
11 v0_3::{ChainConfig, RewardMerkleTreeV1},
12 v0_4::{Delta, PermittedRewardMerkleTreeV2},
13};
14use futures::{StreamExt, future::Future};
15use hotshot_query_service::{
16 availability::{self, AvailabilityDataSource},
17 data_source::{Transaction, VersionedDataSource, storage::pruning::PrunedHeightDataSource},
18 merklized_state::{MerklizedStateHeightPersistence, UpdateStateData},
19 status::StatusDataSource,
20 types::HeightIndexed,
21};
22use hotshot_types::{traits::ValidatedState as HotShotState, utils::is_last_block};
23use jf_merkle_tree_compat::{
24 LookupResult, MerkleTreeScheme, ToTraversalPath, UniversalMerkleTreeScheme,
25};
26use tokio::time::{sleep, timeout};
27use vbs::version::Version;
28use versions::{DRB_AND_HEADER_UPGRADE_VERSION, EPOCH_REWARD_VERSION, EPOCH_VERSION};
29
30use crate::{
31 NodeState, SeqTypes,
32 api::{LightClientProvider, RewardMerkleTreeDataSource},
33 catchup::{CatchupStorage, SingleAttemptCatchup},
34 persistence::ChainConfigPersistence,
35};
36
37pub(crate) async fn compute_state_update(
38 parent_state: &ValidatedState,
39 instance: &NodeState,
40 peers: &impl StateCatchup,
41 parent_leaf: &Leaf2,
42 proposed_leaf: &Leaf2,
43) -> anyhow::Result<(ValidatedState, Delta)> {
44 let header = proposed_leaf.block_header();
45
46 let mut parent_state = parent_state.clone();
47
48 if proposed_leaf.block_header().version() > parent_leaf.block_header().version() {
56 parent_state.chain_config = proposed_leaf.block_header().chain_config()
57 }
58
59 let (state, delta, total_rewards_distributed) = parent_state
60 .apply_header(
61 instance,
62 peers,
63 parent_leaf,
64 header,
65 header.version(),
66 proposed_leaf.view_number(),
67 )
68 .await?;
69
70 ensure!(
72 state.chain_config.commit() == header.chain_config().commit(),
73 "internal error! in-memory chain config {:?} does not match header {:?}",
74 state.chain_config,
75 header.chain_config(),
76 );
77 ensure!(
78 state.block_merkle_tree.commitment() == header.block_merkle_tree_root(),
79 "internal error! in-memory block tree {} does not match header {}",
80 state.block_merkle_tree.commitment(),
81 header.block_merkle_tree_root()
82 );
83 ensure!(
84 state.fee_merkle_tree.commitment() == header.fee_merkle_tree_root(),
85 "internal error! in-memory fee tree {} does not match header {}",
86 state.fee_merkle_tree.commitment(),
87 header.fee_merkle_tree_root()
88 );
89
90 match header.reward_merkle_tree_root() {
91 Either::Left(v1_root) => {
92 ensure!(
93 state.reward_merkle_tree_v1.commitment() == v1_root,
94 "internal error! in-memory v1 reward tree {} does not match header {}",
95 state.reward_merkle_tree_v1.commitment(),
96 v1_root
97 )
98 },
99 Either::Right(v2_root) => {
100 ensure!(
101 state.reward_merkle_tree_v2.commitment() == v2_root,
102 "internal error! in-memory v2 reward tree {} does not match header {}",
103 state.reward_merkle_tree_v2.commitment(),
104 v2_root
105 )
106 },
107 }
108
109 if header.version() >= DRB_AND_HEADER_UPGRADE_VERSION {
110 let Some(actual_total) = total_rewards_distributed else {
111 bail!(
112 "internal error! total_rewards_distributed is None for version {:?}",
113 header.version()
114 );
115 };
116
117 let Some(proposed_total) = header.total_reward_distributed() else {
118 bail!(
119 "internal error! proposed header.total_reward_distributed() is None for version \
120 {:?}",
121 header.version()
122 );
123 };
124
125 ensure!(
126 proposed_total == actual_total,
127 "Total rewards mismatch: proposed header has {proposed_total} but actual total is \
128 {actual_total}",
129 );
130 }
131
132 Ok((state, delta))
133}
134
135async fn store_state_update(
136 tx: &mut impl SequencerStateUpdate,
137 block_number: u64,
138 _version: Version,
139 state: &ValidatedState,
140 delta: &Delta,
141) -> anyhow::Result<()> {
142 let ValidatedState {
143 fee_merkle_tree,
144 block_merkle_tree,
145 ..
146 } = state;
147 let Delta { fees_delta, .. } = delta;
148
149 let fee_proofs: Vec<_> = fees_delta
151 .iter()
152 .map(|delta| {
153 let proof = match fee_merkle_tree.universal_lookup(*delta) {
154 LookupResult::Ok(_, proof) => proof,
155 LookupResult::NotFound(proof) => proof,
156 LookupResult::NotInMemory => bail!("missing merkle path for fee account {delta}"),
157 };
158 let path = FeeAccount::to_traversal_path(delta, fee_merkle_tree.height());
159 Ok((proof, path))
160 })
161 .collect::<anyhow::Result<Vec<_>>>()?;
162
163 tracing::debug!(count = fee_proofs.len(), "inserting fee accounts in batch");
164 UpdateStateData::<SeqTypes, FeeMerkleTree, { FeeMerkleTree::ARITY }>::insert_merkle_nodes_batch(
165 tx,
166 fee_proofs,
167 block_number,
168 )
169 .await
170 .context("failed to store fee merkle nodes")?;
171
172 store_block_frontier(tx, block_number, block_merkle_tree).await
173}
174
175async fn store_block_frontier(
178 tx: &mut impl SequencerStateUpdate,
179 block_number: u64,
180 block_merkle_tree: &BlockMerkleTree,
181) -> anyhow::Result<()> {
182 let (_, proof) = block_merkle_tree
183 .lookup(block_number - 1)
184 .expect_ok()
185 .context("getting blocks frontier")?;
186 let path = <u64 as ToTraversalPath<{ BlockMerkleTree::ARITY }>>::to_traversal_path(
187 &(block_number - 1),
188 block_merkle_tree.height(),
189 );
190
191 tracing::debug!("inserting blocks frontier");
192 UpdateStateData::<SeqTypes, BlockMerkleTree, { BlockMerkleTree::ARITY }>::insert_merkle_nodes(
193 tx,
194 proof,
195 path,
196 block_number,
197 )
198 .await
199 .context("failed to store block merkle nodes")
200}
201
202#[tracing::instrument(
203 skip_all,
204 fields(
205 node_id = instance.node_id,
206 view = ?parent_leaf.view_number(),
207 height = parent_leaf.height(),
208 ),
209)]
210async fn update_state_storage<T>(
211 parent_state: &ValidatedState,
212 storage: &Arc<T>,
213 instance: &NodeState,
214 peers: &impl StateCatchup,
215 parent_leaf: &Leaf2,
216 proposed_leaf: &Leaf2,
217) -> anyhow::Result<ValidatedState>
218where
219 T: SequencerStateDataSource,
220 for<'a> T::Transaction<'a>: SequencerStateUpdate,
221{
222 let parent_chain_config = parent_state.chain_config;
223 let block_number = proposed_leaf.height();
224 let version = proposed_leaf.block_header().version();
225
226 let (state, delta) =
227 compute_state_update(parent_state, instance, peers, parent_leaf, proposed_leaf)
228 .await
229 .context("computing state update")?;
230
231 let has_changed_accounts = version > EPOCH_VERSION && !delta.rewards_delta.is_empty();
232 let is_epoch_boundary = version >= EPOCH_REWARD_VERSION
247 && is_last_block(
248 block_number,
249 instance
250 .epoch_height
251 .expect("epoch_height should be set for version > V3"),
252 );
253
254 if has_changed_accounts || is_epoch_boundary {
255 storage
256 .save_and_gc_reward_tree_v2(
257 instance,
258 block_number,
259 version,
260 &state.reward_merkle_tree_v2,
261 )
262 .await
263 .context("failed to save and gc reward merkle tree v2")?;
264 }
265
266 storage
267 .persist_reward_proofs(instance, block_number, version)
268 .await
269 .context("failed to persist reward proofs")?;
270
271 tracing::debug!("storing state update");
275 let mut tx = storage
276 .write()
277 .await
278 .context("opening transaction for state update")?;
279
280 store_state_update(&mut tx, block_number, version, &state, &delta).await?;
281
282 if parent_chain_config != state.chain_config {
283 let cf = state
284 .chain_config
285 .resolve()
286 .context("failed to resolve to chain config")?;
287
288 tx.insert_chain_config(cf).await?;
289 }
290
291 tracing::debug!(block_number, "updating state height");
292 UpdateStateData::<SeqTypes, _, { BlockMerkleTree::ARITY }>::set_last_state_height(
293 &mut tx,
294 block_number as usize,
295 )
296 .await
297 .context("setting state height")?;
298
299 tx.commit().await?;
300
301 Ok(state)
302}
303
304async fn store_genesis_state<S>(
305 storage: &S,
306 chain_config: ChainConfig,
307 state: &ValidatedState,
308) -> anyhow::Result<()>
309where
310 S: SequencerStateDataSource,
311 for<'a> S::Transaction<'a>: SequencerStateUpdate,
312{
313 ensure!(
314 state.block_merkle_tree.num_leaves() == 0,
315 "genesis state with non-empty block tree is unsupported"
316 );
317
318 let mut tx = storage
319 .write()
320 .await
321 .context("starting transaction for genesis state")?;
322
323 for (account, _) in state.fee_merkle_tree.iter() {
325 let proof = match state.fee_merkle_tree.universal_lookup(account) {
326 LookupResult::Ok(_, proof) => proof,
327 LookupResult::NotFound(proof) => proof,
328 LookupResult::NotInMemory => bail!("missing merkle path for fee account {account}"),
329 };
330 let path: Vec<usize> =
331 <FeeAccount as ToTraversalPath<{ FeeMerkleTree::ARITY }>>::to_traversal_path(
332 account,
333 state.fee_merkle_tree.height(),
334 );
335
336 UpdateStateData::<SeqTypes, FeeMerkleTree, { FeeMerkleTree::ARITY }>::insert_merkle_nodes(
337 &mut tx, proof, path, 0,
338 )
339 .await
340 .context("failed to store fee merkle nodes")?;
341 }
342
343 tx.insert_chain_config(chain_config).await?;
344
345 tx.commit().await?;
346
347 storage
349 .save_reward_tree_v2(0, &state.reward_merkle_tree_v2)
350 .await
351 .context("storing genesis reward merkle tree")?;
352
353 Ok(())
354}
355
356const PRUNED_LEAF_CHECK_INTERVAL: Duration = Duration::from_secs(10);
359
360const BOUNDARY_RETRY_INTERVAL: Duration = Duration::from_secs(10);
363
364const LOCAL_CATCHUP_TIMEOUT: Duration = Duration::from_secs(60);
368
369#[derive(Clone, Copy, Debug, PartialEq, Eq)]
371pub(crate) enum EmptyStateStart {
372 RecentBoundary,
376 Genesis,
379}
380
381#[tracing::instrument(skip_all)]
382pub(crate) async fn update_state_storage_loop<T>(
383 storage: Arc<T>,
384 instance: impl Future<Output = NodeState>,
385 provider: Arc<LightClientProvider>,
386 empty_state_start: EmptyStateStart,
387) -> anyhow::Result<()>
388where
389 T: SequencerStateDataSource,
390 for<'a> T::Transaction<'a>: SequencerStateUpdate,
391{
392 let mut instance = instance.await;
393 instance.epoch_rewards_calculator = Arc::new(Mutex::new(EpochRewardsCalculator::new()));
396 if let Some(catchup) = instance
398 .state_catchup
399 .with_local_timeout(LOCAL_CATCHUP_TIMEOUT)
400 {
401 instance.state_catchup = catchup;
402 }
403
404 let mut last_height = retry_until_ok("loading the state head", || async {
410 Ok(storage.get_last_state_height().await? as u64)
411 })
412 .await;
413 if last_height == 0
417 && empty_state_start == EmptyStateStart::RecentBoundary
418 && let Some(boundary) = seed_boundary_snapshot(&storage, &instance, &provider).await
419 {
420 last_height = boundary;
421 }
422 if last_height > 0 && empty_state_start == EmptyStateStart::Genesis {
423 report_incomplete_trees(&*storage).await;
424 }
425 let current_height = storage.block_height().await.ok();
426 tracing::info!(
427 node_id = instance.node_id,
428 last_height,
429 ?current_height,
430 "updating state storage"
431 );
432
433 let parent_leaf = leaf_at(&storage, &provider, last_height).await;
434 let mut parent_state = ValidatedState::from_header(parent_leaf.block_header());
435
436 if parent_leaf.block_header().version() > EPOCH_VERSION && parent_leaf.height() > 0 {
450 parent_state.reward_merkle_tree_v2 =
451 retry_until_ok("loading the parent's reward tree", || {
452 load_parent_reward_tree(&*storage, &parent_leaf)
453 })
454 .await
455 .tree;
456 }
457
458 if last_height == 0 {
459 tracing::info!("storing genesis merklized state");
462 retry_until_ok("storing the genesis state", || {
464 store_genesis_state(&*storage, instance.chain_config, &instance.genesis_state)
465 })
466 .await;
467 }
468
469 follow_leaves(&storage, &instance, &provider, parent_leaf, parent_state).await;
470 Ok(())
471}
472
473async fn report_incomplete_trees<T>(storage: &T)
477where
478 T: SequencerStateDataSource,
479{
480 match storage.load_state_start_height().await {
481 Ok(Some(start)) => tracing::error!(
482 start,
483 "merklized state started from a boundary snapshot, not genesis, so its trees are \
484 incomplete"
485 ),
486 Ok(None) => {},
487 Err(err) => tracing::warn!(
488 err = %format_args!("{err:#}"),
489 "failed to load the state start height"
490 ),
491 }
492}
493
494async fn retry_until_ok<R, Fut>(step: &str, mut f: impl FnMut() -> Fut) -> R
497where
498 Fut: Future<Output = anyhow::Result<R>>,
499{
500 loop {
501 match f().await {
502 Ok(res) => return res,
503 Err(err) => tracing::error!(
504 step,
505 err = %format_args!("{err:#}"),
506 "state loop start step failed, retrying"
507 ),
508 }
509 sleep(BOUNDARY_RETRY_INTERVAL).await;
510 }
511}
512
513async fn load_parent_reward_tree<T>(
515 storage: &T,
516 parent_leaf: &Leaf2,
517) -> anyhow::Result<PermittedRewardMerkleTreeV2>
518where
519 T: SequencerStateDataSource,
520{
521 let tree = match storage
524 .load_reward_merkle_tree_v2(parent_leaf.height())
525 .await
526 {
527 Ok(tree) => tree,
528 Err(_) => storage
529 .load_latest_reward_merkle_tree_v2(parent_leaf.height())
530 .await
531 .context("failed to load RewardMerkleTreeV2 for the previous height")?,
532 };
533
534 let parent_reward_root = parent_leaf
538 .block_header()
539 .reward_merkle_tree_root()
540 .right()
541 .context("V5+ parent header must have a v2 reward root")?;
542 ensure!(
543 tree.tree.commitment() == parent_reward_root,
544 "loaded reward tree root {} does not match parent header {parent_reward_root}",
545 tree.tree.commitment(),
546 );
547 Ok(tree)
548}
549
550async fn seed_boundary_snapshot<T>(
567 storage: &Arc<T>,
568 instance: &NodeState,
569 provider: &LightClientProvider,
570) -> Option<u64>
571where
572 T: SequencerStateDataSource,
573 for<'a> T::Transaction<'a>: SequencerStateUpdate,
574{
575 let epoch_height = instance.epoch_height.filter(|height| *height > 0)?;
576 let pruned = retry_until_ok("loading the data pruned height", || {
577 storage.load_pruned_height()
578 })
579 .await
580 .unwrap_or(0);
581 let mut retry = 0;
585 let (leaf, state) = loop {
586 let tip = local_tip_above(&**storage, pruned).await;
587 let boundary = tip - tip % epoch_height;
588 if boundary == 0 {
589 return None;
590 }
591 let leaf = leaf_at(storage, provider, boundary).await;
592 if leaf.block_header().version() < EPOCH_REWARD_VERSION {
593 return None;
594 }
595 let catchup = SingleAttemptCatchup::new(instance.state_catchup.clone(), retry);
596 match fetch_boundary_state(&catchup, instance, &leaf).await {
597 Ok(state) => break (leaf, state),
598 Err(err) => {
602 tracing::warn!(
603 boundary,
604 retry,
605 err = %format_args!("{err:#}"),
606 "fetching the state at the epoch boundary failed, retrying"
607 );
608 retry += 1;
609 sleep(BOUNDARY_RETRY_INTERVAL).await;
610 },
611 }
612 };
613 let height = leaf.height();
614 retry_until_ok(
616 &format!("storing the boundary snapshot at height {height}"),
617 || store_boundary_snapshot(&**storage, height, &state),
618 )
619 .await;
620 tracing::info!(
621 height,
622 pruned,
623 "state loop starts from a snapshot at a recent epoch boundary"
624 );
625 Some(height)
626}
627
628async fn local_tip_above<T>(storage: &T, pruned: u64) -> u64
630where
631 T: SequencerStateDataSource,
632{
633 loop {
634 match storage.block_height().await {
635 Ok(height) if height as u64 > pruned + 1 => return height as u64 - 1,
636 Ok(height) => tracing::info!(
637 height,
638 pruned,
639 "no block above the data pruned height yet; waiting for one"
640 ),
641 Err(err) => tracing::warn!(
642 err = %format_args!("{err:#}"),
643 "failed to load the local block height"
644 ),
645 }
646 sleep(BOUNDARY_RETRY_INTERVAL).await;
647 }
648}
649
650async fn fetch_boundary_state(
653 catchup: &impl StateCatchup,
654 instance: &NodeState,
655 leaf: &Leaf2,
656) -> anyhow::Result<ValidatedState> {
657 let header = leaf.block_header();
658 let mut state = ValidatedState::from_header(header);
659 catchup
660 .remember_blocks_merkle_tree(
661 instance,
662 leaf.height(),
663 leaf.view_number(),
664 &mut state.block_merkle_tree,
665 )
666 .await
667 .context("fetching the block frontier")?;
668 let reward_root = header
669 .reward_merkle_tree_root()
670 .right()
671 .context("V5+ header must have a v2 reward root")?;
672 state.reward_merkle_tree_v2 = catchup
673 .fetch_reward_merkle_tree_v2(
674 leaf.height(),
675 leaf.view_number(),
676 reward_root,
677 Default::default(),
678 )
679 .await
680 .context("fetching the reward tree")?
681 .tree;
682 Ok(state)
683}
684
685async fn store_boundary_snapshot<T>(
700 storage: &T,
701 height: u64,
702 state: &ValidatedState,
703) -> anyhow::Result<()>
704where
705 T: SequencerStateDataSource,
706 for<'a> T::Transaction<'a>: SequencerStateUpdate,
707{
708 storage
709 .save_reward_tree_v2(height, &state.reward_merkle_tree_v2)
710 .await
711 .context("storing the boundary reward tree")?;
712
713 let mut tx = storage
714 .write()
715 .await
716 .context("opening transaction for the boundary snapshot")?;
717 store_block_frontier(&mut tx, height, &state.block_merkle_tree).await?;
718 if let Some(chain_config) = state.chain_config.resolve() {
719 tx.insert_chain_config(chain_config).await?;
720 }
721 UpdateStateData::<SeqTypes, _, { BlockMerkleTree::ARITY }>::set_last_state_height(
722 &mut tx,
723 height as usize,
724 )
725 .await
726 .context("setting state height")?;
727 UpdateStateData::<SeqTypes, _, { BlockMerkleTree::ARITY }>::set_state_pruned_height(
728 &mut tx,
729 height - 1,
730 )
731 .await
732 .context("setting state pruned height")?;
733 UpdateStateData::<SeqTypes, _, { BlockMerkleTree::ARITY }>::set_state_start_height(
734 &mut tx, height,
735 )
736 .await
737 .context("setting state start height")?;
738 tx.commit().await
739}
740
741async fn follow_leaves<T>(
746 storage: &Arc<T>,
747 instance: &NodeState,
748 provider: &LightClientProvider,
749 mut parent_leaf: Leaf2,
750 mut parent_state: ValidatedState,
751) where
752 T: SequencerStateDataSource,
753 for<'a> T::Transaction<'a>: SequencerStateUpdate,
754{
755 let limit = availability::Options::default().small_object_range_limit as u64;
757 let mut next_height = parent_leaf.height() + 1;
758 loop {
759 while let Some(pruned) = pruned_past(&**storage, next_height).await {
760 let end = (next_height + limit).min(pruned + 1);
761 tracing::info!(
762 next_height,
763 end,
764 pruned,
765 "fetching leaves below the data pruned height from peers"
766 );
767 for leaf in fetch_pruned_leaves(provider, next_height..end).await {
768 if leaf.parent_commitment() != parent_leaf.commit() {
772 tracing::error!(
773 height = next_height,
774 parent = %parent_leaf.commit(),
775 leaf_parent = %leaf.parent_commitment(),
776 "fetched leaf does not extend the state loop's parent; stopping the loop"
777 );
778 return;
779 }
780 apply_leaf(storage, instance, &mut parent_leaf, &mut parent_state, leaf).await;
781 next_height += 1;
782 }
783 }
784
785 let mut leaves = storage.subscribe_leaves(next_height as usize).await;
786 loop {
787 match timeout(PRUNED_LEAF_CHECK_INTERVAL, leaves.next()).await {
788 Ok(Some(leaf)) => {
789 next_height = leaf.height() + 1;
790 apply_leaf(
791 storage,
792 instance,
793 &mut parent_leaf,
794 &mut parent_state,
795 leaf.leaf().clone(),
796 )
797 .await;
798 },
799 Ok(None) => return,
800 Err(_) => {
803 if pruned_past(&**storage, next_height).await.is_some() {
804 break;
805 }
806 },
807 }
808 }
809 }
810}
811
812async fn pruned_past<T>(storage: &T, height: u64) -> Option<u64>
817where
818 T: SequencerStateDataSource,
819{
820 match storage.load_pruned_height().await {
821 Ok(pruned) => pruned.filter(|pruned| height <= *pruned),
822 Err(err) => {
823 tracing::warn!(height, "failed to load the data pruned height: {err:#}");
824 None
825 },
826 }
827}
828
829async fn leaf_at<T>(storage: &Arc<T>, provider: &LightClientProvider, height: u64) -> Leaf2
831where
832 T: SequencerStateDataSource,
833{
834 loop {
835 if let Some(pruned) = pruned_past(&**storage, height).await {
836 tracing::warn!(
837 height,
838 pruned,
839 "leaf is at or below the data pruned height; fetching it from peers"
840 );
841 return fetch_pruned_leaves(provider, height..height + 1)
842 .await
843 .pop()
844 .expect("a one-height range yields exactly one leaf");
845 }
846 let local = AvailabilityDataSource::get_leaf(&**storage, height as usize).await;
847 if let Ok(leaf) = timeout(PRUNED_LEAF_CHECK_INTERVAL, local).await {
850 return leaf.leaf().clone();
851 }
852 tracing::info!(
853 height,
854 "leaf is not in local storage yet; still waiting for it"
855 );
856 }
857}
858
859async fn fetch_pruned_leaves(provider: &LightClientProvider, heights: Range<u64>) -> Vec<Leaf2> {
865 let start = heights.start as usize;
866 let end = heights.end as usize;
867 let mut delay = Duration::from_secs(1);
868 loop {
869 if let Some(light_client) = provider.light_client().await {
870 match light_client
871 .fetch_leaves_in_range_uncached(start, end)
872 .await
873 {
874 Ok(leaves) => return leaves.into_iter().map(|leaf| leaf.leaf().clone()).collect(),
875 Err(err) => tracing::warn!(
876 ?heights,
877 ?delay,
878 err = %format_args!("{err:#}"),
879 "fetching leaves failed, retrying"
880 ),
881 }
882 }
883 sleep(delay).await;
884 delay = (delay * 2).min(Duration::from_secs(60));
885 }
886}
887
888async fn apply_leaf<T>(
890 storage: &Arc<T>,
891 instance: &NodeState,
892 parent_leaf: &mut Leaf2,
893 parent_state: &mut ValidatedState,
894 leaf: Leaf2,
895) where
896 T: SequencerStateDataSource,
897 for<'a> T::Transaction<'a>: SequencerStateUpdate,
898{
899 for retry in 0.. {
909 tracing::debug!(
910 height = leaf.height(),
911 node_id = instance.node_id,
912 ?leaf,
913 "updating persistent merklized state"
914 );
915 match update_state_storage(
916 parent_state,
917 storage,
918 instance,
919 &SingleAttemptCatchup::new(instance.state_catchup.clone(), retry),
920 parent_leaf,
921 &leaf,
922 )
923 .await
924 {
925 Ok(state) => {
926 *parent_leaf = leaf;
927 *parent_state = state;
928 return;
929 },
930 Err(err) => {
931 tracing::error!(height = leaf.height(), "failed to update state: {err:#}");
932 sleep(Duration::from_secs(1)).await;
934 },
935 }
936 }
937}
938
939pub(crate) trait SequencerStateDataSource:
940 'static
941 + Debug
942 + AvailabilityDataSource<SeqTypes>
943 + StatusDataSource
944 + VersionedDataSource
945 + CatchupStorage
946 + RewardMerkleTreeDataSource
947 + PrunedHeightDataSource
948 + MerklizedStateHeightPersistence
949{
950}
951
952impl<T> SequencerStateDataSource for T where
953 T: 'static
954 + Debug
955 + AvailabilityDataSource<SeqTypes>
956 + StatusDataSource
957 + VersionedDataSource
958 + CatchupStorage
959 + RewardMerkleTreeDataSource
960 + PrunedHeightDataSource
961 + MerklizedStateHeightPersistence
962{
963}
964
965pub(crate) trait SequencerStateUpdate:
966 Transaction
967 + UpdateStateData<SeqTypes, FeeMerkleTree, { FeeMerkleTree::ARITY }>
968 + UpdateStateData<SeqTypes, BlockMerkleTree, { BlockMerkleTree::ARITY }>
969 + UpdateStateData<SeqTypes, RewardMerkleTreeV1, { RewardMerkleTreeV1::ARITY }>
970 + ChainConfigPersistence
971{
972}
973
974impl<T> SequencerStateUpdate for T where
975 T: Transaction
976 + UpdateStateData<SeqTypes, FeeMerkleTree, { FeeMerkleTree::ARITY }>
977 + UpdateStateData<SeqTypes, BlockMerkleTree, { BlockMerkleTree::ARITY }>
978 + UpdateStateData<SeqTypes, RewardMerkleTreeV1, { RewardMerkleTreeV1::ARITY }>
979 + ChainConfigPersistence
980{
981}