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, MaybeSendBoxFuture, ProcessConfirmedBlockMode,
63 CHAIN_INFO_MAX_RECEIVED_LOG_ENTRIES,
64};
65
66const RECEIVED_LOG_PAGES_PER_PROGRESS_MESSAGE: usize = 25;
70
71pub mod chain_client;
73pub use chain_client::ChainClient;
74
75pub use crate::data_types::ClientOutcome;
76
77#[cfg(test)]
78#[path = "../unit_tests/client_tests.rs"]
79mod client_tests;
80pub mod requests_scheduler;
81
82pub use requests_scheduler::{RequestsScheduler, RequestsSchedulerConfig, ScoringWeights};
83mod received_log;
84mod validator_trackers;
85
86#[cfg(with_metrics)]
87pub(crate) mod metrics {
88 use linera_base::prometheus_util::{
89 exponential_bucket_interval, exponential_bucket_latencies, register_histogram_vec,
90 register_int_counter, register_int_counter_vec,
91 };
92 use prometheus::{HistogramVec, IntCounter, IntCounterVec};
93
94 linera_base::declare_metrics! {
95 pub static PROCESS_INBOX_WITHOUT_PREPARE_LATENCY: HistogramVec =
96 register_histogram_vec(
97 "process_inbox_latency",
98 "process_inbox latency",
99 &[],
100 exponential_bucket_latencies(60_000.0),
101 );
102
103 pub static PREPARE_CHAIN_LATENCY: HistogramVec =
104 register_histogram_vec(
105 "prepare_chain_latency",
106 "prepare_chain latency",
107 &[],
108 exponential_bucket_latencies(60_000.0),
109 );
110
111 pub static SYNCHRONIZE_CHAIN_STATE_LATENCY: HistogramVec =
112 register_histogram_vec(
113 "synchronize_chain_state_latency",
114 "synchronize_chain_state latency",
115 &[],
116 exponential_bucket_latencies(600_000.0),
117 );
118
119 pub static EXECUTE_BLOCK_LATENCY: HistogramVec =
120 register_histogram_vec(
121 "execute_block_latency",
122 "execute_block latency",
123 &[],
124 exponential_bucket_latencies(10_000.0),
125 );
126
127 pub static FIND_RECEIVED_CERTIFICATES_LATENCY: HistogramVec =
128 register_histogram_vec(
129 "find_received_certificates_latency",
130 "find_received_certificates latency",
131 &[],
132 exponential_bucket_latencies(3_600_000.0),
133 );
134
135 pub static FIND_RECEIVED_CERTIFICATES_LOG_ENTRIES: HistogramVec =
136 register_histogram_vec(
137 "find_received_certificates_log_entries",
138 "Number of received-log entries collected from the validators, per call",
139 &[],
140 exponential_bucket_interval(1.0, 1_000_000.0),
141 );
142
143 pub static FIND_RECEIVED_CERTIFICATES_SENDER_CHAINS: HistogramVec =
144 register_histogram_vec(
145 "find_received_certificates_sender_chains",
146 "Number of distinct sender chains to synchronize, per call",
147 &[],
148 exponential_bucket_interval(1.0, 100_000.0),
149 );
150
151 pub static SENDER_CERTIFICATES_DISCOVERED_TOTAL: IntCounter =
152 register_int_counter(
153 "sender_certificates_discovered_total",
154 "Total number of sender certificates advertised by the validators' received logs",
155 );
156
157 pub static SENDER_CERTIFICATES_MISSING_TOTAL: IntCounter =
158 register_int_counter(
159 "sender_certificates_missing_total",
160 "Total number of sender certificates not already known locally, hence downloaded",
161 );
162
163 pub static BLOCK_STAGING_FAILURES_TOTAL: IntCounterVec =
164 register_int_counter_vec(
165 "block_staging_failures_total",
166 "Total number of client block staging (execute_block) failures, labelled by error type",
167 &["error_type"],
168 );
169 }
170}
171
172pub static DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE: u64 = 500;
174pub static DEFAULT_CERTIFICATE_UPLOAD_BATCH_SIZE: usize = 500;
176pub static DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE: usize = 20_000;
178pub static DEFAULT_MAX_EVENT_STREAM_QUERIES: usize = 1000;
180pub static DEFAULT_MAX_CONCURRENT_BATCH_DOWNLOADS: usize = 1;
182
183#[derive(Debug, Clone, Copy)]
185#[allow(missing_docs)]
186pub enum TimingType {
187 ExecuteOperations,
188 ExecuteBlock,
189 SubmitBlockProposal,
190 UpdateValidators,
191}
192
193#[derive(Debug, Clone, PartialEq, Eq)]
198pub enum ListeningMode {
199 FullChain,
202 FollowChain,
206 EventsOnly(BTreeSet<StreamId>),
208}
209
210impl PartialOrd for ListeningMode {
211 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
212 match (self, other) {
213 (ListeningMode::FullChain, ListeningMode::FullChain) => Some(Ordering::Equal),
214 (ListeningMode::FullChain, _) => Some(Ordering::Greater),
215 (_, ListeningMode::FullChain) => Some(Ordering::Less),
216 (ListeningMode::FollowChain, ListeningMode::FollowChain) => Some(Ordering::Equal),
217 (ListeningMode::FollowChain, ListeningMode::EventsOnly(_)) => Some(Ordering::Greater),
218 (ListeningMode::EventsOnly(_), ListeningMode::FollowChain) => Some(Ordering::Less),
219 (ListeningMode::EventsOnly(a), ListeningMode::EventsOnly(b)) => {
220 if a == b {
221 Some(Ordering::Equal)
222 } else if a.is_superset(b) {
223 Some(Ordering::Greater)
224 } else if b.is_superset(a) {
225 Some(Ordering::Less)
226 } else {
227 None
228 }
229 }
230 }
231 }
232}
233
234impl ListeningMode {
235 pub fn is_relevant(&self, reason: &Reason) -> bool {
238 match (reason, self) {
239 (Reason::NewEvents { .. }, ListeningMode::FollowChain | ListeningMode::FullChain) => {
242 false
243 }
244 (_, ListeningMode::FullChain) => true,
246 (Reason::NewBlock { .. }, ListeningMode::FollowChain) => true,
249 (_, ListeningMode::FollowChain) => false,
250 (Reason::NewEvents { event_streams, .. }, ListeningMode::EventsOnly(relevant)) => {
252 relevant.intersection(event_streams).next().is_some()
253 }
254 (_, ListeningMode::EventsOnly(_)) => false,
255 }
256 }
257
258 pub fn extend(&mut self, other: Option<ListeningMode>) {
260 match (self, other) {
261 (_, None) => (),
262 (ListeningMode::FullChain, _) => (),
263 (mode, Some(ListeningMode::FullChain)) => {
264 *mode = ListeningMode::FullChain;
265 }
266 (ListeningMode::FollowChain, _) => (),
267 (mode, Some(ListeningMode::FollowChain)) => {
268 *mode = ListeningMode::FollowChain;
269 }
270 (
271 ListeningMode::EventsOnly(self_events),
272 Some(ListeningMode::EventsOnly(other_events)),
273 ) => {
274 self_events.extend(other_events);
275 }
276 }
277 }
278
279 pub fn is_follow_only(&self) -> bool {
282 !matches!(self, ListeningMode::FullChain)
283 }
284
285 pub fn is_full(&self) -> bool {
288 matches!(self, ListeningMode::FullChain)
289 }
290
291 pub fn should_sync_chain_state(&self) -> bool {
293 match self {
294 ListeningMode::FullChain | ListeningMode::FollowChain => true,
295 ListeningMode::EventsOnly(_) => false,
296 }
297 }
298}
299
300#[derive(Debug)]
310pub struct ChainModes {
311 modes: BTreeMap<ChainId, ListeningMode>,
312 full: Arc<Hashed<ChainIdSet>>,
313}
314
315impl Default for ChainModes {
316 fn default() -> Self {
317 Self::new(BTreeMap::new())
318 }
319}
320
321impl ChainModes {
322 pub fn new(modes: BTreeMap<ChainId, ListeningMode>) -> Self {
324 let full = Self::compute_full(&modes);
325 Self { modes, full }
326 }
327
328 fn compute_full(modes: &BTreeMap<ChainId, ListeningMode>) -> Arc<Hashed<ChainIdSet>> {
329 Arc::new(Hashed::new(ChainIdSet(
330 modes
331 .iter()
332 .filter(|(_, mode)| mode.is_full())
333 .map(|(id, _)| *id)
334 .collect(),
335 )))
336 }
337
338 pub fn full(&self) -> Arc<Hashed<ChainIdSet>> {
340 self.full.clone()
341 }
342
343 pub fn get(&self, chain_id: &ChainId) -> Option<&ListeningMode> {
345 self.modes.get(chain_id)
346 }
347
348 pub fn extend_mode(&mut self, chain_id: ChainId, mode: ListeningMode) -> ListeningMode {
352 let entry = self
353 .modes
354 .entry(chain_id)
355 .or_insert_with(|| ListeningMode::EventsOnly(BTreeSet::new()));
356 let was_full = entry.is_full();
357 entry.extend(Some(mode));
358 let result = entry.clone();
359 if !was_full && result.is_full() {
360 self.full = Self::compute_full(&self.modes);
361 }
362 result
363 }
364
365 pub fn remove_mode(&mut self, chain_id: &ChainId) -> Option<ListeningMode> {
368 let removed = self.modes.remove(chain_id)?;
369 if removed.is_full() {
370 self.full = Self::compute_full(&self.modes);
371 }
372 Some(removed)
373 }
374}
375
376pub struct Client<Env: Environment> {
378 environment: Env,
379 pub local_node: LocalNodeClient<Env::Storage>,
382 requests_scheduler: Arc<RequestsScheduler<Env>>,
384 admin_chain_id: ChainId,
386 chain_modes: Arc<RwLock<ChainModes>>,
389 notifier: Arc<ChannelNotifier<Notification>>,
391 chains: papaya::HashMap<ChainId, chain_client::State>,
393 options: chain_client::Options,
395}
396
397type ReceiveSenderCertificateFuture<'a> = MaybeSendBoxFuture<'a, Result<(), chain_client::Error>>;
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 type CertificateBatchFuture =
765 crate::MaybeSendBoxFuture<'static, Result<Vec<ConfirmedBlockCertificate>, NodeError>>;
766
767 let max_concurrent = self.options.max_concurrent_batch_downloads;
768 let batch_size = self.options.certificate_download_batch_size;
769 let (sender, mut receiver) = tokio::sync::mpsc::channel(max_concurrent);
770 let scheduler = self.requests_scheduler.clone();
771 let remote = remote_node.clone();
772
773 let download_task = linera_base::Task::spawn(async move {
774 let mut download_height = next_height;
775 let mut in_flight = FuturesOrdered::<CertificateBatchFuture>::new();
776
777 let try_enqueue = |in_flight: &mut FuturesOrdered<CertificateBatchFuture>,
778 download_height: &mut BlockHeight| {
779 if *download_height >= stop {
780 return;
781 }
782 let limit = u64::from(stop)
783 .saturating_sub(u64::from(*download_height))
784 .min(batch_size);
785 let height = *download_height;
786 let scheduler = scheduler.clone();
787 let remote = remote.clone();
788 in_flight.push_back(Box::pin(async move {
789 scheduler
790 .download_certificates(&remote, chain_id, height, limit)
791 .await
792 }));
793 *download_height = BlockHeight(u64::from(*download_height) + limit);
794 };
795
796 while in_flight.len() < max_concurrent && download_height < stop {
797 try_enqueue(&mut in_flight, &mut download_height);
798 }
799
800 while let Some(result) = in_flight.next().await {
801 if sender.send(result).await.is_err() {
802 break;
803 }
804 try_enqueue(&mut in_flight, &mut download_height);
805 }
806 });
807
808 while let Some(result) = receiver.recv().await {
810 let certificates = result?;
811 let Some(info) = self
812 .process_certificates(
813 slice::from_ref(remote_node),
814 certificates,
815 until_block_time,
816 ProcessConfirmedBlockMode::Execute,
817 )
818 .await?
819 else {
820 break;
821 };
822 assert!(info.next_block_height >= next_height);
823 next_height = info.next_block_height;
824 last_info = info;
825 }
826 download_task.await;
829 Ok(last_info)
830 }
831
832 async fn download_blobs(
833 &self,
834 remote_nodes: &[RemoteNode<Env::ValidatorNode>],
835 blob_ids: &[BlobId],
836 ) -> Result<(), chain_client::Error> {
837 let blobs = &self
838 .requests_scheduler
839 .download_blobs(
840 remote_nodes,
841 blob_ids,
842 self.options.blob_download_hedge_delay,
843 )
844 .await?
845 .ok_or_else(|| {
846 chain_client::Error::RemoteNodeError(NodeError::BlobsNotFound(blob_ids.to_vec()))
847 })?;
848 self.local_node.store_blobs(blobs).await.map_err(Into::into)
849 }
850
851 #[instrument(level = "trace", skip_all)]
856 pub(crate) async fn download_certificates_for_events(
857 &self,
858 event_ids: &[EventId],
859 ) -> Result<(), chain_client::Error> {
860 let mut validators = self.validator_nodes().await?;
861 let hedge_delay = self.options.certificate_batch_download_hedge_delay;
862 let mut remaining_event_ids = event_ids.to_vec();
863
864 while !remaining_event_ids.is_empty() {
865 let remaining_ref = &remaining_event_ids;
866 validators.shuffle(&mut rand::thread_rng());
867 let result = communicate_concurrently(
868 &validators,
869 move |remote_node| {
870 let validator_key = remote_node.public_key;
871 let validator_address = remote_node.address();
872 Box::pin(async move {
873 let heights = remote_node
875 .node
876 .event_block_heights(remaining_ref.to_vec())
877 .await?;
878
879 let mut chain_heights = BTreeMap::<_, BTreeSet<_>>::new();
881 let mut expected_events = BTreeMap::<_, HashSet<EventId>>::new();
882 let mut unresolved = Vec::new();
883 for (event_id, maybe_height) in remaining_ref.iter().zip(heights) {
884 if let Some(height) = maybe_height {
885 chain_heights
886 .entry(event_id.chain_id)
887 .or_default()
888 .insert(height);
889 expected_events
890 .entry((event_id.chain_id, height))
891 .or_default()
892 .insert(event_id.clone());
893 } else {
894 unresolved.push(event_id.clone());
895 }
896 }
897 if chain_heights.is_empty() {
898 return Err(chain_client::Error::from(NodeError::EventsNotFound(remaining_ref.clone())));
900 }
901
902 let mut checked_certificates = Vec::<ConfirmedBlockCertificate>::new();
904 for (chain_id, heights) in chain_heights {
905 let heights_vec = heights.into_iter().collect::<Vec<_>>();
906 let certificates = self
907 .requests_scheduler
908 .download_certificates_by_heights(
909 &remote_node,
910 chain_id,
911 heights_vec,
912 )
913 .await?;
914 for cert in &certificates {
915 let block = cert.block();
917 let block_event_ids = block.event_ids().collect::<HashSet<_>>();
918 if let Some(expected_event_ids) =
919 expected_events.get(&(chain_id, block.header.height))
920 {
921 if !expected_event_ids.is_subset(&block_event_ids) {
922 tracing::debug!(
923 %validator_address, ?expected_event_ids, ?block_event_ids,
924 "validator lied about events in block."
925 );
926 return Err(NodeError::UnexpectedCertificateValue.into());
927 }
928 }
929 }
930 for cert in certificates {
931 self.check_certificate(&cert)
932 .await
933 .map_err(|error| {
934 tracing::debug!(
935 %validator_address, %error,
936 "invalid certificate"
937 );
938 error
939 })?
940 .into_result()
941 .map_err(|error| {
942 tracing::debug!(
943 %validator_address, %error,
944 "could not check certificate"
945 );
946 error
947 })?;
948 checked_certificates.push(cert);
949 }
950 }
951 Ok((checked_certificates, unresolved, validator_key))
952 })
953 },
954 hedge_delay,
955 self.storage_client().clock(),
956 )
957 .await;
958
959 match result {
960 Ok((certificates, unresolved, validator_key)) => {
961 for certificate in certificates {
962 let mode = ReceiveCertificateMode::AlreadyChecked;
963 self.receive_sender_certificate(
964 self.storage_client().cache_certificate(certificate),
965 mode,
966 None,
967 )
968 .await?;
969 }
970 validators.retain(|node| node.public_key != validator_key);
971 remaining_event_ids = unresolved;
972 }
973 Err(errors) => {
974 for (validator, error) in &errors {
975 warn!(
976 %validator,
977 %error,
978 "failed to download event certificates from validator",
979 );
980 }
981 return Err(NodeError::EventsNotFound(remaining_event_ids).into());
983 }
984 }
985 }
986 Ok(())
987 }
988
989 #[instrument(level = "trace", skip_all)]
999 async fn bootstrap_chain_from_checkpoint(
1000 &self,
1001 remote_node: &RemoteNode<Env::ValidatorNode>,
1002 chain_id: ChainId,
1003 checkpoint_height: BlockHeight,
1004 ) -> Result<(), chain_client::Error> {
1005 let local_next = match self.local_node.chain_info(chain_id).await {
1006 Ok(info) => info.next_block_height,
1007 Err(LocalNodeError::BlobsNotFound(_)) => BlockHeight::ZERO,
1012 Err(err) => return Err(err.into()),
1013 };
1014 if local_next > checkpoint_height {
1015 return Ok(());
1016 }
1017 let certificates = remote_node
1018 .download_certificates_by_heights(chain_id, vec![checkpoint_height])
1019 .await?;
1020 if certificates.is_empty() {
1021 return Ok(());
1024 }
1025 self.process_certificates(
1031 slice::from_ref(remote_node),
1032 certificates,
1033 None,
1034 ProcessConfirmedBlockMode::Execute,
1035 )
1036 .await?;
1037 Ok(())
1038 }
1039
1040 #[instrument(level = "trace", skip_all)]
1045 async fn process_certificates(
1046 &self,
1047 remote_nodes: &[RemoteNode<Env::ValidatorNode>],
1048 certificates: Vec<ConfirmedBlockCertificate>,
1049 until_block_time: Option<Timestamp>,
1050 mode: ProcessConfirmedBlockMode,
1051 ) -> Result<Option<Box<ChainInfo>>, chain_client::Error> {
1052 let mut info = None;
1053 let created_blob_ids = certificates
1058 .iter()
1059 .flat_map(|certificate| certificate.value().block().created_blob_ids())
1060 .collect::<BTreeSet<BlobId>>();
1061 let required_blob_ids = certificates
1062 .iter()
1063 .flat_map(|certificate| certificate.value().required_blob_ids())
1064 .filter(|blob_id| !created_blob_ids.contains(blob_id))
1065 .collect::<Vec<_>>();
1066
1067 match self
1068 .local_node
1069 .read_blob_states_from_storage(&required_blob_ids)
1070 .await
1071 {
1072 Err(LocalNodeError::BlobsNotFound(blob_ids)) => {
1073 self.download_blobs(remote_nodes, &blob_ids).await?;
1074 }
1075 x => {
1076 x?;
1077 }
1078 }
1079
1080 for certificate in certificates {
1081 if let Some(until) = until_block_time {
1082 if certificate.value().block().header.timestamp >= until {
1083 break;
1084 }
1085 }
1086 let response = self
1087 .handle_certificate_with_retry(&certificate, remote_nodes, mode)
1088 .await?;
1089 info = Some(response.info);
1090 }
1091
1092 Ok(info)
1093 }
1094
1095 async fn handle_certificate_with_retry(
1099 &self,
1100 certificate: &ConfirmedBlockCertificate,
1101 nodes: &[RemoteNode<Env::ValidatorNode>],
1102 mode: ProcessConfirmedBlockMode,
1103 ) -> Result<ChainInfoResponse, chain_client::Error> {
1104 let mut downloaded_blobs = HashSet::<BlobId>::new();
1105 let mut downloaded_blocks = HashSet::<CryptoHash>::new();
1106 let mut events = EventSetDownloader::new(self);
1107 loop {
1108 let result = self
1109 .handle_confirmed_certificate(certificate.clone(), mode)
1110 .await;
1111 if let Err(LocalNodeError::BlobsNotFound(blob_ids)) = &result {
1112 let new_blobs = filter_new(blob_ids, &downloaded_blobs);
1113 if !new_blobs.is_empty() {
1114 self.download_blobs(nodes, &new_blobs).await?;
1115 downloaded_blobs.extend(new_blobs);
1116 continue;
1117 }
1118 }
1119 if let Err(LocalNodeError::BlocksNotFound(hashes)) = &result {
1120 let new_blocks = filter_new(hashes, &downloaded_blocks);
1121 if !new_blocks.is_empty() {
1122 self.download_pre_checkpoint_blocks(nodes, &new_blocks)
1123 .await?;
1124 downloaded_blocks.extend(new_blocks);
1125 continue;
1126 }
1127 }
1128 if let Err(LocalNodeError::EventsNotFound(event_ids)) = &result {
1129 if events.download_new(event_ids).await? {
1130 continue;
1131 }
1132 }
1133 return Ok(result?);
1134 }
1135 }
1136
1137 async fn download_pre_checkpoint_blocks(
1144 &self,
1145 nodes: &[RemoteNode<Env::ValidatorNode>],
1146 hashes: &[CryptoHash],
1147 ) -> Result<(), chain_client::Error> {
1148 for hash in hashes {
1149 let mut last_error = None;
1150 for node in nodes {
1151 match node.node.download_certificate(*hash).await {
1152 Ok(certificate) => {
1153 Box::pin(self.handle_certificate_with_retry(
1154 &certificate,
1155 nodes,
1156 ProcessConfirmedBlockMode::Auto,
1157 ))
1158 .await?;
1159 last_error = None;
1160 break;
1161 }
1162 Err(error) => last_error = Some(error),
1163 }
1164 }
1165 if let Some(error) = last_error {
1166 return Err(error.into());
1167 }
1168 }
1169 Ok(())
1170 }
1171
1172 async fn handle_certificate<T: ProcessableCertificate>(
1173 &self,
1174 certificate: T::Certificate,
1175 ) -> Result<ChainInfoResponse, LocalNodeError> {
1176 self.local_node
1177 .handle_certificate::<T>(certificate, &self.notifier)
1178 .await
1179 }
1180
1181 async fn handle_confirmed_certificate(
1182 &self,
1183 certificate: ConfirmedBlockCertificate,
1184 mode: ProcessConfirmedBlockMode,
1185 ) -> Result<ChainInfoResponse, LocalNodeError> {
1186 self.local_node
1187 .handle_confirmed_certificate(certificate, mode, &self.notifier)
1188 .await
1189 }
1190
1191 pub async fn admin_committee(&self) -> Result<(Epoch, Arc<Committee>), LocalNodeError> {
1193 let info = self.local_node.chain_info(self.admin_chain_id).await?;
1194 let hash = info
1195 .committee_hash
1196 .ok_or(LocalNodeError::InactiveChain(self.admin_chain_id))?;
1197 let committee = self
1198 .storage_client()
1199 .get_or_load_committee_by_hash(hash)
1200 .await?;
1201 Ok((info.epoch, committee))
1202 }
1203
1204 async fn validator_nodes(
1206 &self,
1207 ) -> Result<Vec<RemoteNode<Env::ValidatorNode>>, chain_client::Error> {
1208 let (_, committee) = self.admin_committee().await?;
1209 Ok(self.make_nodes(&committee)?)
1210 }
1211
1212 fn make_nodes(
1214 &self,
1215 committee: &Committee,
1216 ) -> Result<Vec<RemoteNode<Env::ValidatorNode>>, NodeError> {
1217 Ok(self
1218 .validator_node_provider()
1219 .make_nodes(committee)?
1220 .map(|(public_key, node)| RemoteNode { public_key, node })
1221 .collect())
1222 }
1223
1224 pub async fn get_chain_description_blob(
1227 &self,
1228 chain_id: ChainId,
1229 ) -> Result<Arc<Blob>, chain_client::Error> {
1230 let chain_desc_id = BlobId::new(chain_id.0, BlobType::ChainDescription);
1231 let blob = self
1232 .local_node
1233 .storage_client()
1234 .read_blob(chain_desc_id)
1235 .await?;
1236 if let Some(blob) = blob {
1237 return Ok(blob.into_std());
1239 }
1240 self.synchronize_chain_state(self.admin_chain_id).await?;
1242 let nodes = self.validator_nodes().await?;
1243 Ok(self
1244 .update_local_node_with_blobs_from(vec![chain_desc_id], &nodes)
1245 .await?
1246 .pop()
1247 .unwrap() .into_std())
1249 }
1250
1251 pub async fn get_chain_description(
1254 &self,
1255 chain_id: ChainId,
1256 ) -> Result<ChainDescription, chain_client::Error> {
1257 let blob = self.get_chain_description_blob(chain_id).await?;
1258 Ok(bcs::from_bytes(blob.bytes())?)
1259 }
1260
1261 pub async fn get_application_description_blob(
1266 &self,
1267 application_id: ApplicationId,
1268 ) -> Result<Arc<Blob>, chain_client::Error> {
1269 let blob_id = application_id.description_blob_id();
1270 let blob = self.local_node.storage_client().read_blob(blob_id).await?;
1271 if let Some(blob) = blob {
1272 return Ok(blob.into_std());
1274 }
1275 Box::pin(self.synchronize_chain_state(self.admin_chain_id)).await?;
1277 let nodes = self.validator_nodes().await?;
1278 Ok(self
1279 .update_local_node_with_blobs_from(vec![blob_id], &nodes)
1280 .await?
1281 .pop()
1282 .unwrap() .into_std())
1284 }
1285
1286 pub async fn get_application_description(
1289 &self,
1290 application_id: ApplicationId,
1291 ) -> Result<ApplicationDescription, chain_client::Error> {
1292 let blob = self
1293 .get_application_description_blob(application_id)
1294 .await?;
1295 Ok(bcs::from_bytes(blob.bytes())?)
1296 }
1297
1298 #[instrument(level = "trace", skip_all)]
1300 pub(crate) async fn finalize_block(
1301 self: &Arc<Self>,
1302 committee: &Committee,
1303 certificate: ValidatedBlockCertificate,
1304 ) -> Result<ConfirmedBlockCertificate, chain_client::Error> {
1305 debug!(round = %certificate.round(), "Submitting block for confirmation");
1306 let hashed_value = ConfirmedBlock::new(certificate.block().clone());
1307 let full_justification = certificate.full_justification();
1312 let finalize_action = CommunicateAction::FinalizeBlock {
1313 certificate: Box::new(certificate),
1314 delivery: self.options.cross_chain_message_delivery,
1315 };
1316 let quorum = self
1317 .communicate_chain_action(committee, finalize_action, hashed_value)
1318 .await?;
1319 let justification = if quorum.first_round() {
1325 JustificationChain::default()
1326 } else {
1327 full_justification
1328 };
1329 ensure!(
1332 quorum.justification_commitment() == justification.commitment(quorum.hash()),
1333 chain_client::Error::ProtocolError(
1334 "A quorum confirmed with a justification commitment that does not match the \
1335 validated certificate's justification chain",
1336 )
1337 );
1338 let certificate = ConfirmedBlockCertificate::from_parts(quorum, justification);
1339 self.receive_certificate_with_checked_signatures(
1340 certificate.clone(),
1341 ProcessConfirmedBlockMode::Execute,
1342 )
1343 .await?;
1344 Ok(certificate)
1345 }
1346
1347 #[instrument(level = "trace", skip_all)]
1349 async fn submit_block_proposal<T: ProcessableCertificate>(
1350 self: &Arc<Self>,
1351 committee: Arc<Committee>,
1352 proposal: Box<BlockProposal>,
1353 value: T,
1354 ) -> Result<T::Certificate, chain_client::Error> {
1355 debug!(
1356 round = %proposal.content.round,
1357 "Submitting block proposal to validators"
1358 );
1359
1360 let justification = match proposal.original_proposal.as_ref() {
1364 Some(OriginalProposal::Regular { certificate }) => certificate.full_justification(),
1365 Some(OriginalProposal::Fast(_)) | None => JustificationChain::default(),
1366 };
1367
1368 let block_timestamp = proposal.content.block.timestamp;
1370 let local_time = self.local_node.storage_client().clock().current_time();
1371 if block_timestamp > local_time {
1372 info!(
1373 chain_id = %proposal.content.block.chain_id,
1374 %block_timestamp,
1375 %local_time,
1376 "Block timestamp is in the future; waiting until it can be proposed",
1377 );
1378 }
1379
1380 let (clock_skew_sender, mut clock_skew_receiver) = mpsc::unbounded_channel();
1382 let submit_action = CommunicateAction::SubmitBlock {
1383 proposal,
1384 blob_ids: value.required_blob_ids().into_iter().collect(),
1385 clock_skew_sender,
1386 };
1387
1388 let validity_threshold = committee.validity_threshold();
1390 let committee_clone = committee.clone();
1391 let clock_skew_check_handle = linera_base::Task::spawn(async move {
1392 let mut skew_weight = 0u64;
1393 let mut min_skew = TimeDelta::MAX;
1394 let mut max_skew = TimeDelta::ZERO;
1395 while let Some((public_key, clock_skew)) = clock_skew_receiver.recv().await {
1396 if clock_skew.as_micros() > 0 {
1397 skew_weight += committee_clone.weight(&public_key);
1398 min_skew = min_skew.min(clock_skew);
1399 max_skew = max_skew.max(clock_skew);
1400 if skew_weight >= validity_threshold {
1401 warn!(
1402 skew_weight,
1403 validity_threshold,
1404 min_skew_ms = min_skew.as_micros() / 1000,
1405 max_skew_ms = max_skew.as_micros() / 1000,
1406 "A validity threshold of validators reported clock skew; \
1407 consider checking your system clock",
1408 );
1409 return;
1410 }
1411 }
1412 }
1413 });
1414
1415 let quorum = self
1416 .communicate_chain_action(&committee, submit_action, value)
1417 .await?;
1418
1419 clock_skew_check_handle.await;
1420
1421 ensure!(
1429 quorum.unlocking_round() == justification.top_unlocking_round(),
1430 chain_client::Error::ProtocolError(
1431 "A quorum voted with an unlocking round that does not match the proposal's \
1432 justification chain",
1433 )
1434 );
1435 ensure!(
1436 quorum.justification_commitment() == justification.commitment(quorum.hash()),
1437 chain_client::Error::ProtocolError(
1438 "A quorum voted with a justification commitment that does not match the \
1439 proposal's justification chain",
1440 )
1441 );
1442 let certificate = T::make_certificate(quorum, justification);
1443 self.handle_certificate::<T>(certificate.clone()).await?;
1444 Ok(certificate)
1445 }
1446
1447 fn remote_node_updater(
1449 &self,
1450 remote_node: RemoteNode<Env::ValidatorNode>,
1451 ) -> RemoteNodeUpdater<Env::Storage, Env::ValidatorNode> {
1452 RemoteNodeUpdater {
1453 remote_node,
1454 local_node: self.local_node.clone(),
1455 admin_chain_id: self.admin_chain_id,
1456 certificate_upload_batch_size: self.options.certificate_upload_batch_size,
1457 }
1458 }
1459
1460 async fn process_lag_reports(&self, mut reports: Vec<LagReport<Env::ValidatorNode>>) {
1474 reports.sort_by_key(|report| Reverse(report.remote_progress()));
1475 for report in reports {
1476 if let Err(error) =
1479 Box::pin(self.synchronize_chain_state_from(&report.remote_node, report.chain_id))
1480 .await
1481 {
1482 debug!(
1483 remote_node = report.remote_node.address(),
1484 chain_id = %report.chain_id,
1485 %error,
1486 "failed to pull chain state from a validator that reported being ahead",
1487 );
1488 }
1489 }
1490 }
1491
1492 #[instrument(level = "trace", skip_all, fields(chain_id, block_height, delivery))]
1494 async fn communicate_chain_updates(
1495 self: &Arc<Self>,
1496 committee: &Committee,
1497 chain_id: ChainId,
1498 height: BlockHeight,
1499 delivery: CrossChainMessageDelivery,
1500 latest_certificate: Option<CacheArc<ConfirmedBlockCertificate>>,
1501 ) -> Result<(), chain_client::Error> {
1502 let nodes = self.make_nodes(committee)?;
1503 communicate_with_quorum(
1504 &nodes,
1505 committee,
1506 |_: &()| (),
1507 |remote_node| {
1508 let mut updater = self.remote_node_updater(remote_node);
1509 let certificate = latest_certificate.clone();
1510 Box::pin(async move {
1511 updater
1512 .send_chain_information(chain_id, height, delivery, certificate)
1513 .await
1514 })
1515 },
1516 self.options.quorum_grace_period,
1517 )
1518 .await?;
1519 Ok(())
1520 }
1521
1522 #[instrument(level = "trace", skip_all)]
1528 async fn communicate_chain_action<T: CertificateValue>(
1529 self: &Arc<Self>,
1530 committee: &Committee,
1531 action: CommunicateAction,
1532 value: T,
1533 ) -> Result<GenericCertificate<T>, chain_client::Error> {
1534 let lag_reports = Mutex::new(Vec::new());
1539 let nodes = self.make_nodes(committee)?;
1540 let result = communicate_with_quorum(
1545 &nodes,
1546 committee,
1547 |vote: &LiteVote| {
1548 (
1549 vote.value.value_hash,
1550 vote.round,
1551 vote.unlocking_round,
1552 vote.first_round,
1553 vote.justification_commitment,
1554 )
1555 },
1556 |remote_node| {
1557 let mut updater = self.remote_node_updater(remote_node.clone());
1558 let action = action.clone();
1559 let lag_reports = &lag_reports;
1560 Box::pin(async move {
1561 match updater.send_chain_update(action).await {
1562 Err(chain_client::Error::LocalNodeLagging { chain_id, error }) => {
1563 lag_reports.lock().unwrap().push(LagReport {
1564 remote_node,
1565 chain_id,
1566 error: (*error).clone(),
1567 });
1568 Err((*error).into())
1569 }
1570 result => result,
1571 }
1572 })
1573 },
1574 self.options.quorum_grace_period,
1575 )
1576 .await;
1577 let ((votes_hash, votes_round, _, _, _), votes) = match result {
1578 Ok(quorum) => quorum,
1579 Err(err) => {
1580 self.process_lag_reports(lag_reports.into_inner().unwrap())
1583 .await;
1584 return Err(err.into());
1585 }
1586 };
1587 ensure!(
1588 (votes_hash, votes_round) == (value.hash(), action.round()),
1589 chain_client::Error::UnexpectedQuorum {
1590 hash: votes_hash,
1591 round: votes_round,
1592 expected_hash: value.hash(),
1593 expected_round: action.round(),
1594 }
1595 );
1596 let certificate = LiteCertificate::try_from_votes(votes)
1601 .ok_or_else(|| {
1602 chain_client::Error::InternalError(
1603 "Vote values or rounds don't match; this is a bug",
1604 )
1605 })?
1606 .with_value(value)
1607 .ok_or_else(|| {
1608 chain_client::Error::ProtocolError("A quorum voted for an unexpected value")
1609 })?;
1610 Ok(certificate)
1611 }
1612
1613 #[instrument(level = "trace", skip_all)]
1616 async fn receive_certificate_with_checked_signatures(
1617 &self,
1618 certificate: ConfirmedBlockCertificate,
1619 mode: ProcessConfirmedBlockMode,
1620 ) -> Result<(), chain_client::Error> {
1621 let block = certificate.block();
1622 self.download_certificates(block.header.chain_id, block.header.height)
1624 .await?;
1625 let nodes = self.validator_nodes().await?;
1628 self.handle_certificate_with_retry(&certificate, &nodes, mode)
1629 .await?;
1630 Ok(())
1631 }
1632
1633 #[instrument(level = "trace", skip_all)]
1638 fn receive_sender_certificate(
1643 &self,
1644 certificate: CacheArc<ConfirmedBlockCertificate>,
1645 mode: ReceiveCertificateMode,
1646 nodes: Option<Vec<RemoteNode<Env::ValidatorNode>>>,
1647 ) -> ReceiveSenderCertificateFuture<'_> {
1648 Box::pin(async move {
1649 if let ReceiveCertificateMode::NeedsCheck = mode {
1651 let mut check_result = self.check_certificate(&certificate).await?;
1652 if matches!(check_result, CheckCertificateResult::FutureEpoch) {
1653 let admin_chain_id = self.admin_chain_id;
1661 let epoch = certificate.block().header.epoch;
1662 info!(
1663 %epoch,
1664 "certificate is from an unknown epoch; synchronizing the admin chain"
1665 );
1666 let synced_from_serving_node = if let Some(nodes) = &nodes {
1667 let certificate = &certificate;
1668 communicate_concurrently(
1669 nodes,
1670 |node| {
1671 Box::pin(async move {
1672 self.synchronize_chain_state_from(&node, admin_chain_id)
1673 .await?;
1674 match self.check_certificate(certificate).await? {
1675 CheckCertificateResult::FutureEpoch => {
1676 Err(chain_client::Error::CommitteeSynchronizationError)
1677 }
1678 _ => Ok(()),
1679 }
1680 })
1681 },
1682 self.options.blob_download_hedge_delay,
1683 self.storage_client().clock(),
1684 )
1685 .await
1686 .is_ok()
1687 } else {
1688 false
1689 };
1690 if synced_from_serving_node {
1691 check_result = self.check_certificate(&certificate).await?;
1692 }
1693 if matches!(check_result, CheckCertificateResult::FutureEpoch) {
1694 Box::pin(self.synchronize_chain_state(admin_chain_id)).await?;
1695 check_result = self.check_certificate(&certificate).await?;
1696 }
1697 }
1698 check_result.into_result()?;
1699 }
1700 let nodes = if let Some(nodes) = nodes {
1702 nodes
1703 } else {
1704 self.validator_nodes().await?
1705 };
1706 let processing_mode = if self
1707 .chain_mode(certificate.value().chain_id())
1708 .is_some_and(|m| m.should_sync_chain_state())
1709 {
1710 ProcessConfirmedBlockMode::Auto
1711 } else {
1712 ProcessConfirmedBlockMode::Preprocess
1713 };
1714 self.handle_certificate_with_retry(&certificate, &nodes, processing_mode)
1715 .await?;
1716
1717 Ok(())
1718 })
1719 }
1720
1721 #[instrument(level = "debug", skip_all, fields(chain_id = %sender_chain_id))]
1723 async fn download_and_process_sender_chain(
1724 &self,
1725 sender_chain_id: ChainId,
1726 nodes: &[RemoteNode<Env::ValidatorNode>],
1727 received_log: &ReceivedLogs,
1728 mut remote_heights: Vec<BlockHeight>,
1729 sender: mpsc::UnboundedSender<ChainAndHeight>,
1730 ) {
1731 let mut nodes = nodes.to_vec();
1732 while !remote_heights.is_empty() {
1733 if let Ok(local_certs) = self
1736 .storage_client()
1737 .read_certificates_by_heights(sender_chain_id, &remote_heights)
1738 .await
1739 {
1740 let mut still_needed = Vec::new();
1741 for (height, maybe_cert) in remote_heights.iter().copied().zip(local_certs) {
1742 if let Some(certificate) = maybe_cert {
1743 let chain_id = certificate.block().header.chain_id;
1744 if let Err(error) = sender.send(ChainAndHeight { chain_id, height }) {
1745 error!(
1746 %chain_id, %height, %error,
1747 "failed to send chain and height over the channel",
1748 );
1749 }
1750 } else {
1751 still_needed.push(height);
1752 }
1753 }
1754 remote_heights = still_needed;
1755 if remote_heights.is_empty() {
1756 break;
1757 }
1758 }
1759
1760 let remote_heights_ref = &remote_heights;
1761 let certificates = match communicate_concurrently(
1762 &nodes,
1763 async move |remote_node| {
1764 let mut remote_heights = remote_heights_ref.clone();
1765 remote_heights.retain(|height| {
1768 received_log.validator_has_block(
1769 &remote_node.public_key,
1770 sender_chain_id,
1771 *height,
1772 )
1773 });
1774 if remote_heights.is_empty() {
1775 return Err(NodeError::MissingCertificateValue);
1778 }
1779 let certificates = self
1780 .requests_scheduler
1781 .download_certificates_by_heights(
1782 &remote_node,
1783 sender_chain_id,
1784 remote_heights,
1785 )
1786 .await?;
1787 let mut certificates_with_check_results = vec![];
1788 for cert in certificates {
1789 let check_result = self.check_certificate(&cert).await?;
1790 certificates_with_check_results
1791 .push((cert, check_result.into_result().is_ok()));
1792 }
1793 Ok(certificates_with_check_results)
1794 },
1795 self.options.certificate_batch_download_hedge_delay,
1796 self.storage_client().clock(),
1797 )
1798 .await
1799 {
1800 Ok(certificates_with_check_results) => certificates_with_check_results,
1801 Err(errors) => {
1802 let faulty_validators = errors
1803 .into_iter()
1804 .map(|(validator, error)| {
1805 warn!(
1806 %validator,
1807 %sender_chain_id,
1808 %error,
1809 "failed to download certificates from validator",
1810 );
1811 validator
1812 })
1813 .collect::<BTreeSet<_>>();
1814 nodes.retain(|node| !faulty_validators.contains(&node.public_key));
1816 if nodes.is_empty() {
1817 info!(
1818 chain_id = %sender_chain_id,
1819 "could not download certificates for chain - no more correct validators left"
1820 );
1821 return;
1822 }
1823 continue;
1824 }
1825 };
1826
1827 trace!(
1828 num_certificates = %certificates.len(),
1829 "received certificates",
1830 );
1831
1832 let mut to_remove_from_queue = BTreeSet::new();
1833
1834 for (certificate, check_result) in certificates {
1835 let hash = certificate.hash();
1836 let chain_id = certificate.block().header.chain_id;
1837 let height = certificate.block().header.height;
1838 if !check_result {
1839 to_remove_from_queue.insert(height);
1843 continue;
1844 }
1845 let mode = ReceiveCertificateMode::AlreadyChecked;
1847 if let Err(error) = self
1848 .receive_sender_certificate(
1849 self.storage_client().cache_certificate(certificate),
1850 mode,
1851 None,
1852 )
1853 .await
1854 {
1855 warn!(%error, %hash, "Received invalid certificate");
1856 } else {
1857 to_remove_from_queue.insert(height);
1858 if let Err(error) = sender.send(ChainAndHeight { chain_id, height }) {
1859 error!(
1860 %chain_id,
1861 %height,
1862 %error,
1863 "failed to send chain and height over the channel",
1864 );
1865 }
1866 }
1867 }
1868
1869 remote_heights.retain(|height| !to_remove_from_queue.contains(height));
1870 }
1871 trace!("find_received_certificates: finished processing chain");
1872 }
1873
1874 #[instrument(level = "trace", skip(self))]
1876 async fn get_received_log_from_validator(
1877 &self,
1878 chain_id: ChainId,
1879 remote_node: &RemoteNode<Env::ValidatorNode>,
1880 tracker: u64,
1881 ) -> Result<Vec<ChainAndHeight>, chain_client::Error> {
1882 let mut offset = tracker;
1883
1884 let mut remote_log = Vec::new();
1886 let mut num_pages = 0usize;
1887 loop {
1888 trace!("get_received_log_from_validator: looping");
1889 let query = ChainInfoQuery::new(chain_id).with_received_log_excluding_first_n(offset);
1890 let info = remote_node.handle_chain_info_query(query).await?;
1891 let received_entries = info.requested_received_log.len();
1892 offset += received_entries as u64;
1893 remote_log.extend(info.requested_received_log);
1894 trace!(
1895 remote_node = remote_node.address(),
1896 %received_entries,
1897 "get_received_log_from_validator: received log batch",
1898 );
1899 num_pages += 1;
1900 if num_pages.is_multiple_of(RECEIVED_LOG_PAGES_PER_PROGRESS_MESSAGE) {
1901 info!(
1902 %chain_id,
1903 remote_node = remote_node.address(),
1904 num_entries = remote_log.len(),
1905 "still downloading the received log",
1906 );
1907 }
1908 if received_entries < CHAIN_INFO_MAX_RECEIVED_LOG_ENTRIES {
1909 break;
1910 }
1911 }
1912
1913 trace!(
1914 remote_node = remote_node.address(),
1915 num_entries = remote_log.len(),
1916 "get_received_log_from_validator: returning downloaded log",
1917 );
1918
1919 Ok(remote_log)
1920 }
1921
1922 async fn download_sender_block_with_sending_ancestors(
1928 &self,
1929 receiver_chain_id: ChainId,
1930 sender_chain_id: ChainId,
1931 height: BlockHeight,
1932 remote_node: &RemoteNode<Env::ValidatorNode>,
1933 ) -> Result<(), chain_client::Error> {
1934 let next_outbox_height = self
1935 .local_node
1936 .next_outbox_heights(&[sender_chain_id], receiver_chain_id)
1937 .await?
1938 .get(&sender_chain_id)
1939 .copied()
1940 .unwrap_or(BlockHeight::ZERO);
1941
1942 let mut certificates = BTreeMap::new();
1945 let mut current_height = height;
1946 let mut current_hash: Option<CryptoHash> = None;
1949
1950 while current_height >= next_outbox_height {
1952 let certificate = if let Some(local) = self
1956 .try_read_local_certificate(sender_chain_id, current_height, current_hash)
1957 .await?
1958 {
1959 local
1960 } else {
1961 let downloaded = self
1962 .requests_scheduler
1963 .download_certificates_by_heights(
1964 remote_node,
1965 sender_chain_id,
1966 vec![current_height],
1967 )
1968 .await?;
1969 let Some(certificate) = downloaded.into_iter().next() else {
1970 return Err(chain_client::Error::CannotDownloadMissingSenderBlock {
1971 chain_id: sender_chain_id,
1972 height: current_height,
1973 });
1974 };
1975 self.storage_client().cache_certificate(certificate)
1976 };
1977
1978 self.check_certificate(&certificate).await?.into_result()?;
1980
1981 let block = certificate.block();
1983 let next = block
1984 .body
1985 .previous_message_blocks
1986 .get(&receiver_chain_id)
1987 .map(|(prev_hash, prev_height)| (*prev_hash, *prev_height));
1988
1989 certificates.insert(current_height, certificate);
1991
1992 if let Some((prev_hash, prev_height)) = next {
1993 current_height = prev_height;
1995 current_hash = Some(prev_hash);
1996 } else {
1997 break;
1999 }
2000 }
2001
2002 if certificates.is_empty() {
2003 self.retry_pending_cross_chain_requests(sender_chain_id)
2004 .await?;
2005 }
2006
2007 for certificate in certificates.into_values() {
2009 self.receive_sender_certificate(
2010 certificate,
2011 ReceiveCertificateMode::AlreadyChecked,
2012 Some(vec![remote_node.clone()]),
2013 )
2014 .await?;
2015 }
2016
2017 Ok(())
2018 }
2019
2020 async fn download_event_bearing_blocks(
2024 &self,
2025 publisher_chain_id: ChainId,
2026 initial_blocks: BTreeSet<(BlockHeight, CryptoHash)>,
2027 local_next_block_height: BlockHeight,
2028 subscribed_streams: &BTreeSet<StreamId>,
2029 remote_node: &RemoteNode<Env::ValidatorNode>,
2030 ) -> Result<(), chain_client::Error> {
2031 if initial_blocks.is_empty() {
2032 return Ok(());
2033 }
2034
2035 let mut certificates = BTreeMap::new();
2036 let mut blocks_to_fetch = initial_blocks;
2037 let next_expected_events = self
2038 .local_node
2039 .next_expected_events(
2040 publisher_chain_id,
2041 subscribed_streams.iter().cloned().collect(),
2042 )
2043 .await?;
2044
2045 while let Some((current_height, current_hash)) = blocks_to_fetch.pop_last() {
2046 if current_height < local_next_block_height {
2047 continue; }
2049 if certificates.contains_key(¤t_height) {
2050 continue;
2051 }
2052
2053 let certificate = if let Some(certificate) =
2054 self.storage_client().read_certificate(current_hash).await?
2055 {
2056 certificate
2057 } else {
2058 let downloaded = self
2059 .requests_scheduler
2060 .download_certificates(remote_node, publisher_chain_id, current_height, 1)
2061 .await?;
2062 let Some(certificate) = downloaded.into_iter().next() else {
2063 tracing::debug!(
2064 validator = remote_node.address(),
2065 %publisher_chain_id,
2066 height = %current_height,
2067 "failed to download event publisher block"
2068 );
2069 continue;
2070 };
2071
2072 self.check_certificate(&certificate).await?.into_result()?;
2073
2074 self.storage_client().cache_certificate(certificate)
2075 };
2076
2077 let block = certificate.block();
2078 for stream_id in subscribed_streams {
2080 if let Some((prev_hash, prev_height)) =
2081 block.body.previous_event_blocks.get(stream_id)
2082 {
2083 if next_expected_events.get(stream_id).is_some_and(|index| {
2084 block
2085 .body
2086 .events
2087 .iter()
2088 .flatten()
2089 .find(|event| event.stream_id == *stream_id)
2090 .is_some_and(|event| event.index == *index)
2091 }) {
2092 continue;
2093 }
2094 if !certificates.contains_key(prev_height) {
2095 blocks_to_fetch.insert((*prev_height, *prev_hash));
2096 }
2097 }
2098 }
2099
2100 certificates.insert(current_height, certificate);
2101 }
2102
2103 for certificate in certificates.into_values() {
2105 self.receive_sender_certificate(
2106 certificate,
2107 ReceiveCertificateMode::AlreadyChecked,
2108 Some(vec![remote_node.clone()]),
2109 )
2110 .await?;
2111 }
2112
2113 Ok(())
2114 }
2115
2116 async fn sync_events_from_node(
2119 &self,
2120 chain_id: ChainId,
2121 stream_ids: &BTreeSet<StreamId>,
2122 remote_node: &RemoteNode<Env::ValidatorNode>,
2123 ) -> Result<(), chain_client::Error> {
2124 let stream_ids_vec = stream_ids.iter().cloned().collect::<Vec<_>>();
2125 let mut initial_blocks = BTreeSet::new();
2126 for chunk in stream_ids_vec.chunks(self.options.max_event_stream_queries) {
2127 let query = ChainInfoQuery::new(chain_id).with_previous_event_blocks(chunk.to_vec());
2128 let info = remote_node.handle_chain_info_query(query).await?;
2129 initial_blocks.extend(info.requested_previous_event_blocks.values().copied());
2130 }
2131 let local_height = match self.local_node.chain_info(chain_id).await {
2132 Ok(info) => info.next_block_height,
2133 Err(LocalNodeError::InactiveChain(_) | LocalNodeError::BlobsNotFound(_)) => {
2134 BlockHeight::ZERO
2135 }
2136 Err(error) => return Err(error.into()),
2137 };
2138 self.download_event_bearing_blocks(
2139 chain_id,
2140 initial_blocks,
2141 local_height,
2142 stream_ids,
2143 remote_node,
2144 )
2145 .await
2146 }
2147
2148 #[instrument(
2149 level = "trace", skip_all,
2150 fields(certificate_hash = ?incoming_certificate.hash()),
2151 )]
2152 async fn check_certificate(
2153 &self,
2154 incoming_certificate: &ConfirmedBlockCertificate,
2155 ) -> Result<CheckCertificateResult, NodeError> {
2156 let epoch = incoming_certificate.block().header.epoch;
2157 let storage = self.storage_client();
2158 let view_err = |error: ExecutionError| NodeError::ViewError {
2159 error: error.to_string(),
2160 };
2161 if storage.is_epoch_revoked(epoch).await.map_err(view_err)? {
2162 return Ok(CheckCertificateResult::OldEpoch);
2163 }
2164 let Some(committee) = storage.committee_for_epoch(epoch).await.map_err(view_err)? else {
2165 return Ok(CheckCertificateResult::FutureEpoch);
2166 };
2167 incoming_certificate.check(&committee)?;
2168 Ok(CheckCertificateResult::New)
2169 }
2170
2171 #[instrument(level = "trace", skip_all)]
2175 async fn synchronize_chain_state(
2176 &self,
2177 chain_id: ChainId,
2178 ) -> Result<Box<ChainInfo>, chain_client::Error> {
2179 let (_, committee) = self.admin_committee().await?;
2180 self.synchronize_chain_from_committee(chain_id, committee)
2181 .await
2182 }
2183
2184 #[instrument(level = "trace", skip_all)]
2189 pub(crate) async fn synchronize_chain_from_committee(
2190 &self,
2191 chain_id: ChainId,
2192 committee: Arc<Committee>,
2193 ) -> Result<Box<ChainInfo>, chain_client::Error> {
2194 #[cfg(with_metrics)]
2195 let _latency = if !self.is_chain_follow_only(chain_id) {
2196 Some(metrics::SYNCHRONIZE_CHAIN_STATE_LATENCY.measure_latency())
2197 } else {
2198 None
2199 };
2200
2201 let validators = self.make_nodes(&committee)?;
2202 Box::pin(self.fetch_chain_info(chain_id, &validators)).await?;
2203 communicate_with_quorum(
2204 &validators,
2205 &committee,
2206 |_: &()| (),
2207 |remote_node| async move {
2208 self.synchronize_chain_state_from(&remote_node, chain_id)
2209 .await
2210 },
2211 self.options.quorum_grace_period,
2212 )
2213 .await?;
2214
2215 self.local_node
2216 .chain_info(chain_id)
2217 .await
2218 .map_err(Into::into)
2219 }
2220
2221 #[instrument(level = "trace", skip(self, remote_node, chain_id))]
2227 pub(crate) async fn synchronize_chain_state_from(
2228 &self,
2229 remote_node: &RemoteNode<Env::ValidatorNode>,
2230 chain_id: ChainId,
2231 ) -> Result<(), chain_client::Error> {
2232 let with_manager_values = !self.is_chain_follow_only(chain_id);
2233 let query = if with_manager_values {
2234 ChainInfoQuery::new(chain_id).with_manager_values()
2235 } else {
2236 ChainInfoQuery::new(chain_id)
2237 };
2238 let query = query.with_latest_checkpoint_height();
2239 let remote_info = remote_node.handle_chain_info_query(query).await?;
2240
2241 if let Some(checkpoint_height) = remote_info.requested_latest_checkpoint_height {
2248 self.bootstrap_chain_from_checkpoint(remote_node, chain_id, checkpoint_height)
2249 .await?;
2250 }
2251
2252 let local_info = self
2253 .download_certificates_from(remote_node, chain_id, remote_info.next_block_height, None)
2254 .await?;
2255
2256 if !with_manager_values {
2257 return Ok(());
2258 }
2259
2260 let local_height = local_info.next_block_height;
2262 if local_height != remote_info.next_block_height {
2263 debug!(
2264 remote_node = remote_node.address(),
2265 remote_height = %remote_info.next_block_height,
2266 local_height = %local_height,
2267 "synced from validator, but remote height and local height are different",
2268 );
2269 return Ok(());
2270 };
2271
2272 if let Some(timeout) = remote_info.manager.timeout {
2273 self.handle_certificate::<Timeout>(*timeout).await?;
2274 }
2275 let mut proposals = Vec::new();
2276 if let Some(proposal) = remote_info.manager.requested_signed_proposal {
2277 proposals.push(*proposal);
2278 }
2279 if let Some(proposal) = remote_info.manager.requested_proposed {
2280 proposals.push(*proposal);
2281 }
2282 if let Some(locking) = remote_info.manager.requested_locking {
2283 match *locking {
2284 LockingBlock::Fast(proposal) => {
2285 proposals.push(proposal);
2286 }
2287 LockingBlock::Regular(cert) => {
2288 let hash = cert.hash();
2289 if let Err(error) = self.try_process_locking_block_from(remote_node, cert).await
2290 {
2291 debug!(
2292 remote_node = remote_node.address(),
2293 %hash,
2294 height = %local_height,
2295 %error,
2296 "skipping locked block from validator",
2297 );
2298 }
2299 }
2300 }
2301 }
2302 'proposal_loop: for proposal in proposals {
2303 let owner: AccountOwner = proposal.owner();
2304 if let Err(mut err) = self
2305 .local_node
2306 .handle_block_proposal(proposal.clone())
2307 .await
2308 {
2309 if let LocalNodeError::BlobsNotFound(_) = &err {
2310 let required_blob_ids = proposal.required_blob_ids().collect::<Vec<_>>();
2311 if !required_blob_ids.is_empty() {
2312 let mut blobs = Vec::new();
2313 for blob_id in required_blob_ids {
2314 let blob_content = match self
2315 .requests_scheduler
2316 .download_pending_blob(remote_node, chain_id, blob_id)
2317 .await
2318 {
2319 Ok(content) => content,
2320 Err(error) => {
2321 info!(
2322 remote_node = remote_node.address(),
2323 height = %local_height,
2324 proposer = %owner,
2325 %blob_id,
2326 %error,
2327 "skipping proposal from validator; failed to download blob",
2328 );
2329 continue 'proposal_loop;
2330 }
2331 };
2332 blobs.push(Blob::new(blob_content));
2333 }
2334 self.local_node
2335 .handle_pending_blobs(chain_id, blobs)
2336 .await?;
2337 if let Err(new_err) = self
2339 .local_node
2340 .handle_block_proposal(proposal.clone())
2341 .await
2342 {
2343 err = new_err;
2344 } else {
2345 continue;
2346 }
2347 }
2348 if let LocalNodeError::BlobsNotFound(blob_ids) = &err {
2349 self.update_local_node_with_blobs_from(
2350 blob_ids.clone(),
2351 slice::from_ref(remote_node),
2352 )
2353 .await?;
2354 if let Err(new_err) = self
2356 .local_node
2357 .handle_block_proposal(proposal.clone())
2358 .await
2359 {
2360 err = new_err;
2361 } else {
2362 continue;
2363 }
2364 }
2365 }
2366 if let LocalNodeError::EventsNotFound(event_ids) = &err {
2367 if let Err(error) =
2368 Box::pin(self.download_certificates_for_events(event_ids)).await
2369 {
2370 info!(
2371 remote_node = remote_node.address(),
2372 height = %local_height,
2373 proposer = %owner,
2374 %error,
2375 "skipping proposal from validator; failed to download events",
2376 );
2377 continue 'proposal_loop;
2378 }
2379 if let Err(new_err) = self
2381 .local_node
2382 .handle_block_proposal(proposal.clone())
2383 .await
2384 {
2385 err = new_err;
2386 } else {
2387 continue;
2388 }
2389 }
2390 if let LocalNodeError::WorkerError(WorkerError::ChainError(chain_err)) = &err {
2393 if let ChainError::MissingCrossChainUpdates { chain_id, bundles } = &**chain_err
2394 {
2395 let chain_id = *chain_id;
2396 let mut origin_heights: BTreeMap<ChainId, BlockHeight> = BTreeMap::new();
2402 for (origin, height) in bundles {
2403 let entry = origin_heights.entry(*origin).or_insert(*height);
2404 *entry = (*entry).max(*height);
2405 }
2406 stream::iter(origin_heights.into_iter().map(|(origin, height)| {
2407 self.download_sender_block_with_sending_ancestors(
2408 chain_id,
2409 origin,
2410 height,
2411 remote_node,
2412 )
2413 }))
2414 .buffer_unordered(self.options.max_joined_tasks)
2415 .collect::<Vec<_>>()
2416 .await
2417 .into_iter()
2418 .collect::<Result<(), _>>()?;
2419 if let Err(new_err) = self
2420 .local_node
2421 .handle_block_proposal(proposal.clone())
2422 .await
2423 {
2424 err = new_err;
2425 } else {
2426 continue 'proposal_loop;
2427 }
2428 }
2429 }
2430
2431 debug!(
2432 remote_node = remote_node.address(),
2433 proposer = %owner,
2434 height = %local_height,
2435 error = %err,
2436 "skipping proposal from validator",
2437 );
2438 }
2439 }
2440 Ok(())
2441 }
2442
2443 async fn try_process_locking_block_from(
2444 &self,
2445 remote_node: &RemoteNode<Env::ValidatorNode>,
2446 certificate: ValidatedBlockCertificate,
2447 ) -> Result<(), chain_client::Error> {
2448 let chain_id = certificate.inner().chain_id();
2449 let mut downloaded_blobs = HashSet::<BlobId>::new();
2450 let mut events = EventSetDownloader::new(self);
2451 loop {
2452 let result = self
2453 .handle_certificate::<ValidatedBlock>(certificate.clone())
2454 .await;
2455 if let Err(LocalNodeError::BlobsNotFound(blob_ids)) = &result {
2456 let new_blobs = filter_new(blob_ids, &downloaded_blobs);
2457 if !new_blobs.is_empty() {
2458 let mut blobs = Vec::new();
2459 for blob_id in &new_blobs {
2460 let blob_content = self
2461 .requests_scheduler
2462 .download_pending_blob(remote_node, chain_id, *blob_id)
2463 .await?;
2464 blobs.push(Blob::new(blob_content));
2465 }
2466 self.local_node
2467 .handle_pending_blobs(chain_id, blobs)
2468 .await?;
2469 downloaded_blobs.extend(new_blobs);
2470 continue;
2471 }
2472 }
2473 if let Err(LocalNodeError::EventsNotFound(event_ids)) = &result {
2474 if events.download_new(event_ids).await? {
2475 continue;
2476 }
2477 }
2478 result?;
2479 return Ok(());
2480 }
2481 }
2482
2483 async fn update_local_node_with_blobs_from(
2486 &self,
2487 blob_ids: Vec<BlobId>,
2488 remote_nodes: &[RemoteNode<Env::ValidatorNode>],
2489 ) -> Result<Vec<CacheArc<Blob>>, chain_client::Error> {
2490 let hedge_delay = self.options.blob_download_hedge_delay;
2491 let blob_ids = blob_ids.into_iter().collect::<BTreeSet<_>>();
2493 stream::iter(blob_ids.into_iter().map(|blob_id| {
2494 communicate_concurrently(
2495 remote_nodes,
2496 async move |remote_node| {
2497 let certificate = self
2498 .requests_scheduler
2499 .download_certificate_for_blob(&remote_node, blob_id)
2500 .await?;
2501 self.receive_sender_certificate(
2502 self.storage_client().cache_certificate(certificate),
2503 ReceiveCertificateMode::NeedsCheck,
2504 Some(vec![remote_node.clone()]),
2505 )
2506 .await?;
2507 let blob = self
2508 .local_node
2509 .storage_client()
2510 .read_blob(blob_id)
2511 .await?
2512 .ok_or_else(|| LocalNodeError::BlobsNotFound(vec![blob_id]))?;
2513 Result::<_, chain_client::Error>::Ok(blob)
2514 },
2515 hedge_delay,
2516 self.storage_client().clock(),
2517 )
2518 .map_err(move |errors| {
2519 for (validator, error) in &errors {
2520 warn!(
2521 %validator,
2522 %blob_id,
2523 %error,
2524 "failed to download certificate-for-blob from validator",
2525 );
2526 }
2527 chain_client::Error::CannotDownloadBlob(blob_id)
2528 })
2529 }))
2530 .buffer_unordered(self.options.max_joined_tasks)
2531 .collect::<Vec<_>>()
2532 .await
2533 .into_iter()
2534 .collect()
2535 }
2536
2537 #[instrument(level = "trace", skip(self, block))]
2547 async fn stage_block_execution(
2548 &self,
2549 block: ProposedBlock,
2550 round: Option<u32>,
2551 published_blobs: Vec<Blob>,
2552 policy: BundleExecutionPolicy,
2553 ) -> Result<(Block, ChainInfoResponse, HashSet<ChainId>), chain_client::Error> {
2554 let mut events = EventSetDownloader::new(self);
2555 loop {
2556 let result = self
2557 .local_node
2558 .stage_block_execution(
2559 block.clone(),
2560 round,
2561 published_blobs.clone(),
2562 policy.clone(),
2563 )
2564 .await;
2565 if let Err(LocalNodeError::BlobsNotFound(blob_ids)) = &result {
2566 let validators = self.validator_nodes().await?;
2567 self.update_local_node_with_blobs_from(blob_ids.clone(), &validators)
2568 .await?;
2569 continue; }
2571 if let Err(LocalNodeError::EventsNotFound(event_ids)) = &result {
2572 if events.download_new(event_ids).await? {
2573 continue; }
2575 }
2577 if let Ok((_, executed_block, _, _, _)) = &result {
2578 let hash = executed_block.hash();
2579 let notification = Notification {
2580 chain_id: executed_block.header.chain_id,
2581 reason: Reason::BlockExecuted {
2582 height: executed_block.header.height,
2583 hash,
2584 },
2585 };
2586 self.notifier.notify(&[notification]);
2587 }
2588 let (
2589 _modified_block,
2590 executed_block,
2591 response,
2592 _resource_tracker,
2593 never_reject_origins,
2594 ) = result?;
2595 return Ok((executed_block, response, never_reject_origins));
2596 }
2597 }
2598}
2599
2600fn filter_new<T: Clone + Eq + std::hash::Hash>(
2602 ids: &[T],
2603 already_downloaded: &HashSet<T>,
2604) -> Vec<T> {
2605 ids.iter()
2606 .filter(|id| !already_downloaded.contains(*id))
2607 .cloned()
2608 .collect()
2609}
2610
2611pub(crate) struct EventSetDownloader<'a, Env: Environment> {
2616 client: &'a Client<Env>,
2617 downloaded: HashSet<EventId>,
2618}
2619
2620impl<'a, Env: Environment> EventSetDownloader<'a, Env> {
2621 pub(crate) fn new(client: &'a Client<Env>) -> Self {
2622 Self {
2623 client,
2624 downloaded: HashSet::new(),
2625 }
2626 }
2627
2628 pub(crate) async fn download_new(
2634 &mut self,
2635 event_ids: &[EventId],
2636 ) -> Result<bool, chain_client::Error> {
2637 let new_events = filter_new(event_ids, &self.downloaded);
2638 if new_events.is_empty() {
2639 return Ok(false);
2640 }
2641 Box::pin(self.client.download_certificates_for_events(&new_events)).await?;
2642 self.downloaded.extend(new_events);
2643 Ok(true)
2644 }
2645}
2646
2647pub(crate) type ClockOf<Env> = <<Env as Environment>::Storage as linera_storage::Storage>::Clock;
2653
2654pub(crate) async fn hedged_fan_out<Peer, T, Err, NextPeer, NextFut, Op, OpFut>(
2666 first_peer: Peer,
2667 mut next_peer: NextPeer,
2668 operation: Op,
2669 hedge_schedule: impl Fn(usize) -> Duration,
2670 clock: &(impl linera_storage::Clock + Sync),
2673) -> Result<T, Vec<Err>>
2674where
2675 NextPeer: FnMut() -> NextFut,
2676 NextFut: Future<Output = Option<Peer>>,
2677 Op: Fn(Peer) -> OpFut,
2678 OpFut: Future<Output = Result<T, Err>>,
2679{
2680 use futures::future::{select, Either};
2681
2682 let mut in_flight = FuturesUnordered::new();
2683 let mut errors = vec![];
2684 let mut started = 0usize;
2685 let arm = |started: usize| clock.sleep_for(hedge_schedule(started));
2686
2687 in_flight.push(operation(first_peer));
2688 started += 1;
2689 let mut hedge = arm(started);
2690
2691 loop {
2692 if in_flight.is_empty() {
2693 match next_peer().await {
2695 Some(peer) => {
2696 in_flight.push(operation(peer));
2697 started += 1;
2698 hedge = arm(started);
2699 }
2700 None => return Err(errors),
2701 }
2702 continue;
2703 }
2704 match select(in_flight.next(), hedge).await {
2705 Either::Left((Some(Ok(value)), _)) => return Ok(value),
2707 Either::Left((Some(Err(error)), pending_hedge)) => {
2709 errors.push(error);
2710 hedge = pending_hedge;
2711 if let Some(peer) = next_peer().await {
2712 in_flight.push(operation(peer));
2713 started += 1;
2714 hedge = arm(started);
2715 }
2716 }
2717 Either::Left((None, pending_hedge)) => hedge = pending_hedge,
2719 Either::Right(((), _)) => match next_peer().await {
2721 Some(peer) => {
2722 in_flight.push(operation(peer));
2723 started += 1;
2724 hedge = arm(started);
2725 }
2726 None => break,
2727 },
2728 }
2729 }
2730
2731 while let Some(result) = in_flight.next().await {
2733 match result {
2734 Ok(value) => return Ok(value),
2735 Err(error) => errors.push(error),
2736 }
2737 }
2738 Err(errors)
2739}
2740
2741async fn communicate_concurrently<A, E, F, R, V>(
2747 nodes: &[RemoteNode<A>],
2748 f: F,
2749 hedge_delay: Duration,
2750 clock: &(impl linera_storage::Clock + Sync),
2751) -> Result<V, Vec<(ValidatorPublicKey, E)>>
2752where
2753 F: Clone + FnOnce(RemoteNode<A>) -> R,
2754 RemoteNode<A>: Clone,
2755 R: Future<Output = Result<V, E>>,
2756{
2757 let mut nodes = nodes.to_vec();
2758 nodes.shuffle(&mut rand::thread_rng());
2759 let mut nodes = nodes.into_iter();
2760 let Some(first_peer) = nodes.next() else {
2761 return Err(vec![]);
2762 };
2763 hedged_fan_out(
2764 first_peer,
2765 move || std::future::ready(nodes.next()),
2766 |node: RemoteNode<A>| {
2767 let fun = f.clone();
2768 async move {
2769 let public_key = node.public_key;
2770 fun(node).await.map_err(|err| (public_key, err))
2771 }
2772 },
2773 |started| {
2774 let k = u32::try_from(started).unwrap_or(u32::MAX);
2775 hedge_delay.saturating_mul(k).saturating_mul(k)
2776 },
2777 clock,
2778 )
2779 .await
2780}
2781
2782#[must_use]
2784pub struct AbortOnDrop(pub AbortHandle);
2785
2786impl Drop for AbortOnDrop {
2787 #[instrument(level = "trace", skip(self))]
2788 fn drop(&mut self) {
2789 self.0.abort();
2790 }
2791}
2792
2793#[derive(Clone, Serialize, Deserialize)]
2795pub struct PendingProposal {
2796 pub block: ProposedBlock,
2798 pub blobs: Vec<Blob>,
2800 #[serde(default)]
2803 pub auto_retry_outcome: Option<BlockExecutionOutcome>,
2804 #[serde(default)]
2806 pub round: Option<Round>,
2807}
2808
2809struct LagReport<N> {
2812 remote_node: RemoteNode<N>,
2813 chain_id: ChainId,
2814 error: NodeError,
2815}
2816
2817impl<N> LagReport<N> {
2818 fn remote_progress(&self) -> (Option<BlockHeight>, Option<Round>) {
2821 match &self.error {
2822 NodeError::UnexpectedBlockHeight {
2823 expected_block_height,
2824 ..
2825 } => (Some(*expected_block_height), None),
2826 NodeError::WrongRound(round) => (None, Some(*round)),
2827 _ => (None, None),
2828 }
2829 }
2830}
2831
2832enum ReceiveCertificateMode {
2833 NeedsCheck,
2834 AlreadyChecked,
2835}
2836
2837enum CheckCertificateResult {
2838 OldEpoch,
2840 FutureEpoch,
2843 New,
2844}
2845
2846impl CheckCertificateResult {
2847 fn into_result(self) -> Result<(), chain_client::Error> {
2848 match self {
2849 Self::OldEpoch => Err(chain_client::Error::CommitteeDeprecationError),
2850 Self::FutureEpoch => Err(chain_client::Error::CommitteeSynchronizationError),
2851 Self::New => Ok(()),
2852 }
2853 }
2854}
2855
2856#[cfg(not(target_arch = "wasm32"))]
2860pub async fn create_bytecode_blobs(
2861 contract: Bytecode,
2862 service: Bytecode,
2863 vm_runtime: VmRuntime,
2864 formats: Option<Vec<u8>>,
2865) -> (Vec<Blob>, ModuleId) {
2866 let formats_blob = formats.map(Blob::new_application_formats);
2867 let formats_blob_hash = formats_blob.as_ref().map(|blob| blob.id().hash);
2868 let (mut blobs, module_id) = match vm_runtime {
2869 VmRuntime::Wasm => {
2870 let (compressed_contract, compressed_service) =
2871 tokio::task::spawn_blocking(move || (contract.compress(), service.compress()))
2872 .await
2873 .expect("Compression should not panic");
2874 let contract_blob = Blob::new_contract_bytecode(compressed_contract);
2875 let service_blob = Blob::new_service_bytecode(compressed_service);
2876 let module_id = ModuleId::new_with_formats(
2877 contract_blob.id().hash,
2878 service_blob.id().hash,
2879 vm_runtime,
2880 formats_blob_hash,
2881 );
2882 (vec![contract_blob, service_blob], module_id)
2883 }
2884 VmRuntime::Evm => {
2885 let compressed_contract = contract.compress();
2886 let evm_contract_blob = Blob::new_evm_bytecode(compressed_contract);
2887 let module_id = ModuleId::new_with_formats(
2888 evm_contract_blob.id().hash,
2889 evm_contract_blob.id().hash,
2890 vm_runtime,
2891 formats_blob_hash,
2892 );
2893 (vec![evm_contract_blob], module_id)
2894 }
2895 };
2896 if let Some(blob) = formats_blob {
2897 blobs.push(blob);
2898 }
2899 (blobs, module_id)
2900}
2901
2902#[cfg(test)]
2903mod chain_modes_tests {
2904 use std::collections::BTreeSet;
2905
2906 use linera_base::{crypto::CryptoHash, identifiers::ChainId};
2907
2908 use super::{ChainModes, ListeningMode};
2909
2910 #[test]
2914 fn remove_mode_updates_full_set_only_for_full_chains() {
2915 let mut modes = ChainModes::default();
2916 let full = ChainId(CryptoHash::test_hash("full"));
2917 let events_only = ChainId(CryptoHash::test_hash("events-only"));
2918 modes.extend_mode(full, ListeningMode::FullChain);
2919 modes.extend_mode(events_only, ListeningMode::EventsOnly(BTreeSet::new()));
2920
2921 let full_hash_before = modes.full().hash();
2922 assert!(matches!(
2924 modes.remove_mode(&events_only),
2925 Some(ListeningMode::EventsOnly(_))
2926 ));
2927 assert!(modes.get(&events_only).is_none());
2928 assert!(modes.remove_mode(&events_only).is_none());
2930 assert_eq!(modes.full().hash(), full_hash_before);
2931 assert_eq!(modes.full().inner().0, BTreeSet::from([full]));
2932
2933 assert_eq!(modes.remove_mode(&full), Some(ListeningMode::FullChain));
2935 assert_ne!(modes.full().hash(), full_hash_before);
2936 assert!(modes.full().inner().0.is_empty());
2937 }
2938}
2939
2940#[cfg(test)]
2941mod communicate_concurrently_tests {
2942 use std::sync::{
2943 atomic::{AtomicUsize, Ordering},
2944 Arc,
2945 };
2946
2947 use linera_base::crypto::ValidatorKeypair;
2948 use linera_storage::TestClock;
2949
2950 use super::*;
2951
2952 fn test_node() -> RemoteNode<()> {
2953 RemoteNode {
2954 public_key: ValidatorKeypair::generate().public_key,
2955 node: (),
2956 }
2957 }
2958
2959 #[tokio::test]
2962 async fn does_not_wait_after_failures() {
2963 let clock = TestClock::new();
2964 let nodes: Vec<_> = (0..5).map(|_| test_node()).collect();
2965 let calls = Arc::new(AtomicUsize::new(0));
2966 let result: Result<(), Vec<(ValidatorPublicKey, &str)>> = communicate_concurrently(
2967 &nodes,
2968 {
2969 let calls = calls.clone();
2970 move |_node| {
2971 let calls = calls.clone();
2972 async move {
2973 calls.fetch_add(1, Ordering::SeqCst);
2974 Err("unavailable")
2975 }
2976 }
2977 },
2978 Duration::from_secs(30),
2979 &clock,
2980 )
2981 .await;
2982 assert_eq!(result.unwrap_err().len(), 5);
2983 assert_eq!(calls.load(Ordering::SeqCst), 5);
2984 assert_eq!(clock.current_time(), Timestamp::from(0));
2986 }
2987
2988 #[tokio::test]
2991 async fn fails_over_to_a_working_node() {
2992 let clock = TestClock::new();
2993 let nodes: Vec<_> = (0..5).map(|_| test_node()).collect();
2994 let working = nodes[3].public_key;
2995 let result: Result<u32, Vec<(ValidatorPublicKey, &str)>> = communicate_concurrently(
2996 &nodes,
2997 move |node| async move {
2998 if node.public_key == working {
2999 Ok(42)
3000 } else {
3001 Err("unavailable")
3002 }
3003 },
3004 Duration::from_secs(30),
3005 &clock,
3006 )
3007 .await;
3008 assert_eq!(result.unwrap(), 42);
3009 assert_eq!(clock.current_time(), Timestamp::from(0));
3010 }
3011
3012 fn peer_source(n: usize) -> impl FnMut() -> std::future::Ready<Option<usize>> {
3019 let mut next = 1usize;
3020 move || {
3021 let peer = (next < n).then_some(next);
3022 next += 1;
3023 std::future::ready(peer)
3024 }
3025 }
3026
3027 #[tokio::test]
3030 async fn slow_first_peer_is_hedged_but_not_cancelled() {
3031 let clock = TestClock::new();
3032 let delay = Duration::from_secs(1);
3033 let order = Arc::new(std::sync::Mutex::new(Vec::new()));
3034 let (started_tx, mut started_rx) = tokio::sync::mpsc::unbounded_channel();
3035 let release0 = Arc::new(tokio::sync::Notify::new());
3036 let gates = [release0.clone(), Arc::new(tokio::sync::Notify::new())];
3037
3038 let operation = {
3039 let order = order.clone();
3040 move |peer: usize| {
3041 let started_tx = started_tx.clone();
3042 let order = order.clone();
3043 let gate = gates[peer].clone();
3044 async move {
3045 order.lock().unwrap().push(peer);
3046 started_tx.send(peer).unwrap();
3047 gate.notified().await;
3048 if peer == 0 {
3049 Ok::<u32, &str>(42)
3050 } else {
3051 Err("slow loser")
3052 }
3053 }
3054 }
3055 };
3056
3057 let fan = tokio::spawn({
3058 let clock = clock.clone();
3059 async move {
3060 hedged_fan_out(
3061 0usize,
3062 peer_source(2),
3063 operation,
3064 move |k| delay * u32::try_from(k).unwrap_or(u32::MAX),
3065 &clock,
3066 )
3067 .await
3068 }
3069 });
3070
3071 assert_eq!(started_rx.recv().await, Some(0));
3073 clock.add(TimeDelta::from_duration(delay));
3075 assert_eq!(started_rx.recv().await, Some(1));
3076 release0.notify_one();
3078 assert_eq!(fan.await.unwrap(), Ok(42));
3079 assert_eq!(*order.lock().unwrap(), vec![0, 1]);
3080 }
3081
3082 #[tokio::test]
3086 async fn hedge_schedule_determines_start_times() {
3087 let clock = TestClock::new();
3088 let unit = Duration::from_secs(1);
3089 let starts = Arc::new(std::sync::Mutex::new(Vec::new()));
3090 let (started_tx, mut started_rx) = tokio::sync::mpsc::unbounded_channel();
3091
3092 let operation = {
3093 let starts = starts.clone();
3094 let clock = clock.clone();
3095 move |peer: usize| {
3096 let started_tx = started_tx.clone();
3097 let starts = starts.clone();
3098 let clock = clock.clone();
3099 async move {
3100 starts.lock().unwrap().push((peer, clock.current_time()));
3101 started_tx.send(peer).unwrap();
3102 std::future::pending::<()>().await;
3103 Ok::<u32, &str>(0)
3104 }
3105 }
3106 };
3107
3108 let fan = tokio::spawn({
3109 let clock = clock.clone();
3110 async move {
3111 hedged_fan_out(
3112 0usize,
3113 peer_source(4),
3114 operation,
3115 move |k| {
3116 let k = u32::try_from(k).unwrap_or(u32::MAX);
3117 unit * k * k
3118 },
3119 &clock,
3120 )
3121 .await
3122 }
3123 });
3124
3125 assert_eq!(started_rx.recv().await, Some(0));
3127 clock.add(TimeDelta::from_duration(unit));
3128 assert_eq!(started_rx.recv().await, Some(1));
3129 clock.add(TimeDelta::from_duration(unit * 4));
3130 assert_eq!(started_rx.recv().await, Some(2));
3131 clock.add(TimeDelta::from_duration(unit * 9));
3132 assert_eq!(started_rx.recv().await, Some(3));
3133
3134 assert_eq!(
3136 *starts.lock().unwrap(),
3137 vec![
3138 (0, Timestamp::from(0)),
3139 (1, Timestamp::from(1_000_000)),
3140 (2, Timestamp::from(5_000_000)),
3141 (3, Timestamp::from(14_000_000)),
3142 ]
3143 );
3144 fan.abort();
3145 }
3146
3147 #[tokio::test]
3150 async fn frozen_clock_disables_the_hedge() {
3151 let clock = TestClock::new();
3152 let (started_tx, mut started_rx) = tokio::sync::mpsc::unbounded_channel();
3153 let release0 = Arc::new(tokio::sync::Notify::new());
3154
3155 let operation = {
3156 let release0 = release0.clone();
3157 move |peer: usize| {
3158 let started_tx = started_tx.clone();
3159 let release0 = release0.clone();
3160 async move {
3161 started_tx.send(peer).unwrap();
3162 if peer == 0 {
3163 release0.notified().await;
3164 Ok::<u32, &str>(7)
3165 } else {
3166 Ok(99)
3168 }
3169 }
3170 }
3171 };
3172
3173 let fan = tokio::spawn({
3174 let clock = clock.clone();
3175 async move {
3176 hedged_fan_out(
3177 0usize,
3178 peer_source(2),
3179 operation,
3180 move |k| Duration::from_secs(1) * u32::try_from(k).unwrap_or(u32::MAX),
3181 &clock,
3182 )
3183 .await
3184 }
3185 });
3186
3187 assert_eq!(started_rx.recv().await, Some(0));
3188 tokio::task::yield_now().await;
3190 assert!(
3191 started_rx.try_recv().is_err(),
3192 "the hedge must not fire while the clock is frozen"
3193 );
3194 release0.notify_one();
3196 assert_eq!(fan.await.unwrap(), Ok(7));
3197 assert_eq!(clock.current_time(), Timestamp::from(0));
3198 }
3199
3200 #[tokio::test]
3203 async fn failure_fails_over_without_waiting_out_the_hedge() {
3204 let clock = TestClock::new();
3205 let (started_tx, mut started_rx) = tokio::sync::mpsc::unbounded_channel();
3206 let release0 = Arc::new(tokio::sync::Notify::new());
3207
3208 let operation = {
3209 let release0 = release0.clone();
3210 move |peer: usize| {
3211 let started_tx = started_tx.clone();
3212 let release0 = release0.clone();
3213 async move {
3214 started_tx.send(peer).unwrap();
3215 match peer {
3216 0 => {
3217 release0.notified().await;
3218 Err::<u32, &str>("dead")
3219 }
3220 1 => Err("dead"),
3221 _ => Ok(55),
3222 }
3223 }
3224 }
3225 };
3226
3227 let fan = tokio::spawn({
3228 let clock = clock.clone();
3229 async move {
3230 hedged_fan_out(
3231 0usize,
3232 peer_source(3),
3233 operation,
3234 move |k| Duration::from_secs(100) * u32::try_from(k).unwrap_or(u32::MAX),
3236 &clock,
3237 )
3238 .await
3239 }
3240 });
3241
3242 assert_eq!(started_rx.recv().await, Some(0));
3243 release0.notify_one();
3245 assert_eq!(started_rx.recv().await, Some(1));
3246 assert_eq!(started_rx.recv().await, Some(2));
3247 assert_eq!(fan.await.unwrap(), Ok(55));
3248 assert_eq!(clock.current_time(), Timestamp::from(0));
3250 }
3251}