referrerpolicy=no-referrer-when-downgrade

sc_network_sync/strategy/
state.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//! State sync strategy.
20
21use crate::{
22	schema::v1::{StateRequest, StateResponse},
23	service::network::NetworkServiceHandle,
24	strategy::{
25		disconnected_peers::DisconnectedPeers,
26		state_sync::{ImportResult, StateSync, StateSyncProvider},
27		StrategyKey, SyncingAction,
28	},
29	types::{BadPeer, SyncState, SyncStatus},
30	LOG_TARGET,
31};
32use futures::{channel::oneshot, FutureExt};
33use log::{debug, error, trace};
34use prost::Message;
35use sc_client_api::ProofProvider;
36use sc_consensus::{BlockImportError, BlockImportStatus, IncomingBlock};
37use sc_network::{IfDisconnected, ProtocolName};
38use sc_network_common::sync::message::BlockAnnounce;
39use sc_network_types::PeerId;
40use sp_consensus::BlockOrigin;
41use sp_runtime::{
42	traits::{Block as BlockT, Header, NumberFor},
43	Justifications, SaturatedConversion,
44};
45use std::{any::Any, collections::HashMap, sync::Arc};
46
47mod rep {
48	use sc_network::ReputationChange as Rep;
49
50	/// Peer response data does not have requested bits.
51	pub const BAD_RESPONSE: Rep = Rep::new(-(1 << 12), "Incomplete response");
52
53	/// Reputation change for peers which send us a known bad state.
54	pub const BAD_STATE: Rep = Rep::new(-(1 << 29), "Bad state");
55}
56
57enum PeerState {
58	Available,
59	DownloadingState,
60}
61
62impl PeerState {
63	fn is_available(&self) -> bool {
64		matches!(self, PeerState::Available)
65	}
66}
67
68struct Peer<B: BlockT> {
69	best_number: NumberFor<B>,
70	state: PeerState,
71}
72
73/// Syncing strategy that downloads and imports a recent state directly.
74pub struct StateStrategy<B: BlockT> {
75	state_sync: Box<dyn StateSyncProvider<B>>,
76	peers: HashMap<PeerId, Peer<B>>,
77	disconnected_peers: DisconnectedPeers,
78	actions: Vec<SyncingAction<B>>,
79	protocol_name: ProtocolName,
80	succeeded: bool,
81}
82
83impl<B: BlockT> StateStrategy<B> {
84	/// Strategy key used by state sync.
85	pub const STRATEGY_KEY: StrategyKey = StrategyKey::new("State");
86
87	/// Create a new instance.
88	pub fn new<Client>(
89		client: Arc<Client>,
90		target_header: B::Header,
91		target_body: Option<Vec<B::Extrinsic>>,
92		target_justifications: Option<Justifications>,
93		skip_proof: bool,
94		initial_peers: impl Iterator<Item = (PeerId, NumberFor<B>)>,
95		protocol_name: ProtocolName,
96	) -> Self
97	where
98		Client: ProofProvider<B> + Send + Sync + 'static,
99	{
100		let peers = initial_peers
101			.map(|(peer_id, best_number)| {
102				(peer_id, Peer { best_number, state: PeerState::Available })
103			})
104			.collect();
105		Self {
106			state_sync: Box::new(StateSync::new(
107				client,
108				target_header,
109				target_body,
110				target_justifications,
111				skip_proof,
112			)),
113			peers,
114			disconnected_peers: DisconnectedPeers::new(),
115			actions: Vec::new(),
116			protocol_name,
117			succeeded: false,
118		}
119	}
120
121	/// Create a new instance with a custom state sync provider.
122	///
123	/// Note: In most cases, users should use [`StateStrategy::new`].
124	/// This method is intended for custom sync strategies and advanced use cases.
125	pub fn new_with_provider(
126		state_sync_provider: Box<dyn StateSyncProvider<B>>,
127		initial_peers: impl Iterator<Item = (PeerId, NumberFor<B>)>,
128		protocol_name: ProtocolName,
129	) -> Self {
130		Self {
131			state_sync: state_sync_provider,
132			peers: initial_peers
133				.map(|(peer_id, best_number)| {
134					(peer_id, Peer { best_number, state: PeerState::Available })
135				})
136				.collect(),
137			disconnected_peers: DisconnectedPeers::new(),
138			actions: Vec::new(),
139			protocol_name,
140			succeeded: false,
141		}
142	}
143
144	/// Notify that a new peer has connected.
145	pub fn add_peer(&mut self, peer_id: PeerId, _best_hash: B::Hash, best_number: NumberFor<B>) {
146		self.peers.insert(peer_id, Peer { best_number, state: PeerState::Available });
147	}
148
149	/// Notify that a peer has disconnected.
150	pub fn remove_peer(&mut self, peer_id: &PeerId) {
151		if let Some(state) = self.peers.remove(peer_id) {
152			if !state.state.is_available() {
153				if let Some(bad_peer) =
154					self.disconnected_peers.on_disconnect_during_request(*peer_id)
155				{
156					self.actions.push(SyncingAction::DropPeer(bad_peer));
157				}
158			}
159		}
160	}
161
162	/// Submit a validated block announcement.
163	///
164	/// Returns new best hash & best number of the peer if they are updated.
165	#[must_use]
166	pub fn on_validated_block_announce(
167		&mut self,
168		is_best: bool,
169		peer_id: PeerId,
170		announce: &BlockAnnounce<B::Header>,
171	) -> Option<(B::Hash, NumberFor<B>)> {
172		is_best.then(|| {
173			let best_number = *announce.header.number();
174			let best_hash = announce.header.hash();
175			if let Some(ref mut peer) = self.peers.get_mut(&peer_id) {
176				peer.best_number = best_number;
177			}
178			// Let `SyncingEngine` know that we should update the peer info.
179			(best_hash, best_number)
180		})
181	}
182
183	/// Process state response.
184	pub fn on_state_response(&mut self, peer_id: &PeerId, response: Vec<u8>) {
185		if let Err(bad_peer) = self.on_state_response_inner(peer_id, &response) {
186			self.actions.push(SyncingAction::DropPeer(bad_peer));
187		}
188	}
189
190	fn on_state_response_inner(
191		&mut self,
192		peer_id: &PeerId,
193		response: &[u8],
194	) -> Result<(), BadPeer> {
195		if let Some(peer) = self.peers.get_mut(&peer_id) {
196			peer.state = PeerState::Available;
197		}
198
199		let response = match StateResponse::decode(response) {
200			Ok(response) => response,
201			Err(error) => {
202				debug!(
203					target: LOG_TARGET,
204					"Failed to decode state response from peer {peer_id:?}: {error:?}.",
205				);
206
207				return Err(BadPeer(*peer_id, rep::BAD_RESPONSE));
208			},
209		};
210
211		debug!(
212			target: LOG_TARGET,
213			"Importing state data from {} with {} keys, {} proof nodes.",
214			peer_id,
215			response.entries.len(),
216			response.proof.len(),
217		);
218
219		match self.state_sync.import(response) {
220			ImportResult::Import(hash, header, state, body, justifications) => {
221				let origin = BlockOrigin::NetworkInitialSync;
222				let block = IncomingBlock {
223					hash,
224					header: Some(header),
225					body,
226					justifications,
227					origin: None,
228					allow_missing_state: true,
229					import_existing: true,
230					skip_execution: true,
231					state: Some(state),
232				};
233				debug!(target: LOG_TARGET, "State download is complete. Import is queued");
234				self.actions.push(SyncingAction::ImportBlocks { origin, blocks: vec![block] });
235				Ok(())
236			},
237			ImportResult::Continue => Ok(()),
238			ImportResult::BadResponse => {
239				debug!(target: LOG_TARGET, "Bad state data received from {peer_id}");
240				Err(BadPeer(*peer_id, rep::BAD_STATE))
241			},
242		}
243	}
244
245	/// A batch of blocks have been processed, with or without errors.
246	///
247	/// Normally this should be called when target block with state is imported.
248	pub fn on_blocks_processed(
249		&mut self,
250		imported: usize,
251		count: usize,
252		results: Vec<(Result<BlockImportStatus<NumberFor<B>>, BlockImportError>, B::Hash)>,
253	) {
254		trace!(target: LOG_TARGET, "State sync: imported {imported} of {count}.");
255
256		let results = results
257			.into_iter()
258			.filter_map(|(result, hash)| {
259				if hash == self.state_sync.target_hash() {
260					Some(result)
261				} else {
262					debug!(
263						target: LOG_TARGET,
264						"Unexpected block processed: {hash} with result {result:?}.",
265					);
266					None
267				}
268			})
269			.collect::<Vec<_>>();
270
271		if !results.is_empty() {
272			// We processed the target block
273			results.iter().filter_map(|result| result.as_ref().err()).for_each(|e| {
274				error!(
275					target: LOG_TARGET,
276					"Failed to import target block with state: {e:?}."
277				);
278			});
279			self.succeeded |= results.into_iter().any(|result| result.is_ok());
280			self.actions.push(SyncingAction::Finished);
281		}
282	}
283
284	/// Produce state request.
285	fn state_request(&mut self) -> Option<(PeerId, StateRequest)> {
286		if self.state_sync.is_complete() {
287			return None;
288		}
289
290		if self
291			.peers
292			.values()
293			.any(|peer| matches!(peer.state, PeerState::DownloadingState))
294		{
295			// Only one state request at a time is possible.
296			return None;
297		}
298
299		let peer_id =
300			self.schedule_next_peer(PeerState::DownloadingState, self.state_sync.target_number())?;
301		let request = self.state_sync.next_request();
302		trace!(
303			target: LOG_TARGET,
304			"New state request to {peer_id}: {request:?}.",
305		);
306		Some((peer_id, request))
307	}
308
309	fn schedule_next_peer(
310		&mut self,
311		new_state: PeerState,
312		min_best_number: NumberFor<B>,
313	) -> Option<PeerId> {
314		let mut targets: Vec<_> = self.peers.values().map(|p| p.best_number).collect();
315		if targets.is_empty() {
316			return None;
317		}
318		targets.sort();
319		let median = targets[targets.len() / 2];
320		let threshold = std::cmp::max(median, min_best_number);
321		// Find a random peer that is synced as much as peer majority and is above
322		// `min_best_number`.
323		for (peer_id, peer) in self.peers.iter_mut() {
324			if peer.state.is_available() &&
325				peer.best_number >= threshold &&
326				self.disconnected_peers.is_peer_available(peer_id)
327			{
328				peer.state = new_state;
329				return Some(*peer_id);
330			}
331		}
332		None
333	}
334
335	/// Returns the current sync status.
336	pub fn status(&self) -> SyncStatus<B> {
337		SyncStatus {
338			state: if self.state_sync.is_complete() {
339				SyncState::Idle
340			} else {
341				SyncState::Downloading { target: self.state_sync.target_number() }
342			},
343			best_seen_block: Some(self.state_sync.target_number()),
344			num_peers: self.peers.len().saturated_into(),
345			queued_blocks: 0,
346			state_sync: Some(self.state_sync.progress()),
347			warp_sync: None,
348		}
349	}
350
351	/// Get actions that should be performed.
352	#[must_use]
353	pub fn actions(
354		&mut self,
355		network_service: &NetworkServiceHandle,
356	) -> impl Iterator<Item = SyncingAction<B>> {
357		let state_request = self.state_request().into_iter().map(|(peer_id, request)| {
358			let (tx, rx) = oneshot::channel();
359
360			network_service.start_request(
361				peer_id,
362				self.protocol_name.clone(),
363				request.encode_to_vec(),
364				tx,
365				IfDisconnected::ImmediateError,
366			);
367
368			SyncingAction::StartRequest {
369				peer_id,
370				key: Self::STRATEGY_KEY,
371				request: async move {
372					Ok(rx.await?.and_then(|(response, protocol_name)| {
373						Ok((Box::new(response) as Box<dyn Any + Send>, protocol_name))
374					}))
375				}
376				.boxed(),
377			}
378		});
379		self.actions.extend(state_request);
380
381		std::mem::take(&mut self.actions).into_iter()
382	}
383
384	/// Check if state sync has succeeded.
385	#[must_use]
386	pub fn is_succeeded(&self) -> bool {
387		self.succeeded
388	}
389}
390
391#[cfg(test)]
392mod test {
393	use super::*;
394	use crate::{
395		schema::v1::{StateRequest, StateResponse},
396		service::network::NetworkServiceProvider,
397		strategy::state_sync::{ImportResult, StateSync, StateSyncProgress, StateSyncProvider},
398	};
399	use codec::Decode;
400	use sc_block_builder::BlockBuilderBuilder;
401	use sc_client_api::KeyValueStates;
402	use sc_consensus::{ImportedAux, ImportedState};
403	use sp_core::H256;
404	use sp_runtime::traits::Zero;
405	use substrate_test_runtime_client::{
406		runtime::{Block, Hash},
407		BlockBuilderExt, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
408	};
409
410	mockall::mock! {
411		pub StateSync<B: BlockT> {}
412
413		impl<B: BlockT> StateSyncProvider<B> for StateSync<B> {
414			fn import(&mut self, response: StateResponse) -> ImportResult<B>;
415			fn next_request(&self) -> StateRequest;
416			fn is_complete(&self) -> bool;
417			fn target_number(&self) -> NumberFor<B>;
418			fn target_hash(&self) -> B::Hash;
419			fn progress(&self) -> StateSyncProgress;
420		}
421	}
422
423	#[test]
424	fn no_peer_is_scheduled_if_no_peers_connected() {
425		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
426		let target_block = BlockBuilderBuilder::new(&*client)
427			.on_parent_block(client.chain_info().best_hash)
428			.with_parent_block_number(client.chain_info().best_number)
429			.build()
430			.unwrap()
431			.build()
432			.unwrap()
433			.block;
434		let target_header = target_block.header().clone();
435
436		let mut state_strategy = StateStrategy::new(
437			client,
438			target_header,
439			None,
440			None,
441			false,
442			std::iter::empty(),
443			ProtocolName::Static(""),
444		);
445
446		assert!(state_strategy
447			.schedule_next_peer(PeerState::DownloadingState, Zero::zero())
448			.is_none());
449	}
450
451	#[test]
452	fn at_least_median_synced_peer_is_scheduled() {
453		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
454		let target_block = BlockBuilderBuilder::new(&*client)
455			.on_parent_block(client.chain_info().best_hash)
456			.with_parent_block_number(client.chain_info().best_number)
457			.build()
458			.unwrap()
459			.build()
460			.unwrap()
461			.block;
462
463		for _ in 0..100 {
464			let peers = (1..=10)
465				.map(|best_number| (PeerId::random(), best_number))
466				.collect::<HashMap<_, _>>();
467			let initial_peers = peers.iter().map(|(p, n)| (*p, *n));
468
469			let mut state_strategy = StateStrategy::new(
470				client.clone(),
471				target_block.header().clone(),
472				None,
473				None,
474				false,
475				initial_peers,
476				ProtocolName::Static(""),
477			);
478
479			let peer_id =
480				state_strategy.schedule_next_peer(PeerState::DownloadingState, Zero::zero());
481			assert!(*peers.get(&peer_id.unwrap()).unwrap() >= 6);
482		}
483	}
484
485	#[test]
486	fn min_best_number_peer_is_scheduled() {
487		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
488		let target_block = BlockBuilderBuilder::new(&*client)
489			.on_parent_block(client.chain_info().best_hash)
490			.with_parent_block_number(client.chain_info().best_number)
491			.build()
492			.unwrap()
493			.build()
494			.unwrap()
495			.block;
496
497		for _ in 0..10 {
498			let peers = (1..=10)
499				.map(|best_number| (PeerId::random(), best_number))
500				.collect::<HashMap<_, _>>();
501			let initial_peers = peers.iter().map(|(p, n)| (*p, *n));
502
503			let mut state_strategy = StateStrategy::new(
504				client.clone(),
505				target_block.header().clone(),
506				None,
507				None,
508				false,
509				initial_peers,
510				ProtocolName::Static(""),
511			);
512
513			let peer_id = state_strategy.schedule_next_peer(PeerState::DownloadingState, 10);
514			assert!(*peers.get(&peer_id.unwrap()).unwrap() == 10);
515		}
516	}
517
518	#[test]
519	fn backedoff_number_peer_is_not_scheduled() {
520		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
521		let target_block = BlockBuilderBuilder::new(&*client)
522			.on_parent_block(client.chain_info().best_hash)
523			.with_parent_block_number(client.chain_info().best_number)
524			.build()
525			.unwrap()
526			.build()
527			.unwrap()
528			.block;
529
530		let peers = (1..=10)
531			.map(|best_number| (PeerId::random(), best_number))
532			.collect::<Vec<(_, _)>>();
533		let ninth_peer = peers[8].0;
534		let tenth_peer = peers[9].0;
535		let initial_peers = peers.iter().map(|(p, n)| (*p, *n));
536
537		let mut state_strategy = StateStrategy::new(
538			client.clone(),
539			target_block.header().clone(),
540			None,
541			None,
542			false,
543			initial_peers,
544			ProtocolName::Static(""),
545		);
546
547		// Disconnecting a peer without an inflight request has no effect on persistent states.
548		state_strategy.remove_peer(&tenth_peer);
549		assert!(state_strategy.disconnected_peers.is_peer_available(&tenth_peer));
550
551		// Disconnect the peer with an inflight request.
552		state_strategy.add_peer(tenth_peer, H256::random(), 10);
553		let peer_id: Option<PeerId> =
554			state_strategy.schedule_next_peer(PeerState::DownloadingState, 10);
555		assert_eq!(tenth_peer, peer_id.unwrap());
556		state_strategy.remove_peer(&tenth_peer);
557
558		// Peer is backed off.
559		assert!(!state_strategy.disconnected_peers.is_peer_available(&tenth_peer));
560
561		// No peer available for 10'th best block because of the backoff.
562		state_strategy.add_peer(tenth_peer, H256::random(), 10);
563		let peer_id: Option<PeerId> =
564			state_strategy.schedule_next_peer(PeerState::DownloadingState, 10);
565		assert!(peer_id.is_none());
566
567		// Other requests can still happen.
568		let peer_id: Option<PeerId> =
569			state_strategy.schedule_next_peer(PeerState::DownloadingState, 9);
570		assert_eq!(ninth_peer, peer_id.unwrap());
571	}
572
573	#[test]
574	fn state_request_contains_correct_hash() {
575		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
576		let target_block = BlockBuilderBuilder::new(&*client)
577			.on_parent_block(client.chain_info().best_hash)
578			.with_parent_block_number(client.chain_info().best_number)
579			.build()
580			.unwrap()
581			.build()
582			.unwrap()
583			.block;
584
585		let initial_peers = (1..=10).map(|best_number| (PeerId::random(), best_number));
586
587		let mut state_strategy = StateStrategy::new(
588			client.clone(),
589			target_block.header().clone(),
590			None,
591			None,
592			false,
593			initial_peers,
594			ProtocolName::Static(""),
595		);
596
597		let (_peer_id, request) = state_strategy.state_request().unwrap();
598		let hash = Hash::decode(&mut &*request.block).unwrap();
599
600		assert_eq!(hash, target_block.header().hash());
601	}
602
603	#[test]
604	fn no_parallel_state_requests() {
605		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
606		let target_block = BlockBuilderBuilder::new(&*client)
607			.on_parent_block(client.chain_info().best_hash)
608			.with_parent_block_number(client.chain_info().best_number)
609			.build()
610			.unwrap()
611			.build()
612			.unwrap()
613			.block;
614
615		let initial_peers = (1..=10).map(|best_number| (PeerId::random(), best_number));
616
617		let mut state_strategy = StateStrategy::new(
618			client.clone(),
619			target_block.header().clone(),
620			None,
621			None,
622			false,
623			initial_peers,
624			ProtocolName::Static(""),
625		);
626
627		// First request is sent.
628		assert!(state_strategy.state_request().is_some());
629
630		// No parallel request is sent.
631		assert!(state_strategy.state_request().is_none());
632	}
633
634	#[test]
635	fn received_state_response_makes_peer_available_again() {
636		let mut state_sync_provider = MockStateSync::<Block>::new();
637		state_sync_provider.expect_import().return_once(|_| ImportResult::Continue);
638		let peer_id = PeerId::random();
639		let initial_peers = std::iter::once((peer_id, 10));
640		let mut state_strategy = StateStrategy::new_with_provider(
641			Box::new(state_sync_provider),
642			initial_peers,
643			ProtocolName::Static(""),
644		);
645		// Manually set the peer's state.
646		state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState;
647
648		let dummy_response = StateResponse::default().encode_to_vec();
649		state_strategy.on_state_response(&peer_id, dummy_response);
650
651		assert!(state_strategy.peers.get(&peer_id).unwrap().state.is_available());
652	}
653
654	#[test]
655	fn bad_state_response_drops_peer() {
656		let mut state_sync_provider = MockStateSync::<Block>::new();
657		// Provider says that state response is bad.
658		state_sync_provider.expect_import().return_once(|_| ImportResult::BadResponse);
659		let peer_id = PeerId::random();
660		let initial_peers = std::iter::once((peer_id, 10));
661		let mut state_strategy = StateStrategy::new_with_provider(
662			Box::new(state_sync_provider),
663			initial_peers,
664			ProtocolName::Static(""),
665		);
666		// Manually set the peer's state.
667		state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState;
668		let dummy_response = StateResponse::default().encode_to_vec();
669		// Receiving response drops the peer.
670		assert!(matches!(
671			state_strategy.on_state_response_inner(&peer_id, &dummy_response),
672			Err(BadPeer(id, _rep)) if id == peer_id,
673		));
674	}
675
676	#[test]
677	fn empty_unverified_state_response_is_rejected() {
678		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
679		let target_block = BlockBuilderBuilder::new(&*client)
680			.on_parent_block(client.chain_info().best_hash)
681			.with_parent_block_number(client.chain_info().best_number)
682			.build()
683			.unwrap()
684			.build()
685			.unwrap()
686			.block;
687		let mut state_sync =
688			StateSync::new(client, target_block.header().clone(), None, None, true);
689
690		let response = StateResponse { entries: Vec::new(), proof: vec![1] };
691
692		assert!(matches!(state_sync.import(response), ImportResult::BadResponse));
693	}
694
695	#[test]
696	fn empty_unverified_state_response_drops_peer() {
697		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
698		let target_block = BlockBuilderBuilder::new(&*client)
699			.on_parent_block(client.chain_info().best_hash)
700			.with_parent_block_number(client.chain_info().best_number)
701			.build()
702			.unwrap()
703			.build()
704			.unwrap()
705			.block;
706		let peer_id = PeerId::random();
707		let mut state_strategy = StateStrategy::new(
708			client,
709			target_block.header().clone(),
710			None,
711			None,
712			true,
713			std::iter::once((peer_id, 10)),
714			ProtocolName::Static(""),
715		);
716		let response = StateResponse { entries: Vec::new(), proof: vec![1] }.encode_to_vec();
717
718		state_strategy.on_state_response(&peer_id, response);
719
720		assert!(matches!(
721			state_strategy.actions.as_slice(),
722			[SyncingAction::DropPeer(BadPeer(id, _))] if *id == peer_id,
723		));
724	}
725
726	#[test]
727	fn partial_state_response_doesnt_generate_actions() {
728		let mut state_sync_provider = MockStateSync::<Block>::new();
729		// Sync provider says that the response is partial.
730		state_sync_provider.expect_import().return_once(|_| ImportResult::Continue);
731		let peer_id = PeerId::random();
732		let initial_peers = std::iter::once((peer_id, 10));
733		let mut state_strategy = StateStrategy::new_with_provider(
734			Box::new(state_sync_provider),
735			initial_peers,
736			ProtocolName::Static(""),
737		);
738		// Manually set the peer's state .
739		state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState;
740
741		let dummy_response = StateResponse::default().encode_to_vec();
742		state_strategy.on_state_response(&peer_id, dummy_response);
743
744		// No actions generated.
745		assert_eq!(state_strategy.actions.len(), 0)
746	}
747
748	#[test]
749	fn complete_state_response_leads_to_block_import() {
750		// Build block to use for checks.
751		let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
752		let mut block_builder = BlockBuilderBuilder::new(&*client)
753			.on_parent_block(client.chain_info().best_hash)
754			.with_parent_block_number(client.chain_info().best_number)
755			.build()
756			.unwrap();
757		block_builder.push_storage_change(vec![1, 2, 3], Some(vec![4, 5, 6])).unwrap();
758		let block = block_builder.build().unwrap().block;
759		let header = block.header().clone();
760		let hash = header.hash();
761		let body = Some(block.extrinsics().iter().cloned().collect::<Vec<_>>());
762		let state = ImportedState { block: hash, state: KeyValueStates(Vec::new()) };
763		let justifications = Some(Justifications::from((*b"FRNK", Vec::new())));
764
765		// Prepare `StateSync`
766		let mut state_sync_provider = MockStateSync::<Block>::new();
767		let import = ImportResult::Import(
768			hash,
769			header.clone(),
770			state.clone(),
771			body.clone(),
772			justifications.clone(),
773		);
774		state_sync_provider.expect_import().return_once(move |_| import);
775
776		// Reference values to check against.
777		let expected_origin = BlockOrigin::NetworkInitialSync;
778		let expected_block = IncomingBlock {
779			hash,
780			header: Some(header),
781			body,
782			justifications,
783			origin: None,
784			allow_missing_state: true,
785			import_existing: true,
786			skip_execution: true,
787			state: Some(state),
788		};
789		let expected_blocks = vec![expected_block];
790
791		// Prepare `StateStrategy`.
792		let peer_id = PeerId::random();
793		let initial_peers = std::iter::once((peer_id, 10));
794		let mut state_strategy = StateStrategy::new_with_provider(
795			Box::new(state_sync_provider),
796			initial_peers,
797			ProtocolName::Static(""),
798		);
799		// Manually set the peer's state .
800		state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState;
801
802		// Receive response.
803		let dummy_response = StateResponse::default().encode_to_vec();
804		state_strategy.on_state_response(&peer_id, dummy_response);
805
806		assert_eq!(state_strategy.actions.len(), 1);
807		assert!(matches!(
808			&state_strategy.actions[0],
809			SyncingAction::ImportBlocks { origin, blocks }
810				if *origin == expected_origin && *blocks == expected_blocks,
811		));
812	}
813
814	#[test]
815	fn importing_unknown_block_doesnt_finish_strategy() {
816		let target_hash = Hash::random();
817		let unknown_hash = Hash::random();
818		let mut state_sync_provider = MockStateSync::<Block>::new();
819		state_sync_provider.expect_target_hash().return_const(target_hash);
820
821		let mut state_strategy = StateStrategy::new_with_provider(
822			Box::new(state_sync_provider),
823			std::iter::empty(),
824			ProtocolName::Static(""),
825		);
826
827		// Unknown block imported.
828		state_strategy.on_blocks_processed(
829			1,
830			1,
831			vec![(
832				Ok(BlockImportStatus::ImportedUnknown(1, ImportedAux::default(), None)),
833				unknown_hash,
834			)],
835		);
836
837		// No actions generated.
838		assert_eq!(state_strategy.actions.len(), 0);
839	}
840
841	#[test]
842	fn successfully_importing_target_block_finishes_strategy() {
843		let target_hash = Hash::random();
844		let mut state_sync_provider = MockStateSync::<Block>::new();
845		state_sync_provider.expect_target_hash().return_const(target_hash);
846
847		let mut state_strategy = StateStrategy::new_with_provider(
848			Box::new(state_sync_provider),
849			std::iter::empty(),
850			ProtocolName::Static(""),
851		);
852
853		// Target block imported.
854		state_strategy.on_blocks_processed(
855			1,
856			1,
857			vec![(
858				Ok(BlockImportStatus::ImportedUnknown(1, ImportedAux::default(), None)),
859				target_hash,
860			)],
861		);
862
863		// Strategy finishes.
864		assert_eq!(state_strategy.actions.len(), 1);
865		assert!(matches!(&state_strategy.actions[0], SyncingAction::Finished));
866	}
867
868	#[test]
869	fn failure_to_import_target_block_finishes_strategy() {
870		let target_hash = Hash::random();
871		let mut state_sync_provider = MockStateSync::<Block>::new();
872		state_sync_provider.expect_target_hash().return_const(target_hash);
873
874		let mut state_strategy = StateStrategy::new_with_provider(
875			Box::new(state_sync_provider),
876			std::iter::empty(),
877			ProtocolName::Static(""),
878		);
879
880		// Target block import failed.
881		state_strategy.on_blocks_processed(
882			1,
883			1,
884			vec![(
885				Err(BlockImportError::VerificationFailed(None, String::from("test-error"))),
886				target_hash,
887			)],
888		);
889
890		// Strategy finishes.
891		assert_eq!(state_strategy.actions.len(), 1);
892		assert!(matches!(&state_strategy.actions[0], SyncingAction::Finished));
893	}
894
895	#[test]
896	fn finished_strategy_doesnt_generate_more_actions() {
897		let target_hash = Hash::random();
898		let mut state_sync_provider = MockStateSync::<Block>::new();
899		state_sync_provider.expect_target_hash().return_const(target_hash);
900		state_sync_provider.expect_is_complete().return_const(true);
901
902		// Get enough peers for possible spurious requests.
903		let initial_peers = (1..=10).map(|best_number| (PeerId::random(), best_number));
904
905		let mut state_strategy = StateStrategy::new_with_provider(
906			Box::new(state_sync_provider),
907			initial_peers,
908			ProtocolName::Static(""),
909		);
910
911		state_strategy.on_blocks_processed(
912			1,
913			1,
914			vec![(
915				Ok(BlockImportStatus::ImportedUnknown(1, ImportedAux::default(), None)),
916				target_hash,
917			)],
918		);
919
920		let network_provider = NetworkServiceProvider::new();
921		let network_handle = network_provider.handle();
922
923		// Strategy finishes.
924		let actions = state_strategy.actions(&network_handle).collect::<Vec<_>>();
925		assert_eq!(actions.len(), 1);
926		assert!(matches!(&actions[0], SyncingAction::Finished));
927
928		// No more actions generated.
929		assert_eq!(state_strategy.actions(&network_handle).count(), 0);
930	}
931}