Merge branch 'master' into journaldb_commit

This commit is contained in:
Robert Habermeier
2016-10-13 12:59:32 +02:00
198 changed files with 6496 additions and 2398 deletions

View File

@@ -18,7 +18,7 @@ use ipc::IpcConfig;
use util::H256;
/// Represents what has to be handled by actor listening to chain events
#[derive(Ipc)]
#[ipc]
pub trait ChainNotify : Send + Sync {
/// fires when chain has new blocks.
fn new_blocks(&self,

View File

@@ -23,15 +23,14 @@ use time::precise_time_ns;
// util
use util::{Bytes, PerfTimer, Itertools, Mutex, RwLock};
use util::journaldb::{self, JournalDB};
use util::{U256, H256, Address, H2048, Uint};
use util::sha3::*;
use util::TrieFactory;
use util::{journaldb, TrieFactory, Trie};
use util::trie::TrieSpec;
use util::{U256, H256, Address, H2048, Uint, FixedHash};
use util::kvdb::*;
// other
use io::*;
use views::{BlockView, HeaderView, BodyView};
use views::{HeaderView, BodyView};
use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError};
use header::BlockNumber;
use state::State;
@@ -47,12 +46,12 @@ use transaction::{LocalizedTransaction, SignedTransaction, Action};
use blockchain::extras::TransactionAddress;
use types::filter::Filter;
use log_entry::LocalizedLogEntry;
use block_queue::{BlockQueue, BlockQueueInfo};
use verification::queue::{BlockQueue, QueueInfo as BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
use client::{
BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient,
MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode,
ChainNotify
ChainNotify,
};
use client::Error as ClientError;
use env_info::EnvInfo;
@@ -66,7 +65,7 @@ use miner::{Miner, MinerService};
use snapshot::{self, io as snapshot_io};
use factory::Factories;
use rlp::{View, UntrustedRlp};
use state_db::StateDB;
// re-export
pub use types::blockchain_info::BlockChainInfo;
@@ -126,9 +125,9 @@ pub struct Client {
tracedb: RwLock<TraceDB<BlockChain>>,
engine: Arc<Engine>,
config: ClientConfig,
db: RwLock<Arc<Database>>,
pruning: journaldb::Algorithm,
state_db: RwLock<Box<JournalDB>>,
db: RwLock<Arc<Database>>,
state_db: Mutex<StateDB>,
block_queue: BlockQueue,
report: RwLock<ClientReport>,
import_lock: Mutex<()>,
@@ -148,13 +147,6 @@ pub struct Client {
/// assume finality of a given candidate.
pub const HISTORY: u64 = 1200;
/// Append a path element to the given path and return the string.
pub fn append_path<P>(path: P, item: &str) -> String where P: AsRef<Path> {
let mut p = path.as_ref().to_path_buf();
p.push(item);
p.to_str().unwrap().to_owned()
}
impl Client {
/// Create a new client with given spec and DB path and custom verifier.
pub fn new(
@@ -168,18 +160,24 @@ impl Client {
let path = path.to_path_buf();
let gb = spec.genesis_block();
let db = Arc::new(try!(Database::open(&db_config, &path.to_str().unwrap()).map_err(ClientError::Database)));
let db = Arc::new(try!(Database::open(&db_config, &path.to_str().expect("DB path could not be converted to string.")).map_err(ClientError::Database)));
let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone()));
let tracedb = RwLock::new(try!(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone())));
let tracedb = RwLock::new(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone()));
let mut state_db = journaldb::new(db.clone(), config.pruning, ::db::COL_STATE);
if state_db.is_empty() && try!(spec.ensure_db_good(state_db.as_hashdb_mut())) {
let trie_spec = match config.fat_db {
true => TrieSpec::Fat,
false => TrieSpec::Secure,
};
let journal_db = journaldb::new(db.clone(), config.pruning, ::db::COL_STATE);
let mut state_db = StateDB::new(journal_db, config.state_cache_size);
if state_db.journal_db().is_empty() && try!(spec.ensure_db_good(&mut state_db)) {
let mut batch = DBTransaction::new(&db);
try!(state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash()));
try!(db.write(batch).map_err(ClientError::Database));
}
if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.contains(h.state_root())) {
if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.journal_db().contains(h.state_root())) {
warn!("State root not found for block #{} ({})", chain.best_block_number(), chain.best_block_hash().hex());
}
@@ -192,8 +190,8 @@ impl Client {
let awake = match config.mode { Mode::Dark(..) => false, _ => true };
let factories = Factories {
vm: EvmFactory::new(config.vm_type.clone()),
trie: TrieFactory::new(config.trie_spec.clone()),
vm: EvmFactory::new(config.vm_type.clone(), config.jump_table_size),
trie: TrieFactory::new(trie_spec),
accountdb: Default::default(),
};
@@ -208,7 +206,7 @@ impl Client {
verifier: verification::new(config.verifier_type.clone()),
config: config,
db: RwLock::new(db),
state_db: RwLock::new(state_db),
state_db: Mutex::new(state_db),
block_queue: block_queue,
report: RwLock::new(Default::default()),
import_lock: Mutex::new(()),
@@ -291,30 +289,27 @@ impl Client {
// Check if Parent is in chain
let chain_has_parent = chain.block_header(header.parent_hash());
if let None = chain_has_parent {
if let Some(parent) = chain_has_parent {
// Enact Verified Block
let last_hashes = self.build_last_hashes(header.parent_hash().clone());
let db = self.state_db.lock().boxed_clone_canon(&header.parent_hash());
let enact_result = enact_verified(block, engine, self.tracedb.read().tracing_enabled(), db, &parent, last_hashes, self.factories.clone());
let locked_block = try!(enact_result.map_err(|e| {
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
}));
// Final Verification
if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) {
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(());
}
Ok(locked_block)
} else {
warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash());
return Err(());
};
// Enact Verified Block
let parent = chain_has_parent.unwrap();
let last_hashes = self.build_last_hashes(header.parent_hash().clone());
let db = self.state_db.read().boxed_clone();
let enact_result = enact_verified(block, engine, self.tracedb.read().tracing_enabled(), db, &parent, last_hashes, self.factories.clone());
if let Err(e) = enact_result {
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(());
};
// Final Verification
let locked_block = enact_result.unwrap();
if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) {
warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(());
Err(())
}
Ok(locked_block)
}
fn calculate_enacted_retracted(&self, import_results: &[ImportRoute]) -> (Vec<H256>, Vec<H256>) {
@@ -358,23 +353,21 @@ impl Client {
for block in blocks {
let header = &block.header;
if invalid_blocks.contains(header.parent_hash()) {
let is_invalid = invalid_blocks.contains(header.parent_hash());
if is_invalid {
invalid_blocks.insert(header.hash());
continue;
}
let closed_block = self.check_and_close_block(&block);
if let Err(_) = closed_block {
if let Ok(closed_block) = self.check_and_close_block(&block) {
imported_blocks.push(header.hash());
let route = self.commit_block(closed_block, &header.hash(), &block.bytes);
import_results.push(route);
self.report.write().accrue_block(&block);
} else {
invalid_blocks.insert(header.hash());
continue;
}
let closed_block = closed_block.unwrap();
imported_blocks.push(header.hash());
let route = self.commit_block(closed_block, &header.hash(), &block.bytes);
import_results.push(route);
self.report.write().accrue_block(&block);
}
let imported = imported_blocks.len();
@@ -435,13 +428,13 @@ impl Client {
// CHECK! I *think* this is fine, even if the state_root is equal to another
// already-imported block of the same number.
// TODO: Prove it with a test.
let mut db = block.drain();
let mut state = block.drain();
db.journal_under(&mut batch, number, hash).expect("DB commit failed");
state.journal_under(&mut batch, number, hash).expect("DB commit failed");
if number >= HISTORY {
let n = number - HISTORY;
db.mark_canonical(&mut batch, n, &chain.block_hash(n).unwrap()).expect("DB commit failed");
state.mark_canonical(&mut batch, n, &chain.block_hash(n).unwrap()).expect("DB commit failed");
}
let route = chain.insert_block(&mut batch, block_data, receipts);
@@ -452,10 +445,12 @@ impl Client {
enacted: route.enacted.clone(),
retracted: route.retracted.len()
});
let is_canon = route.enacted.last().map_or(false, |h| h == hash);
state.sync_cache(&route.enacted, &route.retracted, is_canon);
// Final commit to the DB
self.db.read().write_buffered(batch);
chain.commit();
self.update_last_hashes(&parent, hash);
route
}
@@ -497,7 +492,7 @@ impl Client {
};
self.block_header(id).and_then(|header| {
let db = self.state_db.read().boxed_clone();
let db = self.state_db.lock().boxed_clone();
// early exit for pruned blocks
if db.is_pruned() && self.chain.read().best_block_number() >= block_number + HISTORY {
@@ -527,9 +522,11 @@ impl Client {
/// Get a copy of the best block's state.
pub fn state(&self) -> State {
let header = self.best_block_header();
let header = HeaderView::new(&header);
State::from_existing(
self.state_db.read().boxed_clone(),
HeaderView::new(&self.best_block_header()).state_root(),
self.state_db.lock().boxed_clone_canon(&header.hash()),
header.state_root(),
self.engine.account_start_nonce(),
self.factories.clone())
.expect("State root of best block header always valid.")
@@ -543,7 +540,7 @@ impl Client {
/// Get the report.
pub fn report(&self) -> ClientReport {
let mut report = self.report.read().clone();
report.state_db_mem = self.state_db.read().mem_used();
report.state_db_mem = self.state_db.lock().mem_used();
report
}
@@ -599,7 +596,7 @@ impl Client {
/// Take a snapshot at the given block.
/// If the ID given is "latest", this will default to 1000 blocks behind.
pub fn take_snapshot<W: snapshot_io::SnapshotWriter + Send>(&self, writer: W, at: BlockID, p: &snapshot::Progress) -> Result<(), EthcoreError> {
let db = self.state_db.read().boxed_clone();
let db = self.state_db.lock().journal_db().boxed_clone();
let best_block_number = self.chain_info().best_block_number;
let block_number = try!(self.block_number(at).ok_or(snapshot::Error::InvalidStartingBlock(at)));
@@ -678,16 +675,17 @@ impl snapshot::DatabaseRestore for Client {
trace!(target: "snapshot", "Replacing client database with {:?}", new_db);
let _import_lock = self.import_lock.lock();
let mut state_db = self.state_db.write();
let mut state_db = self.state_db.lock();
let mut chain = self.chain.write();
let mut tracedb = self.tracedb.write();
self.miner.clear();
let db = self.db.write();
try!(db.restore(new_db));
*state_db = journaldb::new(db.clone(), self.pruning, ::db::COL_STATE);
let cache_size = state_db.cache_size();
*state_db = StateDB::new(journaldb::new(db.clone(), self.pruning, ::db::COL_STATE), cache_size);
*chain = Arc::new(BlockChain::new(self.config.blockchain.clone(), &[], db.clone()));
*tracedb = try!(TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone()).map_err(ClientError::from));
*tracedb = TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone());
Ok(())
}
}
@@ -805,7 +803,7 @@ impl BlockChainClient for Client {
let chain = self.chain.read();
match Self::block_hash(&chain, id) {
Some(ref hash) if chain.is_known(hash) => BlockStatus::InChain,
Some(hash) => self.block_queue.block_status(&hash),
Some(hash) => self.block_queue.status(&hash).into(),
None => BlockStatus::Unknown
}
}
@@ -830,7 +828,7 @@ impl BlockChainClient for Client {
}
fn code(&self, address: &Address, id: BlockID) -> Option<Option<Bytes>> {
self.state_at(id).map(|s| s.code(address))
self.state_at(id).map(|s| s.code(address).map(|c| (*c).clone()))
}
fn balance(&self, address: &Address, id: BlockID) -> Option<U256> {
@@ -841,6 +839,38 @@ impl BlockChainClient for Client {
self.state_at(id).map(|s| s.storage_at(address, position))
}
fn list_accounts(&self, id: BlockID) -> Option<Vec<Address>> {
if !self.factories.trie.is_fat() {
trace!(target: "fatdb", "list_accounts: Not a fat DB");
return None;
}
let state = match self.state_at(id) {
Some(state) => state,
_ => return None,
};
let (root, db) = state.drop();
let trie = match self.factories.trie.readonly(db.as_hashdb(), &root) {
Ok(trie) => trie,
_ => {
trace!(target: "fatdb", "list_accounts: Couldn't open the DB");
return None;
}
};
let iter = match trie.iter() {
Ok(iter) => iter,
_ => return None,
};
let accounts = iter.filter_map(|item| {
item.ok().map(|(addr, _)| Address::from_slice(&addr))
}).collect();
Some(accounts)
}
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction> {
self.transaction_address(id).and_then(|address| self.chain.read().transaction(&address))
}
@@ -852,12 +882,17 @@ impl BlockChainClient for Client {
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt> {
let chain = self.chain.read();
self.transaction_address(id).and_then(|address| chain.block_number(&address.block_hash).and_then(|block_number| {
self.transaction_address(id)
.and_then(|address| chain.block_number(&address.block_hash).and_then(|block_number| {
let t = chain.block_body(&address.block_hash)
.and_then(|block| BodyView::new(&block).localized_transaction_at(&address.block_hash, block_number, address.index));
.and_then(|block| {
BodyView::new(&block).localized_transaction_at(&address.block_hash, block_number, address.index)
});
match (t, chain.transaction_receipt(&address)) {
(Some(tx), Some(receipt)) => {
let tx_and_sender = t.and_then(|tx| tx.sender().ok().map(|sender| (tx, sender)));
match (tx_and_sender, chain.transaction_receipt(&address)) {
(Some((tx, sender)), Some(receipt)) => {
let block_hash = tx.block_hash.clone();
let block_number = tx.block_number.clone();
let transaction_hash = tx.hash();
@@ -879,7 +914,7 @@ impl BlockChainClient for Client {
gas_used: receipt.gas_used - prior_gas_used,
contract_address: match tx.action {
Action::Call(_) => None,
Action::Create => Some(contract_address(&tx.sender().unwrap(), &tx.nonce))
Action::Create => Some(contract_address(&sender, &tx.nonce))
},
logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry {
entry: log,
@@ -909,7 +944,7 @@ impl BlockChainClient for Client {
}
fn state_data(&self, hash: &H256) -> Option<Bytes> {
self.state_db.read().state(hash)
self.state_db.lock().journal_db().state(hash)
}
fn block_receipts(&self, hash: &H256) -> Option<Bytes> {
@@ -917,16 +952,21 @@ impl BlockChainClient for Client {
}
fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError> {
use verification::queue::kind::HasHash;
use verification::queue::kind::blocks::Unverified;
// create unverified block here so the `sha3` calculation can be cached.
let unverified = Unverified::new(bytes);
{
let header = BlockView::new(&bytes).header_view();
if self.chain.read().is_known(&header.sha3()) {
if self.chain.read().is_known(&unverified.hash()) {
return Err(BlockImportError::Import(ImportError::AlreadyInChain));
}
if self.block_status(BlockID::Hash(header.parent_hash())) == BlockStatus::Unknown {
return Err(BlockImportError::Block(BlockError::UnknownParent(header.parent_hash())));
if self.block_status(BlockID::Hash(unverified.parent_hash())) == BlockStatus::Unknown {
return Err(BlockImportError::Block(BlockError::UnknownParent(unverified.parent_hash())));
}
}
Ok(try!(self.block_queue.import_block(bytes)))
Ok(try!(self.block_queue.import(unverified)))
}
fn queue_info(&self) -> BlockQueueInfo {
@@ -975,17 +1015,18 @@ impl BlockChainClient for Client {
let start = self.block_number(filter.range.start);
let end = self.block_number(filter.range.end);
if start.is_some() && end.is_some() {
let filter = trace::Filter {
range: start.unwrap() as usize..end.unwrap() as usize,
from_address: From::from(filter.from_address),
to_address: From::from(filter.to_address),
};
match (start, end) {
(Some(s), Some(e)) => {
let filter = trace::Filter {
range: s as usize..e as usize,
from_address: From::from(filter.from_address),
to_address: From::from(filter.to_address),
};
let traces = self.tracedb.read().filter(&filter);
Some(traces)
} else {
None
let traces = self.tracedb.read().filter(&filter);
Some(traces)
},
_ => None,
}
}
@@ -1032,7 +1073,7 @@ impl BlockChainClient for Client {
}
fn pending_transactions(&self) -> Vec<SignedTransaction> {
self.miner.pending_transactions()
self.miner.pending_transactions(self.chain.read().best_block_number())
}
}
@@ -1046,7 +1087,7 @@ impl MiningBlockChainClient for Client {
engine,
self.factories.clone(),
false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
self.state_db.read().boxed_clone(),
self.state_db.lock().boxed_clone_canon(&h),
&chain.block_header(&h).expect("h is best block hash: so its header must exist: qed"),
self.build_last_hashes(h.clone()),
author,
@@ -1057,11 +1098,15 @@ impl MiningBlockChainClient for Client {
// Add uncles
chain
.find_uncle_headers(&h, engine.maximum_uncle_age())
.unwrap()
.unwrap_or_else(Vec::new)
.into_iter()
.take(engine.maximum_uncle_count())
.foreach(|h| {
open_block.push_uncle(h).unwrap();
open_block.push_uncle(h).expect("pushing maximum_uncle_count;
open_block was just created;
push_uncle is not ok only if more than maximum_uncle_count is pushed;
so all push_uncle are Ok;
qed");
});
open_block
@@ -1082,6 +1127,7 @@ impl MiningBlockChainClient for Client {
let block_data = block.rlp_bytes();
let route = self.commit_block(block, &h, &block_data);
trace!(target: "client", "Imported sealed block #{} ({})", number, h);
self.state_db.lock().sync_cache(&route.enacted, &route.retracted, false);
let (enacted, retracted) = self.calculate_enacted_retracted(&[route]);
self.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted);

View File

@@ -16,13 +16,12 @@
use std::str::FromStr;
pub use std::time::Duration;
pub use block_queue::BlockQueueConfig;
pub use blockchain::Config as BlockChainConfig;
pub use trace::{Config as TraceConfig, Switch};
pub use trace::Config as TraceConfig;
pub use evm::VMType;
pub use verification::VerifierType;
use verification::{VerifierType, QueueConfig};
use util::{journaldb, CompactionProfile};
use util::trie::TrieSpec;
/// Client state db compaction profile
#[derive(Debug, PartialEq)]
@@ -84,29 +83,33 @@ impl Default for Mode {
#[derive(Debug, PartialEq, Default)]
pub struct ClientConfig {
/// Block queue configuration.
pub queue: BlockQueueConfig,
pub queue: QueueConfig,
/// Blockchain configuration.
pub blockchain: BlockChainConfig,
/// Trace configuration.
pub tracing: TraceConfig,
/// VM type.
pub vm_type: VMType,
/// Trie type.
pub trie_spec: TrieSpec,
/// Fat DB enabled?
pub fat_db: bool,
/// The JournalDB ("pruning") algorithm to use.
pub pruning: journaldb::Algorithm,
/// The name of the client instance.
pub name: String,
/// State db cache-size if not default
/// RocksDB state column cache-size if not default
pub db_cache_size: Option<usize>,
/// State db compaction profile
pub db_compaction: DatabaseCompactionProfile,
/// Should db have WAL enabled?
pub db_wal: bool,
pub db_wal: bool,
/// Operating mode
pub mode: Mode,
/// Type of block verifier used by client.
pub verifier_type: VerifierType,
/// State db cache-size.
pub state_cache_size: usize,
/// EVM jump-tables cache size.
pub jump_table_size: usize,
}
#[cfg(test)]

View File

@@ -23,20 +23,27 @@ mod trace;
mod client;
pub use self::client::*;
pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, Switch, VMType};
pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChainConfig, VMType};
pub use self::error::Error;
pub use types::ids::*;
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
pub use types::trace_filter::Filter as TraceFilter;
pub use executive::{Executed, Executive, TransactOptions};
pub use env_info::{LastHashes, EnvInfo};
pub use self::chain_notify::{ChainNotify, ChainNotifyClient};
pub use self::chain_notify::ChainNotify;
pub use types::call_analytics::CallAnalytics;
pub use block_import_error::BlockImportError;
pub use transaction_import::TransactionImportResult;
pub use transaction_import::TransactionImportError;
pub use self::traits::{BlockChainClient, MiningBlockChainClient, RemoteClient};
pub use self::traits::{BlockChainClient, MiningBlockChainClient};
/// IPC interfaces
#[cfg(feature="ipc")]
pub mod remote {
pub use super::traits::RemoteClient;
pub use super::chain_notify::ChainNotifyClient;
}
mod traits {
#![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues

View File

@@ -25,8 +25,9 @@ use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action};
use blockchain::TreeRoute;
use client::{
BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID,
TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError
TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError,
};
use db::{NUM_COLUMNS, COL_STATE};
use header::{Header as BlockHeader, BlockNumber};
use filter::Filter;
use log_entry::LocalizedLogEntry;
@@ -37,11 +38,12 @@ use evm::{Factory as EvmFactory, VMType};
use miner::{Miner, MinerService, TransactionImportResult};
use spec::Spec;
use block_queue::BlockQueueInfo;
use verification::queue::QueueInfo;
use block::{OpenBlock, SealedBlock};
use executive::Executed;
use error::CallError;
use trace::LocalizedTrace;
use state_db::StateDB;
/// Test client.
pub struct TestBlockChainClient {
@@ -53,6 +55,8 @@ pub struct TestBlockChainClient {
pub genesis_hash: H256,
/// Last block hash.
pub last_hash: RwLock<H256>,
/// Extra data do set for each block
pub extra_data: Bytes,
/// Difficulty.
pub difficulty: RwLock<U256>,
/// Balances.
@@ -103,11 +107,17 @@ impl Default for TestBlockChainClient {
impl TestBlockChainClient {
/// Creates new test client.
pub fn new() -> Self {
Self::new_with_extra_data(Bytes::new())
}
/// Creates new test client with specified extra data for each block
pub fn new_with_extra_data(extra_data: Bytes) -> Self {
let spec = Spec::new_test();
let mut client = TestBlockChainClient {
blocks: RwLock::new(HashMap::new()),
numbers: RwLock::new(HashMap::new()),
genesis_hash: H256::new(),
extra_data: extra_data,
last_hash: RwLock::new(H256::new()),
difficulty: RwLock::new(From::from(0)),
balances: RwLock::new(HashMap::new()),
@@ -120,14 +130,14 @@ impl TestBlockChainClient {
queue_size: AtomicUsize::new(0),
miner: Arc::new(Miner::with_spec(&spec)),
spec: spec,
vm_factory: EvmFactory::new(VMType::Interpreter),
vm_factory: EvmFactory::new(VMType::Interpreter, 1024 * 1024),
latest_block_timestamp: RwLock::new(10_000_000),
};
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
client.genesis_hash = client.last_hash.read().clone();
client
}
/// Set the transaction receipt result
pub fn set_transaction_receipt(&self, id: TransactionID, receipt: LocalizedReceipt) {
self.receipts.write().insert(id, receipt);
@@ -182,6 +192,7 @@ impl TestBlockChainClient {
header.set_parent_hash(self.last_hash.read().clone());
header.set_number(n as BlockNumber);
header.set_gas_limit(U256::from(1_000_000));
header.set_extra_data(self.extra_data.clone());
let uncles = match with {
EachBlockWith::Uncle | EachBlockWith::UncleAndTransaction => {
let mut uncles = RlpStream::new_list(1);
@@ -283,13 +294,14 @@ impl TestBlockChainClient {
}
}
pub fn get_temp_journal_db() -> GuardedTempResult<Box<JournalDB>> {
pub fn get_temp_state_db() -> GuardedTempResult<StateDB> {
let temp = RandomTempPath::new();
let db = Database::open_default(temp.as_str()).unwrap();
let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::EarlyMerge, None);
let db = Database::open(&DatabaseConfig::with_columns(NUM_COLUMNS), temp.as_str()).unwrap();
let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::EarlyMerge, COL_STATE);
let state_db = StateDB::new(journal_db, 1024 * 1024);
GuardedTempResult {
_temp: temp,
result: Some(journal_db)
result: Some(state_db)
}
}
@@ -297,9 +309,9 @@ impl MiningBlockChainClient for TestBlockChainClient {
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
let engine = &*self.spec.engine;
let genesis_header = self.spec.genesis_header();
let mut db_result = get_temp_journal_db();
let mut db_result = get_temp_state_db();
let mut db = db_result.take();
self.spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
self.spec.ensure_db_good(&mut db).unwrap();
let last_hashes = vec![genesis_header.hash()];
let mut open_block = OpenBlock::new(
@@ -382,12 +394,16 @@ impl BlockChainClient for TestBlockChainClient {
}
}
fn list_accounts(&self, _id: BlockID) -> Option<Vec<Address>> {
None
}
fn transaction(&self, _id: TransactionID) -> Option<LocalizedTransaction> {
unimplemented!();
None // Simple default.
}
fn uncle(&self, _id: UncleID) -> Option<Bytes> {
unimplemented!();
None // Simple default.
}
fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt> {
@@ -544,8 +560,8 @@ impl BlockChainClient for TestBlockChainClient {
Ok(h)
}
fn queue_info(&self) -> BlockQueueInfo {
BlockQueueInfo {
fn queue_info(&self) -> QueueInfo {
QueueInfo {
verified_queue_size: self.queue_size.load(AtomicOrder::Relaxed),
unverified_queue_size: 0,
verifying_queue_size: 0,
@@ -595,6 +611,6 @@ impl BlockChainClient for TestBlockChainClient {
}
fn pending_transactions(&self) -> Vec<SignedTransaction> {
self.miner.pending_transactions()
self.miner.pending_transactions(self.chain_info().best_block_number)
}
}

View File

@@ -17,7 +17,7 @@
use std::collections::BTreeMap;
use util::{U256, Address, H256, H2048, Bytes, Itertools};
use blockchain::TreeRoute;
use block_queue::BlockQueueInfo;
use verification::queue::QueueInfo as BlockQueueInfo;
use block::{OpenBlock, SealedBlock};
use header::{BlockNumber};
use transaction::{LocalizedTransaction, SignedTransaction};
@@ -38,7 +38,6 @@ use ipc::IpcConfig;
use types::blockchain_info::BlockChainInfo;
use types::block_status::BlockStatus;
#[derive(Ipc)]
#[ipc(client_ident="RemoteClient")]
/// Blockchain database client. Owns and manages a blockchain and a block queue.
pub trait BlockChainClient : Sync + Send {
@@ -112,6 +111,9 @@ pub trait BlockChainClient : Sync + Send {
Therefore storage_at has returned Some; qed")
}
/// Get a list of all accounts in the block `id`, if fat DB is in operation, otherwise `None`.
fn list_accounts(&self, id: BlockID) -> Option<Vec<Address>>;
/// Get transaction with given hash.
fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction>;