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:
Tomasz Drwięga
2017-08-04 15:58:14 +02:00
committed by Marek Kotewicz
parent 62153b1ff0
commit f157461ee1
22 changed files with 308 additions and 271 deletions

View File

@@ -264,6 +264,7 @@ fn check_known<C>(client: &C, number: BlockNumber) -> Result<(), Error> where C:
match client.block_status(number.into()) {
BlockStatus::InChain => Ok(()),
BlockStatus::Pending => Ok(()),
_ => Err(errors::unknown_block()),
}
}
@@ -361,20 +362,12 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
fn balance(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
let address = address.into();
let res = match num.unwrap_or_default() {
BlockNumber::Pending => {
match self.miner.balance(&*self.client, &address) {
Some(balance) => Ok(balance.into()),
None => Err(errors::database("latest balance missing"))
}
}
id => {
try_bf!(check_known(&*self.client, id.clone()));
match self.client.balance(&address, id.into()) {
Some(balance) => Ok(balance.into()),
None => Err(errors::state_pruned()),
}
}
let id = num.unwrap_or_default();
try_bf!(check_known(&*self.client, id.clone()));
let res = match self.client.balance(&address, id.into()) {
Some(balance) => Ok(balance.into()),
None => Err(errors::state_pruned()),
};
future::done(res).boxed()
@@ -384,20 +377,12 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
let address: Address = RpcH160::into(address);
let position: U256 = RpcU256::into(pos);
let res = match num.unwrap_or_default() {
BlockNumber::Pending => {
match self.miner.storage_at(&*self.client, &address, &H256::from(position)) {
Some(s) => Ok(s.into()),
None => Err(errors::database("latest storage missing"))
}
}
id => {
try_bf!(check_known(&*self.client, id.clone()));
match self.client.storage_at(&address, &H256::from(position), id.into()) {
Some(s) => Ok(s.into()),
None => Err(errors::state_pruned()),
}
}
let id = num.unwrap_or_default();
try_bf!(check_known(&*self.client, id.clone()));
let res = match self.client.storage_at(&address, &H256::from(position), id.into()) {
Some(s) => Ok(s.into()),
None => Err(errors::state_pruned()),
};
future::done(res).boxed()
@@ -410,18 +395,12 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
BlockNumber::Pending if self.options.pending_nonce_from_queue => {
let nonce = self.miner.last_nonce(&address)
.map(|n| n + 1.into())
.or_else(|| self.miner.nonce(&*self.client, &address));
.or_else(|| self.client.nonce(&address, BlockNumber::Pending.into()));
match nonce {
Some(nonce) => Ok(nonce.into()),
None => Err(errors::database("latest nonce missing"))
}
}
BlockNumber::Pending => {
match self.miner.nonce(&*self.client, &address) {
Some(nonce) => Ok(nonce.into()),
None => Err(errors::database("latest nonce missing"))
}
}
id => {
try_bf!(check_known(&*self.client, id.clone()));
match self.client.nonce(&address, id.into()) {
@@ -468,20 +447,12 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
fn code_at(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<Bytes, Error> {
let address: Address = RpcH160::into(address);
let res = match num.unwrap_or_default() {
BlockNumber::Pending => {
match self.miner.code(&*self.client, &address) {
Some(code) => Ok(code.map_or_else(Bytes::default, Bytes::new)),
None => Err(errors::database("latest code missing"))
}
}
id => {
try_bf!(check_known(&*self.client, id.clone()));
match self.client.code(&address, id.into()) {
Some(code) => Ok(code.map_or_else(Bytes::default, Bytes::new)),
None => Err(errors::state_pruned()),
}
}
let id = num.unwrap_or_default();
try_bf!(check_known(&*self.client, id.clone()));
let res = match self.client.code(&address, id.into()) {
Some(code) => Ok(code.map_or_else(Bytes::default, Bytes::new)),
None => Err(errors::state_pruned()),
};
future::done(res).boxed()
@@ -648,10 +619,8 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
Err(e) => return future::err(e).boxed(),
};
let result = match num.unwrap_or_default() {
BlockNumber::Pending => self.miner.call(&*self.client, &signed, Default::default()),
num => self.client.call(&signed, num.into(), Default::default()),
};
let num = num.unwrap_or_default();
let result = self.client.call(&signed, Default::default(), num.into());
future::done(result
.map(|b| b.output.into())

View File

@@ -39,7 +39,7 @@ use v1::helpers::light_fetch::LightFetch;
use v1::metadata::Metadata;
use v1::traits::Parity;
use v1::types::{
Bytes, U256, H160, H256, H512,
Bytes, U256, H160, H256, H512, CallRequest,
Peers, Transaction, RpcSettings, Histogram,
TransactionStats, LocalTransactionStatus,
BlockNumber, ConsensusCapability, VersionInfo,
@@ -389,4 +389,8 @@ impl Parity for ParityClient {
fn ipfs_cid(&self, content: Bytes) -> Result<String, Error> {
ipfs::cid(content)
}
fn call(&self, _requests: Vec<CallRequest>, _block: Trailing<BlockNumber>) -> BoxFuture<Vec<Bytes>, Error> {
future::err(errors::light_unimplemented(None)).boxed()
}
}

View File

@@ -20,7 +20,7 @@ use jsonrpc_core::Error;
use jsonrpc_macros::Trailing;
use v1::traits::Traces;
use v1::helpers::errors;
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, H256};
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, TraceOptions, H256};
/// Traces api implementation.
// TODO: all calling APIs should be possible w. proved remote TX execution.
@@ -43,15 +43,19 @@ impl Traces for TracesClient {
Err(errors::light_unimplemented(None))
}
fn call(&self, _request: CallRequest, _flags: Vec<String>, _block: Trailing<BlockNumber>) -> Result<TraceResults, Error> {
fn call(&self, _request: CallRequest, _flags: TraceOptions, _block: Trailing<BlockNumber>) -> Result<TraceResults, Error> {
Err(errors::light_unimplemented(None))
}
fn raw_transaction(&self, _raw_transaction: Bytes, _flags: Vec<String>, _block: Trailing<BlockNumber>) -> Result<TraceResults, Error> {
fn call_many(&self, _request: Vec<(CallRequest, TraceOptions)>, _block: Trailing<BlockNumber>) -> Result<Vec<TraceResults>, Error> {
Err(errors::light_unimplemented(None))
}
fn replay_transaction(&self, _transaction_hash: H256, _flags: Vec<String>) -> Result<TraceResults, Error> {
fn raw_transaction(&self, _raw_transaction: Bytes, _flags: TraceOptions, _block: Trailing<BlockNumber>) -> Result<TraceResults, Error> {
Err(errors::light_unimplemented(None))
}
fn replay_transaction(&self, _transaction_hash: H256, _flags: TraceOptions) -> Result<TraceResults, Error> {
Err(errors::light_unimplemented(None))
}
}

View File

@@ -28,22 +28,23 @@ use crypto::ecies;
use ethkey::{Brain, Generator};
use ethstore::random_phrase;
use ethsync::{SyncProvider, ManageNetwork};
use ethcore::account_provider::AccountProvider;
use ethcore::client::{MiningBlockChainClient};
use ethcore::ids::BlockId;
use ethcore::miner::MinerService;
use ethcore::client::{MiningBlockChainClient};
use ethcore::mode::Mode;
use ethcore::account_provider::AccountProvider;
use ethcore::transaction::SignedTransaction;
use updater::{Service as UpdateService};
use crypto::DEFAULT_MAC;
use jsonrpc_core::Error;
use jsonrpc_macros::Trailing;
use v1::helpers::{self, errors, ipfs, SigningQueue, SignerService, NetworkSettings};
use v1::helpers::{self, errors, fake_sign, ipfs, SigningQueue, SignerService, NetworkSettings};
use v1::helpers::accounts::unwrap_provider;
use v1::metadata::Metadata;
use v1::traits::Parity;
use v1::types::{
Bytes, U256, H160, H256, H512,
Bytes, U256, H160, H256, H512, CallRequest,
Peers, Transaction, RpcSettings, Histogram,
TransactionStats, LocalTransactionStatus,
BlockNumber, ConsensusCapability, VersionInfo,
@@ -409,4 +410,23 @@ impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
fn ipfs_cid(&self, content: Bytes) -> Result<String, Error> {
ipfs::cid(content)
}
fn call(&self, requests: Vec<CallRequest>, block: Trailing<BlockNumber>) -> BoxFuture<Vec<Bytes>, Error> {
let requests: Result<Vec<(SignedTransaction, _)>, Error> = requests
.into_iter()
.map(|request| Ok((
fake_sign::sign_call(&self.client, &self.miner, request.into())?,
Default::default()
)))
.collect();
let block = block.unwrap_or_default();
let requests = try_bf!(requests);
let result = self.client.call_many(&requests, block.into())
.map(|res| res.into_iter().map(|res| res.output.into()).collect())
.map_err(errors::call);
future::done(result).boxed()
}
}

View File

@@ -27,9 +27,9 @@ use jsonrpc_core::Error;
use jsonrpc_macros::Trailing;
use v1::traits::Traces;
use v1::helpers::{errors, fake_sign};
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, H256};
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, TraceOptions, H256};
fn to_call_analytics(flags: Vec<String>) -> CallAnalytics {
fn to_call_analytics(flags: TraceOptions) -> CallAnalytics {
CallAnalytics {
transaction_tracing: flags.contains(&("trace".to_owned())),
vm_tracing: flags.contains(&("vmTrace".to_owned())),
@@ -79,29 +79,45 @@ impl<C, M> Traces for TracesClient<C, M> where C: MiningBlockChainClient + 'stat
.map(LocalizedTrace::from))
}
fn call(&self, request: CallRequest, flags: Vec<String>, block: Trailing<BlockNumber>) -> Result<TraceResults, Error> {
fn call(&self, request: CallRequest, flags: TraceOptions, block: Trailing<BlockNumber>) -> Result<TraceResults, Error> {
let block = block.unwrap_or_default();
let request = CallRequest::into(request);
let signed = fake_sign::sign_call(&self.client, &self.miner, request)?;
self.client.call(&signed, block.into(), to_call_analytics(flags))
self.client.call(&signed, to_call_analytics(flags), block.into())
.map(TraceResults::from)
.map_err(errors::call)
}
fn raw_transaction(&self, raw_transaction: Bytes, flags: Vec<String>, block: Trailing<BlockNumber>) -> Result<TraceResults, Error> {
fn call_many(&self, requests: Vec<(CallRequest, TraceOptions)>, block: Trailing<BlockNumber>) -> Result<Vec<TraceResults>, Error> {
let block = block.unwrap_or_default();
let requests = requests.into_iter()
.map(|(request, flags)| {
let request = CallRequest::into(request);
let signed = fake_sign::sign_call(&self.client, &self.miner, request)?;
Ok((signed, to_call_analytics(flags)))
})
.collect::<Result<Vec<_>, _>>()?;
self.client.call_many(&requests, block.into())
.map(|results| results.into_iter().map(TraceResults::from).collect())
.map_err(errors::call)
}
fn raw_transaction(&self, raw_transaction: Bytes, flags: TraceOptions, block: Trailing<BlockNumber>) -> Result<TraceResults, Error> {
let block = block.unwrap_or_default();
let tx = UntrustedRlp::new(&raw_transaction.into_vec()).as_val().map_err(|e| errors::invalid_params("Transaction is not valid RLP", e))?;
let signed = SignedTransaction::new(tx).map_err(errors::transaction)?;
self.client.call(&signed, block.into(), to_call_analytics(flags))
self.client.call(&signed, to_call_analytics(flags), block.into())
.map(TraceResults::from)
.map_err(errors::call)
}
fn replay_transaction(&self, transaction_hash: H256, flags: Vec<String>) -> Result<TraceResults, Error> {
fn replay_transaction(&self, transaction_hash: H256, flags: TraceOptions) -> Result<TraceResults, Error> {
self.client.replay(TransactionId::Hash(transaction_hash.into()), to_call_analytics(flags))
.map(TraceResults::from)
.map_err(errors::call)