Merge pull request #3792 from ethcore/dapps-accounts-rpc
Additional RPCs for dapps accounts management
This commit is contained in:
commit
4d6b4d9d72
@ -1 +1 @@
|
|||||||
Subproject commit 9028c4801fd39fbb71a9796979182549a24e81c8
|
Subproject commit e8f4624b7f1a15c63674eecf577c7ab76c3b16be
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
mod stores;
|
mod stores;
|
||||||
|
|
||||||
use self::stores::{AddressBook, DappsSettingsStore};
|
use self::stores::{AddressBook, DappsSettingsStore, NewDappsPolicy};
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -156,10 +156,49 @@ impl AccountProvider {
|
|||||||
Ok(accounts)
|
Ok(accounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets a whitelist of accounts exposed for unknown dapps.
|
||||||
|
/// `None` means that all accounts will be visible.
|
||||||
|
pub fn set_new_dapps_whitelist(&self, accounts: Option<Vec<Address>>) -> Result<(), Error> {
|
||||||
|
self.dapps_settings.write().set_policy(match accounts {
|
||||||
|
None => NewDappsPolicy::AllAccounts,
|
||||||
|
Some(accounts) => NewDappsPolicy::Whitelist(accounts),
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a whitelist of accounts exposed for unknown dapps.
|
||||||
|
/// `None` means that all accounts will be visible.
|
||||||
|
pub fn new_dapps_whitelist(&self) -> Result<Option<Vec<Address>>, Error> {
|
||||||
|
Ok(match self.dapps_settings.read().policy() {
|
||||||
|
NewDappsPolicy::AllAccounts => None,
|
||||||
|
NewDappsPolicy::Whitelist(accounts) => Some(accounts),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a list of dapps recently requesting accounts.
|
||||||
|
pub fn recent_dapps(&self) -> Result<Vec<DappId>, Error> {
|
||||||
|
Ok(self.dapps_settings.read().recent_dapps())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Marks dapp as recently used.
|
||||||
|
pub fn note_dapp_used(&self, dapp: DappId) -> Result<(), Error> {
|
||||||
|
let mut dapps = self.dapps_settings.write();
|
||||||
|
dapps.mark_dapp_used(dapp.clone());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets addresses visile for dapp.
|
/// Gets addresses visile for dapp.
|
||||||
pub fn dapps_addresses(&self, dapp: DappId) -> Result<Vec<Address>, Error> {
|
pub fn dapps_addresses(&self, dapp: DappId) -> Result<Vec<Address>, Error> {
|
||||||
let accounts = self.dapps_settings.read().get();
|
let dapps = self.dapps_settings.read();
|
||||||
Ok(accounts.get(&dapp).map(|settings| settings.accounts.clone()).unwrap_or_else(Vec::new))
|
|
||||||
|
let accounts = dapps.settings().get(&dapp).map(|settings| settings.accounts.clone());
|
||||||
|
match accounts {
|
||||||
|
Some(accounts) => Ok(accounts),
|
||||||
|
None => match dapps.policy() {
|
||||||
|
NewDappsPolicy::AllAccounts => self.accounts(),
|
||||||
|
NewDappsPolicy::Whitelist(accounts) => Ok(accounts),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets addresses visile for dapp.
|
/// Sets addresses visile for dapp.
|
||||||
@ -423,6 +462,8 @@ mod tests {
|
|||||||
// given
|
// given
|
||||||
let ap = AccountProvider::transient_provider();
|
let ap = AccountProvider::transient_provider();
|
||||||
let app = "app1".to_owned();
|
let app = "app1".to_owned();
|
||||||
|
// set `AllAccounts` policy
|
||||||
|
ap.set_new_dapps_whitelist(None).unwrap();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
ap.set_dapps_addresses(app.clone(), vec![1.into(), 2.into()]).unwrap();
|
ap.set_dapps_addresses(app.clone(), vec![1.into(), 2.into()]).unwrap();
|
||||||
@ -430,4 +471,23 @@ mod tests {
|
|||||||
// then
|
// then
|
||||||
assert_eq!(ap.dapps_addresses(app.clone()).unwrap(), vec![1.into(), 2.into()]);
|
assert_eq!(ap.dapps_addresses(app.clone()).unwrap(), vec![1.into(), 2.into()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_set_dapps_policy() {
|
||||||
|
// given
|
||||||
|
let ap = AccountProvider::transient_provider();
|
||||||
|
let address = ap.new_account("test").unwrap();
|
||||||
|
|
||||||
|
// When returning nothing
|
||||||
|
ap.set_new_dapps_whitelist(Some(vec![])).unwrap();
|
||||||
|
assert_eq!(ap.dapps_addresses("app1".into()).unwrap(), vec![]);
|
||||||
|
|
||||||
|
// change to all
|
||||||
|
ap.set_new_dapps_whitelist(None).unwrap();
|
||||||
|
assert_eq!(ap.dapps_addresses("app1".into()).unwrap(), vec![address]);
|
||||||
|
|
||||||
|
// change to a whitelist
|
||||||
|
ap.set_new_dapps_whitelist(Some(vec![1.into()])).unwrap();
|
||||||
|
assert_eq!(ap.dapps_addresses("app1".into()).unwrap(), vec![1.into()]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,11 @@
|
|||||||
//! Address Book and Dapps Settings Store
|
//! Address Book and Dapps Settings Store
|
||||||
|
|
||||||
use std::{fs, fmt, hash, ops};
|
use std::{fs, fmt, hash, ops};
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use ethstore::ethkey::Address;
|
use ethstore::ethkey::Address;
|
||||||
use ethjson::misc::{AccountMeta, DappsSettings as JsonSettings};
|
use ethjson::misc::{AccountMeta, DappsSettings as JsonSettings, NewDappsPolicy as JsonNewDappsPolicy};
|
||||||
use account_provider::DappId;
|
use account_provider::DappId;
|
||||||
|
|
||||||
/// Disk-backed map from Address to String. Uses JSON.
|
/// Disk-backed map from Address to String. Uses JSON.
|
||||||
@ -105,43 +105,106 @@ impl From<DappsSettings> for JsonSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dapps user settings
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
pub enum NewDappsPolicy {
|
||||||
|
AllAccounts,
|
||||||
|
Whitelist(Vec<Address>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<JsonNewDappsPolicy> for NewDappsPolicy {
|
||||||
|
fn from(s: JsonNewDappsPolicy) -> Self {
|
||||||
|
match s {
|
||||||
|
JsonNewDappsPolicy::AllAccounts => NewDappsPolicy::AllAccounts,
|
||||||
|
JsonNewDappsPolicy::Whitelist(accounts) => NewDappsPolicy::Whitelist(
|
||||||
|
accounts.into_iter().map(Into::into).collect()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NewDappsPolicy> for JsonNewDappsPolicy {
|
||||||
|
fn from(s: NewDappsPolicy) -> Self {
|
||||||
|
match s {
|
||||||
|
NewDappsPolicy::AllAccounts => JsonNewDappsPolicy::AllAccounts,
|
||||||
|
NewDappsPolicy::Whitelist(accounts) => JsonNewDappsPolicy::Whitelist(
|
||||||
|
accounts.into_iter().map(Into::into).collect()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_RECENT_DAPPS: usize = 10;
|
||||||
|
|
||||||
/// Disk-backed map from DappId to Settings. Uses JSON.
|
/// Disk-backed map from DappId to Settings. Uses JSON.
|
||||||
pub struct DappsSettingsStore {
|
pub struct DappsSettingsStore {
|
||||||
cache: DiskMap<DappId, DappsSettings>,
|
/// Dapps Settings
|
||||||
|
settings: DiskMap<DappId, DappsSettings>,
|
||||||
|
/// New Dapps Policy
|
||||||
|
policy: DiskMap<String, NewDappsPolicy>,
|
||||||
|
/// Recently Accessed Dapps (transient)
|
||||||
|
recent: VecDeque<DappId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DappsSettingsStore {
|
impl DappsSettingsStore {
|
||||||
/// Creates new store at given directory path.
|
/// Creates new store at given directory path.
|
||||||
pub fn new(path: String) -> Self {
|
pub fn new(path: String) -> Self {
|
||||||
let mut r = DappsSettingsStore {
|
let mut r = DappsSettingsStore {
|
||||||
cache: DiskMap::new(path, "dapps_accounts.json".into())
|
settings: DiskMap::new(path.clone(), "dapps_accounts.json".into()),
|
||||||
|
policy: DiskMap::new(path.clone(), "dapps_policy.json".into()),
|
||||||
|
recent: VecDeque::with_capacity(MAX_RECENT_DAPPS),
|
||||||
};
|
};
|
||||||
r.cache.revert(JsonSettings::read_dapps_settings);
|
r.settings.revert(JsonSettings::read_dapps_settings);
|
||||||
|
r.policy.revert(JsonNewDappsPolicy::read_new_dapps_policy);
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates transient store (no changes are saved to disk).
|
/// Creates transient store (no changes are saved to disk).
|
||||||
pub fn transient() -> Self {
|
pub fn transient() -> Self {
|
||||||
DappsSettingsStore {
|
DappsSettingsStore {
|
||||||
cache: DiskMap::transient()
|
settings: DiskMap::transient(),
|
||||||
|
policy: DiskMap::transient(),
|
||||||
|
recent: VecDeque::with_capacity(MAX_RECENT_DAPPS),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get copy of the dapps settings
|
/// Get copy of the dapps settings
|
||||||
pub fn get(&self) -> HashMap<DappId, DappsSettings> {
|
pub fn settings(&self) -> HashMap<DappId, DappsSettings> {
|
||||||
self.cache.clone()
|
self.settings.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save(&self) {
|
/// Returns current new dapps policy
|
||||||
self.cache.save(JsonSettings::write_dapps_settings)
|
pub fn policy(&self) -> NewDappsPolicy {
|
||||||
|
self.policy.get("default").cloned().unwrap_or(NewDappsPolicy::AllAccounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns recent dapps (in order of last request)
|
||||||
|
pub fn recent_dapps(&self) -> Vec<DappId> {
|
||||||
|
self.recent.iter().cloned().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Marks recent dapp as used
|
||||||
|
pub fn mark_dapp_used(&mut self, dapp: DappId) {
|
||||||
|
self.recent.retain(|id| id != &dapp);
|
||||||
|
self.recent.push_front(dapp);
|
||||||
|
while self.recent.len() > MAX_RECENT_DAPPS {
|
||||||
|
self.recent.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets current new dapps policy
|
||||||
|
pub fn set_policy(&mut self, policy: NewDappsPolicy) {
|
||||||
|
self.policy.insert("default".into(), policy);
|
||||||
|
self.policy.save(JsonNewDappsPolicy::write_new_dapps_policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets accounts for specific dapp.
|
||||||
pub fn set_accounts(&mut self, id: DappId, accounts: Vec<Address>) {
|
pub fn set_accounts(&mut self, id: DappId, accounts: Vec<Address>) {
|
||||||
{
|
{
|
||||||
let mut settings = self.cache.entry(id).or_insert_with(DappsSettings::default);
|
let mut settings = self.settings.entry(id).or_insert_with(DappsSettings::default);
|
||||||
settings.accounts = accounts;
|
settings.accounts = accounts;
|
||||||
}
|
}
|
||||||
self.save();
|
self.settings.save(JsonSettings::write_dapps_settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +279,7 @@ impl<K: hash::Hash + Eq, V> DiskMap<K, V> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{AddressBook, DappsSettingsStore, DappsSettings};
|
use super::{AddressBook, DappsSettingsStore, DappsSettings, NewDappsPolicy};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use ethjson::misc::AccountMeta;
|
use ethjson::misc::AccountMeta;
|
||||||
use devtools::RandomTempPath;
|
use devtools::RandomTempPath;
|
||||||
@ -232,25 +295,6 @@ mod tests {
|
|||||||
assert_eq!(b.get(), hash_map![1.into() => AccountMeta{name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None}]);
|
assert_eq!(b.get(), hash_map![1.into() => AccountMeta{name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_save_and_reload_dapps_settings() {
|
|
||||||
// given
|
|
||||||
let temp = RandomTempPath::create_dir();
|
|
||||||
let path = temp.as_str().to_owned();
|
|
||||||
let mut b = DappsSettingsStore::new(path.clone());
|
|
||||||
|
|
||||||
// when
|
|
||||||
b.set_accounts("dappOne".into(), vec![1.into(), 2.into()]);
|
|
||||||
|
|
||||||
// then
|
|
||||||
let b = DappsSettingsStore::new(path);
|
|
||||||
assert_eq!(b.get(), hash_map![
|
|
||||||
"dappOne".into() => DappsSettings {
|
|
||||||
accounts: vec![1.into(), 2.into()],
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_remove_address() {
|
fn should_remove_address() {
|
||||||
let temp = RandomTempPath::create_dir();
|
let temp = RandomTempPath::create_dir();
|
||||||
@ -268,4 +312,58 @@ mod tests {
|
|||||||
3.into() => AccountMeta{name: "Three".to_owned(), meta: "{}".to_owned(), uuid: None}
|
3.into() => AccountMeta{name: "Three".to_owned(), meta: "{}".to_owned(), uuid: None}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_save_and_reload_dapps_settings() {
|
||||||
|
// given
|
||||||
|
let temp = RandomTempPath::create_dir();
|
||||||
|
let path = temp.as_str().to_owned();
|
||||||
|
let mut b = DappsSettingsStore::new(path.clone());
|
||||||
|
|
||||||
|
// when
|
||||||
|
b.set_accounts("dappOne".into(), vec![1.into(), 2.into()]);
|
||||||
|
|
||||||
|
// then
|
||||||
|
let b = DappsSettingsStore::new(path);
|
||||||
|
assert_eq!(b.settings(), hash_map![
|
||||||
|
"dappOne".into() => DappsSettings {
|
||||||
|
accounts: vec![1.into(), 2.into()],
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_maintain_a_list_of_recent_dapps() {
|
||||||
|
let mut store = DappsSettingsStore::transient();
|
||||||
|
assert!(store.recent_dapps().is_empty(), "Initially recent dapps should be empty.");
|
||||||
|
|
||||||
|
store.mark_dapp_used("dapp1".into());
|
||||||
|
assert_eq!(store.recent_dapps(), vec!["dapp1".to_owned()]);
|
||||||
|
|
||||||
|
store.mark_dapp_used("dapp2".into());
|
||||||
|
assert_eq!(store.recent_dapps(), vec!["dapp2".to_owned(), "dapp1".to_owned()]);
|
||||||
|
|
||||||
|
store.mark_dapp_used("dapp1".into());
|
||||||
|
assert_eq!(store.recent_dapps(), vec!["dapp1".to_owned(), "dapp2".to_owned()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_store_dapps_policy() {
|
||||||
|
// given
|
||||||
|
let temp = RandomTempPath::create_dir();
|
||||||
|
let path = temp.as_str().to_owned();
|
||||||
|
let mut store = DappsSettingsStore::new(path.clone());
|
||||||
|
|
||||||
|
// Test default policy
|
||||||
|
assert_eq!(store.policy(), NewDappsPolicy::AllAccounts);
|
||||||
|
|
||||||
|
// when
|
||||||
|
store.set_policy(NewDappsPolicy::Whitelist(vec![1.into(), 2.into()]));
|
||||||
|
|
||||||
|
// then
|
||||||
|
let store = DappsSettingsStore::new(path);
|
||||||
|
assert_eq!(store.policy.clone(), hash_map![
|
||||||
|
"default".into() => NewDappsPolicy::Whitelist(vec![1.into(), 2.into()])
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,3 +49,32 @@ impl DappsSettings {
|
|||||||
serde_json::to_writer(writer, &m.iter().map(|(a, m)| (a.clone().into(), m.clone().into())).collect::<HashMap<DappId, DappsSettings>>())
|
serde_json::to_writer(writer, &m.iter().map(|(a, m)| (a.clone().into(), m.clone().into())).collect::<HashMap<DappId, DappsSettings>>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Accounts policy for new dapps.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum NewDappsPolicy {
|
||||||
|
/// All accounts are exposed by default.
|
||||||
|
AllAccounts,
|
||||||
|
/// Only accounts listed here are exposed by default for new dapps.
|
||||||
|
Whitelist(Vec<hash::Address>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NewDappsPolicy {
|
||||||
|
/// Read a hash map of `String -> NewDappsPolicy`
|
||||||
|
pub fn read_new_dapps_policy<R, S>(reader: R) -> Result<HashMap<String, S>, serde_json::Error> where
|
||||||
|
R: io::Read,
|
||||||
|
S: From<NewDappsPolicy> + Clone,
|
||||||
|
{
|
||||||
|
serde_json::from_reader(reader).map(|ok: HashMap<String, NewDappsPolicy>|
|
||||||
|
ok.into_iter().map(|(a, m)| (a.into(), m.into())).collect()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a hash map of `String -> NewDappsPolicy`
|
||||||
|
pub fn write_new_dapps_policy<W, S>(m: &HashMap<String, S>, writer: &mut W) -> Result<(), serde_json::Error> where
|
||||||
|
W: io::Write,
|
||||||
|
S: Into<NewDappsPolicy> + Clone,
|
||||||
|
{
|
||||||
|
serde_json::to_writer(writer, &m.iter().map(|(a, m)| (a.clone().into(), m.clone().into())).collect::<HashMap<String, NewDappsPolicy>>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -19,5 +19,5 @@
|
|||||||
mod account_meta;
|
mod account_meta;
|
||||||
mod dapps_settings;
|
mod dapps_settings;
|
||||||
|
|
||||||
pub use self::dapps_settings::DappsSettings;
|
pub use self::dapps_settings::{DappsSettings, NewDappsPolicy};
|
||||||
pub use self::account_meta::AccountMeta;
|
pub use self::account_meta::AccountMeta;
|
||||||
|
@ -340,7 +340,11 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
|
|||||||
let dapp = id.0;
|
let dapp = id.0;
|
||||||
|
|
||||||
let store = take_weak!(self.accounts);
|
let store = take_weak!(self.accounts);
|
||||||
let accounts = try!(store.dapps_addresses(dapp.into()).map_err(|e| errors::internal("Could not fetch accounts.", e)));
|
let accounts = try!(store
|
||||||
|
.note_dapp_used(dapp.clone().into())
|
||||||
|
.and_then(|_| store.dapps_addresses(dapp.into()))
|
||||||
|
.map_err(|e| errors::internal("Could not fetch accounts.", e))
|
||||||
|
);
|
||||||
|
|
||||||
Ok(accounts.into_iter().map(Into::into).collect())
|
Ok(accounts.into_iter().map(Into::into).collect())
|
||||||
}
|
}
|
||||||
|
@ -164,19 +164,51 @@ impl<C: 'static> ParityAccounts for ParityAccountsClient<C> where C: MiningBlock
|
|||||||
|
|
||||||
fn set_dapps_addresses(&self, dapp: DappId, addresses: Vec<RpcH160>) -> Result<bool, Error> {
|
fn set_dapps_addresses(&self, dapp: DappId, addresses: Vec<RpcH160>) -> Result<bool, Error> {
|
||||||
let store = take_weak!(self.accounts);
|
let store = take_weak!(self.accounts);
|
||||||
let addresses = addresses.into_iter().map(Into::into).collect();
|
|
||||||
|
|
||||||
store.set_dapps_addresses(dapp.into(), addresses)
|
store.set_dapps_addresses(dapp.into(), into_vec(addresses))
|
||||||
.map_err(|e| errors::account("Couldn't set dapps addresses.", e))
|
.map_err(|e| errors::account("Couldn't set dapps addresses.", e))
|
||||||
.map(|_| true)
|
.map(|_| true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dapps_addresses(&self, dapp: DappId) -> Result<Vec<RpcH160>, Error> {
|
||||||
|
let store = take_weak!(self.accounts);
|
||||||
|
|
||||||
|
store.dapps_addresses(dapp.into())
|
||||||
|
.map_err(|e| errors::account("Couldn't get dapps addresses.", e))
|
||||||
|
.map(into_vec)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_new_dapps_whitelist(&self, whitelist: Option<Vec<RpcH160>>) -> Result<bool, Error> {
|
||||||
|
let store = take_weak!(self.accounts);
|
||||||
|
|
||||||
|
store
|
||||||
|
.set_new_dapps_whitelist(whitelist.map(into_vec))
|
||||||
|
.map_err(|e| errors::account("Couldn't set dapps whitelist.", e))
|
||||||
|
.map(|_| true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_dapps_whitelist(&self) -> Result<Option<Vec<RpcH160>>, Error> {
|
||||||
|
let store = take_weak!(self.accounts);
|
||||||
|
|
||||||
|
store.new_dapps_whitelist()
|
||||||
|
.map_err(|e| errors::account("Couldn't get dapps whitelist.", e))
|
||||||
|
.map(|accounts| accounts.map(into_vec))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recent_dapps(&self) -> Result<Vec<DappId>, Error> {
|
||||||
|
let store = take_weak!(self.accounts);
|
||||||
|
|
||||||
|
store.recent_dapps()
|
||||||
|
.map_err(|e| errors::account("Couldn't get recent dapps.", e))
|
||||||
|
.map(into_vec)
|
||||||
|
}
|
||||||
|
|
||||||
fn import_geth_accounts(&self, addresses: Vec<RpcH160>) -> Result<Vec<RpcH160>, Error> {
|
fn import_geth_accounts(&self, addresses: Vec<RpcH160>) -> Result<Vec<RpcH160>, Error> {
|
||||||
let store = take_weak!(self.accounts);
|
let store = take_weak!(self.accounts);
|
||||||
|
|
||||||
store
|
store
|
||||||
.import_geth_accounts(addresses.into_iter().map(Into::into).collect(), false)
|
.import_geth_accounts(into_vec(addresses), false)
|
||||||
.map(|imported| imported.into_iter().map(Into::into).collect())
|
.map(into_vec)
|
||||||
.map_err(|e| errors::account("Couldn't import Geth accounts", e))
|
.map_err(|e| errors::account("Couldn't import Geth accounts", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,10 +216,12 @@ impl<C: 'static> ParityAccounts for ParityAccountsClient<C> where C: MiningBlock
|
|||||||
try!(self.active());
|
try!(self.active());
|
||||||
let store = take_weak!(self.accounts);
|
let store = take_weak!(self.accounts);
|
||||||
|
|
||||||
Ok(store.list_geth_accounts(false)
|
Ok(into_vec(store.list_geth_accounts(false)))
|
||||||
.into_iter()
|
|
||||||
.map(Into::into)
|
|
||||||
.collect()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn into_vec<A, B>(a: Vec<A>) -> Vec<B> where
|
||||||
|
A: Into<B>
|
||||||
|
{
|
||||||
|
a.into_iter().map(Into::into).collect()
|
||||||
|
}
|
||||||
|
@ -354,11 +354,18 @@ fn rpc_eth_gas_price() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn rpc_eth_accounts() {
|
fn rpc_eth_accounts() {
|
||||||
let tester = EthTester::default();
|
let tester = EthTester::default();
|
||||||
let _address = tester.accounts_provider.new_account("").unwrap();
|
let address = tester.accounts_provider.new_account("").unwrap();
|
||||||
|
tester.accounts_provider.set_new_dapps_whitelist(None).unwrap();
|
||||||
|
|
||||||
|
// with current policy it should return the account
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":[""#.to_owned() + &format!("0x{:?}", address) + r#""],"id":1}"#;
|
||||||
|
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
|
||||||
|
|
||||||
|
tester.accounts_provider.set_new_dapps_whitelist(Some(vec![1.into()])).unwrap();
|
||||||
// even with some account it should return empty list (no dapp detected)
|
// even with some account it should return empty list (no dapp detected)
|
||||||
let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#;
|
let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#;
|
||||||
let response = r#"{"jsonrpc":"2.0","result":[],"id":1}"#;
|
let response = r#"{"jsonrpc":"2.0","result":["0x0000000000000000000000000000000000000001"],"id":1}"#;
|
||||||
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
|
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
|
||||||
|
|
||||||
// when we add visible address it should return that.
|
// when we add visible address it should return that.
|
||||||
|
@ -117,7 +117,7 @@ fn should_be_able_to_set_meta() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rpc_parity_set_dapps_accounts() {
|
fn rpc_parity_set_and_get_dapps_accounts() {
|
||||||
// given
|
// given
|
||||||
let tester = setup();
|
let tester = setup();
|
||||||
assert_eq!(tester.accounts.dapps_addresses("app1".into()).unwrap(), vec![]);
|
assert_eq!(tester.accounts.dapps_addresses("app1".into()).unwrap(), vec![]);
|
||||||
@ -129,6 +129,52 @@ fn rpc_parity_set_dapps_accounts() {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(tester.accounts.dapps_addresses("app1".into()).unwrap(), vec![10.into()]);
|
assert_eq!(tester.accounts.dapps_addresses("app1".into()).unwrap(), vec![10.into()]);
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "parity_getDappsAddresses","params":["app1"], "id": 1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":["0x000000000000000000000000000000000000000a"],"id":1}"#;
|
||||||
|
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_parity_set_and_get_new_dapps_whitelist() {
|
||||||
|
// given
|
||||||
|
let tester = setup();
|
||||||
|
|
||||||
|
// when set to whitelist
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "parity_setNewDappsWhitelist","params":[["0x000000000000000000000000000000000000000a"]], "id": 1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
|
||||||
|
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(tester.accounts.new_dapps_whitelist().unwrap(), Some(vec![10.into()]));
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "parity_getNewDappsWhitelist","params":[], "id": 1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":["0x000000000000000000000000000000000000000a"],"id":1}"#;
|
||||||
|
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
|
||||||
|
|
||||||
|
// when set to empty
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "parity_setNewDappsWhitelist","params":[null], "id": 1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
|
||||||
|
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(tester.accounts.new_dapps_whitelist().unwrap(), None);
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "parity_getNewDappsWhitelist","params":[], "id": 1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#;
|
||||||
|
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_parity_recent_dapps() {
|
||||||
|
// given
|
||||||
|
let tester = setup();
|
||||||
|
|
||||||
|
// when
|
||||||
|
// trigger dapp usage
|
||||||
|
tester.accounts.note_dapp_used("dapp1".into()).unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "parity_listRecentDapps","params":[], "id": 1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":["dapp1"],"id":1}"#;
|
||||||
|
assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -78,6 +78,24 @@ build_rpc_trait! {
|
|||||||
#[rpc(name = "parity_setDappsAddresses")]
|
#[rpc(name = "parity_setDappsAddresses")]
|
||||||
fn set_dapps_addresses(&self, DappId, Vec<H160>) -> Result<bool, Error>;
|
fn set_dapps_addresses(&self, DappId, Vec<H160>) -> Result<bool, Error>;
|
||||||
|
|
||||||
|
/// Gets accounts exposed for particular dapp.
|
||||||
|
#[rpc(name = "parity_getDappsAddresses")]
|
||||||
|
fn dapps_addresses(&self, DappId) -> Result<Vec<H160>, Error>;
|
||||||
|
|
||||||
|
/// Sets accounts exposed for new dapps.
|
||||||
|
/// `None` means that all accounts will be exposed.
|
||||||
|
#[rpc(name = "parity_setNewDappsWhitelist")]
|
||||||
|
fn set_new_dapps_whitelist(&self, Option<Vec<H160>>) -> Result<bool, Error>;
|
||||||
|
|
||||||
|
/// Gets accounts exposed for new dapps.
|
||||||
|
/// `None` means that all accounts will be exposed.
|
||||||
|
#[rpc(name = "parity_getNewDappsWhitelist")]
|
||||||
|
fn new_dapps_whitelist(&self) -> Result<Option<Vec<H160>>, Error>;
|
||||||
|
|
||||||
|
/// Sets accounts exposed for particular dapp.
|
||||||
|
#[rpc(name = "parity_listRecentDapps")]
|
||||||
|
fn recent_dapps(&self) -> Result<Vec<DappId>, Error>;
|
||||||
|
|
||||||
/// Imports a number of Geth accounts, with the list provided as the argument.
|
/// Imports a number of Geth accounts, with the list provided as the argument.
|
||||||
#[rpc(name = "parity_importGethAccounts")]
|
#[rpc(name = "parity_importGethAccounts")]
|
||||||
fn import_geth_accounts(&self, Vec<H160>) -> Result<Vec<H160>, Error>;
|
fn import_geth_accounts(&self, Vec<H160>) -> Result<Vec<H160>, Error>;
|
||||||
|
@ -26,6 +26,12 @@ impl Into<String> for DappId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<String> for DappId {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
DappId(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user