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
@@ -0,0 +1,146 @@
# The `benchmark overhead` command
Each time an extrinsic or a block is executed, a fixed weight is charged as "execution overhead". This is necessary
since the weight that is calculated by the pallet benchmarks does not include this overhead. The exact overhead to can
vary per Bizinikiwi chain and needs to be calculated per chain. This command calculates the exact values of these
overhead weights for any Bizinikiwi chain that supports it.
## How does it work?
The benchmark consists of two parts; the [`BlockExecutionWeight`] and the [`ExtrinsicBaseWeight`]. Both are executed
sequentially when invoking the command.
## BlockExecutionWeight
The block execution weight is defined as the weight that it takes to execute an *empty block*. It is measured by
constructing an empty block and measuring its executing time. The result are written to a `block_weights.rs` file which
is created from a template. The file will contain the concrete weight value and various statistics about the
measurements. For example:
```rust
/// Time to execute an empty block.
/// Calculated by multiplying the *Average* with `1` and adding `0`.
///
/// Stats [NS]:
/// Min, Max: 3_508_416, 3_680_498
/// Average: 3_532_484
/// Median: 3_522_111
/// Std-Dev: 27070.23
///
/// Percentiles [NS]:
/// 99th: 3_631_863
/// 95th: 3_595_674
/// 75th: 3_526_435
pub const BlockExecutionWeight: Weight =
Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(3_532_484), 0);
```
In this example it takes 3.5 ms to execute an empty block. That means that it always takes at least 3.5 ms to execute
*any* block. This constant weight is therefore added to each block to ensure that Bizinikiwi budgets enough time to
execute it.
## ExtrinsicBaseWeight
The extrinsic base weight is defined as the weight that it takes to execute an *empty* extrinsic. An *empty* extrinsic
is also called a *NO-OP*. It does nothing and is the equivalent to the empty block form above. The benchmark now
constructs a block which is filled with only NO-OP extrinsics. This block is then executed many times and the weights
are measured. The result is divided by the number of extrinsics in that block and the results are written to
`extrinsic_weights.rs`.
The relevant section in the output file looks like this:
```rust
/// Time to execute a NO-OP extrinsic, for example `System::remark`.
/// Calculated by multiplying the *Average* with `1` and adding `0`.
///
/// Stats [NS]:
/// Min, Max: 67_561, 69_855
/// Average: 67_745
/// Median: 67_701
/// Std-Dev: 264.68
///
/// Percentiles [NS]:
/// 99th: 68_758
/// 95th: 67_843
/// 75th: 67_749
pub const ExtrinsicBaseWeight: Weight =
Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul(67_745), 0);
```
In this example it takes 67.7 µs to execute a NO-OP extrinsic. That means that it always takes at least 67.7 µs to
execute *any* extrinsic. This constant weight is therefore added to each extrinsic to ensure that Bizinikiwi budgets
enough time to execute it.
## Invocation
The base command looks like this (for debugging you can use `--release`):
```sh
cargo run --profile=production -- benchmark overhead --dev
```
Output:
```pre
# BlockExecutionWeight
Running 10 warmups...
Executing block 100 times
Per-block execution overhead [ns]:
Total: 353248430
Min: 3508416, Max: 3680498
Average: 3532484, Median: 3522111, Stddev: 27070.23
Percentiles 99th, 95th, 75th: 3631863, 3595674, 3526435
Writing weights to "block_weights.rs"
# Setup
Building block, this takes some time...
Extrinsics per block: 12000
# ExtrinsicBaseWeight
Running 10 warmups...
Executing block 100 times
Per-extrinsic execution overhead [ns]:
Total: 6774590
Min: 67561, Max: 69855
Average: 67745, Median: 67701, Stddev: 264.68
Percentiles 99th, 95th, 75th: 68758, 67843, 67749
Writing weights to "extrinsic_weights.rs"
```
The complete command for PezkuwiChain looks like this:
```sh
cargo run --profile=production -- benchmark overhead --chain=pezkuwi-dev --wasm-execution=compiled --weight-path=runtime/pezkuwi/constants/src/weights/
```
This will overwrite the
[block_weights.rs](https://github.com/paritytech/polkadot/blob/c254e5975711a6497af256f6831e9a6c752d28f5/runtime/polkadot/constants/src/weights/block_weights.rs)
and
[extrinsic_weights.rs](https://github.com/paritytech/polkadot/blob/c254e5975711a6497af256f6831e9a6c752d28f5/runtime/polkadot/constants/src/weights/extrinsic_weights.rs)
files in the PezkuwiChain runtime directory. You can try the same for *pezkuwichain* and to see that the results slightly differ.
👉 It is paramount to use `--profile=production` and `--wasm-execution=compiled` as the results are otherwise useless.
## Output Interpretation
Lower is better. The less weight the execution overhead needs, the better. Since the weights of the overhead is charged
per extrinsic and per block, a larger weight results in less extrinsics per block. Minimizing this is important to have
a large transaction throughput.
## Arguments
- `--chain` / `--dev` Set the chain specification.
- `--weight-path` Set the output directory or file to write the weights to.
- `--repeat` Set the repetitions of both benchmarks.
- `--warmup` Set the rounds of warmup before measuring.
- `--wasm-execution` Should be set to `compiled` for correct results.
- [`--mul`](../shared/README.md#arguments)
- [`--add`](../shared/README.md#arguments)
- [`--metric`](../shared/README.md#arguments)
- [`--weight-path`](../shared/README.md#arguments)
- [`--header`](../shared/README.md#arguments)
License: Apache-2.0
<!-- LINKS -->
[`ExtrinsicBaseWeight`]:
https://github.com/paritytech/bizinikiwi/blob/580ebae17fa30082604f1c9720f6f4a1cfe95b50/frame/support/src/weights/extrinsic_weights.rs#L26
[`BlockExecutionWeight`]:
https://github.com/paritytech/bizinikiwi/blob/580ebae17fa30082604f1c9720f6f4a1cfe95b50/frame/support/src/weights/block_weights.rs#L26
[System::Remark]:
https://github.com/paritytech/bizinikiwi/blob/580ebae17fa30082604f1c9720f6f4a1cfe95b50/frame/system/src/lib.rs#L382
@@ -0,0 +1,783 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// 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.
//! Contains the [`OverheadCmd`] as entry point for the CLI to execute
//! the *overhead* benchmarks.
use crate::{
extrinsic::{
bench::{Benchmark, BenchmarkParams as ExtrinsicBenchmarkParams},
ExtrinsicBuilder,
},
overhead::{
command::ChainType::{Relaychain, Teyrchain, Unknown},
fake_runtime_api,
remark_builder::BizinikiwiRemarkBuilder,
template::TemplateData,
},
shared::{
genesis_state,
genesis_state::{GenesisStateHandler, SpecGenesisSource},
HostInfoParams, WeightParams,
},
};
use clap::{error::ErrorKind, Args, CommandFactory, Parser};
use codec::{Decode, Encode};
use cumulus_client_teyrchain_inherent::MockValidationDataInherentDataProvider;
use fake_runtime_api::RuntimeApi as FakeRuntimeApi;
use pezframe_support::Deserialize;
use genesis_state::WARN_SPEC_GENESIS_CTOR;
use log::info;
use pezkuwi_teyrchain_primitives::primitives::Id as ParaId;
use pezsc_block_builder::BlockBuilderApi;
use pezsc_chain_spec::{ChainSpec, ChainSpecExtension, GenesisBlockBuilder};
use pezsc_cli::{CliConfiguration, Database, ImportParams, Result, SharedParams};
use pezsc_client_api::{execution_extensions::ExecutionExtensions, UsageProvider};
use pezsc_client_db::{BlocksPruning, DatabaseSettings};
use pezsc_executor::WasmExecutor;
use pezsc_runtime_utilities::fetch_latest_metadata_from_code_blob;
use pezsc_service::{new_client, new_db_backend, BasePath, ClientConfig, TFullClient, TaskManager};
use serde::Serialize;
use serde_json::{json, Value};
use pezsp_api::{ApiExt, CallApiAt, Core, ProvideRuntimeApi};
use pezsp_blockchain::HeaderBackend;
use pezsp_core::H256;
use pezsp_inherents::{InherentData, InherentDataProvider};
use pezsp_runtime::{
generic,
traits::{BlakeTwo256, Block as BlockT},
DigestItem, OpaqueExtrinsic,
};
use pezsp_storage::Storage;
use pezsp_wasm_interface::HostFunctions;
use std::{
fmt::{Debug, Display, Formatter},
fs,
path::PathBuf,
sync::Arc,
};
use subxt::{client::RuntimeVersion, ext::futures, Metadata};
const DEFAULT_PARA_ID: u32 = 100;
const LOG_TARGET: &'static str = "pezkuwi_sdk_frame::benchmark::overhead";
/// Benchmark the execution overhead per-block and per-extrinsic.
#[derive(Debug, Parser)]
pub struct OverheadCmd {
#[allow(missing_docs)]
#[clap(flatten)]
pub shared_params: SharedParams,
#[allow(missing_docs)]
#[clap(flatten)]
pub import_params: ImportParams,
#[allow(missing_docs)]
#[clap(flatten)]
pub params: OverheadParams,
}
/// Configures the benchmark, the post-processing and weight generation.
#[derive(Debug, Default, Serialize, Clone, PartialEq, Args)]
pub struct OverheadParams {
#[allow(missing_docs)]
#[clap(flatten)]
pub weight: WeightParams,
#[allow(missing_docs)]
#[clap(flatten)]
pub bench: ExtrinsicBenchmarkParams,
#[allow(missing_docs)]
#[clap(flatten)]
pub hostinfo: HostInfoParams,
/// Add a header to the generated weight output file.
///
/// Good for adding LICENSE headers.
#[arg(long, value_name = "PATH")]
pub header: Option<PathBuf>,
/// Enable the Trie cache.
///
/// This should only be used for performance analysis and not for final results.
#[arg(long)]
pub enable_trie_cache: bool,
/// Optional runtime blob to use instead of the one from the genesis config.
#[arg(
long,
value_name = "PATH",
conflicts_with = "chain",
required_if_eq("genesis_builder", "runtime")
)]
pub runtime: Option<PathBuf>,
/// The preset that we expect to find in the GenesisBuilder runtime API.
///
/// This can be useful when a runtime has a dedicated benchmarking preset instead of using the
/// default one.
#[arg(long, default_value = pezsp_genesis_builder::DEV_RUNTIME_PRESET)]
pub genesis_builder_preset: String,
/// How to construct the genesis state.
///
/// Can be used together with `--chain` to determine whether the
/// genesis state should be initialized with the values from the
/// provided chain spec or a runtime-provided genesis preset.
#[arg(long, value_enum, alias = "genesis-builder-policy")]
pub genesis_builder: Option<GenesisBuilderPolicy>,
/// Teyrchain Id to use for teyrchains. If not specified, the benchmark code will choose
/// a para-id and patch the state accordingly.
#[arg(long)]
pub para_id: Option<u32>,
}
/// How the genesis state for benchmarking should be built.
#[derive(clap::ValueEnum, Debug, Eq, PartialEq, Clone, Copy, Serialize)]
#[clap(rename_all = "kebab-case")]
pub enum GenesisBuilderPolicy {
/// Let the runtime build the genesis state through its `BuildGenesisConfig` runtime API.
/// This will use the `development` preset by default.
Runtime,
/// Use the runtime from the Spec file to build the genesis state.
SpecRuntime,
/// Use the spec file to build the genesis state. This fails when there is no spec.
#[value(alias = "spec")]
SpecGenesis,
}
/// Type of a benchmark.
#[derive(Serialize, Clone, PartialEq, Copy)]
pub(crate) enum BenchmarkType {
/// Measure the per-extrinsic execution overhead.
Extrinsic,
/// Measure the per-block execution overhead.
Block,
}
/// Hostfunctions that are typically used by teyrchains.
pub type TeyrchainHostFunctions = (
cumulus_primitives_proof_size_hostfunction::storage_proof_size::HostFunctions,
pezsp_io::BizinikiwiHostFunctions,
);
pub type BlockNumber = u32;
/// Typical block header.
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
/// Typical block type using `OpaqueExtrinsic`.
pub type OpaqueBlock = generic::Block<Header, OpaqueExtrinsic>;
/// Client type used throughout the benchmarking code.
type OverheadClient<Block, HF> = TFullClient<Block, FakeRuntimeApi, WasmExecutor<HF>>;
/// Creates inherent data for a given teyrchain ID.
///
/// This function constructs the inherent data required for block execution,
/// including the relay chain state and validation data. Not all of these
/// inherents are required for every chain. The runtime will pick the ones
/// it requires based on their identifier.
fn create_inherent_data<Client: UsageProvider<Block> + HeaderBackend<Block>, Block: BlockT>(
client: &Arc<Client>,
chain_type: &ChainType,
) -> InherentData {
let genesis = client.usage_info().chain.best_hash;
let header = client.header(genesis).unwrap().unwrap();
let mut inherent_data = InherentData::new();
// Para inherent can only makes sense when we are handling a teyrchain.
if let Teyrchain(para_id) = chain_type {
let teyrchain_validation_data_provider = MockValidationDataInherentDataProvider::<()> {
para_id: ParaId::from(*para_id),
current_para_block_head: Some(header.encode().into()),
relay_offset: 0,
..Default::default()
};
let _ = futures::executor::block_on(
teyrchain_validation_data_provider.provide_inherent_data(&mut inherent_data),
);
}
// Teyrchain inherent that is used on relay chains to perform teyrchain validation.
let para_inherent = pezkuwi_primitives::InherentData {
bitfields: Vec::new(),
backed_candidates: Vec::new(),
disputes: Vec::new(),
parent_header: header,
};
// Timestamp inherent that is very common in bizinikiwi chains.
let timestamp = pezsp_timestamp::InherentDataProvider::new(std::time::Duration::default().into());
let _ = futures::executor::block_on(timestamp.provide_inherent_data(&mut inherent_data));
let _ =
inherent_data.put_data(pezkuwi_primitives::TEYRCHAINS_INHERENT_IDENTIFIER, &para_inherent);
inherent_data
}
/// Identifies what kind of chain we are dealing with.
///
/// Chains containing the `TeyrchainSystem` and `TeyrchainInfo` pallet are considered teyrchains.
/// Chains containing the `ParaInherent` pallet are considered relay chains.
fn identify_chain(metadata: &Metadata, para_id: Option<u32>) -> ChainType {
let teyrchain_info_exists = metadata.pezpallet_by_name("TeyrchainInfo").is_some();
let teyrchain_system_exists = metadata.pezpallet_by_name("TeyrchainSystem").is_some();
let para_inherent_exists = metadata.pezpallet_by_name("ParaInherent").is_some();
log::debug!("{} TeyrchainSystem", if teyrchain_system_exists { "" } else { "" });
log::debug!("{} TeyrchainInfo", if teyrchain_info_exists { "" } else { "" });
log::debug!("{} ParaInherent", if para_inherent_exists { "" } else { "" });
let chain_type = if teyrchain_system_exists && teyrchain_info_exists {
Teyrchain(para_id.unwrap_or(DEFAULT_PARA_ID))
} else if para_inherent_exists {
Relaychain
} else {
Unknown
};
log::info!(target: LOG_TARGET, "Identified Chain type from metadata: {}", chain_type);
chain_type
}
#[derive(Deserialize, Serialize, Clone, ChainSpecExtension)]
pub struct TeyrchainExtension {
/// The id of the Teyrchain.
pub para_id: Option<u32>,
}
impl OverheadCmd {
fn state_handler_from_cli<HF: HostFunctions>(
&self,
chain_spec_from_api: Option<Box<dyn ChainSpec>>,
) -> Result<(GenesisStateHandler, Option<u32>)> {
let genesis_builder_to_source = || match self.params.genesis_builder {
Some(GenesisBuilderPolicy::Runtime) | Some(GenesisBuilderPolicy::SpecRuntime) =>
SpecGenesisSource::Runtime(self.params.genesis_builder_preset.clone()),
Some(GenesisBuilderPolicy::SpecGenesis) | None => {
log::warn!(target: LOG_TARGET, "{WARN_SPEC_GENESIS_CTOR}");
SpecGenesisSource::SpecJson
},
};
// First handle chain-spec passed in via API parameter.
if let Some(chain_spec) = chain_spec_from_api {
log::debug!(target: LOG_TARGET, "Initializing state handler with chain-spec from API: {:?}", chain_spec);
let source = genesis_builder_to_source();
return Ok((GenesisStateHandler::ChainSpec(chain_spec, source), self.params.para_id));
};
// Handle chain-spec passed in via CLI.
if let Some(chain_spec_path) = &self.shared_params.chain {
log::debug!(target: LOG_TARGET,
"Initializing state handler with chain-spec from path: {:?}",
chain_spec_path
);
let (chain_spec, para_id_from_chain_spec) =
genesis_state::chain_spec_from_path::<HF>(chain_spec_path.to_string().into())?;
let source = genesis_builder_to_source();
return Ok((
GenesisStateHandler::ChainSpec(chain_spec, source),
self.params.para_id.or(para_id_from_chain_spec),
));
};
// Check for runtimes. In general, we make sure that `--runtime` and `--chain` are
// incompatible on the CLI level.
if let Some(runtime_path) = &self.params.runtime {
log::debug!(target: LOG_TARGET, "Initializing state handler with runtime from path: {:?}", runtime_path);
let runtime_blob = fs::read(runtime_path)?;
return Ok((
GenesisStateHandler::Runtime(
runtime_blob,
Some(self.params.genesis_builder_preset.clone()),
),
self.params.para_id,
));
};
Err("Neither a runtime nor a chain-spec were specified".to_string().into())
}
fn check_args(
&self,
chain_spec: &Option<Box<dyn ChainSpec>>,
) -> std::result::Result<(), (ErrorKind, String)> {
if chain_spec.is_none() &&
self.params.runtime.is_none() &&
self.shared_params.chain.is_none()
{
return Err((
ErrorKind::MissingRequiredArgument,
"Provide either a runtime via `--runtime` or a chain spec via `--chain`"
.to_string(),
));
}
match self.params.genesis_builder {
Some(GenesisBuilderPolicy::SpecGenesis | GenesisBuilderPolicy::SpecRuntime) =>
if chain_spec.is_none() && self.shared_params.chain.is_none() {
return Err((
ErrorKind::MissingRequiredArgument,
"Provide a chain spec via `--chain`.".to_string(),
));
},
_ => {},
};
Ok(())
}
/// Run the overhead benchmark with the default extrinsic builder.
///
/// This will use [BizinikiwiRemarkBuilder] to build the extrinsic. It is
/// designed to match common configurations found in bizinikiwi chains.
pub fn run_with_default_builder_and_spec<Block, ExtraHF>(
&self,
chain_spec: Option<Box<dyn ChainSpec>>,
) -> Result<()>
where
Block: BlockT<Extrinsic = OpaqueExtrinsic, Hash = H256>,
ExtraHF: HostFunctions,
{
self.run_with_extrinsic_builder_and_spec::<Block, ExtraHF>(
Box::new(|metadata, hash, version| {
let genesis = subxt::utils::H256::from(hash.to_fixed_bytes());
Box::new(BizinikiwiRemarkBuilder::new(metadata, genesis, version)) as Box<_>
}),
chain_spec,
)
}
/// Run the benchmark overhead command.
///
/// The provided [ExtrinsicBuilder] will be used to build extrinsics for
/// block-building. It is expected that the provided implementation builds
/// a `System::remark` extrinsic.
pub fn run_with_extrinsic_builder_and_spec<Block, ExtraHF>(
&self,
ext_builder_provider: Box<
dyn FnOnce(Metadata, Block::Hash, RuntimeVersion) -> Box<dyn ExtrinsicBuilder>,
>,
chain_spec: Option<Box<dyn ChainSpec>>,
) -> Result<()>
where
Block: BlockT<Extrinsic = OpaqueExtrinsic>,
ExtraHF: HostFunctions,
{
if let Err((error_kind, msg)) = self.check_args(&chain_spec) {
let mut cmd = OverheadCmd::command();
cmd.error(error_kind, msg).exit();
};
let (state_handler, para_id) =
self.state_handler_from_cli::<(TeyrchainHostFunctions, ExtraHF)>(chain_spec)?;
let executor = WasmExecutor::<(TeyrchainHostFunctions, ExtraHF)>::builder()
.with_allow_missing_host_functions(true)
.build();
let opaque_metadata =
fetch_latest_metadata_from_code_blob(&executor, state_handler.get_code_bytes()?)
.map_err(|_| {
<&str as Into<pezsc_cli::Error>>::into("Unable to fetch latest stable metadata")
})?;
let metadata = subxt::Metadata::decode(&mut (*opaque_metadata).as_slice())?;
// At this point we know what kind of chain we are dealing with.
let chain_type = identify_chain(&metadata, para_id);
// If we are dealing with a teyrchain, make sure that the para id in genesis will
// match what we expect.
let genesis_patcher = match chain_type {
Teyrchain(para_id) =>
Some(Box::new(move |value| patch_genesis(value, Some(para_id))) as Box<_>),
_ => None,
};
let client = self.build_client_components::<Block, (TeyrchainHostFunctions, ExtraHF)>(
state_handler.build_storage::<(TeyrchainHostFunctions, ExtraHF)>(genesis_patcher)?,
executor,
&chain_type,
)?;
let inherent_data = create_inherent_data(&client, &chain_type);
let (ext_builder, runtime_name) = {
let genesis = client.usage_info().chain.best_hash;
let version = client.runtime_api().version(genesis).unwrap();
let runtime_name = version.spec_name;
let runtime_version = RuntimeVersion {
spec_version: version.spec_version,
transaction_version: version.transaction_version,
};
(ext_builder_provider(metadata, genesis, runtime_version), runtime_name)
};
self.run(
runtime_name.to_string(),
client,
inherent_data,
Default::default(),
&*ext_builder,
chain_type.requires_proof_recording(),
)
}
/// Run the benchmark overhead command.
pub fn run_with_extrinsic_builder<Block, ExtraHF>(
&self,
ext_builder_provider: Box<
dyn FnOnce(Metadata, Block::Hash, RuntimeVersion) -> Box<dyn ExtrinsicBuilder>,
>,
) -> Result<()>
where
Block: BlockT<Extrinsic = OpaqueExtrinsic>,
ExtraHF: HostFunctions,
{
self.run_with_extrinsic_builder_and_spec::<Block, ExtraHF>(ext_builder_provider, None)
}
fn build_client_components<Block, HF>(
&self,
genesis_storage: Storage,
executor: WasmExecutor<HF>,
chain_type: &ChainType,
) -> Result<Arc<OverheadClient<Block, HF>>>
where
Block: BlockT,
HF: HostFunctions,
{
let extensions = ExecutionExtensions::new(None, Arc::new(executor.clone()));
let base_path = match &self.shared_params.base_path {
None => BasePath::new_temp_dir()?,
Some(path) => BasePath::from(path.clone()),
};
let database_source = self.database_config(
&base_path.path().to_path_buf(),
self.database_cache_size()?.unwrap_or(1024),
self.database()?.unwrap_or(Database::Auto),
)?;
let backend = new_db_backend(DatabaseSettings {
trie_cache_maximum_size: self.trie_cache_maximum_size()?,
state_pruning: None,
blocks_pruning: BlocksPruning::KeepAll,
source: database_source,
metrics_registry: None,
})?;
let genesis_block_builder = GenesisBlockBuilder::new_with_storage(
genesis_storage,
true,
backend.clone(),
executor.clone(),
)?;
let tokio_runtime = pezsc_cli::build_runtime()?;
let task_manager = TaskManager::new(tokio_runtime.handle().clone(), None)
.map_err(|_| "Unable to build task manager")?;
let client: Arc<OverheadClient<Block, HF>> = Arc::new(new_client(
backend.clone(),
executor,
genesis_block_builder,
Default::default(),
Default::default(),
extensions,
Box::new(task_manager.spawn_handle()),
None,
None,
ClientConfig {
offchain_worker_enabled: false,
offchain_indexing_api: false,
wasm_runtime_overrides: None,
no_genesis: false,
wasm_runtime_substitutes: Default::default(),
enable_import_proof_recording: chain_type.requires_proof_recording(),
},
)?);
Ok(client)
}
/// Measure the per-block and per-extrinsic execution overhead.
///
/// Writes the results to console and into two instances of the
/// `weights.hbs` template, one for each benchmark.
pub fn run<Block, C>(
&self,
chain_name: String,
client: Arc<C>,
inherent_data: pezsp_inherents::InherentData,
digest_items: Vec<DigestItem>,
ext_builder: &dyn ExtrinsicBuilder,
should_record_proof: bool,
) -> Result<()>
where
Block: BlockT<Extrinsic = OpaqueExtrinsic>,
C: ProvideRuntimeApi<Block>
+ CallApiAt<Block>
+ UsageProvider<Block>
+ pezsp_blockchain::HeaderBackend<Block>,
C::Api: ApiExt<Block> + BlockBuilderApi<Block>,
{
if ext_builder.pallet() != "system" || ext_builder.extrinsic() != "remark" {
return Err(format!("The extrinsic builder is required to build `System::Remark` extrinsics but builds `{}` extrinsics instead", ext_builder.name()).into());
}
let bench = Benchmark::new(
client,
self.params.bench.clone(),
inherent_data,
digest_items,
should_record_proof,
);
// per-block execution overhead
{
let (stats, proof_size) = bench.bench_block()?;
info!(target: LOG_TARGET, "Per-block execution overhead [ns]:\n{:?}", stats);
let template = TemplateData::new(
BenchmarkType::Block,
&chain_name,
&self.params,
&stats,
proof_size,
)?;
template.write(&self.params.weight.weight_path)?;
}
// per-extrinsic execution overhead
{
let (stats, proof_size) = bench.bench_extrinsic(ext_builder)?;
info!(target: LOG_TARGET, "Per-extrinsic execution overhead [ns]:\n{:?}", stats);
let template = TemplateData::new(
BenchmarkType::Extrinsic,
&chain_name,
&self.params,
&stats,
proof_size,
)?;
template.write(&self.params.weight.weight_path)?;
}
Ok(())
}
}
impl BenchmarkType {
/// Short name of the benchmark type.
pub(crate) fn short_name(&self) -> &'static str {
match self {
Self::Extrinsic => "extrinsic",
Self::Block => "block",
}
}
/// Long name of the benchmark type.
pub(crate) fn long_name(&self) -> &'static str {
match self {
Self::Extrinsic => "ExtrinsicBase",
Self::Block => "BlockExecution",
}
}
}
#[derive(Clone, PartialEq, Debug)]
enum ChainType {
Teyrchain(u32),
Relaychain,
Unknown,
}
impl Display for ChainType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ChainType::Teyrchain(id) => write!(f, "Teyrchain(paraid = {})", id),
ChainType::Relaychain => write!(f, "Relaychain"),
ChainType::Unknown => write!(f, "Unknown"),
}
}
}
impl ChainType {
fn requires_proof_recording(&self) -> bool {
match self {
Teyrchain(_) => true,
Relaychain => false,
Unknown => false,
}
}
}
/// Patch the teyrchain id into the genesis config. This is necessary since the inherents
/// also contain a teyrchain id and they need to match.
fn patch_genesis(mut input_value: Value, para_id: Option<u32>) -> Value {
// If we identified a teyrchain we should patch a teyrchain id into the genesis config.
// This ensures compatibility with the inherents that we provide to successfully build a
// block.
if let Some(para_id) = para_id {
pezsc_chain_spec::json_patch::merge(
&mut input_value,
json!({
"teyrchainInfo": {
"teyrchainId": para_id,
}
}),
);
log::debug!(target: LOG_TARGET, "Genesis Config Json");
log::debug!(target: LOG_TARGET, "{}", input_value);
}
input_value
}
// Boilerplate
impl CliConfiguration for OverheadCmd {
fn shared_params(&self) -> &SharedParams {
&self.shared_params
}
fn import_params(&self) -> Option<&ImportParams> {
Some(&self.import_params)
}
fn base_path(&self) -> Result<Option<BasePath>> {
Ok(Some(BasePath::new_temp_dir()?))
}
fn trie_cache_maximum_size(&self) -> Result<Option<usize>> {
if self.params.enable_trie_cache {
Ok(self.import_params().map(|x| x.trie_cache_maximum_size()).unwrap_or_default())
} else {
Ok(None)
}
}
}
#[cfg(test)]
mod tests {
use crate::{
overhead::command::{identify_chain, ChainType, TeyrchainHostFunctions, DEFAULT_PARA_ID},
OverheadCmd,
};
use clap::Parser;
use codec::Decode;
use pezsc_executor::WasmExecutor;
#[test]
fn test_chain_type_relaychain() {
let executor: WasmExecutor<TeyrchainHostFunctions> = WasmExecutor::builder().build();
let code_bytes = zagros_runtime::WASM_BINARY
.expect("To run this test, build the wasm binary of zagros-runtime")
.to_vec();
let opaque_metadata =
super::fetch_latest_metadata_from_code_blob(&executor, code_bytes.into()).unwrap();
let metadata = subxt::Metadata::decode(&mut (*opaque_metadata).as_slice()).unwrap();
let chain_type = identify_chain(&metadata, None);
assert_eq!(chain_type, ChainType::Relaychain);
assert_eq!(chain_type.requires_proof_recording(), false);
}
#[test]
fn test_chain_type_teyrchain() {
let executor: WasmExecutor<TeyrchainHostFunctions> = WasmExecutor::builder().build();
let code_bytes = cumulus_test_runtime::WASM_BINARY
.expect("To run this test, build the wasm binary of pezcumulus-test-runtime")
.to_vec();
let opaque_metadata =
super::fetch_latest_metadata_from_code_blob(&executor, code_bytes.into()).unwrap();
let metadata = subxt::Metadata::decode(&mut (*opaque_metadata).as_slice()).unwrap();
let chain_type = identify_chain(&metadata, Some(100));
assert_eq!(chain_type, ChainType::Teyrchain(100));
assert!(chain_type.requires_proof_recording());
assert_eq!(identify_chain(&metadata, None), ChainType::Teyrchain(DEFAULT_PARA_ID));
}
#[test]
fn test_chain_type_custom() {
let executor: WasmExecutor<TeyrchainHostFunctions> = WasmExecutor::builder().build();
let code_bytes = bizinikiwi_test_runtime::WASM_BINARY
.expect("To run this test, build the wasm binary of bizinikiwi-test-runtime")
.to_vec();
let opaque_metadata =
super::fetch_latest_metadata_from_code_blob(&executor, code_bytes.into()).unwrap();
let metadata = subxt::Metadata::decode(&mut (*opaque_metadata).as_slice()).unwrap();
let chain_type = identify_chain(&metadata, None);
assert_eq!(chain_type, ChainType::Unknown);
assert_eq!(chain_type.requires_proof_recording(), false);
}
fn cli_succeed(args: &[&str]) -> Result<(), clap::Error> {
let cmd = OverheadCmd::try_parse_from(args)?;
assert!(cmd.check_args(&None).is_ok());
Ok(())
}
fn cli_fail(args: &[&str]) {
let cmd = OverheadCmd::try_parse_from(args);
if let Ok(cmd) = cmd {
assert!(cmd.check_args(&None).is_err());
}
}
#[test]
fn test_cli_conflicts() -> Result<(), clap::Error> {
// Runtime tests
cli_succeed(&["test", "--runtime", "path/to/runtime", "--genesis-builder", "runtime"])?;
cli_succeed(&["test", "--runtime", "path/to/runtime"])?;
cli_succeed(&[
"test",
"--runtime",
"path/to/runtime",
"--genesis-builder-preset",
"preset",
])?;
cli_fail(&["test", "--runtime", "path/to/spec", "--genesis-builder", "spec"]);
cli_fail(&["test", "--runtime", "path/to/spec", "--genesis-builder", "spec-genesis"]);
cli_fail(&["test", "--runtime", "path/to/spec", "--genesis-builder", "spec-runtime"]);
// Spec tests
cli_succeed(&["test", "--chain", "path/to/spec"])?;
cli_succeed(&["test", "--chain", "path/to/spec", "--genesis-builder", "spec"])?;
cli_succeed(&["test", "--chain", "path/to/spec", "--genesis-builder", "spec-genesis"])?;
cli_succeed(&["test", "--chain", "path/to/spec", "--genesis-builder", "spec-runtime"])?;
cli_fail(&["test", "--chain", "path/to/spec", "--genesis-builder", "none"]);
cli_fail(&["test", "--chain", "path/to/spec", "--genesis-builder", "runtime"]);
cli_fail(&[
"test",
"--chain",
"path/to/spec",
"--genesis-builder",
"runtime",
"--genesis-builder-preset",
"preset",
]);
Ok(())
}
}
@@ -0,0 +1,112 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// 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.
//! A fake runtime struct that allows us to instantiate a client.
//! Has all the required runtime APIs implemented to satisfy trait bounds,
//! but the methods are never called since we use WASM exclusively.
use pezsp_core::OpaqueMetadata;
use pezsp_runtime::{
generic,
traits::{BlakeTwo256, Block as BlockT},
transaction_validity::{TransactionSource, TransactionValidity},
ApplyExtrinsicResult, OpaqueExtrinsic,
};
/// Block number
#[allow(dead_code)]
type BlockNumber = u32;
/// Opaque block header type.
#[allow(dead_code)]
type Header = generic::Header<BlockNumber, BlakeTwo256>;
/// Opaque block type.
#[allow(dead_code)]
type Block = generic::Block<Header, OpaqueExtrinsic>;
#[allow(unused)]
pub struct Runtime;
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 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_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!()
}
}
}
@@ -0,0 +1,24 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// 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 command;
pub mod template;
mod fake_runtime_api;
pub mod remark_builder;
pub use command::{OpaqueBlock, OverheadCmd};
@@ -0,0 +1,127 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// 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::extrinsic::ExtrinsicBuilder;
use codec::{Decode, Encode};
use pezsc_client_api::UsageProvider;
use pezsp_api::{ApiExt, Core, Metadata, ProvideRuntimeApi};
use pezsp_runtime::{traits::Block as BlockT, OpaqueExtrinsic};
use std::sync::Arc;
use subxt::{
client::RuntimeVersion as SubxtRuntimeVersion,
config::{bizinikiwi::BizinikiwiExtrinsicParamsBuilder, HashFor},
Config, OfflineClient, BizinikiwiConfig,
};
pub type BizinikiwiRemarkBuilder = DynamicRemarkBuilder<BizinikiwiConfig>;
/// Remark builder that can be used to build simple extrinsics for
/// FRAME-based runtimes.
pub struct DynamicRemarkBuilder<C: Config> {
offline_client: OfflineClient<C>,
}
impl<C: Config> DynamicRemarkBuilder<C> {
/// Initializes a new remark builder from a client.
///
/// This will first fetch metadata and runtime version from the runtime and then
/// construct an offline client that provides the extrinsics.
pub fn new_from_client<Client, Block>(client: Arc<Client>) -> pezsc_cli::Result<Self>
where
Block: BlockT,
Client: UsageProvider<Block> + ProvideRuntimeApi<Block>,
Client::Api: Metadata<Block> + Core<Block>,
{
let genesis = client.usage_info().chain.best_hash;
let api = client.runtime_api();
let Ok(Some(metadata_api_version)) = api.api_version::<dyn Metadata<Block>>(genesis) else {
return Err("Unable to fetch metadata runtime API version.".to_string().into());
};
log::debug!("Found metadata API version {}.", metadata_api_version);
let opaque_metadata = if metadata_api_version > 1 {
let Ok(supported_metadata_versions) = api.metadata_versions(genesis) else {
return Err("Unable to fetch metadata versions".to_string().into());
};
let latest = supported_metadata_versions
.into_iter()
.max()
.ok_or("No stable metadata versions supported".to_string())?;
api.metadata_at_version(genesis, latest)
.map_err(|e| format!("Unable to fetch metadata: {:?}", e))?
.ok_or("Unable to decode metadata".to_string())?
} else {
// Fall back to using the non-versioned metadata API.
api.metadata(genesis)
.map_err(|e| format!("Unable to fetch metadata: {:?}", e))?
};
let version = api.version(genesis).unwrap();
let runtime_version = SubxtRuntimeVersion {
spec_version: version.spec_version,
transaction_version: version.transaction_version,
};
let metadata = subxt::Metadata::decode(&mut (*opaque_metadata).as_slice())?;
let genesis = HashFor::<C>::decode(&mut &genesis.encode()[..])
.map_err(|_| "Incompatible hash types?")?;
Ok(Self { offline_client: OfflineClient::new(genesis, runtime_version, metadata) })
}
}
impl<C: Config> DynamicRemarkBuilder<C> {
/// Constructs a new remark builder.
pub fn new(
metadata: subxt::Metadata,
genesis_hash: HashFor<C>,
runtime_version: SubxtRuntimeVersion,
) -> Self {
Self { offline_client: OfflineClient::new(genesis_hash, runtime_version, metadata) }
}
}
impl ExtrinsicBuilder for DynamicRemarkBuilder<BizinikiwiConfig> {
fn pallet(&self) -> &str {
"system"
}
fn extrinsic(&self) -> &str {
"remark"
}
fn build(&self, nonce: u32) -> std::result::Result<OpaqueExtrinsic, &'static str> {
let signer = subxt_signer::sr25519::dev::alice();
let dynamic_tx = subxt::dynamic::tx("System", "remark", vec![Vec::<u8>::new()]);
let params = BizinikiwiExtrinsicParamsBuilder::new().nonce(nonce.into()).build();
// Default transaction parameters assume a nonce of 0.
let transaction = self
.offline_client
.tx()
.create_partial_offline(&dynamic_tx, params)
.unwrap()
.sign(&signer);
let mut encoded = transaction.into_encoded();
OpaqueExtrinsic::try_from_encoded_extrinsic(&mut encoded)
.map_err(|_| "Unable to construct OpaqueExtrinsic")
}
}
@@ -0,0 +1,128 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// 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.
//! Converts a benchmark result into [`TemplateData`] and writes
//! it into the `weights.hbs` template.
use pezsc_cli::Result;
use handlebars::Handlebars;
use log::info;
use serde::Serialize;
use std::{env, fs, path::PathBuf};
use crate::{
overhead::command::{BenchmarkType, OverheadParams},
shared::{Stats, UnderscoreHelper},
};
static VERSION: &str = env!("CARGO_PKG_VERSION");
static TEMPLATE: &str = include_str!("./weights.hbs");
/// Data consumed by Handlebar to fill out the `weights.hbs` template.
#[derive(Serialize, Debug, Clone)]
pub(crate) struct TemplateData {
/// Short name of the benchmark. Can be "block" or "extrinsic".
long_name: String,
/// Long name of the benchmark. Can be "BlockExecution" or "ExtrinsicBase".
short_name: String,
/// Name of the runtime. Taken from the chain spec.
runtime_name: String,
/// Version of the benchmarking CLI used.
version: String,
/// Date that the template was filled out.
date: String,
/// Hostname of the machine that executed the benchmarks.
hostname: String,
/// CPU name of the machine that executed the benchmarks.
cpuname: String,
/// Header for the generated file.
header: String,
/// Command line arguments that were passed to the CLI.
args: Vec<String>,
/// Params of the executed command.
params: OverheadParams,
/// Stats about the benchmark result.
stats: Stats,
/// The resulting ref time weight.
ref_time: u64,
/// The size of the proof weight.
proof_size: u64,
}
impl TemplateData {
/// Returns a new [`Self`] from the given params.
pub(crate) fn new(
t: BenchmarkType,
chain_name: &String,
params: &OverheadParams,
stats: &Stats,
proof_size: u64,
) -> Result<Self> {
let ref_time = params.weight.calc_weight(stats)?;
let header = params
.header
.as_ref()
.map(|p| std::fs::read_to_string(p))
.transpose()?
.unwrap_or_default();
Ok(TemplateData {
short_name: t.short_name().into(),
long_name: t.long_name().into(),
runtime_name: chain_name.to_owned(),
version: VERSION.into(),
date: chrono::Utc::now().format("%Y-%m-%d (Y/M/D)").to_string(),
hostname: params.hostinfo.hostname(),
cpuname: params.hostinfo.cpuname(),
header,
args: env::args().collect::<Vec<String>>(),
params: params.clone(),
stats: stats.clone(),
ref_time,
proof_size,
})
}
/// Fill out the `weights.hbs` HBS template with its own data.
/// Writes the result to `path` which can be a directory or a file.
pub fn write(&self, path: &Option<PathBuf>) -> Result<()> {
let mut handlebars = Handlebars::new();
// Format large integers with underscores.
handlebars.register_helper("underscore", Box::new(UnderscoreHelper));
// Don't HTML escape any characters.
handlebars.register_escape_fn(|s| -> String { s.to_string() });
let out_path = self.build_path(path)?;
let mut fd = fs::File::create(&out_path)?;
info!("Writing weights to {:?}", fs::canonicalize(&out_path)?);
handlebars
.render_template_to_write(TEMPLATE, &self, &mut fd)
.map_err(|e| format!("HBS template write: {:?}", e).into())
}
/// Build a path for the weight file.
fn build_path(&self, weight_out: &Option<PathBuf>) -> Result<PathBuf> {
let mut path = weight_out.clone().unwrap_or_else(|| PathBuf::from("."));
if !path.is_dir() {
return Err("Need directory as --weight-path".into());
}
path.push(format!("{}_weights.rs", self.short_name));
Ok(path)
}
}
@@ -0,0 +1,76 @@
{{header}}
//! THIS FILE WAS AUTO-GENERATED USING THE BIZINIKIWI BENCHMARK CLI VERSION {{version}}
//! DATE: {{date}}
//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}`
//!
//! SHORT-NAME: `{{short_name}}`, LONG-NAME: `{{long_name}}`, RUNTIME: `{{runtime_name}}`
//! WARMUPS: `{{params.bench.warmup}}`, REPEAT: `{{params.bench.repeat}}`
//! WEIGHT-PATH: `{{params.weight.weight_path}}`
//! WEIGHT-METRIC: `{{params.weight.weight_metric}}`, WEIGHT-MUL: `{{params.weight.weight_mul}}`, WEIGHT-ADD: `{{params.weight.weight_add}}`
// Executed Command:
{{#each args as |arg|}}
// {{arg}}
{{/each}}
use sp_core::parameter_types;
use sp_weights::{constants::WEIGHT_REF_TIME_PER_NANOS, Weight};
parameter_types! {
{{#if (eq short_name "block")}}
/// Weight of executing an empty block.
{{else}}
/// Weight of executing a NO-OP extrinsic, for example `System::remark`.
{{/if}}
/// Calculated by multiplying the *{{params.weight.weight_metric}}* with `{{params.weight.weight_mul}}` and adding `{{params.weight.weight_add}}`.
///
/// Stats nanoseconds:
/// Min, Max: {{underscore stats.min}}, {{underscore stats.max}}
/// Average: {{underscore stats.avg}}
/// Median: {{underscore stats.median}}
/// Std-Dev: {{stats.stddev}}
///
/// Percentiles nanoseconds:
/// 99th: {{underscore stats.p99}}
/// 95th: {{underscore stats.p95}}
/// 75th: {{underscore stats.p75}}
pub const {{long_name}}Weight: Weight =
Weight::from_parts(WEIGHT_REF_TIME_PER_NANOS.saturating_mul({{underscore ref_time}}), {{underscore proof_size}});
}
#[cfg(test)]
mod test_weights {
use sp_weights::constants;
/// Checks that the weight exists and is sane.
// NOTE: If this test fails but you are sure that the generated values are fine,
// you can delete it.
#[test]
fn sane() {
let w = super::{{long_name}}Weight::get();
{{#if (eq short_name "block")}}
// At least 100 µs.
assert!(
w.ref_time() >= 100u64 * constants::WEIGHT_REF_TIME_PER_MICROS,
"Weight should be at least 100 µs."
);
// At most 50 ms.
assert!(
w.ref_time() <= 50u64 * constants::WEIGHT_REF_TIME_PER_MILLIS,
"Weight should be at most 50 ms."
);
{{else}}
// At least 10 µs.
assert!(
w.ref_time() >= 10u64 * constants::WEIGHT_REF_TIME_PER_MICROS,
"Weight should be at least 10 µs."
);
// At most 1 ms.
assert!(
w.ref_time() <= constants::WEIGHT_REF_TIME_PER_MILLIS,
"Weight should be at most 1 ms."
);
{{/if}}
}
}