client.rs 58.9 KiB
Newer Older
			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, &self.factories.vm).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, &self.factories.vm).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();
		Self::block_hash(&chain, 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),
			BlockId::Latest | BlockId::Pending => Some(self.chain.read().best_block_number()),
		}
	}

	fn block_body(&self, id: BlockId) -> Option<encoded::Body> {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let chain = self.chain.read();
		Self::block_hash(&chain, id).and_then(|hash| chain.block_body(&hash))
	fn block(&self, id: BlockId) -> Option<encoded::Block> {
		if let BlockId::Pending = id {
			if let Some(block) = self.miner.pending_block() {
				return Some(encoded::Block::new(block.rlp_bytes(Seal::Without)));
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let chain = self.chain.read();
		Self::block_hash(&chain, id).and_then(|hash| {
			chain.block(&hash)
	fn block_status(&self, id: BlockId) -> BlockStatus {
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
		let chain = self.chain.read();
		match Self::block_hash(&chain, id) {
			Some(ref hash) if chain.is_known(hash) => BlockStatus::InChain,
	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).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();
		Self::block_hash(&chain, 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)) => {
						Some(transaction_receipt(transaction, previous_receipts))
					},
					_ => None,
				}
			}))
	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::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));
			if self.block_status(BlockId::Hash(unverified.parent_hash())) == BlockStatus::Unknown {
				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));
			}
			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 {
		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);
		}
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(|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 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, block_id, Default::default())
			.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 network_id = self.engine.signing_network_id(&self.latest_env_info());
		let signature = self.engine.sign(transaction.hash(network_id))?;
		let signed = SignedTransaction::new(transaction.with_signature(signature, network_id))?;
		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) })
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
	}
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.");
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()));
	}
}

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, 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<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, &self.factories.vm).transact(&transaction, options);

		match res {
			Err(ExecutionError::Internal(_)) => return None,
			_ => return Some(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.
fn transaction_receipt(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,
		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(&sender, &tx.nonce))
		},
		logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry {
			entry: log,
			block_hash: block_hash,
			block_number: block_number,
			transaction_hash: transaction_hash,
			transaction_index: transaction_index,
			transaction_log_index: i,
			log_index: no_of_logs + i,
		}).collect(),
		log_bloom: receipt.log_bloom,
		state_root: receipt.state_root,
	}
}

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

	#[test]
	fn should_return_correct_log_index() {
		use super::transaction_receipt;
		use ethkey::KeyPair;
		use log_entry::{LogEntry, LocalizedLogEntry};
		use receipt::{Receipt, LocalizedReceipt};
		use transaction::{Transaction, LocalizedTransaction, Action};
		use util::Hashable;

		// given
		let key = KeyPair::from_secret_slice(&"test".sha3()).unwrap();
		let secret = key.secret();

		let block_number = 1;
		let block_hash = 5.into();
		let state_root = Some(99.into());
		let gas_used = 10.into();
		let raw_tx = Transaction {
			nonce: 0.into(),
			gas_price: 0.into(),
			gas: 21000.into(),
			action: Action::Call(10.into()),
			value: 0.into(),
			data: vec![],
		};
		let tx1 = raw_tx.clone().sign(secret, None);
		let transaction = LocalizedTransaction {
			signed: tx1.clone().into(),
			block_number: block_number,
			block_hash: block_hash,
			transaction_index: 1,
			cached_sender: Some(tx1.sender()),
		};
		let logs = vec![LogEntry {
			address: 5.into(),
			topics: vec![],
			data: vec![],
		}, LogEntry {
			address: 15.into(),
			topics: vec![],
			data: vec![],
		}];
		let receipts = vec![Receipt {
			state_root: state_root,
			gas_used: 5.into(),
			log_bloom: Default::default(),
			logs: vec![logs[0].clone()],
		}, Receipt {
			state_root: state_root,
			gas_used: gas_used,
			log_bloom: Default::default(),
			logs: logs.clone(),
		}];

		// when
		let receipt = transaction_receipt(transaction, receipts);

		// then
		assert_eq!(receipt, LocalizedReceipt {
			transaction_hash: tx1.hash(),
			transaction_index: 1,
			block_hash: block_hash,
			block_number: block_number,
			cumulative_gas_used: gas_used,
			gas_used: gas_used - 5.into(),
			contract_address: None,
			logs: vec![LocalizedLogEntry {
				entry: logs[0].clone(),
				block_hash: block_hash,
				block_number: block_number,
				transaction_hash: tx1.hash(),
				transaction_index: 1,
				transaction_log_index: 0,
				log_index: 1,
			}, LocalizedLogEntry {
				entry: logs[1].clone(),
				block_hash: block_hash,
				block_number: block_number,
				transaction_hash: tx1.hash(),
				transaction_index: 1,
				transaction_log_index: 1,
				log_index: 2,
			}],
			log_bloom: Default::default(),
			state_root: state_root,
		});
	}