1use 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
80const MAX_IMPORTING_BLOCKS: usize = 2048;
82
83const MAX_DOWNLOAD_AHEAD: u32 = 2048;
85
86const MAX_BLOCKS_TO_LOOK_BACKWARDS: u32 = MAX_DOWNLOAD_AHEAD / 2;
89
90const STATE_SYNC_FINALITY_THRESHOLD: u32 = 8;
92
93const MAJOR_SYNC_BLOCKS: u8 = 5;
99
100mod rep {
101 use sc_network::ReputationChange as Rep;
102 pub const BLOCKCHAIN_READ_ERROR: Rep = Rep::new(-(1 << 16), "DB Error");
105
106 pub const GENESIS_MISMATCH: Rep = Rep::new(i32::MIN, "Genesis mismatch");
109
110 pub const INCOMPLETE_HEADER: Rep = Rep::new(-(1 << 20), "Incomplete header");
112
113 pub const VERIFICATION_FAIL: Rep = Rep::new(-(1 << 29), "Block verification failed");
115
116 pub const BAD_BLOCK: Rep = Rep::new(-(1 << 29), "Bad block");
118
119 pub const NO_BLOCK: Rep = Rep::new(-(1 << 29), "No requested block data");
121
122 pub const NOT_REQUESTED: Rep = Rep::new(-(1 << 29), "Not requested block data");
124
125 pub const NO_GAP_BODIES: Rep = Rep::new(-(1 << 26), "No gap sync bodies");
128
129 pub const BAD_JUSTIFICATION: Rep = Rep::new(-(1 << 16), "Bad justification");
131
132 pub const UNKNOWN_ANCESTOR: Rep = Rep::new(-(1 << 16), "DB Error");
134
135 pub const BAD_RESPONSE: Rep = Rep::new(-(1 << 12), "Incomplete response");
137
138 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#[derive(Debug, Default, Clone)]
240struct GapSyncStats {
241 header_bytes: usize,
243 body_bytes: usize,
245 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#[derive(Copy, Clone, Debug, Eq, PartialEq)]
298pub enum ChainSyncMode {
299 Full,
301 LightState {
303 skip_proofs: bool,
305 storage_chain_mode: bool,
307 },
308}
309
310impl ChainSyncMode {
311 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#[derive(Copy, Clone, Debug, Eq, PartialEq)]
329pub enum GapSyncBodyPolicy {
330 HeadersOnly,
332 All,
334 BodiesWithinWindow(u32),
341}
342
343pub type GapSyncBodyPolicyProvider =
349 Arc<dyn Fn() -> Result<GapSyncBodyPolicy, ClientError> + Send + Sync>;
350
351#[derive(Debug, Clone)]
353pub(crate) struct PeerSync<B: BlockT> {
354 pub peer_id: PeerId,
356 pub common_number: NumberFor<B>,
359 pub best_hash: B::Hash,
361 pub best_number: NumberFor<B>,
363 pub state: PeerSyncState<B>,
366}
367
368impl<B: BlockT> PeerSync<B> {
369 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#[derive(Copy, Clone, Eq, PartialEq, Debug)]
395pub(crate) enum PeerSyncState<B: BlockT> {
396 Available,
398 AncestorSearch {
400 start: NumberFor<B>,
402 current: NumberFor<B>,
404 state: AncestorSearchState<B>,
406 },
407 DownloadingNew(NumberFor<B>),
409 DownloadingStale(B::Hash),
413 DownloadingJustification(B::Hash),
415 DownloadingState,
417 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
427pub struct ChainSync<B: BlockT, Client> {
430 client: Arc<Client>,
432 peers: HashMap<PeerId, PeerSync<B>>,
434 disconnected_peers: DisconnectedPeers,
435 blocks: BlockCollection<B>,
437 best_queued_number: NumberFor<B>,
439 best_queued_hash: B::Hash,
441 mode: ChainSyncMode,
443 extra_justifications: ExtraRequests<B>,
445 queue_blocks: HashSet<B::Hash>,
448 pending_state_sync_attempt: Option<(B::Hash, NumberFor<B>, bool)>,
456 fork_targets: HashMap<B::Hash, ForkTarget<B>>,
458 allowed_requests: AllowedRequests,
460 max_parallel_downloads: u32,
462 max_blocks_per_request: u32,
464 state_request_protocol_name: ProtocolName,
466 downloaded_blocks: usize,
468 state_sync: Option<StateSync<B, Client>>,
470 import_existing: bool,
473 block_downloader: Arc<dyn BlockDownloader<B>>,
475 gap_sync_body_policy: GapSyncBodyPolicy,
477 gap_sync: Option<GapSync<B>>,
479 actions: Vec<SyncingAction<B>>,
481 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 let continues_known_fork =
570 known || known_parent || announce.header.parent_hash() == &peer.best_hash;
571
572 let peer_info = is_best.then(|| {
573 peer.best_number = number;
575 peer.best_hash = hash;
576
577 (hash, number)
578 });
579
580 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 !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 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 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 .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 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 let best_block = self.client.info().best_number;
995 if target > best_block && target - best_block > MAJOR_SYNC_BLOCKS.into() {
996 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 pub const STRATEGY_KEY: StrategyKey = StrategyKey::new("ChainSync");
1106
1107 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 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 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 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 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 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 PeerSyncState::DownloadingState |
1319 PeerSyncState::DownloadingStale(_) |
1320 PeerSyncState::AncestorSearch { .. } => {},
1321 }
1322 self.actions
1323 .push(SyncingAction::CancelRequest { peer_id, key: Self::STRATEGY_KEY });
1324 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 #[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 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 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 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 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 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 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 #[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 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 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 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 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 BlockOrigin::GapSync
1804 } else if !self.status().state.is_major_syncing() {
1805 BlockOrigin::NetworkBroadcast
1807 } else {
1808 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 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 for (n, peer) in self.peers.iter_mut() {
1862 if let PeerSyncState::AncestorSearch { .. } = peer.state {
1863 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 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 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 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 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 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 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 fn is_known(&self, hash: &B::Hash) -> bool {
1999 self.block_status(hash).ok().map_or(false, |s| s != BlockStatus::Unknown)
2000 }
2001
2002 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 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 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 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 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 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 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 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 if !requests.is_empty() {
2231 self.allowed_requests.take();
2232 }
2233
2234 requests
2235 }
2236
2237 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 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 #[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
2367fn legacy_justification_mapping(
2372 justification: Option<EncodedJustification>,
2373) -> Option<Justifications> {
2374 justification.map(|just| (*b"FRNK", just).into())
2375}
2376
2377fn 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#[derive(Copy, Clone, Eq, PartialEq, Debug)]
2392pub(crate) enum AncestorSearchState<B: BlockT> {
2393 ExponentialBackoff(NumberFor<B>),
2396 BinarySearch(NumberFor<B>, NumberFor<B>),
2399}
2400
2401fn 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 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
2457fn 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 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 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
2507fn 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 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
2565fn 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 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>() } else {
2613 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
2637fn 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
2656pub 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}