client.rs 40.4 KiB
Newer Older
// 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 <http://www.gnu.org/licenses/>.
Gav Wood's avatar
Gav Wood committed
use std::collections::{HashSet, HashMap, BTreeMap, VecDeque};
Marek Kotewicz's avatar
Marek Kotewicz committed
use std::path::{Path};
use std::fmt;
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
use std::time::{Instant};
Gav Wood's avatar
Gav Wood committed
use time::precise_time_ns;
asynchronous rob's avatar
asynchronous rob committed
use util::{Bytes, PerfTimer, Itertools, Mutex, RwLock};
use util::{journaldb, TrieFactory, Trie};
use util::trie::TrieSpec;
use util::{U256, H256, Address, H2048, Uint, FixedHash};
use util::kvdb::*;

// other
use views::{HeaderView, BodyView, BlockView};
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult, Error as EthcoreError};
use header::BlockNumber;
use spec::Spec;
use basic_types::Seal;
use engines::Engine;
use service::ClientIoMessage;
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
use env_info::LastHashes;
use verification;
use verification::{PreverifiedBlock, Verifier};
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
use block::*;
Marek Kotewicz's avatar
Marek Kotewicz committed
use transaction::{LocalizedTransaction, SignedTransaction, Action};
Marek Kotewicz's avatar
Marek Kotewicz committed
use blockchain::extras::TransactionAddress;
use types::filter::Filter;
use log_entry::LocalizedLogEntry;
use verification::queue::{BlockQueue, QueueInfo as BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
Gav Wood's avatar
Gav Wood committed
use client::{
	BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient,
	MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode,
Gav Wood's avatar
Gav Wood committed
};
use client::Error as ClientError;
Marek Kotewicz's avatar
Marek Kotewicz committed
use env_info::EnvInfo;
use executive::{Executive, Executed, TransactOptions, contract_address};
use receipt::LocalizedReceipt;
Tomasz Drwięga's avatar
Tomasz Drwięga committed
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
use trace;
use trace::FlatTransactionTraces;
use evm::Factory as EvmFactory;
use snapshot::{self, io as snapshot_io};
asynchronous rob's avatar
asynchronous rob committed
use rlp::{View, UntrustedRlp};
use state_db::StateDB;

// re-export
pub use types::blockchain_info::BlockChainInfo;
pub use types::block_status::BlockStatus;
pub use blockchain::CacheSize as BlockChainCacheSize;
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
const MAX_TX_QUEUE_SIZE: usize = 4096;
const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2;
const MIN_HISTORY_SIZE: u64 = 8;
Gav Wood's avatar
Gav Wood committed
impl fmt::Display for BlockChainInfo {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		write!(f, "#{}.{}", self.best_block_number, self.best_block_hash)
	}
}

Gav Wood's avatar
Gav Wood committed
/// Report on the status of a client.
#[derive(Default, Clone, Debug, Eq, PartialEq)]
Gav Wood's avatar
Gav Wood committed
pub struct ClientReport {
Gav Wood's avatar
Gav Wood committed
	/// How many blocks have been imported so far.
Gav Wood's avatar
Gav Wood committed
	pub blocks_imported: usize,
Gav Wood's avatar
Gav Wood committed
	/// How many transactions have been applied so far.
Gav Wood's avatar
Gav Wood committed
	pub transactions_applied: usize,
Gav Wood's avatar
Gav Wood committed
	/// How much gas has been processed so far.
Gav Wood's avatar
Gav Wood committed
	pub gas_processed: U256,
Gav Wood's avatar
Gav Wood committed
	/// Memory used by state DB
	pub state_db_mem: usize,
Gav Wood's avatar
Gav Wood committed
}

impl ClientReport {
Gav Wood's avatar
Gav Wood committed
	/// Alter internal reporting to reflect the additional `block` has been processed.
	pub fn accrue_block(&mut self, block: &PreverifiedBlock) {
Gav Wood's avatar
Gav Wood committed
		self.blocks_imported += 1;
		self.transactions_applied += block.transactions.len();
		self.gas_processed = self.gas_processed + block.header.gas_used().clone();
struct SleepState {
	last_activity: Option<Instant>,
	last_autosleep: Option<Instant>,
}

impl SleepState {
	fn new(awake: bool) -> Self {
		SleepState {
			last_activity: match awake { false => None, true => Some(Instant::now()) },
			last_autosleep: match awake { false => Some(Instant::now()), true => None },
		}
	}
}

Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
/// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue.
/// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue.
	mode: Mode,
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	chain: RwLock<Arc<BlockChain>>,
	tracedb: RwLock<TraceDB<BlockChain>>,
	engine: Arc<Engine>,
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	config: ClientConfig,
	pruning: journaldb::Algorithm,
	db: RwLock<Arc<Database>>,
	state_db: Mutex<StateDB>,
	block_queue: BlockQueue,
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	report: RwLock<ClientReport>,
Tomusdrw's avatar
Tomusdrw committed
	panic_handler: Arc<PanicHandler>,
	verifier: Box<Verifier>,
	miner: Arc<Miner>,
	sleep_state: Mutex<SleepState>,
	liveness: AtomicBool,
	io_channel: IoChannel<ClientIoMessage>,
	notify: RwLock<Vec<Weak<ChainNotify>>>,
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	queue_transactions: AtomicUsize,
	last_hashes: RwLock<VecDeque<H256>>,
	/// Create a new client with given spec and DB path and custom verifier.
		config: ClientConfig,
		miner: Arc<Miner>,
		message_channel: IoChannel<ClientIoMessage>,
		db_config: &DatabaseConfig,
	) -> Result<Arc<Client>, ClientError> {
Marek Kotewicz's avatar
Marek Kotewicz committed
		let path = path.to_path_buf();
		let gb = spec.genesis_block();
		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)));
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone()));
Marek Kotewicz's avatar
Marek Kotewicz committed
		let tracedb = RwLock::new(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone()));
		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()));
Gav Wood's avatar
Gav Wood committed
			try!(db.write(batch).map_err(ClientError::Database));
		trace!("Cleanup journal: DB Earliest = {:?}, Latest = {:?}", state_db.journal_db().earliest_era(), state_db.journal_db().latest_era());

		let history = if config.history < MIN_HISTORY_SIZE {
			info!(target: "client", "Ignoring pruning history parameter of {}\
				, falling back to minimum of {}",
				config.history, MIN_HISTORY_SIZE);
			MIN_HISTORY_SIZE
		} else {
			config.history
		};

		if let (Some(earliest), Some(latest)) = (state_db.journal_db().earliest_era(), state_db.journal_db().latest_era()) {
			if latest > earliest && latest - earliest > history {
				for era in earliest..(latest - history + 1) {
					trace!("Removing era {}", era);
					let mut batch = DBTransaction::new(&db);
					try!(state_db.mark_canonical(&mut batch, era, &chain.block_hash(era).expect("Old block not found in the database")));
					try!(db.write(batch).map_err(ClientError::Database));
				}
			}
		}

		if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.journal_db().contains(h.state_root())) {
Loading full blame...