diff --git a/ethcore/src/account_provider/mod.rs b/ethcore/src/account_provider/mod.rs index c96942ede..c9e6a0fc6 100755 --- a/ethcore/src/account_provider/mod.rs +++ b/ethcore/src/account_provider/mod.rs @@ -50,30 +50,33 @@ struct AccountData { password: String, } -/// `AccountProvider` errors. +/// Signing error #[derive(Debug)] -pub enum Error { - /// Returned when account is not unlocked. +pub enum SignError { + /// Account is not unlocked NotUnlocked, - /// Returned when signing fails. - SStore(SSError), + /// Low-level error from store + SStore(SSError) } -impl fmt::Display for Error { +impl fmt::Display for SignError { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { match *self { - Error::NotUnlocked => write!(f, "Account is locked"), - Error::SStore(ref e) => write!(f, "{}", e), + SignError::NotUnlocked => write!(f, "Account is locked"), + SignError::SStore(ref e) => write!(f, "{}", e), } } } -impl From for Error { +impl From for SignError { fn from(e: SSError) -> Self { - Error::SStore(e) + SignError::SStore(e) } } +/// `AccountProvider` errors. +pub type Error = SSError; + /// Dapp identifier #[derive(Default, Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct DappId(String); @@ -218,6 +221,14 @@ impl AccountProvider { } } + /// Returns default account for particular dapp falling back to other allowed accounts if necessary. + pub fn default_address(&self, dapp: DappId) -> Result { + self.dapps_addresses(dapp)? + .get(0) + .cloned() + .ok_or(SSError::InvalidAccount) + } + /// Sets addresses visile for dapp. pub fn set_dapps_addresses(&self, dapp: DappId, addresses: Vec
) -> Result<(), Error> { self.dapps_settings.write().set_accounts(dapp, addresses); @@ -288,8 +299,8 @@ impl AccountProvider { } /// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given. - pub fn change_password(&self, address: &Address, password: String, new_password: String) -> Result<(), Error> { - self.sstore.change_password(&StoreAccountRef::root(address.clone()), &password, &new_password).map_err(Error::SStore) + pub fn change_password(&self, account: &Address, password: String, new_password: String) -> Result<(), Error> { + self.sstore.change_password(&StoreAccountRef::root(account.clone()), &password, &new_password) } /// Helper method used for unlocking accounts. @@ -316,16 +327,16 @@ impl AccountProvider { Ok(()) } - fn password(&self, account: &StoreAccountRef) -> Result { + fn password(&self, account: &StoreAccountRef) -> Result { let mut unlocked = self.unlocked.write(); - let data = unlocked.get(account).ok_or(Error::NotUnlocked)?.clone(); + let data = unlocked.get(account).ok_or(SignError::NotUnlocked)?.clone(); if let Unlock::Temp = data.unlock { unlocked.remove(account).expect("data exists: so key must exist: qed"); } if let Unlock::Timed(ref end) = data.unlock { if Instant::now() > *end { unlocked.remove(account).expect("data exists: so key must exist: qed"); - return Err(Error::NotUnlocked); + return Err(SignError::NotUnlocked); } } Ok(data.password.clone()) @@ -354,14 +365,14 @@ impl AccountProvider { } /// Signs the message. If password is not provided the account must be unlocked. - pub fn sign(&self, address: Address, password: Option, message: Message) -> Result { + pub fn sign(&self, address: Address, password: Option, message: Message) -> Result { let account = StoreAccountRef::root(address); let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?; Ok(self.sstore.sign(&account, &password, &message)?) } /// Signs given message with supplied token. Returns a token to use in next signing within this session. - pub fn sign_with_token(&self, address: Address, token: AccountToken, message: Message) -> Result<(Signature, AccountToken), Error> { + pub fn sign_with_token(&self, address: Address, token: AccountToken, message: Message) -> Result<(Signature, AccountToken), SignError> { let account = StoreAccountRef::root(address); let is_std_password = self.sstore.test_password(&account, &token)?; @@ -383,7 +394,7 @@ impl AccountProvider { /// Decrypts a message with given token. Returns a token to use in next operation for this account. pub fn decrypt_with_token(&self, address: Address, token: AccountToken, shared_mac: &[u8], message: &[u8]) - -> Result<(Vec, AccountToken), Error> + -> Result<(Vec, AccountToken), SignError> { let account = StoreAccountRef::root(address); let is_std_password = self.sstore.test_password(&account, &token)?; @@ -405,7 +416,7 @@ impl AccountProvider { } /// Decrypts a message. If password is not provided the account must be unlocked. - pub fn decrypt(&self, address: Address, password: Option, shared_mac: &[u8], message: &[u8]) -> Result, Error> { + pub fn decrypt(&self, address: Address, password: Option, shared_mac: &[u8], message: &[u8]) -> Result, SignError> { let account = StoreAccountRef::root(address); let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?; Ok(self.sstore.decrypt(&account, &password, shared_mac, message)?) diff --git a/ethcore/src/engines/signer.rs b/ethcore/src/engines/signer.rs index 323c8bc08..a5a3d7dda 100644 --- a/ethcore/src/engines/signer.rs +++ b/ethcore/src/engines/signer.rs @@ -47,7 +47,7 @@ impl EngineSigner { } /// Sign a consensus message hash. - pub fn sign(&self, hash: H256) -> Result { + pub fn sign(&self, hash: H256) -> Result { self.account_provider.lock().sign(*self.address.read(), self.password.read().clone(), hash) } diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index b20ca7720..e8a05166c 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -283,11 +283,11 @@ impl Tendermint { } fn is_height(&self, message: &ConsensusMessage) -> bool { - message.vote_step.is_height(self.height.load(AtomicOrdering::SeqCst)) + message.vote_step.is_height(self.height.load(AtomicOrdering::SeqCst)) } fn is_view(&self, message: &ConsensusMessage) -> bool { - message.vote_step.is_view(self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst)) + message.vote_step.is_view(self.height.load(AtomicOrdering::SeqCst), self.view.load(AtomicOrdering::SeqCst)) } fn increment_view(&self, n: View) { @@ -309,7 +309,7 @@ impl Tendermint { fn has_enough_future_step_votes(&self, vote_step: &VoteStep) -> bool { if vote_step.view > self.view.load(AtomicOrdering::SeqCst) { let step_votes = self.votes.count_round_votes(vote_step); - self.is_above_threshold(step_votes) + self.is_above_threshold(step_votes) } else { false } @@ -673,7 +673,7 @@ mod tests { } } - fn vote(engine: &Engine, signer: F, height: usize, view: usize, step: Step, block_hash: Option) -> Bytes where F: FnOnce(H256) -> Result { + fn vote(engine: &Engine, signer: F, height: usize, view: usize, step: Step, block_hash: Option) -> Bytes where F: FnOnce(H256) -> Result { let mi = message_info_rlp(&VoteStep::new(height, view, step), block_hash); let m = message_full_rlp(&signer(mi.sha3()).unwrap().into(), &mi); engine.handle_message(&m).unwrap(); diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index cb3a7d497..984df79bc 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -26,7 +26,7 @@ use types::block_import_error::BlockImportError; use snapshot::Error as SnapshotError; use engines::EngineError; use ethkey::Error as EthkeyError; -use account_provider::Error as AccountsError; +use account_provider::SignError as AccountsError; pub use types::executed::{ExecutionError, CallError}; diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index eb1a030b0..44a23993b 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -18,7 +18,7 @@ use std::time::{Instant, Duration}; use util::*; use util::using_queue::{UsingQueue, GetAction}; -use account_provider::{AccountProvider, Error as AccountError}; +use account_provider::{AccountProvider, SignError as AccountError}; use state::{State, CleanupMode}; use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockId, CallAnalytics, TransactionId}; use client::TransactionImportResult; diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index c67d647db..495dc3bba 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -83,7 +83,7 @@ pub trait MinerService : Send + Sync { fn set_author(&self, author: Address); /// Set info necessary to sign consensus messages. - fn set_engine_signer(&self, address: Address, password: String) -> Result<(), ::account_provider::Error>; + fn set_engine_signer(&self, address: Address, password: String) -> Result<(), ::account_provider::SignError>; /// Get the extra_data that we will seal blocks with. fn extra_data(&self) -> Bytes; diff --git a/rpc/src/v1/helpers/dispatch.rs b/rpc/src/v1/helpers/dispatch.rs index c0d8d04eb..5ef3e8cc1 100644 --- a/rpc/src/v1/helpers/dispatch.rs +++ b/rpc/src/v1/helpers/dispatch.rs @@ -210,11 +210,12 @@ pub fn sign_and_dispatch(client: &C, miner: &M, accounts: &AccountProvider }) } -pub fn fill_optional_fields(request: TransactionRequest, client: &C, miner: &M) -> FilledTransactionRequest +pub fn fill_optional_fields(request: TransactionRequest, default_sender: Address, client: &C, miner: &M) -> FilledTransactionRequest where C: MiningBlockChainClient, M: MinerService { FilledTransactionRequest { - from: request.from, + 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)), @@ -231,15 +232,15 @@ pub fn default_gas_price(client: &C, miner: &M) -> U256 client.gas_price_median(100).unwrap_or_else(|| miner.sensible_gas_price()) } -pub fn from_rpc(payload: RpcConfirmationPayload, client: &C, miner: &M) -> ConfirmationPayload +pub fn from_rpc(payload: RpcConfirmationPayload, default_account: Address, client: &C, miner: &M) -> ConfirmationPayload where C: MiningBlockChainClient, M: MinerService { match payload { RpcConfirmationPayload::SendTransaction(request) => { - ConfirmationPayload::SendTransaction(fill_optional_fields(request.into(), client, miner)) + ConfirmationPayload::SendTransaction(fill_optional_fields(request.into(), default_account, client, miner)) }, RpcConfirmationPayload::SignTransaction(request) => { - ConfirmationPayload::SignTransaction(fill_optional_fields(request.into(), client, miner)) + ConfirmationPayload::SignTransaction(fill_optional_fields(request.into(), default_account, client, miner)) }, RpcConfirmationPayload::Decrypt(RpcDecryptRequest { address, msg }) => { ConfirmationPayload::Decrypt(address.into(), msg.into()) diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index 00272fb8b..e1074c598 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -23,7 +23,7 @@ macro_rules! rpc_unimplemented { use std::fmt; use rlp::DecoderError; use ethcore::error::{Error as EthcoreError, CallError, TransactionError}; -use ethcore::account_provider::{Error as AccountError}; +use ethcore::account_provider::{SignError as AccountError}; use jsonrpc_core::{Error, ErrorCode, Value}; mod codes { diff --git a/rpc/src/v1/helpers/mod.rs b/rpc/src/v1/helpers/mod.rs index 81a9fe02f..da24f0a5b 100644 --- a/rpc/src/v1/helpers/mod.rs +++ b/rpc/src/v1/helpers/mod.rs @@ -29,7 +29,11 @@ mod network_settings; pub use self::poll_manager::PollManager; pub use self::poll_filter::{PollFilter, limit_logs}; -pub use self::requests::{TransactionRequest, FilledTransactionRequest, ConfirmationRequest, ConfirmationPayload, CallRequest}; -pub use self::signing_queue::{ConfirmationsQueue, ConfirmationPromise, ConfirmationResult, SigningQueue, QueueEvent}; +pub use self::requests::{ + TransactionRequest, FilledTransactionRequest, ConfirmationRequest, ConfirmationPayload, CallRequest, +}; +pub use self::signing_queue::{ + ConfirmationsQueue, ConfirmationPromise, ConfirmationResult, SigningQueue, QueueEvent, DefaultAccount, +}; pub use self::signer::SignerService; pub use self::network_settings::NetworkSettings; diff --git a/rpc/src/v1/helpers/requests.rs b/rpc/src/v1/helpers/requests.rs index 2bf51f744..d7a737dc0 100644 --- a/rpc/src/v1/helpers/requests.rs +++ b/rpc/src/v1/helpers/requests.rs @@ -20,7 +20,7 @@ use util::{Address, U256, Bytes}; #[derive(Debug, Clone, Default, Eq, PartialEq, Hash)] pub struct TransactionRequest { /// Sender - pub from: Address, + pub from: Option
, /// Recipient pub to: Option
, /// Gas Price @@ -42,6 +42,8 @@ pub struct TransactionRequest { pub struct FilledTransactionRequest { /// Sender pub from: Address, + /// Indicates if the sender was filled by default value. + pub used_default_from: bool, /// Recipient pub to: Option
, /// Gas Price @@ -61,7 +63,7 @@ pub struct FilledTransactionRequest { impl From for TransactionRequest { fn from(r: FilledTransactionRequest) -> Self { TransactionRequest { - from: r.from, + from: Some(r.from), to: r.to, gas_price: Some(r.gas_price), gas: Some(r.gas), diff --git a/rpc/src/v1/helpers/signing_queue.rs b/rpc/src/v1/helpers/signing_queue.rs index ecef97140..428f17b83 100644 --- a/rpc/src/v1/helpers/signing_queue.rs +++ b/rpc/src/v1/helpers/signing_queue.rs @@ -19,13 +19,36 @@ use std::cell::RefCell; use std::sync::{mpsc, Arc}; use std::collections::BTreeMap; use jsonrpc_core; -use util::{Mutex, RwLock, U256}; +use util::{Mutex, RwLock, U256, Address}; +use ethcore::account_provider::DappId; use v1::helpers::{ConfirmationRequest, ConfirmationPayload}; -use v1::types::ConfirmationResponse; +use v1::metadata::Metadata; +use v1::types::{ConfirmationResponse, H160 as RpcH160}; /// Result that can be returned from JSON RPC. pub type RpcResult = Result; + +/// Type of default account +pub enum DefaultAccount { + /// Default account is known + Provided(Address), + /// Should use default account for dapp + ForDapp(DappId), +} + +impl From for DefaultAccount { + fn from(meta: Metadata) -> Self { + DefaultAccount::ForDapp(meta.dapp_id.unwrap_or_default().into()) + } +} + +impl From for DefaultAccount { + fn from(address: RpcH160) -> Self { + DefaultAccount::Provided(address.into()) + } +} + /// Possible events happening in the queue that can be listened to. #[derive(Debug, PartialEq)] pub enum QueueEvent { @@ -319,6 +342,7 @@ mod test { fn request() -> ConfirmationPayload { ConfirmationPayload::SendTransaction(FilledTransactionRequest { from: Address::from(1), + used_default_from: false, to: Some(Address::from(2)), gas_price: 0.into(), gas: 10_000.into(), diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 4e315679a..5270e417d 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -45,15 +45,15 @@ use ethsync::{SyncProvider}; use jsonrpc_core::Error; use jsonrpc_macros::Trailing; +use v1::helpers::{CallRequest as CRequest, errors, limit_logs}; +use v1::helpers::dispatch::{dispatch_transaction, default_gas_price}; +use v1::helpers::block_import::is_major_importing; use v1::traits::Eth; use v1::types::{ RichBlock, 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::{dispatch_transaction, default_gas_price}; -use v1::helpers::block_import::is_major_importing; use v1::metadata::Metadata; const EXTRA_INFO_PROOF: &'static str = "Object exists in in blockchain (fetched earlier), extra_info is always available if object exists; qed"; diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index 525a52a57..185f094e7 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -35,6 +35,8 @@ use updater::{Service as UpdateService}; use jsonrpc_core::Error; use jsonrpc_macros::Trailing; +use v1::helpers::{errors, SigningQueue, SignerService, NetworkSettings}; +use v1::helpers::dispatch::DEFAULT_MAC; use v1::metadata::Metadata; use v1::traits::Parity; use v1::types::{ @@ -44,8 +46,6 @@ use v1::types::{ BlockNumber, ConsensusCapability, VersionInfo, OperationsInfo, DappId, ChainStatus, }; -use v1::helpers::{errors, SigningQueue, SignerService, NetworkSettings}; -use v1::helpers::dispatch::DEFAULT_MAC; /// Parity implementation. pub struct ParityClient where diff --git a/rpc/src/v1/impls/parity_accounts.rs b/rpc/src/v1/impls/parity_accounts.rs index 7cf67de98..4348c2f42 100644 --- a/rpc/src/v1/impls/parity_accounts.rs +++ b/rpc/src/v1/impls/parity_accounts.rs @@ -24,9 +24,9 @@ use ethcore::account_provider::AccountProvider; use ethcore::client::MiningBlockChainClient; use jsonrpc_core::Error; +use v1::helpers::errors; use v1::traits::ParityAccounts; use v1::types::{H160 as RpcH160, H256 as RpcH256, DappId}; -use v1::helpers::errors; /// Account management (personal) rpc implementation. pub struct ParityAccountsClient where C: MiningBlockChainClient { diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index 082ec8de0..b29cc41ee 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -22,21 +22,29 @@ use ethcore::client::MiningBlockChainClient; use ethcore::miner::MinerService; use util::{Address, U128, Uint}; +use futures::{self, Future, BoxFuture}; use jsonrpc_core::Error; -use v1::traits::Personal; -use v1::types::{H160 as RpcH160, H256 as RpcH256, U128 as RpcU128, TransactionRequest}; use v1::helpers::errors; use v1::helpers::dispatch::{self, sign_and_dispatch}; +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 where + C: MiningBlockChainClient, + M: MinerService, +{ accounts: Weak, client: Weak, miner: Weak, allow_perm_unlock: bool, } -impl PersonalClient where C: MiningBlockChainClient, M: MinerService { +impl PersonalClient where + C: MiningBlockChainClient, + M: MinerService, +{ /// Creates new PersonalClient pub fn new(store: &Arc, client: &Arc, miner: &Arc, allow_perm_unlock: bool) -> Self { PersonalClient { @@ -54,7 +62,12 @@ impl PersonalClient where C: MiningBlockChainClient, M: MinerService } } -impl Personal for PersonalClient where C: MiningBlockChainClient, M: MinerService { +impl Personal for PersonalClient where + C: MiningBlockChainClient + 'static, + M: MinerService + 'static, +{ + type Metadata = Metadata; + fn accounts(&self) -> Result, Error> { self.active()?; @@ -102,19 +115,30 @@ impl Personal for PersonalClient where C: MiningBl } } - fn send_transaction(&self, request: TransactionRequest, password: String) -> Result { - self.active()?; - let client = take_weak!(self.client); - let miner = take_weak!(self.miner); - let accounts = take_weak!(self.accounts); + fn send_transaction(&self, meta: Metadata, request: TransactionRequest, password: String) -> BoxFuture { + let sign_and_send = move || { + 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( - &*client, - &*miner, - &*accounts, - request, - dispatch::SignWith::Password(password) - ).map(|v| v.into_value().into()) + 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()) + }; + + futures::done(sign_and_send()).boxed() } } diff --git a/rpc/src/v1/impls/signer.rs b/rpc/src/v1/impls/signer.rs index f65db3bde..315541c1f 100644 --- a/rpc/src/v1/impls/signer.rs +++ b/rpc/src/v1/impls/signer.rs @@ -25,10 +25,10 @@ use ethcore::transaction::{SignedTransaction, PendingTransaction}; use ethcore::miner::MinerService; use jsonrpc_core::Error; -use v1::traits::Signer; -use v1::types::{TransactionModification, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken, U256, Bytes}; use v1::helpers::{errors, SignerService, SigningQueue, ConfirmationPayload}; use v1::helpers::dispatch::{self, dispatch_transaction, WithToken}; +use v1::traits::Signer; +use v1::types::{TransactionModification, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken, U256, Bytes}; /// Transactions confirmation (personal) rpc implementation. pub struct SignerClient where C: MiningBlockChainClient, M: MinerService { diff --git a/rpc/src/v1/impls/signing.rs b/rpc/src/v1/impls/signing.rs index 5ed215281..2d5fdbba7 100644 --- a/rpc/src/v1/impls/signing.rs +++ b/rpc/src/v1/impls/signing.rs @@ -28,8 +28,10 @@ use futures::{self, BoxFuture, Future}; use jsonrpc_core::Error; use v1::helpers::{ errors, dispatch, + DefaultAccount, SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationPayload, SignerService }; +use v1::metadata::Metadata; use v1::traits::{EthSigning, ParitySigning}; use v1::types::{ H160 as RpcH160, H256 as RpcH256, U256 as RpcU256, Bytes as RpcBytes, H520 as RpcH520, @@ -42,7 +44,7 @@ use v1::types::{ const MAX_PENDING_DURATION: u64 = 60 * 60; -pub enum DispatchResult { +enum DispatchResult { Promise(ConfirmationPromise), Value(RpcConfirmationResponse), } @@ -109,11 +111,15 @@ impl SigningQueueClient where .map_err(|_| errors::request_rejected_limit()) } - fn dispatch(&self, payload: RpcConfirmationPayload) -> Result { + fn dispatch(&self, payload: RpcConfirmationPayload, default_account: DefaultAccount) -> Result { let client = take_weak!(self.client); let miner = take_weak!(self.miner); - let payload = dispatch::from_rpc(payload, &*client, &*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(), + }; + let payload = dispatch::from_rpc(payload, default_account, &*client, &*miner); self.add_to_queue(payload) } } @@ -122,9 +128,11 @@ impl ParitySigning for SigningQueueClient where C: MiningBlockChainClient, M: MinerService, { + type Metadata = Metadata; + fn post_sign(&self, address: RpcH160, data: RpcBytes) -> Result, Error> { self.active()?; - self.dispatch(RpcConfirmationPayload::Signature((address, data).into())) + self.dispatch(RpcConfirmationPayload::Signature((address.clone(), data).into()), DefaultAccount::Provided(address.into())) .map(|result| match result { DispatchResult::Value(v) => RpcEither::Or(v), DispatchResult::Promise(promise) => { @@ -135,17 +143,20 @@ impl ParitySigning for SigningQueueClient where }) } - fn post_transaction(&self, request: RpcTransactionRequest) -> Result, Error> { - self.active()?; - 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 post_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture, Error> { + let post_transaction = move || { + self.active()?; + 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() } fn check_request(&self, id: RpcU256) -> Result, Error> { @@ -166,7 +177,7 @@ impl ParitySigning for SigningQueueClient where fn decrypt_message(&self, address: RpcH160, data: RpcBytes) -> BoxFuture { let res = self.active() - .and_then(|_| self.dispatch(RpcConfirmationPayload::Decrypt((address, data).into()))); + .and_then(|_| self.dispatch(RpcConfirmationPayload::Decrypt((address.clone(), data).into()), address.into())); let (ready, p) = futures::oneshot(); // TODO [todr] typed handle_dispatch @@ -186,8 +197,11 @@ impl EthSigning for SigningQueueClient where C: MiningBlockChainClient, M: MinerService, { + type Metadata = Metadata; + fn sign(&self, address: RpcH160, data: RpcBytes) -> BoxFuture { - let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::Signature((address, data).into()))); + let res = self.active() + .and_then(|_| self.dispatch(RpcConfirmationPayload::Signature((address.clone(), data).into()), address.into())); let (ready, p) = futures::oneshot(); self.handle_dispatch(res, |response| { @@ -201,8 +215,9 @@ impl EthSigning for SigningQueueClient where p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))).boxed() } - fn send_transaction(&self, request: RpcTransactionRequest) -> BoxFuture { - let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::SendTransaction(request))); + fn send_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture { + let res = self.active() + .and_then(|_| self.dispatch(RpcConfirmationPayload::SendTransaction(request), meta.into())); let (ready, p) = futures::oneshot(); self.handle_dispatch(res, |response| { @@ -216,8 +231,8 @@ impl EthSigning for SigningQueueClient where p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))).boxed() } - fn sign_transaction(&self, request: RpcTransactionRequest) -> BoxFuture { - let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::SignTransaction(request))); + fn sign_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture { + let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::SignTransaction(request), meta.into())); let (ready, p) = futures::oneshot(); self.handle_dispatch(res, |response| { diff --git a/rpc/src/v1/impls/signing_unsafe.rs b/rpc/src/v1/impls/signing_unsafe.rs index 1dbffa7aa..a76ea00a5 100644 --- a/rpc/src/v1/impls/signing_unsafe.rs +++ b/rpc/src/v1/impls/signing_unsafe.rs @@ -24,8 +24,8 @@ use ethcore::client::MiningBlockChainClient; use futures::{self, BoxFuture, Future}; use jsonrpc_core::Error; -use v1::helpers::errors; -use v1::helpers::dispatch; +use v1::helpers::{errors, dispatch, DefaultAccount}; +use v1::metadata::Metadata; use v1::traits::{EthSigning, ParitySigning}; use v1::types::{ U256 as RpcU256, @@ -68,13 +68,17 @@ impl SigningUnsafeClient where Ok(()) } - fn handle(&self, payload: RpcConfirmationPayload) -> Result { + fn handle(&self, payload: RpcConfirmationPayload, account: DefaultAccount) -> Result { 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); + let default_account = match account { + DefaultAccount::Provided(acc) => acc, + DefaultAccount::ForDapp(dapp) => accounts.default_address(dapp).ok().unwrap_or_default(), + }; + let payload = dispatch::from_rpc(payload, default_account, &*client, &*miner); dispatch::execute(&*client, &*miner, &*accounts, payload, dispatch::SignWith::Nothing) .map(|v| v.into_value()) } @@ -84,8 +88,10 @@ impl EthSigning for SigningUnsafeClient where C: MiningBlockChainClient, M: MinerService, { + type Metadata = Metadata; + fn sign(&self, address: RpcH160, data: RpcBytes) -> BoxFuture { - let result = match self.handle(RpcConfirmationPayload::Signature((address, data).into())) { + 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)), @@ -94,8 +100,8 @@ impl EthSigning for SigningUnsafeClient where futures::done(result).boxed() } - fn send_transaction(&self, request: RpcTransactionRequest) -> BoxFuture { - let result = match self.handle(RpcConfirmationPayload::SendTransaction(request)) { + 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)), @@ -104,8 +110,8 @@ impl EthSigning for SigningUnsafeClient where futures::done(result).boxed() } - fn sign_transaction(&self, request: RpcTransactionRequest) -> BoxFuture { - let result = match self.handle(RpcConfirmationPayload::SignTransaction(request)) { + 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)), @@ -119,8 +125,10 @@ impl ParitySigning for SigningUnsafeClient where C: MiningBlockChainClient, M: MinerService, { + type Metadata = Metadata; + fn decrypt_message(&self, address: RpcH160, data: RpcBytes) -> BoxFuture { - let result = match self.handle(RpcConfirmationPayload::Decrypt((address, data).into())) { + 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)), @@ -134,9 +142,9 @@ impl ParitySigning for SigningUnsafeClient where Err(errors::signer_disabled()) } - fn post_transaction(&self, _: RpcTransactionRequest) -> Result, Error> { + fn post_transaction(&self, _: Metadata, _: RpcTransactionRequest) -> BoxFuture, Error> { // We don't support this in non-signer mode. - Err(errors::signer_disabled()) + futures::done(Err(errors::signer_disabled())).boxed() } fn check_request(&self, _: RpcU256) -> Result, Error> { diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 07be57846..33c624896 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -25,7 +25,7 @@ use ethcore::header::BlockNumber; use ethcore::transaction::{UnverifiedTransaction, SignedTransaction, PendingTransaction}; use ethcore::receipt::{Receipt, RichReceipt}; use ethcore::miner::{MinerService, MinerStatus, TransactionImportResult, LocalTransactionStatus}; -use ethcore::account_provider::Error as AccountError; +use ethcore::account_provider::SignError as AccountError; /// Test miner service. pub struct TestMinerService { diff --git a/rpc/src/v1/tests/mocked/personal.rs b/rpc/src/v1/tests/mocked/personal.rs index ab8fc1331..3e0228714 100644 --- a/rpc/src/v1/tests/mocked/personal.rs +++ b/rpc/src/v1/tests/mocked/personal.rs @@ -19,14 +19,14 @@ use std::str::FromStr; use jsonrpc_core::IoHandler; use util::{U256, Uint, Address}; use ethcore::account_provider::AccountProvider; -use v1::{PersonalClient, Personal}; +use v1::{PersonalClient, Personal, Metadata}; use v1::tests::helpers::TestMinerService; use ethcore::client::TestBlockChainClient; use ethcore::transaction::{Action, Transaction}; struct PersonalTester { accounts: Arc, - io: IoHandler, + io: IoHandler, miner: Arc, // these unused fields are necessary to keep the data alive // as the handler has only weak pointers. diff --git a/rpc/src/v1/tests/mocked/signer.rs b/rpc/src/v1/tests/mocked/signer.rs index 650f33f55..b1609cf8f 100644 --- a/rpc/src/v1/tests/mocked/signer.rs +++ b/rpc/src/v1/tests/mocked/signer.rs @@ -26,13 +26,14 @@ use rlp::encode; use serde_json; use jsonrpc_core::IoHandler; use v1::{SignerClient, Signer}; +use v1::metadata::Metadata; use v1::tests::helpers::TestMinerService; use v1::helpers::{SigningQueue, SignerService, FilledTransactionRequest, ConfirmationPayload}; struct SignerTester { signer: Arc, accounts: Arc, - io: IoHandler, + io: IoHandler, miner: Arc, // these unused fields are necessary to keep the data alive // as the handler has only weak pointers. @@ -77,6 +78,7 @@ fn should_return_list_of_items_to_confirm() { let tester = signer_tester(); tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { from: Address::from(1), + used_default_from: false, to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), gas_price: U256::from(10_000), gas: U256::from(10_000_000), @@ -107,6 +109,7 @@ fn should_reject_transaction_from_queue_without_dispatching() { let tester = signer_tester(); tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { from: Address::from(1), + used_default_from: false, to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), gas_price: U256::from(10_000), gas: U256::from(10_000_000), @@ -133,6 +136,7 @@ fn should_not_remove_transaction_if_password_is_invalid() { let tester = signer_tester(); tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { from: Address::from(1), + used_default_from: false, to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), gas_price: U256::from(10_000), gas: U256::from(10_000_000), @@ -176,6 +180,7 @@ fn should_confirm_transaction_and_dispatch() { let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { from: address, + used_default_from: false, to: Some(recipient), gas_price: U256::from(10_000), gas: U256::from(10_000_000), @@ -221,6 +226,7 @@ fn should_alter_the_sender_and_nonce() { let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { from: 0.into(), + used_default_from: false, to: Some(recipient), gas_price: U256::from(10_000), gas: U256::from(10_000_000), @@ -270,6 +276,7 @@ fn should_confirm_transaction_with_token() { let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { from: address, + used_default_from: false, to: Some(recipient), gas_price: U256::from(10_000), gas: U256::from(10_000_000), @@ -318,6 +325,7 @@ fn should_confirm_transaction_with_rlp() { let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { from: address, + used_default_from: false, to: Some(recipient), gas_price: U256::from(10_000), gas: U256::from(10_000_000), @@ -365,6 +373,7 @@ fn should_return_error_when_sender_does_not_match() { let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { from: Address::default(), + used_default_from: false, to: Some(recipient), gas_price: U256::from(10_000), gas: U256::from(10_000_000), diff --git a/rpc/src/v1/traits/eth_signing.rs b/rpc/src/v1/traits/eth_signing.rs index fb83387a8..51b7c4efc 100644 --- a/rpc/src/v1/traits/eth_signing.rs +++ b/rpc/src/v1/traits/eth_signing.rs @@ -24,6 +24,8 @@ use v1::types::{Bytes, H160, H256, H520, TransactionRequest, RichRawTransaction} build_rpc_trait! { /// Signing methods implementation relying on unlocked accounts. pub trait EthSigning { + type Metadata; + /// Signs the hash of data with given address signature. #[rpc(async, name = "eth_sign")] fn sign(&self, H160, Bytes) -> BoxFuture; @@ -31,13 +33,13 @@ build_rpc_trait! { /// 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, TransactionRequest) -> BoxFuture; + #[rpc(meta, name = "eth_sendTransaction")] + fn send_transaction(&self, Self::Metadata, TransactionRequest) -> BoxFuture; /// Signs transactions without dispatching it to the network. /// Returns signed transaction RLP representation and the transaction itself. /// It can be later submitted using `eth_sendRawTransaction/eth_submitTransaction`. - #[rpc(async, name = "eth_signTransaction")] - fn sign_transaction(&self, TransactionRequest) -> BoxFuture; + #[rpc(meta, name = "eth_signTransaction")] + fn sign_transaction(&self, Self::Metadata, TransactionRequest) -> BoxFuture; } } diff --git a/rpc/src/v1/traits/parity_signing.rs b/rpc/src/v1/traits/parity_signing.rs index 3ce01e345..9e723e4bf 100644 --- a/rpc/src/v1/traits/parity_signing.rs +++ b/rpc/src/v1/traits/parity_signing.rs @@ -23,6 +23,8 @@ use v1::types::{U256, H160, Bytes, ConfirmationResponse, TransactionRequest, Eit build_rpc_trait! { /// Signing methods implementation. pub trait ParitySigning { + type Metadata; + /// Posts sign request asynchronously. /// Will return a confirmation ID for later use with check_transaction. #[rpc(name = "parity_postSign")] @@ -30,8 +32,8 @@ build_rpc_trait! { /// 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>; + #[rpc(meta, name = "parity_postTransaction")] + fn post_transaction(&self, Self::Metadata, TransactionRequest) -> BoxFuture, Error>; /// Checks the progress of a previously posted request (transaction/sign). /// Should be given a valid send_transaction ID. diff --git a/rpc/src/v1/traits/personal.rs b/rpc/src/v1/traits/personal.rs index afca38f06..2076d87f2 100644 --- a/rpc/src/v1/traits/personal.rs +++ b/rpc/src/v1/traits/personal.rs @@ -17,11 +17,15 @@ //! Personal rpc interface. use jsonrpc_core::Error; +use futures::BoxFuture; + use v1::types::{U128, H160, H256, TransactionRequest}; build_rpc_trait! { /// Personal rpc interface. Safe (read-only) functions. pub trait Personal { + type Metadata; + /// Lists all stored accounts #[rpc(name = "personal_listAccounts")] fn accounts(&self) -> Result, Error>; @@ -36,7 +40,7 @@ build_rpc_trait! { fn unlock_account(&self, H160, String, Option) -> Result; /// Sends transaction and signs it in single call. The account is not unlocked in such case. - #[rpc(name = "personal_sendTransaction")] - fn send_transaction(&self, TransactionRequest, String) -> Result; + #[rpc(meta, name = "personal_sendTransaction")] + fn send_transaction(&self, Self::Metadata, TransactionRequest, String) -> BoxFuture; } } diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index c9b7b2006..0c36fdff9 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -267,6 +267,7 @@ mod tests { id: 15.into(), payload: helpers::ConfirmationPayload::SendTransaction(helpers::FilledTransactionRequest { from: 0.into(), + used_default_from: false, to: None, gas: 15_000.into(), gas_price: 10_000.into(), @@ -292,6 +293,7 @@ mod tests { id: 15.into(), payload: helpers::ConfirmationPayload::SignTransaction(helpers::FilledTransactionRequest { from: 0.into(), + used_default_from: false, to: None, gas: 15_000.into(), gas_price: 10_000.into(), diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index 97f07e351..9434983fa 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -27,7 +27,7 @@ use std::fmt; #[serde(deny_unknown_fields)] pub struct TransactionRequest { /// Sender - pub from: H160, + pub from: Option, /// Recipient pub to: Option, /// Gas Price @@ -86,7 +86,7 @@ impl fmt::Display for TransactionRequest { impl From for TransactionRequest { fn from(r: helpers::TransactionRequest) -> Self { TransactionRequest { - from: r.from.into(), + from: r.from.map(Into::into), to: r.to.map(Into::into), gas_price: r.gas_price.map(Into::into), gas: r.gas.map(Into::into), @@ -101,7 +101,7 @@ impl From for TransactionRequest { impl From for TransactionRequest { fn from(r: helpers::FilledTransactionRequest) -> Self { TransactionRequest { - from: r.from.into(), + from: Some(r.from.into()), to: r.to.map(Into::into), gas_price: Some(r.gas_price.into()), gas: Some(r.gas.into()), @@ -116,7 +116,7 @@ impl From for TransactionRequest { impl Into for TransactionRequest { fn into(self) -> helpers::TransactionRequest { helpers::TransactionRequest { - from: self.from.into(), + from: self.from.map(Into::into), to: self.to.map(Into::into), gas_price: self.gas_price.map(Into::into), gas: self.gas.map(Into::into), @@ -152,7 +152,7 @@ mod tests { let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, TransactionRequest { - from: H160::from(1), + from: Some(H160::from(1)), to: Some(H160::from(2)), gas_price: Some(U256::from(1)), gas: Some(U256::from(2)), @@ -176,7 +176,7 @@ mod tests { let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, TransactionRequest { - from: H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap(), + from: Some(H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap()), to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), gas_price: Some(U256::from_str("9184e72a000").unwrap()), gas: Some(U256::from_str("76c0").unwrap()), @@ -193,7 +193,7 @@ mod tests { let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, TransactionRequest { - from: H160::from(1).into(), + from: Some(H160::from(1).into()), to: None, gas_price: None, gas: None, @@ -217,7 +217,7 @@ mod tests { let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, TransactionRequest { - from: H160::from_str("b5f7502a2807cb23615c7456055e1d65b2508625").unwrap(), + from: Some(H160::from_str("b5f7502a2807cb23615c7456055e1d65b2508625").unwrap()), to: Some(H160::from_str("895d32f2db7d01ebb50053f9e48aacf26584fe40").unwrap()), gas_price: Some(U256::from_str("0ba43b7400").unwrap()), gas: Some(U256::from_str("2fd618").unwrap()),