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:
parent
c32244ea4a
commit
286b67d54b
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -416,6 +416,7 @@ dependencies = [
|
|||||||
"ethcore-ipc 1.4.0",
|
"ethcore-ipc 1.4.0",
|
||||||
"ethcore-util 1.4.0",
|
"ethcore-util 1.4.0",
|
||||||
"ethjson 0.1.0",
|
"ethjson 0.1.0",
|
||||||
|
"ethstore 0.1.0",
|
||||||
"ethsync 1.4.0",
|
"ethsync 1.4.0",
|
||||||
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)",
|
"json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)",
|
||||||
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -505,6 +506,8 @@ name = "ethstore"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ethkey 0.2.0",
|
"ethkey 0.2.0",
|
||||||
|
"itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -191,6 +191,18 @@ impl AccountProvider {
|
|||||||
Ok(Address::from(address).into())
|
Ok(Address::from(address).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Import a new presale wallet.
|
||||||
|
pub fn import_presale(&self, presale_json: &[u8], password: &str) -> Result<H160, Error> {
|
||||||
|
let address = try!(self.sstore.import_presale(presale_json, password));
|
||||||
|
Ok(Address::from(address).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Import a new presale wallet.
|
||||||
|
pub fn import_wallet(&self, json: &[u8], password: &str) -> Result<H160, Error> {
|
||||||
|
let address = try!(self.sstore.import_wallet(json, password));
|
||||||
|
Ok(Address::from(address).into())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns addresses of all accounts.
|
/// Returns addresses of all accounts.
|
||||||
pub fn accounts(&self) -> Result<Vec<H160>, Error> {
|
pub fn accounts(&self) -> Result<Vec<H160>, Error> {
|
||||||
let accounts = try!(self.sstore.accounts()).into_iter().map(|a| H160(a.into())).collect();
|
let accounts = try!(self.sstore.accounts()).into_iter().map(|a| H160(a.into())).collect();
|
||||||
|
@ -5,7 +5,7 @@ authors = ["debris <marek.kotewicz@gmail.com>"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand = "0.3.14"
|
rand = "0.3.14"
|
||||||
lazy_static = "0.2.1"
|
lazy_static = "0.2"
|
||||||
tiny-keccak = "1.0"
|
tiny-keccak = "1.0"
|
||||||
eth-secp256k1 = { git = "https://github.com/ethcore/rust-secp256k1" }
|
eth-secp256k1 = { git = "https://github.com/ethcore/rust-secp256k1" }
|
||||||
rustc-serialize = "0.3"
|
rustc-serialize = "0.3"
|
||||||
|
@ -16,6 +16,8 @@ rust-crypto = "0.2.36"
|
|||||||
tiny-keccak = "1.0"
|
tiny-keccak = "1.0"
|
||||||
docopt = { version = "0.6", optional = true }
|
docopt = { version = "0.6", optional = true }
|
||||||
time = "0.1.34"
|
time = "0.1.34"
|
||||||
|
lazy_static = "0.2"
|
||||||
|
itertools = "0.4"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
serde_codegen = { version = "0.7", optional = true }
|
serde_codegen = { version = "0.7", optional = true }
|
||||||
|
7530
ethstore/res/wordlist.txt
Normal file
7530
ethstore/res/wordlist.txt
Normal file
File diff suppressed because it is too large
Load Diff
@ -66,7 +66,7 @@ impl Into<json::KeyFile> for SafeAccount {
|
|||||||
json::KeyFile {
|
json::KeyFile {
|
||||||
id: From::from(self.id),
|
id: From::from(self.id),
|
||||||
version: self.version.into(),
|
version: self.version.into(),
|
||||||
address: self.address.into(), //From::from(self.address),
|
address: self.address.into(),
|
||||||
crypto: self.crypto.into(),
|
crypto: self.crypto.into(),
|
||||||
name: Some(self.name.into()),
|
name: Some(self.name.into()),
|
||||||
meta: Some(self.meta.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 {
|
SafeAccount {
|
||||||
id: json.id.into(),
|
id: json.id.into(),
|
||||||
version: json.version.into(),
|
version: json.version.into(),
|
||||||
address: json.address.into(),
|
address: json.address.into(),
|
||||||
crypto: json.crypto.into(),
|
crypto: json.crypto.into(),
|
||||||
filename: Some(filename),
|
filename: filename,
|
||||||
name: json.name.unwrap_or(String::new()),
|
name: json.name.unwrap_or(String::new()),
|
||||||
meta: json.meta.unwrap_or("{}".to_owned()),
|
meta: json.meta.unwrap_or("{}".to_owned()),
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ impl DiskDirectory {
|
|||||||
.zip(paths.into_iter())
|
.zip(paths.into_iter())
|
||||||
.map(|(file, path)| match file {
|
.map(|(file, path)| match file {
|
||||||
Ok(file) => Ok((path.clone(), SafeAccount::from_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))),
|
Err(err) => Err(Error::InvalidKeyFile(format!("{:?}: {}", path, err))),
|
||||||
})
|
})
|
||||||
|
@ -24,7 +24,9 @@ use ethkey::{Signature, Address, Message, Secret};
|
|||||||
use dir::KeyDirectory;
|
use dir::KeyDirectory;
|
||||||
use account::SafeAccount;
|
use account::SafeAccount;
|
||||||
use {Error, SecretStore};
|
use {Error, SecretStore};
|
||||||
|
use json;
|
||||||
use json::UUID;
|
use json::UUID;
|
||||||
|
use presale::PresaleWallet;
|
||||||
|
|
||||||
pub struct EthStore {
|
pub struct EthStore {
|
||||||
dir: Box<KeyDirectory>,
|
dir: Box<KeyDirectory>,
|
||||||
@ -89,6 +91,23 @@ impl SecretStore for EthStore {
|
|||||||
Ok(address)
|
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> {
|
fn accounts(&self) -> Result<Vec<Address>, Error> {
|
||||||
try!(self.reload_accounts());
|
try!(self.reload_accounts());
|
||||||
Ok(self.cache.read().unwrap().keys().cloned().collect())
|
Ok(self.cache.read().unwrap().keys().cloned().collect())
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#![cfg_attr(feature="nightly", plugin(serde_macros))]
|
#![cfg_attr(feature="nightly", plugin(serde_macros))]
|
||||||
|
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
extern crate itertools;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
@ -25,6 +26,8 @@ extern crate serde_json;
|
|||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
extern crate crypto as rcrypto;
|
extern crate crypto as rcrypto;
|
||||||
extern crate tiny_keccak;
|
extern crate tiny_keccak;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
// reexport it nicely
|
// reexport it nicely
|
||||||
extern crate ethkey as _ethkey;
|
extern crate ethkey as _ethkey;
|
||||||
|
|
||||||
@ -48,4 +51,5 @@ pub use self::ethstore::EthStore;
|
|||||||
pub use self::import::import_accounts;
|
pub use self::import::import_accounts;
|
||||||
pub use self::presale::PresaleWallet;
|
pub use self::presale::PresaleWallet;
|
||||||
pub use self::secret_store::SecretStore;
|
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/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use rand::{Rng, OsRng};
|
use rand::{Rng, OsRng};
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
pub trait Random {
|
pub trait Random {
|
||||||
fn random() -> Self where Self: Sized;
|
fn random() -> Self where Self: Sized;
|
||||||
@ -37,3 +38,25 @@ impl Random for [u8; 32] {
|
|||||||
result
|
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 {
|
pub trait SecretStore: Send + Sync {
|
||||||
fn insert_account(&self, secret: Secret, password: &str) -> Result<Address, Error>;
|
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 accounts(&self) -> Result<Vec<Address>, Error>;
|
||||||
|
|
||||||
fn change_password(&self, account: &Address, old_password: &str, new_password: &str) -> Result<(), Error>;
|
fn change_password(&self, account: &Address, old_password: &str, new_password: &str) -> Result<(), Error>;
|
||||||
|
@ -17,6 +17,7 @@ jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.gi
|
|||||||
ethcore-io = { path = "../util/io" }
|
ethcore-io = { path = "../util/io" }
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
ethcore = { path = "../ethcore" }
|
ethcore = { path = "../ethcore" }
|
||||||
|
ethstore = { path = "../ethstore" }
|
||||||
ethash = { path = "../ethash" }
|
ethash = { path = "../ethash" }
|
||||||
ethsync = { path = "../sync" }
|
ethsync = { path = "../sync" }
|
||||||
ethjson = { path = "../json" }
|
ethjson = { path = "../json" }
|
||||||
|
@ -30,6 +30,7 @@ extern crate jsonrpc_http_server;
|
|||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
extern crate ethcore_io as io;
|
extern crate ethcore_io as io;
|
||||||
extern crate ethcore;
|
extern crate ethcore;
|
||||||
|
extern crate ethstore;
|
||||||
extern crate ethsync;
|
extern crate ethsync;
|
||||||
extern crate transient_hashmap;
|
extern crate transient_hashmap;
|
||||||
extern crate json_ipc_server as ipc;
|
extern crate json_ipc_server as ipc;
|
||||||
|
@ -19,6 +19,7 @@ use util::{RotatingLogger};
|
|||||||
use util::misc::version_data;
|
use util::misc::version_data;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::collections::{BTreeMap};
|
use std::collections::{BTreeMap};
|
||||||
|
use ethstore::random_phrase;
|
||||||
use ethcore::client::{MiningBlockChainClient};
|
use ethcore::client::{MiningBlockChainClient};
|
||||||
use jsonrpc_core::*;
|
use jsonrpc_core::*;
|
||||||
use ethcore::miner::MinerService;
|
use ethcore::miner::MinerService;
|
||||||
@ -165,4 +166,11 @@ impl<C, M> Ethcore for EthcoreClient<C, M> where M: MinerService + 'static, C: M
|
|||||||
Some(ref queue) => to_value(&queue.len()),
|
Some(ref queue) => to_value(&queue.len()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_secret_phrase(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
try!(self.active());
|
||||||
|
try!(expect_no_params(params));
|
||||||
|
|
||||||
|
to_value(&random_phrase(12))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ use v1::helpers::{errors, TransactionRequest as TRequest};
|
|||||||
use v1::helpers::params::expect_no_params;
|
use v1::helpers::params::expect_no_params;
|
||||||
use v1::helpers::dispatch::unlock_sign_and_dispatch;
|
use v1::helpers::dispatch::unlock_sign_and_dispatch;
|
||||||
use ethcore::account_provider::AccountProvider;
|
use ethcore::account_provider::AccountProvider;
|
||||||
use util::Address;
|
use util::{Address, KeyPair};
|
||||||
use ethcore::client::MiningBlockChainClient;
|
use ethcore::client::MiningBlockChainClient;
|
||||||
use ethcore::miner::MinerService;
|
use ethcore::miner::MinerService;
|
||||||
|
|
||||||
@ -89,6 +89,32 @@ impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBl
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_account_from_phrase(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
try!(self.active());
|
||||||
|
from_params::<(String, String, )>(params).and_then(
|
||||||
|
|(phrase, pass, )| {
|
||||||
|
let store = take_weak!(self.accounts);
|
||||||
|
match store.insert_account(*KeyPair::from_phrase(&phrase).secret(), &pass) {
|
||||||
|
Ok(address) => to_value(&RpcH160::from(address)),
|
||||||
|
Err(e) => Err(errors::account("Could not create account.", e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_account_from_wallet(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
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) => to_value(&RpcH160::from(address)),
|
||||||
|
Err(e) => Err(errors::account("Could not create account.", e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn unlock_account(&self, params: Params) -> Result<Value, Error> {
|
fn unlock_account(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
from_params::<(RpcH160, String, Option<u64>)>(params).and_then(
|
from_params::<(RpcH160, String, Option<u64>)>(params).and_then(
|
||||||
|
@ -67,6 +67,9 @@ pub trait Ethcore: Sized + Send + Sync + 'static {
|
|||||||
/// Returns error when signer is disabled
|
/// Returns error when signer is disabled
|
||||||
fn unsigned_transactions_count(&self, _: Params) -> Result<Value, Error>;
|
fn unsigned_transactions_count(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
|
/// Returns a cryptographically random phrase sufficient for securely seeding a secret key.
|
||||||
|
fn generate_secret_phrase(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
/// Should be used to convert object to io delegate.
|
/// Should be used to convert object to io delegate.
|
||||||
fn to_delegate(self) -> IoDelegate<Self> {
|
fn to_delegate(self) -> IoDelegate<Self> {
|
||||||
let mut delegate = IoDelegate::new(Arc::new(self));
|
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||||
@ -86,6 +89,7 @@ pub trait Ethcore: Sized + Send + Sync + 'static {
|
|||||||
delegate.add_method("ethcore_defaultExtraData", Ethcore::default_extra_data);
|
delegate.add_method("ethcore_defaultExtraData", Ethcore::default_extra_data);
|
||||||
delegate.add_method("ethcore_gasPriceStatistics", Ethcore::gas_price_statistics);
|
delegate.add_method("ethcore_gasPriceStatistics", Ethcore::gas_price_statistics);
|
||||||
delegate.add_method("ethcore_unsignedTransactionsCount", Ethcore::unsigned_transactions_count);
|
delegate.add_method("ethcore_unsignedTransactionsCount", Ethcore::unsigned_transactions_count);
|
||||||
|
delegate.add_method("ethcore_generateSecretPhrase", Ethcore::generate_secret_phrase);
|
||||||
|
|
||||||
delegate
|
delegate
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,17 @@ pub trait Personal: Sized + Send + Sync + 'static {
|
|||||||
fn accounts(&self, _: Params) -> Result<Value, Error>;
|
fn accounts(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
/// Creates new account (it becomes new current unlocked account)
|
/// Creates new account (it becomes new current unlocked account)
|
||||||
|
/// Param is the password for the account.
|
||||||
fn new_account(&self, _: Params) -> Result<Value, Error>;
|
fn new_account(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
|
/// Creates new account from the given phrase using standard brainwallet mechanism.
|
||||||
|
/// Second parameter is password for the new account.
|
||||||
|
fn new_account_from_phrase(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
|
/// Creates new account from the given JSON wallet.
|
||||||
|
/// Second parameter is password for the wallet and the new account.
|
||||||
|
fn new_account_from_wallet(&self, params: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
/// Unlocks specified account for use (can only be one unlocked account at one moment)
|
/// Unlocks specified account for use (can only be one unlocked account at one moment)
|
||||||
fn unlock_account(&self, _: Params) -> Result<Value, Error>;
|
fn unlock_account(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
@ -51,6 +60,8 @@ pub trait Personal: Sized + Send + Sync + 'static {
|
|||||||
delegate.add_method("personal_signerEnabled", Personal::signer_enabled);
|
delegate.add_method("personal_signerEnabled", Personal::signer_enabled);
|
||||||
delegate.add_method("personal_listAccounts", Personal::accounts);
|
delegate.add_method("personal_listAccounts", Personal::accounts);
|
||||||
delegate.add_method("personal_newAccount", Personal::new_account);
|
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_unlockAccount", Personal::unlock_account);
|
||||||
delegate.add_method("personal_signAndSendTransaction", Personal::sign_and_send_transaction);
|
delegate.add_method("personal_signAndSendTransaction", Personal::sign_and_send_transaction);
|
||||||
delegate.add_method("personal_setAccountName", Personal::set_account_name);
|
delegate.add_method("personal_setAccountName", Personal::set_account_name);
|
||||||
|
@ -134,6 +134,29 @@ impl KeyPair {
|
|||||||
public: p,
|
public: p,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: move to ethstore/secret.rs once @debris has refactored necessary dependencies into own crate
|
||||||
|
/// Convert the given phrase into a secret as per brain-wallet spec.
|
||||||
|
/// Taken from https://github.com/ethereum/wiki/wiki/Brain-Wallet
|
||||||
|
/// Note particularly secure for low-entropy keys.
|
||||||
|
pub fn from_phrase(phrase: &str) -> KeyPair {
|
||||||
|
let mut h = phrase.as_bytes().sha3();
|
||||||
|
for _ in 0..16384 {
|
||||||
|
h = h.sha3();
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
let r = KeyPair::from_secret(h);
|
||||||
|
if r.is_ok() {
|
||||||
|
let r = r.unwrap();
|
||||||
|
if r.address()[0] == 0 {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h = h.sha3();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new random key pair
|
/// Create a new random key pair
|
||||||
pub fn create() -> Result<KeyPair, CryptoError> {
|
pub fn create() -> Result<KeyPair, CryptoError> {
|
||||||
let context = &SECP256K1;
|
let context = &SECP256K1;
|
||||||
@ -443,6 +466,11 @@ mod tests {
|
|||||||
assert_eq!(pair.public().hex(), "101b3ef5a4ea7a1c7928e24c4c75fd053c235d7b80c22ae5c03d145d0ac7396e2a4ffff9adee3133a7b05044a5cee08115fd65145e5165d646bde371010d803c");
|
assert_eq!(pair.public().hex(), "101b3ef5a4ea7a1c7928e24c4c75fd053c235d7b80c22ae5c03d145d0ac7396e2a4ffff9adee3133a7b05044a5cee08115fd65145e5165d646bde371010d803c");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_key_from_phrase() {
|
||||||
|
assert_eq!(KeyPair::from_phrase("correct horse battery staple").address(), "0021f80b7f29b9c84e8099c2c6c74a46ed2268c4".into());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ecies_shared() {
|
fn ecies_shared() {
|
||||||
let kp = KeyPair::create().unwrap();
|
let kp = KeyPair::create().unwrap();
|
||||||
|
Loading…
Reference in New Issue
Block a user