Newer
Older
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)?;
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, &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)?)
}
Ok(ret)
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);
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() {
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 {
fn block_header(&self, id: BlockId) -> Option<::encoded::Header> {
let chain = self.chain.read();
Self::block_hash(&chain, id).and_then(|hash| chain.block_header_data(&hash))
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> {
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 Some(block) = self.miner.pending_block() {
return Some(encoded::Block::new(block.rlp_bytes(Seal::Without)));
let chain = self.chain.read();
Self::block_hash(&chain, id).and_then(|hash| {
chain.block(&hash)
fn block_status(&self, id: BlockId) -> BlockStatus {
let chain = self.chain.read();
match Self::block_hash(&chain, id) {
Some(ref hash) if chain.is_known(hash) => BlockStatus::InChain,
asynchronous rob
committed
Some(hash) => self.block_queue.status(&hash).into(),
None => BlockStatus::Unknown
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"));
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> {
let chain = self.chain.read();
Self::block_hash(&chain, id)
Marek Kotewicz
committed
}
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;
}
};
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 accounts = iter.filter_map(|item| {
item.ok().map(|(addr, _)| Address::from_slice(&addr))
Some(accounts)
}
fn list_storage(&self, id: BlockId, account: &Address, after: Option<&H256>, count: u64) -> Option<Vec<H256>> {
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,
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
_ => 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> {
self.transaction_address(id).and_then(|address| self.chain.read().transaction(&address))
fn transaction_block(&self, id: TransactionId) -> Option<H256> {
self.transaction_address(id).map(|addr| addr.block_hash)
}
fn uncle(&self, id: UncleId) -> Option<encoded::Header> {
self.block_body(id.block).and_then(|body| body.view().uncle_rlp_at(index))
.map(encoded::Header::new)
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt> {
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> {
let chain = self.chain.read();
match chain.is_known(from) && chain.is_known(to) {
true => Some(chain.tree_route(from.clone(), to.clone())),
Marek Kotewicz
committed
false => None
}
fn find_uncles(&self, hash: &H256) -> Option<Vec<H256>> {
self.chain.read().find_uncle_hashes(hash, self.engine.maximum_uncle_age())
fn state_data(&self, hash: &H256) -> Option<Bytes> {
self.state_db.lock().journal_db().state(hash)
fn block_receipts(&self, hash: &H256) -> Option<Bytes> {
self.chain.read().block_receipts(hash).map(|receipts| ::rlp::encode(&receipts).to_vec())
fn import_block(&self, bytes: Bytes) -> Result<H256, BlockImportError> {
use verification::queue::kind::BlockLike;
asynchronous rob
committed
use verification::queue::kind::blocks::Unverified;
// create unverified block here so the `sha3` calculation can be cached.
let unverified = Unverified::new(bytes);
asynchronous rob
committed
if self.chain.read().is_known(&unverified.hash()) {
return Err(BlockImportError::Import(ImportError::AlreadyInChain));
if self.block_status(BlockId::Hash(unverified.parent_hash())) == BlockStatus::Unknown {
asynchronous rob
committed
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)
}
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
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)) {
(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()
.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))
.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))
.and_then(|number| self.tracedb.read().transaction_traces(number, tx_address.index))
fn block_traces(&self, block: BlockId) -> Option<Vec<LocalizedTrace>> {
.and_then(|number| self.tracedb.read().block_traces(number))
fn last_hashes(&self) -> LastHashes {
(*self.build_last_hashes(self.chain.read().best_block_hash())).clone()
fn queue_transactions(&self, transactions: Vec<Bytes>, peer_id: usize) {
let queue_size = self.queue_transactions.load(AtomicOrdering::Relaxed);
trace!(target: "external_tx", "Queue size: {}", queue_size);
if queue_size > MAX_TX_QUEUE_SIZE {
debug!("Ignoring {} transactions: queue is full", transactions.len());
} else {
let len = transactions.len();
match self.io_channel.lock().send(ClientIoMessage::NewTransactions(transactions, peer_id)) {
Ok(_) => {
self.queue_transactions.fetch_add(len, AtomicOrdering::SeqCst);
}
Err(e) => {
debug!("Ignoring {} transactions: error queueing: {}", len, e);
}
}
}
}
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)) {
debug!("Ignoring the message, error queueing: {}", e);
}
self.engine.signing_network_id(&self.latest_env_info())
}
fn block_extra_info(&self, id: BlockId) -> Option<BTreeMap<String, String>> {
.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 {
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
})
}
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) })
}
Tomusdrw
committed
impl MiningBlockChainClient for Client {
fn latest_schedule(&self) -> Schedule {
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
let engine = &*self.engine;
let chain = self.chain.read();
let h = chain.best_block_hash();
self.factories.clone(),
false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
self.state_db.lock().boxed_clone_canon(&h),
&chain.block_header(&h).expect("h is best block hash: so its header must exist: qed"),
).expect("OpenBlock::new only fails if parent state root invalid; state root of best block's header is never invalid; qed");
.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");
&self.factories.vm
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()],
precise_time_ns() - start,
);
});
self.db.read().flush().expect("DB flush failed.");
Tomusdrw
committed
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()));
}
}
impl MayPanic for Client {
fn on_panic<F>(&self, closure: F) where F: OnPanicListener {
Tomusdrw
committed
self.panic_handler.on_panic(closure);
}
}
impl ::client::ProvingBlockChainClient for Client {
fn prove_storage(&self, key1: H256, key2: H256, id: BlockId) -> Option<(Vec<Bytes>, H256)> {
.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)> {
.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()),
}
}
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.");
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
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,
}
}
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
#[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
Tomasz Drwięga
committed
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 {
block_number: block_number,
block_hash: block_hash,
transaction_index: 1,
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
};
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,
});
}