Limiting accounts returned by parity_accountInfo (#3931)

* Limiting accountNames returned by parity_accountNames

* Fixing middleware

* Change RPC interface

* Enhance tests for RPC layer

* UI uses parity_allAccountsInfo

* Update dapps to use parity_accountsInfo

* Don't filter by uuid (deprecated)

* Consistency in calls

* Fix js tests (missed stub call)
This commit is contained in:
Tomasz Drwięga 2016-12-23 18:52:02 +01:00 committed by Gav Wood
parent 546246c56b
commit 27ba0e6922
26 changed files with 145 additions and 119 deletions

View File

@ -51,16 +51,16 @@ impl Endpoint for RpcEndpoint {
}
}
const MIDDLEWARE_METHOD: &'static str = "eth_accounts";
struct RpcMiddleware {
handler: Arc<IoHandler>,
methods: Vec<String>,
}
impl RpcMiddleware {
fn new(handler: Arc<IoHandler>) -> Self {
RpcMiddleware {
handler: handler,
methods: vec!["eth_accounts".into(), "parity_accountsInfo".into()],
}
}
@ -68,9 +68,9 @@ impl RpcMiddleware {
fn augment_request(&self, request: &mut Request, meta: Option<Meta>) {
use jsonrpc_core::{Call, Params, to_value};
fn augment_call(call: &mut Call, meta: Option<&Meta>) {
fn augment_call(call: &mut Call, meta: Option<&Meta>, methods: &Vec<String>) {
match (call, meta) {
(&mut Call::MethodCall(ref mut method_call), Some(meta)) if &method_call.method == MIDDLEWARE_METHOD => {
(&mut Call::MethodCall(ref mut method_call), Some(meta)) if methods.contains(&method_call.method) => {
let session = to_value(&meta.app_id);
let params = match method_call.params {
@ -86,10 +86,10 @@ impl RpcMiddleware {
}
match *request {
Request::Single(ref mut call) => augment_call(call, meta.as_ref()),
Request::Single(ref mut call) => augment_call(call, meta.as_ref(), &self.methods),
Request::Batch(ref mut vec) => {
for mut call in vec {
augment_call(call, meta.as_ref())
augment_call(call, meta.as_ref(), &self.methods)
}
},
}

View File

@ -19,19 +19,23 @@ import BigNumber from 'bignumber.js';
import { toChecksumAddress } from '../../abi/util/address';
export function outAccountInfo (infos) {
const ret = {};
return Object
.keys(infos)
.reduce((ret, _address) => {
const info = infos[_address];
const address = outAddress(_address);
Object.keys(infos).forEach((address) => {
const info = infos[address];
ret[address] = {
name: info.name
};
ret[outAddress(address)] = {
name: info.name,
uuid: info.uuid,
meta: JSON.parse(info.meta)
};
});
if (info.meta) {
ret[address].uuid = info.uuid;
ret[address].meta = JSON.parse(info.meta);
}
return ret;
return ret;
}, {});
}
export function outAddress (address) {

View File

@ -35,6 +35,14 @@ describe('api/format/output', () => {
}
});
});
it('returns objects without meta & uuid as required', () => {
expect(outAccountInfo(
{ '0x63cf90d3f0410092fc0fca41846f596223979195': { name: 'name' } }
)).to.deep.equal({
'0x63Cf90D3f0410092FC0fca41846f596223979195': { name: 'name' }
});
});
});
describe('outAddress', () => {

View File

@ -27,18 +27,18 @@ export default class Parity {
.execute('parity_acceptNonReservedPeers');
}
accounts () {
return this._transport
.execute('parity_accounts')
.then(outAccountInfo);
}
accountsInfo () {
return this._transport
.execute('parity_accountsInfo')
.then(outAccountInfo);
}
allAccountsInfo () {
return this._transport
.execute('parity_allAccountsInfo')
.then(outAccountInfo);
}
addReservedPeer (encode) {
return this._transport
.execute('parity_addReservedPeer', encode);

View File

@ -24,7 +24,7 @@ import Signer from './signer';
const events = {
'logging': { module: 'logging' },
'eth_blockNumber': { module: 'eth' },
'parity_accountsInfo': { module: 'personal' },
'parity_allAccountsInfo': { module: 'personal' },
'eth_accounts': { module: 'personal' },
'signer_requestsToConfirm': { module: 'signer' }
};

View File

@ -46,9 +46,9 @@ export default class Personal {
_accountsInfo = () => {
return this._api.parity
.accountsInfo()
.allAccountsInfo()
.then((info) => {
this._updateSubscriptions('parity_accountsInfo', null, info);
this._updateSubscriptions('parity_allAccountsInfo', null, info);
});
}

View File

@ -28,16 +28,16 @@ const TEST_LIST = ['0xfa64203C044691aA57251aF95f4b48d85eC00Dd5'];
function stubApi (accounts, info) {
const _calls = {
accountsInfo: [],
allAccountsInfo: [],
listAccounts: []
};
return {
_calls,
parity: {
accountsInfo: () => {
allAccountsInfo: () => {
const stub = sinon.stub().resolves(info || TEST_INFO)();
_calls.accountsInfo.push(stub);
_calls.allAccountsInfo.push(stub);
return stub;
}
},
@ -86,8 +86,8 @@ describe('api/subscriptions/personal', () => {
expect(personal.isStarted).to.be.true;
});
it('calls parity_accountsInfo', () => {
expect(api._calls.accountsInfo.length).to.be.ok;
it('calls parity_allAccountsInfo', () => {
expect(api._calls.allAccountsInfo.length).to.be.ok;
});
it('calls eth_accounts', () => {
@ -96,7 +96,7 @@ describe('api/subscriptions/personal', () => {
it('updates subscribers', () => {
expect(cb.firstCall).to.have.been.calledWith('eth_accounts', null, TEST_LIST);
expect(cb.secondCall).to.have.been.calledWith('parity_accountsInfo', null, TEST_INFO);
expect(cb.secondCall).to.have.been.calledWith('parity_allAccountsInfo', null, TEST_INFO);
});
});
@ -112,7 +112,7 @@ describe('api/subscriptions/personal', () => {
});
it('calls personal_accountsInfo', () => {
expect(api._calls.accountsInfo.length).to.be.ok;
expect(api._calls.allAccountsInfo.length).to.be.ok;
});
it('calls personal_listAccounts', () => {

View File

@ -83,7 +83,7 @@ export default class Application extends Component {
Promise
.all([
attachInstances(),
api.parity.accounts()
api.parity.accountsInfo()
])
.then(([{ managerInstance, registryInstance, tokenregInstance }, accountsInfo]) => {
accountsInfo = accountsInfo || {};
@ -95,7 +95,7 @@ export default class Application extends Component {
accounts: Object
.keys(accountsInfo)
.sort((a, b) => {
return (accountsInfo[b].uuid || '').localeCompare(accountsInfo[a].uuid || '');
return (accountsInfo[b].name || '').localeCompare(accountsInfo[a].name || '');
})
.reduce((accounts, address) => {
accounts[address] = Object.assign(accountsInfo[address], { address });

View File

@ -126,7 +126,7 @@ export default class Deployment extends Component {
const { baseText, name, nameError, tla, tlaError, totalSupply, totalSupplyError } = this.state;
const hasError = !!(nameError || tlaError || totalSupplyError);
const error = `${styles.input} ${styles.error}`;
const addresses = Object.keys(accounts).filter((address) => accounts[address].uuid);
const addresses = Object.keys(accounts);
// <div className={ styles.input }>
// <label>global registration</label>

View File

@ -94,10 +94,7 @@ export default class Overview extends Component {
loadOwners () {
const { accounts } = this.context;
const addresses = Object
.values(accounts)
.filter((account) => account.uuid)
.map((account) => account.address);
const addresses = Object.keys(accounts);
loadOwnedTokens(addresses)
.then(({ tokens, total }) => {

View File

@ -303,12 +303,9 @@ export default class Send extends Component {
loadBalances () {
const { accounts } = this.context;
const myAccounts = Object
.values(accounts)
.filter((account) => account.uuid)
.map((account) => account.address);
const addresses = Object.keys(accounts);
loadBalances(myAccounts)
loadBalances(addresses)
.then((_tokens) => {
const tokens = _tokens.filter((token) => {
for (let index = 0; index < token.balances.length; index++) {

View File

@ -26,7 +26,6 @@ let instance = null;
export default class DappsStore {
@observable accounts = [];
@observable addresses = [];
@observable apps = [];
@observable contractOwner = null;
@observable currentAccount = null;
@ -191,7 +190,7 @@ export default class DappsStore {
@action setAccounts = (accountsInfo) => {
transaction(() => {
this.addresses = Object
this.accounts = Object
.keys(accountsInfo)
.map((address) => {
const account = accountsInfo[address];
@ -199,7 +198,6 @@ export default class DappsStore {
return account;
});
this.accounts = this.addresses.filter((account) => account.uuid);
this.currentAccount = this.accounts[0];
});
@ -315,7 +313,7 @@ export default class DappsStore {
this
.setApps(appsInfo.map(([appId, owner]) => {
const isOwner = !!this.accounts.find((account) => account.address === owner);
const account = this.addresses.find((account) => account.address === owner);
const account = this.accounts.find((account) => account.address === owner);
const id = api.util.bytesToHex(appId);
return {
@ -445,7 +443,7 @@ export default class DappsStore {
_loadAccounts () {
return api.parity
.accounts()
.accountsInfo()
.then(this.setAccounts)
.catch((error) => {
console.error('Store:loadAccounts', error);

View File

@ -28,7 +28,7 @@ export function attachInterface () {
return Promise
.all([
registry.getAddress.call({}, [api.util.sha3('githubhint'), 'A']),
api.parity.accounts()
api.parity.accountsInfo()
]);
})
.then(([address, accountsInfo]) => {
@ -37,7 +37,6 @@ export function attachInterface () {
const contract = api.newContract(abis.githubhint, address);
const accounts = Object
.keys(accountsInfo)
.filter((address) => accountsInfo[address].uuid)
.reduce((obj, address) => {
const account = accountsInfo[address];

View File

@ -20,14 +20,14 @@ export const set = (addresses) => ({ type: 'addresses set', addresses });
export const fetch = () => (dispatch) => {
return api.parity
.accounts()
.accountsInfo()
.then((accountsInfo) => {
const addresses = Object
.keys(accountsInfo)
.map((address) => ({
...accountsInfo[address],
address,
isAccount: !!accountsInfo[address].uuid
isAccount: true
}));
dispatch(set(addresses));
})

View File

@ -49,7 +49,7 @@ export function attachInterface (callback) {
return Promise
.all([
registry.getAddress.call({}, [api.util.sha3('signaturereg'), 'A']),
api.parity.accounts()
api.parity.accountsInfo()
]);
})
.then(([address, accountsInfo]) => {
@ -58,7 +58,6 @@ export function attachInterface (callback) {
const contract = api.newContract(abis.signaturereg, address);
const accounts = Object
.keys(accountsInfo)
.filter((address) => accountsInfo[address].uuid)
.reduce((obj, address) => {
const info = accountsInfo[address] || {};

View File

@ -36,11 +36,10 @@ export const setSelectedAccount = (address) => ({
export const loadAccounts = () => (dispatch) => {
api.parity
.accounts()
.accountsInfo()
.then((accountsInfo) => {
const accountsList = Object
.keys(accountsInfo)
.filter((address) => accountsInfo[address].uuid)
.map((address) => ({
...accountsInfo[address],
address

View File

@ -26,7 +26,7 @@ export default {
}
},
accounts: {
accountsInfo: {
desc: 'returns a map of accounts as an object',
params: [],
returns: {
@ -36,20 +36,12 @@ export default {
name: {
type: String,
desc: 'Account name'
},
meta: {
type: String,
desc: 'Encoded JSON string the defines additional account metadata'
},
uuid: {
type: String,
desc: 'The account Uuid, or null if not available/unknown/not applicable.'
}
}
}
},
accountsInfo: {
allAccountsInfo: {
desc: 'returns a map of accounts as an object',
params: [],
returns: {

View File

@ -60,7 +60,7 @@ export default class Balances {
subscribeAccountsInfo () {
this._api
.subscribe('parity_accountsInfo', (error, accountsInfo) => {
.subscribe('parity_allAccountsInfo', (error, accountsInfo) => {
if (error) {
return;
}

View File

@ -29,9 +29,9 @@ export default class Personal {
_subscribeAccountsInfo () {
this._api
.subscribe('parity_accountsInfo', (error, accountsInfo) => {
.subscribe('parity_allAccountsInfo', (error, accountsInfo) => {
if (error) {
console.error('parity_accountsInfo', error);
console.error('parity_allAccountsInfo', error);
return;
}
@ -41,7 +41,7 @@ export default class Personal {
_removeDeleted () {
this._api.parity
.accountsInfo()
.allAccountsInfo()
.then((accountsInfo) => {
return Promise.all(
Object

View File

@ -41,7 +41,7 @@ export default class Store {
_checkAccounts () {
this._api.parity
.accountsInfo()
.allAccountsInfo()
.then((info) => {
const accounts = Object.keys(info).filter((address) => info[address].uuid);

View File

@ -17,7 +17,7 @@
//! Parity-specific rpc implementation.
use std::sync::{Arc, Weak};
use std::str::FromStr;
use std::collections::BTreeMap;
use std::collections::{BTreeMap, HashSet};
use util::{RotatingLogger, Address};
use util::misc::version_data;
@ -40,7 +40,7 @@ use v1::types::{
Peers, Transaction, RpcSettings, Histogram,
TransactionStats, LocalTransactionStatus,
BlockNumber, ConsensusCapability, VersionInfo,
OperationsInfo, ChainStatus,
OperationsInfo, DappId, ChainStatus,
};
use v1::helpers::{errors, SigningQueue, SignerService, NetworkSettings};
use v1::helpers::dispatch::DEFAULT_MAC;
@ -111,7 +111,36 @@ impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
M: MinerService + 'static,
C: MiningBlockChainClient + 'static,
S: SyncProvider + 'static,
U: UpdateService + 'static {
U: UpdateService + 'static,
{
fn accounts_info(&self, dapp: Trailing<DappId>) -> Result<BTreeMap<String, BTreeMap<String, String>>, Error> {
try!(self.active());
let dapp = dapp.0;
let store = take_weak!(self.accounts);
let dapp_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))
).into_iter().collect::<HashSet<_>>();
let info = try!(store.accounts_info().map_err(|e| errors::account("Could not fetch account info.", e)));
let other = store.addresses_info().expect("addresses_info always returns Ok; qed");
Ok(info
.into_iter()
.chain(other.into_iter())
.filter(|&(ref a, _)| dapp_accounts.contains(a))
.map(|(a, v)| {
let m = map![
"name".to_owned() => v.name
];
(format!("0x{}", a.hex()), m)
})
.collect()
)
}
fn transactions_limit(&self) -> Result<usize, Error> {
try!(self.active());
@ -350,24 +379,6 @@ impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
take_weak!(self.sync).enode().ok_or_else(errors::network_disabled)
}
fn accounts(&self) -> Result<BTreeMap<String, BTreeMap<String, String>>, Error> {
try!(self.active());
let store = take_weak!(self.accounts);
let info = try!(store.accounts_info().map_err(|e| errors::account("Could not fetch account info.", e)));
let other = store.addresses_info().expect("addresses_info always returns Ok; qed");
Ok(info.into_iter().chain(other.into_iter()).map(|(a, v)| {
let mut m = map![
"name".to_owned() => v.name,
"meta".to_owned() => v.meta
];
if let &Some(ref uuid) = &v.uuid {
m.insert("uuid".to_owned(), format!("{}", uuid));
}
(format!("0x{}", a.hex()), m)
}).collect())
}
fn consensus_capability(&self) -> Result<ConsensusCapability, Error> {
try!(self.active());
let updater = take_weak!(self.updater);

View File

@ -23,7 +23,7 @@ use ethkey::{Brain, Generator};
use ethcore::account_provider::AccountProvider;
use ethcore::client::MiningBlockChainClient;
use jsonrpc_core::{Value, Error, to_value};
use jsonrpc_core::Error;
use v1::traits::ParityAccounts;
use v1::types::{H160 as RpcH160, H256 as RpcH256, DappId};
use v1::helpers::errors;
@ -51,23 +51,21 @@ impl<C> ParityAccountsClient<C> where C: MiningBlockChainClient {
}
impl<C: 'static> ParityAccounts for ParityAccountsClient<C> where C: MiningBlockChainClient {
fn accounts_info(&self) -> Result<BTreeMap<String, Value>, Error> {
fn all_accounts_info(&self) -> Result<BTreeMap<String, BTreeMap<String, String>>, Error> {
try!(self.active());
let store = take_weak!(self.accounts);
let info = try!(store.accounts_info().map_err(|e| errors::account("Could not fetch account info.", e)));
let other = store.addresses_info().expect("addresses_info always returns Ok; qed");
Ok(other.into_iter().chain(info.into_iter()).map(|(a, v)| {
let m = map![
"name".to_owned() => to_value(&v.name),
"meta".to_owned() => to_value(&v.meta),
"uuid".to_owned() => if let &Some(ref uuid) = &v.uuid {
to_value(uuid)
} else {
Value::Null
}
Ok(info.into_iter().chain(other.into_iter()).map(|(a, v)| {
let mut m = map![
"name".to_owned() => v.name,
"meta".to_owned() => v.meta
];
(format!("0x{}", a.hex()), Value::Object(m))
if let &Some(ref uuid) = &v.uuid {
m.insert("uuid".to_owned(), format!("{}", uuid));
}
(format!("0x{}", a.hex()), m)
}).collect())
}

View File

@ -99,6 +99,30 @@ impl Dependencies {
}
}
#[test]
fn rpc_parity_accounts_info() {
let deps = Dependencies::new();
let io = deps.default_client();
deps.accounts.new_account("").unwrap();
let accounts = deps.accounts.accounts().unwrap();
assert_eq!(accounts.len(), 1);
let address = accounts[0];
deps.accounts.set_account_name(address.clone(), "Test".to_owned()).unwrap();
deps.accounts.set_account_meta(address.clone(), "{foo: 69}".to_owned()).unwrap();
let request = r#"{"jsonrpc": "2.0", "method": "parity_accountsInfo", "params": [], "id": 1}"#;
let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{}\":{{\"name\":\"Test\"}}}},\"id\":1}}", address.hex());
assert_eq!(io.handle_request_sync(request), Some(response));
// Change the whitelist
deps.accounts.set_new_dapps_whitelist(Some(vec![1.into()])).unwrap();
let request = r#"{"jsonrpc": "2.0", "method": "parity_accountsInfo", "params": [], "id": 1}"#;
let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{}},\"id\":1}}");
assert_eq!(io.handle_request_sync(request), Some(response));
}
#[test]
fn rpc_parity_consensus_capability() {
let deps = Dependencies::new();

View File

@ -68,7 +68,7 @@ fn should_be_able_to_get_account_info() {
tester.accounts.set_account_name(address.clone(), "Test".to_owned()).unwrap();
tester.accounts.set_account_meta(address.clone(), "{foo: 69}".to_owned()).unwrap();
let request = r#"{"jsonrpc": "2.0", "method": "parity_accountsInfo", "params": [], "id": 1}"#;
let request = r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 1}"#;
let res = tester.io.handle_request_sync(request);
let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{}\":{{\"meta\":\"{{foo: 69}}\",\"name\":\"Test\",\"uuid\":\"{}\"}}}},\"id\":1}}", address.hex(), uuid);
assert_eq!(res, Some(response));
@ -89,7 +89,7 @@ fn should_be_able_to_set_name() {
let uuid = tester.accounts.accounts_info().unwrap().get(&address).unwrap().uuid.as_ref().unwrap().clone();
let request = r#"{"jsonrpc": "2.0", "method": "parity_accountsInfo", "params": [], "id": 1}"#;
let request = r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 1}"#;
let res = tester.io.handle_request_sync(request);
let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{}\":{{\"meta\":\"{{}}\",\"name\":\"Test\",\"uuid\":\"{}\"}}}},\"id\":1}}", address.hex(), uuid);
assert_eq!(res, Some(response));
@ -110,7 +110,7 @@ fn should_be_able_to_set_meta() {
let uuid = tester.accounts.accounts_info().unwrap().get(&address).unwrap().uuid.as_ref().unwrap().clone();
let request = r#"{"jsonrpc": "2.0", "method": "parity_accountsInfo", "params": [], "id": 1}"#;
let request = r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 1}"#;
let res = tester.io.handle_request_sync(request);
let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{}\":{{\"meta\":\"{{foo: 69}}\",\"name\":\"\",\"uuid\":\"{}\"}}}},\"id\":1}}", address.hex(), uuid);
assert_eq!(res, Some(response));
@ -210,9 +210,9 @@ fn should_be_able_to_remove_address() {
assert_eq!(res, Some(response.into()));
// verify it exists
let request = r#"{"jsonrpc": "2.0", "method": "parity_accountsInfo", "params": [], "id": 2}"#;
let request = r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 2}"#;
let res = tester.io.handle_request_sync(request);
let response = r#"{"jsonrpc":"2.0","result":{"0x000baba1000baba2000baba3000baba4000baba5":{"meta":"{}","name":"Test","uuid":null}},"id":2}"#;
let response = r#"{"jsonrpc":"2.0","result":{"0x000baba1000baba2000baba3000baba4000baba5":{"meta":"{}","name":"Test"}},"id":2}"#;
assert_eq!(res, Some(response.into()));
// remove the address
@ -222,7 +222,7 @@ fn should_be_able_to_remove_address() {
assert_eq!(res, Some(response.into()));
// verify empty
let request = r#"{"jsonrpc": "2.0", "method": "parity_accountsInfo", "params": [], "id": 4}"#;
let request = r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 4}"#;
let res = tester.io.handle_request_sync(request);
let response = r#"{"jsonrpc":"2.0","result":{},"id":4}"#;
assert_eq!(res, Some(response.into()));

View File

@ -26,12 +26,16 @@ use v1::types::{
Peers, Transaction, RpcSettings, Histogram,
TransactionStats, LocalTransactionStatus,
BlockNumber, ConsensusCapability, VersionInfo,
OperationsInfo, ChainStatus,
OperationsInfo, DappId, ChainStatus,
};
build_rpc_trait! {
/// Parity-specific rpc interface.
pub trait Parity {
/// Returns accounts information.
#[rpc(name = "parity_accountsInfo")]
fn accounts_info(&self, Trailing<DappId>) -> Result<BTreeMap<String, BTreeMap<String, String>>, Error>;
/// Returns current transactions limit.
#[rpc(name = "parity_transactionsLimit")]
fn transactions_limit(&self) -> Result<usize, Error>;
@ -159,10 +163,6 @@ build_rpc_trait! {
#[rpc(name = "parity_enode")]
fn enode(&self) -> Result<String, Error>;
/// Returns accounts information.
#[rpc(name = "parity_accounts")]
fn accounts(&self) -> Result<BTreeMap<String, BTreeMap<String, String>>, Error>;
/// Returns information on current consensus capability.
#[rpc(name = "parity_consensusCapability")]
fn consensus_capability(&self) -> Result<ConsensusCapability, Error>;

View File

@ -17,15 +17,15 @@
//! Parity Accounts-related rpc interface.
use std::collections::BTreeMap;
use jsonrpc_core::{Value, Error};
use jsonrpc_core::Error;
use v1::types::{H160, H256, DappId};
build_rpc_trait! {
/// Personal Parity rpc interface.
pub trait ParityAccounts {
/// Returns accounts information.
#[rpc(name = "parity_accountsInfo")]
fn accounts_info(&self) -> Result<BTreeMap<String, Value>, Error>;
#[rpc(name = "parity_allAccountsInfo")]
fn all_accounts_info(&self) -> Result<BTreeMap<String, BTreeMap<String, String>>, Error>;
/// Creates new account from the given phrase using standard brainwallet mechanism.
/// Second parameter is password for the new account.