Files
pezkuwi-sdk/pezkuwi/node/service/src/lib.rs
T
pezkuwichain 5a1fd2ea22 fix: update GitHub URLs from master to main branch
- Replace tree/master with tree/main in all documentation and code comments
- Fix issues/168 -> pull/168 reference (issue was converted to PR)
2026-01-27 20:30:20 +03:00

541 lines
17 KiB
Rust

// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
// This file is part of Pezkuwi.
// Pezkuwi is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Pezkuwi is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
//! Pezkuwi service. Specialized wrapper over bizinikiwi service.
#![deny(unused_results)]
pub mod benchmarking;
pub mod chain_spec;
mod fake_runtime_api;
mod grandpa_support;
mod relay_chain_selection;
mod teyrchains_db;
#[cfg(feature = "full-node")]
pub mod builder;
#[cfg(feature = "full-node")]
pub mod overseer;
#[cfg(feature = "full-node")]
pub mod workers;
#[cfg(feature = "full-node")]
pub use crate::builder::{new_full, NewFull, NewFullParams};
#[cfg(feature = "full-node")]
pub use self::overseer::{
CollatorOverseerGen, ExtendedOverseerGenArgs, OverseerGen, OverseerGenArgs,
ValidatorOverseerGen,
};
#[cfg(test)]
mod tests;
#[cfg(feature = "full-node")]
use crate::builder::{new_partial, new_partial_basics};
#[cfg(feature = "full-node")]
use {
pezkuwi_node_core_approval_voting as approval_voting_subsystem,
pezkuwi_node_core_av_store::Error as AvailabilityError,
pezkuwi_node_core_chain_selection as chain_selection_subsystem,
};
use pezkuwi_node_subsystem_util::database::Database;
use pezkuwi_overseer::SpawnGlue;
#[cfg(feature = "full-node")]
pub use {
pezkuwi_overseer::{Handle, Overseer, OverseerConnector, OverseerHandle},
pezkuwi_primitives::runtime_api::TeyrchainHost,
pezsc_client_api::AuxStore,
pezsp_authority_discovery::AuthorityDiscoveryApi,
pezsp_blockchain::{HeaderBackend, HeaderMetadata},
pezsp_consensus_babe::BabeApi,
relay_chain_selection::SelectRelayChain,
};
use std::{path::PathBuf, sync::Arc};
use pezsc_service::SpawnTaskHandle;
use prometheus_endpoint::Registry;
pub use chain_spec::{GenericChainSpec, PezkuwichainChainSpec, ZagrosChainSpec};
pub use pezkuwi_primitives::{Block, BlockId, BlockNumber, CollatorPair, Hash, Id as ParaId};
pub use pezsc_client_api::{Backend, CallExecutor};
pub use pezsc_consensus::{BlockImport, LongestChain};
pub use pezsc_executor::NativeExecutionDispatch;
use pezsc_executor::WasmExecutor;
pub use pezsc_service::{
config::{DatabaseSource, PrometheusConfig},
ChainSpec, Configuration, Error as BizinikiwiServiceError, PruningMode, Role, TFullBackend,
TFullCallExecutor, TFullClient, TaskManager, TransactionPoolOptions,
};
pub use pezsp_api::{ApiRef, ConstructRuntimeApi, Core as CoreApi, ProvideRuntimeApi};
pub use pezsp_consensus::{Proposal, SelectChain};
pub use pezsp_runtime::{
generic,
traits::{self as runtime_traits, BlakeTwo256, Block as BlockT, Header as HeaderT, NumberFor},
};
#[cfg(feature = "pezkuwichain-native")]
pub use {pezkuwichain_runtime, pezkuwichain_runtime_constants};
#[cfg(feature = "zagros-native")]
pub use {zagros_runtime, zagros_runtime_constants};
pub use fake_runtime_api::{GetLastTimestamp, RuntimeApi};
#[cfg(feature = "full-node")]
pub type FullBackend = pezsc_service::TFullBackend<Block>;
#[cfg(feature = "full-node")]
pub type FullClient = pezsc_service::TFullClient<
Block,
RuntimeApi,
WasmExecutor<(
pezsp_io::BizinikiwiHostFunctions,
pezframe_benchmarking::benchmarking::HostFunctions,
)>,
>;
/// The minimum period of blocks on which justifications will be
/// imported and generated.
const GRANDPA_JUSTIFICATION_PERIOD: u32 = 512;
/// The number of hours to keep finalized data in the availability store for live networks.
const KEEP_FINALIZED_FOR_LIVE_NETWORKS: u32 = 25;
/// Provides the header and block number for a hash.
///
/// Decouples `pezsc_client_api::Backend` and `pezsp_blockchain::HeaderBackend`.
pub trait HeaderProvider<Block, Error = pezsp_blockchain::Error>: Send + Sync + 'static
where
Block: BlockT,
Error: std::fmt::Debug + Send + Sync + 'static,
{
/// Obtain the header for a hash.
fn header(
&self,
hash: <Block as BlockT>::Hash,
) -> Result<Option<<Block as BlockT>::Header>, Error>;
/// Obtain the block number for a hash.
fn number(
&self,
hash: <Block as BlockT>::Hash,
) -> Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>, Error>;
}
impl<Block, T> HeaderProvider<Block> for T
where
Block: BlockT,
T: pezsp_blockchain::HeaderBackend<Block> + 'static,
{
fn header(
&self,
hash: Block::Hash,
) -> pezsp_blockchain::Result<Option<<Block as BlockT>::Header>> {
<Self as pezsp_blockchain::HeaderBackend<Block>>::header(self, hash)
}
fn number(
&self,
hash: Block::Hash,
) -> pezsp_blockchain::Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>> {
<Self as pezsp_blockchain::HeaderBackend<Block>>::number(self, hash)
}
}
/// Decoupling the provider.
///
/// Mandated since `trait HeaderProvider` can only be
/// implemented once for a generic `T`.
pub trait HeaderProviderProvider<Block>: Send + Sync + 'static
where
Block: BlockT,
{
type Provider: HeaderProvider<Block> + 'static;
fn header_provider(&self) -> &Self::Provider;
}
impl<Block, T> HeaderProviderProvider<Block> for T
where
Block: BlockT,
T: pezsc_client_api::Backend<Block> + 'static,
{
type Provider = <T as pezsc_client_api::Backend<Block>>::Blockchain;
fn header_provider(&self) -> &Self::Provider {
self.blockchain()
}
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
AddrFormatInvalid(#[from] std::net::AddrParseError),
#[error(transparent)]
Sub(#[from] BizinikiwiServiceError),
#[error(transparent)]
Blockchain(#[from] pezsp_blockchain::Error),
#[error(transparent)]
Consensus(#[from] pezsp_consensus::Error),
#[error("Failed to create an overseer")]
Overseer(#[from] pezkuwi_overseer::SubsystemError),
#[error(transparent)]
Prometheus(#[from] prometheus_endpoint::PrometheusError),
#[error(transparent)]
Telemetry(#[from] pezsc_telemetry::Error),
#[cfg(feature = "full-node")]
#[error(transparent)]
Availability(#[from] AvailabilityError),
#[error("Authorities require the real overseer implementation")]
AuthoritiesRequireRealOverseer,
#[cfg(feature = "full-node")]
#[error("Creating a custom database is required for validators")]
DatabasePathRequired,
#[cfg(feature = "full-node")]
#[error("Expected at least one of pezkuwi, dicle, zagros or pezkuwichain runtime feature")]
NoRuntime,
#[cfg(feature = "full-node")]
#[error("Worker binaries not executable, prepare binary: {prep_worker_path:?}, execute binary: {exec_worker_path:?}")]
InvalidWorkerBinaries { prep_worker_path: PathBuf, exec_worker_path: PathBuf },
#[cfg(feature = "full-node")]
#[error("Worker binaries could not be found, make sure pezkuwi was built and installed correctly. Please see the readme for the latest instructions (https://github.com/pezkuwichain/pezkuwi-sdk/tree/main/pezkuwi). If you ran with `cargo run`, please run `cargo build` first. Searched given workers path ({given_workers_path:?}), pezkuwi binary path ({current_exe_path:?}), and lib path (/usr/lib/pezkuwi), workers names: {workers_names:?}")]
MissingWorkerBinaries {
given_workers_path: Option<PathBuf>,
current_exe_path: PathBuf,
workers_names: Option<(String, String)>,
},
#[cfg(feature = "full-node")]
#[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")]
WorkerBinaryVersionMismatch {
worker_version: String,
node_version: String,
worker_path: PathBuf,
},
}
/// Identifies the variant of the chain.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Chain {
/// Pezkuwi.
Pezkuwi,
/// Dicle.
Dicle,
/// Pezkuwichain or one of its derivations.
Pezkuwichain,
/// Zagros.
Zagros,
/// Unknown chain?
Unknown,
}
/// Can be called for a `Configuration` to identify which network the configuration targets.
pub trait IdentifyVariant {
/// Returns if this is a configuration for the `Pezkuwi` network.
fn is_pezkuwi(&self) -> bool;
/// Returns if this is a configuration for the `Dicle` network.
fn is_dicle(&self) -> bool;
/// Returns if this is a configuration for the `Zagros` network.
fn is_zagros(&self) -> bool;
/// Returns if this is a configuration for the `Pezkuwichain` network.
fn is_pezkuwichain(&self) -> bool;
/// Returns if this is a configuration for the `Versi` test network.
fn is_versi(&self) -> bool;
/// Returns true if this configuration is for a development network.
fn is_dev(&self) -> bool;
/// Identifies the variant of the chain.
fn identify_chain(&self) -> Chain;
}
impl IdentifyVariant for Box<dyn ChainSpec> {
fn is_pezkuwi(&self) -> bool {
self.id().starts_with("pezkuwi") || self.id().starts_with("hez")
}
fn is_dicle(&self) -> bool {
self.id().starts_with("dicle") || self.id().starts_with("dcl")
}
fn is_zagros(&self) -> bool {
self.id().starts_with("zagros") || self.id().starts_with("wnd")
}
fn is_pezkuwichain(&self) -> bool {
self.id().starts_with("pezkuwichain") || self.id().starts_with("rco")
}
fn is_versi(&self) -> bool {
self.id().starts_with("versi") || self.id().starts_with("vrs")
}
fn is_dev(&self) -> bool {
self.id().ends_with("dev")
}
fn identify_chain(&self) -> Chain {
if self.is_pezkuwi() {
Chain::Pezkuwi
} else if self.is_dicle() {
Chain::Dicle
} else if self.is_zagros() {
Chain::Zagros
} else if self.is_pezkuwichain() || self.is_versi() {
Chain::Pezkuwichain
} else {
Chain::Unknown
}
}
}
#[cfg(feature = "full-node")]
pub fn open_database(db_source: &DatabaseSource) -> Result<Arc<dyn Database>, Error> {
let teyrchains_db = match db_source {
DatabaseSource::RocksDb { path, .. } => teyrchains_db::open_creating_rocksdb(
path.clone(),
teyrchains_db::CacheSizes::default(),
)?,
DatabaseSource::ParityDb { path, .. } => teyrchains_db::open_creating_paritydb(
path.parent().ok_or(Error::DatabasePathRequired)?.into(),
teyrchains_db::CacheSizes::default(),
)?,
DatabaseSource::Auto { paritydb_path, rocksdb_path, .. } => {
if paritydb_path.is_dir() && paritydb_path.exists() {
teyrchains_db::open_creating_paritydb(
paritydb_path.parent().ok_or(Error::DatabasePathRequired)?.into(),
teyrchains_db::CacheSizes::default(),
)?
} else {
teyrchains_db::open_creating_rocksdb(
rocksdb_path.clone(),
teyrchains_db::CacheSizes::default(),
)?
}
},
DatabaseSource::Custom { .. } => {
unimplemented!("No pezkuwi subsystem db for custom source.");
},
};
Ok(teyrchains_db)
}
/// Is this node running as in-process node for a teyrchain node?
#[cfg(feature = "full-node")]
#[derive(Clone)]
pub enum IsTeyrchainNode {
/// This node is running as in-process node for a teyrchain collator.
Collator(CollatorPair),
/// This node is running as in-process node for a teyrchain full node.
FullNode,
/// This node is not running as in-process node for a teyrchain node, aka a normal relay chain
/// node.
No,
}
#[cfg(feature = "full-node")]
impl std::fmt::Debug for IsTeyrchainNode {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
use pezsp_core::Pair;
match self {
IsTeyrchainNode::Collator(pair) => write!(fmt, "Collator({})", pair.public()),
IsTeyrchainNode::FullNode => write!(fmt, "FullNode"),
IsTeyrchainNode::No => write!(fmt, "No"),
}
}
}
#[cfg(feature = "full-node")]
impl IsTeyrchainNode {
/// Is this running alongside a collator?
fn is_collator(&self) -> bool {
matches!(self, Self::Collator(_))
}
/// Is this running alongside a full node?
fn is_full_node(&self) -> bool {
matches!(self, Self::FullNode)
}
/// Is this node running alongside a relay chain node?
fn is_running_alongside_teyrchain_node(&self) -> bool {
self.is_collator() || self.is_full_node()
}
}
#[cfg(feature = "full-node")]
macro_rules! chain_ops {
($config:expr, $telemetry_worker_handle:expr) => {{
let telemetry_worker_handle = $telemetry_worker_handle;
let mut config = $config;
let basics = new_partial_basics(config, telemetry_worker_handle)?;
use ::pezsc_consensus::LongestChain;
// use the longest chain selection, since there is no overseer available
let chain_selection = LongestChain::new(basics.backend.clone());
let pezsc_service::PartialComponents {
client, backend, import_queue, task_manager, ..
} = new_partial::<LongestChain<_, Block>>(&mut config, basics, chain_selection)?;
Ok((client, backend, import_queue, task_manager))
}};
}
/// Builds a new object suitable for chain operations.
#[cfg(feature = "full-node")]
pub fn new_chain_ops(
config: &mut Configuration,
) -> Result<
(Arc<FullClient>, Arc<FullBackend>, pezsc_consensus::BasicQueue<Block>, TaskManager),
Error,
> {
config.keystore = pezsc_service::config::KeystoreConfig::InMemory;
if config.chain_spec.is_pezkuwichain() || config.chain_spec.is_versi() {
chain_ops!(config, None)
} else if config.chain_spec.is_dicle() {
chain_ops!(config, None)
} else if config.chain_spec.is_zagros() {
return chain_ops!(config, None);
} else {
chain_ops!(config, None)
}
}
/// Build a full node.
///
/// The actual "flavor", aka if it will use `Pezkuwi`, `Pezkuwichain` or `Dicle` is determined
/// based on [`IdentifyVariant`] using the chain spec.
#[cfg(feature = "full-node")]
pub fn build_full<OverseerGenerator: OverseerGen>(
config: Configuration,
mut params: NewFullParams<OverseerGenerator>,
) -> Result<NewFull, Error> {
let is_pezkuwi = config.chain_spec.is_pezkuwi();
params.overseer_message_channel_capacity_override =
params.overseer_message_channel_capacity_override.map(move |capacity| {
if is_pezkuwi {
gum::warn!("Channel capacity should _never_ be tampered with on pezkuwi!");
}
capacity
});
match config.network.network_backend {
pezsc_network::config::NetworkBackendType::Libp2p => {
new_full::<_, pezsc_network::NetworkWorker<Block, Hash>>(config, params)
},
pezsc_network::config::NetworkBackendType::Litep2p => {
new_full::<_, pezsc_network::Litep2pNetworkBackend>(config, params)
},
}
}
/// Reverts the node state down to at most the last finalized block.
///
/// In particular this reverts:
/// - `ApprovalVotingSubsystem` data in the teyrchains-db;
/// - `ChainSelectionSubsystem` data in the teyrchains-db;
/// - Low level Babe and Grandpa consensus data.
#[cfg(feature = "full-node")]
pub fn revert_backend(
client: Arc<FullClient>,
backend: Arc<FullBackend>,
blocks: BlockNumber,
config: Configuration,
task_handle: SpawnTaskHandle,
) -> Result<(), Error> {
let best_number = client.info().best_number;
let finalized = client.info().finalized_number;
let revertible = blocks.min(best_number - finalized);
if revertible == 0 {
return Ok(());
}
let number = best_number - revertible;
let hash = client.block_hash_from_id(&BlockId::Number(number))?.ok_or(
pezsp_blockchain::Error::Backend(format!(
"Unexpected hash lookup failure for block number: {}",
number
)),
)?;
let teyrchains_db = open_database(&config.database)
.map_err(|err| pezsp_blockchain::Error::Backend(err.to_string()))?;
revert_approval_voting(teyrchains_db.clone(), hash, task_handle)?;
revert_chain_selection(teyrchains_db, hash)?;
// Revert Bizinikiwi consensus related components
pezsc_consensus_babe::revert(client.clone(), backend, blocks)?;
pezsc_consensus_grandpa::revert(client, blocks)?;
Ok(())
}
fn revert_chain_selection(db: Arc<dyn Database>, hash: Hash) -> pezsp_blockchain::Result<()> {
let config = chain_selection_subsystem::Config {
col_data: teyrchains_db::REAL_COLUMNS.col_chain_selection_data,
stagnant_check_interval: chain_selection_subsystem::StagnantCheckInterval::never(),
stagnant_check_mode: chain_selection_subsystem::StagnantCheckMode::PruneOnly,
};
let chain_selection = chain_selection_subsystem::ChainSelectionSubsystem::new(config, db);
chain_selection
.revert_to(hash)
.map_err(|err| pezsp_blockchain::Error::Backend(err.to_string()))
}
fn revert_approval_voting(
db: Arc<dyn Database>,
hash: Hash,
task_handle: SpawnTaskHandle,
) -> pezsp_blockchain::Result<()> {
let config = approval_voting_subsystem::Config {
col_approval_data: teyrchains_db::REAL_COLUMNS.col_approval_data,
slot_duration_millis: Default::default(),
};
let approval_voting = approval_voting_subsystem::ApprovalVotingSubsystem::with_config(
config,
db,
Arc::new(pezsc_keystore::LocalKeystore::in_memory()),
Box::new(pezsp_consensus::NoNetwork),
approval_voting_subsystem::Metrics::default(),
Arc::new(SpawnGlue(task_handle)),
);
approval_voting
.revert_to(hash)
.map_err(|err| pezsp_blockchain::Error::Backend(err.to_string()))
}