Remove support for hardware wallets (#10678)
* Remove support for hardware wallets * Remove rpcs relative hardware support * Remove libudev
This commit is contained in:
parent
67e75e1da1
commit
10c121a299
72
Cargo.lock
generated
72
Cargo.lock
generated
@ -783,8 +783,6 @@ dependencies = [
|
|||||||
"ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethkey 0.3.0",
|
"ethkey 0.3.0",
|
||||||
"ethstore 0.2.1",
|
"ethstore 0.2.1",
|
||||||
"fake-hardware-wallet 0.0.1",
|
|
||||||
"hardware-wallet 1.12.0",
|
|
||||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parking_lot 0.7.1 (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.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1359,14 +1357,6 @@ dependencies = [
|
|||||||
"hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.12.19 (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]]
|
[[package]]
|
||||||
name = "fake-simd"
|
name = "fake-simd"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@ -1545,22 +1535,6 @@ dependencies = [
|
|||||||
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.39 (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.6 (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]]
|
[[package]]
|
||||||
name = "hash-db"
|
name = "hash-db"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@ -1588,15 +1562,6 @@ name = "hex"
|
|||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "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.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "home"
|
name = "home"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@ -2071,25 +2036,6 @@ dependencies = [
|
|||||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.3.6 (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.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"libusb-sys 0.2.4 (git+https://github.com/paritytech/libusb-sys)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libusb-sys"
|
|
||||||
version = "0.2.4"
|
|
||||||
source = "git+https://github.com/paritytech/libusb-sys#1d33d9840a82adaf4d6a1a0f5141f022e5676802"
|
|
||||||
dependencies = [
|
|
||||||
"cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linked-hash-map"
|
name = "linked-hash-map"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@ -3106,11 +3052,6 @@ dependencies = [
|
|||||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-xid 0.1.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]]
|
[[package]]
|
||||||
name = "pulldown-cmark"
|
name = "pulldown-cmark"
|
||||||
version = "0.0.3"
|
version = "0.0.3"
|
||||||
@ -4115,14 +4056,6 @@ name = "transient-hashmap"
|
|||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "trie-db"
|
name = "trie-db"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@ -4615,7 +4548,6 @@ dependencies = [
|
|||||||
"checksum heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)" = "<none>"
|
"checksum heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)" = "<none>"
|
||||||
"checksum heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82"
|
"checksum heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82"
|
||||||
"checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa"
|
"checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa"
|
||||||
"checksum hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)" = "<none>"
|
|
||||||
"checksum home 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "80dff82fb58cfbbc617fb9a9184b010be0529201553cda50ad04372bc2333aff"
|
"checksum home 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "80dff82fb58cfbbc617fb9a9184b010be0529201553cda50ad04372bc2333aff"
|
||||||
"checksum http 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "1a10e5b573b9a0146545010f50772b9e8b1dd0a256564cc4307694c68832a2f5"
|
"checksum http 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "1a10e5b573b9a0146545010f50772b9e8b1dd0a256564cc4307694c68832a2f5"
|
||||||
"checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83"
|
"checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83"
|
||||||
@ -4659,8 +4591,6 @@ dependencies = [
|
|||||||
"checksum lazycell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddba4c30a78328befecec92fc94970e53b3ae385827d28620f0f5bb2493081e0"
|
"checksum lazycell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ddba4c30a78328befecec92fc94970e53b3ae385827d28620f0f5bb2493081e0"
|
||||||
"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047"
|
"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047"
|
||||||
"checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2"
|
"checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2"
|
||||||
"checksum libusb 0.3.0 (git+https://github.com/paritytech/libusb-rs)" = "<none>"
|
|
||||||
"checksum libusb-sys 0.2.4 (git+https://github.com/paritytech/libusb-sys)" = "<none>"
|
|
||||||
"checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e"
|
"checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e"
|
||||||
"checksum local-encoding 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1ceb20f39ff7ae42f3ff9795f3986b1daad821caaa1e1732a0944103a5a1a66"
|
"checksum local-encoding 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1ceb20f39ff7ae42f3ff9795f3986b1daad821caaa1e1732a0944103a5a1a66"
|
||||||
"checksum lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775751a3e69bde4df9b38dd00a1b5d6ac13791e4223d4a0506577f0dd27cfb7a"
|
"checksum lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775751a3e69bde4df9b38dd00a1b5d6ac13791e4223d4a0506577f0dd27cfb7a"
|
||||||
@ -4733,7 +4663,6 @@ dependencies = [
|
|||||||
"checksum primal-estimate 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56ea4531dde757b56906493c8604641da14607bf9cdaa80fb9c9cabd2429f8d5"
|
"checksum primal-estimate 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56ea4531dde757b56906493c8604641da14607bf9cdaa80fb9c9cabd2429f8d5"
|
||||||
"checksum primal-sieve 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "da2d6ed369bb4b0273aeeb43f07c105c0117717cbae827b20719438eb2eb798c"
|
"checksum primal-sieve 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "da2d6ed369bb4b0273aeeb43f07c105c0117717cbae827b20719438eb2eb798c"
|
||||||
"checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee"
|
"checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee"
|
||||||
"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 pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07"
|
||||||
"checksum pwasm-utils 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e9135bed7b452e20dbb395a2d519abaf0c46d60e7ecc02daeeab447d29bada1"
|
"checksum pwasm-utils 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e9135bed7b452e20dbb395a2d519abaf0c46d60e7ecc02daeeab447d29bada1"
|
||||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
||||||
@ -4842,7 +4771,6 @@ dependencies = [
|
|||||||
"checksum trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe82f2f0bf1991e163e757baf044282823155dd326e70f44ce2186c3c320cc9"
|
"checksum trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe82f2f0bf1991e163e757baf044282823155dd326e70f44ce2186c3c320cc9"
|
||||||
"checksum transaction-pool 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d8bd3123931aa6e49dd03bc8a2400490e14701d779458d1f1fff1f04c6f666"
|
"checksum transaction-pool 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d8bd3123931aa6e49dd03bc8a2400490e14701d779458d1f1fff1f04c6f666"
|
||||||
"checksum transient-hashmap 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aeb4b191d033a35edfce392a38cdcf9790b6cebcb30fa690c312c29da4dc433e"
|
"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-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 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"
|
"checksum triehash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d26efb4ddf87870fc08dc9a6580dc3061be350d7b9d0eb30aef1c8b4227aa46"
|
||||||
|
@ -36,7 +36,7 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do
|
|||||||
$ curl https://sh.rustup.rs -sSf | sh
|
$ 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:
|
- OSX:
|
||||||
```bash
|
```bash
|
||||||
|
@ -17,12 +17,6 @@ serde = "1.0"
|
|||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
serde_json = "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]
|
[dev-dependencies]
|
||||||
ethereum-types = "0.4"
|
ethereum-types = "0.4"
|
||||||
tempdir = "0.3"
|
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,101 +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 std::fmt;
|
|
||||||
use ethereum_types::U256;
|
|
||||||
use ethkey::{Address, Signature};
|
|
||||||
|
|
||||||
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,402 +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::sync::{Arc, atomic, atomic::AtomicBool, Weak};
|
|
||||||
use std::{fmt, time::Duration};
|
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
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,463 +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::{min, max};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use ethereum_types::{U256, H256, Address};
|
|
||||||
use ethkey::Signature;
|
|
||||||
use hidapi;
|
|
||||||
use libusb;
|
|
||||||
use parking_lot::{Mutex, RwLock};
|
|
||||||
use protobuf::{self, Message, ProtobufEnum};
|
|
||||||
use super::{DeviceDirection, WalletInfo, TransactionInfo, KeyPath, Wallet, Device, is_valid_hid_device};
|
|
||||||
use trezor_sys::messages::{EthereumAddress, PinMatrixAck, MessageType, EthereumTxRequest, EthereumSignTx, EthereumGetAddress, EthereumTxAck, ButtonAck};
|
|
||||||
|
|
||||||
/// 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: &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 ethereum_types::Address;
|
|
||||||
use MAX_POLLING_DURATION;
|
|
||||||
use super::HardwareWalletManager;
|
|
||||||
|
|
||||||
let manager = HardwareWalletManager::new().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(try_connect_polling(&manager.trezor, &MAX_POLLING_DURATION, DeviceDirection::Arrived), true);
|
|
||||||
|
|
||||||
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 std::fmt;
|
||||||
|
|
||||||
use ethstore::{Error as SSError};
|
use ethstore::{Error as SSError};
|
||||||
use hardware_wallet::{Error as HardwareError};
|
|
||||||
|
|
||||||
/// Signing error
|
/// Signing error
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -26,8 +25,6 @@ pub enum SignError {
|
|||||||
NotUnlocked,
|
NotUnlocked,
|
||||||
/// Account does not exist.
|
/// Account does not exist.
|
||||||
NotFound,
|
NotFound,
|
||||||
/// Low-level hardware device error.
|
|
||||||
Hardware(HardwareError),
|
|
||||||
/// Low-level error from store
|
/// Low-level error from store
|
||||||
SStore(SSError),
|
SStore(SSError),
|
||||||
}
|
}
|
||||||
@ -37,18 +34,11 @@ impl fmt::Display for SignError {
|
|||||||
match *self {
|
match *self {
|
||||||
SignError::NotUnlocked => write!(f, "Account is locked"),
|
SignError::NotUnlocked => write!(f, "Account is locked"),
|
||||||
SignError::NotFound => write!(f, "Account does not exist"),
|
SignError::NotFound => write!(f, "Account does not exist"),
|
||||||
SignError::Hardware(ref e) => write!(f, "{}", e),
|
|
||||||
SignError::SStore(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 {
|
impl From<SSError> for SignError {
|
||||||
fn from(e: SSError) -> Self {
|
fn from(e: SSError) -> Self {
|
||||||
SignError::SStore(e)
|
SignError::SStore(e)
|
||||||
|
@ -22,9 +22,6 @@ mod account_data;
|
|||||||
mod error;
|
mod error;
|
||||||
mod stores;
|
mod stores;
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
|
||||||
extern crate fake_hardware_wallet as hardware_wallet;
|
|
||||||
|
|
||||||
use self::account_data::{Unlock, AccountData};
|
use self::account_data::{Unlock, AccountData};
|
||||||
use self::stores::AddressBook;
|
use self::stores::AddressBook;
|
||||||
|
|
||||||
@ -43,7 +40,6 @@ use parking_lot::RwLock;
|
|||||||
|
|
||||||
pub use ethkey::Signature;
|
pub use ethkey::Signature;
|
||||||
pub use ethstore::{Derivation, IndexDerivation, KeyFile, Error};
|
pub use ethstore::{Derivation, IndexDerivation, KeyFile, Error};
|
||||||
pub use hardware_wallet::{Error as HardwareError, HardwareWalletManager, KeyPath, TransactionInfo};
|
|
||||||
|
|
||||||
pub use self::account_data::AccountMeta;
|
pub use self::account_data::AccountMeta;
|
||||||
pub use self::error::SignError;
|
pub use self::error::SignError;
|
||||||
@ -53,10 +49,6 @@ type AccountToken = Password;
|
|||||||
/// Account management settings.
|
/// Account management settings.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct AccountProviderSettings {
|
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.
|
/// Store raw account secret when unlocking the account permanently.
|
||||||
pub unlock_keep_secret: bool,
|
pub unlock_keep_secret: bool,
|
||||||
/// Disallowed accounts.
|
/// Disallowed accounts.
|
||||||
@ -76,8 +68,6 @@ pub struct AccountProvider {
|
|||||||
sstore: Box<SecretStore>,
|
sstore: Box<SecretStore>,
|
||||||
/// Accounts unlocked with rolling tokens
|
/// Accounts unlocked with rolling tokens
|
||||||
transient_sstore: EthMultiStore,
|
transient_sstore: EthMultiStore,
|
||||||
/// Accounts in hardware wallets.
|
|
||||||
hardware_store: Option<HardwareWalletManager>,
|
|
||||||
/// When unlocking account permanently we additionally keep a raw secret in memory
|
/// When unlocking account permanently we additionally keep a raw secret in memory
|
||||||
/// to increase the performance of transaction signing.
|
/// to increase the performance of transaction signing.
|
||||||
unlock_keep_secret: bool,
|
unlock_keep_secret: bool,
|
||||||
@ -92,18 +82,6 @@ fn transient_sstore() -> EthMultiStore {
|
|||||||
impl AccountProvider {
|
impl AccountProvider {
|
||||||
/// Creates new account provider.
|
/// Creates new account provider.
|
||||||
pub fn new(sstore: Box<SecretStore>, settings: AccountProviderSettings) -> Self {
|
pub fn new(sstore: Box<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() {
|
if let Ok(accounts) = sstore.accounts() {
|
||||||
for account in accounts.into_iter().filter(|a| settings.blacklisted_accounts.contains(&a.address)) {
|
for account in accounts.into_iter().filter(|a| settings.blacklisted_accounts.contains(&a.address)) {
|
||||||
warn!("Local Account {} has a blacklisted (known to be weak) address and will be ignored",
|
warn!("Local Account {} has a blacklisted (known to be weak) address and will be ignored",
|
||||||
@ -121,9 +99,8 @@ impl AccountProvider {
|
|||||||
unlocked_secrets: RwLock::new(HashMap::new()),
|
unlocked_secrets: RwLock::new(HashMap::new()),
|
||||||
unlocked: RwLock::new(HashMap::new()),
|
unlocked: RwLock::new(HashMap::new()),
|
||||||
address_book: RwLock::new(address_book),
|
address_book: RwLock::new(address_book),
|
||||||
sstore: sstore,
|
sstore,
|
||||||
transient_sstore: transient_sstore(),
|
transient_sstore: transient_sstore(),
|
||||||
hardware_store: hardware_store,
|
|
||||||
unlock_keep_secret: settings.unlock_keep_secret,
|
unlock_keep_secret: settings.unlock_keep_secret,
|
||||||
blacklisted_accounts: settings.blacklisted_accounts,
|
blacklisted_accounts: settings.blacklisted_accounts,
|
||||||
}
|
}
|
||||||
@ -137,7 +114,6 @@ impl AccountProvider {
|
|||||||
address_book: RwLock::new(AddressBook::transient()),
|
address_book: RwLock::new(AddressBook::transient()),
|
||||||
sstore: Box::new(EthStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed")),
|
sstore: Box::new(EthStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed")),
|
||||||
transient_sstore: transient_sstore(),
|
transient_sstore: transient_sstore(),
|
||||||
hardware_store: None,
|
|
||||||
unlock_keep_secret: false,
|
unlock_keep_secret: false,
|
||||||
blacklisted_accounts: vec![],
|
blacklisted_accounts: vec![],
|
||||||
}
|
}
|
||||||
@ -219,34 +195,6 @@ impl AccountProvider {
|
|||||||
Ok(self.accounts()?.first().cloned().unwrap_or_default())
|
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.
|
/// Returns each address along with metadata.
|
||||||
pub fn addresses_info(&self) -> HashMap<Address, AccountMeta> {
|
pub fn addresses_info(&self) -> HashMap<Address, AccountMeta> {
|
||||||
self.address_book.read().get()
|
self.address_book.read().get()
|
||||||
@ -277,36 +225,14 @@ impl AccountProvider {
|
|||||||
Ok(r)
|
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.
|
/// Returns each account along with name and meta.
|
||||||
pub fn account_meta(&self, address: Address) -> Result<AccountMeta, Error> {
|
pub fn account_meta(&self, address: Address) -> Result<AccountMeta, Error> {
|
||||||
if let Some(info) = self.hardware_store.as_ref().and_then(|s| s.wallet_info(&address)) {
|
let account = self.sstore.account_ref(&address)?;
|
||||||
Ok(AccountMeta {
|
Ok(AccountMeta {
|
||||||
name: info.name,
|
name: self.sstore.name(&account)?,
|
||||||
meta: info.manufacturer,
|
meta: self.sstore.meta(&account)?,
|
||||||
uuid: None,
|
uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a Uuid
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
let account = self.sstore.account_ref(&address)?;
|
|
||||||
Ok(AccountMeta {
|
|
||||||
name: self.sstore.name(&account)?,
|
|
||||||
meta: self.sstore.meta(&account)?,
|
|
||||||
uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a Uuid
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns account public key.
|
/// Returns account public key.
|
||||||
@ -370,10 +296,7 @@ impl AccountProvider {
|
|||||||
let _ = self.sstore.sign(&account, &password, &Default::default())?;
|
let _ = self.sstore.sign(&account, &password, &Default::default())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = AccountData {
|
let data = AccountData { unlock, password };
|
||||||
unlock: unlock,
|
|
||||||
password: password,
|
|
||||||
};
|
|
||||||
|
|
||||||
unlocked.insert(account, data);
|
unlocked.insert(account, data);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -575,36 +498,6 @@ impl AccountProvider {
|
|||||||
self.sstore.set_vault_meta(name, meta)
|
self.sstore.set_vault_meta(name, meta)
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sign message with hardware wallet.
|
|
||||||
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)]
|
#[cfg(test)]
|
||||||
|
@ -59,10 +59,6 @@ mod test_signer {
|
|||||||
match self.0.sign(self.1, Some(self.2.clone()), hash) {
|
match self.0.sign(self.1, Some(self.2.clone()), hash) {
|
||||||
Err(SignError::NotUnlocked) => unreachable!(),
|
Err(SignError::NotUnlocked) => unreachable!(),
|
||||||
Err(SignError::NotFound) => Err(ethkey::Error::InvalidAddress),
|
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::EthKey(err))) => Err(err),
|
||||||
Err(SignError::SStore(accounts::Error::EthKeyCrypto(err))) => {
|
Err(SignError::SStore(accounts::Error::EthKeyCrypto(err))) => {
|
||||||
warn!("Low level crypto error: {:?}", err);
|
warn!("Low level crypto error: {:?}", err);
|
||||||
|
@ -77,8 +77,6 @@ mod accounts {
|
|||||||
upgrade_key_location(&dirs.legacy_keys_path(cfg.testnet), &path);
|
upgrade_key_location(&dirs.legacy_keys_path(cfg.testnet), &path);
|
||||||
let dir = Box::new(RootDiskDirectory::create(&path).map_err(|e| format!("Could not open keys directory: {}", e))?);
|
let dir = Box::new(RootDiskDirectory::create(&path).map_err(|e| format!("Could not open keys directory: {}", e))?);
|
||||||
let account_settings = AccountProviderSettings {
|
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,
|
unlock_keep_secret: cfg.enable_fast_unlock,
|
||||||
blacklisted_accounts: match *spec {
|
blacklisted_accounts: match *spec {
|
||||||
SpecType::Morden | SpecType::Ropsten | SpecType::Kovan | SpecType::Sokol | SpecType::Dev => vec![],
|
SpecType::Morden | SpecType::Ropsten | SpecType::Kovan | SpecType::Sokol | SpecType::Dev => vec![],
|
||||||
|
@ -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).",
|
"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"]
|
["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(),
|
FLAG flag_fast_unlock: (bool) = false, or |c: &Config| c.account.as_ref()?.fast_unlock.clone(),
|
||||||
"--fast-unlock",
|
"--fast-unlock",
|
||||||
"Use drastically faster unlocking mode. This setting causes raw secrets to be stored unprotected in memory, so use with care.",
|
"Use drastically faster unlocking mode. This setting causes raw secrets to be stored unprotected in memory, so use with care.",
|
||||||
@ -1197,7 +1193,6 @@ struct Account {
|
|||||||
password: Option<Vec<String>>,
|
password: Option<Vec<String>>,
|
||||||
keys_iterations: Option<u32>,
|
keys_iterations: Option<u32>,
|
||||||
refresh_time: Option<u64>,
|
refresh_time: Option<u64>,
|
||||||
disable_hardware: Option<bool>,
|
|
||||||
fast_unlock: Option<bool>,
|
fast_unlock: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1767,7 +1762,6 @@ mod tests {
|
|||||||
arg_password: vec!["~/.safe/password.file".into()],
|
arg_password: vec!["~/.safe/password.file".into()],
|
||||||
arg_keys_iterations: 10240u32,
|
arg_keys_iterations: 10240u32,
|
||||||
arg_accounts_refresh: 5u64,
|
arg_accounts_refresh: 5u64,
|
||||||
flag_no_hardware_wallets: false,
|
|
||||||
flag_fast_unlock: false,
|
flag_fast_unlock: false,
|
||||||
|
|
||||||
// -- Private Transactions Options
|
// -- Private Transactions Options
|
||||||
@ -2050,7 +2044,6 @@ mod tests {
|
|||||||
password: Some(vec!["passwdfile path".into()]),
|
password: Some(vec!["passwdfile path".into()]),
|
||||||
keys_iterations: None,
|
keys_iterations: None,
|
||||||
refresh_time: None,
|
refresh_time: None,
|
||||||
disable_hardware: None,
|
|
||||||
fast_unlock: None,
|
fast_unlock: None,
|
||||||
}),
|
}),
|
||||||
ui: Some(Ui {
|
ui: Some(Ui {
|
||||||
|
@ -540,7 +540,6 @@ impl Configuration {
|
|||||||
testnet: self.args.flag_testnet,
|
testnet: self.args.flag_testnet,
|
||||||
password_files: self.args.arg_password.iter().map(|s| replace_home(&self.directories().base, s)).collect(),
|
password_files: self.args.arg_password.iter().map(|s| replace_home(&self.directories().base, s)).collect(),
|
||||||
unlocked_accounts: to_addresses(&self.args.arg_unlock)?,
|
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,
|
enable_fast_unlock: self.args.flag_fast_unlock,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -225,7 +225,6 @@ pub struct AccountsConfig {
|
|||||||
pub testnet: bool,
|
pub testnet: bool,
|
||||||
pub password_files: Vec<String>,
|
pub password_files: Vec<String>,
|
||||||
pub unlocked_accounts: Vec<Address>,
|
pub unlocked_accounts: Vec<Address>,
|
||||||
pub enable_hardware_wallets: bool,
|
|
||||||
pub enable_fast_unlock: bool,
|
pub enable_fast_unlock: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,7 +236,6 @@ impl Default for AccountsConfig {
|
|||||||
testnet: false,
|
testnet: false,
|
||||||
password_files: Vec::new(),
|
password_files: Vec::new(),
|
||||||
unlocked_accounts: Vec::new(),
|
unlocked_accounts: Vec::new(),
|
||||||
enable_hardware_wallets: true,
|
|
||||||
enable_fast_unlock: false,
|
enable_fast_unlock: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,10 +51,6 @@ impl super::Accounts for Signer {
|
|||||||
data: filled.data,
|
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 hash = t.hash(chain_id);
|
||||||
let signature = signature(&*self.accounts, filled.from, hash, password)?;
|
let signature = signature(&*self.accounts, filled.from, hash, password)?;
|
||||||
|
|
||||||
@ -65,19 +61,6 @@ impl super::Accounts for Signer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sign_message(&self, address: Address, password: SignWith, hash: SignMessage) -> Result<WithToken<Signature>> {
|
fn sign_message(&self, address: Address, 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 {
|
match hash {
|
||||||
SignMessage::Data(data) => {
|
SignMessage::Data(data) => {
|
||||||
let hash = eth_data_hash(data);
|
let hash = eth_data_hash(data);
|
||||||
@ -90,10 +73,6 @@ impl super::Accounts for Signer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt(&self, address: Address, password: SignWith, data: Bytes) -> Result<WithToken<Bytes>> {
|
fn decrypt(&self, address: Address, 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() {
|
match password.clone() {
|
||||||
SignWith::Nothing => self.accounts.decrypt(address, None, &DEFAULT_MAC, &data).map(WithToken::No),
|
SignWith::Nothing => self.accounts.decrypt(address, None, &DEFAULT_MAC, &data).map(WithToken::No),
|
||||||
SignWith::Password(pass) => self.accounts.decrypt(address, Some(pass), &DEFAULT_MAC, &data).map(WithToken::No),
|
SignWith::Password(pass) => self.accounts.decrypt(address, Some(pass), &DEFAULT_MAC, &data).map(WithToken::No),
|
||||||
@ -134,23 +113,3 @@ fn signature(accounts: &AccountProvider, address: Address, hash: H256, password:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -29,7 +29,7 @@ use jsonrpc_core::Result;
|
|||||||
use v1::helpers::deprecated::{self, DeprecationNotice};
|
use v1::helpers::deprecated::{self, DeprecationNotice};
|
||||||
use v1::helpers::errors;
|
use v1::helpers::errors;
|
||||||
use v1::traits::{ParityAccounts, ParityAccountsInfo};
|
use v1::traits::{ParityAccounts, ParityAccountsInfo};
|
||||||
use v1::types::{Derive, DeriveHierarchical, DeriveHash,ExtAccountInfo, AccountInfo, HwAccountInfo};
|
use v1::types::{Derive, DeriveHierarchical, DeriveHash, ExtAccountInfo, AccountInfo};
|
||||||
use ethkey::Password;
|
use ethkey::Password;
|
||||||
|
|
||||||
/// Account management (personal) rpc implementation.
|
/// Account management (personal) rpc implementation.
|
||||||
@ -74,23 +74,6 @@ impl ParityAccountsInfo for ParityAccountsClient {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
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> {
|
fn default_account(&self) -> Result<H160> {
|
||||||
self.deprecation_notice("parity_defaultAccount");
|
self.deprecation_notice("parity_defaultAccount");
|
||||||
|
|
||||||
@ -352,12 +335,6 @@ impl ParityAccounts for ParityAccountsClient {
|
|||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.map_err(|e| errors::account("Could not sign message.", e))
|
.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> where
|
fn into_vec<A, B>(a: Vec<A>) -> Vec<B> where
|
||||||
|
@ -23,7 +23,7 @@ use ethereum_types::{H160, H256, H520};
|
|||||||
use ethkey::Password;
|
use ethkey::Password;
|
||||||
use ethstore::KeyFile;
|
use ethstore::KeyFile;
|
||||||
use v1::types::{DeriveHash, DeriveHierarchical, ExtAccountInfo};
|
use v1::types::{DeriveHash, DeriveHierarchical, ExtAccountInfo};
|
||||||
use v1::types::{AccountInfo, HwAccountInfo};
|
use v1::types::AccountInfo;
|
||||||
|
|
||||||
/// Parity-specific read-only accounts rpc interface.
|
/// Parity-specific read-only accounts rpc interface.
|
||||||
#[rpc]
|
#[rpc]
|
||||||
@ -32,14 +32,6 @@ pub trait ParityAccountsInfo {
|
|||||||
#[rpc(name = "parity_accountsInfo")]
|
#[rpc(name = "parity_accountsInfo")]
|
||||||
fn accounts_info(&self) -> Result<BTreeMap<H160, AccountInfo>>;
|
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.
|
/// Returns default account for dapp.
|
||||||
#[rpc(name = "parity_defaultAccount")]
|
#[rpc(name = "parity_defaultAccount")]
|
||||||
fn default_account(&self) -> Result<H160>;
|
fn default_account(&self) -> Result<H160>;
|
||||||
@ -157,8 +149,4 @@ pub trait ParityAccounts {
|
|||||||
/// Sign raw hash with the key corresponding to address and password.
|
/// Sign raw hash with the key corresponding to address and password.
|
||||||
#[rpc(name = "parity_signMessage")]
|
#[rpc(name = "parity_signMessage")]
|
||||||
fn sign_message(&self, H160, Password, H256) -> Result<H520>;
|
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>;
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ pub struct AccountInfo {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Datastructure with proof for one single storage-entry
|
/// Data structure with proof for one single storage-entry
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
|
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct StorageProof {
|
pub struct StorageProof {
|
||||||
@ -60,15 +60,6 @@ pub struct ExtAccountInfo {
|
|||||||
pub uuid: Option<String>,
|
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
|
/// account derived from a signature
|
||||||
/// as well as information that tells if it is valid for
|
/// as well as information that tells if it is valid for
|
||||||
/// the current chain
|
/// the current chain
|
||||||
|
@ -50,7 +50,7 @@ mod eip191;
|
|||||||
pub mod pubsub;
|
pub mod pubsub;
|
||||||
|
|
||||||
pub use self::eip191::{EIP191Version, PresignedTransaction};
|
pub use self::eip191::{EIP191Version, PresignedTransaction};
|
||||||
pub use self::account_info::{AccountInfo, ExtAccountInfo, HwAccountInfo, EthAccount, StorageProof, RecoveredAccount};
|
pub use self::account_info::{AccountInfo, ExtAccountInfo, EthAccount, StorageProof, RecoveredAccount};
|
||||||
pub use self::bytes::Bytes;
|
pub use self::bytes::Bytes;
|
||||||
pub use self::block::{RichBlock, Block, BlockTransactions, Header, RichHeader, Rich};
|
pub use self::block::{RichBlock, Block, BlockTransactions, Header, RichHeader, Rich};
|
||||||
pub use self::block_number::{BlockNumber, LightBlockNumber, block_number_to_id};
|
pub use self::block_number::{BlockNumber, LightBlockNumber, block_number_to_id};
|
||||||
|
@ -2,7 +2,7 @@ FROM ubuntu:xenial
|
|||||||
LABEL MAINTAINER="Parity Technologies <devops-team@parity.io>"
|
LABEL MAINTAINER="Parity Technologies <devops-team@parity.io>"
|
||||||
|
|
||||||
# install tools and dependencies
|
# install tools and dependencies
|
||||||
RUN apt update && apt install -y --no-install-recommends openssl libudev-dev file curl jq
|
RUN apt update && apt install -y --no-install-recommends openssl file curl jq
|
||||||
|
|
||||||
# show backtraces
|
# show backtraces
|
||||||
ENV RUST_BACKTRACE 1
|
ENV RUST_BACKTRACE 1
|
||||||
|
@ -22,7 +22,6 @@ RUN apt-get -y update && \
|
|||||||
curl make cmake file ca-certificates \
|
curl make cmake file ca-certificates \
|
||||||
g++ gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \
|
g++ gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \
|
||||||
libc6-dev-arm64-cross binutils-aarch64-linux-gnu \
|
libc6-dev-arm64-cross binutils-aarch64-linux-gnu \
|
||||||
libudev-dev libudev-dev:arm64 \
|
|
||||||
&& \
|
&& \
|
||||||
apt-get clean
|
apt-get clean
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ RUN apt-get -y update && \
|
|||||||
apt-get install -y --force-yes --no-install-recommends \
|
apt-get install -y --force-yes --no-install-recommends \
|
||||||
curl git make g++ gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf \
|
curl git make g++ gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf \
|
||||||
libc6-dev-armhf-cross wget file ca-certificates \
|
libc6-dev-armhf-cross wget file ca-certificates \
|
||||||
binutils-arm-linux-gnueabihf cmake3 libudev-dev \
|
binutils-arm-linux-gnueabihf cmake3 \
|
||||||
&& \
|
&& \
|
||||||
apt-get clean
|
apt-get clean
|
||||||
|
|
||||||
|
@ -50,4 +50,4 @@ parts:
|
|||||||
cp -v ethkey $SNAPCRAFT_PART_INSTALL/usr/bin/ethkey
|
cp -v ethkey $SNAPCRAFT_PART_INSTALL/usr/bin/ethkey
|
||||||
cp -v ethstore $SNAPCRAFT_PART_INSTALL/usr/bin/ethstore
|
cp -v ethstore $SNAPCRAFT_PART_INSTALL/usr/bin/ethstore
|
||||||
cp -v whisper $SNAPCRAFT_PART_INSTALL/usr/bin/whisper
|
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