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