client.rs 32.6 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/>.

NikVolf's avatar
NikVolf committed
use std::path::PathBuf;
use std::collections::{HashSet, HashMap};
use std::ops::Deref;
use std::mem;
use std::collections::VecDeque;
use std::sync::*;
use std::path::Path;
use std::fmt;
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
use std::time::Instant;
NikVolf's avatar
NikVolf committed
// util
use util::numbers::*;
use util::panics::*;
use util::network::*;
use util::io::*;
use util::rlp;
use util::sha3::*;
use util::{Bytes};
use util::rlp::{RlpStream, Rlp, UntrustedRlp};
use util::journaldb;
use util::journaldb::JournalDB;
use util::kvdb::*;
use util::Itertools;
use util::PerfTimer;
use util::View;
use util::Stream;

// other
use views::BlockView;
use error::{ImportError, ExecutionError, BlockError, ImportResult};
use header::BlockNumber;
use state::State;
use spec::Spec;
use engine::Engine;
use views::HeaderView;
use service::{NetSyncMessage, SyncMessage};
use env_info::LastHashes;
use verification;
use verification::{PreverifiedBlock, Verifier};
use block::*;
use transaction::{LocalizedTransaction, SignedTransaction, Action};
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, TransactionImportError,
	TransactionImportResult, Mode};
use client::Error as ClientError;
use env_info::EnvInfo;
use executive::{Executive, Executed, TransactOptions, contract_address};
use receipt::LocalizedReceipt;
use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase};
use trace;
use evm::Factory as EvmFactory;
use miner::{Miner, MinerService, AccountDetails};
use util::TrieFactory;
use ipc::IpcConfig;
use ipc::binary::{BinaryConvertError};

// re-export
pub use types::blockchain_info::BlockChainInfo;
pub use types::block_status::BlockStatus;
pub use blockchain::CacheSize as BlockChainCacheSize;

const MAX_TX_QUEUE_SIZE: usize = 4096;
const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2;

impl fmt::Display for BlockChainInfo {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		write!(f, "#{}.{}", self.best_block_number, self.best_block_hash)
	}
}

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

impl ClientReport {
	/// Alter internal reporting to reflect the additional `block` has been processed.
	pub fn accrue_block(&mut self, block: &PreverifiedBlock) {
		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 },
		}
	}
}

/// 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.
pub struct Client {
	mode: Mode,
	chain: Arc<BlockChain>,
	tracedb: Arc<TraceDB<BlockChain>>,
	engine: Arc<Box<Engine>>,
	state_db: Mutex<Box<JournalDB>>,
	block_queue: BlockQueue,
	report: RwLock<ClientReport>,
	import_lock: Mutex<()>,
	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<NetSyncMessage>,
	queue_transactions: AtomicUsize,
}

const HISTORY: u64 = 1200;
// 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";

/// Get the path for the databases given the root path and information on the databases.
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
}

/// Append a path element to the given path and return the string.
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()
}

impl Client {
	///  Create a new client with given spec and DB path and custom verifier.
	pub fn new(
		config: ClientConfig,
		spec: Spec,
		path: &Path,
		miner: Arc<Miner>,
		message_channel: IoChannel<NetSyncMessage>
	) -> Result<Arc<Client>, ClientError> {
		let path = get_db_path(path, config.pruning, spec.genesis_header().hash());
		let gb = spec.genesis_block();
		let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path));
		let tracedb = Arc::new(try!(TraceDB::new(config.tracing, &path, chain.clone())));

		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());
		}

		let mut state_db = journaldb::new(
Loading full blame...