Merge branch 'master' into ui-2
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user