feat: Rebrand Polkadot/Substrate references to PezkuwiChain

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

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

This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "bizinikiwi-test-utils"
version = "4.0.0-dev"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "Bizinikiwi test utilities"
publish = false
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
+39
View File
@@ -0,0 +1,39 @@
[package]
name = "bizinikiwi-cli-test-utils"
description = "CLI testing utilities"
version = "0.1.0"
authors.workspace = true
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]
assert_cmd = { workspace = true }
futures = { workspace = true }
nix = { features = ["signal"], workspace = true }
node-cli = { workspace = true }
node-primitives = { workspace = true, default-features = true }
regex = { workspace = true }
pezsc-cli = { workspace = true, default-features = false }
pezsc-service = { workspace = true, default-features = false }
pezsp-rpc = { workspace = true, default-features = true }
bizinikiwi-rpc-client = { workspace = true, default-features = true }
tokio = { features = ["full"], workspace = true, default-features = true }
[features]
try-runtime = ["node-cli/try-runtime"]
runtime-benchmarks = [
"node-cli/runtime-benchmarks",
"node-primitives/runtime-benchmarks",
"pezsc-cli/runtime-benchmarks",
"pezsc-service/runtime-benchmarks",
"bizinikiwi-rpc-client/runtime-benchmarks",
]
+24
View File
@@ -0,0 +1,24 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::env;
fn main() {
if let Ok(profile) = env::var("PROFILE") {
println!("cargo:rustc-cfg=build_profile=\"{}\"", profile);
}
}
+357
View File
@@ -0,0 +1,357 @@
// 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(unix)]
use assert_cmd::cargo::cargo_bin;
use nix::{
sys::signal::{kill, Signal, Signal::SIGINT},
unistd::Pid,
};
use node_primitives::{Hash, Header};
use regex::Regex;
use pezsp_rpc::{list::ListOrValue, number::NumberOrHex};
use std::{
io::{BufRead, BufReader, Read},
ops::{Deref, DerefMut},
path::{Path, PathBuf},
process::{self, Child, Command},
time::Duration,
};
use tokio::io::{AsyncBufReadExt, AsyncRead};
/// Similar to [`crate::start_node`] spawns a node, but works in environments where the bizinikiwi
/// binary is not accessible with `cargo_bin("bizinikiwi-node")`, and allows customising the args
/// passed in.
///
/// Helpful if you need a Bizinikiwi dev node running in the background of a project external to
/// `bizinikiwi`.
///
/// The downside compared to using [`crate::start_node`] is that this method is blocking rather than
/// returning a [`Child`]. Therefore, you may want to call this method inside a new thread.
///
/// # Example
/// ```ignore
/// // Spawn a dev node.
/// let _ = std::thread::spawn(move || {
/// match common::start_node_inline(vec!["--dev", "--rpc-port=12345"]) {
/// Ok(_) => {}
/// Err(e) => {
/// panic!("Node exited with error: {}", e);
/// }
/// }
/// });
/// ```
pub fn start_node_inline(args: Vec<&str>) -> Result<(), pezsc_service::error::Error> {
use pezsc_cli::BizinikiwiCli;
// Prepend the args with some dummy value because the first arg is skipped.
let cli_call = std::iter::once("node-template").chain(args);
let cli = node_cli::Cli::from_iter(cli_call);
let runner = cli.create_runner(&cli.run).unwrap();
runner.run_node_until_exit(|config| async move { node_cli::service::new_full(config, cli) })
}
/// Starts a new Bizinikiwi node in development mode with a temporary chain.
///
/// This function creates a new Bizinikiwi node using the `bizinikiwi` binary.
/// It configures the node to run in development mode (`--dev`) with a temporary chain (`--tmp`),
/// sets the WebSocket port to 45789 (`--ws-port=45789`).
///
/// # Returns
///
/// A [`Child`] process representing the spawned Bizinikiwi node.
///
/// # Panics
///
/// This function will panic if the `bizinikiwi` binary is not found or if the node fails to start.
///
/// # Examples
///
/// ```ignore
/// use my_crate::start_node;
///
/// let child = start_node();
/// // Interact with the Bizinikiwi node using the WebSocket port 45789.
/// // When done, the node will be killed when the `child` is dropped.
/// ```
///
/// [`Child`]: std::process::Child
pub fn start_node() -> Child {
Command::new(cargo_bin("bizinikiwi-node"))
.stdout(process::Stdio::piped())
.stderr(process::Stdio::piped())
.args(&["--dev", "--tmp", "--rpc-port=45789", "--no-hardware-benchmarks"])
.spawn()
.unwrap()
}
/// Builds the Bizinikiwi project using the provided arguments.
///
/// This function reads the CARGO_MANIFEST_DIR environment variable to find the root workspace
/// directory. It then runs the `cargo b` command in the root directory with the specified
/// arguments.
///
/// This can be useful for building the Bizinikiwi binary with a desired set of features prior
/// to using the binary in a CLI test.
///
/// # Arguments
///
/// * `args: &[&str]` - A slice of string references representing the arguments to pass to the
/// `cargo b` command.
///
/// # Panics
///
/// This function will panic if:
///
/// * The CARGO_MANIFEST_DIR environment variable is not set.
/// * The root workspace directory cannot be determined.
/// * The 'cargo b' command fails to execute.
/// * The 'cargo b' command returns a non-successful status.
///
/// # Examples
///
/// ```ignore
/// build_bizinikiwi(&["--features=try-runtime"]);
/// ```
pub fn build_bizinikiwi(args: &[&str]) {
let is_release_build = !cfg!(build_profile = "debug");
// Get the root workspace directory from the CARGO_MANIFEST_DIR environment variable
let mut cmd = Command::new("cargo");
cmd.arg("build").arg("-p=pezstaging-node-cli");
if is_release_build {
cmd.arg("--release");
}
let output = cmd
.args(args)
.output()
.expect(format!("Failed to execute 'cargo b' with args {:?}'", args).as_str());
if !output.status.success() {
panic!(
"Failed to execute 'cargo b' with args {:?}': \n{}",
args,
String::from_utf8_lossy(&output.stderr)
);
}
}
/// Takes a readable tokio stream (e.g. from a child process `ChildStderr` or `ChildStdout`) and
/// a `Regex` pattern, and checks each line against the given pattern as it is produced.
/// The function returns OK(()) as soon as a line matching the pattern is found, or an Err if
/// the stream ends without any lines matching the pattern.
///
/// # Arguments
///
/// * `child_stream` - An async tokio stream, e.g. from a child process `ChildStderr` or
/// `ChildStdout`.
/// * `re` - A `Regex` pattern to search for in the stream.
///
/// # Returns
///
/// * `Ok(())` if a line matching the pattern is found.
/// * `Err(String)` if the stream ends without any lines matching the pattern.
///
/// # Examples
///
/// ```ignore
/// use regex::Regex;
/// use tokio::process::Command;
/// use tokio::io::AsyncRead;
///
/// # async fn run() {
/// let child = Command::new("some-command").stderr(std::process::Stdio::piped()).spawn().unwrap();
/// let stderr = child.stderr.unwrap();
/// let re = Regex::new("error:").unwrap();
///
/// match wait_for_pattern_match_in_stream(stderr, re).await {
/// Ok(()) => println!("Error found in stderr"),
/// Err(e) => println!("Error: {}", e),
/// }
/// # }
/// ```
pub async fn wait_for_stream_pattern_match<R>(stream: R, re: Regex) -> Result<(), String>
where
R: AsyncRead + Unpin,
{
let mut stdio_reader = tokio::io::BufReader::new(stream).lines();
while let Ok(Some(line)) = stdio_reader.next_line().await {
match re.find(line.as_str()) {
Some(_) => return Ok(()),
None => (),
}
}
Err(String::from("Stream closed without any lines matching the regex."))
}
/// Run the given `future` and panic if the `timeout` is hit.
pub async fn run_with_timeout(timeout: Duration, future: impl futures::Future<Output = ()>) {
tokio::time::timeout(timeout, future).await.expect("Hit timeout");
}
/// Wait for at least n blocks to be finalized from a specified node
pub async fn wait_n_finalized_blocks(n: usize, url: &str) {
use bizinikiwi_rpc_client::{ws_client, ChainApi};
let mut built_blocks = std::collections::HashSet::new();
let block_duration = Duration::from_secs(2);
let mut interval = tokio::time::interval(block_duration);
let rpc = ws_client(url).await.unwrap();
loop {
if let Ok(block) = ChainApi::<(), Hash, Header, ()>::finalized_head(&rpc).await {
built_blocks.insert(block);
if built_blocks.len() > n {
break;
}
};
interval.tick().await;
}
}
/// Run the node for a while (3 blocks)
pub async fn run_node_for_a_while(base_path: &Path, args: &[&str]) {
run_with_timeout(Duration::from_secs(60 * 10), async move {
let mut cmd = Command::new(cargo_bin("bizinikiwi-node"))
.stdout(process::Stdio::piped())
.stderr(process::Stdio::piped())
.args(args)
.arg("-d")
.arg(base_path)
.spawn()
.unwrap();
let stderr = cmd.stderr.take().unwrap();
let mut child = KillChildOnDrop(cmd);
let ws_url = extract_info_from_output(stderr).0.ws_url;
// Let it produce some blocks.
wait_n_finalized_blocks(3, &ws_url).await;
child.assert_still_running();
// Stop the process
child.stop();
})
.await
}
pub async fn block_hash(block_number: u64, url: &str) -> Result<Hash, String> {
use bizinikiwi_rpc_client::{ws_client, ChainApi};
let rpc = ws_client(url).await.unwrap();
let result = ChainApi::<(), Hash, Header, ()>::block_hash(
&rpc,
Some(ListOrValue::Value(NumberOrHex::Number(block_number))),
)
.await
.map_err(|_| "Couldn't get block hash".to_string())?;
match result {
ListOrValue::Value(maybe_block_hash) if maybe_block_hash.is_some() =>
Ok(maybe_block_hash.unwrap()),
_ => Err("Couldn't get block hash".to_string()),
}
}
pub struct KillChildOnDrop(pub Child);
impl KillChildOnDrop {
/// Stop the child and wait until it is finished.
///
/// Asserts if the exit status isn't success.
pub fn stop(&mut self) {
self.stop_with_signal(SIGINT);
}
/// Same as [`Self::stop`] but takes the `signal` that is sent to stop the child.
pub fn stop_with_signal(&mut self, signal: Signal) {
kill(Pid::from_raw(self.id().try_into().unwrap()), signal).unwrap();
assert!(self.wait().unwrap().success());
}
/// Asserts that the child is still running.
pub fn assert_still_running(&mut self) {
assert!(self.try_wait().unwrap().is_none(), "the process should still be running");
}
}
impl Drop for KillChildOnDrop {
fn drop(&mut self) {
let _ = self.0.kill();
}
}
impl Deref for KillChildOnDrop {
type Target = Child;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for KillChildOnDrop {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
/// Information extracted from a running node.
pub struct NodeInfo {
pub ws_url: String,
pub db_path: PathBuf,
}
/// Extract [`NodeInfo`] from a running node by parsing its output.
///
/// Returns the [`NodeInfo`] and all the read data.
pub fn extract_info_from_output(read: impl Read + Send) -> (NodeInfo, String) {
let mut data = String::new();
let ws_url = BufReader::new(read)
.lines()
.find_map(|line| {
let line = line.expect("failed to obtain next line while extracting node info");
data.push_str(&line);
data.push_str("\n");
// does the line contain our port (we expect this specific output from bizinikiwi).
let sock_addr = match line.split_once("Running JSON-RPC server: addr=") {
None => return None,
Some((_, after)) => after.split_once(",").unwrap().0,
};
Some(format!("ws://{}", sock_addr))
})
.unwrap_or_else(|| {
eprintln!("Observed node output:\n{}", data);
panic!("We should get a WebSocket address")
});
// Database path is printed before the ws url!
let re = Regex::new(r"Database: .+ at (\S+)").unwrap();
let db_path = PathBuf::from(re.captures(data.as_str()).unwrap().get(1).unwrap().as_str());
(NodeInfo { ws_url, db_path }, data)
}
+51
View File
@@ -0,0 +1,51 @@
[package]
name = "bizinikiwi-test-client"
description = "Client testing utilities"
version = "2.0.1"
authors.workspace = true
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]
array-bytes = { workspace = true, default-features = true }
async-trait = { workspace = true }
codec = { workspace = true, default-features = true }
futures = { workspace = true }
pezsc-client-api = { workspace = true, default-features = true }
pezsc-client-db = { features = [
"test-helpers",
], workspace = true, default-features = false }
pezsc-consensus = { workspace = true, default-features = true }
pezsc-executor = { workspace = true, default-features = true }
pezsc-service = { workspace = true, default-features = false }
serde = { workspace = true, default-features = true }
serde_json = { 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-keyring = { workspace = true, default-features = true }
pezsp-keystore = { workspace = true, default-features = true }
pezsp-runtime = { workspace = true, default-features = true }
tokio = { features = ["sync"], workspace = true, default-features = true }
[features]
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-blockchain/runtime-benchmarks",
"pezsp-consensus/runtime-benchmarks",
"pezsp-keyring/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
]
@@ -0,0 +1,206 @@
// 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.
//! Client extension for tests.
use pezsc_client_api::{backend::Finalizer, client::BlockBackend};
use pezsc_consensus::{BlockImport, BlockImportParams, ForkChoiceStrategy};
use pezsc_service::client::Client;
use pezsp_consensus::Error as ConsensusError;
use pezsp_runtime::{traits::Block as BlockT, Justification, Justifications};
pub use pezsp_consensus::BlockOrigin;
/// Extension trait for a test client.
pub trait ClientExt<Block: BlockT>: Sized {
/// Finalize a block.
fn finalize_block(
&self,
hash: Block::Hash,
justification: Option<Justification>,
) -> pezsp_blockchain::Result<()>;
/// Returns hash of the genesis block.
fn genesis_hash(&self) -> <Block as BlockT>::Hash;
}
/// Extension trait for a test client around block importing.
#[async_trait::async_trait]
pub trait ClientBlockImportExt<Block: BlockT>: Sized {
/// Import block to the chain. No finality.
async fn import(&self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError>;
/// Import a block and make it our best block if possible.
async fn import_as_best(&self, origin: BlockOrigin, block: Block)
-> Result<(), ConsensusError>;
/// Import a block and finalize it.
async fn import_as_final(
&self,
origin: BlockOrigin,
block: Block,
) -> Result<(), ConsensusError>;
/// Import block with justification(s), finalizes block.
async fn import_justified(
&self,
origin: BlockOrigin,
block: Block,
justifications: Justifications,
) -> Result<(), ConsensusError>;
}
impl<B, E, RA, Block> ClientExt<Block> for Client<B, E, Block, RA>
where
B: pezsc_client_api::backend::Backend<Block>,
E: pezsc_client_api::CallExecutor<Block> + pezsc_executor::RuntimeVersionOf + 'static,
Self: BlockImport<Block, Error = ConsensusError>,
Block: BlockT,
{
fn finalize_block(
&self,
hash: Block::Hash,
justification: Option<Justification>,
) -> pezsp_blockchain::Result<()> {
Finalizer::finalize_block(self, hash, justification, true)
}
fn genesis_hash(&self) -> <Block as BlockT>::Hash {
self.block_hash(0u32.into()).unwrap().unwrap()
}
}
/// This implementation is required, because of the weird api requirements around `BlockImport`.
#[async_trait::async_trait]
impl<Block: BlockT, T> ClientBlockImportExt<Block> for std::sync::Arc<T>
where
for<'r> &'r T: BlockImport<Block, Error = ConsensusError>,
T: Send + Sync,
{
async fn import(&self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> {
let (header, extrinsics) = block.deconstruct();
let mut import = BlockImportParams::new(origin, header);
import.body = Some(extrinsics);
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
BlockImport::import_block(self, import).await.map(|_| ())
}
async fn import_as_best(
&self,
origin: BlockOrigin,
block: Block,
) -> Result<(), ConsensusError> {
let (header, extrinsics) = block.deconstruct();
let mut import = BlockImportParams::new(origin, header);
import.body = Some(extrinsics);
import.fork_choice = Some(ForkChoiceStrategy::Custom(true));
BlockImport::import_block(self, import).await.map(|_| ())
}
async fn import_as_final(
&self,
origin: BlockOrigin,
block: Block,
) -> Result<(), ConsensusError> {
let (header, extrinsics) = block.deconstruct();
let mut import = BlockImportParams::new(origin, header);
import.body = Some(extrinsics);
import.finalized = true;
import.fork_choice = Some(ForkChoiceStrategy::Custom(true));
BlockImport::import_block(self, import).await.map(|_| ())
}
async fn import_justified(
&self,
origin: BlockOrigin,
block: Block,
justifications: Justifications,
) -> Result<(), ConsensusError> {
let (header, extrinsics) = block.deconstruct();
let mut import = BlockImportParams::new(origin, header);
import.justifications = Some(justifications);
import.body = Some(extrinsics);
import.finalized = true;
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
BlockImport::import_block(self, import).await.map(|_| ())
}
}
#[async_trait::async_trait]
impl<B, E, RA, Block: BlockT> ClientBlockImportExt<Block> for Client<B, E, Block, RA>
where
Self: BlockImport<Block, Error = ConsensusError>,
RA: Send + Sync,
B: Send + Sync,
E: Send + Sync,
{
async fn import(&self, origin: BlockOrigin, block: Block) -> Result<(), ConsensusError> {
let (header, extrinsics) = block.deconstruct();
let mut import = BlockImportParams::new(origin, header);
import.body = Some(extrinsics);
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
BlockImport::import_block(self, import).await.map(|_| ())
}
async fn import_as_best(
&self,
origin: BlockOrigin,
block: Block,
) -> Result<(), ConsensusError> {
let (header, extrinsics) = block.deconstruct();
let mut import = BlockImportParams::new(origin, header);
import.body = Some(extrinsics);
import.fork_choice = Some(ForkChoiceStrategy::Custom(true));
BlockImport::import_block(self, import).await.map(|_| ())
}
async fn import_as_final(
&self,
origin: BlockOrigin,
block: Block,
) -> Result<(), ConsensusError> {
let (header, extrinsics) = block.deconstruct();
let mut import = BlockImportParams::new(origin, header);
import.body = Some(extrinsics);
import.finalized = true;
import.fork_choice = Some(ForkChoiceStrategy::Custom(true));
BlockImport::import_block(self, import).await.map(|_| ())
}
async fn import_justified(
&self,
origin: BlockOrigin,
block: Block,
justifications: Justifications,
) -> Result<(), ConsensusError> {
let (header, extrinsics) = block.deconstruct();
let mut import = BlockImportParams::new(origin, header);
import.justifications = Some(justifications);
import.body = Some(extrinsics);
import.finalized = true;
import.fork_choice = Some(ForkChoiceStrategy::LongestChain);
BlockImport::import_block(self, import).await.map(|_| ())
}
}
+448
View File
@@ -0,0 +1,448 @@
// 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.
//! Client testing utilities.
#![warn(missing_docs)]
pub mod client_ext;
pub use self::client_ext::{BlockOrigin, ClientBlockImportExt, ClientExt};
pub use pezsc_client_api::{execution_extensions::ExecutionExtensions, BadBlocks, ForkBlocks};
pub use pezsc_client_db::{self, Backend, BlocksPruning};
pub use pezsc_executor::{self, WasmExecutionMethod, WasmExecutor};
pub use pezsc_service::{client, RpcHandlers};
pub use pezsp_consensus;
pub use pezsp_keyring::{Ed25519Keyring, Sr25519Keyring};
pub use pezsp_keystore::{Keystore, KeystorePtr};
pub use pezsp_runtime::{Storage, StorageChild};
use futures::{future::Future, stream::StreamExt};
use pezsc_client_api::BlockchainEvents;
use pezsc_service::client::{ClientConfig, LocalCallExecutor};
use serde::Deserialize;
use pezsp_core::{storage::ChildInfo, testing::TaskExecutor};
use pezsp_runtime::{
codec::Encode,
traits::{Block as BlockT, Header},
OpaqueExtrinsic,
};
use std::{
collections::{HashMap, HashSet},
pin::Pin,
sync::Arc,
};
/// A genesis storage initialization trait.
pub trait GenesisInit: Default {
/// Construct genesis storage.
fn genesis_storage(&self) -> Storage;
}
impl GenesisInit for () {
fn genesis_storage(&self) -> Storage {
Default::default()
}
}
/// A builder for creating a test client instance.
pub struct TestClientBuilder<Block: BlockT, ExecutorDispatch, Backend: 'static, G: GenesisInit> {
genesis_init: G,
/// The key is an unprefixed storage key, this only contains
/// default child trie content.
child_storage_extension: HashMap<Vec<u8>, StorageChild>,
backend: Arc<Backend>,
_executor: std::marker::PhantomData<ExecutorDispatch>,
fork_blocks: ForkBlocks<Block>,
bad_blocks: BadBlocks<Block>,
enable_offchain_indexing_api: bool,
enable_import_proof_recording: bool,
no_genesis: bool,
}
impl<Block: BlockT, ExecutorDispatch, G: GenesisInit> Default
for TestClientBuilder<Block, ExecutorDispatch, Backend<Block>, G>
{
fn default() -> Self {
Self::with_default_backend()
}
}
impl<Block: BlockT, ExecutorDispatch, G: GenesisInit>
TestClientBuilder<Block, ExecutorDispatch, Backend<Block>, G>
{
/// Create new `TestClientBuilder` with default backend.
pub fn with_default_backend() -> Self {
let backend = Arc::new(Backend::new_test(std::u32::MAX, std::u64::MAX));
Self::with_backend(backend)
}
/// Create new `TestClientBuilder` with default backend and pruning window size
pub fn with_pruning_window(blocks_pruning: u32) -> Self {
let backend = Arc::new(Backend::new_test(blocks_pruning, 0));
Self::with_backend(backend)
}
/// Create new `TestClientBuilder` with default backend and storage chain mode
pub fn with_tx_storage(blocks_pruning: u32) -> Self {
let backend =
Arc::new(Backend::new_test_with_tx_storage(BlocksPruning::Some(blocks_pruning), 0));
Self::with_backend(backend)
}
}
impl<Block: BlockT, ExecutorDispatch, Backend, G: GenesisInit>
TestClientBuilder<Block, ExecutorDispatch, Backend, G>
{
/// Create a new instance of the test client builder.
pub fn with_backend(backend: Arc<Backend>) -> Self {
TestClientBuilder {
backend,
child_storage_extension: Default::default(),
genesis_init: Default::default(),
_executor: Default::default(),
fork_blocks: None,
bad_blocks: None,
enable_offchain_indexing_api: false,
no_genesis: false,
enable_import_proof_recording: false,
}
}
/// Alter the genesis storage parameters.
pub fn genesis_init_mut(&mut self) -> &mut G {
&mut self.genesis_init
}
/// Give access to the underlying backend of these clients
pub fn backend(&self) -> Arc<Backend> {
self.backend.clone()
}
/// Extend child storage
pub fn add_child_storage(
mut self,
child_info: &ChildInfo,
key: impl AsRef<[u8]>,
value: impl AsRef<[u8]>,
) -> Self {
let storage_key = child_info.storage_key();
let entry = self.child_storage_extension.entry(storage_key.to_vec()).or_insert_with(|| {
StorageChild { data: Default::default(), child_info: child_info.clone() }
});
entry.data.insert(key.as_ref().to_vec(), value.as_ref().to_vec());
self
}
/// Sets custom block rules.
pub fn set_block_rules(
mut self,
fork_blocks: ForkBlocks<Block>,
bad_blocks: BadBlocks<Block>,
) -> Self {
self.fork_blocks = fork_blocks;
self.bad_blocks = bad_blocks;
self
}
/// Enable the offchain indexing api.
pub fn enable_offchain_indexing_api(mut self) -> Self {
self.enable_offchain_indexing_api = true;
self
}
/// Enable proof recording on import.
pub fn enable_import_proof_recording(mut self) -> Self {
self.enable_import_proof_recording = true;
self
}
/// Disable writing genesis.
pub fn set_no_genesis(mut self) -> Self {
self.no_genesis = true;
self
}
/// Build the test client with the given native executor.
pub fn build_with_executor<RuntimeApi>(
self,
executor: ExecutorDispatch,
) -> (
client::Client<Backend, ExecutorDispatch, Block, RuntimeApi>,
pezsc_consensus::LongestChain<Backend, Block>,
)
where
ExecutorDispatch:
pezsc_client_api::CallExecutor<Block> + pezsc_executor::RuntimeVersionOf + Clone + 'static,
Backend: pezsc_client_api::backend::Backend<Block>,
<Backend as pezsc_client_api::backend::Backend<Block>>::OffchainStorage: 'static,
{
let storage = {
let mut storage = self.genesis_init.genesis_storage();
// Add some child storage keys.
for (key, child_content) in self.child_storage_extension {
storage.children_default.insert(
key,
StorageChild {
data: child_content.data.into_iter().collect(),
child_info: child_content.child_info,
},
);
}
storage
};
let client_config = ClientConfig {
enable_import_proof_recording: self.enable_import_proof_recording,
offchain_indexing_api: self.enable_offchain_indexing_api,
no_genesis: self.no_genesis,
..Default::default()
};
let genesis_block_builder = pezsc_service::GenesisBlockBuilder::new(
&storage,
!client_config.no_genesis,
self.backend.clone(),
executor.clone(),
)
.expect("Creates genesis block builder");
let spawn_handle = Box::new(TaskExecutor::new());
let client = client::Client::new(
self.backend.clone(),
executor,
spawn_handle,
genesis_block_builder,
self.fork_blocks,
self.bad_blocks,
None,
None,
client_config,
)
.expect("Creates new client");
let longest_chain = pezsc_consensus::LongestChain::new(self.backend);
(client, longest_chain)
}
}
impl<Block: BlockT, H, Backend, G: GenesisInit>
TestClientBuilder<Block, client::LocalCallExecutor<Block, Backend, WasmExecutor<H>>, Backend, G>
{
/// Build the test client with the given native executor.
pub fn build_with_native_executor<RuntimeApi, I>(
self,
executor: I,
) -> (
client::Client<
Backend,
client::LocalCallExecutor<Block, Backend, WasmExecutor<H>>,
Block,
RuntimeApi,
>,
pezsc_consensus::LongestChain<Backend, Block>,
)
where
I: Into<Option<WasmExecutor<H>>>,
Backend: pezsc_client_api::backend::Backend<Block> + 'static,
H: pezsc_executor::HostFunctions,
{
let executor = executor.into().unwrap_or_else(|| WasmExecutor::<H>::builder().build());
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)
}
}
/// The output of an RPC transaction.
pub struct RpcTransactionOutput {
/// The output string of the transaction if any.
pub result: String,
/// An async receiver if data will be returned via a callback.
pub receiver: tokio::sync::mpsc::Receiver<String>,
}
impl std::fmt::Debug for RpcTransactionOutput {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "RpcTransactionOutput {{ result: {:?}, receiver }}", self.result)
}
}
/// An error for when the RPC call fails.
#[derive(Deserialize, Debug)]
pub struct RpcTransactionError {
/// A Number that indicates the error type that occurred.
pub code: i64,
/// A String providing a short description of the error.
pub message: String,
/// A Primitive or Structured value that contains additional information about the error.
pub data: Option<serde_json::Value>,
}
impl std::fmt::Display for RpcTransactionError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}
/// An extension trait for `RpcHandlers`.
#[async_trait::async_trait]
pub trait RpcHandlersExt {
/// Send a transaction through the RpcHandlers.
async fn send_transaction(
&self,
extrinsic: OpaqueExtrinsic,
) -> Result<RpcTransactionOutput, RpcTransactionError>;
}
#[async_trait::async_trait]
impl RpcHandlersExt for RpcHandlers {
async fn send_transaction(
&self,
extrinsic: OpaqueExtrinsic,
) -> Result<RpcTransactionOutput, RpcTransactionError> {
let (result, rx) = self
.rpc_query(&format!(
r#"{{
"jsonrpc": "2.0",
"method": "author_submitExtrinsic",
"params": ["0x{}"],
"id": 0
}}"#,
array_bytes::bytes2hex("", &extrinsic.encode())
))
.await
.expect("valid JSON-RPC request object; qed");
parse_rpc_result(result, rx)
}
}
pub(crate) fn parse_rpc_result(
result: String,
receiver: tokio::sync::mpsc::Receiver<String>,
) -> Result<RpcTransactionOutput, RpcTransactionError> {
let json: serde_json::Value =
serde_json::from_str(&result).expect("the result can only be a JSONRPC string; qed");
let error = json.as_object().expect("JSON result is always an object; qed").get("error");
if let Some(error) = error {
return Err(serde_json::from_value(error.clone())
.expect("the JSONRPC result's error is always valid; qed"));
}
Ok(RpcTransactionOutput { result, receiver })
}
/// An extension trait for `BlockchainEvents`.
pub trait BlockchainEventsExt<C, B>
where
C: BlockchainEvents<B>,
B: BlockT,
{
/// Wait for `count` blocks to be imported in the node and then exit. This function will not
/// return if no blocks are ever created, thus you should restrict the maximum amount of time of
/// the test execution.
fn wait_for_blocks(&self, count: usize) -> Pin<Box<dyn Future<Output = ()> + Send>>;
}
impl<C, B> BlockchainEventsExt<C, B> for C
where
C: BlockchainEvents<B>,
B: BlockT,
{
fn wait_for_blocks(&self, count: usize) -> Pin<Box<dyn Future<Output = ()> + Send>> {
assert!(count > 0, "'count' argument must be greater than 0");
let mut import_notification_stream = self.import_notification_stream();
let mut blocks = HashSet::new();
Box::pin(async move {
while let Some(notification) = import_notification_stream.next().await {
if notification.is_new_best {
blocks.insert(*notification.header.number());
if blocks.len() == count {
break;
}
}
}
})
}
}
#[cfg(test)]
mod tests {
#[test]
fn parses_error_properly() {
let (_, rx) = tokio::sync::mpsc::channel(1);
assert!(super::parse_rpc_result(
r#"{
"jsonrpc": "2.0",
"result": 19,
"id": 1
}"#
.to_string(),
rx
)
.is_ok());
let (_, rx) = tokio::sync::mpsc::channel(1);
let error = super::parse_rpc_result(
r#"{
"jsonrpc": "2.0",
"error": {
"code": -32601,
"message": "Method not found"
},
"id": 1
}"#
.to_string(),
rx,
)
.unwrap_err();
assert_eq!(error.code, -32601);
assert_eq!(error.message, "Method not found");
assert!(error.data.is_none());
let (_, rx) = tokio::sync::mpsc::channel(1);
let error = super::parse_rpc_result(
r#"{
"jsonrpc": "2.0",
"error": {
"code": -32601,
"message": "Method not found",
"data": 42
},
"id": 1
}"#
.to_string(),
rx,
)
.unwrap_err();
assert_eq!(error.code, -32601);
assert_eq!(error.message, "Method not found");
assert!(error.data.is_some());
}
}
+167
View File
@@ -0,0 +1,167 @@
[package]
name = "bizinikiwi-test-runtime"
version = "2.0.0"
authors.workspace = true
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]
codec = { features = ["derive"], workspace = true }
pezframe-executive = { workspace = true }
pezframe-metadata-hash-extension = { workspace = true }
pezframe-support = { workspace = true }
pezframe-system = { workspace = true }
pezframe-system-rpc-runtime-api = { workspace = true }
pezpallet-babe = { workspace = true }
pezpallet-balances = { workspace = true }
pezpallet-timestamp = { workspace = true }
pezpallet-utility = { workspace = true }
pezsc-service = { optional = true, workspace = true }
scale-info = { features = ["derive"], workspace = true }
pezsp-api = { workspace = true }
pezsp-application-crypto = { features = ["serde"], workspace = true }
pezsp-block-builder = { workspace = true }
pezsp-consensus-aura = { features = ["serde"], workspace = true }
pezsp-consensus-babe = { features = ["serde"], workspace = true }
pezsp-consensus-grandpa = { features = ["serde"], workspace = true }
pezsp-core = { features = ["serde"], workspace = true }
pezsp-crypto-hashing = { workspace = true }
pezsp-debug-derive = { workspace = true, default-features = false, features = [
"force-debug",
] }
pezsp-externalities = { workspace = true }
pezsp-genesis-builder = { workspace = true }
pezsp-inherents = { workspace = true }
pezsp-io = { workspace = true }
pezsp-keyring = { workspace = true }
pezsp-offchain = { workspace = true }
pezsp-runtime = { features = ["serde"], workspace = true }
pezsp-session = { workspace = true }
pezsp-state-machine = { workspace = true }
pezsp-transaction-pool = { workspace = true }
pezsp-trie = { workspace = true }
pezsp-version = { workspace = true }
trie-db = { workspace = true }
# 3rd party
array-bytes = { optional = true, workspace = true, default-features = true }
log = { workspace = true }
serde_json = { workspace = true, features = ["alloc"] }
tracing = { workspace = true, default-features = false }
[dev-dependencies]
futures = { workspace = true }
pretty_assertions = { workspace = true }
pezsc-block-builder = { workspace = true, default-features = true }
pezsc-chain-spec = { workspace = true, default-features = true }
pezsc-executor = { workspace = true, default-features = true }
pezsc-executor-common = { workspace = true, default-features = true }
serde = { features = ["alloc", "derive"], workspace = true }
pezsp-consensus = { workspace = true, default-features = true }
pezsp-tracing = { workspace = true, default-features = true }
bizinikiwi-test-runtime-client = { workspace = true }
[build-dependencies]
bizinikiwi-wasm-builder = { optional = true, features = [
"metadata-hash",
], workspace = true, default-features = true }
[features]
default = ["std"]
std = [
"array-bytes",
"codec/std",
"pezframe-executive/std",
"pezframe-metadata-hash-extension/std",
"pezframe-support/std",
"pezframe-system-rpc-runtime-api/std",
"pezframe-system/std",
"log/std",
"pezpallet-babe/std",
"pezpallet-balances/std",
"pezpallet-timestamp/std",
"pezpallet-utility/std",
"pezsc-executor/std",
"pezsc-service",
"scale-info/std",
"serde/std",
"serde_json/std",
"pezsp-api/std",
"pezsp-application-crypto/std",
"pezsp-block-builder/std",
"pezsp-consensus-aura/std",
"pezsp-consensus-babe/std",
"pezsp-consensus-grandpa/std",
"pezsp-core/std",
"pezsp-crypto-hashing/std",
"pezsp-debug-derive/std",
"pezsp-externalities/std",
"pezsp-genesis-builder/std",
"pezsp-inherents/std",
"pezsp-io/std",
"pezsp-keyring/std",
"pezsp-offchain/std",
"pezsp-runtime/std",
"pezsp-session/std",
"pezsp-state-machine/std",
"pezsp-tracing/std",
"pezsp-transaction-pool/std",
"pezsp-trie/std",
"pezsp-version/std",
"bizinikiwi-wasm-builder",
"tracing/std",
"trie-db/std",
]
# Special feature to disable logging
disable-logging = ["pezsp-api/disable-logging"]
# This feature adds BLS crypto primitives.
# It should not be used in production since the implementation and interface may still
# be subject to significant changes.
bls-experimental = ["pezsp-application-crypto/bls-experimental"]
runtime-benchmarks = [
"pezframe-executive/runtime-benchmarks",
"pezframe-metadata-hash-extension/runtime-benchmarks",
"pezframe-support/runtime-benchmarks",
"pezframe-system-rpc-runtime-api/runtime-benchmarks",
"pezframe-system/runtime-benchmarks",
"pezpallet-babe/runtime-benchmarks",
"pezpallet-balances/runtime-benchmarks",
"pezpallet-timestamp/runtime-benchmarks",
"pezpallet-utility/runtime-benchmarks",
"pezsc-block-builder/runtime-benchmarks",
"pezsc-chain-spec/runtime-benchmarks",
"pezsc-executor/runtime-benchmarks",
"pezsc-service?/runtime-benchmarks",
"pezsp-api/runtime-benchmarks",
"pezsp-block-builder/runtime-benchmarks",
"pezsp-consensus-aura/runtime-benchmarks",
"pezsp-consensus-babe/runtime-benchmarks",
"pezsp-consensus-grandpa/runtime-benchmarks",
"pezsp-consensus/runtime-benchmarks",
"pezsp-genesis-builder/runtime-benchmarks",
"pezsp-inherents/runtime-benchmarks",
"pezsp-io/runtime-benchmarks",
"pezsp-keyring/runtime-benchmarks",
"pezsp-offchain/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"pezsp-session/runtime-benchmarks",
"pezsp-state-machine/runtime-benchmarks",
"pezsp-transaction-pool/runtime-benchmarks",
"pezsp-trie/runtime-benchmarks",
"pezsp-version/runtime-benchmarks",
"bizinikiwi-test-runtime-client/runtime-benchmarks",
"bizinikiwi-wasm-builder?/runtime-benchmarks",
]
+43
View File
@@ -0,0 +1,43 @@
// 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.
fn main() {
#[cfg(feature = "std")]
{
bizinikiwi_wasm_builder::WasmBuilder::new()
.with_current_project()
.export_heap_base()
// Note that we set the stack-size to 1MB explicitly even though it is set
// to this value by default. This is because some of our tests
// (`restoration_of_globals`) depend on the stack-size.
.append_to_rust_flags("-Clink-arg=-zstack-size=1048576")
.enable_metadata_hash("TOKEN", 10)
.import_memory()
.build();
}
#[cfg(feature = "std")]
{
bizinikiwi_wasm_builder::WasmBuilder::new()
.with_current_project()
.export_heap_base()
.import_memory()
.set_file_name("wasm_binary_logging_disabled.rs")
.enable_feature("disable-logging")
.build();
}
}
@@ -0,0 +1,42 @@
[package]
name = "bizinikiwi-test-runtime-client"
version = "2.0.0"
authors.workspace = true
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]
futures = { workspace = true }
pezsc-block-builder = { workspace = true, default-features = true }
pezsc-client-api = { workspace = true, default-features = true }
pezsc-consensus = { workspace = true, default-features = true }
pezsp-api = { 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-runtime = { workspace = true, default-features = true }
bizinikiwi-test-client = { workspace = true }
bizinikiwi-test-runtime = { workspace = true }
[features]
bls-experimental = ["bizinikiwi-test-runtime/bls-experimental"]
runtime-benchmarks = [
"pezsc-block-builder/runtime-benchmarks",
"pezsc-client-api/runtime-benchmarks",
"pezsc-consensus/runtime-benchmarks",
"pezsp-api/runtime-benchmarks",
"pezsp-blockchain/runtime-benchmarks",
"pezsp-consensus/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"bizinikiwi-test-client/runtime-benchmarks",
"bizinikiwi-test-runtime/runtime-benchmarks",
]
@@ -0,0 +1,74 @@
// 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.
//! Block Builder extensions for tests.
use pezsc_block_builder::BlockBuilderApi;
use pezsp_api::{ApiExt, ProvideRuntimeApi};
use bizinikiwi_test_runtime::*;
/// Extension trait for test block builder.
pub trait BlockBuilderExt {
/// Add transfer extrinsic to the block.
fn push_transfer(
&mut self,
transfer: bizinikiwi_test_runtime::Transfer,
) -> Result<(), pezsp_blockchain::Error>;
/// Add unsigned storage change extrinsic to the block.
fn push_storage_change(
&mut self,
key: Vec<u8>,
value: Option<Vec<u8>>,
) -> Result<(), pezsp_blockchain::Error>;
/// Adds an extrinsic which pushes DigestItem to header's log
fn push_deposit_log_digest_item(
&mut self,
log: pezsp_runtime::generic::DigestItem,
) -> Result<(), pezsp_blockchain::Error>;
}
impl<'a, A> BlockBuilderExt for pezsc_block_builder::BlockBuilder<'a, bizinikiwi_test_runtime::Block, A>
where
A: ProvideRuntimeApi<bizinikiwi_test_runtime::Block>
+ pezsp_api::CallApiAt<bizinikiwi_test_runtime::Block>
+ 'a,
A::Api: BlockBuilderApi<bizinikiwi_test_runtime::Block> + ApiExt<bizinikiwi_test_runtime::Block>,
{
fn push_transfer(
&mut self,
transfer: bizinikiwi_test_runtime::Transfer,
) -> Result<(), pezsp_blockchain::Error> {
self.push(transfer.into_unchecked_extrinsic())
}
fn push_storage_change(
&mut self,
key: Vec<u8>,
value: Option<Vec<u8>>,
) -> Result<(), pezsp_blockchain::Error> {
self.push(ExtrinsicBuilder::new_storage_change(key, value).build())
}
fn push_deposit_log_digest_item(
&mut self,
log: pezsp_runtime::generic::DigestItem,
) -> Result<(), pezsp_blockchain::Error> {
self.push(ExtrinsicBuilder::new_deposit_log_digest_item(log).build())
}
}
@@ -0,0 +1,217 @@
// 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.
//! Client testing utilities.
#![warn(missing_docs)]
pub mod trait_tests;
mod block_builder_ext;
pub use pezsc_consensus::LongestChain;
use std::sync::Arc;
pub use bizinikiwi_test_client::*;
pub use bizinikiwi_test_runtime as runtime;
pub use self::block_builder_ext::BlockBuilderExt;
use pezsp_core::storage::ChildInfo;
use bizinikiwi_test_runtime::genesismap::GenesisStorageBuilder;
/// A prelude to import in tests.
pub mod prelude {
// Trait extensions
pub use super::{
BlockBuilderExt, ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt,
TestClientBuilderExt,
};
// Client structs
pub use super::{
Backend, ExecutorDispatch, TestClient, TestClientBuilder, WasmExecutionMethod,
};
// Keyring
pub use super::Sr25519Keyring;
pub use futures::executor::block_on;
pub use pezsc_block_builder::BlockBuilderBuilder;
pub use pezsp_blockchain::HeaderBackend;
}
/// Test client database backend.
pub type Backend = bizinikiwi_test_client::Backend<bizinikiwi_test_runtime::Block>;
/// Test client executor.
pub type ExecutorDispatch =
client::LocalCallExecutor<bizinikiwi_test_runtime::Block, Backend, WasmExecutor>;
/// Parameters of test-client builder with test-runtime.
#[derive(Default)]
pub struct GenesisParameters {
heap_pages_override: Option<u64>,
extra_storage: Storage,
wasm_code: Option<Vec<u8>>,
}
impl GenesisParameters {
/// Set the wasm code that should be used at genesis.
pub fn set_wasm_code(&mut self, code: Vec<u8>) {
self.wasm_code = Some(code);
}
/// Access extra genesis storage.
pub fn extra_storage(&mut self) -> &mut Storage {
&mut self.extra_storage
}
}
impl GenesisInit for GenesisParameters {
fn genesis_storage(&self) -> Storage {
GenesisStorageBuilder::default()
.with_heap_pages(self.heap_pages_override)
.with_wasm_code(&self.wasm_code)
.with_extra_storage(self.extra_storage.clone())
.build()
}
}
/// A `TestClient` with `test-runtime` builder.
pub type TestClientBuilder<E, B> = bizinikiwi_test_client::TestClientBuilder<
bizinikiwi_test_runtime::Block,
E,
B,
GenesisParameters,
>;
/// Test client type with `WasmExecutor` and generic Backend.
pub type Client<B> = client::Client<
B,
client::LocalCallExecutor<bizinikiwi_test_runtime::Block, B, WasmExecutor>,
bizinikiwi_test_runtime::Block,
bizinikiwi_test_runtime::RuntimeApi,
>;
/// A test client with default backend.
pub type TestClient = Client<Backend>;
/// A `TestClientBuilder` with default backend and executor.
pub trait DefaultTestClientBuilderExt: Sized {
/// Create new `TestClientBuilder`
fn new() -> Self;
}
impl DefaultTestClientBuilderExt for TestClientBuilder<ExecutorDispatch, Backend> {
fn new() -> Self {
Self::with_default_backend()
}
}
/// A `test-runtime` extensions to `TestClientBuilder`.
pub trait TestClientBuilderExt<B>: Sized {
/// Returns a mutable reference to the genesis parameters.
fn genesis_init_mut(&mut self) -> &mut GenesisParameters;
/// Override the default value for Wasm heap pages.
fn set_heap_pages(mut self, heap_pages: u64) -> Self {
self.genesis_init_mut().heap_pages_override = Some(heap_pages);
self
}
/// Add an extra value into the genesis storage.
///
/// # Panics
///
/// Panics if the key is empty.
fn add_extra_child_storage<K: Into<Vec<u8>>, V: Into<Vec<u8>>>(
mut self,
child_info: &ChildInfo,
key: K,
value: V,
) -> Self {
let storage_key = child_info.storage_key().to_vec();
let key = key.into();
assert!(!storage_key.is_empty());
assert!(!key.is_empty());
self.genesis_init_mut()
.extra_storage
.children_default
.entry(storage_key)
.or_insert_with(|| StorageChild {
data: Default::default(),
child_info: child_info.clone(),
})
.data
.insert(key, value.into());
self
}
/// Add an extra child value into the genesis storage.
///
/// # Panics
///
/// Panics if the key is empty.
fn add_extra_storage<K: Into<Vec<u8>>, V: Into<Vec<u8>>>(mut self, key: K, value: V) -> Self {
let key = key.into();
assert!(!key.is_empty());
self.genesis_init_mut().extra_storage.top.insert(key, value.into());
self
}
/// Build the test client.
fn build(self) -> Client<B> {
self.build_with_longest_chain().0
}
/// Build the test client and longest chain selector.
fn build_with_longest_chain(
self,
) -> (Client<B>, pezsc_consensus::LongestChain<B, bizinikiwi_test_runtime::Block>);
/// Build the test client and the backend.
fn build_with_backend(self) -> (Client<B>, Arc<B>);
}
impl<B> TestClientBuilderExt<B>
for TestClientBuilder<client::LocalCallExecutor<bizinikiwi_test_runtime::Block, B, WasmExecutor>, B>
where
B: pezsc_client_api::backend::Backend<bizinikiwi_test_runtime::Block> + 'static,
{
fn genesis_init_mut(&mut self) -> &mut GenesisParameters {
Self::genesis_init_mut(self)
}
fn build_with_longest_chain(
self,
) -> (Client<B>, pezsc_consensus::LongestChain<B, bizinikiwi_test_runtime::Block>) {
self.build_with_native_executor(None)
}
fn build_with_backend(self) -> (Client<B>, Arc<B>) {
let backend = self.backend();
(self.build_with_native_executor(None).0, backend)
}
}
/// Creates new client instance used for tests.
pub fn new() -> Client<Backend> {
TestClientBuilder::new().build()
}
/// Create a new native executor.
#[deprecated(note = "Switch to `WasmExecutor:default()`.")]
pub fn new_native_or_wasm_executor() -> WasmExecutor {
WasmExecutor::default()
}
@@ -0,0 +1,546 @@
// 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.
//! tests that should hold for all implementations of certain traits.
//! to test implementations without duplication.
#![allow(missing_docs)]
use std::sync::Arc;
use crate::{
BlockBuilderExt, ClientBlockImportExt, Sr25519Keyring, TestClientBuilder, TestClientBuilderExt,
};
use futures::executor::block_on;
use pezsc_block_builder::BlockBuilderBuilder;
use pezsc_client_api::{
backend,
blockchain::{Backend as BlockChainBackendT, HeaderBackend},
};
use pezsp_consensus::BlockOrigin;
use pezsp_runtime::traits::Block as BlockT;
use bizinikiwi_test_runtime::Transfer;
/// helper to test the `leaves` implementation for various backends
pub fn test_leaves_for_backend<B: 'static>(backend: Arc<B>)
where
B: backend::Backend<bizinikiwi_test_runtime::Block>,
{
// block tree:
// G -> A1 -> A2 -> A3 -> A4 -> A5
// A1 -> B2 -> B3 -> B4
// B2 -> C3
// A1 -> D2
let client = TestClientBuilder::with_backend(backend.clone()).build();
let blockchain = backend.blockchain();
let genesis_hash = client.chain_info().genesis_hash;
assert_eq!(blockchain.leaves().unwrap(), vec![genesis_hash]);
// G -> A1
let a1 = BlockBuilderBuilder::new(&client)
.on_parent_block(genesis_hash)
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
assert_eq!(blockchain.leaves().unwrap(), vec![a1.hash()]);
// A1 -> A2
let a2 = BlockBuilderBuilder::new(&client)
.on_parent_block(a1.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
assert_eq!(blockchain.leaves().unwrap(), vec![a2.hash()]);
// A2 -> A3
let a3 = BlockBuilderBuilder::new(&client)
.on_parent_block(a2.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a3.clone())).unwrap();
assert_eq!(blockchain.leaves().unwrap(), vec![a3.hash()]);
// A3 -> A4
let a4 = BlockBuilderBuilder::new(&client)
.on_parent_block(a3.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a4.clone())).unwrap();
assert_eq!(blockchain.leaves().unwrap(), vec![a4.hash()]);
// A4 -> A5
let a5 = BlockBuilderBuilder::new(&client)
.on_parent_block(a4.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap();
assert_eq!(blockchain.leaves().unwrap(), vec![a5.hash()]);
// A1 -> B2
let mut builder = BlockBuilderBuilder::new(&client)
.on_parent_block(a1.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap();
// this push is required as otherwise B2 has the same hash as A2 and won't get imported
builder
.push_transfer(Transfer {
from: Sr25519Keyring::Alice.into(),
to: Sr25519Keyring::Ferdie.into(),
amount: 41,
nonce: 0,
})
.unwrap();
let b2 = builder.build().unwrap().block;
block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap();
assert_eq!(blockchain.leaves().unwrap(), vec![a5.hash(), b2.hash()]);
// B2 -> B3
let b3 = BlockBuilderBuilder::new(&client)
.on_parent_block(b2.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, b3.clone())).unwrap();
assert_eq!(blockchain.leaves().unwrap(), vec![a5.hash(), b3.hash()]);
// B3 -> B4
let b4 = BlockBuilderBuilder::new(&client)
.on_parent_block(b3.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, b4.clone())).unwrap();
assert_eq!(blockchain.leaves().unwrap(), vec![a5.hash(), b4.hash()]);
// // B2 -> C3
let mut builder = BlockBuilderBuilder::new(&client)
.on_parent_block(b2.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap();
// this push is required as otherwise C3 has the same hash as B3 and won't get imported
builder
.push_transfer(Transfer {
from: Sr25519Keyring::Alice.into(),
to: Sr25519Keyring::Ferdie.into(),
amount: 1,
nonce: 1,
})
.unwrap();
let c3 = builder.build().unwrap().block;
block_on(client.import(BlockOrigin::Own, c3.clone())).unwrap();
assert_eq!(blockchain.leaves().unwrap(), vec![a5.hash(), b4.hash(), c3.hash()]);
// A1 -> D2
let mut builder = BlockBuilderBuilder::new(&client)
.on_parent_block(a1.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap();
// this push is required as otherwise D2 has the same hash as B2 and won't get imported
builder
.push_transfer(Transfer {
from: Sr25519Keyring::Alice.into(),
to: Sr25519Keyring::Ferdie.into(),
amount: 1,
nonce: 0,
})
.unwrap();
let d2 = builder.build().unwrap().block;
block_on(client.import(BlockOrigin::Own, d2.clone())).unwrap();
assert_eq!(blockchain.leaves().unwrap(), vec![a5.hash(), b4.hash(), c3.hash(), d2.hash()]);
}
/// helper to test the `children` implementation for various backends
pub fn test_children_for_backend<B: 'static>(backend: Arc<B>)
where
B: backend::LocalBackend<bizinikiwi_test_runtime::Block>,
{
// block tree:
// G -> A1 -> A2 -> A3 -> A4 -> A5
// A1 -> B2 -> B3 -> B4
// B2 -> C3
// A1 -> D2
let client = TestClientBuilder::with_backend(backend.clone()).build();
let blockchain = backend.blockchain();
let genesis_hash = client.chain_info().genesis_hash;
// G -> A1
let a1 = BlockBuilderBuilder::new(&client)
.on_parent_block(genesis_hash)
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
// A1 -> A2
let a2 = BlockBuilderBuilder::new(&client)
.on_parent_block(a1.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
// A2 -> A3
let a3 = BlockBuilderBuilder::new(&client)
.on_parent_block(a2.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a3.clone())).unwrap();
// A3 -> A4
let a4 = BlockBuilderBuilder::new(&client)
.on_parent_block(a3.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a4.clone())).unwrap();
// A4 -> A5
let a5 = BlockBuilderBuilder::new(&client)
.on_parent_block(a4.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap();
// A1 -> B2
let mut builder = BlockBuilderBuilder::new(&client)
.on_parent_block(a1.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap();
// this push is required as otherwise B2 has the same hash as A2 and won't get imported
builder
.push_transfer(Transfer {
from: Sr25519Keyring::Alice.into(),
to: Sr25519Keyring::Ferdie.into(),
amount: 41,
nonce: 0,
})
.unwrap();
let b2 = builder.build().unwrap().block;
block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap();
// B2 -> B3
let b3 = BlockBuilderBuilder::new(&client)
.on_parent_block(b2.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, b3.clone())).unwrap();
// B3 -> B4
let b4 = BlockBuilderBuilder::new(&client)
.on_parent_block(b3.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, b4)).unwrap();
// // B2 -> C3
let mut builder = BlockBuilderBuilder::new(&client)
.on_parent_block(b2.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap();
// this push is required as otherwise C3 has the same hash as B3 and won't get imported
builder
.push_transfer(Transfer {
from: Sr25519Keyring::Alice.into(),
to: Sr25519Keyring::Ferdie.into(),
amount: 1,
nonce: 1,
})
.unwrap();
let c3 = builder.build().unwrap().block;
block_on(client.import(BlockOrigin::Own, c3.clone())).unwrap();
// A1 -> D2
let mut builder = BlockBuilderBuilder::new(&client)
.on_parent_block(a1.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap();
// this push is required as otherwise D2 has the same hash as B2 and won't get imported
builder
.push_transfer(Transfer {
from: Sr25519Keyring::Alice.into(),
to: Sr25519Keyring::Ferdie.into(),
amount: 1,
nonce: 0,
})
.unwrap();
let d2 = builder.build().unwrap().block;
block_on(client.import(BlockOrigin::Own, d2.clone())).unwrap();
let genesis_hash = client.chain_info().genesis_hash;
let children1 = blockchain.children(a4.hash()).unwrap();
assert_eq!(vec![a5.hash()], children1);
let children2 = blockchain.children(a1.hash()).unwrap();
assert_eq!(vec![a2.hash(), b2.hash(), d2.hash()], children2);
let children3 = blockchain.children(genesis_hash).unwrap();
assert_eq!(vec![a1.hash()], children3);
let children4 = blockchain.children(b2.hash()).unwrap();
assert_eq!(vec![b3.hash(), c3.hash()], children4);
}
pub fn test_blockchain_query_by_number_gets_canonical<B: 'static>(backend: Arc<B>)
where
B: backend::LocalBackend<bizinikiwi_test_runtime::Block>,
{
// block tree:
// G -> A1 -> A2 -> A3 -> A4 -> A5
// A1 -> B2 -> B3 -> B4
// B2 -> C3
// A1 -> D2
let client = TestClientBuilder::with_backend(backend.clone()).build();
let blockchain = backend.blockchain();
let genesis_hash = client.chain_info().genesis_hash;
// G -> A1
let a1 = BlockBuilderBuilder::new(&client)
.on_parent_block(genesis_hash)
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
// A1 -> A2
let a2 = BlockBuilderBuilder::new(&client)
.on_parent_block(a1.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
// A2 -> A3
let a3 = BlockBuilderBuilder::new(&client)
.on_parent_block(a2.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a3.clone())).unwrap();
// A3 -> A4
let a4 = BlockBuilderBuilder::new(&client)
.on_parent_block(a3.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a4.clone())).unwrap();
// A4 -> A5
let a5 = BlockBuilderBuilder::new(&client)
.on_parent_block(a4.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a5.clone())).unwrap();
// A1 -> B2
let mut builder = BlockBuilderBuilder::new(&client)
.on_parent_block(a1.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap();
// this push is required as otherwise B2 has the same hash as A2 and won't get imported
builder
.push_transfer(Transfer {
from: Sr25519Keyring::Alice.into(),
to: Sr25519Keyring::Ferdie.into(),
amount: 41,
nonce: 0,
})
.unwrap();
let b2 = builder.build().unwrap().block;
block_on(client.import(BlockOrigin::Own, b2.clone())).unwrap();
// B2 -> B3
let b3 = BlockBuilderBuilder::new(&client)
.on_parent_block(b2.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, b3.clone())).unwrap();
// B3 -> B4
let b4 = BlockBuilderBuilder::new(&client)
.on_parent_block(b3.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, b4)).unwrap();
// // B2 -> C3
let mut builder = BlockBuilderBuilder::new(&client)
.on_parent_block(b2.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap();
// this push is required as otherwise C3 has the same hash as B3 and won't get imported
builder
.push_transfer(Transfer {
from: Sr25519Keyring::Alice.into(),
to: Sr25519Keyring::Ferdie.into(),
amount: 1,
nonce: 1,
})
.unwrap();
let c3 = builder.build().unwrap().block;
block_on(client.import(BlockOrigin::Own, c3)).unwrap();
// A1 -> D2
let mut builder = BlockBuilderBuilder::new(&client)
.on_parent_block(a1.hash())
.fetch_parent_block_number(&client)
.unwrap()
.build()
.unwrap();
// this push is required as otherwise D2 has the same hash as B2 and won't get imported
builder
.push_transfer(Transfer {
from: Sr25519Keyring::Alice.into(),
to: Sr25519Keyring::Ferdie.into(),
amount: 1,
nonce: 0,
})
.unwrap();
let d2 = builder.build().unwrap().block;
block_on(client.import(BlockOrigin::Own, d2)).unwrap();
let genesis_hash = client.chain_info().genesis_hash;
assert_eq!(blockchain.hash(0).unwrap().unwrap(), genesis_hash);
assert_eq!(blockchain.hash(1).unwrap().unwrap(), a1.hash());
assert_eq!(blockchain.hash(2).unwrap().unwrap(), a2.hash());
assert_eq!(blockchain.hash(3).unwrap().unwrap(), a3.hash());
assert_eq!(blockchain.hash(4).unwrap().unwrap(), a4.hash());
assert_eq!(blockchain.hash(5).unwrap().unwrap(), a5.hash());
}
@@ -0,0 +1,24 @@
`default_genesis_config.json` file has been generated by the following code:
```
use crate::genesismap::GenesisStorageBuilder;
#[test]
fn write_default_config_to_tmp_file() {
let j = json::to_string(&GenesisStorageBuilder::default().genesis_config()).unwrap().into_bytes();
let mut file = fs::OpenOptions::new()
.create(true)
.write(true)
.open("/tmp/default_genesis_config.json").unwrap();
file.write_all(&j);
}
```
`:code` field has been manually truncated to reduce file size. Test is only
comparing keys, not the values.
`default_genesis_config_invalid.json` is just a broken copy of
`default_genesis_config.json` with `authorities` field renamed to
`renamed_authorities`.
`default_genesis_config_invalid.json` is just an incomplete copy of
`default_genesis_config.json` with `babe::authorities` field removed.
@@ -0,0 +1,113 @@
{
"system": {},
"babe": {
"authorities": [
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1
],
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
1
],
[
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
1
]
],
"epochConfig": {
"c": [
3,
10
],
"allowed_slots": "PrimaryAndSecondaryPlainSlots"
}
},
"bizinikiwiTest": {
"authorities": [
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y"
]
},
"balances": {
"balances": [
[
"5D34dL5prEUaGNQtPPZ3yN5Y6BnkfXunKXXz6fo7ZJbLwRRH",
100000000000000000
],
[
"5GBNeWRhZc2jXu7D55rBimKYDk8PGk8itRYFTPfC8RJLKG5o",
100000000000000000
],
[
"5Dfis6XL8J2P6JHUnUtArnFWndn62SydeP8ee8sG2ky9nfm9",
100000000000000000
],
[
"5F4H97f7nQovyrbiq4ZetaaviNwThSVcFobcA5aGab6167dK",
100000000000000000
],
[
"5DiDShBWa1fQx6gLzpf3SFBhMinCoyvHM1BWjPNsmXS8hkrW",
100000000000000000
],
[
"5EFb84yH9tpcFuiKUcsmdoF7xeeY3ajG1ZLQimxQoFt9HMKR",
100000000000000000
],
[
"5DZLHESsfGrJ5YzT3HuRPXsSNb589xQ4Unubh1mYLodzKdVY",
100000000000000000
],
[
"5GHJzqvG6tXnngCpG7B12qjUvbo5e4e9z8Xjidk3CQZHxTPZ",
100000000000000000
],
[
"5CUnSsgAyLND3bxxnfNhgWXSe9Wn676JzLpGLgyJv858qhoX",
100000000000000000
],
[
"5CVKn7HAZW1Ky4r7Vkgsr7VEW88C2sHgUNDiwHY9Ct2hjU8q",
100000000000000000
],
[
"5H673aukQ4PeDe1U2nuv1bi32xDEziimh3PZz7hDdYUB7TNz",
100000000000000000
],
[
"5HTe9L15LJryjUAt1jZXZCBPnzbbGnpvFwbjE3NwCWaAqovf",
100000000000000000
],
[
"5D7LFzGpMwHPyDBavkRbWSKWTtJhCaPPZ379wWLT23bJwXJz",
100000000000000000
],
[
"5CLepMARnEgtVR1EkUuJVUvKh97gzergpSxUU3yKGx1v6EwC",
100000000000000000
],
[
"5Chb2UhfvZpmjjEziHbFbotM4quX32ZscRV6QJBt1rUKzz51",
100000000000000000
],
[
"5HmRp3i3ZZk7xsAvbi8hyXVP6whSMnBJGebVC4FsiZVhx52e",
100000000000000000
],
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
100000000000000000
],
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
100000000000000000
],
[
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
100000000000000000
]
]
}
}
@@ -0,0 +1,99 @@
{
"system": {},
"babe": {
"epochConfig": {
"c": [
3,
10
],
"allowed_slots": "PrimaryAndSecondaryPlainSlots"
}
},
"bizinikiwiTest": {
"authorities": [
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y"
]
},
"balances": {
"balances": [
[
"5D34dL5prEUaGNQtPPZ3yN5Y6BnkfXunKXXz6fo7ZJbLwRRH",
100000000000000000
],
[
"5GBNeWRhZc2jXu7D55rBimKYDk8PGk8itRYFTPfC8RJLKG5o",
100000000000000000
],
[
"5Dfis6XL8J2P6JHUnUtArnFWndn62SydeP8ee8sG2ky9nfm9",
100000000000000000
],
[
"5F4H97f7nQovyrbiq4ZetaaviNwThSVcFobcA5aGab6167dK",
100000000000000000
],
[
"5DiDShBWa1fQx6gLzpf3SFBhMinCoyvHM1BWjPNsmXS8hkrW",
100000000000000000
],
[
"5EFb84yH9tpcFuiKUcsmdoF7xeeY3ajG1ZLQimxQoFt9HMKR",
100000000000000000
],
[
"5DZLHESsfGrJ5YzT3HuRPXsSNb589xQ4Unubh1mYLodzKdVY",
100000000000000000
],
[
"5GHJzqvG6tXnngCpG7B12qjUvbo5e4e9z8Xjidk3CQZHxTPZ",
100000000000000000
],
[
"5CUnSsgAyLND3bxxnfNhgWXSe9Wn676JzLpGLgyJv858qhoX",
100000000000000000
],
[
"5CVKn7HAZW1Ky4r7Vkgsr7VEW88C2sHgUNDiwHY9Ct2hjU8q",
100000000000000000
],
[
"5H673aukQ4PeDe1U2nuv1bi32xDEziimh3PZz7hDdYUB7TNz",
100000000000000000
],
[
"5HTe9L15LJryjUAt1jZXZCBPnzbbGnpvFwbjE3NwCWaAqovf",
100000000000000000
],
[
"5D7LFzGpMwHPyDBavkRbWSKWTtJhCaPPZ379wWLT23bJwXJz",
100000000000000000
],
[
"5CLepMARnEgtVR1EkUuJVUvKh97gzergpSxUU3yKGx1v6EwC",
100000000000000000
],
[
"5Chb2UhfvZpmjjEziHbFbotM4quX32ZscRV6QJBt1rUKzz51",
100000000000000000
],
[
"5HmRp3i3ZZk7xsAvbi8hyXVP6whSMnBJGebVC4FsiZVhx52e",
100000000000000000
],
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
100000000000000000
],
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
100000000000000000
],
[
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
100000000000000000
]
]
}
}
@@ -0,0 +1,113 @@
{
"system": {},
"babe": {
"renamed_authorities": [
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1
],
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
1
],
[
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
1
]
],
"epochConfig": {
"c": [
3,
10
],
"allowed_slots": "PrimaryAndSecondaryPlainSlots"
}
},
"bizinikiwiTest": {
"authorities": [
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y"
]
},
"balances": {
"balances": [
[
"5D34dL5prEUaGNQtPPZ3yN5Y6BnkfXunKXXz6fo7ZJbLwRRH",
100000000000000000
],
[
"5GBNeWRhZc2jXu7D55rBimKYDk8PGk8itRYFTPfC8RJLKG5o",
100000000000000000
],
[
"5Dfis6XL8J2P6JHUnUtArnFWndn62SydeP8ee8sG2ky9nfm9",
100000000000000000
],
[
"5F4H97f7nQovyrbiq4ZetaaviNwThSVcFobcA5aGab6167dK",
100000000000000000
],
[
"5DiDShBWa1fQx6gLzpf3SFBhMinCoyvHM1BWjPNsmXS8hkrW",
100000000000000000
],
[
"5EFb84yH9tpcFuiKUcsmdoF7xeeY3ajG1ZLQimxQoFt9HMKR",
100000000000000000
],
[
"5DZLHESsfGrJ5YzT3HuRPXsSNb589xQ4Unubh1mYLodzKdVY",
100000000000000000
],
[
"5GHJzqvG6tXnngCpG7B12qjUvbo5e4e9z8Xjidk3CQZHxTPZ",
100000000000000000
],
[
"5CUnSsgAyLND3bxxnfNhgWXSe9Wn676JzLpGLgyJv858qhoX",
100000000000000000
],
[
"5CVKn7HAZW1Ky4r7Vkgsr7VEW88C2sHgUNDiwHY9Ct2hjU8q",
100000000000000000
],
[
"5H673aukQ4PeDe1U2nuv1bi32xDEziimh3PZz7hDdYUB7TNz",
100000000000000000
],
[
"5HTe9L15LJryjUAt1jZXZCBPnzbbGnpvFwbjE3NwCWaAqovf",
100000000000000000
],
[
"5D7LFzGpMwHPyDBavkRbWSKWTtJhCaPPZ379wWLT23bJwXJz",
100000000000000000
],
[
"5CLepMARnEgtVR1EkUuJVUvKh97gzergpSxUU3yKGx1v6EwC",
100000000000000000
],
[
"5Chb2UhfvZpmjjEziHbFbotM4quX32ZscRV6QJBt1rUKzz51",
100000000000000000
],
[
"5HmRp3i3ZZk7xsAvbi8hyXVP6whSMnBJGebVC4FsiZVhx52e",
100000000000000000
],
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
100000000000000000
],
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
100000000000000000
],
[
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
100000000000000000
]
]
}
}
@@ -0,0 +1,113 @@
{
"system": {},
"babex": {
"authorities": [
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1
],
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
1
],
[
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
1
]
],
"epochConfig": {
"c": [
3,
10
],
"allowed_slots": "PrimaryAndSecondaryPlainSlots"
}
},
"bizinikiwiTest": {
"authorities": [
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y"
]
},
"balances": {
"balances": [
[
"5D34dL5prEUaGNQtPPZ3yN5Y6BnkfXunKXXz6fo7ZJbLwRRH",
100000000000000000
],
[
"5GBNeWRhZc2jXu7D55rBimKYDk8PGk8itRYFTPfC8RJLKG5o",
100000000000000000
],
[
"5Dfis6XL8J2P6JHUnUtArnFWndn62SydeP8ee8sG2ky9nfm9",
100000000000000000
],
[
"5F4H97f7nQovyrbiq4ZetaaviNwThSVcFobcA5aGab6167dK",
100000000000000000
],
[
"5DiDShBWa1fQx6gLzpf3SFBhMinCoyvHM1BWjPNsmXS8hkrW",
100000000000000000
],
[
"5EFb84yH9tpcFuiKUcsmdoF7xeeY3ajG1ZLQimxQoFt9HMKR",
100000000000000000
],
[
"5DZLHESsfGrJ5YzT3HuRPXsSNb589xQ4Unubh1mYLodzKdVY",
100000000000000000
],
[
"5GHJzqvG6tXnngCpG7B12qjUvbo5e4e9z8Xjidk3CQZHxTPZ",
100000000000000000
],
[
"5CUnSsgAyLND3bxxnfNhgWXSe9Wn676JzLpGLgyJv858qhoX",
100000000000000000
],
[
"5CVKn7HAZW1Ky4r7Vkgsr7VEW88C2sHgUNDiwHY9Ct2hjU8q",
100000000000000000
],
[
"5H673aukQ4PeDe1U2nuv1bi32xDEziimh3PZz7hDdYUB7TNz",
100000000000000000
],
[
"5HTe9L15LJryjUAt1jZXZCBPnzbbGnpvFwbjE3NwCWaAqovf",
100000000000000000
],
[
"5D7LFzGpMwHPyDBavkRbWSKWTtJhCaPPZ379wWLT23bJwXJz",
100000000000000000
],
[
"5CLepMARnEgtVR1EkUuJVUvKh97gzergpSxUU3yKGx1v6EwC",
100000000000000000
],
[
"5Chb2UhfvZpmjjEziHbFbotM4quX32ZscRV6QJBt1rUKzz51",
100000000000000000
],
[
"5HmRp3i3ZZk7xsAvbi8hyXVP6whSMnBJGebVC4FsiZVhx52e",
100000000000000000
],
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
100000000000000000
],
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
100000000000000000
],
[
"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y",
100000000000000000
]
]
}
}
@@ -0,0 +1,252 @@
// 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.
//! # bizinikiwi-test pallet
//!
//! Provides functionality used in unit-tests of numerous modules across bizinikiwi that require
//! functioning runtime. Some calls are allowed to be submitted as unsigned extrinsics, however most
//! of them requires signing. Refer to `pallet::Call` for further details.
use alloc::{vec, vec::Vec};
use pezframe_support::{pezpallet_prelude::*, storage};
use pezsp_core::sr25519::Public;
use pezsp_runtime::{
traits::Hash,
transaction_validity::{
InvalidTransaction, TransactionSource, TransactionValidity, ValidTransaction,
},
};
pub use self::pallet::*;
const LOG_TARGET: &str = "bizinikiwi_test_pallet";
#[pezframe_support::pallet(dev_mode)]
pub mod pallet {
use super::*;
use crate::TransferData;
use pezframe_system::pezpallet_prelude::*;
use pezsp_core::storage::well_known_keys;
use pezsp_runtime::{traits::BlakeTwo256, transaction_validity::TransactionPriority, Perbill};
#[pallet::pallet]
#[pallet::without_storage_info]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: pezframe_system::Config {}
#[pallet::storage]
#[pallet::getter(fn authorities)]
pub type Authorities<T> = StorageValue<_, Vec<Public>, ValueQuery>;
#[pallet::genesis_config]
#[derive(pezframe_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
pub authorities: Vec<Public>,
#[serde(skip)]
pub _config: core::marker::PhantomData<T>,
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {
<Authorities<T>>::put(self.authorities.clone());
}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Legacy call used in transaction pool benchmarks.
#[pallet::call_index(0)]
#[pallet::weight(100)]
pub fn bench_call(_origin: OriginFor<T>, _transfer: TransferData) -> DispatchResult {
Ok(())
}
/// Implicitly fill a block body with some data.
#[pallet::call_index(1)]
#[pallet::weight(100)]
pub fn include_data(origin: OriginFor<T>, _data: Vec<u8>) -> DispatchResult {
pezframe_system::ensure_signed(origin)?;
Ok(())
}
/// Put/delete some data from storage. Intended to use as an unsigned extrinsic.
#[pallet::call_index(2)]
#[pallet::weight(100)]
pub fn storage_change(
_origin: OriginFor<T>,
key: Vec<u8>,
value: Option<Vec<u8>>,
) -> DispatchResult {
match value {
Some(value) => storage::unhashed::put_raw(&key, &value),
None => storage::unhashed::kill(&key),
}
Ok(())
}
/// Write a key value pair to the offchain database.
#[pallet::call_index(3)]
#[pallet::weight(100)]
pub fn offchain_index_set(
origin: OriginFor<T>,
key: Vec<u8>,
value: Vec<u8>,
) -> DispatchResult {
pezframe_system::ensure_signed(origin)?;
pezsp_io::offchain_index::set(&key, &value);
Ok(())
}
/// Remove a key and an associated value from the offchain database.
#[pallet::call_index(4)]
#[pallet::weight(100)]
pub fn offchain_index_clear(origin: OriginFor<T>, key: Vec<u8>) -> DispatchResult {
pezframe_system::ensure_signed(origin)?;
pezsp_io::offchain_index::clear(&key);
Ok(())
}
/// Create an index for this call.
#[pallet::call_index(5)]
#[pallet::weight(100)]
pub fn indexed_call(origin: OriginFor<T>, data: Vec<u8>) -> DispatchResult {
pezframe_system::ensure_signed(origin)?;
let content_hash = pezsp_io::hashing::blake2_256(&data);
let extrinsic_index: u32 =
storage::unhashed::get(well_known_keys::EXTRINSIC_INDEX).unwrap();
pezsp_io::transaction_index::index(extrinsic_index, data.len() as u32, content_hash);
Ok(())
}
/// Deposit given digest items into the system storage. They will be included in a header
/// during finalization.
#[pallet::call_index(6)]
#[pallet::weight(100)]
pub fn deposit_log_digest_item(
_origin: OriginFor<T>,
log: pezsp_runtime::generic::DigestItem,
) -> DispatchResult {
<pezframe_system::Pallet<T>>::deposit_log(log);
Ok(())
}
/// This call is validated as `ValidTransaction` with given priority.
#[pallet::call_index(7)]
#[pallet::weight(100)]
pub fn call_with_priority(
_origin: OriginFor<T>,
_priority: TransactionPriority,
) -> DispatchResult {
Ok(())
}
/// This call is validated as non-propagable `ValidTransaction`.
#[pallet::call_index(8)]
#[pallet::weight(100)]
pub fn call_do_not_propagate(_origin: OriginFor<T>) -> DispatchResult {
Ok(())
}
/// Fill the block weight up to the given ratio.
#[pallet::call_index(9)]
#[pallet::weight(*_ratio * T::BlockWeights::get().max_block)]
pub fn fill_block(origin: OriginFor<T>, _ratio: Perbill) -> DispatchResult {
ensure_signed(origin)?;
Ok(())
}
/// Read X times from the state some data.
///
/// Panics if it can not read `X` times.
#[pallet::call_index(10)]
#[pallet::weight(100)]
pub fn read(_origin: OriginFor<T>, count: u32) -> DispatchResult {
Self::execute_read(count, false)
}
/// Read X times from the state some data and then panic!
///
/// Returns `Ok` if it didn't read anything.
#[pallet::call_index(11)]
#[pallet::weight(100)]
pub fn read_and_panic(_origin: OriginFor<T>, count: u32) -> DispatchResult {
Self::execute_read(count, true)
}
}
impl<T: Config> Pallet<T> {
fn execute_read(read: u32, panic_at_end: bool) -> DispatchResult {
let mut next_key = vec![];
for _ in 0..(read as usize) {
if let Some(next) = pezsp_io::storage::next_key(&next_key) {
// Read the value
pezsp_io::storage::get(&next);
next_key = next;
} else {
if panic_at_end {
return Ok(());
} else {
panic!("Could not read {read} times from the state");
}
}
}
if panic_at_end {
panic!("BYE")
} else {
Ok(())
}
}
}
#[pallet::validate_unsigned]
impl<T: Config> ValidateUnsigned for Pallet<T> {
type Call = Call<T>;
fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity {
log::trace!(target: LOG_TARGET, "validate_unsigned {call:?}");
match call {
// Some tests do not need to be complicated with signer and nonce, some need
// reproducible block hash (call signature can't be there).
// Offchain testing requires storage_change.
Call::deposit_log_digest_item { .. } |
Call::storage_change { .. } |
Call::read { .. } |
Call::read_and_panic { .. } => Ok(ValidTransaction {
provides: vec![BlakeTwo256::hash_of(&call).encode()],
..Default::default()
}),
_ => Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
}
}
}
}
pub fn validate_runtime_call<T: pallet::Config>(call: &pallet::Call<T>) -> TransactionValidity {
log::trace!(target: LOG_TARGET, "validate_runtime_call {call:?}");
match call {
Call::call_do_not_propagate {} =>
Ok(ValidTransaction { propagate: false, ..Default::default() }),
Call::call_with_priority { priority } =>
Ok(ValidTransaction { priority: *priority, ..Default::default() }),
_ => Ok(Default::default()),
}
}
@@ -0,0 +1,229 @@
// 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.
//! Provides utils for building the `Extrinsic` instances used with `bizinikiwi-test-runtime`.
use crate::{
bizinikiwi_test_pallet::pallet::Call as PalletCall, AccountId, Balance, BalancesCall,
CheckBizinikiwiCall, Extrinsic, Nonce, Pair, RuntimeCall, SignedPayload, TransferData,
};
use codec::Encode;
use pezframe_metadata_hash_extension::CheckMetadataHash;
use pezframe_system::{CheckNonce, CheckWeight};
use pezsp_core::crypto::Pair as TraitPair;
use pezsp_keyring::Sr25519Keyring;
use pezsp_runtime::{
generic::Preamble, traits::TransactionExtension, transaction_validity::TransactionPriority,
Perbill,
};
/// Transfer used in test bizinikiwi pallet. Extrinsic is created and signed using this data.
#[derive(Clone)]
pub struct Transfer {
/// Transfer sender and signer of created extrinsic
pub from: Pair,
/// The recipient of the transfer
pub to: AccountId,
/// Amount of transfer
pub amount: Balance,
/// Sender's account nonce at which transfer is valid
pub nonce: u64,
}
impl Transfer {
/// Convert into a signed unchecked extrinsic.
pub fn into_unchecked_extrinsic(self) -> Extrinsic {
ExtrinsicBuilder::new_transfer(self).build()
}
}
impl Default for TransferData {
fn default() -> Self {
Self {
from: Sr25519Keyring::Alice.into(),
to: Sr25519Keyring::Bob.into(),
amount: 0,
nonce: 0,
}
}
}
/// If feasible converts given `Extrinsic` to `TransferData`
impl TryFrom<&Extrinsic> for TransferData {
type Error = ();
fn try_from(uxt: &Extrinsic) -> Result<Self, Self::Error> {
match uxt {
Extrinsic {
function: RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value }),
preamble: Preamble::Signed(from, _, ((CheckNonce(nonce), ..), ..)),
} => Ok(TransferData { from: *from, to: *dest, amount: *value, nonce: *nonce }),
Extrinsic {
function: RuntimeCall::BizinikiwiTest(PalletCall::bench_call { transfer }),
preamble: Preamble::Bare(_),
} => Ok(transfer.clone()),
_ => Err(()),
}
}
}
/// Generates `Extrinsic`
pub struct ExtrinsicBuilder {
function: RuntimeCall,
signer: Option<Pair>,
nonce: Option<Nonce>,
metadata_hash: Option<[u8; 32]>,
}
impl ExtrinsicBuilder {
/// Create builder for given `RuntimeCall`. By default `Extrinsic` will be signed by `Alice`.
pub fn new(function: impl Into<RuntimeCall>) -> Self {
Self {
function: function.into(),
signer: Some(Sr25519Keyring::Alice.pair()),
nonce: None,
metadata_hash: None,
}
}
/// Create builder for given `RuntimeCall`. `Extrinsic` will be unsigned.
pub fn new_unsigned(function: impl Into<RuntimeCall>) -> Self {
Self { function: function.into(), signer: None, nonce: None, metadata_hash: None }
}
/// Create builder for `pezpallet_call::bench_transfer` from given `TransferData`.
pub fn new_bench_call(transfer: TransferData) -> Self {
Self::new_unsigned(PalletCall::bench_call { transfer })
}
/// Create builder for given `Transfer`. Transfer `nonce` will be used as `Extrinsic` nonce.
/// Transfer `from` will be used as Extrinsic signer.
pub fn new_transfer(transfer: Transfer) -> Self {
Self {
nonce: Some(transfer.nonce),
signer: Some(transfer.from.clone()),
metadata_hash: None,
..Self::new(BalancesCall::transfer_allow_death {
dest: transfer.to,
value: transfer.amount,
})
}
}
/// Create builder for `PalletCall::include_data` call using given parameters
pub fn new_include_data(data: Vec<u8>) -> Self {
Self::new(PalletCall::include_data { data })
}
/// Create builder for `PalletCall::storage_change` call using given parameters. Will
/// create unsigned Extrinsic.
pub fn new_storage_change(key: Vec<u8>, value: Option<Vec<u8>>) -> Self {
Self::new_unsigned(PalletCall::storage_change { key, value })
}
/// Create builder for `PalletCall::offchain_index_set` call using given parameters
pub fn new_offchain_index_set(key: Vec<u8>, value: Vec<u8>) -> Self {
Self::new(PalletCall::offchain_index_set { key, value })
}
/// Create builder for `PalletCall::offchain_index_clear` call using given parameters
pub fn new_offchain_index_clear(key: Vec<u8>) -> Self {
Self::new(PalletCall::offchain_index_clear { key })
}
/// Create builder for `PalletCall::indexed_call` call using given parameters
pub fn new_indexed_call(data: Vec<u8>) -> Self {
Self::new(PalletCall::indexed_call { data })
}
/// Create builder for `PalletCall::new_deposit_log_digest_item` call using given `log`
pub fn new_deposit_log_digest_item(log: pezsp_runtime::generic::DigestItem) -> Self {
Self::new_unsigned(PalletCall::deposit_log_digest_item { log })
}
/// Create builder for `PalletCall::Call::new_deposit_log_digest_item`
pub fn new_fill_block(ratio: Perbill) -> Self {
Self::new(PalletCall::fill_block { ratio })
}
/// Create builder for `PalletCall::call_do_not_propagate` call using given parameters
pub fn new_call_do_not_propagate() -> Self {
Self::new(PalletCall::call_do_not_propagate {})
}
/// Create builder for `PalletCall::call_with_priority` call using given parameters
pub fn new_call_with_priority(priority: TransactionPriority) -> Self {
Self::new(PalletCall::call_with_priority { priority })
}
/// Create builder for `PalletCall::read` call using given parameters
pub fn new_read(count: u32) -> Self {
Self::new_unsigned(PalletCall::read { count })
}
/// Create builder for `PalletCall::read` call using given parameters
pub fn new_read_and_panic(count: u32) -> Self {
Self::new_unsigned(PalletCall::read_and_panic { count })
}
/// Unsigned `Extrinsic` will be created
pub fn unsigned(mut self) -> Self {
self.signer = None;
self
}
/// Given `nonce` will be set in `Extrinsic`
pub fn nonce(mut self, nonce: Nonce) -> Self {
self.nonce = Some(nonce);
self
}
/// Extrinsic will be signed by `signer`
pub fn signer(mut self, signer: Pair) -> Self {
self.signer = Some(signer);
self
}
/// Metadata hash to put into the signed data of the extrinsic.
pub fn metadata_hash(mut self, metadata_hash: [u8; 32]) -> Self {
self.metadata_hash = Some(metadata_hash);
self
}
/// Build `Extrinsic` using embedded parameters
pub fn build(self) -> Extrinsic {
if let Some(signer) = self.signer {
let tx_ext = (
(CheckNonce::from(self.nonce.unwrap_or(0)), CheckWeight::new()),
CheckBizinikiwiCall {},
self.metadata_hash
.map(CheckMetadataHash::new_with_custom_hash)
.unwrap_or_else(|| CheckMetadataHash::new(false)),
pezframe_system::WeightReclaim::new(),
);
let raw_payload = SignedPayload::from_raw(
self.function.clone(),
tx_ext.clone(),
tx_ext.implicit().unwrap(),
);
let signature = raw_payload.using_encoded(|e| signer.sign(e));
Extrinsic::new_signed(self.function, signer.public(), signature, tx_ext)
} else {
Extrinsic::new_bare(self.function)
}
}
}
@@ -0,0 +1,182 @@
// 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.
//! Tool for creating the genesis block.
use super::{
currency, bizinikiwi_test_pallet, wasm_binary_unwrap, AccountId, Balance, RuntimeGenesisConfig,
};
use codec::Encode;
use pezsc_service::construct_genesis_block;
use pezsp_core::{
sr25519,
storage::{well_known_keys, StateVersion, Storage},
Pair,
};
use pezsp_keyring::Sr25519Keyring;
use pezsp_runtime::{
traits::{Block as BlockT, Hash as HashT, Header as HeaderT},
BuildStorage,
};
/// Builder for generating storage from bizinikiwi-test-runtime genesis config.
///
/// Default storage can be extended with additional key-value pairs.
pub struct GenesisStorageBuilder {
/// Authorities accounts used by any component requiring an authority set (e.g. babe).
authorities: Vec<AccountId>,
/// Accounts to be endowed with some funds.
balances: Vec<(AccountId, u64)>,
/// Override default number of heap pages.
heap_pages_override: Option<u64>,
/// Additional storage key pairs that will be added to the genesis map.
extra_storage: Storage,
/// Optional wasm code override.
wasm_code: Option<Vec<u8>>,
}
impl Default for GenesisStorageBuilder {
/// Creates a builder with default settings for `bizinikiwi_test_runtime`.
fn default() -> Self {
Self::new(
vec![
Sr25519Keyring::Alice.into(),
Sr25519Keyring::Bob.into(),
Sr25519Keyring::Charlie.into(),
],
(0..16_usize)
.into_iter()
.map(|i| Sr25519Keyring::numeric(i).public())
.chain(vec![
Sr25519Keyring::Alice.into(),
Sr25519Keyring::Bob.into(),
Sr25519Keyring::Charlie.into(),
])
.collect(),
1000 * currency::DOLLARS,
)
}
}
impl GenesisStorageBuilder {
/// Creates a storage builder for genesis config. `substrage test runtime`
/// [`RuntimeGenesisConfig`] is initialized with provided `authorities`, `endowed_accounts` with
/// given balance. Key-value pairs from `extra_storage` will be injected into built storage.
/// `HEAP_PAGES` key and value will also be placed into storage.
pub fn new(
authorities: Vec<AccountId>,
endowed_accounts: Vec<AccountId>,
balance: Balance,
) -> Self {
GenesisStorageBuilder {
authorities,
balances: endowed_accounts.into_iter().map(|a| (a, balance)).collect(),
heap_pages_override: None,
extra_storage: Default::default(),
wasm_code: None,
}
}
/// Override default wasm code to be placed into RuntimeGenesisConfig.
pub fn with_wasm_code(mut self, wasm_code: &Option<Vec<u8>>) -> Self {
self.wasm_code = wasm_code.clone();
self
}
pub fn with_heap_pages(mut self, heap_pages_override: Option<u64>) -> Self {
self.heap_pages_override = heap_pages_override;
self
}
pub fn with_extra_storage(mut self, storage: Storage) -> Self {
self.extra_storage = storage;
self
}
/// A `RuntimeGenesisConfig` from internal configuration
pub fn genesis_config(&self) -> RuntimeGenesisConfig {
let authorities_sr25519: Vec<_> = self
.authorities
.clone()
.into_iter()
.map(|id| sr25519::Public::from(id))
.collect();
RuntimeGenesisConfig {
system: Default::default(),
babe: pezpallet_babe::GenesisConfig {
authorities: authorities_sr25519
.clone()
.into_iter()
.map(|x| (x.into(), 1))
.collect(),
..Default::default()
},
bizinikiwi_test: bizinikiwi_test_pallet::GenesisConfig {
authorities: authorities_sr25519.clone(),
..Default::default()
},
balances: pezpallet_balances::GenesisConfig {
balances: self.balances.clone(),
..Default::default()
},
}
}
/// Builds the `RuntimeGenesisConfig` and returns its storage.
pub fn build(self) -> Storage {
let mut storage = self
.genesis_config()
.build_storage()
.expect("Build storage from bizinikiwi-test-runtime RuntimeGenesisConfig");
if let Some(heap_pages) = self.heap_pages_override {
storage.top.insert(well_known_keys::HEAP_PAGES.into(), heap_pages.encode());
}
storage.top.insert(
well_known_keys::CODE.into(),
self.wasm_code.clone().unwrap_or(wasm_binary_unwrap().to_vec()),
);
storage.top.extend(self.extra_storage.top.clone());
storage.children_default.extend(self.extra_storage.children_default.clone());
storage
}
}
pub fn insert_genesis_block(storage: &mut Storage) -> pezsp_core::hash::H256 {
let child_roots = storage.children_default.iter().map(|(sk, child_content)| {
let state_root =
<<<crate::Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
child_content.data.clone().into_iter().collect(),
pezsp_runtime::StateVersion::V1,
);
(sk.clone(), state_root.encode())
});
// add child roots to storage
storage.top.extend(child_roots);
let state_root = <<<crate::Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
storage.top.clone().into_iter().collect(),
pezsp_runtime::StateVersion::V1,
);
let block: crate::Block = construct_genesis_block(state_root, StateVersion::V1);
let genesis_hash = block.header.hash();
genesis_hash
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,37 @@
[package]
name = "bizinikiwi-test-runtime-transaction-pool"
version = "2.0.0"
authors.workspace = true
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]
async-trait = { workspace = true }
codec = { workspace = true, default-features = true }
futures = { workspace = true }
log = { workspace = true }
parking_lot = { workspace = true, default-features = true }
pezsc-transaction-pool = { workspace = true, default-features = true }
pezsc-transaction-pool-api = { workspace = true, default-features = true }
pezsp-blockchain = { workspace = true, default-features = true }
pezsp-runtime = { workspace = true, default-features = true }
bizinikiwi-test-runtime-client = { workspace = true }
thiserror = { workspace = true }
[features]
runtime-benchmarks = [
"pezsc-transaction-pool-api/runtime-benchmarks",
"pezsc-transaction-pool/runtime-benchmarks",
"pezsp-blockchain/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"bizinikiwi-test-runtime-client/runtime-benchmarks",
]
@@ -0,0 +1,559 @@
// 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.
//! Test utils for the transaction pool together with the test runtime.
//!
//! See [`TestApi`] for more information.
use async_trait::async_trait;
use codec::Encode;
use parking_lot::RwLock;
use pezsc_transaction_pool::{ChainApi, ValidateTransactionPriority};
use pezsp_blockchain::{CachedHeaderMetadata, HashAndNumber, TreeRoute};
use pezsp_runtime::{
generic::{self, BlockId},
traits::{
BlakeTwo256, Block as BlockT, Hash as HashT, Header as _, NumberFor, TrailingZeroInput,
},
transaction_validity::{
InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError,
ValidTransaction,
},
};
use std::{
collections::{BTreeMap, HashMap, HashSet},
sync::Arc,
};
use bizinikiwi_test_runtime_client::{
runtime::{
AccountId, Block, BlockNumber, Extrinsic, ExtrinsicBuilder, Hash, Header, Nonce, Transfer,
TransferData,
},
Sr25519Keyring::{self, *},
};
/// Error type used by [`TestApi`].
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct Error(#[from] pub pezsc_transaction_pool_api::error::Error);
impl pezsc_transaction_pool_api::error::IntoPoolError for Error {
fn into_pool_error(self) -> Result<pezsc_transaction_pool_api::error::Error, Self> {
Ok(self.0)
}
}
pub enum IsBestBlock {
Yes,
No,
}
impl IsBestBlock {
pub fn is_best(&self) -> bool {
matches!(self, Self::Yes)
}
}
impl From<bool> for IsBestBlock {
fn from(is_best: bool) -> Self {
if is_best {
Self::Yes
} else {
Self::No
}
}
}
#[derive(Default)]
pub struct ChainState {
pub block_by_number: BTreeMap<BlockNumber, Vec<(Block, IsBestBlock)>>,
pub block_by_hash: HashMap<Hash, Block>,
pub nonces: HashMap<Hash, HashMap<AccountId, u64>>,
pub invalid_hashes: HashSet<Hash>,
pub priorities: HashMap<Hash, u64>,
pub valid_till_blocks: HashMap<Hash, u64>,
}
/// Test Api for transaction pool.
pub struct TestApi {
valid_modifier: RwLock<Box<dyn Fn(&mut ValidTransaction) + Send + Sync>>,
chain: RwLock<ChainState>,
validation_requests: RwLock<Vec<Extrinsic>>,
enable_stale_check: bool,
}
impl TestApi {
/// Test Api with Alice nonce set initially.
pub fn with_alice_nonce(nonce: u64) -> Self {
let api = Self::empty();
assert_eq!(api.chain.read().block_by_hash.len(), 1);
assert_eq!(api.chain.read().nonces.len(), 1);
api.chain
.write()
.nonces
.values_mut()
.nth(0)
.map(|h| h.insert(Alice.into(), nonce));
api
}
/// Default Test Api
pub fn empty() -> Self {
let api = TestApi {
valid_modifier: RwLock::new(Box::new(|_| {})),
chain: Default::default(),
validation_requests: RwLock::new(Default::default()),
enable_stale_check: false,
};
// Push genesis block
api.push_block(0, Vec::new(), true);
let hash0 = *api.chain.read().block_by_hash.keys().nth(0).unwrap();
api.chain.write().nonces.insert(hash0, Default::default());
api
}
pub fn enable_stale_check(mut self) -> Self {
self.enable_stale_check = true;
self
}
/// Set hook on modify valid result of transaction.
pub fn set_valid_modifier(&self, modifier: Box<dyn Fn(&mut ValidTransaction) + Send + Sync>) {
*self.valid_modifier.write() = modifier;
}
/// Push block under given number.
pub fn push_block(
&self,
block_number: BlockNumber,
xts: Vec<Extrinsic>,
is_best_block: bool,
) -> Header {
let parent_hash = {
let chain = self.chain.read();
block_number
.checked_sub(1)
.and_then(|num| {
chain.block_by_number.get(&num).map(|blocks| blocks[0].0.header.hash())
})
.unwrap_or_default()
};
self.push_block_with_parent(parent_hash, xts, is_best_block)
}
/// Push a block using the given `parent`.
///
/// Panics if `parent` does not exists.
pub fn push_block_with_parent(
&self,
parent: Hash,
xts: Vec<Extrinsic>,
is_best_block: bool,
) -> Header {
// `Hash::default()` is the genesis parent hash
let block_number = if parent == Hash::default() {
0
} else {
*self
.chain
.read()
.block_by_hash
.get(&parent)
.expect("`parent` exists")
.header()
.number() + 1
};
let header = Header {
number: block_number,
digest: Default::default(),
extrinsics_root: Hash::random(),
parent_hash: parent,
state_root: Default::default(),
};
self.add_block(Block::new(header.clone(), xts), is_best_block);
header
}
/// Add a block to the internal state.
pub fn add_block(&self, block: Block, is_best_block: bool) {
let hash = block.header.hash();
let block_number = block.header.number();
let mut chain = self.chain.write();
chain.block_by_hash.insert(hash, block.clone());
if *block_number > 0 {
// copy nonces to new block
let prev_nonces = chain
.nonces
.get(block.header.parent_hash())
.expect("there shall be nonces for parent block")
.clone();
chain.nonces.insert(hash, prev_nonces);
}
log::info!(
"add_block: {:?} {:?} {:?} nonces:{:#?}",
block_number,
hash,
block.header.parent_hash(),
chain.nonces
);
if is_best_block {
chain
.block_by_number
.entry(*block_number)
.or_default()
.iter_mut()
.for_each(|x| {
x.1 = IsBestBlock::No;
});
}
chain
.block_by_number
.entry(*block_number)
.or_default()
.push((block, is_best_block.into()));
}
fn hash_and_length_inner(ex: &Extrinsic) -> (Hash, usize) {
let encoded = ex.encode();
(BlakeTwo256::hash(&encoded), encoded.len())
}
/// Mark some transaction is invalid.
///
/// Next time transaction pool will try to validate this
/// extrinsic, api will return invalid result.
pub fn add_invalid(&self, xts: &Extrinsic) {
self.chain.write().invalid_hashes.insert(Self::hash_and_length_inner(xts).0);
}
/// Remove a transaction that was previously declared as invalid via `[Self::add_invalid]`.
///
/// Next time transaction pool will try to validate this
/// extrinsic, api will succeed.
pub fn remove_invalid(&self, xts: &Extrinsic) {
self.chain.write().invalid_hashes.remove(&Self::hash_and_length_inner(xts).0);
}
/// Set a transaction priority.
pub fn set_priority(&self, xts: &Extrinsic, priority: u64) {
self.chain
.write()
.priorities
.insert(Self::hash_and_length_inner(xts).0, priority);
}
/// Set a transaction mortality (block at which it will expire).
pub fn set_valid_till(&self, xts: &Extrinsic, valid_till: u64) {
self.chain
.write()
.valid_till_blocks
.insert(Self::hash_and_length_inner(xts).0, valid_till);
}
/// Query validation requests received.
pub fn validation_requests(&self) -> Vec<Extrinsic> {
self.validation_requests.read().clone()
}
/// get a reference to the chain state
pub fn chain(&self) -> &RwLock<ChainState> {
&self.chain
}
/// Set nonce in the inner state for given block.
pub fn set_nonce(&self, at: Hash, account: AccountId, nonce: u64) {
let mut chain = self.chain.write();
chain.nonces.entry(at).and_modify(|h| {
h.insert(account, nonce);
});
log::debug!("set_nonce: {:?} nonces:{:#?}", at, chain.nonces);
}
/// Increment nonce in the inner state for given block.
pub fn increment_nonce_at_block(&self, at: Hash, account: AccountId) {
let mut chain = self.chain.write();
chain.nonces.entry(at).and_modify(|h| {
h.entry(account).and_modify(|n| *n += 1).or_insert(1);
});
log::debug!("increment_nonce_at_block: {:?} nonces:{:#?}", at, chain.nonces);
}
/// Increment nonce in the inner state.
pub fn increment_nonce(&self, account: AccountId) {
let mut chain = self.chain.write();
// if no particular block was given, then update nonce everywhere
chain.nonces.values_mut().for_each(|h| {
h.entry(account).and_modify(|n| *n += 1).or_insert(1);
})
}
/// Calculate a tree route between the two given blocks.
pub fn tree_route(
&self,
from: Hash,
to: Hash,
) -> Result<pezsp_blockchain::TreeRoute<Block>, Error> {
pezsp_blockchain::tree_route(self, from, to)
}
/// Helper function for mapping block number to hash. Use if mapping shall not fail.
pub fn expect_hash_from_number(&self, n: BlockNumber) -> Hash {
self.block_id_to_hash(&BlockId::Number(n)).unwrap().unwrap()
}
/// Helper function for getting genesis hash
pub fn genesis_hash(&self) -> Hash {
self.expect_hash_from_number(0)
}
pub fn expect_hash_and_number(&self, n: BlockNumber) -> HashAndNumber<Block> {
HashAndNumber { hash: self.expect_hash_from_number(n), number: n }
}
}
trait TagFrom {
fn tag_from(&self) -> u8;
}
impl TagFrom for AccountId {
fn tag_from(&self) -> u8 {
let f = Sr25519Keyring::iter().enumerate().find(|k| AccountId::from(k.1) == *self);
u8::try_from(f.unwrap().0).unwrap()
}
}
#[async_trait]
impl ChainApi for TestApi {
type Block = Block;
type Error = Error;
async fn validate_transaction(
&self,
at: <Self::Block as BlockT>::Hash,
source: TransactionSource,
uxt: Arc<<Self::Block as BlockT>::Extrinsic>,
_: ValidateTransactionPriority,
) -> Result<TransactionValidity, Error> {
self.validate_transaction_blocking(at, source, uxt)
}
fn validate_transaction_blocking(
&self,
at: <Self::Block as BlockT>::Hash,
_source: TransactionSource,
uxt: Arc<<Self::Block as BlockT>::Extrinsic>,
) -> Result<TransactionValidity, Error> {
let uxt = (*uxt).clone();
self.validation_requests.write().push(uxt.clone());
let block_number;
match self.block_id_to_number(&BlockId::Hash(at)) {
Ok(Some(number)) => {
let found_best = self
.chain
.read()
.block_by_number
.get(&number)
.map(|blocks| blocks.iter().any(|b| b.1.is_best()))
.unwrap_or(false);
block_number = Some(number);
// If there is no best block, we don't know based on which block we should validate
// the transaction. (This is not required for this test function, but in real
// environment it would fail because of this).
if !found_best {
return Ok(Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(
1,
))));
}
},
Ok(None) =>
return Ok(Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(2)))),
Err(e) => return Err(e),
}
let (requires, provides) = if let Ok(transfer) = TransferData::try_from(&uxt) {
let chain_nonce = self
.chain
.read()
.nonces
.get(&at)
.expect("nonces must be there for every block")
.get(&transfer.from)
.cloned()
.unwrap_or(0);
let requires = if chain_nonce == transfer.nonce {
vec![]
} else {
if self.enable_stale_check {
vec![vec![transfer.from.tag_from(), (transfer.nonce - 1) as u8]]
} else {
vec![vec![(transfer.nonce - 1) as u8]]
}
};
let provides = if self.enable_stale_check {
vec![vec![transfer.from.tag_from(), transfer.nonce as u8]]
} else {
vec![vec![transfer.nonce as u8]]
};
log::info!(
"test_api::validate_transaction: h:{:?} n:{:?} cn:{:?} tn:{:?} r:{:?} p:{:?}",
at,
block_number,
chain_nonce,
transfer.nonce,
requires,
provides,
);
if self.enable_stale_check && transfer.nonce < chain_nonce {
log::info!("test_api::validate_transaction: invalid_transaction(stale)....");
return Ok(Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)));
}
(requires, provides)
} else {
(Vec::new(), vec![uxt.encode()])
};
if self.chain.read().invalid_hashes.contains(&self.hash_and_length(&uxt).0) {
log::info!("test_api::validate_transaction: invalid_transaction....");
return Ok(Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(0))));
}
let priority = self.chain.read().priorities.get(&self.hash_and_length(&uxt).0).cloned();
let longevity = self
.chain
.read()
.valid_till_blocks
.get(&self.hash_and_length(&uxt).0)
.cloned()
.map(|valid_till| valid_till.saturating_sub(block_number.unwrap()))
.unwrap_or(64);
if longevity == 0 {
return Ok(Err(TransactionValidityError::Invalid(InvalidTransaction::BadProof)));
}
let mut validity = ValidTransaction {
priority: priority.unwrap_or(1),
requires,
provides,
longevity,
propagate: true,
};
(self.valid_modifier.read())(&mut validity);
Ok(Ok(validity))
}
fn block_id_to_number(
&self,
at: &BlockId<Self::Block>,
) -> Result<Option<NumberFor<Self::Block>>, Error> {
Ok(match at {
generic::BlockId::Hash(x) =>
self.chain.read().block_by_hash.get(x).map(|b| *b.header.number()),
generic::BlockId::Number(num) => Some(*num),
})
}
fn block_id_to_hash(
&self,
at: &BlockId<Self::Block>,
) -> Result<Option<<Self::Block as BlockT>::Hash>, Error> {
Ok(match at {
generic::BlockId::Hash(x) => Some(*x),
generic::BlockId::Number(num) =>
self.chain.read().block_by_number.get(num).and_then(|blocks| {
blocks.iter().find(|b| b.1.is_best()).map(|b| b.0.header().hash())
}),
})
}
fn hash_and_length(&self, ex: &<Self::Block as BlockT>::Extrinsic) -> (Hash, usize) {
Self::hash_and_length_inner(ex)
}
async fn block_body(
&self,
hash: <Self::Block as BlockT>::Hash,
) -> Result<Option<Vec<Extrinsic>>, Error> {
Ok(self.chain.read().block_by_hash.get(&hash).map(|b| b.extrinsics().to_vec()))
}
fn block_header(
&self,
hash: <Self::Block as BlockT>::Hash,
) -> Result<Option<<Self::Block as BlockT>::Header>, Self::Error> {
Ok(self.chain.read().block_by_hash.get(&hash).map(|b| b.header().clone()))
}
fn tree_route(
&self,
from: <Self::Block as BlockT>::Hash,
to: <Self::Block as BlockT>::Hash,
) -> Result<TreeRoute<Self::Block>, Self::Error> {
pezsp_blockchain::tree_route::<Block, TestApi>(self, from, to).map_err(Into::into)
}
}
impl pezsp_blockchain::HeaderMetadata<Block> for TestApi {
type Error = Error;
fn header_metadata(&self, hash: Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
let chain = self.chain.read();
let block = chain.block_by_hash.get(&hash).expect("Hash exists");
Ok(block.header().into())
}
fn insert_header_metadata(&self, _: Hash, _: CachedHeaderMetadata<Block>) {
unimplemented!("Not implemented for tests")
}
fn remove_header_metadata(&self, _: Hash) {
unimplemented!("Not implemented for tests")
}
}
/// Generate transfer extrinsic with a given nonce.
///
/// Part of the test api.
pub fn uxt(who: Sr25519Keyring, nonce: Nonce) -> Extrinsic {
let dummy = codec::Decode::decode(&mut TrailingZeroInput::zeroes()).unwrap();
let transfer = Transfer { from: who.into(), to: dummy, nonce, amount: 1 };
ExtrinsicBuilder::new_transfer(transfer).build()
}
+57
View File
@@ -0,0 +1,57 @@
// 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.
//! Test utils
/// Panic when the vectors are different, without taking the order into account.
///
/// # Examples
///
/// ```rust
/// #[macro_use]
/// # use bizinikiwi_test_utils::{assert_eq_uvec};
/// # fn main() {
/// assert_eq_uvec!(vec![1,2], vec![2,1]);
/// # }
/// ```
///
/// ```rust,should_panic
/// #[macro_use]
/// # use bizinikiwi_test_utils::{assert_eq_uvec};
/// # fn main() {
/// assert_eq_uvec!(vec![1,2,3], vec![2,1]);
/// # }
/// ```
#[macro_export]
macro_rules! assert_eq_uvec {
( $x:expr, $y:expr $(,)? ) => {
$crate::__assert_eq_uvec!($x, $y);
$crate::__assert_eq_uvec!($y, $x);
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __assert_eq_uvec {
( $x:expr, $y:expr ) => {
$x.iter().for_each(|e| {
if !$y.contains(e) {
panic!("vectors not equal: {:?} != {:?}", $x, $y);
}
});
};
}