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,
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::{BlockProposal, BundleExecutionPolicy, ChainAndHeight, LiteVote, ProposedBlock},
34 manager::LockingBlock,
35 types::{
36 Block, CertificateValue, ConfirmedBlock, ConfirmedBlockCertificate, GenericCertificate,
37 LiteCertificate, ValidatedBlock, ValidatedBlockCertificate,
38 },
39 ChainError, ChainIdSet,
40};
41use linera_execution::committee::Committee;
42use linera_storage::{Arc as CacheArc, Clock as _, ResultReadCertificates, Storage as _};
43use rand::prelude::SliceRandom as _;
44use received_log::ReceivedLogs;
45use serde::{Deserialize, Serialize};
46use tokio::sync::mpsc;
47use tracing::{debug, error, info, instrument, trace, warn};
48
49use crate::{
50 data_types::{ChainInfo, ChainInfoQuery, ChainInfoResponse},
51 environment::{wallet::Wallet as _, Environment},
52 local_node::{LocalNodeClient, LocalNodeError},
53 node::{CrossChainMessageDelivery, NodeError, ValidatorNode, ValidatorNodeProvider as _},
54 notifier::{ChannelNotifier, Notifier as _},
55 remote_node::RemoteNode,
56 updater::{communicate_with_quorum, CommunicateAction, RemoteNodeUpdater},
57 worker::{Notification, ProcessableCertificate, Reason, WorkerError, WorkerState},
58 ChainWorkerConfig, ProcessConfirmedBlockMode, CHAIN_INFO_MAX_RECEIVED_LOG_ENTRIES,
59};
60
61pub mod chain_client;
63pub use chain_client::ChainClient;
64
65pub use crate::data_types::ClientOutcome;
66
67#[cfg(test)]
68#[path = "../unit_tests/client_tests.rs"]
69mod client_tests;
70pub mod requests_scheduler;
71
72pub use requests_scheduler::{RequestsScheduler, RequestsSchedulerConfig, ScoringWeights};
73mod received_log;
74mod validator_trackers;
75
76#[cfg(with_metrics)]
77mod metrics {
78 use std::sync::LazyLock;
79
80 use linera_base::prometheus_util::{
81 exponential_bucket_latencies, register_histogram_vec, register_int_counter_vec,
82 };
83 use prometheus::{HistogramVec, IntCounterVec};
84
85 pub static PROCESS_INBOX_WITHOUT_PREPARE_LATENCY: LazyLock<HistogramVec> =
86 LazyLock::new(|| {
87 register_histogram_vec(
88 "process_inbox_latency",
89 "process_inbox latency",
90 &[],
91 exponential_bucket_latencies(10_000.0),
92 )
93 });
94
95 pub static PREPARE_CHAIN_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
96 register_histogram_vec(
97 "prepare_chain_latency",
98 "prepare_chain latency",
99 &[],
100 exponential_bucket_latencies(10_000.0),
101 )
102 });
103
104 pub static SYNCHRONIZE_CHAIN_STATE_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
105 register_histogram_vec(
106 "synchronize_chain_state_latency",
107 "synchronize_chain_state latency",
108 &[],
109 exponential_bucket_latencies(10_000.0),
110 )
111 });
112
113 pub static EXECUTE_BLOCK_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
114 register_histogram_vec(
115 "execute_block_latency",
116 "execute_block latency",
117 &[],
118 exponential_bucket_latencies(10_000.0),
119 )
120 });
121
122 pub static FIND_RECEIVED_CERTIFICATES_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
123 register_histogram_vec(
124 "find_received_certificates_latency",
125 "find_received_certificates latency",
126 &[],
127 exponential_bucket_latencies(10_000.0),
128 )
129 });
130
131 pub static BLOCK_STAGING_FAILURES_TOTAL: LazyLock<IntCounterVec> = LazyLock::new(|| {
132 register_int_counter_vec(
133 "block_staging_failures_total",
134 "Total number of client block staging (execute_block) failures, labelled by error type",
135 &["error_type"],
136 )
137 });
138}
139
140pub static DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE: u64 = 500;
142pub static DEFAULT_CERTIFICATE_UPLOAD_BATCH_SIZE: u64 = 500;
144pub static DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE: usize = 20_000;
146pub static DEFAULT_MAX_EVENT_STREAM_QUERIES: usize = 1000;
148pub static DEFAULT_MAX_CONCURRENT_BATCH_DOWNLOADS: usize = 1;
150
151#[derive(Debug, Clone, Copy)]
153#[allow(missing_docs)]
154pub enum TimingType {
155 ExecuteOperations,
156 ExecuteBlock,
157 SubmitBlockProposal,
158 UpdateValidators,
159}
160
161#[derive(Debug, Clone, PartialEq, Eq)]
166pub enum ListeningMode {
167 FullChain,
170 FollowChain,
174 EventsOnly(BTreeSet<StreamId>),
176}
177
178impl PartialOrd for ListeningMode {
179 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
180 match (self, other) {
181 (ListeningMode::FullChain, ListeningMode::FullChain) => Some(Ordering::Equal),
182 (ListeningMode::FullChain, _) => Some(Ordering::Greater),
183 (_, ListeningMode::FullChain) => Some(Ordering::Less),
184 (ListeningMode::FollowChain, ListeningMode::FollowChain) => Some(Ordering::Equal),
185 (ListeningMode::FollowChain, ListeningMode::EventsOnly(_)) => Some(Ordering::Greater),
186 (ListeningMode::EventsOnly(_), ListeningMode::FollowChain) => Some(Ordering::Less),
187 (ListeningMode::EventsOnly(a), ListeningMode::EventsOnly(b)) => {
188 if a == b {
189 Some(Ordering::Equal)
190 } else if a.is_superset(b) {
191 Some(Ordering::Greater)
192 } else if b.is_superset(a) {
193 Some(Ordering::Less)
194 } else {
195 None
196 }
197 }
198 }
199 }
200}
201
202impl ListeningMode {
203 pub fn is_relevant(&self, reason: &Reason) -> bool {
206 match (reason, self) {
207 (Reason::NewEvents { .. }, ListeningMode::FollowChain | ListeningMode::FullChain) => {
208 false
209 }
210 (_, ListeningMode::FullChain) => true,
212 (Reason::NewBlock { .. }, ListeningMode::FollowChain) => true,
215 (_, ListeningMode::FollowChain) => false,
216 (Reason::NewEvents { event_streams, .. }, ListeningMode::EventsOnly(relevant))
220 | (Reason::NewBlock { event_streams, .. }, ListeningMode::EventsOnly(relevant)) => {
221 relevant.intersection(event_streams).next().is_some()
222 }
223 (_, ListeningMode::EventsOnly(_)) => false,
224 }
225 }
226
227 pub fn extend(&mut self, other: Option<ListeningMode>) {
229 match (self, other) {
230 (_, None) => (),
231 (ListeningMode::FullChain, _) => (),
232 (mode, Some(ListeningMode::FullChain)) => {
233 *mode = ListeningMode::FullChain;
234 }
235 (ListeningMode::FollowChain, _) => (),
236 (mode, Some(ListeningMode::FollowChain)) => {
237 *mode = ListeningMode::FollowChain;
238 }
239 (
240 ListeningMode::EventsOnly(self_events),
241 Some(ListeningMode::EventsOnly(other_events)),
242 ) => {
243 self_events.extend(other_events);
244 }
245 }
246 }
247
248 pub fn is_follow_only(&self) -> bool {
251 !matches!(self, ListeningMode::FullChain)
252 }
253
254 pub fn is_full(&self) -> bool {
257 matches!(self, ListeningMode::FullChain)
258 }
259
260 pub fn should_sync_chain_state(&self) -> bool {
263 match self {
264 ListeningMode::FullChain | ListeningMode::FollowChain => true,
265 ListeningMode::EventsOnly(_) => false,
266 }
267 }
268}
269
270#[derive(Debug)]
280pub struct ChainModes {
281 modes: BTreeMap<ChainId, ListeningMode>,
282 full: Arc<Hashed<ChainIdSet>>,
283}
284
285impl Default for ChainModes {
286 fn default() -> Self {
287 Self::new(BTreeMap::new())
288 }
289}
290
291impl ChainModes {
292 pub fn new(modes: BTreeMap<ChainId, ListeningMode>) -> Self {
294 let full = Self::compute_full(&modes);
295 Self { modes, full }
296 }
297
298 fn compute_full(modes: &BTreeMap<ChainId, ListeningMode>) -> Arc<Hashed<ChainIdSet>> {
299 Arc::new(Hashed::new(ChainIdSet(
300 modes
301 .iter()
302 .filter(|(_, mode)| mode.is_full())
303 .map(|(id, _)| *id)
304 .collect(),
305 )))
306 }
307
308 pub fn full(&self) -> Arc<Hashed<ChainIdSet>> {
310 self.full.clone()
311 }
312
313 pub fn get(&self, chain_id: &ChainId) -> Option<&ListeningMode> {
315 self.modes.get(chain_id)
316 }
317
318 pub fn extend_mode(&mut self, chain_id: ChainId, mode: ListeningMode) -> ListeningMode {
322 let entry = self
323 .modes
324 .entry(chain_id)
325 .or_insert_with(|| ListeningMode::EventsOnly(BTreeSet::new()));
326 let was_full = entry.is_full();
327 entry.extend(Some(mode));
328 let result = entry.clone();
329 if !was_full && result.is_full() {
330 self.full = Self::compute_full(&self.modes);
331 }
332 result
333 }
334}
335
336pub struct Client<Env: Environment> {
338 environment: Env,
339 pub local_node: LocalNodeClient<Env::Storage>,
342 requests_scheduler: Arc<RequestsScheduler<Env>>,
344 admin_chain_id: ChainId,
346 chain_modes: Arc<RwLock<ChainModes>>,
349 notifier: Arc<ChannelNotifier<Notification>>,
351 chains: papaya::HashMap<ChainId, chain_client::State>,
353 options: chain_client::Options,
355}
356
357#[cfg(not(web))]
361type ReceiveSenderCertificateFuture<'a> =
362 std::pin::Pin<Box<dyn Future<Output = Result<(), chain_client::Error>> + Send + 'a>>;
363#[cfg(web)]
364type ReceiveSenderCertificateFuture<'a> =
365 std::pin::Pin<Box<dyn Future<Output = Result<(), chain_client::Error>> + 'a>>;
366
367impl<Env: Environment> Client<Env> {
368 #[instrument(level = "trace", skip_all)]
370 #[expect(clippy::too_many_arguments)]
371 pub fn new(
372 environment: Env,
373 admin_chain_id: ChainId,
374 long_lived_services: bool,
375 chain_modes: impl IntoIterator<Item = (ChainId, ListeningMode)>,
376 name: impl Into<String>,
377 chain_worker_ttl: Option<Duration>,
378 sender_chain_worker_ttl: Option<Duration>,
379 cross_chain_batch_size_limit: usize,
380 options: chain_client::Options,
381 requests_scheduler_config: &requests_scheduler::RequestsSchedulerConfig,
382 block_cache_size: usize,
383 execution_state_cache_size: usize,
384 ) -> Self {
385 let mut modes = chain_modes.into_iter().collect::<BTreeMap<_, _>>();
386 modes
390 .entry(admin_chain_id)
391 .or_insert(ListeningMode::FullChain)
392 .extend(Some(ListeningMode::FullChain));
393 let chain_modes = Arc::new(RwLock::new(ChainModes::new(modes)));
394 let config = ChainWorkerConfig {
395 nickname: name.into(),
396 long_lived_services,
397 allow_inactive_chains: true,
398 allow_messages_from_deprecated_epochs: true,
399 ttl: chain_worker_ttl,
400 sender_chain_ttl: sender_chain_worker_ttl,
401 block_cache_size,
402 execution_state_cache_size,
403 cross_chain_batch_size_limit,
404 ..ChainWorkerConfig::default()
405 };
406 let state = WorkerState::new(
407 environment.storage().clone(),
408 config,
409 Some(chain_modes.clone()),
410 );
411 let clock = environment.storage().clock().clone();
412 let local_node = LocalNodeClient::new(state);
413 let requests_scheduler = Arc::new(RequestsScheduler::new(
414 vec![],
415 requests_scheduler_config,
416 clock,
417 ));
418
419 Self {
420 environment,
421 local_node,
422 requests_scheduler,
423 chains: papaya::HashMap::new(),
424 admin_chain_id,
425 chain_modes,
426 notifier: Arc::new(ChannelNotifier::default()),
427 options,
428 }
429 }
430
431 pub fn admin_chain_id(&self) -> ChainId {
433 self.admin_chain_id
434 }
435
436 pub fn subscribe(
438 &self,
439 chain_ids: Vec<ChainId>,
440 ) -> tokio::sync::mpsc::UnboundedReceiver<Notification> {
441 self.notifier.subscribe(chain_ids)
442 }
443
444 pub fn subscribe_extra(
446 &self,
447 chain_ids: Vec<ChainId>,
448 sender: &tokio::sync::mpsc::UnboundedSender<Notification>,
449 ) {
450 self.notifier.add_sender(chain_ids, sender);
451 }
452
453 pub fn storage_client(&self) -> &Env::Storage {
455 self.environment.storage()
456 }
457
458 async fn try_read_local_certificate(
461 &self,
462 chain_id: ChainId,
463 height: BlockHeight,
464 hash: Option<CryptoHash>,
465 ) -> Result<Option<CacheArc<ConfirmedBlockCertificate>>, chain_client::Error> {
466 if let Some(hash) = hash {
467 return Ok(self.storage_client().read_certificate(hash).await?);
468 }
469 let results = self
470 .storage_client()
471 .read_certificates_by_heights(chain_id, &[height])
472 .await?;
473 Ok(results.into_iter().next().flatten())
474 }
475
476 pub fn validator_node_provider(&self) -> &Env::Network {
478 self.environment.network()
479 }
480
481 pub async fn retry_pending_cross_chain_requests(
483 &self,
484 sender_chain: ChainId,
485 ) -> Result<(), LocalNodeError> {
486 self.local_node
487 .retry_pending_cross_chain_requests(sender_chain, &self.notifier)
488 .await
489 }
490
491 #[instrument(level = "trace", skip(self))]
493 pub fn signer(&self) -> &Env::Signer {
494 self.environment.signer()
495 }
496
497 pub async fn has_key_for(&self, owner: &AccountOwner) -> Result<bool, chain_client::Error> {
499 self.signer()
500 .contains_key(owner)
501 .await
502 .map_err(chain_client::Error::signer_failure)
503 }
504
505 pub fn wallet(&self) -> &Env::Wallet {
507 self.environment.wallet()
508 }
509
510 async fn is_chain_follow_only(&self, chain_id: ChainId) -> bool {
515 match self.wallet().get(chain_id).await {
516 Ok(Some(chain)) => chain.owner.is_none(),
517 Ok(None) | Err(_) => true,
519 }
520 }
521
522 #[instrument(level = "trace", skip(self))]
525 pub fn extend_chain_mode(&self, chain_id: ChainId, mode: ListeningMode) -> ListeningMode {
526 self.chain_modes
527 .write()
528 .expect("Panics should not happen while holding a lock to `chain_modes`")
529 .extend_mode(chain_id, mode)
530 }
531
532 pub fn chain_mode(&self, chain_id: ChainId) -> Option<ListeningMode> {
534 self.chain_modes
535 .read()
536 .expect("Panics should not happen while holding a lock to `chain_modes`")
537 .get(&chain_id)
538 .cloned()
539 }
540
541 pub fn is_tracked(&self, chain_id: ChainId) -> bool {
543 self.chain_modes
544 .read()
545 .expect("Panics should not happen while holding a lock to `chain_modes`")
546 .get(&chain_id)
547 .is_some_and(ListeningMode::is_full)
548 }
549
550 #[instrument(level = "trace", skip_all, fields(chain_id, next_block_height))]
552 pub fn create_chain_client(
553 self: &Arc<Self>,
554 chain_id: ChainId,
555 block_hash: Option<CryptoHash>,
556 next_block_height: BlockHeight,
557 pending_proposal: &Option<PendingProposal>,
558 preferred_owner: Option<AccountOwner>,
559 timing_sender: Option<mpsc::UnboundedSender<(u64, TimingType)>>,
560 ) -> ChainClient<Env> {
561 self.chains.pin().get_or_insert_with(chain_id, || {
564 chain_client::State::new(pending_proposal.clone())
565 });
566
567 ChainClient::new(
568 self.clone(),
569 chain_id,
570 self.options.clone(),
571 block_hash,
572 next_block_height,
573 preferred_owner,
574 timing_sender,
575 )
576 }
577
578 async fn fetch_chain_info(
580 &self,
581 chain_id: ChainId,
582 validators: &[RemoteNode<Env::ValidatorNode>],
583 ) -> Result<Box<ChainInfo>, chain_client::Error> {
584 match self.local_node.chain_info(chain_id).await {
585 Ok(info) => Ok(info),
586 Err(LocalNodeError::BlobsNotFound(blob_ids)) => {
587 Box::pin(self.synchronize_chain_state(self.admin_chain_id)).await?;
590 self.update_local_node_with_blobs_from(blob_ids, validators)
591 .await?;
592 Ok(self.local_node.chain_info(chain_id).await?)
593 }
594 Err(err) => Err(err.into()),
595 }
596 }
597
598 #[instrument(level = "trace", skip(self))]
600 async fn download_certificates(
601 &self,
602 chain_id: ChainId,
603 target_next_block_height: BlockHeight,
604 ) -> Result<Box<ChainInfo>, chain_client::Error> {
605 let validators = self.validator_nodes().await?;
606 let mut info = Box::pin(self.fetch_chain_info(chain_id, &validators)).await?;
607 if target_next_block_height <= info.next_block_height {
608 return Ok(info);
609 }
610 info = self
611 .load_local_certificates(chain_id, target_next_block_height, None)
612 .await?;
613 let mut next_height = info.next_block_height;
614 while next_height < target_next_block_height {
616 let limit = u64::from(target_next_block_height)
617 .checked_sub(u64::from(next_height))
618 .ok_or(ArithmeticError::Overflow)?
619 .min(self.options.certificate_download_batch_size);
620 let certificates = self
621 .requests_scheduler
622 .download_certificates_from_validators(
623 &validators,
624 chain_id,
625 next_height,
626 limit,
627 self.options.certificate_batch_download_hedge_delay,
628 )
629 .await?;
630 let Some(new_info) = self
631 .process_certificates(
632 &validators,
633 certificates,
634 None,
635 ProcessConfirmedBlockMode::Execute,
636 )
637 .await?
638 else {
639 break;
640 };
641 assert!(new_info.next_block_height > next_height);
642 next_height = new_info.next_block_height;
643 info = new_info;
644 }
645 ensure!(
646 target_next_block_height <= info.next_block_height,
647 chain_client::Error::CannotDownloadCertificates {
648 chain_id,
649 target_next_block_height,
650 }
651 );
652 Ok(info)
653 }
654
655 async fn load_local_certificates(
660 &self,
661 chain_id: ChainId,
662 end: BlockHeight,
663 until_block_time: Option<Timestamp>,
664 ) -> Result<Box<ChainInfo>, chain_client::Error> {
665 let mut last_info = self.local_node.chain_info(chain_id).await?;
666 let next_height = last_info.next_block_height;
667 let hashes = self
668 .local_node
669 .get_preprocessed_block_hashes(chain_id, next_height, end)
670 .await?;
671 let certificates = self.storage_client().read_certificates(&hashes).await?;
672 let certificates = match ResultReadCertificates::new(certificates, hashes) {
673 ResultReadCertificates::Certificates(certificates) => certificates,
674 ResultReadCertificates::InvalidHashes(hashes) => {
675 return Err(chain_client::Error::ReadCertificatesError(hashes))
676 }
677 };
678 for certificate in certificates {
679 if let Some(until) = until_block_time {
680 if certificate.value().block().header.timestamp >= until {
681 break;
682 }
683 }
684 last_info = self.handle_certificate(certificate).await?.info;
685 }
686 Ok(last_info)
687 }
688
689 #[instrument(level = "trace", skip_all)]
695 async fn download_certificates_from(
696 &self,
697 remote_node: &RemoteNode<Env::ValidatorNode>,
698 chain_id: ChainId,
699 stop: BlockHeight,
700 until_block_time: Option<Timestamp>,
701 ) -> Result<Box<ChainInfo>, chain_client::Error> {
702 let mut last_info = self
703 .load_local_certificates(chain_id, stop, until_block_time)
704 .await?;
705 let mut next_height = last_info.next_block_height;
706
707 if next_height >= stop {
708 return Ok(last_info);
709 }
710
711 #[cfg(not(web))]
715 type CertificateBatchFuture = std::pin::Pin<
716 Box<dyn Future<Output = Result<Vec<ConfirmedBlockCertificate>, NodeError>> + Send>,
717 >;
718 #[cfg(web)]
719 type CertificateBatchFuture = std::pin::Pin<
720 Box<dyn Future<Output = Result<Vec<ConfirmedBlockCertificate>, NodeError>>>,
721 >;
722
723 let max_concurrent = self.options.max_concurrent_batch_downloads;
724 let batch_size = self.options.certificate_download_batch_size;
725 let (sender, mut receiver) = tokio::sync::mpsc::channel(max_concurrent);
726 let scheduler = self.requests_scheduler.clone();
727 let remote = remote_node.clone();
728
729 let download_task = linera_base::Task::spawn(async move {
730 let mut download_height = next_height;
731 let mut in_flight = FuturesOrdered::<CertificateBatchFuture>::new();
732
733 let try_enqueue = |in_flight: &mut FuturesOrdered<CertificateBatchFuture>,
734 download_height: &mut BlockHeight| {
735 if *download_height >= stop {
736 return;
737 }
738 let limit = u64::from(stop)
739 .saturating_sub(u64::from(*download_height))
740 .min(batch_size);
741 let height = *download_height;
742 let scheduler = scheduler.clone();
743 let remote = remote.clone();
744 in_flight.push_back(Box::pin(async move {
745 scheduler
746 .download_certificates(&remote, chain_id, height, limit)
747 .await
748 }));
749 *download_height = BlockHeight(u64::from(*download_height) + limit);
750 };
751
752 while in_flight.len() < max_concurrent && download_height < stop {
753 try_enqueue(&mut in_flight, &mut download_height);
754 }
755
756 while let Some(result) = in_flight.next().await {
757 if sender.send(result).await.is_err() {
758 break;
759 }
760 try_enqueue(&mut in_flight, &mut download_height);
761 }
762 });
763
764 while let Some(result) = receiver.recv().await {
766 let certificates = result?;
767 let Some(info) = self
768 .process_certificates(
769 slice::from_ref(remote_node),
770 certificates,
771 until_block_time,
772 ProcessConfirmedBlockMode::Execute,
773 )
774 .await?
775 else {
776 break;
777 };
778 assert!(info.next_block_height >= next_height);
779 next_height = info.next_block_height;
780 last_info = info;
781 }
782 download_task.await;
785 Ok(last_info)
786 }
787
788 async fn download_blobs(
789 &self,
790 remote_nodes: &[RemoteNode<Env::ValidatorNode>],
791 blob_ids: &[BlobId],
792 ) -> Result<(), chain_client::Error> {
793 let blobs = &self
794 .requests_scheduler
795 .download_blobs(
796 remote_nodes,
797 blob_ids,
798 self.options.blob_download_hedge_delay,
799 )
800 .await?
801 .ok_or_else(|| {
802 chain_client::Error::RemoteNodeError(NodeError::BlobsNotFound(blob_ids.to_vec()))
803 })?;
804 self.local_node.store_blobs(blobs).await.map_err(Into::into)
805 }
806
807 #[instrument(level = "trace", skip_all)]
812 pub(crate) async fn download_certificates_for_events(
813 &self,
814 event_ids: &[EventId],
815 ) -> Result<(), chain_client::Error> {
816 let validators = self.validator_nodes().await?;
817 let timeout = self.options.certificate_batch_download_hedge_delay;
818 let mut required_by_chain = BTreeMap::<_, BTreeMap<StreamId, u32>>::new();
820 for event_id in event_ids {
821 required_by_chain
822 .entry(event_id.chain_id)
823 .or_default()
824 .entry(event_id.stream_id.clone())
825 .and_modify(|max| *max = (*max).max(event_id.index))
826 .or_insert(event_id.index);
827 }
828
829 for (chain_id, required_streams) in required_by_chain {
830 let stream_ids = required_streams.keys().cloned().collect::<BTreeSet<_>>();
831 let stream_ids_ref = &stream_ids;
832 let required_ref = &required_streams;
833 let result = communicate_concurrently(
834 &validators,
835 move |remote_node| {
836 Box::pin(async move {
837 self.sync_events_from_node(chain_id, stream_ids_ref, &remote_node)
838 .await?;
839 let next_expected = self
840 .local_node
841 .next_expected_events(
842 chain_id,
843 stream_ids_ref.iter().cloned().collect(),
844 )
845 .await?;
846 if required_ref.iter().all(|(stream_id, &max_index)| {
847 next_expected
848 .get(stream_id)
849 .is_some_and(|index| *index > max_index)
850 }) {
851 Ok::<(), chain_client::Error>(())
852 } else {
853 Err(chain_client::Error::InternalError("missing events"))
855 }
856 })
857 },
858 |errors| {
859 for (validator, error) in &errors {
860 warn!(
861 %validator,
862 %chain_id,
863 %error,
864 "failed to sync events from validator",
865 );
866 }
867 chain_client::Error::InternalError("missing events")
868 },
869 timeout,
870 self.storage_client().clock(),
871 )
872 .await;
873
874 if result.is_err() {
875 let next_expected = self
876 .local_node
877 .next_expected_events(chain_id, stream_ids.into_iter().collect())
878 .await?;
879 let missing = event_ids
880 .iter()
881 .filter(|id| {
882 id.chain_id == chain_id
883 && next_expected
884 .get(&id.stream_id)
885 .is_none_or(|index| *index <= id.index)
886 })
887 .cloned()
888 .collect();
889 return Err(NodeError::EventsNotFound(missing).into());
890 }
891 }
892 Ok(())
893 }
894
895 #[instrument(level = "trace", skip_all)]
900 async fn process_certificates(
901 &self,
902 remote_nodes: &[RemoteNode<Env::ValidatorNode>],
903 certificates: Vec<ConfirmedBlockCertificate>,
904 until_block_time: Option<Timestamp>,
905 mode: ProcessConfirmedBlockMode,
906 ) -> Result<Option<Box<ChainInfo>>, chain_client::Error> {
907 let mut info = None;
908 let created_blob_ids = certificates
913 .iter()
914 .flat_map(|certificate| certificate.value().block().created_blob_ids())
915 .collect::<BTreeSet<BlobId>>();
916 let required_blob_ids = certificates
917 .iter()
918 .flat_map(|certificate| certificate.value().required_blob_ids())
919 .filter(|blob_id| !created_blob_ids.contains(blob_id))
920 .collect::<Vec<_>>();
921
922 match self
923 .local_node
924 .read_blob_states_from_storage(&required_blob_ids)
925 .await
926 {
927 Err(LocalNodeError::BlobsNotFound(blob_ids)) => {
928 self.download_blobs(remote_nodes, &blob_ids).await?;
929 }
930 x => {
931 x?;
932 }
933 }
934
935 for certificate in certificates {
936 if let Some(until) = until_block_time {
937 if certificate.value().block().header.timestamp >= until {
938 break;
939 }
940 }
941 let response = self
942 .handle_certificate_with_retry(&certificate, remote_nodes, mode)
943 .await?;
944 info = Some(response.info);
945 }
946
947 Ok(info)
948 }
949
950 async fn handle_certificate_with_retry(
954 &self,
955 certificate: &ConfirmedBlockCertificate,
956 nodes: &[RemoteNode<Env::ValidatorNode>],
957 mode: ProcessConfirmedBlockMode,
958 ) -> Result<ChainInfoResponse, chain_client::Error> {
959 let mut downloaded_blobs = HashSet::<BlobId>::new();
960 let mut events = EventSetDownloader::new(self);
961 loop {
962 let result = self
963 .handle_confirmed_certificate(certificate.clone(), mode)
964 .await;
965 if let Err(LocalNodeError::BlobsNotFound(blob_ids)) = &result {
966 let new_blobs = filter_new(blob_ids, &downloaded_blobs);
967 if !new_blobs.is_empty() {
968 self.download_blobs(nodes, &new_blobs).await?;
969 downloaded_blobs.extend(new_blobs);
970 continue;
971 }
972 }
973 if let Err(LocalNodeError::EventsNotFound(event_ids)) = &result {
974 if events.download_new(event_ids).await? {
975 continue;
976 }
977 }
978 return Ok(result?);
979 }
980 }
981
982 async fn handle_certificate<T: ProcessableCertificate>(
983 &self,
984 certificate: GenericCertificate<T>,
985 ) -> Result<ChainInfoResponse, LocalNodeError> {
986 let chain_id = certificate.inner().chain_id();
987 let response = self
988 .local_node
989 .handle_certificate(certificate, &self.notifier)
990 .await?;
991 if self.is_tracked(chain_id) {
992 self.update_publisher_chain_modes(chain_id).await?;
993 }
994 Ok(response)
995 }
996
997 async fn update_publisher_chain_modes(&self, chain_id: ChainId) -> Result<(), LocalNodeError> {
1005 let subscriptions = self.local_node.get_event_subscriptions(chain_id).await?;
1006 let mut publishers = BTreeMap::<ChainId, BTreeSet<StreamId>>::new();
1007 for ((publisher_id, stream_name), _) in subscriptions {
1008 publishers
1009 .entry(publisher_id)
1010 .or_default()
1011 .insert(stream_name);
1012 }
1013 if chain_id != self.admin_chain_id {
1014 publishers.entry(self.admin_chain_id).or_default();
1015 }
1016 for (publisher_id, streams) in publishers {
1017 if publisher_id != chain_id {
1018 self.extend_chain_mode(publisher_id, ListeningMode::EventsOnly(streams));
1019 }
1020 }
1021 Ok(())
1022 }
1023
1024 async fn chain_info_with_committees(
1025 &self,
1026 chain_id: ChainId,
1027 ) -> Result<Box<ChainInfo>, LocalNodeError> {
1028 let query = ChainInfoQuery::new(chain_id).with_committees();
1029 let info = self.local_node.handle_chain_info_query(query).await?.info;
1030 Ok(info)
1031 }
1032
1033 #[instrument(level = "trace", skip_all)]
1041 async fn admin_committees(
1042 &self,
1043 ) -> Result<(Epoch, BTreeMap<Epoch, Committee>), LocalNodeError> {
1044 let query = ChainInfoQuery::new(self.admin_chain_id);
1045 let info = self.local_node.handle_chain_info_query(query).await?.info;
1046 let max_epoch = info.epoch;
1047 let mut committees = BTreeMap::new();
1048 let mut needs_fallback = false;
1049 for index in 0..=max_epoch.0 {
1050 let epoch = Epoch(index);
1051 if let Some(committee) = self.storage_client().get_or_load_committee(epoch).await? {
1052 committees.insert(epoch, (*committee).clone());
1053 } else {
1054 needs_fallback = true;
1055 }
1056 }
1057 if needs_fallback {
1058 let info = self.chain_info_with_committees(self.admin_chain_id).await?;
1059 if let Some(chain_state) = info.requested_committees.as_ref() {
1060 for (epoch, committee) in chain_state {
1061 if !committees.contains_key(epoch) {
1062 self.storage_client()
1063 .shared_committees()
1064 .insert(*epoch, Arc::new(committee.clone()));
1065 committees.insert(*epoch, committee.clone());
1066 }
1067 }
1068 }
1069 }
1070 Ok((max_epoch, committees))
1071 }
1072
1073 async fn handle_confirmed_certificate(
1074 &self,
1075 certificate: ConfirmedBlockCertificate,
1076 mode: ProcessConfirmedBlockMode,
1077 ) -> Result<ChainInfoResponse, LocalNodeError> {
1078 let chain_id = certificate.inner().chain_id();
1079 let response = self
1080 .local_node
1081 .handle_confirmed_certificate(certificate, mode, &self.notifier)
1082 .await?;
1083 if self.is_tracked(chain_id) {
1084 self.update_publisher_chain_modes(chain_id).await?;
1085 }
1086 Ok(response)
1087 }
1088
1089 pub async fn admin_committee(&self) -> Result<(Epoch, Arc<Committee>), LocalNodeError> {
1093 let query = ChainInfoQuery::new(self.admin_chain_id);
1094 let info = self.local_node.handle_chain_info_query(query).await?.info;
1095 let epoch = info.epoch;
1096 if let Some(committee) = self.storage_client().get_or_load_committee(epoch).await? {
1097 return Ok((epoch, committee));
1098 }
1099 let info = self.chain_info_with_committees(self.admin_chain_id).await?;
1103 let committee = info
1104 .requested_committees
1105 .as_ref()
1106 .and_then(|m| m.get(&epoch).cloned())
1107 .ok_or(LocalNodeError::InactiveChain(self.admin_chain_id))?;
1108 let arc_committee = self
1109 .storage_client()
1110 .shared_committees()
1111 .insert(epoch, Arc::new(committee));
1112 Ok((epoch, arc_committee))
1113 }
1114
1115 async fn validator_nodes(
1117 &self,
1118 ) -> Result<Vec<RemoteNode<Env::ValidatorNode>>, chain_client::Error> {
1119 let (_, committee) = self.admin_committee().await?;
1120 Ok(self.make_nodes(&committee)?)
1121 }
1122
1123 fn make_nodes(
1125 &self,
1126 committee: &Committee,
1127 ) -> Result<Vec<RemoteNode<Env::ValidatorNode>>, NodeError> {
1128 Ok(self
1129 .validator_node_provider()
1130 .make_nodes(committee)?
1131 .map(|(public_key, node)| RemoteNode { public_key, node })
1132 .collect())
1133 }
1134
1135 pub async fn get_chain_description_blob(
1138 &self,
1139 chain_id: ChainId,
1140 ) -> Result<Arc<Blob>, chain_client::Error> {
1141 let chain_desc_id = BlobId::new(chain_id.0, BlobType::ChainDescription);
1142 let blob = self
1143 .local_node
1144 .storage_client()
1145 .read_blob(chain_desc_id)
1146 .await?;
1147 if let Some(blob) = blob {
1148 return Ok(blob.into_std());
1150 }
1151 Box::pin(self.synchronize_chain_state(self.admin_chain_id)).await?;
1153 let nodes = self.validator_nodes().await?;
1154 Ok(self
1155 .update_local_node_with_blobs_from(vec![chain_desc_id], &nodes)
1156 .await?
1157 .pop()
1158 .unwrap() .into_std())
1160 }
1161
1162 pub async fn get_chain_description(
1165 &self,
1166 chain_id: ChainId,
1167 ) -> Result<ChainDescription, chain_client::Error> {
1168 let blob = self.get_chain_description_blob(chain_id).await?;
1169 Ok(bcs::from_bytes(blob.bytes())?)
1170 }
1171
1172 pub async fn get_application_description_blob(
1177 &self,
1178 application_id: ApplicationId,
1179 ) -> Result<Arc<Blob>, chain_client::Error> {
1180 let blob_id = application_id.description_blob_id();
1181 let blob = self.local_node.storage_client().read_blob(blob_id).await?;
1182 if let Some(blob) = blob {
1183 return Ok(blob.into_std());
1185 }
1186 Box::pin(self.synchronize_chain_state(self.admin_chain_id)).await?;
1188 let nodes = self.validator_nodes().await?;
1189 Ok(self
1190 .update_local_node_with_blobs_from(vec![blob_id], &nodes)
1191 .await?
1192 .pop()
1193 .unwrap() .into_std())
1195 }
1196
1197 pub async fn get_application_description(
1200 &self,
1201 application_id: ApplicationId,
1202 ) -> Result<ApplicationDescription, chain_client::Error> {
1203 let blob = self
1204 .get_application_description_blob(application_id)
1205 .await?;
1206 Ok(bcs::from_bytes(blob.bytes())?)
1207 }
1208
1209 #[instrument(level = "trace", skip_all)]
1211 async fn finalize_block(
1212 self: &Arc<Self>,
1213 committee: &Committee,
1214 certificate: ValidatedBlockCertificate,
1215 ) -> Result<ConfirmedBlockCertificate, chain_client::Error> {
1216 debug!(round = %certificate.round, "Submitting block for confirmation");
1217 let hashed_value = ConfirmedBlock::new(certificate.inner().block().clone());
1218 let finalize_action = CommunicateAction::FinalizeBlock {
1219 certificate: Box::new(certificate),
1220 delivery: self.options.cross_chain_message_delivery,
1221 };
1222 let certificate = self
1223 .communicate_chain_action(committee, finalize_action, hashed_value)
1224 .await?;
1225 self.receive_certificate_with_checked_signatures(
1226 certificate.clone(),
1227 ProcessConfirmedBlockMode::Execute,
1228 )
1229 .await?;
1230 Ok(certificate)
1231 }
1232
1233 #[instrument(level = "trace", skip_all)]
1235 async fn submit_block_proposal<T: ProcessableCertificate>(
1236 self: &Arc<Self>,
1237 committee: &Committee,
1238 proposal: Box<BlockProposal>,
1239 value: T,
1240 ) -> Result<GenericCertificate<T>, chain_client::Error> {
1241 debug!(
1242 round = %proposal.content.round,
1243 "Submitting block proposal to validators"
1244 );
1245
1246 let block_timestamp = proposal.content.block.timestamp;
1248 let local_time = self.local_node.storage_client().clock().current_time();
1249 if block_timestamp > local_time {
1250 info!(
1251 chain_id = %proposal.content.block.chain_id,
1252 %block_timestamp,
1253 %local_time,
1254 "Block timestamp is in the future; waiting until it can be proposed",
1255 );
1256 }
1257
1258 let (clock_skew_sender, mut clock_skew_receiver) = mpsc::unbounded_channel();
1260 let submit_action = CommunicateAction::SubmitBlock {
1261 proposal,
1262 blob_ids: value.required_blob_ids().into_iter().collect(),
1263 clock_skew_sender,
1264 };
1265
1266 let validity_threshold = committee.validity_threshold();
1268 let committee_clone = committee.clone();
1269 let clock_skew_check_handle = linera_base::Task::spawn(async move {
1270 let mut skew_weight = 0u64;
1271 let mut min_skew = TimeDelta::MAX;
1272 let mut max_skew = TimeDelta::ZERO;
1273 while let Some((public_key, clock_skew)) = clock_skew_receiver.recv().await {
1274 if clock_skew.as_micros() > 0 {
1275 skew_weight += committee_clone.weight(&public_key);
1276 min_skew = min_skew.min(clock_skew);
1277 max_skew = max_skew.max(clock_skew);
1278 if skew_weight >= validity_threshold {
1279 warn!(
1280 skew_weight,
1281 validity_threshold,
1282 min_skew_ms = min_skew.as_micros() / 1000,
1283 max_skew_ms = max_skew.as_micros() / 1000,
1284 "A validity threshold of validators reported clock skew; \
1285 consider checking your system clock",
1286 );
1287 return;
1288 }
1289 }
1290 }
1291 });
1292
1293 let certificate = self
1294 .communicate_chain_action(committee, submit_action, value)
1295 .await?;
1296
1297 clock_skew_check_handle.await;
1298
1299 self.handle_certificate(certificate.clone()).await?;
1300 Ok(certificate)
1301 }
1302
1303 fn remote_node_updater(
1305 &self,
1306 remote_node: RemoteNode<Env::ValidatorNode>,
1307 ) -> RemoteNodeUpdater<Env> {
1308 RemoteNodeUpdater {
1309 remote_node,
1310 local_node: self.local_node.clone(),
1311 admin_chain_id: self.admin_chain_id,
1312 certificate_upload_batch_size: self.options.certificate_upload_batch_size,
1313 }
1314 }
1315
1316 async fn process_lag_reports(&self, mut reports: Vec<LagReport<Env::ValidatorNode>>) {
1330 reports.sort_by_key(|report| Reverse(report.remote_progress()));
1331 for report in reports {
1332 if let Err(error) =
1335 Box::pin(self.synchronize_chain_state_from(&report.remote_node, report.chain_id))
1336 .await
1337 {
1338 debug!(
1339 remote_node = report.remote_node.address(),
1340 chain_id = %report.chain_id,
1341 %error,
1342 "failed to pull chain state from a validator that reported being ahead",
1343 );
1344 }
1345 }
1346 }
1347
1348 #[instrument(level = "trace", skip_all, fields(chain_id, block_height, delivery))]
1350 async fn communicate_chain_updates(
1351 self: &Arc<Self>,
1352 committee: &Committee,
1353 chain_id: ChainId,
1354 height: BlockHeight,
1355 delivery: CrossChainMessageDelivery,
1356 latest_certificate: Option<CacheArc<GenericCertificate<ConfirmedBlock>>>,
1357 ) -> Result<(), chain_client::Error> {
1358 let nodes = self.make_nodes(committee)?;
1359 communicate_with_quorum(
1360 &nodes,
1361 committee,
1362 |_: &()| (),
1363 |remote_node| {
1364 let mut updater = self.remote_node_updater(remote_node);
1365 let certificate = latest_certificate.clone();
1366 Box::pin(async move {
1367 updater
1368 .send_chain_information(chain_id, height, delivery, certificate)
1369 .await
1370 })
1371 },
1372 self.options.quorum_grace_period,
1373 )
1374 .await?;
1375 Ok(())
1376 }
1377
1378 #[instrument(level = "trace", skip_all)]
1384 async fn communicate_chain_action<T: CertificateValue>(
1385 self: &Arc<Self>,
1386 committee: &Committee,
1387 action: CommunicateAction,
1388 value: T,
1389 ) -> Result<GenericCertificate<T>, chain_client::Error> {
1390 let lag_reports = Mutex::new(Vec::new());
1395 let nodes = self.make_nodes(committee)?;
1396 let result = communicate_with_quorum(
1397 &nodes,
1398 committee,
1399 |vote: &LiteVote| (vote.value.value_hash, vote.round),
1400 |remote_node| {
1401 let mut updater = self.remote_node_updater(remote_node.clone());
1402 let action = action.clone();
1403 let lag_reports = &lag_reports;
1404 Box::pin(async move {
1405 match updater.send_chain_update(action).await {
1406 Err(chain_client::Error::LocalNodeLagging { chain_id, error }) => {
1407 lag_reports.lock().unwrap().push(LagReport {
1408 remote_node,
1409 chain_id,
1410 error: (*error).clone(),
1411 });
1412 Err((*error).into())
1413 }
1414 result => result,
1415 }
1416 })
1417 },
1418 self.options.quorum_grace_period,
1419 )
1420 .await;
1421 let ((votes_hash, votes_round), votes) = match result {
1422 Ok(quorum) => quorum,
1423 Err(err) => {
1424 self.process_lag_reports(lag_reports.into_inner().unwrap())
1427 .await;
1428 return Err(err.into());
1429 }
1430 };
1431 ensure!(
1432 (votes_hash, votes_round) == (value.hash(), action.round()),
1433 chain_client::Error::UnexpectedQuorum {
1434 hash: votes_hash,
1435 round: votes_round,
1436 expected_hash: value.hash(),
1437 expected_round: action.round(),
1438 }
1439 );
1440 let certificate = LiteCertificate::try_from_votes(votes)
1445 .ok_or_else(|| {
1446 chain_client::Error::InternalError(
1447 "Vote values or rounds don't match; this is a bug",
1448 )
1449 })?
1450 .with_value(value)
1451 .ok_or_else(|| {
1452 chain_client::Error::ProtocolError("A quorum voted for an unexpected value")
1453 })?;
1454 Ok(certificate)
1455 }
1456
1457 #[instrument(level = "trace", skip_all)]
1460 async fn receive_certificate_with_checked_signatures(
1461 &self,
1462 certificate: ConfirmedBlockCertificate,
1463 mode: ProcessConfirmedBlockMode,
1464 ) -> Result<(), chain_client::Error> {
1465 let block = certificate.block();
1466 self.download_certificates(block.header.chain_id, block.header.height)
1468 .await?;
1469 let nodes = self.validator_nodes().await?;
1472 self.handle_certificate_with_retry(&certificate, &nodes, mode)
1473 .await?;
1474 Ok(())
1475 }
1476
1477 #[instrument(level = "trace", skip_all)]
1482 fn receive_sender_certificate(
1483 &self,
1484 certificate: CacheArc<ConfirmedBlockCertificate>,
1485 mode: ReceiveCertificateMode,
1486 nodes: Option<Vec<RemoteNode<Env::ValidatorNode>>>,
1487 ) -> ReceiveSenderCertificateFuture<'_> {
1488 Box::pin(async move {
1489 let (mut max_epoch, mut committees) = self.admin_committees().await?;
1491 if let ReceiveCertificateMode::NeedsCheck = mode {
1492 let mut check_result =
1493 Self::check_certificate(max_epoch, &committees, &certificate)?;
1494 if matches!(check_result, CheckCertificateResult::FutureEpoch) {
1495 let admin_chain_id = self.admin_chain_id;
1502 let epoch = certificate.block().header.epoch;
1503 info!(
1504 %epoch,
1505 "certificate is from an unknown epoch; synchronizing the admin chain"
1506 );
1507 let synced_from_serving_node = if let Some(nodes) = &nodes {
1508 let certificate = &certificate;
1509 communicate_concurrently(
1510 nodes,
1511 async move |node| {
1512 self.synchronize_chain_state_from(&node, admin_chain_id)
1513 .await?;
1514 let (max_epoch, committees) = self.admin_committees().await?;
1515 match Self::check_certificate(max_epoch, &committees, certificate)?
1516 {
1517 CheckCertificateResult::FutureEpoch => {
1518 Err(chain_client::Error::CommitteeSynchronizationError)
1519 }
1520 _ => Ok(()),
1521 }
1522 },
1523 |errors| {
1524 for (validator, error) in &errors {
1525 warn!(
1526 %validator,
1527 %error,
1528 "failed to synchronize the admin chain from validator",
1529 );
1530 }
1531 chain_client::Error::CommitteeSynchronizationError
1532 },
1533 self.options.blob_download_hedge_delay,
1534 self.storage_client().clock(),
1535 )
1536 .await
1537 .is_ok()
1538 } else {
1539 false
1540 };
1541 if synced_from_serving_node {
1542 (max_epoch, committees) = self.admin_committees().await?;
1543 check_result =
1544 Self::check_certificate(max_epoch, &committees, &certificate)?;
1545 }
1546 if matches!(check_result, CheckCertificateResult::FutureEpoch) {
1547 Box::pin(self.synchronize_chain_state(admin_chain_id)).await?;
1548 (max_epoch, committees) = self.admin_committees().await?;
1549 check_result =
1550 Self::check_certificate(max_epoch, &committees, &certificate)?;
1551 }
1552 }
1553 check_result.into_result()?;
1554 }
1555 let nodes = if let Some(nodes) = nodes {
1557 nodes
1558 } else {
1559 self.validator_nodes().await?
1560 };
1561 let processing_mode = if self
1562 .chain_mode(certificate.value().chain_id())
1563 .is_some_and(|m| m.should_sync_chain_state())
1564 {
1565 ProcessConfirmedBlockMode::Auto
1566 } else {
1567 ProcessConfirmedBlockMode::Preprocess
1568 };
1569 self.handle_certificate_with_retry(&certificate, &nodes, processing_mode)
1570 .await?;
1571
1572 Ok(())
1573 })
1574 }
1575
1576 #[instrument(level = "debug", skip_all, fields(chain_id = %sender_chain_id))]
1578 async fn download_and_process_sender_chain(
1579 &self,
1580 sender_chain_id: ChainId,
1581 nodes: &[RemoteNode<Env::ValidatorNode>],
1582 received_log: &ReceivedLogs,
1583 mut remote_heights: Vec<BlockHeight>,
1584 sender: mpsc::UnboundedSender<ChainAndHeight>,
1585 ) {
1586 let (max_epoch, committees) = match self.admin_committees().await {
1587 Ok(result) => result,
1588 Err(error) => {
1589 error!(%error, %sender_chain_id, "could not read admin committees");
1590 return;
1591 }
1592 };
1593 let committees_ref = &committees;
1594 let mut nodes = nodes.to_vec();
1595 while !remote_heights.is_empty() {
1596 if let Ok(local_certs) = self
1599 .storage_client()
1600 .read_certificates_by_heights(sender_chain_id, &remote_heights)
1601 .await
1602 {
1603 let mut still_needed = Vec::new();
1604 for (height, maybe_cert) in remote_heights.iter().copied().zip(local_certs) {
1605 if let Some(certificate) = maybe_cert {
1606 let chain_id = certificate.block().header.chain_id;
1607 if let Err(error) = sender.send(ChainAndHeight { chain_id, height }) {
1608 error!(
1609 %chain_id, %height, %error,
1610 "failed to send chain and height over the channel",
1611 );
1612 }
1613 } else {
1614 still_needed.push(height);
1615 }
1616 }
1617 remote_heights = still_needed;
1618 if remote_heights.is_empty() {
1619 break;
1620 }
1621 }
1622
1623 let remote_heights_ref = &remote_heights;
1624 let certificates = match communicate_concurrently(
1625 &nodes,
1626 async move |remote_node| {
1627 let mut remote_heights = remote_heights_ref.clone();
1628 remote_heights.retain(|height| {
1631 received_log.validator_has_block(
1632 &remote_node.public_key,
1633 sender_chain_id,
1634 *height,
1635 )
1636 });
1637 if remote_heights.is_empty() {
1638 return Err(NodeError::MissingCertificateValue);
1641 }
1642 let certificates = self
1643 .requests_scheduler
1644 .download_certificates_by_heights(
1645 &remote_node,
1646 sender_chain_id,
1647 remote_heights,
1648 )
1649 .await?;
1650 let mut certificates_with_check_results = vec![];
1651 for cert in certificates {
1652 let check_result =
1653 Self::check_certificate(max_epoch, committees_ref, &cert)?;
1654 certificates_with_check_results
1655 .push((cert, check_result.into_result().is_ok()));
1656 }
1657 Ok(certificates_with_check_results)
1658 },
1659 |errors| {
1660 errors
1661 .into_iter()
1662 .map(|(validator, error)| {
1663 warn!(
1664 %validator,
1665 %sender_chain_id,
1666 %error,
1667 "failed to download certificates from validator",
1668 );
1669 validator
1670 })
1671 .collect::<BTreeSet<_>>()
1672 },
1673 self.options.certificate_batch_download_hedge_delay,
1674 self.storage_client().clock(),
1675 )
1676 .await
1677 {
1678 Ok(certificates_with_check_results) => certificates_with_check_results,
1679 Err(faulty_validators) => {
1680 nodes.retain(|node| !faulty_validators.contains(&node.public_key));
1682 if nodes.is_empty() {
1683 info!(
1684 chain_id = %sender_chain_id,
1685 "could not download certificates for chain - no more correct validators left"
1686 );
1687 return;
1688 }
1689 continue;
1690 }
1691 };
1692
1693 trace!(
1694 num_certificates = %certificates.len(),
1695 "received certificates",
1696 );
1697
1698 let mut to_remove_from_queue = BTreeSet::new();
1699
1700 for (certificate, check_result) in certificates {
1701 let hash = certificate.hash();
1702 let chain_id = certificate.block().header.chain_id;
1703 let height = certificate.block().header.height;
1704 if !check_result {
1705 to_remove_from_queue.insert(height);
1709 continue;
1710 }
1711 let mode = ReceiveCertificateMode::AlreadyChecked;
1713 if let Err(error) = self
1714 .receive_sender_certificate(
1715 self.storage_client().cache_certificate(certificate),
1716 mode,
1717 None,
1718 )
1719 .await
1720 {
1721 warn!(%error, %hash, "Received invalid certificate");
1722 } else {
1723 to_remove_from_queue.insert(height);
1724 if let Err(error) = sender.send(ChainAndHeight { chain_id, height }) {
1725 error!(
1726 %chain_id,
1727 %height,
1728 %error,
1729 "failed to send chain and height over the channel",
1730 );
1731 }
1732 }
1733 }
1734
1735 remote_heights.retain(|height| !to_remove_from_queue.contains(height));
1736 }
1737 trace!("find_received_certificates: finished processing chain");
1738 }
1739
1740 #[instrument(level = "trace", skip(self))]
1742 async fn get_received_log_from_validator(
1743 &self,
1744 chain_id: ChainId,
1745 remote_node: &RemoteNode<Env::ValidatorNode>,
1746 tracker: u64,
1747 ) -> Result<Vec<ChainAndHeight>, chain_client::Error> {
1748 let mut offset = tracker;
1749
1750 let mut remote_log = Vec::new();
1752 loop {
1753 trace!("get_received_log_from_validator: looping");
1754 let query = ChainInfoQuery::new(chain_id).with_received_log_excluding_first_n(offset);
1755 let info = remote_node.handle_chain_info_query(query).await?;
1756 let received_entries = info.requested_received_log.len();
1757 offset += received_entries as u64;
1758 remote_log.extend(info.requested_received_log);
1759 trace!(
1760 remote_node = remote_node.address(),
1761 %received_entries,
1762 "get_received_log_from_validator: received log batch",
1763 );
1764 if received_entries < CHAIN_INFO_MAX_RECEIVED_LOG_ENTRIES {
1765 break;
1766 }
1767 }
1768
1769 trace!(
1770 remote_node = remote_node.address(),
1771 num_entries = remote_log.len(),
1772 "get_received_log_from_validator: returning downloaded log",
1773 );
1774
1775 Ok(remote_log)
1776 }
1777
1778 async fn download_sender_block_with_sending_ancestors(
1784 &self,
1785 receiver_chain_id: ChainId,
1786 sender_chain_id: ChainId,
1787 height: BlockHeight,
1788 remote_node: &RemoteNode<Env::ValidatorNode>,
1789 ) -> Result<(), chain_client::Error> {
1790 let next_outbox_height = self
1791 .local_node
1792 .next_outbox_heights(&[sender_chain_id], receiver_chain_id)
1793 .await?
1794 .get(&sender_chain_id)
1795 .copied()
1796 .unwrap_or(BlockHeight::ZERO);
1797 let (max_epoch, committees) = self.admin_committees().await?;
1798
1799 let mut certificates = BTreeMap::new();
1802 let mut current_height = height;
1803 let mut current_hash: Option<CryptoHash> = None;
1806
1807 while current_height >= next_outbox_height {
1809 let certificate = if let Some(local) = self
1813 .try_read_local_certificate(sender_chain_id, current_height, current_hash)
1814 .await?
1815 {
1816 local
1817 } else {
1818 let downloaded = self
1819 .requests_scheduler
1820 .download_certificates_by_heights(
1821 remote_node,
1822 sender_chain_id,
1823 vec![current_height],
1824 )
1825 .await?;
1826 let Some(certificate) = downloaded.into_iter().next() else {
1827 return Err(chain_client::Error::CannotDownloadMissingSenderBlock {
1828 chain_id: sender_chain_id,
1829 height: current_height,
1830 });
1831 };
1832 self.storage_client().cache_certificate(certificate)
1833 };
1834
1835 Client::<Env>::check_certificate(max_epoch, &committees, &certificate)?
1837 .into_result()?;
1838
1839 let block = certificate.block();
1841 let next = block
1842 .body
1843 .previous_message_blocks
1844 .get(&receiver_chain_id)
1845 .map(|(prev_hash, prev_height)| (*prev_hash, *prev_height));
1846
1847 certificates.insert(current_height, certificate);
1849
1850 if let Some((prev_hash, prev_height)) = next {
1851 current_height = prev_height;
1853 current_hash = Some(prev_hash);
1854 } else {
1855 break;
1857 }
1858 }
1859
1860 if certificates.is_empty() {
1861 self.retry_pending_cross_chain_requests(sender_chain_id)
1862 .await?;
1863 }
1864
1865 for certificate in certificates.into_values() {
1867 self.receive_sender_certificate(
1868 certificate,
1869 ReceiveCertificateMode::AlreadyChecked,
1870 Some(vec![remote_node.clone()]),
1871 )
1872 .await?;
1873 }
1874
1875 Ok(())
1876 }
1877
1878 async fn download_event_bearing_blocks(
1882 &self,
1883 publisher_chain_id: ChainId,
1884 initial_blocks: BTreeSet<(BlockHeight, CryptoHash)>,
1885 local_next_block_height: BlockHeight,
1886 subscribed_streams: &BTreeSet<StreamId>,
1887 remote_node: &RemoteNode<Env::ValidatorNode>,
1888 ) -> Result<(), chain_client::Error> {
1889 if initial_blocks.is_empty() {
1890 return Ok(());
1891 }
1892 let (max_epoch, committees) = self.admin_committees().await?;
1893
1894 let mut certificates = BTreeMap::new();
1895 let mut blocks_to_fetch = initial_blocks;
1896 let next_expected_events = self
1897 .local_node
1898 .next_expected_events(
1899 publisher_chain_id,
1900 subscribed_streams.iter().cloned().collect(),
1901 )
1902 .await?;
1903
1904 while let Some((current_height, current_hash)) = blocks_to_fetch.pop_last() {
1905 if current_height < local_next_block_height {
1906 continue; }
1908 if certificates.contains_key(¤t_height) {
1909 continue;
1910 }
1911
1912 let certificate = if let Some(certificate) =
1913 self.storage_client().read_certificate(current_hash).await?
1914 {
1915 certificate
1916 } else {
1917 let downloaded = self
1918 .requests_scheduler
1919 .download_certificates(remote_node, publisher_chain_id, current_height, 1)
1920 .await?;
1921 let Some(certificate) = downloaded.into_iter().next() else {
1922 tracing::debug!(
1923 validator = remote_node.address(),
1924 %publisher_chain_id,
1925 height = %current_height,
1926 "failed to download event publisher block"
1927 );
1928 continue;
1929 };
1930
1931 Client::<Env>::check_certificate(max_epoch, &committees, &certificate)?
1932 .into_result()?;
1933
1934 self.storage_client().cache_certificate(certificate)
1935 };
1936
1937 let block = certificate.block();
1938 for stream_id in subscribed_streams {
1940 if let Some((prev_hash, prev_height)) =
1941 block.body.previous_event_blocks.get(stream_id)
1942 {
1943 if next_expected_events.get(stream_id).is_some_and(|index| {
1944 block
1945 .body
1946 .events
1947 .iter()
1948 .flatten()
1949 .find(|event| event.stream_id == *stream_id)
1950 .is_some_and(|event| event.index == *index)
1951 }) {
1952 continue;
1953 }
1954 if !certificates.contains_key(prev_height) {
1955 blocks_to_fetch.insert((*prev_height, *prev_hash));
1956 }
1957 }
1958 }
1959
1960 certificates.insert(current_height, certificate);
1961 }
1962
1963 for certificate in certificates.into_values() {
1965 self.receive_sender_certificate(
1966 certificate,
1967 ReceiveCertificateMode::AlreadyChecked,
1968 Some(vec![remote_node.clone()]),
1969 )
1970 .await?;
1971 }
1972
1973 Ok(())
1974 }
1975
1976 async fn sync_events_from_node(
1979 &self,
1980 chain_id: ChainId,
1981 stream_ids: &BTreeSet<StreamId>,
1982 remote_node: &RemoteNode<Env::ValidatorNode>,
1983 ) -> Result<(), chain_client::Error> {
1984 let stream_ids_vec = stream_ids.iter().cloned().collect::<Vec<_>>();
1985 let mut initial_blocks = BTreeSet::new();
1986 for chunk in stream_ids_vec.chunks(self.options.max_event_stream_queries) {
1987 let previous_blocks = remote_node
1988 .node
1989 .previous_event_blocks(chain_id, chunk.to_vec())
1990 .await?;
1991 initial_blocks.extend(previous_blocks.values().copied());
1992 }
1993 let local_height = match self.local_node.chain_info(chain_id).await {
1994 Ok(info) => info.next_block_height,
1995 Err(LocalNodeError::InactiveChain(_) | LocalNodeError::BlobsNotFound(_)) => {
1996 BlockHeight::ZERO
1997 }
1998 Err(error) => return Err(error.into()),
1999 };
2000 self.download_event_bearing_blocks(
2001 chain_id,
2002 initial_blocks,
2003 local_height,
2004 stream_ids,
2005 remote_node,
2006 )
2007 .await
2008 }
2009
2010 #[instrument(
2011 level = "trace", skip_all,
2012 fields(certificate_hash = ?incoming_certificate.hash()),
2013 )]
2014 fn check_certificate(
2015 highest_known_epoch: Epoch,
2016 committees: &BTreeMap<Epoch, Committee>,
2017 incoming_certificate: &ConfirmedBlockCertificate,
2018 ) -> Result<CheckCertificateResult, NodeError> {
2019 let block = incoming_certificate.block();
2020 if block.header.epoch > highest_known_epoch {
2022 return Ok(CheckCertificateResult::FutureEpoch);
2023 }
2024 if let Some(known_committee) = committees.get(&block.header.epoch) {
2025 incoming_certificate.check(known_committee)?;
2028 Ok(CheckCertificateResult::New)
2029 } else {
2030 Ok(CheckCertificateResult::OldEpoch)
2032 }
2033 }
2034
2035 #[instrument(level = "trace", skip_all)]
2039 pub(crate) async fn synchronize_chain_state(
2040 &self,
2041 chain_id: ChainId,
2042 ) -> Result<Box<ChainInfo>, chain_client::Error> {
2043 let (_, committee) = self.admin_committee().await?;
2044 Box::pin(self.synchronize_chain_state_from_committee(chain_id, committee)).await
2045 }
2046
2047 #[instrument(level = "trace", skip_all)]
2052 pub async fn synchronize_chain_state_from_committee(
2053 &self,
2054 chain_id: ChainId,
2055 committee: Arc<Committee>,
2056 ) -> Result<Box<ChainInfo>, chain_client::Error> {
2057 #[cfg(with_metrics)]
2058 let _latency = if !self.is_chain_follow_only(chain_id).await {
2059 Some(metrics::SYNCHRONIZE_CHAIN_STATE_LATENCY.measure_latency())
2060 } else {
2061 None
2062 };
2063
2064 let validators = self.make_nodes(&committee)?;
2065 Box::pin(self.fetch_chain_info(chain_id, &validators)).await?;
2066 communicate_with_quorum(
2067 &validators,
2068 &committee,
2069 |_: &()| (),
2070 |remote_node| async move {
2071 self.synchronize_chain_state_from(&remote_node, chain_id)
2072 .await
2073 },
2074 self.options.quorum_grace_period,
2075 )
2076 .await?;
2077
2078 self.local_node
2079 .chain_info(chain_id)
2080 .await
2081 .map_err(Into::into)
2082 }
2083
2084 #[instrument(level = "trace", skip(self, remote_node, chain_id))]
2090 pub(crate) async fn synchronize_chain_state_from(
2091 &self,
2092 remote_node: &RemoteNode<Env::ValidatorNode>,
2093 chain_id: ChainId,
2094 ) -> Result<(), chain_client::Error> {
2095 let with_manager_values = !self.is_chain_follow_only(chain_id).await;
2096 let query = if with_manager_values {
2097 ChainInfoQuery::new(chain_id).with_manager_values()
2098 } else {
2099 ChainInfoQuery::new(chain_id)
2100 };
2101 let remote_info = remote_node.handle_chain_info_query(query).await?;
2102 let local_info = self
2103 .download_certificates_from(remote_node, chain_id, remote_info.next_block_height, None)
2104 .await?;
2105
2106 if !with_manager_values {
2107 return Ok(());
2108 }
2109
2110 let local_height = local_info.next_block_height;
2112 if local_height != remote_info.next_block_height {
2113 debug!(
2114 remote_node = remote_node.address(),
2115 remote_height = %remote_info.next_block_height,
2116 local_height = %local_height,
2117 "synced from validator, but remote height and local height are different",
2118 );
2119 return Ok(());
2120 };
2121
2122 if let Some(timeout) = remote_info.manager.timeout {
2123 self.handle_certificate(*timeout).await?;
2124 }
2125 let mut proposals = Vec::new();
2126 if let Some(proposal) = remote_info.manager.requested_signed_proposal {
2127 proposals.push(*proposal);
2128 }
2129 if let Some(proposal) = remote_info.manager.requested_proposed {
2130 proposals.push(*proposal);
2131 }
2132 if let Some(locking) = remote_info.manager.requested_locking {
2133 match *locking {
2134 LockingBlock::Fast(proposal) => {
2135 proposals.push(proposal);
2136 }
2137 LockingBlock::Regular(cert) => {
2138 let hash = cert.hash();
2139 if let Err(error) = self.try_process_locking_block_from(remote_node, cert).await
2140 {
2141 debug!(
2142 remote_node = remote_node.address(),
2143 %hash,
2144 height = %local_height,
2145 %error,
2146 "skipping locked block from validator",
2147 );
2148 }
2149 }
2150 }
2151 }
2152 'proposal_loop: for proposal in proposals {
2153 let owner: AccountOwner = proposal.owner();
2154 if let Err(mut err) =
2155 Box::pin(self.local_node.handle_block_proposal(proposal.clone())).await
2156 {
2157 if let LocalNodeError::BlobsNotFound(_) = &err {
2158 let required_blob_ids = proposal.required_blob_ids().collect::<Vec<_>>();
2159 if !required_blob_ids.is_empty() {
2160 let mut blobs = Vec::new();
2161 for blob_id in required_blob_ids {
2162 let blob_content = match self
2163 .requests_scheduler
2164 .download_pending_blob(remote_node, chain_id, blob_id)
2165 .await
2166 {
2167 Ok(content) => content,
2168 Err(error) => {
2169 info!(
2170 remote_node = remote_node.address(),
2171 height = %local_height,
2172 proposer = %owner,
2173 %blob_id,
2174 %error,
2175 "skipping proposal from validator; failed to download blob",
2176 );
2177 continue 'proposal_loop;
2178 }
2179 };
2180 blobs.push(Blob::new(blob_content));
2181 }
2182 self.local_node
2183 .handle_pending_blobs(chain_id, blobs)
2184 .await?;
2185 if let Err(new_err) =
2187 Box::pin(self.local_node.handle_block_proposal(proposal.clone())).await
2188 {
2189 err = new_err;
2190 } else {
2191 continue;
2192 }
2193 }
2194 if let LocalNodeError::BlobsNotFound(blob_ids) = &err {
2195 self.update_local_node_with_blobs_from(
2196 blob_ids.clone(),
2197 slice::from_ref(remote_node),
2198 )
2199 .await?;
2200 if let Err(new_err) =
2202 Box::pin(self.local_node.handle_block_proposal(proposal.clone())).await
2203 {
2204 err = new_err;
2205 } else {
2206 continue;
2207 }
2208 }
2209 }
2210 if let LocalNodeError::EventsNotFound(event_ids) = &err {
2211 if let Err(error) =
2212 Box::pin(self.download_certificates_for_events(event_ids)).await
2213 {
2214 info!(
2215 remote_node = remote_node.address(),
2216 height = %local_height,
2217 proposer = %owner,
2218 %error,
2219 "skipping proposal from validator; failed to download events",
2220 );
2221 continue 'proposal_loop;
2222 }
2223 if let Err(new_err) = self
2225 .local_node
2226 .handle_block_proposal(proposal.clone())
2227 .await
2228 {
2229 err = new_err;
2230 } else {
2231 continue;
2232 }
2233 }
2234 if let LocalNodeError::WorkerError(WorkerError::ChainError(chain_err)) = &err {
2237 if let ChainError::MissingCrossChainUpdates { chain_id, bundles } = &**chain_err
2238 {
2239 let chain_id = *chain_id;
2240 let mut origin_heights: BTreeMap<ChainId, BlockHeight> = BTreeMap::new();
2246 for (origin, height) in bundles {
2247 let entry = origin_heights.entry(*origin).or_insert(*height);
2248 *entry = (*entry).max(*height);
2249 }
2250 stream::iter(origin_heights.into_iter().map(|(origin, height)| {
2251 self.download_sender_block_with_sending_ancestors(
2252 chain_id,
2253 origin,
2254 height,
2255 remote_node,
2256 )
2257 }))
2258 .buffer_unordered(self.options.max_joined_tasks)
2259 .collect::<Vec<_>>()
2260 .await
2261 .into_iter()
2262 .collect::<Result<(), _>>()?;
2263 if let Err(new_err) =
2264 Box::pin(self.local_node.handle_block_proposal(proposal.clone())).await
2265 {
2266 err = new_err;
2267 } else {
2268 continue 'proposal_loop;
2269 }
2270 }
2271 }
2272
2273 debug!(
2274 remote_node = remote_node.address(),
2275 proposer = %owner,
2276 height = %local_height,
2277 error = %err,
2278 "skipping proposal from validator",
2279 );
2280 }
2281 }
2282 Ok(())
2283 }
2284
2285 async fn try_process_locking_block_from(
2286 &self,
2287 remote_node: &RemoteNode<Env::ValidatorNode>,
2288 certificate: GenericCertificate<ValidatedBlock>,
2289 ) -> Result<(), chain_client::Error> {
2290 let chain_id = certificate.inner().chain_id();
2291 let mut downloaded_blobs = HashSet::<BlobId>::new();
2292 let mut events = EventSetDownloader::new(self);
2293 loop {
2294 let result = self.handle_certificate(certificate.clone()).await;
2295 if let Err(LocalNodeError::BlobsNotFound(blob_ids)) = &result {
2296 let new_blobs = filter_new(blob_ids, &downloaded_blobs);
2297 if !new_blobs.is_empty() {
2298 let mut blobs = Vec::new();
2299 for blob_id in &new_blobs {
2300 let blob_content = self
2301 .requests_scheduler
2302 .download_pending_blob(remote_node, chain_id, *blob_id)
2303 .await?;
2304 blobs.push(Blob::new(blob_content));
2305 }
2306 self.local_node
2307 .handle_pending_blobs(chain_id, blobs)
2308 .await?;
2309 downloaded_blobs.extend(new_blobs);
2310 continue;
2311 }
2312 }
2313 if let Err(LocalNodeError::EventsNotFound(event_ids)) = &result {
2314 if events.download_new(event_ids).await? {
2315 continue;
2316 }
2317 }
2318 result?;
2319 return Ok(());
2320 }
2321 }
2322
2323 async fn update_local_node_with_blobs_from(
2326 &self,
2327 blob_ids: Vec<BlobId>,
2328 remote_nodes: &[RemoteNode<Env::ValidatorNode>],
2329 ) -> Result<Vec<CacheArc<Blob>>, chain_client::Error> {
2330 let timeout = self.options.blob_download_hedge_delay;
2331 let blob_ids = blob_ids.into_iter().collect::<BTreeSet<_>>();
2333 stream::iter(blob_ids.into_iter().map(|blob_id| {
2334 communicate_concurrently(
2335 remote_nodes,
2336 async move |remote_node| {
2337 let certificate = self
2338 .requests_scheduler
2339 .download_certificate_for_blob(&remote_node, blob_id)
2340 .await?;
2341 self.receive_sender_certificate(
2342 self.storage_client().cache_certificate(certificate),
2343 ReceiveCertificateMode::NeedsCheck,
2344 Some(vec![remote_node.clone()]),
2345 )
2346 .await?;
2347 let blob = self
2348 .local_node
2349 .storage_client()
2350 .read_blob(blob_id)
2351 .await?
2352 .ok_or_else(|| LocalNodeError::BlobsNotFound(vec![blob_id]))?;
2353 Result::<_, chain_client::Error>::Ok(blob)
2354 },
2355 move |errors| {
2356 for (validator, error) in &errors {
2357 warn!(
2358 %validator,
2359 %blob_id,
2360 %error,
2361 "failed to download certificate-for-blob from validator",
2362 );
2363 }
2364 chain_client::Error::CannotDownloadBlob(blob_id)
2365 },
2366 timeout,
2367 self.storage_client().clock(),
2368 )
2369 }))
2370 .buffer_unordered(self.options.max_joined_tasks)
2371 .collect::<Vec<_>>()
2372 .await
2373 .into_iter()
2374 .collect()
2375 }
2376
2377 #[instrument(level = "trace", skip(self, block))]
2387 async fn stage_block_execution(
2388 &self,
2389 block: ProposedBlock,
2390 round: Option<u32>,
2391 published_blobs: Vec<Blob>,
2392 policy: BundleExecutionPolicy,
2393 ) -> Result<(Block, ChainInfoResponse, HashSet<ChainId>), chain_client::Error> {
2394 let mut events = EventSetDownloader::new(self);
2395 loop {
2396 let result = self
2397 .local_node
2398 .stage_block_execution(
2399 block.clone(),
2400 round,
2401 published_blobs.clone(),
2402 policy.clone(),
2403 )
2404 .await;
2405 if let Err(LocalNodeError::BlobsNotFound(blob_ids)) = &result {
2406 let validators = self.validator_nodes().await?;
2407 self.update_local_node_with_blobs_from(blob_ids.clone(), &validators)
2408 .await?;
2409 continue; }
2411 if let Err(LocalNodeError::EventsNotFound(event_ids)) = &result {
2412 if events.download_new(event_ids).await? {
2413 continue; }
2415 }
2417 if let Ok((_, executed_block, _, _, _)) = &result {
2418 let hash = CryptoHash::new(executed_block);
2419 let notification = Notification {
2420 chain_id: executed_block.header.chain_id,
2421 reason: Reason::BlockExecuted {
2422 height: executed_block.header.height,
2423 hash,
2424 },
2425 };
2426 self.notifier.notify(&[notification]);
2427 }
2428 let (
2429 _modified_block,
2430 executed_block,
2431 response,
2432 _resource_tracker,
2433 never_reject_origins,
2434 ) = result?;
2435 return Ok((executed_block, response, never_reject_origins));
2436 }
2437 }
2438}
2439
2440fn filter_new<T: Clone + Eq + std::hash::Hash>(
2442 ids: &[T],
2443 already_downloaded: &HashSet<T>,
2444) -> Vec<T> {
2445 ids.iter()
2446 .filter(|id| !already_downloaded.contains(*id))
2447 .cloned()
2448 .collect()
2449}
2450
2451pub(crate) struct EventSetDownloader<'a, Env: Environment> {
2456 client: &'a Client<Env>,
2457 downloaded: HashSet<EventId>,
2458}
2459
2460impl<'a, Env: Environment> EventSetDownloader<'a, Env> {
2461 pub(crate) fn new(client: &'a Client<Env>) -> Self {
2462 Self {
2463 client,
2464 downloaded: HashSet::new(),
2465 }
2466 }
2467
2468 pub(crate) async fn download_new(
2474 &mut self,
2475 event_ids: &[EventId],
2476 ) -> Result<bool, chain_client::Error> {
2477 let new_events = filter_new(event_ids, &self.downloaded);
2478 if new_events.is_empty() {
2479 return Ok(false);
2480 }
2481 Box::pin(self.client.download_certificates_for_events(&new_events)).await?;
2482 self.downloaded.extend(new_events);
2483 Ok(true)
2484 }
2485}
2486
2487pub(crate) type ClockOf<Env> = <<Env as Environment>::Storage as linera_storage::Storage>::Clock;
2488
2489pub(crate) async fn hedged_fan_out<Peer, T, Err, NextPeer, NextFut, Op, OpFut>(
2501 first_peer: Peer,
2502 mut next_peer: NextPeer,
2503 operation: Op,
2504 hedge_schedule: impl Fn(usize) -> Duration,
2505 clock: &(impl linera_storage::Clock + Sync),
2508) -> Result<T, Vec<Err>>
2509where
2510 NextPeer: FnMut() -> NextFut,
2511 NextFut: Future<Output = Option<Peer>>,
2512 Op: Fn(Peer) -> OpFut,
2513 OpFut: Future<Output = Result<T, Err>>,
2514{
2515 use futures::future::{select, Either};
2516
2517 let mut in_flight = FuturesUnordered::new();
2518 let mut errors = vec![];
2519 let mut started = 0usize;
2520 let arm = |started: usize| clock.sleep_for(hedge_schedule(started));
2521
2522 in_flight.push(operation(first_peer));
2523 started += 1;
2524 let mut hedge = arm(started);
2525
2526 loop {
2527 if in_flight.is_empty() {
2528 match next_peer().await {
2530 Some(peer) => {
2531 in_flight.push(operation(peer));
2532 started += 1;
2533 hedge = arm(started);
2534 }
2535 None => return Err(errors),
2536 }
2537 continue;
2538 }
2539 match select(in_flight.next(), hedge).await {
2540 Either::Left((Some(Ok(value)), _)) => return Ok(value),
2542 Either::Left((Some(Err(error)), pending_hedge)) => {
2544 errors.push(error);
2545 hedge = pending_hedge;
2546 if let Some(peer) = next_peer().await {
2547 in_flight.push(operation(peer));
2548 started += 1;
2549 hedge = arm(started);
2550 }
2551 }
2552 Either::Left((None, pending_hedge)) => hedge = pending_hedge,
2554 Either::Right(((), _)) => match next_peer().await {
2556 Some(peer) => {
2557 in_flight.push(operation(peer));
2558 started += 1;
2559 hedge = arm(started);
2560 }
2561 None => break,
2562 },
2563 }
2564 }
2565
2566 while let Some(result) = in_flight.next().await {
2568 match result {
2569 Ok(value) => return Ok(value),
2570 Err(error) => errors.push(error),
2571 }
2572 }
2573 Err(errors)
2574}
2575
2576async fn communicate_concurrently<A, E1, E2, F, G, R, V>(
2583 nodes: &[RemoteNode<A>],
2584 f: F,
2585 err: G,
2586 hedge_delay: Duration,
2587 clock: &(impl linera_storage::Clock + Sync),
2588) -> Result<V, E2>
2589where
2590 F: Clone + FnOnce(RemoteNode<A>) -> R,
2591 RemoteNode<A>: Clone,
2592 G: FnOnce(Vec<(ValidatorPublicKey, E1)>) -> E2,
2593 R: Future<Output = Result<V, E1>>,
2594{
2595 let mut nodes = nodes.to_vec();
2596 nodes.shuffle(&mut rand::thread_rng());
2597 let mut nodes = nodes.into_iter();
2598 let Some(first_peer) = nodes.next() else {
2599 return Err(err(vec![]));
2600 };
2601 hedged_fan_out(
2602 first_peer,
2603 move || std::future::ready(nodes.next()),
2604 |node: RemoteNode<A>| {
2605 let fun = f.clone();
2606 async move {
2607 let public_key = node.public_key;
2608 fun(node).await.map_err(|e| (public_key, e))
2609 }
2610 },
2611 |started| {
2612 let k = u32::try_from(started).unwrap_or(u32::MAX);
2613 hedge_delay.saturating_mul(k).saturating_mul(k)
2614 },
2615 clock,
2616 )
2617 .await
2618 .map_err(err)
2619}
2620
2621#[must_use]
2623pub struct AbortOnDrop(pub AbortHandle);
2624
2625impl Drop for AbortOnDrop {
2626 #[instrument(level = "trace", skip(self))]
2627 fn drop(&mut self) {
2628 self.0.abort();
2629 }
2630}
2631
2632#[derive(Clone, Serialize, Deserialize)]
2634pub struct PendingProposal {
2635 pub block: ProposedBlock,
2637 pub blobs: Vec<Blob>,
2639 #[serde(default)]
2641 pub round: Option<Round>,
2642}
2643
2644struct LagReport<N> {
2647 remote_node: RemoteNode<N>,
2648 chain_id: ChainId,
2649 error: NodeError,
2650}
2651
2652impl<N> LagReport<N> {
2653 fn remote_progress(&self) -> (Option<BlockHeight>, Option<Round>) {
2656 match &self.error {
2657 NodeError::UnexpectedBlockHeight {
2658 expected_block_height,
2659 ..
2660 } => (Some(*expected_block_height), None),
2661 NodeError::WrongRound(round) => (None, Some(*round)),
2662 _ => (None, None),
2663 }
2664 }
2665}
2666
2667enum ReceiveCertificateMode {
2668 NeedsCheck,
2669 AlreadyChecked,
2670}
2671
2672enum CheckCertificateResult {
2673 OldEpoch,
2674 New,
2675 FutureEpoch,
2676}
2677
2678impl CheckCertificateResult {
2679 fn into_result(self) -> Result<(), chain_client::Error> {
2680 match self {
2681 Self::OldEpoch => Err(chain_client::Error::CommitteeDeprecationError),
2682 Self::New => Ok(()),
2683 Self::FutureEpoch => Err(chain_client::Error::CommitteeSynchronizationError),
2684 }
2685 }
2686}
2687
2688#[cfg(not(target_arch = "wasm32"))]
2690pub async fn create_bytecode_blobs(
2691 contract: Bytecode,
2692 service: Bytecode,
2693 vm_runtime: VmRuntime,
2694) -> (Vec<Blob>, ModuleId) {
2695 match vm_runtime {
2696 VmRuntime::Wasm => {
2697 let (compressed_contract, compressed_service) =
2698 tokio::task::spawn_blocking(move || (contract.compress(), service.compress()))
2699 .await
2700 .expect("Compression should not panic");
2701 let contract_blob = Blob::new_contract_bytecode(compressed_contract);
2702 let service_blob = Blob::new_service_bytecode(compressed_service);
2703 let module_id =
2704 ModuleId::new(contract_blob.id().hash, service_blob.id().hash, vm_runtime);
2705 (vec![contract_blob, service_blob], module_id)
2706 }
2707 VmRuntime::Evm => {
2708 let compressed_contract = contract.compress();
2709 let evm_contract_blob = Blob::new_evm_bytecode(compressed_contract);
2710 let module_id = ModuleId::new(
2711 evm_contract_blob.id().hash,
2712 evm_contract_blob.id().hash,
2713 vm_runtime,
2714 );
2715 (vec![evm_contract_blob], module_id)
2716 }
2717 }
2718}
2719
2720#[cfg(test)]
2721mod communicate_concurrently_tests {
2722 use std::sync::{
2723 atomic::{AtomicUsize, Ordering},
2724 Arc,
2725 };
2726
2727 use linera_base::crypto::ValidatorKeypair;
2728 use linera_storage::TestClock;
2729
2730 use super::*;
2731
2732 fn test_node() -> RemoteNode<()> {
2733 RemoteNode {
2734 public_key: ValidatorKeypair::generate().public_key,
2735 node: (),
2736 }
2737 }
2738
2739 #[tokio::test]
2742 async fn does_not_wait_after_failures() {
2743 let clock = TestClock::new();
2744 let nodes: Vec<_> = (0..5).map(|_| test_node()).collect();
2745 let calls = Arc::new(AtomicUsize::new(0));
2746 let result: Result<(), Vec<(ValidatorPublicKey, &str)>> = communicate_concurrently(
2747 &nodes,
2748 {
2749 let calls = calls.clone();
2750 move |_node| {
2751 let calls = calls.clone();
2752 async move {
2753 calls.fetch_add(1, Ordering::SeqCst);
2754 Err("unavailable")
2755 }
2756 }
2757 },
2758 |errors| errors,
2759 Duration::from_secs(30),
2760 &clock,
2761 )
2762 .await;
2763 assert_eq!(result.unwrap_err().len(), 5);
2764 assert_eq!(calls.load(Ordering::SeqCst), 5);
2765 assert_eq!(clock.current_time(), Timestamp::from(0));
2767 }
2768
2769 #[tokio::test]
2772 async fn fails_over_to_a_working_node() {
2773 let clock = TestClock::new();
2774 let nodes: Vec<_> = (0..5).map(|_| test_node()).collect();
2775 let working = nodes[3].public_key;
2776 let result: Result<u32, Vec<(ValidatorPublicKey, &str)>> = communicate_concurrently(
2777 &nodes,
2778 move |node| async move {
2779 if node.public_key == working {
2780 Ok(42)
2781 } else {
2782 Err("unavailable")
2783 }
2784 },
2785 |errors| errors,
2786 Duration::from_secs(30),
2787 &clock,
2788 )
2789 .await;
2790 assert_eq!(result.unwrap(), 42);
2791 assert_eq!(clock.current_time(), Timestamp::from(0));
2792 }
2793
2794 fn peer_source(n: usize) -> impl FnMut() -> std::future::Ready<Option<usize>> {
2801 let mut next = 1usize;
2802 move || {
2803 let peer = (next < n).then_some(next);
2804 next += 1;
2805 std::future::ready(peer)
2806 }
2807 }
2808
2809 #[tokio::test]
2812 async fn slow_first_peer_is_hedged_but_not_cancelled() {
2813 let clock = TestClock::new();
2814 let delay = Duration::from_secs(1);
2815 let order = Arc::new(std::sync::Mutex::new(Vec::new()));
2816 let (started_tx, mut started_rx) = tokio::sync::mpsc::unbounded_channel();
2817 let release0 = Arc::new(tokio::sync::Notify::new());
2818 let gates = [release0.clone(), Arc::new(tokio::sync::Notify::new())];
2819
2820 let operation = {
2821 let order = order.clone();
2822 move |peer: usize| {
2823 let started_tx = started_tx.clone();
2824 let order = order.clone();
2825 let gate = gates[peer].clone();
2826 async move {
2827 order.lock().unwrap().push(peer);
2828 started_tx.send(peer).unwrap();
2829 gate.notified().await;
2830 if peer == 0 {
2831 Ok::<u32, &str>(42)
2832 } else {
2833 Err("slow loser")
2834 }
2835 }
2836 }
2837 };
2838
2839 let fan = tokio::spawn({
2840 let clock = clock.clone();
2841 async move {
2842 hedged_fan_out(
2843 0usize,
2844 peer_source(2),
2845 operation,
2846 move |k| delay * u32::try_from(k).unwrap_or(u32::MAX),
2847 &clock,
2848 )
2849 .await
2850 }
2851 });
2852
2853 assert_eq!(started_rx.recv().await, Some(0));
2855 clock.add(TimeDelta::from_duration(delay));
2857 assert_eq!(started_rx.recv().await, Some(1));
2858 release0.notify_one();
2860 assert_eq!(fan.await.unwrap(), Ok(42));
2861 assert_eq!(*order.lock().unwrap(), vec![0, 1]);
2862 }
2863
2864 #[tokio::test]
2868 async fn hedge_schedule_determines_start_times() {
2869 let clock = TestClock::new();
2870 let unit = Duration::from_secs(1);
2871 let starts = Arc::new(std::sync::Mutex::new(Vec::new()));
2872 let (started_tx, mut started_rx) = tokio::sync::mpsc::unbounded_channel();
2873
2874 let operation = {
2875 let starts = starts.clone();
2876 let clock = clock.clone();
2877 move |peer: usize| {
2878 let started_tx = started_tx.clone();
2879 let starts = starts.clone();
2880 let clock = clock.clone();
2881 async move {
2882 starts.lock().unwrap().push((peer, clock.current_time()));
2883 started_tx.send(peer).unwrap();
2884 std::future::pending::<()>().await;
2885 Ok::<u32, &str>(0)
2886 }
2887 }
2888 };
2889
2890 let fan = tokio::spawn({
2891 let clock = clock.clone();
2892 async move {
2893 hedged_fan_out(
2894 0usize,
2895 peer_source(4),
2896 operation,
2897 move |k| {
2898 let k = u32::try_from(k).unwrap_or(u32::MAX);
2899 unit * k * k
2900 },
2901 &clock,
2902 )
2903 .await
2904 }
2905 });
2906
2907 assert_eq!(started_rx.recv().await, Some(0));
2909 clock.add(TimeDelta::from_duration(unit));
2910 assert_eq!(started_rx.recv().await, Some(1));
2911 clock.add(TimeDelta::from_duration(unit * 4));
2912 assert_eq!(started_rx.recv().await, Some(2));
2913 clock.add(TimeDelta::from_duration(unit * 9));
2914 assert_eq!(started_rx.recv().await, Some(3));
2915
2916 assert_eq!(
2918 *starts.lock().unwrap(),
2919 vec![
2920 (0, Timestamp::from(0)),
2921 (1, Timestamp::from(1_000_000)),
2922 (2, Timestamp::from(5_000_000)),
2923 (3, Timestamp::from(14_000_000)),
2924 ]
2925 );
2926 fan.abort();
2927 }
2928
2929 #[tokio::test]
2932 async fn frozen_clock_disables_the_hedge() {
2933 let clock = TestClock::new();
2934 let (started_tx, mut started_rx) = tokio::sync::mpsc::unbounded_channel();
2935 let release0 = Arc::new(tokio::sync::Notify::new());
2936
2937 let operation = {
2938 let release0 = release0.clone();
2939 move |peer: usize| {
2940 let started_tx = started_tx.clone();
2941 let release0 = release0.clone();
2942 async move {
2943 started_tx.send(peer).unwrap();
2944 if peer == 0 {
2945 release0.notified().await;
2946 Ok::<u32, &str>(7)
2947 } else {
2948 Ok(99)
2950 }
2951 }
2952 }
2953 };
2954
2955 let fan = tokio::spawn({
2956 let clock = clock.clone();
2957 async move {
2958 hedged_fan_out(
2959 0usize,
2960 peer_source(2),
2961 operation,
2962 move |k| Duration::from_secs(1) * u32::try_from(k).unwrap_or(u32::MAX),
2963 &clock,
2964 )
2965 .await
2966 }
2967 });
2968
2969 assert_eq!(started_rx.recv().await, Some(0));
2970 tokio::task::yield_now().await;
2972 assert!(
2973 started_rx.try_recv().is_err(),
2974 "the hedge must not fire while the clock is frozen"
2975 );
2976 release0.notify_one();
2978 assert_eq!(fan.await.unwrap(), Ok(7));
2979 assert_eq!(clock.current_time(), Timestamp::from(0));
2980 }
2981
2982 #[tokio::test]
2985 async fn failure_fails_over_without_waiting_out_the_hedge() {
2986 let clock = TestClock::new();
2987 let (started_tx, mut started_rx) = tokio::sync::mpsc::unbounded_channel();
2988 let release0 = Arc::new(tokio::sync::Notify::new());
2989
2990 let operation = {
2991 let release0 = release0.clone();
2992 move |peer: usize| {
2993 let started_tx = started_tx.clone();
2994 let release0 = release0.clone();
2995 async move {
2996 started_tx.send(peer).unwrap();
2997 match peer {
2998 0 => {
2999 release0.notified().await;
3000 Err::<u32, &str>("dead")
3001 }
3002 1 => Err("dead"),
3003 _ => Ok(55),
3004 }
3005 }
3006 }
3007 };
3008
3009 let fan = tokio::spawn({
3010 let clock = clock.clone();
3011 async move {
3012 hedged_fan_out(
3013 0usize,
3014 peer_source(3),
3015 operation,
3016 move |k| Duration::from_secs(100) * u32::try_from(k).unwrap_or(u32::MAX),
3018 &clock,
3019 )
3020 .await
3021 }
3022 });
3023
3024 assert_eq!(started_rx.recv().await, Some(0));
3025 release0.notify_one();
3027 assert_eq!(started_rx.recv().await, Some(1));
3028 assert_eq!(started_rx.recv().await, Some(2));
3029 assert_eq!(fan.await.unwrap(), Ok(55));
3030 assert_eq!(clock.current_time(), Timestamp::from(0));
3032 }
3033}