referrerpolicy=no-referrer-when-downgrade

sc_network_sync/strategy/
chain_sync.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//! Contains the state of the chain synchronization process
20//!
21//! At any given point in time, a running node tries as much as possible to be at the head of the
22//! chain. This module handles the logic of which blocks to request from remotes, and processing
23//! responses. It yields blocks to check and potentially move to the database.
24//!
25//! # Usage
26//!
27//! The `ChainSync` struct maintains the state of the block requests. Whenever something happens on
28//! the network, or whenever a block has been successfully verified, call the appropriate method in
29//! order to update it.
30
31use crate::{
32	block_relay_protocol::{BlockDownloader, BlockResponseError},
33	blocks::{BlockCollection, Metrics as BlockCollectionMetrics},
34	justification_requests::ExtraRequests,
35	schema::v1::{StateRequest, StateResponse},
36	service::network::NetworkServiceHandle,
37	strategy::{
38		disconnected_peers::DisconnectedPeers,
39		state_sync::{ImportResult, StateSync, StateSyncProvider},
40		warp::{WarpSyncPhase, WarpSyncProgress},
41		StrategyKey, SyncingAction, SyncingStrategy,
42	},
43	types::{BadPeer, SyncState, SyncStatus},
44	LOG_TARGET,
45};
46
47use codec::Encode;
48use futures::{channel::oneshot, FutureExt};
49use log::{debug, error, info, trace, warn};
50use prometheus_endpoint::{register, Counter, Gauge, PrometheusError, Registry, U64};
51use prost::Message;
52use sc_client_api::{blockchain::BlockGap, BlockBackend, ProofProvider};
53use sc_consensus::{BlockImportError, BlockImportStatus, IncomingBlock};
54use sc_network::{IfDisconnected, ProtocolName};
55use sc_network_common::sync::message::{
56	BlockAnnounce, BlockAttributes, BlockData, BlockRequest, BlockResponse, Direction, FromBlock,
57};
58use sc_network_types::PeerId;
59use sp_arithmetic::traits::Saturating;
60use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata};
61use sp_consensus::{BlockOrigin, BlockStatus};
62use sp_runtime::{
63	traits::{
64		Block as BlockT, CheckedSub, Header as HeaderT, NumberFor, One, SaturatedConversion, Zero,
65	},
66	EncodedJustification, Justifications,
67};
68
69use std::{
70	any::Any,
71	collections::{HashMap, HashSet},
72	fmt,
73	ops::{AddAssign, Range},
74	sync::Arc,
75};
76
77#[cfg(test)]
78mod test;
79
80/// Maximum blocks to store in the import queue.
81const MAX_IMPORTING_BLOCKS: usize = 2048;
82
83/// Maximum blocks to download ahead of any gap.
84const MAX_DOWNLOAD_AHEAD: u32 = 2048;
85
86/// Maximum blocks to look backwards. The gap is the difference between the highest block and the
87/// common block of a node.
88const MAX_BLOCKS_TO_LOOK_BACKWARDS: u32 = MAX_DOWNLOAD_AHEAD / 2;
89
90/// Pick the state to sync as the latest finalized number minus this.
91const STATE_SYNC_FINALITY_THRESHOLD: u32 = 8;
92
93/// We use a heuristic that with a high likelihood, by the time
94/// `MAJOR_SYNC_BLOCKS` have been imported we'll be on the same
95/// chain as (or at least closer to) the peer so we want to delay
96/// the ancestor search to not waste time doing that when we are
97/// so far behind.
98const MAJOR_SYNC_BLOCKS: u8 = 5;
99
100mod rep {
101	use sc_network::ReputationChange as Rep;
102	/// Reputation change when a peer sent us a message that led to a
103	/// database read error.
104	pub const BLOCKCHAIN_READ_ERROR: Rep = Rep::new(-(1 << 16), "DB Error");
105
106	/// Reputation change when a peer sent us a status message with a different
107	/// genesis than us.
108	pub const GENESIS_MISMATCH: Rep = Rep::new(i32::MIN, "Genesis mismatch");
109
110	/// Reputation change for peers which send us a block with an incomplete header.
111	pub const INCOMPLETE_HEADER: Rep = Rep::new(-(1 << 20), "Incomplete header");
112
113	/// Reputation change for peers which send us a block which we fail to verify.
114	pub const VERIFICATION_FAIL: Rep = Rep::new(-(1 << 29), "Block verification failed");
115
116	/// Reputation change for peers which send us a known bad block.
117	pub const BAD_BLOCK: Rep = Rep::new(-(1 << 29), "Bad block");
118
119	/// Peer did not provide us with advertised block data.
120	pub const NO_BLOCK: Rep = Rep::new(-(1 << 29), "No requested block data");
121
122	/// Reputation change for peers which send us non-requested block data.
123	pub const NOT_REQUESTED: Rep = Rep::new(-(1 << 29), "Not requested block data");
124
125	/// Peer could not serve any body of a gap sync request that required them.
126	/// Mild, since the peer may still be backfilling its own block history.
127	pub const NO_GAP_BODIES: Rep = Rep::new(-(1 << 26), "No gap sync bodies");
128
129	/// Reputation change for peers which send us a block with bad justifications.
130	pub const BAD_JUSTIFICATION: Rep = Rep::new(-(1 << 16), "Bad justification");
131
132	/// Reputation change when a peer sent us invalid ancestry result.
133	pub const UNKNOWN_ANCESTOR: Rep = Rep::new(-(1 << 16), "DB Error");
134
135	/// Peer response data does not have requested bits.
136	pub const BAD_RESPONSE: Rep = Rep::new(-(1 << 12), "Incomplete response");
137
138	/// We received a message that failed to decode.
139	pub const BAD_MESSAGE: Rep = Rep::new(-(1 << 12), "Bad message");
140}
141
142struct Metrics {
143	queued_blocks: Gauge<U64>,
144	fork_targets: Gauge<U64>,
145	block_collection: BlockCollectionMetrics,
146	gap_body_empty_responses: Counter<U64>,
147	gap_header_only_downgrades: Counter<U64>,
148	gap_oldest_required_body: Gauge<U64>,
149}
150
151impl Metrics {
152	fn register(r: &Registry) -> Result<Self, PrometheusError> {
153		Ok(Self {
154			queued_blocks: {
155				let g =
156					Gauge::new("substrate_sync_queued_blocks", "Number of blocks in import queue")?;
157				register(g, r)?
158			},
159			fork_targets: {
160				let g = Gauge::new("substrate_sync_fork_targets", "Number of fork sync targets")?;
161				register(g, r)?
162			},
163			block_collection: BlockCollectionMetrics::register(r)?,
164			gap_body_empty_responses: {
165				let c = Counter::new(
166					"substrate_sync_gap_body_empty_responses_total",
167					"Number of empty responses to gap sync requests that required bodies; \
168					 each drops the responding peer",
169				)?;
170				register(c, r)?
171			},
172			gap_header_only_downgrades: {
173				let c = Counter::new(
174					"substrate_sync_gap_header_only_downgrades_total",
175					"Number of gap sync requests issued header-only because the moving body \
176					 cutoff passed the range",
177				)?;
178				register(c, r)?
179			},
180			gap_oldest_required_body: {
181				let g = Gauge::new(
182					"substrate_sync_gap_oldest_required_body",
183					"Oldest block number for which gap sync still requires a body; \
184					 0 when gap sync is inactive or bodies are not required",
185				)?;
186				register(g, r)?
187			},
188		})
189	}
190}
191
192#[derive(Debug, Clone)]
193enum AllowedRequests {
194	Some(HashSet<PeerId>),
195	All,
196}
197
198impl AllowedRequests {
199	fn add(&mut self, id: &PeerId) {
200		if let Self::Some(ref mut set) = self {
201			set.insert(*id);
202		}
203	}
204
205	fn take(&mut self) -> Self {
206		std::mem::take(self)
207	}
208
209	fn set_all(&mut self) {
210		*self = Self::All;
211	}
212
213	fn contains(&self, id: &PeerId) -> bool {
214		match self {
215			Self::Some(set) => set.contains(id),
216			Self::All => true,
217		}
218	}
219
220	fn is_empty(&self) -> bool {
221		match self {
222			Self::Some(set) => set.is_empty(),
223			Self::All => false,
224		}
225	}
226
227	fn clear(&mut self) {
228		std::mem::take(self);
229	}
230}
231
232impl Default for AllowedRequests {
233	fn default() -> Self {
234		Self::Some(HashSet::default())
235	}
236}
237
238/// Statistics for gap sync operations.
239#[derive(Debug, Default, Clone)]
240struct GapSyncStats {
241	/// Size of headers downloaded during gap sync
242	header_bytes: usize,
243	/// Size of bodies downloaded during gap sync
244	body_bytes: usize,
245	/// Size of justifications downloaded during gap sync
246	justification_bytes: usize,
247}
248
249impl GapSyncStats {
250	fn new() -> Self {
251		Self::default()
252	}
253
254	fn total_bytes(&self) -> usize {
255		self.header_bytes + self.body_bytes + self.justification_bytes
256	}
257
258	fn bytes_to_mib(bytes: usize) -> f64 {
259		bytes as f64 / (1024.0 * 1024.0)
260	}
261}
262
263impl fmt::Display for GapSyncStats {
264	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265		let total = self.total_bytes();
266		write!(
267			f,
268			"hdr: {} B ({:.2} MiB), body: {} B ({:.2} MiB), just: {} B ({:.2} MiB) | total: {} B ({:.2} MiB)",
269			self.header_bytes,
270			Self::bytes_to_mib(self.header_bytes),
271			self.body_bytes,
272			Self::bytes_to_mib(self.body_bytes),
273			self.justification_bytes,
274			Self::bytes_to_mib(self.justification_bytes),
275			total,
276			Self::bytes_to_mib(total),
277		)
278	}
279}
280
281impl AddAssign for GapSyncStats {
282	fn add_assign(&mut self, other: Self) {
283		self.header_bytes += other.header_bytes;
284		self.body_bytes += other.body_bytes;
285		self.justification_bytes += other.justification_bytes;
286	}
287}
288
289struct GapSync<B: BlockT> {
290	blocks: BlockCollection<B>,
291	best_queued_number: NumberFor<B>,
292	target: NumberFor<B>,
293	stats: GapSyncStats,
294}
295
296/// Sync operation mode.
297#[derive(Copy, Clone, Debug, Eq, PartialEq)]
298pub enum ChainSyncMode {
299	/// Full block download and verification.
300	Full,
301	/// Download blocks and the latest state.
302	LightState {
303		/// Skip state proof download and verification.
304		skip_proofs: bool,
305		/// Download indexed transactions for recent blocks.
306		storage_chain_mode: bool,
307	},
308}
309
310impl ChainSyncMode {
311	/// Returns the base block attributes required for this sync mode.
312	pub fn required_block_attributes(&self) -> BlockAttributes {
313		match self {
314			ChainSyncMode::Full | ChainSyncMode::LightState { storage_chain_mode: false, .. } => {
315				BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION | BlockAttributes::BODY
316			},
317			ChainSyncMode::LightState { storage_chain_mode: true, .. } => {
318				BlockAttributes::HEADER |
319					BlockAttributes::JUSTIFICATION |
320					BlockAttributes::INDEXED_BODY
321			},
322		}
323	}
324}
325
326/// Which block bodies gap sync downloads while backfilling the block history below a
327/// warp-synced block.
328#[derive(Copy, Clone, Debug, Eq, PartialEq)]
329pub enum GapSyncBodyPolicy {
330	/// Backfill gap headers and justifications without bodies.
331	HeadersOnly,
332	/// Require bodies for the complete gap.
333	All,
334	/// Require bodies for blocks within the given window below the finalized block.
335	/// The window is expected to be pre-shrunk by the node with a safety margin, so
336	/// that peers whose finality runs ahead of ours still retain every requested body.
337	///
338	/// While the node is major syncing, local finality can lag the network beyond the
339	/// safety margin, so gap ranges above the cutoff are deferred until it has caught up.
340	BodiesWithinWindow(u32),
341}
342
343/// Resolves the [`GapSyncBodyPolicy`] lazily, when a `ChainSync` instance is created.
344///
345/// On a warp-syncing node this happens right after state sync completes, so the
346/// provider can query runtime state at the warp target. Errors fail `ChainSync`
347/// creation instead of silently degrading the policy.
348pub type GapSyncBodyPolicyProvider =
349	Arc<dyn Fn() -> Result<GapSyncBodyPolicy, ClientError> + Send + Sync>;
350
351/// All the data we have about a Peer that we are trying to sync with
352#[derive(Debug, Clone)]
353pub(crate) struct PeerSync<B: BlockT> {
354	/// Peer id of this peer.
355	pub peer_id: PeerId,
356	/// The common number is the block number that is a common point of
357	/// ancestry for both our chains (as far as we know).
358	pub common_number: NumberFor<B>,
359	/// The hash of the best block that we've seen for this peer.
360	pub best_hash: B::Hash,
361	/// The number of the best block that we've seen for this peer.
362	pub best_number: NumberFor<B>,
363	/// The state of syncing this peer is in for us, generally categories
364	/// into `Available` or "busy" with something as defined by `PeerSyncState`.
365	pub state: PeerSyncState<B>,
366}
367
368impl<B: BlockT> PeerSync<B> {
369	/// Update the `common_number` iff `new_common > common_number`.
370	fn update_common_number(&mut self, new_common: NumberFor<B>) {
371		if self.common_number < new_common {
372			trace!(
373				target: LOG_TARGET,
374				"Updating peer {} common number from={} => to={}.",
375				self.peer_id,
376				self.common_number,
377				new_common,
378			);
379			self.common_number = new_common;
380		}
381	}
382}
383
384struct ForkTarget<B: BlockT> {
385	number: NumberFor<B>,
386	parent_hash: Option<B::Hash>,
387	peers: HashSet<PeerId>,
388}
389
390/// The state of syncing between a Peer and ourselves.
391///
392/// Generally two categories, "busy" or `Available`. If busy, the enum
393/// defines what we are busy with.
394#[derive(Copy, Clone, Eq, PartialEq, Debug)]
395pub(crate) enum PeerSyncState<B: BlockT> {
396	/// Available for sync requests.
397	Available,
398	/// Searching for ancestors the Peer has in common with us.
399	AncestorSearch {
400		/// The best queued number when starting the ancestor search.
401		start: NumberFor<B>,
402		/// The current block that is being downloaded.
403		current: NumberFor<B>,
404		/// The state of the search.
405		state: AncestorSearchState<B>,
406	},
407	/// Actively downloading new blocks, starting from the given Number.
408	DownloadingNew(NumberFor<B>),
409	/// Downloading a stale block with given Hash. Stale means that it is a
410	/// block with a number that is lower than our best number. It might be
411	/// from a fork and not necessarily already imported.
412	DownloadingStale(B::Hash),
413	/// Downloading justification for given block hash.
414	DownloadingJustification(B::Hash),
415	/// Downloading state.
416	DownloadingState,
417	/// Actively downloading block history after warp sync.
418	DownloadingGap(NumberFor<B>),
419}
420
421impl<B: BlockT> PeerSyncState<B> {
422	pub fn is_available(&self) -> bool {
423		matches!(self, Self::Available)
424	}
425}
426
427/// The main data structure which contains all the state for a chains
428/// active syncing strategy.
429pub struct ChainSync<B: BlockT, Client> {
430	/// Chain client.
431	client: Arc<Client>,
432	/// The active peers that we are using to sync and their PeerSync status
433	peers: HashMap<PeerId, PeerSync<B>>,
434	disconnected_peers: DisconnectedPeers,
435	/// A `BlockCollection` of blocks that are being downloaded from peers
436	blocks: BlockCollection<B>,
437	/// The best block number in our queue of blocks to import
438	best_queued_number: NumberFor<B>,
439	/// The best block hash in our queue of blocks to import
440	best_queued_hash: B::Hash,
441	/// Current mode (full/light)
442	mode: ChainSyncMode,
443	/// Any extra justification requests.
444	extra_justifications: ExtraRequests<B>,
445	/// A set of hashes of blocks that are being downloaded or have been
446	/// downloaded and are queued for import.
447	queue_blocks: HashSet<B::Hash>,
448	/// A pending attempt to start the state sync.
449	///
450	/// The initiation of state sync may be deferred in cases where other conditions
451	/// are not yet met when the finalized block notification is received, such as
452	/// when `queue_blocks` is not empty or there are no peers. This field holds the
453	/// necessary information to attempt the state sync at a later point when
454	/// conditions are satisfied.
455	pending_state_sync_attempt: Option<(B::Hash, NumberFor<B>, bool)>,
456	/// Fork sync targets.
457	fork_targets: HashMap<B::Hash, ForkTarget<B>>,
458	/// A set of peers for which there might be potential block requests
459	allowed_requests: AllowedRequests,
460	/// Maximum number of peers to ask the same blocks in parallel.
461	max_parallel_downloads: u32,
462	/// Maximum blocks per request.
463	max_blocks_per_request: u32,
464	/// Protocol name used to send out state requests
465	state_request_protocol_name: ProtocolName,
466	/// Total number of downloaded blocks.
467	downloaded_blocks: usize,
468	/// State sync in progress, if any.
469	state_sync: Option<StateSync<B, Client>>,
470	/// Enable importing existing blocks. This is used after the state download to
471	/// catch up to the latest state while re-importing blocks.
472	import_existing: bool,
473	/// Block downloader
474	block_downloader: Arc<dyn BlockDownloader<B>>,
475	/// Which block bodies gap sync downloads.
476	gap_sync_body_policy: GapSyncBodyPolicy,
477	/// Gap download process.
478	gap_sync: Option<GapSync<B>>,
479	/// Pending actions.
480	actions: Vec<SyncingAction<B>>,
481	/// Prometheus metrics.
482	metrics: Option<Metrics>,
483}
484
485impl<B, Client> SyncingStrategy<B> for ChainSync<B, Client>
486where
487	B: BlockT,
488	Client: HeaderBackend<B>
489		+ BlockBackend<B>
490		+ HeaderMetadata<B, Error = sp_blockchain::Error>
491		+ ProofProvider<B>
492		+ Send
493		+ Sync
494		+ 'static,
495{
496	fn add_peer(&mut self, peer_id: PeerId, best_hash: B::Hash, best_number: NumberFor<B>) {
497		match self.add_peer_inner(peer_id, best_hash, best_number) {
498			Ok(Some(request)) => {
499				let action = self.create_block_request_action(peer_id, request);
500				self.actions.push(action);
501			},
502			Ok(None) => {},
503			Err(bad_peer) => self.actions.push(SyncingAction::DropPeer(bad_peer)),
504		}
505	}
506
507	fn remove_peer(&mut self, peer_id: &PeerId) {
508		self.blocks.clear_peer_download(peer_id);
509		if let Some(gap_sync) = &mut self.gap_sync {
510			gap_sync.blocks.clear_peer_download(peer_id)
511		}
512
513		if let Some(state) = self.peers.remove(peer_id) {
514			if !state.state.is_available() {
515				if let Some(bad_peer) =
516					self.disconnected_peers.on_disconnect_during_request(*peer_id)
517				{
518					self.actions.push(SyncingAction::DropPeer(bad_peer));
519				}
520			}
521		}
522
523		self.extra_justifications.cancel_request(peer_id);
524		self.allowed_requests.set_all();
525		self.fork_targets.retain(|_, target| {
526			target.peers.remove(peer_id);
527			!target.peers.is_empty()
528		});
529		if let Some(metrics) = &self.metrics {
530			metrics.fork_targets.set(self.fork_targets.len().try_into().unwrap_or(u64::MAX));
531		}
532
533		let blocks = self.ready_blocks();
534
535		if !blocks.is_empty() {
536			self.validate_and_queue_blocks(blocks, false);
537		}
538	}
539
540	fn on_validated_block_announce(
541		&mut self,
542		is_best: bool,
543		peer_id: PeerId,
544		announce: &BlockAnnounce<B::Header>,
545	) -> Option<(B::Hash, NumberFor<B>)> {
546		let number = *announce.header.number();
547		let hash = announce.header.hash();
548		let parent_status =
549			self.block_status(announce.header.parent_hash()).unwrap_or(BlockStatus::Unknown);
550		let known_parent = parent_status != BlockStatus::Unknown;
551		let ancient_parent = parent_status == BlockStatus::InChainPruned;
552
553		let known = self.is_known(&hash);
554		let is_major_syncing = self.is_major_syncing();
555		let peer = if let Some(peer) = self.peers.get_mut(&peer_id) {
556			peer
557		} else {
558			error!(target: LOG_TARGET, "๐Ÿ’” Called `on_validated_block_announce` with a bad peer ID {peer_id}");
559			return Some((hash, number));
560		};
561
562		if let PeerSyncState::AncestorSearch { .. } = peer.state {
563			trace!(target: LOG_TARGET, "Peer {} is in the ancestor search state.", peer_id);
564			return None;
565		}
566
567		// The node is continuing a known fork if either the block itself is known, the
568		// parent is known or the block references the previously announced `best_hash`.
569		let continues_known_fork =
570			known || known_parent || announce.header.parent_hash() == &peer.best_hash;
571
572		let peer_info = is_best.then(|| {
573			// update their best block
574			peer.best_number = number;
575			peer.best_hash = hash;
576
577			(hash, number)
578		});
579
580		// If the announced block is the best they have and is not ahead of us, our common number
581		// is either one further ahead or it's the one they just announced, if we know about it.
582		if is_best {
583			let best_queued_number = self.best_queued_number;
584
585			if known && best_queued_number >= number {
586				peer.update_common_number(number);
587			} else if announce.header.parent_hash() == &self.best_queued_hash ||
588				known_parent && best_queued_number >= number
589			{
590				peer.update_common_number(number.saturating_sub(One::one()));
591			}
592
593			// If this announced block isn't following any known fork, we have to start an
594			// ancestor search to find out our real common block. However, we skip this during
595			// major sync to avoid pulling peers out of the download pool.
596			if !continues_known_fork && !is_major_syncing {
597				let current = number.min(best_queued_number);
598				peer.common_number = peer.common_number.min(self.client.info().finalized_number);
599				let old_state = std::mem::replace(
600					&mut peer.state,
601					PeerSyncState::AncestorSearch {
602						current,
603						start: best_queued_number,
604						state: AncestorSearchState::ExponentialBackoff(One::one()),
605					},
606				);
607				self.cancel_peer_request(peer_id, old_state);
608
609				let request = ancestry_request::<B>(current);
610				let action = self.create_block_request_action(peer_id, request);
611				self.actions.push(action);
612
613				return peer_info;
614			}
615		}
616		self.allowed_requests.add(&peer_id);
617
618		// known block case
619		if known || self.is_already_downloading(&hash) {
620			trace!(target: LOG_TARGET, "Known block announce from {}: {}", peer_id, hash);
621			if let Some(target) = self.fork_targets.get_mut(&hash) {
622				target.peers.insert(peer_id);
623			}
624			return peer_info;
625		}
626
627		if ancient_parent {
628			trace!(
629				target: LOG_TARGET,
630				"Ignored ancient block announced from {}: {} {:?}",
631				peer_id,
632				hash,
633				announce.header,
634			);
635			return peer_info;
636		}
637
638		if self.status().state == SyncState::Idle {
639			trace!(
640				target: LOG_TARGET,
641				"Added sync target for block announced from {}: {} {:?}",
642				peer_id,
643				hash,
644				announce.summary(),
645			);
646			self.fork_targets
647				.entry(hash)
648				.or_insert_with(|| {
649					if let Some(metrics) = &self.metrics {
650						metrics.fork_targets.inc();
651					}
652
653					ForkTarget {
654						number,
655						parent_hash: Some(*announce.header.parent_hash()),
656						peers: Default::default(),
657					}
658				})
659				.peers
660				.insert(peer_id);
661		}
662
663		peer_info
664	}
665
666	// The implementation is similar to `on_validated_block_announce` with unknown parent hash.
667	fn set_sync_fork_request(
668		&mut self,
669		mut peers: Vec<PeerId>,
670		hash: &B::Hash,
671		number: NumberFor<B>,
672	) {
673		if peers.is_empty() {
674			peers = self
675				.peers
676				.iter()
677				// Only request blocks from peers who are ahead or on a par.
678				.filter(|(_, peer)| peer.best_number >= number)
679				.map(|(id, _)| *id)
680				.collect();
681
682			debug!(
683				target: LOG_TARGET,
684				"Explicit sync request for block {hash:?} with no peers specified. \
685				Syncing from these peers {peers:?} instead.",
686			);
687		} else {
688			debug!(
689				target: LOG_TARGET,
690				"Explicit sync request for block {hash:?} with {peers:?}",
691			);
692		}
693
694		if self.is_known(hash) {
695			debug!(target: LOG_TARGET, "Refusing to sync known hash {hash:?}");
696			return;
697		}
698
699		trace!(target: LOG_TARGET, "Downloading requested old fork {hash:?}");
700		for peer_id in &peers {
701			if let Some(peer) = self.peers.get_mut(peer_id) {
702				if let PeerSyncState::AncestorSearch { .. } = peer.state {
703					continue;
704				}
705
706				if number > peer.best_number {
707					peer.best_number = number;
708					peer.best_hash = *hash;
709				}
710				self.allowed_requests.add(peer_id);
711			}
712		}
713
714		self.fork_targets
715			.entry(*hash)
716			.or_insert_with(|| {
717				if let Some(metrics) = &self.metrics {
718					metrics.fork_targets.inc();
719				}
720
721				ForkTarget { number, peers: Default::default(), parent_hash: None }
722			})
723			.peers
724			.extend(peers);
725	}
726
727	fn request_justification(&mut self, hash: &B::Hash, number: NumberFor<B>) {
728		let client = &self.client;
729		self.extra_justifications
730			.schedule((*hash, number), |base, block| is_descendent_of(&**client, base, block))
731	}
732
733	fn clear_justification_requests(&mut self) {
734		self.extra_justifications.reset();
735	}
736
737	fn on_justification_import(&mut self, hash: B::Hash, number: NumberFor<B>, success: bool) {
738		let finalization_result = if success { Ok((hash, number)) } else { Err(()) };
739		self.extra_justifications
740			.try_finalize_root((hash, number), finalization_result, true);
741		self.allowed_requests.set_all();
742	}
743
744	fn on_generic_response(
745		&mut self,
746		peer_id: &PeerId,
747		key: StrategyKey,
748		protocol_name: ProtocolName,
749		response: Box<dyn Any + Send>,
750	) {
751		if Self::STRATEGY_KEY != key {
752			warn!(
753				target: LOG_TARGET,
754				"Unexpected generic response strategy key {key:?}, protocol {protocol_name}",
755			);
756			debug_assert!(false);
757			return;
758		}
759
760		if protocol_name == self.state_request_protocol_name {
761			let Ok(response) = response.downcast::<Vec<u8>>() else {
762				warn!(target: LOG_TARGET, "Failed to downcast state response");
763				debug_assert!(false);
764				return;
765			};
766
767			if let Err(bad_peer) = self.on_state_data(&peer_id, &response) {
768				self.actions.push(SyncingAction::DropPeer(bad_peer));
769			}
770		} else if &protocol_name == self.block_downloader.protocol_name() {
771			let Ok(response) = response
772				.downcast::<(BlockRequest<B>, Result<Vec<BlockData<B>>, BlockResponseError>)>()
773			else {
774				warn!(target: LOG_TARGET, "Failed to downcast block response");
775				debug_assert!(false);
776				return;
777			};
778
779			let (request, response) = *response;
780			let blocks = match response {
781				Ok(blocks) => blocks,
782				Err(BlockResponseError::DecodeFailed(e)) => {
783					debug!(
784						target: LOG_TARGET,
785						"Failed to decode block response from peer {:?}: {:?}.",
786						peer_id,
787						e
788					);
789					self.actions.push(SyncingAction::DropPeer(BadPeer(*peer_id, rep::BAD_MESSAGE)));
790					return;
791				},
792				Err(BlockResponseError::ExtractionFailed(e)) => {
793					debug!(
794						target: LOG_TARGET,
795						"Failed to extract blocks from peer response {:?}: {:?}.",
796						peer_id,
797						e
798					);
799					self.actions.push(SyncingAction::DropPeer(BadPeer(*peer_id, rep::BAD_MESSAGE)));
800					return;
801				},
802			};
803
804			if let Err(bad_peer) = self.on_block_response(peer_id, key, request, blocks) {
805				self.actions.push(SyncingAction::DropPeer(bad_peer));
806			}
807		} else {
808			warn!(
809				target: LOG_TARGET,
810				"Unexpected generic response protocol {protocol_name}, strategy key \
811				{key:?}",
812			);
813			debug_assert!(false);
814		}
815	}
816
817	fn on_blocks_processed(
818		&mut self,
819		imported: usize,
820		count: usize,
821		results: Vec<(Result<BlockImportStatus<NumberFor<B>>, BlockImportError>, B::Hash)>,
822	) {
823		trace!(target: LOG_TARGET, "Imported {imported} of {count}");
824
825		let mut has_error = false;
826		for (_, hash) in &results {
827			if self.queue_blocks.remove(hash) {
828				if let Some(metrics) = &self.metrics {
829					metrics.queued_blocks.dec();
830				}
831			}
832			self.blocks.clear_queued(hash);
833			if let Some(gap_sync) = &mut self.gap_sync {
834				gap_sync.blocks.clear_queued(hash);
835			}
836		}
837		for (result, hash) in results {
838			if has_error {
839				break;
840			}
841
842			has_error |= result.is_err();
843
844			match result {
845				Ok(BlockImportStatus::ImportedKnown(number, peer_id)) => {
846					if let Some(peer) = peer_id {
847						self.update_peer_common_number(&peer, number);
848					}
849					self.complete_gap_if_target(number);
850				},
851				Ok(BlockImportStatus::ImportedUnknown(number, aux, peer_id)) => {
852					if aux.clear_justification_requests {
853						trace!(
854							target: LOG_TARGET,
855							"Block imported clears all pending justification requests {number}: {hash:?}",
856						);
857						self.clear_justification_requests();
858					}
859
860					if aux.needs_justification {
861						trace!(
862							target: LOG_TARGET,
863							"Block imported but requires justification {number}: {hash:?}",
864						);
865						self.request_justification(&hash, number);
866					}
867
868					if aux.bad_justification {
869						if let Some(ref peer) = peer_id {
870							warn!("๐Ÿ’” Sent block with bad justification to import");
871							self.actions.push(SyncingAction::DropPeer(BadPeer(
872								*peer,
873								rep::BAD_JUSTIFICATION,
874							)));
875						}
876					}
877
878					if let Some(peer) = peer_id {
879						self.update_peer_common_number(&peer, number);
880					}
881					let state_sync_complete =
882						self.state_sync.as_ref().map_or(false, |s| s.target_hash() == hash);
883					if state_sync_complete {
884						info!(
885							target: LOG_TARGET,
886							"State sync is complete ({} MiB), restarting block sync.",
887							self.state_sync.as_ref().map_or(0, |s| s.progress().size / (1024 * 1024)),
888						);
889						self.state_sync = None;
890						self.mode = ChainSyncMode::Full;
891						self.restart();
892					}
893
894					self.complete_gap_if_target(number);
895				},
896				Err(BlockImportError::IncompleteHeader(peer_id)) => {
897					if let Some(peer) = peer_id {
898						warn!(
899							target: LOG_TARGET,
900							"๐Ÿ’” Peer sent block with incomplete header to import",
901						);
902						self.actions
903							.push(SyncingAction::DropPeer(BadPeer(peer, rep::INCOMPLETE_HEADER)));
904						self.restart();
905					}
906				},
907				Err(BlockImportError::VerificationFailed(peer_id, e)) => {
908					let extra_message = peer_id
909						.map_or_else(|| "".into(), |peer| format!(" received from ({peer})"));
910
911					warn!(
912						target: LOG_TARGET,
913						"๐Ÿ’” Verification failed for block {hash:?}{extra_message}: {e:?}",
914					);
915
916					if let Some(peer) = peer_id {
917						self.actions
918							.push(SyncingAction::DropPeer(BadPeer(peer, rep::VERIFICATION_FAIL)));
919					}
920
921					self.restart();
922				},
923				Err(BlockImportError::BadBlock(peer_id)) => {
924					if let Some(peer) = peer_id {
925						warn!(
926							target: LOG_TARGET,
927							"๐Ÿ’” Block {hash:?} received from peer {peer} has been blacklisted",
928						);
929						self.actions.push(SyncingAction::DropPeer(BadPeer(peer, rep::BAD_BLOCK)));
930					}
931				},
932				Err(BlockImportError::MissingState) => {
933					// This may happen if the chain we were requesting upon has been discarded
934					// in the meantime because other chain has been finalized.
935					// Don't mark it as bad as it still may be synced if explicitly requested.
936					trace!(target: LOG_TARGET, "Obsolete block {hash:?}");
937				},
938				e @ Err(BlockImportError::UnknownParent) | e @ Err(BlockImportError::Other(_)) => {
939					warn!(target: LOG_TARGET, "๐Ÿ’” Error importing block {hash:?}: {}", e.unwrap_err());
940					self.state_sync = None;
941					self.restart();
942				},
943				Err(BlockImportError::Cancelled) => {},
944			};
945		}
946
947		self.allowed_requests.set_all();
948	}
949
950	fn on_block_finalized(&mut self, hash: &B::Hash, number: NumberFor<B>) {
951		let client = &self.client;
952		let r = self.extra_justifications.on_block_finalized(hash, number, |base, block| {
953			is_descendent_of(&**client, base, block)
954		});
955
956		if let ChainSyncMode::LightState { skip_proofs, .. } = &self.mode {
957			if self.state_sync.is_none() {
958				if !self.peers.is_empty() && self.queue_blocks.is_empty() {
959					self.attempt_state_sync(*hash, number, *skip_proofs);
960				} else {
961					self.pending_state_sync_attempt.replace((*hash, number, *skip_proofs));
962				}
963			}
964		}
965
966		if let Err(err) = r {
967			warn!(
968				target: LOG_TARGET,
969				"๐Ÿ’” Error cleaning up pending extra justification data requests: {err}",
970			);
971		}
972	}
973
974	fn update_chain_info(&mut self, best_hash: &B::Hash, best_number: NumberFor<B>) {
975		self.on_block_queued(best_hash, best_number);
976	}
977
978	fn is_major_syncing(&self) -> bool {
979		self.status().state.is_major_syncing()
980	}
981
982	fn num_peers(&self) -> usize {
983		self.peers.len()
984	}
985
986	fn status(&self) -> SyncStatus<B> {
987		let median_seen = self.median_seen();
988		let best_seen_block =
989			median_seen.and_then(|median| (median > self.best_queued_number).then_some(median));
990		let sync_state = if let Some(target) = median_seen {
991			// A chain is classified as downloading if the provided best block is
992			// more than `MAJOR_SYNC_BLOCKS` behind the best block or as importing
993			// if the same can be said about queued blocks.
994			let best_block = self.client.info().best_number;
995			if target > best_block && target - best_block > MAJOR_SYNC_BLOCKS.into() {
996				// If target is not queued, we're downloading, otherwise importing.
997				if target > self.best_queued_number {
998					SyncState::Downloading { target }
999				} else {
1000					SyncState::Importing { target }
1001				}
1002			} else {
1003				SyncState::Idle
1004			}
1005		} else {
1006			SyncState::Idle
1007		};
1008
1009		let warp_sync_progress = self.gap_sync.as_ref().map(|gap_sync| WarpSyncProgress {
1010			phase: WarpSyncPhase::DownloadingBlocks(gap_sync.best_queued_number),
1011			total_bytes: 0,
1012			status: None,
1013		});
1014
1015		SyncStatus {
1016			state: sync_state,
1017			best_seen_block,
1018			num_peers: self.peers.len() as u32,
1019			queued_blocks: self.queue_blocks.len() as u32,
1020			state_sync: self.state_sync.as_ref().map(|s| s.progress()),
1021			warp_sync: warp_sync_progress,
1022		}
1023	}
1024
1025	fn num_downloaded_blocks(&self) -> usize {
1026		self.downloaded_blocks
1027	}
1028
1029	fn num_sync_requests(&self) -> usize {
1030		self.fork_targets
1031			.values()
1032			.filter(|f| f.number <= self.best_queued_number)
1033			.count()
1034	}
1035
1036	fn actions(
1037		&mut self,
1038		network_service: &NetworkServiceHandle,
1039	) -> Result<Vec<SyncingAction<B>>, ClientError> {
1040		if !self.peers.is_empty() && self.queue_blocks.is_empty() {
1041			if let Some((hash, number, skip_proofs)) = self.pending_state_sync_attempt.take() {
1042				self.attempt_state_sync(hash, number, skip_proofs);
1043			}
1044		}
1045
1046		let block_requests = self
1047			.block_requests()
1048			.into_iter()
1049			.map(|(peer_id, request)| self.create_block_request_action(peer_id, request))
1050			.collect::<Vec<_>>();
1051		self.actions.extend(block_requests);
1052
1053		let justification_requests = self
1054			.justification_requests()
1055			.into_iter()
1056			.map(|(peer_id, request)| self.create_block_request_action(peer_id, request))
1057			.collect::<Vec<_>>();
1058		self.actions.extend(justification_requests);
1059
1060		let state_request = self.state_request().into_iter().map(|(peer_id, request)| {
1061			trace!(
1062				target: LOG_TARGET,
1063				"Created `StateRequest` to {peer_id}.",
1064			);
1065
1066			let (tx, rx) = oneshot::channel();
1067
1068			network_service.start_request(
1069				peer_id,
1070				self.state_request_protocol_name.clone(),
1071				request.encode_to_vec(),
1072				tx,
1073				IfDisconnected::ImmediateError,
1074			);
1075
1076			SyncingAction::StartRequest {
1077				peer_id,
1078				key: Self::STRATEGY_KEY,
1079				request: async move {
1080					Ok(rx.await?.and_then(|(response, protocol_name)| {
1081						Ok((Box::new(response) as Box<dyn Any + Send>, protocol_name))
1082					}))
1083				}
1084				.boxed(),
1085			}
1086		});
1087		self.actions.extend(state_request);
1088
1089		Ok(std::mem::take(&mut self.actions))
1090	}
1091}
1092
1093impl<B, Client> ChainSync<B, Client>
1094where
1095	B: BlockT,
1096	Client: HeaderBackend<B>
1097		+ BlockBackend<B>
1098		+ HeaderMetadata<B, Error = sp_blockchain::Error>
1099		+ ProofProvider<B>
1100		+ Send
1101		+ Sync
1102		+ 'static,
1103{
1104	/// Strategy key used by chain sync.
1105	pub const STRATEGY_KEY: StrategyKey = StrategyKey::new("ChainSync");
1106
1107	/// Create a new instance.
1108	pub fn new(
1109		mode: ChainSyncMode,
1110		client: Arc<Client>,
1111		max_parallel_downloads: u32,
1112		max_blocks_per_request: u32,
1113		state_request_protocol_name: ProtocolName,
1114		block_downloader: Arc<dyn BlockDownloader<B>>,
1115		gap_sync_body_policy: GapSyncBodyPolicy,
1116		metrics_registry: Option<&Registry>,
1117		initial_peers: impl Iterator<Item = (PeerId, B::Hash, NumberFor<B>)>,
1118	) -> Result<Self, ClientError> {
1119		info!(target: LOG_TARGET, "Gap sync body policy: {gap_sync_body_policy:?}");
1120		let metrics = metrics_registry.and_then(|r| match Metrics::register(r) {
1121			Ok(metrics) => Some(metrics),
1122			Err(err) => {
1123				log::error!(target: LOG_TARGET, "Failed to register `ChainSync` metrics {err:?}");
1124				None
1125			},
1126		});
1127		let mut sync = Self {
1128			client,
1129			peers: HashMap::new(),
1130			disconnected_peers: DisconnectedPeers::new(),
1131			blocks: BlockCollection::with_metrics(
1132				metrics.as_ref().map(|m| m.block_collection.clone()),
1133			),
1134			best_queued_hash: Default::default(),
1135			best_queued_number: Zero::zero(),
1136			extra_justifications: ExtraRequests::new("justification", metrics_registry),
1137			mode,
1138			queue_blocks: Default::default(),
1139			pending_state_sync_attempt: None,
1140			fork_targets: Default::default(),
1141			allowed_requests: Default::default(),
1142			max_parallel_downloads,
1143			max_blocks_per_request,
1144			state_request_protocol_name,
1145			downloaded_blocks: 0,
1146			state_sync: None,
1147			import_existing: false,
1148			block_downloader,
1149			gap_sync_body_policy,
1150			gap_sync: None,
1151			actions: Vec::new(),
1152			metrics,
1153		};
1154
1155		sync.reset_sync_start_point()?;
1156		initial_peers.for_each(|(peer_id, best_hash, best_number)| {
1157			sync.add_peer(peer_id, best_hash, best_number);
1158		});
1159
1160		Ok(sync)
1161	}
1162
1163	/// Complete the gap sync if the target number is reached and there is a gap.
1164	fn complete_gap_if_target(&mut self, number: NumberFor<B>) {
1165		let Some(gap_sync) = &self.gap_sync else { return };
1166
1167		if gap_sync.target != number {
1168			return;
1169		}
1170
1171		info!(
1172			target: LOG_TARGET,
1173			"Block history download is complete. Downloaded {}.",
1174			gap_sync.stats,
1175		);
1176		self.gap_sync = None;
1177		if let Some(metrics) = &self.metrics {
1178			metrics.gap_oldest_required_body.set(0);
1179		}
1180	}
1181
1182	#[must_use]
1183	fn add_peer_inner(
1184		&mut self,
1185		peer_id: PeerId,
1186		best_hash: B::Hash,
1187		best_number: NumberFor<B>,
1188	) -> Result<Option<BlockRequest<B>>, BadPeer> {
1189		// There is nothing sync can get from the node that has no blockchain data.
1190		match self.block_status(&best_hash) {
1191			Err(e) => {
1192				debug!(target: LOG_TARGET, "Error reading blockchain: {e}");
1193				Err(BadPeer(peer_id, rep::BLOCKCHAIN_READ_ERROR))
1194			},
1195			Ok(BlockStatus::KnownBad) => {
1196				info!(
1197					"๐Ÿ’” New peer {peer_id} with known bad best block {best_hash} ({best_number})."
1198				);
1199				Err(BadPeer(peer_id, rep::BAD_BLOCK))
1200			},
1201			Ok(BlockStatus::Unknown) => {
1202				if best_number.is_zero() {
1203					info!(
1204						"๐Ÿ’” New peer {} with unknown genesis hash {} ({}).",
1205						peer_id, best_hash, best_number,
1206					);
1207					return Err(BadPeer(peer_id, rep::GENESIS_MISMATCH));
1208				}
1209
1210				// If there are more than `MAJOR_SYNC_BLOCKS` in the import queue then we have
1211				// enough to do in the import queue that it's not worth kicking off
1212				// an ancestor search, which is what we do in the next match case below.
1213				if self.queue_blocks.len() > MAJOR_SYNC_BLOCKS as usize {
1214					debug!(
1215						target: LOG_TARGET,
1216						"New peer {} with unknown best hash {} ({}), assuming common block.",
1217						peer_id,
1218						self.best_queued_hash,
1219						self.best_queued_number
1220					);
1221					self.peers.insert(
1222						peer_id,
1223						PeerSync {
1224							peer_id,
1225							common_number: self.best_queued_number,
1226							best_hash,
1227							best_number,
1228							state: PeerSyncState::Available,
1229						},
1230					);
1231					return Ok(None);
1232				}
1233
1234				// If we are at genesis, just start downloading.
1235				let (state, req) = if self.best_queued_number.is_zero() {
1236					debug!(
1237						target: LOG_TARGET,
1238						"New peer {peer_id} with best hash {best_hash} ({best_number}).",
1239					);
1240
1241					(PeerSyncState::Available, None)
1242				} else {
1243					let common_best = std::cmp::min(self.best_queued_number, best_number);
1244
1245					debug!(
1246						target: LOG_TARGET,
1247						"New peer {} with unknown best hash {} ({}), searching for common ancestor.",
1248						peer_id,
1249						best_hash,
1250						best_number
1251					);
1252
1253					(
1254						PeerSyncState::AncestorSearch {
1255							current: common_best,
1256							start: self.best_queued_number,
1257							state: AncestorSearchState::ExponentialBackoff(One::one()),
1258						},
1259						Some(ancestry_request::<B>(common_best)),
1260					)
1261				};
1262
1263				self.allowed_requests.add(&peer_id);
1264				self.peers.insert(
1265					peer_id,
1266					PeerSync {
1267						peer_id,
1268						common_number: Zero::zero(),
1269						best_hash,
1270						best_number,
1271						state,
1272					},
1273				);
1274
1275				Ok(req)
1276			},
1277			Ok(BlockStatus::Queued) |
1278			Ok(BlockStatus::InChainWithState) |
1279			Ok(BlockStatus::InChainPruned) => {
1280				debug!(
1281					target: LOG_TARGET,
1282					"New peer {peer_id} with known best hash {best_hash} ({best_number}).",
1283				);
1284				self.peers.insert(
1285					peer_id,
1286					PeerSync {
1287						peer_id,
1288						common_number: std::cmp::min(self.best_queued_number, best_number),
1289						best_hash,
1290						best_number,
1291						state: PeerSyncState::Available,
1292					},
1293				);
1294				self.allowed_requests.add(&peer_id);
1295				Ok(None)
1296			},
1297		}
1298	}
1299
1300	/// Release bookkeeping for a peer's old request and queue its cancellation.
1301	///
1302	/// The caller handles the peer's state transition and must call this before scheduling
1303	/// replacement work. The engine only drops the old response future.
1304	fn cancel_peer_request(&mut self, peer_id: PeerId, old_state: PeerSyncState<B>) {
1305		match old_state {
1306			PeerSyncState::Available => return,
1307			PeerSyncState::DownloadingNew(_) => self.blocks.clear_peer_download(&peer_id),
1308			PeerSyncState::DownloadingGap(_) => {
1309				if let Some(gap_sync) = &mut self.gap_sync {
1310					gap_sync.blocks.clear_peer_download(&peer_id);
1311				}
1312			},
1313			PeerSyncState::DownloadingJustification(_) => {
1314				self.extra_justifications.cancel_request(&peer_id);
1315			},
1316			// State requests are regenerated from the last imported cursor; fork targets
1317			// remain pending. Neither has a separate in-flight range reservation.
1318			PeerSyncState::DownloadingState |
1319			PeerSyncState::DownloadingStale(_) |
1320			PeerSyncState::AncestorSearch { .. } => {},
1321		}
1322		self.actions
1323			.push(SyncingAction::CancelRequest { peer_id, key: Self::STRATEGY_KEY });
1324		// Let any available peer pick up the released work, e.g. while this peer does
1325		// ancestry search.
1326		self.allowed_requests.set_all();
1327	}
1328
1329	fn create_block_request_action(
1330		&mut self,
1331		peer_id: PeerId,
1332		request: BlockRequest<B>,
1333	) -> SyncingAction<B> {
1334		let downloader = self.block_downloader.clone();
1335
1336		SyncingAction::StartRequest {
1337			peer_id,
1338			key: Self::STRATEGY_KEY,
1339			request: async move {
1340				Ok(downloader.download_blocks(peer_id, request.clone()).await?.and_then(
1341					|(response, protocol_name)| {
1342						let decoded_response =
1343							downloader.block_response_into_blocks(&request, response);
1344						let result = Box::new((request, decoded_response)) as Box<dyn Any + Send>;
1345						Ok((result, protocol_name))
1346					},
1347				))
1348			}
1349			.boxed(),
1350		}
1351	}
1352
1353	/// Submit a block response for processing.
1354	#[must_use]
1355	fn on_block_data(
1356		&mut self,
1357		peer_id: &PeerId,
1358		request: Option<BlockRequest<B>>,
1359		response: BlockResponse<B>,
1360	) -> Result<(), BadPeer> {
1361		self.downloaded_blocks += response.blocks.len();
1362		let mut gap = false;
1363		let new_blocks: Vec<IncomingBlock<B>> = if let Some(peer) = self.peers.get_mut(peer_id) {
1364			let mut blocks = response.blocks;
1365			if request.as_ref().map_or(false, |r| r.direction == Direction::Descending) {
1366				trace!(target: LOG_TARGET, "Reversing incoming block list");
1367				blocks.reverse()
1368			}
1369			self.allowed_requests.add(peer_id);
1370			if let Some(request) = request {
1371				match &mut peer.state {
1372					PeerSyncState::DownloadingNew(_) => {
1373						self.blocks.clear_peer_download(peer_id);
1374						peer.state = PeerSyncState::Available;
1375						if let Some(start_block) =
1376							validate_blocks::<B>(&blocks, peer_id, Some(request))?
1377						{
1378							self.blocks.insert(start_block, blocks, *peer_id);
1379						}
1380						self.ready_blocks()
1381					},
1382					PeerSyncState::DownloadingGap(_) => {
1383						peer.state = PeerSyncState::Available;
1384						if blocks.is_empty() && request.fields.contains(BlockAttributes::BODY) {
1385							// An empty response means the peer holds no body of the entire
1386							// range (bodies are pruned oldest-first). Disconnect it to free
1387							// the slot for a peer that does; the mild penalty lets it retry
1388							// later.
1389							debug!(
1390								target: LOG_TARGET,
1391								"Peer {peer_id} sent an empty response for gap block request \
1392								 {request:?} that required bodies; disconnecting it",
1393							);
1394							if let Some(metrics) = &self.metrics {
1395								metrics.gap_body_empty_responses.inc();
1396							}
1397							if let Some(gap_sync) = &mut self.gap_sync {
1398								gap_sync.blocks.clear_peer_download(peer_id);
1399							}
1400							return Err(BadPeer(*peer_id, rep::NO_GAP_BODIES));
1401						}
1402						if let Some(gap_sync) = &mut self.gap_sync {
1403							gap_sync.blocks.clear_peer_download(peer_id);
1404							if let Some(start_block) =
1405								validate_blocks::<B>(&blocks, peer_id, Some(request))?
1406							{
1407								gap_sync.blocks.insert(start_block, blocks, *peer_id);
1408							}
1409							gap = true;
1410							let mut batch_gap_sync_stats = GapSyncStats::new();
1411							let blocks: Vec<_> = gap_sync
1412								.blocks
1413								.ready_blocks(gap_sync.best_queued_number + One::one())
1414								.into_iter()
1415								.map(|block_data| {
1416									let justifications =
1417										block_data.block.justifications.or_else(|| {
1418											legacy_justification_mapping(
1419												block_data.block.justification,
1420											)
1421										});
1422									let gap_sync_stats = GapSyncStats {
1423										header_bytes: block_data
1424											.block
1425											.header
1426											.as_ref()
1427											.map(|h| h.encoded_size())
1428											.unwrap_or(0),
1429										body_bytes: block_data
1430											.block
1431											.body
1432											.as_ref()
1433											.map(|b| b.encoded_size())
1434											.unwrap_or(0),
1435										justification_bytes: justifications
1436											.as_ref()
1437											.map(|j| j.encoded_size())
1438											.unwrap_or(0),
1439									};
1440									batch_gap_sync_stats += gap_sync_stats;
1441
1442									IncomingBlock {
1443										hash: block_data.block.hash,
1444										header: block_data.block.header,
1445										body: block_data.block.body,
1446										indexed_body: block_data.block.indexed_body,
1447										justifications,
1448										origin: block_data.origin,
1449										allow_missing_state: true,
1450										// Warp-synced blocks are header-only. Allow re-import to
1451										// store bodies if gap sync requested them.
1452										import_existing: true,
1453										skip_execution: true,
1454										state: None,
1455									}
1456								})
1457								.collect();
1458
1459							debug!(
1460								target: LOG_TARGET,
1461								"Drained {} gap blocks from {}",
1462								blocks.len(),
1463								gap_sync.best_queued_number,
1464							);
1465
1466							gap_sync.stats += batch_gap_sync_stats;
1467
1468							if blocks.len() > 0 {
1469								trace!(
1470									target: LOG_TARGET,
1471									"Gap sync cumulative stats: {}",
1472									gap_sync.stats
1473								);
1474							}
1475							blocks
1476						} else {
1477							debug!(target: LOG_TARGET, "Unexpected gap block response from {peer_id}");
1478							return Err(BadPeer(*peer_id, rep::NO_BLOCK));
1479						}
1480					},
1481					PeerSyncState::DownloadingStale(_) => {
1482						peer.state = PeerSyncState::Available;
1483						if blocks.is_empty() {
1484							debug!(target: LOG_TARGET, "Empty block response from {peer_id}");
1485							return Err(BadPeer(*peer_id, rep::NO_BLOCK));
1486						}
1487						validate_blocks::<B>(&blocks, peer_id, Some(request))?;
1488						blocks
1489							.into_iter()
1490							.map(|b| {
1491								let justifications = b
1492									.justifications
1493									.or_else(|| legacy_justification_mapping(b.justification));
1494								IncomingBlock {
1495									hash: b.hash,
1496									header: b.header,
1497									body: b.body,
1498									indexed_body: None,
1499									justifications,
1500									origin: Some(*peer_id),
1501									allow_missing_state: true,
1502									import_existing: self.import_existing,
1503									skip_execution: self.skip_execution(),
1504									state: None,
1505								}
1506							})
1507							.collect()
1508					},
1509					PeerSyncState::AncestorSearch { current, start, state } => {
1510						let matching_hash = match (blocks.get(0), self.client.hash(*current)) {
1511							(Some(block), Ok(maybe_our_block_hash)) => {
1512								trace!(
1513									target: LOG_TARGET,
1514									"Got ancestry block #{} ({}) from peer {}",
1515									current,
1516									block.hash,
1517									peer_id,
1518								);
1519								maybe_our_block_hash.filter(|x| x == &block.hash)
1520							},
1521							(None, _) => {
1522								debug!(
1523									target: LOG_TARGET,
1524									"Invalid response when searching for ancestor from {peer_id}",
1525								);
1526								return Err(BadPeer(*peer_id, rep::UNKNOWN_ANCESTOR));
1527							},
1528							(_, Err(e)) => {
1529								info!(
1530									target: LOG_TARGET,
1531									"โŒ Error answering legitimate blockchain query: {e}",
1532								);
1533								return Err(BadPeer(*peer_id, rep::BLOCKCHAIN_READ_ERROR));
1534							},
1535						};
1536						if matching_hash.is_some() {
1537							if *start < self.best_queued_number &&
1538								self.best_queued_number <= peer.best_number
1539							{
1540								// We've made progress on this chain since the search was started.
1541								// Opportunistically set common number to updated number
1542								// instead of the one that started the search.
1543								trace!(
1544									target: LOG_TARGET,
1545									"Ancestry search: opportunistically updating peer {} common number from={} => to={}.",
1546									*peer_id,
1547									peer.common_number,
1548									self.best_queued_number,
1549								);
1550								peer.common_number = self.best_queued_number;
1551							} else if peer.common_number < *current {
1552								trace!(
1553									target: LOG_TARGET,
1554									"Ancestry search: updating peer {} common number from={} => to={}.",
1555									*peer_id,
1556									peer.common_number,
1557									*current,
1558								);
1559								peer.common_number = *current;
1560							}
1561						}
1562						if matching_hash.is_none() && current.is_zero() {
1563							trace!(
1564								target: LOG_TARGET,
1565								"Ancestry search: genesis mismatch for peer {peer_id}",
1566							);
1567							return Err(BadPeer(*peer_id, rep::GENESIS_MISMATCH));
1568						}
1569						if let Some((next_state, next_num)) =
1570							handle_ancestor_search_state(state, *current, matching_hash.is_some())
1571						{
1572							peer.state = PeerSyncState::AncestorSearch {
1573								current: next_num,
1574								start: *start,
1575								state: next_state,
1576							};
1577							let request = ancestry_request::<B>(next_num);
1578							let action = self.create_block_request_action(*peer_id, request);
1579							self.actions.push(action);
1580							return Ok(());
1581						} else {
1582							// Ancestry search is complete. Check if peer is on a stale fork unknown
1583							// to us and add it to sync targets if necessary.
1584							trace!(
1585								target: LOG_TARGET,
1586								"Ancestry search complete. Ours={} ({}), Theirs={} ({}), Common={:?} ({})",
1587								self.best_queued_hash,
1588								self.best_queued_number,
1589								peer.best_hash,
1590								peer.best_number,
1591								matching_hash,
1592								peer.common_number,
1593							);
1594							if peer.common_number < peer.best_number &&
1595								peer.best_number < self.best_queued_number
1596							{
1597								trace!(
1598									target: LOG_TARGET,
1599									"Added fork target {} for {}",
1600									peer.best_hash,
1601									peer_id,
1602								);
1603								self.fork_targets
1604									.entry(peer.best_hash)
1605									.or_insert_with(|| {
1606										if let Some(metrics) = &self.metrics {
1607											metrics.fork_targets.inc();
1608										}
1609
1610										ForkTarget {
1611											number: peer.best_number,
1612											parent_hash: None,
1613											peers: Default::default(),
1614										}
1615									})
1616									.peers
1617									.insert(*peer_id);
1618							}
1619							peer.state = PeerSyncState::Available;
1620							return Ok(());
1621						}
1622					},
1623					PeerSyncState::Available |
1624					PeerSyncState::DownloadingJustification(..) |
1625					PeerSyncState::DownloadingState => Vec::new(),
1626				}
1627			} else {
1628				// When request.is_none() this is a block announcement. Just accept blocks.
1629				validate_blocks::<B>(&blocks, peer_id, None)?;
1630				blocks
1631					.into_iter()
1632					.map(|b| {
1633						let justifications = b
1634							.justifications
1635							.or_else(|| legacy_justification_mapping(b.justification));
1636						IncomingBlock {
1637							hash: b.hash,
1638							header: b.header,
1639							body: b.body,
1640							indexed_body: None,
1641							justifications,
1642							origin: Some(*peer_id),
1643							allow_missing_state: true,
1644							import_existing: false,
1645							skip_execution: true,
1646							state: None,
1647						}
1648					})
1649					.collect()
1650			}
1651		} else {
1652			// We don't know of this peer, so we also did not request anything from it.
1653			return Err(BadPeer(*peer_id, rep::NOT_REQUESTED));
1654		};
1655
1656		self.validate_and_queue_blocks(new_blocks, gap);
1657
1658		Ok(())
1659	}
1660
1661	fn on_block_response(
1662		&mut self,
1663		peer_id: &PeerId,
1664		key: StrategyKey,
1665		request: BlockRequest<B>,
1666		blocks: Vec<BlockData<B>>,
1667	) -> Result<(), BadPeer> {
1668		if key != Self::STRATEGY_KEY {
1669			error!(
1670				target: LOG_TARGET,
1671				"`on_block_response()` called with unexpected key {key:?} for chain sync",
1672			);
1673			debug_assert!(false);
1674		}
1675		let block_response = BlockResponse::<B> { id: request.id, blocks };
1676
1677		let blocks_range = || match (
1678			block_response
1679				.blocks
1680				.first()
1681				.and_then(|b| b.header.as_ref().map(|h| h.number())),
1682			block_response.blocks.last().and_then(|b| b.header.as_ref().map(|h| h.number())),
1683		) {
1684			(Some(first), Some(last)) if first != last => format!(" ({}..{})", first, last),
1685			(Some(first), Some(_)) => format!(" ({})", first),
1686			_ => Default::default(),
1687		};
1688
1689		trace!(
1690			target: LOG_TARGET,
1691			"BlockResponse {} from {} with {} blocks {}",
1692			block_response.id,
1693			peer_id,
1694			block_response.blocks.len(),
1695			blocks_range(),
1696		);
1697
1698		if request.fields == BlockAttributes::JUSTIFICATION {
1699			self.on_block_justification(*peer_id, block_response)
1700		} else {
1701			self.on_block_data(peer_id, Some(request), block_response)
1702		}
1703	}
1704
1705	/// Submit a justification response for processing.
1706	#[must_use]
1707	fn on_block_justification(
1708		&mut self,
1709		peer_id: PeerId,
1710		response: BlockResponse<B>,
1711	) -> Result<(), BadPeer> {
1712		let peer = if let Some(peer) = self.peers.get_mut(&peer_id) {
1713			peer
1714		} else {
1715			error!(
1716				target: LOG_TARGET,
1717				"๐Ÿ’” Called on_block_justification with a peer ID of an unknown peer",
1718			);
1719			return Ok(());
1720		};
1721
1722		self.allowed_requests.add(&peer_id);
1723		if let PeerSyncState::DownloadingJustification(hash) = peer.state {
1724			peer.state = PeerSyncState::Available;
1725
1726			// We only request one justification at a time
1727			let justification = if let Some(block) = response.blocks.into_iter().next() {
1728				if hash != block.hash {
1729					warn!(
1730						target: LOG_TARGET,
1731						"๐Ÿ’” Invalid block justification provided by {}: requested: {:?} got: {:?}",
1732						peer_id,
1733						hash,
1734						block.hash,
1735					);
1736					return Err(BadPeer(peer_id, rep::BAD_JUSTIFICATION));
1737				}
1738
1739				block
1740					.justifications
1741					.or_else(|| legacy_justification_mapping(block.justification))
1742			} else {
1743				// we might have asked the peer for a justification on a block that we assumed it
1744				// had but didn't (regardless of whether it had a justification for it or not).
1745				trace!(
1746					target: LOG_TARGET,
1747					"Peer {peer_id:?} provided empty response for justification request {hash:?}",
1748				);
1749
1750				None
1751			};
1752
1753			if let Some((peer_id, hash, number, justifications)) =
1754				self.extra_justifications.on_response(peer_id, justification)
1755			{
1756				self.actions.push(SyncingAction::ImportJustifications {
1757					peer_id,
1758					hash,
1759					number,
1760					justifications,
1761				});
1762				return Ok(());
1763			}
1764		}
1765
1766		Ok(())
1767	}
1768
1769	/// Returns the median seen block number.
1770	fn median_seen(&self) -> Option<NumberFor<B>> {
1771		let mut best_seens = self.peers.values().map(|p| p.best_number).collect::<Vec<_>>();
1772
1773		if best_seens.is_empty() {
1774			None
1775		} else {
1776			let middle = best_seens.len() / 2;
1777
1778			// Not the "perfect median" when we have an even number of peers.
1779			Some(*best_seens.select_nth_unstable(middle).1)
1780		}
1781	}
1782
1783	fn skip_execution(&self) -> bool {
1784		match self.mode {
1785			ChainSyncMode::Full => false,
1786			ChainSyncMode::LightState { .. } => true,
1787		}
1788	}
1789
1790	fn validate_and_queue_blocks(&mut self, mut new_blocks: Vec<IncomingBlock<B>>, gap: bool) {
1791		let orig_len = new_blocks.len();
1792		new_blocks.retain(|b| !self.queue_blocks.contains(&b.hash));
1793		if new_blocks.len() != orig_len {
1794			debug!(
1795				target: LOG_TARGET,
1796				"Ignoring {} blocks that are already queued",
1797				orig_len - new_blocks.len(),
1798			);
1799		}
1800
1801		let origin = if gap {
1802			// Gap sync: filling historical blocks after warp sync
1803			BlockOrigin::GapSync
1804		} else if !self.status().state.is_major_syncing() {
1805			// Normal operation: receiving new blocks
1806			BlockOrigin::NetworkBroadcast
1807		} else {
1808			// Initial sync: catching up with the chain
1809			BlockOrigin::NetworkInitialSync
1810		};
1811
1812		if let Some((h, n)) = new_blocks
1813			.last()
1814			.and_then(|b| b.header.as_ref().map(|h| (&b.hash, *h.number())))
1815		{
1816			trace!(
1817				target: LOG_TARGET,
1818				"Accepted {} blocks ({:?}) with origin {:?}",
1819				new_blocks.len(),
1820				h,
1821				origin,
1822			);
1823			self.on_block_queued(h, n)
1824		}
1825		self.queue_blocks.extend(new_blocks.iter().map(|b| b.hash));
1826		if let Some(metrics) = &self.metrics {
1827			metrics
1828				.queued_blocks
1829				.set(self.queue_blocks.len().try_into().unwrap_or(u64::MAX));
1830		}
1831
1832		self.actions.push(SyncingAction::ImportBlocks { origin, blocks: new_blocks })
1833	}
1834
1835	fn update_peer_common_number(&mut self, peer_id: &PeerId, new_common: NumberFor<B>) {
1836		if let Some(peer) = self.peers.get_mut(peer_id) {
1837			peer.update_common_number(new_common);
1838		}
1839	}
1840
1841	/// Called when a block has been queued for import.
1842	///
1843	/// Updates our internal state for best queued block and then goes
1844	/// through all peers to update our view of their state as well.
1845	fn on_block_queued(&mut self, hash: &B::Hash, number: NumberFor<B>) {
1846		if self.fork_targets.remove(hash).is_some() {
1847			if let Some(metrics) = &self.metrics {
1848				metrics.fork_targets.dec();
1849			}
1850			trace!(target: LOG_TARGET, "Completed fork sync {hash:?}");
1851		}
1852		if let Some(gap_sync) = &mut self.gap_sync {
1853			if number > gap_sync.best_queued_number && number <= gap_sync.target {
1854				gap_sync.best_queued_number = number;
1855			}
1856		}
1857		if number > self.best_queued_number {
1858			self.best_queued_number = number;
1859			self.best_queued_hash = *hash;
1860			// Update common blocks
1861			for (n, peer) in self.peers.iter_mut() {
1862				if let PeerSyncState::AncestorSearch { .. } = peer.state {
1863					// Wait for ancestry search to complete first.
1864					continue;
1865				}
1866				let new_common_number =
1867					if peer.best_number >= number { number } else { peer.best_number };
1868				trace!(
1869					target: LOG_TARGET,
1870					"Updating peer {} info, ours={}, common={}->{}, their best={}",
1871					n,
1872					number,
1873					peer.common_number,
1874					new_common_number,
1875					peer.best_number,
1876				);
1877				peer.common_number = new_common_number;
1878			}
1879		}
1880		self.allowed_requests.set_all();
1881	}
1882
1883	/// Restart the sync process. This will reset all pending block requests and return an iterator
1884	/// of new block requests to make to peers. Peers that were downloading finality data (i.e.
1885	/// their state was `DownloadingJustification`) are unaffected and will stay in the same state.
1886	fn restart(&mut self) {
1887		self.blocks.clear();
1888		if let Err(e) = self.reset_sync_start_point() {
1889			warn!(target: LOG_TARGET, "๐Ÿ’”  Unable to restart sync: {e}");
1890		}
1891		self.allowed_requests.set_all();
1892		debug!(
1893			target: LOG_TARGET,
1894			"Restarted with {} ({})",
1895			self.best_queued_number,
1896			self.best_queued_hash,
1897		);
1898		let old_peers = std::mem::take(&mut self.peers);
1899
1900		old_peers.into_iter().for_each(|(peer_id, mut peer_sync)| {
1901			match peer_sync.state {
1902				PeerSyncState::DownloadingJustification(_) => {
1903					// Peers that were downloading justifications
1904					// should be kept in that state.
1905					// We make sure our common number is at least something we have.
1906					trace!(
1907						target: LOG_TARGET,
1908						"Keeping peer {} after restart, updating common number from={} => to={} (our best).",
1909						peer_id,
1910						peer_sync.common_number,
1911						self.best_queued_number,
1912					);
1913					peer_sync.common_number = self.best_queued_number;
1914					self.peers.insert(peer_id, peer_sync);
1915				},
1916				_ => {
1917					self.cancel_peer_request(peer_id, peer_sync.state);
1918					self.add_peer(peer_id, peer_sync.best_hash, peer_sync.best_number);
1919				},
1920			}
1921		});
1922	}
1923
1924	/// Find a block to start sync from. If we sync with state, that's the latest block we have
1925	/// state for.
1926	fn reset_sync_start_point(&mut self) -> Result<(), ClientError> {
1927		let info = self.client.info();
1928		debug!(target: LOG_TARGET, "Restarting sync with client info {info:?}");
1929
1930		if matches!(self.mode, ChainSyncMode::LightState { .. }) && info.finalized_state.is_some() {
1931			warn!(
1932				target: LOG_TARGET,
1933				"Can't use fast sync mode with a partially synced database. Reverting to full sync mode."
1934			);
1935			self.mode = ChainSyncMode::Full;
1936		}
1937
1938		self.import_existing = false;
1939		self.best_queued_hash = info.best_hash;
1940		self.best_queued_number = info.best_number;
1941
1942		if self.mode == ChainSyncMode::Full &&
1943			self.client.block_status(info.best_hash)? != BlockStatus::InChainWithState
1944		{
1945			self.import_existing = true;
1946			// Latest state is missing, start with the last finalized state or genesis instead.
1947			if let Some((hash, number)) = info.finalized_state {
1948				debug!(target: LOG_TARGET, "Starting from finalized state #{number}");
1949				self.best_queued_hash = hash;
1950				self.best_queued_number = number;
1951			} else {
1952				debug!(target: LOG_TARGET, "Restarting from genesis");
1953				self.best_queued_hash = Default::default();
1954				self.best_queued_number = Zero::zero();
1955			}
1956		}
1957
1958		// The client is the source of truth for the gap. Rebuild the gap sync state from it, or
1959		// drop ours if the database has already closed the gap.
1960		let old_gap = self.gap_sync.take().map(|g| (g.best_queued_number, g.target));
1961		if let Some(BlockGap { start, end, .. }) = info.block_gap {
1962			debug!(target: LOG_TARGET, "Starting gap sync #{start} - #{end} (old gap best and target: {old_gap:?})");
1963			self.gap_sync = Some(GapSync {
1964				best_queued_number: start - One::one(),
1965				target: end,
1966				blocks: BlockCollection::with_metrics(
1967					self.metrics.as_ref().map(|m| m.block_collection.clone()),
1968				),
1969				stats: GapSyncStats::new(),
1970			});
1971		} else if let Some((best, target)) = old_gap {
1972			debug!(
1973				target: LOG_TARGET,
1974				"Block gap is closed in the database, dropping gap sync state (best: #{best}, target: #{target})",
1975			);
1976			if let Some(metrics) = &self.metrics {
1977				metrics.gap_oldest_required_body.set(0);
1978			}
1979		}
1980		trace!(
1981			target: LOG_TARGET,
1982			"Restarted sync at #{} ({:?})",
1983			self.best_queued_number,
1984			self.best_queued_hash,
1985		);
1986		Ok(())
1987	}
1988
1989	/// What is the status of the block corresponding to the given hash?
1990	fn block_status(&self, hash: &B::Hash) -> Result<BlockStatus, ClientError> {
1991		if self.queue_blocks.contains(hash) {
1992			return Ok(BlockStatus::Queued);
1993		}
1994		self.client.block_status(*hash)
1995	}
1996
1997	/// Is the block corresponding to the given hash known?
1998	fn is_known(&self, hash: &B::Hash) -> bool {
1999		self.block_status(hash).ok().map_or(false, |s| s != BlockStatus::Unknown)
2000	}
2001
2002	/// Is any peer downloading the given hash?
2003	fn is_already_downloading(&self, hash: &B::Hash) -> bool {
2004		self.peers
2005			.iter()
2006			.any(|(_, p)| p.state == PeerSyncState::DownloadingStale(*hash))
2007	}
2008
2009	/// Get the set of downloaded blocks that are ready to be queued for import.
2010	fn ready_blocks(&mut self) -> Vec<IncomingBlock<B>> {
2011		self.blocks
2012			.ready_blocks(self.best_queued_number + One::one())
2013			.into_iter()
2014			.map(|block_data| {
2015				let justifications = block_data
2016					.block
2017					.justifications
2018					.or_else(|| legacy_justification_mapping(block_data.block.justification));
2019				IncomingBlock {
2020					hash: block_data.block.hash,
2021					header: block_data.block.header,
2022					body: block_data.block.body,
2023					indexed_body: block_data.block.indexed_body,
2024					justifications,
2025					origin: block_data.origin,
2026					allow_missing_state: true,
2027					import_existing: self.import_existing,
2028					skip_execution: self.skip_execution(),
2029					state: None,
2030				}
2031			})
2032			.collect()
2033	}
2034
2035	/// Get justification requests scheduled by sync to be sent out.
2036	fn justification_requests(&mut self) -> Vec<(PeerId, BlockRequest<B>)> {
2037		let peers = &mut self.peers;
2038		let mut matcher = self.extra_justifications.matcher();
2039		std::iter::from_fn(move || {
2040			if let Some((peer, request)) = matcher.next(peers) {
2041				peers
2042					.get_mut(&peer)
2043					.expect(
2044						"`Matcher::next` guarantees the `PeerId` comes from the given peers; qed",
2045					)
2046					.state = PeerSyncState::DownloadingJustification(request.0);
2047				let req = BlockRequest::<B> {
2048					id: 0,
2049					fields: BlockAttributes::JUSTIFICATION,
2050					from: FromBlock::Hash(request.0),
2051					direction: Direction::Ascending,
2052					max: Some(1),
2053				};
2054				Some((peer, req))
2055			} else {
2056				None
2057			}
2058		})
2059		.collect()
2060	}
2061
2062	/// Block attributes for gap requests, plus the body cutoff: bodies are required for
2063	/// gap blocks above the cutoff and stripped from ranges entirely at or below it.
2064	/// Recomputed every scheduling pass so the cutoff follows finality.
2065	fn gap_request_attributes(
2066		&self,
2067		finalized_number: NumberFor<B>,
2068	) -> (BlockAttributes, Option<NumberFor<B>>) {
2069		let attrs = self.mode.required_block_attributes();
2070		match self.gap_sync_body_policy {
2071			GapSyncBodyPolicy::HeadersOnly => (attrs & !BlockAttributes::BODY, None),
2072			GapSyncBodyPolicy::All => (attrs, None),
2073			GapSyncBodyPolicy::BodiesWithinWindow(window) => {
2074				// The gap is created by importing a finalized block right above it,
2075				// so `gap.target + 1` is a lower bound for the finalized number even
2076				// while the client's finality info still lags right after warp sync.
2077				let anchor = self.gap_sync.as_ref().map_or(finalized_number, |gap| {
2078					std::cmp::max(finalized_number, gap.target + One::one())
2079				});
2080				(attrs, Some(anchor.saturating_sub(window.into())))
2081			},
2082		}
2083	}
2084
2085	/// Get block requests scheduled by sync to be sent out.
2086	fn block_requests(&mut self) -> Vec<(PeerId, BlockRequest<B>)> {
2087		if self.allowed_requests.is_empty() || self.state_sync.is_some() {
2088			return Vec::new();
2089		}
2090
2091		if self.queue_blocks.len() > MAX_IMPORTING_BLOCKS {
2092			trace!(target: LOG_TARGET, "Too many blocks in the queue.");
2093			return Vec::new();
2094		}
2095		let is_major_syncing = self.status().state.is_major_syncing();
2096		let mode = self.mode;
2097		let finalized_number = self.client.info().finalized_number;
2098		let (gap_attrs, gap_body_cutoff) = self.gap_request_attributes(finalized_number);
2099		if let (Some(metrics), Some(cutoff), Some(gap)) =
2100			(self.metrics.as_ref(), gap_body_cutoff, self.gap_sync.as_ref())
2101		{
2102			// The oldest gap block still requiring a body: the lowest not-yet-queued
2103			// block, clamped from below by the cutoff.
2104			let oldest_required = std::cmp::max(gap.best_queued_number, cutoff) + One::one();
2105			metrics.gap_oldest_required_body.set(if oldest_required <= gap.target {
2106				oldest_required.saturated_into::<u64>()
2107			} else {
2108				0
2109			});
2110		}
2111		let blocks = &mut self.blocks;
2112		let fork_targets = &mut self.fork_targets;
2113		let last_finalized = std::cmp::min(self.best_queued_number, finalized_number);
2114		let best_queued = self.best_queued_number;
2115		let client = &self.client;
2116		let queue_blocks = &self.queue_blocks;
2117		let allowed_requests = self.allowed_requests.clone();
2118		let max_parallel = if is_major_syncing { 1 } else { self.max_parallel_downloads };
2119		let max_blocks_per_request = self.max_blocks_per_request;
2120		let gap_sync = &mut self.gap_sync;
2121		let disconnected_peers = &mut self.disconnected_peers;
2122		let metrics = self.metrics.as_ref();
2123		let requests = self
2124			.peers
2125			.iter_mut()
2126			.filter_map(move |(&id, peer)| {
2127				if !peer.state.is_available() ||
2128					!allowed_requests.contains(&id) ||
2129					!disconnected_peers.is_peer_available(&id)
2130				{
2131					return None;
2132				}
2133
2134				// If our best queued is more than `MAX_BLOCKS_TO_LOOK_BACKWARDS` blocks away from
2135				// the common number, the peer best number is higher than our best queued and the
2136				// common number is smaller than the last finalized block number, we should do an
2137				// ancestor search to find a better common block. If the queue is full we wait till
2138				// all blocks are imported though.
2139				if best_queued.saturating_sub(peer.common_number) >
2140					MAX_BLOCKS_TO_LOOK_BACKWARDS.into() &&
2141					best_queued < peer.best_number &&
2142					peer.common_number < last_finalized &&
2143					queue_blocks.len() <= MAJOR_SYNC_BLOCKS as usize
2144				{
2145					trace!(
2146						target: LOG_TARGET,
2147						"Peer {:?} common block {} too far behind of our best {}. Starting ancestry search.",
2148						id,
2149						peer.common_number,
2150						best_queued,
2151					);
2152					let current = std::cmp::min(peer.best_number, best_queued);
2153					peer.state = PeerSyncState::AncestorSearch {
2154						current,
2155						start: best_queued,
2156						state: AncestorSearchState::ExponentialBackoff(One::one()),
2157					};
2158					Some((id, ancestry_request::<B>(current)))
2159				} else if let Some((range, req)) = peer_block_request(
2160					&id,
2161					peer,
2162					blocks,
2163					mode.required_block_attributes(),
2164					max_parallel,
2165					max_blocks_per_request,
2166					last_finalized,
2167					best_queued,
2168				) {
2169					peer.state = PeerSyncState::DownloadingNew(range.start);
2170					trace!(
2171						target: LOG_TARGET,
2172						"New block request for {}, (best:{}, common:{}) {:?}",
2173						id,
2174						peer.best_number,
2175						peer.common_number,
2176						req,
2177					);
2178					Some((id, req))
2179				} else if let Some((hash, req)) = fork_sync_request(
2180					&id,
2181					fork_targets,
2182					best_queued,
2183					last_finalized,
2184					mode.required_block_attributes(),
2185					|hash| {
2186						if queue_blocks.contains(hash) {
2187							BlockStatus::Queued
2188						} else {
2189							client.block_status(*hash).unwrap_or(BlockStatus::Unknown)
2190						}
2191					},
2192					max_blocks_per_request,
2193					metrics,
2194				) {
2195					trace!(target: LOG_TARGET, "Downloading fork {hash:?} from {id}");
2196					peer.state = PeerSyncState::DownloadingStale(hash);
2197					Some((id, req))
2198				} else if let Some((range, req)) = gap_sync.as_mut().and_then(|sync| {
2199					peer_gap_block_request(
2200						&id,
2201						peer,
2202						&mut sync.blocks,
2203						gap_attrs,
2204						gap_body_cutoff,
2205						is_major_syncing,
2206						sync.target,
2207						sync.best_queued_number,
2208						max_blocks_per_request,
2209						metrics,
2210					)
2211				}) {
2212					peer.state = PeerSyncState::DownloadingGap(range.start);
2213					trace!(
2214						target: LOG_TARGET,
2215						"New gap block request for {}, (best:{}, common:{}) {:?}",
2216						id,
2217						peer.best_number,
2218						peer.common_number,
2219						req,
2220					);
2221					Some((id, req))
2222				} else {
2223					None
2224				}
2225			})
2226			.collect::<Vec<_>>();
2227
2228		// Clear the allowed_requests state when sending new block requests
2229		// to prevent multiple inflight block requests from being issued.
2230		if !requests.is_empty() {
2231			self.allowed_requests.take();
2232		}
2233
2234		requests
2235	}
2236
2237	/// Get a state request scheduled by sync to be sent out (if any).
2238	fn state_request(&mut self) -> Option<(PeerId, StateRequest)> {
2239		if self.allowed_requests.is_empty() {
2240			return None;
2241		}
2242		if self.state_sync.is_some() &&
2243			self.peers.iter().any(|(_, peer)| peer.state == PeerSyncState::DownloadingState)
2244		{
2245			// Only one pending state request is allowed.
2246			return None;
2247		}
2248		if let Some(sync) = &self.state_sync {
2249			if sync.is_complete() {
2250				return None;
2251			}
2252
2253			for (id, peer) in self.peers.iter_mut() {
2254				if peer.state.is_available() &&
2255					peer.common_number >= sync.target_number() &&
2256					self.disconnected_peers.is_peer_available(&id)
2257				{
2258					peer.state = PeerSyncState::DownloadingState;
2259					let request = sync.next_request();
2260					trace!(target: LOG_TARGET, "New StateRequest for {}: {:?}", id, request);
2261					self.allowed_requests.clear();
2262					return Some((*id, request));
2263				}
2264			}
2265		}
2266		None
2267	}
2268
2269	#[must_use]
2270	fn on_state_data(&mut self, peer_id: &PeerId, response: &[u8]) -> Result<(), BadPeer> {
2271		let response = match StateResponse::decode(response) {
2272			Ok(response) => response,
2273			Err(error) => {
2274				debug!(
2275					target: LOG_TARGET,
2276					"Failed to decode state response from peer {peer_id:?}: {error:?}.",
2277				);
2278
2279				return Err(BadPeer(*peer_id, rep::BAD_RESPONSE));
2280			},
2281		};
2282
2283		if let Some(peer) = self.peers.get_mut(peer_id) {
2284			if let PeerSyncState::DownloadingState = peer.state {
2285				peer.state = PeerSyncState::Available;
2286				self.allowed_requests.set_all();
2287			}
2288		}
2289		let import_result = if let Some(sync) = &mut self.state_sync {
2290			debug!(
2291				target: LOG_TARGET,
2292				"Importing state data from {} with {} keys, {} proof nodes.",
2293				peer_id,
2294				response.entries.len(),
2295				response.proof.len(),
2296			);
2297			sync.import(response)
2298		} else {
2299			debug!(target: LOG_TARGET, "Ignored obsolete state response from {peer_id}");
2300			return Err(BadPeer(*peer_id, rep::NOT_REQUESTED));
2301		};
2302
2303		match import_result {
2304			ImportResult::Import(hash, header, state, body, justifications) => {
2305				let origin = BlockOrigin::NetworkInitialSync;
2306				let block = IncomingBlock {
2307					hash,
2308					header: Some(header),
2309					body,
2310					indexed_body: None,
2311					justifications,
2312					origin: None,
2313					allow_missing_state: true,
2314					import_existing: true,
2315					skip_execution: self.skip_execution(),
2316					state: Some(state),
2317				};
2318				debug!(target: LOG_TARGET, "State download is complete. Import is queued");
2319				self.actions.push(SyncingAction::ImportBlocks { origin, blocks: vec![block] });
2320				Ok(())
2321			},
2322			ImportResult::Continue => Ok(()),
2323			ImportResult::BadResponse => {
2324				debug!(target: LOG_TARGET, "Bad state data received from {peer_id}");
2325				Err(BadPeer(*peer_id, rep::BAD_BLOCK))
2326			},
2327		}
2328	}
2329
2330	fn attempt_state_sync(
2331		&mut self,
2332		finalized_hash: B::Hash,
2333		finalized_number: NumberFor<B>,
2334		skip_proofs: bool,
2335	) {
2336		let mut heads: Vec<_> = self.peers.values().map(|peer| peer.best_number).collect();
2337		heads.sort();
2338		let median = heads[heads.len() / 2];
2339		if finalized_number + STATE_SYNC_FINALITY_THRESHOLD.saturated_into() >= median {
2340			if let Ok(Some(header)) = self.client.header(finalized_hash) {
2341				log::debug!(
2342					target: LOG_TARGET,
2343					"Starting state sync for #{finalized_number} ({finalized_hash})",
2344				);
2345				self.state_sync =
2346					Some(StateSync::new(self.client.clone(), header, None, None, skip_proofs));
2347				self.allowed_requests.set_all();
2348			} else {
2349				log::error!(
2350					target: LOG_TARGET,
2351					"Failed to start state sync: header for finalized block \
2352					  #{finalized_number} ({finalized_hash}) is not available",
2353				);
2354				debug_assert!(false);
2355			}
2356		}
2357	}
2358
2359	/// A version of `actions()` that doesn't schedule extra requests. For testing only.
2360	#[cfg(test)]
2361	#[must_use]
2362	fn take_actions(&mut self) -> impl Iterator<Item = SyncingAction<B>> {
2363		std::mem::take(&mut self.actions).into_iter()
2364	}
2365}
2366
2367// This is purely during a backwards compatible transitionary period and should be removed
2368// once we can assume all nodes can send and receive multiple Justifications
2369// The ID tag is hardcoded here to avoid depending on the GRANDPA crate.
2370// See: https://github.com/paritytech/substrate/issues/8172
2371fn legacy_justification_mapping(
2372	justification: Option<EncodedJustification>,
2373) -> Option<Justifications> {
2374	justification.map(|just| (*b"FRNK", just).into())
2375}
2376
2377/// Request the ancestry for a block. Sends a request for header and justification for the given
2378/// block number. Used during ancestry search.
2379fn ancestry_request<B: BlockT>(block: NumberFor<B>) -> BlockRequest<B> {
2380	BlockRequest::<B> {
2381		id: 0,
2382		fields: BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION,
2383		from: FromBlock::Number(block),
2384		direction: Direction::Ascending,
2385		max: Some(1),
2386	}
2387}
2388
2389/// The ancestor search state expresses which algorithm, and its stateful parameters, we are using
2390/// to try to find an ancestor block
2391#[derive(Copy, Clone, Eq, PartialEq, Debug)]
2392pub(crate) enum AncestorSearchState<B: BlockT> {
2393	/// Use exponential backoff to find an ancestor, then switch to binary search.
2394	/// We keep track of the exponent.
2395	ExponentialBackoff(NumberFor<B>),
2396	/// Using binary search to find the best ancestor.
2397	/// We keep track of left and right bounds.
2398	BinarySearch(NumberFor<B>, NumberFor<B>),
2399}
2400
2401/// This function handles the ancestor search strategy used. The goal is to find a common point
2402/// that both our chains agree on that is as close to the tip as possible.
2403/// The way this works is we first have an exponential backoff strategy, where we try to step
2404/// forward until we find a block hash mismatch. The size of the step doubles each step we take.
2405///
2406/// When we've found a block hash mismatch we then fall back to a binary search between the two
2407/// last known points to find the common block closest to the tip.
2408fn handle_ancestor_search_state<B: BlockT>(
2409	state: &AncestorSearchState<B>,
2410	curr_block_num: NumberFor<B>,
2411	block_hash_match: bool,
2412) -> Option<(AncestorSearchState<B>, NumberFor<B>)> {
2413	let two = <NumberFor<B>>::one() + <NumberFor<B>>::one();
2414	match state {
2415		AncestorSearchState::ExponentialBackoff(next_distance_to_tip) => {
2416			let next_distance_to_tip = *next_distance_to_tip;
2417			if block_hash_match && next_distance_to_tip == One::one() {
2418				// We found the ancestor in the first step so there is no need to execute binary
2419				// search.
2420				return None;
2421			}
2422			if block_hash_match {
2423				let left = curr_block_num;
2424				let right = left + next_distance_to_tip / two;
2425				let middle = left + (right - left) / two;
2426				Some((AncestorSearchState::BinarySearch(left, right), middle))
2427			} else {
2428				let next_block_num =
2429					curr_block_num.checked_sub(&next_distance_to_tip).unwrap_or_else(Zero::zero);
2430				let next_distance_to_tip = next_distance_to_tip * two;
2431				Some((
2432					AncestorSearchState::ExponentialBackoff(next_distance_to_tip),
2433					next_block_num,
2434				))
2435			}
2436		},
2437		AncestorSearchState::BinarySearch(mut left, mut right) => {
2438			if left >= curr_block_num {
2439				return None;
2440			}
2441			if block_hash_match {
2442				left = curr_block_num;
2443			} else {
2444				right = curr_block_num;
2445			}
2446			assert!(right >= left);
2447			let middle = left + (right - left) / two;
2448			if middle == curr_block_num {
2449				None
2450			} else {
2451				Some((AncestorSearchState::BinarySearch(left, right), middle))
2452			}
2453		},
2454	}
2455}
2456
2457/// Get a new block request for the peer if any.
2458fn peer_block_request<B: BlockT>(
2459	id: &PeerId,
2460	peer: &PeerSync<B>,
2461	blocks: &mut BlockCollection<B>,
2462	attrs: BlockAttributes,
2463	max_parallel_downloads: u32,
2464	max_blocks_per_request: u32,
2465	finalized: NumberFor<B>,
2466	best_num: NumberFor<B>,
2467) -> Option<(Range<NumberFor<B>>, BlockRequest<B>)> {
2468	if best_num >= peer.best_number {
2469		// Will be downloaded as alternative fork instead.
2470		return None;
2471	} else if peer.common_number < finalized {
2472		trace!(
2473			target: LOG_TARGET,
2474			"Requesting pre-finalized chain from {:?}, common={}, finalized={}, peer best={}, our best={}",
2475			id, peer.common_number, finalized, peer.best_number, best_num,
2476		);
2477	}
2478	let range = blocks.needed_blocks(
2479		*id,
2480		max_blocks_per_request,
2481		peer.best_number,
2482		peer.common_number,
2483		max_parallel_downloads,
2484		MAX_DOWNLOAD_AHEAD,
2485	)?;
2486
2487	// The end is not part of the range.
2488	let last = range.end.saturating_sub(One::one());
2489
2490	let from = if peer.best_number == last {
2491		FromBlock::Hash(peer.best_hash)
2492	} else {
2493		FromBlock::Number(last)
2494	};
2495
2496	let request = BlockRequest::<B> {
2497		id: 0,
2498		fields: attrs,
2499		from,
2500		direction: Direction::Descending,
2501		max: Some((range.end - range.start).saturated_into::<u32>()),
2502	};
2503
2504	Some((range, request))
2505}
2506
2507/// Get a new gap block request for the peer if any.
2508///
2509/// Bodies are stripped from the request if the whole range is at or below
2510/// `body_cutoff`. A range straddling the cutoff requests bodies for all its blocks
2511/// rather than being split.
2512///
2513/// While the node is major syncing, the cutoff is a stale lower bound of the true one
2514/// (peers may have pruned bodies right above it), so scheduling is clamped to the
2515/// header-only region at or below it until the node has caught up.
2516fn peer_gap_block_request<B: BlockT>(
2517	id: &PeerId,
2518	peer: &PeerSync<B>,
2519	blocks: &mut BlockCollection<B>,
2520	attrs: BlockAttributes,
2521	body_cutoff: Option<NumberFor<B>>,
2522	is_major_syncing: bool,
2523	target: NumberFor<B>,
2524	common_number: NumberFor<B>,
2525	max_blocks_per_request: u32,
2526	metrics: Option<&Metrics>,
2527) -> Option<(Range<NumberFor<B>>, BlockRequest<B>)> {
2528	let mut scheduling_bound = std::cmp::min(peer.best_number, target);
2529	if let Some(cutoff) = body_cutoff.filter(|_| is_major_syncing) {
2530		scheduling_bound = std::cmp::min(scheduling_bound, cutoff);
2531	}
2532	let range = blocks.needed_blocks(
2533		*id,
2534		max_blocks_per_request,
2535		scheduling_bound,
2536		common_number,
2537		1,
2538		MAX_DOWNLOAD_AHEAD,
2539	)?;
2540
2541	// The end is not part of the range.
2542	let last = range.end.saturating_sub(One::one());
2543	let from = FromBlock::Number(last);
2544
2545	let attrs = match body_cutoff {
2546		Some(cutoff) if last <= cutoff => {
2547			if let Some(metrics) = metrics {
2548				metrics.gap_header_only_downgrades.inc();
2549			}
2550			attrs & !BlockAttributes::BODY
2551		},
2552		_ => attrs,
2553	};
2554
2555	let request = BlockRequest::<B> {
2556		id: 0,
2557		fields: attrs,
2558		from,
2559		direction: Direction::Descending,
2560		max: Some((range.end - range.start).saturated_into::<u32>()),
2561	};
2562	Some((range, request))
2563}
2564
2565/// Get pending fork sync targets for a peer.
2566fn fork_sync_request<B: BlockT>(
2567	id: &PeerId,
2568	fork_targets: &mut HashMap<B::Hash, ForkTarget<B>>,
2569	best_num: NumberFor<B>,
2570	finalized: NumberFor<B>,
2571	attributes: BlockAttributes,
2572	check_block: impl Fn(&B::Hash) -> BlockStatus,
2573	max_blocks_per_request: u32,
2574	metrics: Option<&Metrics>,
2575) -> Option<(B::Hash, BlockRequest<B>)> {
2576	fork_targets.retain(|hash, r| {
2577		if r.number <= finalized {
2578			trace!(
2579				target: LOG_TARGET,
2580				"Removed expired fork sync request {:?} (#{})",
2581				hash,
2582				r.number,
2583			);
2584			return false;
2585		}
2586		if check_block(hash) != BlockStatus::Unknown {
2587			trace!(
2588				target: LOG_TARGET,
2589				"Removed obsolete fork sync request {:?} (#{})",
2590				hash,
2591				r.number,
2592			);
2593			return false;
2594		}
2595		true
2596	});
2597	if let Some(metrics) = metrics {
2598		metrics.fork_targets.set(fork_targets.len().try_into().unwrap_or(u64::MAX));
2599	}
2600	for (hash, r) in fork_targets {
2601		if !r.peers.contains(&id) {
2602			continue;
2603		}
2604		// Download the fork only if it is behind or not too far ahead our tip of the chain
2605		// Otherwise it should be downloaded in full sync mode.
2606		if r.number <= best_num ||
2607			(r.number - best_num).saturated_into::<u32>() < max_blocks_per_request as u32
2608		{
2609			let parent_status = r.parent_hash.as_ref().map_or(BlockStatus::Unknown, check_block);
2610			let count = if parent_status == BlockStatus::Unknown {
2611				(r.number - finalized).saturated_into::<u32>() // up to the last finalized block
2612			} else {
2613				// request only single block
2614				1
2615			};
2616			trace!(
2617				target: LOG_TARGET,
2618				"Downloading requested fork {hash:?} from {id}, {count} blocks",
2619			);
2620			return Some((
2621				*hash,
2622				BlockRequest::<B> {
2623					id: 0,
2624					fields: attributes,
2625					from: FromBlock::Hash(*hash),
2626					direction: Direction::Descending,
2627					max: Some(count),
2628				},
2629			));
2630		} else {
2631			trace!(target: LOG_TARGET, "Fork too far in the future: {:?} (#{})", hash, r.number);
2632		}
2633	}
2634	None
2635}
2636
2637/// Returns `true` if the given `block` is a descendent of `base`.
2638fn is_descendent_of<Block, T>(
2639	client: &T,
2640	base: &Block::Hash,
2641	block: &Block::Hash,
2642) -> sp_blockchain::Result<bool>
2643where
2644	Block: BlockT,
2645	T: HeaderMetadata<Block, Error = sp_blockchain::Error> + ?Sized,
2646{
2647	if base == block {
2648		return Ok(false);
2649	}
2650
2651	let ancestor = sp_blockchain::lowest_common_ancestor(client, *block, *base)?;
2652
2653	Ok(ancestor.hash == *base)
2654}
2655
2656/// Validate that the given `blocks` are correct.
2657/// Returns the number of the first block in the sequence.
2658///
2659/// It is expected that `blocks` are in ascending order.
2660pub fn validate_blocks<Block: BlockT>(
2661	blocks: &Vec<BlockData<Block>>,
2662	peer_id: &PeerId,
2663	request: Option<BlockRequest<Block>>,
2664) -> Result<Option<NumberFor<Block>>, BadPeer> {
2665	if let Some(request) = request {
2666		if Some(blocks.len() as _) > request.max {
2667			debug!(
2668				target: LOG_TARGET,
2669				"Received more blocks than requested from {}. Expected in maximum {:?}, got {}.",
2670				peer_id,
2671				request.max,
2672				blocks.len(),
2673			);
2674
2675			return Err(BadPeer(*peer_id, rep::NOT_REQUESTED));
2676		}
2677
2678		let block_header =
2679			if request.direction == Direction::Descending { blocks.last() } else { blocks.first() }
2680				.and_then(|b| b.header.as_ref());
2681
2682		let expected_block = block_header.as_ref().map_or(false, |h| match request.from {
2683			FromBlock::Hash(hash) => h.hash() == hash,
2684			FromBlock::Number(n) => h.number() == &n,
2685		});
2686
2687		if !expected_block {
2688			debug!(
2689				target: LOG_TARGET,
2690				"Received block that was not requested. Requested {:?}, got {:?}.",
2691				request.from,
2692				block_header,
2693			);
2694
2695			return Err(BadPeer(*peer_id, rep::NOT_REQUESTED));
2696		}
2697
2698		if request.fields.contains(BlockAttributes::HEADER) &&
2699			blocks.iter().any(|b| b.header.is_none())
2700		{
2701			trace!(
2702				target: LOG_TARGET,
2703				"Missing requested header for a block in response from {peer_id}.",
2704			);
2705
2706			return Err(BadPeer(*peer_id, rep::BAD_RESPONSE));
2707		}
2708
2709		if request.fields.contains(BlockAttributes::BODY) && blocks.iter().any(|b| b.body.is_none())
2710		{
2711			trace!(
2712				target: LOG_TARGET,
2713				"Missing requested body for a block in response from {peer_id}.",
2714			);
2715
2716			return Err(BadPeer(*peer_id, rep::BAD_RESPONSE));
2717		}
2718	}
2719
2720	for b in blocks {
2721		if let Some(header) = &b.header {
2722			let hash = header.hash();
2723			if hash != b.hash {
2724				debug!(
2725					target: LOG_TARGET,
2726					"Bad header received from {}. Expected hash {:?}, got {:?}",
2727					peer_id,
2728					b.hash,
2729					hash,
2730				);
2731				return Err(BadPeer(*peer_id, rep::BAD_BLOCK));
2732			}
2733		}
2734	}
2735
2736	Ok(blocks.first().and_then(|b| b.header.as_ref()).map(|h| *h.number()))
2737}