1use std::{
6 cmp::Ordering,
7 collections::{BTreeMap, BTreeSet, HashSet},
8 slice,
9 sync::{Arc, 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, ValidatorUpdater},
61 worker::{Notification, ProcessableCertificate, Reason, WorkerError, WorkerState},
62 ChainWorkerConfig, ProcessConfirmedBlockMode, CHAIN_INFO_MAX_RECEIVED_LOG_ENTRIES,
63};
64
65pub mod chain_client;
67pub use chain_client::ChainClient;
68
69pub use crate::data_types::ClientOutcome;
70
71#[cfg(test)]
72#[path = "../unit_tests/client_tests.rs"]
73mod client_tests;
74pub mod requests_scheduler;
75
76pub use requests_scheduler::{RequestsScheduler, RequestsSchedulerConfig, ScoringWeights};
77mod received_log;
78mod validator_trackers;
79
80#[cfg(with_metrics)]
81mod metrics {
82 use std::sync::LazyLock;
83
84 use linera_base::prometheus_util::{
85 exponential_bucket_latencies, register_histogram_vec, register_int_counter_vec,
86 };
87 use prometheus::{HistogramVec, IntCounterVec};
88
89 pub static PROCESS_INBOX_WITHOUT_PREPARE_LATENCY: LazyLock<HistogramVec> =
90 LazyLock::new(|| {
91 register_histogram_vec(
92 "process_inbox_latency",
93 "process_inbox latency",
94 &[],
95 exponential_bucket_latencies(10_000.0),
96 )
97 });
98
99 pub static PREPARE_CHAIN_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
100 register_histogram_vec(
101 "prepare_chain_latency",
102 "prepare_chain latency",
103 &[],
104 exponential_bucket_latencies(10_000.0),
105 )
106 });
107
108 pub static SYNCHRONIZE_CHAIN_STATE_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
109 register_histogram_vec(
110 "synchronize_chain_state_latency",
111 "synchronize_chain_state latency",
112 &[],
113 exponential_bucket_latencies(10_000.0),
114 )
115 });
116
117 pub static EXECUTE_BLOCK_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
118 register_histogram_vec(
119 "execute_block_latency",
120 "execute_block latency",
121 &[],
122 exponential_bucket_latencies(10_000.0),
123 )
124 });
125
126 pub static FIND_RECEIVED_CERTIFICATES_LATENCY: LazyLock<HistogramVec> = LazyLock::new(|| {
127 register_histogram_vec(
128 "find_received_certificates_latency",
129 "find_received_certificates latency",
130 &[],
131 exponential_bucket_latencies(10_000.0),
132 )
133 });
134
135 pub static BLOCK_STAGING_FAILURES_TOTAL: LazyLock<IntCounterVec> = LazyLock::new(|| {
136 register_int_counter_vec(
137 "block_staging_failures_total",
138 "Total number of client block staging (execute_block) failures, labelled by error type",
139 &["error_type"],
140 )
141 });
142}
143
144pub static DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE: u64 = 500;
146pub static DEFAULT_CERTIFICATE_UPLOAD_BATCH_SIZE: usize = 500;
148pub static DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE: usize = 20_000;
150pub static DEFAULT_MAX_EVENT_STREAM_QUERIES: usize = 1000;
152pub static DEFAULT_MAX_CONCURRENT_BATCH_DOWNLOADS: usize = 1;
154
155#[derive(Debug, Clone, Copy)]
157#[allow(missing_docs)]
158pub enum TimingType {
159 ExecuteOperations,
160 ExecuteBlock,
161 SubmitBlockProposal,
162 UpdateValidators,
163}
164
165#[derive(Debug, Clone, PartialEq, Eq)]
170pub enum ListeningMode {
171 FullChain,
174 FollowChain,
178 EventsOnly(BTreeSet<StreamId>),
180}
181
182impl PartialOrd for ListeningMode {
183 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
184 match (self, other) {
185 (ListeningMode::FullChain, ListeningMode::FullChain) => Some(Ordering::Equal),
186 (ListeningMode::FullChain, _) => Some(Ordering::Greater),
187 (_, ListeningMode::FullChain) => Some(Ordering::Less),
188 (ListeningMode::FollowChain, ListeningMode::FollowChain) => Some(Ordering::Equal),
189 (ListeningMode::FollowChain, ListeningMode::EventsOnly(_)) => Some(Ordering::Greater),
190 (ListeningMode::EventsOnly(_), ListeningMode::FollowChain) => Some(Ordering::Less),
191 (ListeningMode::EventsOnly(a), ListeningMode::EventsOnly(b)) => {
192 if a == b {
193 Some(Ordering::Equal)
194 } else if a.is_superset(b) {
195 Some(Ordering::Greater)
196 } else if b.is_superset(a) {
197 Some(Ordering::Less)
198 } else {
199 None
200 }
201 }
202 }
203 }
204}
205
206impl ListeningMode {
207 pub fn is_relevant(&self, reason: &Reason) -> bool {
210 match (reason, self) {
211 (Reason::NewEvents { .. }, ListeningMode::FollowChain | ListeningMode::FullChain) => {
214 false
215 }
216 (_, ListeningMode::FullChain) => true,
218 (Reason::NewBlock { .. }, ListeningMode::FollowChain) => true,
221 (_, ListeningMode::FollowChain) => false,
222 (Reason::NewEvents { event_streams, .. }, ListeningMode::EventsOnly(relevant)) => {
224 relevant.intersection(event_streams).next().is_some()
225 }
226 (_, ListeningMode::EventsOnly(_)) => false,
227 }
228 }
229
230 pub fn extend(&mut self, other: Option<ListeningMode>) {
232 match (self, other) {
233 (_, None) => (),
234 (ListeningMode::FullChain, _) => (),
235 (mode, Some(ListeningMode::FullChain)) => {
236 *mode = ListeningMode::FullChain;
237 }
238 (ListeningMode::FollowChain, _) => (),
239 (mode, Some(ListeningMode::FollowChain)) => {
240 *mode = ListeningMode::FollowChain;
241 }
242 (
243 ListeningMode::EventsOnly(self_events),
244 Some(ListeningMode::EventsOnly(other_events)),
245 ) => {
246 self_events.extend(other_events);
247 }
248 }
249 }
250
251 pub fn is_follow_only(&self) -> bool {
254 !matches!(self, ListeningMode::FullChain)
255 }
256
257 pub fn is_full(&self) -> bool {
260 matches!(self, ListeningMode::FullChain)
261 }
262
263 pub fn should_sync_chain_state(&self) -> bool {
265 match self {
266 ListeningMode::FullChain | ListeningMode::FollowChain => true,
267 ListeningMode::EventsOnly(_) => false,
268 }
269 }
270}
271
272#[derive(Debug)]
282pub struct ChainModes {
283 modes: BTreeMap<ChainId, ListeningMode>,
284 full: Arc<Hashed<ChainIdSet>>,
285}
286
287impl Default for ChainModes {
288 fn default() -> Self {
289 Self::new(BTreeMap::new())
290 }
291}
292
293impl ChainModes {
294 pub fn new(modes: BTreeMap<ChainId, ListeningMode>) -> Self {
296 let full = Self::compute_full(&modes);
297 Self { modes, full }
298 }
299
300 fn compute_full(modes: &BTreeMap<ChainId, ListeningMode>) -> Arc<Hashed<ChainIdSet>> {
301 Arc::new(Hashed::new(ChainIdSet(
302 modes
303 .iter()
304 .filter(|(_, mode)| mode.is_full())
305 .map(|(id, _)| *id)
306 .collect(),
307 )))
308 }
309
310 pub fn full(&self) -> Arc<Hashed<ChainIdSet>> {
312 self.full.clone()
313 }
314
315 pub fn get(&self, chain_id: &ChainId) -> Option<&ListeningMode> {
317 self.modes.get(chain_id)
318 }
319
320 pub fn extend_mode(&mut self, chain_id: ChainId, mode: ListeningMode) -> ListeningMode {
324 let entry = self
325 .modes
326 .entry(chain_id)
327 .or_insert_with(|| ListeningMode::EventsOnly(BTreeSet::new()));
328 let was_full = entry.is_full();
329 entry.extend(Some(mode));
330 let result = entry.clone();
331 if !was_full && result.is_full() {
332 self.full = Self::compute_full(&self.modes);
333 }
334 result
335 }
336
337 pub fn remove_mode(&mut self, chain_id: &ChainId) -> Option<ListeningMode> {
340 let removed = self.modes.remove(chain_id)?;
341 if removed.is_full() {
342 self.full = Self::compute_full(&self.modes);
343 }
344 Some(removed)
345 }
346}
347
348pub struct Client<Env: Environment> {
350 environment: Env,
351 pub local_node: LocalNodeClient<Env::Storage>,
354 requests_scheduler: Arc<RequestsScheduler<Env>>,
356 admin_chain_id: ChainId,
358 chain_modes: Arc<RwLock<ChainModes>>,
361 notifier: Arc<ChannelNotifier<Notification>>,
363 chains: papaya::HashMap<ChainId, chain_client::State>,
365 options: chain_client::Options,
367}
368
369#[cfg(not(web))]
373type ReceiveSenderCertificateFuture<'a> =
374 std::pin::Pin<Box<dyn Future<Output = Result<(), chain_client::Error>> + Send + 'a>>;
375#[cfg(web)]
376type ReceiveSenderCertificateFuture<'a> =
377 std::pin::Pin<Box<dyn Future<Output = Result<(), chain_client::Error>> + 'a>>;
378
379impl<Env: Environment> Client<Env> {
380 #[instrument(level = "trace", skip_all)]
382 #[expect(clippy::too_many_arguments)]
383 pub fn new(
384 environment: Env,
385 admin_chain_id: ChainId,
386 long_lived_services: bool,
387 chain_modes: impl IntoIterator<Item = (ChainId, ListeningMode)>,
388 name: impl Into<String>,
389 chain_worker_ttl: Option<Duration>,
390 sender_chain_worker_ttl: Option<Duration>,
391 cross_chain_batch_size_limit: usize,
392 options: chain_client::Options,
393 block_cache_size: usize,
394 execution_state_cache_size: usize,
395 requests_scheduler_config: &requests_scheduler::RequestsSchedulerConfig,
396 ) -> Self {
397 let mut modes = chain_modes.into_iter().collect::<BTreeMap<_, _>>();
398 modes
402 .entry(admin_chain_id)
403 .or_insert(ListeningMode::FullChain)
404 .extend(Some(ListeningMode::FullChain));
405 let chain_modes = Arc::new(RwLock::new(ChainModes::new(modes)));
406 let config = ChainWorkerConfig {
407 nickname: name.into(),
408 long_lived_services,
409 allow_inactive_chains: true,
410 ttl: chain_worker_ttl,
411 sender_chain_ttl: sender_chain_worker_ttl,
412 block_cache_size,
413 execution_state_cache_size,
414 cross_chain_batch_size_limit,
415 ..ChainWorkerConfig::default()
416 };
417 let state = WorkerState::new(
418 environment.storage().clone(),
419 config,
420 Some(chain_modes.clone()),
421 );
422 let clock = environment.storage().clock().clone();
423 let local_node = LocalNodeClient::new(state);
424 let requests_scheduler = Arc::new(RequestsScheduler::new(
425 vec![],
426 requests_scheduler_config,
427 clock,
428 ));
429
430 Self {
431 environment,
432 local_node,
433 requests_scheduler,
434 chains: papaya::HashMap::new(),
435 admin_chain_id,
436 chain_modes,
437 notifier: Arc::new(ChannelNotifier::default()),
438 options,
439 }
440 }
441
442 pub fn admin_chain_id(&self) -> ChainId {
444 self.admin_chain_id
445 }
446
447 pub fn subscribe(
449 &self,
450 chain_ids: Vec<ChainId>,
451 ) -> tokio::sync::mpsc::UnboundedReceiver<Notification> {
452 self.notifier.subscribe(chain_ids)
453 }
454
455 pub fn subscribe_extra(
457 &self,
458 chain_ids: Vec<ChainId>,
459 sender: &tokio::sync::mpsc::UnboundedSender<Notification>,
460 ) {
461 self.notifier.add_sender(chain_ids, sender);
462 }
463
464 pub fn storage_client(&self) -> &Env::Storage {
466 self.environment.storage()
467 }
468
469 async fn try_read_local_certificate(
472 &self,
473 chain_id: ChainId,
474 height: BlockHeight,
475 hash: Option<CryptoHash>,
476 ) -> Result<Option<CacheArc<ConfirmedBlockCertificate>>, chain_client::Error> {
477 if let Some(hash) = hash {
478 return Ok(self.storage_client().read_certificate(hash).await?);
479 }
480 let results = self
481 .storage_client()
482 .read_certificates_by_heights(chain_id, &[height])
483 .await?;
484 Ok(results.into_iter().next().flatten())
485 }
486
487 pub fn validator_node_provider(&self) -> &Env::Network {
489 self.environment.network()
490 }
491
492 pub(crate) fn options(&self) -> &chain_client::Options {
493 &self.options
494 }
495
496 pub async fn retry_pending_cross_chain_requests(
498 &self,
499 sender_chain: ChainId,
500 ) -> Result<(), LocalNodeError> {
501 self.local_node
502 .retry_pending_cross_chain_requests(sender_chain, &self.notifier)
503 .await
504 }
505
506 #[instrument(level = "trace", skip(self))]
508 pub fn signer(&self) -> &Env::Signer {
509 self.environment.signer()
510 }
511
512 pub async fn has_key_for(&self, owner: &AccountOwner) -> Result<bool, chain_client::Error> {
514 self.signer()
515 .contains_key(owner)
516 .await
517 .map_err(chain_client::Error::signer_failure)
518 }
519
520 pub fn wallet(&self) -> &Env::Wallet {
522 self.environment.wallet()
523 }
524
525 #[instrument(level = "trace", skip(self))]
528 pub fn extend_chain_mode(&self, chain_id: ChainId, mode: ListeningMode) -> ListeningMode {
529 self.chain_modes
530 .write()
531 .expect("Panics should not happen while holding a lock to `chain_modes`")
532 .extend_mode(chain_id, mode)
533 }
534
535 #[instrument(level = "trace", skip(self))]
537 pub fn remove_chain_mode(&self, chain_id: ChainId) -> Option<ListeningMode> {
538 self.chain_modes
539 .write()
540 .expect("Panics should not happen while holding a lock to `chain_modes`")
541 .remove_mode(&chain_id)
542 }
543
544 pub fn chain_mode(&self, chain_id: ChainId) -> Option<ListeningMode> {
546 self.chain_modes
547 .read()
548 .expect("Panics should not happen while holding a lock to `chain_modes`")
549 .get(&chain_id)
550 .cloned()
551 }
552
553 pub fn is_tracked(&self, chain_id: ChainId) -> bool {
555 self.chain_modes
556 .read()
557 .expect("Panics should not happen while holding a lock to `chain_modes`")
558 .get(&chain_id)
559 .is_some_and(ListeningMode::is_full)
560 }
561
562 #[expect(clippy::too_many_arguments)]
564 #[instrument(level = "trace", skip_all, fields(chain_id, next_block_height))]
565 pub fn create_chain_client(
566 self: &Arc<Self>,
567 chain_id: ChainId,
568 block_hash: Option<CryptoHash>,
569 next_block_height: BlockHeight,
570 pending_proposal: &Option<PendingProposal>,
571 preferred_owner: Option<AccountOwner>,
572 timing_sender: Option<mpsc::UnboundedSender<(u64, TimingType)>>,
573 follow_only: bool,
574 ) -> ChainClient<Env> {
575 self.chains.pin().get_or_insert_with(chain_id, || {
578 chain_client::State::new(pending_proposal.clone(), follow_only)
579 });
580
581 ChainClient::new(
582 self.clone(),
583 chain_id,
584 self.options.clone(),
585 block_hash,
586 next_block_height,
587 preferred_owner,
588 timing_sender,
589 )
590 }
591
592 fn is_chain_follow_only(&self, chain_id: ChainId) -> bool {
594 self.chains
595 .pin()
596 .get(&chain_id)
597 .is_some_and(|state| state.is_follow_only())
598 }
599
600 pub fn set_chain_follow_only(&self, chain_id: ChainId, follow_only: bool) {
602 self.chains
603 .pin()
604 .update(chain_id, |state| state.with_follow_only(follow_only));
605 }
606
607 async fn fetch_chain_info(
609 &self,
610 chain_id: ChainId,
611 validators: &[RemoteNode<Env::ValidatorNode>],
612 ) -> Result<Box<ChainInfo>, chain_client::Error> {
613 match self.local_node.chain_info(chain_id).await {
614 Ok(info) => Ok(info),
615 Err(LocalNodeError::BlobsNotFound(blob_ids)) => {
616 self.synchronize_chain_state(self.admin_chain_id).await?;
619 self.update_local_node_with_blobs_from(blob_ids, validators)
620 .await?;
621 Ok(self.local_node.chain_info(chain_id).await?)
622 }
623 Err(err) => Err(err.into()),
624 }
625 }
626
627 #[instrument(level = "trace", skip(self))]
629 async fn download_certificates(
630 &self,
631 chain_id: ChainId,
632 target_next_block_height: BlockHeight,
633 ) -> Result<Box<ChainInfo>, chain_client::Error> {
634 let validators = self.validator_nodes().await?;
635 let mut info = Box::pin(self.fetch_chain_info(chain_id, &validators)).await?;
636 if target_next_block_height <= info.next_block_height {
637 return Ok(info);
638 }
639 info = self
640 .load_local_certificates(chain_id, target_next_block_height, None)
641 .await?;
642 let mut next_height = info.next_block_height;
643 while next_height < target_next_block_height {
645 let limit = u64::from(target_next_block_height)
646 .checked_sub(u64::from(next_height))
647 .ok_or(ArithmeticError::Overflow)?
648 .min(self.options.certificate_download_batch_size);
649 let certificates = self
650 .requests_scheduler
651 .download_certificates_from_validators(
652 &validators,
653 chain_id,
654 next_height,
655 limit,
656 self.options.certificate_batch_download_hedge_delay,
657 )
658 .await?;
659 let Some(new_info) = self
660 .process_certificates(
661 &validators,
662 certificates,
663 None,
664 ProcessConfirmedBlockMode::Execute,
665 )
666 .await?
667 else {
668 break;
669 };
670 assert!(new_info.next_block_height > next_height);
671 next_height = new_info.next_block_height;
672 info = new_info;
673 }
674 ensure!(
675 target_next_block_height <= info.next_block_height,
676 chain_client::Error::CannotDownloadCertificates {
677 chain_id,
678 target_next_block_height,
679 }
680 );
681 Ok(info)
682 }
683
684 async fn load_local_certificates(
689 &self,
690 chain_id: ChainId,
691 end: BlockHeight,
692 until_block_time: Option<Timestamp>,
693 ) -> Result<Box<ChainInfo>, chain_client::Error> {
694 let mut last_info = self.local_node.chain_info(chain_id).await?;
695 let next_height = last_info.next_block_height;
696 let hashes = self
697 .local_node
698 .get_preprocessed_block_hashes(chain_id, next_height, end)
699 .await?;
700 let certificates = self.storage_client().read_certificates(&hashes).await?;
701 let certificates = match ResultReadCertificates::new(certificates, hashes) {
702 ResultReadCertificates::Certificates(certificates) => certificates,
703 ResultReadCertificates::InvalidHashes(hashes) => {
704 return Err(chain_client::Error::ReadCertificatesError(hashes))
705 }
706 };
707 for certificate in certificates {
708 if let Some(until) = until_block_time {
709 if certificate.value().block().header.timestamp >= until {
710 break;
711 }
712 }
713 last_info = self
714 .handle_certificate::<ConfirmedBlock>(certificate)
715 .await?
716 .info;
717 }
718 Ok(last_info)
719 }
720
721 #[instrument(level = "trace", skip_all)]
727 async fn download_certificates_from(
728 &self,
729 remote_node: &RemoteNode<Env::ValidatorNode>,
730 chain_id: ChainId,
731 stop: BlockHeight,
732 until_block_time: Option<Timestamp>,
733 ) -> Result<Box<ChainInfo>, chain_client::Error> {
734 let mut last_info = self
735 .load_local_certificates(chain_id, stop, until_block_time)
736 .await?;
737 let mut next_height = last_info.next_block_height;
738
739 if next_height >= stop {
740 return Ok(last_info);
741 }
742
743 #[cfg(not(web))]
747 type CertificateBatchFuture = std::pin::Pin<
748 Box<dyn Future<Output = Result<Vec<ConfirmedBlockCertificate>, NodeError>> + Send>,
749 >;
750 #[cfg(web)]
751 type CertificateBatchFuture = std::pin::Pin<
752 Box<dyn Future<Output = Result<Vec<ConfirmedBlockCertificate>, NodeError>>>,
753 >;
754
755 let max_concurrent = self.options.max_concurrent_batch_downloads;
756 let batch_size = self.options.certificate_download_batch_size;
757 let (sender, mut receiver) = tokio::sync::mpsc::channel(max_concurrent);
758 let scheduler = self.requests_scheduler.clone();
759 let remote = remote_node.clone();
760
761 let download_task = linera_base::Task::spawn(async move {
762 let mut download_height = next_height;
763 let mut in_flight = FuturesOrdered::<CertificateBatchFuture>::new();
764
765 let try_enqueue = |in_flight: &mut FuturesOrdered<CertificateBatchFuture>,
766 download_height: &mut BlockHeight| {
767 if *download_height >= stop {
768 return;
769 }
770 let limit = u64::from(stop)
771 .saturating_sub(u64::from(*download_height))
772 .min(batch_size);
773 let height = *download_height;
774 let scheduler = scheduler.clone();
775 let remote = remote.clone();
776 in_flight.push_back(Box::pin(async move {
777 scheduler
778 .download_certificates(&remote, chain_id, height, limit)
779 .await
780 }));
781 *download_height = BlockHeight(u64::from(*download_height) + limit);
782 };
783
784 while in_flight.len() < max_concurrent && download_height < stop {
785 try_enqueue(&mut in_flight, &mut download_height);
786 }
787
788 while let Some(result) = in_flight.next().await {
789 if sender.send(result).await.is_err() {
790 break;
791 }
792 try_enqueue(&mut in_flight, &mut download_height);
793 }
794 });
795
796 while let Some(result) = receiver.recv().await {
798 let certificates = result?;
799 let Some(info) = self
800 .process_certificates(
801 slice::from_ref(remote_node),
802 certificates,
803 until_block_time,
804 ProcessConfirmedBlockMode::Execute,
805 )
806 .await?
807 else {
808 break;
809 };
810 assert!(info.next_block_height >= next_height);
811 next_height = info.next_block_height;
812 last_info = info;
813 }
814 download_task.await;
817 Ok(last_info)
818 }
819
820 async fn download_blobs(
821 &self,
822 remote_nodes: &[RemoteNode<Env::ValidatorNode>],
823 blob_ids: &[BlobId],
824 ) -> Result<(), chain_client::Error> {
825 let blobs = &self
826 .requests_scheduler
827 .download_blobs(
828 remote_nodes,
829 blob_ids,
830 self.options.blob_download_hedge_delay,
831 )
832 .await?
833 .ok_or_else(|| {
834 chain_client::Error::RemoteNodeError(NodeError::BlobsNotFound(blob_ids.to_vec()))
835 })?;
836 self.local_node.store_blobs(blobs).await.map_err(Into::into)
837 }
838
839 #[instrument(level = "trace", skip_all)]
844 pub(crate) async fn download_certificates_for_events(
845 &self,
846 event_ids: &[EventId],
847 ) -> Result<(), chain_client::Error> {
848 let mut validators = self.validator_nodes().await?;
849 let hedge_delay = self.options.certificate_batch_download_hedge_delay;
850 let mut remaining_event_ids = event_ids.to_vec();
851
852 while !remaining_event_ids.is_empty() {
853 let remaining_ref = &remaining_event_ids;
854 validators.shuffle(&mut rand::thread_rng());
855 let result = communicate_concurrently(
856 &validators,
857 move |remote_node| {
858 let validator_key = remote_node.public_key;
859 let validator_address = remote_node.address();
860 Box::pin(async move {
861 let heights = remote_node
863 .node
864 .event_block_heights(remaining_ref.to_vec())
865 .await?;
866
867 let mut chain_heights = BTreeMap::<_, BTreeSet<_>>::new();
869 let mut expected_events = BTreeMap::<_, HashSet<EventId>>::new();
870 let mut unresolved = Vec::new();
871 for (event_id, maybe_height) in remaining_ref.iter().zip(heights) {
872 if let Some(height) = maybe_height {
873 chain_heights
874 .entry(event_id.chain_id)
875 .or_default()
876 .insert(height);
877 expected_events
878 .entry((event_id.chain_id, height))
879 .or_default()
880 .insert(event_id.clone());
881 } else {
882 unresolved.push(event_id.clone());
883 }
884 }
885 if chain_heights.is_empty() {
886 return Err(chain_client::Error::from(NodeError::EventsNotFound(remaining_ref.clone())));
888 }
889
890 let mut checked_certificates = Vec::<ConfirmedBlockCertificate>::new();
892 for (chain_id, heights) in chain_heights {
893 let heights_vec = heights.into_iter().collect::<Vec<_>>();
894 let certificates = self
895 .requests_scheduler
896 .download_certificates_by_heights(
897 &remote_node,
898 chain_id,
899 heights_vec,
900 )
901 .await?;
902 for cert in &certificates {
903 let block = cert.block();
905 let block_event_ids = block.event_ids().collect::<HashSet<_>>();
906 if let Some(expected_event_ids) =
907 expected_events.get(&(chain_id, block.header.height))
908 {
909 if !expected_event_ids.is_subset(&block_event_ids) {
910 tracing::debug!(
911 %validator_address, ?expected_event_ids, ?block_event_ids,
912 "validator lied about events in block."
913 );
914 return Err(NodeError::UnexpectedCertificateValue.into());
915 }
916 }
917 }
918 for cert in certificates {
919 self.check_certificate(&cert)
920 .await
921 .map_err(|error| {
922 tracing::debug!(
923 %validator_address, %error,
924 "invalid certificate"
925 );
926 error
927 })?
928 .into_result()
929 .map_err(|error| {
930 tracing::debug!(
931 %validator_address, %error,
932 "could not check certificate"
933 );
934 error
935 })?;
936 checked_certificates.push(cert);
937 }
938 }
939 Ok((checked_certificates, unresolved, validator_key))
940 })
941 },
942 hedge_delay,
943 self.storage_client().clock(),
944 )
945 .await;
946
947 match result {
948 Ok((certificates, unresolved, validator_key)) => {
949 for certificate in certificates {
950 let mode = ReceiveCertificateMode::AlreadyChecked;
951 self.receive_sender_certificate(
952 self.storage_client().cache_certificate(certificate),
953 mode,
954 None,
955 )
956 .await?;
957 }
958 validators.retain(|node| node.public_key != validator_key);
959 remaining_event_ids = unresolved;
960 }
961 Err(errors) => {
962 for (validator, error) in &errors {
963 warn!(
964 %validator,
965 %error,
966 "failed to download event certificates from validator",
967 );
968 }
969 return Err(NodeError::EventsNotFound(remaining_event_ids).into());
971 }
972 }
973 }
974 Ok(())
975 }
976
977 #[instrument(level = "trace", skip_all)]
987 async fn bootstrap_chain_from_checkpoint(
988 &self,
989 remote_node: &RemoteNode<Env::ValidatorNode>,
990 chain_id: ChainId,
991 checkpoint_height: BlockHeight,
992 ) -> Result<(), chain_client::Error> {
993 let local_next = match self.local_node.chain_info(chain_id).await {
994 Ok(info) => info.next_block_height,
995 Err(LocalNodeError::BlobsNotFound(_)) => BlockHeight::ZERO,
1000 Err(err) => return Err(err.into()),
1001 };
1002 if local_next > checkpoint_height {
1003 return Ok(());
1004 }
1005 let certificates = remote_node
1006 .download_certificates_by_heights(chain_id, vec![checkpoint_height])
1007 .await?;
1008 if certificates.is_empty() {
1009 return Ok(());
1012 }
1013 self.process_certificates(
1019 slice::from_ref(remote_node),
1020 certificates,
1021 None,
1022 ProcessConfirmedBlockMode::Execute,
1023 )
1024 .await?;
1025 Ok(())
1026 }
1027
1028 #[instrument(level = "trace", skip_all)]
1033 async fn process_certificates(
1034 &self,
1035 remote_nodes: &[RemoteNode<Env::ValidatorNode>],
1036 certificates: Vec<ConfirmedBlockCertificate>,
1037 until_block_time: Option<Timestamp>,
1038 mode: ProcessConfirmedBlockMode,
1039 ) -> Result<Option<Box<ChainInfo>>, chain_client::Error> {
1040 let mut info = None;
1041 let created_blob_ids = certificates
1046 .iter()
1047 .flat_map(|certificate| certificate.value().block().created_blob_ids())
1048 .collect::<BTreeSet<BlobId>>();
1049 let required_blob_ids = certificates
1050 .iter()
1051 .flat_map(|certificate| certificate.value().required_blob_ids())
1052 .filter(|blob_id| !created_blob_ids.contains(blob_id))
1053 .collect::<Vec<_>>();
1054
1055 match self
1056 .local_node
1057 .read_blob_states_from_storage(&required_blob_ids)
1058 .await
1059 {
1060 Err(LocalNodeError::BlobsNotFound(blob_ids)) => {
1061 self.download_blobs(remote_nodes, &blob_ids).await?;
1062 }
1063 x => {
1064 x?;
1065 }
1066 }
1067
1068 for certificate in certificates {
1069 if let Some(until) = until_block_time {
1070 if certificate.value().block().header.timestamp >= until {
1071 break;
1072 }
1073 }
1074 let response = self
1075 .handle_certificate_with_retry(&certificate, remote_nodes, mode)
1076 .await?;
1077 info = Some(response.info);
1078 }
1079
1080 Ok(info)
1081 }
1082
1083 async fn handle_certificate_with_retry(
1087 &self,
1088 certificate: &ConfirmedBlockCertificate,
1089 nodes: &[RemoteNode<Env::ValidatorNode>],
1090 mode: ProcessConfirmedBlockMode,
1091 ) -> Result<ChainInfoResponse, chain_client::Error> {
1092 let mut downloaded_blobs = HashSet::<BlobId>::new();
1093 let mut downloaded_blocks = HashSet::<CryptoHash>::new();
1094 let mut events = EventSetDownloader::new(self);
1095 loop {
1096 let result = self
1097 .handle_confirmed_certificate(certificate.clone(), mode)
1098 .await;
1099 if let Err(LocalNodeError::BlobsNotFound(blob_ids)) = &result {
1100 let new_blobs = filter_new(blob_ids, &downloaded_blobs);
1101 if !new_blobs.is_empty() {
1102 self.download_blobs(nodes, &new_blobs).await?;
1103 downloaded_blobs.extend(new_blobs);
1104 continue;
1105 }
1106 }
1107 if let Err(LocalNodeError::BlocksNotFound(hashes)) = &result {
1108 let new_blocks = filter_new(hashes, &downloaded_blocks);
1109 if !new_blocks.is_empty() {
1110 self.download_pre_checkpoint_blocks(nodes, &new_blocks)
1111 .await?;
1112 downloaded_blocks.extend(new_blocks);
1113 continue;
1114 }
1115 }
1116 if let Err(LocalNodeError::EventsNotFound(event_ids)) = &result {
1117 if events.download_new(event_ids).await? {
1118 continue;
1119 }
1120 }
1121 return Ok(result?);
1122 }
1123 }
1124
1125 async fn download_pre_checkpoint_blocks(
1132 &self,
1133 nodes: &[RemoteNode<Env::ValidatorNode>],
1134 hashes: &[CryptoHash],
1135 ) -> Result<(), chain_client::Error> {
1136 for hash in hashes {
1137 let mut last_error = None;
1138 for node in nodes {
1139 match node.node.download_certificate(*hash).await {
1140 Ok(certificate) => {
1141 Box::pin(self.handle_certificate_with_retry(
1142 &certificate,
1143 nodes,
1144 ProcessConfirmedBlockMode::Auto,
1145 ))
1146 .await?;
1147 last_error = None;
1148 break;
1149 }
1150 Err(error) => last_error = Some(error),
1151 }
1152 }
1153 if let Some(error) = last_error {
1154 return Err(error.into());
1155 }
1156 }
1157 Ok(())
1158 }
1159
1160 async fn handle_certificate<T: ProcessableCertificate>(
1161 &self,
1162 certificate: T::Certificate,
1163 ) -> Result<ChainInfoResponse, LocalNodeError> {
1164 self.local_node
1165 .handle_certificate::<T>(certificate, &self.notifier)
1166 .await
1167 }
1168
1169 async fn handle_confirmed_certificate(
1170 &self,
1171 certificate: ConfirmedBlockCertificate,
1172 mode: ProcessConfirmedBlockMode,
1173 ) -> Result<ChainInfoResponse, LocalNodeError> {
1174 self.local_node
1175 .handle_confirmed_certificate(certificate, mode, &self.notifier)
1176 .await
1177 }
1178
1179 pub async fn admin_committee(&self) -> Result<(Epoch, Arc<Committee>), LocalNodeError> {
1181 let info = self.local_node.chain_info(self.admin_chain_id).await?;
1182 let hash = info
1183 .committee_hash
1184 .ok_or(LocalNodeError::InactiveChain(self.admin_chain_id))?;
1185 let committee = self
1186 .storage_client()
1187 .get_or_load_committee_by_hash(hash)
1188 .await?;
1189 Ok((info.epoch, committee))
1190 }
1191
1192 async fn validator_nodes(
1194 &self,
1195 ) -> Result<Vec<RemoteNode<Env::ValidatorNode>>, chain_client::Error> {
1196 let (_, committee) = self.admin_committee().await?;
1197 Ok(self.make_nodes(&committee)?)
1198 }
1199
1200 fn make_nodes(
1202 &self,
1203 committee: &Committee,
1204 ) -> Result<Vec<RemoteNode<Env::ValidatorNode>>, NodeError> {
1205 Ok(self
1206 .validator_node_provider()
1207 .make_nodes(committee)?
1208 .map(|(public_key, node)| RemoteNode { public_key, node })
1209 .collect())
1210 }
1211
1212 pub async fn get_chain_description_blob(
1215 &self,
1216 chain_id: ChainId,
1217 ) -> Result<Arc<Blob>, chain_client::Error> {
1218 let chain_desc_id = BlobId::new(chain_id.0, BlobType::ChainDescription);
1219 let blob = self
1220 .local_node
1221 .storage_client()
1222 .read_blob(chain_desc_id)
1223 .await?;
1224 if let Some(blob) = blob {
1225 return Ok(blob.into_std());
1227 }
1228 self.synchronize_chain_state(self.admin_chain_id).await?;
1230 let nodes = self.validator_nodes().await?;
1231 Ok(self
1232 .update_local_node_with_blobs_from(vec![chain_desc_id], &nodes)
1233 .await?
1234 .pop()
1235 .unwrap() .into_std())
1237 }
1238
1239 pub async fn get_chain_description(
1242 &self,
1243 chain_id: ChainId,
1244 ) -> Result<ChainDescription, chain_client::Error> {
1245 let blob = self.get_chain_description_blob(chain_id).await?;
1246 Ok(bcs::from_bytes(blob.bytes())?)
1247 }
1248
1249 pub async fn get_application_description_blob(
1254 &self,
1255 application_id: ApplicationId,
1256 ) -> Result<Arc<Blob>, chain_client::Error> {
1257 let blob_id = application_id.description_blob_id();
1258 let blob = self.local_node.storage_client().read_blob(blob_id).await?;
1259 if let Some(blob) = blob {
1260 return Ok(blob.into_std());
1262 }
1263 Box::pin(self.synchronize_chain_state(self.admin_chain_id)).await?;
1265 let nodes = self.validator_nodes().await?;
1266 Ok(self
1267 .update_local_node_with_blobs_from(vec![blob_id], &nodes)
1268 .await?
1269 .pop()
1270 .unwrap() .into_std())
1272 }
1273
1274 pub async fn get_application_description(
1277 &self,
1278 application_id: ApplicationId,
1279 ) -> Result<ApplicationDescription, chain_client::Error> {
1280 let blob = self
1281 .get_application_description_blob(application_id)
1282 .await?;
1283 Ok(bcs::from_bytes(blob.bytes())?)
1284 }
1285
1286 #[instrument(level = "trace", skip_all)]
1288 pub(crate) async fn finalize_block(
1289 self: &Arc<Self>,
1290 committee: &Committee,
1291 certificate: ValidatedBlockCertificate,
1292 ) -> Result<ConfirmedBlockCertificate, chain_client::Error> {
1293 debug!(round = %certificate.round(), "Submitting block for confirmation");
1294 let hashed_value = ConfirmedBlock::new(certificate.block().clone());
1295 let full_justification = certificate.full_justification();
1300 let finalize_action = CommunicateAction::FinalizeBlock {
1301 certificate: Box::new(certificate),
1302 delivery: self.options.cross_chain_message_delivery,
1303 };
1304 let quorum = self
1305 .communicate_chain_action(committee, finalize_action, hashed_value)
1306 .await?;
1307 let justification = if quorum.first_round() {
1313 JustificationChain::default()
1314 } else {
1315 full_justification
1316 };
1317 ensure!(
1320 quorum.justification_commitment() == justification.commitment(quorum.hash()),
1321 chain_client::Error::ProtocolError(
1322 "A quorum confirmed with a justification commitment that does not match the \
1323 validated certificate's justification chain",
1324 )
1325 );
1326 let certificate = ConfirmedBlockCertificate::from_parts(quorum, justification);
1327 self.receive_certificate_with_checked_signatures(
1328 certificate.clone(),
1329 ProcessConfirmedBlockMode::Execute,
1330 )
1331 .await?;
1332 Ok(certificate)
1333 }
1334
1335 #[instrument(level = "trace", skip_all)]
1337 async fn submit_block_proposal<T: ProcessableCertificate>(
1338 self: &Arc<Self>,
1339 committee: Arc<Committee>,
1340 proposal: Box<BlockProposal>,
1341 value: T,
1342 ) -> Result<T::Certificate, chain_client::Error> {
1343 debug!(
1344 round = %proposal.content.round,
1345 "Submitting block proposal to validators"
1346 );
1347
1348 let justification = match proposal.original_proposal.as_ref() {
1352 Some(OriginalProposal::Regular { certificate }) => certificate.full_justification(),
1353 Some(OriginalProposal::Fast(_)) | None => JustificationChain::default(),
1354 };
1355
1356 let block_timestamp = proposal.content.block.timestamp;
1358 let local_time = self.local_node.storage_client().clock().current_time();
1359 if block_timestamp > local_time {
1360 info!(
1361 chain_id = %proposal.content.block.chain_id,
1362 %block_timestamp,
1363 %local_time,
1364 "Block timestamp is in the future; waiting until it can be proposed",
1365 );
1366 }
1367
1368 let (clock_skew_sender, mut clock_skew_receiver) = mpsc::unbounded_channel();
1370 let submit_action = CommunicateAction::SubmitBlock {
1371 proposal,
1372 blob_ids: value.required_blob_ids().into_iter().collect(),
1373 clock_skew_sender,
1374 };
1375
1376 let validity_threshold = committee.validity_threshold();
1378 let committee_clone = committee.clone();
1379 let clock_skew_check_handle = linera_base::Task::spawn(async move {
1380 let mut skew_weight = 0u64;
1381 let mut min_skew = TimeDelta::MAX;
1382 let mut max_skew = TimeDelta::ZERO;
1383 while let Some((public_key, clock_skew)) = clock_skew_receiver.recv().await {
1384 if clock_skew.as_micros() > 0 {
1385 skew_weight += committee_clone.weight(&public_key);
1386 min_skew = min_skew.min(clock_skew);
1387 max_skew = max_skew.max(clock_skew);
1388 if skew_weight >= validity_threshold {
1389 warn!(
1390 skew_weight,
1391 validity_threshold,
1392 min_skew_ms = min_skew.as_micros() / 1000,
1393 max_skew_ms = max_skew.as_micros() / 1000,
1394 "A validity threshold of validators reported clock skew; \
1395 consider checking your system clock",
1396 );
1397 return;
1398 }
1399 }
1400 }
1401 });
1402
1403 let quorum = self
1404 .communicate_chain_action(&committee, submit_action, value)
1405 .await?;
1406
1407 clock_skew_check_handle.await;
1408
1409 ensure!(
1417 quorum.unlocking_round() == justification.top_unlocking_round(),
1418 chain_client::Error::ProtocolError(
1419 "A quorum voted with an unlocking round that does not match the proposal's \
1420 justification chain",
1421 )
1422 );
1423 ensure!(
1424 quorum.justification_commitment() == justification.commitment(quorum.hash()),
1425 chain_client::Error::ProtocolError(
1426 "A quorum voted with a justification commitment that does not match the \
1427 proposal's justification chain",
1428 )
1429 );
1430 let certificate = T::make_certificate(quorum, justification);
1431 self.handle_certificate::<T>(certificate.clone()).await?;
1432 Ok(certificate)
1433 }
1434
1435 #[instrument(level = "trace", skip_all, fields(chain_id, block_height, delivery))]
1437 async fn communicate_chain_updates(
1438 self: &Arc<Self>,
1439 committee: &Committee,
1440 chain_id: ChainId,
1441 height: BlockHeight,
1442 delivery: CrossChainMessageDelivery,
1443 latest_certificate: Option<CacheArc<ConfirmedBlockCertificate>>,
1444 ) -> Result<(), chain_client::Error> {
1445 let nodes = self.make_nodes(committee)?;
1446 communicate_with_quorum(
1447 &nodes,
1448 committee,
1449 |_: &()| (),
1450 |remote_node| {
1451 let mut updater = ValidatorUpdater {
1452 remote_node,
1453 client: self.clone(),
1454 admin_chain_id: self.admin_chain_id,
1455 };
1456 let certificate = latest_certificate.clone();
1457 Box::pin(async move {
1458 updater
1459 .send_chain_information(chain_id, height, delivery, certificate)
1460 .await
1461 })
1462 },
1463 self.options.quorum_grace_period,
1464 )
1465 .await?;
1466 Ok(())
1467 }
1468
1469 #[instrument(level = "trace", skip_all)]
1475 async fn communicate_chain_action<T: CertificateValue>(
1476 self: &Arc<Self>,
1477 committee: &Committee,
1478 action: CommunicateAction,
1479 value: T,
1480 ) -> Result<GenericCertificate<T>, chain_client::Error> {
1481 let nodes = self.make_nodes(committee)?;
1482 let ((votes_hash, votes_round, _, _, _), votes) = communicate_with_quorum(
1487 &nodes,
1488 committee,
1489 |vote: &LiteVote| {
1490 (
1491 vote.value.value_hash,
1492 vote.round,
1493 vote.unlocking_round,
1494 vote.first_round,
1495 vote.justification_commitment,
1496 )
1497 },
1498 |remote_node| {
1499 let mut updater = ValidatorUpdater {
1500 remote_node,
1501 client: self.clone(),
1502 admin_chain_id: self.admin_chain_id,
1503 };
1504 let action = action.clone();
1505 Box::pin(async move { updater.send_chain_update(action).await })
1506 },
1507 self.options.quorum_grace_period,
1508 )
1509 .await?;
1510 ensure!(
1511 (votes_hash, votes_round) == (value.hash(), action.round()),
1512 chain_client::Error::UnexpectedQuorum {
1513 hash: votes_hash,
1514 round: votes_round,
1515 expected_hash: value.hash(),
1516 expected_round: action.round(),
1517 }
1518 );
1519 let certificate = LiteCertificate::try_from_votes(votes)
1524 .ok_or_else(|| {
1525 chain_client::Error::InternalError(
1526 "Vote values or rounds don't match; this is a bug",
1527 )
1528 })?
1529 .with_value(value)
1530 .ok_or_else(|| {
1531 chain_client::Error::ProtocolError("A quorum voted for an unexpected value")
1532 })?;
1533 Ok(certificate)
1534 }
1535
1536 #[instrument(level = "trace", skip_all)]
1539 async fn receive_certificate_with_checked_signatures(
1540 &self,
1541 certificate: ConfirmedBlockCertificate,
1542 mode: ProcessConfirmedBlockMode,
1543 ) -> Result<(), chain_client::Error> {
1544 let block = certificate.block();
1545 self.download_certificates(block.header.chain_id, block.header.height)
1547 .await?;
1548 let nodes = self.validator_nodes().await?;
1551 self.handle_certificate_with_retry(&certificate, &nodes, mode)
1552 .await?;
1553 Ok(())
1554 }
1555
1556 #[instrument(level = "trace", skip_all)]
1561 fn receive_sender_certificate(
1566 &self,
1567 certificate: CacheArc<ConfirmedBlockCertificate>,
1568 mode: ReceiveCertificateMode,
1569 nodes: Option<Vec<RemoteNode<Env::ValidatorNode>>>,
1570 ) -> ReceiveSenderCertificateFuture<'_> {
1571 Box::pin(async move {
1572 if let ReceiveCertificateMode::NeedsCheck = mode {
1574 let mut check_result = self.check_certificate(&certificate).await?;
1575 if matches!(check_result, CheckCertificateResult::FutureEpoch) {
1576 let admin_chain_id = self.admin_chain_id;
1584 let epoch = certificate.block().header.epoch;
1585 info!(
1586 %epoch,
1587 "certificate is from an unknown epoch; synchronizing the admin chain"
1588 );
1589 let synced_from_serving_node = if let Some(nodes) = &nodes {
1590 let certificate = &certificate;
1591 communicate_concurrently(
1592 nodes,
1593 |node| {
1594 Box::pin(async move {
1595 self.synchronize_chain_state_from(&node, admin_chain_id)
1596 .await?;
1597 match self.check_certificate(certificate).await? {
1598 CheckCertificateResult::FutureEpoch => {
1599 Err(chain_client::Error::CommitteeSynchronizationError)
1600 }
1601 _ => Ok(()),
1602 }
1603 })
1604 },
1605 self.options.blob_download_hedge_delay,
1606 self.storage_client().clock(),
1607 )
1608 .await
1609 .is_ok()
1610 } else {
1611 false
1612 };
1613 if synced_from_serving_node {
1614 check_result = self.check_certificate(&certificate).await?;
1615 }
1616 if matches!(check_result, CheckCertificateResult::FutureEpoch) {
1617 Box::pin(self.synchronize_chain_state(admin_chain_id)).await?;
1618 check_result = self.check_certificate(&certificate).await?;
1619 }
1620 }
1621 check_result.into_result()?;
1622 }
1623 let nodes = if let Some(nodes) = nodes {
1625 nodes
1626 } else {
1627 self.validator_nodes().await?
1628 };
1629 let processing_mode = if self
1630 .chain_mode(certificate.value().chain_id())
1631 .is_some_and(|m| m.should_sync_chain_state())
1632 {
1633 ProcessConfirmedBlockMode::Auto
1634 } else {
1635 ProcessConfirmedBlockMode::Preprocess
1636 };
1637 self.handle_certificate_with_retry(&certificate, &nodes, processing_mode)
1638 .await?;
1639
1640 Ok(())
1641 })
1642 }
1643
1644 #[instrument(level = "debug", skip_all, fields(chain_id = %sender_chain_id))]
1646 async fn download_and_process_sender_chain(
1647 &self,
1648 sender_chain_id: ChainId,
1649 nodes: &[RemoteNode<Env::ValidatorNode>],
1650 received_log: &ReceivedLogs,
1651 mut remote_heights: Vec<BlockHeight>,
1652 sender: mpsc::UnboundedSender<ChainAndHeight>,
1653 ) {
1654 let mut nodes = nodes.to_vec();
1655 while !remote_heights.is_empty() {
1656 if let Ok(local_certs) = self
1659 .storage_client()
1660 .read_certificates_by_heights(sender_chain_id, &remote_heights)
1661 .await
1662 {
1663 let mut still_needed = Vec::new();
1664 for (height, maybe_cert) in remote_heights.iter().copied().zip(local_certs) {
1665 if let Some(certificate) = maybe_cert {
1666 let chain_id = certificate.block().header.chain_id;
1667 if let Err(error) = sender.send(ChainAndHeight { chain_id, height }) {
1668 error!(
1669 %chain_id, %height, %error,
1670 "failed to send chain and height over the channel",
1671 );
1672 }
1673 } else {
1674 still_needed.push(height);
1675 }
1676 }
1677 remote_heights = still_needed;
1678 if remote_heights.is_empty() {
1679 break;
1680 }
1681 }
1682
1683 let remote_heights_ref = &remote_heights;
1684 let certificates = match communicate_concurrently(
1685 &nodes,
1686 async move |remote_node| {
1687 let mut remote_heights = remote_heights_ref.clone();
1688 remote_heights.retain(|height| {
1691 received_log.validator_has_block(
1692 &remote_node.public_key,
1693 sender_chain_id,
1694 *height,
1695 )
1696 });
1697 if remote_heights.is_empty() {
1698 return Err(NodeError::MissingCertificateValue);
1701 }
1702 let certificates = self
1703 .requests_scheduler
1704 .download_certificates_by_heights(
1705 &remote_node,
1706 sender_chain_id,
1707 remote_heights,
1708 )
1709 .await?;
1710 let mut certificates_with_check_results = vec![];
1711 for cert in certificates {
1712 let check_result = self.check_certificate(&cert).await?;
1713 certificates_with_check_results
1714 .push((cert, check_result.into_result().is_ok()));
1715 }
1716 Ok(certificates_with_check_results)
1717 },
1718 self.options.certificate_batch_download_hedge_delay,
1719 self.storage_client().clock(),
1720 )
1721 .await
1722 {
1723 Ok(certificates_with_check_results) => certificates_with_check_results,
1724 Err(errors) => {
1725 let faulty_validators = errors
1726 .into_iter()
1727 .map(|(validator, error)| {
1728 warn!(
1729 %validator,
1730 %sender_chain_id,
1731 %error,
1732 "failed to download certificates from validator",
1733 );
1734 validator
1735 })
1736 .collect::<BTreeSet<_>>();
1737 nodes.retain(|node| !faulty_validators.contains(&node.public_key));
1739 if nodes.is_empty() {
1740 info!(
1741 chain_id = %sender_chain_id,
1742 "could not download certificates for chain - no more correct validators left"
1743 );
1744 return;
1745 }
1746 continue;
1747 }
1748 };
1749
1750 trace!(
1751 num_certificates = %certificates.len(),
1752 "received certificates",
1753 );
1754
1755 let mut to_remove_from_queue = BTreeSet::new();
1756
1757 for (certificate, check_result) in certificates {
1758 let hash = certificate.hash();
1759 let chain_id = certificate.block().header.chain_id;
1760 let height = certificate.block().header.height;
1761 if !check_result {
1762 to_remove_from_queue.insert(height);
1766 continue;
1767 }
1768 let mode = ReceiveCertificateMode::AlreadyChecked;
1770 if let Err(error) = self
1771 .receive_sender_certificate(
1772 self.storage_client().cache_certificate(certificate),
1773 mode,
1774 None,
1775 )
1776 .await
1777 {
1778 warn!(%error, %hash, "Received invalid certificate");
1779 } else {
1780 to_remove_from_queue.insert(height);
1781 if let Err(error) = sender.send(ChainAndHeight { chain_id, height }) {
1782 error!(
1783 %chain_id,
1784 %height,
1785 %error,
1786 "failed to send chain and height over the channel",
1787 );
1788 }
1789 }
1790 }
1791
1792 remote_heights.retain(|height| !to_remove_from_queue.contains(height));
1793 }
1794 trace!("find_received_certificates: finished processing chain");
1795 }
1796
1797 #[instrument(level = "trace", skip(self))]
1799 async fn get_received_log_from_validator(
1800 &self,
1801 chain_id: ChainId,
1802 remote_node: &RemoteNode<Env::ValidatorNode>,
1803 tracker: u64,
1804 ) -> Result<Vec<ChainAndHeight>, chain_client::Error> {
1805 let mut offset = tracker;
1806
1807 let mut remote_log = Vec::new();
1809 loop {
1810 trace!("get_received_log_from_validator: looping");
1811 let query = ChainInfoQuery::new(chain_id).with_received_log_excluding_first_n(offset);
1812 let info = remote_node.handle_chain_info_query(query).await?;
1813 let received_entries = info.requested_received_log.len();
1814 offset += received_entries as u64;
1815 remote_log.extend(info.requested_received_log);
1816 trace!(
1817 remote_node = remote_node.address(),
1818 %received_entries,
1819 "get_received_log_from_validator: received log batch",
1820 );
1821 if received_entries < CHAIN_INFO_MAX_RECEIVED_LOG_ENTRIES {
1822 break;
1823 }
1824 }
1825
1826 trace!(
1827 remote_node = remote_node.address(),
1828 num_entries = remote_log.len(),
1829 "get_received_log_from_validator: returning downloaded log",
1830 );
1831
1832 Ok(remote_log)
1833 }
1834
1835 async fn download_sender_block_with_sending_ancestors(
1841 &self,
1842 receiver_chain_id: ChainId,
1843 sender_chain_id: ChainId,
1844 height: BlockHeight,
1845 remote_node: &RemoteNode<Env::ValidatorNode>,
1846 ) -> Result<(), chain_client::Error> {
1847 let next_outbox_height = self
1848 .local_node
1849 .next_outbox_heights(&[sender_chain_id], receiver_chain_id)
1850 .await?
1851 .get(&sender_chain_id)
1852 .copied()
1853 .unwrap_or(BlockHeight::ZERO);
1854
1855 let mut certificates = BTreeMap::new();
1858 let mut current_height = height;
1859 let mut current_hash: Option<CryptoHash> = None;
1862
1863 while current_height >= next_outbox_height {
1865 let certificate = if let Some(local) = self
1869 .try_read_local_certificate(sender_chain_id, current_height, current_hash)
1870 .await?
1871 {
1872 local
1873 } else {
1874 let downloaded = self
1875 .requests_scheduler
1876 .download_certificates_by_heights(
1877 remote_node,
1878 sender_chain_id,
1879 vec![current_height],
1880 )
1881 .await?;
1882 let Some(certificate) = downloaded.into_iter().next() else {
1883 return Err(chain_client::Error::CannotDownloadMissingSenderBlock {
1884 chain_id: sender_chain_id,
1885 height: current_height,
1886 });
1887 };
1888 self.storage_client().cache_certificate(certificate)
1889 };
1890
1891 self.check_certificate(&certificate).await?.into_result()?;
1893
1894 let block = certificate.block();
1896 let next = block
1897 .body
1898 .previous_message_blocks
1899 .get(&receiver_chain_id)
1900 .map(|(prev_hash, prev_height)| (*prev_hash, *prev_height));
1901
1902 certificates.insert(current_height, certificate);
1904
1905 if let Some((prev_hash, prev_height)) = next {
1906 current_height = prev_height;
1908 current_hash = Some(prev_hash);
1909 } else {
1910 break;
1912 }
1913 }
1914
1915 if certificates.is_empty() {
1916 self.retry_pending_cross_chain_requests(sender_chain_id)
1917 .await?;
1918 }
1919
1920 for certificate in certificates.into_values() {
1922 self.receive_sender_certificate(
1923 certificate,
1924 ReceiveCertificateMode::AlreadyChecked,
1925 Some(vec![remote_node.clone()]),
1926 )
1927 .await?;
1928 }
1929
1930 Ok(())
1931 }
1932
1933 async fn download_event_bearing_blocks(
1937 &self,
1938 publisher_chain_id: ChainId,
1939 initial_blocks: BTreeSet<(BlockHeight, CryptoHash)>,
1940 local_next_block_height: BlockHeight,
1941 subscribed_streams: &BTreeSet<StreamId>,
1942 remote_node: &RemoteNode<Env::ValidatorNode>,
1943 ) -> Result<(), chain_client::Error> {
1944 if initial_blocks.is_empty() {
1945 return Ok(());
1946 }
1947
1948 let mut certificates = BTreeMap::new();
1949 let mut blocks_to_fetch = initial_blocks;
1950 let next_expected_events = self
1951 .local_node
1952 .next_expected_events(
1953 publisher_chain_id,
1954 subscribed_streams.iter().cloned().collect(),
1955 )
1956 .await?;
1957
1958 while let Some((current_height, current_hash)) = blocks_to_fetch.pop_last() {
1959 if current_height < local_next_block_height {
1960 continue; }
1962 if certificates.contains_key(¤t_height) {
1963 continue;
1964 }
1965
1966 let certificate = if let Some(certificate) =
1967 self.storage_client().read_certificate(current_hash).await?
1968 {
1969 certificate
1970 } else {
1971 let downloaded = self
1972 .requests_scheduler
1973 .download_certificates(remote_node, publisher_chain_id, current_height, 1)
1974 .await?;
1975 let Some(certificate) = downloaded.into_iter().next() else {
1976 tracing::debug!(
1977 validator = remote_node.address(),
1978 %publisher_chain_id,
1979 height = %current_height,
1980 "failed to download event publisher block"
1981 );
1982 continue;
1983 };
1984
1985 self.check_certificate(&certificate).await?.into_result()?;
1986
1987 self.storage_client().cache_certificate(certificate)
1988 };
1989
1990 let block = certificate.block();
1991 for stream_id in subscribed_streams {
1993 if let Some((prev_hash, prev_height)) =
1994 block.body.previous_event_blocks.get(stream_id)
1995 {
1996 if next_expected_events.get(stream_id).is_some_and(|index| {
1997 block
1998 .body
1999 .events
2000 .iter()
2001 .flatten()
2002 .find(|event| event.stream_id == *stream_id)
2003 .is_some_and(|event| event.index == *index)
2004 }) {
2005 continue;
2006 }
2007 if !certificates.contains_key(prev_height) {
2008 blocks_to_fetch.insert((*prev_height, *prev_hash));
2009 }
2010 }
2011 }
2012
2013 certificates.insert(current_height, certificate);
2014 }
2015
2016 for certificate in certificates.into_values() {
2018 self.receive_sender_certificate(
2019 certificate,
2020 ReceiveCertificateMode::AlreadyChecked,
2021 Some(vec![remote_node.clone()]),
2022 )
2023 .await?;
2024 }
2025
2026 Ok(())
2027 }
2028
2029 async fn sync_events_from_node(
2032 &self,
2033 chain_id: ChainId,
2034 stream_ids: &BTreeSet<StreamId>,
2035 remote_node: &RemoteNode<Env::ValidatorNode>,
2036 ) -> Result<(), chain_client::Error> {
2037 let stream_ids_vec = stream_ids.iter().cloned().collect::<Vec<_>>();
2038 let mut initial_blocks = BTreeSet::new();
2039 for chunk in stream_ids_vec.chunks(self.options.max_event_stream_queries) {
2040 let query = ChainInfoQuery::new(chain_id).with_previous_event_blocks(chunk.to_vec());
2041 let info = remote_node.handle_chain_info_query(query).await?;
2042 initial_blocks.extend(info.requested_previous_event_blocks.values().copied());
2043 }
2044 let local_height = match self.local_node.chain_info(chain_id).await {
2045 Ok(info) => info.next_block_height,
2046 Err(LocalNodeError::InactiveChain(_) | LocalNodeError::BlobsNotFound(_)) => {
2047 BlockHeight::ZERO
2048 }
2049 Err(error) => return Err(error.into()),
2050 };
2051 self.download_event_bearing_blocks(
2052 chain_id,
2053 initial_blocks,
2054 local_height,
2055 stream_ids,
2056 remote_node,
2057 )
2058 .await
2059 }
2060
2061 #[instrument(
2062 level = "trace", skip_all,
2063 fields(certificate_hash = ?incoming_certificate.hash()),
2064 )]
2065 async fn check_certificate(
2066 &self,
2067 incoming_certificate: &ConfirmedBlockCertificate,
2068 ) -> Result<CheckCertificateResult, NodeError> {
2069 let epoch = incoming_certificate.block().header.epoch;
2070 let storage = self.storage_client();
2071 let view_err = |error: ExecutionError| NodeError::ViewError {
2072 error: error.to_string(),
2073 };
2074 if storage.is_epoch_revoked(epoch).await.map_err(view_err)? {
2075 return Ok(CheckCertificateResult::OldEpoch);
2076 }
2077 let Some(committee) = storage.committee_for_epoch(epoch).await.map_err(view_err)? else {
2078 return Ok(CheckCertificateResult::FutureEpoch);
2079 };
2080 incoming_certificate.check(&committee)?;
2081 Ok(CheckCertificateResult::New)
2082 }
2083
2084 #[instrument(level = "trace", skip_all)]
2088 async fn synchronize_chain_state(
2089 &self,
2090 chain_id: ChainId,
2091 ) -> Result<Box<ChainInfo>, chain_client::Error> {
2092 let (_, committee) = self.admin_committee().await?;
2093 self.synchronize_chain_from_committee(chain_id, committee)
2094 .await
2095 }
2096
2097 #[instrument(level = "trace", skip_all)]
2102 pub(crate) async fn synchronize_chain_from_committee(
2103 &self,
2104 chain_id: ChainId,
2105 committee: Arc<Committee>,
2106 ) -> Result<Box<ChainInfo>, chain_client::Error> {
2107 #[cfg(with_metrics)]
2108 let _latency = if !self.is_chain_follow_only(chain_id) {
2109 Some(metrics::SYNCHRONIZE_CHAIN_STATE_LATENCY.measure_latency())
2110 } else {
2111 None
2112 };
2113
2114 let validators = self.make_nodes(&committee)?;
2115 Box::pin(self.fetch_chain_info(chain_id, &validators)).await?;
2116 communicate_with_quorum(
2117 &validators,
2118 &committee,
2119 |_: &()| (),
2120 |remote_node| async move {
2121 self.synchronize_chain_state_from(&remote_node, chain_id)
2122 .await
2123 },
2124 self.options.quorum_grace_period,
2125 )
2126 .await?;
2127
2128 self.local_node
2129 .chain_info(chain_id)
2130 .await
2131 .map_err(Into::into)
2132 }
2133
2134 #[instrument(level = "trace", skip(self, remote_node, chain_id))]
2140 pub(crate) async fn synchronize_chain_state_from(
2141 &self,
2142 remote_node: &RemoteNode<Env::ValidatorNode>,
2143 chain_id: ChainId,
2144 ) -> Result<(), chain_client::Error> {
2145 let with_manager_values = !self.is_chain_follow_only(chain_id);
2146 let query = if with_manager_values {
2147 ChainInfoQuery::new(chain_id).with_manager_values()
2148 } else {
2149 ChainInfoQuery::new(chain_id)
2150 };
2151 let query = query.with_latest_checkpoint_height();
2152 let remote_info = remote_node.handle_chain_info_query(query).await?;
2153
2154 if let Some(checkpoint_height) = remote_info.requested_latest_checkpoint_height {
2161 self.bootstrap_chain_from_checkpoint(remote_node, chain_id, checkpoint_height)
2162 .await?;
2163 }
2164
2165 let local_info = self
2166 .download_certificates_from(remote_node, chain_id, remote_info.next_block_height, None)
2167 .await?;
2168
2169 if !with_manager_values {
2170 return Ok(());
2171 }
2172
2173 let local_height = local_info.next_block_height;
2175 if local_height != remote_info.next_block_height {
2176 debug!(
2177 remote_node = remote_node.address(),
2178 remote_height = %remote_info.next_block_height,
2179 local_height = %local_height,
2180 "synced from validator, but remote height and local height are different",
2181 );
2182 return Ok(());
2183 };
2184
2185 if let Some(timeout) = remote_info.manager.timeout {
2186 self.handle_certificate::<Timeout>(*timeout).await?;
2187 }
2188 let mut proposals = Vec::new();
2189 if let Some(proposal) = remote_info.manager.requested_signed_proposal {
2190 proposals.push(*proposal);
2191 }
2192 if let Some(proposal) = remote_info.manager.requested_proposed {
2193 proposals.push(*proposal);
2194 }
2195 if let Some(locking) = remote_info.manager.requested_locking {
2196 match *locking {
2197 LockingBlock::Fast(proposal) => {
2198 proposals.push(proposal);
2199 }
2200 LockingBlock::Regular(cert) => {
2201 let hash = cert.hash();
2202 if let Err(error) = self.try_process_locking_block_from(remote_node, cert).await
2203 {
2204 debug!(
2205 remote_node = remote_node.address(),
2206 %hash,
2207 height = %local_height,
2208 %error,
2209 "skipping locked block from validator",
2210 );
2211 }
2212 }
2213 }
2214 }
2215 'proposal_loop: for proposal in proposals {
2216 let owner: AccountOwner = proposal.owner();
2217 if let Err(mut err) = self
2218 .local_node
2219 .handle_block_proposal(proposal.clone())
2220 .await
2221 {
2222 if let LocalNodeError::BlobsNotFound(_) = &err {
2223 let required_blob_ids = proposal.required_blob_ids().collect::<Vec<_>>();
2224 if !required_blob_ids.is_empty() {
2225 let mut blobs = Vec::new();
2226 for blob_id in required_blob_ids {
2227 let blob_content = match self
2228 .requests_scheduler
2229 .download_pending_blob(remote_node, chain_id, blob_id)
2230 .await
2231 {
2232 Ok(content) => content,
2233 Err(error) => {
2234 info!(
2235 remote_node = remote_node.address(),
2236 height = %local_height,
2237 proposer = %owner,
2238 %blob_id,
2239 %error,
2240 "skipping proposal from validator; failed to download blob",
2241 );
2242 continue 'proposal_loop;
2243 }
2244 };
2245 blobs.push(Blob::new(blob_content));
2246 }
2247 self.local_node
2248 .handle_pending_blobs(chain_id, blobs)
2249 .await?;
2250 if let Err(new_err) = self
2252 .local_node
2253 .handle_block_proposal(proposal.clone())
2254 .await
2255 {
2256 err = new_err;
2257 } else {
2258 continue;
2259 }
2260 }
2261 if let LocalNodeError::BlobsNotFound(blob_ids) = &err {
2262 self.update_local_node_with_blobs_from(
2263 blob_ids.clone(),
2264 slice::from_ref(remote_node),
2265 )
2266 .await?;
2267 if let Err(new_err) = self
2269 .local_node
2270 .handle_block_proposal(proposal.clone())
2271 .await
2272 {
2273 err = new_err;
2274 } else {
2275 continue;
2276 }
2277 }
2278 }
2279 if let LocalNodeError::EventsNotFound(event_ids) = &err {
2280 if let Err(error) =
2281 Box::pin(self.download_certificates_for_events(event_ids)).await
2282 {
2283 info!(
2284 remote_node = remote_node.address(),
2285 height = %local_height,
2286 proposer = %owner,
2287 %error,
2288 "skipping proposal from validator; failed to download events",
2289 );
2290 continue 'proposal_loop;
2291 }
2292 if let Err(new_err) = self
2294 .local_node
2295 .handle_block_proposal(proposal.clone())
2296 .await
2297 {
2298 err = new_err;
2299 } else {
2300 continue;
2301 }
2302 }
2303 while let LocalNodeError::WorkerError(WorkerError::ChainError(chain_err)) = &err {
2304 if let ChainError::MissingCrossChainUpdate {
2305 chain_id,
2306 origin,
2307 height,
2308 } = &**chain_err
2309 {
2310 self.download_sender_block_with_sending_ancestors(
2311 *chain_id,
2312 *origin,
2313 *height,
2314 remote_node,
2315 )
2316 .await?;
2317 if let Err(new_err) = self
2319 .local_node
2320 .handle_block_proposal(proposal.clone())
2321 .await
2322 {
2323 err = new_err;
2324 } else {
2325 continue 'proposal_loop;
2326 }
2327 } else {
2328 break;
2329 }
2330 }
2331
2332 debug!(
2333 remote_node = remote_node.address(),
2334 proposer = %owner,
2335 height = %local_height,
2336 error = %err,
2337 "skipping proposal from validator",
2338 );
2339 }
2340 }
2341 Ok(())
2342 }
2343
2344 async fn try_process_locking_block_from(
2345 &self,
2346 remote_node: &RemoteNode<Env::ValidatorNode>,
2347 certificate: ValidatedBlockCertificate,
2348 ) -> Result<(), chain_client::Error> {
2349 let chain_id = certificate.inner().chain_id();
2350 let mut downloaded_blobs = HashSet::<BlobId>::new();
2351 let mut events = EventSetDownloader::new(self);
2352 loop {
2353 let result = self
2354 .handle_certificate::<ValidatedBlock>(certificate.clone())
2355 .await;
2356 if let Err(LocalNodeError::BlobsNotFound(blob_ids)) = &result {
2357 let new_blobs = filter_new(blob_ids, &downloaded_blobs);
2358 if !new_blobs.is_empty() {
2359 let mut blobs = Vec::new();
2360 for blob_id in &new_blobs {
2361 let blob_content = self
2362 .requests_scheduler
2363 .download_pending_blob(remote_node, chain_id, *blob_id)
2364 .await?;
2365 blobs.push(Blob::new(blob_content));
2366 }
2367 self.local_node
2368 .handle_pending_blobs(chain_id, blobs)
2369 .await?;
2370 downloaded_blobs.extend(new_blobs);
2371 continue;
2372 }
2373 }
2374 if let Err(LocalNodeError::EventsNotFound(event_ids)) = &result {
2375 if events.download_new(event_ids).await? {
2376 continue;
2377 }
2378 }
2379 result?;
2380 return Ok(());
2381 }
2382 }
2383
2384 async fn update_local_node_with_blobs_from(
2387 &self,
2388 blob_ids: Vec<BlobId>,
2389 remote_nodes: &[RemoteNode<Env::ValidatorNode>],
2390 ) -> Result<Vec<CacheArc<Blob>>, chain_client::Error> {
2391 let hedge_delay = self.options.blob_download_hedge_delay;
2392 let blob_ids = blob_ids.into_iter().collect::<BTreeSet<_>>();
2394 stream::iter(blob_ids.into_iter().map(|blob_id| {
2395 communicate_concurrently(
2396 remote_nodes,
2397 async move |remote_node| {
2398 let certificate = self
2399 .requests_scheduler
2400 .download_certificate_for_blob(&remote_node, blob_id)
2401 .await?;
2402 self.receive_sender_certificate(
2403 self.storage_client().cache_certificate(certificate),
2404 ReceiveCertificateMode::NeedsCheck,
2405 Some(vec![remote_node.clone()]),
2406 )
2407 .await?;
2408 let blob = self
2409 .local_node
2410 .storage_client()
2411 .read_blob(blob_id)
2412 .await?
2413 .ok_or_else(|| LocalNodeError::BlobsNotFound(vec![blob_id]))?;
2414 Result::<_, chain_client::Error>::Ok(blob)
2415 },
2416 hedge_delay,
2417 self.storage_client().clock(),
2418 )
2419 .map_err(move |errors| {
2420 for (validator, error) in &errors {
2421 warn!(
2422 %validator,
2423 %blob_id,
2424 %error,
2425 "failed to download certificate-for-blob from validator",
2426 );
2427 }
2428 chain_client::Error::CannotDownloadBlob(blob_id)
2429 })
2430 }))
2431 .buffer_unordered(self.options.max_joined_tasks)
2432 .collect::<Vec<_>>()
2433 .await
2434 .into_iter()
2435 .collect()
2436 }
2437
2438 #[instrument(level = "trace", skip(self, block))]
2448 async fn stage_block_execution(
2449 &self,
2450 block: ProposedBlock,
2451 round: Option<u32>,
2452 published_blobs: Vec<Blob>,
2453 policy: BundleExecutionPolicy,
2454 ) -> Result<(Block, ChainInfoResponse, HashSet<ChainId>), chain_client::Error> {
2455 let mut events = EventSetDownloader::new(self);
2456 loop {
2457 let result = self
2458 .local_node
2459 .stage_block_execution(
2460 block.clone(),
2461 round,
2462 published_blobs.clone(),
2463 policy.clone(),
2464 )
2465 .await;
2466 if let Err(LocalNodeError::BlobsNotFound(blob_ids)) = &result {
2467 let validators = self.validator_nodes().await?;
2468 self.update_local_node_with_blobs_from(blob_ids.clone(), &validators)
2469 .await?;
2470 continue; }
2472 if let Err(LocalNodeError::EventsNotFound(event_ids)) = &result {
2473 if events.download_new(event_ids).await? {
2474 continue; }
2476 }
2478 if let Ok((_, executed_block, _, _, _)) = &result {
2479 let hash = executed_block.hash();
2480 let notification = Notification {
2481 chain_id: executed_block.header.chain_id,
2482 reason: Reason::BlockExecuted {
2483 height: executed_block.header.height,
2484 hash,
2485 },
2486 };
2487 self.notifier.notify(&[notification]);
2488 }
2489 let (
2490 _modified_block,
2491 executed_block,
2492 response,
2493 _resource_tracker,
2494 never_reject_origins,
2495 ) = result?;
2496 return Ok((executed_block, response, never_reject_origins));
2497 }
2498 }
2499}
2500
2501fn filter_new<T: Clone + Eq + std::hash::Hash>(
2503 ids: &[T],
2504 already_downloaded: &HashSet<T>,
2505) -> Vec<T> {
2506 ids.iter()
2507 .filter(|id| !already_downloaded.contains(*id))
2508 .cloned()
2509 .collect()
2510}
2511
2512pub(crate) struct EventSetDownloader<'a, Env: Environment> {
2517 client: &'a Client<Env>,
2518 downloaded: HashSet<EventId>,
2519}
2520
2521impl<'a, Env: Environment> EventSetDownloader<'a, Env> {
2522 pub(crate) fn new(client: &'a Client<Env>) -> Self {
2523 Self {
2524 client,
2525 downloaded: HashSet::new(),
2526 }
2527 }
2528
2529 pub(crate) async fn download_new(
2535 &mut self,
2536 event_ids: &[EventId],
2537 ) -> Result<bool, chain_client::Error> {
2538 let new_events = filter_new(event_ids, &self.downloaded);
2539 if new_events.is_empty() {
2540 return Ok(false);
2541 }
2542 Box::pin(self.client.download_certificates_for_events(&new_events)).await?;
2543 self.downloaded.extend(new_events);
2544 Ok(true)
2545 }
2546}
2547
2548pub(crate) type ClockOf<Env> = <<Env as Environment>::Storage as linera_storage::Storage>::Clock;
2554
2555pub(crate) async fn hedged_fan_out<Peer, T, Err, NextPeer, NextFut, Op, OpFut>(
2567 first_peer: Peer,
2568 mut next_peer: NextPeer,
2569 operation: Op,
2570 hedge_schedule: impl Fn(usize) -> Duration,
2571 clock: &(impl linera_storage::Clock + Sync),
2574) -> Result<T, Vec<Err>>
2575where
2576 NextPeer: FnMut() -> NextFut,
2577 NextFut: Future<Output = Option<Peer>>,
2578 Op: Fn(Peer) -> OpFut,
2579 OpFut: Future<Output = Result<T, Err>>,
2580{
2581 use futures::future::{select, Either};
2582
2583 let mut in_flight = FuturesUnordered::new();
2584 let mut errors = vec![];
2585 let mut started = 0usize;
2586 let arm = |started: usize| clock.sleep_for(hedge_schedule(started));
2587
2588 in_flight.push(operation(first_peer));
2589 started += 1;
2590 let mut hedge = arm(started);
2591
2592 loop {
2593 if in_flight.is_empty() {
2594 match next_peer().await {
2596 Some(peer) => {
2597 in_flight.push(operation(peer));
2598 started += 1;
2599 hedge = arm(started);
2600 }
2601 None => return Err(errors),
2602 }
2603 continue;
2604 }
2605 match select(in_flight.next(), hedge).await {
2606 Either::Left((Some(Ok(value)), _)) => return Ok(value),
2608 Either::Left((Some(Err(error)), pending_hedge)) => {
2610 errors.push(error);
2611 hedge = pending_hedge;
2612 if let Some(peer) = next_peer().await {
2613 in_flight.push(operation(peer));
2614 started += 1;
2615 hedge = arm(started);
2616 }
2617 }
2618 Either::Left((None, pending_hedge)) => hedge = pending_hedge,
2620 Either::Right(((), _)) => match next_peer().await {
2622 Some(peer) => {
2623 in_flight.push(operation(peer));
2624 started += 1;
2625 hedge = arm(started);
2626 }
2627 None => break,
2628 },
2629 }
2630 }
2631
2632 while let Some(result) = in_flight.next().await {
2634 match result {
2635 Ok(value) => return Ok(value),
2636 Err(error) => errors.push(error),
2637 }
2638 }
2639 Err(errors)
2640}
2641
2642async fn communicate_concurrently<A, E, F, R, V>(
2648 nodes: &[RemoteNode<A>],
2649 f: F,
2650 hedge_delay: Duration,
2651 clock: &(impl linera_storage::Clock + Sync),
2652) -> Result<V, Vec<(ValidatorPublicKey, E)>>
2653where
2654 F: Clone + FnOnce(RemoteNode<A>) -> R,
2655 RemoteNode<A>: Clone,
2656 R: Future<Output = Result<V, E>>,
2657{
2658 let mut nodes = nodes.to_vec();
2659 nodes.shuffle(&mut rand::thread_rng());
2660 let mut nodes = nodes.into_iter();
2661 let Some(first_peer) = nodes.next() else {
2662 return Err(vec![]);
2663 };
2664 hedged_fan_out(
2665 first_peer,
2666 move || std::future::ready(nodes.next()),
2667 |node: RemoteNode<A>| {
2668 let fun = f.clone();
2669 async move {
2670 let public_key = node.public_key;
2671 fun(node).await.map_err(|err| (public_key, err))
2672 }
2673 },
2674 |started| {
2675 let k = u32::try_from(started).unwrap_or(u32::MAX);
2676 hedge_delay.saturating_mul(k).saturating_mul(k)
2677 },
2678 clock,
2679 )
2680 .await
2681}
2682
2683#[must_use]
2685pub struct AbortOnDrop(pub AbortHandle);
2686
2687impl Drop for AbortOnDrop {
2688 #[instrument(level = "trace", skip(self))]
2689 fn drop(&mut self) {
2690 self.0.abort();
2691 }
2692}
2693
2694#[derive(Clone, Serialize, Deserialize)]
2696pub struct PendingProposal {
2697 pub block: ProposedBlock,
2699 pub blobs: Vec<Blob>,
2701 #[serde(default)]
2704 pub auto_retry_outcome: Option<BlockExecutionOutcome>,
2705 #[serde(default)]
2707 pub round: Option<Round>,
2708}
2709
2710enum ReceiveCertificateMode {
2711 NeedsCheck,
2712 AlreadyChecked,
2713}
2714
2715enum CheckCertificateResult {
2716 OldEpoch,
2718 FutureEpoch,
2721 New,
2722}
2723
2724impl CheckCertificateResult {
2725 fn into_result(self) -> Result<(), chain_client::Error> {
2726 match self {
2727 Self::OldEpoch => Err(chain_client::Error::CommitteeDeprecationError),
2728 Self::FutureEpoch => Err(chain_client::Error::CommitteeSynchronizationError),
2729 Self::New => Ok(()),
2730 }
2731 }
2732}
2733
2734#[cfg(not(target_arch = "wasm32"))]
2738pub async fn create_bytecode_blobs(
2739 contract: Bytecode,
2740 service: Bytecode,
2741 vm_runtime: VmRuntime,
2742 formats: Option<Vec<u8>>,
2743) -> (Vec<Blob>, ModuleId) {
2744 let formats_blob = formats.map(Blob::new_application_formats);
2745 let formats_blob_hash = formats_blob.as_ref().map(|blob| blob.id().hash);
2746 let (mut blobs, module_id) = match vm_runtime {
2747 VmRuntime::Wasm => {
2748 let (compressed_contract, compressed_service) =
2749 tokio::task::spawn_blocking(move || (contract.compress(), service.compress()))
2750 .await
2751 .expect("Compression should not panic");
2752 let contract_blob = Blob::new_contract_bytecode(compressed_contract);
2753 let service_blob = Blob::new_service_bytecode(compressed_service);
2754 let module_id = ModuleId::new_with_formats(
2755 contract_blob.id().hash,
2756 service_blob.id().hash,
2757 vm_runtime,
2758 formats_blob_hash,
2759 );
2760 (vec![contract_blob, service_blob], module_id)
2761 }
2762 VmRuntime::Evm => {
2763 let compressed_contract = contract.compress();
2764 let evm_contract_blob = Blob::new_evm_bytecode(compressed_contract);
2765 let module_id = ModuleId::new_with_formats(
2766 evm_contract_blob.id().hash,
2767 evm_contract_blob.id().hash,
2768 vm_runtime,
2769 formats_blob_hash,
2770 );
2771 (vec![evm_contract_blob], module_id)
2772 }
2773 };
2774 if let Some(blob) = formats_blob {
2775 blobs.push(blob);
2776 }
2777 (blobs, module_id)
2778}
2779
2780#[cfg(test)]
2781mod chain_modes_tests {
2782 use std::collections::BTreeSet;
2783
2784 use linera_base::{crypto::CryptoHash, identifiers::ChainId};
2785
2786 use super::{ChainModes, ListeningMode};
2787
2788 #[test]
2792 fn remove_mode_updates_full_set_only_for_full_chains() {
2793 let mut modes = ChainModes::default();
2794 let full = ChainId(CryptoHash::test_hash("full"));
2795 let events_only = ChainId(CryptoHash::test_hash("events-only"));
2796 modes.extend_mode(full, ListeningMode::FullChain);
2797 modes.extend_mode(events_only, ListeningMode::EventsOnly(BTreeSet::new()));
2798
2799 let full_hash_before = modes.full().hash();
2800 assert!(matches!(
2802 modes.remove_mode(&events_only),
2803 Some(ListeningMode::EventsOnly(_))
2804 ));
2805 assert!(modes.get(&events_only).is_none());
2806 assert!(modes.remove_mode(&events_only).is_none());
2808 assert_eq!(modes.full().hash(), full_hash_before);
2809 assert_eq!(modes.full().inner().0, BTreeSet::from([full]));
2810
2811 assert_eq!(modes.remove_mode(&full), Some(ListeningMode::FullChain));
2813 assert_ne!(modes.full().hash(), full_hash_before);
2814 assert!(modes.full().inner().0.is_empty());
2815 }
2816}
2817
2818#[cfg(test)]
2819mod communicate_concurrently_tests {
2820 use std::sync::{
2821 atomic::{AtomicUsize, Ordering},
2822 Arc,
2823 };
2824
2825 use linera_base::crypto::ValidatorKeypair;
2826 use linera_storage::TestClock;
2827
2828 use super::*;
2829
2830 fn test_node() -> RemoteNode<()> {
2831 RemoteNode {
2832 public_key: ValidatorKeypair::generate().public_key,
2833 node: (),
2834 }
2835 }
2836
2837 #[tokio::test]
2840 async fn does_not_wait_after_failures() {
2841 let clock = TestClock::new();
2842 let nodes: Vec<_> = (0..5).map(|_| test_node()).collect();
2843 let calls = Arc::new(AtomicUsize::new(0));
2844 let result: Result<(), Vec<(ValidatorPublicKey, &str)>> = communicate_concurrently(
2845 &nodes,
2846 {
2847 let calls = calls.clone();
2848 move |_node| {
2849 let calls = calls.clone();
2850 async move {
2851 calls.fetch_add(1, Ordering::SeqCst);
2852 Err("unavailable")
2853 }
2854 }
2855 },
2856 Duration::from_secs(30),
2857 &clock,
2858 )
2859 .await;
2860 assert_eq!(result.unwrap_err().len(), 5);
2861 assert_eq!(calls.load(Ordering::SeqCst), 5);
2862 assert_eq!(clock.current_time(), Timestamp::from(0));
2864 }
2865
2866 #[tokio::test]
2869 async fn fails_over_to_a_working_node() {
2870 let clock = TestClock::new();
2871 let nodes: Vec<_> = (0..5).map(|_| test_node()).collect();
2872 let working = nodes[3].public_key;
2873 let result: Result<u32, Vec<(ValidatorPublicKey, &str)>> = communicate_concurrently(
2874 &nodes,
2875 move |node| async move {
2876 if node.public_key == working {
2877 Ok(42)
2878 } else {
2879 Err("unavailable")
2880 }
2881 },
2882 Duration::from_secs(30),
2883 &clock,
2884 )
2885 .await;
2886 assert_eq!(result.unwrap(), 42);
2887 assert_eq!(clock.current_time(), Timestamp::from(0));
2888 }
2889
2890 fn peer_source(n: usize) -> impl FnMut() -> std::future::Ready<Option<usize>> {
2897 let mut next = 1usize;
2898 move || {
2899 let peer = (next < n).then_some(next);
2900 next += 1;
2901 std::future::ready(peer)
2902 }
2903 }
2904
2905 #[tokio::test]
2908 async fn slow_first_peer_is_hedged_but_not_cancelled() {
2909 let clock = TestClock::new();
2910 let delay = Duration::from_secs(1);
2911 let order = Arc::new(std::sync::Mutex::new(Vec::new()));
2912 let (started_tx, mut started_rx) = tokio::sync::mpsc::unbounded_channel();
2913 let release0 = Arc::new(tokio::sync::Notify::new());
2914 let gates = [release0.clone(), Arc::new(tokio::sync::Notify::new())];
2915
2916 let operation = {
2917 let order = order.clone();
2918 move |peer: usize| {
2919 let started_tx = started_tx.clone();
2920 let order = order.clone();
2921 let gate = gates[peer].clone();
2922 async move {
2923 order.lock().unwrap().push(peer);
2924 started_tx.send(peer).unwrap();
2925 gate.notified().await;
2926 if peer == 0 {
2927 Ok::<u32, &str>(42)
2928 } else {
2929 Err("slow loser")
2930 }
2931 }
2932 }
2933 };
2934
2935 let fan = tokio::spawn({
2936 let clock = clock.clone();
2937 async move {
2938 hedged_fan_out(
2939 0usize,
2940 peer_source(2),
2941 operation,
2942 move |k| delay * u32::try_from(k).unwrap_or(u32::MAX),
2943 &clock,
2944 )
2945 .await
2946 }
2947 });
2948
2949 assert_eq!(started_rx.recv().await, Some(0));
2951 clock.add(TimeDelta::from_duration(delay));
2953 assert_eq!(started_rx.recv().await, Some(1));
2954 release0.notify_one();
2956 assert_eq!(fan.await.unwrap(), Ok(42));
2957 assert_eq!(*order.lock().unwrap(), vec![0, 1]);
2958 }
2959
2960 #[tokio::test]
2964 async fn hedge_schedule_determines_start_times() {
2965 let clock = TestClock::new();
2966 let unit = Duration::from_secs(1);
2967 let starts = Arc::new(std::sync::Mutex::new(Vec::new()));
2968 let (started_tx, mut started_rx) = tokio::sync::mpsc::unbounded_channel();
2969
2970 let operation = {
2971 let starts = starts.clone();
2972 let clock = clock.clone();
2973 move |peer: usize| {
2974 let started_tx = started_tx.clone();
2975 let starts = starts.clone();
2976 let clock = clock.clone();
2977 async move {
2978 starts.lock().unwrap().push((peer, clock.current_time()));
2979 started_tx.send(peer).unwrap();
2980 std::future::pending::<()>().await;
2981 Ok::<u32, &str>(0)
2982 }
2983 }
2984 };
2985
2986 let fan = tokio::spawn({
2987 let clock = clock.clone();
2988 async move {
2989 hedged_fan_out(
2990 0usize,
2991 peer_source(4),
2992 operation,
2993 move |k| {
2994 let k = u32::try_from(k).unwrap_or(u32::MAX);
2995 unit * k * k
2996 },
2997 &clock,
2998 )
2999 .await
3000 }
3001 });
3002
3003 assert_eq!(started_rx.recv().await, Some(0));
3005 clock.add(TimeDelta::from_duration(unit));
3006 assert_eq!(started_rx.recv().await, Some(1));
3007 clock.add(TimeDelta::from_duration(unit * 4));
3008 assert_eq!(started_rx.recv().await, Some(2));
3009 clock.add(TimeDelta::from_duration(unit * 9));
3010 assert_eq!(started_rx.recv().await, Some(3));
3011
3012 assert_eq!(
3014 *starts.lock().unwrap(),
3015 vec![
3016 (0, Timestamp::from(0)),
3017 (1, Timestamp::from(1_000_000)),
3018 (2, Timestamp::from(5_000_000)),
3019 (3, Timestamp::from(14_000_000)),
3020 ]
3021 );
3022 fan.abort();
3023 }
3024
3025 #[tokio::test]
3028 async fn frozen_clock_disables_the_hedge() {
3029 let clock = TestClock::new();
3030 let (started_tx, mut started_rx) = tokio::sync::mpsc::unbounded_channel();
3031 let release0 = Arc::new(tokio::sync::Notify::new());
3032
3033 let operation = {
3034 let release0 = release0.clone();
3035 move |peer: usize| {
3036 let started_tx = started_tx.clone();
3037 let release0 = release0.clone();
3038 async move {
3039 started_tx.send(peer).unwrap();
3040 if peer == 0 {
3041 release0.notified().await;
3042 Ok::<u32, &str>(7)
3043 } else {
3044 Ok(99)
3046 }
3047 }
3048 }
3049 };
3050
3051 let fan = tokio::spawn({
3052 let clock = clock.clone();
3053 async move {
3054 hedged_fan_out(
3055 0usize,
3056 peer_source(2),
3057 operation,
3058 move |k| Duration::from_secs(1) * u32::try_from(k).unwrap_or(u32::MAX),
3059 &clock,
3060 )
3061 .await
3062 }
3063 });
3064
3065 assert_eq!(started_rx.recv().await, Some(0));
3066 tokio::task::yield_now().await;
3068 assert!(
3069 started_rx.try_recv().is_err(),
3070 "the hedge must not fire while the clock is frozen"
3071 );
3072 release0.notify_one();
3074 assert_eq!(fan.await.unwrap(), Ok(7));
3075 assert_eq!(clock.current_time(), Timestamp::from(0));
3076 }
3077
3078 #[tokio::test]
3081 async fn failure_fails_over_without_waiting_out_the_hedge() {
3082 let clock = TestClock::new();
3083 let (started_tx, mut started_rx) = tokio::sync::mpsc::unbounded_channel();
3084 let release0 = Arc::new(tokio::sync::Notify::new());
3085
3086 let operation = {
3087 let release0 = release0.clone();
3088 move |peer: usize| {
3089 let started_tx = started_tx.clone();
3090 let release0 = release0.clone();
3091 async move {
3092 started_tx.send(peer).unwrap();
3093 match peer {
3094 0 => {
3095 release0.notified().await;
3096 Err::<u32, &str>("dead")
3097 }
3098 1 => Err("dead"),
3099 _ => Ok(55),
3100 }
3101 }
3102 }
3103 };
3104
3105 let fan = tokio::spawn({
3106 let clock = clock.clone();
3107 async move {
3108 hedged_fan_out(
3109 0usize,
3110 peer_source(3),
3111 operation,
3112 move |k| Duration::from_secs(100) * u32::try_from(k).unwrap_or(u32::MAX),
3114 &clock,
3115 )
3116 .await
3117 }
3118 });
3119
3120 assert_eq!(started_rx.recv().await, Some(0));
3121 release0.notify_one();
3123 assert_eq!(started_rx.recv().await, Some(1));
3124 assert_eq!(started_rx.recv().await, Some(2));
3125 assert_eq!(fan.await.unwrap(), Ok(55));
3126 assert_eq!(clock.current_time(), Timestamp::from(0));
3128 }
3129}