client.rs 50.6 KiB
Newer Older
	fn block_total_difficulty(&self, id: BlockId) -> Option<U256> {
		if let BlockId::Pending = id {
			if let Some(block) = self.miner.pending_block() {
				return Some(*block.header.difficulty() + self.block_total_difficulty(BlockId::Latest).expect("blocks in chain have details; qed"));
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let chain = self.chain.read();
		Self::block_hash(&chain, 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).map(|s| s.nonce(address))
	fn storage_root(&self, address: &Address, id: BlockId) -> Option<H256> {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		self.state_at(id).and_then(|s| s.storage_root(address))
	}

	fn block_hash(&self, id: BlockId) -> Option<H256> {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let chain = self.chain.read();
		Self::block_hash(&chain, id)
	fn code(&self, address: &Address, id: BlockId) -> Option<Option<Bytes>> {
		self.state_at(id).map(|s| s.code(address).map(|c| (*c).clone()))
	fn balance(&self, address: &Address, id: BlockId) -> Option<U256> {
		self.state_at(id).map(|s| s.balance(address))
	fn storage_at(&self, address: &Address, position: &H256, id: BlockId) -> Option<H256> {
		self.state_at(id).map(|s| s.storage_at(address, position))
	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) {
			Some(root) => root,
			_ => 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<Bytes> {
NikVolf's avatar
NikVolf committed
		let index = id.position;
Tomasz Drwięga's avatar
Tomasz Drwięga committed
		self.block_body(id.block).and_then(|body| BodyView::new(&body).uncle_rlp_at(index))
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| {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
			let t = chain.block_body(&address.block_hash)
				.and_then(|block| {
					BodyView::new(&block).localized_transaction_at(&address.block_hash, block_number, address.index)
				});
			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();
					let transaction_index = tx.transaction_index;
					let prior_gas_used = match tx.transaction_index {
						0 => U256::zero(),
						i => {
							let prior_address = TransactionAddress { block_hash: address.block_hash, index: i - 1 };
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
							let prior_receipt = chain.transaction_receipt(&prior_address).expect("Transaction receipt at `address` exists; `prior_address` has lower index in same block; qed");
							prior_receipt.gas_used
						}
					};
					Some(LocalizedReceipt {
						transaction_hash: tx.hash(),
						transaction_index: tx.transaction_index,
						block_hash: tx.block_hash,
						block_number: tx.block_number,
						cumulative_gas_used: receipt.gas_used,
						gas_used: receipt.gas_used - prior_gas_used,
Marek Kotewicz's avatar
Marek Kotewicz committed
						contract_address: match tx.action {
							Action::Call(_) => None,
							Action::Create => Some(contract_address(&sender, &tx.nonce))
Marek Kotewicz's avatar
Marek Kotewicz committed
						},
						logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry {
							entry: log,
							block_hash: block_hash.clone(),
							block_number: block_number,
							transaction_hash: transaction_hash.clone(),
							transaction_index: transaction_index,
							log_index: i
						}).collect(),
						log_bloom: receipt.log_bloom,
						state_root: receipt.state_root,
Tomasz Drwięga's avatar
Tomasz Drwięga committed
		}))
	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 => Some(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> {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		self.chain.read().block_receipts(hash).map(|receipts| ::rlp::encode(&receipts).to_vec())
NikVolf's avatar
NikVolf committed
	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);

NikVolf's avatar
NikVolf committed
				return Err(BlockImportError::Import(ImportError::AlreadyInChain));
			if self.block_status(BlockId::Hash(unverified.parent_hash())) == BlockStatus::Unknown {
				return Err(BlockImportError::Block(BlockError::UnknownParent(unverified.parent_hash())));
	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));
			}
			if self.block_status(BlockId::Hash(header.parent_hash())) == BlockStatus::Unknown {
				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 {
		self.chain.read().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);
				}
			}
		}
	}

Gav Wood's avatar
Gav Wood committed
	fn pending_transactions(&self) -> Vec<SignedTransaction> {
		self.miner.pending_transactions(self.chain.read().best_block_number())
	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);
		}
keorn's avatar
keorn committed
	fn broadcast_consensus_message(&self, message: Bytes) {
		self.notify(|notify| notify.broadcast(message.clone()));
	}


Gav Wood's avatar
Gav Wood committed
	fn signing_network_id(&self) -> Option<u64> {
Gav Wood's avatar
Gav Wood committed
		self.engine.signing_network_id(&self.latest_env_info())
	}
	fn block_extra_info(&self, id: BlockId) -> Option<BTreeMap<String, String>> {
		self.block_header(id)
			.map(|block| decode(&block))
			.map(|header| self.engine.extra_info(&header))
	}

	fn uncle_extra_info(&self, id: UncleId) -> Option<BTreeMap<String, String>> {
			.map(|header| self.engine.extra_info(&decode(&header)))

	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, address: Address, data: Bytes) -> Result<Bytes, String> {
		let from = Address::default();
		let transaction = Transaction {
			nonce: self.latest_nonce(&from),
			action: Action::Call(address),
			gas: U256::from(50_000_000),
			gas_price: U256::default(),
			value: U256::default(),
			data: data,
		}.fake_sign(from);

		self.call(&transaction, BlockId::Latest, Default::default())
			.map_err(|e| format!("{:?}", e))
			.map(|executed| {
				executed.output
			})
	}

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

	fn registry_address(&self, name: String) -> Option<Address> {
		self.registrar.lock().as_ref()
			.and_then(|r| r.get_address(&(name.as_bytes().sha3()), "A").ok())
			.and_then(|a| if a.is_zero() { None } else { Some(a) })
impl MiningBlockChainClient for Client {
	fn latest_schedule(&self) -> Schedule {
Gav Wood's avatar
Gav Wood committed
		self.engine.schedule(&self.latest_env_info())
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();
Nikolay Volf's avatar
Nikolay Volf committed

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),
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
			&chain.block_header(&h).expect("h is best block hash: so its header must exist: qed"),
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
	}
NikVolf's avatar
NikVolf committed

	fn vm_factory(&self) -> &EvmFactory {
NikVolf's avatar
NikVolf committed
	}
	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!")
		}
	}

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 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);
			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.");
Tomusdrw's avatar
Tomusdrw committed
impl MayPanic for Client {
	fn on_panic<F>(&self, closure: F) where F: OnPanicListener {
impl ::client::ProvingBlockChainClient for Client {
	fn prove_storage(&self, key1: H256, key2: H256, from_level: u32, id: BlockId) -> Vec<Bytes> {
		self.state_at(id)
			.and_then(move |state| state.prove_storage(key1, key2, from_level).ok())
			.unwrap_or_else(Vec::new)
	fn prove_account(&self, key1: H256, from_level: u32, id: BlockId) -> Vec<Bytes> {
		self.state_at(id)
			.and_then(move |state| state.prove_account(key1, from_level).ok())
			.unwrap_or_else(Vec::new)
	fn code_by_hash(&self, account_key: H256, id: BlockId) -> Bytes {
		self.state_at(id)
			.and_then(move |state| state.code_by_address_hash(account_key).ok())
			.and_then(|x| x)
			.unwrap_or_else(Vec::new)
keorn's avatar
keorn committed
impl Drop for Client {
	fn drop(&mut self) {
		self.engine.stop();
	}
}

#[cfg(test)]
mod tests {

	#[test]
	fn should_not_cache_details_before_commit() {
		use client::BlockChainClient;
		use tests::helpers::*;

		use std::thread;
		use std::time::Duration;
		use std::sync::Arc;
		use std::sync::atomic::{AtomicBool, Ordering};
		use util::kvdb::DBTransaction;

		let client = generate_dummy_client(0);
		let genesis = client.chain_info().best_block_hash;
		let (new_hash, new_block) = get_good_dummy_block_hash();

		let go = {
			// Separate thread uncommited transaction
			let go = Arc::new(AtomicBool::new(false));
			let go_thread = go.clone();
			let another_client = client.reference().clone();
			thread::spawn(move || {
				let mut batch = DBTransaction::new(&*another_client.chain.read().db().clone());
				another_client.chain.read().insert_block(&mut batch, &new_block, Vec::new());
				go_thread.store(true, Ordering::SeqCst);
			});
			go
		};

		while !go.load(Ordering::SeqCst) { thread::park_timeout(Duration::from_millis(5)); }

		assert!(client.tree_route(&genesis, &new_hash).is_none());
	}