Reformat the source code

This commit is contained in:
Artem Vorotnikov 2020-08-05 07:08:03 +03:00
parent 253ff3f37b
commit 610d9baba4
No known key found for this signature in database
GPG Key ID: E0148C3F2FBB7A20
742 changed files with 175791 additions and 141379 deletions

View File

@ -26,11 +26,13 @@ extern crate threadpool;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
use std::num::ParseIntError; use std::{env, fmt, io, num::ParseIntError, process, sync};
use std::{env, fmt, process, io, sync};
use docopt::Docopt; use docopt::Docopt;
use ethkey::{KeyPair, Random, Brain, BrainPrefix, Prefix, Error as EthkeyError, Generator, sign, verify_public, verify_address, brain_recover}; use ethkey::{
brain_recover, sign, verify_address, verify_public, Brain, BrainPrefix, Error as EthkeyError,
Generator, KeyPair, Prefix, Random,
};
use rustc_hex::{FromHex, FromHexError}; use rustc_hex::{FromHex, FromHexError};
const USAGE: &'static str = r#" const USAGE: &'static str = r#"
@ -179,7 +181,7 @@ fn display(result: (KeyPair, Option<String>), mode: DisplayMode) -> String {
match mode { match mode {
DisplayMode::KeyPair => match result.1 { DisplayMode::KeyPair => match result.1 {
Some(extra_data) => format!("{}\n{}", extra_data, keypair), Some(extra_data) => format!("{}\n{}", extra_data, keypair),
None => format!("{}", keypair) None => format!("{}", keypair),
}, },
DisplayMode::Secret => format!("{:x}", keypair.secret()), DisplayMode::Secret => format!("{:x}", keypair.secret()),
DisplayMode::Public => format!("{:x}", keypair.public()), DisplayMode::Public => format!("{:x}", keypair.public()),
@ -187,9 +189,12 @@ fn display(result: (KeyPair, Option<String>), mode: DisplayMode) -> String {
} }
} }
fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item=S>, S: AsRef<str> { fn execute<S, I>(command: I) -> Result<String, Error>
let args: Args = Docopt::new(USAGE) where
.and_then(|d| d.argv(command).deserialize())?; I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let args: Args = Docopt::new(USAGE).and_then(|d| d.argv(command).deserialize())?;
return if args.cmd_info { return if args.cmd_info {
let display_mode = DisplayMode::new(&args); let display_mode = DisplayMode::new(&args);
@ -197,10 +202,15 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
let result = if args.flag_brain { let result = if args.flag_brain {
let phrase = args.arg_secret_or_phrase; let phrase = args.arg_secret_or_phrase;
let phrase_info = validate_phrase(&phrase); let phrase_info = validate_phrase(&phrase);
let keypair = Brain::new(phrase).generate().expect("Brain wallet generator is infallible; qed"); let keypair = Brain::new(phrase)
.generate()
.expect("Brain wallet generator is infallible; qed");
(keypair, Some(phrase_info)) (keypair, Some(phrase_info))
} else { } else {
let secret = args.arg_secret_or_phrase.parse().map_err(|_| EthkeyError::InvalidSecret)?; let secret = args
.arg_secret_or_phrase
.parse()
.map_err(|_| EthkeyError::InvalidSecret)?;
(KeyPair::from_secret(secret)?, None) (KeyPair::from_secret(secret)?, None)
}; };
Ok(display(result, display_mode)) Ok(display(result, display_mode))
@ -237,33 +247,55 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
} }
})? })?
} else { } else {
return Ok(format!("{}", USAGE)) return Ok(format!("{}", USAGE));
}; };
Ok(display(result, display_mode)) Ok(display(result, display_mode))
} else if args.cmd_sign { } else if args.cmd_sign {
let secret = args.arg_secret.parse().map_err(|_| EthkeyError::InvalidSecret)?; let secret = args
let message = args.arg_message.parse().map_err(|_| EthkeyError::InvalidMessage)?; .arg_secret
.parse()
.map_err(|_| EthkeyError::InvalidSecret)?;
let message = args
.arg_message
.parse()
.map_err(|_| EthkeyError::InvalidMessage)?;
let signature = sign(&secret, &message)?; let signature = sign(&secret, &message)?;
Ok(format!("{}", signature)) Ok(format!("{}", signature))
} else if args.cmd_verify { } else if args.cmd_verify {
let signature = args.arg_signature.parse().map_err(|_| EthkeyError::InvalidSignature)?; let signature = args
let message = args.arg_message.parse().map_err(|_| EthkeyError::InvalidMessage)?; .arg_signature
.parse()
.map_err(|_| EthkeyError::InvalidSignature)?;
let message = args
.arg_message
.parse()
.map_err(|_| EthkeyError::InvalidMessage)?;
let ok = if args.cmd_public { let ok = if args.cmd_public {
let public = args.arg_public.parse().map_err(|_| EthkeyError::InvalidPublic)?; let public = args
.arg_public
.parse()
.map_err(|_| EthkeyError::InvalidPublic)?;
verify_public(&public, &signature, &message)? verify_public(&public, &signature, &message)?
} else if args.cmd_address { } else if args.cmd_address {
let address = args.arg_address.parse().map_err(|_| EthkeyError::InvalidAddress)?; let address = args
.arg_address
.parse()
.map_err(|_| EthkeyError::InvalidAddress)?;
verify_address(&address, &signature, &message)? verify_address(&address, &signature, &message)?
} else { } else {
return Ok(format!("{}", USAGE)) return Ok(format!("{}", USAGE));
}; };
Ok(format!("{}", ok)) Ok(format!("{}", ok))
} else if args.cmd_recover { } else if args.cmd_recover {
let display_mode = DisplayMode::new(&args); let display_mode = DisplayMode::new(&args);
let known_phrase = args.arg_known_phrase; let known_phrase = args.arg_known_phrase;
let address = args.arg_address.parse().map_err(|_| EthkeyError::InvalidAddress)?; let address = args
.arg_address
.parse()
.map_err(|_| EthkeyError::InvalidAddress)?;
let (phrase, keypair) = in_threads(move || { let (phrase, keypair) = in_threads(move || {
let mut it = brain_recover::PhrasesIterator::from_known_phrase(&known_phrase, BRAIN_WORDS); let mut it =
brain_recover::PhrasesIterator::from_known_phrase(&known_phrase, BRAIN_WORDS);
move || { move || {
let mut i = 0; let mut i = 0;
while let Some(phrase) = it.next() { while let Some(phrase) = it.next() {
@ -271,11 +303,11 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
let keypair = Brain::new(phrase.clone()).generate().unwrap(); let keypair = Brain::new(phrase.clone()).generate().unwrap();
if keypair.address() == address { if keypair.address() == address {
return Ok(Some((phrase, keypair))) return Ok(Some((phrase, keypair)));
} }
if i >= 1024 { if i >= 1024 {
return Ok(None) return Ok(None);
} }
} }
@ -285,7 +317,7 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
Ok(display((keypair, Some(phrase)), display_mode)) Ok(display((keypair, Some(phrase)), display_mode))
} else { } else {
Ok(format!("{}", USAGE)) Ok(format!("{}", USAGE))
} };
} }
const BRAIN_WORDS: usize = 12; const BRAIN_WORDS: usize = 12;
@ -293,11 +325,12 @@ const BRAIN_WORDS: usize = 12;
fn validate_phrase(phrase: &str) -> String { fn validate_phrase(phrase: &str) -> String {
match Brain::validate_phrase(phrase, BRAIN_WORDS) { match Brain::validate_phrase(phrase, BRAIN_WORDS) {
Ok(()) => format!("The recovery phrase looks correct.\n"), Ok(()) => format!("The recovery phrase looks correct.\n"),
Err(err) => format!("The recover phrase was not generated by Parity: {}", err) Err(err) => format!("The recover phrase was not generated by Parity: {}", err),
} }
} }
fn in_threads<F, X, O>(prepare: F) -> Result<O, EthkeyError> where fn in_threads<F, X, O>(prepare: F) -> Result<O, EthkeyError>
where
O: Send + 'static, O: Send + 'static,
X: Send + 'static, X: Send + 'static,
F: Fn() -> X, F: Fn() -> X,
@ -344,7 +377,11 @@ mod tests {
#[test] #[test]
fn info() { fn info() {
let command = vec!["ethkey", "info", "17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55"] let command = vec![
"ethkey",
"info",
"17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55",
]
.into_iter() .into_iter()
.map(Into::into) .map(Into::into)
.collect::<Vec<String>>(); .collect::<Vec<String>>();
@ -379,7 +416,8 @@ address: 006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
.map(Into::into) .map(Into::into)
.collect::<Vec<String>>(); .collect::<Vec<String>>();
let expected = "aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2".to_owned(); let expected =
"aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2".to_owned();
assert_eq!(execute(command).unwrap(), expected); assert_eq!(execute(command).unwrap(), expected);
} }
@ -407,7 +445,12 @@ address: 006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
#[test] #[test]
fn sign() { fn sign() {
let command = vec!["ethkey", "sign", "17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"] let command = vec![
"ethkey",
"sign",
"17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55",
"bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987",
]
.into_iter() .into_iter()
.map(Into::into) .map(Into::into)
.collect::<Vec<String>>(); .collect::<Vec<String>>();

View File

@ -14,8 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use super::{Generator, KeyPair, Secret};
use keccak::Keccak256; use keccak::Keccak256;
use super::{KeyPair, Generator, Secret};
use parity_wordlist; use parity_wordlist;
/// Simple brainwallet. /// Simple brainwallet.
@ -45,15 +45,15 @@ impl Generator for Brain {
match i > 16384 { match i > 16384 {
false => i += 1, false => i += 1,
true => { true => {
if let Ok(pair) = Secret::from_unsafe_slice(&secret) if let Ok(pair) =
.and_then(KeyPair::from_secret) Secret::from_unsafe_slice(&secret).and_then(KeyPair::from_secret)
{ {
if pair.address()[0] == 0 { if pair.address()[0] == 0 {
trace!("Testing: {}, got: {:?}", self.0, pair.address()); trace!("Testing: {}, got: {:?}", self.0, pair.address());
return Ok(pair) return Ok(pair);
}
} }
} }
},
} }
} }
} }
@ -61,7 +61,8 @@ impl Generator for Brain {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use {Brain, Generator}; use Brain;
use Generator;
#[test] #[test]
fn test_brain() { fn test_brain() {

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use super::{Generator, KeyPair, Error, Brain}; use super::{Brain, Error, Generator, KeyPair};
use parity_wordlist as wordlist; use parity_wordlist as wordlist;
/// Tries to find brain-seed keypair with address starting with given prefix. /// Tries to find brain-seed keypair with address starting with given prefix.
@ -49,7 +49,7 @@ impl Generator for BrainPrefix {
let keypair = Brain::new(phrase.clone()).generate().unwrap(); let keypair = Brain::new(phrase.clone()).generate().unwrap();
if keypair.address().starts_with(&self.prefix) { if keypair.address().starts_with(&self.prefix) {
self.last_phrase = phrase; self.last_phrase = phrase;
return Ok(keypair) return Ok(keypair);
} }
} }
@ -59,12 +59,15 @@ impl Generator for BrainPrefix {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use {Generator, BrainPrefix}; use BrainPrefix;
use Generator;
#[test] #[test]
fn prefix_generator() { fn prefix_generator() {
let prefix = vec![0x00u8]; let prefix = vec![0x00u8];
let keypair = BrainPrefix::new(prefix.clone(), usize::max_value(), 12).generate().unwrap(); let keypair = BrainPrefix::new(prefix.clone(), usize::max_value(), 12)
.generate()
.unwrap();
assert!(keypair.address().starts_with(&prefix)); assert!(keypair.address().starts_with(&prefix));
} }
} }

View File

@ -32,7 +32,9 @@ pub fn brain_recover(
) -> Option<String> { ) -> Option<String> {
let it = PhrasesIterator::from_known_phrase(known_phrase, expected_words); let it = PhrasesIterator::from_known_phrase(known_phrase, expected_words);
for phrase in it { for phrase in it {
let keypair = Brain::new(phrase.clone()).generate().expect("Brain wallets are infallible; qed"); let keypair = Brain::new(phrase.clone())
.generate()
.expect("Brain wallets are infallible; qed");
trace!("Testing: {}, got: {:?}", phrase, keypair.address()); trace!("Testing: {}, got: {:?}", phrase, keypair.address());
if &keypair.address() == address { if &keypair.address() == address {
return Some(phrase); return Some(phrase);
@ -43,14 +45,14 @@ pub fn brain_recover(
} }
fn generate_substitutions(word: &str) -> Vec<&'static str> { fn generate_substitutions(word: &str) -> Vec<&'static str> {
let mut words = parity_wordlist::WORDS.iter().cloned() let mut words = parity_wordlist::WORDS
.iter()
.cloned()
.map(|w| (edit_distance(w, word), w)) .map(|w| (edit_distance(w, word), w))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
words.sort_by(|a, b| a.0.cmp(&b.0)); words.sort_by(|a, b| a.0.cmp(&b.0));
words.into_iter() words.into_iter().map(|pair| pair.1).collect()
.map(|pair| pair.1)
.collect()
} }
/// Iterator over possible /// Iterator over possible
@ -63,15 +65,22 @@ pub struct PhrasesIterator {
impl PhrasesIterator { impl PhrasesIterator {
pub fn from_known_phrase(known_phrase: &str, expected_words: usize) -> Self { pub fn from_known_phrase(known_phrase: &str, expected_words: usize) -> Self {
let known_words = parity_wordlist::WORDS.iter().cloned().collect::<HashSet<_>>(); let known_words = parity_wordlist::WORDS
let mut words = known_phrase.split(' ') .iter()
.cloned()
.collect::<HashSet<_>>();
let mut words = known_phrase
.split(' ')
.map(|word| match known_words.get(word) { .map(|word| match known_words.get(word) {
None => { None => {
info!("Invalid word '{}', looking for potential substitutions.", word); info!(
"Invalid word '{}', looking for potential substitutions.",
word
);
let substitutions = generate_substitutions(word); let substitutions = generate_substitutions(word);
info!("Closest words: {:?}", &substitutions[..10]); info!("Closest words: {:?}", &substitutions[..10]);
substitutions substitutions
}, }
Some(word) => vec![*word], Some(word) => vec![*word],
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -151,11 +160,8 @@ mod tests {
#[test] #[test]
fn should_generate_possible_combinations() { fn should_generate_possible_combinations() {
let mut it = PhrasesIterator::new(vec![ let mut it =
vec!["1", "2", "3"], PhrasesIterator::new(vec![vec!["1", "2", "3"], vec!["test"], vec!["a", "b", "c"]]);
vec!["test"],
vec!["a", "b", "c"],
]);
assert_eq!(it.combinations(), 9); assert_eq!(it.combinations(), 9);
assert_eq!(it.next(), Some("1 test a".to_owned())); assert_eq!(it.next(), Some("1 test a".to_owned()));
@ -169,5 +175,4 @@ mod tests {
assert_eq!(it.next(), Some("3 test c".to_owned())); assert_eq!(it.next(), Some("3 test c".to_owned()));
assert_eq!(it.next(), None); assert_eq!(it.next(), None);
} }
} }

View File

@ -14,9 +14,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use parity_crypto::error::SymmError;
use secp256k1; use secp256k1;
use std::io; use std::io;
use parity_crypto::error::SymmError;
quick_error! { quick_error! {
#[derive(Debug)] #[derive(Debug)]
@ -43,9 +43,11 @@ quick_error! {
/// ECDH functions /// ECDH functions
pub mod ecdh { pub mod ecdh {
use secp256k1::{self, ecdh, key};
use super::Error; use super::Error;
use {Secret, Public, SECP256K1}; use secp256k1::{self, ecdh, key};
use Public;
use Secret;
use SECP256K1;
/// Agree on a shared secret /// Agree on a shared secret
pub fn agree(secret: &Secret, public: &Public) -> Result<Secret, Error> { pub fn agree(secret: &Secret, public: &Public) -> Result<Secret, Error> {
@ -67,10 +69,13 @@ pub mod ecdh {
/// ECIES function /// ECIES function
pub mod ecies { pub mod ecies {
use parity_crypto::{aes, digest, hmac, is_equal};
use ethereum_types::H128;
use super::{ecdh, Error}; use super::{ecdh, Error};
use {Random, Generator, Public, Secret}; use ethereum_types::H128;
use parity_crypto::{aes, digest, hmac, is_equal};
use Generator;
use Public;
use Random;
use Secret;
/// Encrypt a message with a public key, writing an HMAC covering both /// Encrypt a message with a public key, writing an HMAC covering both
/// the plaintext and authenticated data. /// the plaintext and authenticated data.
@ -126,10 +131,10 @@ pub mod ecies {
let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32])); let mkey = hmac::SigKey::sha256(&digest::sha256(&key[16..32]));
let clen = encrypted.len() - meta_len; let clen = encrypted.len() - meta_len;
let cipher_with_iv = &e[64..(64+16+clen)]; let cipher_with_iv = &e[64..(64 + 16 + clen)];
let cipher_iv = &cipher_with_iv[0..16]; let cipher_iv = &cipher_with_iv[0..16];
let cipher_no_iv = &cipher_with_iv[16..]; let cipher_no_iv = &cipher_with_iv[16..];
let msg_mac = &e[(64+16+clen)..]; let msg_mac = &e[(64 + 16 + clen)..];
// Verify tag // Verify tag
let mut hmac = hmac::Signer::with(&mkey); let mut hmac = hmac::Signer::with(&mkey);
@ -154,7 +159,12 @@ pub mod ecies {
let mut written = 0usize; let mut written = 0usize;
while written < dest.len() { while written < dest.len() {
let mut hasher = digest::Hasher::sha256(); let mut hasher = digest::Hasher::sha256();
let ctrs = [(ctr >> 24) as u8, (ctr >> 16) as u8, (ctr >> 8) as u8, ctr as u8]; let ctrs = [
(ctr >> 24) as u8,
(ctr >> 16) as u8,
(ctr >> 8) as u8,
ctr as u8,
];
hasher.update(&ctrs); hasher.update(&ctrs);
hasher.update(secret); hasher.update(secret);
hasher.update(s1); hasher.update(s1);
@ -169,7 +179,8 @@ pub mod ecies {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::ecies; use super::ecies;
use {Random, Generator}; use Generator;
use Random;
#[test] #[test]
fn ecies_shared() { fn ecies_shared() {

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::{fmt, error}; use std::{error, fmt};
#[derive(Debug)] #[derive(Debug)]
/// Crypto error /// Crypto error

View File

@ -16,10 +16,10 @@
//! Extended keys //! Extended keys
pub use self::derivation::Error as DerivationError;
use ethereum_types::H256;
use secret::Secret; use secret::Secret;
use Public; use Public;
use ethereum_types::H256;
pub use self::derivation::Error as DerivationError;
/// Represents label that can be stored as a part of key derivation /// Represents label that can be stored as a part of key derivation
pub trait Label { pub trait Label {
@ -32,7 +32,9 @@ pub trait Label {
} }
impl Label for u32 { impl Label for u32 {
fn len() -> usize { 4 } fn len() -> usize {
4
}
fn store(&self, target: &mut [u8]) { fn store(&self, target: &mut [u8]) {
let bytes = self.to_be_bytes(); let bytes = self.to_be_bytes();
@ -52,15 +54,16 @@ impl From<u32> for Derivation<u32> {
fn from(index: u32) -> Self { fn from(index: u32) -> Self {
if index < (2 << 30) { if index < (2 << 30) {
Derivation::Soft(index) Derivation::Soft(index)
} } else {
else {
Derivation::Hard(index) Derivation::Hard(index)
} }
} }
} }
impl Label for H256 { impl Label for H256 {
fn len() -> usize { 32 } fn len() -> usize {
32
}
fn store(&self, target: &mut [u8]) { fn store(&self, target: &mut [u8]) {
self.copy_to(&mut target[0..32]); self.copy_to(&mut target[0..32]);
@ -95,8 +98,12 @@ impl ExtendedSecret {
} }
/// Derive new private key /// Derive new private key
pub fn derive<T>(&self, index: Derivation<T>) -> ExtendedSecret where T: Label { pub fn derive<T>(&self, index: Derivation<T>) -> ExtendedSecret
let (derived_key, next_chain_code) = derivation::private(*self.secret, self.chain_code, index); where
T: Label,
{
let (derived_key, next_chain_code) =
derivation::private(*self.secret, self.chain_code, index);
let derived_secret = Secret::from(derived_key.0); let derived_secret = Secret::from(derived_key.0);
@ -118,23 +125,28 @@ pub struct ExtendedPublic {
impl ExtendedPublic { impl ExtendedPublic {
/// New extended public key from known parent and chain code /// New extended public key from known parent and chain code
pub fn new(public: Public, chain_code: H256) -> Self { pub fn new(public: Public, chain_code: H256) -> Self {
ExtendedPublic { public: public, chain_code: chain_code } ExtendedPublic {
public: public,
chain_code: chain_code,
}
} }
/// Create new extended public key from known secret /// Create new extended public key from known secret
pub fn from_secret(secret: &ExtendedSecret) -> Result<Self, DerivationError> { pub fn from_secret(secret: &ExtendedSecret) -> Result<Self, DerivationError> {
Ok( Ok(ExtendedPublic::new(
ExtendedPublic::new(
derivation::point(**secret.as_raw())?, derivation::point(**secret.as_raw())?,
secret.chain_code.clone(), secret.chain_code.clone(),
) ))
)
} }
/// Derive new public key /// Derive new public key
/// Operation is defined only for index belongs [0..2^31) /// Operation is defined only for index belongs [0..2^31)
pub fn derive<T>(&self, index: Derivation<T>) -> Result<Self, DerivationError> where T: Label { pub fn derive<T>(&self, index: Derivation<T>) -> Result<Self, DerivationError>
let (derived_key, next_chain_code) = derivation::public(self.public, self.chain_code, index)?; where
T: Label,
{
let (derived_key, next_chain_code) =
derivation::public(self.public, self.chain_code, index)?;
Ok(ExtendedPublic::new(derived_key, next_chain_code)) Ok(ExtendedPublic::new(derived_key, next_chain_code))
} }
@ -192,7 +204,10 @@ impl ExtendedKeyPair {
&self.public &self.public
} }
pub fn derive<T>(&self, index: Derivation<T>) -> Result<Self, DerivationError> where T: Label { pub fn derive<T>(&self, index: Derivation<T>) -> Result<Self, DerivationError>
where
T: Label,
{
let derived = self.secret.derive(index); let derived = self.secret.derive(index);
Ok(ExtendedKeyPair { Ok(ExtendedKeyPair {
@ -206,13 +221,13 @@ impl ExtendedKeyPair {
// Work is based on BIP0032 // Work is based on BIP0032
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
mod derivation { mod derivation {
use parity_crypto::hmac; use super::{Derivation, Label};
use ethereum_types::{U256, U512, H512, H256}; use ethereum_types::{H256, H512, U256, U512};
use secp256k1::key::{SecretKey, PublicKey};
use SECP256K1;
use keccak; use keccak;
use math::curve_order; use math::curve_order;
use super::{Label, Derivation}; use parity_crypto::hmac;
use secp256k1::key::{PublicKey, SecretKey};
use SECP256K1;
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
@ -228,7 +243,10 @@ mod derivation {
// //
// Can panic if passed `private_key` is not a valid secp256k1 private key // Can panic if passed `private_key` is not a valid secp256k1 private key
// (outside of (0..curve_order()]) field // (outside of (0..curve_order()]) field
pub fn private<T>(private_key: H256, chain_code: H256, index: Derivation<T>) -> (H256, H256) where T: Label { pub fn private<T>(private_key: H256, chain_code: H256, index: Derivation<T>) -> (H256, H256)
where
T: Label,
{
match index { match index {
Derivation::Soft(index) => private_soft(private_key, chain_code, index), Derivation::Soft(index) => private_soft(private_key, chain_code, index),
Derivation::Hard(index) => private_hard(private_key, chain_code, index), Derivation::Hard(index) => private_hard(private_key, chain_code, index),
@ -253,7 +271,10 @@ mod derivation {
// Can panic if passed `private_key` is not a valid secp256k1 private key // Can panic if passed `private_key` is not a valid secp256k1 private key
// (outside of (0..curve_order()]) field // (outside of (0..curve_order()]) field
fn private_soft<T>(private_key: H256, chain_code: H256, index: T) -> (H256, H256) where T: Label { fn private_soft<T>(private_key: H256, chain_code: H256, index: T) -> (H256, H256)
where
T: Label,
{
let mut data = vec![0u8; 33 + T::len()]; let mut data = vec![0u8; 33 + T::len()];
let sec_private = SecretKey::from_slice(&SECP256K1, &*private_key) let sec_private = SecretKey::from_slice(&SECP256K1, &*private_key)
@ -273,7 +294,10 @@ mod derivation {
// Deterministic derivation of the key using secp256k1 elliptic curve // Deterministic derivation of the key using secp256k1 elliptic curve
// This is hardened derivation and does not allow to associate // This is hardened derivation and does not allow to associate
// corresponding public keys of the original and derived private keys // corresponding public keys of the original and derived private keys
fn private_hard<T>(private_key: H256, chain_code: H256, index: T) -> (H256, H256) where T: Label { fn private_hard<T>(private_key: H256, chain_code: H256, index: T) -> (H256, H256)
where
T: Label,
{
let mut data: Vec<u8> = vec![0u8; 33 + T::len()]; let mut data: Vec<u8> = vec![0u8; 33 + T::len()];
let private: U256 = private_key.into(); let private: U256 = private_key.into();
@ -297,16 +321,26 @@ mod derivation {
md.into() md.into()
} }
pub fn public<T>(public_key: H512, chain_code: H256, derivation: Derivation<T>) -> Result<(H512, H256), Error> where T: Label { pub fn public<T>(
public_key: H512,
chain_code: H256,
derivation: Derivation<T>,
) -> Result<(H512, H256), Error>
where
T: Label,
{
let index = match derivation { let index = match derivation {
Derivation::Soft(index) => index, Derivation::Soft(index) => index,
Derivation::Hard(_) => { return Err(Error::InvalidHardenedUse); } Derivation::Hard(_) => {
return Err(Error::InvalidHardenedUse);
}
}; };
let mut public_sec_raw = [0u8; 65]; let mut public_sec_raw = [0u8; 65];
public_sec_raw[0] = 4; public_sec_raw[0] = 4;
public_sec_raw[1..65].copy_from_slice(&*public_key); public_sec_raw[1..65].copy_from_slice(&*public_key);
let public_sec = PublicKey::from_slice(&SECP256K1, &public_sec_raw).map_err(|_| Error::InvalidPoint)?; let public_sec =
PublicKey::from_slice(&SECP256K1, &public_sec_raw).map_err(|_| Error::InvalidPoint)?;
let public_serialized = public_sec.serialize_vec(&SECP256K1, true); let public_serialized = public_sec.serialize_vec(&SECP256K1, true);
let mut data = vec![0u8; 33 + T::len()]; let mut data = vec![0u8; 33 + T::len()];
@ -323,22 +357,22 @@ mod derivation {
let new_chain_code = H256::from(&i_512[32..64]); let new_chain_code = H256::from(&i_512[32..64]);
// Generated private key can (extremely rarely) be out of secp256k1 key field // Generated private key can (extremely rarely) be out of secp256k1 key field
if curve_order() <= new_private.clone().into() { return Err(Error::MissingIndex); } if curve_order() <= new_private.clone().into() {
return Err(Error::MissingIndex);
}
let new_private_sec = SecretKey::from_slice(&SECP256K1, &*new_private) let new_private_sec = SecretKey::from_slice(&SECP256K1, &*new_private)
.expect("Private key belongs to the field [0..CURVE_ORDER) (checked above); So initializing can never fail; qed"); .expect("Private key belongs to the field [0..CURVE_ORDER) (checked above); So initializing can never fail; qed");
let mut new_public = PublicKey::from_secret_key(&SECP256K1, &new_private_sec) let mut new_public = PublicKey::from_secret_key(&SECP256K1, &new_private_sec)
.expect("Valid private key produces valid public key"); .expect("Valid private key produces valid public key");
// Adding two points on the elliptic curves (combining two public keys) // Adding two points on the elliptic curves (combining two public keys)
new_public.add_assign(&SECP256K1, &public_sec) new_public
.add_assign(&SECP256K1, &public_sec)
.expect("Addition of two valid points produce valid point"); .expect("Addition of two valid points produce valid point");
let serialized = new_public.serialize_vec(&SECP256K1, false); let serialized = new_public.serialize_vec(&SECP256K1, false);
Ok(( Ok((H512::from(&serialized[1..65]), new_chain_code))
H512::from(&serialized[1..65]),
new_chain_code,
))
} }
fn sha3(slc: &[u8]) -> H256 { fn sha3(slc: &[u8]) -> H256 {
@ -348,15 +382,16 @@ mod derivation {
pub fn chain_code(secret: H256) -> H256 { pub fn chain_code(secret: H256) -> H256 {
// 10,000 rounds of sha3 // 10,000 rounds of sha3
let mut running_sha3 = sha3(&*secret); let mut running_sha3 = sha3(&*secret);
for _ in 0..99999 { running_sha3 = sha3(&*running_sha3); } for _ in 0..99999 {
running_sha3 = sha3(&*running_sha3);
}
running_sha3 running_sha3
} }
pub fn point(secret: H256) -> Result<H512, Error> { pub fn point(secret: H256) -> Result<H512, Error> {
let sec = SecretKey::from_slice(&SECP256K1, &*secret) let sec = SecretKey::from_slice(&SECP256K1, &*secret).map_err(|_| Error::InvalidPoint)?;
.map_err(|_| Error::InvalidPoint)?; let public_sec =
let public_sec = PublicKey::from_secret_key(&SECP256K1, &sec) PublicKey::from_secret_key(&SECP256K1, &sec).map_err(|_| Error::InvalidPoint)?;
.map_err(|_| Error::InvalidPoint)?;
let serialized = public_sec.serialize_vec(&SECP256K1, false); let serialized = public_sec.serialize_vec(&SECP256K1, false);
Ok(H512::from(&serialized[1..65])) Ok(H512::from(&serialized[1..65]))
} }
@ -374,11 +409,10 @@ mod derivation {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{ExtendedSecret, ExtendedPublic, ExtendedKeyPair}; use super::{derivation, Derivation, ExtendedKeyPair, ExtendedPublic, ExtendedSecret};
use ethereum_types::{H128, H256};
use secret::Secret; use secret::Secret;
use std::str::FromStr; use std::str::FromStr;
use ethereum_types::{H128, H256};
use super::{derivation, Derivation};
fn master_chain_basic() -> (H256, H256) { fn master_chain_basic() -> (H256, H256) {
let seed = H128::from_str("000102030405060708090a0b0c0d0e0f") let seed = H128::from_str("000102030405060708090a0b0c0d0e0f")
@ -388,7 +422,10 @@ mod tests {
derivation::seed_pair(&*seed) derivation::seed_pair(&*seed)
} }
fn test_extended<F>(f: F, test_private: H256) where F: Fn(ExtendedSecret) -> ExtendedSecret { fn test_extended<F>(f: F, test_private: H256)
where
F: Fn(ExtendedSecret) -> ExtendedSecret,
{
let (private_seed, chain_code) = master_chain_basic(); let (private_seed, chain_code) = master_chain_basic();
let extended_secret = ExtendedSecret::with_code(Secret::from(private_seed.0), chain_code); let extended_secret = ExtendedSecret::with_code(Secret::from(private_seed.0), chain_code);
let derived = f(extended_secret); let derived = f(extended_secret);
@ -397,65 +434,116 @@ mod tests {
#[test] #[test]
fn smoky() { fn smoky() {
let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); let secret =
Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65")
.unwrap();
let extended_secret = ExtendedSecret::with_code(secret.clone(), 0u64.into()); let extended_secret = ExtendedSecret::with_code(secret.clone(), 0u64.into());
// hardened // hardened
assert_eq!(&**extended_secret.as_raw(), &*secret); assert_eq!(&**extended_secret.as_raw(), &*secret);
assert_eq!(&**extended_secret.derive(2147483648.into()).as_raw(), &"0927453daed47839608e414a3738dfad10aed17c459bbd9ab53f89b026c834b6".into()); assert_eq!(
assert_eq!(&**extended_secret.derive(2147483649.into()).as_raw(), &"44238b6a29c6dcbe9b401364141ba11e2198c289a5fed243a1c11af35c19dc0f".into()); &**extended_secret.derive(2147483648.into()).as_raw(),
&"0927453daed47839608e414a3738dfad10aed17c459bbd9ab53f89b026c834b6".into()
);
assert_eq!(
&**extended_secret.derive(2147483649.into()).as_raw(),
&"44238b6a29c6dcbe9b401364141ba11e2198c289a5fed243a1c11af35c19dc0f".into()
);
// normal // normal
assert_eq!(&**extended_secret.derive(0.into()).as_raw(), &"bf6a74e3f7b36fc4c96a1e12f31abc817f9f5904f5a8fc27713163d1f0b713f6".into()); assert_eq!(
assert_eq!(&**extended_secret.derive(1.into()).as_raw(), &"bd4fca9eb1f9c201e9448c1eecd66e302d68d4d313ce895b8c134f512205c1bc".into()); &**extended_secret.derive(0.into()).as_raw(),
assert_eq!(&**extended_secret.derive(2.into()).as_raw(), &"86932b542d6cab4d9c65490c7ef502d89ecc0e2a5f4852157649e3251e2a3268".into()); &"bf6a74e3f7b36fc4c96a1e12f31abc817f9f5904f5a8fc27713163d1f0b713f6".into()
);
assert_eq!(
&**extended_secret.derive(1.into()).as_raw(),
&"bd4fca9eb1f9c201e9448c1eecd66e302d68d4d313ce895b8c134f512205c1bc".into()
);
assert_eq!(
&**extended_secret.derive(2.into()).as_raw(),
&"86932b542d6cab4d9c65490c7ef502d89ecc0e2a5f4852157649e3251e2a3268".into()
);
let extended_public = ExtendedPublic::from_secret(&extended_secret).expect("Extended public should be created"); let extended_public = ExtendedPublic::from_secret(&extended_secret)
let derived_public = extended_public.derive(0.into()).expect("First derivation of public should succeed"); .expect("Extended public should be created");
let derived_public = extended_public
.derive(0.into())
.expect("First derivation of public should succeed");
assert_eq!(&*derived_public.public(), &"f7b3244c96688f92372bfd4def26dc4151529747bab9f188a4ad34e141d47bd66522ff048bc6f19a0a4429b04318b1a8796c000265b4fa200dae5f6dda92dd94".into()); assert_eq!(&*derived_public.public(), &"f7b3244c96688f92372bfd4def26dc4151529747bab9f188a4ad34e141d47bd66522ff048bc6f19a0a4429b04318b1a8796c000265b4fa200dae5f6dda92dd94".into());
let keypair = ExtendedKeyPair::with_secret( let keypair = ExtendedKeyPair::with_secret(
Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(), Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65")
.unwrap(),
064.into(), 064.into(),
); );
assert_eq!(&**keypair.derive(2147483648u32.into()).expect("Derivation of keypair should succeed").secret().as_raw(), &"edef54414c03196557cf73774bc97a645c9a1df2164ed34f0c2a78d1375a930c".into()); assert_eq!(
&**keypair
.derive(2147483648u32.into())
.expect("Derivation of keypair should succeed")
.secret()
.as_raw(),
&"edef54414c03196557cf73774bc97a645c9a1df2164ed34f0c2a78d1375a930c".into()
);
} }
#[test] #[test]
fn h256_soft_match() { fn h256_soft_match() {
let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); let secret =
let derivation_secret = H256::from_str("51eaf04f9dbbc1417dc97e789edd0c37ecda88bac490434e367ea81b71b7b015").unwrap(); Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65")
.unwrap();
let derivation_secret =
H256::from_str("51eaf04f9dbbc1417dc97e789edd0c37ecda88bac490434e367ea81b71b7b015")
.unwrap();
let extended_secret = ExtendedSecret::with_code(secret.clone(), 0u64.into()); let extended_secret = ExtendedSecret::with_code(secret.clone(), 0u64.into());
let extended_public = ExtendedPublic::from_secret(&extended_secret).expect("Extended public should be created"); let extended_public = ExtendedPublic::from_secret(&extended_secret)
.expect("Extended public should be created");
let derived_secret0 = extended_secret.derive(Derivation::Soft(derivation_secret)); let derived_secret0 = extended_secret.derive(Derivation::Soft(derivation_secret));
let derived_public0 = extended_public.derive(Derivation::Soft(derivation_secret)).expect("First derivation of public should succeed"); let derived_public0 = extended_public
.derive(Derivation::Soft(derivation_secret))
.expect("First derivation of public should succeed");
let public_from_secret0 = ExtendedPublic::from_secret(&derived_secret0).expect("Extended public should be created"); let public_from_secret0 = ExtendedPublic::from_secret(&derived_secret0)
.expect("Extended public should be created");
assert_eq!(public_from_secret0.public(), derived_public0.public()); assert_eq!(public_from_secret0.public(), derived_public0.public());
} }
#[test] #[test]
fn h256_hard() { fn h256_hard() {
let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); let secret =
let derivation_secret = H256::from_str("51eaf04f9dbbc1417dc97e789edd0c37ecda88bac490434e367ea81b71b7b015").unwrap(); Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65")
.unwrap();
let derivation_secret =
H256::from_str("51eaf04f9dbbc1417dc97e789edd0c37ecda88bac490434e367ea81b71b7b015")
.unwrap();
let extended_secret = ExtendedSecret::with_code(secret.clone(), 1u64.into()); let extended_secret = ExtendedSecret::with_code(secret.clone(), 1u64.into());
assert_eq!(&**extended_secret.derive(Derivation::Hard(derivation_secret)).as_raw(), &"2bc2d696fb744d77ff813b4a1ef0ad64e1e5188b622c54ba917acc5ebc7c5486".into()); assert_eq!(
&**extended_secret
.derive(Derivation::Hard(derivation_secret))
.as_raw(),
&"2bc2d696fb744d77ff813b4a1ef0ad64e1e5188b622c54ba917acc5ebc7c5486".into()
);
} }
#[test] #[test]
fn match_() { fn match_() {
let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); let secret =
Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65")
.unwrap();
let extended_secret = ExtendedSecret::with_code(secret.clone(), 1.into()); let extended_secret = ExtendedSecret::with_code(secret.clone(), 1.into());
let extended_public = ExtendedPublic::from_secret(&extended_secret).expect("Extended public should be created"); let extended_public = ExtendedPublic::from_secret(&extended_secret)
.expect("Extended public should be created");
let derived_secret0 = extended_secret.derive(0.into()); let derived_secret0 = extended_secret.derive(0.into());
let derived_public0 = extended_public.derive(0.into()).expect("First derivation of public should succeed"); let derived_public0 = extended_public
.derive(0.into())
.expect("First derivation of public should succeed");
let public_from_secret0 = ExtendedPublic::from_secret(&derived_secret0).expect("Extended public should be created"); let public_from_secret0 = ExtendedPublic::from_secret(&derived_secret0)
.expect("Extended public should be created");
assert_eq!(public_from_secret0.public(), derived_public0.public()); assert_eq!(public_from_secret0.public(), derived_public0.public());
} }
@ -468,7 +556,8 @@ mod tests {
// private key from bitcoin test vector // private key from bitcoin test vector
// xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs // xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs
let test_private = H256::from_str("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35") let test_private =
H256::from_str("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35")
.expect("Private should be decoded ok"); .expect("Private should be decoded ok");
let (private_seed, _) = derivation::seed_pair(&*seed); let (private_seed, _) = derivation::seed_pair(&*seed);
@ -483,7 +572,7 @@ mod tests {
test_extended( test_extended(
|secret| secret.derive(2147483648.into()), |secret| secret.derive(2147483648.into()),
H256::from_str("edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea") H256::from_str("edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea")
.expect("Private should be decoded ok") .expect("Private should be decoded ok"),
); );
} }
@ -494,7 +583,7 @@ mod tests {
test_extended( test_extended(
|secret| secret.derive(2147483648.into()).derive(1.into()), |secret| secret.derive(2147483648.into()).derive(1.into()),
H256::from_str("3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368") H256::from_str("3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368")
.expect("Private should be decoded ok") .expect("Private should be decoded ok"),
); );
} }
} }

View File

@ -17,7 +17,9 @@
use tiny_keccak::Keccak; use tiny_keccak::Keccak;
pub trait Keccak256<T> { pub trait Keccak256<T> {
fn keccak256(&self) -> T where T: Sized; fn keccak256(&self) -> T
where
T: Sized;
} }
impl Keccak256<[u8; 32]> for [u8] { impl Keccak256<[u8; 32]> for [u8] {

View File

@ -14,11 +14,11 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::fmt; use super::{Address, Error, Public, Secret, SECP256K1};
use secp256k1::key;
use rustc_hex::ToHex;
use keccak::Keccak256; use keccak::Keccak256;
use super::{Secret, Public, Address, SECP256K1, Error}; use rustc_hex::ToHex;
use secp256k1::key;
use std::fmt;
pub fn public_to_address(public: &Public) -> Address { pub fn public_to_address(public: &Public) -> Address {
let hash = public.keccak256(); let hash = public.keccak256();
@ -94,11 +94,14 @@ impl KeyPair {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::str::FromStr; use std::str::FromStr;
use {KeyPair, Secret}; use KeyPair;
use Secret;
#[test] #[test]
fn from_secret() { fn from_secret() {
let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); let secret =
Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65")
.unwrap();
let _ = KeyPair::from_secret(secret).unwrap(); let _ = KeyPair::from_secret(secret).unwrap();
} }
@ -108,7 +111,9 @@ mod tests {
"secret: a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65 "secret: a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65
public: 8ce0db0b0359ffc5866ba61903cc2518c3675ef2cf380a7e54bde7ea20e6fa1ab45b7617346cd11b7610001ee6ae5b0155c41cad9527cbcdff44ec67848943a4 public: 8ce0db0b0359ffc5866ba61903cc2518c3675ef2cf380a7e54bde7ea20e6fa1ab45b7617346cd11b7610001ee6ae5b0155c41cad9527cbcdff44ec67848943a4
address: 5b073e9233944b5e729e46d618f0d8edf3d9c34a".to_owned(); address: 5b073e9233944b5e729e46d618f0d8edf3d9c34a".to_owned();
let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); let secret =
Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65")
.unwrap();
let kp = KeyPair::from_secret(secret).unwrap(); let kp = KeyPair::from_secret(secret).unwrap();
assert_eq!(format!("{}", kp), expected); assert_eq!(format!("{}", kp), expected);
} }

View File

@ -17,9 +17,9 @@
// #![warn(missing_docs)] // #![warn(missing_docs)]
extern crate edit_distance; extern crate edit_distance;
extern crate parity_crypto;
extern crate ethereum_types; extern crate ethereum_types;
extern crate memzero; extern crate memzero;
extern crate parity_crypto;
extern crate parity_wordlist; extern crate parity_wordlist;
#[macro_use] #[macro_use]
extern crate quick_error; extern crate quick_error;
@ -39,31 +39,33 @@ extern crate serde_derive;
mod brain; mod brain;
mod brain_prefix; mod brain_prefix;
mod error; mod error;
mod keypair; mod extended;
mod keccak; mod keccak;
mod keypair;
mod password; mod password;
mod prefix; mod prefix;
mod random; mod random;
mod signature;
mod secret; mod secret;
mod extended; mod signature;
pub mod brain_recover; pub mod brain_recover;
pub mod crypto; pub mod crypto;
pub mod math; pub mod math;
pub use self::parity_wordlist::Error as WordlistError; pub use self::{
pub use self::brain::Brain; brain::Brain,
pub use self::brain_prefix::BrainPrefix; brain_prefix::BrainPrefix,
pub use self::error::Error; error::Error,
pub use self::keypair::{KeyPair, public_to_address}; extended::{Derivation, DerivationError, ExtendedKeyPair, ExtendedPublic, ExtendedSecret},
pub use self::math::public_is_valid; keypair::{public_to_address, KeyPair},
pub use self::password::Password; math::public_is_valid,
pub use self::prefix::Prefix; parity_wordlist::Error as WordlistError,
pub use self::random::Random; password::Password,
pub use self::signature::{sign, verify_public, verify_address, recover, Signature}; prefix::Prefix,
pub use self::secret::Secret; random::Random,
pub use self::extended::{ExtendedPublic, ExtendedSecret, ExtendedKeyPair, DerivationError, Derivation}; secret::Secret,
signature::{recover, sign, verify_address, verify_public, Signature},
};
use ethereum_types::H256; use ethereum_types::H256;

View File

@ -14,14 +14,17 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use super::{SECP256K1, Public, Secret, Error}; use super::{Error, Public, Secret, SECP256K1};
use secp256k1::key; use ethereum_types::{H256, U256};
use secp256k1::constants::{GENERATOR_X, GENERATOR_Y, CURVE_ORDER}; use secp256k1::{
use ethereum_types::{U256, H256}; constants::{CURVE_ORDER, GENERATOR_X, GENERATOR_Y},
key,
};
/// Whether the public key is valid. /// Whether the public key is valid.
pub fn public_is_valid(public: &Public) -> bool { pub fn public_is_valid(public: &Public) -> bool {
to_secp256k1_public(public).ok() to_secp256k1_public(public)
.ok()
.map_or(false, |p| p.is_valid()) .map_or(false, |p| p.is_valid())
} }
@ -98,8 +101,10 @@ fn set_public(public: &mut Public, key_public: &key::PublicKey) {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::super::{Random, Generator}; use super::{
use super::{public_add, public_sub}; super::{Generator, Random},
public_add, public_sub,
};
#[test] #[test]
fn public_addition_is_commutative() { fn public_addition_is_commutative() {

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use super::{Random, Generator, KeyPair, Error}; use super::{Error, Generator, KeyPair, Random};
/// Tries to find keypair with address starting with given prefix. /// Tries to find keypair with address starting with given prefix.
pub struct Prefix { pub struct Prefix {
@ -38,7 +38,7 @@ impl Generator for Prefix {
for _ in 0..self.iterations { for _ in 0..self.iterations {
let keypair = Random.generate()?; let keypair = Random.generate()?;
if keypair.address().starts_with(&self.prefix) { if keypair.address().starts_with(&self.prefix) {
return Ok(keypair) return Ok(keypair);
} }
} }
@ -48,12 +48,15 @@ impl Generator for Prefix {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use {Generator, Prefix}; use Generator;
use Prefix;
#[test] #[test]
fn prefix_generator() { fn prefix_generator() {
let prefix = vec![0xffu8]; let prefix = vec![0xffu8];
let keypair = Prefix::new(prefix.clone(), usize::max_value()).generate().unwrap(); let keypair = Prefix::new(prefix.clone(), usize::max_value())
.generate()
.unwrap();
assert!(keypair.address().starts_with(&prefix)); assert!(keypair.address().starts_with(&prefix));
} }
} }

View File

@ -14,8 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use rand::os::OsRng;
use super::{Generator, KeyPair, SECP256K1}; use super::{Generator, KeyPair, SECP256K1};
use rand::os::OsRng;
/// Randomly generates new keypair, instantiating the RNG each time. /// Randomly generates new keypair, instantiating the RNG each time.
pub struct Random; pub struct Random;
@ -36,7 +36,8 @@ impl Generator for OsRng {
type Error = ::Void; type Error = ::Void;
fn generate(&mut self) -> Result<KeyPair, Self::Error> { fn generate(&mut self) -> Result<KeyPair, Self::Error> {
let (sec, publ) = SECP256K1.generate_keypair(self) let (sec, publ) = SECP256K1
.generate_keypair(self)
.expect("context always created with full capabilities; qed"); .expect("context always created with full capabilities; qed");
Ok(KeyPair::from_keypair(sec, publ)) Ok(KeyPair::from_keypair(sec, publ))

View File

@ -14,15 +14,13 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::fmt;
use std::ops::Deref;
use std::str::FromStr;
use rustc_hex::ToHex;
use secp256k1::constants::{SECRET_KEY_SIZE as SECP256K1_SECRET_KEY_SIZE};
use secp256k1::key;
use ethereum_types::H256; use ethereum_types::H256;
use memzero::Memzero; use memzero::Memzero;
use {Error, SECP256K1}; use rustc_hex::ToHex;
use secp256k1::{constants::SECRET_KEY_SIZE as SECP256K1_SECRET_KEY_SIZE, key};
use std::{fmt, ops::Deref, str::FromStr};
use Error;
use SECP256K1;
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq)]
pub struct Secret { pub struct Secret {
@ -49,7 +47,11 @@ impl fmt::Debug for Secret {
impl fmt::Display for Secret { impl fmt::Display for Secret {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "Secret: 0x{:x}{:x}..{:x}{:x}", self.inner[0], self.inner[1], self.inner[30], self.inner[31]) write!(
fmt,
"Secret: 0x{:x}{:x}..{:x}{:x}",
self.inner[0], self.inner[1], self.inner[30], self.inner[31]
)
} }
} }
@ -57,16 +59,20 @@ impl Secret {
/// Creates a `Secret` from the given slice, returning `None` if the slice length != 32. /// Creates a `Secret` from the given slice, returning `None` if the slice length != 32.
pub fn from_slice(key: &[u8]) -> Option<Self> { pub fn from_slice(key: &[u8]) -> Option<Self> {
if key.len() != 32 { if key.len() != 32 {
return None return None;
} }
let mut h = H256::default(); let mut h = H256::default();
h.copy_from_slice(&key[0..32]); h.copy_from_slice(&key[0..32]);
Some(Secret { inner: Memzero::from(h) }) Some(Secret {
inner: Memzero::from(h),
})
} }
/// Creates zero key, which is invalid for crypto operations, but valid for math operation. /// Creates zero key, which is invalid for crypto operations, but valid for math operation.
pub fn zero() -> Self { pub fn zero() -> Self {
Secret { inner: Memzero::from(H256::default()) } Secret {
inner: Memzero::from(H256::default()),
}
} }
/// Imports and validates the key. /// Imports and validates the key.
@ -87,7 +93,7 @@ impl Secret {
(true, false) => { (true, false) => {
*self = other.clone(); *self = other.clone();
Ok(()) Ok(())
}, }
(false, false) => { (false, false) => {
let mut key_secret = self.to_secp256k1_secret()?; let mut key_secret = self.to_secp256k1_secret()?;
let other_secret = other.to_secp256k1_secret()?; let other_secret = other.to_secp256k1_secret()?;
@ -95,7 +101,7 @@ impl Secret {
*self = key_secret.into(); *self = key_secret.into();
Ok(()) Ok(())
}, }
} }
} }
@ -106,7 +112,7 @@ impl Secret {
(true, false) => { (true, false) => {
*self = other.clone(); *self = other.clone();
self.neg() self.neg()
}, }
(false, false) => { (false, false) => {
let mut key_secret = self.to_secp256k1_secret()?; let mut key_secret = self.to_secp256k1_secret()?;
let mut other_secret = other.to_secp256k1_secret()?; let mut other_secret = other.to_secp256k1_secret()?;
@ -115,7 +121,7 @@ impl Secret {
*self = key_secret.into(); *self = key_secret.into();
Ok(()) Ok(())
}, }
} }
} }
@ -125,14 +131,14 @@ impl Secret {
true => { true => {
*self = key::MINUS_ONE_KEY.into(); *self = key::MINUS_ONE_KEY.into();
Ok(()) Ok(())
}, }
false => { false => {
let mut key_secret = self.to_secp256k1_secret()?; let mut key_secret = self.to_secp256k1_secret()?;
key_secret.add_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; key_secret.add_assign(&SECP256K1, &key::MINUS_ONE_KEY)?;
*self = key_secret.into(); *self = key_secret.into();
Ok(()) Ok(())
}, }
} }
} }
@ -143,7 +149,7 @@ impl Secret {
(false, true) => { (false, true) => {
*self = Self::zero(); *self = Self::zero();
Ok(()) Ok(())
}, }
(false, false) => { (false, false) => {
let mut key_secret = self.to_secp256k1_secret()?; let mut key_secret = self.to_secp256k1_secret()?;
let other_secret = other.to_secp256k1_secret()?; let other_secret = other.to_secp256k1_secret()?;
@ -151,7 +157,7 @@ impl Secret {
*self = key_secret.into(); *self = key_secret.into();
Ok(()) Ok(())
}, }
} }
} }
@ -165,7 +171,7 @@ impl Secret {
*self = key_secret.into(); *self = key_secret.into();
Ok(()) Ok(())
}, }
} }
} }
@ -193,7 +199,7 @@ impl Secret {
for _ in 1..pow { for _ in 1..pow {
self.mul(&c)?; self.mul(&c)?;
} }
}, }
} }
Ok(()) Ok(())
@ -208,13 +214,17 @@ impl Secret {
impl FromStr for Secret { impl FromStr for Secret {
type Err = Error; type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(H256::from_str(s).map_err(|e| Error::Custom(format!("{:?}", e)))?.into()) Ok(H256::from_str(s)
.map_err(|e| Error::Custom(format!("{:?}", e)))?
.into())
} }
} }
impl From<[u8; 32]> for Secret { impl From<[u8; 32]> for Secret {
fn from(k: [u8; 32]) -> Self { fn from(k: [u8; 32]) -> Self {
Secret { inner: Memzero::from(H256(k)) } Secret {
inner: Memzero::from(H256(k)),
}
} }
} }
@ -226,14 +236,18 @@ impl From<H256> for Secret {
impl From<&'static str> for Secret { impl From<&'static str> for Secret {
fn from(s: &'static str) -> Self { fn from(s: &'static str) -> Self {
s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s)) s.parse().expect(&format!(
"invalid string literal for {}: '{}'",
stringify!(Self),
s
))
} }
} }
impl From<key::SecretKey> for Secret { impl From<key::SecretKey> for Secret {
fn from(key: key::SecretKey) -> Self { fn from(key: key::SecretKey) -> Self {
let mut a = [0; SECP256K1_SECRET_KEY_SIZE]; let mut a = [0; SECP256K1_SECRET_KEY_SIZE];
a.copy_from_slice(&key[0 .. SECP256K1_SECRET_KEY_SIZE]); a.copy_from_slice(&key[0..SECP256K1_SECRET_KEY_SIZE]);
a.into() a.into()
} }
} }
@ -248,9 +262,11 @@ impl Deref for Secret {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{
super::{Generator, Random},
Secret,
};
use std::str::FromStr; use std::str::FromStr;
use super::super::{Random, Generator};
use super::Secret;
#[test] #[test]
fn multiplicating_secret_inversion_with_secret_gives_one() { fn multiplicating_secret_inversion_with_secret_gives_one() {
@ -258,7 +274,11 @@ mod tests {
let mut inversion = secret.clone(); let mut inversion = secret.clone();
inversion.inv().unwrap(); inversion.inv().unwrap();
inversion.mul(&secret).unwrap(); inversion.mul(&secret).unwrap();
assert_eq!(inversion, Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap()); assert_eq!(
inversion,
Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001")
.unwrap()
);
} }
#[test] #[test]
@ -276,7 +296,11 @@ mod tests {
let mut pow0 = secret.clone(); let mut pow0 = secret.clone();
pow0.pow(0).unwrap(); pow0.pow(0).unwrap();
assert_eq!(pow0, Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap()); assert_eq!(
pow0,
Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001")
.unwrap()
);
let mut pow1 = secret.clone(); let mut pow1 = secret.clone();
pow1.pow(1).unwrap(); pow1.pow(1).unwrap();

View File

@ -14,16 +14,26 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::ops::{Deref, DerefMut}; use ethereum_types::{H256, H520};
use std::cmp::PartialEq; use public_to_address;
use std::fmt; use rustc_hex::{FromHex, ToHex};
use std::str::FromStr; use secp256k1::{
use std::hash::{Hash, Hasher}; key::{PublicKey, SecretKey},
use secp256k1::{Message as SecpMessage, RecoverableSignature, RecoveryId, Error as SecpError}; Error as SecpError, Message as SecpMessage, RecoverableSignature, RecoveryId,
use secp256k1::key::{SecretKey, PublicKey}; };
use rustc_hex::{ToHex, FromHex}; use std::{
use ethereum_types::{H520, H256}; cmp::PartialEq,
use {Secret, Public, SECP256K1, Error, Message, public_to_address, Address}; fmt,
hash::{Hash, Hasher},
ops::{Deref, DerefMut},
str::FromStr,
};
use Address;
use Error;
use Message;
use Public;
use Secret;
use SECP256K1;
/// Signature encoded as RSV components /// Signature encoded as RSV components
#[repr(C)] #[repr(C)]
@ -76,16 +86,19 @@ impl Signature {
/// Check if this is a "low" signature. /// Check if this is a "low" signature.
pub fn is_low_s(&self) -> bool { pub fn is_low_s(&self) -> bool {
H256::from_slice(self.s()) <= "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0".into() H256::from_slice(self.s())
<= "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0".into()
} }
/// Check if each component of the signature is in range. /// Check if each component of the signature is in range.
pub fn is_valid(&self) -> bool { pub fn is_valid(&self) -> bool {
self.v() <= 1 && self.v() <= 1
H256::from_slice(self.r()) < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() && && H256::from_slice(self.r())
H256::from_slice(self.r()) >= 1.into() && < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into()
H256::from_slice(self.s()) < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() && && H256::from_slice(self.r()) >= 1.into()
H256::from_slice(self.s()) >= 1.into() && H256::from_slice(self.s())
< "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into()
&& H256::from_slice(self.s()) >= 1.into()
} }
} }
@ -98,7 +111,7 @@ impl PartialEq for Signature {
} }
// manual implementation required in Rust 1.13+, see `std::cmp::AssertParamIsEq`. // manual implementation required in Rust 1.13+, see `std::cmp::AssertParamIsEq`.
impl Eq for Signature { } impl Eq for Signature {}
// also manual for the same reason, but the pretty printing might be useful. // also manual for the same reason, but the pretty printing might be useful.
impl fmt::Debug for Signature { impl fmt::Debug for Signature {
@ -126,8 +139,8 @@ impl FromStr for Signature {
let mut data = [0; 65]; let mut data = [0; 65];
data.copy_from_slice(&hex[0..65]); data.copy_from_slice(&hex[0..65]);
Ok(Signature(data)) Ok(Signature(data))
}, }
_ => Err(Error::InvalidSignature) _ => Err(Error::InvalidSignature),
} }
} }
} }
@ -201,9 +214,17 @@ pub fn sign(secret: &Secret, message: &Message) -> Result<Signature, Error> {
Ok(Signature(data_arr)) Ok(Signature(data_arr))
} }
pub fn verify_public(public: &Public, signature: &Signature, message: &Message) -> Result<bool, Error> { pub fn verify_public(
public: &Public,
signature: &Signature,
message: &Message,
) -> Result<bool, Error> {
let context = &SECP256K1; let context = &SECP256K1;
let rsig = RecoverableSignature::from_compact(context, &signature[0..64], RecoveryId::from_i32(signature[64] as i32)?)?; let rsig = RecoverableSignature::from_compact(
context,
&signature[0..64],
RecoveryId::from_i32(signature[64] as i32)?,
)?;
let sig = rsig.to_standard(context); let sig = rsig.to_standard(context);
let pdata: [u8; 65] = { let pdata: [u8; 65] = {
@ -216,11 +237,15 @@ pub fn verify_public(public: &Public, signature: &Signature, message: &Message)
match context.verify(&SecpMessage::from_slice(&message[..])?, &sig, &publ) { match context.verify(&SecpMessage::from_slice(&message[..])?, &sig, &publ) {
Ok(_) => Ok(true), Ok(_) => Ok(true),
Err(SecpError::IncorrectSignature) => Ok(false), Err(SecpError::IncorrectSignature) => Ok(false),
Err(x) => Err(Error::from(x)) Err(x) => Err(Error::from(x)),
} }
} }
pub fn verify_address(address: &Address, signature: &Signature, message: &Message) -> Result<bool, Error> { pub fn verify_address(
address: &Address,
signature: &Signature,
message: &Message,
) -> Result<bool, Error> {
let public = recover(signature, message)?; let public = recover(signature, message)?;
let recovered_address = public_to_address(&public); let recovered_address = public_to_address(&public);
Ok(address == &recovered_address) Ok(address == &recovered_address)
@ -228,7 +253,11 @@ pub fn verify_address(address: &Address, signature: &Signature, message: &Messag
pub fn recover(signature: &Signature, message: &Message) -> Result<Public, Error> { pub fn recover(signature: &Signature, message: &Message) -> Result<Public, Error> {
let context = &SECP256K1; let context = &SECP256K1;
let rsig = RecoverableSignature::from_compact(context, &signature[0..64], RecoveryId::from_i32(signature[64] as i32)?)?; let rsig = RecoverableSignature::from_compact(
context,
&signature[0..64],
RecoveryId::from_i32(signature[64] as i32)?,
)?;
let pubkey = context.recover(&SecpMessage::from_slice(&message[..])?, &rsig)?; let pubkey = context.recover(&SecpMessage::from_slice(&message[..])?, &rsig)?;
let serialized = pubkey.serialize_vec(context, false); let serialized = pubkey.serialize_vec(context, false);
@ -239,9 +268,11 @@ pub fn recover(signature: &Signature, message: &Message) -> Result<Public, Error
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{recover, sign, verify_address, verify_public, Signature};
use std::str::FromStr; use std::str::FromStr;
use {Generator, Random, Message}; use Generator;
use super::{sign, verify_public, verify_address, recover, Signature}; use Message;
use Random;
#[test] #[test]
fn vrs_conversion() { fn vrs_conversion() {

View File

@ -14,12 +14,10 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::{cmp, thread};
use std::sync::Arc;
use std::collections::VecDeque;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{cmp, collections::VecDeque, sync::Arc, thread};
use ethstore::{ethkey::Password, PresaleWallet, Error}; use ethstore::{ethkey::Password, Error, PresaleWallet};
use num_cpus; use num_cpus;
pub fn run(passwords: VecDeque<Password>, wallet_path: &str) -> Result<(), Error> { pub fn run(passwords: VecDeque<Password>, wallet_path: &str) -> Result<(), Error> {
@ -36,7 +34,9 @@ pub fn run(passwords: VecDeque<Password>, wallet_path: &str) -> Result<(), Error
} }
for handle in handles { for handle in handles {
handle.join().map_err(|err| Error::Custom(format!("Error finishing thread: {:?}", err)))?; handle
.join()
.map_err(|err| Error::Custom(format!("Error finishing thread: {:?}", err)))?;
} }
Ok(()) Ok(())
@ -57,9 +57,9 @@ fn look_for_password(passwords: Arc<Mutex<VecDeque<Password>>>, wallet: PresaleW
println!("Found password: {}", pass.as_str()); println!("Found password: {}", pass.as_str());
passwords.lock().clear(); passwords.lock().clear();
return; return;
}, }
_ if counter % 100 == 0 => print!("."), _ if counter % 100 == 0 => print!("."),
_ => {}, _ => {}
} }
} }
} }

View File

@ -28,14 +28,15 @@ extern crate env_logger;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
use std::collections::VecDeque; use std::{collections::VecDeque, env, fmt, fs, io::Read, process};
use std::io::Read;
use std::{env, process, fs, fmt};
use docopt::Docopt; use docopt::Docopt;
use ethstore::accounts_dir::{KeyDirectory, RootDiskDirectory}; use ethstore::{
use ethstore::ethkey::{Address, Password}; accounts_dir::{KeyDirectory, RootDiskDirectory},
use ethstore::{EthStore, SimpleSecretStore, SecretStore, import_accounts, PresaleWallet, SecretVaultRef, StoreAccountRef}; ethkey::{Address, Password},
import_accounts, EthStore, PresaleWallet, SecretStore, SecretVaultRef, SimpleSecretStore,
StoreAccountRef,
};
mod crack; mod crack;
@ -171,7 +172,7 @@ fn key_dir(location: &str, password: Option<Password>) -> Result<Box<KeyDirector
let chain = path.split('-').nth(1).unwrap_or("ethereum"); let chain = path.split('-').nth(1).unwrap_or("ethereum");
let path = dir::parity(chain); let path = dir::parity(chain);
RootDiskDirectory::create(path)? RootDiskDirectory::create(path)?
}, }
path => RootDiskDirectory::create(path)?, path => RootDiskDirectory::create(path)?,
}; };
@ -188,7 +189,11 @@ fn open_args_vault(store: &EthStore, args: &Args) -> Result<SecretVaultRef, Erro
Ok(SecretVaultRef::Vault(args.flag_vault.clone())) Ok(SecretVaultRef::Vault(args.flag_vault.clone()))
} }
fn open_args_vault_account(store: &EthStore, address: Address, args: &Args) -> Result<StoreAccountRef, Error> { fn open_args_vault_account(
store: &EthStore,
address: Address,
args: &Args,
) -> Result<StoreAccountRef, Error> {
match open_args_vault(store, args)? { match open_args_vault(store, args)? {
SecretVaultRef::Root => Ok(StoreAccountRef::root(address)), SecretVaultRef::Root => Ok(StoreAccountRef::root(address)),
SecretVaultRef::Vault(name) => Ok(StoreAccountRef::vault(&name, address)), SecretVaultRef::Vault(name) => Ok(StoreAccountRef::vault(&name, address)),
@ -196,7 +201,8 @@ fn open_args_vault_account(store: &EthStore, address: Address, args: &Args) -> R
} }
fn format_accounts(accounts: &[Address]) -> String { fn format_accounts(accounts: &[Address]) -> String {
accounts.iter() accounts
.iter()
.enumerate() .enumerate()
.map(|(i, a)| format!("{:2}: 0x{:x}", i, a)) .map(|(i, a)| format!("{:2}: 0x{:x}", i, a))
.collect::<Vec<String>>() .collect::<Vec<String>>()
@ -208,32 +214,47 @@ fn format_vaults(vaults: &[String]) -> String {
} }
fn load_password(path: &str) -> Result<Password, Error> { fn load_password(path: &str) -> Result<Password, Error> {
let mut file = fs::File::open(path).map_err(|e| ethstore::Error::Custom(format!("Error opening password file '{}': {}", path, e)))?; let mut file = fs::File::open(path).map_err(|e| {
ethstore::Error::Custom(format!("Error opening password file '{}': {}", path, e))
})?;
let mut password = String::new(); let mut password = String::new();
file.read_to_string(&mut password).map_err(|e| ethstore::Error::Custom(format!("Error reading password file '{}': {}", path, e)))?; file.read_to_string(&mut password).map_err(|e| {
ethstore::Error::Custom(format!("Error reading password file '{}': {}", path, e))
})?;
// drop EOF // drop EOF
let _ = password.pop(); let _ = password.pop();
Ok(password.into()) Ok(password.into())
} }
fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item=S>, S: AsRef<str> { fn execute<S, I>(command: I) -> Result<String, Error>
let args: Args = Docopt::new(USAGE) where
.and_then(|d| d.argv(command).deserialize())?; I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let args: Args = Docopt::new(USAGE).and_then(|d| d.argv(command).deserialize())?;
let store = EthStore::open(key_dir(&args.flag_dir, None)?)?; let store = EthStore::open(key_dir(&args.flag_dir, None)?)?;
return if args.cmd_insert { return if args.cmd_insert {
let secret = args.arg_secret.parse().map_err(|_| ethstore::Error::InvalidSecret)?; let secret = args
.arg_secret
.parse()
.map_err(|_| ethstore::Error::InvalidSecret)?;
let password = load_password(&args.arg_password)?; let password = load_password(&args.arg_password)?;
let vault_ref = open_args_vault(&store, &args)?; let vault_ref = open_args_vault(&store, &args)?;
let account_ref = store.insert_account(vault_ref, secret, &password)?; let account_ref = store.insert_account(vault_ref, secret, &password)?;
Ok(format!("0x{:x}", account_ref.address)) Ok(format!("0x{:x}", account_ref.address))
} else if args.cmd_change_pwd { } else if args.cmd_change_pwd {
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?; let address = args
.arg_address
.parse()
.map_err(|_| ethstore::Error::InvalidAccount)?;
let old_pwd = load_password(&args.arg_old_pwd)?; let old_pwd = load_password(&args.arg_old_pwd)?;
let new_pwd = load_password(&args.arg_new_pwd)?; let new_pwd = load_password(&args.arg_new_pwd)?;
let account_ref = open_args_vault_account(&store, address, &args)?; let account_ref = open_args_vault_account(&store, address, &args)?;
let ok = store.change_password(&account_ref, &old_pwd, &new_pwd).is_ok(); let ok = store
.change_password(&account_ref, &old_pwd, &new_pwd)
.is_ok();
Ok(format!("{}", ok)) Ok(format!("{}", ok))
} else if args.cmd_list { } else if args.cmd_list {
let vault_ref = open_args_vault(&store, &args)?; let vault_ref = open_args_vault(&store, &args)?;
@ -247,7 +268,7 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
} else if args.cmd_import { } else if args.cmd_import {
let password = match args.arg_password.as_ref() { let password = match args.arg_password.as_ref() {
"" => None, "" => None,
_ => Some(load_password(&args.arg_password)?) _ => Some(load_password(&args.arg_password)?),
}; };
let src = key_dir(&args.flag_src, password)?; let src = key_dir(&args.flag_src, password)?;
let dst = key_dir(&args.flag_dir, None)?; let dst = key_dir(&args.flag_dir, None)?;
@ -263,24 +284,40 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
Ok(format!("0x{:x}", account_ref.address)) Ok(format!("0x{:x}", account_ref.address))
} else if args.cmd_find_wallet_pass { } else if args.cmd_find_wallet_pass {
let passwords = load_password(&args.arg_password)?; let passwords = load_password(&args.arg_password)?;
let passwords = passwords.as_str().lines().map(|line| str::to_owned(line).into()).collect::<VecDeque<_>>(); let passwords = passwords
.as_str()
.lines()
.map(|line| str::to_owned(line).into())
.collect::<VecDeque<_>>();
crack::run(passwords, &args.arg_path)?; crack::run(passwords, &args.arg_path)?;
Ok(format!("Password not found.")) Ok(format!("Password not found."))
} else if args.cmd_remove { } else if args.cmd_remove {
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?; let address = args
.arg_address
.parse()
.map_err(|_| ethstore::Error::InvalidAccount)?;
let password = load_password(&args.arg_password)?; let password = load_password(&args.arg_password)?;
let account_ref = open_args_vault_account(&store, address, &args)?; let account_ref = open_args_vault_account(&store, address, &args)?;
let ok = store.remove_account(&account_ref, &password).is_ok(); let ok = store.remove_account(&account_ref, &password).is_ok();
Ok(format!("{}", ok)) Ok(format!("{}", ok))
} else if args.cmd_sign { } else if args.cmd_sign {
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?; let address = args
let message = args.arg_message.parse().map_err(|_| ethstore::Error::InvalidMessage)?; .arg_address
.parse()
.map_err(|_| ethstore::Error::InvalidAccount)?;
let message = args
.arg_message
.parse()
.map_err(|_| ethstore::Error::InvalidMessage)?;
let password = load_password(&args.arg_password)?; let password = load_password(&args.arg_password)?;
let account_ref = open_args_vault_account(&store, address, &args)?; let account_ref = open_args_vault_account(&store, address, &args)?;
let signature = store.sign(&account_ref, &password, &message)?; let signature = store.sign(&account_ref, &password, &message)?;
Ok(format!("0x{}", signature)) Ok(format!("0x{}", signature))
} else if args.cmd_public { } else if args.cmd_public {
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?; let address = args
.arg_address
.parse()
.map_err(|_| ethstore::Error::InvalidAccount)?;
let password = load_password(&args.arg_password)?; let password = load_password(&args.arg_password)?;
let account_ref = open_args_vault_account(&store, address, &args)?; let account_ref = open_args_vault_account(&store, address, &args)?;
let public = store.public(&account_ref, &password)?; let public = store.public(&account_ref, &password)?;
@ -299,19 +336,28 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
store.change_vault_password(&args.arg_vault, &new_pwd)?; store.change_vault_password(&args.arg_vault, &new_pwd)?;
Ok("OK".to_owned()) Ok("OK".to_owned())
} else if args.cmd_move_to_vault { } else if args.cmd_move_to_vault {
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?; let address = args
.arg_address
.parse()
.map_err(|_| ethstore::Error::InvalidAccount)?;
let password = load_password(&args.arg_password)?; let password = load_password(&args.arg_password)?;
let account_ref = open_args_vault_account(&store, address, &args)?; let account_ref = open_args_vault_account(&store, address, &args)?;
store.open_vault(&args.arg_vault, &password)?; store.open_vault(&args.arg_vault, &password)?;
store.change_account_vault(SecretVaultRef::Vault(args.arg_vault), account_ref)?; store.change_account_vault(SecretVaultRef::Vault(args.arg_vault), account_ref)?;
Ok("OK".to_owned()) Ok("OK".to_owned())
} else if args.cmd_move_from_vault { } else if args.cmd_move_from_vault {
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?; let address = args
.arg_address
.parse()
.map_err(|_| ethstore::Error::InvalidAccount)?;
let password = load_password(&args.arg_password)?; let password = load_password(&args.arg_password)?;
store.open_vault(&args.arg_vault, &password)?; store.open_vault(&args.arg_vault, &password)?;
store.change_account_vault(SecretVaultRef::Root, StoreAccountRef::vault(&args.arg_vault, address))?; store.change_account_vault(
SecretVaultRef::Root,
StoreAccountRef::vault(&args.arg_vault, address),
)?;
Ok("OK".to_owned()) Ok("OK".to_owned())
} else { } else {
Ok(format!("{}", USAGE)) Ok(format!("{}", USAGE))
} };
} }

View File

@ -15,10 +15,8 @@
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
extern crate tempdir; extern crate tempdir;
use std::process::Command; use std::{fs::File, io::Write, process::Command};
use tempdir::TempDir; use tempdir::TempDir;
use std::fs::File;
use std::io::Write;
fn run(args: &[&str]) -> String { fn run(args: &[&str]) -> String {
let output = Command::new("cargo") let output = Command::new("cargo")
@ -32,10 +30,7 @@ fn run(args: &[&str]) -> String {
#[test] #[test]
fn cli_cmd() { fn cli_cmd() {
Command::new("cargo") Command::new("cargo").arg("build").output().unwrap();
.arg("build")
.output()
.unwrap();
let dir = TempDir::new("test-vault").unwrap(); let dir = TempDir::new("test-vault").unwrap();
@ -51,32 +46,62 @@ fn cli_cmd() {
let test_vault_addr_buf = dir.path().join("test-vault-addr"); let test_vault_addr_buf = dir.path().join("test-vault-addr");
let test_vault_addr = test_vault_addr_buf.to_str().unwrap(); let test_vault_addr = test_vault_addr_buf.to_str().unwrap();
run(&["create-vault", "test-vault", test_password, "--dir", dir_str]); run(&[
"create-vault",
"test-vault",
test_password,
"--dir",
dir_str,
]);
let output = run(&["insert", "7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5", let output = run(&[
"insert",
"7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5",
test_vault_addr, test_vault_addr,
"--dir", dir_str, "--dir",
"--vault", "test-vault", dir_str,
"--vault-pwd", test_password]); "--vault",
"test-vault",
"--vault-pwd",
test_password,
]);
let address = output.trim(); let address = output.trim();
let output = run(&["list", let output = run(&[
"--dir", dir_str, "list",
"--vault", "test-vault", "--dir",
"--vault-pwd", test_password]); dir_str,
"--vault",
"test-vault",
"--vault-pwd",
test_password,
]);
assert_eq!(output, " 0: 0xa8fa5dd30a87bb9e3288d604eb74949c515ab66e\n"); assert_eq!(output, " 0: 0xa8fa5dd30a87bb9e3288d604eb74949c515ab66e\n");
let output = run(&["sign", &address[2..], let output = run(&[
"sign",
&address[2..],
test_vault_addr, test_vault_addr,
"7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5", "7d29fab185a33e2cd955812397354c472d2b84615b645aa135ff539f6b0d70d5",
"--dir", dir_str, "--dir",
"--vault", "test-vault", dir_str,
"--vault-pwd", test_password]); "--vault",
"test-vault",
"--vault-pwd",
test_password,
]);
assert_eq!(output, "0x54ab6e5cf0c5cb40043fdca5d15d611a3a94285414a076dafecc8dc9c04183f413296a3defff61092c0bb478dc9887ec01070e1275234211208fb8f4be4a9b0101\n"); assert_eq!(output, "0x54ab6e5cf0c5cb40043fdca5d15d611a3a94285414a076dafecc8dc9c04183f413296a3defff61092c0bb478dc9887ec01070e1275234211208fb8f4be4a9b0101\n");
let output = run(&["public", &address[2..], test_vault_addr, let output = run(&[
"--dir", dir_str, "public",
"--vault", "test-vault", &address[2..],
"--vault-pwd", test_password]); test_vault_addr,
"--dir",
dir_str,
"--vault",
"test-vault",
"--vault-pwd",
test_password,
]);
assert_eq!(output, "0x35f222d88b80151857a2877826d940104887376a94c1cbd2c8c7c192eb701df88a18a4ecb8b05b1466c5b3706042027b5e079fe3a3683e66d822b0e047aa3418\n"); assert_eq!(output, "0x35f222d88b80151857a2877826d940104887376a94c1cbd2c8c7c192eb701df88a18a4ecb8b05b1466c5b3706042027b5e079fe3a3683e66d822b0e047aa3418\n");
} }

View File

@ -28,16 +28,14 @@ pub enum Cipher {
impl From<json::Aes128Ctr> for Aes128Ctr { impl From<json::Aes128Ctr> for Aes128Ctr {
fn from(json: json::Aes128Ctr) -> Self { fn from(json: json::Aes128Ctr) -> Self {
Aes128Ctr { Aes128Ctr { iv: json.iv.into() }
iv: json.iv.into()
}
} }
} }
impl Into<json::Aes128Ctr> for Aes128Ctr { impl Into<json::Aes128Ctr> for Aes128Ctr {
fn into(self) -> json::Aes128Ctr { fn into(self) -> json::Aes128Ctr {
json::Aes128Ctr { json::Aes128Ctr {
iv: From::from(self.iv) iv: From::from(self.iv),
} }
} }
} }

View File

@ -14,14 +14,14 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::str; use account::{Aes128Ctr, Cipher, Kdf, Pbkdf2, Prf};
use std::num::NonZeroU32; use crypto::{self, Keccak256};
use ethkey::{Password, Secret}; use ethkey::{Password, Secret};
use {json, Error, crypto}; use json;
use crypto::Keccak256;
use random::Random; use random::Random;
use smallvec::SmallVec; use smallvec::SmallVec;
use account::{Cipher, Kdf, Aes128Ctr, Pbkdf2, Prf}; use std::{num::NonZeroU32, str};
use Error;
/// Encrypted data /// Encrypted data
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@ -74,12 +74,20 @@ impl From<Crypto> for String {
impl Crypto { impl Crypto {
/// Encrypt account secret /// Encrypt account secret
pub fn with_secret(secret: &Secret, password: &Password, iterations: NonZeroU32) -> Result<Self, crypto::Error> { pub fn with_secret(
secret: &Secret,
password: &Password,
iterations: NonZeroU32,
) -> Result<Self, crypto::Error> {
Crypto::with_plain(&*secret, password, iterations) Crypto::with_plain(&*secret, password, iterations)
} }
/// Encrypt custom plain data /// Encrypt custom plain data
pub fn with_plain(plain: &[u8], password: &Password, iterations: NonZeroU32) -> Result<Self, crypto::Error> { pub fn with_plain(
plain: &[u8],
password: &Password,
iterations: NonZeroU32,
) -> Result<Self, crypto::Error> {
let salt: [u8; 32] = Random::random(); let salt: [u8; 32] = Random::random();
let iv: [u8; 16] = Random::random(); let iv: [u8; 16] = Random::random();
@ -100,9 +108,7 @@ impl Crypto {
let mac = crypto::derive_mac(&derived_right_bits, &*ciphertext).keccak256(); let mac = crypto::derive_mac(&derived_right_bits, &*ciphertext).keccak256();
Ok(Crypto { Ok(Crypto {
cipher: Cipher::Aes128Ctr(Aes128Ctr { cipher: Cipher::Aes128Ctr(Aes128Ctr { iv: iv }),
iv: iv,
}),
ciphertext: ciphertext.into_vec(), ciphertext: ciphertext.into_vec(),
kdf: Kdf::Pbkdf2(Pbkdf2 { kdf: Kdf::Pbkdf2(Pbkdf2 {
dklen: crypto::KEY_LENGTH as u32, dklen: crypto::KEY_LENGTH as u32,
@ -132,14 +138,22 @@ impl Crypto {
fn do_decrypt(&self, password: &Password, expected_len: usize) -> Result<Vec<u8>, Error> { fn do_decrypt(&self, password: &Password, expected_len: usize) -> Result<Vec<u8>, Error> {
let (derived_left_bits, derived_right_bits) = match self.kdf { let (derived_left_bits, derived_right_bits) = match self.kdf {
Kdf::Pbkdf2(ref params) => crypto::derive_key_iterations(password.as_bytes(), &params.salt, params.c), Kdf::Pbkdf2(ref params) => {
Kdf::Scrypt(ref params) => crypto::scrypt::derive_key(password.as_bytes(), &params.salt, params.n, params.p, params.r)?, crypto::derive_key_iterations(password.as_bytes(), &params.salt, params.c)
}
Kdf::Scrypt(ref params) => crypto::scrypt::derive_key(
password.as_bytes(),
&params.salt,
params.n,
params.p,
params.r,
)?,
}; };
let mac = crypto::derive_mac(&derived_right_bits, &self.ciphertext).keccak256(); let mac = crypto::derive_mac(&derived_right_bits, &self.ciphertext).keccak256();
if !crypto::is_equal(&mac, &self.mac) { if !crypto::is_equal(&mac, &self.mac) {
return Err(Error::InvalidPassword) return Err(Error::InvalidPassword);
} }
let mut plain: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; expected_len]); let mut plain: SmallVec<[u8; 32]> = SmallVec::from_vec(vec![0; expected_len]);
@ -150,17 +164,22 @@ impl Crypto {
debug_assert!(expected_len >= self.ciphertext.len()); debug_assert!(expected_len >= self.ciphertext.len());
let from = expected_len - self.ciphertext.len(); let from = expected_len - self.ciphertext.len();
crypto::aes::decrypt_128_ctr(&derived_left_bits, &params.iv, &self.ciphertext, &mut plain[from..])?; crypto::aes::decrypt_128_ctr(
&derived_left_bits,
&params.iv,
&self.ciphertext,
&mut plain[from..],
)?;
Ok(plain.into_iter().collect()) Ok(plain.into_iter().collect())
}, }
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ethkey::{Generator, Random};
use super::{Crypto, Error, NonZeroU32}; use super::{Crypto, Error, NonZeroU32};
use ethkey::{Generator, Random};
lazy_static! { lazy_static! {
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed"); static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed");
@ -178,8 +197,12 @@ mod tests {
#[test] #[test]
fn crypto_with_secret_invalid_password() { fn crypto_with_secret_invalid_password() {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let crypto = Crypto::with_secret(keypair.secret(), &"this is sparta".into(), *ITERATIONS).unwrap(); let crypto =
assert_matches!(crypto.secret(&"this is sparta!".into()), Err(Error::InvalidPassword)) Crypto::with_secret(keypair.secret(), &"this is sparta".into(), *ITERATIONS).unwrap();
assert_matches!(
crypto.secret(&"this is sparta!".into()),
Err(Error::InvalidPassword)
)
} }
#[test] #[test]

View File

@ -20,8 +20,10 @@ mod kdf;
mod safe_account; mod safe_account;
mod version; mod version;
pub use self::cipher::{Cipher, Aes128Ctr}; pub use self::{
pub use self::crypto::Crypto; cipher::{Aes128Ctr, Cipher},
pub use self::kdf::{Kdf, Pbkdf2, Scrypt, Prf}; crypto::Crypto,
pub use self::safe_account::SafeAccount; kdf::{Kdf, Pbkdf2, Prf, Scrypt},
pub use self::version::Version; safe_account::SafeAccount,
version::Version,
};

View File

@ -14,13 +14,15 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use ethkey::{self, KeyPair, sign, Address, Password, Signature, Message, Public, Secret}; use super::crypto::Crypto;
use ethkey::crypto::ecdh::agree;
use {json, Error};
use account::Version; use account::Version;
use crypto; use crypto;
use super::crypto::Crypto; use ethkey::{
self, crypto::ecdh::agree, sign, Address, KeyPair, Message, Password, Public, Secret, Signature,
};
use json;
use std::num::NonZeroU32; use std::num::NonZeroU32;
use Error;
/// Account representation. /// Account representation.
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@ -62,7 +64,7 @@ impl SafeAccount {
password: &Password, password: &Password,
iterations: NonZeroU32, iterations: NonZeroU32,
name: String, name: String,
meta: String meta: String,
) -> Result<Self, crypto::Error> { ) -> Result<Self, crypto::Error> {
Ok(SafeAccount { Ok(SafeAccount {
id: id, id: id,
@ -81,16 +83,25 @@ impl SafeAccount {
/// In case `password` is provided, we will attempt to read the secret from the keyfile /// In case `password` is provided, we will attempt to read the secret from the keyfile
/// and derive the address from it instead of reading it directly. /// and derive the address from it instead of reading it directly.
/// Providing password is required for `json::KeyFile`s with no address. /// Providing password is required for `json::KeyFile`s with no address.
pub fn from_file(json: json::KeyFile, filename: Option<String>, password: &Option<Password>) -> Result<Self, Error> { pub fn from_file(
json: json::KeyFile,
filename: Option<String>,
password: &Option<Password>,
) -> Result<Self, Error> {
let crypto = Crypto::from(json.crypto); let crypto = Crypto::from(json.crypto);
let address = match (password, &json.address) { let address = match (password, &json.address) {
(None, Some(json_address)) => json_address.into(), (None, Some(json_address)) => json_address.into(),
(None, None) => Err(Error::Custom( (None, None) => Err(Error::Custom(
"This keystore does not contain address. You need to provide password to import it".into()))?, "This keystore does not contain address. You need to provide password to import it"
.into(),
))?,
(Some(password), json_address) => { (Some(password), json_address) => {
let derived_address = KeyPair::from_secret( let derived_address = KeyPair::from_secret(
crypto.secret(&password).map_err(|_| Error::InvalidPassword)? crypto
)?.address(); .secret(&password)
.map_err(|_| Error::InvalidPassword)?,
)?
.address();
match json_address { match json_address {
Some(json_address) => { Some(json_address) => {
@ -99,8 +110,8 @@ impl SafeAccount {
warn!("Detected address mismatch when opening an account. Derived: {:?}, in json got: {:?}", warn!("Detected address mismatch when opening an account. Derived: {:?}, in json got: {:?}",
derived_address, json_address); derived_address, json_address);
} }
}, }
_ => {}, _ => {}
} }
derived_address derived_address
} }
@ -120,29 +131,44 @@ impl SafeAccount {
/// Create a new `SafeAccount` from the given vault `json`; if it was read from a /// Create a new `SafeAccount` from the given vault `json`; if it was read from a
/// file, the `filename` should be `Some` name. If it is as yet anonymous, then it /// file, the `filename` should be `Some` name. If it is as yet anonymous, then it
/// can be left `None`. /// can be left `None`.
pub fn from_vault_file(password: &Password, json: json::VaultKeyFile, filename: Option<String>) -> Result<Self, Error> { pub fn from_vault_file(
password: &Password,
json: json::VaultKeyFile,
filename: Option<String>,
) -> Result<Self, Error> {
let meta_crypto: Crypto = json.metacrypto.into(); let meta_crypto: Crypto = json.metacrypto.into();
let meta_plain = meta_crypto.decrypt(password)?; let meta_plain = meta_crypto.decrypt(password)?;
let meta_plain = json::VaultKeyMeta::load(&meta_plain).map_err(|e| Error::Custom(format!("{:?}", e)))?; let meta_plain =
json::VaultKeyMeta::load(&meta_plain).map_err(|e| Error::Custom(format!("{:?}", e)))?;
SafeAccount::from_file(json::KeyFile { SafeAccount::from_file(
json::KeyFile {
id: json.id, id: json.id,
version: json.version, version: json.version,
crypto: json.crypto, crypto: json.crypto,
address: Some(meta_plain.address), address: Some(meta_plain.address),
name: meta_plain.name, name: meta_plain.name,
meta: meta_plain.meta, meta: meta_plain.meta,
}, filename, &None) },
filename,
&None,
)
} }
/// Create a new `VaultKeyFile` from the given `self` /// Create a new `VaultKeyFile` from the given `self`
pub fn into_vault_file(self, iterations: NonZeroU32, password: &Password) -> Result<json::VaultKeyFile, Error> { pub fn into_vault_file(
self,
iterations: NonZeroU32,
password: &Password,
) -> Result<json::VaultKeyFile, Error> {
let meta_plain = json::VaultKeyMeta { let meta_plain = json::VaultKeyMeta {
address: self.address.into(), address: self.address.into(),
name: Some(self.name), name: Some(self.name),
meta: Some(self.meta), meta: Some(self.meta),
}; };
let meta_plain = meta_plain.write().map_err(|e| Error::Custom(format!("{:?}", e)))?; let meta_plain = meta_plain
.write()
.map_err(|e| Error::Custom(format!("{:?}", e)))?;
let meta_crypto = Crypto::with_plain(&meta_plain, password, iterations)?; let meta_crypto = Crypto::with_plain(&meta_plain, password, iterations)?;
Ok(json::VaultKeyFile { Ok(json::VaultKeyFile {
@ -160,7 +186,12 @@ impl SafeAccount {
} }
/// Decrypt a message. /// Decrypt a message.
pub fn decrypt(&self, password: &Password, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> { pub fn decrypt(
&self,
password: &Password,
shared_mac: &[u8],
message: &[u8],
) -> Result<Vec<u8>, Error> {
let secret = self.crypto.secret(password)?; let secret = self.crypto.secret(password)?;
ethkey::crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from) ethkey::crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
} }
@ -178,7 +209,12 @@ impl SafeAccount {
} }
/// Change account's password. /// Change account's password.
pub fn change_password(&self, old_password: &Password, new_password: &Password, iterations: NonZeroU32) -> Result<Self, Error> { pub fn change_password(
&self,
old_password: &Password,
new_password: &Password,
iterations: NonZeroU32,
) -> Result<Self, Error> {
let secret = self.crypto.secret(old_password)?; let secret = self.crypto.secret(old_password)?;
let result = SafeAccount { let result = SafeAccount {
id: self.id.clone(), id: self.id.clone(),
@ -200,20 +236,26 @@ impl SafeAccount {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ethkey::{Generator, Random, verify_public, Message}; use super::{NonZeroU32, SafeAccount};
use super::{SafeAccount, NonZeroU32}; use ethkey::{verify_public, Generator, Message, Random};
lazy_static! { lazy_static! {
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed"); static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed");
} }
#[test] #[test]
fn sign_and_verify_public() { fn sign_and_verify_public() {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let password = "hello world".into(); let password = "hello world".into();
let message = Message::default(); let message = Message::default();
let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()); let account = SafeAccount::create(
&keypair,
[0u8; 16],
&password,
*ITERATIONS,
"Test".to_owned(),
"{}".to_owned(),
);
let signature = account.unwrap().sign(&password, &message).unwrap(); let signature = account.unwrap().sign(&password, &message).unwrap();
assert!(verify_public(keypair.public(), &signature, &message).unwrap()); assert!(verify_public(keypair.public(), &signature, &message).unwrap());
} }
@ -224,8 +266,18 @@ mod tests {
let first_password = "hello world".into(); let first_password = "hello world".into();
let sec_password = "this is sparta".into(); let sec_password = "this is sparta".into();
let message = Message::default(); let message = Message::default();
let account = SafeAccount::create(&keypair, [0u8; 16], &first_password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()).unwrap(); let account = SafeAccount::create(
let new_account = account.change_password(&first_password, &sec_password, *ITERATIONS).unwrap(); &keypair,
[0u8; 16],
&first_password,
*ITERATIONS,
"Test".to_owned(),
"{}".to_owned(),
)
.unwrap();
let new_account = account
.change_password(&first_password, &sec_password, *ITERATIONS)
.unwrap();
assert!(account.sign(&first_password, &message).is_ok()); assert!(account.sign(&first_password, &message).is_ok());
assert!(account.sign(&sec_password, &message).is_err()); assert!(account.sign(&sec_password, &message).is_err());
assert!(new_account.sign(&first_password, &message).is_err()); assert!(new_account.sign(&first_password, &message).is_err());

View File

@ -14,16 +14,21 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::{fs, io}; use super::{
use std::io::Write; vault::{VaultDiskDirectory, VAULT_FILE_NAME},
use std::path::{PathBuf, Path}; KeyDirectory, VaultKey, VaultKeyDirectory, VaultKeyDirectoryProvider,
use std::collections::HashMap; };
use time;
use {json, SafeAccount, Error};
use json::Uuid;
use super::{KeyDirectory, VaultKeyDirectory, VaultKeyDirectoryProvider, VaultKey};
use super::vault::{VAULT_FILE_NAME, VaultDiskDirectory};
use ethkey::Password; use ethkey::Password;
use json::{self, Uuid};
use std::{
collections::HashMap,
fs, io,
io::Write,
path::{Path, PathBuf},
};
use time;
use Error;
use SafeAccount;
const IGNORED_FILES: &'static [&'static str] = &[ const IGNORED_FILES: &'static [&'static str] = &[
"thumbs.db", "thumbs.db",
@ -35,7 +40,10 @@ const IGNORED_FILES: &'static [&'static str] = &[
]; ];
/// Find a unique filename that does not exist using four-letter random suffix. /// Find a unique filename that does not exist using four-letter random suffix.
pub fn find_unique_filename_using_random_suffix(parent_path: &Path, original_filename: &str) -> io::Result<String> { pub fn find_unique_filename_using_random_suffix(
parent_path: &Path,
original_filename: &str,
) -> io::Result<String> {
let mut path = parent_path.join(original_filename); let mut path = parent_path.join(original_filename);
let mut deduped_filename = original_filename.to_string(); let mut deduped_filename = original_filename.to_string();
@ -45,7 +53,10 @@ pub fn find_unique_filename_using_random_suffix(parent_path: &Path, original_fil
while path.exists() { while path.exists() {
if retries >= MAX_RETRIES { if retries >= MAX_RETRIES {
return Err(io::Error::new(io::ErrorKind::Other, "Exceeded maximum retries when deduplicating filename.")); return Err(io::Error::new(
io::ErrorKind::Other,
"Exceeded maximum retries when deduplicating filename.",
));
} }
let suffix = ::random::random_string(4); let suffix = ::random::random_string(4);
@ -106,14 +117,21 @@ pub type RootDiskDirectory = DiskDirectory<DiskKeyFileManager>;
/// Disk directory key file manager /// Disk directory key file manager
pub trait KeyFileManager: Send + Sync { pub trait KeyFileManager: Send + Sync {
/// Read `SafeAccount` from given key file stream /// Read `SafeAccount` from given key file stream
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read; fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error>
where
T: io::Read;
/// Write `SafeAccount` to given key file stream /// Write `SafeAccount` to given key file stream
fn write<T>(&self, account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write; fn write<T>(&self, account: SafeAccount, writer: &mut T) -> Result<(), Error>
where
T: io::Write;
} }
/// Disk-based keys directory implementation /// Disk-based keys directory implementation
pub struct DiskDirectory<T> where T: KeyFileManager { pub struct DiskDirectory<T>
where
T: KeyFileManager,
{
path: PathBuf, path: PathBuf,
key_manager: T, key_manager: T,
} }
@ -125,7 +143,10 @@ pub struct DiskKeyFileManager {
} }
impl RootDiskDirectory { impl RootDiskDirectory {
pub fn create<P>(path: P) -> Result<Self, Error> where P: AsRef<Path> { pub fn create<P>(path: P) -> Result<Self, Error>
where
P: AsRef<Path>,
{
fs::create_dir_all(&path)?; fs::create_dir_all(&path)?;
Ok(Self::at(path)) Ok(Self::at(path))
} }
@ -135,14 +156,23 @@ impl RootDiskDirectory {
DiskDirectory::new(&self.path, DiskKeyFileManager { password }) DiskDirectory::new(&self.path, DiskKeyFileManager { password })
} }
pub fn at<P>(path: P) -> Self where P: AsRef<Path> { pub fn at<P>(path: P) -> Self
where
P: AsRef<Path>,
{
DiskDirectory::new(path, DiskKeyFileManager::default()) DiskDirectory::new(path, DiskKeyFileManager::default())
} }
} }
impl<T> DiskDirectory<T> where T: KeyFileManager { impl<T> DiskDirectory<T>
where
T: KeyFileManager,
{
/// Create new disk directory instance /// Create new disk directory instance
pub fn new<P>(path: P, key_manager: T) -> Self where P: AsRef<Path> { pub fn new<P>(path: P, key_manager: T) -> Self
where
P: AsRef<Path>,
{
DiskDirectory { DiskDirectory {
path: path.as_ref().to_path_buf(), path: path.as_ref().to_path_buf(),
key_manager: key_manager, key_manager: key_manager,
@ -164,13 +194,11 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {
!IGNORED_FILES.contains(&&*name) !IGNORED_FILES.contains(&&*name)
}) })
.map(|entry| entry.path()) .map(|entry| entry.path())
.collect::<Vec<PathBuf>>() .collect::<Vec<PathBuf>>())
)
} }
pub fn files_hash(&self) -> Result<u64, Error> { pub fn files_hash(&self) -> Result<u64, Error> {
use std::collections::hash_map::DefaultHasher; use std::{collections::hash_map::DefaultHasher, hash::Hasher};
use std::hash::Hasher;
let mut hasher = DefaultHasher::new(); let mut hasher = DefaultHasher::new();
let files = self.files()?; let files = self.files()?;
@ -183,7 +211,10 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {
fn last_modification_date(&self) -> Result<u64, Error> { fn last_modification_date(&self) -> Result<u64, Error> {
use std::time::{Duration, UNIX_EPOCH}; use std::time::{Duration, UNIX_EPOCH};
let duration = fs::metadata(&self.path)?.modified()?.duration_since(UNIX_EPOCH).unwrap_or(Duration::default()); let duration = fs::metadata(&self.path)?
.modified()?
.duration_since(UNIX_EPOCH)
.unwrap_or(Duration::default());
let timestamp = duration.as_secs() ^ (duration.subsec_nanos() as u64); let timestamp = duration.as_secs() ^ (duration.subsec_nanos() as u64);
Ok(timestamp) Ok(timestamp)
} }
@ -196,7 +227,12 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {
Ok(paths Ok(paths
.into_iter() .into_iter()
.filter_map(|path| { .filter_map(|path| {
let filename = Some(path.file_name().and_then(|n| n.to_str()).expect("Keys have valid UTF8 names only.").to_owned()); let filename = Some(
path.file_name()
.and_then(|n| n.to_str())
.expect("Keys have valid UTF8 names only.")
.to_owned(),
);
fs::File::open(path.clone()) fs::File::open(path.clone())
.map_err(Into::into) .map_err(Into::into)
.and_then(|file| self.key_manager.read(filename, file)) .and_then(|file| self.key_manager.read(filename, file))
@ -207,13 +243,17 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {
.map(|account| (path, account)) .map(|account| (path, account))
.ok() .ok()
}) })
.collect() .collect())
)
} }
/// insert account with given filename. if the filename is a duplicate of any stored account and dedup is set to /// insert account with given filename. if the filename is a duplicate of any stored account and dedup is set to
/// true, a random suffix is appended to the filename. /// true, a random suffix is appended to the filename.
pub fn insert_with_filename(&self, account: SafeAccount, mut filename: String, dedup: bool) -> Result<SafeAccount, Error> { pub fn insert_with_filename(
&self,
account: SafeAccount,
mut filename: String,
dedup: bool,
) -> Result<SafeAccount, Error> {
if dedup { if dedup {
filename = find_unique_filename_using_random_suffix(&self.path, &filename)?; filename = find_unique_filename_using_random_suffix(&self.path, &filename)?;
} }
@ -235,7 +275,9 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {
}; };
// write key content // write key content
self.key_manager.write(original_account, &mut file).map_err(|e| Error::Custom(format!("{:?}", e)))?; self.key_manager
.write(original_account, &mut file)
.map_err(|e| Error::Custom(format!("{:?}", e)))?;
file.flush()?; file.flush()?;
file.sync_all()?; file.sync_all()?;
@ -250,9 +292,13 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {
} }
} }
impl<T> KeyDirectory for DiskDirectory<T> where T: KeyFileManager { impl<T> KeyDirectory for DiskDirectory<T>
where
T: KeyFileManager,
{
fn load(&self) -> Result<Vec<SafeAccount>, Error> { fn load(&self) -> Result<Vec<SafeAccount>, Error> {
let accounts = self.files_content()? let accounts = self
.files_content()?
.into_iter() .into_iter()
.map(|(_, account)| account) .map(|(_, account)| account)
.collect(); .collect();
@ -273,18 +319,21 @@ impl<T> KeyDirectory for DiskDirectory<T> where T: KeyFileManager {
fn remove(&self, account: &SafeAccount) -> Result<(), Error> { fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
// enumerate all entries in keystore // enumerate all entries in keystore
// and find entry with given address // and find entry with given address
let to_remove = self.files_content()? let to_remove = self
.files_content()?
.into_iter() .into_iter()
.find(|&(_, ref acc)| acc.id == account.id && acc.address == account.address); .find(|&(_, ref acc)| acc.id == account.id && acc.address == account.address);
// remove it // remove it
match to_remove { match to_remove {
None => Err(Error::InvalidAccount), None => Err(Error::InvalidAccount),
Some((path, _)) => fs::remove_file(path).map_err(From::from) Some((path, _)) => fs::remove_file(path).map_err(From::from),
} }
} }
fn path(&self) -> Option<&PathBuf> { Some(&self.path) } fn path(&self) -> Option<&PathBuf> {
Some(&self.path)
}
fn as_vault_provider(&self) -> Option<&VaultKeyDirectoryProvider> { fn as_vault_provider(&self) -> Option<&VaultKeyDirectoryProvider> {
Some(self) Some(self)
@ -295,7 +344,10 @@ impl<T> KeyDirectory for DiskDirectory<T> where T: KeyFileManager {
} }
} }
impl<T> VaultKeyDirectoryProvider for DiskDirectory<T> where T: KeyFileManager { impl<T> VaultKeyDirectoryProvider for DiskDirectory<T>
where
T: KeyFileManager,
{
fn create(&self, name: &str, key: VaultKey) -> Result<Box<VaultKeyDirectory>, Error> { fn create(&self, name: &str, key: VaultKey) -> Result<Box<VaultKeyDirectory>, Error> {
let vault_dir = VaultDiskDirectory::create(&self.path, name, key)?; let vault_dir = VaultDiskDirectory::create(&self.path, name, key)?;
Ok(Box::new(vault_dir)) Ok(Box::new(vault_dir))
@ -313,7 +365,9 @@ impl<T> VaultKeyDirectoryProvider for DiskDirectory<T> where T: KeyFileManager {
let mut vault_file_path = path.clone(); let mut vault_file_path = path.clone();
vault_file_path.push(VAULT_FILE_NAME); vault_file_path.push(VAULT_FILE_NAME);
if vault_file_path.is_file() { if vault_file_path.is_file() {
path.file_name().and_then(|f| f.to_str()).map(|f| f.to_owned()) path.file_name()
.and_then(|f| f.to_str())
.map(|f| f.to_owned())
} else { } else {
None None
} }
@ -327,26 +381,36 @@ impl<T> VaultKeyDirectoryProvider for DiskDirectory<T> where T: KeyFileManager {
} }
impl KeyFileManager for DiskKeyFileManager { impl KeyFileManager for DiskKeyFileManager {
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read { fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error>
let key_file = json::KeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?; where
T: io::Read,
{
let key_file =
json::KeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?;
SafeAccount::from_file(key_file, filename, &self.password) SafeAccount::from_file(key_file, filename, &self.password)
} }
fn write<T>(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write { fn write<T>(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error>
where
T: io::Write,
{
// when account is moved back to root directory from vault // when account is moved back to root directory from vault
// => remove vault field from meta // => remove vault field from meta
account.meta = json::remove_vault_name_from_json_meta(&account.meta) account.meta = json::remove_vault_name_from_json_meta(&account.meta)
.map_err(|err| Error::Custom(format!("{:?}", err)))?; .map_err(|err| Error::Custom(format!("{:?}", err)))?;
let key_file: json::KeyFile = account.into(); let key_file: json::KeyFile = account.into();
key_file.write(writer).map_err(|e| Error::Custom(format!("{:?}", e))) key_file
.write(writer)
.map_err(|e| Error::Custom(format!("{:?}", e)))
} }
} }
fn account_filename(account: &SafeAccount) -> String { fn account_filename(account: &SafeAccount) -> String {
// build file path // build file path
account.filename.clone().unwrap_or_else(|| { account.filename.clone().unwrap_or_else(|| {
let timestamp = time::strftime("%Y-%m-%dT%H-%M-%S", &time::now_utc()).expect("Time-format string is valid."); let timestamp = time::strftime("%Y-%m-%dT%H-%M-%S", &time::now_utc())
.expect("Time-format string is valid.");
format!("UTC--{}Z--{}", timestamp, Uuid::from(account.id)) format!("UTC--{}Z--{}", timestamp, Uuid::from(account.id))
}) })
} }
@ -355,12 +419,11 @@ fn account_filename(account: &SafeAccount) -> String {
mod test { mod test {
extern crate tempdir; extern crate tempdir;
use std::{env, fs}; use self::tempdir::TempDir;
use std::num::NonZeroU32;
use super::{KeyDirectory, RootDiskDirectory, VaultKey}; use super::{KeyDirectory, RootDiskDirectory, VaultKey};
use account::SafeAccount; use account::SafeAccount;
use ethkey::{Random, Generator}; use ethkey::{Generator, Random};
use self::tempdir::TempDir; use std::{env, fs, num::NonZeroU32};
lazy_static! { lazy_static! {
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed"); static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed");
@ -376,12 +439,22 @@ mod test {
let directory = RootDiskDirectory::create(dir.clone()).unwrap(); let directory = RootDiskDirectory::create(dir.clone()).unwrap();
// when // when
let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()); let account = SafeAccount::create(
&keypair,
[0u8; 16],
&password,
*ITERATIONS,
"Test".to_owned(),
"{}".to_owned(),
);
let res = directory.insert(account.unwrap()); let res = directory.insert(account.unwrap());
// then // then
assert!(res.is_ok(), "Should save account succesfuly."); assert!(res.is_ok(), "Should save account succesfuly.");
assert!(res.unwrap().filename.is_some(), "Filename has been assigned."); assert!(
res.unwrap().filename.is_some(),
"Filename has been assigned."
);
// cleanup // cleanup
let _ = fs::remove_dir_all(dir); let _ = fs::remove_dir_all(dir);
@ -397,14 +470,36 @@ mod test {
let directory = RootDiskDirectory::create(dir.clone()).unwrap(); let directory = RootDiskDirectory::create(dir.clone()).unwrap();
// when // when
let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()).unwrap(); let account = SafeAccount::create(
&keypair,
[0u8; 16],
&password,
*ITERATIONS,
"Test".to_owned(),
"{}".to_owned(),
)
.unwrap();
let filename = "test".to_string(); let filename = "test".to_string();
let dedup = true; let dedup = true;
directory.insert_with_filename(account.clone(), "foo".to_string(), dedup).unwrap(); directory
let file1 = directory.insert_with_filename(account.clone(), filename.clone(), dedup).unwrap().filename.unwrap(); .insert_with_filename(account.clone(), "foo".to_string(), dedup)
let file2 = directory.insert_with_filename(account.clone(), filename.clone(), dedup).unwrap().filename.unwrap(); .unwrap();
let file3 = directory.insert_with_filename(account.clone(), filename.clone(), dedup).unwrap().filename.unwrap(); let file1 = directory
.insert_with_filename(account.clone(), filename.clone(), dedup)
.unwrap()
.filename
.unwrap();
let file2 = directory
.insert_with_filename(account.clone(), filename.clone(), dedup)
.unwrap()
.filename
.unwrap();
let file3 = directory
.insert_with_filename(account.clone(), filename.clone(), dedup)
.unwrap()
.filename
.unwrap();
// then // then
// the first file should have the original names // the first file should have the original names
@ -433,7 +528,10 @@ mod test {
// and when // and when
let before_root_items_count = fs::read_dir(&dir).unwrap().count(); let before_root_items_count = fs::read_dir(&dir).unwrap().count();
let vault = directory.as_vault_provider().unwrap().create(vault_name, VaultKey::new(&password, *ITERATIONS)); let vault = directory
.as_vault_provider()
.unwrap()
.create(vault_name, VaultKey::new(&password, *ITERATIONS));
// then // then
assert!(vault.is_ok()); assert!(vault.is_ok());
@ -441,7 +539,10 @@ mod test {
assert!(after_root_items_count > before_root_items_count); assert!(after_root_items_count > before_root_items_count);
// and when // and when
let vault = directory.as_vault_provider().unwrap().open(vault_name, VaultKey::new(&password, *ITERATIONS)); let vault = directory
.as_vault_provider()
.unwrap()
.open(vault_name, VaultKey::new(&password, *ITERATIONS));
// then // then
assert!(vault.is_ok()); assert!(vault.is_ok());
@ -459,8 +560,12 @@ mod test {
let directory = RootDiskDirectory::create(&temp_path).unwrap(); let directory = RootDiskDirectory::create(&temp_path).unwrap();
let vault_provider = directory.as_vault_provider().unwrap(); let vault_provider = directory.as_vault_provider().unwrap();
let iter = NonZeroU32::new(1).expect("1 > 0; qed"); let iter = NonZeroU32::new(1).expect("1 > 0; qed");
vault_provider.create("vault1", VaultKey::new(&"password1".into(), iter)).unwrap(); vault_provider
vault_provider.create("vault2", VaultKey::new(&"password2".into(), iter)).unwrap(); .create("vault1", VaultKey::new(&"password1".into(), iter))
.unwrap();
vault_provider
.create("vault2", VaultKey::new(&"password2".into(), iter))
.unwrap();
// then // then
let vaults = vault_provider.list_vaults().unwrap(); let vaults = vault_provider.list_vaults().unwrap();
@ -474,19 +579,32 @@ mod test {
let temp_path = TempDir::new("").unwrap(); let temp_path = TempDir::new("").unwrap();
let directory = RootDiskDirectory::create(&temp_path).unwrap(); let directory = RootDiskDirectory::create(&temp_path).unwrap();
let hash = directory.files_hash().expect("Files hash should be calculated ok"); let hash = directory
assert_eq!( .files_hash()
hash, .expect("Files hash should be calculated ok");
15130871412783076140 assert_eq!(hash, 15130871412783076140);
);
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let password = "test pass".into(); let password = "test pass".into();
let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()); let account = SafeAccount::create(
directory.insert(account.unwrap()).expect("Account should be inserted ok"); &keypair,
[0u8; 16],
&password,
*ITERATIONS,
"Test".to_owned(),
"{}".to_owned(),
);
directory
.insert(account.unwrap())
.expect("Account should be inserted ok");
let new_hash = directory.files_hash().expect("New files hash should be calculated ok"); let new_hash = directory
.files_hash()
.expect("New files hash should be calculated ok");
assert!(new_hash != hash, "hash of the file list should change once directory content changed"); assert!(
new_hash != hash,
"hash of the file list should change once directory content changed"
);
} }
} }

View File

@ -14,13 +14,14 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::collections::HashMap;
use parking_lot::RwLock;
use itertools;
use ethkey::Address; use ethkey::Address;
use itertools;
use parking_lot::RwLock;
use std::collections::HashMap;
use {SafeAccount, Error};
use super::KeyDirectory; use super::KeyDirectory;
use Error;
use SafeAccount;
/// Accounts in-memory storage. /// Accounts in-memory storage.
#[derive(Default)] #[derive(Default)]
@ -68,7 +69,9 @@ impl KeyDirectory for MemoryDirectory {
fn unique_repr(&self) -> Result<u64, Error> { fn unique_repr(&self) -> Result<u64, Error> {
let mut val = 0u64; let mut val = 0u64;
let accounts = self.accounts.read(); let accounts = self.accounts.read();
for acc in accounts.keys() { val = val ^ acc.low_u64() } for acc in accounts.keys() {
val = val ^ acc.low_u64()
}
Ok(val) Ok(val)
} }
} }

View File

@ -17,9 +17,9 @@
//! Accounts Directory //! Accounts Directory
use ethkey::Password; use ethkey::Password;
use std::num::NonZeroU32; use std::{num::NonZeroU32, path::PathBuf};
use std::path::{PathBuf}; use Error;
use {SafeAccount, Error}; use SafeAccount;
mod disk; mod disk;
mod memory; mod memory;
@ -56,9 +56,13 @@ pub trait KeyDirectory: Send + Sync {
/// Remove key from directory /// Remove key from directory
fn remove(&self, account: &SafeAccount) -> Result<(), Error>; fn remove(&self, account: &SafeAccount) -> Result<(), Error>;
/// Get directory filesystem path, if available /// Get directory filesystem path, if available
fn path(&self) -> Option<&PathBuf> { None } fn path(&self) -> Option<&PathBuf> {
None
}
/// Return vault provider, if available /// Return vault provider, if available
fn as_vault_provider(&self) -> Option<&VaultKeyDirectoryProvider> { None } fn as_vault_provider(&self) -> Option<&VaultKeyDirectoryProvider> {
None
}
/// Unique representation of directory account collection /// Unique representation of directory account collection
fn unique_repr(&self) -> Result<u64, Error>; fn unique_repr(&self) -> Result<u64, Error>;
} }
@ -91,9 +95,11 @@ pub trait VaultKeyDirectory: KeyDirectory {
fn set_meta(&self, meta: &str) -> Result<(), Error>; fn set_meta(&self, meta: &str) -> Result<(), Error>;
} }
pub use self::disk::{RootDiskDirectory, DiskKeyFileManager, KeyFileManager}; pub use self::{
pub use self::memory::MemoryDirectory; disk::{DiskKeyFileManager, KeyFileManager, RootDiskDirectory},
pub use self::vault::VaultDiskDirectory; memory::MemoryDirectory,
vault::VaultDiskDirectory,
};
impl VaultKey { impl VaultKey {
/// Create new vault key /// Create new vault key

View File

@ -14,14 +14,20 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::{fs, io}; use super::{
use std::path::{PathBuf, Path}; super::account::Crypto,
use parking_lot::Mutex; disk::{self, DiskDirectory, KeyFileManager},
use {json, SafeAccount, Error}; KeyDirectory, SetKeyError, VaultKey, VaultKeyDirectory,
};
use crypto::Keccak256; use crypto::Keccak256;
use super::super::account::Crypto; use json;
use super::{KeyDirectory, VaultKeyDirectory, VaultKey, SetKeyError}; use parking_lot::Mutex;
use super::disk::{self, DiskDirectory, KeyFileManager}; use std::{
fs, io,
path::{Path, PathBuf},
};
use Error;
use SafeAccount;
/// Name of vault metadata file /// Name of vault metadata file
pub const VAULT_FILE_NAME: &'static str = "vault.json"; pub const VAULT_FILE_NAME: &'static str = "vault.json";
@ -40,7 +46,10 @@ pub struct VaultKeyFileManager {
impl VaultDiskDirectory { impl VaultDiskDirectory {
/// Create new vault directory with given key /// Create new vault directory with given key
pub fn create<P>(root: P, name: &str, key: VaultKey) -> Result<Self, Error> where P: AsRef<Path> { pub fn create<P>(root: P, name: &str, key: VaultKey) -> Result<Self, Error>
where
P: AsRef<Path>,
{
// check that vault directory does not exists // check that vault directory does not exists
let vault_dir_path = make_vault_dir_path(root, name, true)?; let vault_dir_path = make_vault_dir_path(root, name, true)?;
if vault_dir_path.exists() { if vault_dir_path.exists() {
@ -55,11 +64,17 @@ impl VaultDiskDirectory {
return Err(err); return Err(err);
} }
Ok(DiskDirectory::new(vault_dir_path, VaultKeyFileManager::new(name, key, vault_meta))) Ok(DiskDirectory::new(
vault_dir_path,
VaultKeyFileManager::new(name, key, vault_meta),
))
} }
/// Open existing vault directory with given key /// Open existing vault directory with given key
pub fn at<P>(root: P, name: &str, key: VaultKey) -> Result<Self, Error> where P: AsRef<Path> { pub fn at<P>(root: P, name: &str, key: VaultKey) -> Result<Self, Error>
where
P: AsRef<Path>,
{
// check that vault directory exists // check that vault directory exists
let vault_dir_path = make_vault_dir_path(root, name, true)?; let vault_dir_path = make_vault_dir_path(root, name, true)?;
if !vault_dir_path.is_dir() { if !vault_dir_path.is_dir() {
@ -69,11 +84,17 @@ impl VaultDiskDirectory {
// check that passed key matches vault file // check that passed key matches vault file
let meta = read_vault_file(&vault_dir_path, Some(&key))?; let meta = read_vault_file(&vault_dir_path, Some(&key))?;
Ok(DiskDirectory::new(vault_dir_path, VaultKeyFileManager::new(name, key, &meta))) Ok(DiskDirectory::new(
vault_dir_path,
VaultKeyFileManager::new(name, key, &meta),
))
} }
/// Read vault meta without actually opening the vault /// Read vault meta without actually opening the vault
pub fn meta_at<P>(root: P, name: &str) -> Result<String, Error> where P: AsRef<Path> { pub fn meta_at<P>(root: P, name: &str) -> Result<String, Error>
where
P: AsRef<Path>,
{
// check that vault directory exists // check that vault directory exists
let vault_dir_path = make_vault_dir_path(root, name, true)?; let vault_dir_path = make_vault_dir_path(root, name, true)?;
if !vault_dir_path.is_dir() { if !vault_dir_path.is_dir() {
@ -85,7 +106,9 @@ impl VaultDiskDirectory {
} }
fn create_temp_vault(&self, key: VaultKey) -> Result<VaultDiskDirectory, Error> { fn create_temp_vault(&self, key: VaultKey) -> Result<VaultDiskDirectory, Error> {
let original_path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed"); let original_path = self
.path()
.expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed");
let mut path: PathBuf = original_path.clone(); let mut path: PathBuf = original_path.clone();
let name = self.name(); let name = self.name();
@ -105,7 +128,9 @@ impl VaultDiskDirectory {
fn copy_to_vault(&self, vault: &VaultDiskDirectory) -> Result<(), Error> { fn copy_to_vault(&self, vault: &VaultDiskDirectory) -> Result<(), Error> {
for account in self.load()? { for account in self.load()? {
let filename = account.filename.clone().expect("self is instance of DiskDirectory; DiskDirectory fills filename in load; qed"); let filename = account.filename.clone().expect(
"self is instance of DiskDirectory; DiskDirectory fills filename in load; qed",
);
vault.insert_with_filename(account, filename, true)?; vault.insert_with_filename(account, filename, true)?;
} }
@ -113,7 +138,9 @@ impl VaultDiskDirectory {
} }
fn delete(&self) -> Result<(), Error> { fn delete(&self) -> Result<(), Error> {
let path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed"); let path = self
.path()
.expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed");
fs::remove_dir_all(path).map_err(Into::into) fs::remove_dir_all(path).map_err(Into::into)
} }
} }
@ -132,18 +159,30 @@ impl VaultKeyDirectory for VaultDiskDirectory {
} }
fn set_key(&self, new_key: VaultKey) -> Result<(), SetKeyError> { fn set_key(&self, new_key: VaultKey) -> Result<(), SetKeyError> {
let temp_vault = VaultDiskDirectory::create_temp_vault(self, new_key.clone()).map_err(|err| SetKeyError::NonFatalOld(err))?; let temp_vault = VaultDiskDirectory::create_temp_vault(self, new_key.clone())
let mut source_path = temp_vault.path().expect("temp_vault is instance of DiskDirectory; DiskDirectory always returns path; qed").clone(); .map_err(|err| SetKeyError::NonFatalOld(err))?;
let mut target_path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed").clone(); let mut source_path = temp_vault
.path()
.expect(
"temp_vault is instance of DiskDirectory; DiskDirectory always returns path; qed",
)
.clone();
let mut target_path = self
.path()
.expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed")
.clone();
// preserve meta // preserve meta
temp_vault.set_meta(&self.meta()).map_err(SetKeyError::NonFatalOld)?; temp_vault
.set_meta(&self.meta())
.map_err(SetKeyError::NonFatalOld)?;
// jump to next fs level // jump to next fs level
source_path.push("next"); source_path.push("next");
target_path.push("next"); target_path.push("next");
let temp_accounts = self.copy_to_vault(&temp_vault) let temp_accounts = self
.copy_to_vault(&temp_vault)
.and_then(|_| temp_vault.load()) .and_then(|_| temp_vault.load())
.map_err(|err| { .map_err(|err| {
// ignore error, as we already processing error // ignore error, as we already processing error
@ -155,7 +194,9 @@ impl VaultKeyDirectory for VaultDiskDirectory {
// original vault content has already been partially replaced // original vault content has already been partially replaced
// => when error or crash happens here, we can't do anything // => when error or crash happens here, we can't do anything
for temp_account in temp_accounts { for temp_account in temp_accounts {
let filename = temp_account.filename.expect("self is instance of DiskDirectory; DiskDirectory fills filename in load; qed"); let filename = temp_account.filename.expect(
"self is instance of DiskDirectory; DiskDirectory fills filename in load; qed",
);
source_path.set_file_name(&filename); source_path.set_file_name(&filename);
target_path.set_file_name(&filename); target_path.set_file_name(&filename);
fs::rename(&source_path, &target_path).map_err(|err| SetKeyError::Fatal(err.into()))?; fs::rename(&source_path, &target_path).map_err(|err| SetKeyError::Fatal(err.into()))?;
@ -164,7 +205,9 @@ impl VaultKeyDirectory for VaultDiskDirectory {
target_path.set_file_name(VAULT_FILE_NAME); target_path.set_file_name(VAULT_FILE_NAME);
fs::rename(source_path, target_path).map_err(|err| SetKeyError::Fatal(err.into()))?; fs::rename(source_path, target_path).map_err(|err| SetKeyError::Fatal(err.into()))?;
temp_vault.delete().map_err(|err| SetKeyError::NonFatalNew(err)) temp_vault
.delete()
.map_err(|err| SetKeyError::NonFatalNew(err))
} }
fn meta(&self) -> String { fn meta(&self) -> String {
@ -173,7 +216,9 @@ impl VaultKeyDirectory for VaultDiskDirectory {
fn set_meta(&self, meta: &str) -> Result<(), Error> { fn set_meta(&self, meta: &str) -> Result<(), Error> {
let key_manager = self.key_manager(); let key_manager = self.key_manager();
let vault_path = self.path().expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed"); let vault_path = self
.path()
.expect("self is instance of DiskDirectory; DiskDirectory always returns path; qed");
create_vault_file(vault_path, &key_manager.key, meta)?; create_vault_file(vault_path, &key_manager.key, meta)?;
*key_manager.meta.lock() = meta.to_owned(); *key_manager.meta.lock() = meta.to_owned();
Ok(()) Ok(())
@ -191,26 +236,40 @@ impl VaultKeyFileManager {
} }
impl KeyFileManager for VaultKeyFileManager { impl KeyFileManager for VaultKeyFileManager {
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read { fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error>
let vault_file = json::VaultKeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?; where
let mut safe_account = SafeAccount::from_vault_file(&self.key.password, vault_file, filename.clone())?; T: io::Read,
{
let vault_file =
json::VaultKeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?;
let mut safe_account =
SafeAccount::from_vault_file(&self.key.password, vault_file, filename.clone())?;
safe_account.meta = json::insert_vault_name_to_json_meta(&safe_account.meta, &self.name) safe_account.meta = json::insert_vault_name_to_json_meta(&safe_account.meta, &self.name)
.map_err(|err| Error::Custom(format!("{:?}", err)))?; .map_err(|err| Error::Custom(format!("{:?}", err)))?;
Ok(safe_account) Ok(safe_account)
} }
fn write<T>(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error> where T: io::Write { fn write<T>(&self, mut account: SafeAccount, writer: &mut T) -> Result<(), Error>
where
T: io::Write,
{
account.meta = json::remove_vault_name_from_json_meta(&account.meta) account.meta = json::remove_vault_name_from_json_meta(&account.meta)
.map_err(|err| Error::Custom(format!("{:?}", err)))?; .map_err(|err| Error::Custom(format!("{:?}", err)))?;
let vault_file: json::VaultKeyFile = account.into_vault_file(self.key.iterations, &self.key.password)?; let vault_file: json::VaultKeyFile =
vault_file.write(writer).map_err(|e| Error::Custom(format!("{:?}", e))) account.into_vault_file(self.key.iterations, &self.key.password)?;
vault_file
.write(writer)
.map_err(|e| Error::Custom(format!("{:?}", e)))
} }
} }
/// Makes path to vault directory, checking that vault name is appropriate /// Makes path to vault directory, checking that vault name is appropriate
fn make_vault_dir_path<P>(root: P, name: &str, check_name: bool) -> Result<PathBuf, Error> where P: AsRef<Path> { fn make_vault_dir_path<P>(root: P, name: &str, check_name: bool) -> Result<PathBuf, Error>
where
P: AsRef<Path>,
{
// check vault name // check vault name
if check_name && !check_vault_name(name) { if check_name && !check_vault_name(name) {
return Err(Error::InvalidVaultName); return Err(Error::InvalidVaultName);
@ -226,19 +285,24 @@ fn make_vault_dir_path<P>(root: P, name: &str, check_name: bool) -> Result<PathB
/// => we only allow alphanumeric + separator characters in vault name. /// => we only allow alphanumeric + separator characters in vault name.
fn check_vault_name(name: &str) -> bool { fn check_vault_name(name: &str) -> bool {
!name.is_empty() !name.is_empty()
&& name.chars() && name
.all(|c| c.is_alphanumeric() .chars()
|| c.is_whitespace() .all(|c| c.is_alphanumeric() || c.is_whitespace() || c == '-' || c == '_')
|| c == '-' || c == '_')
} }
/// Vault can be empty, but still must be pluggable => we store vault password in separate file /// Vault can be empty, but still must be pluggable => we store vault password in separate file
fn create_vault_file<P>(vault_dir_path: P, key: &VaultKey, meta: &str) -> Result<(), Error> where P: AsRef<Path> { fn create_vault_file<P>(vault_dir_path: P, key: &VaultKey, meta: &str) -> Result<(), Error>
where
P: AsRef<Path>,
{
let password_hash = key.password.as_bytes().keccak256(); let password_hash = key.password.as_bytes().keccak256();
let crypto = Crypto::with_plain(&password_hash, &key.password, key.iterations)?; let crypto = Crypto::with_plain(&password_hash, &key.password, key.iterations)?;
let vault_file_path = vault_dir_path.as_ref().join(VAULT_FILE_NAME); let vault_file_path = vault_dir_path.as_ref().join(VAULT_FILE_NAME);
let temp_vault_file_name = disk::find_unique_filename_using_random_suffix(vault_dir_path.as_ref(), &VAULT_TEMP_FILE_NAME)?; let temp_vault_file_name = disk::find_unique_filename_using_random_suffix(
vault_dir_path.as_ref(),
&VAULT_TEMP_FILE_NAME,
)?;
let temp_vault_file_path = vault_dir_path.as_ref().join(&temp_vault_file_name); let temp_vault_file_path = vault_dir_path.as_ref().join(&temp_vault_file_name);
// this method is used to rewrite existing vault file // this method is used to rewrite existing vault file
@ -248,7 +312,9 @@ fn create_vault_file<P>(vault_dir_path: P, key: &VaultKey, meta: &str) -> Result
crypto: crypto.into(), crypto: crypto.into(),
meta: Some(meta.to_owned()), meta: Some(meta.to_owned()),
}; };
vault_file_contents.write(&mut vault_file).map_err(|e| Error::Custom(format!("{:?}", e)))?; vault_file_contents
.write(&mut vault_file)
.map_err(|e| Error::Custom(format!("{:?}", e)))?;
drop(vault_file); drop(vault_file);
fs::rename(&temp_vault_file_path, &vault_file_path)?; fs::rename(&temp_vault_file_path, &vault_file_path)?;
@ -256,12 +322,16 @@ fn create_vault_file<P>(vault_dir_path: P, key: &VaultKey, meta: &str) -> Result
} }
/// When vault is opened => we must check that password matches && read metadata /// When vault is opened => we must check that password matches && read metadata
fn read_vault_file<P>(vault_dir_path: P, key: Option<&VaultKey>) -> Result<String, Error> where P: AsRef<Path> { fn read_vault_file<P>(vault_dir_path: P, key: Option<&VaultKey>) -> Result<String, Error>
where
P: AsRef<Path>,
{
let mut vault_file_path: PathBuf = vault_dir_path.as_ref().into(); let mut vault_file_path: PathBuf = vault_dir_path.as_ref().into();
vault_file_path.push(VAULT_FILE_NAME); vault_file_path.push(VAULT_FILE_NAME);
let vault_file = fs::File::open(vault_file_path)?; let vault_file = fs::File::open(vault_file_path)?;
let vault_file_contents = json::VaultFile::load(vault_file).map_err(|e| Error::Custom(format!("{:?}", e)))?; let vault_file_contents =
json::VaultFile::load(vault_file).map_err(|e| Error::Custom(format!("{:?}", e)))?;
let vault_file_meta = vault_file_contents.meta.unwrap_or("{}".to_owned()); let vault_file_meta = vault_file_contents.meta.unwrap_or("{}".to_owned());
let vault_file_crypto: Crypto = vault_file_contents.crypto.into(); let vault_file_crypto: Crypto = vault_file_contents.crypto.into();
@ -280,14 +350,12 @@ fn read_vault_file<P>(vault_dir_path: P, key: Option<&VaultKey>) -> Result<Strin
mod test { mod test {
extern crate tempdir; extern crate tempdir;
use std::fs;
use std::io::Write;
use std::num::NonZeroU32;
use std::path::PathBuf;
use super::VaultKey;
use super::{VAULT_FILE_NAME, check_vault_name, make_vault_dir_path, create_vault_file, read_vault_file, VaultDiskDirectory};
use self::tempdir::TempDir; use self::tempdir::TempDir;
use super::{
check_vault_name, create_vault_file, make_vault_dir_path, read_vault_file,
VaultDiskDirectory, VaultKey, VAULT_FILE_NAME,
};
use std::{fs, io::Write, num::NonZeroU32, path::PathBuf};
lazy_static! { lazy_static! {
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed"); static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed");
@ -318,8 +386,14 @@ mod test {
fn make_vault_dir_path_succeeds() { fn make_vault_dir_path_succeeds() {
use std::path::Path; use std::path::Path;
assert_eq!(&make_vault_dir_path("/home/user/parity", "vault", true).unwrap(), &Path::new("/home/user/parity/vault")); assert_eq!(
assert_eq!(&make_vault_dir_path("/home/user/parity", "*bad-name*", false).unwrap(), &Path::new("/home/user/parity/*bad-name*")); &make_vault_dir_path("/home/user/parity", "vault", true).unwrap(),
&Path::new("/home/user/parity/vault")
);
assert_eq!(
&make_vault_dir_path("/home/user/parity", "*bad-name*", false).unwrap(),
&Path::new("/home/user/parity/*bad-name*")
);
} }
#[test] #[test]
@ -357,7 +431,9 @@ mod test {
vault_file_path.push(VAULT_FILE_NAME); vault_file_path.push(VAULT_FILE_NAME);
{ {
let mut vault_file = fs::File::create(vault_file_path).unwrap(); let mut vault_file = fs::File::create(vault_file_path).unwrap();
vault_file.write_all(vault_file_contents.as_bytes()).unwrap(); vault_file
.write_all(vault_file_contents.as_bytes())
.unwrap();
} }
// when // when
@ -386,7 +462,9 @@ mod test {
let vault_file_contents = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"0155e3690be19fbfbecabcd440aa284b"},"ciphertext":"4d6938a1f49b7782","kdf":"pbkdf2","kdfparams":{"c":1024,"dklen":32,"prf":"hmac-sha256","salt":"b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5"},"mac":"16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262"}}"#; let vault_file_contents = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"0155e3690be19fbfbecabcd440aa284b"},"ciphertext":"4d6938a1f49b7782","kdf":"pbkdf2","kdfparams":{"c":1024,"dklen":32,"prf":"hmac-sha256","salt":"b6a9338a7ccd39288a86dba73bfecd9101b4f3db9c9830e7c76afdbd4f6872e5"},"mac":"16381463ea11c6eb2239a9f339c2e780516d29d234ce30ac5f166f9080b5a262"}}"#;
{ {
let mut vault_file = fs::File::create(vault_file_path).unwrap(); let mut vault_file = fs::File::create(vault_file_path).unwrap();
vault_file.write_all(vault_file_contents.as_bytes()).unwrap(); vault_file
.write_all(vault_file_contents.as_bytes())
.unwrap();
} }
// when // when

View File

@ -14,11 +14,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::fmt;
use std::io::Error as IoError;
use ethkey::{self, Error as EthKeyError};
use crypto::{self, Error as EthCryptoError}; use crypto::{self, Error as EthCryptoError};
use ethkey::DerivationError; use ethkey::{self, DerivationError, Error as EthKeyError};
use std::{fmt, io::Error as IoError};
/// Account-related errors. /// Account-related errors.
#[derive(Debug)] #[derive(Debug)]

File diff suppressed because it is too large Load Diff

View File

@ -14,20 +14,25 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::collections::HashSet; use std::{collections::HashSet, fs, path::Path};
use std::path::Path;
use std::fs;
use ethkey::Address; use accounts_dir::{DiskKeyFileManager, KeyDirectory, KeyFileManager, RootDiskDirectory};
use accounts_dir::{KeyDirectory, RootDiskDirectory, DiskKeyFileManager, KeyFileManager};
use dir; use dir;
use ethkey::Address;
use Error; use Error;
/// Import an account from a file. /// Import an account from a file.
pub fn import_account(path: &Path, dst: &KeyDirectory) -> Result<Address, Error> { pub fn import_account(path: &Path, dst: &KeyDirectory) -> Result<Address, Error> {
let key_manager = DiskKeyFileManager::default(); let key_manager = DiskKeyFileManager::default();
let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::<HashSet<_>>(); let existing_accounts = dst
let filename = path.file_name().and_then(|n| n.to_str()).map(|f| f.to_owned()); .load()?
.into_iter()
.map(|a| a.address)
.collect::<HashSet<_>>();
let filename = path
.file_name()
.and_then(|n| n.to_str())
.map(|f| f.to_owned());
let account = fs::File::open(&path) let account = fs::File::open(&path)
.map_err(Into::into) .map_err(Into::into)
.and_then(|file| key_manager.read(filename, file))?; .and_then(|file| key_manager.read(filename, file))?;
@ -42,17 +47,21 @@ pub fn import_account(path: &Path, dst: &KeyDirectory) -> Result<Address, Error>
/// Import all accounts from one directory to the other. /// Import all accounts from one directory to the other.
pub fn import_accounts(src: &KeyDirectory, dst: &KeyDirectory) -> Result<Vec<Address>, Error> { pub fn import_accounts(src: &KeyDirectory, dst: &KeyDirectory) -> Result<Vec<Address>, Error> {
let accounts = src.load()?; let accounts = src.load()?;
let existing_accounts = dst.load()?.into_iter() let existing_accounts = dst
.load()?
.into_iter()
.map(|a| a.address) .map(|a| a.address)
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();
accounts.into_iter() accounts
.into_iter()
.filter(|a| !existing_accounts.contains(&a.address)) .filter(|a| !existing_accounts.contains(&a.address))
.map(|a| { .map(|a| {
let address = a.address.clone(); let address = a.address.clone();
dst.insert(a)?; dst.insert(a)?;
Ok(address) Ok(address)
}).collect() })
.collect()
} }
/// Provide a `HashSet` of all accounts available for import from the Geth keystore. /// Provide a `HashSet` of all accounts available for import from the Geth keystore.
@ -64,17 +73,27 @@ pub fn read_geth_accounts(testnet: bool) -> Vec<Address> {
} }
/// Import specific `desired` accounts from the Geth keystore into `dst`. /// Import specific `desired` accounts from the Geth keystore into `dst`.
pub fn import_geth_accounts(dst: &KeyDirectory, desired: HashSet<Address>, testnet: bool) -> Result<Vec<Address>, Error> { pub fn import_geth_accounts(
dst: &KeyDirectory,
desired: HashSet<Address>,
testnet: bool,
) -> Result<Vec<Address>, Error> {
let src = RootDiskDirectory::at(dir::geth(testnet)); let src = RootDiskDirectory::at(dir::geth(testnet));
let accounts = src.load()?; let accounts = src.load()?;
let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::<HashSet<_>>(); let existing_accounts = dst
.load()?
.into_iter()
.map(|a| a.address)
.collect::<HashSet<_>>();
accounts.into_iter() accounts
.into_iter()
.filter(|a| !existing_accounts.contains(&a.address)) .filter(|a| !existing_accounts.contains(&a.address))
.filter(|a| desired.contains(&a.address)) .filter(|a| desired.contains(&a.address))
.map(|a| { .map(|a| {
let address = a.address.clone(); let address = a.address.clone();
dst.insert(a)?; dst.insert(a)?;
Ok(address) Ok(address)
}).collect() })
.collect()
} }

View File

@ -14,10 +14,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use rustc_hex::{FromHex, FromHexError, ToHex};
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
use std::{ops, str}; use std::{ops, str};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::de::Error;
use rustc_hex::{ToHex, FromHex, FromHexError};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Bytes(Vec<u8>); pub struct Bytes(Vec<u8>);
@ -32,17 +31,22 @@ impl ops::Deref for Bytes {
impl<'a> Deserialize<'a> for Bytes { impl<'a> Deserialize<'a> for Bytes {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> where
D: Deserializer<'a>,
{ {
let s = String::deserialize(deserializer)?; let s = String::deserialize(deserializer)?;
let data = s.from_hex().map_err(|e| Error::custom(format!("Invalid hex value {}", e)))?; let data = s
.from_hex()
.map_err(|e| Error::custom(format!("Invalid hex value {}", e)))?;
Ok(Bytes(data)) Ok(Bytes(data))
} }
} }
impl Serialize for Bytes { impl Serialize for Bytes {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer { where
S: Serializer,
{
serializer.serialize_str(&self.0.to_hex()) serializer.serialize_str(&self.0.to_hex())
} }
} }
@ -57,7 +61,11 @@ impl str::FromStr for Bytes {
impl From<&'static str> for Bytes { impl From<&'static str> for Bytes {
fn from(s: &'static str) -> Self { fn from(s: &'static str) -> Self {
s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s)) s.parse().expect(&format!(
"invalid string literal for {}: '{}'",
stringify!(Self),
s
))
} }
} }

View File

@ -14,10 +14,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::fmt;
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::{Visitor, Error as SerdeError};
use super::{Error, H128}; use super::{Error, H128};
use serde::{
de::{Error as SerdeError, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::fmt;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum CipherSer { pub enum CipherSer {
@ -26,7 +28,9 @@ pub enum CipherSer {
impl Serialize for CipherSer { impl Serialize for CipherSer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer { where
S: Serializer,
{
match *self { match *self {
CipherSer::Aes128Ctr => serializer.serialize_str("aes-128-ctr"), CipherSer::Aes128Ctr => serializer.serialize_str("aes-128-ctr"),
} }
@ -35,7 +39,9 @@ impl Serialize for CipherSer {
impl<'a> Deserialize<'a> for CipherSer { impl<'a> Deserialize<'a> for CipherSer {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> { where
D: Deserializer<'a>,
{
deserializer.deserialize_any(CipherSerVisitor) deserializer.deserialize_any(CipherSerVisitor)
} }
} }
@ -49,14 +55,20 @@ impl<'a> Visitor<'a> for CipherSerVisitor {
write!(formatter, "a valid cipher identifier") write!(formatter, "a valid cipher identifier")
} }
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: SerdeError { fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: SerdeError,
{
match value { match value {
"aes-128-ctr" => Ok(CipherSer::Aes128Ctr), "aes-128-ctr" => Ok(CipherSer::Aes128Ctr),
_ => Err(SerdeError::custom(Error::UnsupportedCipher)) _ => Err(SerdeError::custom(Error::UnsupportedCipher)),
} }
} }
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: SerdeError { fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
where
E: SerdeError,
{
self.visit_str(value.as_ref()) self.visit_str(value.as_ref())
} }
} }
@ -73,7 +85,9 @@ pub enum CipherSerParams {
impl Serialize for CipherSerParams { impl Serialize for CipherSerParams {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer { where
S: Serializer,
{
match *self { match *self {
CipherSerParams::Aes128Ctr(ref params) => params.serialize(serializer), CipherSerParams::Aes128Ctr(ref params) => params.serialize(serializer),
} }
@ -82,7 +96,9 @@ impl Serialize for CipherSerParams {
impl<'a> Deserialize<'a> for CipherSerParams { impl<'a> Deserialize<'a> for CipherSerParams {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> { where
D: Deserializer<'a>,
{
Aes128Ctr::deserialize(deserializer) Aes128Ctr::deserialize(deserializer)
.map(CipherSerParams::Aes128Ctr) .map(CipherSerParams::Aes128Ctr)
.map_err(|_| Error::InvalidCipherParams) .map_err(|_| Error::InvalidCipherParams)

View File

@ -14,12 +14,14 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::{fmt, str}; use super::{Bytes, Cipher, CipherSer, CipherSerParams, Kdf, KdfSer, KdfSerParams, H256};
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{
use serde::ser::SerializeStruct; de::{Error, MapAccess, Visitor},
use serde::de::{Visitor, MapAccess, Error}; ser::SerializeStruct,
Deserialize, Deserializer, Serialize, Serializer,
};
use serde_json; use serde_json;
use super::{Cipher, CipherSer, CipherSerParams, Kdf, KdfSer, KdfSerParams, H256, Bytes}; use std::{fmt, str};
pub type CipherText = Bytes; pub type CipherText = Bytes;
@ -41,7 +43,8 @@ impl str::FromStr for Crypto {
impl From<Crypto> for String { impl From<Crypto> for String {
fn from(c: Crypto) -> Self { fn from(c: Crypto) -> Self {
serde_json::to_string(&c).expect("serialization cannot fail, cause all crypto keys are strings") serde_json::to_string(&c)
.expect("serialization cannot fail, cause all crypto keys are strings")
} }
} }
@ -57,7 +60,8 @@ enum CryptoField {
impl<'a> Deserialize<'a> for CryptoField { impl<'a> Deserialize<'a> for CryptoField {
fn deserialize<D>(deserializer: D) -> Result<CryptoField, D::Error> fn deserialize<D>(deserializer: D) -> Result<CryptoField, D::Error>
where D: Deserializer<'a> where
D: Deserializer<'a>,
{ {
deserializer.deserialize_any(CryptoFieldVisitor) deserializer.deserialize_any(CryptoFieldVisitor)
} }
@ -73,7 +77,8 @@ impl<'a> Visitor<'a> for CryptoFieldVisitor {
} }
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where E: Error where
E: Error,
{ {
match value { match value {
"cipher" => Ok(CryptoField::Cipher), "cipher" => Ok(CryptoField::Cipher),
@ -90,7 +95,8 @@ impl<'a> Visitor<'a> for CryptoFieldVisitor {
impl<'a> Deserialize<'a> for Crypto { impl<'a> Deserialize<'a> for Crypto {
fn deserialize<D>(deserializer: D) -> Result<Crypto, D::Error> fn deserialize<D>(deserializer: D) -> Result<Crypto, D::Error>
where D: Deserializer<'a> where
D: Deserializer<'a>,
{ {
static FIELDS: &'static [&'static str] = &["id", "version", "crypto", "Crypto", "address"]; static FIELDS: &'static [&'static str] = &["id", "version", "crypto", "Crypto", "address"];
deserializer.deserialize_struct("Crypto", FIELDS, CryptoVisitor) deserializer.deserialize_struct("Crypto", FIELDS, CryptoVisitor)
@ -107,7 +113,8 @@ impl<'a> Visitor<'a> for CryptoVisitor {
} }
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error> fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
where V: MapAccess<'a> where
V: MapAccess<'a>,
{ {
let mut cipher = None; let mut cipher = None;
let mut cipherparams = None; let mut cipherparams = None;
@ -118,20 +125,36 @@ impl<'a> Visitor<'a> for CryptoVisitor {
loop { loop {
match visitor.next_key()? { match visitor.next_key()? {
Some(CryptoField::Cipher) => { cipher = Some(visitor.next_value()?); } Some(CryptoField::Cipher) => {
Some(CryptoField::CipherParams) => { cipherparams = Some(visitor.next_value()?); } cipher = Some(visitor.next_value()?);
Some(CryptoField::CipherText) => { ciphertext = Some(visitor.next_value()?); } }
Some(CryptoField::Kdf) => { kdf = Some(visitor.next_value()?); } Some(CryptoField::CipherParams) => {
Some(CryptoField::KdfParams) => { kdfparams = Some(visitor.next_value()?); } cipherparams = Some(visitor.next_value()?);
Some(CryptoField::Mac) => { mac = Some(visitor.next_value()?); } }
Some(CryptoField::CipherText) => {
ciphertext = Some(visitor.next_value()?);
}
Some(CryptoField::Kdf) => {
kdf = Some(visitor.next_value()?);
}
Some(CryptoField::KdfParams) => {
kdfparams = Some(visitor.next_value()?);
}
Some(CryptoField::Mac) => {
mac = Some(visitor.next_value()?);
}
// skip not required version field (it appears in pyethereum generated keystores) // skip not required version field (it appears in pyethereum generated keystores)
Some(CryptoField::Version) => { visitor.next_value().unwrap_or(()) } Some(CryptoField::Version) => visitor.next_value().unwrap_or(()),
None => { break; } None => {
break;
}
} }
} }
let cipher = match (cipher, cipherparams) { let cipher = match (cipher, cipherparams) {
(Some(CipherSer::Aes128Ctr), Some(CipherSerParams::Aes128Ctr(params))) => Cipher::Aes128Ctr(params), (Some(CipherSer::Aes128Ctr), Some(CipherSerParams::Aes128Ctr(params))) => {
Cipher::Aes128Ctr(params)
}
(None, _) => return Err(V::Error::missing_field("cipher")), (None, _) => return Err(V::Error::missing_field("cipher")),
(Some(_), None) => return Err(V::Error::missing_field("cipherparams")), (Some(_), None) => return Err(V::Error::missing_field("cipherparams")),
}; };
@ -167,25 +190,26 @@ impl<'a> Visitor<'a> for CryptoVisitor {
impl Serialize for Crypto { impl Serialize for Crypto {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer where
S: Serializer,
{ {
let mut crypto = serializer.serialize_struct("Crypto", 6)?; let mut crypto = serializer.serialize_struct("Crypto", 6)?;
match self.cipher { match self.cipher {
Cipher::Aes128Ctr(ref params) => { Cipher::Aes128Ctr(ref params) => {
crypto.serialize_field("cipher", &CipherSer::Aes128Ctr)?; crypto.serialize_field("cipher", &CipherSer::Aes128Ctr)?;
crypto.serialize_field("cipherparams", params)?; crypto.serialize_field("cipherparams", params)?;
}, }
} }
crypto.serialize_field("ciphertext", &self.ciphertext)?; crypto.serialize_field("ciphertext", &self.ciphertext)?;
match self.kdf { match self.kdf {
Kdf::Pbkdf2(ref params) => { Kdf::Pbkdf2(ref params) => {
crypto.serialize_field("kdf", &KdfSer::Pbkdf2)?; crypto.serialize_field("kdf", &KdfSer::Pbkdf2)?;
crypto.serialize_field("kdfparams", params)?; crypto.serialize_field("kdfparams", params)?;
}, }
Kdf::Scrypt(ref params) => { Kdf::Scrypt(ref params) => {
crypto.serialize_field("kdf", &KdfSer::Scrypt)?; crypto.serialize_field("kdf", &KdfSer::Scrypt)?;
crypto.serialize_field("kdfparams", params)?; crypto.serialize_field("kdfparams", params)?;
}, }
} }
crypto.serialize_field("mac", &self.mac)?; crypto.serialize_field("mac", &self.mac)?;

View File

@ -14,11 +14,13 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::{ops, fmt, str};
use rustc_hex::{FromHex, ToHex};
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::{Visitor, Error as SerdeError};
use super::Error; use super::Error;
use rustc_hex::{FromHex, ToHex};
use serde::{
de::{Error as SerdeError, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::{fmt, ops, str};
macro_rules! impl_hash { macro_rules! impl_hash {
($name: ident, $size: expr) => { ($name: ident, $size: expr) => {
@ -49,14 +51,18 @@ macro_rules! impl_hash {
impl Serialize for $name { impl Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer { where
S: Serializer,
{
serializer.serialize_str(&self.0.to_hex()) serializer.serialize_str(&self.0.to_hex())
} }
} }
impl<'a> Deserialize<'a> for $name { impl<'a> Deserialize<'a> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> { where
D: Deserializer<'a>,
{
struct HashVisitor; struct HashVisitor;
impl<'b> Visitor<'b> for HashVisitor { impl<'b> Visitor<'b> for HashVisitor {
@ -66,11 +72,17 @@ macro_rules! impl_hash {
write!(formatter, "a hex-encoded {}", stringify!($name)) write!(formatter, "a hex-encoded {}", stringify!($name))
} }
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: SerdeError { fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: SerdeError,
{
value.parse().map_err(SerdeError::custom) value.parse().map_err(SerdeError::custom)
} }
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: SerdeError { fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
where
E: SerdeError,
{
self.visit_str(value.as_ref()) self.visit_str(value.as_ref())
} }
} }
@ -96,7 +108,11 @@ macro_rules! impl_hash {
impl From<&'static str> for $name { impl From<&'static str> for $name {
fn from(s: &'static str) -> Self { fn from(s: &'static str) -> Self {
s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!($name), s)) s.parse().expect(&format!(
"invalid string literal for {}: '{}'",
stringify!($name),
s
))
} }
} }
@ -111,7 +127,7 @@ macro_rules! impl_hash {
self.0 self.0
} }
} }
} };
} }
impl_hash!(H128, 16); impl_hash!(H128, 16);

View File

@ -15,11 +15,13 @@
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
//! Universaly unique identifier. //! Universaly unique identifier.
use std::{fmt, str};
use rustc_hex::{ToHex, FromHex};
use serde::{Deserialize, Serialize, Deserializer, Serializer};
use serde::de::{Visitor, Error as SerdeError};
use super::Error; use super::Error;
use rustc_hex::{FromHex, ToHex};
use serde::{
de::{Error as SerdeError, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::{fmt, str};
/// Universaly unique identifier. /// Universaly unique identifier.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -38,7 +40,11 @@ impl<'a> Into<String> for &'a Uuid {
let d3 = &self.0[6..8]; let d3 = &self.0[6..8];
let d4 = &self.0[8..10]; let d4 = &self.0[8..10];
let d5 = &self.0[10..16]; let d5 = &self.0[10..16];
[d1, d2, d3, d4, d5].into_iter().map(|d| d.to_hex()).collect::<Vec<String>>().join("-") [d1, d2, d3, d4, d5]
.into_iter()
.map(|d| d.to_hex())
.collect::<Vec<String>>()
.join("-")
} }
} }
@ -96,13 +102,19 @@ impl str::FromStr for Uuid {
impl From<&'static str> for Uuid { impl From<&'static str> for Uuid {
fn from(s: &'static str) -> Self { fn from(s: &'static str) -> Self {
s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s)) s.parse().expect(&format!(
"invalid string literal for {}: '{}'",
stringify!(Self),
s
))
} }
} }
impl Serialize for Uuid { impl Serialize for Uuid {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer { where
S: Serializer,
{
let s: String = self.into(); let s: String = self.into();
serializer.serialize_str(&s) serializer.serialize_str(&s)
} }
@ -110,7 +122,9 @@ impl Serialize for Uuid {
impl<'a> Deserialize<'a> for Uuid { impl<'a> Deserialize<'a> for Uuid {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> { where
D: Deserializer<'a>,
{
deserializer.deserialize_any(UuidVisitor) deserializer.deserialize_any(UuidVisitor)
} }
} }
@ -124,11 +138,17 @@ impl<'a> Visitor<'a> for UuidVisitor {
write!(formatter, "a valid hex-encoded UUID") write!(formatter, "a valid hex-encoded UUID")
} }
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: SerdeError { fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: SerdeError,
{
value.parse().map_err(SerdeError::custom) value.parse().map_err(SerdeError::custom)
} }
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: SerdeError { fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
where
E: SerdeError,
{
self.visit_str(value.as_ref()) self.visit_str(value.as_ref())
} }
} }
@ -140,7 +160,13 @@ mod tests {
#[test] #[test]
fn uuid_from_str() { fn uuid_from_str() {
let uuid: Uuid = "3198bc9c-6672-5ab3-d995-4942343ae5b6".into(); let uuid: Uuid = "3198bc9c-6672-5ab3-d995-4942343ae5b6".into();
assert_eq!(uuid, Uuid::from([0x31, 0x98, 0xbc, 0x9c, 0x66, 0x72, 0x5a, 0xb3, 0xd9, 0x95, 0x49, 0x42, 0x34, 0x3a, 0xe5, 0xb6])); assert_eq!(
uuid,
Uuid::from([
0x31, 0x98, 0xbc, 0x9c, 0x66, 0x72, 0x5a, 0xb3, 0xd9, 0x95, 0x49, 0x42, 0x34, 0x3a,
0xe5, 0xb6
])
);
} }
#[test] #[test]

View File

@ -14,11 +14,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::fmt; use super::{Bytes, Error};
use std::num::NonZeroU32; use serde::{
use serde::{Serialize, Serializer, Deserialize, Deserializer}; de::{Error as SerdeError, Visitor},
use serde::de::{Visitor, Error as SerdeError}; Deserialize, Deserializer, Serialize, Serializer,
use super::{Error, Bytes}; };
use std::{fmt, num::NonZeroU32};
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum KdfSer { pub enum KdfSer {
@ -28,7 +29,9 @@ pub enum KdfSer {
impl Serialize for KdfSer { impl Serialize for KdfSer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer { where
S: Serializer,
{
match *self { match *self {
KdfSer::Pbkdf2 => serializer.serialize_str("pbkdf2"), KdfSer::Pbkdf2 => serializer.serialize_str("pbkdf2"),
KdfSer::Scrypt => serializer.serialize_str("scrypt"), KdfSer::Scrypt => serializer.serialize_str("scrypt"),
@ -38,7 +41,9 @@ impl Serialize for KdfSer {
impl<'a> Deserialize<'a> for KdfSer { impl<'a> Deserialize<'a> for KdfSer {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> { where
D: Deserializer<'a>,
{
deserializer.deserialize_any(KdfSerVisitor) deserializer.deserialize_any(KdfSerVisitor)
} }
} }
@ -52,15 +57,21 @@ impl<'a> Visitor<'a> for KdfSerVisitor {
write!(formatter, "a kdf algorithm identifier") write!(formatter, "a kdf algorithm identifier")
} }
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: SerdeError { fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: SerdeError,
{
match value { match value {
"pbkdf2" => Ok(KdfSer::Pbkdf2), "pbkdf2" => Ok(KdfSer::Pbkdf2),
"scrypt" => Ok(KdfSer::Scrypt), "scrypt" => Ok(KdfSer::Scrypt),
_ => Err(SerdeError::custom(Error::UnsupportedKdf)) _ => Err(SerdeError::custom(Error::UnsupportedKdf)),
} }
} }
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: SerdeError { fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
where
E: SerdeError,
{
self.visit_str(value.as_ref()) self.visit_str(value.as_ref())
} }
} }
@ -72,7 +83,9 @@ pub enum Prf {
impl Serialize for Prf { impl Serialize for Prf {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer { where
S: Serializer,
{
match *self { match *self {
Prf::HmacSha256 => serializer.serialize_str("hmac-sha256"), Prf::HmacSha256 => serializer.serialize_str("hmac-sha256"),
} }
@ -81,7 +94,9 @@ impl Serialize for Prf {
impl<'a> Deserialize<'a> for Prf { impl<'a> Deserialize<'a> for Prf {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> { where
D: Deserializer<'a>,
{
deserializer.deserialize_any(PrfVisitor) deserializer.deserialize_any(PrfVisitor)
} }
} }
@ -95,14 +110,20 @@ impl<'a> Visitor<'a> for PrfVisitor {
write!(formatter, "a prf algorithm identifier") write!(formatter, "a prf algorithm identifier")
} }
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: SerdeError { fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: SerdeError,
{
match value { match value {
"hmac-sha256" => Ok(Prf::HmacSha256), "hmac-sha256" => Ok(Prf::HmacSha256),
_ => Err(SerdeError::custom(Error::InvalidPrf)), _ => Err(SerdeError::custom(Error::InvalidPrf)),
} }
} }
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: SerdeError { fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
where
E: SerdeError,
{
self.visit_str(value.as_ref()) self.visit_str(value.as_ref())
} }
} }
@ -132,7 +153,9 @@ pub enum KdfSerParams {
impl Serialize for KdfSerParams { impl Serialize for KdfSerParams {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer { where
S: Serializer,
{
match *self { match *self {
KdfSerParams::Pbkdf2(ref params) => params.serialize(serializer), KdfSerParams::Pbkdf2(ref params) => params.serialize(serializer),
KdfSerParams::Scrypt(ref params) => params.serialize(serializer), KdfSerParams::Scrypt(ref params) => params.serialize(serializer),
@ -142,12 +165,15 @@ impl Serialize for KdfSerParams {
impl<'a> Deserialize<'a> for KdfSerParams { impl<'a> Deserialize<'a> for KdfSerParams {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> { where
use serde_json::{Value, from_value}; D: Deserializer<'a>,
{
use serde_json::{from_value, Value};
let v: Value = Deserialize::deserialize(deserializer)?; let v: Value = Deserialize::deserialize(deserializer)?;
from_value(v.clone()).map(KdfSerParams::Pbkdf2) from_value(v.clone())
.map(KdfSerParams::Pbkdf2)
.or_else(|_| from_value(v).map(KdfSerParams::Scrypt)) .or_else(|_| from_value(v).map(KdfSerParams::Scrypt))
.map_err(|_| D::Error::custom("Invalid KDF algorithm")) .map_err(|_| D::Error::custom("Invalid KDF algorithm"))
} }

View File

@ -14,30 +14,40 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::fmt; use super::{Crypto, Uuid, Version, H160};
use std::io::{Read, Write}; use serde::{
use serde::{Serialize, Serializer, Deserialize, Deserializer}; de::{DeserializeOwned, Error, MapAccess, Visitor},
use serde::de::{Error, Visitor, MapAccess, DeserializeOwned}; Deserialize, Deserializer, Serialize, Serializer,
};
use serde_json; use serde_json;
use super::{Uuid, Version, Crypto, H160}; use std::{
fmt,
io::{Read, Write},
};
/// Public opaque type representing serializable `KeyFile`. /// Public opaque type representing serializable `KeyFile`.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct OpaqueKeyFile { pub struct OpaqueKeyFile {
key_file: KeyFile key_file: KeyFile,
} }
impl Serialize for OpaqueKeyFile { impl Serialize for OpaqueKeyFile {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer, S: Serializer,
{ {
self.key_file.serialize(serializer) self.key_file.serialize(serializer)
} }
} }
impl<T> From<T> for OpaqueKeyFile where T: Into<KeyFile> { impl<T> From<T> for OpaqueKeyFile
where
T: Into<KeyFile>,
{
fn from(val: T) -> Self { fn from(val: T) -> Self {
OpaqueKeyFile { key_file: val.into() } OpaqueKeyFile {
key_file: val.into(),
}
} }
} }
@ -62,7 +72,8 @@ enum KeyFileField {
impl<'a> Deserialize<'a> for KeyFileField { impl<'a> Deserialize<'a> for KeyFileField {
fn deserialize<D>(deserializer: D) -> Result<KeyFileField, D::Error> fn deserialize<D>(deserializer: D) -> Result<KeyFileField, D::Error>
where D: Deserializer<'a> where
D: Deserializer<'a>,
{ {
deserializer.deserialize_any(KeyFileFieldVisitor) deserializer.deserialize_any(KeyFileFieldVisitor)
} }
@ -78,7 +89,8 @@ impl<'a> Visitor<'a> for KeyFileFieldVisitor {
} }
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where E: Error where
E: Error,
{ {
match value { match value {
"id" => Ok(KeyFileField::Id), "id" => Ok(KeyFileField::Id),
@ -95,22 +107,25 @@ impl<'a> Visitor<'a> for KeyFileFieldVisitor {
impl<'a> Deserialize<'a> for KeyFile { impl<'a> Deserialize<'a> for KeyFile {
fn deserialize<D>(deserializer: D) -> Result<KeyFile, D::Error> fn deserialize<D>(deserializer: D) -> Result<KeyFile, D::Error>
where D: Deserializer<'a> where
D: Deserializer<'a>,
{ {
static FIELDS: &'static [&'static str] = &["id", "version", "crypto", "Crypto", "address"]; static FIELDS: &'static [&'static str] = &["id", "version", "crypto", "Crypto", "address"];
deserializer.deserialize_struct("KeyFile", FIELDS, KeyFileVisitor) deserializer.deserialize_struct("KeyFile", FIELDS, KeyFileVisitor)
} }
} }
fn none_if_empty<'a, T>(v: Option<serde_json::Value>) -> Option<T> where fn none_if_empty<'a, T>(v: Option<serde_json::Value>) -> Option<T>
T: DeserializeOwned where
T: DeserializeOwned,
{ {
v.and_then(|v| if v.is_null() { v.and_then(|v| {
if v.is_null() {
None None
} else { } else {
serde_json::from_value(v).ok() serde_json::from_value(v).ok()
}
}) })
} }
struct KeyFileVisitor; struct KeyFileVisitor;
@ -122,7 +137,8 @@ impl<'a> Visitor<'a> for KeyFileVisitor {
} }
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error> fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
where V: MapAccess<'a> where
V: MapAccess<'a>,
{ {
let mut id = None; let mut id = None;
let mut version = None; let mut version = None;
@ -133,13 +149,23 @@ impl<'a> Visitor<'a> for KeyFileVisitor {
loop { loop {
match visitor.next_key()? { match visitor.next_key()? {
Some(KeyFileField::Id) => { id = Some(visitor.next_value()?); } Some(KeyFileField::Id) => {
Some(KeyFileField::Version) => { version = Some(visitor.next_value()?); } id = Some(visitor.next_value()?);
Some(KeyFileField::Crypto) => { crypto = Some(visitor.next_value()?); } }
Some(KeyFileField::Address) => { address = Some(visitor.next_value()?); } Some(KeyFileField::Version) => {
Some(KeyFileField::Name) => { name = none_if_empty(visitor.next_value().ok()) } version = Some(visitor.next_value()?);
Some(KeyFileField::Meta) => { meta = none_if_empty(visitor.next_value().ok()) } }
None => { break; } Some(KeyFileField::Crypto) => {
crypto = Some(visitor.next_value()?);
}
Some(KeyFileField::Address) => {
address = Some(visitor.next_value()?);
}
Some(KeyFileField::Name) => name = none_if_empty(visitor.next_value().ok()),
Some(KeyFileField::Meta) => meta = none_if_empty(visitor.next_value().ok()),
None => {
break;
}
} }
} }
@ -172,20 +198,26 @@ impl<'a> Visitor<'a> for KeyFileVisitor {
} }
impl KeyFile { impl KeyFile {
pub fn load<R>(reader: R) -> Result<Self, serde_json::Error> where R: Read { pub fn load<R>(reader: R) -> Result<Self, serde_json::Error>
where
R: Read,
{
serde_json::from_reader(reader) serde_json::from_reader(reader)
} }
pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error> where W: Write { pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error>
where
W: Write,
{
serde_json::to_writer(writer, self) serde_json::to_writer(writer, self)
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::str::FromStr; use json::{Aes128Ctr, Cipher, Crypto, Kdf, KeyFile, Scrypt, Uuid, Version};
use serde_json; use serde_json;
use json::{KeyFile, Uuid, Version, Crypto, Cipher, Aes128Ctr, Kdf, Scrypt}; use std::str::FromStr;
#[test] #[test]
fn basic_keyfile() { fn basic_keyfile() {
@ -222,7 +254,8 @@ mod tests {
cipher: Cipher::Aes128Ctr(Aes128Ctr { cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: "b5a7ec855ec9e2c405371356855fec83".into(), iv: "b5a7ec855ec9e2c405371356855fec83".into(),
}), }),
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(), ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc"
.into(),
kdf: Kdf::Scrypt(Scrypt { kdf: Kdf::Scrypt(Scrypt {
n: 262144, n: 262144,
dklen: 32, dklen: 32,
@ -273,7 +306,8 @@ mod tests {
cipher: Cipher::Aes128Ctr(Aes128Ctr { cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: "b5a7ec855ec9e2c405371356855fec83".into(), iv: "b5a7ec855ec9e2c405371356855fec83".into(),
}), }),
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(), ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc"
.into(),
kdf: Kdf::Scrypt(Scrypt { kdf: Kdf::Scrypt(Scrypt {
n: 262144, n: 262144,
dklen: 32, dklen: 32,
@ -301,7 +335,8 @@ mod tests {
cipher: Cipher::Aes128Ctr(Aes128Ctr { cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: "b5a7ec855ec9e2c405371356855fec83".into(), iv: "b5a7ec855ec9e2c405371356855fec83".into(),
}), }),
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(), ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc"
.into(),
kdf: Kdf::Scrypt(Scrypt { kdf: Kdf::Scrypt(Scrypt {
n: 262144, n: 262144,
dklen: 32, dklen: 32,

View File

@ -29,15 +29,20 @@ mod vault_file;
mod vault_key_file; mod vault_key_file;
mod version; mod version;
pub use self::bytes::Bytes; pub use self::{
pub use self::cipher::{Cipher, CipherSer, CipherSerParams, Aes128Ctr}; bytes::Bytes,
pub use self::crypto::{Crypto, CipherText}; cipher::{Aes128Ctr, Cipher, CipherSer, CipherSerParams},
pub use self::error::Error; crypto::{CipherText, Crypto},
pub use self::hash::{H128, H160, H256}; error::Error,
pub use self::id::Uuid; hash::{H128, H160, H256},
pub use self::kdf::{Kdf, KdfSer, Prf, Pbkdf2, Scrypt, KdfSerParams}; id::Uuid,
pub use self::key_file::{KeyFile, OpaqueKeyFile}; kdf::{Kdf, KdfSer, KdfSerParams, Pbkdf2, Prf, Scrypt},
pub use self::presale::{PresaleWallet, Encseed}; key_file::{KeyFile, OpaqueKeyFile},
pub use self::vault_file::VaultFile; presale::{Encseed, PresaleWallet},
pub use self::vault_key_file::{VaultKeyFile, VaultKeyMeta, insert_vault_name_to_json_meta, remove_vault_name_from_json_meta}; vault_file::VaultFile,
pub use self::version::Version; vault_key_file::{
insert_vault_name_to_json_meta, remove_vault_name_from_json_meta, VaultKeyFile,
VaultKeyMeta,
},
version::Version,
};

View File

@ -14,9 +14,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::io::Read; use super::{Bytes, H160};
use serde_json; use serde_json;
use super::{H160, Bytes}; use std::io::Read;
pub type Encseed = Bytes; pub type Encseed = Bytes;
@ -28,16 +28,19 @@ pub struct PresaleWallet {
} }
impl PresaleWallet { impl PresaleWallet {
pub fn load<R>(reader: R) -> Result<Self, serde_json::Error> where R: Read { pub fn load<R>(reader: R) -> Result<Self, serde_json::Error>
where
R: Read,
{
serde_json::from_reader(reader) serde_json::from_reader(reader)
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::str::FromStr;
use serde_json;
use json::{PresaleWallet, H160}; use json::{PresaleWallet, H160};
use serde_json;
use std::str::FromStr;
#[test] #[test]
fn presale_wallet() { fn presale_wallet() {

View File

@ -14,9 +14,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::io::{Read, Write};
use serde_json;
use super::Crypto; use super::Crypto;
use serde_json;
use std::io::{Read, Write};
/// Vault meta file /// Vault meta file
#[derive(Debug, PartialEq, Serialize, Deserialize)] #[derive(Debug, PartialEq, Serialize, Deserialize)]
@ -28,19 +28,25 @@ pub struct VaultFile {
} }
impl VaultFile { impl VaultFile {
pub fn load<R>(reader: R) -> Result<Self, serde_json::Error> where R: Read { pub fn load<R>(reader: R) -> Result<Self, serde_json::Error>
where
R: Read,
{
serde_json::from_reader(reader) serde_json::from_reader(reader)
} }
pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error> where W: Write { pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error>
where
W: Write,
{
serde_json::to_writer(writer, self) serde_json::to_writer(writer, self)
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use json::{Aes128Ctr, Cipher, Crypto, Kdf, Pbkdf2, Prf, VaultFile};
use serde_json; use serde_json;
use json::{VaultFile, Crypto, Cipher, Aes128Ctr, Kdf, Pbkdf2, Prf};
use std::num::NonZeroU32; use std::num::NonZeroU32;
lazy_static! { lazy_static! {

View File

@ -14,12 +14,10 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::io::{Read, Write}; use super::{Crypto, Uuid, Version, H160};
use serde::de::Error; use serde::de::Error;
use serde_json; use serde_json::{self, error, value::Value};
use serde_json::value::Value; use std::io::{Read, Write};
use serde_json::error;
use super::{Uuid, Version, Crypto, H160};
/// Meta key name for vault field /// Meta key name for vault field
const VAULT_NAME_META_KEY: &'static str = "vault"; const VAULT_NAME_META_KEY: &'static str = "vault";
@ -49,7 +47,10 @@ pub struct VaultKeyMeta {
} }
/// Insert vault name to the JSON meta field /// Insert vault name to the JSON meta field
pub fn insert_vault_name_to_json_meta(meta: &str, vault_name: &str) -> Result<String, error::Error> { pub fn insert_vault_name_to_json_meta(
meta: &str,
vault_name: &str,
) -> Result<String, error::Error> {
let mut meta = if meta.is_empty() { let mut meta = if meta.is_empty() {
Value::Object(serde_json::Map::new()) Value::Object(serde_json::Map::new())
} else { } else {
@ -57,10 +58,15 @@ pub fn insert_vault_name_to_json_meta(meta: &str, vault_name: &str) -> Result<St
}; };
if let Some(meta_obj) = meta.as_object_mut() { if let Some(meta_obj) = meta.as_object_mut() {
meta_obj.insert(VAULT_NAME_META_KEY.to_owned(), Value::String(vault_name.to_owned())); meta_obj.insert(
VAULT_NAME_META_KEY.to_owned(),
Value::String(vault_name.to_owned()),
);
serde_json::to_string(meta_obj) serde_json::to_string(meta_obj)
} else { } else {
Err(error::Error::custom("Meta is expected to be a serialized JSON object")) Err(error::Error::custom(
"Meta is expected to be a serialized JSON object",
))
} }
} }
@ -76,16 +82,24 @@ pub fn remove_vault_name_from_json_meta(meta: &str) -> Result<String, error::Err
meta_obj.remove(VAULT_NAME_META_KEY); meta_obj.remove(VAULT_NAME_META_KEY);
serde_json::to_string(meta_obj) serde_json::to_string(meta_obj)
} else { } else {
Err(error::Error::custom("Meta is expected to be a serialized JSON object")) Err(error::Error::custom(
"Meta is expected to be a serialized JSON object",
))
} }
} }
impl VaultKeyFile { impl VaultKeyFile {
pub fn load<R>(reader: R) -> Result<Self, serde_json::Error> where R: Read { pub fn load<R>(reader: R) -> Result<Self, serde_json::Error>
where
R: Read,
{
serde_json::from_reader(reader) serde_json::from_reader(reader)
} }
pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error> where W: Write { pub fn write<W>(&self, writer: &mut W) -> Result<(), serde_json::Error>
where
W: Write,
{
serde_json::to_writer(writer, self) serde_json::to_writer(writer, self)
} }
} }
@ -103,9 +117,11 @@ impl VaultKeyMeta {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use json::{
insert_vault_name_to_json_meta, remove_vault_name_from_json_meta, Aes128Ctr, Cipher,
Crypto, Kdf, Pbkdf2, Prf, VaultKeyFile, Version,
};
use serde_json; use serde_json;
use json::{VaultKeyFile, Version, Crypto, Cipher, Aes128Ctr, Kdf, Pbkdf2, Prf,
insert_vault_name_to_json_meta, remove_vault_name_from_json_meta};
use std::num::NonZeroU32; use std::num::NonZeroU32;
lazy_static! { lazy_static! {
@ -153,8 +169,14 @@ mod test {
#[test] #[test]
fn vault_name_inserted_to_json_meta() { fn vault_name_inserted_to_json_meta() {
assert_eq!(insert_vault_name_to_json_meta(r#""#, "MyVault").unwrap(), r#"{"vault":"MyVault"}"#); assert_eq!(
assert_eq!(insert_vault_name_to_json_meta(r#"{"tags":["kalabala"]}"#, "MyVault").unwrap(), r#"{"tags":["kalabala"],"vault":"MyVault"}"#); insert_vault_name_to_json_meta(r#""#, "MyVault").unwrap(),
r#"{"vault":"MyVault"}"#
);
assert_eq!(
insert_vault_name_to_json_meta(r#"{"tags":["kalabala"]}"#, "MyVault").unwrap(),
r#"{"tags":["kalabala"],"vault":"MyVault"}"#
);
} }
#[test] #[test]
@ -165,8 +187,14 @@ mod test {
#[test] #[test]
fn vault_name_removed_from_json_meta() { fn vault_name_removed_from_json_meta() {
assert_eq!(remove_vault_name_from_json_meta(r#"{"vault":"MyVault"}"#).unwrap(), r#"{}"#); assert_eq!(
assert_eq!(remove_vault_name_from_json_meta(r#"{"tags":["kalabala"],"vault":"MyVault"}"#).unwrap(), r#"{"tags":["kalabala"]}"#); remove_vault_name_from_json_meta(r#"{"vault":"MyVault"}"#).unwrap(),
r#"{}"#
);
assert_eq!(
remove_vault_name_from_json_meta(r#"{"tags":["kalabala"],"vault":"MyVault"}"#).unwrap(),
r#"{"tags":["kalabala"]}"#
);
} }
#[test] #[test]

View File

@ -14,10 +14,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::fmt;
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::{Error as SerdeError, Visitor};
use super::Error; use super::Error;
use serde::{
de::{Error as SerdeError, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::fmt;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Version { pub enum Version {
@ -26,16 +28,20 @@ pub enum Version {
impl Serialize for Version { impl Serialize for Version {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer { where
S: Serializer,
{
match *self { match *self {
Version::V3 => serializer.serialize_u64(3) Version::V3 => serializer.serialize_u64(3),
} }
} }
} }
impl<'a> Deserialize<'a> for Version { impl<'a> Deserialize<'a> for Version {
fn deserialize<D>(deserializer: D) -> Result<Version, D::Error> fn deserialize<D>(deserializer: D) -> Result<Version, D::Error>
where D: Deserializer<'a> { where
D: Deserializer<'a>,
{
deserializer.deserialize_any(VersionVisitor) deserializer.deserialize_any(VersionVisitor)
} }
} }
@ -49,10 +55,13 @@ impl<'a> Visitor<'a> for VersionVisitor {
write!(formatter, "a valid key version identifier") write!(formatter, "a valid key version identifier")
} }
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> where E: SerdeError { fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: SerdeError,
{
match value { match value {
3 => Ok(Version::V3), 3 => Ok(Version::V3),
_ => Err(SerdeError::custom(Error::UnsupportedVersion)) _ => Err(SerdeError::custom(Error::UnsupportedVersion)),
} }
} }
} }

View File

@ -27,13 +27,13 @@ extern crate rustc_hex;
extern crate serde; extern crate serde;
extern crate serde_json; extern crate serde_json;
extern crate smallvec; extern crate smallvec;
extern crate tempdir;
extern crate time; extern crate time;
extern crate tiny_keccak; extern crate tiny_keccak;
extern crate tempdir;
extern crate parity_crypto as crypto;
extern crate ethereum_types; extern crate ethereum_types;
extern crate ethkey as _ethkey; extern crate ethkey as _ethkey;
extern crate parity_crypto as crypto;
extern crate parity_wordlist; extern crate parity_wordlist;
#[macro_use] #[macro_use]
@ -60,18 +60,20 @@ mod presale;
mod random; mod random;
mod secret_store; mod secret_store;
pub use self::account::{SafeAccount, Crypto}; pub use self::{
pub use self::error::Error; account::{Crypto, SafeAccount},
pub use self::ethstore::{EthStore, EthMultiStore}; error::Error,
pub use self::import::{import_account, import_accounts, read_geth_accounts}; ethstore::{EthMultiStore, EthStore},
pub use self::json::OpaqueKeyFile as KeyFile; import::{import_account, import_accounts, read_geth_accounts},
pub use self::presale::PresaleWallet; json::OpaqueKeyFile as KeyFile,
pub use self::secret_store::{ parity_wordlist::random_phrase,
SecretVaultRef, StoreAccountRef, SimpleSecretStore, SecretStore, presale::PresaleWallet,
Derivation, IndexDerivation, random::random_string,
secret_store::{
Derivation, IndexDerivation, SecretStore, SecretVaultRef, SimpleSecretStore,
StoreAccountRef,
},
}; };
pub use self::random::random_string;
pub use self::parity_wordlist::random_phrase;
/// An opaque wrapper for secret. /// An opaque wrapper for secret.
pub struct OpaqueSecret(::ethkey::Secret); pub struct OpaqueSecret(::ethkey::Secret);

View File

@ -14,13 +14,11 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::fs; use crypto::{self, pbkdf2, Keccak256};
use std::num::NonZeroU32; use ethkey::{Address, KeyPair, Password, Secret};
use std::path::Path;
use json; use json;
use ethkey::{Address, Secret, KeyPair, Password}; use std::{fs, num::NonZeroU32, path::Path};
use crypto::{Keccak256, pbkdf2}; use Error;
use {crypto, Error};
/// Pre-sale wallet. /// Pre-sale wallet.
pub struct PresaleWallet { pub struct PresaleWallet {
@ -47,10 +45,13 @@ impl From<json::PresaleWallet> for PresaleWallet {
impl PresaleWallet { impl PresaleWallet {
/// Open a pre-sale wallet. /// Open a pre-sale wallet.
pub fn open<P>(path: P) -> Result<Self, Error> where P: AsRef<Path> { pub fn open<P>(path: P) -> Result<Self, Error>
where
P: AsRef<Path>,
{
let file = fs::File::open(path)?; let file = fs::File::open(path)?;
let presale = json::PresaleWallet::load(file) let presale =
.map_err(|e| Error::InvalidKeyFile(format!("{}", e)))?; json::PresaleWallet::load(file).map_err(|e| Error::InvalidKeyFile(format!("{}", e)))?;
Ok(PresaleWallet::from(presale)) Ok(PresaleWallet::from(presale))
} }
@ -63,14 +64,15 @@ impl PresaleWallet {
pbkdf2::sha256(iter, salt, sec, &mut derived_key); pbkdf2::sha256(iter, salt, sec, &mut derived_key);
let mut key = vec![0; self.ciphertext.len()]; let mut key = vec![0; self.ciphertext.len()];
let len = crypto::aes::decrypt_128_cbc(&derived_key[0..16], &self.iv, &self.ciphertext, &mut key) let len =
crypto::aes::decrypt_128_cbc(&derived_key[0..16], &self.iv, &self.ciphertext, &mut key)
.map_err(|_| Error::InvalidPassword)?; .map_err(|_| Error::InvalidPassword)?;
let unpadded = &key[..len]; let unpadded = &key[..len];
let secret = Secret::from_unsafe_slice(&unpadded.keccak256())?; let secret = Secret::from_unsafe_slice(&unpadded.keccak256())?;
if let Ok(kp) = KeyPair::from_secret(secret) { if let Ok(kp) = KeyPair::from_secret(secret) {
if kp.address() == self.address { if kp.address() == self.address {
return Ok(kp) return Ok(kp);
} }
} }

View File

@ -14,10 +14,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use rand::{Rng, OsRng}; use rand::{OsRng, Rng};
pub trait Random { pub trait Random {
fn random() -> Self where Self: Sized; fn random() -> Self
where
Self: Sized;
} }
impl Random for [u8; 16] { impl Random for [u8; 16] {

View File

@ -14,13 +14,15 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::cmp::Ordering;
use ethkey::{Address, Message, Signature, Secret, Password, Public};
use Error;
use json::{Uuid, OpaqueKeyFile};
use ethereum_types::H256; use ethereum_types::H256;
use ethkey::{Address, Message, Password, Public, Secret, Signature};
use json::{OpaqueKeyFile, Uuid};
use std::{
cmp::Ordering,
hash::{Hash, Hasher},
path::PathBuf,
};
use Error;
use OpaqueSecret; use OpaqueSecret;
/// Key directory reference /// Key directory reference
@ -43,7 +45,11 @@ pub struct StoreAccountRef {
impl PartialOrd for StoreAccountRef { impl PartialOrd for StoreAccountRef {
fn partial_cmp(&self, other: &StoreAccountRef) -> Option<Ordering> { fn partial_cmp(&self, other: &StoreAccountRef) -> Option<Ordering> {
Some(self.address.cmp(&other.address).then_with(|| self.vault.cmp(&other.vault))) Some(
self.address
.cmp(&other.address)
.then_with(|| self.vault.cmp(&other.vault)),
)
} }
} }
@ -56,25 +62,72 @@ impl ::std::borrow::Borrow<Address> for StoreAccountRef {
/// Simple Secret Store API /// Simple Secret Store API
pub trait SimpleSecretStore: Send + Sync { pub trait SimpleSecretStore: Send + Sync {
/// Inserts new accounts to the store (or vault) with given password. /// Inserts new accounts to the store (or vault) with given password.
fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &Password) -> Result<StoreAccountRef, Error>; fn insert_account(
&self,
vault: SecretVaultRef,
secret: Secret,
password: &Password,
) -> Result<StoreAccountRef, Error>;
/// Inserts new derived account to the store (or vault) with given password. /// Inserts new derived account to the store (or vault) with given password.
fn insert_derived(&self, vault: SecretVaultRef, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation) -> Result<StoreAccountRef, Error>; fn insert_derived(
&self,
vault: SecretVaultRef,
account_ref: &StoreAccountRef,
password: &Password,
derivation: Derivation,
) -> Result<StoreAccountRef, Error>;
/// Changes accounts password. /// Changes accounts password.
fn change_password(&self, account: &StoreAccountRef, old_password: &Password, new_password: &Password) -> Result<(), Error>; fn change_password(
&self,
account: &StoreAccountRef,
old_password: &Password,
new_password: &Password,
) -> Result<(), Error>;
/// Exports key details for account. /// Exports key details for account.
fn export_account(&self, account: &StoreAccountRef, password: &Password) -> Result<OpaqueKeyFile, Error>; fn export_account(
&self,
account: &StoreAccountRef,
password: &Password,
) -> Result<OpaqueKeyFile, Error>;
/// Entirely removes account from the store and underlying storage. /// Entirely removes account from the store and underlying storage.
fn remove_account(&self, account: &StoreAccountRef, password: &Password) -> Result<(), Error>; fn remove_account(&self, account: &StoreAccountRef, password: &Password) -> Result<(), Error>;
/// Generates new derived account. /// Generates new derived account.
fn generate_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation) -> Result<Address, Error>; fn generate_derived(
&self,
account_ref: &StoreAccountRef,
password: &Password,
derivation: Derivation,
) -> Result<Address, Error>;
/// Sign a message with given account. /// Sign a message with given account.
fn sign(&self, account: &StoreAccountRef, password: &Password, message: &Message) -> Result<Signature, Error>; fn sign(
&self,
account: &StoreAccountRef,
password: &Password,
message: &Message,
) -> Result<Signature, Error>;
/// Sign a message with derived account. /// Sign a message with derived account.
fn sign_derived(&self, account_ref: &StoreAccountRef, password: &Password, derivation: Derivation, message: &Message) -> Result<Signature, Error>; fn sign_derived(
&self,
account_ref: &StoreAccountRef,
password: &Password,
derivation: Derivation,
message: &Message,
) -> Result<Signature, Error>;
/// Decrypt a messages with given account. /// Decrypt a messages with given account.
fn decrypt(&self, account: &StoreAccountRef, password: &Password, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error>; fn decrypt(
&self,
account: &StoreAccountRef,
password: &Password,
shared_mac: &[u8],
message: &[u8],
) -> Result<Vec<u8>, Error>;
/// Agree on shared key. /// Agree on shared key.
fn agree(&self, account: &StoreAccountRef, password: &Password, other: &Public) -> Result<Secret, Error>; fn agree(
&self,
account: &StoreAccountRef,
password: &Password,
other: &Public,
) -> Result<Secret, Error>;
/// Returns all accounts in this secret store. /// Returns all accounts in this secret store.
fn accounts(&self) -> Result<Vec<StoreAccountRef>, Error>; fn accounts(&self) -> Result<Vec<StoreAccountRef>, Error>;
@ -95,7 +148,11 @@ pub trait SimpleSecretStore: Send + Sync {
/// Change vault password /// Change vault password
fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error>; fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error>;
/// Cnage account' vault /// Cnage account' vault
fn change_account_vault(&self, vault: SecretVaultRef, account: StoreAccountRef) -> Result<StoreAccountRef, Error>; fn change_account_vault(
&self,
vault: SecretVaultRef,
account: StoreAccountRef,
) -> Result<StoreAccountRef, Error>;
/// Get vault metadata string. /// Get vault metadata string.
fn get_vault_meta(&self, name: &str) -> Result<String, Error>; fn get_vault_meta(&self, name: &str) -> Result<String, Error>;
/// Set vault metadata string. /// Set vault metadata string.
@ -104,21 +161,46 @@ pub trait SimpleSecretStore: Send + Sync {
/// Secret Store API /// Secret Store API
pub trait SecretStore: SimpleSecretStore { pub trait SecretStore: SimpleSecretStore {
/// Returns a raw opaque Secret that can be later used to sign a message. /// Returns a raw opaque Secret that can be later used to sign a message.
fn raw_secret(&self, account: &StoreAccountRef, password: &Password) -> Result<OpaqueSecret, Error>; fn raw_secret(
&self,
account: &StoreAccountRef,
password: &Password,
) -> Result<OpaqueSecret, Error>;
/// Signs a message with raw secret. /// Signs a message with raw secret.
fn sign_with_secret(&self, secret: &OpaqueSecret, message: &Message) -> Result<Signature, Error> { fn sign_with_secret(
&self,
secret: &OpaqueSecret,
message: &Message,
) -> Result<Signature, Error> {
Ok(::ethkey::sign(&secret.0, message)?) Ok(::ethkey::sign(&secret.0, message)?)
} }
/// Imports presale wallet /// Imports presale wallet
fn import_presale(&self, vault: SecretVaultRef, json: &[u8], password: &Password) -> Result<StoreAccountRef, Error>; fn import_presale(
&self,
vault: SecretVaultRef,
json: &[u8],
password: &Password,
) -> Result<StoreAccountRef, Error>;
/// Imports existing JSON wallet /// Imports existing JSON wallet
fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &Password, gen_id: bool) -> Result<StoreAccountRef, Error>; fn import_wallet(
&self,
vault: SecretVaultRef,
json: &[u8],
password: &Password,
gen_id: bool,
) -> Result<StoreAccountRef, Error>;
/// Copies account between stores and vaults. /// Copies account between stores and vaults.
fn copy_account(&self, new_store: &SimpleSecretStore, new_vault: SecretVaultRef, account: &StoreAccountRef, password: &Password, new_password: &Password) -> Result<(), Error>; fn copy_account(
&self,
new_store: &SimpleSecretStore,
new_vault: SecretVaultRef,
account: &StoreAccountRef,
password: &Password,
new_password: &Password,
) -> Result<(), Error>;
/// Checks if password matches given account. /// Checks if password matches given account.
fn test_password(&self, account: &StoreAccountRef, password: &Password) -> Result<bool, Error>; fn test_password(&self, account: &StoreAccountRef, password: &Password) -> Result<bool, Error>;
@ -142,7 +224,12 @@ pub trait SecretStore: SimpleSecretStore {
/// Lists all found geth accounts. /// Lists all found geth accounts.
fn list_geth_accounts(&self, testnet: bool) -> Vec<Address>; fn list_geth_accounts(&self, testnet: bool) -> Vec<Address>;
/// Imports geth accounts to the store/vault. /// Imports geth accounts to the store/vault.
fn import_geth_accounts(&self, vault: SecretVaultRef, desired: Vec<Address>, testnet: bool) -> Result<Vec<StoreAccountRef>, Error>; fn import_geth_accounts(
&self,
vault: SecretVaultRef,
desired: Vec<Address>,
testnet: bool,
) -> Result<Vec<StoreAccountRef>, Error>;
} }
impl StoreAccountRef { impl StoreAccountRef {

View File

@ -14,14 +14,16 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
extern crate rand;
extern crate ethstore; extern crate ethstore;
extern crate rand;
mod util; mod util;
use ethstore::{EthStore, SimpleSecretStore, SecretVaultRef, StoreAccountRef}; use ethstore::{
use ethstore::ethkey::{Random, Generator, Secret, KeyPair, verify_address}; accounts_dir::RootDiskDirectory,
use ethstore::accounts_dir::RootDiskDirectory; ethkey::{verify_address, Generator, KeyPair, Random, Secret},
EthStore, SecretVaultRef, SimpleSecretStore, StoreAccountRef,
};
use util::TransientDir; use util::TransientDir;
#[test] #[test]
@ -46,9 +48,13 @@ fn secret_store_create_account() {
let dir = TransientDir::create().unwrap(); let dir = TransientDir::create().unwrap();
let store = EthStore::open(Box::new(dir)).unwrap(); let store = EthStore::open(Box::new(dir)).unwrap();
assert_eq!(store.accounts().unwrap().len(), 0); assert_eq!(store.accounts().unwrap().len(), 0);
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok()); assert!(store
.insert_account(SecretVaultRef::Root, random_secret(), &"".into())
.is_ok());
assert_eq!(store.accounts().unwrap().len(), 1); assert_eq!(store.accounts().unwrap().len(), 1);
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok()); assert!(store
.insert_account(SecretVaultRef::Root, random_secret(), &"".into())
.is_ok());
assert_eq!(store.accounts().unwrap().len(), 2); assert_eq!(store.accounts().unwrap().len(), 2);
} }
@ -56,31 +62,49 @@ fn secret_store_create_account() {
fn secret_store_sign() { fn secret_store_sign() {
let dir = TransientDir::create().unwrap(); let dir = TransientDir::create().unwrap();
let store = EthStore::open(Box::new(dir)).unwrap(); let store = EthStore::open(Box::new(dir)).unwrap();
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok()); assert!(store
.insert_account(SecretVaultRef::Root, random_secret(), &"".into())
.is_ok());
let accounts = store.accounts().unwrap(); let accounts = store.accounts().unwrap();
assert_eq!(accounts.len(), 1); assert_eq!(accounts.len(), 1);
assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_ok()); assert!(store
assert!(store.sign(&accounts[0], &"1".into(), &Default::default()).is_err()); .sign(&accounts[0], &"".into(), &Default::default())
.is_ok());
assert!(store
.sign(&accounts[0], &"1".into(), &Default::default())
.is_err());
} }
#[test] #[test]
fn secret_store_change_password() { fn secret_store_change_password() {
let dir = TransientDir::create().unwrap(); let dir = TransientDir::create().unwrap();
let store = EthStore::open(Box::new(dir)).unwrap(); let store = EthStore::open(Box::new(dir)).unwrap();
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok()); assert!(store
.insert_account(SecretVaultRef::Root, random_secret(), &"".into())
.is_ok());
let accounts = store.accounts().unwrap(); let accounts = store.accounts().unwrap();
assert_eq!(accounts.len(), 1); assert_eq!(accounts.len(), 1);
assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_ok()); assert!(store
assert!(store.change_password(&accounts[0], &"".into(), &"1".into()).is_ok()); .sign(&accounts[0], &"".into(), &Default::default())
assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_err()); .is_ok());
assert!(store.sign(&accounts[0], &"1".into(), &Default::default()).is_ok()); assert!(store
.change_password(&accounts[0], &"".into(), &"1".into())
.is_ok());
assert!(store
.sign(&accounts[0], &"".into(), &Default::default())
.is_err());
assert!(store
.sign(&accounts[0], &"1".into(), &Default::default())
.is_ok());
} }
#[test] #[test]
fn secret_store_remove_account() { fn secret_store_remove_account() {
let dir = TransientDir::create().unwrap(); let dir = TransientDir::create().unwrap();
let store = EthStore::open(Box::new(dir)).unwrap(); let store = EthStore::open(Box::new(dir)).unwrap();
assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok()); assert!(store
.insert_account(SecretVaultRef::Root, random_secret(), &"".into())
.is_ok());
let accounts = store.accounts().unwrap(); let accounts = store.accounts().unwrap();
assert_eq!(accounts.len(), 1); assert_eq!(accounts.len(), 1);
assert!(store.remove_account(&accounts[0], &"".into()).is_ok()); assert!(store.remove_account(&accounts[0], &"".into()).is_ok());
@ -113,36 +137,55 @@ fn ciphertext_path() -> &'static str {
fn secret_store_laod_geth_files() { fn secret_store_laod_geth_files() {
let dir = RootDiskDirectory::at(test_path()); let dir = RootDiskDirectory::at(test_path());
let store = EthStore::open(Box::new(dir)).unwrap(); let store = EthStore::open(Box::new(dir)).unwrap();
assert_eq!(store.accounts().unwrap(), vec![ assert_eq!(
store.accounts().unwrap(),
vec![
StoreAccountRef::root("3f49624084b67849c7b4e805c5988c21a430f9d9".into()), StoreAccountRef::root("3f49624084b67849c7b4e805c5988c21a430f9d9".into()),
StoreAccountRef::root("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".into()), StoreAccountRef::root("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".into()),
StoreAccountRef::root("63121b431a52f8043c16fcf0d1df9cb7b5f66649".into()), StoreAccountRef::root("63121b431a52f8043c16fcf0d1df9cb7b5f66649".into()),
]); ]
);
} }
#[test] #[test]
fn secret_store_load_pat_files() { fn secret_store_load_pat_files() {
let dir = RootDiskDirectory::at(pat_path()); let dir = RootDiskDirectory::at(pat_path());
let store = EthStore::open(Box::new(dir)).unwrap(); let store = EthStore::open(Box::new(dir)).unwrap();
assert_eq!(store.accounts().unwrap(), vec![ assert_eq!(
store.accounts().unwrap(),
vec![
StoreAccountRef::root("3f49624084b67849c7b4e805c5988c21a430f9d9".into()), StoreAccountRef::root("3f49624084b67849c7b4e805c5988c21a430f9d9".into()),
StoreAccountRef::root("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".into()), StoreAccountRef::root("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".into()),
]); ]
);
} }
#[test] #[test]
fn test_decrypting_files_with_short_ciphertext() { fn test_decrypting_files_with_short_ciphertext() {
// 31e9d1e6d844bd3a536800ef8d8be6a9975db509, 30 // 31e9d1e6d844bd3a536800ef8d8be6a9975db509, 30
let kp1 = KeyPair::from_secret("000081c29e8142bb6a81bef5a92bda7a8328a5c85bb2f9542e76f9b0f94fc018".parse().unwrap()).unwrap(); let kp1 = KeyPair::from_secret(
"000081c29e8142bb6a81bef5a92bda7a8328a5c85bb2f9542e76f9b0f94fc018"
.parse()
.unwrap(),
)
.unwrap();
// d1e64e5480bfaf733ba7d48712decb8227797a4e , 31 // d1e64e5480bfaf733ba7d48712decb8227797a4e , 31
let kp2 = KeyPair::from_secret("00fa7b3db73dc7dfdf8c5fbdb796d741e4488628c41fc4febd9160a866ba0f35".parse().unwrap()).unwrap(); let kp2 = KeyPair::from_secret(
"00fa7b3db73dc7dfdf8c5fbdb796d741e4488628c41fc4febd9160a866ba0f35"
.parse()
.unwrap(),
)
.unwrap();
let dir = RootDiskDirectory::at(ciphertext_path()); let dir = RootDiskDirectory::at(ciphertext_path());
let store = EthStore::open(Box::new(dir)).unwrap(); let store = EthStore::open(Box::new(dir)).unwrap();
let accounts = store.accounts().unwrap(); let accounts = store.accounts().unwrap();
assert_eq!(accounts, vec![ assert_eq!(
accounts,
vec![
StoreAccountRef::root("31e9d1e6d844bd3a536800ef8d8be6a9975db509".into()), StoreAccountRef::root("31e9d1e6d844bd3a536800ef8d8be6a9975db509".into()),
StoreAccountRef::root("d1e64e5480bfaf733ba7d48712decb8227797a4e".into()), StoreAccountRef::root("d1e64e5480bfaf733ba7d48712decb8227797a4e".into()),
]); ]
);
let message = Default::default(); let message = Default::default();

View File

@ -14,11 +14,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::path::PathBuf; use ethstore::{
use std::{env, fs}; accounts_dir::{KeyDirectory, RootDiskDirectory},
use rand::{Rng, OsRng}; Error, SafeAccount,
use ethstore::accounts_dir::{KeyDirectory, RootDiskDirectory}; };
use ethstore::{Error, SafeAccount}; use rand::{OsRng, Rng};
use std::{env, fs, path::PathBuf};
pub fn random_dir() -> PathBuf { pub fn random_dir() -> PathBuf {
let mut rng = OsRng::new().unwrap(); let mut rng = OsRng::new().unwrap();

View File

@ -19,9 +19,9 @@
extern crate ethereum_types; extern crate ethereum_types;
extern crate ethkey; extern crate ethkey;
use std::fmt;
use ethereum_types::U256; use ethereum_types::U256;
use ethkey::{Address, Signature}; use ethkey::{Address, Signature};
use std::fmt;
pub struct WalletInfo { pub struct WalletInfo {
pub address: Address, pub address: Address,
@ -86,8 +86,14 @@ impl HardwareWalletManager {
Err(Error::NoWallet) Err(Error::NoWallet)
} }
pub fn sign_transaction(&self, _address: &Address, _transaction: &TransactionInfo, _rlp_transaction: &[u8]) -> Result<Signature, Error> { pub fn sign_transaction(
Err(Error::NoWallet) } &self,
_address: &Address,
_transaction: &TransactionInfo,
_rlp_transaction: &[u8],
) -> Result<Signature, Error> {
Err(Error::NoWallet)
}
pub fn sign_message(&self, _address: &Address, _msg: &[u8]) -> Result<Signature, Error> { pub fn sign_message(&self, _address: &Address, _msg: &[u8]) -> Result<Signature, Error> {
Err(Error::NoWallet) Err(Error::NoWallet)

View File

@ -17,26 +17,31 @@
//! Ledger hardware wallet module. Supports Ledger Blue and Nano S. //! Ledger hardware wallet module. Supports Ledger Blue and Nano S.
//! See <https://github.com/LedgerHQ/blue-app-eth/blob/master/doc/ethapp.asc> for protocol details. //! See <https://github.com/LedgerHQ/blue-app-eth/blob/master/doc/ethapp.asc> for protocol details.
use std::cmp::min; use std::{
use std::str::FromStr; cmp::min,
use std::sync::Arc; fmt,
use std::time::{Duration, Instant}; str::FromStr,
use std::fmt; sync::Arc,
time::{Duration, Instant},
};
use ethereum_types::{H256, Address}; use super::{is_valid_hid_device, Device, DeviceDirection, KeyPath, Wallet, WalletInfo};
use ethereum_types::{Address, H256};
use ethkey::Signature; use ethkey::Signature;
use hidapi; use hidapi;
use libusb; use libusb;
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use semver::Version as FirmwareVersion; use semver::Version as FirmwareVersion;
use super::{WalletInfo, KeyPath, Device, DeviceDirection, Wallet, is_valid_hid_device};
const APDU_TAG: u8 = 0x05; const APDU_TAG: u8 = 0x05;
const APDU_CLA: u8 = 0xe0; const APDU_CLA: u8 = 0xe0;
const APDU_PAYLOAD_HEADER_LEN: usize = 7; const APDU_PAYLOAD_HEADER_LEN: usize = 7;
const ETH_DERIVATION_PATH_BE: [u8; 17] = [4, 0x80, 0, 0, 44, 0x80, 0, 0, 60, 0x80, 0, 0, 0, 0, 0, 0, 0]; // 44'/60'/0'/0 const ETH_DERIVATION_PATH_BE: [u8; 17] =
const ETC_DERIVATION_PATH_BE: [u8; 21] = [5, 0x80, 0, 0, 44, 0x80, 0, 0, 60, 0x80, 0x02, 0x73, 0xd0, 0x80, 0, 0, 0, 0, 0, 0, 0]; // 44'/60'/160720'/0'/0 [4, 0x80, 0, 0, 44, 0x80, 0, 0, 60, 0x80, 0, 0, 0, 0, 0, 0, 0]; // 44'/60'/0'/0
const ETC_DERIVATION_PATH_BE: [u8; 21] = [
5, 0x80, 0, 0, 44, 0x80, 0, 0, 60, 0x80, 0x02, 0x73, 0xd0, 0x80, 0, 0, 0, 0, 0, 0, 0,
]; // 44'/60'/160720'/0'/0
/// Ledger vendor ID /// Ledger vendor ID
const LEDGER_VID: u16 = 0x2c97; const LEDGER_VID: u16 = 0x2c97;
@ -48,8 +53,10 @@ const MAX_CHUNK_SIZE: usize = 255;
const HID_PACKET_SIZE: usize = 64 + HID_PREFIX_ZERO; const HID_PACKET_SIZE: usize = 64 + HID_PREFIX_ZERO;
#[cfg(windows)] const HID_PREFIX_ZERO: usize = 1; #[cfg(windows)]
#[cfg(not(windows))] const HID_PREFIX_ZERO: usize = 0; const HID_PREFIX_ZERO: usize = 1;
#[cfg(not(windows))]
const HID_PREFIX_ZERO: usize = 0;
mod commands { mod commands {
pub const GET_APP_CONFIGURATION: u8 = 0x06; pub const GET_APP_CONFIGURATION: u8 = 0x06;
@ -91,8 +98,11 @@ impl fmt::Display for Error {
Error::UserCancel => write!(f, "Operation has been cancelled"), Error::UserCancel => write!(f, "Operation has been cancelled"),
Error::Impossible => write!(f, "Placeholder error"), Error::Impossible => write!(f, "Placeholder error"),
Error::NoDeviceArrived => write!(f, "No device arrived"), Error::NoDeviceArrived => write!(f, "No device arrived"),
Error::NoDeviceLeft=> write!(f, "No device left"), Error::NoDeviceLeft => write!(f, "No device left"),
Error::InvalidDevice => write!(f, "Device with non-supported product ID or vendor ID was detected"), Error::InvalidDevice => write!(
f,
"Device with non-supported product ID or vendor ID was detected"
),
} }
} }
} }
@ -141,22 +151,46 @@ impl Manager {
// * APDU_LENGTH (1 byte) // * APDU_LENGTH (1 byte)
// * APDU_Payload (Variable) // * APDU_Payload (Variable)
// //
fn write(handle: &hidapi::HidDevice, command: u8, p1: u8, p2: u8, data: &[u8]) -> Result<(), Error> { fn write(
handle: &hidapi::HidDevice,
command: u8,
p1: u8,
p2: u8,
data: &[u8],
) -> Result<(), Error> {
let data_len = data.len(); let data_len = data.len();
let mut offset = 0; let mut offset = 0;
let mut sequence_number = 0; let mut sequence_number = 0;
let mut hid_chunk = [0_u8; HID_PACKET_SIZE]; let mut hid_chunk = [0_u8; HID_PACKET_SIZE];
while sequence_number == 0 || offset < data_len { while sequence_number == 0 || offset < data_len {
let header = if sequence_number == 0 { LEDGER_TRANSPORT_HEADER_LEN + APDU_PAYLOAD_HEADER_LEN } else { LEDGER_TRANSPORT_HEADER_LEN }; let header = if sequence_number == 0 {
LEDGER_TRANSPORT_HEADER_LEN + APDU_PAYLOAD_HEADER_LEN
} else {
LEDGER_TRANSPORT_HEADER_LEN
};
let size = min(64 - header, data_len - offset); let size = min(64 - header, data_len - offset);
{ {
let chunk = &mut hid_chunk[HID_PREFIX_ZERO..]; let chunk = &mut hid_chunk[HID_PREFIX_ZERO..];
chunk[0..5].copy_from_slice(&[0x01, 0x01, APDU_TAG, (sequence_number >> 8) as u8, (sequence_number & 0xff) as u8 ]); chunk[0..5].copy_from_slice(&[
0x01,
0x01,
APDU_TAG,
(sequence_number >> 8) as u8,
(sequence_number & 0xff) as u8,
]);
if sequence_number == 0 { if sequence_number == 0 {
let data_len = data.len() + 5; let data_len = data.len() + 5;
chunk[5..12].copy_from_slice(&[(data_len >> 8) as u8, (data_len & 0xff) as u8, APDU_CLA, command, p1, p2, data.len() as u8]); chunk[5..12].copy_from_slice(&[
(data_len >> 8) as u8,
(data_len & 0xff) as u8,
APDU_CLA,
command,
p1,
p2,
data.len() as u8,
]);
} }
chunk[header..header + size].copy_from_slice(&data[offset..offset + size]); chunk[header..header + size].copy_from_slice(&data[offset..offset + size]);
@ -199,7 +233,11 @@ impl Manager {
let mut chunk: [u8; HID_PACKET_SIZE] = [0; HID_PACKET_SIZE]; let mut chunk: [u8; HID_PACKET_SIZE] = [0; HID_PACKET_SIZE];
let chunk_size = handle.read(&mut chunk)?; let chunk_size = handle.read(&mut chunk)?;
trace!(target: "hw", "Ledger read {:?}", &chunk[..]); trace!(target: "hw", "Ledger read {:?}", &chunk[..]);
if chunk_size < LEDGER_TRANSPORT_HEADER_LEN || chunk[0] != 0x01 || chunk[1] != 0x01 || chunk[2] != APDU_TAG { if chunk_size < LEDGER_TRANSPORT_HEADER_LEN
|| chunk[0] != 0x01
|| chunk[1] != 0x01
|| chunk[2] != APDU_TAG
{
return Err(Error::Protocol("Unexpected chunk header")); return Err(Error::Protocol("Unexpected chunk header"));
} }
let seq = (chunk[3] as usize) << 8 | (chunk[4] as usize); let seq = (chunk[3] as usize) << 8 | (chunk[4] as usize);
@ -225,28 +263,38 @@ impl Manager {
if message.len() < 2 { if message.len() < 2 {
return Err(Error::Protocol("No status word")); return Err(Error::Protocol("No status word"));
} }
let status = (message[message.len() - 2] as usize) << 8 | (message[message.len() - 1] as usize); let status =
(message[message.len() - 2] as usize) << 8 | (message[message.len() - 1] as usize);
debug!(target: "hw", "Read status {:x}", status); debug!(target: "hw", "Read status {:x}", status);
match status { match status {
0x6700 => Err(Error::Protocol("Incorrect length")), 0x6700 => Err(Error::Protocol("Incorrect length")),
0x6982 => Err(Error::Protocol("Security status not satisfied (Canceled by user)")), 0x6982 => Err(Error::Protocol(
"Security status not satisfied (Canceled by user)",
)),
0x6a80 => Err(Error::Protocol("Invalid data")), 0x6a80 => Err(Error::Protocol("Invalid data")),
0x6a82 => Err(Error::Protocol("File not found")), 0x6a82 => Err(Error::Protocol("File not found")),
0x6a85 => Err(Error::UserCancel), 0x6a85 => Err(Error::UserCancel),
0x6b00 => Err(Error::Protocol("Incorrect parameters")), 0x6b00 => Err(Error::Protocol("Incorrect parameters")),
0x6d00 => Err(Error::Protocol("Not implemented. Make sure the Ledger Ethereum Wallet app is running.")), 0x6d00 => Err(Error::Protocol(
"Not implemented. Make sure the Ledger Ethereum Wallet app is running.",
)),
0x6faa => Err(Error::Protocol("Your Ledger need to be unplugged")), 0x6faa => Err(Error::Protocol("Your Ledger need to be unplugged")),
0x6f00...0x6fff => Err(Error::Protocol("Internal error")), 0x6f00...0x6fff => Err(Error::Protocol("Internal error")),
0x9000 => Ok(()), 0x9000 => Ok(()),
_ => Err(Error::Protocol("Unknown error")), _ => Err(Error::Protocol("Unknown error")),
}?; }?;
let new_len = message.len() - 2; let new_len = message.len() - 2;
message.truncate(new_len); message.truncate(new_len);
Ok(message) Ok(message)
} }
fn send_apdu(handle: &hidapi::HidDevice, command: u8, p1: u8, p2: u8, data: &[u8]) -> Result<Vec<u8>, Error> { fn send_apdu(
handle: &hidapi::HidDevice,
command: u8,
p1: u8,
p2: u8,
data: &[u8],
) -> Result<Vec<u8>, Error> {
Self::write(&handle, command, p1, p2, data)?; Self::write(&handle, command, p1, p2, data)?;
Self::read(&handle) Self::read(&handle)
} }
@ -256,7 +304,11 @@ impl Manager {
if ver.len() != 4 { if ver.len() != 4 {
return Err(Error::Protocol("Version packet size mismatch")); return Err(Error::Protocol("Version packet size mismatch"));
} }
Ok(FirmwareVersion::new(ver[1].into(), ver[2].into(), ver[3].into())) Ok(FirmwareVersion::new(
ver[1].into(),
ver[2].into(),
ver[3].into(),
))
} }
fn get_derivation_path(&self) -> &[u8] { fn get_derivation_path(&self) -> &[u8] {
@ -266,21 +318,31 @@ impl Manager {
} }
} }
fn signer_helper(&self, address: &Address, data: &[u8], command: u8) -> Result<Signature, Error> { fn signer_helper(
&self,
address: &Address,
data: &[u8],
command: u8,
) -> Result<Signature, Error> {
let usb = self.usb.lock(); let usb = self.usb.lock();
let devices = self.devices.read(); let devices = self.devices.read();
let device = devices.iter().find(|d| &d.info.address == address).ok_or(Error::KeyNotFound)?; let device = devices
.iter()
.find(|d| &d.info.address == address)
.ok_or(Error::KeyNotFound)?;
let handle = self.open_path(|| usb.open_path(&device.path))?; let handle = self.open_path(|| usb.open_path(&device.path))?;
// Signing personal messages are only support by Ledger firmware version 1.0.8 or newer // Signing personal messages are only support by Ledger firmware version 1.0.8 or newer
if command == commands::SIGN_ETH_PERSONAL_MESSAGE { if command == commands::SIGN_ETH_PERSONAL_MESSAGE {
let version = Self::get_firmware_version(&handle)?; let version = Self::get_firmware_version(&handle)?;
if version < FirmwareVersion::new(1, 0, 8) { if version < FirmwareVersion::new(1, 0, 8) {
return Err(Error::Protocol("Signing personal messages with Ledger requires version 1.0.8")); return Err(Error::Protocol(
"Signing personal messages with Ledger requires version 1.0.8",
));
} }
} }
let mut chunk= [0_u8; MAX_CHUNK_SIZE]; let mut chunk = [0_u8; MAX_CHUNK_SIZE];
let derivation_path = self.get_derivation_path(); let derivation_path = self.get_derivation_path();
// Copy the address of the key (only done once) // Copy the address of the key (only done once)
@ -326,7 +388,11 @@ impl<'a> Wallet<'a> for Manager {
type Error = Error; type Error = Error;
type Transaction = &'a [u8]; type Transaction = &'a [u8];
fn sign_transaction(&self, address: &Address, transaction: Self::Transaction) -> Result<Signature, Self::Error> { fn sign_transaction(
&self,
address: &Address,
transaction: Self::Transaction,
) -> Result<Signature, Self::Error> {
self.signer_helper(address, transaction, commands::SIGN_ETH_TRANSACTION) self.signer_helper(address, transaction, commands::SIGN_ETH_TRANSACTION)
} }
@ -346,10 +412,12 @@ impl<'a> Wallet<'a> for Manager {
return Err(Error::NoDeviceArrived); return Err(Error::NoDeviceArrived);
} }
let detected_devices = devices.iter() let detected_devices = devices
.filter(|&d| is_valid_ledger(d.vendor_id, d.product_id) && .iter()
is_valid_hid_device(d.usage_page, d.interface_number) .filter(|&d| {
) is_valid_ledger(d.vendor_id, d.product_id)
&& is_valid_hid_device(d.usage_page, d.interface_number)
})
.fold(Vec::new(), |mut v, d| { .fold(Vec::new(), |mut v, d| {
match self.read_device(&usb, &d) { match self.read_device(&usb, &d) {
Ok(info) => { Ok(info) => {
@ -382,14 +450,26 @@ impl<'a> Wallet<'a> for Manager {
} }
} }
fn read_device(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result<Device, Self::Error> { fn read_device(
&self,
usb: &hidapi::HidApi,
dev_info: &hidapi::HidDeviceInfo,
) -> Result<Device, Self::Error> {
let handle = self.open_path(|| usb.open_path(&dev_info.path))?; let handle = self.open_path(|| usb.open_path(&dev_info.path))?;
let manufacturer = dev_info.manufacturer_string.clone().unwrap_or_else(|| "Unknown".to_owned()); let manufacturer = dev_info
let name = dev_info.product_string.clone().unwrap_or_else(|| "Unknown".to_owned()); .manufacturer_string
let serial = dev_info.serial_number.clone().unwrap_or_else(|| "Unknown".to_owned()); .clone()
.unwrap_or_else(|| "Unknown".to_owned());
let name = dev_info
.product_string
.clone()
.unwrap_or_else(|| "Unknown".to_owned());
let serial = dev_info
.serial_number
.clone()
.unwrap_or_else(|| "Unknown".to_owned());
match self.get_address(&handle) { match self.get_address(&handle) {
Ok(Some(addr)) => { Ok(Some(addr)) => Ok(Device {
Ok(Device {
path: dev_info.path.clone(), path: dev_info.path.clone(),
info: WalletInfo { info: WalletInfo {
name, name,
@ -397,8 +477,7 @@ impl<'a> Wallet<'a> for Manager {
serial, serial,
address: addr, address: addr,
}, },
}) }),
}
// This variant is not possible, but the trait forces this return type // This variant is not possible, but the trait forces this return type
Ok(None) => Err(Error::Impossible), Ok(None) => Err(Error::Impossible),
Err(e) => Err(e), Err(e) => Err(e),
@ -415,7 +494,11 @@ impl<'a> Wallet<'a> for Manager {
} }
fn get_wallet(&self, address: &Address) -> Option<WalletInfo> { fn get_wallet(&self, address: &Address) -> Option<WalletInfo> {
self.devices.read().iter().find(|d| &d.info.address == address).map(|d| d.info.clone()) self.devices
.read()
.iter()
.find(|d| &d.info.address == address)
.map(|d| d.info.clone())
} }
fn get_address(&self, device: &hidapi::HidDevice) -> Result<Option<Address>, Self::Error> { fn get_address(&self, device: &hidapi::HidDevice) -> Result<Option<Address>, Self::Error> {
@ -426,8 +509,15 @@ impl<'a> Wallet<'a> for Manager {
let derivation_path = self.get_derivation_path(); let derivation_path = self.get_derivation_path();
let key_and_address = Self::send_apdu(device, commands::GET_ETH_PUBLIC_ADDRESS, 0, 0, derivation_path)?; let key_and_address = Self::send_apdu(
if key_and_address.len() != 107 { // 1 + 65 PK + 1 + 40 Addr (ascii-hex) device,
commands::GET_ETH_PUBLIC_ADDRESS,
0,
0,
derivation_path,
)?;
if key_and_address.len() != 107 {
// 1 + 65 PK + 1 + 40 Addr (ascii-hex)
return Err(Error::Protocol("Key packet size mismatch")); return Err(Error::Protocol("Key packet size mismatch"));
} }
let address_string = ::std::str::from_utf8(&key_and_address[67..107]) let address_string = ::std::str::from_utf8(&key_and_address[67..107])
@ -440,7 +530,8 @@ impl<'a> Wallet<'a> for Manager {
} }
fn open_path<R, F>(&self, f: F) -> Result<R, Self::Error> fn open_path<R, F>(&self, f: F) -> Result<R, Self::Error>
where F: Fn() -> Result<R, &'static str> where
F: Fn() -> Result<R, &'static str>,
{ {
f().map_err(Into::into) f().map_err(Into::into)
} }
@ -452,7 +543,11 @@ pub fn is_valid_ledger(vendor_id: u16, product_id: u16) -> bool {
} }
/// Poll the device in maximum `max_polling_duration` if it doesn't succeed /// Poll the device in maximum `max_polling_duration` if it doesn't succeed
pub fn try_connect_polling(ledger: &Manager, max_polling_duration: &Duration, device_direction: DeviceDirection) -> bool { pub fn try_connect_polling(
ledger: &Manager,
max_polling_duration: &Duration,
device_direction: DeviceDirection,
) -> bool {
let start_time = Instant::now(); let start_time = Instant::now();
while start_time.elapsed() <= *max_polling_duration { while start_time.elapsed() <= *max_polling_duration {
if let Ok(num_devices) = ledger.update_devices(device_direction) { if let Ok(num_devices) = ledger.update_devices(device_direction) {
@ -465,9 +560,9 @@ pub fn try_connect_polling(ledger: &Manager, max_polling_duration: &Duration, de
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rustc_hex::FromHex;
use super::*; use super::*;
use ::HardwareWalletManager; use rustc_hex::FromHex;
use HardwareWalletManager;
/// This test can't be run without an actual ledger device connected with the `Ledger Wallet Ethereum application` running /// This test can't be run without an actual ledger device connected with the `Ledger Wallet Ethereum application` running
#[test] #[test]
@ -481,7 +576,8 @@ mod tests {
ledger.update_devices(DeviceDirection::Arrived).expect("No Ledger found, make sure you have a unlocked Ledger connected with the Ledger Wallet Ethereum running"); ledger.update_devices(DeviceDirection::Arrived).expect("No Ledger found, make sure you have a unlocked Ledger connected with the Ledger Wallet Ethereum running");
// Fetch the ethereum address of a connected ledger device // Fetch the ethereum address of a connected ledger device
let address = ledger.list_devices() let address = ledger
.list_devices()
.iter() .iter()
.filter(|d| d.manufacturer == "Ledger".to_string()) .filter(|d| d.manufacturer == "Ledger".to_string())
.nth(0) .nth(0)
@ -505,7 +601,8 @@ mod tests {
ledger.update_devices(DeviceDirection::Arrived).expect("No Ledger found, make sure you have a unlocked Ledger connected with the Ledger Wallet Ethereum running"); ledger.update_devices(DeviceDirection::Arrived).expect("No Ledger found, make sure you have a unlocked Ledger connected with the Ledger Wallet Ethereum running");
// Fetch the ethereum address of a connected ledger device // Fetch the ethereum address of a connected ledger device
let address = ledger.list_devices() let address = ledger
.list_devices()
.iter() .iter()
.filter(|d| d.manufacturer == "Ledger".to_string()) .filter(|d| d.manufacturer == "Ledger".to_string())
.nth(0) .nth(0)

View File

@ -28,15 +28,20 @@ extern crate protobuf;
extern crate semver; extern crate semver;
extern crate trezor_sys; extern crate trezor_sys;
#[macro_use] extern crate log; #[macro_use]
#[cfg(test)] extern crate rustc_hex; extern crate log;
#[cfg(test)]
extern crate rustc_hex;
mod ledger; mod ledger;
mod trezor; mod trezor;
use std::sync::{Arc, atomic, atomic::AtomicBool, Weak}; use std::{
use std::{fmt, time::Duration}; fmt,
use std::thread; sync::{atomic, atomic::AtomicBool, Arc, Weak},
thread,
time::Duration,
};
use ethereum_types::U256; use ethereum_types::U256;
use ethkey::{Address, Signature}; use ethkey::{Address, Signature};
@ -62,7 +67,11 @@ pub trait Wallet<'a> {
type Transaction; type Transaction;
/// Sign transaction data with wallet managing `address`. /// Sign transaction data with wallet managing `address`.
fn sign_transaction(&self, address: &Address, transaction: Self::Transaction) -> Result<Signature, Self::Error>; fn sign_transaction(
&self,
address: &Address,
transaction: Self::Transaction,
) -> Result<Signature, Self::Error>;
/// Set key derivation path for a chain. /// Set key derivation path for a chain.
fn set_key_path(&self, key_path: KeyPath); fn set_key_path(&self, key_path: KeyPath);
@ -72,7 +81,11 @@ pub trait Wallet<'a> {
fn update_devices(&self, device_direction: DeviceDirection) -> Result<usize, Self::Error>; fn update_devices(&self, device_direction: DeviceDirection) -> Result<usize, Self::Error>;
/// Read device info /// Read device info
fn read_device(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result<Device, Self::Error>; fn read_device(
&self,
usb: &hidapi::HidApi,
dev_info: &hidapi::HidDeviceInfo,
) -> Result<Device, Self::Error>;
/// List connected and acknowledged wallets /// List connected and acknowledged wallets
fn list_devices(&self) -> Vec<WalletInfo>; fn list_devices(&self) -> Vec<WalletInfo>;
@ -95,7 +108,8 @@ pub trait Wallet<'a> {
/// * <https://github.com/paritytech/hidapi-rs> /// * <https://github.com/paritytech/hidapi-rs>
/// * <https://github.com/rust-lang/libc> /// * <https://github.com/rust-lang/libc>
fn open_path<R, F>(&self, f: F) -> Result<R, Self::Error> fn open_path<R, F>(&self, f: F) -> Result<R, Self::Error>
where F: Fn() -> Result<R, &'static str>; where
F: Fn() -> Result<R, &'static str>;
} }
/// Hardware wallet error. /// Hardware wallet error.
@ -220,7 +234,9 @@ impl HardwareWalletManager {
/// Hardware wallet constructor /// Hardware wallet constructor
pub fn new() -> Result<Self, Error> { pub fn new() -> Result<Self, Error> {
let exiting = Arc::new(AtomicBool::new(false)); let exiting = Arc::new(AtomicBool::new(false));
let hidapi = Arc::new(Mutex::new(hidapi::HidApi::new().map_err(|e| Error::Hid(e.to_string().clone()))?)); let hidapi = Arc::new(Mutex::new(
hidapi::HidApi::new().map_err(|e| Error::Hid(e.to_string().clone()))?,
));
let ledger = ledger::Manager::new(hidapi.clone()); let ledger = ledger::Manager::new(hidapi.clone());
let trezor = trezor::Manager::new(hidapi.clone()); let trezor = trezor::Manager::new(hidapi.clone());
let usb_context = Arc::new(libusb::Context::new()?); let usb_context = Arc::new(libusb::Context::new()?);
@ -232,11 +248,13 @@ impl HardwareWalletManager {
// Subscribe to all vendor IDs (VIDs) and product IDs (PIDs) // Subscribe to all vendor IDs (VIDs) and product IDs (PIDs)
// This means that the `HardwareWalletManager` is responsible to validate the detected device // This means that the `HardwareWalletManager` is responsible to validate the detected device
usb_context.register_callback( usb_context.register_callback(
None, None, Some(HID_USB_DEVICE_CLASS), None,
None,
Some(HID_USB_DEVICE_CLASS),
Box::new(EventHandler::new( Box::new(EventHandler::new(
Arc::downgrade(&ledger), Arc::downgrade(&ledger),
Arc::downgrade(&trezor) Arc::downgrade(&trezor),
)) )),
)?; )?;
// Hardware event subscriber thread // Hardware event subscriber thread
@ -309,7 +327,12 @@ impl HardwareWalletManager {
} }
/// Sign transaction data with wallet managing `address`. /// Sign transaction data with wallet managing `address`.
pub fn sign_transaction(&self, address: &Address, t_info: &TransactionInfo, encoded_transaction: &[u8]) -> Result<Signature, Error> { pub fn sign_transaction(
&self,
address: &Address,
t_info: &TransactionInfo,
encoded_transaction: &[u8],
) -> Result<Signature, Error> {
if self.ledger.get_wallet(address).is_some() { if self.ledger.get_wallet(address).is_some() {
Ok(self.ledger.sign_transaction(address, encoded_transaction)?) Ok(self.ledger.sign_transaction(address, encoded_transaction)?)
} else if self.trezor.get_wallet(address).is_some() { } else if self.trezor.get_wallet(address).is_some() {
@ -323,7 +346,9 @@ impl HardwareWalletManager {
/// This is only applicable to Trezor because Ledger only appears as /// This is only applicable to Trezor because Ledger only appears as
/// a device when it is unlocked /// a device when it is unlocked
pub fn pin_matrix_ack(&self, path: &str, pin: &str) -> Result<bool, Error> { pub fn pin_matrix_ack(&self, path: &str, pin: &str) -> Result<bool, Error> {
self.trezor.pin_matrix_ack(path, pin).map_err(Error::TrezorDevice) self.trezor
.pin_matrix_ack(path, pin)
.map_err(Error::TrezorDevice)
} }
} }
@ -365,11 +390,19 @@ impl libusb::Hotplug for EventHandler {
// Version ID and Product ID are available // Version ID and Product ID are available
if let Ok((vid, pid)) = Self::extract_device_info(&device) { if let Ok((vid, pid)) = Self::extract_device_info(&device) {
if trezor::is_valid_trezor(vid, pid) { if trezor::is_valid_trezor(vid, pid) {
if !trezor::try_connect_polling(&trezor, &MAX_POLLING_DURATION, DeviceDirection::Arrived) { if !trezor::try_connect_polling(
&trezor,
&MAX_POLLING_DURATION,
DeviceDirection::Arrived,
) {
trace!(target: "hw", "Trezor device was detected but connection failed"); trace!(target: "hw", "Trezor device was detected but connection failed");
} }
} else if ledger::is_valid_ledger(vid, pid) { } else if ledger::is_valid_ledger(vid, pid) {
if !ledger::try_connect_polling(&ledger, &MAX_POLLING_DURATION, DeviceDirection::Arrived) { if !ledger::try_connect_polling(
&ledger,
&MAX_POLLING_DURATION,
DeviceDirection::Arrived,
) {
trace!(target: "hw", "Ledger device was detected but connection failed"); trace!(target: "hw", "Ledger device was detected but connection failed");
} }
} }
@ -383,11 +416,19 @@ impl libusb::Hotplug for EventHandler {
// Version ID and Product ID are available // Version ID and Product ID are available
if let Ok((vid, pid)) = Self::extract_device_info(&device) { if let Ok((vid, pid)) = Self::extract_device_info(&device) {
if trezor::is_valid_trezor(vid, pid) { if trezor::is_valid_trezor(vid, pid) {
if !trezor::try_connect_polling(&trezor, &MAX_POLLING_DURATION, DeviceDirection::Left) { if !trezor::try_connect_polling(
&trezor,
&MAX_POLLING_DURATION,
DeviceDirection::Left,
) {
trace!(target: "hw", "Trezor device was detected but disconnection failed"); trace!(target: "hw", "Trezor device was detected but disconnection failed");
} }
} else if ledger::is_valid_ledger(vid, pid) { } else if ledger::is_valid_ledger(vid, pid) {
if !ledger::try_connect_polling(&ledger, &MAX_POLLING_DURATION, DeviceDirection::Left) { if !ledger::try_connect_polling(
&ledger,
&MAX_POLLING_DURATION,
DeviceDirection::Left,
) {
trace!(target: "hw", "Ledger device was detected but disconnection failed"); trace!(target: "hw", "Ledger device was detected but disconnection failed");
} }
} }

View File

@ -19,19 +19,26 @@
//! and <https://github.com/trezor/trezor-common/blob/master/protob/protocol.md> //! and <https://github.com/trezor/trezor-common/blob/master/protob/protocol.md>
//! for protocol details. //! for protocol details.
use std::cmp::{min, max}; use std::{
use std::sync::Arc; cmp::{max, min},
use std::time::{Duration, Instant}; fmt,
use std::fmt; sync::Arc,
time::{Duration, Instant},
};
use ethereum_types::{U256, H256, Address}; use super::{
is_valid_hid_device, Device, DeviceDirection, KeyPath, TransactionInfo, Wallet, WalletInfo,
};
use ethereum_types::{Address, H256, U256};
use ethkey::Signature; use ethkey::Signature;
use hidapi; use hidapi;
use libusb; use libusb;
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use protobuf::{self, Message, ProtobufEnum}; use protobuf::{self, Message, ProtobufEnum};
use super::{DeviceDirection, WalletInfo, TransactionInfo, KeyPath, Wallet, Device, is_valid_hid_device}; use trezor_sys::messages::{
use trezor_sys::messages::{EthereumAddress, PinMatrixAck, MessageType, EthereumTxRequest, EthereumSignTx, EthereumGetAddress, EthereumTxAck, ButtonAck}; ButtonAck, EthereumAddress, EthereumGetAddress, EthereumSignTx, EthereumTxAck,
EthereumTxRequest, MessageType, PinMatrixAck,
};
/// Trezor v1 vendor ID /// Trezor v1 vendor ID
const TREZOR_VID: u16 = 0x534c; const TREZOR_VID: u16 = 0x534c;
@ -77,11 +84,18 @@ impl fmt::Display for Error {
Error::KeyNotFound => write!(f, "Key not found"), Error::KeyNotFound => write!(f, "Key not found"),
Error::UserCancel => write!(f, "Operation has been cancelled"), Error::UserCancel => write!(f, "Operation has been cancelled"),
Error::BadMessageType => write!(f, "Bad Message Type in RPC call"), Error::BadMessageType => write!(f, "Bad Message Type in RPC call"),
Error::LockedDevice(ref s) => write!(f, "Device is locked, needs PIN to perform operations: {}", s), Error::LockedDevice(ref s) => write!(
Error::NoSigningMessage=> write!(f, "Signing messages are not supported by Trezor"), f,
"Device is locked, needs PIN to perform operations: {}",
s
),
Error::NoSigningMessage => write!(f, "Signing messages are not supported by Trezor"),
Error::NoDeviceArrived => write!(f, "No device arrived"), Error::NoDeviceArrived => write!(f, "No device arrived"),
Error::NoDeviceLeft => write!(f, "No device left"), Error::NoDeviceLeft => write!(f, "No device left"),
Error::InvalidDevice => write!(f, "Device with non-supported product ID or vendor ID was detected"), Error::InvalidDevice => write!(
f,
"Device with non-supported product ID or vendor ID was detected"
),
} }
} }
} }
@ -143,7 +157,6 @@ impl Manager {
MessageType::MessageType_EthereumAddress => Ok(true), MessageType::MessageType_EthereumAddress => Ok(true),
// Getting anything else means we didn't unlock it // Getting anything else means we didn't unlock it
_ => Ok(false), _ => Ok(false),
} }
}; };
self.update_devices(DeviceDirection::Arrived)?; self.update_devices(DeviceDirection::Arrived)?;
@ -156,12 +169,21 @@ impl Manager {
buf.iter().skip_while(|x| **x == 0).cloned().collect() buf.iter().skip_while(|x| **x == 0).cloned().collect()
} }
fn signing_loop(&self, handle: &hidapi::HidDevice, chain_id: &Option<u64>, data: &[u8]) -> Result<Signature, Error> { fn signing_loop(
&self,
handle: &hidapi::HidDevice,
chain_id: &Option<u64>,
data: &[u8],
) -> Result<Signature, Error> {
let (resp_type, bytes) = self.read_device_response(&handle)?; let (resp_type, bytes) = self.read_device_response(&handle)?;
match resp_type { match resp_type {
MessageType::MessageType_Cancel => Err(Error::UserCancel), MessageType::MessageType_Cancel => Err(Error::UserCancel),
MessageType::MessageType_ButtonRequest => { MessageType::MessageType_ButtonRequest => {
self.send_device_message(handle, MessageType::MessageType_ButtonAck, &ButtonAck::new())?; self.send_device_message(
handle,
MessageType::MessageType_ButtonAck,
&ButtonAck::new(),
)?;
// Signing loop goes back to the top and reading blocks // Signing loop goes back to the top and reading blocks
// for up to 5 minutes waiting for response from the device // for up to 5 minutes waiting for response from the device
// if the user doesn't click any button within 5 minutes you // if the user doesn't click any button within 5 minutes you
@ -186,7 +208,11 @@ impl Manager {
// so v' = v + 2 * chain_id + 35, but code further down the // so v' = v + 2 * chain_id + 35, but code further down the
// pipeline will already do this transformation, so remove it here // pipeline will already do this transformation, so remove it here
let adjustment = 35 + 2 * c_id as u32; let adjustment = 35 + 2 * c_id as u32;
Ok(Signature::from_rsv(&r, &s, (max(v, adjustment) - adjustment) as u8)) Ok(Signature::from_rsv(
&r,
&s,
(max(v, adjustment) - adjustment) as u8,
))
} else { } else {
// If there isn't a chain_id, v will be returned as v + 27 // If there isn't a chain_id, v will be returned as v + 27
let adjusted_v = if v < 27 { v } else { v - 27 }; let adjusted_v = if v < 27 { v } else { v - 27 };
@ -194,12 +220,19 @@ impl Manager {
} }
} }
} }
MessageType::MessageType_Failure => Err(Error::Protocol("Last message sent to Trezor failed")), MessageType::MessageType_Failure => {
Err(Error::Protocol("Last message sent to Trezor failed"))
}
_ => Err(Error::Protocol("Unexpected response from Trezor device.")), _ => Err(Error::Protocol("Unexpected response from Trezor device.")),
} }
} }
fn send_device_message(&self, device: &hidapi::HidDevice, msg_type: MessageType, msg: &Message) -> Result<usize, Error> { fn send_device_message(
&self,
device: &hidapi::HidDevice,
msg_type: MessageType,
msg: &Message,
) -> Result<usize, Error> {
let msg_id = msg_type as u16; let msg_id = msg_type as u16;
let mut message = msg.write_to_bytes()?; let mut message = msg.write_to_bytes()?;
let msg_size = message.len(); let msg_size = message.len();
@ -247,7 +280,10 @@ impl Manager {
} }
} }
fn read_device_response(&self, device: &hidapi::HidDevice) -> Result<(MessageType, Vec<u8>), Error> { fn read_device_response(
&self,
device: &hidapi::HidDevice,
) -> Result<(MessageType, Vec<u8>), Error> {
let protocol_err = Error::Protocol(&"Unexpected wire response from Trezor Device"); let protocol_err = Error::Protocol(&"Unexpected wire response from Trezor Device");
let mut buf = vec![0; 64]; let mut buf = vec![0; 64];
@ -255,8 +291,13 @@ impl Manager {
if first_chunk < 9 || buf[0] != b'?' || buf[1] != b'#' || buf[2] != b'#' { if first_chunk < 9 || buf[0] != b'?' || buf[1] != b'#' || buf[2] != b'#' {
return Err(protocol_err); return Err(protocol_err);
} }
let msg_type = MessageType::from_i32(((buf[3] as i32 & 0xFF) << 8) + (buf[4] as i32 & 0xFF)).ok_or(protocol_err)?; let msg_type =
let msg_size = ((buf[5] as u32 & 0xFF) << 24) + ((buf[6] as u32 & 0xFF) << 16) + ((buf[7] as u32 & 0xFF) << 8) + (buf[8] as u32 & 0xFF); MessageType::from_i32(((buf[3] as i32 & 0xFF) << 8) + (buf[4] as i32 & 0xFF))
.ok_or(protocol_err)?;
let msg_size = ((buf[5] as u32 & 0xFF) << 24)
+ ((buf[6] as u32 & 0xFF) << 16)
+ ((buf[7] as u32 & 0xFF) << 8)
+ (buf[8] as u32 & 0xFF);
let mut data = Vec::new(); let mut data = Vec::new();
data.extend_from_slice(&buf[9..]); data.extend_from_slice(&buf[9..]);
while data.len() < (msg_size as usize) { while data.len() < (msg_size as usize) {
@ -271,11 +312,17 @@ impl<'a> Wallet<'a> for Manager {
type Error = Error; type Error = Error;
type Transaction = &'a TransactionInfo; type Transaction = &'a TransactionInfo;
fn sign_transaction(&self, address: &Address, t_info: Self::Transaction) -> fn sign_transaction(
Result<Signature, Error> { &self,
address: &Address,
t_info: Self::Transaction,
) -> Result<Signature, Error> {
let usb = self.usb.lock(); let usb = self.usb.lock();
let devices = self.devices.read(); let devices = self.devices.read();
let device = devices.iter().find(|d| &d.info.address == address).ok_or(Error::KeyNotFound)?; let device = devices
.iter()
.find(|d| &d.info.address == address)
.ok_or(Error::KeyNotFound)?;
let handle = self.open_path(|| usb.open_path(&device.path))?; let handle = self.open_path(|| usb.open_path(&device.path))?;
let msg_type = MessageType::MessageType_EthereumSignTx; let msg_type = MessageType::MessageType_EthereumSignTx;
let mut message = EthereumSignTx::new(); let mut message = EthereumSignTx::new();
@ -301,7 +348,11 @@ impl<'a> Wallet<'a> for Manager {
self.send_device_message(&handle, msg_type, &message)?; self.send_device_message(&handle, msg_type, &message)?;
self.signing_loop(&handle, &t_info.chain_id, &t_info.data[first_chunk_length..]) self.signing_loop(
&handle,
&t_info.chain_id,
&t_info.data[first_chunk_length..],
)
} }
fn set_key_path(&self, key_path: KeyPath) { fn set_key_path(&self, key_path: KeyPath) {
@ -314,10 +365,12 @@ impl<'a> Wallet<'a> for Manager {
let devices = usb.devices(); let devices = usb.devices();
let num_prev_devices = self.devices.read().len(); let num_prev_devices = self.devices.read().len();
let detected_devices = devices.iter() let detected_devices = devices
.filter(|&d| is_valid_trezor(d.vendor_id, d.product_id) && .iter()
is_valid_hid_device(d.usage_page, d.interface_number) .filter(|&d| {
) is_valid_trezor(d.vendor_id, d.product_id)
&& is_valid_hid_device(d.usage_page, d.interface_number)
})
.fold(Vec::new(), |mut v, d| { .fold(Vec::new(), |mut v, d| {
match self.read_device(&usb, &d) { match self.read_device(&usb, &d) {
Ok(info) => { Ok(info) => {
@ -350,14 +403,26 @@ impl<'a> Wallet<'a> for Manager {
} }
} }
fn read_device(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result<Device, Error> { fn read_device(
&self,
usb: &hidapi::HidApi,
dev_info: &hidapi::HidDeviceInfo,
) -> Result<Device, Error> {
let handle = self.open_path(|| usb.open_path(&dev_info.path))?; let handle = self.open_path(|| usb.open_path(&dev_info.path))?;
let manufacturer = dev_info.manufacturer_string.clone().unwrap_or_else(|| "Unknown".to_owned()); let manufacturer = dev_info
let name = dev_info.product_string.clone().unwrap_or_else(|| "Unknown".to_owned()); .manufacturer_string
let serial = dev_info.serial_number.clone().unwrap_or_else(|| "Unknown".to_owned()); .clone()
.unwrap_or_else(|| "Unknown".to_owned());
let name = dev_info
.product_string
.clone()
.unwrap_or_else(|| "Unknown".to_owned());
let serial = dev_info
.serial_number
.clone()
.unwrap_or_else(|| "Unknown".to_owned());
match self.get_address(&handle) { match self.get_address(&handle) {
Ok(Some(addr)) => { Ok(Some(addr)) => Ok(Device {
Ok(Device {
path: dev_info.path.clone(), path: dev_info.path.clone(),
info: WalletInfo { info: WalletInfo {
name, name,
@ -365,8 +430,7 @@ impl<'a> Wallet<'a> for Manager {
serial, serial,
address: addr, address: addr,
}, },
}) }),
}
Ok(None) => Err(Error::LockedDevice(dev_info.path.clone())), Ok(None) => Err(Error::LockedDevice(dev_info.path.clone())),
Err(e) => Err(e), Err(e) => Err(e),
} }
@ -381,7 +445,11 @@ impl<'a> Wallet<'a> for Manager {
} }
fn get_wallet(&self, address: &Address) -> Option<WalletInfo> { fn get_wallet(&self, address: &Address) -> Option<WalletInfo> {
self.devices.read().iter().find(|d| &d.info.address == address).map(|d| d.info.clone()) self.devices
.read()
.iter()
.find(|d| &d.info.address == address)
.map(|d| d.info.clone())
} }
fn get_address(&self, device: &hidapi::HidDevice) -> Result<Option<Address>, Error> { fn get_address(&self, device: &hidapi::HidDevice) -> Result<Option<Address>, Error> {
@ -405,7 +473,8 @@ impl<'a> Wallet<'a> for Manager {
} }
fn open_path<R, F>(&self, f: F) -> Result<R, Error> fn open_path<R, F>(&self, f: F) -> Result<R, Error>
where F: Fn() -> Result<R, &'static str> where
F: Fn() -> Result<R, &'static str>,
{ {
f().map_err(Into::into) f().map_err(Into::into)
} }
@ -417,7 +486,7 @@ pub fn try_connect_polling(trezor: &Manager, duration: &Duration, dir: DeviceDir
while start_time.elapsed() <= *duration { while start_time.elapsed() <= *duration {
if let Ok(num_devices) = trezor.update_devices(dir) { if let Ok(num_devices) = trezor.update_devices(dir) {
trace!(target: "hw", "{} Trezor devices {}", num_devices, dir); trace!(target: "hw", "{} Trezor devices {}", num_devices, dir);
return true return true;
} }
} }
false false
@ -433,15 +502,23 @@ pub fn is_valid_trezor(vid: u16, pid: u16) -> bool {
/// This test can't be run without an actual trezor device connected /// This test can't be run without an actual trezor device connected
/// (and unlocked) attached to the machine that's running the test /// (and unlocked) attached to the machine that's running the test
fn test_signature() { fn test_signature() {
use super::HardwareWalletManager;
use ethereum_types::Address; use ethereum_types::Address;
use MAX_POLLING_DURATION; use MAX_POLLING_DURATION;
use super::HardwareWalletManager;
let manager = HardwareWalletManager::new().unwrap(); let manager = HardwareWalletManager::new().unwrap();
assert_eq!(try_connect_polling(&manager.trezor, &MAX_POLLING_DURATION, DeviceDirection::Arrived), true); assert_eq!(
try_connect_polling(
&manager.trezor,
&MAX_POLLING_DURATION,
DeviceDirection::Arrived
),
true
);
let addr: Address = manager.list_wallets() let addr: Address = manager
.list_wallets()
.iter() .iter()
.filter(|d| d.name == "TREZOR".to_string() && d.manufacturer == "SatoshiLabs".to_string()) .filter(|d| d.name == "TREZOR".to_string() && d.manufacturer == "SatoshiLabs".to_string())
.nth(0) .nth(0)

View File

@ -16,13 +16,10 @@
//! Account Metadata //! Account Metadata
use std::{ use std::{collections::HashMap, time::Instant};
collections::HashMap,
time::Instant,
};
use ethkey::{Address, Password}; use ethkey::{Address, Password};
use serde_derive::{Serialize, Deserialize}; use serde_derive::{Deserialize, Serialize};
use serde_json; use serde_json;
/// Type of unlock. /// Type of unlock.
@ -57,17 +54,18 @@ pub struct AccountMeta {
impl AccountMeta { impl AccountMeta {
/// Read a hash map of Address -> AccountMeta /// Read a hash map of Address -> AccountMeta
pub fn read<R>(reader: R) -> Result<HashMap<Address, Self>, serde_json::Error> where pub fn read<R>(reader: R) -> Result<HashMap<Address, Self>, serde_json::Error>
where
R: ::std::io::Read, R: ::std::io::Read,
{ {
serde_json::from_reader(reader) serde_json::from_reader(reader)
} }
/// Write a hash map of Address -> AccountMeta /// Write a hash map of Address -> AccountMeta
pub fn write<W>(m: &HashMap<Address, Self>, writer: &mut W) -> Result<(), serde_json::Error> where pub fn write<W>(m: &HashMap<Address, Self>, writer: &mut W) -> Result<(), serde_json::Error>
where
W: ::std::io::Write, W: ::std::io::Write,
{ {
serde_json::to_writer(writer, m) serde_json::to_writer(writer, m)
} }
} }

View File

@ -16,8 +16,8 @@
use std::fmt; use std::fmt;
use ethstore::{Error as SSError}; use ethstore::Error as SSError;
use hardware_wallet::{Error as HardwareError}; use hardware_wallet::Error as HardwareError;
/// Signing error /// Signing error
#[derive(Debug)] #[derive(Debug)]

View File

@ -25,28 +25,32 @@ mod stores;
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))] #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
extern crate fake_hardware_wallet as hardware_wallet; extern crate fake_hardware_wallet as hardware_wallet;
use self::account_data::{Unlock, AccountData}; use self::{
use self::stores::AddressBook; account_data::{AccountData, Unlock},
stores::AddressBook,
};
use std::collections::HashMap; use std::{
use std::time::{Instant, Duration}; collections::HashMap,
time::{Duration, Instant},
};
use common_types::transaction::{Action, Transaction}; use common_types::transaction::{Action, Transaction};
use ethkey::{Address, Message, Public, Secret, Password, Random, Generator}; use ethkey::{Address, Generator, Message, Password, Public, Random, Secret};
use ethstore::accounts_dir::MemoryDirectory;
use ethstore::{ use ethstore::{
SimpleSecretStore, SecretStore, EthStore, EthMultiStore, accounts_dir::MemoryDirectory, random_string, EthMultiStore, EthStore, OpaqueSecret,
random_string, SecretVaultRef, StoreAccountRef, OpaqueSecret, SecretStore, SecretVaultRef, SimpleSecretStore, StoreAccountRef,
}; };
use log::{warn, debug}; use log::{debug, warn};
use parking_lot::RwLock; use parking_lot::RwLock;
pub use ethkey::Signature; pub use ethkey::Signature;
pub use ethstore::{Derivation, IndexDerivation, KeyFile, Error}; pub use ethstore::{Derivation, Error, IndexDerivation, KeyFile};
pub use hardware_wallet::{Error as HardwareError, HardwareWalletManager, KeyPath, TransactionInfo}; pub use hardware_wallet::{
Error as HardwareError, HardwareWalletManager, KeyPath, TransactionInfo,
};
pub use self::account_data::AccountMeta; pub use self::{account_data::AccountMeta, error::SignError};
pub use self::error::SignError;
type AccountToken = Password; type AccountToken = Password;
@ -86,7 +90,8 @@ pub struct AccountProvider {
} }
fn transient_sstore() -> EthMultiStore { fn transient_sstore() -> EthMultiStore {
EthMultiStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed") EthMultiStore::open(Box::new(MemoryDirectory::default()))
.expect("MemoryDirectory load always succeeds; qed")
} }
impl AccountProvider { impl AccountProvider {
@ -97,15 +102,22 @@ impl AccountProvider {
if settings.enable_hardware_wallets { if settings.enable_hardware_wallets {
match HardwareWalletManager::new() { match HardwareWalletManager::new() {
Ok(manager) => { Ok(manager) => {
manager.set_key_path(if settings.hardware_wallet_classic_key { KeyPath::EthereumClassic } else { KeyPath::Ethereum }); manager.set_key_path(if settings.hardware_wallet_classic_key {
KeyPath::EthereumClassic
} else {
KeyPath::Ethereum
});
hardware_store = Some(manager) hardware_store = Some(manager)
}, }
Err(e) => debug!("Error initializing hardware wallets: {}", e), Err(e) => debug!("Error initializing hardware wallets: {}", e),
} }
} }
if let Ok(accounts) = sstore.accounts() { if let Ok(accounts) = sstore.accounts() {
for account in accounts.into_iter().filter(|a| settings.blacklisted_accounts.contains(&a.address)) { for account in accounts
.into_iter()
.filter(|a| settings.blacklisted_accounts.contains(&a.address))
{
warn!("Local Account {} has a blacklisted (known to be weak) address and will be ignored", warn!("Local Account {} has a blacklisted (known to be weak) address and will be ignored",
account.address); account.address);
} }
@ -135,7 +147,10 @@ impl AccountProvider {
unlocked_secrets: RwLock::new(HashMap::new()), unlocked_secrets: RwLock::new(HashMap::new()),
unlocked: RwLock::new(HashMap::new()), unlocked: RwLock::new(HashMap::new()),
address_book: RwLock::new(AddressBook::transient()), address_book: RwLock::new(AddressBook::transient()),
sstore: Box::new(EthStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed")), sstore: Box::new(
EthStore::open(Box::new(MemoryDirectory::default()))
.expect("MemoryDirectory load always succeeds; qed"),
),
transient_sstore: transient_sstore(), transient_sstore: transient_sstore(),
hardware_store: None, hardware_store: None,
unlock_keep_secret: false, unlock_keep_secret: false,
@ -150,17 +165,23 @@ impl AccountProvider {
/// Creates new random account and returns address and public key /// Creates new random account and returns address and public key
pub fn new_account_and_public(&self, password: &Password) -> Result<(Address, Public), Error> { pub fn new_account_and_public(&self, password: &Password) -> Result<(Address, Public), Error> {
let acc = Random.generate().expect("secp context has generation capabilities; qed"); let acc = Random
.generate()
.expect("secp context has generation capabilities; qed");
let public = acc.public().clone(); let public = acc.public().clone();
let secret = acc.secret().clone(); let secret = acc.secret().clone();
let account = self.sstore.insert_account(SecretVaultRef::Root, secret, password)?; let account = self
.sstore
.insert_account(SecretVaultRef::Root, secret, password)?;
Ok((account.address, public)) Ok((account.address, public))
} }
/// Inserts new account into underlying store. /// Inserts new account into underlying store.
/// Does not unlock account! /// Does not unlock account!
pub fn insert_account(&self, secret: Secret, password: &Password) -> Result<Address, Error> { pub fn insert_account(&self, secret: Secret, password: &Password) -> Result<Address, Error> {
let account = self.sstore.insert_account(SecretVaultRef::Root, secret, password)?; let account = self
.sstore
.insert_account(SecretVaultRef::Root, secret, password)?;
if self.blacklisted_accounts.contains(&account.address) { if self.blacklisted_accounts.contains(&account.address) {
self.sstore.remove_account(&account, password)?; self.sstore.remove_account(&account, password)?;
return Err(Error::InvalidAccount.into()); return Err(Error::InvalidAccount.into());
@ -171,26 +192,49 @@ impl AccountProvider {
/// Generates new derived account based on the existing one /// Generates new derived account based on the existing one
/// If password is not provided, account must be unlocked /// If password is not provided, account must be unlocked
/// New account will be created with the same password (if save: true) /// New account will be created with the same password (if save: true)
pub fn derive_account(&self, address: &Address, password: Option<Password>, derivation: Derivation, save: bool) pub fn derive_account(
-> Result<Address, SignError> &self,
{ address: &Address,
password: Option<Password>,
derivation: Derivation,
save: bool,
) -> Result<Address, SignError> {
let account = self.sstore.account_ref(&address)?; let account = self.sstore.account_ref(&address)?;
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?; let password = password
Ok( .map(Ok)
if save { self.sstore.insert_derived(SecretVaultRef::Root, &account, &password, derivation)?.address } .unwrap_or_else(|| self.password(&account))?;
else { self.sstore.generate_derived(&account, &password, derivation)? } Ok(if save {
) self.sstore
.insert_derived(SecretVaultRef::Root, &account, &password, derivation)?
.address
} else {
self.sstore
.generate_derived(&account, &password, derivation)?
})
} }
/// Import a new presale wallet. /// Import a new presale wallet.
pub fn import_presale(&self, presale_json: &[u8], password: &Password) -> Result<Address, Error> { pub fn import_presale(
let account = self.sstore.import_presale(SecretVaultRef::Root, presale_json, password)?; &self,
presale_json: &[u8],
password: &Password,
) -> Result<Address, Error> {
let account = self
.sstore
.import_presale(SecretVaultRef::Root, presale_json, password)?;
Ok(Address::from(account.address).into()) Ok(Address::from(account.address).into())
} }
/// Import a new wallet. /// Import a new wallet.
pub fn import_wallet(&self, json: &[u8], password: &Password, gen_id: bool) -> Result<Address, Error> { pub fn import_wallet(
let account = self.sstore.import_wallet(SecretVaultRef::Root, json, password, gen_id)?; &self,
json: &[u8],
password: &Password,
gen_id: bool,
) -> Result<Address, Error> {
let account = self
.sstore
.import_wallet(SecretVaultRef::Root, json, password, gen_id)?;
if self.blacklisted_accounts.contains(&account.address) { if self.blacklisted_accounts.contains(&account.address) {
self.sstore.remove_account(&account, password)?; self.sstore.remove_account(&account, password)?;
return Err(Error::InvalidAccount.into()); return Err(Error::InvalidAccount.into());
@ -210,8 +254,7 @@ impl AccountProvider {
.into_iter() .into_iter()
.map(|a| a.address) .map(|a| a.address)
.filter(|address| !self.blacklisted_accounts.contains(address)) .filter(|address| !self.blacklisted_accounts.contains(address))
.collect() .collect())
)
} }
/// Returns the address of default account. /// Returns the address of default account.
@ -226,12 +269,18 @@ impl AccountProvider {
return Ok(accounts.into_iter().map(|a| a.address).collect()); return Ok(accounts.into_iter().map(|a| a.address).collect());
} }
} }
Err(Error::Custom("No hardware wallet accounts were found".into())) Err(Error::Custom(
"No hardware wallet accounts were found".into(),
))
} }
/// Get a list of paths to locked hardware wallets /// Get a list of paths to locked hardware wallets
pub fn locked_hardware_accounts(&self) -> Result<Vec<String>, SignError> { pub fn locked_hardware_accounts(&self) -> Result<Vec<String>, SignError> {
match self.hardware_store.as_ref().map(|h| h.list_locked_wallets()) { match self
.hardware_store
.as_ref()
.map(|h| h.list_locked_wallets())
{
None => Err(SignError::NotFound), None => Err(SignError::NotFound),
Some(Err(e)) => Err(SignError::Hardware(e)), Some(Err(e)) => Err(SignError::Hardware(e)),
Some(Ok(s)) => Ok(s), Some(Ok(s)) => Ok(s),
@ -240,7 +289,11 @@ impl AccountProvider {
/// Provide a pin to a locked hardware wallet on USB path to unlock it /// Provide a pin to a locked hardware wallet on USB path to unlock it
pub fn hardware_pin_matrix_ack(&self, path: &str, pin: &str) -> Result<bool, SignError> { pub fn hardware_pin_matrix_ack(&self, path: &str, pin: &str) -> Result<bool, SignError> {
match self.hardware_store.as_ref().map(|h| h.pin_matrix_ack(path, pin)) { match self
.hardware_store
.as_ref()
.map(|h| h.pin_matrix_ack(path, pin))
{
None => Err(SignError::NotFound), None => Err(SignError::NotFound),
Some(Err(e)) => Err(SignError::Hardware(e)), Some(Err(e)) => Err(SignError::Hardware(e)),
Some(Ok(s)) => Ok(s), Some(Ok(s)) => Ok(s),
@ -269,31 +322,51 @@ impl AccountProvider {
/// Returns each account along with name and meta. /// Returns each account along with name and meta.
pub fn accounts_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> { pub fn accounts_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> {
let r = self.sstore.accounts()? let r = self
.sstore
.accounts()?
.into_iter() .into_iter()
.filter(|a| !self.blacklisted_accounts.contains(&a.address)) .filter(|a| !self.blacklisted_accounts.contains(&a.address))
.map(|a| (a.address.clone(), self.account_meta(a.address).ok().unwrap_or_default())) .map(|a| {
(
a.address.clone(),
self.account_meta(a.address).ok().unwrap_or_default(),
)
})
.collect(); .collect();
Ok(r) Ok(r)
} }
/// Returns each hardware account along with name and meta. /// Returns each hardware account along with name and meta.
pub fn hardware_accounts_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> { pub fn hardware_accounts_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> {
let r = self.hardware_accounts()? let r = self
.hardware_accounts()?
.into_iter() .into_iter()
.map(|address| (address.clone(), self.account_meta(address).ok().unwrap_or_default())) .map(|address| {
(
address.clone(),
self.account_meta(address).ok().unwrap_or_default(),
)
})
.collect(); .collect();
Ok(r) Ok(r)
} }
/// Returns each hardware account along with name and meta. /// Returns each hardware account along with name and meta.
pub fn is_hardware_address(&self, address: &Address) -> bool { pub fn is_hardware_address(&self, address: &Address) -> bool {
self.hardware_store.as_ref().and_then(|s| s.wallet_info(address)).is_some() self.hardware_store
.as_ref()
.and_then(|s| s.wallet_info(address))
.is_some()
} }
/// Returns each account along with name and meta. /// Returns each account along with name and meta.
pub fn account_meta(&self, address: Address) -> Result<AccountMeta, Error> { pub fn account_meta(&self, address: Address) -> Result<AccountMeta, Error> {
if let Some(info) = self.hardware_store.as_ref().and_then(|s| s.wallet_info(&address)) { if let Some(info) = self
.hardware_store
.as_ref()
.and_then(|s| s.wallet_info(&address))
{
Ok(AccountMeta { Ok(AccountMeta {
name: info.name, name: info.name,
meta: info.manufacturer, meta: info.manufacturer,
@ -311,59 +384,78 @@ impl AccountProvider {
/// Returns account public key. /// Returns account public key.
pub fn account_public(&self, address: Address, password: &Password) -> Result<Public, Error> { pub fn account_public(&self, address: Address, password: &Password) -> Result<Public, Error> {
self.sstore.public(&self.sstore.account_ref(&address)?, password) self.sstore
.public(&self.sstore.account_ref(&address)?, password)
} }
/// Returns each account along with name and meta. /// Returns each account along with name and meta.
pub fn set_account_name(&self, address: Address, name: String) -> Result<(), Error> { pub fn set_account_name(&self, address: Address, name: String) -> Result<(), Error> {
self.sstore.set_name(&self.sstore.account_ref(&address)?, name)?; self.sstore
.set_name(&self.sstore.account_ref(&address)?, name)?;
Ok(()) Ok(())
} }
/// Returns each account along with name and meta. /// Returns each account along with name and meta.
pub fn set_account_meta(&self, address: Address, meta: String) -> Result<(), Error> { pub fn set_account_meta(&self, address: Address, meta: String) -> Result<(), Error> {
self.sstore.set_meta(&self.sstore.account_ref(&address)?, meta)?; self.sstore
.set_meta(&self.sstore.account_ref(&address)?, meta)?;
Ok(()) Ok(())
} }
/// Returns `true` if the password for `account` is `password`. `false` if not. /// Returns `true` if the password for `account` is `password`. `false` if not.
pub fn test_password(&self, address: &Address, password: &Password) -> Result<bool, Error> { pub fn test_password(&self, address: &Address, password: &Password) -> Result<bool, Error> {
self.sstore.test_password(&self.sstore.account_ref(&address)?, password) self.sstore
.test_password(&self.sstore.account_ref(&address)?, password)
.map_err(Into::into) .map_err(Into::into)
} }
/// Permanently removes an account. /// Permanently removes an account.
pub fn kill_account(&self, address: &Address, password: &Password) -> Result<(), Error> { pub fn kill_account(&self, address: &Address, password: &Password) -> Result<(), Error> {
self.sstore.remove_account(&self.sstore.account_ref(&address)?, &password)?; self.sstore
.remove_account(&self.sstore.account_ref(&address)?, &password)?;
Ok(()) Ok(())
} }
/// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given. /// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given.
pub fn change_password(&self, address: &Address, password: Password, new_password: Password) -> Result<(), Error> { pub fn change_password(
self.sstore.change_password(&self.sstore.account_ref(address)?, &password, &new_password) &self,
address: &Address,
password: Password,
new_password: Password,
) -> Result<(), Error> {
self.sstore
.change_password(&self.sstore.account_ref(address)?, &password, &new_password)
} }
/// Exports an account for given address. /// Exports an account for given address.
pub fn export_account(&self, address: &Address, password: Password) -> Result<KeyFile, Error> { pub fn export_account(&self, address: &Address, password: Password) -> Result<KeyFile, Error> {
self.sstore.export_account(&self.sstore.account_ref(address)?, &password) self.sstore
.export_account(&self.sstore.account_ref(address)?, &password)
} }
/// Helper method used for unlocking accounts. /// Helper method used for unlocking accounts.
fn unlock_account(&self, address: Address, password: Password, unlock: Unlock) -> Result<(), Error> { fn unlock_account(
&self,
address: Address,
password: Password,
unlock: Unlock,
) -> Result<(), Error> {
let account = self.sstore.account_ref(&address)?; let account = self.sstore.account_ref(&address)?;
// check if account is already unlocked permanently, if it is, do nothing // check if account is already unlocked permanently, if it is, do nothing
let mut unlocked = self.unlocked.write(); let mut unlocked = self.unlocked.write();
if let Some(data) = unlocked.get(&account) { if let Some(data) = unlocked.get(&account) {
if let Unlock::Perm = data.unlock { if let Unlock::Perm = data.unlock {
return Ok(()) return Ok(());
} }
} }
if self.unlock_keep_secret && unlock == Unlock::Perm { if self.unlock_keep_secret && unlock == Unlock::Perm {
// verify password and get the secret // verify password and get the secret
let secret = self.sstore.raw_secret(&account, &password)?; let secret = self.sstore.raw_secret(&account, &password)?;
self.unlocked_secrets.write().insert(account.clone(), secret); self.unlocked_secrets
.write()
.insert(account.clone(), secret);
} else { } else {
// verify password by signing dump message // verify password by signing dump message
// result may be discarded // result may be discarded
@ -383,11 +475,15 @@ impl AccountProvider {
let mut unlocked = self.unlocked.write(); let mut unlocked = self.unlocked.write();
let data = unlocked.get(account).ok_or(SignError::NotUnlocked)?.clone(); let data = unlocked.get(account).ok_or(SignError::NotUnlocked)?.clone();
if let Unlock::OneTime = data.unlock { if let Unlock::OneTime = data.unlock {
unlocked.remove(account).expect("data exists: so key must exist: qed"); unlocked
.remove(account)
.expect("data exists: so key must exist: qed");
} }
if let Unlock::Timed(ref end) = data.unlock { if let Unlock::Timed(ref end) = data.unlock {
if Instant::now() > *end { if Instant::now() > *end {
unlocked.remove(account).expect("data exists: so key must exist: qed"); unlocked
.remove(account)
.expect("data exists: so key must exist: qed");
return Err(SignError::NotUnlocked); return Err(SignError::NotUnlocked);
} }
} }
@ -395,17 +491,30 @@ impl AccountProvider {
} }
/// Unlocks account permanently. /// Unlocks account permanently.
pub fn unlock_account_permanently(&self, account: Address, password: Password) -> Result<(), Error> { pub fn unlock_account_permanently(
&self,
account: Address,
password: Password,
) -> Result<(), Error> {
self.unlock_account(account, password, Unlock::Perm) self.unlock_account(account, password, Unlock::Perm)
} }
/// Unlocks account temporarily (for one signing). /// Unlocks account temporarily (for one signing).
pub fn unlock_account_temporarily(&self, account: Address, password: Password) -> Result<(), Error> { pub fn unlock_account_temporarily(
&self,
account: Address,
password: Password,
) -> Result<(), Error> {
self.unlock_account(account, password, Unlock::OneTime) self.unlock_account(account, password, Unlock::OneTime)
} }
/// Unlocks account temporarily with a timeout. /// Unlocks account temporarily with a timeout.
pub fn unlock_account_timed(&self, account: Address, password: Password, duration: Duration) -> Result<(), Error> { pub fn unlock_account_timed(
&self,
account: Address,
password: Password,
duration: Duration,
) -> Result<(), Error> {
self.unlock_account(account, password, Unlock::Timed(Instant::now() + duration)) self.unlock_account(account, password, Unlock::Timed(Instant::now() + duration))
} }
@ -413,7 +522,8 @@ impl AccountProvider {
pub fn is_unlocked(&self, address: &Address) -> bool { pub fn is_unlocked(&self, address: &Address) -> bool {
let unlocked = self.unlocked.read(); let unlocked = self.unlocked.read();
let unlocked_secrets = self.unlocked_secrets.read(); let unlocked_secrets = self.unlocked_secrets.read();
self.sstore.account_ref(address) self.sstore
.account_ref(address)
.map(|r| unlocked.get(&r).is_some() || unlocked_secrets.get(&r).is_some()) .map(|r| unlocked.get(&r).is_some() || unlocked_secrets.get(&r).is_some())
.unwrap_or(false) .unwrap_or(false)
} }
@ -421,48 +531,78 @@ impl AccountProvider {
/// Checks if given account is unlocked permanently /// Checks if given account is unlocked permanently
pub fn is_unlocked_permanently(&self, address: &Address) -> bool { pub fn is_unlocked_permanently(&self, address: &Address) -> bool {
let unlocked = self.unlocked.read(); let unlocked = self.unlocked.read();
self.sstore.account_ref(address) self.sstore
.map(|r| unlocked.get(&r).map_or(false, |account| account.unlock == Unlock::Perm)) .account_ref(address)
.map(|r| {
unlocked
.get(&r)
.map_or(false, |account| account.unlock == Unlock::Perm)
})
.unwrap_or(false) .unwrap_or(false)
} }
/// Signs the message. If password is not provided the account must be unlocked. /// Signs the message. If password is not provided the account must be unlocked.
pub fn sign(&self, address: Address, password: Option<Password>, message: Message) -> Result<Signature, SignError> { pub fn sign(
&self,
address: Address,
password: Option<Password>,
message: Message,
) -> Result<Signature, SignError> {
let account = self.sstore.account_ref(&address)?; let account = self.sstore.account_ref(&address)?;
match self.unlocked_secrets.read().get(&account) { match self.unlocked_secrets.read().get(&account) {
Some(secret) => { Some(secret) => Ok(self.sstore.sign_with_secret(&secret, &message)?),
Ok(self.sstore.sign_with_secret(&secret, &message)?)
},
None => { None => {
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?; let password = password
.map(Ok)
.unwrap_or_else(|| self.password(&account))?;
Ok(self.sstore.sign(&account, &password, &message)?) Ok(self.sstore.sign(&account, &password, &message)?)
} }
} }
} }
/// Signs message using the derived secret. If password is not provided the account must be unlocked. /// Signs message using the derived secret. If password is not provided the account must be unlocked.
pub fn sign_derived(&self, address: &Address, password: Option<Password>, derivation: Derivation, message: Message) pub fn sign_derived(
-> Result<Signature, SignError> &self,
{ address: &Address,
password: Option<Password>,
derivation: Derivation,
message: Message,
) -> Result<Signature, SignError> {
let account = self.sstore.account_ref(address)?; let account = self.sstore.account_ref(address)?;
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?; let password = password
Ok(self.sstore.sign_derived(&account, &password, derivation, &message)?) .map(Ok)
.unwrap_or_else(|| self.password(&account))?;
Ok(self
.sstore
.sign_derived(&account, &password, derivation, &message)?)
} }
/// Signs given message with supplied token. Returns a token to use in next signing within this session. /// Signs given message with supplied token. Returns a token to use in next signing within this session.
pub fn sign_with_token(&self, address: Address, token: AccountToken, message: Message) -> Result<(Signature, AccountToken), SignError> { pub fn sign_with_token(
&self,
address: Address,
token: AccountToken,
message: Message,
) -> Result<(Signature, AccountToken), SignError> {
let account = self.sstore.account_ref(&address)?; let account = self.sstore.account_ref(&address)?;
let is_std_password = self.sstore.test_password(&account, &token)?; let is_std_password = self.sstore.test_password(&account, &token)?;
let new_token = Password::from(random_string(16)); let new_token = Password::from(random_string(16));
let signature = if is_std_password { let signature = if is_std_password {
// Insert to transient store // Insert to transient store
self.sstore.copy_account(&self.transient_sstore, SecretVaultRef::Root, &account, &token, &new_token)?; self.sstore.copy_account(
&self.transient_sstore,
SecretVaultRef::Root,
&account,
&token,
&new_token,
)?;
// sign // sign
self.sstore.sign(&account, &token, &message)? self.sstore.sign(&account, &token, &message)?
} else { } else {
// check transient store // check transient store
self.transient_sstore.change_password(&account, &token, &new_token)?; self.transient_sstore
.change_password(&account, &token, &new_token)?;
// and sign // and sign
self.transient_sstore.sign(&account, &new_token, &message)? self.transient_sstore.sign(&account, &new_token, &message)?
}; };
@ -471,114 +611,159 @@ impl AccountProvider {
} }
/// Decrypts a message with given token. Returns a token to use in next operation for this account. /// Decrypts a message with given token. Returns a token to use in next operation for this account.
pub fn decrypt_with_token(&self, address: Address, token: AccountToken, shared_mac: &[u8], message: &[u8]) pub fn decrypt_with_token(
-> Result<(Vec<u8>, AccountToken), SignError> &self,
{ address: Address,
token: AccountToken,
shared_mac: &[u8],
message: &[u8],
) -> Result<(Vec<u8>, AccountToken), SignError> {
let account = self.sstore.account_ref(&address)?; let account = self.sstore.account_ref(&address)?;
let is_std_password = self.sstore.test_password(&account, &token)?; let is_std_password = self.sstore.test_password(&account, &token)?;
let new_token = Password::from(random_string(16)); let new_token = Password::from(random_string(16));
let message = if is_std_password { let message = if is_std_password {
// Insert to transient store // Insert to transient store
self.sstore.copy_account(&self.transient_sstore, SecretVaultRef::Root, &account, &token, &new_token)?; self.sstore.copy_account(
&self.transient_sstore,
SecretVaultRef::Root,
&account,
&token,
&new_token,
)?;
// decrypt // decrypt
self.sstore.decrypt(&account, &token, shared_mac, message)? self.sstore.decrypt(&account, &token, shared_mac, message)?
} else { } else {
// check transient store // check transient store
self.transient_sstore.change_password(&account, &token, &new_token)?; self.transient_sstore
.change_password(&account, &token, &new_token)?;
// and decrypt // and decrypt
self.transient_sstore.decrypt(&account, &token, shared_mac, message)? self.transient_sstore
.decrypt(&account, &token, shared_mac, message)?
}; };
Ok((message, new_token)) Ok((message, new_token))
} }
/// Decrypts a message. If password is not provided the account must be unlocked. /// Decrypts a message. If password is not provided the account must be unlocked.
pub fn decrypt(&self, address: Address, password: Option<Password>, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, SignError> { pub fn decrypt(
&self,
address: Address,
password: Option<Password>,
shared_mac: &[u8],
message: &[u8],
) -> Result<Vec<u8>, SignError> {
let account = self.sstore.account_ref(&address)?; let account = self.sstore.account_ref(&address)?;
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?; let password = password
Ok(self.sstore.decrypt(&account, &password, shared_mac, message)?) .map(Ok)
.unwrap_or_else(|| self.password(&account))?;
Ok(self
.sstore
.decrypt(&account, &password, shared_mac, message)?)
} }
/// Agree on shared key. /// Agree on shared key.
pub fn agree(&self, address: Address, password: Option<Password>, other_public: &Public) -> Result<Secret, SignError> { pub fn agree(
&self,
address: Address,
password: Option<Password>,
other_public: &Public,
) -> Result<Secret, SignError> {
let account = self.sstore.account_ref(&address)?; let account = self.sstore.account_ref(&address)?;
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?; let password = password
.map(Ok)
.unwrap_or_else(|| self.password(&account))?;
Ok(self.sstore.agree(&account, &password, other_public)?) Ok(self.sstore.agree(&account, &password, other_public)?)
} }
/// Returns the underlying `SecretStore` reference if one exists. /// Returns the underlying `SecretStore` reference if one exists.
pub fn list_geth_accounts(&self, testnet: bool) -> Vec<Address> { pub fn list_geth_accounts(&self, testnet: bool) -> Vec<Address> {
self.sstore.list_geth_accounts(testnet).into_iter().map(|a| Address::from(a).into()).collect() self.sstore
.list_geth_accounts(testnet)
.into_iter()
.map(|a| Address::from(a).into())
.collect()
} }
/// Returns the underlying `SecretStore` reference if one exists. /// Returns the underlying `SecretStore` reference if one exists.
pub fn import_geth_accounts(&self, desired: Vec<Address>, testnet: bool) -> Result<Vec<Address>, Error> { pub fn import_geth_accounts(
self.sstore.import_geth_accounts(SecretVaultRef::Root, desired, testnet) &self,
desired: Vec<Address>,
testnet: bool,
) -> Result<Vec<Address>, Error> {
self.sstore
.import_geth_accounts(SecretVaultRef::Root, desired, testnet)
.map(|a| a.into_iter().map(|a| a.address).collect()) .map(|a| a.into_iter().map(|a| a.address).collect())
.map_err(Into::into) .map_err(Into::into)
} }
/// Create new vault. /// Create new vault.
pub fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error> { pub fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
self.sstore.create_vault(name, password) self.sstore.create_vault(name, password).map_err(Into::into)
.map_err(Into::into)
} }
/// Open existing vault. /// Open existing vault.
pub fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> { pub fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
self.sstore.open_vault(name, password) self.sstore.open_vault(name, password).map_err(Into::into)
.map_err(Into::into)
} }
/// Close previously opened vault. /// Close previously opened vault.
pub fn close_vault(&self, name: &str) -> Result<(), Error> { pub fn close_vault(&self, name: &str) -> Result<(), Error> {
self.sstore.close_vault(name) self.sstore.close_vault(name).map_err(Into::into)
.map_err(Into::into)
} }
/// List all vaults /// List all vaults
pub fn list_vaults(&self) -> Result<Vec<String>, Error> { pub fn list_vaults(&self) -> Result<Vec<String>, Error> {
self.sstore.list_vaults() self.sstore.list_vaults().map_err(Into::into)
.map_err(Into::into)
} }
/// List all currently opened vaults /// List all currently opened vaults
pub fn list_opened_vaults(&self) -> Result<Vec<String>, Error> { pub fn list_opened_vaults(&self) -> Result<Vec<String>, Error> {
self.sstore.list_opened_vaults() self.sstore.list_opened_vaults().map_err(Into::into)
.map_err(Into::into)
} }
/// Change vault password. /// Change vault password.
pub fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error> { pub fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error> {
self.sstore.change_vault_password(name, new_password) self.sstore
.change_vault_password(name, new_password)
.map_err(Into::into) .map_err(Into::into)
} }
/// Change vault of the given address. /// Change vault of the given address.
pub fn change_vault(&self, address: Address, new_vault: &str) -> Result<(), Error> { pub fn change_vault(&self, address: Address, new_vault: &str) -> Result<(), Error> {
let new_vault_ref = if new_vault.is_empty() { SecretVaultRef::Root } else { SecretVaultRef::Vault(new_vault.to_owned()) }; let new_vault_ref = if new_vault.is_empty() {
SecretVaultRef::Root
} else {
SecretVaultRef::Vault(new_vault.to_owned())
};
let old_account_ref = self.sstore.account_ref(&address)?; let old_account_ref = self.sstore.account_ref(&address)?;
self.sstore.change_account_vault(new_vault_ref, old_account_ref) self.sstore
.change_account_vault(new_vault_ref, old_account_ref)
.map_err(Into::into) .map_err(Into::into)
.map(|_| ()) .map(|_| ())
} }
/// Get vault metadata string. /// Get vault metadata string.
pub fn get_vault_meta(&self, name: &str) -> Result<String, Error> { pub fn get_vault_meta(&self, name: &str) -> Result<String, Error> {
self.sstore.get_vault_meta(name) self.sstore.get_vault_meta(name).map_err(Into::into)
.map_err(Into::into)
} }
/// Set vault metadata string. /// Set vault metadata string.
pub fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> { pub fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> {
self.sstore.set_vault_meta(name, meta) self.sstore.set_vault_meta(name, meta).map_err(Into::into)
.map_err(Into::into)
} }
/// Sign message with hardware wallet. /// Sign message with hardware wallet.
pub fn sign_message_with_hardware(&self, address: &Address, message: &[u8]) -> Result<Signature, SignError> { pub fn sign_message_with_hardware(
match self.hardware_store.as_ref().map(|s| s.sign_message(address, message)) { &self,
address: &Address,
message: &[u8],
) -> Result<Signature, SignError> {
match self
.hardware_store
.as_ref()
.map(|s| s.sign_message(address, message))
{
None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound), None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound),
Some(Err(e)) => Err(From::from(e)), Some(Err(e)) => Err(From::from(e)),
Some(Ok(s)) => Ok(s), Some(Ok(s)) => Ok(s),
@ -586,7 +771,13 @@ impl AccountProvider {
} }
/// Sign transaction with hardware wallet. /// Sign transaction with hardware wallet.
pub fn sign_transaction_with_hardware(&self, address: &Address, transaction: &Transaction, chain_id: Option<u64>, rlp_encoded_transaction: &[u8]) -> Result<Signature, SignError> { pub fn sign_transaction_with_hardware(
&self,
address: &Address,
transaction: &Transaction,
chain_id: Option<u64>,
rlp_encoded_transaction: &[u8],
) -> Result<Signature, SignError> {
let t_info = TransactionInfo { let t_info = TransactionInfo {
nonce: transaction.nonce, nonce: transaction.nonce,
gas_price: transaction.gas_price, gas_price: transaction.gas_price,
@ -599,7 +790,11 @@ impl AccountProvider {
data: transaction.data.to_vec(), data: transaction.data.to_vec(),
chain_id: chain_id, chain_id: chain_id,
}; };
match self.hardware_store.as_ref().map(|s| s.sign_transaction(&address, &t_info, rlp_encoded_transaction)) { match self
.hardware_store
.as_ref()
.map(|s| s.sign_transaction(&address, &t_info, rlp_encoded_transaction))
{
None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound), None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound),
Some(Err(e)) => Err(From::from(e)), Some(Err(e)) => Err(From::from(e)),
Some(Ok(s)) => Ok(s), Some(Ok(s)) => Ok(s),
@ -610,18 +805,24 @@ impl AccountProvider {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{AccountProvider, Unlock}; use super::{AccountProvider, Unlock};
use std::time::{Duration, Instant};
use ethkey::{Generator, Random, Address};
use ethstore::{StoreAccountRef, Derivation};
use ethereum_types::H256; use ethereum_types::H256;
use ethkey::{Address, Generator, Random};
use ethstore::{Derivation, StoreAccountRef};
use std::time::{Duration, Instant};
#[test] #[test]
fn unlock_account_temp() { fn unlock_account_temp() {
let kp = Random.generate().unwrap(); let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider(); let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok()); assert!(ap
assert!(ap.unlock_account_temporarily(kp.address(), "test1".into()).is_err()); .insert_account(kp.secret().clone(), &"test".into())
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok()); .is_ok());
assert!(ap
.unlock_account_temporarily(kp.address(), "test1".into())
.is_err());
assert!(ap
.unlock_account_temporarily(kp.address(), "test".into())
.is_ok());
assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
assert!(ap.sign(kp.address(), None, Default::default()).is_err()); assert!(ap.sign(kp.address(), None, Default::default()).is_err());
} }
@ -630,81 +831,118 @@ mod tests {
fn derived_account_nosave() { fn derived_account_nosave() {
let kp = Random.generate().unwrap(); let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider(); let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok()); assert!(ap
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok()); .insert_account(kp.secret().clone(), &"base".into())
.is_ok());
assert!(ap
.unlock_account_permanently(kp.address(), "base".into())
.is_ok());
let derived_addr = ap.derive_account( let derived_addr = ap
.derive_account(
&kp.address(), &kp.address(),
None, None,
Derivation::SoftHash(H256::from(999)), Derivation::SoftHash(H256::from(999)),
false, false,
).expect("Derivation should not fail"); )
.expect("Derivation should not fail");
assert!(ap.unlock_account_permanently(derived_addr, "base".into()).is_err(), assert!(
"There should be an error because account is not supposed to be saved"); ap.unlock_account_permanently(derived_addr, "base".into())
.is_err(),
"There should be an error because account is not supposed to be saved"
);
} }
#[test] #[test]
fn derived_account_save() { fn derived_account_save() {
let kp = Random.generate().unwrap(); let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider(); let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok()); assert!(ap
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok()); .insert_account(kp.secret().clone(), &"base".into())
.is_ok());
assert!(ap
.unlock_account_permanently(kp.address(), "base".into())
.is_ok());
let derived_addr = ap.derive_account( let derived_addr = ap
.derive_account(
&kp.address(), &kp.address(),
None, None,
Derivation::SoftHash(H256::from(999)), Derivation::SoftHash(H256::from(999)),
true, true,
).expect("Derivation should not fail"); )
.expect("Derivation should not fail");
assert!(ap.unlock_account_permanently(derived_addr, "base_wrong".into()).is_err(), assert!(
"There should be an error because password is invalid"); ap.unlock_account_permanently(derived_addr, "base_wrong".into())
.is_err(),
"There should be an error because password is invalid"
);
assert!(ap.unlock_account_permanently(derived_addr, "base".into()).is_ok(), assert!(
"Should be ok because account is saved and password is valid"); ap.unlock_account_permanently(derived_addr, "base".into())
.is_ok(),
"Should be ok because account is saved and password is valid"
);
} }
#[test] #[test]
fn derived_account_sign() { fn derived_account_sign() {
let kp = Random.generate().unwrap(); let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider(); let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok()); assert!(ap
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok()); .insert_account(kp.secret().clone(), &"base".into())
.is_ok());
assert!(ap
.unlock_account_permanently(kp.address(), "base".into())
.is_ok());
let derived_addr = ap.derive_account( let derived_addr = ap
.derive_account(
&kp.address(), &kp.address(),
None, None,
Derivation::SoftHash(H256::from(1999)), Derivation::SoftHash(H256::from(1999)),
true, true,
).expect("Derivation should not fail"); )
.expect("Derivation should not fail");
ap.unlock_account_permanently(derived_addr, "base".into()) ap.unlock_account_permanently(derived_addr, "base".into())
.expect("Should be ok because account is saved and password is valid"); .expect("Should be ok because account is saved and password is valid");
let msg = Default::default(); let msg = Default::default();
let signed_msg1 = ap.sign(derived_addr, None, msg) let signed_msg1 = ap
.sign(derived_addr, None, msg)
.expect("Signing with existing unlocked account should not fail"); .expect("Signing with existing unlocked account should not fail");
let signed_msg2 = ap.sign_derived( let signed_msg2 = ap
.sign_derived(
&kp.address(), &kp.address(),
None, None,
Derivation::SoftHash(H256::from(1999)), Derivation::SoftHash(H256::from(1999)),
msg, msg,
).expect("Derived signing with existing unlocked account should not fail"); )
.expect("Derived signing with existing unlocked account should not fail");
assert_eq!(signed_msg1, signed_msg2, assert_eq!(signed_msg1, signed_msg2, "Signed messages should match");
"Signed messages should match");
} }
#[test] #[test]
fn unlock_account_perm() { fn unlock_account_perm() {
let kp = Random.generate().unwrap(); let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider(); let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok()); assert!(ap
assert!(ap.unlock_account_permanently(kp.address(), "test1".into()).is_err()); .insert_account(kp.secret().clone(), &"test".into())
assert!(ap.unlock_account_permanently(kp.address(), "test".into()).is_ok()); .is_ok());
assert!(ap
.unlock_account_permanently(kp.address(), "test1".into())
.is_err());
assert!(ap
.unlock_account_permanently(kp.address(), "test".into())
.is_ok());
assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok()); assert!(ap
.unlock_account_temporarily(kp.address(), "test".into())
.is_ok());
assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
} }
@ -713,11 +951,21 @@ mod tests {
fn unlock_account_timer() { fn unlock_account_timer() {
let kp = Random.generate().unwrap(); let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider(); let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok()); assert!(ap
assert!(ap.unlock_account_timed(kp.address(), "test1".into(), Duration::from_secs(60)).is_err()); .insert_account(kp.secret().clone(), &"test".into())
assert!(ap.unlock_account_timed(kp.address(), "test".into(), Duration::from_secs(60)).is_ok()); .is_ok());
assert!(ap
.unlock_account_timed(kp.address(), "test1".into(), Duration::from_secs(60))
.is_err());
assert!(ap
.unlock_account_timed(kp.address(), "test".into(), Duration::from_secs(60))
.is_ok());
assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); assert!(ap.sign(kp.address(), None, Default::default()).is_ok());
ap.unlocked.write().get_mut(&StoreAccountRef::root(kp.address())).unwrap().unlock = Unlock::Timed(Instant::now()); ap.unlocked
.write()
.get_mut(&StoreAccountRef::root(kp.address()))
.unwrap()
.unlock = Unlock::Timed(Instant::now());
assert!(ap.sign(kp.address(), None, Default::default()).is_err()); assert!(ap.sign(kp.address(), None, Default::default()).is_err());
} }
@ -726,15 +974,23 @@ mod tests {
// given // given
let kp = Random.generate().unwrap(); let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider(); let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok()); assert!(ap
.insert_account(kp.secret().clone(), &"test".into())
.is_ok());
// when // when
let (_signature, token) = ap.sign_with_token(kp.address(), "test".into(), Default::default()).unwrap(); let (_signature, token) = ap
.sign_with_token(kp.address(), "test".into(), Default::default())
.unwrap();
// then // then
ap.sign_with_token(kp.address(), token.clone(), Default::default()) ap.sign_with_token(kp.address(), token.clone(), Default::default())
.expect("First usage of token should be correct."); .expect("First usage of token should be correct.");
assert!(ap.sign_with_token(kp.address(), token, Default::default()).is_err(), "Second usage of the same token should fail."); assert!(
ap.sign_with_token(kp.address(), token, Default::default())
.is_err(),
"Second usage of the same token should fail."
);
} }
#[test] #[test]
@ -745,7 +1001,14 @@ mod tests {
ap.blacklisted_accounts = vec![acc]; ap.blacklisted_accounts = vec![acc];
// then // then
assert_eq!(ap.accounts_info().unwrap().keys().cloned().collect::<Vec<Address>>(), vec![]); assert_eq!(
ap.accounts_info()
.unwrap()
.keys()
.cloned()
.collect::<Vec<Address>>(),
vec![]
);
assert_eq!(ap.accounts().unwrap(), vec![]); assert_eq!(ap.accounts().unwrap(), vec![]);
} }
} }

View File

@ -16,9 +16,11 @@
//! Address Book Store //! Address Book Store
use std::{fs, fmt, hash, ops}; use std::{
use std::collections::HashMap; collections::HashMap,
use std::path::{Path, PathBuf}; fmt, fs, hash, ops,
path::{Path, PathBuf},
};
use ethkey::Address; use ethkey::Address;
use log::{trace, warn}; use log::{trace, warn};
@ -34,7 +36,7 @@ impl AddressBook {
/// Creates new address book at given directory. /// Creates new address book at given directory.
pub fn new(path: &Path) -> Self { pub fn new(path: &Path) -> Self {
let mut r = AddressBook { let mut r = AddressBook {
cache: DiskMap::new(path, "address_book.json") cache: DiskMap::new(path, "address_book.json"),
}; };
r.cache.revert(AccountMeta::read); r.cache.revert(AccountMeta::read);
r r
@ -43,7 +45,7 @@ impl AddressBook {
/// Creates transient address book (no changes are saved to disk). /// Creates transient address book (no changes are saved to disk).
pub fn transient() -> Self { pub fn transient() -> Self {
AddressBook { AddressBook {
cache: DiskMap::transient() cache: DiskMap::transient(),
} }
} }
@ -59,8 +61,11 @@ impl AddressBook {
/// Sets new name for given address. /// Sets new name for given address.
pub fn set_name(&mut self, a: Address, name: String) { pub fn set_name(&mut self, a: Address, name: String) {
{ {
let x = self.cache.entry(a) let x = self.cache.entry(a).or_insert_with(|| AccountMeta {
.or_insert_with(|| AccountMeta {name: Default::default(), meta: "{}".to_owned(), uuid: None}); name: Default::default(),
meta: "{}".to_owned(),
uuid: None,
});
x.name = name; x.name = name;
} }
self.save(); self.save();
@ -69,8 +74,11 @@ impl AddressBook {
/// Sets new meta for given address. /// Sets new meta for given address.
pub fn set_meta(&mut self, a: Address, meta: String) { pub fn set_meta(&mut self, a: Address, meta: String) {
{ {
let x = self.cache.entry(a) let x = self.cache.entry(a).or_insert_with(|| AccountMeta {
.or_insert_with(|| AccountMeta {name: "Anonymous".to_owned(), meta: Default::default(), uuid: None}); name: "Anonymous".to_owned(),
meta: Default::default(),
uuid: None,
});
x.meta = meta; x.meta = meta;
} }
self.save(); self.save();
@ -122,31 +130,40 @@ impl<K: hash::Hash + Eq, V> DiskMap<K, V> {
map map
} }
fn revert<F, E>(&mut self, read: F) where fn revert<F, E>(&mut self, read: F)
where
F: Fn(fs::File) -> Result<HashMap<K, V>, E>, F: Fn(fs::File) -> Result<HashMap<K, V>, E>,
E: fmt::Display, E: fmt::Display,
{ {
if self.transient { return; } if self.transient {
return;
}
trace!(target: "diskmap", "revert {:?}", self.path); trace!(target: "diskmap", "revert {:?}", self.path);
let _ = fs::File::open(self.path.clone()) let _ = fs::File::open(self.path.clone())
.map_err(|e| trace!(target: "diskmap", "Couldn't open disk map: {}", e)) .map_err(|e| trace!(target: "diskmap", "Couldn't open disk map: {}", e))
.and_then(|f| read(f).map_err(|e| warn!(target: "diskmap", "Couldn't read disk map: {}", e))) .and_then(|f| {
read(f).map_err(|e| warn!(target: "diskmap", "Couldn't read disk map: {}", e))
})
.and_then(|m| { .and_then(|m| {
self.cache = m; self.cache = m;
Ok(()) Ok(())
}); });
} }
fn save<F, E>(&self, write: F) where fn save<F, E>(&self, write: F)
where
F: Fn(&HashMap<K, V>, &mut fs::File) -> Result<(), E>, F: Fn(&HashMap<K, V>, &mut fs::File) -> Result<(), E>,
E: fmt::Display, E: fmt::Display,
{ {
if self.transient { return; } if self.transient {
return;
}
trace!(target: "diskmap", "save {:?}", self.path); trace!(target: "diskmap", "save {:?}", self.path);
let _ = fs::File::create(self.path.clone()) let _ = fs::File::create(self.path.clone())
.map_err(|e| warn!(target: "diskmap", "Couldn't open disk map for writing: {}", e)) .map_err(|e| warn!(target: "diskmap", "Couldn't open disk map for writing: {}", e))
.and_then(|mut f| { .and_then(|mut f| {
write(&self.cache, &mut f).map_err(|e| warn!(target: "diskmap", "Couldn't write to disk map: {}", e)) write(&self.cache, &mut f)
.map_err(|e| warn!(target: "diskmap", "Couldn't write to disk map: {}", e))
}); });
} }
} }
@ -154,9 +171,9 @@ impl<K: hash::Hash + Eq, V> DiskMap<K, V> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::AddressBook; use super::AddressBook;
use crate::account_data::AccountMeta;
use std::collections::HashMap; use std::collections::HashMap;
use tempdir::TempDir; use tempdir::TempDir;
use crate::account_data::AccountMeta;
#[test] #[test]
fn should_save_and_reload_address_book() { fn should_save_and_reload_address_book() {
@ -165,9 +182,20 @@ mod tests {
b.set_name(1.into(), "One".to_owned()); b.set_name(1.into(), "One".to_owned());
b.set_meta(1.into(), "{1:1}".to_owned()); b.set_meta(1.into(), "{1:1}".to_owned());
let b = AddressBook::new(tempdir.path()); let b = AddressBook::new(tempdir.path());
assert_eq!(b.get(), vec![ assert_eq!(
(1, AccountMeta {name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None}) b.get(),
].into_iter().map(|(a, b)| (a.into(), b)).collect::<HashMap<_, _>>()); vec![(
1,
AccountMeta {
name: "One".to_owned(),
meta: "{1:1}".to_owned(),
uuid: None
}
)]
.into_iter()
.map(|(a, b)| (a.into(), b))
.collect::<HashMap<_, _>>()
);
} }
#[test] #[test]
@ -181,9 +209,29 @@ mod tests {
b.remove(2.into()); b.remove(2.into());
let b = AddressBook::new(tempdir.path()); let b = AddressBook::new(tempdir.path());
assert_eq!(b.get(), vec![ assert_eq!(
(1, AccountMeta{name: "One".to_owned(), meta: "{}".to_owned(), uuid: None}), b.get(),
(3, AccountMeta{name: "Three".to_owned(), meta: "{}".to_owned(), uuid: None}), vec![
].into_iter().map(|(a, b)| (a.into(), b)).collect::<HashMap<_, _>>()); (
1,
AccountMeta {
name: "One".to_owned(),
meta: "{}".to_owned(),
uuid: None
}
),
(
3,
AccountMeta {
name: "Three".to_owned(),
meta: "{}".to_owned(),
uuid: None
}
),
]
.into_iter()
.map(|(a, b)| (a.into(), b))
.collect::<HashMap<_, _>>()
);
} }
} }

View File

@ -14,11 +14,11 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
extern crate serde_json;
extern crate ethjson; extern crate ethjson;
extern crate serde_json;
use std::{fs, env, process};
use ethjson::spec::Spec; use ethjson::spec::Spec;
use std::{env, fs, process};
fn quit(s: &str) -> ! { fn quit(s: &str) -> ! {
println!("{}", s); println!("{}", s);
@ -28,9 +28,11 @@ fn quit(s: &str) -> ! {
fn main() { fn main() {
let mut args = env::args(); let mut args = env::args();
if args.len() != 2 { if args.len() != 2 {
quit("You need to specify chainspec.json\n\ quit(
"You need to specify chainspec.json\n\
\n\ \n\
./chainspec <chainspec.json>"); ./chainspec <chainspec.json>",
);
} }
let path = args.nth(1).expect("args.len() == 2; qed"); let path = args.nth(1).expect("args.len() == 2; qed");

View File

@ -14,45 +14,38 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::fmt::{Debug, Formatter, Error as FmtError}; use std::{
use std::io::{BufReader, BufRead}; collections::BTreeMap,
use std::sync::Arc; fmt::{Debug, Error as FmtError, Formatter},
use std::sync::atomic::{AtomicUsize, Ordering}; io::{BufRead, BufReader},
use std::collections::BTreeMap; sync::{
use std::thread; atomic::{AtomicUsize, Ordering},
use std::time; Arc,
},
thread, time,
};
use std::path::PathBuf;
use hash::keccak; use hash::keccak;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{fs::File, path::PathBuf};
use url::Url; use url::Url;
use std::fs::File;
use ws::ws::{ use ws::ws::{
self, self, Error as WsError, ErrorKind as WsErrorKind, Handler, Handshake, Message, Request,
Request, Result as WsResult, Sender,
Handler,
Sender,
Handshake,
Error as WsError,
ErrorKind as WsErrorKind,
Message,
Result as WsResult,
}; };
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde_json::{ use serde_json::{self as json, Error as JsonError, Value as JsonValue};
self as json,
Value as JsonValue, use futures::{done, oneshot, Canceled, Complete, Future};
Error as JsonError,
use jsonrpc_core::{
request::MethodCall,
response::{Failure, Output, Success},
Error as JsonRpcError, Id, Params, Version,
}; };
use futures::{Canceled, Complete, Future, oneshot, done};
use jsonrpc_core::{Id, Version, Params, Error as JsonRpcError};
use jsonrpc_core::request::MethodCall;
use jsonrpc_core::response::{Output, Success, Failure};
use BoxFuture; use BoxFuture;
/// The actual websocket connection handler, passed into the /// The actual websocket connection handler, passed into the
@ -67,11 +60,7 @@ struct RpcHandler {
} }
impl RpcHandler { impl RpcHandler {
fn new( fn new(out: Sender, auth_code: String, complete: Complete<Result<Rpc, RpcError>>) -> Self {
out: Sender,
auth_code: String,
complete: Complete<Result<Rpc, RpcError>>
) -> Self {
RpcHandler { RpcHandler {
out: Some(out), out: Some(out),
auth_code: auth_code, auth_code: auth_code,
@ -85,23 +74,22 @@ impl Handler for RpcHandler {
fn build_request(&mut self, url: &Url) -> WsResult<Request> { fn build_request(&mut self, url: &Url) -> WsResult<Request> {
match Request::from_url(url) { match Request::from_url(url) {
Ok(mut r) => { Ok(mut r) => {
let timestamp = time::UNIX_EPOCH.elapsed().map_err(|err| { let timestamp = time::UNIX_EPOCH
WsError::new(WsErrorKind::Internal, format!("{}", err)) .elapsed()
})?; .map_err(|err| WsError::new(WsErrorKind::Internal, format!("{}", err)))?;
let secs = timestamp.as_secs(); let secs = timestamp.as_secs();
let hashed = keccak(format!("{}:{}", self.auth_code, secs)); let hashed = keccak(format!("{}:{}", self.auth_code, secs));
let proto = format!("{:x}_{}", hashed, secs); let proto = format!("{:x}_{}", hashed, secs);
r.add_protocol(&proto); r.add_protocol(&proto);
Ok(r) Ok(r)
}, }
Err(e) => Err(e) => Err(WsError::new(WsErrorKind::Internal, format!("{}", e))),
Err(WsError::new(WsErrorKind::Internal, format!("{}", e))),
} }
} }
fn on_error(&mut self, err: WsError) { fn on_error(&mut self, err: WsError) {
match self.complete.take() { match self.complete.take() {
Some(c) => match c.send(Err(RpcError::WsError(err))) { Some(c) => match c.send(Err(RpcError::WsError(err))) {
Ok(_) => {}, Ok(_) => {}
Err(_) => warn!(target: "rpc-client", "Unable to notify about error."), Err(_) => warn!(target: "rpc-client", "Unable to notify about error."),
}, },
None => warn!(target: "rpc-client", "unexpected error: {}", err), None => warn!(target: "rpc-client", "unexpected error: {}", err),
@ -119,7 +107,7 @@ impl Handler for RpcHandler {
warn!(target: "rpc-client", "Unable to open a connection.") warn!(target: "rpc-client", "Unable to open a connection.")
} }
Ok(()) Ok(())
}, }
_ => { _ => {
let msg = format!("on_open called twice"); let msg = format!("on_open called twice");
Err(WsError::new(WsErrorKind::Internal, msg)) Err(WsError::new(WsErrorKind::Internal, msg))
@ -131,12 +119,19 @@ impl Handler for RpcHandler {
let response_id; let response_id;
let string = &msg.to_string(); let string = &msg.to_string();
match json::from_str::<Output>(&string) { match json::from_str::<Output>(&string) {
Ok(Output::Success(Success { result, id: Id::Num(id), .. })) => Ok(Output::Success(Success {
{ result,
id: Id::Num(id),
..
})) => {
ret = Ok(result); ret = Ok(result);
response_id = id as usize; response_id = id as usize;
} }
Ok(Output::Failure(Failure { error, id: Id::Num(id), .. })) => { Ok(Output::Failure(Failure {
error,
id: Id::Num(id),
..
})) => {
ret = Err(error); ret = Err(error);
response_id = id as usize; response_id = id as usize;
} }
@ -147,22 +142,24 @@ impl Handler for RpcHandler {
string, string,
e e
); );
return Ok(()) return Ok(());
}, }
_ => { _ => {
warn!( warn!(
target: "rpc-client", target: "rpc-client",
"recieved invalid message: {}", "recieved invalid message: {}",
string string
); );
return Ok(()) return Ok(());
} }
} }
match self.pending.remove(response_id) { match self.pending.remove(response_id) {
Some(c) => if let Err(_) = c.send(ret.map_err(|err| RpcError::JsonRpc(err))) { Some(c) => {
if let Err(_) = c.send(ret.map_err(|err| RpcError::JsonRpc(err))) {
warn!(target: "rpc-client", "Unable to send response.") warn!(target: "rpc-client", "Unable to send response.")
}, }
}
None => warn!( None => warn!(
target: "rpc-client", target: "rpc-client",
"warning: unexpected id: {}", "warning: unexpected id: {}",
@ -175,9 +172,7 @@ impl Handler for RpcHandler {
/// Keeping track of issued requests to be matched up with responses /// Keeping track of issued requests to be matched up with responses
#[derive(Clone)] #[derive(Clone)]
struct Pending( struct Pending(Arc<Mutex<BTreeMap<usize, Complete<Result<JsonValue, RpcError>>>>>);
Arc<Mutex<BTreeMap<usize, Complete<Result<JsonValue, RpcError>>>>>
);
impl Pending { impl Pending {
fn new() -> Self { fn new() -> Self {
@ -186,10 +181,7 @@ impl Pending {
fn insert(&mut self, k: usize, v: Complete<Result<JsonValue, RpcError>>) { fn insert(&mut self, k: usize, v: Complete<Result<JsonValue, RpcError>>) {
self.0.lock().insert(k, v); self.0.lock().insert(k, v);
} }
fn remove( fn remove(&mut self, k: usize) -> Option<Complete<Result<JsonValue, RpcError>>> {
&mut self,
k: usize
) -> Option<Complete<Result<JsonValue, RpcError>>> {
self.0.lock().remove(&k) self.0.lock().remove(&k)
} }
} }
@ -223,9 +215,7 @@ impl Rpc {
} }
/// Non-blocking, returns a future /// Non-blocking, returns a future
pub fn connect( pub fn connect(url: &str, authpath: &PathBuf) -> BoxFuture<Result<Self, RpcError>, Canceled> {
url: &str, authpath: &PathBuf
) -> BoxFuture<Result<Self, RpcError>, Canceled> {
let (c, p) = oneshot::<Result<Self, RpcError>>(); let (c, p) = oneshot::<Result<Self, RpcError>>();
match get_authcode(authpath) { match get_authcode(authpath) {
Err(e) => return Box::new(done(Ok(Err(e)))), Err(e) => return Box::new(done(Ok(Err(e)))),
@ -239,20 +229,18 @@ impl Rpc {
let conn = ws::connect(url, |out| { let conn = ws::connect(url, |out| {
// this will panic if the closure is called twice, // this will panic if the closure is called twice,
// which it should never be. // which it should never be.
let c = once.take() let c = once.take().expect("connection closure called only once");
.expect("connection closure called only once");
RpcHandler::new(out, code.clone(), c) RpcHandler::new(out, code.clone(), c)
}); });
match conn { match conn {
Err(err) => { Err(err) => {
// since ws::connect is only called once, it cannot // since ws::connect is only called once, it cannot
// both fail and succeed. // both fail and succeed.
let c = once.take() let c = once.take().expect("connection closure called only once");
.expect("connection closure called only once");
let _ = c.send(Err(RpcError::WsError(err))); let _ = c.send(Err(RpcError::WsError(err)));
}, }
// c will complete on the `on_open` event in the Handler // c will complete on the `on_open` event in the Handler
_ => () _ => (),
} }
}); });
Box::new(p) Box::new(p)
@ -262,10 +250,13 @@ impl Rpc {
/// Non-blocking, returns a future of the request response /// Non-blocking, returns a future of the request response
pub fn request<T>( pub fn request<T>(
&mut self, method: &'static str, params: Vec<JsonValue> &mut self,
method: &'static str,
params: Vec<JsonValue>,
) -> BoxFuture<Result<T, RpcError>, Canceled> ) -> BoxFuture<Result<T, RpcError>, Canceled>
where T: DeserializeOwned + Send + Sized { where
T: DeserializeOwned + Send + Sized,
{
let (c, p) = oneshot::<Result<JsonValue, RpcError>>(); let (c, p) = oneshot::<Result<JsonValue, RpcError>>();
let id = self.counter.fetch_add(1, Ordering::Relaxed); let id = self.counter.fetch_add(1, Ordering::Relaxed);
@ -278,18 +269,15 @@ impl Rpc {
id: Id::Num(id as u64), id: Id::Num(id as u64),
}; };
let serialized = json::to_string(&request) let serialized = json::to_string(&request).expect("request is serializable");
.expect("request is serializable");
let _ = self.out.send(serialized); let _ = self.out.send(serialized);
Box::new(p.map(|result| { Box::new(p.map(|result| match result {
match result {
Ok(json) => { Ok(json) => {
let t: T = json::from_value(json)?; let t: T = json::from_value(json)?;
Ok(t) Ok(t)
},
Err(err) => Err(err)
} }
Err(err) => Err(err),
})) }))
} }
} }
@ -308,22 +296,14 @@ pub enum RpcError {
impl Debug for RpcError { impl Debug for RpcError {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
match *self { match *self {
RpcError::WrongVersion(ref s) RpcError::WrongVersion(ref s) => write!(f, "Expected version 2.0, got {}", s),
=> write!(f, "Expected version 2.0, got {}", s), RpcError::ParseError(ref err) => write!(f, "ParseError: {}", err),
RpcError::ParseError(ref err) RpcError::MalformedResponse(ref s) => write!(f, "Malformed response: {}", s),
=> write!(f, "ParseError: {}", err), RpcError::JsonRpc(ref json) => write!(f, "JsonRpc error: {:?}", json),
RpcError::MalformedResponse(ref s) RpcError::WsError(ref s) => write!(f, "Websocket error: {}", s),
=> write!(f, "Malformed response: {}", s), RpcError::Canceled(ref s) => write!(f, "Futures error: {:?}", s),
RpcError::JsonRpc(ref json) RpcError::UnexpectedId => write!(f, "Unexpected response id"),
=> write!(f, "JsonRpc error: {:?}", json), RpcError::NoAuthCode => write!(f, "No authcodes available"),
RpcError::WsError(ref s)
=> write!(f, "Websocket error: {}", s),
RpcError::Canceled(ref s)
=> write!(f, "Futures error: {:?}", s),
RpcError::UnexpectedId
=> write!(f, "Unexpected response id"),
RpcError::NoAuthCode
=> write!(f, "No authcodes available"),
} }
} }
} }

View File

@ -21,12 +21,12 @@ extern crate ethereum_types;
extern crate futures; extern crate futures;
extern crate jsonrpc_core; extern crate jsonrpc_core;
extern crate jsonrpc_ws_server as ws; extern crate jsonrpc_ws_server as ws;
extern crate keccak_hash as hash;
extern crate parity_rpc as rpc; extern crate parity_rpc as rpc;
extern crate parking_lot; extern crate parking_lot;
extern crate serde; extern crate serde;
extern crate serde_json; extern crate serde_json;
extern crate url; extern crate url;
extern crate keccak_hash as hash;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
@ -36,15 +36,15 @@ extern crate log;
extern crate matches; extern crate matches;
/// Boxed future response. /// Boxed future response.
pub type BoxFuture<T, E> = Box<futures::Future<Item=T, Error=E> + Send>; pub type BoxFuture<T, E> = Box<futures::Future<Item = T, Error = E> + Send>;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use futures::Future;
use std::path::PathBuf;
use client::{Rpc, RpcError}; use client::{Rpc, RpcError};
use futures::Future;
use rpc; use rpc;
use std::path::PathBuf;
#[test] #[test]
fn test_connection_refused() { fn test_connection_refused() {
@ -53,12 +53,13 @@ mod tests {
let _ = authcodes.generate_new(); let _ = authcodes.generate_new();
authcodes.to_file(&authcodes.path).unwrap(); authcodes.to_file(&authcodes.path).unwrap();
let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port - 1), let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port - 1), &authcodes.path);
&authcodes.path);
let _ = connect.map(|conn| { let _ = connect
.map(|conn| {
assert!(matches!(&conn, &Err(RpcError::WsError(_)))); assert!(matches!(&conn, &Err(RpcError::WsError(_))));
}).wait(); })
.wait();
} }
#[test] #[test]
@ -68,9 +69,11 @@ mod tests {
let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &path); let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &path);
let _ = connect.map(|conn| { let _ = connect
.map(|conn| {
assert!(matches!(&conn, &Err(RpcError::NoAuthCode))); assert!(matches!(&conn, &Err(RpcError::NoAuthCode)));
}).wait(); })
.wait();
} }
#[test] #[test]
@ -80,12 +83,8 @@ mod tests {
let _ = authcodes.generate_new(); let _ = authcodes.generate_new();
authcodes.to_file(&authcodes.path).unwrap(); authcodes.to_file(&authcodes.path).unwrap();
let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &authcodes.path);
&authcodes.path);
let _ = connect.map(|conn| { let _ = connect.map(|conn| assert!(conn.is_ok())).wait();
assert!(conn.is_ok())
}).wait();
} }
} }

View File

@ -16,12 +16,12 @@
use client::{Rpc, RpcError}; use client::{Rpc, RpcError};
use ethereum_types::U256; use ethereum_types::U256;
use rpc::signer::{ConfirmationRequest, TransactionModification, TransactionCondition}; use futures::Canceled;
use rpc::signer::{ConfirmationRequest, TransactionCondition, TransactionModification};
use serde; use serde;
use serde_json::{Value as JsonValue, to_value}; use serde_json::{to_value, Value as JsonValue};
use std::path::PathBuf; use std::path::PathBuf;
use futures::{Canceled}; use BoxFuture;
use {BoxFuture};
pub struct SignerRpc { pub struct SignerRpc {
rpc: Rpc, rpc: Rpc,
@ -29,10 +29,14 @@ pub struct SignerRpc {
impl SignerRpc { impl SignerRpc {
pub fn new(url: &str, authfile: &PathBuf) -> Result<Self, RpcError> { pub fn new(url: &str, authfile: &PathBuf) -> Result<Self, RpcError> {
Ok(SignerRpc { rpc: Rpc::new(&url, authfile)? }) Ok(SignerRpc {
rpc: Rpc::new(&url, authfile)?,
})
} }
pub fn requests_to_confirm(&mut self) -> BoxFuture<Result<Vec<ConfirmationRequest>, RpcError>, Canceled> { pub fn requests_to_confirm(
&mut self,
) -> BoxFuture<Result<Vec<ConfirmationRequest>, RpcError>, Canceled> {
self.rpc.request("signer_requestsToConfirm", vec![]) self.rpc.request("signer_requestsToConfirm", vec![])
} }
@ -42,19 +46,28 @@ impl SignerRpc {
new_gas: Option<U256>, new_gas: Option<U256>,
new_gas_price: Option<U256>, new_gas_price: Option<U256>,
new_condition: Option<Option<TransactionCondition>>, new_condition: Option<Option<TransactionCondition>>,
pwd: &str pwd: &str,
) -> BoxFuture<Result<U256, RpcError>, Canceled> { ) -> BoxFuture<Result<U256, RpcError>, Canceled> {
self.rpc.request("signer_confirmRequest", vec![ self.rpc.request(
"signer_confirmRequest",
vec![
Self::to_value(&format!("{:#x}", id)), Self::to_value(&format!("{:#x}", id)),
Self::to_value(&TransactionModification { sender: None, gas_price: new_gas_price, gas: new_gas, condition: new_condition }), Self::to_value(&TransactionModification {
sender: None,
gas_price: new_gas_price,
gas: new_gas,
condition: new_condition,
}),
Self::to_value(&pwd), Self::to_value(&pwd),
]) ],
)
} }
pub fn reject_request(&mut self, id: U256) -> BoxFuture<Result<bool, RpcError>, Canceled> { pub fn reject_request(&mut self, id: U256) -> BoxFuture<Result<bool, RpcError>, Canceled> {
self.rpc.request("signer_rejectRequest", vec![ self.rpc.request(
JsonValue::String(format!("{:#x}", id)) "signer_rejectRequest",
]) vec![JsonValue::String(format!("{:#x}", id))],
)
} }
fn to_value<T: serde::Serialize>(v: &T) -> JsonValue { fn to_value<T: serde::Serialize>(v: &T) -> JsonValue {

View File

@ -21,135 +21,111 @@ extern crate rpassword;
extern crate parity_rpc as rpc; extern crate parity_rpc as rpc;
extern crate parity_rpc_client as client; extern crate parity_rpc_client as client;
use client::signer_client::SignerRpc;
use ethereum_types::U256; use ethereum_types::U256;
use rpc::signer::ConfirmationRequest; use rpc::signer::ConfirmationRequest;
use client::signer_client::SignerRpc; use std::{
use std::io::{Write, BufRead, BufReader, stdout, stdin}; fs::File,
use std::path::PathBuf; io::{stdin, stdout, BufRead, BufReader, Write},
use std::fs::File; path::PathBuf,
};
use futures::Future; use futures::Future;
fn sign_interactive( fn sign_interactive(signer: &mut SignerRpc, password: &str, request: ConfirmationRequest) {
signer: &mut SignerRpc, print!(
password: &str, "\n{}\nSign this transaction? (y)es/(N)o/(r)eject: ",
request: ConfirmationRequest request
) { );
print!("\n{}\nSign this transaction? (y)es/(N)o/(r)eject: ", request);
let _ = stdout().flush(); let _ = stdout().flush();
match BufReader::new(stdin()).lines().next() { match BufReader::new(stdin()).lines().next() {
Some(Ok(line)) => { Some(Ok(line)) => match line.to_lowercase().chars().nth(0) {
match line.to_lowercase().chars().nth(0) { Some('y') => match sign_transaction(signer, request.id, password) {
Some('y') => {
match sign_transaction(signer, request.id, password) {
Ok(s) | Err(s) => println!("{}", s), Ok(s) | Err(s) => println!("{}", s),
} },
} Some('r') => match reject_transaction(signer, request.id) {
Some('r') => {
match reject_transaction(signer, request.id) {
Ok(s) | Err(s) => println!("{}", s), Ok(s) | Err(s) => println!("{}", s),
} },
} _ => (),
_ => () },
} _ => println!("Could not read from stdin"),
}
_ => println!("Could not read from stdin")
} }
} }
fn sign_transactions( fn sign_transactions(signer: &mut SignerRpc, password: String) -> Result<String, String> {
signer: &mut SignerRpc, signer
password: String .requests_to_confirm()
) -> Result<String, String> { .map(|reqs| match reqs {
signer.requests_to_confirm().map(|reqs| { Ok(ref reqs) if reqs.is_empty() => Ok("No transactions in signing queue".to_owned()),
match reqs {
Ok(ref reqs) if reqs.is_empty() => {
Ok("No transactions in signing queue".to_owned())
}
Ok(reqs) => { Ok(reqs) => {
for r in reqs { for r in reqs {
sign_interactive(signer, &password, r) sign_interactive(signer, &password, r)
} }
Ok("".to_owned()) Ok("".to_owned())
} }
Err(err) => { Err(err) => Err(format!("error: {:?}", err)),
Err(format!("error: {:?}", err)) })
} .map_err(|err| format!("{:?}", err))
} .wait()?
}).map_err(|err| {
format!("{:?}", err)
}).wait()?
} }
fn list_transactions(signer: &mut SignerRpc) -> Result<String, String> { fn list_transactions(signer: &mut SignerRpc) -> Result<String, String> {
signer.requests_to_confirm().map(|reqs| { signer
match reqs { .requests_to_confirm()
Ok(ref reqs) if reqs.is_empty() => { .map(|reqs| match reqs {
Ok("No transactions in signing queue".to_owned()) Ok(ref reqs) if reqs.is_empty() => Ok("No transactions in signing queue".to_owned()),
} Ok(ref reqs) => Ok(format!(
Ok(ref reqs) => { "Transaction queue:\n{}",
Ok(format!("Transaction queue:\n{}", reqs reqs.iter()
.iter()
.map(|r| format!("{}", r)) .map(|r| format!("{}", r))
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("\n"))) .join("\n")
} )),
Err(err) => { Err(err) => Err(format!("error: {:?}", err)),
Err(format!("error: {:?}", err)) })
} .map_err(|err| format!("{:?}", err))
} .wait()?
}).map_err(|err| {
format!("{:?}", err)
}).wait()?
} }
fn sign_transaction( fn sign_transaction(signer: &mut SignerRpc, id: U256, password: &str) -> Result<String, String> {
signer: &mut SignerRpc, id: U256, password: &str signer
) -> Result<String, String> { .confirm_request(id, None, None, None, password)
signer.confirm_request(id, None, None, None, password).map(|res| { .map(|res| match res {
match res {
Ok(u) => Ok(format!("Signed transaction id: {:#x}", u)), Ok(u) => Ok(format!("Signed transaction id: {:#x}", u)),
Err(e) => Err(format!("{:?}", e)), Err(e) => Err(format!("{:?}", e)),
} })
}).map_err(|err| { .map_err(|err| format!("{:?}", err))
format!("{:?}", err) .wait()?
}).wait()?
} }
fn reject_transaction( fn reject_transaction(signer: &mut SignerRpc, id: U256) -> Result<String, String> {
signer: &mut SignerRpc, id: U256) -> Result<String, String> signer
{ .reject_request(id)
signer.reject_request(id).map(|res| { .map(|res| match res {
match res {
Ok(true) => Ok(format!("Rejected transaction id {:#x}", id)), Ok(true) => Ok(format!("Rejected transaction id {:#x}", id)),
Ok(false) => Err(format!("No such request")), Ok(false) => Err(format!("No such request")),
Err(e) => Err(format!("{:?}", e)), Err(e) => Err(format!("{:?}", e)),
} })
}).map_err(|err| { .map_err(|err| format!("{:?}", err))
format!("{:?}", err) .wait()?
}).wait()?
} }
// cmds // cmds
pub fn signer_list( pub fn signer_list(signerport: u16, authfile: PathBuf) -> Result<String, String> {
signerport: u16, authfile: PathBuf
) -> Result<String, String> {
let addr = &format!("ws://127.0.0.1:{}", signerport); let addr = &format!("ws://127.0.0.1:{}", signerport);
let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| { let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| format!("{:?}", err))?;
format!("{:?}", err)
})?;
list_transactions(&mut signer) list_transactions(&mut signer)
} }
pub fn signer_reject( pub fn signer_reject(
id: Option<usize>, signerport: u16, authfile: PathBuf id: Option<usize>,
signerport: u16,
authfile: PathBuf,
) -> Result<String, String> { ) -> Result<String, String> {
let id = id.ok_or(format!("id required for signer reject"))?; let id = id.ok_or(format!("id required for signer reject"))?;
let addr = &format!("ws://127.0.0.1:{}", signerport); let addr = &format!("ws://127.0.0.1:{}", signerport);
let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| { let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| format!("{:?}", err))?;
format!("{:?}", err)
})?;
reject_transaction(&mut signer, U256::from(id)) reject_transaction(&mut signer, U256::from(id))
} }
@ -157,22 +133,17 @@ pub fn signer_sign(
id: Option<usize>, id: Option<usize>,
pwfile: Option<PathBuf>, pwfile: Option<PathBuf>,
signerport: u16, signerport: u16,
authfile: PathBuf authfile: PathBuf,
) -> Result<String, String> { ) -> Result<String, String> {
let password; let password;
match pwfile { match pwfile {
Some(pwfile) => { Some(pwfile) => match File::open(pwfile) {
match File::open(pwfile) { Ok(fd) => match BufReader::new(fd).lines().next() {
Ok(fd) => {
match BufReader::new(fd).lines().next() {
Some(Ok(line)) => password = line, Some(Ok(line)) => password = line,
_ => return Err(format!("No password in file")) _ => return Err(format!("No password in file")),
} },
Err(e) => return Err(format!("Could not open password file: {}", e)),
}, },
Err(e) =>
return Err(format!("Could not open password file: {}", e))
}
}
None => { None => {
password = match rpassword::prompt_password_stdout("Password: ") { password = match rpassword::prompt_password_stdout("Password: ") {
Ok(p) => p, Ok(p) => p,
@ -182,16 +153,10 @@ pub fn signer_sign(
} }
let addr = &format!("ws://127.0.0.1:{}", signerport); let addr = &format!("ws://127.0.0.1:{}", signerport);
let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| { let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| format!("{:?}", err))?;
format!("{:?}", err)
})?;
match id { match id {
Some(id) => { Some(id) => sign_transaction(&mut signer, U256::from(id), &password),
sign_transaction(&mut signer, U256::from(id), &password) None => sign_transactions(&mut signer, password),
},
None => {
sign_transactions(&mut signer, password)
}
} }
} }

View File

@ -21,9 +21,10 @@ extern crate ethash;
use criterion::Criterion; use criterion::Criterion;
use ethash::{NodeCacheBuilder, OptimizeFor}; use ethash::{NodeCacheBuilder, OptimizeFor};
const HASH: [u8; 32] = [0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, const HASH: [u8; 32] = [
0xe4, 0x0a, 0xb3, 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3, 0x35, 0x8a,
0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, 0x05, 0x52, 0x7d, 0x72]; 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, 0x05, 0x52, 0x7d, 0x72,
];
const NONCE: u64 = 0xd7b3ac70a301a249; const NONCE: u64 = 0xd7b3ac70a301a249;
criterion_group!( criterion_group!(
@ -43,7 +44,9 @@ fn bench_light_compute_memmap(b: &mut Criterion) {
let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
let light = builder.light(&env::temp_dir(), 486382); let light = builder.light(&env::temp_dir(), 486382);
b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| light.compute(&HASH, NONCE, u64::max_value()))); b.bench_function("bench_light_compute_memmap", move |b| {
b.iter(|| light.compute(&HASH, NONCE, u64::max_value()))
});
} }
fn bench_light_compute_memory(b: &mut Criterion) { fn bench_light_compute_memory(b: &mut Criterion) {
@ -52,27 +55,33 @@ fn bench_light_compute_memory(b: &mut Criterion) {
let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value());
let light = builder.light(&env::temp_dir(), 486382); let light = builder.light(&env::temp_dir(), 486382);
b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| light.compute(&HASH, NONCE, u64::max_value()))); b.bench_function("bench_light_compute_memmap", move |b| {
b.iter(|| light.compute(&HASH, NONCE, u64::max_value()))
});
} }
fn bench_light_new_round_trip_memmap(b: &mut Criterion) { fn bench_light_new_round_trip_memmap(b: &mut Criterion) {
use std::env; use std::env;
b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| { b.bench_function("bench_light_compute_memmap", move |b| {
b.iter(|| {
let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
let light = builder.light(&env::temp_dir(), 486382); let light = builder.light(&env::temp_dir(), 486382);
light.compute(&HASH, NONCE, u64::max_value()); light.compute(&HASH, NONCE, u64::max_value());
})); })
});
} }
fn bench_light_new_round_trip_memory(b: &mut Criterion) { fn bench_light_new_round_trip_memory(b: &mut Criterion) {
use std::env; use std::env;
b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| { b.bench_function("bench_light_compute_memmap", move |b| {
b.iter(|| {
let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value());
let light = builder.light(&env::temp_dir(), 486382); let light = builder.light(&env::temp_dir(), 486382);
light.compute(&HASH, NONCE, u64::max_value()); light.compute(&HASH, NONCE, u64::max_value());
})); })
});
} }
fn bench_light_from_file_round_trip_memory(b: &mut Criterion) { fn bench_light_from_file_round_trip_memory(b: &mut Criterion) {
@ -86,11 +95,13 @@ fn bench_light_from_file_round_trip_memory(b: &mut Criterion) {
dummy.to_file().unwrap(); dummy.to_file().unwrap();
} }
b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| { b.bench_function("bench_light_compute_memmap", move |b| {
b.iter(|| {
let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value());
let light = builder.light_from_file(&dir, 486382).unwrap(); let light = builder.light_from_file(&dir, 486382).unwrap();
light.compute(&HASH, NONCE, u64::max_value()); light.compute(&HASH, NONCE, u64::max_value());
})); })
});
} }
fn bench_light_from_file_round_trip_memmap(b: &mut Criterion) { fn bench_light_from_file_round_trip_memmap(b: &mut Criterion) {
@ -105,9 +116,11 @@ fn bench_light_from_file_round_trip_memmap(b: &mut Criterion) {
dummy.to_file().unwrap(); dummy.to_file().unwrap();
} }
b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| { b.bench_function("bench_light_compute_memmap", move |b| {
b.iter(|| {
let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
let light = builder.light_from_file(&dir, 486382).unwrap(); let light = builder.light_from_file(&dir, 486382).unwrap();
light.compute(&HASH, NONCE, u64::max_value()); light.compute(&HASH, NONCE, u64::max_value());
})); })
});
} }

View File

@ -7,16 +7,16 @@ extern crate tempdir;
use criterion::Criterion; use criterion::Criterion;
use ethash::progpow; use ethash::progpow;
use tempdir::TempDir; use ethash::{compute::light_compute, NodeCacheBuilder, OptimizeFor};
use rustc_hex::FromHex; use rustc_hex::FromHex;
use ethash::{NodeCacheBuilder, OptimizeFor}; use tempdir::TempDir;
use ethash::compute::light_compute;
fn bench_hashimoto_light(c: &mut Criterion) { fn bench_hashimoto_light(c: &mut Criterion) {
let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
let tempdir = TempDir::new("").unwrap(); let tempdir = TempDir::new("").unwrap();
let light = builder.light(&tempdir.path(), 1); let light = builder.light(&tempdir.path(), 1);
let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f").unwrap(); let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f")
.unwrap();
let mut hash = [0; 32]; let mut hash = [0; 32];
hash.copy_from_slice(&h); hash.copy_from_slice(&h);
@ -30,20 +30,15 @@ fn bench_progpow_light(c: &mut Criterion) {
let tempdir = TempDir::new("").unwrap(); let tempdir = TempDir::new("").unwrap();
let cache = builder.new_cache(tempdir.into_path(), 0); let cache = builder.new_cache(tempdir.into_path(), 0);
let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f").unwrap(); let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f")
.unwrap();
let mut hash = [0; 32]; let mut hash = [0; 32];
hash.copy_from_slice(&h); hash.copy_from_slice(&h);
c.bench_function("progpow_light", move |b| { c.bench_function("progpow_light", move |b| {
b.iter(|| { b.iter(|| {
let c_dag = progpow::generate_cdag(cache.as_ref()); let c_dag = progpow::generate_cdag(cache.as_ref());
progpow::progpow( progpow::progpow(hash, 0, 0, cache.as_ref(), &c_dag);
hash,
0,
0,
cache.as_ref(),
&c_dag,
);
}) })
}); });
} }
@ -54,19 +49,14 @@ fn bench_progpow_optimal_light(c: &mut Criterion) {
let cache = builder.new_cache(tempdir.into_path(), 0); let cache = builder.new_cache(tempdir.into_path(), 0);
let c_dag = progpow::generate_cdag(cache.as_ref()); let c_dag = progpow::generate_cdag(cache.as_ref());
let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f").unwrap(); let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f")
.unwrap();
let mut hash = [0; 32]; let mut hash = [0; 32];
hash.copy_from_slice(&h); hash.copy_from_slice(&h);
c.bench_function("progpow_optimal_light", move |b| { c.bench_function("progpow_optimal_light", move |b| {
b.iter(|| { b.iter(|| {
progpow::progpow( progpow::progpow(hash, 0, 0, cache.as_ref(), &c_dag);
hash,
0,
0,
cache.as_ref(),
&c_dag,
);
}) })
}); });
} }
@ -77,7 +67,8 @@ fn bench_keccak_f800_long(c: &mut Criterion) {
}); });
} }
criterion_group!(benches, criterion_group!(
benches,
bench_hashimoto_light, bench_hashimoto_light,
bench_progpow_light, bench_progpow_light,
bench_progpow_optimal_light, bench_progpow_optimal_light,

View File

@ -16,19 +16,21 @@
use compute::Light; use compute::Light;
use either::Either; use either::Either;
use keccak::{H256, keccak_512}; use keccak::{keccak_512, H256};
use memmap::MmapMut; use memmap::MmapMut;
use parking_lot::Mutex; use parking_lot::Mutex;
use seed_compute::SeedHashCompute; use seed_compute::SeedHashCompute;
use shared::{ETHASH_CACHE_ROUNDS, NODE_BYTES, NODE_DWORDS, Node, epoch, get_cache_size, to_hex}; use shared::{epoch, get_cache_size, to_hex, Node, ETHASH_CACHE_ROUNDS, NODE_BYTES, NODE_DWORDS};
use std::borrow::Cow; use std::{
use std::fs; borrow::Cow,
use std::io::{self, Read, Write}; fs,
use std::path::{Path, PathBuf}; io::{self, Read, Write},
use std::slice; path::{Path, PathBuf},
use std::sync::Arc; slice,
sync::Arc,
};
type Cache = Either<Vec<Node>, MmapMut>; type Cache = Either<Vec<Node>, MmapMut>;
@ -59,9 +61,9 @@ fn new_buffer(path: &Path, num_nodes: usize, ident: &H256, optimize_for: Optimiz
OptimizeFor::Memory => make_memmapped_cache(path, num_nodes, ident).ok(), OptimizeFor::Memory => make_memmapped_cache(path, num_nodes, ident).ok(),
}; };
memmap.map(Either::Right).unwrap_or_else(|| { memmap
Either::Left(make_memory_cache(num_nodes, ident)) .map(Either::Right)
}) .unwrap_or_else(|| Either::Left(make_memory_cache(num_nodes, ident)))
} }
#[derive(Clone)] #[derive(Clone)]
@ -94,7 +96,7 @@ impl NodeCacheBuilder {
NodeCacheBuilder { NodeCacheBuilder {
seedhash: Arc::new(Mutex::new(SeedHashCompute::default())), seedhash: Arc::new(Mutex::new(SeedHashCompute::default())),
optimize_for: optimize_for.into().unwrap_or_default(), optimize_for: optimize_for.into().unwrap_or_default(),
progpow_transition progpow_transition,
} }
} }
@ -169,9 +171,10 @@ impl NodeCache {
} }
pub fn flush(&mut self) -> io::Result<()> { pub fn flush(&mut self) -> io::Result<()> {
if let Some(last) = self.epoch.checked_sub(2).map(|ep| { if let Some(last) = self
cache_path(self.cache_dir.as_ref(), &self.builder.epoch_to_ident(ep)) .epoch
}) .checked_sub(2)
.map(|ep| cache_path(self.cache_dir.as_ref(), &self.builder.epoch_to_ident(ep)))
{ {
fs::remove_file(last).unwrap_or_else(|error| match error.kind() { fs::remove_file(last).unwrap_or_else(|error| match error.kind() {
io::ErrorKind::NotFound => (), io::ErrorKind::NotFound => (),
@ -234,9 +237,7 @@ fn consume_cache(cache: &mut Cache, path: &Path) -> io::Result<()> {
file.write_all(buf).map(|_| ()) file.write_all(buf).map(|_| ())
} }
Either::Right(ref mmap) => { Either::Right(ref mmap) => mmap.flush(),
mmap.flush()
}
} }
} }
@ -244,25 +245,31 @@ fn cache_from_path(path: &Path, optimize_for: OptimizeFor) -> io::Result<Cache>
let memmap = match optimize_for { let memmap = match optimize_for {
OptimizeFor::Cpu => None, OptimizeFor::Cpu => None,
OptimizeFor::Memory => { OptimizeFor::Memory => {
let file = fs::OpenOptions::new().read(true).write(true).create(true).open(path)?; let file = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)?;
unsafe { MmapMut::map_mut(&file).ok() } unsafe { MmapMut::map_mut(&file).ok() }
}, }
}; };
memmap.map(Either::Right).ok_or(()).or_else(|_| { memmap
read_from_path(path).map(Either::Left) .map(Either::Right)
}) .ok_or(())
.or_else(|_| read_from_path(path).map(Either::Left))
} }
fn read_from_path(path: &Path) -> io::Result<Vec<Node>> { fn read_from_path(path: &Path) -> io::Result<Vec<Node>> {
use std::fs::File; use std::{fs::File, mem};
use std::mem;
let mut file = File::open(path)?; let mut file = File::open(path)?;
let mut nodes: Vec<u8> = Vec::with_capacity(file.metadata().map(|m| m.len() as _).unwrap_or( let mut nodes: Vec<u8> = Vec::with_capacity(
NODE_BYTES * 1_000_000, file.metadata()
)); .map(|m| m.len() as _)
.unwrap_or(NODE_BYTES * 1_000_000),
);
file.read_to_end(&mut nodes)?; file.read_to_end(&mut nodes)?;
nodes.shrink_to_fit(); nodes.shrink_to_fit();

View File

@ -19,15 +19,14 @@
// TODO: fix endianess for big endian // TODO: fix endianess for big endian
use keccak::{keccak_512, keccak_256, H256};
use cache::{NodeCache, NodeCacheBuilder}; use cache::{NodeCache, NodeCacheBuilder};
use progpow::{CDag, generate_cdag, progpow, keccak_f800_short, keccak_f800_long}; use keccak::{keccak_256, keccak_512, H256};
use progpow::{generate_cdag, keccak_f800_long, keccak_f800_short, progpow, CDag};
use seed_compute::SeedHashCompute; use seed_compute::SeedHashCompute;
use shared::*; use shared::*;
use std::io; use std::io;
use std::{mem, ptr}; use std::{mem, path::Path, ptr};
use std::path::Path;
const MIX_WORDS: usize = ETHASH_MIX_BYTES / 4; const MIX_WORDS: usize = ETHASH_MIX_BYTES / 4;
const MIX_NODES: usize = MIX_WORDS / NODE_WORDS; const MIX_NODES: usize = MIX_WORDS / NODE_WORDS;
@ -68,7 +67,11 @@ impl Light {
Algorithm::Hashimoto Algorithm::Hashimoto
}; };
Light { block_number, cache, algorithm } Light {
block_number,
cache,
algorithm,
}
} }
/// Calculate the light boundary data /// Calculate the light boundary data
@ -86,10 +89,9 @@ impl Light {
); );
ProofOfWork { value, mix_hash } ProofOfWork { value, mix_hash }
}, }
Algorithm::Hashimoto => light_compute(self, header_hash, nonce), Algorithm::Hashimoto => light_compute(self, header_hash, nonce),
} }
} }
pub fn from_file_with_builder( pub fn from_file_with_builder(
@ -106,7 +108,11 @@ impl Light {
Algorithm::Hashimoto Algorithm::Hashimoto
}; };
Ok(Light { block_number, cache, algorithm }) Ok(Light {
block_number,
cache,
algorithm,
})
} }
pub fn to_file(&mut self) -> io::Result<&Path> { pub fn to_file(&mut self) -> io::Result<&Path> {
@ -129,7 +135,12 @@ fn fnv_hash(x: u32, y: u32) -> u32 {
/// `nonce` The block's nonce /// `nonce` The block's nonce
/// `mix_hash` The mix digest hash /// `mix_hash` The mix digest hash
/// Boundary recovered from mix hash /// Boundary recovered from mix hash
pub fn quick_get_difficulty(header_hash: &H256, nonce: u64, mix_hash: &H256, progpow: bool) -> H256 { pub fn quick_get_difficulty(
header_hash: &H256,
nonce: u64,
mix_hash: &H256,
progpow: bool,
) -> H256 {
unsafe { unsafe {
if progpow { if progpow {
let seed = keccak_f800_short(*header_hash, nonce, [0u32; 8]); let seed = keccak_f800_short(*header_hash, nonce, [0u32; 8]);
@ -173,14 +184,14 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
// We use explicit lifetimes to ensure that val's borrow is invalidated until the // We use explicit lifetimes to ensure that val's borrow is invalidated until the
// transmuted val dies. // transmuted val dies.
unsafe fn make_const_array<T, U>(val: &mut [T]) -> &mut [U; $n] { unsafe fn make_const_array<T, U>(val: &mut [T]) -> &mut [U; $n] {
use ::std::mem; use std::mem;
debug_assert_eq!(val.len() * mem::size_of::<T>(), $n * mem::size_of::<U>()); debug_assert_eq!(val.len() * mem::size_of::<T>(), $n * mem::size_of::<U>());
&mut *(val.as_mut_ptr() as *mut [U; $n]) &mut *(val.as_mut_ptr() as *mut [U; $n])
} }
make_const_array($value) make_const_array($value)
}} }};
} }
#[repr(C)] #[repr(C)]
@ -318,7 +329,10 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
buf.compress_bytes buf.compress_bytes
}; };
ProofOfWork { mix_hash: mix_hash, value: value } ProofOfWork {
mix_hash: mix_hash,
value: value,
}
} }
// TODO: Use the `simd` crate // TODO: Use the `simd` crate
@ -331,8 +345,8 @@ pub fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node {
debug_assert_eq!(NODE_WORDS, 16); debug_assert_eq!(NODE_WORDS, 16);
for i in 0..ETHASH_DATASET_PARENTS as u32 { for i in 0..ETHASH_DATASET_PARENTS as u32 {
let parent_index = fnv_hash(node_index ^ i, ret.as_words()[i as usize % NODE_WORDS]) % let parent_index = fnv_hash(node_index ^ i, ret.as_words()[i as usize % NODE_WORDS])
num_parent_nodes as u32; % num_parent_nodes as u32;
let parent = &cache[parent_index as usize]; let parent = &cache[parent_index as usize];
unroll! { unroll! {
@ -363,7 +377,10 @@ mod test {
assert_eq!(16907456usize, get_cache_size(ETHASH_EPOCH_LENGTH + 1)); assert_eq!(16907456usize, get_cache_size(ETHASH_EPOCH_LENGTH + 1));
assert_eq!(284950208usize, get_cache_size(2046 * ETHASH_EPOCH_LENGTH)); assert_eq!(284950208usize, get_cache_size(2046 * ETHASH_EPOCH_LENGTH));
assert_eq!(285081536usize, get_cache_size(2047 * ETHASH_EPOCH_LENGTH)); assert_eq!(285081536usize, get_cache_size(2047 * ETHASH_EPOCH_LENGTH));
assert_eq!(285081536usize, get_cache_size(2048 * ETHASH_EPOCH_LENGTH - 1)); assert_eq!(
285081536usize,
get_cache_size(2048 * ETHASH_EPOCH_LENGTH - 1)
);
} }
#[test] #[test]
@ -396,7 +413,10 @@ mod test {
0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a,
0xe9, 0x7e, 0x53, 0x84, 0xe9, 0x7e, 0x53, 0x84,
]; ];
assert_eq!(quick_get_difficulty(&hash, nonce, &mix_hash, false)[..], boundary_good[..]); assert_eq!(
quick_get_difficulty(&hash, nonce, &mix_hash, false)[..],
boundary_good[..]
);
let boundary_bad = [ let boundary_bad = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3a, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3a, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2,
0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a,
@ -436,16 +456,28 @@ mod test {
fn test_drop_old_data() { fn test_drop_old_data() {
let tempdir = TempDir::new("").unwrap(); let tempdir = TempDir::new("").unwrap();
let builder = NodeCacheBuilder::new(None, u64::max_value()); let builder = NodeCacheBuilder::new(None, u64::max_value());
let first = builder.light(tempdir.path(), 0).to_file().unwrap().to_owned(); let first = builder
.light(tempdir.path(), 0)
.to_file()
.unwrap()
.to_owned();
let second = builder.light(tempdir.path(), ETHASH_EPOCH_LENGTH).to_file().unwrap().to_owned(); let second = builder
.light(tempdir.path(), ETHASH_EPOCH_LENGTH)
.to_file()
.unwrap()
.to_owned();
assert!(fs::metadata(&first).is_ok()); assert!(fs::metadata(&first).is_ok());
let _ = builder.light(tempdir.path(), ETHASH_EPOCH_LENGTH * 2).to_file(); let _ = builder
.light(tempdir.path(), ETHASH_EPOCH_LENGTH * 2)
.to_file();
assert!(fs::metadata(&first).is_err()); assert!(fs::metadata(&first).is_err());
assert!(fs::metadata(&second).is_ok()); assert!(fs::metadata(&second).is_ok());
let _ = builder.light(tempdir.path(), ETHASH_EPOCH_LENGTH * 3).to_file(); let _ = builder
.light(tempdir.path(), ETHASH_EPOCH_LENGTH * 3)
.to_file();
assert!(fs::metadata(&second).is_err()); assert!(fs::metadata(&second).is_err());
} }
} }

View File

@ -31,7 +31,12 @@ pub mod keccak_512 {
// This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This // This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This
// means that we can reuse the input buffer for both input and output. // means that we can reuse the input buffer for both input and output.
unsafe { unsafe {
hash::keccak_512_unchecked(input.as_mut_ptr(), input.len(), input.as_ptr(), input.len()); hash::keccak_512_unchecked(
input.as_mut_ptr(),
input.len(),
input.as_ptr(),
input.len(),
);
} }
} }
} }
@ -50,7 +55,12 @@ pub mod keccak_256 {
// This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This // This is safe since `keccak_*` uses an internal buffer and copies the result to the output. This
// means that we can reuse the input buffer for both input and output. // means that we can reuse the input buffer for both input and output.
unsafe { unsafe {
hash::keccak_256_unchecked(input.as_mut_ptr(), input.len(), input.as_ptr(), input.len()); hash::keccak_256_unchecked(
input.as_mut_ptr(),
input.len(),
input.as_ptr(),
input.len(),
);
} }
} }
} }

View File

@ -39,9 +39,9 @@ pub mod compute;
#[cfg(not(feature = "bench"))] #[cfg(not(feature = "bench"))]
mod compute; mod compute;
mod seed_compute;
mod cache; mod cache;
mod keccak; mod keccak;
mod seed_compute;
mod shared; mod shared;
#[cfg(feature = "bench")] #[cfg(feature = "bench")]
@ -50,15 +50,17 @@ pub mod progpow;
mod progpow; mod progpow;
pub use cache::{NodeCacheBuilder, OptimizeFor}; pub use cache::{NodeCacheBuilder, OptimizeFor};
pub use compute::{ProofOfWork, quick_get_difficulty, slow_hash_block_number};
use compute::Light; use compute::Light;
pub use compute::{quick_get_difficulty, slow_hash_block_number, ProofOfWork};
use ethereum_types::{U256, U512}; use ethereum_types::{U256, U512};
use keccak::H256; use keccak::H256;
use parking_lot::Mutex; use parking_lot::Mutex;
pub use seed_compute::SeedHashCompute; pub use seed_compute::SeedHashCompute;
pub use shared::ETHASH_EPOCH_LENGTH; pub use shared::ETHASH_EPOCH_LENGTH;
use std::mem; use std::{
use std::path::{Path, PathBuf}; mem,
path::{Path, PathBuf},
};
use std::sync::Arc; use std::sync::Arc;
@ -79,10 +81,17 @@ pub struct EthashManager {
impl EthashManager { impl EthashManager {
/// Create a new new instance of ethash manager /// Create a new new instance of ethash manager
pub fn new<T: Into<Option<OptimizeFor>>>(cache_dir: &Path, optimize_for: T, progpow_transition: u64) -> EthashManager { pub fn new<T: Into<Option<OptimizeFor>>>(
cache_dir: &Path,
optimize_for: T,
progpow_transition: u64,
) -> EthashManager {
EthashManager { EthashManager {
cache_dir: cache_dir.to_path_buf(), cache_dir: cache_dir.to_path_buf(),
nodecache_builder: NodeCacheBuilder::new(optimize_for.into().unwrap_or_default(), progpow_transition), nodecache_builder: NodeCacheBuilder::new(
optimize_for.into().unwrap_or_default(),
progpow_transition,
),
progpow_transition: progpow_transition, progpow_transition: progpow_transition,
cache: Mutex::new(LightCache { cache: Mutex::new(LightCache {
recent_epoch: None, recent_epoch: None,
@ -131,17 +140,15 @@ impl EthashManager {
match light { match light {
None => { None => {
let light = match self.nodecache_builder.light_from_file( let light = match self
&self.cache_dir, .nodecache_builder
block_number, .light_from_file(&self.cache_dir, block_number)
) { {
Ok(light) => Arc::new(light), Ok(light) => Arc::new(light),
Err(e) => { Err(e) => {
debug!("Light cache file not found for {}:{}", block_number, e); debug!("Light cache file not found for {}:{}", block_number, e);
let mut light = self.nodecache_builder.light( let mut light =
&self.cache_dir, self.nodecache_builder.light(&self.cache_dir, block_number);
block_number,
);
if let Err(e) = light.to_file() { if let Err(e) = light.to_file() {
warn!("Light cache file write error: {}", e); warn!("Light cache file write error: {}", e);
} }
@ -206,10 +213,22 @@ fn test_difficulty_to_boundary() {
use ethereum_types::H256; use ethereum_types::H256;
use std::str::FromStr; use std::str::FromStr;
assert_eq!(difficulty_to_boundary(&U256::from(1)), H256::from(U256::max_value())); assert_eq!(
assert_eq!(difficulty_to_boundary(&U256::from(2)), H256::from_str("8000000000000000000000000000000000000000000000000000000000000000").unwrap()); difficulty_to_boundary(&U256::from(1)),
assert_eq!(difficulty_to_boundary(&U256::from(4)), H256::from_str("4000000000000000000000000000000000000000000000000000000000000000").unwrap()); H256::from(U256::max_value())
assert_eq!(difficulty_to_boundary(&U256::from(32)), H256::from_str("0800000000000000000000000000000000000000000000000000000000000000").unwrap()); );
assert_eq!(
difficulty_to_boundary(&U256::from(2)),
H256::from_str("8000000000000000000000000000000000000000000000000000000000000000").unwrap()
);
assert_eq!(
difficulty_to_boundary(&U256::from(4)),
H256::from_str("4000000000000000000000000000000000000000000000000000000000000000").unwrap()
);
assert_eq!(
difficulty_to_boundary(&U256::from(32)),
H256::from_str("0800000000000000000000000000000000000000000000000000000000000000").unwrap()
);
} }
#[test] #[test]
@ -219,10 +238,22 @@ fn test_difficulty_to_boundary_regression() {
// the last bit was originally being truncated when performing the conversion // the last bit was originally being truncated when performing the conversion
// https://github.com/paritytech/parity-ethereum/issues/8397 // https://github.com/paritytech/parity-ethereum/issues/8397
for difficulty in 1..9 { for difficulty in 1..9 {
assert_eq!(U256::from(difficulty), boundary_to_difficulty(&difficulty_to_boundary(&difficulty.into()))); assert_eq!(
assert_eq!(H256::from(difficulty), difficulty_to_boundary(&boundary_to_difficulty(&difficulty.into()))); U256::from(difficulty),
assert_eq!(U256::from(difficulty), boundary_to_difficulty(&boundary_to_difficulty(&difficulty.into()).into())); boundary_to_difficulty(&difficulty_to_boundary(&difficulty.into()))
assert_eq!(H256::from(difficulty), difficulty_to_boundary(&difficulty_to_boundary(&difficulty.into()).into())); );
assert_eq!(
H256::from(difficulty),
difficulty_to_boundary(&boundary_to_difficulty(&difficulty.into()))
);
assert_eq!(
U256::from(difficulty),
boundary_to_difficulty(&boundary_to_difficulty(&difficulty.into()).into())
);
assert_eq!(
H256::from(difficulty),
difficulty_to_boundary(&difficulty_to_boundary(&difficulty.into()).into())
);
} }
} }

View File

@ -14,9 +14,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use compute::{FNV_PRIME, calculate_dag_item}; use compute::{calculate_dag_item, FNV_PRIME};
use keccak::H256; use keccak::H256;
use shared::{ETHASH_ACCESSES, ETHASH_MIX_BYTES, Node, get_data_size}; use shared::{get_data_size, Node, ETHASH_ACCESSES, ETHASH_MIX_BYTES};
const PROGPOW_CACHE_BYTES: usize = 16 * 1024; const PROGPOW_CACHE_BYTES: usize = 16 * 1024;
const PROGPOW_CACHE_WORDS: usize = PROGPOW_CACHE_BYTES / 4; const PROGPOW_CACHE_WORDS: usize = PROGPOW_CACHE_BYTES / 4;
@ -32,20 +32,17 @@ const PROGPOW_REGS: usize = 32;
const FNV_HASH: u32 = 0x811c9dc5; const FNV_HASH: u32 = 0x811c9dc5;
const KECCAKF_RNDC: [u32; 24] = [ const KECCAKF_RNDC: [u32; 24] = [
0x00000001, 0x00008082, 0x0000808a, 0x80008000, 0x0000808b, 0x80000001, 0x00000001, 0x00008082, 0x0000808a, 0x80008000, 0x0000808b, 0x80000001, 0x80008081, 0x00008009,
0x80008081, 0x00008009, 0x0000008a, 0x00000088, 0x80008009, 0x8000000a, 0x0000008a, 0x00000088, 0x80008009, 0x8000000a, 0x8000808b, 0x0000008b, 0x00008089, 0x00008003,
0x8000808b, 0x0000008b, 0x00008089, 0x00008003, 0x00008002, 0x00000080, 0x00008002, 0x00000080, 0x0000800a, 0x8000000a, 0x80008081, 0x00008080, 0x80000001, 0x80008008,
0x0000800a, 0x8000000a, 0x80008081, 0x00008080, 0x80000001, 0x80008008
]; ];
const KECCAKF_ROTC: [u32; 24] = [ const KECCAKF_ROTC: [u32; 24] = [
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44
]; ];
const KECCAKF_PILN: [usize; 24] = [ const KECCAKF_PILN: [usize; 24] = [
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1
]; ];
fn keccak_f800_round(st: &mut [u32; 25], r: usize) { fn keccak_f800_round(st: &mut [u32; 25], r: usize) {
@ -90,10 +87,10 @@ fn keccak_f800_round(st: &mut [u32; 25], r: usize) {
fn keccak_f800(header_hash: H256, nonce: u64, result: [u32; 8], st: &mut [u32; 25]) { fn keccak_f800(header_hash: H256, nonce: u64, result: [u32; 8], st: &mut [u32; 25]) {
for i in 0..8 { for i in 0..8 {
st[i] = (header_hash[4 * i] as u32) + st[i] = (header_hash[4 * i] as u32)
((header_hash[4 * i + 1] as u32) << 8) + + ((header_hash[4 * i + 1] as u32) << 8)
((header_hash[4 * i + 2] as u32) << 16) + + ((header_hash[4 * i + 2] as u32) << 16)
((header_hash[4 * i + 3] as u32) << 24); + ((header_hash[4 * i + 3] as u32) << 24);
} }
st[8] = nonce as u32; st[8] = nonce as u32;
@ -119,11 +116,7 @@ pub fn keccak_f800_long(header_hash: H256, nonce: u64, result: [u32; 8]) -> H256
keccak_f800(header_hash, nonce, result, &mut st); keccak_f800(header_hash, nonce, result, &mut st);
// NOTE: transmute from `[u32; 8]` to `[u8; 32]` // NOTE: transmute from `[u32; 8]` to `[u8; 32]`
unsafe { unsafe { std::mem::transmute([st[0], st[1], st[2], st[3], st[4], st[5], st[6], st[7]]) }
std::mem::transmute(
[st[0], st[1], st[2], st[3], st[4], st[5], st[6], st[7]]
)
}
} }
#[inline] #[inline]
@ -146,8 +139,12 @@ impl Kiss99 {
#[inline] #[inline]
fn next_u32(&mut self) -> u32 { fn next_u32(&mut self) -> u32 {
self.z = 36969u32.wrapping_mul(self.z & 65535).wrapping_add(self.z >> 16); self.z = 36969u32
self.w = 18000u32.wrapping_mul(self.w & 65535).wrapping_add(self.w >> 16); .wrapping_mul(self.z & 65535)
.wrapping_add(self.z >> 16);
self.w = 18000u32
.wrapping_mul(self.w & 65535)
.wrapping_add(self.w >> 16);
let mwc = (self.z << 16).wrapping_add(self.w); let mwc = (self.z << 16).wrapping_add(self.w);
self.jsr ^= self.jsr << 17; self.jsr ^= self.jsr << 17;
self.jsr ^= self.jsr >> 13; self.jsr ^= self.jsr >> 13;
@ -247,8 +244,8 @@ fn progpow_loop(
) { ) {
// All lanes share a base address for the global load. Global offset uses // All lanes share a base address for the global load. Global offset uses
// mix[0] to guarantee it depends on the load result. // mix[0] to guarantee it depends on the load result.
let g_offset = mix[loop_ % PROGPOW_LANES][0] as usize % let g_offset = mix[loop_ % PROGPOW_LANES][0] as usize
(64 * data_size / (PROGPOW_LANES * PROGPOW_DAG_LOADS)); % (64 * data_size / (PROGPOW_LANES * PROGPOW_DAG_LOADS));
// 256 bytes of dag data // 256 bytes of dag data
let mut dag_item = [0u32; 64]; let mut dag_item = [0u32; 64];
@ -354,14 +351,7 @@ pub fn progpow(
// Execute the randomly generated inner loop // Execute the randomly generated inner loop
let period = block_number / PROGPOW_PERIOD_LENGTH as u64; let period = block_number / PROGPOW_PERIOD_LENGTH as u64;
for i in 0..PROGPOW_CNT_DAG { for i in 0..PROGPOW_CNT_DAG {
progpow_loop( progpow_loop(period, i, &mut mix, cache, c_dag, data_size);
period,
i,
&mut mix,
cache,
c_dag,
data_size,
);
} }
// Reduce mix data to a single per-lane result // Reduce mix data to a single per-lane result
@ -403,12 +393,12 @@ pub fn generate_cdag(cache: &[Node]) -> CDag {
mod test { mod test {
use tempdir::TempDir; use tempdir::TempDir;
use super::*;
use cache::{NodeCacheBuilder, OptimizeFor}; use cache::{NodeCacheBuilder, OptimizeFor};
use keccak::H256; use keccak::H256;
use rustc_hex::FromHex; use rustc_hex::FromHex;
use serde_json::{self, Value}; use serde_json::{self, Value};
use std::collections::VecDeque; use std::collections::VecDeque;
use super::*;
fn h256(hex: &str) -> H256 { fn h256(hex: &str) -> H256 {
let bytes = FromHex::from_hex(hex).unwrap(); let bytes = FromHex::from_hex(hex).unwrap();
@ -426,16 +416,29 @@ mod test {
let c_dag = generate_cdag(cache.as_ref()); let c_dag = generate_cdag(cache.as_ref());
let expected = vec![ let expected = vec![
690150178u32, 1181503948, 2248155602, 2118233073, 2193871115, 690150178u32,
1791778428, 1067701239, 724807309, 530799275, 3480325829, 3899029234, 1181503948,
1998124059, 2541974622, 1100859971, 1297211151, 3268320000, 2217813733, 2248155602,
2690422980, 3172863319, 2651064309 2118233073,
2193871115,
1791778428,
1067701239,
724807309,
530799275,
3480325829,
3899029234,
1998124059,
2541974622,
1100859971,
1297211151,
3268320000,
2217813733,
2690422980,
3172863319,
2651064309,
]; ];
assert_eq!( assert_eq!(c_dag.iter().take(20).cloned().collect::<Vec<_>>(), expected,);
c_dag.iter().take(20).cloned().collect::<Vec<_>>(),
expected,
);
} }
#[test] #[test]
@ -452,10 +455,7 @@ mod test {
]; ];
for (i, &(a, b, expected)) in tests.iter().enumerate() { for (i, &(a, b, expected)) in tests.iter().enumerate() {
assert_eq!( assert_eq!(merge(a, b, i as u32), expected,);
merge(a, b, i as u32),
expected,
);
} }
} }
@ -487,29 +487,20 @@ mod test {
]; ];
for (i, &(a, b, expected)) in tests.iter().enumerate() { for (i, &(a, b, expected)) in tests.iter().enumerate() {
assert_eq!( assert_eq!(math(a, b, i as u32), expected,);
math(a, b, i as u32),
expected,
);
} }
} }
#[test] #[test]
fn test_keccak_256() { fn test_keccak_256() {
let expected = "5dd431e5fbc604f499bfa0232f45f8f142d0ff5178f539e5a7800bf0643697af"; let expected = "5dd431e5fbc604f499bfa0232f45f8f142d0ff5178f539e5a7800bf0643697af";
assert_eq!( assert_eq!(keccak_f800_long([0; 32], 0, [0; 8]), h256(expected),);
keccak_f800_long([0; 32], 0, [0; 8]),
h256(expected),
);
} }
#[test] #[test]
fn test_keccak_64() { fn test_keccak_64() {
let expected: u64 = 0x5dd431e5fbc604f4; let expected: u64 = 0x5dd431e5fbc604f4;
assert_eq!( assert_eq!(keccak_f800_short([0; 32], 0, [0; 8]), expected,);
keccak_f800_short([0; 32], 0, [0; 8]),
expected,
);
} }
#[test] #[test]
@ -521,26 +512,18 @@ mod test {
let header_hash = [0; 32]; let header_hash = [0; 32];
let (digest, result) = progpow( let (digest, result) = progpow(header_hash, 0, 0, cache.as_ref(), &c_dag);
header_hash,
0,
0,
cache.as_ref(),
&c_dag,
);
let expected_digest = FromHex::from_hex("63155f732f2bf556967f906155b510c917e48e99685ead76ea83f4eca03ab12b").unwrap(); let expected_digest =
let expected_result = FromHex::from_hex("faeb1be51075b03a4ff44b335067951ead07a3b078539ace76fd56fc410557a3").unwrap(); FromHex::from_hex("63155f732f2bf556967f906155b510c917e48e99685ead76ea83f4eca03ab12b")
.unwrap();
let expected_result =
FromHex::from_hex("faeb1be51075b03a4ff44b335067951ead07a3b078539ace76fd56fc410557a3")
.unwrap();
assert_eq!( assert_eq!(digest.to_vec(), expected_digest,);
digest.to_vec(),
expected_digest,
);
assert_eq!( assert_eq!(result.to_vec(), expected_result,);
result.to_vec(),
expected_result,
);
} }
#[test] #[test]
@ -556,11 +539,14 @@ mod test {
let tests: Vec<VecDeque<Value>> = let tests: Vec<VecDeque<Value>> =
serde_json::from_slice(include_bytes!("../res/progpow_testvectors.json")).unwrap(); serde_json::from_slice(include_bytes!("../res/progpow_testvectors.json")).unwrap();
let tests: Vec<ProgpowTest> = tests.into_iter().map(|mut test: VecDeque<Value>| { let tests: Vec<ProgpowTest> = tests
.into_iter()
.map(|mut test: VecDeque<Value>| {
assert!(test.len() == 5); assert!(test.len() == 5);
let block_number: u64 = serde_json::from_value(test.pop_front().unwrap()).unwrap(); let block_number: u64 = serde_json::from_value(test.pop_front().unwrap()).unwrap();
let header_hash: String = serde_json::from_value(test.pop_front().unwrap()).unwrap(); let header_hash: String =
serde_json::from_value(test.pop_front().unwrap()).unwrap();
let nonce: String = serde_json::from_value(test.pop_front().unwrap()).unwrap(); let nonce: String = serde_json::from_value(test.pop_front().unwrap()).unwrap();
let mix_hash: String = serde_json::from_value(test.pop_front().unwrap()).unwrap(); let mix_hash: String = serde_json::from_value(test.pop_front().unwrap()).unwrap();
let final_hash: String = serde_json::from_value(test.pop_front().unwrap()).unwrap(); let final_hash: String = serde_json::from_value(test.pop_front().unwrap()).unwrap();
@ -572,7 +558,8 @@ mod test {
mix_hash: h256(&mix_hash), mix_hash: h256(&mix_hash),
final_hash: h256(&final_hash), final_hash: h256(&final_hash),
} }
}).collect(); })
.collect();
for test in tests { for test in tests {
let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());

View File

@ -14,8 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use shared;
use keccak::{keccak_256, H256}; use keccak::{keccak_256, H256};
use shared;
use std::cell::Cell; use std::cell::Cell;
@ -71,7 +71,10 @@ mod tests {
#[test] #[test]
fn test_seed_compute_once() { fn test_seed_compute_once() {
let seed_compute = SeedHashCompute::default(); let seed_compute = SeedHashCompute::default();
let hash = [241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162]; let hash = [
241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253,
147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162,
];
assert_eq!(seed_compute.hash_block_number(486382), hash); assert_eq!(seed_compute.hash_block_number(486382), hash);
} }
@ -86,7 +89,10 @@ mod tests {
let seed_compute = SeedHashCompute::default(); let seed_compute = SeedHashCompute::default();
// calculating an older value first shouldn't affect the result // calculating an older value first shouldn't affect the result
let _ = seed_compute.hash_block_number(50000); let _ = seed_compute.hash_block_number(50000);
let hash = [241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162]; let hash = [
241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253,
147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162,
];
assert_eq!(seed_compute.hash_block_number(486382), hash); assert_eq!(seed_compute.hash_block_number(486382), hash);
} }
@ -95,8 +101,10 @@ mod tests {
let seed_compute = SeedHashCompute::default(); let seed_compute = SeedHashCompute::default();
// calculating an newer value first shouldn't affect the result // calculating an newer value first shouldn't affect the result
let _ = seed_compute.hash_block_number(972764); let _ = seed_compute.hash_block_number(972764);
let hash = [241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253, 147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162]; let hash = [
241, 175, 44, 134, 39, 121, 245, 239, 228, 236, 43, 160, 195, 152, 46, 7, 199, 5, 253,
147, 241, 206, 98, 43, 3, 104, 17, 40, 192, 79, 106, 162,
];
assert_eq!(seed_compute.hash_block_number(486382), hash); assert_eq!(seed_compute.hash_block_number(486382), hash);
} }
} }

View File

@ -57,7 +57,8 @@ pub fn get_cache_size(block_number: u64) -> usize {
pub fn get_data_size(block_number: u64) -> usize { pub fn get_data_size(block_number: u64) -> usize {
// TODO: Memoise // TODO: Memoise
let mut sz: u64 = DATASET_BYTES_INIT + DATASET_BYTES_GROWTH * (block_number / ETHASH_EPOCH_LENGTH); let mut sz: u64 =
DATASET_BYTES_INIT + DATASET_BYTES_GROWTH * (block_number / ETHASH_EPOCH_LENGTH);
sz = sz - ETHASH_MIX_BYTES as u64; sz = sz - ETHASH_MIX_BYTES as u64;
while !is_prime(sz / ETHASH_MIX_BYTES as u64) { while !is_prime(sz / ETHASH_MIX_BYTES as u64) {
sz = sz - 2 * ETHASH_MIX_BYTES as u64; sz = sz - 2 * ETHASH_MIX_BYTES as u64;
@ -107,7 +108,11 @@ pub union Node {
impl Clone for Node { impl Clone for Node {
fn clone(&self) -> Self { fn clone(&self) -> Self {
unsafe { Node { bytes: *&self.bytes } } unsafe {
Node {
bytes: *&self.bytes,
}
}
} }
} }

View File

@ -19,18 +19,17 @@ extern crate criterion;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
extern crate ethcore_builtin;
extern crate ethcore; extern crate ethcore;
extern crate ethcore_builtin;
extern crate ethereum_types; extern crate ethereum_types;
extern crate parity_bytes as bytes; extern crate parity_bytes as bytes;
extern crate rustc_hex; extern crate rustc_hex;
use criterion::{Criterion, Bencher};
use bytes::BytesRef; use bytes::BytesRef;
use criterion::{Bencher, Criterion};
use ethcore::{ethereum::new_byzantium_test_machine, machine::EthereumMachine};
use ethcore_builtin::Builtin; use ethcore_builtin::Builtin;
use ethcore::machine::EthereumMachine;
use ethereum_types::U256; use ethereum_types::U256;
use ethcore::ethereum::new_byzantium_test_machine;
use rustc_hex::FromHex; use rustc_hex::FromHex;
lazy_static! { lazy_static! {
@ -52,7 +51,9 @@ impl<'a> BuiltinBenchmark<'a> {
let expected = FromHex::from_hex(expected).unwrap(); let expected = FromHex::from_hex(expected).unwrap();
BuiltinBenchmark { BuiltinBenchmark {
builtin, input, expected builtin,
input,
expected,
} }
} }
@ -60,20 +61,16 @@ impl<'a> BuiltinBenchmark<'a> {
let mut output = vec![0; self.expected.len()]; let mut output = vec![0; self.expected.len()];
b.iter(|| { b.iter(|| {
self.builtin.execute(&self.input, &mut BytesRef::Fixed(&mut output)).unwrap(); self.builtin
.execute(&self.input, &mut BytesRef::Fixed(&mut output))
.unwrap();
}); });
assert_eq!(self.expected[..], output[..]); assert_eq!(self.expected[..], output[..]);
} }
} }
fn bench( fn bench(id: &str, builtin_address: &'static str, input: &str, expected: &str, b: &mut Criterion) {
id: &str,
builtin_address: &'static str,
input: &str,
expected: &str,
b: &mut Criterion,
) {
let bench = BuiltinBenchmark::new(builtin_address, input, expected); let bench = BuiltinBenchmark::new(builtin_address, input, expected);
b.bench_function(id, move |b| bench.run(b)); b.bench_function(id, move |b| bench.run(b));
} }

View File

@ -16,8 +16,7 @@
use ethereum_types::{H256, U256}; use ethereum_types::{H256, U256};
use common_types::{encoded, BlockNumber}; use common_types::{encoded, header::Header, BlockNumber};
use common_types::header::Header;
/// Contains information on a best block that is specific to the consensus engine. /// Contains information on a best block that is specific to the consensus engine.
/// ///

View File

@ -14,8 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use ethereum_types::{H256, U256};
use common_types::BlockNumber; use common_types::BlockNumber;
use ethereum_types::{H256, U256};
/// Brief info about inserted block. /// Brief info about inserted block.
#[derive(Clone)] #[derive(Clone)]
@ -27,7 +27,7 @@ pub struct BlockInfo {
/// Total block difficulty. /// Total block difficulty.
pub total_difficulty: U256, pub total_difficulty: U256,
/// Block location in blockchain. /// Block location in blockchain.
pub location: BlockLocation pub location: BlockLocation,
} }
/// Describes location of newly inserted block. /// Describes location of newly inserted block.

File diff suppressed because it is too large Load Diff

View File

@ -16,14 +16,16 @@
//! Blockchain generator for tests. //! Blockchain generator for tests.
use ethereum_types::{Bloom, H256, U256};
use std::collections::VecDeque; use std::collections::VecDeque;
use ethereum_types::{U256, H256, Bloom};
use common_types::encoded; use common_types::{
use common_types::header::Header; encoded,
use common_types::transaction::{SignedTransaction, Transaction, Action}; header::Header,
use common_types::view; transaction::{Action, SignedTransaction, Transaction},
use common_types::views::BlockView; view,
views::BlockView,
};
use keccak_hash::keccak; use keccak_hash::keccak;
use rlp::encode; use rlp::encode;
use rlp_derive::RlpEncodable; use rlp_derive::RlpEncodable;
@ -37,7 +39,7 @@ pub struct Block {
/// Block transactions /// Block transactions
pub transactions: Vec<SignedTransaction>, pub transactions: Vec<SignedTransaction>,
/// Block uncles /// Block uncles
pub uncles: Vec<Header> pub uncles: Vec<Header>,
} }
impl Block { impl Block {
@ -105,9 +107,7 @@ impl BlockBuilder {
let mut blocks = VecDeque::with_capacity(1); let mut blocks = VecDeque::with_capacity(1);
blocks.push_back(Block::default()); blocks.push_back(Block::default());
BlockBuilder { BlockBuilder { blocks }
blocks,
}
} }
/// Add new block with default options. /// Add new block with default options.
@ -124,13 +124,19 @@ impl BlockBuilder {
/// Add block with specified options. /// Add block with specified options.
#[inline] #[inline]
pub fn add_block_with<T>(&self, get_metadata: T) -> Self where T: Fn() -> BlockOptions { pub fn add_block_with<T>(&self, get_metadata: T) -> Self
where
T: Fn() -> BlockOptions,
{
self.add_blocks_with(1, get_metadata) self.add_blocks_with(1, get_metadata)
} }
/// Add a block with given difficulty /// Add a block with given difficulty
#[inline] #[inline]
pub fn add_block_with_difficulty<T>(&self, difficulty: T) -> Self where T: Into<U256> { pub fn add_block_with_difficulty<T>(&self, difficulty: T) -> Self
where
T: Into<U256>,
{
let difficulty = difficulty.into(); let difficulty = difficulty.into();
self.add_blocks_with(1, move || BlockOptions { self.add_blocks_with(1, move || BlockOptions {
difficulty, difficulty,
@ -155,8 +161,10 @@ impl BlockBuilder {
action: Action::Create, action: Action::Create,
value: 100.into(), value: 100.into(),
data, data,
}.sign(&keccak("").into(), None) }
}).take(count); .sign(&keccak("").into(), None)
})
.take(count);
self.add_block_with_transactions(transactions) self.add_block_with_transactions(transactions)
} }
@ -164,7 +172,9 @@ impl BlockBuilder {
/// Add a block with given transactions. /// Add a block with given transactions.
#[inline] #[inline]
pub fn add_block_with_transactions<T>(&self, transactions: T) -> Self pub fn add_block_with_transactions<T>(&self, transactions: T) -> Self
where T: IntoIterator<Item = SignedTransaction> { where
T: IntoIterator<Item = SignedTransaction>,
{
let transactions = transactions.into_iter().collect::<Vec<_>>(); let transactions = transactions.into_iter().collect::<Vec<_>>();
self.add_blocks_with(1, || BlockOptions { self.add_blocks_with(1, || BlockOptions {
transactions: transactions.clone(), transactions: transactions.clone(),
@ -182,7 +192,10 @@ impl BlockBuilder {
} }
/// Add a bunch of blocks with given metadata. /// Add a bunch of blocks with given metadata.
pub fn add_blocks_with<T>(&self, count: usize, get_metadata: T) -> Self where T: Fn() -> BlockOptions { pub fn add_blocks_with<T>(&self, count: usize, get_metadata: T) -> Self
where
T: Fn() -> BlockOptions,
{
assert!(count > 0, "There must be at least 1 block"); assert!(count > 0, "There must be at least 1 block");
let mut parent_hash = self.last().hash(); let mut parent_hash = self.last().hash();
let mut parent_number = self.last().number(); let mut parent_number = self.last().number();
@ -207,15 +220,15 @@ impl BlockBuilder {
blocks.push_back(block); blocks.push_back(block);
} }
BlockBuilder { BlockBuilder { blocks }
blocks,
}
} }
/// Get a reference to the last generated block. /// Get a reference to the last generated block.
#[inline] #[inline]
pub fn last(&self) -> &Block { pub fn last(&self) -> &Block {
self.blocks.back().expect("There is always at least 1 block") self.blocks
.back()
.expect("There is always at least 1 block")
} }
} }
@ -227,7 +240,10 @@ pub struct BlockGenerator {
impl BlockGenerator { impl BlockGenerator {
/// Create new block generator. /// Create new block generator.
pub fn new<T>(builders: T) -> Self where T: IntoIterator<Item = BlockBuilder> { pub fn new<T>(builders: T) -> Self
where
T: IntoIterator<Item = BlockBuilder>,
{
BlockGenerator { BlockGenerator {
builders: builders.into_iter().collect(), builders: builders.into_iter().collect(),
} }
@ -244,18 +260,17 @@ impl Iterator for BlockGenerator {
if let Some(block) = builder.blocks.pop_front() { if let Some(block) = builder.blocks.pop_front() {
return Some(block); return Some(block);
} }
}, }
None => return None, None => return None,
} }
self.builders.pop_front(); self.builders.pop_front();
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{BlockBuilder, BlockOptions, BlockGenerator}; use super::{BlockBuilder, BlockGenerator, BlockOptions};
#[test] #[test]
fn test_block_builder() { fn test_block_builder() {

View File

@ -16,8 +16,8 @@
//! Import route. //! Import route.
use ethereum_types::H256;
use crate::block_info::{BlockInfo, BlockLocation}; use crate::block_info::{BlockInfo, BlockLocation};
use ethereum_types::H256;
/// Import route for newly inserted block. /// Import route for newly inserted block.
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@ -68,17 +68,20 @@ impl From<BlockInfo> for ImportRoute {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ethereum_types::{H256, U256};
use crate::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData};
use super::ImportRoute; use super::ImportRoute;
use crate::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData};
use ethereum_types::{H256, U256};
#[test] #[test]
fn import_route_none() { fn import_route_none() {
assert_eq!(ImportRoute::none(), ImportRoute { assert_eq!(
ImportRoute::none(),
ImportRoute {
enacted: vec![], enacted: vec![],
retracted: vec![], retracted: vec![],
omitted: vec![], omitted: vec![],
}); }
);
} }
#[test] #[test]
@ -90,11 +93,14 @@ mod tests {
location: BlockLocation::Branch, location: BlockLocation::Branch,
}; };
assert_eq!(ImportRoute::from(info), ImportRoute { assert_eq!(
ImportRoute::from(info),
ImportRoute {
retracted: vec![], retracted: vec![],
enacted: vec![], enacted: vec![],
omitted: vec![H256::from(U256::from(1))], omitted: vec![H256::from(U256::from(1))],
}); }
);
} }
#[test] #[test]
@ -106,11 +112,14 @@ mod tests {
location: BlockLocation::CanonChain, location: BlockLocation::CanonChain,
}; };
assert_eq!(ImportRoute::from(info), ImportRoute { assert_eq!(
ImportRoute::from(info),
ImportRoute {
retracted: vec![], retracted: vec![],
enacted: vec![H256::from(U256::from(1))], enacted: vec![H256::from(U256::from(1))],
omitted: vec![], omitted: vec![],
}); }
);
} }
#[test] #[test]
@ -123,13 +132,16 @@ mod tests {
ancestor: H256::from(U256::from(0)), ancestor: H256::from(U256::from(0)),
enacted: vec![H256::from(U256::from(1))], enacted: vec![H256::from(U256::from(1))],
retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))], retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))],
}) }),
}; };
assert_eq!(ImportRoute::from(info), ImportRoute { assert_eq!(
ImportRoute::from(info),
ImportRoute {
retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))], retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))],
enacted: vec![H256::from(U256::from(1)), H256::from(U256::from(2))], enacted: vec![H256::from(U256::from(1)), H256::from(U256::from(2))],
omitted: vec![], omitted: vec![],
}); }
);
} }
} }

View File

@ -28,10 +28,12 @@ mod update;
pub mod generator; pub mod generator;
pub use self::blockchain::{BlockProvider, BlockChain, BlockChainDB, BlockChainDBHandler}; pub use self::{
pub use self::cache::CacheSize; blockchain::{BlockChain, BlockChainDB, BlockChainDBHandler, BlockProvider},
pub use self::config::Config; cache::CacheSize,
pub use self::import_route::ImportRoute; config::Config,
pub use self::update::ExtrasInsert; import_route::ImportRoute,
pub use ethcore_db::keys::{BlockReceipts, BlockDetails, TransactionAddress, BlockNumberKey}; update::ExtrasInsert,
};
pub use common_types::tree_route::TreeRoute; pub use common_types::tree_route::TreeRoute;
pub use ethcore_db::keys::{BlockDetails, BlockNumberKey, BlockReceipts, TransactionAddress};

View File

@ -16,11 +16,9 @@
use std::collections::HashMap; use std::collections::HashMap;
use common_types::BlockNumber; use common_types::{encoded::Block, engines::ForkChoice, BlockNumber};
use common_types::encoded::Block;
use common_types::engines::ForkChoice;
use ethcore_db::keys::{BlockDetails, BlockReceipts, TransactionAddress}; use ethcore_db::keys::{BlockDetails, BlockReceipts, TransactionAddress};
use ethereum_types::{H256, Bloom}; use ethereum_types::{Bloom, H256};
use crate::block_info::BlockInfo; use crate::block_info::BlockInfo;

View File

@ -22,19 +22,19 @@ use std::{
cmp::{max, min}, cmp::{max, min},
collections::BTreeMap, collections::BTreeMap,
convert::{TryFrom, TryInto}, convert::{TryFrom, TryInto},
io::{self, Read, Cursor}, io::{self, Cursor, Read},
mem::size_of, mem::size_of,
str::FromStr str::FromStr,
}; };
use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
use eip_152::compress;
use ethereum_types::{H256, U256}; use ethereum_types::{H256, U256};
use ethjson; use ethjson;
use ethkey::{Signature, recover as ec_recover}; use ethkey::{recover as ec_recover, Signature};
use eip_152::compress;
use keccak_hash::keccak; use keccak_hash::keccak;
use log::{warn, trace}; use log::{trace, warn};
use num::{BigUint, Zero, One}; use num::{BigUint, One, Zero};
use parity_bytes::BytesRef; use parity_bytes::BytesRef;
use parity_crypto::digest; use parity_crypto::digest;
@ -149,7 +149,9 @@ impl Pricer for ModexpPricer {
// read lengths as U256 here for accurate gas calculation. // read lengths as U256 here for accurate gas calculation.
let mut read_len = || { let mut read_len = || {
reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); reader
.read_exact(&mut buf[..])
.expect("reading from zero-extended memory cannot fail; qed");
U256::from(H256::from_slice(&buf[..])) U256::from(H256::from_slice(&buf[..]))
}; };
let base_len = read_len(); let base_len = read_len();
@ -157,14 +159,15 @@ impl Pricer for ModexpPricer {
let mod_len = read_len(); let mod_len = read_len();
if mod_len.is_zero() && base_len.is_zero() { if mod_len.is_zero() && base_len.is_zero() {
return U256::zero() return U256::zero();
} }
let max_len = U256::from(u32::max_value() / 2); let max_len = U256::from(u32::max_value() / 2);
if base_len > max_len || mod_len > max_len || exp_len > max_len { if base_len > max_len || mod_len > max_len || exp_len > max_len {
return U256::max_value(); return U256::max_value();
} }
let (base_len, exp_len, mod_len) = (base_len.low_u64(), exp_len.low_u64(), mod_len.low_u64()); let (base_len, exp_len, mod_len) =
(base_len.low_u64(), exp_len.low_u64(), mod_len.low_u64());
let m = max(mod_len, base_len); let m = max(mod_len, base_len);
// read fist 32-byte word of the exponent. // read fist 32-byte word of the exponent.
@ -174,7 +177,9 @@ impl Pricer for ModexpPricer {
buf.iter_mut().for_each(|b| *b = 0); buf.iter_mut().for_each(|b| *b = 0);
let mut reader = input[(96 + base_len as usize)..].chain(io::repeat(0)); let mut reader = input[(96 + base_len as usize)..].chain(io::repeat(0));
let len = min(exp_len, 32) as usize; let len = min(exp_len, 32) as usize;
reader.read_exact(&mut buf[(32 - len)..]).expect("reading from zero-extended memory cannot fail; qed"); reader
.read_exact(&mut buf[(32 - len)..])
.expect("reading from zero-extended memory cannot fail; qed");
U256::from(H256::from_slice(&buf[..])) U256::from(H256::from_slice(&buf[..]))
}; };
@ -190,7 +195,11 @@ impl Pricer for ModexpPricer {
impl ModexpPricer { impl ModexpPricer {
fn adjusted_exp_len(len: u64, exp_low: U256) -> u64 { fn adjusted_exp_len(len: u64, exp_low: U256) -> u64 {
let bit_index = if exp_low.is_zero() { 0 } else { (255 - exp_low.leading_zeros()) as u64 }; let bit_index = if exp_low.is_zero() {
0
} else {
(255 - exp_low.leading_zeros()) as u64
};
if len <= 32 { if len <= 32 {
bit_index bit_index
} else { } else {
@ -268,22 +277,18 @@ impl From<ethjson::spec::builtin::Pricing> for Pricing {
ethjson::spec::builtin::Pricing::Blake2F { gas_per_round } => { ethjson::spec::builtin::Pricing::Blake2F { gas_per_round } => {
Pricing::Blake2F(gas_per_round) Pricing::Blake2F(gas_per_round)
} }
ethjson::spec::builtin::Pricing::Linear(linear) => { ethjson::spec::builtin::Pricing::Linear(linear) => Pricing::Linear(Linear {
Pricing::Linear(Linear {
base: linear.base, base: linear.base,
word: linear.word, word: linear.word,
}) }),
} ethjson::spec::builtin::Pricing::Modexp(exp) => Pricing::Modexp(ModexpPricer {
ethjson::spec::builtin::Pricing::Modexp(exp) => {
Pricing::Modexp(ModexpPricer {
divisor: if exp.divisor == 0 { divisor: if exp.divisor == 0 {
warn!(target: "builtin", "Zero modexp divisor specified. Falling back to default: 10."); warn!(target: "builtin", "Zero modexp divisor specified. Falling back to default: 10.");
10 10
} else { } else {
exp.divisor exp.divisor
} },
}) }),
}
ethjson::spec::builtin::Pricing::AltBn128Pairing(pricer) => { ethjson::spec::builtin::Pricing::AltBn128Pairing(pricer) => {
Pricing::AltBn128Pairing(AltBn128PairingPricer { Pricing::AltBn128Pairing(AltBn128PairingPricer {
price: AltBn128PairingPrice { price: AltBn128PairingPrice {
@ -294,7 +299,7 @@ impl From<ethjson::spec::builtin::Pricing> for Pricing {
} }
ethjson::spec::builtin::Pricing::AltBn128ConstOperations(pricer) => { ethjson::spec::builtin::Pricing::AltBn128ConstOperations(pricer) => {
Pricing::AltBn128ConstOperations(AltBn128ConstOperations { Pricing::AltBn128ConstOperations(AltBn128ConstOperations {
price: pricer.price price: pricer.price,
}) })
} }
} }
@ -320,7 +325,7 @@ enum EthereumBuiltin {
/// alt_bn128_pairing /// alt_bn128_pairing
Bn128Pairing(Bn128Pairing), Bn128Pairing(Bn128Pairing),
/// blake2_f (The Blake2 compression function F, EIP-152) /// blake2_f (The Blake2 compression function F, EIP-152)
Blake2F(Blake2F) Blake2F(Blake2F),
} }
impl FromStr for EthereumBuiltin { impl FromStr for EthereumBuiltin {
@ -406,7 +411,9 @@ impl Implementation for EcRecover {
let bit = match v[31] { let bit = match v[31] {
27 | 28 if v.0[..31] == [0; 31] => v[31] - 27, 27 | 28 if v.0[..31] == [0; 31] => v[31] - 27,
_ => { return Ok(()); }, _ => {
return Ok(());
}
}; };
let s = Signature::from_rsv(&r, &s, bit); let s = Signature::from_rsv(&r, &s, bit);
@ -439,7 +446,7 @@ impl Implementation for Blake2F {
if input.len() != BLAKE2_F_ARG_LEN { if input.len() != BLAKE2_F_ARG_LEN {
trace!(target: "builtin", "input length for Blake2 F precompile should be exactly 213 bytes, was {}", input.len()); trace!(target: "builtin", "input length for Blake2 F precompile should be exactly 213 bytes, was {}", input.len());
return Err("input length for Blake2 F precompile should be exactly 213 bytes".into()) return Err("input length for Blake2 F precompile should be exactly 213 bytes".into());
} }
let mut cursor = Cursor::new(input); let mut cursor = Cursor::new(input);
@ -469,7 +476,7 @@ impl Implementation for Blake2F {
Some(0) => false, Some(0) => false,
_ => { _ => {
trace!(target: "builtin", "incorrect final block indicator flag, was: {:?}", input.last()); trace!(target: "builtin", "incorrect final block indicator flag, was: {:?}", input.last());
return Err("incorrect final block indicator flag".into()) return Err("incorrect final block indicator flag".into());
} }
}; };
@ -477,7 +484,7 @@ impl Implementation for Blake2F {
let mut output_buf = [0u8; 8 * size_of::<u64>()]; let mut output_buf = [0u8; 8 * size_of::<u64>()];
for (i, state_word) in h.iter().enumerate() { for (i, state_word) in h.iter().enumerate() {
output_buf[i*8..(i+1)*8].copy_from_slice(&state_word.to_le_bytes()); output_buf[i * 8..(i + 1) * 8].copy_from_slice(&state_word.to_le_bytes());
} }
output.write(0, &output_buf[..]); output.write(0, &output_buf[..]);
Ok(()) Ok(())
@ -518,7 +525,9 @@ fn modexp(mut base: BigUint, exp: Vec<u8>, modulus: BigUint) -> BigUint {
base %= &modulus; base %= &modulus;
// Fast path for base divisible by modulus. // Fast path for base divisible by modulus.
if base.is_zero() { return BigUint::zero() } if base.is_zero() {
return BigUint::zero();
}
// Left-to-right binary exponentiation (Handbook of Applied Cryptography - Algorithm 14.79). // Left-to-right binary exponentiation (Handbook of Applied Cryptography - Algorithm 14.79).
// http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf // http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf
@ -550,7 +559,9 @@ impl Implementation for Modexp {
// ignoring the first 24 bytes might technically lead us to fall out of consensus, // ignoring the first 24 bytes might technically lead us to fall out of consensus,
// but so would running out of addressable memory! // but so would running out of addressable memory!
let mut read_len = |reader: &mut io::Chain<&[u8], io::Repeat>| { let mut read_len = |reader: &mut io::Chain<&[u8], io::Repeat>| {
reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); reader
.read_exact(&mut buf[..])
.expect("reading from zero-extended memory cannot fail; qed");
let mut len_bytes = [0u8; 8]; let mut len_bytes = [0u8; 8];
len_bytes.copy_from_slice(&buf[24..]); len_bytes.copy_from_slice(&buf[24..]);
u64::from_be_bytes(len_bytes) as usize u64::from_be_bytes(len_bytes) as usize
@ -567,14 +578,18 @@ impl Implementation for Modexp {
// read the numbers themselves. // read the numbers themselves.
let mut buf = vec![0; max(mod_len, max(base_len, exp_len))]; let mut buf = vec![0; max(mod_len, max(base_len, exp_len))];
let mut read_num = |reader: &mut io::Chain<&[u8], io::Repeat>, len: usize| { let mut read_num = |reader: &mut io::Chain<&[u8], io::Repeat>, len: usize| {
reader.read_exact(&mut buf[..len]).expect("reading from zero-extended memory cannot fail; qed"); reader
.read_exact(&mut buf[..len])
.expect("reading from zero-extended memory cannot fail; qed");
BigUint::from_bytes_be(&buf[..len]) BigUint::from_bytes_be(&buf[..len])
}; };
let base = read_num(&mut reader, base_len); let base = read_num(&mut reader, base_len);
let mut exp_buf = vec![0; exp_len]; let mut exp_buf = vec![0; exp_len];
reader.read_exact(&mut exp_buf[..exp_len]).expect("reading from zero-extended memory cannot fail; qed"); reader
.read_exact(&mut exp_buf[..exp_len])
.expect("reading from zero-extended memory cannot fail; qed");
let modulus = read_num(&mut reader, mod_len); let modulus = read_num(&mut reader, mod_len);
@ -598,27 +613,33 @@ impl Implementation for Modexp {
fn read_fr(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result<bn::Fr, &'static str> { fn read_fr(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result<bn::Fr, &'static str> {
let mut buf = [0u8; 32]; let mut buf = [0u8; 32];
reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); reader
.read_exact(&mut buf[..])
.expect("reading from zero-extended memory cannot fail; qed");
bn::Fr::from_slice(&buf[0..32]).map_err(|_| "Invalid field element") bn::Fr::from_slice(&buf[0..32]).map_err(|_| "Invalid field element")
} }
fn read_point(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result<bn::G1, &'static str> { fn read_point(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result<bn::G1, &'static str> {
use bn::{Fq, AffineG1, G1, Group}; use bn::{AffineG1, Fq, Group, G1};
let mut buf = [0u8; 32]; let mut buf = [0u8; 32];
reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); reader
.read_exact(&mut buf[..])
.expect("reading from zero-extended memory cannot fail; qed");
let px = Fq::from_slice(&buf[0..32]).map_err(|_| "Invalid point x coordinate")?; let px = Fq::from_slice(&buf[0..32]).map_err(|_| "Invalid point x coordinate")?;
reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); reader
.read_exact(&mut buf[..])
.expect("reading from zero-extended memory cannot fail; qed");
let py = Fq::from_slice(&buf[0..32]).map_err(|_| "Invalid point y coordinate")?; let py = Fq::from_slice(&buf[0..32]).map_err(|_| "Invalid point y coordinate")?;
Ok( Ok(if px == Fq::zero() && py == Fq::zero() {
if px == Fq::zero() && py == Fq::zero() {
G1::zero() G1::zero()
} else { } else {
AffineG1::new(px, py).map_err(|_| "Invalid curve point")?.into() AffineG1::new(px, py)
} .map_err(|_| "Invalid curve point")?
) .into()
})
} }
impl Implementation for Bn128Add { impl Implementation for Bn128Add {
@ -633,8 +654,12 @@ impl Implementation for Bn128Add {
let mut write_buf = [0u8; 64]; let mut write_buf = [0u8; 64];
if let Some(sum) = AffineG1::from_jacobian(p1 + p2) { if let Some(sum) = AffineG1::from_jacobian(p1 + p2) {
// point not at infinity // point not at infinity
sum.x().to_big_endian(&mut write_buf[0..32]).expect("Cannot fail since 0..32 is 32-byte length"); sum.x()
sum.y().to_big_endian(&mut write_buf[32..64]).expect("Cannot fail since 32..64 is 32-byte length"); .to_big_endian(&mut write_buf[0..32])
.expect("Cannot fail since 0..32 is 32-byte length");
sum.y()
.to_big_endian(&mut write_buf[32..64])
.expect("Cannot fail since 32..64 is 32-byte length");
} }
output.write(0, &write_buf); output.write(0, &write_buf);
@ -654,8 +679,12 @@ impl Implementation for Bn128Mul {
let mut write_buf = [0u8; 64]; let mut write_buf = [0u8; 64];
if let Some(sum) = AffineG1::from_jacobian(p * fr) { if let Some(sum) = AffineG1::from_jacobian(p * fr) {
// point not at infinity // point not at infinity
sum.x().to_big_endian(&mut write_buf[0..32]).expect("Cannot fail since 0..32 is 32-byte length"); sum.x()
sum.y().to_big_endian(&mut write_buf[32..64]).expect("Cannot fail since 32..64 is 32-byte length"); .to_big_endian(&mut write_buf[0..32])
.expect("Cannot fail since 0..32 is 32-byte length");
sum.y()
.to_big_endian(&mut write_buf[32..64])
.expect("Cannot fail since 32..64 is 32-byte length");
} }
output.write(0, &write_buf); output.write(0, &write_buf);
Ok(()) Ok(())
@ -669,12 +698,12 @@ impl Implementation for Bn128Pairing {
/// - any of even points does not belong to the twisted bn128 curve over the field F_p^2 = F_p[i] / (i^2 + 1) /// - any of even points does not belong to the twisted bn128 curve over the field F_p^2 = F_p[i] / (i^2 + 1)
fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> {
if input.len() % 192 != 0 { if input.len() % 192 != 0 {
return Err("Invalid input length, must be multiple of 192 (3 * (32*2))".into()) return Err("Invalid input length, must be multiple of 192 (3 * (32*2))".into());
} }
if let Err(err) = self.execute_with_error(input, output) { if let Err(err) = self.execute_with_error(input, output) {
trace!(target: "builtin", "Pairing error: {:?}", err); trace!(target: "builtin", "Pairing error: {:?}", err);
return Err(err) return Err(err);
} }
Ok(()) Ok(())
} }
@ -682,7 +711,7 @@ impl Implementation for Bn128Pairing {
impl Bn128Pairing { impl Bn128Pairing {
fn execute_with_error(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { fn execute_with_error(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> {
use bn::{AffineG1, AffineG2, Fq, Fq2, pairing, G1, G2, Gt, Group}; use bn::{pairing, AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2};
let ret_val = if input.is_empty() { let ret_val = if input.is_empty() {
U256::one() U256::one()
@ -691,22 +720,22 @@ impl Bn128Pairing {
let elements = input.len() / 192; let elements = input.len() / 192;
let mut vals = Vec::new(); let mut vals = Vec::new();
for idx in 0..elements { for idx in 0..elements {
let a_x = Fq::from_slice(&input[idx*192..idx*192+32]) let a_x = Fq::from_slice(&input[idx * 192..idx * 192 + 32])
.map_err(|_| "Invalid a argument x coordinate")?; .map_err(|_| "Invalid a argument x coordinate")?;
let a_y = Fq::from_slice(&input[idx*192+32..idx*192+64]) let a_y = Fq::from_slice(&input[idx * 192 + 32..idx * 192 + 64])
.map_err(|_| "Invalid a argument y coordinate")?; .map_err(|_| "Invalid a argument y coordinate")?;
let b_a_y = Fq::from_slice(&input[idx*192+64..idx*192+96]) let b_a_y = Fq::from_slice(&input[idx * 192 + 64..idx * 192 + 96])
.map_err(|_| "Invalid b argument imaginary coeff x coordinate")?; .map_err(|_| "Invalid b argument imaginary coeff x coordinate")?;
let b_a_x = Fq::from_slice(&input[idx*192+96..idx*192+128]) let b_a_x = Fq::from_slice(&input[idx * 192 + 96..idx * 192 + 128])
.map_err(|_| "Invalid b argument imaginary coeff y coordinate")?; .map_err(|_| "Invalid b argument imaginary coeff y coordinate")?;
let b_b_y = Fq::from_slice(&input[idx*192+128..idx*192+160]) let b_b_y = Fq::from_slice(&input[idx * 192 + 128..idx * 192 + 160])
.map_err(|_| "Invalid b argument real coeff x coordinate")?; .map_err(|_| "Invalid b argument real coeff x coordinate")?;
let b_b_x = Fq::from_slice(&input[idx*192+160..idx*192+192]) let b_b_x = Fq::from_slice(&input[idx * 192 + 160..idx * 192 + 192])
.map_err(|_| "Invalid b argument real coeff y coordinate")?; .map_err(|_| "Invalid b argument real coeff y coordinate")?;
let b_a = Fq2::new(b_a_x, b_a_y); let b_a = Fq2::new(b_a_x, b_a_y);
@ -714,17 +743,23 @@ impl Bn128Pairing {
let b = if b_a.is_zero() && b_b.is_zero() { let b = if b_a.is_zero() && b_b.is_zero() {
G2::zero() G2::zero()
} else { } else {
G2::from(AffineG2::new(b_a, b_b).map_err(|_| "Invalid b argument - not on curve")?) G2::from(
AffineG2::new(b_a, b_b).map_err(|_| "Invalid b argument - not on curve")?,
)
}; };
let a = if a_x.is_zero() && a_y.is_zero() { let a = if a_x.is_zero() && a_y.is_zero() {
G1::zero() G1::zero()
} else { } else {
G1::from(AffineG1::new(a_x, a_y).map_err(|_| "Invalid a argument - not on curve")?) G1::from(
AffineG1::new(a_x, a_y).map_err(|_| "Invalid a argument - not on curve")?,
)
}; };
vals.push((a, b)); vals.push((a, b));
}; }
let mul = vals.into_iter().fold(Gt::one(), |s, (a, b)| s * pairing(a, b)); let mul = vals
.into_iter()
.fold(Gt::one(), |s, (a, b)| s * pairing(a, b));
if mul == Gt::one() { if mul == Gt::one() {
U256::one() U256::one()
@ -743,20 +778,20 @@ impl Bn128Pairing {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::convert::TryFrom; use super::{
modexp as me, BTreeMap, Builtin, EthereumBuiltin, FromStr, Implementation, Linear,
ModexpPricer, Pricing,
};
use ethereum_types::U256; use ethereum_types::U256;
use ethjson::spec::builtin::{ use ethjson::spec::builtin::{
Builtin as JsonBuiltin, Linear as JsonLinearPricing, AltBn128Pairing as JsonAltBn128PairingPricing, Builtin as JsonBuiltin,
PricingAt, AltBn128Pairing as JsonAltBn128PairingPricing, Pricing as JsonPricing, Linear as JsonLinearPricing, Pricing as JsonPricing, PricingAt,
}; };
use hex_literal::hex; use hex_literal::hex;
use macros::map; use macros::map;
use num::{BigUint, Zero, One}; use num::{BigUint, One, Zero};
use parity_bytes::BytesRef; use parity_bytes::BytesRef;
use super::{ use std::convert::TryFrom;
BTreeMap, Builtin, EthereumBuiltin, FromStr, Implementation, Linear,
ModexpPricer, modexp as me, Pricing
};
#[test] #[test]
fn blake2f_cost() { fn blake2f_cost() {
@ -767,9 +802,10 @@ mod tests {
// 5 rounds // 5 rounds
let input = hex!("0000000548c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); let input = hex!("0000000548c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001");
let mut output = [0u8; 64]; let mut output = [0u8; 64];
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).unwrap(); f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]))
.unwrap();
assert_eq!(f.cost(&input[..], 0), U256::from(123*5)); assert_eq!(f.cost(&input[..], 0), U256::from(123 * 5));
} }
#[test] #[test]
@ -793,7 +829,10 @@ mod tests {
let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])); let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..]));
assert!(result.is_err()); assert!(result.is_err());
assert_eq!(result.unwrap_err(), "input length for Blake2 F precompile should be exactly 213 bytes"); assert_eq!(
result.unwrap_err(),
"input length for Blake2 F precompile should be exactly 213 bytes"
);
} }
#[test] #[test]
@ -805,7 +844,10 @@ mod tests {
let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])); let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..]));
assert!(result.is_err()); assert!(result.is_err());
assert_eq!(result.unwrap_err(), "input length for Blake2 F precompile should be exactly 213 bytes"); assert_eq!(
result.unwrap_err(),
"input length for Blake2 F precompile should be exactly 213 bytes"
);
} }
#[test] #[test]
@ -827,7 +869,9 @@ mod tests {
let input = hex!("0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); let input = hex!("0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001");
let expected = hex!("08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b"); let expected = hex!("08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b");
let mut output = [0u8; 64]; let mut output = [0u8; 64];
blake2.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).unwrap(); blake2
.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]))
.unwrap();
assert_eq!(&output[..], &expected[..]); assert_eq!(&output[..], &expected[..]);
} }
@ -838,7 +882,9 @@ mod tests {
let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001");
let expected = hex!("ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"); let expected = hex!("ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923");
let mut out = [0u8; 64]; let mut out = [0u8; 64];
blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])).unwrap(); blake2
.execute(&input[..], &mut BytesRef::Fixed(&mut out[..]))
.unwrap();
assert_eq!(&out[..], &expected[..]); assert_eq!(&out[..], &expected[..]);
} }
@ -849,7 +895,9 @@ mod tests {
let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000"); let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000");
let expected = hex!("75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735"); let expected = hex!("75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735");
let mut out = [0u8; 64]; let mut out = [0u8; 64];
blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])).unwrap(); blake2
.execute(&input[..], &mut BytesRef::Fixed(&mut out[..]))
.unwrap();
assert_eq!(&out[..], &expected[..]); assert_eq!(&out[..], &expected[..]);
} }
@ -860,7 +908,9 @@ mod tests {
let input = hex!("0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); let input = hex!("0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001");
let expected = hex!("b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421"); let expected = hex!("b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421");
let mut out = [0u8; 64]; let mut out = [0u8; 64];
blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])).unwrap(); blake2
.execute(&input[..], &mut BytesRef::Fixed(&mut out[..]))
.unwrap();
assert_eq!(&out[..], &expected[..]); assert_eq!(&out[..], &expected[..]);
} }
@ -873,7 +923,9 @@ mod tests {
let input = hex!("ffffffff48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); let input = hex!("ffffffff48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001");
let expected = hex!("fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615"); let expected = hex!("fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615");
let mut out = [0u8; 64]; let mut out = [0u8; 64];
blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])).unwrap(); blake2
.execute(&input[..], &mut BytesRef::Fixed(&mut out[..]))
.unwrap();
assert_eq!(&out[..], &expected[..]); assert_eq!(&out[..], &expected[..]);
} }
@ -907,7 +959,10 @@ mod tests {
base = BigUint::parse_bytes(b"12345", 10).unwrap(); base = BigUint::parse_bytes(b"12345", 10).unwrap();
exp = BigUint::parse_bytes(b"789", 10).unwrap(); exp = BigUint::parse_bytes(b"789", 10).unwrap();
modulus = BigUint::parse_bytes(b"97", 10).unwrap(); modulus = BigUint::parse_bytes(b"97", 10).unwrap();
assert_eq!(me(base, exp.to_bytes_be(), modulus), BigUint::parse_bytes(b"55", 10).unwrap()); assert_eq!(
me(base, exp.to_bytes_be(), modulus),
BigUint::parse_bytes(b"55", 10).unwrap()
);
} }
#[test] #[test]
@ -916,15 +971,18 @@ mod tests {
let i = [0u8, 1, 2, 3]; let i = [0u8, 1, 2, 3];
let mut o2 = [255u8; 2]; let mut o2 = [255u8; 2];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o2[..])).expect("Builtin should not fail"); f.execute(&i[..], &mut BytesRef::Fixed(&mut o2[..]))
.expect("Builtin should not fail");
assert_eq!(i[0..2], o2); assert_eq!(i[0..2], o2);
let mut o4 = [255u8; 4]; let mut o4 = [255u8; 4];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o4[..])).expect("Builtin should not fail"); f.execute(&i[..], &mut BytesRef::Fixed(&mut o4[..]))
.expect("Builtin should not fail");
assert_eq!(i, o4); assert_eq!(i, o4);
let mut o8 = [255u8; 8]; let mut o8 = [255u8; 8];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])).expect("Builtin should not fail"); f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..]))
.expect("Builtin should not fail");
assert_eq!(i, o8[..4]); assert_eq!(i, o8[..4]);
assert_eq!([255u8; 4], o8[4..]); assert_eq!([255u8; 4], o8[4..]);
} }
@ -935,20 +993,33 @@ mod tests {
let i = [0u8; 0]; let i = [0u8; 0];
let mut o = [255u8; 32]; let mut o = [255u8; 32];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]))
assert_eq!(&o[..], hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); .expect("Builtin should not fail");
assert_eq!(
&o[..],
hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
);
let mut o8 = [255u8; 8]; let mut o8 = [255u8; 8];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])).expect("Builtin should not fail"); f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..]))
.expect("Builtin should not fail");
assert_eq!(&o8[..], hex!("e3b0c44298fc1c14")); assert_eq!(&o8[..], hex!("e3b0c44298fc1c14"));
let mut o34 = [255u8; 34]; let mut o34 = [255u8; 34];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])).expect("Builtin should not fail"); f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..]))
assert_eq!(&o34[..], &hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffff")[..]); .expect("Builtin should not fail");
assert_eq!(
&o34[..],
&hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffff")[..]
);
let mut ov = vec![]; let mut ov = vec![];
f.execute(&i[..], &mut BytesRef::Flexible(&mut ov)).expect("Builtin should not fail"); f.execute(&i[..], &mut BytesRef::Flexible(&mut ov))
assert_eq!(&ov[..], &hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")[..]); .expect("Builtin should not fail");
assert_eq!(
&ov[..],
&hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")[..]
);
} }
#[test] #[test]
@ -957,16 +1028,25 @@ mod tests {
let i = [0u8; 0]; let i = [0u8; 0];
let mut o = [255u8; 32]; let mut o = [255u8; 32];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]))
assert_eq!(&o[..], &hex!("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31")[..]); .expect("Builtin should not fail");
assert_eq!(
&o[..],
&hex!("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31")[..]
);
let mut o8 = [255u8; 8]; let mut o8 = [255u8; 8];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])).expect("Builtin should not fail"); f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..]))
.expect("Builtin should not fail");
assert_eq!(&o8[..], &hex!("0000000000000000")[..]); assert_eq!(&o8[..], &hex!("0000000000000000")[..]);
let mut o34 = [255u8; 34]; let mut o34 = [255u8; 34];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])).expect("Builtin should not fail"); f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..]))
assert_eq!(&o34[..], &hex!("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31ffff")[..]); .expect("Builtin should not fail");
assert_eq!(
&o34[..],
&hex!("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31ffff")[..]
);
} }
#[test] #[test]
@ -976,41 +1056,70 @@ mod tests {
let i = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03"); let i = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03");
let mut o = [255u8; 32]; let mut o = [255u8; 32];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]))
assert_eq!(&o[..], &hex!("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb")[..]); .expect("Builtin should not fail");
assert_eq!(
&o[..],
&hex!("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb")[..]
);
let mut o8 = [255u8; 8]; let mut o8 = [255u8; 8];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])).expect("Builtin should not fail"); f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..]))
.expect("Builtin should not fail");
assert_eq!(&o8[..], &hex!("0000000000000000")[..]); assert_eq!(&o8[..], &hex!("0000000000000000")[..]);
let mut o34 = [255u8; 34]; let mut o34 = [255u8; 34];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])).expect("Builtin should not fail"); f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..]))
assert_eq!(&o34[..], &hex!("000000000000000000000000c08b5542d177ac6686946920409741463a15dddbffff")[..]); .expect("Builtin should not fail");
assert_eq!(
&o34[..],
&hex!("000000000000000000000000c08b5542d177ac6686946920409741463a15dddbffff")[..]
);
let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001a650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03"); let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001a650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03");
let mut o = [255u8; 32]; let mut o = [255u8; 32];
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..]))
assert_eq!(&o[..], &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]); .expect("Builtin should not fail");
assert_eq!(
&o[..],
&hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]
);
let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000"); let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000");
let mut o = [255u8; 32]; let mut o = [255u8; 32];
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..]))
assert_eq!(&o[..], &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]); .expect("Builtin should not fail");
assert_eq!(
&o[..],
&hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]
);
let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b"); let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b");
let mut o = [255u8; 32]; let mut o = [255u8; 32];
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..]))
assert_eq!(&o[..], &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]); .expect("Builtin should not fail");
assert_eq!(
&o[..],
&hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]
);
let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000001b"); let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000001b");
let mut o = [255u8; 32]; let mut o = [255u8; 32];
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..]))
assert_eq!(&o[..], &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]); .expect("Builtin should not fail");
assert_eq!(
&o[..],
&hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]
);
let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
let mut o = [255u8; 32]; let mut o = [255u8; 32];
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..]))
assert_eq!(&o[..], &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]); .expect("Builtin should not fail");
assert_eq!(
&o[..],
&hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]
);
// TODO: Should this (corrupted version of the above) fail rather than returning some address? // TODO: Should this (corrupted version of the above) fail rather than returning some address?
/* let i_bad = FromHex::from_hex("48173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap(); /* let i_bad = FromHex::from_hex("48173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
@ -1035,7 +1144,8 @@ mod tests {
// test for potential exp len overflow // test for potential exp len overflow
{ {
let input = hex!(" let input = hex!(
"
00000000000000000000000000000000000000000000000000000000000000ff 00000000000000000000000000000000000000000000000000000000000000ff
2a1e530000000000000000000000000000000000000000000000000000000000 2a1e530000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000" 0000000000000000000000000000000000000000000000000000000000000000"
@ -1045,14 +1155,16 @@ mod tests {
let expected = hex!("0000000000000000000000000000000000000000000000000000000000000000"); let expected = hex!("0000000000000000000000000000000000000000000000000000000000000000");
let expected_cost = U256::max_value(); let expected_cost = U256::max_value();
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should fail"); f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]))
.expect("Builtin should fail");
assert_eq!(output, expected); assert_eq!(output, expected);
assert_eq!(f.cost(&input[..], 0), expected_cost); assert_eq!(f.cost(&input[..], 0), expected_cost);
} }
// fermat's little theorem example. // fermat's little theorem example.
{ {
let input = hex!(" let input = hex!(
"
0000000000000000000000000000000000000000000000000000000000000001 0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000020 0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000020 0000000000000000000000000000000000000000000000000000000000000020
@ -1065,14 +1177,16 @@ mod tests {
let expected = hex!("0000000000000000000000000000000000000000000000000000000000000001"); let expected = hex!("0000000000000000000000000000000000000000000000000000000000000001");
let expected_cost = 13056; let expected_cost = 13056;
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]))
.expect("Builtin should not fail");
assert_eq!(output, expected); assert_eq!(output, expected);
assert_eq!(f.cost(&input[..], 0), expected_cost.into()); assert_eq!(f.cost(&input[..], 0), expected_cost.into());
} }
// second example from EIP: zero base. // second example from EIP: zero base.
{ {
let input = hex!(" let input = hex!(
"
0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000020 0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000020 0000000000000000000000000000000000000000000000000000000000000020
@ -1084,14 +1198,16 @@ mod tests {
let expected = hex!("0000000000000000000000000000000000000000000000000000000000000000"); let expected = hex!("0000000000000000000000000000000000000000000000000000000000000000");
let expected_cost = 13056; let expected_cost = 13056;
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]))
.expect("Builtin should not fail");
assert_eq!(output, expected); assert_eq!(output, expected);
assert_eq!(f.cost(&input[..], 0), expected_cost.into()); assert_eq!(f.cost(&input[..], 0), expected_cost.into());
} }
// another example from EIP: zero-padding // another example from EIP: zero-padding
{ {
let input = hex!(" let input = hex!(
"
0000000000000000000000000000000000000000000000000000000000000001 0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000020 0000000000000000000000000000000000000000000000000000000000000020
@ -1104,14 +1220,16 @@ mod tests {
let expected = hex!("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab"); let expected = hex!("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab");
let expected_cost = 768; let expected_cost = 768;
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]))
.expect("Builtin should not fail");
assert_eq!(output, expected); assert_eq!(output, expected);
assert_eq!(f.cost(&input[..], 0), expected_cost.into()); assert_eq!(f.cost(&input[..], 0), expected_cost.into());
} }
// zero-length modulus. // zero-length modulus.
{ {
let input = hex!(" let input = hex!(
"
0000000000000000000000000000000000000000000000000000000000000001 0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000
@ -1122,7 +1240,8 @@ mod tests {
let mut output = vec![]; let mut output = vec![];
let expected_cost = 0; let expected_cost = 0;
f.execute(&input[..], &mut BytesRef::Flexible(&mut output)).expect("Builtin should not fail"); f.execute(&input[..], &mut BytesRef::Flexible(&mut output))
.expect("Builtin should not fail");
assert_eq!(output.len(), 0); // shouldn't have written any output. assert_eq!(output.len(), 0); // shouldn't have written any output.
assert_eq!(f.cost(&input[..], 0), expected_cost.into()); assert_eq!(f.cost(&input[..], 0), expected_cost.into());
} }
@ -1130,7 +1249,6 @@ mod tests {
#[test] #[test]
fn bn128_add() { fn bn128_add() {
let f = Builtin { let f = Builtin {
pricer: map![0 => Pricing::Linear(Linear { base: 0, word: 0 })], pricer: map![0 => Pricing::Linear(Linear { base: 0, word: 0 })],
native: EthereumBuiltin::from_str("alt_bn128_add").unwrap(), native: EthereumBuiltin::from_str("alt_bn128_add").unwrap(),
@ -1138,7 +1256,8 @@ mod tests {
// zero-points additions // zero-points additions
{ {
let input = hex!(" let input = hex!(
"
0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000
@ -1146,12 +1265,14 @@ mod tests {
); );
let mut output = vec![0u8; 64]; let mut output = vec![0u8; 64];
let expected = hex!(" let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000" 0000000000000000000000000000000000000000000000000000000000000000"
); );
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]))
.expect("Builtin should not fail");
assert_eq!(output, &expected[..]); assert_eq!(output, &expected[..]);
} }
@ -1161,18 +1282,21 @@ mod tests {
let input = BytesRef::Fixed(&mut empty); let input = BytesRef::Fixed(&mut empty);
let mut output = vec![0u8; 64]; let mut output = vec![0u8; 64];
let expected = hex!(" let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000" 0000000000000000000000000000000000000000000000000000000000000000"
); );
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]))
.expect("Builtin should not fail");
assert_eq!(output, &expected[..]); assert_eq!(output, &expected[..]);
} }
// should fail - point not on curve // should fail - point not on curve
{ {
let input = hex!(" let input = hex!(
"
1111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111
@ -1195,25 +1319,29 @@ mod tests {
// zero-point multiplication // zero-point multiplication
{ {
let input = hex!(" let input = hex!(
"
0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000
0200000000000000000000000000000000000000000000000000000000000000" 0200000000000000000000000000000000000000000000000000000000000000"
); );
let mut output = vec![0u8; 64]; let mut output = vec![0u8; 64];
let expected = hex!(" let expected = hex!(
"
0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000" 0000000000000000000000000000000000000000000000000000000000000000"
); );
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]))
.expect("Builtin should not fail");
assert_eq!(output, &expected[..]); assert_eq!(output, &expected[..]);
} }
// should fail - point not on curve // should fail - point not on curve
{ {
let input = hex!(" let input = hex!(
"
1111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111
0f00000000000000000000000000000000000000000000000000000000000000" 0f00000000000000000000000000000000000000000000000000000000000000"
@ -1239,7 +1367,8 @@ mod tests {
let mut output = vec![0u8; expected.len()]; let mut output = vec![0u8; expected.len()];
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail"); f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]))
.expect("Builtin should not fail");
assert_eq!(output, expected); assert_eq!(output, expected);
} }
@ -1249,7 +1378,10 @@ mod tests {
if let Some(msg) = msg_contains { if let Some(msg) = msg_contains {
if let Err(e) = res { if let Err(e) = res {
if !e.contains(msg) { if !e.contains(msg) {
panic!("There should be error containing '{}' here, but got: '{}'", msg, e); panic!(
"There should be error containing '{}' here, but got: '{}'",
msg, e
);
} }
} }
} else { } else {
@ -1271,7 +1403,8 @@ mod tests {
// should fail - point not on curve // should fail - point not on curve
error_test( error_test(
builtin_pairing(), builtin_pairing(),
&hex!(" &hex!(
"
1111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111
@ -1288,7 +1421,8 @@ mod tests {
// should fail - input length is invalid // should fail - input length is invalid
error_test( error_test(
builtin_pairing(), builtin_pairing(),
&hex!(" &hex!(
"
1111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111
111111111111111111111111111111" 111111111111111111111111111111"
@ -1331,7 +1465,8 @@ mod tests {
let i = [0u8, 1, 2, 3]; let i = [0u8, 1, 2, 3];
let mut o = [255u8; 4]; let mut o = [255u8; 4];
b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]))
.expect("Builtin should not fail");
assert_eq!(i, o); assert_eq!(i, o);
} }
@ -1344,8 +1479,9 @@ mod tests {
info: None, info: None,
price: JsonPricing::Linear(JsonLinearPricing { base: 10, word: 20 }) price: JsonPricing::Linear(JsonLinearPricing { base: 10, word: 20 })
} }
] ],
}).expect("known builtin"); })
.expect("known builtin");
assert_eq!(b.cost(&[0; 0], 0), U256::from(10)); assert_eq!(b.cost(&[0; 0], 0), U256::from(10));
assert_eq!(b.cost(&[0; 1], 0), U256::from(30)); assert_eq!(b.cost(&[0; 1], 0), U256::from(30));
@ -1354,7 +1490,8 @@ mod tests {
let i = [0u8, 1, 2, 3]; let i = [0u8, 1, 2, 3];
let mut o = [255u8; 4]; let mut o = [255u8; 4];
b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail"); b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]))
.expect("Builtin should not fail");
assert_eq!(i, o); assert_eq!(i, o);
} }
@ -1378,10 +1515,19 @@ mod tests {
}), }),
} }
], ],
}).unwrap(); })
.unwrap();
assert_eq!(b.cost(&[0; 192 * 3], 10), U256::from(340_000), "80 000 * 3 + 100 000 == 340 000"); assert_eq!(
assert_eq!(b.cost(&[0; 192 * 7], 20), U256::from(283_000), "34 000 * 7 + 45 000 == 283 000"); b.cost(&[0; 192 * 3], 10),
U256::from(340_000),
"80 000 * 3 + 100 000 == 340 000"
);
assert_eq!(
b.cost(&[0; 192 * 7], 20),
U256::from(283_000),
"34 000 * 7 + 45 000 == 283 000"
);
} }
#[test] #[test]
@ -1404,10 +1550,15 @@ mod tests {
}), }),
} }
], ],
}).unwrap(); })
.unwrap();
assert_eq!(b.cost(&[0; 192], 10), U256::from(500)); assert_eq!(b.cost(&[0; 192], 10), U256::from(500));
assert_eq!(b.cost(&[0; 10], 20), U256::from(150), "after istanbul hardfork gas cost for add should be 150"); assert_eq!(
b.cost(&[0; 10], 20),
U256::from(150),
"after istanbul hardfork gas cost for add should be 150"
);
} }
#[test] #[test]
@ -1430,13 +1581,17 @@ mod tests {
}), }),
} }
], ],
}).unwrap(); })
.unwrap();
assert_eq!(b.cost(&[0; 192], 10), U256::from(40_000)); assert_eq!(b.cost(&[0; 192], 10), U256::from(40_000));
assert_eq!(b.cost(&[0; 10], 20), U256::from(6_000), "after istanbul hardfork gas cost for mul should be 6 000"); assert_eq!(
b.cost(&[0; 10], 20),
U256::from(6_000),
"after istanbul hardfork gas cost for mul should be 6 000"
);
} }
#[test] #[test]
fn multimap_use_most_recent_on_activate() { fn multimap_use_most_recent_on_activate() {
let b = Builtin::try_from(JsonBuiltin { let b = Builtin::try_from(JsonBuiltin {
@ -1463,18 +1618,26 @@ mod tests {
word: 0, word: 0,
}) })
} }
] ],
}).unwrap(); })
.unwrap();
assert_eq!(b.cost(&[0; 2], 0), U256::zero(), "not activated yet; should be zero"); assert_eq!(
b.cost(&[0; 2], 0),
U256::zero(),
"not activated yet; should be zero"
);
assert_eq!(b.cost(&[0; 3], 10), U256::from(40_000), "use price #1"); assert_eq!(b.cost(&[0; 3], 10), U256::from(40_000), "use price #1");
assert_eq!(b.cost(&[0; 4], 20), U256::from(6_000), "use price #2"); assert_eq!(b.cost(&[0; 4], 20), U256::from(6_000), "use price #2");
assert_eq!(b.cost(&[0; 1], 99), U256::from(6_000), "use price #2"); assert_eq!(b.cost(&[0; 1], 99), U256::from(6_000), "use price #2");
assert_eq!(b.cost(&[0; 1], 100), U256::from(1_337), "use price #3"); assert_eq!(b.cost(&[0; 1], 100), U256::from(1_337), "use price #3");
assert_eq!(b.cost(&[0; 1], u64::max_value()), U256::from(1_337), "use price #3 indefinitely"); assert_eq!(
b.cost(&[0; 1], u64::max_value()),
U256::from(1_337),
"use price #3 indefinitely"
);
} }
#[test] #[test]
fn multimap_use_last_with_same_activate_at() { fn multimap_use_last_with_same_activate_at() {
let b = Builtin::try_from(JsonBuiltin { let b = Builtin::try_from(JsonBuiltin {
@ -1502,7 +1665,8 @@ mod tests {
}), }),
} }
], ],
}).unwrap(); })
.unwrap();
assert_eq!(b.cost(&[0; 1], 0), U256::from(0), "not activated yet"); assert_eq!(b.cost(&[0; 1], 0), U256::from(0), "not activated yet");
assert_eq!(b.cost(&[0; 1], 1), U256::from(1_337)); assert_eq!(b.cost(&[0; 1], 1), U256::from(1_337));

View File

@ -16,8 +16,10 @@
//! Database cache manager //! Database cache manager
use std::collections::{VecDeque, HashSet}; use std::{
use std::hash::Hash; collections::{HashSet, VecDeque},
hash::Hash,
};
const COLLECTION_QUEUE_SIZE: usize = 8; const COLLECTION_QUEUE_SIZE: usize = 8;
@ -26,24 +28,39 @@ pub struct CacheManager<T> {
pref_cache_size: usize, pref_cache_size: usize,
max_cache_size: usize, max_cache_size: usize,
bytes_per_cache_entry: usize, bytes_per_cache_entry: usize,
cache_usage: VecDeque<HashSet<T>> cache_usage: VecDeque<HashSet<T>>,
} }
impl<T> CacheManager<T> where T: Eq + Hash { impl<T> CacheManager<T>
where
T: Eq + Hash,
{
/// Create new cache manager with preferred (heap) sizes. /// Create new cache manager with preferred (heap) sizes.
pub fn new(pref_cache_size: usize, max_cache_size: usize, bytes_per_cache_entry: usize) -> Self { pub fn new(
pref_cache_size: usize,
max_cache_size: usize,
bytes_per_cache_entry: usize,
) -> Self {
CacheManager { CacheManager {
pref_cache_size: pref_cache_size, pref_cache_size: pref_cache_size,
max_cache_size: max_cache_size, max_cache_size: max_cache_size,
bytes_per_cache_entry: bytes_per_cache_entry, bytes_per_cache_entry: bytes_per_cache_entry,
cache_usage: (0..COLLECTION_QUEUE_SIZE).into_iter().map(|_| Default::default()).collect(), cache_usage: (0..COLLECTION_QUEUE_SIZE)
.into_iter()
.map(|_| Default::default())
.collect(),
} }
} }
/// Mark element as used. /// Mark element as used.
pub fn note_used(&mut self, id: T) { pub fn note_used(&mut self, id: T) {
if !self.cache_usage[0].contains(&id) { if !self.cache_usage[0].contains(&id) {
if let Some(c) = self.cache_usage.iter_mut().skip(1).find(|e| e.contains(&id)) { if let Some(c) = self
.cache_usage
.iter_mut()
.skip(1)
.find(|e| e.contains(&id))
{
c.remove(&id); c.remove(&id);
} }
self.cache_usage[0].insert(id); self.cache_usage[0].insert(id);
@ -53,7 +70,10 @@ impl<T> CacheManager<T> where T: Eq + Hash {
/// Collects unused objects from cache. /// Collects unused objects from cache.
/// First params is the current size of the cache. /// First params is the current size of the cache.
/// Second one is an with objects to remove. It should also return new size of the cache. /// Second one is an with objects to remove. It should also return new size of the cache.
pub fn collect_garbage<F>(&mut self, current_size: usize, mut notify_unused: F) where F: FnMut(HashSet<T>) -> usize { pub fn collect_garbage<F>(&mut self, current_size: usize, mut notify_unused: F)
where
F: FnMut(HashSet<T>) -> usize,
{
if current_size < self.pref_cache_size { if current_size < self.pref_cache_size {
self.rotate_cache_if_needed(); self.rotate_cache_if_needed();
return; return;
@ -64,16 +84,20 @@ impl<T> CacheManager<T> where T: Eq + Hash {
let current_size = notify_unused(back); let current_size = notify_unused(back);
self.cache_usage.push_front(Default::default()); self.cache_usage.push_front(Default::default());
if current_size < self.max_cache_size { if current_size < self.max_cache_size {
break break;
} }
} }
} }
} }
fn rotate_cache_if_needed(&mut self) { fn rotate_cache_if_needed(&mut self) {
if self.cache_usage.is_empty() { return } if self.cache_usage.is_empty() {
return;
}
if self.cache_usage[0].len() * self.bytes_per_cache_entry > self.pref_cache_size / COLLECTION_QUEUE_SIZE { if self.cache_usage[0].len() * self.bytes_per_cache_entry
> self.pref_cache_size / COLLECTION_QUEUE_SIZE
{
if let Some(cache) = self.cache_usage.pop_back() { if let Some(cache) = self.cache_usage.pop_back() {
self.cache_usage.push_front(cache); self.cache_usage.push_front(cache);
} }

View File

@ -16,11 +16,9 @@
//! Database utilities and definitions. //! Database utilities and definitions.
use std::ops::Deref;
use std::hash::Hash;
use std::collections::HashMap;
use parking_lot::RwLock;
use kvdb::{DBTransaction, KeyValueDB}; use kvdb::{DBTransaction, KeyValueDB};
use parking_lot::RwLock;
use std::{collections::HashMap, hash::Hash, ops::Deref};
use rlp; use rlp;
@ -65,7 +63,10 @@ pub trait Cache<K, V> {
fn get(&self, k: &K) -> Option<&V>; fn get(&self, k: &K) -> Option<&V>;
} }
impl<K, V> Cache<K, V> for HashMap<K, V> where K: Hash + Eq { impl<K, V> Cache<K, V> for HashMap<K, V>
where
K: Hash + Eq,
{
fn insert(&mut self, k: K, v: V) -> Option<V> { fn insert(&mut self, k: K, v: V) -> Option<V> {
HashMap::insert(self, k, v) HashMap::insert(self, k, v)
} }
@ -91,21 +92,35 @@ pub trait Key<T> {
/// Should be used to write value into database. /// Should be used to write value into database.
pub trait Writable { pub trait Writable {
/// Writes the value into the database. /// Writes the value into the database.
fn write<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>, value: &T) where T: rlp::Encodable, R: Deref<Target = [u8]>; fn write<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>, value: &T)
where
T: rlp::Encodable,
R: Deref<Target = [u8]>;
/// Deletes key from the databse. /// Deletes key from the databse.
fn delete<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>) where T: rlp::Encodable, R: Deref<Target = [u8]>; fn delete<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>)
where
T: rlp::Encodable,
R: Deref<Target = [u8]>;
/// Writes the value into the database and updates the cache. /// Writes the value into the database and updates the cache.
fn write_with_cache<K, T, R>(&mut self, col: Option<u32>, cache: &mut Cache<K, T>, key: K, value: T, policy: CacheUpdatePolicy) where fn write_with_cache<K, T, R>(
&mut self,
col: Option<u32>,
cache: &mut Cache<K, T>,
key: K,
value: T,
policy: CacheUpdatePolicy,
) where
K: Key<T, Target = R> + Hash + Eq, K: Key<T, Target = R> + Hash + Eq,
T: rlp::Encodable, T: rlp::Encodable,
R: Deref<Target = [u8]> { R: Deref<Target = [u8]>,
{
self.write(col, &key, &value); self.write(col, &key, &value);
match policy { match policy {
CacheUpdatePolicy::Overwrite => { CacheUpdatePolicy::Overwrite => {
cache.insert(key, value); cache.insert(key, value);
}, }
CacheUpdatePolicy::Remove => { CacheUpdatePolicy::Remove => {
cache.remove(&key); cache.remove(&key);
} }
@ -113,31 +128,45 @@ pub trait Writable {
} }
/// Writes the values into the database and updates the cache. /// Writes the values into the database and updates the cache.
fn extend_with_cache<K, T, R>(&mut self, col: Option<u32>, cache: &mut Cache<K, T>, values: HashMap<K, T>, policy: CacheUpdatePolicy) where fn extend_with_cache<K, T, R>(
&mut self,
col: Option<u32>,
cache: &mut Cache<K, T>,
values: HashMap<K, T>,
policy: CacheUpdatePolicy,
) where
K: Key<T, Target = R> + Hash + Eq, K: Key<T, Target = R> + Hash + Eq,
T: rlp::Encodable, T: rlp::Encodable,
R: Deref<Target = [u8]> { R: Deref<Target = [u8]>,
{
match policy { match policy {
CacheUpdatePolicy::Overwrite => { CacheUpdatePolicy::Overwrite => {
for (key, value) in values { for (key, value) in values {
self.write(col, &key, &value); self.write(col, &key, &value);
cache.insert(key, value); cache.insert(key, value);
} }
}, }
CacheUpdatePolicy::Remove => { CacheUpdatePolicy::Remove => {
for (key, value) in &values { for (key, value) in &values {
self.write(col, key, value); self.write(col, key, value);
cache.remove(key); cache.remove(key);
} }
}, }
} }
} }
/// Writes and removes the values into the database and updates the cache. /// Writes and removes the values into the database and updates the cache.
fn extend_with_option_cache<K, T, R>(&mut self, col: Option<u32>, cache: &mut Cache<K, Option<T>>, values: HashMap<K, Option<T>>, policy: CacheUpdatePolicy) where fn extend_with_option_cache<K, T, R>(
&mut self,
col: Option<u32>,
cache: &mut Cache<K, Option<T>>,
values: HashMap<K, Option<T>>,
policy: CacheUpdatePolicy,
) where
K: Key<T, Target = R> + Hash + Eq, K: Key<T, Target = R> + Hash + Eq,
T: rlp::Encodable, T: rlp::Encodable,
R: Deref<Target = [u8]> { R: Deref<Target = [u8]>,
{
match policy { match policy {
CacheUpdatePolicy::Overwrite => { CacheUpdatePolicy::Overwrite => {
for (key, value) in values { for (key, value) in values {
@ -147,7 +176,7 @@ pub trait Writable {
} }
cache.insert(key, value); cache.insert(key, value);
} }
}, }
CacheUpdatePolicy::Remove => { CacheUpdatePolicy::Remove => {
for (key, value) in values { for (key, value) in values {
match value { match value {
@ -156,24 +185,26 @@ pub trait Writable {
} }
cache.remove(&key); cache.remove(&key);
} }
},
} }
} }
}
} }
/// Should be used to read values from database. /// Should be used to read values from database.
pub trait Readable { pub trait Readable {
/// Returns value for given key. /// Returns value for given key.
fn read<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> Option<T> where fn read<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> Option<T>
where
T: rlp::Decodable, T: rlp::Decodable,
R: Deref<Target = [u8]>; R: Deref<Target = [u8]>;
/// Returns value for given key either in cache or in database. /// Returns value for given key either in cache or in database.
fn read_with_cache<K, T, C>(&self, col: Option<u32>, cache: &RwLock<C>, key: &K) -> Option<T> where fn read_with_cache<K, T, C>(&self, col: Option<u32>, cache: &RwLock<C>, key: &K) -> Option<T>
where
K: Key<T> + Eq + Hash + Clone, K: Key<T> + Eq + Hash + Clone,
T: Clone + rlp::Decodable, T: Clone + rlp::Decodable,
C: Cache<K, T> { C: Cache<K, T>,
{
{ {
let read = cache.read(); let read = cache.read();
if let Some(v) = read.get(key) { if let Some(v) = read.get(key) {
@ -181,7 +212,7 @@ pub trait Readable {
} }
} }
self.read(col, key).map(|value: T|{ self.read(col, key).map(|value: T| {
let mut write = cache.write(); let mut write = cache.write();
write.insert(key.clone(), value.clone()); write.insert(key.clone(), value.clone());
value value
@ -189,10 +220,18 @@ pub trait Readable {
} }
/// Returns value for given key either in two-layered cache or in database. /// Returns value for given key either in two-layered cache or in database.
fn read_with_two_layer_cache<K, T, C>(&self, col: Option<u32>, l1_cache: &RwLock<C>, l2_cache: &RwLock<C>, key: &K) -> Option<T> where fn read_with_two_layer_cache<K, T, C>(
&self,
col: Option<u32>,
l1_cache: &RwLock<C>,
l2_cache: &RwLock<C>,
key: &K,
) -> Option<T>
where
K: Key<T> + Eq + Hash + Clone, K: Key<T> + Eq + Hash + Clone,
T: Clone + rlp::Decodable, T: Clone + rlp::Decodable,
C: Cache<K, T> { C: Cache<K, T>,
{
{ {
let read = l1_cache.read(); let read = l1_cache.read();
if let Some(v) = read.get(key) { if let Some(v) = read.get(key) {
@ -204,13 +243,17 @@ pub trait Readable {
} }
/// Returns true if given value exists. /// Returns true if given value exists.
fn exists<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> bool where R: Deref<Target= [u8]>; fn exists<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> bool
where
R: Deref<Target = [u8]>;
/// Returns true if given value exists either in cache or in database. /// Returns true if given value exists either in cache or in database.
fn exists_with_cache<K, T, R, C>(&self, col: Option<u32>, cache: &RwLock<C>, key: &K) -> bool where fn exists_with_cache<K, T, R, C>(&self, col: Option<u32>, cache: &RwLock<C>, key: &K) -> bool
where
K: Eq + Hash + Key<T, Target = R>, K: Eq + Hash + Key<T, Target = R>,
R: Deref<Target = [u8]>, R: Deref<Target = [u8]>,
C: Cache<K, T> { C: Cache<K, T>,
{
{ {
let read = cache.read(); let read = cache.read();
if read.get(key).is_some() { if read.get(key).is_some() {
@ -223,31 +266,48 @@ pub trait Readable {
} }
impl Writable for DBTransaction { impl Writable for DBTransaction {
fn write<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>, value: &T) where T: rlp::Encodable, R: Deref<Target = [u8]> { fn write<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>, value: &T)
where
T: rlp::Encodable,
R: Deref<Target = [u8]>,
{
self.put(col, &key.key(), &rlp::encode(value)); self.put(col, &key.key(), &rlp::encode(value));
} }
fn delete<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>) where T: rlp::Encodable, R: Deref<Target = [u8]> { fn delete<T, R>(&mut self, col: Option<u32>, key: &Key<T, Target = R>)
where
T: rlp::Encodable,
R: Deref<Target = [u8]>,
{
self.delete(col, &key.key()); self.delete(col, &key.key());
} }
} }
impl<KVDB: KeyValueDB + ?Sized> Readable for KVDB { impl<KVDB: KeyValueDB + ?Sized> Readable for KVDB {
fn read<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> Option<T> fn read<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> Option<T>
where T: rlp::Decodable, R: Deref<Target = [u8]> { where
T: rlp::Decodable,
R: Deref<Target = [u8]>,
{
self.get(col, &key.key()) self.get(col, &key.key())
.expect(&format!("db get failed, key: {:?}", &key.key() as &[u8])) .expect(&format!("db get failed, key: {:?}", &key.key() as &[u8]))
.map(|v| rlp::decode(&v).expect("decode db value failed") ) .map(|v| rlp::decode(&v).expect("decode db value failed"))
} }
fn exists<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> bool where R: Deref<Target = [u8]> { fn exists<T, R>(&self, col: Option<u32>, key: &Key<T, Target = R>) -> bool
where
R: Deref<Target = [u8]>,
{
let result = self.get(col, &key.key()); let result = self.get(col, &key.key());
match result { match result {
Ok(v) => v.is_some(), Ok(v) => v.is_some(),
Err(err) => { Err(err) => {
panic!("db get failed, key: {:?}, err: {:?}", &key.key() as &[u8], err); panic!(
"db get failed, key: {:?}, err: {:?}",
&key.key() as &[u8],
err
);
} }
} }
} }

View File

@ -16,17 +16,14 @@
//! Blockchain DB extras. //! Blockchain DB extras.
use std::io::Write; use std::{io::Write, ops};
use std::ops;
use common_types::BlockNumber; use common_types::{engines::epoch::Transition as EpochTransition, receipt::Receipt, BlockNumber};
use common_types::engines::epoch::Transition as EpochTransition;
use common_types::receipt::Receipt;
use ethereum_types::{H256, H264, U256}; use ethereum_types::{H256, H264, U256};
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
use kvdb::PREFIX_LEN as DB_PREFIX_LEN; use kvdb::PREFIX_LEN as DB_PREFIX_LEN;
use rlp; use rlp;
use rlp_derive::{RlpEncodableWrapper, RlpDecodableWrapper, RlpEncodable, RlpDecodable}; use rlp_derive::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper};
use crate::db::Key; use crate::db::Key;
@ -117,7 +114,18 @@ pub const EPOCH_KEY_LEN: usize = DB_PREFIX_LEN + 16;
/// epoch key prefix. /// epoch key prefix.
/// used to iterate over all epoch transitions in order from genesis. /// used to iterate over all epoch transitions in order from genesis.
pub const EPOCH_KEY_PREFIX: &'static [u8; DB_PREFIX_LEN] = &[ pub const EPOCH_KEY_PREFIX: &'static [u8; DB_PREFIX_LEN] = &[
ExtrasIndex::EpochTransitions as u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ExtrasIndex::EpochTransitions as u8,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
]; ];
/// Epoch transitions key /// Epoch transitions key
@ -126,7 +134,9 @@ pub struct EpochTransitionsKey([u8; EPOCH_KEY_LEN]);
impl ops::Deref for EpochTransitionsKey { impl ops::Deref for EpochTransitionsKey {
type Target = [u8]; type Target = [u8];
fn deref(&self) -> &[u8] { &self.0[..] } fn deref(&self) -> &[u8] {
&self.0[..]
}
} }
impl Key<EpochTransitions> for u64 { impl Key<EpochTransitions> for u64 {
@ -163,8 +173,12 @@ impl rlp::Encodable for BlockDetails {
let use_short_version = !self.is_finalized; let use_short_version = !self.is_finalized;
match use_short_version { match use_short_version {
true => { stream.begin_list(4); }, true => {
false => { stream.begin_list(5); }, stream.begin_list(4);
}
false => {
stream.begin_list(5);
}
} }
stream.append(&self.number); stream.append(&self.number);
@ -211,11 +225,13 @@ pub struct TransactionAddress {
/// Block hash /// Block hash
pub block_hash: H256, pub block_hash: H256,
/// Transaction index within the block /// Transaction index within the block
pub index: usize pub index: usize,
} }
impl HeapSizeOf for TransactionAddress { impl HeapSizeOf for TransactionAddress {
fn heap_size_of_children(&self) -> usize { 0 } fn heap_size_of_children(&self) -> usize {
0
}
} }
/// Contains all block receipts. /// Contains all block receipts.
@ -228,9 +244,7 @@ pub struct BlockReceipts {
impl BlockReceipts { impl BlockReceipts {
/// Create new block receipts wrapper. /// Create new block receipts wrapper.
pub fn new(receipts: Vec<Receipt>) -> Self { pub fn new(receipts: Vec<Receipt>) -> Self {
BlockReceipts { BlockReceipts { receipts: receipts }
receipts: receipts
}
} }
} }

View File

@ -20,7 +20,7 @@
mod db; mod db;
pub mod keys;
pub mod cache_manager; pub mod cache_manager;
pub mod keys;
pub use self::db::*; pub use self::db::*;

View File

@ -20,23 +20,21 @@
extern crate criterion; extern crate criterion;
extern crate bit_set; extern crate bit_set;
extern crate ethereum_types; extern crate ethereum_types;
extern crate parking_lot;
extern crate heapsize;
extern crate vm;
extern crate evm; extern crate evm;
extern crate heapsize;
extern crate keccak_hash as hash; extern crate keccak_hash as hash;
extern crate memory_cache; extern crate memory_cache;
extern crate parity_bytes as bytes; extern crate parity_bytes as bytes;
extern crate parking_lot;
extern crate rustc_hex; extern crate rustc_hex;
extern crate vm;
use criterion::{Criterion, Bencher, black_box}; use criterion::{black_box, Bencher, Criterion};
use std::str::FromStr; use ethereum_types::{Address, U256};
use std::sync::Arc;
use ethereum_types::{U256, Address};
use vm::{ActionParams, Result, GasLeft, Ext};
use vm::tests::FakeExt;
use evm::Factory; use evm::Factory;
use rustc_hex::FromHex; use rustc_hex::FromHex;
use std::{str::FromStr, sync::Arc};
use vm::{tests::FakeExt, ActionParams, Ext, GasLeft, Result};
criterion_group!( criterion_group!(
basic, basic,
@ -68,9 +66,7 @@ fn simple_loop_log0(gas: U256, b: &mut Bencher) {
let mut ext = FakeExt::new(); let mut ext = FakeExt::new();
let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
let code = black_box( let code = black_box("62ffffff5b600190036000600fa0600357".from_hex().unwrap());
"62ffffff5b600190036000600fa0600357".from_hex().unwrap()
);
b.iter(|| { b.iter(|| {
let mut params = ActionParams::default(); let mut params = ActionParams::default();
@ -104,7 +100,9 @@ fn mem_gas_calculation_same(gas: U256, b: &mut Bencher) {
b.iter(|| { b.iter(|| {
let code = black_box( let code = black_box(
"6110006001556001546000555b610fff805560016000540380600055600c57".from_hex().unwrap() "6110006001556001546000555b610fff805560016000540380600055600c57"
.from_hex()
.unwrap(),
); );
let mut params = ActionParams::default(); let mut params = ActionParams::default();
@ -138,7 +136,9 @@ fn mem_gas_calculation_increasing(gas: U256, b: &mut Bencher) {
b.iter(|| { b.iter(|| {
let code = black_box( let code = black_box(
"6110006001556001546000555b610fff60005401805560016000540380600055600c57".from_hex().unwrap() "6110006001556001546000555b610fff60005401805560016000540380600055600c57"
.from_hex()
.unwrap(),
); );
let mut params = ActionParams::default(); let mut params = ActionParams::default();

View File

@ -16,9 +16,9 @@
//! Evm interface. //! Evm interface.
use std::{ops, cmp, fmt};
use ethereum_types::{U128, U256, U512}; use ethereum_types::{U128, U256, U512};
use vm::{Ext, Result, ReturnData, GasLeft, Error}; use std::{cmp, fmt, ops};
use vm::{Error, Ext, GasLeft, Result, ReturnData};
/// Finalization result. Gas Left: either it is a known value, or it needs to be computed by processing /// Finalization result. Gas Left: either it is a known value, or it needs to be computed by processing
/// a return instruction. /// a return instruction.
@ -44,18 +44,22 @@ pub trait Finalize {
impl Finalize for Result<GasLeft> { impl Finalize for Result<GasLeft> {
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult> { fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult> {
match self { match self {
Ok(GasLeft::Known(gas_left)) => { Ok(GasLeft::Known(gas_left)) => Ok(FinalizationResult {
Ok(FinalizationResult {
gas_left, gas_left,
apply_state: true, apply_state: true,
return_data: ReturnData::empty() return_data: ReturnData::empty(),
}) }),
}, Ok(GasLeft::NeedsReturn {
Ok(GasLeft::NeedsReturn { gas_left, data, apply_state }) => { gas_left,
ext.ret(&gas_left, &data, apply_state).map(|gas_left| data,
FinalizationResult { gas_left, apply_state, return_data: data } apply_state,
) }) => ext
}, .ret(&gas_left, &data, apply_state)
.map(|gas_left| FinalizationResult {
gas_left,
apply_state,
return_data: data,
}),
Err(err) => Err(err), Err(err) => Err(err),
} }
} }
@ -68,10 +72,20 @@ impl Finalize for Error {
} }
/// Cost calculation type. For low-gas usage we calculate costs using usize instead of U256 /// Cost calculation type. For low-gas usage we calculate costs using usize instead of U256
pub trait CostType: Sized + From<usize> + Copy + Send pub trait CostType:
+ ops::Mul<Output=Self> + ops::Div<Output=Self> + ops::Add<Output=Self> + ops::Sub<Output=Self> Sized
+ ops::Shr<usize, Output=Self> + ops::Shl<usize, Output=Self> + From<usize>
+ cmp::Ord + fmt::Debug { + Copy
+ Send
+ ops::Mul<Output = Self>
+ ops::Div<Output = Self>
+ ops::Add<Output = Self>
+ ops::Sub<Output = Self>
+ ops::Shr<usize, Output = Self>
+ ops::Shl<usize, Output = Self>
+ cmp::Ord
+ fmt::Debug
{
/// Converts this cost into `U256` /// Converts this cost into `U256`
fn as_u256(&self) -> U256; fn as_u256(&self) -> U256;
/// Tries to fit `U256` into this `Cost` type /// Tries to fit `U256` into this `Cost` type
@ -113,10 +127,7 @@ impl CostType for U256 {
let U512(parts) = x; let U512(parts) = x;
let overflow = (parts[4] | parts[5] | parts[6] | parts[7]) > 0; let overflow = (parts[4] | parts[5] | parts[6] | parts[7]) > 0;
let U512(parts) = x >> shr; let U512(parts) = x >> shr;
( (U256([parts[0], parts[1], parts[2], parts[3]]), overflow)
U256([parts[0], parts[1], parts[2], parts[3]]),
overflow
)
} }
} }
@ -161,8 +172,8 @@ impl CostType for usize {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ethereum_types::U256;
use super::CostType; use super::CostType;
use ethereum_types::U256;
#[test] #[test]
fn should_calculate_overflow_mul_shr_without_overflow() { fn should_calculate_overflow_mul_shr_without_overflow() {

View File

@ -16,12 +16,10 @@
//! Evm factory. //! Evm factory.
//! //!
use super::{interpreter::SharedCache, vm::ActionParams, vmtype::VMType};
use ethereum_types::U256;
use std::sync::Arc; use std::sync::Arc;
use vm::{Exec, Schedule}; use vm::{Exec, Schedule};
use ethereum_types::U256;
use super::vm::ActionParams;
use super::interpreter::SharedCache;
use super::vmtype::VMType;
/// Evm factory. Creates appropriate Evm. /// Evm factory. Creates appropriate Evm.
#[derive(Clone)] #[derive(Clone)]
@ -35,10 +33,22 @@ impl Factory {
/// Might choose implementation depending on supplied gas. /// Might choose implementation depending on supplied gas.
pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box<Exec> { pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box<Exec> {
match self.evm { match self.evm {
VMType::Interpreter => if Self::can_fit_in_usize(&params.gas) { VMType::Interpreter => {
Box::new(super::interpreter::Interpreter::<usize>::new(params, self.evm_cache.clone(), schedule, depth)) if Self::can_fit_in_usize(&params.gas) {
Box::new(super::interpreter::Interpreter::<usize>::new(
params,
self.evm_cache.clone(),
schedule,
depth,
))
} else { } else {
Box::new(super::interpreter::Interpreter::<U256>::new(params, self.evm_cache.clone(), schedule, depth)) Box::new(super::interpreter::Interpreter::<U256>::new(
params,
self.evm_cache.clone(),
schedule,
depth,
))
}
} }
} }
} }
@ -69,9 +79,8 @@ impl Default for Factory {
#[test] #[test]
fn test_create_vm() { fn test_create_vm() {
use vm::Ext;
use vm::tests::FakeExt;
use bytes::Bytes; use bytes::Bytes;
use vm::{tests::FakeExt, Ext};
let mut params = ActionParams::default(); let mut params = ActionParams::default();
params.code = Some(Arc::new(Bytes::default())); params.code = Some(Arc::new(Bytes::default()));

View File

@ -440,13 +440,18 @@ pub struct InstructionInfo {
/// Number of returned stack items. /// Number of returned stack items.
pub ret: usize, pub ret: usize,
/// Gas price tier. /// Gas price tier.
pub tier: GasPriceTier pub tier: GasPriceTier,
} }
impl InstructionInfo { impl InstructionInfo {
/// Create new instruction info. /// Create new instruction info.
pub fn new(name: &'static str, args: usize, ret: usize, tier: GasPriceTier) -> Self { pub fn new(name: &'static str, args: usize, ret: usize, tier: GasPriceTier) -> Self {
InstructionInfo { name, args, ret, tier } InstructionInfo {
name,
args,
ret,
tier,
}
} }
} }

View File

@ -14,28 +14,30 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::cmp;
use ethereum_types::{U256, H256};
use super::u256_to_address; use super::u256_to_address;
use ethereum_types::{H256, U256};
use std::cmp;
use {evm, vm}; use evm;
use instructions::{self, Instruction, InstructionInfo}; use instructions::{self, Instruction, InstructionInfo};
use interpreter::stack::Stack; use interpreter::stack::Stack;
use vm::Schedule; use vm::{self, Schedule};
macro_rules! overflowing { macro_rules! overflowing {
($x: expr) => {{ ($x: expr) => {{
let (v, overflow) = $x; let (v, overflow) = $x;
if overflow { return Err(vm::Error::OutOfGas); } if overflow {
return Err(vm::Error::OutOfGas);
}
v v
}} }};
} }
enum Request<Cost: ::evm::CostType> { enum Request<Cost: ::evm::CostType> {
Gas(Cost), Gas(Cost),
GasMem(Cost, Cost), GasMem(Cost, Cost),
GasMemProvide(Cost, Cost, Option<U256>), GasMemProvide(Cost, Cost, Option<U256>),
GasMemCopy(Cost, Cost, Cost) GasMemCopy(Cost, Cost, Cost),
} }
pub struct InstructionRequirements<Cost> { pub struct InstructionRequirements<Cost> {
@ -51,7 +53,6 @@ pub struct Gasometer<Gas> {
} }
impl<Gas: evm::CostType> Gasometer<Gas> { impl<Gas: evm::CostType> Gasometer<Gas> {
pub fn new(current_gas: Gas) -> Self { pub fn new(current_gas: Gas) -> Self {
Gasometer { Gasometer {
current_gas: current_gas, current_gas: current_gas,
@ -62,13 +63,18 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
pub fn verify_gas(&self, gas_cost: &Gas) -> vm::Result<()> { pub fn verify_gas(&self, gas_cost: &Gas) -> vm::Result<()> {
match &self.current_gas < gas_cost { match &self.current_gas < gas_cost {
true => Err(vm::Error::OutOfGas), true => Err(vm::Error::OutOfGas),
false => Ok(()) false => Ok(()),
} }
} }
/// How much gas is provided to a CALL/CREATE, given that we need to deduct `needed` for this operation /// How much gas is provided to a CALL/CREATE, given that we need to deduct `needed` for this operation
/// and that we `requested` some. /// and that we `requested` some.
pub fn gas_provided(&self, schedule: &Schedule, needed: Gas, requested: Option<U256>) -> vm::Result<Gas> { pub fn gas_provided(
&self,
schedule: &Schedule,
needed: Gas,
requested: Option<U256>,
) -> vm::Result<Gas> {
// Try converting requested gas to `Gas` (`U256/u64`) // Try converting requested gas to `Gas` (`U256/u64`)
// but in EIP150 even if we request more we should never fail from OOG // but in EIP150 even if we request more we should never fail from OOG
let requested = requested.map(Gas::from_u256); let requested = requested.map(Gas::from_u256);
@ -86,7 +92,7 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
} else { } else {
Ok(max_gas_provided) Ok(max_gas_provided)
} }
}, }
_ => { _ => {
if let Some(r) = requested { if let Some(r) = requested {
r r
@ -95,7 +101,7 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
} else { } else {
Ok(0.into()) Ok(0.into())
} }
}, }
} }
} }
@ -117,9 +123,7 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
let default_gas = Gas::from(schedule.tier_step_gas[tier]); let default_gas = Gas::from(schedule.tier_step_gas[tier]);
let cost = match instruction { let cost = match instruction {
instructions::JUMPDEST => { instructions::JUMPDEST => Request::Gas(Gas::from(1)),
Request::Gas(Gas::from(1))
},
instructions::SSTORE => { instructions::SSTORE => {
if schedule.eip1706 && self.current_gas <= Gas::from(schedule.call_stipend) { if schedule.eip1706 && self.current_gas <= Gas::from(schedule.call_stipend) {
return Err(vm::Error::OutOfGas); return Err(vm::Error::OutOfGas);
@ -141,77 +145,87 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
} }
}; };
Request::Gas(Gas::from(gas)) Request::Gas(Gas::from(gas))
}, }
instructions::SLOAD => { instructions::SLOAD => Request::Gas(Gas::from(schedule.sload_gas)),
Request::Gas(Gas::from(schedule.sload_gas)) instructions::BALANCE => Request::Gas(Gas::from(schedule.balance_gas)),
}, instructions::EXTCODESIZE => Request::Gas(Gas::from(schedule.extcodesize_gas)),
instructions::BALANCE => { instructions::EXTCODEHASH => Request::Gas(Gas::from(schedule.extcodehash_gas)),
Request::Gas(Gas::from(schedule.balance_gas))
},
instructions::EXTCODESIZE => {
Request::Gas(Gas::from(schedule.extcodesize_gas))
},
instructions::EXTCODEHASH => {
Request::Gas(Gas::from(schedule.extcodehash_gas))
},
instructions::SUICIDE => { instructions::SUICIDE => {
let mut gas = Gas::from(schedule.suicide_gas); let mut gas = Gas::from(schedule.suicide_gas);
let is_value_transfer = !ext.origin_balance()?.is_zero(); let is_value_transfer = !ext.origin_balance()?.is_zero();
let address = u256_to_address(stack.peek(0)); let address = u256_to_address(stack.peek(0));
if ( if (!schedule.no_empty && !ext.exists(&address)?)
!schedule.no_empty && !ext.exists(&address)? || (schedule.no_empty
) || ( && is_value_transfer
schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address)? && !ext.exists_and_not_null(&address)?)
) { {
gas = overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into())); gas =
overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into()));
} }
Request::Gas(gas) Request::Gas(gas)
}, }
instructions::MSTORE | instructions::MLOAD => { instructions::MSTORE | instructions::MLOAD => {
Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 32)?) Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 32)?)
}, }
instructions::MSTORE8 => { instructions::MSTORE8 => {
Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 1)?) Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 1)?)
}, }
instructions::RETURN | instructions::REVERT => { instructions::RETURN | instructions::REVERT => {
Request::GasMem(default_gas, mem_needed(stack.peek(0), stack.peek(1))?) Request::GasMem(default_gas, mem_needed(stack.peek(0), stack.peek(1))?)
}, }
instructions::SHA3 => { instructions::SHA3 => {
let words = overflowing!(to_word_size(Gas::from_u256(*stack.peek(1))?)); let words = overflowing!(to_word_size(Gas::from_u256(*stack.peek(1))?));
let gas = overflowing!(Gas::from(schedule.sha3_gas).overflow_add(overflowing!(Gas::from(schedule.sha3_word_gas).overflow_mul(words)))); let gas = overflowing!(Gas::from(schedule.sha3_gas).overflow_add(overflowing!(
Gas::from(schedule.sha3_word_gas).overflow_mul(words)
)));
Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?) Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?)
}, }
instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => { instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => {
Request::GasMemCopy(default_gas, mem_needed(stack.peek(0), stack.peek(2))?, Gas::from_u256(*stack.peek(2))?) Request::GasMemCopy(
}, default_gas,
instructions::EXTCODECOPY => { mem_needed(stack.peek(0), stack.peek(2))?,
Request::GasMemCopy(schedule.extcodecopy_base_gas.into(), mem_needed(stack.peek(1), stack.peek(3))?, Gas::from_u256(*stack.peek(3))?) Gas::from_u256(*stack.peek(2))?,
}, )
instructions::LOG0 | instructions::LOG1 | instructions::LOG2 | instructions::LOG3 | instructions::LOG4 => { }
let no_of_topics = instruction.log_topics().expect("log_topics always return some for LOG* instructions; qed"); instructions::EXTCODECOPY => Request::GasMemCopy(
schedule.extcodecopy_base_gas.into(),
mem_needed(stack.peek(1), stack.peek(3))?,
Gas::from_u256(*stack.peek(3))?,
),
instructions::LOG0
| instructions::LOG1
| instructions::LOG2
| instructions::LOG3
| instructions::LOG4 => {
let no_of_topics = instruction
.log_topics()
.expect("log_topics always return some for LOG* instructions; qed");
let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics; let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics;
let data_gas = overflowing!(Gas::from_u256(*stack.peek(1))?.overflow_mul(Gas::from(schedule.log_data_gas))); let data_gas =
overflowing!(Gas::from_u256(*stack.peek(1))?
.overflow_mul(Gas::from(schedule.log_data_gas)));
let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas))); let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas)));
Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?) Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?)
}, }
instructions::CALL | instructions::CALLCODE => { instructions::CALL | instructions::CALLCODE => {
let mut gas = Gas::from(schedule.call_gas); let mut gas = Gas::from(schedule.call_gas);
let mem = cmp::max( let mem = cmp::max(
mem_needed(stack.peek(5), stack.peek(6))?, mem_needed(stack.peek(5), stack.peek(6))?,
mem_needed(stack.peek(3), stack.peek(4))? mem_needed(stack.peek(3), stack.peek(4))?,
); );
let address = u256_to_address(stack.peek(1)); let address = u256_to_address(stack.peek(1));
let is_value_transfer = !stack.peek(2).is_zero(); let is_value_transfer = !stack.peek(2).is_zero();
if instruction == instructions::CALL && ( if instruction == instructions::CALL
(!schedule.no_empty && !ext.exists(&address)?) && ((!schedule.no_empty && !ext.exists(&address)?)
|| || (schedule.no_empty
(schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address)?) && is_value_transfer
) { && !ext.exists_and_not_null(&address)?))
{
gas = overflowing!(gas.overflow_add(schedule.call_new_account_gas.into())); gas = overflowing!(gas.overflow_add(schedule.call_new_account_gas.into()));
} }
@ -222,17 +236,17 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
let requested = *stack.peek(0); let requested = *stack.peek(0);
Request::GasMemProvide(gas, mem, Some(requested)) Request::GasMemProvide(gas, mem, Some(requested))
}, }
instructions::DELEGATECALL | instructions::STATICCALL => { instructions::DELEGATECALL | instructions::STATICCALL => {
let gas = Gas::from(schedule.call_gas); let gas = Gas::from(schedule.call_gas);
let mem = cmp::max( let mem = cmp::max(
mem_needed(stack.peek(4), stack.peek(5))?, mem_needed(stack.peek(4), stack.peek(5))?,
mem_needed(stack.peek(2), stack.peek(3))? mem_needed(stack.peek(2), stack.peek(3))?,
); );
let requested = *stack.peek(0); let requested = *stack.peek(0);
Request::GasMemProvide(gas, mem, Some(requested)) Request::GasMemProvide(gas, mem, Some(requested))
}, }
instructions::CREATE => { instructions::CREATE => {
let start = stack.peek(1); let start = stack.peek(1);
let len = stack.peek(2); let len = stack.peek(2);
@ -241,7 +255,7 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
let mem = mem_needed(start, len)?; let mem = mem_needed(start, len)?;
Request::GasMemProvide(gas, mem, None) Request::GasMemProvide(gas, mem, None)
}, }
instructions::CREATE2 => { instructions::CREATE2 => {
let start = stack.peek(1); let start = stack.peek(1);
let len = stack.peek(2); let len = stack.peek(2);
@ -253,30 +267,27 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
let mem = mem_needed(start, len)?; let mem = mem_needed(start, len)?;
Request::GasMemProvide(gas, mem, None) Request::GasMemProvide(gas, mem, None)
}, }
instructions::EXP => { instructions::EXP => {
let expon = stack.peek(1); let expon = stack.peek(1);
let bytes = ((expon.bits() + 7) / 8) as usize; let bytes = ((expon.bits() + 7) / 8) as usize;
let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes); let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
Request::Gas(gas) Request::Gas(gas)
}, }
instructions::BLOCKHASH => { instructions::BLOCKHASH => Request::Gas(Gas::from(schedule.blockhash_gas)),
Request::Gas(Gas::from(schedule.blockhash_gas))
},
_ => Request::Gas(default_gas), _ => Request::Gas(default_gas),
}; };
Ok(match cost { Ok(match cost {
Request::Gas(gas) => { Request::Gas(gas) => InstructionRequirements {
InstructionRequirements {
gas_cost: gas, gas_cost: gas,
provide_gas: None, provide_gas: None,
memory_required_size: 0, memory_required_size: 0,
memory_total_gas: self.current_mem_gas, memory_total_gas: self.current_mem_gas,
}
}, },
Request::GasMem(gas, mem_size) => { Request::GasMem(gas, mem_size) => {
let (mem_gas_cost, new_mem_gas, new_mem_size) = self.mem_gas_cost(schedule, current_mem_size, &mem_size)?; let (mem_gas_cost, new_mem_gas, new_mem_size) =
self.mem_gas_cost(schedule, current_mem_size, &mem_size)?;
let gas = overflowing!(gas.overflow_add(mem_gas_cost)); let gas = overflowing!(gas.overflow_add(mem_gas_cost));
InstructionRequirements { InstructionRequirements {
gas_cost: gas, gas_cost: gas,
@ -284,9 +295,10 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
memory_required_size: new_mem_size, memory_required_size: new_mem_size,
memory_total_gas: new_mem_gas, memory_total_gas: new_mem_gas,
} }
}, }
Request::GasMemProvide(gas, mem_size, requested) => { Request::GasMemProvide(gas, mem_size, requested) => {
let (mem_gas_cost, new_mem_gas, new_mem_size) = self.mem_gas_cost(schedule, current_mem_size, &mem_size)?; let (mem_gas_cost, new_mem_gas, new_mem_size) =
self.mem_gas_cost(schedule, current_mem_size, &mem_size)?;
let gas = overflowing!(gas.overflow_add(mem_gas_cost)); let gas = overflowing!(gas.overflow_add(mem_gas_cost));
let provided = self.gas_provided(schedule, gas, requested)?; let provided = self.gas_provided(schedule, gas, requested)?;
let total_gas = overflowing!(gas.overflow_add(provided)); let total_gas = overflowing!(gas.overflow_add(provided));
@ -297,9 +309,10 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
memory_required_size: new_mem_size, memory_required_size: new_mem_size,
memory_total_gas: new_mem_gas, memory_total_gas: new_mem_gas,
} }
}, }
Request::GasMemCopy(gas, mem_size, copy) => { Request::GasMemCopy(gas, mem_size, copy) => {
let (mem_gas_cost, new_mem_gas, new_mem_size) = self.mem_gas_cost(schedule, current_mem_size, &mem_size)?; let (mem_gas_cost, new_mem_gas, new_mem_size) =
self.mem_gas_cost(schedule, current_mem_size, &mem_size)?;
let copy = overflowing!(to_word_size(copy)); let copy = overflowing!(to_word_size(copy));
let copy_gas = overflowing!(Gas::from(schedule.copy_gas).overflow_mul(copy)); let copy_gas = overflowing!(Gas::from(schedule.copy_gas).overflow_mul(copy));
let gas = overflowing!(gas.overflow_add(copy_gas)); let gas = overflowing!(gas.overflow_add(copy_gas));
@ -311,11 +324,16 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
memory_required_size: new_mem_size, memory_required_size: new_mem_size,
memory_total_gas: new_mem_gas, memory_total_gas: new_mem_gas,
} }
}, }
}) })
} }
fn mem_gas_cost(&self, schedule: &Schedule, current_mem_size: usize, mem_size: &Gas) -> vm::Result<(Gas, Gas, usize)> { fn mem_gas_cost(
&self,
schedule: &Schedule,
current_mem_size: usize,
mem_size: &Gas,
) -> vm::Result<(Gas, Gas, usize)> {
let gas_for_mem = |mem_size: Gas| { let gas_for_mem = |mem_size: Gas| {
let s = mem_size >> 5; let s = mem_size >> 5;
// s * memory_gas + s * s / quad_coeff_div // s * memory_gas + s * s / quad_coeff_div
@ -371,9 +389,13 @@ fn to_word_size<Gas: evm::CostType>(value: Gas) -> (Gas, bool) {
} }
#[inline] #[inline]
fn calculate_eip1283_sstore_gas<Gas: evm::CostType>(schedule: &Schedule, original: &U256, current: &U256, new: &U256) -> Gas { fn calculate_eip1283_sstore_gas<Gas: evm::CostType>(
Gas::from( schedule: &Schedule,
if current == new { original: &U256,
current: &U256,
new: &U256,
) -> Gas {
Gas::from(if current == new {
// 1. If current value equals new value (this is a no-op), 200 gas is deducted. // 1. If current value equals new value (this is a no-op), 200 gas is deducted.
schedule.sload_gas schedule.sload_gas
} else { } else {
@ -401,11 +423,15 @@ fn calculate_eip1283_sstore_gas<Gas: evm::CostType>(schedule: &Schedule, origina
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter. // 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
// 2.2.2.2. Otherwise, add 4800 gas to refund counter. // 2.2.2.2. Otherwise, add 4800 gas to refund counter.
} }
} })
)
} }
pub fn handle_eip1283_sstore_clears_refund(ext: &mut vm::Ext, original: &U256, current: &U256, new: &U256) { pub fn handle_eip1283_sstore_clears_refund(
ext: &mut vm::Ext,
original: &U256,
current: &U256,
new: &U256,
) {
let sstore_clears_schedule = ext.schedule().sstore_refund_gas; let sstore_clears_schedule = ext.schedule().sstore_refund_gas;
if current == new { if current == new {
@ -479,7 +505,9 @@ fn test_calculate_mem_cost() {
let mem_size = 5; let mem_size = 5;
// when // when
let (mem_cost, new_mem_gas, mem_size) = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size).unwrap(); let (mem_cost, new_mem_gas, mem_size) = gasometer
.mem_gas_cost(&schedule, current_mem_size, &mem_size)
.unwrap();
// then // then
assert_eq!(mem_cost, 3); assert_eq!(mem_cost, 3);

View File

@ -20,7 +20,7 @@ pub use self::inner::*;
#[cfg(not(feature = "evm-debug"))] #[cfg(not(feature = "evm-debug"))]
mod inner { mod inner {
macro_rules! evm_debug { macro_rules! evm_debug {
($x: expr) => {} ($x: expr) => {};
} }
pub struct EvmInformant; pub struct EvmInformant;
@ -35,20 +35,22 @@ mod inner {
#[macro_use] #[macro_use]
#[cfg(feature = "evm-debug")] #[cfg(feature = "evm-debug")]
mod inner { mod inner {
use std::iter; use std::{
use std::collections::HashMap; collections::HashMap,
use std::time::{Instant, Duration}; iter,
time::{Duration, Instant},
};
use ethereum_types::U256; use ethereum_types::U256;
use interpreter::stack::Stack;
use instructions::{Instruction, InstructionInfo}; use instructions::{Instruction, InstructionInfo};
use interpreter::stack::Stack;
use CostType; use CostType;
macro_rules! evm_debug { macro_rules! evm_debug {
($x: expr) => { ($x: expr) => {
$x $x
} };
} }
fn print(data: String) { fn print(data: String) {
@ -66,7 +68,6 @@ mod inner {
} }
impl EvmInformant { impl EvmInformant {
fn color(instruction: Instruction, name: &str) -> String { fn color(instruction: Instruction, name: &str) -> String {
let c = instruction as usize % 6; let c = instruction as usize % 6;
let colors = [31, 34, 33, 32, 35, 36]; let colors = [31, 34, 33, 32, 35, 36];
@ -89,11 +90,19 @@ mod inner {
} }
} }
pub fn before_instruction<Cost: CostType>(&mut self, pc: usize, instruction: Instruction, info: &InstructionInfo, current_gas: &Cost, stack: &Stack<U256>) { pub fn before_instruction<Cost: CostType>(
&mut self,
pc: usize,
instruction: Instruction,
info: &InstructionInfo,
current_gas: &Cost,
stack: &Stack<U256>,
) {
let time = self.last_instruction.elapsed(); let time = self.last_instruction.elapsed();
self.last_instruction = Instant::now(); self.last_instruction = Instant::now();
print(format!("{}[0x{:<3x}][{:>19}(0x{:<2x}) Gas Left: {:6?} (Previous took: {:10}μs)", print(format!(
"{}[0x{:<3x}][{:>19}(0x{:<2x}) Gas Left: {:6?} (Previous took: {:10}μs)",
&self.spacing, &self.spacing,
pc, pc,
Self::color(instruction, info.name), Self::color(instruction, info.name),
@ -110,20 +119,24 @@ mod inner {
} }
pub fn after_instruction(&mut self, instruction: Instruction) { pub fn after_instruction(&mut self, instruction: Instruction) {
let stats = self.stats.entry(instruction).or_insert_with(|| Stats::default()); let stats = self
.stats
.entry(instruction)
.or_insert_with(|| Stats::default());
let took = self.last_instruction.elapsed(); let took = self.last_instruction.elapsed();
stats.note(took); stats.note(took);
} }
pub fn done(&mut self) { pub fn done(&mut self) {
// Print out stats // Print out stats
let mut stats: Vec<(_,_)> = self.stats.drain().collect(); let mut stats: Vec<(_, _)> = self.stats.drain().collect();
stats.sort_by(|ref a, ref b| b.1.avg().cmp(&a.1.avg())); stats.sort_by(|ref a, ref b| b.1.avg().cmp(&a.1.avg()));
print(format!("\n{}-------OPCODE STATS:", self.spacing)); print(format!("\n{}-------OPCODE STATS:", self.spacing));
for (instruction, stats) in stats.into_iter() { for (instruction, stats) in stats.into_iter() {
let info = instruction.info(); let info = instruction.info();
print(format!("{}-------{:>19}(0x{:<2x}) count: {:4}, avg: {:10}μs", print(format!(
"{}-------{:>19}(0x{:<2x}) count: {:4}, avg: {:10}μs",
self.spacing, self.spacing,
Self::color(instruction, info.name), Self::color(instruction, info.name),
instruction as u8, instruction as u8,
@ -132,7 +145,6 @@ mod inner {
)); ));
} }
} }
} }
struct Stats { struct Stats {

View File

@ -37,7 +37,7 @@ pub trait Memory {
/// Retrieve part of the memory between offset and offset + size /// Retrieve part of the memory between offset and offset + size
fn read_slice(&self, offset: U256, size: U256) -> &[u8]; fn read_slice(&self, offset: U256, size: U256) -> &[u8];
/// Retrieve writeable part of memory /// Retrieve writeable part of memory
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8]; fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8];
/// Convert memory into return data. /// Convert memory into return data.
fn into_return_data(self, offset: U256, size: U256) -> ReturnData; fn into_return_data(self, offset: U256, size: U256) -> ReturnData;
} }
@ -60,13 +60,13 @@ impl Memory for Vec<u8> {
if !is_valid_range(off, size) { if !is_valid_range(off, size) {
&self[0..0] &self[0..0]
} else { } else {
&self[off..off+size] &self[off..off + size]
} }
} }
fn read(&self, offset: U256) -> U256 { fn read(&self, offset: U256) -> U256 {
let off = offset.low_u64() as usize; let off = offset.low_u64() as usize;
U256::from(&self[off..off+32]) U256::from(&self[off..off + 32])
} }
fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8] { fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8] {
@ -75,20 +75,20 @@ impl Memory for Vec<u8> {
if !is_valid_range(off, s) { if !is_valid_range(off, s) {
&mut self[0..0] &mut self[0..0]
} else { } else {
&mut self[off..off+s] &mut self[off..off + s]
} }
} }
fn write_slice(&mut self, offset: U256, slice: &[u8]) { fn write_slice(&mut self, offset: U256, slice: &[u8]) {
if !slice.is_empty() { if !slice.is_empty() {
let off = offset.low_u64() as usize; let off = offset.low_u64() as usize;
self[off..off+slice.len()].copy_from_slice(slice); self[off..off + slice.len()].copy_from_slice(slice);
} }
} }
fn write(&mut self, offset: U256, value: U256) { fn write(&mut self, offset: U256, value: U256) {
let off = offset.low_u64() as usize; let off = offset.low_u64() as usize;
value.to_big_endian(&mut self[off..off+32]); value.to_big_endian(&mut self[off..off + 32]);
} }
fn write_byte(&mut self, offset: U256, value: U256) { fn write_byte(&mut self, offset: U256, value: U256) {
@ -130,8 +130,8 @@ impl Memory for Vec<u8> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ethereum_types::U256;
use super::Memory; use super::Memory;
use ethereum_types::U256;
#[test] #[test]
fn test_memory_read_and_write() { fn test_memory_read_and_write() {
@ -178,7 +178,10 @@ mod tests {
let slice = "67890".as_bytes(); let slice = "67890".as_bytes();
mem.write_slice(U256::from(0x1), slice); mem.write_slice(U256::from(0x1), slice);
assert_eq!(mem.read_slice(U256::from(0), U256::from(7)), "a67890g".as_bytes()); assert_eq!(
mem.read_slice(U256::from(0), U256::from(7)),
"a67890g".as_bytes()
);
} }
// write empty slice out of bounds // write empty slice out of bounds

File diff suppressed because it is too large Load Diff

View File

@ -14,14 +14,14 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc; use super::super::instructions::{self, Instruction};
use bit_set::BitSet;
use ethereum_types::H256;
use hash::KECCAK_EMPTY; use hash::KECCAK_EMPTY;
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
use ethereum_types::H256;
use parking_lot::Mutex;
use memory_cache::MemoryLruCache; use memory_cache::MemoryLruCache;
use bit_set::BitSet; use parking_lot::Mutex;
use super::super::instructions::{self, Instruction}; use std::sync::Arc;
const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024; const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024;
@ -64,7 +64,9 @@ impl SharedCache {
let d = Self::find_jump_destinations(code); let d = Self::find_jump_destinations(code);
if let Some(ref code_hash) = code_hash { if let Some(ref code_hash) = code_hash {
self.jump_destinations.lock().insert(*code_hash, Bits(d.clone())); self.jump_destinations
.lock()
.insert(*code_hash, Bits(d.clone()));
} }
d d

View File

@ -14,8 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::fmt;
use instructions; use instructions;
use std::fmt;
/// Stack trait with VM-friendly API /// Stack trait with VM-friendly API
pub trait Stack<T> { pub trait Stack<T> {
@ -39,19 +39,19 @@ pub trait Stack<T> {
pub struct VecStack<S> { pub struct VecStack<S> {
stack: Vec<S>, stack: Vec<S>,
logs: [S; instructions::MAX_NO_OF_TOPICS] logs: [S; instructions::MAX_NO_OF_TOPICS],
} }
impl<S : Copy> VecStack<S> { impl<S: Copy> VecStack<S> {
pub fn with_capacity(capacity: usize, zero: S) -> Self { pub fn with_capacity(capacity: usize, zero: S) -> Self {
VecStack { VecStack {
stack: Vec::with_capacity(capacity), stack: Vec::with_capacity(capacity),
logs: [zero; instructions::MAX_NO_OF_TOPICS] logs: [zero; instructions::MAX_NO_OF_TOPICS],
} }
} }
} }
impl<S : fmt::Display> Stack<S> for VecStack<S> { impl<S: fmt::Display> Stack<S> for VecStack<S> {
fn peek(&self, no_from_top: usize) -> &S { fn peek(&self, no_from_top: usize) -> &S {
&self.stack[self.stack.len() - no_from_top - 1] &self.stack[self.stack.len() - no_from_top - 1]
} }
@ -66,7 +66,9 @@ impl<S : fmt::Display> Stack<S> for VecStack<S> {
} }
fn pop_back(&mut self) -> S { fn pop_back(&mut self) -> S {
self.stack.pop().expect("instruction validation prevents from popping too many items; qed") self.stack
.pop()
.expect("instruction validation prevents from popping too many items; qed")
} }
fn pop_n(&mut self, no_of_elems: usize) -> &[S] { fn pop_n(&mut self, no_of_elems: usize) -> &[S] {
@ -87,7 +89,10 @@ impl<S : fmt::Display> Stack<S> for VecStack<S> {
} }
fn peek_top(&self, no_from_top: usize) -> &[S] { fn peek_top(&self, no_from_top: usize) -> &[S] {
assert!(self.stack.len() >= no_from_top, "peek_top asked for more items than exist."); assert!(
&self.stack[self.stack.len() - no_from_top .. self.stack.len()] self.stack.len() >= no_from_top,
"peek_top asked for more items than exist."
);
&self.stack[self.stack.len() - no_from_top..self.stack.len()]
} }
} }

View File

@ -18,13 +18,13 @@
extern crate bit_set; extern crate bit_set;
extern crate ethereum_types; extern crate ethereum_types;
extern crate parking_lot;
extern crate heapsize; extern crate heapsize;
extern crate vm;
extern crate keccak_hash as hash; extern crate keccak_hash as hash;
extern crate memory_cache; extern crate memory_cache;
extern crate parity_bytes as bytes;
extern crate num_bigint; extern crate num_bigint;
extern crate parity_bytes as bytes;
extern crate parking_lot;
extern crate vm;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
@ -32,28 +32,29 @@ extern crate lazy_static;
#[cfg_attr(feature = "evm-debug", macro_use)] #[cfg_attr(feature = "evm-debug", macro_use)]
extern crate log; extern crate log;
#[cfg(test)]
extern crate rustc_hex;
#[cfg(test)] #[cfg(test)]
extern crate hex_literal; extern crate hex_literal;
#[cfg(test)]
extern crate rustc_hex;
pub mod evm; pub mod evm;
pub mod interpreter; pub mod interpreter;
#[macro_use] #[macro_use]
pub mod factory; pub mod factory;
mod vmtype;
mod instructions; mod instructions;
mod vmtype;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
pub use vm::{ pub use self::{
Schedule, CleanDustMode, EnvInfo, CallType, ActionParams, Ext, evm::{CostType, FinalizationResult, Finalize},
ContractCreateResult, MessageCallResult, CreateContractAddress, factory::Factory,
GasLeft, ReturnData instructions::{Instruction, InstructionInfo},
vmtype::VMType,
};
pub use vm::{
ActionParams, CallType, CleanDustMode, ContractCreateResult, CreateContractAddress, EnvInfo,
Ext, GasLeft, MessageCallResult, ReturnData, Schedule,
}; };
pub use self::evm::{Finalize, FinalizationResult, CostType};
pub use self::instructions::{InstructionInfo, Instruction};
pub use self::vmtype::VMType;
pub use self::factory::Factory;

File diff suppressed because it is too large Load Diff

View File

@ -20,14 +20,18 @@ use std::fmt;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum VMType { pub enum VMType {
/// RUST EVM /// RUST EVM
Interpreter Interpreter,
} }
impl fmt::Display for VMType { impl fmt::Display for VMType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", match *self { write!(
VMType::Interpreter => "INT" f,
}) "{}",
match *self {
VMType::Interpreter => "INT",
}
)
} }
} }

View File

@ -20,11 +20,9 @@
//! Furthermore, stores a "gas price corpus" of relative recency, which is a sorted //! Furthermore, stores a "gas price corpus" of relative recency, which is a sorted
//! vector of all gas prices from a recent range of blocks. //! vector of all gas prices from a recent range of blocks.
use std::time::{Instant, Duration}; use std::time::{Duration, Instant};
use common_types::encoded; use common_types::{encoded, receipt::Receipt, BlockNumber};
use common_types::BlockNumber;
use common_types::receipt::Receipt;
use ethereum_types::{H256, U256}; use ethereum_types::{H256, U256};
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
use memory_cache::MemoryLruCache; use memory_cache::MemoryLruCache;

View File

@ -23,21 +23,23 @@
//! root has. A correct proof implies that the claimed block is identical to the one //! root has. A correct proof implies that the claimed block is identical to the one
//! we discarded. //! we discarded.
use bytes::Bytes;
use common_types::ids::BlockId; use common_types::ids::BlockId;
use ethereum_types::{H256, U256}; use ethereum_types::{H256, U256};
use ethtrie::{self, TrieDB, TrieDBMut};
use hash_db::HashDB; use hash_db::HashDB;
use journaldb::new_memory_db;
use keccak_hasher::KeccakHasher; use keccak_hasher::KeccakHasher;
use kvdb::DBValue; use kvdb::DBValue;
use memory_db::MemoryDB; use memory_db::MemoryDB;
use journaldb::new_memory_db; use rlp::{Rlp, RlpStream};
use bytes::Bytes; use trie::{Recorder, Trie, TrieMut};
use trie::{TrieMut, Trie, Recorder};
use ethtrie::{self, TrieDB, TrieDBMut};
use rlp::{RlpStream, Rlp};
// encode a key. // encode a key.
macro_rules! key { macro_rules! key {
($num: expr) => { ::rlp::encode(&$num) } ($num: expr) => {
::rlp::encode(&$num)
};
} }
macro_rules! val { macro_rules! val {
@ -45,7 +47,7 @@ macro_rules! val {
let mut stream = RlpStream::new_list(2); let mut stream = RlpStream::new_list(2);
stream.append(&$hash).append(&$td); stream.append(&$hash).append(&$td);
stream.drain() stream.drain()
}} }};
} }
/// The size of each CHT. /// The size of each CHT.
@ -62,19 +64,25 @@ pub struct CHT<DB: HashDB<KeccakHasher, DBValue>> {
impl<DB: HashDB<KeccakHasher, DBValue>> CHT<DB> { impl<DB: HashDB<KeccakHasher, DBValue>> CHT<DB> {
/// Query the root of the CHT. /// Query the root of the CHT.
pub fn root(&self) -> H256 { self.root } pub fn root(&self) -> H256 {
self.root
}
/// Query the number of the CHT. /// Query the number of the CHT.
pub fn number(&self) -> u64 { self.number } pub fn number(&self) -> u64 {
self.number
}
/// Generate an inclusion proof for the entry at a specific block. /// Generate an inclusion proof for the entry at a specific block.
/// Nodes before level `from_level` will be omitted. /// Nodes before level `from_level` will be omitted.
/// Returns an error on an incomplete trie, and `Ok(None)` on an unprovable request. /// Returns an error on an incomplete trie, and `Ok(None)` on an unprovable request.
pub fn prove(&self, num: u64, from_level: u32) -> ethtrie::Result<Option<Vec<Bytes>>> { pub fn prove(&self, num: u64, from_level: u32) -> ethtrie::Result<Option<Vec<Bytes>>> {
if block_to_cht_number(num) != Some(self.number) { return Ok(None) } if block_to_cht_number(num) != Some(self.number) {
return Ok(None);
}
let mut recorder = Recorder::with_depth(from_level); let mut recorder = Recorder::with_depth(from_level);
let db: &HashDB<_,_> = &self.db; let db: &HashDB<_, _> = &self.db;
let t = TrieDB::new(&db, &self.root)?; let t = TrieDB::new(&db, &self.root)?;
t.get_with(&key!(num), &mut recorder)?; t.get_with(&key!(num), &mut recorder)?;
@ -96,7 +104,8 @@ pub struct BlockInfo {
/// about blocks. If the fetcher ever fails to provide the info, the CHT /// about blocks. If the fetcher ever fails to provide the info, the CHT
/// will not be generated. /// will not be generated.
pub fn build<F>(cht_num: u64, mut fetcher: F) -> Option<CHT<MemoryDB<KeccakHasher, DBValue>>> pub fn build<F>(cht_num: u64, mut fetcher: F) -> Option<CHT<MemoryDB<KeccakHasher, DBValue>>>
where F: FnMut(BlockId) -> Option<BlockInfo> where
F: FnMut(BlockId) -> Option<BlockInfo>,
{ {
let mut db = new_memory_db(); let mut db = new_memory_db();
@ -131,7 +140,8 @@ pub fn build<F>(cht_num: u64, mut fetcher: F) -> Option<CHT<MemoryDB<KeccakHashe
/// SIZE items. The items are assumed to proceed sequentially from `start_number(cht_num)`. /// SIZE items. The items are assumed to proceed sequentially from `start_number(cht_num)`.
/// Discards the trie's nodes. /// Discards the trie's nodes.
pub fn compute_root<I>(cht_num: u64, iterable: I) -> Option<H256> pub fn compute_root<I>(cht_num: u64, iterable: I) -> Option<H256>
where I: IntoIterator<Item=(H256, U256)> where
I: IntoIterator<Item = (H256, U256)>,
{ {
let mut v = Vec::with_capacity(SIZE as usize); let mut v = Vec::with_capacity(SIZE as usize);
let start_num = start_number(cht_num) as usize; let start_num = start_number(cht_num) as usize;
@ -154,7 +164,9 @@ pub fn compute_root<I>(cht_num: u64, iterable: I) -> Option<H256>
pub fn check_proof(proof: &[Bytes], num: u64, root: H256) -> Option<(H256, U256)> { pub fn check_proof(proof: &[Bytes], num: u64, root: H256) -> Option<(H256, U256)> {
let mut db = new_memory_db(); let mut db = new_memory_db();
for node in proof { db.insert(&node[..]); } for node in proof {
db.insert(&node[..]);
}
let res = match TrieDB::new(&db, &root) { let res = match TrieDB::new(&db, &root) {
Err(_) => return None, Err(_) => return None,
Ok(trie) => trie.get_with(&key!(num), |val: &[u8]| { Ok(trie) => trie.get_with(&key!(num), |val: &[u8]| {
@ -162,7 +174,7 @@ pub fn check_proof(proof: &[Bytes], num: u64, root: H256) -> Option<(H256, U256)
rlp.val_at::<H256>(0) rlp.val_at::<H256>(0)
.and_then(|h| rlp.val_at::<U256>(1).map(|td| (h, td))) .and_then(|h| rlp.val_at::<U256>(1).map(|td| (h, td)))
.ok() .ok()
}) }),
}; };
match res { match res {

View File

@ -18,11 +18,11 @@
use std::sync::Arc; use std::sync::Arc;
use common_types::encoded; use common_types::{encoded, header::Header, receipt::Receipt};
use common_types::header::Header; use ethcore::{
use common_types::receipt::Receipt; engines::{EthEngine, StateDependentProof},
use ethcore::engines::{EthEngine, StateDependentProof}; machine::EthereumMachine,
use ethcore::machine::EthereumMachine; };
use ethereum_types::H256; use ethereum_types::H256;
use futures::future::IntoFuture; use futures::future::IntoFuture;
@ -32,11 +32,11 @@ pub trait ChainDataFetcher: Send + Sync + 'static {
type Error: ::std::fmt::Debug; type Error: ::std::fmt::Debug;
/// Future for fetching block body. /// Future for fetching block body.
type Body: IntoFuture<Item=encoded::Block, Error=Self::Error>; type Body: IntoFuture<Item = encoded::Block, Error = Self::Error>;
/// Future for fetching block receipts. /// Future for fetching block receipts.
type Receipts: IntoFuture<Item=Vec<Receipt>, Error=Self::Error>; type Receipts: IntoFuture<Item = Vec<Receipt>, Error = Self::Error>;
/// Future for fetching epoch transition /// Future for fetching epoch transition
type Transition: IntoFuture<Item=Vec<u8>, Error=Self::Error>; type Transition: IntoFuture<Item = Vec<u8>, Error = Self::Error>;
/// Fetch a block body. /// Fetch a block body.
fn block_body(&self, header: &Header) -> Self::Body; fn block_body(&self, header: &Header) -> Self::Body;
@ -49,7 +49,7 @@ pub trait ChainDataFetcher: Send + Sync + 'static {
&self, &self,
_hash: H256, _hash: H256,
_engine: Arc<EthEngine>, _engine: Arc<EthEngine>,
_checker: Arc<StateDependentProof<EthereumMachine>> _checker: Arc<StateDependentProof<EthereumMachine>>,
) -> Self::Transition; ) -> Self::Transition;
} }
@ -57,7 +57,9 @@ pub trait ChainDataFetcher: Send + Sync + 'static {
pub struct Unavailable; pub struct Unavailable;
/// Create a fetcher which has all data unavailable. /// Create a fetcher which has all data unavailable.
pub fn unavailable() -> Unavailable { Unavailable } pub fn unavailable() -> Unavailable {
Unavailable
}
impl ChainDataFetcher for Unavailable { impl ChainDataFetcher for Unavailable {
type Error = &'static str; type Error = &'static str;
@ -78,7 +80,7 @@ impl ChainDataFetcher for Unavailable {
&self, &self,
_hash: H256, _hash: H256,
_engine: Arc<EthEngine>, _engine: Arc<EthEngine>,
_checker: Arc<StateDependentProof<EthereumMachine>> _checker: Arc<StateDependentProof<EthereumMachine>>,
) -> Self::Transition { ) -> Self::Transition {
Err("fetching epoch transition proofs unavailable") Err("fetching epoch transition proofs unavailable")
} }

View File

@ -25,24 +25,22 @@
//! - It stores only headers (and a pruned subset of them) //! - It stores only headers (and a pruned subset of them)
//! - To allow for flexibility in the database layout.. //! - To allow for flexibility in the database layout..
use std::collections::BTreeMap; use std::{collections::BTreeMap, sync::Arc};
use std::sync::Arc;
use cache::Cache; use cache::Cache;
use cht; use cht;
use common_types::block_status::BlockStatus; use common_types::{block_status::BlockStatus, encoded, header::Header, ids::BlockId};
use common_types::encoded; use ethcore::{
use common_types::header::Header; engines::epoch::{PendingTransition as PendingEpochTransition, Transition as EpochTransition},
use common_types::ids::BlockId; error::{BlockError, Error, ErrorKind as EthcoreErrorKind, EthcoreResult},
use ethcore::engines::epoch::{Transition as EpochTransition, PendingTransition as PendingEpochTransition}; spec::{Spec, SpecHardcodedSync},
use ethcore::error::{Error, EthcoreResult, ErrorKind as EthcoreErrorKind, BlockError}; };
use ethcore::spec::{Spec, SpecHardcodedSync};
use ethereum_types::{H256, H264, U256}; use ethereum_types::{H256, H264, U256};
use fastmap::H256FastMap;
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
use kvdb::{DBTransaction, KeyValueDB}; use kvdb::{DBTransaction, KeyValueDB};
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use fastmap::H256FastMap; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream};
use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp};
use smallvec::SmallVec; use smallvec::SmallVec;
/// Store at least this many candidate headers at all times. /// Store at least this many candidate headers at all times.
@ -71,7 +69,7 @@ pub struct BlockDescriptor {
#[derive(RlpEncodable, RlpDecodable)] #[derive(RlpEncodable, RlpDecodable)]
struct BestAndLatest { struct BestAndLatest {
best_num: u64, best_num: u64,
latest_num: u64 latest_num: u64,
} }
impl BestAndLatest { impl BestAndLatest {
@ -130,7 +128,9 @@ impl Decodable for Entry {
}) })
} }
if candidates.is_empty() { return Err(DecoderError::Custom("Empty candidates vector submitted.")) } if candidates.is_empty() {
return Err(DecoderError::Custom("Empty candidates vector submitted."));
}
// rely on the invariant that the canonical entry is always first. // rely on the invariant that the canonical entry is always first.
let canon_hash = candidates[0].hash; let canon_hash = candidates[0].hash;
@ -222,7 +222,7 @@ impl HeaderChain {
let decoded_header = spec.genesis_header(); let decoded_header = spec.genesis_header();
let chain = if let Some(current) = db.get(col, CURRENT_KEY)? { let chain = if let Some(current) = db.get(col, CURRENT_KEY)? {
let curr : BestAndLatest = ::rlp::decode(&current).expect("decoding db value failed"); let curr: BestAndLatest = ::rlp::decode(&current).expect("decoding db value failed");
let mut cur_number = curr.latest_num; let mut cur_number = curr.latest_num;
let mut candidates = BTreeMap::new(); let mut candidates = BTreeMap::new();
@ -238,11 +238,14 @@ impl HeaderChain {
let key = transition_key(c.hash); let key = transition_key(c.hash);
if let Some(proof) = db.get(col, &*key)? { if let Some(proof) = db.get(col, &*key)? {
live_epoch_proofs.insert(c.hash, EpochTransition { live_epoch_proofs.insert(
c.hash,
EpochTransition {
block_hash: c.hash, block_hash: c.hash,
block_number: cur_number, block_number: cur_number,
proof: proof.into_vec(), proof: proof.into_vec(),
}); },
);
} }
} }
candidates.insert(cur_number, entry); candidates.insert(cur_number, entry);
@ -274,7 +277,6 @@ impl HeaderChain {
col, col,
cache, cache,
} }
} else { } else {
let chain = HeaderChain { let chain = HeaderChain {
genesis_header: encoded::Header::new(genesis), genesis_header: encoded::Header::new(genesis),
@ -291,12 +293,18 @@ impl HeaderChain {
}; };
// insert the hardcoded sync into the database. // insert the hardcoded sync into the database.
if let (&Some(ref hardcoded_sync), HardcodedSync::Allow) = (&spec.hardcoded_sync, allow_hs) { if let (&Some(ref hardcoded_sync), HardcodedSync::Allow) =
(&spec.hardcoded_sync, allow_hs)
{
let mut batch = db.transaction(); let mut batch = db.transaction();
// insert the hardcoded CHT roots into the database. // insert the hardcoded CHT roots into the database.
for (cht_num, cht_root) in hardcoded_sync.chts.iter().enumerate() { for (cht_num, cht_root) in hardcoded_sync.chts.iter().enumerate() {
batch.put(col, cht_key(cht_num as u64).as_bytes(), &::rlp::encode(cht_root)); batch.put(
col,
cht_key(cht_num as u64).as_bytes(),
&::rlp::encode(cht_root),
);
} }
let decoded_header = hardcoded_sync.header.decode()?; let decoded_header = hardcoded_sync.header.decode()?;
@ -304,8 +312,12 @@ impl HeaderChain {
// write the block in the DB. // write the block in the DB.
info!(target: "chain", "Inserting hardcoded block #{} in chain", decoded_header_num); info!(target: "chain", "Inserting hardcoded block #{} in chain", decoded_header_num);
let pending = chain.insert_with_td(&mut batch, &decoded_header, let pending = chain.insert_with_td(
hardcoded_sync.total_difficulty, None)?; &mut batch,
&decoded_header,
hardcoded_sync.total_difficulty,
None,
)?;
// check that we have enough hardcoded CHT roots. avoids panicking later. // check that we have enough hardcoded CHT roots. avoids panicking later.
let cht_num = cht::block_to_cht_number(decoded_header_num - 1) let cht_num = cht::block_to_cht_number(decoded_header_num - 1)
@ -365,7 +377,12 @@ impl HeaderChain {
total_difficulty: U256, total_difficulty: U256,
transition_proof: Option<Vec<u8>>, transition_proof: Option<Vec<u8>>,
) -> EthcoreResult<PendingChanges> { ) -> EthcoreResult<PendingChanges> {
self.insert_inner(transaction, header, Some(total_difficulty), transition_proof) self.insert_inner(
transaction,
header,
Some(total_difficulty),
transition_proof,
)
} }
fn insert_inner( fn insert_inner(
@ -384,9 +401,7 @@ impl HeaderChain {
proof, proof,
}); });
let mut pending = PendingChanges { let mut pending = PendingChanges { best_block: None };
best_block: None,
};
// hold candidates the whole time to guard import order. // hold candidates the whole time to guard import order.
let mut candidates = self.candidates.write(); let mut candidates = self.candidates.write();
@ -395,11 +410,11 @@ impl HeaderChain {
let total_difficulty = match total_difficulty { let total_difficulty = match total_difficulty {
Some(td) => td, Some(td) => td,
None => { None => {
let parent_td = let parent_td = if number == 1 {
if number == 1 {
self.genesis_header.difficulty() self.genesis_header.difficulty()
} else { } else {
candidates.get(&(number - 1)) candidates
.get(&(number - 1))
.and_then(|entry| entry.candidates.iter().find(|c| c.hash == parent_hash)) .and_then(|entry| entry.candidates.iter().find(|c| c.hash == parent_hash))
.map(|c| c.total_difficulty) .map(|c| c.total_difficulty)
.ok_or_else(|| BlockError::UnknownParent(parent_hash)) .ok_or_else(|| BlockError::UnknownParent(parent_hash))
@ -407,13 +422,15 @@ impl HeaderChain {
}; };
parent_td + *header.difficulty() parent_td + *header.difficulty()
}, }
}; };
// insert headers and candidates entries and write era to disk. // insert headers and candidates entries and write era to disk.
{ {
let cur_era = candidates.entry(number) let cur_era = candidates.entry(number).or_insert_with(|| Entry {
.or_insert_with(|| Entry { candidates: SmallVec::new(), canonical_hash: hash }); candidates: SmallVec::new(),
canonical_hash: hash,
});
cur_era.candidates.push(Candidate { cur_era.candidates.push(Candidate {
hash, hash,
parent_hash, parent_hash,
@ -427,7 +444,11 @@ impl HeaderChain {
cur_era.canonical_hash = hash; cur_era.canonical_hash = hash;
} }
transaction.put(self.col, era_key(number).as_bytes(), &::rlp::encode(&*cur_era)) transaction.put(
self.col,
era_key(number).as_bytes(),
&::rlp::encode(&*cur_era),
)
} }
if let Some(transition) = transition { if let Some(transition) = transition {
@ -452,8 +473,14 @@ impl HeaderChain {
// respective candidates vectors. // respective candidates vectors.
if is_new_best { if is_new_best {
let mut canon_hash = hash; let mut canon_hash = hash;
for (&height, entry) in candidates.iter_mut().rev().skip_while(|&(height, _)| *height > number) { for (&height, entry) in candidates
if height != number && entry.canonical_hash == canon_hash { break; } .iter_mut()
.rev()
.skip_while(|&(height, _)| *height > number)
{
if height != number && entry.canonical_hash == canon_hash {
break;
}
trace!(target: "chain", "Setting new canonical block {} for block height {}", trace!(target: "chain", "Setting new canonical block {} for block height {}",
canon_hash, height); canon_hash, height);
@ -485,7 +512,10 @@ impl HeaderChain {
}); });
// produce next CHT root if it's time. // produce next CHT root if it's time.
let earliest_era = *candidates.keys().next().expect("at least one era just created; qed"); let earliest_era = *candidates
.keys()
.next()
.expect("at least one era just created; qed");
if earliest_era + HISTORY + cht::SIZE <= number { if earliest_era + HISTORY + cht::SIZE <= number {
let cht_num = cht::block_to_cht_number(earliest_era) let cht_num = cht::block_to_cht_number(earliest_era)
.expect("fails only for number == 0; genesis never imported; qed"); .expect("fails only for number == 0; genesis never imported; qed");
@ -498,7 +528,8 @@ impl HeaderChain {
// iterable function which removes the candidates as it goes // iterable function which removes the candidates as it goes
// along. this will only be called until the CHT is complete. // along. this will only be called until the CHT is complete.
let iter = || { let iter = || {
let era_entry = candidates.remove(&i) let era_entry = candidates
.remove(&i)
.expect("all eras are sequential with no gaps; qed"); .expect("all eras are sequential with no gaps; qed");
transaction.delete(self.col, era_key(i).as_bytes()); transaction.delete(self.col, era_key(i).as_bytes());
@ -538,7 +569,11 @@ impl HeaderChain {
// write the CHT root to the database. // write the CHT root to the database.
debug!(target: "chain", "Produced CHT {} root: {:?}", cht_num, cht_root); debug!(target: "chain", "Produced CHT {} root: {:?}", cht_num, cht_root);
transaction.put(self.col, cht_key(cht_num).as_bytes(), &::rlp::encode(&cht_root)); transaction.put(
self.col,
cht_key(cht_num).as_bytes(),
&::rlp::encode(&cht_root),
);
// update the last canonical transition proof // update the last canonical transition proof
if let Some((epoch_transition, header)) = last_canonical_transition { if let Some((epoch_transition, header)) = last_canonical_transition {
@ -550,7 +585,12 @@ impl HeaderChain {
// write the best and latest eras to the database. // write the best and latest eras to the database.
{ {
let latest_num = *candidates.iter().rev().next().expect("at least one era just inserted; qed").0; let latest_num = *candidates
.iter()
.rev()
.next()
.expect("at least one era just inserted; qed")
.0;
let curr = BestAndLatest::new(best_num, latest_num); let curr = BestAndLatest::new(best_num, latest_num);
transaction.put(self.col, CURRENT_KEY, &::rlp::encode(&curr)) transaction.put(self.col, CURRENT_KEY, &::rlp::encode(&curr))
} }
@ -574,23 +614,33 @@ impl HeaderChain {
let header = if let Some(header) = self.block_header(BlockId::Number(h_num)) { let header = if let Some(header) = self.block_header(BlockId::Number(h_num)) {
header header
} else { } else {
let msg = format!("header of block #{} not found in DB ; database in an \ let msg = format!(
inconsistent state", h_num); "header of block #{} not found in DB ; database in an \
inconsistent state",
h_num
);
bail!(msg); bail!(msg);
}; };
let decoded = header.decode().expect("decoding db value failed"); let decoded = header.decode().expect("decoding db value failed");
let entry: Entry = { let entry: Entry = {
let bytes = self.db.get(self.col, era_key(h_num).as_bytes())? let bytes = self
.db
.get(self.col, era_key(h_num).as_bytes())?
.ok_or_else(|| { .ok_or_else(|| {
format!("entry for era #{} not found in DB ; database \ format!(
in an inconsistent state", h_num) "entry for era #{} not found in DB ; database \
in an inconsistent state",
h_num
)
})?; })?;
::rlp::decode(&bytes).expect("decoding db value failed") ::rlp::decode(&bytes).expect("decoding db value failed")
}; };
let total_difficulty = entry.candidates.iter() let total_difficulty = entry
.candidates
.iter()
.find(|c| c.hash == decoded.hash()) .find(|c| c.hash == decoded.hash())
.ok_or_else(|| { .ok_or_else(|| {
"no candidate matching block found in DB ; database in an \ "no candidate matching block found in DB ; database in an \
@ -603,10 +653,10 @@ impl HeaderChain {
total_difficulty, total_difficulty,
chts, chts,
})); }));
}, }
None => { None => {
break Ok(None); break Ok(None);
}, }
}; };
chts.push(cht); chts.push(cht);
@ -629,12 +679,15 @@ impl HeaderChain {
BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_hash()), BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_hash()),
BlockId::Hash(hash) => Some(hash), BlockId::Hash(hash) => Some(hash),
BlockId::Number(num) => { BlockId::Number(num) => {
if self.best_block.read().number < num { return None } if self.best_block.read().number < num {
self.candidates.read().get(&num).map(|entry| entry.canonical_hash) return None;
} }
BlockId::Latest => { self.candidates
Some(self.best_block.read().hash) .read()
.get(&num)
.map(|entry| entry.canonical_hash)
} }
BlockId::Latest => Some(self.best_block.read().hash),
} }
} }
@ -646,32 +699,35 @@ impl HeaderChain {
match cache.block_header(&hash) { match cache.block_header(&hash) {
Some(header) => Some(header), Some(header) => Some(header),
None => { None => match self.db.get(self.col, &hash) {
match self.db.get(self.col, &hash) { Ok(db_value) => db_value
Ok(db_value) => { .map(|x| x.into_vec())
db_value.map(|x| x.into_vec()).map(encoded::Header::new) .map(encoded::Header::new)
.and_then(|header| { .and_then(|header| {
cache.insert_block_header(hash, header.clone()); cache.insert_block_header(hash, header.clone());
Some(header) Some(header)
}) }),
},
Err(e) => { Err(e) => {
warn!(target: "chain", "Failed to read from database: {}", e); warn!(target: "chain", "Failed to read from database: {}", e);
None None
} }
} },
}
} }
}; };
match id { match id {
BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_header.clone()), BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_header.clone()),
BlockId::Hash(hash) if hash == self.genesis_hash() => { Some(self.genesis_header.clone()) } BlockId::Hash(hash) if hash == self.genesis_hash() => Some(self.genesis_header.clone()),
BlockId::Hash(hash) => load_from_db(hash), BlockId::Hash(hash) => load_from_db(hash),
BlockId::Number(num) => { BlockId::Number(num) => {
if self.best_block.read().number < num { return None } if self.best_block.read().number < num {
return None;
}
self.candidates.read().get(&num).map(|entry| entry.canonical_hash) self.candidates
.read()
.get(&num)
.map(|entry| entry.canonical_hash)
.and_then(load_from_db) .and_then(load_from_db)
} }
BlockId::Latest => { BlockId::Latest => {
@ -681,7 +737,7 @@ impl HeaderChain {
let hash = { let hash = {
let best = self.best_block.read(); let best = self.best_block.read();
if best.number == 0 { if best.number == 0 {
return Some(self.genesis_header.clone()) return Some(self.genesis_header.clone());
} }
best.hash best.hash
@ -700,23 +756,31 @@ impl HeaderChain {
BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_header.difficulty()), BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_header.difficulty()),
BlockId::Hash(hash) if hash == genesis_hash => Some(self.genesis_header.difficulty()), BlockId::Hash(hash) if hash == genesis_hash => Some(self.genesis_header.difficulty()),
BlockId::Hash(hash) => match self.block_header(BlockId::Hash(hash)) { BlockId::Hash(hash) => match self.block_header(BlockId::Hash(hash)) {
Some(header) => self.candidates.read().get(&header.number()) Some(header) => self
.candidates
.read()
.get(&header.number())
.and_then(|era| era.candidates.iter().find(|e| e.hash == hash)) .and_then(|era| era.candidates.iter().find(|e| e.hash == hash))
.map(|c| c.total_difficulty), .map(|c| c.total_difficulty),
None => None, None => None,
}, },
BlockId::Number(num) => { BlockId::Number(num) => {
let candidates = self.candidates.read(); let candidates = self.candidates.read();
if self.best_block.read().number < num { return None } if self.best_block.read().number < num {
candidates.get(&num).map(|era| era.candidates[0].total_difficulty) return None;
} }
BlockId::Latest => Some(self.best_block.read().total_difficulty) candidates
.get(&num)
.map(|era| era.candidates[0].total_difficulty)
}
BlockId::Latest => Some(self.best_block.read().total_difficulty),
} }
} }
/// Get the best block's header. /// Get the best block's header.
pub fn best_header(&self) -> encoded::Header { pub fn best_header(&self) -> encoded::Header {
self.block_header(BlockId::Latest).expect("Header for best block always stored; qed") self.block_header(BlockId::Latest)
.expect("Header for best block always stored; qed")
} }
/// Get an iterator over a block and its ancestry. /// Get an iterator over a block and its ancestry.
@ -737,7 +801,9 @@ impl HeaderChain {
/// so including it within a CHT would be redundant. /// so including it within a CHT would be redundant.
pub fn cht_root(&self, n: usize) -> Option<H256> { pub fn cht_root(&self, n: usize) -> Option<H256> {
match self.db.get(self.col, cht_key(n as u64).as_bytes()) { match self.db.get(self.col, cht_key(n as u64).as_bytes()) {
Ok(db_fetch) => db_fetch.map(|bytes| ::rlp::decode(&bytes).expect("decoding value from db failed")), Ok(db_fetch) => {
db_fetch.map(|bytes| ::rlp::decode(&bytes).expect("decoding value from db failed"))
}
Err(e) => { Err(e) => {
warn!(target: "chain", "Error reading from database: {}", e); warn!(target: "chain", "Error reading from database: {}", e);
None None
@ -764,15 +830,24 @@ impl HeaderChain {
Some((&height, entry)) => Some(BlockDescriptor { Some((&height, entry)) => Some(BlockDescriptor {
number: height, number: height,
hash: entry.canonical_hash, hash: entry.canonical_hash,
total_difficulty: entry.candidates.iter().find(|x| x.hash == entry.canonical_hash) total_difficulty: entry
.expect("entry always stores canonical candidate; qed").total_difficulty, .candidates
}) .iter()
.find(|x| x.hash == entry.canonical_hash)
.expect("entry always stores canonical candidate; qed")
.total_difficulty,
}),
} }
} }
/// Get block status. /// Get block status.
pub fn status(&self, hash: &H256) -> BlockStatus { pub fn status(&self, hash: &H256) -> BlockStatus {
if self.db.get(self.col, hash).ok().map_or(false, |x| x.is_some()) { if self
.db
.get(self.col, hash)
.ok()
.map_or(false, |x| x.is_some())
{
BlockStatus::InChain BlockStatus::InChain
} else { } else {
BlockStatus::Unknown BlockStatus::Unknown
@ -780,7 +855,12 @@ impl HeaderChain {
} }
/// Insert a pending transition. /// Insert a pending transition.
pub fn insert_pending_transition(&self, batch: &mut DBTransaction, hash: H256, t: &PendingEpochTransition) { pub fn insert_pending_transition(
&self,
batch: &mut DBTransaction,
hash: H256,
t: &PendingEpochTransition,
) {
let key = pending_transition_key(hash); let key = pending_transition_key(hash);
batch.put(self.col, &*key, &*::rlp::encode(t)); batch.put(self.col, &*key, &*::rlp::encode(t));
} }
@ -789,7 +869,9 @@ impl HeaderChain {
pub fn pending_transition(&self, hash: H256) -> Option<PendingEpochTransition> { pub fn pending_transition(&self, hash: H256) -> Option<PendingEpochTransition> {
let key = pending_transition_key(hash); let key = pending_transition_key(hash);
match self.db.get(self.col, &*key) { match self.db.get(self.col, &*key) {
Ok(db_fetch) => db_fetch.map(|bytes| ::rlp::decode(&bytes).expect("decoding value from db failed")), Ok(db_fetch) => {
db_fetch.map(|bytes| ::rlp::decode(&bytes).expect("decoding value from db failed"))
}
Err(e) => { Err(e) => {
warn!(target: "chain", "Error reading from database: {}", e); warn!(target: "chain", "Error reading from database: {}", e);
None None
@ -808,9 +890,10 @@ impl HeaderChain {
for hdr in self.ancestry_iter(BlockId::Hash(parent_hash)) { for hdr in self.ancestry_iter(BlockId::Hash(parent_hash)) {
if let Some(transition) = live_proofs.get(&hdr.hash()).cloned() { if let Some(transition) = live_proofs.get(&hdr.hash()).cloned() {
return hdr.decode().map(|decoded_hdr| { return hdr
(decoded_hdr, transition.proof) .decode()
}).ok(); .map(|decoded_hdr| (decoded_hdr, transition.proof))
.ok();
} }
} }
@ -859,19 +942,18 @@ impl<'a> Iterator for AncestryIter<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{HeaderChain, HardcodedSync}; use super::{HardcodedSync, HeaderChain};
use std::sync::Arc; use std::sync::Arc;
use cache::Cache; use cache::Cache;
use common_types::header::Header; use common_types::{header::Header, ids::BlockId};
use common_types::ids::BlockId;
use ethcore::spec::Spec; use ethcore::spec::Spec;
use ethereum_types::U256; use ethereum_types::U256;
use kvdb::KeyValueDB; use kvdb::KeyValueDB;
use kvdb_memorydb; use kvdb_memorydb;
use std::time::Duration;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::time::Duration;
fn make_db() -> Arc<KeyValueDB> { fn make_db() -> Arc<KeyValueDB> {
Arc::new(kvdb_memorydb::create(0)) Arc::new(kvdb_memorydb::create(0))
@ -883,7 +965,10 @@ mod tests {
let genesis_header = spec.genesis_header(); let genesis_header = spec.genesis_header();
let db = make_db(); let db = make_db();
let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); let cache = Arc::new(Mutex::new(Cache::new(
Default::default(),
Duration::from_secs(6 * 3600),
)));
let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).unwrap(); let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).unwrap();
@ -916,7 +1001,10 @@ mod tests {
let spec = Spec::new_test(); let spec = Spec::new_test();
let genesis_header = spec.genesis_header(); let genesis_header = spec.genesis_header();
let db = make_db(); let db = make_db();
let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); let cache = Arc::new(Mutex::new(Cache::new(
Default::default(),
Duration::from_secs(6 * 3600),
)));
let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).unwrap(); let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).unwrap();
@ -998,7 +1086,10 @@ mod tests {
fn earliest_is_latest() { fn earliest_is_latest() {
let spec = Spec::new_test(); let spec = Spec::new_test();
let db = make_db(); let db = make_db();
let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); let cache = Arc::new(Mutex::new(Cache::new(
Default::default(),
Duration::from_secs(6 * 3600),
)));
let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).unwrap(); let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).unwrap();
@ -1011,11 +1102,15 @@ mod tests {
let spec = Spec::new_test(); let spec = Spec::new_test();
let genesis_header = spec.genesis_header(); let genesis_header = spec.genesis_header();
let db = make_db(); let db = make_db();
let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); let cache = Arc::new(Mutex::new(Cache::new(
Default::default(),
Duration::from_secs(6 * 3600),
)));
{ {
let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone(), let chain =
HardcodedSync::Allow).unwrap(); HeaderChain::new(db.clone(), None, &spec, cache.clone(), HardcodedSync::Allow)
.unwrap();
let mut parent_hash = genesis_header.hash(); let mut parent_hash = genesis_header.hash();
let mut rolling_timestamp = genesis_header.timestamp(); let mut rolling_timestamp = genesis_header.timestamp();
for i in 1..10000 { for i in 1..10000 {
@ -1035,8 +1130,8 @@ mod tests {
} }
} }
let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone(), let chain =
HardcodedSync::Allow).unwrap(); HeaderChain::new(db.clone(), None, &spec, cache.clone(), HardcodedSync::Allow).unwrap();
assert!(chain.block_header(BlockId::Number(10)).is_none()); assert!(chain.block_header(BlockId::Number(10)).is_none());
assert!(chain.block_header(BlockId::Number(9000)).is_some()); assert!(chain.block_header(BlockId::Number(9000)).is_some());
assert!(chain.cht_root(2).is_some()); assert!(chain.cht_root(2).is_some());
@ -1049,11 +1144,15 @@ mod tests {
let spec = Spec::new_test(); let spec = Spec::new_test();
let genesis_header = spec.genesis_header(); let genesis_header = spec.genesis_header();
let db = make_db(); let db = make_db();
let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); let cache = Arc::new(Mutex::new(Cache::new(
Default::default(),
Duration::from_secs(6 * 3600),
)));
{ {
let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone(), let chain =
HardcodedSync::Allow).unwrap(); HeaderChain::new(db.clone(), None, &spec, cache.clone(), HardcodedSync::Allow)
.unwrap();
let mut parent_hash = genesis_header.hash(); let mut parent_hash = genesis_header.hash();
let mut rolling_timestamp = genesis_header.timestamp(); let mut rolling_timestamp = genesis_header.timestamp();
@ -1080,7 +1179,8 @@ mod tests {
header.set_parent_hash(parent_hash); header.set_parent_hash(parent_hash);
header.set_number(i); header.set_number(i);
header.set_timestamp(rolling_timestamp); header.set_timestamp(rolling_timestamp);
header.set_difficulty(*genesis_header.difficulty() * U256::from(i as u32 * 1000u32)); header
.set_difficulty(*genesis_header.difficulty() * U256::from(i as u32 * 1000u32));
parent_hash = header.hash(); parent_hash = header.hash();
let mut tx = db.transaction(); let mut tx = db.transaction();
@ -1095,8 +1195,8 @@ mod tests {
} }
// after restoration, non-canonical eras should still be loaded. // after restoration, non-canonical eras should still be loaded.
let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone(), let chain =
HardcodedSync::Allow).unwrap(); HeaderChain::new(db.clone(), None, &spec, cache.clone(), HardcodedSync::Allow).unwrap();
assert_eq!(chain.block_header(BlockId::Latest).unwrap().number(), 10); assert_eq!(chain.block_header(BlockId::Latest).unwrap().number(), 10);
assert!(chain.candidates.read().get(&100).is_some()) assert!(chain.candidates.read().get(&100).is_some())
} }
@ -1106,14 +1206,19 @@ mod tests {
let spec = Spec::new_test(); let spec = Spec::new_test();
let genesis_header = spec.genesis_header(); let genesis_header = spec.genesis_header();
let db = make_db(); let db = make_db();
let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); let cache = Arc::new(Mutex::new(Cache::new(
Default::default(),
Duration::from_secs(6 * 3600),
)));
let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone(), let chain =
HardcodedSync::Allow).unwrap(); HeaderChain::new(db.clone(), None, &spec, cache.clone(), HardcodedSync::Allow).unwrap();
assert!(chain.block_header(BlockId::Earliest).is_some()); assert!(chain.block_header(BlockId::Earliest).is_some());
assert!(chain.block_header(BlockId::Number(0)).is_some()); assert!(chain.block_header(BlockId::Number(0)).is_some());
assert!(chain.block_header(BlockId::Hash(genesis_header.hash())).is_some()); assert!(chain
.block_header(BlockId::Hash(genesis_header.hash()))
.is_some());
} }
#[test] #[test]
@ -1121,7 +1226,10 @@ mod tests {
let spec = Spec::new_test(); let spec = Spec::new_test();
let genesis_header = spec.genesis_header(); let genesis_header = spec.genesis_header();
let db = make_db(); let db = make_db();
let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); let cache = Arc::new(Mutex::new(Cache::new(
Default::default(),
Duration::from_secs(6 * 3600),
)));
let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).unwrap(); let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).unwrap();
@ -1136,11 +1244,7 @@ mod tests {
parent_hash = header.hash(); parent_hash = header.hash();
let mut tx = db.transaction(); let mut tx = db.transaction();
let epoch_proof = if i == 3 { let epoch_proof = if i == 3 { Some(vec![1, 2, 3, 4]) } else { None };
Some(vec![1, 2, 3, 4])
} else {
None
};
let pending = chain.insert(&mut tx, &header, epoch_proof).unwrap(); let pending = chain.insert(&mut tx, &header, epoch_proof).unwrap();
db.write(tx).unwrap(); db.write(tx).unwrap();
@ -1152,13 +1256,19 @@ mod tests {
// these 3 should end up falling back to the genesis epoch proof in DB // these 3 should end up falling back to the genesis epoch proof in DB
for i in 0..3 { for i in 0..3 {
let hash = chain.block_hash(BlockId::Number(i)).unwrap(); let hash = chain.block_hash(BlockId::Number(i)).unwrap();
assert_eq!(chain.epoch_transition_for(hash).unwrap().1, Vec::<u8>::new()); assert_eq!(
chain.epoch_transition_for(hash).unwrap().1,
Vec::<u8>::new()
);
} }
// these are live. // these are live.
for i in 3..6 { for i in 3..6 {
let hash = chain.block_hash(BlockId::Number(i)).unwrap(); let hash = chain.block_hash(BlockId::Number(i)).unwrap();
assert_eq!(chain.epoch_transition_for(hash).unwrap().1, vec![1, 2, 3, 4]); assert_eq!(
chain.epoch_transition_for(hash).unwrap().1,
vec![1, 2, 3, 4]
);
} }
for i in 6..10000 { for i in 6..10000 {
@ -1179,7 +1289,10 @@ mod tests {
// no live blocks have associated epoch proofs -- make sure we aren't leaking memory. // no live blocks have associated epoch proofs -- make sure we aren't leaking memory.
assert!(chain.live_epoch_proofs.read().is_empty()); assert!(chain.live_epoch_proofs.read().is_empty());
assert_eq!(chain.epoch_transition_for(parent_hash).unwrap().1, vec![1, 2, 3, 4]); assert_eq!(
chain.epoch_transition_for(parent_hash).unwrap().1,
vec![1, 2, 3, 4]
);
} }
#[test] #[test]
@ -1188,9 +1301,13 @@ mod tests {
let genesis_header = spec.genesis_header(); let genesis_header = spec.genesis_header();
let db = make_db(); let db = make_db();
let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); let cache = Arc::new(Mutex::new(Cache::new(
Default::default(),
Duration::from_secs(6 * 3600),
)));
let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow).expect("failed to instantiate a new HeaderChain"); let chain = HeaderChain::new(db.clone(), None, &spec, cache, HardcodedSync::Allow)
.expect("failed to instantiate a new HeaderChain");
let mut parent_hash = genesis_header.hash(); let mut parent_hash = genesis_header.hash();
let mut rolling_timestamp = genesis_header.timestamp(); let mut rolling_timestamp = genesis_header.timestamp();
@ -1209,14 +1326,19 @@ mod tests {
parent_hash = header.hash(); parent_hash = header.hash();
let mut tx = db.transaction(); let mut tx = db.transaction();
let pending = chain.insert(&mut tx, &header, None).expect("failed inserting a transaction"); let pending = chain
.insert(&mut tx, &header, None)
.expect("failed inserting a transaction");
db.write(tx).unwrap(); db.write(tx).unwrap();
chain.apply_pending(pending); chain.apply_pending(pending);
rolling_timestamp += 10; rolling_timestamp += 10;
} }
let hardcoded_sync = chain.read_hardcoded_sync().expect("failed reading hardcoded sync").expect("failed unwrapping hardcoded sync"); let hardcoded_sync = chain
.read_hardcoded_sync()
.expect("failed reading hardcoded sync")
.expect("failed unwrapping hardcoded sync");
assert_eq!(hardcoded_sync.chts.len(), 3); assert_eq!(hardcoded_sync.chts.len(), 3);
assert_eq!(hardcoded_sync.total_difficulty, total_difficulty); assert_eq!(hardcoded_sync.total_difficulty, total_difficulty);
let decoded: Header = hardcoded_sync.header.decode().expect("decoding failed"); let decoded: Header = hardcoded_sync.header.decode().expect("decoding failed");

View File

@ -16,29 +16,31 @@
//! Light client implementation. Stores data from light sync //! Light client implementation. Stores data from light sync
use std::sync::{Weak, Arc}; use std::sync::{Arc, Weak};
use ethcore::client::{ClientReport, EnvInfo, ClientIoMessage, traits::ForceUpdateSealing}; use common_types::{
use ethcore::engines::{epoch, EthEngine, EpochChange, EpochTransition, Proof}; block_status::BlockStatus, blockchain_info::BlockChainInfo, encoded, header::Header,
use ethcore::machine::EthereumMachine; ids::BlockId, BlockNumber,
use ethcore::error::{Error, EthcoreResult}; };
use ethcore::verification::queue::{self, HeaderQueue}; use ethcore::{
use ethcore::spec::{Spec, SpecHardcodedSync}; client::{traits::ForceUpdateSealing, ClientIoMessage, ClientReport, EnvInfo},
engines::{epoch, EpochChange, EpochTransition, EthEngine, Proof},
error::{Error, EthcoreResult},
machine::EthereumMachine,
spec::{Spec, SpecHardcodedSync},
verification::queue::{self, HeaderQueue},
};
use ethereum_types::{H256, U256};
use futures::{Future, IntoFuture};
use io::IoChannel; use io::IoChannel;
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use ethereum_types::{H256, U256};
use futures::{IntoFuture, Future};
use common_types::BlockNumber;
use common_types::block_status::BlockStatus;
use common_types::blockchain_info::BlockChainInfo;
use common_types::encoded;
use common_types::header::Header;
use common_types::ids::BlockId;
use kvdb::KeyValueDB; use kvdb::KeyValueDB;
use self::fetch::ChainDataFetcher; use self::{
use self::header_chain::{AncestryIter, HeaderChain, HardcodedSync}; fetch::ChainDataFetcher,
header_chain::{AncestryIter, HardcodedSync, HeaderChain},
};
use cache::Cache; use cache::Cache;
@ -101,7 +103,7 @@ pub trait LightChainClient: Send + Sync {
fn score(&self, id: BlockId) -> Option<U256>; fn score(&self, id: BlockId) -> Option<U256>;
/// Get an iterator over a block and its ancestry. /// Get an iterator over a block and its ancestry.
fn ancestry_iter<'a>(&'a self, start: BlockId) -> Box<Iterator<Item=encoded::Header> + 'a>; fn ancestry_iter<'a>(&'a self, start: BlockId) -> Box<Iterator<Item = encoded::Header> + 'a>;
/// Get the signing chain ID. /// Get the signing chain ID.
fn signing_chain_id(&self) -> Option<u64>; fn signing_chain_id(&self) -> Option<u64>;
@ -153,7 +155,9 @@ pub trait AsLightClient {
impl<T: LightChainClient> AsLightClient for T { impl<T: LightChainClient> AsLightClient for T {
type Client = Self; type Client = Self;
fn as_light_client(&self) -> &Self { self } fn as_light_client(&self) -> &Self {
self
}
} }
/// Light client implementation. /// Light client implementation.
@ -180,13 +184,22 @@ impl<T: ChainDataFetcher> Client<T> {
spec: &Spec, spec: &Spec,
fetcher: T, fetcher: T,
io_channel: IoChannel<ClientIoMessage>, io_channel: IoChannel<ClientIoMessage>,
cache: Arc<Mutex<Cache>> cache: Arc<Mutex<Cache>>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
Ok(Self { Ok(Self {
queue: HeaderQueue::new(config.queue, spec.engine.clone(), io_channel, config.check_seal), queue: HeaderQueue::new(
config.queue,
spec.engine.clone(),
io_channel,
config.check_seal,
),
engine: spec.engine.clone(), engine: spec.engine.clone(),
chain: { chain: {
let hs_cfg = if config.no_hardcoded_sync { HardcodedSync::Deny } else { HardcodedSync::Allow }; let hs_cfg = if config.no_hardcoded_sync {
HardcodedSync::Deny
} else {
HardcodedSync::Allow
};
HeaderChain::new(db.clone(), chain_col, &spec, cache, hs_cfg)? HeaderChain::new(db.clone(), chain_col, &spec, cache, hs_cfg)?
}, },
report: RwLock::new(ClientReport::default()), report: RwLock::new(ClientReport::default()),
@ -240,7 +253,11 @@ impl<T: ChainDataFetcher> Client<T> {
best_block_hash: best_hdr.hash(), best_block_hash: best_hdr.hash(),
best_block_number: best_hdr.number(), best_block_number: best_hdr.number(),
best_block_timestamp: best_hdr.timestamp(), best_block_timestamp: best_hdr.timestamp(),
ancient_block_hash: if first_block.is_some() { Some(genesis_hash) } else { None }, ancient_block_hash: if first_block.is_some() {
Some(genesis_hash)
} else {
None
},
ancient_block_number: if first_block.is_some() { Some(0) } else { None }, ancient_block_number: if first_block.is_some() { Some(0) } else { None },
first_block_hash: first_block.as_ref().map(|first| first.hash), first_block_hash: first_block.as_ref().map(|first| first.hash),
first_block_number: first_block.as_ref().map(|first| first.number), first_block_number: first_block.as_ref().map(|first| first.number),
@ -305,14 +322,13 @@ impl<T: ChainDataFetcher> Client<T> {
trace!(target: "client", "importing block {}", num); trace!(target: "client", "importing block {}", num);
if self.verify_full && !self.check_header(&mut bad, &verified_header) { if self.verify_full && !self.check_header(&mut bad, &verified_header) {
continue continue;
} }
let write_proof_result = match self.check_epoch_signal(&verified_header) { let write_proof_result = match self.check_epoch_signal(&verified_header) {
Ok(Some(proof)) => self.write_pending_proof(&verified_header, proof), Ok(Some(proof)) => self.write_pending_proof(&verified_header, proof),
Ok(None) => Ok(()), Ok(None) => Ok(()),
Err(e) => Err(e) => panic!("Unable to fetch epoch transition proof: {:?}", e),
panic!("Unable to fetch epoch transition proof: {:?}", e),
}; };
if let Err(e) = write_proof_result { if let Err(e) = write_proof_result {
@ -322,7 +338,11 @@ impl<T: ChainDataFetcher> Client<T> {
let epoch_proof = self.engine.is_epoch_end_light( let epoch_proof = self.engine.is_epoch_end_light(
&verified_header, &verified_header,
&|h| self.chain.block_header(BlockId::Hash(h)).and_then(|hdr| hdr.decode().ok()), &|h| {
self.chain
.block_header(BlockId::Hash(h))
.and_then(|hdr| hdr.decode().ok())
},
&|h| self.chain.pending_transition(h), &|h| self.chain.pending_transition(h),
); );
@ -370,7 +390,10 @@ impl<T: ChainDataFetcher> Client<T> {
/// ///
/// The parameter passed to the callback is the name of the new chain spec to use after /// The parameter passed to the callback is the name of the new chain spec to use after
/// the restart. /// the restart.
pub fn set_exit_handler<F>(&self, f: F) where F: Fn(String) + 'static + Send { pub fn set_exit_handler<F>(&self, f: F)
where
F: Fn(String) + 'static + Send,
{
*self.exit_handler.lock() = Some(Box::new(f)); *self.exit_handler.lock() = Some(Box::new(f));
} }
@ -428,24 +451,25 @@ impl<T: ChainDataFetcher> Client<T> {
// should skip. // should skip.
fn check_header(&self, bad: &mut Vec<H256>, verified_header: &Header) -> bool { fn check_header(&self, bad: &mut Vec<H256>, verified_header: &Header) -> bool {
let hash = verified_header.hash(); let hash = verified_header.hash();
let parent_header = match self.chain.block_header(BlockId::Hash(*verified_header.parent_hash())) { let parent_header = match self
.chain
.block_header(BlockId::Hash(*verified_header.parent_hash()))
{
Some(header) => header, Some(header) => header,
None => { None => {
trace!(target: "client", "No parent for block ({}, {})", trace!(target: "client", "No parent for block ({}, {})",
verified_header.number(), hash); verified_header.number(), hash);
return false // skip import of block with missing parent. return false; // skip import of block with missing parent.
} }
}; };
// Verify Block Family // Verify Block Family
let verify_family_result = { let verify_family_result = {
parent_header.decode() parent_header
.decode()
.map_err(|dec_err| dec_err.into()) .map_err(|dec_err| dec_err.into())
.and_then(|decoded| { .and_then(|decoded| self.engine.verify_block_family(&verified_header, &decoded))
self.engine.verify_block_family(&verified_header, &decoded)
})
}; };
if let Err(e) = verify_family_result { if let Err(e) = verify_family_result {
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}",
@ -467,14 +491,16 @@ impl<T: ChainDataFetcher> Client<T> {
true true
} }
fn check_epoch_signal(&self, verified_header: &Header) -> Result<Option<Proof<EthereumMachine>>, T::Error> { fn check_epoch_signal(
use ethcore::machine::{AuxiliaryRequest, AuxiliaryData}; &self,
verified_header: &Header,
) -> Result<Option<Proof<EthereumMachine>>, T::Error> {
use ethcore::machine::{AuxiliaryData, AuxiliaryRequest};
let mut block: Option<Vec<u8>> = None; let mut block: Option<Vec<u8>> = None;
let mut receipts: Option<Vec<_>> = None; let mut receipts: Option<Vec<_>> = None;
loop { loop {
let is_signal = { let is_signal = {
let auxiliary = AuxiliaryData { let auxiliary = AuxiliaryData {
bytes: block.as_ref().map(|x| &x[..]), bytes: block.as_ref().map(|x| &x[..]),
@ -490,10 +516,12 @@ impl<T: ChainDataFetcher> Client<T> {
EpochChange::Yes(proof) => return Ok(Some(proof)), EpochChange::Yes(proof) => return Ok(Some(proof)),
EpochChange::Unsure(unsure) => { EpochChange::Unsure(unsure) => {
let (b, r) = match unsure { let (b, r) = match unsure {
AuxiliaryRequest::Body => AuxiliaryRequest::Body => {
(Some(self.fetcher.block_body(verified_header)), None), (Some(self.fetcher.block_body(verified_header)), None)
AuxiliaryRequest::Receipts => }
(None, Some(self.fetcher.block_receipts(verified_header))), AuxiliaryRequest::Receipts => {
(None, Some(self.fetcher.block_receipts(verified_header)))
}
AuxiliaryRequest::Both => ( AuxiliaryRequest::Both => (
Some(self.fetcher.block_body(verified_header)), Some(self.fetcher.block_body(verified_header)),
Some(self.fetcher.block_receipts(verified_header)), Some(self.fetcher.block_receipts(verified_header)),
@ -513,22 +541,26 @@ impl<T: ChainDataFetcher> Client<T> {
} }
// attempts to fetch the epoch proof from the network until successful. // attempts to fetch the epoch proof from the network until successful.
fn write_pending_proof(&self, header: &Header, proof: Proof<EthereumMachine>) -> Result<(), T::Error> { fn write_pending_proof(
&self,
header: &Header,
proof: Proof<EthereumMachine>,
) -> Result<(), T::Error> {
let proof = match proof { let proof = match proof {
Proof::Known(known) => known, Proof::Known(known) => known,
Proof::WithState(state_dependent) => { Proof::WithState(state_dependent) => self
self.fetcher.epoch_transition( .fetcher
header.hash(), .epoch_transition(header.hash(), self.engine.clone(), state_dependent)
self.engine.clone(), .into_future()
state_dependent .wait()?,
).into_future().wait()?
}
}; };
let mut batch = self.db.transaction(); let mut batch = self.db.transaction();
self.chain.insert_pending_transition(&mut batch, header.hash(), &epoch::PendingTransition { self.chain.insert_pending_transition(
proof, &mut batch,
}); header.hash(),
&epoch::PendingTransition { proof },
);
self.db.write_buffered(batch); self.db.write_buffered(batch);
Ok(()) Ok(())
} }
@ -539,7 +571,9 @@ impl<T: ChainDataFetcher> LightChainClient for Client<T> {
Client::add_listener(self, listener) Client::add_listener(self, listener)
} }
fn chain_info(&self) -> BlockChainInfo { Client::chain_info(self) } fn chain_info(&self) -> BlockChainInfo {
Client::chain_info(self)
}
fn queue_header(&self, header: Header) -> EthcoreResult<H256> { fn queue_header(&self, header: Header) -> EthcoreResult<H256> {
self.import_header(header) self.import_header(header)
@ -561,7 +595,7 @@ impl<T: ChainDataFetcher> LightChainClient for Client<T> {
Client::score(self, id) Client::score(self, id)
} }
fn ancestry_iter<'a>(&'a self, start: BlockId) -> Box<Iterator<Item=encoded::Header> + 'a> { fn ancestry_iter<'a>(&'a self, start: BlockId) -> Box<Iterator<Item = encoded::Header> + 'a> {
Box::new(Client::ancestry_iter(self, start)) Box::new(Client::ancestry_iter(self, start))
} }
@ -621,11 +655,13 @@ impl<T: ChainDataFetcher> ::ethcore::client::ChainInfo for Client<T> {
impl<T: ChainDataFetcher> ::ethcore::client::EngineClient for Client<T> { impl<T: ChainDataFetcher> ::ethcore::client::EngineClient for Client<T> {
fn update_sealing(&self, _force: ForceUpdateSealing) {} fn update_sealing(&self, _force: ForceUpdateSealing) {}
fn submit_seal(&self, _block_hash: H256, _seal: Vec<Vec<u8>>) { } fn submit_seal(&self, _block_hash: H256, _seal: Vec<Vec<u8>>) {}
fn broadcast_consensus_message(&self, _message: Vec<u8>) { } fn broadcast_consensus_message(&self, _message: Vec<u8>) {}
fn epoch_transition_for(&self, parent_hash: H256) -> Option<EpochTransition> { fn epoch_transition_for(&self, parent_hash: H256) -> Option<EpochTransition> {
self.chain.epoch_transition_for(parent_hash).map(|(hdr, proof)| EpochTransition { self.chain
.epoch_transition_for(parent_hash)
.map(|(hdr, proof)| EpochTransition {
block_hash: hdr.hash(), block_hash: hdr.hash(),
block_number: hdr.number(), block_number: hdr.number(),
proof, proof,

Some files were not shown because too many files have changed in this diff Show More