Merge branch 'master' into fix_suicide

Conflicts:
	src/externalities.rs
This commit is contained in:
Tomusdrw
2016-01-27 17:40:56 +01:00
35 changed files with 797 additions and 72 deletions

View File

@@ -1,143 +0,0 @@
#![feature(plugin)]
#![plugin(docopt_macros)]
extern crate docopt;
extern crate rustc_serialize;
extern crate ethcore_util as util;
extern crate ethcore;
extern crate log;
extern crate env_logger;
extern crate ctrlc;
use std::env;
use log::{LogLevelFilter};
use env_logger::LogBuilder;
use ctrlc::CtrlC;
use util::*;
use ethcore::client::*;
use ethcore::service::{ClientService, NetSyncMessage};
use ethcore::ethereum;
use ethcore::blockchain::CacheSize;
use ethcore::sync::EthSync;
docopt!(Args derive Debug, "
Parity. Ethereum Client.
Usage:
parity [options]
parity [options] <enode>...
Options:
-l --logging LOGGING Specify the logging level
-h --help Show this screen.
");
fn setup_log(init: &String) {
let mut builder = LogBuilder::new();
builder.filter(None, LogLevelFilter::Info);
if env::var("RUST_LOG").is_ok() {
builder.parse(&env::var("RUST_LOG").unwrap());
}
builder.parse(init);
builder.init().unwrap();
}
fn main() {
let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
setup_log(&args.flag_logging);
let spec = ethereum::new_frontier();
let init_nodes = match args.arg_enode.len() {
0 => spec.nodes().clone(),
_ => args.arg_enode.clone(),
};
let mut net_settings = NetworkConfiguration::new();
net_settings.boot_nodes = init_nodes;
let mut service = ClientService::start(spec, net_settings).unwrap();
let io_handler = Arc::new(ClientIoHandler { client: service.client(), info: Default::default(), sync: service.sync() });
service.io().register_handler(io_handler).expect("Error registering IO handler");
let exit = Arc::new(Condvar::new());
let e = exit.clone();
CtrlC::set_handler(move || { e.notify_all(); });
let mutex = Mutex::new(());
let _ = exit.wait(mutex.lock().unwrap()).unwrap();
}
struct Informant {
chain_info: RwLock<Option<BlockChainInfo>>,
cache_info: RwLock<Option<CacheSize>>,
report: RwLock<Option<ClientReport>>,
}
impl Default for Informant {
fn default() -> Self {
Informant {
chain_info: RwLock::new(None),
cache_info: RwLock::new(None),
report: RwLock::new(None),
}
}
}
impl Informant {
pub fn tick(&self, client: &Client, sync: &EthSync) {
// 5 seconds betwen calls. TODO: calculate this properly.
let dur = 5usize;
let chain_info = client.chain_info();
let queue_info = client.queue_info();
let cache_info = client.cache_info();
let report = client.report();
let sync_info = sync.status();
if let (_, &Some(ref last_cache_info), &Some(ref last_report)) = (self.chain_info.read().unwrap().deref(), self.cache_info.read().unwrap().deref(), self.report.read().unwrap().deref()) {
println!("[ {} {} ]---[ {} blk/s | {} tx/s | {} gas/s //··· {}/{} peers, {} downloaded, {}+{} queued ···// {} ({}) bl {} ({}) ex ]",
chain_info.best_block_number,
chain_info.best_block_hash,
(report.blocks_imported - last_report.blocks_imported) / dur,
(report.transactions_applied - last_report.transactions_applied) / dur,
(report.gas_processed - last_report.gas_processed) / From::from(dur),
sync_info.num_active_peers,
sync_info.num_peers,
sync_info.blocks_received,
queue_info.unverified_queue_size,
queue_info.verified_queue_size,
cache_info.blocks,
cache_info.blocks as isize - last_cache_info.blocks as isize,
cache_info.block_details,
cache_info.block_details as isize - last_cache_info.block_details as isize
);
}
*self.chain_info.write().unwrap().deref_mut() = Some(chain_info);
*self.cache_info.write().unwrap().deref_mut() = Some(cache_info);
*self.report.write().unwrap().deref_mut() = Some(report);
}
}
const INFO_TIMER: TimerToken = 0;
struct ClientIoHandler {
client: Arc<Client>,
sync: Arc<EthSync>,
info: Informant,
}
impl IoHandler<NetSyncMessage> for ClientIoHandler {
fn initialize(&self, io: &IoContext<NetSyncMessage>) {
io.register_timer(INFO_TIMER, 5000).expect("Error registering timer");
}
fn timeout(&self, _io: &IoContext<NetSyncMessage>, timer: TimerToken) {
if INFO_TIMER == timer {
self.info.tick(&self.client, &self.sync);
}
}
}

View File

@@ -158,7 +158,6 @@ impl BlockQueue {
},
Err(err) => {
let mut v = verification.lock().unwrap();
flushln!("Stage 2 block verification failed for {}\nError: {:?}", block_hash, err);
warn!(target: "client", "Stage 2 block verification failed for {}\nError: {:?}", block_hash, err);
v.bad.insert(block_hash.clone());
v.verifying.retain(|e| e.hash != block_hash);
@@ -200,34 +199,35 @@ impl BlockQueue {
/// Add a block to the queue.
pub fn import_block(&mut self, bytes: Bytes) -> ImportResult {
let header = BlockView::new(&bytes).header();
if self.processing.contains(&header.hash()) {
let h = header.hash();
if self.processing.contains(&h) {
return Err(ImportError::AlreadyQueued);
}
{
let mut verification = self.verification.lock().unwrap();
if verification.bad.contains(&header.hash()) {
if verification.bad.contains(&h) {
return Err(ImportError::Bad(None));
}
if verification.bad.contains(&header.parent_hash) {
verification.bad.insert(header.hash());
verification.bad.insert(h.clone());
return Err(ImportError::Bad(None));
}
}
match verify_block_basic(&header, &bytes, self.engine.deref().deref()) {
Ok(()) => {
self.processing.insert(header.hash());
self.processing.insert(h.clone());
self.verification.lock().unwrap().unverified.push_back(UnVerifiedBlock { header: header, bytes: bytes });
self.more_to_verify.notify_all();
Ok(h)
},
Err(err) => {
flushln!("Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), err);
warn!(target: "client", "Stage 1 block verification failed for {}\nError: {:?}", BlockView::new(&bytes).header_view().sha3(), err);
self.verification.lock().unwrap().bad.insert(header.hash());
self.verification.lock().unwrap().bad.insert(h.clone());
Err(From::from(err))
}
}
Ok(())
}
/// Mark given block and all its children as bad. Stops verification.

View File

@@ -565,15 +565,6 @@ impl BlockChain {
}
}
/// Tries to squeeze the cache if its too big.
pub fn squeeze_to_fit(&self, size: CacheSize) {
self.blocks.write().unwrap().squeeze(size.blocks);
self.block_details.write().unwrap().squeeze(size.block_details);
self.transaction_addresses.write().unwrap().squeeze(size.transaction_addresses);
self.block_logs.write().unwrap().squeeze(size.block_logs);
self.blocks_blooms.write().unwrap().squeeze(size.blocks_blooms);
}
/// Let the cache system know that a cacheable item has been used.
fn note_used(&self, id: CacheID) {
let mut cache_man = self.cache_man.write().unwrap();

View File

@@ -66,6 +66,9 @@ pub trait BlockChainClient : Sync + Send {
/// Get block status by block header hash.
fn block_status(&self, hash: &H256) -> BlockStatus;
/// Get block total difficulty.
fn block_total_difficulty(&self, hash: &H256) -> Option<U256>;
/// Get raw block header data by block number.
fn block_header_at(&self, n: BlockNumber) -> Option<Bytes>;
@@ -79,6 +82,9 @@ pub trait BlockChainClient : Sync + Send {
/// Get block status by block number.
fn block_status_at(&self, n: BlockNumber) -> BlockStatus;
/// Get block total difficulty.
fn block_total_difficulty_at(&self, n: BlockNumber) -> Option<U256>;
/// Get a tree route between `from` and `to`.
/// See `BlockChain::tree_route`.
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute>;
@@ -193,12 +199,12 @@ impl Client {
}
/// This is triggered by a message coming from a block queue when the block is ready for insertion
pub fn import_verified_blocks(&self, _io: &IoChannel<NetSyncMessage>) {
pub fn import_verified_blocks(&self, _io: &IoChannel<NetSyncMessage>) -> usize {
let mut ret = 0;
let mut bad = HashSet::new();
let _import_lock = self.import_lock.lock();
let blocks = self.block_queue.write().unwrap().drain(128);
for block in blocks {
// flushln!("Importing {}...", block.header.hash());
if bad.contains(&block.header.parent_hash) {
self.block_queue.write().unwrap().mark_as_bad(&block.header.hash());
bad.insert(block.header.hash());
@@ -207,20 +213,18 @@ impl Client {
let header = &block.header;
if let Err(e) = verify_block_family(&header, &block.bytes, self.engine.deref().deref(), self.chain.read().unwrap().deref()) {
flushln!("Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
self.block_queue.write().unwrap().mark_as_bad(&header.hash());
bad.insert(block.header.hash());
return;
break;
};
let parent = match self.chain.read().unwrap().block_header(&header.parent_hash) {
Some(p) => p,
None => {
flushln!("Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash);
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash);
self.block_queue.write().unwrap().mark_as_bad(&header.hash());
bad.insert(block.header.hash());
return;
break;
},
};
// build last hashes
@@ -240,18 +244,16 @@ impl Client {
let result = match enact_verified(&block, self.engine.deref().deref(), db, &parent, &last_hashes) {
Ok(b) => b,
Err(e) => {
flushln!("Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
bad.insert(block.header.hash());
self.block_queue.write().unwrap().mark_as_bad(&header.hash());
return;
break;
}
};
if let Err(e) = verify_block_final(&header, result.block().header()) {
flushln!("Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
self.block_queue.write().unwrap().mark_as_bad(&header.hash());
return;
break;
}
self.chain.write().unwrap().insert_block(&block.bytes); //TODO: err here?
@@ -260,12 +262,14 @@ impl Client {
Ok(_) => (),
Err(e) => {
warn!(target: "client", "State DB commit failed: {:?}", e);
return;
break;
}
}
self.report.write().unwrap().accrue_block(&block);
trace!(target: "client", "Imported #{} ({})", header.number(), header.hash());
ret += 1;
}
ret
}
/// Clear cached state overlay
@@ -316,6 +320,10 @@ impl BlockChainClient for Client {
fn block_status(&self, hash: &H256) -> BlockStatus {
if self.chain.read().unwrap().is_known(&hash) { BlockStatus::InChain } else { BlockStatus::Unknown }
}
fn block_total_difficulty(&self, hash: &H256) -> Option<U256> {
self.chain.read().unwrap().block_details(hash).map(|d| d.total_difficulty)
}
fn block_header_at(&self, n: BlockNumber) -> Option<Bytes> {
self.chain.read().unwrap().block_hash(n).and_then(|h| self.block_header(&h))
@@ -336,6 +344,10 @@ impl BlockChainClient for Client {
}
}
fn block_total_difficulty_at(&self, n: BlockNumber) -> Option<U256> {
self.chain.read().unwrap().block_hash(n).and_then(|h| self.block_total_difficulty(&h))
}
fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
self.chain.read().unwrap().tree_route(from.clone(), to.clone())
}

View File

@@ -120,7 +120,9 @@ pub enum BlockError {
/// TODO [arkpar] Please document me
InvalidParentHash(Mismatch<H256>),
/// TODO [arkpar] Please document me
InvalidNumber(OutOfBounds<BlockNumber>),
InvalidNumber(Mismatch<BlockNumber>),
/// Block number isn't sensible.
RidiculousNumber(OutOfBounds<BlockNumber>),
/// TODO [arkpar] Please document me
UnknownParent(H256),
/// TODO [Gav Wood] Please document me
@@ -145,7 +147,7 @@ impl From<Error> for ImportError {
}
/// Result of import block operation.
pub type ImportResult = Result<(), ImportError>;
pub type ImportResult = Result<H256, ImportError>;
#[derive(Debug)]
/// General error type which should be capable of representing all errors in ethcore.

View File

@@ -101,7 +101,7 @@ impl Engine for Ethash {
fn verify_block_basic(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
// check the seal fields.
try!(UntrustedRlp::new(&header.seal[0]).as_val::<H256>());
try!(UntrustedRlp::new(&header.seal[1]).as_val::<u64>());
try!(UntrustedRlp::new(&header.seal[1]).as_val::<H64>());
let min_difficulty = decode(self.spec().engine_params.get("minimumDifficulty").unwrap());
if header.difficulty < min_difficulty {
@@ -109,7 +109,7 @@ impl Engine for Ethash {
}
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(quick_get_difficulty(
&Ethash::to_ethash(header.bare_hash()),
header.nonce(),
header.nonce().low_u64(),
&Ethash::to_ethash(header.mix_hash()))));
if difficulty < header.difficulty {
return Err(From::from(BlockError::InvalidEthashDifficulty(Mismatch { expected: header.difficulty, found: difficulty })));
@@ -118,7 +118,7 @@ impl Engine for Ethash {
}
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
let result = self.pow.compute_light(header.number as u64, &Ethash::to_ethash(header.bare_hash()), header.nonce());
let result = self.pow.compute_light(header.number as u64, &Ethash::to_ethash(header.bare_hash()), header.nonce().low_u64());
let mix = Ethash::from_ethash(result.mix_hash);
let difficulty = Ethash::boundary_to_difficulty(&Ethash::from_ethash(result.value));
if mix != header.mix_hash() {
@@ -208,7 +208,7 @@ impl Ethash {
}
impl Header {
fn nonce(&self) -> u64 {
fn nonce(&self) -> H64 {
decode(&self.seal()[1])
}
fn mix_hash(&self) -> H256 {

View File

@@ -415,7 +415,7 @@ impl ChainSync {
Err(ImportError::AlreadyQueued) => {
trace!(target: "sync", "New block already queued {:?}", h);
},
Ok(()) => {
Ok(_) => {
trace!(target: "sync", "New block queued {:?}", h);
},
Err(e) => {
@@ -680,7 +680,7 @@ impl ChainSync {
self.last_imported_block = headers.0 + i as BlockNumber;
self.last_imported_hash = h.clone();
},
Ok(()) => {
Ok(_) => {
trace!(target: "sync", "Block queued {:?}", h);
self.last_imported_block = headers.0 + i as BlockNumber;
self.last_imported_hash = h.clone();

View File

@@ -51,6 +51,10 @@ impl TestBlockChainClient {
}
impl BlockChainClient for TestBlockChainClient {
fn block_total_difficulty(&self, _h: &H256) -> Option<U256> {
unimplemented!();
}
fn block_header(&self, h: &H256) -> Option<Bytes> {
self.blocks.read().unwrap().get(h).map(|r| Rlp::new(r).at(0).as_raw().to_vec())
@@ -76,6 +80,10 @@ impl BlockChainClient for TestBlockChainClient {
}
}
fn block_total_difficulty_at(&self, _number: BlockNumber) -> Option<U256> {
unimplemented!();
}
fn block_header_at(&self, n: BlockNumber) -> Option<Bytes> {
self.numbers.read().unwrap().get(&(n as usize)).and_then(|h| self.block_header(h))
}
@@ -114,6 +122,7 @@ impl BlockChainClient for TestBlockChainClient {
fn import_block(&self, b: Bytes) -> ImportResult {
let header = Rlp::new(&b).val_at::<BlockHeader>(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);
@@ -134,9 +143,9 @@ impl BlockChainClient for TestBlockChainClient {
let len = self.numbers.read().unwrap().len();
if number == len {
*self.difficulty.write().unwrap().deref_mut() += header.difficulty;
mem::replace(self.last_hash.write().unwrap().deref_mut(), header.hash());
self.blocks.write().unwrap().insert(header.hash(), b);
self.numbers.write().unwrap().insert(number, header.hash());
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;
@@ -148,9 +157,9 @@ impl BlockChainClient for TestBlockChainClient {
}
}
else {
self.blocks.write().unwrap().insert(header.hash(), b.to_vec());
self.blocks.write().unwrap().insert(h.clone(), b.to_vec());
}
Ok(())
Ok(h)
}
fn queue_info(&self) -> BlockQueueInfo {

View File

@@ -21,7 +21,7 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
flush(format!(" - {}...", name));
let blocks: Vec<Bytes> = test["blocks"].as_array().unwrap().iter().map(|e| xjson!(&e["rlp"])).collect();
let blocks: Vec<(Bytes, bool)> = test["blocks"].as_array().unwrap().iter().map(|e| (xjson!(&e["rlp"]), e.find("blockHeader").is_some())).collect();
let mut spec = ethereum::new_frontier_like_test();
let s = PodState::from_json(test.find("pre").unwrap());
spec.set_genesis_state(s);
@@ -32,11 +32,14 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
dir.push(H32::random().hex());
{
let client = Client::new(spec, &dir, IoChannel::disconnected()).unwrap();
for b in blocks.into_iter().filter(|ref b| Block::is_good(b)) {
client.import_block(b).unwrap();
for (b, is_valid) in blocks.into_iter() {
if Block::is_good(&b) {
let _ = client.import_block(b.clone());
}
client.flush_queue();
let imported_ok = client.import_verified_blocks(&IoChannel::disconnected()) > 0;
assert_eq!(imported_ok, is_valid);
}
client.flush_queue();
client.import_verified_blocks(&IoChannel::disconnected());
fail_unless(client.chain_info().best_block_hash == H256::from_json(&test["lastblockhash"]));
}
fs::remove_dir_all(&dir).unwrap();
@@ -51,16 +54,16 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
declare_test!{BlockchainTests_bcBlockGasLimitTest, "BlockchainTests/bcBlockGasLimitTest"}
declare_test!{BlockchainTests_bcForkBlockTest, "BlockchainTests/bcForkBlockTest"}
declare_test!{BlockchainTests_bcForkStressTest, "BlockchainTests/bcForkStressTest"} // STILL FAILS
declare_test!{BlockchainTests_bcForkUncle, "BlockchainTests/bcForkUncle"} // STILL FAILS
declare_test!{BlockchainTests_bcForkStressTest, "BlockchainTests/bcForkStressTest"}
declare_test!{BlockchainTests_bcForkUncle, "BlockchainTests/bcForkUncle"}
declare_test!{BlockchainTests_bcGasPricerTest, "BlockchainTests/bcGasPricerTest"}
declare_test!{BlockchainTests_bcInvalidHeaderTest, "BlockchainTests/bcInvalidHeaderTest"}
declare_test!{BlockchainTests_bcInvalidRLPTest, "BlockchainTests/bcInvalidRLPTest"} // FAILS
declare_test!{BlockchainTests_bcMultiChainTest, "BlockchainTests/bcMultiChainTest"} // FAILS
declare_test!{BlockchainTests_bcInvalidRLPTest, "BlockchainTests/bcInvalidRLPTest"}
declare_test!{BlockchainTests_bcMultiChainTest, "BlockchainTests/bcMultiChainTest"}
declare_test!{BlockchainTests_bcRPC_API_Test, "BlockchainTests/bcRPC_API_Test"}
declare_test!{BlockchainTests_bcStateTest, "BlockchainTests/bcStateTest"}
declare_test!{BlockchainTests_bcTotalDifficultyTest, "BlockchainTests/bcTotalDifficultyTest"}
declare_test!{BlockchainTests_bcUncleHeaderValiditiy, "BlockchainTests/bcUncleHeaderValiditiy"} // FAILS
declare_test!{BlockchainTests_bcUncleTest, "BlockchainTests/bcUncleTest"} // FAILS
declare_test!{BlockchainTests_bcValidBlockTest, "BlockchainTests/bcValidBlockTest"} // FAILS
declare_test!{BlockchainTests_bcWalletTest, "BlockchainTests/bcWalletTest"} // FAILS
declare_test!{BlockchainTests_bcUncleHeaderValiditiy, "BlockchainTests/bcUncleHeaderValiditiy"}
declare_test!{BlockchainTests_bcUncleTest, "BlockchainTests/bcUncleTest"}
declare_test!{BlockchainTests_bcValidBlockTest, "BlockchainTests/bcValidBlockTest"}
declare_test!{BlockchainTests_bcWalletTest, "BlockchainTests/bcWalletTest"}

117
src/tests/client.rs Normal file
View File

@@ -0,0 +1,117 @@
use client::{BlockChainClient,Client};
use super::test_common::*;
use super::helpers::*;
fn get_good_dummy_block() -> Bytes {
let mut block_header = Header::new();
let test_spec = get_test_spec();
let test_engine = test_spec.to_engine().unwrap();
block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
block_header.timestamp = 40;
block_header.number = 1;
block_header.parent_hash = test_engine.spec().genesis_header().hash();
block_header.state_root = test_engine.spec().genesis_header().state_root;
create_test_block(&block_header)
}
fn get_bad_state_dummy_block() -> Bytes {
let mut block_header = Header::new();
let test_spec = get_test_spec();
let test_engine = test_spec.to_engine().unwrap();
block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
block_header.timestamp = 40;
block_header.number = 1;
block_header.parent_hash = test_engine.spec().genesis_header().hash();
block_header.state_root = x!(0xbad);
create_test_block(&block_header)
}
fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> Arc<Client> {
let dir = RandomTempPath::new();
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
for block in &blocks {
if let Err(_) = client.import_block(block.clone()) {
panic!("panic importing block which is well-formed");
}
}
client.flush_queue();
client.import_verified_blocks(&IoChannel::disconnected());
client
}
#[test]
fn created() {
let dir = RandomTempPath::new();
let client_result = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected());
assert!(client_result.is_ok());
}
#[test]
fn imports_from_empty() {
let dir = RandomTempPath::new();
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
client.import_verified_blocks(&IoChannel::disconnected());
client.flush_queue();
}
#[test]
fn imports_good_block() {
let dir = RandomTempPath::new();
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
let good_block = get_good_dummy_block();
if let Err(_) = client.import_block(good_block) {
panic!("error importing block being good by definition");
}
client.flush_queue();
client.import_verified_blocks(&IoChannel::disconnected());
let block = client.block_header_at(1).unwrap();
assert!(!block.is_empty());
}
#[test]
fn query_none_block() {
let dir = RandomTempPath::new();
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
let non_existant = client.block_header_at(188);
assert!(non_existant.is_none());
}
#[test]
fn query_bad_block() {
let client = get_test_client_with_blocks(vec![get_bad_state_dummy_block()]);
let bad_block:Option<Bytes> = client.block_header_at(1);
assert!(bad_block.is_none());
}
#[test]
fn returns_chain_info() {
let dummy_block = get_good_dummy_block();
let client = get_test_client_with_blocks(vec![dummy_block.clone()]);
let block = BlockView::new(&dummy_block);
let info = client.chain_info();
assert_eq!(info.best_block_hash, block.header().hash());
}
#[test]
fn imports_block_sequence() {
let client = generate_dummy_client(6);
let block = client.block_header_at(5).unwrap();
assert!(!block.is_empty());
}
#[test]
fn can_collect_garbage() {
let client = generate_dummy_client(100);
client.tick();
assert!(client.cache_info().blocks < 100 * 1024);
}

80
src/tests/helpers.rs Normal file
View File

@@ -0,0 +1,80 @@
use client::{BlockChainClient,Client};
use std::env;
use super::test_common::*;
use std::path::PathBuf;
use spec::*;
use std::fs::{remove_dir_all};
pub struct RandomTempPath {
path: PathBuf
}
impl RandomTempPath {
pub fn new() -> RandomTempPath {
let mut dir = env::temp_dir();
dir.push(H32::random().hex());
RandomTempPath {
path: dir.clone()
}
}
pub fn as_path(&self) -> &PathBuf {
&self.path
}
}
impl Drop for RandomTempPath {
fn drop(&mut self) {
if let Err(e) = remove_dir_all(self.as_path()) {
panic!("failed to remove temp directory, probably something failed to destroyed ({})", e);
}
}
}
pub fn get_test_spec() -> Spec {
Spec::new_test()
}
pub fn create_test_block(header: &Header) -> Bytes {
let mut rlp = RlpStream::new_list(3);
rlp.append(header);
rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1);
rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1);
rlp.out()
}
pub fn generate_dummy_client(block_number: usize) -> Arc<Client> {
let dir = RandomTempPath::new();
let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap();
let test_spec = get_test_spec();
let test_engine = test_spec.to_engine().unwrap();
let state_root = test_engine.spec().genesis_header().state_root;
let mut rolling_hash = test_engine.spec().genesis_header().hash();
let mut rolling_block_number = 1;
let mut rolling_timestamp = 40;
for _ in 0..block_number {
let mut header = Header::new();
header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap());
header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap());
header.timestamp = rolling_timestamp;
header.number = rolling_block_number;
header.parent_hash = rolling_hash;
header.state_root = state_root.clone();
rolling_hash = header.hash();
rolling_block_number = rolling_block_number + 1;
rolling_timestamp = rolling_timestamp + 10;
if let Err(_) = client.import_block(create_test_block(&header)) {
panic!("error importing block which is valid by definition");
}
}
client.flush_queue();
client.import_verified_blocks(&IoChannel::disconnected());
client
}

View File

@@ -4,4 +4,6 @@ mod test_common;
mod transaction;
mod executive;
mod state;
mod client;
mod chain;
mod helpers;

View File

@@ -162,7 +162,7 @@ pub fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error>
/// Check basic header parameters.
fn verify_header(header: &Header, engine: &Engine) -> Result<(), Error> {
if header.number >= From::from(BlockNumber::max_value()) {
return Err(From::from(BlockError::InvalidNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number })))
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number })))
}
if header.gas_used > header.gas_limit {
return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: Some(header.gas_limit), min: None, found: header.gas_used })));
@@ -186,8 +186,8 @@ fn verify_parent(header: &Header, parent: &Header) -> Result<(), Error> {
if header.timestamp <= parent.timestamp {
return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp + 1), found: header.timestamp })))
}
if header.number <= parent.number {
return Err(From::from(BlockError::InvalidNumber(OutOfBounds { max: None, min: Some(parent.number + 1), found: header.number })));
if header.number != parent.number + 1 {
return Err(From::from(BlockError::InvalidNumber(Mismatch { expected: parent.number + 1, found: header.number })));
}
Ok(())
}
@@ -400,7 +400,7 @@ mod tests {
header = good.clone();
header.number = BlockNumber::max_value();
check_fail(basic_test(&create_test_block(&header), engine.deref()),
InvalidNumber(OutOfBounds { max: Some(BlockNumber::max_value()), min: None, found: header.number }));
RidiculousNumber(OutOfBounds { max: Some(BlockNumber::max_value()), min: None, found: header.number }));
header = good.clone();
header.gas_used = header.gas_limit + From::from(1);
@@ -443,7 +443,7 @@ mod tests {
header = good.clone();
header.number = 9;
check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &good_uncles), engine.deref(), &bc),
InvalidNumber(OutOfBounds { max: None, min: Some(parent.number + 1), found: header.number }));
InvalidNumber(Mismatch { expected: parent.number + 1, found: header.number }));
header = good.clone();
let mut bad_uncles = good_uncles.clone();