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 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 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 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 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 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 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 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 #[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 #[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 state_strategy.remove_peer(&tenth_peer);
549 assert!(state_strategy.disconnected_peers.is_peer_available(&tenth_peer));
550
551 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 assert!(!state_strategy.disconnected_peers.is_peer_available(&tenth_peer));
560
561 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 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 assert!(state_strategy.state_request().is_some());
629
630 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 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 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 state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState;
668 let dummy_response = StateResponse::default().encode_to_vec();
669 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 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 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 assert_eq!(state_strategy.actions.len(), 0)
746 }
747
748 #[test]
749 fn complete_state_response_leads_to_block_import() {
750 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 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 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 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 state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState;
801
802 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 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 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 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 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 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 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 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 let actions = state_strategy.actions(&network_handle).collect::<Vec<_>>();
925 assert_eq!(actions.len(), 1);
926 assert!(matches!(&actions[0], SyncingAction::Finished));
927
928 assert_eq!(state_strategy.actions(&network_handle).count(), 0);
930 }
931}