From af5ed8b5f7af5b576fed3181dc03c45fdee50f1b Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Fri, 4 Mar 2016 20:10:07 +0300 Subject: [PATCH 1/6] rpc-signing-extend --- parity/main.rs | 1 + rpc/src/v1/impls/eth.rs | 17 ++++++++++++++++- rpc/src/v1/impls/personal.rs | 21 ++++++++++----------- rpc/src/v1/types/bytes.rs | 32 ++++++++++++++++++++++++++++++-- rpc/src/v1/types/mod.rs.in | 2 ++ rpc/src/v1/types/transaction.rs | 13 +++++++++++++ 6 files changed, 72 insertions(+), 14 deletions(-) diff --git a/parity/main.rs b/parity/main.rs index b991f36cd..91a884beb 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -157,6 +157,7 @@ fn setup_rpc_server(client: Arc, sync: Arc, url: &str, cors_dom server.add_delegate(EthClient::new(&client, &sync).to_delegate()); server.add_delegate(EthFilterClient::new(&client).to_delegate()); server.add_delegate(NetClient::new(&sync).to_delegate()); + server.add_delegate(PersonalClient::new(&client).to_delegate()); server.start_async(url, cors_domain); } diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 2313d5114..16b68f90f 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -29,7 +29,7 @@ use ethcore::views::*; use ethcore::ethereum::Ethash; use ethcore::ethereum::denominations::shannon; use v1::traits::{Eth, EthFilter}; -use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, OptionalValue, Index, Filter, Log}; +use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, OptionalValue, Index, Filter, Log}; use v1::helpers::{PollFilter, PollManager}; /// Eth rpc implementation. @@ -253,6 +253,21 @@ impl Eth for EthClient { to_value(&true) }) } + + fn send_transaction(&self, params: Params) -> Result { + from_params::<(TransactionRequest, )>(params) + .and_then(|(transaction_request, )| { + let client = take_weak!(self.client); + let store = client.secret_store().read().unwrap(); + match store.account_secret(&transaction_request.from) { + Ok(_) => { + // todo: actually sign and push to queue transaction here + Ok(Value::Bool(true)) + }, + Err(_) => { Ok(Value::Bool(false ))} + } + }) + } } /// Eth filter rpc implementation. diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index 48e1b1c6a..a2788b9d9 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -18,28 +18,27 @@ use std::sync::{Arc, Weak}; use jsonrpc_core::*; use v1::traits::Personal; -use util::keys::store::*; use util::Address; -use std::sync::RwLock; +use ethcore::client::Client; /// Account management (personal) rpc implementation. pub struct PersonalClient { - secret_store: Weak>, + client: Weak, } impl PersonalClient { /// Creates new PersonalClient - pub fn new(store: &Arc>) -> Self { + pub fn new(client: &Arc) -> Self { PersonalClient { - secret_store: Arc::downgrade(store), + client: Arc::downgrade(client), } } } impl Personal for PersonalClient { fn accounts(&self, _: Params) -> Result { - let store_wk = take_weak!(self.secret_store); - let store = store_wk.read().unwrap(); + let client = take_weak!(self.client); + let store = client.secret_store().read().unwrap(); match store.accounts() { Ok(account_list) => { Ok(Value::Array(account_list.iter() @@ -54,8 +53,8 @@ impl Personal for PersonalClient { fn new_account(&self, params: Params) -> Result { from_params::<(String, )>(params).and_then( |(pass, )| { - let store_wk = take_weak!(self.secret_store); - let mut store = store_wk.write().unwrap(); + let client = take_weak!(self.client); + let mut store = client.secret_store().write().unwrap(); match store.new_account(&pass) { Ok(address) => Ok(Value::String(format!("{:?}", address))), Err(_) => Err(Error::internal_error()) @@ -67,8 +66,8 @@ impl Personal for PersonalClient { fn unlock_account(&self, params: Params) -> Result { from_params::<(Address, String, u64)>(params).and_then( |(account, account_pass, _)|{ - let store_wk = take_weak!(self.secret_store); - let store = store_wk.read().unwrap(); + let client = take_weak!(self.client); + let store = client.secret_store().read().unwrap(); match store.unlock_account(&account, &account_pass) { Ok(_) => Ok(Value::Bool(true)), Err(_) => Ok(Value::Bool(false)), diff --git a/rpc/src/v1/types/bytes.rs b/rpc/src/v1/types/bytes.rs index f09f24e4d..44809ac70 100644 --- a/rpc/src/v1/types/bytes.rs +++ b/rpc/src/v1/types/bytes.rs @@ -15,7 +15,9 @@ // along with Parity. If not, see . use rustc_serialize::hex::ToHex; -use serde::{Serialize, Serializer}; +use serde::{Serialize, Serializer, Deserialize, Deserializer, Error}; +use serde::de::Visitor; +use util::common::FromHex; /// Wrapper structure around vector of bytes. #[derive(Debug)] @@ -36,7 +38,7 @@ impl Default for Bytes { } impl Serialize for Bytes { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { let mut serialized = "0x".to_owned(); serialized.push_str(self.0.to_hex().as_ref()); @@ -44,6 +46,32 @@ impl Serialize for Bytes { } } +impl Deserialize for Bytes { + fn deserialize(deserializer: &mut D) -> Result + where D: Deserializer { + deserializer.deserialize(BytesVisitor) + } +} + +struct BytesVisitor; + +impl Visitor for BytesVisitor { + type Value = Bytes; + + fn visit_str(&mut self, value: &str) -> Result where E: Error { + if value.len() >= 2 && &value[0..2] == "0x" { + Ok(Bytes::new(FromHex::from_hex(&value[2..]).unwrap_or_else(|_| vec![]))) + } else { + Err(Error::custom("invalid hex")) + } + } + + fn visit_string(&mut self, value: String) -> Result where E: Error { + self.visit_str(value.as_ref()) + } +} + + #[cfg(test)] mod tests { use super::*; diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index 34c1f1cff..2b2390ecb 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -33,3 +33,5 @@ pub use self::log::Log; pub use self::optionals::OptionalValue; pub use self::sync::{SyncStatus, SyncInfo}; pub use self::transaction::Transaction; +pub use self::transaction::TransactionRequest; + diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 232cf0bf3..7d40d8a49 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -17,6 +17,7 @@ use util::numbers::*; use ethcore::transaction::{LocalizedTransaction, Action}; use v1::types::{Bytes, OptionalValue}; +use serde::{Deserializer, Error}; #[derive(Debug, Default, Serialize)] pub struct Transaction { @@ -37,6 +38,18 @@ pub struct Transaction { pub input: Bytes } +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct TransactionRequest { + pub from: Address, + pub to: Option
, + #[serde(rename="gasPrice")] + pub gas_price: Option, + pub gas: Option, + pub value: Option, + pub data: Bytes, + pub nonce: Option, +} + impl From for Transaction { fn from(t: LocalizedTransaction) -> Transaction { Transaction { From 1aaae7b55333625c572fc77cc722ea98c2517825 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Sat, 5 Mar 2016 16:42:02 +0300 Subject: [PATCH 2/6] [ci skip] codegen bug --- rpc/src/v1/types/transaction.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 7d40d8a49..c24bcd08f 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -18,6 +18,8 @@ use util::numbers::*; use ethcore::transaction::{LocalizedTransaction, Action}; use v1::types::{Bytes, OptionalValue}; use serde::{Deserializer, Error}; +use ethcore; +use util; #[derive(Debug, Default, Serialize)] pub struct Transaction { @@ -50,6 +52,22 @@ pub struct TransactionRequest { pub nonce: Option, } +impl TransactionRequest { + fn to_eth(self) -> (ethcore::transaction::Transaction, Address) { + (ethcore::transaction::Transaction { + nonce: self.nonce.unwrap_or(U256::zero()), + action: match self.to { + None => ethcore::transaction::Action::Create, + Some(addr) => ethcore::transaction::Action::Call(addr) + }, + gas: self.gas.unwrap_or(U256::zero()), + gas_price: self.gas_price.unwrap_or(U256::zero()), + value: self.value.unwrap_or(U256::zero()), + data: { let (ref x) = self.data; x } + }, self.from) + } +} + impl From for Transaction { fn from(t: LocalizedTransaction) -> Transaction { Transaction { From bb8a79f18c44575d59ce68d4a9b5c03009679585 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Sat, 5 Mar 2016 18:29:01 +0300 Subject: [PATCH 3/6] finalizing --- rpc/src/v1/impls/eth.rs | 12 ++++++++---- rpc/src/v1/types/bytes.rs | 1 + rpc/src/v1/types/transaction.rs | 6 +++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 16b68f90f..91ccaa05a 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -260,11 +260,15 @@ impl Eth for EthClient { let client = take_weak!(self.client); let store = client.secret_store().read().unwrap(); match store.account_secret(&transaction_request.from) { - Ok(_) => { - // todo: actually sign and push to queue transaction here - Ok(Value::Bool(true)) + Ok(secret) => { + let sync = take_weak!(self.sync); + let (transaction, _) = transaction_request.to_eth(); + let signed_transaction = transaction.sign(&secret); + let hash = signed_transaction.hash(); + sync.insert_transaction(signed_transaction); + to_value(&hash) }, - Err(_) => { Ok(Value::Bool(false ))} + Err(_) => { to_value(&U256::zero()) } } }) } diff --git a/rpc/src/v1/types/bytes.rs b/rpc/src/v1/types/bytes.rs index 44809ac70..466fbebde 100644 --- a/rpc/src/v1/types/bytes.rs +++ b/rpc/src/v1/types/bytes.rs @@ -28,6 +28,7 @@ impl Bytes { pub fn new(bytes: Vec) -> Bytes { Bytes(bytes) } + pub fn to_vec(self) -> Vec { let Bytes(x) = self; x } } impl Default for Bytes { diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index c24bcd08f..17b42cfcf 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -19,7 +19,6 @@ use ethcore::transaction::{LocalizedTransaction, Action}; use v1::types::{Bytes, OptionalValue}; use serde::{Deserializer, Error}; use ethcore; -use util; #[derive(Debug, Default, Serialize)] pub struct Transaction { @@ -53,7 +52,8 @@ pub struct TransactionRequest { } impl TransactionRequest { - fn to_eth(self) -> (ethcore::transaction::Transaction, Address) { + /// maps transaction request to the transaction that can be signed and inserted + pub fn to_eth(self) -> (ethcore::transaction::Transaction, Address) { (ethcore::transaction::Transaction { nonce: self.nonce.unwrap_or(U256::zero()), action: match self.to { @@ -63,7 +63,7 @@ impl TransactionRequest { gas: self.gas.unwrap_or(U256::zero()), gas_price: self.gas_price.unwrap_or(U256::zero()), value: self.value.unwrap_or(U256::zero()), - data: { let (ref x) = self.data; x } + data: self.data.to_vec() }, self.from) } } From 094ae4e9f9a77708c15751afa17c4835e1fb16a0 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Thu, 10 Mar 2016 19:15:10 +0400 Subject: [PATCH 4/6] personal is back to the master ver --- rpc/src/v1/impls/personal.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index a2788b9d9..48e1b1c6a 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -18,27 +18,28 @@ use std::sync::{Arc, Weak}; use jsonrpc_core::*; use v1::traits::Personal; +use util::keys::store::*; use util::Address; -use ethcore::client::Client; +use std::sync::RwLock; /// Account management (personal) rpc implementation. pub struct PersonalClient { - client: Weak, + secret_store: Weak>, } impl PersonalClient { /// Creates new PersonalClient - pub fn new(client: &Arc) -> Self { + pub fn new(store: &Arc>) -> Self { PersonalClient { - client: Arc::downgrade(client), + secret_store: Arc::downgrade(store), } } } impl Personal for PersonalClient { fn accounts(&self, _: Params) -> Result { - let client = take_weak!(self.client); - let store = client.secret_store().read().unwrap(); + let store_wk = take_weak!(self.secret_store); + let store = store_wk.read().unwrap(); match store.accounts() { Ok(account_list) => { Ok(Value::Array(account_list.iter() @@ -53,8 +54,8 @@ impl Personal for PersonalClient { fn new_account(&self, params: Params) -> Result { from_params::<(String, )>(params).and_then( |(pass, )| { - let client = take_weak!(self.client); - let mut store = client.secret_store().write().unwrap(); + let store_wk = take_weak!(self.secret_store); + let mut store = store_wk.write().unwrap(); match store.new_account(&pass) { Ok(address) => Ok(Value::String(format!("{:?}", address))), Err(_) => Err(Error::internal_error()) @@ -66,8 +67,8 @@ impl Personal for PersonalClient { fn unlock_account(&self, params: Params) -> Result { from_params::<(Address, String, u64)>(params).and_then( |(account, account_pass, _)|{ - let client = take_weak!(self.client); - let store = client.secret_store().read().unwrap(); + let store_wk = take_weak!(self.secret_store); + let store = store_wk.read().unwrap(); match store.unlock_account(&account, &account_pass) { Ok(_) => Ok(Value::Bool(true)), Err(_) => Ok(Value::Bool(false)), From 5571503c224b5d1b185243d7f5e6f1f1bc3a6856 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Thu, 10 Mar 2016 20:18:01 +0400 Subject: [PATCH 5/6] traitified secret store --- rpc/src/v1/impls/eth.rs | 16 +++++++++------- util/src/keys/store.rs | 22 ++++++++++++++++++---- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 4a8461c45..97d248ef6 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -30,20 +30,23 @@ use ethcore::ethereum::denominations::shannon; use v1::traits::{Eth, EthFilter}; use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, OptionalValue, Index, Filter, Log}; use v1::helpers::{PollFilter, PollManager}; +use util::keys::store::AccountProvider; /// Eth rpc implementation. -pub struct EthClient where C: BlockChainClient, S: SyncStatusProvider { +pub struct EthClient where C: BlockChainClient, S: SyncStatusProvider, A: AccountProvider { client: Weak, sync: Weak, + accounts: Weak, hashrates: RwLock>, } -impl EthClient where C: BlockChainClient, S: SyncStatusProvider { +impl EthClient where C: BlockChainClient, S: SyncStatusProvider, A: AccountProvider { /// Creates new EthClient. - pub fn new(client: &Arc, sync: &Arc) -> Self { + pub fn new(client: &Arc, sync: &Arc, accounts: &Arc) -> Self { EthClient { client: Arc::downgrade(client), sync: Arc::downgrade(sync), + accounts: Arc::downgrade(accounts), hashrates: RwLock::new(HashMap::new()), } } @@ -94,7 +97,7 @@ impl EthClient where C: BlockChainClient, S: SyncStatusProvider { } } -impl Eth for EthClient where C: BlockChainClient + 'static, S: SyncStatusProvider + 'static { +impl Eth for EthClient where C: BlockChainClient + 'static, S: SyncStatusProvider + 'static, A: AccountProvider + 'static { fn protocol_version(&self, params: Params) -> Result { match params { Params::None => to_value(&U256::from(take_weak!(self.sync).status().protocol_version)), @@ -256,9 +259,8 @@ impl Eth for EthClient where C: BlockChainClient + 'static, S: SyncS fn send_transaction(&self, params: Params) -> Result { from_params::<(TransactionRequest, )>(params) .and_then(|(transaction_request, )| { - let client = take_weak!(self.client); - let store = client.secret_store().read().unwrap(); - match store.account_secret(&transaction_request.from) { + let accounts = take_weak!(self.accounts); + match accounts.account_secret(&transaction_request.from) { Ok(secret) => { let sync = take_weak!(self.sync); let (transaction, _) = transaction_request.to_eth(); diff --git a/util/src/keys/store.rs b/util/src/keys/store.rs index dcc165259..9ea00cbba 100644 --- a/util/src/keys/store.rs +++ b/util/src/keys/store.rs @@ -78,6 +78,18 @@ struct AccountUnlock { expires: DateTime, } +/// Basic account management trait +pub trait AccountProvider : Send + Sync { + /// Unlocks account with the password provided + fn unlock_account(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError>; + /// Creates account + fn new_account(&mut self, pass: &str) -> Result; + /// Returns secret for unlocked account + fn account_secret(&self, account: &Address) -> Result; + /// Returns secret for unlocked account + fn sign(&self, account: &Address, message: &H256) -> Result; +} + impl SecretStore { /// new instance of Secret Store in default home directory pub fn new() -> SecretStore { @@ -144,9 +156,11 @@ impl SecretStore { unlocks: RwLock::new(HashMap::new()), } } +} +impl AccountProvider for SecretStore { /// Unlocks account for use - pub fn unlock_account(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError> { + fn unlock_account(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError> { let secret_id = try!(self.account(&account).ok_or(EncryptedHashMapError::UnknownIdentifier)); let secret = try!(self.get(&secret_id, pass)); { @@ -160,7 +174,7 @@ impl SecretStore { } /// Creates new account - pub fn new_account(&mut self, pass: &str) -> Result { + fn new_account(&mut self, pass: &str) -> Result { let secret = H256::random(); let key_id = H128::random(); self.insert(key_id.clone(), secret, pass); @@ -173,7 +187,7 @@ impl SecretStore { } /// Signs message with unlocked account - pub fn sign(&self, account: &Address, message: &H256) -> Result { + fn sign(&self, account: &Address, message: &H256) -> Result { let read_lock = self.unlocks.read().unwrap(); let unlock = try!(read_lock.get(account).ok_or(SigningError::AccountNotUnlocked)); match crypto::KeyPair::from_secret(unlock.secret) { @@ -186,7 +200,7 @@ impl SecretStore { } /// Returns secret for unlocked account - pub fn account_secret(&self, account: &Address) -> Result { + fn account_secret(&self, account: &Address) -> Result { let read_lock = self.unlocks.read().unwrap(); let unlock = try!(read_lock.get(account).ok_or(SigningError::AccountNotUnlocked)); Ok(unlock.secret as crypto::Secret) From a2dea3885b393da47dad0127f22773b7fcae00bc Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Thu, 10 Mar 2016 23:09:45 +0400 Subject: [PATCH 6/6] refactoring to AccountService --- parity/main.rs | 10 ++----- rpc/src/v1/impls/personal.rs | 18 +++++------- util/src/keys/store.rs | 53 +++++++++++++++++++++++++++++++----- 3 files changed, 56 insertions(+), 25 deletions(-) diff --git a/parity/main.rs b/parity/main.rs index 6d1c08162..b6ed5cba3 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -196,7 +196,7 @@ fn setup_log(init: &Option) { } #[cfg(feature = "rpc")] -fn setup_rpc_server(client: Arc, sync: Arc, secret_store: Arc, url: &str, cors_domain: &str, apis: Vec<&str>) -> Option> { +fn setup_rpc_server(client: Arc, sync: Arc, secret_store: Arc, url: &str, cors_domain: &str, apis: Vec<&str>) -> Option> { use rpc::v1::*; let server = rpc::RpcServer::new(); @@ -416,11 +416,7 @@ impl Configuration { let sync = EthSync::register(service.network(), sync_config, client); // Secret Store - let secret_store = Arc::new(SecretStore::new()); - { - let import_ref = Arc::make_mut(&mut secret_store); - import_ref.try_import_existing(); - } + let account_service = Arc::new(AccountService::new()); // Setup rpc if self.args.flag_jsonrpc || self.args.flag_rpc { @@ -432,7 +428,7 @@ impl Configuration { let cors = self.args.flag_rpccorsdomain.as_ref().unwrap_or(&self.args.flag_jsonrpc_cors); // TODO: use this as the API list. let apis = self.args.flag_rpcapi.as_ref().unwrap_or(&self.args.flag_jsonrpc_apis); - let server_handler = setup_rpc_server(service.client(), sync.clone(), secret_store.clone(), &url, cors, apis.split(",").collect()); + let server_handler = setup_rpc_server(service.client(), sync.clone(), account_service.clone(), &url, cors, apis.split(",").collect()); if let Some(handler) = server_handler { panic_handler.forward_from(handler.deref()); } diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index 48e1b1c6a..7b79ceae7 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -20,30 +20,28 @@ use jsonrpc_core::*; use v1::traits::Personal; use util::keys::store::*; use util::Address; -use std::sync::RwLock; /// Account management (personal) rpc implementation. pub struct PersonalClient { - secret_store: Weak>, + accounts: Weak, } impl PersonalClient { /// Creates new PersonalClient - pub fn new(store: &Arc>) -> Self { + pub fn new(store: &Arc) -> Self { PersonalClient { - secret_store: Arc::downgrade(store), + accounts: Arc::downgrade(store), } } } impl Personal for PersonalClient { fn accounts(&self, _: Params) -> Result { - let store_wk = take_weak!(self.secret_store); - let store = store_wk.read().unwrap(); + let store = take_weak!(self.accounts); match store.accounts() { Ok(account_list) => { Ok(Value::Array(account_list.iter() - .map(|&(account, _)| Value::String(format!("{:?}", account))) + .map(|&account| Value::String(format!("{:?}", account))) .collect::>()) ) } @@ -54,8 +52,7 @@ impl Personal for PersonalClient { fn new_account(&self, params: Params) -> Result { from_params::<(String, )>(params).and_then( |(pass, )| { - let store_wk = take_weak!(self.secret_store); - let mut store = store_wk.write().unwrap(); + let store = take_weak!(self.accounts); match store.new_account(&pass) { Ok(address) => Ok(Value::String(format!("{:?}", address))), Err(_) => Err(Error::internal_error()) @@ -67,8 +64,7 @@ impl Personal for PersonalClient { fn unlock_account(&self, params: Params) -> Result { from_params::<(Address, String, u64)>(params).and_then( |(account, account_pass, _)|{ - let store_wk = take_weak!(self.secret_store); - let store = store_wk.read().unwrap(); + let store = take_weak!(self.accounts); match store.unlock_account(&account, &account_pass) { Ok(_) => Ok(Value::Bool(true)), Err(_) => Ok(Value::Bool(false)), diff --git a/util/src/keys/store.rs b/util/src/keys/store.rs index 9ea00cbba..ea97cc80e 100644 --- a/util/src/keys/store.rs +++ b/util/src/keys/store.rs @@ -80,16 +80,57 @@ struct AccountUnlock { /// Basic account management trait pub trait AccountProvider : Send + Sync { + /// Lists all accounts + fn accounts(&self) -> Result, ::std::io::Error>; /// Unlocks account with the password provided fn unlock_account(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError>; /// Creates account - fn new_account(&mut self, pass: &str) -> Result; + fn new_account(&self, pass: &str) -> Result; /// Returns secret for unlocked account fn account_secret(&self, account: &Address) -> Result; /// Returns secret for unlocked account fn sign(&self, account: &Address, message: &H256) -> Result; } +/// Thread-safe accounts management +pub struct AccountService { + secret_store: RwLock, +} + +impl AccountProvider for AccountService { + /// Lists all accounts + fn accounts(&self) -> Result, ::std::io::Error> { + Ok(try!(self.secret_store.read().unwrap().accounts()).iter().map(|&(addr, _)| addr).collect::>()) + } + /// Unlocks account with the password provided + fn unlock_account(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError> { + self.secret_store.read().unwrap().unlock_account(account, pass) + } + /// Creates account + fn new_account(&self, pass: &str) -> Result { + self.secret_store.write().unwrap().new_account(pass) + } + /// Returns secret for unlocked account + fn account_secret(&self, account: &Address) -> Result { + self.secret_store.read().unwrap().account_secret(account) + } + /// Returns secret for unlocked account + fn sign(&self, account: &Address, message: &H256) -> Result { + self.secret_store.read().unwrap().sign(account, message) + } +} + +impl AccountService { + /// New account service with the default location + pub fn new() -> AccountService { + let secret_store = RwLock::new(SecretStore::new()); + secret_store.write().unwrap().try_import_existing(); + AccountService { + secret_store: secret_store + } + } +} + impl SecretStore { /// new instance of Secret Store in default home directory pub fn new() -> SecretStore { @@ -156,11 +197,9 @@ impl SecretStore { unlocks: RwLock::new(HashMap::new()), } } -} -impl AccountProvider for SecretStore { /// Unlocks account for use - fn unlock_account(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError> { + pub fn unlock_account(&self, account: &Address, pass: &str) -> Result<(), EncryptedHashMapError> { let secret_id = try!(self.account(&account).ok_or(EncryptedHashMapError::UnknownIdentifier)); let secret = try!(self.get(&secret_id, pass)); { @@ -174,7 +213,7 @@ impl AccountProvider for SecretStore { } /// Creates new account - fn new_account(&mut self, pass: &str) -> Result { + pub fn new_account(&mut self, pass: &str) -> Result { let secret = H256::random(); let key_id = H128::random(); self.insert(key_id.clone(), secret, pass); @@ -187,7 +226,7 @@ impl AccountProvider for SecretStore { } /// Signs message with unlocked account - fn sign(&self, account: &Address, message: &H256) -> Result { + pub fn sign(&self, account: &Address, message: &H256) -> Result { let read_lock = self.unlocks.read().unwrap(); let unlock = try!(read_lock.get(account).ok_or(SigningError::AccountNotUnlocked)); match crypto::KeyPair::from_secret(unlock.secret) { @@ -200,7 +239,7 @@ impl AccountProvider for SecretStore { } /// Returns secret for unlocked account - fn account_secret(&self, account: &Address) -> Result { + pub fn account_secret(&self, account: &Address) -> Result { let read_lock = self.unlocks.read().unwrap(); let unlock = try!(read_lock.get(account).ok_or(SigningError::AccountNotUnlocked)); Ok(unlock.secret as crypto::Secret)