1pub use sp_consensus_grandpa::{AuthorityList, SetId};
22
23use crate::{
24 strategy::{chain_sync::validate_blocks, disconnected_peers::DisconnectedPeers},
25 types::{BadPeer, SyncState, SyncStatus},
26 LOG_TARGET,
27};
28use codec::{Decode, Encode};
29use log::{debug, error, trace, warn};
30use sc_network::ProtocolName;
31use sc_network_common::sync::message::{
32 BlockAnnounce, BlockAttributes, BlockData, BlockRequest, Direction, FromBlock,
33};
34use sc_network_types::PeerId;
35use sp_blockchain::HeaderBackend;
36use sp_runtime::{
37 traits::{Block as BlockT, Header, NumberFor, Zero},
38 Justifications, SaturatedConversion,
39};
40use std::{collections::HashMap, fmt, sync::Arc};
41
42const MIN_PEERS_TO_START_WARP_SYNC: usize = 3;
44
45pub struct EncodedProof(pub Vec<u8>);
47
48#[derive(Encode, Decode, Debug, Clone)]
50pub struct WarpProofRequest<B: BlockT> {
51 pub begin: B::Hash,
53}
54
55pub enum VerificationResult<Block: BlockT> {
57 Partial(SetId, AuthorityList, Block::Hash),
59 Complete(SetId, AuthorityList, Block::Header),
61}
62
63pub trait WarpSyncProvider<Block: BlockT>: Send + Sync {
65 fn generate(
68 &self,
69 start: Block::Hash,
70 ) -> Result<EncodedProof, Box<dyn std::error::Error + Send + Sync>>;
71 fn verify(
73 &self,
74 proof: &EncodedProof,
75 set_id: SetId,
76 authorities: AuthorityList,
77 ) -> Result<VerificationResult<Block>, Box<dyn std::error::Error + Send + Sync>>;
78 fn current_authorities(&self) -> AuthorityList;
81}
82
83mod rep {
84 use sc_network::ReputationChange as Rep;
85
86 pub const UNEXPECTED_RESPONSE: Rep = Rep::new(-(1 << 29), "Unexpected response");
88
89 pub const BAD_WARP_PROOF: Rep = Rep::new(-(1 << 29), "Bad warp proof");
91
92 pub const NO_BLOCK: Rep = Rep::new(-(1 << 29), "No requested block data");
94
95 pub const NOT_REQUESTED: Rep = Rep::new(-(1 << 29), "Not requested block data");
97
98 pub const VERIFICATION_FAIL: Rep = Rep::new(-(1 << 29), "Block verification failed");
100}
101
102#[derive(Clone, Eq, PartialEq, Debug)]
104pub enum WarpSyncPhase<Block: BlockT> {
105 AwaitingPeers { required_peers: usize },
107 DownloadingWarpProofs,
109 DownloadingTargetBlock,
111 DownloadingState,
113 ImportingState,
115 DownloadingBlocks(NumberFor<Block>),
117 Complete,
119}
120
121impl<Block: BlockT> fmt::Display for WarpSyncPhase<Block> {
122 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
123 match self {
124 Self::AwaitingPeers { required_peers } =>
125 write!(f, "Waiting for {required_peers} peers to be connected"),
126 Self::DownloadingWarpProofs => write!(f, "Downloading finality proofs"),
127 Self::DownloadingTargetBlock => write!(f, "Downloading target block"),
128 Self::DownloadingState => write!(f, "Downloading state"),
129 Self::ImportingState => write!(f, "Importing state"),
130 Self::DownloadingBlocks(n) => write!(f, "Downloading block history (#{})", n),
131 Self::Complete => write!(f, "Warp sync is complete"),
132 }
133 }
134}
135
136#[derive(Clone, Eq, PartialEq, Debug)]
138pub struct WarpSyncProgress<Block: BlockT> {
139 pub phase: WarpSyncPhase<Block>,
141 pub total_bytes: u64,
143}
144
145pub enum WarpSyncConfig<Block: BlockT> {
147 WithProvider(Arc<dyn WarpSyncProvider<Block>>),
149 WithTarget(<Block as BlockT>::Header),
153}
154
155enum Phase<B: BlockT> {
157 WaitingForPeers { warp_sync_provider: Arc<dyn WarpSyncProvider<B>> },
159 WarpProof {
161 set_id: SetId,
162 authorities: AuthorityList,
163 last_hash: B::Hash,
164 warp_sync_provider: Arc<dyn WarpSyncProvider<B>>,
165 },
166 TargetBlock(B::Header),
168 Complete,
170}
171
172enum PeerState {
173 Available,
174 DownloadingProofs,
175 DownloadingTargetBlock,
176}
177
178impl PeerState {
179 fn is_available(&self) -> bool {
180 matches!(self, PeerState::Available)
181 }
182}
183
184struct Peer<B: BlockT> {
185 best_number: NumberFor<B>,
186 state: PeerState,
187}
188
189pub enum WarpSyncAction<B: BlockT> {
191 SendWarpProofRequest {
193 peer_id: PeerId,
194 protocol_name: ProtocolName,
195 request: WarpProofRequest<B>,
196 },
197 SendBlockRequest { peer_id: PeerId, request: BlockRequest<B> },
199 DropPeer(BadPeer),
201 Finished,
203}
204
205pub struct WarpSyncResult<B: BlockT> {
206 pub target_header: B::Header,
207 pub target_body: Option<Vec<B::Extrinsic>>,
208 pub target_justifications: Option<Justifications>,
209}
210
211pub struct WarpSync<B: BlockT, Client> {
213 phase: Phase<B>,
214 client: Arc<Client>,
215 total_proof_bytes: u64,
216 total_state_bytes: u64,
217 peers: HashMap<PeerId, Peer<B>>,
218 disconnected_peers: DisconnectedPeers,
219 protocol_name: Option<ProtocolName>,
220 actions: Vec<WarpSyncAction<B>>,
221 result: Option<WarpSyncResult<B>>,
222}
223
224impl<B, Client> WarpSync<B, Client>
225where
226 B: BlockT,
227 Client: HeaderBackend<B> + 'static,
228{
229 pub fn new(
233 client: Arc<Client>,
234 warp_sync_config: WarpSyncConfig<B>,
235 protocol_name: Option<ProtocolName>,
236 ) -> Self {
237 if client.info().finalized_state.is_some() {
238 error!(
239 target: LOG_TARGET,
240 "Can't use warp sync mode with a partially synced database. Reverting to full sync mode."
241 );
242 return Self {
243 client,
244 phase: Phase::Complete,
245 total_proof_bytes: 0,
246 total_state_bytes: 0,
247 peers: HashMap::new(),
248 disconnected_peers: DisconnectedPeers::new(),
249 protocol_name,
250 actions: vec![WarpSyncAction::Finished],
251 result: None,
252 }
253 }
254
255 let phase = match warp_sync_config {
256 WarpSyncConfig::WithProvider(warp_sync_provider) =>
257 Phase::WaitingForPeers { warp_sync_provider },
258 WarpSyncConfig::WithTarget(target_header) => Phase::TargetBlock(target_header),
259 };
260
261 Self {
262 client,
263 phase,
264 total_proof_bytes: 0,
265 total_state_bytes: 0,
266 peers: HashMap::new(),
267 disconnected_peers: DisconnectedPeers::new(),
268 protocol_name,
269 actions: Vec::new(),
270 result: None,
271 }
272 }
273
274 pub fn add_peer(&mut self, peer_id: PeerId, _best_hash: B::Hash, best_number: NumberFor<B>) {
276 self.peers.insert(peer_id, Peer { best_number, state: PeerState::Available });
277
278 self.try_to_start_warp_sync();
279 }
280
281 pub fn remove_peer(&mut self, peer_id: &PeerId) {
283 if let Some(state) = self.peers.remove(peer_id) {
284 if !state.state.is_available() {
285 if let Some(bad_peer) =
286 self.disconnected_peers.on_disconnect_during_request(*peer_id)
287 {
288 self.actions.push(WarpSyncAction::DropPeer(bad_peer));
289 }
290 }
291 }
292 }
293
294 #[must_use]
298 pub fn on_validated_block_announce(
299 &mut self,
300 is_best: bool,
301 peer_id: PeerId,
302 announce: &BlockAnnounce<B::Header>,
303 ) -> Option<(B::Hash, NumberFor<B>)> {
304 is_best.then_some({
305 let best_number = *announce.header.number();
306 let best_hash = announce.header.hash();
307 if let Some(ref mut peer) = self.peers.get_mut(&peer_id) {
308 peer.best_number = best_number;
309 }
310 (best_hash, best_number)
312 })
313 }
314
315 fn try_to_start_warp_sync(&mut self) {
317 let Phase::WaitingForPeers { warp_sync_provider } = &self.phase else { return };
318
319 if self.peers.len() < MIN_PEERS_TO_START_WARP_SYNC {
320 return
321 }
322
323 self.phase = Phase::WarpProof {
324 set_id: 0,
325 authorities: warp_sync_provider.current_authorities(),
326 last_hash: self.client.info().genesis_hash,
327 warp_sync_provider: Arc::clone(warp_sync_provider),
328 };
329 trace!(target: LOG_TARGET, "Started warp sync with {} peers.", self.peers.len());
330 }
331
332 pub fn on_warp_proof_response(&mut self, peer_id: &PeerId, response: EncodedProof) {
334 if let Some(peer) = self.peers.get_mut(peer_id) {
335 peer.state = PeerState::Available;
336 }
337
338 let Phase::WarpProof { set_id, authorities, last_hash, warp_sync_provider } =
339 &mut self.phase
340 else {
341 debug!(target: LOG_TARGET, "Unexpected warp proof response");
342 self.actions
343 .push(WarpSyncAction::DropPeer(BadPeer(*peer_id, rep::UNEXPECTED_RESPONSE)));
344 return
345 };
346
347 match warp_sync_provider.verify(&response, *set_id, authorities.clone()) {
348 Err(e) => {
349 debug!(target: LOG_TARGET, "Bad warp proof response: {}", e);
350 self.actions
351 .push(WarpSyncAction::DropPeer(BadPeer(*peer_id, rep::BAD_WARP_PROOF)))
352 },
353 Ok(VerificationResult::Partial(new_set_id, new_authorities, new_last_hash)) => {
354 log::debug!(target: LOG_TARGET, "Verified partial proof, set_id={:?}", new_set_id);
355 *set_id = new_set_id;
356 *authorities = new_authorities;
357 *last_hash = new_last_hash;
358 self.total_proof_bytes += response.0.len() as u64;
359 },
360 Ok(VerificationResult::Complete(new_set_id, _, header)) => {
361 log::debug!(
362 target: LOG_TARGET,
363 "Verified complete proof, set_id={:?}. Continuing with target block download: {} ({}).",
364 new_set_id,
365 header.hash(),
366 header.number(),
367 );
368 self.total_proof_bytes += response.0.len() as u64;
369 self.phase = Phase::TargetBlock(header);
370 },
371 }
372 }
373
374 pub fn on_block_response(
376 &mut self,
377 peer_id: PeerId,
378 request: BlockRequest<B>,
379 blocks: Vec<BlockData<B>>,
380 ) {
381 if let Err(bad_peer) = self.on_block_response_inner(peer_id, request, blocks) {
382 self.actions.push(WarpSyncAction::DropPeer(bad_peer));
383 }
384 }
385
386 fn on_block_response_inner(
387 &mut self,
388 peer_id: PeerId,
389 request: BlockRequest<B>,
390 mut blocks: Vec<BlockData<B>>,
391 ) -> Result<(), BadPeer> {
392 if let Some(peer) = self.peers.get_mut(&peer_id) {
393 peer.state = PeerState::Available;
394 }
395
396 let Phase::TargetBlock(header) = &mut self.phase else {
397 debug!(target: LOG_TARGET, "Unexpected target block response from {peer_id}");
398 return Err(BadPeer(peer_id, rep::UNEXPECTED_RESPONSE))
399 };
400
401 if blocks.is_empty() {
402 debug!(
403 target: LOG_TARGET,
404 "Downloading target block failed: empty block response from {peer_id}",
405 );
406 return Err(BadPeer(peer_id, rep::NO_BLOCK))
407 }
408
409 if blocks.len() > 1 {
410 debug!(
411 target: LOG_TARGET,
412 "Too many blocks ({}) in warp target block response from {peer_id}",
413 blocks.len(),
414 );
415 return Err(BadPeer(peer_id, rep::NOT_REQUESTED))
416 }
417
418 validate_blocks::<B>(&blocks, &peer_id, Some(request))?;
419
420 let block = blocks.pop().expect("`blocks` len checked above; qed");
421
422 let Some(block_header) = &block.header else {
423 debug!(
424 target: LOG_TARGET,
425 "Downloading target block failed: missing header in response from {peer_id}.",
426 );
427 return Err(BadPeer(peer_id, rep::VERIFICATION_FAIL))
428 };
429
430 if block_header != header {
431 debug!(
432 target: LOG_TARGET,
433 "Downloading target block failed: different header in response from {peer_id}.",
434 );
435 return Err(BadPeer(peer_id, rep::VERIFICATION_FAIL))
436 }
437
438 if block.body.is_none() {
439 debug!(
440 target: LOG_TARGET,
441 "Downloading target block failed: missing body in response from {peer_id}.",
442 );
443 return Err(BadPeer(peer_id, rep::VERIFICATION_FAIL))
444 }
445
446 self.result = Some(WarpSyncResult {
447 target_header: header.clone(),
448 target_body: block.body,
449 target_justifications: block.justifications,
450 });
451 self.phase = Phase::Complete;
452 self.actions.push(WarpSyncAction::Finished);
453 Ok(())
454 }
455
456 fn schedule_next_peer(
458 &mut self,
459 new_state: PeerState,
460 min_best_number: Option<NumberFor<B>>,
461 ) -> Option<PeerId> {
462 let mut targets: Vec<_> = self.peers.values().map(|p| p.best_number).collect();
463 if targets.is_empty() {
464 return None
465 }
466 targets.sort();
467 let median = targets[targets.len() / 2];
468 let threshold = std::cmp::max(median, min_best_number.unwrap_or(Zero::zero()));
469 for (peer_id, peer) in self.peers.iter_mut() {
472 if peer.state.is_available() &&
473 peer.best_number >= threshold &&
474 self.disconnected_peers.is_peer_available(peer_id)
475 {
476 peer.state = new_state;
477 return Some(*peer_id)
478 }
479 }
480 None
481 }
482
483 fn warp_proof_request(&mut self) -> Option<(PeerId, ProtocolName, WarpProofRequest<B>)> {
485 let Phase::WarpProof { last_hash, .. } = &self.phase else { return None };
486
487 let begin = *last_hash;
489
490 if self
491 .peers
492 .values()
493 .any(|peer| matches!(peer.state, PeerState::DownloadingProofs))
494 {
495 return None
497 }
498
499 let peer_id = self.schedule_next_peer(PeerState::DownloadingProofs, None)?;
500 trace!(target: LOG_TARGET, "New WarpProofRequest to {peer_id}, begin hash: {begin}.");
501
502 let request = WarpProofRequest { begin };
503
504 let Some(protocol_name) = self.protocol_name.clone() else {
505 warn!(
506 target: LOG_TARGET,
507 "Trying to send warp sync request when no protocol is configured {request:?}",
508 );
509 return None;
510 };
511
512 Some((peer_id, protocol_name, request))
513 }
514
515 fn target_block_request(&mut self) -> Option<(PeerId, BlockRequest<B>)> {
517 let Phase::TargetBlock(target_header) = &self.phase else { return None };
518
519 if self
520 .peers
521 .values()
522 .any(|peer| matches!(peer.state, PeerState::DownloadingTargetBlock))
523 {
524 return None
526 }
527
528 let target_hash = target_header.hash();
530 let target_number = *target_header.number();
531
532 let peer_id =
533 self.schedule_next_peer(PeerState::DownloadingTargetBlock, Some(target_number))?;
534
535 trace!(
536 target: LOG_TARGET,
537 "New target block request to {peer_id}, target: {} ({}).",
538 target_hash,
539 target_number,
540 );
541
542 Some((
543 peer_id,
544 BlockRequest::<B> {
545 id: 0,
546 fields: BlockAttributes::HEADER |
547 BlockAttributes::BODY |
548 BlockAttributes::JUSTIFICATION,
549 from: FromBlock::Hash(target_hash),
550 direction: Direction::Ascending,
551 max: Some(1),
552 },
553 ))
554 }
555
556 pub fn progress(&self) -> WarpSyncProgress<B> {
558 match &self.phase {
559 Phase::WaitingForPeers { .. } => WarpSyncProgress {
560 phase: WarpSyncPhase::AwaitingPeers {
561 required_peers: MIN_PEERS_TO_START_WARP_SYNC,
562 },
563 total_bytes: self.total_proof_bytes,
564 },
565 Phase::WarpProof { .. } => WarpSyncProgress {
566 phase: WarpSyncPhase::DownloadingWarpProofs,
567 total_bytes: self.total_proof_bytes,
568 },
569 Phase::TargetBlock(_) => WarpSyncProgress {
570 phase: WarpSyncPhase::DownloadingTargetBlock,
571 total_bytes: self.total_proof_bytes,
572 },
573 Phase::Complete => WarpSyncProgress {
574 phase: WarpSyncPhase::Complete,
575 total_bytes: self.total_proof_bytes + self.total_state_bytes,
576 },
577 }
578 }
579
580 pub fn num_peers(&self) -> usize {
582 self.peers.len()
583 }
584
585 pub fn status(&self) -> SyncStatus<B> {
587 SyncStatus {
588 state: match &self.phase {
589 Phase::WaitingForPeers { .. } => SyncState::Downloading { target: Zero::zero() },
590 Phase::WarpProof { .. } => SyncState::Downloading { target: Zero::zero() },
591 Phase::TargetBlock(header) => SyncState::Downloading { target: *header.number() },
592 Phase::Complete => SyncState::Idle,
593 },
594 best_seen_block: match &self.phase {
595 Phase::WaitingForPeers { .. } => None,
596 Phase::WarpProof { .. } => None,
597 Phase::TargetBlock(header) => Some(*header.number()),
598 Phase::Complete => None,
599 },
600 num_peers: self.peers.len().saturated_into(),
601 queued_blocks: 0,
602 state_sync: None,
603 warp_sync: Some(self.progress()),
604 }
605 }
606
607 #[must_use]
609 pub fn actions(&mut self) -> impl Iterator<Item = WarpSyncAction<B>> {
610 let warp_proof_request =
611 self.warp_proof_request().into_iter().map(|(peer_id, protocol_name, request)| {
612 WarpSyncAction::SendWarpProofRequest { peer_id, protocol_name, request }
613 });
614 self.actions.extend(warp_proof_request);
615
616 let target_block_request = self
617 .target_block_request()
618 .into_iter()
619 .map(|(peer_id, request)| WarpSyncAction::SendBlockRequest { peer_id, request });
620 self.actions.extend(target_block_request);
621
622 std::mem::take(&mut self.actions).into_iter()
623 }
624
625 #[must_use]
627 pub fn take_result(&mut self) -> Option<WarpSyncResult<B>> {
628 self.result.take()
629 }
630}
631
632#[cfg(test)]
633mod test {
634 use super::*;
635 use sc_block_builder::BlockBuilderBuilder;
636 use sp_blockchain::{BlockStatus, Error as BlockchainError, HeaderBackend, Info};
637 use sp_consensus_grandpa::{AuthorityList, SetId};
638 use sp_core::H256;
639 use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor};
640 use std::{io::ErrorKind, sync::Arc};
641 use substrate_test_runtime_client::{
642 runtime::{Block, Hash},
643 BlockBuilderExt, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
644 };
645
646 mockall::mock! {
647 pub Client<B: BlockT> {}
648
649 impl<B: BlockT> HeaderBackend<B> for Client<B> {
650 fn header(&self, hash: B::Hash) -> Result<Option<B::Header>, BlockchainError>;
651 fn info(&self) -> Info<B>;
652 fn status(&self, hash: B::Hash) -> Result<BlockStatus, BlockchainError>;
653 fn number(
654 &self,
655 hash: B::Hash,
656 ) -> Result<Option<<<B as BlockT>::Header as HeaderT>::Number>, BlockchainError>;
657 fn hash(&self, number: NumberFor<B>) -> Result<Option<B::Hash>, BlockchainError>;
658 }
659 }
660
661 mockall::mock! {
662 pub WarpSyncProvider<B: BlockT> {}
663
664 impl<B: BlockT> super::WarpSyncProvider<B> for WarpSyncProvider<B> {
665 fn generate(
666 &self,
667 start: B::Hash,
668 ) -> Result<EncodedProof, Box<dyn std::error::Error + Send + Sync>>;
669 fn verify(
670 &self,
671 proof: &EncodedProof,
672 set_id: SetId,
673 authorities: AuthorityList,
674 ) -> Result<VerificationResult<B>, Box<dyn std::error::Error + Send + Sync>>;
675 fn current_authorities(&self) -> AuthorityList;
676 }
677 }
678
679 fn mock_client_with_state() -> MockClient<Block> {
680 let mut client = MockClient::<Block>::new();
681 let genesis_hash = Hash::random();
682 client.expect_info().return_once(move || Info {
683 best_hash: genesis_hash,
684 best_number: 0,
685 genesis_hash,
686 finalized_hash: genesis_hash,
687 finalized_number: 0,
688 finalized_state: Some((genesis_hash, 0)),
690 number_leaves: 0,
691 block_gap: None,
692 });
693
694 client
695 }
696
697 fn mock_client_without_state() -> MockClient<Block> {
698 let mut client = MockClient::<Block>::new();
699 let genesis_hash = Hash::random();
700 client.expect_info().returning(move || Info {
701 best_hash: genesis_hash,
702 best_number: 0,
703 genesis_hash,
704 finalized_hash: genesis_hash,
705 finalized_number: 0,
706 finalized_state: None,
707 number_leaves: 0,
708 block_gap: None,
709 });
710
711 client
712 }
713
714 #[test]
715 fn warp_sync_with_provider_for_db_with_finalized_state_is_noop() {
716 let client = mock_client_with_state();
717 let provider = MockWarpSyncProvider::<Block>::new();
718 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
719 let mut warp_sync = WarpSync::new(Arc::new(client), config, None);
720
721 let actions = warp_sync.actions().collect::<Vec<_>>();
723 assert_eq!(actions.len(), 1);
724 assert!(matches!(actions[0], WarpSyncAction::Finished));
725
726 assert!(warp_sync.take_result().is_none());
728 }
729
730 #[test]
731 fn warp_sync_to_target_for_db_with_finalized_state_is_noop() {
732 let client = mock_client_with_state();
733 let config = WarpSyncConfig::WithTarget(<Block as BlockT>::Header::new(
734 1,
735 Default::default(),
736 Default::default(),
737 Default::default(),
738 Default::default(),
739 ));
740 let mut warp_sync = WarpSync::new(Arc::new(client), config, None);
741
742 let actions = warp_sync.actions().collect::<Vec<_>>();
744 assert_eq!(actions.len(), 1);
745 assert!(matches!(actions[0], WarpSyncAction::Finished));
746
747 assert!(warp_sync.take_result().is_none());
749 }
750
751 #[test]
752 fn warp_sync_with_provider_for_empty_db_doesnt_finish_instantly() {
753 let client = mock_client_without_state();
754 let provider = MockWarpSyncProvider::<Block>::new();
755 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
756 let mut warp_sync = WarpSync::new(Arc::new(client), config, None);
757
758 assert_eq!(warp_sync.actions().count(), 0)
760 }
761
762 #[test]
763 fn warp_sync_to_target_for_empty_db_doesnt_finish_instantly() {
764 let client = mock_client_without_state();
765 let config = WarpSyncConfig::WithTarget(<Block as BlockT>::Header::new(
766 1,
767 Default::default(),
768 Default::default(),
769 Default::default(),
770 Default::default(),
771 ));
772 let mut warp_sync = WarpSync::new(Arc::new(client), config, None);
773
774 assert_eq!(warp_sync.actions().count(), 0)
776 }
777
778 #[test]
779 fn warp_sync_is_started_only_when_there_is_enough_peers() {
780 let client = mock_client_without_state();
781 let mut provider = MockWarpSyncProvider::<Block>::new();
782 provider
783 .expect_current_authorities()
784 .once()
785 .return_const(AuthorityList::default());
786 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
787 let mut warp_sync = WarpSync::new(Arc::new(client), config, None);
788
789 for _ in 0..(MIN_PEERS_TO_START_WARP_SYNC - 1) {
791 warp_sync.add_peer(PeerId::random(), Hash::random(), 10);
792 assert!(matches!(warp_sync.phase, Phase::WaitingForPeers { .. }))
793 }
794
795 warp_sync.add_peer(PeerId::random(), Hash::random(), 10);
797 assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }))
798 }
799
800 #[test]
801 fn no_peer_is_scheduled_if_no_peers_connected() {
802 let client = mock_client_without_state();
803 let provider = MockWarpSyncProvider::<Block>::new();
804 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
805 let mut warp_sync = WarpSync::new(Arc::new(client), config, None);
806
807 assert!(warp_sync.schedule_next_peer(PeerState::DownloadingProofs, None).is_none());
808 }
809
810 #[test]
811 fn enough_peers_are_used_in_tests() {
812 assert!(
814 10 >= MIN_PEERS_TO_START_WARP_SYNC,
815 "Tests must be updated to use that many initial peers.",
816 );
817 }
818
819 #[test]
820 fn at_least_median_synced_peer_is_scheduled() {
821 for _ in 0..100 {
822 let client = mock_client_without_state();
823 let mut provider = MockWarpSyncProvider::<Block>::new();
824 provider
825 .expect_current_authorities()
826 .once()
827 .return_const(AuthorityList::default());
828 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
829 let mut warp_sync = WarpSync::new(Arc::new(client), config, None);
830
831 for best_number in 1..11 {
832 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
833 }
834
835 let peer_id = warp_sync.schedule_next_peer(PeerState::DownloadingProofs, None);
836 assert!(warp_sync.peers.get(&peer_id.unwrap()).unwrap().best_number >= 6);
837 }
838 }
839
840 #[test]
841 fn min_best_number_peer_is_scheduled() {
842 for _ in 0..10 {
843 let client = mock_client_without_state();
844 let mut provider = MockWarpSyncProvider::<Block>::new();
845 provider
846 .expect_current_authorities()
847 .once()
848 .return_const(AuthorityList::default());
849 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
850 let mut warp_sync = WarpSync::new(Arc::new(client), config, None);
851
852 for best_number in 1..11 {
853 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
854 }
855
856 let peer_id = warp_sync.schedule_next_peer(PeerState::DownloadingProofs, Some(10));
857 assert!(warp_sync.peers.get(&peer_id.unwrap()).unwrap().best_number == 10);
858 }
859 }
860
861 #[test]
862 fn backedoff_number_peer_is_not_scheduled() {
863 let client = mock_client_without_state();
864 let mut provider = MockWarpSyncProvider::<Block>::new();
865 provider
866 .expect_current_authorities()
867 .once()
868 .return_const(AuthorityList::default());
869 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
870 let mut warp_sync = WarpSync::new(Arc::new(client), config, None);
871
872 for best_number in 1..11 {
873 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
874 }
875
876 let ninth_peer =
877 *warp_sync.peers.iter().find(|(_, state)| state.best_number == 9).unwrap().0;
878 let tenth_peer =
879 *warp_sync.peers.iter().find(|(_, state)| state.best_number == 10).unwrap().0;
880
881 warp_sync.remove_peer(&tenth_peer);
883 assert!(warp_sync.disconnected_peers.is_peer_available(&tenth_peer));
884
885 warp_sync.add_peer(tenth_peer, H256::random(), 10);
886 let peer_id = warp_sync.schedule_next_peer(PeerState::DownloadingProofs, Some(10));
887 assert_eq!(tenth_peer, peer_id.unwrap());
888 warp_sync.remove_peer(&tenth_peer);
889
890 assert!(!warp_sync.disconnected_peers.is_peer_available(&tenth_peer));
892
893 warp_sync.add_peer(tenth_peer, H256::random(), 10);
895 let peer_id: Option<PeerId> =
896 warp_sync.schedule_next_peer(PeerState::DownloadingProofs, Some(10));
897 assert!(peer_id.is_none());
898
899 let peer_id: Option<PeerId> =
901 warp_sync.schedule_next_peer(PeerState::DownloadingProofs, Some(9));
902 assert_eq!(ninth_peer, peer_id.unwrap());
903 }
904
905 #[test]
906 fn no_warp_proof_request_in_another_phase() {
907 let client = mock_client_without_state();
908 let mut provider = MockWarpSyncProvider::<Block>::new();
909 provider
910 .expect_current_authorities()
911 .once()
912 .return_const(AuthorityList::default());
913 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
914 let mut warp_sync = WarpSync::new(Arc::new(client), config, Some(ProtocolName::Static("")));
915
916 for best_number in 1..11 {
918 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
919 }
920
921 warp_sync.phase = Phase::TargetBlock(<Block as BlockT>::Header::new(
923 1,
924 Default::default(),
925 Default::default(),
926 Default::default(),
927 Default::default(),
928 ));
929
930 assert!(warp_sync.warp_proof_request().is_none());
932 }
933
934 #[test]
935 fn warp_proof_request_starts_at_last_hash() {
936 let client = mock_client_without_state();
937 let mut provider = MockWarpSyncProvider::<Block>::new();
938 provider
939 .expect_current_authorities()
940 .once()
941 .return_const(AuthorityList::default());
942 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
943 let mut warp_sync = WarpSync::new(Arc::new(client), config, Some(ProtocolName::Static("")));
944
945 for best_number in 1..11 {
947 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
948 }
949 assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }));
950
951 let known_last_hash = Hash::random();
952
953 match &mut warp_sync.phase {
955 Phase::WarpProof { last_hash, .. } => {
956 *last_hash = known_last_hash;
957 },
958 _ => panic!("Invalid phase."),
959 }
960
961 let (_peer_id, _protocol_name, request) = warp_sync.warp_proof_request().unwrap();
962 assert_eq!(request.begin, known_last_hash);
963 }
964
965 #[test]
966 fn no_parallel_warp_proof_requests() {
967 let client = mock_client_without_state();
968 let mut provider = MockWarpSyncProvider::<Block>::new();
969 provider
970 .expect_current_authorities()
971 .once()
972 .return_const(AuthorityList::default());
973 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
974 let mut warp_sync = WarpSync::new(Arc::new(client), config, Some(ProtocolName::Static("")));
975
976 for best_number in 1..11 {
978 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
979 }
980 assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }));
981
982 assert!(warp_sync.warp_proof_request().is_some());
984 assert!(warp_sync.warp_proof_request().is_none());
986 }
987
988 #[test]
989 fn bad_warp_proof_response_drops_peer() {
990 let client = mock_client_without_state();
991 let mut provider = MockWarpSyncProvider::<Block>::new();
992 provider
993 .expect_current_authorities()
994 .once()
995 .return_const(AuthorityList::default());
996 provider.expect_verify().return_once(|_proof, _set_id, _authorities| {
998 Err(Box::new(std::io::Error::new(ErrorKind::Other, "test-verification-failure")))
999 });
1000 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1001 let mut warp_sync = WarpSync::new(Arc::new(client), config, Some(ProtocolName::Static("")));
1002
1003 for best_number in 1..11 {
1005 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1006 }
1007 assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }));
1008
1009 let actions = warp_sync.actions().collect::<Vec<_>>();
1011 assert_eq!(actions.len(), 1);
1012 let WarpSyncAction::SendWarpProofRequest { peer_id: request_peer_id, .. } = actions[0]
1013 else {
1014 panic!("Invalid action");
1015 };
1016
1017 warp_sync.on_warp_proof_response(&request_peer_id, EncodedProof(Vec::new()));
1018
1019 let actions = std::mem::take(&mut warp_sync.actions);
1021 assert_eq!(actions.len(), 1);
1022 assert!(matches!(
1023 actions[0],
1024 WarpSyncAction::DropPeer(BadPeer(peer_id, _rep)) if peer_id == request_peer_id
1025 ));
1026 assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }));
1027 }
1028
1029 #[test]
1030 fn partial_warp_proof_doesnt_advance_phase() {
1031 let client = mock_client_without_state();
1032 let mut provider = MockWarpSyncProvider::<Block>::new();
1033 provider
1034 .expect_current_authorities()
1035 .once()
1036 .return_const(AuthorityList::default());
1037 provider.expect_verify().return_once(|_proof, set_id, authorities| {
1039 Ok(VerificationResult::Partial(set_id, authorities, Hash::random()))
1040 });
1041 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1042 let mut warp_sync = WarpSync::new(Arc::new(client), config, Some(ProtocolName::Static("")));
1043
1044 for best_number in 1..11 {
1046 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1047 }
1048 assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }));
1049
1050 let actions = warp_sync.actions().collect::<Vec<_>>();
1052 assert_eq!(actions.len(), 1);
1053 let WarpSyncAction::SendWarpProofRequest { peer_id: request_peer_id, .. } = actions[0]
1054 else {
1055 panic!("Invalid action");
1056 };
1057
1058 warp_sync.on_warp_proof_response(&request_peer_id, EncodedProof(Vec::new()));
1059
1060 assert!(warp_sync.actions.is_empty(), "No extra actions generated");
1061 assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }));
1062 }
1063
1064 #[test]
1065 fn complete_warp_proof_advances_phase() {
1066 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1067 let mut provider = MockWarpSyncProvider::<Block>::new();
1068 provider
1069 .expect_current_authorities()
1070 .once()
1071 .return_const(AuthorityList::default());
1072 let target_block = BlockBuilderBuilder::new(&*client)
1073 .on_parent_block(client.chain_info().best_hash)
1074 .with_parent_block_number(client.chain_info().best_number)
1075 .build()
1076 .unwrap()
1077 .build()
1078 .unwrap()
1079 .block;
1080 let target_header = target_block.header().clone();
1081 provider.expect_verify().return_once(move |_proof, set_id, authorities| {
1083 Ok(VerificationResult::Complete(set_id, authorities, target_header))
1084 });
1085 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1086 let mut warp_sync = WarpSync::new(client, config, Some(ProtocolName::Static("")));
1087
1088 for best_number in 1..11 {
1090 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1091 }
1092 assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }));
1093
1094 let actions = warp_sync.actions().collect::<Vec<_>>();
1096 assert_eq!(actions.len(), 1);
1097 let WarpSyncAction::SendWarpProofRequest { peer_id: request_peer_id, .. } = actions[0]
1098 else {
1099 panic!("Invalid action.");
1100 };
1101
1102 warp_sync.on_warp_proof_response(&request_peer_id, EncodedProof(Vec::new()));
1103
1104 assert!(warp_sync.actions.is_empty(), "No extra actions generated.");
1105 assert!(
1106 matches!(warp_sync.phase, Phase::TargetBlock(header) if header == *target_block.header())
1107 );
1108 }
1109
1110 #[test]
1111 fn no_target_block_requests_in_another_phase() {
1112 let client = mock_client_without_state();
1113 let mut provider = MockWarpSyncProvider::<Block>::new();
1114 provider
1115 .expect_current_authorities()
1116 .once()
1117 .return_const(AuthorityList::default());
1118 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1119 let mut warp_sync = WarpSync::new(Arc::new(client), config, None);
1120
1121 for best_number in 1..11 {
1123 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1124 }
1125 assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }));
1127
1128 assert!(warp_sync.target_block_request().is_none());
1130 }
1131
1132 #[test]
1133 fn target_block_request_is_correct() {
1134 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1135 let mut provider = MockWarpSyncProvider::<Block>::new();
1136 provider
1137 .expect_current_authorities()
1138 .once()
1139 .return_const(AuthorityList::default());
1140 let target_block = BlockBuilderBuilder::new(&*client)
1141 .on_parent_block(client.chain_info().best_hash)
1142 .with_parent_block_number(client.chain_info().best_number)
1143 .build()
1144 .unwrap()
1145 .build()
1146 .unwrap()
1147 .block;
1148 let target_header = target_block.header().clone();
1149 provider.expect_verify().return_once(move |_proof, set_id, authorities| {
1151 Ok(VerificationResult::Complete(set_id, authorities, target_header))
1152 });
1153 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1154 let mut warp_sync = WarpSync::new(client, config, None);
1155
1156 for best_number in 1..11 {
1158 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1159 }
1160
1161 warp_sync.phase = Phase::TargetBlock(target_block.header().clone());
1163
1164 let (_peer_id, request) = warp_sync.target_block_request().unwrap();
1165 assert_eq!(request.from, FromBlock::Hash(target_block.header().hash()));
1166 assert_eq!(
1167 request.fields,
1168 BlockAttributes::HEADER | BlockAttributes::BODY | BlockAttributes::JUSTIFICATION
1169 );
1170 assert_eq!(request.max, Some(1));
1171 }
1172
1173 #[test]
1174 fn externally_set_target_block_is_requested() {
1175 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1176 let target_block = BlockBuilderBuilder::new(&*client)
1177 .on_parent_block(client.chain_info().best_hash)
1178 .with_parent_block_number(client.chain_info().best_number)
1179 .build()
1180 .unwrap()
1181 .build()
1182 .unwrap()
1183 .block;
1184 let target_header = target_block.header().clone();
1185 let config = WarpSyncConfig::WithTarget(target_header);
1186 let mut warp_sync = WarpSync::new(client, config, None);
1187
1188 for best_number in 1..11 {
1190 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1191 }
1192
1193 assert!(matches!(warp_sync.phase, Phase::TargetBlock(_)));
1194
1195 let (_peer_id, request) = warp_sync.target_block_request().unwrap();
1196 assert_eq!(request.from, FromBlock::Hash(target_block.header().hash()));
1197 assert_eq!(
1198 request.fields,
1199 BlockAttributes::HEADER | BlockAttributes::BODY | BlockAttributes::JUSTIFICATION
1200 );
1201 assert_eq!(request.max, Some(1));
1202 }
1203
1204 #[test]
1205 fn no_parallel_target_block_requests() {
1206 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1207 let mut provider = MockWarpSyncProvider::<Block>::new();
1208 provider
1209 .expect_current_authorities()
1210 .once()
1211 .return_const(AuthorityList::default());
1212 let target_block = BlockBuilderBuilder::new(&*client)
1213 .on_parent_block(client.chain_info().best_hash)
1214 .with_parent_block_number(client.chain_info().best_number)
1215 .build()
1216 .unwrap()
1217 .build()
1218 .unwrap()
1219 .block;
1220 let target_header = target_block.header().clone();
1221 provider.expect_verify().return_once(move |_proof, set_id, authorities| {
1223 Ok(VerificationResult::Complete(set_id, authorities, target_header))
1224 });
1225 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1226 let mut warp_sync = WarpSync::new(client, config, None);
1227
1228 for best_number in 1..11 {
1230 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1231 }
1232
1233 warp_sync.phase = Phase::TargetBlock(target_block.header().clone());
1235
1236 assert!(warp_sync.target_block_request().is_some());
1238 assert!(warp_sync.target_block_request().is_none());
1240 }
1241
1242 #[test]
1243 fn target_block_response_with_no_blocks_drops_peer() {
1244 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1245 let mut provider = MockWarpSyncProvider::<Block>::new();
1246 provider
1247 .expect_current_authorities()
1248 .once()
1249 .return_const(AuthorityList::default());
1250 let target_block = BlockBuilderBuilder::new(&*client)
1251 .on_parent_block(client.chain_info().best_hash)
1252 .with_parent_block_number(client.chain_info().best_number)
1253 .build()
1254 .unwrap()
1255 .build()
1256 .unwrap()
1257 .block;
1258 let target_header = target_block.header().clone();
1259 provider.expect_verify().return_once(move |_proof, set_id, authorities| {
1261 Ok(VerificationResult::Complete(set_id, authorities, target_header))
1262 });
1263 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1264 let mut warp_sync = WarpSync::new(client, config, None);
1265
1266 for best_number in 1..11 {
1268 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1269 }
1270
1271 warp_sync.phase = Phase::TargetBlock(target_block.header().clone());
1273
1274 let (peer_id, request) = warp_sync.target_block_request().unwrap();
1275
1276 let response = Vec::new();
1278 assert!(matches!(
1280 warp_sync.on_block_response_inner(peer_id, request, response),
1281 Err(BadPeer(id, _rep)) if id == peer_id,
1282 ));
1283 }
1284
1285 #[test]
1286 fn target_block_response_with_extra_blocks_drops_peer() {
1287 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1288 let mut provider = MockWarpSyncProvider::<Block>::new();
1289 provider
1290 .expect_current_authorities()
1291 .once()
1292 .return_const(AuthorityList::default());
1293 let target_block = BlockBuilderBuilder::new(&*client)
1294 .on_parent_block(client.chain_info().best_hash)
1295 .with_parent_block_number(client.chain_info().best_number)
1296 .build()
1297 .unwrap()
1298 .build()
1299 .unwrap()
1300 .block;
1301
1302 let mut extra_block_builder = BlockBuilderBuilder::new(&*client)
1303 .on_parent_block(client.chain_info().best_hash)
1304 .with_parent_block_number(client.chain_info().best_number)
1305 .build()
1306 .unwrap();
1307 extra_block_builder
1308 .push_storage_change(vec![1, 2, 3], Some(vec![4, 5, 6]))
1309 .unwrap();
1310 let extra_block = extra_block_builder.build().unwrap().block;
1311
1312 let target_header = target_block.header().clone();
1313 provider.expect_verify().return_once(move |_proof, set_id, authorities| {
1315 Ok(VerificationResult::Complete(set_id, authorities, target_header))
1316 });
1317 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1318 let mut warp_sync = WarpSync::new(client, config, None);
1319
1320 for best_number in 1..11 {
1322 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1323 }
1324
1325 warp_sync.phase = Phase::TargetBlock(target_block.header().clone());
1327
1328 let (peer_id, request) = warp_sync.target_block_request().unwrap();
1329
1330 let response = vec![
1332 BlockData::<Block> {
1333 hash: target_block.header().hash(),
1334 header: Some(target_block.header().clone()),
1335 body: Some(target_block.extrinsics().iter().cloned().collect::<Vec<_>>()),
1336 indexed_body: None,
1337 receipt: None,
1338 message_queue: None,
1339 justification: None,
1340 justifications: None,
1341 },
1342 BlockData::<Block> {
1343 hash: extra_block.header().hash(),
1344 header: Some(extra_block.header().clone()),
1345 body: Some(extra_block.extrinsics().iter().cloned().collect::<Vec<_>>()),
1346 indexed_body: None,
1347 receipt: None,
1348 message_queue: None,
1349 justification: None,
1350 justifications: None,
1351 },
1352 ];
1353 assert!(matches!(
1355 warp_sync.on_block_response_inner(peer_id, request, response),
1356 Err(BadPeer(id, _rep)) if id == peer_id,
1357 ));
1358 }
1359
1360 #[test]
1361 fn target_block_response_with_wrong_block_drops_peer() {
1362 sp_tracing::try_init_simple();
1363
1364 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1365 let mut provider = MockWarpSyncProvider::<Block>::new();
1366 provider
1367 .expect_current_authorities()
1368 .once()
1369 .return_const(AuthorityList::default());
1370 let target_block = BlockBuilderBuilder::new(&*client)
1371 .on_parent_block(client.chain_info().best_hash)
1372 .with_parent_block_number(client.chain_info().best_number)
1373 .build()
1374 .unwrap()
1375 .build()
1376 .unwrap()
1377 .block;
1378
1379 let mut wrong_block_builder = BlockBuilderBuilder::new(&*client)
1380 .on_parent_block(client.chain_info().best_hash)
1381 .with_parent_block_number(client.chain_info().best_number)
1382 .build()
1383 .unwrap();
1384 wrong_block_builder
1385 .push_storage_change(vec![1, 2, 3], Some(vec![4, 5, 6]))
1386 .unwrap();
1387 let wrong_block = wrong_block_builder.build().unwrap().block;
1388
1389 let target_header = target_block.header().clone();
1390 provider.expect_verify().return_once(move |_proof, set_id, authorities| {
1392 Ok(VerificationResult::Complete(set_id, authorities, target_header))
1393 });
1394 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1395 let mut warp_sync = WarpSync::new(client, config, None);
1396
1397 for best_number in 1..11 {
1399 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1400 }
1401
1402 warp_sync.phase = Phase::TargetBlock(target_block.header().clone());
1404
1405 let (peer_id, request) = warp_sync.target_block_request().unwrap();
1406
1407 let response = vec![BlockData::<Block> {
1409 hash: wrong_block.header().hash(),
1410 header: Some(wrong_block.header().clone()),
1411 body: Some(wrong_block.extrinsics().iter().cloned().collect::<Vec<_>>()),
1412 indexed_body: None,
1413 receipt: None,
1414 message_queue: None,
1415 justification: None,
1416 justifications: None,
1417 }];
1418 assert!(matches!(
1420 warp_sync.on_block_response_inner(peer_id, request, response),
1421 Err(BadPeer(id, _rep)) if id == peer_id,
1422 ));
1423 }
1424
1425 #[test]
1426 fn correct_target_block_response_sets_strategy_result() {
1427 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1428 let mut provider = MockWarpSyncProvider::<Block>::new();
1429 provider
1430 .expect_current_authorities()
1431 .once()
1432 .return_const(AuthorityList::default());
1433 let mut target_block_builder = BlockBuilderBuilder::new(&*client)
1434 .on_parent_block(client.chain_info().best_hash)
1435 .with_parent_block_number(client.chain_info().best_number)
1436 .build()
1437 .unwrap();
1438 target_block_builder
1439 .push_storage_change(vec![1, 2, 3], Some(vec![4, 5, 6]))
1440 .unwrap();
1441 let target_block = target_block_builder.build().unwrap().block;
1442 let target_header = target_block.header().clone();
1443 provider.expect_verify().return_once(move |_proof, set_id, authorities| {
1445 Ok(VerificationResult::Complete(set_id, authorities, target_header))
1446 });
1447 let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1448 let mut warp_sync = WarpSync::new(client, config, None);
1449
1450 for best_number in 1..11 {
1452 warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1453 }
1454
1455 warp_sync.phase = Phase::TargetBlock(target_block.header().clone());
1457
1458 let (peer_id, request) = warp_sync.target_block_request().unwrap();
1459
1460 let body = Some(target_block.extrinsics().iter().cloned().collect::<Vec<_>>());
1462 let justifications = Some(Justifications::from((*b"FRNK", Vec::new())));
1463 let response = vec![BlockData::<Block> {
1464 hash: target_block.header().hash(),
1465 header: Some(target_block.header().clone()),
1466 body: body.clone(),
1467 indexed_body: None,
1468 receipt: None,
1469 message_queue: None,
1470 justification: None,
1471 justifications: justifications.clone(),
1472 }];
1473
1474 assert!(warp_sync.on_block_response_inner(peer_id, request, response).is_ok());
1475
1476 let actions = warp_sync.actions().collect::<Vec<_>>();
1478 assert_eq!(actions.len(), 1);
1479 assert!(matches!(actions[0], WarpSyncAction::Finished));
1480
1481 let result = warp_sync.take_result().unwrap();
1483 assert_eq!(result.target_header, *target_block.header());
1484 assert_eq!(result.target_body, body);
1485 assert_eq!(result.target_justifications, justifications);
1486 }
1487}