diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index bb117629b..3e25605d4 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -472,6 +472,12 @@ impl PendingTransaction { } } +impl Deref for PendingTransaction { + type Target = SignedTransaction; + + fn deref(&self) -> &SignedTransaction { &self.transaction } +} + impl From for PendingTransaction { fn from(t: SignedTransaction) -> Self { PendingTransaction { diff --git a/rpc/src/v1/helpers/dispatch.rs b/rpc/src/v1/helpers/dispatch.rs index 126b26bc2..e2bb0b80f 100644 --- a/rpc/src/v1/helpers/dispatch.rs +++ b/rpc/src/v1/helpers/dispatch.rs @@ -16,6 +16,9 @@ use std::fmt::Debug; use std::ops::Deref; +use std::sync::Weak; + +use futures::{future, Future, BoxFuture}; use rlp; use util::{Address, H520, H256, U256, Uint, Bytes}; use util::bytes::ToPretty; @@ -38,6 +41,118 @@ use v1::types::{ DecryptRequest as RpcDecryptRequest, }; +/// Has the capability to dispatch, sign, and decrypt. +/// +/// Requires a clone implementation, with the implication that it be cheap; +/// usually just bumping a reference count or two. +pub trait Dispatcher: Send + Sync + Clone { + // TODO: when ATC exist, use zero-cost + // type Out: IntoFuture + + /// Fill optional fields of a transaction request, fetching gas price but not nonce. + fn fill_optional_fields(&self, request: TransactionRequest, default_sender: Address) + -> BoxFuture; + + /// Sign the given transaction request without dispatching, fetching appropriate nonce. + fn sign(&self, accounts: &AccountProvider, filled: FilledTransactionRequest, password: SignWith) + -> BoxFuture, Error>; + + /// "Dispatch" a local transaction. + fn dispatch_transaction(&self, signed_transaction: PendingTransaction) -> Result; +} + +/// A dispatcher which uses references to a client and miner in order to sign +/// requests locally. +#[derive(Debug)] +pub struct FullDispatcher { + client: Weak, + miner: Weak, +} + +impl FullDispatcher { + /// Create a `FullDispatcher` from weak references to a client and miner. + pub fn new(client: Weak, miner: Weak) -> Self { + FullDispatcher { + client: client, + miner: miner, + } + } +} + +impl Clone for FullDispatcher { + fn clone(&self) -> Self { + FullDispatcher { + client: self.client.clone(), + miner: self.miner.clone(), + } + } +} + +impl Dispatcher for FullDispatcher { + fn fill_optional_fields(&self, request: TransactionRequest, default_sender: Address) + -> BoxFuture + { + let inner = move || { + let (client, miner) = (take_weak!(self.client), take_weak!(self.miner)); + Ok(FilledTransactionRequest { + from: request.from.unwrap_or(default_sender), + used_default_from: request.from.is_none(), + 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), + condition: request.condition, + }) + }; + future::done(inner()).boxed() + } + + fn sign(&self, accounts: &AccountProvider, filled: FilledTransactionRequest, password: SignWith) + -> BoxFuture, Error> + { + let inner = move || { + let (client, miner) = (take_weak!(self.client), take_weak!(self.miner)); + let network_id = client.signing_network_id(); + let address = filled.from; + let signed_transaction = { + 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 = signature(accounts, address, hash, password)?; + signature.map(|sig| { + SignedTransaction::new(t.with_signature(sig, network_id)) + .expect("Transaction was signed by AccountsProvider; it never produces invalid signatures; qed") + }) + }; + Ok(signed_transaction) + }; + + future::done(inner()).boxed() + } + + fn dispatch_transaction(&self, signed_transaction: PendingTransaction) -> Result { + let hash = signed_transaction.transaction.hash(); + + take_weak!(self.miner).import_own_transaction(take_weak!(self.client), signed_transaction) + .map_err(errors::from_transaction_error) + .map(|_| hash) + } +} + pub const DEFAULT_MAC: [u8; 2] = [0, 0]; type AccountToken = String; @@ -83,6 +198,14 @@ impl WithToken { WithToken::Yes(v, _) => v, } } + + /// Convert the `WithToken` into a tuple. + pub fn into_tuple(self) -> (T, Option) { + match self { + WithToken::No(v) => (v, None), + WithToken::Yes(v, token) => (v, Some(token)) + } + } } impl From<(T, AccountToken)> for WithToken { @@ -91,26 +214,44 @@ impl From<(T, AccountToken)> for WithToken { } } -pub fn execute(client: &C, miner: &M, accounts: &AccountProvider, payload: ConfirmationPayload, pass: SignWith) -> Result, Error> - where C: MiningBlockChainClient, M: MinerService -{ +impl From<(T, Option)> for WithToken { + fn from(tuple: (T, Option)) -> Self { + match tuple.1 { + Some(token) => WithToken::Yes(tuple.0, token), + None => WithToken::No(tuple.0), + } + } +} + +pub fn execute( + dispatcher: D, + accounts: &AccountProvider, + payload: ConfirmationPayload, + pass: SignWith +) -> BoxFuture, Error> { match payload { ConfirmationPayload::SendTransaction(request) => { - sign_and_dispatch(client, miner, accounts, request, pass) - .map(|result| result - .map(RpcH256::from) - .map(ConfirmationResponse::SendTransaction) - ) + let condition = request.condition.clone(); + dispatcher.sign(accounts, request, pass) + .map(move |v| v.map(move |tx| PendingTransaction::new(tx, condition))) + .map(WithToken::into_tuple) + .map(|(tx, token)| (tx, token, dispatcher)) + .and_then(|(tx, tok, dispatcher)| { + dispatcher.dispatch_transaction(tx) + .map(RpcH256::from) + .map(ConfirmationResponse::SendTransaction) + .map(move |h| WithToken::from((h, tok))) + }).boxed() }, ConfirmationPayload::SignTransaction(request) => { - sign_no_dispatch(client, miner, accounts, request, pass) + dispatcher.sign(accounts, request, pass) .map(|result| result .map(RpcRichRawTransaction::from) .map(ConfirmationResponse::SignTransaction) - ) + ).boxed() }, ConfirmationPayload::Signature(address, data) => { - signature(accounts, address, data.sha3(), pass) + let res = signature(accounts, address, data.sha3(), pass) .map(|result| result .map(|rsv| { let mut vrs = [0u8; 65]; @@ -122,14 +263,16 @@ pub fn execute(client: &C, miner: &M, accounts: &AccountProvider, payload: }) .map(RpcH520::from) .map(ConfirmationResponse::Signature) - ) + ); + future::done(res).boxed() }, ConfirmationPayload::Decrypt(address, data) => { - decrypt(accounts, address, data, pass) + let res = decrypt(accounts, address, data, pass) .map(|result| result .map(RpcBytes) .map(ConfirmationResponse::Decrypt) - ) + ); + future::done(res).boxed() }, } } @@ -156,105 +299,34 @@ fn decrypt(accounts: &AccountProvider, address: Address, msg: Bytes, password: S }) } -pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: PendingTransaction) -> Result - where C: MiningBlockChainClient, M: MinerService { - let hash = signed_transaction.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: SignWith) -> Result, Error> - where C: MiningBlockChainClient, M: MinerService { - - let network_id = client.signing_network_id(); - let address = filled.from; - let signed_transaction = { - 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 = signature(accounts, address, hash, password)?; - signature.map(|sig| { - SignedTransaction::new(t.with_signature(sig, network_id)) - .expect("Transaction was signed by AccountsProvider; it never produces invalid signatures; qed") - }) - }; - Ok(signed_transaction) -} - -pub fn sign_and_dispatch(client: &C, miner: &M, accounts: &AccountProvider, filled: FilledTransactionRequest, password: SignWith) -> Result, Error> - where C: MiningBlockChainClient, M: MinerService -{ - - let network_id = client.signing_network_id(); - let condition = filled.condition.clone(); - let signed_transaction = sign_no_dispatch(client, miner, accounts, filled, password)?; - - let (signed_transaction, token) = match signed_transaction { - WithToken::No(signed_transaction) => (signed_transaction, None), - WithToken::Yes(signed_transaction, token) => (signed_transaction, Some(token)), - }; - - trace!(target: "miner", "send_transaction: dispatching tx: {} for network ID {:?}", rlp::encode(&signed_transaction).to_vec().pretty(), network_id); - let pending_transaction = PendingTransaction::new(signed_transaction, condition.map(Into::into)); - dispatch_transaction(&*client, &*miner, pending_transaction).map(|hash| { - match token { - Some(ref token) => WithToken::Yes(hash, token.clone()), - None => WithToken::No(hash), - } - }) -} - -pub fn fill_optional_fields(request: TransactionRequest, default_sender: Address, client: &C, miner: &M) -> FilledTransactionRequest - where C: MiningBlockChainClient, M: MinerService -{ - FilledTransactionRequest { - from: request.from.unwrap_or(default_sender), - used_default_from: request.from.is_none(), - 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), - condition: request.condition, - } -} - +/// Extract default gas price from a client and miner. 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, default_account: Address, client: &C, miner: &M) -> ConfirmationPayload - where C: MiningBlockChainClient, M: MinerService { - +/// Convert RPC confirmation payload to signer confirmation payload. +/// May need to resolve in the future to fetch things like gas price. +pub fn from_rpc(payload: RpcConfirmationPayload, default_account: Address, dispatcher: &D) -> BoxFuture + where D: Dispatcher +{ match payload { RpcConfirmationPayload::SendTransaction(request) => { - ConfirmationPayload::SendTransaction(fill_optional_fields(request.into(), default_account, client, miner)) + dispatcher.fill_optional_fields(request.into(), default_account) + .map(ConfirmationPayload::SendTransaction) + .boxed() }, RpcConfirmationPayload::SignTransaction(request) => { - ConfirmationPayload::SignTransaction(fill_optional_fields(request.into(), default_account, client, miner)) + dispatcher.fill_optional_fields(request.into(), default_account) + .map(ConfirmationPayload::SignTransaction) + .boxed() }, RpcConfirmationPayload::Decrypt(RpcDecryptRequest { address, msg }) => { - ConfirmationPayload::Decrypt(address.into(), msg.into()) + future::ok(ConfirmationPayload::Decrypt(address.into(), msg.into())).boxed() }, RpcConfirmationPayload::Signature(RpcSignRequest { address, data }) => { - ConfirmationPayload::Signature(address.into(), data.into()) + future::ok(ConfirmationPayload::Signature(address.into(), data.into())).boxed() }, } } diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index 5d74877b0..f5149d721 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -16,15 +16,6 @@ //! Ethereum rpc interface implementation. -macro_rules! take_weak { - ($weak: expr) => { - match $weak.upgrade() { - Some(arc) => arc, - None => return Err(Error::internal_error()) - } - } -} - mod eth; mod eth_filter; mod net; diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index 44e854412..15d876910 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -20,46 +20,37 @@ use std::sync::{Arc, Weak}; use ethcore::account_provider::AccountProvider; use ethcore::client::MiningBlockChainClient; use ethcore::miner::MinerService; -use util::{Address, U128, Uint}; +use ethcore::transaction::PendingTransaction; -use futures::{self, Future, BoxFuture}; +use util::{Address, U128, Uint, ToPretty}; + +use futures::{self, future, Future, BoxFuture}; use jsonrpc_core::Error; use v1::helpers::errors; -use v1::helpers::dispatch::{self, sign_and_dispatch}; +use v1::helpers::dispatch::{Dispatcher, SignWith}; use v1::traits::Personal; use v1::types::{H160 as RpcH160, H256 as RpcH256, U128 as RpcU128, TransactionRequest}; use v1::metadata::Metadata; /// Account management (personal) rpc implementation. -pub struct PersonalClient where - C: MiningBlockChainClient, - M: MinerService, -{ +pub struct PersonalClient { accounts: Weak, - client: Weak, - miner: Weak, + dispatcher: D, allow_perm_unlock: bool, } -impl PersonalClient where - C: MiningBlockChainClient, - M: MinerService, -{ +impl PersonalClient { /// Creates new PersonalClient - pub fn new(store: &Arc, client: &Arc, miner: &Arc, allow_perm_unlock: bool) -> Self { + pub fn new(store: &Arc, dispatcher: D, allow_perm_unlock: bool) -> Self { PersonalClient { accounts: Arc::downgrade(store), - client: Arc::downgrade(client), - miner: Arc::downgrade(miner), + dispatcher: dispatcher, allow_perm_unlock: allow_perm_unlock, } } } -impl Personal for PersonalClient where - C: MiningBlockChainClient + 'static, - M: MinerService + 'static, -{ +impl Personal for PersonalClient { type Metadata = Metadata; fn accounts(&self) -> Result, Error> { @@ -106,28 +97,41 @@ impl Personal for PersonalClient where } fn send_transaction(&self, meta: Metadata, request: TransactionRequest, password: String) -> BoxFuture { - let sign_and_send = move || { - let client = take_weak!(self.client); - let miner = take_weak!(self.miner); + let setup = || { + let dispatcher = self.dispatcher.clone(); let accounts = take_weak!(self.accounts); - let default_account = match request.from { - Some(ref account) => account.clone().into(), - None => accounts - .default_address(meta.dapp_id.unwrap_or_default().into()) - .map_err(|e| errors::account("Cannot find default account.", e))?, - }; - - let request = dispatch::fill_optional_fields(request.into(), default_account, &*client, &*miner); - sign_and_dispatch( - &*client, - &*miner, - &*accounts, - request, - dispatch::SignWith::Password(password) - ).map(|v| v.into_value().into()) + Ok((accounts, dispatcher)) }; - futures::done(sign_and_send()).boxed() + future::done(setup()) + .and_then(move |(accounts, dispatcher)| { + let default = match request.from.as_ref() { + Some(account) => Ok(account.clone().into()), + None => accounts + .default_address(meta.dapp_id.unwrap_or_default().into()) + .map_err(|e| errors::account("Cannot find default account.", e)), + }; + + let dis = dispatcher.clone(); + future::done(default) + .and_then(move |default| dis.fill_optional_fields(request.into(), default)) + .map(move |tx| (tx, accounts, dispatcher)) + }) + .and_then(move |(filled, accounts, dispatcher)| { + let condition = filled.condition.clone().map(Into::into); + dispatcher.sign(&accounts, filled, SignWith::Password(password)) + .map(|tx| tx.into_value()) + .map(move |tx| PendingTransaction::new(tx, condition)) + .map(move |tx| (tx, dispatcher)) + }) + .and_then(move |(pending_tx, dispatcher)| { + let network_id = pending_tx.network_id(); + trace!(target: "miner", "send_transaction: dispatching tx: {} for network ID {:?}", + ::rlp::encode(&*pending_tx).to_vec().pretty(), network_id); + + dispatcher.dispatch_transaction(pending_tx).map(Into::into) + }) + .boxed() } } diff --git a/rpc/src/v1/impls/signing.rs b/rpc/src/v1/impls/signing.rs index 0db90436c..639dfe09b 100644 --- a/rpc/src/v1/impls/signing.rs +++ b/rpc/src/v1/impls/signing.rs @@ -24,13 +24,14 @@ use ethcore::account_provider::AccountProvider; use ethcore::miner::MinerService; use ethcore::client::MiningBlockChainClient; -use futures::{self, BoxFuture, Future}; +use futures::{self, future, BoxFuture, Future}; use jsonrpc_core::Error; use v1::helpers::{ - errors, dispatch, + errors, DefaultAccount, SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationPayload, SignerService }; +use v1::helpers::dispatch::{self, Dispatcher}; use v1::metadata::Metadata; use v1::traits::{EthSigning, ParitySigning}; use v1::types::{ @@ -50,105 +51,97 @@ enum DispatchResult { } /// Implementation of functions that require signing when no trusted signer is used. -pub struct SigningQueueClient where C: MiningBlockChainClient, M: MinerService { +pub struct SigningQueueClient { signer: Weak, accounts: Weak, - client: Weak, - miner: Weak, - - pending: Mutex>, + dispatcher: D, + pending: Arc>>, } -impl SigningQueueClient where - C: MiningBlockChainClient, - M: MinerService, +fn handle_dispatch(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)), + } +} + +impl SigningQueueClient { /// Creates a new signing queue client given shared signing queue. - pub fn new(signer: &Arc, client: &Arc, miner: &Arc, accounts: &Arc) -> Self { + pub fn new(signer: &Arc, dispatcher: D, accounts: &Arc) -> Self { SigningQueueClient { signer: Arc::downgrade(signer), accounts: Arc::downgrade(accounts), - client: Arc::downgrade(client), - miner: Arc::downgrade(miner), - pending: Mutex::new(TransientHashMap::new(MAX_PENDING_DURATION)), + dispatcher: dispatcher, + pending: Arc::new(Mutex::new(TransientHashMap::new(MAX_PENDING_DURATION))), } } - 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 dispatch(&self, payload: RpcConfirmationPayload, default_account: DefaultAccount) -> BoxFuture { + let setup = || { + let accounts = take_weak!(self.accounts); + let default_account = match default_account { + DefaultAccount::Provided(acc) => acc, + DefaultAccount::ForDapp(dapp) => accounts.default_address(dapp).ok().unwrap_or_default(), + }; - 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 dispatch::execute(&*client, &*miner, &*accounts, payload, dispatch::SignWith::Nothing) - .map(|v| v.into_value()) - .map(DispatchResult::Value); - } - - take_weak!(self.signer).add_request(payload) - .map(DispatchResult::Promise) - .map_err(|_| errors::request_rejected_limit()) - } - - fn dispatch(&self, payload: RpcConfirmationPayload, default_account: DefaultAccount) -> Result { - let client = take_weak!(self.client); - let miner = take_weak!(self.miner); - - let default_account = match default_account { - DefaultAccount::Provided(acc) => acc, - DefaultAccount::ForDapp(dapp) => take_weak!(self.accounts).default_address(dapp).ok().unwrap_or_default(), + (self.dispatcher.clone(), accounts, default_account) }; - let payload = dispatch::from_rpc(payload, default_account, &*client, &*miner); - self.add_to_queue(payload) + + let weak_signer = self.signer.clone(); + future::done(setup) + .and_then(move |(dispatcher, accounts, default_account)| { + dispatch::from_rpc(payload, default_account, &dispatcher) + .and_then(move |payload| { + let sender = payload.sender(); + if accounts.is_unlocked(sender) { + dispatch::execute(dispatcher, &accounts, payload, dispatch::SignWith::Nothing) + .boxed() + } else { + future::lazy(move || take_weak!(weak_signer).add_request(payload)) + .map(DispatchResult::Promise) + .map_err(|_| errors::request_rejected_limit()) + .boxed() + } + }) + }) + .boxed() } } -impl ParitySigning for SigningQueueClient where - C: MiningBlockChainClient, - M: MinerService, -{ +impl ParitySigning for SigningQueueClient { type Metadata = Metadata; - fn post_sign(&self, address: RpcH160, data: RpcBytes) -> Result, Error> { + fn post_sign(&self, address: RpcH160, data: RpcBytes) -> BoxFuture, Error> { + let pending = self.pending.clone(); self.dispatch(RpcConfirmationPayload::Signature((address.clone(), data).into()), DefaultAccount::Provided(address.into())) - .map(|result| match result { + .map(move |result| match result { DispatchResult::Value(v) => RpcEither::Or(v), DispatchResult::Promise(promise) => { let id = promise.id(); - self.pending.lock().insert(id, promise); + pending.lock().insert(id, promise); RpcEither::Either(id.into()) }, }) } fn post_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture, Error> { - let post_transaction = move || { - self.dispatch(RpcConfirmationPayload::SendTransaction(request), meta.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()) - }, - }) - }; - futures::done(post_transaction()).boxed() + let pending = self.pending.clone(); + self.dispatch(RpcConfirmationPayload::SendTransaction(request), meta.into()) + .map(move |result| match result { + DispatchResult::Value(v) => RpcEither::Or(v), + DispatchResult::Promise(promise) => { + let id = promise.id(); + pending.lock().insert(id, promise); + RpcEither::Either(id.into()) + }, + }) } fn check_request(&self, id: RpcU256) -> Result, Error> { @@ -170,67 +163,78 @@ impl ParitySigning for SigningQueueClient where let res = self.dispatch(RpcConfirmationPayload::Decrypt((address.clone(), data).into()), address.into()); let (ready, p) = futures::oneshot(); - // TODO [todr] typed handle_dispatch - self.handle_dispatch(res, |response| { - match response { - Ok(RpcConfirmationResponse::Decrypt(data)) => ready.complete(Ok(data)), - Err(e) => ready.complete(Err(e)), - e => ready.complete(Err(errors::internal("Unexpected result.", e))), - } - }); - p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))).boxed() + // when dispatch is complete + res.then(move |res| { + // register callback via the oneshot sender. + handle_dispatch(res, move |response| { + match response { + Ok(RpcConfirmationResponse::Decrypt(data)) => ready.complete(Ok(data)), + Err(e) => ready.complete(Err(e)), + e => ready.complete(Err(errors::internal("Unexpected result.", e))), + } + }); + + // and wait for that to resolve. + p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))).boxed() + }).boxed() } } -impl EthSigning for SigningQueueClient where - C: MiningBlockChainClient, - M: MinerService, -{ +impl EthSigning for SigningQueueClient { type Metadata = Metadata; fn sign(&self, address: RpcH160, data: RpcBytes) -> BoxFuture { let res = self.dispatch(RpcConfirmationPayload::Signature((address.clone(), data).into()), address.into()); let (ready, p) = futures::oneshot(); - self.handle_dispatch(res, |response| { - match response { - Ok(RpcConfirmationResponse::Signature(signature)) => ready.complete(Ok(signature)), - Err(e) => ready.complete(Err(e)), - e => ready.complete(Err(errors::internal("Unexpected result.", e))), - } - }); - p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))).boxed() + res.then(move |res| { + handle_dispatch(res, move |response| { + match response { + Ok(RpcConfirmationResponse::Signature(sig)) => ready.complete(Ok(sig)), + Err(e) => ready.complete(Err(e)), + e => ready.complete(Err(errors::internal("Unexpected result.", e))), + } + }); + + p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))).boxed() + }).boxed() } fn send_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture { let res = self.dispatch(RpcConfirmationPayload::SendTransaction(request), meta.into()); let (ready, p) = futures::oneshot(); - self.handle_dispatch(res, |response| { - match response { - Ok(RpcConfirmationResponse::SendTransaction(hash)) => ready.complete(Ok(hash)), - Err(e) => ready.complete(Err(e)), - e => ready.complete(Err(errors::internal("Unexpected result.", e))), - } - }); - p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))).boxed() + res.then(move |res| { + handle_dispatch(res, move |response| { + match response { + Ok(RpcConfirmationResponse::SendTransaction(hash)) => ready.complete(Ok(hash)), + Err(e) => ready.complete(Err(e)), + e => ready.complete(Err(errors::internal("Unexpected result.", e))), + } + }); + + p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))).boxed() + }).boxed() } fn sign_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture { let res = self.dispatch(RpcConfirmationPayload::SignTransaction(request), meta.into()); let (ready, p) = futures::oneshot(); - self.handle_dispatch(res, |response| { - match response { - Ok(RpcConfirmationResponse::SignTransaction(tx)) => ready.complete(Ok(tx)), - Err(e) => ready.complete(Err(e)), - e => ready.complete(Err(errors::internal("Unexpected result.", e))), - } - }); - p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))).boxed() + res.then(move |res| { + handle_dispatch(res, move |response| { + match response { + Ok(RpcConfirmationResponse::SignTransaction(tx)) => ready.complete(Ok(tx)), + Err(e) => ready.complete(Err(e)), + e => ready.complete(Err(errors::internal("Unexpected result.", e))), + } + }); + + p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))).boxed() + }).boxed() } } diff --git a/rpc/src/v1/impls/signing_unsafe.rs b/rpc/src/v1/impls/signing_unsafe.rs index b43362c76..53956f23c 100644 --- a/rpc/src/v1/impls/signing_unsafe.rs +++ b/rpc/src/v1/impls/signing_unsafe.rs @@ -22,9 +22,10 @@ use ethcore::account_provider::AccountProvider; use ethcore::miner::MinerService; use ethcore::client::MiningBlockChainClient; -use futures::{self, BoxFuture, Future}; +use futures::{self, future, BoxFuture, Future}; use jsonrpc_core::Error; -use v1::helpers::{errors, dispatch, DefaultAccount}; +use v1::helpers::{errors, DefaultAccount}; +use v1::helpers::dispatch::{self, Dispatcher}; use v1::metadata::Metadata; use v1::traits::{EthSigning, ParitySigning}; use v1::types::{ @@ -38,106 +39,100 @@ use v1::types::{ }; /// Implementation of functions that require signing when no trusted signer is used. -pub struct SigningUnsafeClient where - C: MiningBlockChainClient, - M: MinerService, -{ +pub struct SigningUnsafeClient { accounts: Weak, - client: Weak, - miner: Weak, + dispatcher: D, } -impl SigningUnsafeClient where - C: MiningBlockChainClient, - M: MinerService, -{ - +impl SigningUnsafeClient { /// Creates new SigningUnsafeClient. - pub fn new(client: &Arc, accounts: &Arc, miner: &Arc) - -> Self { + pub fn new(accounts: &Arc, dispatcher: D) -> Self { SigningUnsafeClient { - client: Arc::downgrade(client), - miner: Arc::downgrade(miner), accounts: Arc::downgrade(accounts), + dispatcher: dispatcher, } } - fn handle(&self, payload: RpcConfirmationPayload, account: DefaultAccount) -> Result { - let client = take_weak!(self.client); - let miner = take_weak!(self.miner); - let accounts = take_weak!(self.accounts); + fn handle(&self, payload: RpcConfirmationPayload, account: DefaultAccount) -> BoxFuture { + let setup = move || { + let accounts = take_weak!(self.accounts); + let default_account = match account { + DefaultAccount::Provided(acc) => acc, + DefaultAccount::ForDapp(dapp) => accounts.default_address(dapp).ok().unwrap_or_default(), + }; - let default_account = match account { - DefaultAccount::Provided(acc) => acc, - DefaultAccount::ForDapp(dapp) => accounts.default_address(dapp).ok().unwrap_or_default(), + (accounts, default_account) }; - let payload = dispatch::from_rpc(payload, default_account, &*client, &*miner); - dispatch::execute(&*client, &*miner, &*accounts, payload, dispatch::SignWith::Nothing) - .map(|v| v.into_value()) + + let dis = self.dispatcher.clone(); + future::done(setup()) + .and_then(move |(accounts, default)| { + dispatch::from_rpc(payload, default, &dis) + .and_then(|payload| { + dispatch::execute(&dis, &accounts, payload, dispatch::SignWith::Nothing) + }) + .map(|v| v.into_value()) + }) + .boxed() } } -impl EthSigning for SigningUnsafeClient where - C: MiningBlockChainClient, - M: MinerService, +impl EthSigning for SigningUnsafeClient { type Metadata = Metadata; fn sign(&self, address: RpcH160, data: RpcBytes) -> BoxFuture { - let result = match self.handle(RpcConfirmationPayload::Signature((address.clone(), data).into()), address.into()) { - Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature), - Err(e) => Err(e), - e => Err(errors::internal("Unexpected result", e)), - }; - - futures::done(result).boxed() + self.handle(RpcConfirmationPayload::Signature((address.clone(), data).into()), address.into()) + .then(|res| match res { + Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature), + Err(e) => Err(e), + e => Err(errors::internal("Unexpected result", e)), + }) + .boxed() } fn send_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture { - let result = match self.handle(RpcConfirmationPayload::SendTransaction(request), meta.into()) { - Ok(RpcConfirmationResponse::SendTransaction(hash)) => Ok(hash), - Err(e) => Err(e), - e => Err(errors::internal("Unexpected result", e)), - }; - - futures::done(result).boxed() + self.handle(RpcConfirmationPayload::SendTransaction(request), meta.into()) + .then(|res| match res { + Ok(RpcConfirmationResponse::SendTransaction(hash)) => Ok(hash), + Err(e) => Err(e), + e => Err(errors::internal("Unexpected result", e)), + }) + .boxed() } fn sign_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture { - let result = match self.handle(RpcConfirmationPayload::SignTransaction(request), meta.into()) { - Ok(RpcConfirmationResponse::SignTransaction(tx)) => Ok(tx), - Err(e) => Err(e), - e => Err(errors::internal("Unexpected result", e)), - }; - - futures::done(result).boxed() + self.handle(RpcConfirmationPayload::SignTransaction(request), meta.into()) + .then(|res| match res { + Ok(RpcConfirmationResponse::SignTransaction(tx)) => Ok(tx), + Err(e) => Err(e), + e => Err(errors::internal("Unexpected result", e)), + }) + .boxed() } } -impl ParitySigning for SigningUnsafeClient where - C: MiningBlockChainClient, - M: MinerService, -{ +impl ParitySigning for SigningUnsafeClient { type Metadata = Metadata; fn decrypt_message(&self, address: RpcH160, data: RpcBytes) -> BoxFuture { - let result = match self.handle(RpcConfirmationPayload::Decrypt((address.clone(), data).into()), address.into()) { - Ok(RpcConfirmationResponse::Decrypt(data)) => Ok(data), - Err(e) => Err(e), - e => Err(errors::internal("Unexpected result", e)), - }; - - futures::done(result).boxed() + self.handle(RpcConfirmationPayload::Decrypt((address.clone(), data).into()), address.into()) + .then(|res| match res { + Ok(RpcConfirmationResponse::Decrypt(data)) => Ok(data), + Err(e) => Err(e), + e => Err(errors::internal("Unexpected result", e)), + }) + .boxed() } - fn post_sign(&self, _: RpcH160, _: RpcBytes) -> Result, Error> { + fn post_sign(&self, _: RpcH160, _: RpcBytes) -> BoxFuture, Error> { // We don't support this in non-signer mode. - Err(errors::signer_disabled()) + future::err(errors::signer_disabled()).boxed() } fn post_transaction(&self, _: Metadata, _: RpcTransactionRequest) -> BoxFuture, Error> { // We don't support this in non-signer mode. - futures::done(Err(errors::signer_disabled())).boxed() + future::err((errors::signer_disabled())).boxed() } fn check_request(&self, _: RpcU256) -> Result, Error> { diff --git a/rpc/src/v1/mod.rs b/rpc/src/v1/mod.rs index 0b0787717..617dda0f9 100644 --- a/rpc/src/v1/mod.rs +++ b/rpc/src/v1/mod.rs @@ -18,6 +18,15 @@ //! //! Compliant with ethereum rpc. +macro_rules! take_weak { + ($weak: expr) => { + match $weak.upgrade() { + Some(arc) => arc, + None => return Err(Error::internal_error()) + } + } +} + #[macro_use] mod helpers; mod impls; diff --git a/rpc/src/v1/traits/parity_signing.rs b/rpc/src/v1/traits/parity_signing.rs index 9e723e4bf..3370bc259 100644 --- a/rpc/src/v1/traits/parity_signing.rs +++ b/rpc/src/v1/traits/parity_signing.rs @@ -27,8 +27,8 @@ build_rpc_trait! { /// Posts sign request asynchronously. /// Will return a confirmation ID for later use with check_transaction. - #[rpc(name = "parity_postSign")] - fn post_sign(&self, H160, Bytes) -> Result, Error>; + #[rpc(async, name = "parity_postSign")] + fn post_sign(&self, H160, Bytes) -> BoxFuture, Error>; /// Posts transaction asynchronously. /// Will return a transaction ID for later use with check_transaction. diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index 64c792606..7905dc5e5 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -164,7 +164,7 @@ mod tests { logs_bloom: H2048::default(), timestamp: U256::default(), difficulty: U256::default(), - total_difficulty: U256::default(), + total_difficulty: Some(U256::default()), seal_fields: vec![Bytes::default(), Bytes::default()], uncles: vec![], transactions: BlockTransactions::Hashes(vec![].into()),