diff --git a/ethtools/Cargo.toml b/ethtools/Cargo.toml new file mode 100644 index 000000000..5529c1d33 --- /dev/null +++ b/ethtools/Cargo.toml @@ -0,0 +1,10 @@ +[package] +description = "Ethcore Ethereum tools" +homepage = "http://ethcore.io" +license = "GPL-3.0" +name = "ethtools" +version = "0.9.99" +authors = ["Ethcore "] + +[dependencies] +ethcore-util = { path = "../util" } diff --git a/ethtools/README.md b/ethtools/README.md new file mode 100644 index 000000000..f3e852ff5 --- /dev/null +++ b/ethtools/README.md @@ -0,0 +1 @@ +# ethtools diff --git a/ethtools/res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9 b/ethtools/res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9 new file mode 100644 index 000000000..a62d3056c --- /dev/null +++ b/ethtools/res/geth_keystore/UTC--2016-02-17T09-20-45.721400158Z--3f49624084b67849c7b4e805c5988c21a430f9d9 @@ -0,0 +1 @@ +{"address":"3f49624084b67849c7b4e805c5988c21a430f9d9","Crypto":{"cipher":"aes-128-ctr","ciphertext":"9f27e3dd4fc73e7103ed61e5493662189a3eb52223ae49e3d1deacc04c889eae","cipherparams":{"iv":"457494bf05f2618c397dc74dbb5181c0"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"db14edb18c41ee7f5ec4397df89c3a2ae4d0af60884c52bb54ce490574f8df33"},"mac":"572d24532438d31fdf513c744a3ff26c933ffda5744ee42bc71661cbe3f2112e"},"id":"62a0ad73-556d-496a-8e1c-0783d30d3ace","version":3} \ No newline at end of file diff --git a/ethtools/res/geth_keystore/UTC--2016-02-20T09-33-03.984382741Z--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf b/ethtools/res/geth_keystore/UTC--2016-02-20T09-33-03.984382741Z--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf new file mode 100644 index 000000000..b6caa1c47 --- /dev/null +++ b/ethtools/res/geth_keystore/UTC--2016-02-20T09-33-03.984382741Z--5ba4dcf897e97c2bdf8315b9ef26c13c085988cf @@ -0,0 +1 @@ +{"address":"5ba4dcf897e97c2bdf8315b9ef26c13c085988cf","Crypto":{"cipher":"aes-128-ctr","ciphertext":"d4a08ec930163778273920f6ad1d49b71836337be6fd9863993ac700a612fddd","cipherparams":{"iv":"89ce5ec129fc27cd5bcbeb8c92bdad50"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"612ab108dc37e69ee8af37a7b24bf7f2234086d7bbf945bacdeccce331f7f84a"},"mac":"4152caa7444e06784223d735cea80cd2690b4c587ad8db3d5529442227b25695"},"id":"35086353-fb12-4029-b56b-033cd61ce35b","version":3} \ No newline at end of file diff --git a/ethtools/src/geth_keys.rs b/ethtools/src/geth_keys.rs new file mode 100644 index 000000000..58dc2a0b1 --- /dev/null +++ b/ethtools/src/geth_keys.rs @@ -0,0 +1,57 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Geth keys import/export tool + +use util::hash::*; +use std::path::Path; +use std::result::*; +use std::fs; +use std::str::FromStr; + +/// Enumerates all geth keys in the directory and returns collection of tuples `(accountId, filename)` +pub fn enumerate_geth_keys(path: &Path) -> Result, ::std::io::Error> { + let mut entries = Vec::new(); + for entry in try!(fs::read_dir(path)) { + let entry = try!(entry); + if !try!(fs::metadata(entry.path())).is_dir() { + match entry.file_name().to_str() { + Some(name) => { + let parts: Vec<&str> = name.split("--").collect(); + if parts.len() != 3 { continue; } + match Address::from_str(parts[2]) { + Ok(account_id) => { entries.push((account_id, name.to_owned())); } + Err(e) => { panic!("error: {:?}", e); } + } + }, + None => { continue; } + }; + } + } + Ok(entries) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::path::Path; + + #[test] + fn can_enumerate() { + let keys = enumerate_geth_keys(Path::new("res/geth_keystore")).unwrap(); + assert_eq!(2, keys.len()); + } +} diff --git a/ethtools/src/lib.rs b/ethtools/src/lib.rs new file mode 100644 index 000000000..41dd69cf9 --- /dev/null +++ b/ethtools/src/lib.rs @@ -0,0 +1,21 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Ethereum Tools Library + +extern crate ethcore_util as util; + +pub mod geth_keys; diff --git a/util/src/hash.rs b/util/src/hash.rs index 71c690ef6..a6e8f7950 100644 --- a/util/src/hash.rs +++ b/util/src/hash.rs @@ -235,7 +235,7 @@ macro_rules! impl_hash { } impl serde::Serialize for $from { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer { let mut hex = "0x".to_owned(); hex.push_str(self.to_hex().as_ref()); @@ -250,7 +250,7 @@ macro_rules! impl_hash { impl serde::de::Visitor for HashVisitor { type Value = $from; - + fn visit_str(&mut self, value: &str) -> Result where E: serde::Error { // 0x + len if value.len() != 2 + $size * 2 { @@ -719,4 +719,3 @@ mod tests { assert_eq!(r, u); } } - diff --git a/util/src/keys/directory.rs b/util/src/keys/directory.rs index bc875db3f..298f67b99 100644 --- a/util/src/keys/directory.rs +++ b/util/src/keys/directory.rs @@ -333,7 +333,9 @@ pub struct KeyFileContent { /// Holds cypher and decrypt function settings. pub crypto: KeyFileCrypto, /// The identifier. - pub id: Uuid + pub id: Uuid, + /// Account (if present) + pub account: Option
, } #[derive(Debug)] @@ -374,7 +376,8 @@ impl KeyFileContent { KeyFileContent { id: new_uuid(), version: KeyFileVersion::V3(3), - crypto: crypto + crypto: crypto, + account: None } } @@ -407,6 +410,9 @@ impl KeyFileContent { Ok(id) => id }; + let account = as_object.get("account").and_then(|json| json.as_string()).and_then( + |account_text| match Address::from_str(account_text) { Ok(account) => Some(account), Err(_) => None }); + let crypto = match as_object.get("crypto") { None => { return Err(KeyFileParseError::NoCryptoSection); } Some(crypto_json) => match KeyFileCrypto::from_json(crypto_json) { @@ -418,7 +424,8 @@ impl KeyFileContent { Ok(KeyFileContent { version: version, id: id.clone(), - crypto: crypto + crypto: crypto, + account: account }) } diff --git a/util/src/keys/store.rs b/util/src/keys/store.rs index ae44d567a..100f3d30c 100644 --- a/util/src/keys/store.rs +++ b/util/src/keys/store.rs @@ -63,12 +63,22 @@ impl SecretStore { /// new instance of Secret Store pub fn new() -> SecretStore { let mut path = ::std::env::home_dir().expect("Failed to get home dir"); - path.push(".keys"); + path.push("keystore"); SecretStore { directory: KeyDirectory::new(&path) } } + pub fn accounts(&self) -> Result, ::std::io::Error> { + let accounts = try!(self.directory.list()).iter().map(|key_id| self.directory.get(key_id)) + .filter(|key| key.is_some()) + .map(|key| { let some_key = key.unwrap(); (some_key.account, some_key.id) }) + .filter(|&(ref account, _)| account.is_some()) + .map(|(account, id)| (account.unwrap(), id)) + .collect::>(); + Ok(accounts) + } + #[cfg(test)] fn new_test(path: &::devtools::RandomTempPath) -> SecretStore { SecretStore {