From 8dff4012a68c35fe50450364b617d7c1a7045c17 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 27 Oct 2016 19:26:34 +0200 Subject: [PATCH] Personal split (#2879) * Split personal namespace into Safe and Unsafe part * Re-add api.personal.accountsInfo() calls to dapps * Removing listGethAccounts from safe personal --- .../basiccoin/Application/application.js | 4 +- .../dapps/gavcoin/Application/application.js | 2 +- js/src/dapps/githubhint/services.js | 2 +- js/src/dapps/registry/addresses/actions.js | 12 +- js/src/dapps/signaturereg/services.js | 2 +- js/src/dapps/tokenreg/Accounts/actions.js | 2 +- parity/cli/config.full.toml | 4 +- parity/cli/mod.rs | 8 +- parity/cli/usage.txt | 2 +- parity/rpc_apis.rs | 27 ++- rpc/src/v1/impls/mod.rs | 2 + rpc/src/v1/impls/personal.rs | 154 +------------- rpc/src/v1/impls/personal_accounts.rs | 194 ++++++++++++++++++ rpc/src/v1/mod.rs | 2 +- rpc/src/v1/tests/mocked/personal.rs | 6 +- rpc/src/v1/traits/mod.rs | 2 +- rpc/src/v1/traits/personal.rs | 50 +++-- 17 files changed, 276 insertions(+), 199 deletions(-) create mode 100644 rpc/src/v1/impls/personal_accounts.rs diff --git a/js/src/dapps/basiccoin/Application/application.js b/js/src/dapps/basiccoin/Application/application.js index d84085c98..4ab97ab6c 100644 --- a/js/src/dapps/basiccoin/Application/application.js +++ b/js/src/dapps/basiccoin/Application/application.js @@ -16,7 +16,7 @@ import React, { Component, PropTypes } from 'react'; -// import { api } from '../parity'; +import { api } from '../parity'; import { attachInstances } from '../services'; import Header from './Header'; @@ -83,7 +83,7 @@ export default class Application extends Component { Promise .all([ attachInstances(), - null // api.personal.accountsInfo() + api.personal.accountsInfo() ]) .then(([{ managerInstance, registryInstance, tokenregInstance }, accountsInfo]) => { accountsInfo = accountsInfo || {}; diff --git a/js/src/dapps/gavcoin/Application/application.js b/js/src/dapps/gavcoin/Application/application.js index 29c86c78d..d5d26bc3e 100644 --- a/js/src/dapps/gavcoin/Application/application.js +++ b/js/src/dapps/gavcoin/Application/application.js @@ -206,7 +206,7 @@ export default class Application extends Component { .all([ registry.getAddress.call({}, [api.util.sha3('gavcoin'), 'A']), api.eth.accounts(), - null // api.personal.accountsInfo() + api.personal.accountsInfo() ]); }) .then(([address, addresses, infos]) => { diff --git a/js/src/dapps/githubhint/services.js b/js/src/dapps/githubhint/services.js index 1904be2d7..b7676d5f5 100644 --- a/js/src/dapps/githubhint/services.js +++ b/js/src/dapps/githubhint/services.js @@ -29,7 +29,7 @@ export function attachInterface () { .all([ registry.getAddress.call({}, [api.util.sha3('githubhint'), 'A']), api.eth.accounts(), - null // api.personal.accountsInfo() + api.personal.accountsInfo() ]); }) .then(([address, addresses, accountsInfo]) => { diff --git a/js/src/dapps/registry/addresses/actions.js b/js/src/dapps/registry/addresses/actions.js index dfd7d16a3..17975f9e6 100644 --- a/js/src/dapps/registry/addresses/actions.js +++ b/js/src/dapps/registry/addresses/actions.js @@ -22,12 +22,16 @@ export const fetch = () => (dispatch) => { return Promise .all([ api.eth.accounts(), - null // api.personal.accountsInfo() + api.personal.accountsInfo() ]) .then(([ accounts, data ]) => { - const addresses = accounts.map((address) => { - return { address, isAccount: true }; - }); + data = data || {}; + const addresses = Object.keys(data) + .filter((address) => data[address] && !data[address].meta.deleted) + .map((address) => ({ + ...data[address], address, + isAccount: accounts.includes(address) + })); dispatch(set(addresses)); }) .catch((error) => { diff --git a/js/src/dapps/signaturereg/services.js b/js/src/dapps/signaturereg/services.js index 7219ddff1..cab324f7e 100644 --- a/js/src/dapps/signaturereg/services.js +++ b/js/src/dapps/signaturereg/services.js @@ -50,7 +50,7 @@ export function attachInterface (callback) { .all([ registry.getAddress.call({}, [api.util.sha3('signaturereg'), 'A']), api.eth.accounts(), - null // api.personal.accountsInfo() + api.personal.accountsInfo() ]); }) .then(([address, addresses, accountsInfo]) => { diff --git a/js/src/dapps/tokenreg/Accounts/actions.js b/js/src/dapps/tokenreg/Accounts/actions.js index f093b5300..f501399c2 100644 --- a/js/src/dapps/tokenreg/Accounts/actions.js +++ b/js/src/dapps/tokenreg/Accounts/actions.js @@ -38,7 +38,7 @@ export const loadAccounts = () => (dispatch) => { Promise .all([ api.eth.accounts(), - null // api.personal.accountsInfo() + api.personal.accountsInfo() ]) .then(([ accounts, accountsInfo ]) => { accountsInfo = accountsInfo || {}; diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml index fd2e11f98..4d1d48025 100644 --- a/parity/cli/config.full.toml +++ b/parity/cli/config.full.toml @@ -41,13 +41,13 @@ disable = false port = 8545 interface = "local" cors = "null" -apis = ["web3", "eth", "net", "ethcore", "traces", "rpc"] +apis = ["web3", "eth", "net", "ethcore", "traces", "rpc", "personal_safe"] hosts = ["none"] [ipc] disable = false path = "$HOME/.parity/jsonrpc.ipc" -apis = ["web3", "eth", "net", "ethcore", "traces", "rpc"] +apis = ["web3", "eth", "net", "ethcore", "traces", "rpc", "personal_safe"] [dapps] disable = false diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 9290ab0f9..68411414f 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -145,7 +145,7 @@ usage! { or |c: &Config| otry!(c.rpc).interface.clone(), flag_jsonrpc_cors: Option = None, or |c: &Config| otry!(c.rpc).cors.clone().map(Some), - flag_jsonrpc_apis: String = "web3,eth,net,ethcore,traces,rpc", + flag_jsonrpc_apis: String = "web3,eth,net,ethcore,traces,rpc,personal_safe", or |c: &Config| otry!(c.rpc).apis.clone().map(|vec| vec.join(",")), flag_jsonrpc_hosts: String = "none", or |c: &Config| otry!(c.rpc).hosts.clone().map(|vec| vec.join(",")), @@ -155,7 +155,7 @@ usage! { or |c: &Config| otry!(c.ipc).disable.clone(), flag_ipc_path: String = "$HOME/.parity/jsonrpc.ipc", or |c: &Config| otry!(c.ipc).path.clone(), - flag_ipc_apis: String = "web3,eth,net,ethcore,traces,rpc", + flag_ipc_apis: String = "web3,eth,net,ethcore,traces,rpc,personal_safe", or |c: &Config| otry!(c.ipc).apis.clone().map(|vec| vec.join(",")), // DAPPS @@ -534,13 +534,13 @@ mod tests { flag_jsonrpc_port: 8545u16, flag_jsonrpc_interface: "local".into(), flag_jsonrpc_cors: Some("null".into()), - flag_jsonrpc_apis: "web3,eth,net,ethcore,traces,rpc".into(), + flag_jsonrpc_apis: "web3,eth,net,ethcore,traces,rpc,personal_safe".into(), flag_jsonrpc_hosts: "none".into(), // IPC flag_no_ipc: false, flag_ipc_path: "$HOME/.parity/jsonrpc.ipc".into(), - flag_ipc_apis: "web3,eth,net,ethcore,traces,rpc".into(), + flag_ipc_apis: "web3,eth,net,ethcore,traces,rpc,personal_safe".into(), // DAPPS flag_no_dapps: false, diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index bebdafd97..d96d875cf 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -107,7 +107,7 @@ API and Console Options: --jsonrpc-apis APIS Specify the APIs available through the JSONRPC interface. APIS is a comma-delimited list of API name. Possible name are web3, eth, net, personal, - ethcore, ethcore_set, traces, rpc. + ethcore, ethcore_set, traces, rpc, personal_safe. (default: {flag_jsonrpc_apis}). --jsonrpc-hosts HOSTS List of allowed Host header values. This option will validate the Host header sent by the browser, it diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index f6ccf16a3..491f58a1b 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -33,7 +33,8 @@ pub enum Api { Web3, Net, Eth, - Personal, + PersonalSafe, + PersonalAccounts, Signer, Ethcore, EthcoreSet, @@ -51,7 +52,8 @@ impl FromStr for Api { "web3" => Ok(Web3), "net" => Ok(Net), "eth" => Ok(Eth), - "personal" => Ok(Personal), + "personal" => Ok(PersonalAccounts), + "personal_safe" => Ok(PersonalSafe), "signer" => Ok(Signer), "ethcore" => Ok(Ethcore), "ethcore_set" => Ok(EthcoreSet), @@ -114,7 +116,8 @@ fn to_modules(apis: &[Api]) -> BTreeMap { Api::Web3 => ("web3", "1.0"), Api::Net => ("net", "1.0"), Api::Eth => ("eth", "1.0"), - Api::Personal => ("personal", "1.0"), + Api::PersonalSafe => ("personal_safe", "1.0"), + Api::PersonalAccounts => ("personal", "1.0"), Api::Signer => ("signer", "1.0"), Api::Ethcore => ("ethcore", "1.0"), Api::EthcoreSet => ("ethcore_set", "1.0"), @@ -131,11 +134,11 @@ impl ApiSet { match *self { ApiSet::List(ref apis) => apis.clone(), ApiSet::UnsafeContext => { - vec![Api::Web3, Api::Net, Api::Eth, Api::Ethcore, Api::Traces, Api::Rpc] + vec![Api::Web3, Api::Net, Api::Eth, Api::Ethcore, Api::Traces, Api::Rpc, Api::PersonalSafe] .into_iter().collect() }, ApiSet::SafeContext => { - vec![Api::Web3, Api::Net, Api::Eth, Api::Personal, Api::Signer, Api::Ethcore, Api::EthcoreSet, Api::Traces, Api::Rpc] + vec![Api::Web3, Api::Net, Api::Eth, Api::PersonalAccounts, Api::PersonalSafe, Api::Signer, Api::Ethcore, Api::EthcoreSet, Api::Traces, Api::Rpc] .into_iter().collect() }, } @@ -178,8 +181,11 @@ pub fn setup_rpc(server: T, deps: Arc, apis: ApiSet server.add_delegate(EthSigningUnsafeClient::new(&deps.client, &deps.secret_store, &deps.miner).to_delegate()); } }, - Api::Personal => { - server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner, deps.geth_compatibility).to_delegate()); + Api::PersonalAccounts => { + server.add_delegate(PersonalAccountsClient::new(&deps.secret_store, &deps.client, &deps.miner, deps.geth_compatibility).to_delegate()); + }, + Api::PersonalSafe => { + server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client).to_delegate()); }, Api::Signer => { server.add_delegate(SignerClient::new(&deps.secret_store, &deps.client, &deps.miner, &deps.signer_service).to_delegate()); @@ -224,7 +230,8 @@ mod test { assert_eq!(Api::Web3, "web3".parse().unwrap()); assert_eq!(Api::Net, "net".parse().unwrap()); assert_eq!(Api::Eth, "eth".parse().unwrap()); - assert_eq!(Api::Personal, "personal".parse().unwrap()); + assert_eq!(Api::PersonalAccounts, "personal".parse().unwrap()); + assert_eq!(Api::PersonalSafe, "personal_safe".parse().unwrap()); assert_eq!(Api::Signer, "signer".parse().unwrap()); assert_eq!(Api::Ethcore, "ethcore".parse().unwrap()); assert_eq!(Api::EthcoreSet, "ethcore_set".parse().unwrap()); @@ -245,14 +252,14 @@ mod test { #[test] fn test_api_set_unsafe_context() { - let expected = vec![Api::Web3, Api::Net, Api::Eth, Api::Ethcore, Api::Traces, Api::Rpc] + let expected = vec![Api::Web3, Api::Net, Api::Eth, Api::Ethcore, Api::Traces, Api::Rpc, Api::PersonalSafe] .into_iter().collect(); assert_eq!(ApiSet::UnsafeContext.list_apis(), expected); } #[test] fn test_api_set_safe_context() { - let expected = vec![Api::Web3, Api::Net, Api::Eth, Api::Personal, Api::Signer, Api::Ethcore, Api::EthcoreSet, Api::Traces, Api::Rpc] + let expected = vec![Api::Web3, Api::Net, Api::Eth, Api::PersonalAccounts, Api::PersonalSafe, Api::Signer, Api::Ethcore, Api::EthcoreSet, Api::Traces, Api::Rpc] .into_iter().collect(); assert_eq!(ApiSet::SafeContext.list_apis(), expected); } diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index bf2013d99..c108f0b6b 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -32,6 +32,7 @@ mod ethcore; mod ethcore_set; mod net; mod personal; +mod personal_accounts; mod personal_signer; mod rpc; mod traces; @@ -43,6 +44,7 @@ pub use self::eth_filter::EthFilterClient; pub use self::eth_signing::{EthSigningUnsafeClient, EthSigningQueueClient}; pub use self::net::NetClient; pub use self::personal::PersonalClient; +pub use self::personal_accounts::PersonalAccountsClient; pub use self::personal_signer::SignerClient; pub use self::ethcore::EthcoreClient; pub use self::ethcore_set::EthcoreSetClient; diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index 0d6b63240..aacf90c91 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -17,34 +17,26 @@ //! Account management (personal) rpc implementation use std::sync::{Arc, Weak}; use std::collections::{BTreeMap}; -use util::{Address}; use jsonrpc_core::*; -use ethkey::{Brain, Generator}; use v1::traits::Personal; -use v1::types::{H160 as RpcH160, TransactionRequest}; +use v1::types::{H160 as RpcH160}; use v1::helpers::errors; use v1::helpers::params::expect_no_params; -use v1::helpers::dispatch::sign_and_dispatch; use ethcore::account_provider::AccountProvider; use ethcore::client::MiningBlockChainClient; -use ethcore::miner::MinerService; /// Account management (personal) rpc implementation. -pub struct PersonalClient where C: MiningBlockChainClient, M: MinerService { +pub struct PersonalClient where C: MiningBlockChainClient { accounts: Weak, client: Weak, - miner: Weak, - allow_perm_unlock: bool, } -impl PersonalClient where C: MiningBlockChainClient, M: MinerService { +impl PersonalClient where C: MiningBlockChainClient { /// Creates new PersonalClient - pub fn new(store: &Arc, client: &Arc, miner: &Arc, allow_perm_unlock: bool) -> Self { + pub fn new(store: &Arc, client: &Arc) -> Self { PersonalClient { accounts: Arc::downgrade(store), client: Arc::downgrade(client), - miner: Arc::downgrade(miner), - allow_perm_unlock: allow_perm_unlock, } } @@ -55,7 +47,7 @@ impl PersonalClient where C: MiningBlockChainClient, M: MinerService } } -impl Personal for PersonalClient where C: MiningBlockChainClient, M: MinerService { +impl Personal for PersonalClient where C: MiningBlockChainClient { fn accounts(&self, params: Params) -> Result { try!(self.active()); @@ -66,125 +58,6 @@ impl Personal for PersonalClient where C: MiningBl Ok(to_value(&accounts.into_iter().map(Into::into).collect::>())) } - fn new_account(&self, params: Params) -> Result { - try!(self.active()); - from_params::<(String, )>(params).and_then( - |(pass, )| { - let store = take_weak!(self.accounts); - match store.new_account(&pass) { - Ok(address) => Ok(to_value(&RpcH160::from(address))), - Err(e) => Err(errors::account("Could not create account.", e)), - } - } - ) - } - - fn new_account_from_phrase(&self, params: Params) -> Result { - try!(self.active()); - from_params::<(String, String, )>(params).and_then( - |(phrase, pass, )| { - let store = take_weak!(self.accounts); - match store.insert_account(*Brain::new(phrase).generate().unwrap().secret(), &pass) { - Ok(address) => Ok(to_value(&RpcH160::from(address))), - Err(e) => Err(errors::account("Could not create account.", e)), - } - } - ) - } - - fn new_account_from_wallet(&self, params: Params) -> Result { - try!(self.active()); - from_params::<(String, String, )>(params).and_then( - |(json, pass, )| { - let store = take_weak!(self.accounts); - match store.import_presale(json.as_bytes(), &pass).or_else(|_| store.import_wallet(json.as_bytes(), &pass)) { - Ok(address) => Ok(to_value(&RpcH160::from(address))), - Err(e) => Err(errors::account("Could not create account.", e)), - } - } - ) - } - - fn unlock_account(&self, params: Params) -> Result { - try!(self.active()); - from_params::<(RpcH160, String, Option)>(params).and_then( - |(account, account_pass, duration)| { - let account: Address = account.into(); - let store = take_weak!(self.accounts); - let r = match (self.allow_perm_unlock, duration) { - (false, _) => store.unlock_account_temporarily(account, account_pass), - (true, Some(0)) => store.unlock_account_permanently(account, account_pass), - (true, Some(d)) => store.unlock_account_timed(account, account_pass, d as u32 * 1000), - (true, None) => store.unlock_account_timed(account, account_pass, 300_000), - }; - match r { - Ok(_) => Ok(Value::Bool(true)), - Err(_) => Ok(Value::Bool(false)), - } - } - ) - } - - fn test_password(&self, params: Params) -> Result { - try!(self.active()); - from_params::<(RpcH160, String)>(params).and_then( - |(account, password)| { - let account: Address = account.into(); - take_weak!(self.accounts) - .test_password(&account, password) - .map(|b| Value::Bool(b)) - .map_err(|e| errors::account("Could not fetch account info.", e)) - } - ) - } - - fn change_password(&self, params: Params) -> Result { - try!(self.active()); - from_params::<(RpcH160, String, String)>(params).and_then( - |(account, password, new_password)| { - let account: Address = account.into(); - take_weak!(self.accounts) - .change_password(&account, password, new_password) - .map(|_| Value::Null) - .map_err(|e| errors::account("Could not fetch account info.", e)) - } - ) - } - - fn sign_and_send_transaction(&self, params: Params) -> Result { - try!(self.active()); - from_params::<(TransactionRequest, String)>(params) - .and_then(|(request, password)| { - sign_and_dispatch( - &*take_weak!(self.client), - &*take_weak!(self.miner), - &*take_weak!(self.accounts), - request.into(), - Some(password) - ) - }) - } - - fn set_account_name(&self, params: Params) -> Result { - try!(self.active()); - let store = take_weak!(self.accounts); - from_params::<(RpcH160, String)>(params).and_then(|(addr, name)| { - let addr: Address = addr.into(); - store.set_account_name(addr.clone(), name.clone()).or_else(|_| store.set_address_name(addr, name)).expect("set_address_name always returns Ok; qed"); - Ok(Value::Null) - }) - } - - fn set_account_meta(&self, params: Params) -> Result { - try!(self.active()); - let store = take_weak!(self.accounts); - from_params::<(RpcH160, String)>(params).and_then(|(addr, meta)| { - let addr: Address = addr.into(); - store.set_account_meta(addr.clone(), meta.clone()).or_else(|_| store.set_address_meta(addr, meta)).expect("set_address_meta always returns Ok; qed"); - Ok(Value::Null) - }) - } - fn accounts_info(&self, params: Params) -> Result { try!(self.active()); try!(expect_no_params(params)); @@ -204,21 +77,4 @@ impl Personal for PersonalClient where C: MiningBl (format!("0x{}", a.hex()), Value::Object(m)) }).collect::>())) } - - fn geth_accounts(&self, params: Params) -> Result { - try!(self.active()); - try!(expect_no_params(params)); - let store = take_weak!(self.accounts); - Ok(to_value(&store.list_geth_accounts(false).into_iter().map(Into::into).collect::>())) - } - - fn import_geth_accounts(&self, params: Params) -> Result { - from_params::<(Vec,)>(params).and_then(|(addresses,)| { - let store = take_weak!(self.accounts); - Ok(to_value(&try!(store - .import_geth_accounts(addresses.into_iter().map(Into::into).collect(), false) - .map_err(|e| errors::account("Couldn't import Geth accounts", e)) - ).into_iter().map(Into::into).collect::>())) - }) - } } diff --git a/rpc/src/v1/impls/personal_accounts.rs b/rpc/src/v1/impls/personal_accounts.rs new file mode 100644 index 000000000..0fa236845 --- /dev/null +++ b/rpc/src/v1/impls/personal_accounts.rs @@ -0,0 +1,194 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Account management (personal) rpc implementation +use std::sync::{Arc, Weak}; +use util::{Address}; +use jsonrpc_core::*; +use ethkey::{Brain, Generator}; +use v1::traits::PersonalAccounts; +use v1::types::{H160 as RpcH160, TransactionRequest}; +use v1::helpers::errors; +use v1::helpers::params::expect_no_params; +use v1::helpers::dispatch::sign_and_dispatch; +use ethcore::account_provider::AccountProvider; +use ethcore::client::MiningBlockChainClient; +use ethcore::miner::MinerService; + +/// Account management (personal) rpc implementation. +pub struct PersonalAccountsClient where C: MiningBlockChainClient, M: MinerService { + accounts: Weak, + client: Weak, + miner: Weak, + allow_perm_unlock: bool, +} + +impl PersonalAccountsClient where C: MiningBlockChainClient, M: MinerService { + /// Creates new PersonalClient + pub fn new(store: &Arc, client: &Arc, miner: &Arc, allow_perm_unlock: bool) -> Self { + PersonalAccountsClient { + accounts: Arc::downgrade(store), + client: Arc::downgrade(client), + miner: Arc::downgrade(miner), + allow_perm_unlock: allow_perm_unlock, + } + } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } +} + +impl PersonalAccounts for PersonalAccountsClient where C: MiningBlockChainClient, M: MinerService { + + fn new_account(&self, params: Params) -> Result { + try!(self.active()); + from_params::<(String, )>(params).and_then( + |(pass, )| { + let store = take_weak!(self.accounts); + match store.new_account(&pass) { + Ok(address) => Ok(to_value(&RpcH160::from(address))), + Err(e) => Err(errors::account("Could not create account.", e)), + } + } + ) + } + + fn new_account_from_phrase(&self, params: Params) -> Result { + try!(self.active()); + from_params::<(String, String, )>(params).and_then( + |(phrase, pass, )| { + let store = take_weak!(self.accounts); + match store.insert_account(*Brain::new(phrase).generate().unwrap().secret(), &pass) { + Ok(address) => Ok(to_value(&RpcH160::from(address))), + Err(e) => Err(errors::account("Could not create account.", e)), + } + } + ) + } + + fn new_account_from_wallet(&self, params: Params) -> Result { + try!(self.active()); + from_params::<(String, String, )>(params).and_then( + |(json, pass, )| { + let store = take_weak!(self.accounts); + match store.import_presale(json.as_bytes(), &pass).or_else(|_| store.import_wallet(json.as_bytes(), &pass)) { + Ok(address) => Ok(to_value(&RpcH160::from(address))), + Err(e) => Err(errors::account("Could not create account.", e)), + } + } + ) + } + + fn unlock_account(&self, params: Params) -> Result { + try!(self.active()); + from_params::<(RpcH160, String, Option)>(params).and_then( + |(account, account_pass, duration)| { + let account: Address = account.into(); + let store = take_weak!(self.accounts); + let r = match (self.allow_perm_unlock, duration) { + (false, _) => store.unlock_account_temporarily(account, account_pass), + (true, Some(0)) => store.unlock_account_permanently(account, account_pass), + (true, Some(d)) => store.unlock_account_timed(account, account_pass, d as u32 * 1000), + (true, None) => store.unlock_account_timed(account, account_pass, 300_000), + }; + match r { + Ok(_) => Ok(Value::Bool(true)), + Err(_) => Ok(Value::Bool(false)), + } + } + ) + } + + fn test_password(&self, params: Params) -> Result { + try!(self.active()); + from_params::<(RpcH160, String)>(params).and_then( + |(account, password)| { + let account: Address = account.into(); + take_weak!(self.accounts) + .test_password(&account, password) + .map(|b| Value::Bool(b)) + .map_err(|e| errors::account("Could not fetch account info.", e)) + } + ) + } + + fn change_password(&self, params: Params) -> Result { + try!(self.active()); + from_params::<(RpcH160, String, String)>(params).and_then( + |(account, password, new_password)| { + let account: Address = account.into(); + take_weak!(self.accounts) + .change_password(&account, password, new_password) + .map(|_| Value::Null) + .map_err(|e| errors::account("Could not fetch account info.", e)) + } + ) + } + + fn sign_and_send_transaction(&self, params: Params) -> Result { + try!(self.active()); + from_params::<(TransactionRequest, String)>(params) + .and_then(|(request, password)| { + sign_and_dispatch( + &*take_weak!(self.client), + &*take_weak!(self.miner), + &*take_weak!(self.accounts), + request.into(), + Some(password) + ) + }) + } + + fn set_account_name(&self, params: Params) -> Result { + try!(self.active()); + let store = take_weak!(self.accounts); + from_params::<(RpcH160, String)>(params).and_then(|(addr, name)| { + let addr: Address = addr.into(); + store.set_account_name(addr.clone(), name.clone()).or_else(|_| store.set_address_name(addr, name)).expect("set_address_name always returns Ok; qed"); + Ok(Value::Null) + }) + } + + fn set_account_meta(&self, params: Params) -> Result { + try!(self.active()); + let store = take_weak!(self.accounts); + from_params::<(RpcH160, String)>(params).and_then(|(addr, meta)| { + let addr: Address = addr.into(); + store.set_account_meta(addr.clone(), meta.clone()).or_else(|_| store.set_address_meta(addr, meta)).expect("set_address_meta always returns Ok; qed"); + Ok(Value::Null) + }) + } + + fn import_geth_accounts(&self, params: Params) -> Result { + from_params::<(Vec,)>(params).and_then(|(addresses,)| { + let store = take_weak!(self.accounts); + Ok(to_value(&try!(store + .import_geth_accounts(addresses.into_iter().map(Into::into).collect(), false) + .map_err(|e| errors::account("Couldn't import Geth accounts", e)) + ).into_iter().map(Into::into).collect::>())) + }) + } + + fn geth_accounts(&self, params: Params) -> Result { + try!(self.active()); + try!(expect_no_params(params)); + let store = take_weak!(self.accounts); + Ok(to_value(&store.list_geth_accounts(false).into_iter().map(Into::into).collect::>())) + } +} diff --git a/rpc/src/v1/mod.rs b/rpc/src/v1/mod.rs index 5ba302cea..24560160c 100644 --- a/rpc/src/v1/mod.rs +++ b/rpc/src/v1/mod.rs @@ -26,6 +26,6 @@ pub mod traits; pub mod tests; pub mod types; -pub use self::traits::{Web3, Eth, EthFilter, EthSigning, Personal, PersonalSigner, Net, Ethcore, EthcoreSet, Traces, Rpc}; +pub use self::traits::{Web3, Eth, EthFilter, EthSigning, Personal, PersonalAccounts, PersonalSigner, Net, Ethcore, EthcoreSet, Traces, Rpc}; pub use self::impls::*; pub use self::helpers::{SigningQueue, SignerService, ConfirmationsQueue, NetworkSettings, block_import}; diff --git a/rpc/src/v1/tests/mocked/personal.rs b/rpc/src/v1/tests/mocked/personal.rs index 91e1ef0f5..2dd186cca 100644 --- a/rpc/src/v1/tests/mocked/personal.rs +++ b/rpc/src/v1/tests/mocked/personal.rs @@ -19,7 +19,7 @@ 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, PersonalAccountsClient, PersonalAccounts, Personal}; use v1::tests::helpers::TestMinerService; use ethcore::client::TestBlockChainClient; use ethcore::transaction::{Action, Transaction}; @@ -50,10 +50,12 @@ fn setup() -> PersonalTester { let accounts = accounts_provider(); let client = blockchain_client(); let miner = miner_service(); - let personal = PersonalClient::new(&accounts, &client, &miner, false); + let personal = PersonalClient::new(&accounts, &client); + let personal_accounts = PersonalAccountsClient::new(&accounts, &client, &miner, false); let io = IoHandler::new(); io.add_delegate(personal.to_delegate()); + io.add_delegate(personal_accounts.to_delegate()); let tester = PersonalTester { accounts: accounts, diff --git a/rpc/src/v1/traits/mod.rs b/rpc/src/v1/traits/mod.rs index e804c5553..ea0834463 100644 --- a/rpc/src/v1/traits/mod.rs +++ b/rpc/src/v1/traits/mod.rs @@ -30,7 +30,7 @@ pub use self::web3::Web3; pub use self::eth::{Eth, EthFilter}; pub use self::eth_signing::EthSigning; pub use self::net::Net; -pub use self::personal::{Personal, PersonalSigner}; +pub use self::personal::{Personal, PersonalAccounts, PersonalSigner}; pub use self::ethcore::Ethcore; pub use self::ethcore_set::EthcoreSet; pub use self::traces::Traces; diff --git a/rpc/src/v1/traits/personal.rs b/rpc/src/v1/traits/personal.rs index 2114131d4..92a9df6eb 100644 --- a/rpc/src/v1/traits/personal.rs +++ b/rpc/src/v1/traits/personal.rs @@ -18,12 +18,28 @@ use std::sync::Arc; use jsonrpc_core::*; -/// Personal rpc interface. +/// Personal rpc interface. Safe (read-only) functions. pub trait Personal: Sized + Send + Sync + 'static { /// Lists all stored accounts fn accounts(&self, _: Params) -> Result; + /// Returns accounts information. + fn accounts_info(&self, _: Params) -> Result; + + /// Should be used to convert object to io delegate. + fn to_delegate(self) -> IoDelegate { + let mut delegate = IoDelegate::new(Arc::new(self)); + delegate.add_method("personal_listAccounts", Personal::accounts); + delegate.add_method("personal_accountsInfo", Personal::accounts_info); + + delegate + } +} + +/// Personal rpc methods altering stored accounts or their settings. +pub trait PersonalAccounts: Sized + Send + Sync + 'static { + /// Creates new account (it becomes new current unlocked account) /// Param is the password for the account. fn new_account(&self, _: Params) -> Result; @@ -56,31 +72,26 @@ pub trait Personal: Sized + Send + Sync + 'static { /// Set an account's metadata string. fn set_account_meta(&self, _: Params) -> Result; - /// Returns accounts information. - fn accounts_info(&self, _: Params) -> Result; + /// Imports a number of Geth accounts, with the list provided as the argument. + fn import_geth_accounts(&self, _: Params) -> Result; /// Returns the accounts available for importing from Geth. fn geth_accounts(&self, _: Params) -> Result; - /// Imports a number of Geth accounts, with the list provided as the argument. - fn import_geth_accounts(&self, _: Params) -> Result; - /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { let mut delegate = IoDelegate::new(Arc::new(self)); - delegate.add_method("personal_listAccounts", Personal::accounts); - delegate.add_method("personal_newAccount", Personal::new_account); - delegate.add_method("personal_newAccountFromPhrase", Personal::new_account_from_phrase); - delegate.add_method("personal_newAccountFromWallet", Personal::new_account_from_wallet); - delegate.add_method("personal_unlockAccount", Personal::unlock_account); - delegate.add_method("personal_testPassword", Personal::test_password); - delegate.add_method("personal_changePassword", Personal::change_password); - delegate.add_method("personal_signAndSendTransaction", Personal::sign_and_send_transaction); - delegate.add_method("personal_setAccountName", Personal::set_account_name); - delegate.add_method("personal_setAccountMeta", Personal::set_account_meta); - delegate.add_method("personal_accountsInfo", Personal::accounts_info); - delegate.add_method("personal_listGethAccounts", Personal::geth_accounts); - delegate.add_method("personal_importGethAccounts", Personal::import_geth_accounts); + delegate.add_method("personal_newAccount", PersonalAccounts::new_account); + delegate.add_method("personal_newAccountFromPhrase", PersonalAccounts::new_account_from_phrase); + delegate.add_method("personal_newAccountFromWallet", PersonalAccounts::new_account_from_wallet); + delegate.add_method("personal_unlockAccount", PersonalAccounts::unlock_account); + delegate.add_method("personal_testPassword", PersonalAccounts::test_password); + delegate.add_method("personal_changePassword", PersonalAccounts::change_password); + delegate.add_method("personal_signAndSendTransaction", PersonalAccounts::sign_and_send_transaction); + delegate.add_method("personal_setAccountName", PersonalAccounts::set_account_name); + delegate.add_method("personal_setAccountMeta", PersonalAccounts::set_account_meta); + delegate.add_method("personal_importGethAccounts", PersonalAccounts::import_geth_accounts); + delegate.add_method("personal_listGethAccounts", PersonalAccounts::geth_accounts); delegate } @@ -108,6 +119,7 @@ pub trait PersonalSigner: Sized + Send + Sync + 'static { delegate.add_method("personal_confirmRequest", PersonalSigner::confirm_request); delegate.add_method("personal_rejectRequest", PersonalSigner::reject_request); delegate.add_method("personal_generateAuthorizationToken", PersonalSigner::generate_token); + delegate } }