Wallet rpcs (#1898)
* Add wallet RPCs. * Add wordlist file. * Add standard brain wallet tests. * Allow import of JSON wallets. * Address grumble.
This commit is contained in:
committed by
Arkadiy Paronyan
parent
c32244ea4a
commit
286b67d54b
@@ -66,7 +66,7 @@ impl Into<json::KeyFile> for SafeAccount {
|
||||
json::KeyFile {
|
||||
id: From::from(self.id),
|
||||
version: self.version.into(),
|
||||
address: self.address.into(), //From::from(self.address),
|
||||
address: self.address.into(),
|
||||
crypto: self.crypto.into(),
|
||||
name: Some(self.name.into()),
|
||||
meta: Some(self.meta.into()),
|
||||
@@ -150,13 +150,16 @@ impl SafeAccount {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_file(json: json::KeyFile, filename: String) -> Self {
|
||||
/// Create a new `SafeAccount` from the given `json`; if it was read from a
|
||||
/// file, the `filename` should be `Some` name. If it is as yet anonymous, then it
|
||||
/// can be left `None`.
|
||||
pub fn from_file(json: json::KeyFile, filename: Option<String>) -> Self {
|
||||
SafeAccount {
|
||||
id: json.id.into(),
|
||||
version: json.version.into(),
|
||||
address: json.address.into(),
|
||||
crypto: json.crypto.into(),
|
||||
filename: Some(filename),
|
||||
filename: filename,
|
||||
name: json.name.unwrap_or(String::new()),
|
||||
meta: json.meta.unwrap_or("{}".to_owned()),
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ impl DiskDirectory {
|
||||
.zip(paths.into_iter())
|
||||
.map(|(file, path)| match file {
|
||||
Ok(file) => Ok((path.clone(), SafeAccount::from_file(
|
||||
file, path.file_name().and_then(|n| n.to_str()).expect("Keys have valid UTF8 names only.").to_owned()
|
||||
file, Some(path.file_name().and_then(|n| n.to_str()).expect("Keys have valid UTF8 names only.").to_owned())
|
||||
))),
|
||||
Err(err) => Err(Error::InvalidKeyFile(format!("{:?}: {}", path, err))),
|
||||
})
|
||||
|
||||
@@ -24,7 +24,9 @@ use ethkey::{Signature, Address, Message, Secret};
|
||||
use dir::KeyDirectory;
|
||||
use account::SafeAccount;
|
||||
use {Error, SecretStore};
|
||||
use json;
|
||||
use json::UUID;
|
||||
use presale::PresaleWallet;
|
||||
|
||||
pub struct EthStore {
|
||||
dir: Box<KeyDirectory>,
|
||||
@@ -89,6 +91,23 @@ impl SecretStore for EthStore {
|
||||
Ok(address)
|
||||
}
|
||||
|
||||
fn import_presale(&self, json: &[u8], password: &str) -> Result<Address, Error> {
|
||||
let json_wallet = try!(json::PresaleWallet::load(json).map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned())));
|
||||
let wallet = PresaleWallet::from(json_wallet);
|
||||
let keypair = try!(wallet.decrypt(password).map_err(|_| Error::InvalidPassword));
|
||||
self.insert_account(keypair.secret().clone(), password)
|
||||
}
|
||||
|
||||
fn import_wallet(&self, json: &[u8], password: &str) -> Result<Address, Error> {
|
||||
let json_keyfile = try!(json::KeyFile::load(json).map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned())));
|
||||
let mut safe_account = SafeAccount::from_file(json_keyfile, None);
|
||||
let secret = try!(safe_account.crypto.secret(password).map_err(|_| Error::InvalidPassword));
|
||||
safe_account.address = try!(KeyPair::from_secret(secret)).address();
|
||||
let address = safe_account.address.clone();
|
||||
try!(self.save(safe_account));
|
||||
Ok(address)
|
||||
}
|
||||
|
||||
fn accounts(&self) -> Result<Vec<Address>, Error> {
|
||||
try!(self.reload_accounts());
|
||||
Ok(self.cache.read().unwrap().keys().cloned().collect())
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#![cfg_attr(feature="nightly", plugin(serde_macros))]
|
||||
|
||||
extern crate libc;
|
||||
extern crate itertools;
|
||||
extern crate rand;
|
||||
extern crate time;
|
||||
extern crate serde;
|
||||
@@ -25,6 +26,8 @@ extern crate serde_json;
|
||||
extern crate rustc_serialize;
|
||||
extern crate crypto as rcrypto;
|
||||
extern crate tiny_keccak;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
// reexport it nicely
|
||||
extern crate ethkey as _ethkey;
|
||||
|
||||
@@ -48,4 +51,5 @@ pub use self::ethstore::EthStore;
|
||||
pub use self::import::import_accounts;
|
||||
pub use self::presale::PresaleWallet;
|
||||
pub use self::secret_store::SecretStore;
|
||||
pub use self::random::random_phrase;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use rand::{Rng, OsRng};
|
||||
use itertools::Itertools;
|
||||
|
||||
pub trait Random {
|
||||
fn random() -> Self where Self: Sized;
|
||||
@@ -37,3 +38,25 @@ impl Random for [u8; 32] {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a string which is a random phrase of a number of lowercase words.
|
||||
///
|
||||
/// `words` is the number of words, chosen from a dictionary of 7,530. An value of
|
||||
/// 12 gives 155 bits of entropy (almost saturating address space); 20 gives 258 bits
|
||||
/// which is enough to saturate 32-byte key space
|
||||
pub fn random_phrase(words: usize) -> String {
|
||||
lazy_static! {
|
||||
static ref WORDS: Vec<String> = String::from_utf8_lossy(include_bytes!("../res/wordlist.txt"))
|
||||
.split("\n")
|
||||
.map(|s| s.to_owned())
|
||||
.collect();
|
||||
}
|
||||
let mut rng = OsRng::new().unwrap();
|
||||
(0..words).map(|_| rng.choose(&WORDS).unwrap()).join(" ")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_produce_right_number_of_words() {
|
||||
let p = random_phrase(10);
|
||||
assert_eq!(p.split(" ").count(), 10);
|
||||
}
|
||||
@@ -21,6 +21,10 @@ use json::UUID;
|
||||
pub trait SecretStore: Send + Sync {
|
||||
fn insert_account(&self, secret: Secret, password: &str) -> Result<Address, Error>;
|
||||
|
||||
fn import_presale(&self, json: &[u8], password: &str) -> Result<Address, Error>;
|
||||
|
||||
fn import_wallet(&self, json: &[u8], password: &str) -> Result<Address, Error>;
|
||||
|
||||
fn accounts(&self) -> Result<Vec<Address>, Error>;
|
||||
|
||||
fn change_password(&self, account: &Address, old_password: &str, new_password: &str) -> Result<(), Error>;
|
||||
|
||||
Reference in New Issue
Block a user