Supporting blockid in eth_call and trace_call/trace_raw (#1837)

* Supporting blockid in eth_call and trace_call/trace_raw

* Nicer state diff handling

* Purging deref.deref
This commit is contained in:
Tomasz Drwięga 2016-08-04 18:17:21 +02:00 committed by Gav Wood
parent 979f4e0617
commit 25aabe6e52
15 changed files with 154 additions and 117 deletions

4
Cargo.lock generated
View File

@ -1542,13 +1542,13 @@ dependencies = [
"checksum daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "271ec51b7e0bee92f0d04601422c73eb76ececf197026711c97ad25038a010cf" "checksum daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "271ec51b7e0bee92f0d04601422c73eb76ececf197026711c97ad25038a010cf"
"checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf" "checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
"checksum docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4cc0acb4ce0828c6a5a11d47baa432fe885881c27428c3a4e473e454ffe57a76" "checksum docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4cc0acb4ce0828c6a5a11d47baa432fe885881c27428c3a4e473e454ffe57a76"
"checksum elastic-array 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a7ec9af4640023400b86c9ce9126c79ed17ab247ec10a2f530eb78e3893b51aa" "checksum elastic-array 0.4.0 (git+https://github.com/ethcore/elastic-array)" = "<none>"
"checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5" "checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5"
"checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "<none>" "checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "<none>"
"checksum gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "3da3a2cbaeb01363c8e3704fd9fd0eb2ceb17c6f27abd4c1ef040fb57d20dc79" "checksum gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "3da3a2cbaeb01363c8e3704fd9fd0eb2ceb17c6f27abd4c1ef040fb57d20dc79"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1" "checksum hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1"
"checksum heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "927f352867eb72d0ef81b0e2aa457cd9b0888b2d26672cf7ca5912d771215191" "checksum heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "abb306abb8d398e053cfb1b3e7b72c2f580be048b85745c52652954f8ad1439c"
"checksum hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2da7d3a34cf6406d9d700111b8eafafe9a251de41ae71d8052748259343b58" "checksum hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2da7d3a34cf6406d9d700111b8eafafe9a251de41ae71d8052748259343b58"
"checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae" "checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae"
"checksum hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bb0f4d00bb781e559b6e66ae4b5479df0fdf9ab15949f52fa2f1f5de16d4cc07" "checksum hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bb0f4d00bb781e559b6e66ae4b5479df0fdf9ab15949f52fa2f1f5de16d4cc07"

View File

@ -226,7 +226,7 @@ impl BlockQueue {
}; };
let block_hash = block.header.hash(); let block_hash = block.header.hash();
match verify_block_unordered(block.header, block.bytes, engine.deref().deref()) { match verify_block_unordered(block.header, block.bytes, &**engine) {
Ok(verified) => { Ok(verified) => {
let mut verifying = verification.verifying.lock(); let mut verifying = verification.verifying.lock();
for e in verifying.iter_mut() { for e in verifying.iter_mut() {
@ -319,7 +319,7 @@ impl BlockQueue {
} }
} }
match verify_block_basic(&header, &bytes, self.engine.deref().deref()) { match verify_block_basic(&header, &bytes, &**self.engine) {
Ok(()) => { Ok(()) => {
self.processing.write().insert(h.clone()); self.processing.write().insert(h.clone());
self.verification.unverified.lock().push_back(UnverifiedBlock { header: header, bytes: bytes }); self.verification.unverified.lock().push_back(UnverifiedBlock { header: header, bytes: bytes });

View File

@ -15,7 +15,6 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::collections::{HashSet, HashMap, VecDeque}; use std::collections::{HashSet, HashMap, VecDeque};
use std::ops::Deref;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use std::path::{Path}; use std::path::{Path};
use std::fmt; use std::fmt;
@ -35,7 +34,7 @@ use util::kvdb::*;
// other // other
use views::{BlockView, HeaderView, BodyView}; use views::{BlockView, HeaderView, BodyView};
use error::{ImportError, ExecutionError, ReplayError, BlockError, ImportResult}; use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult};
use header::BlockNumber; use header::BlockNumber;
use state::State; use state::State;
use spec::Spec; use spec::Spec;
@ -272,7 +271,7 @@ impl Client {
} }
fn check_and_close_block(&self, block: &PreverifiedBlock) -> Result<LockedBlock, ()> { fn check_and_close_block(&self, block: &PreverifiedBlock) -> Result<LockedBlock, ()> {
let engine = self.engine.deref().deref(); let engine = &**self.engine;
let header = &block.header; let header = &block.header;
// Check the block isn't so old we won't be able to enact it. // Check the block isn't so old we won't be able to enact it.
@ -283,7 +282,7 @@ impl Client {
} }
// Verify Block Family // Verify Block Family
let verify_family_result = self.verifier.verify_block_family(header, &block.bytes, engine, self.chain.deref()); let verify_family_result = self.verifier.verify_block_family(header, &block.bytes, engine, &*self.chain);
if let Err(e) = verify_family_result { if let Err(e) = verify_family_result {
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(()); return Err(());
@ -639,8 +638,8 @@ impl Client {
} }
impl BlockChainClient for Client { impl BlockChainClient for Client {
fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> { fn call(&self, t: &SignedTransaction, block: BlockID, analytics: CallAnalytics) -> Result<Executed, CallError> {
let header = self.block_header(BlockID::Latest).unwrap(); let header = try!(self.block_header(block).ok_or(CallError::StatePruned));
let view = HeaderView::new(&header); let view = HeaderView::new(&header);
let last_hashes = self.build_last_hashes(view.hash()); let last_hashes = self.build_last_hashes(view.hash());
let env_info = EnvInfo { let env_info = EnvInfo {
@ -653,7 +652,9 @@ impl BlockChainClient for Client {
gas_limit: U256::max_value(), gas_limit: U256::max_value(),
}; };
// that's just a copy of the state. // that's just a copy of the state.
let mut state = self.state(); let mut state = try!(self.state_at(block).ok_or(CallError::StatePruned));
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
let sender = try!(t.sender().map_err(|e| { let sender = try!(t.sender().map_err(|e| {
let message = format!("Transaction malformed: {:?}", e); let message = format!("Transaction malformed: {:?}", e);
ExecutionError::TransactionMalformed(message) ExecutionError::TransactionMalformed(message)
@ -665,26 +666,23 @@ impl BlockChainClient for Client {
state.add_balance(&sender, &(needed_balance - balance)); state.add_balance(&sender, &(needed_balance - balance));
} }
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false }; 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.deref().deref(), &self.vm_factory).transact(t, options); let mut ret = try!(Executive::new(&mut state, &env_info, &**self.engine, &self.vm_factory).transact(t, options));
// TODO gav move this into Executive. // TODO gav move this into Executive.
if analytics.state_diffing { ret.state_diff = original_state.map(|original| state.diff_from(original));
if let Ok(ref mut x) = ret {
x.state_diff = Some(state.diff_from(self.state())); Ok(ret)
}
}
ret
} }
fn replay(&self, id: TransactionID, analytics: CallAnalytics) -> Result<Executed, ReplayError> { fn replay(&self, id: TransactionID, analytics: CallAnalytics) -> Result<Executed, CallError> {
let address = try!(self.transaction_address(id).ok_or(ReplayError::TransactionNotFound)); let address = try!(self.transaction_address(id).ok_or(CallError::TransactionNotFound));
let header_data = try!(self.block_header(BlockID::Hash(address.block_hash)).ok_or(ReplayError::StatePruned)); let header_data = try!(self.block_header(BlockID::Hash(address.block_hash)).ok_or(CallError::StatePruned));
let body_data = try!(self.block_body(BlockID::Hash(address.block_hash)).ok_or(ReplayError::StatePruned)); let body_data = try!(self.block_body(BlockID::Hash(address.block_hash)).ok_or(CallError::StatePruned));
let mut state = try!(self.state_at_beginning(BlockID::Hash(address.block_hash)).ok_or(ReplayError::StatePruned)); let mut state = try!(self.state_at_beginning(BlockID::Hash(address.block_hash)).ok_or(CallError::StatePruned));
let txs = BodyView::new(&body_data).transactions(); let txs = BodyView::new(&body_data).transactions();
if address.index >= txs.len() { if address.index >= txs.len() {
return Err(ReplayError::TransactionNotFound); return Err(CallError::TransactionNotFound);
} }
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false }; let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false };
@ -700,20 +698,18 @@ impl BlockChainClient for Client {
gas_limit: view.gas_limit(), gas_limit: view.gas_limit(),
}; };
for t in txs.iter().take(address.index) { for t in txs.iter().take(address.index) {
match Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, Default::default()) { match Executive::new(&mut state, &env_info, &**self.engine, &self.vm_factory).transact(t, Default::default()) {
Ok(x) => { env_info.gas_used = env_info.gas_used + x.gas_used; } Ok(x) => { env_info.gas_used = env_info.gas_used + x.gas_used; }
Err(ee) => { return Err(ReplayError::Execution(ee)) } Err(ee) => { return Err(CallError::Execution(ee)) }
} }
} }
let t = &txs[address.index]; let t = &txs[address.index];
let orig = state.clone();
let mut ret = Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options); let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
if analytics.state_diffing { let mut ret = try!(Executive::new(&mut state, &env_info, &**self.engine, &self.vm_factory).transact(t, options));
if let Ok(ref mut x) = ret { ret.state_diff = original_state.map(|original| state.diff_from(original));
x.state_diff = Some(state.diff_from(orig));
} Ok(ret)
}
ret.map_err(ReplayError::Execution)
} }
fn keep_alive(&self) { fn keep_alive(&self) {
@ -1002,7 +998,7 @@ impl BlockChainClient for Client {
impl MiningBlockChainClient for Client { impl MiningBlockChainClient for Client {
fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock {
let engine = self.engine.deref().deref(); let engine = &**self.engine;
let h = self.chain.best_block_hash(); let h = self.chain.best_block_hash();
let mut open_block = OpenBlock::new( let mut open_block = OpenBlock::new(

View File

@ -37,7 +37,7 @@ use spec::Spec;
use block_queue::BlockQueueInfo; use block_queue::BlockQueueInfo;
use block::{OpenBlock, SealedBlock}; use block::{OpenBlock, SealedBlock};
use executive::Executed; use executive::Executed;
use error::{ExecutionError, ReplayError}; use error::CallError;
use trace::LocalizedTrace; use trace::LocalizedTrace;
/// Test client. /// Test client.
@ -61,7 +61,7 @@ pub struct TestBlockChainClient {
/// Code. /// Code.
pub code: RwLock<HashMap<Address, Bytes>>, pub code: RwLock<HashMap<Address, Bytes>>,
/// Execution result. /// Execution result.
pub execution_result: RwLock<Option<Executed>>, pub execution_result: RwLock<Option<Result<Executed, CallError>>>,
/// Transaction receipts. /// Transaction receipts.
pub receipts: RwLock<HashMap<TransactionID, LocalizedReceipt>>, pub receipts: RwLock<HashMap<TransactionID, LocalizedReceipt>>,
/// Block queue size. /// Block queue size.
@ -125,7 +125,7 @@ impl TestBlockChainClient {
} }
/// Set the execution result. /// Set the execution result.
pub fn set_execution_result(&self, result: Executed) { pub fn set_execution_result(&self, result: Result<Executed, CallError>) {
*self.execution_result.write() = Some(result); *self.execution_result.write() = Some(result);
} }
@ -292,12 +292,12 @@ impl MiningBlockChainClient for TestBlockChainClient {
} }
impl BlockChainClient for TestBlockChainClient { impl BlockChainClient for TestBlockChainClient {
fn call(&self, _t: &SignedTransaction, _analytics: CallAnalytics) -> Result<Executed, ExecutionError> { fn call(&self, _t: &SignedTransaction, _block: BlockID, _analytics: CallAnalytics) -> Result<Executed, CallError> {
Ok(self.execution_result.read().clone().unwrap()) self.execution_result.read().clone().unwrap()
} }
fn replay(&self, _id: TransactionID, _analytics: CallAnalytics) -> Result<Executed, ReplayError> { fn replay(&self, _id: TransactionID, _analytics: CallAnalytics) -> Result<Executed, CallError> {
Ok(self.execution_result.read().clone().unwrap()) self.execution_result.read().clone().unwrap()
} }
fn block_total_difficulty(&self, _id: BlockID) -> Option<U256> { fn block_total_difficulty(&self, _id: BlockID) -> Option<U256> {

View File

@ -26,7 +26,7 @@ use transaction::{LocalizedTransaction, SignedTransaction};
use log_entry::LocalizedLogEntry; use log_entry::LocalizedLogEntry;
use filter::Filter; use filter::Filter;
use views::{BlockView}; use views::{BlockView};
use error::{ImportResult, ExecutionError, ReplayError}; use error::{ImportResult, CallError};
use receipt::LocalizedReceipt; use receipt::LocalizedReceipt;
use trace::LocalizedTrace; use trace::LocalizedTrace;
use evm::Factory as EvmFactory; use evm::Factory as EvmFactory;
@ -154,11 +154,10 @@ pub trait BlockChainClient : Sync + Send {
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>; fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
/// Makes a non-persistent transaction call. /// Makes a non-persistent transaction call.
// TODO: should be able to accept blockchain location for call. fn call(&self, t: &SignedTransaction, block: BlockID, analytics: CallAnalytics) -> Result<Executed, CallError>;
fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError>;
/// Replays a given transaction for inspection. /// Replays a given transaction for inspection.
fn replay(&self, t: TransactionID, analytics: CallAnalytics) -> Result<Executed, ReplayError>; fn replay(&self, t: TransactionID, analytics: CallAnalytics) -> Result<Executed, CallError>;
/// Returns traces matching given filter. /// Returns traces matching given filter.
fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>>; fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>>;

View File

@ -22,7 +22,7 @@ use basic_types::LogBloom;
use client::Error as ClientError; use client::Error as ClientError;
use ipc::binary::{BinaryConvertError, BinaryConvertable}; use ipc::binary::{BinaryConvertError, BinaryConvertable};
use types::block_import_error::BlockImportError; use types::block_import_error::BlockImportError;
pub use types::executed::{ExecutionError, ReplayError}; pub use types::executed::{ExecutionError, CallError};
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
/// Errors concerning transaction processing. /// Errors concerning transaction processing.

View File

@ -171,7 +171,7 @@ pub struct Miner {
// for sealing... // for sealing...
options: MinerOptions, options: MinerOptions,
next_allowed_reseal: Mutex<Instant>, next_allowed_reseal: Mutex<Instant>,
sealing_block_last_request: Mutex<u64>, sealing_block_last_request: Mutex<u64>,
gas_range_target: RwLock<(U256, U256)>, gas_range_target: RwLock<(U256, U256)>,
@ -420,7 +420,7 @@ impl Miner {
*sealing_block_last_request = best_number; *sealing_block_last_request = best_number;
} }
// Return if we restarted // Return if we restarted
prepare_new prepare_new
} }
@ -464,7 +464,7 @@ impl MinerService for Miner {
} }
} }
fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError> { fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError> {
let sealing_work = self.sealing_work.lock(); let sealing_work = self.sealing_work.lock();
match sealing_work.queue.peek_last_ref() { match sealing_work.queue.peek_last_ref() {
Some(work) => { Some(work) => {
@ -484,6 +484,8 @@ impl MinerService for Miner {
}; };
// that's just a copy of the state. // that's just a copy of the state.
let mut state = block.state().clone(); let mut state = block.state().clone();
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None };
let sender = try!(t.sender().map_err(|e| { let sender = try!(t.sender().map_err(|e| {
let message = format!("Transaction malformed: {:?}", e); let message = format!("Transaction malformed: {:?}", e);
ExecutionError::TransactionMalformed(message) ExecutionError::TransactionMalformed(message)
@ -495,18 +497,15 @@ impl MinerService for Miner {
state.add_balance(&sender, &(needed_balance - balance)); state.add_balance(&sender, &(needed_balance - balance));
} }
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false }; 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(), chain.vm_factory()).transact(t, options); let mut ret = try!(Executive::new(&mut state, &env_info, self.engine(), chain.vm_factory()).transact(t, options));
// TODO gav move this into Executive. // TODO gav move this into Executive.
if analytics.state_diffing { ret.state_diff = original_state.map(|original| state.diff_from(original));
if let Ok(ref mut x) = ret {
x.state_diff = Some(state.diff_from(block.state().clone())); Ok(ret)
}
}
ret
}, },
None => { None => {
chain.call(t, analytics) chain.call(t, BlockID::Latest, analytics)
} }
} }
} }
@ -770,7 +769,7 @@ impl MinerService for Miner {
false false
} }
}; };
if requires_reseal { if requires_reseal {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// | NOTE Code below requires transaction_queue and sealing_work locks. | // | NOTE Code below requires transaction_queue and sealing_work locks. |

View File

@ -58,7 +58,7 @@ use util::{H256, U256, Address, Bytes};
use client::{MiningBlockChainClient, Executed, CallAnalytics}; use client::{MiningBlockChainClient, Executed, CallAnalytics};
use block::ClosedBlock; use block::ClosedBlock;
use receipt::Receipt; use receipt::Receipt;
use error::{Error, ExecutionError}; use error::{Error, CallError};
use transaction::SignedTransaction; use transaction::SignedTransaction;
/// Miner client API /// Miner client API
@ -163,7 +163,7 @@ pub trait MinerService : Send + Sync {
fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256; fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256;
/// Call into contract code using pending state. /// Call into contract code using pending state.
fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, ExecutionError>; fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result<Executed, CallError>;
/// Get storage value in pending state. /// Get storage value in pending state.
fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256; fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256;

View File

@ -105,7 +105,7 @@ pub struct Executed {
} }
/// Result of executing the transaction. /// Result of executing the transaction.
#[derive(PartialEq, Debug, Binary)] #[derive(PartialEq, Debug, Clone, Binary)]
pub enum ExecutionError { pub enum ExecutionError {
/// Returned when there gas paid for transaction execution is /// Returned when there gas paid for transaction execution is
/// lower than base gas required. /// lower than base gas required.
@ -172,19 +172,25 @@ impl fmt::Display for ExecutionError {
} }
/// Result of executing the transaction. /// Result of executing the transaction.
#[derive(PartialEq, Debug, Binary)] #[derive(PartialEq, Debug, Clone, Binary)]
pub enum ReplayError { pub enum CallError {
/// Couldn't find the transaction in the chain. /// Couldn't find the transaction in the chain.
TransactionNotFound, TransactionNotFound,
/// Couldn't find the transaction block's state in the chain. /// Couldn't find requested block's state in the chain.
StatePruned, StatePruned,
/// Error executing. /// Error executing.
Execution(ExecutionError), Execution(ExecutionError),
} }
impl fmt::Display for ReplayError { impl From<ExecutionError> for CallError {
fn from(error: ExecutionError) -> Self {
CallError::Execution(error)
}
}
impl fmt::Display for CallError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::ReplayError::*; use self::CallError::*;
let msg = match *self { let msg = match *self {
TransactionNotFound => "Transaction couldn't be found in the chain".into(), TransactionNotFound => "Transaction couldn't be found in the chain".into(),
@ -192,7 +198,7 @@ impl fmt::Display for ReplayError {
Execution(ref e) => format!("{}", e), Execution(ref e) => format!("{}", e),
}; };
f.write_fmt(format_args!("Transaction replay error ({}).", msg)) f.write_fmt(format_args!("Transaction execution error ({}).", msg))
} }
} }

View File

@ -23,7 +23,7 @@ use std::mem;
use std::collections::VecDeque; use std::collections::VecDeque;
/// Uniquely identifies block. /// Uniquely identifies block.
#[derive(Debug, PartialEq, Clone, Hash, Eq, Binary)] #[derive(Debug, PartialEq, Copy, Clone, Hash, Eq, Binary)]
pub enum BlockID { pub enum BlockID {
/// Block's sha3. /// Block's sha3.
/// Querying by hash is always faster. /// Querying by hash is always faster.

View File

@ -44,8 +44,7 @@ use self::ethash::SeedHashCompute;
use v1::traits::Eth; use v1::traits::Eth;
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, Index, Filter, Log, Receipt, H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256}; use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, Index, Filter, Log, Receipt, H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256};
use v1::helpers::CallRequest as CRequest; use v1::helpers::CallRequest as CRequest;
use v1::impls::{default_gas_price, dispatch_transaction, error_codes}; use v1::impls::{default_gas_price, dispatch_transaction, error_codes, from_params_default_second, from_params_default_third};
use serde;
/// Eth RPC options /// Eth RPC options
pub struct EthClientOptions { pub struct EthClientOptions {
@ -215,27 +214,6 @@ pub fn pending_logs<M>(miner: &M, filter: &EthcoreFilter) -> Vec<Log> where M: M
const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6. const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6.
fn params_len(params: &Params) -> usize {
match params {
&Params::Array(ref vec) => vec.len(),
_ => 0,
}
}
fn from_params_default_second<F>(params: Params) -> Result<(F, BlockNumber, ), Error> where F: serde::de::Deserialize {
match params_len(&params) {
1 => from_params::<(F, )>(params).map(|(f,)| (f, BlockNumber::Latest)),
_ => from_params::<(F, BlockNumber)>(params),
}
}
fn from_params_default_third<F1, F2>(params: Params) -> Result<(F1, F2, BlockNumber, ), Error> where F1: serde::de::Deserialize, F2: serde::de::Deserialize {
match params_len(&params) {
2 => from_params::<(F1, F2, )>(params).map(|(f1, f2)| (f1, f2, BlockNumber::Latest)),
_ => from_params::<(F1, F2, BlockNumber)>(params)
}
}
fn make_unsupported_err() -> Error { fn make_unsupported_err() -> Error {
Error { Error {
code: ErrorCode::ServerError(error_codes::UNSUPPORTED_REQUEST_CODE), code: ErrorCode::ServerError(error_codes::UNSUPPORTED_REQUEST_CODE),
@ -656,8 +634,7 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
let signed = try!(self.sign_call(request)); let signed = try!(self.sign_call(request));
let r = match block_number { let r = match block_number {
BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, Default::default()), BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, Default::default()),
BlockNumber::Latest => take_weak!(self.client).call(&signed, Default::default()), block_number => take_weak!(self.client).call(&signed, block_number.into(), Default::default()),
_ => panic!("{:?}", block_number),
}; };
to_value(&r.map(|e| Bytes(e.output)).unwrap_or(Bytes::new(vec![]))) to_value(&r.map(|e| Bytes(e.output)).unwrap_or(Bytes::new(vec![])))
}) })
@ -671,8 +648,7 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
let signed = try!(self.sign_call(request)); let signed = try!(self.sign_call(request));
let r = match block_number { let r = match block_number {
BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, Default::default()), BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, Default::default()),
BlockNumber::Latest => take_weak!(self.client).call(&signed, Default::default()), block => take_weak!(self.client).call(&signed, block.into(), Default::default()),
_ => return Err(Error::invalid_params()),
}; };
to_value(&RpcU256::from(r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0)))) to_value(&RpcU256::from(r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0))))
}) })

View File

@ -53,8 +53,9 @@ pub use self::ethcore_set::EthcoreSetClient;
pub use self::traces::TracesClient; pub use self::traces::TracesClient;
pub use self::rpc::RpcClient; pub use self::rpc::RpcClient;
use serde;
use v1::helpers::TransactionRequest; use v1::helpers::TransactionRequest;
use v1::types::{H256 as RpcH256, H520 as RpcH520}; use v1::types::{H256 as RpcH256, H520 as RpcH520, BlockNumber};
use ethcore::error::Error as EthcoreError; use ethcore::error::Error as EthcoreError;
use ethcore::miner::MinerService; use ethcore::miner::MinerService;
use ethcore::client::MiningBlockChainClient; use ethcore::client::MiningBlockChainClient;
@ -63,7 +64,7 @@ use ethcore::account_provider::{AccountProvider, Error as AccountError};
use util::numbers::*; use util::numbers::*;
use util::rlp::encode; use util::rlp::encode;
use util::bytes::ToPretty; use util::bytes::ToPretty;
use jsonrpc_core::{Error, ErrorCode, Value, to_value}; use jsonrpc_core::{Error, ErrorCode, Value, to_value, from_params, Params};
mod error_codes { mod error_codes {
// NOTE [ToDr] Codes from [-32099, -32000] // NOTE [ToDr] Codes from [-32099, -32000]
@ -79,6 +80,30 @@ mod error_codes {
pub const REQUEST_NOT_FOUND: i64 = -32041; pub const REQUEST_NOT_FOUND: i64 = -32041;
} }
fn params_len(params: &Params) -> usize {
match params {
&Params::Array(ref vec) => vec.len(),
_ => 0,
}
}
/// Deserialize request parameters with optional second parameter `BlockNumber` defaulting to `BlockNumber::Latest`.
pub fn from_params_default_second<F>(params: Params) -> Result<(F, BlockNumber, ), Error> where F: serde::de::Deserialize {
match params_len(&params) {
1 => from_params::<(F, )>(params).map(|(f,)| (f, BlockNumber::Latest)),
_ => from_params::<(F, BlockNumber)>(params),
}
}
/// Deserialize request parameters with optional third parameter `BlockNumber` defaulting to `BlockNumber::Latest`.
pub fn from_params_default_third<F1, F2>(params: Params) -> Result<(F1, F2, BlockNumber, ), Error> where F1: serde::de::Deserialize, F2: serde::de::Deserialize {
match params_len(&params) {
2 => from_params::<(F1, F2, )>(params).map(|(f1, f2)| (f1, f2, BlockNumber::Latest)),
_ => from_params::<(F1, F2, BlockNumber)>(params)
}
}
fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result<Value, Error> fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result<Value, Error>
where C: MiningBlockChainClient, M: MinerService { where C: MiningBlockChainClient, M: MinerService {
let hash = RpcH256::from(signed_transaction.hash()); let hash = RpcH256::from(signed_transaction.hash());

View File

@ -25,6 +25,7 @@ use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Act
use v1::traits::Traces; use v1::traits::Traces;
use v1::helpers::CallRequest as CRequest; use v1::helpers::CallRequest as CRequest;
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, H256}; use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, H256};
use v1::impls::from_params_default_third;
fn to_call_analytics(flags: Vec<String>) -> CallAnalytics { fn to_call_analytics(flags: Vec<String>) -> CallAnalytics {
CallAnalytics { CallAnalytics {
@ -122,11 +123,11 @@ impl<C, M> Traces for TracesClient<C, M> where C: BlockChainClient + 'static, M:
fn call(&self, params: Params) -> Result<Value, Error> { fn call(&self, params: Params) -> Result<Value, Error> {
try!(self.active()); try!(self.active());
from_params(params) from_params_default_third(params)
.and_then(|(request, flags)| { .and_then(|(request, flags, block)| {
let request = CallRequest::into(request); let request = CallRequest::into(request);
let signed = try!(self.sign_call(request)); let signed = try!(self.sign_call(request));
match take_weak!(self.client).call(&signed, to_call_analytics(flags)) { match take_weak!(self.client).call(&signed, block.into(), to_call_analytics(flags)) {
Ok(e) => to_value(&TraceResults::from(e)), Ok(e) => to_value(&TraceResults::from(e)),
_ => Ok(Value::Null), _ => Ok(Value::Null),
} }
@ -135,11 +136,11 @@ impl<C, M> Traces for TracesClient<C, M> where C: BlockChainClient + 'static, M:
fn raw_transaction(&self, params: Params) -> Result<Value, Error> { fn raw_transaction(&self, params: Params) -> Result<Value, Error> {
try!(self.active()); try!(self.active());
from_params::<(Bytes, _)>(params) from_params_default_third(params)
.and_then(|(raw_transaction, flags)| { .and_then(|(raw_transaction, flags, block)| {
let raw_transaction = raw_transaction.to_vec(); let raw_transaction = Bytes::to_vec(raw_transaction);
match UntrustedRlp::new(&raw_transaction).as_val() { match UntrustedRlp::new(&raw_transaction).as_val() {
Ok(signed) => match take_weak!(self.client).call(&signed, to_call_analytics(flags)) { Ok(signed) => match take_weak!(self.client).call(&signed, block.into(), to_call_analytics(flags)) {
Ok(e) => to_value(&TraceResults::from(e)), Ok(e) => to_value(&TraceResults::from(e)),
_ => Ok(Value::Null), _ => Ok(Value::Null),
}, },

View File

@ -18,7 +18,7 @@
use util::{Address, H256, Bytes, U256, FixedHash, Uint}; use util::{Address, H256, Bytes, U256, FixedHash, Uint};
use util::standard::*; use util::standard::*;
use ethcore::error::{Error, ExecutionError}; use ethcore::error::{Error, CallError};
use ethcore::client::{MiningBlockChainClient, Executed, CallAnalytics}; use ethcore::client::{MiningBlockChainClient, Executed, CallAnalytics};
use ethcore::block::{ClosedBlock, IsBlock}; use ethcore::block::{ClosedBlock, IsBlock};
use ethcore::transaction::SignedTransaction; use ethcore::transaction::SignedTransaction;
@ -220,7 +220,7 @@ impl MinerService for TestMinerService {
self.latest_closed_block.lock().as_ref().map_or_else(U256::zero, |b| b.block().fields().state.balance(address).clone()) self.latest_closed_block.lock().as_ref().map_or_else(U256::zero, |b| b.block().fields().state.balance(address).clone())
} }
fn call(&self, _chain: &MiningBlockChainClient, _t: &SignedTransaction, _analytics: CallAnalytics) -> Result<Executed, ExecutionError> { fn call(&self, _chain: &MiningBlockChainClient, _t: &SignedTransaction, _analytics: CallAnalytics) -> Result<Executed, CallError> {
unimplemented!(); unimplemented!();
} }

View File

@ -423,9 +423,9 @@ fn rpc_eth_code() {
} }
#[test] #[test]
fn rpc_eth_call() { fn rpc_eth_call_latest() {
let tester = EthTester::default(); let tester = EthTester::default();
tester.client.set_execution_result(Executed { tester.client.set_execution_result(Ok(Executed {
gas: U256::zero(), gas: U256::zero(),
gas_used: U256::from(0xff30), gas_used: U256::from(0xff30),
refunded: U256::from(0x5), refunded: U256::from(0x5),
@ -436,7 +436,7 @@ fn rpc_eth_call() {
trace: vec![], trace: vec![],
vm_trace: None, vm_trace: None,
state_diff: None, state_diff: None,
}); }));
let request = r#"{ let request = r#"{
"jsonrpc": "2.0", "jsonrpc": "2.0",
@ -458,9 +458,9 @@ fn rpc_eth_call() {
} }
#[test] #[test]
fn rpc_eth_call_default_block() { fn rpc_eth_call() {
let tester = EthTester::default(); let tester = EthTester::default();
tester.client.set_execution_result(Executed { tester.client.set_execution_result(Ok(Executed {
gas: U256::zero(), gas: U256::zero(),
gas_used: U256::from(0xff30), gas_used: U256::from(0xff30),
refunded: U256::from(0x5), refunded: U256::from(0x5),
@ -471,7 +471,42 @@ fn rpc_eth_call_default_block() {
trace: vec![], trace: vec![],
vm_trace: None, vm_trace: None,
state_diff: None, state_diff: None,
}); }));
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_call",
"params": [{
"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a",
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
},
"0x0"],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x1234ff","id":1}"#;
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
}
#[test]
fn rpc_eth_call_default_block() {
let tester = EthTester::default();
tester.client.set_execution_result(Ok(Executed {
gas: U256::zero(),
gas_used: U256::from(0xff30),
refunded: U256::from(0x5),
cumulative_gas_used: U256::zero(),
logs: vec![],
contracts_created: vec![],
output: vec![0x12, 0x34, 0xff],
trace: vec![],
vm_trace: None,
state_diff: None,
}));
let request = r#"{ let request = r#"{
"jsonrpc": "2.0", "jsonrpc": "2.0",
@ -494,7 +529,7 @@ fn rpc_eth_call_default_block() {
#[test] #[test]
fn rpc_eth_estimate_gas() { fn rpc_eth_estimate_gas() {
let tester = EthTester::default(); let tester = EthTester::default();
tester.client.set_execution_result(Executed { tester.client.set_execution_result(Ok(Executed {
gas: U256::zero(), gas: U256::zero(),
gas_used: U256::from(0xff30), gas_used: U256::from(0xff30),
refunded: U256::from(0x5), refunded: U256::from(0x5),
@ -505,7 +540,7 @@ fn rpc_eth_estimate_gas() {
trace: vec![], trace: vec![],
vm_trace: None, vm_trace: None,
state_diff: None, state_diff: None,
}); }));
let request = r#"{ let request = r#"{
"jsonrpc": "2.0", "jsonrpc": "2.0",
@ -529,7 +564,7 @@ fn rpc_eth_estimate_gas() {
#[test] #[test]
fn rpc_eth_estimate_gas_default_block() { fn rpc_eth_estimate_gas_default_block() {
let tester = EthTester::default(); let tester = EthTester::default();
tester.client.set_execution_result(Executed { tester.client.set_execution_result(Ok(Executed {
gas: U256::zero(), gas: U256::zero(),
gas_used: U256::from(0xff30), gas_used: U256::from(0xff30),
refunded: U256::from(0x5), refunded: U256::from(0x5),
@ -540,7 +575,7 @@ fn rpc_eth_estimate_gas_default_block() {
trace: vec![], trace: vec![],
vm_trace: None, vm_trace: None,
state_diff: None, state_diff: None,
}); }));
let request = r#"{ let request = r#"{
"jsonrpc": "2.0", "jsonrpc": "2.0",