From b33b237f7661af0dc36d74d085bb76e2537f826d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 9 Nov 2016 13:13:35 +0100 Subject: [PATCH] Refactoring Signer to auto_args + eth_signTransaction (#3261) * Sign transaction initial * Refactoring signer to auto_args --- rpc/src/v1/helpers/dispatch.rs | 147 +++++++++++++----- rpc/src/v1/helpers/requests.rs | 17 ++- rpc/src/v1/helpers/signing_queue.rs | 14 +- rpc/src/v1/impls/eth.rs | 4 +- rpc/src/v1/impls/personal.rs | 16 +- rpc/src/v1/impls/signer.rs | 30 ++-- rpc/src/v1/impls/signing.rs | 222 ++++++++++++++-------------- rpc/src/v1/impls/signing_unsafe.rs | 78 +++++++--- rpc/src/v1/tests/mocked/eth.rs | 41 ++++- rpc/src/v1/tests/mocked/signer.rs | 12 +- rpc/src/v1/tests/mocked/signing.rs | 65 +++++++- rpc/src/v1/traits/eth.rs | 2 +- rpc/src/v1/traits/eth_signing.rs | 35 ++--- rpc/src/v1/traits/parity_signing.rs | 50 +++---- rpc/src/v1/traits/signer.rs | 6 +- rpc/src/v1/types/confirmations.rs | 102 +++++++++++-- rpc/src/v1/types/mod.rs.in | 2 +- 17 files changed, 560 insertions(+), 283 deletions(-) diff --git a/rpc/src/v1/helpers/dispatch.rs b/rpc/src/v1/helpers/dispatch.rs index 457f96552..cbae49cc3 100644 --- a/rpc/src/v1/helpers/dispatch.rs +++ b/rpc/src/v1/helpers/dispatch.rs @@ -14,84 +14,153 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use rlp; use util::{Address, H256, U256, Uint, Bytes}; use util::bytes::ToPretty; + use ethkey::Signature; use ethcore::miner::MinerService; use ethcore::client::MiningBlockChainClient; use ethcore::transaction::{Action, SignedTransaction, Transaction}; use ethcore::account_provider::AccountProvider; -use jsonrpc_core::{Error, Value, to_value}; -use v1::helpers::TransactionRequest; -use v1::types::{H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes}; -use v1::helpers::errors; + +use jsonrpc_core::Error; +use v1::helpers::{errors, TransactionRequest, FilledTransactionRequest, ConfirmationPayload}; +use v1::types::{ + H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes, + ConfirmationPayload as RpcConfirmationPayload, + ConfirmationResponse, + SignRequest as RpcSignRequest, + DecryptRequest as RpcDecryptRequest, +}; pub const DEFAULT_MAC: [u8; 2] = [0, 0]; -pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result - where C: MiningBlockChainClient, M: MinerService { - let hash = RpcH256::from(signed_transaction.hash()); - - miner.import_own_transaction(client, signed_transaction) - .map_err(errors::from_transaction_error) - .map(|_| hash) +pub fn execute(client: &C, miner: &M, accounts: &AccountProvider, payload: ConfirmationPayload, pass: Option) -> Result + where C: MiningBlockChainClient, M: MinerService +{ + match payload { + ConfirmationPayload::SendTransaction(request) => { + sign_and_dispatch(client, miner, accounts, request, pass) + .map(RpcH256::from) + .map(ConfirmationResponse::SendTransaction) + }, + ConfirmationPayload::SignTransaction(request) => { + sign_no_dispatch(client, miner, accounts, request, pass) + .map(|tx| rlp::encode(&tx).to_vec()) + .map(RpcBytes) + .map(ConfirmationResponse::SignTransaction) + }, + ConfirmationPayload::Signature(address, hash) => { + signature(accounts, address, hash, pass) + .map(RpcH520::from) + .map(ConfirmationResponse::Signature) + }, + ConfirmationPayload::Decrypt(address, data) => { + decrypt(accounts, address, data, pass) + .map(RpcBytes) + .map(ConfirmationResponse::Decrypt) + }, + } } -fn signature(accounts: &AccountProvider, address: Address, password: Option, hash: H256) -> Result { +fn signature(accounts: &AccountProvider, address: Address, hash: H256, password: Option) -> Result { accounts.sign(address, password.clone(), hash).map_err(|e| match password { Some(_) => errors::from_password_error(e), None => errors::from_signing_error(e), }) } -pub fn sign(accounts: &AccountProvider, address: Address, password: Option, hash: H256) -> Result { - signature(accounts, address, password, hash) - .map(RpcH520::from) - .map(to_value) -} - -pub fn decrypt(accounts: &AccountProvider, address: Address, password: Option, msg: Bytes) -> Result { +fn decrypt(accounts: &AccountProvider, address: Address, msg: Bytes, password: Option) -> Result { accounts.decrypt(address, password.clone(), &DEFAULT_MAC, &msg) .map_err(|e| match password { Some(_) => errors::from_password_error(e), None => errors::from_signing_error(e), }) - .map(RpcBytes::from) - .map(to_value) } -pub fn sign_and_dispatch(client: &C, miner: &M, accounts: &AccountProvider, request: TransactionRequest, password: Option) -> Result +pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result + where C: MiningBlockChainClient, M: MinerService { + let hash = signed_transaction.hash(); + + miner.import_own_transaction(client, signed_transaction) + .map_err(errors::from_transaction_error) + .map(|_| hash) +} + +pub fn sign_no_dispatch(client: &C, miner: &M, accounts: &AccountProvider, filled: FilledTransactionRequest, password: Option) -> Result where C: MiningBlockChainClient, M: MinerService { let network_id = client.signing_network_id(); - let address = request.from; + let address = filled.from; let signed_transaction = { - let t = prepare_transaction(client, miner, request); + let t = Transaction { + nonce: filled.nonce + .or_else(|| miner + .last_nonce(&filled.from) + .map(|nonce| nonce + U256::one())) + .unwrap_or_else(|| client.latest_nonce(&filled.from)), + + action: filled.to.map_or(Action::Create, Action::Call), + gas: filled.gas, + gas_price: filled.gas_price, + value: filled.value, + data: filled.data, + }; + let hash = t.hash(network_id); - let signature = try!(signature(accounts, address, password, hash)); + let signature = try!(signature(accounts, address, hash, password)); t.with_signature(signature, network_id) }; + Ok(signed_transaction) +} - trace!(target: "miner", "send_transaction: dispatching tx: {} for network ID {:?}", ::rlp::encode(&signed_transaction).to_vec().pretty(), network_id); +pub fn sign_and_dispatch(client: &C, miner: &M, accounts: &AccountProvider, filled: FilledTransactionRequest, password: Option) -> Result + where C: MiningBlockChainClient, M: MinerService +{ + + let network_id = client.signing_network_id(); + let signed_transaction = try!(sign_no_dispatch(client, miner, accounts, filled, password)); + + trace!(target: "miner", "send_transaction: dispatching tx: {} for network ID {:?}", rlp::encode(&signed_transaction).to_vec().pretty(), network_id); dispatch_transaction(&*client, &*miner, signed_transaction) } -fn prepare_transaction(client: &C, miner: &M, request: TransactionRequest) -> Transaction where C: MiningBlockChainClient, M: MinerService { - Transaction { - nonce: request.nonce - .or_else(|| miner - .last_nonce(&request.from) - .map(|nonce| nonce + U256::one())) - .unwrap_or_else(|| client.latest_nonce(&request.from)), - - action: request.to.map_or(Action::Create, Action::Call), - gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()), +pub fn fill_optional_fields(request: TransactionRequest, client: &C, miner: &M) -> FilledTransactionRequest + where C: MiningBlockChainClient, M: MinerService +{ + FilledTransactionRequest { + from: request.from, + to: request.to, + nonce: request.nonce, gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(client, miner)), - value: request.value.unwrap_or_else(U256::zero), - data: request.data.map_or_else(Vec::new, |b| b.to_vec()), + gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()), + value: request.value.unwrap_or_else(|| 0.into()), + data: request.data.unwrap_or_else(Vec::new), } } -pub fn default_gas_price(client: &C, miner: &M) -> U256 where C: MiningBlockChainClient, M: MinerService { +pub fn default_gas_price(client: &C, miner: &M) -> U256 + where C: MiningBlockChainClient, M: MinerService +{ client.gas_price_median(100).unwrap_or_else(|| miner.sensible_gas_price()) } + +pub fn from_rpc(payload: RpcConfirmationPayload, client: &C, miner: &M) -> ConfirmationPayload + where C: MiningBlockChainClient, M: MinerService { + + match payload { + RpcConfirmationPayload::SendTransaction(request) => { + ConfirmationPayload::SendTransaction(fill_optional_fields(request.into(), client, miner)) + }, + RpcConfirmationPayload::SignTransaction(request) => { + ConfirmationPayload::SignTransaction(fill_optional_fields(request.into(), client, miner)) + }, + RpcConfirmationPayload::Decrypt(RpcDecryptRequest { address, msg }) => { + ConfirmationPayload::Decrypt(address.into(), msg.into()) + }, + RpcConfirmationPayload::Signature(RpcSignRequest { address, hash }) => { + ConfirmationPayload::Signature(address.into(), hash.into()) + }, + } +} diff --git a/rpc/src/v1/helpers/requests.rs b/rpc/src/v1/helpers/requests.rs index 5cb6108c1..7c5e89b87 100644 --- a/rpc/src/v1/helpers/requests.rs +++ b/rpc/src/v1/helpers/requests.rs @@ -100,9 +100,22 @@ pub struct ConfirmationRequest { #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub enum ConfirmationPayload { /// Transaction - Transaction(FilledTransactionRequest), + SendTransaction(FilledTransactionRequest), + /// Sign Transaction + SignTransaction(FilledTransactionRequest), /// Sign request - Sign(Address, H256), + Signature(Address, H256), /// Decrypt request Decrypt(Address, Bytes), } + +impl ConfirmationPayload { + pub fn sender(&self) -> Address { + match *self { + ConfirmationPayload::SendTransaction(ref request) => request.from, + ConfirmationPayload::SignTransaction(ref request) => request.from, + ConfirmationPayload::Signature(ref address, _) => *address, + ConfirmationPayload::Decrypt(ref address, _) => *address, + } + } +} diff --git a/rpc/src/v1/helpers/signing_queue.rs b/rpc/src/v1/helpers/signing_queue.rs index 22e15d0d6..144e672c1 100644 --- a/rpc/src/v1/helpers/signing_queue.rs +++ b/rpc/src/v1/helpers/signing_queue.rs @@ -21,9 +21,10 @@ use std::collections::BTreeMap; use jsonrpc_core; use util::{Mutex, RwLock, U256}; use v1::helpers::{ConfirmationRequest, ConfirmationPayload}; +use v1::types::ConfirmationResponse; /// Result that can be returned from JSON RPC. -pub type RpcResult = Result; +pub type RpcResult = Result; /// Possible events happening in the queue that can be listened to. #[derive(Debug, PartialEq)] @@ -314,13 +315,12 @@ mod test { use std::time::Duration; use std::thread; use std::sync::{mpsc, Arc}; - use util::{Address, U256, H256, Mutex}; + use util::{Address, U256, Mutex}; use v1::helpers::{SigningQueue, ConfirmationsQueue, QueueEvent, FilledTransactionRequest, ConfirmationPayload}; - use v1::types::H256 as NH256; - use jsonrpc_core::to_value; + use v1::types::ConfirmationResponse; fn request() -> ConfirmationPayload { - ConfirmationPayload::Transaction(FilledTransactionRequest { + ConfirmationPayload::SendTransaction(FilledTransactionRequest { from: Address::from(1), to: Some(Address::from(2)), gas_price: 0.into(), @@ -353,10 +353,10 @@ mod test { // Just wait for the other thread to start thread::sleep(Duration::from_millis(100)); } - queue.request_confirmed(id, Ok(to_value(&NH256::from(H256::from(1))))); + queue.request_confirmed(id, Ok(ConfirmationResponse::SendTransaction(1.into()))); // then - assert_eq!(handle.join().expect("Thread should finish nicely"), Ok(to_value(&NH256::from(H256::from(1))))); + assert_eq!(handle.join().expect("Thread should finish nicely"), Ok(ConfirmationResponse::SendTransaction(1.into()))); } #[test] diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 4e54078fb..6986cf0ed 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -49,7 +49,7 @@ use v1::types::{ 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::dispatch::{dispatch_transaction, default_gas_price}; use v1::helpers::block_import::is_major_importing; use v1::helpers::auto_args::Trailing; @@ -610,7 +610,7 @@ impl Eth for EthClient where 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), + Ok(signed_transaction) => dispatch_transaction(&*take_weak!(self.client), &*take_weak!(self.miner), signed_transaction).map(Into::into), Err(e) => Err(errors::from_rlp_error(e)), } } diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index ffb20610c..5c4a2ff51 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -26,7 +26,7 @@ use jsonrpc_core::Error; use v1::traits::Personal; use v1::types::{H160 as RpcH160, H256 as RpcH256, TransactionRequest}; use v1::helpers::errors; -use v1::helpers::dispatch::sign_and_dispatch; +use v1::helpers::dispatch::{self, sign_and_dispatch}; /// Account management (personal) rpc implementation. pub struct PersonalClient where C: MiningBlockChainClient, M: MinerService { @@ -92,13 +92,17 @@ impl Personal for PersonalClient where C: MiningBl fn sign_and_send_transaction(&self, request: TransactionRequest, password: String) -> Result { try!(self.active()); + let client = take_weak!(self.client); + let miner = take_weak!(self.miner); + let accounts = take_weak!(self.accounts); + let request = dispatch::fill_optional_fields(request.into(), &*client, &*miner); sign_and_dispatch( - &*take_weak!(self.client), - &*take_weak!(self.miner), - &*take_weak!(self.accounts), - request.into(), + &*client, + &*miner, + &*accounts, + request, Some(password) - ) + ).map(Into::into) } } diff --git a/rpc/src/v1/impls/signer.rs b/rpc/src/v1/impls/signer.rs index 61453e02b..0ee06b5c5 100644 --- a/rpc/src/v1/impls/signer.rs +++ b/rpc/src/v1/impls/signer.rs @@ -17,14 +17,15 @@ //! Transactions Confirmations rpc implementation use std::sync::{Arc, Weak}; + use jsonrpc_core::*; use ethcore::account_provider::AccountProvider; use ethcore::client::MiningBlockChainClient; use ethcore::miner::MinerService; use v1::traits::Signer; -use v1::types::{TransactionModification, ConfirmationRequest, U256}; +use v1::types::{TransactionModification, ConfirmationRequest, ConfirmationResponse, U256}; use v1::helpers::{errors, SignerService, SigningQueue, ConfirmationPayload}; -use v1::helpers::dispatch::{sign_and_dispatch, sign, decrypt}; +use v1::helpers::dispatch; /// Transactions confirmation (personal) rpc implementation. pub struct SignerClient where C: MiningBlockChainClient, M: MinerService { @@ -73,7 +74,7 @@ impl Signer for SignerClient where C: MiningBlockC // TODO [ToDr] TransactionModification is redundant for some calls // might be better to replace it in future - fn confirm_request(&self, id: U256, modification: TransactionModification, pass: String) -> Result { + fn confirm_request(&self, id: U256, modification: TransactionModification, pass: String) -> Result { try!(self.active()); let id = id.into(); @@ -83,21 +84,16 @@ impl Signer for SignerClient where C: MiningBlockC let miner = take_weak!(self.miner); signer.peek(&id).map(|confirmation| { - let result = match confirmation.payload { - ConfirmationPayload::Transaction(mut request) => { - // apply modification - if let Some(gas_price) = modification.gas_price { - request.gas_price = gas_price.into(); - } - sign_and_dispatch(&*client, &*miner, &*accounts, request.into(), Some(pass)).map(to_value) + let mut payload = confirmation.payload.clone(); + // Modify payload + match (&mut payload, modification.gas_price) { + (&mut ConfirmationPayload::SendTransaction(ref mut request), Some(gas_price)) => { + request.gas_price = gas_price.into(); }, - ConfirmationPayload::Sign(address, hash) => { - sign(&*accounts, address, Some(pass), hash) - }, - ConfirmationPayload::Decrypt(address, msg) => { - decrypt(&*accounts, address, Some(pass), msg) - }, - }; + _ => {}, + } + // Execute + let result = dispatch::execute(&*client, &*miner, &*accounts, payload, Some(pass)); if let Ok(ref response) = result { signer.request_confirmed(id, Ok(response.clone())); } diff --git a/rpc/src/v1/impls/signing.rs b/rpc/src/v1/impls/signing.rs index f54847757..cf20f1045 100644 --- a/rpc/src/v1/impls/signing.rs +++ b/rpc/src/v1/impls/signing.rs @@ -17,24 +17,33 @@ //! Signing RPC implementation. use std::sync::{Arc, Weak}; +use transient_hashmap::TransientHashMap; +use util::{U256, Mutex}; use ethcore::account_provider::AccountProvider; use ethcore::miner::MinerService; use ethcore::client::MiningBlockChainClient; -use transient_hashmap::TransientHashMap; -use util::{U256, Address, H256, Mutex}; -use jsonrpc_core::*; -use v1::helpers::{errors, SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationPayload, TransactionRequest as TRequest, FilledTransactionRequest as FilledRequest, SignerService}; -use v1::helpers::dispatch::{default_gas_price, sign_and_dispatch, sign, decrypt}; +use jsonrpc_core::Error; +use v1::helpers::auto_args::Ready; +use v1::helpers::{ + errors, dispatch, + SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationPayload, SignerService +}; use v1::traits::{EthSigning, ParitySigning}; -use v1::types::{TransactionRequest, H160 as RpcH160, H256 as RpcH256, U256 as RpcU256, Bytes as RpcBytes}; +use v1::types::{ + H160 as RpcH160, H256 as RpcH256, U256 as RpcU256, Bytes as RpcBytes, H520 as RpcH520, + Either as RpcEither, + TransactionRequest as RpcTransactionRequest, + ConfirmationPayload as RpcConfirmationPayload, + ConfirmationResponse as RpcConfirmationResponse +}; const MAX_PENDING_DURATION: u64 = 60 * 60; pub enum DispatchResult { Promise(ConfirmationPromise), - Value(Value), + Value(RpcConfirmationResponse), } /// Implementation of functions that require signing when no trusted signer is used. @@ -68,59 +77,41 @@ impl SigningQueueClient where Ok(()) } - fn add_to_queue(&self, sender: Address, when_unlocked: WhenUnlocked, payload: Payload) - -> Result where - WhenUnlocked: Fn(&AccountProvider) -> Result, - Payload: Fn() -> ConfirmationPayload, { + fn handle_dispatch(&self, res: Result, on_response: OnResponse) + where OnResponse: FnOnce(Result) + Send + 'static + { + match res { + Ok(DispatchResult::Value(result)) => on_response(Ok(result)), + Ok(DispatchResult::Promise(promise)) => { + promise.wait_for_result(move |result| { + on_response(result.unwrap_or_else(|| Err(errors::request_rejected()))) + }) + }, + Err(e) => on_response(Err(e)), + } + } + fn add_to_queue(&self, payload: ConfirmationPayload) -> Result { + let client = take_weak!(self.client); + let miner = take_weak!(self.miner); let accounts = take_weak!(self.accounts); + + let sender = payload.sender(); if accounts.is_unlocked(sender) { - return when_unlocked(&accounts).map(DispatchResult::Value); + return dispatch::execute(&*client, &*miner, &*accounts, payload, None).map(DispatchResult::Value); } - take_weak!(self.signer).add_request(payload()) + take_weak!(self.signer).add_request(payload) .map(DispatchResult::Promise) .map_err(|_| errors::request_rejected_limit()) } - fn handle_dispatch(&self, res: Result, ready: Ready) { - match res { - Ok(DispatchResult::Value(v)) => ready.ready(Ok(v)), - Ok(DispatchResult::Promise(promise)) => { - promise.wait_for_result(move |result| { - ready.ready(result.unwrap_or_else(|| Err(errors::request_rejected()))) - }) - }, - Err(e) => ready.ready(Err(e)), - } - } + fn dispatch(&self, payload: RpcConfirmationPayload) -> Result { + let client = take_weak!(self.client); + let miner = take_weak!(self.miner); - fn dispatch_sign(&self, params: Params) -> Result { - from_params::<(RpcH160, RpcH256)>(params).and_then(|(address, msg)| { - let address: Address = address.into(); - let msg: H256 = msg.into(); - - self.add_to_queue( - address, - |accounts| sign(accounts, address, None, msg.clone()), - || ConfirmationPayload::Sign(address, msg.clone()), - ) - }) - } - - fn dispatch_transaction(&self, params: Params) -> Result { - from_params::<(TransactionRequest, )>(params).and_then(|(request, )| { - let request: TRequest = request.into(); - let (client, miner) = (take_weak!(self.client), take_weak!(self.miner)); - self.add_to_queue( - request.from, - |accounts| sign_and_dispatch(&*client, &*miner, accounts, request.clone(), None).map(to_value), - || { - let request = fill_optional_fields(request.clone(), &*client, &*miner); - ConfirmationPayload::Transaction(request) - } - ) - }) + let payload = dispatch::from_rpc(payload, &*client, &*miner); + self.add_to_queue(payload) } } @@ -128,62 +119,59 @@ impl ParitySigning for SigningQueueClient where C: MiningBlockChainClient, M: MinerService, { - fn post_sign(&self, params: Params) -> Result { + fn post_sign(&self, address: RpcH160, hash: RpcH256) -> Result, Error> { try!(self.active()); - self.dispatch_sign(params).map(|result| match result { - DispatchResult::Value(v) => v, - DispatchResult::Promise(promise) => { - let id = promise.id(); - self.pending.lock().insert(id, promise); - to_value(&RpcU256::from(id)) - }, - }) + self.dispatch(RpcConfirmationPayload::Signature((address, hash).into())) + .map(|result| match result { + DispatchResult::Value(v) => RpcEither::Or(v), + DispatchResult::Promise(promise) => { + let id = promise.id(); + self.pending.lock().insert(id, promise); + RpcEither::Either(id.into()) + }, + }) } - fn post_transaction(&self, params: Params) -> Result { + fn post_transaction(&self, request: RpcTransactionRequest) -> Result, Error> { try!(self.active()); - self.dispatch_transaction(params).map(|result| match result { - DispatchResult::Value(v) => v, - DispatchResult::Promise(promise) => { - let id = promise.id(); - self.pending.lock().insert(id, promise); - to_value(&RpcU256::from(id)) - }, - }) + self.dispatch(RpcConfirmationPayload::SendTransaction(request)) + .map(|result| match result { + DispatchResult::Value(v) => RpcEither::Or(v), + DispatchResult::Promise(promise) => { + let id = promise.id(); + self.pending.lock().insert(id, promise); + RpcEither::Either(id.into()) + }, + }) } - fn check_request(&self, params: Params) -> Result { + fn check_request(&self, id: RpcU256) -> Result, Error> { try!(self.active()); let mut pending = self.pending.lock(); - from_params::<(RpcU256, )>(params).and_then(|(id, )| { - let id: U256 = id.into(); - let res = match pending.get(&id) { - Some(ref promise) => match promise.result() { - ConfirmationResult::Waiting => { return Ok(Value::Null); } - ConfirmationResult::Rejected => Err(errors::request_rejected()), - ConfirmationResult::Confirmed(rpc_response) => rpc_response, - }, - _ => { return Err(errors::request_not_found()); } - }; - pending.remove(&id); - res - }) + let id: U256 = id.into(); + let res = match pending.get(&id) { + Some(ref promise) => match promise.result() { + ConfirmationResult::Waiting => { return Ok(None); } + ConfirmationResult::Rejected => Err(errors::request_rejected()), + ConfirmationResult::Confirmed(rpc_response) => rpc_response.map(Some), + }, + _ => { return Err(errors::request_not_found()); } + }; + pending.remove(&id); + res } - fn decrypt_message(&self, params: Params, ready: Ready) { + fn decrypt_message(&self, ready: Ready, address: RpcH160, data: RpcBytes) { let res = self.active() - .and_then(|_| from_params::<(RpcH160, RpcBytes)>(params)) - .and_then(|(address, msg)| { - let address: Address = address.into(); - - self.add_to_queue( - address, - |accounts| decrypt(accounts, address, None, msg.clone().into()), - || ConfirmationPayload::Decrypt(address, msg.clone().into()) - ) - }); - - self.handle_dispatch(res, ready); + .and_then(|_| self.dispatch(RpcConfirmationPayload::Decrypt((address, data).into()))); + // TODO [todr] typed handle_dispatch + self.handle_dispatch(res, |response| { + match response { + Ok(RpcConfirmationResponse::Decrypt(data)) => ready.ready(Ok(data)), + Err(e) => ready.ready(Err(e)), + e => ready.ready(Err(errors::internal("Unexpected result.", e))), + } + }); } } @@ -191,26 +179,36 @@ impl EthSigning for SigningQueueClient where C: MiningBlockChainClient, M: MinerService, { - fn sign(&self, params: Params, ready: Ready) { - let res = self.active().and_then(|_| self.dispatch_sign(params)); - self.handle_dispatch(res, ready); + fn sign(&self, ready: Ready, address: RpcH160, hash: RpcH256) { + let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::Signature((address, hash).into()))); + self.handle_dispatch(res, |response| { + match response { + Ok(RpcConfirmationResponse::Signature(signature)) => ready.ready(Ok(signature)), + Err(e) => ready.ready(Err(e)), + e => ready.ready(Err(errors::internal("Unexpected result.", e))), + } + }); } - fn send_transaction(&self, params: Params, ready: Ready) { - let res = self.active().and_then(|_| self.dispatch_transaction(params)); - self.handle_dispatch(res, ready); + fn send_transaction(&self, ready: Ready, request: RpcTransactionRequest) { + let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::SendTransaction(request))); + self.handle_dispatch(res, |response| { + match response { + Ok(RpcConfirmationResponse::SendTransaction(hash)) => ready.ready(Ok(hash)), + Err(e) => ready.ready(Err(e)), + e => ready.ready(Err(errors::internal("Unexpected result.", e))), + } + }); } -} -fn fill_optional_fields(request: TRequest, client: &C, miner: &M) -> FilledRequest - where C: MiningBlockChainClient, M: MinerService { - FilledRequest { - from: request.from, - to: request.to, - nonce: request.nonce, - gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(client, miner)), - gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()), - value: request.value.unwrap_or_else(|| 0.into()), - data: request.data.unwrap_or_else(Vec::new), + fn sign_transaction(&self, ready: Ready, request: RpcTransactionRequest) { + let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::SignTransaction(request))); + self.handle_dispatch(res, |response| { + match response { + Ok(RpcConfirmationResponse::SignTransaction(rlp)) => ready.ready(Ok(rlp)), + Err(e) => ready.ready(Err(e)), + e => ready.ready(Err(errors::internal("Unexpected result.", e))), + } + }); } } diff --git a/rpc/src/v1/impls/signing_unsafe.rs b/rpc/src/v1/impls/signing_unsafe.rs index 251ce9329..da9aeb901 100644 --- a/rpc/src/v1/impls/signing_unsafe.rs +++ b/rpc/src/v1/impls/signing_unsafe.rs @@ -22,11 +22,19 @@ use ethcore::account_provider::AccountProvider; use ethcore::miner::MinerService; use ethcore::client::MiningBlockChainClient; -use jsonrpc_core::*; +use jsonrpc_core::Error; +use v1::helpers::auto_args::Ready; use v1::helpers::errors; -use v1::helpers::dispatch::{sign_and_dispatch, sign, decrypt}; +use v1::helpers::dispatch; use v1::traits::{EthSigning, ParitySigning}; -use v1::types::{TransactionRequest, H160 as RpcH160, H256 as RpcH256, Bytes as RpcBytes}; +use v1::types::{ + U256 as RpcU256, + H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes, + Either as RpcEither, + TransactionRequest as RpcTransactionRequest, + ConfirmationPayload as RpcConfirmationPayload, + ConfirmationResponse as RpcConfirmationResponse, +}; /// Implementation of functions that require signing when no trusted signer is used. pub struct SigningUnsafeClient where @@ -58,26 +66,47 @@ impl SigningUnsafeClient where take_weak!(self.client).keep_alive(); Ok(()) } + + fn handle(&self, payload: RpcConfirmationPayload) -> Result { + try!(self.active()); + let client = take_weak!(self.client); + let miner = take_weak!(self.miner); + let accounts = take_weak!(self.accounts); + + let payload = dispatch::from_rpc(payload, &*client, &*miner); + dispatch::execute(&*client, &*miner, &*accounts, payload, None) + } } impl EthSigning for SigningUnsafeClient where C: MiningBlockChainClient, M: MinerService, { - fn sign(&self, params: Params, ready: Ready) { - ready.ready(self.active() - .and_then(|_| from_params::<(RpcH160, RpcH256)>(params)) - .and_then(|(address, msg)| { - sign(&*take_weak!(self.accounts), address.into(), None, msg.into()) - })) + fn sign(&self, ready: Ready, address: RpcH160, hash: RpcH256) { + let result = match self.handle(RpcConfirmationPayload::Signature((address, hash).into())) { + Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature), + Err(e) => Err(e), + e => Err(errors::internal("Unexpected result", e)), + }; + ready.ready(result); } - fn send_transaction(&self, params: Params, ready: Ready) { - ready.ready(self.active() - .and_then(|_| from_params::<(TransactionRequest, )>(params)) - .and_then(|(request, )| { - sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), &*take_weak!(self.accounts), request.into(), None).map(to_value) - })) + fn send_transaction(&self, ready: Ready, request: RpcTransactionRequest) { + let result = match self.handle(RpcConfirmationPayload::SendTransaction(request)) { + Ok(RpcConfirmationResponse::SendTransaction(hash)) => Ok(hash), + Err(e) => Err(e), + e => Err(errors::internal("Unexpected result", e)), + }; + ready.ready(result); + } + + fn sign_transaction(&self, ready: Ready, request: RpcTransactionRequest) { + let result = match self.handle(RpcConfirmationPayload::SignTransaction(request)) { + Ok(RpcConfirmationResponse::SignTransaction(rlp)) => Ok(rlp), + Err(e) => Err(e), + e => Err(errors::internal("Unexpected result", e)), + }; + ready.ready(result); } } @@ -85,25 +114,26 @@ impl ParitySigning for SigningUnsafeClient where C: MiningBlockChainClient, M: MinerService, { - fn decrypt_message(&self, params: Params, ready: Ready) { - ready.ready(self.active() - .and_then(|_| from_params::<(RpcH160, RpcBytes)>(params)) - .and_then(|(address, ciphertext)| { - decrypt(&*take_weak!(self.accounts), address.into(), None, ciphertext.0) - })) + fn decrypt_message(&self, ready: Ready, address: RpcH160, data: RpcBytes) { + let result = match self.handle(RpcConfirmationPayload::Decrypt((address, data).into())) { + Ok(RpcConfirmationResponse::Decrypt(data)) => Ok(data), + Err(e) => Err(e), + e => Err(errors::internal("Unexpected result", e)), + }; + ready.ready(result); } - fn post_sign(&self, _: Params) -> Result { + fn post_sign(&self, _: RpcH160, _: RpcH256) -> Result, Error> { // We don't support this in non-signer mode. Err(errors::signer_disabled()) } - fn post_transaction(&self, _: Params) -> Result { + fn post_transaction(&self, _: RpcTransactionRequest) -> Result, Error> { // We don't support this in non-signer mode. Err(errors::signer_disabled()) } - fn check_request(&self, _: Params) -> Result { + fn check_request(&self, _: RpcU256) -> Result, Error> { // We don't support this in non-signer mode. Err(errors::signer_disabled()) } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 9f654e7e0..7119de2c1 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -18,6 +18,7 @@ use std::str::FromStr; use std::collections::HashMap; use std::sync::Arc; use std::time::{Instant, Duration}; +use rlp; use jsonrpc_core::IoHandler; use util::{Uint, U256, Address, H256, FixedHash, Mutex}; use ethcore::account_provider::AccountProvider; @@ -761,6 +762,44 @@ fn rpc_eth_send_transaction() { assert_eq!(tester.io.handle_request_sync(&request), Some(response)); } + +#[test] +fn rpc_eth_sign_transaction() { + let tester = EthTester::default(); + let address = tester.accounts_provider.new_account("").unwrap(); + tester.accounts_provider.unlock_account_permanently(address, "".into()).unwrap(); + let request = r#"{ + "jsonrpc": "2.0", + "method": "eth_signTransaction", + "params": [{ + "from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a" + }], + "id": 1 + }"#; + + let t = Transaction { + nonce: U256::one(), + gas_price: U256::from(0x9184e72a000u64), + gas: U256::from(0x76c0), + action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + value: U256::from(0x9184e72au64), + data: vec![] + }; + let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap(); + let t = t.with_signature(signature, None); + let rlp = rlp::encode(&t); + + let response = r#"{"jsonrpc":"2.0","result":"0x"#.to_owned() + &rlp.to_hex() + r#"","id":1}"#; + + tester.miner.last_nonces.write().insert(address.clone(), U256::zero()); + + assert_eq!(tester.io.handle_request_sync(&request), Some(response)); +} + #[test] fn rpc_eth_send_transaction_with_bad_to() { let tester = EthTester::default(); @@ -839,7 +878,7 @@ fn rpc_eth_send_raw_transaction() { let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap(); let t = t.with_signature(signature, None); - let rlp = ::rlp::encode(&t).to_vec().to_hex(); + let rlp = rlp::encode(&t).to_vec().to_hex(); let req = r#"{ "jsonrpc": "2.0", diff --git a/rpc/src/v1/tests/mocked/signer.rs b/rpc/src/v1/tests/mocked/signer.rs index 92e20676f..8807e2373 100644 --- a/rpc/src/v1/tests/mocked/signer.rs +++ b/rpc/src/v1/tests/mocked/signer.rs @@ -71,7 +71,7 @@ fn signer_tester() -> SignerTester { fn should_return_list_of_items_to_confirm() { // given let tester = signer_tester(); - tester.signer.add_request(ConfirmationPayload::Transaction(FilledTransactionRequest { + tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { from: Address::from(1), to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), gas_price: U256::from(10_000), @@ -80,7 +80,7 @@ fn should_return_list_of_items_to_confirm() { data: vec![], nonce: None, })).unwrap(); - tester.signer.add_request(ConfirmationPayload::Sign(1.into(), 5.into())).unwrap(); + tester.signer.add_request(ConfirmationPayload::Signature(1.into(), 5.into())).unwrap(); // when let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#; @@ -100,7 +100,7 @@ fn should_return_list_of_items_to_confirm() { fn should_reject_transaction_from_queue_without_dispatching() { // given let tester = signer_tester(); - tester.signer.add_request(ConfirmationPayload::Transaction(FilledTransactionRequest { + tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { from: Address::from(1), to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), gas_price: U256::from(10_000), @@ -125,7 +125,7 @@ fn should_reject_transaction_from_queue_without_dispatching() { fn should_not_remove_transaction_if_password_is_invalid() { // given let tester = signer_tester(); - tester.signer.add_request(ConfirmationPayload::Transaction(FilledTransactionRequest { + tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { from: Address::from(1), to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), gas_price: U256::from(10_000), @@ -149,7 +149,7 @@ fn should_not_remove_transaction_if_password_is_invalid() { fn should_not_remove_sign_if_password_is_invalid() { // given let tester = signer_tester(); - tester.signer.add_request(ConfirmationPayload::Sign(0.into(), 5.into())).unwrap(); + tester.signer.add_request(ConfirmationPayload::Signature(0.into(), 5.into())).unwrap(); assert_eq!(tester.signer.requests().len(), 1); // when @@ -167,7 +167,7 @@ fn should_confirm_transaction_and_dispatch() { let tester = signer_tester(); let address = tester.accounts.new_account("test").unwrap(); let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); - tester.signer.add_request(ConfirmationPayload::Transaction(FilledTransactionRequest { + tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { from: address, to: Some(recipient), gas_price: U256::from(10_000), diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index fc460409c..b43c1e180 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -16,15 +16,17 @@ use std::str::FromStr; use std::sync::Arc; -use jsonrpc_core::{IoHandler, to_value, Success}; +use rlp; + +use jsonrpc_core::{IoHandler, Success}; use v1::impls::SigningQueueClient; use v1::traits::{EthSigning, ParitySigning, Parity}; use v1::helpers::{SignerService, SigningQueue}; -use v1::types::{H256 as RpcH256, H520 as RpcH520, Bytes}; +use v1::types::ConfirmationResponse; use v1::tests::helpers::TestMinerService; use v1::tests::mocked::parity; -use util::{Address, FixedHash, Uint, U256, H256, H520}; +use util::{Address, FixedHash, Uint, U256, H256, ToPretty}; use ethcore::account_provider::AccountProvider; use ethcore::client::TestBlockChainClient; use ethcore::transaction::{Transaction, Action}; @@ -88,7 +90,7 @@ fn should_add_sign_to_queue() { let async_result = tester.io.handle_request(&request).unwrap(); assert_eq!(tester.signer.requests().len(), 1); // respond - tester.signer.request_confirmed(U256::from(1), Ok(to_value(&RpcH520::from(H520::default())))); + tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::Signature(0.into()))); assert!(async_result.on_result(move |res| { assert_eq!(res, response.to_owned()); })); @@ -162,7 +164,7 @@ fn should_check_status_of_request_when_its_resolved() { "id": 1 }"#; tester.io.handle_request_sync(&request).expect("Sent"); - tester.signer.request_confirmed(U256::from(1), Ok(to_value(&"Hello World!"))); + tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::Signature(1.into()))); // when let request = r#"{ @@ -171,7 +173,7 @@ fn should_check_status_of_request_when_its_resolved() { "params": ["0x1"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"Hello World!","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","id":1}"#; // then assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); @@ -228,7 +230,54 @@ fn should_add_transaction_to_queue() { let async_result = tester.io.handle_request(&request).unwrap(); assert_eq!(tester.signer.requests().len(), 1); // respond - tester.signer.request_confirmed(U256::from(1), Ok(to_value(&RpcH256::from(H256::default())))); + tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::SendTransaction(0.into()))); + assert!(async_result.on_result(move |res| { + assert_eq!(res, response.to_owned()); + })); +} + +#[test] +fn should_add_sign_transaction_to_the_queue() { + // given + let tester = eth_signing(); + let address = tester.accounts.new_account("test").unwrap(); + + assert_eq!(tester.signer.requests().len(), 0); + + // when + let request = r#"{ + "jsonrpc": "2.0", + "method": "eth_signTransaction", + "params": [{ + "from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a" + }], + "id": 1 + }"#; + + let t = Transaction { + nonce: U256::one(), + gas_price: U256::from(0x9184e72a000u64), + gas: U256::from(0x76c0), + action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + value: U256::from(0x9184e72au64), + data: vec![] + }; + let signature = tester.accounts.sign(address, Some("test".into()), t.hash(None)).unwrap(); + let t = t.with_signature(signature, None); + let rlp = rlp::encode(&t); + + let response = r#"{"jsonrpc":"2.0","result":"0x"#.to_owned() + &rlp.to_hex() + r#"","id":1}"#; + + // then + tester.miner.last_nonces.write().insert(address.clone(), U256::zero()); + let async_result = tester.io.handle_request(&request).unwrap(); + assert_eq!(tester.signer.requests().len(), 1); + // respond + tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::SignTransaction(rlp.to_vec().into()))); assert!(async_result.on_result(move |res| { assert_eq!(res, response.to_owned()); })); @@ -325,7 +374,7 @@ fn should_add_decryption_to_the_queue() { let async_result = tester.io.handle_request(&request).unwrap(); assert_eq!(tester.signer.requests().len(), 1); // respond - tester.signer.request_confirmed(U256::from(1), Ok(to_value(Bytes(vec![0x1, 0x2])))); + tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::Decrypt(vec![0x1, 0x2].into()))); assert!(async_result.on_result(move |res| { assert_eq!(res, response.to_owned()); })); diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index 49f433ffd..b524c616f 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . //! Eth rpc interface. -use jsonrpc_core::*; +use jsonrpc_core::Error; use v1::types::{RichBlock, BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index}; use v1::types::{Log, Receipt, SyncStatus, Transaction, Work}; diff --git a/rpc/src/v1/traits/eth_signing.rs b/rpc/src/v1/traits/eth_signing.rs index 80979a7db..bd1d1ecdc 100644 --- a/rpc/src/v1/traits/eth_signing.rs +++ b/rpc/src/v1/traits/eth_signing.rs @@ -15,26 +15,27 @@ // along with Parity. If not, see . //! Eth rpc interface. -use std::sync::Arc; -use jsonrpc_core::*; -/// Signing methods implementation relying on unlocked accounts. -pub trait EthSigning: Sized + Send + Sync + 'static { - /// Signs the data with given address signature. - fn sign(&self, _: Params, _: Ready); +use v1::helpers::auto_args::{WrapAsync, Ready}; +use v1::types::{H160, H256, H520, TransactionRequest, Bytes}; - /// Sends transaction; will block for 20s to try to return the - /// transaction hash. - /// If it cannot yet be signed, it will return a transaction ID for - /// later use with check_transaction. - fn send_transaction(&self, _: Params, _: Ready); +build_rpc_trait! { + /// Signing methods implementation relying on unlocked accounts. + pub trait EthSigning { + /// Signs the data with given address signature. + #[rpc(async, name = "eth_sign")] + fn sign(&self, Ready, H160, H256); - /// Should be used to convert object to io delegate. - fn to_delegate(self) -> IoDelegate { - let mut delegate = IoDelegate::new(Arc::new(self)); - delegate.add_async_method("eth_sign", EthSigning::sign); - delegate.add_async_method("eth_sendTransaction", EthSigning::send_transaction); + /// Sends transaction; will block waiting for signer to return the + /// transaction hash. + /// If Signer is disable it will require the account to be unlocked. + #[rpc(async, name = "eth_sendTransaction")] + fn send_transaction(&self, Ready, TransactionRequest); - delegate + /// Signs transactions without dispatching it to the network. + /// Returns signed transaction RLP representation. + /// It can be later submitted using `eth_sendRawTransaction`. + #[rpc(async, name = "eth_signTransaction")] + fn sign_transaction(&self, Ready, TransactionRequest); } } diff --git a/rpc/src/v1/traits/parity_signing.rs b/rpc/src/v1/traits/parity_signing.rs index 1c2778b31..d97b9882b 100644 --- a/rpc/src/v1/traits/parity_signing.rs +++ b/rpc/src/v1/traits/parity_signing.rs @@ -15,38 +15,32 @@ // along with Parity. If not, see . //! ParitySigning rpc interface. -use std::sync::Arc; -use jsonrpc_core::*; +use jsonrpc_core::Error; -/// Signing methods implementation relying on unlocked accounts. -pub trait ParitySigning: Sized + Send + Sync + 'static { - /// Posts sign request asynchronously. - /// Will return a confirmation ID for later use with check_transaction. - fn post_sign(&self, _: Params) -> Result; +use v1::helpers::auto_args::{Wrap, WrapAsync, Ready}; +use v1::types::{U256, H160, H256, Bytes, ConfirmationResponse, TransactionRequest, Either}; - /// Posts transaction asynchronously. - /// Will return a transaction ID for later use with check_transaction. - fn post_transaction(&self, _: Params) -> Result; +build_rpc_trait! { + /// Signing methods implementation. + pub trait ParitySigning { + /// Posts sign request asynchronously. + /// Will return a confirmation ID for later use with check_transaction. + #[rpc(name = "parity_postSign")] + fn post_sign(&self, H160, H256) -> Result, Error>; - /// Checks the progress of a previously posted request (transaction/sign). - /// Should be given a valid send_transaction ID. - /// Returns the transaction hash, the zero hash (not yet available), - /// or the signature, - /// or an error. - fn check_request(&self, _: Params) -> Result; + /// Posts transaction asynchronously. + /// Will return a transaction ID for later use with check_transaction. + #[rpc(name = "parity_postTransaction")] + fn post_transaction(&self, TransactionRequest) -> Result, Error>; - /// Decrypt some ECIES-encrypted message. - /// First parameter is the address with which it is encrypted, second is the ciphertext. - fn decrypt_message(&self, _: Params, _: Ready); + /// Checks the progress of a previously posted request (transaction/sign). + /// Should be given a valid send_transaction ID. + #[rpc(name = "parity_checkRequest")] + fn check_request(&self, U256) -> 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("parity_postSign", ParitySigning::post_sign); - delegate.add_method("parity_postTransaction", ParitySigning::post_transaction); - delegate.add_method("parity_checkRequest", ParitySigning::check_request); - delegate.add_async_method("parity_decryptMessage", ParitySigning::decrypt_message); - - delegate + /// Decrypt some ECIES-encrypted message. + /// First parameter is the address with which it is encrypted, second is the ciphertext. + #[rpc(async, name = "parity_decryptMessage")] + fn decrypt_message(&self, Ready, H160, Bytes); } } diff --git a/rpc/src/v1/traits/signer.rs b/rpc/src/v1/traits/signer.rs index d80c6f1a6..26d6899cb 100644 --- a/rpc/src/v1/traits/signer.rs +++ b/rpc/src/v1/traits/signer.rs @@ -15,10 +15,10 @@ // along with Parity. If not, see . //! Parity Signer-related rpc interface. -use jsonrpc_core::{Value, Error}; +use jsonrpc_core::Error; use v1::helpers::auto_args::Wrap; -use v1::types::{U256, TransactionModification, ConfirmationRequest}; +use v1::types::{U256, TransactionModification, ConfirmationRequest, ConfirmationResponse}; build_rpc_trait! { @@ -31,7 +31,7 @@ build_rpc_trait! { /// Confirm specific request. #[rpc(name = "signer_confirmRequest")] - fn confirm_request(&self, U256, TransactionModification, String) -> Result; + fn confirm_request(&self, U256, TransactionModification, String) -> Result; /// Reject the confirmation request. #[rpc(name = "signer_rejectRequest")] diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index c5ef4efa9..e83989326 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -16,10 +16,11 @@ //! Types used in Confirmations queue (Trusted Signer) -use v1::types::{U256, TransactionRequest, H160, H256, Bytes}; +use std::fmt; +use serde::{Serialize, Serializer}; +use v1::types::{U256, TransactionRequest, H160, H256, H520, Bytes}; use v1::helpers; - /// Confirmation waiting in a queue #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)] pub struct ConfirmationRequest { @@ -47,6 +48,15 @@ pub struct SignRequest { pub hash: H256, } +impl From<(H160, H256)> for SignRequest { + fn from(tuple: (H160, H256)) -> Self { + SignRequest { + address: tuple.0, + hash: tuple.1, + } + } +} + /// Decrypt request #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)] pub struct DecryptRequest { @@ -56,15 +66,53 @@ pub struct DecryptRequest { pub msg: Bytes, } +impl From<(H160, Bytes)> for DecryptRequest { + fn from(tuple: (H160, Bytes)) -> Self { + DecryptRequest { + address: tuple.0, + msg: tuple.1, + } + } +} + +/// Confirmation response for particular payload +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub enum ConfirmationResponse { + /// Transaction Hash + SendTransaction(H256), + /// Transaction RLP + SignTransaction(Bytes), + /// Signature + Signature(H520), + /// Decrypted data + Decrypt(Bytes), +} + +impl Serialize for ConfirmationResponse { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: Serializer + { + match *self { + ConfirmationResponse::SendTransaction(ref hash) => hash.serialize(serializer), + ConfirmationResponse::SignTransaction(ref rlp) => rlp.serialize(serializer), + ConfirmationResponse::Signature(ref signature) => signature.serialize(serializer), + ConfirmationResponse::Decrypt(ref data) => data.serialize(serializer), + } + } +} + /// Confirmation payload, i.e. the thing to be confirmed #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)] pub enum ConfirmationPayload { - /// Transaction + /// Send Transaction #[serde(rename="transaction")] - Transaction(TransactionRequest), + SendTransaction(TransactionRequest), + /// Sign Transaction + #[serde(rename="transaction")] + SignTransaction(TransactionRequest), /// Signature #[serde(rename="sign")] - Sign(SignRequest), + Signature(SignRequest), /// Decryption #[serde(rename="decrypt")] Decrypt(DecryptRequest), @@ -73,8 +121,9 @@ pub enum ConfirmationPayload { impl From for ConfirmationPayload { fn from(c: helpers::ConfirmationPayload) -> Self { match c { - helpers::ConfirmationPayload::Transaction(t) => ConfirmationPayload::Transaction(t.into()), - helpers::ConfirmationPayload::Sign(address, hash) => ConfirmationPayload::Sign(SignRequest { + helpers::ConfirmationPayload::SendTransaction(t) => ConfirmationPayload::SendTransaction(t.into()), + helpers::ConfirmationPayload::SignTransaction(t) => ConfirmationPayload::SignTransaction(t.into()), + helpers::ConfirmationPayload::Signature(address, hash) => ConfirmationPayload::Signature(SignRequest { address: address.into(), hash: hash.into(), }), @@ -94,6 +143,41 @@ pub struct TransactionModification { pub gas_price: Option, } +/// Represents two possible return values. +#[derive(Debug, Clone)] +pub enum Either where + A: fmt::Debug + Clone, + B: fmt::Debug + Clone, +{ + /// Primary value + Either(A), + /// Secondary value + Or(B), +} + +impl From for Either where + A: fmt::Debug + Clone, + B: fmt::Debug + Clone, +{ + fn from(a: A) -> Self { + Either::Either(a) + } +} + +impl Serialize for Either where + A: Serialize + fmt::Debug + Clone, + B: Serialize + fmt::Debug + Clone, +{ + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: Serializer + { + match *self { + Either::Either(ref a) => a.serialize(serializer), + Either::Or(ref b) => b.serialize(serializer), + } + } +} + #[cfg(test)] mod tests { use std::str::FromStr; @@ -107,7 +191,7 @@ mod tests { // given let request = helpers::ConfirmationRequest { id: 15.into(), - payload: helpers::ConfirmationPayload::Sign(1.into(), 5.into()), + payload: helpers::ConfirmationPayload::Signature(1.into(), 5.into()), }; // when @@ -123,7 +207,7 @@ mod tests { // given let request = helpers::ConfirmationRequest { id: 15.into(), - payload: helpers::ConfirmationPayload::Transaction(helpers::FilledTransactionRequest { + payload: helpers::ConfirmationPayload::SendTransaction(helpers::FilledTransactionRequest { from: 0.into(), to: None, gas: 15_000.into(), diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index cba7487fc..b1e4ae2c9 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -38,7 +38,7 @@ pub use self::bytes::Bytes; pub use self::block::{RichBlock, Block, BlockTransactions}; pub use self::block_number::BlockNumber; pub use self::call_request::CallRequest; -pub use self::confirmations::{ConfirmationPayload, ConfirmationRequest, TransactionModification}; +pub use self::confirmations::{ConfirmationPayload, ConfirmationRequest, ConfirmationResponse, TransactionModification, SignRequest, DecryptRequest, Either}; pub use self::filter::{Filter, FilterChanges}; pub use self::hash::{H64, H160, H256, H512, H520, H2048}; pub use self::index::Index;