Using jsonrpc-macros

This commit is contained in:
Tomasz Drwięga
2016-12-13 14:27:27 +01:00
parent 1114ad3ca7
commit f47be50843
24 changed files with 59 additions and 405 deletions

View File

@@ -15,6 +15,7 @@ serde_json = "0.8"
jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" }
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git" }
jsonrpc-ipc-server = { git = "https://github.com/ethcore/jsonrpc.git" }
jsonrpc-macros = { git = "https://github.com/ethcore/jsonrpc.git" }
ethcore-io = { path = "../util/io" }
ethcore-util = { path = "../util" }
ethcore = { path = "../ethcore" }

View File

@@ -24,6 +24,8 @@ extern crate serde;
extern crate serde_json;
extern crate jsonrpc_core;
extern crate jsonrpc_http_server;
#[macro_use]
extern crate jsonrpc_macros;
extern crate ethcore_io as io;
extern crate ethcore;

View File

@@ -1,310 +0,0 @@
// Copyright 2015, 2016 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/>.
//! Automatically serialize and deserialize parameters around a strongly-typed function.
// because we reuse the type names as idents in the macros as a dirty hack to
// work around `concat_idents!` being unstable.
#![allow(non_snake_case)]
use super::errors;
use jsonrpc_core::{Error, Params, Value, from_params, to_value};
use serde::{Serialize, Deserialize};
/// Auto-generates an RPC trait from trait definition.
///
/// This just copies out all the methods, docs, and adds another
/// function `to_delegate` which will automatically wrap each strongly-typed
/// function in a wrapper which handles parameter and output type serialization.
///
/// RPC functions may come in a couple forms: async and synchronous.
/// These are parsed with the custom `#[rpc]` attribute, which must follow
/// documentation.
///
/// ## The #[rpc] attribute
///
/// Valid forms:
/// - `#[rpc(name = "name_here")]` (a synchronous rpc function which should be bound to the given name)
/// - `#[rpc(async, name = "name_here")]` (an async rpc function which should be bound to the given name)
///
/// Synchronous function format:
/// `fn foo(&self, Param1, Param2, Param3) -> Out`.
///
/// Asynchronous RPC functions must come in this form:
/// `fn foo(&self, Param1, Param2, Param3, Ready<Out>);
///
/// Anything else will be rejected by the code generator.
macro_rules! build_rpc_trait {
// entry-point. todo: make another for traits w/ bounds.
(
$(#[$t_attr: meta])*
pub trait $name: ident {
$(
$( #[doc=$m_doc:expr] )*
#[ rpc( $($t:tt)* ) ]
fn $m_name: ident ( $($p: tt)* ) $( -> Result<$out: ty, Error> )* ;
)*
}
) => {
$(#[$t_attr])*
pub trait $name: Sized + Send + Sync + 'static {
$(
$(#[doc=$m_doc])*
fn $m_name ( $($p)* ) $( -> Result<$out, Error> )* ;
)*
/// Transform this into an `IoDelegate`, automatically wrapping
/// the parameters.
fn to_delegate(self) -> ::jsonrpc_core::IoDelegate<Self> {
let mut del = ::jsonrpc_core::IoDelegate::new(self.into());
$(
build_rpc_trait!(WRAP del =>
( $($t)* )
fn $m_name ( $($p)* ) $( -> Result<$out, Error> )*
);
)*
del
}
}
};
( WRAP $del: expr =>
(name = $name: expr)
fn $method: ident (&self $(, $param: ty)*) -> Result<$out: ty, Error>
) => {
$del.add_method($name, move |base, params| {
(Self::$method as fn(&_ $(, $param)*) -> Result<$out, Error>).wrap_rpc(base, params)
})
};
( WRAP $del: expr =>
(async, name = $name: expr)
fn $method: ident (&self, Ready<$out: ty> $(, $param: ty)*)
) => {
$del.add_async_method($name, move |base, params, ready| {
(Self::$method as fn(&_, Ready<$out> $(, $param)*)).wrap_rpc(base, params, ready)
})
};
}
/// A wrapper type without an implementation of `Deserialize`
/// which allows a special implementation of `Wrap` for functions
/// that take a trailing default parameter.
pub struct Trailing<T: Default + Deserialize>(pub T);
/// A wrapper type for `jsonrpc_core`'s weakly-typed `Ready` struct.
pub struct Ready<T: Serialize> {
inner: ::jsonrpc_core::Ready,
_marker: ::std::marker::PhantomData<T>,
}
impl<T: Serialize> From<::jsonrpc_core::Ready> for Ready<T> {
fn from(ready: ::jsonrpc_core::Ready) -> Self {
Ready { inner: ready, _marker: ::std::marker::PhantomData }
}
}
impl<T: Serialize> Ready<T> {
/// Respond withthe asynchronous result.
pub fn ready(self, result: Result<T, Error>) {
self.inner.ready(result.map(to_value))
}
}
/// Wrapper trait for synchronous RPC functions.
pub trait Wrap<B: Send + Sync + 'static> {
fn wrap_rpc(&self, base: &B, params: Params) -> Result<Value, Error>;
}
/// Wrapper trait for asynchronous RPC functions.
pub trait WrapAsync<B: Send + Sync + 'static> {
fn wrap_rpc(&self, base: &B, params: Params, ready: ::jsonrpc_core::Ready);
}
// special impl for no parameters.
impl<B, OUT> Wrap<B> for fn(&B) -> Result<OUT, Error>
where B: Send + Sync + 'static, OUT: Serialize
{
fn wrap_rpc(&self, base: &B, params: Params) -> Result<Value, Error> {
::v1::helpers::params::expect_no_params(params)
.and_then(|()| (self)(base))
.map(to_value)
}
}
impl<B, OUT> WrapAsync<B> for fn(&B, Ready<OUT>)
where B: Send + Sync + 'static, OUT: Serialize
{
fn wrap_rpc(&self, base: &B, params: Params, ready: ::jsonrpc_core::Ready) {
match ::v1::helpers::params::expect_no_params(params) {
Ok(()) => (self)(base, ready.into()),
Err(e) => ready.ready(Err(e)),
}
}
}
// creates a wrapper implementation which deserializes the parameters,
// calls the function with concrete type, and serializes the output.
macro_rules! wrap {
($($x: ident),+) => {
// synchronous implementation
impl <
BASE: Send + Sync + 'static,
OUT: Serialize,
$($x: Deserialize,)+
> Wrap<BASE> for fn(&BASE, $($x,)+) -> Result<OUT, Error> {
fn wrap_rpc(&self, base: &BASE, params: Params) -> Result<Value, Error> {
from_params::<($($x,)+)>(params).and_then(|($($x,)+)| {
(self)(base, $($x,)+)
}).map(to_value)
}
}
// asynchronous implementation
impl <
BASE: Send + Sync + 'static,
OUT: Serialize,
$($x: Deserialize,)+
> WrapAsync<BASE> for fn(&BASE, Ready<OUT>, $($x,)+ ) {
fn wrap_rpc(&self, base: &BASE, params: Params, ready: ::jsonrpc_core::Ready) {
match from_params::<($($x,)+)>(params) {
Ok(($($x,)+)) => (self)(base, ready.into(), $($x,)+),
Err(e) => ready.ready(Err(e)),
}
}
}
}
}
// special impl for no parameters other than block parameter.
impl<B, OUT, T> Wrap<B> for fn(&B, Trailing<T>) -> Result<OUT, Error>
where B: Send + Sync + 'static, OUT: Serialize, T: Default + Deserialize
{
fn wrap_rpc(&self, base: &B, params: Params) -> Result<Value, Error> {
let len = match params {
Params::Array(ref v) => v.len(),
Params::None => 0,
_ => return Err(errors::invalid_params("not an array", "")),
};
let (id,) = match len {
0 => (T::default(),),
1 => try!(from_params::<(T,)>(params)),
_ => return Err(Error::invalid_params()),
};
(self)(base, Trailing(id)).map(to_value)
}
}
impl<B, OUT, T> WrapAsync<B> for fn(&B, Ready<OUT>, Trailing<T>)
where B: Send + Sync + 'static, OUT: Serialize, T: Default + Deserialize
{
fn wrap_rpc(&self, base: &B, params: Params, ready: ::jsonrpc_core::Ready) {
let len = match params {
Params::Array(ref v) => v.len(),
Params::None => 0,
_ => return ready.ready(Err(errors::invalid_params("not an array", ""))),
};
let id = match len {
0 => Ok((T::default(),)),
1 => from_params::<(T,)>(params),
_ => Err(Error::invalid_params()),
};
match id {
Ok((id,)) => (self)(base, ready.into(), Trailing(id)),
Err(e) => ready.ready(Err(e)),
}
}
}
// similar to `wrap!`, but handles a single default trailing parameter
// accepts an additional argument indicating the number of non-trailing parameters.
macro_rules! wrap_with_trailing {
($num: expr, $($x: ident),+) => {
// synchronous implementation
impl <
BASE: Send + Sync + 'static,
OUT: Serialize,
$($x: Deserialize,)+
TRAILING: Default + Deserialize,
> Wrap<BASE> for fn(&BASE, $($x,)+ Trailing<TRAILING>) -> Result<OUT, Error> {
fn wrap_rpc(&self, base: &BASE, params: Params) -> Result<Value, Error> {
let len = match params {
Params::Array(ref v) => v.len(),
Params::None => 0,
_ => return Err(errors::invalid_params("not an array", "")),
};
let params = match len - $num {
0 => from_params::<($($x,)+)>(params)
.map(|($($x,)+)| ($($x,)+ TRAILING::default())),
1 => from_params::<($($x,)+ TRAILING)>(params)
.map(|($($x,)+ id)| ($($x,)+ id)),
_ => Err(Error::invalid_params()),
};
let ($($x,)+ id) = try!(params);
(self)(base, $($x,)+ Trailing(id)).map(to_value)
}
}
// asynchronous implementation
impl <
BASE: Send + Sync + 'static,
OUT: Serialize,
$($x: Deserialize,)+
TRAILING: Default + Deserialize,
> WrapAsync<BASE> for fn(&BASE, Ready<OUT>, $($x,)+ Trailing<TRAILING>) {
fn wrap_rpc(&self, base: &BASE, params: Params, ready: ::jsonrpc_core::Ready) {
let len = match params {
Params::Array(ref v) => v.len(),
Params::None => 0,
_ => return ready.ready(Err(errors::invalid_params("not an array", ""))),
};
let params = match len - $num {
0 => from_params::<($($x,)+)>(params)
.map(|($($x,)+)| ($($x,)+ TRAILING::default())),
1 => from_params::<($($x,)+ TRAILING)>(params)
.map(|($($x,)+ id)| ($($x,)+ id)),
_ => Err(Error::invalid_params()),
};
match params {
Ok(($($x,)+ id)) => (self)(base, ready.into(), $($x,)+ Trailing(id)),
Err(e) => ready.ready(Err(e))
}
}
}
}
}
wrap!(A, B, C, D, E);
wrap!(A, B, C, D);
wrap!(A, B, C);
wrap!(A, B);
wrap!(A);
wrap_with_trailing!(5, A, B, C, D, E);
wrap_with_trailing!(4, A, B, C, D);
wrap_with_trailing!(3, A, B, C);
wrap_with_trailing!(2, A, B);
wrap_with_trailing!(1, A);

View File

@@ -14,14 +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 auto_args;
#[macro_use]
pub mod errors;
pub mod dispatch;
pub mod params;
pub mod block_import;
mod poll_manager;

View File

@@ -1,46 +0,0 @@
// Copyright 2015, 2016 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/>.
//! 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)),
}
}
/// Returns number of different parameters in given `Params` object.
pub fn params_len(params: &Params) -> usize {
match params {
&Params::Array(ref vec) => vec.len(),
_ => 0,
}
}
/// 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

@@ -27,6 +27,7 @@ use time::get_time;
use ethsync::{SyncProvider};
use ethcore::miner::{MinerService, ExternalMinerService};
use jsonrpc_core::*;
use jsonrpc_macros::Trailing;
use util::{H256, Address, FixedHash, U256, H64, Uint};
use util::sha3::*;
use util::{FromHex, Mutex};
@@ -51,7 +52,6 @@ use v1::types::{
use v1::helpers::{CallRequest as CRequest, errors, limit_logs};
use v1::helpers::dispatch::{dispatch_transaction, default_gas_price};
use v1::helpers::block_import::is_major_importing;
use v1::helpers::auto_args::Trailing;
const EXTRA_INFO_PROOF: &'static str = "Object exists in in blockchain (fetched earlier), extra_info is always available if object exists; qed";

View File

@@ -32,6 +32,7 @@ use ethcore::mode::Mode;
use ethcore::account_provider::AccountProvider;
use jsonrpc_core::Error;
use jsonrpc_macros::Trailing;
use v1::traits::Parity;
use v1::types::{
Bytes, U256, H160, H256, H512,
@@ -41,7 +42,6 @@ use v1::types::{
};
use v1::helpers::{errors, SigningQueue, SignerService, NetworkSettings};
use v1::helpers::dispatch::DEFAULT_MAC;
use v1::helpers::auto_args::Trailing;
/// Parity implementation.
pub struct ParityClient<C, M, S: ?Sized> where

View File

@@ -26,7 +26,7 @@ use fetch::{Client as FetchClient, Fetch};
use util::{Mutex, sha3};
use jsonrpc_core::Error;
use v1::helpers::auto_args::Ready;
use jsonrpc_macros::Ready;
use v1::helpers::errors;
use v1::traits::ParitySet;
use v1::types::{Bytes, H160, H256, U256};

View File

@@ -25,7 +25,7 @@ use ethcore::miner::MinerService;
use ethcore::client::MiningBlockChainClient;
use jsonrpc_core::Error;
use v1::helpers::auto_args::Ready;
use jsonrpc_macros::Ready;
use v1::helpers::{
errors, dispatch,
SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationPayload, SignerService

View File

@@ -24,7 +24,7 @@ use ethcore::miner::MinerService;
use ethcore::client::MiningBlockChainClient;
use jsonrpc_core::Error;
use v1::helpers::auto_args::Ready;
use jsonrpc_macros::Ready;
use v1::helpers::errors;
use v1::helpers::dispatch;
use v1::traits::{EthSigning, ParitySigning};

View File

@@ -18,13 +18,15 @@
use std::sync::{Weak, Arc};
use jsonrpc_core::*;
use serde;
use rlp::{UntrustedRlp, View};
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::{errors, CallRequest as CRequest};
use v1::helpers::params::from_params_default_third;
use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, H256};
fn to_call_analytics(flags: Vec<String>) -> CallAnalytics {
@@ -35,6 +37,22 @@ fn to_call_analytics(flags: Vec<String>) -> CallAnalytics {
}
}
/// Returns number of different parameters in given `Params` object.
fn params_len(params: &Params) -> usize {
match params {
&Params::Array(ref vec) => vec.len(),
_ => 0,
}
}
/// Deserialize request parameters with optional third parameter `BlockNumber` defaulting to `BlockNumber::Latest`.
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)
}
}
/// Traces api implementation.
pub struct TracesClient<C, M> where C: BlockChainClient, M: MinerService {
client: Weak<C>,

View File

@@ -21,7 +21,7 @@ use v1::types::{RichBlock, BlockNumber, Bytes, CallRequest, Filter, FilterChange
use v1::types::{Log, Receipt, SyncStatus, Transaction, Work};
use v1::types::{H64, H160, H256, U256};
use v1::helpers::auto_args::{Trailing, Wrap};
use jsonrpc_macros::Trailing;
build_rpc_trait! {
/// Eth rpc interface.

View File

@@ -16,7 +16,8 @@
//! Eth rpc interface.
use v1::helpers::auto_args::{WrapAsync, Ready};
use jsonrpc_macros::Ready;
use v1::types::{Bytes, H160, H256, H520, TransactionRequest, RichRawTransaction};
build_rpc_trait! {

View File

@@ -17,8 +17,6 @@
//! Net rpc interface.
use jsonrpc_core::Error;
use v1::helpers::auto_args::Wrap;
build_rpc_trait! {
/// Net rpc interface.
pub trait Net {
@@ -35,4 +33,4 @@ build_rpc_trait! {
#[rpc(name = "net_listening")]
fn is_listening(&self) -> Result<bool, Error>;
}
}
}

View File

@@ -15,10 +15,12 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Parity-specific rpc interface.
use jsonrpc_core::Error;
use std::collections::BTreeMap;
use v1::helpers::auto_args::{Wrap, Trailing};
use jsonrpc_core::Error;
use jsonrpc_macros::Trailing;
use v1::types::{
H160, H256, H512, U256, Bytes,
Peers, Transaction, RpcSettings, Histogram,

View File

@@ -16,9 +16,8 @@
//! Parity Accounts-related rpc interface.
use std::collections::BTreeMap;
use jsonrpc_core::{Value, Error};
use v1::helpers::auto_args::Wrap;
use jsonrpc_core::{Value, Error};
use v1::types::{H160, H256, DappId};
build_rpc_trait! {

View File

@@ -17,8 +17,8 @@
//! Parity-specific rpc interface for operations altering the settings.
use jsonrpc_core::Error;
use jsonrpc_macros::Ready;
use v1::helpers::auto_args::{Wrap, WrapAsync, Ready};
use v1::types::{Bytes, H160, H256, U256};
build_rpc_trait! {

View File

@@ -16,8 +16,8 @@
//! ParitySigning rpc interface.
use jsonrpc_core::Error;
use jsonrpc_macros::Ready;
use v1::helpers::auto_args::{Wrap, WrapAsync, Ready};
use v1::types::{U256, H160, H256, Bytes, ConfirmationResponse, TransactionRequest, Either};
build_rpc_trait! {

View File

@@ -17,7 +17,6 @@
//! Personal rpc interface.
use jsonrpc_core::Error;
use v1::helpers::auto_args::Wrap;
use v1::types::{U128, H160, H256, TransactionRequest};
build_rpc_trait! {

View File

@@ -16,12 +16,10 @@
//! RPC interface.
use jsonrpc_core::Error;
use v1::helpers::auto_args::Wrap;
use std::collections::BTreeMap;
use jsonrpc_core::Error;
build_rpc_trait! {
/// RPC Interface.
pub trait Rpc {
@@ -33,4 +31,4 @@ build_rpc_trait! {
#[rpc(name = "rpc_modules")]
fn rpc_modules(&self) -> Result<BTreeMap<String, String>, Error>;
}
}
}

View File

@@ -17,10 +17,8 @@
//! Parity Signer-related rpc interface.
use jsonrpc_core::Error;
use v1::helpers::auto_args::Wrap;
use v1::types::{U256, Bytes, TransactionModification, ConfirmationRequest, ConfirmationResponse};
build_rpc_trait! {
/// Signer extension for confirmations rpc interface.
pub trait Signer {

View File

@@ -15,8 +15,9 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Traces specific rpc interface.
use std::sync::Arc;
use jsonrpc_core::*;
use jsonrpc_core::{Params, Value, Error, IoDelegate};
/// Traces specific rpc interface.
pub trait Traces: Sized + Send + Sync + 'static {

View File

@@ -17,10 +17,8 @@
//! Web3 rpc interface.
use jsonrpc_core::Error;
use v1::helpers::auto_args::Wrap;
use v1::types::{H256, Bytes};
build_rpc_trait! {
/// Web3 rpc interface.
pub trait Web3 {