RPC errors & logs (#1845)

* Refactoring errors in RPC

* Updating jsonrpc-core

* Fixing code_at

* Avoid mentioning obvious segments in proof

[ci:skip]
This commit is contained in:
Tomasz Drwięga 2016-08-08 17:25:15 +02:00 committed by Gav Wood
parent 7ed4bded52
commit 59b0f8c7a3
25 changed files with 602 additions and 462 deletions

16
Cargo.lock generated
View File

@ -274,7 +274,7 @@ dependencies = [
"ethcore-rpc 1.3.0",
"ethcore-util 1.3.0",
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -414,7 +414,7 @@ dependencies = [
"ethjson 0.1.0",
"ethsync 1.3.0",
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)",
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
@ -434,7 +434,7 @@ dependencies = [
"ethcore-io 1.3.0",
"ethcore-rpc 1.3.0",
"ethcore-util 1.3.0",
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps-signer 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
@ -682,7 +682,7 @@ source = "git+https://github.com/ethcore/json-ipc-server.git#56b6307130710ebc73c
dependencies = [
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -692,13 +692,13 @@ dependencies = [
[[package]]
name = "jsonrpc-core"
version = "2.0.7"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -707,7 +707,7 @@ version = "6.1.0"
source = "git+https://github.com/ethcore/jsonrpc-http-server.git#4e3f93eb79125e91a46e04d77c25ff8885498b86"
dependencies = [
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
"jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1581,7 +1581,7 @@ dependencies = [
"checksum isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7408a548dc0e406b7912d9f84c261cc533c1866e047644a811c133c56041ac0c"
"checksum itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "086e1fa5fe48840b1cfdef3a20c7e3115599f8d5c4c87ef32a794a7cdd184d76"
"checksum json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)" = "<none>"
"checksum jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "91755680900913f73576065c85359ee793ac3883bc461dbca90fc4a603be84cc"
"checksum jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ec4477e4e8218da23caa5dd31f4eb39999aa0ea9035660617eccfb19a23bf5ad"
"checksum jsonrpc-http-server 6.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)" = "<none>"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"

View File

@ -10,7 +10,7 @@ build = "build.rs"
[dependencies]
log = "0.3"
jsonrpc-core = "2.0"
jsonrpc-core = "2.1"
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
unicase = "1.3"

View File

@ -784,8 +784,8 @@ impl BlockChainClient for Client {
Self::block_hash(&self.chain, id)
}
fn code(&self, address: &Address) -> Option<Bytes> {
self.state().code(address)
fn code(&self, address: &Address, id: BlockID) -> Option<Option<Bytes>> {
self.state_at(id).map(|s| s.code(address))
}
fn balance(&self, address: &Address, id: BlockID) -> Option<U256> {

View File

@ -319,8 +319,11 @@ impl BlockChainClient for TestBlockChainClient {
self.nonce(address, BlockID::Latest).unwrap()
}
fn code(&self, address: &Address) -> Option<Bytes> {
self.code.read().get(address).cloned()
fn code(&self, address: &Address, id: BlockID) -> Option<Option<Bytes>> {
match id {
BlockID::Latest => Some(self.code.read().get(address).cloned()),
_ => None,
}
}
fn balance(&self, address: &Address, id: BlockID) -> Option<U256> {

View File

@ -81,8 +81,14 @@ pub trait BlockChainClient : Sync + Send {
/// Get block hash.
fn block_hash(&self, id: BlockID) -> Option<H256>;
/// Get address code.
fn code(&self, address: &Address) -> Option<Bytes>;
/// Get address code at given block's state.
fn code(&self, address: &Address, id: BlockID) -> Option<Option<Bytes>>;
/// Get address code at the latest block's state.
fn latest_code(&self, address: &Address) -> Option<Bytes> {
self.code(address, BlockID::Latest)
.expect("code will return Some if given BlockID::Latest; qed")
}
/// Get address balance at the given block's state.
///

View File

@ -528,7 +528,7 @@ impl MinerService for Miner {
fn code(&self, chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes> {
let sealing_work = self.sealing_work.lock();
sealing_work.queue.peek_last_ref().map_or_else(|| chain.code(address), |b| b.block().fields().state.code(address))
sealing_work.queue.peek_last_ref().map_or_else(|| chain.latest_code(address), |b| b.block().fields().state.code(address))
}
fn set_author(&self, author: Address) {

View File

@ -12,7 +12,7 @@ build = "build.rs"
log = "0.3"
serde = "0.7.0"
serde_json = "0.7.0"
jsonrpc-core = "2.0"
jsonrpc-core = "2.1"
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" }
ethcore-io = { path = "../util/io" }
ethcore-util = { path = "../util" }

View File

@ -0,0 +1,96 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use util::numbers::*;
use util::rlp::encode;
use util::bytes::ToPretty;
use ethcore::miner::MinerService;
use ethcore::client::MiningBlockChainClient;
use ethcore::transaction::{Action, SignedTransaction, Transaction};
use ethcore::account_provider::AccountProvider;
use jsonrpc_core::{Error, Value, to_value};
use v1::helpers::TransactionRequest;
use v1::types::{H256 as RpcH256, H520 as RpcH520};
use v1::helpers::errors;
fn prepare_transaction<C, M>(client: &C, miner: &M, request: TransactionRequest) -> Transaction where C: MiningBlockChainClient, M: MinerService {
Transaction {
nonce: request.nonce
.or_else(|| miner
.last_nonce(&request.from)
.map(|nonce| nonce + U256::one()))
.unwrap_or_else(|| client.latest_nonce(&request.from)),
action: request.to.map_or(Action::Create, Action::Call),
gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()),
gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(client, miner)),
value: request.value.unwrap_or_else(U256::zero),
data: request.data.map_or_else(Vec::new, |b| b.to_vec()),
}
}
pub fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result<Value, Error>
where C: MiningBlockChainClient, M: MinerService {
let hash = RpcH256::from(signed_transaction.hash());
let import = miner.import_own_transaction(client, signed_transaction);
import
.map_err(errors::from_transaction_error)
.and_then(|_| to_value(&hash))
}
pub fn signature_with_password(accounts: &AccountProvider, address: Address, hash: H256, pass: String) -> Result<Value, Error> {
accounts.sign_with_password(address, pass, hash)
.map_err(errors::from_password_error)
.and_then(|hash| to_value(&RpcH520::from(hash)))
}
pub fn unlock_sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, password: String) -> Result<Value, Error>
where C: MiningBlockChainClient, M: MinerService {
let address = request.from;
let signed_transaction = {
let t = prepare_transaction(client, miner, request);
let hash = t.hash();
let signature = try!(account_provider.sign_with_password(address, password, hash).map_err(errors::from_password_error));
t.with_signature(signature)
};
trace!(target: "miner", "send_transaction: dispatching tx: {}", encode(&signed_transaction).to_vec().pretty());
dispatch_transaction(&*client, &*miner, signed_transaction)
}
pub fn sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, address: Address) -> Result<Value, Error>
where C: MiningBlockChainClient, M: MinerService {
let signed_transaction = {
let t = prepare_transaction(client, miner, request);
let hash = t.hash();
let signature = try!(account_provider.sign(address, hash).map_err(errors::from_signing_error));
t.with_signature(signature)
};
trace!(target: "miner", "send_transaction: dispatching tx: {}", encode(&signed_transaction).to_vec().pretty());
dispatch_transaction(&*client, &*miner, signed_transaction)
}
pub fn default_gas_price<C, M>(client: &C, miner: &M) -> U256 where C: MiningBlockChainClient, M: MinerService {
client
.gas_price_statistics(100, 8)
.map(|x| x[4])
.unwrap_or_else(|_| miner.sensible_gas_price())
}

View File

@ -0,0 +1,188 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! RPC Error codes and error objects
macro_rules! rpc_unimplemented {
() => (Err(::v1::helpers::errors::unimplemented()))
}
use std::fmt;
use ethcore::error::Error as EthcoreError;
use ethcore::account_provider::{Error as AccountError};
use jsonrpc_core::{Error, ErrorCode, Value};
mod codes {
// NOTE [ToDr] Codes from [-32099, -32000]
pub const UNSUPPORTED_REQUEST: i64 = -32000;
pub const NO_WORK: i64 = -32001;
pub const NO_AUTHOR: i64 = -32002;
pub const UNKNOWN_ERROR: i64 = -32009;
pub const TRANSACTION_ERROR: i64 = -32010;
pub const ACCOUNT_LOCKED: i64 = -32020;
pub const PASSWORD_INVALID: i64 = -32021;
pub const ACCOUNT_ERROR: i64 = -32023;
pub const SIGNER_DISABLED: i64 = -32030;
pub const REQUEST_REJECTED: i64 = -32040;
pub const REQUEST_NOT_FOUND: i64 = -32041;
pub const COMPILATION_ERROR: i64 = -32050;
}
pub fn unimplemented() -> Error {
Error {
code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST),
message: "This request is not implemented yet. Please create an issue on Github repo.".into(),
data: None
}
}
pub fn request_not_found() -> Error {
Error {
code: ErrorCode::ServerError(codes::REQUEST_NOT_FOUND),
message: "Request not found.".into(),
data: None,
}
}
pub fn request_rejected() -> Error {
Error {
code: ErrorCode::ServerError(codes::REQUEST_REJECTED),
message: "Request has been rejected.".into(),
data: None,
}
}
pub fn account<T: fmt::Debug>(error: &str, details: T) -> Error {
Error {
code: ErrorCode::ServerError(codes::ACCOUNT_ERROR),
message: error.into(),
data: Some(Value::String(format!("{:?}", details))),
}
}
pub fn compilation<T: fmt::Debug>(error: T) -> Error {
Error {
code: ErrorCode::ServerError(codes::COMPILATION_ERROR),
message: "Error while compiling code.".into(),
data: Some(Value::String(format!("{:?}", error))),
}
}
pub fn internal<T: fmt::Debug>(error: &str, data: T) -> Error {
Error {
code: ErrorCode::InternalError,
message: format!("Internal error occurred: {}", error),
data: Some(Value::String(format!("{:?}", data))),
}
}
pub fn invalid_params<T: fmt::Debug>(param: &str, details: T) -> Error {
Error {
code: ErrorCode::InvalidParams,
message: format!("Couldn't parse parameters: {}", param),
data: Some(Value::String(format!("{:?}", details))),
}
}
pub fn state_pruned() -> Error {
Error {
code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST),
message: "This request is not supported because your node is running with state pruning. Run with --pruning=archive.".into(),
data: None
}
}
pub fn no_work() -> Error {
Error {
code: ErrorCode::ServerError(codes::NO_WORK),
message: "Still syncing.".into(),
data: None
}
}
pub fn no_author() -> Error {
Error {
code: ErrorCode::ServerError(codes::NO_AUTHOR),
message: "Author not configured. Run Parity with --author to configure.".into(),
data: None
}
}
pub fn signer_disabled() -> Error {
Error {
code: ErrorCode::ServerError(codes::SIGNER_DISABLED),
message: "Trusted Signer is disabled. This API is not available.".into(),
data: None
}
}
pub fn from_signing_error(error: AccountError) -> Error {
Error {
code: ErrorCode::ServerError(codes::ACCOUNT_LOCKED),
message: "Your account is locked. Unlock the account via CLI, personal_unlockAccount or use Trusted Signer.".into(),
data: Some(Value::String(format!("{:?}", error))),
}
}
pub fn from_password_error(error: AccountError) -> Error {
Error {
code: ErrorCode::ServerError(codes::PASSWORD_INVALID),
message: "Account password is invalid or account does not exist.".into(),
data: Some(Value::String(format!("{:?}", error))),
}
}
pub fn from_transaction_error(error: EthcoreError) -> Error {
use ethcore::error::TransactionError::*;
if let EthcoreError::Transaction(e) = error {
let msg = match e {
AlreadyImported => "Transaction with the same hash was already imported.".into(),
Old => "Transaction nonce is too low. Try incrementing the nonce.".into(),
TooCheapToReplace => {
"Transaction fee is too low. There is another transaction with same nonce in the queue. Try increasing the fee or incrementing the nonce.".into()
},
LimitReached => {
"There are too many transactions in the queue. Your transaction was dropped due to limit. Try increasing the fee.".into()
},
InsufficientGasPrice { minimal, got } => {
format!("Transaction fee is too low. It does not satisfy your node's minimal fee (minimal: {}, got: {}). Try increasing the fee.", minimal, got)
},
InsufficientBalance { balance, cost } => {
format!("Insufficient funds. Account you try to send transaction from does not have enough funds. Required {} and got: {}.", cost, balance)
},
GasLimitExceeded { limit, got } => {
format!("Transaction cost exceeds current gas limit. Limit: {}, got: {}. Try decreasing supplied gas.", limit, got)
},
InvalidGasLimit(_) => "Supplied gas is beyond limit.".into(),
};
Error {
code: ErrorCode::ServerError(codes::TRANSACTION_ERROR),
message: msg,
data: None,
}
} else {
Error {
code: ErrorCode::ServerError(codes::UNKNOWN_ERROR),
message: "Unknown error when sending transaction.".into(),
data: Some(Value::String(format!("{:?}", error))),
}
}
}

View File

@ -14,6 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
#[macro_use]
pub mod errors;
pub mod dispatch;
pub mod params;
mod poll_manager;
mod poll_filter;
mod requests;

View File

@ -0,0 +1,53 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Parameters parsing helpers
use serde;
use jsonrpc_core::{Error, Params, from_params};
use v1::types::BlockNumber;
use v1::helpers::errors;
pub fn expect_no_params(params: Params) -> Result<(), Error> {
match params {
Params::None => Ok(()),
p => Err(errors::invalid_params("No parameters were expected", p)),
}
}
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)
}
}

View File

@ -43,8 +43,9 @@ use ethcore::filter::Filter as EthcoreFilter;
use self::ethash::SeedHashCompute;
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::helpers::CallRequest as CRequest;
use v1::impls::{default_gas_price, dispatch_transaction, error_codes, from_params_default_second, from_params_default_third};
use v1::helpers::{CallRequest as CRequest, errors};
use v1::helpers::dispatch::{default_gas_price, dispatch_transaction};
use v1::helpers::params::{expect_no_params, from_params_default_second, from_params_default_third};
/// Eth RPC options
pub struct EthClientOptions {
@ -214,30 +215,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.
fn make_unsupported_err() -> Error {
Error {
code: ErrorCode::ServerError(error_codes::UNSUPPORTED_REQUEST_CODE),
message: "Unsupported request.".into(),
data: None
}
}
fn no_work_err() -> Error {
Error {
code: ErrorCode::ServerError(error_codes::NO_WORK_CODE),
message: "Still syncing.".into(),
data: None
}
}
fn no_author_err() -> Error {
Error {
code: ErrorCode::ServerError(error_codes::NO_AUTHOR_CODE),
message: "Author not configured. Run parity with --author to configure.".into(),
data: None
}
}
impl<C, S: ?Sized, M, EM> EthClient<C, S, M, EM> where
C: MiningBlockChainClient + 'static,
S: SyncProvider + 'static,
@ -265,94 +242,80 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
fn protocol_version(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => Ok(Value::String(format!("{}", take_weak!(self.sync).status().protocol_version).to_owned())),
_ => Err(Error::invalid_params())
}
try!(expect_no_params(params));
Ok(Value::String(format!("{}", take_weak!(self.sync).status().protocol_version).to_owned()))
}
fn syncing(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => {
let status = take_weak!(self.sync).status();
let res = match status.state {
SyncState::Idle => SyncStatus::None,
SyncState::Waiting | SyncState::Blocks | SyncState::NewBlocks | SyncState::ChainHead => {
let current_block = U256::from(take_weak!(self.client).chain_info().best_block_number);
let highest_block = U256::from(status.highest_block_number.unwrap_or(status.start_block_number));
try!(expect_no_params(params));
if highest_block > current_block + U256::from(6) {
let info = SyncInfo {
starting_block: status.start_block_number.into(),
current_block: current_block.into(),
highest_block: highest_block.into(),
};
SyncStatus::Info(info)
} else {
SyncStatus::None
}
}
};
to_value(&res)
let status = take_weak!(self.sync).status();
let res = match status.state {
SyncState::Idle => SyncStatus::None,
SyncState::Waiting | SyncState::Blocks | SyncState::NewBlocks | SyncState::ChainHead => {
let current_block = U256::from(take_weak!(self.client).chain_info().best_block_number);
let highest_block = U256::from(status.highest_block_number.unwrap_or(status.start_block_number));
if highest_block > current_block + U256::from(6) {
let info = SyncInfo {
starting_block: status.start_block_number.into(),
current_block: current_block.into(),
highest_block: highest_block.into(),
};
SyncStatus::Info(info)
} else {
SyncStatus::None
}
}
_ => Err(Error::invalid_params()),
}
};
to_value(&res)
}
fn author(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => to_value(&RpcH160::from(take_weak!(self.miner).author())),
_ => Err(Error::invalid_params()),
}
try!(expect_no_params(params));
to_value(&RpcH160::from(take_weak!(self.miner).author()))
}
fn is_mining(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => to_value(&(take_weak!(self.miner).is_sealing())),
_ => Err(Error::invalid_params())
}
try!(expect_no_params(params));
to_value(&(take_weak!(self.miner).is_sealing()))
}
fn hashrate(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => to_value(&RpcU256::from(self.external_miner.hashrate())),
_ => Err(Error::invalid_params())
}
try!(expect_no_params(params));
to_value(&RpcU256::from(self.external_miner.hashrate()))
}
fn gas_price(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => {
let (client, miner) = (take_weak!(self.client), take_weak!(self.miner));
to_value(&RpcU256::from(default_gas_price(&*client, &*miner)))
}
_ => Err(Error::invalid_params())
}
try!(expect_no_params(params));
let (client, miner) = (take_weak!(self.client), take_weak!(self.miner));
to_value(&RpcU256::from(default_gas_price(&*client, &*miner)))
}
fn accounts(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => {
let store = take_weak!(self.accounts);
let accounts = try!(store.accounts().map_err(|_| Error::internal_error()));
to_value(&accounts.into_iter().map(Into::into).collect::<Vec<RpcH160>>())
},
_ => Err(Error::invalid_params())
}
try!(expect_no_params(params));
let store = take_weak!(self.accounts);
let accounts = try!(store.accounts().map_err(|e| errors::internal("Could not fetch accounts.", e)));
to_value(&accounts.into_iter().map(Into::into).collect::<Vec<RpcH160>>())
}
fn block_number(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => to_value(&RpcU256::from(take_weak!(self.client).chain_info().best_block_number)),
_ => Err(Error::invalid_params())
}
try!(expect_no_params(params));
to_value(&RpcU256::from(take_weak!(self.client).chain_info().best_block_number))
}
fn balance(&self, params: Params) -> Result<Value, Error> {
@ -362,7 +325,10 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
let address: Address = RpcH160::into(address);
match block_number {
BlockNumber::Pending => to_value(&RpcU256::from(take_weak!(self.miner).balance(take_weak!(self.client).deref(), &address))),
id => to_value(&RpcU256::from(try!(take_weak!(self.client).balance(&address, id.into()).ok_or_else(make_unsupported_err)))),
id => match take_weak!(self.client).balance(&address, id.into()) {
Some(balance) => to_value(&RpcU256::from(balance)),
None => Err(errors::state_pruned()),
}
}
})
}
@ -377,7 +343,7 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
BlockNumber::Pending => to_value(&RpcU256::from(take_weak!(self.miner).storage_at(&*take_weak!(self.client), &address, &H256::from(position)))),
id => match take_weak!(self.client).storage_at(&address, &H256::from(position), id.into()) {
Some(s) => to_value(&RpcH256::from(s)),
None => Err(make_unsupported_err()), // None is only returned on unsupported requests.
None => Err(errors::state_pruned()),
}
}
})
@ -391,7 +357,10 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
let address: Address = RpcH160::into(address);
match block_number {
BlockNumber::Pending => to_value(&RpcU256::from(take_weak!(self.miner).nonce(take_weak!(self.client).deref(), &address))),
id => to_value(&take_weak!(self.client).nonce(&address, id.into()).map(RpcU256::from)),
id => match take_weak!(self.client).nonce(&address, id.into()) {
Some(nonce) => to_value(&RpcU256::from(nonce)),
None => Err(errors::state_pruned()),
}
}
})
}
@ -441,8 +410,10 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
let address: Address = RpcH160::into(address);
match block_number {
BlockNumber::Pending => to_value(&take_weak!(self.miner).code(take_weak!(self.client).deref(), &address).map_or_else(Bytes::default, Bytes::new)),
BlockNumber::Latest => to_value(&take_weak!(self.client).code(&address).map_or_else(Bytes::default, Bytes::new)),
_ => Err(Error::invalid_params()),
_ => match take_weak!(self.client).code(&address, block_number.into()) {
Some(code) => to_value(&code.map_or_else(Bytes::default, Bytes::new)),
None => Err(errors::state_pruned()),
},
}
})
}
@ -515,16 +486,13 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
fn compilers(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => {
let mut compilers = vec![];
if Command::new(SOLC).output().is_ok() {
compilers.push("solidity".to_owned())
}
to_value(&compilers)
}
_ => Err(Error::invalid_params())
try!(expect_no_params(params));
let mut compilers = vec![];
if Command::new(SOLC).output().is_ok() {
compilers.push("solidity".to_owned())
}
to_value(&compilers)
}
fn logs(&self, params: Params) -> Result<Value, Error> {
@ -549,45 +517,42 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
fn work(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => {
let client = take_weak!(self.client);
// check if we're still syncing and return empty strings in that case
{
//TODO: check if initial sync is complete here
//let sync = take_weak!(self.sync);
if /*sync.status().state != SyncState::Idle ||*/ client.queue_info().total_queue_size() > MAX_QUEUE_SIZE_TO_MINE_ON {
trace!(target: "miner", "Syncing. Cannot give any work.");
return Err(no_work_err());
}
try!(expect_no_params(params));
// Otherwise spin until our submitted block has been included.
let timeout = Instant::now() + Duration::from_millis(1000);
while Instant::now() < timeout && client.queue_info().total_queue_size() > 0 {
thread::sleep(Duration::from_millis(1));
}
}
let client = take_weak!(self.client);
// check if we're still syncing and return empty strings in that case
{
//TODO: check if initial sync is complete here
//let sync = take_weak!(self.sync);
if /*sync.status().state != SyncState::Idle ||*/ client.queue_info().total_queue_size() > MAX_QUEUE_SIZE_TO_MINE_ON {
trace!(target: "miner", "Syncing. Cannot give any work.");
return Err(errors::no_work());
}
let miner = take_weak!(self.miner);
if miner.author().is_zero() {
warn!(target: "miner", "Cannot give work package - no author is configured. Use --author to configure!");
return Err(no_author_err())
}
miner.map_sealing_work(client.deref(), |b| {
let pow_hash = b.hash();
let target = Ethash::difficulty_to_boundary(b.block().header().difficulty());
let seed_hash = self.seed_compute.lock().get_seedhash(b.block().header().number());
if self.options.send_block_number_in_get_work {
let block_number = RpcU256::from(b.block().header().number());
to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target), block_number))
} else {
to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target)))
}
}).unwrap_or(Err(Error::internal_error())) // no work found.
},
_ => Err(Error::invalid_params())
// Otherwise spin until our submitted block has been included.
let timeout = Instant::now() + Duration::from_millis(1000);
while Instant::now() < timeout && client.queue_info().total_queue_size() > 0 {
thread::sleep(Duration::from_millis(1));
}
}
let miner = take_weak!(self.miner);
if miner.author().is_zero() {
warn!(target: "miner", "Cannot give work package - no author is configured. Use --author to configure!");
return Err(errors::no_author())
}
miner.map_sealing_work(client.deref(), |b| {
let pow_hash = b.hash();
let target = Ethash::difficulty_to_boundary(b.block().header().difficulty());
let seed_hash = self.seed_compute.lock().get_seedhash(b.block().header().number());
if self.options.send_block_number_in_get_work {
let block_number = RpcU256::from(b.block().header().number());
to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target), block_number))
} else {
to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target)))
}
}).unwrap_or(Err(Error::internal_error())) // no work found.
}
fn submit_work(&self, params: Params) -> Result<Value, Error> {
@ -627,7 +592,6 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
fn call(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
trace!(target: "jsonrpc", "call: {:?}", params);
from_params_default_second(params)
.and_then(|(request, block_number,)| {
let request = CallRequest::into(request);
@ -675,17 +639,23 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn();
if let Ok(mut child) = maybe_child {
if let Ok(_) = child.stdin.as_mut().expect("we called child.stdin(Stdio::piped()) before spawn; qed").write_all(code.as_bytes()) {
if let Ok(output) = child.wait_with_output() {
let s = String::from_utf8_lossy(&output.stdout);
if let Some(hex) = s.lines().skip_while(|ref l| !l.contains("Binary")).skip(1).next() {
return to_value(&Bytes::new(hex.from_hex().unwrap_or(vec![])));
}
maybe_child
.map_err(errors::compilation)
.and_then(|mut child| {
try!(child.stdin.as_mut()
.expect("we called child.stdin(Stdio::piped()) before spawn; qed")
.write_all(code.as_bytes())
.map_err(errors::compilation));
let output = try!(child.wait_with_output().map_err(errors::compilation));
let s = String::from_utf8_lossy(&output.stdout);
if let Some(hex) = s.lines().skip_while(|ref l| !l.contains("Binary")).skip(1).next() {
to_value(&Bytes::new(hex.from_hex().unwrap_or(vec![])))
} else {
Err(errors::compilation("Unexpected output."))
}
}
}
Err(Error::invalid_params())
})
})
}
}

View File

@ -27,6 +27,7 @@ use util::Mutex;
use v1::traits::EthFilter;
use v1::types::{BlockNumber, Index, Filter, Log, H256 as RpcH256, U256 as RpcU256};
use v1::helpers::{PollFilter, PollManager};
use v1::helpers::params::expect_no_params;
use v1::impls::eth::pending_logs;
/// Eth filter rpc implementation.
@ -76,28 +77,22 @@ impl<C, M> EthFilter for EthFilterClient<C, M> where
fn new_block_filter(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => {
let mut polls = self.polls.lock();
let id = polls.create_poll(PollFilter::Block(take_weak!(self.client).chain_info().best_block_number));
to_value(&RpcU256::from(id))
},
_ => Err(Error::invalid_params())
}
try!(expect_no_params(params));
let mut polls = self.polls.lock();
let id = polls.create_poll(PollFilter::Block(take_weak!(self.client).chain_info().best_block_number));
to_value(&RpcU256::from(id))
}
fn new_pending_transaction_filter(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => {
let mut polls = self.polls.lock();
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes();
let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions));
try!(expect_no_params(params));
to_value(&RpcU256::from(id))
},
_ => Err(Error::invalid_params())
}
let mut polls = self.polls.lock();
let pending_transactions = take_weak!(self.miner).pending_transactions_hashes();
let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions));
to_value(&RpcU256::from(id))
}
fn filter_changes(&self, params: Params) -> Result<Value, Error> {

View File

@ -23,10 +23,10 @@ use ethcore::client::MiningBlockChainClient;
use util::{U256, Address, H256, Mutex};
use transient_hashmap::TransientHashMap;
use ethcore::account_provider::AccountProvider;
use v1::helpers::{SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationsQueue, ConfirmationPayload, TransactionRequest as TRequest, FilledTransactionRequest as FilledRequest};
use v1::helpers::{errors, SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationsQueue, ConfirmationPayload, TransactionRequest as TRequest, FilledTransactionRequest as FilledRequest};
use v1::helpers::dispatch::{default_gas_price, sign_and_dispatch};
use v1::traits::EthSigning;
use v1::types::{TransactionRequest, H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, U256 as RpcU256};
use v1::impls::{default_gas_price, sign_and_dispatch, request_rejected_error, request_not_found_error, signer_disabled_error};
fn fill_optional_fields<C, M>(request: TRequest, client: &C, miner: &M) -> FilledRequest
where C: MiningBlockChainClient, M: MinerService {
@ -151,10 +151,10 @@ impl<C, M> EthSigning for EthSigningQueueClient<C, M>
let res = match pending.get(&id) {
Some(ref promise) => match promise.result() {
ConfirmationResult::Waiting => { return Ok(Value::Null); }
ConfirmationResult::Rejected => Err(request_rejected_error()),
ConfirmationResult::Rejected => Err(errors::request_rejected()),
ConfirmationResult::Confirmed(rpc_response) => rpc_response,
},
_ => { return Err(request_not_found_error()); }
_ => { return Err(errors::request_not_found()); }
};
pending.remove(&id);
res
@ -217,16 +217,16 @@ impl<C, M> EthSigning for EthSigningUnsafeClient<C, M> where
fn post_sign(&self, _: Params) -> Result<Value, Error> {
// We don't support this in non-signer mode.
Err(signer_disabled_error())
Err(errors::signer_disabled())
}
fn post_transaction(&self, _: Params) -> Result<Value, Error> {
// We don't support this in non-signer mode.
Err(signer_disabled_error())
Err(errors::signer_disabled())
}
fn check_request(&self, _: Params) -> Result<Value, Error> {
// We don't support this in non-signer mode.
Err(signer_disabled_error())
Err(errors::signer_disabled())
}
}

View File

@ -25,8 +25,8 @@ use jsonrpc_core::*;
use ethcore::miner::MinerService;
use v1::traits::Ethcore;
use v1::types::{Bytes, U256};
use v1::helpers::{SigningQueue, ConfirmationsQueue, NetworkSettings};
use v1::impls::signer_disabled_error;
use v1::helpers::{errors, SigningQueue, ConfirmationsQueue, NetworkSettings};
use v1::helpers::params::expect_no_params;
/// Ethcore implementation.
pub struct EthcoreClient<C, M> where
@ -61,64 +61,76 @@ impl<C, M> EthcoreClient<C, M> where C: MiningBlockChainClient, M: MinerService
impl<C, M> Ethcore for EthcoreClient<C, M> where M: MinerService + 'static, C: MiningBlockChainClient + 'static {
fn transactions_limit(&self, _: Params) -> Result<Value, Error> {
fn transactions_limit(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
to_value(&take_weak!(self.miner).transactions_limit())
}
fn min_gas_price(&self, _: Params) -> Result<Value, Error> {
fn min_gas_price(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
to_value(&U256::from(take_weak!(self.miner).minimal_gas_price()))
}
fn extra_data(&self, _: Params) -> Result<Value, Error> {
fn extra_data(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
to_value(&Bytes::new(take_weak!(self.miner).extra_data()))
}
fn gas_floor_target(&self, _: Params) -> Result<Value, Error> {
fn gas_floor_target(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
to_value(&U256::from(take_weak!(self.miner).gas_floor_target()))
}
fn gas_ceil_target(&self, _: Params) -> Result<Value, Error> {
fn gas_ceil_target(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
to_value(&U256::from(take_weak!(self.miner).gas_ceil_target()))
}
fn dev_logs(&self, _params: Params) -> Result<Value, Error> {
fn dev_logs(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
let logs = self.logger.logs();
to_value(&logs.deref().as_slice())
}
fn dev_logs_levels(&self, _params: Params) -> Result<Value, Error> {
fn dev_logs_levels(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
to_value(&self.logger.levels())
}
fn net_chain(&self, _params: Params) -> Result<Value, Error> {
fn net_chain(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
to_value(&self.settings.chain)
}
fn net_max_peers(&self, _params: Params) -> Result<Value, Error> {
fn net_max_peers(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
to_value(&self.settings.max_peers)
}
fn net_port(&self, _params: Params) -> Result<Value, Error> {
fn net_port(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
to_value(&self.settings.network_port)
}
fn node_name(&self, _params: Params) -> Result<Value, Error> {
fn node_name(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
to_value(&self.settings.name)
}
fn rpc_settings(&self, _params: Params) -> Result<Value, Error> {
fn rpc_settings(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
let mut map = BTreeMap::new();
map.insert("enabled".to_owned(), Value::Bool(self.settings.rpc_enabled));
map.insert("interface".to_owned(), Value::String(self.settings.rpc_interface.clone()));
@ -128,30 +140,29 @@ impl<C, M> Ethcore for EthcoreClient<C, M> where M: MinerService + 'static, C: M
fn default_extra_data(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => to_value(&Bytes::new(version_data())),
_ => Err(Error::invalid_params()),
}
try!(expect_no_params(params));
to_value(&Bytes::new(version_data()))
}
fn gas_price_statistics(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => match take_weak!(self.client).gas_price_statistics(100, 8) {
Ok(stats) => to_value(&stats
.into_iter()
.map(|x| to_value(&U256::from(x)).expect("x must be U256; qed"))
.collect::<Vec<_>>()),
_ => Err(Error::internal_error()),
},
_ => Err(Error::invalid_params()),
try!(expect_no_params(params));
match take_weak!(self.client).gas_price_statistics(100, 8) {
Ok(stats) => to_value(&stats
.into_iter()
.map(|x| to_value(&U256::from(x)).expect("x must be U256; qed"))
.collect::<Vec<_>>()),
_ => Err(Error::internal_error()),
}
}
fn unsigned_transactions_count(&self, _params: Params) -> Result<Value, Error> {
fn unsigned_transactions_count(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
match self.confirmations_queue {
None => Err(signer_disabled_error()),
None => Err(errors::signer_disabled()),
Some(ref queue) => to_value(&queue.len()),
}
}

View File

@ -20,6 +20,8 @@ use jsonrpc_core::*;
use ethcore::miner::MinerService;
use ethcore::client::MiningBlockChainClient;
use ethsync::ManageNetwork;
use v1::helpers::errors;
use v1::helpers::params::expect_no_params;
use v1::traits::EthcoreSet;
use v1::types::{Bytes, H160, U256};
@ -117,7 +119,7 @@ impl<C, M> EthcoreSet for EthcoreSetClient<C, M> where
from_params::<(String,)>(params).and_then(|(peer,)| {
match take_weak!(self.net).add_reserved_peer(peer) {
Ok(()) => to_value(&true),
Err(_) => Err(Error::invalid_params()),
Err(e) => Err(errors::invalid_params("Peer address", e)),
}
})
}
@ -127,29 +129,33 @@ impl<C, M> EthcoreSet for EthcoreSetClient<C, M> where
from_params::<(String,)>(params).and_then(|(peer,)| {
match take_weak!(self.net).remove_reserved_peer(peer) {
Ok(()) => to_value(&true),
Err(_) => Err(Error::invalid_params()),
Err(e) => Err(errors::invalid_params("Peer address", e)),
}
})
}
fn drop_non_reserved_peers(&self, _: Params) -> Result<Value, Error> {
fn drop_non_reserved_peers(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
take_weak!(self.net).deny_unreserved_peers();
to_value(&true)
}
fn accept_non_reserved_peers(&self, _: Params) -> Result<Value, Error> {
fn accept_non_reserved_peers(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
take_weak!(self.net).accept_unreserved_peers();
to_value(&true)
}
fn start_network(&self, _: Params) -> Result<Value, Error> {
fn start_network(&self, params: Params) -> Result<Value, Error> {
try!(expect_no_params(params));
take_weak!(self.net).start_network();
Ok(Value::Bool(true))
}
fn stop_network(&self, _: Params) -> Result<Value, Error> {
fn stop_network(&self, params: Params) -> Result<Value, Error> {
try!(expect_no_params(params));
take_weak!(self.net).stop_network();
Ok(Value::Bool(true))
}

View File

@ -25,21 +25,17 @@ macro_rules! take_weak {
}
}
macro_rules! rpc_unimplemented {
() => (Err(Error::internal_error()))
}
mod web3;
mod eth;
mod eth_filter;
mod eth_signing;
mod ethcore;
mod ethcore_set;
mod net;
mod personal;
mod personal_signer;
mod ethcore;
mod ethcore_set;
mod traces;
mod rpc;
mod traces;
mod web3;
pub use self::web3::Web3Client;
pub use self::eth::{EthClient, EthClientOptions};
@ -53,202 +49,3 @@ pub use self::ethcore_set::EthcoreSetClient;
pub use self::traces::TracesClient;
pub use self::rpc::RpcClient;
use serde;
use v1::helpers::TransactionRequest;
use v1::types::{H256 as RpcH256, H520 as RpcH520, BlockNumber};
use ethcore::error::Error as EthcoreError;
use ethcore::miner::MinerService;
use ethcore::client::MiningBlockChainClient;
use ethcore::transaction::{Action, SignedTransaction, Transaction};
use ethcore::account_provider::{AccountProvider, Error as AccountError};
use util::numbers::*;
use util::rlp::encode;
use util::bytes::ToPretty;
use jsonrpc_core::{Error, ErrorCode, Value, to_value, from_params, Params};
mod error_codes {
// NOTE [ToDr] Codes from [-32099, -32000]
pub const UNSUPPORTED_REQUEST_CODE: i64 = -32000;
pub const NO_WORK_CODE: i64 = -32001;
pub const NO_AUTHOR_CODE: i64 = -32002;
pub const UNKNOWN_ERROR: i64 = -32009;
pub const TRANSACTION_ERROR: i64 = -32010;
pub const ACCOUNT_LOCKED: i64 = -32020;
pub const PASSWORD_INVALID: i64 = -32021;
pub const SIGNER_DISABLED: i64 = -32030;
pub const REQUEST_REJECTED: i64 = -32040;
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>
where C: MiningBlockChainClient, M: MinerService {
let hash = RpcH256::from(signed_transaction.hash());
let import = miner.import_own_transaction(client, signed_transaction);
import
.map_err(transaction_error)
.and_then(|_| to_value(&hash))
}
fn signature_with_password(accounts: &AccountProvider, address: Address, hash: H256, pass: String) -> Result<Value, Error> {
accounts.sign_with_password(address, pass, hash)
.map_err(password_error)
.and_then(|hash| to_value(&RpcH520::from(hash)))
}
fn prepare_transaction<C, M>(client: &C, miner: &M, request: TransactionRequest) -> Transaction where C: MiningBlockChainClient, M: MinerService {
Transaction {
nonce: request.nonce
.or_else(|| miner
.last_nonce(&request.from)
.map(|nonce| nonce + U256::one()))
.unwrap_or_else(|| client.latest_nonce(&request.from)),
action: request.to.map_or(Action::Create, Action::Call),
gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()),
gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(client, miner)),
value: request.value.unwrap_or_else(U256::zero),
data: request.data.map_or_else(Vec::new, |b| b.to_vec()),
}
}
fn unlock_sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, password: String) -> Result<Value, Error>
where C: MiningBlockChainClient, M: MinerService {
let address = request.from;
let signed_transaction = {
let t = prepare_transaction(client, miner, request);
let hash = t.hash();
let signature = try!(account_provider.sign_with_password(address, password, hash).map_err(password_error));
t.with_signature(signature)
};
trace!(target: "miner", "send_transaction: dispatching tx: {}", encode(&signed_transaction).to_vec().pretty());
dispatch_transaction(&*client, &*miner, signed_transaction)
}
fn sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, address: Address) -> Result<Value, Error>
where C: MiningBlockChainClient, M: MinerService {
let signed_transaction = {
let t = prepare_transaction(client, miner, request);
let hash = t.hash();
let signature = try!(account_provider.sign(address, hash).map_err(signing_error));
t.with_signature(signature)
};
trace!(target: "miner", "send_transaction: dispatching tx: {}", encode(&signed_transaction).to_vec().pretty());
dispatch_transaction(&*client, &*miner, signed_transaction)
}
fn default_gas_price<C, M>(client: &C, miner: &M) -> U256 where C: MiningBlockChainClient, M: MinerService {
client
.gas_price_statistics(100, 8)
.map(|x| x[4])
.unwrap_or_else(|_| miner.sensible_gas_price())
}
fn signer_disabled_error() -> Error {
Error {
code: ErrorCode::ServerError(error_codes::SIGNER_DISABLED),
message: "Trusted Signer is disabled. This API is not available.".into(),
data: None
}
}
fn signing_error(error: AccountError) -> Error {
Error {
code: ErrorCode::ServerError(error_codes::ACCOUNT_LOCKED),
message: "Your account is locked. Unlock the account via CLI, personal_unlockAccount or use Trusted Signer.".into(),
data: Some(Value::String(format!("{:?}", error))),
}
}
fn password_error(error: AccountError) -> Error {
Error {
code: ErrorCode::ServerError(error_codes::PASSWORD_INVALID),
message: "Account password is invalid or account does not exist.".into(),
data: Some(Value::String(format!("{:?}", error))),
}
}
/// Error returned when request is rejected (in Trusted Signer).
pub fn request_rejected_error() -> Error {
Error {
code: ErrorCode::ServerError(error_codes::REQUEST_REJECTED),
message: "Request has been rejected.".into(),
data: None,
}
}
/// Error returned when request is not found in queue.
pub fn request_not_found_error() -> Error {
Error {
code: ErrorCode::ServerError(error_codes::REQUEST_NOT_FOUND),
message: "Request not found.".into(),
data: None,
}
}
fn transaction_error(error: EthcoreError) -> Error {
use ethcore::error::TransactionError::*;
if let EthcoreError::Transaction(e) = error {
let msg = match e {
AlreadyImported => "Transaction with the same hash was already imported.".into(),
Old => "Transaction nonce is too low. Try incrementing the nonce.".into(),
TooCheapToReplace => {
"Transaction fee is too low. There is another transaction with same nonce in the queue. Try increasing the fee or incrementing the nonce.".into()
},
LimitReached => {
"There are too many transactions in the queue. Your transaction was dropped due to limit. Try increasing the fee.".into()
},
InsufficientGasPrice { minimal, got } => {
format!("Transaction fee is too low. It does not satisfy your node's minimal fee (minimal: {}, got: {}). Try increasing the fee.", minimal, got)
},
InsufficientBalance { balance, cost } => {
format!("Insufficient funds. Account you try to send transaction from does not have enough funds. Required {} and got: {}.", cost, balance)
},
GasLimitExceeded { limit, got } => {
format!("Transaction cost exceeds current gas limit. Limit: {}, got: {}. Try decreasing supplied gas.", limit, got)
},
InvalidGasLimit(_) => "Supplied gas is beyond limit.".into(),
};
Error {
code: ErrorCode::ServerError(error_codes::TRANSACTION_ERROR),
message: msg,
data: None,
}
} else {
Error {
code: ErrorCode::ServerError(error_codes::UNKNOWN_ERROR),
message: "Unknown error when sending transaction.".into(),
data: Some(Value::String(format!("{:?}", error))),
}
}
}

View File

@ -19,6 +19,7 @@ use std::sync::{Arc, Weak};
use jsonrpc_core::*;
use ethsync::SyncProvider;
use v1::traits::Net;
use v1::helpers::params::expect_no_params;
/// Net rpc implementation.
pub struct NetClient<S: ?Sized> where S: SyncProvider {
@ -35,15 +36,18 @@ impl<S: ?Sized> NetClient<S> where S: SyncProvider {
}
impl<S: ?Sized> Net for NetClient<S> where S: SyncProvider + 'static {
fn version(&self, _: Params) -> Result<Value, Error> {
fn version(&self, params: Params) -> Result<Value, Error> {
try!(expect_no_params(params));
Ok(Value::String(format!("{}", take_weak!(self.sync).status().network_id).to_owned()))
}
fn peer_count(&self, _params: Params) -> Result<Value, Error> {
fn peer_count(&self, params: Params) -> Result<Value, Error> {
try!(expect_no_params(params));
Ok(Value::String(format!("0x{:x}", take_weak!(self.sync).status().num_peers as u64).to_owned()))
}
fn is_listening(&self, _: Params) -> Result<Value, Error> {
fn is_listening(&self, params: Params) -> Result<Value, Error> {
try!(expect_no_params(params));
// right now (11 march 2016), we are always listening for incoming connections
Ok(Value::Bool(true))
}

View File

@ -20,8 +20,9 @@ use std::collections::{BTreeMap};
use jsonrpc_core::*;
use v1::traits::Personal;
use v1::types::{H160 as RpcH160, TransactionRequest};
use v1::impls::unlock_sign_and_dispatch;
use v1::helpers::{TransactionRequest as TRequest};
use v1::helpers::{errors, TransactionRequest as TRequest};
use v1::helpers::params::expect_no_params;
use v1::helpers::dispatch::unlock_sign_and_dispatch;
use ethcore::account_provider::AccountProvider;
use util::Address;
use ethcore::client::MiningBlockChainClient;
@ -57,8 +58,10 @@ impl<C, M> PersonalClient<C, M> where C: MiningBlockChainClient, M: MinerService
impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBlockChainClient, M: MinerService {
fn signer_enabled(&self, _: Params) -> Result<Value, Error> {
fn signer_enabled(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
self.signer_port
.map(|v| to_value(&v))
.unwrap_or_else(|| to_value(&false))
@ -66,14 +69,11 @@ impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBl
fn accounts(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
match params {
Params::None => {
let store = take_weak!(self.accounts);
let accounts = try!(store.accounts().map_err(|_| Error::internal_error()));
to_value(&accounts.into_iter().map(Into::into).collect::<Vec<RpcH160>>())
},
_ => Err(Error::invalid_params())
}
try!(expect_no_params(params));
let store = take_weak!(self.accounts);
let accounts = try!(store.accounts().map_err(|e| errors::internal("Could not fetch accounts.", e)));
to_value(&accounts.into_iter().map(Into::into).collect::<Vec<RpcH160>>())
}
fn new_account(&self, params: Params) -> Result<Value, Error> {
@ -83,7 +83,7 @@ impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBl
let store = take_weak!(self.accounts);
match store.new_account(&pass) {
Ok(address) => to_value(&RpcH160::from(address)),
Err(_) => Err(Error::internal_error())
Err(e) => Err(errors::account("Could not create account.", e)),
}
}
)
@ -124,7 +124,7 @@ impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBl
let store = take_weak!(self.accounts);
from_params::<(RpcH160, _)>(params).and_then(|(addr, name)| {
let addr: Address = addr.into();
store.set_account_name(addr, name).map_err(|_| Error::invalid_params()).map(|_| Value::Null)
store.set_account_name(addr, name).map_err(|e| errors::account("Could not set account name.", e)).map(|_| Value::Null)
})
}
@ -133,14 +133,16 @@ impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBl
let store = take_weak!(self.accounts);
from_params::<(RpcH160, _)>(params).and_then(|(addr, meta)| {
let addr: Address = addr.into();
store.set_account_meta(addr, meta).map_err(|_| Error::invalid_params()).map(|_| Value::Null)
store.set_account_meta(addr, meta).map_err(|e| errors::account("Could not set account meta.", e)).map(|_| Value::Null)
})
}
fn accounts_info(&self, _: Params) -> Result<Value, Error> {
fn accounts_info(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
let store = take_weak!(self.accounts);
Ok(Value::Object(try!(store.accounts_info().map_err(|_| Error::invalid_params())).into_iter().map(|(a, v)| {
let info = try!(store.accounts_info().map_err(|e| errors::account("Could not fetch account info.", e)));
Ok(Value::Object(info.into_iter().map(|(a, v)| {
let m = map![
"name".to_owned() => to_value(&v.name).unwrap(),
"meta".to_owned() => to_value(&v.meta).unwrap(),

View File

@ -23,8 +23,9 @@ use ethcore::client::MiningBlockChainClient;
use ethcore::miner::MinerService;
use v1::traits::PersonalSigner;
use v1::types::{TransactionModification, ConfirmationRequest, U256};
use v1::impls::{unlock_sign_and_dispatch, signature_with_password};
use v1::helpers::{SigningQueue, ConfirmationsQueue, ConfirmationPayload};
use v1::helpers::{errors, SigningQueue, ConfirmationsQueue, ConfirmationPayload};
use v1::helpers::params::expect_no_params;
use v1::helpers::dispatch::{unlock_sign_and_dispatch, signature_with_password};
/// Transactions confirmation (personal) rpc implementation.
pub struct SignerClient<C, M> where C: MiningBlockChainClient, M: MinerService {
@ -55,8 +56,9 @@ impl<C: 'static, M: 'static> SignerClient<C, M> where C: MiningBlockChainClient,
impl<C: 'static, M: 'static> PersonalSigner for SignerClient<C, M> where C: MiningBlockChainClient, M: MinerService {
fn requests_to_confirm(&self, _params: Params) -> Result<Value, Error> {
fn requests_to_confirm(&self, params: Params) -> Result<Value, Error> {
try!(self.active());
try!(expect_no_params(params));
let queue = take_weak!(self.queue);
to_value(&queue.requests().into_iter().map(From::from).collect::<Vec<ConfirmationRequest>>())
}
@ -91,7 +93,7 @@ impl<C: 'static, M: 'static> PersonalSigner for SignerClient<C, M> where C: Mini
queue.request_confirmed(id, Ok(response.clone()));
}
result
}).unwrap_or_else(|| Err(Error::invalid_params()))
}).unwrap_or_else(|| Err(errors::invalid_params("Unknown RequestID", id)))
}
)
}

View File

@ -18,6 +18,7 @@
use std::collections::BTreeMap;
use jsonrpc_core::*;
use v1::traits::Rpc;
use v1::helpers::params::expect_no_params;
/// RPC generic methods implementation.
pub struct RpcClient {
@ -39,7 +40,8 @@ impl RpcClient {
}
impl Rpc for RpcClient {
fn rpc_modules(&self, _: Params) -> Result<Value, Error> {
fn rpc_modules(&self, params: Params) -> Result<Value, Error> {
try!(expect_no_params(params));
let modules = self.modules.iter()
.fold(BTreeMap::new(), |mut map, (k, v)| {
map.insert(k.to_owned(), Value::String(v.to_owned()));
@ -48,7 +50,8 @@ impl Rpc for RpcClient {
Ok(Value::Object(modules))
}
fn modules(&self, _: Params) -> Result<Value, Error> {
fn modules(&self, params: Params) -> Result<Value, Error> {
try!(expect_no_params(params));
let modules = self.modules.iter()
.filter(|&(k, _v)| {
self.valid_apis.contains(k)

View File

@ -23,9 +23,9 @@ use ethcore::client::{BlockChainClient, CallAnalytics, TransactionID, TraceId};
use ethcore::miner::MinerService;
use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
use v1::traits::Traces;
use v1::helpers::CallRequest as CRequest;
use v1::helpers::{errors, CallRequest as CRequest};
use v1::helpers::params::from_params_default_third;
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 {
CallAnalytics {
@ -144,7 +144,7 @@ impl<C, M> Traces for TracesClient<C, M> where C: BlockChainClient + 'static, M:
Ok(e) => to_value(&TraceResults::from(e)),
_ => Ok(Value::Null),
},
Err(_) => Err(Error::invalid_params()),
Err(e) => Err(errors::invalid_params("Transaction is not valid RLP", e)),
}
})
}

View File

@ -19,6 +19,7 @@ use jsonrpc_core::*;
use util::version;
use v1::traits::Web3;
use v1::types::{H256, Bytes};
use v1::helpers::params::expect_no_params;
use util::sha3::Hashable;
/// Web3 rpc implementation.
@ -31,10 +32,8 @@ impl Web3Client {
impl Web3 for Web3Client {
fn client_version(&self, params: Params) -> Result<Value, Error> {
match params {
Params::None => Ok(Value::String(version().to_owned().replace("Parity/", "Parity//"))),
_ => Err(Error::invalid_params())
}
try!(expect_no_params(params));
Ok(Value::String(version().to_owned().replace("Parity/", "Parity//")))
}
fn sha3(&self, params: Params) -> Result<Value, Error> {

View File

@ -18,8 +18,9 @@
//!
//! Compliant with ethereum rpc.
mod impls;
#[macro_use]
mod helpers;
mod impls;
pub mod traits;
pub mod tests;

View File

@ -12,7 +12,7 @@ rustc_version = "0.1"
[dependencies]
rand = "0.3.14"
jsonrpc-core = "2.0"
jsonrpc-core = "2.1"
log = "0.3"
env_logger = "0.3"
ws = { git = "https://github.com/ethcore/ws-rs.git", branch = "stable" }