diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs
index 0daa17553..58a21f151 100644
--- a/ethcore/src/client/mod.rs
+++ b/ethcore/src/client/mod.rs
@@ -19,10 +19,12 @@
mod client;
mod config;
mod ids;
+mod test_client;
pub use self::client::*;
pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig};
pub use self::ids::{BlockId, TransactionId};
+pub use self::test_client::{TestBlockChainClient, EachBlockWith};
use util::bytes::Bytes;
use util::hash::{Address, H256, H2048};
diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs
new file mode 100644
index 000000000..4ca30dcd5
--- /dev/null
+++ b/ethcore/src/client/test_client.rs
@@ -0,0 +1,322 @@
+// Copyright 2015, 2016 Ethcore (UK) Ltd.
+// This file is part of Parity.
+
+// Parity 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.
+
+// Parity 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 Parity. If not, see .
+
+//use std::mem;
+//use std::ops::{Deref, DerefMut};
+//use std::collections::HashMap;
+//use rustc_serialize::hex::FromHex;
+//use util::rlp;
+//use util::rlp::*;
+//use util::bytes::Bytes;
+//use util::hash::{FixedHash, Address, H256, H2048};
+//use util::numbers::{Uint, U256};
+//use util::crypto::KeyPair;
+//use util::sha3::Hashable;
+use util::*;
+//use std::sync::RwLock;
+use transaction::{Transaction, LocalizedTransaction, Action};
+use blockchain::TreeRoute;
+use client::{BlockChainClient, BlockChainInfo, BlockStatus, BlockId, TransactionId};
+use header::{Header as BlockHeader, BlockNumber};
+use filter::Filter;
+use log_entry::LocalizedLogEntry;
+use receipt::Receipt;
+use error::ImportResult;
+use block_queue::BlockQueueInfo;
+
+pub struct TestBlockChainClient {
+ pub blocks: RwLock>,
+ pub numbers: RwLock>,
+ pub genesis_hash: H256,
+ pub last_hash: RwLock,
+ pub difficulty: RwLock,
+}
+
+#[derive(Clone)]
+pub enum EachBlockWith {
+ Nothing,
+ Uncle,
+ Transaction,
+ UncleAndTransaction
+}
+
+impl TestBlockChainClient {
+ pub fn new() -> TestBlockChainClient {
+
+ let mut client = TestBlockChainClient {
+ blocks: RwLock::new(HashMap::new()),
+ numbers: RwLock::new(HashMap::new()),
+ genesis_hash: H256::new(),
+ last_hash: RwLock::new(H256::new()),
+ difficulty: RwLock::new(From::from(0)),
+ };
+ client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
+ client.genesis_hash = client.last_hash.read().unwrap().clone();
+ client
+ }
+
+ pub fn add_blocks(&mut self, count: usize, with: EachBlockWith) {
+ let len = self.numbers.read().unwrap().len();
+ for n in len..(len + count) {
+ let mut header = BlockHeader::new();
+ header.difficulty = From::from(n);
+ header.parent_hash = self.last_hash.read().unwrap().clone();
+ header.number = n as BlockNumber;
+ let uncles = match with {
+ EachBlockWith::Uncle | EachBlockWith::UncleAndTransaction => {
+ let mut uncles = RlpStream::new_list(1);
+ let mut uncle_header = BlockHeader::new();
+ uncle_header.difficulty = From::from(n);
+ uncle_header.parent_hash = self.last_hash.read().unwrap().clone();
+ uncle_header.number = n as BlockNumber;
+ uncles.append(&uncle_header);
+ header.uncles_hash = uncles.as_raw().sha3();
+ uncles
+ },
+ _ => RlpStream::new_list(0)
+ };
+ let txs = match with {
+ EachBlockWith::Transaction | EachBlockWith::UncleAndTransaction => {
+ let mut txs = RlpStream::new_list(1);
+ let keypair = KeyPair::create().unwrap();
+ let tx = Transaction {
+ action: Action::Create,
+ value: U256::from(100),
+ data: "3331600055".from_hex().unwrap(),
+ gas: U256::from(100_000),
+ gas_price: U256::one(),
+ nonce: U256::zero()
+ };
+ let signed_tx = tx.sign(&keypair.secret());
+ txs.append(&signed_tx);
+ txs.out()
+ },
+ _ => rlp::NULL_RLP.to_vec()
+ };
+
+ let mut rlp = RlpStream::new_list(3);
+ rlp.append(&header);
+ rlp.append_raw(&txs, 1);
+ rlp.append_raw(uncles.as_raw(), 1);
+ self.import_block(rlp.as_raw().to_vec()).unwrap();
+ }
+ }
+
+ pub fn corrupt_block(&mut self, n: BlockNumber) {
+ let hash = self.block_hash(BlockId::Number(n)).unwrap();
+ let mut header: BlockHeader = decode(&self.block_header(BlockId::Number(n)).unwrap());
+ header.parent_hash = H256::new();
+ let mut rlp = RlpStream::new_list(3);
+ rlp.append(&header);
+ rlp.append_raw(&rlp::NULL_RLP, 1);
+ rlp.append_raw(&rlp::NULL_RLP, 1);
+ self.blocks.write().unwrap().insert(hash, rlp.out());
+ }
+
+ pub fn block_hash_delta_minus(&mut self, delta: usize) -> H256 {
+ let blocks_read = self.numbers.read().unwrap();
+ let index = blocks_read.len() - delta;
+ blocks_read[&index].clone()
+ }
+
+ fn block_hash(&self, id: BlockId) -> Option {
+ match id {
+ BlockId::Hash(hash) => Some(hash),
+ BlockId::Number(n) => self.numbers.read().unwrap().get(&(n as usize)).cloned(),
+ BlockId::Earliest => self.numbers.read().unwrap().get(&0).cloned(),
+ BlockId::Latest => self.numbers.read().unwrap().get(&(self.numbers.read().unwrap().len() - 1)).cloned()
+ }
+ }
+}
+
+impl BlockChainClient for TestBlockChainClient {
+ fn block_total_difficulty(&self, _id: BlockId) -> Option {
+ Some(U256::zero())
+ }
+
+ fn block_hash(&self, _id: BlockId) -> Option {
+ unimplemented!();
+ }
+
+ fn nonce(&self, _address: &Address) -> U256 {
+ U256::zero()
+ }
+
+ fn code(&self, _address: &Address) -> Option {
+ unimplemented!();
+ }
+
+ fn transaction(&self, _id: TransactionId) -> Option {
+ unimplemented!();
+ }
+
+ fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockId, _to_block: BlockId) -> Option> {
+ unimplemented!();
+ }
+
+ fn logs(&self, _filter: Filter) -> Vec {
+ unimplemented!();
+ }
+
+ fn block_header(&self, id: BlockId) -> Option {
+ self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec()))
+ }
+
+ fn block_body(&self, id: BlockId) -> Option {
+ self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| {
+ let mut stream = RlpStream::new_list(2);
+ stream.append_raw(Rlp::new(&r).at(1).as_raw(), 1);
+ stream.append_raw(Rlp::new(&r).at(2).as_raw(), 1);
+ stream.out()
+ }))
+ }
+
+ fn block(&self, id: BlockId) -> Option {
+ self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).cloned())
+ }
+
+ fn block_status(&self, id: BlockId) -> BlockStatus {
+ match id {
+ BlockId::Number(number) if (number as usize) < self.blocks.read().unwrap().len() => BlockStatus::InChain,
+ BlockId::Hash(ref hash) if self.blocks.read().unwrap().get(hash).is_some() => BlockStatus::InChain,
+ _ => BlockStatus::Unknown
+ }
+ }
+
+ // works only if blocks are one after another 1 -> 2 -> 3
+ fn tree_route(&self, from: &H256, to: &H256) -> Option {
+ Some(TreeRoute {
+ ancestor: H256::new(),
+ index: 0,
+ blocks: {
+ let numbers_read = self.numbers.read().unwrap();
+ let mut adding = false;
+
+ let mut blocks = Vec::new();
+ for (_, hash) in numbers_read.iter().sort_by(|tuple1, tuple2| tuple1.0.cmp(tuple2.0)) {
+ if hash == to {
+ if adding {
+ blocks.push(hash.clone());
+ }
+ adding = false;
+ break;
+ }
+ if hash == from {
+ adding = true;
+ }
+ if adding {
+ blocks.push(hash.clone());
+ }
+ }
+ if adding { Vec::new() } else { blocks }
+ }
+ })
+ }
+
+ // TODO: returns just hashes instead of node state rlp(?)
+ fn state_data(&self, hash: &H256) -> Option {
+ // starts with 'f' ?
+ if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") {
+ let mut rlp = RlpStream::new();
+ rlp.append(&hash.clone());
+ return Some(rlp.out());
+ }
+ None
+ }
+
+ fn block_receipts(&self, hash: &H256) -> Option {
+ // starts with 'f' ?
+ if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") {
+ let receipt = Receipt::new(
+ H256::zero(),
+ U256::zero(),
+ vec![]);
+ let mut rlp = RlpStream::new();
+ rlp.append(&receipt);
+ return Some(rlp.out());
+ }
+ None
+ }
+
+ fn import_block(&self, b: Bytes) -> ImportResult {
+ let header = Rlp::new(&b).val_at::(0);
+ let h = header.hash();
+ let number: usize = header.number as usize;
+ if number > self.blocks.read().unwrap().len() {
+ panic!("Unexpected block number. Expected {}, got {}", self.blocks.read().unwrap().len(), number);
+ }
+ if number > 0 {
+ match self.blocks.read().unwrap().get(&header.parent_hash) {
+ Some(parent) => {
+ let parent = Rlp::new(parent).val_at::(0);
+ if parent.number != (header.number - 1) {
+ panic!("Unexpected block parent");
+ }
+ },
+ None => {
+ panic!("Unknown block parent {:?} for block {}", header.parent_hash, number);
+ }
+ }
+ }
+ let len = self.numbers.read().unwrap().len();
+ if number == len {
+ {
+ let mut difficulty = self.difficulty.write().unwrap();
+ *difficulty.deref_mut() = *difficulty.deref() + header.difficulty;
+ }
+ mem::replace(self.last_hash.write().unwrap().deref_mut(), h.clone());
+ self.blocks.write().unwrap().insert(h.clone(), b);
+ self.numbers.write().unwrap().insert(number, h.clone());
+ let mut parent_hash = header.parent_hash;
+ if number > 0 {
+ let mut n = number - 1;
+ while n > 0 && self.numbers.read().unwrap()[&n] != parent_hash {
+ *self.numbers.write().unwrap().get_mut(&n).unwrap() = parent_hash.clone();
+ n -= 1;
+ parent_hash = Rlp::new(&self.blocks.read().unwrap()[&parent_hash]).val_at::(0).parent_hash;
+ }
+ }
+ }
+ else {
+ self.blocks.write().unwrap().insert(h.clone(), b.to_vec());
+ }
+ Ok(h)
+ }
+
+ fn queue_info(&self) -> BlockQueueInfo {
+ BlockQueueInfo {
+ verified_queue_size: 0,
+ unverified_queue_size: 0,
+ verifying_queue_size: 0,
+ max_queue_size: 0,
+ max_mem_use: 0,
+ mem_used: 0,
+ }
+ }
+
+ fn clear_queue(&self) {
+ }
+
+ fn chain_info(&self) -> BlockChainInfo {
+ BlockChainInfo {
+ total_difficulty: *self.difficulty.read().unwrap(),
+ pending_total_difficulty: *self.difficulty.read().unwrap(),
+ genesis_hash: self.genesis_hash.clone(),
+ best_block_hash: self.last_hash.read().unwrap().clone(),
+ best_block_number: self.blocks.read().unwrap().len() as BlockNumber - 1,
+ }
+ }
+}
diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs
index 469364eb3..0ff5c1903 100644
--- a/ethcore/src/lib.rs
+++ b/ethcore/src/lib.rs
@@ -86,6 +86,7 @@ extern crate crossbeam;
#[cfg(feature = "jit" )] extern crate evmjit;
pub mod block;
+pub mod block_queue;
pub mod client;
pub mod error;
pub mod ethereum;
@@ -119,7 +120,6 @@ mod substate;
mod executive;
mod externalities;
mod verification;
-mod block_queue;
mod blockchain;
#[cfg(test)]
diff --git a/sync/src/tests/chain.rs b/sync/src/tests/chain.rs
index 855aa79a6..eebbdb164 100644
--- a/sync/src/tests/chain.rs
+++ b/sync/src/tests/chain.rs
@@ -15,7 +15,7 @@
// along with Parity. If not, see .
use util::*;
-use ethcore::client::{BlockChainClient, BlockId};
+use ethcore::client::{BlockChainClient, BlockId, EachBlockWith};
use io::SyncIo;
use chain::{SyncState};
use super::helpers::*;
diff --git a/sync/src/tests/helpers.rs b/sync/src/tests/helpers.rs
index d01dba0b2..ca4ae5158 100644
--- a/sync/src/tests/helpers.rs
+++ b/sync/src/tests/helpers.rs
@@ -15,300 +15,10 @@
// along with Parity. If not, see .
use util::*;
-use ethcore::client::{BlockChainClient, BlockStatus, TreeRoute, BlockChainInfo, TransactionId, BlockId, BlockQueueInfo};
-use ethcore::header::{Header as BlockHeader, BlockNumber};
-use ethcore::error::*;
+use ethcore::client::{TestBlockChainClient, BlockChainClient};
use io::SyncIo;
use chain::ChainSync;
use ::SyncConfig;
-use ethcore::receipt::Receipt;
-use ethcore::transaction::{LocalizedTransaction, Transaction, Action};
-use ethcore::filter::Filter;
-use ethcore::log_entry::LocalizedLogEntry;
-
-pub struct TestBlockChainClient {
- pub blocks: RwLock>,
- pub numbers: RwLock>,
- pub genesis_hash: H256,
- pub last_hash: RwLock,
- pub difficulty: RwLock,
-}
-
-#[derive(Clone)]
-pub enum EachBlockWith {
- Nothing,
- Uncle,
- Transaction,
- UncleAndTransaction
-}
-
-impl TestBlockChainClient {
- pub fn new() -> TestBlockChainClient {
-
- let mut client = TestBlockChainClient {
- blocks: RwLock::new(HashMap::new()),
- numbers: RwLock::new(HashMap::new()),
- genesis_hash: H256::new(),
- last_hash: RwLock::new(H256::new()),
- difficulty: RwLock::new(From::from(0)),
- };
- client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
- client.genesis_hash = client.last_hash.read().unwrap().clone();
- client
- }
-
- pub fn add_blocks(&mut self, count: usize, with: EachBlockWith) {
- let len = self.numbers.read().unwrap().len();
- for n in len..(len + count) {
- let mut header = BlockHeader::new();
- header.difficulty = From::from(n);
- header.parent_hash = self.last_hash.read().unwrap().clone();
- header.number = n as BlockNumber;
- let uncles = match with {
- EachBlockWith::Uncle | EachBlockWith::UncleAndTransaction => {
- let mut uncles = RlpStream::new_list(1);
- let mut uncle_header = BlockHeader::new();
- uncle_header.difficulty = From::from(n);
- uncle_header.parent_hash = self.last_hash.read().unwrap().clone();
- uncle_header.number = n as BlockNumber;
- uncles.append(&uncle_header);
- header.uncles_hash = uncles.as_raw().sha3();
- uncles
- },
- _ => RlpStream::new_list(0)
- };
- let txs = match with {
- EachBlockWith::Transaction | EachBlockWith::UncleAndTransaction => {
- let mut txs = RlpStream::new_list(1);
- let keypair = KeyPair::create().unwrap();
- let tx = Transaction {
- action: Action::Create,
- value: U256::from(100),
- data: "3331600055".from_hex().unwrap(),
- gas: U256::from(100_000),
- gas_price: U256::one(),
- nonce: U256::zero()
- };
- let signed_tx = tx.sign(&keypair.secret());
- txs.append(&signed_tx);
- txs.out()
- },
- _ => rlp::NULL_RLP.to_vec()
- };
-
- let mut rlp = RlpStream::new_list(3);
- rlp.append(&header);
- rlp.append_raw(&txs, 1);
- rlp.append_raw(uncles.as_raw(), 1);
- self.import_block(rlp.as_raw().to_vec()).unwrap();
- }
- }
-
- pub fn corrupt_block(&mut self, n: BlockNumber) {
- let hash = self.block_hash(BlockId::Number(n)).unwrap();
- let mut header: BlockHeader = decode(&self.block_header(BlockId::Number(n)).unwrap());
- header.parent_hash = H256::new();
- let mut rlp = RlpStream::new_list(3);
- rlp.append(&header);
- rlp.append_raw(&rlp::NULL_RLP, 1);
- rlp.append_raw(&rlp::NULL_RLP, 1);
- self.blocks.write().unwrap().insert(hash, rlp.out());
- }
-
- pub fn block_hash_delta_minus(&mut self, delta: usize) -> H256 {
- let blocks_read = self.numbers.read().unwrap();
- let index = blocks_read.len() - delta;
- blocks_read[&index].clone()
- }
-
- fn block_hash(&self, id: BlockId) -> Option {
- match id {
- BlockId::Hash(hash) => Some(hash),
- BlockId::Number(n) => self.numbers.read().unwrap().get(&(n as usize)).cloned(),
- BlockId::Earliest => self.numbers.read().unwrap().get(&0).cloned(),
- BlockId::Latest => self.numbers.read().unwrap().get(&(self.numbers.read().unwrap().len() - 1)).cloned()
- }
- }
-}
-
-impl BlockChainClient for TestBlockChainClient {
- fn block_total_difficulty(&self, _id: BlockId) -> Option {
- Some(U256::zero())
- }
-
- fn block_hash(&self, _id: BlockId) -> Option {
- unimplemented!();
- }
-
- fn nonce(&self, _address: &Address) -> U256 {
- U256::zero()
- }
-
- fn code(&self, _address: &Address) -> Option {
- unimplemented!();
- }
-
- fn transaction(&self, _id: TransactionId) -> Option {
- unimplemented!();
- }
-
- fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockId, _to_block: BlockId) -> Option> {
- unimplemented!();
- }
-
- fn logs(&self, _filter: Filter) -> Vec {
- unimplemented!();
- }
-
- fn block_header(&self, id: BlockId) -> Option {
- self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec()))
- }
-
- fn block_body(&self, id: BlockId) -> Option {
- self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).map(|r| {
- let mut stream = RlpStream::new_list(2);
- stream.append_raw(Rlp::new(&r).at(1).as_raw(), 1);
- stream.append_raw(Rlp::new(&r).at(2).as_raw(), 1);
- stream.out()
- }))
- }
-
- fn block(&self, id: BlockId) -> Option {
- self.block_hash(id).and_then(|hash| self.blocks.read().unwrap().get(&hash).cloned())
- }
-
- fn block_status(&self, id: BlockId) -> BlockStatus {
- match id {
- BlockId::Number(number) if (number as usize) < self.blocks.read().unwrap().len() => BlockStatus::InChain,
- BlockId::Hash(ref hash) if self.blocks.read().unwrap().get(hash).is_some() => BlockStatus::InChain,
- _ => BlockStatus::Unknown
- }
- }
-
- // works only if blocks are one after another 1 -> 2 -> 3
- fn tree_route(&self, from: &H256, to: &H256) -> Option {
- Some(TreeRoute {
- ancestor: H256::new(),
- index: 0,
- blocks: {
- let numbers_read = self.numbers.read().unwrap();
- let mut adding = false;
-
- let mut blocks = Vec::new();
- for (_, hash) in numbers_read.iter().sort_by(|tuple1, tuple2| tuple1.0.cmp(tuple2.0)) {
- if hash == to {
- if adding {
- blocks.push(hash.clone());
- }
- adding = false;
- break;
- }
- if hash == from {
- adding = true;
- }
- if adding {
- blocks.push(hash.clone());
- }
- }
- if adding { Vec::new() } else { blocks }
- }
- })
- }
-
- // TODO: returns just hashes instead of node state rlp(?)
- fn state_data(&self, hash: &H256) -> Option {
- // starts with 'f' ?
- if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") {
- let mut rlp = RlpStream::new();
- rlp.append(&hash.clone());
- return Some(rlp.out());
- }
- None
- }
-
- fn block_receipts(&self, hash: &H256) -> Option {
- // starts with 'f' ?
- if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") {
- let receipt = Receipt::new(
- H256::zero(),
- U256::zero(),
- vec![]);
- let mut rlp = RlpStream::new();
- rlp.append(&receipt);
- return Some(rlp.out());
- }
- None
- }
-
- fn import_block(&self, b: Bytes) -> ImportResult {
- let header = Rlp::new(&b).val_at::(0);
- let h = header.hash();
- let number: usize = header.number as usize;
- if number > self.blocks.read().unwrap().len() {
- panic!("Unexpected block number. Expected {}, got {}", self.blocks.read().unwrap().len(), number);
- }
- if number > 0 {
- match self.blocks.read().unwrap().get(&header.parent_hash) {
- Some(parent) => {
- let parent = Rlp::new(parent).val_at::(0);
- if parent.number != (header.number - 1) {
- panic!("Unexpected block parent");
- }
- },
- None => {
- panic!("Unknown block parent {:?} for block {}", header.parent_hash, number);
- }
- }
- }
- let len = self.numbers.read().unwrap().len();
- if number == len {
- {
- let mut difficulty = self.difficulty.write().unwrap();
- *difficulty.deref_mut() = *difficulty.deref() + header.difficulty;
- }
- mem::replace(self.last_hash.write().unwrap().deref_mut(), h.clone());
- self.blocks.write().unwrap().insert(h.clone(), b);
- self.numbers.write().unwrap().insert(number, h.clone());
- let mut parent_hash = header.parent_hash;
- if number > 0 {
- let mut n = number - 1;
- while n > 0 && self.numbers.read().unwrap()[&n] != parent_hash {
- *self.numbers.write().unwrap().get_mut(&n).unwrap() = parent_hash.clone();
- n -= 1;
- parent_hash = Rlp::new(&self.blocks.read().unwrap()[&parent_hash]).val_at::(0).parent_hash;
- }
- }
- }
- else {
- self.blocks.write().unwrap().insert(h.clone(), b.to_vec());
- }
- Ok(h)
- }
-
- fn queue_info(&self) -> BlockQueueInfo {
- BlockQueueInfo {
- verified_queue_size: 0,
- unverified_queue_size: 0,
- verifying_queue_size: 0,
- max_queue_size: 0,
- max_mem_use: 0,
- mem_used: 0,
- }
- }
-
- fn clear_queue(&self) {
- }
-
- fn chain_info(&self) -> BlockChainInfo {
- BlockChainInfo {
- total_difficulty: *self.difficulty.read().unwrap(),
- pending_total_difficulty: *self.difficulty.read().unwrap(),
- genesis_hash: self.genesis_hash.clone(),
- best_block_hash: self.last_hash.read().unwrap().clone(),
- best_block_number: self.blocks.read().unwrap().len() as BlockNumber - 1,
- }
- }
-}
pub struct TestIo<'p> {
pub chain: &'p mut TestBlockChainClient,