Skip to content
client.rs 35 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/>.

use std::collections::{HashSet, HashMap, VecDeque};
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::fmt;
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
Gav Wood's avatar
Gav Wood committed
use std::time::{Instant, Duration};
use time::precise_time_ns;
Gav Wood's avatar
Gav Wood committed
use util::{journaldb, rlp, Bytes, Stream, View, PerfTimer, Itertools, Mutex, RwLock, Colour};
use util::journaldb::JournalDB;
use util::rlp::{RlpStream, Rlp, UntrustedRlp};
use util::numbers::*;
use util::io::*;
use util::sha3::*;
use util::kvdb::*;

// other
use views::BlockView;
NikVolf's avatar
NikVolf committed
use error::{ImportError, ExecutionError, BlockError, ImportResult};
use header::BlockNumber;
use spec::Spec;
use basic_types::Seal;
use engine::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 block_queue::{BlockQueue, BlockQueueInfo};
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig,
	DatabaseCompactionProfile, BlockChainClient, MiningBlockChainClient,
	TraceFilter, CallAnalytics, BlockImportError, Mode, ChainNotify};
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 evm::Factory as EvmFactory;
use util::TrieFactory;

// 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;
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;
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,
	chain: Arc<BlockChain>,
	tracedb: Arc<TraceDB<BlockChain>>,
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	engine: Arc<Box<Engine>>,
Gav Wood's avatar
Gav Wood committed
	state_db: Mutex<Box<JournalDB>>,
	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>,
	vm_factory: Arc<EvmFactory>,
	trie_factory: TrieFactory,
	miner: Arc<Miner>,
	sleep_state: Mutex<SleepState>,
	liveness: AtomicBool,
	io_channel: IoChannel<ClientIoMessage>,
	notify: RwLock<Option<Weak<ChainNotify>>>,
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	queue_transactions: AtomicUsize,
Gav Wood's avatar
Gav Wood committed
	skipped: AtomicUsize,
	last_import: Mutex<Instant>,
	last_hashes: RwLock<VecDeque<H256>>,
const HISTORY: u64 = 1200;
Gav Wood's avatar
Gav Wood committed
// DO NOT TOUCH THIS ANY MORE UNLESS YOU REALLY KNOW WHAT YOU'RE DOING.
// Altering it will force a blanket DB update for *all* JournalDB-derived
//   databases.
// Instead, add/upgrade the version string of the individual JournalDB-derived database
// of which you actually want force an upgrade.
const CLIENT_DB_VER_STR: &'static str = "5.3";
Gav Wood's avatar
Gav Wood committed
/// Get the path for the databases given the root path and information on the databases.
Gav Wood's avatar
Gav Wood committed
pub fn get_db_path(path: &Path, pruning: journaldb::Algorithm, genesis_hash: H256) -> PathBuf {
	let mut dir = path.to_path_buf();
	dir.push(H64::from(genesis_hash).hex());
	//TODO: sec/fat: pruned/full versioning
	// version here is a bit useless now, since it's controlled only be the pruning algo.
	dir.push(format!("v{}-sec-{}", CLIENT_DB_VER_STR, pruning));
	dir
}

Gav Wood's avatar
Gav Wood committed
/// Append a path element to the given path and return the string.
Gav Wood's avatar
Gav Wood committed
pub fn append_path(path: &Path, item: &str) -> String {
	let mut p = path.to_path_buf();
	p.push(item);
	p.to_str().unwrap().to_owned()
}

Marek Kotewicz's avatar
Marek Kotewicz committed
	///  Create a new client with given spec and DB path and custom verifier.
		config: ClientConfig,
		spec: Spec,
		path: &Path,
		miner: Arc<Miner>,
		message_channel: IoChannel<ClientIoMessage>,
	) -> Result<Arc<Client>, ClientError> {
Gav Wood's avatar
Gav Wood committed
		let path = get_db_path(path, config.pruning, spec.genesis_header().hash());
		let gb = spec.genesis_block();
Gav Wood's avatar
Gav Wood committed
		let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path));
		let tracedb = Arc::new(try!(TraceDB::new(config.tracing, &path, chain.clone())));
Nikolay Volf's avatar
Nikolay Volf committed
		let mut state_db_config = match config.db_cache_size {
			None => DatabaseConfig::default(),
			Some(cache_size) => DatabaseConfig::with_cache(cache_size),
		};

		if config.db_compaction == DatabaseCompactionProfile::HDD {
			state_db_config = state_db_config.compaction(CompactionProfile::hdd());
		}

Nikolay Volf's avatar
Nikolay Volf committed
		let mut state_db = journaldb::new(
			&append_path(&path, "state"),
			config.pruning,
			state_db_config
Gav Wood's avatar
Gav Wood committed
		);
		if state_db.is_empty() && spec.ensure_db_good(state_db.as_hashdb_mut()) {
			state_db.commit(0, &spec.genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
Loading full blame...