1use std::{
6 cmp::{Ordering, Reverse},
7 collections::{BTreeMap, BTreeSet, HashSet},
8 slice,
9 sync::{Arc, Mutex, RwLock},
10};
11
12use custom_debug_derive::Debug;
13use futures::{
14 future::{Future, TryFutureExt as _},
15 stream::{self, AbortHandle, FuturesOrdered, FuturesUnordered, StreamExt},
16};
17#[cfg(with_metrics)]
18use linera_base::prometheus_util::MeasureLatency as _;
19use linera_base::{
20 crypto::{CryptoHash, Signer as _, ValidatorPublicKey},
21 data_types::{
22 ApplicationDescription, ArithmeticError, Blob, BlockHeight, ChainDescription, Epoch, Round,
23 TimeDelta, Timestamp,
24 },
25 ensure,
26 hashed::Hashed,
27 identifiers::{AccountOwner, ApplicationId, BlobId, BlobType, ChainId, EventId, StreamId},
28 time::Duration,
29};
30#[cfg(not(target_arch = "wasm32"))]
31use linera_base::{data_types::Bytecode, identifiers::ModuleId, vm::VmRuntime};
32use linera_chain::{
33 data_types::{
34 BlockExecutionOutcome, BlockProposal, BundleExecutionPolicy, ChainAndHeight, LiteVote,
35 OriginalProposal, ProposedBlock,
36 },
37 justification::JustificationChain,
38 manager::LockingBlock,
39 types::{
40 Block, CertificateValue, Certified, ConfirmedBlock, ConfirmedBlockCertificate,
41 GenericCertificate, LiteCertificate, Timeout, ValidatedBlock, ValidatedBlockCertificate,
42 },
43 ChainError, ChainIdSet,
44};
45use linera_execution::{committee::Committee, ExecutionError};
46use linera_storage::{Arc as CacheArc, Clock as _, ResultReadCertificates, Storage as _};
47use rand::seq::SliceRandom;
48use received_log::ReceivedLogs;
49use serde::{Deserialize, Serialize};
50use tokio::sync::mpsc;
51use tracing::{debug, error, info, instrument, trace, warn};
52
53use crate::{
54 data_types::{ChainInfo, ChainInfoQuery, ChainInfoResponse},
55 environment::Environment,
56 local_node::{LocalNodeClient, LocalNodeError},
57 node::{CrossChainMessageDelivery, NodeError, ValidatorNode as _, ValidatorNodeProvider as _},
58 notifier::{ChannelNotifier, Notifier as _},
59 remote_node::RemoteNode,
60 updater::{communicate_with_quorum, CommunicateAction, RemoteNodeUpdater},
61 worker::{Notification, ProcessableCertificate, Reason, WorkerError, WorkerState},
62 ChainWorkerConfig, ProcessConfirmedBlockMode, CHAIN_INFO_MAX_RECEIVED_LOG_ENTRIES,
63};
64
65pub mod chain_client;
67pub use chain_client::ChainClient;
68
69pub use crate::data_types::ClientOutcome;
70
71#[cfg(test)]
72#[path = "../unit_tests/client_tests.rs"]
73mod client_tests;
74pub mod requests_scheduler;
75
76pub use requests_scheduler::{RequestsScheduler, RequestsSchedulerConfig, ScoringWeights};
77mod received_log;
78mod validator_trackers;
79
80#[cfg(with_metrics)]
81pub(crate) mod metrics {
82 use linera_base::prometheus_util::{
83 exponential_bucket_interval, exponential_bucket_latencies, register_histogram_vec,
84 register_int_counter, register_int_counter_vec,
85 };
86 use prometheus::{HistogramVec, IntCounter, IntCounterVec};
87
88 linera_base::declare_metrics! {
89 pub static PROCESS_INBOX_WITHOUT_PREPARE_LATENCY: HistogramVec =
90 register_histogram_vec(
91 "process_inbox_latency",
92 "process_inbox latency",
93 &[],
94 exponential_bucket_latencies(60_000.0),
95 );
96
97 pub static PREPARE_CHAIN_LATENCY: HistogramVec =
98 register_histogram_vec(
99 "prepare_chain_latency",
100 "prepare_chain latency",
101 &[],
102 exponential_bucket_latencies(60_000.0),
103 );
104
105 pub static SYNCHRONIZE_CHAIN_STATE_LATENCY: HistogramVec =
106 register_histogram_vec(
107 "synchronize_chain_state_latency",
108 "synchronize_chain_state latency",
109 &[],
110 exponential_bucket_latencies(600_000.0),
111 );
112
113 pub static EXECUTE_BLOCK_LATENCY: HistogramVec =
114 register_histogram_vec(
115 "execute_block_latency",
116 "execute_block latency",
117 &[],
118 exponential_bucket_latencies(10_000.0),
119 );
120
121 pub static FIND_RECEIVED_CERTIFICATES_LATENCY: HistogramVec =
122 register_histogram_vec(
123 "find_received_certificates_latency",
124 "find_received_certificates latency",
125 &[],
126 exponential_bucket_latencies(3_600_000.0),
127 );
128
129 pub static FIND_RECEIVED_CERTIFICATES_LOG_ENTRIES: HistogramVec =
130 register_histogram_vec(
131 "find_received_certificates_log_entries",
132 "Number of received-log entries collected from the validators, per call",
133 &[],
134 exponential_bucket_interval(1.0, 1_000_000.0),
135 );
136
137 pub static FIND_RECEIVED_CERTIFICATES_SENDER_CHAINS: HistogramVec =
138 register_histogram_vec(
139 "find_received_certificates_sender_chains",
140 "Number of distinct sender chains to synchronize, per call",
141 &[],
142 exponential_bucket_interval(1.0, 100_000.0),
143 );
144
145 pub static SENDER_CERTIFICATES_DISCOVERED_TOTAL: IntCounter =
146 register_int_counter(
147 "sender_certificates_discovered_total",
148 "Total number of sender certificates advertised by the validators' received logs",
149 );
150
151 pub static SENDER_CERTIFICATES_MISSING_TOTAL: IntCounter =
152 register_int_counter(
153 "sender_certificates_missing_total",
154 "Total number of sender certificates not already known locally, hence downloaded",
155 );
156
157 pub static BLOCK_STAGING_FAILURES_TOTAL: IntCounterVec =
158 register_int_counter_vec(
159 "block_staging_failures_total",
160 "Total number of client block staging (execute_block) failures, labelled by error type",
161 &["error_type"],
162 );
163 }
164}
165
166pub static DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE: u64 = 500;
168pub static DEFAULT_CERTIFICATE_UPLOAD_BATCH_SIZE: usize = 500;
170pub static DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE: usize = 20_000;
172pub static DEFAULT_MAX_EVENT_STREAM_QUERIES: usize = 1000;
174pub static DEFAULT_MAX_CONCURRENT_BATCH_DOWNLOADS: usize = 1;
176
177#[derive(Debug, Clone, Copy)]
179#[allow(missing_docs)]
180pub enum TimingType {
181 ExecuteOperations,
182 ExecuteBlock,
183 SubmitBlockProposal,
184 UpdateValidators,
185}
186
187#[derive(Debug, Clone, PartialEq, Eq)]
192pub enum ListeningMode {
193 FullChain,
196 FollowChain,
200 EventsOnly(BTreeSet<StreamId>),
202}
203
204impl PartialOrd for ListeningMode {
205 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
206 match (self, other) {
207 (ListeningMode::FullChain, ListeningMode::FullChain) => Some(Ordering::Equal),
208 (ListeningMode::FullChain, _) => Some(Ordering::Greater),
209 (_, ListeningMode::FullChain) => Some(Ordering::Less),
210 (ListeningMode::FollowChain, ListeningMode::FollowChain) => Some(Ordering::Equal),
211 (ListeningMode::FollowChain, ListeningMode::EventsOnly(_)) => Some(Ordering::Greater),
212 (ListeningMode::EventsOnly(_), ListeningMode::FollowChain) => Some(Ordering::Less),
213 (ListeningMode::EventsOnly(a), ListeningMode::EventsOnly(b)) => {
214 if a == b {
215 Some(Ordering::Equal)
216 } else if a.is_superset(b) {
217 Some(Ordering::Greater)
218 } else if b.is_superset(a) {
219 Some(Ordering::Less)
220 } else {
221 None
222 }
223 }
224 }
225 }
226}
227
228impl ListeningMode {
229 pub fn is_relevant(&self, reason: &Reason) -> bool {
232 match (reason, self) {
233 (Reason::NewEvents { .. }, ListeningMode::FollowChain | ListeningMode::FullChain) => {
236 false
237 }
238 (_, ListeningMode::FullChain) => true,
240 (Reason::NewBlock { .. }, ListeningMode::FollowChain) => true,
243 (_, ListeningMode::FollowChain) => false,
244 (Reason::NewEvents { event_streams, .. }, ListeningMode::EventsOnly(relevant)) => {
246 relevant.intersection(event_streams).next().is_some()
247 }
248 (_, ListeningMode::EventsOnly(_)) => false,
249 }
250 }
251
252 pub fn extend(&mut self, other: Option<ListeningMode>) {
254 match (self, other) {
255 (_, None) => (),
256 (ListeningMode::FullChain, _) => (),
257 (mode, Some(ListeningMode::FullChain)) => {
258 *mode = ListeningMode::FullChain;
259 }
260 (ListeningMode::FollowChain, _) => (),
261 (mode, Some(ListeningMode::FollowChain)) => {
262 *mode = ListeningMode::FollowChain;
263 }
264 (
265 ListeningMode::EventsOnly(self_events),
266 Some(ListeningMode::EventsOnly(other_events)),
267 ) => {
268 self_events.extend(other_events);
269 }
270 }
271 }
272
273 pub fn is_follow_only(&self) -> bool {
276 !matches!(self, ListeningMode::FullChain)
277 }
278
279 pub fn is_full(&self) -> bool {
282 matches!(self, ListeningMode::FullChain)
283 }
284
285 pub fn should_sync_chain_state(&self) -> bool {
287 match self {
288 ListeningMode::FullChain | ListeningMode::FollowChain => true,
289 ListeningMode::EventsOnly(_) => false,
290 }
291 }
292}
293
294#[derive(Debug)]
304pub struct ChainModes {
305 modes: BTreeMap<ChainId, ListeningMode>,
306 full: Arc<Hashed<ChainIdSet>>,
307}
308
309impl Default for ChainModes {
310 fn default() -> Self {
311 Self::new(BTreeMap::new())
312 }
313}
314
315impl ChainModes {
316 pub fn new(modes: BTreeMap<ChainId, ListeningMode>) -> Self {
318 let full = Self::compute_full(&modes);
319 Self { modes, full }
320 }
321
322 fn compute_full(modes: &BTreeMap<ChainId, ListeningMode>) -> Arc<Hashed<ChainIdSet>> {
323 Arc::new(Hashed::new(ChainIdSet(
324 modes
325 .iter()
326 .filter(|(_, mode)| mode.is_full())
327 .map(|(id, _)| *id)
328 .collect(),
329 )))
330 }
331
332 pub fn full(&self) -> Arc<Hashed<ChainIdSet>> {
334 self.full.clone()
335 }
336
337 pub fn get(&self, chain_id: &ChainId) -> Option<&ListeningMode> {
339 self.modes.get(chain_id)
340 }
341
342 pub fn extend_mode(&mut self, chain_id: ChainId, mode: ListeningMode) -> ListeningMode {
346 let entry = self
347 .modes
348 .entry(chain_id)
349 .or_insert_with(|| ListeningMode::EventsOnly(BTreeSet::new()));
350 let was_full = entry.is_full();
351 entry.extend(Some(mode));
352 let result = entry.clone();
353 if !was_full && result.is_full() {
354 self.full = Self::compute_full(&self.modes);
355 }
356 result
357 }
358
359 pub fn remove_mode(&mut self, chain_id: &ChainId) -> Option<ListeningMode> {
362 let removed = self.modes.remove(chain_id)?;
363 if removed.is_full() {
364 self.full = Self::compute_full(&self.modes);
365 }
366 Some(removed)
367 }
368}
369
370pub struct Client<Env: Environment> {
372 environment: Env,
373 pub local_node: LocalNodeClient<Env::Storage>,
376 requests_scheduler: Arc<RequestsScheduler<Env>>,
378 admin_chain_id: ChainId,
380 chain_modes: Arc<RwLock<ChainModes>>,
383 notifier: Arc<ChannelNotifier<Notification>>,
385 chains: papaya::HashMap<ChainId, chain_client::State>,
387 options: chain_client::Options,
389}
390
391#[cfg(not(web))]
395type ReceiveSenderCertificateFuture<'a> =
396 std::pin::Pin<Box<dyn Future<Output = Result<(), chain_client::Error>> + Send + 'a>>;
397#[cfg(web)]
398type ReceiveSenderCertificateFuture<'a> =
399 std::pin::Pin<Box<dyn Future<Output = Result<(), chain_client::Error>> + 'a>>;
400
401impl<Env: Environment> Client<Env> {
402 #[instrument(level = "trace", skip_all)]
404 #[expect(clippy::too_many_arguments)]
405 pub fn new(
406 environment: Env,
407 admin_chain_id: ChainId,
408 long_lived_services: bool,
409 chain_modes: impl IntoIterator<Item = (ChainId, ListeningMode)>,
410 name: impl Into<String>,
411 chain_worker_ttl: Option<Duration>,
412 sender_chain_worker_ttl: Option<Duration>,
413 cross_chain_batch_size_limit: usize,
414 options: chain_client::Options,
415 block_cache_size: usize,
416 execution_state_cache_size: usize,
417 requests_scheduler_config: &requests_scheduler::RequestsSchedulerConfig,
418 ) -> Self {
419 let mut modes = chain_modes.into_iter().collect::<BTreeMap<_, _>>();
420 modes
424 .entry(admin_chain_id)
425 .or_insert(ListeningMode::FullChain)
426 .extend(Some(ListeningMode::FullChain));
427 let chain_modes = Arc::new(RwLock::new(ChainModes::new(modes)));
428 let config = ChainWorkerConfig {
429 nickname: name.into(),
430 long_lived_services,
431 allow_inactive_chains: true,
432 ttl: chain_worker_ttl,
433 sender_chain_ttl: sender_chain_worker_ttl,
434 block_cache_size,
435 execution_state_cache_size,
436 cross_chain_batch_size_limit,
437 ..ChainWorkerConfig::default()
438 };
439 let state = WorkerState::new(
440 environment.storage().clone(),
441 config,
442 Some(chain_modes.clone()),
443 );
444 let clock = environment.storage().clock().clone();
445 let local_node = LocalNodeClient::new(state);
446 let requests_scheduler = Arc::new(RequestsScheduler::new(
447 vec![],
448 requests_scheduler_config,
449 clock,
450 ));
451
452 Self {
453 environment,
454 local_node,
455 requests_scheduler,
456 chains: papaya::HashMap::new(),
457 admin_chain_id,
458 chain_modes,
459 notifier: Arc::new(ChannelNotifier::default()),
460 options,
461 }
462 }
463
464 pub fn admin_chain_id(&self) -> ChainId {
466 self.admin_chain_id
467 }
468
469 pub fn subscribe(
471 &self,
472 chain_ids: Vec<ChainId>,
473 ) -> tokio::sync::mpsc::UnboundedReceiver<Notification> {
474 self.notifier.subscribe(chain_ids)
475 }
476
477 pub fn subscribe_extra(
479 &self,
480 chain_ids: Vec<ChainId>,
481 sender: &tokio::sync::mpsc::UnboundedSender<Notification>,
482 ) {
483 self.notifier.add_sender(chain_ids, sender);
484 }
485
486 pub fn storage_client(&self) -> &Env::Storage {
488 self.environment.storage()
489 }
490
491 async fn try_read_local_certificate(
494 &self,
495 chain_id: ChainId,
496 height: BlockHeight,
497 hash: Option<CryptoHash>,
498 ) -> Result<Option<CacheArc<ConfirmedBlockCertificate>>, chain_client::Error> {
499 if let Some(hash) = hash {
500 return Ok(self.storage_client().read_certificate(hash).await?);
501 }
502 let results = self
503 .storage_client()
504 .read_certificates_by_heights(chain_id, &[height])
505 .await?;
506 Ok(results.into_iter().next().flatten())
507 }
508
509 pub fn validator_node_provider(&self) -> &Env::Network {
511 self.environment.network()
512 }
513
514 pub async fn retry_pending_cross_chain_requests(
516 &self,
517 sender_chain: ChainId,
518 ) -> Result<(), LocalNodeError> {
519 self.local_node
520 .retry_pending_cross_chain_requests(sender_chain, &self.notifier)
521 .await
522 }
523
524 #[instrument(level = "trace", skip(self))]
526 pub fn signer(&self) -> &Env::Signer {
527 self.environment.signer()
528 }
529
530 pub async fn has_key_for(&self, owner: &AccountOwner) -> Result<bool, chain_client::Error> {
532 self.signer()
533 .contains_key(owner)
534 .await
535 .map_err(chain_client::Error::signer_failure)
536 }
537
538 pub fn wallet(&self) -> &Env::Wallet {
540 self.environment.wallet()
541 }
542
543 #[instrument(level = "trace", skip(self))]
546 pub fn extend_chain_mode(&self, chain_id: ChainId, mode: ListeningMode) -> ListeningMode {
547 self.chain_modes
548 .write()
549 .expect("Panics should not happen while holding a lock to `chain_modes`")
550 .extend_mode(chain_id, mode)
551 }
552
553 #[instrument(level = "trace", skip(self))]
555 pub fn remove_chain_mode(&self, chain_id: ChainId) -> Option<ListeningMode> {
556 self.chain_modes
557 .write()
558 .expect("Panics should not happen while holding a lock to `chain_modes`")
559 .remove_mode(&chain_id)
560 }
561
562 pub fn chain_mode(&self, chain_id: ChainId) -> Option<ListeningMode> {
564 self.chain_modes
565 .read()
566 .expect("Panics should not happen while holding a lock to `chain_modes`")
567 .get(&chain_id)
568 .cloned()
569 }
570
571 pub fn is_tracked(&self, chain_id: ChainId) -> bool {
573 self.chain_modes
574 .read()
575 .expect("Panics should not happen while holding a lock to `chain_modes`")
576 .get(&chain_id)
577 .is_some_and(ListeningMode::is_full)
578 }
579
580 #[expect(clippy::too_many_arguments)]
582 #[instrument(level = "trace", skip_all, fields(chain_id, next_block_height))]
583 pub fn create_chain_client(
584 self: &Arc<Self>,
585 chain_id: ChainId,
586 block_hash: Option<CryptoHash>,
587 next_block_height: BlockHeight,
588 pending_proposal: &Option<PendingProposal>,
589 preferred_owner: Option<AccountOwner>,
590 timing_sender: Option<mpsc::UnboundedSender<(u64, TimingType)>>,
591 follow_only: bool,
592 ) -> ChainClient<Env> {
593 self.chains.pin().get_or_insert_with(chain_id, || {
596 chain_client::State::new(pending_proposal.clone(), follow_only)
597 });
598
599 ChainClient::new(
600 self.clone(),
601 chain_id,
602 self.options.clone(),
603 block_hash,
604 next_block_height,
605 preferred_owner,
606 timing_sender,
607 )
608 }
609
610 fn is_chain_follow_only(&self, chain_id: ChainId) -> bool {
612 self.chains
613 .pin()
614 .get(&chain_id)
615 .is_some_and(|state| state.is_follow_only())
616 }
617
618 pub fn set_chain_follow_only(&self, chain_id: ChainId, follow_only: bool) {
620 self.chains
621 .pin()
622 .update(chain_id, |state| state.with_follow_only(follow_only));
623 }
624
625 async fn fetch_chain_info(
627 &self,
628 chain_id: ChainId,
629 validators: &[RemoteNode<Env::ValidatorNode>],
630 ) -> Result<Box<ChainInfo>, chain_client::Error> {
631 match self.local_node.chain_info(chain_id).await {
632 Ok(info) => Ok(info),
633 Err(LocalNodeError::BlobsNotFound(blob_ids)) => {
634 self.synchronize_chain_state(self.admin_chain_id).await?;
637 self.update_local_node_with_blobs_from(blob_ids, validators)
638 .await?;
639 Ok(self.local_node.chain_info(chain_id).await?)
640 }
641 Err(err) => Err(err.into()),
642 }
643 }
644
645 #[instrument(level = "trace", skip(self))]
647 async fn download_certificates(
648 &self,
649 chain_id: ChainId,
650 target_next_block_height: BlockHeight,
651 ) -> Result<Box<ChainInfo>, chain_client::Error> {
652 let validators = self.validator_nodes().await?;
653 let mut info = Box::pin(self.fetch_chain_info(chain_id, &validators)).await?;
654 if target_next_block_height <= info.next_block_height {
655 return Ok(info);
656 }
657 info = self
658 .load_local_certificates(chain_id, target_next_block_height, None)
659 .await?;
660 let mut next_height = info.next_block_height;
661 while next_height < target_next_block_height {
663 let limit = u64::from(target_next_block_height)
664 .checked_sub(u64::from(next_height))
665 .ok_or(ArithmeticError::Overflow)?
666 .min(self.options.certificate_download_batch_size);
667 let certificates = self
668 .requests_scheduler
669 .download_certificates_from_validators(
670 &validators,
671 chain_id,
672 next_height,
673 limit,
674 self.options.certificate_batch_download_hedge_delay,
675 )
676 .await?;
677 let Some(new_info) = self
678 .process_certificates(
679 &validators,
680 certificates,
681 None,
682 ProcessConfirmedBlockMode::Execute,
683 )
684 .await?
685 else {
686 break;
687 };
688 assert!(new_info.next_block_height > next_height);
689 next_height = new_info.next_block_height;
690 info = new_info;
691 }
692 ensure!(
693 target_next_block_height <= info.next_block_height,
694 chain_client::Error::CannotDownloadCertificates {
695 chain_id,
696 target_next_block_height,
697 }
698 );
699 Ok(info)
700 }
701
702 async fn load_local_certificates(
707 &self,
708 chain_id: ChainId,
709 end: BlockHeight,
710 until_block_time: Option<Timestamp>,
711 ) -> Result<Box<ChainInfo>, chain_client::Error> {
712 let mut last_info = self.local_node.chain_info(chain_id).await?;
713 let next_height = last_info.next_block_height;
714 let hashes = self
715 .local_node
716 .get_preprocessed_block_hashes(chain_id, next_height, end)
717 .await?;
718 let certificates = self.storage_client().read_certificates(&hashes).await?;
719 let certificates = match ResultReadCertificates::new(certificates, hashes) {
720 ResultReadCertificates::Certificates(certificates) => certificates,
721 ResultReadCertificates::InvalidHashes(hashes) => {
722 return Err(chain_client::Error::ReadCertificatesError(hashes))
723 }
724 };
725 for certificate in certificates {
726 if let Some(until) = until_block_time {
727 if certificate.value().block().header.timestamp >= until {
728 break;
729 }
730 }
731 last_info = self
732 .handle_certificate::<ConfirmedBlock>(certificate)
733 .await?
734 .info;
735 }
736 Ok(last_info)
737 }
738
739 #[instrument(level = "trace", skip_all)]
745 async fn download_certificates_from(
746 &self,
747 remote_node: &RemoteNode<Env::ValidatorNode>,
748 chain_id: ChainId,
749 stop: BlockHeight,
750 until_block_time: Option<Timestamp>,
751 ) -> Result<Box<ChainInfo>, chain_client::Error> {
752 let mut last_info = self
753 .load_local_certificates(chain_id, stop, until_block_time)
754 .await?;
755 let mut next_height = last_info.next_block_height;
756
757 if next_height >= stop {
758 return Ok(last_info);
759 }
760
761 #[cfg(not(web))]
765 type CertificateBatchFuture = std::pin::Pin<
766 Box<dyn Future<Output = Result<Vec<ConfirmedBlockCertificate>, NodeError>> + Send>,
767 >;
768 #[cfg(web)]
769 type CertificateBatchFuture = std::pin::Pin<
770 Box<dyn Future<Output = Result<Vec<ConfirmedBlockCertificate>, NodeError>>>,
771 >;
772
773 let max_concurrent = self.options.max_concurrent_batch_downloads;
774 let batch_size = self.options.certificate_download_batch_size;
775 let (sender, mut receiver) = tokio::sync::mpsc::channel(max_concurrent);
776 let scheduler = self.requests_scheduler.clone();
777 let remote = remote_node.clone();
778
779 let download_task = linera_base::Task::spawn(async move {
780 let mut download_height = next_height;
781 let mut in_flight = FuturesOrdered::<CertificateBatchFuture>::new();
782
783 let try_enqueue = |in_flight: &mut FuturesOrdered<CertificateBatchFuture>,
784 download_height: &mut BlockHeight| {
785 if *download_height >= stop {
786 return;
787 }
788 let limit = u64::from(stop)
789 .saturating_sub(u64::from(*download_height))
790 .min(batch_size);
791 let height = *download_height;
792 let scheduler = scheduler.clone();
793 let remote = remote.clone();
794 in_flight.push_back(Box::pin(async move {
795 scheduler
796 .download_certificates(&remote, chain_id, height, limit)
797 .await
798 }));
799 *download_height = BlockHeight(u64::from(*download_height) + limit);
800 };
801
802 while in_flight.len() < max_concurrent && download_height < stop {
803 try_enqueue(&mut in_flight, &mut download_height);
804 }
805
806 while let Some(result) = in_flight.next().await {
807 if sender.send(result).await.is_err() {
808 break;
809 }
810 try_enqueue(&mut in_flight, &mut download_height);
811 }
812 });
813
814 while let Some(result) = receiver.recv().await {
816 let certificates = result?;
817 let Some(info) = self
818 .process_certificates(
819 slice::from_ref(remote_node),
820 certificates,
821 until_block_time,
822 ProcessConfirmedBlockMode::Execute,
823 )
824 .await?
825 else {
826 break;
827 };
828 assert!(info.next_block_height >= next_height);
829 next_height = info.next_block_height;
830 last_info = info;
831 }
832 download_task.await;
835 Ok(last_info)
836 }
837
838 async fn download_blobs(
839 &self,
840 remote_nodes: &[RemoteNode<Env::ValidatorNode>],
841 blob_ids: &[BlobId],
842 ) -> Result<(), chain_client::Error> {
843 let blobs = &self
844 .requests_scheduler
845 .download_blobs(
846 remote_nodes,
847 blob_ids,
848 self.options.blob_download_hedge_delay,
849 )
850 .await?
851 .ok_or_else(|| {
852 chain_client::Error::RemoteNodeError(NodeError::BlobsNotFound(blob_ids.to_vec()))
853 })?;
854 self.local_node.store_blobs(blobs).await.map_err(Into::into)
855 }
856
857 #[instrument(level = "trace", skip_all)]
862 pub(crate) async fn download_certificates_for_events(
863 &self,
864 event_ids: &[EventId],
865 ) -> Result<(), chain_client::Error> {
866 let mut validators = self.validator_nodes().await?;
867 let hedge_delay = self.options.certificate_batch_download_hedge_delay;
868 let mut remaining_event_ids = event_ids.to_vec();
869
870 while !remaining_event_ids.is_empty() {
871 let remaining_ref = &remaining_event_ids;
872 validators.shuffle(&mut rand::thread_rng());
873 let result = communicate_concurrently(
874 &validators,
875 move |remote_node| {
876 let validator_key = remote_node.public_key;
877 let validator_address = remote_node.address();
878 Box::pin(async move {
879 let heights = remote_node
881 .node
882 .event_block_heights(remaining_ref.to_vec())
883 .await?;
884
885 let mut chain_heights = BTreeMap::<_, BTreeSet<_>>::new();
887 let mut expected_events = BTreeMap::<_, HashSet<EventId>>::new();
888 let mut unresolved = Vec::new();
889 for (event_id, maybe_height) in remaining_ref.iter().zip(heights) {
890 if let Some(height) = maybe_height {
891 chain_heights
892 .entry(event_id.chain_id)
893 .or_default()
894 .insert(height);
895 expected_events
896 .entry((event_id.chain_id, height))
897 .or_default()
898 .insert(event_id.clone());
899 } else {
900 unresolved.push(event_id.clone());
901 }
902 }
903 if chain_heights.is_empty() {
904 return Err(chain_client::Error::from(NodeError::EventsNotFound(remaining_ref.clone())));
906 }
907
908 let mut checked_certificates = Vec::<ConfirmedBlockCertificate>::new();
910 for (chain_id, heights) in chain_heights {
911 let heights_vec = heights.into_iter().collect::<Vec<_>>();
912 let certificates = self
913 .requests_scheduler
914 .download_certificates_by_heights(
915 &remote_node,
916 chain_id,
917 heights_vec,
918 )
919 .await?;
920 for cert in &certificates {
921 let block = cert.block();
923 let block_event_ids = block.event_ids().collect::<HashSet<_>>();
924 if let Some(expected_event_ids) =
925 expected_events.get(&(chain_id, block.header.height))
926 {
927 if !expected_event_ids.is_subset(&block_event_ids) {
928 tracing::debug!(
929 %validator_address, ?expected_event_ids, ?block_event_ids,
930 "validator lied about events in block."
931 );
932 return Err(NodeError::UnexpectedCertificateValue.into());
933 }
934 }
935 }
936 for cert in certificates {
937 self.check_certificate(&cert)
938 .await
939 .map_err(|error| {
940 tracing::debug!(
941 %validator_address, %error,
942 "invalid certificate"
943 );
944 error
945 })?
946 .into_result()
947 .map_err(|error| {
948 tracing::debug!(
949 %validator_address, %error,
950 "could not check certificate"
951 );
952 error
953 })?;
954 checked_certificates.push(cert);
955 }
956 }
957 Ok((checked_certificates, unresolved, validator_key))
958 })
959 },
960 hedge_delay,
961 self.storage_client().clock(),
962 )
963 .await;
964
965 match result {
966 Ok((certificates, unresolved, validator_key)) => {
967 for certificate in certificates {
968 let mode = ReceiveCertificateMode::AlreadyChecked;
969 self.receive_sender_certificate(
970 self.storage_client().cache_certificate(certificate),
971 mode,
972 None,
973 )
974 .await?;
975 }
976 validators.retain(|node| node.public_key != validator_key);
977 remaining_event_ids = unresolved;
978 }
979 Err(errors) => {
980 for (validator, error) in &errors {
981 warn!(
982 %validator,
983 %error,
984 "failed to download event certificates from validator",
985 );
986 }
987 return Err(NodeError::EventsNotFound(remaining_event_ids).into());
989 }
990 }
991 }
992 Ok(())
993 }
994
995 #[instrument(level = "trace", skip_all)]
1005 async fn bootstrap_chain_from_checkpoint(
1006 &self,
1007 remote_node: &RemoteNode<Env::ValidatorNode>,
1008 chain_id: ChainId,
1009 checkpoint_height: BlockHeight,
1010 ) -> Result<(), chain_client::Error> {
1011 let local_next = match self.local_node.chain_info(chain_id).await {
1012 Ok(info) => info.next_block_height,
1013 Err(LocalNodeError::BlobsNotFound(_)) => BlockHeight::ZERO,
1018 Err(err) => return Err(err.into()),
1019 };
1020 if local_next > checkpoint_height {
1021 return Ok(());
1022 }
1023 let certificates = remote_node
1024 .download_certificates_by_heights(chain_id, vec![checkpoint_height])
1025 .await?;
1026 if certificates.is_empty() {
1027 return Ok(());
1030 }
1031 self.process_certificates(
1037 slice::from_ref(remote_node),
1038 certificates,
1039 None,
1040 ProcessConfirmedBlockMode::Execute,
1041 )
1042 .await?;
1043 Ok(())
1044 }
1045
1046 #[instrument(level = "trace", skip_all)]
1051 async fn process_certificates(
1052 &self,
1053 remote_nodes: &[RemoteNode<Env::ValidatorNode>],
1054 certificates: Vec<ConfirmedBlockCertificate>,
1055 until_block_time: Option<Timestamp>,
1056 mode: ProcessConfirmedBlockMode,
1057 ) -> Result<Option<Box<ChainInfo>>, chain_client::Error> {
1058 let mut info = None;
1059 let created_blob_ids = certificates
1064 .iter()
1065 .flat_map(|certificate| certificate.value().block().created_blob_ids())
1066 .collect::<BTreeSet<BlobId>>();
1067 let required_blob_ids = certificates
1068 .iter()
1069 .flat_map(|certificate| certificate.value().required_blob_ids())
1070 .filter(|blob_id| !created_blob_ids.contains(blob_id))
1071 .collect::<Vec<_>>();
1072
1073 match self
1074 .local_node
1075 .read_blob_states_from_storage(&required_blob_ids)
1076 .await
1077 {
1078 Err(LocalNodeError::BlobsNotFound(blob_ids)) => {
1079 self.download_blobs(remote_nodes, &blob_ids).await?;
1080 }
1081 x => {
1082 x?;
1083 }
1084 }
1085
1086 for certificate in certificates {
1087 if let Some(until) = until_block_time {
1088 if certificate.value().block().header.timestamp >= until {
1089 break;
1090 }
1091 }
1092 let response = self
1093 .handle_certificate_with_retry(&certificate, remote_nodes, mode)
1094 .await?;
1095 info = Some(response.info);
1096 }
1097
1098 Ok(info)
1099 }
1100
1101 async fn handle_certificate_with_retry(
1105 &self,
1106 certificate: &ConfirmedBlockCertificate,
1107 nodes: &[RemoteNode<Env::ValidatorNode>],
1108 mode: ProcessConfirmedBlockMode,
1109 ) -> Result<ChainInfoResponse, chain_client::Error> {
1110 let mut downloaded_blobs = HashSet::<BlobId>::new();
1111 let mut downloaded_blocks = HashSet::<CryptoHash>::new();
1112 let mut events = EventSetDownloader::new(self);
1113 loop {
1114 let result = self
1115 .handle_confirmed_certificate(certificate.clone(), mode)
1116 .await;
1117 if let Err(LocalNodeError::BlobsNotFound(blob_ids)) = &result {
1118 let new_blobs = filter_new(blob_ids, &downloaded_blobs);
1119 if !new_blobs.is_empty() {
1120 self.download_blobs(nodes, &new_blobs).await?;
1121 downloaded_blobs.extend(new_blobs);
1122 continue;
1123 }
1124 }
1125 if let Err(LocalNodeError::BlocksNotFound(hashes)) = &result {
1126 let new_blocks = filter_new(hashes, &downloaded_blocks);
1127 if !new_blocks.is_empty() {
1128 self.download_pre_checkpoint_blocks(nodes, &new_blocks)
1129 .await?;
1130 downloaded_blocks.extend(new_blocks);
1131 continue;
1132 }
1133 }
1134 if let Err(LocalNodeError::EventsNotFound(event_ids)) = &result {
1135 if events.download_new(event_ids).await? {
1136 continue;
1137 }
1138 }
1139 return Ok(result?);
1140 }
1141 }
1142
1143 async fn download_pre_checkpoint_blocks(
1150 &self,
1151 nodes: &[RemoteNode<Env::ValidatorNode>],
1152 hashes: &[CryptoHash],
1153 ) -> Result<(), chain_client::Error> {
1154 for hash in hashes {
1155 let mut last_error = None;
1156 for node in nodes {
1157 match node.node.download_certificate(*hash).await {
1158 Ok(certificate) => {
1159 Box::pin(self.handle_certificate_with_retry(
1160 &certificate,
1161 nodes,
1162 ProcessConfirmedBlockMode::Auto,
1163 ))
1164 .await?;
1165 last_error = None;
1166 break;
1167 }
1168 Err(error) => last_error = Some(error),
1169 }
1170 }
1171 if let Some(error) = last_error {
1172 return Err(error.into());
1173 }
1174 }
1175 Ok(())
1176 }
1177
1178 async fn handle_certificate<T: ProcessableCertificate>(
1179 &self,
1180 certificate: T::Certificate,
1181 ) -> Result<ChainInfoResponse, LocalNodeError> {
1182 self.local_node
1183 .handle_certificate::<T>(certificate, &self.notifier)
1184 .await
1185 }
1186
1187 async fn handle_confirmed_certificate(
1188 &self,
1189 certificate: ConfirmedBlockCertificate,
1190 mode: ProcessConfirmedBlockMode,
1191 ) -> Result<ChainInfoResponse, LocalNodeError> {
1192 self.local_node
1193 .handle_confirmed_certificate(certificate, mode, &self.notifier)
1194 .await
1195 }
1196
1197 pub async fn admin_committee(&self) -> Result<(Epoch, Arc<Committee>), LocalNodeError> {
1199 let info = self.local_node.chain_info(self.admin_chain_id).await?;
1200 let hash = info
1201 .committee_hash
1202 .ok_or(LocalNodeError::InactiveChain(self.admin_chain_id))?;
1203 let committee = self
1204 .storage_client()
1205 .get_or_load_committee_by_hash(hash)
1206 .await?;
1207 Ok((info.epoch, committee))
1208 }
1209
1210 async fn validator_nodes(
1212 &self,
1213 ) -> Result<Vec<RemoteNode<Env::ValidatorNode>>, chain_client::Error> {
1214 let (_, committee) = self.admin_committee().await?;
1215 Ok(self.make_nodes(&committee)?)
1216 }
1217
1218 fn make_nodes(
1220 &self,
1221 committee: &Committee,
1222 ) -> Result<Vec<RemoteNode<Env::ValidatorNode>>, NodeError> {
1223 Ok(self
1224 .validator_node_provider()
1225 .make_nodes(committee)?
1226 .map(|(public_key, node)| RemoteNode { public_key, node })
1227 .collect())
1228 }
1229
1230 pub async fn get_chain_description_blob(
1233 &self,
1234 chain_id: ChainId,
1235 ) -> Result<Arc<Blob>, chain_client::Error> {
1236 let chain_desc_id = BlobId::new(chain_id.0, BlobType::ChainDescription);
1237 let blob = self
1238 .local_node
1239 .storage_client()
1240 .read_blob(chain_desc_id)
1241 .await?;
1242 if let Some(blob) = blob {
1243 return Ok(blob.into_std());
1245 }
1246 self.synchronize_chain_state(self.admin_chain_id).await?;
1248 let nodes = self.validator_nodes().await?;
1249 Ok(self
1250 .update_local_node_with_blobs_from(vec![chain_desc_id], &nodes)
1251 .await?
1252 .pop()
1253 .unwrap() .into_std())
1255 }
1256
1257 pub async fn get_chain_description(
1260 &self,
1261 chain_id: ChainId,
1262 ) -> Result<ChainDescription, chain_client::Error> {
1263 let blob = self.get_chain_description_blob(chain_id).await?;
1264 Ok(bcs::from_bytes(blob.bytes())?)
1265 }
1266
1267 pub async fn get_application_description_blob(
1272 &self,
1273 application_id: ApplicationId,
1274 ) -> Result<Arc<Blob>, chain_client::Error> {
1275 let blob_id = application_id.description_blob_id();
1276 let blob = self.local_node.storage_client().read_blob(blob_id).await?;
1277 if let Some(blob) = blob {
1278 return Ok(blob.into_std());
1280 }
1281 Box::pin(self.synchronize_chain_state(self.admin_chain_id)).await?;
1283 let nodes = self.validator_nodes().await?;
1284 Ok(self
1285 .update_local_node_with_blobs_from(vec![blob_id], &nodes)
1286 .await?
1287 .pop()
1288 .unwrap() .into_std())
1290 }
1291
1292 pub async fn get_application_description(
1295 &self,
1296 application_id: ApplicationId,
1297 ) -> Result<ApplicationDescription, chain_client::Error> {
1298 let blob = self
1299 .get_application_description_blob(application_id)
1300 .await?;
1301 Ok(bcs::from_bytes(blob.bytes())?)
1302 }
1303
1304 #[instrument(level = "trace", skip_all)]
1306 pub(crate) async fn finalize_block(
1307 self: &Arc<Self>,
1308 committee: &Committee,
1309 certificate: ValidatedBlockCertificate,
1310 ) -> Result<ConfirmedBlockCertificate, chain_client::Error> {
1311 debug!(round = %certificate.round(), "Submitting block for confirmation");
1312 let hashed_value = ConfirmedBlock::new(certificate.block().clone());
1313 let full_justification = certificate.full_justification();
1318 let finalize_action = CommunicateAction::FinalizeBlock {
1319 certificate: Box::new(certificate),
1320 delivery: self.options.cross_chain_message_delivery,
1321 };
1322 let quorum = self
1323 .communicate_chain_action(committee, finalize_action, hashed_value)
1324 .await?;
1325 let justification = if quorum.first_round() {
1331 JustificationChain::default()
1332 } else {
1333 full_justification
1334 };
1335 ensure!(
1338 quorum.justification_commitment() == justification.commitment(quorum.hash()),
1339 chain_client::Error::ProtocolError(
1340 "A quorum confirmed with a justification commitment that does not match the \
1341 validated certificate's justification chain",
1342 )
1343 );
1344 let certificate = ConfirmedBlockCertificate::from_parts(quorum, justification);
1345 self.receive_certificate_with_checked_signatures(
1346 certificate.clone(),
1347 ProcessConfirmedBlockMode::Execute,
1348 )
1349 .await?;
1350 Ok(certificate)
1351 }
1352
1353 #[instrument(level = "trace", skip_all)]
1355 async fn submit_block_proposal<T: ProcessableCertificate>(
1356 self: &Arc<Self>,
1357 committee: Arc<Committee>,
1358 proposal: Box<BlockProposal>,
1359 value: T,
1360 ) -> Result<T::Certificate, chain_client::Error> {
1361 debug!(
1362 round = %proposal.content.round,
1363 "Submitting block proposal to validators"
1364 );
1365
1366 let justification = match proposal.original_proposal.as_ref() {
1370 Some(OriginalProposal::Regular { certificate }) => certificate.full_justification(),
1371 Some(OriginalProposal::Fast(_)) | None => JustificationChain::default(),
1372 };
1373
1374 let block_timestamp = proposal.content.block.timestamp;
1376 let local_time = self.local_node.storage_client().clock().current_time();
1377 if block_timestamp > local_time {
1378 info!(
1379 chain_id = %proposal.content.block.chain_id,
1380 %block_timestamp,
1381 %local_time,
1382 "Block timestamp is in the future; waiting until it can be proposed",
1383 );
1384 }
1385
1386 let (clock_skew_sender, mut clock_skew_receiver) = mpsc::unbounded_channel();
1388 let submit_action = CommunicateAction::SubmitBlock {
1389 proposal,
1390 blob_ids: value.required_blob_ids().into_iter().collect(),
1391 clock_skew_sender,
1392 };
1393
1394 let validity_threshold = committee.validity_threshold();
1396 let committee_clone = committee.clone();
1397 let clock_skew_check_handle = linera_base::Task::spawn(async move {
1398 let mut skew_weight = 0u64;
1399 let mut min_skew = TimeDelta::MAX;
1400 let mut max_skew = TimeDelta::ZERO;
1401 while let Some((public_key, clock_skew)) = clock_skew_receiver.recv().await {
1402 if clock_skew.as_micros() > 0 {
1403 skew_weight += committee_clone.weight(&public_key);
1404 min_skew = min_skew.min(clock_skew);
1405 max_skew = max_skew.max(clock_skew);
1406 if skew_weight >= validity_threshold {
1407 warn!(
1408 skew_weight,
1409 validity_threshold,
1410 min_skew_ms = min_skew.as_micros() / 1000,
1411 max_skew_ms = max_skew.as_micros() / 1000,
1412 "A validity threshold of validators reported clock skew; \
1413 consider checking your system clock",
1414 );
1415 return;
1416 }
1417 }
1418 }
1419 });
1420
1421 let quorum = self
1422 .communicate_chain_action(&committee, submit_action, value)
1423 .await?;
1424
1425 clock_skew_check_handle.await;
1426
1427 ensure!(
1435 quorum.unlocking_round() == justification.top_unlocking_round(),
1436 chain_client::Error::ProtocolError(
1437 "A quorum voted with an unlocking round that does not match the proposal's \
1438 justification chain",
1439 )
1440 );
1441 ensure!(
1442 quorum.justification_commitment() == justification.commitment(quorum.hash()),
1443 chain_client::Error::ProtocolError(
1444 "A quorum voted with a justification commitment that does not match the \
1445 proposal's justification chain",
1446 )
1447 );
1448 let certificate = T::make_certificate(quorum, justification);
1449 self.handle_certificate::<T>(certificate.clone()).await?;
1450 Ok(certificate)
1451 }
1452
1453 fn remote_node_updater(
1455 &self,
1456 remote_node: RemoteNode<Env::ValidatorNode>,
1457 ) -> RemoteNodeUpdater<Env::Storage, Env::ValidatorNode> {
1458 RemoteNodeUpdater {
1459 remote_node,
1460 local_node: self.local_node.clone(),
1461 admin_chain_id: self.admin_chain_id,
1462 certificate_upload_batch_size: self.options.certificate_upload_batch_size,
1463 }
1464 }
1465
1466 async fn process_lag_reports(&self, mut reports: Vec<LagReport<Env::ValidatorNode>>) {
1480 reports.sort_by_key(|report| Reverse(report.remote_progress()));
1481 for report in reports {
1482 if let Err(error) =
1485 Box::pin(self.synchronize_chain_state_from(&report.remote_node, report.chain_id))
1486 .await
1487 {
1488 debug!(
1489 remote_node = report.remote_node.address(),
1490 chain_id = %report.chain_id,
1491 %error,
1492 "failed to pull chain state from a validator that reported being ahead",
1493 );
1494 }
1495 }
1496 }
1497
1498 #[instrument(level = "trace", skip_all, fields(chain_id, block_height, delivery))]
1500 async fn communicate_chain_updates(
1501 self: &Arc<Self>,
1502 committee: &Committee,
1503 chain_id: ChainId,
1504 height: BlockHeight,
1505 delivery: CrossChainMessageDelivery,
1506 latest_certificate: Option<CacheArc<ConfirmedBlockCertificate>>,
1507 ) -> Result<(), chain_client::Error> {
1508 let nodes = self.make_nodes(committee)?;
1509 communicate_with_quorum(
1510 &nodes,
1511 committee,
1512 |_: &()| (),
1513 |remote_node| {
1514 let mut updater = self.remote_node_updater(remote_node);
1515 let certificate = latest_certificate.clone();
1516 Box::pin(async move {
1517 updater
1518 .send_chain_information(chain_id, height, delivery, certificate)
1519 .await
1520 })
1521 },
1522 self.options.quorum_grace_period,
1523 )
1524 .await?;
1525 Ok(())
1526 }
1527
1528 #[instrument(level = "trace", skip_all)]
1534 async fn communicate_chain_action<T: CertificateValue>(
1535 self: &Arc<Self>,
1536 committee: &Committee,
1537 action: CommunicateAction,
1538 value: T,
1539 ) -> Result<GenericCertificate<T>, chain_client::Error> {
1540 let lag_reports = Mutex::new(Vec::new());
1545 let nodes = self.make_nodes(committee)?;
1546 let result = communicate_with_quorum(
1551 &nodes,
1552 committee,
1553 |vote: &LiteVote| {
1554 (
1555 vote.value.value_hash,
1556 vote.round,
1557 vote.unlocking_round,
1558 vote.first_round,
1559 vote.justification_commitment,
1560 )
1561 },
1562 |remote_node| {
1563 let mut updater = self.remote_node_updater(remote_node.clone());
1564 let action = action.clone();
1565 let lag_reports = &lag_reports;
1566 Box::pin(async move {
1567 match updater.send_chain_update(action).await {
1568 Err(chain_client::Error::LocalNodeLagging { chain_id, error }) => {
1569 lag_reports.lock().unwrap().push(LagReport {
1570 remote_node,
1571 chain_id,
1572 error: (*error).clone(),
1573 });
1574 Err((*error).into())
1575 }
1576 result => result,
1577 }
1578 })
1579 },
1580 self.options.quorum_grace_period,
1581 )
1582 .await;
1583 let ((votes_hash, votes_round, _, _, _), votes) = match result {
1584 Ok(quorum) => quorum,
1585 Err(err) => {
1586 self.process_lag_reports(lag_reports.into_inner().unwrap())
1589 .await;
1590 return Err(err.into());
1591 }
1592 };
1593 ensure!(
1594 (votes_hash, votes_round) == (value.hash(), action.round()),
1595 chain_client::Error::UnexpectedQuorum {
1596 hash: votes_hash,
1597 round: votes_round,
1598 expected_hash: value.hash(),
1599 expected_round: action.round(),
1600 }
1601 );
1602 let certificate = LiteCertificate::try_from_votes(votes)
1607 .ok_or_else(|| {
1608 chain_client::Error::InternalError(
1609 "Vote values or rounds don't match; this is a bug",
1610 )
1611 })?
1612 .with_value(value)
1613 .ok_or_else(|| {
1614 chain_client::Error::ProtocolError("A quorum voted for an unexpected value")
1615 })?;
1616 Ok(certificate)
1617 }
1618
1619 #[instrument(level = "trace", skip_all)]
1622 async fn receive_certificate_with_checked_signatures(
1623 &self,
1624 certificate: ConfirmedBlockCertificate,
1625 mode: ProcessConfirmedBlockMode,
1626 ) -> Result<(), chain_client::Error> {
1627 let block = certificate.block();
1628 self.download_certificates(block.header.chain_id, block.header.height)
1630 .await?;
1631 let nodes = self.validator_nodes().await?;
1634 self.handle_certificate_with_retry(&certificate, &nodes, mode)
1635 .await?;
1636 Ok(())
1637 }
1638
1639 #[instrument(level = "trace", skip_all)]
1644 fn receive_sender_certificate(
1649 &self,
1650 certificate: CacheArc<ConfirmedBlockCertificate>,
1651 mode: ReceiveCertificateMode,
1652 nodes: Option<Vec<RemoteNode<Env::ValidatorNode>>>,
1653 ) -> ReceiveSenderCertificateFuture<'_> {
1654 Box::pin(async move {
1655 if let ReceiveCertificateMode::NeedsCheck = mode {
1657 let mut check_result = self.check_certificate(&certificate).await?;
1658 if matches!(check_result, CheckCertificateResult::FutureEpoch) {
1659 let admin_chain_id = self.admin_chain_id;
1667 let epoch = certificate.block().header.epoch;
1668 info!(
1669 %epoch,
1670 "certificate is from an unknown epoch; synchronizing the admin chain"
1671 );
1672 let synced_from_serving_node = if let Some(nodes) = &nodes {
1673 let certificate = &certificate;
1674 communicate_concurrently(
1675 nodes,
1676 |node| {
1677 Box::pin(async move {
1678 self.synchronize_chain_state_from(&node, admin_chain_id)
1679 .await?;
1680 match self.check_certificate(certificate).await? {
1681 CheckCertificateResult::FutureEpoch => {
1682 Err(chain_client::Error::CommitteeSynchronizationError)
1683 }
1684 _ => Ok(()),
1685 }
1686 })
1687 },
1688 self.options.blob_download_hedge_delay,
1689 self.storage_client().clock(),
1690 )
1691 .await
1692 .is_ok()
1693 } else {
1694 false
1695 };
1696 if synced_from_serving_node {
1697 check_result = self.check_certificate(&certificate).await?;
1698 }
1699 if matches!(check_result, CheckCertificateResult::FutureEpoch) {
1700 Box::pin(self.synchronize_chain_state(admin_chain_id)).await?;
1701 check_result = self.check_certificate(&certificate).await?;
1702 }
1703 }
1704 check_result.into_result()?;
1705 }
1706 let nodes = if let Some(nodes) = nodes {
1708 nodes
1709 } else {
1710 self.validator_nodes().await?
1711 };
1712 let processing_mode = if self
1713 .chain_mode(certificate.value().chain_id())
1714 .is_some_and(|m| m.should_sync_chain_state())
1715 {
1716 ProcessConfirmedBlockMode::Auto
1717 } else {
1718 ProcessConfirmedBlockMode::Preprocess
1719 };
1720 self.handle_certificate_with_retry(&certificate, &nodes, processing_mode)
1721 .await?;
1722
1723 Ok(())
1724 })
1725 }
1726
1727 #[instrument(level = "debug", skip_all, fields(chain_id = %sender_chain_id))]
1729 async fn download_and_process_sender_chain(
1730 &self,
1731 sender_chain_id: ChainId,
1732 nodes: &[RemoteNode<Env::ValidatorNode>],
1733 received_log: &ReceivedLogs,
1734 mut remote_heights: Vec<BlockHeight>,
1735 sender: mpsc::UnboundedSender<ChainAndHeight>,
1736 ) {
1737 let mut nodes = nodes.to_vec();
1738 while !remote_heights.is_empty() {
1739 if let Ok(local_certs) = self
1742 .storage_client()
1743 .read_certificates_by_heights(sender_chain_id, &remote_heights)
1744 .await
1745 {
1746 let mut still_needed = Vec::new();
1747 for (height, maybe_cert) in remote_heights.iter().copied().zip(local_certs) {
1748 if let Some(certificate) = maybe_cert {
1749 let chain_id = certificate.block().header.chain_id;
1750 if let Err(error) = sender.send(ChainAndHeight { chain_id, height }) {
1751 error!(
1752 %chain_id, %height, %error,
1753 "failed to send chain and height over the channel",
1754 );
1755 }
1756 } else {
1757 still_needed.push(height);
1758 }
1759 }
1760 remote_heights = still_needed;
1761 if remote_heights.is_empty() {
1762 break;
1763 }
1764 }
1765
1766 let remote_heights_ref = &remote_heights;
1767 let certificates = match communicate_concurrently(
1768 &nodes,
1769 async move |remote_node| {
1770 let mut remote_heights = remote_heights_ref.clone();
1771 remote_heights.retain(|height| {
1774 received_log.validator_has_block(
1775 &remote_node.public_key,
1776 sender_chain_id,
1777 *height,
1778 )
1779 });
1780 if remote_heights.is_empty() {
1781 return Err(NodeError::MissingCertificateValue);
1784 }
1785 let certificates = self
1786 .requests_scheduler
1787 .download_certificates_by_heights(
1788 &remote_node,
1789 sender_chain_id,
1790 remote_heights,
1791 )
1792 .await?;
1793 let mut certificates_with_check_results = vec![];
1794 for cert in certificates {
1795 let check_result = self.check_certificate(&cert).await?;
1796 certificates_with_check_results
1797 .push((cert, check_result.into_result().is_ok()));
1798 }
1799 Ok(certificates_with_check_results)
1800 },
1801 self.options.certificate_batch_download_hedge_delay,
1802 self.storage_client().clock(),
1803 )
1804 .await
1805 {
1806 Ok(certificates_with_check_results) => certificates_with_check_results,
1807 Err(errors) => {
1808 let faulty_validators = errors
1809 .into_iter()
1810 .map(|(validator, error)| {
1811 warn!(
1812 %validator,
1813 %sender_chain_id,
1814 %error,
1815 "failed to download certificates from validator",
1816 );
1817 validator
1818 })
1819 .collect::<BTreeSet<_>>();
1820 nodes.retain(|node| !faulty_validators.contains(&node.public_key));
1822 if nodes.is_empty() {
1823 info!(
1824 chain_id = %sender_chain_id,
1825 "could not download certificates for chain - no more correct validators left"
1826 );
1827 return;
1828 }
1829 continue;
1830 }
1831 };
1832
1833 trace!(
1834 num_certificates = %certificates.len(),
1835 "received certificates",
1836 );
1837
1838 let mut to_remove_from_queue = BTreeSet::new();
1839
1840 for (certificate, check_result) in certificates {
1841 let hash = certificate.hash();
1842 let chain_id = certificate.block().header.chain_id;
1843 let height = certificate.block().header.height;
1844 if !check_result {
1845 to_remove_from_queue.insert(height);
1849 continue;
1850 }
1851 let mode = ReceiveCertificateMode::AlreadyChecked;
1853 if let Err(error) = self
1854 .receive_sender_certificate(
1855 self.storage_client().cache_certificate(certificate),
1856 mode,
1857 None,
1858 )
1859 .await
1860 {
1861 warn!(%error, %hash, "Received invalid certificate");
1862 } else {
1863 to_remove_from_queue.insert(height);
1864 if let Err(error) = sender.send(ChainAndHeight { chain_id, height }) {
1865 error!(
1866 %chain_id,
1867 %height,
1868 %error,
1869 "failed to send chain and height over the channel",
1870 );
1871 }
1872 }
1873 }
1874
1875 remote_heights.retain(|height| !to_remove_from_queue.contains(height));
1876 }
1877 trace!("find_received_certificates: finished processing chain");
1878 }
1879
1880 #[instrument(level = "trace", skip(self))]
1882 async fn get_received_log_from_validator(
1883 &self,
1884 chain_id: ChainId,
1885 remote_node: &RemoteNode<Env::ValidatorNode>,
1886 tracker: u64,
1887 ) -> Result<Vec<ChainAndHeight>, chain_client::Error> {
1888 let mut offset = tracker;
1889
1890 let mut remote_log = Vec::new();
1892 loop {
1893 trace!("get_received_log_from_validator: looping");
1894 let query = ChainInfoQuery::new(chain_id).with_received_log_excluding_first_n(offset);
1895 let info = remote_node.handle_chain_info_query(query).await?;
1896 let received_entries = info.requested_received_log.len();
1897 offset += received_entries as u64;
1898 remote_log.extend(info.requested_received_log);
1899 trace!(
1900 remote_node = remote_node.address(),
1901 %received_entries,
1902 "get_received_log_from_validator: received log batch",
1903 );
1904 if received_entries < CHAIN_INFO_MAX_RECEIVED_LOG_ENTRIES {
1905 break;
1906 }
1907 }
1908
1909 trace!(
1910 remote_node = remote_node.address(),
1911 num_entries = remote_log.len(),
1912 "get_received_log_from_validator: returning downloaded log",
1913 );
1914
1915 Ok(remote_log)
1916 }
1917
1918 async fn download_sender_block_with_sending_ancestors(
1924 &self,
1925 receiver_chain_id: ChainId,
1926 sender_chain_id: ChainId,
1927 height: BlockHeight,
1928 remote_node: &RemoteNode<Env::ValidatorNode>,
1929 ) -> Result<(), chain_client::Error> {
1930 let next_outbox_height = self
1931 .local_node
1932 .next_outbox_heights(&[sender_chain_id], receiver_chain_id)
1933 .await?
1934 .get(&sender_chain_id)
1935 .copied()
1936 .unwrap_or(BlockHeight::ZERO);
1937
1938 let mut certificates = BTreeMap::new();
1941 let mut current_height = height;
1942 let mut current_hash: Option<CryptoHash> = None;
1945
1946 while current_height >= next_outbox_height {
1948 let certificate = if let Some(local) = self
1952 .try_read_local_certificate(sender_chain_id, current_height, current_hash)
1953 .await?
1954 {
1955 local
1956 } else {
1957 let downloaded = self
1958 .requests_scheduler
1959 .download_certificates_by_heights(
1960 remote_node,
1961 sender_chain_id,
1962 vec![current_height],
1963 )
1964 .await?;
1965 let Some(certificate) = downloaded.into_iter().next() else {
1966 return Err(chain_client::Error::CannotDownloadMissingSenderBlock {
1967 chain_id: sender_chain_id,
1968 height: current_height,
1969 });
1970 };
1971 self.storage_client().cache_certificate(certificate)
1972 };
1973
1974 self.check_certificate(&certificate).await?.into_result()?;
1976
1977 let block = certificate.block();
1979 let next = block
1980 .body
1981 .previous_message_blocks
1982 .get(&receiver_chain_id)
1983 .map(|(prev_hash, prev_height)| (*prev_hash, *prev_height));
1984
1985 certificates.insert(current_height, certificate);
1987
1988 if let Some((prev_hash, prev_height)) = next {
1989 current_height = prev_height;
1991 current_hash = Some(prev_hash);
1992 } else {
1993 break;
1995 }
1996 }
1997
1998 if certificates.is_empty() {
1999 self.retry_pending_cross_chain_requests(sender_chain_id)
2000 .await?;
2001 }
2002
2003 for certificate in certificates.into_values() {
2005 self.receive_sender_certificate(
2006 certificate,
2007 ReceiveCertificateMode::AlreadyChecked,
2008 Some(vec![remote_node.clone()]),
2009 )
2010 .await?;
2011 }
2012
2013 Ok(())
2014 }
2015
2016 async fn download_event_bearing_blocks(
2020 &self,
2021 publisher_chain_id: ChainId,
2022 initial_blocks: BTreeSet<(BlockHeight, CryptoHash)>,
2023 local_next_block_height: BlockHeight,
2024 subscribed_streams: &BTreeSet<StreamId>,
2025 remote_node: &RemoteNode<Env::ValidatorNode>,
2026 ) -> Result<(), chain_client::Error> {
2027 if initial_blocks.is_empty() {
2028 return Ok(());
2029 }
2030
2031 let mut certificates = BTreeMap::new();
2032 let mut blocks_to_fetch = initial_blocks;
2033 let next_expected_events = self
2034 .local_node
2035 .next_expected_events(
2036 publisher_chain_id,
2037 subscribed_streams.iter().cloned().collect(),
2038 )
2039 .await?;
2040
2041 while let Some((current_height, current_hash)) = blocks_to_fetch.pop_last() {
2042 if current_height < local_next_block_height {
2043 continue; }
2045 if certificates.contains_key(¤t_height) {
2046 continue;
2047 }
2048
2049 let certificate = if let Some(certificate) =
2050 self.storage_client().read_certificate(current_hash).await?
2051 {
2052 certificate
2053 } else {
2054 let downloaded = self
2055 .requests_scheduler
2056 .download_certificates(remote_node, publisher_chain_id, current_height, 1)
2057 .await?;
2058 let Some(certificate) = downloaded.into_iter().next() else {
2059 tracing::debug!(
2060 validator = remote_node.address(),
2061 %publisher_chain_id,
2062 height = %current_height,
2063 "failed to download event publisher block"
2064 );
2065 continue;
2066 };
2067
2068 self.check_certificate(&certificate).await?.into_result()?;
2069
2070 self.storage_client().cache_certificate(certificate)
2071 };
2072
2073 let block = certificate.block();
2074 for stream_id in subscribed_streams {
2076 if let Some((prev_hash, prev_height)) =
2077 block.body.previous_event_blocks.get(stream_id)
2078 {
2079 if next_expected_events.get(stream_id).is_some_and(|index| {
2080 block
2081 .body
2082 .events
2083 .iter()
2084 .flatten()
2085 .find(|event| event.stream_id == *stream_id)
2086 .is_some_and(|event| event.index == *index)
2087 }) {
2088 continue;
2089 }
2090 if !certificates.contains_key(prev_height) {
2091 blocks_to_fetch.insert((*prev_height, *prev_hash));
2092 }
2093 }
2094 }
2095
2096 certificates.insert(current_height, certificate);
2097 }
2098
2099 for certificate in certificates.into_values() {
2101 self.receive_sender_certificate(
2102 certificate,
2103 ReceiveCertificateMode::AlreadyChecked,
2104 Some(vec![remote_node.clone()]),
2105 )
2106 .await?;
2107 }
2108
2109 Ok(())
2110 }
2111
2112 async fn sync_events_from_node(
2115 &self,
2116 chain_id: ChainId,
2117 stream_ids: &BTreeSet<StreamId>,
2118 remote_node: &RemoteNode<Env::ValidatorNode>,
2119 ) -> Result<(), chain_client::Error> {
2120 let stream_ids_vec = stream_ids.iter().cloned().collect::<Vec<_>>();
2121 let mut initial_blocks = BTreeSet::new();
2122 for chunk in stream_ids_vec.chunks(self.options.max_event_stream_queries) {
2123 let query = ChainInfoQuery::new(chain_id).with_previous_event_blocks(chunk.to_vec());
2124 let info = remote_node.handle_chain_info_query(query).await?;
2125 initial_blocks.extend(info.requested_previous_event_blocks.values().copied());
2126 }
2127 let local_height = match self.local_node.chain_info(chain_id).await {
2128 Ok(info) => info.next_block_height,
2129 Err(LocalNodeError::InactiveChain(_) | LocalNodeError::BlobsNotFound(_)) => {
2130 BlockHeight::ZERO
2131 }
2132 Err(error) => return Err(error.into()),
2133 };
2134 self.download_event_bearing_blocks(
2135 chain_id,
2136 initial_blocks,
2137 local_height,
2138 stream_ids,
2139 remote_node,
2140 )
2141 .await
2142 }
2143
2144 #[instrument(
2145 level = "trace", skip_all,
2146 fields(certificate_hash = ?incoming_certificate.hash()),
2147 )]
2148 async fn check_certificate(
2149 &self,
2150 incoming_certificate: &ConfirmedBlockCertificate,
2151 ) -> Result<CheckCertificateResult, NodeError> {
2152 let epoch = incoming_certificate.block().header.epoch;
2153 let storage = self.storage_client();
2154 let view_err = |error: ExecutionError| NodeError::ViewError {
2155 error: error.to_string(),
2156 };
2157 if storage.is_epoch_revoked(epoch).await.map_err(view_err)? {
2158 return Ok(CheckCertificateResult::OldEpoch);
2159 }
2160 let Some(committee) = storage.committee_for_epoch(epoch).await.map_err(view_err)? else {
2161 return Ok(CheckCertificateResult::FutureEpoch);
2162 };
2163 incoming_certificate.check(&committee)?;
2164 Ok(CheckCertificateResult::New)
2165 }
2166
2167 #[instrument(level = "trace", skip_all)]
2171 async fn synchronize_chain_state(
2172 &self,
2173 chain_id: ChainId,
2174 ) -> Result<Box<ChainInfo>, chain_client::Error> {
2175 let (_, committee) = self.admin_committee().await?;
2176 self.synchronize_chain_from_committee(chain_id, committee)
2177 .await
2178 }
2179
2180 #[instrument(level = "trace", skip_all)]
2185 pub(crate) async fn synchronize_chain_from_committee(
2186 &self,
2187 chain_id: ChainId,
2188 committee: Arc<Committee>,
2189 ) -> Result<Box<ChainInfo>, chain_client::Error> {
2190 #[cfg(with_metrics)]
2191 let _latency = if !self.is_chain_follow_only(chain_id) {
2192 Some(metrics::SYNCHRONIZE_CHAIN_STATE_LATENCY.measure_latency())
2193 } else {
2194 None
2195 };
2196
2197 let validators = self.make_nodes(&committee)?;
2198 Box::pin(self.fetch_chain_info(chain_id, &validators)).await?;
2199 communicate_with_quorum(
2200 &validators,
2201 &committee,
2202 |_: &()| (),
2203 |remote_node| async move {
2204 self.synchronize_chain_state_from(&remote_node, chain_id)
2205 .await
2206 },
2207 self.options.quorum_grace_period,
2208 )
2209 .await?;
2210
2211 self.local_node
2212 .chain_info(chain_id)
2213 .await
2214 .map_err(Into::into)
2215 }
2216
2217 #[instrument(level = "trace", skip(self, remote_node, chain_id))]
2223 pub(crate) async fn synchronize_chain_state_from(
2224 &self,
2225 remote_node: &RemoteNode<Env::ValidatorNode>,
2226 chain_id: ChainId,
2227 ) -> Result<(), chain_client::Error> {
2228 let with_manager_values = !self.is_chain_follow_only(chain_id);
2229 let query = if with_manager_values {
2230 ChainInfoQuery::new(chain_id).with_manager_values()
2231 } else {
2232 ChainInfoQuery::new(chain_id)
2233 };
2234 let query = query.with_latest_checkpoint_height();
2235 let remote_info = remote_node.handle_chain_info_query(query).await?;
2236
2237 if let Some(checkpoint_height) = remote_info.requested_latest_checkpoint_height {
2244 self.bootstrap_chain_from_checkpoint(remote_node, chain_id, checkpoint_height)
2245 .await?;
2246 }
2247
2248 let local_info = self
2249 .download_certificates_from(remote_node, chain_id, remote_info.next_block_height, None)
2250 .await?;
2251
2252 if !with_manager_values {
2253 return Ok(());
2254 }
2255
2256 let local_height = local_info.next_block_height;
2258 if local_height != remote_info.next_block_height {
2259 debug!(
2260 remote_node = remote_node.address(),
2261 remote_height = %remote_info.next_block_height,
2262 local_height = %local_height,
2263 "synced from validator, but remote height and local height are different",
2264 );
2265 return Ok(());
2266 };
2267
2268 if let Some(timeout) = remote_info.manager.timeout {
2269 self.handle_certificate::<Timeout>(*timeout).await?;
2270 }
2271 let mut proposals = Vec::new();
2272 if let Some(proposal) = remote_info.manager.requested_signed_proposal {
2273 proposals.push(*proposal);
2274 }
2275 if let Some(proposal) = remote_info.manager.requested_proposed {
2276 proposals.push(*proposal);
2277 }
2278 if let Some(locking) = remote_info.manager.requested_locking {
2279 match *locking {
2280 LockingBlock::Fast(proposal) => {
2281 proposals.push(proposal);
2282 }
2283 LockingBlock::Regular(cert) => {
2284 let hash = cert.hash();
2285 if let Err(error) = self.try_process_locking_block_from(remote_node, cert).await
2286 {
2287 debug!(
2288 remote_node = remote_node.address(),
2289 %hash,
2290 height = %local_height,
2291 %error,
2292 "skipping locked block from validator",
2293 );
2294 }
2295 }
2296 }
2297 }
2298 'proposal_loop: for proposal in proposals {
2299 let owner: AccountOwner = proposal.owner();
2300 if let Err(mut err) = self
2301 .local_node
2302 .handle_block_proposal(proposal.clone())
2303 .await
2304 {
2305 if let LocalNodeError::BlobsNotFound(_) = &err {
2306 let required_blob_ids = proposal.required_blob_ids().collect::<Vec<_>>();
2307 if !required_blob_ids.is_empty() {
2308 let mut blobs = Vec::new();
2309 for blob_id in required_blob_ids {
2310 let blob_content = match self
2311 .requests_scheduler
2312 .download_pending_blob(remote_node, chain_id, blob_id)
2313 .await
2314 {
2315 Ok(content) => content,
2316 Err(error) => {
2317 info!(
2318 remote_node = remote_node.address(),
2319 height = %local_height,
2320 proposer = %owner,
2321 %blob_id,
2322 %error,
2323 "skipping proposal from validator; failed to download blob",
2324 );
2325 continue 'proposal_loop;
2326 }
2327 };
2328 blobs.push(Blob::new(blob_content));
2329 }
2330 self.local_node
2331 .handle_pending_blobs(chain_id, blobs)
2332 .await?;
2333 if let Err(new_err) = self
2335 .local_node
2336 .handle_block_proposal(proposal.clone())
2337 .await
2338 {
2339 err = new_err;
2340 } else {
2341 continue;
2342 }
2343 }
2344 if let LocalNodeError::BlobsNotFound(blob_ids) = &err {
2345 self.update_local_node_with_blobs_from(
2346 blob_ids.clone(),
2347 slice::from_ref(remote_node),
2348 )
2349 .await?;
2350 if let Err(new_err) = self
2352 .local_node
2353 .handle_block_proposal(proposal.clone())
2354 .await
2355 {
2356 err = new_err;
2357 } else {
2358 continue;
2359 }
2360 }
2361 }
2362 if let LocalNodeError::EventsNotFound(event_ids) = &err {
2363 if let Err(error) =
2364 Box::pin(self.download_certificates_for_events(event_ids)).await
2365 {
2366 info!(
2367 remote_node = remote_node.address(),
2368 height = %local_height,
2369 proposer = %owner,
2370 %error,
2371 "skipping proposal from validator; failed to download events",
2372 );
2373 continue 'proposal_loop;
2374 }
2375 if let Err(new_err) = self
2377 .local_node
2378 .handle_block_proposal(proposal.clone())
2379 .await
2380 {
2381 err = new_err;
2382 } else {
2383 continue;
2384 }
2385 }
2386 if let LocalNodeError::WorkerError(WorkerError::ChainError(chain_err)) = &err {
2389 if let ChainError::MissingCrossChainUpdates { chain_id, bundles } = &**chain_err
2390 {
2391 let chain_id = *chain_id;
2392 let mut origin_heights: BTreeMap<ChainId, BlockHeight> = BTreeMap::new();
2398 for (origin, height) in bundles {
2399 let entry = origin_heights.entry(*origin).or_insert(*height);
2400 *entry = (*entry).max(*height);
2401 }
2402 stream::iter(origin_heights.into_iter().map(|(origin, height)| {
2403 self.download_sender_block_with_sending_ancestors(
2404 chain_id,
2405 origin,
2406 height,
2407 remote_node,
2408 )
2409 }))
2410 .buffer_unordered(self.options.max_joined_tasks)
2411 .collect::<Vec<_>>()
2412 .await
2413 .into_iter()
2414 .collect::<Result<(), _>>()?;
2415 if let Err(new_err) = self
2416 .local_node
2417 .handle_block_proposal(proposal.clone())
2418 .await
2419 {
2420 err = new_err;
2421 } else {
2422 continue 'proposal_loop;
2423 }
2424 }
2425 }
2426
2427 debug!(
2428 remote_node = remote_node.address(),
2429 proposer = %owner,
2430 height = %local_height,
2431 error = %err,
2432 "skipping proposal from validator",
2433 );
2434 }
2435 }
2436 Ok(())
2437 }
2438
2439 async fn try_process_locking_block_from(
2440 &self,
2441 remote_node: &RemoteNode<Env::ValidatorNode>,
2442 certificate: ValidatedBlockCertificate,
2443 ) -> Result<(), chain_client::Error> {
2444 let chain_id = certificate.inner().chain_id();
2445 let mut downloaded_blobs = HashSet::<BlobId>::new();
2446 let mut events = EventSetDownloader::new(self);
2447 loop {
2448 let result = self
2449 .handle_certificate::<ValidatedBlock>(certificate.clone())
2450 .await;
2451 if let Err(LocalNodeError::BlobsNotFound(blob_ids)) = &result {
2452 let new_blobs = filter_new(blob_ids, &downloaded_blobs);
2453 if !new_blobs.is_empty() {
2454 let mut blobs = Vec::new();
2455 for blob_id in &new_blobs {
2456 let blob_content = self
2457 .requests_scheduler
2458 .download_pending_blob(remote_node, chain_id, *blob_id)
2459 .await?;
2460 blobs.push(Blob::new(blob_content));
2461 }
2462 self.local_node
2463 .handle_pending_blobs(chain_id, blobs)
2464 .await?;
2465 downloaded_blobs.extend(new_blobs);
2466 continue;
2467 }
2468 }
2469 if let Err(LocalNodeError::EventsNotFound(event_ids)) = &result {
2470 if events.download_new(event_ids).await? {
2471 continue;
2472 }
2473 }
2474 result?;
2475 return Ok(());
2476 }
2477 }
2478
2479 async fn update_local_node_with_blobs_from(
2482 &self,
2483 blob_ids: Vec<BlobId>,
2484 remote_nodes: &[RemoteNode<Env::ValidatorNode>],
2485 ) -> Result<Vec<CacheArc<Blob>>, chain_client::Error> {
2486 let hedge_delay = self.options.blob_download_hedge_delay;
2487 let blob_ids = blob_ids.into_iter().collect::<BTreeSet<_>>();
2489 stream::iter(blob_ids.into_iter().map(|blob_id| {
2490 communicate_concurrently(
2491 remote_nodes,
2492 async move |remote_node| {
2493 let certificate = self
2494 .requests_scheduler
2495 .download_certificate_for_blob(&remote_node, blob_id)
2496 .await?;
2497 self.receive_sender_certificate(
2498 self.storage_client().cache_certificate(certificate),
2499 ReceiveCertificateMode::NeedsCheck,
2500 Some(vec![remote_node.clone()]),
2501 )
2502 .await?;
2503 let blob = self
2504 .local_node
2505 .storage_client()
2506 .read_blob(blob_id)
2507 .await?
2508 .ok_or_else(|| LocalNodeError::BlobsNotFound(vec![blob_id]))?;
2509 Result::<_, chain_client::Error>::Ok(blob)
2510 },
2511 hedge_delay,
2512 self.storage_client().clock(),
2513 )
2514 .map_err(move |errors| {
2515 for (validator, error) in &errors {
2516 warn!(
2517 %validator,
2518 %blob_id,
2519 %error,
2520 "failed to download certificate-for-blob from validator",
2521 );
2522 }
2523 chain_client::Error::CannotDownloadBlob(blob_id)
2524 })
2525 }))
2526 .buffer_unordered(self.options.max_joined_tasks)
2527 .collect::<Vec<_>>()
2528 .await
2529 .into_iter()
2530 .collect()
2531 }
2532
2533 #[instrument(level = "trace", skip(self, block))]
2543 async fn stage_block_execution(
2544 &self,
2545 block: ProposedBlock,
2546 round: Option<u32>,
2547 published_blobs: Vec<Blob>,
2548 policy: BundleExecutionPolicy,
2549 ) -> Result<(Block, ChainInfoResponse, HashSet<ChainId>), chain_client::Error> {
2550 let mut events = EventSetDownloader::new(self);
2551 loop {
2552 let result = self
2553 .local_node
2554 .stage_block_execution(
2555 block.clone(),
2556 round,
2557 published_blobs.clone(),
2558 policy.clone(),
2559 )
2560 .await;
2561 if let Err(LocalNodeError::BlobsNotFound(blob_ids)) = &result {
2562 let validators = self.validator_nodes().await?;
2563 self.update_local_node_with_blobs_from(blob_ids.clone(), &validators)
2564 .await?;
2565 continue; }
2567 if let Err(LocalNodeError::EventsNotFound(event_ids)) = &result {
2568 if events.download_new(event_ids).await? {
2569 continue; }
2571 }
2573 if let Ok((_, executed_block, _, _, _)) = &result {
2574 let hash = executed_block.hash();
2575 let notification = Notification {
2576 chain_id: executed_block.header.chain_id,
2577 reason: Reason::BlockExecuted {
2578 height: executed_block.header.height,
2579 hash,
2580 },
2581 };
2582 self.notifier.notify(&[notification]);
2583 }
2584 let (
2585 _modified_block,
2586 executed_block,
2587 response,
2588 _resource_tracker,
2589 never_reject_origins,
2590 ) = result?;
2591 return Ok((executed_block, response, never_reject_origins));
2592 }
2593 }
2594}
2595
2596fn filter_new<T: Clone + Eq + std::hash::Hash>(
2598 ids: &[T],
2599 already_downloaded: &HashSet<T>,
2600) -> Vec<T> {
2601 ids.iter()
2602 .filter(|id| !already_downloaded.contains(*id))
2603 .cloned()
2604 .collect()
2605}
2606
2607pub(crate) struct EventSetDownloader<'a, Env: Environment> {
2612 client: &'a Client<Env>,
2613 downloaded: HashSet<EventId>,
2614}
2615
2616impl<'a, Env: Environment> EventSetDownloader<'a, Env> {
2617 pub(crate) fn new(client: &'a Client<Env>) -> Self {
2618 Self {
2619 client,
2620 downloaded: HashSet::new(),
2621 }
2622 }
2623
2624 pub(crate) async fn download_new(
2630 &mut self,
2631 event_ids: &[EventId],
2632 ) -> Result<bool, chain_client::Error> {
2633 let new_events = filter_new(event_ids, &self.downloaded);
2634 if new_events.is_empty() {
2635 return Ok(false);
2636 }
2637 Box::pin(self.client.download_certificates_for_events(&new_events)).await?;
2638 self.downloaded.extend(new_events);
2639 Ok(true)
2640 }
2641}
2642
2643pub(crate) type ClockOf<Env> = <<Env as Environment>::Storage as linera_storage::Storage>::Clock;
2649
2650pub(crate) async fn hedged_fan_out<Peer, T, Err, NextPeer, NextFut, Op, OpFut>(
2662 first_peer: Peer,
2663 mut next_peer: NextPeer,
2664 operation: Op,
2665 hedge_schedule: impl Fn(usize) -> Duration,
2666 clock: &(impl linera_storage::Clock + Sync),
2669) -> Result<T, Vec<Err>>
2670where
2671 NextPeer: FnMut() -> NextFut,
2672 NextFut: Future<Output = Option<Peer>>,
2673 Op: Fn(Peer) -> OpFut,
2674 OpFut: Future<Output = Result<T, Err>>,
2675{
2676 use futures::future::{select, Either};
2677
2678 let mut in_flight = FuturesUnordered::new();
2679 let mut errors = vec![];
2680 let mut started = 0usize;
2681 let arm = |started: usize| clock.sleep_for(hedge_schedule(started));
2682
2683 in_flight.push(operation(first_peer));
2684 started += 1;
2685 let mut hedge = arm(started);
2686
2687 loop {
2688 if in_flight.is_empty() {
2689 match next_peer().await {
2691 Some(peer) => {
2692 in_flight.push(operation(peer));
2693 started += 1;
2694 hedge = arm(started);
2695 }
2696 None => return Err(errors),
2697 }
2698 continue;
2699 }
2700 match select(in_flight.next(), hedge).await {
2701 Either::Left((Some(Ok(value)), _)) => return Ok(value),
2703 Either::Left((Some(Err(error)), pending_hedge)) => {
2705 errors.push(error);
2706 hedge = pending_hedge;
2707 if let Some(peer) = next_peer().await {
2708 in_flight.push(operation(peer));
2709 started += 1;
2710 hedge = arm(started);
2711 }
2712 }
2713 Either::Left((None, pending_hedge)) => hedge = pending_hedge,
2715 Either::Right(((), _)) => match next_peer().await {
2717 Some(peer) => {
2718 in_flight.push(operation(peer));
2719 started += 1;
2720 hedge = arm(started);
2721 }
2722 None => break,
2723 },
2724 }
2725 }
2726
2727 while let Some(result) = in_flight.next().await {
2729 match result {
2730 Ok(value) => return Ok(value),
2731 Err(error) => errors.push(error),
2732 }
2733 }
2734 Err(errors)
2735}
2736
2737async fn communicate_concurrently<A, E, F, R, V>(
2743 nodes: &[RemoteNode<A>],
2744 f: F,
2745 hedge_delay: Duration,
2746 clock: &(impl linera_storage::Clock + Sync),
2747) -> Result<V, Vec<(ValidatorPublicKey, E)>>
2748where
2749 F: Clone + FnOnce(RemoteNode<A>) -> R,
2750 RemoteNode<A>: Clone,
2751 R: Future<Output = Result<V, E>>,
2752{
2753 let mut nodes = nodes.to_vec();
2754 nodes.shuffle(&mut rand::thread_rng());
2755 let mut nodes = nodes.into_iter();
2756 let Some(first_peer) = nodes.next() else {
2757 return Err(vec![]);
2758 };
2759 hedged_fan_out(
2760 first_peer,
2761 move || std::future::ready(nodes.next()),
2762 |node: RemoteNode<A>| {
2763 let fun = f.clone();
2764 async move {
2765 let public_key = node.public_key;
2766 fun(node).await.map_err(|err| (public_key, err))
2767 }
2768 },
2769 |started| {
2770 let k = u32::try_from(started).unwrap_or(u32::MAX);
2771 hedge_delay.saturating_mul(k).saturating_mul(k)
2772 },
2773 clock,
2774 )
2775 .await
2776}
2777
2778#[must_use]
2780pub struct AbortOnDrop(pub AbortHandle);
2781
2782impl Drop for AbortOnDrop {
2783 #[instrument(level = "trace", skip(self))]
2784 fn drop(&mut self) {
2785 self.0.abort();
2786 }
2787}
2788
2789#[derive(Clone, Serialize, Deserialize)]
2791pub struct PendingProposal {
2792 pub block: ProposedBlock,
2794 pub blobs: Vec<Blob>,
2796 #[serde(default)]
2799 pub auto_retry_outcome: Option<BlockExecutionOutcome>,
2800 #[serde(default)]
2802 pub round: Option<Round>,
2803}
2804
2805struct LagReport<N> {
2808 remote_node: RemoteNode<N>,
2809 chain_id: ChainId,
2810 error: NodeError,
2811}
2812
2813impl<N> LagReport<N> {
2814 fn remote_progress(&self) -> (Option<BlockHeight>, Option<Round>) {
2817 match &self.error {
2818 NodeError::UnexpectedBlockHeight {
2819 expected_block_height,
2820 ..
2821 } => (Some(*expected_block_height), None),
2822 NodeError::WrongRound(round) => (None, Some(*round)),
2823 _ => (None, None),
2824 }
2825 }
2826}
2827
2828enum ReceiveCertificateMode {
2829 NeedsCheck,
2830 AlreadyChecked,
2831}
2832
2833enum CheckCertificateResult {
2834 OldEpoch,
2836 FutureEpoch,
2839 New,
2840}
2841
2842impl CheckCertificateResult {
2843 fn into_result(self) -> Result<(), chain_client::Error> {
2844 match self {
2845 Self::OldEpoch => Err(chain_client::Error::CommitteeDeprecationError),
2846 Self::FutureEpoch => Err(chain_client::Error::CommitteeSynchronizationError),
2847 Self::New => Ok(()),
2848 }
2849 }
2850}
2851
2852#[cfg(not(target_arch = "wasm32"))]
2856pub async fn create_bytecode_blobs(
2857 contract: Bytecode,
2858 service: Bytecode,
2859 vm_runtime: VmRuntime,
2860 formats: Option<Vec<u8>>,
2861) -> (Vec<Blob>, ModuleId) {
2862 let formats_blob = formats.map(Blob::new_application_formats);
2863 let formats_blob_hash = formats_blob.as_ref().map(|blob| blob.id().hash);
2864 let (mut blobs, module_id) = match vm_runtime {
2865 VmRuntime::Wasm => {
2866 let (compressed_contract, compressed_service) =
2867 tokio::task::spawn_blocking(move || (contract.compress(), service.compress()))
2868 .await
2869 .expect("Compression should not panic");
2870 let contract_blob = Blob::new_contract_bytecode(compressed_contract);
2871 let service_blob = Blob::new_service_bytecode(compressed_service);
2872 let module_id = ModuleId::new_with_formats(
2873 contract_blob.id().hash,
2874 service_blob.id().hash,
2875 vm_runtime,
2876 formats_blob_hash,
2877 );
2878 (vec![contract_blob, service_blob], module_id)
2879 }
2880 VmRuntime::Evm => {
2881 let compressed_contract = contract.compress();
2882 let evm_contract_blob = Blob::new_evm_bytecode(compressed_contract);
2883 let module_id = ModuleId::new_with_formats(
2884 evm_contract_blob.id().hash,
2885 evm_contract_blob.id().hash,
2886 vm_runtime,
2887 formats_blob_hash,
2888 );
2889 (vec![evm_contract_blob], module_id)
2890 }
2891 };
2892 if let Some(blob) = formats_blob {
2893 blobs.push(blob);
2894 }
2895 (blobs, module_id)
2896}
2897
2898#[cfg(test)]
2899mod chain_modes_tests {
2900 use std::collections::BTreeSet;
2901
2902 use linera_base::{crypto::CryptoHash, identifiers::ChainId};
2903
2904 use super::{ChainModes, ListeningMode};
2905
2906 #[test]
2910 fn remove_mode_updates_full_set_only_for_full_chains() {
2911 let mut modes = ChainModes::default();
2912 let full = ChainId(CryptoHash::test_hash("full"));
2913 let events_only = ChainId(CryptoHash::test_hash("events-only"));
2914 modes.extend_mode(full, ListeningMode::FullChain);
2915 modes.extend_mode(events_only, ListeningMode::EventsOnly(BTreeSet::new()));
2916
2917 let full_hash_before = modes.full().hash();
2918 assert!(matches!(
2920 modes.remove_mode(&events_only),
2921 Some(ListeningMode::EventsOnly(_))
2922 ));
2923 assert!(modes.get(&events_only).is_none());
2924 assert!(modes.remove_mode(&events_only).is_none());
2926 assert_eq!(modes.full().hash(), full_hash_before);
2927 assert_eq!(modes.full().inner().0, BTreeSet::from([full]));
2928
2929 assert_eq!(modes.remove_mode(&full), Some(ListeningMode::FullChain));
2931 assert_ne!(modes.full().hash(), full_hash_before);
2932 assert!(modes.full().inner().0.is_empty());
2933 }
2934}
2935
2936#[cfg(test)]
2937mod communicate_concurrently_tests {
2938 use std::sync::{
2939 atomic::{AtomicUsize, Ordering},
2940 Arc,
2941 };
2942
2943 use linera_base::crypto::ValidatorKeypair;
2944 use linera_storage::TestClock;
2945
2946 use super::*;
2947
2948 fn test_node() -> RemoteNode<()> {
2949 RemoteNode {
2950 public_key: ValidatorKeypair::generate().public_key,
2951 node: (),
2952 }
2953 }
2954
2955 #[tokio::test]
2958 async fn does_not_wait_after_failures() {
2959 let clock = TestClock::new();
2960 let nodes: Vec<_> = (0..5).map(|_| test_node()).collect();
2961 let calls = Arc::new(AtomicUsize::new(0));
2962 let result: Result<(), Vec<(ValidatorPublicKey, &str)>> = communicate_concurrently(
2963 &nodes,
2964 {
2965 let calls = calls.clone();
2966 move |_node| {
2967 let calls = calls.clone();
2968 async move {
2969 calls.fetch_add(1, Ordering::SeqCst);
2970 Err("unavailable")
2971 }
2972 }
2973 },
2974 Duration::from_secs(30),
2975 &clock,
2976 )
2977 .await;
2978 assert_eq!(result.unwrap_err().len(), 5);
2979 assert_eq!(calls.load(Ordering::SeqCst), 5);
2980 assert_eq!(clock.current_time(), Timestamp::from(0));
2982 }
2983
2984 #[tokio::test]
2987 async fn fails_over_to_a_working_node() {
2988 let clock = TestClock::new();
2989 let nodes: Vec<_> = (0..5).map(|_| test_node()).collect();
2990 let working = nodes[3].public_key;
2991 let result: Result<u32, Vec<(ValidatorPublicKey, &str)>> = communicate_concurrently(
2992 &nodes,
2993 move |node| async move {
2994 if node.public_key == working {
2995 Ok(42)
2996 } else {
2997 Err("unavailable")
2998 }
2999 },
3000 Duration::from_secs(30),
3001 &clock,
3002 )
3003 .await;
3004 assert_eq!(result.unwrap(), 42);
3005 assert_eq!(clock.current_time(), Timestamp::from(0));
3006 }
3007
3008 fn peer_source(n: usize) -> impl FnMut() -> std::future::Ready<Option<usize>> {
3015 let mut next = 1usize;
3016 move || {
3017 let peer = (next < n).then_some(next);
3018 next += 1;
3019 std::future::ready(peer)
3020 }
3021 }
3022
3023 #[tokio::test]
3026 async fn slow_first_peer_is_hedged_but_not_cancelled() {
3027 let clock = TestClock::new();
3028 let delay = Duration::from_secs(1);
3029 let order = Arc::new(std::sync::Mutex::new(Vec::new()));
3030 let (started_tx, mut started_rx) = tokio::sync::mpsc::unbounded_channel();
3031 let release0 = Arc::new(tokio::sync::Notify::new());
3032 let gates = [release0.clone(), Arc::new(tokio::sync::Notify::new())];
3033
3034 let operation = {
3035 let order = order.clone();
3036 move |peer: usize| {
3037 let started_tx = started_tx.clone();
3038 let order = order.clone();
3039 let gate = gates[peer].clone();
3040 async move {
3041 order.lock().unwrap().push(peer);
3042 started_tx.send(peer).unwrap();
3043 gate.notified().await;
3044 if peer == 0 {
3045 Ok::<u32, &str>(42)
3046 } else {
3047 Err("slow loser")
3048 }
3049 }
3050 }
3051 };
3052
3053 let fan = tokio::spawn({
3054 let clock = clock.clone();
3055 async move {
3056 hedged_fan_out(
3057 0usize,
3058 peer_source(2),
3059 operation,
3060 move |k| delay * u32::try_from(k).unwrap_or(u32::MAX),
3061 &clock,
3062 )
3063 .await
3064 }
3065 });
3066
3067 assert_eq!(started_rx.recv().await, Some(0));
3069 clock.add(TimeDelta::from_duration(delay));
3071 assert_eq!(started_rx.recv().await, Some(1));
3072 release0.notify_one();
3074 assert_eq!(fan.await.unwrap(), Ok(42));
3075 assert_eq!(*order.lock().unwrap(), vec![0, 1]);
3076 }
3077
3078 #[tokio::test]
3082 async fn hedge_schedule_determines_start_times() {
3083 let clock = TestClock::new();
3084 let unit = Duration::from_secs(1);
3085 let starts = Arc::new(std::sync::Mutex::new(Vec::new()));
3086 let (started_tx, mut started_rx) = tokio::sync::mpsc::unbounded_channel();
3087
3088 let operation = {
3089 let starts = starts.clone();
3090 let clock = clock.clone();
3091 move |peer: usize| {
3092 let started_tx = started_tx.clone();
3093 let starts = starts.clone();
3094 let clock = clock.clone();
3095 async move {
3096 starts.lock().unwrap().push((peer, clock.current_time()));
3097 started_tx.send(peer).unwrap();
3098 std::future::pending::<()>().await;
3099 Ok::<u32, &str>(0)
3100 }
3101 }
3102 };
3103
3104 let fan = tokio::spawn({
3105 let clock = clock.clone();
3106 async move {
3107 hedged_fan_out(
3108 0usize,
3109 peer_source(4),
3110 operation,
3111 move |k| {
3112 let k = u32::try_from(k).unwrap_or(u32::MAX);
3113 unit * k * k
3114 },
3115 &clock,
3116 )
3117 .await
3118 }
3119 });
3120
3121 assert_eq!(started_rx.recv().await, Some(0));
3123 clock.add(TimeDelta::from_duration(unit));
3124 assert_eq!(started_rx.recv().await, Some(1));
3125 clock.add(TimeDelta::from_duration(unit * 4));
3126 assert_eq!(started_rx.recv().await, Some(2));
3127 clock.add(TimeDelta::from_duration(unit * 9));
3128 assert_eq!(started_rx.recv().await, Some(3));
3129
3130 assert_eq!(
3132 *starts.lock().unwrap(),
3133 vec![
3134 (0, Timestamp::from(0)),
3135 (1, Timestamp::from(1_000_000)),
3136 (2, Timestamp::from(5_000_000)),
3137 (3, Timestamp::from(14_000_000)),
3138 ]
3139 );
3140 fan.abort();
3141 }
3142
3143 #[tokio::test]
3146 async fn frozen_clock_disables_the_hedge() {
3147 let clock = TestClock::new();
3148 let (started_tx, mut started_rx) = tokio::sync::mpsc::unbounded_channel();
3149 let release0 = Arc::new(tokio::sync::Notify::new());
3150
3151 let operation = {
3152 let release0 = release0.clone();
3153 move |peer: usize| {
3154 let started_tx = started_tx.clone();
3155 let release0 = release0.clone();
3156 async move {
3157 started_tx.send(peer).unwrap();
3158 if peer == 0 {
3159 release0.notified().await;
3160 Ok::<u32, &str>(7)
3161 } else {
3162 Ok(99)
3164 }
3165 }
3166 }
3167 };
3168
3169 let fan = tokio::spawn({
3170 let clock = clock.clone();
3171 async move {
3172 hedged_fan_out(
3173 0usize,
3174 peer_source(2),
3175 operation,
3176 move |k| Duration::from_secs(1) * u32::try_from(k).unwrap_or(u32::MAX),
3177 &clock,
3178 )
3179 .await
3180 }
3181 });
3182
3183 assert_eq!(started_rx.recv().await, Some(0));
3184 tokio::task::yield_now().await;
3186 assert!(
3187 started_rx.try_recv().is_err(),
3188 "the hedge must not fire while the clock is frozen"
3189 );
3190 release0.notify_one();
3192 assert_eq!(fan.await.unwrap(), Ok(7));
3193 assert_eq!(clock.current_time(), Timestamp::from(0));
3194 }
3195
3196 #[tokio::test]
3199 async fn failure_fails_over_without_waiting_out_the_hedge() {
3200 let clock = TestClock::new();
3201 let (started_tx, mut started_rx) = tokio::sync::mpsc::unbounded_channel();
3202 let release0 = Arc::new(tokio::sync::Notify::new());
3203
3204 let operation = {
3205 let release0 = release0.clone();
3206 move |peer: usize| {
3207 let started_tx = started_tx.clone();
3208 let release0 = release0.clone();
3209 async move {
3210 started_tx.send(peer).unwrap();
3211 match peer {
3212 0 => {
3213 release0.notified().await;
3214 Err::<u32, &str>("dead")
3215 }
3216 1 => Err("dead"),
3217 _ => Ok(55),
3218 }
3219 }
3220 }
3221 };
3222
3223 let fan = tokio::spawn({
3224 let clock = clock.clone();
3225 async move {
3226 hedged_fan_out(
3227 0usize,
3228 peer_source(3),
3229 operation,
3230 move |k| Duration::from_secs(100) * u32::try_from(k).unwrap_or(u32::MAX),
3232 &clock,
3233 )
3234 .await
3235 }
3236 });
3237
3238 assert_eq!(started_rx.recv().await, Some(0));
3239 release0.notify_one();
3241 assert_eq!(started_rx.recv().await, Some(1));
3242 assert_eq!(started_rx.recv().await, Some(2));
3243 assert_eq!(fan.await.unwrap(), Ok(55));
3244 assert_eq!(clock.current_time(), Timestamp::from(0));
3246 }
3247}