diff --git a/rpc/src/v1/helpers/auto_args.rs b/rpc/src/v1/helpers/auto_args.rs new file mode 100644 index 000000000..c7deb0436 --- /dev/null +++ b/rpc/src/v1/helpers/auto_args.rs @@ -0,0 +1,171 @@ +// 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 . + +//! 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. +/// +/// Every function must have a `#[name("rpc_nameHere")]` attribute after +/// its documentation, and no other attributes. All function names are +/// allowed except for `to_delegate`, which is auto-generated. +macro_rules! build_rpc_trait { + ( + $(#[$t_attr: meta])* + pub trait $name: ident { + $( + $(#[doc=$m_doc: expr])* #[name($rpc_name: expr)] + fn $method: ident (&self $(, $param: ty)*) -> $out: ty; + )* + } + ) => { + $(#[$t_attr])* + pub trait $name: Sized + Send + Sync + 'static { + $( + $(#[doc=$m_doc])* + fn $method(&self $(, $param)*) -> $out; + )* + + /// Transform this into an `IoDelegate`, automatically wrapping + /// the parameters. + fn to_delegate(self) -> ::jsonrpc_core::IoDelegate { + let mut del = ::jsonrpc_core::IoDelegate::new(self.into()); + $( + del.add_method($rpc_name, move |base, params| { + ($name::$method as fn(&_ $(, $param)*) -> $out).wrap_rpc(base, params) + }); + )* + del + } + } + } +} + +/// 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(pub T); + +/// Wrapper trait for synchronous RPC functions. +pub trait Wrap { + fn wrap_rpc(&self, base: &B, params: Params) -> Result; +} + +// special impl for no parameters. +impl Wrap for fn(&B) -> Result + where B: Send + Sync + 'static, OUT: Serialize +{ + fn wrap_rpc(&self, base: &B, params: Params) -> Result { + ::v1::helpers::params::expect_no_params(params) + .and_then(|()| (self)(base)) + .map(to_value) + } +} + +// creates a wrapper implementation which deserializes the parameters, +// calls the function with concrete type, and serializes the output. +macro_rules! wrap { + ($($x: ident),+) => { + impl < + BASE: Send + Sync + 'static, + OUT: Serialize, + $($x: Deserialize,)+ + > Wrap for fn(&BASE, $($x,)+) -> Result { + fn wrap_rpc(&self, base: &BASE, params: Params) -> Result { + from_params::<($($x,)+)>(params).and_then(|($($x,)+)| { + (self)(base, $($x,)+) + }).map(to_value) + } + } + } +} + +// special impl for no parameters other than block parameter. +impl Wrap for fn(&B, Trailing) -> Result + where B: Send + Sync + 'static, OUT: Serialize, T: Default + Deserialize +{ + fn wrap_rpc(&self, base: &B, params: Params) -> Result { + 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) + } +} + +// 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),+) => { + impl < + BASE: Send + Sync + 'static, + OUT: Serialize, + $($x: Deserialize,)+ + TRAILING: Default + Deserialize, + > Wrap for fn(&BASE, $($x,)+ Trailing) -> Result { + fn wrap_rpc(&self, base: &BASE, params: Params) -> Result { + 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) + } + } + } +} + +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); \ No newline at end of file diff --git a/rpc/src/v1/helpers/dispatch.rs b/rpc/src/v1/helpers/dispatch.rs index 5587673d8..df2d8cbd3 100644 --- a/rpc/src/v1/helpers/dispatch.rs +++ b/rpc/src/v1/helpers/dispatch.rs @@ -41,7 +41,7 @@ fn prepare_transaction(client: &C, miner: &M, request: TransactionRequest) } } -pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result +pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result where C: MiningBlockChainClient, M: MinerService { let hash = RpcH256::from(signed_transaction.hash()); @@ -49,7 +49,7 @@ pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: Sig import .map_err(errors::from_transaction_error) - .map(|_| to_value(&hash)) + .map(|_| hash) } pub fn signature_with_password(accounts: &AccountProvider, address: Address, hash: H256, pass: String) -> Result { @@ -70,7 +70,7 @@ pub fn unlock_sign_and_dispatch(client: &C, miner: &M, request: Transactio }; trace!(target: "miner", "send_transaction: dispatching tx: {}", ::rlp::encode(&signed_transaction).to_vec().pretty()); - dispatch_transaction(&*client, &*miner, signed_transaction) + dispatch_transaction(&*client, &*miner, signed_transaction).map(to_value) } pub fn sign_and_dispatch(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, address: Address) -> Result @@ -84,7 +84,7 @@ pub fn sign_and_dispatch(client: &C, miner: &M, request: TransactionReques }; trace!(target: "miner", "send_transaction: dispatching tx: {}", ::rlp::encode(&signed_transaction).to_vec().pretty()); - dispatch_transaction(&*client, &*miner, signed_transaction) + dispatch_transaction(&*client, &*miner, signed_transaction).map(to_value) } pub fn default_gas_price(client: &C, miner: &M) -> U256 where C: MiningBlockChainClient, M: MinerService { diff --git a/rpc/src/v1/helpers/mod.rs b/rpc/src/v1/helpers/mod.rs index 7d4e19749..e6ada3379 100644 --- a/rpc/src/v1/helpers/mod.rs +++ b/rpc/src/v1/helpers/mod.rs @@ -14,10 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +#[macro_use] +pub mod auto_args; + #[macro_use] pub mod errors; + pub mod dispatch; pub mod params; + mod poll_manager; mod poll_filter; mod requests; diff --git a/rpc/src/v1/helpers/params.rs b/rpc/src/v1/helpers/params.rs index 4a6abf542..f56c500fc 100644 --- a/rpc/src/v1/helpers/params.rs +++ b/rpc/src/v1/helpers/params.rs @@ -36,14 +36,6 @@ pub fn params_len(params: &Params) -> usize { } } -/// Deserialize request parameters with optional second parameter `BlockNumber` defaulting to `BlockNumber::Latest`. -pub fn from_params_default_second(params: Params) -> Result<(F, BlockNumber, ), Error> where F: serde::de::Deserialize { - match params_len(¶ms) { - 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(params: Params) -> Result<(F1, F2, BlockNumber, ), Error> where F1: serde::de::Deserialize, F2: serde::de::Deserialize { match params_len(¶ms) { diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index cffbe87e5..755539ebd 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -42,10 +42,14 @@ use ethcore::log_entry::LogEntry; 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::types::{ + Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, + Transaction, CallRequest, Index, Filter, Log, Receipt, Work, + H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256, +}; use v1::helpers::{CallRequest as CRequest, errors, limit_logs}; use v1::helpers::dispatch::{default_gas_price, dispatch_transaction}; -use v1::helpers::params::{expect_no_params, from_params_default_second, from_params_default_third}; +use v1::helpers::auto_args::Trailing; /// Eth RPC options pub struct EthClientOptions { @@ -100,7 +104,7 @@ impl EthClient where } } - fn block(&self, id: BlockID, include_txs: bool) -> Result { + fn block(&self, id: BlockID, include_txs: bool) -> Result, Error> { let client = take_weak!(self.client); match (client.block(id.clone()), client.block_total_difficulty(id)) { (Some(bytes), Some(total_difficulty)) => { @@ -131,28 +135,28 @@ impl EthClient where }, extra_data: Bytes::new(view.extra_data()) }; - Ok(to_value(&block)) + Ok(Some(block)) }, - _ => Ok(Value::Null) + _ => Ok(None) } } - fn transaction(&self, id: TransactionID) -> Result { + fn transaction(&self, id: TransactionID) -> Result, Error> { match take_weak!(self.client).transaction(id) { - Some(t) => Ok(to_value(&Transaction::from(t))), - None => Ok(Value::Null) + Some(t) => Ok(Some(Transaction::from(t))), + None => Ok(None), } } - fn uncle(&self, id: UncleID) -> Result { + fn uncle(&self, id: UncleID) -> Result, Error> { let client = take_weak!(self.client); let uncle: BlockHeader = match client.uncle(id) { Some(rlp) => rlp::decode(&rlp), - None => { return Ok(Value::Null); } + None => { return Ok(None); } }; let parent_difficulty = match client.block_total_difficulty(BlockID::Hash(uncle.parent_hash().clone())) { Some(difficulty) => difficulty, - None => { return Ok(Value::Null); } + None => { return Ok(None); } }; let block = Block { @@ -177,7 +181,7 @@ impl EthClient where uncles: vec![], transactions: BlockTransactions::Hashes(vec![]), }; - Ok(to_value(&block)) + Ok(Some(block)) } fn sign_call(&self, request: CRequest) -> Result { @@ -240,20 +244,19 @@ impl Eth for EthClient where M: MinerService + 'static, EM: ExternalMinerService + 'static { - fn protocol_version(&self, params: Params) -> Result { + fn protocol_version(&self) -> Result { try!(self.active()); - try!(expect_no_params(params)); - Ok(Value::String(format!("{}", take_weak!(self.sync).status().protocol_version).to_owned())) + let version = take_weak!(self.sync).status().protocol_version.to_owned(); + Ok(format!("{}", version)) } - fn syncing(&self, params: Params) -> Result { + fn syncing(&self) -> Result { try!(self.active()); - try!(expect_no_params(params)); let status = take_weak!(self.sync).status(); - let res = match status.state { - SyncState::Idle => SyncStatus::None, + match status.state { + SyncState::Idle => Ok(SyncStatus::None), SyncState::Waiting | SyncState::Blocks | SyncState::NewBlocks | SyncState::ChainHead | SyncState::SnapshotManifest | SyncState::SnapshotData | SyncState::SnapshotWaiting => { let current_block = U256::from(take_weak!(self.client).chain_info().best_block_number); @@ -265,260 +268,242 @@ impl Eth for EthClient where current_block: current_block.into(), highest_block: highest_block.into(), }; - SyncStatus::Info(info) + Ok(SyncStatus::Info(info)) } else { - SyncStatus::None + Ok(SyncStatus::None) } } - }; - Ok(to_value(&res)) + } } - fn author(&self, params: Params) -> Result { + fn author(&self) -> Result { try!(self.active()); - try!(expect_no_params(params)); - Ok(to_value(&RpcH160::from(take_weak!(self.miner).author()))) + Ok(RpcH160::from(take_weak!(self.miner).author())) } - fn is_mining(&self, params: Params) -> Result { + fn is_mining(&self) -> Result { try!(self.active()); - try!(expect_no_params(params)); - Ok(to_value(&(take_weak!(self.miner).is_sealing()))) + Ok(take_weak!(self.miner).is_sealing()) } - fn hashrate(&self, params: Params) -> Result { + fn hashrate(&self) -> Result { try!(self.active()); - try!(expect_no_params(params)); - Ok(to_value(&RpcU256::from(self.external_miner.hashrate()))) + Ok(RpcU256::from(self.external_miner.hashrate())) } - fn gas_price(&self, params: Params) -> Result { + fn gas_price(&self) -> Result { try!(self.active()); - try!(expect_no_params(params)); let (client, miner) = (take_weak!(self.client), take_weak!(self.miner)); - Ok(to_value(&RpcU256::from(default_gas_price(&*client, &*miner)))) + Ok(RpcU256::from(default_gas_price(&*client, &*miner))) } - fn accounts(&self, params: Params) -> Result { + fn accounts(&self) -> Result, Error> { try!(self.active()); - 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))); - Ok(to_value(&accounts.into_iter().map(Into::into).collect::>())) + Ok(accounts.into_iter().map(Into::into).collect()) } - fn block_number(&self, params: Params) -> Result { + fn block_number(&self) -> Result { try!(self.active()); - try!(expect_no_params(params)); - Ok(to_value(&RpcU256::from(take_weak!(self.client).chain_info().best_block_number))) + Ok(RpcU256::from(take_weak!(self.client).chain_info().best_block_number)) } - fn balance(&self, params: Params) -> Result { + fn balance(&self, address: RpcH160, num: Trailing) -> Result { try!(self.active()); - from_params_default_second(params) - .and_then(|(address, block_number,)| { - let address: Address = RpcH160::into(address); - match block_number { - BlockNumber::Pending => Ok(to_value(&RpcU256::from(take_weak!(self.miner).balance(&*take_weak!(self.client), &address)))), - id => match take_weak!(self.client).balance(&address, id.into()) { - Some(balance) => Ok(to_value(&RpcU256::from(balance))), - None => Err(errors::state_pruned()), - } - } - }) + + let address = address.into(); + match num.0 { + BlockNumber::Pending => Ok(take_weak!(self.miner).balance(&*take_weak!(self.client), &address).into()), + id => match take_weak!(self.client).balance(&address, id.into()) { + Some(balance) => Ok(balance.into()), + None => Err(errors::state_pruned()), + } + } } - fn storage_at(&self, params: Params) -> Result { + fn storage_at(&self, address: RpcH160, pos: RpcU256, num: Trailing) -> Result { try!(self.active()); - from_params_default_third::(params) - .and_then(|(address, position, block_number,)| { - let address: Address = RpcH160::into(address); - let position: U256 = RpcU256::into(position); - match block_number { - BlockNumber::Pending => Ok(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) => Ok(to_value(&RpcH256::from(s))), - None => Err(errors::state_pruned()), - } - } - }) - + let address: Address = RpcH160::into(address); + let position: U256 = RpcU256::into(pos); + match num.0 { + BlockNumber::Pending => Ok(take_weak!(self.miner).storage_at(&*take_weak!(self.client), &address, &H256::from(position)).into()), + id => match take_weak!(self.client).storage_at(&address, &H256::from(position), id.into()) { + Some(s) => Ok(s.into()), + None => Err(errors::state_pruned()), + } + } } - fn transaction_count(&self, params: Params) -> Result { + fn transaction_count(&self, address: RpcH160, num: Trailing) -> Result { try!(self.active()); - from_params_default_second(params) - .and_then(|(address, block_number,)| { - let address: Address = RpcH160::into(address); - match block_number { - BlockNumber::Pending => Ok(to_value(&RpcU256::from(take_weak!(self.miner).nonce(&*take_weak!(self.client), &address)))), - id => match take_weak!(self.client).nonce(&address, id.into()) { - Some(nonce) => Ok(to_value(&RpcU256::from(nonce))), - None => Err(errors::state_pruned()), - } - } - }) + + let address: Address = RpcH160::into(address); + match num.0 { + BlockNumber::Pending => Ok(take_weak!(self.miner).nonce(&*take_weak!(self.client), &address).into()), + id => match take_weak!(self.client).nonce(&address, id.into()) { + Some(nonce) => Ok(nonce.into()), + None => Err(errors::state_pruned()), + } + } } - fn block_transaction_count_by_hash(&self, params: Params) -> Result { + fn block_transaction_count_by_hash(&self, hash: RpcH256) -> Result, Error> { try!(self.active()); - from_params::<(RpcH256,)>(params) - .and_then(|(hash,)| // match - take_weak!(self.client).block(BlockID::Hash(hash.into())) - .map_or(Ok(Value::Null), |bytes| Ok(to_value(&RpcU256::from(BlockView::new(&bytes).transactions_count()))))) + Ok( + take_weak!(self.client).block(BlockID::Hash(hash.into())) + .map(|bytes| BlockView::new(&bytes).transactions_count().into()) + ) } - fn block_transaction_count_by_number(&self, params: Params) -> Result { + fn block_transaction_count_by_number(&self, num: BlockNumber) -> Result, Error> { try!(self.active()); - from_params::<(BlockNumber,)>(params) - .and_then(|(block_number,)| match block_number { - BlockNumber::Pending => Ok(to_value( - &RpcU256::from(take_weak!(self.miner).status().transactions_in_pending_block) - )), - _ => take_weak!(self.client).block(block_number.into()) - .map_or(Ok(Value::Null), |bytes| Ok(to_value(&RpcU256::from(BlockView::new(&bytes).transactions_count())))) - }) + + match num { + BlockNumber::Pending => Ok(Some( + take_weak!(self.miner).status().transactions_in_pending_block.into() + )), + _ => Ok( + take_weak!(self.client).block(num.into()) + .map(|bytes| BlockView::new(&bytes).transactions_count().into()) + ) + } } - fn block_uncles_count_by_hash(&self, params: Params) -> Result { + fn block_uncles_count_by_hash(&self, hash: RpcH256) -> Result, Error> { try!(self.active()); - from_params::<(RpcH256,)>(params) - .and_then(|(hash,)| - take_weak!(self.client).block(BlockID::Hash(hash.into())) - .map_or(Ok(Value::Null), |bytes| Ok(to_value(&RpcU256::from(BlockView::new(&bytes).uncles_count()))))) + + Ok( + take_weak!(self.client).block(BlockID::Hash(hash.into())) + .map(|bytes| BlockView::new(&bytes).uncles_count().into()) + ) } - fn block_uncles_count_by_number(&self, params: Params) -> Result { + fn block_uncles_count_by_number(&self, num: BlockNumber) -> Result, Error> { try!(self.active()); - from_params::<(BlockNumber,)>(params) - .and_then(|(block_number,)| match block_number { - BlockNumber::Pending => Ok(to_value(&RpcU256::from(0))), - _ => take_weak!(self.client).block(block_number.into()) - .map_or(Ok(Value::Null), |bytes| Ok(to_value(&RpcU256::from(BlockView::new(&bytes).uncles_count())))) - }) + + match num { + BlockNumber::Pending => Ok(Some(0.into())), + _ => Ok( + take_weak!(self.client).block(num.into()) + .map(|bytes| BlockView::new(&bytes).uncles_count().into()) + ), + } } - fn code_at(&self, params: Params) -> Result { + fn code_at(&self, address: RpcH160, num: Trailing) -> Result { try!(self.active()); - from_params_default_second(params) - .and_then(|(address, block_number,)| { - let address: Address = RpcH160::into(address); - match block_number { - BlockNumber::Pending => Ok(to_value(&take_weak!(self.miner).code(&*take_weak!(self.client), &address).map_or_else(Bytes::default, Bytes::new))), - _ => match take_weak!(self.client).code(&address, block_number.into()) { - Some(code) => Ok(to_value(&code.map_or_else(Bytes::default, Bytes::new))), - None => Err(errors::state_pruned()), - }, - } - }) + + let address: Address = RpcH160::into(address); + match num.0 { + BlockNumber::Pending => Ok(take_weak!(self.miner).code(&*take_weak!(self.client), &address).map_or_else(Bytes::default, Bytes::new)), + _ => match take_weak!(self.client).code(&address, num.0.into()) { + Some(code) => Ok(code.map_or_else(Bytes::default, Bytes::new)), + None => Err(errors::state_pruned()), + }, + } } - fn block_by_hash(&self, params: Params) -> Result { + fn block_by_hash(&self, hash: RpcH256, include_txs: bool) -> Result, Error> { try!(self.active()); - from_params::<(RpcH256, bool)>(params) - .and_then(|(hash, include_txs)| self.block(BlockID::Hash(hash.into()), include_txs)) + + self.block(BlockID::Hash(hash.into()), include_txs) } - fn block_by_number(&self, params: Params) -> Result { + fn block_by_number(&self, num: BlockNumber, include_txs: bool) -> Result, Error> { try!(self.active()); - from_params::<(BlockNumber, bool)>(params) - .and_then(|(number, include_txs)| self.block(number.into(), include_txs)) + + self.block(num.into(), include_txs) } - fn transaction_by_hash(&self, params: Params) -> Result { + fn transaction_by_hash(&self, hash: RpcH256) -> Result, Error> { try!(self.active()); - from_params::<(RpcH256,)>(params) - .and_then(|(hash,)| { - let miner = take_weak!(self.miner); - let hash: H256 = hash.into(); - match miner.transaction(&hash) { - Some(pending_tx) => Ok(to_value(&Transaction::from(pending_tx))), - None => self.transaction(TransactionID::Hash(hash)) - } - }) + + let miner = take_weak!(self.miner); + let hash: H256 = hash.into(); + match miner.transaction(&hash) { + Some(pending_tx) => Ok(Some(pending_tx.into())), + None => self.transaction(TransactionID::Hash(hash)) + } } - fn transaction_by_block_hash_and_index(&self, params: Params) -> Result { + fn transaction_by_block_hash_and_index(&self, hash: RpcH256, index: Index) -> Result, Error> { try!(self.active()); - from_params::<(RpcH256, Index)>(params) - .and_then(|(hash, index)| self.transaction(TransactionID::Location(BlockID::Hash(hash.into()), index.value()))) + + self.transaction(TransactionID::Location(BlockID::Hash(hash.into()), index.value())) } - fn transaction_by_block_number_and_index(&self, params: Params) -> Result { + fn transaction_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> Result, Error> { try!(self.active()); - from_params::<(BlockNumber, Index)>(params) - .and_then(|(number, index)| self.transaction(TransactionID::Location(number.into(), index.value()))) + + self.transaction(TransactionID::Location(num.into(), index.value())) } - fn transaction_receipt(&self, params: Params) -> Result { + fn transaction_receipt(&self, hash: RpcH256) -> Result, Error> { try!(self.active()); - from_params::<(RpcH256,)>(params) - .and_then(|(hash,)| { - let miner = take_weak!(self.miner); - let hash: H256 = hash.into(); - match (miner.pending_receipt(&hash), self.options.allow_pending_receipt_query) { - (Some(receipt), true) => Ok(to_value(&Receipt::from(receipt))), - _ => { - let client = take_weak!(self.client); - let receipt = client.transaction_receipt(TransactionID::Hash(hash)); - Ok(to_value(&receipt.map(Receipt::from))) - } - } - }) + + let miner = take_weak!(self.miner); + let hash: H256 = hash.into(); + match (miner.pending_receipt(&hash), self.options.allow_pending_receipt_query) { + (Some(receipt), true) => Ok(Some(receipt.into())), + _ => { + let client = take_weak!(self.client); + let receipt = client.transaction_receipt(TransactionID::Hash(hash)); + Ok(receipt.map(Into::into)) + } + } } - fn uncle_by_block_hash_and_index(&self, params: Params) -> Result { + fn uncle_by_block_hash_and_index(&self, hash: RpcH256, index: Index) -> Result, Error> { try!(self.active()); - from_params::<(RpcH256, Index)>(params) - .and_then(|(hash, index)| self.uncle(UncleID { block: BlockID::Hash(hash.into()), position: index.value() })) + + self.uncle(UncleID { block: BlockID::Hash(hash.into()), position: index.value() }) } - fn uncle_by_block_number_and_index(&self, params: Params) -> Result { + fn uncle_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> Result, Error> { try!(self.active()); - from_params::<(BlockNumber, Index)>(params) - .and_then(|(number, index)| self.uncle(UncleID { block: number.into(), position: index.value() })) + + self.uncle(UncleID { block: num.into(), position: index.value() }) } - fn compilers(&self, params: Params) -> Result { + fn compilers(&self) -> Result, Error> { try!(self.active()); - try!(expect_no_params(params)); let mut compilers = vec![]; if Command::new(SOLC).output().is_ok() { compilers.push("solidity".to_owned()) } - Ok(to_value(&compilers)) + + Ok(compilers) } - fn logs(&self, params: Params) -> Result { - try!(self.active()); - from_params::<(Filter, )>(params).and_then(|(filter,)| { - let include_pending = filter.to_block == Some(BlockNumber::Pending); - let filter: EthcoreFilter = filter.into(); - let mut logs = take_weak!(self.client).logs(filter.clone()) - .into_iter() - .map(From::from) - .collect::>(); + fn logs(&self, filter: Filter) -> Result, Error> { + let include_pending = filter.to_block == Some(BlockNumber::Pending); + let filter: EthcoreFilter = filter.into(); + let mut logs = take_weak!(self.client).logs(filter.clone()) + .into_iter() + .map(From::from) + .collect::>(); - if include_pending { - let pending = pending_logs(&*take_weak!(self.miner), &filter); - logs.extend(pending); - } + if include_pending { + let pending = pending_logs(&*take_weak!(self.miner), &filter); + logs.extend(pending); + } - let logs = limit_logs(logs, filter.limit); - Ok(to_value(&logs)) - }) + let logs = limit_logs(logs, filter.limit); + + Ok(logs) } - fn work(&self, params: Params) -> Result { + fn work(&self, no_new_work_timeout: Trailing) -> Result { try!(self.active()); - let (no_new_work_timeout,) = from_params::<(u64,)>(params).unwrap_or((0,)); + let no_new_work_timeout = no_new_work_timeout.0; let client = take_weak!(self.client); // check if we're still syncing and return empty strings in that case @@ -550,115 +535,118 @@ impl Eth for EthClient where if no_new_work_timeout > 0 && b.block().header().timestamp() + no_new_work_timeout < get_time().sec as u64 { Err(errors::no_new_work()) } else if self.options.send_block_number_in_get_work { - let block_number = RpcU256::from(b.block().header().number()); - Ok(to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target), block_number))) + let block_number = b.block().header().number(); + Ok(Work { + pow_hash: pow_hash.into(), + seed_hash: seed_hash.into(), + target: target.into(), + number: Some(block_number), + }) } else { - Ok(to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target)))) + Ok(Work { + pow_hash: pow_hash.into(), + seed_hash: seed_hash.into(), + target: target.into(), + number: None + }) } }).unwrap_or(Err(Error::internal_error())) // no work found. } - fn submit_work(&self, params: Params) -> Result { + fn submit_work(&self, nonce: RpcH64, pow_hash: RpcH256, mix_hash: RpcH256) -> Result { try!(self.active()); - from_params::<(RpcH64, RpcH256, RpcH256)>(params).and_then(|(nonce, pow_hash, mix_hash)| { - let nonce: H64 = nonce.into(); - let pow_hash: H256 = pow_hash.into(); - let mix_hash: H256 = mix_hash.into(); - trace!(target: "miner", "submit_work: Decoded: nonce={}, pow_hash={}, mix_hash={}", nonce, pow_hash, mix_hash); - let miner = take_weak!(self.miner); - let client = take_weak!(self.client); - let seal = vec![rlp::encode(&mix_hash).to_vec(), rlp::encode(&nonce).to_vec()]; - let r = miner.submit_seal(&*client, pow_hash, seal); - Ok(to_value(&r.is_ok())) - }) + + let nonce: H64 = nonce.into(); + let pow_hash: H256 = pow_hash.into(); + let mix_hash: H256 = mix_hash.into(); + trace!(target: "miner", "submit_work: Decoded: nonce={}, pow_hash={}, mix_hash={}", nonce, pow_hash, mix_hash); + + let miner = take_weak!(self.miner); + let client = take_weak!(self.client); + let seal = vec![rlp::encode(&mix_hash).to_vec(), rlp::encode(&nonce).to_vec()]; + Ok(miner.submit_seal(&*client, pow_hash, seal).is_ok()) } - fn submit_hashrate(&self, params: Params) -> Result { + fn submit_hashrate(&self, rate: RpcU256, id: RpcH256) -> Result { try!(self.active()); - from_params::<(RpcU256, RpcH256)>(params).and_then(|(rate, id)| { - self.external_miner.submit_hashrate(rate.into(), id.into()); - Ok(to_value(&true)) - }) + self.external_miner.submit_hashrate(rate.into(), id.into()); + Ok(true) } - fn send_raw_transaction(&self, params: Params) -> Result { + fn send_raw_transaction(&self, raw: Bytes) -> Result { try!(self.active()); - from_params::<(Bytes, )>(params) - .and_then(|(raw_transaction, )| { - let raw_transaction = raw_transaction.to_vec(); - match UntrustedRlp::new(&raw_transaction).as_val() { - Ok(signed_transaction) => dispatch_transaction(&*take_weak!(self.client), &*take_weak!(self.miner), signed_transaction), - Err(_) => Ok(to_value(&RpcH256::from(H256::from(0)))), + + let raw_transaction = raw.to_vec(); + match UntrustedRlp::new(&raw_transaction).as_val() { + Ok(signed_transaction) => dispatch_transaction(&*take_weak!(self.client), &*take_weak!(self.miner), signed_transaction), + Err(_) => Ok(RpcH256::from(H256::from(0))), + } + } + + fn call(&self, request: CallRequest, num: Trailing) -> Result { + try!(self.active()); + + let request = CallRequest::into(request); + let signed = try!(self.sign_call(request)); + + let r = match num.0 { + BlockNumber::Pending => take_weak!(self.miner).call(&*take_weak!(self.client), &signed, Default::default()), + num => take_weak!(self.client).call(&signed, num.into(), Default::default()), + }; + + Ok(r.map(|e| Bytes(e.output)).unwrap_or(Bytes::new(vec![]))) + } + + fn estimate_gas(&self, request: CallRequest, num: Trailing) -> Result { + try!(self.active()); + + let request = CallRequest::into(request); + let signed = try!(self.sign_call(request)); + let r = match num.0 { + BlockNumber::Pending => take_weak!(self.miner).call(&*take_weak!(self.client), &signed, Default::default()), + num => take_weak!(self.client).call(&signed, num.into(), Default::default()), + }; + + Ok(RpcU256::from(r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0)))) + } + + fn compile_lll(&self, _: String) -> Result { + try!(self.active()); + + rpc_unimplemented!() + } + + fn compile_serpent(&self, _: String) -> Result { + try!(self.active()); + + rpc_unimplemented!() + } + + fn compile_solidity(&self, code: String) -> Result { + try!(self.active()); + let maybe_child = Command::new(SOLC) + .arg("--bin") + .arg("--optimize") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .spawn(); + + 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() { + Ok(Bytes::new(hex.from_hex().unwrap_or(vec![]))) + } else { + Err(errors::compilation("Unexpected output.")) } - }) - } - - fn call(&self, params: Params) -> Result { - try!(self.active()); - from_params_default_second(params) - .and_then(|(request, block_number,)| { - let request = CallRequest::into(request); - let signed = try!(self.sign_call(request)); - let r = match block_number { - BlockNumber::Pending => take_weak!(self.miner).call(&*take_weak!(self.client), &signed, Default::default()), - block_number => take_weak!(self.client).call(&signed, block_number.into(), Default::default()), - }; - Ok(to_value(&r.map(|e| Bytes(e.output)).unwrap_or(Bytes::new(vec![])))) - }) - } - - fn estimate_gas(&self, params: Params) -> Result { - try!(self.active()); - from_params_default_second(params) - .and_then(|(request, block_number,)| { - let request = CallRequest::into(request); - let signed = try!(self.sign_call(request)); - let r = match block_number { - BlockNumber::Pending => take_weak!(self.miner).call(&*take_weak!(self.client), &signed, Default::default()), - block => take_weak!(self.client).call(&signed, block.into(), Default::default()), - }; - Ok(to_value(&RpcU256::from(r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0))))) - }) - } - - fn compile_lll(&self, _: Params) -> Result { - try!(self.active()); - rpc_unimplemented!() - } - - fn compile_serpent(&self, _: Params) -> Result { - try!(self.active()); - rpc_unimplemented!() - } - - fn compile_solidity(&self, params: Params) -> Result { - try!(self.active()); - from_params::<(String, )>(params) - .and_then(|(code, )| { - let maybe_child = Command::new(SOLC) - .arg("--bin") - .arg("--optimize") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::null()) - .spawn(); - - 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() { - Ok(to_value(&Bytes::new(hex.from_hex().unwrap_or(vec![])))) - } else { - Err(errors::compilation("Unexpected output.")) - } - }) }) } } diff --git a/rpc/src/v1/impls/eth_filter.rs b/rpc/src/v1/impls/eth_filter.rs index b6e932d28..03d9d7215 100644 --- a/rpc/src/v1/impls/eth_filter.rs +++ b/rpc/src/v1/impls/eth_filter.rs @@ -24,9 +24,8 @@ use ethcore::filter::Filter as EthcoreFilter; use ethcore::client::{BlockChainClient, BlockID}; use util::Mutex; use v1::traits::EthFilter; -use v1::types::{BlockNumber, Index, Filter, Log, H256 as RpcH256, U256 as RpcU256}; +use v1::types::{BlockNumber, Index, Filter, FilterChanges, Log, H256 as RpcH256, U256 as RpcU256}; use v1::helpers::{PollFilter, PollManager, limit_logs}; -use v1::helpers::params::expect_no_params; use v1::impls::eth::pending_logs; /// Eth filter rpc implementation. @@ -59,168 +58,154 @@ impl EthFilterClient where } } -impl EthFilter for EthFilterClient where - C: BlockChainClient + 'static, - M: MinerService + 'static { - - fn new_filter(&self, params: Params) -> Result { +impl EthFilter for EthFilterClient + where C: BlockChainClient + 'static, M: MinerService + 'static +{ + fn new_filter(&self, filter: Filter) -> Result { try!(self.active()); - from_params::<(Filter, )>(params) - .and_then(|(filter, )| { - let mut polls = self.polls.lock(); - let block_number = take_weak!(self.client).chain_info().best_block_number; - let id = polls.create_poll(PollFilter::Logs(block_number, Default::default(), filter)); - Ok(to_value(&RpcU256::from(id))) - }) + let mut polls = self.polls.lock(); + let block_number = take_weak!(self.client).chain_info().best_block_number; + let id = polls.create_poll(PollFilter::Logs(block_number, Default::default(), filter)); + Ok(id.into()) } - fn new_block_filter(&self, params: Params) -> Result { + fn new_block_filter(&self) -> Result { try!(self.active()); - 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)); - Ok(to_value(&RpcU256::from(id))) + Ok(id.into()) } - fn new_pending_transaction_filter(&self, params: Params) -> Result { + fn new_pending_transaction_filter(&self) -> Result { try!(self.active()); - try!(expect_no_params(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)); - - Ok(to_value(&RpcU256::from(id))) + Ok(id.into()) } - fn filter_changes(&self, params: Params) -> Result { + fn filter_changes(&self, index: Index) -> Result { try!(self.active()); let client = take_weak!(self.client); - from_params::<(Index,)>(params) - .and_then(|(index,)| { - let mut polls = self.polls.lock(); - match polls.poll_mut(&index.value()) { - None => Ok(Value::Array(vec![] as Vec)), - Some(filter) => match *filter { - PollFilter::Block(ref mut block_number) => { - // + 1, cause we want to return hashes including current block hash. - let current_number = client.chain_info().best_block_number + 1; - let hashes = (*block_number..current_number).into_iter() - .map(BlockID::Number) - .filter_map(|id| client.block_hash(id)) - .map(Into::into) - .collect::>(); + let mut polls = self.polls.lock(); + match polls.poll_mut(&index.value()) { + None => Ok(FilterChanges::Empty), + Some(filter) => match *filter { + PollFilter::Block(ref mut block_number) => { + // + 1, cause we want to return hashes including current block hash. + let current_number = client.chain_info().best_block_number + 1; + let hashes = (*block_number..current_number).into_iter() + .map(BlockID::Number) + .filter_map(|id| client.block_hash(id)) + .map(Into::into) + .collect::>(); - *block_number = current_number; + *block_number = current_number; - Ok(to_value(&hashes)) - }, - PollFilter::PendingTransaction(ref mut previous_hashes) => { - // get hashes of pending transactions - let current_hashes = take_weak!(self.miner).pending_transactions_hashes(); + Ok(FilterChanges::Hashes(hashes)) + }, + PollFilter::PendingTransaction(ref mut previous_hashes) => { + // get hashes of pending transactions + let current_hashes = take_weak!(self.miner).pending_transactions_hashes(); - let new_hashes = - { - let previous_hashes_set = previous_hashes.iter().collect::>(); + let new_hashes = + { + let previous_hashes_set = previous_hashes.iter().collect::>(); - // find all new hashes - current_hashes - .iter() - .filter(|hash| !previous_hashes_set.contains(hash)) - .cloned() - .map(Into::into) - .collect::>() - }; + // find all new hashes + current_hashes + .iter() + .filter(|hash| !previous_hashes_set.contains(hash)) + .cloned() + .map(Into::into) + .collect::>() + }; - // save all hashes of pending transactions - *previous_hashes = current_hashes; + // save all hashes of pending transactions + *previous_hashes = current_hashes; - // return new hashes - Ok(to_value(&new_hashes)) - }, - PollFilter::Logs(ref mut block_number, ref mut previous_logs, ref filter) => { - // retrive the current block number - let current_number = client.chain_info().best_block_number; + // return new hashes + Ok(FilterChanges::Hashes(new_hashes)) + }, + PollFilter::Logs(ref mut block_number, ref mut previous_logs, ref filter) => { + // retrive the current block number + let current_number = client.chain_info().best_block_number; - // check if we need to check pending hashes - let include_pending = filter.to_block == Some(BlockNumber::Pending); + // check if we need to check pending hashes + let include_pending = filter.to_block == Some(BlockNumber::Pending); - // build appropriate filter - let mut filter: EthcoreFilter = filter.clone().into(); - filter.from_block = BlockID::Number(*block_number); - filter.to_block = BlockID::Latest; + // build appropriate filter + let mut filter: EthcoreFilter = filter.clone().into(); + filter.from_block = BlockID::Number(*block_number); + filter.to_block = BlockID::Latest; - // retrieve logs in range from_block..min(BlockID::Latest..to_block) - let mut logs = client.logs(filter.clone()) - .into_iter() - .map(From::from) - .collect::>(); + // retrieve logs in range from_block..min(BlockID::Latest..to_block) + let mut logs = client.logs(filter.clone()) + .into_iter() + .map(From::from) + .collect::>(); - // additionally retrieve pending logs - if include_pending { - let pending_logs = pending_logs(&*take_weak!(self.miner), &filter); + // additionally retrieve pending logs + if include_pending { + let pending_logs = pending_logs(&*take_weak!(self.miner), &filter); - // remove logs about which client was already notified about - let new_pending_logs: Vec<_> = pending_logs.iter() - .filter(|p| !previous_logs.contains(p)) - .cloned() - .collect(); + // remove logs about which client was already notified about + let new_pending_logs: Vec<_> = pending_logs.iter() + .filter(|p| !previous_logs.contains(p)) + .cloned() + .collect(); - // save all logs retrieved by client - *previous_logs = pending_logs.into_iter().collect(); + // save all logs retrieved by client + *previous_logs = pending_logs.into_iter().collect(); - // append logs array with new pending logs - logs.extend(new_pending_logs); - } - - let logs = limit_logs(logs, filter.limit); - - // save the number of the next block as a first block from which - // we want to get logs - *block_number = current_number + 1; - - Ok(to_value(&logs)) - } + // append logs array with new pending logs + logs.extend(new_pending_logs); } + + let logs = limit_logs(logs, filter.limit); + + // save the number of the next block as a first block from which + // we want to get logs + *block_number = current_number + 1; + + Ok(FilterChanges::Logs(logs)) } - }) + } + } } - fn filter_logs(&self, params: Params) -> Result { + fn filter_logs(&self, index: Index) -> Result, Error> { try!(self.active()); - from_params::<(Index,)>(params) - .and_then(|(index,)| { - let mut polls = self.polls.lock(); - match polls.poll(&index.value()) { - Some(&PollFilter::Logs(ref _block_number, ref _previous_log, ref filter)) => { - let include_pending = filter.to_block == Some(BlockNumber::Pending); - let filter: EthcoreFilter = filter.clone().into(); - let mut logs = take_weak!(self.client).logs(filter.clone()) - .into_iter() - .map(From::from) - .collect::>(); - if include_pending { - logs.extend(pending_logs(&*take_weak!(self.miner), &filter)); - } + let mut polls = self.polls.lock(); + match polls.poll(&index.value()) { + Some(&PollFilter::Logs(ref _block_number, ref _previous_log, ref filter)) => { + let include_pending = filter.to_block == Some(BlockNumber::Pending); + let filter: EthcoreFilter = filter.clone().into(); + let mut logs = take_weak!(self.client).logs(filter.clone()) + .into_iter() + .map(From::from) + .collect::>(); - let logs = limit_logs(logs, filter.limit); - - Ok(to_value(&logs)) - }, - // just empty array - _ => Ok(Value::Array(vec![] as Vec)), + if include_pending { + logs.extend(pending_logs(&*take_weak!(self.miner), &filter)); } - }) + + let logs = limit_logs(logs, filter.limit); + + Ok(logs) + }, + // just empty array + _ => Ok(Vec::new()), + } } - fn uninstall_filter(&self, params: Params) -> Result { + fn uninstall_filter(&self, index: Index) -> Result { try!(self.active()); - from_params::<(Index,)>(params) - .map(|(index,)| { - self.polls.lock().remove_poll(&index.value()); - to_value(&true) - }) + + self.polls.lock().remove_poll(&index.value()); + Ok(true) } } diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index 744d3b83a..80789fd0e 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -18,186 +18,185 @@ use std::sync::Arc; use jsonrpc_core::*; -/// Eth rpc interface. -pub trait Eth: Sized + Send + Sync + 'static { - /// Returns protocol version. - fn protocol_version(&self, _: Params) -> Result; +use v1::types::{Block, BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index}; +use v1::types::{Log, Receipt, SyncStatus, Transaction, Work}; +use v1::types::{H64, H160, H256, U256}; - /// Returns an object with data about the sync status or false. (wtf?) - fn syncing(&self, _: Params) -> Result; +use v1::helpers::auto_args::{Trailing, Wrap}; - /// Returns the number of hashes per second that the node is mining with. - fn hashrate(&self, _: Params) -> Result; +build_rpc_trait! { + /// Eth rpc interface. + pub trait Eth { + /// Returns protocol version encoded as a string (quotes are necessary). + #[name("eth_protocolVersion")] + fn protocol_version(&self) -> Result; - /// Returns block author. - fn author(&self, _: Params) -> Result; + /// Returns an object with data about the sync status or false. (wtf?) + #[name("eth_syncing")] + fn syncing(&self) -> Result; - /// Returns true if client is actively mining new blocks. - fn is_mining(&self, _: Params) -> Result; + /// Returns the number of hashes per second that the node is mining with. + #[name("eth_hashrate")] + fn hashrate(&self) -> Result; - /// Returns current gas_price. - fn gas_price(&self, _: Params) -> Result; + /// Returns block author. + #[name("eth_coinbase")] + fn author(&self) -> Result; - /// Returns accounts list. - fn accounts(&self, _: Params) -> Result; + /// Returns true if client is actively mining new blocks. + #[name("eth_mining")] + fn is_mining(&self) -> Result; - /// Returns highest block number. - fn block_number(&self, _: Params) -> Result; + /// Returns current gas_price. + #[name("eth_gasPrice")] + fn gas_price(&self) -> Result; - /// Returns balance of the given account. - fn balance(&self, _: Params) -> Result; + /// Returns accounts list. + #[name("eth_accounts")] + fn accounts(&self) -> Result, Error>; - /// Returns content of the storage at given address. - fn storage_at(&self, _: Params) -> Result; + /// Returns highest block number. + #[name("eth_blockNumber")] + fn block_number(&self) -> Result; - /// Returns block with given hash. - fn block_by_hash(&self, _: Params) -> Result; + /// Returns balance of the given account. + #[name("eth_getBalance")] + fn balance(&self, H160, Trailing) -> Result; - /// Returns block with given number. - fn block_by_number(&self, _: Params) -> Result; + /// Returns content of the storage at given address. + #[name("eth_getStorageAt")] + fn storage_at(&self, H160, U256, Trailing) -> Result; - /// Returns the number of transactions sent from given address at given time (block number). - fn transaction_count(&self, _: Params) -> Result; + /// Returns block with given hash. + #[name("eth_getBlockByHash")] + fn block_by_hash(&self, H256, bool) -> Result, Error>; - /// Returns the number of transactions in a block with given hash. - fn block_transaction_count_by_hash(&self, _: Params) -> Result; + /// Returns block with given number. + #[name("eth_getBlockByNumber")] + fn block_by_number(&self, BlockNumber, bool) -> Result, Error>; - /// Returns the number of transactions in a block with given block number. - fn block_transaction_count_by_number(&self, _: Params) -> Result; + /// Returns the number of transactions sent from given address at given time (block number). + #[name("eth_getTransactionCount")] + fn transaction_count(&self, H160, Trailing) -> Result; - /// Returns the number of uncles in a block with given hash. - fn block_uncles_count_by_hash(&self, _: Params) -> Result; + /// Returns the number of transactions in a block with given hash. + #[name("eth_getBlockTransactionCountByHash")] + fn block_transaction_count_by_hash(&self, H256) -> Result, Error>; - /// Returns the number of uncles in a block with given block number. - fn block_uncles_count_by_number(&self, _: Params) -> Result; + /// Returns the number of transactions in a block with given block number. + #[name("eth_getBlockTransactionCountByNumber")] + fn block_transaction_count_by_number(&self, BlockNumber) -> Result, Error>; - /// Returns the code at given address at given time (block number). - fn code_at(&self, _: Params) -> Result; + /// Returns the number of uncles in a block with given hash. + #[name("eth_getUncleCountByBlockHash")] + fn block_uncles_count_by_hash(&self, H256) -> Result, Error>; - /// Sends signed transaction. - fn send_raw_transaction(&self, _: Params) -> Result; + /// Returns the number of uncles in a block with given block number. + #[name("eth_getUncleCountByBlockNumber")] + fn block_uncles_count_by_number(&self, BlockNumber) -> Result, Error>; - /// Call contract. - fn call(&self, _: Params) -> Result; + /// Returns the code at given address at given time (block number). + #[name("eth_getCode")] + fn code_at(&self, H160, Trailing) -> Result; - /// Estimate gas needed for execution of given contract. - fn estimate_gas(&self, _: Params) -> Result; + /// Sends signed transaction, returning its hash. + #[name("eth_sendRawTransaction")] + fn send_raw_transaction(&self, Bytes) -> Result; - /// Get transaction by its hash. - fn transaction_by_hash(&self, _: Params) -> Result; + /// Call contract, returning the output data. + #[name("eth_call")] + fn call(&self, CallRequest, Trailing) -> Result; - /// Returns transaction at given block hash and index. - fn transaction_by_block_hash_and_index(&self, _: Params) -> Result; + /// Estimate gas needed for execution of given contract. + #[name("eth_estimateGas")] + fn estimate_gas(&self, CallRequest, Trailing) -> Result; - /// Returns transaction by given block number and index. - fn transaction_by_block_number_and_index(&self, _: Params) -> Result; + /// Get transaction by its hash. + #[name("eth_getTransactionByHash")] + fn transaction_by_hash(&self, H256) -> Result, Error>; - /// Returns transaction receipt. - fn transaction_receipt(&self, _: Params) -> Result; + /// Returns transaction at given block hash and index. + #[name("eth_getTransactionByBlockHashAndIndex")] + fn transaction_by_block_hash_and_index(&self, H256, Index) -> Result, Error>; - /// Returns an uncles at given block and index. - fn uncle_by_block_hash_and_index(&self, _: Params) -> Result; + /// Returns transaction by given block number and index. + #[name("eth_getTransactionByBlockNumberAndIndex")] + fn transaction_by_block_number_and_index(&self, BlockNumber, Index) -> Result, Error>; - /// Returns an uncles at given block and index. - fn uncle_by_block_number_and_index(&self, _: Params) -> Result; + /// Returns transaction receipt. + #[name("eth_getTransactionReceipt")] + fn transaction_receipt(&self, H256) -> Result, Error>; - /// Returns available compilers. - fn compilers(&self, _: Params) -> Result; + /// Returns an uncles at given block and index. + #[name("eth_getUncleByBlockHashAndIndex")] + fn uncle_by_block_hash_and_index(&self, H256, Index) -> Result, Error>; - /// Compiles lll code. - fn compile_lll(&self, _: Params) -> Result; + /// Returns an uncles at given block and index. + #[name("eth_getUncleByBlockNumberAndIndex")] + fn uncle_by_block_number_and_index(&self, BlockNumber, Index) -> Result, Error>; - /// Compiles solidity. - fn compile_solidity(&self, _: Params) -> Result; + /// Returns available compilers. + #[name("eth_getCompilers")] + fn compilers(&self) -> Result, Error>; - /// Compiles serpent. - fn compile_serpent(&self, _: Params) -> Result; + /// Compiles lll code. + #[name("eth_compileLLL")] + fn compile_lll(&self, String) -> Result; - /// Returns logs matching given filter object. - fn logs(&self, _: Params) -> Result; + /// Compiles solidity. + #[name("eth_compileSolidity")] + fn compile_solidity(&self, String) -> Result; - /// Returns the hash of the current block, the seedHash, and the boundary condition to be met. - fn work(&self, _: Params) -> Result; + /// Compiles serpent. + #[name("eth_compileSerpent")] + fn compile_serpent(&self, String) -> Result; - /// Used for submitting a proof-of-work solution. - fn submit_work(&self, _: Params) -> Result; + /// Returns logs matching given filter object. + #[name("eth_getLogs")] + fn logs(&self, Filter) -> Result, Error>; - /// Used for submitting mining hashrate. - fn submit_hashrate(&self, _: Params) -> Result; + /// Returns the hash of the current block, the seedHash, and the boundary condition to be met. + #[name("eth_getWork")] + fn work(&self, Trailing) -> Result; - /// Should be used to convert object to io delegate. - fn to_delegate(self) -> IoDelegate { - let mut delegate = IoDelegate::new(Arc::new(self)); - delegate.add_method("eth_protocolVersion", Eth::protocol_version); - delegate.add_method("eth_syncing", Eth::syncing); - delegate.add_method("eth_hashrate", Eth::hashrate); - delegate.add_method("eth_coinbase", Eth::author); - delegate.add_method("eth_mining", Eth::is_mining); - delegate.add_method("eth_gasPrice", Eth::gas_price); - delegate.add_method("eth_accounts", Eth::accounts); - delegate.add_method("eth_blockNumber", Eth::block_number); - delegate.add_method("eth_getBalance", Eth::balance); - delegate.add_method("eth_getStorageAt", Eth::storage_at); - delegate.add_method("eth_getTransactionCount", Eth::transaction_count); - delegate.add_method("eth_getBlockTransactionCountByHash", Eth::block_transaction_count_by_hash); - delegate.add_method("eth_getBlockTransactionCountByNumber", Eth::block_transaction_count_by_number); - delegate.add_method("eth_getUncleCountByBlockHash", Eth::block_uncles_count_by_hash); - delegate.add_method("eth_getUncleCountByBlockNumber", Eth::block_uncles_count_by_number); - delegate.add_method("eth_getCode", Eth::code_at); - delegate.add_method("eth_sendRawTransaction", Eth::send_raw_transaction); - delegate.add_method("eth_call", Eth::call); - delegate.add_method("eth_estimateGas", Eth::estimate_gas); - delegate.add_method("eth_getBlockByHash", Eth::block_by_hash); - delegate.add_method("eth_getBlockByNumber", Eth::block_by_number); - delegate.add_method("eth_getTransactionByHash", Eth::transaction_by_hash); - delegate.add_method("eth_getTransactionByBlockHashAndIndex", Eth::transaction_by_block_hash_and_index); - delegate.add_method("eth_getTransactionByBlockNumberAndIndex", Eth::transaction_by_block_number_and_index); - delegate.add_method("eth_getTransactionReceipt", Eth::transaction_receipt); - delegate.add_method("eth_getUncleByBlockHashAndIndex", Eth::uncle_by_block_hash_and_index); - delegate.add_method("eth_getUncleByBlockNumberAndIndex", Eth::uncle_by_block_number_and_index); - delegate.add_method("eth_getCompilers", Eth::compilers); - delegate.add_method("eth_compileLLL", Eth::compile_lll); - delegate.add_method("eth_compileSolidity", Eth::compile_solidity); - delegate.add_method("eth_compileSerpent", Eth::compile_serpent); - delegate.add_method("eth_getLogs", Eth::logs); - delegate.add_method("eth_getWork", Eth::work); - delegate.add_method("eth_submitWork", Eth::submit_work); - delegate.add_method("eth_submitHashrate", Eth::submit_hashrate); - delegate + /// Used for submitting a proof-of-work solution. + #[name("eth_submitWork")] + fn submit_work(&self, H64, H256, H256) -> Result; + + /// Used for submitting mining hashrate. + #[name("eth_submitHashrate")] + fn submit_hashrate(&self, U256, H256) -> Result; } } -/// Eth filters rpc api (polling). -// TODO: do filters api properly -pub trait EthFilter: Sized + Send + Sync + 'static { - /// Returns id of new filter. - fn new_filter(&self, _: Params) -> Result; +build_rpc_trait! { - /// Returns id of new block filter. - fn new_block_filter(&self, _: Params) -> Result; + /// Eth filters rpc api (polling). + // TODO: do filters api properly + pub trait EthFilter { + /// Returns id of new filter. + #[name("eth_newFilter")] + fn new_filter(&self, Filter) -> Result; - /// Returns id of new block filter. - fn new_pending_transaction_filter(&self, _: Params) -> Result; + /// Returns id of new block filter. + #[name("eth_newBlockFilter")] + fn new_block_filter(&self) -> Result; - /// Returns filter changes since last poll. - fn filter_changes(&self, _: Params) -> Result; + /// Returns id of new block filter. + #[name("eth_newPendingTransactionFilter")] + fn new_pending_transaction_filter(&self) -> Result; - /// Returns all logs matching given filter (in a range 'from' - 'to'). - fn filter_logs(&self, _: Params) -> Result; + /// Returns filter changes since last poll. + #[name("eth_getFilterChanges")] + fn filter_changes(&self, Index) -> Result; - /// Uninstalls filter. - fn uninstall_filter(&self, _: Params) -> Result; + /// Returns all logs matching given filter (in a range 'from' - 'to'). + #[name("eth_getFilterLogs")] + fn filter_logs(&self, Index) -> Result, Error>; - /// Should be used to convert object to io delegate. - fn to_delegate(self) -> IoDelegate { - let mut delegate = IoDelegate::new(Arc::new(self)); - delegate.add_method("eth_newFilter", EthFilter::new_filter); - delegate.add_method("eth_newBlockFilter", EthFilter::new_block_filter); - delegate.add_method("eth_newPendingTransactionFilter", EthFilter::new_pending_transaction_filter); - delegate.add_method("eth_getFilterChanges", EthFilter::filter_changes); - delegate.add_method("eth_getFilterLogs", EthFilter::filter_logs); - delegate.add_method("eth_uninstallFilter", EthFilter::uninstall_filter); - delegate + /// Uninstalls filter. + #[name("eth_uninstallFilter")] + fn uninstall_filter(&self, Index) -> Result; } } diff --git a/rpc/src/v1/types/block_number.rs b/rpc/src/v1/types/block_number.rs index 302d099d5..01625f8ed 100644 --- a/rpc/src/v1/types/block_number.rs +++ b/rpc/src/v1/types/block_number.rs @@ -31,6 +31,12 @@ pub enum BlockNumber { Pending, } +impl Default for BlockNumber { + fn default() -> Self { + BlockNumber::Latest + } +} + impl Deserialize for BlockNumber { fn deserialize(deserializer: &mut D) -> Result where D: Deserializer { diff --git a/rpc/src/v1/types/filter.rs b/rpc/src/v1/types/filter.rs index 8e6223b12..b4a45272b 100644 --- a/rpc/src/v1/types/filter.rs +++ b/rpc/src/v1/types/filter.rs @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use serde::{Deserialize, Deserializer, Error}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, Error}; use serde_json::value; use jsonrpc_core::Value; use ethcore::filter::Filter as EthFilter; use ethcore::client::BlockID; -use v1::types::{BlockNumber, H160, H256}; +use v1::types::{BlockNumber, H160, H256, Log}; /// Variadic value #[derive(Debug, PartialEq, Clone)] @@ -93,6 +93,27 @@ impl Into for Filter { } } +/// Results of the filter_changes RPC. +#[derive(Debug, PartialEq)] +pub enum FilterChanges { + /// New logs. + Logs(Vec), + /// New hashes (block or transactions) + Hashes(Vec), + /// Empty result, + Empty, +} + +impl Serialize for FilterChanges { + fn serialize(&self, s: &mut S) -> Result<(), S::Error> where S: Serializer { + match *self { + FilterChanges::Logs(ref logs) => logs.serialize(s), + FilterChanges::Hashes(ref hashes) => hashes.serialize(s), + FilterChanges::Empty => (&[] as &[Value]).serialize(s), + } + } +} + #[cfg(test)] mod tests { use serde_json; diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index e4fd613aa..1369037ed 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -30,13 +30,14 @@ mod receipt; mod trace; mod trace_filter; mod uint; +mod work; pub use self::bytes::Bytes; pub use self::block::{Block, BlockTransactions}; pub use self::block_number::BlockNumber; pub use self::call_request::CallRequest; pub use self::confirmations::{ConfirmationPayload, ConfirmationRequest, TransactionModification}; -pub use self::filter::Filter; +pub use self::filter::{Filter, FilterChanges}; pub use self::hash::{H64, H160, H256, H512, H520, H2048}; pub use self::index::Index; pub use self::log::Log; @@ -47,3 +48,4 @@ pub use self::receipt::Receipt; pub use self::trace::{LocalizedTrace, TraceResults}; pub use self::trace_filter::TraceFilter; pub use self::uint::U256; +pub use self::work::Work; diff --git a/rpc/src/v1/types/work.rs b/rpc/src/v1/types/work.rs new file mode 100644 index 000000000..0817eb24a --- /dev/null +++ b/rpc/src/v1/types/work.rs @@ -0,0 +1,43 @@ +// 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 . + +use super::{H256, U256}; + +use serde::{Serialize, Serializer}; + +/// The result of an `eth_getWork` call: it differs based on an option +/// whether to send the block number. +#[derive(Debug, PartialEq, Eq)] +pub struct Work { + /// The proof-of-work hash. + pub pow_hash: H256, + /// The seed hash. + pub seed_hash: H256, + /// The target. + pub target: H256, + /// The block number: this isn't always stored. + pub number: Option, +} + +impl Serialize for Work { + fn serialize(&self, s: &mut S) -> Result<(), S::Error> where S: Serializer { + match self.number.as_ref() { + Some(num) => (&self.pow_hash, &self.seed_hash, &self.target, U256::from(*num)).serialize(s), + None => (&self.pow_hash, &self.seed_hash, &self.target).serialize(s), + } + } +} +