Remove support for hardware wallets (#10678)
This commit is contained in:
parent
c270599a23
commit
11fb967c6a
72
Cargo.lock
generated
72
Cargo.lock
generated
@ -909,8 +909,6 @@ dependencies = [
|
||||
"ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethkey 0.3.0",
|
||||
"ethstore 0.2.1",
|
||||
"fake-hardware-wallet 0.0.1",
|
||||
"hardware-wallet 1.12.0",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1502,14 +1500,6 @@ dependencies = [
|
||||
"hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fake-hardware-wallet"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethkey 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fake-simd"
|
||||
version = "0.1.2"
|
||||
@ -1696,22 +1686,6 @@ name = "hamming"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "hardware-wallet"
|
||||
version = "1.12.0"
|
||||
dependencies = [
|
||||
"ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethkey 0.3.0",
|
||||
"hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)",
|
||||
"libusb 0.3.0 (git+https://github.com/paritytech/libusb-rs)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"protobuf 1.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"trezor-sys 1.0.0 (git+https://github.com/paritytech/trezor-sys)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash-db"
|
||||
version = "0.11.0"
|
||||
@ -1764,15 +1738,6 @@ dependencies = [
|
||||
"proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hidapi"
|
||||
version = "0.3.1"
|
||||
source = "git+https://github.com/paritytech/hidapi-rs#d4d323767d6f27cf5a3d73fbae0b0f2134d579bf"
|
||||
dependencies = [
|
||||
"cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.6.3"
|
||||
@ -2270,25 +2235,6 @@ dependencies = [
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libusb"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/paritytech/libusb-rs#442708954a720bc89a9cf41e7be021a778bdbc27"
|
||||
dependencies = [
|
||||
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libusb-sys 0.2.5 (git+https://github.com/paritytech/libusb-sys)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libusb-sys"
|
||||
version = "0.2.5"
|
||||
source = "git+https://github.com/paritytech/libusb-sys#f49d8fc7ca43a465c721c8eafc37c86fd06f795f"
|
||||
dependencies = [
|
||||
"cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.2"
|
||||
@ -3384,11 +3330,6 @@ dependencies = [
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf"
|
||||
version = "1.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.0.3"
|
||||
@ -4580,14 +4521,6 @@ name = "transient-hashmap"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "trezor-sys"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/paritytech/trezor-sys#8a401705e58c83db6c29c199d9577b78fde40709"
|
||||
dependencies = [
|
||||
"protobuf 1.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trie-db"
|
||||
version = "0.11.0"
|
||||
@ -5141,7 +5074,6 @@ dependencies = [
|
||||
"checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa"
|
||||
"checksum hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0"
|
||||
"checksum hex-literal-impl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d4c5c844e2fee0bf673d54c2c177f1713b3d2af2ff6e666b49cb7572e6cf42d"
|
||||
"checksum hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)" = "<none>"
|
||||
"checksum hmac 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "733e1b3ac906631ca01ebb577e9bb0f5e37a454032b9036b5eaea4013ed6f99a"
|
||||
"checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695"
|
||||
"checksum home 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "29302b90cfa76231a757a887d1e3153331a63c7f80b6c75f86366334cbe70708"
|
||||
@ -5188,8 +5120,6 @@ dependencies = [
|
||||
"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
|
||||
"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8"
|
||||
"checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753"
|
||||
"checksum libusb 0.3.0 (git+https://github.com/paritytech/libusb-rs)" = "<none>"
|
||||
"checksum libusb-sys 0.2.5 (git+https://github.com/paritytech/libusb-sys)" = "<none>"
|
||||
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
|
||||
"checksum local-encoding 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1ceb20f39ff7ae42f3ff9795f3986b1daad821caaa1e1732a0944103a5a1a66"
|
||||
"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c"
|
||||
@ -5269,7 +5199,6 @@ dependencies = [
|
||||
"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5"
|
||||
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||
"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27"
|
||||
"checksum protobuf 1.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e14ccd6b79ec748412d4f2dfde1a80fa363a67def4062969f8aed3d790a30f28"
|
||||
"checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07"
|
||||
"checksum pwasm-utils 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "efb0dcbddbb600f47a7098d33762a00552c671992171637f5bb310b37fe1f0e4"
|
||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
||||
@ -5396,7 +5325,6 @@ dependencies = [
|
||||
"checksum trace-time 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c9adf04084eeb9a1ea91be6c3f8ef3df392391c91fc7d8f696d4875f6754e715"
|
||||
"checksum transaction-pool 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d8bbee24c711a878e7d8f89460569034cacf2d8c58dde785b5ffa06ed6b59663"
|
||||
"checksum transient-hashmap 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aeb4b191d033a35edfce392a38cdcf9790b6cebcb30fa690c312c29da4dc433e"
|
||||
"checksum trezor-sys 1.0.0 (git+https://github.com/paritytech/trezor-sys)" = "<none>"
|
||||
"checksum trie-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7319e28ca295f27359d944a682f7f65b419158bf1590c92cadc0000258d788"
|
||||
"checksum trie-standardmap 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e26f52976a57a0859616d6fcec87092ac35d08eabbd78dc3dabee93b480ea5f"
|
||||
"checksum triehash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d26efb4ddf87870fc08dc9a6580dc3061be350d7b9d0eb30aef1c8b4227aa46"
|
||||
|
@ -57,7 +57,7 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do
|
||||
$ curl https://sh.rustup.rs -sSf | sh
|
||||
```
|
||||
|
||||
Parity Ethereum also requires `gcc`, `g++`, `libudev-dev`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed.
|
||||
Parity Ethereum also requires `gcc`, `g++`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed.
|
||||
|
||||
- OSX:
|
||||
```bash
|
||||
|
@ -17,12 +17,6 @@ serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))'.dependencies]
|
||||
hardware-wallet = { path = "hw" }
|
||||
|
||||
[target.'cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))'.dependencies]
|
||||
fake-hardware-wallet = { path = "fake-hardware-wallet" }
|
||||
|
||||
[dev-dependencies]
|
||||
ethereum-types = "0.4"
|
||||
tempdir = "0.3"
|
||||
|
@ -1,10 +0,0 @@
|
||||
[package]
|
||||
description = "Fake hardware-wallet, for OS' that don't support libusb"
|
||||
name = "fake-hardware-wallet"
|
||||
version = "0.0.1"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethereum-types = "0.4"
|
||||
ethkey = { path = "../../accounts/ethkey" }
|
@ -1,107 +0,0 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Dummy module for platforms that does not provide support for hardware wallets (libusb)
|
||||
|
||||
extern crate ethereum_types;
|
||||
extern crate ethkey;
|
||||
|
||||
use ethereum_types::U256;
|
||||
use ethkey::{Address, Signature};
|
||||
use std::fmt;
|
||||
|
||||
pub struct WalletInfo {
|
||||
pub address: Address,
|
||||
pub name: String,
|
||||
pub manufacturer: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// `ErrorType` for devices with no `hardware wallet`
|
||||
pub enum Error {
|
||||
NoWallet,
|
||||
KeyNotFound,
|
||||
}
|
||||
|
||||
pub struct TransactionInfo {
|
||||
/// Nonce
|
||||
pub nonce: U256,
|
||||
/// Gas price
|
||||
pub gas_price: U256,
|
||||
/// Gas limit
|
||||
pub gas_limit: U256,
|
||||
/// Receiver
|
||||
pub to: Option<Address>,
|
||||
/// Value
|
||||
pub value: U256,
|
||||
/// Data
|
||||
pub data: Vec<u8>,
|
||||
/// Chain ID
|
||||
pub chain_id: Option<u64>,
|
||||
}
|
||||
|
||||
pub enum KeyPath {
|
||||
/// Ethereum.
|
||||
Ethereum,
|
||||
/// Ethereum classic.
|
||||
EthereumClassic,
|
||||
}
|
||||
|
||||
/// `HardwareWalletManager` for devices with no `hardware wallet`
|
||||
pub struct HardwareWalletManager;
|
||||
|
||||
impl HardwareWalletManager {
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
Err(Error::NoWallet)
|
||||
}
|
||||
|
||||
pub fn set_key_path(&self, _key_path: KeyPath) {}
|
||||
|
||||
pub fn wallet_info(&self, _: &Address) -> Option<WalletInfo> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn list_wallets(&self) -> Vec<WalletInfo> {
|
||||
Vec::with_capacity(0)
|
||||
}
|
||||
|
||||
pub fn list_locked_wallets(&self) -> Result<Vec<String>, Error> {
|
||||
Err(Error::NoWallet)
|
||||
}
|
||||
|
||||
pub fn pin_matrix_ack(&self, _: &str, _: &str) -> Result<bool, 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)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "No hardware wallet!!")
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
[package]
|
||||
description = "Hardware wallet support."
|
||||
homepage = "http://parity.io"
|
||||
license = "GPL-3.0"
|
||||
name = "hardware-wallet"
|
||||
version = "1.12.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
parking_lot = "0.7"
|
||||
protobuf = "1.4"
|
||||
hidapi = { git = "https://github.com/paritytech/hidapi-rs" }
|
||||
libusb = { git = "https://github.com/paritytech/libusb-rs" }
|
||||
trezor-sys = { git = "https://github.com/paritytech/trezor-sys" }
|
||||
ethkey = { path = "../ethkey" }
|
||||
ethereum-types = "0.4"
|
||||
semver = "0.9"
|
||||
|
||||
[dev-dependencies]
|
||||
rustc-hex = "1.0"
|
File diff suppressed because one or more lines are too long
@ -1,443 +0,0 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Hardware wallet management.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![warn(warnings)]
|
||||
|
||||
extern crate ethereum_types;
|
||||
extern crate ethkey;
|
||||
extern crate hidapi;
|
||||
extern crate libusb;
|
||||
extern crate parking_lot;
|
||||
extern crate protobuf;
|
||||
extern crate semver;
|
||||
extern crate trezor_sys;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[cfg(test)]
|
||||
extern crate rustc_hex;
|
||||
|
||||
mod ledger;
|
||||
mod trezor;
|
||||
|
||||
use std::{
|
||||
fmt,
|
||||
sync::{atomic, atomic::AtomicBool, Arc, Weak},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use ethereum_types::U256;
|
||||
use ethkey::{Address, Signature};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
const HID_GLOBAL_USAGE_PAGE: u16 = 0xFF00;
|
||||
const HID_USB_DEVICE_CLASS: u8 = 0;
|
||||
const MAX_POLLING_DURATION: Duration = Duration::from_millis(500);
|
||||
const USB_EVENT_POLLING_INTERVAL: Duration = Duration::from_millis(500);
|
||||
|
||||
/// `HardwareWallet` device
|
||||
#[derive(Debug)]
|
||||
pub struct Device {
|
||||
path: String,
|
||||
info: WalletInfo,
|
||||
}
|
||||
|
||||
/// `Wallet` trait
|
||||
pub trait Wallet<'a> {
|
||||
/// Error
|
||||
type Error;
|
||||
/// Transaction data format
|
||||
type Transaction;
|
||||
|
||||
/// Sign transaction data with wallet managing `address`.
|
||||
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);
|
||||
|
||||
/// Re-populate device list
|
||||
/// Note, this assumes all devices are iterated over and updated
|
||||
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>;
|
||||
|
||||
/// List connected and acknowledged wallets
|
||||
fn list_devices(&self) -> Vec<WalletInfo>;
|
||||
|
||||
/// List locked wallets
|
||||
/// This may be moved if it is the wrong assumption, for example this is not supported by Ledger
|
||||
/// Then this method return a empty vector
|
||||
fn list_locked_devices(&self) -> Vec<String>;
|
||||
|
||||
/// Get wallet info.
|
||||
fn get_wallet(&self, address: &Address) -> Option<WalletInfo>;
|
||||
|
||||
/// Generate ethereum address for a Wallet
|
||||
fn get_address(&self, device: &hidapi::HidDevice) -> Result<Option<Address>, Self::Error>;
|
||||
|
||||
/// Open a device using `device path`
|
||||
/// Note, f - is a closure that borrows HidResult<HidDevice>
|
||||
/// HidDevice is in turn a type alias for a `c_void function pointer`
|
||||
/// For further information see:
|
||||
/// * <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>;
|
||||
}
|
||||
|
||||
/// Hardware wallet error.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Ledger device error.
|
||||
LedgerDevice(ledger::Error),
|
||||
/// Trezor device error
|
||||
TrezorDevice(trezor::Error),
|
||||
/// USB error.
|
||||
Usb(libusb::Error),
|
||||
/// HID error
|
||||
Hid(String),
|
||||
/// Hardware wallet not found for specified key.
|
||||
KeyNotFound,
|
||||
}
|
||||
|
||||
/// This is the transaction info we need to supply to Trezor message. It's more
|
||||
/// or less a duplicate of `ethcore::transaction::Transaction`, but we can't
|
||||
/// import ethcore here as that would be a circular dependency.
|
||||
pub struct TransactionInfo {
|
||||
/// Nonce
|
||||
pub nonce: U256,
|
||||
/// Gas price
|
||||
pub gas_price: U256,
|
||||
/// Gas limit
|
||||
pub gas_limit: U256,
|
||||
/// Receiver
|
||||
pub to: Option<Address>,
|
||||
/// Value
|
||||
pub value: U256,
|
||||
/// Data
|
||||
pub data: Vec<u8>,
|
||||
/// Chain ID
|
||||
pub chain_id: Option<u64>,
|
||||
}
|
||||
|
||||
/// Hardware wallet information.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WalletInfo {
|
||||
/// Wallet device name.
|
||||
pub name: String,
|
||||
/// Wallet device manufacturer.
|
||||
pub manufacturer: String,
|
||||
/// Wallet device serial number.
|
||||
pub serial: String,
|
||||
/// Ethereum address.
|
||||
pub address: Address,
|
||||
}
|
||||
|
||||
/// Key derivation paths used on hardware wallets.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum KeyPath {
|
||||
/// Ethereum.
|
||||
Ethereum,
|
||||
/// Ethereum classic.
|
||||
EthereumClassic,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Error::KeyNotFound => write!(f, "Key not found for given address."),
|
||||
Error::LedgerDevice(ref e) => write!(f, "{}", e),
|
||||
Error::TrezorDevice(ref e) => write!(f, "{}", e),
|
||||
Error::Usb(ref e) => write!(f, "{}", e),
|
||||
Error::Hid(ref e) => write!(f, "{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ledger::Error> for Error {
|
||||
fn from(err: ledger::Error) -> Self {
|
||||
match err {
|
||||
ledger::Error::KeyNotFound => Error::KeyNotFound,
|
||||
_ => Error::LedgerDevice(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<trezor::Error> for Error {
|
||||
fn from(err: trezor::Error) -> Self {
|
||||
match err {
|
||||
trezor::Error::KeyNotFound => Error::KeyNotFound,
|
||||
_ => Error::TrezorDevice(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libusb::Error> for Error {
|
||||
fn from(err: libusb::Error) -> Self {
|
||||
Error::Usb(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies the direction of the `HardwareWallet` i.e, whether it arrived or left
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum DeviceDirection {
|
||||
/// Device arrived
|
||||
Arrived,
|
||||
/// Device left
|
||||
Left,
|
||||
}
|
||||
|
||||
impl fmt::Display for DeviceDirection {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
DeviceDirection::Arrived => write!(f, "arrived"),
|
||||
DeviceDirection::Left => write!(f, "left"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Hardware wallet management interface.
|
||||
pub struct HardwareWalletManager {
|
||||
exiting: Arc<AtomicBool>,
|
||||
ledger: Arc<ledger::Manager>,
|
||||
trezor: Arc<trezor::Manager>,
|
||||
}
|
||||
|
||||
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 ledger = ledger::Manager::new(hidapi.clone());
|
||||
let trezor = trezor::Manager::new(hidapi.clone());
|
||||
let usb_context = Arc::new(libusb::Context::new()?);
|
||||
|
||||
let l = ledger.clone();
|
||||
let t = trezor.clone();
|
||||
let exit = exiting.clone();
|
||||
|
||||
// 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),
|
||||
Box::new(EventHandler::new(
|
||||
Arc::downgrade(&ledger),
|
||||
Arc::downgrade(&trezor),
|
||||
)),
|
||||
)?;
|
||||
|
||||
// Hardware event subscriber thread
|
||||
thread::Builder::new()
|
||||
.name("hw_wallet_manager".to_string())
|
||||
.spawn(move || {
|
||||
if let Err(e) = l.update_devices(DeviceDirection::Arrived) {
|
||||
debug!(target: "hw", "Ledger couldn't connect at startup, error: {}", e);
|
||||
}
|
||||
if let Err(e) = t.update_devices(DeviceDirection::Arrived) {
|
||||
debug!(target: "hw", "Trezor couldn't connect at startup, error: {}", e);
|
||||
}
|
||||
|
||||
while !exit.load(atomic::Ordering::Acquire) {
|
||||
if let Err(e) = usb_context.handle_events(Some(USB_EVENT_POLLING_INTERVAL)) {
|
||||
debug!(target: "hw", "HardwareWalletManager event handler error: {}", e);
|
||||
}
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
|
||||
Ok(Self {
|
||||
exiting,
|
||||
trezor,
|
||||
ledger,
|
||||
})
|
||||
}
|
||||
|
||||
/// Select key derivation path for a chain.
|
||||
/// Currently, only one hard-coded keypath is supported
|
||||
/// It is managed by `ethcore/account_provider`
|
||||
pub fn set_key_path(&self, key_path: KeyPath) {
|
||||
self.ledger.set_key_path(key_path);
|
||||
self.trezor.set_key_path(key_path);
|
||||
}
|
||||
|
||||
/// List connected wallets. This only returns wallets that are ready to be used.
|
||||
pub fn list_wallets(&self) -> Vec<WalletInfo> {
|
||||
let mut wallets = Vec::new();
|
||||
wallets.extend(self.ledger.list_devices());
|
||||
wallets.extend(self.trezor.list_devices());
|
||||
wallets
|
||||
}
|
||||
|
||||
/// Return a list of paths to locked hardware wallets
|
||||
/// This is only applicable to Trezor because Ledger only appears as
|
||||
/// a device when it is unlocked
|
||||
pub fn list_locked_wallets(&self) -> Result<Vec<String>, Error> {
|
||||
Ok(self.trezor.list_locked_devices())
|
||||
}
|
||||
|
||||
/// Get connected wallet info.
|
||||
pub fn wallet_info(&self, address: &Address) -> Option<WalletInfo> {
|
||||
if let Some(info) = self.ledger.get_wallet(address) {
|
||||
Some(info)
|
||||
} else {
|
||||
self.trezor.get_wallet(address)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sign a message with the wallet (only supported by Ledger)
|
||||
pub fn sign_message(&self, address: &Address, msg: &[u8]) -> Result<Signature, Error> {
|
||||
if self.ledger.get_wallet(address).is_some() {
|
||||
Ok(self.ledger.sign_message(address, msg)?)
|
||||
} else if self.trezor.get_wallet(address).is_some() {
|
||||
Err(Error::TrezorDevice(trezor::Error::NoSigningMessage))
|
||||
} else {
|
||||
Err(Error::KeyNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sign transaction data with wallet managing `address`.
|
||||
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() {
|
||||
Ok(self.trezor.sign_transaction(address, t_info)?)
|
||||
} else {
|
||||
Err(Error::KeyNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a pin to a device at a certain path to unlock it
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for HardwareWalletManager {
|
||||
fn drop(&mut self) {
|
||||
// Indicate to the USB Hotplug handler that it
|
||||
// shall terminate but don't wait for it to terminate.
|
||||
// If it doesn't terminate for some reason USB Hotplug events will be handled
|
||||
// even if the HardwareWalletManger has been dropped
|
||||
self.exiting.store(true, atomic::Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
/// Hardware wallet event handler
|
||||
///
|
||||
/// Note, that this runs to completion and race-conditions can't occur but it can
|
||||
/// stop other events for being processed with an infinite loop or similar
|
||||
struct EventHandler {
|
||||
ledger: Weak<ledger::Manager>,
|
||||
trezor: Weak<trezor::Manager>,
|
||||
}
|
||||
|
||||
impl EventHandler {
|
||||
/// Trezor event handler constructor
|
||||
pub fn new(ledger: Weak<ledger::Manager>, trezor: Weak<trezor::Manager>) -> Self {
|
||||
Self { ledger, trezor }
|
||||
}
|
||||
|
||||
fn extract_device_info(device: &libusb::Device) -> Result<(u16, u16), Error> {
|
||||
let desc = device.device_descriptor()?;
|
||||
Ok((desc.vendor_id(), desc.product_id()))
|
||||
}
|
||||
}
|
||||
|
||||
impl libusb::Hotplug for EventHandler {
|
||||
fn device_arrived(&mut self, device: libusb::Device) {
|
||||
// Upgrade reference to an Arc
|
||||
if let (Some(ledger), Some(trezor)) = (self.ledger.upgrade(), self.trezor.upgrade()) {
|
||||
// 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,
|
||||
) {
|
||||
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,
|
||||
) {
|
||||
trace!(target: "hw", "Ledger device was detected but connection failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn device_left(&mut self, device: libusb::Device) {
|
||||
// Upgrade reference to an Arc
|
||||
if let (Some(ledger), Some(trezor)) = (self.ledger.upgrade(), self.trezor.upgrade()) {
|
||||
// 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,
|
||||
) {
|
||||
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,
|
||||
) {
|
||||
trace!(target: "hw", "Ledger device was detected but disconnection failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper to determine if a device is a valid HID
|
||||
pub fn is_valid_hid_device(usage_page: u16, interface_number: i32) -> bool {
|
||||
usage_page == HID_GLOBAL_USAGE_PAGE || interface_number == HID_USB_DEVICE_CLASS as i32
|
||||
}
|
@ -1,540 +0,0 @@
|
||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity Ethereum.
|
||||
|
||||
// Parity Ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity Ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Trezor hardware wallet module. Supports Trezor v1.
|
||||
//! See <http://doc.satoshilabs.com/trezor-tech/api-protobuf.html>
|
||||
//! and <https://github.com/trezor/trezor-common/blob/master/protob/protocol.md>
|
||||
//! for protocol details.
|
||||
|
||||
use std::{
|
||||
cmp::{max, min},
|
||||
fmt,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
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 trezor_sys::messages::{
|
||||
ButtonAck, EthereumAddress, EthereumGetAddress, EthereumSignTx, EthereumTxAck,
|
||||
EthereumTxRequest, MessageType, PinMatrixAck,
|
||||
};
|
||||
|
||||
/// Trezor v1 vendor ID
|
||||
const TREZOR_VID: u16 = 0x534c;
|
||||
/// Trezor product IDs
|
||||
const TREZOR_PIDS: [u16; 1] = [0x0001];
|
||||
|
||||
const ETH_DERIVATION_PATH: [u32; 5] = [0x8000_002C, 0x8000_003C, 0x8000_0000, 0, 0]; // m/44'/60'/0'/0/0
|
||||
const ETC_DERIVATION_PATH: [u32; 5] = [0x8000_002C, 0x8000_003D, 0x8000_0000, 0, 0]; // m/44'/61'/0'/0/0
|
||||
|
||||
/// Hardware wallet error.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Ethereum wallet protocol error.
|
||||
Protocol(&'static str),
|
||||
/// Hidapi error.
|
||||
Usb(hidapi::HidError),
|
||||
/// Libusb error
|
||||
LibUsb(libusb::Error),
|
||||
/// Device with request key is not available.
|
||||
KeyNotFound,
|
||||
/// Signing has been cancelled by user.
|
||||
UserCancel,
|
||||
/// The Message Type given in the trezor RPC call is not something we recognize
|
||||
BadMessageType,
|
||||
/// Trying to read from a closed device at the given path
|
||||
LockedDevice(String),
|
||||
/// Signing messages are not supported by Trezor
|
||||
NoSigningMessage,
|
||||
/// No device arrived
|
||||
NoDeviceArrived,
|
||||
/// No device left
|
||||
NoDeviceLeft,
|
||||
/// Invalid PID or VID
|
||||
InvalidDevice,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Error::Protocol(ref s) => write!(f, "Trezor protocol error: {}", s),
|
||||
Error::Usb(ref e) => write!(f, "USB communication error: {}", e),
|
||||
Error::LibUsb(ref e) => write!(f, "LibUSB communication error: {}", e),
|
||||
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::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"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<hidapi::HidError> for Error {
|
||||
fn from(err: hidapi::HidError) -> Self {
|
||||
Error::Usb(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libusb::Error> for Error {
|
||||
fn from(err: libusb::Error) -> Self {
|
||||
Error::LibUsb(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<protobuf::ProtobufError> for Error {
|
||||
fn from(_: protobuf::ProtobufError) -> Self {
|
||||
Error::Protocol(&"Could not read response from Trezor Device")
|
||||
}
|
||||
}
|
||||
|
||||
/// Trezor device manager
|
||||
pub struct Manager {
|
||||
usb: Arc<Mutex<hidapi::HidApi>>,
|
||||
devices: RwLock<Vec<Device>>,
|
||||
locked_devices: RwLock<Vec<String>>,
|
||||
key_path: RwLock<KeyPath>,
|
||||
}
|
||||
|
||||
/// HID Version used for the Trezor device
|
||||
enum HidVersion {
|
||||
V1,
|
||||
V2,
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
/// Create a new instance.
|
||||
pub fn new(usb: Arc<Mutex<hidapi::HidApi>>) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
usb,
|
||||
devices: RwLock::new(Vec::new()),
|
||||
locked_devices: RwLock::new(Vec::new()),
|
||||
key_path: RwLock::new(KeyPath::Ethereum),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pin_matrix_ack(&self, device_path: &str, pin: &str) -> Result<bool, Error> {
|
||||
let unlocked = {
|
||||
let usb = self.usb.lock();
|
||||
let device = self.open_path(|| usb.open_path(&device_path))?;
|
||||
let t = MessageType::MessageType_PinMatrixAck;
|
||||
let mut m = PinMatrixAck::new();
|
||||
m.set_pin(pin.to_string());
|
||||
self.send_device_message(&device, t, &m)?;
|
||||
let (resp_type, _) = self.read_device_response(&device)?;
|
||||
match resp_type {
|
||||
// Getting an Address back means it's unlocked, this is undocumented behavior
|
||||
MessageType::MessageType_EthereumAddress => Ok(true),
|
||||
// Getting anything else means we didn't unlock it
|
||||
_ => Ok(false),
|
||||
}
|
||||
};
|
||||
self.update_devices(DeviceDirection::Arrived)?;
|
||||
unlocked
|
||||
}
|
||||
|
||||
fn u256_to_be_vec(&self, val: &U256) -> Vec<u8> {
|
||||
let mut buf = [0_u8; 32];
|
||||
val.to_big_endian(&mut buf);
|
||||
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> {
|
||||
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(),
|
||||
)?;
|
||||
// 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
|
||||
// get a signing error and the device sort of locks up on the signing screen
|
||||
self.signing_loop(handle, chain_id, data)
|
||||
}
|
||||
MessageType::MessageType_EthereumTxRequest => {
|
||||
let resp: EthereumTxRequest = protobuf::core::parse_from_bytes(&bytes)?;
|
||||
if resp.has_data_length() {
|
||||
let mut msg = EthereumTxAck::new();
|
||||
let len = resp.get_data_length() as usize;
|
||||
msg.set_data_chunk(data[..len].to_vec());
|
||||
self.send_device_message(handle, MessageType::MessageType_EthereumTxAck, &msg)?;
|
||||
self.signing_loop(handle, chain_id, &data[len..])
|
||||
} else {
|
||||
let v = resp.get_signature_v();
|
||||
let r = H256::from_slice(resp.get_signature_r());
|
||||
let s = H256::from_slice(resp.get_signature_s());
|
||||
if let Some(c_id) = *chain_id {
|
||||
// If there is a chain_id supplied, Trezor will return a v
|
||||
// part of the signature that is already adjusted for EIP-155,
|
||||
// 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,
|
||||
))
|
||||
} 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 };
|
||||
Ok(Signature::from_rsv(&r, &s, adjusted_v as u8))
|
||||
}
|
||||
}
|
||||
}
|
||||
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: &dyn Message,
|
||||
) -> Result<usize, Error> {
|
||||
let msg_id = msg_type as u16;
|
||||
let mut message = msg.write_to_bytes()?;
|
||||
let msg_size = message.len();
|
||||
let mut data = Vec::new();
|
||||
let hid_version = self.probe_hid_version(device)?;
|
||||
// Magic constants
|
||||
data.push(b'#');
|
||||
data.push(b'#');
|
||||
// Convert msg_id to BE and split into bytes
|
||||
data.push(((msg_id >> 8) & 0xFF) as u8);
|
||||
data.push((msg_id & 0xFF) as u8);
|
||||
// Convert msg_size to BE and split into bytes
|
||||
data.push(((msg_size >> 24) & 0xFF) as u8);
|
||||
data.push(((msg_size >> 16) & 0xFF) as u8);
|
||||
data.push(((msg_size >> 8) & 0xFF) as u8);
|
||||
data.push((msg_size & 0xFF) as u8);
|
||||
data.append(&mut message);
|
||||
while data.len() % 63 > 0 {
|
||||
data.push(0);
|
||||
}
|
||||
let mut total_written = 0;
|
||||
for chunk in data.chunks(63) {
|
||||
let mut padded_chunk = match hid_version {
|
||||
HidVersion::V1 => vec![b'?'],
|
||||
HidVersion::V2 => vec![0, b'?'],
|
||||
};
|
||||
padded_chunk.extend_from_slice(&chunk);
|
||||
total_written += device.write(&padded_chunk)?;
|
||||
}
|
||||
Ok(total_written)
|
||||
}
|
||||
|
||||
fn probe_hid_version(&self, device: &hidapi::HidDevice) -> Result<HidVersion, Error> {
|
||||
let mut buf2 = [0xFF_u8; 65];
|
||||
buf2[0] = 0;
|
||||
buf2[1] = 63;
|
||||
let mut buf1 = [0xFF_u8; 64];
|
||||
buf1[0] = 63;
|
||||
if device.write(&buf2)? == 65 {
|
||||
Ok(HidVersion::V2)
|
||||
} else if device.write(&buf1)? == 64 {
|
||||
Ok(HidVersion::V1)
|
||||
} else {
|
||||
Err(Error::Usb("Unable to determine HID Version"))
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
let first_chunk = device.read_timeout(&mut buf, 300_000)?;
|
||||
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 mut data = Vec::new();
|
||||
data.extend_from_slice(&buf[9..]);
|
||||
while data.len() < (msg_size as usize) {
|
||||
device.read_timeout(&mut buf, 10_000)?;
|
||||
data.extend_from_slice(&buf[1..]);
|
||||
}
|
||||
Ok((msg_type, data[..msg_size as usize].to_vec()))
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
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 handle = self.open_path(|| usb.open_path(&device.path))?;
|
||||
let msg_type = MessageType::MessageType_EthereumSignTx;
|
||||
let mut message = EthereumSignTx::new();
|
||||
match *self.key_path.read() {
|
||||
KeyPath::Ethereum => message.set_address_n(ETH_DERIVATION_PATH.to_vec()),
|
||||
KeyPath::EthereumClassic => message.set_address_n(ETC_DERIVATION_PATH.to_vec()),
|
||||
}
|
||||
message.set_nonce(self.u256_to_be_vec(&t_info.nonce));
|
||||
message.set_gas_limit(self.u256_to_be_vec(&t_info.gas_limit));
|
||||
message.set_gas_price(self.u256_to_be_vec(&t_info.gas_price));
|
||||
message.set_value(self.u256_to_be_vec(&t_info.value));
|
||||
|
||||
if let Some(addr) = t_info.to {
|
||||
message.set_to(addr.to_vec())
|
||||
}
|
||||
let first_chunk_length = min(t_info.data.len(), 1024);
|
||||
let chunk = &t_info.data[0..first_chunk_length];
|
||||
message.set_data_initial_chunk(chunk.to_vec());
|
||||
message.set_data_length(t_info.data.len() as u32);
|
||||
if let Some(c_id) = t_info.chain_id {
|
||||
message.set_chain_id(c_id as u32);
|
||||
}
|
||||
|
||||
self.send_device_message(&handle, msg_type, &message)?;
|
||||
|
||||
self.signing_loop(
|
||||
&handle,
|
||||
&t_info.chain_id,
|
||||
&t_info.data[first_chunk_length..],
|
||||
)
|
||||
}
|
||||
|
||||
fn set_key_path(&self, key_path: KeyPath) {
|
||||
*self.key_path.write() = key_path;
|
||||
}
|
||||
|
||||
fn update_devices(&self, device_direction: DeviceDirection) -> Result<usize, Error> {
|
||||
let mut usb = self.usb.lock();
|
||||
usb.refresh_devices();
|
||||
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)
|
||||
})
|
||||
.fold(Vec::new(), |mut v, d| {
|
||||
match self.read_device(&usb, &d) {
|
||||
Ok(info) => {
|
||||
trace!(target: "hw", "Found device: {:?}", info);
|
||||
v.push(info);
|
||||
}
|
||||
Err(e) => trace!(target: "hw", "Error reading device info: {}", e),
|
||||
};
|
||||
v
|
||||
});
|
||||
|
||||
let num_curr_devices = detected_devices.len();
|
||||
*self.devices.write() = detected_devices;
|
||||
|
||||
match device_direction {
|
||||
DeviceDirection::Arrived => {
|
||||
if num_curr_devices > num_prev_devices {
|
||||
Ok(num_curr_devices - num_prev_devices)
|
||||
} else {
|
||||
Err(Error::NoDeviceArrived)
|
||||
}
|
||||
}
|
||||
DeviceDirection::Left => {
|
||||
if num_prev_devices > num_curr_devices {
|
||||
Ok(num_prev_devices - num_curr_devices)
|
||||
} else {
|
||||
Err(Error::NoDeviceLeft)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
match self.get_address(&handle) {
|
||||
Ok(Some(addr)) => Ok(Device {
|
||||
path: dev_info.path.clone(),
|
||||
info: WalletInfo {
|
||||
name,
|
||||
manufacturer,
|
||||
serial,
|
||||
address: addr,
|
||||
},
|
||||
}),
|
||||
Ok(None) => Err(Error::LockedDevice(dev_info.path.clone())),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn list_devices(&self) -> Vec<WalletInfo> {
|
||||
self.devices.read().iter().map(|d| d.info.clone()).collect()
|
||||
}
|
||||
|
||||
fn list_locked_devices(&self) -> Vec<String> {
|
||||
(*self.locked_devices.read()).clone()
|
||||
}
|
||||
|
||||
fn get_wallet(&self, address: &Address) -> Option<WalletInfo> {
|
||||
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> {
|
||||
let typ = MessageType::MessageType_EthereumGetAddress;
|
||||
let mut message = EthereumGetAddress::new();
|
||||
match *self.key_path.read() {
|
||||
KeyPath::Ethereum => message.set_address_n(ETH_DERIVATION_PATH.to_vec()),
|
||||
KeyPath::EthereumClassic => message.set_address_n(ETC_DERIVATION_PATH.to_vec()),
|
||||
}
|
||||
message.set_show_display(false);
|
||||
self.send_device_message(&device, typ, &message)?;
|
||||
|
||||
let (resp_type, bytes) = self.read_device_response(&device)?;
|
||||
match resp_type {
|
||||
MessageType::MessageType_EthereumAddress => {
|
||||
let response: EthereumAddress = protobuf::core::parse_from_bytes(&bytes)?;
|
||||
Ok(Some(From::from(response.get_address())))
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn open_path<R, F>(&self, f: F) -> Result<R, Error>
|
||||
where
|
||||
F: Fn() -> Result<R, &'static str>,
|
||||
{
|
||||
f().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
/// Poll the device in maximum `max_polling_duration` if it doesn't succeed
|
||||
pub fn try_connect_polling(trezor: &Manager, duration: &Duration, dir: DeviceDirection) -> bool {
|
||||
let start_time = Instant::now();
|
||||
while start_time.elapsed() <= *duration {
|
||||
if let Ok(num_devices) = trezor.update_devices(dir) {
|
||||
trace!(target: "hw", "{} Trezor devices {}", num_devices, dir);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Check if the detected device is a Trezor device by checking both the product ID and the vendor ID
|
||||
pub fn is_valid_trezor(vid: u16, pid: u16) -> bool {
|
||||
vid == TREZOR_VID && TREZOR_PIDS.contains(&pid)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
/// 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;
|
||||
|
||||
let manager = HardwareWalletManager::new().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
try_connect_polling(
|
||||
&manager.trezor,
|
||||
&MAX_POLLING_DURATION,
|
||||
DeviceDirection::Arrived
|
||||
),
|
||||
true
|
||||
);
|
||||
|
||||
let addr: Address = manager
|
||||
.list_wallets()
|
||||
.iter()
|
||||
.filter(|d| d.name == "TREZOR".to_string() && d.manufacturer == "SatoshiLabs".to_string())
|
||||
.nth(0)
|
||||
.map(|d| d.address)
|
||||
.unwrap();
|
||||
|
||||
let t_info = TransactionInfo {
|
||||
nonce: U256::from(1),
|
||||
gas_price: U256::from(100),
|
||||
gas_limit: U256::from(21_000),
|
||||
to: Some(Address::from(1337)),
|
||||
chain_id: Some(1),
|
||||
value: U256::from(1_000_000),
|
||||
data: (&[1u8; 3000]).to_vec(),
|
||||
};
|
||||
|
||||
let signature = manager.trezor.sign_transaction(&addr, &t_info);
|
||||
assert!(signature.is_ok());
|
||||
}
|
@ -17,7 +17,6 @@
|
||||
use std::fmt;
|
||||
|
||||
use ethstore::Error as SSError;
|
||||
use hardware_wallet::Error as HardwareError;
|
||||
|
||||
/// Signing error
|
||||
#[derive(Debug)]
|
||||
@ -26,8 +25,6 @@ pub enum SignError {
|
||||
NotUnlocked,
|
||||
/// Account does not exist.
|
||||
NotFound,
|
||||
/// Low-level hardware device error.
|
||||
Hardware(HardwareError),
|
||||
/// Low-level error from store
|
||||
SStore(SSError),
|
||||
}
|
||||
@ -37,18 +34,11 @@ impl fmt::Display for SignError {
|
||||
match *self {
|
||||
SignError::NotUnlocked => write!(f, "Account is locked"),
|
||||
SignError::NotFound => write!(f, "Account does not exist"),
|
||||
SignError::Hardware(ref e) => write!(f, "{}", e),
|
||||
SignError::SStore(ref e) => write!(f, "{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HardwareError> for SignError {
|
||||
fn from(e: HardwareError) -> Self {
|
||||
SignError::Hardware(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SSError> for SignError {
|
||||
fn from(e: SSError) -> Self {
|
||||
SignError::SStore(e)
|
||||
|
@ -22,9 +22,6 @@ mod account_data;
|
||||
mod error;
|
||||
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::{AccountData, Unlock},
|
||||
stores::AddressBook,
|
||||
@ -35,20 +32,16 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use common_types::transaction::{Action, Transaction};
|
||||
use ethkey::{Address, Generator, Message, Password, Public, Random, Secret};
|
||||
use ethstore::{
|
||||
accounts_dir::MemoryDirectory, random_string, EthMultiStore, EthStore, OpaqueSecret,
|
||||
SecretStore, SecretVaultRef, SimpleSecretStore, StoreAccountRef,
|
||||
};
|
||||
use log::{debug, warn};
|
||||
use log::*;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
pub use ethkey::Signature;
|
||||
pub use ethstore::{Derivation, Error, IndexDerivation, KeyFile};
|
||||
pub use hardware_wallet::{
|
||||
Error as HardwareError, HardwareWalletManager, KeyPath, TransactionInfo,
|
||||
};
|
||||
|
||||
pub use self::{account_data::AccountMeta, error::SignError};
|
||||
|
||||
@ -57,10 +50,6 @@ type AccountToken = Password;
|
||||
/// Account management settings.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AccountProviderSettings {
|
||||
/// Enable hardware wallet support.
|
||||
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.
|
||||
@ -80,8 +69,6 @@ pub struct AccountProvider {
|
||||
sstore: Box<dyn SecretStore>,
|
||||
/// Accounts unlocked with rolling tokens
|
||||
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,
|
||||
@ -97,22 +84,6 @@ fn transient_sstore() -> EthMultiStore {
|
||||
impl AccountProvider {
|
||||
/// Creates new account provider.
|
||||
pub fn new(sstore: Box<dyn SecretStore>, settings: AccountProviderSettings) -> Self {
|
||||
let mut hardware_store = None;
|
||||
|
||||
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
|
||||
});
|
||||
hardware_store = Some(manager)
|
||||
}
|
||||
Err(e) => debug!("Error initializing hardware wallets: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(accounts) = sstore.accounts() {
|
||||
for account in accounts
|
||||
.into_iter()
|
||||
@ -135,7 +106,6 @@ impl AccountProvider {
|
||||
address_book: RwLock::new(address_book),
|
||||
sstore: sstore,
|
||||
transient_sstore: transient_sstore(),
|
||||
hardware_store: hardware_store,
|
||||
unlock_keep_secret: settings.unlock_keep_secret,
|
||||
blacklisted_accounts: settings.blacklisted_accounts,
|
||||
}
|
||||
@ -152,7 +122,6 @@ impl AccountProvider {
|
||||
.expect("MemoryDirectory load always succeeds; qed"),
|
||||
),
|
||||
transient_sstore: transient_sstore(),
|
||||
hardware_store: None,
|
||||
unlock_keep_secret: false,
|
||||
blacklisted_accounts: vec![],
|
||||
}
|
||||
@ -262,44 +231,6 @@ impl AccountProvider {
|
||||
Ok(self.accounts()?.first().cloned().unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Returns addresses of hardware accounts.
|
||||
pub fn hardware_accounts(&self) -> Result<Vec<Address>, Error> {
|
||||
if let Some(accounts) = self.hardware_store.as_ref().map(|h| h.list_wallets()) {
|
||||
if !accounts.is_empty() {
|
||||
return Ok(accounts.into_iter().map(|a| a.address).collect());
|
||||
}
|
||||
}
|
||||
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())
|
||||
{
|
||||
None => Err(SignError::NotFound),
|
||||
Some(Err(e)) => Err(SignError::Hardware(e)),
|
||||
Some(Ok(s)) => Ok(s),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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))
|
||||
{
|
||||
None => Err(SignError::NotFound),
|
||||
Some(Err(e)) => Err(SignError::Hardware(e)),
|
||||
Some(Ok(s)) => Ok(s),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns each address along with metadata.
|
||||
pub fn addresses_info(&self) -> HashMap<Address, AccountMeta> {
|
||||
self.address_book.read().get()
|
||||
@ -337,42 +268,8 @@ impl AccountProvider {
|
||||
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()?
|
||||
.into_iter()
|
||||
.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()
|
||||
}
|
||||
|
||||
/// 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))
|
||||
{
|
||||
Ok(AccountMeta {
|
||||
name: info.name,
|
||||
meta: info.manufacturer,
|
||||
uuid: None,
|
||||
})
|
||||
} else {
|
||||
let account = self.sstore.account_ref(&address)?;
|
||||
Ok(AccountMeta {
|
||||
name: self.sstore.name(&account)?,
|
||||
@ -380,7 +277,6 @@ impl AccountProvider {
|
||||
uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a Uuid
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns account public key.
|
||||
pub fn account_public(&self, address: Address, password: &Password) -> Result<Public, Error> {
|
||||
@ -752,54 +648,6 @@ impl AccountProvider {
|
||||
pub fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error> {
|
||||
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))
|
||||
{
|
||||
None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound),
|
||||
Some(Err(e)) => Err(From::from(e)),
|
||||
Some(Ok(s)) => Ok(s),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
let t_info = TransactionInfo {
|
||||
nonce: transaction.nonce,
|
||||
gas_price: transaction.gas_price,
|
||||
gas_limit: transaction.gas,
|
||||
to: match transaction.action {
|
||||
Action::Create => None,
|
||||
Action::Call(ref to) => Some(to.clone()),
|
||||
},
|
||||
value: transaction.value,
|
||||
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))
|
||||
{
|
||||
None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound),
|
||||
Some(Err(e)) => Err(From::from(e)),
|
||||
Some(Ok(s)) => Ok(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -59,10 +59,6 @@ mod test_signer {
|
||||
match self.0.sign(self.1, Some(self.2.clone()), hash) {
|
||||
Err(SignError::NotUnlocked) => unreachable!(),
|
||||
Err(SignError::NotFound) => Err(ethkey::Error::InvalidAddress),
|
||||
Err(SignError::Hardware(err)) => {
|
||||
warn!("Error using hardware wallet for engine: {:?}", err);
|
||||
Err(ethkey::Error::InvalidSecret)
|
||||
}
|
||||
Err(SignError::SStore(accounts::Error::EthKey(err))) => Err(err),
|
||||
Err(SignError::SStore(accounts::Error::EthKeyCrypto(err))) => {
|
||||
warn!("Low level crypto error: {:?}", err);
|
||||
|
@ -102,8 +102,6 @@ mod accounts {
|
||||
.map_err(|e| format!("Could not open keys directory: {}", e))?,
|
||||
);
|
||||
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
|
||||
|
@ -332,10 +332,6 @@ usage! {
|
||||
"Add SHIFT to all port numbers Parity is listening on. Includes network port and all servers (HTTP JSON-RPC, WebSockets JSON-RPC, IPFS, SecretStore).",
|
||||
|
||||
["Account Options"]
|
||||
FLAG flag_no_hardware_wallets: (bool) = false, or |c: &Config| c.account.as_ref()?.disable_hardware.clone(),
|
||||
"--no-hardware-wallets",
|
||||
"Disables hardware wallet support.",
|
||||
|
||||
FLAG flag_fast_unlock: (bool) = false, or |c: &Config| c.account.as_ref()?.fast_unlock.clone(),
|
||||
"--fast-unlock",
|
||||
"Use drastically faster unlocking mode. This setting causes raw secrets to be stored unprotected in memory, so use with care.",
|
||||
@ -1191,7 +1187,6 @@ struct Account {
|
||||
password: Option<Vec<String>>,
|
||||
keys_iterations: Option<u32>,
|
||||
refresh_time: Option<u64>,
|
||||
disable_hardware: Option<bool>,
|
||||
fast_unlock: Option<bool>,
|
||||
}
|
||||
|
||||
@ -1719,7 +1714,6 @@ mod tests {
|
||||
arg_password: vec!["~/.safe/password.file".into()],
|
||||
arg_keys_iterations: 10240u32,
|
||||
arg_accounts_refresh: 5u64,
|
||||
flag_no_hardware_wallets: false,
|
||||
flag_fast_unlock: false,
|
||||
|
||||
// -- Private Transactions Options
|
||||
@ -2009,7 +2003,6 @@ mod tests {
|
||||
password: Some(vec!["passwdfile path".into()]),
|
||||
keys_iterations: None,
|
||||
refresh_time: None,
|
||||
disable_hardware: None,
|
||||
fast_unlock: None,
|
||||
}),
|
||||
ui: Some(Ui {
|
||||
|
@ -616,7 +616,6 @@ impl Configuration {
|
||||
.map(|s| replace_home(&self.directories().base, s))
|
||||
.collect(),
|
||||
unlocked_accounts: to_addresses(&self.args.arg_unlock)?,
|
||||
enable_hardware_wallets: !self.args.flag_no_hardware_wallets,
|
||||
enable_fast_unlock: self.args.flag_fast_unlock,
|
||||
};
|
||||
|
||||
|
@ -242,7 +242,6 @@ pub struct AccountsConfig {
|
||||
pub testnet: bool,
|
||||
pub password_files: Vec<String>,
|
||||
pub unlocked_accounts: Vec<Address>,
|
||||
pub enable_hardware_wallets: bool,
|
||||
pub enable_fast_unlock: bool,
|
||||
}
|
||||
|
||||
@ -254,7 +253,6 @@ impl Default for AccountsConfig {
|
||||
testnet: false,
|
||||
password_files: Vec::new(),
|
||||
unlocked_accounts: Vec::new(),
|
||||
enable_hardware_wallets: true,
|
||||
enable_fast_unlock: false,
|
||||
}
|
||||
}
|
||||
|
@ -57,10 +57,6 @@ impl super::Accounts for Signer {
|
||||
data: filled.data,
|
||||
};
|
||||
|
||||
if self.accounts.is_hardware_address(&filled.from) {
|
||||
return hardware_signature(&*self.accounts, filled.from, t, chain_id).map(WithToken::No);
|
||||
}
|
||||
|
||||
let hash = t.hash(chain_id);
|
||||
let signature = signature(&*self.accounts, filled.from, hash, password)?;
|
||||
|
||||
@ -76,24 +72,6 @@ impl super::Accounts for Signer {
|
||||
password: SignWith,
|
||||
hash: SignMessage,
|
||||
) -> Result<WithToken<Signature>> {
|
||||
if self.accounts.is_hardware_address(&address) {
|
||||
return if let SignMessage::Data(data) = hash {
|
||||
let signature = self
|
||||
.accounts
|
||||
.sign_message_with_hardware(&address, &data)
|
||||
// TODO: is this correct? I guess the `token` is the wallet in this context
|
||||
.map(WithToken::No)
|
||||
.map_err(|e| errors::account("Error signing message with hardware_wallet", e));
|
||||
|
||||
signature
|
||||
} else {
|
||||
Err(errors::account(
|
||||
"Error signing message with hardware_wallet",
|
||||
"Message signing is unsupported",
|
||||
))
|
||||
};
|
||||
}
|
||||
|
||||
match hash {
|
||||
SignMessage::Data(data) => {
|
||||
let hash = eth_data_hash(data);
|
||||
@ -109,13 +87,6 @@ impl super::Accounts for Signer {
|
||||
password: SignWith,
|
||||
data: Bytes,
|
||||
) -> Result<WithToken<Bytes>> {
|
||||
if self.accounts.is_hardware_address(&address) {
|
||||
return Err(errors::unsupported(
|
||||
"Decrypting via hardware wallets is not supported.",
|
||||
None,
|
||||
));
|
||||
}
|
||||
|
||||
match password.clone() {
|
||||
SignWith::Nothing => self
|
||||
.accounts
|
||||
@ -173,27 +144,3 @@ fn signature(
|
||||
_ => errors::password(e),
|
||||
})
|
||||
}
|
||||
|
||||
// obtain a hardware signature from the given account.
|
||||
fn hardware_signature(
|
||||
accounts: &AccountProvider,
|
||||
address: Address,
|
||||
t: Transaction,
|
||||
chain_id: Option<u64>,
|
||||
) -> Result<SignedTransaction> {
|
||||
debug_assert!(accounts.is_hardware_address(&address));
|
||||
|
||||
let mut stream = rlp::RlpStream::new();
|
||||
t.rlp_append_unsigned_transaction(&mut stream, chain_id);
|
||||
let signature = accounts
|
||||
.sign_transaction_with_hardware(&address, &t, chain_id, &stream.as_raw())
|
||||
.map_err(|e| {
|
||||
debug!(target: "miner", "Error signing transaction with hardware wallet: {}", e);
|
||||
errors::account("Error signing transaction with hardware wallet", e)
|
||||
})?;
|
||||
|
||||
SignedTransaction::new(t.with_signature(signature, chain_id)).map_err(|e| {
|
||||
debug!(target: "miner", "Hardware wallet has produced invalid signature: {}", e);
|
||||
errors::account("Invalid signature generated", e)
|
||||
})
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ use v1::{
|
||||
errors,
|
||||
},
|
||||
traits::{ParityAccounts, ParityAccountsInfo},
|
||||
types::{AccountInfo, Derive, DeriveHash, DeriveHierarchical, ExtAccountInfo, HwAccountInfo},
|
||||
types::{AccountInfo, Derive, DeriveHash, DeriveHierarchical, ExtAccountInfo},
|
||||
};
|
||||
|
||||
/// Account management (personal) rpc implementation.
|
||||
@ -85,35 +85,6 @@ impl ParityAccountsInfo for ParityAccountsClient {
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn hardware_accounts_info(&self) -> Result<BTreeMap<H160, HwAccountInfo>> {
|
||||
self.deprecation_notice("parity_hardwareAccountsInfo");
|
||||
|
||||
let info = self
|
||||
.accounts
|
||||
.hardware_accounts_info()
|
||||
.map_err(|e| errors::account("Could not fetch account info.", e))?;
|
||||
Ok(info
|
||||
.into_iter()
|
||||
.map(|(a, v)| {
|
||||
(
|
||||
H160::from(a),
|
||||
HwAccountInfo {
|
||||
name: v.name,
|
||||
manufacturer: v.meta,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn locked_hardware_accounts_info(&self) -> Result<Vec<String>> {
|
||||
self.deprecation_notice("parity_lockedHardwareAccountsInfo");
|
||||
|
||||
self.accounts
|
||||
.locked_hardware_accounts()
|
||||
.map_err(|e| errors::account("Error communicating with hardware wallet.", e))
|
||||
}
|
||||
|
||||
fn default_account(&self) -> Result<H160> {
|
||||
self.deprecation_notice("parity_defaultAccount");
|
||||
|
||||
@ -404,14 +375,6 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
.map(Into::into)
|
||||
.map_err(|e| errors::account("Could not sign message.", e))
|
||||
}
|
||||
|
||||
fn hardware_pin_matrix_ack(&self, path: String, pin: String) -> Result<bool> {
|
||||
self.deprecation_notice("parity_hardwarePinMatrixAck");
|
||||
|
||||
self.accounts
|
||||
.hardware_pin_matrix_ack(&path, &pin)
|
||||
.map_err(|e| errors::account("Error communicating with hardware wallet.", e))
|
||||
}
|
||||
}
|
||||
|
||||
fn into_vec<A, B>(a: Vec<A>) -> Vec<B>
|
||||
|
@ -22,7 +22,7 @@ use ethkey::Password;
|
||||
use ethstore::KeyFile;
|
||||
use jsonrpc_core::Result;
|
||||
use jsonrpc_derive::rpc;
|
||||
use v1::types::{AccountInfo, DeriveHash, DeriveHierarchical, ExtAccountInfo, HwAccountInfo};
|
||||
use v1::types::{AccountInfo, DeriveHash, DeriveHierarchical, ExtAccountInfo};
|
||||
|
||||
/// Parity-specific read-only accounts rpc interface.
|
||||
#[rpc(server)]
|
||||
@ -31,14 +31,6 @@ pub trait ParityAccountsInfo {
|
||||
#[rpc(name = "parity_accountsInfo")]
|
||||
fn accounts_info(&self) -> Result<BTreeMap<H160, AccountInfo>>;
|
||||
|
||||
/// Returns hardware accounts information.
|
||||
#[rpc(name = "parity_hardwareAccountsInfo")]
|
||||
fn hardware_accounts_info(&self) -> Result<BTreeMap<H160, HwAccountInfo>>;
|
||||
|
||||
/// Get a list of paths to locked hardware wallets
|
||||
#[rpc(name = "parity_lockedHardwareAccountsInfo")]
|
||||
fn locked_hardware_accounts_info(&self) -> Result<Vec<String>>;
|
||||
|
||||
/// Returns default account for dapp.
|
||||
#[rpc(name = "parity_defaultAccount")]
|
||||
fn default_account(&self) -> Result<H160>;
|
||||
@ -162,8 +154,4 @@ pub trait ParityAccounts {
|
||||
/// Sign raw hash with the key corresponding to address and password.
|
||||
#[rpc(name = "parity_signMessage")]
|
||||
fn sign_message(&self, _: H160, _: Password, _: H256) -> Result<H520>;
|
||||
|
||||
/// Send a PinMatrixAck to a hardware wallet, unlocking it
|
||||
#[rpc(name = "parity_hardwarePinMatrixAck")]
|
||||
fn hardware_pin_matrix_ack(&self, _: String, _: String) -> Result<bool>;
|
||||
}
|
||||
|
@ -60,15 +60,6 @@ pub struct ExtAccountInfo {
|
||||
pub uuid: Option<String>,
|
||||
}
|
||||
|
||||
/// Hardware wallet information.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
|
||||
pub struct HwAccountInfo {
|
||||
/// Device name.
|
||||
pub name: String,
|
||||
/// Device manufacturer.
|
||||
pub manufacturer: String,
|
||||
}
|
||||
|
||||
/// account derived from a signature
|
||||
/// as well as information that tells if it is valid for
|
||||
/// the current chain
|
||||
|
@ -49,9 +49,7 @@ mod work;
|
||||
pub mod pubsub;
|
||||
|
||||
pub use self::{
|
||||
account_info::{
|
||||
AccountInfo, EthAccount, ExtAccountInfo, HwAccountInfo, RecoveredAccount, StorageProof,
|
||||
},
|
||||
account_info::{AccountInfo, EthAccount, ExtAccountInfo, RecoveredAccount, StorageProof},
|
||||
block::{Block, BlockTransactions, Header, Rich, RichBlock, RichHeader},
|
||||
block_number::{block_number_to_id, BlockNumber, LightBlockNumber},
|
||||
bytes::Bytes,
|
||||
|
@ -22,7 +22,6 @@ RUN apt-get -y update && \
|
||||
curl make cmake file ca-certificates \
|
||||
g++ gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \
|
||||
libc6-dev-arm64-cross binutils-aarch64-linux-gnu \
|
||||
libudev-dev libudev-dev:arm64 \
|
||||
&& \
|
||||
apt-get clean
|
||||
|
||||
|
@ -6,7 +6,7 @@ RUN apt-get -y update && \
|
||||
apt-get install -y --force-yes --no-install-recommends \
|
||||
curl git make g++ gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf \
|
||||
libc6-dev-armhf-cross wget file ca-certificates \
|
||||
binutils-arm-linux-gnueabihf cmake3 libudev-dev \
|
||||
binutils-arm-linux-gnueabihf cmake3 \
|
||||
&& \
|
||||
apt-get clean
|
||||
|
||||
|
@ -50,4 +50,4 @@ parts:
|
||||
cp -v ethkey $SNAPCRAFT_PART_INSTALL/usr/bin/ethkey
|
||||
cp -v ethstore $SNAPCRAFT_PART_INSTALL/usr/bin/ethstore
|
||||
cp -v whisper $SNAPCRAFT_PART_INSTALL/usr/bin/whisper
|
||||
stage-packages: [libudev1, libstdc++6, cmake, libdb5.3]
|
||||
stage-packages: [libstdc++6, cmake, libdb5.3]
|
||||
|
Loading…
Reference in New Issue
Block a user