From c2ffa904789f27d1a4982494489c481c87b3e829 Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 21 Jun 2016 13:09:27 +0200 Subject: [PATCH 01/10] importing presale wallet in progress --- ethstore/src/crypto.rs | 11 ++++++-- ethstore/src/ethkey.rs | 8 ++++++ ethstore/src/json/hash.rs | 27 +++++++++++++++++- ethstore/src/json/mod.rs.in | 4 ++- ethstore/src/json/presale.rs | 42 ++++++++++++++++++++++++++++ ethstore/src/lib.rs | 1 + ethstore/src/presale.rs | 53 ++++++++++++++++++++++++++++++++++++ 7 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 ethstore/src/json/presale.rs create mode 100644 ethstore/src/presale.rs diff --git a/ethstore/src/crypto.rs b/ethstore/src/crypto.rs index 2858808d7..2733fa720 100644 --- a/ethstore/src/crypto.rs +++ b/ethstore/src/crypto.rs @@ -65,8 +65,8 @@ impl Keccak256<[u8; 32]> for [u8] { /// AES encryption pub mod aes { - use rcrypto::blockmodes::CtrMode; - use rcrypto::aessafe::AesSafe128Encryptor; + use rcrypto::blockmodes::{CtrMode, CbcDecryptor, PkcsPadding}; + use rcrypto::aessafe::{AesSafe128Encryptor, AesSafe128Decryptor}; use rcrypto::symmetriccipher::{Encryptor, Decryptor}; use rcrypto::buffer::{RefReadBuffer, RefWriteBuffer}; @@ -81,5 +81,12 @@ pub mod aes { let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec()); encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true).expect("Invalid length or padding"); } + + /// Decrypt a message using cbc mode + pub fn decrypt_cbc(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) { + let mut encryptor = CbcDecryptor::new(AesSafe128Decryptor::new(k), PkcsPadding, iv.to_vec()); + encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true).expect("Invalid length or padding"); + } + } diff --git a/ethstore/src/ethkey.rs b/ethstore/src/ethkey.rs index eba877397..9d8858b79 100644 --- a/ethstore/src/ethkey.rs +++ b/ethstore/src/ethkey.rs @@ -31,3 +31,11 @@ impl From for Address { From::from(a) } } + +impl<'a> From<&'a json::H160> for Address { + fn from(json: &'a json::H160) -> Self { + let mut a = [0u8; 20]; + a.copy_from_slice(json); + From::from(a) + } +} diff --git a/ethstore/src/json/hash.rs b/ethstore/src/json/hash.rs index f0fb91e7a..2edc7b80b 100644 --- a/ethstore/src/json/hash.rs +++ b/ethstore/src/json/hash.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use std::fmt; +use std::ops; use std::str::FromStr; use rustc_serialize::hex::{FromHex, ToHex}; use serde::{Serialize, Serializer, Deserialize, Deserializer, Error as SerdeError}; @@ -22,9 +24,31 @@ use super::Error; macro_rules! impl_hash { ($name: ident, $size: expr) => { - #[derive(Debug, PartialEq)] pub struct $name([u8; $size]); + impl fmt::Debug for $name { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + let self_ref: &[u8] = &self.0; + write!(f, "{:?}", self_ref) + } + } + + impl PartialEq for $name { + fn eq(&self, other: &Self) -> bool { + let self_ref: &[u8] = &self.0; + let other_ref: &[u8] = &other.0; + self_ref == other_ref + } + } + + impl ops::Deref for $name { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl Serialize for $name { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { @@ -85,3 +109,4 @@ macro_rules! impl_hash { impl_hash!(H128, 16); impl_hash!(H160, 20); impl_hash!(H256, 32); +impl_hash!(H768, 96); diff --git a/ethstore/src/json/mod.rs.in b/ethstore/src/json/mod.rs.in index c4a67a287..7272d7e2e 100644 --- a/ethstore/src/json/mod.rs.in +++ b/ethstore/src/json/mod.rs.in @@ -5,14 +5,16 @@ mod hash; mod id; mod kdf; mod key_file; +mod presale; mod version; pub use self::cipher::{Cipher, CipherSer, CipherSerParams, Aes128Ctr}; pub use self::crypto::Crypto; pub use self::error::Error; -pub use self::hash::{H128, H160, H256}; +pub use self::hash::{H128, H160, H256, H768}; pub use self::id::UUID; pub use self::kdf::{Kdf, KdfSer, Prf, Pbkdf2, Scrypt, KdfSerParams}; pub use self::key_file::KeyFile; +pub use self::presale::PresaleWallet; pub use self::version::Version; diff --git a/ethstore/src/json/presale.rs b/ethstore/src/json/presale.rs new file mode 100644 index 000000000..cba50695f --- /dev/null +++ b/ethstore/src/json/presale.rs @@ -0,0 +1,42 @@ +use std::io::Read; +use serde_json; +use super::{H160, H768}; + +#[derive(Debug, PartialEq, Deserialize)] +pub struct PresaleWallet { + pub encseed: H768, + #[serde(rename = "ethaddr")] + pub address: H160, +} + +impl PresaleWallet { + pub fn load(reader: R) -> Result where R: Read { + serde_json::from_reader(reader) + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + use serde_json; + use json::{PresaleWallet, H160, H768}; + + #[test] + fn presale_wallet() { + let json = r#" + { + "encseed": "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066", + "ethaddr": "ede84640d1a1d3e06902048e67aa7db8d52c2ce1", + "email": "123@gmail.com", + "btcaddr": "1JvqEc6WLhg6GnyrLBe2ztPAU28KRfuseH" + } "#; + + let expected = PresaleWallet { + encseed: H768::from_str("137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066").unwrap(), + address: H160::from_str("ede84640d1a1d3e06902048e67aa7db8d52c2ce1").unwrap(), + }; + + let wallet: PresaleWallet = serde_json::from_str(json).unwrap(); + assert_eq!(expected, wallet); + } +} diff --git a/ethstore/src/lib.rs b/ethstore/src/lib.rs index 19f1c4806..90bb367fa 100644 --- a/ethstore/src/lib.rs +++ b/ethstore/src/lib.rs @@ -37,6 +37,7 @@ mod crypto; mod error; mod ethstore; mod import; +mod presale; mod random; mod secret_store; diff --git a/ethstore/src/presale.rs b/ethstore/src/presale.rs new file mode 100644 index 000000000..aad19b15c --- /dev/null +++ b/ethstore/src/presale.rs @@ -0,0 +1,53 @@ +use rcrypto::pbkdf2::pbkdf2; +use rcrypto::sha2::Sha256; +use rcrypto::hmac::Hmac; +use json::PresaleWallet; +use ethkey::{Address, Secret, KeyPair}; +use crypto::Keccak256; +use {crypto, Error}; + +fn decrypt_presale_wallet(wallet: &PresaleWallet, password: &str) -> Result { + let mut iv = [0u8; 16]; + iv.copy_from_slice(&wallet.encseed[..16]); + + let mut ciphertext = [0u8; 80]; + ciphertext.copy_from_slice(&wallet.encseed[16..]); + + let mut h_mac = Hmac::new(Sha256::new(), password.as_bytes()); + let mut derived_key = vec![0u8; 16]; + pbkdf2(&mut h_mac, password.as_bytes(), 2000, &mut derived_key); + + let mut key = [0u8; 64]; + crypto::aes::decrypt_cbc(&derived_key, &iv, &ciphertext, &mut key); + + let secret = Secret::from(key.keccak256()); + if let Ok(kp) = KeyPair::from_secret(secret) { + if kp.address() == Address::from(&wallet.address) { + return Ok(kp) + } + } + + Err(Error::InvalidPassword) +} + +#[cfg(test)] +mod tests { + use ethkey::{KeyPair, Address}; + use super::decrypt_presale_wallet; + use json::PresaleWallet; + + #[test] + fn test() { + let json = r#" + { + "encseed": "137103c28caeebbcea5d7f95edb97a289ded151b72159137cb7b2671f394f54cff8c121589dcb373e267225547b3c71cbdb54f6e48ec85cd549f96cf0dedb3bc0a9ac6c79b9c426c5878ca2c9d06ff42a23cb648312fc32ba83649de0928e066", + "ethaddr": "ede84640d1a1d3e06902048e67aa7db8d52c2ce1", + "email": "123@gmail.com", + "btcaddr": "1JvqEc6WLhg6GnyrLBe2ztPAU28KRfuseH" + } "#; + + let wallet = PresaleWallet::load(json.as_bytes()).unwrap(); + let kp = decrypt_presale_wallet(&wallet, "123").unwrap(); + assert_eq!(kp.address(), Address::from(wallet.address)); + } +} From a8a731ba11fee703516bc3aaaac1cfa9776873f5 Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 21 Jun 2016 13:30:32 +0200 Subject: [PATCH 02/10] PresaleWallet data structure --- ethstore/src/lib.rs | 1 + ethstore/src/presale.rs | 71 ++++++++++++++++++++++++++++------------- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/ethstore/src/lib.rs b/ethstore/src/lib.rs index 90bb367fa..96d860db4 100644 --- a/ethstore/src/lib.rs +++ b/ethstore/src/lib.rs @@ -45,5 +45,6 @@ pub use self::account::SafeAccount; pub use self::error::Error; pub use self::ethstore::EthStore; pub use self::import::import_accounts; +pub use self::presale::PresaleWallet; pub use self::secret_store::SecretStore; diff --git a/ethstore/src/presale.rs b/ethstore/src/presale.rs index aad19b15c..5ba57b8d4 100644 --- a/ethstore/src/presale.rs +++ b/ethstore/src/presale.rs @@ -1,40 +1,66 @@ +use std::fs; +use std::path::Path; use rcrypto::pbkdf2::pbkdf2; use rcrypto::sha2::Sha256; use rcrypto::hmac::Hmac; -use json::PresaleWallet; +use json; use ethkey::{Address, Secret, KeyPair}; use crypto::Keccak256; use {crypto, Error}; -fn decrypt_presale_wallet(wallet: &PresaleWallet, password: &str) -> Result { - let mut iv = [0u8; 16]; - iv.copy_from_slice(&wallet.encseed[..16]); +pub struct PresaleWallet { + iv: [u8; 16], + ciphertext: [u8; 80], + address: Address, +} - let mut ciphertext = [0u8; 80]; - ciphertext.copy_from_slice(&wallet.encseed[16..]); +impl From for PresaleWallet { + fn from(wallet: json::PresaleWallet) -> Self { + let mut iv = [0u8; 16]; + iv.copy_from_slice(&wallet.encseed[..16]); - let mut h_mac = Hmac::new(Sha256::new(), password.as_bytes()); - let mut derived_key = vec![0u8; 16]; - pbkdf2(&mut h_mac, password.as_bytes(), 2000, &mut derived_key); + let mut ciphertext = [0u8; 80]; + ciphertext.copy_from_slice(&wallet.encseed[16..]); - let mut key = [0u8; 64]; - crypto::aes::decrypt_cbc(&derived_key, &iv, &ciphertext, &mut key); - - let secret = Secret::from(key.keccak256()); - if let Ok(kp) = KeyPair::from_secret(secret) { - if kp.address() == Address::from(&wallet.address) { - return Ok(kp) + PresaleWallet { + iv: iv, + ciphertext: ciphertext, + address: Address::from(wallet.address), } } +} - Err(Error::InvalidPassword) +impl PresaleWallet { + pub fn open

(path: P) -> Result where P: AsRef { + let file = try!(fs::File::open(path)); + let presale = json::PresaleWallet::load(file).unwrap(); + Ok(PresaleWallet::from(presale)) + } + + pub fn decrypt(&self, password: &str) -> Result { + let mut h_mac = Hmac::new(Sha256::new(), password.as_bytes()); + let mut derived_key = vec![0u8; 16]; + pbkdf2(&mut h_mac, password.as_bytes(), 2000, &mut derived_key); + + let mut key = [0u8; 64]; + crypto::aes::decrypt_cbc(&derived_key, &self.iv, &self.ciphertext, &mut key); + + let secret = Secret::from(key.keccak256()); + if let Ok(kp) = KeyPair::from_secret(secret) { + if kp.address() == self.address { + return Ok(kp) + } + } + + Err(Error::InvalidPassword) + } } #[cfg(test)] mod tests { - use ethkey::{KeyPair, Address}; - use super::decrypt_presale_wallet; - use json::PresaleWallet; + use ethkey::Address; + use super::PresaleWallet; + use json; #[test] fn test() { @@ -46,8 +72,9 @@ mod tests { "btcaddr": "1JvqEc6WLhg6GnyrLBe2ztPAU28KRfuseH" } "#; - let wallet = PresaleWallet::load(json.as_bytes()).unwrap(); - let kp = decrypt_presale_wallet(&wallet, "123").unwrap(); + let wallet = json::PresaleWallet::load(json.as_bytes()).unwrap(); + let wallet = PresaleWallet::from(wallet); + let kp = wallet.decrypt("123").unwrap(); assert_eq!(kp.address(), Address::from(wallet.address)); } } From be03a6acbdaa58d884c6fb454e38178af68a8ed8 Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 21 Jun 2016 15:04:36 +0200 Subject: [PATCH 03/10] import-wallet option for ethstore executable --- ethstore/README.md | 15 +++++++++++++++ ethstore/src/bin/ethstore.rs | 11 ++++++++++- ethstore/src/crypto.rs | 7 ++++--- ethstore/src/ethkey.rs | 8 -------- ethstore/src/presale.rs | 2 +- 5 files changed, 30 insertions(+), 13 deletions(-) diff --git a/ethstore/README.md b/ethstore/README.md index aba4911bf..0cf50f454 100644 --- a/ethstore/README.md +++ b/ethstore/README.md @@ -20,6 +20,7 @@ Usage: ethstore change-pwd

[--dir DIR] ethstore list [--dir DIR] ethstore import [--src DIR] [--dir DIR] + ethstore import-wallet [--dir DIR] ethstore remove
[--dir DIR] ethstore sign
[--dir DIR] ethstore [-h | --help] @@ -38,6 +39,7 @@ Commands: change-pwd Change account password. list List accounts. import Import accounts from src. + import-wallet Import presale wallet. remove Remove account. sign Sign message. ``` @@ -119,6 +121,19 @@ ethstore list -- +#### `import-wallet [--dir DIR]` +*Import account from presale wallet.* + +- `` - presale wallet path +- `` - account password, any string +- `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity + +``` +e6a3d25a7cb7cd21cb720df5b5e8afd154af1bbb +``` + +-- + #### `remove
[--dir DIR]` *Remove account from secret store.* diff --git a/ethstore/src/bin/ethstore.rs b/ethstore/src/bin/ethstore.rs index 6020679d4..948d7a76e 100644 --- a/ethstore/src/bin/ethstore.rs +++ b/ethstore/src/bin/ethstore.rs @@ -24,7 +24,7 @@ use std::str::FromStr; use docopt::Docopt; use ethstore::ethkey::{Secret, Address, Message}; use ethstore::dir::{KeyDirectory, ParityDirectory, DiskDirectory, GethDirectory, DirectoryType}; -use ethstore::{EthStore, SecretStore, import_accounts, Error}; +use ethstore::{EthStore, SecretStore, import_accounts, Error, PresaleWallet}; pub const USAGE: &'static str = r#" Ethereum key management. @@ -35,6 +35,7 @@ Usage: ethstore change-pwd
[--dir DIR] ethstore list [--dir DIR] ethstore import [--src DIR] [--dir DIR] + ethstore import-wallet [--dir DIR] ethstore remove
[--dir DIR] ethstore sign
[--dir DIR] ethstore [-h | --help] @@ -53,6 +54,7 @@ Commands: change-pwd Change password. list List accounts. import Import accounts from src. + import-wallet Import presale wallet. remove Remove account. sign Sign message. "#; @@ -63,6 +65,7 @@ struct Args { cmd_change_pwd: bool, cmd_list: bool, cmd_import: bool, + cmd_import_wallet: bool, cmd_remove: bool, cmd_sign: bool, arg_secret: String, @@ -71,6 +74,7 @@ struct Args { arg_new_pwd: String, arg_address: String, arg_message: String, + arg_path: String, flag_src: String, flag_dir: String, } @@ -128,6 +132,11 @@ fn execute(command: I) -> Result where I: IntoIterator for [u8] { pub mod aes { use rcrypto::blockmodes::{CtrMode, CbcDecryptor, PkcsPadding}; use rcrypto::aessafe::{AesSafe128Encryptor, AesSafe128Decryptor}; - use rcrypto::symmetriccipher::{Encryptor, Decryptor}; + use rcrypto::symmetriccipher::{Encryptor, Decryptor, SymmetricCipherError}; use rcrypto::buffer::{RefReadBuffer, RefWriteBuffer}; /// Encrypt a message @@ -83,9 +83,10 @@ pub mod aes { } /// Decrypt a message using cbc mode - pub fn decrypt_cbc(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) { + pub fn decrypt_cbc(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) -> Result<(), SymmetricCipherError> { let mut encryptor = CbcDecryptor::new(AesSafe128Decryptor::new(k), PkcsPadding, iv.to_vec()); - encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true).expect("Invalid length or padding"); + try!(encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true)); + Ok(()) } } diff --git a/ethstore/src/ethkey.rs b/ethstore/src/ethkey.rs index 9d8858b79..eba877397 100644 --- a/ethstore/src/ethkey.rs +++ b/ethstore/src/ethkey.rs @@ -31,11 +31,3 @@ impl From for Address { From::from(a) } } - -impl<'a> From<&'a json::H160> for Address { - fn from(json: &'a json::H160) -> Self { - let mut a = [0u8; 20]; - a.copy_from_slice(json); - From::from(a) - } -} diff --git a/ethstore/src/presale.rs b/ethstore/src/presale.rs index 5ba57b8d4..8c8172473 100644 --- a/ethstore/src/presale.rs +++ b/ethstore/src/presale.rs @@ -43,7 +43,7 @@ impl PresaleWallet { pbkdf2(&mut h_mac, password.as_bytes(), 2000, &mut derived_key); let mut key = [0u8; 64]; - crypto::aes::decrypt_cbc(&derived_key, &self.iv, &self.ciphertext, &mut key); + try!(crypto::aes::decrypt_cbc(&derived_key, &self.iv, &self.ciphertext, &mut key).map_err(|_| Error::InvalidPassword)); let secret = Secret::from(key.keccak256()); if let Ok(kp) = KeyPair::from_secret(secret) { From 7136cd7057b2ea42910fd24fc718c1e2c91073f4 Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 21 Jun 2016 15:07:15 +0200 Subject: [PATCH 04/10] improved import wallet test --- ethstore/src/presale.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ethstore/src/presale.rs b/ethstore/src/presale.rs index 8c8172473..09c86abea 100644 --- a/ethstore/src/presale.rs +++ b/ethstore/src/presale.rs @@ -58,7 +58,6 @@ impl PresaleWallet { #[cfg(test)] mod tests { - use ethkey::Address; use super::PresaleWallet; use json; @@ -74,7 +73,7 @@ mod tests { let wallet = json::PresaleWallet::load(json.as_bytes()).unwrap(); let wallet = PresaleWallet::from(wallet); - let kp = wallet.decrypt("123").unwrap(); - assert_eq!(kp.address(), Address::from(wallet.address)); + assert!(wallet.decrypt("123").is_ok()); + assert!(wallet.decrypt("124").is_err()); } } From bbe5cd001a1663e544237b83c7528eb7be5e9b3b Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 21 Jun 2016 17:50:22 +0200 Subject: [PATCH 05/10] presale wallet cli for parity --- parity/cli.rs | 2 ++ parity/main.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/parity/cli.rs b/parity/cli.rs index 507137f8b..d8effec88 100644 --- a/parity/cli.rs +++ b/parity/cli.rs @@ -25,6 +25,7 @@ Usage: parity daemon [options] parity account (new | list ) [options] parity account import ... [options] + parity wallet import --password FILE [options] parity import [ ] [options] parity export [ ] [options] parity signer new-token [options] @@ -215,6 +216,7 @@ Miscellaneous Options: pub struct Args { pub cmd_daemon: bool, pub cmd_account: bool, + pub cmd_wallet: bool, pub cmd_new: bool, pub cmd_list: bool, pub cmd_export: bool, diff --git a/parity/main.rs b/parity/main.rs index a44cdf6d3..b610e3025 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -129,6 +129,11 @@ fn execute(conf: Configuration) { return; } + if conf.args.cmd_wallet { + execute_wallet_cli(conf); + return; + } + if conf.args.cmd_export { execute_export(conf); return; @@ -534,6 +539,30 @@ fn execute_account_cli(conf: Configuration) { } } +fn execute_wallet_cli(conf: Configuration) { + use ethcore::ethstore::{PresaleWallet, SecretStore, EthStore}; + use ethcore::ethstore::dir::DiskDirectory; + use ethcore::account_provider::AccountProvider; + + let wallet_path = conf.args.arg_path.first().unwrap(); + let filename = conf.args.flag_password.first().unwrap(); + let mut file = File::open(filename).unwrap_or_else(|_| die!("{} Unable to read password file.", filename)); + let mut file_content = String::new(); + file.read_to_string(&mut file_content).unwrap_or_else(|_| die!("{} Unable to read password file.", filename)); + + let dir = Box::new(DiskDirectory::create(conf.keys_path()).unwrap()); + let iterations = conf.keys_iterations(); + let store = AccountProvider::new(Box::new(EthStore::open_with_iterations(dir, iterations).unwrap())); + + // remove eof + let pass = &file_content[..file_content.len() - 1]; + let wallet = PresaleWallet::open(wallet_path).unwrap_or_else(|_| die!("Unable to open presale wallet.")); + let kp = wallet.decrypt(pass).unwrap_or_else(|_| die!("Invalid password")); + let address = store.insert_account(kp.secret().clone(), pass).unwrap(); + + println!("Imported account: {}", address); +} + fn wait_for_exit( panic_handler: Arc, _rpc_server: Option, From 9547324b464e5680afa7e88c4fc488bea2fa5fef Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 22 Jun 2016 17:02:40 +0200 Subject: [PATCH 06/10] ethstore cli loads passwords from files --- ethstore/README.md | 28 ++++++++++++++++++---------- ethstore/src/bin/ethstore.rs | 30 +++++++++++++++++++++++------- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/ethstore/README.md b/ethstore/README.md index 0cf50f454..0b85d99b4 100644 --- a/ethstore/README.md +++ b/ethstore/README.md @@ -50,11 +50,11 @@ Commands: *Encrypt secret with a password and save it in secret store.* - `` - ethereum secret, 32 bytes long -- `` - account password, any string +- `` - account password, file path - `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity ``` -ethstore insert 7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5 "this is sparta" +ethstore insert 7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5 password.txt ``` ``` @@ -77,12 +77,12 @@ ethstore insert `ethkey generate random -s` "this is sparta" *Change account password.* - `
` - ethereum address, 20 bytes long -- `` - old account password, any string -- `` - new account password, any string +- `` - old account password, file path +- `` - new account password, file path - `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity ``` -ethstore change-pwd a8fa5dd30a87bb9e3288d604eb74949c515ab66e "this is sparta" "hello world" +ethstore change-pwd a8fa5dd30a87bb9e3288d604eb74949c515ab66e old_pwd.txt new_pwd.txt ``` ``` @@ -114,6 +114,10 @@ ethstore list - `[--src DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: geth - `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity +``` +ethstore import +``` + ``` 0: e6a3d25a7cb7cd21cb720df5b5e8afd154af1bbb 1: 6edddfc6349aff20bc6467ccf276c5b52487f7a8 @@ -125,9 +129,13 @@ ethstore list *Import account from presale wallet.* - `` - presale wallet path -- `` - account password, any string +- `` - account password, file path - `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity +``` +ethstore import-wallet ethwallet.json password.txt +``` + ``` e6a3d25a7cb7cd21cb720df5b5e8afd154af1bbb ``` @@ -138,11 +146,11 @@ e6a3d25a7cb7cd21cb720df5b5e8afd154af1bbb *Remove account from secret store.* - `
` - ethereum address, 20 bytes long -- `` - account password, any string +- `` - account password, file path - `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity ``` -ethstore remove a8fa5dd30a87bb9e3288d604eb74949c515ab66e "hello world" +ethstore remove a8fa5dd30a87bb9e3288d604eb74949c515ab66e password.txt ``` ``` @@ -155,12 +163,12 @@ true *Sign message with account's secret.* - `
` - ethereum address, 20 bytes long -- `` - account password, any string +- `` - account password, file path - `` - message to sign, 32 bytes long - `[--dir DIR]` - secret store directory, It may be either parity, parity-test, geth, geth-test or a path. default: parity ``` -ethstore sign 24edfff680d536a5f6fe862d36df6f8f6f40f115 "this is sparta" 7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5 +ethstore sign 24edfff680d536a5f6fe862d36df6f8f6f40f115 password.txt 7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5 ``` ``` diff --git a/ethstore/src/bin/ethstore.rs b/ethstore/src/bin/ethstore.rs index 948d7a76e..5683a8116 100644 --- a/ethstore/src/bin/ethstore.rs +++ b/ethstore/src/bin/ethstore.rs @@ -18,7 +18,8 @@ extern crate rustc_serialize; extern crate docopt; extern crate ethstore; -use std::{env, process}; +use std::{env, process, fs}; +use std::io::Read; use std::ops::Deref; use std::str::FromStr; use docopt::Docopt; @@ -109,6 +110,15 @@ fn format_accounts(accounts: &[Address]) -> String { .join("\n") } +fn load_password(path: &str) -> Result { + let mut file = try!(fs::File::open(path)); + let mut password = String::new(); + try!(file.read_to_string(&mut password)); + // drop EOF + let _ = password.pop(); + Ok(password) +} + fn execute(command: I) -> Result where I: IntoIterator, S: AsRef { let args: Args = Docopt::new(USAGE) .and_then(|d| d.argv(command).decode()) @@ -118,11 +128,14 @@ fn execute(command: I) -> Result where I: IntoIterator(command: I) -> Result where I: IntoIterator Date: Wed, 22 Jun 2016 19:52:18 +0200 Subject: [PATCH 07/10] Removing signer connection limit (#1396) --- signer/src/ws_server/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/signer/src/ws_server/mod.rs b/signer/src/ws_server/mod.rs index beffbd2f7..b6b7c15f7 100644 --- a/signer/src/ws_server/mod.rs +++ b/signer/src/ws_server/mod.rs @@ -93,7 +93,6 @@ impl Server { let config = { let mut config = ws::Settings::default(); // It's also used for handling min-sysui requests (browser can make many of them in paralel) - config.max_connections = 15; config.method_strict = true; // Was shutting down server when suspending on linux: config.shutdown_on_interrupt = false; From e346cbc7f92db44917995f8cc6eca2c0ad5e1702 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 22 Jun 2016 21:32:17 +0200 Subject: [PATCH 08/10] Make --signer default. (#1392) --- parity/cli.rs | 4 ++-- parity/configuration.rs | 6 +++--- parity/main.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/parity/cli.rs b/parity/cli.rs index 144e2e51c..7192cc9ce 100644 --- a/parity/cli.rs +++ b/parity/cli.rs @@ -116,7 +116,7 @@ API and Console Options: --dapps-path PATH Specify directory where dapps should be installed. [default: $HOME/.parity/dapps] - --signer Enable Trusted Signer WebSocket endpoint used by + --signer-off Disable Trusted Signer WebSocket endpoint used by Signer UIs. --signer-port PORT Specify the port of Trusted Signer server [default: 8180]. @@ -274,7 +274,7 @@ pub struct Args { pub flag_dapps_user: Option, pub flag_dapps_pass: Option, pub flag_dapps_path: String, - pub flag_signer: bool, + pub flag_signer_off: bool, pub flag_signer_port: u16, pub flag_signer_path: String, pub flag_no_token: bool, diff --git a/parity/configuration.rs b/parity/configuration.rs index 94835a8a9..4196564b5 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -432,10 +432,10 @@ impl Configuration { } pub fn signer_port(&self) -> Option { - if self.args.flag_signer { - Some(self.args.flag_signer_port) - } else { + if self.args.flag_signer_off { None + } else { + Some(self.args.flag_signer_port) } } } diff --git a/parity/main.rs b/parity/main.rs index b610e3025..c6960993a 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -192,7 +192,7 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) let sync_config = conf.sync_config(&spec); // Create and display a new token for UIs. - if conf.args.flag_signer && !conf.args.flag_no_token { + if !conf.args.flag_signer_off && !conf.args.flag_no_token { new_token(conf.directories().signer).unwrap_or_else(|e| { die!("Error generating token: {:?}", e) }); From 9a1e1b7c890903d54a6e82664cc3cd215997dc18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 22 Jun 2016 21:32:26 +0200 Subject: [PATCH 09/10] Signer with Unlocked Account (#1398) --- ethcore/src/account_provider.rs | 7 ++++ parity/main.rs | 6 ++++ parity/rpc_apis.rs | 2 +- rpc/src/v1/impls/eth_signing.rs | 15 ++++++-- rpc/src/v1/tests/mocked/eth_signing.rs | 47 ++++++++++++++++++++++++-- 5 files changed, 72 insertions(+), 5 deletions(-) diff --git a/ethcore/src/account_provider.rs b/ethcore/src/account_provider.rs index 6744c6bd2..a71251727 100644 --- a/ethcore/src/account_provider.rs +++ b/ethcore/src/account_provider.rs @@ -205,6 +205,13 @@ impl AccountProvider { self.unlock_account(account, password, Unlock::Temp) } + /// Checks if given account is unlocked + pub fn is_unlocked(&self, account: A) -> bool where Address: From { + let account = Address::from(account).into(); + let unlocked = self.unlocked.read().unwrap(); + unlocked.get(&account).is_some() + } + /// Signs the message. Account must be unlocked. pub fn sign(&self, account: A, message: M) -> Result where Address: From, Message: From { let account = Address::from(account).into(); diff --git a/parity/main.rs b/parity/main.rs index c6960993a..17d941a5d 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -198,6 +198,12 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) }); } + // Display warning about using unlock with signer + if conf.args.flag_signer && conf.args.flag_unlock.is_some() { + warn!("Using Trusted Signer and --unlock is not recommended!"); + warn!("NOTE that Signer will not ask you to confirm transactions from unlocked account."); + } + // Secret Store let account_service = Arc::new(conf.account_service()); diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index 22520f266..c0daaa926 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -150,7 +150,7 @@ pub fn setup_rpc(server: T, deps: Arc, apis: ApiSet server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate()); if deps.signer_port.is_some() { - server.add_delegate(EthSigningQueueClient::new(&deps.signer_queue, &deps.client, &deps.miner).to_delegate()); + server.add_delegate(EthSigningQueueClient::new(&deps.signer_queue, &deps.client, &deps.miner, &deps.secret_store).to_delegate()); } else { server.add_delegate(EthSigningUnsafeClient::new(&deps.client, &deps.secret_store, &deps.miner).to_delegate()); } diff --git a/rpc/src/v1/impls/eth_signing.rs b/rpc/src/v1/impls/eth_signing.rs index c5103fd2d..700c679d6 100644 --- a/rpc/src/v1/impls/eth_signing.rs +++ b/rpc/src/v1/impls/eth_signing.rs @@ -43,15 +43,17 @@ fn fill_optional_fields(request: &mut TransactionRequest, client: &C, mine /// Implementation of functions that require signing when no trusted signer is used. pub struct EthSigningQueueClient where C: MiningBlockChainClient, M: MinerService { queue: Weak, + accounts: Weak, client: Weak, miner: Weak, } impl EthSigningQueueClient where C: MiningBlockChainClient, M: MinerService { /// Creates a new signing queue client given shared signing queue. - pub fn new(queue: &Arc, client: &Arc, miner: &Arc) -> Self { + pub fn new(queue: &Arc, client: &Arc, miner: &Arc, accounts: &Arc) -> Self { EthSigningQueueClient { queue: Arc::downgrade(queue), + accounts: Arc::downgrade(accounts), client: Arc::downgrade(client), miner: Arc::downgrade(miner), } @@ -71,9 +73,18 @@ impl EthSigning for EthSigningQueueClient fn send_transaction(&self, params: Params) -> Result { from_params::<(TransactionRequest, )>(params) .and_then(|(mut request, )| { - let queue = take_weak!(self.queue); + let accounts = take_weak!(self.accounts); let (client, miner) = (take_weak!(self.client), take_weak!(self.miner)); + if accounts.is_unlocked(request.from) { + let sender = request.from; + return match sign_and_dispatch(&*client, &*miner, request, &*accounts, sender) { + Ok(hash) => to_value(&hash), + _ => to_value(&H256::zero()), + } + } + + let queue = take_weak!(self.queue); fill_optional_fields(&mut request, &*client, &*miner); let id = queue.add_request(request); let result = id.wait_with_timeout(); diff --git a/rpc/src/v1/tests/mocked/eth_signing.rs b/rpc/src/v1/tests/mocked/eth_signing.rs index a2755ce18..5f3e75d35 100644 --- a/rpc/src/v1/tests/mocked/eth_signing.rs +++ b/rpc/src/v1/tests/mocked/eth_signing.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use std::str::FromStr; use std::sync::Arc; use jsonrpc_core::IoHandler; use v1::impls::EthSigningQueueClient; @@ -21,12 +22,16 @@ use v1::traits::EthSigning; use v1::helpers::{ConfirmationsQueue, SigningQueue}; use v1::tests::helpers::TestMinerService; use util::{Address, FixedHash}; +use util::numbers::{Uint, U256}; +use ethcore::account_provider::AccountProvider; use ethcore::client::TestBlockChainClient; +use ethcore::transaction::{Transaction, Action}; struct EthSigningTester { pub queue: Arc, pub client: Arc, pub miner: Arc, + pub accounts: Arc, pub io: IoHandler, } @@ -35,13 +40,15 @@ impl Default for EthSigningTester { let queue = Arc::new(ConfirmationsQueue::default()); let client = Arc::new(TestBlockChainClient::default()); let miner = Arc::new(TestMinerService::default()); + let accounts = Arc::new(AccountProvider::transient_provider()); let io = IoHandler::new(); - io.add_delegate(EthSigningQueueClient::new(&queue, &client, &miner).to_delegate()); + io.add_delegate(EthSigningQueueClient::new(&queue, &client, &miner, &accounts).to_delegate()); EthSigningTester { queue: queue, client: client, miner: miner, + accounts: accounts, io: io, } } @@ -78,5 +85,41 @@ fn should_add_transaction_to_queue() { // then assert_eq!(tester.io.handle_request(&request), Some(response.to_owned())); assert_eq!(tester.queue.requests().len(), 1); - +} + +#[test] +fn should_dispatch_transaction_if_account_is_unlocked() { + // given + let tester = eth_signing(); + let acc = tester.accounts.new_account("test").unwrap(); + tester.accounts.unlock_account_permanently(acc, "test".into()).unwrap(); + + let t = Transaction { + nonce: U256::zero(), + gas_price: U256::from(0x9184e72a000u64), + gas: U256::from(0x76c0), + action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + value: U256::from(0x9184e72au64), + data: vec![] + }; + let signature = tester.accounts.sign(acc, t.hash()).unwrap(); + let t = t.with_signature(signature); + + // when + let request = r#"{ + "jsonrpc": "2.0", + "method": "eth_sendTransaction", + "params": [{ + "from": ""#.to_owned() + format!("0x{:?}", acc).as_ref() + r#"", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a" + }], + "id": 1 + }"#; + let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#; + + // then + assert_eq!(tester.io.handle_request(&request), Some(response.to_owned())); } From 230c6c889ab34b7f874be852eb64e5055d65bda7 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 22 Jun 2016 21:33:10 +0200 Subject: [PATCH 10/10] Ensure judging the SF trigger by relative branch (#1399) Rather than just the canon chain. --- ethcore/src/client/client.rs | 6 +++--- ethcore/src/client/mod.rs | 16 +++++++++++++--- ethcore/src/miner/miner.rs | 2 +- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index fa063cd61..693296469 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -230,7 +230,7 @@ impl Client where V: Verifier { let last_hashes = self.build_last_hashes(header.parent_hash.clone()); let db = self.state_db.lock().unwrap().boxed_clone(); - let enact_result = enact_verified(&block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, self.dao_rescue_block_gas_limit(), &self.vm_factory); + let enact_result = enact_verified(&block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, self.dao_rescue_block_gas_limit(header.parent_hash.clone()), &self.vm_factory); if let Err(e) = enact_result { warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); return Err(()); @@ -486,7 +486,7 @@ impl BlockChainClient for Client where V: Verifier { last_hashes: last_hashes, gas_used: U256::zero(), gas_limit: U256::max_value(), - dao_rescue_block_gas_limit: self.dao_rescue_block_gas_limit(), + dao_rescue_block_gas_limit: self.dao_rescue_block_gas_limit(view.parent_hash()), }; // that's just a copy of the state. let mut state = self.state(); @@ -808,7 +808,7 @@ impl MiningBlockChainClient for Client where V: Verifier { self.state_db.lock().unwrap().boxed_clone(), &self.chain.block_header(&h).expect("h is best block hash: so it's header must exist: qed"), self.build_last_hashes(h.clone()), - self.dao_rescue_block_gas_limit(), + self.dao_rescue_block_gas_limit(h.clone()), author, gas_floor_target, extra_data, diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index c1bc3203c..589b65e16 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -227,9 +227,19 @@ pub trait BlockChainClient : Sync + Send { /// Get `Some` gas limit of block 1_760_000, or `None` if chain is not yet that long. - fn dao_rescue_block_gas_limit(&self) -> Option { - self.block_header(BlockID::Number(1_760_000)) - .map(|header| HeaderView::new(&header).gas_limit()) + fn dao_rescue_block_gas_limit(&self, chain_hash: H256) -> Option { + if let Some(mut header) = self.block_header(BlockID::Hash(chain_hash)) { + if HeaderView::new(&header).number() < 1_760_000 { + None + } else { + while HeaderView::new(&header).number() != 1_760_000 { + header = self.block_header(BlockID::Hash(HeaderView::new(&header).parent_hash())).expect("chain is complete; parent of chain entry must be in chain; qed"); + } + Some(HeaderView::new(&header).gas_limit()) + } + } else { + None + } } } diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 41e4b4810..290e94059 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -274,7 +274,7 @@ impl MinerService for Miner { last_hashes: last_hashes, gas_used: U256::zero(), gas_limit: U256::max_value(), - dao_rescue_block_gas_limit: chain.dao_rescue_block_gas_limit(), + dao_rescue_block_gas_limit: chain.dao_rescue_block_gas_limit(header.parent_hash().clone()), }; // that's just a copy of the state. let mut state = block.state().clone();