1use 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 pub const BAD_RESPONSE: Rep = Rep::new(-(1 << 12), "Incomplete response");
52
53 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
73pub 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 pub const STRATEGY_KEY: StrategyKey = StrategyKey::new("State");
86
87 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 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 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 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 #[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 (best_hash, best_number)
180 })
181 }
182
183 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 indexed_body: None,
227 justifications,
228 origin: None,
229 allow_missing_state: true,
230 import_existing: true,
231 skip_execution: true,
232 state: Some(state),
233 };
234 debug!(target: LOG_TARGET, "State download is complete. Import is queued");
235 self.actions.push(SyncingAction::ImportBlocks { origin, blocks: vec![block] });
236 Ok(())
237 },
238 ImportResult::Continue => Ok(()),
239 ImportResult::BadResponse => {
240 debug!(target: LOG_TARGET, "Bad state data received from {peer_id}");
241 Err(BadPeer(*peer_id, rep::BAD_STATE))
242 },
243 }
244 }
245
246 pub fn on_blocks_processed(
250 &mut self,
251 imported: usize,
252 count: usize,
253 results: Vec<(Result<BlockImportStatus<NumberFor<B>>, BlockImportError>, B::Hash)>,
254 ) {
255 trace!(target: LOG_TARGET, "State sync: imported {imported} of {count}.");
256
257 let results = results
258 .into_iter()
259 .filter_map(|(result, hash)| {
260 if hash == self.state_sync.target_hash() {
261 Some(result)
262 } else {
263 debug!(
264 target: LOG_TARGET,
265 "Unexpected block processed: {hash} with result {result:?}.",
266 );
267 None
268 }
269 })
270 .collect::<Vec<_>>();
271
272 if !results.is_empty() {
273 results.iter().filter_map(|result| result.as_ref().err()).for_each(|e| {
275 error!(
276 target: LOG_TARGET,
277 "Failed to import target block with state: {e:?}."
278 );
279 });
280 self.succeeded |= results.into_iter().any(|result| result.is_ok());
281 self.actions.push(SyncingAction::Finished);
282 }
283 }
284
285 fn state_request(&mut self) -> Option<(PeerId, StateRequest)> {
287 if self.state_sync.is_complete() {
288 return None;
289 }
290
291 if self
292 .peers
293 .values()
294 .any(|peer| matches!(peer.state, PeerState::DownloadingState))
295 {
296 return None;
298 }
299
300 let peer_id =
301 self.schedule_next_peer(PeerState::DownloadingState, self.state_sync.target_number())?;
302 let request = self.state_sync.next_request();
303 trace!(
304 target: LOG_TARGET,
305 "New state request to {peer_id}: {request:?}.",
306 );
307 Some((peer_id, request))
308 }
309
310 fn schedule_next_peer(
311 &mut self,
312 new_state: PeerState,
313 min_best_number: NumberFor<B>,
314 ) -> Option<PeerId> {
315 let mut targets: Vec<_> = self.peers.values().map(|p| p.best_number).collect();
316 if targets.is_empty() {
317 return None;
318 }
319 targets.sort();
320 let median = targets[targets.len() / 2];
321 let threshold = std::cmp::max(median, min_best_number);
322 for (peer_id, peer) in self.peers.iter_mut() {
325 if peer.state.is_available() &&
326 peer.best_number >= threshold &&
327 self.disconnected_peers.is_peer_available(peer_id)
328 {
329 peer.state = new_state;
330 return Some(*peer_id);
331 }
332 }
333 None
334 }
335
336 pub fn status(&self) -> SyncStatus<B> {
338 SyncStatus {
339 state: if self.state_sync.is_complete() {
340 SyncState::Idle
341 } else {
342 SyncState::Downloading { target: self.state_sync.target_number() }
343 },
344 best_seen_block: Some(self.state_sync.target_number()),
345 num_peers: self.peers.len().saturated_into(),
346 queued_blocks: 0,
347 state_sync: Some(self.state_sync.progress()),
348 warp_sync: None,
349 }
350 }
351
352 #[must_use]
354 pub fn actions(
355 &mut self,
356 network_service: &NetworkServiceHandle,
357 ) -> impl Iterator<Item = SyncingAction<B>> {
358 let state_request = self.state_request().into_iter().map(|(peer_id, request)| {
359 let (tx, rx) = oneshot::channel();
360
361 network_service.start_request(
362 peer_id,
363 self.protocol_name.clone(),
364 request.encode_to_vec(),
365 tx,
366 IfDisconnected::ImmediateError,
367 );
368
369 SyncingAction::StartRequest {
370 peer_id,
371 key: Self::STRATEGY_KEY,
372 request: async move {
373 Ok(rx.await?.and_then(|(response, protocol_name)| {
374 Ok((Box::new(response) as Box<dyn Any + Send>, protocol_name))
375 }))
376 }
377 .boxed(),
378 }
379 });
380 self.actions.extend(state_request);
381
382 std::mem::take(&mut self.actions).into_iter()
383 }
384
385 #[must_use]
387 pub fn is_succeeded(&self) -> bool {
388 self.succeeded
389 }
390}
391
392#[cfg(test)]
393mod test {
394 use super::*;
395 use crate::{
396 schema::v1::{StateRequest, StateResponse},
397 service::network::NetworkServiceProvider,
398 strategy::state_sync::{ImportResult, StateSync, StateSyncProgress, StateSyncProvider},
399 };
400 use codec::Decode;
401 use sc_block_builder::BlockBuilderBuilder;
402 use sc_client_api::KeyValueStates;
403 use sc_consensus::{ImportedAux, ImportedState};
404 use sp_core::H256;
405 use sp_runtime::traits::Zero;
406 use substrate_test_runtime_client::{
407 runtime::{Block, Hash},
408 BlockBuilderExt, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
409 };
410
411 mockall::mock! {
412 pub StateSync<B: BlockT> {}
413
414 impl<B: BlockT> StateSyncProvider<B> for StateSync<B> {
415 fn import(&mut self, response: StateResponse) -> ImportResult<B>;
416 fn next_request(&self) -> StateRequest;
417 fn is_complete(&self) -> bool;
418 fn target_number(&self) -> NumberFor<B>;
419 fn target_hash(&self) -> B::Hash;
420 fn progress(&self) -> StateSyncProgress;
421 }
422 }
423
424 #[test]
425 fn no_peer_is_scheduled_if_no_peers_connected() {
426 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
427 let target_block = BlockBuilderBuilder::new(&*client)
428 .on_parent_block(client.chain_info().best_hash)
429 .with_parent_block_number(client.chain_info().best_number)
430 .build()
431 .unwrap()
432 .build()
433 .unwrap()
434 .block;
435 let target_header = target_block.header().clone();
436
437 let mut state_strategy = StateStrategy::new(
438 client,
439 target_header,
440 None,
441 None,
442 false,
443 std::iter::empty(),
444 ProtocolName::Static(""),
445 );
446
447 assert!(state_strategy
448 .schedule_next_peer(PeerState::DownloadingState, Zero::zero())
449 .is_none());
450 }
451
452 #[test]
453 fn at_least_median_synced_peer_is_scheduled() {
454 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
455 let target_block = BlockBuilderBuilder::new(&*client)
456 .on_parent_block(client.chain_info().best_hash)
457 .with_parent_block_number(client.chain_info().best_number)
458 .build()
459 .unwrap()
460 .build()
461 .unwrap()
462 .block;
463
464 for _ in 0..100 {
465 let peers = (1..=10)
466 .map(|best_number| (PeerId::random(), best_number))
467 .collect::<HashMap<_, _>>();
468 let initial_peers = peers.iter().map(|(p, n)| (*p, *n));
469
470 let mut state_strategy = StateStrategy::new(
471 client.clone(),
472 target_block.header().clone(),
473 None,
474 None,
475 false,
476 initial_peers,
477 ProtocolName::Static(""),
478 );
479
480 let peer_id =
481 state_strategy.schedule_next_peer(PeerState::DownloadingState, Zero::zero());
482 assert!(*peers.get(&peer_id.unwrap()).unwrap() >= 6);
483 }
484 }
485
486 #[test]
487 fn min_best_number_peer_is_scheduled() {
488 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
489 let target_block = BlockBuilderBuilder::new(&*client)
490 .on_parent_block(client.chain_info().best_hash)
491 .with_parent_block_number(client.chain_info().best_number)
492 .build()
493 .unwrap()
494 .build()
495 .unwrap()
496 .block;
497
498 for _ in 0..10 {
499 let peers = (1..=10)
500 .map(|best_number| (PeerId::random(), best_number))
501 .collect::<HashMap<_, _>>();
502 let initial_peers = peers.iter().map(|(p, n)| (*p, *n));
503
504 let mut state_strategy = StateStrategy::new(
505 client.clone(),
506 target_block.header().clone(),
507 None,
508 None,
509 false,
510 initial_peers,
511 ProtocolName::Static(""),
512 );
513
514 let peer_id = state_strategy.schedule_next_peer(PeerState::DownloadingState, 10);
515 assert!(*peers.get(&peer_id.unwrap()).unwrap() == 10);
516 }
517 }
518
519 #[test]
520 fn backedoff_number_peer_is_not_scheduled() {
521 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
522 let target_block = BlockBuilderBuilder::new(&*client)
523 .on_parent_block(client.chain_info().best_hash)
524 .with_parent_block_number(client.chain_info().best_number)
525 .build()
526 .unwrap()
527 .build()
528 .unwrap()
529 .block;
530
531 let peers = (1..=10)
532 .map(|best_number| (PeerId::random(), best_number))
533 .collect::<Vec<(_, _)>>();
534 let ninth_peer = peers[8].0;
535 let tenth_peer = peers[9].0;
536 let initial_peers = peers.iter().map(|(p, n)| (*p, *n));
537
538 let mut state_strategy = StateStrategy::new(
539 client.clone(),
540 target_block.header().clone(),
541 None,
542 None,
543 false,
544 initial_peers,
545 ProtocolName::Static(""),
546 );
547
548 state_strategy.remove_peer(&tenth_peer);
550 assert!(state_strategy.disconnected_peers.is_peer_available(&tenth_peer));
551
552 state_strategy.add_peer(tenth_peer, H256::random(), 10);
554 let peer_id: Option<PeerId> =
555 state_strategy.schedule_next_peer(PeerState::DownloadingState, 10);
556 assert_eq!(tenth_peer, peer_id.unwrap());
557 state_strategy.remove_peer(&tenth_peer);
558
559 assert!(!state_strategy.disconnected_peers.is_peer_available(&tenth_peer));
561
562 state_strategy.add_peer(tenth_peer, H256::random(), 10);
564 let peer_id: Option<PeerId> =
565 state_strategy.schedule_next_peer(PeerState::DownloadingState, 10);
566 assert!(peer_id.is_none());
567
568 let peer_id: Option<PeerId> =
570 state_strategy.schedule_next_peer(PeerState::DownloadingState, 9);
571 assert_eq!(ninth_peer, peer_id.unwrap());
572 }
573
574 #[test]
575 fn state_request_contains_correct_hash() {
576 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
577 let target_block = BlockBuilderBuilder::new(&*client)
578 .on_parent_block(client.chain_info().best_hash)
579 .with_parent_block_number(client.chain_info().best_number)
580 .build()
581 .unwrap()
582 .build()
583 .unwrap()
584 .block;
585
586 let initial_peers = (1..=10).map(|best_number| (PeerId::random(), best_number));
587
588 let mut state_strategy = StateStrategy::new(
589 client.clone(),
590 target_block.header().clone(),
591 None,
592 None,
593 false,
594 initial_peers,
595 ProtocolName::Static(""),
596 );
597
598 let (_peer_id, request) = state_strategy.state_request().unwrap();
599 let hash = Hash::decode(&mut &*request.block).unwrap();
600
601 assert_eq!(hash, target_block.header().hash());
602 }
603
604 #[test]
605 fn no_parallel_state_requests() {
606 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
607 let target_block = BlockBuilderBuilder::new(&*client)
608 .on_parent_block(client.chain_info().best_hash)
609 .with_parent_block_number(client.chain_info().best_number)
610 .build()
611 .unwrap()
612 .build()
613 .unwrap()
614 .block;
615
616 let initial_peers = (1..=10).map(|best_number| (PeerId::random(), best_number));
617
618 let mut state_strategy = StateStrategy::new(
619 client.clone(),
620 target_block.header().clone(),
621 None,
622 None,
623 false,
624 initial_peers,
625 ProtocolName::Static(""),
626 );
627
628 assert!(state_strategy.state_request().is_some());
630
631 assert!(state_strategy.state_request().is_none());
633 }
634
635 #[test]
636 fn received_state_response_makes_peer_available_again() {
637 let mut state_sync_provider = MockStateSync::<Block>::new();
638 state_sync_provider.expect_import().return_once(|_| ImportResult::Continue);
639 let peer_id = PeerId::random();
640 let initial_peers = std::iter::once((peer_id, 10));
641 let mut state_strategy = StateStrategy::new_with_provider(
642 Box::new(state_sync_provider),
643 initial_peers,
644 ProtocolName::Static(""),
645 );
646 state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState;
648
649 let dummy_response = StateResponse::default().encode_to_vec();
650 state_strategy.on_state_response(&peer_id, dummy_response);
651
652 assert!(state_strategy.peers.get(&peer_id).unwrap().state.is_available());
653 }
654
655 #[test]
656 fn bad_state_response_drops_peer() {
657 let mut state_sync_provider = MockStateSync::<Block>::new();
658 state_sync_provider.expect_import().return_once(|_| ImportResult::BadResponse);
660 let peer_id = PeerId::random();
661 let initial_peers = std::iter::once((peer_id, 10));
662 let mut state_strategy = StateStrategy::new_with_provider(
663 Box::new(state_sync_provider),
664 initial_peers,
665 ProtocolName::Static(""),
666 );
667 state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState;
669 let dummy_response = StateResponse::default().encode_to_vec();
670 assert!(matches!(
672 state_strategy.on_state_response_inner(&peer_id, &dummy_response),
673 Err(BadPeer(id, _rep)) if id == peer_id,
674 ));
675 }
676
677 #[test]
678 fn empty_unverified_state_response_is_rejected() {
679 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
680 let target_block = BlockBuilderBuilder::new(&*client)
681 .on_parent_block(client.chain_info().best_hash)
682 .with_parent_block_number(client.chain_info().best_number)
683 .build()
684 .unwrap()
685 .build()
686 .unwrap()
687 .block;
688 let mut state_sync =
689 StateSync::new(client, target_block.header().clone(), None, None, true);
690
691 let response = StateResponse { entries: Vec::new(), proof: vec![1] };
692
693 assert!(matches!(state_sync.import(response), ImportResult::BadResponse));
694 }
695
696 #[test]
697 fn empty_unverified_state_response_drops_peer() {
698 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
699 let target_block = BlockBuilderBuilder::new(&*client)
700 .on_parent_block(client.chain_info().best_hash)
701 .with_parent_block_number(client.chain_info().best_number)
702 .build()
703 .unwrap()
704 .build()
705 .unwrap()
706 .block;
707 let peer_id = PeerId::random();
708 let mut state_strategy = StateStrategy::new(
709 client,
710 target_block.header().clone(),
711 None,
712 None,
713 true,
714 std::iter::once((peer_id, 10)),
715 ProtocolName::Static(""),
716 );
717 let response = StateResponse { entries: Vec::new(), proof: vec![1] }.encode_to_vec();
718
719 state_strategy.on_state_response(&peer_id, response);
720
721 assert!(matches!(
722 state_strategy.actions.as_slice(),
723 [SyncingAction::DropPeer(BadPeer(id, _))] if *id == peer_id,
724 ));
725 }
726
727 #[test]
728 fn partial_state_response_doesnt_generate_actions() {
729 let mut state_sync_provider = MockStateSync::<Block>::new();
730 state_sync_provider.expect_import().return_once(|_| ImportResult::Continue);
732 let peer_id = PeerId::random();
733 let initial_peers = std::iter::once((peer_id, 10));
734 let mut state_strategy = StateStrategy::new_with_provider(
735 Box::new(state_sync_provider),
736 initial_peers,
737 ProtocolName::Static(""),
738 );
739 state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState;
741
742 let dummy_response = StateResponse::default().encode_to_vec();
743 state_strategy.on_state_response(&peer_id, dummy_response);
744
745 assert_eq!(state_strategy.actions.len(), 0)
747 }
748
749 #[test]
750 fn complete_state_response_leads_to_block_import() {
751 let client = Arc::new(TestClientBuilder::new().set_no_genesis().build());
753 let mut block_builder = BlockBuilderBuilder::new(&*client)
754 .on_parent_block(client.chain_info().best_hash)
755 .with_parent_block_number(client.chain_info().best_number)
756 .build()
757 .unwrap();
758 block_builder.push_storage_change(vec![1, 2, 3], Some(vec![4, 5, 6])).unwrap();
759 let block = block_builder.build().unwrap().block;
760 let header = block.header().clone();
761 let hash = header.hash();
762 let body = Some(block.extrinsics().iter().cloned().collect::<Vec<_>>());
763 let state = ImportedState { block: hash, state: KeyValueStates(Vec::new()) };
764 let justifications = Some(Justifications::from((*b"FRNK", Vec::new())));
765
766 let mut state_sync_provider = MockStateSync::<Block>::new();
768 let import = ImportResult::Import(
769 hash,
770 header.clone(),
771 state.clone(),
772 body.clone(),
773 justifications.clone(),
774 );
775 state_sync_provider.expect_import().return_once(move |_| import);
776
777 let expected_origin = BlockOrigin::NetworkInitialSync;
779 let expected_block = IncomingBlock {
780 hash,
781 header: Some(header),
782 body,
783 indexed_body: None,
784 justifications,
785 origin: None,
786 allow_missing_state: true,
787 import_existing: true,
788 skip_execution: true,
789 state: Some(state),
790 };
791 let expected_blocks = vec![expected_block];
792
793 let peer_id = PeerId::random();
795 let initial_peers = std::iter::once((peer_id, 10));
796 let mut state_strategy = StateStrategy::new_with_provider(
797 Box::new(state_sync_provider),
798 initial_peers,
799 ProtocolName::Static(""),
800 );
801 state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState;
803
804 let dummy_response = StateResponse::default().encode_to_vec();
806 state_strategy.on_state_response(&peer_id, dummy_response);
807
808 assert_eq!(state_strategy.actions.len(), 1);
809 assert!(matches!(
810 &state_strategy.actions[0],
811 SyncingAction::ImportBlocks { origin, blocks }
812 if *origin == expected_origin && *blocks == expected_blocks,
813 ));
814 }
815
816 #[test]
817 fn importing_unknown_block_doesnt_finish_strategy() {
818 let target_hash = Hash::random();
819 let unknown_hash = Hash::random();
820 let mut state_sync_provider = MockStateSync::<Block>::new();
821 state_sync_provider.expect_target_hash().return_const(target_hash);
822
823 let mut state_strategy = StateStrategy::new_with_provider(
824 Box::new(state_sync_provider),
825 std::iter::empty(),
826 ProtocolName::Static(""),
827 );
828
829 state_strategy.on_blocks_processed(
831 1,
832 1,
833 vec![(
834 Ok(BlockImportStatus::ImportedUnknown(1, ImportedAux::default(), None)),
835 unknown_hash,
836 )],
837 );
838
839 assert_eq!(state_strategy.actions.len(), 0);
841 }
842
843 #[test]
844 fn successfully_importing_target_block_finishes_strategy() {
845 let target_hash = Hash::random();
846 let mut state_sync_provider = MockStateSync::<Block>::new();
847 state_sync_provider.expect_target_hash().return_const(target_hash);
848
849 let mut state_strategy = StateStrategy::new_with_provider(
850 Box::new(state_sync_provider),
851 std::iter::empty(),
852 ProtocolName::Static(""),
853 );
854
855 state_strategy.on_blocks_processed(
857 1,
858 1,
859 vec![(
860 Ok(BlockImportStatus::ImportedUnknown(1, ImportedAux::default(), None)),
861 target_hash,
862 )],
863 );
864
865 assert_eq!(state_strategy.actions.len(), 1);
867 assert!(matches!(&state_strategy.actions[0], SyncingAction::Finished));
868 }
869
870 #[test]
871 fn failure_to_import_target_block_finishes_strategy() {
872 let target_hash = Hash::random();
873 let mut state_sync_provider = MockStateSync::<Block>::new();
874 state_sync_provider.expect_target_hash().return_const(target_hash);
875
876 let mut state_strategy = StateStrategy::new_with_provider(
877 Box::new(state_sync_provider),
878 std::iter::empty(),
879 ProtocolName::Static(""),
880 );
881
882 state_strategy.on_blocks_processed(
884 1,
885 1,
886 vec![(
887 Err(BlockImportError::VerificationFailed(None, String::from("test-error"))),
888 target_hash,
889 )],
890 );
891
892 assert_eq!(state_strategy.actions.len(), 1);
894 assert!(matches!(&state_strategy.actions[0], SyncingAction::Finished));
895 }
896
897 #[test]
898 fn finished_strategy_doesnt_generate_more_actions() {
899 let target_hash = Hash::random();
900 let mut state_sync_provider = MockStateSync::<Block>::new();
901 state_sync_provider.expect_target_hash().return_const(target_hash);
902 state_sync_provider.expect_is_complete().return_const(true);
903
904 let initial_peers = (1..=10).map(|best_number| (PeerId::random(), best_number));
906
907 let mut state_strategy = StateStrategy::new_with_provider(
908 Box::new(state_sync_provider),
909 initial_peers,
910 ProtocolName::Static(""),
911 );
912
913 state_strategy.on_blocks_processed(
914 1,
915 1,
916 vec![(
917 Ok(BlockImportStatus::ImportedUnknown(1, ImportedAux::default(), None)),
918 target_hash,
919 )],
920 );
921
922 let network_provider = NetworkServiceProvider::new();
923 let network_handle = network_provider.handle();
924
925 let actions = state_strategy.actions(&network_handle).collect::<Vec<_>>();
927 assert_eq!(actions.len(), 1);
928 assert!(matches!(&actions[0], SyncingAction::Finished));
929
930 assert_eq!(state_strategy.actions(&network_handle).count(), 0);
932 }
933}