feat: initialize Kurdistan SDK - independent fork of Polkadot SDK
This commit is contained in:
@@ -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",
|
||||
]
|
||||
@@ -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()
|
||||
}
|
||||
Reference in New Issue
Block a user