1use std::{
2 cmp::Ordering,
3 collections::HashMap,
4 fmt::{Debug, Display},
5 sync::Arc,
6 time::Duration,
7};
8
9use anyhow::{Context, anyhow, bail, ensure};
10use async_lock::RwLock;
11use async_trait::async_trait;
12use committable::{Commitment, Committable};
13use espresso_types::{
14 BackoffParams, BlockMerkleTree, Certificate2, FeeAccount, FeeAccountProof, FeeMerkleCommitment,
15 FeeMerkleTree, Leaf2, NodeState, SeqTypes, ValidatedState,
16 config::PublicNetworkConfig,
17 v0::traits::StateCatchup,
18 v0_3::{
19 ChainConfig, RewardAccountProofV1, RewardAccountV1, RewardMerkleCommitmentV1,
20 RewardMerkleTreeV1,
21 },
22 v0_4::{
23 PermittedRewardMerkleTreeV2, RewardAccountProofV2, RewardAccountV2,
24 RewardMerkleCommitmentV2, RewardMerkleTreeV2, forgotten_accounts_include,
25 },
26};
27use futures::{
28 StreamExt,
29 future::{Future, FutureExt, TryFuture, TryFutureExt},
30 stream::FuturesUnordered,
31};
32use hotshot_new_protocol::{storage::NewProtocolStorage, utils::verify_new_protocol_leaf_chain};
33use hotshot_types::{
34 ValidatorConfig,
35 data::{EpochNumber, ViewNumber},
36 epoch_membership::EpochMembershipCoordinator,
37 message::UpgradeLock,
38 network::NetworkConfig,
39 simple_certificate::LightClientStateUpdateCertificateV2,
40 traits::{
41 ValidatedState as ValidatedStateTrait,
42 metrics::{Counter, CounterFamily, Metrics},
43 },
44 utils::{epoch_from_block_number, verify_leaf_chain},
45};
46use itertools::Itertools;
47use jf_merkle_tree_compat::{ForgetableMerkleTreeScheme, MerkleTreeScheme, prelude::MerkleNode};
48use parking_lot::Mutex;
49use priority_queue::PriorityQueue;
50use serde::de::DeserializeOwned;
51use surf_disco::Request;
52use tide_disco::error::ServerError;
53use tokio::time::timeout;
54use tokio_util::task::AbortOnDropHandle;
55use url::Url;
56use vbs::version::StaticVersionType;
57use versions::{EPOCH_VERSION, NEW_PROTOCOL_VERSION};
58
59use crate::{
60 api::{BlocksFrontier, RewardMerkleTreeDataSource, RewardMerkleTreeV2Data},
61 consensus_handle::ConsensusHandle,
62};
63
64#[derive(Debug, Clone)]
67struct Client<ServerError, ApiVer: StaticVersionType> {
68 inner: surf_disco::Client<ServerError, ApiVer>,
69 url: Url,
70 requests: Arc<Box<dyn Counter>>,
71 failures: Arc<Box<dyn Counter>>,
72}
73
74impl<ApiVer: StaticVersionType> Client<ServerError, ApiVer> {
75 pub fn new(
76 url: Url,
77 requests: &(impl CounterFamily + ?Sized),
78 failures: &(impl CounterFamily + ?Sized),
79 ) -> Self {
80 Self {
81 inner: surf_disco::Client::new(url.clone()),
82 requests: Arc::new(requests.create(vec![url.to_string()])),
83 failures: Arc::new(failures.create(vec![url.to_string()])),
84 url,
85 }
86 }
87
88 pub fn get<T: DeserializeOwned>(&self, route: &str) -> Request<T, ServerError, ApiVer> {
89 self.inner.get(route)
90 }
91}
92
93#[derive(Clone, Copy, Debug, Default)]
102struct PeerScore {
103 requests: usize,
104 failures: usize,
105}
106
107impl Ord for PeerScore {
108 fn cmp(&self, other: &Self) -> Ordering {
109 (other.failures * self.requests).cmp(&(self.failures * other.requests))
114 }
115}
116
117impl PartialOrd for PeerScore {
118 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
119 Some(self.cmp(other))
120 }
121}
122
123impl PartialEq for PeerScore {
124 fn eq(&self, other: &Self) -> bool {
125 self.cmp(other).is_eq()
126 }
127}
128
129impl Eq for PeerScore {}
130
131#[derive(Debug, Clone, Default)]
132pub struct StatePeers<ApiVer: StaticVersionType> {
133 scores: Arc<RwLock<PriorityQueue<usize, PeerScore>>>,
135 clients: Vec<Client<ServerError, ApiVer>>,
136 backoff: BackoffParams,
137 base_timeout: Duration,
139}
140
141impl<ApiVer: StaticVersionType> StatePeers<ApiVer> {
142 async fn fetch<Fut>(
143 &self,
144 retry: usize,
145 f: impl Fn(Client<ServerError, ApiVer>) -> Fut,
146 ) -> anyhow::Result<Fut::Ok>
147 where
148 Fut: TryFuture<Error: Display>,
149 {
150 let timeout_dur = self.base_timeout * (retry as u32 + 1);
161
162 let mut requests = HashMap::new();
165 let mut res = Err(anyhow!("failed fetching from every peer"));
166
167 let mut scores = { (*self.scores.read().await).clone() };
172 let mut logs = vec![format!("Fetching failed.\n")];
173 while let Some((id, score)) = scores.pop() {
174 let client = &self.clients[id];
175 tracing::info!("fetching from {}", client.url);
176 match timeout(timeout_dur, TryFutureExt::into_future(f(client.clone()))).await {
177 Ok(Ok(t)) => {
178 requests.insert(id, true);
179 res = Ok(t);
180 logs = Vec::new();
181 break;
182 },
183 Ok(Err(err)) => {
184 tracing::debug!(id, ?score, peer = %client.url, "error from peer: {err:#}");
185 logs.push(format!(
186 "Error from peer {} with id {id} and score {score:?}: {err:#}",
187 client.url
188 ));
189 requests.insert(id, false);
190 },
191 Err(_) => {
192 tracing::debug!(id, ?score, peer = %client.url, ?timeout_dur, "request timed out");
193 logs.push(format!(
194 "Error from peer {} with id {id} and score {score:?}: request timed out",
195 client.url
196 ));
197 requests.insert(id, false);
198 },
199 }
200 }
201
202 if !logs.is_empty() {
203 tracing::warn!("{}", logs.join("\n"));
204 }
205
206 let mut scores = self.scores.write().await;
208 for (id, success) in requests {
209 scores.change_priority_by(&id, |score| {
210 score.requests += 1;
211 self.clients[id].requests.add(1);
212 if !success {
213 score.failures += 1;
214 self.clients[id].failures.add(1);
215 }
216 });
217 }
218
219 res
220 }
221
222 pub fn from_urls(
223 urls: Vec<Url>,
224 backoff: BackoffParams,
225 base_timeout: Duration,
226 metrics: &(impl Metrics + ?Sized),
227 ) -> Self {
228 if urls.is_empty() {
229 panic!("Cannot create StatePeers with no peers");
230 }
231
232 let metrics = metrics.subgroup("catchup".into());
233 let requests = metrics.counter_family("requests".into(), vec!["peer".into()]);
234 let failures = metrics.counter_family("request_failures".into(), vec!["peer".into()]);
235
236 let scores = urls
237 .iter()
238 .enumerate()
239 .map(|(i, _)| (i, PeerScore::default()))
240 .collect();
241 let clients = urls
242 .into_iter()
243 .map(|url| Client::new(url, &*requests, &*failures))
244 .collect();
245
246 Self {
247 clients,
248 scores: Arc::new(RwLock::new(scores)),
249 backoff,
250 base_timeout,
251 }
252 }
253
254 #[tracing::instrument(skip(self, my_own_validator_config))]
255 pub async fn fetch_config(
256 &self,
257 my_own_validator_config: ValidatorConfig<SeqTypes>,
258 ) -> anyhow::Result<NetworkConfig<SeqTypes>> {
259 self.backoff()
260 .retry(self, move |provider, retry| {
261 let my_own_validator_config = my_own_validator_config.clone();
262 async move {
263 let cfg: PublicNetworkConfig = provider
264 .fetch(retry, |client| {
265 let url = client.url.join("config/hotshot").unwrap();
266
267 reqwest::get(url.clone())
268 })
269 .await?
270 .json()
271 .await?;
272 cfg.into_network_config(my_own_validator_config)
273 .context("fetched config, but failed to convert to private config")
274 }
275 .boxed()
276 })
277 .await
278 }
279}
280
281pub(crate) async fn verify_legacy_leaf_chain(
283 leaf_chain: Vec<Leaf2>,
284 coordinator: &EpochMembershipCoordinator<SeqTypes>,
285 height: u64,
286) -> anyhow::Result<Leaf2> {
287 let upgrade_lock = UpgradeLock::<SeqTypes>::new(versions::Upgrade::trivial(EPOCH_VERSION));
288 let epoch = EpochNumber::new(epoch_from_block_number(height, *coordinator.epoch_height()));
289 let membership = coordinator
290 .stake_table_for_epoch(Some(epoch))
291 .map_err(|err| anyhow!("no stake table available for epoch {epoch}: {err:?}"))?;
292 let stake_table: Vec<_> = membership.stake_table().cloned().collect();
293 verify_leaf_chain(
294 leaf_chain,
295 &stake_table,
296 membership.success_threshold(),
297 height,
298 &upgrade_lock,
299 )
300 .await
301 .with_context(|| format!("failed to verify leaf chain at height {height}"))
302}
303
304#[async_trait]
305impl<ApiVer: StaticVersionType> StateCatchup for StatePeers<ApiVer> {
306 #[tracing::instrument(skip(self, _instance))]
307 async fn try_fetch_accounts(
308 &self,
309 retry: usize,
310 _instance: &NodeState,
311 height: u64,
312 view: ViewNumber,
313 fee_merkle_tree_root: FeeMerkleCommitment,
314 accounts: &[FeeAccount],
315 ) -> anyhow::Result<Vec<FeeAccountProof>> {
316 self.fetch(retry, |client| async move {
317 let tree = client
318 .inner
319 .post::<FeeMerkleTree>(&format!("catchup/{height}/{}/accounts", view.u64()))
320 .body_binary(&accounts.to_vec())?
321 .send()
322 .await?;
323
324 let mut proofs = Vec::new();
326 for account in accounts {
327 let (proof, _) = FeeAccountProof::prove(&tree, (*account).into())
328 .context(format!("response missing fee account {account}"))?;
329 proof.verify(&fee_merkle_tree_root).context(format!(
330 "invalid proof for fee account {account}, root: {fee_merkle_tree_root}"
331 ))?;
332 proofs.push(proof);
333 }
334
335 anyhow::Ok(proofs)
336 })
337 .await
338 }
339
340 #[tracing::instrument(skip(self, _instance, mt))]
341 async fn try_remember_blocks_merkle_tree(
342 &self,
343 retry: usize,
344 _instance: &NodeState,
345 height: u64,
346 view: ViewNumber,
347 mt: &mut BlockMerkleTree,
348 ) -> anyhow::Result<()> {
349 *mt = self
350 .fetch(retry, |client| {
351 let mut mt = mt.clone();
352 async move {
353 let frontier = client
354 .get::<BlocksFrontier>(&format!("catchup/{height}/{}/blocks", view.u64()))
355 .send()
356 .await?;
357 let elem = frontier
358 .elem()
359 .context("provided frontier is missing leaf element")?;
360 mt.remember(mt.num_leaves() - 1, *elem, &frontier)
361 .context("verifying block proof")?;
362 anyhow::Ok(mt)
363 }
364 })
365 .await?;
366 Ok(())
367 }
368
369 async fn try_fetch_chain_config(
370 &self,
371 retry: usize,
372 commitment: Commitment<ChainConfig>,
373 ) -> anyhow::Result<ChainConfig> {
374 self.fetch(retry, |client| async move {
375 let cf = client
376 .get::<ChainConfig>(&format!("catchup/chain-config/{commitment}"))
377 .send()
378 .await?;
379 ensure!(
380 cf.commit() == commitment,
381 "received chain config with mismatched commitment: expected {commitment}, got {}",
382 cf.commit()
383 );
384 Ok(cf)
385 })
386 .await
387 }
388
389 async fn try_fetch_leaf(
390 &self,
391 retry: usize,
392 coordinator: EpochMembershipCoordinator<SeqTypes>,
393 height: u64,
394 ) -> anyhow::Result<Leaf2> {
395 let leaf_chain = self
399 .fetch(retry, |client| async move {
400 let chain = client
401 .get::<Vec<Leaf2>>(&format!("catchup/{height}/leafchain"))
402 .send()
403 .await?;
404 anyhow::Ok(chain)
405 })
406 .await
407 .with_context(|| format!("failed to fetch leaf chain at height {height}"))?;
408
409 let first = leaf_chain
410 .first()
411 .ok_or_else(|| anyhow!("empty leaf chain returned for height {height}"))?;
412
413 if first.block_header().version() >= NEW_PROTOCOL_VERSION {
414 let upgrade_lock =
415 UpgradeLock::<SeqTypes>::new(versions::Upgrade::trivial(NEW_PROTOCOL_VERSION));
416
417 let cert2_height = leaf_chain
420 .last()
421 .ok_or_else(|| anyhow!("empty leaf chain returned for height {height}"))?
422 .height();
423 let cert2 = self
424 .fetch(retry, |client| async move {
425 let cert2 = client
426 .get::<Certificate2<SeqTypes>>(&format!("catchup/{cert2_height}/cert2"))
427 .send()
428 .await?;
429 anyhow::Ok(cert2)
430 })
431 .await
432 .with_context(|| format!("failed to fetch cert2 for height {cert2_height}"))?;
433
434 verify_new_protocol_leaf_chain(leaf_chain, &coordinator, height, &upgrade_lock, cert2)
435 .await
436 .with_context(|| {
437 format!("failed to verify leaf chain with cert2 at height {height}")
438 })
439 } else {
440 verify_legacy_leaf_chain(leaf_chain, &coordinator, height).await
441 }
442 }
443
444 async fn try_fetch_reward_merkle_tree_v2(
445 &self,
446 retry: usize,
447 height: u64,
448 view: ViewNumber,
449 reward_merkle_tree_root: RewardMerkleCommitmentV2,
450 accounts: Arc<Vec<RewardAccountV2>>,
451 ) -> anyhow::Result<PermittedRewardMerkleTreeV2> {
452 let result = self
453 .fetch(retry, |client| async move {
454 let tree_bytes = match client
458 .inner
459 .get::<Vec<u8>>(&format!("catchup/reward-merkle-tree-v2/{height}/{}", *view))
460 .send()
461 .await
462 {
463 Ok(bytes) => bytes,
464 Err(err) => {
465 tracing::info!(
466 "catchup endpoint failed, falling back to reward-state-v2: {err:#}"
467 );
468 client
469 .inner
470 .get::<Vec<u8>>(&format!(
471 "reward-state-v2/reward-merkle-tree-v2/{height}"
472 ))
473 .send()
474 .await?
475 },
476 };
477
478 Ok::<Vec<u8>, anyhow::Error>(tree_bytes)
479 })
480 .await
481 .context("Fetching from peer failed")?;
482
483 let tree_data = bincode::deserialize::<RewardMerkleTreeV2Data>(&result)
484 .context("Failed to deserialize merkle tree from catchup")?;
485
486 let tree: PermittedRewardMerkleTreeV2 =
487 PermittedRewardMerkleTreeV2::try_from_kv_set(tree_data.balances).await?;
488
489 ensure!(
490 tree.tree.commitment() == reward_merkle_tree_root,
491 "RewardMerkleTreeV2 from peer failed commitment check."
492 );
493 ensure!(!forgotten_accounts_include(&tree, &accounts));
494
495 Ok(tree)
496 }
497
498 #[tracing::instrument(skip(self, _instance))]
499 async fn try_fetch_reward_accounts_v1(
500 &self,
501 retry: usize,
502 _instance: &NodeState,
503 height: u64,
504 view: ViewNumber,
505 reward_merkle_tree_root: RewardMerkleCommitmentV1,
506 accounts: &[RewardAccountV1],
507 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
508 self.fetch(retry, |client| async move {
509 let tree = client
510 .inner
511 .post::<RewardMerkleTreeV1>(&format!(
512 "catchup/{height}/{}/reward-accounts",
513 view.u64()
514 ))
515 .body_binary(&accounts.to_vec())?
516 .send()
517 .await?;
518
519 let mut proofs = Vec::new();
521 for account in accounts {
522 let (proof, _) = RewardAccountProofV1::prove(&tree, (*account).into())
523 .context(format!("response missing reward account {account}"))?;
524 proof.verify(&reward_merkle_tree_root).context(format!(
525 "invalid proof for v1 reward account {account}, root: \
526 {reward_merkle_tree_root} height {height} view {view}"
527 ))?;
528 proofs.push(proof);
529 }
530
531 anyhow::Ok(proofs)
532 })
533 .await
534 }
535
536 async fn try_fetch_state_cert(
537 &self,
538 retry: usize,
539 epoch: u64,
540 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
541 self.fetch(retry, |client| async move {
542 client
543 .get::<LightClientStateUpdateCertificateV2<SeqTypes>>(&format!(
544 "catchup/{epoch}/state-cert"
545 ))
546 .send()
547 .await
548 })
549 .await
550 }
551
552 fn backoff(&self) -> &BackoffParams {
553 &self.backoff
554 }
555
556 fn name(&self) -> String {
557 format!(
558 "StatePeers({})",
559 self.clients
560 .iter()
561 .map(|client| client.url.to_string())
562 .join(",")
563 )
564 }
565
566 fn is_local(&self) -> bool {
567 false
568 }
569}
570
571pub(crate) trait CatchupStorage: Sync {
572 fn get_accounts(
583 &self,
584 _instance: &NodeState,
585 _height: u64,
586 _view: ViewNumber,
587 _accounts: &[FeeAccount],
588 ) -> impl Send + Future<Output = anyhow::Result<(FeeMerkleTree, Leaf2)>> {
589 async {
594 bail!("merklized state catchup is not supported for this data source");
595 }
596 }
597
598 fn get_reward_accounts_v1(
599 &self,
600 _instance: &NodeState,
601 _height: u64,
602 _view: ViewNumber,
603 _accounts: &[RewardAccountV1],
604 ) -> impl Send + Future<Output = anyhow::Result<(RewardMerkleTreeV1, Leaf2)>> {
605 async {
606 bail!("merklized state catchup is not supported for this data source");
607 }
608 }
609
610 fn get_reward_accounts_v2(
611 &self,
612 _instance: &NodeState,
613 _height: u64,
614 _view: ViewNumber,
615 _accounts: &[RewardAccountV2],
616 ) -> impl Send + Future<Output = anyhow::Result<(RewardMerkleTreeV2, Leaf2)>> {
617 async {
618 bail!("merklized state catchup is not supported for this data source");
619 }
620 }
621
622 fn get_frontier(
629 &self,
630 _instance: &NodeState,
631 _height: u64,
632 _view: ViewNumber,
633 ) -> impl Send + Future<Output = anyhow::Result<BlocksFrontier>> {
634 async {
639 bail!("merklized state catchup is not supported for this data source");
640 }
641 }
642
643 fn get_chain_config(
644 &self,
645 _commitment: Commitment<ChainConfig>,
646 ) -> impl Send + Future<Output = anyhow::Result<ChainConfig>> {
647 async {
648 bail!("chain config catchup is not supported for this data source");
649 }
650 }
651
652 fn get_leaf_chain(
653 &self,
654 _height: u64,
655 ) -> impl Send + Future<Output = anyhow::Result<Vec<Leaf2>>> {
656 async {
657 bail!("leaf chain catchup is not supported for this data source");
658 }
659 }
660
661 fn load_cert2(
663 &self,
664 _height: u64,
665 ) -> impl Send + Future<Output = anyhow::Result<Option<Certificate2<SeqTypes>>>> {
666 async { Ok(None) }
667 }
668
669 fn get_leaf(&self, _height: u64) -> impl Send + Future<Output = anyhow::Result<Leaf2>> {
671 async {
672 bail!("leaf fetch is not supported for this data source");
673 }
674 }
675}
676
677impl CatchupStorage for hotshot_query_service::data_source::MetricsDataSource {}
678
679impl<T, S> CatchupStorage for hotshot_query_service::data_source::ExtensibleDataSource<T, S>
680where
681 T: CatchupStorage,
682 S: Sync,
683{
684 async fn get_accounts(
685 &self,
686 instance: &NodeState,
687 height: u64,
688 view: ViewNumber,
689 accounts: &[FeeAccount],
690 ) -> anyhow::Result<(FeeMerkleTree, Leaf2)> {
691 self.inner()
692 .get_accounts(instance, height, view, accounts)
693 .await
694 }
695
696 async fn get_reward_accounts_v2(
697 &self,
698 instance: &NodeState,
699 height: u64,
700 view: ViewNumber,
701 accounts: &[RewardAccountV2],
702 ) -> anyhow::Result<(RewardMerkleTreeV2, Leaf2)> {
703 self.inner()
704 .get_reward_accounts_v2(instance, height, view, accounts)
705 .await
706 }
707
708 async fn get_reward_accounts_v1(
709 &self,
710 instance: &NodeState,
711 height: u64,
712 view: ViewNumber,
713 accounts: &[RewardAccountV1],
714 ) -> anyhow::Result<(RewardMerkleTreeV1, Leaf2)> {
715 self.inner()
716 .get_reward_accounts_v1(instance, height, view, accounts)
717 .await
718 }
719
720 async fn get_frontier(
721 &self,
722 instance: &NodeState,
723 height: u64,
724 view: ViewNumber,
725 ) -> anyhow::Result<BlocksFrontier> {
726 self.inner().get_frontier(instance, height, view).await
727 }
728
729 async fn get_chain_config(
730 &self,
731 commitment: Commitment<ChainConfig>,
732 ) -> anyhow::Result<ChainConfig> {
733 self.inner().get_chain_config(commitment).await
734 }
735 async fn get_leaf_chain(&self, height: u64) -> anyhow::Result<Vec<Leaf2>> {
736 self.inner().get_leaf_chain(height).await
737 }
738
739 async fn load_cert2(&self, height: u64) -> anyhow::Result<Option<Certificate2<SeqTypes>>> {
740 self.inner().load_cert2(height).await
741 }
742
743 async fn get_leaf(&self, height: u64) -> anyhow::Result<Leaf2> {
744 self.inner().get_leaf(height).await
745 }
746}
747
748#[derive(Debug)]
749pub(crate) struct SqlStateCatchup<T> {
750 db: Arc<T>,
751 backoff: BackoffParams,
752}
753
754impl<T> SqlStateCatchup<T> {
755 pub(crate) fn new(db: Arc<T>, backoff: BackoffParams) -> Self {
756 Self { db, backoff }
757 }
758}
759
760#[async_trait]
761impl<T> StateCatchup for SqlStateCatchup<T>
762where
763 T: CatchupStorage + RewardMerkleTreeDataSource + Send + Sync,
764{
765 async fn try_fetch_leaf(
766 &self,
767 _retry: usize,
768 _coordinator: EpochMembershipCoordinator<SeqTypes>,
769 height: u64,
770 ) -> anyhow::Result<Leaf2> {
771 self.db
774 .get_leaf(height)
775 .await
776 .with_context(|| format!("failed to load leaf at height {height} from DB"))
777 }
778
779 #[tracing::instrument(skip(self, _retry, instance))]
782 async fn try_fetch_accounts(
783 &self,
784 _retry: usize,
785 instance: &NodeState,
786 block_height: u64,
787 view: ViewNumber,
788 fee_merkle_tree_root: FeeMerkleCommitment,
789 accounts: &[FeeAccount],
790 ) -> anyhow::Result<Vec<FeeAccountProof>> {
791 let (fee_merkle_tree_from_db, _) = self
793 .db
794 .get_accounts(instance, block_height, view, accounts)
795 .await
796 .with_context(|| "failed to get fee accounts from DB")?;
797
798 let mut proofs = Vec::new();
800 for account in accounts {
801 let (proof, _) = FeeAccountProof::prove(&fee_merkle_tree_from_db, (*account).into())
802 .context(format!("response missing account {account}"))?;
803 proof.verify(&fee_merkle_tree_root).context(format!(
804 "invalid proof for fee account {account}, root: {fee_merkle_tree_root}"
805 ))?;
806 proofs.push(proof);
807 }
808
809 Ok(proofs)
810 }
811
812 #[tracing::instrument(skip(self, _retry, instance, mt))]
813 async fn try_remember_blocks_merkle_tree(
814 &self,
815 _retry: usize,
816 instance: &NodeState,
817 bh: u64,
818 view: ViewNumber,
819 mt: &mut BlockMerkleTree,
820 ) -> anyhow::Result<()> {
821 if bh == 0 {
822 return Ok(());
823 }
824
825 let proof = self.db.get_frontier(instance, bh, view).await?;
826 match proof
827 .proof
828 .first()
829 .context(format!("empty proof for frontier at height {bh}"))?
830 {
831 MerkleNode::Leaf { pos, elem, .. } => mt
832 .remember(pos, elem, proof.clone())
833 .context("failed to remember proof"),
834 _ => bail!("invalid proof"),
835 }
836 }
837
838 async fn try_fetch_chain_config(
839 &self,
840 _retry: usize,
841 commitment: Commitment<ChainConfig>,
842 ) -> anyhow::Result<ChainConfig> {
843 let cf = self.db.get_chain_config(commitment).await?;
844
845 if cf.commit() != commitment {
846 panic!(
847 "Critical error: Mismatched chain config detected. Expected chain config: {:?}, \
848 but got: {:?}.
849 This may indicate a compromised database",
850 commitment,
851 cf.commit()
852 )
853 }
854
855 Ok(cf)
856 }
857
858 async fn try_fetch_reward_merkle_tree_v2(
859 &self,
860 _retry: usize,
861 height: u64,
862 _view: ViewNumber,
863 reward_merkle_tree_root: RewardMerkleCommitmentV2,
864 accounts: Arc<Vec<RewardAccountV2>>,
865 ) -> anyhow::Result<PermittedRewardMerkleTreeV2> {
866 let tree: PermittedRewardMerkleTreeV2 = self.db.load_reward_merkle_tree_v2(height).await?;
867
868 ensure!(tree.tree.commitment() == reward_merkle_tree_root);
869 ensure!(!forgotten_accounts_include(&tree, &accounts));
870
871 Ok(tree)
872 }
873
874 #[tracing::instrument(skip(self, _retry, instance))]
875 async fn try_fetch_reward_accounts_v1(
876 &self,
877 _retry: usize,
878 instance: &NodeState,
879 block_height: u64,
880 view: ViewNumber,
881 reward_merkle_tree_root: RewardMerkleCommitmentV1,
882 accounts: &[RewardAccountV1],
883 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
884 let (reward_merkle_tree_from_db, _) = self
886 .db
887 .get_reward_accounts_v1(instance, block_height, view, accounts)
888 .await
889 .with_context(|| "failed to get reward accounts from DB")?;
890 let mut proofs = Vec::new();
892 for account in accounts {
893 let (proof, _) =
894 RewardAccountProofV1::prove(&reward_merkle_tree_from_db, (*account).into())
895 .context(format!("response missing account {account}"))?;
896 proof.verify(&reward_merkle_tree_root).context(format!(
897 "invalid proof for v1 reward account {account}, root: {reward_merkle_tree_root}"
898 ))?;
899 proofs.push(proof);
900 }
901
902 Ok(proofs)
903 }
904
905 async fn try_fetch_state_cert(
906 &self,
907 _retry: usize,
908 _epoch: u64,
909 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
910 bail!("state cert catchup not supported for SqlStateCatchup");
911 }
912
913 fn backoff(&self) -> &BackoffParams {
914 &self.backoff
915 }
916
917 fn name(&self) -> String {
918 "SqlStateCatchup".into()
919 }
920
921 fn is_local(&self) -> bool {
922 true
923 }
924}
925
926#[derive(Clone, Debug)]
928pub struct NullStateCatchup {
929 backoff: BackoffParams,
930 chain_configs: HashMap<Commitment<ChainConfig>, ChainConfig>,
931}
932
933impl Default for NullStateCatchup {
934 fn default() -> Self {
935 Self {
936 backoff: BackoffParams::disabled(),
937 chain_configs: Default::default(),
938 }
939 }
940}
941
942impl NullStateCatchup {
943 pub fn add_chain_config(&mut self, cf: ChainConfig) {
953 self.chain_configs.insert(cf.commit(), cf);
954 }
955}
956
957#[async_trait]
958impl StateCatchup for NullStateCatchup {
959 async fn try_fetch_leaf(
960 &self,
961 _retry: usize,
962 _coordinator: EpochMembershipCoordinator<SeqTypes>,
963 _height: u64,
964 ) -> anyhow::Result<Leaf2> {
965 bail!("state catchup is disabled")
966 }
967
968 async fn try_fetch_accounts(
969 &self,
970 _retry: usize,
971 _instance: &NodeState,
972 _height: u64,
973 _view: ViewNumber,
974 _fee_merkle_tree_root: FeeMerkleCommitment,
975 _account: &[FeeAccount],
976 ) -> anyhow::Result<Vec<FeeAccountProof>> {
977 bail!("state catchup is disabled");
978 }
979
980 async fn try_remember_blocks_merkle_tree(
981 &self,
982 _retry: usize,
983 _instance: &NodeState,
984 _height: u64,
985 _view: ViewNumber,
986 _mt: &mut BlockMerkleTree,
987 ) -> anyhow::Result<()> {
988 bail!("state catchup is disabled");
989 }
990
991 async fn try_fetch_chain_config(
992 &self,
993 _retry: usize,
994 commitment: Commitment<ChainConfig>,
995 ) -> anyhow::Result<ChainConfig> {
996 self.chain_configs
997 .get(&commitment)
998 .copied()
999 .context(format!("chain config {commitment} not available"))
1000 }
1001
1002 async fn try_fetch_reward_merkle_tree_v2(
1003 &self,
1004 _retry: usize,
1005 _height: u64,
1006 _view: ViewNumber,
1007 _reward_merkle_tree_root: RewardMerkleCommitmentV2,
1008 _accounts: Arc<Vec<RewardAccountV2>>,
1009 ) -> anyhow::Result<PermittedRewardMerkleTreeV2> {
1010 bail!("state catchup is disabled");
1011 }
1012
1013 async fn try_fetch_reward_accounts_v1(
1014 &self,
1015 _retry: usize,
1016 _instance: &NodeState,
1017 _height: u64,
1018 _view: ViewNumber,
1019 _fee_merkle_tree_root: RewardMerkleCommitmentV1,
1020 _account: &[RewardAccountV1],
1021 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
1022 bail!("state catchup is disabled");
1023 }
1024
1025 async fn try_fetch_state_cert(
1026 &self,
1027 _retry: usize,
1028 _epoch: u64,
1029 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
1030 bail!("state catchup is disabled");
1031 }
1032
1033 fn backoff(&self) -> &BackoffParams {
1034 &self.backoff
1035 }
1036
1037 fn name(&self) -> String {
1038 "NullStateCatchup".into()
1039 }
1040
1041 fn is_local(&self) -> bool {
1042 true
1043 }
1044}
1045
1046#[derive(Clone)]
1049pub struct ParallelStateCatchup {
1050 providers: Arc<Mutex<Vec<Arc<dyn StateCatchup>>>>,
1051 backoff: BackoffParams,
1052 local_timeout: Duration,
1054}
1055
1056impl ParallelStateCatchup {
1057 pub fn new(providers: &[Arc<dyn StateCatchup>], local_timeout: Duration) -> Self {
1059 Self {
1060 providers: Arc::new(Mutex::new(providers.to_vec())),
1061 backoff: BackoffParams::disabled(),
1062 local_timeout,
1063 }
1064 }
1065
1066 pub fn add_provider(&self, provider: Arc<dyn StateCatchup>) {
1068 self.providers.lock().push(provider);
1069 }
1070
1071 pub async fn on_local_providers<C, F, RT>(&self, closure: C) -> anyhow::Result<RT>
1076 where
1077 C: Fn(Arc<dyn StateCatchup>) -> F + Clone + Send + Sync + 'static,
1078 F: Future<Output = anyhow::Result<RT>> + Send + 'static,
1079 RT: Send + Sync + 'static,
1080 {
1081 let local_timeout = self.local_timeout;
1082 match timeout(
1083 local_timeout,
1084 self.on_providers(|provider| provider.is_local(), closure),
1085 )
1086 .await
1087 {
1088 Ok(result) => result,
1089 Err(_) => {
1090 let err = format!("local provider timed out after {local_timeout:?}");
1091 tracing::warn!("{err}");
1092 Err(anyhow::anyhow!(err))
1093 },
1094 }
1095 }
1096
1097 pub async fn on_remote_providers<C, F, RT>(&self, closure: C) -> anyhow::Result<RT>
1099 where
1100 C: Fn(Arc<dyn StateCatchup>) -> F + Clone + Send + Sync + 'static,
1101 F: Future<Output = anyhow::Result<RT>> + Send + 'static,
1102 RT: Send + Sync + 'static,
1103 {
1104 self.on_providers(|provider| !provider.is_local(), closure)
1105 .await
1106 }
1107
1108 pub async fn on_providers<P, C, F, RT>(&self, predicate: P, closure: C) -> anyhow::Result<RT>
1110 where
1111 P: Fn(&Arc<dyn StateCatchup>) -> bool + Clone + Send + Sync + 'static,
1112 C: Fn(Arc<dyn StateCatchup>) -> F + Clone + Send + Sync + 'static,
1113 F: Future<Output = anyhow::Result<RT>> + Send + 'static,
1114 RT: Send + Sync + 'static,
1115 {
1116 let providers = self.providers.lock().clone();
1118 if providers.is_empty() {
1119 return Err(anyhow::anyhow!("no providers were initialized"));
1120 }
1121
1122 let providers = providers.into_iter().filter(predicate).collect::<Vec<_>>();
1124 if providers.is_empty() {
1125 return Err(anyhow::anyhow!("no providers matched the given predicate"));
1126 }
1127
1128 let mut futures = FuturesUnordered::new();
1130 for provider in providers {
1131 let closure = closure.clone();
1132 futures.push(AbortOnDropHandle::new(tokio::spawn(closure(provider))));
1133 }
1134
1135 let mut logs = vec![format!("No providers returned a successful result.\n")];
1136 while let Some(result) = futures.next().await {
1138 let result = match result {
1140 Ok(res) => res,
1141 Err(err) => {
1142 tracing::debug!("Failed to join on provider: {err:#}.");
1143 logs.push(format!("Failed to join on provider: {err:#}."));
1144 continue;
1145 },
1146 };
1147
1148 let result = match result {
1150 Ok(res) => res,
1151 Err(err) => {
1152 tracing::debug!("Failed to fetch data: {err:#}.");
1153 logs.push(format!("Failed to fetch data: {err:#}."));
1154 continue;
1155 },
1156 };
1157
1158 return Ok(result);
1159 }
1160
1161 Err(anyhow::anyhow!(logs.join("\n")))
1162 }
1163}
1164
1165macro_rules! clone {
1166 ( ($( $x:ident ),*) $y:expr ) => {
1167 {
1168 $(let $x = $x.clone();)*
1169 $y
1170 }
1171 };
1172}
1173
1174#[async_trait]
1177impl StateCatchup for ParallelStateCatchup {
1178 async fn try_fetch_leaf(
1179 &self,
1180 retry: usize,
1181 coordinator: EpochMembershipCoordinator<SeqTypes>,
1182 height: u64,
1183 ) -> anyhow::Result<Leaf2> {
1184 let local_result = self
1186 .on_local_providers(clone! {(coordinator) move |provider| {
1187 clone!{(coordinator) async move {
1188 provider
1189 .try_fetch_leaf(retry, coordinator, height)
1190 .await
1191 }}
1192 }})
1193 .await;
1194
1195 match &local_result {
1197 Ok(_) => return local_result,
1198 Err(err) => tracing::debug!("{err:#}"),
1199 }
1200
1201 self.on_remote_providers(clone! {(coordinator) move |provider| {
1203 clone!{(coordinator) async move {
1204 provider
1205 .try_fetch_leaf(retry, coordinator, height)
1206 .await
1207 }}
1208 }})
1209 .await
1210 }
1211
1212 async fn try_fetch_accounts(
1213 &self,
1214 retry: usize,
1215 instance: &NodeState,
1216 height: u64,
1217 view: ViewNumber,
1218 fee_merkle_tree_root: FeeMerkleCommitment,
1219 accounts: &[FeeAccount],
1220 ) -> anyhow::Result<Vec<FeeAccountProof>> {
1221 let accounts_vec = accounts.to_vec();
1223 let local_result = self
1224 .on_local_providers(clone! {(instance, accounts_vec) move |provider| {
1225 clone! {(instance, accounts_vec) async move {
1226 provider
1227 .try_fetch_accounts(
1228 retry,
1229 &instance,
1230 height,
1231 view,
1232 fee_merkle_tree_root,
1233 &accounts_vec,
1234 )
1235 .await
1236 }}
1237 }})
1238 .await;
1239
1240 match &local_result {
1242 Ok(_) => return local_result,
1243 Err(err) => tracing::debug!("{err:#}"),
1244 }
1245
1246 self.on_remote_providers(clone! {(instance, accounts_vec) move |provider| {
1248 clone!{(instance, accounts_vec) async move {
1249 provider
1250 .try_fetch_accounts(
1251 retry,
1252 &instance,
1253 height,
1254 view,
1255 fee_merkle_tree_root,
1256 &accounts_vec,
1257 ).await
1258 }}
1259 }})
1260 .await
1261 }
1262
1263 async fn try_remember_blocks_merkle_tree(
1264 &self,
1265 retry: usize,
1266 instance: &NodeState,
1267 height: u64,
1268 view: ViewNumber,
1269 mt: &mut BlockMerkleTree,
1270 ) -> anyhow::Result<()> {
1271 let local_result = self
1273 .on_local_providers(clone! {(mt, instance) move |provider| {
1274 let mut mt = mt.clone();
1275 clone! {(instance) async move {
1276 provider
1278 .try_remember_blocks_merkle_tree(
1279 retry,
1280 &instance,
1281 height,
1282 view,
1283 &mut mt,
1284 )
1285 .await?;
1286
1287 Ok(mt)
1289 }}
1290 }})
1291 .await;
1292
1293 if let Ok(modified_mt) = local_result {
1295 *mt = modified_mt;
1297
1298 return Ok(());
1299 }
1300
1301 let remote_result = self
1303 .on_remote_providers(clone! {(mt, instance) move |provider| {
1304 let mut mt = mt.clone();
1305 clone!{(instance) async move {
1306 provider
1308 .try_remember_blocks_merkle_tree(
1309 retry,
1310 &instance,
1311 height,
1312 view,
1313 &mut mt,
1314 )
1315 .await?;
1316
1317 Ok(mt)
1319 }}
1320 }})
1321 .await?;
1322
1323 *mt = remote_result;
1325
1326 Ok(())
1327 }
1328
1329 async fn try_fetch_chain_config(
1330 &self,
1331 retry: usize,
1332 commitment: Commitment<ChainConfig>,
1333 ) -> anyhow::Result<ChainConfig> {
1334 let local_result = self
1336 .on_local_providers(move |provider| async move {
1337 provider.try_fetch_chain_config(retry, commitment).await
1338 })
1339 .await;
1340
1341 match &local_result {
1343 Ok(_) => return local_result,
1344 Err(err) => tracing::debug!("{err:#}"),
1345 }
1346
1347 self.on_remote_providers(move |provider| async move {
1349 provider.try_fetch_chain_config(retry, commitment).await
1350 })
1351 .await
1352 }
1353
1354 async fn try_fetch_reward_merkle_tree_v2(
1355 &self,
1356 retry: usize,
1357 height: u64,
1358 view: ViewNumber,
1359 reward_merkle_tree_root: RewardMerkleCommitmentV2,
1360 accounts: Arc<Vec<RewardAccountV2>>,
1361 ) -> anyhow::Result<PermittedRewardMerkleTreeV2> {
1362 let local_result = self
1363 .on_local_providers(clone! {(accounts) move |provider| {
1364 clone! {(accounts) async move {
1365 provider
1366 .try_fetch_reward_merkle_tree_v2(
1367 retry,
1368 height,
1369 view,
1370 reward_merkle_tree_root,
1371 accounts,
1372 )
1373 .await
1374 }}
1375 }})
1376 .await;
1377
1378 match &local_result {
1380 Ok(_) => return local_result,
1381 Err(err) => tracing::debug!("{err:#}"),
1382 }
1383
1384 self.on_remote_providers(clone! {(accounts) move |provider| {
1386 clone!{(accounts) async move {
1387 provider
1388 .try_fetch_reward_merkle_tree_v2(
1389 retry,
1390 height,
1391 view,
1392 reward_merkle_tree_root,
1393 accounts
1394 ).await
1395 }}
1396 }})
1397 .await
1398 }
1399
1400 async fn try_fetch_reward_accounts_v1(
1401 &self,
1402 retry: usize,
1403 instance: &NodeState,
1404 height: u64,
1405 view: ViewNumber,
1406 reward_merkle_tree_root: RewardMerkleCommitmentV1,
1407 accounts: &[RewardAccountV1],
1408 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
1409 let accounts_vec = accounts.to_vec();
1411 let local_result = self
1412 .on_local_providers(clone! {(instance, accounts_vec) move |provider| {
1413 clone! {(instance, accounts_vec) async move {
1414 provider
1415 .try_fetch_reward_accounts_v1(
1416 retry,
1417 &instance,
1418 height,
1419 view,
1420 reward_merkle_tree_root,
1421 &accounts_vec,
1422 )
1423 .await
1424 }}
1425 }})
1426 .await;
1427
1428 match &local_result {
1430 Ok(_) => return local_result,
1431 Err(err) => tracing::debug!("{err:#}"),
1432 }
1433
1434 self.on_remote_providers(clone! {(instance, accounts_vec) move |provider| {
1436 clone!{(instance, accounts_vec) async move {
1437 provider
1438 .try_fetch_reward_accounts_v1(
1439 retry,
1440 &instance,
1441 height,
1442 view,
1443 reward_merkle_tree_root,
1444 &accounts_vec,
1445 ).await
1446 }}
1447 }})
1448 .await
1449 }
1450
1451 async fn try_fetch_state_cert(
1452 &self,
1453 retry: usize,
1454 epoch: u64,
1455 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
1456 let local_result = self
1458 .on_local_providers(move |provider| async move {
1459 provider.try_fetch_state_cert(retry, epoch).await
1460 })
1461 .await;
1462
1463 match &local_result {
1465 Ok(_) => return local_result,
1466 Err(err) => tracing::debug!("{err:#}"),
1467 }
1468
1469 self.on_remote_providers(move |provider| async move {
1471 provider.try_fetch_state_cert(retry, epoch).await
1472 })
1473 .await
1474 }
1475
1476 fn backoff(&self) -> &BackoffParams {
1477 &self.backoff
1478 }
1479
1480 fn name(&self) -> String {
1481 format!(
1482 "[{}]",
1483 self.providers
1484 .lock()
1485 .iter()
1486 .map(|p| p.name())
1487 .collect::<Vec<_>>()
1488 .join(", ")
1489 )
1490 }
1491
1492 async fn fetch_accounts(
1493 &self,
1494 instance: &NodeState,
1495 height: u64,
1496 view: ViewNumber,
1497 fee_merkle_tree_root: FeeMerkleCommitment,
1498 accounts: Vec<FeeAccount>,
1499 ) -> anyhow::Result<Vec<FeeAccountProof>> {
1500 let accounts_vec = accounts.to_vec();
1502 let local_result = self
1503 .on_local_providers(clone! {(instance, accounts_vec) move |provider| {
1504 clone! {(instance, accounts_vec) async move {
1505 provider
1506 .try_fetch_accounts(
1507 0,
1508 &instance,
1509 height,
1510 view,
1511 fee_merkle_tree_root,
1512 &accounts_vec,
1513 )
1514 .await
1515 }}
1516 }})
1517 .await;
1518
1519 match &local_result {
1521 Ok(_) => return local_result,
1522 Err(err) => tracing::debug!("{err:#}"),
1523 }
1524
1525 self.on_remote_providers(clone! {(instance, accounts_vec) move |provider| {
1527 clone!{(instance, accounts_vec) async move {
1528 provider
1529 .fetch_accounts(
1530 &instance,
1531 height,
1532 view,
1533 fee_merkle_tree_root,
1534 accounts_vec,
1535 ).await
1536 }}
1537 }})
1538 .await
1539 }
1540
1541 async fn fetch_leaf(
1542 &self,
1543 coordinator: EpochMembershipCoordinator<SeqTypes>,
1544 height: u64,
1545 ) -> anyhow::Result<Leaf2> {
1546 let local_result = self
1548 .on_local_providers(clone! {(coordinator) move |provider| {
1549 clone!{(coordinator) async move {
1550 provider
1551 .try_fetch_leaf(0, coordinator, height)
1552 .await
1553 }}
1554 }})
1555 .await;
1556
1557 match &local_result {
1559 Ok(_) => return local_result,
1560 Err(err) => tracing::debug!("{err:#}"),
1561 }
1562
1563 self.on_remote_providers(clone! {(coordinator) move |provider| {
1565 clone!{(coordinator) async move {
1566 provider
1567 .fetch_leaf(coordinator, height)
1568 .await
1569 }}
1570 }})
1571 .await
1572 }
1573
1574 async fn fetch_chain_config(
1575 &self,
1576 commitment: Commitment<ChainConfig>,
1577 ) -> anyhow::Result<ChainConfig> {
1578 let local_result = self
1580 .on_local_providers(move |provider| async move {
1581 provider.try_fetch_chain_config(0, commitment).await
1582 })
1583 .await;
1584
1585 match &local_result {
1587 Ok(_) => return local_result,
1588 Err(err) => tracing::debug!("{err:#}"),
1589 }
1590
1591 self.on_remote_providers(move |provider| async move {
1593 provider.fetch_chain_config(commitment).await
1594 })
1595 .await
1596 }
1597
1598 async fn fetch_reward_accounts_v1(
1599 &self,
1600 instance: &NodeState,
1601 height: u64,
1602 view: ViewNumber,
1603 reward_merkle_tree_root: RewardMerkleCommitmentV1,
1604 accounts: Vec<RewardAccountV1>,
1605 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
1606 let accounts_vec = accounts.to_vec();
1608 let local_result = self
1609 .on_local_providers(clone! {(instance, accounts_vec) move |provider| {
1610 clone! {(instance, accounts_vec) async move {
1611 provider
1612 .try_fetch_reward_accounts_v1(
1613 0,
1614 &instance,
1615 height,
1616 view,
1617 reward_merkle_tree_root,
1618 &accounts_vec,
1619 )
1620 .await
1621 }}
1622 }})
1623 .await;
1624
1625 match &local_result {
1627 Ok(_) => return local_result,
1628 Err(err) => tracing::debug!("{err:#}"),
1629 }
1630
1631 self.on_remote_providers(clone! {(instance, accounts_vec) move |provider| {
1633 clone!{(instance, accounts_vec) async move {
1634 provider
1635 .fetch_reward_accounts_v1(
1636 &instance,
1637 height,
1638 view,
1639 reward_merkle_tree_root,
1640 accounts_vec,
1641 ).await
1642 }}
1643 }})
1644 .await
1645 }
1646
1647 async fn fetch_state_cert(
1648 &self,
1649 epoch: u64,
1650 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
1651 let local_result = self
1652 .on_local_providers(move |provider| async move {
1653 provider.try_fetch_state_cert(0, epoch).await
1654 })
1655 .await;
1656
1657 match &local_result {
1659 Ok(_) => return local_result,
1660 Err(err) => tracing::debug!("{err:#}"),
1661 }
1662
1663 self.on_remote_providers(
1665 move |provider| async move { provider.fetch_state_cert(epoch).await },
1666 )
1667 .await
1668 }
1669
1670 async fn remember_blocks_merkle_tree(
1671 &self,
1672 instance: &NodeState,
1673 height: u64,
1674 view: ViewNumber,
1675 mt: &mut BlockMerkleTree,
1676 ) -> anyhow::Result<()> {
1677 let local_result = self
1679 .on_local_providers(clone! {(mt, instance) move |provider| {
1680 let mut mt = mt.clone();
1681 clone! {(instance) async move {
1682 provider
1684 .try_remember_blocks_merkle_tree(
1685 0,
1686 &instance,
1687 height,
1688 view,
1689 &mut mt,
1690 )
1691 .await?;
1692
1693 Ok(mt)
1695 }}
1696 }})
1697 .await;
1698
1699 if let Ok(modified_mt) = local_result {
1701 *mt = modified_mt;
1704
1705 return Ok(());
1706 }
1707
1708 let remote_result = self
1710 .on_remote_providers(clone! {(mt, instance) move |provider| {
1711 let mut mt = mt.clone();
1712 clone!{(instance) async move {
1713 provider
1715 .remember_blocks_merkle_tree(
1716 &instance,
1717 height,
1718 view,
1719 &mut mt,
1720 )
1721 .await?;
1722
1723 Ok(mt)
1725 }}
1726 }})
1727 .await?;
1728
1729 *mt = remote_result;
1731
1732 Ok(())
1733 }
1734
1735 fn is_local(&self) -> bool {
1736 self.providers.lock().iter().all(|p| p.is_local())
1737 }
1738}
1739
1740pub async fn add_fee_accounts_to_state<I: hotshot::traits::NodeImplementation<SeqTypes>>(
1743 consensus_handle: &ConsensusHandle<SeqTypes, I>,
1744 view: &ViewNumber,
1745 accounts: &[FeeAccount],
1746 tree: &FeeMerkleTree,
1747 leaf: Leaf2,
1748) -> anyhow::Result<()>
1749where
1750 I::Storage: NewProtocolStorage<SeqTypes>,
1751{
1752 let (existing_state, delta) = consensus_handle.state_and_delta(*view).await;
1753 let (state, delta) = match existing_state {
1754 Some(existing) => {
1755 let mut state = (*existing).clone();
1756 for account in accounts {
1757 if let Some((proof, _)) = FeeAccountProof::prove(tree, (*account).into()) {
1758 if let Err(err) = proof.remember(&mut state.fee_merkle_tree) {
1759 tracing::warn!(
1760 ?view,
1761 %account,
1762 "cannot update fetched account state: {err:#}"
1763 );
1764 }
1765 } else {
1766 tracing::warn!(?view, %account, "cannot update fetched account state because account is not in the merkle tree");
1767 };
1768 }
1769 (Arc::new(state), delta)
1770 },
1771 None => {
1772 let mut state = ValidatedState::from_header(leaf.block_header());
1777 state.fee_merkle_tree = tree.clone();
1778 (Arc::new(state), None)
1779 },
1780 };
1781
1782 consensus_handle
1783 .update_leaf(leaf, state, delta)
1784 .await
1785 .with_context(|| "failed to update leaf")?;
1786
1787 Ok(())
1788}
1789
1790pub async fn add_v2_reward_accounts_to_state<I: hotshot::traits::NodeImplementation<SeqTypes>>(
1793 consensus_handle: &ConsensusHandle<SeqTypes, I>,
1794 view: &ViewNumber,
1795 accounts: &[RewardAccountV2],
1796 tree: &RewardMerkleTreeV2,
1797 leaf: Leaf2,
1798) -> anyhow::Result<()>
1799where
1800 I::Storage: NewProtocolStorage<SeqTypes>,
1801{
1802 let (existing_state, delta) = consensus_handle.state_and_delta(*view).await;
1803 let (state, delta) = match existing_state {
1804 Some(existing) => {
1805 let mut state = (*existing).clone();
1806 for account in accounts {
1807 if let Some((proof, _)) = RewardAccountProofV2::prove(tree, (*account).into()) {
1808 if let Err(err) = proof.remember(&mut state.reward_merkle_tree_v2) {
1809 tracing::warn!(
1810 ?view,
1811 %account,
1812 "cannot update fetched account state: {err:#}"
1813 );
1814 }
1815 } else {
1816 tracing::warn!(?view, %account, "cannot update fetched account state because account is not in the merkle tree");
1817 };
1818 }
1819 (Arc::new(state), delta)
1820 },
1821 None => {
1822 let mut state = ValidatedState::from_header(leaf.block_header());
1827 state.reward_merkle_tree_v2 = tree.clone();
1828 (Arc::new(state), None)
1829 },
1830 };
1831
1832 consensus_handle
1833 .update_leaf(leaf, state, delta)
1834 .await
1835 .with_context(|| "failed to update leaf")?;
1836
1837 Ok(())
1838}
1839
1840pub async fn add_v1_reward_accounts_to_state<I: hotshot::traits::NodeImplementation<SeqTypes>>(
1843 consensus_handle: &ConsensusHandle<SeqTypes, I>,
1844 view: &ViewNumber,
1845 accounts: &[RewardAccountV1],
1846 tree: &RewardMerkleTreeV1,
1847 leaf: Leaf2,
1848) -> anyhow::Result<()>
1849where
1850 I::Storage: NewProtocolStorage<SeqTypes>,
1851{
1852 let (existing_state, delta) = consensus_handle.state_and_delta(*view).await;
1853 let (state, delta) = match existing_state {
1854 Some(existing) => {
1855 let mut state = (*existing).clone();
1856 for account in accounts {
1857 if let Some((proof, _)) = RewardAccountProofV1::prove(tree, (*account).into()) {
1858 if let Err(err) = proof.remember(&mut state.reward_merkle_tree_v1) {
1859 tracing::warn!(
1860 ?view,
1861 %account,
1862 "cannot update fetched account state: {err:#}"
1863 );
1864 }
1865 } else {
1866 tracing::warn!(?view, %account, "cannot update fetched account state because account is not in the merkle tree");
1867 };
1868 }
1869 (Arc::new(state), delta)
1870 },
1871 None => {
1872 let mut state = ValidatedState::from_header(leaf.block_header());
1877 state.reward_merkle_tree_v1 = tree.clone();
1878 (Arc::new(state), None)
1879 },
1880 };
1881
1882 consensus_handle
1883 .update_leaf(leaf, state, delta)
1884 .await
1885 .with_context(|| "failed to update leaf")?;
1886
1887 Ok(())
1888}
1889
1890#[cfg(test)]
1891mod test {
1892 use super::*;
1893
1894 #[test]
1895 fn test_peer_priority() {
1896 let good_peer = PeerScore {
1897 requests: 1000,
1898 failures: 2,
1899 };
1900 let bad_peer = PeerScore {
1901 requests: 10,
1902 failures: 1,
1903 };
1904 assert!(good_peer > bad_peer);
1905
1906 let mut peers: PriorityQueue<_, _> = [(0, good_peer), (1, bad_peer)].into_iter().collect();
1907 assert_eq!(peers.pop(), Some((0, good_peer)));
1908 assert_eq!(peers.pop(), Some((1, bad_peer)));
1909 }
1910}