feat: Rebrand Polkadot/Substrate references to PezkuwiChain

This commit systematically rebrands various references from Parity Technologies'
Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk.

Key changes include:
- Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks.
- Modified internal documentation and code comments to reflect PezkuwiChain naming and structure.
- Replaced direct references to  with  or specific paths within the  for XCM, Pezkuwi, and other modules.
- Cleaned up deprecated  issue and PR references in various  and  files, particularly in  and  modules.
- Adjusted image and logo URLs in documentation to point to PezkuwiChain assets.
- Removed or rephrased comments related to external Polkadot/Substrate PRs and issues.

This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
+33
View File
@@ -0,0 +1,33 @@
[package]
name = "pezkuwi-omni-node"
version = "0.1.0"
authors.workspace = true
edition.workspace = true
build = "build.rs"
description = "Generic binary that can run a teyrchain node with u32 block number and Aura consensus"
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
[lints]
workspace = true
[dependencies]
color-eyre = { workspace = true }
# Local
pezkuwi-omni-node-lib = { workspace = true, features = [
"pezkuwichain-native",
"zagros-native",
] }
[dev-dependencies]
assert_cmd = { workspace = true }
[build-dependencies]
bizinikiwi-build-script-utils = { workspace = true, default-features = true }
[features]
default = []
runtime-benchmarks = ["pezkuwi-omni-node-lib/runtime-benchmarks"]
try-runtime = ["pezkuwi-omni-node-lib/try-runtime"]
+83
View File
@@ -0,0 +1,83 @@
# PezkuwiChain Omni Node
This is a white labeled implementation based on `pezkuwi-omni-node-lib`.
It can be used to start a teyrchain node from a provided chain spec file. It is only compatible with runtimes that use block
number `u32` and `Aura` consensus.
## Installation
Download & expose it via `PATH`:
```bash
# Download and set it on PATH.
wget https://github.com/pezkuwichain/pezkuwi-sdk/releases/download/<stable_release_tag>/pezkuwi-omni-node
chmod +x pezkuwi-omni-node
export PATH="$PATH:`pwd`"
```
> Replace `<stable_release_tag>` with the latest stable tag from the [Pezkuwi SDK releases](https://github.com/pezkuwichain/pezkuwi-sdk/releases)
>
> For example:
> ```bash
> wget https://github.com/pezkuwichain/pezkuwi-sdk/releases/download/pezkuwi-stable2506-1/pezkuwi-omni-node
> ```
Compile & install via `cargo`:
```bash
# Assuming ~/.cargo/bin is on the PATH
cargo install pezkuwi-omni-node --locked
```
## Usage
A basic example for an Omni Node run starts from a runtime which implements the [`sp_genesis_builder::GenesisBuilder`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html).
The interface mandates the runtime to expose a [`named-preset`](https://docs.rs/pezstaging-chain-spec-builder/latest/staging_chain_spec_builder/#generate-chain-spec-using-runtime-provided-genesis-config-preset).
### 1. Install chain-spec-builder
**Note**: `chain-spec-builder` binary is published on [`crates.io`](https://crates.io) under
[`pezstaging-chain-spec-builder`](https://crates.io/crates/pezstaging-chain-spec-builder) due to a name conflict.
Install it with `cargo` like bellow :
```bash
cargo install pezstaging-chain-spec-builder --locked
```
### 2. Generate a chain spec
Omni Node requires the chain spec to include a JSON key named `relay_chain`. It is set to a chain id,
representing the chain name, e.g. `zagros`, `paseo`, `pezkuwichain`, `pezkuwi`, or `kusama`, but
there are also local variants that can be used for testing, like `pezkuwichain-local` or `zagros-local`. The
local variants are available only for a build of `pezkuwi-omni-node` with
`zagros-native` and `pezkuwichain-native` features respectively.
<!-- TODO: https://github.com/pezkuwichain/pezkuwi-sdk/issues/156 -->
Additionaly, the `--para-id` flag can be used to set the JSON key named `para_id`. This flag is used
by nodes to determine the teyrchain id, and it is especially useful when the teyrchain id can not be
fetched from the runtime, when the state points to a runtime that does not implement the
`cumulus_primitives_core::GetTeyrchainInfo` runtime API. It is recommended for runtimes to implement
the runtime API and be upgraded on chain.
Example command bellow:
```bash
chain-spec-builder create --relay-chain <relay_chain_id> --para-id <id> -r <runtime.wasm> named-preset <preset_name>
```
### 3. Run Omni Node
And now with the generated chain spec we can start the node in development mode like so:
```bash
pezkuwi-omni-node --dev --chain <chain_spec.json>
```
## Useful links
* [`Omni Node Pezkuwi SDK Docs`](https://docs.pezkuwichain.io/sdk/master/pezkuwi_sdk_docs/reference_docs/omni_node/index.html)
* [`Chain Spec Genesis Reference Docs`](https://docs.pezkuwichain.io/sdk/master/pezkuwi_sdk_docs/reference_docs/chain_spec_genesis/index.html)
* `pezkuwi-teyrchain-bin`
* [`pezkuwi-sdk-teyrchain-template`](https://github.com/pezkuwichain/pezkuwi-sdk-teyrchain-template)
* [`frame-omni-bencher`](https://crates.io/crates/frame-omni-bencher)
* [`pezstaging-chain-spec-builder`](https://crates.io/crates/pezstaging-chain-spec-builder)
+22
View File
@@ -0,0 +1,22 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use bizinikiwi_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed};
fn main() {
generate_cargo_keys();
rerun_if_git_head_changed();
}
+191
View File
@@ -0,0 +1,191 @@
[package]
name = "pezkuwi-omni-node-lib"
version = "0.1.0"
authors.workspace = true
edition.workspace = true
description = "Helper library that can be used to build a teyrchain node"
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
[lints]
workspace = true
[lib]
path = "src/lib.rs"
[dependencies]
async-trait = { workspace = true }
chain-spec-builder = { workspace = true }
clap = { features = ["derive"], workspace = true }
codec = { workspace = true, default-features = true }
color-print = { workspace = true }
docify = { workspace = true }
futures = { workspace = true }
log = { workspace = true, default-features = true }
serde = { features = ["derive"], workspace = true, default-features = true }
serde_json = { workspace = true, default-features = true }
# Local
jsonrpsee = { features = ["server"], workspace = true }
scale-info = { workspace = true }
subxt-metadata = { workspace = true, default-features = true }
teyrchains-common = { workspace = true, default-features = true }
# Bizinikiwi
pezframe-benchmarking = { optional = true, workspace = true, default-features = true }
pezframe-benchmarking-cli = { workspace = true, default-features = true }
pezframe-support = { optional = true, workspace = true, default-features = true }
pezframe-system-rpc-runtime-api = { workspace = true, default-features = true }
pezframe-try-runtime = { optional = true, workspace = true, default-features = true }
pezpallet-transaction-payment = { workspace = true, default-features = true }
pezpallet-transaction-payment-rpc = { workspace = true, default-features = true }
pezpallet-transaction-payment-rpc-runtime-api = { workspace = true, default-features = true }
prometheus-endpoint = { workspace = true, default-features = true }
pezsc-basic-authorship = { workspace = true, default-features = true }
pezsc-chain-spec = { workspace = true, default-features = true }
pezsc-cli = { workspace = true, default-features = false }
pezsc-client-api = { workspace = true, default-features = true }
pezsc-client-db = { workspace = true, default-features = true }
pezsc-consensus = { workspace = true, default-features = true }
pezsc-consensus-manual-seal = { workspace = true, default-features = true }
pezsc-executor = { workspace = true, default-features = true }
pezsc-keystore = { workspace = true, default-features = true }
pezsc-network = { workspace = true, default-features = true }
pezsc-network-statement = { workspace = true, default-features = true }
pezsc-network-sync = { workspace = true, default-features = true }
pezsc-offchain = { workspace = true, default-features = true }
pezsc-rpc = { workspace = true, default-features = true }
pezsc-runtime-utilities = { workspace = true, default-features = true }
pezsc-service = { workspace = true, default-features = false }
pezsc-statement-store = { workspace = true, default-features = true }
pezsc-storage-monitor = { workspace = true, default-features = true }
pezsc-sysinfo = { workspace = true, default-features = true }
pezsc-telemetry = { workspace = true, default-features = true }
pezsc-tracing = { workspace = true, default-features = true }
pezsc-transaction-pool = { workspace = true, default-features = true }
pezsc-transaction-pool-api = { workspace = true, default-features = true }
pezsp-api = { workspace = true, default-features = true }
pezsp-block-builder = { workspace = true, default-features = true }
pezsp-consensus = { workspace = true, default-features = true }
pezsp-consensus-aura = { workspace = true, default-features = true }
pezsp-core = { workspace = true, default-features = true }
pezsp-genesis-builder = { workspace = true }
pezsp-inherents = { workspace = true, default-features = true }
pezsp-keystore = { workspace = true, default-features = true }
pezsp-offchain = { workspace = true, default-features = true }
pezsp-runtime = { workspace = true }
pezsp-session = { workspace = true, default-features = true }
pezsp-statement-store = { workspace = true, default-features = true }
pezsp-storage = { workspace = true, default-features = true }
pezsp-timestamp = { workspace = true, default-features = true }
pezsp-transaction-pool = { workspace = true, default-features = true }
pezsp-version = { workspace = true, default-features = true }
pezsp-weights = { workspace = true, default-features = true }
bizinikiwi-frame-rpc-system = { workspace = true, default-features = true }
bizinikiwi-state-trie-migration-rpc = { workspace = true, default-features = true }
# Pezkuwi
pezkuwi-cli = { workspace = true, default-features = true, features = [
"service",
] }
pezkuwi-primitives = { workspace = true, default-features = true }
# Pezcumulus
pezcumulus-client-bootnodes = { workspace = true, default-features = true }
pezcumulus-client-cli = { workspace = true, default-features = true }
pezcumulus-client-collator = { workspace = true, default-features = true }
pezcumulus-client-consensus-aura = { workspace = true, default-features = true }
pezcumulus-client-consensus-common = { workspace = true, default-features = true }
pezcumulus-client-consensus-proposer = { workspace = true, default-features = true }
pezcumulus-client-consensus-relay-chain = { workspace = true, default-features = true }
pezcumulus-client-service = { workspace = true, default-features = true }
pezcumulus-client-teyrchain-inherent = { workspace = true, default-features = true }
pezcumulus-primitives-aura = { workspace = true, default-features = true }
pezcumulus-primitives-core = { workspace = true, default-features = true }
pezcumulus-relay-chain-interface = { workspace = true, default-features = true }
futures-timer = { workspace = true }
pezsc-consensus-aura = { workspace = true }
[dev-dependencies]
assert_cmd = { workspace = true }
pezcumulus-test-runtime = { workspace = true }
nix = { features = ["signal"], workspace = true }
tokio = { version = "1.43.1", features = ["macros", "parking_lot", "time"] }
wait-timeout = { workspace = true }
[features]
default = []
pezkuwichain-native = ["pezkuwi-cli/pezkuwichain-native"]
zagros-native = ["pezkuwi-cli/zagros-native"]
runtime-benchmarks = [
"chain-spec-builder/runtime-benchmarks",
"pezcumulus-client-bootnodes/runtime-benchmarks",
"pezcumulus-client-cli/runtime-benchmarks",
"pezcumulus-client-collator/runtime-benchmarks",
"pezcumulus-client-consensus-aura/runtime-benchmarks",
"pezcumulus-client-consensus-common/runtime-benchmarks",
"pezcumulus-client-consensus-proposer/runtime-benchmarks",
"pezcumulus-client-consensus-relay-chain/runtime-benchmarks",
"pezcumulus-client-service/runtime-benchmarks",
"pezcumulus-client-teyrchain-inherent/runtime-benchmarks",
"pezcumulus-primitives-aura/runtime-benchmarks",
"pezcumulus-primitives-core/runtime-benchmarks",
"pezcumulus-relay-chain-interface/runtime-benchmarks",
"pezcumulus-test-runtime/runtime-benchmarks",
"pezframe-benchmarking-cli/runtime-benchmarks",
"pezframe-benchmarking/runtime-benchmarks",
"pezframe-support/runtime-benchmarks",
"pezframe-system-rpc-runtime-api/runtime-benchmarks",
"pezframe-try-runtime?/runtime-benchmarks",
"pezpallet-transaction-payment-rpc-runtime-api/runtime-benchmarks",
"pezpallet-transaction-payment-rpc/runtime-benchmarks",
"pezpallet-transaction-payment/runtime-benchmarks",
"pezkuwi-cli/runtime-benchmarks",
"pezkuwi-primitives/runtime-benchmarks",
"pezsc-basic-authorship/runtime-benchmarks",
"pezsc-chain-spec/runtime-benchmarks",
"pezsc-cli/runtime-benchmarks",
"pezsc-client-api/runtime-benchmarks",
"pezsc-client-db/runtime-benchmarks",
"pezsc-consensus-aura/runtime-benchmarks",
"pezsc-consensus-manual-seal/runtime-benchmarks",
"pezsc-consensus/runtime-benchmarks",
"pezsc-executor/runtime-benchmarks",
"pezsc-network-statement/runtime-benchmarks",
"pezsc-network-sync/runtime-benchmarks",
"pezsc-network/runtime-benchmarks",
"pezsc-offchain/runtime-benchmarks",
"pezsc-rpc/runtime-benchmarks",
"pezsc-runtime-utilities/runtime-benchmarks",
"pezsc-service/runtime-benchmarks",
"pezsc-statement-store/runtime-benchmarks",
"pezsc-sysinfo/runtime-benchmarks",
"pezsc-tracing/runtime-benchmarks",
"pezsc-transaction-pool-api/runtime-benchmarks",
"pezsc-transaction-pool/runtime-benchmarks",
"pezsp-api/runtime-benchmarks",
"pezsp-block-builder/runtime-benchmarks",
"pezsp-consensus-aura/runtime-benchmarks",
"pezsp-consensus/runtime-benchmarks",
"pezsp-genesis-builder/runtime-benchmarks",
"pezsp-inherents/runtime-benchmarks",
"pezsp-offchain/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"pezsp-session/runtime-benchmarks",
"pezsp-statement-store/runtime-benchmarks",
"pezsp-timestamp/runtime-benchmarks",
"pezsp-transaction-pool/runtime-benchmarks",
"pezsp-version/runtime-benchmarks",
"bizinikiwi-frame-rpc-system/runtime-benchmarks",
"bizinikiwi-state-trie-migration-rpc/runtime-benchmarks",
"teyrchains-common/runtime-benchmarks",
]
try-runtime = [
"pezframe-support/try-runtime",
"pezframe-try-runtime/try-runtime",
"pezpallet-transaction-payment/try-runtime",
"pezkuwi-cli/try-runtime",
"pezsp-runtime/try-runtime",
"teyrchains-common/try-runtime",
]
@@ -0,0 +1,26 @@
# PezkuwiChain Omni Node Library
Helper library that can be used to run a teyrchain node.
## Overview
This library can be used to run a teyrchain node while also customizing the chain specs
that are supported by default by the `--chain-spec` argument of the node's `CLI`
and the parameters of the runtime that is associated with each of these chain specs.
## API
The library exposes the possibility to provide a [`RunConfig`]. Through this structure
2 optional configurations can be provided:
- a chain spec loader (an implementation of [`chain_spec::LoadSpec`]): this can be used for
providing the chain specs that are supported by default by the `--chain-spec` argument of the
node's `CLI` and the actual chain config associated with each one.
- a runtime resolver (an implementation of [`runtime::RuntimeResolver`]): this can be used for
providing the parameters of the runtime that is associated with each of the chain specs
Apart from this, a [`CliConfig`] can also be provided, that can be used to customize some
user-facing binary author, support url, etc.
## Examples
For an example, see the `pezkuwi-teyrchain-bin` crate.
+521
View File
@@ -0,0 +1,521 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! CLI options of the omni-node. See [`Command`].
/// Default block time for dev mode when using `--dev` flag.
const DEFAULT_DEV_BLOCK_TIME_MS: u64 = 3000;
use crate::{
chain_spec::DiskChainSpecLoader,
common::{
chain_spec::{Extensions, LoadSpec},
NodeExtraArgs,
},
};
use chain_spec_builder::ChainSpecBuilder;
use clap::{Command, CommandFactory, FromArgMatches, ValueEnum};
use pezsc_chain_spec::ChainSpec;
use pezsc_cli::{
CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, NetworkParams,
RpcEndpoint, SharedParams, BizinikiwiCli,
};
use pezsc_service::{config::PrometheusConfig, BasePath};
use pezsc_storage_monitor::StorageMonitorParams;
use std::{
fmt::{Debug, Display, Formatter},
marker::PhantomData,
path::PathBuf,
};
/// Trait that can be used to customize some of the customer-facing info related to the node binary
/// that is being built using this library.
///
/// The related info is shown to the customer as part of logs or help messages.
/// It does not impact functionality.
pub trait CliConfig {
/// The version of the resulting node binary.
fn impl_version() -> String;
/// The description of the resulting node binary.
fn description(executable_name: String) -> String {
format!(
"The command-line arguments provided first will be passed to the teyrchain node, \n\
and the arguments provided after -- will be passed to the relay chain node. \n\
\n\
Example: \n\
\n\
{} [teyrchain-args] -- [relay-chain-args]",
executable_name
)
}
/// The author of the resulting node binary.
fn author() -> String;
/// The support URL for the resulting node binary.
fn support_url() -> String;
/// The starting copyright year of the resulting node binary.
fn copyright_start_year() -> u16;
}
/// Sub-commands supported by the collator.
#[derive(Debug, clap::Subcommand)]
pub enum Subcommand {
/// Key management CLI utilities
#[command(subcommand)]
Key(pezsc_cli::KeySubcommand),
/// Build a chain specification.
///
/// The `build-spec` command relies on the chain specification built (hard-coded) into the node
/// binary, and may utilize the genesis presets of the runtimes also embedded in the nodes
/// that support this command. Since `pezkuwi-omni-node` does not contain any embedded
/// runtime, and requires a `chain-spec` path to be passed to its `--chain` flag, the command
/// isn't bringing significant value as it does for other node binaries (e.g. the
/// `pezkuwi` binary).
///
/// For a more versatile `chain-spec` manipulation experience please check out the
/// `pezkuwi-omni-node chain-spec-builder` subcommand.
#[deprecated(
note = "build-spec will be removed after 1/06/2025. Use chain-spec-builder instead"
)]
BuildSpec(pezsc_cli::BuildSpecCmd),
/// Validate blocks.
CheckBlock(pezsc_cli::CheckBlockCmd),
/// Export blocks.
ExportBlocks(pezsc_cli::ExportBlocksCmd),
/// Export the state of a given block into a chain spec.
ExportState(pezsc_cli::ExportStateCmd),
/// Import blocks.
ImportBlocks(pezsc_cli::ImportBlocksCmd),
/// Revert the chain to a previous state.
Revert(pezsc_cli::RevertCmd),
/// Subcommand for generating and managing chain specifications.
///
/// A `chain-spec-builder` subcommand corresponds to the existing `chain-spec-builder` tool
/// (<https://crates.io/crates/pezstaging-chain-spec-builder>), which can be used already standalone.
/// It provides the same functionality as the tool but bundled with `pezkuwi-omni-node` to
/// enable easier access to chain-spec generation, patching, converting to raw or validation,
/// from a single binary, which can be used as a teyrchain node tool
/// For a detailed usage guide please check out the standalone tool's crates.io or docs.rs
/// pages:
/// - <https://crates.io/crates/pezstaging-chain-spec-builder>
/// - <https://docs.rs/pezstaging-chain-spec-builder/latest/pezstaging_chain_spec_builder/>
ChainSpecBuilder(ChainSpecBuilder),
/// Remove the whole chain.
PurgeChain(cumulus_client_cli::PurgeChainCmd),
/// Export the genesis state of the teyrchain.
#[command(alias = "export-genesis-state")]
ExportGenesisHead(cumulus_client_cli::ExportGenesisHeadCommand),
/// Export the genesis wasm of the teyrchain.
ExportGenesisWasm(cumulus_client_cli::ExportGenesisWasmCommand),
/// Sub-commands concerned with benchmarking.
/// The pallet benchmarking moved to the `pallet` sub-command.
#[command(subcommand)]
Benchmark(pezframe_benchmarking_cli::BenchmarkCmd),
}
/// CLI Options shipped with `pezkuwi-omni-node`.
#[derive(clap::Parser)]
#[command(
propagate_version = true,
args_conflicts_with_subcommands = true,
subcommand_negates_reqs = true
)]
pub struct Cli<Config: CliConfig> {
#[arg(skip)]
pub(crate) chain_spec_loader: Option<Box<dyn LoadSpec>>,
/// Possible subcommands. See [`Subcommand`].
#[command(subcommand)]
pub subcommand: Option<Subcommand>,
/// The shared parameters with all pezcumulus-based teyrchain nodes.
#[command(flatten)]
pub run: cumulus_client_cli::RunCmd,
/// Parameters for storage monitoring.
#[command(flatten)]
pub storage_monitor: StorageMonitorParams,
/// Start a dev node that produces a block each `dev_block_time` ms.
///
/// This is a dev option. It enables a manual sealing, meaning blocks are produced manually
/// rather than being part of an actual network consensus process. Using the option won't
/// result in starting or connecting to a teyrchain network. The resulting node will work on
/// its own, running the wasm blob and artificially producing a block each `dev_block_time` ms,
/// as if it was part of a teyrchain.
///
/// The `--dev` flag sets the `dev_block_time` to a default value of 3000ms unless explicitly
/// provided.
#[arg(long, conflicts_with = "instant_seal")]
pub dev_block_time: Option<u64>,
/// Start a dev node with instant seal.
///
/// This is a dev option that enables instant sealing, meaning blocks are produced
/// immediately when transactions are received, rather than at fixed intervals.
/// Using this option won't result in starting or connecting to a teyrchain network.
/// The resulting node will work on its own, running the wasm blob and producing blocks
/// instantly upon receiving transactions.
#[arg(long, conflicts_with = "dev_block_time")]
pub instant_seal: bool,
/// DEPRECATED: This feature has been stabilized, pLease use `--authoring slot-based` instead.
///
/// Use slot-based collator which can handle elastic scaling.
/// Use with care, this flag is unstable and subject to change.
#[arg(long, conflicts_with = "authoring")]
pub experimental_use_slot_based: bool,
/// Authoring style to use.
#[arg(long, default_value_t = AuthoringPolicy::Lookahead)]
pub authoring: AuthoringPolicy,
/// Disable automatic hardware benchmarks.
///
/// By default these benchmarks are automatically ran at startup and measure
/// the CPU speed, the memory bandwidth and the disk speed.
///
/// The results are then printed out in the logs, and also sent as part of
/// telemetry, if telemetry is enabled.
#[arg(long)]
pub no_hardware_benchmarks: bool,
/// Export all `PoVs` build by this collator to the given folder.
///
/// This is useful for debugging issues that are occurring while validating these `PoVs` on the
/// relay chain.
#[arg(long)]
pub export_pov_to_path: Option<PathBuf>,
/// Relay chain arguments
#[arg(raw = true)]
pub relay_chain_args: Vec<String>,
/// Enable the statement store.
///
/// The statement store is a store for statements validated using the runtime API
/// `validate_statement`. It should be enabled for chains that provide this runtime API.
#[arg(long)]
pub enable_statement_store: bool,
#[arg(skip)]
pub(crate) _phantom: PhantomData<Config>,
}
/// Development sealing mode.
#[derive(Debug, Clone, Copy)]
pub(crate) enum DevSealMode {
/// Produces blocks immediately upon receiving transactions.
InstantSeal,
/// Produces blocks at fixed time intervals.
/// The u64 parameter represents the block time in milliseconds.
ManualSeal(u64),
}
/// Collator implementation to use.
#[derive(PartialEq, Debug, ValueEnum, Clone, Copy)]
pub enum AuthoringPolicy {
/// Use the lookahead collator. Builds a block once per imported relay chain block and
/// on relay chain forks. Default for asynchronous backing chains.
Lookahead,
/// Use the slot-based collator. Builds a block based on time. Can utilize multiple cores,
/// always builds on the best relay chain block available. Should be used with elastic-scaling
/// chains.
SlotBased,
}
impl Display for AuthoringPolicy {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
AuthoringPolicy::Lookahead => write!(f, "lookahead"),
AuthoringPolicy::SlotBased => write!(f, "slot-based"),
}
}
}
impl<Config: CliConfig> Cli<Config> {
pub(crate) fn node_extra_args(&self) -> NodeExtraArgs {
NodeExtraArgs {
authoring_policy: self
.experimental_use_slot_based
.then(|| AuthoringPolicy::SlotBased)
.unwrap_or(self.authoring),
export_pov: self.export_pov_to_path.clone(),
max_pov_percentage: self.run.experimental_max_pov_percentage,
enable_statement_store: self.enable_statement_store,
storage_monitor: self.storage_monitor.clone(),
}
}
/// Returns the dev seal mode if the node is in dev mode.
pub(crate) fn dev_mode(&self) -> Option<DevSealMode> {
if self.instant_seal {
Some(DevSealMode::InstantSeal)
} else if let Some(dev_block_time) = self.dev_block_time {
Some(DevSealMode::ManualSeal(dev_block_time))
} else if self.run.base.is_dev().unwrap_or(false) {
Some(DevSealMode::ManualSeal(DEFAULT_DEV_BLOCK_TIME_MS))
} else {
None
}
}
}
impl<Config: CliConfig> BizinikiwiCli for Cli<Config> {
fn impl_name() -> String {
Self::executable_name()
}
fn impl_version() -> String {
Config::impl_version()
}
fn description() -> String {
Config::description(Self::executable_name())
}
fn author() -> String {
Config::author()
}
fn support_url() -> String {
Config::support_url()
}
fn copyright_start_year() -> i32 {
Config::copyright_start_year() as i32
}
fn load_spec(&self, id: &str) -> Result<Box<dyn ChainSpec>, String> {
match &self.chain_spec_loader {
Some(chain_spec_loader) => chain_spec_loader.load_spec(id),
None => DiskChainSpecLoader.load_spec(id),
}
}
}
/// The relay chain CLI flags. These are passed in after a `--` at the end.
#[derive(Debug)]
pub struct RelayChainCli<Config: CliConfig> {
/// The actual relay chain cli object.
pub base: pezkuwi_cli::RunCmd,
/// Optional chain id that should be passed to the relay chain.
pub chain_id: Option<String>,
/// The base path that should be used by the relay chain.
pub base_path: Option<PathBuf>,
_phantom: PhantomData<Config>,
}
impl<Config: CliConfig> RelayChainCli<Config> {
fn pezkuwi_cmd() -> Command {
let help_template = color_print::cformat!(
"The arguments that are passed to the relay chain node. \n\
\n\
<bold><underline>RELAY_CHAIN_ARGS:</></> \n\
{{options}}",
);
pezkuwi_cli::RunCmd::command().no_binary_name(true).help_template(help_template)
}
/// Parse the relay chain CLI parameters using the teyrchain `Configuration`.
pub fn new<'a>(
para_config: &pezsc_service::Configuration,
relay_chain_args: impl Iterator<Item = &'a String>,
) -> Self {
let pezkuwi_cmd = Self::pezkuwi_cmd();
let matches = pezkuwi_cmd.get_matches_from(relay_chain_args);
let base = FromArgMatches::from_arg_matches(&matches).unwrap_or_else(|e| e.exit());
let extension = Extensions::try_get(&*para_config.chain_spec);
let chain_id = extension.map(|e| e.relay_chain());
let base_path = para_config.base_path.path().join("pezkuwi");
Self { base, chain_id, base_path: Some(base_path), _phantom: Default::default() }
}
}
impl<Config: CliConfig> BizinikiwiCli for RelayChainCli<Config> {
fn impl_name() -> String {
Cli::<Config>::impl_name()
}
fn impl_version() -> String {
Cli::<Config>::impl_version()
}
fn description() -> String {
Cli::<Config>::description()
}
fn author() -> String {
Cli::<Config>::author()
}
fn support_url() -> String {
Cli::<Config>::support_url()
}
fn copyright_start_year() -> i32 {
Cli::<Config>::copyright_start_year()
}
fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn ChainSpec>, String> {
pezkuwi_cli::Cli::from_iter([Self::executable_name()].iter()).load_spec(id)
}
}
impl<Config: CliConfig> DefaultConfigurationValues for RelayChainCli<Config> {
fn p2p_listen_port() -> u16 {
30334
}
fn rpc_listen_port() -> u16 {
9945
}
fn prometheus_listen_port() -> u16 {
9616
}
}
impl<Config: CliConfig> CliConfiguration<Self> for RelayChainCli<Config> {
fn shared_params(&self) -> &SharedParams {
self.base.base.shared_params()
}
fn import_params(&self) -> Option<&ImportParams> {
self.base.base.import_params()
}
fn network_params(&self) -> Option<&NetworkParams> {
self.base.base.network_params()
}
fn keystore_params(&self) -> Option<&KeystoreParams> {
self.base.base.keystore_params()
}
fn base_path(&self) -> pezsc_cli::Result<Option<BasePath>> {
Ok(self
.shared_params()
.base_path()?
.or_else(|| self.base_path.clone().map(Into::into)))
}
fn rpc_addr(&self, default_listen_port: u16) -> pezsc_cli::Result<Option<Vec<RpcEndpoint>>> {
self.base.base.rpc_addr(default_listen_port)
}
fn prometheus_config(
&self,
default_listen_port: u16,
chain_spec: &Box<dyn ChainSpec>,
) -> pezsc_cli::Result<Option<PrometheusConfig>> {
self.base.base.prometheus_config(default_listen_port, chain_spec)
}
fn init<F>(
&self,
_support_url: &String,
_impl_version: &String,
_logger_hook: F,
) -> pezsc_cli::Result<()>
where
F: FnOnce(&mut pezsc_cli::LoggerBuilder),
{
unreachable!("PezkuwiCli is never initialized; qed");
}
fn chain_id(&self, is_dev: bool) -> pezsc_cli::Result<String> {
let chain_id = self.base.base.chain_id(is_dev)?;
Ok(if chain_id.is_empty() { self.chain_id.clone().unwrap_or_default() } else { chain_id })
}
fn role(&self, is_dev: bool) -> pezsc_cli::Result<pezsc_service::Role> {
self.base.base.role(is_dev)
}
fn transaction_pool(
&self,
is_dev: bool,
) -> pezsc_cli::Result<pezsc_service::config::TransactionPoolOptions> {
self.base.base.transaction_pool(is_dev)
}
fn trie_cache_maximum_size(&self) -> pezsc_cli::Result<Option<usize>> {
self.base.base.trie_cache_maximum_size()
}
fn rpc_methods(&self) -> pezsc_cli::Result<pezsc_service::config::RpcMethods> {
self.base.base.rpc_methods()
}
fn rpc_max_connections(&self) -> pezsc_cli::Result<u32> {
self.base.base.rpc_max_connections()
}
fn rpc_cors(&self, is_dev: bool) -> pezsc_cli::Result<Option<Vec<String>>> {
self.base.base.rpc_cors(is_dev)
}
fn default_heap_pages(&self) -> pezsc_cli::Result<Option<u64>> {
self.base.base.default_heap_pages()
}
fn force_authoring(&self) -> pezsc_cli::Result<bool> {
self.base.base.force_authoring()
}
fn disable_grandpa(&self) -> pezsc_cli::Result<bool> {
self.base.base.disable_grandpa()
}
fn max_runtime_instances(&self) -> pezsc_cli::Result<Option<usize>> {
self.base.base.max_runtime_instances()
}
fn announce_block(&self) -> pezsc_cli::Result<bool> {
self.base.base.announce_block()
}
fn telemetry_endpoints(
&self,
chain_spec: &Box<dyn ChainSpec>,
) -> pezsc_cli::Result<Option<pezsc_telemetry::TelemetryEndpoints>> {
self.base.base.telemetry_endpoints(chain_spec)
}
fn node_name(&self) -> pezsc_cli::Result<String> {
self.base.base.node_name()
}
}
@@ -0,0 +1,378 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
cli::{Cli, RelayChainCli, Subcommand},
common::{
chain_spec::LoadSpec,
runtime::{
AuraConsensusId, Consensus, Runtime, RuntimeResolver as RuntimeResolverT,
RuntimeResolver,
},
spec::DynNodeSpec,
types::Block,
NodeBlock, NodeExtraArgs,
},
extra_subcommand::DefaultExtraSubcommands,
fake_runtime_api,
runtime::BlockNumber,
};
use clap::{CommandFactory, FromArgMatches};
#[cfg(feature = "runtime-benchmarks")]
use cumulus_client_service::storage_proof_size::HostFunctions as ReclaimHostFunctions;
use pezframe_benchmarking_cli::{BenchmarkCmd, BIZINIKIWI_REFERENCE_HARDWARE};
use log::info;
use pezsc_cli::{Result, BizinikiwiCli};
#[cfg(feature = "runtime-benchmarks")]
use pezsp_runtime::traits::HashingFor;
/// Structure that can be used in order to provide customizers for different functionalities of the
/// node binary that is being built using this library.
pub struct RunConfig {
/// A custom chain spec loader.
pub chain_spec_loader: Box<dyn LoadSpec>,
/// A custom runtime resolver.
pub runtime_resolver: Box<dyn RuntimeResolver>,
}
impl RunConfig {
/// Creates a new `RunConfig` instance.
pub fn new(
runtime_resolver: Box<dyn RuntimeResolver>,
chain_spec_loader: Box<dyn LoadSpec>,
) -> Self {
RunConfig { runtime_resolver, chain_spec_loader }
}
}
pub fn new_aura_node_spec<Block>(
aura_id: AuraConsensusId,
extra_args: &NodeExtraArgs,
) -> Box<dyn DynNodeSpec>
where
Block: NodeBlock,
{
match aura_id {
AuraConsensusId::Sr25519 => crate::nodes::aura::new_aura_node_spec::<
Block,
fake_runtime_api::aura_sr25519::RuntimeApi,
pezsp_consensus_aura::sr25519::AuthorityId,
>(extra_args),
AuraConsensusId::Ed25519 => crate::nodes::aura::new_aura_node_spec::<
Block,
fake_runtime_api::aura_ed25519::RuntimeApi,
pezsp_consensus_aura::ed25519::AuthorityId,
>(extra_args),
}
}
fn new_node_spec(
config: &pezsc_service::Configuration,
runtime_resolver: &Box<dyn RuntimeResolverT>,
extra_args: &NodeExtraArgs,
) -> std::result::Result<Box<dyn DynNodeSpec>, pezsc_cli::Error> {
let runtime = runtime_resolver.runtime(config.chain_spec.as_ref())?;
Ok(match runtime {
Runtime::Omni(block_number, consensus) => match (block_number, consensus) {
(BlockNumber::U32, Consensus::Aura(aura_id)) =>
new_aura_node_spec::<Block<u32>>(aura_id, extra_args),
(BlockNumber::U64, Consensus::Aura(aura_id)) =>
new_aura_node_spec::<Block<u64>>(aura_id, extra_args),
},
})
}
/// Parse command line arguments into service configuration.
pub fn run<CliConfig: crate::cli::CliConfig>(cmd_config: RunConfig) -> Result<()> {
run_with_custom_cli::<CliConfig, DefaultExtraSubcommands>(cmd_config)
}
/// Parse commandline arguments into service configuration and inject an
/// optional extra subcommand.
///
/// `run_with_custom_cli` builds the base CLI for the node binary, then asks the
/// `Extra` type for an optional extra subcommand.
///
/// When the user actually invokes that extra subcommand,
/// `Extra::from_arg_matches` returns a parsed value which is immediately passed
/// to `extra.handle(&cfg)` and the process exits. Otherwise control falls
/// through to the normal nodestartup / utility subcommand match.
///
/// # Type Parameters
/// * `CliConfig` customization trait supplying userfacing info (name, description, version) for
/// the binary.
/// * `Extra` an implementation of `ExtraSubcommand`. Use *`NoExtraSubcommand`* if the binary
/// should not expose any extra subcommands.
pub fn run_with_custom_cli<CliConfig, ExtraSubcommand>(cmd_config: RunConfig) -> Result<()>
where
CliConfig: crate::cli::CliConfig,
ExtraSubcommand: crate::extra_subcommand::ExtraSubcommand,
{
let cli_command = Cli::<CliConfig>::command();
let cli_command = ExtraSubcommand::augment_subcommands(cli_command);
let cli_command = Cli::<CliConfig>::setup_command(cli_command);
// Get matches for all CLI, including extra args.
let matches = cli_command.get_matches();
// Parse only the part corresponding to the extra args.
if let Ok(extra) = ExtraSubcommand::from_arg_matches(&matches) {
// Handle the extra, and return - subcommands are self contained,
// no need to handle the rest of the CLI or node running.
extra.handle(&cmd_config)?;
return Ok(());
}
// If matching on the extra subcommands fails, match on the rest of the node CLI as usual.
let mut cli =
Cli::<CliConfig>::from_arg_matches(&matches).map_err(|e| pezsc_cli::Error::Cli(e.into()))?;
cli.chain_spec_loader = Some(cmd_config.chain_spec_loader);
#[allow(deprecated)]
match &cli.subcommand {
Some(Subcommand::BuildSpec(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.sync_run(|config| cmd.run(config.chain_spec, config.network))
},
Some(Subcommand::CheckBlock(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.async_run(|config| {
let node =
new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?;
node.prepare_check_block_cmd(config, cmd)
})
},
Some(Subcommand::ExportBlocks(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.async_run(|config| {
let node =
new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?;
node.prepare_export_blocks_cmd(config, cmd)
})
},
Some(Subcommand::ExportState(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.async_run(|config| {
let node =
new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?;
node.prepare_export_state_cmd(config, cmd)
})
},
Some(Subcommand::ImportBlocks(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.async_run(|config| {
let node =
new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?;
node.prepare_import_blocks_cmd(config, cmd)
})
},
Some(Subcommand::Revert(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.async_run(|config| {
let node =
new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?;
node.prepare_revert_cmd(config, cmd)
})
},
Some(Subcommand::ChainSpecBuilder(cmd)) =>
cmd.run().map_err(|err| pezsc_cli::Error::Application(err.into())),
Some(Subcommand::PurgeChain(cmd)) => {
let runner = cli.create_runner(cmd)?;
let pezkuwi_cli =
RelayChainCli::<CliConfig>::new(runner.config(), cli.relay_chain_args.iter());
runner.sync_run(|config| {
let pezkuwi_config = BizinikiwiCli::create_configuration(
&pezkuwi_cli,
&pezkuwi_cli,
config.tokio_handle.clone(),
)
.map_err(|err| format!("Relay chain argument error: {}", err))?;
cmd.run(config, pezkuwi_config)
})
},
Some(Subcommand::ExportGenesisHead(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.sync_run(|config| {
let node =
new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?;
node.run_export_genesis_head_cmd(config, cmd)
})
},
Some(Subcommand::ExportGenesisWasm(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.sync_run(|_config| {
let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?;
cmd.run(&*spec)
})
},
Some(Subcommand::Benchmark(cmd)) => {
// Switch on the concrete benchmark sub-command-
match cmd {
#[cfg(feature = "runtime-benchmarks")]
BenchmarkCmd::Pallet(cmd) => {
let chain = cmd
.shared_params
.chain
.as_ref()
.map(|chain| cli.load_spec(&chain))
.transpose()?;
cmd.run_with_spec::<HashingFor<Block<u32>>, ReclaimHostFunctions>(chain)
},
BenchmarkCmd::Block(cmd) => {
// The command needs the full node configuration because it uses the node
// client and the database source, which in its turn has a dependency on the
// chain spec, given via the `--chain` flag.
let runner = cli.create_runner(cmd)?;
runner.sync_run(|config| {
let node = new_node_spec(
&config,
&cmd_config.runtime_resolver,
&cli.node_extra_args(),
)?;
node.run_benchmark_block_cmd(config, cmd)
})
},
#[cfg(feature = "runtime-benchmarks")]
BenchmarkCmd::Storage(cmd) => {
// The command needs the full node configuration because it uses the node
// client and the database API, storage and shared_trie_cache. It requires
// the `--chain` flag to be passed.
let runner = cli.create_runner(cmd)?;
runner.sync_run(|config| {
let node = new_node_spec(
&config,
&cmd_config.runtime_resolver,
&cli.node_extra_args(),
)?;
node.run_benchmark_storage_cmd(config, cmd)
})
},
BenchmarkCmd::Machine(cmd) => {
// The command needs the full node configuration, and implicitly a chain
// spec to be passed, even if it doesn't use it directly. The `--chain` flag is
// relevant in determining the database path, which is used for the disk
// benchmark.
//
// TODO: change `machine` subcommand to take instead a disk path we want to
// benchmark?.
let runner = cli.create_runner(cmd)?;
runner.sync_run(|config| cmd.run(&config, BIZINIKIWI_REFERENCE_HARDWARE.clone()))
},
#[allow(unreachable_patterns)]
_ => Err("Benchmarking sub-command unsupported or compilation feature missing. \
Make sure to compile omni-node with --features=runtime-benchmarks \
to enable all supported benchmarks."
.into()),
}
},
Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?),
None => {
let runner = cli.create_runner(&cli.run.normalize())?;
let pezkuwi_cli =
RelayChainCli::<CliConfig>::new(runner.config(), cli.relay_chain_args.iter());
let collator_options = cli.run.collator_options();
if cli.experimental_use_slot_based {
log::warn!(
"Deprecated: The flag --experimental-use-slot-based is no longer \
supported. Please use --authoring slot-based instead. This feature will be removed \
after May 2025."
);
}
runner.run_node_until_exit(|config| async move {
let node_spec =
new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?;
if let Some(dev_mode) = cli.dev_mode() {
return node_spec.start_dev_node(config, dev_mode).map_err(Into::into);
}
// If Statemint (Statemine, Westmint, Rockmine) DB exists and we're using the
// asset-hub chain spec, then rename the base path to the new chain ID. In the case
// that both file paths exist, the node will exit, as the user must decide (by
// deleting one path) the information that they want to use as their DB.
let old_name = match config.chain_spec.id() {
"asset-hub-pezkuwi" => Some("statemint"),
"asset-hub-kusama" => Some("statemine"),
"asset-hub-zagros" => Some("westmint"),
"asset-hub-pezkuwichain" => Some("rockmine"),
_ => None,
};
if let Some(old_name) = old_name {
let new_path = config.base_path.config_dir(config.chain_spec.id());
let old_path = config.base_path.config_dir(old_name);
if old_path.exists() && new_path.exists() {
return Err(format!(
"Found legacy {} path {} and new Asset Hub path {}. \
Delete one path such that only one exists.",
old_name,
old_path.display(),
new_path.display()
)
.into());
}
if old_path.exists() {
std::fs::rename(old_path.clone(), new_path.clone())?;
info!(
"{} was renamed to Asset Hub. The filepath with associated data on disk \
has been renamed from {} to {}.",
old_name,
old_path.display(),
new_path.display()
);
}
}
let hwbench = (!cli.no_hardware_benchmarks)
.then(|| {
config.database.path().map(|database_path| {
let _ = std::fs::create_dir_all(database_path);
pezsc_sysinfo::gather_hwbench(
Some(database_path),
&BIZINIKIWI_REFERENCE_HARDWARE,
)
})
})
.flatten();
let tokio_handle = config.tokio_handle.clone();
let pezkuwi_config =
BizinikiwiCli::create_configuration(&pezkuwi_cli, &pezkuwi_cli, tokio_handle)
.map_err(|err| format!("Relay chain argument error: {}", err))?;
info!("✍️ Is collating: {}", if config.role.is_authority() { "yes" } else { "no" });
node_spec
.start_node(
config,
pezkuwi_config,
collator_options,
hwbench,
cli.node_extra_args(),
)
.await
.map_err(Into::into)
})
},
}
}
@@ -0,0 +1,70 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Aura-related primitives for pezcumulus teyrchain collators.
use codec::Codec;
use cumulus_primitives_aura::AuraUnincludedSegmentApi;
use pezsp_consensus_aura::AuraApi;
use pezsp_runtime::{
app_crypto::{AppCrypto, AppPair, AppSignature, Pair},
traits::Block as BlockT,
};
/// Convenience trait for defining the basic bounds of an `AuraId`.
pub trait AuraIdT: AppCrypto<Pair = Self::BoundedPair> + Codec + Send {
/// Extra bounds for the `Pair`.
type BoundedPair: AppPair + AppCrypto<Signature = Self::BoundedSignature>;
/// Extra bounds for the `Signature`.
type BoundedSignature: AppSignature
+ TryFrom<Vec<u8>>
+ std::hash::Hash
+ pezsp_runtime::traits::Member
+ Codec;
}
impl<T> AuraIdT for T
where
T: AppCrypto + Codec + Send + Sync,
<<T as AppCrypto>::Pair as AppCrypto>::Signature:
TryFrom<Vec<u8>> + std::hash::Hash + pezsp_runtime::traits::Member + Codec,
{
type BoundedPair = <T as AppCrypto>::Pair;
type BoundedSignature = <<T as AppCrypto>::Pair as AppCrypto>::Signature;
}
/// Convenience trait for defining the basic bounds of a teyrchain runtime that supports
/// the Aura consensus.
pub trait AuraRuntimeApi<Block: BlockT, AuraId: AuraIdT>:
pezsp_api::ApiExt<Block>
+ AuraApi<Block, <AuraId::BoundedPair as Pair>::Public>
+ AuraUnincludedSegmentApi<Block>
+ Sized
{
/// Check if the runtime has the Aura API.
fn has_aura_api(&self, at: Block::Hash) -> bool {
self.has_api::<dyn AuraApi<Block, <AuraId::BoundedPair as Pair>::Public>>(at)
.unwrap_or(false)
}
}
impl<T, Block: BlockT, AuraId: AuraIdT> AuraRuntimeApi<Block, AuraId> for T where
T: pezsp_api::ApiExt<Block>
+ AuraApi<Block, <AuraId::BoundedPair as Pair>::Public>
+ AuraUnincludedSegmentApi<Block>
{
}
@@ -0,0 +1,102 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Chain spec primitives.
pub use pezsc_chain_spec::ChainSpec;
use pezsc_chain_spec::ChainSpecExtension;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
/// Helper trait used for loading/building a chain spec starting from the chain ID.
pub trait LoadSpec {
/// Load/Build a chain spec starting from the chain ID.
fn load_spec(&self, id: &str) -> Result<Box<dyn ChainSpec>, String>;
}
/// Default implementation for `LoadSpec` that just reads a chain spec from the disk.
pub struct DiskChainSpecLoader;
impl LoadSpec for DiskChainSpecLoader {
fn load_spec(&self, path: &str) -> Result<Box<dyn ChainSpec>, String> {
Ok(Box::new(GenericChainSpec::from_json_file(path.into())?))
}
}
/// Generic extensions for Teyrchain ChainSpecs used for extracting the extensions from chain specs.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecExtension)]
pub struct Extensions {
/// The relay chain of the Teyrchain. It is kept here only for compatibility reasons until
/// people migrate to using the new `Extensions` struct and associated logic in the node
/// corresponding to pulling the teyrchain id from the runtime.
#[serde(alias = "relayChain", alias = "RelayChain")]
relay_chain: String,
/// The id of the Teyrchain.
#[serde(alias = "paraId", alias = "ParaId")]
para_id: Option<u32>,
}
impl Extensions {
/// Try to get the extension from the given `ChainSpec`.
pub fn try_get(chain_spec: &dyn pezsc_service::ChainSpec) -> Option<&Self> {
pezsc_chain_spec::get_extension(chain_spec.extensions())
}
/// Create the extensions only with the relay_chain.
pub fn new_with_relay_chain(relay_chain: String) -> Self {
Extensions { relay_chain, para_id: None }
}
/// Initialize extensions based on given parameters.
pub fn new(relay_chain: String, para_id: u32) -> Self {
Extensions { relay_chain, para_id: Some(para_id) }
}
/// Para id field getter
pub fn para_id(&self) -> Option<u32> {
self.para_id
}
/// Relay chain field getter
pub fn relay_chain(&self) -> String {
self.relay_chain.clone()
}
}
/// Generic chain spec for all pezkuwi-teyrchain runtimes
pub type GenericChainSpec = pezsc_service::GenericChainSpec<Extensions>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_decode_extension_camel_and_snake_case() {
let camel_case = r#"{"relayChain":"relay","paraId":1}"#;
let snake_case = r#"{"relay_chain":"relay","para_id":1}"#;
let pascal_case = r#"{"RelayChain":"relay","ParaId":1}"#;
let para_id_missing = r#"{"RelayChain":"zagros"}"#;
let camel_case_extension: Extensions = serde_json::from_str(camel_case).unwrap();
let snake_case_extension: Extensions = serde_json::from_str(snake_case).unwrap();
let pascal_case_extension: Extensions = serde_json::from_str(pascal_case).unwrap();
let missing_paraid_extension: Extensions = serde_json::from_str(para_id_missing).unwrap();
assert_eq!(camel_case_extension, snake_case_extension);
assert_eq!(snake_case_extension, pascal_case_extension);
assert_eq!(missing_paraid_extension.relay_chain, "zagros".to_string());
assert!(missing_paraid_extension.para_id.is_none());
}
}
@@ -0,0 +1,162 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::common::spec::BaseNodeSpec;
use cumulus_client_cli::ExportGenesisHeadCommand;
use pezframe_benchmarking_cli::BlockCmd;
#[cfg(any(feature = "runtime-benchmarks"))]
use pezframe_benchmarking_cli::StorageCmd;
use pezsc_cli::{CheckBlockCmd, ExportBlocksCmd, ExportStateCmd, ImportBlocksCmd, RevertCmd};
use pezsc_service::{Configuration, TaskManager};
use std::{future::Future, pin::Pin};
type SyncCmdResult = pezsc_cli::Result<()>;
type AsyncCmdResult<'a> =
pezsc_cli::Result<(Pin<Box<dyn Future<Output = SyncCmdResult> + 'a>>, TaskManager)>;
pub trait NodeCommandRunner {
fn prepare_check_block_cmd(
self: Box<Self>,
config: Configuration,
cmd: &CheckBlockCmd,
) -> AsyncCmdResult<'_>;
fn prepare_export_blocks_cmd(
self: Box<Self>,
config: Configuration,
cmd: &ExportBlocksCmd,
) -> AsyncCmdResult<'_>;
fn prepare_export_state_cmd(
self: Box<Self>,
config: Configuration,
cmd: &ExportStateCmd,
) -> AsyncCmdResult<'_>;
fn prepare_import_blocks_cmd(
self: Box<Self>,
config: Configuration,
cmd: &ImportBlocksCmd,
) -> AsyncCmdResult<'_>;
fn prepare_revert_cmd(
self: Box<Self>,
config: Configuration,
cmd: &RevertCmd,
) -> AsyncCmdResult<'_>;
fn run_export_genesis_head_cmd(
self: Box<Self>,
config: Configuration,
cmd: &ExportGenesisHeadCommand,
) -> SyncCmdResult;
fn run_benchmark_block_cmd(
self: Box<Self>,
config: Configuration,
cmd: &BlockCmd,
) -> SyncCmdResult;
#[cfg(any(feature = "runtime-benchmarks"))]
fn run_benchmark_storage_cmd(
self: Box<Self>,
config: Configuration,
cmd: &StorageCmd,
) -> SyncCmdResult;
}
impl<T> NodeCommandRunner for T
where
T: BaseNodeSpec,
{
fn prepare_check_block_cmd(
self: Box<Self>,
config: Configuration,
cmd: &CheckBlockCmd,
) -> AsyncCmdResult<'_> {
let partial = T::new_partial(&config).map_err(pezsc_cli::Error::Service)?;
Ok((Box::pin(cmd.run(partial.client, partial.import_queue)), partial.task_manager))
}
fn prepare_export_blocks_cmd(
self: Box<Self>,
config: Configuration,
cmd: &ExportBlocksCmd,
) -> AsyncCmdResult<'_> {
let partial = T::new_partial(&config).map_err(pezsc_cli::Error::Service)?;
Ok((Box::pin(cmd.run(partial.client, config.database)), partial.task_manager))
}
fn prepare_export_state_cmd(
self: Box<Self>,
config: Configuration,
cmd: &ExportStateCmd,
) -> AsyncCmdResult<'_> {
let partial = T::new_partial(&config).map_err(pezsc_cli::Error::Service)?;
Ok((Box::pin(cmd.run(partial.client, config.chain_spec)), partial.task_manager))
}
fn prepare_import_blocks_cmd(
self: Box<Self>,
config: Configuration,
cmd: &ImportBlocksCmd,
) -> AsyncCmdResult<'_> {
let partial = T::new_partial(&config).map_err(pezsc_cli::Error::Service)?;
Ok((Box::pin(cmd.run(partial.client, partial.import_queue)), partial.task_manager))
}
fn prepare_revert_cmd(
self: Box<Self>,
config: Configuration,
cmd: &RevertCmd,
) -> AsyncCmdResult<'_> {
let partial = T::new_partial(&config).map_err(pezsc_cli::Error::Service)?;
Ok((Box::pin(cmd.run(partial.client, partial.backend, None)), partial.task_manager))
}
fn run_export_genesis_head_cmd(
self: Box<Self>,
config: Configuration,
cmd: &ExportGenesisHeadCommand,
) -> SyncCmdResult {
let partial = T::new_partial(&config).map_err(pezsc_cli::Error::Service)?;
cmd.run(partial.client)
}
fn run_benchmark_block_cmd(
self: Box<Self>,
config: Configuration,
cmd: &BlockCmd,
) -> SyncCmdResult {
let partial = T::new_partial(&config).map_err(pezsc_cli::Error::Service)?;
cmd.run(partial.client)
}
#[cfg(any(feature = "runtime-benchmarks"))]
fn run_benchmark_storage_cmd(
self: Box<Self>,
config: Configuration,
cmd: &StorageCmd,
) -> SyncCmdResult {
let partial = T::new_partial(&config).map_err(pezsc_cli::Error::Service)?;
let db = partial.backend.expose_db();
let storage = partial.backend.expose_storage();
let shared_trie_cache = partial.backend.expose_shared_trie_cache();
cmd.run(config, partial.client, db, storage, shared_trie_cache)
}
}
@@ -0,0 +1,132 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Pezcumulus teyrchain collator primitives.
#![warn(missing_docs)]
pub(crate) mod aura;
pub mod chain_spec;
pub mod command;
pub mod rpc;
pub mod runtime;
pub mod spec;
pub(crate) mod statement_store;
pub mod types;
use crate::cli::AuthoringPolicy;
use cumulus_primitives_core::{CollectCollationInfo, GetTeyrchainInfo, RelayParentOffsetApi};
use pezsc_client_db::DbHash;
use pezsc_offchain::OffchainWorkerApi;
use serde::de::DeserializeOwned;
use pezsp_api::{ApiExt, CallApiAt, ConstructRuntimeApi, Metadata};
use pezsp_block_builder::BlockBuilder;
use pezsp_runtime::{
traits::{Block as BlockT, BlockNumber, Header as HeaderT, NumberFor},
OpaqueExtrinsic,
};
use pezsp_session::SessionKeys;
use pezsp_statement_store::runtime_api::ValidateStatement;
use pezsp_transaction_pool::runtime_api::TaggedTransactionQueue;
use std::{fmt::Debug, path::PathBuf, str::FromStr};
pub trait NodeBlock:
BlockT<Extrinsic = OpaqueExtrinsic, Header = Self::BoundedHeader, Hash = DbHash> + DeserializeOwned
{
type BoundedFromStrErr: Debug;
type BoundedNumber: FromStr<Err = Self::BoundedFromStrErr> + BlockNumber;
type BoundedHeader: HeaderT<Number = Self::BoundedNumber, Hash = DbHash> + Unpin;
}
impl<T> NodeBlock for T
where
T: BlockT<Extrinsic = OpaqueExtrinsic, Hash = DbHash> + DeserializeOwned,
<T as BlockT>::Header: Unpin,
<NumberFor<T> as FromStr>::Err: Debug,
{
type BoundedFromStrErr = <NumberFor<T> as FromStr>::Err;
type BoundedNumber = NumberFor<T>;
type BoundedHeader = <T as BlockT>::Header;
}
/// Convenience trait that defines the basic bounds for the `RuntimeApi` of a teyrchain node.
pub trait NodeRuntimeApi<Block: BlockT>:
ApiExt<Block>
+ Metadata<Block>
+ SessionKeys<Block>
+ BlockBuilder<Block>
+ TaggedTransactionQueue<Block>
+ OffchainWorkerApi<Block>
+ CollectCollationInfo<Block>
+ ValidateStatement<Block>
+ GetTeyrchainInfo<Block>
+ RelayParentOffsetApi<Block>
+ Sized
{
}
impl<T, Block: BlockT> NodeRuntimeApi<Block> for T where
T: ApiExt<Block>
+ Metadata<Block>
+ SessionKeys<Block>
+ BlockBuilder<Block>
+ TaggedTransactionQueue<Block>
+ OffchainWorkerApi<Block>
+ RelayParentOffsetApi<Block>
+ CollectCollationInfo<Block>
+ ValidateStatement<Block>
+ GetTeyrchainInfo<Block>
{
}
/// Convenience trait that defines the basic bounds for the `ConstructRuntimeApi` of a teyrchain
/// node.
pub trait ConstructNodeRuntimeApi<Block: BlockT, C: CallApiAt<Block>>:
ConstructRuntimeApi<Block, C, RuntimeApi = Self::BoundedRuntimeApi> + Send + Sync + 'static
{
/// Basic bounds for the `RuntimeApi` of a teyrchain node.
type BoundedRuntimeApi: NodeRuntimeApi<Block>;
}
impl<T, Block: BlockT, C: CallApiAt<Block>> ConstructNodeRuntimeApi<Block, C> for T
where
T: ConstructRuntimeApi<Block, C> + Send + Sync + 'static,
T::RuntimeApi: NodeRuntimeApi<Block>,
{
type BoundedRuntimeApi = T::RuntimeApi;
}
/// Extra args that are passed when creating a new node spec.
pub struct NodeExtraArgs {
/// The authoring policy to use.
///
/// Can be used to influence details of block production.
pub authoring_policy: AuthoringPolicy,
/// If set, each `PoV` build by the node will be exported to this folder.
pub export_pov: Option<PathBuf>,
/// The maximum percentage of the maximum PoV size that the collator can use.
/// It will be removed once <https://github.com/pezkuwichain/pezkuwi-sdk/issues/23> is fixed.
pub max_pov_percentage: Option<u32>,
/// If true then the statement store will be enabled.
pub enable_statement_store: bool,
/// Parameters for storage monitoring.
pub storage_monitor: pezsc_storage_monitor::StorageMonitorParams,
}
@@ -0,0 +1,85 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Teyrchain-specific RPCs implementation.
#![warn(missing_docs)]
use crate::common::{
types::{AccountId, Balance, Nonce, TeyrchainBackend, TeyrchainClient},
ConstructNodeRuntimeApi,
};
use pezpallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer};
use pezsc_rpc::{
dev::{Dev, DevApiServer},
statement::{StatementApiServer, StatementStore},
};
use pezsp_runtime::traits::Block as BlockT;
use std::{marker::PhantomData, sync::Arc};
use bizinikiwi_frame_rpc_system::{System, SystemApiServer};
use bizinikiwi_state_trie_migration_rpc::{StateMigration, StateMigrationApiServer};
/// A type representing all RPC extensions.
pub type RpcExtension = jsonrpsee::RpcModule<()>;
pub(crate) trait BuildRpcExtensions<Client, Backend, Pool, StatementStore> {
fn build_rpc_extensions(
client: Arc<Client>,
backend: Arc<Backend>,
pool: Arc<Pool>,
statement_store: Option<Arc<StatementStore>>,
) -> pezsc_service::error::Result<RpcExtension>;
}
pub(crate) struct BuildTeyrchainRpcExtensions<Block, RuntimeApi>(PhantomData<(Block, RuntimeApi)>);
impl<Block: BlockT, RuntimeApi>
BuildRpcExtensions<
TeyrchainClient<Block, RuntimeApi>,
TeyrchainBackend<Block>,
pezsc_transaction_pool::TransactionPoolHandle<Block, TeyrchainClient<Block, RuntimeApi>>,
pezsc_statement_store::Store,
> for BuildTeyrchainRpcExtensions<Block, RuntimeApi>
where
RuntimeApi:
ConstructNodeRuntimeApi<Block, TeyrchainClient<Block, RuntimeApi>> + Send + Sync + 'static,
RuntimeApi::RuntimeApi: pezpallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
+ bizinikiwi_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>,
{
fn build_rpc_extensions(
client: Arc<TeyrchainClient<Block, RuntimeApi>>,
backend: Arc<TeyrchainBackend<Block>>,
pool: Arc<
pezsc_transaction_pool::TransactionPoolHandle<Block, TeyrchainClient<Block, RuntimeApi>>,
>,
statement_store: Option<Arc<pezsc_statement_store::Store>>,
) -> pezsc_service::error::Result<RpcExtension> {
let build = || -> Result<RpcExtension, Box<dyn std::error::Error + Send + Sync>> {
let mut module = RpcExtension::new(());
module.merge(System::new(client.clone(), pool).into_rpc())?;
module.merge(TransactionPayment::new(client.clone()).into_rpc())?;
module.merge(StateMigration::new(client.clone(), backend).into_rpc())?;
if let Some(statement_store) = statement_store {
module.merge(StatementStore::new(statement_store).into_rpc())?;
}
module.merge(Dev::new(client).into_rpc())?;
Ok(module)
};
build().map_err(Into::into)
}
}
@@ -0,0 +1,213 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Runtime parameters.
use codec::Decode;
use cumulus_client_service::TeyrchainHostFunctions;
use pezsc_chain_spec::ChainSpec;
use pezsc_executor::WasmExecutor;
use pezsc_runtime_utilities::fetch_latest_metadata_from_code_blob;
use scale_info::{form::PortableForm, TypeDef, TypeDefPrimitive};
use std::fmt::Display;
use subxt_metadata::{Metadata, StorageEntryType};
/// Expected teyrchain system pallet runtime type name.
pub const DEFAULT_TEYRCHAIN_SYSTEM_PALLET_NAME: &str = "TeyrchainSystem";
/// Expected frame system pallet runtime type name.
pub const DEFAULT_FRAME_SYSTEM_PALLET_NAME: &str = "System";
/// The Aura ID used by the Aura consensus
#[derive(PartialEq)]
pub enum AuraConsensusId {
/// Ed25519
Ed25519,
/// Sr25519
Sr25519,
}
/// The choice of consensus for the teyrchain omni-node.
#[derive(PartialEq)]
pub enum Consensus {
/// Aura consensus.
Aura(AuraConsensusId),
}
/// The choice of block number for the teyrchain omni-node.
#[derive(PartialEq, Debug)]
pub enum BlockNumber {
/// u32
U32,
/// u64
U64,
}
impl Display for BlockNumber {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BlockNumber::U32 => write!(f, "u32"),
BlockNumber::U64 => write!(f, "u64"),
}
}
}
impl Into<TypeDefPrimitive> for BlockNumber {
fn into(self) -> TypeDefPrimitive {
match self {
BlockNumber::U32 => TypeDefPrimitive::U32,
BlockNumber::U64 => TypeDefPrimitive::U64,
}
}
}
impl BlockNumber {
fn from_type_def(type_def: &TypeDef<PortableForm>) -> Option<BlockNumber> {
match type_def {
TypeDef::Primitive(TypeDefPrimitive::U32) => Some(BlockNumber::U32),
TypeDef::Primitive(TypeDefPrimitive::U64) => Some(BlockNumber::U64),
_ => None,
}
}
}
/// Helper enum listing the supported Runtime types
#[derive(PartialEq)]
pub enum Runtime {
/// None of the system-chain runtimes, rather the node will act agnostic to the runtime ie. be
/// an omni-node, and simply run a node with the given consensus algorithm.
Omni(BlockNumber, Consensus),
}
/// Helper trait used for extracting the Runtime variant from the chain spec ID.
pub trait RuntimeResolver {
/// Extract the Runtime variant from the chain spec ID.
fn runtime(&self, chain_spec: &dyn ChainSpec) -> pezsc_cli::Result<Runtime>;
}
/// Default implementation for `RuntimeResolver` that just returns
/// `Runtime::Omni(BlockNumber::U32, Consensus::Aura(AuraConsensusId::Sr25519))`.
pub struct DefaultRuntimeResolver;
impl RuntimeResolver for DefaultRuntimeResolver {
fn runtime(&self, chain_spec: &dyn ChainSpec) -> pezsc_cli::Result<Runtime> {
let Ok(metadata_inspector) = MetadataInspector::new(chain_spec) else {
log::info!("Unable to check metadata. Skipping metadata checks. Metadata checks are supported for metadata versions v14 and higher.");
return Ok(Runtime::Omni(BlockNumber::U32, Consensus::Aura(AuraConsensusId::Sr25519)));
};
let block_number = match metadata_inspector.block_number() {
Some(inner) => inner,
None => {
log::warn!(
r#"⚠️ There isn't a runtime type named `System`, corresponding to the `pezframe-system`
pallet (https://docs.rs/pezframe-system/latest/pezframe_system/). Please check Omni Node docs for runtime conventions:
https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html#runtime-conventions.
Note: We'll assume a block number size of `u32`."#
);
BlockNumber::U32
},
};
if !metadata_inspector.pezpallet_exists(DEFAULT_TEYRCHAIN_SYSTEM_PALLET_NAME) {
log::warn!(
r#"⚠️ The teyrchain system pallet (https://docs.rs/crate/pezcumulus-pezpallet-parachain-system/latest) is
missing from the runtimes metadata. Please check Omni Node docs for runtime conventions:
https://docs.pezkuwichain.io/sdk/master/polkadot_sdk_docs/reference_docs/omni_node/index.html#runtime-conventions."#
);
}
Ok(Runtime::Omni(block_number, Consensus::Aura(AuraConsensusId::Sr25519)))
}
}
struct MetadataInspector(Metadata);
impl MetadataInspector {
fn new(chain_spec: &dyn ChainSpec) -> Result<MetadataInspector, pezsc_cli::Error> {
MetadataInspector::fetch_metadata(chain_spec).map(MetadataInspector)
}
fn pezpallet_exists(&self, name: &str) -> bool {
self.0.pezpallet_by_name(name).is_some()
}
fn block_number(&self) -> Option<BlockNumber> {
let pezpallet_metadata = self.0.pezpallet_by_name(DEFAULT_FRAME_SYSTEM_PALLET_NAME);
pezpallet_metadata
.and_then(|inner| inner.storage())
.and_then(|inner| inner.entry_by_name("Number"))
.and_then(|number_ty| match number_ty.entry_type() {
StorageEntryType::Plain(ty_id) => Some(ty_id),
_ => None,
})
.and_then(|ty_id| self.0.types().resolve(*ty_id))
.and_then(|portable_type| BlockNumber::from_type_def(&portable_type.type_def))
}
fn fetch_metadata(chain_spec: &dyn ChainSpec) -> Result<Metadata, pezsc_cli::Error> {
let mut storage = chain_spec.build_storage()?;
let code_bytes = storage
.top
.remove(pezsp_storage::well_known_keys::CODE)
.ok_or("chain spec genesis does not contain code")?;
let opaque_metadata = fetch_latest_metadata_from_code_blob(
&WasmExecutor::<TeyrchainHostFunctions>::builder()
.with_allow_missing_host_functions(true)
.build(),
pezsp_runtime::Cow::Borrowed(code_bytes.as_slice()),
)
.map_err(|err| err.to_string())?;
Metadata::decode(&mut (*opaque_metadata).as_slice()).map_err(Into::into)
}
}
#[cfg(test)]
mod tests {
use crate::runtime::{
BlockNumber, MetadataInspector, DEFAULT_FRAME_SYSTEM_PALLET_NAME,
DEFAULT_TEYRCHAIN_SYSTEM_PALLET_NAME,
};
use codec::Decode;
use cumulus_client_service::TeyrchainHostFunctions;
use pezsc_executor::WasmExecutor;
use pezsc_runtime_utilities::fetch_latest_metadata_from_code_blob;
fn cumulus_test_runtime_metadata() -> subxt_metadata::Metadata {
let opaque_metadata = fetch_latest_metadata_from_code_blob(
&WasmExecutor::<TeyrchainHostFunctions>::builder()
.with_allow_missing_host_functions(true)
.build(),
pezsp_runtime::Cow::Borrowed(cumulus_test_runtime::WASM_BINARY.unwrap()),
)
.unwrap();
subxt_metadata::Metadata::decode(&mut (*opaque_metadata).as_slice()).unwrap()
}
#[test]
fn test_pallet_exists() {
let metadata_inspector = MetadataInspector(cumulus_test_runtime_metadata());
assert!(metadata_inspector.pezpallet_exists(DEFAULT_TEYRCHAIN_SYSTEM_PALLET_NAME));
assert!(metadata_inspector.pezpallet_exists(DEFAULT_FRAME_SYSTEM_PALLET_NAME));
}
#[test]
fn test_runtime_block_number() {
let metadata_inspector = MetadataInspector(cumulus_test_runtime_metadata());
assert_eq!(metadata_inspector.block_number().unwrap(), BlockNumber::U32);
}
}
@@ -0,0 +1,634 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
chain_spec::Extensions,
cli::DevSealMode,
common::{
command::NodeCommandRunner,
rpc::BuildRpcExtensions,
statement_store::{build_statement_store, new_statement_handler_proto},
types::{
TeyrchainBackend, TeyrchainBlockImport, TeyrchainClient, TeyrchainHostFunctions,
TeyrchainService,
},
ConstructNodeRuntimeApi, NodeBlock, NodeExtraArgs,
},
};
use codec::Encode;
use cumulus_client_bootnodes::{start_bootnode_tasks, StartBootnodeTasksParams};
use cumulus_client_cli::CollatorOptions;
use cumulus_client_service::{
build_network, build_relay_chain_interface, prepare_node_config, start_relay_chain_tasks,
BuildNetworkParams, CollatorSybilResistance, DARecoveryProfile, StartRelayChainTasksParams,
TeyrchainTracingExecuteBlock,
};
use cumulus_primitives_core::{BlockT, GetTeyrchainInfo, ParaId};
use cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface};
use futures::FutureExt;
use log::info;
use pezkuwi_primitives::CollatorPair;
use prometheus_endpoint::Registry;
use pezsc_client_api::Backend;
use pezsc_consensus::DefaultImportQueue;
use pezsc_executor::{HeapAllocStrategy, DEFAULT_HEAP_ALLOC_STRATEGY};
use pezsc_network::{
config::FullNetworkConfiguration, NetworkBackend, NetworkBlock, NetworkStateInfo, PeerId,
};
use pezsc_service::{Configuration, ImportQueue, PartialComponents, TaskManager};
use pezsc_statement_store::Store;
use pezsc_sysinfo::HwBench;
use pezsc_telemetry::{TelemetryHandle, TelemetryWorker};
use pezsc_tracing::tracing::Instrument;
use pezsc_transaction_pool::TransactionPoolHandle;
use pezsc_transaction_pool_api::OffchainTransactionPoolFactory;
use pezsp_api::{ApiExt, ProvideRuntimeApi};
use pezsp_keystore::KeystorePtr;
use pezsp_runtime::traits::AccountIdConversion;
use std::{future::Future, pin::Pin, sync::Arc, time::Duration};
use teyrchains_common::Hash;
pub(crate) trait BuildImportQueue<
Block: BlockT,
RuntimeApi,
BlockImport: pezsc_consensus::BlockImport<Block>,
>
{
fn build_import_queue(
client: Arc<TeyrchainClient<Block, RuntimeApi>>,
block_import: TeyrchainBlockImport<Block, BlockImport>,
config: &Configuration,
telemetry_handle: Option<TelemetryHandle>,
task_manager: &TaskManager,
) -> pezsc_service::error::Result<DefaultImportQueue<Block>>;
}
pub(crate) trait StartConsensus<Block: BlockT, RuntimeApi, BI, BIAuxiliaryData>
where
RuntimeApi: ConstructNodeRuntimeApi<Block, TeyrchainClient<Block, RuntimeApi>>,
{
fn start_consensus(
client: Arc<TeyrchainClient<Block, RuntimeApi>>,
block_import: TeyrchainBlockImport<Block, BI>,
prometheus_registry: Option<&Registry>,
telemetry: Option<TelemetryHandle>,
task_manager: &TaskManager,
relay_chain_interface: Arc<dyn RelayChainInterface>,
transaction_pool: Arc<TransactionPoolHandle<Block, TeyrchainClient<Block, RuntimeApi>>>,
keystore: KeystorePtr,
relay_chain_slot_duration: Duration,
para_id: ParaId,
collator_key: CollatorPair,
collator_peer_id: PeerId,
overseer_handle: OverseerHandle,
announce_block: Arc<dyn Fn(Hash, Option<Vec<u8>>) + Send + Sync>,
backend: Arc<TeyrchainBackend<Block>>,
node_extra_args: NodeExtraArgs,
block_import_extra_return_value: BIAuxiliaryData,
) -> Result<(), pezsc_service::Error>;
}
/// Checks that the hardware meets the requirements and print a warning otherwise.
fn warn_if_slow_hardware(hwbench: &pezsc_sysinfo::HwBench) {
// Pezkuwi para-chains should generally use these requirements to ensure that the relay-chain
// will not take longer than expected to import its blocks.
if let Err(err) =
pezframe_benchmarking_cli::BIZINIKIWI_REFERENCE_HARDWARE.check_hardware(hwbench, false)
{
log::warn!(
"⚠️ The hardware does not meet the minimal requirements {} for role 'Authority' find out more at:\n\
https://wiki.network.pezkuwichain.io/docs/maintain-guides-how-to-validate-polkadot#reference-hardware",
err
);
}
}
pub(crate) trait InitBlockImport<Block: BlockT, RuntimeApi> {
type BlockImport: pezsc_consensus::BlockImport<Block> + Clone + Send + Sync;
type BlockImportAuxiliaryData;
fn init_block_import(
client: Arc<TeyrchainClient<Block, RuntimeApi>>,
) -> pezsc_service::error::Result<(Self::BlockImport, Self::BlockImportAuxiliaryData)>;
}
pub(crate) struct ClientBlockImport;
impl<Block: BlockT, RuntimeApi> InitBlockImport<Block, RuntimeApi> for ClientBlockImport
where
RuntimeApi: Send + ConstructNodeRuntimeApi<Block, TeyrchainClient<Block, RuntimeApi>>,
{
type BlockImport = Arc<TeyrchainClient<Block, RuntimeApi>>;
type BlockImportAuxiliaryData = ();
fn init_block_import(
client: Arc<TeyrchainClient<Block, RuntimeApi>>,
) -> pezsc_service::error::Result<(Self::BlockImport, Self::BlockImportAuxiliaryData)> {
Ok((client.clone(), ()))
}
}
pub(crate) trait BaseNodeSpec {
type Block: NodeBlock;
type RuntimeApi: ConstructNodeRuntimeApi<
Self::Block,
TeyrchainClient<Self::Block, Self::RuntimeApi>,
>;
type BuildImportQueue: BuildImportQueue<
Self::Block,
Self::RuntimeApi,
<Self::InitBlockImport as InitBlockImport<Self::Block, Self::RuntimeApi>>::BlockImport,
>;
type InitBlockImport: self::InitBlockImport<Self::Block, Self::RuntimeApi>;
/// Retrieves teyrchain id.
fn teyrchain_id(
client: &TeyrchainClient<Self::Block, Self::RuntimeApi>,
teyrchain_config: &Configuration,
) -> Option<ParaId> {
let best_hash = client.chain_info().best_hash;
let para_id = if client
.runtime_api()
.has_api::<dyn GetTeyrchainInfo<Self::Block>>(best_hash)
.ok()
.filter(|has_api| *has_api)
.is_some()
{
client
.runtime_api()
.teyrchain_id(best_hash)
.inspect_err(|err| {
log::error!(
"`cumulus_primitives_core::GetTeyrchainInfo` runtime API call errored with {}",
err
);
})
.ok()?
} else {
ParaId::from(
Extensions::try_get(&*teyrchain_config.chain_spec).and_then(|ext| ext.para_id())?,
)
};
let teyrchain_account =
AccountIdConversion::<pezkuwi_primitives::AccountId>::into_account_truncating(&para_id);
info!("🪪 Teyrchain id: {:?}", para_id);
info!("🧾 Teyrchain Account: {}", teyrchain_account);
Some(para_id)
}
/// Starts a `ServiceBuilder` for a full service.
///
/// Use this macro if you don't actually need the full service, but just the builder in order to
/// be able to perform chain operations.
fn new_partial(
config: &Configuration,
) -> pezsc_service::error::Result<
TeyrchainService<
Self::Block,
Self::RuntimeApi,
<Self::InitBlockImport as InitBlockImport<Self::Block, Self::RuntimeApi>>::BlockImport,
<Self::InitBlockImport as InitBlockImport<Self::Block, Self::RuntimeApi>>::BlockImportAuxiliaryData
>
>{
let telemetry = config
.telemetry_endpoints
.clone()
.filter(|x| !x.is_empty())
.map(|endpoints| -> Result<_, pezsc_telemetry::Error> {
let worker = TelemetryWorker::new(16)?;
let telemetry = worker.handle().new_telemetry(endpoints);
Ok((worker, telemetry))
})
.transpose()?;
let heap_pages =
config.executor.default_heap_pages.map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| {
HeapAllocStrategy::Static { extra_pages: h as _ }
});
let executor = pezsc_executor::WasmExecutor::<TeyrchainHostFunctions>::builder()
.with_execution_method(config.executor.wasm_method)
.with_max_runtime_instances(config.executor.max_runtime_instances)
.with_runtime_cache_size(config.executor.runtime_cache_size)
.with_onchain_heap_alloc_strategy(heap_pages)
.with_offchain_heap_alloc_strategy(heap_pages)
.build();
let (client, backend, keystore_container, task_manager) =
pezsc_service::new_full_parts_record_import::<Self::Block, Self::RuntimeApi, _>(
config,
telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
executor,
true,
)?;
let client = Arc::new(client);
let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle());
let telemetry = telemetry.map(|(worker, telemetry)| {
task_manager.spawn_handle().spawn("telemetry", None, worker.run());
telemetry
});
let transaction_pool = Arc::from(
pezsc_transaction_pool::Builder::new(
task_manager.spawn_essential_handle(),
client.clone(),
config.role.is_authority().into(),
)
.with_options(config.transaction_pool.clone())
.with_prometheus(config.prometheus_registry())
.build(),
);
let (block_import, block_import_auxiliary_data) =
Self::InitBlockImport::init_block_import(client.clone())?;
let block_import = TeyrchainBlockImport::new(block_import, backend.clone());
let import_queue = Self::BuildImportQueue::build_import_queue(
client.clone(),
block_import.clone(),
config,
telemetry.as_ref().map(|telemetry| telemetry.handle()),
&task_manager,
)?;
Ok(PartialComponents {
backend,
client,
import_queue,
keystore_container,
task_manager,
transaction_pool,
select_chain: (),
other: (block_import, telemetry, telemetry_worker_handle, block_import_auxiliary_data),
})
}
}
pub(crate) trait NodeSpec: BaseNodeSpec {
type BuildRpcExtensions: BuildRpcExtensions<
TeyrchainClient<Self::Block, Self::RuntimeApi>,
TeyrchainBackend<Self::Block>,
TransactionPoolHandle<Self::Block, TeyrchainClient<Self::Block, Self::RuntimeApi>>,
Store,
>;
type StartConsensus: StartConsensus<
Self::Block,
Self::RuntimeApi,
<Self::InitBlockImport as InitBlockImport<Self::Block, Self::RuntimeApi>>::BlockImport,
<Self::InitBlockImport as InitBlockImport<Self::Block, Self::RuntimeApi>>::BlockImportAuxiliaryData,
>;
const SYBIL_RESISTANCE: CollatorSybilResistance;
fn start_dev_node(
_config: Configuration,
_mode: DevSealMode,
) -> pezsc_service::error::Result<TaskManager> {
Err(pezsc_service::Error::Other("Dev not supported for this node type".into()))
}
/// Start a node with the given teyrchain spec.
///
/// This is the actual implementation that is abstract over the executor and the runtime api.
fn start_node<Net>(
teyrchain_config: Configuration,
pezkuwi_config: Configuration,
collator_options: CollatorOptions,
hwbench: Option<pezsc_sysinfo::HwBench>,
node_extra_args: NodeExtraArgs,
) -> Pin<Box<dyn Future<Output = pezsc_service::error::Result<TaskManager>>>>
where
Net: NetworkBackend<Self::Block, Hash>,
{
let fut = async move {
let teyrchain_config = prepare_node_config(teyrchain_config);
let teyrchain_public_addresses = teyrchain_config.network.public_addresses.clone();
let teyrchain_fork_id = teyrchain_config.chain_spec.fork_id().map(ToString::to_string);
let advertise_non_global_ips = teyrchain_config.network.allow_non_globals_in_dht;
let params = Self::new_partial(&teyrchain_config)?;
let (block_import, mut telemetry, telemetry_worker_handle, block_import_auxiliary_data) =
params.other;
let client = params.client.clone();
let backend = params.backend.clone();
let mut task_manager = params.task_manager;
// Resolve teyrchain id based on runtime, or based on chain spec.
let para_id = Self::teyrchain_id(&client, &teyrchain_config)
.ok_or("Failed to retrieve the teyrchain id")?;
let relay_chain_fork_id = pezkuwi_config.chain_spec.fork_id().map(ToString::to_string);
let (relay_chain_interface, collator_key, relay_chain_network, paranode_rx) =
build_relay_chain_interface(
pezkuwi_config,
&teyrchain_config,
telemetry_worker_handle,
&mut task_manager,
collator_options.clone(),
hwbench.clone(),
)
.await
.map_err(|e| pezsc_service::Error::Application(Box::new(e)))?;
let validator = teyrchain_config.role.is_authority();
let prometheus_registry = teyrchain_config.prometheus_registry().cloned();
let transaction_pool = params.transaction_pool.clone();
let import_queue_service = params.import_queue.service();
let mut net_config = FullNetworkConfiguration::<_, _, Net>::new(
&teyrchain_config.network,
prometheus_registry.clone(),
);
let metrics = Net::register_notification_metrics(
teyrchain_config.prometheus_config.as_ref().map(|config| &config.registry),
);
let statement_handler_proto = node_extra_args.enable_statement_store.then(|| {
new_statement_handler_proto(&*client, &teyrchain_config, &metrics, &mut net_config)
});
let (network, system_rpc_tx, tx_handler_controller, sync_service) =
build_network(BuildNetworkParams {
teyrchain_config: &teyrchain_config,
net_config,
client: client.clone(),
transaction_pool: transaction_pool.clone(),
para_id,
spawn_handle: task_manager.spawn_handle(),
relay_chain_interface: relay_chain_interface.clone(),
import_queue: params.import_queue,
sybil_resistance_level: Self::SYBIL_RESISTANCE,
metrics,
})
.await?;
let peer_id = network.local_peer_id();
let statement_store = statement_handler_proto
.map(|statement_handler_proto| {
build_statement_store(
&teyrchain_config,
&mut task_manager,
client.clone(),
network.clone(),
sync_service.clone(),
params.keystore_container.local_keystore(),
statement_handler_proto,
)
})
.transpose()?;
if teyrchain_config.offchain_worker.enabled {
let custom_extensions = {
let statement_store = statement_store.clone();
move |_hash| {
if let Some(statement_store) = &statement_store {
vec![Box::new(statement_store.clone().as_statement_store_ext())
as Box<_>]
} else {
vec![]
}
}
};
let offchain_workers =
pezsc_offchain::OffchainWorkers::new(pezsc_offchain::OffchainWorkerOptions {
runtime_api_provider: client.clone(),
keystore: Some(params.keystore_container.keystore()),
offchain_db: backend.offchain_storage(),
transaction_pool: Some(OffchainTransactionPoolFactory::new(
transaction_pool.clone(),
)),
network_provider: Arc::new(network.clone()),
is_validator: teyrchain_config.role.is_authority(),
enable_http_requests: true,
custom_extensions,
})?;
task_manager.spawn_handle().spawn(
"offchain-workers-runner",
"offchain-work",
offchain_workers.run(client.clone(), task_manager.spawn_handle()).boxed(),
);
}
let rpc_builder = {
let client = client.clone();
let transaction_pool = transaction_pool.clone();
let backend_for_rpc = backend.clone();
let statement_store = statement_store.clone();
Box::new(move |_| {
Self::BuildRpcExtensions::build_rpc_extensions(
client.clone(),
backend_for_rpc.clone(),
transaction_pool.clone(),
statement_store.clone(),
)
})
};
let database_path = teyrchain_config.database.path().map(|p| p.to_path_buf());
pezsc_service::spawn_tasks(pezsc_service::SpawnTasksParams {
rpc_builder,
client: client.clone(),
transaction_pool: transaction_pool.clone(),
task_manager: &mut task_manager,
config: teyrchain_config,
keystore: params.keystore_container.keystore(),
backend: backend.clone(),
network: network.clone(),
sync_service: sync_service.clone(),
system_rpc_tx,
tx_handler_controller,
telemetry: telemetry.as_mut(),
tracing_execute_block: Some(Arc::new(TeyrchainTracingExecuteBlock::new(
client.clone(),
))),
})?;
// Spawn the storage monitor
if let Some(database_path) = database_path {
pezsc_storage_monitor::StorageMonitorService::try_spawn(
node_extra_args.storage_monitor.clone(),
database_path,
&task_manager.spawn_essential_handle(),
)
.map_err(|e| pezsc_service::Error::Application(Box::new(e) as Box<_>))?;
}
if let Some(hwbench) = hwbench {
pezsc_sysinfo::print_hwbench(&hwbench);
if validator {
warn_if_slow_hardware(&hwbench);
}
if let Some(ref mut telemetry) = telemetry {
let telemetry_handle = telemetry.handle();
task_manager.spawn_handle().spawn(
"telemetry_hwbench",
None,
pezsc_sysinfo::initialize_hwbench_telemetry(telemetry_handle, hwbench),
);
}
}
let announce_block = {
let sync_service = sync_service.clone();
Arc::new(move |hash, data| sync_service.announce_block(hash, data))
};
let relay_chain_slot_duration = Duration::from_secs(6);
let overseer_handle = relay_chain_interface
.overseer_handle()
.map_err(|e| pezsc_service::Error::Application(Box::new(e)))?;
start_relay_chain_tasks(StartRelayChainTasksParams {
client: client.clone(),
announce_block: announce_block.clone(),
para_id,
relay_chain_interface: relay_chain_interface.clone(),
task_manager: &mut task_manager,
da_recovery_profile: if validator {
DARecoveryProfile::Collator
} else {
DARecoveryProfile::FullNode
},
import_queue: import_queue_service,
relay_chain_slot_duration,
recovery_handle: Box::new(overseer_handle.clone()),
sync_service,
prometheus_registry: prometheus_registry.as_ref(),
})?;
start_bootnode_tasks(StartBootnodeTasksParams {
embedded_dht_bootnode: collator_options.embedded_dht_bootnode,
dht_bootnode_discovery: collator_options.dht_bootnode_discovery,
para_id,
task_manager: &mut task_manager,
relay_chain_interface: relay_chain_interface.clone(),
relay_chain_fork_id,
relay_chain_network,
request_receiver: paranode_rx,
teyrchain_network: network,
advertise_non_global_ips,
teyrchain_genesis_hash: client.chain_info().genesis_hash.encode(),
teyrchain_fork_id,
teyrchain_public_addresses,
});
if validator {
Self::StartConsensus::start_consensus(
client.clone(),
block_import,
prometheus_registry.as_ref(),
telemetry.as_ref().map(|t| t.handle()),
&task_manager,
relay_chain_interface.clone(),
transaction_pool,
params.keystore_container.keystore(),
relay_chain_slot_duration,
para_id,
collator_key.expect("Command line arguments do not allow this. qed"),
peer_id,
overseer_handle,
announce_block,
backend.clone(),
node_extra_args,
block_import_auxiliary_data,
)?;
}
Ok(task_manager)
};
Box::pin(Instrument::instrument(
fut,
pezsc_tracing::tracing::info_span!(
pezsc_tracing::logging::PREFIX_LOG_SPAN,
name = "Teyrchain"
),
))
}
}
pub(crate) trait DynNodeSpec: NodeCommandRunner {
/// Start node with manual or instant seal consensus.
fn start_dev_node(
self: Box<Self>,
config: Configuration,
mode: DevSealMode,
) -> pezsc_service::error::Result<TaskManager>;
/// Start the node.
fn start_node(
self: Box<Self>,
teyrchain_config: Configuration,
pezkuwi_config: Configuration,
collator_options: CollatorOptions,
hwbench: Option<HwBench>,
node_extra_args: NodeExtraArgs,
) -> Pin<Box<dyn Future<Output = pezsc_service::error::Result<TaskManager>>>>;
}
impl<T> DynNodeSpec for T
where
T: NodeSpec + NodeCommandRunner,
{
fn start_dev_node(
self: Box<Self>,
config: Configuration,
mode: DevSealMode,
) -> pezsc_service::error::Result<TaskManager> {
<Self as NodeSpec>::start_dev_node(config, mode)
}
fn start_node(
self: Box<Self>,
teyrchain_config: Configuration,
pezkuwi_config: Configuration,
collator_options: CollatorOptions,
hwbench: Option<HwBench>,
node_extra_args: NodeExtraArgs,
) -> Pin<Box<dyn Future<Output = pezsc_service::error::Result<TaskManager>>>> {
match teyrchain_config.network.network_backend {
pezsc_network::config::NetworkBackendType::Libp2p =>
<Self as NodeSpec>::start_node::<pezsc_network::NetworkWorker<_, _>>(
teyrchain_config,
pezkuwi_config,
collator_options,
hwbench,
node_extra_args,
),
pezsc_network::config::NetworkBackendType::Litep2p =>
<Self as NodeSpec>::start_node::<pezsc_network::Litep2pNetworkBackend>(
teyrchain_config,
pezkuwi_config,
collator_options,
hwbench,
node_extra_args,
),
}
}
}
@@ -0,0 +1,96 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::common::{types::TeyrchainClient, ConstructNodeRuntimeApi, NodeBlock};
use pezsc_network::{
config::FullNetworkConfiguration, service::traits::NetworkService, NetworkBackend,
};
use pezsc_service::{Configuration, TaskManager};
use pezsc_statement_store::Store;
use std::sync::Arc;
use teyrchains_common::Hash;
/// Helper function to setup the statement store in `NodeSpec::start_node`.
///
/// Functions are tailored for internal usage, types are unnecessary opinionated for usage in
/// `NodeSpec::start_node`.
/// Build the statement handler prototype. Register the notification protocol in the network
/// configuration.
pub(crate) fn new_statement_handler_proto<
Block: NodeBlock,
RuntimeApi,
Net: NetworkBackend<Block, Hash>,
>(
client: &TeyrchainClient<Block, RuntimeApi>,
teyrchain_config: &Configuration,
metrics: &pezsc_network::NotificationMetrics,
net_config: &mut FullNetworkConfiguration<Block, Hash, Net>,
) -> pezsc_network_statement::StatementHandlerPrototype {
let (statement_handler_proto, statement_config) =
pezsc_network_statement::StatementHandlerPrototype::new::<_, _, Net>(
client.chain_info().genesis_hash,
teyrchain_config.chain_spec.fork_id(),
metrics.clone(),
Arc::clone(&net_config.peer_store_handle()),
);
net_config.add_notification_protocol(statement_config);
statement_handler_proto
}
/// Build the statement store, spawn the tasks.
pub(crate) fn build_statement_store<
Block: NodeBlock,
RuntimeApi: ConstructNodeRuntimeApi<Block, TeyrchainClient<Block, RuntimeApi>>,
>(
teyrchain_config: &Configuration,
task_manager: &mut TaskManager,
client: Arc<TeyrchainClient<Block, RuntimeApi>>,
network: Arc<dyn NetworkService + 'static>,
sync_service: Arc<pezsc_network_sync::service::syncing_service::SyncingService<Block>>,
local_keystore: Arc<pezsc_keystore::LocalKeystore>,
statement_handler_proto: pezsc_network_statement::StatementHandlerPrototype,
) -> pezsc_service::error::Result<Arc<Store>> {
let statement_store = pezsc_statement_store::Store::new_shared(
&teyrchain_config.data_path,
Default::default(),
client,
local_keystore,
teyrchain_config.prometheus_registry(),
&task_manager.spawn_handle(),
)
.map_err(|e| pezsc_service::Error::Application(Box::new(e) as Box<_>))?;
let statement_protocol_executor = {
let spawn_handle = task_manager.spawn_handle();
Box::new(move |fut| {
spawn_handle.spawn("network-statement-validator", Some("networking"), fut);
})
};
let statement_handler = statement_handler_proto.build(
network,
sync_service,
statement_store.clone(),
teyrchain_config.prometheus_registry(),
statement_protocol_executor,
)?;
task_manager.spawn_handle().spawn(
"network-statement-handler",
Some("networking"),
statement_handler.run(),
);
Ok(statement_store)
}
@@ -0,0 +1,64 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use cumulus_client_consensus_common::TeyrchainBlockImport as TTeyrchainBlockImport;
use cumulus_primitives_core::relay_chain::UncheckedExtrinsic;
use pezsc_consensus::DefaultImportQueue;
use pezsc_executor::WasmExecutor;
use pezsc_service::{PartialComponents, TFullBackend, TFullClient};
use pezsc_telemetry::{Telemetry, TelemetryWorkerHandle};
use pezsc_transaction_pool::TransactionPoolHandle;
use pezsp_runtime::{generic, traits::BlakeTwo256};
pub use teyrchains_common::{AccountId, Balance, Hash, Nonce};
type Header<BlockNumber> = generic::Header<BlockNumber, BlakeTwo256>;
pub type Block<BlockNumber> = generic::Block<Header<BlockNumber>, UncheckedExtrinsic>;
#[cfg(not(feature = "runtime-benchmarks"))]
pub type TeyrchainHostFunctions = (
cumulus_client_service::TeyrchainHostFunctions,
pezsp_statement_store::runtime_api::HostFunctions,
);
#[cfg(feature = "runtime-benchmarks")]
pub type TeyrchainHostFunctions = (
cumulus_client_service::TeyrchainHostFunctions,
pezsp_statement_store::runtime_api::HostFunctions,
pezframe_benchmarking::benchmarking::HostFunctions,
);
pub type TeyrchainClient<Block, RuntimeApi> =
TFullClient<Block, RuntimeApi, WasmExecutor<TeyrchainHostFunctions>>;
pub type TeyrchainBackend<Block> = TFullBackend<Block>;
pub type TeyrchainBlockImport<Block, BI> =
TTeyrchainBlockImport<Block, BI, TeyrchainBackend<Block>>;
/// Assembly of PartialComponents (enough to run chain ops subcommands)
pub type TeyrchainService<Block, RuntimeApi, BI, BIExtraReturnValue> = PartialComponents<
TeyrchainClient<Block, RuntimeApi>,
TeyrchainBackend<Block>,
(),
DefaultImportQueue<Block>,
TransactionPoolHandle<Block, TeyrchainClient<Block, RuntimeApi>>,
(
TeyrchainBlockImport<Block, BI>,
Option<Telemetry>,
Option<TelemetryWorkerHandle>,
BIExtraReturnValue,
),
>;
@@ -0,0 +1,146 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Optional / additional CLI options for binaries built with
//! `pezkuwiomninodelib`.
/// * Binaries that need extra utilities (e.g. `export-chain-spec`) should pass
/// [`DefaultExtraSubcommands`] to `run_with_custom_cli`, which injects that one command.
/// * Binaries that should stay minimal pass [`NoExtraSubcommand`], which requests no extras at
/// all.
use clap::Subcommand;
use pezsc_cli::{ExportChainSpecCmd, Result};
use crate::RunConfig;
/// A trait for injecting and handling additional CLI subcommands in a composable way.
///
/// This trait allows downstream crates using `pezkuwi-omni-node-lib` to plug in their own custom
/// subcommands without having to modify the main CLI definition. This is especially useful for
/// teyrchain node binaries that want to define optional utilities.
///
/// ## Implementing a Custom Extra Command
///
/// To create your own subcommand:
///
/// 1. Define the subcommand using [`clap::Subcommand`].
/// 2. Implement this trait for it.
/// 3. Use it when running the node via `run_with_custom_cli::<CliConfig,
/// YourExtraCommand>(run_config)`
///
/// ### Minimal Example:
///
///
/// use clap::Parser;
/// use pezkuwi_omni_node_lib::{ExtraSubcommand, RunConfig};
///
/// #[derive(Debug, Clone, Subcommand)]
/// pub enum ExtraCmd {
/// NewCmd,
/// }
///
/// impl ExtraSubcommand for ExtraCmd {
///
/// fn handle(_cmd: ExtraCmd, _config: &RunConfig) -> pezsc_cli::Result<()> {
/// println!("Hello from Extra!");
/// Ok(())
/// }
/// }
///
///
/// To use this in a binary:
///
///
/// let config = RunConfig::new(...);
/// run_with_custom_cli::<CliConfig, ExtraCmd>(config)?;
///
///
/// Running it:
///
/// ```bash
/// $ your-binary new-cmd
/// Hello from Extra!
/// ```
///
/// ## Supporting Multiple Subcommands
///
/// You can compose multiple extra commands via an enum. Just derive [`clap::Subcommand`] and match
/// over the variants in `handle`.
///
///
/// #[derive(Debug, clap::Parser)]
/// pub enum MyExtras {
/// Foo(FooCmd),
/// Bar(BarCmd),
/// }
///
/// impl ExtraSubcommand for MyExtras {
///
/// fn handle(cmd: Self, config: &RunConfig) -> pezsc_cli::Result<()> {
/// match cmd {
/// MyExtras::Foo(foo) => { ... }
/// MyExtras::Bar(bar) => { ... }
/// }
/// Ok(())
/// }
/// }
/// Trait implemented by a set of optional subcommands**.
pub trait ExtraSubcommand: Subcommand {
/// Handle the command once it's been parsed.
fn handle(self, cfg: &RunConfig) -> Result<()>;
}
/// Built-in extra subcommands provided by `pezkuwi-omni-node-lib`.
///
/// Currently, it includes:
/// - `export-chain-spec`
///
/// You can use this by passing [`DefaultExtraSubcommands`] to `run_with_custom_cli`
/// or by just calling `run` as this is the default.
///
/// This enables default support for utilities like:
/// ```bash
/// $ your-binary export-chain-spec --chain westmint
/// ```
#[derive(Debug, Subcommand)]
pub enum DefaultExtraSubcommands {
/// Export the chain spec to JSON.
ExportChainSpec(ExportChainSpecCmd),
}
impl ExtraSubcommand for DefaultExtraSubcommands {
fn handle(self, cfg: &RunConfig) -> Result<()> {
match self {
DefaultExtraSubcommands::ExportChainSpec(cmd) => {
let spec = cfg.chain_spec_loader.load_spec(&cmd.chain)?;
cmd.run(spec)?;
},
}
Ok(())
}
}
/// No-op subcommand handler. Use this when a binary does not expose any extra subcommands.
///
/// You can use this by passing [`NoExtraSubcommand`] to `run_with_custom_cli`.
#[derive(Debug, Subcommand)]
pub enum NoExtraSubcommand {}
impl ExtraSubcommand for NoExtraSubcommand {
fn handle(self, _cfg: &RunConfig) -> Result<()> {
Ok(())
}
}
@@ -0,0 +1,39 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! In an ideal world this would be one runtime which would simplify the code massively.
//! This is not an ideal world - Pezkuwi Asset Hub has a different key type.
mod utils;
use utils::{impl_node_runtime_apis, imports::*};
#[allow(dead_code)]
type CustomBlock = crate::common::types::Block<u32>;
pub mod aura_sr25519 {
use super::*;
#[allow(dead_code)]
struct FakeRuntime;
impl_node_runtime_apis!(FakeRuntime, CustomBlock, pezsp_consensus_aura::sr25519::AuthorityId);
}
pub mod aura_ed25519 {
use super::*;
#[allow(dead_code)]
struct FakeRuntime;
impl_node_runtime_apis!(FakeRuntime, CustomBlock, pezsp_consensus_aura::ed25519::AuthorityId);
}
@@ -0,0 +1,254 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pub(crate) mod imports {
pub use cumulus_primitives_core::ParaId;
pub use pezsp_core::{crypto::KeyTypeId, OpaqueMetadata};
pub use pezsp_runtime::{
traits::Block as BlockT,
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult,
};
pub use pezsp_weights::Weight;
pub use teyrchains_common::{AccountId, Balance, Nonce};
}
macro_rules! impl_node_runtime_apis {
($runtime: ty, $block: tt, $aura_id: ty) => {
pezsp_api::impl_runtime_apis! {
impl pezsp_api::Core<$block> for $runtime {
fn version() -> pezsp_version::RuntimeVersion {
unimplemented!()
}
fn execute_block(_: <$block as BlockT>::LazyBlock) {
unimplemented!()
}
fn initialize_block(
_: &<$block as BlockT>::Header
) -> pezsp_runtime::ExtrinsicInclusionMode {
unimplemented!()
}
}
impl pezsp_api::Metadata<$block> for $runtime {
fn metadata() -> OpaqueMetadata {
unimplemented!()
}
fn metadata_at_version(_: u32) -> Option<OpaqueMetadata> {
unimplemented!()
}
fn metadata_versions() -> Vec<u32> {
unimplemented!()
}
}
impl cumulus_primitives_core::RelayParentOffsetApi<$block> for $runtime {
fn relay_parent_offset() -> u32 {
unimplemented!()
}
}
impl pezsp_consensus_aura::AuraApi<$block, $aura_id> for $runtime {
fn slot_duration() -> pezsp_consensus_aura::SlotDuration {
unimplemented!()
}
fn authorities() -> Vec<$aura_id> {
unimplemented!()
}
}
impl cumulus_primitives_aura::AuraUnincludedSegmentApi<$block> for $runtime {
fn can_build_upon(
_: <$block as BlockT>::Hash,
_: cumulus_primitives_aura::Slot,
) -> bool {
unimplemented!()
}
}
impl pezsp_block_builder::BlockBuilder<$block> for $runtime {
fn apply_extrinsic(_: <$block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {
unimplemented!()
}
fn finalize_block() -> <$block as BlockT>::Header {
unimplemented!()
}
fn inherent_extrinsics(
_: pezsp_inherents::InherentData
) -> Vec<<$block as BlockT>::Extrinsic> {
unimplemented!()
}
fn check_inherents(
_: <$block as BlockT>::LazyBlock,
_: pezsp_inherents::InherentData
) -> pezsp_inherents::CheckInherentsResult {
unimplemented!()
}
}
impl pezsp_transaction_pool::runtime_api::TaggedTransactionQueue<$block> for $runtime {
fn validate_transaction(
_: TransactionSource,
_: <$block as BlockT>::Extrinsic,
_: <$block as BlockT>::Hash,
) -> TransactionValidity {
unimplemented!()
}
}
impl pezsp_offchain::OffchainWorkerApi<$block> for $runtime {
fn offchain_worker(_: &<$block as BlockT>::Header) {
unimplemented!()
}
}
impl pezsp_session::SessionKeys<$block> for $runtime {
fn generate_session_keys(_: Option<Vec<u8>>) -> Vec<u8> {
unimplemented!()
}
fn decode_session_keys(
_: Vec<u8>,
) -> Option<Vec<(Vec<u8>, KeyTypeId)>> {
unimplemented!()
}
}
impl
pezpallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<
$block,
Balance,
> for $runtime
{
fn query_info(
_: <$block as BlockT>::Extrinsic,
_: u32,
) -> pezpallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> {
unimplemented!()
}
fn query_fee_details(
_: <$block as BlockT>::Extrinsic,
_: u32,
) -> pezpallet_transaction_payment::FeeDetails<Balance> {
unimplemented!()
}
fn query_weight_to_fee(_: Weight) -> Balance {
unimplemented!()
}
fn query_length_to_fee(_: u32) -> Balance {
unimplemented!()
}
}
impl cumulus_primitives_core::CollectCollationInfo<$block> for $runtime {
fn collect_collation_info(
_: &<$block as BlockT>::Header
) -> cumulus_primitives_core::CollationInfo {
unimplemented!()
}
}
impl cumulus_primitives_core::GetTeyrchainInfo<$block> for $runtime {
fn teyrchain_id() -> ParaId {
unimplemented!()
}
}
#[cfg(feature = "try-runtime")]
impl pezframe_try_runtime::TryRuntime<$block> for $runtime {
fn on_runtime_upgrade(
_: pezframe_try_runtime::UpgradeCheckSelect
) -> (Weight, Weight) {
unimplemented!()
}
fn execute_block(
_: <$block as BlockT>::LazyBlock,
_: bool,
_: bool,
_: pezframe_try_runtime::TryStateSelect,
) -> Weight {
unimplemented!()
}
}
impl pezframe_system_rpc_runtime_api::AccountNonceApi<
$block,
AccountId,
Nonce
> for $runtime {
fn account_nonce(_: AccountId) -> Nonce {
unimplemented!()
}
}
#[cfg(feature = "runtime-benchmarks")]
impl pezframe_benchmarking::Benchmark<$block> for $runtime {
fn benchmark_metadata(_: bool) -> (
Vec<pezframe_benchmarking::BenchmarkList>,
Vec<pezframe_support::traits::StorageInfo>,
) {
unimplemented!()
}
#[allow(non_local_definitions)]
fn dispatch_benchmark(
_: pezframe_benchmarking::BenchmarkConfig
) -> Result<Vec<pezframe_benchmarking::BenchmarkBatch>, String> {
unimplemented!()
}
}
impl pezsp_genesis_builder::GenesisBuilder<$block> for $runtime {
fn build_state(_: Vec<u8>) -> pezsp_genesis_builder::Result {
unimplemented!()
}
fn get_preset(_id: &Option<pezsp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
unimplemented!()
}
fn preset_names() -> Vec<pezsp_genesis_builder::PresetId> {
unimplemented!()
}
}
impl pezsp_statement_store::runtime_api::ValidateStatement<$block> for $runtime {
fn validate_statement(
_source: pezsp_statement_store::runtime_api::StatementSource,
_statement: pezsp_statement_store::Statement,
) -> Result<pezsp_statement_store::runtime_api::ValidStatement, pezsp_statement_store::runtime_api::InvalidStatement> {
unimplemented!()
}
}
impl cumulus_primitives_core::TargetBlockRate<$block> for $runtime {
fn target_block_rate() -> u32 {
unimplemented!()
}
}
}
};
}
pub(crate) use impl_node_runtime_apis;
@@ -0,0 +1,30 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
pub mod cli;
mod command;
mod common;
pub mod extra_subcommand;
mod fake_runtime_api;
mod nodes;
pub use cli::CliConfig;
pub use command::{run, run_with_custom_cli, RunConfig};
pub use common::{chain_spec, runtime};
pub use nodes::NODE_VERSION;
@@ -0,0 +1,757 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
cli::{AuthoringPolicy, DevSealMode},
common::{
aura::{AuraIdT, AuraRuntimeApi},
rpc::{BuildRpcExtensions, BuildTeyrchainRpcExtensions},
spec::{
BaseNodeSpec, BuildImportQueue, ClientBlockImport, DynNodeSpec, InitBlockImport,
NodeSpec, StartConsensus,
},
types::{
AccountId, Balance, Hash, Nonce, TeyrchainBackend, TeyrchainBlockImport,
TeyrchainClient,
},
ConstructNodeRuntimeApi, NodeBlock, NodeExtraArgs,
},
};
use codec::Encode;
use cumulus_client_collator::service::{
CollatorService, ServiceInterface as CollatorServiceInterface,
};
#[docify::export(slot_based_colator_import)]
use cumulus_client_consensus_aura::collators::slot_based::{
self as slot_based, Params as SlotBasedParams,
};
use cumulus_client_consensus_aura::{
collators::{
lookahead::{self as aura, Params as AuraParams},
slot_based::{SlotBasedBlockImport, SlotBasedBlockImportHandle},
},
equivocation_import_queue::Verifier as EquivocationVerifier,
};
use cumulus_client_consensus_proposer::ProposerInterface;
use cumulus_client_consensus_relay_chain::Verifier as RelayChainVerifier;
use cumulus_client_service::CollatorSybilResistance;
use cumulus_client_teyrchain_inherent::MockValidationDataInherentDataProvider;
use cumulus_primitives_core::{
relay_chain::ValidationCode, CollectCollationInfo, GetTeyrchainInfo, ParaId,
};
use cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface};
use futures::{prelude::*, FutureExt};
use pezkuwi_primitives::{CollatorPair, UpgradeGoAhead};
use prometheus_endpoint::Registry;
use pezsc_client_api::{Backend, BlockchainEvents};
use pezsc_client_db::DbHash;
use pezsc_consensus::{
import_queue::{BasicQueue, Verifier as VerifierT},
BlockImportParams, DefaultImportQueue, LongestChain,
};
use pezsc_consensus_manual_seal::consensus::aura::AuraConsensusDataProvider;
use pezsc_network::{config::FullNetworkConfiguration, NotificationMetrics, PeerId};
use pezsc_service::{Configuration, Error, PartialComponents, TaskManager};
use pezsc_telemetry::TelemetryHandle;
use pezsc_transaction_pool::TransactionPoolHandle;
use pezsc_transaction_pool_api::OffchainTransactionPoolFactory;
use pezsp_api::ProvideRuntimeApi;
use pezsp_core::traits::SpawnEssentialNamed;
use pezsp_inherents::CreateInherentDataProviders;
use pezsp_keystore::KeystorePtr;
use pezsp_runtime::{
app_crypto::AppCrypto,
traits::{Block as BlockT, Header as HeaderT, UniqueSaturatedInto},
};
use std::{marker::PhantomData, sync::Arc, time::Duration};
struct Verifier<Block, Client, AuraId> {
client: Arc<Client>,
aura_verifier: Box<dyn VerifierT<Block>>,
relay_chain_verifier: Box<dyn VerifierT<Block>>,
_phantom: PhantomData<AuraId>,
}
#[async_trait::async_trait]
impl<Block: BlockT, Client, AuraId> VerifierT<Block> for Verifier<Block, Client, AuraId>
where
Client: ProvideRuntimeApi<Block> + Send + Sync,
Client::Api: AuraRuntimeApi<Block, AuraId>,
AuraId: AuraIdT + Sync,
{
async fn verify(
&self,
block_import: BlockImportParams<Block>,
) -> Result<BlockImportParams<Block>, String> {
if self.client.runtime_api().has_aura_api(*block_import.header.parent_hash()) {
self.aura_verifier.verify(block_import).await
} else {
self.relay_chain_verifier.verify(block_import).await
}
}
}
/// Build the import queue for teyrchain runtimes that started with relay chain consensus and
/// switched to aura.
pub(crate) struct BuildRelayToAuraImportQueue<Block, RuntimeApi, AuraId, BlockImport>(
PhantomData<(Block, RuntimeApi, AuraId, BlockImport)>,
);
impl<Block: BlockT, RuntimeApi, AuraId, BlockImport>
BuildImportQueue<Block, RuntimeApi, BlockImport>
for BuildRelayToAuraImportQueue<Block, RuntimeApi, AuraId, BlockImport>
where
RuntimeApi: ConstructNodeRuntimeApi<Block, TeyrchainClient<Block, RuntimeApi>>,
RuntimeApi::RuntimeApi: AuraRuntimeApi<Block, AuraId>,
AuraId: AuraIdT + Sync,
BlockImport:
pezsc_consensus::BlockImport<Block, Error = pezsp_consensus::Error> + Send + Sync + 'static,
{
fn build_import_queue(
client: Arc<TeyrchainClient<Block, RuntimeApi>>,
block_import: TeyrchainBlockImport<Block, BlockImport>,
config: &Configuration,
telemetry_handle: Option<TelemetryHandle>,
task_manager: &TaskManager,
) -> pezsc_service::error::Result<DefaultImportQueue<Block>> {
let inherent_data_providers =
move |_, _| async move { Ok(pezsp_timestamp::InherentDataProvider::from_system_time()) };
let registry = config.prometheus_registry();
let spawner = task_manager.spawn_essential_handle();
let relay_chain_verifier =
Box::new(RelayChainVerifier::new(client.clone(), inherent_data_providers));
let equivocation_aura_verifier =
EquivocationVerifier::<<AuraId as AppCrypto>::Pair, _, _, _>::new(
client.clone(),
inherent_data_providers,
telemetry_handle,
);
let verifier = Verifier {
client,
aura_verifier: Box::new(equivocation_aura_verifier),
relay_chain_verifier,
_phantom: Default::default(),
};
Ok(BasicQueue::new(verifier, Box::new(block_import), None, &spawner, registry))
}
}
/// Uses the lookahead collator to support async backing.
///
/// Start an aura powered teyrchain node. Some system chains use this.
pub(crate) struct AuraNode<Block, RuntimeApi, AuraId, StartConsensus, InitBlockImport>(
pub PhantomData<(Block, RuntimeApi, AuraId, StartConsensus, InitBlockImport)>,
);
impl<Block, RuntimeApi, AuraId, StartConsensus, InitBlockImport> Default
for AuraNode<Block, RuntimeApi, AuraId, StartConsensus, InitBlockImport>
{
fn default() -> Self {
Self(Default::default())
}
}
impl<Block, RuntimeApi, AuraId, StartConsensus, InitBlockImport> BaseNodeSpec
for AuraNode<Block, RuntimeApi, AuraId, StartConsensus, InitBlockImport>
where
Block: NodeBlock,
RuntimeApi: ConstructNodeRuntimeApi<Block, TeyrchainClient<Block, RuntimeApi>>,
RuntimeApi::RuntimeApi: AuraRuntimeApi<Block, AuraId>
+ pezpallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
+ bizinikiwi_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>,
AuraId: AuraIdT + Sync,
InitBlockImport: self::InitBlockImport<Block, RuntimeApi> + Send,
InitBlockImport::BlockImport:
pezsc_consensus::BlockImport<Block, Error = pezsp_consensus::Error> + 'static,
{
type Block = Block;
type RuntimeApi = RuntimeApi;
type BuildImportQueue =
BuildRelayToAuraImportQueue<Block, RuntimeApi, AuraId, InitBlockImport::BlockImport>;
type InitBlockImport = InitBlockImport;
}
impl<Block, RuntimeApi, AuraId, StartConsensus, InitBlockImport> NodeSpec
for AuraNode<Block, RuntimeApi, AuraId, StartConsensus, InitBlockImport>
where
Block: NodeBlock,
RuntimeApi: ConstructNodeRuntimeApi<Block, TeyrchainClient<Block, RuntimeApi>>,
RuntimeApi::RuntimeApi: AuraRuntimeApi<Block, AuraId>
+ pezpallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
+ bizinikiwi_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>,
AuraId: AuraIdT + Sync,
StartConsensus: self::StartConsensus<
Block,
RuntimeApi,
InitBlockImport::BlockImport,
InitBlockImport::BlockImportAuxiliaryData,
> + 'static,
InitBlockImport: self::InitBlockImport<Block, RuntimeApi> + Send + 'static,
InitBlockImport::BlockImport:
pezsc_consensus::BlockImport<Block, Error = pezsp_consensus::Error> + 'static,
{
type BuildRpcExtensions = BuildTeyrchainRpcExtensions<Block, RuntimeApi>;
type StartConsensus = StartConsensus;
const SYBIL_RESISTANCE: CollatorSybilResistance = CollatorSybilResistance::Resistant;
fn start_dev_node(
mut config: Configuration,
mode: DevSealMode,
) -> pezsc_service::error::Result<TaskManager> {
let PartialComponents {
client,
backend,
mut task_manager,
import_queue,
keystore_container,
select_chain: _,
transaction_pool,
other: (_, mut telemetry, _, _),
} = Self::new_partial(&config)?;
// Since this is a dev node, prevent it from connecting to peers.
config.network.default_peers_set.in_peers = 0;
config.network.default_peers_set.out_peers = 0;
let net_config = FullNetworkConfiguration::<_, _, pezsc_network::Litep2pNetworkBackend>::new(
&config.network,
None,
);
let (network, system_rpc_tx, tx_handler_controller, sync_service) =
pezsc_service::build_network(pezsc_service::BuildNetworkParams {
config: &config,
client: client.clone(),
transaction_pool: transaction_pool.clone(),
spawn_handle: task_manager.spawn_handle(),
import_queue,
net_config,
block_announce_validator_builder: None,
warp_sync_config: None,
block_relay: None,
metrics: NotificationMetrics::new(None),
})?;
if config.offchain_worker.enabled {
let offchain_workers =
pezsc_offchain::OffchainWorkers::new(pezsc_offchain::OffchainWorkerOptions {
runtime_api_provider: client.clone(),
keystore: Some(keystore_container.keystore()),
offchain_db: backend.offchain_storage(),
transaction_pool: Some(OffchainTransactionPoolFactory::new(
transaction_pool.clone(),
)),
network_provider: Arc::new(network.clone()),
is_validator: config.role.is_authority(),
enable_http_requests: true,
custom_extensions: move |_| vec![],
})?;
task_manager.spawn_handle().spawn(
"offchain-workers-runner",
"offchain-work",
offchain_workers.run(client.clone(), task_manager.spawn_handle()).boxed(),
);
}
let proposer = pezsc_basic_authorship::ProposerFactory::new(
task_manager.spawn_handle(),
client.clone(),
transaction_pool.clone(),
None,
None,
);
// Note: Changing slot durations are currently not supported
let slot_duration = pezsc_consensus_aura::slot_duration(&*client)
.expect("slot_duration is always present; qed.");
// The aura digest provider will provide digests that match the provided timestamp data.
// Without this, the AURA teyrchain runtimes complain about slot mismatches.
let aura_digest_provider = AuraConsensusDataProvider::new_with_slot_duration(slot_duration);
let para_id =
Self::teyrchain_id(&client, &config).ok_or("Failed to retrieve the teyrchain id")?;
let create_inherent_data_providers =
Self::create_dev_node_inherent_data_providers(client.clone(), para_id, slot_duration);
match mode {
DevSealMode::InstantSeal => {
let params = pezsc_consensus_manual_seal::InstantSealParams {
block_import: client.clone(),
env: proposer,
client: client.clone(),
pool: transaction_pool.clone(),
select_chain: LongestChain::new(backend.clone()),
consensus_data_provider: Some(Box::new(aura_digest_provider)),
create_inherent_data_providers,
};
let authorship_future = pezsc_consensus_manual_seal::run_instant_seal(params);
task_manager.spawn_essential_handle().spawn_blocking(
"instant-seal",
None,
authorship_future,
);
},
DevSealMode::ManualSeal(block_time) => {
let (manual_seal_sink, manual_seal_stream) = futures::channel::mpsc::channel(1024);
let mut manual_seal_sink_clone = manual_seal_sink.clone();
task_manager
.spawn_essential_handle()
.spawn("block_authoring", None, async move {
loop {
futures_timer::Delay::new(std::time::Duration::from_millis(block_time))
.await;
manual_seal_sink_clone
.try_send(pezsc_consensus_manual_seal::EngineCommand::SealNewBlock {
create_empty: true,
finalize: true,
parent_hash: None,
sender: None,
})
.unwrap();
}
});
let params = pezsc_consensus_manual_seal::ManualSealParams {
block_import: client.clone(),
env: proposer,
client: client.clone(),
pool: transaction_pool.clone(),
select_chain: LongestChain::new(backend.clone()),
commands_stream: Box::pin(manual_seal_stream),
consensus_data_provider: Some(Box::new(aura_digest_provider)),
create_inherent_data_providers,
};
let authorship_future = pezsc_consensus_manual_seal::run_manual_seal(params);
task_manager.spawn_essential_handle().spawn_blocking(
"manual-seal",
None,
authorship_future,
);
},
}
let rpc_extensions_builder = {
let client = client.clone();
let transaction_pool = transaction_pool.clone();
let backend_for_rpc = backend.clone();
Box::new(move |_| {
let module = Self::BuildRpcExtensions::build_rpc_extensions(
client.clone(),
backend_for_rpc.clone(),
transaction_pool.clone(),
None,
)?;
Ok(module)
})
};
let _rpc_handlers = pezsc_service::spawn_tasks(pezsc_service::SpawnTasksParams {
network,
client,
keystore: keystore_container.keystore(),
task_manager: &mut task_manager,
transaction_pool,
rpc_builder: rpc_extensions_builder,
backend,
system_rpc_tx,
tx_handler_controller,
sync_service,
config,
telemetry: telemetry.as_mut(),
tracing_execute_block: None,
})?;
Ok(task_manager)
}
}
impl<Block, RuntimeApi, AuraId, StartConsensus, InitBlockImport>
AuraNode<Block, RuntimeApi, AuraId, StartConsensus, InitBlockImport>
where
Block: NodeBlock,
RuntimeApi: ConstructNodeRuntimeApi<Block, TeyrchainClient<Block, RuntimeApi>>,
RuntimeApi::RuntimeApi: AuraRuntimeApi<Block, AuraId>,
AuraId: AuraIdT + Sync,
{
/// Creates the inherent data providers for manual and instant seal consensus.
///
/// This function sets up the timestamp and teyrchain validation data providers
/// required for dev seal block production in a teyrchain environment.
fn create_dev_node_inherent_data_providers(
client: Arc<TeyrchainClient<Block, RuntimeApi>>,
para_id: ParaId,
slot_duration: pezsp_consensus_aura::SlotDuration,
) -> impl Fn(
Hash,
(),
) -> future::Ready<
Result<
(pezsp_timestamp::InherentDataProvider, MockValidationDataInherentDataProvider<()>),
Box<dyn std::error::Error + Send + Sync>,
>,
> + Send
+ Sync {
move |block: Hash, ()| {
let current_para_head = client
.header(block)
.expect("Header lookup should succeed")
.expect("Header passed in as parent should be present in backend.");
let should_send_go_ahead = client
.runtime_api()
.collect_collation_info(block, &current_para_head)
.map(|info| info.new_validation_code.is_some())
.unwrap_or_default();
let current_para_block_head =
Some(pezkuwi_primitives::HeadData(current_para_head.encode()));
let current_block_number =
UniqueSaturatedInto::<u32>::unique_saturated_into(*current_para_head.number()) + 1;
log::info!("Current block number: {current_block_number}");
let mocked_teyrchain = MockValidationDataInherentDataProvider::<()> {
current_para_block: current_block_number,
para_id,
current_para_block_head,
relay_blocks_per_para_block: 1,
para_blocks_per_relay_epoch: 10,
upgrade_go_ahead: should_send_go_ahead.then(|| {
log::info!("Detected pending validation code, sending go-ahead signal.");
UpgradeGoAhead::GoAhead
}),
..Default::default()
};
let timestamp_provider = pezsp_timestamp::InherentDataProvider::new(
(slot_duration.as_millis() * current_block_number as u64).into(),
);
futures::future::ready(Ok((timestamp_provider, mocked_teyrchain)))
}
}
}
pub fn new_aura_node_spec<Block, RuntimeApi, AuraId>(
extra_args: &NodeExtraArgs,
) -> Box<dyn DynNodeSpec>
where
Block: NodeBlock,
RuntimeApi: ConstructNodeRuntimeApi<Block, TeyrchainClient<Block, RuntimeApi>>,
RuntimeApi::RuntimeApi: AuraRuntimeApi<Block, AuraId>
+ pezpallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
+ bizinikiwi_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>
+ GetTeyrchainInfo<Block>,
AuraId: AuraIdT + Sync + Send,
<AuraId as AppCrypto>::Pair: Send + Sync,
{
if extra_args.authoring_policy == AuthoringPolicy::SlotBased {
Box::new(AuraNode::<
Block,
RuntimeApi,
AuraId,
StartSlotBasedAuraConsensus<Block, RuntimeApi, AuraId>,
StartSlotBasedAuraConsensus<Block, RuntimeApi, AuraId>,
>::default())
} else {
Box::new(AuraNode::<
Block,
RuntimeApi,
AuraId,
StartLookaheadAuraConsensus<Block, RuntimeApi, AuraId>,
ClientBlockImport,
>::default())
}
}
/// Start consensus using the lookahead aura collator.
pub(crate) struct StartSlotBasedAuraConsensus<Block, RuntimeApi, AuraId>(
PhantomData<(Block, RuntimeApi, AuraId)>,
);
impl<Block: BlockT<Hash = DbHash>, RuntimeApi, AuraId>
StartSlotBasedAuraConsensus<Block, RuntimeApi, AuraId>
where
RuntimeApi: ConstructNodeRuntimeApi<Block, TeyrchainClient<Block, RuntimeApi>>,
RuntimeApi::RuntimeApi: AuraRuntimeApi<Block, AuraId>,
AuraId: AuraIdT + Sync + Send,
<AuraId as AppCrypto>::Pair: Send + Sync,
{
#[docify::export_content]
fn launch_slot_based_collator<CIDP, CHP, Proposer, CS, Spawner>(
params_with_export: SlotBasedParams<
Block,
TeyrchainBlockImport<
Block,
SlotBasedBlockImport<
Block,
Arc<TeyrchainClient<Block, RuntimeApi>>,
TeyrchainClient<Block, RuntimeApi>,
>,
>,
CIDP,
TeyrchainClient<Block, RuntimeApi>,
TeyrchainBackend<Block>,
Arc<dyn RelayChainInterface>,
CHP,
Proposer,
CS,
Spawner,
>,
) where
CIDP: CreateInherentDataProviders<Block, ()> + 'static,
CIDP::InherentDataProviders: Send,
CHP: cumulus_client_consensus_common::ValidationCodeHashProvider<Hash> + Send + 'static,
Proposer: ProposerInterface<Block> + Send + Sync + 'static,
CS: CollatorServiceInterface<Block> + Send + Sync + Clone + 'static,
Spawner: SpawnEssentialNamed + Clone + 'static,
{
slot_based::run::<Block, <AuraId as AppCrypto>::Pair, _, _, _, _, _, _, _, _, _>(
params_with_export,
);
}
}
impl<Block: BlockT<Hash = DbHash>, RuntimeApi, AuraId>
StartConsensus<
Block,
RuntimeApi,
SlotBasedBlockImport<
Block,
Arc<TeyrchainClient<Block, RuntimeApi>>,
TeyrchainClient<Block, RuntimeApi>,
>,
SlotBasedBlockImportHandle<Block>,
> for StartSlotBasedAuraConsensus<Block, RuntimeApi, AuraId>
where
RuntimeApi: ConstructNodeRuntimeApi<Block, TeyrchainClient<Block, RuntimeApi>>,
RuntimeApi::RuntimeApi: AuraRuntimeApi<Block, AuraId>,
AuraId: AuraIdT + Sync + Send,
<AuraId as AppCrypto>::Pair: Send + Sync,
{
fn start_consensus(
client: Arc<TeyrchainClient<Block, RuntimeApi>>,
block_import: TeyrchainBlockImport<
Block,
SlotBasedBlockImport<
Block,
Arc<TeyrchainClient<Block, RuntimeApi>>,
TeyrchainClient<Block, RuntimeApi>,
>,
>,
prometheus_registry: Option<&Registry>,
telemetry: Option<TelemetryHandle>,
task_manager: &TaskManager,
relay_chain_interface: Arc<dyn RelayChainInterface>,
transaction_pool: Arc<TransactionPoolHandle<Block, TeyrchainClient<Block, RuntimeApi>>>,
keystore: KeystorePtr,
relay_chain_slot_duration: Duration,
para_id: ParaId,
collator_key: CollatorPair,
collator_peer_id: PeerId,
_overseer_handle: OverseerHandle,
announce_block: Arc<dyn Fn(Hash, Option<Vec<u8>>) + Send + Sync>,
backend: Arc<TeyrchainBackend<Block>>,
node_extra_args: NodeExtraArgs,
block_import_handle: SlotBasedBlockImportHandle<Block>,
) -> Result<(), Error> {
let proposer = pezsc_basic_authorship::ProposerFactory::with_proof_recording(
task_manager.spawn_handle(),
client.clone(),
transaction_pool,
prometheus_registry,
telemetry.clone(),
);
let collator_service = CollatorService::new(
client.clone(),
Arc::new(task_manager.spawn_handle()),
announce_block,
client.clone(),
);
let client_for_aura = client.clone();
let params = SlotBasedParams {
create_inherent_data_providers: move |_, ()| async move { Ok(()) },
block_import,
para_client: client.clone(),
para_backend: backend.clone(),
relay_client: relay_chain_interface,
relay_chain_slot_duration,
code_hash_provider: move |block_hash| {
client_for_aura.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash())
},
keystore,
collator_key,
collator_peer_id,
para_id,
proposer,
collator_service,
authoring_duration: Duration::from_millis(2000),
reinitialize: false,
slot_offset: Duration::from_secs(1),
block_import_handle,
spawner: task_manager.spawn_essential_handle(),
export_pov: node_extra_args.export_pov,
max_pov_percentage: node_extra_args.max_pov_percentage,
};
// We have a separate function only to be able to use `docify::export` on this piece of
// code.
Self::launch_slot_based_collator(params);
Ok(())
}
}
impl<Block: BlockT<Hash = DbHash>, RuntimeApi, AuraId> InitBlockImport<Block, RuntimeApi>
for StartSlotBasedAuraConsensus<Block, RuntimeApi, AuraId>
where
RuntimeApi: ConstructNodeRuntimeApi<Block, TeyrchainClient<Block, RuntimeApi>>,
RuntimeApi::RuntimeApi: AuraRuntimeApi<Block, AuraId>,
AuraId: AuraIdT + Sync,
{
type BlockImport = SlotBasedBlockImport<
Block,
Arc<TeyrchainClient<Block, RuntimeApi>>,
TeyrchainClient<Block, RuntimeApi>,
>;
type BlockImportAuxiliaryData = SlotBasedBlockImportHandle<Block>;
fn init_block_import(
client: Arc<TeyrchainClient<Block, RuntimeApi>>,
) -> pezsc_service::error::Result<(Self::BlockImport, Self::BlockImportAuxiliaryData)> {
Ok(SlotBasedBlockImport::new(client.clone(), client))
}
}
/// Wait for the Aura runtime API to appear on chain.
/// This is useful for chains that started out without Aura. Components that
/// are depending on Aura functionality will wait until Aura appears in the runtime.
async fn wait_for_aura<Block: BlockT, RuntimeApi, AuraId>(
client: Arc<TeyrchainClient<Block, RuntimeApi>>,
) where
RuntimeApi: ConstructNodeRuntimeApi<Block, TeyrchainClient<Block, RuntimeApi>>,
RuntimeApi::RuntimeApi: AuraRuntimeApi<Block, AuraId>,
AuraId: AuraIdT + Sync,
{
let finalized_hash = client.chain_info().finalized_hash;
if client.runtime_api().has_aura_api(finalized_hash) {
return;
};
let mut stream = client.finality_notification_stream();
while let Some(notification) = stream.next().await {
if client.runtime_api().has_aura_api(notification.hash) {
return;
}
}
}
/// Start consensus using the lookahead aura collator.
pub(crate) struct StartLookaheadAuraConsensus<Block, RuntimeApi, AuraId>(
PhantomData<(Block, RuntimeApi, AuraId)>,
);
impl<Block: BlockT<Hash = DbHash>, RuntimeApi, AuraId>
StartConsensus<Block, RuntimeApi, Arc<TeyrchainClient<Block, RuntimeApi>>, ()>
for StartLookaheadAuraConsensus<Block, RuntimeApi, AuraId>
where
RuntimeApi: ConstructNodeRuntimeApi<Block, TeyrchainClient<Block, RuntimeApi>>,
RuntimeApi::RuntimeApi: AuraRuntimeApi<Block, AuraId>,
AuraId: AuraIdT + Sync + Send,
<AuraId as AppCrypto>::Pair: Send + Sync,
{
fn start_consensus(
client: Arc<TeyrchainClient<Block, RuntimeApi>>,
block_import: TeyrchainBlockImport<Block, Arc<TeyrchainClient<Block, RuntimeApi>>>,
prometheus_registry: Option<&Registry>,
telemetry: Option<TelemetryHandle>,
task_manager: &TaskManager,
relay_chain_interface: Arc<dyn RelayChainInterface>,
transaction_pool: Arc<TransactionPoolHandle<Block, TeyrchainClient<Block, RuntimeApi>>>,
keystore: KeystorePtr,
relay_chain_slot_duration: Duration,
para_id: ParaId,
collator_key: CollatorPair,
collator_peer_id: PeerId,
overseer_handle: OverseerHandle,
announce_block: Arc<dyn Fn(Hash, Option<Vec<u8>>) + Send + Sync>,
backend: Arc<TeyrchainBackend<Block>>,
node_extra_args: NodeExtraArgs,
_: (),
) -> Result<(), Error> {
let proposer = pezsc_basic_authorship::ProposerFactory::with_proof_recording(
task_manager.spawn_handle(),
client.clone(),
transaction_pool,
prometheus_registry,
telemetry.clone(),
);
let collator_service = CollatorService::new(
client.clone(),
Arc::new(task_manager.spawn_handle()),
announce_block,
client.clone(),
);
let params = aura::ParamsWithExport {
export_pov: node_extra_args.export_pov,
params: AuraParams {
create_inherent_data_providers: move |_, ()| async move { Ok(()) },
block_import,
para_client: client.clone(),
para_backend: backend,
relay_client: relay_chain_interface,
code_hash_provider: {
let client = client.clone();
move |block_hash| {
client.code_at(block_hash).ok().map(|c| ValidationCode::from(c).hash())
}
},
keystore,
collator_key,
collator_peer_id,
para_id,
overseer_handle,
relay_chain_slot_duration,
proposer,
collator_service,
authoring_duration: Duration::from_millis(2000),
reinitialize: false,
max_pov_percentage: node_extra_args.max_pov_percentage,
},
};
let fut = async move {
wait_for_aura(client).await;
aura::run_with_export::<Block, <AuraId as AppCrypto>::Pair, _, _, _, _, _, _, _, _>(
params,
)
.await;
};
task_manager.spawn_essential_handle().spawn("aura", None, fut);
Ok(())
}
}
@@ -0,0 +1,22 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pub mod aura;
/// The current node version for pezcumulus official binaries, which takes the basic
/// SemVer form `<major>.<minor>.<patch>`. It should correspond to the latest
/// `pezkuwi` version of a stable release.
pub const NODE_VERSION: &'static str = "1.20.2";
@@ -0,0 +1,61 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![cfg(feature = "runtime-benchmarks")]
use assert_cmd::cargo::cargo_bin;
use std::{
path::Path,
process::{Command, ExitStatus},
};
use tempfile::tempdir;
/// The runtimes that this command supports.
static RUNTIMES: [&str; 1] = ["asset-hub-zagros"];
/// The `benchmark storage` command works for the dev runtimes.
#[test]
#[ignore]
fn benchmark_storage_works() {
for runtime in RUNTIMES {
let tmp_dir = tempdir().expect("could not create a temp dir");
let base_path = tmp_dir.path();
let runtime = format!("{}-dev", runtime);
// Benchmarking the storage works and creates the weight file.
assert!(benchmark_storage("rocksdb", &runtime, base_path).success());
assert!(base_path.join("rocksdb_weights.rs").exists());
assert!(benchmark_storage("paritydb", &runtime, base_path).success());
assert!(base_path.join("paritydb_weights.rs").exists());
}
}
/// Invoke the `benchmark storage` sub-command for the given database and runtime.
fn benchmark_storage(db: &str, runtime: &str, base_path: &Path) -> ExitStatus {
Command::new(cargo_bin("pezkuwi-teyrchain"))
.args(["benchmark", "storage", "--chain", runtime])
.arg("--db")
.arg(db)
.arg("--weight-path")
.arg(base_path)
.args(["--state-version", "0"])
.args(["--batch-size", "1"])
.args(["--warmups", "0"])
.args(["--add", "100", "--mul", "1.2", "--metric", "p75"])
.status()
.unwrap()
}
@@ -0,0 +1,136 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![cfg(unix)]
use assert_cmd::cargo::cargo_bin;
use nix::{
sys::signal::{kill, Signal},
unistd::Pid,
};
use std::{
io::{BufRead, BufReader, Read},
ops::{Deref, DerefMut},
path::Path,
process::{self, Child, Command, ExitStatus},
};
use tokio::time::{sleep, Duration};
/// Wait for the given `child` the given number of `secs`.
///
/// Returns the `Some(exit status)` or `None` if the process did not finish in the given time.
pub fn wait_for(child: &mut Child, secs: u64) -> Result<ExitStatus, ()> {
let result = wait_timeout::ChildExt::wait_timeout(child, Duration::from_secs(5.min(secs)))
.map_err(|_| ())?;
if let Some(exit_status) = result {
Ok(exit_status)
} else {
if secs > 5 {
eprintln!("Child process taking over 5 seconds to exit gracefully");
let result = wait_timeout::ChildExt::wait_timeout(child, Duration::from_secs(secs - 5))
.map_err(|_| ())?;
if let Some(exit_status) = result {
return Ok(exit_status)
}
}
eprintln!("Took too long to exit (> {} seconds). Killing...", secs);
let _ = child.kill();
child.wait().unwrap();
Err(())
}
}
/// Run the node for a while (till the RPC is up + 30 secs)
/// TODO: needs to be revisited to hit the RPC
pub async fn run_node_for_a_while(base_path: &Path, args: &[&str], signal: Signal) {
let mut cmd = Command::new(cargo_bin("pezkuwi-teyrchain"))
.stdout(process::Stdio::piped())
.stderr(process::Stdio::piped())
.arg("-d")
.arg(base_path)
.args(args)
.spawn()
.unwrap();
let stderr = cmd.stderr.take().unwrap();
let mut child = KillChildOnDrop(cmd);
// TODO: use this instead of the timeout going forward?
let (_, _) = find_ws_url_from_output(stderr);
// TODO: Revisit this to find a better approach for collators
sleep(Duration::from_secs(120)).await;
assert!(child.try_wait().unwrap().is_none(), "the process should still be running");
// Stop the process
kill(Pid::from_raw(child.id().try_into().unwrap()), signal).unwrap();
assert!(wait_for(&mut child, 40).map(|x| x.success()).unwrap());
}
pub struct KillChildOnDrop(pub Child);
impl Drop for KillChildOnDrop {
fn drop(&mut self) {
let _ = self.0.kill();
}
}
impl Deref for KillChildOnDrop {
type Target = Child;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for KillChildOnDrop {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
/// Read the RPC server address from the output.
///
/// This is hack to get the actual bound sockaddr because
/// bizinikiwi assigns a random port if the specified port was already bound.
pub fn find_ws_url_from_output(read: impl Read + Send) -> (String, String) {
let mut data = String::new();
let ws_url = BufReader::new(read)
.lines()
.find_map(|line| {
let line =
line.expect("failed to obtain next line from stdout for WS address discovery");
data.push_str(&line);
data.push('\n');
// does the line contain our port (we expect this specific output from bizinikiwi).
let sock_addr = match line.split_once("Running JSON-RPC server: addr=") {
None => return None,
Some((_, after)) => after.split_once(',').unwrap().0,
};
Some(format!("ws://{}", sock_addr))
})
.unwrap_or_else(|| {
eprintln!("Output:\n{}", data);
panic!("We should get an address")
});
(ws_url, data)
}
@@ -0,0 +1,39 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use tempfile::tempdir;
mod common;
#[tokio::test]
#[cfg(unix)]
#[ignore]
async fn pezkuwi_argument_parsing() {
use nix::sys::signal::Signal::{SIGINT, SIGTERM};
let base_dir = tempdir().expect("could not create a temp dir");
let args = &[
"--",
"--chain=pezkuwichain-local",
"--bootnodes",
"/ip4/127.0.0.1/tcp/30333/p2p/Qmbx43psh7LVkrYTRXisUpzCubbgYojkejzAgj5mteDnxy",
"--bootnodes",
"/ip4/127.0.0.1/tcp/50500/p2p/Qma6SpS7tzfCrhtgEVKR9Uhjmuv55ovC3kY6y6rPBxpWde",
];
common::run_node_for_a_while(base_dir.path(), args, SIGINT).await;
common::run_node_for_a_while(base_dir.path(), args, SIGTERM).await;
}
@@ -0,0 +1,33 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use tempfile::tempdir;
mod common;
#[tokio::test]
#[cfg(unix)]
#[ignore]
async fn interrupt_pezkuwi_mdns_issue_test() {
use nix::sys::signal::Signal::{SIGINT, SIGTERM};
let base_dir = tempdir().expect("could not create a temp dir");
let args = &["--", "--chain=pezkuwichain-local"];
common::run_node_for_a_while(base_dir.path(), args, SIGINT).await;
common::run_node_for_a_while(base_dir.path(), args, SIGTERM).await;
}
@@ -0,0 +1,53 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use assert_cmd::cargo::cargo_bin;
use nix::sys::signal::SIGINT;
use std::process::Command;
use tempfile::tempdir;
mod common;
#[tokio::test]
#[cfg(unix)]
#[ignore]
async fn purge_chain_works() {
// Check that both databases are deleted
let base_dir = tempdir().expect("could not create a temp dir");
let base_dir_path = format!("{}/pezkuwi", base_dir.path().display());
let args = &["--", "-d", &base_dir_path, "--chain=pezkuwichain-local"];
common::run_node_for_a_while(base_dir.path(), args, SIGINT).await;
assert!(base_dir.path().join("chains/local_testnet/db/full").exists());
assert!(base_dir.path().join("pezkuwi/chains/pezkuwichain_local_testnet/db/full").exists());
let status = Command::new(cargo_bin("pezkuwi-teyrchain"))
.args(["purge-chain", "-d"])
.arg(base_dir.path())
.arg("-y")
.status()
.unwrap();
assert!(status.success());
// Make sure that the `teyrchain_local_testnet` chain folder exists, but the `db` is deleted.
assert!(base_dir.path().join("chains/local_testnet").exists());
assert!(!base_dir.path().join("chains/local_testnet/db/full").exists());
assert!(base_dir.path().join("pezkuwi/chains/pezkuwichain_local_testnet").exists());
assert!(!base_dir.path().join("pezkuwi/chains/pezkuwichain_local_testnet/db/full").exists());
}
@@ -0,0 +1,33 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use tempfile::tempdir;
mod common;
#[tokio::test]
#[cfg(unix)]
#[ignore]
async fn running_the_node_works_and_can_be_interrupted() {
use nix::sys::signal::Signal::{SIGINT, SIGTERM};
let base_dir = tempdir().expect("could not create a temp dir");
let args = &["--", "--chain=pezkuwichain-local"];
common::run_node_for_a_while(base_dir.path(), args, SIGINT).await;
common::run_node_for_a_while(base_dir.path(), args, SIGTERM).await;
}
+54
View File
@@ -0,0 +1,54 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! White labeled pezkuwi omni-node.
//!
//! For documentation, see [`pezkuwi_omni_node_lib`].
#![warn(missing_docs)]
#![warn(unused_extern_crates)]
use pezkuwi_omni_node_lib::{
chain_spec::DiskChainSpecLoader, extra_subcommand::NoExtraSubcommand, run_with_custom_cli,
runtime::DefaultRuntimeResolver, CliConfig as CliConfigT, RunConfig, NODE_VERSION,
};
struct CliConfig;
impl CliConfigT for CliConfig {
fn impl_version() -> String {
let commit_hash = env!("BIZINIKIWI_CLI_COMMIT_HASH");
format!("{}-{commit_hash}", NODE_VERSION)
}
fn author() -> String {
env!("CARGO_PKG_AUTHORS").into()
}
fn support_url() -> String {
"https://github.com/pezkuwichain/pezkuwi-sdk/issues/new".into()
}
fn copyright_start_year() -> u16 {
2017
}
}
fn main() -> color_eyre::eyre::Result<()> {
color_eyre::install()?;
let config = RunConfig::new(Box::new(DefaultRuntimeResolver), Box::new(DiskChainSpecLoader));
Ok(run_with_custom_cli::<CliConfig, NoExtraSubcommand>(config)?)
}
@@ -0,0 +1,39 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// Integration tests that spawn the actual binary `pezkuwi-omni-node`
/// using `assert_cmd`. We verify that the help text
/// excludes the `export-chain-spec` subcommand exactly as intended
use assert_cmd::Command;
#[test]
fn pezkuwi_omni_node_help_excludes_export_chain_spec() {
// Run `pezkuwi-omni-node --help` and capture stdout.
let output = Command::cargo_bin("pezkuwi-omni-node")
.expect("binary `pezkuwi-omni-node` should be built by the workspace")
.arg("--help")
.assert()
.success()
.get_output()
.stdout
.clone();
let help_text = String::from_utf8_lossy(&output);
assert!(
!help_text.contains("export-chain-spec"),
"`pezkuwi-omni-node --help` must NOT list the \"export-chain-spec\" subcommand"
);
}