Newer
Older
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);
let start_hash = match at {
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
}
fn block_hash(chain: &BlockChain, id: BlockId) -> Option<H256> {
match id {
BlockId::Hash(hash) => Some(hash),
BlockId::Number(number) => chain.block_hash(number),
BlockId::Earliest => chain.block_hash(0),
BlockId::Latest | BlockId::Pending => Some(chain.best_block_hash()),
}
}
fn transaction_address(&self, id: TransactionId) -> Option<TransactionAddress> {
TransactionId::Hash(ref hash) => self.chain.read().transaction_address(hash),
TransactionId::Location(id, index) => Self::block_hash(&self.chain.read(), id).map(|hash| TransactionAddress {
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)
}
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);
let mut state_db = self.state_db.lock();
let mut chain = self.chain.write();
let mut tracedb = self.tracedb.write();
self.miner.clear();
let db = self.db.write();
let cache_size = state_db.cache_size();
*state_db = StateDB::new(journaldb::new(db.clone(), self.pruning, ::db::COL_STATE), cache_size);
*chain = Arc::new(BlockChain::new(self.config.blockchain.clone(), &[], db.clone()));
*tracedb = TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone());
impl BlockChainClient for Client {
fn call(&self, t: &SignedTransaction, block: BlockId, analytics: CallAnalytics) -> Result<Executed, CallError> {
let mut env_info = self.env_info(block).ok_or(CallError::StatePruned)?;
env_info.gas_limit = U256::max_value();
let mut state = self.state_at(block).ok_or(CallError::StatePruned)?;
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(&mut 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)
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 options = TransactOptions { tracing: true, vm_tracing: false, check_nonce: false };
let cond = |gas| {
let mut tx = t.as_unsigned().clone();
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())
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);
let c = cond(mid)?;
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).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)?)
}
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,
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
_ => 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(self.engine(), 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 => 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).into_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 transaction = self.contract_call_tx(block_id, address, data);
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) })
}
fn eip86_transition(&self) -> u64 {
self.engine().params().eip86_transition
}
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();
let best_header = &chain.block_header(&h)
.expect("h is best block hash: so its header must exist: qed");
let is_epoch_begin = chain.epoch_transition(best_header.number(), h).is_some();
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),
best_header,
is_epoch_begin,
).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");
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
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
}
&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 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()],
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()));
}
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)> {
.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<(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())),
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(engine: &Engine, mut tx: LocalizedTransaction, mut receipts: Vec<Receipt>) -> LocalizedReceipt {
assert_eq!(receipts.len(), tx.transaction_index + 1, "All previous receipts are provided.");
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,
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)
},
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,
}
}
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
#[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.clone();
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};