feat: Rebrand Polkadot/Substrate references to PezkuwiChain
This commit systematically rebrands various references from Parity Technologies' Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk. Key changes include: - Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks. - Modified internal documentation and code comments to reflect PezkuwiChain naming and structure. - Replaced direct references to with or specific paths within the for XCM, Pezkuwi, and other modules. - Cleaned up deprecated issue and PR references in various and files, particularly in and modules. - Adjusted image and logo URLs in documentation to point to PezkuwiChain assets. - Removed or rephrased comments related to external Polkadot/Substrate PRs and issues. This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
@@ -0,0 +1,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"]
|
||||
@@ -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",
|
||||
]
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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(|_| ())
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
]
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user