client.rs 69.5 KiB
Newer Older
						self.sleep();
						ss.last_activity = None;
						ss.last_autosleep = Some(now);
					}
				}
				if let Some(t) = ss.last_autosleep {
					if now > t + wakeup_after {
						self.wake_up();
						ss.last_activity = Some(now);
						ss.last_autosleep = None;
					}
				}
			}
			_ => {}
		}
	/// 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.lock().journal_db().boxed_clone();
		let best_block_number = self.chain_info().best_block_number;
		let block_number = self.block_number(at).ok_or(snapshot::Error::InvalidStartingBlock(at))?;
		if db.is_pruned() && self.pruning_info().earliest_state > block_number {
			return Err(snapshot::Error::OldBlockPrunedDB.into());
		}

		let history = ::std::cmp::min(self.history, 1000);

			BlockId::Latest => {
				let start_num = match db.earliest_era() {
					Some(era) => ::std::cmp::max(era, best_block_number.saturating_sub(history)),
					None => best_block_number.saturating_sub(history),
				match self.block_hash(BlockId::Number(start_num)) {
					Some(h) => h,
					None => return Err(snapshot::Error::InvalidStartingBlock(at).into()),
				}
			}
			_ => match self.block_hash(at) {
				Some(hash) => hash,
				None => return Err(snapshot::Error::InvalidStartingBlock(at).into()),
			},
		snapshot::take_snapshot(&*self.engine, &self.chain.read(), start_hash, db.as_hashdb(), writer, p)?;
	/// Ask the client what the history parameter is.
	pub fn pruning_history(&self) -> u64 {
		self.history
	}

Tomasz Drwięga's avatar
Tomasz Drwięga committed
	fn block_hash(chain: &BlockChain, miner: &Miner, id: BlockId) -> Option<H256> {
			BlockId::Hash(hash) => Some(hash),
			BlockId::Number(number) => chain.block_hash(number),
			BlockId::Earliest => chain.block_hash(0),
Tomasz Drwięga's avatar
Tomasz Drwięga committed
			BlockId::Latest => Some(chain.best_block_hash()),
			BlockId::Pending => miner.pending_block_header(chain.best_block_number()).map(|header| header.hash())
	fn transaction_address(&self, id: TransactionId) -> Option<TransactionAddress> {
Marek Kotewicz's avatar
Marek Kotewicz committed
		match id {
			TransactionId::Hash(ref hash) => self.chain.read().transaction_address(hash),
Tomasz Drwięga's avatar
Tomasz Drwięga committed
			TransactionId::Location(id, index) => Self::block_hash(&self.chain.read(), &self.miner, id).map(|hash| TransactionAddress {
Marek Kotewicz's avatar
Marek Kotewicz committed
				block_hash: hash,
				index: index,

	fn wake_up(&self) {
		if !self.liveness.load(AtomicOrdering::Relaxed) {
			self.liveness.store(true, AtomicOrdering::Relaxed);
			self.notify(|n| n.start());
			trace!(target: "mode", "wake_up: Waking.");
		}
	}

	fn sleep(&self) {
		if self.liveness.load(AtomicOrdering::Relaxed) {
			// only sleep if the import queue is mostly empty.
			if self.queue_info().total_queue_size() <= MAX_QUEUE_SIZE_TO_SLEEP_ON {
				self.liveness.store(false, AtomicOrdering::Relaxed);
				self.notify(|n| n.stop());
				trace!(target: "mode", "sleep: Sleeping.");
			} else {
				trace!(target: "mode", "sleep: Cannot sleep - syncing ongoing.");
				// TODO: Consider uncommenting.
				//*self.last_activity.lock() = Some(Instant::now());

	// transaction for calling contracts from services like engine.
	// from the null sender, with 50M gas.
	fn contract_call_tx(&self, block_id: BlockId, address: Address, data: Bytes) -> SignedTransaction {
		let from = Address::default();
		Transaction {
			nonce: self.nonce(&from, block_id).unwrap_or_else(|| self.engine.account_start_nonce(0)),
			action: Action::Call(address),
			gas: U256::from(50_000_000),
			gas_price: U256::default(),
			value: U256::default(),
			data: data,
		}.fake_sign(from)
	}
Tomasz Drwięga's avatar
Tomasz Drwięga committed
	fn do_call(&self, env_info: &EnvInfo, state: &mut State<StateDB>, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> {
Tomasz Drwięga's avatar
Tomasz Drwięga committed
		let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };

		let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
		let mut ret = Executive::new(state, env_info, &*self.engine).transact_virtual(t, options)?;

		// TODO gav move this into Executive.
		if let Some(original) = original_state {
			ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?);
		}

		Ok(ret)
	}
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
impl snapshot::DatabaseRestore for Client {
	/// Restart the client with a new backend
	fn restore_db(&self, new_db: &str) -> Result<(), EthcoreError> {
		trace!(target: "snapshot", "Replacing client database with {:?}", new_db);

Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let _import_lock = self.import_lock.lock();
		let mut state_db = self.state_db.lock();
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let mut chain = self.chain.write();
		let mut tracedb = self.tracedb.write();
		self.miner.clear();
		let db = self.db.write();
		db.restore(new_db)?;
		let cache_size = state_db.cache_size();
		*state_db = StateDB::new(journaldb::new(db.clone(), self.pruning, ::db::COL_STATE), cache_size);
keorn's avatar
keorn committed
		*chain = Arc::new(BlockChain::new(self.config.blockchain.clone(), &[], db.clone()));
Marek Kotewicz's avatar
Marek Kotewicz committed
		*tracedb = TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone());
impl BlockChainClient for Client {
Tomasz Drwięga's avatar
Tomasz Drwięga committed
	fn call(&self, transaction: &SignedTransaction, analytics: CallAnalytics, block: BlockId) -> Result<Executed, CallError> {
		let mut env_info = self.env_info(block).ok_or(CallError::StatePruned)?;
		env_info.gas_limit = U256::max_value();

Marek Kotewicz's avatar
Marek Kotewicz committed
		// that's just a copy of the state.
		let mut state = self.state_at(block).ok_or(CallError::StatePruned)?;
Tomasz Drwięga's avatar
Tomasz Drwięga committed
		self.do_call(&env_info, &mut state, transaction, analytics)
Tomasz Drwięga's avatar
Tomasz Drwięga committed
	fn call_many(&self, transactions: &[(SignedTransaction, CallAnalytics)], block: BlockId) -> Result<Vec<Executed>, CallError> {
		let mut env_info = self.env_info(block).ok_or(CallError::StatePruned)?;
		env_info.gas_limit = U256::max_value();

		// that's just a copy of the state.
		let mut state = self.state_at(block).ok_or(CallError::StatePruned)?;
		let mut results = Vec::with_capacity(transactions.len());

		for &(ref t, analytics) in transactions {
Tomasz Drwięga's avatar
Tomasz Drwięga committed
			let ret = self.do_call(&env_info, &mut state, t, analytics)?;
Tomasz Drwięga's avatar
Tomasz Drwięga committed
			env_info.gas_used = ret.cumulative_gas_used;
			results.push(ret);
Tomasz Drwięga's avatar
Tomasz Drwięga committed
		Ok(results)
	fn estimate_gas(&self, t: &SignedTransaction, block: BlockId) -> Result<U256, CallError> {
		const UPPER_CEILING: u64 = 1_000_000_000_000u64;
		let (mut upper, env_info)  = {
			let mut env_info = self.env_info(block).ok_or(CallError::StatePruned)?;
			let initial_upper = env_info.gas_limit;
			env_info.gas_limit = UPPER_CEILING.into();
			(initial_upper, env_info)
		// that's just a copy of the state.
		let original_state = self.state_at(block).ok_or(CallError::StatePruned)?;
		let sender = t.sender();
		let options = TransactOptions { tracing: true, vm_tracing: false, check_nonce: false };

		let cond = |gas| {
			let mut tx = t.as_unsigned().clone();
			let tx = tx.fake_sign(sender);

			let mut state = original_state.clone();
			Ok(Executive::new(&mut state, &env_info, &*self.engine)
				.transact_virtual(&tx, options.clone())
				.map(|r| r.exception.is_none())
			// impossible at block gas limit - try `UPPER_CEILING` instead.
			// TODO: consider raising limit by powers of two.
			upper = UPPER_CEILING.into();
				trace!(target: "estimate_gas", "estimate_gas failed with {}", upper);
				let err = ExecutionError::Internal(format!("Requires higher than upper limit of {}", upper));
				return Err(err.into())
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let lower = t.gas_required(&self.engine.schedule(env_info.number)).into();
			trace!(target: "estimate_gas", "estimate_gas succeeded with {}", lower);
			return Ok(lower)
		}

		/// Find transition point between `lower` and `upper` where `cond` changes from `false` to `true`.
		/// Returns the lowest value between `lower` and `upper` for which `cond` returns true.
		/// We assert: `cond(lower) = false`, `cond(upper) = true`
		fn binary_chop<F, E>(mut lower: U256, mut upper: U256, mut cond: F) -> Result<U256, E>
			where F: FnMut(U256) -> Result<bool, E>
		{
			while upper - lower > 1.into() {
				let mid = (lower + upper) / 2.into();
				trace!(target: "estimate_gas", "{} .. {} .. {}", lower, mid, upper);
				match c {
					true => upper = mid,
					false => lower = mid,
				};
				trace!(target: "estimate_gas", "{} => {} .. {}", c, lower, upper);
			}
		}

		// binary chop to non-excepting call with gas somewhere between 21000 and block gas limit
		trace!(target: "estimate_gas", "estimate_gas chopping {} .. {}", lower, upper);
		binary_chop(lower, upper, cond)
	fn replay(&self, id: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError> {
		let address = self.transaction_address(id).ok_or(CallError::TransactionNotFound)?;
		let mut env_info = self.env_info(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?;
		let body = self.block_body(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?;
		let mut state = self.state_at_beginning(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)?;
		let mut txs = body.transactions();

		if address.index >= txs.len() {
			return Err(CallError::TransactionNotFound);
		}

		let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
		const PROOF: &'static str = "Transactions fetched from blockchain; blockchain transactions are valid; qed";
		let rest = txs.split_off(address.index);
		for t in txs {
			let t = SignedTransaction::new(t).expect(PROOF);
			let x = Executive::new(&mut state, &env_info, &*self.engine).transact(&t, Default::default())?;
			env_info.gas_used = env_info.gas_used + x.gas_used;
		let first = rest.into_iter().next().expect("We split off < `address.index`; Length is checked earlier; qed");
		let t = SignedTransaction::new(first).expect(PROOF);
		let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
		let mut ret = Executive::new(&mut state, &env_info, &*self.engine).transact(&t, options)?;
		if let Some(original) = original_state {
			ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?)
		}
	fn mode(&self) -> IpcMode {
		let r = self.mode.lock().clone().into();
		trace!(target: "mode", "Asked for mode = {:?}. returning {:?}", &*self.mode.lock(), r);
		r
	}
	fn disable(&self) {
		self.set_mode(IpcMode::Off);
		self.enabled.store(false, AtomicOrdering::Relaxed);
Gav Wood's avatar
Gav Wood committed
		self.clear_queue();
	fn set_mode(&self, new_mode: IpcMode) {
		trace!(target: "mode", "Client::set_mode({:?})", new_mode);
		if !self.enabled.load(AtomicOrdering::Relaxed) {
			return;
		}
		{
			let mut mode = self.mode.lock();
			*mode = new_mode.clone().into();
			trace!(target: "mode", "Mode now {:?}", &*mode);
			if let Some(ref mut f) = *self.on_user_defaults_change.lock() {
Tomasz Drwięga's avatar
Tomasz Drwięga committed
				trace!(target: "mode", "Making callback...");
				f(Some((&*mode).clone()))
			}
		}
		match new_mode {
Gav Wood's avatar
Gav Wood committed
			IpcMode::Active => self.wake_up(),
			IpcMode::Off => self.sleep(),
			_ => {(*self.sleep_state.lock()).last_activity = Some(Instant::now()); }
		}
	}

	fn spec_name(&self) -> String {
		self.config.spec_name.clone()
	}

	fn set_spec_name(&self, new_spec_name: String) {
		trace!(target: "mode", "Client::set_spec_name({:?})", new_spec_name);
		if !self.enabled.load(AtomicOrdering::Relaxed) {
			return;
		}
		if let Some(ref h) = *self.exit_handler.lock() {
			(*h)(true, Some(new_spec_name));
		} else {
			warn!("Not hypervised; cannot change chain.");
		}
	}

	fn best_block_header(&self) -> encoded::Header {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		self.chain.read().best_block_header()
	fn block_header(&self, id: BlockId) -> Option<::encoded::Header> {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let chain = self.chain.read();
Tomasz Drwięga's avatar
Tomasz Drwięga committed

		if let BlockId::Pending = id {
			if let Some(block) = self.miner.pending_block(chain.best_block_number()) {
				return Some(encoded::Header::new(block.header.rlp(Seal::Without)));
			}
			// fall back to latest
			return self.block_header(BlockId::Latest);
		}

		Self::block_hash(&chain, &self.miner, id).and_then(|hash| chain.block_header_data(&hash))
Gav Wood's avatar
Gav Wood committed
	fn block_number(&self, id: BlockId) -> Option<BlockNumber> {
		match id {
			BlockId::Number(number) => Some(number),
			BlockId::Hash(ref hash) => self.chain.read().block_number(hash),
			BlockId::Earliest => Some(0),
Tomasz Drwięga's avatar
Tomasz Drwięga committed
			BlockId::Latest => Some(self.chain.read().best_block_number()),
			BlockId::Pending => Some(self.chain.read().best_block_number() + 1),
Gav Wood's avatar
Gav Wood committed
		}
	}

	fn block_body(&self, id: BlockId) -> Option<encoded::Body> {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let chain = self.chain.read();
Tomasz Drwięga's avatar
Tomasz Drwięga committed

		if let BlockId::Pending = id {
			if let Some(block) = self.miner.pending_block(chain.best_block_number()) {
				return Some(encoded::Body::new(BlockChain::block_to_body(&block.rlp_bytes(Seal::Without))));
			}
			// fall back to latest
			return self.block_body(BlockId::Latest);
		}

		Self::block_hash(&chain, &self.miner, id).and_then(|hash| chain.block_body(&hash))
	fn block(&self, id: BlockId) -> Option<encoded::Block> {
Tomasz Drwięga's avatar
Tomasz Drwięga committed
		let chain = self.chain.read();

		if let BlockId::Pending = id {
Tomasz Drwięga's avatar
Tomasz Drwięga committed
			if let Some(block) = self.miner.pending_block(chain.best_block_number()) {
				return Some(encoded::Block::new(block.rlp_bytes(Seal::Without)));
Tomasz Drwięga's avatar
Tomasz Drwięga committed
			// fall back to latest
			return self.block(BlockId::Latest);
Tomasz Drwięga's avatar
Tomasz Drwięga committed

		Self::block_hash(&chain, &self.miner, id).and_then(|hash| {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
			chain.block(&hash)
	fn block_status(&self, id: BlockId) -> BlockStatus {
Tomasz Drwięga's avatar
Tomasz Drwięga committed
		if let BlockId::Pending = id {
			return BlockStatus::Pending;
		}

Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let chain = self.chain.read();
Tomasz Drwięga's avatar
Tomasz Drwięga committed
		match Self::block_hash(&chain, &self.miner, id) {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
			Some(ref hash) if chain.is_known(hash) => BlockStatus::InChain,
	fn block_total_difficulty(&self, id: BlockId) -> Option<U256> {
Tomasz Drwięga's avatar
Tomasz Drwięga committed
		let chain = self.chain.read();
		if let BlockId::Pending = id {
Tomasz Drwięga's avatar
Tomasz Drwięga committed
			let latest_difficulty = self.block_total_difficulty(BlockId::Latest).expect("blocks in chain have details; qed");
			let pending_difficulty = self.miner.pending_block_header(chain.best_block_number()).map(|header| *header.difficulty());
			if let Some(difficulty) = pending_difficulty {
				return Some(difficulty + latest_difficulty);
Tomasz Drwięga's avatar
Tomasz Drwięga committed
			// fall back to latest
			return Some(latest_difficulty);
Tomasz Drwięga's avatar
Tomasz Drwięga committed

		Self::block_hash(&chain, &self.miner, id).and_then(|hash| chain.block_details(&hash)).map(|d| d.total_difficulty)
	fn nonce(&self, address: &Address, id: BlockId) -> Option<U256> {
		self.state_at(id).and_then(|s| s.nonce(address).ok())
	fn storage_root(&self, address: &Address, id: BlockId) -> Option<H256> {
		self.state_at(id).and_then(|s| s.storage_root(address).ok()).and_then(|x| x)
	fn block_hash(&self, id: BlockId) -> Option<H256> {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let chain = self.chain.read();
Tomasz Drwięga's avatar
Tomasz Drwięga committed
		Self::block_hash(&chain, &self.miner, id)
	fn code(&self, address: &Address, id: BlockId) -> Option<Option<Bytes>> {
		self.state_at(id).and_then(|s| s.code(address).ok()).map(|c| c.map(|c| (&*c).clone()))
	fn balance(&self, address: &Address, id: BlockId) -> Option<U256> {
		self.state_at(id).and_then(|s| s.balance(address).ok())
	fn storage_at(&self, address: &Address, position: &H256, id: BlockId) -> Option<H256> {
		self.state_at(id).and_then(|s| s.storage_at(address, position).ok())
	fn list_accounts(&self, id: BlockId, after: Option<&Address>, count: u64) -> 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;
			}
		};

Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let mut iter = match trie.iter() {
			Ok(iter) => iter,
			_ => return None,
		};

Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		if let Some(after) = after {
			if let Err(e) = iter.seek(after) {
				trace!(target: "fatdb", "list_accounts: Couldn't seek the DB: {:?}", e);
			}
		}

		let accounts = iter.filter_map(|item| {
			item.ok().map(|(addr, _)| Address::from_slice(&addr))
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		}).take(count as usize).collect();
	fn list_storage(&self, id: BlockId, account: &Address, after: Option<&H256>, count: u64) -> Option<Vec<H256>> {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		if !self.factories.trie.is_fat() {
			trace!(target: "fatdb", "list_stroage: Not a fat DB");
			return None;
		}

		let state = match self.state_at(id) {
			Some(state) => state,
			_ => return None,
		};

		let root = match state.storage_root(account) {
			Ok(Some(root)) => root,
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
			_ => return None,
		};

		let (_, db) = state.drop();
		let account_db = self.factories.accountdb.readonly(db.as_hashdb(), account.sha3());
		let trie = match self.factories.trie.readonly(account_db.as_hashdb(), &root) {
			Ok(trie) => trie,
			_ => {
				trace!(target: "fatdb", "list_storage: Couldn't open the DB");
				return None;
			}
		};

		let mut iter = match trie.iter() {
			Ok(iter) => iter,
			_ => return None,
		};

		if let Some(after) = after {
			if let Err(e) = iter.seek(after) {
				trace!(target: "fatdb", "list_accounts: Couldn't seek the DB: {:?}", e);
			}
		}

		let keys = iter.filter_map(|item| {
			item.ok().map(|(key, _)| H256::from_slice(&key))
		}).take(count as usize).collect();

		Some(keys)
	}

	fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction> {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		self.transaction_address(id).and_then(|address| self.chain.read().transaction(&address))
	fn transaction_block(&self, id: TransactionId) -> Option<H256> {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		self.transaction_address(id).map(|addr| addr.block_hash)
	}

	fn uncle(&self, id: UncleId) -> Option<encoded::Header> {
NikVolf's avatar
NikVolf committed
		let index = id.position;
		self.block_body(id.block).and_then(|body| body.view().uncle_rlp_at(index))
			.map(encoded::Header::new)
Marek Kotewicz's avatar
Marek Kotewicz committed
	}

	fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt> {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let chain = self.chain.read();
		self.transaction_address(id)
			.and_then(|address| chain.block_number(&address.block_hash).and_then(|block_number| {
				let transaction = chain.block_body(&address.block_hash)
					.and_then(|body| body.view().localized_transaction_at(&address.block_hash, block_number, address.index));

				let previous_receipts = (0..address.index + 1)
					.map(|index| {
						let mut address = address.clone();
						address.index = index;
						chain.transaction_receipt(&address)
					.collect();
				match (transaction, previous_receipts) {
					(Some(transaction), Some(previous_receipts)) => {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
						Some(transaction_receipt(self.engine(), transaction, previous_receipts))
	fn tree_route(&self, from: &H256, to: &H256) -> Option<TreeRoute> {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let chain = self.chain.read();
		match chain.is_known(from) && chain.is_known(to) {
			true => chain.tree_route(from.clone(), to.clone()),
	fn find_uncles(&self, hash: &H256) -> Option<Vec<H256>> {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		self.chain.read().find_uncle_hashes(hash, self.engine.maximum_uncle_age())
Nikolay Volf's avatar
Nikolay Volf committed
	fn state_data(&self, hash: &H256) -> Option<Bytes> {
		self.state_db.lock().journal_db().state(hash)
	fn block_receipts(&self, hash: &H256) -> Option<Bytes> {
Vurich's avatar
Vurich committed
		self.chain.read().block_receipts(hash).map(|receipts| ::rlp::encode(&receipts).into_vec())
NikVolf's avatar
NikVolf committed
	fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError> {
		use verification::queue::kind::BlockLike;
		use verification::queue::kind::blocks::Unverified;

		// create unverified block here so the `sha3` calculation can be cached.
		let unverified = Unverified::new(bytes);

NikVolf's avatar
NikVolf committed
				return Err(BlockImportError::Import(ImportError::AlreadyInChain));
Tomasz Drwięga's avatar
Tomasz Drwięga committed
			let status = self.block_status(BlockId::Hash(unverified.parent_hash()));
			if status == BlockStatus::Unknown || status == BlockStatus::Pending {
				return Err(BlockImportError::Block(BlockError::UnknownParent(unverified.parent_hash())));
		Ok(self.block_queue.import(unverified)?)
	fn import_block_with_receipts(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result<H256, BlockImportError> {
		{
			// check block order
			let header = BlockView::new(&block_bytes).header_view();
			if self.chain.read().is_known(&header.hash()) {
				return Err(BlockImportError::Import(ImportError::AlreadyInChain));
			}
Tomasz Drwięga's avatar
Tomasz Drwięga committed
			let status = self.block_status(BlockId::Hash(header.parent_hash()));
			if  status == BlockStatus::Unknown || status == BlockStatus::Pending {
				return Err(BlockImportError::Block(BlockError::UnknownParent(header.parent_hash())));
			}
		}
		self.import_old_block(block_bytes, receipts_bytes).map_err(Into::into)
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	fn queue_info(&self) -> BlockQueueInfo {
		self.block_queue.queue_info()
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	fn clear_queue(&self) {
		self.block_queue.clear();
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	}

	fn chain_info(&self) -> BlockChainInfo {
		let mut chain_info = self.chain.read().chain_info();
		chain_info.pending_total_difficulty = chain_info.total_difficulty + self.block_queue.total_difficulty();
		chain_info
Gav Wood's avatar
Gav Wood committed
	fn additional_params(&self) -> BTreeMap<String, String> {
		self.engine.additional_params().into_iter().collect()
	}

	fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockId, to_block: BlockId) -> Option<Vec<BlockNumber>> {
		match (self.block_number(from_block), self.block_number(to_block)) {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
			(Some(from), Some(to)) => Some(self.chain.read().blocks_with_bloom(bloom, from, to)),
	fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry> {
		let blocks = filter.bloom_possibilities().iter()
Marek Kotewicz's avatar
Marek Kotewicz committed
			.filter_map(|bloom| self.blocks_with_bloom(bloom, filter.from_block.clone(), filter.to_block.clone()))
			.flat_map(|m| m)
			// remove duplicate elements
			.collect::<HashSet<u64>>()
			.into_iter()
			.collect::<Vec<u64>>();

		self.chain.read().logs(blocks, |entry| filter.matches(entry), filter.limit)
	fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>> {
		let start = self.block_number(filter.range.start);
		let end = self.block_number(filter.range.end);

		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)
			},
			_ => None,
		}
	}

	fn trace(&self, trace: TraceId) -> Option<LocalizedTrace> {
		let trace_address = trace.address;
		self.transaction_address(trace.transaction)
			.and_then(|tx_address| {
				self.block_number(BlockId::Hash(tx_address.block_hash))
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
					.and_then(|number| self.tracedb.read().trace(number, tx_address.index, trace_address))
	fn transaction_traces(&self, transaction: TransactionId) -> Option<Vec<LocalizedTrace>> {
		self.transaction_address(transaction)
			.and_then(|tx_address| {
				self.block_number(BlockId::Hash(tx_address.block_hash))
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
					.and_then(|number| self.tracedb.read().transaction_traces(number, tx_address.index))
	fn block_traces(&self, block: BlockId) -> Option<Vec<LocalizedTrace>> {
		self.block_number(block)
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
			.and_then(|number| self.tracedb.read().block_traces(number))
	fn last_hashes(&self) -> LastHashes {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		(*self.build_last_hashes(self.chain.read().best_block_hash())).clone()
	fn queue_transactions(&self, transactions: Vec<Bytes>, peer_id: usize) {
keorn's avatar
keorn committed
		let queue_size = self.queue_transactions.load(AtomicOrdering::Relaxed);
		trace!(target: "external_tx", "Queue size: {}", queue_size);
		if queue_size > MAX_TX_QUEUE_SIZE {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
			debug!("Ignoring {} transactions: queue is full", transactions.len());
		} else {
			let len = transactions.len();
			match self.io_channel.lock().send(ClientIoMessage::NewTransactions(transactions, peer_id)) {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
				Ok(_) => {
					self.queue_transactions.fetch_add(len, AtomicOrdering::SeqCst);
				}
				Err(e) => {
					debug!("Ignoring {} transactions: error queueing: {}", len, e);
				}
			}
		}
	}

Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
	fn ready_transactions(&self) -> Vec<PendingTransaction> {
		let (number, timestamp) = {
			let chain = self.chain.read();
			(chain.best_block_number(), chain.best_block_timestamp())
		};
		self.miner.ready_transactions(number, timestamp)
	fn queue_consensus_message(&self, message: Bytes) {
		let channel = self.io_channel.lock().clone();
		if let Err(e) = channel.send(ClientIoMessage::NewMessage(message)) {
keorn's avatar
keorn committed
			debug!("Ignoring the message, error queueing: {}", e);
		}
	fn signing_chain_id(&self) -> Option<u64> {
		self.engine.signing_chain_id(&self.latest_env_info())
Gav Wood's avatar
Gav Wood committed
	}
	fn block_extra_info(&self, id: BlockId) -> Option<BTreeMap<String, String>> {
		self.block_header(id)
			.map(|header| self.engine.extra_info(&header.decode()))
	fn uncle_extra_info(&self, id: UncleId) -> Option<BTreeMap<String, String>> {
			.map(|header| self.engine.extra_info(&header.decode()))

	fn pruning_info(&self) -> PruningInfo {
		PruningInfo {
asynchronous rob's avatar
asynchronous rob committed
			earliest_chain: self.chain.read().first_block_number().unwrap_or(1),
			earliest_state: self.state_db.lock().journal_db().earliest_era().unwrap_or(0),
		}
	}
	fn call_contract(&self, block_id: BlockId, address: Address, data: Bytes) -> Result<Bytes, String> {
		let transaction = self.contract_call_tx(block_id, address, data);
Tomasz Drwięga's avatar
Tomasz Drwięga committed
		self.call(&transaction, Default::default(), block_id)
			.map_err(|e| format!("{:?}", e))
			.map(|executed| {
				executed.output
			})
	}

keorn's avatar
keorn committed
	fn transact_contract(&self, address: Address, data: Bytes) -> Result<TransactionImportResult, EthcoreError> {
		let transaction = Transaction {
			nonce: self.latest_nonce(&self.miner.author()),
			action: Action::Call(address),
			gas: self.miner.gas_floor_target(),
			gas_price: self.miner.sensible_gas_price(),
			value: U256::zero(),
			data: data,
		};
		let chain_id = self.engine.signing_chain_id(&self.latest_env_info());
		let signature = self.engine.sign(transaction.hash(chain_id))?;
		let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?;
keorn's avatar
keorn committed
		self.miner.import_own_transaction(self, signed.into())
	}

	fn registrar_address(&self) -> Option<Address> {
		self.registrar.lock().as_ref().map(|r| r.address)
	}

	fn registry_address(&self, name: String) -> Option<Address> {
		self.registrar.lock().as_ref()
			.and_then(|r| {
				let dispatch = move |reg_addr, data| {
					future::done(self.call_contract(BlockId::Latest, reg_addr, data))
				};
				r.get_address(dispatch, name.as_bytes().sha3(), "A".to_string()).wait().ok()
			})
			.and_then(|a| if a.is_zero() { None } else { Some(a) })
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed

	fn eip86_transition(&self) -> u64 {
		self.engine().params().eip86_transition
	}
impl MiningBlockChainClient for Client {
	fn latest_schedule(&self) -> Schedule {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		self.engine.schedule(self.latest_env_info().number)
Gav Wood's avatar
Gav Wood committed
	fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
		let engine = &*self.engine;
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let chain = self.chain.read();
		let h = chain.best_block_hash();
		let best_header = &chain.block_header(&h)
			.expect("h is best block hash: so its header must exist: qed");
Nikolay Volf's avatar
Nikolay Volf committed

		let is_epoch_begin = chain.epoch_transition(best_header.number(), h).is_some();
Marek Kotewicz's avatar
Marek Kotewicz committed
		let mut open_block = OpenBlock::new(
Nikolay Volf's avatar
Nikolay Volf committed
			engine,
Nikolay Volf's avatar
Nikolay Volf committed
			false,	// TODO: this will need to be parameterised once we want to do immediate mining insertion.
			self.state_db.lock().boxed_clone_canon(&h),
Nikolay Volf's avatar
Nikolay Volf committed
			self.build_last_hashes(h.clone()),
			author,
Gav Wood's avatar
Gav Wood committed
			gas_range_target,
Nikolay Volf's avatar
Nikolay Volf committed
			extra_data,
Gav Wood's avatar
Gav Wood committed
		).expect("OpenBlock::new only fails if parent state root invalid; state root of best block's header is never invalid; qed");
Nikolay Volf's avatar
Nikolay Volf committed

		// Add uncles
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		chain
Nikolay Volf's avatar
Nikolay Volf committed
			.find_uncle_headers(&h, engine.maximum_uncle_age())
			.unwrap_or_else(Vec::new)
Nikolay Volf's avatar
Nikolay Volf committed
			.into_iter()
			.take(engine.maximum_uncle_count())
			.foreach(|h| {
				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");
Nikolay Volf's avatar
Nikolay Volf committed
			});

Marek Kotewicz's avatar
Marek Kotewicz committed
		open_block
Nikolay Volf's avatar
Nikolay Volf committed
	}
	fn reopen_block(&self, block: ClosedBlock) -> OpenBlock {
		let engine = &*self.engine;
		let mut block = block.reopen(engine);
		let max_uncles = engine.maximum_uncle_count();
		if block.uncles().len() < max_uncles {
			let chain = self.chain.read();
			let h = chain.best_block_hash();
			// Add new uncles
			let uncles = chain
				.find_uncle_hashes(&h, engine.maximum_uncle_age())
				.unwrap_or_else(Vec::new);

			for h in uncles {
				if !block.uncles().iter().any(|header| header.hash() == h) {
					let uncle = chain.block_header(&h).expect("find_uncle_hashes only returns hashes for existing headers; qed");
					block.push_uncle(uncle).expect("pushing up to maximum_uncle_count;
												push_uncle is not ok only if more than maximum_uncle_count is pushed;
												so all push_uncle are Ok;
												qed");
					if block.uncles().len() >= max_uncles { break }
				}
			}

		}
		block
	}

NikVolf's avatar
NikVolf committed
	fn vm_factory(&self) -> &EvmFactory {
NikVolf's avatar
NikVolf committed
	}
keorn's avatar
keorn committed
	fn broadcast_proposal_block(&self, block: SealedBlock) {
		self.notify(|notify| {
			notify.new_blocks(
				vec![],
				vec![],
				vec![],
				vec![],
				vec![],
				vec![block.rlp_bytes()],
				0,
			);
		});
	}

	fn import_sealed_block(&self, block: SealedBlock) -> ImportResult {
		let h = block.header().hash();
		let start = precise_time_ns();
		let route = {
			// scope for self.import_lock
			let _import_lock = self.import_lock.lock();
			let _timer = PerfTimer::new("import_sealed_block");

			let number = block.header().number();
			let block_data = block.rlp_bytes();
			let header = block.header().clone();

			let route = self.commit_block(block, &header, &block_data);
			trace!(target: "client", "Imported sealed block #{} ({})", number, h);
			self.state_db.lock().sync_cache(&route.enacted, &route.retracted, false);
			route
		};
		let (enacted, retracted) = self.calculate_enacted_retracted(&[route]);
		self.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted);
		self.notify(|notify| {
			notify.new_blocks(
				vec![h.clone()],
				vec![],
				enacted.clone(),
				retracted.clone(),
				vec![h.clone()],
keorn's avatar
keorn committed
				vec![],
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		self.db.read().flush().expect("DB flush failed.");
impl EngineClient for Client {
	fn update_sealing(&self) {
		self.miner.update_sealing(self)
	}

	fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {
		if self.miner.submit_seal(self, block_hash, seal).is_err() {
			warn!(target: "poa", "Wrong internal seal submission!")
		}
	}

	fn broadcast_consensus_message(&self, message: Bytes) {
		self.notify(|notify| notify.broadcast(message.clone()));
	}

	fn epoch_transition_for(&self, parent_hash: H256) -> Option<::engines::EpochTransition> {
		self.chain.read().epoch_transition_for(parent_hash)
	}
impl ProvingBlockChainClient for Client {
	fn prove_storage(&self, key1: H256, key2: H256, id: BlockId) -> Option<(Vec<Bytes>, H256)> {
		self.state_at(id)
			.and_then(move |state| state.prove_storage(key1, key2).ok())
	fn prove_account(&self, key1: H256, id: BlockId) -> Option<(Vec<Bytes>, ::types::basic_account::BasicAccount)> {
		self.state_at(id)
			.and_then(move |state| state.prove_account(key1).ok())
	fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<(Bytes, Vec<DBValue>)> {
		let (state, mut env_info) = match (self.state_at(id), self.env_info(id)) {
			(Some(s), Some(e)) => (s, e),
			_ => return None,
		};

		env_info.gas_limit = transaction.gas.clone();
		let mut jdb = self.state_db.lock().journal_db().boxed_clone();
		let backend = state::backend::Proving::new(jdb.as_hashdb_mut());

		let mut state = state.replace_backend(backend);
		let options = TransactOptions { tracing: false, vm_tracing: false, check_nonce: false };
		let res = Executive::new(&mut state, &env_info, &*self.engine).transact(&transaction, options);
			Err(ExecutionError::Internal(_)) => None,
			Err(e) => {
				trace!(target: "client", "Proved call failed: {}", e);
				Some((Vec::new(), state.drop().1.extract_proof()))
			}
			Ok(res) => Some((res.output, state.drop().1.extract_proof())),
keorn's avatar
keorn committed
impl Drop for Client {
	fn drop(&mut self) {
		self.engine.stop();
	}
}

/// Returns `LocalizedReceipt` given `LocalizedTransaction`
/// and a vector of receipts from given block up to transaction index.
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
fn transaction_receipt(engine: &Engine, mut tx: LocalizedTransaction, mut receipts: Vec<Receipt>) -> LocalizedReceipt {
	assert_eq!(receipts.len(), tx.transaction_index + 1, "All previous receipts are provided.");

	let sender = tx.sender();
	let receipt = receipts.pop().expect("Current receipt is provided; qed");
	let prior_gas_used = match tx.transaction_index {
		0 => 0.into(),
		i => receipts.get(i - 1).expect("All previous receipts are provided; qed").gas_used,
	};
	let no_of_logs = receipts.into_iter().map(|receipt| receipt.logs.len()).sum::<usize>();
	let transaction_hash = tx.hash();
	let block_hash = tx.block_hash;
	let block_number = tx.block_number;
	let transaction_index = tx.transaction_index;

	LocalizedReceipt {
		transaction_hash: transaction_hash,
		transaction_index: transaction_index,
		block_hash: block_hash,
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		block_number: block_number,
		cumulative_gas_used: receipt.gas_used,
		gas_used: receipt.gas_used - prior_gas_used,
		contract_address: match tx.action {
			Action::Call(_) => None,
			Action::Create => Some(contract_address(engine.create_address_scheme(block_number), &sender, &tx.nonce, &tx.data).0)