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,7 @@
|
||||
doc
|
||||
**target*
|
||||
.idea/
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
.local
|
||||
.env*
|
||||
@@ -0,0 +1,27 @@
|
||||
root = true
|
||||
[*]
|
||||
indent_style=tab
|
||||
indent_size=tab
|
||||
tab_width=4
|
||||
end_of_line=lf
|
||||
charset=utf-8
|
||||
trim_trailing_whitespace=true
|
||||
max_line_length=100
|
||||
insert_final_newline=true
|
||||
|
||||
[*.md]
|
||||
max_line_length=80
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
|
||||
[*.yml]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
tab_width=8
|
||||
end_of_line=lf
|
||||
|
||||
[*.sh]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
tab_width=8
|
||||
end_of_line=lf
|
||||
@@ -0,0 +1,15 @@
|
||||
# You can easily exclude big automated code changes by running
|
||||
#
|
||||
# git config blame.ignoreRevsFile .git-blame-ignore-revs
|
||||
#
|
||||
# in your local repository. It will work also in IDE integrations.
|
||||
#
|
||||
# On versions of Git 2.20 and later comments (#), empty lines, and any leading and
|
||||
# trailing whitespace is ignored. Everything but a SHA-1 per line will error out on
|
||||
# older versions.
|
||||
#
|
||||
# You should add new commit hashes to this file when you create or find such big
|
||||
# automated refactorings while reading code history. If you only know the short hash,
|
||||
# use `git rev-parse 7b56ab15b4` to expand it to the full SHA1 hash needed in this file.
|
||||
|
||||
7b56ab15b4a8e06df5eefc17e600e3c1419aede5 # Run cargo fmt on the whole code base (#9394)
|
||||
@@ -0,0 +1,2 @@
|
||||
Cargo.lock linguist-generated=true
|
||||
frame/**/src/weights.rs linguist-generated=true
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# Script for building only the WASM binary of the given project.
|
||||
|
||||
set -e
|
||||
|
||||
PROJECT_ROOT=`git rev-parse --show-toplevel`
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
echo "You need to pass the name of the crate you want to compile!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WASM_BUILDER_RUNNER="$PROJECT_ROOT/target/release/wbuild-runner/$1"
|
||||
|
||||
if [ -z "$2" ]; then
|
||||
export WASM_TARGET_DIRECTORY=$(pwd)
|
||||
else
|
||||
export WASM_TARGET_DIRECTORY=$2
|
||||
fi
|
||||
|
||||
if [ -d $WASM_BUILDER_RUNNER ]; then
|
||||
export DEBUG=false
|
||||
export OUT_DIR="$PROJECT_ROOT/target/release/build"
|
||||
cargo run --release --manifest-path="$WASM_BUILDER_RUNNER/Cargo.toml" \
|
||||
| grep -vE "cargo:rerun-if-|Executing build command"
|
||||
else
|
||||
cargo build --release -p $1
|
||||
fi
|
||||
@@ -0,0 +1,55 @@
|
||||
<%
|
||||
const capFirst = s => (s && s[0].toUpperCase() + s.slice(1)) || "";
|
||||
%>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><%= capFirst(repo_name) %> Rustdocs</title>
|
||||
<meta name="description" content="Nothing here." />
|
||||
<meta name="robots" content="noindex">
|
||||
<style>
|
||||
body {
|
||||
font-family: Helvetica, Arial, Sans Serif;
|
||||
margin: 0;
|
||||
}
|
||||
.center-me {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
}
|
||||
.content {
|
||||
display: block;
|
||||
}
|
||||
.content li {
|
||||
font-size: 1em;
|
||||
line-height: .4em;
|
||||
padding: .8em 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="center-me">
|
||||
<div class="content">
|
||||
<h1><%= capFirst(repo_name) %> Rustdocs</h1>
|
||||
<section>
|
||||
<ul>
|
||||
<%_ deploy_refs.split(/\s+/).forEach(ref => { _%>
|
||||
<li>
|
||||
<a href="/<%= repo_name _%>/<%= ref _%>"><%- ref -%></a>
|
||||
<%_ if (latest && latest.trim() !== '' && latest === ref) { _%>
|
||||
(<a href="/<%= repo_name _%>/latest">latest</a>)
|
||||
<%_ } _%>
|
||||
</li>
|
||||
<%_ }) _%>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,138 @@
|
||||
// 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.
|
||||
|
||||
{{header}}
|
||||
//! Autogenerated weights for `{{pallet}}`
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE BIZINIKIWI BENCHMARK CLI VERSION {{version}}
|
||||
//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}`
|
||||
//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}`
|
||||
//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}`
|
||||
//! WASM-EXECUTION: `{{cmd.wasm_execution}}`, CHAIN: `{{cmd.chain}}`, DB CACHE: `{{cmd.db_cache}}`
|
||||
|
||||
// Executed Command:
|
||||
{{#each args as |arg|}}
|
||||
// {{arg}}
|
||||
{{/each}}
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(missing_docs)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use frame::weights_prelude::*;
|
||||
|
||||
/// Weight functions needed for `{{pallet}}`.
|
||||
pub trait WeightInfo {
|
||||
{{#each benchmarks as |benchmark|}}
|
||||
fn {{benchmark.name~}}
|
||||
(
|
||||
{{~#each benchmark.components as |c| ~}}
|
||||
{{c.name}}: u32, {{/each~}}
|
||||
) -> Weight;
|
||||
{{/each}}
|
||||
}
|
||||
|
||||
/// Weights for `{{pallet}}` using the Bizinikiwi node and recommended hardware.
|
||||
pub struct SubstrateWeight<T>(PhantomData<T>);
|
||||
{{#if (or (eq pallet "frame_system") (eq pallet "frame_system_extensions"))}}
|
||||
impl<T: crate::Config> WeightInfo for SubstrateWeight<T> {
|
||||
{{else}}
|
||||
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
{{/if}}
|
||||
{{#each benchmarks as |benchmark|}}
|
||||
{{#each benchmark.comments as |comment|}}
|
||||
/// {{comment}}
|
||||
{{/each}}
|
||||
{{#each benchmark.component_ranges as |range|}}
|
||||
/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.
|
||||
{{/each}}
|
||||
fn {{benchmark.name~}}
|
||||
(
|
||||
{{~#each benchmark.components as |c| ~}}
|
||||
{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}
|
||||
) -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`
|
||||
// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`
|
||||
// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.
|
||||
Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})
|
||||
{{#each benchmark.component_weight as |cw|}}
|
||||
// Standard Error: {{underscore cw.error}}
|
||||
.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))
|
||||
{{/each}}
|
||||
{{#if (ne benchmark.base_reads "0")}}
|
||||
.saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64))
|
||||
{{/if}}
|
||||
{{#each benchmark.component_reads as |cr|}}
|
||||
.saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))
|
||||
{{/each}}
|
||||
{{#if (ne benchmark.base_writes "0")}}
|
||||
.saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64))
|
||||
{{/if}}
|
||||
{{#each benchmark.component_writes as |cw|}}
|
||||
.saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))
|
||||
{{/each}}
|
||||
{{#each benchmark.component_calculated_proof_size as |cp|}}
|
||||
.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))
|
||||
{{/each}}
|
||||
}
|
||||
{{/each}}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests.
|
||||
impl WeightInfo for () {
|
||||
{{#each benchmarks as |benchmark|}}
|
||||
{{#each benchmark.comments as |comment|}}
|
||||
/// {{comment}}
|
||||
{{/each}}
|
||||
{{#each benchmark.component_ranges as |range|}}
|
||||
/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.
|
||||
{{/each}}
|
||||
fn {{benchmark.name~}}
|
||||
(
|
||||
{{~#each benchmark.components as |c| ~}}
|
||||
{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}
|
||||
) -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`
|
||||
// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`
|
||||
// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.
|
||||
Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})
|
||||
{{#each benchmark.component_weight as |cw|}}
|
||||
// Standard Error: {{underscore cw.error}}
|
||||
.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))
|
||||
{{/each}}
|
||||
{{#if (ne benchmark.base_reads "0")}}
|
||||
.saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64))
|
||||
{{/if}}
|
||||
{{#each benchmark.component_reads as |cr|}}
|
||||
.saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))
|
||||
{{/each}}
|
||||
{{#if (ne benchmark.base_writes "0")}}
|
||||
.saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64))
|
||||
{{/if}}
|
||||
{{#each benchmark.component_writes as |cw|}}
|
||||
.saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))
|
||||
{{/each}}
|
||||
{{#each benchmark.component_calculated_proof_size as |cp|}}
|
||||
.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))
|
||||
{{/each}}
|
||||
}
|
||||
{{/each}}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
// 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.
|
||||
|
||||
{{header}}
|
||||
//! Autogenerated weights for `{{pallet}}`
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE BIZINIKIWI BENCHMARK CLI VERSION {{version}}
|
||||
//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}`
|
||||
//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}`
|
||||
//! HOSTNAME: `{{hostname}}`, CPU: `{{cpuname}}`
|
||||
//! WASM-EXECUTION: `{{cmd.wasm_execution}}`, CHAIN: `{{cmd.chain}}`, DB CACHE: `{{cmd.db_cache}}`
|
||||
|
||||
// Executed Command:
|
||||
{{#each args as |arg|}}
|
||||
// {{arg}}
|
||||
{{/each}}
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(missing_docs)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Weight functions needed for `{{pallet}}`.
|
||||
pub trait WeightInfo {
|
||||
{{#each benchmarks as |benchmark|}}
|
||||
fn {{benchmark.name~}}
|
||||
(
|
||||
{{~#each benchmark.components as |c| ~}}
|
||||
{{c.name}}: u32, {{/each~}}
|
||||
) -> Weight;
|
||||
{{/each}}
|
||||
}
|
||||
|
||||
/// Weights for `{{pallet}}` using the Bizinikiwi node and recommended hardware.
|
||||
pub struct SubstrateWeight<T>(PhantomData<T>);
|
||||
{{#if (or (eq pallet "frame_system") (eq pallet "frame_system_extensions"))}}
|
||||
impl<T: crate::Config> WeightInfo for SubstrateWeight<T> {
|
||||
{{else}}
|
||||
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
{{/if}}
|
||||
{{#each benchmarks as |benchmark|}}
|
||||
{{#each benchmark.comments as |comment|}}
|
||||
/// {{comment}}
|
||||
{{/each}}
|
||||
{{#each benchmark.component_ranges as |range|}}
|
||||
/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.
|
||||
{{/each}}
|
||||
fn {{benchmark.name~}}
|
||||
(
|
||||
{{~#each benchmark.components as |c| ~}}
|
||||
{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}
|
||||
) -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`
|
||||
// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`
|
||||
// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.
|
||||
Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})
|
||||
{{#each benchmark.component_weight as |cw|}}
|
||||
// Standard Error: {{underscore cw.error}}
|
||||
.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))
|
||||
{{/each}}
|
||||
{{#if (ne benchmark.base_reads "0")}}
|
||||
.saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64))
|
||||
{{/if}}
|
||||
{{#each benchmark.component_reads as |cr|}}
|
||||
.saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))
|
||||
{{/each}}
|
||||
{{#if (ne benchmark.base_writes "0")}}
|
||||
.saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64))
|
||||
{{/if}}
|
||||
{{#each benchmark.component_writes as |cw|}}
|
||||
.saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))
|
||||
{{/each}}
|
||||
{{#each benchmark.component_calculated_proof_size as |cp|}}
|
||||
.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))
|
||||
{{/each}}
|
||||
}
|
||||
{{/each}}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests.
|
||||
impl WeightInfo for () {
|
||||
{{#each benchmarks as |benchmark|}}
|
||||
{{#each benchmark.comments as |comment|}}
|
||||
/// {{comment}}
|
||||
{{/each}}
|
||||
{{#each benchmark.component_ranges as |range|}}
|
||||
/// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`.
|
||||
{{/each}}
|
||||
fn {{benchmark.name~}}
|
||||
(
|
||||
{{~#each benchmark.components as |c| ~}}
|
||||
{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}
|
||||
) -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`
|
||||
// Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}`
|
||||
// Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds.
|
||||
Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}})
|
||||
{{#each benchmark.component_weight as |cw|}}
|
||||
// Standard Error: {{underscore cw.error}}
|
||||
.saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into()))
|
||||
{{/each}}
|
||||
{{#if (ne benchmark.base_reads "0")}}
|
||||
.saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64))
|
||||
{{/if}}
|
||||
{{#each benchmark.component_reads as |cr|}}
|
||||
.saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into())))
|
||||
{{/each}}
|
||||
{{#if (ne benchmark.base_writes "0")}}
|
||||
.saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64))
|
||||
{{/if}}
|
||||
{{#each benchmark.component_writes as |cw|}}
|
||||
.saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into())))
|
||||
{{/each}}
|
||||
{{#each benchmark.component_calculated_proof_size as |cp|}}
|
||||
.saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into()))
|
||||
{{/each}}
|
||||
}
|
||||
{{/each}}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
# Docker compose file to start a multi node local test network.
|
||||
#
|
||||
# # Nodes
|
||||
#
|
||||
# - Validator node A
|
||||
# - Validator node B
|
||||
# - Light client C
|
||||
#
|
||||
# # Auxiliary nodes
|
||||
#
|
||||
# - Prometheus monitoring each node.
|
||||
# - Grafana pointed at the Prometheus node, configured with all dashboards.
|
||||
#
|
||||
# # Usage
|
||||
#
|
||||
# 1. Build `target/release/bizinikiwi` binary: `cargo build --release`
|
||||
# 2. Start networks and containers:
|
||||
# `sudo docker-compose -f .maintain/sentry-node/docker-compose.yml up`
|
||||
# 3. Connect to nodes:
|
||||
# - validator-a: localhost:9944
|
||||
# - validator-b: localhost:9945
|
||||
# - light-c: localhost:9946
|
||||
# - via polkadot.js/apps: https://pezkuwichain.io/?rpc=ws%3A%2F%2Flocalhost%3A<NODE_PORT>#/explorer
|
||||
# - grafana: localhost:3001
|
||||
# - prometheus: localhost:9090
|
||||
|
||||
|
||||
version: "3.7"
|
||||
services:
|
||||
|
||||
validator-a:
|
||||
ports:
|
||||
- "9944:9944"
|
||||
- "9615:9615"
|
||||
volumes:
|
||||
- ../../target/release/bizinikiwi:/usr/local/bin/bizinikiwi
|
||||
image: pezkuwichain/bizinikiwi
|
||||
networks:
|
||||
- internet
|
||||
command:
|
||||
- "--node-key"
|
||||
- "0000000000000000000000000000000000000000000000000000000000000001"
|
||||
- "--base-path"
|
||||
- "/tmp/alice"
|
||||
- "--chain=local"
|
||||
- "--port"
|
||||
- "30333"
|
||||
- "--validator"
|
||||
- "--alice"
|
||||
- "--bootnodes"
|
||||
- "/dns/validator-b/tcp/30333/p2p/12D3KooWHdiAxVd8uMQR1hGWXccidmfCwLqcMpGwR6QcTP6QRMuD"
|
||||
# Not only bind to localhost.
|
||||
- "--unsafe-ws-external"
|
||||
- "--unsafe-rpc-external"
|
||||
- "--log"
|
||||
- "sub-libp2p=trace"
|
||||
- "--no-telemetry"
|
||||
- "--rpc-cors"
|
||||
- "all"
|
||||
- "--prometheus-external"
|
||||
|
||||
validator-b:
|
||||
image: pezkuwichain/bizinikiwi
|
||||
ports:
|
||||
- "9945:9944"
|
||||
volumes:
|
||||
- ../../target/release/bizinikiwi:/usr/local/bin/bizinikiwi
|
||||
networks:
|
||||
- internet
|
||||
command:
|
||||
- "--node-key"
|
||||
- "0000000000000000000000000000000000000000000000000000000000000002"
|
||||
- "--base-path"
|
||||
- "/tmp/bob"
|
||||
- "--chain=local"
|
||||
- "--port"
|
||||
- "30333"
|
||||
- "--validator"
|
||||
- "--bob"
|
||||
- "--bootnodes"
|
||||
- "/dns/validator-a/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp"
|
||||
- "--no-telemetry"
|
||||
- "--rpc-cors"
|
||||
- "all"
|
||||
# Not only bind to localhost.
|
||||
- "--unsafe-ws-external"
|
||||
- "--unsafe-rpc-external"
|
||||
- "--log"
|
||||
- "sub-libp2p=trace"
|
||||
- "--prometheus-external"
|
||||
|
||||
light-c:
|
||||
image: pezkuwichain/bizinikiwi
|
||||
ports:
|
||||
- "9946:9944"
|
||||
volumes:
|
||||
- ../../target/release/bizinikiwi:/usr/local/bin/bizinikiwi
|
||||
networks:
|
||||
- internet
|
||||
command:
|
||||
- "--node-key"
|
||||
- "0000000000000000000000000000000000000000000000000000000000000003"
|
||||
- "--base-path"
|
||||
- "/tmp/light"
|
||||
- "--chain=local"
|
||||
- "--port"
|
||||
- "30333"
|
||||
- "--light"
|
||||
- "--bootnodes"
|
||||
- "/dns/validator-a/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp"
|
||||
- "--bootnodes"
|
||||
- "/dns/validator-b/tcp/30333/p2p/12D3KooWHdiAxVd8uMQR1hGWXccidmfCwLqcMpGwR6QcTP6QRMuD"
|
||||
- "--no-telemetry"
|
||||
- "--rpc-cors"
|
||||
- "all"
|
||||
# Not only bind to localhost.
|
||||
- "--unsafe-ws-external"
|
||||
- "--unsafe-rpc-external"
|
||||
- "--log"
|
||||
- "sub-libp2p=trace"
|
||||
- "--prometheus-external"
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus
|
||||
networks:
|
||||
- internet
|
||||
ports:
|
||||
- "9090:9090"
|
||||
links:
|
||||
- validator-a:validator-a
|
||||
- validator-b:validator-b
|
||||
- light-c:light-c
|
||||
volumes:
|
||||
- ./prometheus/:/etc/prometheus/
|
||||
restart: always
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana
|
||||
user: "104"
|
||||
depends_on:
|
||||
- prometheus
|
||||
networks:
|
||||
- internet
|
||||
ports:
|
||||
- 3001:3000
|
||||
volumes:
|
||||
- ./grafana/provisioning/:/etc/grafana/provisioning
|
||||
- ../monitoring/grafana-dashboards/:/etc/grafana/provisioning/dashboard-definitions
|
||||
restart: always
|
||||
|
||||
networks:
|
||||
network-a:
|
||||
internet:
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
apiVersion: 1
|
||||
|
||||
providers:
|
||||
- name: 'Prometheus'
|
||||
orgId: 1
|
||||
folder: ''
|
||||
type: file
|
||||
disableDeletion: false
|
||||
editable: false
|
||||
options:
|
||||
path: /etc/grafana/provisioning/dashboard-definitions
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
# config file version
|
||||
apiVersion: 1
|
||||
|
||||
# list of datasources that should be deleted from the database
|
||||
deleteDatasources:
|
||||
- name: Prometheus
|
||||
orgId: 1
|
||||
|
||||
# list of datasources to insert/update depending
|
||||
# whats available in the database
|
||||
datasources:
|
||||
# <string, required> name of the datasource. Required
|
||||
- name: Prometheus
|
||||
# <string, required> datasource type. Required
|
||||
type: prometheus
|
||||
# <string, required> access mode. direct or proxy. Required
|
||||
access: proxy
|
||||
# <int> org id. will default to orgId 1 if not specified
|
||||
orgId: 1
|
||||
# <string> url
|
||||
url: http://prometheus:9090
|
||||
# <string> database password, if used
|
||||
password:
|
||||
# <string> database user, if used
|
||||
user:
|
||||
# <string> database name, if used
|
||||
database:
|
||||
# <bool> enable/disable basic auth
|
||||
basicAuth: false
|
||||
# <string> basic auth username, if used
|
||||
basicAuthUser:
|
||||
# <string> basic auth password, if used
|
||||
basicAuthPassword:
|
||||
# <bool> enable/disable with credentials headers
|
||||
withCredentials:
|
||||
# <bool> mark as default datasource. Max one per org
|
||||
isDefault: true
|
||||
# <map> fields that will be converted to json and stored in json_data
|
||||
jsonData:
|
||||
graphiteVersion: "1.1"
|
||||
tlsAuth: false
|
||||
tlsAuthWithCACert: false
|
||||
# <string> json object of data that will be encrypted.
|
||||
secureJsonData:
|
||||
tlsCACert: "..."
|
||||
tlsClientCert: "..."
|
||||
tlsClientKey: "..."
|
||||
version: 1
|
||||
# <bool> allow users to edit datasources from the UI.
|
||||
editable: true
|
||||
@@ -0,0 +1,15 @@
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: 'bizinikiwi-nodes'
|
||||
static_configs:
|
||||
- targets: ['validator-a:9615']
|
||||
labels:
|
||||
network: dev
|
||||
- targets: ['validator-b:9615']
|
||||
labels:
|
||||
network: dev
|
||||
- targets: ['light-c:9615']
|
||||
labels:
|
||||
network: dev
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function rust_rename() {
|
||||
sed -i "s/$1/$2/g" `grep -Rl --include="*.rs" --include="*.stderr" "$1" *` > /dev/null
|
||||
}
|
||||
|
||||
function cargo_rename() {
|
||||
find . -name "Cargo.toml" -exec sed -i "s/\(^\|[^\/]\)$1/\1$2/g" {} \;
|
||||
}
|
||||
|
||||
function rename_gitlabci() {
|
||||
sed -i "s/$1/$2/g" .gitlab-ci.yml
|
||||
}
|
||||
|
||||
function rename() {
|
||||
old=$(echo $1 | cut -f1 -d\ );
|
||||
new=$(echo $1 | cut -f2 -d\ );
|
||||
|
||||
echo "Renaming $old to $new"
|
||||
# rename in Cargo.tomls
|
||||
cargo_rename $old $new
|
||||
rename_gitlabci $old $new
|
||||
# and it appears, we have the same syntax in rust files
|
||||
rust_rename $old $new
|
||||
|
||||
# but generally we have the snail case syntax in rust files
|
||||
old=$(echo $old | sed s/-/_/g );
|
||||
new=$(echo $new | sed s/-/_/g );
|
||||
|
||||
echo " > $old to $new"
|
||||
rust_rename $old $new
|
||||
}
|
||||
|
||||
TO_RENAME=(
|
||||
# OLD-CRATE-NAME NEW-CRATE-NAME
|
||||
|
||||
# post initial rename fixes
|
||||
"sc-application-crypto sp-application-crypto"
|
||||
"sp-transaction-pool-api sp-transaction-pool"
|
||||
"sp-transaction-pool-runtime-api sp-transaction-pool"
|
||||
"sp-core-storage sp-storage"
|
||||
"transaction-factory node-transaction-factory"
|
||||
"sp-finality-granpda sp-finality-grandpa"
|
||||
"sp-sesssion sp-session"
|
||||
"sp-tracing-pool sp-transaction-pool"
|
||||
"sc-basic-authority sc-basic-authorship"
|
||||
"sc-api sc-client-api"
|
||||
"sc-database sc-client-db"
|
||||
|
||||
# PRIMITIVES
|
||||
"bizinikiwi-application-crypto sp-application-crypto"
|
||||
"bizinikiwi-authority-discovery-primitives sp-authority-discovery"
|
||||
"bizinikiwi-block-builder-runtime-api sp-block-builder"
|
||||
"bizinikiwi-consensus-aura-primitives sp-consensus-aura"
|
||||
"bizinikiwi-consensus-babe-primitives sp-consensus-babe"
|
||||
"bizinikiwi-consensus-common sp-consensus"
|
||||
"bizinikiwi-consensus-pow-primitives sp-consensus-pow"
|
||||
"bizinikiwi-primitives sp-core"
|
||||
"bizinikiwi-debug-derive sp-debug-derive"
|
||||
"bizinikiwi-primitives-storage sp-storage"
|
||||
"bizinikiwi-externalities sp-externalities"
|
||||
"bizinikiwi-finality-grandpa-primitives sp-finality-grandpa"
|
||||
"bizinikiwi-inherents sp-inherents"
|
||||
"bizinikiwi-keyring sp-keyring"
|
||||
"bizinikiwi-offchain-primitives sp-offchain"
|
||||
"bizinikiwi-panic-handler sp-panic-handler"
|
||||
"bizinikiwi-phragmen sp-npos-elections"
|
||||
"bizinikiwi-rpc-primitives sp-rpc"
|
||||
"bizinikiwi-runtime-interface sp-runtime-interface"
|
||||
"bizinikiwi-runtime-interface-proc-macro sp-runtime-interface-proc-macro"
|
||||
"bizinikiwi-runtime-interface-test-wasm sp-runtime-interface-test-wasm"
|
||||
"bizinikiwi-serializer sp-serializer"
|
||||
"bizinikiwi-session sp-session"
|
||||
"sr-api sp-api"
|
||||
"sr-api-proc-macro sp-api-proc-macro"
|
||||
"sr-api-test sp-api-test"
|
||||
"sr-arithmetic sp-arithmetic"
|
||||
"sr-arithmetic-fuzzer sp-arithmetic-fuzzer"
|
||||
"sr-io sp-io"
|
||||
"sr-primitives sp-runtime"
|
||||
"sr-sandbox sp-sandbox"
|
||||
"sr-staking-primitives sp-staking"
|
||||
"sr-std sp-std"
|
||||
"sr-version sp-version"
|
||||
"bizinikiwi-state-machine sp-state-machine"
|
||||
"bizinikiwi-transaction-pool-runtime-api sp-transaction-pool"
|
||||
"bizinikiwi-trie sp-trie"
|
||||
"bizinikiwi-wasm-interface sp-wasm-interface"
|
||||
|
||||
# # CLIENT
|
||||
"bizinikiwi-client sc-client"
|
||||
"bizinikiwi-client-api sc-client-api"
|
||||
"bizinikiwi-authority-discovery sc-authority-discovery"
|
||||
"bizinikiwi-basic-authorship sc-basic-authorship"
|
||||
"bizinikiwi-block-builder sc-block-builder"
|
||||
"bizinikiwi-chain-spec sc-chain-spec"
|
||||
"bizinikiwi-chain-spec-derive sc-chain-spec-derive"
|
||||
"bizinikiwi-cli sc-cli"
|
||||
"bizinikiwi-consensus-aura sc-consensus-aura"
|
||||
"bizinikiwi-consensus-babe sc-consensus-babe"
|
||||
"bizinikiwi-consensus-pow sc-consensus-pow"
|
||||
"bizinikiwi-consensus-slots sc-consensus-slots"
|
||||
"bizinikiwi-consensus-uncles sc-consensus-uncles"
|
||||
"bizinikiwi-client-db sc-client-db"
|
||||
"bizinikiwi-executor sc-executor"
|
||||
"bizinikiwi-runtime-test sc-runtime-test"
|
||||
"bizinikiwi-finality-grandpa sc-finality-grandpa"
|
||||
"bizinikiwi-keystore sc-keystore"
|
||||
"bizinikiwi-network sc-network"
|
||||
"bizinikiwi-offchain sc-offchain"
|
||||
"bizinikiwi-peerset sc-peerset"
|
||||
"bizinikiwi-rpc-servers sc-rpc-server"
|
||||
"bizinikiwi-rpc sc-rpc"
|
||||
"bizinikiwi-service sc-service"
|
||||
"bizinikiwi-service-test sc-service-test"
|
||||
"bizinikiwi-state-db sc-state-db"
|
||||
"bizinikiwi-telemetry sc-telemetry"
|
||||
"bizinikiwi-test-primitives sp-test-primitives"
|
||||
"bizinikiwi-tracing sc-tracing"
|
||||
|
||||
);
|
||||
|
||||
for rule in "${TO_RENAME[@]}"
|
||||
do
|
||||
rename "$rule";
|
||||
done
|
||||
Executable
+245
@@ -0,0 +1,245 @@
|
||||
#!/usr/bin/env bash
|
||||
# set -x
|
||||
|
||||
# This script used to manage the deployment of Bizinikiwi rustdocs to https://paritytech.github.io/bizinikiwi/.
|
||||
# It is no longer used anywhere, and only here for historical/demonstration purposes.
|
||||
# - With `deploy` sub-command, it will checkout the passed-in branch/tag ref, build the rustdocs
|
||||
# locally (this takes some time), update the `index.html` index page, and push it to remote
|
||||
# `gh-pages` branch. So users running this command need to have write access to the remote
|
||||
# `gh-pages` branch. This sub-command depends on [@bizinikiwi/index-tpl-crud](https://www.npmjs.com/package/@bizinikiwi/index-tpl-crud)
|
||||
# to update the DOM of index.html page.
|
||||
# - With `remove` sub-command, it will remove the deployed rustdocs from `gh-pages`, and update the
|
||||
# index.html page as necessary. It may remove the `latest` symbolic link.
|
||||
#
|
||||
# Examples:
|
||||
# # Showing help text
|
||||
# rustdocs-release.sh -h
|
||||
#
|
||||
# # Deploy rustdocs of `monthly-2021-10` tag
|
||||
# rustdocs-release.sh deploy monthly-2021-10
|
||||
#
|
||||
# # In addition to the above, the `latest` symlink will point to this version of rustdocs
|
||||
# rustdocs-release.sh deploy -l monthly-2021-10
|
||||
#
|
||||
# # Remove the rustdocs of `monthly-2021-10` from `gh-pages`.
|
||||
# rustdocs-release.sh remove monthly-2021-10
|
||||
#
|
||||
# Dependencies:
|
||||
# - @bizinikiwi/index-tpl-crud - https://www.npmjs.com/package/@bizinikiwi/index-tpl-crud
|
||||
#
|
||||
|
||||
# Script setting
|
||||
# The git repo http URL
|
||||
REMOTE_REPO="https://github.com/paritytech/bizinikiwi.git"
|
||||
TMP_PREFIX="/tmp" # tmp location that the built doc is copied to.
|
||||
DOC_INDEX_PAGE="sc_service/index.html"
|
||||
|
||||
# Set to `true` if using cargo `nightly` toolchain to build the doc.
|
||||
# Set to `false` to use the default cargo toolchain. This is preferred if you want to build
|
||||
# the rustdocs with a pinned nightly version set to your default toolchain.
|
||||
CARGO_NIGHTLY=false
|
||||
|
||||
# Set the git remote name. Most of the time the default is `origin`.
|
||||
GIT_REMOTE="origin"
|
||||
LATEST=false
|
||||
|
||||
# Setting the help text
|
||||
declare -A HELP_TXT
|
||||
HELP_TXT["deploy"]=$(cat <<-EOH
|
||||
Build and deploy the rustdocs of the specified branch/tag to \`gh-pages\` branch.
|
||||
|
||||
usage: $0 deploy [-l] <git_branch_ref>
|
||||
example: $0 deploy -l monthly-2021-10
|
||||
|
||||
options:
|
||||
-l The \`latest\` path will be sym'linked to this rustdocs version
|
||||
EOH
|
||||
)
|
||||
|
||||
HELP_TXT["remove"]=$(cat <<-EOH
|
||||
Remove the rustdocs of the specified version from \`gh-pages\` branch.
|
||||
|
||||
usage: $0 remove <git_branch_ref>
|
||||
example: $0 remove monthly-2021-10
|
||||
EOH
|
||||
)
|
||||
|
||||
set_and_check_rustdoc_ref() {
|
||||
[[ -z "$1" ]] && {
|
||||
echo -e "git branch_ref is not specified.\n"
|
||||
echo "${HELP_TXT[$2]}"
|
||||
exit 1
|
||||
}
|
||||
BUILD_RUSTDOC_REF=$1
|
||||
}
|
||||
|
||||
check_local_change() {
|
||||
# Check there is no local changes before proceeding
|
||||
[[ -n $(git status --porcelain) ]] \
|
||||
&& echo "Local changes exist, please either discard or commit them as this command will change the current checkout branch." \
|
||||
&& exit 1
|
||||
}
|
||||
|
||||
build_rustdocs() {
|
||||
# Build the docs
|
||||
time cargo $($CARGO_NIGHTLY && echo "+nightly") doc --workspace --all-features --verbose \
|
||||
|| { echo "Generate $1 rustdocs failed" && exit 1; }
|
||||
rm -f target/doc/.lock
|
||||
|
||||
# Moving the built doc to the tmp location
|
||||
mv target/doc "${2}"
|
||||
[[ -n "${DOC_INDEX_PAGE}" ]] \
|
||||
&& echo "<meta http-equiv=refresh content=0;url=${DOC_INDEX_PAGE}>" > "${2}/index.html"
|
||||
}
|
||||
|
||||
upsert_index_page() {
|
||||
# Check if `index-tpl-crud` exists
|
||||
which index-tpl-crud &> /dev/null || yarn global add @bizinikiwi/index-tpl-crud
|
||||
index-tpl-crud upsert $($1 && echo "-l") ./index.html "$2"
|
||||
}
|
||||
|
||||
rm_index_page() {
|
||||
which index-tpl-crud &> /dev/null || yarn global add @bizinikiwi/index-tpl-crud
|
||||
index-tpl-crud rm ./index.html "$1"
|
||||
}
|
||||
|
||||
git_add_commit_push() {
|
||||
git add --all
|
||||
git commit -m "$1" || echo "Nothing to commit"
|
||||
git push "${GIT_REMOTE}" gh-pages --force
|
||||
}
|
||||
|
||||
import_gh_key() {
|
||||
[[ -n $GITHUB_SSH_PRIV_KEY ]] && {
|
||||
eval $(ssh-agent)
|
||||
ssh-add - <<< $GITHUB_SSH_PRIV_KEY
|
||||
}
|
||||
|
||||
# Adding github.com as known_hosts
|
||||
ssh-keygen -F github.com &>/dev/null || {
|
||||
[[ -e ~/.ssh ]] || mkdir ~/.ssh
|
||||
[[ -e ~/.ssh/known_hosts ]] || touch ~/.ssh/known_hosts
|
||||
ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts
|
||||
}
|
||||
}
|
||||
|
||||
deploy_main() {
|
||||
check_local_change
|
||||
import_gh_key
|
||||
|
||||
CURRENT_GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
TMP_PROJECT_PATH="${TMP_PREFIX}/${PROJECT_NAME}"
|
||||
DOC_PATH="${TMP_PROJECT_PATH}/${BUILD_RUSTDOC_REF}"
|
||||
|
||||
# Build the tmp project path
|
||||
rm -rf "${TMP_PROJECT_PATH}" && mkdir "${TMP_PROJECT_PATH}"
|
||||
|
||||
# Copy .gitignore file to tmp
|
||||
[[ -e "${PROJECT_PATH}/.gitignore" ]] && cp "${PROJECT_PATH}/.gitignore" "${TMP_PROJECT_PATH}"
|
||||
|
||||
git fetch --all
|
||||
git checkout -f ${BUILD_RUSTDOC_REF} || { echo "Checkout \`${BUILD_RUSTDOC_REF}\` error." && exit 1; }
|
||||
build_rustdocs "${BUILD_RUSTDOC_REF}" "${DOC_PATH}"
|
||||
|
||||
# git checkout `gh-pages` branch
|
||||
git fetch "${GIT_REMOTE}" gh-pages
|
||||
git checkout gh-pages
|
||||
# Move the built back
|
||||
[[ -e "${TMP_PROJECT_PATH}/.gitignore" ]] && cp -f "${TMP_PROJECT_PATH}/.gitignore" .
|
||||
# Ensure the destination dir doesn't exist under current path.
|
||||
rm -rf "${BUILD_RUSTDOC_REF}"
|
||||
mv -f "${DOC_PATH}" "${BUILD_RUSTDOC_REF}"
|
||||
|
||||
upsert_index_page $LATEST "${BUILD_RUSTDOC_REF}"
|
||||
# Add the latest symlink
|
||||
$LATEST && rm -rf latest && ln -sf "${BUILD_RUSTDOC_REF}" latest
|
||||
|
||||
git_add_commit_push "___Deployed rustdocs of ${BUILD_RUSTDOC_REF}___"
|
||||
# Clean up
|
||||
# Remove the tmp asset created
|
||||
rm -rf "${TMP_PROJECT_PATH}"
|
||||
# Resume back previous checkout branch.
|
||||
git checkout -f "$CURRENT_GIT_BRANCH"
|
||||
}
|
||||
|
||||
remove_main() {
|
||||
check_local_change
|
||||
import_gh_key
|
||||
|
||||
CURRENT_GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
|
||||
# git checkout `gh-pages` branch
|
||||
git fetch "${GIT_REMOTE}" gh-pages
|
||||
git checkout gh-pages
|
||||
|
||||
rm -rf "${BUILD_RUSTDOC_REF}"
|
||||
rm_index_page "${BUILD_RUSTDOC_REF}"
|
||||
# check if the destination of `latest` exists and rmove if not.
|
||||
[[ -e "latest" ]] || rm latest
|
||||
|
||||
git_add_commit_push "___Removed rustdocs of ${BUILD_RUSTDOC_REF}___"
|
||||
|
||||
# Resume back previous checkout branch.
|
||||
git checkout -f "$CURRENT_GIT_BRANCH"
|
||||
}
|
||||
|
||||
# ---- The script execution entry point starts here ----
|
||||
|
||||
# Arguments handling
|
||||
SUBCMD=$1
|
||||
[[ $SUBCMD == "deploy" || $SUBCMD == "remove" ]] \
|
||||
|| { echo "Please specify a subcommand of \`deploy\` or \`remove\`" && exit 1 ;}
|
||||
shift
|
||||
|
||||
# After removing the subcommand, there could only be 1 or 2 parameters afterward
|
||||
[[ $# -lt 1 || $# -gt 2 ]] && {
|
||||
echo "${HELP_TXT[${SUBCMD}]}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Parsing options and argument for `deploy` subcommand
|
||||
[[ $SUBCMD == "deploy" ]] && {
|
||||
while getopts :lh opt; do
|
||||
case $opt in
|
||||
l)
|
||||
LATEST=true
|
||||
;;
|
||||
h)
|
||||
echo "${HELP_TXT[$SUBCMD]}"
|
||||
exit 0
|
||||
;;
|
||||
\?)
|
||||
echo "Invalid option: -$OPTARG" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
# Parsing options and argument for `remove` subcommand
|
||||
[[ $SUBCMD == "remove" ]] && {
|
||||
while getopts :h opt; do
|
||||
case $opt in
|
||||
h)
|
||||
echo "${HELP_TXT[$SUBCMD]}"
|
||||
exit 0
|
||||
;;
|
||||
\?)
|
||||
echo "Invalid option: -$OPTARG" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
shift $(($OPTIND - 1))
|
||||
set_and_check_rustdoc_ref ${1:-''} $SUBCMD
|
||||
|
||||
SCRIPT=$(realpath $0)
|
||||
SCRIPT_PATH=$(dirname $SCRIPT)
|
||||
PROJECT_PATH=$(dirname ${SCRIPT_PATH})
|
||||
PROJECT_NAME=$(basename "$PROJECT_PATH")
|
||||
|
||||
pushd "${PROJECT_PATH}" &>/dev/null
|
||||
[[ $SUBCMD == "deploy" ]] && deploy_main
|
||||
[[ $SUBCMD == "remove" ]] && remove_main
|
||||
popd &>/dev/null
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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.
|
||||
@@ -0,0 +1,17 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
@@ -0,0 +1,211 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
|
||||
NOTE
|
||||
|
||||
Individual files contain the following tag instead of the full license
|
||||
text.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
This enables machine processing of license information based on the SPDX
|
||||
License Identifiers that are here available: http://spdx.org/licenses/
|
||||
@@ -0,0 +1,700 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{one line to give the program's name and a brief idea of what it does.}
|
||||
Copyright (C) {year} {name of author}
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
{project} Copyright (C) {year} {fullname}
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
||||
"CLASSPATH" EXCEPTION TO THE GPL
|
||||
|
||||
Linking this library statically or dynamically with other modules is making
|
||||
a combined work based on this library. Thus, the terms and conditions of the
|
||||
GNU General Public License cover the whole combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent modules,
|
||||
and to copy and distribute the resulting executable under terms of your
|
||||
choice, provided that you also meet, for each linked independent module,
|
||||
the terms and conditions of the license of that module. An independent
|
||||
module is a module which is not derived from or based on this library.
|
||||
If you modify this library, you may extend this exception to your version
|
||||
of the library, but you are not obligated to do so. If you do not wish to
|
||||
do so, delete this exception statement from your version.
|
||||
|
||||
NOTE
|
||||
|
||||
Individual files contain the following tag instead of the full license text.
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
This enables machine processing of license information based on the SPDX
|
||||
License Identifiers that are here available: http://spdx.org/licenses/
|
||||
@@ -0,0 +1,48 @@
|
||||
# Bizinikiwi
|
||||
|
||||
[](#LICENSE)
|
||||
[](https://gitlab.parity.io/parity/mirrors/polkadot-sdk/-/pipelines)
|
||||
[](docs/contributor/CONTRIBUTING.md)
|
||||
[](https://exchange.pezkuwichain.app/)
|
||||
<p align="center">
|
||||
<img src="../bizinikiwi/docs/media/sub.gif">
|
||||
</p>
|
||||
|
||||
Bizinikiwi is a next-generation framework for blockchain innovation 🚀.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Head to [`docs.bizinikiwi.io`](https://github.com/pezkuwichain/docs.pezkuwichain.io) and follow the [installation](https://docs.pezkuwichain.io/install/)
|
||||
instructions. Then try out one of the [tutorials](https://docs.pezkuwichain.io/tutorials/). Refer to the [Docker
|
||||
instructions](./docker/README.md) to quickly run Bizinikiwi, Bizinikiwi Node Template, Subkey, or to build a chain spec.
|
||||
|
||||
## Community & Support
|
||||
|
||||
Join the highly active and supportive community on the [Bizinikiwi Stack Exchange](https://exchange.pezkuwichain.app/)
|
||||
to ask questions about use and problems you run into using this software. Please do report bugs and [issues
|
||||
here](https://github.com/pezkuwichain/pezkuwi-sdk/issues) for anything you suspect requires action in the source.
|
||||
|
||||
## Contributions & Code of Conduct
|
||||
|
||||
Please follow the contributions guidelines as outlined in [`docs/contributor/CONTRIBUTING.md`](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/docs/contributor/CONTRIBUTING.md).
|
||||
In all communications and contributions, this project follows the [Contributor Covenant Code of Conduct](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/docs/contributor/CODE_OF_CONDUCT.md).
|
||||
|
||||
## Security
|
||||
|
||||
The security policy and procedures can be found in
|
||||
[`docs/contributor/SECURITY.md`](https://github.com/pezkuwichain/pezkuwi-sdk/blob/master/docs/contributor/SECURITY.md).
|
||||
|
||||
## License
|
||||
|
||||
- Bizinikiwi Primitives (`pezsp-*`), Frame (`frame-*`) and the pallets (`pallets-*`), binaries (`/bin`) and all other
|
||||
utilities are licensed under [Apache 2.0](LICENSE-APACHE2). - Bizinikiwi Client (`/client/*` / `pezsc-*`) is licensed under
|
||||
[GPL v3.0 with a classpath linking exception](LICENSE-GPL3).
|
||||
|
||||
The reason for the split-licensing is to ensure that for the vast majority of teams using Bizinikiwi to create
|
||||
feature-chains, then all changes can be made entirely in Apache2-licensed code, allowing teams full freedom over what
|
||||
and how they release and giving licensing clarity to commercial teams.
|
||||
|
||||
In the interests of the community, we require any deeper improvements made to Bizinikiwi's core logic (e.g. Bizinikiwi's
|
||||
internal consensus, crypto or database code) to be contributed back so everyone can benefit.
|
||||
@@ -0,0 +1,64 @@
|
||||
[package]
|
||||
name = "node-bench"
|
||||
version = "0.9.0-dev"
|
||||
authors.workspace = true
|
||||
description = "Bizinikiwi node integration benchmarks."
|
||||
edition.workspace = true
|
||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
array-bytes = { workspace = true, default-features = true }
|
||||
async-trait = { workspace = true }
|
||||
clap = { features = ["derive"], workspace = true }
|
||||
derive_more = { features = ["display"], workspace = true }
|
||||
fs_extra = { workspace = true }
|
||||
futures = { features = ["thread-pool"], workspace = true }
|
||||
hash-db = { workspace = true, default-features = true }
|
||||
kitchensink-runtime = { workspace = true }
|
||||
kvdb = { workspace = true }
|
||||
kvdb-rocksdb = { workspace = true }
|
||||
log = { workspace = true, default-features = true }
|
||||
node-primitives = { workspace = true, default-features = true }
|
||||
node-testing = { workspace = true }
|
||||
parity-db = { workspace = true }
|
||||
rand = { features = ["small_rng"], workspace = true, default-features = true }
|
||||
pezsc-basic-authorship = { workspace = true, default-features = true }
|
||||
pezsc-client-api = { workspace = true, default-features = true }
|
||||
pezsc-transaction-pool = { workspace = true, default-features = true }
|
||||
pezsc-transaction-pool-api = { workspace = true, default-features = true }
|
||||
serde = { workspace = true, default-features = true }
|
||||
serde_json = { workspace = true, default-features = true }
|
||||
pezsp-consensus = { workspace = true, default-features = true }
|
||||
pezsp-core = { workspace = true, default-features = true }
|
||||
pezsp-inherents = { workspace = true, default-features = true }
|
||||
pezsp-runtime = { workspace = true, default-features = true }
|
||||
pezsp-state-machine = { workspace = true, default-features = true }
|
||||
pezsp-timestamp = { workspace = true }
|
||||
pezsp-tracing = { workspace = true, default-features = true }
|
||||
pezsp-trie = { workspace = true, default-features = true }
|
||||
tempfile = { workspace = true }
|
||||
|
||||
[features]
|
||||
runtime-benchmarks = [
|
||||
"kitchensink-runtime/runtime-benchmarks",
|
||||
"node-primitives/runtime-benchmarks",
|
||||
"node-testing/runtime-benchmarks",
|
||||
"pezsc-basic-authorship/runtime-benchmarks",
|
||||
"pezsc-client-api/runtime-benchmarks",
|
||||
"pezsc-transaction-pool-api/runtime-benchmarks",
|
||||
"pezsc-transaction-pool/runtime-benchmarks",
|
||||
"pezsp-consensus/runtime-benchmarks",
|
||||
"pezsp-inherents/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"pezsp-state-machine/runtime-benchmarks",
|
||||
"pezsp-timestamp/runtime-benchmarks",
|
||||
"pezsp-trie/runtime-benchmarks",
|
||||
]
|
||||
@@ -0,0 +1,47 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#[derive(Clone, Copy, Debug, derive_more::Display)]
|
||||
pub enum SizeType {
|
||||
#[display(fmt = "empty")]
|
||||
Empty,
|
||||
#[display(fmt = "small")]
|
||||
Small,
|
||||
#[display(fmt = "medium")]
|
||||
Medium,
|
||||
#[display(fmt = "large")]
|
||||
Large,
|
||||
#[display(fmt = "full")]
|
||||
Full,
|
||||
#[display(fmt = "custom")]
|
||||
Custom(usize),
|
||||
}
|
||||
|
||||
impl SizeType {
|
||||
pub fn transactions(&self) -> Option<usize> {
|
||||
match self {
|
||||
SizeType::Empty => Some(0),
|
||||
SizeType::Small => Some(10),
|
||||
SizeType::Medium => Some(100),
|
||||
SizeType::Large => Some(500),
|
||||
SizeType::Full => None,
|
||||
// Custom SizeType will use the `--transactions` input parameter
|
||||
SizeType::Custom(val) => Some(*val),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Block construction benchmark.
|
||||
//!
|
||||
//! This benchmark is expected to measure block construction.
|
||||
//! We want to protect against cold-cache attacks, and so this
|
||||
//! benchmark should not rely on any caching (except those entries that
|
||||
//! DO NOT depend on user input). Thus transaction generation should be
|
||||
//! based on randomized data.
|
||||
|
||||
use std::{borrow::Cow, collections::HashMap, pin::Pin, sync::Arc};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use node_primitives::Block;
|
||||
use node_testing::bench::{BenchDb, BlockType, DatabaseType, KeyTypes};
|
||||
use pezsc_transaction_pool_api::{
|
||||
ImportNotificationStream, PoolStatus, ReadyTransactions, TransactionFor, TransactionSource,
|
||||
TransactionStatusStreamFor, TxHash, TxInvalidityReportMap,
|
||||
};
|
||||
use pezsp_consensus::{Environment, Proposer};
|
||||
use pezsp_inherents::InherentDataProvider;
|
||||
use pezsp_runtime::OpaqueExtrinsic;
|
||||
|
||||
use crate::{
|
||||
common::SizeType,
|
||||
core::{self, Mode, Path},
|
||||
};
|
||||
|
||||
pub struct ConstructionBenchmarkDescription {
|
||||
pub key_types: KeyTypes,
|
||||
pub block_type: BlockType,
|
||||
pub size: SizeType,
|
||||
pub database_type: DatabaseType,
|
||||
}
|
||||
|
||||
pub struct ConstructionBenchmark {
|
||||
database: BenchDb,
|
||||
transactions: Transactions,
|
||||
}
|
||||
|
||||
impl core::BenchmarkDescription for ConstructionBenchmarkDescription {
|
||||
fn path(&self) -> Path {
|
||||
let mut path = Path::new(&["node", "proposer"]);
|
||||
|
||||
match self.key_types {
|
||||
KeyTypes::Sr25519 => path.push("sr25519"),
|
||||
KeyTypes::Ed25519 => path.push("ed25519"),
|
||||
}
|
||||
|
||||
match self.block_type {
|
||||
BlockType::RandomTransfersKeepAlive => path.push("transfer"),
|
||||
BlockType::RandomTransfersReaping => path.push("transfer_reaping"),
|
||||
BlockType::Noop => path.push("noop"),
|
||||
}
|
||||
|
||||
match self.database_type {
|
||||
DatabaseType::RocksDb => path.push("rocksdb"),
|
||||
DatabaseType::ParityDb => path.push("paritydb"),
|
||||
}
|
||||
|
||||
path.push(&format!("{}", self.size));
|
||||
|
||||
path
|
||||
}
|
||||
|
||||
fn setup(self: Box<Self>) -> Box<dyn core::Benchmark> {
|
||||
let mut extrinsics: Vec<Arc<PoolTransaction>> = Vec::new();
|
||||
|
||||
let mut bench_db = BenchDb::with_key_types(self.database_type, 50_000, self.key_types);
|
||||
|
||||
let client = bench_db.client();
|
||||
|
||||
let content_type = self.block_type.to_content(self.size.transactions());
|
||||
for transaction in bench_db.block_content(content_type, &client) {
|
||||
extrinsics.push(Arc::new(transaction.into()));
|
||||
}
|
||||
|
||||
Box::new(ConstructionBenchmark {
|
||||
database: bench_db,
|
||||
transactions: Transactions(extrinsics),
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
format!(
|
||||
"Block construction ({:?}/{}, {:?} backend)",
|
||||
self.block_type, self.size, self.database_type,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl core::Benchmark for ConstructionBenchmark {
|
||||
fn run(&mut self, mode: Mode) -> std::time::Duration {
|
||||
let context = self.database.create_context();
|
||||
|
||||
let _ = context
|
||||
.client
|
||||
.runtime_version_at(context.client.chain_info().genesis_hash)
|
||||
.expect("Failed to get runtime version")
|
||||
.spec_version;
|
||||
|
||||
if mode == Mode::Profile {
|
||||
std::thread::park_timeout(std::time::Duration::from_secs(3));
|
||||
}
|
||||
|
||||
let mut proposer_factory = pezsc_basic_authorship::ProposerFactory::new(
|
||||
context.spawn_handle.clone(),
|
||||
context.client.clone(),
|
||||
self.transactions.clone().into(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let timestamp_provider = pezsp_timestamp::InherentDataProvider::from_system_time();
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
let proposer = futures::executor::block_on(
|
||||
proposer_factory.init(
|
||||
&context
|
||||
.client
|
||||
.header(context.client.chain_info().genesis_hash)
|
||||
.expect("Database error querying block #0")
|
||||
.expect("Block #0 should exist"),
|
||||
),
|
||||
)
|
||||
.expect("Proposer initialization failed");
|
||||
|
||||
let inherent_data = futures::executor::block_on(timestamp_provider.create_inherent_data())
|
||||
.expect("Create inherent data failed");
|
||||
let _block = futures::executor::block_on(Proposer::propose(
|
||||
proposer,
|
||||
inherent_data,
|
||||
Default::default(),
|
||||
std::time::Duration::from_secs(20),
|
||||
None,
|
||||
))
|
||||
.map(|r| r.block)
|
||||
.expect("Proposing failed");
|
||||
|
||||
let elapsed = start.elapsed();
|
||||
|
||||
if mode == Mode::Profile {
|
||||
std::thread::park_timeout(std::time::Duration::from_secs(1));
|
||||
}
|
||||
|
||||
elapsed
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PoolTransaction {
|
||||
data: Arc<OpaqueExtrinsic>,
|
||||
hash: node_primitives::Hash,
|
||||
}
|
||||
|
||||
impl From<OpaqueExtrinsic> for PoolTransaction {
|
||||
fn from(e: OpaqueExtrinsic) -> Self {
|
||||
PoolTransaction { data: Arc::from(e), hash: node_primitives::Hash::zero() }
|
||||
}
|
||||
}
|
||||
|
||||
impl pezsc_transaction_pool_api::InPoolTransaction for PoolTransaction {
|
||||
type Transaction = Arc<OpaqueExtrinsic>;
|
||||
type Hash = node_primitives::Hash;
|
||||
|
||||
fn data(&self) -> &Self::Transaction {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn hash(&self) -> &Self::Hash {
|
||||
&self.hash
|
||||
}
|
||||
|
||||
fn priority(&self) -> &u64 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn longevity(&self) -> &u64 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn requires(&self) -> &[Vec<u8>] {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn provides(&self) -> &[Vec<u8>] {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn is_propagable(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Transactions(Vec<Arc<PoolTransaction>>);
|
||||
pub struct TransactionsIterator(std::vec::IntoIter<Arc<PoolTransaction>>);
|
||||
|
||||
impl Iterator for TransactionsIterator {
|
||||
type Item = Arc<PoolTransaction>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.0.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl ReadyTransactions for TransactionsIterator {
|
||||
fn report_invalid(&mut self, _tx: &Self::Item) {}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl pezsc_transaction_pool_api::TransactionPool for Transactions {
|
||||
type Block = Block;
|
||||
type Hash = node_primitives::Hash;
|
||||
type InPoolTransaction = PoolTransaction;
|
||||
type Error = pezsc_transaction_pool_api::error::Error;
|
||||
|
||||
/// Asynchronously imports a bunch of unverified transactions to the pool.
|
||||
async fn submit_at(
|
||||
&self,
|
||||
_at: Self::Hash,
|
||||
_source: TransactionSource,
|
||||
_xts: Vec<TransactionFor<Self>>,
|
||||
) -> Result<Vec<Result<node_primitives::Hash, Self::Error>>, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Asynchronously imports one unverified transaction to the pool.
|
||||
async fn submit_one(
|
||||
&self,
|
||||
_at: Self::Hash,
|
||||
_source: TransactionSource,
|
||||
_xt: TransactionFor<Self>,
|
||||
) -> Result<TxHash<Self>, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn submit_and_watch(
|
||||
&self,
|
||||
_at: Self::Hash,
|
||||
_source: TransactionSource,
|
||||
_xt: TransactionFor<Self>,
|
||||
) -> Result<Pin<Box<TransactionStatusStreamFor<Self>>>, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn ready_at(
|
||||
&self,
|
||||
_at: Self::Hash,
|
||||
) -> Box<dyn ReadyTransactions<Item = Arc<Self::InPoolTransaction>> + Send> {
|
||||
Box::new(TransactionsIterator(self.0.clone().into_iter()))
|
||||
}
|
||||
|
||||
fn ready(&self) -> Box<dyn ReadyTransactions<Item = Arc<Self::InPoolTransaction>> + Send> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn report_invalid(
|
||||
&self,
|
||||
_at: Option<Self::Hash>,
|
||||
_invalid_tx_errors: TxInvalidityReportMap<TxHash<Self>>,
|
||||
) -> Vec<Arc<Self::InPoolTransaction>> {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn futures(&self) -> Vec<Self::InPoolTransaction> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn status(&self) -> PoolStatus {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn import_notification_stream(&self) -> ImportNotificationStream<TxHash<Self>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_broadcasted(&self, _propagations: HashMap<TxHash<Self>, Vec<String>>) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn hash_of(&self, _xt: &TransactionFor<Self>) -> TxHash<Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn ready_transaction(&self, _hash: &TxHash<Self>) -> Option<Arc<Self::InPoolTransaction>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn ready_at_with_timeout(
|
||||
&self,
|
||||
_at: Self::Hash,
|
||||
_timeout: std::time::Duration,
|
||||
) -> Box<dyn ReadyTransactions<Item = Arc<Self::InPoolTransaction>> + Send> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use serde::Serialize;
|
||||
use std::{
|
||||
borrow::{Cow, ToOwned},
|
||||
fmt,
|
||||
};
|
||||
|
||||
pub struct Path(Vec<String>);
|
||||
|
||||
impl Path {
|
||||
pub fn new(initial: &'static [&'static str]) -> Self {
|
||||
Path(initial.iter().map(|x| x.to_string()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl Path {
|
||||
pub fn push(&mut self, item: &str) {
|
||||
self.0.push(item.to_string());
|
||||
}
|
||||
|
||||
pub fn full(&self) -> String {
|
||||
self.0.iter().fold(String::new(), |mut val, next| {
|
||||
val.push_str("::");
|
||||
val.push_str(next);
|
||||
val
|
||||
})
|
||||
}
|
||||
|
||||
pub fn has(&self, path: &str) -> bool {
|
||||
self.full().contains(path)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BenchmarkDescription {
|
||||
fn path(&self) -> Path;
|
||||
|
||||
fn setup(self: Box<Self>) -> Box<dyn Benchmark>;
|
||||
|
||||
fn name(&self) -> Cow<'static, str>;
|
||||
}
|
||||
|
||||
pub trait Benchmark {
|
||||
fn run(&mut self, mode: Mode) -> std::time::Duration;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct BenchmarkOutput {
|
||||
name: String,
|
||||
raw_average: u64,
|
||||
average: u64,
|
||||
}
|
||||
|
||||
pub struct NsFormatter(pub u64);
|
||||
|
||||
impl fmt::Display for NsFormatter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let v = self.0;
|
||||
match v {
|
||||
v if v < 100 => write!(f, "{} ns", v),
|
||||
v if v < 100_000 => write!(f, "{:.1} µs", v as f64 / 1000.0),
|
||||
v if v < 1_000_000 => write!(f, "{:.4} ms", v as f64 / 1_000_000.0),
|
||||
v if v < 100_000_000 => write!(f, "{:.1} ms", v as f64 / 1_000_000.0),
|
||||
_ => write!(f, "{:.4} s", v as f64 / 1_000_000_000.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Mode {
|
||||
Regular,
|
||||
Profile,
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Mode {
|
||||
type Err = &'static str;
|
||||
fn from_str(day: &str) -> Result<Self, Self::Err> {
|
||||
match day {
|
||||
"regular" => Ok(Mode::Regular),
|
||||
"profile" => Ok(Mode::Profile),
|
||||
_ => Err("Could not parse mode"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BenchmarkOutput {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}: avg {}, w_avg {}",
|
||||
self.name,
|
||||
NsFormatter(self.raw_average),
|
||||
NsFormatter(self.average),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_benchmark(benchmark: Box<dyn BenchmarkDescription>, mode: Mode) -> BenchmarkOutput {
|
||||
let name = benchmark.name().to_owned();
|
||||
let mut benchmark = benchmark.setup();
|
||||
|
||||
let mut durations: Vec<u128> = vec![];
|
||||
for _ in 0..50 {
|
||||
let duration = benchmark.run(mode);
|
||||
durations.push(duration.as_nanos());
|
||||
}
|
||||
|
||||
durations.sort();
|
||||
|
||||
let raw_average = (durations.iter().sum::<u128>() / (durations.len() as u128)) as u64;
|
||||
let average = (durations.iter().skip(10).take(30).sum::<u128>() / 30) as u64;
|
||||
|
||||
BenchmarkOutput { name: name.into(), raw_average, average }
|
||||
}
|
||||
|
||||
macro_rules! matrix(
|
||||
( $var:tt in $over:expr => $tt:expr, $( $rest:tt )* ) => {
|
||||
{
|
||||
let mut res = Vec::<Box<dyn crate::core::BenchmarkDescription>>::new();
|
||||
for $var in $over {
|
||||
res.push(Box::new($tt));
|
||||
}
|
||||
res.extend(matrix!( $($rest)* ));
|
||||
res
|
||||
}
|
||||
};
|
||||
( $var:expr, $( $rest:tt )*) => {
|
||||
{
|
||||
let mut res = vec![Box::new($var) as Box<dyn crate::core::BenchmarkDescription>];
|
||||
res.extend(matrix!( $($rest)* ));
|
||||
res
|
||||
}
|
||||
};
|
||||
() => { vec![] }
|
||||
);
|
||||
@@ -0,0 +1,69 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use kvdb::KeyValueDB;
|
||||
use node_primitives::Hash;
|
||||
use pezsp_trie::{trie_types::TrieDBMutBuilderV1, TrieMut};
|
||||
|
||||
use crate::simple_trie::SimpleTrie;
|
||||
|
||||
/// Generate trie from given `key_values`.
|
||||
///
|
||||
/// Will fill your database `db` with trie data from `key_values` and
|
||||
/// return root.
|
||||
pub fn generate_trie(
|
||||
db: Arc<dyn KeyValueDB>,
|
||||
key_values: impl IntoIterator<Item = (Vec<u8>, Vec<u8>)>,
|
||||
) -> Hash {
|
||||
let mut root = Hash::default();
|
||||
|
||||
let (db, overlay) = {
|
||||
let mut overlay = HashMap::new();
|
||||
overlay.insert(
|
||||
array_bytes::hex2bytes(
|
||||
"03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314",
|
||||
)
|
||||
.expect("null key is valid"),
|
||||
Some(vec![0]),
|
||||
);
|
||||
let mut trie = SimpleTrie { db, overlay: &mut overlay };
|
||||
{
|
||||
let mut trie_db =
|
||||
TrieDBMutBuilderV1::<crate::simple_trie::Hasher>::new(&mut trie, &mut root).build();
|
||||
for (key, value) in key_values {
|
||||
trie_db.insert(&key, &value).expect("trie insertion failed");
|
||||
}
|
||||
|
||||
trie_db.commit();
|
||||
}
|
||||
(trie.db, overlay)
|
||||
};
|
||||
|
||||
let mut transaction = db.transaction();
|
||||
for (key, value) in overlay.into_iter() {
|
||||
match value {
|
||||
Some(value) => transaction.put(0, &key[..], &value[..]),
|
||||
None => transaction.delete(0, &key[..]),
|
||||
}
|
||||
}
|
||||
db.write(transaction).expect("Failed to write transaction");
|
||||
|
||||
root
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Block import benchmark.
|
||||
//!
|
||||
//! This benchmark is expected to measure block import operation of
|
||||
//! some more or less full block.
|
||||
//!
|
||||
//! As we also want to protect against cold-cache attacks, this
|
||||
//! benchmark should not rely on any caching (except those that
|
||||
//! DO NOT depend on user input). Thus block generation should be
|
||||
//! based on randomized operation.
|
||||
//!
|
||||
//! This is supposed to be very simple benchmark and is not subject
|
||||
//! to much configuring - just block full of randomized transactions.
|
||||
//! It is not supposed to measure runtime modules weight correctness
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use node_primitives::Block;
|
||||
use node_testing::bench::{BenchDb, BlockType, DatabaseType, KeyTypes};
|
||||
use pezsc_client_api::backend::Backend;
|
||||
|
||||
use crate::{
|
||||
common::SizeType,
|
||||
core::{self, Mode, Path},
|
||||
};
|
||||
|
||||
pub struct ImportBenchmarkDescription {
|
||||
pub key_types: KeyTypes,
|
||||
pub block_type: BlockType,
|
||||
pub size: SizeType,
|
||||
pub database_type: DatabaseType,
|
||||
}
|
||||
|
||||
pub struct ImportBenchmark {
|
||||
database: BenchDb,
|
||||
block: Block,
|
||||
}
|
||||
|
||||
impl core::BenchmarkDescription for ImportBenchmarkDescription {
|
||||
fn path(&self) -> Path {
|
||||
let mut path = Path::new(&["node", "import"]);
|
||||
|
||||
match self.key_types {
|
||||
KeyTypes::Sr25519 => path.push("sr25519"),
|
||||
KeyTypes::Ed25519 => path.push("ed25519"),
|
||||
}
|
||||
|
||||
match self.block_type {
|
||||
BlockType::RandomTransfersKeepAlive => path.push("transfer_keep_alive"),
|
||||
BlockType::RandomTransfersReaping => path.push("transfer_reaping"),
|
||||
BlockType::Noop => path.push("noop"),
|
||||
}
|
||||
|
||||
match self.database_type {
|
||||
DatabaseType::RocksDb => path.push("rocksdb"),
|
||||
DatabaseType::ParityDb => path.push("paritydb"),
|
||||
}
|
||||
|
||||
path.push(&format!("{}", self.size));
|
||||
|
||||
path
|
||||
}
|
||||
|
||||
fn setup(self: Box<Self>) -> Box<dyn core::Benchmark> {
|
||||
let mut bench_db = BenchDb::with_key_types(self.database_type, 50_000, self.key_types);
|
||||
let block = bench_db.generate_block(self.block_type.to_content(self.size.transactions()));
|
||||
Box::new(ImportBenchmark { database: bench_db, block })
|
||||
}
|
||||
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
format!(
|
||||
"Block import ({:?}/{}, {:?} backend)",
|
||||
self.block_type, self.size, self.database_type,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl core::Benchmark for ImportBenchmark {
|
||||
fn run(&mut self, mode: Mode) -> std::time::Duration {
|
||||
let mut context = self.database.create_context();
|
||||
|
||||
let _ = context
|
||||
.client
|
||||
.runtime_version_at(context.client.chain_info().genesis_hash)
|
||||
.expect("Failed to get runtime version")
|
||||
.spec_version;
|
||||
|
||||
if mode == Mode::Profile {
|
||||
std::thread::park_timeout(std::time::Duration::from_secs(3));
|
||||
}
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
context.import_block(self.block.clone());
|
||||
let elapsed = start.elapsed();
|
||||
|
||||
if mode == Mode::Profile {
|
||||
std::thread::park_timeout(std::time::Duration::from_secs(1));
|
||||
}
|
||||
|
||||
log::info!(
|
||||
target: "bench-logistics",
|
||||
"imported block with {} tx, took: {:#?}",
|
||||
self.block.extrinsics.len(),
|
||||
elapsed,
|
||||
);
|
||||
|
||||
log::info!(
|
||||
target: "bench-logistics",
|
||||
"usage info: {}",
|
||||
context.backend.usage_info()
|
||||
.expect("RocksDB backend always provides usage info!"),
|
||||
);
|
||||
|
||||
elapsed
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
mod common;
|
||||
mod construct;
|
||||
#[macro_use]
|
||||
mod core;
|
||||
mod generator;
|
||||
mod import;
|
||||
mod simple_trie;
|
||||
mod state_sizes;
|
||||
mod tempdb;
|
||||
mod trie;
|
||||
mod txpool;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use node_testing::bench::{BlockType, DatabaseType as BenchDataBaseType, KeyTypes};
|
||||
|
||||
use crate::{
|
||||
common::SizeType,
|
||||
construct::ConstructionBenchmarkDescription,
|
||||
core::{run_benchmark, Mode as BenchmarkMode},
|
||||
import::ImportBenchmarkDescription,
|
||||
tempdb::DatabaseType,
|
||||
trie::{DatabaseSize, TrieReadBenchmarkDescription, TrieWriteBenchmarkDescription},
|
||||
txpool::PoolBenchmarkDescription,
|
||||
};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(name = "node-bench", about = "Node integration benchmarks")]
|
||||
struct Opt {
|
||||
/// Show list of all available benchmarks.
|
||||
///
|
||||
/// Will output ("name", "path"). Benchmarks can then be filtered by path.
|
||||
#[arg(short, long)]
|
||||
list: bool,
|
||||
|
||||
/// Machine readable json output.
|
||||
///
|
||||
/// This also suppresses all regular output (except to stderr)
|
||||
#[arg(short, long)]
|
||||
json: bool,
|
||||
|
||||
/// Filter benchmarks.
|
||||
///
|
||||
/// Run with `--list` for the hint of what to filter.
|
||||
filter: Option<String>,
|
||||
|
||||
/// Number of transactions for block import with `custom` size.
|
||||
#[arg(long)]
|
||||
transactions: Option<usize>,
|
||||
|
||||
/// Mode
|
||||
///
|
||||
/// "regular" for regular benchmark
|
||||
///
|
||||
/// "profile" mode adds pauses between measurable runs,
|
||||
/// so that actual interval can be selected in the profiler of choice.
|
||||
#[arg(short, long, default_value = "regular")]
|
||||
mode: BenchmarkMode,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opt = Opt::parse();
|
||||
|
||||
if !opt.json {
|
||||
pezsp_tracing::try_init_simple();
|
||||
}
|
||||
|
||||
let mut import_benchmarks = Vec::new();
|
||||
|
||||
for size in [
|
||||
SizeType::Empty,
|
||||
SizeType::Small,
|
||||
SizeType::Medium,
|
||||
SizeType::Large,
|
||||
SizeType::Full,
|
||||
SizeType::Custom(opt.transactions.unwrap_or(0)),
|
||||
] {
|
||||
for block_type in [
|
||||
BlockType::RandomTransfersKeepAlive,
|
||||
BlockType::RandomTransfersReaping,
|
||||
BlockType::Noop,
|
||||
] {
|
||||
for database_type in [BenchDataBaseType::RocksDb, BenchDataBaseType::ParityDb] {
|
||||
import_benchmarks.push((size, block_type, database_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let benchmarks = matrix!(
|
||||
(size, block_type, database_type) in import_benchmarks.into_iter() =>
|
||||
ImportBenchmarkDescription {
|
||||
key_types: KeyTypes::Sr25519,
|
||||
size,
|
||||
block_type,
|
||||
database_type,
|
||||
},
|
||||
(size, db_type) in
|
||||
[
|
||||
DatabaseSize::Empty, DatabaseSize::Smallest, DatabaseSize::Small,
|
||||
DatabaseSize::Medium, DatabaseSize::Large, DatabaseSize::Huge,
|
||||
]
|
||||
.iter().flat_map(|size|
|
||||
[
|
||||
DatabaseType::RocksDb, DatabaseType::ParityDb
|
||||
]
|
||||
.iter().map(move |db_type| (size, db_type)))
|
||||
=> TrieReadBenchmarkDescription { database_size: *size, database_type: *db_type },
|
||||
(size, db_type) in
|
||||
[
|
||||
DatabaseSize::Empty, DatabaseSize::Smallest, DatabaseSize::Small,
|
||||
DatabaseSize::Medium, DatabaseSize::Large, DatabaseSize::Huge,
|
||||
]
|
||||
.iter().flat_map(|size|
|
||||
[
|
||||
DatabaseType::RocksDb, DatabaseType::ParityDb
|
||||
]
|
||||
.iter().map(move |db_type| (size, db_type)))
|
||||
=> TrieWriteBenchmarkDescription { database_size: *size, database_type: *db_type },
|
||||
ConstructionBenchmarkDescription {
|
||||
key_types: KeyTypes::Sr25519,
|
||||
block_type: BlockType::RandomTransfersKeepAlive,
|
||||
size: SizeType::Medium,
|
||||
database_type: BenchDataBaseType::RocksDb,
|
||||
},
|
||||
ConstructionBenchmarkDescription {
|
||||
key_types: KeyTypes::Sr25519,
|
||||
block_type: BlockType::RandomTransfersKeepAlive,
|
||||
size: SizeType::Large,
|
||||
database_type: BenchDataBaseType::RocksDb,
|
||||
},
|
||||
PoolBenchmarkDescription { database_type: BenchDataBaseType::RocksDb },
|
||||
);
|
||||
|
||||
if opt.list {
|
||||
println!("Available benchmarks:");
|
||||
if let Some(filter) = opt.filter.as_ref() {
|
||||
println!("\t(filtered by \"{}\")", filter);
|
||||
}
|
||||
for benchmark in benchmarks.iter() {
|
||||
if opt.filter.as_ref().map(|f| benchmark.path().has(f)).unwrap_or(true) {
|
||||
println!("{}: {}", benchmark.name(), benchmark.path().full())
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let mut results = Vec::new();
|
||||
for benchmark in benchmarks {
|
||||
if opt.filter.as_ref().map(|f| benchmark.path().has(f)).unwrap_or(true) {
|
||||
log::info!("Starting {}", benchmark.name());
|
||||
let result = run_benchmark(benchmark, opt.mode);
|
||||
log::info!("{}", result);
|
||||
|
||||
results.push(result);
|
||||
}
|
||||
}
|
||||
|
||||
if results.is_empty() {
|
||||
eprintln!("No benchmark was found for query");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
if opt.json {
|
||||
let json_result: String =
|
||||
serde_json::to_string(&results).expect("Failed to construct json");
|
||||
println!("{}", json_result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use hash_db::{AsHashDB, HashDB, Hasher as _, Prefix};
|
||||
use kvdb::KeyValueDB;
|
||||
use node_primitives::Hash;
|
||||
use pezsp_trie::DBValue;
|
||||
|
||||
pub type Hasher = pezsp_core::Blake2Hasher;
|
||||
|
||||
/// Immutable generated trie database with root.
|
||||
pub struct SimpleTrie<'a> {
|
||||
pub db: Arc<dyn KeyValueDB>,
|
||||
pub overlay: &'a mut HashMap<Vec<u8>, Option<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl<'a> AsHashDB<Hasher, DBValue> for SimpleTrie<'a> {
|
||||
fn as_hash_db(&self) -> &dyn hash_db::HashDB<Hasher, DBValue> {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn HashDB<Hasher, DBValue> + 'b) {
|
||||
&mut *self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> HashDB<Hasher, DBValue> for SimpleTrie<'a> {
|
||||
fn get(&self, key: &Hash, prefix: Prefix) -> Option<DBValue> {
|
||||
let key = pezsp_trie::prefixed_key::<Hasher>(key, prefix);
|
||||
if let Some(value) = self.overlay.get(&key) {
|
||||
return value.clone();
|
||||
}
|
||||
self.db.get(0, &key).expect("Database backend error")
|
||||
}
|
||||
|
||||
fn contains(&self, hash: &Hash, prefix: Prefix) -> bool {
|
||||
self.get(hash, prefix).is_some()
|
||||
}
|
||||
|
||||
fn insert(&mut self, prefix: Prefix, value: &[u8]) -> Hash {
|
||||
let key = Hasher::hash(value);
|
||||
self.emplace(key, prefix, value.to_vec());
|
||||
key
|
||||
}
|
||||
|
||||
fn emplace(&mut self, key: Hash, prefix: Prefix, value: DBValue) {
|
||||
let key = pezsp_trie::prefixed_key::<Hasher>(&key, prefix);
|
||||
self.overlay.insert(key, Some(value));
|
||||
}
|
||||
|
||||
fn remove(&mut self, key: &Hash, prefix: Prefix) {
|
||||
let key = pezsp_trie::prefixed_key::<Hasher>(key, prefix);
|
||||
self.overlay.insert(key, None);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,123 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use kvdb::{DBKeyValue, DBTransaction, KeyValueDB};
|
||||
use kvdb_rocksdb::{Database, DatabaseConfig};
|
||||
use std::{io, path::PathBuf, sync::Arc};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum DatabaseType {
|
||||
RocksDb,
|
||||
ParityDb,
|
||||
}
|
||||
|
||||
pub struct TempDatabase(tempfile::TempDir);
|
||||
|
||||
struct ParityDbWrapper(parity_db::Db);
|
||||
|
||||
impl KeyValueDB for ParityDbWrapper {
|
||||
/// Get a value by key.
|
||||
fn get(&self, col: u32, key: &[u8]) -> io::Result<Option<Vec<u8>>> {
|
||||
Ok(self.0.get(col as u8, &key[key.len() - 32..]).expect("db error"))
|
||||
}
|
||||
|
||||
/// Get a value by partial key. Only works for flushed data.
|
||||
fn get_by_prefix(&self, _col: u32, _prefix: &[u8]) -> io::Result<Option<Vec<u8>>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Write a transaction of changes to the buffer.
|
||||
fn write(&self, transaction: DBTransaction) -> io::Result<()> {
|
||||
self.0
|
||||
.commit(transaction.ops.iter().map(|op| match op {
|
||||
kvdb::DBOp::Insert { col, key, value } =>
|
||||
(*col as u8, &key[key.len() - 32..], Some(value.to_vec())),
|
||||
kvdb::DBOp::Delete { col, key } => (*col as u8, &key[key.len() - 32..], None),
|
||||
kvdb::DBOp::DeletePrefix { col: _, prefix: _ } => unimplemented!(),
|
||||
}))
|
||||
.expect("db error");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Iterate over flushed data for a given column.
|
||||
fn iter<'a>(&'a self, _col: u32) -> Box<dyn Iterator<Item = io::Result<DBKeyValue>> + 'a> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Iterate over flushed data for a given column, starting from a given prefix.
|
||||
fn iter_with_prefix<'a>(
|
||||
&'a self,
|
||||
_col: u32,
|
||||
_prefix: &'a [u8],
|
||||
) -> Box<dyn Iterator<Item = io::Result<DBKeyValue>> + 'a> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl TempDatabase {
|
||||
pub fn new() -> Self {
|
||||
let dir = tempfile::tempdir().expect("temp dir creation failed");
|
||||
log::trace!(
|
||||
target: "bench-logistics",
|
||||
"Created temp db at {}",
|
||||
dir.path().to_string_lossy(),
|
||||
);
|
||||
|
||||
TempDatabase(dir)
|
||||
}
|
||||
|
||||
pub fn open(&mut self, db_type: DatabaseType) -> Arc<dyn KeyValueDB> {
|
||||
match db_type {
|
||||
DatabaseType::RocksDb => {
|
||||
let db_cfg = DatabaseConfig::with_columns(1);
|
||||
let db = Database::open(&db_cfg, &self.0.path()).expect("Database backend error");
|
||||
Arc::new(db)
|
||||
},
|
||||
DatabaseType::ParityDb => Arc::new(ParityDbWrapper({
|
||||
let mut options = parity_db::Options::with_columns(self.0.path(), 1);
|
||||
let column_options = &mut options.columns[0];
|
||||
column_options.ref_counted = true;
|
||||
column_options.preimage = true;
|
||||
column_options.uniform = true;
|
||||
parity_db::Db::open_or_create(&options).expect("db open error")
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for TempDatabase {
|
||||
fn clone(&self) -> Self {
|
||||
let new_dir = tempfile::tempdir().expect("temp dir creation failed");
|
||||
let self_dir = self.0.path();
|
||||
|
||||
log::trace!(
|
||||
target: "bench-logistics",
|
||||
"Cloning db ({}) to {}",
|
||||
self_dir.to_string_lossy(),
|
||||
new_dir.path().to_string_lossy(),
|
||||
);
|
||||
let self_db_files = std::fs::read_dir(self_dir)
|
||||
.expect("failed to list file in seed dir")
|
||||
.map(|f_result| f_result.expect("failed to read file in seed db").path())
|
||||
.collect::<Vec<PathBuf>>();
|
||||
fs_extra::copy_items(&self_db_files, new_dir.path(), &fs_extra::dir::CopyOptions::new())
|
||||
.expect("Copy of seed database is ok");
|
||||
|
||||
TempDatabase(new_dir)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Trie benchmark (integrated).
|
||||
|
||||
use hash_db::Prefix;
|
||||
use kvdb::KeyValueDB;
|
||||
use rand::Rng;
|
||||
use pezsp_state_machine::Backend as _;
|
||||
use pezsp_trie::{trie_types::TrieDBMutBuilderV1, TrieMut as _};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
sync::{Arc, LazyLock},
|
||||
};
|
||||
|
||||
use node_primitives::Hash;
|
||||
|
||||
use crate::{
|
||||
core::{self, Mode, Path},
|
||||
generator::generate_trie,
|
||||
simple_trie::SimpleTrie,
|
||||
tempdb::{DatabaseType, TempDatabase},
|
||||
};
|
||||
|
||||
pub const SAMPLE_SIZE: usize = 100;
|
||||
pub const TEST_WRITE_SIZE: usize = 128;
|
||||
|
||||
pub type KeyValue = (Vec<u8>, Vec<u8>);
|
||||
pub type KeyValues = Vec<KeyValue>;
|
||||
|
||||
#[derive(Clone, Copy, Debug, derive_more::Display)]
|
||||
pub enum DatabaseSize {
|
||||
#[display(fmt = "empty")]
|
||||
Empty,
|
||||
#[display(fmt = "smallest")]
|
||||
Smallest,
|
||||
#[display(fmt = "small")]
|
||||
Small,
|
||||
#[display(fmt = "medium")]
|
||||
Medium,
|
||||
#[display(fmt = "large")]
|
||||
Large,
|
||||
#[display(fmt = "huge")]
|
||||
Huge,
|
||||
}
|
||||
|
||||
static KUSAMA_STATE_DISTRIBUTION: LazyLock<SizePool> =
|
||||
LazyLock::new(|| SizePool::from_histogram(crate::state_sizes::KUSAMA_STATE_DISTRIBUTION));
|
||||
|
||||
impl DatabaseSize {
|
||||
/// Should be multiple of SAMPLE_SIZE!
|
||||
fn keys(&self) -> usize {
|
||||
let val = match *self {
|
||||
Self::Empty => 200, // still need some keys to query
|
||||
Self::Smallest => 1_000,
|
||||
Self::Small => 10_000,
|
||||
Self::Medium => 100_000,
|
||||
Self::Large => 200_000,
|
||||
Self::Huge => 1_000_000,
|
||||
};
|
||||
|
||||
assert_eq!(val % SAMPLE_SIZE, 0);
|
||||
|
||||
val
|
||||
}
|
||||
}
|
||||
|
||||
fn pretty_print(v: usize) -> String {
|
||||
let mut print = String::new();
|
||||
for (idx, val) in v.to_string().chars().rev().enumerate() {
|
||||
if idx != 0 && idx % 3 == 0 {
|
||||
print.insert(0, ',');
|
||||
}
|
||||
print.insert(0, val);
|
||||
}
|
||||
print
|
||||
}
|
||||
|
||||
pub struct TrieReadBenchmarkDescription {
|
||||
pub database_size: DatabaseSize,
|
||||
pub database_type: DatabaseType,
|
||||
}
|
||||
|
||||
pub struct TrieReadBenchmark {
|
||||
database: TempDatabase,
|
||||
root: Hash,
|
||||
warmup_keys: KeyValues,
|
||||
query_keys: KeyValues,
|
||||
database_type: DatabaseType,
|
||||
}
|
||||
|
||||
impl core::BenchmarkDescription for TrieReadBenchmarkDescription {
|
||||
fn path(&self) -> Path {
|
||||
let mut path = Path::new(&["trie", "read"]);
|
||||
path.push(&format!("{}", self.database_size));
|
||||
path
|
||||
}
|
||||
|
||||
fn setup(self: Box<Self>) -> Box<dyn core::Benchmark> {
|
||||
let mut database = TempDatabase::new();
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let warmup_prefix = KUSAMA_STATE_DISTRIBUTION.key(&mut rng);
|
||||
|
||||
let mut key_values = KeyValues::new();
|
||||
let mut warmup_keys = KeyValues::new();
|
||||
let mut query_keys = KeyValues::new();
|
||||
let every_x_key = self.database_size.keys() / SAMPLE_SIZE;
|
||||
for idx in 0..self.database_size.keys() {
|
||||
let kv = (
|
||||
KUSAMA_STATE_DISTRIBUTION.key(&mut rng).to_vec(),
|
||||
KUSAMA_STATE_DISTRIBUTION.value(&mut rng),
|
||||
);
|
||||
if idx % every_x_key == 0 {
|
||||
// warmup keys go to separate tree with high prob
|
||||
let mut actual_warmup_key = warmup_prefix.clone();
|
||||
actual_warmup_key[16..].copy_from_slice(&kv.0[16..]);
|
||||
warmup_keys.push((actual_warmup_key.clone(), kv.1.clone()));
|
||||
key_values.push((actual_warmup_key.clone(), kv.1.clone()));
|
||||
} else if idx % every_x_key == 1 {
|
||||
query_keys.push(kv.clone());
|
||||
}
|
||||
|
||||
key_values.push(kv)
|
||||
}
|
||||
|
||||
assert_eq!(warmup_keys.len(), SAMPLE_SIZE);
|
||||
assert_eq!(query_keys.len(), SAMPLE_SIZE);
|
||||
|
||||
let root = generate_trie(database.open(self.database_type), key_values);
|
||||
|
||||
Box::new(TrieReadBenchmark {
|
||||
database,
|
||||
root,
|
||||
warmup_keys,
|
||||
query_keys,
|
||||
database_type: self.database_type,
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
format!(
|
||||
"Trie read benchmark({:?} database ({} keys), db_type: {:?})",
|
||||
self.database_size,
|
||||
pretty_print(self.database_size.keys()),
|
||||
self.database_type,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
struct Storage(Arc<dyn KeyValueDB>);
|
||||
|
||||
impl pezsp_state_machine::Storage<pezsp_core::Blake2Hasher> for Storage {
|
||||
fn get(&self, key: &Hash, prefix: Prefix) -> Result<Option<Vec<u8>>, String> {
|
||||
let key = pezsp_trie::prefixed_key::<pezsp_core::Blake2Hasher>(key, prefix);
|
||||
self.0.get(0, &key).map_err(|e| format!("Database backend error: {:?}", e))
|
||||
}
|
||||
}
|
||||
|
||||
impl core::Benchmark for TrieReadBenchmark {
|
||||
fn run(&mut self, mode: Mode) -> std::time::Duration {
|
||||
let mut db = self.database.clone();
|
||||
|
||||
let storage: Arc<dyn pezsp_state_machine::Storage<pezsp_core::Blake2Hasher>> =
|
||||
Arc::new(Storage(db.open(self.database_type)));
|
||||
|
||||
let trie_backend = pezsp_state_machine::TrieBackendBuilder::new(storage, self.root).build();
|
||||
for (warmup_key, warmup_value) in self.warmup_keys.iter() {
|
||||
let value = trie_backend
|
||||
.storage(&warmup_key[..])
|
||||
.expect("Failed to get key: db error")
|
||||
.expect("Warmup key should exist");
|
||||
|
||||
// sanity for warmup keys
|
||||
assert_eq!(&value, warmup_value);
|
||||
}
|
||||
|
||||
if mode == Mode::Profile {
|
||||
std::thread::park_timeout(std::time::Duration::from_secs(3));
|
||||
}
|
||||
|
||||
let started = std::time::Instant::now();
|
||||
for (key, _) in self.query_keys.iter() {
|
||||
let _ = trie_backend.storage(&key[..]);
|
||||
}
|
||||
let elapsed = started.elapsed();
|
||||
|
||||
if mode == Mode::Profile {
|
||||
std::thread::park_timeout(std::time::Duration::from_secs(1));
|
||||
}
|
||||
|
||||
elapsed / (SAMPLE_SIZE as u32)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TrieWriteBenchmarkDescription {
|
||||
pub database_size: DatabaseSize,
|
||||
pub database_type: DatabaseType,
|
||||
}
|
||||
|
||||
impl core::BenchmarkDescription for TrieWriteBenchmarkDescription {
|
||||
fn path(&self) -> Path {
|
||||
let mut path = Path::new(&["trie", "write"]);
|
||||
path.push(&format!("{}", self.database_size));
|
||||
path
|
||||
}
|
||||
|
||||
fn setup(self: Box<Self>) -> Box<dyn core::Benchmark> {
|
||||
let mut database = TempDatabase::new();
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let warmup_prefix = KUSAMA_STATE_DISTRIBUTION.key(&mut rng);
|
||||
|
||||
let mut key_values = KeyValues::new();
|
||||
let mut warmup_keys = KeyValues::new();
|
||||
let every_x_key = self.database_size.keys() / SAMPLE_SIZE;
|
||||
for idx in 0..self.database_size.keys() {
|
||||
let kv = (
|
||||
KUSAMA_STATE_DISTRIBUTION.key(&mut rng).to_vec(),
|
||||
KUSAMA_STATE_DISTRIBUTION.value(&mut rng),
|
||||
);
|
||||
if idx % every_x_key == 0 {
|
||||
// warmup keys go to separate tree with high prob
|
||||
let mut actual_warmup_key = warmup_prefix.clone();
|
||||
actual_warmup_key[16..].copy_from_slice(&kv.0[16..]);
|
||||
warmup_keys.push((actual_warmup_key.clone(), kv.1.clone()));
|
||||
key_values.push((actual_warmup_key.clone(), kv.1.clone()));
|
||||
}
|
||||
|
||||
key_values.push(kv)
|
||||
}
|
||||
|
||||
assert_eq!(warmup_keys.len(), SAMPLE_SIZE);
|
||||
|
||||
let root = generate_trie(database.open(self.database_type), key_values);
|
||||
|
||||
Box::new(TrieWriteBenchmark {
|
||||
database,
|
||||
root,
|
||||
warmup_keys,
|
||||
database_type: self.database_type,
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
format!(
|
||||
"Trie write benchmark({:?} database ({} keys), db_type = {:?})",
|
||||
self.database_size,
|
||||
pretty_print(self.database_size.keys()),
|
||||
self.database_type,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
struct TrieWriteBenchmark {
|
||||
database: TempDatabase,
|
||||
root: Hash,
|
||||
warmup_keys: KeyValues,
|
||||
database_type: DatabaseType,
|
||||
}
|
||||
|
||||
impl core::Benchmark for TrieWriteBenchmark {
|
||||
fn run(&mut self, mode: Mode) -> std::time::Duration {
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut db = self.database.clone();
|
||||
let kvdb = db.open(self.database_type);
|
||||
|
||||
let mut new_root = self.root;
|
||||
|
||||
let mut overlay = HashMap::new();
|
||||
let mut trie = SimpleTrie { db: kvdb.clone(), overlay: &mut overlay };
|
||||
let mut trie_db_mut = TrieDBMutBuilderV1::from_existing(&mut trie, &mut new_root).build();
|
||||
|
||||
for (warmup_key, warmup_value) in self.warmup_keys.iter() {
|
||||
let value = trie_db_mut
|
||||
.get(&warmup_key[..])
|
||||
.expect("Failed to get key: db error")
|
||||
.expect("Warmup key should exist");
|
||||
|
||||
// sanity for warmup keys
|
||||
assert_eq!(&value, warmup_value);
|
||||
}
|
||||
|
||||
let test_key = random_vec(&mut rng, 32);
|
||||
let test_val = random_vec(&mut rng, TEST_WRITE_SIZE);
|
||||
|
||||
if mode == Mode::Profile {
|
||||
std::thread::park_timeout(std::time::Duration::from_secs(3));
|
||||
}
|
||||
|
||||
let started = std::time::Instant::now();
|
||||
|
||||
trie_db_mut.insert(&test_key, &test_val).expect("Should be inserted ok");
|
||||
trie_db_mut.commit();
|
||||
drop(trie_db_mut);
|
||||
|
||||
let mut transaction = kvdb.transaction();
|
||||
for (key, value) in overlay.into_iter() {
|
||||
match value {
|
||||
Some(value) => transaction.put(0, &key[..], &value[..]),
|
||||
None => transaction.delete(0, &key[..]),
|
||||
}
|
||||
}
|
||||
kvdb.write(transaction).expect("Failed to write transaction");
|
||||
|
||||
let elapsed = started.elapsed();
|
||||
|
||||
// sanity check
|
||||
assert!(new_root != self.root);
|
||||
|
||||
if mode == Mode::Profile {
|
||||
std::thread::park_timeout(std::time::Duration::from_secs(1));
|
||||
}
|
||||
|
||||
elapsed
|
||||
}
|
||||
}
|
||||
|
||||
fn random_vec<R: Rng>(rng: &mut R, len: usize) -> Vec<u8> {
|
||||
let mut val = vec![0u8; len];
|
||||
rng.fill_bytes(&mut val[..]);
|
||||
val
|
||||
}
|
||||
|
||||
struct SizePool {
|
||||
distribution: std::collections::BTreeMap<u32, u32>,
|
||||
total: u32,
|
||||
}
|
||||
|
||||
impl SizePool {
|
||||
fn from_histogram(h: &[(u32, u32)]) -> SizePool {
|
||||
let mut distribution = std::collections::BTreeMap::default();
|
||||
let mut total = 0;
|
||||
for (size, count) in h {
|
||||
total += count;
|
||||
distribution.insert(total, *size);
|
||||
}
|
||||
SizePool { distribution, total }
|
||||
}
|
||||
|
||||
fn value<R: Rng>(&self, rng: &mut R) -> Vec<u8> {
|
||||
let sr = (rng.next_u64() % self.total as u64) as u32;
|
||||
let mut range = self
|
||||
.distribution
|
||||
.range((std::ops::Bound::Included(sr), std::ops::Bound::Unbounded));
|
||||
let size = *range.next().unwrap().1 as usize;
|
||||
random_vec(rng, size)
|
||||
}
|
||||
|
||||
fn key<R: Rng>(&self, rng: &mut R) -> Vec<u8> {
|
||||
random_vec(rng, 32)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Transaction pool integrated benchmarks.
|
||||
//!
|
||||
//! The goal of this benchmark is to figure out time needed to fill
|
||||
//! the transaction pool for the next block.
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use node_testing::bench::{BenchDb, BlockType, DatabaseType, KeyTypes};
|
||||
|
||||
use pezsc_transaction_pool::BasicPool;
|
||||
use pezsc_transaction_pool_api::{TransactionPool, TransactionSource};
|
||||
|
||||
use crate::core::{self, Mode, Path};
|
||||
|
||||
pub struct PoolBenchmarkDescription {
|
||||
pub database_type: DatabaseType,
|
||||
}
|
||||
|
||||
pub struct PoolBenchmark {
|
||||
database: BenchDb,
|
||||
}
|
||||
|
||||
impl core::BenchmarkDescription for PoolBenchmarkDescription {
|
||||
fn path(&self) -> Path {
|
||||
Path::new(&["node", "txpool"])
|
||||
}
|
||||
|
||||
fn setup(self: Box<Self>) -> Box<dyn core::Benchmark> {
|
||||
Box::new(PoolBenchmark {
|
||||
database: BenchDb::with_key_types(self.database_type, 50_000, KeyTypes::Sr25519),
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> Cow<'static, str> {
|
||||
"Transaction pool benchmark".into()
|
||||
}
|
||||
}
|
||||
|
||||
impl core::Benchmark for PoolBenchmark {
|
||||
fn run(&mut self, mode: Mode) -> std::time::Duration {
|
||||
let context = self.database.create_context();
|
||||
let genesis_hash = context.client.chain_info().genesis_hash;
|
||||
|
||||
let _ = context
|
||||
.client
|
||||
.runtime_version_at(genesis_hash)
|
||||
.expect("Failed to get runtime version")
|
||||
.spec_version;
|
||||
|
||||
if mode == Mode::Profile {
|
||||
std::thread::park_timeout(std::time::Duration::from_secs(3));
|
||||
}
|
||||
|
||||
let executor = pezsp_core::testing::TaskExecutor::new();
|
||||
let txpool = BasicPool::new_full(
|
||||
Default::default(),
|
||||
true.into(),
|
||||
None,
|
||||
executor,
|
||||
context.client.clone(),
|
||||
);
|
||||
|
||||
let generated_transactions = self
|
||||
.database
|
||||
.block_content(
|
||||
BlockType::RandomTransfersKeepAlive.to_content(Some(100)),
|
||||
&context.client,
|
||||
)
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
let submissions = generated_transactions
|
||||
.into_iter()
|
||||
.map(|tx| txpool.submit_one(genesis_hash, TransactionSource::External, tx));
|
||||
futures::executor::block_on(futures::future::join_all(submissions));
|
||||
let elapsed = start.elapsed();
|
||||
|
||||
if mode == Mode::Profile {
|
||||
std::thread::park_timeout(std::time::Duration::from_secs(1));
|
||||
}
|
||||
elapsed
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
[package]
|
||||
name = "pezstaging-node-cli"
|
||||
version = "3.0.0-dev"
|
||||
authors.workspace = true
|
||||
description = "Generic Bizinikiwi node implementation in Rust."
|
||||
build = "build.rs"
|
||||
edition.workspace = true
|
||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||
default-run = "bizinikiwi-node"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.wasm-pack.profile.release]
|
||||
# `wasm-opt` has some problems on linux, see
|
||||
# https://github.com/rustwasm/wasm-pack/issues/781 etc.
|
||||
wasm-opt = false
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
is-it-maintained-issue-resolution = { repository = "pezkuwichain/pezkuwi-sdk" }
|
||||
is-it-maintained-open-issues = { repository = "pezkuwichain/pezkuwi-sdk" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[[bin]]
|
||||
name = "bizinikiwi-node"
|
||||
path = "bin/main.rs"
|
||||
required-features = ["cli"]
|
||||
|
||||
[[bench]]
|
||||
name = "transaction_pool"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "block_production"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "executor"
|
||||
harness = false
|
||||
|
||||
[dependencies]
|
||||
# third-party dependencies
|
||||
array-bytes = { workspace = true, default-features = true }
|
||||
clap = { features = ["derive"], optional = true, workspace = true }
|
||||
codec = { workspace = true, default-features = true }
|
||||
futures = { workspace = true }
|
||||
jsonrpsee = { features = ["server"], workspace = true }
|
||||
log = { workspace = true, default-features = true }
|
||||
rand = { workspace = true, default-features = true }
|
||||
serde = { features = ["derive"], workspace = true, default-features = true }
|
||||
serde_json = { workspace = true, default-features = true }
|
||||
subxt-signer = { workspace = true, features = ["unstable-eth"] }
|
||||
|
||||
# The Pezkuwi-SDK:
|
||||
pezkuwi-sdk = { features = [
|
||||
"fork-tree",
|
||||
"pezframe-benchmarking-cli",
|
||||
"frame-remote-externalities",
|
||||
"pezframe-support-procedural-tools",
|
||||
"generate-bags",
|
||||
"mmr-gadget",
|
||||
"mmr-rpc",
|
||||
"pezpallet-transaction-payment-rpc",
|
||||
"pezsc-allocator",
|
||||
"pezsc-authority-discovery",
|
||||
"pezsc-basic-authorship",
|
||||
"pezsc-block-builder",
|
||||
"pezsc-chain-spec",
|
||||
"pezsc-cli",
|
||||
"pezsc-client-api",
|
||||
"pezsc-client-db",
|
||||
"pezsc-consensus",
|
||||
"pezsc-consensus-aura",
|
||||
"pezsc-consensus-babe",
|
||||
"pezsc-consensus-babe-rpc",
|
||||
"pezsc-consensus-beefy",
|
||||
"pezsc-consensus-beefy-rpc",
|
||||
"pezsc-consensus-epochs",
|
||||
"pezsc-consensus-grandpa",
|
||||
"pezsc-consensus-grandpa-rpc",
|
||||
"pezsc-consensus-manual-seal",
|
||||
"pezsc-consensus-pow",
|
||||
"pezsc-consensus-slots",
|
||||
"pezsc-executor",
|
||||
"pezsc-executor-common",
|
||||
"pezsc-executor-polkavm",
|
||||
"pezsc-executor-wasmtime",
|
||||
"pezsc-informant",
|
||||
"pezsc-keystore",
|
||||
"pezsc-mixnet",
|
||||
"pezsc-network",
|
||||
"pezsc-network-common",
|
||||
"pezsc-network-gossip",
|
||||
"pezsc-network-light",
|
||||
"pezsc-network-statement",
|
||||
"pezsc-network-sync",
|
||||
"pezsc-network-transactions",
|
||||
"pezsc-network-types",
|
||||
"pezsc-offchain",
|
||||
"pezsc-proposer-metrics",
|
||||
"pezsc-rpc",
|
||||
"pezsc-rpc-api",
|
||||
"pezsc-rpc-server",
|
||||
"pezsc-rpc-spec-v2",
|
||||
"pezsc-service",
|
||||
"pezsc-state-db",
|
||||
"pezsc-statement-store",
|
||||
"pezsc-storage-monitor",
|
||||
"pezsc-sync-state-rpc",
|
||||
"pezsc-sysinfo",
|
||||
"pezsc-telemetry",
|
||||
"pezsc-tracing",
|
||||
"pezsc-transaction-pool",
|
||||
"pezsc-transaction-pool-api",
|
||||
"pezsc-utils",
|
||||
"pezsp-blockchain",
|
||||
"pezsp-consensus",
|
||||
"pezsp-core-hashing",
|
||||
"pezsp-core-hashing-proc-macro",
|
||||
"pezsp-database",
|
||||
"pezsp-maybe-compressed-blob",
|
||||
"pezsp-panic-handler",
|
||||
"pezsp-rpc",
|
||||
"pezstaging-chain-spec-builder",
|
||||
"pezstaging-node-inspect",
|
||||
"pezstaging-tracking-allocator",
|
||||
"std",
|
||||
"subkey",
|
||||
"bizinikiwi-build-script-utils",
|
||||
"bizinikiwi-frame-rpc-support",
|
||||
"bizinikiwi-frame-rpc-system",
|
||||
"bizinikiwi-prometheus-endpoint",
|
||||
"bizinikiwi-rpc-client",
|
||||
"bizinikiwi-state-trie-migration-rpc",
|
||||
"bizinikiwi-wasm-builder",
|
||||
"tracing-gum",
|
||||
], workspace = true, default-features = true }
|
||||
|
||||
# Shared code between the staging node and kitchensink runtime:
|
||||
kitchensink-runtime = { workspace = true }
|
||||
node-inspect = { optional = true, workspace = true, default-features = true }
|
||||
node-primitives = { workspace = true, default-features = true }
|
||||
node-rpc = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = { workspace = true }
|
||||
criterion = { features = [
|
||||
"async_tokio",
|
||||
], workspace = true, default-features = true }
|
||||
nix = { features = ["signal"], workspace = true }
|
||||
pretty_assertions.workspace = true
|
||||
regex = { workspace = true }
|
||||
scale-info = { features = [
|
||||
"derive",
|
||||
"serde",
|
||||
], workspace = true, default-features = true }
|
||||
soketto = { workspace = true }
|
||||
pezsp-keyring = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
tokio = { features = [
|
||||
"macros",
|
||||
"parking_lot",
|
||||
"time",
|
||||
], workspace = true, default-features = true }
|
||||
tokio-util = { features = ["compat"], workspace = true }
|
||||
wat = { workspace = true }
|
||||
|
||||
# These testing-only dependencies are not exported by the Pezkuwi-SDK crate:
|
||||
node-testing = { workspace = true }
|
||||
pezsc-service-test = { workspace = true }
|
||||
bizinikiwi-cli-test-utils = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
clap = { optional = true, workspace = true }
|
||||
clap_complete = { optional = true, workspace = true }
|
||||
|
||||
node-inspect = { optional = true, workspace = true, default-features = true }
|
||||
|
||||
pezkuwi-sdk = { features = [
|
||||
"pezframe-benchmarking-cli",
|
||||
"pezsc-cli",
|
||||
"pezsc-storage-monitor",
|
||||
"bizinikiwi-build-script-utils",
|
||||
], optional = true, workspace = true, default-features = true }
|
||||
|
||||
[features]
|
||||
default = ["cli"]
|
||||
cli = ["clap", "clap_complete", "node-inspect", "pezkuwi-sdk"]
|
||||
runtime-benchmarks = [
|
||||
"kitchensink-runtime/runtime-benchmarks",
|
||||
"node-inspect?/runtime-benchmarks",
|
||||
"node-primitives/runtime-benchmarks",
|
||||
"node-rpc/runtime-benchmarks",
|
||||
"node-testing/runtime-benchmarks",
|
||||
"pezkuwi-sdk/runtime-benchmarks",
|
||||
"pezsc-service-test/runtime-benchmarks",
|
||||
"pezsp-keyring/runtime-benchmarks",
|
||||
"bizinikiwi-cli-test-utils/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"kitchensink-runtime/try-runtime",
|
||||
"pezkuwi-sdk/try-runtime",
|
||||
"bizinikiwi-cli-test-utils/try-runtime",
|
||||
]
|
||||
@@ -0,0 +1,256 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput};
|
||||
|
||||
use kitchensink_runtime::{constants::currency::*, BalancesCall};
|
||||
use node_cli::service::{create_extrinsic, FullClient};
|
||||
use pezkuwi_sdk::pezsc_service::config::{ExecutorConfiguration, RpcConfiguration};
|
||||
use pezsc_block_builder::{BlockBuilderBuilder, BuiltBlock};
|
||||
use pezsc_consensus::{
|
||||
block_import::{BlockImportParams, ForkChoiceStrategy},
|
||||
BlockImport, StateAction,
|
||||
};
|
||||
use pezsc_service::{
|
||||
config::{
|
||||
BlocksPruning, DatabaseSource, KeystoreConfig, NetworkConfiguration, OffchainWorkerConfig,
|
||||
PruningMode, RpcBatchRequestConfig, WasmExecutionMethod, WasmtimeInstantiationStrategy,
|
||||
},
|
||||
BasePath, Configuration, Role,
|
||||
};
|
||||
use pezsp_blockchain::{ApplyExtrinsicFailed::Validity, Error::ApplyExtrinsicFailed};
|
||||
use pezsp_consensus::BlockOrigin;
|
||||
use pezsp_keyring::Sr25519Keyring;
|
||||
use pezsp_runtime::{
|
||||
generic,
|
||||
transaction_validity::{InvalidTransaction, TransactionValidityError},
|
||||
AccountId32, MultiAddress, OpaqueExtrinsic,
|
||||
};
|
||||
use pezstaging_node_cli as node_cli;
|
||||
use tokio::runtime::Handle;
|
||||
|
||||
fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase {
|
||||
let base_path = BasePath::new_temp_dir()
|
||||
.expect("getting the base path of a temporary path doesn't fail; qed");
|
||||
let root = base_path.path().to_path_buf();
|
||||
|
||||
let network_config = NetworkConfiguration::new(
|
||||
Sr25519Keyring::Alice.to_seed(),
|
||||
"network/test/0.1",
|
||||
Default::default(),
|
||||
None,
|
||||
);
|
||||
|
||||
let spec = Box::new(node_cli::chain_spec::development_config());
|
||||
|
||||
let config = Configuration {
|
||||
impl_name: "BenchmarkImpl".into(),
|
||||
impl_version: "1.0".into(),
|
||||
// We don't use the authority role since that would start producing blocks
|
||||
// in the background which would mess with our benchmark.
|
||||
role: Role::Full,
|
||||
tokio_handle,
|
||||
transaction_pool: Default::default(),
|
||||
network: network_config,
|
||||
keystore: KeystoreConfig::InMemory,
|
||||
database: DatabaseSource::RocksDb { path: root.join("db"), cache_size: 128 },
|
||||
trie_cache_maximum_size: Some(64 * 1024 * 1024),
|
||||
warm_up_trie_cache: None,
|
||||
state_pruning: Some(PruningMode::ArchiveAll),
|
||||
blocks_pruning: BlocksPruning::KeepAll,
|
||||
chain_spec: spec,
|
||||
executor: ExecutorConfiguration {
|
||||
wasm_method: WasmExecutionMethod::Compiled {
|
||||
instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite,
|
||||
},
|
||||
..ExecutorConfiguration::default()
|
||||
},
|
||||
rpc: RpcConfiguration {
|
||||
addr: None,
|
||||
max_connections: Default::default(),
|
||||
cors: None,
|
||||
methods: Default::default(),
|
||||
max_request_size: Default::default(),
|
||||
max_response_size: Default::default(),
|
||||
id_provider: Default::default(),
|
||||
max_subs_per_conn: Default::default(),
|
||||
port: 9944,
|
||||
message_buffer_capacity: Default::default(),
|
||||
batch_config: RpcBatchRequestConfig::Unlimited,
|
||||
rate_limit: None,
|
||||
rate_limit_whitelisted_ips: Default::default(),
|
||||
rate_limit_trust_proxy_headers: Default::default(),
|
||||
request_logger_limit: 1024,
|
||||
},
|
||||
prometheus_config: None,
|
||||
telemetry_endpoints: None,
|
||||
offchain_worker: OffchainWorkerConfig { enabled: true, indexing_enabled: false },
|
||||
force_authoring: false,
|
||||
disable_grandpa: false,
|
||||
dev_key_seed: Some(Sr25519Keyring::Alice.to_seed()),
|
||||
tracing_targets: None,
|
||||
tracing_receiver: Default::default(),
|
||||
announce_block: true,
|
||||
data_path: base_path.path().into(),
|
||||
base_path,
|
||||
wasm_runtime_overrides: None,
|
||||
};
|
||||
|
||||
node_cli::service::new_full_base::<pezsc_network::NetworkWorker<_, _>>(
|
||||
config,
|
||||
None,
|
||||
false,
|
||||
|_, _| (),
|
||||
)
|
||||
.expect("creating a full node doesn't fail")
|
||||
}
|
||||
|
||||
fn extrinsic_set_time(now: u64) -> OpaqueExtrinsic {
|
||||
let utx: kitchensink_runtime::UncheckedExtrinsic = generic::UncheckedExtrinsic::new_bare(
|
||||
kitchensink_runtime::RuntimeCall::Timestamp(pezpallet_timestamp::Call::set { now }),
|
||||
)
|
||||
.into();
|
||||
utx.into()
|
||||
}
|
||||
|
||||
fn import_block(client: &FullClient, built: BuiltBlock<node_primitives::Block>) {
|
||||
let mut params = BlockImportParams::new(BlockOrigin::File, built.block.header);
|
||||
params.state_action =
|
||||
StateAction::ApplyChanges(pezsc_consensus::StorageChanges::Changes(built.storage_changes));
|
||||
params.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
||||
futures::executor::block_on(client.import_block(params))
|
||||
.expect("importing a block doesn't fail");
|
||||
}
|
||||
|
||||
fn prepare_benchmark(client: &FullClient) -> (usize, Vec<OpaqueExtrinsic>) {
|
||||
const MINIMUM_PERIOD_FOR_BLOCKS: u64 = 1500;
|
||||
|
||||
let mut max_transfer_count = 0;
|
||||
let mut extrinsics = Vec::new();
|
||||
let mut block_builder = BlockBuilderBuilder::new(client)
|
||||
.on_parent_block(client.chain_info().best_hash)
|
||||
.with_parent_block_number(client.chain_info().best_number)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// Every block needs one timestamp extrinsic.
|
||||
let extrinsic_set_time = extrinsic_set_time(1 + MINIMUM_PERIOD_FOR_BLOCKS);
|
||||
block_builder.push(extrinsic_set_time.clone()).unwrap();
|
||||
extrinsics.push(extrinsic_set_time);
|
||||
|
||||
// Creating those is surprisingly costly, so let's only do it once and later just `clone` them.
|
||||
let src = Sr25519Keyring::Alice.pair();
|
||||
let dst: MultiAddress<AccountId32, u32> = Sr25519Keyring::Bob.to_account_id().into();
|
||||
|
||||
// Add as many transfer extrinsics as possible into a single block.
|
||||
for nonce in 0.. {
|
||||
let extrinsic: OpaqueExtrinsic = create_extrinsic(
|
||||
client,
|
||||
src.clone(),
|
||||
BalancesCall::transfer_allow_death { dest: dst.clone(), value: 1 * DOLLARS },
|
||||
Some(nonce),
|
||||
)
|
||||
.into();
|
||||
|
||||
match block_builder.push(extrinsic.clone()) {
|
||||
Ok(_) => {},
|
||||
Err(ApplyExtrinsicFailed(Validity(TransactionValidityError::Invalid(
|
||||
InvalidTransaction::ExhaustsResources,
|
||||
)))) => break,
|
||||
Err(error) => panic!("{}", error),
|
||||
}
|
||||
|
||||
extrinsics.push(extrinsic);
|
||||
max_transfer_count += 1;
|
||||
}
|
||||
|
||||
(max_transfer_count, extrinsics)
|
||||
}
|
||||
|
||||
fn block_production(c: &mut Criterion) {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let runtime = tokio::runtime::Runtime::new().expect("creating tokio runtime doesn't fail; qed");
|
||||
let tokio_handle = runtime.handle().clone();
|
||||
|
||||
let node = new_node(tokio_handle.clone());
|
||||
let client = &*node.client;
|
||||
|
||||
// Building the very first block is around ~30x slower than any subsequent one,
|
||||
// so let's make sure it's built and imported before we benchmark anything.
|
||||
let mut block_builder = BlockBuilderBuilder::new(client)
|
||||
.on_parent_block(client.chain_info().best_hash)
|
||||
.with_parent_block_number(client.chain_info().best_number)
|
||||
.build()
|
||||
.unwrap();
|
||||
block_builder.push(extrinsic_set_time(1)).unwrap();
|
||||
import_block(client, block_builder.build().unwrap());
|
||||
|
||||
let (max_transfer_count, extrinsics) = prepare_benchmark(&client);
|
||||
log::info!("Maximum transfer count: {}", max_transfer_count);
|
||||
|
||||
let mut group = c.benchmark_group("Block production");
|
||||
|
||||
group.sample_size(10);
|
||||
group.throughput(Throughput::Elements(max_transfer_count as u64));
|
||||
|
||||
let chain = client.chain_info();
|
||||
let best_hash = chain.best_hash;
|
||||
let best_number = chain.best_number;
|
||||
|
||||
group.bench_function(format!("{} transfers (no proof)", max_transfer_count), |b| {
|
||||
b.iter_batched(
|
||||
|| extrinsics.clone(),
|
||||
|extrinsics| {
|
||||
let mut block_builder = BlockBuilderBuilder::new(client)
|
||||
.on_parent_block(best_hash)
|
||||
.with_parent_block_number(best_number)
|
||||
.build()
|
||||
.unwrap();
|
||||
for extrinsic in extrinsics {
|
||||
block_builder.push(extrinsic).unwrap();
|
||||
}
|
||||
block_builder.build().unwrap()
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
)
|
||||
});
|
||||
|
||||
group.bench_function(format!("{} transfers (with proof)", max_transfer_count), |b| {
|
||||
b.iter_batched(
|
||||
|| extrinsics.clone(),
|
||||
|extrinsics| {
|
||||
let mut block_builder = BlockBuilderBuilder::new(client)
|
||||
.on_parent_block(best_hash)
|
||||
.with_parent_block_number(best_number)
|
||||
.build()
|
||||
.unwrap();
|
||||
for extrinsic in extrinsics {
|
||||
block_builder.push(extrinsic).unwrap();
|
||||
}
|
||||
block_builder.build().unwrap()
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, block_production);
|
||||
criterion_main!(benches);
|
||||
@@ -0,0 +1,206 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use pezframe_support::Hashable;
|
||||
use kitchensink_runtime::{
|
||||
constants::currency::*, Block, BuildStorage, CheckedExtrinsic, Header, RuntimeCall,
|
||||
RuntimeGenesisConfig, UncheckedExtrinsic,
|
||||
};
|
||||
use node_primitives::{BlockNumber, Hash};
|
||||
use node_testing::keyring::*;
|
||||
use pezsc_executor::{Externalities, RuntimeVersionOf};
|
||||
use pezsp_core::{
|
||||
storage::well_known_keys,
|
||||
traits::{CallContext, CodeExecutor, RuntimeCode},
|
||||
};
|
||||
use pezsp_runtime::{generic::ExtrinsicFormat, traits::BlakeTwo256};
|
||||
use pezsp_state_machine::TestExternalities as CoreTestExternalities;
|
||||
use pezstaging_node_cli::service::RuntimeExecutor;
|
||||
|
||||
criterion_group!(benches, bench_execute_block);
|
||||
criterion_main!(benches);
|
||||
|
||||
/// The wasm runtime code.
|
||||
pub fn compact_code_unwrap() -> &'static [u8] {
|
||||
kitchensink_runtime::WASM_BINARY.expect(
|
||||
"Development wasm binary is not available. Testing is only supported with the flag \
|
||||
disabled.",
|
||||
)
|
||||
}
|
||||
|
||||
const GENESIS_HASH: [u8; 32] = [69u8; 32];
|
||||
|
||||
const TRANSACTION_VERSION: u32 = kitchensink_runtime::VERSION.transaction_version;
|
||||
|
||||
const SPEC_VERSION: u32 = kitchensink_runtime::VERSION.spec_version;
|
||||
|
||||
const HEAP_PAGES: u64 = 20;
|
||||
|
||||
type TestExternalities<H> = CoreTestExternalities<H>;
|
||||
|
||||
fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic {
|
||||
node_testing::keyring::sign(xt, SPEC_VERSION, TRANSACTION_VERSION, GENESIS_HASH, None)
|
||||
}
|
||||
|
||||
fn new_test_ext(genesis_config: &RuntimeGenesisConfig) -> TestExternalities<BlakeTwo256> {
|
||||
let mut test_ext = TestExternalities::new_with_code(
|
||||
compact_code_unwrap(),
|
||||
genesis_config.build_storage().unwrap(),
|
||||
);
|
||||
test_ext
|
||||
.ext()
|
||||
.place_storage(well_known_keys::HEAP_PAGES.to_vec(), Some(HEAP_PAGES.encode()));
|
||||
test_ext
|
||||
}
|
||||
|
||||
fn construct_block<E: Externalities>(
|
||||
executor: &RuntimeExecutor,
|
||||
ext: &mut E,
|
||||
number: BlockNumber,
|
||||
parent_hash: Hash,
|
||||
extrinsics: Vec<CheckedExtrinsic>,
|
||||
) -> (Vec<u8>, Hash) {
|
||||
use pezsp_trie::{LayoutV0, TrieConfiguration};
|
||||
|
||||
// sign extrinsics.
|
||||
let extrinsics = extrinsics.into_iter().map(sign).collect::<Vec<_>>();
|
||||
|
||||
// calculate the header fields that we can.
|
||||
let extrinsics_root =
|
||||
LayoutV0::<BlakeTwo256>::ordered_trie_root(extrinsics.iter().map(Encode::encode))
|
||||
.to_fixed_bytes()
|
||||
.into();
|
||||
|
||||
let header = Header {
|
||||
parent_hash,
|
||||
number,
|
||||
extrinsics_root,
|
||||
state_root: Default::default(),
|
||||
digest: Default::default(),
|
||||
};
|
||||
|
||||
let runtime_code = RuntimeCode {
|
||||
code_fetcher: &pezsp_core::traits::WrappedRuntimeCode(compact_code_unwrap().into()),
|
||||
hash: vec![1, 2, 3],
|
||||
heap_pages: None,
|
||||
};
|
||||
|
||||
// execute the block to get the real header.
|
||||
executor
|
||||
.call(ext, &runtime_code, "Core_initialize_block", &header.encode(), CallContext::Offchain)
|
||||
.0
|
||||
.unwrap();
|
||||
|
||||
for i in extrinsics.iter() {
|
||||
executor
|
||||
.call(
|
||||
ext,
|
||||
&runtime_code,
|
||||
"BlockBuilder_apply_extrinsic",
|
||||
&i.encode(),
|
||||
CallContext::Offchain,
|
||||
)
|
||||
.0
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let header = Header::decode(
|
||||
&mut &executor
|
||||
.call(
|
||||
ext,
|
||||
&runtime_code,
|
||||
"BlockBuilder_finalize_block",
|
||||
&[0u8; 0],
|
||||
CallContext::Offchain,
|
||||
)
|
||||
.0
|
||||
.unwrap()[..],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let hash = header.blake2_256();
|
||||
(Block { header, extrinsics }.encode(), hash.into())
|
||||
}
|
||||
|
||||
fn test_blocks(
|
||||
genesis_config: &RuntimeGenesisConfig,
|
||||
executor: &RuntimeExecutor,
|
||||
) -> Vec<(Vec<u8>, Hash)> {
|
||||
let mut test_ext = new_test_ext(genesis_config);
|
||||
let mut block1_extrinsics = vec![CheckedExtrinsic {
|
||||
format: ExtrinsicFormat::Bare,
|
||||
function: RuntimeCall::Timestamp(pezpallet_timestamp::Call::set { now: 0 }),
|
||||
}];
|
||||
block1_extrinsics.extend((0..20).map(|i| CheckedExtrinsic {
|
||||
format: ExtrinsicFormat::Signed(alice(), tx_ext(i, 0)),
|
||||
function: RuntimeCall::Balances(pezpallet_balances::Call::transfer_allow_death {
|
||||
dest: bob().into(),
|
||||
value: 1 * DOLLARS,
|
||||
}),
|
||||
}));
|
||||
let block1 =
|
||||
construct_block(executor, &mut test_ext.ext(), 1, GENESIS_HASH.into(), block1_extrinsics);
|
||||
|
||||
vec![block1]
|
||||
}
|
||||
|
||||
fn bench_execute_block(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("execute blocks");
|
||||
|
||||
group.bench_function("wasm", |b| {
|
||||
let genesis_config = node_testing::genesis::config();
|
||||
|
||||
let executor = RuntimeExecutor::builder().build();
|
||||
let runtime_code = RuntimeCode {
|
||||
code_fetcher: &pezsp_core::traits::WrappedRuntimeCode(compact_code_unwrap().into()),
|
||||
hash: vec![1, 2, 3],
|
||||
heap_pages: None,
|
||||
};
|
||||
|
||||
// Get the runtime version to initialize the runtimes cache.
|
||||
{
|
||||
let mut test_ext = new_test_ext(&genesis_config);
|
||||
executor.runtime_version(&mut test_ext.ext(), &runtime_code).unwrap();
|
||||
}
|
||||
|
||||
let blocks = test_blocks(&genesis_config, &executor);
|
||||
|
||||
b.iter_batched_ref(
|
||||
|| new_test_ext(&genesis_config),
|
||||
|test_ext| {
|
||||
for block in blocks.iter() {
|
||||
executor
|
||||
.call(
|
||||
&mut test_ext.ext(),
|
||||
&runtime_code,
|
||||
"Core_execute_block",
|
||||
&block.0,
|
||||
CallContext::Offchain,
|
||||
)
|
||||
.0
|
||||
.unwrap();
|
||||
}
|
||||
},
|
||||
BatchSize::LargeInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput};
|
||||
use futures::{future, StreamExt};
|
||||
use kitchensink_runtime::{constants::currency::*, BalancesCall, SudoCall};
|
||||
use node_cli::service::{create_extrinsic, fetch_nonce, FullClient, TransactionPool};
|
||||
use node_primitives::AccountId;
|
||||
use pezkuwi_sdk::{
|
||||
pezsc_service::config::{ExecutorConfiguration, RpcConfiguration},
|
||||
pezsc_transaction_pool_api::TransactionPool as _,
|
||||
*,
|
||||
};
|
||||
use pezsc_service::{
|
||||
config::{
|
||||
BlocksPruning, DatabaseSource, KeystoreConfig, NetworkConfiguration, OffchainWorkerConfig,
|
||||
PruningMode, RpcBatchRequestConfig, TransactionPoolOptions,
|
||||
},
|
||||
BasePath, Configuration, Role,
|
||||
};
|
||||
use pezsc_transaction_pool_api::{TransactionSource, TransactionStatus};
|
||||
use pezsp_core::{crypto::Pair, sr25519};
|
||||
use pezsp_keyring::Sr25519Keyring;
|
||||
use pezsp_runtime::OpaqueExtrinsic;
|
||||
use pezstaging_node_cli as node_cli;
|
||||
use tokio::runtime::Handle;
|
||||
|
||||
fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase {
|
||||
let base_path = BasePath::new_temp_dir().expect("Creates base path");
|
||||
let root = base_path.path().to_path_buf();
|
||||
|
||||
let network_config = NetworkConfiguration::new(
|
||||
Sr25519Keyring::Alice.to_seed(),
|
||||
"network/test/0.1",
|
||||
Default::default(),
|
||||
None,
|
||||
);
|
||||
|
||||
let spec = Box::new(node_cli::chain_spec::development_config());
|
||||
|
||||
let config = Configuration {
|
||||
impl_name: "BenchmarkImpl".into(),
|
||||
impl_version: "1.0".into(),
|
||||
role: Role::Authority,
|
||||
tokio_handle: tokio_handle.clone(),
|
||||
transaction_pool: TransactionPoolOptions::new_for_benchmarks(),
|
||||
network: network_config,
|
||||
keystore: KeystoreConfig::InMemory,
|
||||
database: DatabaseSource::RocksDb { path: root.join("db"), cache_size: 128 },
|
||||
trie_cache_maximum_size: Some(64 * 1024 * 1024),
|
||||
warm_up_trie_cache: None,
|
||||
state_pruning: Some(PruningMode::ArchiveAll),
|
||||
blocks_pruning: BlocksPruning::KeepAll,
|
||||
chain_spec: spec,
|
||||
executor: ExecutorConfiguration::default(),
|
||||
rpc: RpcConfiguration {
|
||||
addr: None,
|
||||
max_connections: Default::default(),
|
||||
cors: None,
|
||||
methods: Default::default(),
|
||||
max_request_size: Default::default(),
|
||||
max_response_size: Default::default(),
|
||||
id_provider: Default::default(),
|
||||
max_subs_per_conn: Default::default(),
|
||||
port: 9944,
|
||||
message_buffer_capacity: Default::default(),
|
||||
batch_config: RpcBatchRequestConfig::Unlimited,
|
||||
rate_limit: None,
|
||||
rate_limit_whitelisted_ips: Default::default(),
|
||||
rate_limit_trust_proxy_headers: Default::default(),
|
||||
request_logger_limit: 1024,
|
||||
},
|
||||
prometheus_config: None,
|
||||
telemetry_endpoints: None,
|
||||
offchain_worker: OffchainWorkerConfig { enabled: true, indexing_enabled: false },
|
||||
force_authoring: false,
|
||||
disable_grandpa: false,
|
||||
dev_key_seed: Some(Sr25519Keyring::Alice.to_seed()),
|
||||
tracing_targets: None,
|
||||
tracing_receiver: Default::default(),
|
||||
announce_block: true,
|
||||
data_path: base_path.path().into(),
|
||||
base_path,
|
||||
wasm_runtime_overrides: None,
|
||||
};
|
||||
|
||||
tokio_handle.block_on(async move {
|
||||
node_cli::service::new_full_base::<pezsc_network::NetworkWorker<_, _>>(
|
||||
config,
|
||||
None,
|
||||
false,
|
||||
|_, _| (),
|
||||
)
|
||||
.expect("Creates node")
|
||||
})
|
||||
}
|
||||
|
||||
fn create_accounts(num: usize) -> Vec<sr25519::Pair> {
|
||||
(0..num)
|
||||
.map(|i| {
|
||||
Pair::from_string(&format!("{}/{}", Sr25519Keyring::Alice.to_seed(), i), None)
|
||||
.expect("Creates account pair")
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Create the extrinsics that will initialize the accounts from the sudo account (Alice).
|
||||
///
|
||||
/// `start_nonce` is the current nonce of Alice.
|
||||
fn create_account_extrinsics(
|
||||
client: &FullClient,
|
||||
accounts: &[sr25519::Pair],
|
||||
) -> Vec<OpaqueExtrinsic> {
|
||||
let start_nonce = fetch_nonce(client, Sr25519Keyring::Alice.pair());
|
||||
|
||||
accounts
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(i, a)| {
|
||||
vec![
|
||||
// Reset the nonce by removing any funds
|
||||
create_extrinsic(
|
||||
client,
|
||||
Sr25519Keyring::Alice.pair(),
|
||||
SudoCall::sudo {
|
||||
call: Box::new(
|
||||
BalancesCall::force_set_balance {
|
||||
who: AccountId::from(a.public()).into(),
|
||||
new_free: 0,
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
},
|
||||
Some(start_nonce + (i as u32) * 2),
|
||||
),
|
||||
// Give back funds
|
||||
create_extrinsic(
|
||||
client,
|
||||
Sr25519Keyring::Alice.pair(),
|
||||
SudoCall::sudo {
|
||||
call: Box::new(
|
||||
BalancesCall::force_set_balance {
|
||||
who: AccountId::from(a.public()).into(),
|
||||
new_free: 1_000_000 * DOLLARS,
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
},
|
||||
Some(start_nonce + (i as u32) * 2 + 1),
|
||||
),
|
||||
]
|
||||
})
|
||||
.map(OpaqueExtrinsic::from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn create_benchmark_extrinsics(
|
||||
client: &FullClient,
|
||||
accounts: &[sr25519::Pair],
|
||||
extrinsics_per_account: usize,
|
||||
) -> Vec<OpaqueExtrinsic> {
|
||||
accounts
|
||||
.iter()
|
||||
.flat_map(|account| {
|
||||
(0..extrinsics_per_account).map(move |nonce| {
|
||||
create_extrinsic(
|
||||
client,
|
||||
account.clone(),
|
||||
BalancesCall::transfer_allow_death {
|
||||
dest: Sr25519Keyring::Bob.to_account_id().into(),
|
||||
value: 1 * DOLLARS,
|
||||
},
|
||||
Some(nonce as u32),
|
||||
)
|
||||
})
|
||||
})
|
||||
.map(OpaqueExtrinsic::from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
async fn submit_tx_and_wait_for_inclusion(
|
||||
tx_pool: &TransactionPool,
|
||||
tx: OpaqueExtrinsic,
|
||||
client: &FullClient,
|
||||
wait_for_finalized: bool,
|
||||
) {
|
||||
let best_hash = client.chain_info().best_hash;
|
||||
|
||||
let mut watch = tx_pool
|
||||
.submit_and_watch(best_hash, TransactionSource::External, tx.clone())
|
||||
.await
|
||||
.expect("Submits tx to pool")
|
||||
.fuse();
|
||||
|
||||
loop {
|
||||
match watch.select_next_some().await {
|
||||
TransactionStatus::Finalized(_) => break,
|
||||
TransactionStatus::InBlock(_) if !wait_for_finalized => break,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn transaction_pool_benchmarks(c: &mut Criterion) {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let runtime = tokio::runtime::Runtime::new().expect("Creates tokio runtime");
|
||||
let tokio_handle = runtime.handle().clone();
|
||||
|
||||
let node = new_node(tokio_handle.clone());
|
||||
|
||||
let account_num = 10;
|
||||
let extrinsics_per_account = 2000;
|
||||
let accounts = create_accounts(account_num);
|
||||
|
||||
let mut group = c.benchmark_group("Transaction pool");
|
||||
|
||||
group.sample_size(10);
|
||||
group.throughput(Throughput::Elements(account_num as u64 * extrinsics_per_account as u64));
|
||||
|
||||
let mut counter = 1;
|
||||
group.bench_function(
|
||||
format!("{} transfers from {} accounts", account_num * extrinsics_per_account, account_num),
|
||||
move |b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
let prepare_extrinsics = create_account_extrinsics(&node.client, &accounts);
|
||||
|
||||
runtime.block_on(future::join_all(prepare_extrinsics.into_iter().map(|tx| {
|
||||
submit_tx_and_wait_for_inclusion(
|
||||
&node.transaction_pool,
|
||||
tx,
|
||||
&node.client,
|
||||
true,
|
||||
)
|
||||
})));
|
||||
|
||||
create_benchmark_extrinsics(&node.client, &accounts, extrinsics_per_account)
|
||||
},
|
||||
|extrinsics| {
|
||||
runtime.block_on(future::join_all(extrinsics.into_iter().map(|tx| {
|
||||
submit_tx_and_wait_for_inclusion(
|
||||
&node.transaction_pool,
|
||||
tx,
|
||||
&node.client,
|
||||
false,
|
||||
)
|
||||
})));
|
||||
|
||||
println!("Finished {}", counter);
|
||||
counter += 1;
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
criterion_group!(benches, transaction_pool_benchmarks);
|
||||
criterion_main!(benches);
|
||||
@@ -0,0 +1,28 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Bizinikiwi Node CLI
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use pezkuwi_sdk::*;
|
||||
use pezstaging_node_cli as node_cli;
|
||||
|
||||
fn main() -> pezsc_cli::Result<()> {
|
||||
node_cli::run()
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
fn main() {
|
||||
#[cfg(feature = "cli")]
|
||||
cli::main();
|
||||
}
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
mod cli {
|
||||
include!("src/cli.rs");
|
||||
|
||||
use clap::{CommandFactory, ValueEnum};
|
||||
use clap_complete::{generate_to, Shell};
|
||||
use pezkuwi_sdk::bizinikiwi_build_script_utils::{
|
||||
generate_cargo_keys, rerun_if_git_head_changed,
|
||||
};
|
||||
use std::{env, fs, path::Path};
|
||||
|
||||
pub fn main() {
|
||||
build_shell_completion();
|
||||
generate_cargo_keys();
|
||||
|
||||
rerun_if_git_head_changed();
|
||||
}
|
||||
|
||||
/// Build shell completion scripts for all known shells.
|
||||
fn build_shell_completion() {
|
||||
for shell in Shell::value_variants() {
|
||||
build_completion(shell);
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the shell auto-completion for a given Shell.
|
||||
fn build_completion(shell: &Shell) {
|
||||
let outdir = match env::var_os("OUT_DIR") {
|
||||
None => return,
|
||||
Some(dir) => dir,
|
||||
};
|
||||
let path = Path::new(&outdir)
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join("completion-scripts");
|
||||
|
||||
fs::create_dir(&path).ok();
|
||||
|
||||
let _ = generate_to(*shell, &mut Cli::command(), "bizinikiwi-node", &path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
# Shell completion
|
||||
|
||||
The Bizinikiwi cli command supports shell auto-completion. For this to work, you will need to run the
|
||||
completion script matching your build and system.
|
||||
|
||||
Assuming you built a release version using `cargo build --release` and use `bash` run the following:
|
||||
|
||||
`source target/release/completion-scripts/bizinikiwi.bash`
|
||||
|
||||
You can find completion scripts for:
|
||||
- bash
|
||||
- fish
|
||||
- zsh
|
||||
- elvish
|
||||
- powershell
|
||||
|
||||
To make this change persistent, you can proceed as follows:
|
||||
|
||||
```shell
|
||||
COMPL_DIR=$HOME/.completion
|
||||
mkdir -p $COMPL_DIR
|
||||
cp -f target/release/completion-scripts/bizinikiwi.bash $COMPL_DIR/
|
||||
echo "source $COMPL_DIR/bizinikiwi.bash" >> $HOME/.bash_profile
|
||||
source $HOME/.bash_profile
|
||||
```
|
||||
|
||||
When you build a new version of Bizinikiwi, the following will ensure your auto-completion script matches the current binary:
|
||||
|
||||
```shell
|
||||
COMPL_DIR=$HOME/.completion
|
||||
mkdir -p $COMPL_DIR
|
||||
cp -f target/release/completion-scripts/bizinikiwi.bash $COMPL_DIR/
|
||||
source $HOME/.bash_profile
|
||||
```
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,124 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Setup code for [`super::command`] which would otherwise bloat that module.
|
||||
//!
|
||||
//! Should only be used for benchmarking as it may break in other contexts.
|
||||
|
||||
use crate::service::{create_extrinsic, FullClient};
|
||||
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use kitchensink_runtime::{BalancesCall, SystemCall};
|
||||
use node_primitives::{AccountId, Balance};
|
||||
use pezsc_cli::Result;
|
||||
use pezsp_inherents::{InherentData, InherentDataProvider};
|
||||
use pezsp_keyring::Sr25519Keyring;
|
||||
use pezsp_runtime::OpaqueExtrinsic;
|
||||
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
/// Generates `System::Remark` extrinsics for the benchmarks.
|
||||
///
|
||||
/// Note: Should only be used for benchmarking.
|
||||
pub struct RemarkBuilder {
|
||||
client: Arc<FullClient>,
|
||||
}
|
||||
|
||||
impl RemarkBuilder {
|
||||
/// Creates a new [`Self`] from the given client.
|
||||
pub fn new(client: Arc<FullClient>) -> Self {
|
||||
Self { client }
|
||||
}
|
||||
}
|
||||
|
||||
impl pezframe_benchmarking_cli::ExtrinsicBuilder for RemarkBuilder {
|
||||
fn pallet(&self) -> &str {
|
||||
"system"
|
||||
}
|
||||
|
||||
fn extrinsic(&self) -> &str {
|
||||
"remark"
|
||||
}
|
||||
|
||||
fn build(&self, nonce: u32) -> std::result::Result<OpaqueExtrinsic, &'static str> {
|
||||
let acc = Sr25519Keyring::Bob.pair();
|
||||
let extrinsic: OpaqueExtrinsic = create_extrinsic(
|
||||
self.client.as_ref(),
|
||||
acc,
|
||||
SystemCall::remark { remark: vec![] },
|
||||
Some(nonce),
|
||||
)
|
||||
.into();
|
||||
|
||||
Ok(extrinsic)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates `Balances::TransferKeepAlive` extrinsics for the benchmarks.
|
||||
///
|
||||
/// Note: Should only be used for benchmarking.
|
||||
pub struct TransferKeepAliveBuilder {
|
||||
client: Arc<FullClient>,
|
||||
dest: AccountId,
|
||||
value: Balance,
|
||||
}
|
||||
|
||||
impl TransferKeepAliveBuilder {
|
||||
/// Creates a new [`Self`] from the given client.
|
||||
pub fn new(client: Arc<FullClient>, dest: AccountId, value: Balance) -> Self {
|
||||
Self { client, dest, value }
|
||||
}
|
||||
}
|
||||
|
||||
impl pezframe_benchmarking_cli::ExtrinsicBuilder for TransferKeepAliveBuilder {
|
||||
fn pallet(&self) -> &str {
|
||||
"balances"
|
||||
}
|
||||
|
||||
fn extrinsic(&self) -> &str {
|
||||
"transfer_keep_alive"
|
||||
}
|
||||
|
||||
fn build(&self, nonce: u32) -> std::result::Result<OpaqueExtrinsic, &'static str> {
|
||||
let acc = Sr25519Keyring::Bob.pair();
|
||||
let extrinsic: OpaqueExtrinsic = create_extrinsic(
|
||||
self.client.as_ref(),
|
||||
acc,
|
||||
BalancesCall::transfer_keep_alive {
|
||||
dest: self.dest.clone().into(),
|
||||
value: self.value.into(),
|
||||
},
|
||||
Some(nonce),
|
||||
)
|
||||
.into();
|
||||
|
||||
Ok(extrinsic)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates inherent data for the `benchmark overhead` command.
|
||||
pub fn inherent_benchmark_data() -> Result<InherentData> {
|
||||
let mut inherent_data = InherentData::new();
|
||||
let d = Duration::from_millis(0);
|
||||
let timestamp = pezsp_timestamp::InherentDataProvider::new(d.into());
|
||||
|
||||
futures::executor::block_on(timestamp.provide_inherent_data(&mut inherent_data))
|
||||
.map_err(|e| format!("creating inherent data: {:?}", e))?;
|
||||
Ok(inherent_data)
|
||||
}
|
||||
@@ -0,0 +1,507 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Bizinikiwi chain configurations.
|
||||
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use crate::chain_spec::pezsc_service::Properties;
|
||||
use kitchensink_runtime::{
|
||||
genesis_config_presets::{Staker, ENDOWMENT, STASH},
|
||||
wasm_binary_unwrap, Block, MaxNominations, StakerStatus,
|
||||
};
|
||||
use pezpallet_im_online::sr25519::AuthorityId as ImOnlineId;
|
||||
use pezpallet_revive::is_eth_derived;
|
||||
use pezsc_chain_spec::ChainSpecExtension;
|
||||
use pezsc_service::ChainType;
|
||||
use pezsc_telemetry::TelemetryEndpoints;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use pezsp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
|
||||
use pezsp_consensus_babe::AuthorityId as BabeId;
|
||||
use pezsp_consensus_beefy::ecdsa_crypto::AuthorityId as BeefyId;
|
||||
use pezsp_consensus_grandpa::AuthorityId as GrandpaId;
|
||||
use pezsp_core::crypto::UncheckedInto;
|
||||
use pezsp_mixnet::types::AuthorityId as MixnetId;
|
||||
|
||||
pub use kitchensink_runtime::RuntimeGenesisConfig;
|
||||
pub use node_primitives::{AccountId, Balance, Signature};
|
||||
|
||||
const STAGING_TELEMETRY_URL: &str = "wss://telemetry.pezkuwichain.io/submit/";
|
||||
|
||||
/// Node `ChainSpec` extensions.
|
||||
///
|
||||
/// Additional parameters for some Bizinikiwi core modules,
|
||||
/// customizable from the chain spec.
|
||||
#[derive(Default, Clone, Serialize, Deserialize, ChainSpecExtension)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Extensions {
|
||||
/// Block numbers with known hashes.
|
||||
pub fork_blocks: pezsc_client_api::ForkBlocks<Block>,
|
||||
/// Known bad block hashes.
|
||||
pub bad_blocks: pezsc_client_api::BadBlocks<Block>,
|
||||
/// The light sync state extension used by the sync-state rpc.
|
||||
pub light_sync_state: pezsc_sync_state_rpc::LightSyncStateExtension,
|
||||
}
|
||||
|
||||
/// Specialized `ChainSpec`.
|
||||
pub type ChainSpec = pezsc_service::GenericChainSpec<Extensions>;
|
||||
/// Flaming Fir testnet generator
|
||||
pub fn flaming_fir_config() -> Result<ChainSpec, String> {
|
||||
ChainSpec::from_json_bytes(&include_bytes!("../res/flaming-fir.json")[..])
|
||||
}
|
||||
|
||||
fn configure_accounts_for_staging_testnet() -> (
|
||||
Vec<(
|
||||
AccountId,
|
||||
AccountId,
|
||||
GrandpaId,
|
||||
BabeId,
|
||||
ImOnlineId,
|
||||
AuthorityDiscoveryId,
|
||||
MixnetId,
|
||||
BeefyId,
|
||||
)>,
|
||||
AccountId,
|
||||
Vec<AccountId>,
|
||||
) {
|
||||
#[rustfmt::skip]
|
||||
// stash, controller, session-key, beefy id
|
||||
// generated with secret:
|
||||
// for i in 1 2 3 4 ; do for j in stash controller; do subkey inspect "$secret"/fir/$j/$i; done; done
|
||||
//
|
||||
// and
|
||||
//
|
||||
// for i in 1 2 3 4 ; do for j in session; do subkey inspect --scheme ed25519 "$secret"//fir//$j//$i; done; done
|
||||
//
|
||||
// and
|
||||
//
|
||||
// for i in 1 2 3 4 ; do for j in session; do subkey inspect --scheme ecdsa "$secret"//fir//$j//$i; done; done
|
||||
|
||||
let initial_authorities: Vec<(
|
||||
AccountId,
|
||||
AccountId,
|
||||
GrandpaId,
|
||||
BabeId,
|
||||
ImOnlineId,
|
||||
AuthorityDiscoveryId,
|
||||
MixnetId,
|
||||
BeefyId,
|
||||
)> = vec![
|
||||
(
|
||||
// 5Fbsd6WXDGiLTxunqeK5BATNiocfCqu9bS1yArVjCgeBLkVy
|
||||
array_bytes::hex_n_into_unchecked("9c7a2ee14e565db0c69f78c7b4cd839fbf52b607d867e9e9c5a79042898a0d12"),
|
||||
// 5EnCiV7wSHeNhjW3FSUwiJNkcc2SBkPLn5Nj93FmbLtBjQUq
|
||||
array_bytes::hex_n_into_unchecked("781ead1e2fa9ccb74b44c19d29cb2a7a4b5be3972927ae98cd3877523976a276"),
|
||||
// 5Fb9ayurnxnaXj56CjmyQLBiadfRCqUbL2VWNbbe1nZU6wiC
|
||||
array_bytes::hex2array_unchecked("9becad03e6dcac03cee07edebca5475314861492cdfc96a2144a67bbe9699332")
|
||||
.unchecked_into(),
|
||||
// 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8
|
||||
array_bytes::hex2array_unchecked("6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106")
|
||||
.unchecked_into(),
|
||||
// 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8
|
||||
array_bytes::hex2array_unchecked("6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106")
|
||||
.unchecked_into(),
|
||||
// 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8
|
||||
array_bytes::hex2array_unchecked("6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106")
|
||||
.unchecked_into(),
|
||||
// 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8
|
||||
array_bytes::hex2array_unchecked("6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106")
|
||||
.unchecked_into(),
|
||||
// 5DMLFcDdLLQbw696YfHaWBpQR99HwR456ycSCfr6L7KXGYK8
|
||||
array_bytes::hex2array_unchecked("035560fafa241739869360aa4b32bc98953172ceb41a19c6cc1a27962fb3d1ecec")
|
||||
.unchecked_into(),
|
||||
),
|
||||
(
|
||||
// 5ERawXCzCWkjVq3xz1W5KGNtVx2VdefvZ62Bw1FEuZW4Vny2
|
||||
array_bytes::hex_n_into_unchecked("68655684472b743e456907b398d3a44c113f189e56d1bbfd55e889e295dfde78"),
|
||||
// 5Gc4vr42hH1uDZc93Nayk5G7i687bAQdHHc9unLuyeawHipF
|
||||
array_bytes::hex_n_into_unchecked("c8dc79e36b29395413399edaec3e20fcca7205fb19776ed8ddb25d6f427ec40e"),
|
||||
// 5EockCXN6YkiNCDjpqqnbcqd4ad35nU4RmA1ikM4YeRN4WcE
|
||||
array_bytes::hex2array_unchecked("7932cff431e748892fa48e10c63c17d30f80ca42e4de3921e641249cd7fa3c2f")
|
||||
.unchecked_into(),
|
||||
// 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ
|
||||
array_bytes::hex2array_unchecked("482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e")
|
||||
.unchecked_into(),
|
||||
// 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ
|
||||
array_bytes::hex2array_unchecked("482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e")
|
||||
.unchecked_into(),
|
||||
// 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ
|
||||
array_bytes::hex2array_unchecked("482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e")
|
||||
.unchecked_into(),
|
||||
// 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ
|
||||
array_bytes::hex2array_unchecked("482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e")
|
||||
.unchecked_into(),
|
||||
// 5FYk11kNtB4178wLKJ2RNoUzzcjgRUciFe3SJDVZXhqX4dzG
|
||||
array_bytes::hex2array_unchecked("02da1ab255ed888ee3e19b73d335fc13160b3eb10456c2d17c6a8ea7de403d2445")
|
||||
.unchecked_into(),
|
||||
),
|
||||
(
|
||||
// 5DyVtKWPidondEu8iHZgi6Ffv9yrJJ1NDNLom3X9cTDi98qp
|
||||
array_bytes::hex_n_into_unchecked("547ff0ab649283a7ae01dbc2eb73932eba2fb09075e9485ff369082a2ff38d65"),
|
||||
// 5FeD54vGVNpFX3PndHPXJ2MDakc462vBCD5mgtWRnWYCpZU9
|
||||
array_bytes::hex_n_into_unchecked("9e42241d7cd91d001773b0b616d523dd80e13c6c2cab860b1234ef1b9ffc1526"),
|
||||
// 5E1jLYfLdUQKrFrtqoKgFrRvxM3oQPMbf6DfcsrugZZ5Bn8d
|
||||
array_bytes::hex2array_unchecked("5633b70b80a6c8bb16270f82cca6d56b27ed7b76c8fd5af2986a25a4788ce440")
|
||||
.unchecked_into(),
|
||||
// 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH
|
||||
array_bytes::hex2array_unchecked("482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a")
|
||||
.unchecked_into(),
|
||||
// 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH
|
||||
array_bytes::hex2array_unchecked("482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a")
|
||||
.unchecked_into(),
|
||||
// 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH
|
||||
array_bytes::hex2array_unchecked("482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a")
|
||||
.unchecked_into(),
|
||||
// 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH
|
||||
array_bytes::hex2array_unchecked("482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a")
|
||||
.unchecked_into(),
|
||||
// 5GQx4FToRBPqfani6o7owFJE1UstiviqbPP7HPWyvtXWWukn
|
||||
array_bytes::hex2array_unchecked("036a818b3f59579c5fbbe4fede64f49dbf090ba883eb2a175d5ca90e5adb5f0b3e")
|
||||
.unchecked_into(),
|
||||
),
|
||||
(
|
||||
// 5HYZnKWe5FVZQ33ZRJK1rG3WaLMztxWrrNDb1JRwaHHVWyP9
|
||||
array_bytes::hex_n_into_unchecked("f26cdb14b5aec7b2789fd5ca80f979cef3761897ae1f37ffb3e154cbcc1c2663"),
|
||||
// 5EPQdAQ39WQNLCRjWsCk5jErsCitHiY5ZmjfWzzbXDoAoYbn
|
||||
array_bytes::hex_n_into_unchecked("66bc1e5d275da50b72b15de072a2468a5ad414919ca9054d2695767cf650012f"),
|
||||
// 5DMa31Hd5u1dwoRKgC4uvqyrdK45RHv3CpwvpUC1EzuwDit4
|
||||
array_bytes::hex2array_unchecked("3919132b851ef0fd2dae42a7e734fe547af5a6b809006100f48944d7fae8e8ef")
|
||||
.unchecked_into(),
|
||||
// 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x
|
||||
array_bytes::hex2array_unchecked("00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378")
|
||||
.unchecked_into(),
|
||||
// 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x
|
||||
array_bytes::hex2array_unchecked("00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378")
|
||||
.unchecked_into(),
|
||||
// 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x
|
||||
array_bytes::hex2array_unchecked("00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378")
|
||||
.unchecked_into(),
|
||||
// 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x
|
||||
array_bytes::hex2array_unchecked("00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378")
|
||||
.unchecked_into(),
|
||||
// 5FCu2pY928VVHPgnNVJssvxFJZECyNe1CyH3WTG79Wisx58B
|
||||
array_bytes::hex2array_unchecked("020ce02b963548f9f8ade8765f7a4a06638c17819c78422a1cc35b647873583eef")
|
||||
.unchecked_into(),
|
||||
),
|
||||
];
|
||||
|
||||
// generated with secret: subkey inspect "$secret"/fir
|
||||
let root_key: AccountId = array_bytes::hex_n_into_unchecked(
|
||||
// 5Ff3iXP75ruzroPWRP2FYBHWnmGGBSb63857BgnzCoXNxfPo
|
||||
"9ee5e5bdc0ec239eb164f865ecc345ce4c88e76ee002e0f7e318097347471809",
|
||||
);
|
||||
|
||||
let endowed_accounts: Vec<AccountId> = vec![root_key.clone()];
|
||||
(initial_authorities, root_key, endowed_accounts)
|
||||
}
|
||||
|
||||
fn pezstaging_testnet_genesis_patch() -> serde_json::Value {
|
||||
let (initial_authorities, root_key, endowed_accounts) =
|
||||
configure_accounts_for_staging_testnet();
|
||||
testnet_genesis_patch(initial_authorities, vec![], root_key, endowed_accounts)
|
||||
}
|
||||
|
||||
/// Staging testnet config.
|
||||
pub fn pezstaging_testnet_config() -> ChainSpec {
|
||||
ChainSpec::builder(wasm_binary_unwrap(), Default::default())
|
||||
.with_name("Staging Testnet")
|
||||
.with_id("pezstaging_testnet")
|
||||
.with_chain_type(ChainType::Live)
|
||||
.with_genesis_config_preset_name(pezsp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET)
|
||||
.with_genesis_config_patch(pezstaging_testnet_genesis_patch())
|
||||
.with_telemetry_endpoints(
|
||||
TelemetryEndpoints::new(vec![(STAGING_TELEMETRY_URL.to_string(), 0)])
|
||||
.expect("Staging telemetry url is valid; qed"),
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
/// Configure the accounts for the testnet.
|
||||
///
|
||||
/// * Adds `initial_authorities` and `initial_nominators` to endowed accounts if missing.
|
||||
/// * Sets up the stakers consisting of the `initial_authorities` and `initial_nominators`.
|
||||
fn configure_accounts(
|
||||
initial_authorities: Vec<(
|
||||
AccountId,
|
||||
AccountId,
|
||||
GrandpaId,
|
||||
BabeId,
|
||||
ImOnlineId,
|
||||
AuthorityDiscoveryId,
|
||||
MixnetId,
|
||||
BeefyId,
|
||||
)>,
|
||||
initial_nominators: Vec<AccountId>,
|
||||
endowed_accounts: Vec<AccountId>,
|
||||
stash: Balance,
|
||||
) -> (
|
||||
Vec<(
|
||||
AccountId,
|
||||
AccountId,
|
||||
GrandpaId,
|
||||
BabeId,
|
||||
ImOnlineId,
|
||||
AuthorityDiscoveryId,
|
||||
MixnetId,
|
||||
BeefyId,
|
||||
)>,
|
||||
Vec<AccountId>,
|
||||
Vec<Staker>,
|
||||
) {
|
||||
let mut endowed_accounts = endowed_accounts;
|
||||
// endow all authorities and nominators.
|
||||
initial_authorities
|
||||
.iter()
|
||||
.map(|x| &x.0)
|
||||
.chain(initial_nominators.iter())
|
||||
.for_each(|x| {
|
||||
if !endowed_accounts.contains(x) {
|
||||
endowed_accounts.push(x.clone())
|
||||
}
|
||||
});
|
||||
|
||||
// stakers: all validators and nominators.
|
||||
let mut rng = rand::thread_rng();
|
||||
let stakers = initial_authorities
|
||||
.iter()
|
||||
.map(|x| (x.0.clone(), x.0.clone(), stash, StakerStatus::Validator))
|
||||
.chain(initial_nominators.iter().map(|x| {
|
||||
use rand::{seq::SliceRandom, Rng};
|
||||
let limit = (MaxNominations::get() as usize).min(initial_authorities.len());
|
||||
let count = rng.gen::<usize>() % limit;
|
||||
let nominations = initial_authorities
|
||||
.as_slice()
|
||||
.choose_multiple(&mut rng, count)
|
||||
.into_iter()
|
||||
.map(|choice| choice.0.clone())
|
||||
.collect::<Vec<_>>();
|
||||
(x.clone(), x.clone(), stash, StakerStatus::Nominator(nominations))
|
||||
}))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
(initial_authorities, endowed_accounts, stakers)
|
||||
}
|
||||
|
||||
/// Helper function to create RuntimeGenesisConfig json patch for testing.
|
||||
pub fn testnet_genesis_patch(
|
||||
initial_authorities: Vec<(
|
||||
AccountId,
|
||||
AccountId,
|
||||
GrandpaId,
|
||||
BabeId,
|
||||
ImOnlineId,
|
||||
AuthorityDiscoveryId,
|
||||
MixnetId,
|
||||
BeefyId,
|
||||
)>,
|
||||
initial_nominators: Vec<AccountId>,
|
||||
root_key: AccountId,
|
||||
endowed_accounts: Vec<AccountId>,
|
||||
) -> serde_json::Value {
|
||||
let (initial_authorities, endowed_accounts, stakers) =
|
||||
configure_accounts(initial_authorities, initial_nominators, endowed_accounts, STASH);
|
||||
|
||||
let validator_count = initial_authorities.len();
|
||||
let minimum_validator_count = validator_count;
|
||||
|
||||
let collective = collective(&endowed_accounts);
|
||||
|
||||
serde_json::json!({
|
||||
"balances": {
|
||||
"balances": endowed_accounts.iter().cloned().map(|x| (x, ENDOWMENT)).collect::<Vec<_>>()
|
||||
},
|
||||
"session": {
|
||||
"keys": initial_authorities
|
||||
.iter()
|
||||
.map(|x| {
|
||||
(
|
||||
x.0.clone(),
|
||||
// stash account is controller
|
||||
x.0.clone(),
|
||||
session_keys_json(
|
||||
x.2.clone(),
|
||||
x.3.clone(),
|
||||
x.4.clone(),
|
||||
x.5.clone(),
|
||||
x.6.clone(),
|
||||
x.7.clone(),
|
||||
)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
},
|
||||
"elections": {
|
||||
"members": collective.iter().cloned().map(|member| (member, STASH)).collect::<Vec<_>>(),
|
||||
},
|
||||
"technicalCommittee": {
|
||||
"members": collective,
|
||||
},
|
||||
"staking": {
|
||||
"validatorCount": validator_count,
|
||||
"minimumValidatorCount": minimum_validator_count,
|
||||
"invulnerables": initial_authorities
|
||||
.iter()
|
||||
.map(|x| x.0.clone())
|
||||
.collect::<Vec<_>>(),
|
||||
"stakers": stakers,
|
||||
},
|
||||
"sudo": {
|
||||
"key": root_key,
|
||||
},
|
||||
"revive": {
|
||||
"mappedAccounts": endowed_accounts.iter().filter(|x| ! is_eth_derived(x)).cloned().collect::<Vec<_>>()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates the session keys as defined by the runtime.
|
||||
fn session_keys_json(
|
||||
grandpa: GrandpaId,
|
||||
babe: BabeId,
|
||||
im_online: ImOnlineId,
|
||||
authority_discovery: AuthorityDiscoveryId,
|
||||
mixnet: MixnetId,
|
||||
beefy: BeefyId,
|
||||
) -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"authority_discovery": authority_discovery,
|
||||
"babe": babe,
|
||||
"beefy": beefy,
|
||||
"grandpa": grandpa,
|
||||
"im_online": im_online,
|
||||
"mixnet": mixnet
|
||||
})
|
||||
}
|
||||
|
||||
/// Extract some accounts from endowed to be put into the collective.
|
||||
fn collective(endowed: &[AccountId]) -> Vec<AccountId> {
|
||||
const MAX_COLLECTIVE_SIZE: usize = 50;
|
||||
let endowed_accounts_count = endowed.len();
|
||||
endowed
|
||||
.iter()
|
||||
.take((endowed_accounts_count.div_ceil(2)).min(MAX_COLLECTIVE_SIZE))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn props() -> Properties {
|
||||
let mut properties = Properties::new();
|
||||
properties.insert("tokenDecimals".to_string(), 12.into());
|
||||
properties
|
||||
}
|
||||
|
||||
/// Development config (single validator Alice).
|
||||
pub fn development_config() -> ChainSpec {
|
||||
ChainSpec::builder(wasm_binary_unwrap(), Default::default())
|
||||
.with_name("Development")
|
||||
.with_id("dev")
|
||||
.with_chain_type(ChainType::Development)
|
||||
.with_properties(props())
|
||||
.with_genesis_config_preset_name(pezsp_genesis_builder::DEV_RUNTIME_PRESET)
|
||||
.build()
|
||||
}
|
||||
|
||||
/// Local testnet config (multivalidator Alice + Bob).
|
||||
pub fn local_testnet_config() -> ChainSpec {
|
||||
ChainSpec::builder(wasm_binary_unwrap(), Default::default())
|
||||
.with_name("Local Testnet")
|
||||
.with_id("local_testnet")
|
||||
.with_chain_type(ChainType::Local)
|
||||
.with_genesis_config_preset_name(pezsp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET)
|
||||
.build()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::service::{new_full_base, NewFullBase};
|
||||
use kitchensink_runtime::genesis_config_presets::well_known_including_eth_accounts;
|
||||
use pezsc_service_test;
|
||||
use pezsp_runtime::{AccountId32, BuildStorage};
|
||||
|
||||
/// Local testnet config (single validator - Alice).
|
||||
pub fn integration_test_config_with_single_authority() -> ChainSpec {
|
||||
ChainSpec::builder(wasm_binary_unwrap(), Default::default())
|
||||
.with_name("Integration Test")
|
||||
.with_id("test")
|
||||
.with_chain_type(ChainType::Development)
|
||||
.with_genesis_config_preset_name(pezsp_genesis_builder::DEV_RUNTIME_PRESET)
|
||||
.build()
|
||||
}
|
||||
|
||||
/// Local testnet config (multivalidator Alice + Bob).
|
||||
pub fn integration_test_config_with_two_authorities() -> ChainSpec {
|
||||
ChainSpec::builder(wasm_binary_unwrap(), Default::default())
|
||||
.with_name("Integration Test")
|
||||
.with_id("test")
|
||||
.with_chain_type(ChainType::Local)
|
||||
.with_genesis_config_preset_name(pezsp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET)
|
||||
.build()
|
||||
}
|
||||
|
||||
fn eth_account(from: subxt_signer::eth::Keypair) -> AccountId32 {
|
||||
let mut account_id = AccountId32::new([0xEE; 32]);
|
||||
<AccountId32 as AsMut<[u8; 32]>>::as_mut(&mut account_id)[..20]
|
||||
.copy_from_slice(&from.public_key().to_account_id().as_ref());
|
||||
account_id
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_connectivity() {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
pezsc_service_test::connectivity(integration_test_config_with_two_authorities(), |config| {
|
||||
let NewFullBase { task_manager, client, network, sync, transaction_pool, .. } =
|
||||
new_full_base::<pezsc_network::NetworkWorker<_, _>>(config, None, false, |_, _| ())?;
|
||||
Ok(pezsc_service_test::TestNetComponents::new(
|
||||
task_manager,
|
||||
client,
|
||||
network,
|
||||
sync,
|
||||
transaction_pool,
|
||||
))
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_development_chain_spec() {
|
||||
development_config().build_storage().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_local_testnet_chain_spec() {
|
||||
local_testnet_config().build_storage().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_staging_test_net_chain_spec() {
|
||||
pezstaging_testnet_config().build_storage().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ensure_eth_accounts_are_in_endowed() {
|
||||
let alith = eth_account(subxt_signer::eth::dev::alith());
|
||||
let baltathar = eth_account(subxt_signer::eth::dev::baltathar());
|
||||
|
||||
let endowed = well_known_including_eth_accounts();
|
||||
|
||||
assert!(endowed.contains(&alith), "Alith must be in endowed for integration tests");
|
||||
assert!(endowed.contains(&baltathar), "Baltathar must be in endowed for integration tests");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
/// An overarching CLI command definition.
|
||||
#[derive(Debug, clap::Parser)]
|
||||
pub struct Cli {
|
||||
/// Possible subcommand with parameters.
|
||||
#[command(subcommand)]
|
||||
pub subcommand: Option<Subcommand>,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[clap(flatten)]
|
||||
pub run: pezsc_cli::RunCmd,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[clap(flatten)]
|
||||
pub mixnet_params: pezsc_cli::MixnetParams,
|
||||
|
||||
/// Disable automatic hardware benchmarks.
|
||||
///
|
||||
/// By default these benchmarks are automatically ran at startup and measure
|
||||
/// the CPU speed, the memory bandwidth and the disk speed.
|
||||
///
|
||||
/// The results are then printed out in the logs, and also sent as part of
|
||||
/// telemetry, if telemetry is enabled.
|
||||
#[arg(long)]
|
||||
pub no_hardware_benchmarks: bool,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[clap(flatten)]
|
||||
pub storage_monitor: pezsc_storage_monitor::StorageMonitorParams,
|
||||
}
|
||||
|
||||
/// Possible subcommands of the main binary.
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
pub enum Subcommand {
|
||||
/// The custom inspect subcommand for decoding blocks and extrinsics.
|
||||
#[command(
|
||||
name = "inspect",
|
||||
about = "Decode given block or extrinsic using current native runtime."
|
||||
)]
|
||||
Inspect(node_inspect::cli::InspectCmd),
|
||||
|
||||
/// Sub-commands concerned with benchmarking.
|
||||
///
|
||||
/// The pallet benchmarking moved to the `pallet` sub-command.
|
||||
#[command(subcommand)]
|
||||
Benchmark(pezframe_benchmarking_cli::BenchmarkCmd),
|
||||
|
||||
/// Key management cli utilities
|
||||
#[command(subcommand)]
|
||||
Key(pezsc_cli::KeySubcommand),
|
||||
|
||||
/// Verify a signature for a message, provided on STDIN, with a given (public or secret) key.
|
||||
Verify(pezsc_cli::VerifyCmd),
|
||||
|
||||
/// Generate a seed that provides a vanity address.
|
||||
Vanity(pezsc_cli::VanityCmd),
|
||||
|
||||
/// Sign a message, with a given (secret) key.
|
||||
Sign(pezsc_cli::SignCmd),
|
||||
|
||||
/// Build a chain specification.
|
||||
/// DEPRECATED: `build-spec` command will be removed after 1/04/2026. Use `export-chain-spec`
|
||||
/// command instead.
|
||||
#[deprecated(
|
||||
note = "build-spec command will be removed after 1/04/2026. Use export-chain-spec command instead"
|
||||
)]
|
||||
BuildSpec(pezsc_cli::BuildSpecCmd),
|
||||
|
||||
/// Export the chain specification.
|
||||
ExportChainSpec(pezsc_cli::ExportChainSpecCmd),
|
||||
|
||||
/// Validate blocks.
|
||||
CheckBlock(pezsc_cli::CheckBlockCmd),
|
||||
|
||||
/// Export blocks.
|
||||
ExportBlocks(pezsc_cli::ExportBlocksCmd),
|
||||
|
||||
/// Export the state of a given block into a chain spec.
|
||||
ExportState(pezsc_cli::ExportStateCmd),
|
||||
|
||||
/// Import blocks.
|
||||
ImportBlocks(pezsc_cli::ImportBlocksCmd),
|
||||
|
||||
/// Remove the whole chain.
|
||||
PurgeChain(pezsc_cli::PurgeChainCmd),
|
||||
|
||||
/// Revert the chain to a previous state.
|
||||
Revert(pezsc_cli::RevertCmd),
|
||||
|
||||
/// Db meta columns information.
|
||||
ChainInfo(pezsc_cli::ChainInfoCmd),
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use super::benchmarking::{inherent_benchmark_data, RemarkBuilder, TransferKeepAliveBuilder};
|
||||
use crate::{
|
||||
chain_spec, service,
|
||||
service::{new_partial, FullClient},
|
||||
Cli, Subcommand,
|
||||
};
|
||||
use pezframe_benchmarking_cli::*;
|
||||
use kitchensink_runtime::{ExistentialDeposit, RuntimeApi};
|
||||
use node_primitives::Block;
|
||||
use pezsc_cli::{Result, BizinikiwiCli};
|
||||
use pezsc_service::PartialComponents;
|
||||
use pezsp_keyring::Sr25519Keyring;
|
||||
use pezsp_runtime::traits::HashingFor;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
impl BizinikiwiCli for Cli {
|
||||
fn impl_name() -> String {
|
||||
"Bizinikiwi Node".into()
|
||||
}
|
||||
|
||||
fn impl_version() -> String {
|
||||
env!("BIZINIKIWI_CLI_IMPL_VERSION").into()
|
||||
}
|
||||
|
||||
fn description() -> String {
|
||||
env!("CARGO_PKG_DESCRIPTION").into()
|
||||
}
|
||||
|
||||
fn author() -> String {
|
||||
env!("CARGO_PKG_AUTHORS").into()
|
||||
}
|
||||
|
||||
fn support_url() -> String {
|
||||
"https://github.com/pezkuwichain/pezkuwi-sdk/issues/new".into()
|
||||
}
|
||||
|
||||
fn copyright_start_year() -> i32 {
|
||||
2017
|
||||
}
|
||||
|
||||
fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn pezsc_service::ChainSpec>, String> {
|
||||
let spec = match id {
|
||||
"" =>
|
||||
return Err(
|
||||
"Please specify which chain you want to run, e.g. --dev or --chain=local"
|
||||
.into(),
|
||||
),
|
||||
"dev" => Box::new(chain_spec::development_config()),
|
||||
"local" => Box::new(chain_spec::local_testnet_config()),
|
||||
"fir" | "flaming-fir" => Box::new(chain_spec::flaming_fir_config()?),
|
||||
"staging" => Box::new(chain_spec::pezstaging_testnet_config()),
|
||||
path =>
|
||||
Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?),
|
||||
};
|
||||
Ok(spec)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse command line arguments into service configuration.
|
||||
pub fn run() -> Result<()> {
|
||||
let cli = Cli::from_args();
|
||||
|
||||
match &cli.subcommand {
|
||||
None => {
|
||||
let runner = cli.create_runner(&cli.run)?;
|
||||
runner.run_node_until_exit(|config| async move {
|
||||
service::new_full(config, cli).map_err(pezsc_cli::Error::Service)
|
||||
})
|
||||
},
|
||||
Some(Subcommand::Inspect(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
|
||||
runner.sync_run(|config| cmd.run::<Block, RuntimeApi>(config))
|
||||
},
|
||||
Some(Subcommand::ExportChainSpec(cmd)) => {
|
||||
let chain_spec = cli.load_spec(&cmd.chain)?;
|
||||
cmd.run(chain_spec)
|
||||
},
|
||||
Some(Subcommand::Benchmark(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
|
||||
runner.sync_run(|config| {
|
||||
// This switch needs to be in the client, since the client decides
|
||||
// which sub-commands it wants to support.
|
||||
match cmd {
|
||||
BenchmarkCmd::Pallet(cmd) => {
|
||||
if !cfg!(feature = "runtime-benchmarks") {
|
||||
return Err(
|
||||
"Runtime benchmarking wasn't enabled when building the node. \
|
||||
You can enable it with `--features runtime-benchmarks`."
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
cmd.run_with_spec::<HashingFor<Block>, pezsp_statement_store::runtime_api::HostFunctions>(Some(config.chain_spec))
|
||||
},
|
||||
BenchmarkCmd::Block(cmd) => {
|
||||
// ensure that we keep the task manager alive
|
||||
let partial = new_partial(&config, None)?;
|
||||
cmd.run(partial.client)
|
||||
},
|
||||
#[cfg(not(feature = "runtime-benchmarks"))]
|
||||
BenchmarkCmd::Storage(_) => Err(
|
||||
"Storage benchmarking can be enabled with `--features runtime-benchmarks`."
|
||||
.into(),
|
||||
),
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
BenchmarkCmd::Storage(cmd) => {
|
||||
// ensure that we keep the task manager alive
|
||||
let partial = new_partial(&config, None)?;
|
||||
let db = partial.backend.expose_db();
|
||||
let storage = partial.backend.expose_storage();
|
||||
let shared_trie_cache = partial.backend.expose_shared_trie_cache();
|
||||
|
||||
cmd.run(config, partial.client, db, storage, shared_trie_cache)
|
||||
},
|
||||
BenchmarkCmd::Overhead(cmd) => {
|
||||
// ensure that we keep the task manager alive
|
||||
let partial = new_partial(&config, None)?;
|
||||
let ext_builder = RemarkBuilder::new(partial.client.clone());
|
||||
|
||||
cmd.run(
|
||||
config.chain_spec.name().into(),
|
||||
partial.client,
|
||||
inherent_benchmark_data()?,
|
||||
Vec::new(),
|
||||
&ext_builder,
|
||||
false,
|
||||
)
|
||||
},
|
||||
BenchmarkCmd::Extrinsic(cmd) => {
|
||||
// ensure that we keep the task manager alive
|
||||
let partial = service::new_partial(&config, None)?;
|
||||
// Register the *Remark* and *TKA* builders.
|
||||
let ext_factory = ExtrinsicFactory(vec![
|
||||
Box::new(RemarkBuilder::new(partial.client.clone())),
|
||||
Box::new(TransferKeepAliveBuilder::new(
|
||||
partial.client.clone(),
|
||||
Sr25519Keyring::Alice.to_account_id(),
|
||||
ExistentialDeposit::get(),
|
||||
)),
|
||||
]);
|
||||
|
||||
cmd.run(
|
||||
partial.client,
|
||||
inherent_benchmark_data()?,
|
||||
Vec::new(),
|
||||
&ext_factory,
|
||||
)
|
||||
},
|
||||
BenchmarkCmd::Machine(cmd) =>
|
||||
cmd.run(&config, BIZINIKIWI_REFERENCE_HARDWARE.clone()),
|
||||
}
|
||||
})
|
||||
},
|
||||
Some(Subcommand::Key(cmd)) => cmd.run(&cli),
|
||||
Some(Subcommand::Sign(cmd)) => cmd.run(),
|
||||
Some(Subcommand::Verify(cmd)) => cmd.run(),
|
||||
Some(Subcommand::Vanity(cmd)) => cmd.run(),
|
||||
#[allow(deprecated)]
|
||||
Some(Subcommand::BuildSpec(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.sync_run(|config| cmd.run(config.chain_spec, config.network))
|
||||
},
|
||||
Some(Subcommand::CheckBlock(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.async_run(|config| {
|
||||
let PartialComponents { client, task_manager, import_queue, .. } =
|
||||
new_partial(&config, None)?;
|
||||
Ok((cmd.run(client, import_queue), task_manager))
|
||||
})
|
||||
},
|
||||
Some(Subcommand::ExportBlocks(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.async_run(|config| {
|
||||
let PartialComponents { client, task_manager, .. } = new_partial(&config, None)?;
|
||||
Ok((cmd.run(client, config.database), task_manager))
|
||||
})
|
||||
},
|
||||
Some(Subcommand::ExportState(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.async_run(|config| {
|
||||
let PartialComponents { client, task_manager, .. } = new_partial(&config, None)?;
|
||||
Ok((cmd.run(client, config.chain_spec), task_manager))
|
||||
})
|
||||
},
|
||||
Some(Subcommand::ImportBlocks(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.async_run(|config| {
|
||||
let PartialComponents { client, task_manager, import_queue, .. } =
|
||||
new_partial(&config, None)?;
|
||||
Ok((cmd.run(client, import_queue), task_manager))
|
||||
})
|
||||
},
|
||||
Some(Subcommand::PurgeChain(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.sync_run(|config| cmd.run(config.database))
|
||||
},
|
||||
Some(Subcommand::Revert(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.async_run(|config| {
|
||||
let PartialComponents { client, task_manager, backend, .. } =
|
||||
new_partial(&config, None)?;
|
||||
let aux_revert = Box::new(|client: Arc<FullClient>, backend, blocks| {
|
||||
pezsc_consensus_babe::revert(client.clone(), backend, blocks)?;
|
||||
pezsc_consensus_grandpa::revert(client, blocks)?;
|
||||
Ok(())
|
||||
});
|
||||
Ok((cmd.run(client, backend, Some(aux_revert)), task_manager))
|
||||
})
|
||||
},
|
||||
Some(Subcommand::ChainInfo(cmd)) => {
|
||||
let runner = cli.create_runner(cmd)?;
|
||||
runner.sync_run(|config| cmd.run::<Block>(&config))
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Bizinikiwi CLI library.
|
||||
//!
|
||||
//! This package has two Cargo features:
|
||||
//!
|
||||
//! - `cli` (default): exposes functions that parse command-line options, then start and run the
|
||||
//! node as a CLI application.
|
||||
//!
|
||||
//! - `browser`: exposes the content of the `browser` module, which consists of exported symbols
|
||||
//! that are meant to be passed through the `wasm-bindgen` utility and called from JavaScript.
|
||||
//! Despite its name the produced WASM can theoretically also be used from NodeJS, although this
|
||||
//! hasn't been tested.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
mod benchmarking;
|
||||
pub mod chain_spec;
|
||||
#[cfg(feature = "cli")]
|
||||
mod cli;
|
||||
#[cfg(feature = "cli")]
|
||||
mod command;
|
||||
pub mod service;
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
pub use cli::*;
|
||||
#[cfg(feature = "cli")]
|
||||
pub use command::*;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,892 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use codec::{Decode, Encode, Joiner};
|
||||
use pezframe_support::{
|
||||
dispatch::{DispatchClass, GetDispatchInfo},
|
||||
traits::Currency,
|
||||
weights::Weight,
|
||||
};
|
||||
use pezframe_system::{self, AccountInfo, DispatchEventInfo, EventRecord, Phase};
|
||||
use pezkuwi_sdk::*;
|
||||
use pezsp_core::{storage::well_known_keys, traits::Externalities};
|
||||
use pezsp_runtime::{
|
||||
traits::Hash as HashT, transaction_validity::InvalidTransaction, ApplyExtrinsicResult,
|
||||
};
|
||||
|
||||
use kitchensink_runtime::{
|
||||
constants::{currency::*, time::SLOT_DURATION},
|
||||
Balances, CheckedExtrinsic, Header, Runtime, RuntimeCall, RuntimeEvent, System,
|
||||
TransactionPayment, Treasury, UncheckedExtrinsic,
|
||||
};
|
||||
use node_primitives::{Balance, Hash};
|
||||
use node_testing::keyring::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
use wat;
|
||||
|
||||
pub mod common;
|
||||
use self::common::{sign, *};
|
||||
|
||||
/// The wasm runtime binary which hasn't undergone the compacting process.
|
||||
///
|
||||
/// The idea here is to pass it as the current runtime code to the executor so the executor will
|
||||
/// have to execute provided wasm code instead of the native equivalent. This trick is used to
|
||||
/// test code paths that differ between native and wasm versions.
|
||||
pub fn bloaty_code_unwrap() -> &'static [u8] {
|
||||
kitchensink_runtime::WASM_BINARY_BLOATY.expect(
|
||||
"Development wasm binary is not available. \
|
||||
Testing is only supported with the flag disabled.",
|
||||
)
|
||||
}
|
||||
|
||||
/// Default transfer fee. This will use the same logic that is implemented in transaction-payment
|
||||
/// module.
|
||||
///
|
||||
/// Note that reads the multiplier from storage directly, hence to get the fee of `extrinsic`
|
||||
/// at block `n`, it must be called prior to executing block `n` to do the calculation with the
|
||||
/// correct multiplier.
|
||||
fn transfer_fee(extrinsic: &UncheckedExtrinsic) -> Balance {
|
||||
let mut info = default_transfer_call().get_dispatch_info();
|
||||
info.extension_weight = extrinsic.0.extension_weight();
|
||||
TransactionPayment::compute_fee(extrinsic.encode().len() as u32, &info, 0)
|
||||
}
|
||||
|
||||
/// Default transfer fee, same as `transfer_fee`, but with a weight refund factored in.
|
||||
fn transfer_fee_with_refund(extrinsic: &UncheckedExtrinsic, weight_refund: Weight) -> Balance {
|
||||
let mut info = default_transfer_call().get_dispatch_info();
|
||||
info.extension_weight = extrinsic.0.extension_weight();
|
||||
let post_info = (Some(info.total_weight().saturating_sub(weight_refund)), info.pays_fee).into();
|
||||
TransactionPayment::compute_actual_fee(extrinsic.encode().len() as u32, &info, &post_info, 0)
|
||||
}
|
||||
|
||||
fn xt() -> UncheckedExtrinsic {
|
||||
sign(CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(0, 0)),
|
||||
function: RuntimeCall::Balances(default_transfer_call()),
|
||||
})
|
||||
}
|
||||
|
||||
fn set_heap_pages<E: Externalities>(ext: &mut E, heap_pages: u64) {
|
||||
ext.place_storage(well_known_keys::HEAP_PAGES.to_vec(), Some(heap_pages.encode()));
|
||||
}
|
||||
|
||||
fn changes_trie_block() -> (Vec<u8>, Hash) {
|
||||
let time = 42 * 1000;
|
||||
construct_block(
|
||||
&mut new_test_ext(compact_code_unwrap()),
|
||||
1,
|
||||
GENESIS_HASH.into(),
|
||||
vec![
|
||||
CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Bare,
|
||||
function: RuntimeCall::Timestamp(pezpallet_timestamp::Call::set { now: time }),
|
||||
},
|
||||
CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(0, 0)),
|
||||
function: RuntimeCall::Balances(pezpallet_balances::Call::transfer_allow_death {
|
||||
dest: bob().into(),
|
||||
value: 69 * DOLLARS,
|
||||
}),
|
||||
},
|
||||
],
|
||||
(time / SLOT_DURATION).into(),
|
||||
)
|
||||
}
|
||||
|
||||
/// block 1 and 2 must be created together to ensure transactions are only signed once (since they
|
||||
/// are not guaranteed to be deterministic) and to ensure that the correct state is propagated
|
||||
/// from block1's execution to block2 to derive the correct storage_root.
|
||||
fn blocks() -> ((Vec<u8>, Hash), (Vec<u8>, Hash)) {
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
let time1 = 42 * 1000;
|
||||
let block1 = construct_block(
|
||||
&mut t,
|
||||
1,
|
||||
GENESIS_HASH.into(),
|
||||
vec![
|
||||
CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Bare,
|
||||
function: RuntimeCall::Timestamp(pezpallet_timestamp::Call::set { now: time1 }),
|
||||
},
|
||||
CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(0, 0)),
|
||||
function: RuntimeCall::Balances(pezpallet_balances::Call::transfer_allow_death {
|
||||
dest: bob().into(),
|
||||
value: 69 * DOLLARS,
|
||||
}),
|
||||
},
|
||||
],
|
||||
(time1 / SLOT_DURATION).into(),
|
||||
);
|
||||
let time2 = 52 * 1000;
|
||||
let block2 = construct_block(
|
||||
&mut t,
|
||||
2,
|
||||
block1.1,
|
||||
vec![
|
||||
CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Bare,
|
||||
function: RuntimeCall::Timestamp(pezpallet_timestamp::Call::set { now: time2 }),
|
||||
},
|
||||
CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Signed(bob(), tx_ext(0, 0)),
|
||||
function: RuntimeCall::Balances(pezpallet_balances::Call::transfer_allow_death {
|
||||
dest: alice().into(),
|
||||
value: 5 * DOLLARS,
|
||||
}),
|
||||
},
|
||||
CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(1, 0)),
|
||||
function: RuntimeCall::Balances(pezpallet_balances::Call::transfer_allow_death {
|
||||
dest: bob().into(),
|
||||
value: 15 * DOLLARS,
|
||||
}),
|
||||
},
|
||||
],
|
||||
(time2 / SLOT_DURATION).into(),
|
||||
);
|
||||
|
||||
// session change => consensus authorities change => authorities change digest item appears
|
||||
let digest = Header::decode(&mut &block2.0[..]).unwrap().digest;
|
||||
assert_eq!(digest.logs().len(), 2 /* Just babe and BEEFY slots */);
|
||||
|
||||
(block1, block2)
|
||||
}
|
||||
|
||||
fn block_with_size(time: u64, nonce: u32, size: usize) -> (Vec<u8>, Hash) {
|
||||
construct_block(
|
||||
&mut new_test_ext(compact_code_unwrap()),
|
||||
1,
|
||||
GENESIS_HASH.into(),
|
||||
vec![
|
||||
CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Bare,
|
||||
function: RuntimeCall::Timestamp(pezpallet_timestamp::Call::set { now: time * 1000 }),
|
||||
},
|
||||
CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(nonce, 0)),
|
||||
function: RuntimeCall::System(pezframe_system::Call::remark { remark: vec![0; size] }),
|
||||
},
|
||||
],
|
||||
(time * 1000 / SLOT_DURATION).into(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panic_execution_with_foreign_code_gives_error() {
|
||||
let mut t = new_test_ext(bloaty_code_unwrap());
|
||||
t.insert(
|
||||
<pezframe_system::Account<Runtime>>::hashed_key_for(alice()),
|
||||
AccountInfo::<<Runtime as pezframe_system::Config>::Nonce, _> {
|
||||
providers: 1,
|
||||
data: (69u128, 0u128, 0u128, 1u128 << 127),
|
||||
..Default::default()
|
||||
}
|
||||
.encode(),
|
||||
);
|
||||
t.insert(<pezpallet_balances::TotalIssuance<Runtime>>::hashed_key().to_vec(), 69_u128.encode());
|
||||
t.insert(<pezframe_system::BlockHash<Runtime>>::hashed_key_for(0), vec![0u8; 32]);
|
||||
|
||||
let r = executor_call(&mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u32))).0;
|
||||
assert!(r.is_ok());
|
||||
let v = executor_call(&mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt()))
|
||||
.0
|
||||
.unwrap();
|
||||
let r = ApplyExtrinsicResult::decode(&mut &v[..]).unwrap();
|
||||
assert_eq!(r, Err(InvalidTransaction::Payment.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_extrinsic_with_native_equivalent_code_gives_error() {
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
t.insert(
|
||||
<pezframe_system::Account<Runtime>>::hashed_key_for(alice()),
|
||||
AccountInfo::<<Runtime as pezframe_system::Config>::Nonce, _> {
|
||||
providers: 1,
|
||||
data: (69u128, 0u128, 0u128, 1u128 << 127),
|
||||
..Default::default()
|
||||
}
|
||||
.encode(),
|
||||
);
|
||||
t.insert(<pezpallet_balances::TotalIssuance<Runtime>>::hashed_key().to_vec(), 69u128.encode());
|
||||
t.insert(<pezframe_system::BlockHash<Runtime>>::hashed_key_for(0), vec![0u8; 32]);
|
||||
|
||||
let r = executor_call(&mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u32))).0;
|
||||
assert!(r.is_ok());
|
||||
let v = executor_call(&mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt()))
|
||||
.0
|
||||
.unwrap();
|
||||
let r = ApplyExtrinsicResult::decode(&mut &v[..]).unwrap();
|
||||
assert_eq!(r, Err(InvalidTransaction::Payment.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn successful_execution_with_native_equivalent_code_gives_ok() {
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
t.insert(
|
||||
<pezframe_system::Account<Runtime>>::hashed_key_for(alice()),
|
||||
AccountInfo::<<Runtime as pezframe_system::Config>::Nonce, _> {
|
||||
providers: 1,
|
||||
data: (111 * DOLLARS, 0u128, 0u128, 1u128 << 127),
|
||||
..Default::default()
|
||||
}
|
||||
.encode(),
|
||||
);
|
||||
t.insert(
|
||||
<pezframe_system::Account<Runtime>>::hashed_key_for(bob()),
|
||||
AccountInfo::<
|
||||
<Runtime as pezframe_system::Config>::Nonce,
|
||||
<Runtime as pezframe_system::Config>::AccountData,
|
||||
>::default()
|
||||
.encode(),
|
||||
);
|
||||
t.insert(
|
||||
<pezpallet_balances::TotalIssuance<Runtime>>::hashed_key().to_vec(),
|
||||
(111 * DOLLARS).encode(),
|
||||
);
|
||||
t.insert(<pezframe_system::BlockHash<Runtime>>::hashed_key_for(0), vec![0u8; 32]);
|
||||
|
||||
let r = executor_call(&mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u32))).0;
|
||||
assert!(r.is_ok());
|
||||
|
||||
let weight_refund = Weight::zero();
|
||||
let fees_after_refund = t.execute_with(|| transfer_fee_with_refund(&xt(), weight_refund));
|
||||
|
||||
let r = executor_call(&mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt())).0;
|
||||
assert!(r.is_ok());
|
||||
|
||||
t.execute_with(|| {
|
||||
assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees_after_refund);
|
||||
assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn successful_execution_with_foreign_code_gives_ok() {
|
||||
let mut t = new_test_ext(bloaty_code_unwrap());
|
||||
t.insert(
|
||||
<pezframe_system::Account<Runtime>>::hashed_key_for(alice()),
|
||||
AccountInfo::<<Runtime as pezframe_system::Config>::Nonce, _> {
|
||||
providers: 1,
|
||||
data: (111 * DOLLARS, 0u128, 0u128, 1u128 << 127),
|
||||
..Default::default()
|
||||
}
|
||||
.encode(),
|
||||
);
|
||||
t.insert(
|
||||
<pezframe_system::Account<Runtime>>::hashed_key_for(bob()),
|
||||
AccountInfo::<
|
||||
<Runtime as pezframe_system::Config>::Nonce,
|
||||
<Runtime as pezframe_system::Config>::AccountData,
|
||||
>::default()
|
||||
.encode(),
|
||||
);
|
||||
t.insert(
|
||||
<pezpallet_balances::TotalIssuance<Runtime>>::hashed_key().to_vec(),
|
||||
(111 * DOLLARS).encode(),
|
||||
);
|
||||
t.insert(<pezframe_system::BlockHash<Runtime>>::hashed_key_for(0), vec![0u8; 32]);
|
||||
|
||||
let r = executor_call(&mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u32))).0;
|
||||
assert!(r.is_ok());
|
||||
|
||||
let weight_refund = Weight::zero();
|
||||
let fees_after_refund = t.execute_with(|| transfer_fee_with_refund(&xt(), weight_refund));
|
||||
|
||||
let r = executor_call(&mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt())).0;
|
||||
assert!(r.is_ok());
|
||||
|
||||
t.execute_with(|| {
|
||||
assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees_after_refund);
|
||||
assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_native_block_import_works() {
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
|
||||
let (block1, block2) = blocks();
|
||||
|
||||
let mut alice_last_known_balance: Balance = Default::default();
|
||||
let mut fees = t.execute_with(|| transfer_fee(&xt()));
|
||||
let extension_weight = xt().0.extension_weight();
|
||||
let weight_refund = Weight::zero();
|
||||
let fees_after_refund = t.execute_with(|| transfer_fee_with_refund(&xt(), weight_refund));
|
||||
|
||||
let transfer_weight = default_transfer_call().get_dispatch_info().call_weight.saturating_add(
|
||||
<Runtime as pezframe_system::Config>::BlockWeights::get()
|
||||
.get(DispatchClass::Normal)
|
||||
.base_extrinsic,
|
||||
);
|
||||
let timestamp_weight = pezpallet_timestamp::Call::set::<Runtime> { now: Default::default() }
|
||||
.get_dispatch_info()
|
||||
.call_weight
|
||||
.saturating_add(
|
||||
<Runtime as pezframe_system::Config>::BlockWeights::get()
|
||||
.get(DispatchClass::Mandatory)
|
||||
.base_extrinsic,
|
||||
);
|
||||
|
||||
executor_call(&mut t, "Core_execute_block", &block1.0).0.unwrap();
|
||||
|
||||
t.execute_with(|| {
|
||||
assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees_after_refund);
|
||||
assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS);
|
||||
alice_last_known_balance = Balances::total_balance(&alice());
|
||||
let events = vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: RuntimeEvent::System(pezframe_system::Event::ExtrinsicSuccess {
|
||||
dispatch_info: DispatchEventInfo {
|
||||
weight: timestamp_weight,
|
||||
class: DispatchClass::Mandatory,
|
||||
pays_fee: Default::default(),
|
||||
},
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(1),
|
||||
event: RuntimeEvent::Balances(pezpallet_balances::Event::Withdraw {
|
||||
who: alice().into(),
|
||||
amount: fees,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(1),
|
||||
event: RuntimeEvent::Balances(pezpallet_balances::Event::Transfer {
|
||||
from: alice().into(),
|
||||
to: bob().into(),
|
||||
amount: 69 * DOLLARS,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(1),
|
||||
event: RuntimeEvent::Balances(pezpallet_balances::Event::Deposit {
|
||||
who: pezpallet_treasury::Pallet::<Runtime>::account_id(),
|
||||
amount: fees_after_refund,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(1),
|
||||
event: RuntimeEvent::TransactionPayment(
|
||||
pezpallet_transaction_payment::Event::TransactionFeePaid {
|
||||
who: alice().into(),
|
||||
actual_fee: fees_after_refund,
|
||||
tip: 0,
|
||||
},
|
||||
),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(1),
|
||||
event: RuntimeEvent::System(pezframe_system::Event::ExtrinsicSuccess {
|
||||
dispatch_info: DispatchEventInfo {
|
||||
weight: transfer_weight
|
||||
.saturating_add(extension_weight.saturating_sub(weight_refund)),
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
];
|
||||
let filtered_events: Vec<_> = System::events()
|
||||
.into_iter()
|
||||
.filter(|ev| {
|
||||
!matches!(
|
||||
ev.event,
|
||||
RuntimeEvent::VoterList(
|
||||
pezpallet_bags_list::Event::<Runtime, _>::ScoreUpdated { .. }
|
||||
)
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
assert_eq!(filtered_events, events);
|
||||
});
|
||||
|
||||
fees = t.execute_with(|| transfer_fee(&xt()));
|
||||
let pot = t.execute_with(|| Treasury::pot());
|
||||
let extension_weight = xt().0.extension_weight();
|
||||
let weight_refund = Weight::zero();
|
||||
let fees_after_refund = t.execute_with(|| transfer_fee_with_refund(&xt(), weight_refund));
|
||||
|
||||
executor_call(&mut t, "Core_execute_block", &block2.0).0.unwrap();
|
||||
|
||||
t.execute_with(|| {
|
||||
assert_eq!(
|
||||
Balances::total_balance(&alice()),
|
||||
alice_last_known_balance - 10 * DOLLARS - fees_after_refund,
|
||||
);
|
||||
assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - fees_after_refund);
|
||||
let events = vec![
|
||||
EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: RuntimeEvent::Treasury(pezpallet_treasury::Event::UpdatedInactive {
|
||||
reactivated: 0,
|
||||
deactivated: pot,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: RuntimeEvent::System(pezframe_system::Event::ExtrinsicSuccess {
|
||||
dispatch_info: DispatchEventInfo {
|
||||
weight: timestamp_weight,
|
||||
class: DispatchClass::Mandatory,
|
||||
pays_fee: Default::default(),
|
||||
},
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(1),
|
||||
event: RuntimeEvent::Balances(pezpallet_balances::Event::Withdraw {
|
||||
who: bob().into(),
|
||||
amount: fees,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(1),
|
||||
event: RuntimeEvent::Balances(pezpallet_balances::Event::Transfer {
|
||||
from: bob().into(),
|
||||
to: alice().into(),
|
||||
amount: 5 * DOLLARS,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(1),
|
||||
event: RuntimeEvent::Balances(pezpallet_balances::Event::Deposit {
|
||||
who: pezpallet_treasury::Pallet::<Runtime>::account_id(),
|
||||
amount: fees_after_refund,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(1),
|
||||
event: RuntimeEvent::TransactionPayment(
|
||||
pezpallet_transaction_payment::Event::TransactionFeePaid {
|
||||
who: bob().into(),
|
||||
actual_fee: fees_after_refund,
|
||||
tip: 0,
|
||||
},
|
||||
),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(1),
|
||||
event: RuntimeEvent::System(pezframe_system::Event::ExtrinsicSuccess {
|
||||
dispatch_info: DispatchEventInfo {
|
||||
weight: transfer_weight
|
||||
.saturating_add(extension_weight.saturating_sub(weight_refund)),
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(2),
|
||||
event: RuntimeEvent::Balances(pezpallet_balances::Event::Withdraw {
|
||||
who: alice().into(),
|
||||
amount: fees,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(2),
|
||||
event: RuntimeEvent::Balances(pezpallet_balances::Event::Transfer {
|
||||
from: alice().into(),
|
||||
to: bob().into(),
|
||||
amount: 15 * DOLLARS,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(2),
|
||||
event: RuntimeEvent::Balances(pezpallet_balances::Event::Deposit {
|
||||
who: pezpallet_treasury::Pallet::<Runtime>::account_id(),
|
||||
amount: fees_after_refund,
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(2),
|
||||
event: RuntimeEvent::TransactionPayment(
|
||||
pezpallet_transaction_payment::Event::TransactionFeePaid {
|
||||
who: alice().into(),
|
||||
actual_fee: fees_after_refund,
|
||||
tip: 0,
|
||||
},
|
||||
),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(2),
|
||||
event: RuntimeEvent::System(pezframe_system::Event::ExtrinsicSuccess {
|
||||
dispatch_info: DispatchEventInfo {
|
||||
weight: transfer_weight
|
||||
.saturating_add(extension_weight.saturating_sub(weight_refund)),
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
topics: vec![],
|
||||
},
|
||||
];
|
||||
let all_events = System::events();
|
||||
// Ensure that all expected events (`events`) are present in the full event log
|
||||
// (`all_events`). We use this instead of strict equality since some events (like
|
||||
// VoterList::ScoreUpdated) may be emitted non-deterministically depending on runtime
|
||||
// internals or auto-rebagging logic.
|
||||
for expected_event in &events {
|
||||
assert!(
|
||||
all_events.contains(expected_event),
|
||||
"Expected event {:?} not found in actual events",
|
||||
expected_event
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_wasm_block_import_works() {
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
|
||||
let (block1, block2) = blocks();
|
||||
|
||||
let mut alice_last_known_balance: Balance = Default::default();
|
||||
let weight_refund = Weight::zero();
|
||||
let fees_after_refund = t.execute_with(|| transfer_fee_with_refund(&xt(), weight_refund));
|
||||
|
||||
executor_call(&mut t, "Core_execute_block", &block1.0).0.unwrap();
|
||||
|
||||
t.execute_with(|| {
|
||||
assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees_after_refund);
|
||||
assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS);
|
||||
alice_last_known_balance = Balances::total_balance(&alice());
|
||||
});
|
||||
|
||||
let weight_refund = Weight::zero();
|
||||
let fees_after_refund = t.execute_with(|| transfer_fee_with_refund(&xt(), weight_refund));
|
||||
|
||||
executor_call(&mut t, "Core_execute_block", &block2.0).0.unwrap();
|
||||
|
||||
t.execute_with(|| {
|
||||
assert_eq!(
|
||||
Balances::total_balance(&alice()),
|
||||
alice_last_known_balance - 10 * DOLLARS - fees_after_refund,
|
||||
);
|
||||
assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - 1 * fees_after_refund);
|
||||
});
|
||||
}
|
||||
|
||||
const CODE_TRANSFER: &str = r#"
|
||||
(module
|
||||
;; seal_call(
|
||||
;; callee_ptr: u32,
|
||||
;; callee_len: u32,
|
||||
;; gas: u64,
|
||||
;; value_ptr: u32,
|
||||
;; value_len: u32,
|
||||
;; input_data_ptr: u32,
|
||||
;; input_data_len: u32,
|
||||
;; output_ptr: u32,
|
||||
;; output_len_ptr: u32
|
||||
;; ) -> u32
|
||||
(import "seal0" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32 i32) (result i32)))
|
||||
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
(func (export "deploy")
|
||||
)
|
||||
(func (export "call")
|
||||
(block $fail
|
||||
;; Load input data to contract memory
|
||||
(call $seal_input
|
||||
(i32.const 0)
|
||||
(i32.const 52)
|
||||
)
|
||||
|
||||
;; fail if the input size is not != 4
|
||||
(br_if $fail
|
||||
(i32.ne
|
||||
(i32.const 4)
|
||||
(i32.load (i32.const 52))
|
||||
)
|
||||
)
|
||||
|
||||
(br_if $fail
|
||||
(i32.ne
|
||||
(i32.load8_u (i32.const 0))
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
(br_if $fail
|
||||
(i32.ne
|
||||
(i32.load8_u (i32.const 1))
|
||||
(i32.const 1)
|
||||
)
|
||||
)
|
||||
(br_if $fail
|
||||
(i32.ne
|
||||
(i32.load8_u (i32.const 2))
|
||||
(i32.const 2)
|
||||
)
|
||||
)
|
||||
(br_if $fail
|
||||
(i32.ne
|
||||
(i32.load8_u (i32.const 3))
|
||||
(i32.const 3)
|
||||
)
|
||||
)
|
||||
|
||||
(drop
|
||||
(call $seal_call
|
||||
(i32.const 4) ;; Pointer to "callee" address.
|
||||
(i32.const 32) ;; Length of "callee" address.
|
||||
(i64.const 0) ;; How much gas to devote for the execution. 0 = all.
|
||||
(i32.const 36) ;; Pointer to the buffer with value to transfer
|
||||
(i32.const 16) ;; Length of the buffer with value to transfer.
|
||||
(i32.const 0) ;; Pointer to input data buffer address
|
||||
(i32.const 0) ;; Length of input data buffer
|
||||
(i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output
|
||||
(i32.const 0) ;; Length is ignored in this case
|
||||
)
|
||||
)
|
||||
|
||||
(return)
|
||||
)
|
||||
unreachable
|
||||
)
|
||||
;; Destination AccountId to transfer the funds.
|
||||
;; Represented by H256 (32 bytes long) in little endian.
|
||||
(data (i32.const 4)
|
||||
"\09\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00"
|
||||
"\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00"
|
||||
"\00\00\00\00"
|
||||
)
|
||||
;; Amount of value to transfer.
|
||||
;; Represented by u128 (16 bytes long) in little endian.
|
||||
(data (i32.const 36)
|
||||
"\06\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00"
|
||||
"\00\00"
|
||||
)
|
||||
;; Length of the input buffer
|
||||
(data (i32.const 52) "\04")
|
||||
)
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn deploying_wasm_contract_should_work() {
|
||||
let transfer_code = wat::parse_str(CODE_TRANSFER).unwrap();
|
||||
let transfer_ch = <Runtime as pezframe_system::Config>::Hashing::hash(&transfer_code);
|
||||
|
||||
let addr =
|
||||
pezpallet_contracts::Pallet::<Runtime>::contract_address(&charlie(), &transfer_ch, &[], &[]);
|
||||
|
||||
let time = 42 * 1000;
|
||||
let b = construct_block(
|
||||
&mut new_test_ext(compact_code_unwrap()),
|
||||
1,
|
||||
GENESIS_HASH.into(),
|
||||
vec![
|
||||
CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Bare,
|
||||
function: RuntimeCall::Timestamp(pezpallet_timestamp::Call::set { now: time }),
|
||||
},
|
||||
CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Signed(charlie(), tx_ext(0, 0)),
|
||||
function: RuntimeCall::Contracts(pezpallet_contracts::Call::instantiate_with_code::<
|
||||
Runtime,
|
||||
> {
|
||||
value: 0,
|
||||
gas_limit: Weight::from_parts(500_000_000, 0),
|
||||
storage_deposit_limit: None,
|
||||
code: transfer_code,
|
||||
data: Vec::new(),
|
||||
salt: Vec::new(),
|
||||
}),
|
||||
},
|
||||
CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Signed(charlie(), tx_ext(1, 0)),
|
||||
function: RuntimeCall::Contracts(pezpallet_contracts::Call::call::<Runtime> {
|
||||
dest: pezsp_runtime::MultiAddress::Id(addr.clone()),
|
||||
value: 10,
|
||||
gas_limit: Weight::from_parts(500_000_000, 0),
|
||||
storage_deposit_limit: None,
|
||||
data: vec![0x00, 0x01, 0x02, 0x03],
|
||||
}),
|
||||
},
|
||||
],
|
||||
(time / SLOT_DURATION).into(),
|
||||
);
|
||||
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
|
||||
executor_call(&mut t, "Core_execute_block", &b.0).0.unwrap();
|
||||
|
||||
t.execute_with(|| {
|
||||
// Verify that the contract does exist by querying some of its storage items
|
||||
// It does not matter that the storage item itself does not exist.
|
||||
assert!(&pezpallet_contracts::Pallet::<Runtime>::get_storage(addr, vec![]).is_ok());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wasm_big_block_import_fails() {
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
|
||||
set_heap_pages(&mut t.ext(), 4);
|
||||
|
||||
let result = executor_call(&mut t, "Core_execute_block", &block_with_size(42, 0, 120_000).0).0;
|
||||
assert!(result.is_err()); // Err(Wasmi(Trap(Trap { kind: Host(AllocatorOutOfSpace) })))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_big_block_import_succeeds() {
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
|
||||
executor_call(&mut t, "Core_execute_block", &block_with_size(42, 0, 120_000).0)
|
||||
.0
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_big_block_import_fails_on_fallback() {
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
|
||||
// We set the heap pages to 8 because we know that should give an OOM in WASM with the given
|
||||
// block.
|
||||
set_heap_pages(&mut t.ext(), 8);
|
||||
|
||||
assert!(executor_call(&mut t, "Core_execute_block", &block_with_size(42, 0, 120_000).0)
|
||||
.0
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panic_execution_gives_error() {
|
||||
let mut t = new_test_ext(bloaty_code_unwrap());
|
||||
t.insert(
|
||||
<pezframe_system::Account<Runtime>>::hashed_key_for(alice()),
|
||||
AccountInfo::<<Runtime as pezframe_system::Config>::Nonce, _> {
|
||||
data: (0 * DOLLARS, 0u128, 0u128, 0u128),
|
||||
..Default::default()
|
||||
}
|
||||
.encode(),
|
||||
);
|
||||
t.insert(<pezpallet_balances::TotalIssuance<Runtime>>::hashed_key().to_vec(), 0_u128.encode());
|
||||
t.insert(<pezframe_system::BlockHash<Runtime>>::hashed_key_for(0), vec![0u8; 32]);
|
||||
|
||||
let r = executor_call(&mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u32))).0;
|
||||
assert!(r.is_ok());
|
||||
let r = executor_call(&mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt()))
|
||||
.0
|
||||
.unwrap();
|
||||
let r = ApplyExtrinsicResult::decode(&mut &r[..]).unwrap();
|
||||
assert_eq!(r, Err(InvalidTransaction::Payment.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn successful_execution_gives_ok() {
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
t.insert(
|
||||
<pezframe_system::Account<Runtime>>::hashed_key_for(alice()),
|
||||
AccountInfo::<<Runtime as pezframe_system::Config>::Nonce, _> {
|
||||
providers: 1,
|
||||
data: (111 * DOLLARS, 0u128, 0u128, 1u128 << 127),
|
||||
..Default::default()
|
||||
}
|
||||
.encode(),
|
||||
);
|
||||
t.insert(
|
||||
<pezframe_system::Account<Runtime>>::hashed_key_for(bob()),
|
||||
AccountInfo::<
|
||||
<Runtime as pezframe_system::Config>::Nonce,
|
||||
<Runtime as pezframe_system::Config>::AccountData,
|
||||
>::default()
|
||||
.encode(),
|
||||
);
|
||||
t.insert(
|
||||
<pezpallet_balances::TotalIssuance<Runtime>>::hashed_key().to_vec(),
|
||||
(111 * DOLLARS).encode(),
|
||||
);
|
||||
t.insert(<pezframe_system::BlockHash<Runtime>>::hashed_key_for(0), vec![0u8; 32]);
|
||||
|
||||
let r = executor_call(&mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u32))).0;
|
||||
assert!(r.is_ok());
|
||||
t.execute_with(|| {
|
||||
assert_eq!(Balances::total_balance(&alice()), 111 * DOLLARS);
|
||||
});
|
||||
|
||||
let weight_refund = Weight::zero();
|
||||
let fees_after_refund = t.execute_with(|| transfer_fee_with_refund(&xt(), weight_refund));
|
||||
|
||||
let r = executor_call(&mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt()))
|
||||
.0
|
||||
.unwrap();
|
||||
ApplyExtrinsicResult::decode(&mut &r[..])
|
||||
.unwrap()
|
||||
.expect("Extrinsic could not be applied")
|
||||
.expect("Extrinsic failed");
|
||||
|
||||
t.execute_with(|| {
|
||||
assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees_after_refund);
|
||||
assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_import_block_with_test_client() {
|
||||
use node_testing::client::{
|
||||
pezsp_consensus::BlockOrigin, ClientBlockImportExt, TestClientBuilder, TestClientBuilderExt,
|
||||
};
|
||||
|
||||
let client = TestClientBuilder::new().build();
|
||||
let block1 = changes_trie_block();
|
||||
let block_data = block1.0;
|
||||
let block = node_primitives::Block::decode(&mut &block_data[..]).unwrap();
|
||||
|
||||
futures::executor::block_on(client.import(BlockOrigin::Own, block)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_config_as_json_works() {
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
let r = executor_call(
|
||||
&mut t,
|
||||
"GenesisBuilder_get_preset",
|
||||
&None::<&pezsp_genesis_builder::PresetId>.encode(),
|
||||
)
|
||||
.0
|
||||
.unwrap();
|
||||
let r = Option::<Vec<u8>>::decode(&mut &r[..])
|
||||
.unwrap()
|
||||
.expect("default config is there");
|
||||
let json = String::from_utf8(r.into()).expect("returned value is json. qed.");
|
||||
let expected = include_str!("res/default_genesis_config.json").to_string();
|
||||
|
||||
assert_eq!(
|
||||
serde_json::from_str::<serde_json::Value>(&expected).unwrap(),
|
||||
serde_json::from_str::<serde_json::Value>(&json).unwrap()
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Unix only since it uses signals from [`common::run_node_for_a_while`].
|
||||
#![cfg(unix)]
|
||||
|
||||
use assert_cmd::cargo::cargo_bin;
|
||||
use std::process::Command;
|
||||
use tempfile::tempdir;
|
||||
|
||||
use bizinikiwi_cli_test_utils as common;
|
||||
|
||||
/// `benchmark block` works for the dev runtime using the wasm executor.
|
||||
#[tokio::test]
|
||||
async fn benchmark_block_works() {
|
||||
let base_dir = tempdir().expect("could not create a temp dir");
|
||||
|
||||
common::run_node_for_a_while(base_dir.path(), &["--dev", "--no-hardware-benchmarks"]).await;
|
||||
|
||||
// Invoke `benchmark block` with all options to make sure that they are valid.
|
||||
let status = Command::new(cargo_bin("bizinikiwi-node"))
|
||||
.args(["benchmark", "block", "--dev"])
|
||||
.arg("-d")
|
||||
.arg(base_dir.path())
|
||||
.args(["--from", "1", "--to", "1"])
|
||||
.args(["--repeat", "1"])
|
||||
.args(["--wasm-execution=compiled"])
|
||||
.status()
|
||||
.unwrap();
|
||||
|
||||
assert!(status.success())
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use assert_cmd::cargo::cargo_bin;
|
||||
use std::process::Command;
|
||||
use tempfile::tempdir;
|
||||
|
||||
/// Tests that the `benchmark extrinsic` command works for
|
||||
/// remark and transfer_keep_alive within the bizinikiwi dev runtime.
|
||||
#[test]
|
||||
fn benchmark_extrinsic_works() {
|
||||
benchmark_extrinsic("system", "remark");
|
||||
benchmark_extrinsic("balances", "transfer_keep_alive");
|
||||
}
|
||||
|
||||
/// Checks that the `benchmark extrinsic` command works for the given pallet and extrinsic.
|
||||
fn benchmark_extrinsic(pallet: &str, extrinsic: &str) {
|
||||
let base_dir = tempdir().expect("could not create a temp dir");
|
||||
|
||||
let status = Command::new(cargo_bin("bizinikiwi-node"))
|
||||
.args(&["benchmark", "extrinsic", "--dev"])
|
||||
.arg("-d")
|
||||
.arg(base_dir.path())
|
||||
.args(&["--pallet", pallet, "--extrinsic", extrinsic])
|
||||
// Run with low repeats for faster execution.
|
||||
.args(["--warmup=10", "--repeat=10", "--max-ext-per-block=10"])
|
||||
.args(["--wasm-execution=compiled"])
|
||||
.status()
|
||||
.unwrap();
|
||||
|
||||
assert!(status.success());
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use assert_cmd::cargo::cargo_bin;
|
||||
use std::process::Command;
|
||||
|
||||
/// Tests that the `benchmark machine` command works for the bizinikiwi dev runtime.
|
||||
#[test]
|
||||
fn benchmark_machine_works() {
|
||||
let status = Command::new(cargo_bin("bizinikiwi-node"))
|
||||
.args(["benchmark", "machine", "--dev"])
|
||||
.args([
|
||||
"--verify-duration",
|
||||
"0.1",
|
||||
"--disk-duration",
|
||||
"0.1",
|
||||
"--memory-duration",
|
||||
"0.1",
|
||||
"--hash-duration",
|
||||
"0.1",
|
||||
])
|
||||
// Make it succeed.
|
||||
.args(["--allow-fail"])
|
||||
.status()
|
||||
.unwrap();
|
||||
|
||||
assert!(status.success());
|
||||
}
|
||||
|
||||
/// Test that the hardware does not meet the requirements.
|
||||
///
|
||||
/// This is most likely to succeed since it uses a test profile.
|
||||
#[test]
|
||||
#[cfg(debug_assertions)]
|
||||
fn benchmark_machine_fails_with_slow_hardware() {
|
||||
let output = Command::new(cargo_bin("bizinikiwi-node"))
|
||||
.args(["benchmark", "machine", "--dev"])
|
||||
.args([
|
||||
"--verify-duration",
|
||||
"1.0",
|
||||
"--disk-duration",
|
||||
"2",
|
||||
"--hash-duration",
|
||||
"1.0",
|
||||
"--memory-duration",
|
||||
"1.0",
|
||||
"--tolerance",
|
||||
"0",
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
// Command should have failed.
|
||||
assert!(!output.status.success());
|
||||
// An `UnmetRequirement` error should have been printed.
|
||||
let log = String::from_utf8_lossy(&output.stderr).to_string();
|
||||
assert!(log.contains("UnmetRequirement"));
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use assert_cmd::cargo::cargo_bin;
|
||||
use std::process::Command;
|
||||
use tempfile::tempdir;
|
||||
|
||||
/// Tests that the `benchmark overhead` command works for the bizinikiwi dev runtime.
|
||||
#[test]
|
||||
fn benchmark_overhead_works() {
|
||||
let tmp_dir = tempdir().expect("could not create a temp dir");
|
||||
let base_path = tmp_dir.path();
|
||||
|
||||
// Only put 10 extrinsics into the block otherwise it takes forever to build it
|
||||
// especially for a non-release build.
|
||||
let status = Command::new(cargo_bin("bizinikiwi-node"))
|
||||
.args(&["benchmark", "overhead", "--dev", "-d"])
|
||||
.arg(base_path)
|
||||
.arg("--weight-path")
|
||||
.arg(base_path)
|
||||
.args(["--warmup", "10", "--repeat", "10"])
|
||||
.args(["--add", "100", "--mul", "1.2", "--metric", "p75"])
|
||||
.args(["--max-ext-per-block", "10"])
|
||||
.args(["--wasm-execution=compiled"])
|
||||
.status()
|
||||
.unwrap();
|
||||
assert!(status.success());
|
||||
|
||||
// Weight files have been created.
|
||||
assert!(base_path.join("block_weights.rs").exists());
|
||||
assert!(base_path.join("extrinsic_weights.rs").exists());
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![cfg(feature = "runtime-benchmarks")]
|
||||
|
||||
use assert_cmd::cargo::cargo_bin;
|
||||
use std::process::Command;
|
||||
|
||||
/// `benchmark pallet` works for the different combinations of `steps` and `repeat`.
|
||||
#[test]
|
||||
fn benchmark_pallet_works() {
|
||||
// Some invalid combinations:
|
||||
benchmark_pallet(0, 10, false);
|
||||
benchmark_pallet(1, 10, false);
|
||||
// ... and some valid:
|
||||
benchmark_pallet(2, 1, true);
|
||||
benchmark_pallet(50, 20, true);
|
||||
benchmark_pallet(20, 50, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn benchmark_pallet_args_work() {
|
||||
benchmark_pallet_args(&["--list", "--pallet=pezpallet_balances"], true);
|
||||
benchmark_pallet_args(&["--list", "--pallet=pezpallet_balances"], true);
|
||||
benchmark_pallet_args(
|
||||
&["--list", "--pallet=pezpallet_balances", "--genesis-builder=spec-genesis"],
|
||||
true,
|
||||
);
|
||||
benchmark_pallet_args(
|
||||
&["--list", "--pallet=pezpallet_balances", "--chain=dev", "--genesis-builder=spec-genesis"],
|
||||
true,
|
||||
);
|
||||
benchmark_pallet_args(
|
||||
&["--list", "--pallet=pezpallet_balances", "--chain=dev", "--genesis-builder=spec-runtime"],
|
||||
true,
|
||||
);
|
||||
// Error because no runtime is provided:
|
||||
benchmark_pallet_args(
|
||||
&["--list", "--pallet=pezpallet_balances", "--chain=dev", "--genesis-builder=runtime"],
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
fn benchmark_pallet(steps: u32, repeat: u32, should_work: bool) {
|
||||
let status = Command::new(cargo_bin("bizinikiwi-node"))
|
||||
.args(["benchmark", "pallet", "--dev"])
|
||||
// Use the `addition` benchmark since is the fastest.
|
||||
.args(["--pallet", "pezframe-benchmarking", "--extrinsic", "addition"])
|
||||
.args(["--steps", &format!("{}", steps), "--repeat", &format!("{}", repeat)])
|
||||
.args([
|
||||
"--wasm-execution=compiled",
|
||||
"--no-storage-info",
|
||||
"--no-median-slopes",
|
||||
"--no-min-squares",
|
||||
"--heap-pages=4096",
|
||||
])
|
||||
.status()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(status.success(), should_work);
|
||||
}
|
||||
|
||||
fn benchmark_pallet_args(args: &[&str], should_work: bool) {
|
||||
let status = Command::new(cargo_bin("bizinikiwi-node"))
|
||||
.args(["benchmark", "pallet"])
|
||||
.args(args)
|
||||
.status()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(status.success(), should_work);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![cfg(feature = "runtime-benchmarks")]
|
||||
|
||||
use assert_cmd::cargo::cargo_bin;
|
||||
use std::{
|
||||
path::Path,
|
||||
process::{Command, ExitStatus},
|
||||
};
|
||||
use tempfile::tempdir;
|
||||
|
||||
/// Tests that the `benchmark storage` command works for the dev runtime.
|
||||
#[test]
|
||||
fn benchmark_storage_works() {
|
||||
let tmp_dir = tempdir().expect("could not create a temp dir");
|
||||
let base_path = tmp_dir.path();
|
||||
|
||||
// Benchmarking the storage works and creates the correct weight file.
|
||||
assert!(benchmark_storage("rocksdb", base_path).success());
|
||||
assert!(base_path.join("rocksdb_weights.rs").exists());
|
||||
|
||||
assert!(benchmark_storage("paritydb", base_path).success());
|
||||
assert!(base_path.join("paritydb_weights.rs").exists());
|
||||
}
|
||||
|
||||
fn benchmark_storage(db: &str, base_path: &Path) -> ExitStatus {
|
||||
Command::new(cargo_bin("bizinikiwi-node"))
|
||||
.args(&["benchmark", "storage", "--dev"])
|
||||
.arg("--db")
|
||||
.arg(db)
|
||||
.arg("--weight-path")
|
||||
.arg(base_path)
|
||||
.args(["--state-version", "1"])
|
||||
.args(["--batch-size", "1"])
|
||||
.args(["--warmups", "0"])
|
||||
.args(["--add", "100", "--mul", "1.2", "--metric", "p75"])
|
||||
.arg("--include-child-trees")
|
||||
.status()
|
||||
.unwrap()
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use assert_cmd::cargo::cargo_bin;
|
||||
use std::process::Command;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[test]
|
||||
fn build_spec_works() {
|
||||
let base_path = tempdir().expect("could not create a temp dir");
|
||||
|
||||
let output = Command::new(cargo_bin("bizinikiwi-node"))
|
||||
.args(&["build-spec", "--dev", "-d"])
|
||||
.arg(base_path.path())
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success());
|
||||
|
||||
// Make sure that the `dev` chain folder exists, but the `db` doesn't
|
||||
assert!(base_path.path().join("chains/dev/").exists());
|
||||
assert!(!base_path.path().join("chains/dev/db").exists());
|
||||
|
||||
let _value: serde_json::Value = serde_json::from_slice(output.stdout.as_slice()).unwrap();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![cfg(unix)]
|
||||
|
||||
use assert_cmd::cargo::cargo_bin;
|
||||
use std::process::Command;
|
||||
use tempfile::tempdir;
|
||||
|
||||
use bizinikiwi_cli_test_utils as common;
|
||||
|
||||
#[tokio::test]
|
||||
async fn check_block_works() {
|
||||
let base_path = tempdir().expect("could not create a temp dir");
|
||||
|
||||
common::run_node_for_a_while(base_path.path(), &["--dev", "--no-hardware-benchmarks"]).await;
|
||||
|
||||
let status = Command::new(cargo_bin("bizinikiwi-node"))
|
||||
.args(&["check-block", "--dev", "-d"])
|
||||
.arg(base_path.path())
|
||||
.arg("1")
|
||||
.status()
|
||||
.unwrap();
|
||||
assert!(status.success());
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use pezframe_support::Hashable;
|
||||
use pezframe_system::offchain::AppCrypto;
|
||||
use pezkuwi_sdk::*;
|
||||
use pezsc_executor::error::Result;
|
||||
use pezsp_consensus_babe::{
|
||||
digests::{PreDigest, SecondaryPlainPreDigest},
|
||||
Slot, BABE_ENGINE_ID,
|
||||
};
|
||||
use pezsp_core::{
|
||||
crypto::KeyTypeId,
|
||||
sr25519::Signature,
|
||||
traits::{CallContext, CodeExecutor, RuntimeCode},
|
||||
};
|
||||
use pezsp_runtime::{
|
||||
traits::{BlakeTwo256, Header as HeaderT},
|
||||
ApplyExtrinsicResult, Digest, DigestItem, MultiSignature, MultiSigner,
|
||||
};
|
||||
use pezsp_state_machine::TestExternalities as CoreTestExternalities;
|
||||
|
||||
use kitchensink_runtime::{
|
||||
constants::currency::*, Block, BuildStorage, CheckedExtrinsic, Header, Runtime,
|
||||
UncheckedExtrinsic,
|
||||
};
|
||||
use node_primitives::{BlockNumber, Hash};
|
||||
use node_testing::keyring::*;
|
||||
use pezsp_externalities::Externalities;
|
||||
use pezstaging_node_cli::service::RuntimeExecutor;
|
||||
|
||||
pub const TEST_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"test");
|
||||
|
||||
pub mod sr25519 {
|
||||
mod app_sr25519 {
|
||||
use super::super::TEST_KEY_TYPE_ID;
|
||||
use pezkuwi_sdk::pezsp_application_crypto::{app_crypto, sr25519};
|
||||
app_crypto!(sr25519, TEST_KEY_TYPE_ID);
|
||||
}
|
||||
|
||||
pub type AuthorityId = app_sr25519::Public;
|
||||
}
|
||||
|
||||
pub struct TestAuthorityId;
|
||||
impl AppCrypto<MultiSigner, MultiSignature> for TestAuthorityId {
|
||||
type RuntimeAppPublic = sr25519::AuthorityId;
|
||||
type GenericSignature = Signature;
|
||||
type GenericPublic = pezsp_core::sr25519::Public;
|
||||
}
|
||||
|
||||
/// The wasm runtime code.
|
||||
///
|
||||
/// `compact` since it is after post-processing with wasm-gc which performs tree-shaking thus
|
||||
/// making the binary slimmer. There is a convention to use compact version of the runtime
|
||||
/// as canonical.
|
||||
pub fn compact_code_unwrap() -> &'static [u8] {
|
||||
kitchensink_runtime::WASM_BINARY.expect(
|
||||
"Development wasm binary is not available. Testing is only supported with the flag \
|
||||
disabled.",
|
||||
)
|
||||
}
|
||||
|
||||
pub const GENESIS_HASH: [u8; 32] = [69u8; 32];
|
||||
|
||||
pub const SPEC_VERSION: u32 = kitchensink_runtime::VERSION.spec_version;
|
||||
|
||||
pub const TRANSACTION_VERSION: u32 = kitchensink_runtime::VERSION.transaction_version;
|
||||
|
||||
pub type TestExternalities<H> = CoreTestExternalities<H>;
|
||||
|
||||
pub fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic {
|
||||
node_testing::keyring::sign(xt, SPEC_VERSION, TRANSACTION_VERSION, GENESIS_HASH, None)
|
||||
}
|
||||
|
||||
pub fn default_transfer_call() -> pezpallet_balances::Call<Runtime> {
|
||||
pezpallet_balances::Call::<Runtime>::transfer_allow_death {
|
||||
dest: bob().into(),
|
||||
value: 69 * DOLLARS,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_block_number(n: u32) -> Header {
|
||||
Header::new(n, Default::default(), Default::default(), [69; 32].into(), Default::default())
|
||||
}
|
||||
|
||||
pub fn executor() -> RuntimeExecutor {
|
||||
RuntimeExecutor::builder().build()
|
||||
}
|
||||
|
||||
pub fn executor_call(
|
||||
t: &mut TestExternalities<BlakeTwo256>,
|
||||
method: &str,
|
||||
data: &[u8],
|
||||
) -> (Result<Vec<u8>>, bool) {
|
||||
let mut t = t.ext();
|
||||
|
||||
let code = t.storage(pezsp_core::storage::well_known_keys::CODE).unwrap();
|
||||
let heap_pages = t.storage(pezsp_core::storage::well_known_keys::HEAP_PAGES);
|
||||
let runtime_code = RuntimeCode {
|
||||
code_fetcher: &pezsp_core::traits::WrappedRuntimeCode(code.as_slice().into()),
|
||||
hash: pezsp_crypto_hashing::blake2_256(&code).to_vec(),
|
||||
heap_pages: heap_pages.and_then(|hp| Decode::decode(&mut &hp[..]).ok()),
|
||||
};
|
||||
pezsp_tracing::try_init_simple();
|
||||
executor().call(&mut t, &runtime_code, method, data, CallContext::Onchain)
|
||||
}
|
||||
|
||||
pub fn new_test_ext(code: &[u8]) -> TestExternalities<BlakeTwo256> {
|
||||
pezsp_tracing::try_init_simple();
|
||||
let ext = TestExternalities::new_with_code(
|
||||
code,
|
||||
node_testing::genesis::config().build_storage().unwrap(),
|
||||
);
|
||||
ext
|
||||
}
|
||||
|
||||
/// Construct a fake block.
|
||||
///
|
||||
/// `extrinsics` must be a list of valid extrinsics, i.e. none of the extrinsics for example
|
||||
/// can report `ExhaustResources`. Otherwise, this function panics.
|
||||
pub fn construct_block(
|
||||
env: &mut TestExternalities<BlakeTwo256>,
|
||||
number: BlockNumber,
|
||||
parent_hash: Hash,
|
||||
extrinsics: Vec<CheckedExtrinsic>,
|
||||
babe_slot: Slot,
|
||||
) -> (Vec<u8>, Hash) {
|
||||
use pezsp_trie::{LayoutV1 as Layout, TrieConfiguration};
|
||||
|
||||
// sign extrinsics.
|
||||
let extrinsics = extrinsics.into_iter().map(sign).collect::<Vec<_>>();
|
||||
|
||||
// calculate the header fields that we can.
|
||||
let extrinsics_root =
|
||||
Layout::<BlakeTwo256>::ordered_trie_root(extrinsics.iter().map(Encode::encode))
|
||||
.to_fixed_bytes()
|
||||
.into();
|
||||
|
||||
let header = Header {
|
||||
parent_hash,
|
||||
number,
|
||||
extrinsics_root,
|
||||
state_root: Default::default(),
|
||||
digest: Digest {
|
||||
logs: vec![DigestItem::PreRuntime(
|
||||
BABE_ENGINE_ID,
|
||||
PreDigest::SecondaryPlain(SecondaryPlainPreDigest {
|
||||
slot: babe_slot,
|
||||
authority_index: 42,
|
||||
})
|
||||
.encode(),
|
||||
)],
|
||||
},
|
||||
};
|
||||
|
||||
// execute the block to get the real header.
|
||||
executor_call(env, "Core_initialize_block", &header.encode()).0.unwrap();
|
||||
|
||||
for extrinsic in extrinsics.iter() {
|
||||
// Try to apply the `extrinsic`. It should be valid, in the sense that it passes
|
||||
// all pre-inclusion checks.
|
||||
let r = executor_call(env, "BlockBuilder_apply_extrinsic", &extrinsic.encode())
|
||||
.0
|
||||
.expect("application of an extrinsic failed");
|
||||
|
||||
match ApplyExtrinsicResult::decode(&mut &r[..])
|
||||
.expect("apply result deserialization failed")
|
||||
{
|
||||
Ok(_) => {},
|
||||
Err(e) => panic!("Applying extrinsic failed: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
let header = Header::decode(
|
||||
&mut &executor_call(env, "BlockBuilder_finalize_block", &[0u8; 0]).0.unwrap()[..],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let hash = header.blake2_256();
|
||||
(Block { header, extrinsics }.encode(), hash.into())
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![cfg(unix)]
|
||||
|
||||
use assert_cmd::cargo::cargo_bin;
|
||||
use regex::Regex;
|
||||
use std::{fs, path::PathBuf, process::Command};
|
||||
use tempfile::{tempdir, TempDir};
|
||||
|
||||
use bizinikiwi_cli_test_utils as common;
|
||||
|
||||
fn contains_error(logged_output: &str) -> bool {
|
||||
logged_output.contains("Error")
|
||||
}
|
||||
|
||||
/// Helper struct to execute the export/import/revert tests.
|
||||
/// The fields are paths to a temporary directory
|
||||
struct ExportImportRevertExecutor<'a> {
|
||||
base_path: &'a TempDir,
|
||||
exported_blocks_file: &'a PathBuf,
|
||||
db_path: &'a PathBuf,
|
||||
num_exported_blocks: Option<u64>,
|
||||
}
|
||||
|
||||
/// Format options for export / import commands.
|
||||
enum FormatOpt {
|
||||
Json,
|
||||
Binary,
|
||||
}
|
||||
|
||||
/// Command corresponding to the different commands we would like to run.
|
||||
enum SubCommand {
|
||||
ExportBlocks,
|
||||
ImportBlocks,
|
||||
}
|
||||
|
||||
impl ToString for SubCommand {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
SubCommand::ExportBlocks => String::from("export-blocks"),
|
||||
SubCommand::ImportBlocks => String::from("import-blocks"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExportImportRevertExecutor<'a> {
|
||||
fn new(
|
||||
base_path: &'a TempDir,
|
||||
exported_blocks_file: &'a PathBuf,
|
||||
db_path: &'a PathBuf,
|
||||
) -> Self {
|
||||
Self { base_path, exported_blocks_file, db_path, num_exported_blocks: None }
|
||||
}
|
||||
|
||||
/// Helper method to run a command. Returns a string corresponding to what has been logged.
|
||||
fn run_block_command(
|
||||
&self,
|
||||
sub_command: SubCommand,
|
||||
format_opt: FormatOpt,
|
||||
expected_to_fail: bool,
|
||||
) -> String {
|
||||
let sub_command_str = sub_command.to_string();
|
||||
// Adding "--binary" if need be.
|
||||
let arguments: Vec<&str> = match format_opt {
|
||||
FormatOpt::Binary => {
|
||||
vec![&sub_command_str, "--dev", "--binary", "-d"]
|
||||
},
|
||||
FormatOpt::Json => vec![&sub_command_str, "--dev", "-d"],
|
||||
};
|
||||
|
||||
let tmp: TempDir;
|
||||
// Setting base_path to be a temporary folder if we are importing blocks.
|
||||
// This allows us to make sure we are importing from scratch.
|
||||
let base_path = match sub_command {
|
||||
SubCommand::ExportBlocks => &self.base_path.path(),
|
||||
SubCommand::ImportBlocks => {
|
||||
tmp = tempdir().unwrap();
|
||||
tmp.path()
|
||||
},
|
||||
};
|
||||
|
||||
// Running the command and capturing the output.
|
||||
let output = Command::new(cargo_bin("bizinikiwi-node"))
|
||||
.args(&arguments)
|
||||
.arg(&base_path)
|
||||
.arg(&self.exported_blocks_file)
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let logged_output = String::from_utf8_lossy(&output.stderr).to_string();
|
||||
|
||||
if expected_to_fail {
|
||||
// Checking that we did indeed find an error.
|
||||
assert!(contains_error(&logged_output), "expected to error but did not error!");
|
||||
assert!(!output.status.success());
|
||||
} else {
|
||||
// Making sure no error were logged.
|
||||
assert!(
|
||||
!contains_error(&logged_output),
|
||||
"expected not to error but error'd: \n{logged_output}"
|
||||
);
|
||||
assert!(output.status.success());
|
||||
}
|
||||
|
||||
logged_output
|
||||
}
|
||||
|
||||
/// Runs the `export-blocks` command.
|
||||
fn run_export(&mut self, fmt_opt: FormatOpt) {
|
||||
let log = self.run_block_command(SubCommand::ExportBlocks, fmt_opt, false);
|
||||
|
||||
// Using regex to find out how many block we exported.
|
||||
let re = Regex::new(r"Exporting blocks from #\d* to #(?P<exported_blocks>\d*)").unwrap();
|
||||
let caps = re.captures(&log).unwrap();
|
||||
// Saving the number of blocks we've exported for further use.
|
||||
self.num_exported_blocks = Some(caps["exported_blocks"].parse::<u64>().unwrap());
|
||||
|
||||
let metadata = fs::metadata(&self.exported_blocks_file).unwrap();
|
||||
assert!(metadata.len() > 0, "file exported_blocks should not be empty");
|
||||
|
||||
let _ = fs::remove_dir_all(&self.db_path);
|
||||
}
|
||||
|
||||
/// Runs the `import-blocks` command, asserting that an error was found or
|
||||
/// not depending on `expected_to_fail`.
|
||||
fn run_import(&mut self, fmt_opt: FormatOpt, expected_to_fail: bool) {
|
||||
let log = self.run_block_command(SubCommand::ImportBlocks, fmt_opt, expected_to_fail);
|
||||
|
||||
if !expected_to_fail {
|
||||
// Using regex to find out how much block we imported,
|
||||
// and what's the best current block.
|
||||
let re =
|
||||
Regex::new(r"Imported (?P<imported>\d*) blocks. Best: #(?P<best>\d*)").unwrap();
|
||||
let caps = re.captures(&log).expect("capture should have succeeded");
|
||||
let imported = caps["imported"].parse::<u64>().unwrap();
|
||||
let best = caps["best"].parse::<u64>().unwrap();
|
||||
|
||||
assert_eq!(imported, best, "numbers of blocks imported and best number differs");
|
||||
assert_eq!(
|
||||
best,
|
||||
self.num_exported_blocks.expect("number of exported blocks cannot be None; qed"),
|
||||
"best block number and number of expected blocks should not differ"
|
||||
);
|
||||
}
|
||||
self.num_exported_blocks = None;
|
||||
}
|
||||
|
||||
/// Runs the `revert` command.
|
||||
fn run_revert(&self) {
|
||||
let output = Command::new(cargo_bin("bizinikiwi-node"))
|
||||
.args(&["revert", "--dev", "-d"])
|
||||
.arg(&self.base_path.path())
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let logged_output = String::from_utf8_lossy(&output.stderr).to_string();
|
||||
|
||||
// Reverting should not log any error.
|
||||
assert!(!contains_error(&logged_output));
|
||||
// Command should never fail.
|
||||
assert!(output.status.success());
|
||||
}
|
||||
|
||||
/// Helper function that runs the whole export / import / revert flow and checks for errors.
|
||||
fn run(&mut self, export_fmt: FormatOpt, import_fmt: FormatOpt, expected_to_fail: bool) {
|
||||
self.run_export(export_fmt);
|
||||
self.run_import(import_fmt, expected_to_fail);
|
||||
self.run_revert();
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn export_import_revert() {
|
||||
let base_path = tempdir().expect("could not create a temp dir");
|
||||
let exported_blocks_file = base_path.path().join("exported_blocks");
|
||||
let db_path = base_path.path().join("db");
|
||||
|
||||
common::run_node_for_a_while(base_path.path(), &["--dev", "--no-hardware-benchmarks"]).await;
|
||||
|
||||
let mut executor = ExportImportRevertExecutor::new(&base_path, &exported_blocks_file, &db_path);
|
||||
|
||||
// Binary and binary should work.
|
||||
executor.run(FormatOpt::Binary, FormatOpt::Binary, false);
|
||||
// Binary and JSON should fail.
|
||||
executor.run(FormatOpt::Binary, FormatOpt::Json, true);
|
||||
// JSON and JSON should work.
|
||||
executor.run(FormatOpt::Json, FormatOpt::Json, false);
|
||||
// JSON and binary should fail.
|
||||
executor.run(FormatOpt::Json, FormatOpt::Binary, true);
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use codec::{Encode, Joiner};
|
||||
use pezframe_support::{
|
||||
dispatch::GetDispatchInfo,
|
||||
traits::Currency,
|
||||
weights::{constants::ExtrinsicBaseWeight, IdentityFee, WeightToFee},
|
||||
};
|
||||
use kitchensink_runtime::{
|
||||
constants::{currency::*, time::SLOT_DURATION},
|
||||
Balances, CheckedExtrinsic, Multiplier, Runtime, RuntimeCall, TransactionByteFee,
|
||||
TransactionPayment,
|
||||
};
|
||||
use node_primitives::Balance;
|
||||
use node_testing::keyring::*;
|
||||
use pezkuwi_sdk::*;
|
||||
use pezsp_runtime::{traits::One, Perbill};
|
||||
|
||||
pub mod common;
|
||||
use self::common::{sign, *};
|
||||
|
||||
#[test]
|
||||
fn fee_multiplier_increases_and_decreases_on_big_weight() {
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
|
||||
// initial fee multiplier must be one.
|
||||
let mut prev_multiplier = Multiplier::one();
|
||||
|
||||
t.execute_with(|| {
|
||||
assert_eq!(TransactionPayment::next_fee_multiplier(), prev_multiplier);
|
||||
});
|
||||
|
||||
let mut tt = new_test_ext(compact_code_unwrap());
|
||||
|
||||
let time1 = 42 * 1000;
|
||||
// big one in terms of weight.
|
||||
let block1 = construct_block(
|
||||
&mut tt,
|
||||
1,
|
||||
GENESIS_HASH.into(),
|
||||
vec![
|
||||
CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Bare,
|
||||
function: RuntimeCall::Timestamp(pezpallet_timestamp::Call::set { now: time1 }),
|
||||
},
|
||||
CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Signed(charlie(), tx_ext(0, 0)),
|
||||
function: RuntimeCall::Sudo(pezpallet_sudo::Call::sudo {
|
||||
call: Box::new(RuntimeCall::RootTesting(
|
||||
pezpallet_root_testing::Call::fill_block { ratio: Perbill::from_percent(60) },
|
||||
)),
|
||||
}),
|
||||
},
|
||||
],
|
||||
(time1 / SLOT_DURATION).into(),
|
||||
);
|
||||
|
||||
let time2 = 52 * 1000;
|
||||
// small one in terms of weight.
|
||||
let block2 = construct_block(
|
||||
&mut tt,
|
||||
2,
|
||||
block1.1,
|
||||
vec![
|
||||
CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Bare,
|
||||
function: RuntimeCall::Timestamp(pezpallet_timestamp::Call::set { now: time2 }),
|
||||
},
|
||||
CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Signed(charlie(), tx_ext(1, 0)),
|
||||
function: RuntimeCall::System(pezframe_system::Call::remark { remark: vec![0; 1] }),
|
||||
},
|
||||
],
|
||||
(time2 / SLOT_DURATION).into(),
|
||||
);
|
||||
|
||||
println!(
|
||||
"++ Block 1 size: {} / Block 2 size {}",
|
||||
block1.0.encode().len(),
|
||||
block2.0.encode().len(),
|
||||
);
|
||||
|
||||
// execute a big block.
|
||||
executor_call(&mut t, "Core_execute_block", &block1.0).0.unwrap();
|
||||
|
||||
// weight multiplier is increased for next block.
|
||||
t.execute_with(|| {
|
||||
let fm = TransactionPayment::next_fee_multiplier();
|
||||
println!("After a big block: {:?} -> {:?}", prev_multiplier, fm);
|
||||
assert!(fm > prev_multiplier);
|
||||
prev_multiplier = fm;
|
||||
});
|
||||
|
||||
// execute a big block.
|
||||
executor_call(&mut t, "Core_execute_block", &block2.0).0.unwrap();
|
||||
|
||||
// weight multiplier is increased for next block.
|
||||
t.execute_with(|| {
|
||||
let fm = TransactionPayment::next_fee_multiplier();
|
||||
println!("After a small block: {:?} -> {:?}", prev_multiplier, fm);
|
||||
assert!(fm < prev_multiplier);
|
||||
});
|
||||
}
|
||||
|
||||
fn new_account_info(free_dollars: u128) -> Vec<u8> {
|
||||
pezframe_system::AccountInfo {
|
||||
nonce: 0u32,
|
||||
consumers: 0,
|
||||
providers: 1,
|
||||
sufficients: 0,
|
||||
data: (free_dollars * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 1u128 << 127),
|
||||
}
|
||||
.encode()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transaction_fee_is_correct() {
|
||||
// This uses the exact values of bizinikiwi-node.
|
||||
//
|
||||
// weight of transfer call as of now: 1_000_000
|
||||
// if weight of the cheapest weight would be 10^7, this would be 10^9, which is:
|
||||
// - 1 MILLICENTS in bizinikiwi node.
|
||||
// - 1 milli-dot based on current pezkuwi runtime.
|
||||
// (this based on assigning 0.1 CENT to the cheapest tx with `weight = 100`)
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
t.insert(<pezframe_system::Account<Runtime>>::hashed_key_for(alice()), new_account_info(100));
|
||||
t.insert(<pezframe_system::Account<Runtime>>::hashed_key_for(bob()), new_account_info(10));
|
||||
t.insert(
|
||||
<pezpallet_balances::TotalIssuance<Runtime>>::hashed_key().to_vec(),
|
||||
(110 * DOLLARS).encode(),
|
||||
);
|
||||
t.insert(<pezframe_system::BlockHash<Runtime>>::hashed_key_for(0), vec![0u8; 32]);
|
||||
|
||||
let tip = 1_000_000;
|
||||
let xt = sign(CheckedExtrinsic {
|
||||
format: pezsp_runtime::generic::ExtrinsicFormat::Signed(alice(), tx_ext(0, tip)),
|
||||
function: RuntimeCall::Balances(default_transfer_call()),
|
||||
});
|
||||
|
||||
let r = executor_call(&mut t, "Core_initialize_block", &vec![].and(&from_block_number(1u32))).0;
|
||||
|
||||
assert!(r.is_ok());
|
||||
let r = executor_call(&mut t, "BlockBuilder_apply_extrinsic", &vec![].and(&xt.clone())).0;
|
||||
assert!(r.is_ok());
|
||||
|
||||
t.execute_with(|| {
|
||||
assert_eq!(Balances::total_balance(&bob()), (10 + 69) * DOLLARS);
|
||||
// Components deducted from alice's balances:
|
||||
// - Base fee
|
||||
// - Weight fee
|
||||
// - Length fee
|
||||
// - Tip
|
||||
// - Creation-fee of bob's account.
|
||||
let mut balance_alice = (100 - 69) * DOLLARS;
|
||||
|
||||
let base_weight = ExtrinsicBaseWeight::get();
|
||||
let base_fee = IdentityFee::<Balance>::weight_to_fee(&base_weight);
|
||||
|
||||
let length_fee = TransactionByteFee::get() * (xt.clone().encode().len() as Balance);
|
||||
balance_alice -= length_fee;
|
||||
|
||||
let mut info = default_transfer_call().get_dispatch_info();
|
||||
info.extension_weight = xt.0.extension_weight();
|
||||
let weight = info.total_weight();
|
||||
let weight_fee = IdentityFee::<Balance>::weight_to_fee(&weight);
|
||||
|
||||
// we know that weight to fee multiplier is effect-less in block 1.
|
||||
// current weight of transfer = 200_000_000
|
||||
// Linear weight to fee is 1:1 right now (1 weight = 1 unit of balance)
|
||||
assert_eq!(weight_fee, weight.ref_time() as Balance);
|
||||
balance_alice -= base_fee;
|
||||
balance_alice -= weight_fee;
|
||||
balance_alice -= tip;
|
||||
|
||||
assert_eq!(Balances::total_balance(&alice()), balance_alice);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![cfg(unix)]
|
||||
|
||||
use assert_cmd::cargo::cargo_bin;
|
||||
use std::process::Command;
|
||||
use tempfile::tempdir;
|
||||
|
||||
use bizinikiwi_cli_test_utils as common;
|
||||
|
||||
#[tokio::test]
|
||||
async fn inspect_works() {
|
||||
let base_path = tempdir().expect("could not create a temp dir");
|
||||
|
||||
common::run_node_for_a_while(base_path.path(), &["--dev", "--no-hardware-benchmarks"]).await;
|
||||
|
||||
let status = Command::new(cargo_bin("bizinikiwi-node"))
|
||||
.args(&["inspect", "--dev", "-d"])
|
||||
.arg(base_path.path())
|
||||
.args(&["block", "1"])
|
||||
.status()
|
||||
.unwrap();
|
||||
assert!(status.success());
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use assert_cmd::cargo::cargo_bin;
|
||||
use std::process::Command;
|
||||
use tempfile::tempdir;
|
||||
|
||||
use bizinikiwi_cli_test_utils as common;
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg(unix)]
|
||||
async fn purge_chain_works() {
|
||||
let base_path = tempdir().expect("could not create a temp dir");
|
||||
|
||||
common::run_node_for_a_while(base_path.path(), &["--dev", "--no-hardware-benchmarks"]).await;
|
||||
|
||||
let status = Command::new(cargo_bin("bizinikiwi-node"))
|
||||
.args(&["purge-chain", "--dev", "-d"])
|
||||
.arg(base_path.path())
|
||||
.arg("-y")
|
||||
.status()
|
||||
.unwrap();
|
||||
assert!(status.success());
|
||||
|
||||
// Make sure that the `dev` chain folder exists, but the `db` is deleted.
|
||||
assert!(base_path.path().join("chains/dev/").exists());
|
||||
assert!(!base_path.path().join("chains/dev/db/full").exists());
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use tempfile::tempdir;
|
||||
|
||||
use bizinikiwi_cli_test_utils as common;
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg(unix)]
|
||||
async fn remember_state_pruning_works() {
|
||||
let base_path = tempdir().expect("could not create a temp dir");
|
||||
|
||||
// First run with `--state-pruning=archive`.
|
||||
common::run_node_for_a_while(
|
||||
base_path.path(),
|
||||
&["--dev", "--state-pruning=archive", "--no-hardware-benchmarks"],
|
||||
)
|
||||
.await;
|
||||
|
||||
// Then run again without specifying the state pruning.
|
||||
// This should load state pruning settings from the db.
|
||||
common::run_node_for_a_while(base_path.path(), &["--dev", "--no-hardware-benchmarks"]).await;
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
{
|
||||
"system": {},
|
||||
"babe": {
|
||||
"authorities": [],
|
||||
"epochConfig": {
|
||||
"allowed_slots": "PrimaryAndSecondaryVRFSlots",
|
||||
"c": [
|
||||
1,
|
||||
4
|
||||
]
|
||||
}
|
||||
},
|
||||
"indices": {
|
||||
"indices": []
|
||||
},
|
||||
"balances": {
|
||||
"balances": [],
|
||||
"devAccounts": null
|
||||
},
|
||||
"broker": {},
|
||||
"transactionPayment": {
|
||||
"multiplier": "1000000000000000000"
|
||||
},
|
||||
"staking": {
|
||||
"validatorCount": 0,
|
||||
"minimumValidatorCount": 0,
|
||||
"invulnerables": [],
|
||||
"forceEra": "NotForcing",
|
||||
"slashRewardFraction": 0,
|
||||
"canceledPayout": 0,
|
||||
"stakers": [],
|
||||
"minNominatorBond": 0,
|
||||
"minValidatorBond": 0,
|
||||
"maxValidatorCount": null,
|
||||
"maxNominatorCount": null
|
||||
},
|
||||
"session": {
|
||||
"keys": [],
|
||||
"nonAuthorityKeys": []
|
||||
|
||||
},
|
||||
"revive": {
|
||||
},
|
||||
"democracy": {},
|
||||
"council": {
|
||||
"members": []
|
||||
},
|
||||
"technicalCommittee": {
|
||||
"members": []
|
||||
},
|
||||
"elections": {
|
||||
"members": []
|
||||
},
|
||||
"technicalMembership": {
|
||||
"members": []
|
||||
},
|
||||
"grandpa": {
|
||||
"authorities": []
|
||||
},
|
||||
"beefy": {
|
||||
"authorities": [],
|
||||
"genesisBlock": 1
|
||||
},
|
||||
"treasury": {},
|
||||
"sudo": {
|
||||
"key": null
|
||||
},
|
||||
"imOnline": {
|
||||
"keys": []
|
||||
},
|
||||
"authorityDiscovery": {
|
||||
"keys": []
|
||||
},
|
||||
"society": {
|
||||
"pot": 0
|
||||
},
|
||||
"vesting": {
|
||||
"vesting": []
|
||||
},
|
||||
"glutton": {
|
||||
"compute": "0",
|
||||
"storage": "0",
|
||||
"blockLength": "0",
|
||||
"trashDataCount": 0
|
||||
},
|
||||
"assets": {
|
||||
"assets": [],
|
||||
"metadata": [],
|
||||
"accounts": [],
|
||||
"nextAssetId": null,
|
||||
"reserves": []
|
||||
},
|
||||
"poolAssets": {
|
||||
"assets": [],
|
||||
"metadata": [],
|
||||
"accounts": [],
|
||||
"nextAssetId": null,
|
||||
"reserves": []
|
||||
},
|
||||
"transactionStorage": {
|
||||
"byteFee": 10,
|
||||
"entryFee": 1000,
|
||||
"storagePeriod": 100800
|
||||
},
|
||||
"allianceMotion": {
|
||||
"members": []
|
||||
},
|
||||
"alliance": {
|
||||
"fellows": [],
|
||||
"allies": []
|
||||
},
|
||||
"mixnet": {
|
||||
"mixnodes": []
|
||||
},
|
||||
"nominationPools": {
|
||||
"minJoinBond": 0,
|
||||
"minCreateBond": 0,
|
||||
"maxPools": 16,
|
||||
"maxMembersPerPool": 32,
|
||||
"maxMembers": 512,
|
||||
"globalMaxCommission": null
|
||||
},
|
||||
"txPause": {
|
||||
"paused": []
|
||||
},
|
||||
"safeMode": {
|
||||
"enteredUntil": null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![cfg(unix)]
|
||||
use assert_cmd::cargo::cargo_bin;
|
||||
use nix::sys::signal::Signal::{self, SIGINT, SIGTERM};
|
||||
use std::{
|
||||
process::{self, Command},
|
||||
time::Duration,
|
||||
};
|
||||
use tempfile::tempdir;
|
||||
|
||||
use bizinikiwi_cli_test_utils as common;
|
||||
|
||||
#[tokio::test]
|
||||
async fn running_the_node_works_and_can_be_interrupted() {
|
||||
common::run_with_timeout(Duration::from_secs(60 * 10), async move {
|
||||
async fn run_command_and_kill(signal: Signal) {
|
||||
let base_path = tempdir().expect("could not create a temp dir");
|
||||
let mut cmd = common::KillChildOnDrop(
|
||||
Command::new(cargo_bin("bizinikiwi-node"))
|
||||
.stdout(process::Stdio::piped())
|
||||
.stderr(process::Stdio::piped())
|
||||
.args(&["--dev", "-d"])
|
||||
.arg(base_path.path())
|
||||
.arg("--db=paritydb")
|
||||
.arg("--no-hardware-benchmarks")
|
||||
.spawn()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let stderr = cmd.stderr.take().unwrap();
|
||||
|
||||
let ws_url = common::extract_info_from_output(stderr).0.ws_url;
|
||||
|
||||
common::wait_n_finalized_blocks(3, &ws_url).await;
|
||||
|
||||
cmd.assert_still_running();
|
||||
|
||||
cmd.stop_with_signal(signal);
|
||||
|
||||
// Check if the database was closed gracefully. If it was not,
|
||||
// there may exist a ref cycle that prevents the Client from being dropped properly.
|
||||
//
|
||||
// parity-db only writes the stats file on clean shutdown.
|
||||
let stats_file = base_path.path().join("chains/dev/paritydb/full/stats.txt");
|
||||
assert!(std::path::Path::exists(&stats_file));
|
||||
}
|
||||
|
||||
run_command_and_kill(SIGINT).await;
|
||||
run_command_and_kill(SIGTERM).await;
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn running_two_nodes_with_the_same_ws_port_should_work() {
|
||||
common::run_with_timeout(Duration::from_secs(60 * 10), async move {
|
||||
let mut first_node = common::KillChildOnDrop(common::start_node());
|
||||
let mut second_node = common::KillChildOnDrop(common::start_node());
|
||||
|
||||
let stderr = first_node.stderr.take().unwrap();
|
||||
let ws_url = common::extract_info_from_output(stderr).0.ws_url;
|
||||
|
||||
common::wait_n_finalized_blocks(3, &ws_url).await;
|
||||
|
||||
first_node.assert_still_running();
|
||||
second_node.assert_still_running();
|
||||
|
||||
first_node.stop();
|
||||
second_node.stop();
|
||||
})
|
||||
.await;
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use codec::Decode;
|
||||
use pezframe_system::offchain::{SendSignedTransaction, Signer, SubmitTransaction};
|
||||
use kitchensink_runtime::{Executive, ExistentialDeposit, Indices, Runtime, UncheckedExtrinsic};
|
||||
use pezkuwi_sdk::*;
|
||||
use pezsp_application_crypto::AppCrypto;
|
||||
use pezsp_core::offchain::{testing::TestTransactionPoolExt, TransactionPoolExt};
|
||||
use pezsp_keyring::sr25519::Keyring::Alice;
|
||||
use pezsp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt};
|
||||
use pezsp_runtime::generic;
|
||||
|
||||
pub mod common;
|
||||
use self::common::*;
|
||||
|
||||
#[test]
|
||||
fn should_submit_unsigned_transaction() {
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
let (pool, state) = TestTransactionPoolExt::new();
|
||||
t.register_extension(TransactionPoolExt::new(pool));
|
||||
|
||||
t.execute_with(|| {
|
||||
let signature =
|
||||
pezpallet_im_online::sr25519::AuthoritySignature::try_from(vec![0; 64]).unwrap();
|
||||
let heartbeat_data = pezpallet_im_online::Heartbeat {
|
||||
block_number: 1,
|
||||
session_index: 1,
|
||||
authority_index: 0,
|
||||
validators_len: 0,
|
||||
};
|
||||
|
||||
let call = pezpallet_im_online::Call::heartbeat { heartbeat: heartbeat_data, signature };
|
||||
let xt = generic::UncheckedExtrinsic::new_bare(call.into()).into();
|
||||
SubmitTransaction::<Runtime, pezpallet_im_online::Call<Runtime>>::submit_transaction(xt)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(state.read().transactions.len(), 1)
|
||||
});
|
||||
}
|
||||
|
||||
const PHRASE: &str = "news slush supreme milk chapter athlete soap sausage put clutch what kitten";
|
||||
|
||||
#[test]
|
||||
fn should_submit_signed_transaction() {
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
let (pool, state) = TestTransactionPoolExt::new();
|
||||
t.register_extension(TransactionPoolExt::new(pool));
|
||||
|
||||
let keystore = MemoryKeystore::new();
|
||||
keystore
|
||||
.sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE)))
|
||||
.unwrap();
|
||||
keystore
|
||||
.sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter2", PHRASE)))
|
||||
.unwrap();
|
||||
keystore
|
||||
.sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter3", PHRASE)))
|
||||
.unwrap();
|
||||
t.register_extension(KeystoreExt::new(keystore));
|
||||
|
||||
t.execute_with(|| {
|
||||
let results =
|
||||
Signer::<Runtime, TestAuthorityId>::all_accounts().send_signed_transaction(|_| {
|
||||
pezpallet_balances::Call::transfer_allow_death {
|
||||
dest: Alice.to_account_id().into(),
|
||||
value: Default::default(),
|
||||
}
|
||||
});
|
||||
|
||||
let len = results.len();
|
||||
assert_eq!(len, 3);
|
||||
assert_eq!(results.into_iter().filter_map(|x| x.1.ok()).count(), len);
|
||||
assert_eq!(state.read().transactions.len(), len);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_submit_signed_twice_from_the_same_account() {
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
let (pool, state) = TestTransactionPoolExt::new();
|
||||
t.register_extension(TransactionPoolExt::new(pool));
|
||||
|
||||
let keystore = MemoryKeystore::new();
|
||||
keystore
|
||||
.sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE)))
|
||||
.unwrap();
|
||||
keystore
|
||||
.sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter2", PHRASE)))
|
||||
.unwrap();
|
||||
t.register_extension(KeystoreExt::new(keystore));
|
||||
|
||||
t.execute_with(|| {
|
||||
let result =
|
||||
Signer::<Runtime, TestAuthorityId>::any_account().send_signed_transaction(|_| {
|
||||
pezpallet_balances::Call::transfer_allow_death {
|
||||
dest: Alice.to_account_id().into(),
|
||||
value: Default::default(),
|
||||
}
|
||||
});
|
||||
|
||||
assert!(result.is_some());
|
||||
assert_eq!(state.read().transactions.len(), 1);
|
||||
|
||||
// submit another one from the same account. The nonce should be incremented.
|
||||
let result =
|
||||
Signer::<Runtime, TestAuthorityId>::any_account().send_signed_transaction(|_| {
|
||||
pezpallet_balances::Call::transfer_allow_death {
|
||||
dest: Alice.to_account_id().into(),
|
||||
value: Default::default(),
|
||||
}
|
||||
});
|
||||
|
||||
assert!(result.is_some());
|
||||
assert_eq!(state.read().transactions.len(), 2);
|
||||
|
||||
// now check that the transaction nonces are not equal
|
||||
let s = state.read();
|
||||
fn nonce(tx: UncheckedExtrinsic) -> pezframe_system::CheckNonce<Runtime> {
|
||||
let extra = tx.0.preamble.to_signed().unwrap().2;
|
||||
extra.6
|
||||
}
|
||||
let nonce1 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[0]).unwrap());
|
||||
let nonce2 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[1]).unwrap());
|
||||
assert!(nonce1 != nonce2, "Transactions should have different nonces. Got: {:?}", nonce1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_submit_signed_twice_from_all_accounts() {
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
let (pool, state) = TestTransactionPoolExt::new();
|
||||
t.register_extension(TransactionPoolExt::new(pool));
|
||||
|
||||
let keystore = MemoryKeystore::new();
|
||||
keystore
|
||||
.sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE)))
|
||||
.unwrap();
|
||||
keystore
|
||||
.sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter2", PHRASE)))
|
||||
.unwrap();
|
||||
t.register_extension(KeystoreExt::new(keystore));
|
||||
|
||||
t.execute_with(|| {
|
||||
let results = Signer::<Runtime, TestAuthorityId>::all_accounts()
|
||||
.send_signed_transaction(|_| {
|
||||
pezpallet_balances::Call::transfer_allow_death { dest: Alice.to_account_id().into(), value: Default::default() }
|
||||
});
|
||||
|
||||
let len = results.len();
|
||||
assert_eq!(len, 2);
|
||||
assert_eq!(results.into_iter().filter_map(|x| x.1.ok()).count(), len);
|
||||
assert_eq!(state.read().transactions.len(), 2);
|
||||
|
||||
// submit another one from the same account. The nonce should be incremented.
|
||||
let results = Signer::<Runtime, TestAuthorityId>::all_accounts()
|
||||
.send_signed_transaction(|_| {
|
||||
pezpallet_balances::Call::transfer_allow_death { dest: Alice.to_account_id().into(), value: Default::default() }
|
||||
});
|
||||
|
||||
let len = results.len();
|
||||
assert_eq!(len, 2);
|
||||
assert_eq!(results.into_iter().filter_map(|x| x.1.ok()).count(), len);
|
||||
assert_eq!(state.read().transactions.len(), 4);
|
||||
|
||||
// now check that the transaction nonces are not equal
|
||||
let s = state.read();
|
||||
fn nonce(tx: UncheckedExtrinsic) -> pezframe_system::CheckNonce<Runtime> {
|
||||
let extra = tx.0.preamble.to_signed().unwrap().2;
|
||||
extra.6
|
||||
}
|
||||
let nonce1 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[0]).unwrap());
|
||||
let nonce2 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[1]).unwrap());
|
||||
let nonce3 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[2]).unwrap());
|
||||
let nonce4 = nonce(UncheckedExtrinsic::decode(&mut &*s.transactions[3]).unwrap());
|
||||
assert!(
|
||||
nonce1 != nonce3,
|
||||
"Transactions should have different nonces. Got: 1st tx nonce: {:?}, 2nd nonce: {:?}", nonce1, nonce3
|
||||
);
|
||||
assert!(
|
||||
nonce2 != nonce4,
|
||||
"Transactions should have different nonces. Got: 1st tx nonce: {:?}, 2nd tx nonce: {:?}", nonce2, nonce4
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn submitted_transaction_should_be_valid() {
|
||||
use codec::Encode;
|
||||
use pezsp_runtime::{
|
||||
traits::StaticLookup,
|
||||
transaction_validity::{TransactionSource, TransactionTag},
|
||||
};
|
||||
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
let (pool, state) = TestTransactionPoolExt::new();
|
||||
t.register_extension(TransactionPoolExt::new(pool));
|
||||
|
||||
let keystore = MemoryKeystore::new();
|
||||
keystore
|
||||
.sr25519_generate_new(sr25519::AuthorityId::ID, Some(&format!("{}/hunter1", PHRASE)))
|
||||
.unwrap();
|
||||
t.register_extension(KeystoreExt::new(keystore));
|
||||
|
||||
t.execute_with(|| {
|
||||
let results =
|
||||
Signer::<Runtime, TestAuthorityId>::all_accounts().send_signed_transaction(|_| {
|
||||
pezpallet_balances::Call::transfer_allow_death {
|
||||
dest: Alice.to_account_id().into(),
|
||||
value: Default::default(),
|
||||
}
|
||||
});
|
||||
let len = results.len();
|
||||
assert_eq!(len, 1);
|
||||
assert_eq!(results.into_iter().filter_map(|x| x.1.ok()).count(), len);
|
||||
});
|
||||
|
||||
// check that transaction is valid, but reset environment storage,
|
||||
// since CreateTransaction increments the nonce
|
||||
let tx0 = state.read().transactions[0].clone();
|
||||
let mut t = new_test_ext(compact_code_unwrap());
|
||||
t.execute_with(|| {
|
||||
let source = TransactionSource::External;
|
||||
let extrinsic = UncheckedExtrinsic::decode(&mut &*tx0).unwrap();
|
||||
// add balance to the account
|
||||
let author = extrinsic.0.preamble.clone().to_signed().clone().unwrap().0;
|
||||
let address = Indices::lookup(author).unwrap();
|
||||
let data = pezpallet_balances::AccountData {
|
||||
free: ExistentialDeposit::get() * 10,
|
||||
..Default::default()
|
||||
};
|
||||
let account = pezframe_system::AccountInfo { providers: 1, data, ..Default::default() };
|
||||
<pezframe_system::Account<Runtime>>::insert(&address, account);
|
||||
|
||||
// check validity
|
||||
let res = Executive::validate_transaction(
|
||||
source,
|
||||
extrinsic,
|
||||
pezframe_system::BlockHash::<Runtime>::get(0),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// We ignore res.priority since this number can change based on updates to weights and such.
|
||||
assert_eq!(res.requires, Vec::<TransactionTag>::new());
|
||||
assert_eq!(res.provides, vec![(address, 0).encode()]);
|
||||
assert_eq!(res.longevity, 2047);
|
||||
assert_eq!(res.propagate, true);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use assert_cmd::cargo::cargo_bin;
|
||||
use std::{process, time::Duration};
|
||||
|
||||
use crate::common::KillChildOnDrop;
|
||||
|
||||
use bizinikiwi_cli_test_utils as common;
|
||||
pub mod websocket_server;
|
||||
|
||||
#[tokio::test]
|
||||
async fn telemetry_works() {
|
||||
common::run_with_timeout(Duration::from_secs(60 * 10), async move {
|
||||
let config = websocket_server::Config {
|
||||
capacity: 1,
|
||||
max_frame_size: 1048 * 1024,
|
||||
send_buffer_len: 32,
|
||||
bind_address: "127.0.0.1:0".parse().unwrap(),
|
||||
};
|
||||
let mut server = websocket_server::WsServer::new(config).await.unwrap();
|
||||
|
||||
let addr = server.local_addr().unwrap();
|
||||
|
||||
let server_task = tokio::spawn(async move {
|
||||
loop {
|
||||
use websocket_server::Event;
|
||||
match server.next_event().await {
|
||||
// New connection on the listener.
|
||||
Event::ConnectionOpen { address } => {
|
||||
println!("New connection from {:?}", address);
|
||||
server.accept();
|
||||
},
|
||||
|
||||
// Received a message from a connection.
|
||||
Event::BinaryFrame { message, .. } => {
|
||||
let json: serde_json::Value = serde_json::from_slice(&message).unwrap();
|
||||
let object =
|
||||
json.as_object().unwrap().get("payload").unwrap().as_object().unwrap();
|
||||
if matches!(object.get("best"), Some(serde_json::Value::String(_))) {
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
Event::TextFrame { .. } => {
|
||||
panic!("Got a TextFrame over the socket, this is a bug")
|
||||
},
|
||||
|
||||
// Connection has been closed.
|
||||
Event::ConnectionError { .. } => {},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut bizinikiwi = process::Command::new(cargo_bin("bizinikiwi-node"));
|
||||
|
||||
let mut bizinikiwi = KillChildOnDrop(
|
||||
bizinikiwi
|
||||
.args(&["--dev", "--tmp", "--telemetry-url"])
|
||||
.arg(format!("ws://{} 10", addr))
|
||||
.arg("--no-hardware-benchmarks")
|
||||
.stdout(process::Stdio::piped())
|
||||
.stderr(process::Stdio::piped())
|
||||
.stdin(process::Stdio::null())
|
||||
.spawn()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
server_task.await.expect("server task panicked");
|
||||
|
||||
bizinikiwi.assert_still_running();
|
||||
|
||||
// Stop the process
|
||||
bizinikiwi.stop();
|
||||
})
|
||||
.await;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#![cfg(unix)]
|
||||
|
||||
use assert_cmd::cargo::cargo_bin;
|
||||
use std::{
|
||||
process::{Command, Stdio},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use bizinikiwi_cli_test_utils as common;
|
||||
|
||||
#[allow(dead_code)]
|
||||
// Apparently `#[ignore]` doesn't actually work to disable this one.
|
||||
//#[tokio::test]
|
||||
async fn temp_base_path_works() {
|
||||
common::run_with_timeout(Duration::from_secs(60 * 10), async move {
|
||||
let mut cmd = Command::new(cargo_bin("bizinikiwi-node"));
|
||||
let mut child = common::KillChildOnDrop(
|
||||
cmd.args(&["--dev", "--tmp", "--no-hardware-benchmarks"])
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let mut stderr = child.stderr.take().unwrap();
|
||||
let node_info = common::extract_info_from_output(&mut stderr).0;
|
||||
|
||||
// Let it produce some blocks.
|
||||
common::wait_n_finalized_blocks(3, &node_info.ws_url).await;
|
||||
|
||||
// Ensure the db path exists while the node is running
|
||||
assert!(node_info.db_path.exists());
|
||||
|
||||
child.assert_still_running();
|
||||
|
||||
// Stop the process
|
||||
child.stop();
|
||||
|
||||
if node_info.db_path.exists() {
|
||||
panic!("Database path `{}` wasn't deleted!", node_info.db_path.display());
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use assert_cmd::cargo::cargo_bin;
|
||||
use regex::Regex;
|
||||
use std::process::Command;
|
||||
|
||||
fn expected_regex() -> Regex {
|
||||
Regex::new(r"^bizinikiwi-node (.+)-([a-f\d]+)$").unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_is_full() {
|
||||
let expected = expected_regex();
|
||||
let output = Command::new(cargo_bin("bizinikiwi-node")).args(&["--version"]).output().unwrap();
|
||||
|
||||
assert!(output.status.success(), "command returned with non-success exit code");
|
||||
|
||||
let output = dbg!(String::from_utf8_lossy(&output.stdout).trim().to_owned());
|
||||
let captures = expected.captures(output.as_str()).expect("could not parse version in output");
|
||||
|
||||
assert_eq!(&captures[1], env!("CARGO_PKG_VERSION"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_regex_matches_properly() {
|
||||
let expected = expected_regex();
|
||||
|
||||
let captures = expected.captures("bizinikiwi-node 2.0.0-da487d19d").unwrap();
|
||||
assert_eq!(&captures[1], "2.0.0");
|
||||
assert_eq!(&captures[2], "da487d19d");
|
||||
|
||||
let captures = expected.captures("bizinikiwi-node 2.0.0-alpha.5-da487d19d").unwrap();
|
||||
assert_eq!(&captures[1], "2.0.0-alpha.5");
|
||||
assert_eq!(&captures[2], "da487d19d");
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use core::pin::Pin;
|
||||
use futures::prelude::*;
|
||||
use soketto::handshake::{server::Response, Server};
|
||||
use std::{io, net::SocketAddr};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio_util::compat::{Compat, TokioAsyncReadCompatExt};
|
||||
|
||||
/// Configuration for a [`WsServer`].
|
||||
pub struct Config {
|
||||
/// IP address to try to bind to.
|
||||
pub bind_address: SocketAddr,
|
||||
|
||||
/// Maximum size, in bytes, of a frame sent by the remote.
|
||||
///
|
||||
/// Since the messages are entirely buffered before being returned, a maximum value is
|
||||
/// necessary in order to prevent malicious clients from sending huge frames that would
|
||||
/// occupy a lot of memory.
|
||||
pub max_frame_size: usize,
|
||||
|
||||
/// Number of pending messages to buffer up for sending before the socket is considered
|
||||
/// unresponsive.
|
||||
pub send_buffer_len: usize,
|
||||
|
||||
/// Pre-allocated capacity for the list of connections.
|
||||
pub capacity: usize,
|
||||
}
|
||||
|
||||
/// Identifier for a connection with regard to a [`WsServer`].
|
||||
///
|
||||
/// After a connection has been closed, its [`ConnectionId`] might be reused.
|
||||
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
||||
pub struct ConnectionId(u64);
|
||||
|
||||
/// A WebSocket message.
|
||||
pub enum Message {
|
||||
Text(String),
|
||||
Binary(Vec<u8>),
|
||||
}
|
||||
|
||||
/// WebSockets listening socket and list of open connections.
|
||||
pub struct WsServer {
|
||||
/// Value passed through [`Config::max_frame_size`].
|
||||
max_frame_size: usize,
|
||||
|
||||
/// Endpoint for incoming TCP sockets.
|
||||
listener: TcpListener,
|
||||
|
||||
/// Pending incoming connection to accept. Accepted by calling [`WsServer::accept`].
|
||||
pending_incoming: Option<TcpStream>,
|
||||
|
||||
/// List of TCP connections that are currently negotiating the WebSocket handshake.
|
||||
///
|
||||
/// The output can be an error if the handshake fails.
|
||||
negotiating: stream::FuturesUnordered<
|
||||
Pin<
|
||||
Box<
|
||||
dyn Future<
|
||||
Output = Result<
|
||||
Server<'static, Compat<TcpStream>>,
|
||||
Box<dyn std::error::Error>,
|
||||
>,
|
||||
> + Send,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
|
||||
/// List of streams of incoming messages for all connections.
|
||||
incoming_messages: stream::SelectAll<
|
||||
Pin<Box<dyn Stream<Item = Result<Message, Box<dyn std::error::Error>>> + Send>>,
|
||||
>,
|
||||
|
||||
/// Tasks dedicated to closing sockets that have been rejected.
|
||||
rejected_sockets: stream::FuturesUnordered<Pin<Box<dyn Future<Output = ()> + Send>>>,
|
||||
}
|
||||
|
||||
impl WsServer {
|
||||
/// Try opening a TCP listening socket.
|
||||
///
|
||||
/// Returns an error if the listening socket fails to open.
|
||||
pub async fn new(config: Config) -> Result<Self, io::Error> {
|
||||
let listener = TcpListener::bind(config.bind_address).await?;
|
||||
|
||||
Ok(WsServer {
|
||||
max_frame_size: config.max_frame_size,
|
||||
listener,
|
||||
pending_incoming: None,
|
||||
negotiating: stream::FuturesUnordered::new(),
|
||||
incoming_messages: stream::SelectAll::new(),
|
||||
rejected_sockets: stream::FuturesUnordered::new(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Address of the local TCP listening socket, as provided by the operating system.
|
||||
pub fn local_addr(&self) -> Result<SocketAddr, io::Error> {
|
||||
self.listener.local_addr()
|
||||
}
|
||||
|
||||
/// Accepts the pending connection.
|
||||
///
|
||||
/// Either [`WsServer::accept`] or [`WsServer::reject`] must be called after a
|
||||
/// [`Event::ConnectionOpen`] event is returned.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// Panics if no connection is pending.
|
||||
pub fn accept(&mut self) {
|
||||
let pending_incoming = self.pending_incoming.take().expect("no pending socket");
|
||||
|
||||
self.negotiating.push(Box::pin(async move {
|
||||
let mut server = Server::new(pending_incoming.compat());
|
||||
|
||||
let websocket_key = match server.receive_request().await {
|
||||
Ok(req) => req.key(),
|
||||
Err(err) => return Err(Box::new(err) as Box<_>),
|
||||
};
|
||||
|
||||
match server
|
||||
.send_response(&{ Response::Accept { key: websocket_key, protocol: None } })
|
||||
.await
|
||||
{
|
||||
Ok(()) => {},
|
||||
Err(err) => return Err(Box::new(err) as Box<_>),
|
||||
};
|
||||
|
||||
Ok(server)
|
||||
}));
|
||||
}
|
||||
|
||||
/// Reject the pending connection.
|
||||
///
|
||||
/// Either [`WsServer::accept`] or [`WsServer::reject`] must be called after a
|
||||
/// [`Event::ConnectionOpen`] event is returned.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// Panics if no connection is pending.
|
||||
pub fn reject(&mut self) {
|
||||
let _ = self.pending_incoming.take().expect("no pending socket");
|
||||
}
|
||||
|
||||
/// Returns the next event happening on the server.
|
||||
pub async fn next_event(&mut self) -> Event {
|
||||
loop {
|
||||
futures::select! {
|
||||
// Only try to fetch a new incoming connection if none is pending.
|
||||
socket = {
|
||||
let listener = &self.listener;
|
||||
let has_pending = self.pending_incoming.is_some();
|
||||
async move {
|
||||
if !has_pending {
|
||||
listener.accept().await
|
||||
} else {
|
||||
loop { futures::pending!() }
|
||||
}
|
||||
}
|
||||
}.fuse() => {
|
||||
let (socket, address) = match socket {
|
||||
Ok(s) => s,
|
||||
Err(_) => continue,
|
||||
};
|
||||
debug_assert!(self.pending_incoming.is_none());
|
||||
self.pending_incoming = Some(socket);
|
||||
return Event::ConnectionOpen { address };
|
||||
},
|
||||
|
||||
result = self.negotiating.select_next_some() => {
|
||||
let server = match result {
|
||||
Ok(s) => s,
|
||||
Err(error) => return Event::ConnectionError {
|
||||
error,
|
||||
},
|
||||
};
|
||||
|
||||
let (mut _sender, receiver) = {
|
||||
let mut builder = server.into_builder();
|
||||
builder.set_max_frame_size(self.max_frame_size);
|
||||
builder.set_max_message_size(self.max_frame_size);
|
||||
builder.finish()
|
||||
};
|
||||
|
||||
// Spawn a task dedicated to receiving messages from the socket.
|
||||
self.incoming_messages.push({
|
||||
// Turn `receiver` into a stream of received packets.
|
||||
let socket_packets = stream::unfold((receiver, Vec::new()), move |(mut receiver, mut buf)| async {
|
||||
buf.clear();
|
||||
let ret = match receiver.receive_data(&mut buf).await {
|
||||
Ok(soketto::Data::Text(len)) => String::from_utf8(buf[..len].to_vec())
|
||||
.map(Message::Text)
|
||||
.map_err(|err| Box::new(err) as Box<_>),
|
||||
Ok(soketto::Data::Binary(len)) => Ok(Message::Binary(buf[..len].to_vec())),
|
||||
Err(err) => Err(Box::new(err) as Box<_>),
|
||||
};
|
||||
Some((ret, (receiver, buf)))
|
||||
});
|
||||
|
||||
Box::pin(socket_packets.map(move |msg| (msg)))
|
||||
});
|
||||
},
|
||||
|
||||
result = self.incoming_messages.select_next_some() => {
|
||||
let message = match result {
|
||||
Ok(m) => m,
|
||||
Err(error) => return Event::ConnectionError {
|
||||
error,
|
||||
},
|
||||
};
|
||||
|
||||
match message {
|
||||
Message::Text(message) => {
|
||||
return Event::TextFrame {
|
||||
message,
|
||||
}
|
||||
}
|
||||
Message::Binary(message) => {
|
||||
return Event::BinaryFrame {
|
||||
message,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_ = self.rejected_sockets.select_next_some() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Event that has happened on a [`WsServer`].
|
||||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
/// A new TCP connection has arrived on the listening socket.
|
||||
///
|
||||
/// The connection *must* be accepted or rejected using [`WsServer::accept`] or
|
||||
/// [`WsServer::reject`].
|
||||
/// No other [`Event::ConnectionOpen`] event will be generated until the current pending
|
||||
/// connection has been either accepted or rejected.
|
||||
ConnectionOpen {
|
||||
/// Address of the remote, as provided by the operating system.
|
||||
address: SocketAddr,
|
||||
},
|
||||
|
||||
/// An error has happened on a connection. The connection is now closed and its
|
||||
/// [`ConnectionId`] is now invalid.
|
||||
ConnectionError { error: Box<dyn std::error::Error> },
|
||||
|
||||
/// A text frame has been received on a connection.
|
||||
TextFrame {
|
||||
/// Message sent by the remote. Its content is entirely decided by the client, and
|
||||
/// nothing must be assumed about the validity of this message.
|
||||
message: String,
|
||||
},
|
||||
|
||||
/// A text frame has been received on a connection.
|
||||
BinaryFrame {
|
||||
/// Message sent by the remote. Its content is entirely decided by the client, and
|
||||
/// nothing must be assumed about the validity of this message.
|
||||
message: Vec<u8>,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
[package]
|
||||
name = "pezstaging-node-inspect"
|
||||
version = "0.12.0"
|
||||
authors.workspace = true
|
||||
description = "Bizinikiwi node block inspection tool."
|
||||
edition.workspace = true
|
||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
clap = { features = ["derive"], workspace = true }
|
||||
codec = { workspace = true, default-features = true }
|
||||
pezsc-cli = { workspace = true }
|
||||
pezsc-client-api = { workspace = true, default-features = true }
|
||||
pezsc-service = { workspace = true }
|
||||
pezsp-blockchain = { workspace = true, default-features = true }
|
||||
pezsp-core = { workspace = true, default-features = true }
|
||||
pezsp-io = { workspace = true, default-features = true }
|
||||
pezsp-runtime = { workspace = true, default-features = true }
|
||||
pezsp-statement-store = { workspace = true, default-features = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[features]
|
||||
runtime-benchmarks = [
|
||||
"pezsc-cli/runtime-benchmarks",
|
||||
"pezsc-client-api/runtime-benchmarks",
|
||||
"pezsc-service/runtime-benchmarks",
|
||||
"pezsp-blockchain/runtime-benchmarks",
|
||||
"pezsp-io/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"pezsp-statement-store/runtime-benchmarks",
|
||||
]
|
||||
@@ -0,0 +1,62 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Structs to easily compose inspect sub-command for CLI.
|
||||
|
||||
use pezsc_cli::{ImportParams, SharedParams};
|
||||
|
||||
/// The `inspect` command used to print decoded chain data.
|
||||
#[derive(Debug, clap::Parser)]
|
||||
pub struct InspectCmd {
|
||||
#[allow(missing_docs)]
|
||||
#[clap(subcommand)]
|
||||
pub command: InspectSubCmd,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[clap(flatten)]
|
||||
pub shared_params: SharedParams,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[clap(flatten)]
|
||||
pub import_params: ImportParams,
|
||||
}
|
||||
|
||||
/// A possible inspect sub-commands.
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
pub enum InspectSubCmd {
|
||||
/// Decode block with native version of runtime and print out the details.
|
||||
Block {
|
||||
/// Address of the block to print out.
|
||||
///
|
||||
/// Can be either a block hash (no 0x prefix) or a number to retrieve existing block,
|
||||
/// or a 0x-prefixed bytes hex string, representing SCALE encoding of
|
||||
/// a block.
|
||||
#[arg(value_name = "HASH or NUMBER or BYTES")]
|
||||
input: String,
|
||||
},
|
||||
/// Decode extrinsic with native version of runtime and print out the details.
|
||||
Extrinsic {
|
||||
/// Address of an extrinsic to print out.
|
||||
///
|
||||
/// Can be either a block hash (no 0x prefix) or number and the index, in the form
|
||||
/// of `{block}:{index}` or a 0x-prefixed bytes hex string,
|
||||
/// representing SCALE encoding of an extrinsic.
|
||||
#[arg(value_name = "BLOCK:INDEX or BYTES")]
|
||||
input: String,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Command ran by the CLI
|
||||
|
||||
use crate::{
|
||||
cli::{InspectCmd, InspectSubCmd},
|
||||
Inspector,
|
||||
};
|
||||
use pezsc_cli::{CliConfiguration, ImportParams, Result, SharedParams};
|
||||
use pezsc_service::Configuration;
|
||||
use pezsp_runtime::traits::Block;
|
||||
|
||||
type HostFunctions =
|
||||
(pezsp_io::BizinikiwiHostFunctions, pezsp_statement_store::runtime_api::HostFunctions);
|
||||
|
||||
impl InspectCmd {
|
||||
/// Run the inspect command, passing the inspector.
|
||||
pub fn run<B, RA>(&self, config: Configuration) -> Result<()>
|
||||
where
|
||||
B: Block,
|
||||
RA: Send + Sync + 'static,
|
||||
{
|
||||
let executor = pezsc_service::new_wasm_executor::<HostFunctions>(&config.executor);
|
||||
let client = pezsc_service::new_full_client::<B, RA, _>(&config, None, executor)?;
|
||||
let inspect = Inspector::<B>::new(client);
|
||||
|
||||
match &self.command {
|
||||
InspectSubCmd::Block { input } => {
|
||||
let input = input.parse()?;
|
||||
let res = inspect.block(input).map_err(|e| e.to_string())?;
|
||||
println!("{res}");
|
||||
Ok(())
|
||||
},
|
||||
InspectSubCmd::Extrinsic { input } => {
|
||||
let input = input.parse()?;
|
||||
let res = inspect.extrinsic(input).map_err(|e| e.to_string())?;
|
||||
println!("{res}");
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CliConfiguration for InspectCmd {
|
||||
fn shared_params(&self) -> &SharedParams {
|
||||
&self.shared_params
|
||||
}
|
||||
|
||||
fn import_params(&self) -> Option<&ImportParams> {
|
||||
Some(&self.import_params)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
//
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! A CLI extension for bizinikiwi node, adding sub-command to pretty print debug info
|
||||
//! about blocks and extrinsics.
|
||||
//!
|
||||
//! The blocks and extrinsics can either be retrieved from the database (on-chain),
|
||||
//! or a raw SCALE-encoding can be provided.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
pub mod cli;
|
||||
pub mod command;
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use pezsc_client_api::BlockBackend;
|
||||
use pezsp_blockchain::HeaderBackend;
|
||||
use pezsp_core::hexdisplay::HexDisplay;
|
||||
use pezsp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{Block, Hash, HashingFor, NumberFor},
|
||||
};
|
||||
use std::{fmt, fmt::Debug, marker::PhantomData, str::FromStr};
|
||||
|
||||
/// A helper type for a generic block input.
|
||||
pub type BlockAddressFor<TBlock> =
|
||||
BlockAddress<<HashingFor<TBlock> as Hash>::Output, NumberFor<TBlock>>;
|
||||
|
||||
/// A Pretty formatter implementation.
|
||||
pub trait PrettyPrinter<TBlock: Block> {
|
||||
/// Nicely format block.
|
||||
fn fmt_block(&self, fmt: &mut fmt::Formatter, block: &TBlock) -> fmt::Result;
|
||||
/// Nicely format extrinsic.
|
||||
fn fmt_extrinsic(&self, fmt: &mut fmt::Formatter, extrinsic: &TBlock::Extrinsic)
|
||||
-> fmt::Result;
|
||||
}
|
||||
|
||||
/// Default dummy debug printer.
|
||||
#[derive(Default)]
|
||||
pub struct DebugPrinter;
|
||||
impl<TBlock: Block> PrettyPrinter<TBlock> for DebugPrinter {
|
||||
fn fmt_block(&self, fmt: &mut fmt::Formatter, block: &TBlock) -> fmt::Result {
|
||||
writeln!(fmt, "Header:")?;
|
||||
writeln!(fmt, "{:?}", block.header())?;
|
||||
writeln!(fmt, "Block bytes: {:?}", HexDisplay::from(&block.encode()))?;
|
||||
writeln!(fmt, "Extrinsics ({})", block.extrinsics().len())?;
|
||||
for (idx, ex) in block.extrinsics().iter().enumerate() {
|
||||
writeln!(fmt, "- {}:", idx)?;
|
||||
<DebugPrinter as PrettyPrinter<TBlock>>::fmt_extrinsic(self, fmt, ex)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fmt_extrinsic(
|
||||
&self,
|
||||
fmt: &mut fmt::Formatter,
|
||||
extrinsic: &TBlock::Extrinsic,
|
||||
) -> fmt::Result {
|
||||
writeln!(fmt, " {:#?}", extrinsic)?;
|
||||
writeln!(fmt, " Bytes: {:?}", HexDisplay::from(&extrinsic.encode()))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Aggregated error for `Inspector` operations.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
/// Could not decode Block or Extrinsic.
|
||||
#[error(transparent)]
|
||||
Codec(#[from] codec::Error),
|
||||
/// Error accessing blockchain DB.
|
||||
#[error(transparent)]
|
||||
Blockchain(#[from] pezsp_blockchain::Error),
|
||||
/// Given block has not been found.
|
||||
#[error("{0}")]
|
||||
NotFound(String),
|
||||
}
|
||||
|
||||
/// A helper trait to access block headers and bodies.
|
||||
pub trait ChainAccess<TBlock: Block>: HeaderBackend<TBlock> + BlockBackend<TBlock> {}
|
||||
|
||||
impl<T, TBlock> ChainAccess<TBlock> for T
|
||||
where
|
||||
TBlock: Block,
|
||||
T: pezsp_blockchain::HeaderBackend<TBlock> + pezsc_client_api::BlockBackend<TBlock>,
|
||||
{
|
||||
}
|
||||
|
||||
/// Blockchain inspector.
|
||||
pub struct Inspector<TBlock: Block, TPrinter: PrettyPrinter<TBlock> = DebugPrinter> {
|
||||
printer: TPrinter,
|
||||
chain: Box<dyn ChainAccess<TBlock>>,
|
||||
_block: PhantomData<TBlock>,
|
||||
}
|
||||
|
||||
impl<TBlock: Block, TPrinter: PrettyPrinter<TBlock>> Inspector<TBlock, TPrinter> {
|
||||
/// Create new instance of the inspector with default printer.
|
||||
pub fn new(chain: impl ChainAccess<TBlock> + 'static) -> Self
|
||||
where
|
||||
TPrinter: Default,
|
||||
{
|
||||
Self::with_printer(chain, Default::default())
|
||||
}
|
||||
|
||||
/// Customize pretty-printing of the data.
|
||||
pub fn with_printer(chain: impl ChainAccess<TBlock> + 'static, printer: TPrinter) -> Self {
|
||||
Inspector { chain: Box::new(chain) as _, printer, _block: Default::default() }
|
||||
}
|
||||
|
||||
/// Get a pretty-printed block.
|
||||
pub fn block(&self, input: BlockAddressFor<TBlock>) -> Result<String, Error> {
|
||||
struct BlockPrinter<'a, A, B>(A, &'a B);
|
||||
impl<'a, A: Block, B: PrettyPrinter<A>> fmt::Display for BlockPrinter<'a, A, B> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.1.fmt_block(fmt, &self.0)
|
||||
}
|
||||
}
|
||||
|
||||
let block = self.get_block(input)?;
|
||||
Ok(format!("{}", BlockPrinter(block, &self.printer)))
|
||||
}
|
||||
|
||||
fn get_block(&self, input: BlockAddressFor<TBlock>) -> Result<TBlock, Error> {
|
||||
Ok(match input {
|
||||
BlockAddress::Bytes(bytes) => TBlock::decode(&mut &*bytes)?,
|
||||
BlockAddress::Number(number) => {
|
||||
let id = BlockId::number(number);
|
||||
let hash = self.chain.expect_block_hash_from_id(&id)?;
|
||||
let not_found = format!("Could not find block {:?}", id);
|
||||
let body = self
|
||||
.chain
|
||||
.block_body(hash)?
|
||||
.ok_or_else(|| Error::NotFound(not_found.clone()))?;
|
||||
let header =
|
||||
self.chain.header(hash)?.ok_or_else(|| Error::NotFound(not_found.clone()))?;
|
||||
TBlock::new(header, body)
|
||||
},
|
||||
BlockAddress::Hash(hash) => {
|
||||
let not_found = format!("Could not find block {:?}", BlockId::<TBlock>::Hash(hash));
|
||||
let body = self
|
||||
.chain
|
||||
.block_body(hash)?
|
||||
.ok_or_else(|| Error::NotFound(not_found.clone()))?;
|
||||
let header =
|
||||
self.chain.header(hash)?.ok_or_else(|| Error::NotFound(not_found.clone()))?;
|
||||
TBlock::new(header, body)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a pretty-printed extrinsic.
|
||||
pub fn extrinsic(
|
||||
&self,
|
||||
input: ExtrinsicAddress<<HashingFor<TBlock> as Hash>::Output, NumberFor<TBlock>>,
|
||||
) -> Result<String, Error> {
|
||||
struct ExtrinsicPrinter<'a, A: Block, B>(A::Extrinsic, &'a B);
|
||||
impl<'a, A: Block, B: PrettyPrinter<A>> fmt::Display for ExtrinsicPrinter<'a, A, B> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.1.fmt_extrinsic(fmt, &self.0)
|
||||
}
|
||||
}
|
||||
|
||||
let ext = match input {
|
||||
ExtrinsicAddress::Block(block, index) => {
|
||||
let block = self.get_block(block)?;
|
||||
block.extrinsics().get(index).cloned().ok_or_else(|| {
|
||||
Error::NotFound(format!(
|
||||
"Could not find extrinsic {} in block {:?}",
|
||||
index, block
|
||||
))
|
||||
})?
|
||||
},
|
||||
ExtrinsicAddress::Bytes(bytes) => TBlock::Extrinsic::decode(&mut &*bytes)?,
|
||||
};
|
||||
|
||||
Ok(format!("{}", ExtrinsicPrinter(ext, &self.printer)))
|
||||
}
|
||||
}
|
||||
|
||||
/// A block to retrieve.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum BlockAddress<Hash, Number> {
|
||||
/// Get block by hash.
|
||||
Hash(Hash),
|
||||
/// Get block by number.
|
||||
Number(Number),
|
||||
/// Raw SCALE-encoded bytes.
|
||||
Bytes(Vec<u8>),
|
||||
}
|
||||
|
||||
impl<Hash: FromStr, Number: FromStr> FromStr for BlockAddress<Hash, Number> {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
// try to parse hash first
|
||||
if let Ok(hash) = s.parse() {
|
||||
return Ok(Self::Hash(hash));
|
||||
}
|
||||
|
||||
// then number
|
||||
if let Ok(number) = s.parse() {
|
||||
return Ok(Self::Number(number));
|
||||
}
|
||||
|
||||
// then assume it's bytes (hex-encoded)
|
||||
pezsp_core::bytes::from_hex(s).map(Self::Bytes).map_err(|e| {
|
||||
format!(
|
||||
"Given string does not look like hash or number. It could not be parsed as bytes either: {}",
|
||||
e
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An extrinsic address to decode and print out.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ExtrinsicAddress<Hash, Number> {
|
||||
/// Extrinsic as part of existing block.
|
||||
Block(BlockAddress<Hash, Number>, usize),
|
||||
/// Raw SCALE-encoded extrinsic bytes.
|
||||
Bytes(Vec<u8>),
|
||||
}
|
||||
|
||||
impl<Hash: FromStr + Debug, Number: FromStr + Debug> FromStr for ExtrinsicAddress<Hash, Number> {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
// first try raw bytes
|
||||
if let Ok(bytes) = pezsp_core::bytes::from_hex(s).map(Self::Bytes) {
|
||||
return Ok(bytes);
|
||||
}
|
||||
|
||||
// split by a bunch of different characters
|
||||
let mut it = s.split(|c| c == '.' || c == ':' || c == ' ');
|
||||
let block = it
|
||||
.next()
|
||||
.expect("First element of split iterator is never empty; qed")
|
||||
.parse()?;
|
||||
|
||||
let index = it
|
||||
.next()
|
||||
.ok_or("Extrinsic index missing: example \"5:0\"")?
|
||||
.parse()
|
||||
.map_err(|e| format!("Invalid index format: {}", e))?;
|
||||
|
||||
Ok(Self::Block(block, index))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pezsp_core::hash::H160 as Hash;
|
||||
|
||||
#[test]
|
||||
fn should_parse_block_strings() {
|
||||
type BlockAddress = super::BlockAddress<Hash, u64>;
|
||||
|
||||
let b0 = BlockAddress::from_str("3BfC20f0B9aFcAcE800D73D2191166FF16540258");
|
||||
let b1 = BlockAddress::from_str("1234");
|
||||
let b2 = BlockAddress::from_str("0");
|
||||
let b3 = BlockAddress::from_str("0x0012345f");
|
||||
|
||||
assert_eq!(
|
||||
b0,
|
||||
Ok(BlockAddress::Hash("3BfC20f0B9aFcAcE800D73D2191166FF16540258".parse().unwrap()))
|
||||
);
|
||||
assert_eq!(b1, Ok(BlockAddress::Number(1234)));
|
||||
assert_eq!(b2, Ok(BlockAddress::Number(0)));
|
||||
assert_eq!(b3, Ok(BlockAddress::Bytes(vec![0, 0x12, 0x34, 0x5f])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_parse_extrinsic_address() {
|
||||
type BlockAddress = super::BlockAddress<Hash, u64>;
|
||||
type ExtrinsicAddress = super::ExtrinsicAddress<Hash, u64>;
|
||||
|
||||
let e0 = ExtrinsicAddress::from_str("1234");
|
||||
let b0 = ExtrinsicAddress::from_str("3BfC20f0B9aFcAcE800D73D2191166FF16540258:5");
|
||||
let b1 = ExtrinsicAddress::from_str("1234:0");
|
||||
let b2 = ExtrinsicAddress::from_str("0 0");
|
||||
let b3 = ExtrinsicAddress::from_str("0x0012345f");
|
||||
|
||||
assert_eq!(e0, Ok(ExtrinsicAddress::Bytes(vec![0x12, 0x34])));
|
||||
assert_eq!(
|
||||
b0,
|
||||
Ok(ExtrinsicAddress::Block(
|
||||
BlockAddress::Hash("3BfC20f0B9aFcAcE800D73D2191166FF16540258".parse().unwrap()),
|
||||
5
|
||||
))
|
||||
);
|
||||
assert_eq!(b1, Ok(ExtrinsicAddress::Block(BlockAddress::Number(1234), 0)));
|
||||
assert_eq!(b2, Ok(ExtrinsicAddress::Bytes(vec![0, 0])));
|
||||
assert_eq!(b3, Ok(ExtrinsicAddress::Bytes(vec![0, 0x12, 0x34, 0x5f])));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "node-primitives"
|
||||
version = "2.0.0"
|
||||
authors.workspace = true
|
||||
description = "Bizinikiwi node low-level primitives."
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
pezsp-core = { workspace = true }
|
||||
pezsp-runtime = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["pezsp-core/std", "pezsp-runtime/std"]
|
||||
runtime-benchmarks = ["pezsp-runtime/runtime-benchmarks"]
|
||||
@@ -0,0 +1,66 @@
|
||||
// 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.
|
||||
|
||||
//! Low-level types used throughout the Bizinikiwi code.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use pezsp_runtime::{
|
||||
generic,
|
||||
traits::{BlakeTwo256, IdentifyAccount, Verify},
|
||||
MultiSignature, OpaqueExtrinsic,
|
||||
};
|
||||
|
||||
/// An index to a block.
|
||||
pub type BlockNumber = u32;
|
||||
|
||||
/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
|
||||
pub type Signature = MultiSignature;
|
||||
|
||||
/// Some way of identifying an account on the chain. We intentionally make it equivalent
|
||||
/// to the public key of our transaction signing scheme.
|
||||
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
|
||||
|
||||
/// The type for looking up accounts. We don't expect more than 4 billion of them.
|
||||
pub type AccountIndex = u32;
|
||||
|
||||
/// Balance of an account.
|
||||
pub type Balance = u128;
|
||||
|
||||
/// Type used for expressing timestamp.
|
||||
pub type Moment = u64;
|
||||
|
||||
/// Index of a transaction in the chain.
|
||||
pub type Nonce = u32;
|
||||
|
||||
/// A hash of some data used by the chain.
|
||||
pub type Hash = pezsp_core::H256;
|
||||
|
||||
/// A timestamp: milliseconds since the unix epoch.
|
||||
/// `u64` is enough to represent a duration of half a billion years, when the
|
||||
/// time scale is milliseconds.
|
||||
pub type Timestamp = u64;
|
||||
|
||||
/// Digest item type.
|
||||
pub type DigestItem = generic::DigestItem;
|
||||
/// Header type.
|
||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||
/// Block type.
|
||||
pub type Block = generic::Block<Header, OpaqueExtrinsic>;
|
||||
/// Block ID.
|
||||
pub type BlockId = generic::BlockId<Block>;
|
||||
@@ -0,0 +1,75 @@
|
||||
[package]
|
||||
name = "node-rpc"
|
||||
version = "3.0.0-dev"
|
||||
authors.workspace = true
|
||||
description = "Bizinikiwi node rpc methods."
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
jsonrpsee = { features = ["server"], workspace = true }
|
||||
mmr-rpc = { workspace = true, default-features = true }
|
||||
node-primitives = { workspace = true, default-features = true }
|
||||
pezpallet-transaction-payment-rpc = { workspace = true, default-features = true }
|
||||
pezsc-chain-spec = { workspace = true, default-features = true }
|
||||
pezsc-client-api = { workspace = true, default-features = true }
|
||||
pezsc-consensus-babe = { workspace = true, default-features = true }
|
||||
pezsc-consensus-babe-rpc = { workspace = true, default-features = true }
|
||||
pezsc-consensus-beefy = { workspace = true, default-features = true }
|
||||
pezsc-consensus-beefy-rpc = { workspace = true, default-features = true }
|
||||
pezsc-consensus-grandpa = { workspace = true, default-features = true }
|
||||
pezsc-consensus-grandpa-rpc = { workspace = true, default-features = true }
|
||||
pezsc-mixnet = { workspace = true, default-features = true }
|
||||
pezsc-rpc = { workspace = true, default-features = true }
|
||||
pezsc-sync-state-rpc = { workspace = true, default-features = true }
|
||||
pezsc-transaction-pool-api = { workspace = true, default-features = true }
|
||||
pezsp-api = { workspace = true, default-features = true }
|
||||
pezsp-application-crypto = { workspace = true, default-features = true }
|
||||
pezsp-block-builder = { workspace = true, default-features = true }
|
||||
pezsp-blockchain = { workspace = true, default-features = true }
|
||||
pezsp-consensus = { workspace = true, default-features = true }
|
||||
pezsp-consensus-babe = { workspace = true, default-features = true }
|
||||
pezsp-consensus-beefy = { workspace = true, default-features = true }
|
||||
pezsp-keystore = { workspace = true, default-features = true }
|
||||
pezsp-runtime = { workspace = true, default-features = true }
|
||||
pezsp-statement-store = { workspace = true, default-features = true }
|
||||
bizinikiwi-frame-rpc-system = { workspace = true, default-features = true }
|
||||
bizinikiwi-state-trie-migration-rpc = { workspace = true, default-features = true }
|
||||
|
||||
[features]
|
||||
runtime-benchmarks = [
|
||||
"mmr-rpc/runtime-benchmarks",
|
||||
"node-primitives/runtime-benchmarks",
|
||||
"pezpallet-transaction-payment-rpc/runtime-benchmarks",
|
||||
"pezsc-chain-spec/runtime-benchmarks",
|
||||
"pezsc-client-api/runtime-benchmarks",
|
||||
"pezsc-consensus-babe-rpc/runtime-benchmarks",
|
||||
"pezsc-consensus-babe/runtime-benchmarks",
|
||||
"pezsc-consensus-beefy-rpc/runtime-benchmarks",
|
||||
"pezsc-consensus-beefy/runtime-benchmarks",
|
||||
"pezsc-consensus-grandpa-rpc/runtime-benchmarks",
|
||||
"pezsc-consensus-grandpa/runtime-benchmarks",
|
||||
"pezsc-mixnet/runtime-benchmarks",
|
||||
"pezsc-rpc/runtime-benchmarks",
|
||||
"pezsc-sync-state-rpc/runtime-benchmarks",
|
||||
"pezsc-transaction-pool-api/runtime-benchmarks",
|
||||
"pezsp-api/runtime-benchmarks",
|
||||
"pezsp-block-builder/runtime-benchmarks",
|
||||
"pezsp-blockchain/runtime-benchmarks",
|
||||
"pezsp-consensus-babe/runtime-benchmarks",
|
||||
"pezsp-consensus-beefy/runtime-benchmarks",
|
||||
"pezsp-consensus/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"pezsp-statement-store/runtime-benchmarks",
|
||||
"bizinikiwi-frame-rpc-system/runtime-benchmarks",
|
||||
"bizinikiwi-state-trie-migration-rpc/runtime-benchmarks",
|
||||
]
|
||||
@@ -0,0 +1,227 @@
|
||||
// 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 collection of node-specific RPC methods.
|
||||
//!
|
||||
//! Since `bizinikiwi` core functionality makes no assumptions
|
||||
//! about the modules used inside the runtime, so do
|
||||
//! RPC methods defined in `sc-rpc` crate.
|
||||
//! It means that `client/rpc` can't have any methods that
|
||||
//! need some strong assumptions about the particular runtime.
|
||||
//!
|
||||
//! The RPCs available in this crate however can make some assumptions
|
||||
//! about how the runtime is constructed and what FRAME pallets
|
||||
//! are part of it. Therefore all node-runtime-specific RPCs can
|
||||
//! be placed here or imported from corresponding FRAME RPC definitions.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![warn(unused_crate_dependencies)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use jsonrpsee::RpcModule;
|
||||
use node_primitives::{AccountId, Balance, Block, BlockNumber, Hash, Nonce};
|
||||
use pezsc_client_api::AuxStore;
|
||||
use pezsc_consensus_babe::BabeWorkerHandle;
|
||||
use pezsc_consensus_beefy::communication::notification::{
|
||||
BeefyBestBlockStream, BeefyVersionedFinalityProofStream,
|
||||
};
|
||||
use pezsc_consensus_grandpa::{
|
||||
FinalityProofProvider, GrandpaJustificationStream, SharedAuthoritySet, SharedVoterState,
|
||||
};
|
||||
pub use pezsc_rpc::SubscriptionTaskExecutor;
|
||||
use pezsc_transaction_pool_api::TransactionPool;
|
||||
use pezsp_api::ProvideRuntimeApi;
|
||||
use pezsp_application_crypto::RuntimeAppPublic;
|
||||
use pezsp_block_builder::BlockBuilder;
|
||||
use pezsp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata};
|
||||
use pezsp_consensus::SelectChain;
|
||||
use pezsp_consensus_babe::BabeApi;
|
||||
use pezsp_consensus_beefy::AuthorityIdBound;
|
||||
use pezsp_keystore::KeystorePtr;
|
||||
|
||||
/// Extra dependencies for BABE.
|
||||
pub struct BabeDeps {
|
||||
/// A handle to the BABE worker for issuing requests.
|
||||
pub babe_worker_handle: BabeWorkerHandle<Block>,
|
||||
/// The keystore that manages the keys of the node.
|
||||
pub keystore: KeystorePtr,
|
||||
}
|
||||
|
||||
/// Extra dependencies for GRANDPA
|
||||
pub struct GrandpaDeps<B> {
|
||||
/// Voting round info.
|
||||
pub shared_voter_state: SharedVoterState,
|
||||
/// Authority set info.
|
||||
pub shared_authority_set: SharedAuthoritySet<Hash, BlockNumber>,
|
||||
/// Receives notifications about justification events from Grandpa.
|
||||
pub justification_stream: GrandpaJustificationStream<Block>,
|
||||
/// Executor to drive the subscription manager in the Grandpa RPC handler.
|
||||
pub subscription_executor: SubscriptionTaskExecutor,
|
||||
/// Finality proof provider.
|
||||
pub finality_provider: Arc<FinalityProofProvider<B, Block>>,
|
||||
}
|
||||
|
||||
/// Dependencies for BEEFY
|
||||
pub struct BeefyDeps<AuthorityId: AuthorityIdBound> {
|
||||
/// Receives notifications about finality proof events from BEEFY.
|
||||
pub beefy_finality_proof_stream: BeefyVersionedFinalityProofStream<Block, AuthorityId>,
|
||||
/// Receives notifications about best block events from BEEFY.
|
||||
pub beefy_best_block_stream: BeefyBestBlockStream<Block>,
|
||||
/// Executor to drive the subscription manager in the BEEFY RPC handler.
|
||||
pub subscription_executor: SubscriptionTaskExecutor,
|
||||
}
|
||||
|
||||
/// Full client dependencies.
|
||||
pub struct FullDeps<C, P, SC, B, AuthorityId: AuthorityIdBound> {
|
||||
/// The client instance to use.
|
||||
pub client: Arc<C>,
|
||||
/// Transaction pool instance.
|
||||
pub pool: Arc<P>,
|
||||
/// The SelectChain Strategy
|
||||
pub select_chain: SC,
|
||||
/// A copy of the chain spec.
|
||||
pub chain_spec: Box<dyn pezsc_chain_spec::ChainSpec>,
|
||||
/// BABE specific dependencies.
|
||||
pub babe: BabeDeps,
|
||||
/// GRANDPA specific dependencies.
|
||||
pub grandpa: GrandpaDeps<B>,
|
||||
/// BEEFY specific dependencies.
|
||||
pub beefy: BeefyDeps<AuthorityId>,
|
||||
/// Shared statement store reference.
|
||||
pub statement_store: Arc<dyn pezsp_statement_store::StatementStore>,
|
||||
/// The backend used by the node.
|
||||
pub backend: Arc<B>,
|
||||
/// Mixnet API.
|
||||
pub mixnet_api: Option<pezsc_mixnet::Api>,
|
||||
}
|
||||
|
||||
/// Instantiate all Full RPC extensions.
|
||||
pub fn create_full<C, P, SC, B, AuthorityId>(
|
||||
FullDeps {
|
||||
client,
|
||||
pool,
|
||||
select_chain,
|
||||
chain_spec,
|
||||
babe,
|
||||
grandpa,
|
||||
beefy,
|
||||
statement_store,
|
||||
backend,
|
||||
mixnet_api,
|
||||
}: FullDeps<C, P, SC, B, AuthorityId>,
|
||||
) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
|
||||
where
|
||||
C: ProvideRuntimeApi<Block>
|
||||
+ pezsc_client_api::BlockBackend<Block>
|
||||
+ HeaderBackend<Block>
|
||||
+ AuxStore
|
||||
+ HeaderMetadata<Block, Error = BlockChainError>
|
||||
+ Sync
|
||||
+ Send
|
||||
+ 'static,
|
||||
C::Api: bizinikiwi_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>,
|
||||
C::Api: mmr_rpc::MmrRuntimeApi<Block, <Block as pezsp_runtime::traits::Block>::Hash, BlockNumber>,
|
||||
C::Api: pezpallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>,
|
||||
C::Api: BabeApi<Block>,
|
||||
C::Api: BlockBuilder<Block>,
|
||||
P: TransactionPool + 'static,
|
||||
SC: SelectChain<Block> + 'static,
|
||||
B: pezsc_client_api::Backend<Block> + Send + Sync + 'static,
|
||||
B::State: pezsc_client_api::backend::StateBackend<pezsp_runtime::traits::HashingFor<Block>>,
|
||||
AuthorityId: AuthorityIdBound,
|
||||
<AuthorityId as RuntimeAppPublic>::Signature: Send + Sync,
|
||||
{
|
||||
use mmr_rpc::{Mmr, MmrApiServer};
|
||||
use pezpallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer};
|
||||
use pezsc_consensus_babe_rpc::{Babe, BabeApiServer};
|
||||
use pezsc_consensus_beefy_rpc::{Beefy, BeefyApiServer};
|
||||
use pezsc_consensus_grandpa_rpc::{Grandpa, GrandpaApiServer};
|
||||
use pezsc_rpc::{
|
||||
dev::{Dev, DevApiServer},
|
||||
mixnet::MixnetApiServer,
|
||||
statement::StatementApiServer,
|
||||
};
|
||||
use pezsc_sync_state_rpc::{SyncState, SyncStateApiServer};
|
||||
use bizinikiwi_frame_rpc_system::{System, SystemApiServer};
|
||||
use bizinikiwi_state_trie_migration_rpc::{StateMigration, StateMigrationApiServer};
|
||||
|
||||
let mut io = RpcModule::new(());
|
||||
|
||||
let BabeDeps { keystore, babe_worker_handle } = babe;
|
||||
let GrandpaDeps {
|
||||
shared_voter_state,
|
||||
shared_authority_set,
|
||||
justification_stream,
|
||||
subscription_executor,
|
||||
finality_provider,
|
||||
} = grandpa;
|
||||
|
||||
io.merge(System::new(client.clone(), pool).into_rpc())?;
|
||||
// Making synchronous calls in light client freezes the browser currently,
|
||||
// more context: https://github.com/pezkuwichain/kurdistan-sdk/issues/53
|
||||
// These RPCs should use an asynchronous caller instead.
|
||||
io.merge(
|
||||
Mmr::new(
|
||||
client.clone(),
|
||||
backend
|
||||
.offchain_storage()
|
||||
.ok_or_else(|| "Backend doesn't provide an offchain storage")?,
|
||||
)
|
||||
.into_rpc(),
|
||||
)?;
|
||||
io.merge(TransactionPayment::new(client.clone()).into_rpc())?;
|
||||
io.merge(
|
||||
Babe::new(client.clone(), babe_worker_handle.clone(), keystore, select_chain).into_rpc(),
|
||||
)?;
|
||||
io.merge(
|
||||
Grandpa::new(
|
||||
subscription_executor,
|
||||
shared_authority_set.clone(),
|
||||
shared_voter_state,
|
||||
justification_stream,
|
||||
finality_provider,
|
||||
)
|
||||
.into_rpc(),
|
||||
)?;
|
||||
|
||||
io.merge(
|
||||
SyncState::new(chain_spec, client.clone(), shared_authority_set, babe_worker_handle)?
|
||||
.into_rpc(),
|
||||
)?;
|
||||
|
||||
io.merge(StateMigration::new(client.clone(), backend).into_rpc())?;
|
||||
io.merge(Dev::new(client).into_rpc())?;
|
||||
let statement_store = pezsc_rpc::statement::StatementStore::new(statement_store).into_rpc();
|
||||
io.merge(statement_store)?;
|
||||
|
||||
if let Some(mixnet_api) = mixnet_api {
|
||||
let mixnet = pezsc_rpc::mixnet::Mixnet::new(mixnet_api).into_rpc();
|
||||
io.merge(mixnet)?;
|
||||
}
|
||||
|
||||
io.merge(
|
||||
Beefy::<Block, AuthorityId>::new(
|
||||
beefy.beefy_finality_proof_stream,
|
||||
beefy.beefy_best_block_stream,
|
||||
beefy.subscription_executor,
|
||||
)?
|
||||
.into_rpc(),
|
||||
)?;
|
||||
|
||||
Ok(io)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
[package]
|
||||
name = "kitchensink-runtime"
|
||||
version = "3.0.0-dev"
|
||||
authors.workspace = true
|
||||
description = "Bizinikiwi node kitchensink runtime."
|
||||
edition.workspace = true
|
||||
build = "build.rs"
|
||||
license = "Apache-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
# third-party dependencies
|
||||
array-bytes = { workspace = true }
|
||||
codec = { features = ["derive", "max-encoded-len"], workspace = true }
|
||||
log = { workspace = true }
|
||||
rand = { workspace = true, optional = true }
|
||||
rand_pcg = { workspace = true, optional = true }
|
||||
scale-info = { features = ["derive", "serde"], workspace = true }
|
||||
serde_json = { features = ["alloc", "arbitrary_precision"], workspace = true }
|
||||
pezsp-debug-derive = { workspace = true, features = ["force-debug"] }
|
||||
static_assertions = { workspace = true, default-features = true }
|
||||
|
||||
# pezpallet-asset-conversion: turn on "num-traits" feature
|
||||
primitive-types = { features = [
|
||||
"codec",
|
||||
"num-traits",
|
||||
"scale-info",
|
||||
], workspace = true }
|
||||
|
||||
pezkuwi-sdk = { features = ["runtime-full", "tuples-96"], workspace = true }
|
||||
|
||||
# shared code between runtime and node
|
||||
node-primitives = { workspace = true }
|
||||
|
||||
# Example pallets that are not published:
|
||||
pezpallet-example-mbm = { workspace = true }
|
||||
pezpallet-example-tasks = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
bizinikiwi-wasm-builder = { optional = true, workspace = true, default-features = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
with-tracing = ["pezkuwi-sdk/with-tracing"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"log/std",
|
||||
"node-primitives/std",
|
||||
"pezpallet-example-mbm/std",
|
||||
"pezpallet-example-tasks/std",
|
||||
"pezkuwi-sdk/std",
|
||||
"primitive-types/std",
|
||||
"rand?/std",
|
||||
"scale-info/std",
|
||||
"serde_json/std",
|
||||
"pezsp-debug-derive/std",
|
||||
"bizinikiwi-wasm-builder",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"node-primitives/runtime-benchmarks",
|
||||
"pezpallet-example-mbm/runtime-benchmarks",
|
||||
"pezpallet-example-tasks/runtime-benchmarks",
|
||||
"pezkuwi-sdk/runtime-benchmarks",
|
||||
"rand",
|
||||
"rand_pcg",
|
||||
"bizinikiwi-wasm-builder?/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"pezpallet-example-mbm/try-runtime",
|
||||
"pezpallet-example-tasks/try-runtime",
|
||||
"pezkuwi-sdk/try-runtime",
|
||||
]
|
||||
experimental = ["pezpallet-example-tasks/experimental"]
|
||||
metadata-hash = ["bizinikiwi-wasm-builder/metadata-hash"]
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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.
|
||||
|
||||
#[cfg(all(feature = "std", not(feature = "metadata-hash")))]
|
||||
fn main() {
|
||||
bizinikiwi_wasm_builder::WasmBuilder::build_using_defaults()
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", feature = "metadata-hash"))]
|
||||
fn main() {
|
||||
bizinikiwi_wasm_builder::WasmBuilder::init_with_defaults()
|
||||
.enable_metadata_hash("Test", 14)
|
||||
.build()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn main() {}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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.
|
||||
|
||||
//! Runtime API definition for assets.
|
||||
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use codec::Codec;
|
||||
|
||||
pezsp_api::decl_runtime_apis! {
|
||||
pub trait AssetsApi<AccountId, AssetBalance, AssetId>
|
||||
where
|
||||
AccountId: Codec,
|
||||
AssetBalance: Codec,
|
||||
AssetId: Codec,
|
||||
{
|
||||
/// Returns the list of `AssetId`s and corresponding balance that an `AccountId` has.
|
||||
fn account_balances(account: AccountId) -> Vec<(AssetId, AssetBalance)>;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// 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 set of constant values used in bizinikiwi runtime.
|
||||
|
||||
/// Money matters.
|
||||
pub mod currency {
|
||||
use node_primitives::Balance;
|
||||
|
||||
pub const MILLICENTS: Balance = 1_000_000_000;
|
||||
pub const CENTS: Balance = 1_000 * MILLICENTS; // assume this is worth about a cent.
|
||||
pub const DOLLARS: Balance = 100 * CENTS;
|
||||
|
||||
pub const fn deposit(items: u32, bytes: u32) -> Balance {
|
||||
items as Balance * 15 * CENTS + (bytes as Balance) * 6 * CENTS
|
||||
}
|
||||
}
|
||||
|
||||
/// Time.
|
||||
pub mod time {
|
||||
use node_primitives::{BlockNumber, Moment};
|
||||
|
||||
/// Since BABE is probabilistic this is the average expected block time that
|
||||
/// we are targeting. Blocks will be produced at a minimum duration defined
|
||||
/// by `SLOT_DURATION`, but some slots will not be allocated to any
|
||||
/// authority and hence no block will be produced. We expect to have this
|
||||
/// block time on average following the defined slot duration and the value
|
||||
/// of `c` configured for BABE (where `1 - c` represents the probability of
|
||||
/// a slot being empty).
|
||||
/// This value is only used indirectly to define the unit constants below
|
||||
/// that are expressed in blocks. The rest of the code should use
|
||||
/// `SLOT_DURATION` instead (like the Timestamp pallet for calculating the
|
||||
/// minimum period).
|
||||
///
|
||||
/// If using BABE with secondary slots (default) then all of the slots will
|
||||
/// always be assigned, in which case `MILLISECS_PER_BLOCK` and
|
||||
/// `SLOT_DURATION` should have the same value.
|
||||
///
|
||||
/// <https://research.web3.foundation/Polkadot/protocols/block-production/Babe#6-practical-results>
|
||||
pub const MILLISECS_PER_BLOCK: Moment = 3000;
|
||||
pub const SECS_PER_BLOCK: Moment = MILLISECS_PER_BLOCK / 1000;
|
||||
|
||||
// NOTE: Currently it is not possible to change the slot duration after the chain has started.
|
||||
// Attempting to do so will brick block production.
|
||||
pub const SLOT_DURATION: Moment = MILLISECS_PER_BLOCK;
|
||||
|
||||
// 1 in 4 blocks (on average, not counting collisions) will be primary BABE blocks.
|
||||
pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4);
|
||||
|
||||
// NOTE: Currently it is not possible to change the epoch duration after the chain has started.
|
||||
// Attempting to do so will brick block production.
|
||||
pub const EPOCH_DURATION_IN_BLOCKS: BlockNumber = 10 * MINUTES;
|
||||
pub const EPOCH_DURATION_IN_SLOTS: u64 = {
|
||||
const SLOT_FILL_RATE: f64 = MILLISECS_PER_BLOCK as f64 / SLOT_DURATION as f64;
|
||||
|
||||
(EPOCH_DURATION_IN_BLOCKS as f64 * SLOT_FILL_RATE) as u64
|
||||
};
|
||||
|
||||
// These time units are defined in number of blocks.
|
||||
pub const MINUTES: BlockNumber = 60 / (SECS_PER_BLOCK as BlockNumber);
|
||||
pub const HOURS: BlockNumber = MINUTES * 60;
|
||||
pub const DAYS: BlockNumber = HOURS * 24;
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
// 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.
|
||||
|
||||
//! Genesis Presets for the Kitchensink Runtime
|
||||
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use crate::{
|
||||
constants::currency::*, pezframe_support::build_struct_json_patch, AccountId, AssetsConfig,
|
||||
BabeConfig, Balance, BalancesConfig, ElectionsConfig, NominationPoolsConfig, ReviveConfig,
|
||||
RuntimeGenesisConfig, SessionConfig, SessionKeys, SocietyConfig, StakerStatus, StakingConfig,
|
||||
SudoConfig, TechnicalCommitteeConfig, BABE_GENESIS_EPOCH_CONFIG,
|
||||
};
|
||||
use alloc::{vec, vec::Vec};
|
||||
use pezpallet_im_online::sr25519::AuthorityId as ImOnlineId;
|
||||
use pezpallet_revive::is_eth_derived;
|
||||
use pezsp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
|
||||
use pezsp_consensus_babe::AuthorityId as BabeId;
|
||||
use pezsp_consensus_beefy::ecdsa_crypto::AuthorityId as BeefyId;
|
||||
use pezsp_consensus_grandpa::AuthorityId as GrandpaId;
|
||||
use pezsp_core::{crypto::get_public_from_string_or_panic, sr25519};
|
||||
use pezsp_genesis_builder::PresetId;
|
||||
use pezsp_keyring::Sr25519Keyring;
|
||||
use pezsp_mixnet::types::AuthorityId as MixnetId;
|
||||
use pezsp_runtime::Perbill;
|
||||
|
||||
pub const ENDOWMENT: Balance = 10_000_000 * DOLLARS;
|
||||
pub const STASH: Balance = ENDOWMENT / 1000;
|
||||
|
||||
/// The staker type as supplied ot the Staking config.
|
||||
pub type Staker = (AccountId, AccountId, Balance, StakerStatus<AccountId>);
|
||||
|
||||
/// Helper function to create RuntimeGenesisConfig json patch for testing.
|
||||
pub fn kitchensink_genesis(
|
||||
initial_authorities: Vec<(AccountId, AccountId, SessionKeys)>,
|
||||
root_key: AccountId,
|
||||
endowed_accounts: Vec<AccountId>,
|
||||
stakers: Vec<Staker>,
|
||||
) -> serde_json::Value {
|
||||
let validator_count = initial_authorities.len() as u32;
|
||||
let minimum_validator_count = validator_count;
|
||||
|
||||
let collective = collective(&endowed_accounts);
|
||||
|
||||
build_struct_json_patch!(RuntimeGenesisConfig {
|
||||
balances: BalancesConfig {
|
||||
balances: endowed_accounts.iter().cloned().map(|x| (x, ENDOWMENT)).collect(),
|
||||
..Default::default()
|
||||
},
|
||||
session: SessionConfig {
|
||||
keys: initial_authorities
|
||||
.iter()
|
||||
.map(|x| { (x.0.clone(), x.1.clone(), x.2.clone()) })
|
||||
.collect(),
|
||||
},
|
||||
staking: StakingConfig {
|
||||
validator_count,
|
||||
minimum_validator_count,
|
||||
invulnerables: initial_authorities
|
||||
.iter()
|
||||
.map(|x| x.0.clone())
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.expect("Too many invulnerable validators: upper limit is MaxInvulnerables from pallet staking config"),
|
||||
slash_reward_fraction: Perbill::from_percent(10),
|
||||
stakers,
|
||||
},
|
||||
elections: ElectionsConfig {
|
||||
members: collective.iter().cloned().map(|member| (member, STASH)).collect(),
|
||||
},
|
||||
technical_committee: TechnicalCommitteeConfig { members: collective },
|
||||
sudo: SudoConfig { key: Some(root_key) },
|
||||
babe: BabeConfig { epoch_config: BABE_GENESIS_EPOCH_CONFIG },
|
||||
society: SocietyConfig { pot: 0 },
|
||||
assets: AssetsConfig {
|
||||
// This asset is used by the NIS pallet as counterpart currency.
|
||||
assets: vec![(9, Sr25519Keyring::Alice.to_account_id(), true, 1)],
|
||||
..Default::default()
|
||||
},
|
||||
nomination_pools: NominationPoolsConfig {
|
||||
min_create_bond: 10 * DOLLARS,
|
||||
min_join_bond: 1 * DOLLARS,
|
||||
},
|
||||
revive: ReviveConfig {
|
||||
mapped_accounts: endowed_accounts.iter().filter(|x| ! is_eth_derived(x)).cloned().collect(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Provides the JSON representation of predefined genesis config for given `id`.
|
||||
pub fn get_preset(id: &PresetId) -> Option<Vec<u8>> {
|
||||
// Note: Can't use `Sr25519Keyring::Alice.to_seed()` because the seed comes with `//`.
|
||||
let (alice_stash, alice, alice_session_keys) = authority_keys_from_seed("Alice");
|
||||
let (bob_stash, _bob, bob_session_keys) = authority_keys_from_seed("Bob");
|
||||
|
||||
let endowed = well_known_including_eth_accounts();
|
||||
|
||||
let patch = match id.as_ref() {
|
||||
pezsp_genesis_builder::DEV_RUNTIME_PRESET => kitchensink_genesis(
|
||||
// Use stash as controller account, otherwise grandpa can't load the authority set at
|
||||
// genesis.
|
||||
vec![(alice_stash.clone(), alice_stash.clone(), alice_session_keys)],
|
||||
alice.clone(),
|
||||
endowed,
|
||||
vec![validator(alice_stash.clone())],
|
||||
),
|
||||
pezsp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET => kitchensink_genesis(
|
||||
vec![
|
||||
// Use stash as controller account, otherwise grandpa can't load the authority set
|
||||
// at genesis.
|
||||
(alice_stash.clone(), alice_stash.clone(), alice_session_keys),
|
||||
(bob_stash.clone(), bob_stash.clone(), bob_session_keys),
|
||||
],
|
||||
alice,
|
||||
endowed,
|
||||
vec![validator(alice_stash), validator(bob_stash)],
|
||||
),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(
|
||||
serde_json::to_string(&patch)
|
||||
.expect("serialization to json is expected to work. qed.")
|
||||
.into_bytes(),
|
||||
)
|
||||
}
|
||||
|
||||
/// List of supported presets.
|
||||
pub fn preset_names() -> Vec<PresetId> {
|
||||
vec![
|
||||
PresetId::from(pezsp_genesis_builder::DEV_RUNTIME_PRESET),
|
||||
PresetId::from(pezsp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET),
|
||||
]
|
||||
}
|
||||
|
||||
/// Sets up the `account` to be a staker of validator variant as supplied to the
|
||||
/// staking config.
|
||||
pub fn validator(account: AccountId) -> Staker {
|
||||
// validator, controller, stash, staker status
|
||||
(account.clone(), account, STASH, StakerStatus::Validator)
|
||||
}
|
||||
|
||||
/// Extract some accounts from endowed to be put into the collective.
|
||||
fn collective(endowed: &[AccountId]) -> Vec<AccountId> {
|
||||
const MAX_COLLECTIVE_SIZE: usize = 50;
|
||||
let endowed_accounts_count = endowed.len();
|
||||
endowed
|
||||
.iter()
|
||||
.take((endowed_accounts_count.div_ceil(2)).min(MAX_COLLECTIVE_SIZE))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The Keyring's wellknown accounts + Alith and Baltathar.
|
||||
///
|
||||
/// Some integration tests require these ETH accounts.
|
||||
pub fn well_known_including_eth_accounts() -> Vec<AccountId> {
|
||||
Sr25519Keyring::well_known()
|
||||
.map(|k| k.to_account_id())
|
||||
.chain([
|
||||
// subxt_signer::eth::dev::alith()
|
||||
array_bytes::hex_n_into_unchecked(
|
||||
"f24ff3a9cf04c71dbc94d0b566f7a27b94566caceeeeeeeeeeeeeeeeeeeeeeee",
|
||||
),
|
||||
// subxt_signer::eth::dev::baltathar()
|
||||
array_bytes::hex_n_into_unchecked(
|
||||
"3cd0a705a2dc65e5b1e1205896baa2be8a07c6e0eeeeeeeeeeeeeeeeeeeeeeee",
|
||||
),
|
||||
])
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// Helper function to generate stash, controller and session key from seed.
|
||||
///
|
||||
/// Note: `//` is prepended internally.
|
||||
pub fn authority_keys_from_seed(seed: &str) -> (AccountId, AccountId, SessionKeys) {
|
||||
(
|
||||
get_public_from_string_or_panic::<sr25519::Public>(&alloc::format!("{seed}//stash")).into(),
|
||||
get_public_from_string_or_panic::<sr25519::Public>(seed).into(),
|
||||
session_keys_from_seed(seed),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn session_keys(
|
||||
grandpa: GrandpaId,
|
||||
babe: BabeId,
|
||||
im_online: ImOnlineId,
|
||||
authority_discovery: AuthorityDiscoveryId,
|
||||
mixnet: MixnetId,
|
||||
beefy: BeefyId,
|
||||
) -> SessionKeys {
|
||||
SessionKeys { grandpa, babe, im_online, authority_discovery, mixnet, beefy }
|
||||
}
|
||||
|
||||
/// We have this method as there is no straight forward way to convert the
|
||||
/// account keyring into these ids.
|
||||
///
|
||||
/// Note: `//` is prepended internally.
|
||||
pub fn session_keys_from_seed(seed: &str) -> SessionKeys {
|
||||
session_keys(
|
||||
get_public_from_string_or_panic::<GrandpaId>(seed),
|
||||
get_public_from_string_or_panic::<BabeId>(seed),
|
||||
get_public_from_string_or_panic::<ImOnlineId>(seed),
|
||||
get_public_from_string_or_panic::<AuthorityDiscoveryId>(seed),
|
||||
get_public_from_string_or_panic::<MixnetId>(seed),
|
||||
get_public_from_string_or_panic::<BeefyId>(seed),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,493 @@
|
||||
// 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.
|
||||
|
||||
//! Some configurable implementations as associated type for the bizinikiwi runtime.
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use pezframe_support::{
|
||||
pezpallet_prelude::*,
|
||||
traits::{
|
||||
fungibles::{Balanced, Credit},
|
||||
Currency, OnUnbalanced,
|
||||
},
|
||||
};
|
||||
use pezpallet_alliance::{IdentityVerifier, ProposalIndex, ProposalProvider};
|
||||
use pezpallet_asset_tx_payment::HandleCredit;
|
||||
use pezpallet_identity::legacy::IdentityField;
|
||||
use pezkuwi_sdk::*;
|
||||
|
||||
use crate::{
|
||||
AccountId, AllianceCollective, AllianceMotion, Assets, Authorship, Balances, Hash,
|
||||
NegativeImbalance, Runtime, RuntimeCall,
|
||||
};
|
||||
|
||||
pub struct Author;
|
||||
impl OnUnbalanced<NegativeImbalance> for Author {
|
||||
fn on_nonzero_unbalanced(amount: NegativeImbalance) {
|
||||
if let Some(author) = Authorship::author() {
|
||||
Balances::resolve_creating(&author, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A `HandleCredit` implementation that naively transfers the fees to the block author.
|
||||
/// Will drop and burn the assets in case the transfer fails.
|
||||
pub struct CreditToBlockAuthor;
|
||||
impl HandleCredit<AccountId, Assets> for CreditToBlockAuthor {
|
||||
fn handle_credit(credit: Credit<AccountId, Assets>) {
|
||||
if let Some(author) = pezpallet_authorship::Pallet::<Runtime>::author() {
|
||||
// Drop the result which will trigger the `OnDrop` of the imbalance in case of error.
|
||||
let _ = Assets::resolve(&author, credit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AllianceIdentityVerifier;
|
||||
impl IdentityVerifier<AccountId> for AllianceIdentityVerifier {
|
||||
fn has_required_identities(who: &AccountId) -> bool {
|
||||
crate::Identity::has_identity(who, (IdentityField::Display | IdentityField::Web).bits())
|
||||
}
|
||||
|
||||
fn has_good_judgement(who: &AccountId) -> bool {
|
||||
use pezpallet_identity::{IdentityOf, Judgement};
|
||||
IdentityOf::<Runtime>::get(who)
|
||||
.map(|registration| registration.judgements)
|
||||
.map_or(false, |judgements| {
|
||||
judgements
|
||||
.iter()
|
||||
.any(|(_, j)| matches!(j, Judgement::KnownGood | Judgement::Reasonable))
|
||||
})
|
||||
}
|
||||
|
||||
fn super_account_id(who: &AccountId) -> Option<AccountId> {
|
||||
use pezpallet_identity::SuperOf;
|
||||
SuperOf::<Runtime>::get(who).map(|parent| parent.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AllianceProposalProvider;
|
||||
impl ProposalProvider<AccountId, Hash, RuntimeCall> for AllianceProposalProvider {
|
||||
fn propose_proposal(
|
||||
who: AccountId,
|
||||
threshold: u32,
|
||||
proposal: Box<RuntimeCall>,
|
||||
length_bound: u32,
|
||||
) -> Result<(u32, u32), DispatchError> {
|
||||
AllianceMotion::do_propose_proposed(who, threshold, proposal, length_bound)
|
||||
}
|
||||
|
||||
fn vote_proposal(
|
||||
who: AccountId,
|
||||
proposal: Hash,
|
||||
index: ProposalIndex,
|
||||
approve: bool,
|
||||
) -> Result<bool, DispatchError> {
|
||||
AllianceMotion::do_vote(who, proposal, index, approve)
|
||||
}
|
||||
|
||||
fn close_proposal(
|
||||
proposal_hash: Hash,
|
||||
proposal_index: ProposalIndex,
|
||||
proposal_weight_bound: Weight,
|
||||
length_bound: u32,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
AllianceMotion::do_close(proposal_hash, proposal_index, proposal_weight_bound, length_bound)
|
||||
}
|
||||
|
||||
fn proposal_of(proposal_hash: Hash) -> Option<RuntimeCall> {
|
||||
pezpallet_collective::ProposalOf::<Runtime, AllianceCollective>::get(proposal_hash)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod multiplier_tests {
|
||||
use pezframe_support::{
|
||||
dispatch::DispatchClass,
|
||||
weights::{Weight, WeightToFee},
|
||||
};
|
||||
use pezpallet_transaction_payment::{Multiplier, TargetedFeeAdjustment};
|
||||
use pezkuwi_sdk::*;
|
||||
use pezsp_runtime::{
|
||||
assert_eq_error_rate,
|
||||
traits::{Convert, One, Zero},
|
||||
BuildStorage, FixedPointNumber,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
constants::{currency::*, time::*},
|
||||
AdjustmentVariable, MaximumMultiplier, MinimumMultiplier, Runtime,
|
||||
RuntimeBlockWeights as BlockWeights, System, TargetBlockFullness, TransactionPayment,
|
||||
};
|
||||
|
||||
fn max_normal() -> Weight {
|
||||
BlockWeights::get()
|
||||
.get(DispatchClass::Normal)
|
||||
.max_total
|
||||
.unwrap_or_else(|| BlockWeights::get().max_block)
|
||||
}
|
||||
|
||||
fn min_multiplier() -> Multiplier {
|
||||
MinimumMultiplier::get()
|
||||
}
|
||||
|
||||
fn target() -> Weight {
|
||||
TargetBlockFullness::get() * max_normal()
|
||||
}
|
||||
|
||||
// update based on runtime impl.
|
||||
fn runtime_multiplier_update(fm: Multiplier) -> Multiplier {
|
||||
TargetedFeeAdjustment::<
|
||||
Runtime,
|
||||
TargetBlockFullness,
|
||||
AdjustmentVariable,
|
||||
MinimumMultiplier,
|
||||
MaximumMultiplier,
|
||||
>::convert(fm)
|
||||
}
|
||||
|
||||
// update based on reference impl.
|
||||
fn truth_value_update(block_weight: Weight, previous: Multiplier) -> Multiplier {
|
||||
let accuracy = Multiplier::accuracy() as f64;
|
||||
let previous_float = previous.into_inner() as f64 / accuracy;
|
||||
// bump if it is zero.
|
||||
let previous_float = previous_float.max(min_multiplier().into_inner() as f64 / accuracy);
|
||||
|
||||
let max_normal = max_normal();
|
||||
let target_weight = target();
|
||||
let normalized_weight_dimensions = (
|
||||
block_weight.ref_time() as f64 / max_normal.ref_time() as f64,
|
||||
block_weight.proof_size() as f64 / max_normal.proof_size() as f64,
|
||||
);
|
||||
|
||||
let (normal, max, target) =
|
||||
if normalized_weight_dimensions.0 < normalized_weight_dimensions.1 {
|
||||
(block_weight.proof_size(), max_normal.proof_size(), target_weight.proof_size())
|
||||
} else {
|
||||
(block_weight.ref_time(), max_normal.ref_time(), target_weight.ref_time())
|
||||
};
|
||||
|
||||
// maximum tx weight
|
||||
let m = max as f64;
|
||||
// block weight always truncated to max weight
|
||||
let block_weight = (normal as f64).min(m);
|
||||
let v: f64 = AdjustmentVariable::get().to_float();
|
||||
|
||||
// Ideal saturation in terms of weight
|
||||
let ss = target as f64;
|
||||
// Current saturation in terms of weight
|
||||
let s = block_weight;
|
||||
|
||||
let t1 = v * (s / m - ss / m);
|
||||
let t2 = v.powi(2) * (s / m - ss / m).powi(2) / 2.0;
|
||||
let next_float = previous_float * (1.0 + t1 + t2);
|
||||
Multiplier::from_float(next_float)
|
||||
}
|
||||
|
||||
fn run_with_system_weight<F>(w: Weight, assertions: F)
|
||||
where
|
||||
F: Fn() -> (),
|
||||
{
|
||||
let mut t: pezsp_io::TestExternalities = pezframe_system::GenesisConfig::<Runtime>::default()
|
||||
.build_storage()
|
||||
.unwrap()
|
||||
.into();
|
||||
t.execute_with(|| {
|
||||
System::set_block_consumed_resources(w, 0);
|
||||
assertions()
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn truth_value_update_poc_works() {
|
||||
let fm = Multiplier::saturating_from_rational(1, 2);
|
||||
let test_set = vec![
|
||||
(Weight::zero(), fm),
|
||||
(Weight::from_parts(100, 0), fm),
|
||||
(Weight::from_parts(1000, 0), fm),
|
||||
(target(), fm),
|
||||
(max_normal() / 2, fm),
|
||||
(max_normal(), fm),
|
||||
];
|
||||
test_set.into_iter().for_each(|(w, fm)| {
|
||||
run_with_system_weight(w, || {
|
||||
assert_eq_error_rate!(
|
||||
truth_value_update(w, fm),
|
||||
runtime_multiplier_update(fm),
|
||||
// Error is only 1 in 100^18
|
||||
Multiplier::from_inner(100),
|
||||
);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiplier_can_grow_from_zero() {
|
||||
// if the min is too small, then this will not change, and we are doomed forever.
|
||||
// the block ref time is 1/100th bigger than target.
|
||||
run_with_system_weight(target().set_ref_time(target().ref_time() * 101 / 100), || {
|
||||
let next = runtime_multiplier_update(min_multiplier());
|
||||
assert!(next > min_multiplier(), "{:?} !> {:?}", next, min_multiplier());
|
||||
});
|
||||
|
||||
// the block proof size is 1/100th bigger than target.
|
||||
run_with_system_weight(target().set_proof_size((target().proof_size() / 100) * 101), || {
|
||||
let next = runtime_multiplier_update(min_multiplier());
|
||||
assert!(next > min_multiplier(), "{:?} !> {:?}", next, min_multiplier());
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiplier_cannot_go_below_limit() {
|
||||
// will not go any further below even if block is empty.
|
||||
run_with_system_weight(Weight::zero(), || {
|
||||
let next = runtime_multiplier_update(min_multiplier());
|
||||
assert_eq!(next, min_multiplier());
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn time_to_reach_zero() {
|
||||
// blocks per 24h in bizinikiwi-node: 28,800 (k)
|
||||
// s* = 0.1875
|
||||
// The bound from the research in an empty chain is:
|
||||
// v <~ (p / k(0 - s*))
|
||||
// p > v * k * -0.1875
|
||||
// to get p == -1 we'd need
|
||||
// -1 > 0.00001 * k * -0.1875
|
||||
// 1 < 0.00001 * k * 0.1875
|
||||
// 10^9 / 1875 < k
|
||||
// k > 533_333 ~ 18,5 days.
|
||||
run_with_system_weight(Weight::zero(), || {
|
||||
// start from 1, the default.
|
||||
let mut fm = Multiplier::one();
|
||||
let mut iterations: u64 = 0;
|
||||
loop {
|
||||
let next = runtime_multiplier_update(fm);
|
||||
fm = next;
|
||||
if fm == min_multiplier() {
|
||||
break;
|
||||
}
|
||||
iterations += 1;
|
||||
}
|
||||
assert!(iterations > 533_333);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn min_change_per_day() {
|
||||
run_with_system_weight(max_normal(), || {
|
||||
let mut fm = Multiplier::one();
|
||||
// See the example in the doc of `TargetedFeeAdjustment`. are at least 0.234, hence
|
||||
// `fm > 1.234`.
|
||||
for _ in 0..DAYS {
|
||||
let next = runtime_multiplier_update(fm);
|
||||
fm = next;
|
||||
}
|
||||
assert!(fm > Multiplier::saturating_from_rational(1234, 1000));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn congested_chain_simulation() {
|
||||
// `cargo test congested_chain_simulation -- --nocapture` to get some insight.
|
||||
|
||||
// almost full. The entire quota of normal transactions is taken.
|
||||
let block_weight = BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap() -
|
||||
Weight::from_parts(100, 0);
|
||||
|
||||
// Default bizinikiwi weight.
|
||||
let tx_weight = pezframe_support::weights::constants::ExtrinsicBaseWeight::get();
|
||||
|
||||
run_with_system_weight(block_weight, || {
|
||||
// initial value configured on module
|
||||
let mut fm = Multiplier::one();
|
||||
assert_eq!(fm, TransactionPayment::next_fee_multiplier());
|
||||
|
||||
let mut iterations: u64 = 0;
|
||||
loop {
|
||||
let next = runtime_multiplier_update(fm);
|
||||
// if no change, panic. This should never happen in this case.
|
||||
if fm == next {
|
||||
panic!("The fee should ever increase");
|
||||
}
|
||||
fm = next;
|
||||
iterations += 1;
|
||||
let fee =
|
||||
<Runtime as pezpallet_transaction_payment::Config>::WeightToFee::weight_to_fee(
|
||||
&tx_weight,
|
||||
);
|
||||
let adjusted_fee = fm.saturating_mul_acc_int(fee);
|
||||
println!(
|
||||
"iteration {}, new fm = {:?}. Fee at this point is: {} units / {} millicents, \
|
||||
{} cents, {} dollars",
|
||||
iterations,
|
||||
fm,
|
||||
adjusted_fee,
|
||||
adjusted_fee / MILLICENTS,
|
||||
adjusted_fee / CENTS,
|
||||
adjusted_fee / DOLLARS,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stateless_weight_mul() {
|
||||
let fm = Multiplier::saturating_from_rational(1, 2);
|
||||
run_with_system_weight(target() / 4, || {
|
||||
let next = runtime_multiplier_update(fm);
|
||||
assert_eq_error_rate!(
|
||||
next,
|
||||
truth_value_update(target() / 4, fm),
|
||||
Multiplier::from_inner(100),
|
||||
);
|
||||
|
||||
// Light block. Multiplier is reduced a little.
|
||||
assert!(next < fm);
|
||||
});
|
||||
|
||||
run_with_system_weight(target() / 2, || {
|
||||
let next = runtime_multiplier_update(fm);
|
||||
assert_eq_error_rate!(
|
||||
next,
|
||||
truth_value_update(target() / 2, fm),
|
||||
Multiplier::from_inner(100),
|
||||
);
|
||||
// Light block. Multiplier is reduced a little.
|
||||
assert!(next < fm);
|
||||
});
|
||||
run_with_system_weight(target(), || {
|
||||
let next = runtime_multiplier_update(fm);
|
||||
assert_eq_error_rate!(
|
||||
next,
|
||||
truth_value_update(target(), fm),
|
||||
Multiplier::from_inner(100),
|
||||
);
|
||||
// ideal. No changes.
|
||||
assert_eq!(next, fm)
|
||||
});
|
||||
run_with_system_weight(target() * 2, || {
|
||||
// More than ideal. Fee is increased.
|
||||
let next = runtime_multiplier_update(fm);
|
||||
assert_eq_error_rate!(
|
||||
next,
|
||||
truth_value_update(target() * 2, fm),
|
||||
Multiplier::from_inner(100),
|
||||
);
|
||||
|
||||
// Heavy block. Fee is increased a little.
|
||||
assert!(next > fm);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn weight_mul_grow_on_big_block() {
|
||||
run_with_system_weight(target() * 2, || {
|
||||
let mut original = Multiplier::zero();
|
||||
let mut next = Multiplier::default();
|
||||
|
||||
(0..1_000).for_each(|_| {
|
||||
next = runtime_multiplier_update(original);
|
||||
assert_eq_error_rate!(
|
||||
next,
|
||||
truth_value_update(target() * 2, original),
|
||||
Multiplier::from_inner(100),
|
||||
);
|
||||
// must always increase
|
||||
assert!(next > original, "{:?} !>= {:?}", next, original);
|
||||
original = next;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn weight_mul_decrease_on_small_block() {
|
||||
run_with_system_weight(target() / 2, || {
|
||||
let mut original = Multiplier::saturating_from_rational(1, 2);
|
||||
let mut next;
|
||||
|
||||
for _ in 0..100 {
|
||||
// decreases
|
||||
next = runtime_multiplier_update(original);
|
||||
assert!(next < original, "{:?} !<= {:?}", next, original);
|
||||
original = next;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn weight_to_fee_should_not_overflow_on_large_weights() {
|
||||
let kb_time = Weight::from_parts(1024, 0);
|
||||
let kb_size = Weight::from_parts(0, 1024);
|
||||
let mb_time = 1024u64 * kb_time;
|
||||
let max_fm = Multiplier::saturating_from_integer(i128::MAX);
|
||||
|
||||
// check that for all values it can compute, correctly.
|
||||
vec![
|
||||
Weight::zero(),
|
||||
// testcases ignoring proof size part of the weight.
|
||||
Weight::from_parts(1, 0),
|
||||
Weight::from_parts(10, 0),
|
||||
Weight::from_parts(1000, 0),
|
||||
kb_time,
|
||||
10u64 * kb_time,
|
||||
100u64 * kb_time,
|
||||
mb_time,
|
||||
10u64 * mb_time,
|
||||
Weight::from_parts(2147483647, 0),
|
||||
Weight::from_parts(4294967295, 0),
|
||||
// testcases ignoring ref time part of the weight.
|
||||
Weight::from_parts(0, 100000000000),
|
||||
1000000u64 * kb_size,
|
||||
1000000000u64 * kb_size,
|
||||
Weight::from_parts(0, 18014398509481983),
|
||||
Weight::from_parts(0, 9223372036854775807),
|
||||
// test cases with both parts of the weight.
|
||||
BlockWeights::get().max_block / 1024,
|
||||
BlockWeights::get().max_block / 2,
|
||||
BlockWeights::get().max_block,
|
||||
Weight::MAX / 2,
|
||||
Weight::MAX,
|
||||
]
|
||||
.into_iter()
|
||||
.for_each(|i| {
|
||||
run_with_system_weight(i, || {
|
||||
let next = runtime_multiplier_update(Multiplier::one());
|
||||
let truth = truth_value_update(i, Multiplier::one());
|
||||
assert_eq_error_rate!(truth, next, Multiplier::from_inner(50_000_000));
|
||||
});
|
||||
});
|
||||
|
||||
// Some values that are all above the target and will cause an increase.
|
||||
let t = target();
|
||||
vec![
|
||||
t + Weight::from_parts(100, 0),
|
||||
t + Weight::from_parts(0, t.proof_size() * 2),
|
||||
t * 2,
|
||||
t * 4,
|
||||
]
|
||||
.into_iter()
|
||||
.for_each(|i| {
|
||||
run_with_system_weight(i, || {
|
||||
let fm = runtime_multiplier_update(max_fm);
|
||||
// won't grow. The convert saturates everything.
|
||||
assert_eq!(fm, max_fm);
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,238 @@
|
||||
// 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.
|
||||
|
||||
//! Autogenerated bag thresholds.
|
||||
//!
|
||||
//! Generated on 2022-08-15T19:26:59.939787+00:00
|
||||
//! Arguments
|
||||
//! Total issuance: 100000000000000
|
||||
//! Minimum balance: 100000000000000
|
||||
//! for the node runtime.
|
||||
|
||||
/// Existential weight for this runtime.
|
||||
#[cfg(any(test, feature = "std"))]
|
||||
#[allow(unused)]
|
||||
pub const EXISTENTIAL_WEIGHT: u64 = 100_000_000_000_000;
|
||||
|
||||
/// Constant ratio between bags for this runtime.
|
||||
#[cfg(any(test, feature = "std"))]
|
||||
#[allow(unused)]
|
||||
pub const CONSTANT_RATIO: f64 = 1.0628253590743408;
|
||||
|
||||
/// Upper thresholds delimiting the bag list.
|
||||
pub const THRESHOLDS: [u64; 200] = [
|
||||
100_000_000_000_000,
|
||||
106_282_535_907_434,
|
||||
112_959_774_389_150,
|
||||
120_056_512_776_105,
|
||||
127_599_106_300_477,
|
||||
135_615_565_971_369,
|
||||
144_135_662_599_590,
|
||||
153_191_037_357_827,
|
||||
162_815_319_286_803,
|
||||
173_044_250_183_800,
|
||||
183_915_817_337_347,
|
||||
195_470_394_601_017,
|
||||
207_750_892_330_229,
|
||||
220_802_916_738_890,
|
||||
234_674_939_267_673,
|
||||
249_418_476_592_914,
|
||||
265_088_281_944_639,
|
||||
281_742_548_444_211,
|
||||
299_443_125_216_738,
|
||||
318_255_747_080_822,
|
||||
338_250_278_668_647,
|
||||
359_500_973_883_001,
|
||||
382_086_751_654_776,
|
||||
406_091_489_025_036,
|
||||
431_604_332_640_068,
|
||||
458_720_029_816_222,
|
||||
487_539_280_404_019,
|
||||
518_169_110_758_247,
|
||||
550_723_271_202_866,
|
||||
585_322_658_466_782,
|
||||
622_095_764_659_305,
|
||||
661_179_154_452_653,
|
||||
702_717_972_243_610,
|
||||
746_866_481_177_808,
|
||||
793_788_636_038_393,
|
||||
843_658_692_126_636,
|
||||
896_661_852_395_681,
|
||||
952_994_955_240_703,
|
||||
1_012_867_205_499_736,
|
||||
1_076_500_951_379_881,
|
||||
1_144_132_510_194_192,
|
||||
1_216_013_045_975_769,
|
||||
1_292_409_502_228_280,
|
||||
1_373_605_593_276_862,
|
||||
1_459_902_857_901_004,
|
||||
1_551_621_779_162_291,
|
||||
1_649_102_974_585_730,
|
||||
1_752_708_461_114_642,
|
||||
1_862_822_999_536_805,
|
||||
1_979_855_523_374_646,
|
||||
2_104_240_657_545_975,
|
||||
2_236_440_332_435_128,
|
||||
2_376_945_499_368_703,
|
||||
2_526_277_953_866_680,
|
||||
2_684_992_273_439_945,
|
||||
2_853_677_877_130_641,
|
||||
3_032_961_214_443_876,
|
||||
3_223_508_091_799_862,
|
||||
3_426_026_145_146_232,
|
||||
3_641_267_467_913_124,
|
||||
3_870_031_404_070_482,
|
||||
4_113_167_516_660_186,
|
||||
4_371_578_742_827_277,
|
||||
4_646_224_747_067_156,
|
||||
4_938_125_485_141_739,
|
||||
5_248_364_991_899_922,
|
||||
5_578_095_407_069_235,
|
||||
5_928_541_253_969_291,
|
||||
6_301_003_987_036_955,
|
||||
6_696_866_825_051_405,
|
||||
7_117_599_888_008_300,
|
||||
7_564_765_656_719_910,
|
||||
8_040_024_775_416_580,
|
||||
8_545_142_218_898_723,
|
||||
9_081_993_847_142_344,
|
||||
9_652_573_371_700_016,
|
||||
10_258_999_759_768_490,
|
||||
10_903_525_103_419_522,
|
||||
11_588_542_983_217_942,
|
||||
12_316_597_357_287_042,
|
||||
13_090_392_008_832_678,
|
||||
13_912_800_587_211_472,
|
||||
14_786_877_279_832_732,
|
||||
15_715_868_154_526_436,
|
||||
16_703_223_214_499_558,
|
||||
17_752_609_210_649_358,
|
||||
18_867_923_258_814_856,
|
||||
20_053_307_312_537_008,
|
||||
21_313_163_545_075_252,
|
||||
22_652_170_697_804_756,
|
||||
24_075_301_455_707_600,
|
||||
25_587_840_914_485_432,
|
||||
27_195_406_207_875_088,
|
||||
28_903_967_368_057_400,
|
||||
30_719_869_496_628_636,
|
||||
32_649_856_328_471_220,
|
||||
34_701_095_276_033_064,
|
||||
36_881_204_047_022_752,
|
||||
39_198_278_934_370_992,
|
||||
41_660_924_883_519_016,
|
||||
44_278_287_448_695_240,
|
||||
47_060_086_756_856_400,
|
||||
50_016_653_605_425_536,
|
||||
53_158_967_827_883_320,
|
||||
56_498_699_069_691_424,
|
||||
60_048_250_125_977_912,
|
||||
63_820_803_001_928_304,
|
||||
67_830_367_866_937_216,
|
||||
72_091_835_084_322_176,
|
||||
76_621_030_509_822_880,
|
||||
81_434_774_264_248_528,
|
||||
86_550_943_198_537_824,
|
||||
91_988_537_283_208_848,
|
||||
97_767_750_168_749_840,
|
||||
103_910_044_178_992_000,
|
||||
110_438_230_015_967_792,
|
||||
117_376_551_472_255_616,
|
||||
124_750_775_465_407_920,
|
||||
132_588_287_728_824_640,
|
||||
140_918_194_514_440_064,
|
||||
149_771_430_684_917_568,
|
||||
159_180_874_596_775_264,
|
||||
169_181_470_201_085_280,
|
||||
179_810_356_815_193_344,
|
||||
191_107_007_047_393_216,
|
||||
203_113_373_386_768_288,
|
||||
215_874_044_002_592_672,
|
||||
229_436_408_331_885_600,
|
||||
243_850_833_070_063_392,
|
||||
259_170_849_218_267_264,
|
||||
275_453_350_882_006_752,
|
||||
292_758_806_559_399_232,
|
||||
311_151_483_703_668_992,
|
||||
330_699_687_393_865_920,
|
||||
351_476_014_000_157_824,
|
||||
373_557_620_785_735_808,
|
||||
397_026_512_446_556_096,
|
||||
421_969_845_653_044_224,
|
||||
448_480_252_724_740_928,
|
||||
476_656_185_639_923_904,
|
||||
506_602_281_657_757_760,
|
||||
538_429_751_910_786_752,
|
||||
572_256_794_410_890_176,
|
||||
608_209_033_002_485_632,
|
||||
646_419_983_893_124_352,
|
||||
687_031_551_494_039_552,
|
||||
730_194_555_412_054_016,
|
||||
776_069_290_549_944_960,
|
||||
824_826_122_395_314_176,
|
||||
876_646_119_708_695_936,
|
||||
931_721_726_960_522_368,
|
||||
990_257_479_014_182_144,
|
||||
1_052_470_760_709_299_712,
|
||||
1_118_592_614_166_106_112,
|
||||
1_188_868_596_808_997_376,
|
||||
1_263_559_693_295_730_432,
|
||||
1_342_943_284_738_898_688,
|
||||
1_427_314_178_819_094_784,
|
||||
1_516_985_704_615_302_400,
|
||||
1_612_290_876_218_400_768,
|
||||
1_713_583_629_449_105_408,
|
||||
1_821_240_136_273_157_632,
|
||||
1_935_660_201_795_120_128,
|
||||
2_057_268_749_018_809_600,
|
||||
2_186_517_396_888_336_384,
|
||||
2_323_886_137_470_138_880,
|
||||
2_469_885_118_504_583_168,
|
||||
2_625_056_537_947_004_416,
|
||||
2_789_976_657_533_970_944,
|
||||
2_965_257_942_852_572_160,
|
||||
3_151_551_337_860_326_400,
|
||||
3_349_548_682_302_620_672,
|
||||
3_559_985_281_005_267_968,
|
||||
3_783_642_634_583_792_128,
|
||||
4_021_351_341_710_503_936,
|
||||
4_273_994_183_717_548_544,
|
||||
4_542_509_402_991_247_872,
|
||||
4_827_894_187_332_742_144,
|
||||
5_131_208_373_224_844_288,
|
||||
5_453_578_381_757_959_168,
|
||||
5_796_201_401_831_965_696,
|
||||
6_160_349_836_169_256_960,
|
||||
6_547_376_026_650_146_816,
|
||||
6_958_717_276_519_173_120,
|
||||
7_395_901_188_113_309_696,
|
||||
7_860_551_335_934_872_576,
|
||||
8_354_393_296_137_270_272,
|
||||
8_879_261_054_815_360_000,
|
||||
9_437_103_818_898_946_048,
|
||||
10_029_993_254_943_105_024,
|
||||
10_660_131_182_698_121_216,
|
||||
11_329_857_752_030_707_712,
|
||||
12_041_660_133_563_240_448,
|
||||
12_798_181_755_305_525_248,
|
||||
13_602_232_119_581_272_064,
|
||||
14_456_797_236_706_498_560,
|
||||
15_365_050_714_167_523_328,
|
||||
16_330_365_542_480_556_032,
|
||||
17_356_326_621_502_140_416,
|
||||
18_446_744_073_709_551_615,
|
||||
];
|
||||
@@ -0,0 +1,81 @@
|
||||
[package]
|
||||
name = "node-testing"
|
||||
version = "3.0.0-dev"
|
||||
authors.workspace = true
|
||||
description = "Test utilities for Bizinikiwi node."
|
||||
edition.workspace = true
|
||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { workspace = true, default-features = true }
|
||||
pezframe-metadata-hash-extension = { workspace = true, default-features = true }
|
||||
pezframe-system = { workspace = true, default-features = true }
|
||||
fs_extra = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
kitchensink-runtime = { workspace = true }
|
||||
log = { workspace = true, default-features = true }
|
||||
node-cli = { workspace = true }
|
||||
node-primitives = { workspace = true, default-features = true }
|
||||
pezpallet-asset-conversion = { workspace = true, default-features = true }
|
||||
pezpallet-asset-conversion-tx-payment = { workspace = true, default-features = true }
|
||||
pezpallet-revive = { workspace = true, default-features = true }
|
||||
pezpallet-skip-feeless-payment = { workspace = true, default-features = true }
|
||||
pezsc-block-builder = { workspace = true, default-features = true }
|
||||
pezsc-client-api = { workspace = true, default-features = true }
|
||||
pezsc-client-db = { features = [
|
||||
"rocksdb",
|
||||
], workspace = true, default-features = true }
|
||||
pezsc-consensus = { workspace = true, default-features = true }
|
||||
pezsc-executor = { workspace = true, default-features = true }
|
||||
pezsc-service = { features = [
|
||||
"rocksdb",
|
||||
], workspace = true, default-features = true }
|
||||
pezsp-api = { workspace = true, default-features = true }
|
||||
pezsp-block-builder = { workspace = true, default-features = true }
|
||||
pezsp-blockchain = { workspace = true, default-features = true }
|
||||
pezsp-consensus = { workspace = true, default-features = true }
|
||||
pezsp-core = { workspace = true, default-features = true }
|
||||
pezsp-crypto-hashing = { workspace = true, default-features = true }
|
||||
pezsp-inherents = { workspace = true, default-features = true }
|
||||
pezsp-keyring = { workspace = true, default-features = true }
|
||||
pezsp-runtime = { workspace = true, default-features = true }
|
||||
pezsp-timestamp = { workspace = true }
|
||||
bizinikiwi-test-client = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
|
||||
[features]
|
||||
runtime-benchmarks = [
|
||||
"pezframe-metadata-hash-extension/runtime-benchmarks",
|
||||
"pezframe-system/runtime-benchmarks",
|
||||
"kitchensink-runtime/runtime-benchmarks",
|
||||
"node-cli/runtime-benchmarks",
|
||||
"node-primitives/runtime-benchmarks",
|
||||
"pezpallet-asset-conversion-tx-payment/runtime-benchmarks",
|
||||
"pezpallet-asset-conversion/runtime-benchmarks",
|
||||
"pezpallet-revive/runtime-benchmarks",
|
||||
"pezpallet-skip-feeless-payment/runtime-benchmarks",
|
||||
"pezsc-block-builder/runtime-benchmarks",
|
||||
"pezsc-client-api/runtime-benchmarks",
|
||||
"pezsc-client-db/runtime-benchmarks",
|
||||
"pezsc-consensus/runtime-benchmarks",
|
||||
"pezsc-executor/runtime-benchmarks",
|
||||
"pezsc-service/runtime-benchmarks",
|
||||
"pezsp-api/runtime-benchmarks",
|
||||
"pezsp-block-builder/runtime-benchmarks",
|
||||
"pezsp-blockchain/runtime-benchmarks",
|
||||
"pezsp-consensus/runtime-benchmarks",
|
||||
"pezsp-inherents/runtime-benchmarks",
|
||||
"pezsp-keyring/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"pezsp-timestamp/runtime-benchmarks",
|
||||
"bizinikiwi-test-client/runtime-benchmarks",
|
||||
]
|
||||
@@ -0,0 +1,680 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Benchmarking module.
|
||||
//!
|
||||
//! Utilities to do full-scale benchmarks involving database. With `BenchDb` you
|
||||
//! can pregenerate seed database and `clone` it for every iteration of your benchmarks
|
||||
//! or tests to get consistent, smooth benchmark experience!
|
||||
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
client::{Backend, Client},
|
||||
keyring::*,
|
||||
};
|
||||
use codec::{Decode, Encode};
|
||||
use futures::executor;
|
||||
use kitchensink_runtime::{
|
||||
constants::currency::DOLLARS, AccountId, BalancesCall, CheckedExtrinsic, MinimumPeriod,
|
||||
RuntimeCall, Signature, SystemCall, UncheckedExtrinsic,
|
||||
};
|
||||
use node_primitives::Block;
|
||||
use pezsc_block_builder::BlockBuilderBuilder;
|
||||
use pezsc_client_api::{execution_extensions::ExecutionExtensions, UsageProvider};
|
||||
use pezsc_client_db::PruningMode;
|
||||
use pezsc_consensus::{BlockImport, BlockImportParams, ForkChoiceStrategy, ImportResult, ImportedAux};
|
||||
use pezsc_executor::{WasmExecutionMethod, WasmtimeInstantiationStrategy};
|
||||
use pezsp_api::ProvideRuntimeApi;
|
||||
use pezsp_block_builder::BlockBuilder;
|
||||
use pezsp_consensus::BlockOrigin;
|
||||
use pezsp_core::{
|
||||
crypto::get_public_from_string_or_panic, ed25519, sr25519, traits::SpawnNamed, Pair,
|
||||
};
|
||||
use pezsp_crypto_hashing::blake2_256;
|
||||
use pezsp_inherents::InherentData;
|
||||
use pezsp_runtime::{
|
||||
generic::{self, ExtrinsicFormat, Preamble},
|
||||
traits::{Block as BlockT, IdentifyAccount, Verify},
|
||||
OpaqueExtrinsic,
|
||||
};
|
||||
|
||||
/// Keyring full of accounts for benching.
|
||||
///
|
||||
/// Accounts are ordered:
|
||||
/// //endowed-user//00
|
||||
/// //endowed-user//01
|
||||
/// ...
|
||||
/// //endowed-user//N
|
||||
#[derive(Clone)]
|
||||
pub struct BenchKeyring {
|
||||
accounts: BTreeMap<AccountId, BenchPair>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum BenchPair {
|
||||
Sr25519(sr25519::Pair),
|
||||
Ed25519(ed25519::Pair),
|
||||
}
|
||||
|
||||
impl BenchPair {
|
||||
fn sign(&self, payload: &[u8]) -> Signature {
|
||||
match self {
|
||||
Self::Sr25519(pair) => pair.sign(payload).into(),
|
||||
Self::Ed25519(pair) => pair.sign(payload).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Drop system cache.
|
||||
///
|
||||
/// Will panic if cache drop is impossible.
|
||||
pub fn drop_system_cache() {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
log::warn!(
|
||||
target: "bench-logistics",
|
||||
"Clearing system cache on windows is not supported. Benchmark might totally be wrong.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
std::process::Command::new("sync")
|
||||
.output()
|
||||
.expect("Failed to execute system cache clear");
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
log::trace!(target: "bench-logistics", "Clearing system cache...");
|
||||
std::process::Command::new("echo")
|
||||
.args(&["3", ">", "/proc/sys/vm/drop_caches", "2>", "/dev/null"])
|
||||
.output()
|
||||
.expect("Failed to execute system cache clear");
|
||||
|
||||
let temp = tempfile::tempdir().expect("Failed to spawn tempdir");
|
||||
let temp_file_path = format!("of={}/buf", temp.path().to_string_lossy());
|
||||
|
||||
// this should refill write cache with 2GB of garbage
|
||||
std::process::Command::new("dd")
|
||||
.args(&["if=/dev/urandom", &temp_file_path, "bs=64M", "count=32"])
|
||||
.output()
|
||||
.expect("Failed to execute dd for cache clear");
|
||||
|
||||
// remove tempfile of previous command
|
||||
std::process::Command::new("rm")
|
||||
.arg(&temp_file_path)
|
||||
.output()
|
||||
.expect("Failed to remove temp file");
|
||||
|
||||
std::process::Command::new("sync")
|
||||
.output()
|
||||
.expect("Failed to execute system cache clear");
|
||||
|
||||
log::trace!(target: "bench-logistics", "Clearing system cache done!");
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
log::trace!(target: "bench-logistics", "Clearing system cache...");
|
||||
if let Err(err) = std::process::Command::new("purge").output() {
|
||||
log::error!("purge error {:?}: ", err);
|
||||
panic!("Could not clear system cache. Run under sudo?");
|
||||
}
|
||||
log::trace!(target: "bench-logistics", "Clearing system cache done!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Pre-initialized benchmarking database.
|
||||
///
|
||||
/// This is prepared database with genesis and keyring
|
||||
/// that can be cloned and then used for any benchmarking.
|
||||
pub struct BenchDb {
|
||||
keyring: BenchKeyring,
|
||||
directory_guard: Guard,
|
||||
database_type: DatabaseType,
|
||||
}
|
||||
|
||||
impl Clone for BenchDb {
|
||||
fn clone(&self) -> Self {
|
||||
let keyring = self.keyring.clone();
|
||||
let database_type = self.database_type;
|
||||
let dir = tempfile::tempdir().expect("temp dir creation failed");
|
||||
|
||||
let seed_dir = self.directory_guard.0.path();
|
||||
|
||||
log::trace!(
|
||||
target: "bench-logistics",
|
||||
"Copying seed db from {} to {}",
|
||||
seed_dir.to_string_lossy(),
|
||||
dir.path().to_string_lossy(),
|
||||
);
|
||||
let seed_db_files = std::fs::read_dir(seed_dir)
|
||||
.expect("failed to list file in seed dir")
|
||||
.map(|f_result| f_result.expect("failed to read file in seed db").path())
|
||||
.collect::<Vec<PathBuf>>();
|
||||
fs_extra::copy_items(&seed_db_files, dir.path(), &fs_extra::dir::CopyOptions::new())
|
||||
.expect("Copy of seed database is ok");
|
||||
|
||||
// We clear system cache after db clone but before any warmups.
|
||||
// This populates system cache with some data unrelated to actual
|
||||
// data we will be querying further under benchmark (like what
|
||||
// would have happened in real system that queries random entries
|
||||
// from database).
|
||||
drop_system_cache();
|
||||
|
||||
BenchDb { keyring, directory_guard: Guard(dir), database_type }
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of block for generation
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum BlockType {
|
||||
/// Bunch of random transfers.
|
||||
RandomTransfersKeepAlive,
|
||||
/// Bunch of random transfers that drain all of the source balance.
|
||||
RandomTransfersReaping,
|
||||
/// Bunch of "no-op" calls.
|
||||
Noop,
|
||||
}
|
||||
|
||||
impl BlockType {
|
||||
/// Create block content description with specified number of transactions.
|
||||
pub fn to_content(self, size: Option<usize>) -> BlockContent {
|
||||
BlockContent { block_type: self, size }
|
||||
}
|
||||
}
|
||||
|
||||
/// Content of the generated block.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BlockContent {
|
||||
block_type: BlockType,
|
||||
size: Option<usize>,
|
||||
}
|
||||
|
||||
/// Type of backend database.
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum DatabaseType {
|
||||
/// RocksDb backend.
|
||||
RocksDb,
|
||||
/// Parity DB backend.
|
||||
ParityDb,
|
||||
}
|
||||
|
||||
impl DatabaseType {
|
||||
fn into_settings(self, path: PathBuf) -> pezsc_client_db::DatabaseSource {
|
||||
match self {
|
||||
Self::RocksDb => pezsc_client_db::DatabaseSource::RocksDb { path, cache_size: 512 },
|
||||
Self::ParityDb => pezsc_client_db::DatabaseSource::ParityDb { path },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Benchmarking task executor.
|
||||
///
|
||||
/// Uses multiple threads as the regular executable.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TaskExecutor {
|
||||
pool: executor::ThreadPool,
|
||||
}
|
||||
|
||||
impl TaskExecutor {
|
||||
fn new() -> Self {
|
||||
Self { pool: executor::ThreadPool::new().expect("Failed to create task executor") }
|
||||
}
|
||||
}
|
||||
|
||||
impl SpawnNamed for TaskExecutor {
|
||||
fn spawn(
|
||||
&self,
|
||||
_: &'static str,
|
||||
_: Option<&'static str>,
|
||||
future: futures::future::BoxFuture<'static, ()>,
|
||||
) {
|
||||
self.pool.spawn_ok(future);
|
||||
}
|
||||
|
||||
fn spawn_blocking(
|
||||
&self,
|
||||
_: &'static str,
|
||||
_: Option<&'static str>,
|
||||
future: futures::future::BoxFuture<'static, ()>,
|
||||
) {
|
||||
self.pool.spawn_ok(future);
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator for block content.
|
||||
pub struct BlockContentIterator<'a> {
|
||||
iteration: usize,
|
||||
content: BlockContent,
|
||||
runtime_version: pezsc_executor::RuntimeVersion,
|
||||
genesis_hash: node_primitives::Hash,
|
||||
keyring: &'a BenchKeyring,
|
||||
}
|
||||
|
||||
impl<'a> BlockContentIterator<'a> {
|
||||
fn new(content: BlockContent, keyring: &'a BenchKeyring, client: &Client) -> Self {
|
||||
let genesis_hash = client.chain_info().genesis_hash;
|
||||
let runtime_version = client
|
||||
.runtime_version_at(genesis_hash)
|
||||
.expect("There should be runtime version at 0");
|
||||
|
||||
BlockContentIterator { iteration: 0, content, keyring, runtime_version, genesis_hash }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for BlockContentIterator<'a> {
|
||||
type Item = OpaqueExtrinsic;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.content.size.map(|size| size <= self.iteration).unwrap_or(false) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let sender = self.keyring.at(self.iteration);
|
||||
let receiver = get_public_from_string_or_panic::<sr25519::Public>(&format!(
|
||||
"random-user//{}",
|
||||
self.iteration
|
||||
))
|
||||
.into();
|
||||
|
||||
let signed = self.keyring.sign(
|
||||
CheckedExtrinsic {
|
||||
format: ExtrinsicFormat::Signed(
|
||||
sender,
|
||||
tx_ext(0, kitchensink_runtime::ExistentialDeposit::get() + 1),
|
||||
),
|
||||
function: match self.content.block_type {
|
||||
BlockType::RandomTransfersKeepAlive =>
|
||||
RuntimeCall::Balances(BalancesCall::transfer_keep_alive {
|
||||
dest: pezsp_runtime::MultiAddress::Id(receiver),
|
||||
value: kitchensink_runtime::ExistentialDeposit::get() + 1,
|
||||
}),
|
||||
BlockType::RandomTransfersReaping => {
|
||||
RuntimeCall::Balances(BalancesCall::transfer_allow_death {
|
||||
dest: pezsp_runtime::MultiAddress::Id(receiver),
|
||||
// Transfer so that ending balance would be 1 less than existential
|
||||
// deposit so that we kill the sender account.
|
||||
value: 100 * DOLLARS -
|
||||
(kitchensink_runtime::ExistentialDeposit::get() - 1),
|
||||
})
|
||||
},
|
||||
BlockType::Noop =>
|
||||
RuntimeCall::System(SystemCall::remark { remark: Vec::new() }),
|
||||
},
|
||||
},
|
||||
self.runtime_version.spec_version,
|
||||
self.runtime_version.transaction_version,
|
||||
self.genesis_hash.into(),
|
||||
);
|
||||
|
||||
let encoded = Encode::encode(&signed);
|
||||
|
||||
let opaque = OpaqueExtrinsic::decode(&mut &encoded[..]).expect("Failed to decode opaque");
|
||||
|
||||
self.iteration += 1;
|
||||
|
||||
Some(opaque)
|
||||
}
|
||||
}
|
||||
|
||||
impl BenchDb {
|
||||
/// New immutable benchmarking database.
|
||||
///
|
||||
/// See [`BenchDb::new`] method documentation for more information about the purpose
|
||||
/// of this structure.
|
||||
pub fn with_key_types(
|
||||
database_type: DatabaseType,
|
||||
keyring_length: usize,
|
||||
key_types: KeyTypes,
|
||||
) -> Self {
|
||||
let keyring = BenchKeyring::new(keyring_length, key_types);
|
||||
|
||||
let dir = tempfile::tempdir().expect("temp dir creation failed");
|
||||
log::trace!(
|
||||
target: "bench-logistics",
|
||||
"Created seed db at {}",
|
||||
dir.path().to_string_lossy(),
|
||||
);
|
||||
let (_client, _backend, _task_executor) =
|
||||
Self::bench_client(database_type, dir.path(), &keyring);
|
||||
let directory_guard = Guard(dir);
|
||||
|
||||
BenchDb { keyring, directory_guard, database_type }
|
||||
}
|
||||
|
||||
/// New immutable benchmarking database.
|
||||
///
|
||||
/// This will generate database files in random temporary directory
|
||||
/// and keep it there until struct is dropped.
|
||||
///
|
||||
/// You can `clone` this database or you can `create_context` from it
|
||||
/// (which also does `clone`) to run actual operation against new database
|
||||
/// which will be identical to the original.
|
||||
pub fn new(database_type: DatabaseType, keyring_length: usize) -> Self {
|
||||
Self::with_key_types(database_type, keyring_length, KeyTypes::Sr25519)
|
||||
}
|
||||
|
||||
// This should return client that is doing everything that full node
|
||||
// is doing.
|
||||
//
|
||||
// - This client should use best wasm execution method.
|
||||
// - This client should work with real database only.
|
||||
fn bench_client(
|
||||
database_type: DatabaseType,
|
||||
dir: &std::path::Path,
|
||||
keyring: &BenchKeyring,
|
||||
) -> (Client, std::sync::Arc<Backend>, TaskExecutor) {
|
||||
let db_config = pezsc_client_db::DatabaseSettings {
|
||||
trie_cache_maximum_size: Some(16 * 1024 * 1024),
|
||||
state_pruning: Some(PruningMode::ArchiveAll),
|
||||
source: database_type.into_settings(dir.into()),
|
||||
blocks_pruning: pezsc_client_db::BlocksPruning::KeepAll,
|
||||
metrics_registry: None,
|
||||
};
|
||||
let task_executor = TaskExecutor::new();
|
||||
|
||||
let backend = pezsc_service::new_db_backend(db_config).expect("Should not fail");
|
||||
let executor = pezsc_executor::WasmExecutor::builder()
|
||||
.with_execution_method(WasmExecutionMethod::Compiled {
|
||||
instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite,
|
||||
})
|
||||
.build();
|
||||
|
||||
let client_config = pezsc_service::ClientConfig::default();
|
||||
let genesis_block_builder = pezsc_service::GenesisBlockBuilder::new(
|
||||
keyring.as_storage_builder(),
|
||||
!client_config.no_genesis,
|
||||
backend.clone(),
|
||||
executor.clone(),
|
||||
)
|
||||
.expect("Failed to create genesis block builder");
|
||||
|
||||
let client = pezsc_service::new_client(
|
||||
backend.clone(),
|
||||
executor.clone(),
|
||||
genesis_block_builder,
|
||||
None,
|
||||
None,
|
||||
ExecutionExtensions::new(None, Arc::new(executor)),
|
||||
Box::new(task_executor.clone()),
|
||||
None,
|
||||
None,
|
||||
client_config,
|
||||
)
|
||||
.expect("Should not fail");
|
||||
|
||||
(client, backend, task_executor)
|
||||
}
|
||||
|
||||
/// Generate list of required inherents.
|
||||
///
|
||||
/// Uses already instantiated Client.
|
||||
pub fn generate_inherents(&mut self, client: &Client) -> Vec<OpaqueExtrinsic> {
|
||||
let mut inherent_data = InherentData::new();
|
||||
let timestamp = 1 * MinimumPeriod::get();
|
||||
|
||||
inherent_data
|
||||
.put_data(pezsp_timestamp::INHERENT_IDENTIFIER, ×tamp)
|
||||
.expect("Put timestamp failed");
|
||||
|
||||
client
|
||||
.runtime_api()
|
||||
.inherent_extrinsics(client.chain_info().genesis_hash, inherent_data)
|
||||
.expect("Get inherents failed")
|
||||
}
|
||||
|
||||
/// Iterate over some block content with transaction signed using this database keyring.
|
||||
pub fn block_content(
|
||||
&self,
|
||||
content: BlockContent,
|
||||
client: &Client,
|
||||
) -> BlockContentIterator<'_> {
|
||||
BlockContentIterator::new(content, &self.keyring, client)
|
||||
}
|
||||
|
||||
/// Get client for this database operations.
|
||||
pub fn client(&mut self) -> Client {
|
||||
let (client, _backend, _task_executor) =
|
||||
Self::bench_client(self.database_type, self.directory_guard.path(), &self.keyring);
|
||||
|
||||
client
|
||||
}
|
||||
|
||||
/// Generate new block using this database.
|
||||
pub fn generate_block(&mut self, content: BlockContent) -> Block {
|
||||
let client = self.client();
|
||||
let chain = client.usage_info().chain;
|
||||
|
||||
let mut block = BlockBuilderBuilder::new(&client)
|
||||
.on_parent_block(chain.best_hash)
|
||||
.with_parent_block_number(chain.best_number)
|
||||
.build()
|
||||
.expect("Failed to create block builder.");
|
||||
|
||||
for extrinsic in self.generate_inherents(&client) {
|
||||
block.push(extrinsic).expect("Push inherent failed");
|
||||
}
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
for opaque in self.block_content(content, &client) {
|
||||
match block.push(opaque) {
|
||||
Err(pezsp_blockchain::Error::ApplyExtrinsicFailed(
|
||||
pezsp_blockchain::ApplyExtrinsicFailed::Validity(e),
|
||||
)) if e.exhausted_resources() => break,
|
||||
Err(err) => panic!("Error pushing transaction: {:?}", err),
|
||||
Ok(_) => {},
|
||||
}
|
||||
}
|
||||
|
||||
let block = block.build().expect("Block build failed").block;
|
||||
|
||||
log::info!(
|
||||
target: "bench-logistics",
|
||||
"Block construction: {:#?} ({} tx)",
|
||||
start.elapsed(), block.extrinsics.len()
|
||||
);
|
||||
|
||||
block
|
||||
}
|
||||
|
||||
/// Database path.
|
||||
pub fn path(&self) -> &Path {
|
||||
self.directory_guard.path()
|
||||
}
|
||||
|
||||
/// Clone this database and create context for testing/benchmarking.
|
||||
pub fn create_context(&self) -> BenchContext {
|
||||
let BenchDb { directory_guard, keyring, database_type } = self.clone();
|
||||
let (client, backend, task_executor) =
|
||||
Self::bench_client(database_type, directory_guard.path(), &keyring);
|
||||
|
||||
BenchContext {
|
||||
client: Arc::new(client),
|
||||
db_guard: directory_guard,
|
||||
backend,
|
||||
spawn_handle: Box::new(task_executor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Key types to be used in benching keyring
|
||||
pub enum KeyTypes {
|
||||
/// sr25519 signing keys
|
||||
Sr25519,
|
||||
/// ed25519 signing keys
|
||||
Ed25519,
|
||||
}
|
||||
|
||||
impl BenchKeyring {
|
||||
/// New keyring.
|
||||
///
|
||||
/// `length` is the number of accounts generated.
|
||||
pub fn new(length: usize, key_types: KeyTypes) -> Self {
|
||||
let mut accounts = BTreeMap::new();
|
||||
|
||||
for n in 0..length {
|
||||
let seed = format!("//endowed-user/{}", n);
|
||||
let (account_id, pair) = match key_types {
|
||||
KeyTypes::Sr25519 => {
|
||||
let pair =
|
||||
sr25519::Pair::from_string(&seed, None).expect("failed to generate pair");
|
||||
let account_id = AccountPublic::from(pair.public()).into_account();
|
||||
(account_id, BenchPair::Sr25519(pair))
|
||||
},
|
||||
KeyTypes::Ed25519 => {
|
||||
let pair = ed25519::Pair::from_seed(&blake2_256(seed.as_bytes()));
|
||||
let account_id = AccountPublic::from(pair.public()).into_account();
|
||||
(account_id, BenchPair::Ed25519(pair))
|
||||
},
|
||||
};
|
||||
accounts.insert(account_id, pair);
|
||||
}
|
||||
|
||||
Self { accounts }
|
||||
}
|
||||
|
||||
/// Generated account id-s from keyring keypairs.
|
||||
pub fn collect_account_ids(&self) -> Vec<AccountId> {
|
||||
self.accounts.keys().cloned().collect()
|
||||
}
|
||||
|
||||
/// Get account id at position `index`
|
||||
pub fn at(&self, index: usize) -> AccountId {
|
||||
self.accounts.keys().nth(index).expect("Failed to get account").clone()
|
||||
}
|
||||
|
||||
/// Sign transaction with keypair from this keyring.
|
||||
pub fn sign(
|
||||
&self,
|
||||
xt: CheckedExtrinsic,
|
||||
spec_version: u32,
|
||||
tx_version: u32,
|
||||
genesis_hash: [u8; 32],
|
||||
) -> UncheckedExtrinsic {
|
||||
match xt.format {
|
||||
ExtrinsicFormat::Signed(signed, tx_ext) => {
|
||||
let payload = (
|
||||
xt.function,
|
||||
tx_ext.clone(),
|
||||
spec_version,
|
||||
tx_version,
|
||||
genesis_hash,
|
||||
genesis_hash,
|
||||
// metadata_hash
|
||||
None::<()>,
|
||||
);
|
||||
let key = self.accounts.get(&signed).expect("Account id not found in keyring");
|
||||
let signature = payload.using_encoded(|b| {
|
||||
if b.len() > 256 {
|
||||
key.sign(&blake2_256(b))
|
||||
} else {
|
||||
key.sign(b)
|
||||
}
|
||||
});
|
||||
generic::UncheckedExtrinsic::new_signed(
|
||||
payload.0,
|
||||
pezsp_runtime::MultiAddress::Id(signed),
|
||||
signature,
|
||||
tx_ext,
|
||||
)
|
||||
.into()
|
||||
},
|
||||
ExtrinsicFormat::Bare => generic::UncheckedExtrinsic::new_bare(xt.function).into(),
|
||||
ExtrinsicFormat::General(ext_version, tx_ext) =>
|
||||
generic::UncheckedExtrinsic::from_parts(
|
||||
xt.function,
|
||||
Preamble::General(ext_version, tx_ext),
|
||||
)
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate genesis with accounts from this keyring endowed with some balance and
|
||||
/// kitchensink_runtime code blob.
|
||||
pub fn as_storage_builder(&self) -> &dyn pezsp_runtime::BuildStorage {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl pezsp_runtime::BuildStorage for BenchKeyring {
|
||||
fn assimilate_storage(&self, storage: &mut pezsp_core::storage::Storage) -> Result<(), String> {
|
||||
storage.top.insert(
|
||||
pezsp_core::storage::well_known_keys::CODE.to_vec(),
|
||||
kitchensink_runtime::wasm_binary_unwrap().into(),
|
||||
);
|
||||
crate::genesis::config_endowed(self.collect_account_ids()).assimilate_storage(storage)
|
||||
}
|
||||
}
|
||||
|
||||
struct Guard(tempfile::TempDir);
|
||||
|
||||
impl Guard {
|
||||
fn path(&self) -> &Path {
|
||||
self.0.path()
|
||||
}
|
||||
}
|
||||
|
||||
/// Benchmarking/test context holding instantiated client and backend references.
|
||||
pub struct BenchContext {
|
||||
/// Node client.
|
||||
pub client: Arc<Client>,
|
||||
/// Node backend.
|
||||
pub backend: Arc<Backend>,
|
||||
/// Spawn handle.
|
||||
pub spawn_handle: Box<dyn SpawnNamed>,
|
||||
|
||||
db_guard: Guard,
|
||||
}
|
||||
|
||||
type AccountPublic = <Signature as Verify>::Signer;
|
||||
|
||||
impl BenchContext {
|
||||
/// Import some block.
|
||||
pub fn import_block(&mut self, block: Block) {
|
||||
let mut import_params =
|
||||
BlockImportParams::new(BlockOrigin::NetworkBroadcast, block.header.clone());
|
||||
import_params.body = Some(block.extrinsics().to_vec());
|
||||
import_params.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
||||
|
||||
assert_eq!(self.client.chain_info().best_number, 0);
|
||||
|
||||
assert_eq!(
|
||||
futures::executor::block_on(self.client.import_block(import_params))
|
||||
.expect("Failed to import block"),
|
||||
ImportResult::Imported(ImportedAux {
|
||||
header_only: false,
|
||||
clear_justification_requests: false,
|
||||
needs_justification: false,
|
||||
bad_justification: false,
|
||||
is_new_best: true,
|
||||
})
|
||||
);
|
||||
|
||||
assert_eq!(self.client.chain_info().best_number, 1);
|
||||
}
|
||||
|
||||
/// Database path for the current context.
|
||||
pub fn path(&self) -> &Path {
|
||||
self.db_guard.path()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Utilities to build a `TestClient` for `kitchensink-runtime`.
|
||||
|
||||
use pezsp_runtime::BuildStorage;
|
||||
/// Re-export test-client utilities.
|
||||
pub use bizinikiwi_test_client::*;
|
||||
|
||||
/// Call executor for `kitchensink-runtime` `TestClient`.
|
||||
use node_cli::service::RuntimeExecutor;
|
||||
|
||||
/// Default backend type.
|
||||
pub type Backend = pezsc_client_db::Backend<node_primitives::Block>;
|
||||
|
||||
/// Test client type.
|
||||
pub type Client = client::Client<
|
||||
Backend,
|
||||
client::LocalCallExecutor<node_primitives::Block, Backend, RuntimeExecutor>,
|
||||
node_primitives::Block,
|
||||
kitchensink_runtime::RuntimeApi,
|
||||
>;
|
||||
|
||||
/// Genesis configuration parameters for `TestClient`.
|
||||
#[derive(Default)]
|
||||
pub struct GenesisParameters;
|
||||
|
||||
impl bizinikiwi_test_client::GenesisInit for GenesisParameters {
|
||||
fn genesis_storage(&self) -> Storage {
|
||||
let mut storage = crate::genesis::config().build_storage().unwrap();
|
||||
storage.top.insert(
|
||||
pezsp_core::storage::well_known_keys::CODE.to_vec(),
|
||||
kitchensink_runtime::wasm_binary_unwrap().into(),
|
||||
);
|
||||
storage
|
||||
}
|
||||
}
|
||||
|
||||
/// A `test-runtime` extensions to `TestClientBuilder`.
|
||||
pub trait TestClientBuilderExt: Sized {
|
||||
/// Create test client builder.
|
||||
fn new() -> Self;
|
||||
|
||||
/// Build the test client.
|
||||
fn build(self) -> Client;
|
||||
}
|
||||
|
||||
impl TestClientBuilderExt
|
||||
for bizinikiwi_test_client::TestClientBuilder<
|
||||
node_primitives::Block,
|
||||
client::LocalCallExecutor<node_primitives::Block, Backend, RuntimeExecutor>,
|
||||
Backend,
|
||||
GenesisParameters,
|
||||
>
|
||||
{
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
fn build(self) -> Client {
|
||||
let executor = RuntimeExecutor::builder().build();
|
||||
use pezsc_service::client::LocalCallExecutor;
|
||||
use std::sync::Arc;
|
||||
let executor = LocalCallExecutor::new(
|
||||
self.backend().clone(),
|
||||
executor.clone(),
|
||||
Default::default(),
|
||||
ExecutionExtensions::new(None, Arc::new(executor)),
|
||||
)
|
||||
.expect("Creates LocalCallExecutor");
|
||||
self.build_with_executor(executor).0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Genesis Configuration.
|
||||
|
||||
use crate::keyring::*;
|
||||
use kitchensink_runtime::{
|
||||
constants::currency::*, AccountId, AssetsConfig, BalancesConfig, IndicesConfig,
|
||||
RuntimeGenesisConfig, SessionConfig, SocietyConfig, StakerStatus, StakingConfig,
|
||||
};
|
||||
use pezsp_keyring::Ed25519Keyring;
|
||||
use pezsp_runtime::Perbill;
|
||||
|
||||
/// Create genesis runtime configuration for tests.
|
||||
pub fn config() -> RuntimeGenesisConfig {
|
||||
config_endowed(Default::default())
|
||||
}
|
||||
|
||||
/// Create genesis runtime configuration for tests with some extra
|
||||
/// endowed accounts.
|
||||
pub fn config_endowed(extra_endowed: Vec<AccountId>) -> RuntimeGenesisConfig {
|
||||
let mut endowed = vec![
|
||||
(alice(), 111 * DOLLARS),
|
||||
(bob(), 100 * DOLLARS),
|
||||
(charlie(), 100_000_000 * DOLLARS),
|
||||
(dave(), 112 * DOLLARS),
|
||||
(eve(), 101 * DOLLARS),
|
||||
(ferdie(), 101 * DOLLARS),
|
||||
];
|
||||
|
||||
endowed.extend(extra_endowed.into_iter().map(|endowed| (endowed, 100 * DOLLARS)));
|
||||
|
||||
RuntimeGenesisConfig {
|
||||
indices: IndicesConfig { indices: vec![] },
|
||||
balances: BalancesConfig { balances: endowed, ..Default::default() },
|
||||
session: SessionConfig {
|
||||
keys: vec![
|
||||
(alice(), dave(), session_keys_from_seed(Ed25519Keyring::Alice.into())),
|
||||
(bob(), eve(), session_keys_from_seed(Ed25519Keyring::Bob.into())),
|
||||
(charlie(), ferdie(), session_keys_from_seed(Ed25519Keyring::Charlie.into())),
|
||||
],
|
||||
..Default::default()
|
||||
},
|
||||
staking: StakingConfig {
|
||||
stakers: vec![
|
||||
(dave(), dave(), 111 * DOLLARS, StakerStatus::Validator),
|
||||
(eve(), eve(), 100 * DOLLARS, StakerStatus::Validator),
|
||||
(ferdie(), ferdie(), 100 * DOLLARS, StakerStatus::Validator),
|
||||
],
|
||||
validator_count: 3,
|
||||
minimum_validator_count: 0,
|
||||
slash_reward_fraction: Perbill::from_percent(10),
|
||||
invulnerables: vec![alice(), bob(), charlie()],
|
||||
..Default::default()
|
||||
},
|
||||
society: SocietyConfig { pot: 0 },
|
||||
assets: AssetsConfig { assets: vec![(9, alice(), true, 1)], ..Default::default() },
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Test accounts.
|
||||
|
||||
use codec::Encode;
|
||||
use kitchensink_runtime::{CheckedExtrinsic, SessionKeys, TxExtension, UncheckedExtrinsic};
|
||||
use node_primitives::{AccountId, Balance, Nonce};
|
||||
use pezsp_core::{crypto::get_public_from_string_or_panic, ecdsa, ed25519, sr25519};
|
||||
use pezsp_crypto_hashing::blake2_256;
|
||||
use pezsp_keyring::Sr25519Keyring;
|
||||
use pezsp_runtime::generic::{self, Era, ExtrinsicFormat};
|
||||
|
||||
/// Alice's account id.
|
||||
pub fn alice() -> AccountId {
|
||||
Sr25519Keyring::Alice.into()
|
||||
}
|
||||
|
||||
/// Bob's account id.
|
||||
pub fn bob() -> AccountId {
|
||||
Sr25519Keyring::Bob.into()
|
||||
}
|
||||
|
||||
/// Charlie's account id.
|
||||
pub fn charlie() -> AccountId {
|
||||
Sr25519Keyring::Charlie.into()
|
||||
}
|
||||
|
||||
/// Dave's account id.
|
||||
pub fn dave() -> AccountId {
|
||||
Sr25519Keyring::Dave.into()
|
||||
}
|
||||
|
||||
/// Eve's account id.
|
||||
pub fn eve() -> AccountId {
|
||||
Sr25519Keyring::Eve.into()
|
||||
}
|
||||
|
||||
/// Ferdie's account id.
|
||||
pub fn ferdie() -> AccountId {
|
||||
Sr25519Keyring::Ferdie.into()
|
||||
}
|
||||
|
||||
/// Convert keyrings into `SessionKeys`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Function will panic when invalid string is provided.
|
||||
pub fn session_keys_from_seed(seed: &str) -> SessionKeys {
|
||||
SessionKeys {
|
||||
grandpa: get_public_from_string_or_panic::<ed25519::Public>(seed).into(),
|
||||
babe: get_public_from_string_or_panic::<sr25519::Public>(seed).into(),
|
||||
im_online: get_public_from_string_or_panic::<sr25519::Public>(seed).into(),
|
||||
authority_discovery: get_public_from_string_or_panic::<sr25519::Public>(seed).into(),
|
||||
mixnet: get_public_from_string_or_panic::<sr25519::Public>(seed).into(),
|
||||
beefy: get_public_from_string_or_panic::<ecdsa::Public>(seed).into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns transaction extra.
|
||||
pub fn tx_ext(nonce: Nonce, extra_fee: Balance) -> TxExtension {
|
||||
(
|
||||
pezframe_system::AuthorizeCall::new(),
|
||||
pezframe_system::CheckNonZeroSender::new(),
|
||||
pezframe_system::CheckSpecVersion::new(),
|
||||
pezframe_system::CheckTxVersion::new(),
|
||||
pezframe_system::CheckGenesis::new(),
|
||||
pezframe_system::CheckEra::from(Era::mortal(256, 0)),
|
||||
pezframe_system::CheckNonce::from(nonce),
|
||||
pezframe_system::CheckWeight::new(),
|
||||
pezpallet_skip_feeless_payment::SkipCheckIfFeeless::from(
|
||||
pezpallet_asset_conversion_tx_payment::ChargeAssetTxPayment::from(extra_fee, None),
|
||||
),
|
||||
pezframe_metadata_hash_extension::CheckMetadataHash::new(false),
|
||||
pezpallet_revive::evm::tx_extension::SetOrigin::default(),
|
||||
pezframe_system::WeightReclaim::new(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Sign given `CheckedExtrinsic`.
|
||||
pub fn sign(
|
||||
xt: CheckedExtrinsic,
|
||||
spec_version: u32,
|
||||
tx_version: u32,
|
||||
genesis_hash: [u8; 32],
|
||||
metadata_hash: Option<[u8; 32]>,
|
||||
) -> UncheckedExtrinsic {
|
||||
match xt.format {
|
||||
ExtrinsicFormat::Signed(signed, tx_ext) => {
|
||||
let payload = (
|
||||
xt.function,
|
||||
tx_ext.clone(),
|
||||
spec_version,
|
||||
tx_version,
|
||||
genesis_hash,
|
||||
genesis_hash,
|
||||
metadata_hash,
|
||||
);
|
||||
let key = Sr25519Keyring::from_account_id(&signed).unwrap();
|
||||
let signature =
|
||||
payload
|
||||
.using_encoded(|b| {
|
||||
if b.len() > 256 {
|
||||
key.sign(&blake2_256(b))
|
||||
} else {
|
||||
key.sign(b)
|
||||
}
|
||||
})
|
||||
.into();
|
||||
generic::UncheckedExtrinsic::new_signed(
|
||||
payload.0,
|
||||
pezsp_runtime::MultiAddress::Id(signed),
|
||||
signature,
|
||||
tx_ext,
|
||||
)
|
||||
.into()
|
||||
},
|
||||
ExtrinsicFormat::Bare => generic::UncheckedExtrinsic::new_bare(xt.function).into(),
|
||||
ExtrinsicFormat::General(ext_version, tx_ext) => generic::UncheckedExtrinsic::from_parts(
|
||||
xt.function,
|
||||
generic::Preamble::General(ext_version, tx_ext),
|
||||
)
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! A set of testing utilities for Bizinikiwi Node.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
pub mod bench;
|
||||
pub mod client;
|
||||
pub mod genesis;
|
||||
pub mod keyring;
|
||||
@@ -0,0 +1,51 @@
|
||||
[package]
|
||||
name = "pezstaging-chain-spec-builder"
|
||||
version = "1.6.1"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
build = "build.rs"
|
||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
publish = true
|
||||
description = "Utility for building chain-specification files for Bizinikiwi-based runtimes based on `pezsp-genesis-builder`"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[[bin]]
|
||||
path = "bin/main.rs"
|
||||
name = "chain-spec-builder"
|
||||
|
||||
[lib]
|
||||
# Docs tests are not needed since the code samples that would be executed
|
||||
# are exercised already in the context of unit/integration tests, by virtue
|
||||
# of using a combination of encapsulation in functions + `docify::export`.
|
||||
# This is a practice we should use for new code samples if any.
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
clap = { features = ["derive"], workspace = true }
|
||||
docify = { workspace = true }
|
||||
pezsc-chain-spec = { features = [
|
||||
"clap",
|
||||
], workspace = true, default-features = true }
|
||||
serde = { workspace = true, default-features = true }
|
||||
serde_json = { workspace = true, default-features = true }
|
||||
pezsp-tracing = { workspace = true, default-features = true }
|
||||
|
||||
[dev-dependencies]
|
||||
cmd_lib = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
bizinikiwi-test-runtime = { workspace = true }
|
||||
|
||||
[features]
|
||||
# `cargo build --feature=generate-readme` updates the `README.md` file.
|
||||
generate-readme = []
|
||||
runtime-benchmarks = [
|
||||
"pezsc-chain-spec/runtime-benchmarks",
|
||||
"bizinikiwi-test-runtime/runtime-benchmarks",
|
||||
]
|
||||
@@ -0,0 +1,168 @@
|
||||
# Chain Spec Builder
|
||||
|
||||
Bizinikiwi's chain spec builder utility.
|
||||
|
||||
A chain-spec is short for `chain-specification`. See the [`pezsc-chain-spec`](https://crates.io/docs.rs/pezsc-chain-spec/latest/sc_chain_spec)
|
||||
for more information.
|
||||
|
||||
_Note:_ this binary is a more flexible alternative to the `build-spec` subcommand, contained in typical Bizinikiwi-based nodes.
|
||||
This particular binary is capable of interacting with [`pezsp-genesis-builder`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/index.html)
|
||||
implementation of any provided runtime allowing to build chain-spec JSON files.
|
||||
|
||||
See [`ChainSpecBuilderCmd`](https://docs.rs/pezstaging-chain-spec-builder/6.0.0/staging_chain_spec_builder/enum.ChainSpecBuilderCmd.html)
|
||||
for a list of available commands.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
cargo install pezstaging-chain-spec-builder --locked
|
||||
```
|
||||
|
||||
_Note:_ `chain-spec-builder` binary is published on [crates.io](https://crates.io) under
|
||||
[`pezstaging-chain-spec-builder`](https://crates.io/crates/pezstaging-chain-spec-builder) due to a name conflict.
|
||||
|
||||
## Usage
|
||||
|
||||
Please note that below usage is backed by integration tests. The commands' examples are wrapped
|
||||
around by the `bash!(...)` macro calls.
|
||||
|
||||
### Note for `CreateCmd`'s `para-id` flag
|
||||
|
||||
<!-- TODO: https://github.com/pezkuwichain/pezkuwi-sdk/issues/156 -->
|
||||
Runtimes relying on generating the chain specification with this tool should
|
||||
implement `cumulus_primitives_core::GetTeyrchainInfo` trait, a new runtime API
|
||||
designed to provide the teyrchain ID from the `teyrchain-info`
|
||||
pallet. The `para-id` flag can be used though if the runtime does not implement
|
||||
the runtime API, and the teyrchain id will be fetched by the node from chain
|
||||
specification. This can be especially useful when syncing a node from a state
|
||||
where the runtime does not implement `cumulus_primitives_core::GetTeyrchainInfo`.
|
||||
|
||||
For reference, generating a chain specification with a `para_id` field can be
|
||||
done like below:
|
||||
|
||||
```bash
|
||||
chain-spec-builder -c "/dev/stdout" create --relay-chain "dev" --para-id 1000 -r $runtime_path named-preset "staging"
|
||||
```
|
||||
|
||||
### Generate chains-spec using default config from runtime
|
||||
|
||||
Query the default genesis config from the provided runtime WASM blob and use it in the chain spec.
|
||||
|
||||
<!-- docify::embed!("tests/test.rs", cmd_create_default) -->
|
||||
|
||||
_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset)
|
||||
runtime function is called.
|
||||
|
||||
### Display the runtime's default `GenesisConfig`
|
||||
|
||||
<!-- docify::embed!("tests/test.rs", cmd_display_default_preset) -->
|
||||
|
||||
_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset)
|
||||
runtime function is called.
|
||||
|
||||
### Display the `GenesisConfig` preset with given name
|
||||
|
||||
<!-- docify::embed!("tests/test.rs", cmd_display_preset)-->
|
||||
|
||||
_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset)
|
||||
runtime function is called.
|
||||
|
||||
### List the names of `GenesisConfig` presets provided by runtime
|
||||
|
||||
<!-- docify::embed!("tests/test.rs", cmd_list_presets)-->
|
||||
|
||||
_Note:_ [`GenesisBuilder::preset_names`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.preset_names)
|
||||
runtime function is called.
|
||||
|
||||
### Generate chain spec using runtime provided genesis config preset
|
||||
|
||||
Patch the runtime's default genesis config with the named preset provided by the runtime and generate the plain
|
||||
version of chain spec:
|
||||
|
||||
<!-- docify::embed!("tests/test.rs", cmd_create_with_named_preset)-->
|
||||
|
||||
_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset)
|
||||
runtime functions are called.
|
||||
|
||||
### Generate raw storage chain spec using genesis config patch
|
||||
|
||||
Patch the runtime's default genesis config with provided `patch.json` and generate raw
|
||||
storage (`-s`) version of chain spec:
|
||||
|
||||
<!-- docify::embed!("tests/test.rs", cmd_create_with_patch_raw)-->
|
||||
|
||||
Refer to [_patch file_](#patch-file) for some details on the patch file format.
|
||||
|
||||
_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset)
|
||||
and
|
||||
[`GenesisBuilder::build_state`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.build_state)
|
||||
runtime functions are called.
|
||||
|
||||
### Generate raw storage chain spec using full genesis config
|
||||
|
||||
Build the chain spec using provided full genesis config json file. No defaults will be used:
|
||||
|
||||
<!-- docify::embed!("tests/test.rs", cmd_create_full_raw)-->
|
||||
|
||||
Refer to [_full config file_](#full-genesis-config-file) for some details on the full file format.
|
||||
|
||||
_Note_: [`GenesisBuilder::build_state`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.build_state)
|
||||
runtime function is called.
|
||||
|
||||
### Generate human readable chain spec using provided genesis config patch
|
||||
|
||||
<!-- docify::embed!("tests/test.rs", cmd_create_with_patch_plain)-->
|
||||
|
||||
Refer to [_patch file_](#patch-file) for some details on the patch file format.
|
||||
|
||||
### Generate human readable chain spec using provided full genesis config
|
||||
|
||||
<!-- docify::embed!("tests/test.rs", cmd_create_full_plain)-->
|
||||
|
||||
Refer to [_full config file_](#full-genesis-config-file) for some details on the full file format.
|
||||
|
||||
## Patch and full genesis config files
|
||||
|
||||
This section provides details on the files that can be used with `create patch` or `create full` subcommands.
|
||||
|
||||
### Patch file
|
||||
|
||||
The patch file for genesis config contains the key-value pairs valid for given runtime, that needs to be customized,
|
||||
e.g:
|
||||
|
||||
```ignore
|
||||
{
|
||||
"balances": {
|
||||
"balances": [
|
||||
[
|
||||
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
|
||||
1000000000000000
|
||||
],
|
||||
[
|
||||
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
|
||||
1000000000000000
|
||||
],
|
||||
[
|
||||
"5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b",
|
||||
5000000000000000
|
||||
]
|
||||
]
|
||||
},
|
||||
"sudo": {
|
||||
"key": "5Ff3iXP75ruzroPWRP2FYBHWnmGGBSb63857BgnzCoXNxfPo"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The rest of genesis config keys will be initialized with default values.
|
||||
|
||||
### Full genesis config file
|
||||
|
||||
The full genesis config file must contain values for _all_ the keys present in the genesis config for given runtime. The
|
||||
format of the file is similar to patch format. Example is not provided here as it heavily depends on the runtime.
|
||||
|
||||
### Extra tools
|
||||
|
||||
The `chain-spec-builder` provides also some extra utilities: [`VerifyCmd`](https://docs.rs/pezstaging-chain-spec-builder/latest/staging_chain_spec_builder/struct.VerifyCmd.html),
|
||||
[`ConvertToRawCmd`](https://docs.rs/pezstaging-chain-spec-builder/latest/staging_chain_spec_builder/struct.ConvertToRawCmd.html),
|
||||
[`UpdateCodeCmd`](https://docs.rs/pezstaging-chain-spec-builder/latest/staging_chain_spec_builder/struct.UpdateCodeCmd.html).
|
||||
@@ -0,0 +1,206 @@
|
||||
# Chain Spec Builder
|
||||
|
||||
Bizinikiwi's chain spec builder utility.
|
||||
|
||||
A chain-spec is short for `chain-specification`. See the [`pezsc-chain-spec`](https://crates.io/docs.rs/pezsc-chain-spec/latest/sc_chain_spec)
|
||||
for more information.
|
||||
|
||||
_Note:_ this binary is a more flexible alternative to the `build-spec` subcommand, contained in typical Bizinikiwi-based nodes.
|
||||
This particular binary is capable of interacting with [`pezsp-genesis-builder`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/index.html)
|
||||
implementation of any provided runtime allowing to build chain-spec JSON files.
|
||||
|
||||
See [`ChainSpecBuilderCmd`](https://docs.rs/pezstaging-chain-spec-builder/6.0.0/staging_chain_spec_builder/enum.ChainSpecBuilderCmd.html)
|
||||
for a list of available commands.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
cargo install pezstaging-chain-spec-builder --locked
|
||||
```
|
||||
|
||||
_Note:_ `chain-spec-builder` binary is published on [crates.io](https://crates.io) under
|
||||
[`pezstaging-chain-spec-builder`](https://crates.io/crates/pezstaging-chain-spec-builder) due to a name conflict.
|
||||
|
||||
## Usage
|
||||
|
||||
Please note that below usage is backed by integration tests. The commands' examples are wrapped
|
||||
around by the `bash!(...)` macro calls.
|
||||
|
||||
### Note for `CreateCmd`'s `para-id` flag
|
||||
|
||||
<!-- TODO: https://github.com/pezkuwichain/pezkuwi-sdk/issues/156 -->
|
||||
Runtimes relying on generating the chain specification with this tool should
|
||||
implement `cumulus_primitives_core::GetTeyrchainInfo` trait, a new runtime API
|
||||
designed to provide the teyrchain ID from the `teyrchain-info`
|
||||
pallet. The `para-id` flag can be used though if the runtime does not implement
|
||||
the runtime API, and the teyrchain id will be fetched by the node from chain
|
||||
specification. This can be especially useful when syncing a node from a state
|
||||
where the runtime does not implement `cumulus_primitives_core::GetTeyrchainInfo`.
|
||||
|
||||
For reference, generating a chain specification with a `para_id` field can be
|
||||
done like below:
|
||||
|
||||
```bash
|
||||
chain-spec-builder -c "/dev/stdout" create --relay-chain "dev" --para-id 1000 -r $runtime_path named-preset "staging"
|
||||
```
|
||||
|
||||
### Generate chains-spec using default config from runtime
|
||||
|
||||
Query the default genesis config from the provided runtime WASM blob and use it in the chain spec.
|
||||
|
||||
```rust,ignore
|
||||
bash!(
|
||||
chain-spec-builder -c "/dev/stdout" create -r $runtime_path default
|
||||
)
|
||||
```
|
||||
|
||||
_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset)
|
||||
runtime function is called.
|
||||
|
||||
### Display the runtime's default `GenesisConfig`
|
||||
|
||||
```rust,ignore
|
||||
bash!(
|
||||
chain-spec-builder display-preset -r $runtime_path
|
||||
)
|
||||
```
|
||||
|
||||
_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset)
|
||||
runtime function is called.
|
||||
|
||||
### Display the `GenesisConfig` preset with given name
|
||||
|
||||
```rust,ignore
|
||||
fn cmd_display_preset(runtime_path: &str) -> String {
|
||||
bash!(
|
||||
chain-spec-builder display-preset -r $runtime_path -p "staging"
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset)
|
||||
runtime function is called.
|
||||
|
||||
### List the names of `GenesisConfig` presets provided by runtime
|
||||
|
||||
```rust,ignore
|
||||
bash!(
|
||||
chain-spec-builder list-presets -r $runtime_path
|
||||
)
|
||||
```
|
||||
|
||||
_Note:_ [`GenesisBuilder::preset_names`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.preset_names)
|
||||
runtime function is called.
|
||||
|
||||
### Generate chain spec using runtime provided genesis config preset
|
||||
|
||||
Patch the runtime's default genesis config with the named preset provided by the runtime and generate the plain
|
||||
version of chain spec:
|
||||
|
||||
```rust,ignore
|
||||
bash!(
|
||||
chain-spec-builder -c "/dev/stdout" create --relay-chain "dev" -r $runtime_path named-preset "staging"
|
||||
)
|
||||
```
|
||||
|
||||
_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset)
|
||||
runtime functions are called.
|
||||
|
||||
### Generate raw storage chain spec using genesis config patch
|
||||
|
||||
Patch the runtime's default genesis config with provided `patch.json` and generate raw
|
||||
storage (`-s`) version of chain spec:
|
||||
|
||||
```rust,ignore
|
||||
bash!(
|
||||
chain-spec-builder -c "/dev/stdout" create -s -r $runtime_path patch "tests/input/patch.json"
|
||||
)
|
||||
```
|
||||
|
||||
Refer to [_patch file_](#patch-file) for some details on the patch file format.
|
||||
|
||||
_Note:_ [`GenesisBuilder::get_preset`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset)
|
||||
and
|
||||
[`GenesisBuilder::build_state`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.build_state)
|
||||
runtime functions are called.
|
||||
|
||||
### Generate raw storage chain spec using full genesis config
|
||||
|
||||
Build the chain spec using provided full genesis config json file. No defaults will be used:
|
||||
|
||||
```rust,ignore
|
||||
bash!(
|
||||
chain-spec-builder -c "/dev/stdout" create -s -r $runtime_path full "tests/input/full.json"
|
||||
)
|
||||
```
|
||||
|
||||
Refer to [_full config file_](#full-genesis-config-file) for some details on the full file format.
|
||||
|
||||
_Note_: [`GenesisBuilder::build_state`](https://docs.rs/pezsp-genesis-builder/latest/sp_genesis_builder/trait.GenesisBuilder.html#method.build_state)
|
||||
runtime function is called.
|
||||
|
||||
### Generate human readable chain spec using provided genesis config patch
|
||||
|
||||
```rust,ignore
|
||||
bash!(
|
||||
chain-spec-builder -c "/dev/stdout" create -r $runtime_path patch "tests/input/patch.json"
|
||||
)
|
||||
```
|
||||
|
||||
Refer to [_patch file_](#patch-file) for some details on the patch file format.
|
||||
|
||||
### Generate human readable chain spec using provided full genesis config
|
||||
|
||||
```rust,ignore
|
||||
bash!(
|
||||
chain-spec-builder -c "/dev/stdout" create -r $runtime_path full "tests/input/full.json"
|
||||
)
|
||||
```
|
||||
|
||||
Refer to [_full config file_](#full-genesis-config-file) for some details on the full file format.
|
||||
|
||||
## Patch and full genesis config files
|
||||
|
||||
This section provides details on the files that can be used with `create patch` or `create full` subcommands.
|
||||
|
||||
### Patch file
|
||||
|
||||
The patch file for genesis config contains the key-value pairs valid for given runtime, that needs to be customized,
|
||||
e.g:
|
||||
|
||||
```ignore
|
||||
{
|
||||
"balances": {
|
||||
"balances": [
|
||||
[
|
||||
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
|
||||
1000000000000000
|
||||
],
|
||||
[
|
||||
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
|
||||
1000000000000000
|
||||
],
|
||||
[
|
||||
"5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b",
|
||||
5000000000000000
|
||||
]
|
||||
]
|
||||
},
|
||||
"sudo": {
|
||||
"key": "5Ff3iXP75ruzroPWRP2FYBHWnmGGBSb63857BgnzCoXNxfPo"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The rest of genesis config keys will be initialized with default values.
|
||||
|
||||
### Full genesis config file
|
||||
|
||||
The full genesis config file must contain values for _all_ the keys present in the genesis config for given runtime. The
|
||||
format of the file is similar to patch format. Example is not provided here as it heavily depends on the runtime.
|
||||
|
||||
### Extra tools
|
||||
|
||||
The `chain-spec-builder` provides also some extra utilities: [`VerifyCmd`](https://docs.rs/pezstaging-chain-spec-builder/latest/staging_chain_spec_builder/struct.VerifyCmd.html),
|
||||
[`ConvertToRawCmd`](https://docs.rs/pezstaging-chain-spec-builder/latest/staging_chain_spec_builder/struct.ConvertToRawCmd.html),
|
||||
[`UpdateCodeCmd`](https://docs.rs/pezstaging-chain-spec-builder/latest/staging_chain_spec_builder/struct.UpdateCodeCmd.html).
|
||||
@@ -0,0 +1,36 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use chain_spec_builder::ChainSpecBuilder;
|
||||
use clap::Parser;
|
||||
use pezstaging_chain_spec_builder as chain_spec_builder;
|
||||
|
||||
//avoid error message escaping
|
||||
fn main() {
|
||||
match inner_main() {
|
||||
Err(e) => eprintln!("{}", format!("{e}")),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn inner_main() -> Result<(), String> {
|
||||
pezsp_tracing::try_init_simple();
|
||||
|
||||
let builder = ChainSpecBuilder::parse();
|
||||
builder.run()
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
if let Ok(profile) = env::var("PROFILE") {
|
||||
println!("cargo:rustc-cfg=build_type=\"{}\"", profile);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,478 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#![doc = include_str!("../README.md")]
|
||||
#[cfg(feature = "generate-readme")]
|
||||
docify::compile_markdown!("README.docify.md", "README.md");
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use pezsc_chain_spec::{
|
||||
json_patch, set_code_substitute_in_json_chain_spec, update_code_in_json_chain_spec, ChainType,
|
||||
GenericChainSpec, GenesisConfigBuilderRuntimeCaller,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
/// A utility to easily create a chain spec definition.
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(rename_all = "kebab-case", version, about)]
|
||||
pub struct ChainSpecBuilder {
|
||||
#[command(subcommand)]
|
||||
pub command: ChainSpecBuilderCmd,
|
||||
/// The path where the chain spec should be saved.
|
||||
#[arg(long, short, default_value = "./chain_spec.json")]
|
||||
pub chain_spec_path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[command(rename_all = "kebab-case")]
|
||||
pub enum ChainSpecBuilderCmd {
|
||||
Create(CreateCmd),
|
||||
Verify(VerifyCmd),
|
||||
UpdateCode(UpdateCodeCmd),
|
||||
ConvertToRaw(ConvertToRawCmd),
|
||||
ListPresets(ListPresetsCmd),
|
||||
DisplayPreset(DisplayPresetCmd),
|
||||
AddCodeSubstitute(AddCodeSubstituteCmd),
|
||||
}
|
||||
|
||||
/// Create a new chain spec by interacting with the provided runtime wasm blob.
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct CreateCmd {
|
||||
/// The name of chain.
|
||||
#[arg(long, short = 'n', default_value = "Custom")]
|
||||
chain_name: String,
|
||||
/// The chain id.
|
||||
#[arg(long, short = 'i', default_value = "custom")]
|
||||
chain_id: String,
|
||||
/// The chain type.
|
||||
#[arg(value_enum, short = 't', default_value = "live")]
|
||||
chain_type: ChainType,
|
||||
/// The para ID for your chain.
|
||||
#[arg(long, value_enum, short = 'p', requires = "relay_chain")]
|
||||
pub para_id: Option<u32>,
|
||||
/// The relay chain you wish to connect to.
|
||||
#[arg(long, value_enum, short = 'c')]
|
||||
pub relay_chain: Option<String>,
|
||||
/// The path to runtime wasm blob.
|
||||
#[arg(long, short, alias = "runtime-wasm-path")]
|
||||
runtime: PathBuf,
|
||||
/// Export chainspec as raw storage.
|
||||
#[arg(long, short = 's')]
|
||||
raw_storage: bool,
|
||||
/// Verify the genesis config. This silently generates the raw storage from genesis config. Any
|
||||
/// errors will be reported.
|
||||
#[arg(long, short = 'v')]
|
||||
verify: bool,
|
||||
/// Chain properties in `KEY=VALUE` format.
|
||||
///
|
||||
/// Multiple `KEY=VALUE` entries can be specified and separated by a comma.
|
||||
///
|
||||
/// Example: `--properties tokenSymbol=UNIT,tokenDecimals=12,ss58Format=42,isEthereum=false`
|
||||
/// Or: `--properties tokenSymbol=UNIT --properties tokenDecimals=12 --properties ss58Format=42
|
||||
/// --properties=isEthereum=false`
|
||||
///
|
||||
/// The first uses comma as separation and the second passes the argument multiple times. Both
|
||||
/// styles can also be mixed.
|
||||
#[arg(long, default_value = "tokenSymbol=UNIT,tokenDecimals=12")]
|
||||
pub properties: Vec<String>,
|
||||
#[command(subcommand)]
|
||||
action: GenesisBuildAction,
|
||||
|
||||
/// Allows to provide the runtime code blob, instead of reading it from the provided file path.
|
||||
#[clap(skip)]
|
||||
code: Option<Cow<'static, [u8]>>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
enum GenesisBuildAction {
|
||||
Patch(PatchCmd),
|
||||
Full(FullCmd),
|
||||
Default(DefaultCmd),
|
||||
NamedPreset(NamedPresetCmd),
|
||||
}
|
||||
|
||||
/// Patches the runtime's default genesis config with provided patch.
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
struct PatchCmd {
|
||||
/// The path to the runtime genesis config patch.
|
||||
patch_path: PathBuf,
|
||||
}
|
||||
|
||||
/// Build the genesis config for runtime using provided json file. No defaults will be used.
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
struct FullCmd {
|
||||
/// The path to the full runtime genesis config json file.
|
||||
config_path: PathBuf,
|
||||
}
|
||||
|
||||
/// Gets the default genesis config for the runtime and uses it in ChainSpec. Please note that
|
||||
/// default genesis config may not be valid. For some runtimes initial values should be added there
|
||||
/// (e.g. session keys, babe epoch).
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
struct DefaultCmd {}
|
||||
|
||||
/// Uses named preset provided by runtime to build the chains spec.
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
struct NamedPresetCmd {
|
||||
preset_name: String,
|
||||
}
|
||||
|
||||
/// Updates the code in the provided input chain spec.
|
||||
///
|
||||
/// The code field of the chain spec will be updated with the runtime provided in the
|
||||
/// command line. This operation supports both plain and raw formats.
|
||||
///
|
||||
/// This command does not update chain-spec file in-place. The result of this command will be stored
|
||||
/// in a file given as `-c/--chain-spec-path` command line argument.
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
pub struct UpdateCodeCmd {
|
||||
/// Chain spec to be updated.
|
||||
///
|
||||
/// Please note that the file will not be updated in-place.
|
||||
pub input_chain_spec: PathBuf,
|
||||
/// The path to new runtime wasm blob to be stored into chain-spec.
|
||||
#[arg(alias = "runtime-wasm-path")]
|
||||
pub runtime: PathBuf,
|
||||
}
|
||||
|
||||
/// Add a code substitute in the chain spec.
|
||||
///
|
||||
/// The `codeSubstitute` object of the chain spec will be updated with the block height as key and
|
||||
/// runtime code as value. This operation supports both plain and raw formats. The `codeSubstitute`
|
||||
/// field instructs the node to use the provided runtime code at the given block height. This is
|
||||
/// useful when the chain can not progress on its own due to a bug that prevents block-building.
|
||||
///
|
||||
/// Note: For teyrchains, the validation function on the relaychain needs to be adjusted too,
|
||||
/// otherwise blocks built using the substituted teyrchain runtime will be rejected.
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
pub struct AddCodeSubstituteCmd {
|
||||
/// Chain spec to be updated.
|
||||
pub input_chain_spec: PathBuf,
|
||||
/// New runtime wasm blob that should replace the existing code.
|
||||
#[arg(alias = "runtime-wasm-path")]
|
||||
pub runtime: PathBuf,
|
||||
/// The block height at which the code should be substituted.
|
||||
pub block_height: u64,
|
||||
}
|
||||
|
||||
/// Converts the given chain spec into the raw format.
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
pub struct ConvertToRawCmd {
|
||||
/// Chain spec to be converted.
|
||||
pub input_chain_spec: PathBuf,
|
||||
}
|
||||
|
||||
/// Lists available presets
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
pub struct ListPresetsCmd {
|
||||
/// The path to runtime wasm blob.
|
||||
#[arg(long, short, alias = "runtime-wasm-path")]
|
||||
pub runtime: PathBuf,
|
||||
}
|
||||
|
||||
/// Displays given preset
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
pub struct DisplayPresetCmd {
|
||||
/// The path to runtime wasm blob.
|
||||
#[arg(long, short, alias = "runtime-wasm-path")]
|
||||
pub runtime: PathBuf,
|
||||
/// Preset to be displayed. If none is given default will be displayed.
|
||||
#[arg(long, short)]
|
||||
pub preset_name: Option<String>,
|
||||
}
|
||||
|
||||
/// Verifies the provided input chain spec.
|
||||
///
|
||||
/// Silently checks if given input chain spec can be converted to raw. It allows to check if all
|
||||
/// RuntimeGenesisConfig fields are properly initialized and if the json does not contain invalid
|
||||
/// fields.
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
pub struct VerifyCmd {
|
||||
/// Chain spec to be verified.
|
||||
pub input_chain_spec: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
pub struct TeyrchainExtension {
|
||||
/// The relay chain of the Teyrchain.
|
||||
pub relay_chain: String,
|
||||
/// The id of the Teyrchain.
|
||||
pub para_id: Option<u32>,
|
||||
}
|
||||
|
||||
type ChainSpec = GenericChainSpec<()>;
|
||||
|
||||
impl ChainSpecBuilder {
|
||||
/// Executes the internal command.
|
||||
pub fn run(&self) -> Result<(), String> {
|
||||
let chain_spec_path = self.chain_spec_path.to_path_buf();
|
||||
|
||||
match &self.command {
|
||||
ChainSpecBuilderCmd::Create(cmd) => {
|
||||
let chain_spec_json = generate_chain_spec_for_runtime(&cmd)?;
|
||||
fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?;
|
||||
},
|
||||
ChainSpecBuilderCmd::UpdateCode(UpdateCodeCmd {
|
||||
ref input_chain_spec,
|
||||
ref runtime,
|
||||
}) => {
|
||||
let mut chain_spec_json = extract_chain_spec_json(input_chain_spec.as_path())?;
|
||||
|
||||
update_code_in_json_chain_spec(
|
||||
&mut chain_spec_json,
|
||||
&fs::read(runtime.as_path())
|
||||
.map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..],
|
||||
);
|
||||
|
||||
let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json)
|
||||
.map_err(|e| format!("to pretty failed: {e}"))?;
|
||||
fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?;
|
||||
},
|
||||
ChainSpecBuilderCmd::AddCodeSubstitute(AddCodeSubstituteCmd {
|
||||
ref input_chain_spec,
|
||||
ref runtime,
|
||||
block_height,
|
||||
}) => {
|
||||
let mut chain_spec_json = extract_chain_spec_json(input_chain_spec.as_path())?;
|
||||
|
||||
set_code_substitute_in_json_chain_spec(
|
||||
&mut chain_spec_json,
|
||||
&fs::read(runtime.as_path())
|
||||
.map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..],
|
||||
*block_height,
|
||||
);
|
||||
let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json)
|
||||
.map_err(|e| format!("to pretty failed: {e}"))?;
|
||||
fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?;
|
||||
},
|
||||
ChainSpecBuilderCmd::ConvertToRaw(ConvertToRawCmd { ref input_chain_spec }) => {
|
||||
let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?;
|
||||
|
||||
let mut genesis_json =
|
||||
serde_json::from_str::<serde_json::Value>(&chain_spec.as_json(true)?)
|
||||
.map_err(|e| format!("Conversion to json failed: {e}"))?;
|
||||
|
||||
// We want to extract only raw genesis ("genesis::raw" key), and apply it as a patch
|
||||
// for the original json file.
|
||||
genesis_json.as_object_mut().map(|map| {
|
||||
map.retain(|key, _| key == "genesis");
|
||||
});
|
||||
|
||||
let mut org_chain_spec_json = extract_chain_spec_json(input_chain_spec.as_path())?;
|
||||
|
||||
// The original plain genesis ("genesis::runtimeGenesis") is no longer needed, so
|
||||
// just remove it:
|
||||
org_chain_spec_json
|
||||
.get_mut("genesis")
|
||||
.and_then(|genesis| genesis.as_object_mut())
|
||||
.and_then(|genesis| genesis.remove("runtimeGenesis"));
|
||||
json_patch::merge(&mut org_chain_spec_json, genesis_json);
|
||||
|
||||
let chain_spec_json = serde_json::to_string_pretty(&org_chain_spec_json)
|
||||
.map_err(|e| format!("Conversion to pretty failed: {e}"))?;
|
||||
fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?;
|
||||
},
|
||||
ChainSpecBuilderCmd::Verify(VerifyCmd { ref input_chain_spec }) => {
|
||||
let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?;
|
||||
serde_json::from_str::<serde_json::Value>(&chain_spec.as_json(true)?)
|
||||
.map_err(|e| format!("Conversion to json failed: {e}"))?;
|
||||
},
|
||||
ChainSpecBuilderCmd::ListPresets(ListPresetsCmd { runtime }) => {
|
||||
let code = fs::read(runtime.as_path())
|
||||
.map_err(|e| format!("wasm blob shall be readable {e}"))?;
|
||||
let caller: GenesisConfigBuilderRuntimeCaller =
|
||||
GenesisConfigBuilderRuntimeCaller::new(&code[..]);
|
||||
let presets = caller
|
||||
.preset_names()
|
||||
.map_err(|e| format!("getting default config from runtime should work: {e}"))?;
|
||||
println!("{}", serde_json::json!({"presets":presets}).to_string());
|
||||
},
|
||||
ChainSpecBuilderCmd::DisplayPreset(DisplayPresetCmd { runtime, preset_name }) => {
|
||||
let code = fs::read(runtime.as_path())
|
||||
.map_err(|e| format!("wasm blob shall be readable {e}"))?;
|
||||
let caller: GenesisConfigBuilderRuntimeCaller =
|
||||
GenesisConfigBuilderRuntimeCaller::new(&code[..]);
|
||||
let preset = caller
|
||||
.get_named_preset(preset_name.as_ref())
|
||||
.map_err(|e| format!("getting default config from runtime should work: {e}"))?;
|
||||
println!("{preset}");
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets the code used by [`CreateCmd`]
|
||||
///
|
||||
/// The file pointed by `CreateCmd::runtime` field will not be read. Provided blob will used
|
||||
/// instead for chain spec generation.
|
||||
pub fn set_create_cmd_runtime_code(&mut self, code: Cow<'static, [u8]>) {
|
||||
match &mut self.command {
|
||||
ChainSpecBuilderCmd::Create(cmd) => {
|
||||
cmd.code = Some(code);
|
||||
},
|
||||
_ => {
|
||||
panic!("Overwriting code blob is only supported for CreateCmd");
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn process_action<T: Serialize + Clone + Sync + 'static>(
|
||||
cmd: &CreateCmd,
|
||||
code: &[u8],
|
||||
builder: pezsc_chain_spec::ChainSpecBuilder<T>,
|
||||
) -> Result<String, String> {
|
||||
let builder = match cmd.action {
|
||||
GenesisBuildAction::NamedPreset(NamedPresetCmd { ref preset_name }) =>
|
||||
builder.with_genesis_config_preset_name(&preset_name),
|
||||
GenesisBuildAction::Patch(PatchCmd { ref patch_path }) => {
|
||||
let patch = fs::read(patch_path.as_path())
|
||||
.map_err(|e| format!("patch file {patch_path:?} shall be readable: {e}"))?;
|
||||
builder.with_genesis_config_patch(serde_json::from_slice::<Value>(&patch[..]).map_err(
|
||||
|e| format!("patch file {patch_path:?} shall contain a valid json: {e}"),
|
||||
)?)
|
||||
},
|
||||
GenesisBuildAction::Full(FullCmd { ref config_path }) => {
|
||||
let config = fs::read(config_path.as_path())
|
||||
.map_err(|e| format!("config file {config_path:?} shall be readable: {e}"))?;
|
||||
builder.with_genesis_config(serde_json::from_slice::<Value>(&config[..]).map_err(
|
||||
|e| format!("config file {config_path:?} shall contain a valid json: {e}"),
|
||||
)?)
|
||||
},
|
||||
GenesisBuildAction::Default(DefaultCmd {}) => {
|
||||
let caller: GenesisConfigBuilderRuntimeCaller =
|
||||
GenesisConfigBuilderRuntimeCaller::new(&code);
|
||||
let default_config = caller
|
||||
.get_default_config()
|
||||
.map_err(|e| format!("getting default config from runtime should work: {e}"))?;
|
||||
builder.with_genesis_config(default_config)
|
||||
},
|
||||
};
|
||||
|
||||
let chain_spec = builder.build();
|
||||
|
||||
match (cmd.verify, cmd.raw_storage) {
|
||||
(_, true) => chain_spec.as_json(true),
|
||||
(true, false) => {
|
||||
chain_spec.as_json(true)?;
|
||||
println!("Genesis config verification: OK");
|
||||
chain_spec.as_json(false)
|
||||
},
|
||||
(false, false) => chain_spec.as_json(false),
|
||||
}
|
||||
}
|
||||
|
||||
impl CreateCmd {
|
||||
/// Returns the associated runtime code.
|
||||
///
|
||||
/// If the code blob was previously set, returns it. Otherwise reads the file.
|
||||
fn get_runtime_code(&self) -> Result<Cow<'static, [u8]>, String> {
|
||||
Ok(if let Some(code) = self.code.clone() {
|
||||
code
|
||||
} else {
|
||||
fs::read(self.runtime.as_path())
|
||||
.map_err(|e| format!("wasm blob shall be readable {e}"))?
|
||||
.into()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses chain properties passed as a comma-separated KEY=VALUE pairs.
|
||||
fn parse_properties(raw: &String, props: &mut pezsc_chain_spec::Properties) -> Result<(), String> {
|
||||
for pair in raw.split(',') {
|
||||
let mut iter = pair.splitn(2, '=');
|
||||
let key = iter
|
||||
.next()
|
||||
.ok_or_else(|| format!("Invalid chain property key: {pair}"))?
|
||||
.trim()
|
||||
.to_owned();
|
||||
let value_str = iter
|
||||
.next()
|
||||
.ok_or_else(|| format!("Invalid chain property value for key: {key}"))?
|
||||
.trim();
|
||||
|
||||
// Try to parse as bool, number, or fallback to String
|
||||
let value = match value_str.parse::<bool>() {
|
||||
Ok(b) => Value::Bool(b),
|
||||
Err(_) => match value_str.parse::<u32>() {
|
||||
Ok(i) => Value::Number(i.into()),
|
||||
Err(_) => Value::String(value_str.to_string()),
|
||||
},
|
||||
};
|
||||
|
||||
props.insert(key, value);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Processes `CreateCmd` and returns string representation of JSON version of `ChainSpec`.
|
||||
pub fn generate_chain_spec_for_runtime(cmd: &CreateCmd) -> Result<String, String> {
|
||||
let code = cmd.get_runtime_code()?;
|
||||
|
||||
let chain_type = &cmd.chain_type;
|
||||
|
||||
let mut properties = pezsc_chain_spec::Properties::new();
|
||||
for raw in &cmd.properties {
|
||||
parse_properties(raw, &mut properties)?;
|
||||
}
|
||||
|
||||
let builder = ChainSpec::builder(&code[..], Default::default())
|
||||
.with_name(&cmd.chain_name[..])
|
||||
.with_id(&cmd.chain_id[..])
|
||||
.with_properties(properties)
|
||||
.with_chain_type(chain_type.clone());
|
||||
|
||||
let chain_spec_json_string = process_action(&cmd, &code[..], builder)?;
|
||||
let teyrchain_properties = cmd.relay_chain.as_ref().map(|rc| {
|
||||
cmd.para_id
|
||||
.map(|para_id| {
|
||||
serde_json::json!({
|
||||
"relay_chain": rc,
|
||||
"para_id": para_id,
|
||||
})
|
||||
})
|
||||
.unwrap_or(serde_json::json!({
|
||||
"relay_chain": rc,
|
||||
}))
|
||||
});
|
||||
|
||||
let chain_spec = teyrchain_properties
|
||||
.map(|props| {
|
||||
let chain_spec_json_blob = serde_json::from_str(chain_spec_json_string.as_str())
|
||||
.map_err(|e| format!("deserialization a json failed {e}"));
|
||||
chain_spec_json_blob.and_then(|mut cs| {
|
||||
json_patch::merge(&mut cs, props);
|
||||
serde_json::to_string_pretty(&cs).map_err(|e| format!("to pretty failed: {e}"))
|
||||
})
|
||||
})
|
||||
.unwrap_or(Ok(chain_spec_json_string));
|
||||
chain_spec
|
||||
}
|
||||
|
||||
/// Extract any chain spec and convert it to JSON
|
||||
fn extract_chain_spec_json(input_chain_spec: &Path) -> Result<serde_json::Value, String> {
|
||||
let chain_spec = &fs::read(input_chain_spec)
|
||||
.map_err(|e| format!("Provided chain spec could not be read: {e}"))?;
|
||||
|
||||
serde_json::from_slice(&chain_spec).map_err(|e| format!("Conversion to json failed: {e}"))
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"bootNodes": [],
|
||||
"chainType": "Live",
|
||||
"codeSubstitutes": {
|
||||
"100": "0x040506"
|
||||
},
|
||||
"custom_field": "custom_value",
|
||||
"genesis": {
|
||||
"runtimeGenesis": {
|
||||
"code": "0x010203",
|
||||
"config": {
|
||||
"babe": {
|
||||
"authorities": [],
|
||||
"epochConfig": {
|
||||
"allowed_slots": "PrimaryAndSecondaryVRFSlots",
|
||||
"c": [
|
||||
1,
|
||||
4
|
||||
]
|
||||
}
|
||||
},
|
||||
"balances": {
|
||||
"balances": []
|
||||
},
|
||||
"bizinikiwiTest": {
|
||||
"authorities": []
|
||||
},
|
||||
"system": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": "custom",
|
||||
"name": "Custom",
|
||||
"properties": {
|
||||
"tokenDecimals": 12,
|
||||
"tokenSymbol": "UNIT"
|
||||
},
|
||||
"protocolId": null,
|
||||
"relay_chain": "pezkuwichain-local",
|
||||
"telemetryEndpoints": null
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "Custom",
|
||||
"id": "custom",
|
||||
"chainType": "Live",
|
||||
"bootNodes": [],
|
||||
"telemetryEndpoints": null,
|
||||
"protocolId": null,
|
||||
"properties": {
|
||||
"tokenDecimals": 12,
|
||||
"tokenSymbol": "UNIT"
|
||||
},
|
||||
"relay_chain": "pezkuwichain-local",
|
||||
"custom_field": "custom_value",
|
||||
"codeSubstitutes": {},
|
||||
"genesis": {
|
||||
"raw": {
|
||||
"childrenDefault": {},
|
||||
"top": {
|
||||
"0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
|
||||
"0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x00",
|
||||
"0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
|
||||
"0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000",
|
||||
"0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000",
|
||||
"0x3a636f6465": "0x010203",
|
||||
"0x3a65787472696e7369635f696e646578": "0x00000000",
|
||||
"0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100",
|
||||
"0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x0000000000000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "Custom",
|
||||
"id": "custom",
|
||||
"chainType": "Live",
|
||||
"bootNodes": [],
|
||||
"telemetryEndpoints": null,
|
||||
"protocolId": null,
|
||||
"properties": {
|
||||
"tokenDecimals": 12,
|
||||
"tokenSymbol": "UNIT"
|
||||
},
|
||||
"codeSubstitutes": {},
|
||||
"genesis": {
|
||||
"runtimeGenesis": {
|
||||
"code": "0x010203",
|
||||
"config": {
|
||||
"babe": {
|
||||
"authorities": [],
|
||||
"epochConfig": {
|
||||
"allowed_slots": "PrimaryAndSecondaryVRFSlots",
|
||||
"c": [
|
||||
1,
|
||||
4
|
||||
]
|
||||
}
|
||||
},
|
||||
"balances": {
|
||||
"balances": [],
|
||||
"devAccounts": null
|
||||
},
|
||||
"bizinikiwiTest": {
|
||||
"authorities": []
|
||||
},
|
||||
"system": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "Custom",
|
||||
"id": "custom",
|
||||
"chainType": "Live",
|
||||
"bootNodes": [],
|
||||
"telemetryEndpoints": null,
|
||||
"protocolId": null,
|
||||
"properties": {
|
||||
"tokenDecimals": 12,
|
||||
"tokenSymbol": "UNIT"
|
||||
},
|
||||
"codeSubstitutes": {},
|
||||
"genesis": {
|
||||
"raw": {
|
||||
"top": {
|
||||
"0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
|
||||
"0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e",
|
||||
"0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
|
||||
"0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000",
|
||||
"0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92c2a60ec6dd16cd8ab911865ecf7555b186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e": "0x00000000000000000000000001000000000000000080e03779c311000000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080",
|
||||
"0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000",
|
||||
"0x3a636f6465": "0x010203",
|
||||
"0x3a65787472696e7369635f696e646578": "0x00000000",
|
||||
"0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100",
|
||||
"0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00806d8176de1800",
|
||||
"0xd5e1a2fa16732ce6906189438c0a82c64e7b9012096b41c4eb3aaf947f6ea429": "0x0000"
|
||||
},
|
||||
"childrenDefault": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "test_chain",
|
||||
"id": "100",
|
||||
"chainType": "Live",
|
||||
"bootNodes": [],
|
||||
"telemetryEndpoints": null,
|
||||
"protocolId": null,
|
||||
"properties": {
|
||||
"tokenDecimals": 12,
|
||||
"tokenSymbol": "UNIT"
|
||||
},
|
||||
"relay_chain": "pezkuwichain-local",
|
||||
"codeSubstitutes": {},
|
||||
"genesis": {
|
||||
"runtimeGenesis": {
|
||||
"code": "0x010203",
|
||||
"config": {
|
||||
"babe": {
|
||||
"authorities": [],
|
||||
"epochConfig": {
|
||||
"allowed_slots": "PrimaryAndSecondaryVRFSlots",
|
||||
"c": [
|
||||
1,
|
||||
4
|
||||
]
|
||||
}
|
||||
},
|
||||
"balances": {
|
||||
"balances": [],
|
||||
"devAccounts": null
|
||||
},
|
||||
"bizinikiwiTest": {
|
||||
"authorities": []
|
||||
},
|
||||
"system": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user