feat: initialize Kurdistan SDK - independent fork of Polkadot SDK

This commit is contained in:
2025-12-13 15:44:15 +03:00
commit e4778b4576
6838 changed files with 1847450 additions and 0 deletions
+167
View File
@@ -0,0 +1,167 @@
[package]
name = "substrate-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 }
frame-executive = { workspace = true }
frame-metadata-hash-extension = { workspace = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
frame-system-rpc-runtime-api = { workspace = true }
pallet-babe = { workspace = true }
pallet-balances = { workspace = true }
pallet-timestamp = { workspace = true }
pallet-utility = { workspace = true }
sc-service = { optional = true, workspace = true }
scale-info = { features = ["derive"], workspace = true }
sp-api = { workspace = true }
sp-application-crypto = { features = ["serde"], workspace = true }
sp-block-builder = { workspace = true }
sp-consensus-aura = { features = ["serde"], workspace = true }
sp-consensus-babe = { features = ["serde"], workspace = true }
sp-consensus-grandpa = { features = ["serde"], workspace = true }
sp-core = { features = ["serde"], workspace = true }
sp-crypto-hashing = { workspace = true }
sp-debug-derive = { workspace = true, default-features = false, features = [
"force-debug",
] }
sp-externalities = { workspace = true }
sp-genesis-builder = { workspace = true }
sp-inherents = { workspace = true }
sp-io = { workspace = true }
sp-keyring = { workspace = true }
sp-offchain = { workspace = true }
sp-runtime = { features = ["serde"], workspace = true }
sp-session = { workspace = true }
sp-state-machine = { workspace = true }
sp-transaction-pool = { workspace = true }
sp-trie = { workspace = true }
sp-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 }
sc-block-builder = { workspace = true, default-features = true }
sc-chain-spec = { workspace = true, default-features = true }
sc-executor = { workspace = true, default-features = true }
sc-executor-common = { workspace = true, default-features = true }
serde = { features = ["alloc", "derive"], workspace = true }
sp-consensus = { workspace = true, default-features = true }
sp-tracing = { workspace = true, default-features = true }
substrate-test-runtime-client = { workspace = true }
[build-dependencies]
substrate-wasm-builder = { optional = true, features = [
"metadata-hash",
], workspace = true, default-features = true }
[features]
default = ["std"]
std = [
"array-bytes",
"codec/std",
"frame-executive/std",
"frame-metadata-hash-extension/std",
"frame-support/std",
"frame-system-rpc-runtime-api/std",
"frame-system/std",
"log/std",
"pallet-babe/std",
"pallet-balances/std",
"pallet-timestamp/std",
"pallet-utility/std",
"sc-executor/std",
"sc-service",
"scale-info/std",
"serde/std",
"serde_json/std",
"sp-api/std",
"sp-application-crypto/std",
"sp-block-builder/std",
"sp-consensus-aura/std",
"sp-consensus-babe/std",
"sp-consensus-grandpa/std",
"sp-core/std",
"sp-crypto-hashing/std",
"sp-debug-derive/std",
"sp-externalities/std",
"sp-genesis-builder/std",
"sp-inherents/std",
"sp-io/std",
"sp-keyring/std",
"sp-offchain/std",
"sp-runtime/std",
"sp-session/std",
"sp-state-machine/std",
"sp-tracing/std",
"sp-transaction-pool/std",
"sp-trie/std",
"sp-version/std",
"substrate-wasm-builder",
"tracing/std",
"trie-db/std",
]
# Special feature to disable logging
disable-logging = ["sp-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 = ["sp-application-crypto/bls-experimental"]
runtime-benchmarks = [
"frame-executive/runtime-benchmarks",
"frame-metadata-hash-extension/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system-rpc-runtime-api/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-babe/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-timestamp/runtime-benchmarks",
"pallet-utility/runtime-benchmarks",
"sc-block-builder/runtime-benchmarks",
"sc-chain-spec/runtime-benchmarks",
"sc-executor/runtime-benchmarks",
"sc-service?/runtime-benchmarks",
"sp-api/runtime-benchmarks",
"sp-block-builder/runtime-benchmarks",
"sp-consensus-aura/runtime-benchmarks",
"sp-consensus-babe/runtime-benchmarks",
"sp-consensus-grandpa/runtime-benchmarks",
"sp-consensus/runtime-benchmarks",
"sp-genesis-builder/runtime-benchmarks",
"sp-inherents/runtime-benchmarks",
"sp-io/runtime-benchmarks",
"sp-keyring/runtime-benchmarks",
"sp-offchain/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"sp-session/runtime-benchmarks",
"sp-state-machine/runtime-benchmarks",
"sp-transaction-pool/runtime-benchmarks",
"sp-trie/runtime-benchmarks",
"sp-version/runtime-benchmarks",
"substrate-test-runtime-client/runtime-benchmarks",
"substrate-wasm-builder?/runtime-benchmarks",
]
+43
View File
@@ -0,0 +1,43 @@
// This file is part of Substrate.
// 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")]
{
substrate_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")]
{
substrate_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 = "substrate-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 }
sc-block-builder = { workspace = true, default-features = true }
sc-client-api = { workspace = true, default-features = true }
sc-consensus = { workspace = true, default-features = true }
sp-api = { workspace = true, default-features = true }
sp-blockchain = { workspace = true, default-features = true }
sp-consensus = { workspace = true, default-features = true }
sp-core = { workspace = true, default-features = true }
sp-runtime = { workspace = true, default-features = true }
substrate-test-client = { workspace = true }
substrate-test-runtime = { workspace = true }
[features]
bls-experimental = ["substrate-test-runtime/bls-experimental"]
runtime-benchmarks = [
"sc-block-builder/runtime-benchmarks",
"sc-client-api/runtime-benchmarks",
"sc-consensus/runtime-benchmarks",
"sp-api/runtime-benchmarks",
"sp-blockchain/runtime-benchmarks",
"sp-consensus/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"substrate-test-client/runtime-benchmarks",
"substrate-test-runtime/runtime-benchmarks",
]
@@ -0,0 +1,74 @@
// This file is part of Substrate.
// 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 sc_block_builder::BlockBuilderApi;
use sp_api::{ApiExt, ProvideRuntimeApi};
use substrate_test_runtime::*;
/// Extension trait for test block builder.
pub trait BlockBuilderExt {
/// Add transfer extrinsic to the block.
fn push_transfer(
&mut self,
transfer: substrate_test_runtime::Transfer,
) -> Result<(), sp_blockchain::Error>;
/// Add unsigned storage change extrinsic to the block.
fn push_storage_change(
&mut self,
key: Vec<u8>,
value: Option<Vec<u8>>,
) -> Result<(), sp_blockchain::Error>;
/// Adds an extrinsic which pushes DigestItem to header's log
fn push_deposit_log_digest_item(
&mut self,
log: sp_runtime::generic::DigestItem,
) -> Result<(), sp_blockchain::Error>;
}
impl<'a, A> BlockBuilderExt for sc_block_builder::BlockBuilder<'a, substrate_test_runtime::Block, A>
where
A: ProvideRuntimeApi<substrate_test_runtime::Block>
+ sp_api::CallApiAt<substrate_test_runtime::Block>
+ 'a,
A::Api: BlockBuilderApi<substrate_test_runtime::Block> + ApiExt<substrate_test_runtime::Block>,
{
fn push_transfer(
&mut self,
transfer: substrate_test_runtime::Transfer,
) -> Result<(), sp_blockchain::Error> {
self.push(transfer.into_unchecked_extrinsic())
}
fn push_storage_change(
&mut self,
key: Vec<u8>,
value: Option<Vec<u8>>,
) -> Result<(), sp_blockchain::Error> {
self.push(ExtrinsicBuilder::new_storage_change(key, value).build())
}
fn push_deposit_log_digest_item(
&mut self,
log: sp_runtime::generic::DigestItem,
) -> Result<(), sp_blockchain::Error> {
self.push(ExtrinsicBuilder::new_deposit_log_digest_item(log).build())
}
}
@@ -0,0 +1,217 @@
// This file is part of Substrate.
// 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 sc_consensus::LongestChain;
use std::sync::Arc;
pub use substrate_test_client::*;
pub use substrate_test_runtime as runtime;
pub use self::block_builder_ext::BlockBuilderExt;
use sp_core::storage::ChildInfo;
use substrate_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 sc_block_builder::BlockBuilderBuilder;
pub use sp_blockchain::HeaderBackend;
}
/// Test client database backend.
pub type Backend = substrate_test_client::Backend<substrate_test_runtime::Block>;
/// Test client executor.
pub type ExecutorDispatch =
client::LocalCallExecutor<substrate_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> = substrate_test_client::TestClientBuilder<
substrate_test_runtime::Block,
E,
B,
GenesisParameters,
>;
/// Test client type with `WasmExecutor` and generic Backend.
pub type Client<B> = client::Client<
B,
client::LocalCallExecutor<substrate_test_runtime::Block, B, WasmExecutor>,
substrate_test_runtime::Block,
substrate_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>, sc_consensus::LongestChain<B, substrate_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<substrate_test_runtime::Block, B, WasmExecutor>, B>
where
B: sc_client_api::backend::Backend<substrate_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>, sc_consensus::LongestChain<B, substrate_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 Substrate.
// 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 sc_block_builder::BlockBuilderBuilder;
use sc_client_api::{
backend,
blockchain::{Backend as BlockChainBackendT, HeaderBackend},
};
use sp_consensus::BlockOrigin;
use sp_runtime::traits::Block as BlockT;
use substrate_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<substrate_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<substrate_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<substrate_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"
}
},
"substrateTest": {
"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"
}
},
"substrateTest": {
"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"
}
},
"substrateTest": {
"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"
}
},
"substrateTest": {
"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,229 @@
// This file is part of Substrate.
// 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 `substrate-test-runtime`.
use crate::{
substrate_test_pallet::pallet::Call as PalletCall, AccountId, Balance, BalancesCall,
CheckSubstrateCall, Extrinsic, Nonce, Pair, RuntimeCall, SignedPayload, TransferData,
};
use codec::Encode;
use frame_metadata_hash_extension::CheckMetadataHash;
use frame_system::{CheckNonce, CheckWeight};
use sp_core::crypto::Pair as TraitPair;
use sp_keyring::Sr25519Keyring;
use sp_runtime::{
generic::Preamble, traits::TransactionExtension, transaction_validity::TransactionPriority,
Perbill,
};
/// Transfer used in test substrate 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::SubstrateTest(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 `pallet_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: sp_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()),
CheckSubstrateCall {},
self.metadata_hash
.map(CheckMetadataHash::new_with_custom_hash)
.unwrap_or_else(|| CheckMetadataHash::new(false)),
frame_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 Substrate.
// 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, substrate_test_pallet, wasm_binary_unwrap, AccountId, Balance, RuntimeGenesisConfig,
};
use codec::Encode;
use sc_service::construct_genesis_block;
use sp_core::{
sr25519,
storage::{well_known_keys, StateVersion, Storage},
Pair,
};
use sp_keyring::Sr25519Keyring;
use sp_runtime::{
traits::{Block as BlockT, Hash as HashT, Header as HeaderT},
BuildStorage,
};
/// Builder for generating storage from substrate-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 `substrate_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: pallet_babe::GenesisConfig {
authorities: authorities_sr25519
.clone()
.into_iter()
.map(|x| (x.into(), 1))
.collect(),
..Default::default()
},
substrate_test: substrate_test_pallet::GenesisConfig {
authorities: authorities_sr25519.clone(),
..Default::default()
},
balances: pallet_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 substrate-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) -> sp_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(),
sp_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(),
sp_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,252 @@
// This file is part of Substrate.
// 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.
//! # substrate-test pallet
//!
//! Provides functionality used in unit-tests of numerous modules across substrate 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 frame_support::{pallet_prelude::*, storage};
use sp_core::sr25519::Public;
use sp_runtime::{
traits::Hash,
transaction_validity::{
InvalidTransaction, TransactionSource, TransactionValidity, ValidTransaction,
},
};
pub use self::pallet::*;
const LOG_TARGET: &str = "substrate_test_pallet";
#[frame_support::pallet(dev_mode)]
pub mod pallet {
use super::*;
use crate::TransferData;
use frame_system::pallet_prelude::*;
use sp_core::storage::well_known_keys;
use sp_runtime::{traits::BlakeTwo256, transaction_validity::TransactionPriority, Perbill};
#[pallet::pallet]
#[pallet::without_storage_info]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::storage]
#[pallet::getter(fn authorities)]
pub type Authorities<T> = StorageValue<_, Vec<Public>, ValueQuery>;
#[pallet::genesis_config]
#[derive(frame_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 {
frame_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 {
frame_system::ensure_signed(origin)?;
sp_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 {
frame_system::ensure_signed(origin)?;
sp_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 {
frame_system::ensure_signed(origin)?;
let content_hash = sp_io::hashing::blake2_256(&data);
let extrinsic_index: u32 =
storage::unhashed::get(well_known_keys::EXTRINSIC_INDEX).unwrap();
sp_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: sp_runtime::generic::DigestItem,
) -> DispatchResult {
<frame_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) = sp_io::storage::next_key(&next_key) {
// Read the value
sp_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,37 @@
[package]
name = "substrate-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 }
sc-transaction-pool = { workspace = true, default-features = true }
sc-transaction-pool-api = { workspace = true, default-features = true }
sp-blockchain = { workspace = true, default-features = true }
sp-runtime = { workspace = true, default-features = true }
substrate-test-runtime-client = { workspace = true }
thiserror = { workspace = true }
[features]
runtime-benchmarks = [
"sc-transaction-pool-api/runtime-benchmarks",
"sc-transaction-pool/runtime-benchmarks",
"sp-blockchain/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"substrate-test-runtime-client/runtime-benchmarks",
]
@@ -0,0 +1,559 @@
// This file is part of Substrate.
// 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 sc_transaction_pool::{ChainApi, ValidateTransactionPriority};
use sp_blockchain::{CachedHeaderMetadata, HashAndNumber, TreeRoute};
use sp_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 substrate_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 sc_transaction_pool_api::error::Error);
impl sc_transaction_pool_api::error::IntoPoolError for Error {
fn into_pool_error(self) -> Result<sc_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<sp_blockchain::TreeRoute<Block>, Error> {
sp_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> {
sp_blockchain::tree_route::<Block, TestApi>(self, from, to).map_err(Into::into)
}
}
impl sp_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()
}