Merge branch 'master' into Fix-4858

This commit is contained in:
Gav Wood
2017-08-29 15:11:05 +02:00
committed by GitHub
275 changed files with 6719 additions and 3562 deletions

View File

@@ -24,6 +24,7 @@ serde_json = "1.0"
time = "0.1"
tokio-timer = "0.1"
transient-hashmap = "0.4"
itertools = "0.5"
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
@@ -46,12 +47,13 @@ ethjson = { path = "../json" }
ethcore-devtools = { path = "../devtools" }
ethcore-light = { path = "../ethcore/light" }
ethcore-logger = { path = "../logger" }
vm = { path = "../ethcore/vm" }
parity-updater = { path = "../updater" }
parity-reactor = { path = "../util/reactor" }
rlp = { path = "../util/rlp" }
fetch = { path = "../util/fetch" }
node-health = { path = "../dapps/node-health" }
parity-reactor = { path = "../util/reactor" }
parity-updater = { path = "../updater" }
rlp = { path = "../util/rlp" }
stats = { path = "../util/stats" }
vm = { path = "../ethcore/vm" }
clippy = { version = "0.0.103", optional = true}
pretty_assertions = "0.1"

View File

@@ -18,9 +18,10 @@ use std::io::{self, Read, Write};
use std::path::Path;
use std::{fs, time, mem};
use itertools::Itertools;
use rand::Rng;
use rand::os::OsRng;
use util::{H256, Hashable, Itertools};
use util::{H256, Hashable};
/// Providing current time in seconds
pub trait TimeProvider {

View File

@@ -24,6 +24,7 @@ extern crate cid;
extern crate crypto as rust_crypto;
extern crate futures;
extern crate futures_cpupool;
extern crate itertools;
extern crate multihash;
extern crate order_stat;
extern crate rand;
@@ -54,6 +55,7 @@ extern crate ethsync;
extern crate ethcore_logger;
extern crate vm;
extern crate fetch;
extern crate node_health;
extern crate parity_reactor;
extern crate parity_updater as updater;
extern crate rlp;

View File

@@ -22,12 +22,6 @@ use v1::types::LocalDapp;
pub trait DappsService: Send + Sync + 'static {
/// List available local dapps.
fn list_dapps(&self) -> Vec<LocalDapp>;
}
impl<F> DappsService for F where
F: Fn() -> Vec<LocalDapp> + Send + Sync + 'static
{
fn list_dapps(&self) -> Vec<LocalDapp> {
(*self)()
}
/// Refresh local dapps list
fn refresh_local_dapps(&self) -> bool;
}

View File

@@ -133,7 +133,7 @@ impl<C: MiningBlockChainClient, M: MinerService> Dispatcher for FullDispatcher<C
-> BoxFuture<WithToken<SignedTransaction>, Error>
{
let (client, miner) = (self.client.clone(), self.miner.clone());
let network_id = client.signing_network_id();
let chain_id = client.signing_chain_id();
let address = filled.from;
future::done({
let t = Transaction {
@@ -146,12 +146,12 @@ impl<C: MiningBlockChainClient, M: MinerService> Dispatcher for FullDispatcher<C
};
if accounts.is_hardware_address(address) {
hardware_signature(&*accounts, address, t, network_id).map(WithToken::No)
hardware_signature(&*accounts, address, t, chain_id).map(WithToken::No)
} else {
let hash = t.hash(network_id);
let hash = t.hash(chain_id);
let signature = try_bf!(signature(&*accounts, address, hash, password));
Ok(signature.map(|sig| {
SignedTransaction::new(t.with_signature(sig, network_id))
SignedTransaction::new(t.with_signature(sig, chain_id))
.expect("Transaction was signed by AccountsProvider; it never produces invalid signatures; qed")
}))
}
@@ -358,7 +358,7 @@ impl Dispatcher for LightDispatcher {
fn sign(&self, accounts: Arc<AccountProvider>, filled: FilledTransactionRequest, password: SignWith)
-> BoxFuture<WithToken<SignedTransaction>, Error>
{
let network_id = self.client.signing_network_id();
let chain_id = self.client.signing_chain_id();
let address = filled.from;
let with_nonce = move |filled: FilledTransactionRequest, nonce| {
@@ -372,14 +372,14 @@ impl Dispatcher for LightDispatcher {
};
if accounts.is_hardware_address(address) {
return hardware_signature(&*accounts, address, t, network_id).map(WithToken::No)
return hardware_signature(&*accounts, address, t, chain_id).map(WithToken::No)
}
let hash = t.hash(network_id);
let hash = t.hash(chain_id);
let signature = signature(&*accounts, address, hash, password)?;
Ok(signature.map(|sig| {
SignedTransaction::new(t.with_signature(sig, network_id))
SignedTransaction::new(t.with_signature(sig, chain_id))
.expect("Transaction was signed by AccountsProvider; it never produces invalid signatures; qed")
}))
};
@@ -512,6 +512,10 @@ pub fn execute<D: Dispatcher + 'static>(
).boxed()
},
ConfirmationPayload::EthSignMessage(address, data) => {
if accounts.is_hardware_address(address) {
return future::err(errors::unsupported("Signing via hardware wallets is not supported.", None)).boxed();
}
let hash = eth_data_hash(data);
let res = signature(&accounts, address, hash, pass)
.map(|result| result
@@ -522,6 +526,10 @@ pub fn execute<D: Dispatcher + 'static>(
future::done(res).boxed()
},
ConfirmationPayload::Decrypt(address, data) => {
if accounts.is_hardware_address(address) {
return future::err(errors::unsupported("Decrypting via hardware wallets is not supported.", None)).boxed();
}
let res = decrypt(&accounts, address, data, pass)
.map(|result| result
.map(RpcBytes)
@@ -544,20 +552,20 @@ fn signature(accounts: &AccountProvider, address: Address, hash: H256, password:
}
// obtain a hardware signature from the given account.
fn hardware_signature(accounts: &AccountProvider, address: Address, t: Transaction, network_id: Option<u64>)
fn hardware_signature(accounts: &AccountProvider, address: Address, t: Transaction, chain_id: Option<u64>)
-> Result<SignedTransaction, Error>
{
debug_assert!(accounts.is_hardware_address(address));
let mut stream = rlp::RlpStream::new();
t.rlp_append_unsigned_transaction(&mut stream, network_id);
t.rlp_append_unsigned_transaction(&mut stream, chain_id);
let signature = accounts.sign_with_hardware(address, &stream.as_raw())
.map_err(|e| {
debug!(target: "miner", "Error signing transaction with hardware wallet: {}", e);
errors::account("Error signing transaction with hardware wallet", e)
})?;
SignedTransaction::new(t.with_signature(signature, network_id))
SignedTransaction::new(t.with_signature(signature, chain_id))
.map_err(|e| {
debug!(target: "miner", "Hardware wallet has produced invalid signature: {}", e);
errors::account("Invalid signature generated", e)
@@ -576,8 +584,9 @@ fn decrypt(accounts: &AccountProvider, address: Address, msg: Bytes, password: S
}
/// Extract the default gas price from a client and miner.
pub fn default_gas_price<C, M>(client: &C, miner: &M) -> U256
where C: MiningBlockChainClient, M: MinerService
pub fn default_gas_price<C, M>(client: &C, miner: &M) -> U256 where
C: MiningBlockChainClient,
M: MinerService,
{
client.gas_price_corpus(100).median().cloned().unwrap_or_else(|| miner.sensible_gas_price())
}

View File

@@ -71,6 +71,14 @@ pub fn public_unsupported(details: Option<String>) -> Error {
}
}
pub fn unsupported<T: Into<String>>(msg: T, details: Option<T>) -> Error {
Error {
code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST),
message: msg.into(),
data: details.map(Into::into).map(Value::String),
}
}
pub fn request_not_found() -> Error {
Error {
code: ErrorCode::ServerError(codes::REQUEST_NOT_FOUND),
@@ -302,7 +310,7 @@ pub fn transaction_message(error: TransactionError) -> String {
GasLimitExceeded { limit, got } => {
format!("Transaction cost exceeds current gas limit. Limit: {}, got: {}. Try decreasing supplied gas.", limit, got)
},
InvalidNetworkId => "Invalid network id.".into(),
InvalidChainId => "Invalid chain id.".into(),
InvalidGasLimit(_) => "Supplied gas is beyond limit.".into(),
SenderBanned => "Sender is banned in local queue.".into(),
RecipientBanned => "Recipient is banned in local queue.".into(),

View File

@@ -18,22 +18,34 @@ use std::sync::Arc;
use ethcore::client::MiningBlockChainClient;
use ethcore::miner::MinerService;
use ethcore::transaction::{Transaction, SignedTransaction, Action};
use util::U256;
use jsonrpc_core::Error;
use v1::helpers::CallRequest;
use v1::helpers::dispatch::default_gas_price;
pub fn sign_call<B: MiningBlockChainClient, M: MinerService>(
client: &Arc<B>,
pub fn sign_call<C: MiningBlockChainClient, M: MinerService> (
client: &Arc<C>,
miner: &Arc<M>,
request: CallRequest,
gas_cap: bool,
) -> Result<SignedTransaction, Error> {
let max_gas = 50_000_000.into();
let gas = match request.gas {
Some(gas) if gas_cap && gas > max_gas => {
warn!("Gas limit capped to {} (from {})", max_gas, gas);
max_gas
}
Some(gas) => gas,
None if gas_cap => max_gas,
None => U256::from(2) << 50,
};
let from = request.from.unwrap_or(0.into());
Ok(Transaction {
nonce: request.nonce.unwrap_or_else(|| client.latest_nonce(&from)),
action: request.to.map_or(Action::Create, Action::Call),
gas: request.gas.unwrap_or(50_000_000.into()),
gas,
gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(&**client, &**miner)),
value: request.value.unwrap_or(0.into()),
data: request.data.unwrap_or_default(),

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()
@@ -641,17 +612,15 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
self.send_raw_transaction(raw)
}
fn call(&self, request: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<Bytes, Error> {
fn call(&self, meta: Self::Metadata, request: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<Bytes, Error> {
let request = CallRequest::into(request);
let signed = match fake_sign::sign_call(&self.client, &self.miner, request) {
let signed = match fake_sign::sign_call(&self.client, &self.miner, request, meta.is_dapp()) {
Ok(signed) => signed,
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())
@@ -659,9 +628,9 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
).boxed()
}
fn estimate_gas(&self, request: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
fn estimate_gas(&self, meta: Self::Metadata, request: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
let request = CallRequest::into(request);
let signed = match fake_sign::sign_call(&self.client, &self.miner, request) {
let signed = match fake_sign::sign_call(&self.client, &self.miner, request, meta.is_dapp()) {
Ok(signed) => signed,
Err(e) => return future::err(e).boxed(),
};

View File

@@ -245,17 +245,27 @@ impl<C: Send + Sync + 'static> EthPubSub for EthPubSubClient<C> {
kind: pubsub::Kind,
params: Trailing<pubsub::Params>,
) {
match (kind, params.into()) {
let error = match (kind, params.into()) {
(pubsub::Kind::NewHeads, None) => {
self.heads_subscribers.write().push(subscriber)
self.heads_subscribers.write().push(subscriber);
return;
},
(pubsub::Kind::Logs, Some(pubsub::Params::Logs(filter))) => {
self.logs_subscribers.write().push(subscriber, filter.into());
return;
},
(pubsub::Kind::NewHeads, _) => {
errors::invalid_params("newHeads", "Expected no parameters.")
},
(pubsub::Kind::Logs, _) => {
errors::invalid_params("logs", "Expected a filter object.")
},
_ => {
let _ = subscriber.reject(errors::unimplemented(None));
errors::unimplemented(None)
},
}
};
let _ = subscriber.reject(error);
}
fn unsubscribe(&self, id: SubscriptionId) -> BoxFuture<bool, Error> {

View File

@@ -383,7 +383,7 @@ impl Eth for EthClient {
self.send_raw_transaction(raw)
}
fn call(&self, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<Bytes, Error> {
fn call(&self, _meta: Self::Metadata, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<Bytes, Error> {
self.fetcher().proved_execution(req, num).and_then(|res| {
match res {
Ok(exec) => Ok(exec.output.into()),
@@ -392,7 +392,7 @@ impl Eth for EthClient {
}).boxed()
}
fn estimate_gas(&self, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
fn estimate_gas(&self, _meta: Self::Metadata, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
// TODO: binary chop for more accurate estimates.
self.fetcher().proved_execution(req, num).and_then(|res| {
match res {

View File

@@ -19,15 +19,15 @@ use std::sync::Arc;
use std::collections::{BTreeMap, HashSet};
use futures::{future, Future, BoxFuture};
use ethcore_logger::RotatingLogger;
use util::misc::version_data;
use crypto::ecies;
use crypto::{ecies, DEFAULT_MAC};
use ethkey::{Brain, Generator};
use ethstore::random_phrase;
use ethsync::LightSyncProvider;
use ethcore::account_provider::AccountProvider;
use crypto::DEFAULT_MAC;
use ethcore_logger::RotatingLogger;
use node_health::{NodeHealth, Health};
use light::client::LightChainClient;
@@ -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,
@@ -53,6 +53,7 @@ pub struct ParityClient {
accounts: Arc<AccountProvider>,
logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>,
health: NodeHealth,
signer: Option<Arc<SignerService>>,
dapps_address: Option<(String, u16)>,
ws_address: Option<(String, u16)>,
@@ -67,18 +68,20 @@ impl ParityClient {
accounts: Arc<AccountProvider>,
logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>,
health: NodeHealth,
signer: Option<Arc<SignerService>>,
dapps_address: Option<(String, u16)>,
ws_address: Option<(String, u16)>,
) -> Self {
ParityClient {
light_dispatch: light_dispatch,
accounts: accounts,
logger: logger,
settings: settings,
signer: signer,
dapps_address: dapps_address,
ws_address: ws_address,
light_dispatch,
accounts,
logger,
settings,
health,
signer,
dapps_address,
ws_address,
eip86_transition: client.eip86_transition(),
}
}
@@ -389,4 +392,14 @@ impl Parity for ParityClient {
fn ipfs_cid(&self, content: Bytes) -> Result<String, Error> {
ipfs::cid(content)
}
fn call(&self, _meta: Self::Metadata, _requests: Vec<CallRequest>, _block: Trailing<BlockNumber>) -> BoxFuture<Vec<Bytes>, Error> {
future::err(errors::light_unimplemented(None)).boxed()
}
fn node_health(&self) -> BoxFuture<Health, Error> {
self.health.health()
.map_err(|err| errors::internal("Health API failure.", err))
.boxed()
}
}

View File

@@ -135,6 +135,10 @@ impl<F: Fetch> ParitySet for ParitySetClient<F> {
}))
}
fn dapps_refresh(&self) -> Result<bool, Error> {
self.dapps.as_ref().map(|dapps| dapps.refresh_local_dapps()).ok_or_else(errors::dapps_disabled)
}
fn dapps_list(&self) -> Result<Vec<LocalDapp>, Error> {
self.dapps.as_ref().map(|dapps| dapps.list_dapps()).ok_or_else(errors::dapps_disabled)
}

View File

@@ -17,16 +17,20 @@
//! Traces api implementation.
use jsonrpc_core::Error;
use jsonrpc_core::futures::{future, Future, BoxFuture};
use jsonrpc_macros::Trailing;
use v1::Metadata;
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.
pub struct TracesClient;
impl Traces for TracesClient {
type Metadata = Metadata;
fn filter(&self, _filter: TraceFilter) -> Result<Option<Vec<LocalizedTrace>>, Error> {
Err(errors::light_unimplemented(None))
}
@@ -43,15 +47,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, _meta: Self::Metadata, _request: CallRequest, _flags: TraceOptions, _block: Trailing<BlockNumber>) -> BoxFuture<TraceResults, Error> {
future::err(errors::light_unimplemented(None)).boxed()
}
fn call_many(&self, _meta: Self::Metadata, _request: Vec<(CallRequest, TraceOptions)>, _block: Trailing<BlockNumber>) -> BoxFuture<Vec<TraceResults>, Error> {
future::err(errors::light_unimplemented(None)).boxed()
}
fn raw_transaction(&self, _raw_transaction: Bytes, _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> {
Err(errors::light_unimplemented(None))
}
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> {
Err(errors::light_unimplemented(None))
}
}

View File

@@ -20,30 +20,31 @@ use std::str::FromStr;
use std::collections::{BTreeMap, HashSet};
use futures::{future, Future, BoxFuture};
use ethcore_logger::RotatingLogger;
use util::Address;
use util::misc::version_data;
use crypto::ecies;
use crypto::{DEFAULT_MAC, 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 ethcore_logger::RotatingLogger;
use node_health::{NodeHealth, Health};
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,
@@ -52,17 +53,13 @@ use v1::types::{
};
/// Parity implementation.
pub struct ParityClient<C, M, S: ?Sized, U> where
C: MiningBlockChainClient,
M: MinerService,
S: SyncProvider,
U: UpdateService,
{
pub struct ParityClient<C, M, U> {
client: Arc<C>,
miner: Arc<M>,
sync: Arc<S>,
updater: Arc<U>,
sync: Arc<SyncProvider>,
net: Arc<ManageNetwork>,
health: NodeHealth,
accounts: Option<Arc<AccountProvider>>,
logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>,
@@ -72,39 +69,39 @@ pub struct ParityClient<C, M, S: ?Sized, U> where
eip86_transition: u64,
}
impl<C, M, S: ?Sized, U> ParityClient<C, M, S, U> where
impl<C, M, U> ParityClient<C, M, U> where
C: MiningBlockChainClient,
M: MinerService,
S: SyncProvider,
U: UpdateService,
{
/// Creates new `ParityClient`.
pub fn new(
client: &Arc<C>,
miner: &Arc<M>,
sync: &Arc<S>,
updater: &Arc<U>,
net: &Arc<ManageNetwork>,
store: &Option<Arc<AccountProvider>>,
client: Arc<C>,
miner: Arc<M>,
sync: Arc<SyncProvider>,
updater: Arc<U>,
net: Arc<ManageNetwork>,
health: NodeHealth,
accounts: Option<Arc<AccountProvider>>,
logger: Arc<RotatingLogger>,
settings: Arc<NetworkSettings>,
signer: Option<Arc<SignerService>>,
dapps_address: Option<(String, u16)>,
ws_address: Option<(String, u16)>,
) -> Self {
let eip86_transition = client.eip86_transition();
ParityClient {
client: client.clone(),
miner: miner.clone(),
sync: sync.clone(),
updater: updater.clone(),
net: net.clone(),
accounts: store.clone(),
logger: logger,
settings: settings,
signer: signer,
dapps_address: dapps_address,
ws_address: ws_address,
eip86_transition: client.eip86_transition(),
client,
miner,
sync,
updater,
net,
health,
accounts,
logger,
settings,
signer,
dapps_address,
ws_address,
eip86_transition,
}
}
@@ -115,10 +112,9 @@ impl<C, M, S: ?Sized, U> ParityClient<C, M, S, U> where
}
}
impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
M: MinerService + 'static,
impl<C, M, U> Parity for ParityClient<C, M, U> where
C: MiningBlockChainClient + 'static,
S: SyncProvider + 'static,
M: MinerService + 'static,
U: UpdateService + 'static,
{
type Metadata = Metadata;
@@ -409,4 +405,29 @@ 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, meta: Self::Metadata, 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(), meta.is_dapp())?,
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()
}
fn node_health(&self) -> BoxFuture<Health, Error> {
self.health.health()
.map_err(|err| errors::internal("Health API failure.", err))
.boxed()
}
}

View File

@@ -176,6 +176,10 @@ impl<C, M, U, F> ParitySet for ParitySetClient<C, M, U, F> where
}))
}
fn dapps_refresh(&self) -> Result<bool, Error> {
self.dapps.as_ref().map(|dapps| dapps.refresh_local_dapps()).ok_or_else(errors::dapps_disabled)
}
fn dapps_list(&self) -> Result<Vec<LocalDapp>, Error> {
self.dapps.as_ref().map(|dapps| dapps.list_dapps()).ok_or_else(errors::dapps_disabled)
}

View File

@@ -124,9 +124,9 @@ impl<D: Dispatcher + 'static> Personal for PersonalClient<D> {
.map(move |tx| (tx, dispatcher))
})
.and_then(|(pending_tx, dispatcher)| {
let network_id = pending_tx.network_id();
trace!(target: "miner", "send_transaction: dispatching tx: {} for network ID {:?}",
::rlp::encode(&*pending_tx).into_vec().pretty(), network_id);
let chain_id = pending_tx.chain_id();
trace!(target: "miner", "send_transaction: dispatching tx: {} for chain ID {:?}",
::rlp::encode(&*pending_tx).into_vec().pretty(), chain_id);
dispatcher.dispatch_transaction(pending_tx).map(Into::into)
})

View File

@@ -18,18 +18,20 @@
use std::sync::Arc;
use rlp::UntrustedRlp;
use ethcore::client::{MiningBlockChainClient, CallAnalytics, TransactionId, TraceId};
use ethcore::miner::MinerService;
use ethcore::transaction::SignedTransaction;
use rlp::UntrustedRlp;
use jsonrpc_core::Error;
use jsonrpc_core::futures::{self, Future, BoxFuture};
use jsonrpc_macros::Trailing;
use v1::Metadata;
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())),
@@ -54,6 +56,8 @@ impl<C, M> TracesClient<C, M> {
}
impl<C, M> Traces for TracesClient<C, M> where C: MiningBlockChainClient + 'static, M: MinerService + 'static {
type Metadata = Metadata;
fn filter(&self, filter: TraceFilter) -> Result<Option<Vec<LocalizedTrace>>, Error> {
Ok(self.client.filter_traces(filter.into())
.map(|traces| traces.into_iter().map(LocalizedTrace::from).collect()))
@@ -79,29 +83,49 @@ 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, meta: Self::Metadata, request: CallRequest, flags: TraceOptions, block: Trailing<BlockNumber>) -> BoxFuture<TraceResults, Error> {
let block = block.unwrap_or_default();
let request = CallRequest::into(request);
let signed = fake_sign::sign_call(&self.client, &self.miner, request)?;
let signed = try_bf!(fake_sign::sign_call(&self.client, &self.miner, request, meta.is_dapp()));
self.client.call(&signed, block.into(), to_call_analytics(flags))
let res = self.client.call(&signed, to_call_analytics(flags), block.into())
.map(TraceResults::from)
.map_err(errors::call)
.map_err(errors::call);
futures::done(res).boxed()
}
fn raw_transaction(&self, raw_transaction: Bytes, flags: Vec<String>, block: Trailing<BlockNumber>) -> Result<TraceResults, Error> {
fn call_many(&self, meta: Self::Metadata, requests: Vec<(CallRequest, TraceOptions)>, block: Trailing<BlockNumber>) -> BoxFuture<Vec<TraceResults>, Error> {
let block = block.unwrap_or_default();
let requests = try_bf!(requests.into_iter()
.map(|(request, flags)| {
let request = CallRequest::into(request);
let signed = fake_sign::sign_call(&self.client, &self.miner, request, meta.is_dapp())?;
Ok((signed, to_call_analytics(flags)))
})
.collect::<Result<Vec<_>, Error>>());
let res = self.client.call_many(&requests, block.into())
.map(|results| results.into_iter().map(TraceResults::from).collect())
.map_err(errors::call);
futures::done(res).boxed()
}
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)

View File

@@ -42,6 +42,15 @@ impl Metadata {
_ => DappId::default(),
}
}
/// Returns true if the request originates from a Dapp.
pub fn is_dapp(&self) -> bool {
if let Origin::Dapps(_) = self.origin {
true
} else {
false
}
}
}
impl jsonrpc_core::Metadata for Metadata {}

View File

@@ -34,4 +34,8 @@ impl DappsService for TestDappsService {
icon_url: "title.png".into(),
}]
}
fn refresh_local_dapps(&self) -> bool {
true
}
}

View File

@@ -19,9 +19,9 @@
use std::collections::{BTreeMap, HashMap};
use std::collections::hash_map::Entry;
use util::{Address, H256, Bytes, U256, RwLock, Mutex};
use ethcore::error::{Error, CallError};
use ethcore::client::{MiningBlockChainClient, Executed, CallAnalytics};
use ethcore::block::{ClosedBlock, IsBlock};
use ethcore::error::Error;
use ethcore::client::MiningBlockChainClient;
use ethcore::block::ClosedBlock;
use ethcore::header::BlockNumber;
use ethcore::transaction::{UnverifiedTransaction, SignedTransaction, PendingTransaction};
use ethcore::receipt::{Receipt, RichReceipt};
@@ -280,41 +280,6 @@ impl MinerService for TestMinerService {
unimplemented!();
}
fn balance(&self, _chain: &MiningBlockChainClient, address: &Address) -> Option<U256> {
self.latest_closed_block.lock()
.as_ref()
.map(|b| b.block().fields().state.balance(address))
.map(|b| b.ok())
.unwrap_or(Some(U256::default()))
}
fn call(&self, _chain: &MiningBlockChainClient, _t: &SignedTransaction, _analytics: CallAnalytics) -> Result<Executed, CallError> {
unimplemented!();
}
fn storage_at(&self, _chain: &MiningBlockChainClient, address: &Address, position: &H256) -> Option<H256> {
self.latest_closed_block.lock()
.as_ref()
.map(|b| b.block().fields().state.storage_at(address, position))
.map(|s| s.ok())
.unwrap_or(Some(H256::default()))
}
fn nonce(&self, _chain: &MiningBlockChainClient, address: &Address) -> Option<U256> {
// we assume all transactions are in a pending block, ignoring the
// reality of gas limits.
Some(self.last_nonce(address).unwrap_or(U256::zero()))
}
fn code(&self, _chain: &MiningBlockChainClient, address: &Address) -> Option<Option<Bytes>> {
self.latest_closed_block.lock()
.as_ref()
.map(|b| b.block().fields().state.code(address))
.map(|c| c.ok())
.unwrap_or(None)
.map(|c| c.map(|c| (&*c).clone()))
}
fn sensible_gas_price(&self) -> U256 {
20000000000u64.into()
}

View File

@@ -432,10 +432,7 @@ fn rpc_eth_balance_pending() {
"id": 1
}"#;
// the TestMinerService doesn't communicate with the the TestBlockChainClient in any way.
// if this returns zero, we know that the "pending" call is being properly forwarded to the
// miner.
let response = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x5","id":1}"#;
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
}
@@ -547,7 +544,7 @@ fn rpc_eth_pending_transaction_by_hash() {
tester.miner.pending_transactions.lock().insert(H256::zero(), tx);
}
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"condition":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","networkId":null,"nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":"0x0","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":"0x1b","value":"0xa"},"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"chainId":null,"condition":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":"0x0","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":"0x1b","value":"0xa"},"id":1}"#;
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_getTransactionByHash",
@@ -863,12 +860,13 @@ fn rpc_eth_sign_transaction() {
let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() +
r#""raw":"0x"# + &rlp.to_hex() + r#"","# +
r#""tx":{"# +
r#""blockHash":null,"blockNumber":null,"condition":null,"creates":null,"# +
r#""blockHash":null,"blockNumber":null,"# +
&format!("\"chainId\":{},", t.chain_id().map_or("null".to_owned(), |n| format!("{}", n))) +
r#""condition":null,"creates":null,"# +
&format!("\"from\":\"0x{:?}\",", &address) +
r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# +
&format!("\"hash\":\"0x{:?}\",", t.hash()) +
r#""input":"0x","# +
&format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) +
r#""nonce":"0x1","# +
&format!("\"publicKey\":\"0x{:?}\",", t.recover_public().unwrap()) +
&format!("\"r\":\"0x{}\",", U256::from(signature.r()).to_hex()) +

View File

@@ -15,13 +15,15 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use ethcore_logger::RotatingLogger;
use util::Address;
use ethsync::ManageNetwork;
use ethcore::account_provider::AccountProvider;
use ethcore::client::{TestBlockChainClient};
use ethcore::client::{TestBlockChainClient, Executed};
use ethcore::miner::LocalTransactionStatus;
use ethcore_logger::RotatingLogger;
use ethstore::ethkey::{Generator, Random};
use ethsync::ManageNetwork;
use node_health::{self, NodeHealth};
use parity_reactor;
use util::Address;
use jsonrpc_core::IoHandler;
use v1::{Parity, ParityClient};
@@ -30,13 +32,14 @@ use v1::helpers::{SignerService, NetworkSettings};
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestUpdater};
use super::manage_network::TestManageNetwork;
pub type TestParityClient = ParityClient<TestBlockChainClient, TestMinerService, TestSyncProvider, TestUpdater>;
pub type TestParityClient = ParityClient<TestBlockChainClient, TestMinerService, TestUpdater>;
pub struct Dependencies {
pub miner: Arc<TestMinerService>,
pub client: Arc<TestBlockChainClient>,
pub sync: Arc<TestSyncProvider>,
pub updater: Arc<TestUpdater>,
pub health: NodeHealth,
pub logger: Arc<RotatingLogger>,
pub settings: Arc<NetworkSettings>,
pub network: Arc<ManageNetwork>,
@@ -54,6 +57,11 @@ impl Dependencies {
network_id: 3,
num_peers: 120,
})),
health: NodeHealth::new(
Arc::new(FakeSync),
node_health::TimeChecker::new::<String>(&[], node_health::CpuPool::new(1)),
parity_reactor::Remote::new_sync(),
),
updater: Arc::new(TestUpdater::default()),
logger: Arc::new(RotatingLogger::new("rpc=trace".to_owned())),
settings: Arc::new(NetworkSettings {
@@ -75,12 +83,13 @@ impl Dependencies {
let opt_accounts = Some(self.accounts.clone());
ParityClient::new(
&self.client,
&self.miner,
&self.sync,
&self.updater,
&self.network,
&opt_accounts,
self.client.clone(),
self.miner.clone(),
self.sync.clone(),
self.updater.clone(),
self.network.clone(),
self.health.clone(),
opt_accounts.clone(),
self.logger.clone(),
self.settings.clone(),
signer,
@@ -102,6 +111,13 @@ impl Dependencies {
}
}
#[derive(Debug)]
struct FakeSync;
impl node_health::SyncStatus for FakeSync {
fn is_major_importing(&self) -> bool { false }
fn peers(&self) -> (usize, usize) { (4, 25) }
}
#[test]
fn rpc_parity_accounts_info() {
let deps = Dependencies::new();
@@ -504,3 +520,53 @@ fn rpc_parity_cid() {
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_call() {
use util::U256;
let deps = Dependencies::new();
deps.client.set_execution_result(Ok(Executed {
exception: None,
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 io = deps.default_client();
let request = r#"{
"jsonrpc": "2.0",
"method": "parity_call",
"params": [[{
"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a",
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
}],
"latest"],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":["0x1234ff"],"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_node_health() {
let deps = Dependencies::new();
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_nodeHealth", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"peers":{"details":[4,25],"message":"","status":"ok"},"sync":{"details":false,"message":"","status":"ok"},"time":{"details":0,"message":"","status":"ok"}},"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}

View File

@@ -233,7 +233,7 @@ fn rpc_parity_remove_transaction() {
let hash = signed.hash();
let request = r#"{"jsonrpc": "2.0", "method": "parity_removeTransaction", "params":[""#.to_owned() + &format!("0x{:?}", hash) + r#""], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"condition":null,"creates":null,"from":"0x0000000000000000000000000000000000000002","gas":"0x76c0","gasPrice":"0x9184e72a000","hash":"0xa2e0da8a8064e0b9f93e95a53c2db6d01280efb8ac72a708d25487e67dd0f8fc","input":"0x","networkId":null,"nonce":"0x1","publicKey":null,"r":"0x1","raw":"0xe9018609184e72a0008276c0940000000000000000000000000000000000000005849184e72a80800101","s":"0x1","standardV":"0x4","to":"0x0000000000000000000000000000000000000005","transactionIndex":null,"v":"0x0","value":"0x9184e72a"},"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"chainId":null,"condition":null,"creates":null,"from":"0x0000000000000000000000000000000000000002","gas":"0x76c0","gasPrice":"0x9184e72a000","hash":"0xa2e0da8a8064e0b9f93e95a53c2db6d01280efb8ac72a708d25487e67dd0f8fc","input":"0x","nonce":"0x1","publicKey":null,"r":"0x1","raw":"0xe9018609184e72a0008276c0940000000000000000000000000000000000000005849184e72a80800101","s":"0x1","standardV":"0x4","to":"0x0000000000000000000000000000000000000005","transactionIndex":null,"v":"0x0","value":"0x9184e72a"},"id":1}"#;
miner.pending_transactions.lock().insert(hash, signed);
assert_eq!(io.handle_request_sync(&request), Some(response.to_owned()));

View File

@@ -454,12 +454,13 @@ fn should_confirm_sign_transaction_with_rlp() {
let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() +
r#""raw":"0x"# + &rlp.to_hex() + r#"","# +
r#""tx":{"# +
r#""blockHash":null,"blockNumber":null,"condition":null,"creates":null,"# +
r#""blockHash":null,"blockNumber":null,"# +
&format!("\"chainId\":{},", t.chain_id().map_or("null".to_owned(), |n| format!("{}", n))) +
r#""condition":null,"creates":null,"# +
&format!("\"from\":\"0x{:?}\",", &address) +
r#""gas":"0x989680","gasPrice":"0x1000","# +
&format!("\"hash\":\"0x{:?}\",", t.hash()) +
r#""input":"0x","# +
&format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) +
r#""nonce":"0x0","# +
&format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) +
&format!("\"r\":\"0x{}\",", U256::from(signature.r()).to_hex()) +

View File

@@ -297,12 +297,13 @@ fn should_add_sign_transaction_to_the_queue() {
let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() +
r#""raw":"0x"# + &rlp.to_hex() + r#"","# +
r#""tx":{"# +
r#""blockHash":null,"blockNumber":null,"condition":null,"creates":null,"# +
r#""blockHash":null,"blockNumber":null,"# +
&format!("\"chainId\":{},", t.chain_id().map_or("null".to_owned(), |n| format!("{}", n))) +
r#""condition":null,"creates":null,"# +
&format!("\"from\":\"0x{:?}\",", &address) +
r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# +
&format!("\"hash\":\"0x{:?}\",", t.hash()) +
r#""input":"0x","# +
&format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) +
r#""nonce":"0x1","# +
&format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) +
&format!("\"r\":\"0x{}\",", U256::from(signature.r()).to_hex()) +

View File

@@ -25,12 +25,12 @@ use vm::CallType;
use jsonrpc_core::IoHandler;
use v1::tests::helpers::{TestMinerService};
use v1::{Traces, TracesClient};
use v1::{Metadata, Traces, TracesClient};
struct Tester {
client: Arc<TestBlockChainClient>,
_miner: Arc<TestMinerService>,
io: IoHandler,
io: IoHandler<Metadata>,
}
fn io() -> Tester {
@@ -67,7 +67,7 @@ fn io() -> Tester {
}));
let miner = Arc::new(TestMinerService::default());
let traces = TracesClient::new(&client, &miner);
let mut io = IoHandler::new();
let mut io = IoHandler::default();
io.extend_with(traces.to_delegate());
Tester {
@@ -170,6 +170,16 @@ fn rpc_trace_call() {
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_trace_multi_call() {
let tester = io();
let request = r#"{"jsonrpc":"2.0","method":"trace_callMany","params":[[[{}, ["stateDiff", "vmTrace", "trace"]]]],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":[{"output":"0x010203","stateDiff":null,"trace":[],"vmTrace":null}],"id":1}"#;
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_trace_call_state_pruned() {
let tester = io();

View File

@@ -110,12 +110,12 @@ build_rpc_trait! {
fn submit_transaction(&self, Bytes) -> Result<H256, Error>;
/// Call contract, returning the output data.
#[rpc(async, name = "eth_call")]
fn call(&self, CallRequest, Trailing<BlockNumber>) -> BoxFuture<Bytes, Error>;
#[rpc(meta, name = "eth_call")]
fn call(&self, Self::Metadata, CallRequest, Trailing<BlockNumber>) -> BoxFuture<Bytes, Error>;
/// Estimate gas needed for execution of given contract.
#[rpc(async, name = "eth_estimateGas")]
fn estimate_gas(&self, CallRequest, Trailing<BlockNumber>) -> BoxFuture<U256, Error>;
#[rpc(meta, name = "eth_estimateGas")]
fn estimate_gas(&self, Self::Metadata, CallRequest, Trailing<BlockNumber>) -> BoxFuture<U256, Error>;
/// Get transaction by its hash.
#[rpc(name = "eth_getTransactionByHash")]

View File

@@ -22,8 +22,9 @@ use jsonrpc_core::Error;
use jsonrpc_macros::Trailing;
use futures::BoxFuture;
use node_health::Health;
use v1::types::{
H160, H256, H512, U256, Bytes,
H160, H256, H512, U256, Bytes, CallRequest,
Peers, Transaction, RpcSettings, Histogram,
TransactionStats, LocalTransactionStatus,
BlockNumber, ConsensusCapability, VersionInfo,
@@ -203,5 +204,13 @@ build_rpc_trait! {
/// Get IPFS CIDv0 given protobuf encoded bytes.
#[rpc(name = "parity_cidV0")]
fn ipfs_cid(&self, Bytes) -> Result<String, Error>;
/// Call contract, returning the output data.
#[rpc(meta, name = "parity_call")]
fn call(&self, Self::Metadata, Vec<CallRequest>, Trailing<BlockNumber>) -> BoxFuture<Vec<Bytes>, Error>;
/// Returns node's health report.
#[rpc(async, name = "parity_nodeHealth")]
fn node_health(&self) -> BoxFuture<Health, Error>;
}
}

View File

@@ -96,6 +96,10 @@ build_rpc_trait! {
#[rpc(async, name = "parity_hashContent")]
fn hash_content(&self, String) -> BoxFuture<H256, Error>;
/// Returns true if refresh successful, error if unsuccessful or server is disabled.
#[rpc(name = "parity_dappsRefresh")]
fn dapps_refresh(&self) -> Result<bool, Error>;
/// Returns a list of local dapps
#[rpc(name = "parity_dappsList")]
fn dapps_list(&self) -> Result<Vec<LocalDapp>, Error>;

View File

@@ -17,12 +17,15 @@
//! Traces specific rpc interface.
use jsonrpc_core::Error;
use jsonrpc_core::futures::BoxFuture;
use jsonrpc_macros::Trailing;
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, H256};
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, H256, TraceOptions};
build_rpc_trait! {
/// Traces specific rpc interface.
pub trait Traces {
type Metadata;
/// Returns traces matching given filter.
#[rpc(name = "trace_filter")]
fn filter(&self, TraceFilter) -> Result<Option<Vec<LocalizedTrace>>, Error>;
@@ -40,15 +43,19 @@ build_rpc_trait! {
fn block_traces(&self, BlockNumber) -> Result<Option<Vec<LocalizedTrace>>, Error>;
/// Executes the given call and returns a number of possible traces for it.
#[rpc(name = "trace_call")]
fn call(&self, CallRequest, Vec<String>, Trailing<BlockNumber>) -> Result<TraceResults, Error>;
#[rpc(meta, name = "trace_call")]
fn call(&self, Self::Metadata, CallRequest, TraceOptions, Trailing<BlockNumber>) -> BoxFuture<TraceResults, Error>;
/// Executes all given calls and returns a number of possible traces for each of it.
#[rpc(meta, name = "trace_callMany")]
fn call_many(&self, Self::Metadata, Vec<(CallRequest, TraceOptions)>, Trailing<BlockNumber>) -> BoxFuture<Vec<TraceResults>, Error>;
/// Executes the given raw transaction and returns a number of possible traces for it.
#[rpc(name = "trace_rawTransaction")]
fn raw_transaction(&self, Bytes, Vec<String>, Trailing<BlockNumber>) -> Result<TraceResults, Error>;
fn raw_transaction(&self, Bytes, TraceOptions, Trailing<BlockNumber>) -> Result<TraceResults, Error>;
/// Executes the transaction with the given hash and returns a number of possible traces for it.
#[rpc(name = "trace_replayTransaction")]
fn replay_transaction(&self, H256, Vec<String>) -> Result<TraceResults, Error>;
fn replay_transaction(&self, H256, TraceOptions) -> Result<TraceResults, Error>;
}
}

View File

@@ -214,7 +214,7 @@ impl<T: Serialize> Serialize for Rich<T> {
// and serialize
value.serialize(serializer)
} else {
Err(S::Error::custom("Unserializable structures."))
Err(S::Error::custom("Unserializable structures: expected objects"))
}
}
}
@@ -230,7 +230,7 @@ mod tests {
fn test_serialize_block_transactions() {
let t = BlockTransactions::Full(vec![Transaction::default()]);
let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","condition":null}]"#);
assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"chainId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","condition":null}]"#);
let t = BlockTransactions::Hashes(vec![H256::default().into()]);
let serialized = serde_json::to_string(&t).unwrap();

View File

@@ -79,8 +79,12 @@ impl<'a> Visitor<'a> for BlockNumberVisitor {
"latest" => Ok(BlockNumber::Latest),
"earliest" => Ok(BlockNumber::Earliest),
"pending" => Ok(BlockNumber::Pending),
_ if value.starts_with("0x") => u64::from_str_radix(&value[2..], 16).map(BlockNumber::Num).map_err(|_| Error::custom("invalid block number")),
_ => value.parse::<u64>().map(BlockNumber::Num).map_err(|_| Error::custom("invalid block number"))
_ if value.starts_with("0x") => u64::from_str_radix(&value[2..], 16).map(BlockNumber::Num).map_err(|e| {
Error::custom(format!("Invalid block number: {}", e))
}),
_ => value.parse::<u64>().map(BlockNumber::Num).map_err(|e| {
Error::custom(format!("Invalid block number: {}", e))
}),
}
}

View File

@@ -81,9 +81,9 @@ impl<'a> Visitor<'a> for BytesVisitor {
);
Ok(Bytes::new(Vec::new()))
} else if value.len() >= 2 && &value[0..2] == "0x" && value.len() & 1 == 0 {
Ok(Bytes::new(FromHex::from_hex(&value[2..]).map_err(|_| Error::custom("invalid hex"))?))
Ok(Bytes::new(FromHex::from_hex(&value[2..]).map_err(|e| Error::custom(format!("Invalid hex: {}", e)))?))
} else {
Err(Error::custom("invalid format"))
Err(Error::custom("Invalid bytes format. Expected a 0x-prefixed hex string with even length"))
}
}

View File

@@ -121,7 +121,7 @@ impl<'a> Visitor<'a> for DerivationTypeVisitor {
match value {
"soft" => Ok(DerivationType::Soft),
"hard" => Ok(DerivationType::Hard),
_ => Err(Error::custom("invalid derivation type")),
v => Err(Error::custom(format!("invalid derivation type: {:?}", v))),
}
}

View File

@@ -43,7 +43,7 @@ impl<'a, T> Deserialize<'a> for VariadicValue<T> where T: DeserializeOwned {
from_value(v.clone()).map(VariadicValue::Single)
.or_else(|_| from_value(v).map(VariadicValue::Multiple))
.map_err(|_| D::Error::custom("Invalid type."))
.map_err(|err| D::Error::custom(format!("Invalid variadic value type: {}", err)))
}
}

View File

@@ -47,8 +47,12 @@ impl<'a> Visitor<'a> for IndexVisitor {
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: Error {
match value {
_ if value.starts_with("0x") => usize::from_str_radix(&value[2..], 16).map(Index).map_err(|_| Error::custom("invalid index")),
_ => value.parse::<usize>().map(Index).map_err(|_| Error::custom("invalid index")),
_ if value.starts_with("0x") => usize::from_str_radix(&value[2..], 16).map(Index).map_err(|e| {
Error::custom(format!("Invalid index: {}", e))
}),
_ => value.parse::<usize>().map(Index).map_err(|e| {
Error::custom(format!("Invalid index: {}", e))
}),
}
}

View File

@@ -78,3 +78,7 @@ pub use self::transaction_request::TransactionRequest;
pub use self::transaction_condition::TransactionCondition;
pub use self::uint::{U128, U256};
pub use self::work::Work;
// TODO [ToDr] Refactor to a proper type Vec of enums?
/// Expected tracing type.
pub type TraceOptions = Vec<String>;

View File

@@ -84,7 +84,7 @@ impl<'a> Deserialize<'a> for Params {
}
from_value(v.clone()).map(Params::Logs)
.map_err(|_| D::Error::custom("Invalid type."))
.map_err(|e| D::Error::custom(format!("Invalid Pub-Sub parameters: {}", e)))
}
}

View File

@@ -59,8 +59,8 @@ pub struct Transaction {
#[serde(rename="publicKey")]
pub public_key: Option<H512>,
/// The network id of the transaction, if any.
#[serde(rename="networkId")]
pub network_id: Option<u64>,
#[serde(rename="chainId")]
pub chain_id: Option<u64>,
/// The standardised V field of the signature (0 or 1).
#[serde(rename="standardV")]
pub standard_v: U256,
@@ -196,7 +196,7 @@ impl Transaction {
},
raw: ::rlp::encode(&t.signed).into_vec().into(),
public_key: t.recover_public().ok().map(Into::into),
network_id: t.network_id(),
chain_id: t.chain_id(),
standard_v: t.standard_v().into(),
v: t.original_v().into(),
r: signature.r().into(),
@@ -230,7 +230,7 @@ impl Transaction {
},
raw: ::rlp::encode(&t).into_vec().into(),
public_key: t.public_key().map(Into::into),
network_id: t.network_id(),
chain_id: t.chain_id(),
standard_v: t.standard_v().into(),
v: t.original_v().into(),
r: signature.r().into(),
@@ -273,7 +273,7 @@ mod tests {
fn test_transaction_serialize() {
let t = Transaction::default();
let serialized = serde_json::to_string(&t).unwrap();
assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","condition":null}"#);
assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"chainId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","condition":null}"#);
}
#[test]