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
+23 -10
View File
@@ -834,7 +834,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "jsonrpc-core"
version = "8.0.2"
source = "git+https://github.com/paritytech/jsonrpc.git#9cfa451b0534281d83f7564bf41fda465a6e18c8"
source = "git+https://github.com/paritytech/jsonrpc.git#0fd13be062625c6d4c89859c8686d4da3bbb552c"
dependencies = [
"futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -846,7 +846,7 @@ dependencies = [
[[package]]
name = "jsonrpc-http-server"
version = "8.0.1"
source = "git+https://github.com/paritytech/jsonrpc.git#9cfa451b0534281d83f7564bf41fda465a6e18c8"
source = "git+https://github.com/paritytech/jsonrpc.git#0fd13be062625c6d4c89859c8686d4da3bbb552c"
dependencies = [
"hyper 0.11.25 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 8.0.2 (git+https://github.com/paritytech/jsonrpc.git)",
@@ -859,7 +859,7 @@ dependencies = [
[[package]]
name = "jsonrpc-macros"
version = "8.0.1"
source = "git+https://github.com/paritytech/jsonrpc.git#9cfa451b0534281d83f7564bf41fda465a6e18c8"
source = "git+https://github.com/paritytech/jsonrpc.git#0fd13be062625c6d4c89859c8686d4da3bbb552c"
dependencies = [
"jsonrpc-core 8.0.2 (git+https://github.com/paritytech/jsonrpc.git)",
"jsonrpc-pubsub 8.0.1 (git+https://github.com/paritytech/jsonrpc.git)",
@@ -869,7 +869,7 @@ dependencies = [
[[package]]
name = "jsonrpc-pubsub"
version = "8.0.1"
source = "git+https://github.com/paritytech/jsonrpc.git#9cfa451b0534281d83f7564bf41fda465a6e18c8"
source = "git+https://github.com/paritytech/jsonrpc.git#0fd13be062625c6d4c89859c8686d4da3bbb552c"
dependencies = [
"jsonrpc-core 8.0.2 (git+https://github.com/paritytech/jsonrpc.git)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -879,7 +879,7 @@ dependencies = [
[[package]]
name = "jsonrpc-server-utils"
version = "8.0.1"
source = "git+https://github.com/paritytech/jsonrpc.git#9cfa451b0534281d83f7564bf41fda465a6e18c8"
source = "git+https://github.com/paritytech/jsonrpc.git#0fd13be062625c6d4c89859c8686d4da3bbb552c"
dependencies = [
"bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"globset 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -892,7 +892,7 @@ dependencies = [
[[package]]
name = "jsonrpc-ws-server"
version = "8.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git#9cfa451b0534281d83f7564bf41fda465a6e18c8"
source = "git+https://github.com/paritytech/jsonrpc.git#0fd13be062625c6d4c89859c8686d4da3bbb552c"
dependencies = [
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 8.0.2 (git+https://github.com/paritytech/jsonrpc.git)",
@@ -1810,7 +1810,7 @@ dependencies = [
"substrate-runtime-io 0.1.0",
"substrate-runtime-support 0.1.0",
"substrate-state-machine 0.1.0",
"substrate-test-runtime 0.1.0",
"substrate-test-client 0.1.0",
"triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1884,13 +1884,12 @@ dependencies = [
"substrate-bft 0.1.0",
"substrate-client 0.1.0",
"substrate-codec 0.1.0",
"substrate-executor 0.1.0",
"substrate-keyring 0.1.0",
"substrate-primitives 0.1.0",
"substrate-runtime-support 0.1.0",
"substrate-serializer 0.1.0",
"substrate-state-machine 0.1.0",
"substrate-test-runtime 0.1.0",
"substrate-test-client 0.1.0",
]
[[package]]
@@ -1926,8 +1925,8 @@ dependencies = [
"substrate-client 0.1.0",
"substrate-executor 0.1.0",
"substrate-primitives 0.1.0",
"substrate-runtime-support 0.1.0",
"substrate-state-machine 0.1.0",
"substrate-test-client 0.1.0",
"tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2157,6 +2156,20 @@ dependencies = [
"triehash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "substrate-test-client"
version = "0.1.0"
dependencies = [
"substrate-bft 0.1.0",
"substrate-client 0.1.0",
"substrate-codec 0.1.0",
"substrate-executor 0.1.0",
"substrate-keyring 0.1.0",
"substrate-primitives 0.1.0",
"substrate-runtime-support 0.1.0",
"substrate-test-runtime 0.1.0",
]
[[package]]
name = "substrate-test-runtime"
version = "0.1.0"
+1 -1
View File
@@ -21,4 +21,4 @@ substrate-state-machine = { path = "../state-machine" }
substrate-keyring = { path = "../../substrate/keyring" }
[dev-dependencies]
substrate-test-runtime = { path = "../test-runtime" }
substrate-test-client = { path = "../test-client" }
@@ -20,7 +20,6 @@ use primitives::block::{self, Id as BlockId};
use primitives;
use error::Result;
/// Blockchain database backend. Does not perform any validation.
pub trait Backend: Send + Sync {
/// Get block header. Returns `None` if block is not found.
+10 -91
View File
@@ -447,62 +447,15 @@ mod tests {
use super::*;
use codec::Slicable;
use keyring::Keyring;
use {primitives, genesis};
use primitives::block::Extrinsic as PrimitiveExtrinsic;
use test_runtime::genesismap::{GenesisConfig, additional_storage_with_genesis};
use test_runtime::{UncheckedTransaction, Transaction};
use test_runtime;
native_executor_instance!(Executor, test_runtime::api::dispatch, include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm"));
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() -> (primitives::block::Header, Vec<(Vec<u8>, Vec<u8>)>) {
let mut storage = genesis_config().genesis_map();
let block = genesis::construct_genesis_block(&storage);
storage.extend(additional_storage_with_genesis(&block));
(primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect())
}
// 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 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,
}
}
use test_client::{self, TestClient};
use test_client::client::BlockOrigin;
use test_client::runtime as test_runtime;
use test_client::runtime::{UncheckedTransaction, Transaction};
#[test]
fn client_initialises_from_genesis_ok() {
let client = new_in_mem(Executor::new(), prepare_genesis).unwrap();
let client = test_client::new();
let genesis_hash = client.block_hash(0).unwrap().unwrap();
assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), genesis_hash);
@@ -512,15 +465,7 @@ mod tests {
#[test]
fn authorities_call_works() {
let genesis_config = genesis_config();
let prepare_genesis = || {
let mut storage = genesis_config.genesis_map();
let block = genesis::construct_genesis_block(&storage);
storage.extend(additional_storage_with_genesis(&block));
(primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect())
};
let client = new_in_mem(Executor::new(), prepare_genesis).unwrap();
let client = test_client::new();
assert_eq!(client.info().unwrap().chain.best_number, 0);
assert_eq!(client.authorities_at(&BlockId::Number(0)).unwrap(), vec![
@@ -532,22 +477,11 @@ mod tests {
#[test]
fn block_builder_works_with_no_transactions() {
let genesis_config = genesis_config();
let prepare_genesis = || {
let mut storage = genesis_config.genesis_map();
let block = genesis::construct_genesis_block(&storage);
storage.extend(additional_storage_with_genesis(&block));
(primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect())
};
let client = new_in_mem(Executor::new(), prepare_genesis).unwrap();
let client = test_client::new();
let builder = client.new_block().unwrap();
let block = builder.bake().unwrap();
let justification = justify(&block.header);
let justified = client.check_justification(block.header, justification).unwrap();
client.import_block(BlockOrigin::Own, justified, Some(block.transactions)).unwrap();
client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap();
assert_eq!(client.info().unwrap().chain.best_number, 1);
assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), client.block_hash(1).unwrap().unwrap());
@@ -565,19 +499,7 @@ mod tests {
#[test]
fn block_builder_works_with_transactions() {
let genesis_config = GenesisConfig::new_simple(vec![
Keyring::Alice.to_raw_public(),
Keyring::Bob.to_raw_public(),
Keyring::Charlie.to_raw_public()
], 1000);
let prepare_genesis = || {
let mut storage = genesis_config.genesis_map();
let block = genesis::construct_genesis_block(&storage);
storage.extend(additional_storage_with_genesis(&block));
(primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect())
};
let client = new_in_mem(Executor::new(), prepare_genesis).unwrap();
let client = test_client::new();
let mut builder = client.new_block().unwrap();
@@ -587,11 +509,8 @@ mod tests {
amount: 42,
nonce: 0
}.signed()).unwrap();
let block = builder.bake().unwrap();
let justification = justify(&block.header);
let justified = client.check_justification(block.header, justification).unwrap();
client.import_block(BlockOrigin::Own, justified, Some(block.transactions)).unwrap();
client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap();
assert_eq!(client.info().unwrap().chain.best_number, 1);
assert!(client.state_at(&BlockId::Number(1)).unwrap() != client.state_at(&BlockId::Number(0)).unwrap());
+4 -3
View File
@@ -42,15 +42,16 @@ mod tests {
use codec::{Slicable, Joiner};
use runtime_support::Hashable;
use keyring::Keyring;
use test_runtime::genesismap::{GenesisConfig, additional_storage_with_genesis};
use executor::WasmExecutor;
use state_machine::{execute, OverlayedChanges};
use state_machine::backend::InMemory;
use test_runtime::{self, Hash, Block, BlockNumber, Header, Digest, Transaction,
use test_client;
use test_client::runtime::genesismap::{GenesisConfig, additional_storage_with_genesis};
use test_client::runtime::{Hash, Block, BlockNumber, Header, Digest, Transaction,
UncheckedTransaction};
use ed25519::{Public, Pair};
native_executor_instance!(Executor, test_runtime::api::dispatch, include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm"));
native_executor_instance!(Executor, test_client::runtime::api::dispatch, include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm"));
fn construct_block(backend: &InMemory, number: BlockNumber, parent_hash: Hash, state_root: Hash, txs: Vec<Transaction>) -> (Vec<u8>, Hash) {
use triehash::ordered_trie_root;
+17 -12
View File
@@ -19,22 +19,23 @@
#![warn(missing_docs)]
extern crate substrate_bft as bft;
extern crate substrate_runtime_support as runtime_support;
extern crate substrate_runtime_io as runtime_io;
extern crate substrate_primitives as primitives;
extern crate substrate_state_machine as state_machine;
extern crate substrate_codec as codec;
#[cfg(test)] #[macro_use] extern crate substrate_executor as executor;
extern crate ed25519;
#[cfg(test)] extern crate substrate_test_runtime as test_runtime;
extern crate substrate_primitives as primitives;
extern crate substrate_runtime_io as runtime_io;
extern crate substrate_runtime_support as runtime_support;
extern crate substrate_state_machine as state_machine;
#[cfg(test)] extern crate substrate_keyring as keyring;
#[cfg(test)] extern crate substrate_test_client as test_client;
extern crate triehash;
extern crate parking_lot;
extern crate ed25519;
extern crate futures;
#[cfg(test)] #[macro_use] extern crate hex_literal;
extern crate parking_lot;
extern crate triehash;
#[macro_use] extern crate error_chain;
#[macro_use] extern crate log;
#[cfg(test)] #[macro_use] extern crate substrate_executor as executor;
#[cfg(test)] #[macro_use] extern crate hex_literal;
pub mod error;
pub mod blockchain;
@@ -44,6 +45,10 @@ pub mod genesis;
pub mod block_builder;
mod client;
pub use client::{Client, ClientInfo, CallResult, ImportResult, ChainHead,
BlockStatus, BlockOrigin, new_in_mem, BlockchainEventStream, BlockchainEvents};
pub use client::{
new_in_mem,
BlockStatus, BlockOrigin, BlockchainEventStream, BlockchainEvents,
Client, ClientInfo, CallResult, ChainHead,
ImportResult,
};
pub use blockchain::Info as ChainInfo;
+3 -5
View File
@@ -29,9 +29,7 @@ substrate-runtime-support = { path = "../../substrate/runtime-support" }
substrate-bft = { path = "../../substrate/bft" }
[dev-dependencies]
substrate-test-runtime = { path = "../test-runtime" }
substrate-executor = { path = "../../substrate/executor" }
substrate-keyring = { path = "../../substrate/keyring" }
substrate-codec = { path = "../../substrate/codec" }
substrate-bft = { path = "../bft" }
env_logger = "0.4"
substrate-codec = { path = "../../substrate/codec" }
substrate-keyring = { path = "../../substrate/keyring" }
substrate-test-client = { path = "../../substrate/test-client" }
+2 -4
View File
@@ -40,11 +40,9 @@ extern crate ed25519;
#[macro_use] extern crate error_chain;
#[cfg(test)] extern crate env_logger;
#[cfg(test)] extern crate substrate_test_runtime as test_runtime;
#[cfg(test)] extern crate substrate_keyring as keyring;
#[cfg(test)] #[macro_use] extern crate substrate_executor as executor;
#[cfg(test)] extern crate substrate_codec as codec;
#[cfg(test)] extern crate substrate_bft as bft;
#[cfg(test)] extern crate substrate_keyring as keyring;
#[cfg(test)] extern crate substrate_test_client as test_client;
mod service;
mod sync;
+9 -50
View File
@@ -18,25 +18,21 @@ mod sync;
use std::collections::{VecDeque, HashSet, HashMap};
use std::sync::Arc;
use parking_lot::RwLock;
use client::{self, genesis, BlockOrigin};
use client;
use client::block_builder::BlockBuilder;
use primitives::block::{Id as BlockId, ExtrinsicHash};
use primitives;
use executor;
use io::SyncIo;
use protocol::Protocol;
use config::ProtocolConfig;
use service::TransactionPool;
use network::{PeerId, SessionInfo, Error as NetworkError};
use test_runtime::genesismap::{GenesisConfig, additional_storage_with_genesis};
use runtime_support::Hashable;
use test_runtime;
use keyring::Keyring;
use codec::Slicable;
use bft;
native_executor_instance!(Executor, test_runtime::api::dispatch, include_bytes!("../../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm"));
use test_client::{self, TestClient};
pub struct TestIo<'p> {
pub queue: &'p RwLock<VecDeque<TestPacket>>,
@@ -103,7 +99,7 @@ pub struct TestPacket {
}
pub struct Peer {
client: Arc<client::Client<client::in_mem::Backend, executor::NativeExecutor<Executor>>>,
client: Arc<client::Client<test_client::Backend, test_client::Executor>>,
pub sync: Protocol,
pub queue: RwLock<VecDeque<TestPacket>>,
}
@@ -161,37 +157,13 @@ impl Peer {
fn flush(&self) {
}
fn justify(header: &primitives::block::Header) -> bft::UncheckedJustification {
let hash = header.blake2_256().into();
let authorities = vec![ Keyring::Alice.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 generate_blocks<F>(&self, count: usize, mut edit_block: F) where F: FnMut(&mut BlockBuilder<client::in_mem::Backend, executor::NativeExecutor<Executor>>) {
fn generate_blocks<F>(&self, count: usize, mut edit_block: F) where F: FnMut(&mut BlockBuilder<test_client::Backend, test_client::Executor>) {
for _ in 0 .. count {
let mut builder = self.client.new_block().unwrap();
edit_block(&mut builder);
let block = builder.bake().unwrap();
trace!("Generating {}, (#{})", primitives::block::HeaderHash::from(block.header.blake2_256()), block.header.number);
let justification = Self::justify(&block.header);
let justified = self.client.check_justification(block.header, justification).unwrap();
self.client.import_block(BlockOrigin::File, justified, Some(block.transactions)).unwrap();
self.client.justify_and_import(client::BlockOrigin::File, block).unwrap();
}
}
@@ -199,14 +171,14 @@ impl Peer {
let mut nonce = 0;
if with_tx {
self.generate_blocks(count, |builder| {
let tx = test_runtime::Transaction {
let tx = test_client::runtime::Transaction {
from: Keyring::Alice.to_raw_public(),
to: Keyring::Alice.to_raw_public(),
amount: 1,
nonce: nonce,
};
let signature = Keyring::from_raw_public(tx.from.clone()).unwrap().sign(&tx.encode());
let tx = primitives::block::Extrinsic::decode(&mut test_runtime::UncheckedTransaction { signature, tx: tx }.encode().as_ref()).unwrap();
let tx = primitives::block::Extrinsic::decode(&mut test_client::runtime::UncheckedTransaction { signature, tx: tx }.encode().as_ref()).unwrap();
builder.push(tx).unwrap();
nonce = nonce + 1;
});
@@ -235,19 +207,6 @@ pub struct TestNet {
}
impl TestNet {
fn genesis_config() -> GenesisConfig {
GenesisConfig::new_simple(vec![
Keyring::Alice.to_raw_public(),
], 1000)
}
fn prepare_genesis() -> (primitives::block::Header, Vec<(Vec<u8>, Vec<u8>)>) {
let mut storage = Self::genesis_config().genesis_map();
let block = genesis::construct_genesis_block(&storage);
storage.extend(additional_storage_with_genesis(&block));
(primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect())
}
pub fn new(n: usize) -> Self {
Self::new_with_config(n, ProtocolConfig::default())
}
@@ -260,7 +219,7 @@ impl TestNet {
};
for _ in 0..n {
let client = Arc::new(client::new_in_mem(Executor::new(), Self::prepare_genesis).unwrap());
let client = Arc::new(test_client::new());
let tx_pool = Arc::new(EmptyTransactionPool);
let sync = Protocol::new(config.clone(), client.clone(), tx_pool).unwrap();
net.peers.push(Arc::new(Peer {
+1 -2
View File
@@ -18,5 +18,4 @@ tokio-core = "0.1.12"
[dev-dependencies]
assert_matches = "1.1"
substrate-executor = { path = "../executor" }
substrate-runtime-support = { path = "../runtime-support" }
substrate-test-client = { path = "../test-client" }
+38 -16
View File
@@ -14,42 +14,64 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use substrate_executor as executor;
use client;
use runtime_support::Hashable;
use super::*;
use jsonrpc_macros::pubsub;
use client::BlockOrigin;
use test_client::{self, TestClient};
#[test]
fn should_return_header() {
let test_genesis_block = block::Header {
parent_hash: 0.into(),
number: 0,
state_root: 0.into(),
extrinsics_root: Default::default(),
digest: Default::default(),
};
let core = ::tokio_core::reactor::Core::new().unwrap();
let remote = core.remote();
let client = Chain {
client: Arc::new(client::new_in_mem(executor::WasmExecutor, || (test_genesis_block.clone(), vec![])).unwrap()),
client: Arc::new(test_client::new()),
subscriptions: Subscriptions::new(remote),
};
assert_matches!(
ChainApi::header(&client, test_genesis_block.blake2_256().into()),
client.header(client.client.genesis_hash()),
Ok(Some(ref x)) if x == &block::Header {
parent_hash: 0.into(),
number: 0,
state_root: 0.into(),
extrinsics_root: Default::default(),
state_root: "6da331d07a82d99f4debaafb0110a2e36244ed34162f9a7f6312a23fd52989ed".into(),
extrinsics_root: "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".into(),
digest: Default::default(),
}
);
assert_matches!(
ChainApi::header(&client, 5.into()),
client.header(5.into()),
Ok(None)
);
}
#[test]
fn should_notify_about_latest_block() {
let mut core = ::tokio_core::reactor::Core::new().unwrap();
let remote = core.remote();
let (subscriber, id, transport) = pubsub::Subscriber::new_test("test");
{
let api = Chain {
client: Arc::new(test_client::new()),
subscriptions: Subscriptions::new(remote),
};
api.subscribe_new_head(Default::default(), subscriber);
// assert id assigned
assert_eq!(core.run(id), Ok(Ok(SubscriptionId::Number(0))));
let builder = api.client.new_block().unwrap();
api.client.justify_and_import(BlockOrigin::Own, builder.bake().unwrap()).unwrap();
}
// assert notification send to transport
let (notification, next) = core.run(transport.into_future()).unwrap();
assert_eq!(notification, Some(
r#"{"jsonrpc":"2.0","method":"test","params":{"result":{"digest":{"logs":[]},"extrinsicsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","number":1,"parentHash":"0x4c4ab196ed07bbd5b8c901ae5092d9d3990cbb4d44421af8e988af7d3c2a4226","stateRoot":"0x75b634da2a0d272e8a5145ab704406d3b50676c7739f977f2ccb2d0e5a0cdbd0"},"subscription":0}}"#.to_owned()
));
// no more notifications on this channel
assert_eq!(core.run(next.into_future()).unwrap().0, None);
}
+1 -3
View File
@@ -33,13 +33,11 @@ extern crate jsonrpc_macros;
#[macro_use]
extern crate log;
#[cfg(test)]
extern crate substrate_executor;
#[cfg(test)]
#[macro_use]
extern crate assert_matches;
#[cfg(test)]
extern crate substrate_runtime_support as runtime_support;
extern crate substrate_test_client as test_client;
mod subscriptions;
+5 -25
View File
@@ -15,23 +15,13 @@
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use super::*;
use substrate_executor as executor;
use self::error::{Error, ErrorKind};
use runtime_support::Hashable;
use client;
use test_client::{self, TestClient};
#[test]
fn should_return_storage() {
let test_genesis_block = block::Header {
parent_hash: 0.into(),
number: 0,
state_root: 0.into(),
extrinsics_root: Default::default(),
digest: Default::default(),
};
let client = Arc::new(client::new_in_mem(executor::WasmExecutor, || (test_genesis_block.clone(), vec![])).unwrap());
let genesis_hash = test_genesis_block.blake2_256().into();
let client = Arc::new(test_client::new());
let genesis_hash = client.genesis_hash();
assert_matches!(
StateApi::storage_at(&client, StorageKey(vec![10]), genesis_hash),
@@ -40,19 +30,9 @@ fn should_return_storage() {
}
#[test]
#[ignore] // TODO: [ToDr] reenable once we can properly mock the wasm executor env
fn should_call_contract() {
// TODO [ToDr] Fix test after we are able to mock state.
let test_genesis_block = block::Header {
parent_hash: 0.into(),
number: 0,
state_root: 0.into(),
extrinsics_root: Default::default(),
digest: Default::default(),
};
let client = Arc::new(client::new_in_mem(executor::WasmExecutor, || (test_genesis_block.clone(), vec![])).unwrap());
let genesis_hash = test_genesis_block.blake2_256().into();
let client = Arc::new(test_client::new());
let genesis_hash = client.genesis_hash();
assert_matches!(
StateApi::call_at(&client, "balanceOf".into(), vec![1,2,3], genesis_hash),
@@ -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()
}