Merge remote-tracking branch 'upstream/new-jsonrpc' into pip-msg

This commit is contained in:
Robert Habermeier
2017-03-21 21:15:06 +01:00
48 changed files with 829 additions and 659 deletions

View File

@@ -19,31 +19,32 @@
#![cfg_attr(feature="nightly", feature(plugin))]
#![cfg_attr(feature="nightly", plugin(clippy))]
extern crate semver;
extern crate rustc_serialize;
extern crate serde;
extern crate serde_json;
extern crate jsonrpc_core;
extern crate jsonrpc_http_server;
extern crate ethcore_io as io;
extern crate ethcore;
extern crate ethkey;
extern crate ethcrypto as crypto;
extern crate ethstore;
extern crate ethsync;
extern crate ethash;
extern crate ethcore_light as light;
extern crate transient_hashmap;
extern crate jsonrpc_ipc_server as ipc;
extern crate ethcore_ipc;
extern crate time;
extern crate rlp;
extern crate fetch;
extern crate futures;
extern crate order_stat;
extern crate parity_updater as updater;
extern crate rustc_serialize;
extern crate semver;
extern crate serde;
extern crate serde_json;
extern crate time;
extern crate transient_hashmap;
extern crate jsonrpc_core;
pub extern crate jsonrpc_http_server as http;
pub extern crate jsonrpc_ipc_server as ipc;
extern crate ethash;
extern crate ethcore;
extern crate ethcore_io as io;
extern crate ethcore_ipc;
extern crate ethcore_light as light;
extern crate ethcrypto as crypto;
extern crate ethkey;
extern crate ethstore;
extern crate ethsync;
extern crate fetch;
extern crate parity_reactor;
extern crate parity_updater as updater;
extern crate rlp;
extern crate stats;
#[macro_use]
@@ -60,57 +61,53 @@ extern crate ethjson;
#[cfg(test)]
extern crate ethcore_devtools as devtools;
use std::sync::Arc;
use std::net::SocketAddr;
use io::PanicHandler;
use jsonrpc_core::reactor::RpcHandler;
pub use ipc::{Server as IpcServer, Error as IpcServerError};
pub use jsonrpc_http_server::{ServerBuilder, Server, RpcServerError, HttpMetaExtractor};
pub mod v1;
pub use ipc::{Server as IpcServer, MetaExtractor as IpcMetaExtractor, RequestContext as IpcRequestContext};
pub use http::{HttpMetaExtractor, Server as HttpServer, Error as HttpServerError, AccessControlAllowOrigin, Host};
pub use v1::{SigningQueue, SignerService, ConfirmationsQueue, NetworkSettings, Metadata, Origin, informant, dispatch};
pub use v1::block_import::is_major_importing;
use std::net::SocketAddr;
use http::tokio_core;
/// Start http server asynchronously and returns result with `Server` handle on success or an error.
pub fn start_http<M, T, S>(
pub fn start_http<M, S, H, T>(
addr: &SocketAddr,
cors_domains: Option<Vec<String>>,
allowed_hosts: Option<Vec<String>>,
panic_handler: Arc<PanicHandler>,
handler: RpcHandler<M, S>,
cors_domains: http::DomainsValidation<http::AccessControlAllowOrigin>,
allowed_hosts: http::DomainsValidation<http::Host>,
handler: H,
remote: tokio_core::reactor::Remote,
extractor: T,
) -> Result<Server, RpcServerError> where
) -> Result<HttpServer, HttpServerError> where
M: jsonrpc_core::Metadata,
S: jsonrpc_core::Middleware<M>,
H: Into<jsonrpc_core::MetaIoHandler<M, S>>,
T: HttpMetaExtractor<M>,
{
let cors_domains = cors_domains.map(|domains| {
domains.into_iter()
.map(|v| match v.as_str() {
"*" => jsonrpc_http_server::AccessControlAllowOrigin::Any,
"null" => jsonrpc_http_server::AccessControlAllowOrigin::Null,
v => jsonrpc_http_server::AccessControlAllowOrigin::Value(v.into()),
})
.collect()
});
ServerBuilder::with_rpc_handler(handler)
.meta_extractor(Arc::new(extractor))
http::ServerBuilder::new(handler)
.event_loop_remote(remote)
.meta_extractor(extractor)
.cors(cors_domains.into())
.allowed_hosts(allowed_hosts.into())
.panic_handler(move || {
panic_handler.notify_all("Panic in RPC thread.".to_owned());
})
.start_http(addr)
}
/// Start ipc server asynchronously and returns result with `Server` handle on success or an error.
pub fn start_ipc<M: jsonrpc_core::Metadata, S: jsonrpc_core::Middleware<M>>(
pub fn start_ipc<M, S, H, T>(
addr: &str,
handler: RpcHandler<M, S>,
) -> Result<ipc::Server<M, S>, ipc::Error> {
let server = ipc::Server::with_rpc_handler(addr, handler)?;
server.run_async()?;
Ok(server)
handler: H,
remote: tokio_core::reactor::Remote,
extractor: T,
) -> ::std::io::Result<ipc::Server> where
M: jsonrpc_core::Metadata,
S: jsonrpc_core::Middleware<M>,
H: Into<jsonrpc_core::MetaIoHandler<M, S>>,
T: IpcMetaExtractor<M>,
{
ipc::ServerBuilder::new(handler)
.event_loop_remote(remote)
.session_metadata_extractor(extractor)
.start(addr)
}

View File

@@ -21,6 +21,7 @@ pub mod block_import;
pub mod dispatch;
pub mod fake_sign;
pub mod informant;
pub mod oneshot;
mod network_settings;
mod poll_manager;

View File

@@ -0,0 +1,67 @@
// Copyright 2015-2017 Parity Technologies (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 jsonrpc_core::Error;
use futures::{self, Future};
use futures::sync::oneshot;
use v1::helpers::errors;
pub type Res<T> = Result<T, Error>;
pub struct Sender<T> {
sender: oneshot::Sender<Res<T>>,
}
impl<T> Sender<T> {
pub fn send(self, data: Res<T>) {
let res = self.sender.send(data);
if let Err(_) = res {
debug!(target: "rpc", "Responding to a no longer active request.");
}
}
}
pub struct Receiver<T> {
receiver: oneshot::Receiver<Res<T>>,
}
impl<T> Future for Receiver<T> {
type Item = T;
type Error = Error;
fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
let res = self.receiver.poll();
match res {
Ok(futures::Async::NotReady) => Ok(futures::Async::NotReady),
Ok(futures::Async::Ready(Ok(res))) => Ok(futures::Async::Ready(res)),
Ok(futures::Async::Ready(Err(err))) => Err(err),
Err(e) => {
debug!(target: "rpc", "Responding to a canceled request: {:?}", e);
Err(errors::internal("Request was canceled by client.", e))
},
}
}
}
pub fn oneshot<T>() -> (Sender<T>, Receiver<T>) {
let (tx, rx) = futures::oneshot();
(Sender {
sender: tx,
}, Receiver {
receiver: rx,
})
}

View File

@@ -22,10 +22,10 @@ use util::{U256, Mutex};
use ethcore::account_provider::AccountProvider;
use futures::{self, future, BoxFuture, Future};
use futures::{future, BoxFuture, Future};
use jsonrpc_core::Error;
use v1::helpers::{
errors,
errors, oneshot,
DefaultAccount,
SIGNING_QUEUE_LIMIT, SigningQueue, ConfirmationPromise, ConfirmationResult, SignerService
};
@@ -188,21 +188,20 @@ impl<D: Dispatcher + 'static> ParitySigning for SigningQueueClient<D> {
meta.origin,
);
let (ready, p) = futures::oneshot();
let (ready, p) = oneshot::oneshot();
// when dispatch is complete
res.then(move |res| {
// register callback via the oneshot sender.
handle_dispatch(res, move |response| {
match response {
Ok(RpcConfirmationResponse::Decrypt(data)) => ready.complete(Ok(data)),
Err(e) => ready.complete(Err(e)),
e => ready.complete(Err(errors::internal("Unexpected result.", e))),
Ok(RpcConfirmationResponse::Decrypt(data)) => ready.send(Ok(data)),
Err(e) => ready.send(Err(e)),
e => ready.send(Err(errors::internal("Unexpected result.", e))),
}
});
// and wait for that to resolve.
p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled.")))
p
}).boxed()
}
}
@@ -217,18 +216,18 @@ impl<D: Dispatcher + 'static> EthSigning for SigningQueueClient<D> {
meta.origin,
);
let (ready, p) = futures::oneshot();
let (ready, p) = oneshot::oneshot();
res.then(move |res| {
handle_dispatch(res, move |response| {
match response {
Ok(RpcConfirmationResponse::Signature(sig)) => ready.complete(Ok(sig)),
Err(e) => ready.complete(Err(e)),
e => ready.complete(Err(errors::internal("Unexpected result.", e))),
Ok(RpcConfirmationResponse::Signature(sig)) => ready.send(Ok(sig)),
Err(e) => ready.send(Err(e)),
e => ready.send(Err(errors::internal("Unexpected result.", e))),
}
});
p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled.")))
p
}).boxed()
}
@@ -239,18 +238,18 @@ impl<D: Dispatcher + 'static> EthSigning for SigningQueueClient<D> {
meta.origin,
);
let (ready, p) = futures::oneshot();
let (ready, p) = oneshot::oneshot();
res.then(move |res| {
handle_dispatch(res, move |response| {
match response {
Ok(RpcConfirmationResponse::SendTransaction(hash)) => ready.complete(Ok(hash)),
Err(e) => ready.complete(Err(e)),
e => ready.complete(Err(errors::internal("Unexpected result.", e))),
Ok(RpcConfirmationResponse::SendTransaction(hash)) => ready.send(Ok(hash)),
Err(e) => ready.send(Err(e)),
e => ready.send(Err(errors::internal("Unexpected result.", e))),
}
});
p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled.")))
p
}).boxed()
}
@@ -261,18 +260,18 @@ impl<D: Dispatcher + 'static> EthSigning for SigningQueueClient<D> {
meta.origin,
);
let (ready, p) = futures::oneshot();
let (ready, p) = oneshot::oneshot();
res.then(move |res| {
handle_dispatch(res, move |response| {
match response {
Ok(RpcConfirmationResponse::SignTransaction(tx)) => ready.complete(Ok(tx)),
Err(e) => ready.complete(Err(e)),
e => ready.complete(Err(errors::internal("Unexpected result.", e))),
Ok(RpcConfirmationResponse::SignTransaction(tx)) => ready.send(Ok(tx)),
Err(e) => ready.send(Err(e)),
e => ready.send(Err(errors::internal("Unexpected result.", e))),
}
});
p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled.")))
p
}).boxed()
}
}

View File

@@ -35,7 +35,7 @@ impl Fetch for TestFetch {
let (tx, rx) = futures::oneshot();
thread::spawn(move || {
let cursor = io::Cursor::new(b"Some content");
tx.complete(fetch::Response::from_reader(cursor));
tx.send(fetch::Response::from_reader(cursor)).unwrap();
});
rx.map_err(|_| fetch::Error::Aborted).boxed()

View File

@@ -901,7 +901,7 @@ fn rpc_eth_send_transaction_with_bad_to() {
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid length.","data":null},"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params: expected a hex-encoded hash with 0x prefix."},"id":1}"#;
assert_eq!(tester.io.handle_request_sync(&request), Some(response.into()));
}
@@ -1084,7 +1084,7 @@ fn rpc_get_work_returns_no_work_if_cant_mine() {
eth_tester.client.set_queue_size(10);
let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32001,"message":"Still syncing.","data":null},"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32001,"message":"Still syncing."},"id":1}"#;
assert_eq!(eth_tester.io.handle_request_sync(request), Some(response.to_owned()));
}
@@ -1142,6 +1142,6 @@ fn rpc_get_work_should_timeout() {
// Request with timeout of 10 seconds. This should fail.
let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": ["10"], "id": 1}"#;
let err_response = r#"{"jsonrpc":"2.0","error":{"code":-32003,"message":"Work has not changed.","data":null},"id":1}"#;
let err_response = r#"{"jsonrpc":"2.0","error":{"code":-32003,"message":"Work has not changed."},"id":1}"#;
assert_eq!(eth_tester.io.handle_request_sync(request), Some(err_response.to_owned()));
}

View File

@@ -357,7 +357,7 @@ fn rpc_parity_unsigned_transactions_count_when_signer_disabled() {
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_unsignedTransactionsCount", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Trusted Signer is disabled. This API is not available.","data":null},"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Trusted Signer is disabled. This API is not available."},"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
@@ -393,7 +393,7 @@ fn rpc_parity_signer_port() {
// when
let request = r#"{"jsonrpc": "2.0", "method": "parity_signerPort", "params": [], "id": 1}"#;
let response1 = r#"{"jsonrpc":"2.0","result":18180,"id":1}"#;
let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Trusted Signer is disabled. This API is not available.","data":null},"id":1}"#;
let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Trusted Signer is disabled. This API is not available."},"id":1}"#;
// then
assert_eq!(io1.handle_request_sync(request), Some(response1.to_owned()));
@@ -411,7 +411,7 @@ fn rpc_parity_dapps_port() {
// when
let request = r#"{"jsonrpc": "2.0", "method": "parity_dappsPort", "params": [], "id": 1}"#;
let response1 = r#"{"jsonrpc":"2.0","result":18080,"id":1}"#;
let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Dapps Server is disabled. This API is not available.","data":null},"id":1}"#;
let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Dapps Server is disabled. This API is not available."},"id":1}"#;
// then
assert_eq!(io1.handle_request_sync(request), Some(response1.to_owned()));
@@ -429,7 +429,7 @@ fn rpc_parity_dapps_interface() {
// when
let request = r#"{"jsonrpc": "2.0", "method": "parity_dappsInterface", "params": [], "id": 1}"#;
let response1 = r#"{"jsonrpc":"2.0","result":"127.0.0.1","id":1}"#;
let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Dapps Server is disabled. This API is not available.","data":null},"id":1}"#;
let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Dapps Server is disabled. This API is not available."},"id":1}"#;
// then
assert_eq!(io1.handle_request_sync(request), Some(response1.to_owned()));

View File

@@ -230,7 +230,7 @@ fn should_be_able_to_kill_account() {
let address = accounts[0];
let request = format!(r#"{{"jsonrpc": "2.0", "method": "parity_killAccount", "params": ["0xf00baba2f00baba2f00baba2f00baba2f00baba2"], "id": 1}}"#);
let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"invalid length 1, expected a tuple of size 2","data":null},"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params: invalid length 1, expected a tuple of size 2."},"id":1}"#;
let res = tester.io.handle_request_sync(&request);
assert_eq!(res, Some(response.into()));

View File

@@ -174,7 +174,7 @@ fn rpc_trace_call_state_pruned() {
*tester.client.execution_result.write() = Some(Err(CallError::StatePruned));
let request = r#"{"jsonrpc":"2.0","method":"trace_call","params":[{}, ["stateDiff", "vmTrace", "trace"]],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive.","data":null},"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive."},"id":1}"#;
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
}
@@ -195,7 +195,7 @@ fn rpc_trace_raw_transaction_state_pruned() {
*tester.client.execution_result.write() = Some(Err(CallError::StatePruned));
let request = r#"{"jsonrpc":"2.0","method":"trace_rawTransaction","params":["0xf869018609184e72a0008276c094d46e8dd67c5d32be8058bb8eb970870f07244567849184e72a801ba0617f39c1a107b63302449c476d96a6cb17a5842fc98ff0c5bcf4d5c4d8166b95a009fdb6097c6196b9bbafc3a59f02f38d91baeef23d0c60a8e4f23c7714cea3a9", ["stateDiff", "vmTrace", "trace"]],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive.","data":null},"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive."},"id":1}"#;
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
}
@@ -216,7 +216,7 @@ fn rpc_trace_replay_transaction_state_pruned() {
*tester.client.execution_result.write() = Some(Err(CallError::StatePruned));
let request = r#"{"jsonrpc":"2.0","method":"trace_replayTransaction","params":["0x0000000000000000000000000000000000000000000000000000000000000005", ["trace", "stateDiff", "vmTrace"]],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive.","data":null},"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive."},"id":1}"#;
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
}

View File

@@ -124,13 +124,16 @@ macro_rules! impl_hash {
type Value = $name;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a 0x-prefixed, padded, hex-encoded hash of type {}", stringify!($name))
write!(formatter, "a 0x-prefixed, padded, hex-encoded hash with length {}", $size * 2)
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: serde::de::Error {
if value.len() < 2 || &value[0..2] != "0x" {
return Err(E::custom("expected a hex-encoded hash with 0x prefix"));
}
if value.len() != 2 + $size * 2 {
return Err(E::custom("Invalid length."));
return Err(E::invalid_length(value.len() - 2, &self));
}
match value[2..].from_hex() {
@@ -139,7 +142,7 @@ macro_rules! impl_hash {
result.copy_from_slice(v);
Ok($name(result))
},
_ => Err(E::custom("Invalid hex value."))
Err(e) => Err(E::custom(format!("invalid hex value: {:?}", e))),
}
}

View File

@@ -74,20 +74,20 @@ macro_rules! impl_uint {
type Value = $name;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a 0x-prefixed, hex-encoded number of type {}", stringify!($name))
write!(formatter, "a 0x-prefixed, hex-encoded number of length {}", $size*16)
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: serde::de::Error {
if value.len() < 2 || &value[0..2] != "0x" {
return Err(E::custom("expected a hex-encoded numbers with 0x prefix"))
}
// 0x + len
if value.len() > 2 + $size * 16 || value.len() < 2 {
return Err(E::custom("Invalid length."));
if value.len() > 2 + $size * 16 {
return Err(E::invalid_length(value.len() - 2, &self));
}
if &value[0..2] != "0x" {
return Err(E::custom("Use hex encoded numbers with 0x prefix."))
}
$other::from_str(&value[2..]).map($name).map_err(|_| E::custom("Invalid hex value."))
$other::from_str(&value[2..]).map($name).map_err(|e| E::custom(&format!("invalid hex value: {:?}", e)))
}
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: serde::de::Error {