mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 21:41:12 +00:00
Reorganising the repository - external renames and moves (#4074)
* Adding first rough ouline of the repository structure * Remove old CI stuff * add title * formatting fixes * move node-exits job's script to scripts dir * Move docs into subdir * move to bin * move maintainence scripts, configs and helpers into its own dir * add .local to ignore * move core->client * start up 'test' area * move test client * move test runtime * make test move compile * Add dependencies rule enforcement. * Fix indexing. * Update docs to reflect latest changes * Moving /srml->/paint * update docs * move client/sr-* -> primitives/ * clean old readme * remove old broken code in rhd * update lock * Step 1. * starting to untangle client * Fix after merge. * start splitting out client interfaces * move children and blockchain interfaces * Move trie and state-machine to primitives. * Fix WASM builds. * fixing broken imports * more interface moves * move backend and light to interfaces * move CallExecutor * move cli off client * moving around more interfaces * re-add consensus crates into the mix * fix subkey path * relieve client from executor * starting to pull out client from grandpa * move is_decendent_of out of client * grandpa still depends on client directly * lemme tests pass * rename srml->paint * Make it compile. * rename interfaces->client-api * Move keyring to primitives. * fixup libp2p dep * fix broken use * allow dependency enforcement to fail * move fork-tree * Moving wasm-builder * make env * move build-script-utils * fixup broken crate depdencies and names * fix imports for authority discovery * fix typo * update cargo.lock * fixing imports * Fix paths and add missing crates * re-add missing crates
This commit is contained in:
committed by
Bastian Köcher
parent
becc3b0a4f
commit
60e5011c72
@@ -0,0 +1,83 @@
|
||||
[package]
|
||||
name = "substrate-test-runtime"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
app-crypto = { package = "substrate-application-crypto", path = "../../../primitives/application-crypto", default-features = false }
|
||||
aura-primitives = { package = "substrate-consensus-aura-primitives", path = "../../../primitives/consensus/aura", default-features = false }
|
||||
babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../../../primitives/consensus/babe", default-features = false }
|
||||
block-builder-api = { package = "substrate-block-builder-runtime-api", path = "../../../primitives/block-builder/runtime-api", default-features = false }
|
||||
cfg-if = "0.1.10"
|
||||
codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] }
|
||||
executive = { package = "paint-executive", path = "../../../paint/executive", default-features = false }
|
||||
inherents = { package = "substrate-inherents", path = "../../../primitives/inherents", default-features = false }
|
||||
keyring = { package = "substrate-keyring", path = "../../../primitives/keyring", optional = true }
|
||||
log = { version = "0.4.8", optional = true }
|
||||
memory-db = { version = "0.15.2", default-features = false }
|
||||
offchain-primitives = { package = "substrate-offchain-primitives", path = "../../../primitives/offchain", default-features = false}
|
||||
primitives = { package = "substrate-primitives", path = "../../../primitives/core", default-features = false }
|
||||
rstd = { package = "sr-std", path = "../../../primitives/sr-std", default-features = false }
|
||||
runtime-interface = { package = "substrate-runtime-interface", path = "../../../primitives/runtime-interface", default-features = false}
|
||||
runtime_io = { package = "sr-io", path = "../../../primitives/sr-io", default-features = false }
|
||||
runtime_support = { package = "paint-support", path = "../../../paint/support", default-features = false }
|
||||
runtime_version = { package = "sr-version", path = "../../../primitives/sr-version", default-features = false }
|
||||
serde = { version = "1.0.101", optional = true, features = ["derive"] }
|
||||
session = { package = "substrate-session", path = "../../../primitives/session", default-features = false }
|
||||
sr-api = { path = "../../../primitives/sr-api", default-features = false }
|
||||
sr-primitives = { path = "../../../primitives/sr-primitives", default-features = false }
|
||||
paint-babe = { path = "../../../paint/babe", default-features = false }
|
||||
paint-system = { path = "../../../paint/system", default-features = false }
|
||||
paint-system-rpc-runtime-api = { path = "../../../paint/system/rpc/runtime-api", default-features = false }
|
||||
paint-timestamp = { path = "../../../paint/timestamp", default-features = false }
|
||||
substrate-client = { path = "../../../client", optional = true }
|
||||
substrate-trie = { path = "../../../primitives/trie", default-features = false }
|
||||
transaction-pool-api = { package = "substrate-transaction-pool-runtime-api", path = "../../../primitives/transaction-pool/runtime-api", default-features = false }
|
||||
trie-db = { version = "0.15.2", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
substrate-executor = { path = "../../../client/executor" }
|
||||
substrate-test-runtime-client = { path = "./client" }
|
||||
state_machine = { package = "substrate-state-machine", path = "../../../primitives/state-machine" }
|
||||
|
||||
[build-dependencies]
|
||||
wasm-builder-runner = { package = "substrate-wasm-builder-runner", path = "../../../client/utils/wasm-builder-runner", version = "1.0.4" }
|
||||
|
||||
[features]
|
||||
default = [
|
||||
"std",
|
||||
]
|
||||
std = [
|
||||
"app-crypto/std",
|
||||
"aura-primitives/std",
|
||||
"babe-primitives/std",
|
||||
"block-builder-api/std",
|
||||
"codec/std",
|
||||
"executive/std",
|
||||
"inherents/std",
|
||||
"keyring",
|
||||
"log",
|
||||
"memory-db/std",
|
||||
"offchain-primitives/std",
|
||||
"primitives/std",
|
||||
"primitives/std",
|
||||
"rstd/std",
|
||||
"runtime-interface/std",
|
||||
"runtime_io/std",
|
||||
"runtime_support/std",
|
||||
"runtime_version/std",
|
||||
"serde",
|
||||
"session/std",
|
||||
"sr-api/std",
|
||||
"sr-primitives/std",
|
||||
"paint-babe/std",
|
||||
"paint-system-rpc-runtime-api/std",
|
||||
"paint-system/std",
|
||||
"paint-timestamp/std",
|
||||
"substrate-client",
|
||||
"substrate-trie/std",
|
||||
"transaction-pool-api/std",
|
||||
"trie-db/std",
|
||||
]
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use wasm_builder_runner::{build_current_project_with_rustflags, WasmBuilderSource};
|
||||
|
||||
fn main() {
|
||||
build_current_project_with_rustflags(
|
||||
"wasm_binary.rs",
|
||||
WasmBuilderSource::CratesOrPath {
|
||||
path: "../../../utils/wasm-builder",
|
||||
version: "1.0.8",
|
||||
},
|
||||
// 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.
|
||||
//
|
||||
// The --export=__heap_base instructs LLD to export __heap_base as a global variable, which
|
||||
// is used by the external memory allocator.
|
||||
"-Clink-arg=-zstack-size=1048576 \
|
||||
-Clink-arg=--export=__heap_base",
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "substrate-test-runtime-client"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
block-builder = { package = "substrate-block-builder", path = "../../../../client/block-builder" }
|
||||
generic-test-client = { package = "substrate-test-client", path = "../../client" }
|
||||
primitives = { package = "substrate-primitives", path = "../../../../primitives/core" }
|
||||
runtime = { package = "substrate-test-runtime", path = "../../runtime" }
|
||||
sr-primitives = { path = "../../../../primitives/sr-primitives" }
|
||||
codec = { package = "parity-scale-codec", version = "1.0.0" }
|
||||
client-api = { package = "substrate-client-api", path = "../../../../client/api" }
|
||||
client = { package = "substrate-client", path = "../../../../client/" }
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Block Builder extensions for tests.
|
||||
|
||||
use runtime;
|
||||
use sr_primitives::traits::ProvideRuntimeApi;
|
||||
use generic_test_client::client;
|
||||
|
||||
use block_builder::BlockBuilderApi;
|
||||
|
||||
/// Extension trait for test block builder.
|
||||
pub trait BlockBuilderExt {
|
||||
/// Add transfer extrinsic to the block.
|
||||
fn push_transfer(&mut self, transfer: runtime::Transfer) -> Result<(), client::error::Error>;
|
||||
/// Add storage change extrinsic to the block.
|
||||
fn push_storage_change(
|
||||
&mut self,
|
||||
key: Vec<u8>,
|
||||
value: Option<Vec<u8>>,
|
||||
) -> Result<(), client::error::Error>;
|
||||
}
|
||||
|
||||
impl<'a, A> BlockBuilderExt for block_builder::BlockBuilder<'a, runtime::Block, A> where
|
||||
A: ProvideRuntimeApi + 'a,
|
||||
A::Api: BlockBuilderApi<runtime::Block, Error = client::error::Error>,
|
||||
{
|
||||
fn push_transfer(&mut self, transfer: runtime::Transfer) -> Result<(), client::error::Error> {
|
||||
self.push(transfer.into_signed_tx())
|
||||
}
|
||||
|
||||
fn push_storage_change(
|
||||
&mut self,
|
||||
key: Vec<u8>,
|
||||
value: Option<Vec<u8>>,
|
||||
) -> Result<(), client::error::Error> {
|
||||
self.push(runtime::Extrinsic::StorageChange(key, value))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Client testing utilities.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
pub mod trait_tests;
|
||||
|
||||
mod block_builder_ext;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
pub use block_builder_ext::BlockBuilderExt;
|
||||
pub use generic_test_client::*;
|
||||
pub use runtime;
|
||||
|
||||
use primitives::sr25519;
|
||||
use runtime::genesismap::{GenesisConfig, additional_storage_with_genesis};
|
||||
use sr_primitives::traits::{Block as BlockT, Header as HeaderT, Hash as HashT};
|
||||
|
||||
/// A prelude to import in tests.
|
||||
pub mod prelude {
|
||||
// Trait extensions
|
||||
pub use super::{BlockBuilderExt, DefaultTestClientBuilderExt, TestClientBuilderExt, ClientExt};
|
||||
// Client structs
|
||||
pub use super::{
|
||||
TestClient, TestClientBuilder, Backend, LightBackend,
|
||||
Executor, LightExecutor, LocalExecutor, NativeExecutor, WasmExecutionMethod,
|
||||
};
|
||||
// Keyring
|
||||
pub use super::{AccountKeyring, Sr25519Keyring};
|
||||
}
|
||||
|
||||
mod local_executor {
|
||||
#![allow(missing_docs)]
|
||||
use runtime;
|
||||
use crate::executor::native_executor_instance;
|
||||
// FIXME #1576 change the macro and pass in the `BlakeHasher` that dispatch needs from here instead
|
||||
native_executor_instance!(
|
||||
pub LocalExecutor,
|
||||
runtime::api::dispatch,
|
||||
runtime::native_version
|
||||
);
|
||||
}
|
||||
|
||||
/// Native executor used for tests.
|
||||
pub use local_executor::LocalExecutor;
|
||||
|
||||
/// Test client database backend.
|
||||
pub type Backend = generic_test_client::Backend<runtime::Block>;
|
||||
|
||||
/// Test client executor.
|
||||
pub type Executor = client::LocalCallExecutor<
|
||||
Backend,
|
||||
NativeExecutor<LocalExecutor>,
|
||||
>;
|
||||
|
||||
/// Test client light database backend.
|
||||
pub type LightBackend = generic_test_client::LightBackend<runtime::Block>;
|
||||
|
||||
/// Test client light executor.
|
||||
pub type LightExecutor = client::light::call_executor::GenesisCallExecutor<
|
||||
LightBackend,
|
||||
client::LocalCallExecutor<
|
||||
client::light::backend::Backend<
|
||||
client_db::light::LightStorage<runtime::Block>,
|
||||
Blake2Hasher,
|
||||
>,
|
||||
NativeExecutor<LocalExecutor>
|
||||
>
|
||||
>;
|
||||
|
||||
/// Parameters of test-client builder with test-runtime.
|
||||
#[derive(Default)]
|
||||
pub struct GenesisParameters {
|
||||
support_changes_trie: bool,
|
||||
heap_pages_override: Option<u64>,
|
||||
extra_storage: HashMap<Vec<u8>, Vec<u8>>,
|
||||
child_extra_storage: HashMap<Vec<u8>, HashMap<Vec<u8>, Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl GenesisParameters {
|
||||
fn genesis_config(&self) -> GenesisConfig {
|
||||
GenesisConfig::new(
|
||||
self.support_changes_trie,
|
||||
vec![
|
||||
sr25519::Public::from(Sr25519Keyring::Alice).into(),
|
||||
sr25519::Public::from(Sr25519Keyring::Bob).into(),
|
||||
sr25519::Public::from(Sr25519Keyring::Charlie).into(),
|
||||
],
|
||||
vec![
|
||||
AccountKeyring::Alice.into(),
|
||||
AccountKeyring::Bob.into(),
|
||||
AccountKeyring::Charlie.into(),
|
||||
],
|
||||
1000,
|
||||
self.heap_pages_override,
|
||||
self.extra_storage.clone(),
|
||||
self.child_extra_storage.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl generic_test_client::GenesisInit for GenesisParameters {
|
||||
fn genesis_storage(&self) -> (StorageOverlay, ChildrenStorageOverlay) {
|
||||
use codec::Encode;
|
||||
let mut storage = self.genesis_config().genesis_map();
|
||||
|
||||
let child_roots = storage.1.iter().map(|(sk, child_map)| {
|
||||
let state_root = <<<runtime::Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
|
||||
child_map.clone().into_iter().collect()
|
||||
);
|
||||
(sk.clone(), state_root.encode())
|
||||
});
|
||||
let state_root = <<<runtime::Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
|
||||
storage.0.clone().into_iter().chain(child_roots).collect()
|
||||
);
|
||||
let block: runtime::Block = client::genesis::construct_genesis_block(state_root);
|
||||
storage.0.extend(additional_storage_with_genesis(&block));
|
||||
|
||||
storage
|
||||
}
|
||||
}
|
||||
|
||||
/// A `TestClient` with `test-runtime` builder.
|
||||
pub type TestClientBuilder<E, B> = generic_test_client::TestClientBuilder<E, B, GenesisParameters>;
|
||||
|
||||
/// Test client type with `LocalExecutor` and generic Backend.
|
||||
pub type Client<B> = client::Client<
|
||||
B,
|
||||
client::LocalCallExecutor<B, executor::NativeExecutor<LocalExecutor>>,
|
||||
runtime::Block,
|
||||
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<
|
||||
Executor,
|
||||
Backend,
|
||||
> {
|
||||
fn new() -> Self {
|
||||
Self::with_default_backend()
|
||||
}
|
||||
}
|
||||
|
||||
/// A `test-runtime` extensions to `TestClientBuilder`.
|
||||
pub trait TestClientBuilderExt<B>: Sized {
|
||||
/// Enable or disable support for changes trie in genesis.
|
||||
fn set_support_changes_trie(self, support_changes_trie: bool) -> Self;
|
||||
|
||||
/// Override the default value for Wasm heap pages.
|
||||
fn set_heap_pages(self, heap_pages: u64) -> Self;
|
||||
|
||||
/// Add an extra value into the genesis storage.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the key is empty.
|
||||
fn add_extra_child_storage<SK: Into<Vec<u8>>, K: Into<Vec<u8>>, V: Into<Vec<u8>>>(
|
||||
self,
|
||||
storage_key: SK,
|
||||
key: K,
|
||||
value: V,
|
||||
) -> 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>>>(self, key: K, value: V) -> 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>, client::LongestChain<B, runtime::Block>);
|
||||
}
|
||||
|
||||
impl<B> TestClientBuilderExt<B> for TestClientBuilder<
|
||||
client::LocalCallExecutor<B, executor::NativeExecutor<LocalExecutor>>,
|
||||
B
|
||||
> where
|
||||
B: client_api::backend::Backend<runtime::Block, Blake2Hasher>,
|
||||
{
|
||||
fn set_heap_pages(mut self, heap_pages: u64) -> Self {
|
||||
self.genesis_init_mut().heap_pages_override = Some(heap_pages);
|
||||
self
|
||||
}
|
||||
|
||||
fn set_support_changes_trie(mut self, support_changes_trie: bool) -> Self {
|
||||
self.genesis_init_mut().support_changes_trie = support_changes_trie;
|
||||
self
|
||||
}
|
||||
|
||||
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.insert(key, value.into());
|
||||
self
|
||||
}
|
||||
|
||||
fn add_extra_child_storage<SK: Into<Vec<u8>>, K: Into<Vec<u8>>, V: Into<Vec<u8>>>(
|
||||
mut self,
|
||||
storage_key: SK,
|
||||
key: K,
|
||||
value: V,
|
||||
) -> Self {
|
||||
let storage_key = storage_key.into();
|
||||
let key = key.into();
|
||||
assert!(!storage_key.is_empty());
|
||||
assert!(!key.is_empty());
|
||||
self.genesis_init_mut().child_extra_storage
|
||||
.entry(storage_key)
|
||||
.or_insert_with(Default::default)
|
||||
.insert(key, value.into());
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn build_with_longest_chain(self) -> (Client<B>, client::LongestChain<B, runtime::Block>) {
|
||||
self.build_with_native_executor(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new client instance used for tests.
|
||||
pub fn new() -> Client<Backend> {
|
||||
TestClientBuilder::new().build()
|
||||
}
|
||||
|
||||
/// Creates new light client instance used for tests.
|
||||
pub fn new_light() -> (
|
||||
client::Client<LightBackend, LightExecutor, runtime::Block, runtime::RuntimeApi>,
|
||||
Arc<LightBackend>,
|
||||
) {
|
||||
|
||||
let storage = client_db::light::LightStorage::new_test();
|
||||
let blockchain = Arc::new(client::light::blockchain::Blockchain::new(storage));
|
||||
let backend = Arc::new(LightBackend::new(blockchain.clone()));
|
||||
let executor = NativeExecutor::new(WasmExecutionMethod::Interpreted, None);
|
||||
let local_call_executor = client::LocalCallExecutor::new(backend.clone(), executor, None);
|
||||
let call_executor = LightExecutor::new(
|
||||
backend.clone(),
|
||||
local_call_executor,
|
||||
);
|
||||
|
||||
(
|
||||
TestClientBuilder::with_backend(backend.clone())
|
||||
.build_with_executor(call_executor)
|
||||
.0,
|
||||
backend,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! tests that should hold for all implementations of certain traits.
|
||||
//! to test implementations without duplication.
|
||||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use client_api::backend::LocalBackend;
|
||||
use crate::block_builder_ext::BlockBuilderExt;
|
||||
use client_api::blockchain::{Backend as BlockChainBackendT, HeaderBackend};
|
||||
use crate::{AccountKeyring, ClientExt, TestClientBuilder, TestClientBuilderExt};
|
||||
use generic_test_client::consensus::BlockOrigin;
|
||||
use primitives::Blake2Hasher;
|
||||
use runtime::{self, Transfer};
|
||||
use sr_primitives::generic::BlockId;
|
||||
use sr_primitives::traits::Block as BlockT;
|
||||
|
||||
/// helper to test the `leaves` implementation for various backends
|
||||
pub fn test_leaves_for_backend<B: 'static>(backend: Arc<B>) where
|
||||
B: LocalBackend<runtime::Block, Blake2Hasher>,
|
||||
{
|
||||
// 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.info().chain.genesis_hash;
|
||||
|
||||
assert_eq!(
|
||||
blockchain.leaves().unwrap(),
|
||||
vec![genesis_hash]);
|
||||
|
||||
// G -> A1
|
||||
let a1 = client.new_block(Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, a1.clone()).unwrap();
|
||||
assert_eq!(
|
||||
blockchain.leaves().unwrap(),
|
||||
vec![a1.hash()]);
|
||||
|
||||
// A1 -> A2
|
||||
let a2 = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, a2.clone()).unwrap();
|
||||
|
||||
#[allow(deprecated)]
|
||||
assert_eq!(
|
||||
blockchain.leaves().unwrap(),
|
||||
vec![a2.hash()]);
|
||||
|
||||
// A2 -> A3
|
||||
let a3 = client.new_block_at(&BlockId::Hash(a2.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, a3.clone()).unwrap();
|
||||
assert_eq!(
|
||||
blockchain.leaves().unwrap(),
|
||||
vec![a3.hash()]);
|
||||
|
||||
// A3 -> A4
|
||||
let a4 = client.new_block_at(&BlockId::Hash(a3.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, a4.clone()).unwrap();
|
||||
assert_eq!(
|
||||
blockchain.leaves().unwrap(),
|
||||
vec![a4.hash()]);
|
||||
|
||||
// A4 -> A5
|
||||
let a5 = client.new_block_at(&BlockId::Hash(a4.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, a5.clone()).unwrap();
|
||||
assert_eq!(
|
||||
blockchain.leaves().unwrap(),
|
||||
vec![a5.hash()]);
|
||||
|
||||
// A1 -> B2
|
||||
let mut builder = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap();
|
||||
// this push is required as otherwise B2 has the same hash as A2 and won't get imported
|
||||
builder.push_transfer(Transfer {
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: AccountKeyring::Ferdie.into(),
|
||||
amount: 41,
|
||||
nonce: 0,
|
||||
}).unwrap();
|
||||
let b2 = builder.bake().unwrap();
|
||||
client.import(BlockOrigin::Own, b2.clone()).unwrap();
|
||||
assert_eq!(
|
||||
blockchain.leaves().unwrap(),
|
||||
vec![a5.hash(), b2.hash()]);
|
||||
|
||||
// B2 -> B3
|
||||
let b3 = client.new_block_at(&BlockId::Hash(b2.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, b3.clone()).unwrap();
|
||||
assert_eq!(
|
||||
blockchain.leaves().unwrap(),
|
||||
vec![a5.hash(), b3.hash()]);
|
||||
|
||||
// B3 -> B4
|
||||
let b4 = client.new_block_at(&BlockId::Hash(b3.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, b4.clone()).unwrap();
|
||||
assert_eq!(
|
||||
blockchain.leaves().unwrap(),
|
||||
vec![a5.hash(), b4.hash()]);
|
||||
|
||||
// // B2 -> C3
|
||||
let mut builder = client.new_block_at(&BlockId::Hash(b2.hash()), Default::default()).unwrap();
|
||||
// this push is required as otherwise C3 has the same hash as B3 and won't get imported
|
||||
builder.push_transfer(Transfer {
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: AccountKeyring::Ferdie.into(),
|
||||
amount: 1,
|
||||
nonce: 1,
|
||||
}).unwrap();
|
||||
let c3 = builder.bake().unwrap();
|
||||
client.import(BlockOrigin::Own, c3.clone()).unwrap();
|
||||
assert_eq!(
|
||||
blockchain.leaves().unwrap(),
|
||||
vec![a5.hash(), b4.hash(), c3.hash()]);
|
||||
|
||||
// A1 -> D2
|
||||
let mut builder = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap();
|
||||
// this push is required as otherwise D2 has the same hash as B2 and won't get imported
|
||||
builder.push_transfer(Transfer {
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: AccountKeyring::Ferdie.into(),
|
||||
amount: 1,
|
||||
nonce: 0,
|
||||
}).unwrap();
|
||||
let d2 = builder.bake().unwrap();
|
||||
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: LocalBackend<runtime::Block, Blake2Hasher>,
|
||||
{
|
||||
// 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();
|
||||
|
||||
// G -> A1
|
||||
let a1 = client.new_block(Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, a1.clone()).unwrap();
|
||||
|
||||
// A1 -> A2
|
||||
let a2 = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, a2.clone()).unwrap();
|
||||
|
||||
// A2 -> A3
|
||||
let a3 = client.new_block_at(&BlockId::Hash(a2.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, a3.clone()).unwrap();
|
||||
|
||||
// A3 -> A4
|
||||
let a4 = client.new_block_at(&BlockId::Hash(a3.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, a4.clone()).unwrap();
|
||||
|
||||
// A4 -> A5
|
||||
let a5 = client.new_block_at(&BlockId::Hash(a4.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, a5.clone()).unwrap();
|
||||
|
||||
// A1 -> B2
|
||||
let mut builder = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap();
|
||||
// this push is required as otherwise B2 has the same hash as A2 and won't get imported
|
||||
builder.push_transfer(Transfer {
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: AccountKeyring::Ferdie.into(),
|
||||
amount: 41,
|
||||
nonce: 0,
|
||||
}).unwrap();
|
||||
let b2 = builder.bake().unwrap();
|
||||
client.import(BlockOrigin::Own, b2.clone()).unwrap();
|
||||
|
||||
// B2 -> B3
|
||||
let b3 = client.new_block_at(&BlockId::Hash(b2.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, b3.clone()).unwrap();
|
||||
|
||||
// B3 -> B4
|
||||
let b4 = client.new_block_at(&BlockId::Hash(b3.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, b4.clone()).unwrap();
|
||||
|
||||
// // B2 -> C3
|
||||
let mut builder = client.new_block_at(&BlockId::Hash(b2.hash()), Default::default()).unwrap();
|
||||
// this push is required as otherwise C3 has the same hash as B3 and won't get imported
|
||||
builder.push_transfer(Transfer {
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: AccountKeyring::Ferdie.into(),
|
||||
amount: 1,
|
||||
nonce: 1,
|
||||
}).unwrap();
|
||||
let c3 = builder.bake().unwrap();
|
||||
client.import(BlockOrigin::Own, c3.clone()).unwrap();
|
||||
|
||||
// A1 -> D2
|
||||
let mut builder = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap();
|
||||
// this push is required as otherwise D2 has the same hash as B2 and won't get imported
|
||||
builder.push_transfer(Transfer {
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: AccountKeyring::Ferdie.into(),
|
||||
amount: 1,
|
||||
nonce: 0,
|
||||
}).unwrap();
|
||||
let d2 = builder.bake().unwrap();
|
||||
client.import(BlockOrigin::Own, d2.clone()).unwrap();
|
||||
|
||||
let genesis_hash = client.info().chain.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: LocalBackend<runtime::Block, Blake2Hasher>,
|
||||
{
|
||||
// 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();
|
||||
|
||||
// G -> A1
|
||||
let a1 = client.new_block(Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, a1.clone()).unwrap();
|
||||
|
||||
// A1 -> A2
|
||||
let a2 = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, a2.clone()).unwrap();
|
||||
|
||||
// A2 -> A3
|
||||
let a3 = client.new_block_at(&BlockId::Hash(a2.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, a3.clone()).unwrap();
|
||||
|
||||
// A3 -> A4
|
||||
let a4 = client.new_block_at(&BlockId::Hash(a3.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, a4.clone()).unwrap();
|
||||
|
||||
// A4 -> A5
|
||||
let a5 = client.new_block_at(&BlockId::Hash(a4.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, a5.clone()).unwrap();
|
||||
|
||||
// A1 -> B2
|
||||
let mut builder = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap();
|
||||
// this push is required as otherwise B2 has the same hash as A2 and won't get imported
|
||||
builder.push_transfer(Transfer {
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: AccountKeyring::Ferdie.into(),
|
||||
amount: 41,
|
||||
nonce: 0,
|
||||
}).unwrap();
|
||||
let b2 = builder.bake().unwrap();
|
||||
client.import(BlockOrigin::Own, b2.clone()).unwrap();
|
||||
|
||||
// B2 -> B3
|
||||
let b3 = client.new_block_at(&BlockId::Hash(b2.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, b3.clone()).unwrap();
|
||||
|
||||
// B3 -> B4
|
||||
let b4 = client.new_block_at(&BlockId::Hash(b3.hash()), Default::default()).unwrap().bake().unwrap();
|
||||
client.import(BlockOrigin::Own, b4.clone()).unwrap();
|
||||
|
||||
// // B2 -> C3
|
||||
let mut builder = client.new_block_at(&BlockId::Hash(b2.hash()), Default::default()).unwrap();
|
||||
// this push is required as otherwise C3 has the same hash as B3 and won't get imported
|
||||
builder.push_transfer(Transfer {
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: AccountKeyring::Ferdie.into(),
|
||||
amount: 1,
|
||||
nonce: 1,
|
||||
}).unwrap();
|
||||
let c3 = builder.bake().unwrap();
|
||||
client.import(BlockOrigin::Own, c3.clone()).unwrap();
|
||||
|
||||
// A1 -> D2
|
||||
let mut builder = client.new_block_at(&BlockId::Hash(a1.hash()), Default::default()).unwrap();
|
||||
// this push is required as otherwise D2 has the same hash as B2 and won't get imported
|
||||
builder.push_transfer(Transfer {
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: AccountKeyring::Ferdie.into(),
|
||||
amount: 1,
|
||||
nonce: 0,
|
||||
}).unwrap();
|
||||
let d2 = builder.bake().unwrap();
|
||||
client.import(BlockOrigin::Own, d2.clone()).unwrap();
|
||||
|
||||
let genesis_hash = client.info().chain.genesis_hash;
|
||||
|
||||
assert_eq!(blockchain.header(BlockId::Number(0)).unwrap().unwrap().hash(), genesis_hash);
|
||||
assert_eq!(blockchain.hash(0).unwrap().unwrap(), genesis_hash);
|
||||
|
||||
assert_eq!(blockchain.header(BlockId::Number(1)).unwrap().unwrap().hash(), a1.hash());
|
||||
assert_eq!(blockchain.hash(1).unwrap().unwrap(), a1.hash());
|
||||
|
||||
assert_eq!(blockchain.header(BlockId::Number(2)).unwrap().unwrap().hash(), a2.hash());
|
||||
assert_eq!(blockchain.hash(2).unwrap().unwrap(), a2.hash());
|
||||
|
||||
assert_eq!(blockchain.header(BlockId::Number(3)).unwrap().unwrap().hash(), a3.hash());
|
||||
assert_eq!(blockchain.hash(3).unwrap().unwrap(), a3.hash());
|
||||
|
||||
assert_eq!(blockchain.header(BlockId::Number(4)).unwrap().unwrap().hash(), a4.hash());
|
||||
assert_eq!(blockchain.hash(4).unwrap().unwrap(), a4.hash());
|
||||
|
||||
assert_eq!(blockchain.header(BlockId::Number(5)).unwrap().unwrap().hash(), a5.hash());
|
||||
assert_eq!(blockchain.hash(5).unwrap().unwrap(), a5.hash());
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Tool for creating the genesis block.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use runtime_io::hashing::{blake2_256, twox_128};
|
||||
use super::{AuthorityId, AccountId, WASM_BINARY, system};
|
||||
use codec::{Encode, KeyedVec, Joiner};
|
||||
use primitives::{ChangesTrieConfiguration, map, storage::well_known_keys};
|
||||
use sr_primitives::traits::{Block as BlockT, Hash as HashT, Header as HeaderT};
|
||||
|
||||
/// Configuration of a general Substrate test genesis block.
|
||||
pub struct GenesisConfig {
|
||||
changes_trie_config: Option<ChangesTrieConfiguration>,
|
||||
authorities: Vec<AuthorityId>,
|
||||
balances: Vec<(AccountId, u64)>,
|
||||
heap_pages_override: Option<u64>,
|
||||
/// Additional storage key pairs that will be added to the genesis map.
|
||||
extra_storage: HashMap<Vec<u8>, Vec<u8>>,
|
||||
child_extra_storage: HashMap<Vec<u8>, HashMap<Vec<u8>, Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl GenesisConfig {
|
||||
pub fn new(
|
||||
support_changes_trie: bool,
|
||||
authorities: Vec<AuthorityId>,
|
||||
endowed_accounts: Vec<AccountId>,
|
||||
balance: u64,
|
||||
heap_pages_override: Option<u64>,
|
||||
extra_storage: HashMap<Vec<u8>, Vec<u8>>,
|
||||
child_extra_storage: HashMap<Vec<u8>, HashMap<Vec<u8>, Vec<u8>>>,
|
||||
) -> Self {
|
||||
GenesisConfig {
|
||||
changes_trie_config: match support_changes_trie {
|
||||
true => Some(super::changes_trie_config()),
|
||||
false => None,
|
||||
},
|
||||
authorities: authorities.clone(),
|
||||
balances: endowed_accounts.into_iter().map(|a| (a, balance)).collect(),
|
||||
heap_pages_override,
|
||||
extra_storage,
|
||||
child_extra_storage,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn genesis_map(&self) -> (
|
||||
HashMap<Vec<u8>, Vec<u8>>,
|
||||
HashMap<Vec<u8>, HashMap<Vec<u8>, Vec<u8>>>,
|
||||
) {
|
||||
let wasm_runtime = WASM_BINARY.to_vec();
|
||||
let mut map: HashMap<Vec<u8>, Vec<u8>> = self.balances.iter()
|
||||
.map(|&(ref account, balance)| (account.to_keyed_vec(b"balance:"), vec![].and(&balance)))
|
||||
.map(|(k, v)| (blake2_256(&k[..])[..].to_vec(), v.to_vec()))
|
||||
.chain(vec![
|
||||
(well_known_keys::CODE.into(), wasm_runtime),
|
||||
(
|
||||
well_known_keys::HEAP_PAGES.into(),
|
||||
vec![].and(&(self.heap_pages_override.unwrap_or(16 as u64))),
|
||||
),
|
||||
].into_iter())
|
||||
.collect();
|
||||
if let Some(ref changes_trie_config) = self.changes_trie_config {
|
||||
map.insert(well_known_keys::CHANGES_TRIE_CONFIG.to_vec(), changes_trie_config.encode());
|
||||
}
|
||||
map.insert(twox_128(&b"sys:auth"[..])[..].to_vec(), self.authorities.encode());
|
||||
// Add the extra storage entries.
|
||||
map.extend(self.extra_storage.clone().into_iter());
|
||||
|
||||
// Assimilate the system genesis config.
|
||||
let mut storage = (map, self.child_extra_storage.clone());
|
||||
let mut config = system::GenesisConfig::default();
|
||||
config.authorities = self.authorities.clone();
|
||||
config.assimilate_storage(&mut storage).expect("Adding `system::GensisConfig` to the genesis");
|
||||
|
||||
storage
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_genesis_block(
|
||||
storage: &mut (
|
||||
HashMap<Vec<u8>, Vec<u8>>,
|
||||
HashMap<Vec<u8>, HashMap<Vec<u8>, Vec<u8>>>,
|
||||
)
|
||||
) -> primitives::hash::H256 {
|
||||
let child_roots = storage.1.iter().map(|(sk, child_map)| {
|
||||
let state_root = <<<crate::Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
|
||||
child_map.clone().into_iter().collect(),
|
||||
);
|
||||
(sk.clone(), state_root.encode())
|
||||
});
|
||||
let state_root = <<<crate::Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
|
||||
storage.0.clone().into_iter().chain(child_roots).collect()
|
||||
);
|
||||
let block: crate::Block = substrate_client::genesis::construct_genesis_block(state_root);
|
||||
let genesis_hash = block.header.hash();
|
||||
storage.0.extend(additional_storage_with_genesis(&block));
|
||||
genesis_hash
|
||||
}
|
||||
|
||||
pub fn additional_storage_with_genesis(genesis_block: &crate::Block) -> HashMap<Vec<u8>, Vec<u8>> {
|
||||
map![
|
||||
twox_128(&b"latest"[..]).to_vec() => genesis_block.hash().as_fixed_bytes().to_vec()
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,496 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! System manager: Handles all of the top-level stuff; executing block/transaction, setting code
|
||||
//! and depositing logs.
|
||||
|
||||
use rstd::prelude::*;
|
||||
use runtime_io::{
|
||||
storage::root as storage_root, storage::changes_root as storage_changes_root,
|
||||
hashing::blake2_256,
|
||||
};
|
||||
use runtime_support::storage;
|
||||
use runtime_support::{decl_storage, decl_module};
|
||||
use sr_primitives::{
|
||||
traits::{Hash as HashT, BlakeTwo256, Header as _}, generic, ApplyError, ApplyResult,
|
||||
transaction_validity::{TransactionValidity, ValidTransaction, InvalidTransaction},
|
||||
};
|
||||
use codec::{KeyedVec, Encode};
|
||||
use paint_system::Trait;
|
||||
use crate::{
|
||||
AccountId, BlockNumber, Extrinsic, Transfer, H256 as Hash, Block, Header, Digest, AuthorityId
|
||||
};
|
||||
use primitives::storage::well_known_keys;
|
||||
|
||||
const NONCE_OF: &[u8] = b"nonce:";
|
||||
const BALANCE_OF: &[u8] = b"balance:";
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {}
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as TestRuntime {
|
||||
ExtrinsicData: map u32 => Vec<u8>;
|
||||
// The current block number being processed. Set by `execute_block`.
|
||||
Number get(fn number): Option<BlockNumber>;
|
||||
ParentHash get(fn parent_hash): Hash;
|
||||
NewAuthorities get(fn new_authorities): Option<Vec<AuthorityId>>;
|
||||
StorageDigest get(fn storage_digest): Option<Digest>;
|
||||
Authorities get(fn authorities) config(): Vec<AuthorityId>;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn balance_of_key(who: AccountId) -> Vec<u8> {
|
||||
who.to_keyed_vec(BALANCE_OF)
|
||||
}
|
||||
|
||||
pub fn balance_of(who: AccountId) -> u64 {
|
||||
storage::hashed::get_or(&blake2_256, &balance_of_key(who), 0)
|
||||
}
|
||||
|
||||
pub fn nonce_of(who: AccountId) -> u64 {
|
||||
storage::hashed::get_or(&blake2_256, &who.to_keyed_vec(NONCE_OF), 0)
|
||||
}
|
||||
|
||||
pub fn initialize_block(header: &Header) {
|
||||
// populate environment.
|
||||
<Number>::put(&header.number);
|
||||
<ParentHash>::put(&header.parent_hash);
|
||||
<StorageDigest>::put(header.digest());
|
||||
storage::unhashed::put(well_known_keys::EXTRINSIC_INDEX, &0u32);
|
||||
|
||||
// try to read something that depends on current header digest
|
||||
// so that it'll be included in execution proof
|
||||
if let Some(generic::DigestItem::Other(v)) = header.digest().logs().iter().next() {
|
||||
let _: Option<u32> = storage::unhashed::get(&v);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn authorities() -> Vec<AuthorityId> {
|
||||
Authorities::get()
|
||||
}
|
||||
|
||||
pub fn get_block_number() -> Option<BlockNumber> {
|
||||
Number::get()
|
||||
}
|
||||
|
||||
pub fn take_block_number() -> Option<BlockNumber> {
|
||||
Number::take()
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Mode {
|
||||
Verify,
|
||||
Overwrite,
|
||||
}
|
||||
|
||||
/// Actually execute all transitioning for `block`.
|
||||
pub fn polish_block(block: &mut Block) {
|
||||
execute_block_with_state_root_handler(block, Mode::Overwrite);
|
||||
}
|
||||
|
||||
pub fn execute_block(mut block: Block) {
|
||||
execute_block_with_state_root_handler(&mut block, Mode::Verify);
|
||||
}
|
||||
|
||||
fn execute_block_with_state_root_handler(
|
||||
block: &mut Block,
|
||||
mode: Mode,
|
||||
) {
|
||||
let header = &mut block.header;
|
||||
|
||||
// check transaction trie root represents the transactions.
|
||||
let txs = block.extrinsics.iter().map(Encode::encode).collect::<Vec<_>>();
|
||||
let txs_root = BlakeTwo256::ordered_trie_root(txs);
|
||||
info_expect_equal_hash(&txs_root, &header.extrinsics_root);
|
||||
if let Mode::Overwrite = mode {
|
||||
header.extrinsics_root = txs_root;
|
||||
} else {
|
||||
assert!(txs_root == header.extrinsics_root, "Transaction trie root must be valid.");
|
||||
}
|
||||
|
||||
// try to read something that depends on current header digest
|
||||
// so that it'll be included in execution proof
|
||||
if let Some(generic::DigestItem::Other(v)) = header.digest().logs().iter().next() {
|
||||
let _: Option<u32> = storage::unhashed::get(&v);
|
||||
}
|
||||
|
||||
// execute transactions
|
||||
block.extrinsics.iter().enumerate().for_each(|(i, e)| {
|
||||
storage::unhashed::put(well_known_keys::EXTRINSIC_INDEX, &(i as u32));
|
||||
let _ = execute_transaction_backend(e).unwrap_or_else(|_| panic!("Invalid transaction"));
|
||||
storage::unhashed::kill(well_known_keys::EXTRINSIC_INDEX);
|
||||
});
|
||||
|
||||
let o_new_authorities = <NewAuthorities>::take();
|
||||
|
||||
if let Mode::Overwrite = mode {
|
||||
header.state_root = storage_root().into();
|
||||
} else {
|
||||
// check storage root.
|
||||
let storage_root = storage_root().into();
|
||||
info_expect_equal_hash(&storage_root, &header.state_root);
|
||||
assert!(storage_root == header.state_root, "Storage root must match that calculated.");
|
||||
}
|
||||
|
||||
// check digest
|
||||
let digest = &mut header.digest;
|
||||
if let Some(storage_changes_root) = storage_changes_root(header.parent_hash.into()) {
|
||||
digest.push(generic::DigestItem::ChangesTrieRoot(storage_changes_root.into()));
|
||||
}
|
||||
if let Some(new_authorities) = o_new_authorities {
|
||||
digest.push(generic::DigestItem::Consensus(*b"aura", new_authorities.encode()));
|
||||
digest.push(generic::DigestItem::Consensus(*b"babe", new_authorities.encode()));
|
||||
}
|
||||
}
|
||||
|
||||
/// The block executor.
|
||||
pub struct BlockExecutor;
|
||||
|
||||
impl executive::ExecuteBlock<Block> for BlockExecutor {
|
||||
fn execute_block(block: Block) {
|
||||
execute_block(block);
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a transaction outside of the block execution function.
|
||||
/// This doesn't attempt to validate anything regarding the block.
|
||||
pub fn validate_transaction(utx: Extrinsic) -> TransactionValidity {
|
||||
if check_signature(&utx).is_err() {
|
||||
return InvalidTransaction::BadProof.into();
|
||||
}
|
||||
|
||||
let tx = utx.transfer();
|
||||
let nonce_key = tx.from.to_keyed_vec(NONCE_OF);
|
||||
let expected_nonce: u64 = storage::hashed::get_or(&blake2_256, &nonce_key, 0);
|
||||
if tx.nonce < expected_nonce {
|
||||
return InvalidTransaction::Stale.into();
|
||||
}
|
||||
if tx.nonce > expected_nonce + 64 {
|
||||
return InvalidTransaction::Future.into();
|
||||
}
|
||||
|
||||
let encode = |from: &AccountId, nonce: u64| (from, nonce).encode();
|
||||
let requires = if tx.nonce != expected_nonce && tx.nonce > 0 {
|
||||
vec![encode(&tx.from, tx.nonce - 1)]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
let provides = vec![encode(&tx.from, tx.nonce)];
|
||||
|
||||
Ok(ValidTransaction {
|
||||
priority: tx.amount,
|
||||
requires,
|
||||
provides,
|
||||
longevity: 64,
|
||||
propagate: true,
|
||||
})
|
||||
}
|
||||
|
||||
/// Execute a transaction outside of the block execution function.
|
||||
/// This doesn't attempt to validate anything regarding the block.
|
||||
pub fn execute_transaction(utx: Extrinsic) -> ApplyResult {
|
||||
let extrinsic_index: u32 = storage::unhashed::get(well_known_keys::EXTRINSIC_INDEX).unwrap();
|
||||
let result = execute_transaction_backend(&utx);
|
||||
ExtrinsicData::insert(extrinsic_index, utx.encode());
|
||||
storage::unhashed::put(well_known_keys::EXTRINSIC_INDEX, &(extrinsic_index + 1));
|
||||
result
|
||||
}
|
||||
|
||||
/// Finalize the block.
|
||||
pub fn finalize_block() -> Header {
|
||||
let extrinsic_index: u32 = storage::unhashed::take(well_known_keys::EXTRINSIC_INDEX).unwrap();
|
||||
let txs: Vec<_> = (0..extrinsic_index).map(ExtrinsicData::take).collect();
|
||||
let extrinsics_root = BlakeTwo256::ordered_trie_root(txs).into();
|
||||
let number = <Number>::take().expect("Number is set by `initialize_block`");
|
||||
let parent_hash = <ParentHash>::take();
|
||||
let mut digest = <StorageDigest>::take().expect("StorageDigest is set by `initialize_block`");
|
||||
|
||||
let o_new_authorities = <NewAuthorities>::take();
|
||||
// This MUST come after all changes to storage are done. Otherwise we will fail the
|
||||
// “Storage root does not match that calculated” assertion.
|
||||
let storage_root = BlakeTwo256::storage_root();
|
||||
let storage_changes_root = BlakeTwo256::storage_changes_root(parent_hash);
|
||||
|
||||
if let Some(storage_changes_root) = storage_changes_root {
|
||||
digest.push(generic::DigestItem::ChangesTrieRoot(storage_changes_root));
|
||||
}
|
||||
|
||||
if let Some(new_authorities) = o_new_authorities {
|
||||
digest.push(generic::DigestItem::Consensus(*b"aura", new_authorities.encode()));
|
||||
digest.push(generic::DigestItem::Consensus(*b"babe", new_authorities.encode()));
|
||||
}
|
||||
|
||||
Header {
|
||||
number,
|
||||
extrinsics_root,
|
||||
state_root: storage_root,
|
||||
parent_hash,
|
||||
digest: digest,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn check_signature(utx: &Extrinsic) -> Result<(), ApplyError> {
|
||||
use sr_primitives::traits::BlindCheckable;
|
||||
utx.clone().check().map_err(|_| InvalidTransaction::BadProof.into()).map(|_| ())
|
||||
}
|
||||
|
||||
fn execute_transaction_backend(utx: &Extrinsic) -> ApplyResult {
|
||||
check_signature(utx)?;
|
||||
match utx {
|
||||
Extrinsic::Transfer(ref transfer, _) => execute_transfer_backend(transfer),
|
||||
Extrinsic::AuthoritiesChange(ref new_auth) => execute_new_authorities_backend(new_auth),
|
||||
Extrinsic::IncludeData(_) => Ok(Ok(())),
|
||||
Extrinsic::StorageChange(key, value) => execute_storage_change(key, value.as_ref().map(|v| &**v)),
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_transfer_backend(tx: &Transfer) -> ApplyResult {
|
||||
// check nonce
|
||||
let nonce_key = tx.from.to_keyed_vec(NONCE_OF);
|
||||
let expected_nonce: u64 = storage::hashed::get_or(&blake2_256, &nonce_key, 0);
|
||||
if !(tx.nonce == expected_nonce) {
|
||||
return Err(InvalidTransaction::Stale.into());
|
||||
}
|
||||
|
||||
// increment nonce in storage
|
||||
storage::hashed::put(&blake2_256, &nonce_key, &(expected_nonce + 1));
|
||||
|
||||
// check sender balance
|
||||
let from_balance_key = tx.from.to_keyed_vec(BALANCE_OF);
|
||||
let from_balance: u64 = storage::hashed::get_or(&blake2_256, &from_balance_key, 0);
|
||||
|
||||
// enact transfer
|
||||
if !(tx.amount <= from_balance) {
|
||||
return Err(InvalidTransaction::Payment.into());
|
||||
}
|
||||
let to_balance_key = tx.to.to_keyed_vec(BALANCE_OF);
|
||||
let to_balance: u64 = storage::hashed::get_or(&blake2_256, &to_balance_key, 0);
|
||||
storage::hashed::put(&blake2_256, &from_balance_key, &(from_balance - tx.amount));
|
||||
storage::hashed::put(&blake2_256, &to_balance_key, &(to_balance + tx.amount));
|
||||
Ok(Ok(()))
|
||||
}
|
||||
|
||||
fn execute_new_authorities_backend(new_authorities: &[AuthorityId]) -> ApplyResult {
|
||||
NewAuthorities::put(new_authorities.to_vec());
|
||||
Ok(Ok(()))
|
||||
}
|
||||
|
||||
fn execute_storage_change(key: &[u8], value: Option<&[u8]>) -> ApplyResult {
|
||||
match value {
|
||||
Some(value) => storage::unhashed::put_raw(key, value),
|
||||
None => storage::unhashed::kill(key),
|
||||
}
|
||||
Ok(Ok(()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn info_expect_equal_hash(given: &Hash, expected: &Hash) {
|
||||
use primitives::hexdisplay::HexDisplay;
|
||||
if given != expected {
|
||||
println!(
|
||||
"Hash: given={}, expected={}",
|
||||
HexDisplay::from(given.as_fixed_bytes()),
|
||||
HexDisplay::from(expected.as_fixed_bytes()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn info_expect_equal_hash(given: &Hash, expected: &Hash) {
|
||||
if given != expected {
|
||||
sr_primitives::print("Hash not equal");
|
||||
sr_primitives::print(given.as_bytes());
|
||||
sr_primitives::print(expected.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use runtime_io::TestExternalities;
|
||||
use substrate_test_runtime_client::{AccountKeyring, Sr25519Keyring};
|
||||
use crate::{Header, Transfer, WASM_BINARY};
|
||||
use primitives::{NeverNativeValue, map, traits::CodeExecutor};
|
||||
use substrate_executor::{NativeExecutor, WasmExecutionMethod, native_executor_instance};
|
||||
use runtime_io::hashing::twox_128;
|
||||
|
||||
// Declare an instance of the native executor dispatch for the test runtime.
|
||||
native_executor_instance!(
|
||||
NativeDispatch,
|
||||
crate::api::dispatch,
|
||||
crate::native_version
|
||||
);
|
||||
|
||||
fn executor() -> NativeExecutor<NativeDispatch> {
|
||||
NativeExecutor::new(WasmExecutionMethod::Interpreted, None)
|
||||
}
|
||||
|
||||
fn new_test_ext() -> TestExternalities {
|
||||
let authorities = vec![
|
||||
Sr25519Keyring::Alice.to_raw_public(),
|
||||
Sr25519Keyring::Bob.to_raw_public(),
|
||||
Sr25519Keyring::Charlie.to_raw_public()
|
||||
];
|
||||
TestExternalities::new_with_code(
|
||||
WASM_BINARY,
|
||||
(
|
||||
map![
|
||||
twox_128(b"latest").to_vec() => vec![69u8; 32],
|
||||
twox_128(b"sys:auth").to_vec() => authorities.encode(),
|
||||
blake2_256(&AccountKeyring::Alice.to_raw_public().to_keyed_vec(b"balance:")).to_vec() => {
|
||||
vec![111u8, 0, 0, 0, 0, 0, 0, 0]
|
||||
}
|
||||
],
|
||||
map![],
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn block_import_works<F>(block_executor: F) where F: Fn(Block, &mut TestExternalities) {
|
||||
let h = Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: Default::default(),
|
||||
extrinsics_root: Default::default(),
|
||||
digest: Default::default(),
|
||||
};
|
||||
let mut b = Block {
|
||||
header: h,
|
||||
extrinsics: vec![],
|
||||
};
|
||||
|
||||
new_test_ext().execute_with(|| polish_block(&mut b));
|
||||
|
||||
block_executor(b, &mut new_test_ext());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_import_works_native() {
|
||||
block_import_works(|b, ext| ext.execute_with(|| execute_block(b)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_import_works_wasm() {
|
||||
block_import_works(|b, ext| {
|
||||
let mut ext = ext.ext();
|
||||
executor().call::<_, NeverNativeValue, fn() -> _>(
|
||||
&mut ext,
|
||||
"Core_execute_block",
|
||||
&b.encode(),
|
||||
false,
|
||||
None,
|
||||
).0.unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
fn block_import_with_transaction_works<F>(block_executor: F)
|
||||
where F: Fn(Block, &mut TestExternalities)
|
||||
{
|
||||
let mut b1 = Block {
|
||||
header: Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: Default::default(),
|
||||
extrinsics_root: Default::default(),
|
||||
digest: Default::default(),
|
||||
},
|
||||
extrinsics: vec![
|
||||
Transfer {
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: AccountKeyring::Bob.into(),
|
||||
amount: 69,
|
||||
nonce: 0,
|
||||
}.into_signed_tx()
|
||||
],
|
||||
};
|
||||
|
||||
let mut dummy_ext = new_test_ext();
|
||||
dummy_ext.execute_with(|| polish_block(&mut b1));
|
||||
|
||||
let mut b2 = Block {
|
||||
header: Header {
|
||||
parent_hash: b1.header.hash(),
|
||||
number: 2,
|
||||
state_root: Default::default(),
|
||||
extrinsics_root: Default::default(),
|
||||
digest: Default::default(),
|
||||
},
|
||||
extrinsics: vec![
|
||||
Transfer {
|
||||
from: AccountKeyring::Bob.into(),
|
||||
to: AccountKeyring::Alice.into(),
|
||||
amount: 27,
|
||||
nonce: 0,
|
||||
}.into_signed_tx(),
|
||||
Transfer {
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: AccountKeyring::Charlie.into(),
|
||||
amount: 69,
|
||||
nonce: 1,
|
||||
}.into_signed_tx(),
|
||||
],
|
||||
};
|
||||
|
||||
dummy_ext.execute_with(|| polish_block(&mut b2));
|
||||
drop(dummy_ext);
|
||||
|
||||
let mut t = new_test_ext();
|
||||
|
||||
t.execute_with(|| {
|
||||
assert_eq!(balance_of(AccountKeyring::Alice.into()), 111);
|
||||
assert_eq!(balance_of(AccountKeyring::Bob.into()), 0);
|
||||
});
|
||||
|
||||
block_executor(b1, &mut t);
|
||||
|
||||
t.execute_with(|| {
|
||||
assert_eq!(balance_of(AccountKeyring::Alice.into()), 42);
|
||||
assert_eq!(balance_of(AccountKeyring::Bob.into()), 69);
|
||||
});
|
||||
|
||||
block_executor(b2, &mut t);
|
||||
|
||||
t.execute_with(|| {
|
||||
assert_eq!(balance_of(AccountKeyring::Alice.into()), 0);
|
||||
assert_eq!(balance_of(AccountKeyring::Bob.into()), 42);
|
||||
assert_eq!(balance_of(AccountKeyring::Charlie.into()), 69);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_import_with_transaction_works_native() {
|
||||
block_import_with_transaction_works(|b, ext| ext.execute_with(|| execute_block(b)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_import_with_transaction_works_wasm() {
|
||||
block_import_with_transaction_works(|b, ext| {
|
||||
let mut ext = ext.ext();
|
||||
executor().call::<_, NeverNativeValue, fn() -> _>(
|
||||
&mut ext,
|
||||
"Core_execute_block",
|
||||
&b.encode(),
|
||||
false,
|
||||
None,
|
||||
).0.unwrap();
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user