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]
extern crate serde_derive;
use std::num::ParseIntError;
use std::{env, fmt, process, io, sync};
use std::{env, fmt, io, num::ParseIntError, process, sync};
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};
const USAGE: &'static str = r#"
@ -179,7 +181,7 @@ fn display(result: (KeyPair, Option<String>), mode: DisplayMode) -> String {
match mode {
DisplayMode::KeyPair => match result.1 {
Some(extra_data) => format!("{}\n{}", extra_data, keypair),
None => format!("{}", keypair)
None => format!("{}", keypair),
},
DisplayMode::Secret => format!("{:x}", keypair.secret()),
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> {
let args: Args = Docopt::new(USAGE)
.and_then(|d| d.argv(command).deserialize())?;
fn execute<S, I>(command: I) -> Result<String, Error>
where
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 {
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 phrase = args.arg_secret_or_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))
} 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)
};
Ok(display(result, display_mode))
@ -237,33 +247,55 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
}
})?
} else {
return Ok(format!("{}", USAGE))
return Ok(format!("{}", USAGE));
};
Ok(display(result, display_mode))
} else if args.cmd_sign {
let secret = args.arg_secret.parse().map_err(|_| EthkeyError::InvalidSecret)?;
let message = args.arg_message.parse().map_err(|_| EthkeyError::InvalidMessage)?;
let secret = args
.arg_secret
.parse()
.map_err(|_| EthkeyError::InvalidSecret)?;
let message = args
.arg_message
.parse()
.map_err(|_| EthkeyError::InvalidMessage)?;
let signature = sign(&secret, &message)?;
Ok(format!("{}", signature))
} else if args.cmd_verify {
let signature = args.arg_signature.parse().map_err(|_| EthkeyError::InvalidSignature)?;
let message = args.arg_message.parse().map_err(|_| EthkeyError::InvalidMessage)?;
let signature = args
.arg_signature
.parse()
.map_err(|_| EthkeyError::InvalidSignature)?;
let message = args
.arg_message
.parse()
.map_err(|_| EthkeyError::InvalidMessage)?;
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)?
} 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)?
} else {
return Ok(format!("{}", USAGE))
return Ok(format!("{}", USAGE));
};
Ok(format!("{}", ok))
} else if args.cmd_recover {
let display_mode = DisplayMode::new(&args);
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 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 || {
let mut i = 0;
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();
if keypair.address() == address {
return Ok(Some((phrase, keypair)))
return Ok(Some((phrase, keypair)));
}
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))
} else {
Ok(format!("{}", USAGE))
}
};
}
const BRAIN_WORDS: usize = 12;
@ -293,11 +325,12 @@ const BRAIN_WORDS: usize = 12;
fn validate_phrase(phrase: &str) -> String {
match Brain::validate_phrase(phrase, BRAIN_WORDS) {
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,
X: Send + 'static,
F: Fn() -> X,
@ -344,7 +377,11 @@ mod tests {
#[test]
fn info() {
let command = vec!["ethkey", "info", "17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55"]
let command = vec![
"ethkey",
"info",
"17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55",
]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();
@ -379,7 +416,8 @@ address: 006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
.map(Into::into)
.collect::<Vec<String>>();
let expected = "aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2".to_owned();
let expected =
"aa22b54c0cb43ee30a014afe5ef3664b1cde299feabca46cd3167a85a57c39f2".to_owned();
assert_eq!(execute(command).unwrap(), expected);
}
@ -407,7 +445,12 @@ address: 006e27b6a72e1f34c626762f3c4761547aff1421".to_owned();
#[test]
fn sign() {
let command = vec!["ethkey", "sign", "17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"]
let command = vec![
"ethkey",
"sign",
"17d08f5fe8c77af811caa0c9a187e668ce3b74a99acc3f6d976f075fa8e0be55",
"bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987",
]
.into_iter()
.map(Into::into)
.collect::<Vec<String>>();

View File

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

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// 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;
/// 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();
if keypair.address().starts_with(&self.prefix) {
self.last_phrase = phrase;
return Ok(keypair)
return Ok(keypair);
}
}
@ -59,12 +59,15 @@ impl Generator for BrainPrefix {
#[cfg(test)]
mod tests {
use {Generator, BrainPrefix};
use BrainPrefix;
use Generator;
#[test]
fn prefix_generator() {
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));
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// 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.
pub struct Prefix {
@ -38,7 +38,7 @@ impl Generator for Prefix {
for _ in 0..self.iterations {
let keypair = Random.generate()?;
if keypair.address().starts_with(&self.prefix) {
return Ok(keypair)
return Ok(keypair);
}
}
@ -48,12 +48,15 @@ impl Generator for Prefix {
#[cfg(test)]
mod tests {
use {Generator, Prefix};
use Generator;
use Prefix;
#[test]
fn prefix_generator() {
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));
}
}

View File

@ -14,8 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use rand::os::OsRng;
use super::{Generator, KeyPair, SECP256K1};
use rand::os::OsRng;
/// Randomly generates new keypair, instantiating the RNG each time.
pub struct Random;
@ -36,7 +36,8 @@ impl Generator for OsRng {
type Error = ::Void;
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");
Ok(KeyPair::from_keypair(sec, publ))

View File

@ -14,15 +14,13 @@
// You should have received a copy of the GNU General Public License
// 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 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)]
pub struct Secret {
@ -49,7 +47,11 @@ impl fmt::Debug for Secret {
impl fmt::Display for Secret {
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.
pub fn from_slice(key: &[u8]) -> Option<Self> {
if key.len() != 32 {
return None
return None;
}
let mut h = H256::default();
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.
pub fn zero() -> Self {
Secret { inner: Memzero::from(H256::default()) }
Secret {
inner: Memzero::from(H256::default()),
}
}
/// Imports and validates the key.
@ -87,7 +93,7 @@ impl Secret {
(true, false) => {
*self = other.clone();
Ok(())
},
}
(false, false) => {
let mut key_secret = self.to_secp256k1_secret()?;
let other_secret = other.to_secp256k1_secret()?;
@ -95,7 +101,7 @@ impl Secret {
*self = key_secret.into();
Ok(())
},
}
}
}
@ -106,7 +112,7 @@ impl Secret {
(true, false) => {
*self = other.clone();
self.neg()
},
}
(false, false) => {
let mut key_secret = self.to_secp256k1_secret()?;
let mut other_secret = other.to_secp256k1_secret()?;
@ -115,7 +121,7 @@ impl Secret {
*self = key_secret.into();
Ok(())
},
}
}
}
@ -125,14 +131,14 @@ impl Secret {
true => {
*self = key::MINUS_ONE_KEY.into();
Ok(())
},
}
false => {
let mut key_secret = self.to_secp256k1_secret()?;
key_secret.add_assign(&SECP256K1, &key::MINUS_ONE_KEY)?;
*self = key_secret.into();
Ok(())
},
}
}
}
@ -143,7 +149,7 @@ impl Secret {
(false, true) => {
*self = Self::zero();
Ok(())
},
}
(false, false) => {
let mut key_secret = self.to_secp256k1_secret()?;
let other_secret = other.to_secp256k1_secret()?;
@ -151,7 +157,7 @@ impl Secret {
*self = key_secret.into();
Ok(())
},
}
}
}
@ -165,7 +171,7 @@ impl Secret {
*self = key_secret.into();
Ok(())
},
}
}
}
@ -193,7 +199,7 @@ impl Secret {
for _ in 1..pow {
self.mul(&c)?;
}
},
}
}
Ok(())
@ -208,13 +214,17 @@ impl Secret {
impl FromStr for Secret {
type Err = Error;
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 {
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 {
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 {
fn from(key: key::SecretKey) -> Self {
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()
}
}
@ -248,9 +262,11 @@ impl Deref for Secret {
#[cfg(test)]
mod tests {
use super::{
super::{Generator, Random},
Secret,
};
use std::str::FromStr;
use super::super::{Random, Generator};
use super::Secret;
#[test]
fn multiplicating_secret_inversion_with_secret_gives_one() {
@ -258,7 +274,11 @@ mod tests {
let mut inversion = secret.clone();
inversion.inv().unwrap();
inversion.mul(&secret).unwrap();
assert_eq!(inversion, Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap());
assert_eq!(
inversion,
Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001")
.unwrap()
);
}
#[test]
@ -276,7 +296,11 @@ mod tests {
let mut pow0 = secret.clone();
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();
pow1.pow(1).unwrap();

View File

@ -14,16 +14,26 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::ops::{Deref, DerefMut};
use std::cmp::PartialEq;
use std::fmt;
use std::str::FromStr;
use std::hash::{Hash, Hasher};
use secp256k1::{Message as SecpMessage, RecoverableSignature, RecoveryId, Error as SecpError};
use secp256k1::key::{SecretKey, PublicKey};
use rustc_hex::{ToHex, FromHex};
use ethereum_types::{H520, H256};
use {Secret, Public, SECP256K1, Error, Message, public_to_address, Address};
use ethereum_types::{H256, H520};
use public_to_address;
use rustc_hex::{FromHex, ToHex};
use secp256k1::{
key::{PublicKey, SecretKey},
Error as SecpError, Message as SecpMessage, RecoverableSignature, RecoveryId,
};
use std::{
cmp::PartialEq,
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
#[repr(C)]
@ -76,16 +86,19 @@ impl Signature {
/// Check if this is a "low" signature.
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.
pub fn is_valid(&self) -> bool {
self.v() <= 1 &&
H256::from_slice(self.r()) < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() &&
H256::from_slice(self.r()) >= 1.into() &&
H256::from_slice(self.s()) < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() &&
H256::from_slice(self.s()) >= 1.into()
self.v() <= 1
&& H256::from_slice(self.r())
< "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into()
&& H256::from_slice(self.r()) >= 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`.
impl Eq for Signature { }
impl Eq for Signature {}
// also manual for the same reason, but the pretty printing might be useful.
impl fmt::Debug for Signature {
@ -126,8 +139,8 @@ impl FromStr for Signature {
let mut data = [0; 65];
data.copy_from_slice(&hex[0..65]);
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))
}
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 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 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) {
Ok(_) => Ok(true),
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 recovered_address = public_to_address(&public);
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> {
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 serialized = pubkey.serialize_vec(context, false);
@ -239,9 +268,11 @@ pub fn recover(signature: &Signature, message: &Message) -> Result<Public, Error
#[cfg(test)]
mod tests {
use super::{recover, sign, verify_address, verify_public, Signature};
use std::str::FromStr;
use {Generator, Random, Message};
use super::{sign, verify_public, verify_address, recover, Signature};
use Generator;
use Message;
use Random;
#[test]
fn vrs_conversion() {

View File

@ -14,12 +14,10 @@
// You should have received a copy of the GNU General Public License
// 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 std::{cmp, collections::VecDeque, sync::Arc, thread};
use ethstore::{ethkey::Password, PresaleWallet, Error};
use ethstore::{ethkey::Password, Error, PresaleWallet};
use num_cpus;
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 {
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(())
@ -57,9 +57,9 @@ fn look_for_password(passwords: Arc<Mutex<VecDeque<Password>>>, wallet: PresaleW
println!("Found password: {}", pass.as_str());
passwords.lock().clear();
return;
},
}
_ if counter % 100 == 0 => print!("."),
_ => {},
_ => {}
}
}
}

View File

@ -28,14 +28,15 @@ extern crate env_logger;
#[macro_use]
extern crate serde_derive;
use std::collections::VecDeque;
use std::io::Read;
use std::{env, process, fs, fmt};
use std::{collections::VecDeque, env, fmt, fs, io::Read, process};
use docopt::Docopt;
use ethstore::accounts_dir::{KeyDirectory, RootDiskDirectory};
use ethstore::ethkey::{Address, Password};
use ethstore::{EthStore, SimpleSecretStore, SecretStore, import_accounts, PresaleWallet, SecretVaultRef, StoreAccountRef};
use ethstore::{
accounts_dir::{KeyDirectory, RootDiskDirectory},
ethkey::{Address, Password},
import_accounts, EthStore, PresaleWallet, SecretStore, SecretVaultRef, SimpleSecretStore,
StoreAccountRef,
};
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 path = dir::parity(chain);
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()))
}
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)? {
SecretVaultRef::Root => Ok(StoreAccountRef::root(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 {
accounts.iter()
accounts
.iter()
.enumerate()
.map(|(i, a)| format!("{:2}: 0x{:x}", i, a))
.collect::<Vec<String>>()
@ -208,32 +214,47 @@ fn format_vaults(vaults: &[String]) -> String {
}
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();
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
let _ = password.pop();
Ok(password.into())
}
fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item=S>, S: AsRef<str> {
let args: Args = Docopt::new(USAGE)
.and_then(|d| d.argv(command).deserialize())?;
fn execute<S, I>(command: I) -> Result<String, Error>
where
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)?)?;
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 vault_ref = open_args_vault(&store, &args)?;
let account_ref = store.insert_account(vault_ref, secret, &password)?;
Ok(format!("0x{:x}", account_ref.address))
} 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 new_pwd = load_password(&args.arg_new_pwd)?;
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))
} else if args.cmd_list {
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 {
let password = match args.arg_password.as_ref() {
"" => None,
_ => Some(load_password(&args.arg_password)?)
_ => Some(load_password(&args.arg_password)?),
};
let src = key_dir(&args.flag_src, password)?;
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))
} else if args.cmd_find_wallet_pass {
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)?;
Ok(format!("Password not found."))
} 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 account_ref = open_args_vault_account(&store, address, &args)?;
let ok = store.remove_account(&account_ref, &password).is_ok();
Ok(format!("{}", ok))
} else if args.cmd_sign {
let address = args.arg_address.parse().map_err(|_| ethstore::Error::InvalidAccount)?;
let message = args.arg_message.parse().map_err(|_| ethstore::Error::InvalidMessage)?;
let address = args
.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 account_ref = open_args_vault_account(&store, address, &args)?;
let signature = store.sign(&account_ref, &password, &message)?;
Ok(format!("0x{}", signature))
} 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 account_ref = open_args_vault_account(&store, address, &args)?;
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)?;
Ok("OK".to_owned())
} 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 account_ref = open_args_vault_account(&store, address, &args)?;
store.open_vault(&args.arg_vault, &password)?;
store.change_account_vault(SecretVaultRef::Vault(args.arg_vault), account_ref)?;
Ok("OK".to_owned())
} 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)?;
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())
} else {
Ok(format!("{}", USAGE))
}
};
}

View File

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

View File

@ -28,16 +28,14 @@ pub enum Cipher {
impl From<json::Aes128Ctr> for Aes128Ctr {
fn from(json: json::Aes128Ctr) -> Self {
Aes128Ctr {
iv: json.iv.into()
}
Aes128Ctr { iv: json.iv.into() }
}
}
impl Into<json::Aes128Ctr> for Aes128Ctr {
fn into(self) -> 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
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::str;
use std::num::NonZeroU32;
use account::{Aes128Ctr, Cipher, Kdf, Pbkdf2, Prf};
use crypto::{self, Keccak256};
use ethkey::{Password, Secret};
use {json, Error, crypto};
use crypto::Keccak256;
use json;
use random::Random;
use smallvec::SmallVec;
use account::{Cipher, Kdf, Aes128Ctr, Pbkdf2, Prf};
use std::{num::NonZeroU32, str};
use Error;
/// Encrypted data
#[derive(Debug, PartialEq, Clone)]
@ -74,12 +74,20 @@ impl From<Crypto> for String {
impl Crypto {
/// 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)
}
/// 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 iv: [u8; 16] = Random::random();
@ -100,9 +108,7 @@ impl Crypto {
let mac = crypto::derive_mac(&derived_right_bits, &*ciphertext).keccak256();
Ok(Crypto {
cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: iv,
}),
cipher: Cipher::Aes128Ctr(Aes128Ctr { iv: iv }),
ciphertext: ciphertext.into_vec(),
kdf: Kdf::Pbkdf2(Pbkdf2 {
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> {
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::Scrypt(ref params) => crypto::scrypt::derive_key(password.as_bytes(), &params.salt, params.n, params.p, params.r)?,
Kdf::Pbkdf2(ref params) => {
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();
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]);
@ -150,17 +164,22 @@ impl Crypto {
debug_assert!(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())
},
}
}
}
}
#[cfg(test)]
mod tests {
use ethkey::{Generator, Random};
use super::{Crypto, Error, NonZeroU32};
use ethkey::{Generator, Random};
lazy_static! {
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed");
@ -178,8 +197,12 @@ mod tests {
#[test]
fn crypto_with_secret_invalid_password() {
let keypair = Random.generate().unwrap();
let crypto = Crypto::with_secret(keypair.secret(), &"this is sparta".into(), *ITERATIONS).unwrap();
assert_matches!(crypto.secret(&"this is sparta!".into()), Err(Error::InvalidPassword))
let crypto =
Crypto::with_secret(keypair.secret(), &"this is sparta".into(), *ITERATIONS).unwrap();
assert_matches!(
crypto.secret(&"this is sparta!".into()),
Err(Error::InvalidPassword)
)
}
#[test]

View File

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

View File

@ -14,13 +14,15 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use ethkey::{self, KeyPair, sign, Address, Password, Signature, Message, Public, Secret};
use ethkey::crypto::ecdh::agree;
use {json, Error};
use super::crypto::Crypto;
use account::Version;
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 Error;
/// Account representation.
#[derive(Debug, PartialEq, Clone)]
@ -62,7 +64,7 @@ impl SafeAccount {
password: &Password,
iterations: NonZeroU32,
name: String,
meta: String
meta: String,
) -> Result<Self, crypto::Error> {
Ok(SafeAccount {
id: id,
@ -81,16 +83,25 @@ impl SafeAccount {
/// 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.
/// 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 address = match (password, &json.address) {
(None, Some(json_address)) => json_address.into(),
(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) => {
let derived_address = KeyPair::from_secret(
crypto.secret(&password).map_err(|_| Error::InvalidPassword)?
)?.address();
crypto
.secret(&password)
.map_err(|_| Error::InvalidPassword)?,
)?
.address();
match json_address {
Some(json_address) => {
@ -99,8 +110,8 @@ impl SafeAccount {
warn!("Detected address mismatch when opening an account. Derived: {:?}, in json got: {:?}",
derived_address, json_address);
}
},
_ => {},
}
_ => {}
}
derived_address
}
@ -120,29 +131,44 @@ impl SafeAccount {
/// 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
/// 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_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,
version: json.version,
crypto: json.crypto,
address: Some(meta_plain.address),
name: meta_plain.name,
meta: meta_plain.meta,
}, filename, &None)
},
filename,
&None,
)
}
/// 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 {
address: self.address.into(),
name: Some(self.name),
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)?;
Ok(json::VaultKeyFile {
@ -160,7 +186,12 @@ impl SafeAccount {
}
/// 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)?;
ethkey::crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
}
@ -178,7 +209,12 @@ impl SafeAccount {
}
/// 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 result = SafeAccount {
id: self.id.clone(),
@ -200,20 +236,26 @@ impl SafeAccount {
#[cfg(test)]
mod tests {
use ethkey::{Generator, Random, verify_public, Message};
use super::{SafeAccount, NonZeroU32};
use super::{NonZeroU32, SafeAccount};
use ethkey::{verify_public, Generator, Message, Random};
lazy_static! {
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed");
}
#[test]
fn sign_and_verify_public() {
let keypair = Random.generate().unwrap();
let password = "hello world".into();
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();
assert!(verify_public(keypair.public(), &signature, &message).unwrap());
}
@ -224,8 +266,18 @@ mod tests {
let first_password = "hello world".into();
let sec_password = "this is sparta".into();
let message = Message::default();
let account = SafeAccount::create(&keypair, [0u8; 16], &first_password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()).unwrap();
let new_account = account.change_password(&first_password, &sec_password, *ITERATIONS).unwrap();
let account = SafeAccount::create(
&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(&sec_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
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::{fs, io};
use std::io::Write;
use std::path::{PathBuf, Path};
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 super::{
vault::{VaultDiskDirectory, VAULT_FILE_NAME},
KeyDirectory, VaultKey, VaultKeyDirectory, VaultKeyDirectoryProvider,
};
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] = &[
"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.
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 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() {
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);
@ -106,14 +117,21 @@ pub type RootDiskDirectory = DiskDirectory<DiskKeyFileManager>;
/// Disk directory key file manager
pub trait KeyFileManager: Send + Sync {
/// 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
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
pub struct DiskDirectory<T> where T: KeyFileManager {
pub struct DiskDirectory<T>
where
T: KeyFileManager,
{
path: PathBuf,
key_manager: T,
}
@ -125,7 +143,10 @@ pub struct DiskKeyFileManager {
}
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)?;
Ok(Self::at(path))
}
@ -135,14 +156,23 @@ impl RootDiskDirectory {
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())
}
}
impl<T> DiskDirectory<T> where T: KeyFileManager {
impl<T> DiskDirectory<T>
where
T: KeyFileManager,
{
/// 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 {
path: path.as_ref().to_path_buf(),
key_manager: key_manager,
@ -164,13 +194,11 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {
!IGNORED_FILES.contains(&&*name)
})
.map(|entry| entry.path())
.collect::<Vec<PathBuf>>()
)
.collect::<Vec<PathBuf>>())
}
pub fn files_hash(&self) -> Result<u64, Error> {
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
use std::{collections::hash_map::DefaultHasher, hash::Hasher};
let mut hasher = DefaultHasher::new();
let files = self.files()?;
@ -183,7 +211,10 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {
fn last_modification_date(&self) -> Result<u64, Error> {
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);
Ok(timestamp)
}
@ -196,7 +227,12 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {
Ok(paths
.into_iter()
.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())
.map_err(Into::into)
.and_then(|file| self.key_manager.read(filename, file))
@ -207,13 +243,17 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {
.map(|account| (path, account))
.ok()
})
.collect()
)
.collect())
}
/// 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.
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 {
filename = find_unique_filename_using_random_suffix(&self.path, &filename)?;
}
@ -235,7 +275,9 @@ impl<T> DiskDirectory<T> where T: KeyFileManager {
};
// 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.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> {
let accounts = self.files_content()?
let accounts = self
.files_content()?
.into_iter()
.map(|(_, account)| account)
.collect();
@ -273,18 +319,21 @@ impl<T> KeyDirectory for DiskDirectory<T> where T: KeyFileManager {
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
// enumerate all entries in keystore
// and find entry with given address
let to_remove = self.files_content()?
let to_remove = self
.files_content()?
.into_iter()
.find(|&(_, ref acc)| acc.id == account.id && acc.address == account.address);
// remove it
match to_remove {
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> {
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> {
let vault_dir = VaultDiskDirectory::create(&self.path, name, key)?;
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();
vault_file_path.push(VAULT_FILE_NAME);
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 {
None
}
@ -327,26 +381,36 @@ impl<T> VaultKeyDirectoryProvider for DiskDirectory<T> where T: KeyFileManager {
}
impl KeyFileManager for DiskKeyFileManager {
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where T: io::Read {
let key_file = json::KeyFile::load(reader).map_err(|e| Error::Custom(format!("{:?}", e)))?;
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error>
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)
}
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
// => remove vault field from meta
account.meta = json::remove_vault_name_from_json_meta(&account.meta)
.map_err(|err| Error::Custom(format!("{:?}", err)))?;
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 {
// build file path
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))
})
}
@ -355,12 +419,11 @@ fn account_filename(account: &SafeAccount) -> String {
mod test {
extern crate tempdir;
use std::{env, fs};
use std::num::NonZeroU32;
use self::tempdir::TempDir;
use super::{KeyDirectory, RootDiskDirectory, VaultKey};
use account::SafeAccount;
use ethkey::{Random, Generator};
use self::tempdir::TempDir;
use ethkey::{Generator, Random};
use std::{env, fs, num::NonZeroU32};
lazy_static! {
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed");
@ -376,12 +439,22 @@ mod test {
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
// 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());
// then
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
let _ = fs::remove_dir_all(dir);
@ -397,14 +470,36 @@ mod test {
let directory = RootDiskDirectory::create(dir.clone()).unwrap();
// 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 dedup = true;
directory.insert_with_filename(account.clone(), "foo".to_string(), dedup).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();
directory
.insert_with_filename(account.clone(), "foo".to_string(), dedup)
.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
// the first file should have the original names
@ -433,7 +528,10 @@ mod test {
// and when
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
assert!(vault.is_ok());
@ -441,7 +539,10 @@ mod test {
assert!(after_root_items_count > before_root_items_count);
// 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
assert!(vault.is_ok());
@ -459,8 +560,12 @@ mod test {
let directory = RootDiskDirectory::create(&temp_path).unwrap();
let vault_provider = directory.as_vault_provider().unwrap();
let iter = NonZeroU32::new(1).expect("1 > 0; qed");
vault_provider.create("vault1", VaultKey::new(&"password1".into(), iter)).unwrap();
vault_provider.create("vault2", VaultKey::new(&"password2".into(), iter)).unwrap();
vault_provider
.create("vault1", VaultKey::new(&"password1".into(), iter))
.unwrap();
vault_provider
.create("vault2", VaultKey::new(&"password2".into(), iter))
.unwrap();
// then
let vaults = vault_provider.list_vaults().unwrap();
@ -474,19 +579,32 @@ mod test {
let temp_path = TempDir::new("").unwrap();
let directory = RootDiskDirectory::create(&temp_path).unwrap();
let hash = directory.files_hash().expect("Files hash should be calculated ok");
assert_eq!(
hash,
15130871412783076140
);
let hash = directory
.files_hash()
.expect("Files hash should be calculated ok");
assert_eq!(hash, 15130871412783076140);
let keypair = Random.generate().unwrap();
let password = "test pass".into();
let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned());
directory.insert(account.unwrap()).expect("Account should be inserted ok");
let account = SafeAccount::create(
&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
// 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 itertools;
use parking_lot::RwLock;
use std::collections::HashMap;
use {SafeAccount, Error};
use super::KeyDirectory;
use Error;
use SafeAccount;
/// Accounts in-memory storage.
#[derive(Default)]
@ -68,7 +69,9 @@ impl KeyDirectory for MemoryDirectory {
fn unique_repr(&self) -> Result<u64, Error> {
let mut val = 0u64;
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)
}
}

View File

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

View File

@ -14,14 +14,20 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::{fs, io};
use std::path::{PathBuf, Path};
use parking_lot::Mutex;
use {json, SafeAccount, Error};
use super::{
super::account::Crypto,
disk::{self, DiskDirectory, KeyFileManager},
KeyDirectory, SetKeyError, VaultKey, VaultKeyDirectory,
};
use crypto::Keccak256;
use super::super::account::Crypto;
use super::{KeyDirectory, VaultKeyDirectory, VaultKey, SetKeyError};
use super::disk::{self, DiskDirectory, KeyFileManager};
use json;
use parking_lot::Mutex;
use std::{
fs, io,
path::{Path, PathBuf},
};
use Error;
use SafeAccount;
/// Name of vault metadata file
pub const VAULT_FILE_NAME: &'static str = "vault.json";
@ -40,7 +46,10 @@ pub struct VaultKeyFileManager {
impl VaultDiskDirectory {
/// 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
let vault_dir_path = make_vault_dir_path(root, name, true)?;
if vault_dir_path.exists() {
@ -55,11 +64,17 @@ impl VaultDiskDirectory {
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
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
let vault_dir_path = make_vault_dir_path(root, name, true)?;
if !vault_dir_path.is_dir() {
@ -69,11 +84,17 @@ impl VaultDiskDirectory {
// check that passed key matches vault file
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
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
let vault_dir_path = make_vault_dir_path(root, name, true)?;
if !vault_dir_path.is_dir() {
@ -85,7 +106,9 @@ impl VaultDiskDirectory {
}
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 name = self.name();
@ -105,7 +128,9 @@ impl VaultDiskDirectory {
fn copy_to_vault(&self, vault: &VaultDiskDirectory) -> Result<(), Error> {
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)?;
}
@ -113,7 +138,9 @@ impl VaultDiskDirectory {
}
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)
}
}
@ -132,18 +159,30 @@ impl VaultKeyDirectory for VaultDiskDirectory {
}
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 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();
let temp_vault = VaultDiskDirectory::create_temp_vault(self, new_key.clone())
.map_err(|err| SetKeyError::NonFatalOld(err))?;
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
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
source_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())
.map_err(|err| {
// ignore error, as we already processing error
@ -155,7 +194,9 @@ impl VaultKeyDirectory for VaultDiskDirectory {
// original vault content has already been partially replaced
// => when error or crash happens here, we can't do anything
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);
target_path.set_file_name(&filename);
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);
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 {
@ -173,7 +216,9 @@ impl VaultKeyDirectory for VaultDiskDirectory {
fn set_meta(&self, meta: &str) -> Result<(), Error> {
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)?;
*key_manager.meta.lock() = meta.to_owned();
Ok(())
@ -191,26 +236,40 @@ impl VaultKeyFileManager {
}
impl KeyFileManager for VaultKeyFileManager {
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error> where 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())?;
fn read<T>(&self, filename: Option<String>, reader: T) -> Result<SafeAccount, Error>
where
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)
.map_err(|err| Error::Custom(format!("{:?}", err)))?;
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)
.map_err(|err| Error::Custom(format!("{:?}", err)))?;
let vault_file: json::VaultKeyFile = account.into_vault_file(self.key.iterations, &self.key.password)?;
vault_file.write(writer).map_err(|e| Error::Custom(format!("{:?}", e)))
let vault_file: json::VaultKeyFile =
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
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
if check_name && !check_vault_name(name) {
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.
fn check_vault_name(name: &str) -> bool {
!name.is_empty()
&& name.chars()
.all(|c| c.is_alphanumeric()
|| c.is_whitespace()
|| c == '-' || c == '_')
&& name
.chars()
.all(|c| c.is_alphanumeric() || c.is_whitespace() || c == '-' || c == '_')
}
/// 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 crypto = Crypto::with_plain(&password_hash, &key.password, key.iterations)?;
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);
// 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(),
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);
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
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();
vault_file_path.push(VAULT_FILE_NAME);
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_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 {
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 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! {
static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(1024).expect("1024 > 0; qed");
@ -318,8 +386,14 @@ mod test {
fn make_vault_dir_path_succeeds() {
use std::path::Path;
assert_eq!(&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*"));
assert_eq!(
&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]
@ -357,7 +431,9 @@ mod test {
vault_file_path.push(VAULT_FILE_NAME);
{
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
@ -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 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

View File

@ -14,11 +14,9 @@
// You should have received a copy of the GNU General Public License
// 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 ethkey::DerivationError;
use ethkey::{self, DerivationError, Error as EthKeyError};
use std::{fmt, io::Error as IoError};
/// Account-related errors.
#[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
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::collections::HashSet;
use std::path::Path;
use std::fs;
use std::{collections::HashSet, fs, path::Path};
use ethkey::Address;
use accounts_dir::{KeyDirectory, RootDiskDirectory, DiskKeyFileManager, KeyFileManager};
use accounts_dir::{DiskKeyFileManager, KeyDirectory, KeyFileManager, RootDiskDirectory};
use dir;
use ethkey::Address;
use Error;
/// Import an account from a file.
pub fn import_account(path: &Path, dst: &KeyDirectory) -> Result<Address, Error> {
let key_manager = DiskKeyFileManager::default();
let existing_accounts = dst.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 existing_accounts = dst
.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)
.map_err(Into::into)
.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.
pub fn import_accounts(src: &KeyDirectory, dst: &KeyDirectory) -> Result<Vec<Address>, Error> {
let accounts = src.load()?;
let existing_accounts = dst.load()?.into_iter()
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))
.map(|a| {
let address = a.address.clone();
dst.insert(a)?;
Ok(address)
}).collect()
})
.collect()
}
/// 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`.
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 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| desired.contains(&a.address))
.map(|a| {
let address = a.address.clone();
dst.insert(a)?;
Ok(address)
}).collect()
})
.collect()
}

View File

@ -14,10 +14,9 @@
// You should have received a copy of the GNU General Public License
// 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 serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::de::Error;
use rustc_hex::{ToHex, FromHex, FromHexError};
#[derive(Debug, PartialEq)]
pub struct Bytes(Vec<u8>);
@ -32,17 +31,22 @@ impl ops::Deref for Bytes {
impl<'a> Deserialize<'a> for Bytes {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a>
where
D: Deserializer<'a>,
{
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))
}
}
impl Serialize for Bytes {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
where
S: Serializer,
{
serializer.serialize_str(&self.0.to_hex())
}
}
@ -57,7 +61,11 @@ impl str::FromStr for Bytes {
impl From<&'static str> for Bytes {
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
// 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 serde::{
de::{Error as SerdeError, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::fmt;
#[derive(Debug, PartialEq)]
pub enum CipherSer {
@ -26,7 +28,9 @@ pub enum CipherSer {
impl Serialize for CipherSer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
where
S: Serializer,
{
match *self {
CipherSer::Aes128Ctr => serializer.serialize_str("aes-128-ctr"),
}
@ -35,7 +39,9 @@ impl Serialize for CipherSer {
impl<'a> Deserialize<'a> for CipherSer {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> {
where
D: Deserializer<'a>,
{
deserializer.deserialize_any(CipherSerVisitor)
}
}
@ -49,14 +55,20 @@ impl<'a> Visitor<'a> for CipherSerVisitor {
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 {
"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())
}
}
@ -73,7 +85,9 @@ pub enum CipherSerParams {
impl Serialize for CipherSerParams {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
where
S: Serializer,
{
match *self {
CipherSerParams::Aes128Ctr(ref params) => params.serialize(serializer),
}
@ -82,7 +96,9 @@ impl Serialize for CipherSerParams {
impl<'a> Deserialize<'a> for CipherSerParams {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> {
where
D: Deserializer<'a>,
{
Aes128Ctr::deserialize(deserializer)
.map(CipherSerParams::Aes128Ctr)
.map_err(|_| Error::InvalidCipherParams)

View File

@ -14,12 +14,14 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::{fmt, str};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::ser::SerializeStruct;
use serde::de::{Visitor, MapAccess, Error};
use super::{Bytes, Cipher, CipherSer, CipherSerParams, Kdf, KdfSer, KdfSerParams, H256};
use serde::{
de::{Error, MapAccess, Visitor},
ser::SerializeStruct,
Deserialize, Deserializer, Serialize, Serializer,
};
use serde_json;
use super::{Cipher, CipherSer, CipherSerParams, Kdf, KdfSer, KdfSerParams, H256, Bytes};
use std::{fmt, str};
pub type CipherText = Bytes;
@ -41,7 +43,8 @@ impl str::FromStr for Crypto {
impl From<Crypto> for String {
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 {
fn deserialize<D>(deserializer: D) -> Result<CryptoField, D::Error>
where D: Deserializer<'a>
where
D: Deserializer<'a>,
{
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>
where E: Error
where
E: Error,
{
match value {
"cipher" => Ok(CryptoField::Cipher),
@ -90,7 +95,8 @@ impl<'a> Visitor<'a> for CryptoFieldVisitor {
impl<'a> Deserialize<'a> for Crypto {
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"];
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>
where V: MapAccess<'a>
where
V: MapAccess<'a>,
{
let mut cipher = None;
let mut cipherparams = None;
@ -118,20 +125,36 @@ impl<'a> Visitor<'a> for CryptoVisitor {
loop {
match visitor.next_key()? {
Some(CryptoField::Cipher) => { cipher = Some(visitor.next_value()?); }
Some(CryptoField::CipherParams) => { cipherparams = 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()?); }
Some(CryptoField::Cipher) => {
cipher = Some(visitor.next_value()?);
}
Some(CryptoField::CipherParams) => {
cipherparams = 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)
Some(CryptoField::Version) => { visitor.next_value().unwrap_or(()) }
None => { break; }
Some(CryptoField::Version) => visitor.next_value().unwrap_or(()),
None => {
break;
}
}
}
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")),
(Some(_), None) => return Err(V::Error::missing_field("cipherparams")),
};
@ -167,25 +190,26 @@ impl<'a> Visitor<'a> for CryptoVisitor {
impl Serialize for Crypto {
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)?;
match self.cipher {
Cipher::Aes128Ctr(ref params) => {
crypto.serialize_field("cipher", &CipherSer::Aes128Ctr)?;
crypto.serialize_field("cipherparams", params)?;
},
}
}
crypto.serialize_field("ciphertext", &self.ciphertext)?;
match self.kdf {
Kdf::Pbkdf2(ref params) => {
crypto.serialize_field("kdf", &KdfSer::Pbkdf2)?;
crypto.serialize_field("kdfparams", params)?;
},
}
Kdf::Scrypt(ref params) => {
crypto.serialize_field("kdf", &KdfSer::Scrypt)?;
crypto.serialize_field("kdfparams", params)?;
},
}
}
crypto.serialize_field("mac", &self.mac)?;

View File

@ -14,11 +14,13 @@
// You should have received a copy of the GNU General Public License
// 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 rustc_hex::{FromHex, ToHex};
use serde::{
de::{Error as SerdeError, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::{fmt, ops, str};
macro_rules! impl_hash {
($name: ident, $size: expr) => {
@ -49,14 +51,18 @@ macro_rules! impl_hash {
impl Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
where
S: Serializer,
{
serializer.serialize_str(&self.0.to_hex())
}
}
impl<'a> Deserialize<'a> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> {
where
D: Deserializer<'a>,
{
struct HashVisitor;
impl<'b> Visitor<'b> for HashVisitor {
@ -66,11 +72,17 @@ macro_rules! impl_hash {
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)
}
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())
}
}
@ -96,7 +108,11 @@ macro_rules! impl_hash {
impl From<&'static str> for $name {
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
}
}
}
};
}
impl_hash!(H128, 16);

View File

@ -15,11 +15,13 @@
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
//! 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 rustc_hex::{FromHex, ToHex};
use serde::{
de::{Error as SerdeError, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::{fmt, str};
/// Universaly unique identifier.
#[derive(Debug, PartialEq)]
@ -38,7 +40,11 @@ impl<'a> Into<String> for &'a Uuid {
let d3 = &self.0[6..8];
let d4 = &self.0[8..10];
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 {
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 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
where
S: Serializer,
{
let s: String = self.into();
serializer.serialize_str(&s)
}
@ -110,7 +122,9 @@ impl Serialize for Uuid {
impl<'a> Deserialize<'a> for Uuid {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> {
where
D: Deserializer<'a>,
{
deserializer.deserialize_any(UuidVisitor)
}
}
@ -124,11 +138,17 @@ impl<'a> Visitor<'a> for UuidVisitor {
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)
}
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())
}
}
@ -140,7 +160,13 @@ mod tests {
#[test]
fn uuid_from_str() {
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]

View File

@ -14,11 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::fmt;
use std::num::NonZeroU32;
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::{Visitor, Error as SerdeError};
use super::{Error, Bytes};
use super::{Bytes, Error};
use serde::{
de::{Error as SerdeError, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::{fmt, num::NonZeroU32};
#[derive(Debug, PartialEq)]
pub enum KdfSer {
@ -28,7 +29,9 @@ pub enum KdfSer {
impl Serialize for KdfSer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
where
S: Serializer,
{
match *self {
KdfSer::Pbkdf2 => serializer.serialize_str("pbkdf2"),
KdfSer::Scrypt => serializer.serialize_str("scrypt"),
@ -38,7 +41,9 @@ impl Serialize for KdfSer {
impl<'a> Deserialize<'a> for KdfSer {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> {
where
D: Deserializer<'a>,
{
deserializer.deserialize_any(KdfSerVisitor)
}
}
@ -52,15 +57,21 @@ impl<'a> Visitor<'a> for KdfSerVisitor {
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 {
"pbkdf2" => Ok(KdfSer::Pbkdf2),
"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())
}
}
@ -72,7 +83,9 @@ pub enum Prf {
impl Serialize for Prf {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
where
S: Serializer,
{
match *self {
Prf::HmacSha256 => serializer.serialize_str("hmac-sha256"),
}
@ -81,7 +94,9 @@ impl Serialize for Prf {
impl<'a> Deserialize<'a> for Prf {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> {
where
D: Deserializer<'a>,
{
deserializer.deserialize_any(PrfVisitor)
}
}
@ -95,14 +110,20 @@ impl<'a> Visitor<'a> for PrfVisitor {
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 {
"hmac-sha256" => Ok(Prf::HmacSha256),
_ => 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())
}
}
@ -132,7 +153,9 @@ pub enum KdfSerParams {
impl Serialize for KdfSerParams {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
where
S: Serializer,
{
match *self {
KdfSerParams::Pbkdf2(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 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a> {
use serde_json::{Value, from_value};
where
D: Deserializer<'a>,
{
use serde_json::{from_value, Value};
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))
.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
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::fmt;
use std::io::{Read, Write};
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::{Error, Visitor, MapAccess, DeserializeOwned};
use super::{Crypto, Uuid, Version, H160};
use serde::{
de::{DeserializeOwned, Error, MapAccess, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use serde_json;
use super::{Uuid, Version, Crypto, H160};
use std::{
fmt,
io::{Read, Write},
};
/// Public opaque type representing serializable `KeyFile`.
#[derive(Debug, PartialEq)]
pub struct OpaqueKeyFile {
key_file: KeyFile
key_file: KeyFile,
}
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,
{
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 {
OpaqueKeyFile { key_file: val.into() }
OpaqueKeyFile {
key_file: val.into(),
}
}
}
@ -62,7 +72,8 @@ enum KeyFileField {
impl<'a> Deserialize<'a> for KeyFileField {
fn deserialize<D>(deserializer: D) -> Result<KeyFileField, D::Error>
where D: Deserializer<'a>
where
D: Deserializer<'a>,
{
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>
where E: Error
where
E: Error,
{
match value {
"id" => Ok(KeyFileField::Id),
@ -95,22 +107,25 @@ impl<'a> Visitor<'a> for KeyFileFieldVisitor {
impl<'a> Deserialize<'a> for KeyFile {
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"];
deserializer.deserialize_struct("KeyFile", FIELDS, KeyFileVisitor)
}
}
fn none_if_empty<'a, T>(v: Option<serde_json::Value>) -> Option<T> where
T: DeserializeOwned
fn none_if_empty<'a, T>(v: Option<serde_json::Value>) -> Option<T>
where
T: DeserializeOwned,
{
v.and_then(|v| if v.is_null() {
v.and_then(|v| {
if v.is_null() {
None
} else {
serde_json::from_value(v).ok()
}
})
}
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>
where V: MapAccess<'a>
where
V: MapAccess<'a>,
{
let mut id = None;
let mut version = None;
@ -133,13 +149,23 @@ impl<'a> Visitor<'a> for KeyFileVisitor {
loop {
match visitor.next_key()? {
Some(KeyFileField::Id) => { id = Some(visitor.next_value()?); }
Some(KeyFileField::Version) => { version = Some(visitor.next_value()?); }
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; }
Some(KeyFileField::Id) => {
id = Some(visitor.next_value()?);
}
Some(KeyFileField::Version) => {
version = Some(visitor.next_value()?);
}
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 {
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)
}
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)
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use json::{Aes128Ctr, Cipher, Crypto, Kdf, KeyFile, Scrypt, Uuid, Version};
use serde_json;
use json::{KeyFile, Uuid, Version, Crypto, Cipher, Aes128Ctr, Kdf, Scrypt};
use std::str::FromStr;
#[test]
fn basic_keyfile() {
@ -222,7 +254,8 @@ mod tests {
cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
}),
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc"
.into(),
kdf: Kdf::Scrypt(Scrypt {
n: 262144,
dklen: 32,
@ -273,7 +306,8 @@ mod tests {
cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
}),
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc"
.into(),
kdf: Kdf::Scrypt(Scrypt {
n: 262144,
dklen: 32,
@ -301,7 +335,8 @@ mod tests {
cipher: Cipher::Aes128Ctr(Aes128Ctr {
iv: "b5a7ec855ec9e2c405371356855fec83".into(),
}),
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc".into(),
ciphertext: "7203da0676d141b138cd7f8e1a4365f59cc1aa6978dc5443f364ca943d7cb4bc"
.into(),
kdf: Kdf::Scrypt(Scrypt {
n: 262144,
dklen: 32,

View File

@ -29,15 +29,20 @@ mod vault_file;
mod vault_key_file;
mod version;
pub use self::bytes::Bytes;
pub use self::cipher::{Cipher, CipherSer, CipherSerParams, Aes128Ctr};
pub use self::crypto::{Crypto, CipherText};
pub use self::error::Error;
pub use self::hash::{H128, H160, H256};
pub use self::id::Uuid;
pub use self::kdf::{Kdf, KdfSer, Prf, Pbkdf2, Scrypt, KdfSerParams};
pub use self::key_file::{KeyFile, OpaqueKeyFile};
pub use self::presale::{PresaleWallet, Encseed};
pub use self::vault_file::VaultFile;
pub use self::vault_key_file::{VaultKeyFile, VaultKeyMeta, insert_vault_name_to_json_meta, remove_vault_name_from_json_meta};
pub use self::version::Version;
pub use self::{
bytes::Bytes,
cipher::{Aes128Ctr, Cipher, CipherSer, CipherSerParams},
crypto::{CipherText, Crypto},
error::Error,
hash::{H128, H160, H256},
id::Uuid,
kdf::{Kdf, KdfSer, KdfSerParams, Pbkdf2, Prf, Scrypt},
key_file::{KeyFile, OpaqueKeyFile},
presale::{Encseed, PresaleWallet},
vault_file::VaultFile,
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
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::io::Read;
use super::{Bytes, H160};
use serde_json;
use super::{H160, Bytes};
use std::io::Read;
pub type Encseed = Bytes;
@ -28,16 +28,19 @@ pub struct 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)
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use serde_json;
use json::{PresaleWallet, H160};
use serde_json;
use std::str::FromStr;
#[test]
fn presale_wallet() {

View File

@ -14,9 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::io::{Read, Write};
use serde_json;
use super::Crypto;
use serde_json;
use std::io::{Read, Write};
/// Vault meta file
#[derive(Debug, PartialEq, Serialize, Deserialize)]
@ -28,19 +28,25 @@ pub struct 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)
}
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)
}
}
#[cfg(test)]
mod test {
use json::{Aes128Ctr, Cipher, Crypto, Kdf, Pbkdf2, Prf, VaultFile};
use serde_json;
use json::{VaultFile, Crypto, Cipher, Aes128Ctr, Kdf, Pbkdf2, Prf};
use std::num::NonZeroU32;
lazy_static! {

View File

@ -14,12 +14,10 @@
// You should have received a copy of the GNU General Public License
// 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_json;
use serde_json::value::Value;
use serde_json::error;
use super::{Uuid, Version, Crypto, H160};
use serde_json::{self, error, value::Value};
use std::io::{Read, Write};
/// Meta key name for vault field
const VAULT_NAME_META_KEY: &'static str = "vault";
@ -49,7 +47,10 @@ pub struct VaultKeyMeta {
}
/// 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() {
Value::Object(serde_json::Map::new())
} 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() {
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)
} 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);
serde_json::to_string(meta_obj)
} 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 {
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)
}
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)
}
}
@ -103,9 +117,11 @@ impl VaultKeyMeta {
#[cfg(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 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;
lazy_static! {
@ -153,8 +169,14 @@ mod test {
#[test]
fn vault_name_inserted_to_json_meta() {
assert_eq!(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"}"#);
assert_eq!(
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]
@ -165,8 +187,14 @@ mod test {
#[test]
fn vault_name_removed_from_json_meta() {
assert_eq!(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"]}"#);
assert_eq!(
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]

View File

@ -14,10 +14,12 @@
// You should have received a copy of the GNU General Public License
// 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 serde::{
de::{Error as SerdeError, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::fmt;
#[derive(Debug, PartialEq)]
pub enum Version {
@ -26,16 +28,20 @@ pub enum Version {
impl Serialize for Version {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
where
S: Serializer,
{
match *self {
Version::V3 => serializer.serialize_u64(3)
Version::V3 => serializer.serialize_u64(3),
}
}
}
impl<'a> Deserialize<'a> for Version {
fn deserialize<D>(deserializer: D) -> Result<Version, D::Error>
where D: Deserializer<'a> {
where
D: Deserializer<'a>,
{
deserializer.deserialize_any(VersionVisitor)
}
}
@ -49,10 +55,13 @@ impl<'a> Visitor<'a> for VersionVisitor {
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 {
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_json;
extern crate smallvec;
extern crate tempdir;
extern crate time;
extern crate tiny_keccak;
extern crate tempdir;
extern crate parity_crypto as crypto;
extern crate ethereum_types;
extern crate ethkey as _ethkey;
extern crate parity_crypto as crypto;
extern crate parity_wordlist;
#[macro_use]
@ -60,18 +60,20 @@ mod presale;
mod random;
mod secret_store;
pub use self::account::{SafeAccount, Crypto};
pub use self::error::Error;
pub use self::ethstore::{EthStore, EthMultiStore};
pub use self::import::{import_account, import_accounts, read_geth_accounts};
pub use self::json::OpaqueKeyFile as KeyFile;
pub use self::presale::PresaleWallet;
pub use self::secret_store::{
SecretVaultRef, StoreAccountRef, SimpleSecretStore, SecretStore,
Derivation, IndexDerivation,
pub use self::{
account::{Crypto, SafeAccount},
error::Error,
ethstore::{EthMultiStore, EthStore},
import::{import_account, import_accounts, read_geth_accounts},
json::OpaqueKeyFile as KeyFile,
parity_wordlist::random_phrase,
presale::PresaleWallet,
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.
pub struct OpaqueSecret(::ethkey::Secret);

View File

@ -14,13 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::fs;
use std::num::NonZeroU32;
use std::path::Path;
use crypto::{self, pbkdf2, Keccak256};
use ethkey::{Address, KeyPair, Password, Secret};
use json;
use ethkey::{Address, Secret, KeyPair, Password};
use crypto::{Keccak256, pbkdf2};
use {crypto, Error};
use std::{fs, num::NonZeroU32, path::Path};
use Error;
/// Pre-sale wallet.
pub struct PresaleWallet {
@ -47,10 +45,13 @@ impl From<json::PresaleWallet> for PresaleWallet {
impl PresaleWallet {
/// 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 presale = json::PresaleWallet::load(file)
.map_err(|e| Error::InvalidKeyFile(format!("{}", e)))?;
let presale =
json::PresaleWallet::load(file).map_err(|e| Error::InvalidKeyFile(format!("{}", e)))?;
Ok(PresaleWallet::from(presale))
}
@ -63,14 +64,15 @@ impl PresaleWallet {
pbkdf2::sha256(iter, salt, sec, &mut derived_key);
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)?;
let unpadded = &key[..len];
let secret = Secret::from_unsafe_slice(&unpadded.keccak256())?;
if let Ok(kp) = KeyPair::from_secret(secret) {
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
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use rand::{Rng, OsRng};
use rand::{OsRng, Rng};
pub trait Random {
fn random() -> Self where Self: Sized;
fn random() -> Self
where
Self: Sized;
}
impl Random for [u8; 16] {

View File

@ -14,13 +14,15 @@
// You should have received a copy of the GNU General Public License
// 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 ethkey::{Address, Message, Password, Public, Secret, Signature};
use json::{OpaqueKeyFile, Uuid};
use std::{
cmp::Ordering,
hash::{Hash, Hasher},
path::PathBuf,
};
use Error;
use OpaqueSecret;
/// Key directory reference
@ -43,7 +45,11 @@ pub struct StoreAccountRef {
impl PartialOrd for StoreAccountRef {
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
pub trait SimpleSecretStore: Send + Sync {
/// 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.
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.
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.
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.
fn remove_account(&self, account: &StoreAccountRef, password: &Password) -> Result<(), Error>;
/// 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.
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.
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.
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.
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.
fn accounts(&self) -> Result<Vec<StoreAccountRef>, Error>;
@ -95,7 +148,11 @@ pub trait SimpleSecretStore: Send + Sync {
/// Change vault password
fn change_vault_password(&self, name: &str, new_password: &Password) -> Result<(), Error>;
/// 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.
fn get_vault_meta(&self, name: &str) -> Result<String, Error>;
/// Set vault metadata string.
@ -104,21 +161,46 @@ pub trait SimpleSecretStore: Send + Sync {
/// Secret Store API
pub trait SecretStore: SimpleSecretStore {
/// 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.
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)?)
}
/// 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
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.
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.
fn test_password(&self, account: &StoreAccountRef, password: &Password) -> Result<bool, Error>;
@ -142,7 +224,12 @@ pub trait SecretStore: SimpleSecretStore {
/// Lists all found geth accounts.
fn list_geth_accounts(&self, testnet: bool) -> Vec<Address>;
/// 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 {

View File

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

View File

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

View File

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

View File

@ -17,26 +17,31 @@
//! 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.
use std::cmp::min;
use std::str::FromStr;
use std::sync::Arc;
use std::time::{Duration, Instant};
use std::fmt;
use std::{
cmp::min,
fmt,
str::FromStr,
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 hidapi;
use libusb;
use parking_lot::{Mutex, RwLock};
use semver::Version as FirmwareVersion;
use super::{WalletInfo, KeyPath, Device, DeviceDirection, Wallet, is_valid_hid_device};
const APDU_TAG: u8 = 0x05;
const APDU_CLA: u8 = 0xe0;
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 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
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 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
const LEDGER_VID: u16 = 0x2c97;
@ -48,8 +53,10 @@ const MAX_CHUNK_SIZE: usize = 255;
const HID_PACKET_SIZE: usize = 64 + HID_PREFIX_ZERO;
#[cfg(windows)] const HID_PREFIX_ZERO: usize = 1;
#[cfg(not(windows))] const HID_PREFIX_ZERO: usize = 0;
#[cfg(windows)]
const HID_PREFIX_ZERO: usize = 1;
#[cfg(not(windows))]
const HID_PREFIX_ZERO: usize = 0;
mod commands {
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::Impossible => write!(f, "Placeholder error"),
Error::NoDeviceArrived => write!(f, "No device arrived"),
Error::NoDeviceLeft=> write!(f, "No device left"),
Error::InvalidDevice => write!(f, "Device with non-supported product ID or vendor ID was detected"),
Error::NoDeviceLeft => write!(f, "No device left"),
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_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 mut offset = 0;
let mut sequence_number = 0;
let mut hid_chunk = [0_u8; HID_PACKET_SIZE];
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 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 {
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]);
@ -199,7 +233,11 @@ impl Manager {
let mut chunk: [u8; HID_PACKET_SIZE] = [0; HID_PACKET_SIZE];
let chunk_size = handle.read(&mut 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"));
}
let seq = (chunk[3] as usize) << 8 | (chunk[4] as usize);
@ -225,28 +263,38 @@ impl Manager {
if message.len() < 2 {
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);
match status {
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")),
0x6a82 => Err(Error::Protocol("File not found")),
0x6a85 => Err(Error::UserCancel),
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")),
0x6f00...0x6fff => Err(Error::Protocol("Internal error")),
0x9000 => Ok(()),
_ => Err(Error::Protocol("Unknown error")),
}?;
let new_len = message.len() - 2;
message.truncate(new_len);
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::read(&handle)
}
@ -256,7 +304,11 @@ impl Manager {
if ver.len() != 4 {
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] {
@ -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 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))?;
// Signing personal messages are only support by Ledger firmware version 1.0.8 or newer
if command == commands::SIGN_ETH_PERSONAL_MESSAGE {
let version = Self::get_firmware_version(&handle)?;
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();
// Copy the address of the key (only done once)
@ -326,7 +388,11 @@ impl<'a> Wallet<'a> for Manager {
type Error = Error;
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)
}
@ -346,10 +412,12 @@ impl<'a> Wallet<'a> for Manager {
return Err(Error::NoDeviceArrived);
}
let detected_devices = devices.iter()
.filter(|&d| is_valid_ledger(d.vendor_id, d.product_id) &&
is_valid_hid_device(d.usage_page, d.interface_number)
)
let detected_devices = devices
.iter()
.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| {
match self.read_device(&usb, &d) {
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 manufacturer = dev_info.manufacturer_string.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());
let manufacturer = dev_info
.manufacturer_string
.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) {
Ok(Some(addr)) => {
Ok(Device {
Ok(Some(addr)) => Ok(Device {
path: dev_info.path.clone(),
info: WalletInfo {
name,
@ -397,8 +477,7 @@ impl<'a> Wallet<'a> for Manager {
serial,
address: addr,
},
})
}
}),
// This variant is not possible, but the trait forces this return type
Ok(None) => Err(Error::Impossible),
Err(e) => Err(e),
@ -415,7 +494,11 @@ impl<'a> Wallet<'a> for Manager {
}
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> {
@ -426,8 +509,15 @@ impl<'a> Wallet<'a> for Manager {
let derivation_path = self.get_derivation_path();
let key_and_address = Self::send_apdu(device, commands::GET_ETH_PUBLIC_ADDRESS, 0, 0, derivation_path)?;
if key_and_address.len() != 107 { // 1 + 65 PK + 1 + 40 Addr (ascii-hex)
let key_and_address = Self::send_apdu(
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"));
}
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>
where F: Fn() -> Result<R, &'static str>
where
F: Fn() -> Result<R, &'static str>,
{
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
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();
while start_time.elapsed() <= *max_polling_duration {
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)]
mod tests {
use rustc_hex::FromHex;
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
#[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");
// Fetch the ethereum address of a connected ledger device
let address = ledger.list_devices()
let address = ledger
.list_devices()
.iter()
.filter(|d| d.manufacturer == "Ledger".to_string())
.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");
// Fetch the ethereum address of a connected ledger device
let address = ledger.list_devices()
let address = ledger
.list_devices()
.iter()
.filter(|d| d.manufacturer == "Ledger".to_string())
.nth(0)

View File

@ -28,15 +28,20 @@ extern crate protobuf;
extern crate semver;
extern crate trezor_sys;
#[macro_use] extern crate log;
#[cfg(test)] extern crate rustc_hex;
#[macro_use]
extern crate log;
#[cfg(test)]
extern crate rustc_hex;
mod ledger;
mod trezor;
use std::sync::{Arc, atomic, atomic::AtomicBool, Weak};
use std::{fmt, time::Duration};
use std::thread;
use std::{
fmt,
sync::{atomic, atomic::AtomicBool, Arc, Weak},
thread,
time::Duration,
};
use ethereum_types::U256;
use ethkey::{Address, Signature};
@ -62,7 +67,11 @@ pub trait Wallet<'a> {
type Transaction;
/// 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.
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>;
/// 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
fn list_devices(&self) -> Vec<WalletInfo>;
@ -95,7 +108,8 @@ pub trait Wallet<'a> {
/// * <https://github.com/paritytech/hidapi-rs>
/// * <https://github.com/rust-lang/libc>
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.
@ -220,7 +234,9 @@ impl HardwareWalletManager {
/// Hardware wallet constructor
pub fn new() -> Result<Self, Error> {
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 trezor = trezor::Manager::new(hidapi.clone());
let usb_context = Arc::new(libusb::Context::new()?);
@ -232,11 +248,13 @@ impl HardwareWalletManager {
// Subscribe to all vendor IDs (VIDs) and product IDs (PIDs)
// This means that the `HardwareWalletManager` is responsible to validate the detected device
usb_context.register_callback(
None, None, Some(HID_USB_DEVICE_CLASS),
None,
None,
Some(HID_USB_DEVICE_CLASS),
Box::new(EventHandler::new(
Arc::downgrade(&ledger),
Arc::downgrade(&trezor)
))
Arc::downgrade(&trezor),
)),
)?;
// Hardware event subscriber thread
@ -309,7 +327,12 @@ impl HardwareWalletManager {
}
/// 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() {
Ok(self.ledger.sign_transaction(address, encoded_transaction)?)
} 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
/// a device when it is unlocked
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
if let Ok((vid, pid)) = Self::extract_device_info(&device) {
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");
}
} 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");
}
}
@ -383,11 +416,19 @@ impl libusb::Hotplug for EventHandler {
// Version ID and Product ID are available
if let Ok((vid, pid)) = Self::extract_device_info(&device) {
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");
}
} 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");
}
}

View File

@ -19,19 +19,26 @@
//! and <https://github.com/trezor/trezor-common/blob/master/protob/protocol.md>
//! for protocol details.
use std::cmp::{min, max};
use std::sync::Arc;
use std::time::{Duration, Instant};
use std::fmt;
use std::{
cmp::{max, min},
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 hidapi;
use libusb;
use parking_lot::{Mutex, RwLock};
use protobuf::{self, Message, ProtobufEnum};
use super::{DeviceDirection, WalletInfo, TransactionInfo, KeyPath, Wallet, Device, is_valid_hid_device};
use trezor_sys::messages::{EthereumAddress, PinMatrixAck, MessageType, EthereumTxRequest, EthereumSignTx, EthereumGetAddress, EthereumTxAck, ButtonAck};
use trezor_sys::messages::{
ButtonAck, EthereumAddress, EthereumGetAddress, EthereumSignTx, EthereumTxAck,
EthereumTxRequest, MessageType, PinMatrixAck,
};
/// Trezor v1 vendor ID
const TREZOR_VID: u16 = 0x534c;
@ -77,11 +84,18 @@ impl fmt::Display for Error {
Error::KeyNotFound => write!(f, "Key not found"),
Error::UserCancel => write!(f, "Operation has been cancelled"),
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::NoSigningMessage=> write!(f, "Signing messages are not supported by Trezor"),
Error::LockedDevice(ref s) => write!(
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::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),
// Getting anything else means we didn't unlock it
_ => Ok(false),
}
};
self.update_devices(DeviceDirection::Arrived)?;
@ -156,12 +169,21 @@ impl Manager {
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)?;
match resp_type {
MessageType::MessageType_Cancel => Err(Error::UserCancel),
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
// for up to 5 minutes waiting for response from the device
// 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
// pipeline will already do this transformation, so remove it here
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 {
// If there isn't a chain_id, v will be returned as 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.")),
}
}
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 mut message = msg.write_to_bytes()?;
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 mut buf = vec![0; 64];
@ -255,8 +291,13 @@ impl Manager {
if first_chunk < 9 || buf[0] != b'?' || buf[1] != b'#' || buf[2] != b'#' {
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_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 msg_type =
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();
data.extend_from_slice(&buf[9..]);
while data.len() < (msg_size as usize) {
@ -271,11 +312,17 @@ impl<'a> Wallet<'a> for Manager {
type Error = Error;
type Transaction = &'a TransactionInfo;
fn sign_transaction(&self, address: &Address, t_info: Self::Transaction) ->
Result<Signature, Error> {
fn sign_transaction(
&self,
address: &Address,
t_info: Self::Transaction,
) -> Result<Signature, Error> {
let usb = self.usb.lock();
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 msg_type = MessageType::MessageType_EthereumSignTx;
let mut message = EthereumSignTx::new();
@ -301,7 +348,11 @@ impl<'a> Wallet<'a> for Manager {
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) {
@ -314,10 +365,12 @@ impl<'a> Wallet<'a> for Manager {
let devices = usb.devices();
let num_prev_devices = self.devices.read().len();
let detected_devices = devices.iter()
.filter(|&d| is_valid_trezor(d.vendor_id, d.product_id) &&
is_valid_hid_device(d.usage_page, d.interface_number)
)
let detected_devices = devices
.iter()
.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| {
match self.read_device(&usb, &d) {
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 manufacturer = dev_info.manufacturer_string.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());
let manufacturer = dev_info
.manufacturer_string
.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) {
Ok(Some(addr)) => {
Ok(Device {
Ok(Some(addr)) => Ok(Device {
path: dev_info.path.clone(),
info: WalletInfo {
name,
@ -365,8 +430,7 @@ impl<'a> Wallet<'a> for Manager {
serial,
address: addr,
},
})
}
}),
Ok(None) => Err(Error::LockedDevice(dev_info.path.clone())),
Err(e) => Err(e),
}
@ -381,7 +445,11 @@ impl<'a> Wallet<'a> for Manager {
}
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> {
@ -405,7 +473,8 @@ impl<'a> Wallet<'a> for Manager {
}
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)
}
@ -417,7 +486,7 @@ pub fn try_connect_polling(trezor: &Manager, duration: &Duration, dir: DeviceDir
while start_time.elapsed() <= *duration {
if let Ok(num_devices) = trezor.update_devices(dir) {
trace!(target: "hw", "{} Trezor devices {}", num_devices, dir);
return true
return true;
}
}
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
/// (and unlocked) attached to the machine that's running the test
fn test_signature() {
use super::HardwareWalletManager;
use ethereum_types::Address;
use MAX_POLLING_DURATION;
use super::HardwareWalletManager;
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()
.filter(|d| d.name == "TREZOR".to_string() && d.manufacturer == "SatoshiLabs".to_string())
.nth(0)

View File

@ -16,13 +16,10 @@
//! Account Metadata
use std::{
collections::HashMap,
time::Instant,
};
use std::{collections::HashMap, time::Instant};
use ethkey::{Address, Password};
use serde_derive::{Serialize, Deserialize};
use serde_derive::{Deserialize, Serialize};
use serde_json;
/// Type of unlock.
@ -57,17 +54,18 @@ pub struct AccountMeta {
impl 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,
{
serde_json::from_reader(reader)
}
/// 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,
{
serde_json::to_writer(writer, m)
}
}

View File

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

View File

@ -25,28 +25,32 @@ mod stores;
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
extern crate fake_hardware_wallet as hardware_wallet;
use self::account_data::{Unlock, AccountData};
use self::stores::AddressBook;
use self::{
account_data::{AccountData, Unlock},
stores::AddressBook,
};
use std::collections::HashMap;
use std::time::{Instant, Duration};
use std::{
collections::HashMap,
time::{Duration, Instant},
};
use common_types::transaction::{Action, Transaction};
use ethkey::{Address, Message, Public, Secret, Password, Random, Generator};
use ethstore::accounts_dir::MemoryDirectory;
use ethkey::{Address, Generator, Message, Password, Public, Random, Secret};
use ethstore::{
SimpleSecretStore, SecretStore, EthStore, EthMultiStore,
random_string, SecretVaultRef, StoreAccountRef, OpaqueSecret,
accounts_dir::MemoryDirectory, random_string, EthMultiStore, EthStore, OpaqueSecret,
SecretStore, SecretVaultRef, SimpleSecretStore, StoreAccountRef,
};
use log::{warn, debug};
use log::{debug, warn};
use parking_lot::RwLock;
pub use ethkey::Signature;
pub use ethstore::{Derivation, IndexDerivation, KeyFile, Error};
pub use hardware_wallet::{Error as HardwareError, HardwareWalletManager, KeyPath, TransactionInfo};
pub use ethstore::{Derivation, Error, IndexDerivation, KeyFile};
pub use hardware_wallet::{
Error as HardwareError, HardwareWalletManager, KeyPath, TransactionInfo,
};
pub use self::account_data::AccountMeta;
pub use self::error::SignError;
pub use self::{account_data::AccountMeta, error::SignError};
type AccountToken = Password;
@ -86,7 +90,8 @@ pub struct AccountProvider {
}
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 {
@ -97,15 +102,22 @@ impl AccountProvider {
if settings.enable_hardware_wallets {
match HardwareWalletManager::new() {
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)
},
}
Err(e) => debug!("Error initializing hardware wallets: {}", e),
}
}
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",
account.address);
}
@ -135,7 +147,10 @@ impl AccountProvider {
unlocked_secrets: RwLock::new(HashMap::new()),
unlocked: RwLock::new(HashMap::new()),
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(),
hardware_store: None,
unlock_keep_secret: false,
@ -150,17 +165,23 @@ impl AccountProvider {
/// Creates new random account and returns address and public key
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 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))
}
/// Inserts new account into underlying store.
/// Does not unlock account!
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) {
self.sstore.remove_account(&account, password)?;
return Err(Error::InvalidAccount.into());
@ -171,26 +192,49 @@ impl AccountProvider {
/// Generates new derived account based on the existing one
/// If password is not provided, account must be unlocked
/// 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)
-> Result<Address, SignError>
{
pub fn derive_account(
&self,
address: &Address,
password: Option<Password>,
derivation: Derivation,
save: bool,
) -> Result<Address, SignError> {
let account = self.sstore.account_ref(&address)?;
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
Ok(
if save { self.sstore.insert_derived(SecretVaultRef::Root, &account, &password, derivation)?.address }
else { self.sstore.generate_derived(&account, &password, derivation)? }
)
let password = password
.map(Ok)
.unwrap_or_else(|| self.password(&account))?;
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.
pub fn import_presale(&self, presale_json: &[u8], password: &Password) -> Result<Address, Error> {
let account = self.sstore.import_presale(SecretVaultRef::Root, presale_json, password)?;
pub fn import_presale(
&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())
}
/// Import a new wallet.
pub fn import_wallet(&self, json: &[u8], password: &Password, gen_id: bool) -> Result<Address, Error> {
let account = self.sstore.import_wallet(SecretVaultRef::Root, json, password, gen_id)?;
pub fn import_wallet(
&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) {
self.sstore.remove_account(&account, password)?;
return Err(Error::InvalidAccount.into());
@ -210,8 +254,7 @@ impl AccountProvider {
.into_iter()
.map(|a| a.address)
.filter(|address| !self.blacklisted_accounts.contains(address))
.collect()
)
.collect())
}
/// Returns the address of default account.
@ -226,12 +269,18 @@ impl AccountProvider {
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
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),
Some(Err(e)) => Err(SignError::Hardware(e)),
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
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),
Some(Err(e)) => Err(SignError::Hardware(e)),
Some(Ok(s)) => Ok(s),
@ -269,31 +322,51 @@ impl AccountProvider {
/// Returns each account along with name and meta.
pub fn accounts_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> {
let r = self.sstore.accounts()?
let r = self
.sstore
.accounts()?
.into_iter()
.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();
Ok(r)
}
/// Returns each hardware account along with name and meta.
pub fn hardware_accounts_info(&self) -> Result<HashMap<Address, AccountMeta>, Error> {
let r = self.hardware_accounts()?
let r = self
.hardware_accounts()?
.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();
Ok(r)
}
/// Returns each hardware account along with name and meta.
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.
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 {
name: info.name,
meta: info.manufacturer,
@ -311,59 +384,78 @@ impl AccountProvider {
/// Returns account public key.
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.
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(())
}
/// Returns each account along with name and meta.
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(())
}
/// Returns `true` if the password for `account` is `password`. `false` if not.
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)
}
/// Permanently removes an account.
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(())
}
/// 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> {
self.sstore.change_password(&self.sstore.account_ref(address)?, &password, &new_password)
pub fn change_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.
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.
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)?;
// check if account is already unlocked permanently, if it is, do nothing
let mut unlocked = self.unlocked.write();
if let Some(data) = unlocked.get(&account) {
if let Unlock::Perm = data.unlock {
return Ok(())
return Ok(());
}
}
if self.unlock_keep_secret && unlock == Unlock::Perm {
// verify password and get the secret
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 {
// verify password by signing dump message
// result may be discarded
@ -383,11 +475,15 @@ impl AccountProvider {
let mut unlocked = self.unlocked.write();
let data = unlocked.get(account).ok_or(SignError::NotUnlocked)?.clone();
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 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);
}
}
@ -395,17 +491,30 @@ impl AccountProvider {
}
/// 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)
}
/// 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)
}
/// 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))
}
@ -413,7 +522,8 @@ impl AccountProvider {
pub fn is_unlocked(&self, address: &Address) -> bool {
let unlocked = self.unlocked.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())
.unwrap_or(false)
}
@ -421,48 +531,78 @@ impl AccountProvider {
/// Checks if given account is unlocked permanently
pub fn is_unlocked_permanently(&self, address: &Address) -> bool {
let unlocked = self.unlocked.read();
self.sstore.account_ref(address)
.map(|r| unlocked.get(&r).map_or(false, |account| account.unlock == Unlock::Perm))
self.sstore
.account_ref(address)
.map(|r| {
unlocked
.get(&r)
.map_or(false, |account| account.unlock == Unlock::Perm)
})
.unwrap_or(false)
}
/// 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)?;
match self.unlocked_secrets.read().get(&account) {
Some(secret) => {
Ok(self.sstore.sign_with_secret(&secret, &message)?)
},
Some(secret) => Ok(self.sstore.sign_with_secret(&secret, &message)?),
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)?)
}
}
}
/// 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)
-> Result<Signature, SignError>
{
pub fn sign_derived(
&self,
address: &Address,
password: Option<Password>,
derivation: Derivation,
message: Message,
) -> Result<Signature, SignError> {
let account = self.sstore.account_ref(address)?;
let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
Ok(self.sstore.sign_derived(&account, &password, derivation, &message)?)
let password = password
.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.
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 is_std_password = self.sstore.test_password(&account, &token)?;
let new_token = Password::from(random_string(16));
let signature = if is_std_password {
// 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
self.sstore.sign(&account, &token, &message)?
} else {
// check transient store
self.transient_sstore.change_password(&account, &token, &new_token)?;
self.transient_sstore
.change_password(&account, &token, &new_token)?;
// and sign
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.
pub fn decrypt_with_token(&self, address: Address, token: AccountToken, shared_mac: &[u8], message: &[u8])
-> Result<(Vec<u8>, AccountToken), SignError>
{
pub fn decrypt_with_token(
&self,
address: Address,
token: AccountToken,
shared_mac: &[u8],
message: &[u8],
) -> Result<(Vec<u8>, AccountToken), SignError> {
let account = self.sstore.account_ref(&address)?;
let is_std_password = self.sstore.test_password(&account, &token)?;
let new_token = Password::from(random_string(16));
let message = if is_std_password {
// 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
self.sstore.decrypt(&account, &token, shared_mac, message)?
} else {
// check transient store
self.transient_sstore.change_password(&account, &token, &new_token)?;
self.transient_sstore
.change_password(&account, &token, &new_token)?;
// and decrypt
self.transient_sstore.decrypt(&account, &token, shared_mac, message)?
self.transient_sstore
.decrypt(&account, &token, shared_mac, message)?
};
Ok((message, new_token))
}
/// 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 password = password.map(Ok).unwrap_or_else(|| self.password(&account))?;
Ok(self.sstore.decrypt(&account, &password, shared_mac, message)?)
let password = password
.map(Ok)
.unwrap_or_else(|| self.password(&account))?;
Ok(self
.sstore
.decrypt(&account, &password, shared_mac, message)?)
}
/// 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 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)?)
}
/// Returns the underlying `SecretStore` reference if one exists.
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.
pub fn import_geth_accounts(&self, desired: Vec<Address>, testnet: bool) -> Result<Vec<Address>, Error> {
self.sstore.import_geth_accounts(SecretVaultRef::Root, desired, testnet)
pub fn import_geth_accounts(
&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_err(Into::into)
}
/// Create new vault.
pub fn create_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
self.sstore.create_vault(name, password)
.map_err(Into::into)
self.sstore.create_vault(name, password).map_err(Into::into)
}
/// Open existing vault.
pub fn open_vault(&self, name: &str, password: &Password) -> Result<(), Error> {
self.sstore.open_vault(name, password)
.map_err(Into::into)
self.sstore.open_vault(name, password).map_err(Into::into)
}
/// Close previously opened vault.
pub fn close_vault(&self, name: &str) -> Result<(), Error> {
self.sstore.close_vault(name)
.map_err(Into::into)
self.sstore.close_vault(name).map_err(Into::into)
}
/// List all vaults
pub fn list_vaults(&self) -> Result<Vec<String>, Error> {
self.sstore.list_vaults()
.map_err(Into::into)
self.sstore.list_vaults().map_err(Into::into)
}
/// List all currently opened vaults
pub fn list_opened_vaults(&self) -> Result<Vec<String>, Error> {
self.sstore.list_opened_vaults()
.map_err(Into::into)
self.sstore.list_opened_vaults().map_err(Into::into)
}
/// Change vault password.
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)
}
/// Change vault of the given address.
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)?;
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(|_| ())
}
/// Get vault metadata string.
pub fn get_vault_meta(&self, name: &str) -> Result<String, Error> {
self.sstore.get_vault_meta(name)
.map_err(Into::into)
self.sstore.get_vault_meta(name).map_err(Into::into)
}
/// Set vault metadata string.
pub fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> {
self.sstore.set_vault_meta(name, meta)
.map_err(Into::into)
self.sstore.set_vault_meta(name, meta).map_err(Into::into)
}
/// Sign message with hardware wallet.
pub fn sign_message_with_hardware(&self, address: &Address, message: &[u8]) -> Result<Signature, SignError> {
match self.hardware_store.as_ref().map(|s| s.sign_message(address, message)) {
pub fn sign_message_with_hardware(
&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),
Some(Err(e)) => Err(From::from(e)),
Some(Ok(s)) => Ok(s),
@ -586,7 +771,13 @@ impl AccountProvider {
}
/// 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 {
nonce: transaction.nonce,
gas_price: transaction.gas_price,
@ -599,7 +790,11 @@ impl AccountProvider {
data: transaction.data.to_vec(),
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),
Some(Err(e)) => Err(From::from(e)),
Some(Ok(s)) => Ok(s),
@ -610,18 +805,24 @@ impl AccountProvider {
#[cfg(test)]
mod tests {
use super::{AccountProvider, Unlock};
use std::time::{Duration, Instant};
use ethkey::{Generator, Random, Address};
use ethstore::{StoreAccountRef, Derivation};
use ethereum_types::H256;
use ethkey::{Address, Generator, Random};
use ethstore::{Derivation, StoreAccountRef};
use std::time::{Duration, Instant};
#[test]
fn unlock_account_temp() {
let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).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
.insert_account(kp.secret().clone(), &"test".into())
.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_err());
}
@ -630,81 +831,118 @@ mod tests {
fn derived_account_nosave() {
let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok());
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok());
assert!(ap
.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(),
None,
Derivation::SoftHash(H256::from(999)),
false,
).expect("Derivation should not fail");
)
.expect("Derivation should not fail");
assert!(ap.unlock_account_permanently(derived_addr, "base".into()).is_err(),
"There should be an error because account is not supposed to be saved");
assert!(
ap.unlock_account_permanently(derived_addr, "base".into())
.is_err(),
"There should be an error because account is not supposed to be saved"
);
}
#[test]
fn derived_account_save() {
let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok());
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok());
assert!(ap
.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(),
None,
Derivation::SoftHash(H256::from(999)),
true,
).expect("Derivation should not fail");
)
.expect("Derivation should not fail");
assert!(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_wrong".into())
.is_err(),
"There should be an error because password is invalid"
);
assert!(ap.unlock_account_permanently(derived_addr, "base".into()).is_ok(),
"Should be ok because account is saved and password is valid");
assert!(
ap.unlock_account_permanently(derived_addr, "base".into())
.is_ok(),
"Should be ok because account is saved and password is valid"
);
}
#[test]
fn derived_account_sign() {
let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok());
assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok());
assert!(ap
.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(),
None,
Derivation::SoftHash(H256::from(1999)),
true,
).expect("Derivation should not fail");
)
.expect("Derivation should not fail");
ap.unlock_account_permanently(derived_addr, "base".into())
.expect("Should be ok because account is saved and password is valid");
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");
let signed_msg2 = ap.sign_derived(
let signed_msg2 = ap
.sign_derived(
&kp.address(),
None,
Derivation::SoftHash(H256::from(1999)),
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,
"Signed messages should match");
assert_eq!(signed_msg1, signed_msg2, "Signed messages should match");
}
#[test]
fn unlock_account_perm() {
let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).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
.insert_account(kp.secret().clone(), &"test".into())
.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.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());
}
@ -713,11 +951,21 @@ mod tests {
fn unlock_account_timer() {
let kp = Random.generate().unwrap();
let ap = AccountProvider::transient_provider();
assert!(ap.insert_account(kp.secret().clone(), &"test".into()).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
.insert_account(kp.secret().clone(), &"test".into())
.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());
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());
}
@ -726,15 +974,23 @@ mod tests {
// given
let kp = Random.generate().unwrap();
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
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
ap.sign_with_token(kp.address(), token.clone(), Default::default())
.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]
@ -745,7 +1001,14 @@ mod tests {
ap.blacklisted_accounts = vec![acc];
// 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![]);
}
}

View File

@ -16,9 +16,11 @@
//! Address Book Store
use std::{fs, fmt, hash, ops};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::{
collections::HashMap,
fmt, fs, hash, ops,
path::{Path, PathBuf},
};
use ethkey::Address;
use log::{trace, warn};
@ -34,7 +36,7 @@ impl AddressBook {
/// Creates new address book at given directory.
pub fn new(path: &Path) -> Self {
let mut r = AddressBook {
cache: DiskMap::new(path, "address_book.json")
cache: DiskMap::new(path, "address_book.json"),
};
r.cache.revert(AccountMeta::read);
r
@ -43,7 +45,7 @@ impl AddressBook {
/// Creates transient address book (no changes are saved to disk).
pub fn transient() -> Self {
AddressBook {
cache: DiskMap::transient()
cache: DiskMap::transient(),
}
}
@ -59,8 +61,11 @@ impl AddressBook {
/// Sets new name for given address.
pub fn set_name(&mut self, a: Address, name: String) {
{
let x = self.cache.entry(a)
.or_insert_with(|| AccountMeta {name: Default::default(), meta: "{}".to_owned(), uuid: None});
let x = self.cache.entry(a).or_insert_with(|| AccountMeta {
name: Default::default(),
meta: "{}".to_owned(),
uuid: None,
});
x.name = name;
}
self.save();
@ -69,8 +74,11 @@ impl AddressBook {
/// Sets new meta for given address.
pub fn set_meta(&mut self, a: Address, meta: String) {
{
let x = self.cache.entry(a)
.or_insert_with(|| AccountMeta {name: "Anonymous".to_owned(), meta: Default::default(), uuid: None});
let x = self.cache.entry(a).or_insert_with(|| AccountMeta {
name: "Anonymous".to_owned(),
meta: Default::default(),
uuid: None,
});
x.meta = meta;
}
self.save();
@ -122,31 +130,40 @@ impl<K: hash::Hash + Eq, V> DiskMap<K, V> {
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>,
E: fmt::Display,
{
if self.transient { return; }
if self.transient {
return;
}
trace!(target: "diskmap", "revert {:?}", self.path);
let _ = fs::File::open(self.path.clone())
.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| {
self.cache = m;
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>,
E: fmt::Display,
{
if self.transient { return; }
if self.transient {
return;
}
trace!(target: "diskmap", "save {:?}", self.path);
let _ = fs::File::create(self.path.clone())
.map_err(|e| warn!(target: "diskmap", "Couldn't open disk map for writing: {}", e))
.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)]
mod tests {
use super::AddressBook;
use crate::account_data::AccountMeta;
use std::collections::HashMap;
use tempdir::TempDir;
use crate::account_data::AccountMeta;
#[test]
fn should_save_and_reload_address_book() {
@ -165,9 +182,20 @@ mod tests {
b.set_name(1.into(), "One".to_owned());
b.set_meta(1.into(), "{1:1}".to_owned());
let b = AddressBook::new(tempdir.path());
assert_eq!(b.get(), vec![
(1, AccountMeta {name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None})
].into_iter().map(|(a, b)| (a.into(), b)).collect::<HashMap<_, _>>());
assert_eq!(
b.get(),
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]
@ -181,9 +209,29 @@ mod tests {
b.remove(2.into());
let b = AddressBook::new(tempdir.path());
assert_eq!(b.get(), vec![
(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<_, _>>());
assert_eq!(
b.get(),
vec![
(
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
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
extern crate serde_json;
extern crate ethjson;
extern crate serde_json;
use std::{fs, env, process};
use ethjson::spec::Spec;
use std::{env, fs, process};
fn quit(s: &str) -> ! {
println!("{}", s);
@ -28,9 +28,11 @@ fn quit(s: &str) -> ! {
fn main() {
let mut args = env::args();
if args.len() != 2 {
quit("You need to specify chainspec.json\n\
quit(
"You need to specify chainspec.json\n\
\n\
./chainspec <chainspec.json>");
./chainspec <chainspec.json>",
);
}
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
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::fmt::{Debug, Formatter, Error as FmtError};
use std::io::{BufReader, BufRead};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::collections::BTreeMap;
use std::thread;
use std::time;
use std::{
collections::BTreeMap,
fmt::{Debug, Error as FmtError, Formatter},
io::{BufRead, BufReader},
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
},
thread, time,
};
use std::path::PathBuf;
use hash::keccak;
use parking_lot::Mutex;
use std::{fs::File, path::PathBuf};
use url::Url;
use std::fs::File;
use ws::ws::{
self,
Request,
Handler,
Sender,
Handshake,
Error as WsError,
ErrorKind as WsErrorKind,
Message,
Result as WsResult,
self, Error as WsError, ErrorKind as WsErrorKind, Handler, Handshake, Message, Request,
Result as WsResult, Sender,
};
use serde::de::DeserializeOwned;
use serde_json::{
self as json,
Value as JsonValue,
Error as JsonError,
use serde_json::{self as json, Error as JsonError, Value as JsonValue};
use futures::{done, oneshot, Canceled, Complete, Future};
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;
/// The actual websocket connection handler, passed into the
@ -67,11 +60,7 @@ struct RpcHandler {
}
impl RpcHandler {
fn new(
out: Sender,
auth_code: String,
complete: Complete<Result<Rpc, RpcError>>
) -> Self {
fn new(out: Sender, auth_code: String, complete: Complete<Result<Rpc, RpcError>>) -> Self {
RpcHandler {
out: Some(out),
auth_code: auth_code,
@ -85,23 +74,22 @@ impl Handler for RpcHandler {
fn build_request(&mut self, url: &Url) -> WsResult<Request> {
match Request::from_url(url) {
Ok(mut r) => {
let timestamp = time::UNIX_EPOCH.elapsed().map_err(|err| {
WsError::new(WsErrorKind::Internal, format!("{}", err))
})?;
let timestamp = time::UNIX_EPOCH
.elapsed()
.map_err(|err| WsError::new(WsErrorKind::Internal, format!("{}", err)))?;
let secs = timestamp.as_secs();
let hashed = keccak(format!("{}:{}", self.auth_code, secs));
let proto = format!("{:x}_{}", hashed, secs);
r.add_protocol(&proto);
Ok(r)
},
Err(e) =>
Err(WsError::new(WsErrorKind::Internal, format!("{}", e))),
}
Err(e) => Err(WsError::new(WsErrorKind::Internal, format!("{}", e))),
}
}
fn on_error(&mut self, err: WsError) {
match self.complete.take() {
Some(c) => match c.send(Err(RpcError::WsError(err))) {
Ok(_) => {},
Ok(_) => {}
Err(_) => warn!(target: "rpc-client", "Unable to notify about error."),
},
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.")
}
Ok(())
},
}
_ => {
let msg = format!("on_open called twice");
Err(WsError::new(WsErrorKind::Internal, msg))
@ -131,12 +119,19 @@ impl Handler for RpcHandler {
let response_id;
let string = &msg.to_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);
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);
response_id = id as usize;
}
@ -147,22 +142,24 @@ impl Handler for RpcHandler {
string,
e
);
return Ok(())
},
return Ok(());
}
_ => {
warn!(
target: "rpc-client",
"recieved invalid message: {}",
string
);
return Ok(())
return Ok(());
}
}
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.")
},
}
}
None => warn!(
target: "rpc-client",
"warning: unexpected id: {}",
@ -175,9 +172,7 @@ impl Handler for RpcHandler {
/// Keeping track of issued requests to be matched up with responses
#[derive(Clone)]
struct Pending(
Arc<Mutex<BTreeMap<usize, Complete<Result<JsonValue, RpcError>>>>>
);
struct Pending(Arc<Mutex<BTreeMap<usize, Complete<Result<JsonValue, RpcError>>>>>);
impl Pending {
fn new() -> Self {
@ -186,10 +181,7 @@ impl Pending {
fn insert(&mut self, k: usize, v: Complete<Result<JsonValue, RpcError>>) {
self.0.lock().insert(k, v);
}
fn remove(
&mut self,
k: usize
) -> Option<Complete<Result<JsonValue, RpcError>>> {
fn remove(&mut self, k: usize) -> Option<Complete<Result<JsonValue, RpcError>>> {
self.0.lock().remove(&k)
}
}
@ -223,9 +215,7 @@ impl Rpc {
}
/// Non-blocking, returns a future
pub fn connect(
url: &str, authpath: &PathBuf
) -> BoxFuture<Result<Self, RpcError>, Canceled> {
pub fn connect(url: &str, authpath: &PathBuf) -> BoxFuture<Result<Self, RpcError>, Canceled> {
let (c, p) = oneshot::<Result<Self, RpcError>>();
match get_authcode(authpath) {
Err(e) => return Box::new(done(Ok(Err(e)))),
@ -239,20 +229,18 @@ impl Rpc {
let conn = ws::connect(url, |out| {
// this will panic if the closure is called twice,
// which it should never be.
let c = once.take()
.expect("connection closure called only once");
let c = once.take().expect("connection closure called only once");
RpcHandler::new(out, code.clone(), c)
});
match conn {
Err(err) => {
// since ws::connect is only called once, it cannot
// both fail and succeed.
let c = once.take()
.expect("connection closure called only once");
let c = once.take().expect("connection closure called only once");
let _ = c.send(Err(RpcError::WsError(err)));
},
}
// c will complete on the `on_open` event in the Handler
_ => ()
_ => (),
}
});
Box::new(p)
@ -262,10 +250,13 @@ impl Rpc {
/// Non-blocking, returns a future of the request response
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>
where T: DeserializeOwned + Send + Sized {
where
T: DeserializeOwned + Send + Sized,
{
let (c, p) = oneshot::<Result<JsonValue, RpcError>>();
let id = self.counter.fetch_add(1, Ordering::Relaxed);
@ -278,18 +269,15 @@ impl Rpc {
id: Id::Num(id as u64),
};
let serialized = json::to_string(&request)
.expect("request is serializable");
let serialized = json::to_string(&request).expect("request is serializable");
let _ = self.out.send(serialized);
Box::new(p.map(|result| {
match result {
Box::new(p.map(|result| match result {
Ok(json) => {
let t: T = json::from_value(json)?;
Ok(t)
},
Err(err) => Err(err)
}
Err(err) => Err(err),
}))
}
}
@ -308,22 +296,14 @@ pub enum RpcError {
impl Debug for RpcError {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
match *self {
RpcError::WrongVersion(ref s)
=> write!(f, "Expected version 2.0, got {}", s),
RpcError::ParseError(ref err)
=> write!(f, "ParseError: {}", err),
RpcError::MalformedResponse(ref s)
=> write!(f, "Malformed response: {}", s),
RpcError::JsonRpc(ref json)
=> write!(f, "JsonRpc error: {:?}", json),
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"),
RpcError::WrongVersion(ref s) => write!(f, "Expected version 2.0, got {}", s),
RpcError::ParseError(ref err) => write!(f, "ParseError: {}", err),
RpcError::MalformedResponse(ref s) => write!(f, "Malformed response: {}", s),
RpcError::JsonRpc(ref json) => write!(f, "JsonRpc error: {:?}", json),
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 jsonrpc_core;
extern crate jsonrpc_ws_server as ws;
extern crate keccak_hash as hash;
extern crate parity_rpc as rpc;
extern crate parking_lot;
extern crate serde;
extern crate serde_json;
extern crate url;
extern crate keccak_hash as hash;
#[macro_use]
extern crate log;
@ -36,15 +36,15 @@ extern crate log;
extern crate matches;
/// 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)]
mod tests {
use futures::Future;
use std::path::PathBuf;
use client::{Rpc, RpcError};
use futures::Future;
use rpc;
use std::path::PathBuf;
#[test]
fn test_connection_refused() {
@ -53,12 +53,13 @@ mod tests {
let _ = authcodes.generate_new();
authcodes.to_file(&authcodes.path).unwrap();
let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port - 1),
&authcodes.path);
let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port - 1), &authcodes.path);
let _ = connect.map(|conn| {
let _ = connect
.map(|conn| {
assert!(matches!(&conn, &Err(RpcError::WsError(_))));
}).wait();
})
.wait();
}
#[test]
@ -68,9 +69,11 @@ mod tests {
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)));
}).wait();
})
.wait();
}
#[test]
@ -80,12 +83,8 @@ mod tests {
let _ = authcodes.generate_new();
authcodes.to_file(&authcodes.path).unwrap();
let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port),
&authcodes.path);
let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &authcodes.path);
let _ = connect.map(|conn| {
assert!(conn.is_ok())
}).wait();
let _ = connect.map(|conn| assert!(conn.is_ok())).wait();
}
}

View File

@ -16,12 +16,12 @@
use client::{Rpc, RpcError};
use ethereum_types::U256;
use rpc::signer::{ConfirmationRequest, TransactionModification, TransactionCondition};
use futures::Canceled;
use rpc::signer::{ConfirmationRequest, TransactionCondition, TransactionModification};
use serde;
use serde_json::{Value as JsonValue, to_value};
use serde_json::{to_value, Value as JsonValue};
use std::path::PathBuf;
use futures::{Canceled};
use {BoxFuture};
use BoxFuture;
pub struct SignerRpc {
rpc: Rpc,
@ -29,10 +29,14 @@ pub struct SignerRpc {
impl SignerRpc {
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![])
}
@ -42,19 +46,28 @@ impl SignerRpc {
new_gas: Option<U256>,
new_gas_price: Option<U256>,
new_condition: Option<Option<TransactionCondition>>,
pwd: &str
pwd: &str,
) -> 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(&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),
])
],
)
}
pub fn reject_request(&mut self, id: U256) -> BoxFuture<Result<bool, RpcError>, Canceled> {
self.rpc.request("signer_rejectRequest", vec![
JsonValue::String(format!("{:#x}", id))
])
self.rpc.request(
"signer_rejectRequest",
vec![JsonValue::String(format!("{:#x}", id))],
)
}
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_client as client;
use client::signer_client::SignerRpc;
use ethereum_types::U256;
use rpc::signer::ConfirmationRequest;
use client::signer_client::SignerRpc;
use std::io::{Write, BufRead, BufReader, stdout, stdin};
use std::path::PathBuf;
use std::fs::File;
use std::{
fs::File,
io::{stdin, stdout, BufRead, BufReader, Write},
path::PathBuf,
};
use futures::Future;
fn sign_interactive(
signer: &mut SignerRpc,
password: &str,
request: ConfirmationRequest
) {
print!("\n{}\nSign this transaction? (y)es/(N)o/(r)eject: ", request);
fn sign_interactive(signer: &mut SignerRpc, password: &str, request: ConfirmationRequest) {
print!(
"\n{}\nSign this transaction? (y)es/(N)o/(r)eject: ",
request
);
let _ = stdout().flush();
match BufReader::new(stdin()).lines().next() {
Some(Ok(line)) => {
match line.to_lowercase().chars().nth(0) {
Some('y') => {
match sign_transaction(signer, request.id, password) {
Some(Ok(line)) => match line.to_lowercase().chars().nth(0) {
Some('y') => match sign_transaction(signer, request.id, password) {
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),
}
}
_ => ()
}
}
_ => println!("Could not read from stdin")
},
_ => (),
},
_ => println!("Could not read from stdin"),
}
}
fn sign_transactions(
signer: &mut SignerRpc,
password: String
) -> Result<String, String> {
signer.requests_to_confirm().map(|reqs| {
match reqs {
Ok(ref reqs) if reqs.is_empty() => {
Ok("No transactions in signing queue".to_owned())
}
fn sign_transactions(signer: &mut SignerRpc, password: String) -> Result<String, String> {
signer
.requests_to_confirm()
.map(|reqs| match reqs {
Ok(ref reqs) if reqs.is_empty() => Ok("No transactions in signing queue".to_owned()),
Ok(reqs) => {
for r in reqs {
sign_interactive(signer, &password, r)
}
Ok("".to_owned())
}
Err(err) => {
Err(format!("error: {:?}", err))
}
}
}).map_err(|err| {
format!("{:?}", err)
}).wait()?
Err(err) => Err(format!("error: {:?}", err)),
})
.map_err(|err| format!("{:?}", err))
.wait()?
}
fn list_transactions(signer: &mut SignerRpc) -> Result<String, String> {
signer.requests_to_confirm().map(|reqs| {
match reqs {
Ok(ref reqs) if reqs.is_empty() => {
Ok("No transactions in signing queue".to_owned())
}
Ok(ref reqs) => {
Ok(format!("Transaction queue:\n{}", reqs
.iter()
signer
.requests_to_confirm()
.map(|reqs| match reqs {
Ok(ref reqs) if reqs.is_empty() => Ok("No transactions in signing queue".to_owned()),
Ok(ref reqs) => Ok(format!(
"Transaction queue:\n{}",
reqs.iter()
.map(|r| format!("{}", r))
.collect::<Vec<String>>()
.join("\n")))
}
Err(err) => {
Err(format!("error: {:?}", err))
}
}
}).map_err(|err| {
format!("{:?}", err)
}).wait()?
.join("\n")
)),
Err(err) => Err(format!("error: {:?}", err)),
})
.map_err(|err| format!("{:?}", err))
.wait()?
}
fn sign_transaction(
signer: &mut SignerRpc, id: U256, password: &str
) -> Result<String, String> {
signer.confirm_request(id, None, None, None, password).map(|res| {
match res {
fn sign_transaction(signer: &mut SignerRpc, id: U256, password: &str) -> Result<String, String> {
signer
.confirm_request(id, None, None, None, password)
.map(|res| match res {
Ok(u) => Ok(format!("Signed transaction id: {:#x}", u)),
Err(e) => Err(format!("{:?}", e)),
}
}).map_err(|err| {
format!("{:?}", err)
}).wait()?
})
.map_err(|err| format!("{:?}", err))
.wait()?
}
fn reject_transaction(
signer: &mut SignerRpc, id: U256) -> Result<String, String>
{
signer.reject_request(id).map(|res| {
match res {
fn reject_transaction(signer: &mut SignerRpc, id: U256) -> Result<String, String> {
signer
.reject_request(id)
.map(|res| match res {
Ok(true) => Ok(format!("Rejected transaction id {:#x}", id)),
Ok(false) => Err(format!("No such request")),
Err(e) => Err(format!("{:?}", e)),
}
}).map_err(|err| {
format!("{:?}", err)
}).wait()?
})
.map_err(|err| format!("{:?}", err))
.wait()?
}
// cmds
pub fn signer_list(
signerport: u16, authfile: PathBuf
) -> Result<String, String> {
pub fn signer_list(signerport: u16, authfile: PathBuf) -> Result<String, String> {
let addr = &format!("ws://127.0.0.1:{}", signerport);
let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| {
format!("{:?}", err)
})?;
let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| format!("{:?}", err))?;
list_transactions(&mut signer)
}
pub fn signer_reject(
id: Option<usize>, signerport: u16, authfile: PathBuf
id: Option<usize>,
signerport: u16,
authfile: PathBuf,
) -> Result<String, String> {
let id = id.ok_or(format!("id required for signer reject"))?;
let addr = &format!("ws://127.0.0.1:{}", signerport);
let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| {
format!("{:?}", err)
})?;
let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| format!("{:?}", err))?;
reject_transaction(&mut signer, U256::from(id))
}
@ -157,22 +133,17 @@ pub fn signer_sign(
id: Option<usize>,
pwfile: Option<PathBuf>,
signerport: u16,
authfile: PathBuf
authfile: PathBuf,
) -> Result<String, String> {
let password;
match pwfile {
Some(pwfile) => {
match File::open(pwfile) {
Ok(fd) => {
match BufReader::new(fd).lines().next() {
Some(pwfile) => match File::open(pwfile) {
Ok(fd) => match BufReader::new(fd).lines().next() {
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 => {
password = match rpassword::prompt_password_stdout("Password: ") {
Ok(p) => p,
@ -182,16 +153,10 @@ pub fn signer_sign(
}
let addr = &format!("ws://127.0.0.1:{}", signerport);
let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| {
format!("{:?}", err)
})?;
let mut signer = SignerRpc::new(addr, &authfile).map_err(|err| format!("{:?}", err))?;
match id {
Some(id) => {
sign_transaction(&mut signer, U256::from(id), &password)
},
None => {
sign_transactions(&mut signer, password)
}
Some(id) => sign_transaction(&mut signer, U256::from(id), &password),
None => sign_transactions(&mut signer, password),
}
}

View File

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

View File

@ -7,16 +7,16 @@ extern crate tempdir;
use criterion::Criterion;
use ethash::progpow;
use tempdir::TempDir;
use ethash::{compute::light_compute, NodeCacheBuilder, OptimizeFor};
use rustc_hex::FromHex;
use ethash::{NodeCacheBuilder, OptimizeFor};
use ethash::compute::light_compute;
use tempdir::TempDir;
fn bench_hashimoto_light(c: &mut Criterion) {
let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value());
let tempdir = TempDir::new("").unwrap();
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];
hash.copy_from_slice(&h);
@ -30,20 +30,15 @@ fn bench_progpow_light(c: &mut Criterion) {
let tempdir = TempDir::new("").unwrap();
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];
hash.copy_from_slice(&h);
c.bench_function("progpow_light", move |b| {
b.iter(|| {
let c_dag = progpow::generate_cdag(cache.as_ref());
progpow::progpow(
hash,
0,
0,
cache.as_ref(),
&c_dag,
);
progpow::progpow(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 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];
hash.copy_from_slice(&h);
c.bench_function("progpow_optimal_light", move |b| {
b.iter(|| {
progpow::progpow(
hash,
0,
0,
cache.as_ref(),
&c_dag,
);
progpow::progpow(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_progpow_light,
bench_progpow_optimal_light,

View File

@ -16,19 +16,21 @@
use compute::Light;
use either::Either;
use keccak::{H256, keccak_512};
use keccak::{keccak_512, H256};
use memmap::MmapMut;
use parking_lot::Mutex;
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::fs;
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::slice;
use std::sync::Arc;
use std::{
borrow::Cow,
fs,
io::{self, Read, Write},
path::{Path, PathBuf},
slice,
sync::Arc,
};
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(),
};
memmap.map(Either::Right).unwrap_or_else(|| {
Either::Left(make_memory_cache(num_nodes, ident))
})
memmap
.map(Either::Right)
.unwrap_or_else(|| Either::Left(make_memory_cache(num_nodes, ident)))
}
#[derive(Clone)]
@ -94,7 +96,7 @@ impl NodeCacheBuilder {
NodeCacheBuilder {
seedhash: Arc::new(Mutex::new(SeedHashCompute::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<()> {
if let Some(last) = self.epoch.checked_sub(2).map(|ep| {
cache_path(self.cache_dir.as_ref(), &self.builder.epoch_to_ident(ep))
})
if let Some(last) = self
.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() {
io::ErrorKind::NotFound => (),
@ -234,9 +237,7 @@ fn consume_cache(cache: &mut Cache, path: &Path) -> io::Result<()> {
file.write_all(buf).map(|_| ())
}
Either::Right(ref mmap) => {
mmap.flush()
}
Either::Right(ref mmap) => mmap.flush(),
}
}
@ -244,25 +245,31 @@ fn cache_from_path(path: &Path, optimize_for: OptimizeFor) -> io::Result<Cache>
let memmap = match optimize_for {
OptimizeFor::Cpu => None,
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() }
},
}
};
memmap.map(Either::Right).ok_or(()).or_else(|_| {
read_from_path(path).map(Either::Left)
})
memmap
.map(Either::Right)
.ok_or(())
.or_else(|_| read_from_path(path).map(Either::Left))
}
fn read_from_path(path: &Path) -> io::Result<Vec<Node>> {
use std::fs::File;
use std::mem;
use std::{fs::File, mem};
let mut file = File::open(path)?;
let mut nodes: Vec<u8> = Vec::with_capacity(file.metadata().map(|m| m.len() as _).unwrap_or(
NODE_BYTES * 1_000_000,
));
let mut nodes: Vec<u8> = Vec::with_capacity(
file.metadata()
.map(|m| m.len() as _)
.unwrap_or(NODE_BYTES * 1_000_000),
);
file.read_to_end(&mut nodes)?;
nodes.shrink_to_fit();

View File

@ -19,15 +19,14 @@
// TODO: fix endianess for big endian
use keccak::{keccak_512, keccak_256, H256};
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 shared::*;
use std::io;
use std::{mem, ptr};
use std::path::Path;
use std::{mem, path::Path, ptr};
const MIX_WORDS: usize = ETHASH_MIX_BYTES / 4;
const MIX_NODES: usize = MIX_WORDS / NODE_WORDS;
@ -68,7 +67,11 @@ impl Light {
Algorithm::Hashimoto
};
Light { block_number, cache, algorithm }
Light {
block_number,
cache,
algorithm,
}
}
/// Calculate the light boundary data
@ -86,10 +89,9 @@ impl Light {
);
ProofOfWork { value, mix_hash }
},
}
Algorithm::Hashimoto => light_compute(self, header_hash, nonce),
}
}
pub fn from_file_with_builder(
@ -106,7 +108,11 @@ impl Light {
Algorithm::Hashimoto
};
Ok(Light { block_number, cache, algorithm })
Ok(Light {
block_number,
cache,
algorithm,
})
}
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
/// `mix_hash` The mix digest 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 {
if progpow {
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
// transmuted val dies.
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>());
&mut *(val.as_mut_ptr() as *mut [U; $n])
}
make_const_array($value)
}}
}};
}
#[repr(C)]
@ -318,7 +329,10 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64)
buf.compress_bytes
};
ProofOfWork { mix_hash: mix_hash, value: value }
ProofOfWork {
mix_hash: mix_hash,
value: value,
}
}
// 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);
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]) %
num_parent_nodes as u32;
let parent_index = fnv_hash(node_index ^ i, ret.as_words()[i as usize % NODE_WORDS])
% num_parent_nodes as u32;
let parent = &cache[parent_index as usize];
unroll! {
@ -363,7 +377,10 @@ mod test {
assert_eq!(16907456usize, get_cache_size(ETHASH_EPOCH_LENGTH + 1));
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(2048 * ETHASH_EPOCH_LENGTH - 1));
assert_eq!(
285081536usize,
get_cache_size(2048 * ETHASH_EPOCH_LENGTH - 1)
);
}
#[test]
@ -396,7 +413,10 @@ mod test {
0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a,
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 = [
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,
@ -436,16 +456,28 @@ mod test {
fn test_drop_old_data() {
let tempdir = TempDir::new("").unwrap();
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());
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(&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());
}
}

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
// means that we can reuse the input buffer for both input and output.
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
// means that we can reuse the input buffer for both input and output.
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"))]
mod compute;
mod seed_compute;
mod cache;
mod keccak;
mod seed_compute;
mod shared;
#[cfg(feature = "bench")]
@ -50,15 +50,17 @@ pub mod progpow;
mod progpow;
pub use cache::{NodeCacheBuilder, OptimizeFor};
pub use compute::{ProofOfWork, quick_get_difficulty, slow_hash_block_number};
use compute::Light;
pub use compute::{quick_get_difficulty, slow_hash_block_number, ProofOfWork};
use ethereum_types::{U256, U512};
use keccak::H256;
use parking_lot::Mutex;
pub use seed_compute::SeedHashCompute;
pub use shared::ETHASH_EPOCH_LENGTH;
use std::mem;
use std::path::{Path, PathBuf};
use std::{
mem,
path::{Path, PathBuf},
};
use std::sync::Arc;
@ -79,10 +81,17 @@ pub struct EthashManager {
impl EthashManager {
/// 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 {
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,
cache: Mutex::new(LightCache {
recent_epoch: None,
@ -131,17 +140,15 @@ impl EthashManager {
match light {
None => {
let light = match self.nodecache_builder.light_from_file(
&self.cache_dir,
block_number,
) {
let light = match self
.nodecache_builder
.light_from_file(&self.cache_dir, block_number)
{
Ok(light) => Arc::new(light),
Err(e) => {
debug!("Light cache file not found for {}:{}", block_number, e);
let mut light = self.nodecache_builder.light(
&self.cache_dir,
block_number,
);
let mut light =
self.nodecache_builder.light(&self.cache_dir, block_number);
if let Err(e) = light.to_file() {
warn!("Light cache file write error: {}", e);
}
@ -206,10 +213,22 @@ fn test_difficulty_to_boundary() {
use ethereum_types::H256;
use std::str::FromStr;
assert_eq!(difficulty_to_boundary(&U256::from(1)), H256::from(U256::max_value()));
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());
assert_eq!(
difficulty_to_boundary(&U256::from(1)),
H256::from(U256::max_value())
);
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]
@ -219,10 +238,22 @@ fn test_difficulty_to_boundary_regression() {
// the last bit was originally being truncated when performing the conversion
// https://github.com/paritytech/parity-ethereum/issues/8397
for difficulty in 1..9 {
assert_eq!(U256::from(difficulty), boundary_to_difficulty(&difficulty_to_boundary(&difficulty.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()));
assert_eq!(
U256::from(difficulty),
boundary_to_difficulty(&difficulty_to_boundary(&difficulty.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
// 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 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_WORDS: usize = PROGPOW_CACHE_BYTES / 4;
@ -32,20 +32,17 @@ const PROGPOW_REGS: usize = 32;
const FNV_HASH: u32 = 0x811c9dc5;
const KECCAKF_RNDC: [u32; 24] = [
0x00000001, 0x00008082, 0x0000808a, 0x80008000, 0x0000808b, 0x80000001,
0x80008081, 0x00008009, 0x0000008a, 0x00000088, 0x80008009, 0x8000000a,
0x8000808b, 0x0000008b, 0x00008089, 0x00008003, 0x00008002, 0x00000080,
0x0000800a, 0x8000000a, 0x80008081, 0x00008080, 0x80000001, 0x80008008
0x00000001, 0x00008082, 0x0000808a, 0x80008000, 0x0000808b, 0x80000001, 0x80008081, 0x00008009,
0x0000008a, 0x00000088, 0x80008009, 0x8000000a, 0x8000808b, 0x0000008b, 0x00008089, 0x00008003,
0x00008002, 0x00000080, 0x0000800a, 0x8000000a, 0x80008081, 0x00008080, 0x80000001, 0x80008008,
];
const KECCAKF_ROTC: [u32; 24] = [
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
];
const KECCAKF_PILN: [usize; 24] = [
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
];
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]) {
for i in 0..8 {
st[i] = (header_hash[4 * i] as u32) +
((header_hash[4 * i + 1] as u32) << 8) +
((header_hash[4 * i + 2] as u32) << 16) +
((header_hash[4 * i + 3] as u32) << 24);
st[i] = (header_hash[4 * i] as u32)
+ ((header_hash[4 * i + 1] as u32) << 8)
+ ((header_hash[4 * i + 2] as u32) << 16)
+ ((header_hash[4 * i + 3] as u32) << 24);
}
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);
// NOTE: transmute from `[u32; 8]` to `[u8; 32]`
unsafe {
std::mem::transmute(
[st[0], st[1], st[2], st[3], st[4], st[5], st[6], st[7]]
)
}
unsafe { std::mem::transmute([st[0], st[1], st[2], st[3], st[4], st[5], st[6], st[7]]) }
}
#[inline]
@ -146,8 +139,12 @@ impl Kiss99 {
#[inline]
fn next_u32(&mut self) -> u32 {
self.z = 36969u32.wrapping_mul(self.z & 65535).wrapping_add(self.z >> 16);
self.w = 18000u32.wrapping_mul(self.w & 65535).wrapping_add(self.w >> 16);
self.z = 36969u32
.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);
self.jsr ^= self.jsr << 17;
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
// mix[0] to guarantee it depends on the load result.
let g_offset = mix[loop_ % PROGPOW_LANES][0] as usize %
(64 * data_size / (PROGPOW_LANES * PROGPOW_DAG_LOADS));
let g_offset = mix[loop_ % PROGPOW_LANES][0] as usize
% (64 * data_size / (PROGPOW_LANES * PROGPOW_DAG_LOADS));
// 256 bytes of dag data
let mut dag_item = [0u32; 64];
@ -354,14 +351,7 @@ pub fn progpow(
// Execute the randomly generated inner loop
let period = block_number / PROGPOW_PERIOD_LENGTH as u64;
for i in 0..PROGPOW_CNT_DAG {
progpow_loop(
period,
i,
&mut mix,
cache,
c_dag,
data_size,
);
progpow_loop(period, i, &mut mix, cache, c_dag, data_size);
}
// Reduce mix data to a single per-lane result
@ -403,12 +393,12 @@ pub fn generate_cdag(cache: &[Node]) -> CDag {
mod test {
use tempdir::TempDir;
use super::*;
use cache::{NodeCacheBuilder, OptimizeFor};
use keccak::H256;
use rustc_hex::FromHex;
use serde_json::{self, Value};
use std::collections::VecDeque;
use super::*;
fn h256(hex: &str) -> H256 {
let bytes = FromHex::from_hex(hex).unwrap();
@ -426,16 +416,29 @@ mod test {
let c_dag = generate_cdag(cache.as_ref());
let expected = vec![
690150178u32, 1181503948, 2248155602, 2118233073, 2193871115,
1791778428, 1067701239, 724807309, 530799275, 3480325829, 3899029234,
1998124059, 2541974622, 1100859971, 1297211151, 3268320000, 2217813733,
2690422980, 3172863319, 2651064309
690150178u32,
1181503948,
2248155602,
2118233073,
2193871115,
1791778428,
1067701239,
724807309,
530799275,
3480325829,
3899029234,
1998124059,
2541974622,
1100859971,
1297211151,
3268320000,
2217813733,
2690422980,
3172863319,
2651064309,
];
assert_eq!(
c_dag.iter().take(20).cloned().collect::<Vec<_>>(),
expected,
);
assert_eq!(c_dag.iter().take(20).cloned().collect::<Vec<_>>(), expected,);
}
#[test]
@ -452,10 +455,7 @@ mod test {
];
for (i, &(a, b, expected)) in tests.iter().enumerate() {
assert_eq!(
merge(a, b, i as u32),
expected,
);
assert_eq!(merge(a, b, i as u32), expected,);
}
}
@ -487,29 +487,20 @@ mod test {
];
for (i, &(a, b, expected)) in tests.iter().enumerate() {
assert_eq!(
math(a, b, i as u32),
expected,
);
assert_eq!(math(a, b, i as u32), expected,);
}
}
#[test]
fn test_keccak_256() {
let expected = "5dd431e5fbc604f499bfa0232f45f8f142d0ff5178f539e5a7800bf0643697af";
assert_eq!(
keccak_f800_long([0; 32], 0, [0; 8]),
h256(expected),
);
assert_eq!(keccak_f800_long([0; 32], 0, [0; 8]), h256(expected),);
}
#[test]
fn test_keccak_64() {
let expected: u64 = 0x5dd431e5fbc604f4;
assert_eq!(
keccak_f800_short([0; 32], 0, [0; 8]),
expected,
);
assert_eq!(keccak_f800_short([0; 32], 0, [0; 8]), expected,);
}
#[test]
@ -521,26 +512,18 @@ mod test {
let header_hash = [0; 32];
let (digest, result) = progpow(
header_hash,
0,
0,
cache.as_ref(),
&c_dag,
);
let (digest, result) = progpow(header_hash, 0, 0, cache.as_ref(), &c_dag);
let expected_digest = FromHex::from_hex("63155f732f2bf556967f906155b510c917e48e99685ead76ea83f4eca03ab12b").unwrap();
let expected_result = FromHex::from_hex("faeb1be51075b03a4ff44b335067951ead07a3b078539ace76fd56fc410557a3").unwrap();
let expected_digest =
FromHex::from_hex("63155f732f2bf556967f906155b510c917e48e99685ead76ea83f4eca03ab12b")
.unwrap();
let expected_result =
FromHex::from_hex("faeb1be51075b03a4ff44b335067951ead07a3b078539ace76fd56fc410557a3")
.unwrap();
assert_eq!(
digest.to_vec(),
expected_digest,
);
assert_eq!(digest.to_vec(), expected_digest,);
assert_eq!(
result.to_vec(),
expected_result,
);
assert_eq!(result.to_vec(), expected_result,);
}
#[test]
@ -556,11 +539,14 @@ mod test {
let tests: Vec<VecDeque<Value>> =
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);
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 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();
@ -572,7 +558,8 @@ mod test {
mix_hash: h256(&mix_hash),
final_hash: h256(&final_hash),
}
}).collect();
})
.collect();
for test in tests {
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
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use shared;
use keccak::{keccak_256, H256};
use shared;
use std::cell::Cell;
@ -71,7 +71,10 @@ mod tests {
#[test]
fn test_seed_compute_once() {
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);
}
@ -86,7 +89,10 @@ mod tests {
let seed_compute = SeedHashCompute::default();
// calculating an older value first shouldn't affect the result
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);
}
@ -95,8 +101,10 @@ mod tests {
let seed_compute = SeedHashCompute::default();
// calculating an newer value first shouldn't affect the result
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);
}
}

View File

@ -57,7 +57,8 @@ pub fn get_cache_size(block_number: u64) -> usize {
pub fn get_data_size(block_number: u64) -> usize {
// 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;
while !is_prime(sz / ETHASH_MIX_BYTES as u64) {
sz = sz - 2 * ETHASH_MIX_BYTES as u64;
@ -107,7 +108,11 @@ pub union Node {
impl Clone for Node {
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]
extern crate lazy_static;
extern crate ethcore_builtin;
extern crate ethcore;
extern crate ethcore_builtin;
extern crate ethereum_types;
extern crate parity_bytes as bytes;
extern crate rustc_hex;
use criterion::{Criterion, Bencher};
use bytes::BytesRef;
use criterion::{Bencher, Criterion};
use ethcore::{ethereum::new_byzantium_test_machine, machine::EthereumMachine};
use ethcore_builtin::Builtin;
use ethcore::machine::EthereumMachine;
use ethereum_types::U256;
use ethcore::ethereum::new_byzantium_test_machine;
use rustc_hex::FromHex;
lazy_static! {
@ -52,7 +51,9 @@ impl<'a> BuiltinBenchmark<'a> {
let expected = FromHex::from_hex(expected).unwrap();
BuiltinBenchmark {
builtin, input, expected
builtin,
input,
expected,
}
}
@ -60,20 +61,16 @@ impl<'a> BuiltinBenchmark<'a> {
let mut output = vec![0; self.expected.len()];
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[..]);
}
}
fn bench(
id: &str,
builtin_address: &'static str,
input: &str,
expected: &str,
b: &mut Criterion,
) {
fn bench(id: &str, builtin_address: &'static str, input: &str, expected: &str, b: &mut Criterion) {
let bench = BuiltinBenchmark::new(builtin_address, input, expected);
b.bench_function(id, move |b| bench.run(b));
}

View File

@ -16,8 +16,7 @@
use ethereum_types::{H256, U256};
use common_types::{encoded, BlockNumber};
use common_types::header::Header;
use common_types::{encoded, header::Header, BlockNumber};
/// 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
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use ethereum_types::{H256, U256};
use common_types::BlockNumber;
use ethereum_types::{H256, U256};
/// Brief info about inserted block.
#[derive(Clone)]
@ -27,7 +27,7 @@ pub struct BlockInfo {
/// Total block difficulty.
pub total_difficulty: U256,
/// Block location in blockchain.
pub location: BlockLocation
pub location: BlockLocation,
}
/// 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.
use ethereum_types::{Bloom, H256, U256};
use std::collections::VecDeque;
use ethereum_types::{U256, H256, Bloom};
use common_types::encoded;
use common_types::header::Header;
use common_types::transaction::{SignedTransaction, Transaction, Action};
use common_types::view;
use common_types::views::BlockView;
use common_types::{
encoded,
header::Header,
transaction::{Action, SignedTransaction, Transaction},
view,
views::BlockView,
};
use keccak_hash::keccak;
use rlp::encode;
use rlp_derive::RlpEncodable;
@ -37,7 +39,7 @@ pub struct Block {
/// Block transactions
pub transactions: Vec<SignedTransaction>,
/// Block uncles
pub uncles: Vec<Header>
pub uncles: Vec<Header>,
}
impl Block {
@ -105,9 +107,7 @@ impl BlockBuilder {
let mut blocks = VecDeque::with_capacity(1);
blocks.push_back(Block::default());
BlockBuilder {
blocks,
}
BlockBuilder { blocks }
}
/// Add new block with default options.
@ -124,13 +124,19 @@ impl BlockBuilder {
/// Add block with specified options.
#[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)
}
/// Add a block with given difficulty
#[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();
self.add_blocks_with(1, move || BlockOptions {
difficulty,
@ -155,8 +161,10 @@ impl BlockBuilder {
action: Action::Create,
value: 100.into(),
data,
}.sign(&keccak("").into(), None)
}).take(count);
}
.sign(&keccak("").into(), None)
})
.take(count);
self.add_block_with_transactions(transactions)
}
@ -164,7 +172,9 @@ impl BlockBuilder {
/// Add a block with given transactions.
#[inline]
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<_>>();
self.add_blocks_with(1, || BlockOptions {
transactions: transactions.clone(),
@ -182,7 +192,10 @@ impl BlockBuilder {
}
/// 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");
let mut parent_hash = self.last().hash();
let mut parent_number = self.last().number();
@ -207,15 +220,15 @@ impl BlockBuilder {
blocks.push_back(block);
}
BlockBuilder {
blocks,
}
BlockBuilder { blocks }
}
/// Get a reference to the last generated block.
#[inline]
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 {
/// 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 {
builders: builders.into_iter().collect(),
}
@ -244,18 +260,17 @@ impl Iterator for BlockGenerator {
if let Some(block) = builder.blocks.pop_front() {
return Some(block);
}
},
}
None => return None,
}
self.builders.pop_front();
}
}
}
#[cfg(test)]
mod tests {
use super::{BlockBuilder, BlockOptions, BlockGenerator};
use super::{BlockBuilder, BlockGenerator, BlockOptions};
#[test]
fn test_block_builder() {

View File

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

View File

@ -28,10 +28,12 @@ mod update;
pub mod generator;
pub use self::blockchain::{BlockProvider, BlockChain, BlockChainDB, BlockChainDBHandler};
pub use self::cache::CacheSize;
pub use self::config::Config;
pub use self::import_route::ImportRoute;
pub use self::update::ExtrasInsert;
pub use ethcore_db::keys::{BlockReceipts, BlockDetails, TransactionAddress, BlockNumberKey};
pub use self::{
blockchain::{BlockChain, BlockChainDB, BlockChainDBHandler, BlockProvider},
cache::CacheSize,
config::Config,
import_route::ImportRoute,
update::ExtrasInsert,
};
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 common_types::BlockNumber;
use common_types::encoded::Block;
use common_types::engines::ForkChoice;
use common_types::{encoded::Block, engines::ForkChoice, BlockNumber};
use ethcore_db::keys::{BlockDetails, BlockReceipts, TransactionAddress};
use ethereum_types::{H256, Bloom};
use ethereum_types::{Bloom, H256};
use crate::block_info::BlockInfo;

View File

@ -22,19 +22,19 @@ use std::{
cmp::{max, min},
collections::BTreeMap,
convert::{TryFrom, TryInto},
io::{self, Read, Cursor},
io::{self, Cursor, Read},
mem::size_of,
str::FromStr
str::FromStr,
};
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
use eip_152::compress;
use ethereum_types::{H256, U256};
use ethjson;
use ethkey::{Signature, recover as ec_recover};
use eip_152::compress;
use ethkey::{recover as ec_recover, Signature};
use keccak_hash::keccak;
use log::{warn, trace};
use num::{BigUint, Zero, One};
use log::{trace, warn};
use num::{BigUint, One, Zero};
use parity_bytes::BytesRef;
use parity_crypto::digest;
@ -149,7 +149,9 @@ impl Pricer for ModexpPricer {
// read lengths as U256 here for accurate gas calculation.
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[..]))
};
let base_len = read_len();
@ -157,14 +159,15 @@ impl Pricer for ModexpPricer {
let mod_len = read_len();
if mod_len.is_zero() && base_len.is_zero() {
return U256::zero()
return U256::zero();
}
let max_len = U256::from(u32::max_value() / 2);
if base_len > max_len || mod_len > max_len || exp_len > max_len {
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);
// read fist 32-byte word of the exponent.
@ -174,7 +177,9 @@ impl Pricer for ModexpPricer {
buf.iter_mut().for_each(|b| *b = 0);
let mut reader = input[(96 + base_len as usize)..].chain(io::repeat(0));
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[..]))
};
@ -190,7 +195,11 @@ impl Pricer for ModexpPricer {
impl ModexpPricer {
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 {
bit_index
} else {
@ -268,22 +277,18 @@ impl From<ethjson::spec::builtin::Pricing> for Pricing {
ethjson::spec::builtin::Pricing::Blake2F { gas_per_round } => {
Pricing::Blake2F(gas_per_round)
}
ethjson::spec::builtin::Pricing::Linear(linear) => {
Pricing::Linear(Linear {
ethjson::spec::builtin::Pricing::Linear(linear) => Pricing::Linear(Linear {
base: linear.base,
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 {
warn!(target: "builtin", "Zero modexp divisor specified. Falling back to default: 10.");
10
} else {
exp.divisor
}
})
}
},
}),
ethjson::spec::builtin::Pricing::AltBn128Pairing(pricer) => {
Pricing::AltBn128Pairing(AltBn128PairingPricer {
price: AltBn128PairingPrice {
@ -294,7 +299,7 @@ impl From<ethjson::spec::builtin::Pricing> for Pricing {
}
ethjson::spec::builtin::Pricing::AltBn128ConstOperations(pricer) => {
Pricing::AltBn128ConstOperations(AltBn128ConstOperations {
price: pricer.price
price: pricer.price,
})
}
}
@ -320,7 +325,7 @@ enum EthereumBuiltin {
/// alt_bn128_pairing
Bn128Pairing(Bn128Pairing),
/// blake2_f (The Blake2 compression function F, EIP-152)
Blake2F(Blake2F)
Blake2F(Blake2F),
}
impl FromStr for EthereumBuiltin {
@ -406,7 +411,9 @@ impl Implementation for EcRecover {
let bit = match v[31] {
27 | 28 if v.0[..31] == [0; 31] => v[31] - 27,
_ => { return Ok(()); },
_ => {
return Ok(());
}
};
let s = Signature::from_rsv(&r, &s, bit);
@ -439,7 +446,7 @@ impl Implementation for Blake2F {
if input.len() != BLAKE2_F_ARG_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);
@ -469,7 +476,7 @@ impl Implementation for Blake2F {
Some(0) => false,
_ => {
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>()];
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[..]);
Ok(())
@ -518,7 +525,9 @@ fn modexp(mut base: BigUint, exp: Vec<u8>, modulus: BigUint) -> BigUint {
base %= &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).
// 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,
// but so would running out of addressable memory!
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];
len_bytes.copy_from_slice(&buf[24..]);
u64::from_be_bytes(len_bytes) as usize
@ -567,14 +578,18 @@ impl Implementation for Modexp {
// read the numbers themselves.
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| {
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])
};
let base = read_num(&mut reader, base_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);
@ -598,27 +613,33 @@ impl Implementation for Modexp {
fn read_fr(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result<bn::Fr, &'static str> {
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")
}
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];
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")?;
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")?;
Ok(
if px == Fq::zero() && py == Fq::zero() {
Ok(if px == Fq::zero() && py == Fq::zero() {
G1::zero()
} 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 {
@ -633,8 +654,12 @@ impl Implementation for Bn128Add {
let mut write_buf = [0u8; 64];
if let Some(sum) = AffineG1::from_jacobian(p1 + p2) {
// 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.y().to_big_endian(&mut write_buf[32..64]).expect("Cannot fail since 32..64 is 32-byte length");
sum.x()
.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);
@ -654,8 +679,12 @@ impl Implementation for Bn128Mul {
let mut write_buf = [0u8; 64];
if let Some(sum) = AffineG1::from_jacobian(p * fr) {
// 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.y().to_big_endian(&mut write_buf[32..64]).expect("Cannot fail since 32..64 is 32-byte length");
sum.x()
.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);
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)
fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> {
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) {
trace!(target: "builtin", "Pairing error: {:?}", err);
return Err(err)
return Err(err);
}
Ok(())
}
@ -682,7 +711,7 @@ impl Implementation for Bn128Pairing {
impl Bn128Pairing {
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() {
U256::one()
@ -691,22 +720,22 @@ impl Bn128Pairing {
let elements = input.len() / 192;
let mut vals = Vec::new();
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")?;
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")?;
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")?;
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")?;
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")?;
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")?;
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() {
G2::zero()
} 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() {
G1::zero()
} 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));
};
}
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() {
U256::one()
@ -743,20 +778,20 @@ impl Bn128Pairing {
#[cfg(test)]
mod tests {
use std::convert::TryFrom;
use super::{
modexp as me, BTreeMap, Builtin, EthereumBuiltin, FromStr, Implementation, Linear,
ModexpPricer, Pricing,
};
use ethereum_types::U256;
use ethjson::spec::builtin::{
Builtin as JsonBuiltin, Linear as JsonLinearPricing,
PricingAt, AltBn128Pairing as JsonAltBn128PairingPricing, Pricing as JsonPricing,
AltBn128Pairing as JsonAltBn128PairingPricing, Builtin as JsonBuiltin,
Linear as JsonLinearPricing, Pricing as JsonPricing, PricingAt,
};
use hex_literal::hex;
use macros::map;
use num::{BigUint, Zero, One};
use num::{BigUint, One, Zero};
use parity_bytes::BytesRef;
use super::{
BTreeMap, Builtin, EthereumBuiltin, FromStr, Implementation, Linear,
ModexpPricer, modexp as me, Pricing
};
use std::convert::TryFrom;
#[test]
fn blake2f_cost() {
@ -767,9 +802,10 @@ mod tests {
// 5 rounds
let input = hex!("0000000548c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001");
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]
@ -793,7 +829,10 @@ mod tests {
let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..]));
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]
@ -805,7 +844,10 @@ mod tests {
let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..]));
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]
@ -827,7 +869,9 @@ mod tests {
let input = hex!("0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001");
let expected = hex!("08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b");
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[..]);
}
@ -838,7 +882,9 @@ mod tests {
let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001");
let expected = hex!("ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923");
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[..]);
}
@ -849,7 +895,9 @@ mod tests {
let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000");
let expected = hex!("75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735");
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[..]);
}
@ -860,7 +908,9 @@ mod tests {
let input = hex!("0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001");
let expected = hex!("b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421");
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[..]);
}
@ -873,7 +923,9 @@ mod tests {
let input = hex!("ffffffff48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001");
let expected = hex!("fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615");
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[..]);
}
@ -907,7 +959,10 @@ mod tests {
base = BigUint::parse_bytes(b"12345", 10).unwrap();
exp = BigUint::parse_bytes(b"789", 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]
@ -916,15 +971,18 @@ mod tests {
let i = [0u8, 1, 2, 3];
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);
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);
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!([255u8; 4], o8[4..]);
}
@ -935,20 +993,33 @@ mod tests {
let i = [0u8; 0];
let mut o = [255u8; 32];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
assert_eq!(&o[..], hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"));
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]))
.expect("Builtin should not fail");
assert_eq!(
&o[..],
hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
);
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"));
let mut o34 = [255u8; 34];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])).expect("Builtin should not fail");
assert_eq!(&o34[..], &hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffff")[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..]))
.expect("Builtin should not fail");
assert_eq!(
&o34[..],
&hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffff")[..]
);
let mut ov = vec![];
f.execute(&i[..], &mut BytesRef::Flexible(&mut ov)).expect("Builtin should not fail");
assert_eq!(&ov[..], &hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")[..]);
f.execute(&i[..], &mut BytesRef::Flexible(&mut ov))
.expect("Builtin should not fail");
assert_eq!(
&ov[..],
&hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")[..]
);
}
#[test]
@ -957,16 +1028,25 @@ mod tests {
let i = [0u8; 0];
let mut o = [255u8; 32];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
assert_eq!(&o[..], &hex!("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31")[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]))
.expect("Builtin should not fail");
assert_eq!(
&o[..],
&hex!("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31")[..]
);
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")[..]);
let mut o34 = [255u8; 34];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])).expect("Builtin should not fail");
assert_eq!(&o34[..], &hex!("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31ffff")[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..]))
.expect("Builtin should not fail");
assert_eq!(
&o34[..],
&hex!("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31ffff")[..]
);
}
#[test]
@ -976,41 +1056,70 @@ mod tests {
let i = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03");
let mut o = [255u8; 32];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
assert_eq!(&o[..], &hex!("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb")[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..]))
.expect("Builtin should not fail");
assert_eq!(
&o[..],
&hex!("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb")[..]
);
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")[..]);
let mut o34 = [255u8; 34];
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])).expect("Builtin should not fail");
assert_eq!(&o34[..], &hex!("000000000000000000000000c08b5542d177ac6686946920409741463a15dddbffff")[..]);
f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..]))
.expect("Builtin should not fail");
assert_eq!(
&o34[..],
&hex!("000000000000000000000000c08b5542d177ac6686946920409741463a15dddbffff")[..]
);
let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001a650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03");
let mut o = [255u8; 32];
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
assert_eq!(&o[..], &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]);
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..]))
.expect("Builtin should not fail");
assert_eq!(
&o[..],
&hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]
);
let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000");
let mut o = [255u8; 32];
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
assert_eq!(&o[..], &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]);
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..]))
.expect("Builtin should not fail");
assert_eq!(
&o[..],
&hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]
);
let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b");
let mut o = [255u8; 32];
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
assert_eq!(&o[..], &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]);
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..]))
.expect("Builtin should not fail");
assert_eq!(
&o[..],
&hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]
);
let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000001b");
let mut o = [255u8; 32];
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
assert_eq!(&o[..], &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]);
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..]))
.expect("Builtin should not fail");
assert_eq!(
&o[..],
&hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]
);
let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
let mut o = [255u8; 32];
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
assert_eq!(&o[..], &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]);
f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..]))
.expect("Builtin should not fail");
assert_eq!(
&o[..],
&hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..]
);
// TODO: Should this (corrupted version of the above) fail rather than returning some address?
/* let i_bad = FromHex::from_hex("48173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
@ -1035,7 +1144,8 @@ mod tests {
// test for potential exp len overflow
{
let input = hex!("
let input = hex!(
"
00000000000000000000000000000000000000000000000000000000000000ff
2a1e530000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000"
@ -1045,14 +1155,16 @@ mod tests {
let expected = hex!("0000000000000000000000000000000000000000000000000000000000000000");
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!(f.cost(&input[..], 0), expected_cost);
}
// fermat's little theorem example.
{
let input = hex!("
let input = hex!(
"
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000020
@ -1065,14 +1177,16 @@ mod tests {
let expected = hex!("0000000000000000000000000000000000000000000000000000000000000001");
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!(f.cost(&input[..], 0), expected_cost.into());
}
// second example from EIP: zero base.
{
let input = hex!("
let input = hex!(
"
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000020
@ -1084,14 +1198,16 @@ mod tests {
let expected = hex!("0000000000000000000000000000000000000000000000000000000000000000");
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!(f.cost(&input[..], 0), expected_cost.into());
}
// another example from EIP: zero-padding
{
let input = hex!("
let input = hex!(
"
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000020
@ -1104,14 +1220,16 @@ mod tests {
let expected = hex!("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab");
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!(f.cost(&input[..], 0), expected_cost.into());
}
// zero-length modulus.
{
let input = hex!("
let input = hex!(
"
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000002
0000000000000000000000000000000000000000000000000000000000000000
@ -1122,7 +1240,8 @@ mod tests {
let mut output = vec![];
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!(f.cost(&input[..], 0), expected_cost.into());
}
@ -1130,7 +1249,6 @@ mod tests {
#[test]
fn bn128_add() {
let f = Builtin {
pricer: map![0 => Pricing::Linear(Linear { base: 0, word: 0 })],
native: EthereumBuiltin::from_str("alt_bn128_add").unwrap(),
@ -1138,7 +1256,8 @@ mod tests {
// zero-points additions
{
let input = hex!("
let input = hex!(
"
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
@ -1146,12 +1265,14 @@ mod tests {
);
let mut output = vec![0u8; 64];
let expected = hex!("
let expected = hex!(
"
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[..]);
}
@ -1161,18 +1282,21 @@ mod tests {
let input = BytesRef::Fixed(&mut empty);
let mut output = vec![0u8; 64];
let expected = hex!("
let expected = hex!(
"
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[..]);
}
// should fail - point not on curve
{
let input = hex!("
let input = hex!(
"
1111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111
@ -1195,25 +1319,29 @@ mod tests {
// zero-point multiplication
{
let input = hex!("
let input = hex!(
"
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0200000000000000000000000000000000000000000000000000000000000000"
);
let mut output = vec![0u8; 64];
let expected = hex!("
let expected = hex!(
"
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[..]);
}
// should fail - point not on curve
{
let input = hex!("
let input = hex!(
"
1111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111
0f00000000000000000000000000000000000000000000000000000000000000"
@ -1239,7 +1367,8 @@ mod tests {
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);
}
@ -1249,7 +1378,10 @@ mod tests {
if let Some(msg) = msg_contains {
if let Err(e) = res {
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 {
@ -1271,7 +1403,8 @@ mod tests {
// should fail - point not on curve
error_test(
builtin_pairing(),
&hex!("
&hex!(
"
1111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111
@ -1288,7 +1421,8 @@ mod tests {
// should fail - input length is invalid
error_test(
builtin_pairing(),
&hex!("
&hex!(
"
1111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111
111111111111111111111111111111"
@ -1331,7 +1465,8 @@ mod tests {
let i = [0u8, 1, 2, 3];
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);
}
@ -1344,8 +1479,9 @@ mod tests {
info: None,
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; 1], 0), U256::from(30));
@ -1354,7 +1490,8 @@ mod tests {
let i = [0u8, 1, 2, 3];
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);
}
@ -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!(b.cost(&[0; 192 * 7], 20), U256::from(283_000), "34 000 * 7 + 45 000 == 283 000");
assert_eq!(
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]
@ -1404,10 +1550,15 @@ mod tests {
}),
}
],
}).unwrap();
})
.unwrap();
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]
@ -1430,13 +1581,17 @@ mod tests {
}),
}
],
}).unwrap();
})
.unwrap();
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]
fn multimap_use_most_recent_on_activate() {
let b = Builtin::try_from(JsonBuiltin {
@ -1463,18 +1618,26 @@ mod tests {
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; 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], 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]
fn multimap_use_last_with_same_activate_at() {
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], 1), U256::from(1_337));

View File

@ -16,8 +16,10 @@
//! Database cache manager
use std::collections::{VecDeque, HashSet};
use std::hash::Hash;
use std::{
collections::{HashSet, VecDeque},
hash::Hash,
};
const COLLECTION_QUEUE_SIZE: usize = 8;
@ -26,24 +28,39 @@ pub struct CacheManager<T> {
pref_cache_size: usize,
max_cache_size: 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.
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 {
pref_cache_size: pref_cache_size,
max_cache_size: max_cache_size,
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.
pub fn note_used(&mut self, id: T) {
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);
}
self.cache_usage[0].insert(id);
@ -53,7 +70,10 @@ impl<T> CacheManager<T> where T: Eq + Hash {
/// Collects unused objects from 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.
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 {
self.rotate_cache_if_needed();
return;
@ -64,16 +84,20 @@ impl<T> CacheManager<T> where T: Eq + Hash {
let current_size = notify_unused(back);
self.cache_usage.push_front(Default::default());
if current_size < self.max_cache_size {
break
break;
}
}
}
}
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() {
self.cache_usage.push_front(cache);
}

View File

@ -16,11 +16,9 @@
//! 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 parking_lot::RwLock;
use std::{collections::HashMap, hash::Hash, ops::Deref};
use rlp;
@ -65,7 +63,10 @@ pub trait Cache<K, 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> {
HashMap::insert(self, k, v)
}
@ -91,21 +92,35 @@ pub trait Key<T> {
/// Should be used to write value into database.
pub trait Writable {
/// 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.
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.
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,
T: rlp::Encodable,
R: Deref<Target = [u8]> {
R: Deref<Target = [u8]>,
{
self.write(col, &key, &value);
match policy {
CacheUpdatePolicy::Overwrite => {
cache.insert(key, value);
},
}
CacheUpdatePolicy::Remove => {
cache.remove(&key);
}
@ -113,31 +128,45 @@ pub trait Writable {
}
/// 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,
T: rlp::Encodable,
R: Deref<Target = [u8]> {
R: Deref<Target = [u8]>,
{
match policy {
CacheUpdatePolicy::Overwrite => {
for (key, value) in values {
self.write(col, &key, &value);
cache.insert(key, value);
}
},
}
CacheUpdatePolicy::Remove => {
for (key, value) in &values {
self.write(col, key, value);
cache.remove(key);
}
},
}
}
}
/// 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,
T: rlp::Encodable,
R: Deref<Target = [u8]> {
R: Deref<Target = [u8]>,
{
match policy {
CacheUpdatePolicy::Overwrite => {
for (key, value) in values {
@ -147,7 +176,7 @@ pub trait Writable {
}
cache.insert(key, value);
}
},
}
CacheUpdatePolicy::Remove => {
for (key, value) in values {
match value {
@ -156,24 +185,26 @@ pub trait Writable {
}
cache.remove(&key);
}
},
}
}
}
}
/// Should be used to read values from database.
pub trait Readable {
/// 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,
R: Deref<Target = [u8]>;
/// 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,
T: Clone + rlp::Decodable,
C: Cache<K, T> {
C: Cache<K, T>,
{
{
let read = cache.read();
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();
write.insert(key.clone(), value.clone());
value
@ -189,10 +220,18 @@ pub trait Readable {
}
/// 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,
T: Clone + rlp::Decodable,
C: Cache<K, T> {
C: Cache<K, T>,
{
{
let read = l1_cache.read();
if let Some(v) = read.get(key) {
@ -204,13 +243,17 @@ pub trait Readable {
}
/// 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.
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>,
R: Deref<Target = [u8]>,
C: Cache<K, T> {
C: Cache<K, T>,
{
{
let read = cache.read();
if read.get(key).is_some() {
@ -223,31 +266,48 @@ pub trait Readable {
}
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));
}
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());
}
}
impl<KVDB: KeyValueDB + ?Sized> Readable for KVDB {
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())
.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());
match result {
Ok(v) => v.is_some(),
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.
use std::io::Write;
use std::ops;
use std::{io::Write, ops};
use common_types::BlockNumber;
use common_types::engines::epoch::Transition as EpochTransition;
use common_types::receipt::Receipt;
use common_types::{engines::epoch::Transition as EpochTransition, receipt::Receipt, BlockNumber};
use ethereum_types::{H256, H264, U256};
use heapsize::HeapSizeOf;
use kvdb::PREFIX_LEN as DB_PREFIX_LEN;
use rlp;
use rlp_derive::{RlpEncodableWrapper, RlpDecodableWrapper, RlpEncodable, RlpDecodable};
use rlp_derive::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper};
use crate::db::Key;
@ -117,7 +114,18 @@ pub const EPOCH_KEY_LEN: usize = DB_PREFIX_LEN + 16;
/// epoch key prefix.
/// used to iterate over all epoch transitions in order from genesis.
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
@ -126,7 +134,9 @@ pub struct EpochTransitionsKey([u8; EPOCH_KEY_LEN]);
impl ops::Deref for EpochTransitionsKey {
type Target = [u8];
fn deref(&self) -> &[u8] { &self.0[..] }
fn deref(&self) -> &[u8] {
&self.0[..]
}
}
impl Key<EpochTransitions> for u64 {
@ -163,8 +173,12 @@ impl rlp::Encodable for BlockDetails {
let use_short_version = !self.is_finalized;
match use_short_version {
true => { stream.begin_list(4); },
false => { stream.begin_list(5); },
true => {
stream.begin_list(4);
}
false => {
stream.begin_list(5);
}
}
stream.append(&self.number);
@ -211,11 +225,13 @@ pub struct TransactionAddress {
/// Block hash
pub block_hash: H256,
/// Transaction index within the block
pub index: usize
pub index: usize,
}
impl HeapSizeOf for TransactionAddress {
fn heap_size_of_children(&self) -> usize { 0 }
fn heap_size_of_children(&self) -> usize {
0
}
}
/// Contains all block receipts.
@ -228,9 +244,7 @@ pub struct BlockReceipts {
impl BlockReceipts {
/// Create new block receipts wrapper.
pub fn new(receipts: Vec<Receipt>) -> Self {
BlockReceipts {
receipts: receipts
}
BlockReceipts { receipts: receipts }
}
}

View File

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

View File

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

View File

@ -16,9 +16,9 @@
//! Evm interface.
use std::{ops, cmp, fmt};
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
/// a return instruction.
@ -44,18 +44,22 @@ pub trait Finalize {
impl Finalize for Result<GasLeft> {
fn finalize<E: Ext>(self, ext: E) -> Result<FinalizationResult> {
match self {
Ok(GasLeft::Known(gas_left)) => {
Ok(FinalizationResult {
Ok(GasLeft::Known(gas_left)) => Ok(FinalizationResult {
gas_left,
apply_state: true,
return_data: ReturnData::empty()
})
},
Ok(GasLeft::NeedsReturn { gas_left, data, apply_state }) => {
ext.ret(&gas_left, &data, apply_state).map(|gas_left|
FinalizationResult { gas_left, apply_state, return_data: data }
)
},
return_data: ReturnData::empty(),
}),
Ok(GasLeft::NeedsReturn {
gas_left,
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),
}
}
@ -68,10 +72,20 @@ impl Finalize for Error {
}
/// Cost calculation type. For low-gas usage we calculate costs using usize instead of U256
pub trait CostType: Sized + From<usize> + 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 {
pub trait CostType:
Sized
+ From<usize>
+ 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`
fn as_u256(&self) -> U256;
/// Tries to fit `U256` into this `Cost` type
@ -113,10 +127,7 @@ impl CostType for U256 {
let U512(parts) = x;
let overflow = (parts[4] | parts[5] | parts[6] | parts[7]) > 0;
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)]
mod tests {
use ethereum_types::U256;
use super::CostType;
use ethereum_types::U256;
#[test]
fn should_calculate_overflow_mul_shr_without_overflow() {

View File

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

View File

@ -440,13 +440,18 @@ pub struct InstructionInfo {
/// Number of returned stack items.
pub ret: usize,
/// Gas price tier.
pub tier: GasPriceTier
pub tier: GasPriceTier,
}
impl InstructionInfo {
/// Create new instruction info.
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
// 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 ethereum_types::{H256, U256};
use std::cmp;
use {evm, vm};
use evm;
use instructions::{self, Instruction, InstructionInfo};
use interpreter::stack::Stack;
use vm::Schedule;
use vm::{self, Schedule};
macro_rules! overflowing {
($x: expr) => {{
let (v, overflow) = $x;
if overflow { return Err(vm::Error::OutOfGas); }
if overflow {
return Err(vm::Error::OutOfGas);
}
v
}}
}};
}
enum Request<Cost: ::evm::CostType> {
Gas(Cost),
GasMem(Cost, Cost),
GasMemProvide(Cost, Cost, Option<U256>),
GasMemCopy(Cost, Cost, Cost)
GasMemCopy(Cost, Cost, Cost),
}
pub struct InstructionRequirements<Cost> {
@ -51,7 +53,6 @@ pub struct Gasometer<Gas> {
}
impl<Gas: evm::CostType> Gasometer<Gas> {
pub fn new(current_gas: Gas) -> Self {
Gasometer {
current_gas: current_gas,
@ -62,13 +63,18 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
pub fn verify_gas(&self, gas_cost: &Gas) -> vm::Result<()> {
match &self.current_gas < gas_cost {
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
/// 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`)
// but in EIP150 even if we request more we should never fail from OOG
let requested = requested.map(Gas::from_u256);
@ -86,7 +92,7 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
} else {
Ok(max_gas_provided)
}
},
}
_ => {
if let Some(r) = requested {
r
@ -95,7 +101,7 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
} else {
Ok(0.into())
}
},
}
}
}
@ -117,9 +123,7 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
let default_gas = Gas::from(schedule.tier_step_gas[tier]);
let cost = match instruction {
instructions::JUMPDEST => {
Request::Gas(Gas::from(1))
},
instructions::JUMPDEST => Request::Gas(Gas::from(1)),
instructions::SSTORE => {
if schedule.eip1706 && self.current_gas <= Gas::from(schedule.call_stipend) {
return Err(vm::Error::OutOfGas);
@ -141,77 +145,87 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
}
};
Request::Gas(Gas::from(gas))
},
instructions::SLOAD => {
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::EXTCODEHASH => {
Request::Gas(Gas::from(schedule.extcodehash_gas))
},
}
instructions::SLOAD => 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::EXTCODEHASH => Request::Gas(Gas::from(schedule.extcodehash_gas)),
instructions::SUICIDE => {
let mut gas = Gas::from(schedule.suicide_gas);
let is_value_transfer = !ext.origin_balance()?.is_zero();
let address = u256_to_address(stack.peek(0));
if (
!schedule.no_empty && !ext.exists(&address)?
) || (
schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address)?
) {
gas = overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into()));
if (!schedule.no_empty && !ext.exists(&address)?)
|| (schedule.no_empty
&& is_value_transfer
&& !ext.exists_and_not_null(&address)?)
{
gas =
overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into()));
}
Request::Gas(gas)
},
}
instructions::MSTORE | instructions::MLOAD => {
Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 32)?)
},
}
instructions::MSTORE8 => {
Request::GasMem(default_gas, mem_needed_const(stack.peek(0), 1)?)
},
}
instructions::RETURN | instructions::REVERT => {
Request::GasMem(default_gas, mem_needed(stack.peek(0), stack.peek(1))?)
},
}
instructions::SHA3 => {
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))?)
},
}
instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => {
Request::GasMemCopy(default_gas, mem_needed(stack.peek(0), stack.peek(2))?, Gas::from_u256(*stack.peek(2))?)
},
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");
Request::GasMemCopy(
default_gas,
mem_needed(stack.peek(0), stack.peek(2))?,
Gas::from_u256(*stack.peek(2))?,
)
}
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 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)));
Request::GasMem(gas, mem_needed(stack.peek(0), stack.peek(1))?)
},
}
instructions::CALL | instructions::CALLCODE => {
let mut gas = Gas::from(schedule.call_gas);
let mem = cmp::max(
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 is_value_transfer = !stack.peek(2).is_zero();
if instruction == instructions::CALL && (
(!schedule.no_empty && !ext.exists(&address)?)
||
(schedule.no_empty && is_value_transfer && !ext.exists_and_not_null(&address)?)
) {
if instruction == instructions::CALL
&& ((!schedule.no_empty && !ext.exists(&address)?)
|| (schedule.no_empty
&& is_value_transfer
&& !ext.exists_and_not_null(&address)?))
{
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);
Request::GasMemProvide(gas, mem, Some(requested))
},
}
instructions::DELEGATECALL | instructions::STATICCALL => {
let gas = Gas::from(schedule.call_gas);
let mem = cmp::max(
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);
Request::GasMemProvide(gas, mem, Some(requested))
},
}
instructions::CREATE => {
let start = stack.peek(1);
let len = stack.peek(2);
@ -241,7 +255,7 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
let mem = mem_needed(start, len)?;
Request::GasMemProvide(gas, mem, None)
},
}
instructions::CREATE2 => {
let start = stack.peek(1);
let len = stack.peek(2);
@ -253,30 +267,27 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
let mem = mem_needed(start, len)?;
Request::GasMemProvide(gas, mem, None)
},
}
instructions::EXP => {
let expon = stack.peek(1);
let bytes = ((expon.bits() + 7) / 8) as usize;
let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes);
Request::Gas(gas)
},
instructions::BLOCKHASH => {
Request::Gas(Gas::from(schedule.blockhash_gas))
},
}
instructions::BLOCKHASH => Request::Gas(Gas::from(schedule.blockhash_gas)),
_ => Request::Gas(default_gas),
};
Ok(match cost {
Request::Gas(gas) => {
InstructionRequirements {
Request::Gas(gas) => InstructionRequirements {
gas_cost: gas,
provide_gas: None,
memory_required_size: 0,
memory_total_gas: self.current_mem_gas,
}
},
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));
InstructionRequirements {
gas_cost: gas,
@ -284,9 +295,10 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
memory_required_size: new_mem_size,
memory_total_gas: new_mem_gas,
}
},
}
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 provided = self.gas_provided(schedule, gas, requested)?;
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_total_gas: new_mem_gas,
}
},
}
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_gas = overflowing!(Gas::from(schedule.copy_gas).overflow_mul(copy));
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_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 s = mem_size >> 5;
// s * memory_gas + s * s / quad_coeff_div
@ -371,9 +389,13 @@ fn to_word_size<Gas: evm::CostType>(value: Gas) -> (Gas, bool) {
}
#[inline]
fn calculate_eip1283_sstore_gas<Gas: evm::CostType>(schedule: &Schedule, original: &U256, current: &U256, new: &U256) -> Gas {
Gas::from(
if current == new {
fn calculate_eip1283_sstore_gas<Gas: evm::CostType>(
schedule: &Schedule,
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.
schedule.sload_gas
} 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.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;
if current == new {
@ -479,7 +505,9 @@ fn test_calculate_mem_cost() {
let mem_size = 5;
// 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
assert_eq!(mem_cost, 3);

View File

@ -20,7 +20,7 @@ pub use self::inner::*;
#[cfg(not(feature = "evm-debug"))]
mod inner {
macro_rules! evm_debug {
($x: expr) => {}
($x: expr) => {};
}
pub struct EvmInformant;
@ -35,20 +35,22 @@ mod inner {
#[macro_use]
#[cfg(feature = "evm-debug")]
mod inner {
use std::iter;
use std::collections::HashMap;
use std::time::{Instant, Duration};
use std::{
collections::HashMap,
iter,
time::{Duration, Instant},
};
use ethereum_types::U256;
use interpreter::stack::Stack;
use instructions::{Instruction, InstructionInfo};
use interpreter::stack::Stack;
use CostType;
macro_rules! evm_debug {
($x: expr) => {
$x
}
};
}
fn print(data: String) {
@ -66,7 +68,6 @@ mod inner {
}
impl EvmInformant {
fn color(instruction: Instruction, name: &str) -> String {
let c = instruction as usize % 6;
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();
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,
pc,
Self::color(instruction, info.name),
@ -110,20 +119,24 @@ mod inner {
}
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();
stats.note(took);
}
pub fn done(&mut self) {
// 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()));
print(format!("\n{}-------OPCODE STATS:", self.spacing));
for (instruction, stats) in stats.into_iter() {
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::color(instruction, info.name),
instruction as u8,
@ -132,7 +145,6 @@ mod inner {
));
}
}
}
struct Stats {

View File

@ -37,7 +37,7 @@ pub trait Memory {
/// Retrieve part of the memory between offset and offset + size
fn read_slice(&self, offset: U256, size: U256) -> &[u8];
/// 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.
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) {
&self[0..0]
} else {
&self[off..off+size]
&self[off..off + size]
}
}
fn read(&self, offset: U256) -> U256 {
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] {
@ -75,20 +75,20 @@ impl Memory for Vec<u8> {
if !is_valid_range(off, s) {
&mut self[0..0]
} else {
&mut self[off..off+s]
&mut self[off..off + s]
}
}
fn write_slice(&mut self, offset: U256, slice: &[u8]) {
if !slice.is_empty() {
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) {
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) {
@ -130,8 +130,8 @@ impl Memory for Vec<u8> {
#[cfg(test)]
mod tests {
use ethereum_types::U256;
use super::Memory;
use ethereum_types::U256;
#[test]
fn test_memory_read_and_write() {
@ -178,7 +178,10 @@ mod tests {
let slice = "67890".as_bytes();
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

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
// 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 heapsize::HeapSizeOf;
use ethereum_types::H256;
use parking_lot::Mutex;
use memory_cache::MemoryLruCache;
use bit_set::BitSet;
use super::super::instructions::{self, Instruction};
use parking_lot::Mutex;
use std::sync::Arc;
const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024;
@ -64,7 +64,9 @@ impl SharedCache {
let d = Self::find_jump_destinations(code);
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

View File

@ -14,8 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::fmt;
use instructions;
use std::fmt;
/// Stack trait with VM-friendly API
pub trait Stack<T> {
@ -39,19 +39,19 @@ pub trait Stack<T> {
pub struct VecStack<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 {
VecStack {
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 {
&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 {
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] {
@ -87,7 +89,10 @@ impl<S : fmt::Display> Stack<S> for VecStack<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.");
&self.stack[self.stack.len() - no_from_top .. self.stack.len()]
assert!(
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 ethereum_types;
extern crate parking_lot;
extern crate heapsize;
extern crate vm;
extern crate keccak_hash as hash;
extern crate memory_cache;
extern crate parity_bytes as bytes;
extern crate num_bigint;
extern crate parity_bytes as bytes;
extern crate parking_lot;
extern crate vm;
#[macro_use]
extern crate lazy_static;
@ -32,28 +32,29 @@ extern crate lazy_static;
#[cfg_attr(feature = "evm-debug", macro_use)]
extern crate log;
#[cfg(test)]
extern crate rustc_hex;
#[cfg(test)]
extern crate hex_literal;
#[cfg(test)]
extern crate rustc_hex;
pub mod evm;
pub mod interpreter;
#[macro_use]
pub mod factory;
mod vmtype;
mod instructions;
mod vmtype;
#[cfg(test)]
mod tests;
pub use vm::{
Schedule, CleanDustMode, EnvInfo, CallType, ActionParams, Ext,
ContractCreateResult, MessageCallResult, CreateContractAddress,
GasLeft, ReturnData
pub use self::{
evm::{CostType, FinalizationResult, Finalize},
factory::Factory,
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)]
pub enum VMType {
/// RUST EVM
Interpreter
Interpreter,
}
impl fmt::Display for VMType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", match *self {
VMType::Interpreter => "INT"
})
write!(
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
//! 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::BlockNumber;
use common_types::receipt::Receipt;
use common_types::{encoded, receipt::Receipt, BlockNumber};
use ethereum_types::{H256, U256};
use heapsize::HeapSizeOf;
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
//! we discarded.
use bytes::Bytes;
use common_types::ids::BlockId;
use ethereum_types::{H256, U256};
use ethtrie::{self, TrieDB, TrieDBMut};
use hash_db::HashDB;
use journaldb::new_memory_db;
use keccak_hasher::KeccakHasher;
use kvdb::DBValue;
use memory_db::MemoryDB;
use journaldb::new_memory_db;
use bytes::Bytes;
use trie::{TrieMut, Trie, Recorder};
use ethtrie::{self, TrieDB, TrieDBMut};
use rlp::{RlpStream, Rlp};
use rlp::{Rlp, RlpStream};
use trie::{Recorder, Trie, TrieMut};
// encode a key.
macro_rules! key {
($num: expr) => { ::rlp::encode(&$num) }
($num: expr) => {
::rlp::encode(&$num)
};
}
macro_rules! val {
@ -45,7 +47,7 @@ macro_rules! val {
let mut stream = RlpStream::new_list(2);
stream.append(&$hash).append(&$td);
stream.drain()
}}
}};
}
/// The size of each CHT.
@ -62,19 +64,25 @@ pub struct CHT<DB: HashDB<KeccakHasher, DBValue>> {
impl<DB: HashDB<KeccakHasher, DBValue>> CHT<DB> {
/// 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.
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.
/// Nodes before level `from_level` will be omitted.
/// 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>>> {
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 db: &HashDB<_,_> = &self.db;
let db: &HashDB<_, _> = &self.db;
let t = TrieDB::new(&db, &self.root)?;
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
/// will not be generated.
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();
@ -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)`.
/// Discards the trie's nodes.
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 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)> {
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) {
Err(_) => return None,
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)
.and_then(|h| rlp.val_at::<U256>(1).map(|td| (h, td)))
.ok()
})
}),
};
match res {

View File

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

View File

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

View File

@ -16,29 +16,31 @@
//! 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 ethcore::engines::{epoch, EthEngine, EpochChange, EpochTransition, Proof};
use ethcore::machine::EthereumMachine;
use ethcore::error::{Error, EthcoreResult};
use ethcore::verification::queue::{self, HeaderQueue};
use ethcore::spec::{Spec, SpecHardcodedSync};
use common_types::{
block_status::BlockStatus, blockchain_info::BlockChainInfo, encoded, header::Header,
ids::BlockId, BlockNumber,
};
use ethcore::{
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 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 self::fetch::ChainDataFetcher;
use self::header_chain::{AncestryIter, HeaderChain, HardcodedSync};
use self::{
fetch::ChainDataFetcher,
header_chain::{AncestryIter, HardcodedSync, HeaderChain},
};
use cache::Cache;
@ -101,7 +103,7 @@ pub trait LightChainClient: Send + Sync {
fn score(&self, id: BlockId) -> Option<U256>;
/// 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.
fn signing_chain_id(&self) -> Option<u64>;
@ -153,7 +155,9 @@ pub trait AsLightClient {
impl<T: LightChainClient> AsLightClient for T {
type Client = Self;
fn as_light_client(&self) -> &Self { self }
fn as_light_client(&self) -> &Self {
self
}
}
/// Light client implementation.
@ -180,13 +184,22 @@ impl<T: ChainDataFetcher> Client<T> {
spec: &Spec,
fetcher: T,
io_channel: IoChannel<ClientIoMessage>,
cache: Arc<Mutex<Cache>>
cache: Arc<Mutex<Cache>>,
) -> Result<Self, Error> {
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(),
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)?
},
report: RwLock::new(ClientReport::default()),
@ -240,7 +253,11 @@ impl<T: ChainDataFetcher> Client<T> {
best_block_hash: best_hdr.hash(),
best_block_number: best_hdr.number(),
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 },
first_block_hash: first_block.as_ref().map(|first| first.hash),
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);
if self.verify_full && !self.check_header(&mut bad, &verified_header) {
continue
continue;
}
let write_proof_result = match self.check_epoch_signal(&verified_header) {
Ok(Some(proof)) => self.write_pending_proof(&verified_header, proof),
Ok(None) => Ok(()),
Err(e) =>
panic!("Unable to fetch epoch transition proof: {:?}", e),
Err(e) => panic!("Unable to fetch epoch transition proof: {:?}", e),
};
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(
&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),
);
@ -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 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));
}
@ -428,24 +451,25 @@ impl<T: ChainDataFetcher> Client<T> {
// should skip.
fn check_header(&self, bad: &mut Vec<H256>, verified_header: &Header) -> bool {
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,
None => {
trace!(target: "client", "No parent for block ({}, {})",
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
let verify_family_result = {
parent_header.decode()
parent_header
.decode()
.map_err(|dec_err| dec_err.into())
.and_then(|decoded| {
self.engine.verify_block_family(&verified_header, &decoded)
})
.and_then(|decoded| self.engine.verify_block_family(&verified_header, &decoded))
};
if let Err(e) = verify_family_result {
warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}",
@ -467,14 +491,16 @@ impl<T: ChainDataFetcher> Client<T> {
true
}
fn check_epoch_signal(&self, verified_header: &Header) -> Result<Option<Proof<EthereumMachine>>, T::Error> {
use ethcore::machine::{AuxiliaryRequest, AuxiliaryData};
fn check_epoch_signal(
&self,
verified_header: &Header,
) -> Result<Option<Proof<EthereumMachine>>, T::Error> {
use ethcore::machine::{AuxiliaryData, AuxiliaryRequest};
let mut block: Option<Vec<u8>> = None;
let mut receipts: Option<Vec<_>> = None;
loop {
let is_signal = {
let auxiliary = AuxiliaryData {
bytes: block.as_ref().map(|x| &x[..]),
@ -490,10 +516,12 @@ impl<T: ChainDataFetcher> Client<T> {
EpochChange::Yes(proof) => return Ok(Some(proof)),
EpochChange::Unsure(unsure) => {
let (b, r) = match unsure {
AuxiliaryRequest::Body =>
(Some(self.fetcher.block_body(verified_header)), None),
AuxiliaryRequest::Receipts =>
(None, Some(self.fetcher.block_receipts(verified_header))),
AuxiliaryRequest::Body => {
(Some(self.fetcher.block_body(verified_header)), None)
}
AuxiliaryRequest::Receipts => {
(None, Some(self.fetcher.block_receipts(verified_header)))
}
AuxiliaryRequest::Both => (
Some(self.fetcher.block_body(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.
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 {
Proof::Known(known) => known,
Proof::WithState(state_dependent) => {
self.fetcher.epoch_transition(
header.hash(),
self.engine.clone(),
state_dependent
).into_future().wait()?
}
Proof::WithState(state_dependent) => self
.fetcher
.epoch_transition(header.hash(), self.engine.clone(), state_dependent)
.into_future()
.wait()?,
};
let mut batch = self.db.transaction();
self.chain.insert_pending_transition(&mut batch, header.hash(), &epoch::PendingTransition {
proof,
});
self.chain.insert_pending_transition(
&mut batch,
header.hash(),
&epoch::PendingTransition { proof },
);
self.db.write_buffered(batch);
Ok(())
}
@ -539,7 +571,9 @@ impl<T: ChainDataFetcher> LightChainClient for Client<T> {
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> {
self.import_header(header)
@ -561,7 +595,7 @@ impl<T: ChainDataFetcher> LightChainClient for Client<T> {
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))
}
@ -621,11 +655,13 @@ impl<T: ChainDataFetcher> ::ethcore::client::ChainInfo for Client<T> {
impl<T: ChainDataFetcher> ::ethcore::client::EngineClient for Client<T> {
fn update_sealing(&self, _force: ForceUpdateSealing) {}
fn submit_seal(&self, _block_hash: H256, _seal: Vec<Vec<u8>>) { }
fn broadcast_consensus_message(&self, _message: Vec<u8>) { }
fn submit_seal(&self, _block_hash: H256, _seal: Vec<Vec<u8>>) {}
fn broadcast_consensus_message(&self, _message: Vec<u8>) {}
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_number: hdr.number(),
proof,

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