referrerpolicy=no-referrer-when-downgrade

sc_network_sync/strategy/
warp.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Warp syncing strategy. Bootstraps chain by downloading warp proofs and state.
20
21use sc_consensus::IncomingBlock;
22use sp_consensus::BlockOrigin;
23
24use crate::{
25	block_relay_protocol::{BlockDownloader, BlockResponseError},
26	service::network::NetworkServiceHandle,
27	strategy::{
28		chain_sync::validate_blocks, disconnected_peers::DisconnectedPeers, StrategyKey,
29		SyncingAction,
30	},
31	types::{BadPeer, SyncState, SyncStatus},
32	LOG_TARGET,
33};
34use codec::{Decode, Encode};
35use futures::{channel::oneshot, FutureExt};
36use log::{debug, error, trace, warn};
37use sc_network::{IfDisconnected, ProtocolName};
38use sc_network_common::sync::message::{
39	BlockAnnounce, BlockAttributes, BlockData, BlockRequest, Direction, FromBlock,
40};
41use sc_network_types::PeerId;
42use sp_blockchain::HeaderBackend;
43use sp_runtime::{
44	traits::{Block as BlockT, Header, NumberFor, Zero},
45	Justifications, SaturatedConversion,
46};
47use std::{any::Any, collections::HashMap, fmt, sync::Arc};
48
49/// Number of peers that need to be connected before warp sync is started.
50const MIN_PEERS_TO_START_WARP_SYNC: usize = 3;
51
52/// Scale-encoded warp sync proof response.
53pub struct EncodedProof(pub Vec<u8>);
54
55/// Warp sync request
56#[derive(Encode, Decode, Debug, Clone)]
57pub struct WarpProofRequest<B: BlockT> {
58	/// Start collecting proofs from this block.
59	pub begin: B::Hash,
60}
61
62/// Verifier for warp sync proofs. Each verifier operates in a specific context.
63pub trait Verifier<Block: BlockT>: Send + Sync {
64	/// Verify a warp sync proof.
65	fn verify(
66		&mut self,
67		proof: &EncodedProof,
68	) -> Result<VerificationResult<Block>, Box<dyn std::error::Error + Send + Sync>>;
69	/// Hash to be used as the starting point for the next proof request.
70	fn next_proof_context(&self) -> Block::Hash;
71	/// Get status text for progress reporting
72	fn status(&self) -> Option<String>;
73}
74
75/// Proof verification result.
76pub enum VerificationResult<Block: BlockT> {
77	/// Proof is valid, but the target was not reached.
78	Partial(Vec<(Block::Header, Justifications)>),
79	/// Target finality is proved.
80	Complete(Block::Header, Vec<(Block::Header, Justifications)>),
81}
82
83/// Warp sync backend. Handles retrieving and verifying warp sync proofs.
84pub trait WarpSyncProvider<Block: BlockT>: Send + Sync {
85	/// Generate proof starting at given block hash. The proof is accumulated until maximum proof
86	/// size is reached.
87	fn generate(
88		&self,
89		start: Block::Hash,
90	) -> Result<EncodedProof, Box<dyn std::error::Error + Send + Sync>>;
91	/// Create a verifier for warp sync proofs.
92	fn create_verifier(&self) -> Box<dyn Verifier<Block>>;
93}
94
95mod rep {
96	use sc_network::ReputationChange as Rep;
97
98	/// Unexpected response received form a peer
99	pub const UNEXPECTED_RESPONSE: Rep = Rep::new(-(1 << 29), "Unexpected response");
100
101	/// Peer provided invalid warp proof data
102	pub const BAD_WARP_PROOF: Rep = Rep::new(-(1 << 29), "Bad warp proof");
103
104	/// Peer did not provide us with advertised block data.
105	pub const NO_BLOCK: Rep = Rep::new(-(1 << 29), "No requested block data");
106
107	/// Reputation change for peers which send us non-requested block data.
108	pub const NOT_REQUESTED: Rep = Rep::new(-(1 << 29), "Not requested block data");
109
110	/// Reputation change for peers which send us a block which we fail to verify.
111	pub const VERIFICATION_FAIL: Rep = Rep::new(-(1 << 29), "Block verification failed");
112
113	/// We received a message that failed to decode.
114	pub const BAD_MESSAGE: Rep = Rep::new(-(1 << 12), "Bad message");
115}
116
117/// Reported warp sync phase.
118#[derive(Clone, Eq, PartialEq, Debug)]
119pub enum WarpSyncPhase<Block: BlockT> {
120	/// Waiting for peers to connect.
121	AwaitingPeers { required_peers: usize },
122	/// Downloading and verifying warp proofs.
123	DownloadingWarpProofs,
124	/// Downloading target block.
125	DownloadingTargetBlock,
126	/// Downloading state data.
127	DownloadingState,
128	/// Importing state.
129	ImportingState,
130	/// Downloading block history.
131	DownloadingBlocks(NumberFor<Block>),
132	/// Warp sync is complete.
133	Complete,
134}
135
136impl<Block: BlockT> fmt::Display for WarpSyncPhase<Block> {
137	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138		match self {
139			Self::AwaitingPeers { required_peers } => {
140				write!(f, "Waiting for {required_peers} peers to be connected")
141			},
142			Self::DownloadingWarpProofs => write!(f, "Downloading finality proofs"),
143			Self::DownloadingTargetBlock => write!(f, "Downloading target block"),
144			Self::DownloadingState => write!(f, "Downloading state"),
145			Self::ImportingState => write!(f, "Importing state"),
146			Self::DownloadingBlocks(n) => write!(f, "Downloading block history (#{})", n),
147			Self::Complete => write!(f, "Warp sync is complete"),
148		}
149	}
150}
151
152/// Reported warp sync progress.
153#[derive(Clone, Eq, PartialEq, Debug)]
154pub struct WarpSyncProgress<Block: BlockT> {
155	/// Estimated download percentage.
156	pub phase: WarpSyncPhase<Block>,
157	/// Total bytes downloaded so far.
158	pub total_bytes: u64,
159	/// Optional status text from the verifier.
160	pub status: Option<String>,
161}
162
163/// Warp sync configuration as accepted by [`WarpSync`].
164pub enum WarpSyncConfig<Block: BlockT> {
165	/// Standard warp sync for the chain.
166	WithProvider(Arc<dyn WarpSyncProvider<Block>>),
167	/// Skip downloading proofs and use provided header of the state that should be downloaded.
168	///
169	/// It is expected that the header provider ensures that the header is trusted.
170	WithTarget(<Block as BlockT>::Header),
171}
172
173/// Warp sync phase used by warp sync state machine.
174enum Phase<B: BlockT> {
175	/// Waiting for enough peers to connect.
176	WaitingForPeers { warp_sync_provider: Arc<dyn WarpSyncProvider<B>> },
177	/// Downloading warp proofs.
178	WarpProof { verifier: Box<dyn Verifier<B>> },
179	/// Downloading target block.
180	TargetBlock(B::Header),
181	/// Warp sync is complete.
182	Complete,
183}
184
185enum PeerState {
186	Available,
187	DownloadingProofs,
188	DownloadingTargetBlock,
189}
190
191impl PeerState {
192	fn is_available(&self) -> bool {
193		matches!(self, PeerState::Available)
194	}
195}
196
197struct Peer<B: BlockT> {
198	best_number: NumberFor<B>,
199	state: PeerState,
200}
201
202pub struct WarpSyncResult<B: BlockT> {
203	pub target_header: B::Header,
204	pub target_body: Option<Vec<B::Extrinsic>>,
205	pub target_justifications: Option<Justifications>,
206}
207
208/// Warp sync state machine. Accumulates warp proofs and state.
209pub struct WarpSync<B: BlockT> {
210	phase: Phase<B>,
211	total_proof_bytes: u64,
212	total_state_bytes: u64,
213	peers: HashMap<PeerId, Peer<B>>,
214	disconnected_peers: DisconnectedPeers,
215	protocol_name: Option<ProtocolName>,
216	block_downloader: Arc<dyn BlockDownloader<B>>,
217	actions: Vec<SyncingAction<B>>,
218	result: Option<WarpSyncResult<B>>,
219	/// Number of peers that need to be connected before warp sync is started.
220	min_peers_to_start_warp_sync: usize,
221}
222
223impl<B> WarpSync<B>
224where
225	B: BlockT,
226{
227	/// Strategy key used by warp sync.
228	pub const STRATEGY_KEY: StrategyKey = StrategyKey::new("Warp");
229
230	/// Create a new instance. When passing a warp sync provider we will be checking for proof and
231	/// authorities. Alternatively we can pass a target block when we want to skip downloading
232	/// proofs, in this case we will continue polling until the target block is known.
233	pub fn new<Client>(
234		client: Arc<Client>,
235		warp_sync_config: WarpSyncConfig<B>,
236		protocol_name: Option<ProtocolName>,
237		block_downloader: Arc<dyn BlockDownloader<B>>,
238		min_peers_to_start_warp_sync: Option<usize>,
239	) -> Self
240	where
241		Client: HeaderBackend<B> + 'static,
242	{
243		let min_peers_to_start_warp_sync =
244			min_peers_to_start_warp_sync.unwrap_or(MIN_PEERS_TO_START_WARP_SYNC);
245		if client.info().finalized_state.is_some() {
246			error!(
247				target: LOG_TARGET,
248				"Can't use warp sync mode with a partially synced database. Reverting to full sync mode."
249			);
250			return Self {
251				phase: Phase::Complete,
252				total_proof_bytes: 0,
253				total_state_bytes: 0,
254				peers: HashMap::new(),
255				disconnected_peers: DisconnectedPeers::new(),
256				protocol_name,
257				block_downloader,
258				actions: vec![SyncingAction::Finished],
259				result: None,
260				min_peers_to_start_warp_sync,
261			};
262		}
263
264		let phase = match warp_sync_config {
265			WarpSyncConfig::WithProvider(warp_sync_provider) => {
266				Phase::WaitingForPeers { warp_sync_provider }
267			},
268			WarpSyncConfig::WithTarget(target_header) => Phase::TargetBlock(target_header),
269		};
270
271		Self {
272			phase,
273			total_proof_bytes: 0,
274			total_state_bytes: 0,
275			peers: HashMap::new(),
276			disconnected_peers: DisconnectedPeers::new(),
277			protocol_name,
278			block_downloader,
279			actions: Vec::new(),
280			result: None,
281			min_peers_to_start_warp_sync,
282		}
283	}
284
285	/// Notify that a new peer has connected.
286	pub fn add_peer(&mut self, peer_id: PeerId, _best_hash: B::Hash, best_number: NumberFor<B>) {
287		self.peers.insert(peer_id, Peer { best_number, state: PeerState::Available });
288
289		self.try_to_start_warp_sync();
290	}
291
292	/// Notify that a peer has disconnected.
293	pub fn remove_peer(&mut self, peer_id: &PeerId) {
294		if let Some(state) = self.peers.remove(peer_id) {
295			if !state.state.is_available() {
296				if let Some(bad_peer) =
297					self.disconnected_peers.on_disconnect_during_request(*peer_id)
298				{
299					self.actions.push(SyncingAction::DropPeer(bad_peer));
300				}
301			}
302		}
303	}
304
305	/// Submit a validated block announcement.
306	///
307	/// Returns new best hash & best number of the peer if they are updated.
308	#[must_use]
309	pub fn on_validated_block_announce(
310		&mut self,
311		is_best: bool,
312		peer_id: PeerId,
313		announce: &BlockAnnounce<B::Header>,
314	) -> Option<(B::Hash, NumberFor<B>)> {
315		is_best.then(|| {
316			let best_number = *announce.header.number();
317			let best_hash = announce.header.hash();
318			if let Some(ref mut peer) = self.peers.get_mut(&peer_id) {
319				peer.best_number = best_number;
320			}
321			// Let `SyncingEngine` know that we should update the peer info.
322			(best_hash, best_number)
323		})
324	}
325
326	/// Start warp sync as soon as we have enough peers.
327	fn try_to_start_warp_sync(&mut self) {
328		let Phase::WaitingForPeers { warp_sync_provider } = &self.phase else { return };
329
330		if self.peers.len() < self.min_peers_to_start_warp_sync {
331			return;
332		}
333
334		let verifier = warp_sync_provider.create_verifier();
335		self.phase = Phase::WarpProof { verifier };
336		debug!(target: LOG_TARGET, "Started warp sync with {} peers.", self.peers.len());
337	}
338
339	pub fn on_generic_response(
340		&mut self,
341		peer_id: &PeerId,
342		protocol_name: ProtocolName,
343		response: Box<dyn Any + Send>,
344	) {
345		if &protocol_name == self.block_downloader.protocol_name() {
346			let Ok(response) = response
347				.downcast::<(BlockRequest<B>, Result<Vec<BlockData<B>>, BlockResponseError>)>()
348			else {
349				warn!(target: LOG_TARGET, "Failed to downcast block response");
350				debug_assert!(false);
351				return;
352			};
353
354			let (request, response) = *response;
355			let blocks = match response {
356				Ok(blocks) => blocks,
357				Err(BlockResponseError::DecodeFailed(e)) => {
358					debug!(
359						target: LOG_TARGET,
360						"Failed to decode block response from peer {:?}: {:?}.",
361						peer_id,
362						e
363					);
364					self.actions.push(SyncingAction::DropPeer(BadPeer(*peer_id, rep::BAD_MESSAGE)));
365					return;
366				},
367				Err(BlockResponseError::ExtractionFailed(e)) => {
368					debug!(
369						target: LOG_TARGET,
370						"Failed to extract blocks from peer response {:?}: {:?}.",
371						peer_id,
372						e
373					);
374					self.actions.push(SyncingAction::DropPeer(BadPeer(*peer_id, rep::BAD_MESSAGE)));
375					return;
376				},
377			};
378
379			self.on_block_response(*peer_id, request, blocks);
380		} else {
381			let Ok(response) = response.downcast::<Vec<u8>>() else {
382				warn!(target: LOG_TARGET, "Failed to downcast warp sync response");
383				debug_assert!(false);
384				return;
385			};
386
387			self.on_warp_proof_response(peer_id, EncodedProof(*response));
388		}
389	}
390
391	/// Process warp proof response.
392	pub fn on_warp_proof_response(&mut self, peer_id: &PeerId, response: EncodedProof) {
393		if let Some(peer) = self.peers.get_mut(peer_id) {
394			peer.state = PeerState::Available;
395		}
396
397		let Phase::WarpProof { verifier } = &mut self.phase else {
398			debug!(target: LOG_TARGET, "Unexpected warp proof response");
399			self.actions
400				.push(SyncingAction::DropPeer(BadPeer(*peer_id, rep::UNEXPECTED_RESPONSE)));
401			return;
402		};
403
404		let proof_to_incoming_block =
405			|(header, justifications): (B::Header, Justifications)| -> IncomingBlock<B> {
406				IncomingBlock {
407					hash: header.hash(),
408					header: Some(header),
409					body: None,
410					justifications: Some(justifications),
411					origin: Some(*peer_id),
412					// We are still in warp sync, so we don't have the state. This means
413					// we also can't execute the block.
414					allow_missing_state: true,
415					skip_execution: true,
416					// Shouldn't already exist in the database.
417					import_existing: false,
418					state: None,
419				}
420			};
421
422		match verifier.verify(&response) {
423			Err(e) => {
424				debug!(target: LOG_TARGET, "Bad warp proof response: {}", e);
425				self.actions
426					.push(SyncingAction::DropPeer(BadPeer(*peer_id, rep::BAD_WARP_PROOF)))
427			},
428			Ok(VerificationResult::Partial(proofs)) => {
429				debug!(target: LOG_TARGET, "Verified partial proof");
430				self.total_proof_bytes += response.0.len() as u64;
431				self.actions.push(SyncingAction::ImportBlocks {
432					origin: BlockOrigin::WarpSync,
433					blocks: proofs.into_iter().map(proof_to_incoming_block).collect(),
434				});
435			},
436			Ok(VerificationResult::Complete(header, proofs)) => {
437				debug!(
438					target: LOG_TARGET,
439					"Verified complete proof. Continuing with target block download: {} ({}).",
440					header.hash(),
441					header.number(),
442				);
443				self.total_proof_bytes += response.0.len() as u64;
444				self.phase = Phase::TargetBlock(header.clone());
445				let incoming_blocks: Vec<_> = proofs
446					.into_iter()
447					.map(proof_to_incoming_block)
448					.filter(|i| {
449						// We need target block with state and warp sync does not provide this.
450						// That's why we filter out target block here, otherwise oncoming state sync
451						// (which comes after warp sync) will abort because target block is already
452						// imported.
453						if header.number() != i.header.as_ref().unwrap().number() {
454							true
455						} else {
456							log::trace!(
457								target: LOG_TARGET,
458								"Filtered out target block: {} ({})",
459								header.hash(),
460								header.number()
461							);
462							false
463						}
464					})
465					.collect();
466				self.actions.push(SyncingAction::ImportBlocks {
467					origin: BlockOrigin::WarpSync,
468					blocks: incoming_blocks,
469				});
470			},
471		}
472	}
473
474	/// Process (target) block response.
475	pub fn on_block_response(
476		&mut self,
477		peer_id: PeerId,
478		request: BlockRequest<B>,
479		blocks: Vec<BlockData<B>>,
480	) {
481		if let Err(bad_peer) = self.on_block_response_inner(peer_id, request, blocks) {
482			self.actions.push(SyncingAction::DropPeer(bad_peer));
483		}
484	}
485
486	fn on_block_response_inner(
487		&mut self,
488		peer_id: PeerId,
489		request: BlockRequest<B>,
490		mut blocks: Vec<BlockData<B>>,
491	) -> Result<(), BadPeer> {
492		if let Some(peer) = self.peers.get_mut(&peer_id) {
493			peer.state = PeerState::Available;
494		}
495
496		let Phase::TargetBlock(header) = &mut self.phase else {
497			debug!(target: LOG_TARGET, "Unexpected target block response from {peer_id}");
498			return Err(BadPeer(peer_id, rep::UNEXPECTED_RESPONSE));
499		};
500
501		if blocks.is_empty() {
502			debug!(
503				target: LOG_TARGET,
504				"Downloading target block failed: empty block response from {peer_id}",
505			);
506			return Err(BadPeer(peer_id, rep::NO_BLOCK));
507		}
508
509		if blocks.len() > 1 {
510			debug!(
511				target: LOG_TARGET,
512				"Too many blocks ({}) in warp target block response from {peer_id}",
513				blocks.len(),
514			);
515			return Err(BadPeer(peer_id, rep::NOT_REQUESTED));
516		}
517
518		validate_blocks::<B>(&blocks, &peer_id, Some(request))?;
519
520		let block = blocks.pop().expect("`blocks` len checked above; qed");
521
522		let Some(block_header) = &block.header else {
523			debug!(
524				target: LOG_TARGET,
525				"Downloading target block failed: missing header in response from {peer_id}.",
526			);
527			return Err(BadPeer(peer_id, rep::VERIFICATION_FAIL));
528		};
529
530		if block_header != header {
531			debug!(
532				target: LOG_TARGET,
533				"Downloading target block failed: different header in response from {peer_id}.",
534			);
535			return Err(BadPeer(peer_id, rep::VERIFICATION_FAIL));
536		}
537
538		if block.body.is_none() {
539			debug!(
540				target: LOG_TARGET,
541				"Downloading target block failed: missing body in response from {peer_id}.",
542			);
543			return Err(BadPeer(peer_id, rep::VERIFICATION_FAIL));
544		}
545
546		self.result = Some(WarpSyncResult {
547			target_header: header.clone(),
548			target_body: block.body,
549			target_justifications: block.justifications,
550		});
551		self.phase = Phase::Complete;
552		self.actions.push(SyncingAction::Finished);
553		Ok(())
554	}
555
556	/// Reserve a peer for a request assigning `new_state`.
557	fn schedule_next_peer(
558		&mut self,
559		new_state: PeerState,
560		min_best_number: Option<NumberFor<B>>,
561	) -> Option<PeerId> {
562		let mut targets: Vec<_> = self.peers.values().map(|p| p.best_number).collect();
563		if targets.is_empty() {
564			return None;
565		}
566		targets.sort();
567		let median = targets[targets.len() / 2];
568		let threshold = std::cmp::max(median, min_best_number.unwrap_or(Zero::zero()));
569		// Find a random peer that is synced as much as peer majority and is above
570		// `min_best_number`.
571		for (peer_id, peer) in self.peers.iter_mut() {
572			if peer.state.is_available() &&
573				peer.best_number >= threshold &&
574				self.disconnected_peers.is_peer_available(peer_id)
575			{
576				peer.state = new_state;
577				return Some(*peer_id);
578			}
579		}
580		None
581	}
582
583	/// Produce warp proof request.
584	fn warp_proof_request(&mut self) -> Option<(PeerId, ProtocolName, WarpProofRequest<B>)> {
585		let Phase::WarpProof { verifier } = &self.phase else { return None };
586
587		// Copy verifier context early to cut the borrowing tie.
588		let begin = verifier.next_proof_context();
589
590		if self
591			.peers
592			.values()
593			.any(|peer| matches!(peer.state, PeerState::DownloadingProofs))
594		{
595			// Only one warp proof request at a time is possible.
596			return None;
597		}
598
599		let peer_id = self.schedule_next_peer(PeerState::DownloadingProofs, None)?;
600		trace!(target: LOG_TARGET, "New WarpProofRequest to {peer_id}, begin hash: {begin}.");
601
602		let request = WarpProofRequest { begin };
603
604		let Some(protocol_name) = self.protocol_name.clone() else {
605			warn!(
606				target: LOG_TARGET,
607				"Trying to send warp sync request when no protocol is configured {request:?}",
608			);
609			return None;
610		};
611
612		Some((peer_id, protocol_name, request))
613	}
614
615	/// Produce target block request.
616	fn target_block_request(&mut self) -> Option<(PeerId, BlockRequest<B>)> {
617		let Phase::TargetBlock(target_header) = &self.phase else { return None };
618
619		if self
620			.peers
621			.values()
622			.any(|peer| matches!(peer.state, PeerState::DownloadingTargetBlock))
623		{
624			// Only one target block request at a time is possible.
625			return None;
626		}
627
628		// Cut the borrowing tie.
629		let target_hash = target_header.hash();
630		let target_number = *target_header.number();
631
632		let peer_id =
633			self.schedule_next_peer(PeerState::DownloadingTargetBlock, Some(target_number))?;
634
635		trace!(
636			target: LOG_TARGET,
637			"New target block request to {peer_id}, target: {} ({}).",
638			target_hash,
639			target_number,
640		);
641
642		Some((
643			peer_id,
644			BlockRequest::<B> {
645				id: 0,
646				fields: BlockAttributes::HEADER |
647					BlockAttributes::BODY |
648					BlockAttributes::JUSTIFICATION,
649				from: FromBlock::Hash(target_hash),
650				direction: Direction::Ascending,
651				max: Some(1),
652			},
653		))
654	}
655
656	/// Returns warp sync estimated progress (stage, bytes received).
657	pub fn progress(&self) -> WarpSyncProgress<B> {
658		match &self.phase {
659			Phase::WaitingForPeers { .. } => WarpSyncProgress {
660				phase: WarpSyncPhase::AwaitingPeers {
661					required_peers: self.min_peers_to_start_warp_sync,
662				},
663				total_bytes: self.total_proof_bytes,
664				status: None,
665			},
666			Phase::WarpProof { verifier } => WarpSyncProgress {
667				phase: WarpSyncPhase::DownloadingWarpProofs,
668				total_bytes: self.total_proof_bytes,
669				status: verifier.status(),
670			},
671			Phase::TargetBlock(_) => WarpSyncProgress {
672				phase: WarpSyncPhase::DownloadingTargetBlock,
673				total_bytes: self.total_proof_bytes,
674				status: None,
675			},
676			Phase::Complete => WarpSyncProgress {
677				phase: WarpSyncPhase::Complete,
678				total_bytes: self.total_proof_bytes + self.total_state_bytes,
679				status: None,
680			},
681		}
682	}
683
684	/// Get the number of peers known to warp sync.
685	pub fn num_peers(&self) -> usize {
686		self.peers.len()
687	}
688
689	/// Returns the current sync status.
690	pub fn status(&self) -> SyncStatus<B> {
691		SyncStatus {
692			state: match &self.phase {
693				Phase::WaitingForPeers { .. } => SyncState::Downloading { target: Zero::zero() },
694				Phase::WarpProof { .. } => SyncState::Downloading { target: Zero::zero() },
695				Phase::TargetBlock(header) => SyncState::Downloading { target: *header.number() },
696				Phase::Complete => SyncState::Idle,
697			},
698			best_seen_block: match &self.phase {
699				Phase::WaitingForPeers { .. } => None,
700				Phase::WarpProof { .. } => None,
701				Phase::TargetBlock(header) => Some(*header.number()),
702				Phase::Complete => None,
703			},
704			num_peers: self.peers.len().saturated_into(),
705			queued_blocks: 0,
706			state_sync: None,
707			warp_sync: Some(self.progress()),
708		}
709	}
710
711	/// Get actions that should be performed by the owner on [`WarpSync`]'s behalf
712	#[must_use]
713	pub fn actions(
714		&mut self,
715		network_service: &NetworkServiceHandle,
716	) -> impl Iterator<Item = SyncingAction<B>> {
717		let warp_proof_request =
718			self.warp_proof_request().into_iter().map(|(peer_id, protocol_name, request)| {
719				trace!(
720					target: LOG_TARGET,
721					"Created `WarpProofRequest` to {}, request: {:?}.",
722					peer_id,
723					request,
724				);
725
726				let (tx, rx) = oneshot::channel();
727
728				network_service.start_request(
729					peer_id,
730					protocol_name,
731					request.encode(),
732					tx,
733					IfDisconnected::ImmediateError,
734				);
735
736				SyncingAction::StartRequest {
737					peer_id,
738					key: Self::STRATEGY_KEY,
739					request: async move {
740						Ok(rx.await?.and_then(|(response, protocol_name)| {
741							Ok((Box::new(response) as Box<dyn Any + Send>, protocol_name))
742						}))
743					}
744					.boxed(),
745				}
746			});
747		self.actions.extend(warp_proof_request);
748
749		let target_block_request =
750			self.target_block_request().into_iter().map(|(peer_id, request)| {
751				let downloader = self.block_downloader.clone();
752
753				SyncingAction::StartRequest {
754					peer_id,
755					key: Self::STRATEGY_KEY,
756					request: async move {
757						Ok(downloader.download_blocks(peer_id, request.clone()).await?.and_then(
758							|(response, protocol_name)| {
759								let decoded_response =
760									downloader.block_response_into_blocks(&request, response);
761								let result =
762									Box::new((request, decoded_response)) as Box<dyn Any + Send>;
763								Ok((result, protocol_name))
764							},
765						))
766					}
767					.boxed(),
768				}
769			});
770		self.actions.extend(target_block_request);
771
772		std::mem::take(&mut self.actions).into_iter()
773	}
774
775	/// Take the result of finished warp sync, returning `None` if the sync was unsuccessful.
776	#[must_use]
777	pub fn take_result(&mut self) -> Option<WarpSyncResult<B>> {
778		self.result.take()
779	}
780}
781
782#[cfg(test)]
783mod test {
784	use super::*;
785	use crate::{mock::MockBlockDownloader, service::network::NetworkServiceProvider};
786	use sc_block_builder::BlockBuilderBuilder;
787	use sp_blockchain::{BlockStatus, Error as BlockchainError, HeaderBackend, Info};
788	use sp_core::H256;
789	use sp_runtime::{
790		traits::{Block as BlockT, Header as HeaderT, NumberFor},
791		ConsensusEngineId,
792	};
793	use std::{io::ErrorKind, sync::Arc};
794	use substrate_test_runtime_client::{
795		runtime::{Block, Hash},
796		BlockBuilderExt, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
797	};
798
799	pub const TEST_ENGINE_ID: ConsensusEngineId = *b"TEST";
800
801	mockall::mock! {
802		pub Client<B: BlockT> {}
803
804		impl<B: BlockT> HeaderBackend<B> for Client<B> {
805			fn header(&self, hash: B::Hash) -> Result<Option<B::Header>, BlockchainError>;
806			fn info(&self) -> Info<B>;
807			fn status(&self, hash: B::Hash) -> Result<BlockStatus, BlockchainError>;
808			fn number(
809				&self,
810				hash: B::Hash,
811			) -> Result<Option<<<B as BlockT>::Header as HeaderT>::Number>, BlockchainError>;
812			fn hash(&self, number: NumberFor<B>) -> Result<Option<B::Hash>, BlockchainError>;
813		}
814	}
815
816	mockall::mock! {
817		pub WarpSyncProvider<B: BlockT> {}
818
819		impl<B: BlockT> super::WarpSyncProvider<B> for WarpSyncProvider<B> {
820			fn generate(
821				&self,
822				start: B::Hash,
823			) -> Result<EncodedProof, Box<dyn std::error::Error + Send + Sync>>;
824			fn create_verifier(&self) -> Box<dyn super::Verifier<B>>;
825		}
826	}
827
828	mockall::mock! {
829		pub Verifier<B: BlockT> {}
830
831		impl<B: BlockT> super::Verifier<B> for Verifier<B> {
832			fn verify(
833				&mut self,
834				proof: &EncodedProof,
835			) -> Result<VerificationResult<B>, Box<dyn std::error::Error + Send + Sync>>;
836			fn next_proof_context(&self) -> B::Hash;
837			fn status(&self) -> Option<String>;
838		}
839	}
840
841	fn mock_client_with_state() -> MockClient<Block> {
842		let mut client = MockClient::<Block>::new();
843		let genesis_hash = Hash::random();
844		client.expect_info().return_once(move || Info {
845			best_hash: genesis_hash,
846			best_number: 0,
847			genesis_hash,
848			finalized_hash: genesis_hash,
849			finalized_number: 0,
850			// We need some finalized state to render warp sync impossible.
851			finalized_state: Some((genesis_hash, 0)),
852			number_leaves: 0,
853			block_gap: None,
854		});
855
856		client
857	}
858
859	fn mock_client_without_state() -> MockClient<Block> {
860		let mut client = MockClient::<Block>::new();
861		let genesis_hash = Hash::random();
862		client.expect_info().returning(move || Info {
863			best_hash: genesis_hash,
864			best_number: 0,
865			genesis_hash,
866			finalized_hash: genesis_hash,
867			finalized_number: 0,
868			finalized_state: None,
869			number_leaves: 0,
870			block_gap: None,
871		});
872
873		client
874	}
875
876	#[test]
877	fn warp_sync_with_provider_for_db_with_finalized_state_is_noop() {
878		let client = mock_client_with_state();
879		let provider = MockWarpSyncProvider::<Block>::new();
880		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
881		let mut warp_sync = WarpSync::new(
882			Arc::new(client),
883			config,
884			None,
885			Arc::new(MockBlockDownloader::new()),
886			None,
887		);
888
889		let network_provider = NetworkServiceProvider::new();
890		let network_handle = network_provider.handle();
891
892		// Warp sync instantly finishes
893		let actions = warp_sync.actions(&network_handle).collect::<Vec<_>>();
894		assert_eq!(actions.len(), 1);
895		assert!(matches!(actions[0], SyncingAction::Finished));
896
897		// ... with no result.
898		assert!(warp_sync.take_result().is_none());
899	}
900
901	#[test]
902	fn warp_sync_to_target_for_db_with_finalized_state_is_noop() {
903		let client = mock_client_with_state();
904		let config = WarpSyncConfig::WithTarget(<Block as BlockT>::Header::new(
905			1,
906			Default::default(),
907			Default::default(),
908			Default::default(),
909			Default::default(),
910		));
911		let mut warp_sync = WarpSync::new(
912			Arc::new(client),
913			config,
914			None,
915			Arc::new(MockBlockDownloader::new()),
916			None,
917		);
918
919		let network_provider = NetworkServiceProvider::new();
920		let network_handle = network_provider.handle();
921
922		// Warp sync instantly finishes
923		let actions = warp_sync.actions(&network_handle).collect::<Vec<_>>();
924		assert_eq!(actions.len(), 1);
925		assert!(matches!(actions[0], SyncingAction::Finished));
926
927		// ... with no result.
928		assert!(warp_sync.take_result().is_none());
929	}
930
931	#[test]
932	fn warp_sync_with_provider_for_empty_db_doesnt_finish_instantly() {
933		let client = mock_client_without_state();
934		let provider = MockWarpSyncProvider::<Block>::new();
935		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
936		let mut warp_sync = WarpSync::new(
937			Arc::new(client),
938			config,
939			None,
940			Arc::new(MockBlockDownloader::new()),
941			None,
942		);
943
944		let network_provider = NetworkServiceProvider::new();
945		let network_handle = network_provider.handle();
946
947		// No actions are emitted.
948		assert_eq!(warp_sync.actions(&network_handle).count(), 0)
949	}
950
951	#[test]
952	fn warp_sync_to_target_for_empty_db_doesnt_finish_instantly() {
953		let client = mock_client_without_state();
954		let config = WarpSyncConfig::WithTarget(<Block as BlockT>::Header::new(
955			1,
956			Default::default(),
957			Default::default(),
958			Default::default(),
959			Default::default(),
960		));
961		let mut warp_sync = WarpSync::new(
962			Arc::new(client),
963			config,
964			None,
965			Arc::new(MockBlockDownloader::new()),
966			None,
967		);
968
969		let network_provider = NetworkServiceProvider::new();
970		let network_handle = network_provider.handle();
971
972		// No actions are emitted.
973		assert_eq!(warp_sync.actions(&network_handle).count(), 0)
974	}
975
976	#[test]
977	fn warp_sync_is_started_only_when_there_is_enough_peers() {
978		let client = mock_client_without_state();
979		let mut provider = MockWarpSyncProvider::<Block>::new();
980		let mut verifier = MockVerifier::<Block>::new();
981		verifier.expect_next_proof_context().returning(|| Hash::random());
982		verifier
983			.expect_verify()
984			.returning(|_| unreachable!("verify should not be called in this test"));
985		provider.expect_create_verifier().return_once(move || Box::new(verifier));
986		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
987		let mut warp_sync = WarpSync::new(
988			Arc::new(client),
989			config,
990			None,
991			Arc::new(MockBlockDownloader::new()),
992			None,
993		);
994
995		// Warp sync is not started when there is not enough peers.
996		for _ in 0..(MIN_PEERS_TO_START_WARP_SYNC - 1) {
997			warp_sync.add_peer(PeerId::random(), Hash::random(), 10);
998			assert!(matches!(warp_sync.phase, Phase::WaitingForPeers { .. }))
999		}
1000
1001		// Now we have enough peers and warp sync is started.
1002		warp_sync.add_peer(PeerId::random(), Hash::random(), 10);
1003		assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }))
1004	}
1005
1006	#[test]
1007	fn no_peer_is_scheduled_if_no_peers_connected() {
1008		let client = mock_client_without_state();
1009		let provider = MockWarpSyncProvider::<Block>::new();
1010		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1011		let mut warp_sync = WarpSync::new(
1012			Arc::new(client),
1013			config,
1014			None,
1015			Arc::new(MockBlockDownloader::new()),
1016			None,
1017		);
1018
1019		assert!(warp_sync.schedule_next_peer(PeerState::DownloadingProofs, None).is_none());
1020	}
1021
1022	#[test]
1023	fn enough_peers_are_used_in_tests() {
1024		// Tests below use 10 peers. Fail early if it's less than a threshold for warp sync.
1025		assert!(
1026			10 >= MIN_PEERS_TO_START_WARP_SYNC,
1027			"Tests must be updated to use that many initial peers.",
1028		);
1029	}
1030
1031	#[test]
1032	fn at_least_median_synced_peer_is_scheduled() {
1033		for _ in 0..100 {
1034			let client = mock_client_without_state();
1035			let mut provider = MockWarpSyncProvider::<Block>::new();
1036			let mut verifier = MockVerifier::<Block>::new();
1037			verifier.expect_next_proof_context().returning(|| Hash::random());
1038			verifier
1039				.expect_verify()
1040				.returning(|_| unreachable!("verify should not be called in this test"));
1041			provider.expect_create_verifier().return_once(move || Box::new(verifier));
1042			let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1043			let mut warp_sync = WarpSync::new(
1044				Arc::new(client),
1045				config,
1046				None,
1047				Arc::new(MockBlockDownloader::new()),
1048				None,
1049			);
1050
1051			for best_number in 1..11 {
1052				warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1053			}
1054
1055			let peer_id = warp_sync.schedule_next_peer(PeerState::DownloadingProofs, None);
1056			assert!(warp_sync.peers.get(&peer_id.unwrap()).unwrap().best_number >= 6);
1057		}
1058	}
1059
1060	#[test]
1061	fn min_best_number_peer_is_scheduled() {
1062		for _ in 0..10 {
1063			let client = mock_client_without_state();
1064			let mut provider = MockWarpSyncProvider::<Block>::new();
1065			let mut verifier = MockVerifier::<Block>::new();
1066			verifier.expect_next_proof_context().returning(|| Hash::random());
1067			verifier
1068				.expect_verify()
1069				.returning(|_| unreachable!("verify should not be called in this test"));
1070			provider.expect_create_verifier().return_once(move || Box::new(verifier));
1071			let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1072			let mut warp_sync = WarpSync::new(
1073				Arc::new(client),
1074				config,
1075				None,
1076				Arc::new(MockBlockDownloader::new()),
1077				None,
1078			);
1079
1080			for best_number in 1..11 {
1081				warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1082			}
1083
1084			let peer_id = warp_sync.schedule_next_peer(PeerState::DownloadingProofs, Some(10));
1085			assert!(warp_sync.peers.get(&peer_id.unwrap()).unwrap().best_number == 10);
1086		}
1087	}
1088
1089	#[test]
1090	fn backedoff_number_peer_is_not_scheduled() {
1091		let client = mock_client_without_state();
1092		let mut provider = MockWarpSyncProvider::<Block>::new();
1093		let mut verifier = MockVerifier::<Block>::new();
1094		verifier.expect_next_proof_context().returning(|| Hash::random());
1095		verifier
1096			.expect_verify()
1097			.returning(|_| unreachable!("verify should not be called in this test"));
1098		provider.expect_create_verifier().return_once(move || Box::new(verifier));
1099		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1100		let mut warp_sync = WarpSync::new(
1101			Arc::new(client),
1102			config,
1103			None,
1104			Arc::new(MockBlockDownloader::new()),
1105			None,
1106		);
1107
1108		for best_number in 1..11 {
1109			warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1110		}
1111
1112		let ninth_peer =
1113			*warp_sync.peers.iter().find(|(_, state)| state.best_number == 9).unwrap().0;
1114		let tenth_peer =
1115			*warp_sync.peers.iter().find(|(_, state)| state.best_number == 10).unwrap().0;
1116
1117		// Disconnecting a peer without an inflight request has no effect on persistent states.
1118		warp_sync.remove_peer(&tenth_peer);
1119		assert!(warp_sync.disconnected_peers.is_peer_available(&tenth_peer));
1120
1121		warp_sync.add_peer(tenth_peer, H256::random(), 10);
1122		let peer_id = warp_sync.schedule_next_peer(PeerState::DownloadingProofs, Some(10));
1123		assert_eq!(tenth_peer, peer_id.unwrap());
1124		warp_sync.remove_peer(&tenth_peer);
1125
1126		// Peer is backed off.
1127		assert!(!warp_sync.disconnected_peers.is_peer_available(&tenth_peer));
1128
1129		// No peer available for 10'th best block because of the backoff.
1130		warp_sync.add_peer(tenth_peer, H256::random(), 10);
1131		let peer_id: Option<PeerId> =
1132			warp_sync.schedule_next_peer(PeerState::DownloadingProofs, Some(10));
1133		assert!(peer_id.is_none());
1134
1135		// Other requests can still happen.
1136		let peer_id: Option<PeerId> =
1137			warp_sync.schedule_next_peer(PeerState::DownloadingProofs, Some(9));
1138		assert_eq!(ninth_peer, peer_id.unwrap());
1139	}
1140
1141	#[test]
1142	fn no_warp_proof_request_in_another_phase() {
1143		let client = mock_client_without_state();
1144		let mut provider = MockWarpSyncProvider::<Block>::new();
1145		let mut verifier = MockVerifier::<Block>::new();
1146		verifier.expect_next_proof_context().returning(|| Hash::random());
1147		verifier
1148			.expect_verify()
1149			.returning(|_| unreachable!("verify should not be called in this test"));
1150		provider.expect_create_verifier().return_once(move || Box::new(verifier));
1151		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1152		let mut warp_sync = WarpSync::new(
1153			Arc::new(client),
1154			config,
1155			Some(ProtocolName::Static("")),
1156			Arc::new(MockBlockDownloader::new()),
1157			None,
1158		);
1159
1160		// Make sure we have enough peers to make a request.
1161		for best_number in 1..11 {
1162			warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1163		}
1164
1165		// Manually set to another phase.
1166		warp_sync.phase = Phase::TargetBlock(<Block as BlockT>::Header::new(
1167			1,
1168			Default::default(),
1169			Default::default(),
1170			Default::default(),
1171			Default::default(),
1172		));
1173
1174		// No request is made.
1175		assert!(warp_sync.warp_proof_request().is_none());
1176	}
1177
1178	#[test]
1179	fn warp_proof_request_starts_at_last_hash() {
1180		let client = mock_client_without_state();
1181		let mut provider = MockWarpSyncProvider::<Block>::new();
1182		let mut verifier = MockVerifier::<Block>::new();
1183		let known_last_hash = Hash::random();
1184		verifier.expect_next_proof_context().returning(move || known_last_hash);
1185		verifier
1186			.expect_verify()
1187			.returning(|_| unreachable!("verify should not be called in this test"));
1188		provider.expect_create_verifier().return_once(move || Box::new(verifier));
1189		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1190		let mut warp_sync = WarpSync::new(
1191			Arc::new(client),
1192			config,
1193			Some(ProtocolName::Static("")),
1194			Arc::new(MockBlockDownloader::new()),
1195			None,
1196		);
1197
1198		// Make sure we have enough peers to make a request.
1199		for best_number in 1..11 {
1200			warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1201		}
1202		assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }));
1203
1204		let (_peer_id, _protocol_name, request) = warp_sync.warp_proof_request().unwrap();
1205		assert_eq!(request.begin, known_last_hash);
1206	}
1207
1208	#[test]
1209	fn no_parallel_warp_proof_requests() {
1210		let client = mock_client_without_state();
1211		let mut provider = MockWarpSyncProvider::<Block>::new();
1212		let mut verifier = MockVerifier::<Block>::new();
1213		verifier.expect_next_proof_context().returning(|| Hash::random());
1214		verifier
1215			.expect_verify()
1216			.returning(|_| unreachable!("verify should not be called in this test"));
1217		provider.expect_create_verifier().return_once(move || Box::new(verifier));
1218		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1219		let mut warp_sync = WarpSync::new(
1220			Arc::new(client),
1221			config,
1222			Some(ProtocolName::Static("")),
1223			Arc::new(MockBlockDownloader::new()),
1224			None,
1225		);
1226
1227		// Make sure we have enough peers to make requests.
1228		for best_number in 1..11 {
1229			warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1230		}
1231		assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }));
1232
1233		// First request is made.
1234		assert!(warp_sync.warp_proof_request().is_some());
1235		// Second request is not made.
1236		assert!(warp_sync.warp_proof_request().is_none());
1237	}
1238
1239	#[test]
1240	fn bad_warp_proof_response_drops_peer() {
1241		let client = mock_client_without_state();
1242		let mut provider = MockWarpSyncProvider::<Block>::new();
1243		let mut verifier = MockVerifier::<Block>::new();
1244		verifier.expect_next_proof_context().returning(|| Hash::random());
1245		// Warp proof verification fails.
1246		verifier.expect_verify().return_once(|_proof| {
1247			Err(Box::new(std::io::Error::new(ErrorKind::Other, "test-verification-failure")))
1248		});
1249		provider.expect_create_verifier().return_once(move || Box::new(verifier));
1250		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1251		let mut warp_sync = WarpSync::new(
1252			Arc::new(client),
1253			config,
1254			Some(ProtocolName::Static("")),
1255			Arc::new(MockBlockDownloader::new()),
1256			None,
1257		);
1258
1259		// Make sure we have enough peers to make a request.
1260		for best_number in 1..11 {
1261			warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1262		}
1263		assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }));
1264
1265		let network_provider = NetworkServiceProvider::new();
1266		let network_handle = network_provider.handle();
1267
1268		// Consume `SendWarpProofRequest` action.
1269		let actions = warp_sync.actions(&network_handle).collect::<Vec<_>>();
1270		assert_eq!(actions.len(), 1);
1271		let SyncingAction::StartRequest { peer_id: request_peer_id, .. } = actions[0] else {
1272			panic!("Invalid action");
1273		};
1274
1275		warp_sync.on_warp_proof_response(&request_peer_id, EncodedProof(Vec::new()));
1276
1277		// We only interested in already generated actions, not new requests.
1278		let actions = std::mem::take(&mut warp_sync.actions);
1279		assert_eq!(actions.len(), 1);
1280		assert!(matches!(
1281			actions[0],
1282			SyncingAction::DropPeer(BadPeer(peer_id, _rep)) if peer_id == request_peer_id
1283		));
1284		assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }));
1285	}
1286
1287	#[test]
1288	fn partial_warp_proof_doesnt_advance_phase() {
1289		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1290		let mut provider = MockWarpSyncProvider::<Block>::new();
1291		let target_block = BlockBuilderBuilder::new(&*client)
1292			.on_parent_block(client.chain_info().best_hash)
1293			.with_parent_block_number(client.chain_info().best_number)
1294			.build()
1295			.unwrap()
1296			.build()
1297			.unwrap()
1298			.block;
1299		let target_header = target_block.header().clone();
1300		let justifications = Justifications::new(vec![(TEST_ENGINE_ID, vec![1, 2, 3, 4, 5])]);
1301		// Warp proof is partial.
1302		let mut verifier = MockVerifier::<Block>::new();
1303		let context = client.info().genesis_hash;
1304		verifier.expect_next_proof_context().returning(move || context);
1305		let header_for_verify = target_header.clone();
1306		let just_for_verify = justifications.clone();
1307		verifier.expect_verify().return_once(move |_proof| {
1308			Ok(VerificationResult::Partial(vec![(
1309				header_for_verify.clone(),
1310				just_for_verify.clone(),
1311			)]))
1312		});
1313		provider.expect_create_verifier().return_once(move || Box::new(verifier));
1314		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1315		let mut warp_sync = WarpSync::new(
1316			client,
1317			config,
1318			Some(ProtocolName::Static("")),
1319			Arc::new(MockBlockDownloader::new()),
1320			None,
1321		);
1322
1323		// Make sure we have enough peers to make a request.
1324		for best_number in 1..11 {
1325			warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1326		}
1327		assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }));
1328
1329		let network_provider = NetworkServiceProvider::new();
1330		let network_handle = network_provider.handle();
1331
1332		// Consume `SendWarpProofRequest` action.
1333		let actions = warp_sync.actions(&network_handle).collect::<Vec<_>>();
1334		assert_eq!(actions.len(), 1);
1335		let SyncingAction::StartRequest { peer_id: request_peer_id, .. } = actions[0] else {
1336			panic!("Invalid action");
1337		};
1338
1339		warp_sync.on_warp_proof_response(&request_peer_id, EncodedProof(Vec::new()));
1340
1341		assert_eq!(warp_sync.actions.len(), 1);
1342		let SyncingAction::ImportBlocks { origin, mut blocks } = warp_sync.actions.pop().unwrap()
1343		else {
1344			panic!("Expected `ImportBlocks` action.");
1345		};
1346		assert_eq!(origin, BlockOrigin::WarpSync);
1347		assert_eq!(blocks.len(), 1);
1348		let import_block = blocks.pop().unwrap();
1349		assert_eq!(
1350			import_block,
1351			IncomingBlock {
1352				hash: target_header.hash(),
1353				header: Some(target_header),
1354				body: None,
1355				justifications: Some(justifications),
1356				origin: Some(request_peer_id),
1357				allow_missing_state: true,
1358				skip_execution: true,
1359				import_existing: false,
1360				state: None,
1361			}
1362		);
1363		assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }));
1364	}
1365
1366	#[test]
1367	fn complete_warp_proof_advances_phase() {
1368		use crate::{pending_responses::PendingResponses, service::network::ToServiceCommand};
1369		use futures::{executor::block_on, StreamExt};
1370
1371		// Initialize logging
1372		sp_tracing::try_init_simple();
1373
1374		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1375		let mut provider = MockWarpSyncProvider::<Block>::new();
1376
1377		// Building both blocks manually. Can't use BlockBuilderBuilder to build one block on top of
1378		// another, if no genesis is set (required for warp sync to make sure finalized_state is not
1379		// set)
1380		let warp_synced_header = <<Block as BlockT>::Header as HeaderT>::new(
1381			1,
1382			Default::default(),
1383			Default::default(),
1384			client.chain_info().best_hash,
1385			Default::default(),
1386		);
1387
1388		// Build target_block on top of warp_synced_block
1389		let target_header = <<Block as BlockT>::Header as HeaderT>::new(
1390			2,
1391			Default::default(),
1392			Default::default(),
1393			warp_synced_header.hash(),
1394			Default::default(),
1395		);
1396		let warp_justifications = Justifications::new(vec![(TEST_ENGINE_ID, vec![1, 2, 3, 4, 5])]);
1397		let target_justifications =
1398			Justifications::new(vec![(TEST_ENGINE_ID, vec![6, 7, 8, 9, 10])]);
1399
1400		// Warp proof is complete and contains both blocks.
1401		let mut verifier = MockVerifier::<Block>::new();
1402		let context = client.info().genesis_hash;
1403		verifier.expect_next_proof_context().returning(move || context);
1404		let warp_synced_header_for_verify = warp_synced_header.clone();
1405		let warp_just_for_verify = warp_justifications.clone();
1406		let target_header_for_verify = target_header.clone();
1407		let target_just_for_verify = target_justifications.clone();
1408		verifier.expect_verify().return_once(move |_proof| {
1409			Ok(VerificationResult::Complete(
1410				target_header_for_verify.clone(),
1411				vec![
1412					(warp_synced_header_for_verify, warp_just_for_verify),
1413					(target_header_for_verify, target_just_for_verify),
1414				],
1415			))
1416		});
1417		provider.expect_create_verifier().return_once(move || Box::new(verifier));
1418		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1419		let mut warp_sync = WarpSync::new(
1420			client,
1421			config,
1422			Some(ProtocolName::Static("")),
1423			Arc::new(MockBlockDownloader::new()),
1424			Some(1),
1425		);
1426
1427		// One peer must serve both the proof and the subsequent target-block request.
1428		let peer_id = PeerId::random();
1429		warp_sync.add_peer(peer_id, Hash::random(), 10);
1430		assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }));
1431
1432		let (tx, mut rx) = sc_utils::mpsc::tracing_unbounded("warp_test", 10);
1433		let network_handle = NetworkServiceHandle::new(tx);
1434		let mut pending = PendingResponses::new();
1435		let mut actions = warp_sync.actions(&network_handle).collect::<Vec<_>>();
1436		assert_eq!(actions.len(), 1);
1437		let SyncingAction::StartRequest { peer_id: request_peer_id, key, request } =
1438			actions.pop().unwrap()
1439		else {
1440			panic!("Expected warp proof request.");
1441		};
1442		assert_eq!(request_peer_id, peer_id);
1443		pending.insert(request_peer_id, key, request);
1444		let ToServiceCommand::StartRequest(_, protocol, _, response_tx, _) =
1445			block_on(rx.next()).unwrap()
1446		else {
1447			panic!("Expected network request.");
1448		};
1449		response_tx.send(Ok((Vec::new(), protocol))).unwrap();
1450		let event = block_on(pending.next()).unwrap();
1451		// Receiving the proof already frees this peer's request slot.
1452		assert_eq!(pending.len(), 0);
1453		let (response, _) = event.response.unwrap().unwrap();
1454		warp_sync.on_warp_proof_response(
1455			&event.peer_id,
1456			EncodedProof(*response.downcast::<Vec<u8>>().unwrap()),
1457		);
1458
1459		assert_eq!(warp_sync.actions.len(), 1);
1460		let SyncingAction::ImportBlocks { origin, mut blocks } = warp_sync.actions.pop().unwrap()
1461		else {
1462			panic!("Expected `ImportBlocks` action.");
1463		};
1464		assert_eq!(origin, BlockOrigin::WarpSync);
1465		// Only warp_synced_block should be in the import list; target_block is filtered out
1466		assert_eq!(blocks.len(), 1);
1467		let import_block = blocks.pop().unwrap();
1468		assert_eq!(
1469			import_block,
1470			IncomingBlock {
1471				hash: warp_synced_header.hash(),
1472				header: Some(warp_synced_header),
1473				body: None,
1474				justifications: Some(warp_justifications),
1475				origin: Some(request_peer_id),
1476				allow_missing_state: true,
1477				skip_execution: true,
1478				import_existing: false,
1479				state: None,
1480			}
1481		);
1482		assert!(matches!(&warp_sync.phase, Phase::TargetBlock(header) if *header == target_header));
1483
1484		let mut actions = warp_sync.actions(&network_handle).collect::<Vec<_>>();
1485		assert_eq!(actions.len(), 1);
1486		let SyncingAction::StartRequest { peer_id: target_peer, key, request } =
1487			actions.pop().unwrap()
1488		else {
1489			panic!("Expected target block request.");
1490		};
1491		assert_eq!(target_peer, request_peer_id);
1492		// No explicit cancellation or implicit removal is needed between these requests.
1493		pending.insert(target_peer, key, request);
1494		assert_eq!(pending.len(), 1);
1495		assert_eq!(warp_sync.actions(&network_handle).count(), 0);
1496	}
1497
1498	#[test]
1499	fn no_target_block_requests_in_another_phase() {
1500		let client = mock_client_without_state();
1501		let mut provider = MockWarpSyncProvider::<Block>::new();
1502		let mut verifier = MockVerifier::<Block>::new();
1503		verifier.expect_next_proof_context().returning(|| Hash::random());
1504		verifier
1505			.expect_verify()
1506			.returning(|_| unreachable!("verify should not be called in this test"));
1507		provider.expect_create_verifier().return_once(move || Box::new(verifier));
1508		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1509		let mut warp_sync = WarpSync::new(
1510			Arc::new(client),
1511			config,
1512			None,
1513			Arc::new(MockBlockDownloader::new()),
1514			None,
1515		);
1516
1517		// Make sure we have enough peers to make a request.
1518		for best_number in 1..11 {
1519			warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1520		}
1521		// We are not in `Phase::TargetBlock`
1522		assert!(matches!(warp_sync.phase, Phase::WarpProof { .. }));
1523
1524		// No request is made.
1525		assert!(warp_sync.target_block_request().is_none());
1526	}
1527
1528	#[test]
1529	fn target_block_request_is_correct() {
1530		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1531		let mut provider = MockWarpSyncProvider::<Block>::new();
1532		let mut verifier = MockVerifier::<Block>::new();
1533		let header_for_ctx = client.info().genesis_hash;
1534		verifier.expect_next_proof_context().returning(move || header_for_ctx);
1535		let target_block = BlockBuilderBuilder::new(&*client)
1536			.on_parent_block(client.chain_info().best_hash)
1537			.with_parent_block_number(client.chain_info().best_number)
1538			.build()
1539			.unwrap()
1540			.build()
1541			.unwrap()
1542			.block;
1543		let target_header = target_block.header().clone();
1544		// Warp proof is complete.
1545		let header_for_verify = target_header.clone();
1546		verifier.expect_verify().return_once(move |_proof| {
1547			Ok(VerificationResult::Complete(header_for_verify, Default::default()))
1548		});
1549		provider.expect_create_verifier().return_once(move || Box::new(verifier));
1550		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1551		let mut warp_sync =
1552			WarpSync::new(client, config, None, Arc::new(MockBlockDownloader::new()), None);
1553
1554		// Make sure we have enough peers to make a request.
1555		for best_number in 1..11 {
1556			warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1557		}
1558
1559		// Manually set `TargetBlock` phase.
1560		warp_sync.phase = Phase::TargetBlock(target_block.header().clone());
1561
1562		let (_peer_id, request) = warp_sync.target_block_request().unwrap();
1563		assert_eq!(request.from, FromBlock::Hash(target_block.header().hash()));
1564		assert_eq!(
1565			request.fields,
1566			BlockAttributes::HEADER | BlockAttributes::BODY | BlockAttributes::JUSTIFICATION
1567		);
1568		assert_eq!(request.max, Some(1));
1569	}
1570
1571	#[test]
1572	fn externally_set_target_block_is_requested() {
1573		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1574		let target_block = BlockBuilderBuilder::new(&*client)
1575			.on_parent_block(client.chain_info().best_hash)
1576			.with_parent_block_number(client.chain_info().best_number)
1577			.build()
1578			.unwrap()
1579			.build()
1580			.unwrap()
1581			.block;
1582		let target_header = target_block.header().clone();
1583		let config = WarpSyncConfig::WithTarget(target_header);
1584		let mut warp_sync =
1585			WarpSync::new(client, config, None, Arc::new(MockBlockDownloader::new()), None);
1586
1587		// Make sure we have enough peers to make a request.
1588		for best_number in 1..11 {
1589			warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1590		}
1591
1592		assert!(matches!(warp_sync.phase, Phase::TargetBlock(_)));
1593
1594		let (_peer_id, request) = warp_sync.target_block_request().unwrap();
1595		assert_eq!(request.from, FromBlock::Hash(target_block.header().hash()));
1596		assert_eq!(
1597			request.fields,
1598			BlockAttributes::HEADER | BlockAttributes::BODY | BlockAttributes::JUSTIFICATION
1599		);
1600		assert_eq!(request.max, Some(1));
1601	}
1602
1603	#[test]
1604	fn no_parallel_target_block_requests() {
1605		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1606		let mut provider = MockWarpSyncProvider::<Block>::new();
1607		let mut verifier = MockVerifier::<Block>::new();
1608		let header_for_ctx = client.info().genesis_hash;
1609		verifier.expect_next_proof_context().returning(move || header_for_ctx);
1610		let target_block = BlockBuilderBuilder::new(&*client)
1611			.on_parent_block(client.chain_info().best_hash)
1612			.with_parent_block_number(client.chain_info().best_number)
1613			.build()
1614			.unwrap()
1615			.build()
1616			.unwrap()
1617			.block;
1618		let target_header = target_block.header().clone();
1619		// Warp proof is complete.
1620		let header_for_verify = target_header.clone();
1621		verifier.expect_verify().return_once(move |_proof| {
1622			Ok(VerificationResult::Complete(header_for_verify, Default::default()))
1623		});
1624		provider.expect_create_verifier().return_once(move || Box::new(verifier));
1625		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1626		let mut warp_sync =
1627			WarpSync::new(client, config, None, Arc::new(MockBlockDownloader::new()), None);
1628
1629		// Make sure we have enough peers to make a request.
1630		for best_number in 1..11 {
1631			warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1632		}
1633
1634		// Manually set `TargetBlock` phase.
1635		warp_sync.phase = Phase::TargetBlock(target_block.header().clone());
1636
1637		// First target block request is made.
1638		assert!(warp_sync.target_block_request().is_some());
1639		// No parallel request is made.
1640		assert!(warp_sync.target_block_request().is_none());
1641	}
1642
1643	#[test]
1644	fn target_block_response_with_no_blocks_drops_peer() {
1645		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1646		let mut provider = MockWarpSyncProvider::<Block>::new();
1647		let mut verifier = MockVerifier::<Block>::new();
1648		let header_for_ctx = client.info().genesis_hash;
1649		verifier.expect_next_proof_context().returning(move || header_for_ctx);
1650		let target_block = BlockBuilderBuilder::new(&*client)
1651			.on_parent_block(client.chain_info().best_hash)
1652			.with_parent_block_number(client.chain_info().best_number)
1653			.build()
1654			.unwrap()
1655			.build()
1656			.unwrap()
1657			.block;
1658		let target_header = target_block.header().clone();
1659		// Warp proof is complete.
1660		let header_for_verify = target_header.clone();
1661		verifier.expect_verify().return_once(move |_proof| {
1662			Ok(VerificationResult::Complete(header_for_verify, Default::default()))
1663		});
1664		provider.expect_create_verifier().return_once(move || Box::new(verifier));
1665		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1666		let mut warp_sync =
1667			WarpSync::new(client, config, None, Arc::new(MockBlockDownloader::new()), None);
1668
1669		// Make sure we have enough peers to make a request.
1670		for best_number in 1..11 {
1671			warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1672		}
1673
1674		// Manually set `TargetBlock` phase.
1675		warp_sync.phase = Phase::TargetBlock(target_block.header().clone());
1676
1677		let (peer_id, request) = warp_sync.target_block_request().unwrap();
1678
1679		// Empty block response received.
1680		let response = Vec::new();
1681		// Peer is dropped.
1682		assert!(matches!(
1683			warp_sync.on_block_response_inner(peer_id, request, response),
1684			Err(BadPeer(id, _rep)) if id == peer_id,
1685		));
1686	}
1687
1688	#[test]
1689	fn target_block_response_with_extra_blocks_drops_peer() {
1690		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1691		let mut provider = MockWarpSyncProvider::<Block>::new();
1692		let mut verifier = MockVerifier::<Block>::new();
1693		let header_for_ctx = client.info().genesis_hash;
1694		verifier.expect_next_proof_context().returning(move || header_for_ctx);
1695		let target_block = BlockBuilderBuilder::new(&*client)
1696			.on_parent_block(client.chain_info().best_hash)
1697			.with_parent_block_number(client.chain_info().best_number)
1698			.build()
1699			.unwrap()
1700			.build()
1701			.unwrap()
1702			.block;
1703
1704		let mut extra_block_builder = BlockBuilderBuilder::new(&*client)
1705			.on_parent_block(client.chain_info().best_hash)
1706			.with_parent_block_number(client.chain_info().best_number)
1707			.build()
1708			.unwrap();
1709		extra_block_builder
1710			.push_storage_change(vec![1, 2, 3], Some(vec![4, 5, 6]))
1711			.unwrap();
1712		let extra_block = extra_block_builder.build().unwrap().block;
1713
1714		let target_header = target_block.header().clone();
1715		// Warp proof is complete.
1716		let header_for_verify = target_header.clone();
1717		verifier.expect_verify().return_once(move |_proof| {
1718			Ok(VerificationResult::Complete(header_for_verify, Default::default()))
1719		});
1720		provider.expect_create_verifier().return_once(move || Box::new(verifier));
1721		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1722		let mut warp_sync =
1723			WarpSync::new(client, config, None, Arc::new(MockBlockDownloader::new()), None);
1724
1725		// Make sure we have enough peers to make a request.
1726		for best_number in 1..11 {
1727			warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1728		}
1729
1730		// Manually set `TargetBlock` phase.
1731		warp_sync.phase = Phase::TargetBlock(target_block.header().clone());
1732
1733		let (peer_id, request) = warp_sync.target_block_request().unwrap();
1734
1735		// Block response with extra blocks received.
1736		let response = vec![
1737			BlockData::<Block> {
1738				hash: target_block.header().hash(),
1739				header: Some(target_block.header().clone()),
1740				body: Some(target_block.extrinsics().iter().cloned().collect::<Vec<_>>()),
1741				receipt: None,
1742				message_queue: None,
1743				justification: None,
1744				justifications: None,
1745			},
1746			BlockData::<Block> {
1747				hash: extra_block.header().hash(),
1748				header: Some(extra_block.header().clone()),
1749				body: Some(extra_block.extrinsics().iter().cloned().collect::<Vec<_>>()),
1750				receipt: None,
1751				message_queue: None,
1752				justification: None,
1753				justifications: None,
1754			},
1755		];
1756		// Peer is dropped.
1757		assert!(matches!(
1758			warp_sync.on_block_response_inner(peer_id, request, response),
1759			Err(BadPeer(id, _rep)) if id == peer_id,
1760		));
1761	}
1762
1763	#[test]
1764	fn target_block_response_with_wrong_block_drops_peer() {
1765		sp_tracing::try_init_simple();
1766
1767		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1768		let mut provider = MockWarpSyncProvider::<Block>::new();
1769		let mut verifier = MockVerifier::<Block>::new();
1770		let header_for_ctx = client.info().genesis_hash;
1771		verifier.expect_next_proof_context().returning(move || header_for_ctx);
1772		let target_block = BlockBuilderBuilder::new(&*client)
1773			.on_parent_block(client.chain_info().best_hash)
1774			.with_parent_block_number(client.chain_info().best_number)
1775			.build()
1776			.unwrap()
1777			.build()
1778			.unwrap()
1779			.block;
1780
1781		let mut wrong_block_builder = BlockBuilderBuilder::new(&*client)
1782			.on_parent_block(client.chain_info().best_hash)
1783			.with_parent_block_number(client.chain_info().best_number)
1784			.build()
1785			.unwrap();
1786		wrong_block_builder
1787			.push_storage_change(vec![1, 2, 3], Some(vec![4, 5, 6]))
1788			.unwrap();
1789		let wrong_block = wrong_block_builder.build().unwrap().block;
1790
1791		let target_header = target_block.header().clone();
1792		// Warp proof is complete.
1793		let header_for_verify = target_header.clone();
1794		verifier.expect_verify().return_once(move |_proof| {
1795			Ok(VerificationResult::Complete(header_for_verify, Default::default()))
1796		});
1797		provider.expect_create_verifier().return_once(move || Box::new(verifier));
1798		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1799		let mut warp_sync =
1800			WarpSync::new(client, config, None, Arc::new(MockBlockDownloader::new()), None);
1801
1802		// Make sure we have enough peers to make a request.
1803		for best_number in 1..11 {
1804			warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1805		}
1806
1807		// Manually set `TargetBlock` phase.
1808		warp_sync.phase = Phase::TargetBlock(target_block.header().clone());
1809
1810		let (peer_id, request) = warp_sync.target_block_request().unwrap();
1811
1812		// Wrong block received.
1813		let response = vec![BlockData::<Block> {
1814			hash: wrong_block.header().hash(),
1815			header: Some(wrong_block.header().clone()),
1816			body: Some(wrong_block.extrinsics().iter().cloned().collect::<Vec<_>>()),
1817			receipt: None,
1818			message_queue: None,
1819			justification: None,
1820			justifications: None,
1821		}];
1822		// Peer is dropped.
1823		assert!(matches!(
1824			warp_sync.on_block_response_inner(peer_id, request, response),
1825			Err(BadPeer(id, _rep)) if id == peer_id,
1826		));
1827	}
1828
1829	#[test]
1830	fn correct_target_block_response_sets_strategy_result() {
1831		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
1832		let mut provider = MockWarpSyncProvider::<Block>::new();
1833		let mut verifier = MockVerifier::<Block>::new();
1834		let header_for_ctx = client.info().genesis_hash;
1835		verifier.expect_next_proof_context().returning(move || header_for_ctx);
1836		let mut target_block_builder = BlockBuilderBuilder::new(&*client)
1837			.on_parent_block(client.chain_info().best_hash)
1838			.with_parent_block_number(client.chain_info().best_number)
1839			.build()
1840			.unwrap();
1841		target_block_builder
1842			.push_storage_change(vec![1, 2, 3], Some(vec![4, 5, 6]))
1843			.unwrap();
1844		let target_block = target_block_builder.build().unwrap().block;
1845		let target_header = target_block.header().clone();
1846		// Warp proof is complete.
1847		let header_for_verify = target_header.clone();
1848		verifier.expect_verify().return_once(move |_proof| {
1849			Ok(VerificationResult::Complete(header_for_verify, Default::default()))
1850		});
1851		provider.expect_create_verifier().return_once(move || Box::new(verifier));
1852		let config = WarpSyncConfig::WithProvider(Arc::new(provider));
1853		let mut warp_sync =
1854			WarpSync::new(client, config, None, Arc::new(MockBlockDownloader::new()), None);
1855
1856		// Make sure we have enough peers to make a request.
1857		for best_number in 1..11 {
1858			warp_sync.add_peer(PeerId::random(), Hash::random(), best_number);
1859		}
1860
1861		// Manually set `TargetBlock` phase.
1862		warp_sync.phase = Phase::TargetBlock(target_block.header().clone());
1863
1864		let (peer_id, request) = warp_sync.target_block_request().unwrap();
1865
1866		// Correct block received.
1867		let body = Some(target_block.extrinsics().iter().cloned().collect::<Vec<_>>());
1868		let justifications = Some(Justifications::from((*b"FRNK", Vec::new())));
1869		let response = vec![BlockData::<Block> {
1870			hash: target_block.header().hash(),
1871			header: Some(target_block.header().clone()),
1872			body: body.clone(),
1873			receipt: None,
1874			message_queue: None,
1875			justification: None,
1876			justifications: justifications.clone(),
1877		}];
1878
1879		assert!(warp_sync.on_block_response_inner(peer_id, request, response).is_ok());
1880
1881		let network_provider = NetworkServiceProvider::new();
1882		let network_handle = network_provider.handle();
1883
1884		// Strategy finishes.
1885		let actions = warp_sync.actions(&network_handle).collect::<Vec<_>>();
1886		assert_eq!(actions.len(), 1);
1887		assert!(matches!(actions[0], SyncingAction::Finished));
1888
1889		// With correct result.
1890		let result = warp_sync.take_result().unwrap();
1891		assert_eq!(result.target_header, *target_block.header());
1892		assert_eq!(result.target_body, body);
1893		assert_eq!(result.target_justifications, justifications);
1894	}
1895}