Multi-call RPC (#6195)
* Removing duplicated pending state accessors in miner. * Merge miner+client call. * Multicall & multicall RPC. * Sensible defaults. * Fix tests.
This commit is contained in:
committed by
Marek Kotewicz
parent
62153b1ff0
commit
f157461ee1
@@ -906,7 +906,7 @@ impl Client {
|
||||
pub fn state_at(&self, id: BlockId) -> Option<State<StateDB>> {
|
||||
// fast path for latest state.
|
||||
match id.clone() {
|
||||
BlockId::Pending => return self.miner.pending_state().or_else(|| Some(self.state())),
|
||||
BlockId::Pending => return self.miner.pending_state(self.chain.read().best_block_number()).or_else(|| Some(self.state())),
|
||||
BlockId::Latest => return Some(self.state()),
|
||||
_ => {},
|
||||
}
|
||||
@@ -1055,19 +1055,20 @@ impl Client {
|
||||
self.history
|
||||
}
|
||||
|
||||
fn block_hash(chain: &BlockChain, id: BlockId) -> Option<H256> {
|
||||
fn block_hash(chain: &BlockChain, miner: &Miner, 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()),
|
||||
BlockId::Latest => Some(chain.best_block_hash()),
|
||||
BlockId::Pending => miner.pending_block_header(chain.best_block_number()).map(|header| header.hash())
|
||||
}
|
||||
}
|
||||
|
||||
fn transaction_address(&self, id: TransactionId) -> Option<TransactionAddress> {
|
||||
match id {
|
||||
TransactionId::Hash(ref hash) => self.chain.read().transaction_address(hash),
|
||||
TransactionId::Location(id, index) => Self::block_hash(&self.chain.read(), id).map(|hash| TransactionAddress {
|
||||
TransactionId::Location(id, index) => Self::block_hash(&self.chain.read(), &self.miner, id).map(|hash| TransactionAddress {
|
||||
block_hash: hash,
|
||||
index: index,
|
||||
})
|
||||
@@ -1110,6 +1111,31 @@ impl Client {
|
||||
data: data,
|
||||
}.fake_sign(from)
|
||||
}
|
||||
|
||||
fn do_call(&self, env_info: &EnvInfo, state: &mut State<StateDB>, increase_balance: bool, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
|
||||
|
||||
// give the sender a sufficient balance (if calling in pending block)
|
||||
if increase_balance {
|
||||
let sender = t.sender();
|
||||
let balance = state.balance(&sender).map_err(ExecutionError::from)?;
|
||||
let needed_balance = t.value + t.gas * t.gas_price;
|
||||
if balance < needed_balance {
|
||||
state.add_balance(&sender, &(needed_balance - balance), state::CleanupMode::NoEmpty)
|
||||
.map_err(ExecutionError::from)?;
|
||||
}
|
||||
}
|
||||
|
||||
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
|
||||
let mut ret = Executive::new(state, env_info, &*self.engine).transact_virtual(t, options)?;
|
||||
|
||||
// TODO gav move this into Executive.
|
||||
if let Some(original) = original_state {
|
||||
ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?);
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl snapshot::DatabaseRestore for Client {
|
||||
@@ -1134,23 +1160,31 @@ impl snapshot::DatabaseRestore for Client {
|
||||
}
|
||||
|
||||
impl BlockChainClient for Client {
|
||||
fn call(&self, t: &SignedTransaction, block: BlockId, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
fn call(&self, transaction: &SignedTransaction, analytics: CallAnalytics, block: BlockId) -> Result<Executed, CallError> {
|
||||
let mut env_info = self.env_info(block).ok_or(CallError::StatePruned)?;
|
||||
env_info.gas_limit = U256::max_value();
|
||||
|
||||
// that's just a copy of the state.
|
||||
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)?;
|
||||
self.do_call(&env_info, &mut state, block == BlockId::Pending, transaction, analytics)
|
||||
}
|
||||
|
||||
// TODO gav move this into Executive.
|
||||
if let Some(original) = original_state {
|
||||
ret.state_diff = Some(state.diff_from(original).map_err(ExecutionError::from)?);
|
||||
fn call_many(&self, transactions: &[(SignedTransaction, CallAnalytics)], block: BlockId) -> Result<Vec<Executed>, CallError> {
|
||||
let mut env_info = self.env_info(block).ok_or(CallError::StatePruned)?;
|
||||
env_info.gas_limit = U256::max_value();
|
||||
|
||||
// that's just a copy of the state.
|
||||
let mut state = self.state_at(block).ok_or(CallError::StatePruned)?;
|
||||
let mut results = Vec::with_capacity(transactions.len());
|
||||
|
||||
for &(ref t, analytics) in transactions {
|
||||
let ret = self.do_call(&env_info, &mut state, block == BlockId::Pending, t, analytics)?;
|
||||
env_info.gas_used = ret.cumulative_gas_used;
|
||||
results.push(ret);
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
fn estimate_gas(&self, t: &SignedTransaction, block: BlockId) -> Result<U256, CallError> {
|
||||
@@ -1303,7 +1337,16 @@ impl BlockChainClient for Client {
|
||||
|
||||
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))
|
||||
|
||||
if let BlockId::Pending = id {
|
||||
if let Some(block) = self.miner.pending_block(chain.best_block_number()) {
|
||||
return Some(encoded::Header::new(block.header.rlp(Seal::Without)));
|
||||
}
|
||||
// fall back to latest
|
||||
return self.block_header(BlockId::Latest);
|
||||
}
|
||||
|
||||
Self::block_hash(&chain, &self.miner, id).and_then(|hash| chain.block_header_data(&hash))
|
||||
}
|
||||
|
||||
fn block_number(&self, id: BlockId) -> Option<BlockNumber> {
|
||||
@@ -1311,30 +1354,48 @@ impl BlockChainClient for Client {
|
||||
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()),
|
||||
BlockId::Latest => Some(self.chain.read().best_block_number()),
|
||||
BlockId::Pending => Some(self.chain.read().best_block_number() + 1),
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
if let BlockId::Pending = id {
|
||||
if let Some(block) = self.miner.pending_block(chain.best_block_number()) {
|
||||
return Some(encoded::Body::new(BlockChain::block_to_body(&block.rlp_bytes(Seal::Without))));
|
||||
}
|
||||
// fall back to latest
|
||||
return self.block_body(BlockId::Latest);
|
||||
}
|
||||
|
||||
Self::block_hash(&chain, &self.miner, id).and_then(|hash| chain.block_body(&hash))
|
||||
}
|
||||
|
||||
fn block(&self, id: BlockId) -> Option<encoded::Block> {
|
||||
let chain = self.chain.read();
|
||||
|
||||
if let BlockId::Pending = id {
|
||||
if let Some(block) = self.miner.pending_block() {
|
||||
if let Some(block) = self.miner.pending_block(chain.best_block_number()) {
|
||||
return Some(encoded::Block::new(block.rlp_bytes(Seal::Without)));
|
||||
}
|
||||
// fall back to latest
|
||||
return self.block(BlockId::Latest);
|
||||
}
|
||||
let chain = self.chain.read();
|
||||
Self::block_hash(&chain, id).and_then(|hash| {
|
||||
|
||||
Self::block_hash(&chain, &self.miner, id).and_then(|hash| {
|
||||
chain.block(&hash)
|
||||
})
|
||||
}
|
||||
|
||||
fn block_status(&self, id: BlockId) -> BlockStatus {
|
||||
if let BlockId::Pending = id {
|
||||
return BlockStatus::Pending;
|
||||
}
|
||||
|
||||
let chain = self.chain.read();
|
||||
match Self::block_hash(&chain, id) {
|
||||
match Self::block_hash(&chain, &self.miner, id) {
|
||||
Some(ref hash) if chain.is_known(hash) => BlockStatus::InChain,
|
||||
Some(hash) => self.block_queue.status(&hash).into(),
|
||||
None => BlockStatus::Unknown
|
||||
@@ -1342,13 +1403,18 @@ impl BlockChainClient for Client {
|
||||
}
|
||||
|
||||
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)
|
||||
if let BlockId::Pending = id {
|
||||
let latest_difficulty = self.block_total_difficulty(BlockId::Latest).expect("blocks in chain have details; qed");
|
||||
let pending_difficulty = self.miner.pending_block_header(chain.best_block_number()).map(|header| *header.difficulty());
|
||||
if let Some(difficulty) = pending_difficulty {
|
||||
return Some(difficulty + latest_difficulty);
|
||||
}
|
||||
// fall back to latest
|
||||
return Some(latest_difficulty);
|
||||
}
|
||||
|
||||
Self::block_hash(&chain, &self.miner, id).and_then(|hash| chain.block_details(&hash)).map(|d| d.total_difficulty)
|
||||
}
|
||||
|
||||
fn nonce(&self, address: &Address, id: BlockId) -> Option<U256> {
|
||||
@@ -1361,7 +1427,7 @@ impl BlockChainClient for Client {
|
||||
|
||||
fn block_hash(&self, id: BlockId) -> Option<H256> {
|
||||
let chain = self.chain.read();
|
||||
Self::block_hash(&chain, id)
|
||||
Self::block_hash(&chain, &self.miner, id)
|
||||
}
|
||||
|
||||
fn code(&self, address: &Address, id: BlockId) -> Option<Option<Bytes>> {
|
||||
@@ -1526,7 +1592,8 @@ impl BlockChainClient for Client {
|
||||
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 {
|
||||
let status = self.block_status(BlockId::Hash(unverified.parent_hash()));
|
||||
if status == BlockStatus::Unknown || status == BlockStatus::Pending {
|
||||
return Err(BlockImportError::Block(BlockError::UnknownParent(unverified.parent_hash())));
|
||||
}
|
||||
}
|
||||
@@ -1540,7 +1607,8 @@ impl BlockChainClient for Client {
|
||||
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 {
|
||||
let status = self.block_status(BlockId::Hash(header.parent_hash()));
|
||||
if status == BlockStatus::Unknown || status == BlockStatus::Pending {
|
||||
return Err(BlockImportError::Block(BlockError::UnknownParent(header.parent_hash())));
|
||||
}
|
||||
}
|
||||
@@ -1686,7 +1754,7 @@ impl BlockChainClient for Client {
|
||||
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())
|
||||
self.call(&transaction, Default::default(), block_id)
|
||||
.map_err(|e| format!("{:?}", e))
|
||||
.map(|executed| {
|
||||
executed.output
|
||||
|
||||
@@ -401,10 +401,18 @@ impl MiningBlockChainClient for TestBlockChainClient {
|
||||
}
|
||||
|
||||
impl BlockChainClient for TestBlockChainClient {
|
||||
fn call(&self, _t: &SignedTransaction, _block: BlockId, _analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
fn call(&self, _t: &SignedTransaction, _analytics: CallAnalytics, _block: BlockId) -> Result<Executed, CallError> {
|
||||
self.execution_result.read().clone().unwrap()
|
||||
}
|
||||
|
||||
fn call_many(&self, txs: &[(SignedTransaction, CallAnalytics)], block: BlockId) -> Result<Vec<Executed>, CallError> {
|
||||
let mut res = Vec::with_capacity(txs.len());
|
||||
for &(ref tx, analytics) in txs {
|
||||
res.push(self.call(tx, analytics, block)?);
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn estimate_gas(&self, _t: &SignedTransaction, _block: BlockId) -> Result<U256, CallError> {
|
||||
Ok(21000.into())
|
||||
}
|
||||
@@ -423,7 +431,7 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
|
||||
fn nonce(&self, address: &Address, id: BlockId) -> Option<U256> {
|
||||
match id {
|
||||
BlockId::Latest => Some(self.nonces.read().get(address).cloned().unwrap_or(self.spec.params().account_start_nonce)),
|
||||
BlockId::Latest | BlockId::Pending => Some(self.nonces.read().get(address).cloned().unwrap_or(self.spec.params().account_start_nonce)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -438,16 +446,15 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
|
||||
fn code(&self, address: &Address, id: BlockId) -> Option<Option<Bytes>> {
|
||||
match id {
|
||||
BlockId::Latest => Some(self.code.read().get(address).cloned()),
|
||||
BlockId::Latest | BlockId::Pending => Some(self.code.read().get(address).cloned()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn balance(&self, address: &Address, id: BlockId) -> Option<U256> {
|
||||
if let BlockId::Latest = id {
|
||||
Some(self.balances.read().get(address).cloned().unwrap_or_else(U256::zero))
|
||||
} else {
|
||||
None
|
||||
match id {
|
||||
BlockId::Latest | BlockId::Pending => Some(self.balances.read().get(address).cloned().unwrap_or_else(U256::zero)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,10 +463,9 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
}
|
||||
|
||||
fn storage_at(&self, address: &Address, position: &H256, id: BlockId) -> Option<H256> {
|
||||
if let BlockId::Latest = id {
|
||||
Some(self.storage.read().get(&(address.clone(), position.clone())).cloned().unwrap_or_else(H256::new))
|
||||
} else {
|
||||
None
|
||||
match id {
|
||||
BlockId::Latest | BlockId::Pending => Some(self.storage.read().get(&(address.clone(), position.clone())).cloned().unwrap_or_else(H256::new)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,7 +554,8 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
match id {
|
||||
BlockId::Number(number) if (number as usize) < self.blocks.read().len() => BlockStatus::InChain,
|
||||
BlockId::Hash(ref hash) if self.blocks.read().get(hash).is_some() => BlockStatus::InChain,
|
||||
BlockId::Latest | BlockId::Pending | BlockId::Earliest => BlockStatus::InChain,
|
||||
BlockId::Latest | BlockId::Earliest => BlockStatus::InChain,
|
||||
BlockId::Pending => BlockStatus::Pending,
|
||||
_ => BlockStatus::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,11 @@ pub trait BlockChainClient : Sync + Send {
|
||||
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
|
||||
|
||||
/// Makes a non-persistent transaction call.
|
||||
fn call(&self, t: &SignedTransaction, block: BlockId, analytics: CallAnalytics) -> Result<Executed, CallError>;
|
||||
fn call(&self, tx: &SignedTransaction, analytics: CallAnalytics, block: BlockId) -> Result<Executed, CallError>;
|
||||
|
||||
/// Makes multiple non-persistent but dependent transaction calls.
|
||||
/// Returns a vector of successes or a failure if any of the transaction fails.
|
||||
fn call_many(&self, txs: &[(SignedTransaction, CallAnalytics)], block: BlockId) -> Result<Vec<Executed>, CallError>;
|
||||
|
||||
/// Estimates how much gas will be necessary for a call.
|
||||
fn estimate_gas(&self, t: &SignedTransaction, block: BlockId) -> Result<U256, CallError>;
|
||||
|
||||
@@ -1050,7 +1050,7 @@ mod tests {
|
||||
client.miner().import_own_transaction(client.as_ref(), transaction.into()).unwrap();
|
||||
|
||||
// Propose
|
||||
let proposal = Some(client.miner().pending_block().unwrap().header.bare_hash());
|
||||
let proposal = Some(client.miner().pending_block(0).unwrap().header.bare_hash());
|
||||
// Propose timeout
|
||||
engine.step();
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ use std::sync::Arc;
|
||||
use util::*;
|
||||
use using_queue::{UsingQueue, GetAction};
|
||||
use account_provider::{AccountProvider, SignError as AccountError};
|
||||
use state::{State, CleanupMode};
|
||||
use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockId, CallAnalytics, TransactionId};
|
||||
use state::State;
|
||||
use client::{MiningBlockChainClient, BlockId, TransactionId};
|
||||
use client::TransactionImportResult;
|
||||
use executive::contract_address;
|
||||
use block::{ClosedBlock, IsBlock, Block};
|
||||
@@ -39,7 +39,7 @@ use miner::local_transactions::{Status as LocalTransactionStatus};
|
||||
use miner::service_transaction_checker::ServiceTransactionChecker;
|
||||
use price_info::{Client as PriceInfoClient, PriceInfo};
|
||||
use price_info::fetch::Client as FetchClient;
|
||||
use header::BlockNumber;
|
||||
use header::{Header, BlockNumber};
|
||||
|
||||
/// Different possible definitions for pending transaction set.
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -331,13 +331,28 @@ impl Miner {
|
||||
}
|
||||
|
||||
/// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing.
|
||||
pub fn pending_state(&self) -> Option<State<::state_db::StateDB>> {
|
||||
self.sealing_work.lock().queue.peek_last_ref().map(|b| b.block().fields().state.clone())
|
||||
pub fn pending_state(&self, latest_block_number: BlockNumber) -> Option<State<::state_db::StateDB>> {
|
||||
self.map_pending_block(|b| b.state().clone(), latest_block_number)
|
||||
}
|
||||
|
||||
/// Get `Some` `clone()` of the current pending block or `None` if we're not sealing.
|
||||
pub fn pending_block(&self) -> Option<Block> {
|
||||
self.sealing_work.lock().queue.peek_last_ref().map(|b| b.to_base())
|
||||
pub fn pending_block(&self, latest_block_number: BlockNumber) -> Option<Block> {
|
||||
self.map_pending_block(|b| b.to_base(), latest_block_number)
|
||||
}
|
||||
|
||||
/// Get `Some` `clone()` of the current pending block header or `None` if we're not sealing.
|
||||
pub fn pending_block_header(&self, latest_block_number: BlockNumber) -> Option<Header> {
|
||||
self.map_pending_block(|b| b.header().clone(), latest_block_number)
|
||||
}
|
||||
|
||||
fn map_pending_block<F, T>(&self, f: F, latest_block_number: BlockNumber) -> Option<T> where
|
||||
F: FnOnce(&ClosedBlock) -> T,
|
||||
{
|
||||
self.from_pending_block(
|
||||
latest_block_number,
|
||||
|| None,
|
||||
|block| Some(f(block)),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="dev", allow(match_same_arms))]
|
||||
@@ -679,7 +694,7 @@ impl Miner {
|
||||
#[cfg_attr(feature="dev", allow(wrong_self_convention))]
|
||||
#[cfg_attr(feature="dev", allow(redundant_closure))]
|
||||
fn from_pending_block<H, F, G>(&self, latest_block_number: BlockNumber, from_chain: F, map_block: G) -> H
|
||||
where F: Fn() -> H, G: Fn(&ClosedBlock) -> H {
|
||||
where F: Fn() -> H, G: FnOnce(&ClosedBlock) -> H {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
sealing_work.queue.peek_last_ref().map_or_else(
|
||||
|| from_chain(),
|
||||
@@ -717,84 +732,6 @@ impl MinerService for Miner {
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&self, client: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> {
|
||||
let sealing_work = self.sealing_work.lock();
|
||||
match sealing_work.queue.peek_last_ref() {
|
||||
Some(work) => {
|
||||
let block = work.block();
|
||||
|
||||
// TODO: merge this code with client.rs's fn call somwhow.
|
||||
let header = block.header();
|
||||
let last_hashes = Arc::new(client.last_hashes());
|
||||
let env_info = EnvInfo {
|
||||
number: header.number(),
|
||||
author: *header.author(),
|
||||
timestamp: header.timestamp(),
|
||||
difficulty: *header.difficulty(),
|
||||
last_hashes: last_hashes,
|
||||
gas_used: U256::zero(),
|
||||
gas_limit: U256::max_value(),
|
||||
};
|
||||
// that's just a copy of the state.
|
||||
let mut state = block.state().clone();
|
||||
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
|
||||
|
||||
let sender = t.sender();
|
||||
let balance = state.balance(&sender).map_err(ExecutionError::from)?;
|
||||
let needed_balance = t.value + t.gas * t.gas_price;
|
||||
if balance < needed_balance {
|
||||
// give the sender a sufficient balance
|
||||
state.add_balance(&sender, &(needed_balance - balance), CleanupMode::NoEmpty)
|
||||
.map_err(ExecutionError::from)?;
|
||||
}
|
||||
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(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)
|
||||
},
|
||||
None => client.call(t, BlockId::Latest, analytics)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: The `chain.latest_x` actually aren't infallible, they just panic on corruption.
|
||||
// TODO: return trie::Result<T> here, or other.
|
||||
fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<U256> {
|
||||
self.from_pending_block(
|
||||
chain.chain_info().best_block_number,
|
||||
|| Some(chain.latest_balance(address)),
|
||||
|b| b.block().fields().state.balance(address).ok(),
|
||||
)
|
||||
}
|
||||
|
||||
fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> Option<H256> {
|
||||
self.from_pending_block(
|
||||
chain.chain_info().best_block_number,
|
||||
|| Some(chain.latest_storage_at(address, position)),
|
||||
|b| b.block().fields().state.storage_at(address, position).ok(),
|
||||
)
|
||||
}
|
||||
|
||||
fn nonce(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<U256> {
|
||||
self.from_pending_block(
|
||||
chain.chain_info().best_block_number,
|
||||
|| Some(chain.latest_nonce(address)),
|
||||
|b| b.block().fields().state.nonce(address).ok(),
|
||||
)
|
||||
}
|
||||
|
||||
fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<Option<Bytes>> {
|
||||
self.from_pending_block(
|
||||
chain.chain_info().best_block_number,
|
||||
|| Some(chain.latest_code(address)),
|
||||
|b| b.block().fields().state.code(address).ok().map(|c| c.map(|c| (&*c).clone()))
|
||||
)
|
||||
}
|
||||
|
||||
fn set_author(&self, author: Address) {
|
||||
if self.engine.seals_internally().is_some() {
|
||||
let mut sealing_work = self.sealing_work.lock();
|
||||
@@ -1466,14 +1403,14 @@ mod tests {
|
||||
|
||||
miner.update_sealing(&*client);
|
||||
client.flush_queue();
|
||||
assert!(miner.pending_block().is_none());
|
||||
assert!(miner.pending_block(0).is_none());
|
||||
assert_eq!(client.chain_info().best_block_number, 3 as BlockNumber);
|
||||
|
||||
assert_eq!(miner.import_own_transaction(&*client, PendingTransaction::new(transaction_with_network_id(spec.network_id()).into(), None)).unwrap(), TransactionImportResult::Current);
|
||||
|
||||
miner.update_sealing(&*client);
|
||||
client.flush_queue();
|
||||
assert!(miner.pending_block().is_none());
|
||||
assert!(miner.pending_block(0).is_none());
|
||||
assert_eq!(client.chain_info().best_block_number, 4 as BlockNumber);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,12 +62,12 @@ pub use self::stratum::{Stratum, Error as StratumError, Options as StratumOption
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use util::{H256, U256, Address, Bytes};
|
||||
use client::{MiningBlockChainClient, Executed, CallAnalytics};
|
||||
use client::{MiningBlockChainClient};
|
||||
use block::ClosedBlock;
|
||||
use header::BlockNumber;
|
||||
use receipt::{RichReceipt, Receipt};
|
||||
use error::{Error, CallError};
|
||||
use transaction::{UnverifiedTransaction, PendingTransaction, SignedTransaction};
|
||||
use error::{Error};
|
||||
use transaction::{UnverifiedTransaction, PendingTransaction};
|
||||
|
||||
/// Miner client API
|
||||
pub trait MinerService : Send + Sync {
|
||||
@@ -185,21 +185,6 @@ pub trait MinerService : Send + Sync {
|
||||
|
||||
/// Suggested gas limit.
|
||||
fn sensible_gas_limit(&self) -> U256 { 21000.into() }
|
||||
|
||||
/// Latest account balance in pending state.
|
||||
fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<U256>;
|
||||
|
||||
/// Call into contract code using pending state.
|
||||
fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError>;
|
||||
|
||||
/// Get storage value in pending state.
|
||||
fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> Option<H256>;
|
||||
|
||||
/// Get account nonce in pending state.
|
||||
fn nonce(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<U256>;
|
||||
|
||||
/// Get contract code in pending state.
|
||||
fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<Option<Bytes>>;
|
||||
}
|
||||
|
||||
/// Mining status
|
||||
|
||||
Reference in New Issue
Block a user