referrerpolicy=no-referrer-when-downgrade

polkadot_service/
lib.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Polkadot service. Specialized wrapper over substrate service.
18
19#![deny(unused_results)]
20
21pub mod benchmarking;
22pub mod chain_spec;
23mod fake_runtime_api;
24mod grandpa_support;
25mod parachains_db;
26mod relay_chain_selection;
27
28#[cfg(feature = "full-node")]
29pub mod builder;
30#[cfg(feature = "full-node")]
31pub mod overseer;
32#[cfg(feature = "full-node")]
33pub mod workers;
34
35#[cfg(feature = "full-node")]
36pub use crate::builder::{new_full, NewFull, NewFullParams};
37
38#[cfg(feature = "full-node")]
39pub use self::overseer::{
40	CollatorOverseerGen, ExtendedOverseerGenArgs, OverseerGen, OverseerGenArgs,
41	ValidatorOverseerGen,
42};
43
44#[cfg(test)]
45mod tests;
46
47#[cfg(feature = "full-node")]
48use crate::builder::{new_partial, new_partial_basics};
49
50#[cfg(feature = "full-node")]
51use {
52	polkadot_node_core_approval_voting as approval_voting_subsystem,
53	polkadot_node_core_av_store::Error as AvailabilityError,
54	polkadot_node_core_chain_selection as chain_selection_subsystem,
55};
56
57use polkadot_node_subsystem_util::database::Database;
58use polkadot_overseer::SpawnGlue;
59
60#[cfg(feature = "full-node")]
61pub use {
62	polkadot_overseer::{Handle, Overseer, OverseerConnector, OverseerHandle},
63	polkadot_primitives::runtime_api::ParachainHost,
64	relay_chain_selection::SelectRelayChain,
65	sc_client_api::AuxStore,
66	sp_authority_discovery::AuthorityDiscoveryApi,
67	sp_blockchain::{HeaderBackend, HeaderMetadata},
68	sp_consensus_babe::BabeApi,
69};
70
71use std::{path::PathBuf, sync::Arc};
72
73use prometheus_endpoint::Registry;
74use sc_service::SpawnTaskHandle;
75
76pub use chain_spec::{GenericChainSpec, RococoChainSpec, WestendChainSpec};
77pub use polkadot_primitives::{Block, BlockId, BlockNumber, CollatorPair, Hash, Id as ParaId};
78pub use sc_client_api::{Backend, CallExecutor};
79pub use sc_consensus::{BlockImport, LongestChain};
80pub use sc_executor::NativeExecutionDispatch;
81use sc_executor::WasmExecutor;
82pub use sc_service::{
83	config::{DatabaseSource, PrometheusConfig},
84	ChainSpec, Configuration, Error as SubstrateServiceError, PruningMode, Role, TFullBackend,
85	TFullCallExecutor, TFullClient, TaskManager, TransactionPoolOptions,
86};
87pub use sp_api::{ApiRef, ConstructRuntimeApi, Core as CoreApi, ProvideRuntimeApi};
88pub use sp_consensus::{Proposal, SelectChain};
89pub use sp_runtime::{
90	generic,
91	traits::{self as runtime_traits, BlakeTwo256, Block as BlockT, Header as HeaderT, NumberFor},
92};
93
94#[cfg(feature = "rococo-native")]
95pub use {rococo_runtime, rococo_runtime_constants};
96#[cfg(feature = "westend-native")]
97pub use {westend_runtime, westend_runtime_constants};
98
99pub use fake_runtime_api::{GetLastTimestamp, RuntimeApi};
100
101#[cfg(feature = "full-node")]
102pub type FullBackend = sc_service::TFullBackend<Block>;
103
104#[cfg(feature = "full-node")]
105pub type FullClient = sc_service::TFullClient<
106	Block,
107	RuntimeApi,
108	WasmExecutor<(sp_io::SubstrateHostFunctions, frame_benchmarking::benchmarking::HostFunctions)>,
109>;
110
111/// The minimum period of blocks on which justifications will be
112/// imported and generated.
113const GRANDPA_JUSTIFICATION_PERIOD: u32 = 512;
114
115/// The number of hours to keep finalized data in the availability store for live networks.
116const KEEP_FINALIZED_FOR_LIVE_NETWORKS: u32 = 25;
117
118/// Provides the header and block number for a hash.
119///
120/// Decouples `sc_client_api::Backend` and `sp_blockchain::HeaderBackend`.
121pub trait HeaderProvider<Block, Error = sp_blockchain::Error>: Send + Sync + 'static
122where
123	Block: BlockT,
124	Error: std::fmt::Debug + Send + Sync + 'static,
125{
126	/// Obtain the header for a hash.
127	fn header(
128		&self,
129		hash: <Block as BlockT>::Hash,
130	) -> Result<Option<<Block as BlockT>::Header>, Error>;
131	/// Obtain the block number for a hash.
132	fn number(
133		&self,
134		hash: <Block as BlockT>::Hash,
135	) -> Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>, Error>;
136}
137
138impl<Block, T> HeaderProvider<Block> for T
139where
140	Block: BlockT,
141	T: sp_blockchain::HeaderBackend<Block> + 'static,
142{
143	fn header(
144		&self,
145		hash: Block::Hash,
146	) -> sp_blockchain::Result<Option<<Block as BlockT>::Header>> {
147		<Self as sp_blockchain::HeaderBackend<Block>>::header(self, hash)
148	}
149	fn number(
150		&self,
151		hash: Block::Hash,
152	) -> sp_blockchain::Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>> {
153		<Self as sp_blockchain::HeaderBackend<Block>>::number(self, hash)
154	}
155}
156
157/// Decoupling the provider.
158///
159/// Mandated since `trait HeaderProvider` can only be
160/// implemented once for a generic `T`.
161pub trait HeaderProviderProvider<Block>: Send + Sync + 'static
162where
163	Block: BlockT,
164{
165	type Provider: HeaderProvider<Block> + 'static;
166
167	fn header_provider(&self) -> &Self::Provider;
168}
169
170impl<Block, T> HeaderProviderProvider<Block> for T
171where
172	Block: BlockT,
173	T: sc_client_api::Backend<Block> + 'static,
174{
175	type Provider = <T as sc_client_api::Backend<Block>>::Blockchain;
176
177	fn header_provider(&self) -> &Self::Provider {
178		self.blockchain()
179	}
180}
181
182#[derive(thiserror::Error, Debug)]
183pub enum Error {
184	#[error(transparent)]
185	Io(#[from] std::io::Error),
186
187	#[error(transparent)]
188	AddrFormatInvalid(#[from] std::net::AddrParseError),
189
190	#[error(transparent)]
191	Sub(#[from] SubstrateServiceError),
192
193	#[error(transparent)]
194	Blockchain(#[from] sp_blockchain::Error),
195
196	#[error(transparent)]
197	Consensus(#[from] sp_consensus::Error),
198
199	#[error("Failed to create an overseer")]
200	Overseer(#[from] polkadot_overseer::SubsystemError),
201
202	#[error(transparent)]
203	Prometheus(#[from] prometheus_endpoint::PrometheusError),
204
205	#[error(transparent)]
206	Telemetry(#[from] sc_telemetry::Error),
207
208	#[cfg(feature = "full-node")]
209	#[error(transparent)]
210	Availability(#[from] AvailabilityError),
211
212	#[error("Authorities require the real overseer implementation")]
213	AuthoritiesRequireRealOverseer,
214
215	#[cfg(feature = "full-node")]
216	#[error("Creating a custom database is required for validators")]
217	DatabasePathRequired,
218
219	#[cfg(feature = "full-node")]
220	#[error("Expected at least one of polkadot, kusama, westend or rococo runtime feature")]
221	NoRuntime,
222
223	#[cfg(feature = "full-node")]
224	#[error("Worker binaries not executable, prepare binary: {prep_worker_path:?}, execute binary: {exec_worker_path:?}")]
225	InvalidWorkerBinaries { prep_worker_path: PathBuf, exec_worker_path: PathBuf },
226
227	#[cfg(feature = "full-node")]
228	#[error("Worker binaries could not be found, make sure polkadot was built and installed correctly. Please see the readme for the latest instructions (https://github.com/paritytech/polkadot-sdk/tree/master/polkadot). If you ran with `cargo run`, please run `cargo build` first. Searched given workers path ({given_workers_path:?}), polkadot binary path ({current_exe_path:?}), and lib path (/usr/lib/polkadot), workers names: {workers_names:?}")]
229	MissingWorkerBinaries {
230		given_workers_path: Option<PathBuf>,
231		current_exe_path: PathBuf,
232		workers_names: Option<(String, String)>,
233	},
234
235	#[cfg(feature = "full-node")]
236	#[error("Version of worker binary ({worker_version}) is different from node version ({node_version}), worker_path: {worker_path}. If you ran with `cargo run`, please run `cargo build` first, otherwise try to `cargo clean`. TESTING ONLY: this check can be disabled with --disable-worker-version-check")]
237	WorkerBinaryVersionMismatch {
238		worker_version: String,
239		node_version: String,
240		worker_path: PathBuf,
241	},
242}
243
244/// Identifies the variant of the chain.
245#[derive(Debug, Clone, Copy, PartialEq)]
246pub enum Chain {
247	/// Polkadot.
248	Polkadot,
249	/// Kusama.
250	Kusama,
251	/// Rococo or one of its derivations.
252	Rococo,
253	/// Westend.
254	Westend,
255	/// Unknown chain?
256	Unknown,
257}
258
259/// Can be called for a `Configuration` to identify which network the configuration targets.
260pub trait IdentifyVariant {
261	/// Returns if this is a configuration for the `Polkadot` network.
262	fn is_polkadot(&self) -> bool;
263
264	/// Returns if this is a configuration for the `Kusama` network.
265	fn is_kusama(&self) -> bool;
266
267	/// Returns if this is a configuration for the `Westend` network.
268	fn is_westend(&self) -> bool;
269
270	/// Returns if this is a configuration for the `Rococo` network.
271	fn is_rococo(&self) -> bool;
272
273	/// Returns if this is a configuration for the `Versi` test network.
274	fn is_versi(&self) -> bool;
275
276	/// Returns true if this configuration is for a development network.
277	fn is_dev(&self) -> bool;
278
279	/// Identifies the variant of the chain.
280	fn identify_chain(&self) -> Chain;
281}
282
283impl IdentifyVariant for Box<dyn ChainSpec> {
284	fn is_polkadot(&self) -> bool {
285		self.id().starts_with("polkadot") || self.id().starts_with("dot")
286	}
287	fn is_kusama(&self) -> bool {
288		self.id().starts_with("kusama") || self.id().starts_with("ksm")
289	}
290	fn is_westend(&self) -> bool {
291		self.id().starts_with("westend") || self.id().starts_with("wnd")
292	}
293	fn is_rococo(&self) -> bool {
294		self.id().starts_with("rococo") || self.id().starts_with("rco")
295	}
296	fn is_versi(&self) -> bool {
297		self.id().starts_with("versi") || self.id().starts_with("vrs")
298	}
299	fn is_dev(&self) -> bool {
300		self.id().ends_with("dev")
301	}
302	fn identify_chain(&self) -> Chain {
303		if self.is_polkadot() {
304			Chain::Polkadot
305		} else if self.is_kusama() {
306			Chain::Kusama
307		} else if self.is_westend() {
308			Chain::Westend
309		} else if self.is_rococo() || self.is_versi() {
310			Chain::Rococo
311		} else {
312			Chain::Unknown
313		}
314	}
315}
316
317#[cfg(feature = "full-node")]
318pub fn open_database(db_source: &DatabaseSource) -> Result<Arc<dyn Database>, Error> {
319	let parachains_db = match db_source {
320		DatabaseSource::RocksDb { path, .. } => parachains_db::open_creating_rocksdb(
321			path.clone(),
322			parachains_db::CacheSizes::default(),
323		)?,
324		DatabaseSource::ParityDb { path, .. } => parachains_db::open_creating_paritydb(
325			path.parent().ok_or(Error::DatabasePathRequired)?.into(),
326			parachains_db::CacheSizes::default(),
327		)?,
328		DatabaseSource::Auto { paritydb_path, rocksdb_path, .. } => {
329			if paritydb_path.is_dir() && paritydb_path.exists() {
330				parachains_db::open_creating_paritydb(
331					paritydb_path.parent().ok_or(Error::DatabasePathRequired)?.into(),
332					parachains_db::CacheSizes::default(),
333				)?
334			} else {
335				parachains_db::open_creating_rocksdb(
336					rocksdb_path.clone(),
337					parachains_db::CacheSizes::default(),
338				)?
339			}
340		},
341		DatabaseSource::Custom { .. } => {
342			unimplemented!("No polkadot subsystem db for custom source.");
343		},
344	};
345	Ok(parachains_db)
346}
347
348/// Is this node running as in-process node for a parachain node?
349#[cfg(feature = "full-node")]
350#[derive(Clone)]
351pub enum IsParachainNode {
352	/// This node is running as in-process node for a parachain collator.
353	Collator(CollatorPair),
354	/// This node is running as in-process node for a parachain full node.
355	FullNode,
356	/// This node is not running as in-process node for a parachain node, aka a normal relay chain
357	/// node.
358	No,
359}
360
361#[cfg(feature = "full-node")]
362impl std::fmt::Debug for IsParachainNode {
363	fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
364		use sp_core::Pair;
365		match self {
366			IsParachainNode::Collator(pair) => write!(fmt, "Collator({})", pair.public()),
367			IsParachainNode::FullNode => write!(fmt, "FullNode"),
368			IsParachainNode::No => write!(fmt, "No"),
369		}
370	}
371}
372
373#[cfg(feature = "full-node")]
374impl IsParachainNode {
375	/// Is this running alongside a collator?
376	fn is_collator(&self) -> bool {
377		matches!(self, Self::Collator(_))
378	}
379
380	/// Is this running alongside a full node?
381	fn is_full_node(&self) -> bool {
382		matches!(self, Self::FullNode)
383	}
384
385	/// Is this node running alongside a relay chain node?
386	fn is_running_alongside_parachain_node(&self) -> bool {
387		self.is_collator() || self.is_full_node()
388	}
389}
390
391#[cfg(feature = "full-node")]
392macro_rules! chain_ops {
393	($config:expr, $telemetry_worker_handle:expr) => {{
394		let telemetry_worker_handle = $telemetry_worker_handle;
395		let mut config = $config;
396		let basics = new_partial_basics(config, telemetry_worker_handle)?;
397
398		use ::sc_consensus::LongestChain;
399		// use the longest chain selection, since there is no overseer available
400		let chain_selection = LongestChain::new(basics.backend.clone());
401
402		let sc_service::PartialComponents { client, backend, import_queue, task_manager, .. } =
403			new_partial::<LongestChain<_, Block>>(&mut config, basics, chain_selection)?;
404		Ok((client, backend, import_queue, task_manager))
405	}};
406}
407
408/// Builds a new object suitable for chain operations.
409#[cfg(feature = "full-node")]
410pub fn new_chain_ops(
411	config: &mut Configuration,
412) -> Result<(Arc<FullClient>, Arc<FullBackend>, sc_consensus::BasicQueue<Block>, TaskManager), Error>
413{
414	config.keystore = sc_service::config::KeystoreConfig::InMemory;
415
416	if config.chain_spec.is_rococo() || config.chain_spec.is_versi() {
417		chain_ops!(config, None)
418	} else if config.chain_spec.is_kusama() {
419		chain_ops!(config, None)
420	} else if config.chain_spec.is_westend() {
421		return chain_ops!(config, None);
422	} else {
423		chain_ops!(config, None)
424	}
425}
426
427/// Build a full node.
428///
429/// The actual "flavor", aka if it will use `Polkadot`, `Rococo` or `Kusama` is determined based on
430/// [`IdentifyVariant`] using the chain spec.
431#[cfg(feature = "full-node")]
432pub fn build_full<OverseerGenerator: OverseerGen>(
433	config: Configuration,
434	mut params: NewFullParams<OverseerGenerator>,
435) -> Result<NewFull, Error> {
436	let is_polkadot = config.chain_spec.is_polkadot();
437
438	params.overseer_message_channel_capacity_override =
439		params.overseer_message_channel_capacity_override.map(move |capacity| {
440			if is_polkadot {
441				gum::warn!("Channel capacity should _never_ be tampered with on polkadot!");
442			}
443			capacity
444		});
445
446	match config.network.network_backend {
447		sc_network::config::NetworkBackendType::Libp2p =>
448			new_full::<_, sc_network::NetworkWorker<Block, Hash>>(config, params),
449		sc_network::config::NetworkBackendType::Litep2p =>
450			new_full::<_, sc_network::Litep2pNetworkBackend>(config, params),
451	}
452}
453
454/// Reverts the node state down to at most the last finalized block.
455///
456/// In particular this reverts:
457/// - `ApprovalVotingSubsystem` data in the parachains-db;
458/// - `ChainSelectionSubsystem` data in the parachains-db;
459/// - Low level Babe and Grandpa consensus data.
460#[cfg(feature = "full-node")]
461pub fn revert_backend(
462	client: Arc<FullClient>,
463	backend: Arc<FullBackend>,
464	blocks: BlockNumber,
465	config: Configuration,
466	task_handle: SpawnTaskHandle,
467) -> Result<(), Error> {
468	let best_number = client.info().best_number;
469	let finalized = client.info().finalized_number;
470	let revertible = blocks.min(best_number - finalized);
471
472	if revertible == 0 {
473		return Ok(());
474	}
475
476	let number = best_number - revertible;
477	let hash = client.block_hash_from_id(&BlockId::Number(number))?.ok_or(
478		sp_blockchain::Error::Backend(format!(
479			"Unexpected hash lookup failure for block number: {}",
480			number
481		)),
482	)?;
483
484	let parachains_db = open_database(&config.database)
485		.map_err(|err| sp_blockchain::Error::Backend(err.to_string()))?;
486
487	revert_approval_voting(parachains_db.clone(), hash, task_handle)?;
488	revert_chain_selection(parachains_db, hash)?;
489	// Revert Substrate consensus related components
490	sc_consensus_babe::revert(client.clone(), backend, blocks)?;
491	sc_consensus_grandpa::revert(client, blocks)?;
492
493	Ok(())
494}
495
496fn revert_chain_selection(db: Arc<dyn Database>, hash: Hash) -> sp_blockchain::Result<()> {
497	let config = chain_selection_subsystem::Config {
498		col_data: parachains_db::REAL_COLUMNS.col_chain_selection_data,
499		stagnant_check_interval: chain_selection_subsystem::StagnantCheckInterval::never(),
500		stagnant_check_mode: chain_selection_subsystem::StagnantCheckMode::PruneOnly,
501	};
502
503	let chain_selection = chain_selection_subsystem::ChainSelectionSubsystem::new(config, db);
504
505	chain_selection
506		.revert_to(hash)
507		.map_err(|err| sp_blockchain::Error::Backend(err.to_string()))
508}
509
510fn revert_approval_voting(
511	db: Arc<dyn Database>,
512	hash: Hash,
513	task_handle: SpawnTaskHandle,
514) -> sp_blockchain::Result<()> {
515	let config = approval_voting_subsystem::Config {
516		col_approval_data: parachains_db::REAL_COLUMNS.col_approval_data,
517		slot_duration_millis: Default::default(),
518	};
519
520	let approval_voting = approval_voting_subsystem::ApprovalVotingSubsystem::with_config(
521		config,
522		db,
523		Arc::new(sc_keystore::LocalKeystore::in_memory()),
524		Box::new(sp_consensus::NoNetwork),
525		approval_voting_subsystem::Metrics::default(),
526		Arc::new(SpawnGlue(task_handle)),
527	);
528
529	approval_voting
530		.revert_to(hash)
531		.map_err(|err| sp_blockchain::Error::Backend(err.to_string()))
532}