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_api::routes::v1 as paths;
14use espresso_types::{
15 BackoffParams, BlockMerkleTree, Certificate2, FeeAccount, FeeAccountProof, FeeMerkleCommitment,
16 FeeMerkleTree, Leaf2, NodeState, SeqTypes, ValidatedState,
17 config::PublicNetworkConfig,
18 v0::traits::StateCatchup,
19 v0_3::{
20 ChainConfig, RewardAccountProofV1, RewardAccountV1, RewardMerkleCommitmentV1,
21 RewardMerkleTreeV1,
22 },
23 v0_4::{
24 PermittedRewardMerkleTreeV2, RewardAccountProofV2, RewardAccountV2,
25 RewardMerkleCommitmentV2, RewardMerkleTreeV2, forgotten_accounts_include,
26 },
27};
28use futures::{
29 StreamExt,
30 future::{Future, FutureExt, TryFuture, TryFutureExt},
31 stream::FuturesUnordered,
32};
33use hotshot_new_protocol::{storage::NewProtocolStorage, utils::verify_new_protocol_leaf_chain};
34use hotshot_types::{
35 ValidatorConfig,
36 data::ViewNumber,
37 epoch_membership::EpochMembershipCoordinator,
38 message::UpgradeLock,
39 network::NetworkConfig,
40 simple_certificate::LightClientStateUpdateCertificateV2,
41 traits::{
42 ValidatedState as ValidatedStateTrait,
43 metrics::{Counter, CounterFamily, Metrics},
44 },
45 utils::verify_leaf_chain,
46};
47use http_client::{Request, error::ClientErr};
48use itertools::Itertools;
49use jf_merkle_tree_compat::{ForgetableMerkleTreeScheme, MerkleTreeScheme, prelude::MerkleNode};
50use parking_lot::Mutex;
51use priority_queue::PriorityQueue;
52use serde::de::DeserializeOwned;
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<ApiVer: StaticVersionType> {
68 inner: http_client::Client<ClientErr, ApiVer>,
69 url: Url,
70 requests: Arc<Box<dyn Counter>>,
71 failures: Arc<Box<dyn Counter>>,
72}
73
74impl<ApiVer: StaticVersionType> Client<ApiVer> {
75 pub fn new(
76 url: Url,
77 requests: &(impl CounterFamily + ?Sized),
78 failures: &(impl CounterFamily + ?Sized),
79 ) -> Self {
80 Self {
81 inner: http_client::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, ClientErr, 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<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<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!["Fetching failed.\n".to_string()];
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(&paths::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 verify_leaf_chain(leaf_chain, coordinator, height, &upgrade_lock)
289 .await
290 .with_context(|| format!("failed to verify leaf chain at height {height}"))
291}
292
293#[async_trait]
294impl<ApiVer: StaticVersionType> StateCatchup for StatePeers<ApiVer> {
295 #[tracing::instrument(skip(self, _instance))]
296 async fn try_fetch_accounts(
297 &self,
298 retry: usize,
299 _instance: &NodeState,
300 height: u64,
301 view: ViewNumber,
302 fee_merkle_tree_root: FeeMerkleCommitment,
303 accounts: &[FeeAccount],
304 ) -> anyhow::Result<Vec<FeeAccountProof>> {
305 self.fetch(retry, |client| async move {
306 let tree = client
307 .inner
308 .post::<FeeMerkleTree>(&paths::catchup_accounts(height, view.u64()))
309 .body_binary(&accounts.to_vec())?
310 .send()
311 .await?;
312
313 let mut proofs = Vec::new();
315 for account in accounts {
316 let (proof, _) = FeeAccountProof::prove(&tree, (*account).into())
317 .context(format!("response missing fee account {account}"))?;
318 proof.verify(&fee_merkle_tree_root).context(format!(
319 "invalid proof for fee account {account}, root: {fee_merkle_tree_root}"
320 ))?;
321 proofs.push(proof);
322 }
323
324 anyhow::Ok(proofs)
325 })
326 .await
327 }
328
329 #[tracing::instrument(skip(self, _instance, mt))]
330 async fn try_remember_blocks_merkle_tree(
331 &self,
332 retry: usize,
333 _instance: &NodeState,
334 height: u64,
335 view: ViewNumber,
336 mt: &mut BlockMerkleTree,
337 ) -> anyhow::Result<()> {
338 *mt = self
339 .fetch(retry, |client| {
340 let mut mt = mt.clone();
341 async move {
342 let frontier = client
343 .get::<BlocksFrontier>(&paths::catchup_blocks(height, view.u64()))
344 .send()
345 .await?;
346 let elem = frontier
347 .elem()
348 .context("provided frontier is missing leaf element")?;
349 mt.remember(mt.num_leaves() - 1, *elem, &frontier)
350 .context("verifying block proof")?;
351 anyhow::Ok(mt)
352 }
353 })
354 .await?;
355 Ok(())
356 }
357
358 async fn try_fetch_chain_config(
359 &self,
360 retry: usize,
361 commitment: Commitment<ChainConfig>,
362 ) -> anyhow::Result<ChainConfig> {
363 self.fetch(retry, |client| async move {
364 let cf = client
365 .get::<ChainConfig>(&paths::catchup_chainconfig(commitment))
366 .send()
367 .await?;
368 ensure!(
369 cf.commit() == commitment,
370 "received chain config with mismatched commitment: expected {commitment}, got {}",
371 cf.commit()
372 );
373 Ok(cf)
374 })
375 .await
376 }
377
378 async fn try_fetch_leaf(
379 &self,
380 retry: usize,
381 coordinator: EpochMembershipCoordinator<SeqTypes>,
382 height: u64,
383 ) -> anyhow::Result<Leaf2> {
384 let leaf_chain = self
388 .fetch(retry, |client| async move {
389 let chain = client
390 .get::<Vec<Leaf2>>(&paths::catchup_leafchain(height))
391 .send()
392 .await?;
393 anyhow::Ok(chain)
394 })
395 .await
396 .with_context(|| format!("failed to fetch leaf chain at height {height}"))?;
397
398 let first = leaf_chain
399 .first()
400 .ok_or_else(|| anyhow!("empty leaf chain returned for height {height}"))?;
401
402 if first.block_header().version() >= NEW_PROTOCOL_VERSION {
403 let upgrade_lock =
404 UpgradeLock::<SeqTypes>::new(versions::Upgrade::trivial(NEW_PROTOCOL_VERSION));
405
406 let cert2_height = leaf_chain
409 .last()
410 .ok_or_else(|| anyhow!("empty leaf chain returned for height {height}"))?
411 .height();
412 let cert2 = self
413 .fetch(retry, |client| async move {
414 let cert2 = client
415 .get::<Certificate2<SeqTypes>>(&paths::catchup_cert2(cert2_height))
416 .send()
417 .await?;
418 anyhow::Ok(cert2)
419 })
420 .await
421 .with_context(|| format!("failed to fetch cert2 for height {cert2_height}"))?;
422
423 verify_new_protocol_leaf_chain(leaf_chain, &coordinator, height, &upgrade_lock, cert2)
424 .await
425 .with_context(|| {
426 format!("failed to verify leaf chain with cert2 at height {height}")
427 })
428 } else {
429 verify_legacy_leaf_chain(leaf_chain, &coordinator, height).await
430 }
431 }
432
433 async fn try_fetch_reward_merkle_tree_v2(
434 &self,
435 retry: usize,
436 height: u64,
437 view: ViewNumber,
438 reward_merkle_tree_root: RewardMerkleCommitmentV2,
439 accounts: Arc<Vec<RewardAccountV2>>,
440 ) -> anyhow::Result<PermittedRewardMerkleTreeV2> {
441 let result = self
442 .fetch(retry, |client| async move {
443 let tree_bytes = match client
447 .inner
448 .get::<Vec<u8>>(&paths::catchup_reward_merkle_tree_v2(height, *view))
449 .send()
450 .await
451 {
452 Ok(bytes) => bytes,
453 Err(err) => {
454 tracing::info!(
455 "catchup endpoint failed, falling back to reward-state-v2: {err:#}"
456 );
457 client
458 .inner
459 .get::<Vec<u8>>(&paths::reward_merkle_tree_v2(height))
460 .send()
461 .await?
462 },
463 };
464
465 Ok::<Vec<u8>, anyhow::Error>(tree_bytes)
466 })
467 .await
468 .context("Fetching from peer failed")?;
469
470 let tree_data = bincode::deserialize::<RewardMerkleTreeV2Data>(&result)
471 .context("Failed to deserialize merkle tree from catchup")?;
472
473 let tree: PermittedRewardMerkleTreeV2 =
474 PermittedRewardMerkleTreeV2::try_from_kv_set(tree_data.balances).await?;
475
476 ensure!(
477 tree.tree.commitment() == reward_merkle_tree_root,
478 "RewardMerkleTreeV2 from peer failed commitment check."
479 );
480 ensure!(!forgotten_accounts_include(&tree, &accounts));
481
482 Ok(tree)
483 }
484
485 #[tracing::instrument(skip(self, _instance))]
486 async fn try_fetch_reward_accounts_v1(
487 &self,
488 retry: usize,
489 _instance: &NodeState,
490 height: u64,
491 view: ViewNumber,
492 reward_merkle_tree_root: RewardMerkleCommitmentV1,
493 accounts: &[RewardAccountV1],
494 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
495 self.fetch(retry, |client| async move {
496 let tree = client
497 .inner
498 .post::<RewardMerkleTreeV1>(&paths::catchup_reward_accounts(height, view.u64()))
499 .body_binary(&accounts.to_vec())?
500 .send()
501 .await?;
502
503 let mut proofs = Vec::new();
505 for account in accounts {
506 let (proof, _) = RewardAccountProofV1::prove(&tree, (*account).into())
507 .context(format!("response missing reward account {account}"))?;
508 proof.verify(&reward_merkle_tree_root).context(format!(
509 "invalid proof for v1 reward account {account}, root: \
510 {reward_merkle_tree_root} height {height} view {view}"
511 ))?;
512 proofs.push(proof);
513 }
514
515 anyhow::Ok(proofs)
516 })
517 .await
518 }
519
520 async fn try_fetch_state_cert(
521 &self,
522 retry: usize,
523 epoch: u64,
524 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
525 self.fetch(retry, |client| async move {
526 client
527 .get::<LightClientStateUpdateCertificateV2<SeqTypes>>(&paths::catchup_state_cert(
528 epoch,
529 ))
530 .send()
531 .await
532 })
533 .await
534 }
535
536 fn backoff(&self) -> &BackoffParams {
537 &self.backoff
538 }
539
540 fn name(&self) -> String {
541 format!(
542 "StatePeers({})",
543 self.clients
544 .iter()
545 .map(|client| client.url.to_string())
546 .join(",")
547 )
548 }
549
550 fn is_local(&self) -> bool {
551 false
552 }
553}
554
555pub(crate) trait CatchupStorage: Sync {
556 fn get_accounts(
567 &self,
568 _instance: &NodeState,
569 _height: u64,
570 _view: ViewNumber,
571 _accounts: &[FeeAccount],
572 ) -> impl Send + Future<Output = anyhow::Result<(FeeMerkleTree, Leaf2)>> {
573 async {
578 bail!("merklized state catchup is not supported for this data source");
579 }
580 }
581
582 fn get_reward_accounts_v1(
583 &self,
584 _instance: &NodeState,
585 _height: u64,
586 _view: ViewNumber,
587 _accounts: &[RewardAccountV1],
588 ) -> impl Send + Future<Output = anyhow::Result<(RewardMerkleTreeV1, Leaf2)>> {
589 async {
590 bail!("merklized state catchup is not supported for this data source");
591 }
592 }
593
594 fn get_reward_accounts_v2(
595 &self,
596 _instance: &NodeState,
597 _height: u64,
598 _view: ViewNumber,
599 _accounts: &[RewardAccountV2],
600 ) -> impl Send + Future<Output = anyhow::Result<(RewardMerkleTreeV2, Leaf2)>> {
601 async {
602 bail!("merklized state catchup is not supported for this data source");
603 }
604 }
605
606 fn get_frontier(
613 &self,
614 _instance: &NodeState,
615 _height: u64,
616 _view: ViewNumber,
617 ) -> impl Send + Future<Output = anyhow::Result<BlocksFrontier>> {
618 async {
623 bail!("merklized state catchup is not supported for this data source");
624 }
625 }
626
627 fn get_chain_config(
628 &self,
629 _commitment: Commitment<ChainConfig>,
630 ) -> impl Send + Future<Output = anyhow::Result<ChainConfig>> {
631 async {
632 bail!("chain config catchup is not supported for this data source");
633 }
634 }
635
636 fn get_leaf_chain(
637 &self,
638 _height: u64,
639 ) -> impl Send + Future<Output = anyhow::Result<Vec<Leaf2>>> {
640 async {
641 bail!("leaf chain catchup is not supported for this data source");
642 }
643 }
644
645 fn load_cert2(
647 &self,
648 _height: u64,
649 ) -> impl Send + Future<Output = anyhow::Result<Option<Certificate2<SeqTypes>>>> {
650 async { Ok(None) }
651 }
652
653 fn get_leaf(&self, _height: u64) -> impl Send + Future<Output = anyhow::Result<Leaf2>> {
655 async {
656 bail!("leaf fetch is not supported for this data source");
657 }
658 }
659}
660
661impl CatchupStorage for hotshot_query_service::data_source::MetricsDataSource {}
662
663impl<T, S> CatchupStorage for hotshot_query_service::data_source::ExtensibleDataSource<T, S>
664where
665 T: CatchupStorage,
666 S: Sync,
667{
668 async fn get_accounts(
669 &self,
670 instance: &NodeState,
671 height: u64,
672 view: ViewNumber,
673 accounts: &[FeeAccount],
674 ) -> anyhow::Result<(FeeMerkleTree, Leaf2)> {
675 self.inner()
676 .get_accounts(instance, height, view, accounts)
677 .await
678 }
679
680 async fn get_reward_accounts_v2(
681 &self,
682 instance: &NodeState,
683 height: u64,
684 view: ViewNumber,
685 accounts: &[RewardAccountV2],
686 ) -> anyhow::Result<(RewardMerkleTreeV2, Leaf2)> {
687 self.inner()
688 .get_reward_accounts_v2(instance, height, view, accounts)
689 .await
690 }
691
692 async fn get_reward_accounts_v1(
693 &self,
694 instance: &NodeState,
695 height: u64,
696 view: ViewNumber,
697 accounts: &[RewardAccountV1],
698 ) -> anyhow::Result<(RewardMerkleTreeV1, Leaf2)> {
699 self.inner()
700 .get_reward_accounts_v1(instance, height, view, accounts)
701 .await
702 }
703
704 async fn get_frontier(
705 &self,
706 instance: &NodeState,
707 height: u64,
708 view: ViewNumber,
709 ) -> anyhow::Result<BlocksFrontier> {
710 self.inner().get_frontier(instance, height, view).await
711 }
712
713 async fn get_chain_config(
714 &self,
715 commitment: Commitment<ChainConfig>,
716 ) -> anyhow::Result<ChainConfig> {
717 self.inner().get_chain_config(commitment).await
718 }
719 async fn get_leaf_chain(&self, height: u64) -> anyhow::Result<Vec<Leaf2>> {
720 self.inner().get_leaf_chain(height).await
721 }
722
723 async fn load_cert2(&self, height: u64) -> anyhow::Result<Option<Certificate2<SeqTypes>>> {
724 self.inner().load_cert2(height).await
725 }
726
727 async fn get_leaf(&self, height: u64) -> anyhow::Result<Leaf2> {
728 self.inner().get_leaf(height).await
729 }
730}
731
732#[derive(Debug)]
733pub(crate) struct SqlStateCatchup<T> {
734 db: Arc<T>,
735 backoff: BackoffParams,
736}
737
738impl<T> SqlStateCatchup<T> {
739 pub(crate) fn new(db: Arc<T>, backoff: BackoffParams) -> Self {
740 Self { db, backoff }
741 }
742}
743
744#[async_trait]
745impl<T> StateCatchup for SqlStateCatchup<T>
746where
747 T: CatchupStorage + RewardMerkleTreeDataSource + Send + Sync,
748{
749 async fn try_fetch_leaf(
750 &self,
751 _retry: usize,
752 _coordinator: EpochMembershipCoordinator<SeqTypes>,
753 height: u64,
754 ) -> anyhow::Result<Leaf2> {
755 self.db
758 .get_leaf(height)
759 .await
760 .with_context(|| format!("failed to load leaf at height {height} from DB"))
761 }
762
763 #[tracing::instrument(skip(self, _retry, instance))]
766 async fn try_fetch_accounts(
767 &self,
768 _retry: usize,
769 instance: &NodeState,
770 block_height: u64,
771 view: ViewNumber,
772 fee_merkle_tree_root: FeeMerkleCommitment,
773 accounts: &[FeeAccount],
774 ) -> anyhow::Result<Vec<FeeAccountProof>> {
775 let (fee_merkle_tree_from_db, _) = self
777 .db
778 .get_accounts(instance, block_height, view, accounts)
779 .await
780 .with_context(|| "failed to get fee accounts from DB")?;
781
782 let mut proofs = Vec::new();
784 for account in accounts {
785 let (proof, _) = FeeAccountProof::prove(&fee_merkle_tree_from_db, (*account).into())
786 .context(format!("response missing account {account}"))?;
787 proof.verify(&fee_merkle_tree_root).context(format!(
788 "invalid proof for fee account {account}, root: {fee_merkle_tree_root}"
789 ))?;
790 proofs.push(proof);
791 }
792
793 Ok(proofs)
794 }
795
796 #[tracing::instrument(skip(self, _retry, instance, mt))]
797 async fn try_remember_blocks_merkle_tree(
798 &self,
799 _retry: usize,
800 instance: &NodeState,
801 bh: u64,
802 view: ViewNumber,
803 mt: &mut BlockMerkleTree,
804 ) -> anyhow::Result<()> {
805 if bh == 0 {
806 return Ok(());
807 }
808
809 let proof = self.db.get_frontier(instance, bh, view).await?;
810 match proof
811 .proof
812 .first()
813 .context(format!("empty proof for frontier at height {bh}"))?
814 {
815 MerkleNode::Leaf { pos, elem, .. } => mt
816 .remember(pos, elem, proof.clone())
817 .context("failed to remember proof"),
818 _ => bail!("invalid proof"),
819 }
820 }
821
822 async fn try_fetch_chain_config(
823 &self,
824 _retry: usize,
825 commitment: Commitment<ChainConfig>,
826 ) -> anyhow::Result<ChainConfig> {
827 let cf = self.db.get_chain_config(commitment).await?;
828
829 if cf.commit() != commitment {
830 panic!(
831 "Critical error: Mismatched chain config detected. Expected chain config: {:?}, \
832 but got: {:?}.
833 This may indicate a compromised database",
834 commitment,
835 cf.commit()
836 )
837 }
838
839 Ok(cf)
840 }
841
842 async fn try_fetch_reward_merkle_tree_v2(
843 &self,
844 _retry: usize,
845 height: u64,
846 _view: ViewNumber,
847 reward_merkle_tree_root: RewardMerkleCommitmentV2,
848 accounts: Arc<Vec<RewardAccountV2>>,
849 ) -> anyhow::Result<PermittedRewardMerkleTreeV2> {
850 let tree: PermittedRewardMerkleTreeV2 = self.db.load_reward_merkle_tree_v2(height).await?;
851
852 ensure!(tree.tree.commitment() == reward_merkle_tree_root);
853 ensure!(!forgotten_accounts_include(&tree, &accounts));
854
855 Ok(tree)
856 }
857
858 #[tracing::instrument(skip(self, _retry, instance))]
859 async fn try_fetch_reward_accounts_v1(
860 &self,
861 _retry: usize,
862 instance: &NodeState,
863 block_height: u64,
864 view: ViewNumber,
865 reward_merkle_tree_root: RewardMerkleCommitmentV1,
866 accounts: &[RewardAccountV1],
867 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
868 let (reward_merkle_tree_from_db, _) = self
870 .db
871 .get_reward_accounts_v1(instance, block_height, view, accounts)
872 .await
873 .with_context(|| "failed to get reward accounts from DB")?;
874 let mut proofs = Vec::new();
876 for account in accounts {
877 let (proof, _) =
878 RewardAccountProofV1::prove(&reward_merkle_tree_from_db, (*account).into())
879 .context(format!("response missing account {account}"))?;
880 proof.verify(&reward_merkle_tree_root).context(format!(
881 "invalid proof for v1 reward account {account}, root: {reward_merkle_tree_root}"
882 ))?;
883 proofs.push(proof);
884 }
885
886 Ok(proofs)
887 }
888
889 async fn try_fetch_state_cert(
890 &self,
891 _retry: usize,
892 _epoch: u64,
893 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
894 bail!("state cert catchup not supported for SqlStateCatchup");
895 }
896
897 fn backoff(&self) -> &BackoffParams {
898 &self.backoff
899 }
900
901 fn name(&self) -> String {
902 "SqlStateCatchup".into()
903 }
904
905 fn is_local(&self) -> bool {
906 true
907 }
908}
909
910#[derive(Clone, Debug)]
912pub struct NullStateCatchup {
913 backoff: BackoffParams,
914 chain_configs: HashMap<Commitment<ChainConfig>, ChainConfig>,
915}
916
917impl Default for NullStateCatchup {
918 fn default() -> Self {
919 Self {
920 backoff: BackoffParams::disabled(),
921 chain_configs: Default::default(),
922 }
923 }
924}
925
926impl NullStateCatchup {
927 pub fn add_chain_config(&mut self, cf: ChainConfig) {
937 self.chain_configs.insert(cf.commit(), cf);
938 }
939}
940
941#[async_trait]
942impl StateCatchup for NullStateCatchup {
943 async fn try_fetch_leaf(
944 &self,
945 _retry: usize,
946 _coordinator: EpochMembershipCoordinator<SeqTypes>,
947 _height: u64,
948 ) -> anyhow::Result<Leaf2> {
949 bail!("state catchup is disabled")
950 }
951
952 async fn try_fetch_accounts(
953 &self,
954 _retry: usize,
955 _instance: &NodeState,
956 _height: u64,
957 _view: ViewNumber,
958 _fee_merkle_tree_root: FeeMerkleCommitment,
959 _account: &[FeeAccount],
960 ) -> anyhow::Result<Vec<FeeAccountProof>> {
961 bail!("state catchup is disabled");
962 }
963
964 async fn try_remember_blocks_merkle_tree(
965 &self,
966 _retry: usize,
967 _instance: &NodeState,
968 _height: u64,
969 _view: ViewNumber,
970 _mt: &mut BlockMerkleTree,
971 ) -> anyhow::Result<()> {
972 bail!("state catchup is disabled");
973 }
974
975 async fn try_fetch_chain_config(
976 &self,
977 _retry: usize,
978 commitment: Commitment<ChainConfig>,
979 ) -> anyhow::Result<ChainConfig> {
980 self.chain_configs
981 .get(&commitment)
982 .copied()
983 .context(format!("chain config {commitment} not available"))
984 }
985
986 async fn try_fetch_reward_merkle_tree_v2(
987 &self,
988 _retry: usize,
989 _height: u64,
990 _view: ViewNumber,
991 _reward_merkle_tree_root: RewardMerkleCommitmentV2,
992 _accounts: Arc<Vec<RewardAccountV2>>,
993 ) -> anyhow::Result<PermittedRewardMerkleTreeV2> {
994 bail!("state catchup is disabled");
995 }
996
997 async fn try_fetch_reward_accounts_v1(
998 &self,
999 _retry: usize,
1000 _instance: &NodeState,
1001 _height: u64,
1002 _view: ViewNumber,
1003 _fee_merkle_tree_root: RewardMerkleCommitmentV1,
1004 _account: &[RewardAccountV1],
1005 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
1006 bail!("state catchup is disabled");
1007 }
1008
1009 async fn try_fetch_state_cert(
1010 &self,
1011 _retry: usize,
1012 _epoch: u64,
1013 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
1014 bail!("state catchup is disabled");
1015 }
1016
1017 fn backoff(&self) -> &BackoffParams {
1018 &self.backoff
1019 }
1020
1021 fn name(&self) -> String {
1022 "NullStateCatchup".into()
1023 }
1024
1025 fn is_local(&self) -> bool {
1026 true
1027 }
1028}
1029
1030#[derive(Clone)]
1033pub struct ParallelStateCatchup {
1034 providers: Arc<Mutex<Vec<Arc<dyn StateCatchup>>>>,
1035 backoff: BackoffParams,
1036 local_timeout: Duration,
1038}
1039
1040impl ParallelStateCatchup {
1041 pub fn new(providers: &[Arc<dyn StateCatchup>], local_timeout: Duration) -> Self {
1043 Self {
1044 providers: Arc::new(Mutex::new(providers.to_vec())),
1045 backoff: BackoffParams::disabled(),
1046 local_timeout,
1047 }
1048 }
1049
1050 pub fn add_provider(&self, provider: Arc<dyn StateCatchup>) {
1052 self.providers.lock().push(provider);
1053 }
1054
1055 pub async fn on_local_providers<C, F, RT>(&self, closure: C) -> anyhow::Result<RT>
1060 where
1061 C: Fn(Arc<dyn StateCatchup>) -> F + Clone + Send + Sync + 'static,
1062 F: Future<Output = anyhow::Result<RT>> + Send + 'static,
1063 RT: Send + Sync + 'static,
1064 {
1065 let local_timeout = self.local_timeout;
1066 match timeout(
1067 local_timeout,
1068 self.on_providers(|provider| provider.is_local(), closure),
1069 )
1070 .await
1071 {
1072 Ok(result) => result,
1073 Err(_) => {
1074 let err = format!("local provider timed out after {local_timeout:?}");
1075 tracing::warn!("{err}");
1076 Err(anyhow::anyhow!(err))
1077 },
1078 }
1079 }
1080
1081 pub async fn on_remote_providers<C, F, RT>(&self, closure: C) -> anyhow::Result<RT>
1083 where
1084 C: Fn(Arc<dyn StateCatchup>) -> F + Clone + Send + Sync + 'static,
1085 F: Future<Output = anyhow::Result<RT>> + Send + 'static,
1086 RT: Send + Sync + 'static,
1087 {
1088 self.on_providers(|provider| !provider.is_local(), closure)
1089 .await
1090 }
1091
1092 pub async fn on_providers<P, C, F, RT>(&self, predicate: P, closure: C) -> anyhow::Result<RT>
1094 where
1095 P: Fn(&Arc<dyn StateCatchup>) -> bool + Clone + Send + Sync + 'static,
1096 C: Fn(Arc<dyn StateCatchup>) -> F + Clone + Send + Sync + 'static,
1097 F: Future<Output = anyhow::Result<RT>> + Send + 'static,
1098 RT: Send + Sync + 'static,
1099 {
1100 let providers = self.providers.lock().clone();
1102 if providers.is_empty() {
1103 return Err(anyhow::anyhow!("no providers were initialized"));
1104 }
1105
1106 let providers = providers.into_iter().filter(predicate).collect::<Vec<_>>();
1108 if providers.is_empty() {
1109 return Err(anyhow::anyhow!("no providers matched the given predicate"));
1110 }
1111
1112 let mut futures = FuturesUnordered::new();
1114 for provider in providers {
1115 let closure = closure.clone();
1116 futures.push(AbortOnDropHandle::new(tokio::spawn(closure(provider))));
1117 }
1118
1119 let mut logs = vec!["No providers returned a successful result.\n".to_string()];
1120 while let Some(result) = futures.next().await {
1122 let result = match result {
1124 Ok(res) => res,
1125 Err(err) => {
1126 tracing::debug!("Failed to join on provider: {err:#}.");
1127 logs.push(format!("Failed to join on provider: {err:#}."));
1128 continue;
1129 },
1130 };
1131
1132 let result = match result {
1134 Ok(res) => res,
1135 Err(err) => {
1136 tracing::debug!("Failed to fetch data: {err:#}.");
1137 logs.push(format!("Failed to fetch data: {err:#}."));
1138 continue;
1139 },
1140 };
1141
1142 return Ok(result);
1143 }
1144
1145 Err(anyhow::anyhow!(logs.join("\n")))
1146 }
1147}
1148
1149macro_rules! clone {
1150 ( ($( $x:ident ),*) $y:expr ) => {
1151 {
1152 $(let $x = $x.clone();)*
1153 $y
1154 }
1155 };
1156}
1157
1158#[async_trait]
1161impl StateCatchup for ParallelStateCatchup {
1162 async fn try_fetch_leaf(
1163 &self,
1164 retry: usize,
1165 coordinator: EpochMembershipCoordinator<SeqTypes>,
1166 height: u64,
1167 ) -> anyhow::Result<Leaf2> {
1168 let local_result = self
1170 .on_local_providers(clone! {(coordinator) move |provider| {
1171 clone!{(coordinator) async move {
1172 provider
1173 .try_fetch_leaf(retry, coordinator, height)
1174 .await
1175 }}
1176 }})
1177 .await;
1178
1179 match &local_result {
1181 Ok(_) => return local_result,
1182 Err(err) => tracing::debug!("{err:#}"),
1183 }
1184
1185 self.on_remote_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
1196 async fn try_fetch_accounts(
1197 &self,
1198 retry: usize,
1199 instance: &NodeState,
1200 height: u64,
1201 view: ViewNumber,
1202 fee_merkle_tree_root: FeeMerkleCommitment,
1203 accounts: &[FeeAccount],
1204 ) -> anyhow::Result<Vec<FeeAccountProof>> {
1205 let accounts_vec = accounts.to_vec();
1207 let local_result = self
1208 .on_local_providers(clone! {(instance, accounts_vec) move |provider| {
1209 clone! {(instance, accounts_vec) async move {
1210 provider
1211 .try_fetch_accounts(
1212 retry,
1213 &instance,
1214 height,
1215 view,
1216 fee_merkle_tree_root,
1217 &accounts_vec,
1218 )
1219 .await
1220 }}
1221 }})
1222 .await;
1223
1224 match &local_result {
1226 Ok(_) => return local_result,
1227 Err(err) => tracing::debug!("{err:#}"),
1228 }
1229
1230 self.on_remote_providers(clone! {(instance, accounts_vec) move |provider| {
1232 clone!{(instance, accounts_vec) async move {
1233 provider
1234 .try_fetch_accounts(
1235 retry,
1236 &instance,
1237 height,
1238 view,
1239 fee_merkle_tree_root,
1240 &accounts_vec,
1241 ).await
1242 }}
1243 }})
1244 .await
1245 }
1246
1247 async fn try_remember_blocks_merkle_tree(
1248 &self,
1249 retry: usize,
1250 instance: &NodeState,
1251 height: u64,
1252 view: ViewNumber,
1253 mt: &mut BlockMerkleTree,
1254 ) -> anyhow::Result<()> {
1255 let local_result = self
1257 .on_local_providers(clone! {(mt, instance) move |provider| {
1258 let mut mt = mt.clone();
1259 clone! {(instance) async move {
1260 provider
1262 .try_remember_blocks_merkle_tree(
1263 retry,
1264 &instance,
1265 height,
1266 view,
1267 &mut mt,
1268 )
1269 .await?;
1270
1271 Ok(mt)
1273 }}
1274 }})
1275 .await;
1276
1277 if let Ok(modified_mt) = local_result {
1279 *mt = modified_mt;
1281
1282 return Ok(());
1283 }
1284
1285 let remote_result = self
1287 .on_remote_providers(clone! {(mt, instance) move |provider| {
1288 let mut mt = mt.clone();
1289 clone!{(instance) async move {
1290 provider
1292 .try_remember_blocks_merkle_tree(
1293 retry,
1294 &instance,
1295 height,
1296 view,
1297 &mut mt,
1298 )
1299 .await?;
1300
1301 Ok(mt)
1303 }}
1304 }})
1305 .await?;
1306
1307 *mt = remote_result;
1309
1310 Ok(())
1311 }
1312
1313 async fn try_fetch_chain_config(
1314 &self,
1315 retry: usize,
1316 commitment: Commitment<ChainConfig>,
1317 ) -> anyhow::Result<ChainConfig> {
1318 let local_result = self
1320 .on_local_providers(move |provider| async move {
1321 provider.try_fetch_chain_config(retry, commitment).await
1322 })
1323 .await;
1324
1325 match &local_result {
1327 Ok(_) => return local_result,
1328 Err(err) => tracing::debug!("{err:#}"),
1329 }
1330
1331 self.on_remote_providers(move |provider| async move {
1333 provider.try_fetch_chain_config(retry, commitment).await
1334 })
1335 .await
1336 }
1337
1338 async fn try_fetch_reward_merkle_tree_v2(
1339 &self,
1340 retry: usize,
1341 height: u64,
1342 view: ViewNumber,
1343 reward_merkle_tree_root: RewardMerkleCommitmentV2,
1344 accounts: Arc<Vec<RewardAccountV2>>,
1345 ) -> anyhow::Result<PermittedRewardMerkleTreeV2> {
1346 let local_result = self
1347 .on_local_providers(clone! {(accounts) move |provider| {
1348 clone! {(accounts) async move {
1349 provider
1350 .try_fetch_reward_merkle_tree_v2(
1351 retry,
1352 height,
1353 view,
1354 reward_merkle_tree_root,
1355 accounts,
1356 )
1357 .await
1358 }}
1359 }})
1360 .await;
1361
1362 match &local_result {
1364 Ok(_) => return local_result,
1365 Err(err) => tracing::debug!("{err:#}"),
1366 }
1367
1368 self.on_remote_providers(clone! {(accounts) move |provider| {
1370 clone!{(accounts) async move {
1371 provider
1372 .try_fetch_reward_merkle_tree_v2(
1373 retry,
1374 height,
1375 view,
1376 reward_merkle_tree_root,
1377 accounts
1378 ).await
1379 }}
1380 }})
1381 .await
1382 }
1383
1384 async fn try_fetch_reward_accounts_v1(
1385 &self,
1386 retry: usize,
1387 instance: &NodeState,
1388 height: u64,
1389 view: ViewNumber,
1390 reward_merkle_tree_root: RewardMerkleCommitmentV1,
1391 accounts: &[RewardAccountV1],
1392 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
1393 let accounts_vec = accounts.to_vec();
1395 let local_result = self
1396 .on_local_providers(clone! {(instance, accounts_vec) move |provider| {
1397 clone! {(instance, accounts_vec) async move {
1398 provider
1399 .try_fetch_reward_accounts_v1(
1400 retry,
1401 &instance,
1402 height,
1403 view,
1404 reward_merkle_tree_root,
1405 &accounts_vec,
1406 )
1407 .await
1408 }}
1409 }})
1410 .await;
1411
1412 match &local_result {
1414 Ok(_) => return local_result,
1415 Err(err) => tracing::debug!("{err:#}"),
1416 }
1417
1418 self.on_remote_providers(clone! {(instance, accounts_vec) move |provider| {
1420 clone!{(instance, accounts_vec) async move {
1421 provider
1422 .try_fetch_reward_accounts_v1(
1423 retry,
1424 &instance,
1425 height,
1426 view,
1427 reward_merkle_tree_root,
1428 &accounts_vec,
1429 ).await
1430 }}
1431 }})
1432 .await
1433 }
1434
1435 async fn try_fetch_state_cert(
1436 &self,
1437 retry: usize,
1438 epoch: u64,
1439 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
1440 let local_result = self
1442 .on_local_providers(move |provider| async move {
1443 provider.try_fetch_state_cert(retry, epoch).await
1444 })
1445 .await;
1446
1447 match &local_result {
1449 Ok(_) => return local_result,
1450 Err(err) => tracing::debug!("{err:#}"),
1451 }
1452
1453 self.on_remote_providers(move |provider| async move {
1455 provider.try_fetch_state_cert(retry, epoch).await
1456 })
1457 .await
1458 }
1459
1460 fn backoff(&self) -> &BackoffParams {
1461 &self.backoff
1462 }
1463
1464 fn name(&self) -> String {
1465 format!(
1466 "[{}]",
1467 self.providers
1468 .lock()
1469 .iter()
1470 .map(|p| p.name())
1471 .collect::<Vec<_>>()
1472 .join(", ")
1473 )
1474 }
1475
1476 async fn fetch_accounts(
1477 &self,
1478 instance: &NodeState,
1479 height: u64,
1480 view: ViewNumber,
1481 fee_merkle_tree_root: FeeMerkleCommitment,
1482 accounts: Vec<FeeAccount>,
1483 ) -> anyhow::Result<Vec<FeeAccountProof>> {
1484 let accounts_vec = accounts.to_vec();
1486 let local_result = self
1487 .on_local_providers(clone! {(instance, accounts_vec) move |provider| {
1488 clone! {(instance, accounts_vec) async move {
1489 provider
1490 .try_fetch_accounts(
1491 0,
1492 &instance,
1493 height,
1494 view,
1495 fee_merkle_tree_root,
1496 &accounts_vec,
1497 )
1498 .await
1499 }}
1500 }})
1501 .await;
1502
1503 match &local_result {
1505 Ok(_) => return local_result,
1506 Err(err) => tracing::debug!("{err:#}"),
1507 }
1508
1509 self.on_remote_providers(clone! {(instance, accounts_vec) move |provider| {
1511 clone!{(instance, accounts_vec) async move {
1512 provider
1513 .fetch_accounts(
1514 &instance,
1515 height,
1516 view,
1517 fee_merkle_tree_root,
1518 accounts_vec,
1519 ).await
1520 }}
1521 }})
1522 .await
1523 }
1524
1525 async fn fetch_leaf(
1526 &self,
1527 coordinator: EpochMembershipCoordinator<SeqTypes>,
1528 height: u64,
1529 ) -> anyhow::Result<Leaf2> {
1530 let local_result = self
1532 .on_local_providers(clone! {(coordinator) move |provider| {
1533 clone!{(coordinator) async move {
1534 provider
1535 .try_fetch_leaf(0, coordinator, height)
1536 .await
1537 }}
1538 }})
1539 .await;
1540
1541 match &local_result {
1543 Ok(_) => return local_result,
1544 Err(err) => tracing::debug!("{err:#}"),
1545 }
1546
1547 self.on_remote_providers(clone! {(coordinator) move |provider| {
1549 clone!{(coordinator) async move {
1550 provider
1551 .fetch_leaf(coordinator, height)
1552 .await
1553 }}
1554 }})
1555 .await
1556 }
1557
1558 async fn fetch_chain_config(
1559 &self,
1560 commitment: Commitment<ChainConfig>,
1561 ) -> anyhow::Result<ChainConfig> {
1562 let local_result = self
1564 .on_local_providers(move |provider| async move {
1565 provider.try_fetch_chain_config(0, commitment).await
1566 })
1567 .await;
1568
1569 match &local_result {
1571 Ok(_) => return local_result,
1572 Err(err) => tracing::debug!("{err:#}"),
1573 }
1574
1575 self.on_remote_providers(move |provider| async move {
1577 provider.fetch_chain_config(commitment).await
1578 })
1579 .await
1580 }
1581
1582 async fn fetch_reward_accounts_v1(
1583 &self,
1584 instance: &NodeState,
1585 height: u64,
1586 view: ViewNumber,
1587 reward_merkle_tree_root: RewardMerkleCommitmentV1,
1588 accounts: Vec<RewardAccountV1>,
1589 ) -> anyhow::Result<Vec<RewardAccountProofV1>> {
1590 let accounts_vec = accounts.to_vec();
1592 let local_result = self
1593 .on_local_providers(clone! {(instance, accounts_vec) move |provider| {
1594 clone! {(instance, accounts_vec) async move {
1595 provider
1596 .try_fetch_reward_accounts_v1(
1597 0,
1598 &instance,
1599 height,
1600 view,
1601 reward_merkle_tree_root,
1602 &accounts_vec,
1603 )
1604 .await
1605 }}
1606 }})
1607 .await;
1608
1609 match &local_result {
1611 Ok(_) => return local_result,
1612 Err(err) => tracing::debug!("{err:#}"),
1613 }
1614
1615 self.on_remote_providers(clone! {(instance, accounts_vec) move |provider| {
1617 clone!{(instance, accounts_vec) async move {
1618 provider
1619 .fetch_reward_accounts_v1(
1620 &instance,
1621 height,
1622 view,
1623 reward_merkle_tree_root,
1624 accounts_vec,
1625 ).await
1626 }}
1627 }})
1628 .await
1629 }
1630
1631 async fn fetch_state_cert(
1632 &self,
1633 epoch: u64,
1634 ) -> anyhow::Result<LightClientStateUpdateCertificateV2<SeqTypes>> {
1635 let local_result = self
1636 .on_local_providers(move |provider| async move {
1637 provider.try_fetch_state_cert(0, epoch).await
1638 })
1639 .await;
1640
1641 match &local_result {
1643 Ok(_) => return local_result,
1644 Err(err) => tracing::debug!("{err:#}"),
1645 }
1646
1647 self.on_remote_providers(
1649 move |provider| async move { provider.fetch_state_cert(epoch).await },
1650 )
1651 .await
1652 }
1653
1654 async fn remember_blocks_merkle_tree(
1655 &self,
1656 instance: &NodeState,
1657 height: u64,
1658 view: ViewNumber,
1659 mt: &mut BlockMerkleTree,
1660 ) -> anyhow::Result<()> {
1661 let local_result = self
1663 .on_local_providers(clone! {(mt, instance) move |provider| {
1664 let mut mt = mt.clone();
1665 clone! {(instance) async move {
1666 provider
1668 .try_remember_blocks_merkle_tree(
1669 0,
1670 &instance,
1671 height,
1672 view,
1673 &mut mt,
1674 )
1675 .await?;
1676
1677 Ok(mt)
1679 }}
1680 }})
1681 .await;
1682
1683 if let Ok(modified_mt) = local_result {
1685 *mt = modified_mt;
1688
1689 return Ok(());
1690 }
1691
1692 let remote_result = self
1694 .on_remote_providers(clone! {(mt, instance) move |provider| {
1695 let mut mt = mt.clone();
1696 clone!{(instance) async move {
1697 provider
1699 .remember_blocks_merkle_tree(
1700 &instance,
1701 height,
1702 view,
1703 &mut mt,
1704 )
1705 .await?;
1706
1707 Ok(mt)
1709 }}
1710 }})
1711 .await?;
1712
1713 *mt = remote_result;
1715
1716 Ok(())
1717 }
1718
1719 fn is_local(&self) -> bool {
1720 self.providers.lock().iter().all(|p| p.is_local())
1721 }
1722}
1723
1724pub async fn add_fee_accounts_to_state<I: hotshot::traits::NodeImplementation<SeqTypes>>(
1727 consensus_handle: &ConsensusHandle<SeqTypes, I>,
1728 view: &ViewNumber,
1729 accounts: &[FeeAccount],
1730 tree: &FeeMerkleTree,
1731 leaf: Leaf2,
1732) -> anyhow::Result<()>
1733where
1734 I::Storage: NewProtocolStorage<SeqTypes>,
1735{
1736 let (existing_state, delta) = consensus_handle.state_and_delta(*view).await;
1737 let (state, delta) = match existing_state {
1738 Some(existing) => {
1739 let mut state = (*existing).clone();
1740 for account in accounts {
1741 if let Some((proof, _)) = FeeAccountProof::prove(tree, (*account).into()) {
1742 if let Err(err) = proof.remember(&mut state.fee_merkle_tree) {
1743 tracing::warn!(
1744 ?view,
1745 %account,
1746 "cannot update fetched account state: {err:#}"
1747 );
1748 }
1749 } else {
1750 tracing::warn!(?view, %account, "cannot update fetched account state because account is not in the merkle tree");
1751 };
1752 }
1753 (Arc::new(state), delta)
1754 },
1755 None => {
1756 let mut state = ValidatedState::from_header(leaf.block_header());
1761 state.fee_merkle_tree = tree.clone();
1762 (Arc::new(state), None)
1763 },
1764 };
1765
1766 consensus_handle
1767 .update_leaf(leaf, state, delta)
1768 .await
1769 .with_context(|| "failed to update leaf")?;
1770
1771 Ok(())
1772}
1773
1774pub async fn add_v2_reward_accounts_to_state<I: hotshot::traits::NodeImplementation<SeqTypes>>(
1777 consensus_handle: &ConsensusHandle<SeqTypes, I>,
1778 view: &ViewNumber,
1779 accounts: &[RewardAccountV2],
1780 tree: &RewardMerkleTreeV2,
1781 leaf: Leaf2,
1782) -> anyhow::Result<()>
1783where
1784 I::Storage: NewProtocolStorage<SeqTypes>,
1785{
1786 let (existing_state, delta) = consensus_handle.state_and_delta(*view).await;
1787 let (state, delta) = match existing_state {
1788 Some(existing) => {
1789 let mut state = (*existing).clone();
1790 for account in accounts {
1791 if let Some((proof, _)) = RewardAccountProofV2::prove(tree, (*account).into()) {
1792 if let Err(err) = proof.remember(&mut state.reward_merkle_tree_v2) {
1793 tracing::warn!(
1794 ?view,
1795 %account,
1796 "cannot update fetched account state: {err:#}"
1797 );
1798 }
1799 } else {
1800 tracing::warn!(?view, %account, "cannot update fetched account state because account is not in the merkle tree");
1801 };
1802 }
1803 (Arc::new(state), delta)
1804 },
1805 None => {
1806 let mut state = ValidatedState::from_header(leaf.block_header());
1811 state.reward_merkle_tree_v2 = tree.clone();
1812 (Arc::new(state), None)
1813 },
1814 };
1815
1816 consensus_handle
1817 .update_leaf(leaf, state, delta)
1818 .await
1819 .with_context(|| "failed to update leaf")?;
1820
1821 Ok(())
1822}
1823
1824pub async fn add_v1_reward_accounts_to_state<I: hotshot::traits::NodeImplementation<SeqTypes>>(
1827 consensus_handle: &ConsensusHandle<SeqTypes, I>,
1828 view: &ViewNumber,
1829 accounts: &[RewardAccountV1],
1830 tree: &RewardMerkleTreeV1,
1831 leaf: Leaf2,
1832) -> anyhow::Result<()>
1833where
1834 I::Storage: NewProtocolStorage<SeqTypes>,
1835{
1836 let (existing_state, delta) = consensus_handle.state_and_delta(*view).await;
1837 let (state, delta) = match existing_state {
1838 Some(existing) => {
1839 let mut state = (*existing).clone();
1840 for account in accounts {
1841 if let Some((proof, _)) = RewardAccountProofV1::prove(tree, (*account).into()) {
1842 if let Err(err) = proof.remember(&mut state.reward_merkle_tree_v1) {
1843 tracing::warn!(
1844 ?view,
1845 %account,
1846 "cannot update fetched account state: {err:#}"
1847 );
1848 }
1849 } else {
1850 tracing::warn!(?view, %account, "cannot update fetched account state because account is not in the merkle tree");
1851 };
1852 }
1853 (Arc::new(state), delta)
1854 },
1855 None => {
1856 let mut state = ValidatedState::from_header(leaf.block_header());
1861 state.reward_merkle_tree_v1 = tree.clone();
1862 (Arc::new(state), None)
1863 },
1864 };
1865
1866 consensus_handle
1867 .update_leaf(leaf, state, delta)
1868 .await
1869 .with_context(|| "failed to update leaf")?;
1870
1871 Ok(())
1872}
1873
1874#[cfg(test)]
1875mod test {
1876 use super::*;
1877
1878 #[test]
1879 fn test_peer_priority() {
1880 let good_peer = PeerScore {
1881 requests: 1000,
1882 failures: 2,
1883 };
1884 let bad_peer = PeerScore {
1885 requests: 10,
1886 failures: 1,
1887 };
1888 assert!(good_peer > bad_peer);
1889
1890 let mut peers: PriorityQueue<_, _> = [(0, good_peer), (1, bad_peer)].into_iter().collect();
1891 assert_eq!(peers.pop(), Some((0, good_peer)));
1892 assert_eq!(peers.pop(), Some((1, bad_peer)));
1893 }
1894}