Substrate test client crate & chain subscription test (#139)

* Test client used in RPC tests.

* Use test-client for network tests.

* Expose BlockOrigin and clean up the API.
This commit is contained in:
Tomasz Drwięga
2018-05-01 16:39:55 +02:00
committed by Robert Habermeier
parent 101549238e
commit f116f67382
16 changed files with 290 additions and 223 deletions
@@ -0,0 +1,14 @@
[package]
name = "substrate-test-client"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
substrate-bft = { path = "../bft" }
substrate-client = { path = "../client" }
substrate-codec = { path = "../codec" }
substrate-executor = { path = "../executor" }
substrate-keyring = { path = "../../substrate/keyring" }
substrate-primitives = { path = "../primitives" }
substrate-runtime-support = { path = "../runtime-support" }
substrate-test-runtime = { path = "../test-runtime" }
@@ -0,0 +1,108 @@
// Copyright 2018 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 extension for tests.
use codec::Slicable;
use client::{self, Client};
use keyring::Keyring;
use runtime_support::Hashable;
use runtime::genesismap::{GenesisConfig, additional_storage_with_genesis};
use primitives::block;
use bft;
use {Backend, Executor, NativeExecutor};
/// Extension trait for a test client.
pub trait TestClient {
/// Crates new client instance for tests.
fn new_for_tests() -> Self;
/// Justify and import block to the chain.
fn justify_and_import(&self, origin: client::BlockOrigin, block: block::Block) -> client::error::Result<()>;
/// Returns hash of the genesis block.
fn genesis_hash(&self) -> block::HeaderHash;
}
impl TestClient for Client<Backend, Executor> {
fn new_for_tests() -> Self {
client::new_in_mem(NativeExecutor::new(), prepare_genesis).unwrap()
}
fn justify_and_import(&self, origin: client::BlockOrigin, block: block::Block) -> client::error::Result<()> {
let justification = fake_justify(&block.header);
let justified = self.check_justification(block.header, justification)?;
self.import_block(origin, justified, Some(block.transactions))?;
Ok(())
}
fn genesis_hash(&self) -> block::HeaderHash {
self.block_hash(0).unwrap().unwrap()
}
}
/// Prepare fake justification for the header.
///
/// since we are in the client module we can create falsely justified
/// headers.
/// TODO: remove this in favor of custom verification pipelines for the
/// client
fn fake_justify(header: &block::Header) -> bft::UncheckedJustification {
let hash = header.blake2_256().into();
let authorities = vec![
Keyring::Alice.into(),
Keyring::Bob.into(),
Keyring::Charlie.into(),
];
bft::UncheckedJustification {
digest: hash,
signatures: authorities.iter().map(|key| {
let msg = bft::sign_message(
bft::generic::Vote::Commit(1, hash).into(),
key,
header.parent_hash
);
match msg {
bft::generic::LocalizedMessage::Vote(vote) => vote.signature,
_ => panic!("signing vote leads to signed vote"),
}
}).collect(),
round_number: 1,
}
}
fn genesis_config() -> GenesisConfig {
GenesisConfig::new_simple(vec![
Keyring::Alice.to_raw_public(),
Keyring::Bob.to_raw_public(),
Keyring::Charlie.to_raw_public()
], 1000)
}
fn prepare_genesis() -> (block::Header, Vec<(Vec<u8>, Vec<u8>)>) {
let mut storage = genesis_config().genesis_map();
let block = client::genesis::construct_genesis_block(&storage);
storage.extend(additional_storage_with_genesis(&block));
(
block::Header::decode(&mut block.header.encode().as_ref())
.expect("to_vec() always gives a valid serialisation; qed"),
storage.into_iter().collect()
)
}
@@ -0,0 +1,54 @@
// Copyright 2018 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)]
extern crate substrate_bft as bft;
extern crate substrate_codec as codec;
extern crate substrate_keyring as keyring;
extern crate substrate_primitives as primitives;
extern crate substrate_runtime_support as runtime_support;
#[macro_use] extern crate substrate_executor as executor;
pub extern crate substrate_test_runtime as runtime;
pub extern crate substrate_client as client;
mod client_ext;
pub use client_ext::TestClient;
mod native_executor {
#![allow(missing_docs)]
use super::runtime;
native_executor_instance!(pub NativeExecutor, runtime::api::dispatch, include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm"));
}
/// Native executor used for tests.
pub use self::native_executor::NativeExecutor;
/// Test client database backend.
pub type Backend = client::in_mem::Backend;
/// Test client executor.
pub type Executor = executor::NativeExecutor<NativeExecutor>;
/// Creates new client instance used for tests.
pub fn new() -> client::Client<Backend, Executor> {
TestClient::new_for_tests()
}