1use std::{
4 collections::{BTreeMap, BTreeSet},
5 sync::Arc,
6 time::Duration,
7};
8
9use bytes::Bytes;
10use consensus_config::AuthorityIndex;
11use consensus_types::block::{BlockRef, Round, TransactionIndex};
12use futures::{StreamExt as _, stream::FuturesUnordered};
13use itertools::Itertools as _;
14use mysten_common::{ZipDebugEqIteratorExt, debug_fatal};
15use mysten_metrics::{
16 monitored_future,
17 monitored_mpsc::{Receiver, Sender, channel},
18 monitored_scope,
19};
20use parking_lot::{Mutex, RwLock};
21use rand::{prelude::SliceRandom as _, rngs::ThreadRng};
22use sui_macros::fail_point_async;
23use tap::TapFallible;
24use tokio::{
25 sync::{mpsc::error::TrySendError, oneshot},
26 task::JoinSet,
27 time::{Instant, sleep, sleep_until, timeout},
28};
29use tracing::{debug, info, trace, warn};
30
31use crate::{
32 BlockAPI,
33 block::{ExtendedBlock, SignedBlock, VerifiedBlock},
34 block_verifier::BlockVerifier,
35 commit::CommitIndex,
36 commit_vote_monitor::{CommitVoteMonitor, is_commit_lagging},
37 context::Context,
38 dag_state::DagState,
39 error::{ConsensusError, ConsensusResult},
40 network::{ObserverNetworkClient, PeerId, SynchronizerClient, ValidatorNetworkClient},
41 peers_pool::PeersPool,
42 round_tracker::RoundTracker,
43 task::{shutdown_join_set, spawn_blocking},
44};
45use crate::{core_thread::CoreThreadDispatcher, transaction_vote_tracker::TransactionVoteTracker};
46
47const FETCH_BLOCKS_CONCURRENCY: usize = 5;
49
50const FETCH_REQUEST_TIMEOUT: Duration = Duration::from_millis(2_000);
52const FETCH_FROM_PEERS_TIMEOUT: Duration = Duration::from_millis(4_000);
53
54const MAX_AUTHORITIES_TO_FETCH_PER_BLOCK: usize = 2;
55
56const MAX_PERIODIC_SYNC_PEERS: usize = 3;
58
59const COMMIT_PROGRESS_TIMEOUT: Duration = Duration::from_secs(10);
61
62struct BlocksGuard {
63 map: Arc<InflightBlocksMap>,
64 block_refs: BTreeSet<BlockRef>,
65 peer: PeerId,
66}
67
68impl Drop for BlocksGuard {
69 fn drop(&mut self) {
70 self.map.unlock_blocks(&self.block_refs, self.peer.clone());
71 }
72}
73
74struct InflightBlocksMap {
79 inner: Mutex<BTreeMap<BlockRef, BTreeSet<PeerId>>>,
80}
81
82impl InflightBlocksMap {
83 fn new() -> Arc<Self> {
84 Arc::new(Self {
85 inner: Mutex::new(BTreeMap::new()),
86 })
87 }
88
89 fn lock_blocks(
95 self: &Arc<Self>,
96 missing_block_refs: BTreeSet<BlockRef>,
97 peer: PeerId,
98 ) -> Option<BlocksGuard> {
99 let mut blocks = BTreeSet::new();
100 let mut inner = self.inner.lock();
101
102 for block_ref in missing_block_refs {
103 let peers = inner.entry(block_ref).or_default();
106 if peers.len() < MAX_AUTHORITIES_TO_FETCH_PER_BLOCK && peers.get(&peer).is_none() {
107 assert!(peers.insert(peer.clone()));
108 blocks.insert(block_ref);
109 }
110 }
111
112 if blocks.is_empty() {
113 None
114 } else {
115 Some(BlocksGuard {
116 map: self.clone(),
117 block_refs: blocks,
118 peer,
119 })
120 }
121 }
122
123 fn unlock_blocks(self: &Arc<Self>, block_refs: &BTreeSet<BlockRef>, peer: PeerId) {
127 let mut blocks_to_fetch = self.inner.lock();
129 for block_ref in block_refs {
130 let peers = blocks_to_fetch
131 .get_mut(block_ref)
132 .expect("Should have found a non empty map");
133
134 assert!(peers.remove(&peer), "Peer should be present!");
135
136 if peers.is_empty() {
138 blocks_to_fetch.remove(block_ref);
139 }
140 }
141 }
142
143 fn swap_locks(
147 self: &Arc<Self>,
148 blocks_guard: BlocksGuard,
149 peer: PeerId,
150 ) -> Option<BlocksGuard> {
151 let block_refs = blocks_guard.block_refs.clone();
152
153 drop(blocks_guard);
155
156 self.lock_blocks(block_refs, peer)
158 }
159
160 #[cfg(test)]
161 fn num_of_locked_blocks(self: &Arc<Self>) -> usize {
162 let inner = self.inner.lock();
163 inner.len()
164 }
165}
166
167enum Command {
168 FetchBlocks {
169 missing_block_refs: BTreeSet<BlockRef>,
170 peer: PeerId,
171 result: oneshot::Sender<Result<(), ConsensusError>>,
172 },
173 FetchOwnLastBlock,
174 KickOffScheduler,
175 Shutdown {
176 result: oneshot::Sender<()>,
177 },
178}
179
180pub(crate) struct SynchronizerHandle {
181 commands_sender: Sender<Command>,
182 tasks: tokio::sync::Mutex<JoinSet<()>>,
183}
184
185impl SynchronizerHandle {
186 pub(crate) async fn fetch_blocks(
189 &self,
190 missing_block_refs: BTreeSet<BlockRef>,
191 peer: PeerId,
192 ) -> ConsensusResult<()> {
193 let (sender, receiver) = oneshot::channel();
194 self.commands_sender
195 .send(Command::FetchBlocks {
196 missing_block_refs,
197 peer,
198 result: sender,
199 })
200 .await
201 .map_err(|_err| ConsensusError::Shutdown)?;
202 receiver.await.map_err(|_err| ConsensusError::Shutdown)?
203 }
204
205 pub(crate) async fn stop(&self) {
206 let (shutdown_sender, shutdown_receiver) = oneshot::channel();
207 let _ = self
208 .commands_sender
209 .send(Command::Shutdown {
210 result: shutdown_sender,
211 })
212 .await;
213 let _ = shutdown_receiver.await;
214
215 let mut tasks = self.tasks.lock().await;
216 shutdown_join_set(&mut tasks).await;
217 }
218
219 #[cfg(test)]
220 pub(crate) fn new_for_test() -> Arc<Self> {
222 use tokio::task::JoinSet;
223 let (tx, _rx) = channel("test_synchronizer", 1);
224 Arc::new(Self {
225 commands_sender: tx,
226 tasks: tokio::sync::Mutex::new(JoinSet::new()),
227 })
228 }
229}
230
231pub(crate) struct Synchronizer<
253 V: BlockVerifier,
254 D: CoreThreadDispatcher,
255 VC: ValidatorNetworkClient,
256 OC: ObserverNetworkClient,
257> {
258 context: Arc<Context>,
259 commands_receiver: Receiver<Command>,
260 fetch_block_senders: BTreeMap<PeerId, Sender<BlocksGuard>>,
261 core_dispatcher: Arc<D>,
262 commit_vote_monitor: Arc<CommitVoteMonitor>,
263 dag_state: Arc<RwLock<DagState>>,
264 fetch_blocks_scheduler_task: JoinSet<()>,
265 fetch_own_last_block_task: JoinSet<()>,
266 network_client: Arc<SynchronizerClient<VC, OC>>,
267 block_verifier: Arc<V>,
268 transaction_vote_tracker: TransactionVoteTracker,
269 round_tracker: Arc<RwLock<RoundTracker>>,
270 inflight_blocks_map: Arc<InflightBlocksMap>,
271 commands_sender: Sender<Command>,
272 last_changed_commit_index: CommitIndex,
273 last_commit_change_time: Instant,
274 commit_sync_failover: bool,
276 peers_pool: Arc<PeersPool>,
277}
278
279impl<V, D, VC, OC> Synchronizer<V, D, VC, OC>
280where
281 V: BlockVerifier,
282 D: CoreThreadDispatcher,
283 VC: ValidatorNetworkClient,
284 OC: ObserverNetworkClient,
285{
286 pub(crate) fn start(
287 network_client: Arc<SynchronizerClient<VC, OC>>,
288 context: Arc<Context>,
289 core_dispatcher: Arc<D>,
290 commit_vote_monitor: Arc<CommitVoteMonitor>,
291 block_verifier: Arc<V>,
292 transaction_vote_tracker: TransactionVoteTracker,
293 round_tracker: Arc<RwLock<RoundTracker>>,
294 dag_state: Arc<RwLock<DagState>>,
295 peers_pool: Arc<PeersPool>,
296 sync_last_known_own_block: bool,
297 ) -> Arc<SynchronizerHandle> {
298 let (commands_sender, commands_receiver) =
299 channel("consensus_synchronizer_commands", 1_000);
300 let inflight_blocks_map = InflightBlocksMap::new();
301
302 let mut fetch_block_senders = BTreeMap::new();
304 let mut tasks = JoinSet::new();
305
306 let known_peers = peers_pool.get_known_peers();
309 for peer in known_peers {
310 let (sender, receiver) =
311 channel("consensus_synchronizer_fetches", FETCH_BLOCKS_CONCURRENCY);
312 let fetch_blocks_from_peer_async = Self::fetch_blocks_from_peer(
313 peer.clone(),
314 network_client.clone(),
315 block_verifier.clone(),
316 transaction_vote_tracker.clone(),
317 commit_vote_monitor.clone(),
318 context.clone(),
319 core_dispatcher.clone(),
320 dag_state.clone(),
321 receiver,
322 commands_sender.clone(),
323 round_tracker.clone(),
324 peers_pool.clone(),
325 );
326 tasks.spawn(monitored_future!(fetch_blocks_from_peer_async));
327 fetch_block_senders.insert(peer, sender);
328 }
329
330 let commands_sender_clone = commands_sender.clone();
331
332 if sync_last_known_own_block {
333 commands_sender
334 .try_send(Command::FetchOwnLastBlock)
335 .expect("Failed to sync our last block");
336 }
337
338 tasks.spawn(monitored_future!(async move {
340 let mut s = Self {
341 context,
342 commands_receiver,
343 fetch_block_senders,
344 core_dispatcher,
345 commit_vote_monitor,
346 fetch_blocks_scheduler_task: JoinSet::new(),
347 fetch_own_last_block_task: JoinSet::new(),
348 network_client,
349 block_verifier,
350 transaction_vote_tracker,
351 inflight_blocks_map,
352 commands_sender: commands_sender_clone,
353 dag_state,
354 round_tracker,
355 last_changed_commit_index: 0,
356 last_commit_change_time: Instant::now(),
357 commit_sync_failover: false,
358 peers_pool,
359 };
360 s.run().await;
361 }));
362
363 Arc::new(SynchronizerHandle {
364 commands_sender,
365 tasks: tokio::sync::Mutex::new(tasks),
366 })
367 }
368
369 async fn run(&mut self) {
371 const PERIODIC_FETCH_INTERVAL: Duration = Duration::from_millis(200);
373 let scheduler_timeout = sleep_until(Instant::now() + PERIODIC_FETCH_INTERVAL);
374
375 tokio::pin!(scheduler_timeout);
376
377 loop {
378 tokio::select! {
379 Some(command) = self.commands_receiver.recv() => {
380 match command {
381 Command::FetchBlocks{ missing_block_refs, peer, result } => {
382 if !self.peers_pool.is_peer_known(&peer) {
384 result.send(Err(ConsensusError::PeerUnavailable(format!("{:?}", peer)))).ok();
385 continue;
386 }
387
388 let missing_block_refs = missing_block_refs
392 .into_iter()
393 .take(self.context.parameters.max_blocks_per_sync)
394 .collect();
395
396 let blocks_guard = self.inflight_blocks_map.lock_blocks(missing_block_refs, peer.clone());
397 let Some(blocks_guard) = blocks_guard else {
398 result.send(Ok(())).ok();
399 continue;
400 };
401
402 let r = self
405 .fetch_block_senders
406 .get(&peer)
407 .ok_or(ConsensusError::PeerNotFound(format!("Peer {} not found in fetch_block_senders", peer)))
408 .and_then(|sender| {
409 sender
410 .try_send(blocks_guard)
411 .map_err(|err| {
412 match err {
413 TrySendError::Full(_) => {
414 let peer_name = peer.labelname(&self.context);
415 self.context
416 .metrics
417 .node_metrics
418 .synchronizer_skipped_fetch_requests
419 .with_label_values(&[peer_name])
420 .inc();
421 ConsensusError::SynchronizerSaturated(format!("{:?}", peer))
422 },
423 TrySendError::Closed(_) => ConsensusError::Shutdown
424 }
425 })
426 });
427
428 result.send(r).ok();
429 }
430 Command::FetchOwnLastBlock => {
431 if self.fetch_own_last_block_task.is_empty() {
432 self.start_fetch_own_last_block_task();
433 }
434 }
435 Command::KickOffScheduler => {
436 let timeout = if self.fetch_blocks_scheduler_task.is_empty() {
439 Instant::now()
440 } else {
441 Instant::now() + PERIODIC_FETCH_INTERVAL.checked_div(2).unwrap()
442 };
443
444 if timeout < scheduler_timeout.deadline() {
446 scheduler_timeout.as_mut().reset(timeout);
447 }
448 }
449 Command::Shutdown { result } => {
450 self.shutdown_tasks().await;
451 self.fetch_block_senders.clear();
452 let _ = result.send(());
453 return;
454 }
455 }
456 },
457 Some(result) = self.fetch_own_last_block_task.join_next(), if !self.fetch_own_last_block_task.is_empty() => {
458 match result {
459 Ok(()) => {},
460 Err(e) => {
461 if e.is_cancelled() {
462 } else if e.is_panic() {
463 std::panic::resume_unwind(e.into_panic());
464 } else {
465 panic!("fetch our last block task failed: {e}");
466 }
467 },
468 };
469 },
470 Some(result) = self.fetch_blocks_scheduler_task.join_next(), if !self.fetch_blocks_scheduler_task.is_empty() => {
471 match result {
472 Ok(()) => {},
473 Err(e) => {
474 if e.is_cancelled() {
475 } else if e.is_panic() {
476 std::panic::resume_unwind(e.into_panic());
477 } else {
478 panic!("fetch blocks scheduler task failed: {e}");
479 }
480 },
481 };
482 },
483 () = &mut scheduler_timeout => {
484 if self.fetch_blocks_scheduler_task.is_empty()
487 && let Err(err) = self.start_fetch_missing_blocks_task().await {
488 debug!("Core is shutting down, synchronizer is shutting down: {err:?}");
489 self.shutdown_tasks().await;
490 return;
491 };
492
493 scheduler_timeout
494 .as_mut()
495 .reset(Instant::now() + PERIODIC_FETCH_INTERVAL);
496 }
497 }
498 }
499 }
500
501 async fn shutdown_tasks(&mut self) {
504 shutdown_join_set(&mut self.fetch_own_last_block_task).await;
505 shutdown_join_set(&mut self.fetch_blocks_scheduler_task).await;
506 }
507
508 async fn fetch_blocks_from_peer(
509 peer: PeerId,
510 network_client: Arc<SynchronizerClient<VC, OC>>,
511 block_verifier: Arc<V>,
512 transaction_vote_tracker: TransactionVoteTracker,
513 commit_vote_monitor: Arc<CommitVoteMonitor>,
514 context: Arc<Context>,
515 core_dispatcher: Arc<D>,
516 dag_state: Arc<RwLock<DagState>>,
517 mut receiver: Receiver<BlocksGuard>,
518 commands_sender: Sender<Command>,
519 round_tracker: Arc<RwLock<RoundTracker>>,
520 _peers_pool: Arc<PeersPool>,
521 ) {
522 const MAX_RETRIES: u32 = 3;
523 let mut requests = FuturesUnordered::new();
524
525 loop {
526 tokio::select! {
527 Some(blocks_guard) = receiver.recv(), if requests.len() < FETCH_BLOCKS_CONCURRENCY => {
528 let fetch_after_rounds = Self::get_fetch_after_rounds(&context, dag_state.clone());
529
530 requests.push(Self::fetch_blocks_request(network_client.clone(), peer.clone(), blocks_guard, fetch_after_rounds, true, FETCH_REQUEST_TIMEOUT, 1))
531 },
532 Some((response, blocks_guard, retries, _peer, fetch_after_rounds)) = requests.next() => {
533 match response {
534 Ok(blocks) => {
535 if let Err(err) = Self::process_fetched_blocks(blocks,
536 peer.clone(),
537 blocks_guard,
538 core_dispatcher.clone(),
539 block_verifier.clone(),
540 transaction_vote_tracker.clone(),
541 commit_vote_monitor.clone(),
542 context.clone(),
543 commands_sender.clone(),
544 round_tracker.clone(),
545 "live"
546 ).await {
547 warn!("Error while processing fetched blocks from peer {}: {err}", peer.hostname(&context));
548 context.metrics.node_metrics.synchronizer_process_fetched_failures.with_label_values(&[peer.labelname(&context).as_str(), "live"]).inc();
549 }
550 },
551 Err(_) => {
552 context.metrics.node_metrics.synchronizer_fetch_failures.with_label_values(&[peer.labelname(&context).as_str(), "live"]).inc();
553 if retries <= MAX_RETRIES {
554 requests.push(Self::fetch_blocks_request(network_client.clone(), peer.clone(), blocks_guard, fetch_after_rounds, true, FETCH_REQUEST_TIMEOUT, retries))
555 } else {
556 warn!("Max retries {retries} reached while trying to fetch blocks from peer {}.", peer.hostname(&context));
557 drop(blocks_guard);
559 }
560 }
561 }
562 },
563 else => {
564 info!("Fetching blocks from peer {} task will now abort.", peer.hostname(&context));
565 break;
566 }
567 }
568 }
569 }
570
571 async fn process_fetched_blocks(
574 mut serialized_blocks: Vec<Bytes>,
575 peer: PeerId,
576 requested_blocks_guard: BlocksGuard,
577 core_dispatcher: Arc<D>,
578 block_verifier: Arc<V>,
579 transaction_vote_tracker: TransactionVoteTracker,
580 commit_vote_monitor: Arc<CommitVoteMonitor>,
581 context: Arc<Context>,
582 commands_sender: Sender<Command>,
583 round_tracker: Arc<RwLock<RoundTracker>>,
584 sync_method: &str,
585 ) -> ConsensusResult<()> {
586 if serialized_blocks.is_empty() {
587 return Ok(());
588 }
589
590 serialized_blocks.truncate(context.parameters.max_blocks_per_sync);
592
593 let (blocks, voted_blocks) = spawn_blocking({
595 let block_verifier = block_verifier.clone();
596 let context = context.clone();
597 let peer = peer.clone();
598 move || Self::verify_blocks(serialized_blocks, block_verifier, &context, peer)
599 })
600 .await??;
601
602 if context.protocol_config.transaction_voting_enabled() {
603 transaction_vote_tracker.add_voted_blocks(voted_blocks);
604 }
605
606 for block in &blocks {
608 commit_vote_monitor.observe_block(block);
609 }
610
611 {
614 let mut tracker = round_tracker.write();
615 for block in &blocks {
616 tracker.update_from_verified_block(&ExtendedBlock {
617 block: block.clone(),
618 excluded_ancestors: vec![],
619 });
620 }
621 }
622
623 let metrics = &context.metrics.node_metrics;
624 metrics
625 .synchronizer_fetched_blocks_by_peer
626 .with_label_values(&[peer.labelname(&context).as_str(), sync_method])
627 .inc_by(blocks.len() as u64);
628 for block in &blocks {
629 let block_hostname = &context.committee.authority(block.author()).hostname;
630 metrics
631 .synchronizer_fetched_blocks_by_authority
632 .with_label_values(&[block_hostname.as_str(), sync_method])
633 .inc();
634 }
635
636 debug!(
637 "Synced {} missing blocks from peer {:?}: {}",
638 blocks.len(),
639 peer,
640 blocks.iter().map(|b| b.reference().to_string()).join(", "),
641 );
642
643 let missing_blocks = core_dispatcher
647 .add_blocks(blocks)
648 .await
649 .map_err(|_| ConsensusError::Shutdown)?;
650
651 drop(requested_blocks_guard);
653
654 if !missing_blocks.is_empty() {
656 if let Err(TrySendError::Full(_)) = commands_sender.try_send(Command::KickOffScheduler)
658 {
659 warn!("Commands channel is full")
660 }
661 }
662
663 context
664 .metrics
665 .node_metrics
666 .missing_blocks_after_fetch_total
667 .inc_by(missing_blocks.len() as u64);
668
669 Ok(())
670 }
671
672 fn get_fetch_after_rounds(
673 context: &Arc<Context>,
674 dag_state: Arc<RwLock<DagState>>,
675 ) -> Vec<Round> {
676 let (blocks, gc_round) = {
677 let dag_state = dag_state.read();
678 (
679 dag_state.get_last_cached_block_per_authority(Round::MAX),
680 dag_state.gc_round(),
681 )
682 };
683 assert_eq!(blocks.len(), context.committee.size());
684
685 blocks
686 .into_iter()
687 .map(|(block, _)| block.round().max(gc_round))
688 .collect::<Vec<_>>()
689 }
690
691 fn verify_blocks(
692 serialized_blocks: Vec<Bytes>,
693 block_verifier: Arc<V>,
694 context: &Context,
695 peer: PeerId,
696 ) -> ConsensusResult<(
697 Vec<VerifiedBlock>,
698 Vec<(VerifiedBlock, Vec<TransactionIndex>)>,
699 )> {
700 let mut verified_blocks = Vec::new();
701 let mut voted_blocks = Vec::new();
702 for serialized_block in serialized_blocks {
703 let signed_block: SignedBlock =
704 bcs::from_bytes(&serialized_block).map_err(ConsensusError::MalformedBlock)?;
705
706 let (verified_block, reject_txn_votes) = block_verifier
708 .verify_and_vote(signed_block, serialized_block)
709 .tap_err(|e| {
710 let peer_label = peer.labelname(context);
711 context
712 .metrics
713 .node_metrics
714 .invalid_blocks
715 .with_label_values(&[peer_label.as_str(), "synchronizer", e.clone().name()])
716 .inc();
717 info!("Invalid block received from {}: {}", peer, e);
718 })?;
719
720 let now = context.clock.timestamp_utc_ms();
722 let drift = verified_block.timestamp_ms().saturating_sub(now);
723 if drift > 0 {
724 let peer_hostname = &context
725 .committee
726 .authority(verified_block.author())
727 .hostname;
728 context
729 .metrics
730 .node_metrics
731 .block_timestamp_drift_ms
732 .with_label_values(&[peer_hostname.as_str(), "synchronizer"])
733 .inc_by(drift);
734
735 trace!(
736 "Synced block {} timestamp {} is in the future (now={}).",
737 verified_block.reference(),
738 verified_block.timestamp_ms(),
739 now
740 );
741 }
742
743 verified_blocks.push(verified_block.clone());
744 voted_blocks.push((verified_block, reject_txn_votes));
745 }
746
747 Ok((verified_blocks, voted_blocks))
748 }
749
750 async fn fetch_blocks_request(
751 network_client: Arc<SynchronizerClient<VC, OC>>,
752 peer: PeerId,
753 blocks_guard: BlocksGuard,
754 fetch_after_rounds: Vec<Round>,
755 fetch_missing_ancestors: bool,
756 request_timeout: Duration,
757 mut retries: u32,
758 ) -> (
759 ConsensusResult<Vec<Bytes>>,
760 BlocksGuard,
761 u32,
762 PeerId,
763 Vec<Round>,
764 ) {
765 let start = Instant::now();
766 let resp = timeout(
767 request_timeout,
768 network_client.fetch_blocks(
769 peer.clone(),
770 blocks_guard
771 .block_refs
772 .clone()
773 .into_iter()
774 .collect::<Vec<_>>(),
775 fetch_after_rounds.clone().into_iter().collect::<Vec<_>>(),
776 fetch_missing_ancestors,
777 request_timeout,
778 ),
779 )
780 .await;
781
782 fail_point_async!("consensus-delay");
783
784 let resp = match resp {
785 Ok(Err(err)) => {
786 sleep_until(start + request_timeout).await;
789 retries += 1;
790 Err(err)
791 } Err(err) => {
793 sleep_until(start + request_timeout).await;
795 retries += 1;
796 Err(ConsensusError::NetworkRequestTimeout(err.to_string()))
797 }
798 Ok(result) => result,
799 };
800 (resp, blocks_guard, retries, peer, fetch_after_rounds)
801 }
802
803 fn start_fetch_own_last_block_task(&mut self) {
804 const FETCH_OWN_BLOCK_RETRY_DELAY: Duration = Duration::from_millis(1_000);
805 const MAX_RETRY_DELAY_STEP: Duration = Duration::from_millis(4_000);
806
807 let context = self.context.clone();
808 let dag_state = self.dag_state.clone();
809 let network_client = self.network_client.clone();
810 let block_verifier = self.block_verifier.clone();
811 let core_dispatcher = self.core_dispatcher.clone();
812
813 self.fetch_own_last_block_task
814 .spawn(monitored_future!(async move {
815 let _scope = monitored_scope("FetchOwnLastBlockTask");
816
817 let fetch_own_block = |authority_index: AuthorityIndex, fetch_own_block_delay: Duration| {
818 let network_client_cloned = network_client.clone();
819 let own_index = context.own_index;
820 async move {
821 sleep(fetch_own_block_delay).await;
822 let r = network_client_cloned.fetch_latest_blocks(authority_index, vec![own_index], FETCH_REQUEST_TIMEOUT).await;
823 (r, authority_index)
824 }
825 };
826
827
828 let process_blocks = |blocks: Vec<Bytes>, authority_index: AuthorityIndex| -> ConsensusResult<Vec<VerifiedBlock>> {
829 let mut result = Vec::new();
830 for serialized_block in blocks {
831 let signed_block = bcs::from_bytes(&serialized_block).map_err(ConsensusError::MalformedBlock)?;
832 let (verified_block, _) = block_verifier.verify_and_vote(signed_block, serialized_block).tap_err(|err|{
833 let hostname = context.committee.authority(authority_index).hostname.clone();
834 context
835 .metrics
836 .node_metrics
837 .invalid_blocks
838 .with_label_values(&[hostname.as_str(), "synchronizer_own_block", err.clone().name()])
839 .inc();
840 warn!("Invalid block received from {}: {}", authority_index, err);
841 })?;
842
843 if verified_block.author() != context.own_index {
844 return Err(ConsensusError::UnexpectedLastOwnBlock { index: authority_index, block_ref: verified_block.reference()});
845 }
846 result.push(verified_block);
847 }
848 Ok(result)
849 };
850
851 let mut highest_round;
853 let mut retries = 0;
854 let mut retry_delay_step = Duration::from_millis(500);
855 'main:loop {
856 if context.committee.size() == 1 {
857 highest_round = dag_state.read().get_last_proposed_block().expect("Last proposed block should be returned on validators").round();
858 info!("Only one node in the network, will not try fetching own last block from peers.");
859 break 'main;
860 }
861
862 let mut total_stake = 0;
863 highest_round = 0;
864
865 let mut results = FuturesUnordered::new();
867
868 for (authority_index, _authority) in context.committee.authorities() {
869 if authority_index != context.own_index {
870 results.push(fetch_own_block(authority_index, Duration::from_millis(0)));
871 }
872 }
873
874 let timer = sleep_until(Instant::now() + context.parameters.sync_last_known_own_block_timeout);
876 tokio::pin!(timer);
877
878 'inner: loop {
879 tokio::select! {
880 result = results.next() => {
881 let Some((result, authority_index)) = result else {
882 break 'inner;
883 };
884 match result {
885 Ok(result) => {
886 match process_blocks(result, authority_index) {
887 Ok(blocks) => {
888 let max_round = blocks.into_iter().map(|b|b.round()).max().unwrap_or(0);
889 highest_round = highest_round.max(max_round);
890
891 total_stake += context.committee.stake(authority_index);
892 },
893 Err(err) => {
894 warn!("Invalid result returned from {authority_index} while fetching last own block: {err}");
895 }
896 }
897 },
898 Err(err) => {
899 warn!("Error {err} while fetching our own block from peer {authority_index}. Will retry.");
900 results.push(fetch_own_block(authority_index, FETCH_OWN_BLOCK_RETRY_DELAY));
901 }
902 }
903 },
904 () = &mut timer => {
905 info!("Timeout while trying to sync our own last block from peers");
906 break 'inner;
907 }
908 }
909 }
910
911 if context.committee.reached_validity(total_stake) {
913 info!("{} out of {} total stake returned acceptable results for our own last block with highest round {}, with {retries} retries.", total_stake, context.committee.total_stake(), highest_round);
914 break 'main;
915 }
916
917 retries += 1;
918 context.metrics.node_metrics.sync_last_known_own_block_retries.inc();
919 warn!("Not enough stake: {} out of {} total stake returned acceptable results for our own last block with highest round {}. Will now retry {retries}.", total_stake, context.committee.total_stake(), highest_round);
920
921 sleep(retry_delay_step).await;
922
923 retry_delay_step = Duration::from_secs_f64(retry_delay_step.as_secs_f64() * 1.5);
924 retry_delay_step = retry_delay_step.min(MAX_RETRY_DELAY_STEP);
925 }
926
927 context.metrics.node_metrics.last_known_own_block_round.set(highest_round as i64);
929
930 if let Err(err) = core_dispatcher.set_last_known_proposed_round(highest_round) {
931 warn!("Error received while calling dispatcher, probably dispatcher is shutting down, will now exit: {err:?}");
932 }
933 }));
934 }
935
936 async fn start_fetch_missing_blocks_task(&mut self) -> ConsensusResult<()> {
937 if self.context.committee.size() == 1 {
938 trace!(
939 "Only one node in the network, will not try fetching missing blocks from peers."
940 );
941 return Ok(());
942 }
943
944 if !self.should_run_periodic_sync() {
948 return Ok(());
949 }
950
951 let context = self.context.clone();
952 let network_client = self.network_client.clone();
953 let block_verifier = self.block_verifier.clone();
954 let transaction_vote_tracker = self.transaction_vote_tracker.clone();
955 let commit_vote_monitor = self.commit_vote_monitor.clone();
956 let core_dispatcher = self.core_dispatcher.clone();
957 let blocks_to_fetch = self.inflight_blocks_map.clone();
958 let commands_sender = self.commands_sender.clone();
959 let dag_state = self.dag_state.clone();
960 let round_tracker = self.round_tracker.clone();
961 let peers_pool = self.peers_pool.clone();
962
963 let mut missing_blocks = self
964 .core_dispatcher
965 .get_missing_blocks()
966 .await
967 .map_err(|_err| ConsensusError::Shutdown)?;
968 if self.commit_sync_failover {
969 let fetch_after_rounds = Self::get_fetch_after_rounds(&context, dag_state.clone());
972 missing_blocks.retain(|block| block.round <= fetch_after_rounds[block.author.value()]);
973 } else if missing_blocks.is_empty() {
974 return Ok(());
975 }
976
977 self.fetch_blocks_scheduler_task
978 .spawn(monitored_future!(async move {
979 let _scope = monitored_scope("FetchMissingBlocksScheduler");
980 context
981 .metrics
982 .node_metrics
983 .fetch_blocks_scheduler_inflight
984 .inc();
985 let total_requested = missing_blocks.len();
986
987 let results = if missing_blocks.is_empty() {
988 let _scope = monitored_scope("BlockSync::Periodic::HighestAcceptedRounds");
989 Self::fetch_blocks_with_fetch_after_rounds(
991 context.clone(),
992 blocks_to_fetch.clone(),
993 network_client,
994 dag_state,
995 peers_pool,
996 )
997 .await
998 } else {
999 let _scope = monitored_scope("BlockSync::Periodic::MissingBlocks");
1000 Self::fetch_blocks_from_peers(
1002 context.clone(),
1003 blocks_to_fetch.clone(),
1004 network_client,
1005 missing_blocks,
1006 dag_state,
1007 peers_pool,
1008 )
1009 .await
1010 };
1011 context
1012 .metrics
1013 .node_metrics
1014 .fetch_blocks_scheduler_inflight
1015 .dec();
1016 if results.is_empty() {
1017 return;
1018 }
1019
1020 fail_point_async!("consensus-delay");
1021
1022 let mut total_fetched = 0;
1024 for (blocks_guard, fetched_blocks, peer) in results {
1025 total_fetched += fetched_blocks.len();
1026
1027 if let Err(err) = Self::process_fetched_blocks(
1028 fetched_blocks,
1029 peer.clone(),
1030 blocks_guard,
1031 core_dispatcher.clone(),
1032 block_verifier.clone(),
1033 transaction_vote_tracker.clone(),
1034 commit_vote_monitor.clone(),
1035 context.clone(),
1036 commands_sender.clone(),
1037 round_tracker.clone(),
1038 "periodic",
1039 )
1040 .await
1041 {
1042 warn!(
1043 "Error occurred while processing fetched blocks from peer {:?}: {err}",
1044 peer
1045 );
1046 let peer_name = peer.labelname(&context);
1047 context
1048 .metrics
1049 .node_metrics
1050 .synchronizer_process_fetched_failures
1051 .with_label_values(&[peer_name.as_str(), "periodic"])
1052 .inc();
1053 }
1054 }
1055
1056 debug!(
1057 "Total blocks requested to fetch: {}, total fetched: {}",
1058 total_requested, total_fetched
1059 );
1060 }));
1061
1062 Ok(())
1063 }
1064
1065 fn should_run_periodic_sync(&mut self) -> bool {
1066 let current_commit_index = self.dag_state.read().last_commit_index();
1067 let quorum_commit_index = self.commit_vote_monitor.quorum_commit_index();
1068 let now = Instant::now();
1069 let metrics = &self.context.metrics.node_metrics;
1070
1071 if !is_commit_lagging(
1073 self.context.as_ref(),
1074 current_commit_index,
1075 quorum_commit_index,
1076 ) {
1077 metrics
1078 .synchronizer_periodic_sync_decision
1079 .with_label_values(&["true", "default"])
1080 .inc();
1081 self.last_changed_commit_index = current_commit_index;
1083 self.last_commit_change_time = now;
1084 self.commit_sync_failover = false;
1085 return true;
1087 }
1088 if self.commit_sync_failover {
1092 if current_commit_index
1093 < self.last_changed_commit_index + self.context.parameters.commit_sync_batch_size
1094 {
1095 metrics
1096 .synchronizer_periodic_sync_decision
1097 .with_label_values(&["true", "commit_catchup::run"])
1098 .inc();
1099 return true;
1103 } else {
1104 metrics
1105 .synchronizer_periodic_sync_decision
1106 .with_label_values(&["false", "commit_catchup::end"])
1107 .inc();
1108 self.last_changed_commit_index = current_commit_index;
1110 self.last_commit_change_time = now;
1111 self.commit_sync_failover = false;
1112 return false;
1114 }
1115 }
1116
1117 if current_commit_index == self.last_changed_commit_index {
1119 if now.duration_since(self.last_commit_change_time) >= COMMIT_PROGRESS_TIMEOUT {
1121 metrics
1122 .synchronizer_periodic_sync_decision
1123 .with_label_values(&["true", "commit_catchup::start"])
1124 .inc();
1125 self.commit_sync_failover = true;
1126 return true;
1128 }
1129 } else {
1130 self.last_changed_commit_index = current_commit_index;
1132 self.last_commit_change_time = now;
1133 }
1134
1135 metrics
1136 .synchronizer_periodic_sync_decision
1137 .with_label_values(&["false", "commit_lag"])
1138 .inc();
1139 false
1140 }
1141
1142 async fn fetch_blocks_with_fetch_after_rounds(
1145 context: Arc<Context>,
1146 inflight_blocks: Arc<InflightBlocksMap>,
1147 network_client: Arc<SynchronizerClient<VC, OC>>,
1148 dag_state: Arc<RwLock<DagState>>,
1149 peers_pool: Arc<PeersPool>,
1150 ) -> Vec<(BlocksGuard, Vec<Bytes>, PeerId)> {
1151 let fetch_after_rounds = Self::get_fetch_after_rounds(&context, dag_state.clone());
1152
1153 let mut peers = peers_pool.get_known_peers();
1156
1157 assert!(!peers.is_empty(), "No known peers to fetch blocks from");
1160
1161 if cfg!(not(test)) {
1162 peers.shuffle(&mut ThreadRng::default());
1163 }
1164
1165 let peer = peers.first().unwrap().clone();
1166
1167 let response = timeout(
1168 FETCH_REQUEST_TIMEOUT,
1169 network_client.fetch_blocks(
1170 peer.clone(),
1171 vec![],
1172 fetch_after_rounds,
1173 false,
1174 FETCH_REQUEST_TIMEOUT,
1175 ),
1176 )
1177 .await;
1178
1179 let serialized_blocks = match response {
1180 Ok(Ok(blocks)) => blocks,
1181 Ok(Err(err)) => {
1182 debug!("Failed to fetch blocks with fetch_after_rounds from peer {peer}: {err}");
1183 return vec![];
1184 }
1185 Err(_) => {
1186 debug!("Timed out fetching blocks with fetch_after_rounds from peer {peer}");
1187 return vec![];
1188 }
1189 };
1190
1191 let blocks_guard = BlocksGuard {
1192 map: inflight_blocks,
1193 block_refs: BTreeSet::new(),
1194 peer: peer.clone(),
1195 };
1196
1197 vec![(blocks_guard, serialized_blocks, peer)]
1198 }
1199
1200 async fn fetch_blocks_from_peers(
1206 context: Arc<Context>,
1207 inflight_blocks: Arc<InflightBlocksMap>,
1208 network_client: Arc<SynchronizerClient<VC, OC>>,
1209 missing_blocks: BTreeSet<BlockRef>,
1210 dag_state: Arc<RwLock<DagState>>,
1211 peers_pool: Arc<PeersPool>,
1212 ) -> Vec<(BlocksGuard, Vec<Bytes>, PeerId)> {
1213 let missing_blocks = missing_blocks
1217 .into_iter()
1218 .take(2 * MAX_PERIODIC_SYNC_PEERS * context.parameters.max_blocks_per_fetch)
1219 .collect::<Vec<_>>();
1220
1221 let mut authorities = BTreeMap::<AuthorityIndex, Vec<BlockRef>>::new();
1223 for block_ref in &missing_blocks {
1224 authorities
1225 .entry(block_ref.author)
1226 .or_default()
1227 .push(*block_ref);
1228 }
1229
1230 let mut peers = peers_pool.get_known_peers();
1232
1233 assert!(!peers.is_empty(), "No known peers to fetch blocks from");
1238
1239 let num_authorities_per_peer = authorities
1240 .len()
1241 .div_ceil(peers.len().min(MAX_PERIODIC_SYNC_PEERS));
1242
1243 let mut missing_blocks_per_authority = vec![0; context.committee.size()];
1245 for (authority, blocks) in &authorities {
1246 missing_blocks_per_authority[*authority] += blocks.len();
1247 }
1248 for (missing, (_, authority)) in missing_blocks_per_authority
1249 .into_iter()
1250 .zip_debug_eq(context.committee.authorities())
1251 {
1252 context
1253 .metrics
1254 .node_metrics
1255 .synchronizer_missing_blocks_by_authority
1256 .with_label_values(&[&authority.hostname])
1257 .inc_by(missing as u64);
1258 context
1259 .metrics
1260 .node_metrics
1261 .synchronizer_current_missing_blocks_by_authority
1262 .with_label_values(&[&authority.hostname])
1263 .set(missing as i64);
1264 }
1265
1266 if cfg!(not(test)) {
1268 peers.shuffle(&mut ThreadRng::default());
1270 }
1271
1272 let mut peers = peers.into_iter();
1273 let mut request_futures = FuturesUnordered::new();
1274
1275 let mut authorities = authorities.into_values().collect::<Vec<_>>();
1277 if cfg!(not(test)) {
1278 authorities.shuffle(&mut ThreadRng::default());
1280 }
1281
1282 let fetch_after_rounds = Self::get_fetch_after_rounds(&context, dag_state.clone());
1283
1284 for batch in authorities.chunks(num_authorities_per_peer) {
1286 let Some(peer) = peers.next() else {
1287 debug_fatal!("No more peers left to fetch blocks!");
1288 break;
1289 };
1290 let peer_name = peer.hostname(&context);
1291 let block_refs = batch
1294 .iter()
1295 .flatten()
1296 .cloned()
1297 .collect::<BTreeSet<_>>()
1298 .into_iter()
1299 .take(context.parameters.max_blocks_per_fetch)
1300 .collect::<BTreeSet<_>>();
1301
1302 if let Some(blocks_guard) =
1304 inflight_blocks.lock_blocks(block_refs.clone(), peer.clone())
1305 {
1306 info!(
1307 "Periodic sync of {} missing blocks from peer {} {:?}: {}",
1308 peer_name.as_str(),
1309 block_refs.len(),
1310 peer,
1311 block_refs
1312 .iter()
1313 .map(|b| b.to_string())
1314 .collect::<Vec<_>>()
1315 .join(", ")
1316 );
1317 request_futures.push(Self::fetch_blocks_request(
1318 network_client.clone(),
1319 peer,
1320 blocks_guard,
1321 fetch_after_rounds.clone(),
1322 false,
1323 FETCH_REQUEST_TIMEOUT,
1324 1,
1325 ));
1326 }
1327 }
1328
1329 let mut results = Vec::new();
1330 let fetcher_timeout = sleep(FETCH_FROM_PEERS_TIMEOUT);
1331
1332 tokio::pin!(fetcher_timeout);
1333
1334 loop {
1335 tokio::select! {
1336 Some((response, blocks_guard, _retries, peer, fetch_after_rounds)) = request_futures.next() => {
1337 match response {
1338 Ok(fetched_blocks) => {
1339 results.push((blocks_guard, fetched_blocks, peer));
1340
1341 if request_futures.is_empty() {
1343 break;
1344 }
1345 },
1346 Err(_) => {
1347 let peer_name = peer.labelname(&context);
1348 context.metrics.node_metrics.synchronizer_fetch_failures.with_label_values(&[peer_name.as_str(), "periodic"]).inc();
1349 if let Some(next_peer) = peers.next() {
1351 if let Some(blocks_guard) = inflight_blocks.swap_locks(blocks_guard, next_peer.clone()) {
1353 info!(
1354 "Retrying syncing {} missing blocks from peer {:?}: {}",
1355 blocks_guard.block_refs.len(),
1356 next_peer,
1357 blocks_guard.block_refs
1358 .iter()
1359 .map(|b| b.to_string())
1360 .collect::<Vec<_>>()
1361 .join(", ")
1362 );
1363 request_futures.push(Self::fetch_blocks_request(
1364 network_client.clone(),
1365 next_peer,
1366 blocks_guard,
1367 fetch_after_rounds,
1368 false,
1369 FETCH_REQUEST_TIMEOUT,
1370 1,
1371 ));
1372 } else {
1373 debug!("Couldn't acquire locks to fetch blocks from peer {:?}.", next_peer)
1374 }
1375 } else {
1376 debug!("No more peers left to fetch blocks");
1377 }
1378 }
1379 }
1380 },
1381 _ = &mut fetcher_timeout => {
1382 debug!("Timed out while fetching missing blocks");
1383 break;
1384 }
1385 }
1386 }
1387
1388 results
1389 }
1390}
1391
1392#[cfg(test)]
1393mod tests {
1394 use std::{
1395 collections::{BTreeMap, BTreeSet},
1396 sync::Arc,
1397 time::Duration,
1398 };
1399
1400 use async_trait::async_trait;
1401 use bytes::Bytes;
1402 use consensus_config::{AuthorityIndex, Parameters};
1403 use consensus_types::block::{BlockDigest, BlockRef, Round};
1404 use mysten_metrics::monitored_mpsc;
1405 use parking_lot::RwLock;
1406 use tokio::{sync::Mutex, time::sleep};
1407
1408 use crate::commit::{CommitVote, TrustedCommit};
1409 use crate::{
1410 CommitDigest, CommitIndex,
1411 block::{TestBlock, VerifiedBlock},
1412 block_verifier::NoopBlockVerifier,
1413 commit_vote_monitor::CommitVoteMonitor,
1414 context::Context,
1415 core_thread::CoreThreadDispatcher,
1416 dag_state::DagState,
1417 error::{ConsensusError, ConsensusResult},
1418 network::{
1419 BlockStream, ObserverNetworkClient, PeerId, SynchronizerClient, ValidatorNetworkClient,
1420 },
1421 storage::mem_store::MemStore,
1422 synchronizer::{
1423 COMMIT_PROGRESS_TIMEOUT, FETCH_BLOCKS_CONCURRENCY, FETCH_REQUEST_TIMEOUT,
1424 InflightBlocksMap, Synchronizer,
1425 },
1426 };
1427 use crate::{
1428 commit_vote_monitor::COMMIT_LAG_MULTIPLIER, core_thread::MockCoreThreadDispatcher,
1429 peers_pool::PeersPool, round_tracker::RoundTracker,
1430 transaction_vote_tracker::TransactionVoteTracker,
1431 };
1432
1433 type FetchRequestKey = (Vec<BlockRef>, AuthorityIndex);
1434 type FetchRequestResponse = (Vec<VerifiedBlock>, Option<Duration>);
1435 type FetchLatestBlockKey = (AuthorityIndex, Vec<AuthorityIndex>);
1436 type FetchLatestBlockResponse = (Vec<VerifiedBlock>, Option<Duration>);
1437
1438 #[derive(Default)]
1439 struct MockNetworkClient {
1440 fetch_blocks_requests: Mutex<BTreeMap<FetchRequestKey, FetchRequestResponse>>,
1441 fetch_latest_blocks_requests:
1442 Mutex<BTreeMap<FetchLatestBlockKey, Vec<FetchLatestBlockResponse>>>,
1443 }
1444
1445 impl MockNetworkClient {
1446 async fn stub_fetch_blocks(
1447 &self,
1448 blocks: Vec<VerifiedBlock>,
1449 peer: AuthorityIndex,
1450 latency: Option<Duration>,
1451 ) {
1452 let mut lock = self.fetch_blocks_requests.lock().await;
1453 let block_refs = blocks
1454 .iter()
1455 .map(|block| block.reference())
1456 .collect::<Vec<_>>();
1457 lock.insert((block_refs, peer), (blocks, latency));
1458 }
1459
1460 async fn stub_fetch_blocks_for_key(
1461 &self,
1462 key_refs: Vec<BlockRef>,
1463 response_blocks: Vec<VerifiedBlock>,
1464 peer: AuthorityIndex,
1465 latency: Option<Duration>,
1466 ) {
1467 let mut lock = self.fetch_blocks_requests.lock().await;
1468 lock.insert((key_refs, peer), (response_blocks, latency));
1469 }
1470
1471 async fn stub_fetch_latest_blocks(
1472 &self,
1473 blocks: Vec<VerifiedBlock>,
1474 peer: AuthorityIndex,
1475 authorities: Vec<AuthorityIndex>,
1476 latency: Option<Duration>,
1477 ) {
1478 let mut lock = self.fetch_latest_blocks_requests.lock().await;
1479 lock.entry((peer, authorities))
1480 .or_default()
1481 .push((blocks, latency));
1482 }
1483
1484 async fn fetch_latest_blocks_pending_calls(&self) -> usize {
1485 let lock = self.fetch_latest_blocks_requests.lock().await;
1486 lock.len()
1487 }
1488 }
1489
1490 #[async_trait]
1491 impl ValidatorNetworkClient for MockNetworkClient {
1492 async fn subscribe_blocks(
1493 &self,
1494 _peer: AuthorityIndex,
1495 _last_received: Round,
1496 _timeout: Duration,
1497 ) -> ConsensusResult<BlockStream> {
1498 unimplemented!("subscribe_blocks not implemented in mock")
1499 }
1500
1501 async fn fetch_blocks(
1502 &self,
1503 peer: AuthorityIndex,
1504 block_refs: Vec<BlockRef>,
1505 _fetch_after_rounds: Vec<Round>,
1506 _fetch_missing_ancestors: bool,
1507 _timeout: Duration,
1508 ) -> ConsensusResult<Vec<Bytes>> {
1509 let mut lock = self.fetch_blocks_requests.lock().await;
1510 let response = lock.remove(&(block_refs.clone(), peer)).unwrap_or_else(|| {
1511 panic!(
1512 "Unexpected fetch blocks request made: {:?} {}. Current lock: {:?}",
1513 block_refs, peer, lock
1514 );
1515 });
1516
1517 let serialised = response
1518 .0
1519 .into_iter()
1520 .map(|block| block.serialized().clone())
1521 .collect::<Vec<_>>();
1522
1523 drop(lock);
1524
1525 if let Some(latency) = response.1 {
1526 sleep(latency).await;
1527 }
1528
1529 Ok(serialised)
1530 }
1531
1532 async fn fetch_commits(
1533 &self,
1534 _peer: AuthorityIndex,
1535 _commit_range: crate::commit::CommitRange,
1536 _timeout: Duration,
1537 ) -> ConsensusResult<(Vec<Bytes>, Vec<Bytes>)> {
1538 unimplemented!("fetch_commits not implemented in mock")
1539 }
1540
1541 async fn fetch_latest_blocks(
1542 &self,
1543 peer: AuthorityIndex,
1544 authorities: Vec<AuthorityIndex>,
1545 _timeout: Duration,
1546 ) -> ConsensusResult<Vec<Bytes>> {
1547 let mut lock = self.fetch_latest_blocks_requests.lock().await;
1548 let mut responses = lock
1549 .remove(&(peer, authorities.clone()))
1550 .expect("Unexpected fetch blocks request made");
1551
1552 let response = responses.remove(0);
1553 let serialised = response
1554 .0
1555 .into_iter()
1556 .map(|block| block.serialized().clone())
1557 .collect::<Vec<_>>();
1558
1559 if !responses.is_empty() {
1560 lock.insert((peer, authorities), responses);
1561 }
1562
1563 drop(lock);
1564
1565 if let Some(latency) = response.1 {
1566 sleep(latency).await;
1567 }
1568
1569 Ok(serialised)
1570 }
1571
1572 async fn get_latest_rounds(
1573 &self,
1574 _peer: AuthorityIndex,
1575 _timeout: Duration,
1576 ) -> ConsensusResult<(Vec<Round>, Vec<Round>)> {
1577 unimplemented!("get_latest_rounds not implemented in mock")
1578 }
1579
1580 #[cfg(test)]
1581 async fn send_block(
1582 &self,
1583 _peer: AuthorityIndex,
1584 _block: &VerifiedBlock,
1585 _timeout: Duration,
1586 ) -> ConsensusResult<()> {
1587 unimplemented!("send_block not implemented in mock")
1588 }
1589 }
1590
1591 #[async_trait]
1592 impl ObserverNetworkClient for MockNetworkClient {
1593 async fn stream_blocks(
1594 &self,
1595 _peer: crate::network::PeerId,
1596 _highest_round_per_authority: Vec<Round>,
1597 _timeout: Duration,
1598 ) -> ConsensusResult<crate::network::ObserverBlockStream> {
1599 unimplemented!("stream_blocks not implemented in mock")
1600 }
1601
1602 async fn fetch_blocks(
1603 &self,
1604 _peer: crate::network::PeerId,
1605 _block_refs: Vec<BlockRef>,
1606 _fetch_after_rounds: Vec<Round>,
1607 _fetch_missing_ancestors: bool,
1608 _timeout: Duration,
1609 ) -> ConsensusResult<Vec<Bytes>> {
1610 unimplemented!("Observer fetch_blocks not implemented in mock")
1611 }
1612
1613 async fn fetch_commits(
1614 &self,
1615 _peer: crate::network::PeerId,
1616 _commit_range: crate::commit::CommitRange,
1617 _timeout: Duration,
1618 ) -> ConsensusResult<(Vec<Bytes>, Vec<Bytes>)> {
1619 unimplemented!("Observer fetch_commits not implemented in mock")
1620 }
1621 }
1622
1623 #[test]
1624 fn test_inflight_blocks_map() {
1625 let map = InflightBlocksMap::new();
1627 let some_block_refs = [
1628 BlockRef::new(1, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
1629 BlockRef::new(10, AuthorityIndex::new_for_test(0), BlockDigest::MIN),
1630 BlockRef::new(12, AuthorityIndex::new_for_test(3), BlockDigest::MIN),
1631 BlockRef::new(15, AuthorityIndex::new_for_test(2), BlockDigest::MIN),
1632 ];
1633 let missing_block_refs = some_block_refs.iter().cloned().collect::<BTreeSet<_>>();
1634
1635 {
1637 let mut all_guards = Vec::new();
1638
1639 for i in 1..=2 {
1641 let authority = AuthorityIndex::new_for_test(i);
1642 let peer = PeerId::Validator(authority);
1643
1644 let guard = map.lock_blocks(missing_block_refs.clone(), peer.clone());
1645 let guard = guard.expect("Guard should be created");
1646 assert_eq!(guard.block_refs.len(), 4);
1647
1648 all_guards.push(guard);
1649
1650 let guard = map.lock_blocks(missing_block_refs.clone(), peer);
1652 assert!(guard.is_none());
1653 }
1654
1655 let authority_3 = AuthorityIndex::new_for_test(3);
1657 let peer_3 = PeerId::Validator(authority_3);
1658
1659 let guard = map.lock_blocks(missing_block_refs.clone(), peer_3.clone());
1660 assert!(guard.is_none());
1661
1662 drop(all_guards.remove(0));
1664
1665 let guard = map.lock_blocks(missing_block_refs.clone(), peer_3);
1666 let guard = guard.expect("Guard should be successfully acquired");
1667
1668 assert_eq!(guard.block_refs, missing_block_refs);
1669
1670 drop(guard);
1672 drop(all_guards);
1673
1674 assert_eq!(map.num_of_locked_blocks(), 0);
1675 }
1676
1677 {
1679 let authority_1 = AuthorityIndex::new_for_test(1);
1681 let peer_1 = PeerId::Validator(authority_1);
1682 let guard = map.lock_blocks(missing_block_refs.clone(), peer_1).unwrap();
1683
1684 let authority_2 = AuthorityIndex::new_for_test(2);
1686 let peer_2 = PeerId::Validator(authority_2);
1687 let guard = map.swap_locks(guard, peer_2);
1688
1689 assert_eq!(guard.unwrap().block_refs, missing_block_refs);
1690 }
1691 }
1692
1693 #[tokio::test]
1694 async fn successful_fetch_blocks_from_peer() {
1695 let (context, _) = Context::new_for_test(4);
1697 let context = Arc::new(context);
1698 let block_verifier = Arc::new(NoopBlockVerifier {});
1699 let core_dispatcher = Arc::new(MockCoreThreadDispatcher::default());
1700 let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone()));
1701 let mock_client = Arc::new(MockNetworkClient::default());
1702 let store = Arc::new(MemStore::new());
1703 let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store)));
1704 let transaction_vote_tracker =
1705 TransactionVoteTracker::new(context.clone(), block_verifier.clone(), dag_state.clone());
1706 let round_tracker = Arc::new(RwLock::new(RoundTracker::new(context.clone(), vec![])));
1707
1708 let network_client = Arc::new(SynchronizerClient::new(
1709 context.clone(),
1710 Some(mock_client.clone()),
1711 Some(mock_client.clone()),
1712 ));
1713 let peers_pool = Arc::new(PeersPool::new(context.clone()));
1714 let handle = Synchronizer::start(
1715 network_client,
1716 context.clone(),
1717 core_dispatcher.clone(),
1718 commit_vote_monitor,
1719 block_verifier,
1720 transaction_vote_tracker,
1721 round_tracker,
1722 dag_state,
1723 peers_pool.clone(),
1724 false,
1725 );
1726
1727 let expected_blocks = (0..10)
1729 .map(|round| VerifiedBlock::new_for_test(TestBlock::new(round, 0).build()))
1730 .collect::<Vec<_>>();
1731 let missing_blocks = expected_blocks
1732 .iter()
1733 .map(|block| block.reference())
1734 .collect::<BTreeSet<_>>();
1735
1736 let peer = AuthorityIndex::new_for_test(1);
1738 mock_client
1739 .stub_fetch_blocks(expected_blocks.clone(), peer, None)
1740 .await;
1741
1742 assert!(
1744 handle
1745 .fetch_blocks(missing_blocks, PeerId::Validator(peer))
1746 .await
1747 .is_ok()
1748 );
1749
1750 sleep(Duration::from_millis(1_000)).await;
1752
1753 let added_blocks = core_dispatcher.get_add_blocks().await;
1755 assert_eq!(added_blocks, expected_blocks);
1756 }
1757
1758 #[tokio::test]
1759 async fn saturate_fetch_blocks_from_peer() {
1760 let (context, _) = Context::new_for_test(4);
1762 let context = Arc::new(context);
1763 let block_verifier = Arc::new(NoopBlockVerifier {});
1764 let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone()));
1765 let core_dispatcher = Arc::new(MockCoreThreadDispatcher::default());
1766 let mock_client = Arc::new(MockNetworkClient::default());
1767 let store = Arc::new(MemStore::new());
1768 let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store)));
1769 let transaction_vote_tracker =
1770 TransactionVoteTracker::new(context.clone(), block_verifier.clone(), dag_state.clone());
1771 let round_tracker = Arc::new(RwLock::new(RoundTracker::new(context.clone(), vec![])));
1772
1773 let network_client = Arc::new(SynchronizerClient::new(
1774 context.clone(),
1775 Some(mock_client.clone()),
1776 Some(mock_client.clone()),
1777 ));
1778 let peers_pool = Arc::new(PeersPool::new(context.clone()));
1779 let handle = Synchronizer::start(
1780 network_client,
1781 context.clone(),
1782 core_dispatcher.clone(),
1783 commit_vote_monitor,
1784 block_verifier,
1785 transaction_vote_tracker,
1786 round_tracker,
1787 dag_state,
1788 peers_pool.clone(),
1789 false,
1790 );
1791
1792 let expected_blocks = (0..=2 * FETCH_BLOCKS_CONCURRENCY)
1794 .map(|round| VerifiedBlock::new_for_test(TestBlock::new(round as Round, 0).build()))
1795 .collect::<Vec<_>>();
1796
1797 let peer = AuthorityIndex::new_for_test(1);
1799 let mut iter = expected_blocks.iter().peekable();
1800 while let Some(block) = iter.next() {
1801 mock_client
1804 .stub_fetch_blocks(
1805 vec![block.clone()],
1806 peer,
1807 Some(Duration::from_millis(5_000)),
1808 )
1809 .await;
1810
1811 let mut missing_blocks = BTreeSet::new();
1812 missing_blocks.insert(block.reference());
1813
1814 if iter.peek().is_none() {
1817 match handle
1818 .fetch_blocks(missing_blocks, PeerId::Validator(peer))
1819 .await
1820 {
1821 Err(ConsensusError::SynchronizerSaturated(peer_str)) => {
1822 assert_eq!(peer_str, format!("{:?}", PeerId::Validator(peer)));
1823 }
1824 _ => panic!("A saturated synchronizer error was expected"),
1825 }
1826 } else {
1827 assert!(
1828 handle
1829 .fetch_blocks(missing_blocks, PeerId::Validator(peer))
1830 .await
1831 .is_ok()
1832 );
1833 }
1834 }
1835 }
1836
1837 #[tokio::test(flavor = "current_thread", start_paused = true)]
1838 async fn synchronizer_periodic_task_fetch_blocks() {
1839 let (context, _) = Context::new_for_test(4);
1841 let context = Arc::new(context);
1842 let block_verifier = Arc::new(NoopBlockVerifier {});
1843 let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone()));
1844 let core_dispatcher = Arc::new(MockCoreThreadDispatcher::default());
1845 let mock_client = Arc::new(MockNetworkClient::default());
1846 let store = Arc::new(MemStore::new());
1847 let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store)));
1848 let transaction_vote_tracker =
1849 TransactionVoteTracker::new(context.clone(), block_verifier.clone(), dag_state.clone());
1850 let round_tracker = Arc::new(RwLock::new(RoundTracker::new(context.clone(), vec![])));
1851
1852 let expected_blocks = (0..10)
1854 .map(|round| VerifiedBlock::new_for_test(TestBlock::new(round, 0).build()))
1855 .collect::<Vec<_>>();
1856 let missing_blocks = expected_blocks
1857 .iter()
1858 .map(|block| block.reference())
1859 .collect::<BTreeSet<_>>();
1860
1861 core_dispatcher
1863 .stub_missing_blocks(missing_blocks.clone())
1864 .await;
1865
1866 mock_client
1870 .stub_fetch_blocks(
1871 expected_blocks.clone(),
1872 AuthorityIndex::new_for_test(1),
1873 Some(FETCH_REQUEST_TIMEOUT),
1874 )
1875 .await;
1876 mock_client
1877 .stub_fetch_blocks(
1878 expected_blocks.clone(),
1879 AuthorityIndex::new_for_test(2),
1880 None,
1881 )
1882 .await;
1883
1884 let network_client = Arc::new(SynchronizerClient::new(
1886 context.clone(),
1887 Some(mock_client.clone()),
1888 Some(mock_client.clone()),
1889 ));
1890 let peers_pool = Arc::new(PeersPool::new(context.clone()));
1891 let _handle = Synchronizer::start(
1892 network_client,
1893 context.clone(),
1894 core_dispatcher.clone(),
1895 commit_vote_monitor,
1896 block_verifier,
1897 transaction_vote_tracker,
1898 round_tracker,
1899 dag_state,
1900 peers_pool.clone(),
1901 false,
1902 );
1903
1904 sleep(2 * FETCH_REQUEST_TIMEOUT).await;
1905
1906 let added_blocks = core_dispatcher.get_add_blocks().await;
1908 assert_eq!(added_blocks, expected_blocks);
1909
1910 assert!(
1912 core_dispatcher
1913 .get_missing_blocks()
1914 .await
1915 .unwrap()
1916 .is_empty()
1917 );
1918 }
1919
1920 #[tokio::test(flavor = "current_thread", start_paused = true)]
1921 async fn synchronizer_periodic_task_when_commit_lagging_gets_disabled() {
1922 let (context, _) = Context::new_for_test(4);
1924 let context = Arc::new(context);
1925 let block_verifier = Arc::new(NoopBlockVerifier {});
1926 let core_dispatcher = Arc::new(MockCoreThreadDispatcher::default());
1927 let mock_client = Arc::new(MockNetworkClient::default());
1928 let store = Arc::new(MemStore::new());
1929 let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store)));
1930 let transaction_vote_tracker =
1931 TransactionVoteTracker::new(context.clone(), block_verifier.clone(), dag_state.clone());
1932 let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone()));
1933 let round_tracker = Arc::new(RwLock::new(RoundTracker::new(context.clone(), vec![])));
1934
1935 let sync_missing_block_round_threshold = context.parameters.commit_sync_batch_size;
1937 let stub_blocks = (sync_missing_block_round_threshold * 2
1938 ..sync_missing_block_round_threshold * 3)
1939 .map(|round| VerifiedBlock::new_for_test(TestBlock::new(round, 0).build()))
1940 .collect::<Vec<_>>();
1941 let missing_blocks = stub_blocks
1942 .iter()
1943 .map(|block| block.reference())
1944 .collect::<BTreeSet<_>>();
1945 core_dispatcher
1946 .stub_missing_blocks(missing_blocks.clone())
1947 .await;
1948
1949 mock_client
1954 .stub_fetch_blocks(
1955 stub_blocks.clone(),
1956 AuthorityIndex::new_for_test(1),
1957 Some(FETCH_REQUEST_TIMEOUT),
1958 )
1959 .await;
1960 mock_client
1961 .stub_fetch_blocks(stub_blocks.clone(), AuthorityIndex::new_for_test(2), None)
1962 .await;
1963 let mut expected_blocks = stub_blocks
1964 .iter()
1965 .take(context.parameters.max_blocks_per_sync)
1966 .cloned()
1967 .collect::<Vec<_>>();
1968
1969 let round = context.parameters.commit_sync_batch_size * COMMIT_LAG_MULTIPLIER * 2;
1971 let commit_index: CommitIndex = round - 1;
1972 let blocks = (0..4)
1973 .map(|authority| {
1974 let commit_votes = vec![CommitVote::new(commit_index, CommitDigest::MIN)];
1975 let block = TestBlock::new(round, authority)
1976 .set_commit_votes(commit_votes)
1977 .build();
1978
1979 VerifiedBlock::new_for_test(block)
1980 })
1981 .collect::<Vec<_>>();
1982
1983 for block in blocks {
1986 commit_vote_monitor.observe_block(&block);
1987 }
1988
1989 let network_client = Arc::new(SynchronizerClient::new(
1991 context.clone(),
1992 Some(mock_client.clone()),
1993 Some(mock_client.clone()),
1994 ));
1995 let peers_pool = Arc::new(PeersPool::new(context.clone()));
1996 let _handle = Synchronizer::start(
1997 network_client,
1998 context.clone(),
1999 core_dispatcher.clone(),
2000 commit_vote_monitor.clone(),
2001 block_verifier,
2002 transaction_vote_tracker,
2003 round_tracker,
2004 dag_state.clone(),
2005 peers_pool.clone(),
2006 false,
2007 );
2008
2009 sleep(COMMIT_PROGRESS_TIMEOUT / 2).await;
2012
2013 let added_blocks = core_dispatcher.get_add_blocks().await;
2016 assert_eq!(added_blocks, vec![]);
2017
2018 {
2021 let mut d = dag_state.write();
2022 for index in 1..=commit_index {
2023 let commit =
2024 TrustedCommit::new_for_test(index, CommitDigest::MIN, 0, BlockRef::MIN, vec![]);
2025
2026 d.add_commit(commit);
2027 }
2028
2029 assert_eq!(
2030 d.last_commit_index(),
2031 commit_vote_monitor.quorum_commit_index()
2032 );
2033 }
2034
2035 core_dispatcher
2037 .stub_missing_blocks(missing_blocks.clone())
2038 .await;
2039
2040 sleep(2 * FETCH_REQUEST_TIMEOUT).await;
2041
2042 let mut added_blocks = core_dispatcher.get_add_blocks().await;
2044
2045 added_blocks.sort_by_key(|block| block.reference());
2046 expected_blocks.sort_by_key(|block| block.reference());
2047
2048 assert_eq!(added_blocks, expected_blocks);
2049 }
2050
2051 #[tokio::test(flavor = "current_thread", start_paused = true)]
2052 async fn synchronizer_periodic_sync_resumes_when_commit_sync_stalled() {
2053 let (context, _) = Context::new_for_test(4);
2055 let context = Arc::new(context);
2056 let block_verifier = Arc::new(NoopBlockVerifier {});
2057 let core_dispatcher = Arc::new(MockCoreThreadDispatcher::default());
2058 let mock_client = Arc::new(MockNetworkClient::default());
2059 let store = Arc::new(MemStore::new());
2060 let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store)));
2061 let transaction_vote_tracker =
2062 TransactionVoteTracker::new(context.clone(), block_verifier.clone(), dag_state.clone());
2063 let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone()));
2064 let round_tracker = Arc::new(RwLock::new(RoundTracker::new(context.clone(), vec![])));
2065
2066 let sync_missing_block_round_threshold = context.parameters.commit_sync_batch_size;
2068 let stub_blocks = (sync_missing_block_round_threshold * 2
2069 ..sync_missing_block_round_threshold * 3)
2070 .map(|round| VerifiedBlock::new_for_test(TestBlock::new(round, 0).build()))
2071 .collect::<Vec<_>>();
2072 let missing_blocks = stub_blocks
2073 .iter()
2074 .map(|block| block.reference())
2075 .collect::<BTreeSet<_>>();
2076 core_dispatcher
2077 .stub_missing_blocks(missing_blocks.clone())
2078 .await;
2079
2080 mock_client
2083 .stub_fetch_blocks(
2084 stub_blocks.clone(),
2085 AuthorityIndex::new_for_test(1),
2086 Some(FETCH_REQUEST_TIMEOUT),
2087 )
2088 .await;
2089 mock_client
2090 .stub_fetch_blocks(stub_blocks.clone(), AuthorityIndex::new_for_test(2), None)
2091 .await;
2092 let mut expected_blocks = stub_blocks
2093 .iter()
2094 .take(context.parameters.max_blocks_per_sync)
2095 .cloned()
2096 .collect::<Vec<_>>();
2097
2098 let round = context.parameters.commit_sync_batch_size * COMMIT_LAG_MULTIPLIER * 2;
2100 let commit_index: CommitIndex = round - 1;
2101 let blocks = (0..4)
2102 .map(|authority| {
2103 let commit_votes = vec![CommitVote::new(commit_index, CommitDigest::MIN)];
2104 let block = TestBlock::new(round, authority)
2105 .set_commit_votes(commit_votes)
2106 .build();
2107 VerifiedBlock::new_for_test(block)
2108 })
2109 .collect::<Vec<_>>();
2110 for block in blocks {
2111 commit_vote_monitor.observe_block(&block);
2112 }
2113
2114 let network_client = Arc::new(SynchronizerClient::new(
2116 context.clone(),
2117 Some(mock_client.clone()),
2118 Some(mock_client.clone()),
2119 ));
2120 let peers_pool = Arc::new(PeersPool::new(context.clone()));
2121 let _handle = Synchronizer::start(
2122 network_client,
2123 context.clone(),
2124 core_dispatcher.clone(),
2125 commit_vote_monitor.clone(),
2126 block_verifier,
2127 transaction_vote_tracker,
2128 round_tracker,
2129 dag_state.clone(),
2130 peers_pool.clone(),
2131 false,
2132 );
2133
2134 sleep(COMMIT_PROGRESS_TIMEOUT - Duration::from_millis(100)).await;
2136 let added_blocks = core_dispatcher.get_add_blocks().await;
2137 assert_eq!(added_blocks, vec![]);
2138
2139 core_dispatcher
2141 .stub_missing_blocks(missing_blocks.clone())
2142 .await;
2143 mock_client
2146 .stub_fetch_blocks_for_key(
2147 vec![],
2148 expected_blocks.clone(),
2149 AuthorityIndex::new_for_test(1),
2150 Some(Duration::from_millis(500)),
2151 )
2152 .await;
2153
2154 sleep(Duration::from_millis(200) + FETCH_REQUEST_TIMEOUT).await;
2156
2157 let mut added_blocks = core_dispatcher.get_add_blocks().await;
2158 assert!(
2159 !added_blocks.is_empty(),
2160 "Expected periodic sync to resume after commit sync stall"
2161 );
2162 added_blocks.sort_by_key(|block| block.reference());
2163 expected_blocks.sort_by_key(|block| block.reference());
2164 assert_eq!(added_blocks, expected_blocks);
2165
2166 core_dispatcher.get_add_blocks().await;
2169 {
2170 let current = dag_state.read().last_commit_index();
2171 let mut d = dag_state.write();
2172 for index in (current + 1)..=(current + context.parameters.commit_sync_batch_size) {
2174 let commit =
2175 TrustedCommit::new_for_test(index, CommitDigest::MIN, 0, BlockRef::MIN, vec![]);
2176 d.add_commit(commit);
2177 }
2178 }
2179
2180 core_dispatcher
2182 .stub_missing_blocks(missing_blocks.clone())
2183 .await;
2184 mock_client
2185 .stub_fetch_blocks(
2186 stub_blocks
2187 .iter()
2188 .take(context.parameters.max_blocks_per_sync)
2189 .cloned()
2190 .collect::<Vec<_>>(),
2191 AuthorityIndex::new_for_test(2),
2192 None,
2193 )
2194 .await;
2195
2196 sleep(2 * FETCH_REQUEST_TIMEOUT).await;
2199 let added_blocks = core_dispatcher.get_add_blocks().await;
2200 assert_eq!(
2201 added_blocks,
2202 vec![],
2203 "Expected periodic sync to be skipped after stall resolved"
2204 );
2205 }
2206
2207 #[tokio::test(flavor = "current_thread", start_paused = true)]
2208 async fn synchronizer_fetch_own_last_block() {
2209 let (context, _) = Context::new_for_test(4);
2211 let context = Arc::new(context.with_parameters(Parameters {
2212 sync_last_known_own_block_timeout: Duration::from_millis(2_000),
2213 ..Default::default()
2214 }));
2215 let block_verifier = Arc::new(NoopBlockVerifier {});
2216 let core_dispatcher = Arc::new(MockCoreThreadDispatcher::default());
2217 let mock_client = Arc::new(MockNetworkClient::default());
2218 let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone()));
2219 let store = Arc::new(MemStore::new());
2220 let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store)));
2221 let transaction_vote_tracker =
2222 TransactionVoteTracker::new(context.clone(), block_verifier.clone(), dag_state.clone());
2223 let round_tracker = Arc::new(RwLock::new(RoundTracker::new(context.clone(), vec![])));
2224 let our_index = AuthorityIndex::new_for_test(0);
2225
2226 let mut expected_blocks = (9..=10)
2228 .map(|round| VerifiedBlock::new_for_test(TestBlock::new(round, 0).build()))
2229 .collect::<Vec<_>>();
2230
2231 let block_1 = expected_blocks.pop().unwrap();
2234 mock_client
2235 .stub_fetch_latest_blocks(
2236 vec![block_1.clone()],
2237 AuthorityIndex::new_for_test(1),
2238 vec![our_index],
2239 None,
2240 )
2241 .await;
2242 mock_client
2243 .stub_fetch_latest_blocks(
2244 vec![block_1],
2245 AuthorityIndex::new_for_test(1),
2246 vec![our_index],
2247 None,
2248 )
2249 .await;
2250
2251 let block_2 = expected_blocks.pop().unwrap();
2253 mock_client
2254 .stub_fetch_latest_blocks(
2255 vec![block_2.clone()],
2256 AuthorityIndex::new_for_test(2),
2257 vec![our_index],
2258 Some(Duration::from_secs(10)),
2259 )
2260 .await;
2261 mock_client
2262 .stub_fetch_latest_blocks(
2263 vec![block_2],
2264 AuthorityIndex::new_for_test(2),
2265 vec![our_index],
2266 None,
2267 )
2268 .await;
2269
2270 mock_client
2272 .stub_fetch_latest_blocks(
2273 vec![],
2274 AuthorityIndex::new_for_test(3),
2275 vec![our_index],
2276 Some(Duration::from_secs(10)),
2277 )
2278 .await;
2279 mock_client
2280 .stub_fetch_latest_blocks(
2281 vec![],
2282 AuthorityIndex::new_for_test(3),
2283 vec![our_index],
2284 None,
2285 )
2286 .await;
2287
2288 let network_client = Arc::new(SynchronizerClient::new(
2290 context.clone(),
2291 Some(mock_client.clone()),
2292 Some(mock_client.clone()),
2293 ));
2294 let peers_pool = Arc::new(PeersPool::new(context.clone()));
2295 let handle = Synchronizer::start(
2296 network_client,
2297 context.clone(),
2298 core_dispatcher.clone(),
2299 commit_vote_monitor,
2300 block_verifier,
2301 transaction_vote_tracker,
2302 round_tracker,
2303 dag_state,
2304 peers_pool.clone(),
2305 true,
2306 );
2307
2308 sleep(context.parameters.sync_last_known_own_block_timeout * 2).await;
2310
2311 assert_eq!(
2313 core_dispatcher.get_last_own_proposed_round().await,
2314 vec![10]
2315 );
2316
2317 assert_eq!(mock_client.fetch_latest_blocks_pending_calls().await, 0);
2319
2320 assert_eq!(
2322 context
2323 .metrics
2324 .node_metrics
2325 .sync_last_known_own_block_retries
2326 .get(),
2327 1
2328 );
2329
2330 handle.stop().await;
2332 }
2333
2334 #[tokio::test]
2335 async fn test_process_fetched_blocks() {
2336 let (context, _) = Context::new_for_test(4);
2338 let context = Arc::new(context);
2339 let block_verifier = Arc::new(NoopBlockVerifier {});
2340 let core_dispatcher = Arc::new(MockCoreThreadDispatcher::default());
2341 let commit_vote_monitor = Arc::new(CommitVoteMonitor::new(context.clone()));
2342 let store = Arc::new(MemStore::new());
2343 let dag_state = Arc::new(RwLock::new(DagState::new(context.clone(), store)));
2344 let transaction_vote_tracker =
2345 TransactionVoteTracker::new(context.clone(), block_verifier.clone(), dag_state.clone());
2346 let round_tracker = Arc::new(RwLock::new(RoundTracker::new(context.clone(), vec![])));
2347 let (commands_sender, _commands_receiver) =
2348 monitored_mpsc::channel("consensus_synchronizer_commands", 1000);
2349
2350 let mut expected_blocks = vec![VerifiedBlock::new_for_test(TestBlock::new(60, 0).build())];
2354 expected_blocks.extend(
2355 (30..=60).map(|round| VerifiedBlock::new_for_test(TestBlock::new(round, 1).build())),
2356 );
2357 assert_eq!(
2358 expected_blocks.len(),
2359 context.parameters.max_blocks_per_sync
2360 );
2361
2362 let expected_serialized_blocks = expected_blocks
2363 .iter()
2364 .map(|b| b.serialized().clone())
2365 .collect::<Vec<_>>();
2366
2367 let expected_block_refs = expected_blocks
2368 .iter()
2369 .map(|b| b.reference())
2370 .collect::<BTreeSet<_>>();
2371
2372 let peer_index = AuthorityIndex::new_for_test(2);
2374 let peer = PeerId::Validator(peer_index);
2375
2376 let inflight_blocks_map = InflightBlocksMap::new();
2378 let blocks_guard = inflight_blocks_map
2379 .lock_blocks(expected_block_refs.clone(), peer.clone())
2380 .expect("Failed to lock blocks");
2381
2382 assert_eq!(
2383 inflight_blocks_map.num_of_locked_blocks(),
2384 expected_block_refs.len()
2385 );
2386
2387 let result = Synchronizer::<
2389 NoopBlockVerifier,
2390 MockCoreThreadDispatcher,
2391 MockNetworkClient,
2392 MockNetworkClient,
2393 >::process_fetched_blocks(
2394 expected_serialized_blocks,
2395 peer,
2396 blocks_guard, core_dispatcher.clone(),
2398 block_verifier,
2399 transaction_vote_tracker,
2400 commit_vote_monitor,
2401 context.clone(),
2402 commands_sender,
2403 round_tracker,
2404 "test",
2405 )
2406 .await;
2407
2408 assert!(result.is_ok());
2410
2411 let added_blocks = core_dispatcher.get_add_blocks().await;
2413 assert_eq!(
2414 added_blocks
2415 .iter()
2416 .map(|b| b.reference())
2417 .collect::<BTreeSet<_>>(),
2418 expected_block_refs,
2419 );
2420
2421 assert_eq!(inflight_blocks_map.num_of_locked_blocks(), 0);
2423 }
2424}