1#![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
111const GRANDPA_JUSTIFICATION_PERIOD: u32 = 512;
114
115const KEEP_FINALIZED_FOR_LIVE_NETWORKS: u32 = 25;
117
118pub trait HeaderProvider<Block, Error = sp_blockchain::Error>: Send + Sync + 'static
122where
123 Block: BlockT,
124 Error: std::fmt::Debug + Send + Sync + 'static,
125{
126 fn header(
128 &self,
129 hash: <Block as BlockT>::Hash,
130 ) -> Result<Option<<Block as BlockT>::Header>, Error>;
131 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
157pub 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#[derive(Debug, Clone, Copy, PartialEq)]
246pub enum Chain {
247 Polkadot,
249 Kusama,
251 Rococo,
253 Westend,
255 Unknown,
257}
258
259pub trait IdentifyVariant {
261 fn is_polkadot(&self) -> bool;
263
264 fn is_kusama(&self) -> bool;
266
267 fn is_westend(&self) -> bool;
269
270 fn is_rococo(&self) -> bool;
272
273 fn is_versi(&self) -> bool;
275
276 fn is_dev(&self) -> bool;
278
279 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#[cfg(feature = "full-node")]
350#[derive(Clone)]
351pub enum IsParachainNode {
352 Collator(CollatorPair),
354 FullNode,
356 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 fn is_collator(&self) -> bool {
377 matches!(self, Self::Collator(_))
378 }
379
380 fn is_full_node(&self) -> bool {
382 matches!(self, Self::FullNode)
383 }
384
385 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 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#[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#[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#[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 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}