Skip to main content

soil_network/sync/
strategy.rs

1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
6
7//! [`SyncingStrategy`] defines an interface [`crate::engine::SyncingEngine`] uses as a specific
8//! syncing algorithm.
9//!
10//! A few different strategies are provided by Substrate out of the box with custom strategies
11//! possible too.
12
13pub mod chain_sync;
14mod disconnected_peers;
15pub mod polkadot;
16pub mod state;
17pub mod state_sync;
18pub mod warp;
19
20use crate::sync::{
21	pending_responses::ResponseFuture,
22	service::network::NetworkServiceHandle,
23	types::{BadPeer, SyncStatus},
24};
25use soil_client::blockchain::Error as ClientError;
26use soil_client::consensus::BlockOrigin;
27use soil_client::import::{BlockImportError, BlockImportStatus, IncomingBlock};
28use soil_network::common::sync::message::BlockAnnounce;
29use soil_network::types::PeerId;
30use soil_network::ProtocolName;
31use std::any::Any;
32use subsoil::runtime::{
33	traits::{Block as BlockT, NumberFor},
34	Justifications,
35};
36
37/// Syncing strategy for syncing engine to use
38pub trait SyncingStrategy<B: BlockT>: Send
39where
40	B: BlockT,
41{
42	/// Notify syncing state machine that a new sync peer has connected.
43	fn add_peer(&mut self, peer_id: PeerId, best_hash: B::Hash, best_number: NumberFor<B>);
44
45	/// Notify that a sync peer has disconnected.
46	fn remove_peer(&mut self, peer_id: &PeerId);
47
48	/// Submit a validated block announcement.
49	///
50	/// Returns new best hash & best number of the peer if they are updated.
51	#[must_use]
52	fn on_validated_block_announce(
53		&mut self,
54		is_best: bool,
55		peer_id: PeerId,
56		announce: &BlockAnnounce<B::Header>,
57	) -> Option<(B::Hash, NumberFor<B>)>;
58
59	/// Configure an explicit fork sync request in case external code has detected that there is a
60	/// stale fork missing.
61	///
62	/// Note that this function should not be used for recent blocks.
63	/// Sync should be able to download all the recent forks normally.
64	///
65	/// Passing empty `peers` set effectively removes the sync request.
66	fn set_sync_fork_request(&mut self, peers: Vec<PeerId>, hash: &B::Hash, number: NumberFor<B>);
67
68	/// Request extra justification.
69	fn request_justification(&mut self, hash: &B::Hash, number: NumberFor<B>);
70
71	/// Clear extra justification requests.
72	fn clear_justification_requests(&mut self);
73
74	/// Report a justification import (successful or not).
75	fn on_justification_import(&mut self, hash: B::Hash, number: NumberFor<B>, success: bool);
76
77	/// Process generic response.
78	///
79	/// Strategy has to create opaque response and should be to downcast it back into concrete type
80	/// internally. Failure to downcast is an implementation bug.
81	fn on_generic_response(
82		&mut self,
83		peer_id: &PeerId,
84		key: StrategyKey,
85		protocol_name: ProtocolName,
86		response: Box<dyn Any + Send>,
87	);
88
89	/// A batch of blocks that have been processed, with or without errors.
90	///
91	/// Call this when a batch of blocks that have been processed by the import queue, with or
92	/// without errors.
93	fn on_blocks_processed(
94		&mut self,
95		imported: usize,
96		count: usize,
97		results: Vec<(Result<BlockImportStatus<NumberFor<B>>, BlockImportError>, B::Hash)>,
98	);
99
100	/// Notify a syncing strategy that a block has been finalized.
101	fn on_block_finalized(&mut self, hash: &B::Hash, number: NumberFor<B>);
102
103	/// Inform sync about a new best imported block.
104	fn update_chain_info(&mut self, best_hash: &B::Hash, best_number: NumberFor<B>);
105
106	// Are we in major sync mode?
107	fn is_major_syncing(&self) -> bool;
108
109	/// Get the number of peers known to the syncing strategy.
110	fn num_peers(&self) -> usize;
111
112	/// Returns the current sync status.
113	fn status(&self) -> SyncStatus<B>;
114
115	/// Get the total number of downloaded blocks.
116	fn num_downloaded_blocks(&self) -> usize;
117
118	/// Get an estimate of the number of parallel sync requests.
119	fn num_sync_requests(&self) -> usize;
120
121	/// Get actions that should be performed by the owner on the strategy's behalf
122	#[must_use]
123	fn actions(
124		&mut self,
125		// TODO: Consider making this internal property of the strategy
126		network_service: &NetworkServiceHandle,
127	) -> Result<Vec<SyncingAction<B>>, ClientError>;
128}
129
130/// The key identifying a specific strategy for responses routing.
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
132pub struct StrategyKey(&'static str);
133
134impl StrategyKey {
135	/// Instantiate opaque strategy key.
136	pub const fn new(key: &'static str) -> Self {
137		Self(key)
138	}
139}
140
141pub enum SyncingAction<B: BlockT> {
142	/// Start request to peer.
143	StartRequest {
144		peer_id: PeerId,
145		key: StrategyKey,
146		request: ResponseFuture,
147		// Whether to remove obsolete pending responses.
148		remove_obsolete: bool,
149	},
150	/// Drop stale request.
151	CancelRequest { peer_id: PeerId, key: StrategyKey },
152	/// Peer misbehaved. Disconnect, report it and cancel any requests to it.
153	DropPeer(BadPeer),
154	/// Import blocks.
155	ImportBlocks { origin: BlockOrigin, blocks: Vec<IncomingBlock<B>> },
156	/// Import justifications.
157	ImportJustifications {
158		peer_id: PeerId,
159		hash: B::Hash,
160		number: NumberFor<B>,
161		justifications: Justifications,
162	},
163	/// Strategy finished. Nothing to do, this is handled by `PolkadotSyncingStrategy`.
164	Finished,
165}
166
167// Note: Ideally we can deduce this information with #[derive(derive_more::Debug)].
168// However, we'd need a bump to the latest version 2 of the crate.
169impl<B> std::fmt::Debug for SyncingAction<B>
170where
171	B: BlockT,
172{
173	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174		match &self {
175			Self::StartRequest { peer_id, key, remove_obsolete, .. } => {
176				write!(
177					f,
178					"StartRequest {{ peer_id: {:?}, key: {:?}, remove_obsolete: {:?} }}",
179					peer_id, key, remove_obsolete
180				)
181			},
182			Self::CancelRequest { peer_id, key } => {
183				write!(f, "CancelRequest {{ peer_id: {:?}, key: {:?} }}", peer_id, key)
184			},
185			Self::DropPeer(peer) => write!(f, "DropPeer({:?})", peer),
186			Self::ImportBlocks { blocks, .. } => write!(f, "ImportBlocks({:?})", blocks),
187			Self::ImportJustifications { hash, number, .. } => {
188				write!(f, "ImportJustifications({:?}, {:?})", hash, number)
189			},
190			Self::Finished => write!(f, "Finished"),
191		}
192	}
193}
194
195impl<B: BlockT> SyncingAction<B> {
196	/// Returns `true` if the syncing action has completed.
197	pub fn is_finished(&self) -> bool {
198		matches!(self, SyncingAction::Finished)
199	}
200
201	#[cfg(test)]
202	pub(crate) fn name(&self) -> &'static str {
203		match self {
204			Self::StartRequest { .. } => "StartRequest",
205			Self::CancelRequest { .. } => "CancelRequest",
206			Self::DropPeer(_) => "DropPeer",
207			Self::ImportBlocks { .. } => "ImportBlocks",
208			Self::ImportJustifications { .. } => "ImportJustifications",
209			Self::Finished => "Finished",
210		}
211	}
212}