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:
@@ -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, ¶_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}}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user