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};
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
110const GRANDPA_JUSTIFICATION_PERIOD: u32 = 512;
113
114const KEEP_FINALIZED_FOR_LIVE_NETWORKS: u32 = 25;
116
117pub trait HeaderProvider<Block, Error = sp_blockchain::Error>: Send + Sync + 'static
121where
122 Block: BlockT,
123 Error: std::fmt::Debug + Send + Sync + 'static,
124{
125 fn header(
127 &self,
128 hash: <Block as BlockT>::Hash,
129 ) -> Result<Option<<Block as BlockT>::Header>, Error>;
130 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
156pub 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#[derive(Debug, Clone, Copy, PartialEq)]
245pub enum Chain {
246 Polkadot,
248 Kusama,
250 Rococo,
252 Westend,
254 Unknown,
256}
257
258pub trait IdentifyVariant {
260 fn is_polkadot(&self) -> bool;
262
263 fn is_kusama(&self) -> bool;
265
266 fn is_westend(&self) -> bool;
268
269 fn is_rococo(&self) -> bool;
271
272 fn is_versi(&self) -> bool;
274
275 fn is_dev(&self) -> bool;
277
278 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#[cfg(feature = "full-node")]
349#[derive(Clone)]
350pub enum IsParachainNode {
351 Collator(CollatorPair),
353 FullNode,
355 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 fn is_collator(&self) -> bool {
376 matches!(self, Self::Collator(_))
377 }
378
379 fn is_full_node(&self) -> bool {
381 matches!(self, Self::FullNode)
382 }
383
384 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 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#[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#[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#[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 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}