Merge branch 'master' into ui-2

This commit is contained in:
Jaco Greeff 2017-06-19 10:39:05 +02:00
commit 4dd26c0d7c
22 changed files with 179 additions and 107 deletions

View File

@ -71,13 +71,13 @@ linux-snap:
- triggers
script:
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
- cd scripts
- cd snap
- rm -rf *snap
- sed -i 's/master/'"$VER"'/g' snapcraft.yaml
- echo "Version:"$VER
- snapcraft
- ls
- cp parity_master_amd64.snap parity_"$VER"_amd64.snap
- cp "parity_"$CI_BUILD"_REF_NAME_amd64.snap" "parity_"$VER"_amd64.snap"
- md5sum "parity_"$VER"_amd64.snap" > "parity_"$VER"_amd64.snap.md5"
- aws configure set aws_access_key_id $s3_key
- aws configure set aws_secret_access_key $s3_secret

2
Cargo.lock generated
View File

@ -1803,7 +1803,7 @@ dependencies = [
[[package]]
name = "parity-ui-precompiled"
version = "1.4.0"
source = "git+https://github.com/paritytech/js-precompiled.git#e0cdec722ff2d32de6308638710fc4eb8a07253e"
source = "git+https://github.com/paritytech/js-precompiled.git#bebd4fc6ab48574cc976f3570357359f6e831a71"
dependencies = [
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

View File

@ -53,7 +53,7 @@ below to build from source.
## Build dependencies
**Parity requires Rust version 1.17.0 to build**
**Parity requires Rust version 1.18.0 to build**
We recommend installing Rust through [rustup](https://www.rustup.rs/). If you don't already have rustup, you can install it like this:

View File

@ -26,7 +26,7 @@ use std::time::{Instant, Duration};
use util::{RwLock};
use ethstore::{
SimpleSecretStore, SecretStore, Error as SSError, EthStore, EthMultiStore,
random_string, SecretVaultRef, StoreAccountRef,
random_string, SecretVaultRef, StoreAccountRef, OpaqueSecret,
};
use ethstore::dir::MemoryDirectory;
use ethstore::ethkey::{Address, Message, Public, Secret, Random, Generator};
@ -36,10 +36,10 @@ pub use ethstore::ethkey::Signature;
pub use ethstore::{Derivation, IndexDerivation, KeyFile};
/// Type of unlock.
#[derive(Clone)]
#[derive(Clone, PartialEq)]
enum Unlock {
/// If account is unlocked temporarily, it should be locked after first usage.
Temp,
OneTime,
/// Account unlocked permantently can always sign message.
/// Use with caution.
Perm,
@ -116,8 +116,13 @@ type AccountToken = String;
/// Account management.
/// Responsible for unlocking accounts.
pub struct AccountProvider {
/// For performance reasons some methods can re-use unlocked secrets.
unlocked_secrets: RwLock<HashMap<StoreAccountRef, OpaqueSecret>>,
/// Unlocked account data.
unlocked: RwLock<HashMap<StoreAccountRef, AccountData>>,
/// Address book.
address_book: RwLock<AddressBook>,
/// Dapps settings.
dapps_settings: RwLock<DappsSettingsStore>,
/// Accounts on disk
sstore: Box<SecretStore>,
@ -125,6 +130,9 @@ pub struct AccountProvider {
transient_sstore: EthMultiStore,
/// Accounts in hardware wallets.
hardware_store: Option<HardwareWalletManager>,
/// When unlocking account permanently we additionally keep a raw secret in memory
/// to increase the performance of transaction signing.
unlock_keep_secret: bool,
/// Disallowed accounts.
blacklisted_accounts: Vec<Address>,
}
@ -135,6 +143,8 @@ pub struct AccountProviderSettings {
pub enable_hardware_wallets: bool,
/// Use the classic chain key on the hardware wallet.
pub hardware_wallet_classic_key: bool,
/// Store raw account secret when unlocking the account permanently.
pub unlock_keep_secret: bool,
/// Disallowed accounts.
pub blacklisted_accounts: Vec<Address>,
}
@ -144,6 +154,7 @@ impl Default for AccountProviderSettings {
AccountProviderSettings {
enable_hardware_wallets: false,
hardware_wallet_classic_key: false,
unlock_keep_secret: false,
blacklisted_accounts: vec![],
}
}
@ -170,12 +181,14 @@ impl AccountProvider {
}
AccountProvider {
unlocked_secrets: RwLock::new(HashMap::new()),
unlocked: RwLock::new(HashMap::new()),
address_book: RwLock::new(address_book),
dapps_settings: RwLock::new(DappsSettingsStore::new(&sstore.local_path())),
sstore: sstore,
transient_sstore: transient_sstore(),
hardware_store: hardware_store,
unlock_keep_secret: settings.unlock_keep_secret,
blacklisted_accounts: settings.blacklisted_accounts,
}
}
@ -183,12 +196,14 @@ impl AccountProvider {
/// Creates not disk backed provider.
pub fn transient_provider() -> Self {
AccountProvider {
unlocked_secrets: RwLock::new(HashMap::new()),
unlocked: RwLock::new(HashMap::new()),
address_book: RwLock::new(AddressBook::transient()),
dapps_settings: RwLock::new(DappsSettingsStore::transient()),
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,
blacklisted_accounts: vec![],
}
}
@ -537,10 +552,7 @@ impl AccountProvider {
/// Helper method used for unlocking accounts.
fn unlock_account(&self, address: Address, password: String, unlock: Unlock) -> Result<(), Error> {
// verify password by signing dump message
// result may be discarded
let account = self.sstore.account_ref(&address)?;
let _ = self.sstore.sign(&account, &password, &Default::default())?;
// check if account is already unlocked pernamently, if it is, do nothing
let mut unlocked = self.unlocked.write();
@ -550,6 +562,16 @@ impl AccountProvider {
}
}
if self.unlock_keep_secret && unlock != Unlock::OneTime {
// verify password and get the secret
let secret = self.sstore.raw_secret(&account, &password)?;
self.unlocked_secrets.write().insert(account.clone(), secret);
} else {
// verify password by signing dump message
// result may be discarded
let _ = self.sstore.sign(&account, &password, &Default::default())?;
}
let data = AccountData {
unlock: unlock,
password: password,
@ -562,7 +584,7 @@ impl AccountProvider {
fn password(&self, account: &StoreAccountRef) -> Result<String, SignError> {
let mut unlocked = self.unlocked.write();
let data = unlocked.get(account).ok_or(SignError::NotUnlocked)?.clone();
if let Unlock::Temp = data.unlock {
if let Unlock::OneTime = data.unlock {
unlocked.remove(account).expect("data exists: so key must exist: qed");
}
if let Unlock::Timed(ref end) = data.unlock {
@ -581,7 +603,7 @@ impl AccountProvider {
/// Unlocks account temporarily (for one signing).
pub fn unlock_account_temporarily(&self, account: Address, password: String) -> Result<(), Error> {
self.unlock_account(account, password, Unlock::Temp)
self.unlock_account(account, password, Unlock::OneTime)
}
/// Unlocks account temporarily with a timeout.
@ -592,16 +614,24 @@ impl AccountProvider {
/// Checks if given account is unlocked
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)
.map(|r| unlocked.get(&r).is_some())
.map(|r| unlocked.get(&r).is_some() || unlocked_secrets.get(&r).is_some())
.unwrap_or(false)
}
/// Signs the message. If password is not provided the account must be unlocked.
pub fn sign(&self, address: Address, password: Option<String>, 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(&account, &password, &message)?)
match self.unlocked_secrets.read().get(&account) {
Some(secret) => {
Ok(self.sstore.sign_with_secret(&secret, &message)?)
},
None => {
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.

View File

@ -111,7 +111,7 @@ impl Builtin {
pub fn cost(&self, input: &[u8]) -> U256 { self.pricer.cost(input) }
/// Simple forwarder for execute.
pub fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> {
pub fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> {
self.native.execute(input, output)
}
@ -288,9 +288,11 @@ impl Impl for ModexpImpl {
let exp = read_num(exp_len);
let modulus = read_num(mod_len);
// calculate modexp: exponentiation by squaring.
// calculate modexp: exponentiation by squaring. the `num` crate has pow, but not modular.
fn modexp(mut base: BigUint, mut exp: BigUint, modulus: BigUint) -> BigUint {
match (base == BigUint::zero(), exp == BigUint::zero()) {
use num::Integer;
match (base.is_zero(), exp.is_zero()) {
(_, true) => return BigUint::one(), // n^0 % m
(true, false) => return BigUint::zero(), // 0^n % m, n>0
(false, false) if modulus <= BigUint::one() => return BigUint::zero(), // a^b % 1 = 0.
@ -301,10 +303,9 @@ impl Impl for ModexpImpl {
base = base % &modulus;
// fast path for base divisible by modulus.
if base == BigUint::zero() { return result }
while exp != BigUint::zero() {
// exp has to be on the right here to avoid move.
if BigUint::one() & &exp == BigUint::one() {
if base.is_zero() { return result }
while !exp.is_zero() {
if exp.is_odd() {
result = (result * &base) % &modulus;
}
@ -331,22 +332,22 @@ impl Impl for ModexpImpl {
fn read_fr(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result<::bn::Fr, Error> {
let mut buf = [0u8; 32];
reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed");
::bn::Fr::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid field element"))
}
fn read_point(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result<::bn::G1, Error> {
use bn::{Fq, AffineG1, G1, Group};
let mut buf = [0u8; 32];
reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed");
let px = Fq::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid point x coordinate"))?;
reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed");
let py = Fq::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid point x coordinate"))?;
Ok(
if px == Fq::zero() && py == Fq::zero() {
G1::zero()
@ -374,7 +375,7 @@ impl Impl for Bn128AddImpl {
output.write(0, &write_buf);
Ok(())
}
}
}
impl Impl for Bn128MulImpl {
@ -394,7 +395,7 @@ impl Impl for Bn128MulImpl {
}
output.write(0, &write_buf);
Ok(())
}
}
}
mod bn128_gen {
@ -420,13 +421,13 @@ mod bn128_gen {
.expect("a valid field element"),
Fq::from_str("4082367875863433681332203403145435568316851327593401208105741076214120093531")
.expect("a valid field element"),
),
),
).expect("the generator P2(10857046999023057135944570762232829481370756359578518086990519993285655852781 + 11559732032986387107991004021392285783925812861821192530917403151452391805634i, 8495653923123431417604973247489272438418190587263600148770280649306958101930 + 4082367875863433681332203403145435568316851327593401208105741076214120093531i) is a valid curve point"));
}
}
lazy_static! {
pub static ref P1_P2_PAIRING: Gt = pairing(P1.clone(), P2.clone());
}
}
}
impl Impl for Bn128PairingImpl {
@ -438,8 +439,8 @@ impl Impl for Bn128PairingImpl {
use bn::{AffineG1, AffineG2, Fq, Fq2, pairing, G1, G2, Gt};
let elements = input.len() / 192; // (a, b_a, b_b - each 64-byte affine coordinates)
if input.len() % 192 != 0 {
return Err("Invalid input length, must be multiple of 192 (3 * (32*2))".into())
if input.len() % 192 != 0 {
return Err("Invalid input length, must be multiple of 192 (3 * (32*2))".into())
}
let ret_val = if input.len() == 0 {
U256::one()
@ -459,11 +460,11 @@ impl Impl for Bn128PairingImpl {
.map_err(|_| Error::from("Invalid b argument imaginary coeff y coordinate"))?;
let b_a_x = Fq::from_slice(&input[idx*192+128..idx*192+160])
.map_err(|_| Error::from("Invalid b argument real coeff x coordinate"))?;
.map_err(|_| Error::from("Invalid b argument real coeff x coordinate"))?;
let b_a_y = Fq::from_slice(&input[idx*192+160..idx*192+192])
.map_err(|_| Error::from("Invalid b argument real coeff y coordinate"))?;
.map_err(|_| Error::from("Invalid b argument real coeff y coordinate"))?;
vals.push((
G1::from(
AffineG1::new(a_x, a_y).map_err(|_| Error::from("Invalid a argument - not on curve"))?
@ -719,7 +720,7 @@ mod tests {
pricer: Box::new(Linear { base: 0, word: 0 }),
native: ethereum_builtin("bn128_add"),
activate_at: 0,
};
};
// zero-points additions
{
@ -738,7 +739,7 @@ mod tests {
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail");
assert_eq!(output, expected);
}
}
// no input, should not fail
@ -754,7 +755,7 @@ mod tests {
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail");
assert_eq!(output, expected);
}
}
// should fail - point not on curve
{
@ -769,7 +770,7 @@ mod tests {
let res = f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]));
assert!(res.is_err(), "There should be built-in error here");
}
}
}
@ -781,7 +782,7 @@ mod tests {
pricer: Box::new(Linear { base: 0, word: 0 }),
native: ethereum_builtin("bn128_mul"),
activate_at: 0,
};
};
// zero-point multiplication
{
@ -799,7 +800,7 @@ mod tests {
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail");
assert_eq!(output, expected);
}
}
// should fail - point not on curve
{
@ -813,7 +814,7 @@ mod tests {
let res = f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..]));
assert!(res.is_err(), "There should be built-in error here");
}
}
}
fn builtin_pairing() -> Builtin {
@ -826,12 +827,12 @@ mod tests {
fn empty_test(f: Builtin, expected: Vec<u8>) {
let mut empty = [0u8; 0];
let input = BytesRef::Fixed(&mut empty);
let input = BytesRef::Fixed(&mut empty);
let mut output = vec![0u8; expected.len()];
f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should not fail");
assert_eq!(output, expected);
assert_eq!(output, expected);
}
fn error_test(f: Builtin, input: &[u8], msg_contains: Option<&str>) {
@ -851,12 +852,12 @@ mod tests {
fn bytes(s: &'static str) -> Vec<u8> {
FromHex::from_hex(s).expect("static str should contain valid hex bytes")
}
#[test]
fn bn128_pairing_empty() {
// should not fail, because empty input is a valid input of 0 elements
empty_test(
builtin_pairing(),
builtin_pairing(),
bytes("0000000000000000000000000000000000000000000000000000000000000001"),
);
}
@ -890,7 +891,7 @@ mod tests {
),
Some("Invalid input length"),
);
}
}
#[test]
#[should_panic]
@ -953,4 +954,4 @@ mod tests {
b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])).expect("Builtin should not fail");
assert_eq!(i, o);
}
}
}

View File

@ -25,3 +25,4 @@ pub use self::crypto::Crypto;
pub use self::kdf::{Kdf, Pbkdf2, Scrypt, Prf};
pub use self::safe_account::SafeAccount;
pub use self::version::Version;

View File

@ -26,7 +26,7 @@ use dir::{KeyDirectory, VaultKeyDirectory, VaultKey, SetKeyError};
use account::SafeAccount;
use presale::PresaleWallet;
use json::{self, Uuid, OpaqueKeyFile};
use {import, Error, SimpleSecretStore, SecretStore, SecretVaultRef, StoreAccountRef, Derivation};
use {import, Error, SimpleSecretStore, SecretStore, SecretVaultRef, StoreAccountRef, Derivation, OpaqueSecret};
/// Accounts store.
pub struct EthStore {
@ -140,6 +140,10 @@ impl SimpleSecretStore for EthStore {
}
impl SecretStore for EthStore {
fn raw_secret(&self, account: &StoreAccountRef, password: &str) -> Result<OpaqueSecret, Error> {
Ok(OpaqueSecret(self.get(account)?.crypto.secret(password)?))
}
fn import_presale(&self, vault: SecretVaultRef, json: &[u8], password: &str) -> Result<StoreAccountRef, Error> {
let json_wallet = json::PresaleWallet::load(json).map_err(|_| Error::InvalidKeyFile("Invalid JSON format".to_owned()))?;
let wallet = PresaleWallet::from(json_wallet);

View File

@ -66,3 +66,6 @@ pub use self::secret_store::{
};
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

@ -20,6 +20,7 @@ use ethkey::{Address, Message, Signature, Secret, Public};
use Error;
use json::{Uuid, OpaqueKeyFile};
use bigint::hash::H256;
use OpaqueSecret;
/// Key directory reference
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
@ -88,6 +89,15 @@ 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: &str) -> Result<OpaqueSecret, Error>;
/// Signs a message with raw secret.
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: &str) -> Result<StoreAccountRef, Error>;
/// Imports existing JSON wallet

View File

@ -77,11 +77,11 @@ impl IpfsHandler {
/// Implement Hyper's HTTP handler
impl Handler<HttpStream> for IpfsHandler {
fn on_request(&mut self, req: Request<HttpStream>) -> Next {
if *req.method() != Method::Get {
return Next::write();
match *req.method() {
Method::Get | Method::Post => {},
_ => return Next::write()
}
if !http::is_host_allowed(&req, &self.allowed_hosts) {
self.out = Out::Bad("Disallowed Host header");

View File

@ -1,6 +1,6 @@
{
"name": "parity.js",
"version": "1.7.90",
"version": "1.7.91",
"main": "release/index.js",
"jsnext:main": "src/index.js",
"author": "Parity Team <admin@parity.io>",

View File

@ -19,19 +19,19 @@ const PAGES = [
path: 'overview',
title: 'Overview',
byline: 'Display all the current information relating to your own deployed tokens',
description: 'View the total number of tokens in circulation, the number of different tokens associated with your accounts as well as the types of tokens created by you. In addition view the balances associated with your accounts in reltion to the total in circulation.'
description: 'View the total number of tokens in circulation, the number of different tokens associated with your accounts, as well as the types of tokens created by you. In addition, view the balances associated with your accounts in relation to the total in circulation.'
},
{
path: 'transfer',
title: 'Transfer',
byline: 'Send tokens associated with your accounts to other addresses',
description: 'Send any tokens created byt you or received from others. In addition have a bird\'s eye view of all events relating to token transfers, be it yours, created byt others, either local or globally available on the network.'
description: 'Send any tokens created by you or received from others. In addition, have a bird\'s eye view of all events relating to token transfers, be it yours, created by others, either local or globally available on the network.'
},
{
path: 'deploy',
title: 'Deploy',
byline: 'Deploy a new token to the network',
description: 'Token registration has never been this easy. Select the name for your token, the TLA and the number of tokens in circulation. Start sending the tokens to contacts right from this interface. Optionally you can register the token with the Token Registry which would allow you to transaction in tokens from anywhere these transactions are allowed.'
description: 'Token registration has never been this easy. Select the name for your token, the TLA and the number of tokens in circulation. Start sending the tokens to contacts right from this interface. Optionally you can register the token with the Token Registry which would allow you to transact in tokens from anywhere these transactions are allowed.'
}
];

View File

@ -16,5 +16,5 @@
export default {
install: `Installeer de extensie nu`,
intro: `Parity heeft nu een extensie voor Chrome beschikbaar waarmee je veillig Ethereum-enabled gedistribueerde applicaties kunt bekijken. Het wordt ten zeerste aanbevolen om deze extensie te installeren om je Parity ervaring nog beter te maken.`
intro: `Parity heeft nu een extensie voor Chrome beschikbaar waarmee je veillig Ethereum-enabled decentrale applicaties kunt bekijken. Het wordt ten zeerste aanbevolen om deze extensie te installeren om je Parity ervaring nog beter te maken.`
};

View File

@ -69,7 +69,7 @@ export default {
label: `Adresboek`
},
apps: {
description: `Gedistibueerde applicaties die gebruik maken van het onderliggende Ethereum netwerk. Voeg applicaties toe, beheer je applicatie portfolio en maak gebruik van applicaties op het wereldwijde netwerk.`,
description: `Decentrale applicaties die gebruik maken van het onderliggende Ethereum netwerk. Voeg applicaties toe, beheer je applicatie portfolio en maak gebruik van applicaties op het wereldwijde netwerk.`,
label: `Applicaties`
},
contracts: {
@ -85,7 +85,7 @@ export default {
label: `Instellingen`
},
signer: {
description: `Het beveiligde transactie beheergebied van de applicatie waar je elke uitgaande transactie die je hebt gemaakt met Parity evenals de transacties die in de wachtrij zijn geplaatst door gedistribueerde applicaties kan goedkeuren.`,
description: `Het beveiligde transactie beheergebied van de applicatie waar je goedkeuring kunt verlenen aan elke uitgaande transactie die je hebt gemaakt met Parity evenals de transacties die in de wachtrij zijn geplaatst door decentrale applicaties.`,
label: `Signer`
},
status: {

View File

@ -16,6 +16,6 @@
export default {
tooltip: {
overview: `navigeer tussen de verschillende onderdelen en weergaven in de applicatie, wissel tussen de account weergave, token weergave en gedistribueerde applicatie weergave`
overview: `navigeer tussen de verschillende onderdelen en weergaven in de applicatie, wissel tussen de account weergave, token weergave en decentrale applicatie weergave`
}
};

View File

@ -114,6 +114,8 @@ usage! {
or |c: &Config| otry!(c.account).keys_iterations.clone(),
flag_no_hardware_wallets: bool = false,
or |c: &Config| otry!(c.account).disable_hardware.clone(),
flag_fast_unlock: bool = false,
or |c: &Config| otry!(c.account).fast_unlock.clone(),
flag_force_ui: bool = false,
@ -424,6 +426,7 @@ struct Account {
password: Option<Vec<String>>,
keys_iterations: Option<u32>,
disable_hardware: Option<bool>,
fast_unlock: Option<bool>,
}
#[derive(Default, Debug, PartialEq, RustcDecodable)]
@ -707,6 +710,7 @@ mod tests {
flag_password: vec!["~/.safe/password.file".into()],
flag_keys_iterations: 10240u32,
flag_no_hardware_wallets: false,
flag_fast_unlock: false,
flag_force_ui: false,
flag_no_ui: false,
@ -927,6 +931,7 @@ mod tests {
password: Some(vec!["passwdfile path".into()]),
keys_iterations: None,
disable_hardware: None,
fast_unlock: None,
}),
ui: Some(Ui {
force: None,

View File

@ -99,6 +99,9 @@ Account Options:
deriving key from the password (bigger is more
secure) (default: {flag_keys_iterations}).
--no-hardware-wallets Disables hardware wallet support. (default: {flag_no_hardware_wallets})
--fast-unlock Use drasticly faster unlocking mode. This setting causes
raw secrets to be stored unprotected in memory,
so use with care. (default: {flag_fast_unlock})
UI Options:
--force-ui Enable Trusted UI WebSocket endpoint,

View File

@ -497,6 +497,7 @@ impl Configuration {
password_files: self.args.flag_password.clone(),
unlocked_accounts: to_addresses(&self.args.flag_unlock)?,
enable_hardware_wallets: !self.args.flag_no_hardware_wallets,
enable_fast_unlock: self.args.flag_fast_unlock,
};
Ok(cfg)

View File

@ -180,6 +180,7 @@ pub struct AccountsConfig {
pub password_files: Vec<String>,
pub unlocked_accounts: Vec<Address>,
pub enable_hardware_wallets: bool,
pub enable_fast_unlock: bool,
}
impl Default for AccountsConfig {
@ -190,6 +191,7 @@ impl Default for AccountsConfig {
password_files: Vec::new(),
unlocked_accounts: Vec::new(),
enable_hardware_wallets: true,
enable_fast_unlock: false,
}
}
}

View File

@ -777,6 +777,7 @@ fn prepare_account_provider(spec: &SpecType, dirs: &Directories, data_dir: &str,
let account_settings = AccountProviderSettings {
enable_hardware_wallets: cfg.enable_hardware_wallets,
hardware_wallet_classic_key: spec == &SpecType::Classic,
unlock_keep_secret: cfg.enable_fast_unlock,
blacklisted_accounts: match *spec {
SpecType::Morden | SpecType::Ropsten | SpecType::Kovan | SpecType::Dev => vec![],
_ => vec![

View File

@ -86,7 +86,7 @@ pub trait HashDB: AsHashDB + Send + Sync {
fn emplace(&mut self, key: H256, value: DBValue);
/// Remove a datum previously inserted. Insertions can be "owed" such that the same number of `insert()`s may
/// happen without the data being eventually being inserted into the DB.
/// happen without the data being eventually being inserted into the DB. It can be "owed" more than once.
///
/// # Examples
/// ```rust
@ -100,6 +100,10 @@ pub trait HashDB: AsHashDB + Send + Sync {
/// let key = &d.sha3();
/// m.remove(key); // OK - we now owe an insertion.
/// assert!(!m.contains(key));
/// m.remove(key); // OK - we now owe two insertions.
/// assert!(!m.contains(key));
/// m.insert(d); // OK - still owed.
/// assert!(!m.contains(key));
/// m.insert(d); // OK - now it's "empty" again.
/// assert!(!m.contains(key));
/// m.insert(d); // OK - now we've

View File

@ -103,11 +103,7 @@ impl MemoryDB {
/// Purge all zero-referenced data from the database.
pub fn purge(&mut self) {
let empties: Vec<_> = self.data.iter()
.filter(|&(_, &(_, rc))| rc == 0)
.map(|(k, _)| k.clone())
.collect();
for empty in empties { self.data.remove(&empty); }
self.data.retain(|_, &mut (_, rc)| rc != 0);
}
/// Return the internal map of hashes to data, clearing the current state.
@ -250,46 +246,57 @@ impl HashDB for MemoryDB {
}
}
#[test]
fn memorydb_remove_and_purge() {
let hello_bytes = b"Hello world!";
let hello_key = hello_bytes.sha3();
#[cfg(test)]
mod tests {
use super::*;
let mut m = MemoryDB::new();
m.remove(&hello_key);
assert_eq!(m.raw(&hello_key).unwrap().1, -1);
m.purge();
assert_eq!(m.raw(&hello_key).unwrap().1, -1);
m.insert(hello_bytes);
assert_eq!(m.raw(&hello_key).unwrap().1, 0);
m.purge();
assert_eq!(m.raw(&hello_key), None);
#[test]
fn memorydb_remove_and_purge() {
let hello_bytes = b"Hello world!";
let hello_key = hello_bytes.sha3();
let mut m = MemoryDB::new();
assert!(m.remove_and_purge(&hello_key).is_none());
assert_eq!(m.raw(&hello_key).unwrap().1, -1);
m.insert(hello_bytes);
m.insert(hello_bytes);
assert_eq!(m.raw(&hello_key).unwrap().1, 1);
assert_eq!(&*m.remove_and_purge(&hello_key).unwrap(), hello_bytes);
assert_eq!(m.raw(&hello_key), None);
assert!(m.remove_and_purge(&hello_key).is_none());
}
#[test]
fn consolidate() {
let mut main = MemoryDB::new();
let mut other = MemoryDB::new();
let remove_key = other.insert(b"doggo");
main.remove(&remove_key);
let insert_key = other.insert(b"arf");
main.emplace(insert_key, DBValue::from_slice(b"arf"));
main.consolidate(other);
let overlay = main.drain();
assert_eq!(overlay.get(&remove_key).unwrap(), &(DBValue::from_slice(b"doggo"), 0));
assert_eq!(overlay.get(&insert_key).unwrap(), &(DBValue::from_slice(b"arf"), 2));
let mut m = MemoryDB::new();
m.remove(&hello_key);
assert_eq!(m.raw(&hello_key).unwrap().1, -1);
m.purge();
assert_eq!(m.raw(&hello_key).unwrap().1, -1);
m.insert(hello_bytes);
assert_eq!(m.raw(&hello_key).unwrap().1, 0);
m.purge();
assert_eq!(m.raw(&hello_key), None);
let mut m = MemoryDB::new();
assert!(m.remove_and_purge(&hello_key).is_none());
assert_eq!(m.raw(&hello_key).unwrap().1, -1);
m.insert(hello_bytes);
m.insert(hello_bytes);
assert_eq!(m.raw(&hello_key).unwrap().1, 1);
assert_eq!(&*m.remove_and_purge(&hello_key).unwrap(), hello_bytes);
assert_eq!(m.raw(&hello_key), None);
assert!(m.remove_and_purge(&hello_key).is_none());
}
#[test]
fn consolidate() {
let mut main = MemoryDB::new();
let mut other = MemoryDB::new();
let remove_key = other.insert(b"doggo");
main.remove(&remove_key);
let insert_key = other.insert(b"arf");
main.emplace(insert_key, DBValue::from_slice(b"arf"));
let negative_remove_key = other.insert(b"negative");
other.remove(&negative_remove_key); // ref cnt: 0
other.remove(&negative_remove_key); // ref cnt: -1
main.remove(&negative_remove_key); // ref cnt: -1
main.consolidate(other);
let overlay = main.drain();
assert_eq!(overlay.get(&remove_key).unwrap(), &(DBValue::from_slice(b"doggo"), 0));
assert_eq!(overlay.get(&insert_key).unwrap(), &(DBValue::from_slice(b"arf"), 2));
assert_eq!(overlay.get(&negative_remove_key).unwrap(), &(DBValue::from_slice(b"negative"), -2));
}
}