mirror of
https://github.com/Qortal/piratewallet-light-cli.git
synced 2025-01-30 02:22:15 +00:00
Merge pull request #1 from zerocurrencycoin/merge_upstream
Merge upstream
This commit is contained in:
commit
096dfcf780
279
Cargo.lock
generated
279
Cargo.lock
generated
@ -133,6 +133,11 @@ name = "autocfg"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.40"
|
||||
@ -171,6 +176,11 @@ name = "base64"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bech32"
|
||||
version = "0.7.1"
|
||||
@ -432,6 +442,14 @@ name = "either"
|
||||
version = "1.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.6"
|
||||
@ -514,6 +532,19 @@ name = "fnv"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "fpe"
|
||||
version = "0.2.0"
|
||||
@ -563,6 +594,11 @@ name = "futures-core"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.1"
|
||||
@ -579,8 +615,11 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -724,6 +763,28 @@ dependencies = [
|
||||
"want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-normalization 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.3.0"
|
||||
@ -740,6 +801,11 @@ dependencies = [
|
||||
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.8.2"
|
||||
@ -860,6 +926,11 @@ dependencies = [
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "maybe-uninit"
|
||||
version = "2.0.0"
|
||||
@ -870,6 +941,20 @@ name = "memchr"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.3.5"
|
||||
@ -942,6 +1027,23 @@ name = "multimap"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.10.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl-sys 0.9.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"security-framework 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"security-framework-sys 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "net2"
|
||||
version = "0.2.33"
|
||||
@ -1022,11 +1124,36 @@ name = "opaque-debug"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl-sys 0.9.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "1.0.2"
|
||||
@ -1429,6 +1556,41 @@ dependencies = [
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"encoding_rs 0.8.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"http-body 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper-tls 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ipnet 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"js-sys 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime_guess 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pin-project-lite 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-futures 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"web-sys 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winreg 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.12"
|
||||
@ -1662,6 +1824,17 @@ dependencies = [
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.8.11"
|
||||
@ -1878,6 +2051,11 @@ dependencies = [
|
||||
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "0.2.21"
|
||||
@ -1922,6 +2100,15 @@ dependencies = [
|
||||
"webpki 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tls"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.3.1"
|
||||
@ -2200,6 +2387,30 @@ name = "typenum"
|
||||
version = "1.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"tinyvec 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.6.0"
|
||||
@ -2228,6 +2439,16 @@ name = "untrusted"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.1.1"
|
||||
@ -2248,6 +2469,11 @@ name = "version_check"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
@ -2283,6 +2509,8 @@ version = "0.2.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen-macro 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -2300,6 +2528,17 @@ dependencies = [
|
||||
"wasm-bindgen-shared 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"js-sys 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasm-bindgen 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"web-sys 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.56"
|
||||
@ -2423,6 +2662,14 @@ name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ws2_32-sys"
|
||||
version = "0.2.1"
|
||||
@ -2504,7 +2751,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zecwallet-cli"
|
||||
version = "1.3.3"
|
||||
version = "1.4.1"
|
||||
dependencies = [
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2541,10 +2788,14 @@ dependencies = [
|
||||
"prost-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"reqwest 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.16.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rust-embed 5.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"secp256k1 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sodiumoxide 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2581,11 +2832,13 @@ dependencies = [
|
||||
"checksum async-trait 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c8df72488e87761e772f14ae0c2480396810e51b2c2ade912f97f0f7e5b95e3c"
|
||||
"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
|
||||
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
||||
"checksum autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
"checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea"
|
||||
"checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491"
|
||||
"checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83"
|
||||
"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
|
||||
"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
|
||||
"checksum base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
|
||||
"checksum bech32 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0089c35ab7c6f2bc55ab23f769913f0ac65b1023e7e74638a1f43128dd5df2"
|
||||
"checksum bellman 0.6.0 (git+https://github.com/zerocurrencycoin/librustzcash.git?rev=0883d7f3fc26b4f615b049151e43c112c8c7b920)" = "<none>"
|
||||
"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f"
|
||||
@ -2620,6 +2873,7 @@ dependencies = [
|
||||
"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
|
||||
"checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e"
|
||||
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
||||
"checksum encoding_rs 0.8.24 (registry+https://github.com/rust-lang/crates.io-index)" = "a51b8cf747471cb9499b6d59e59b0444f4c90eba8968c4e44874e92b5b64ace2"
|
||||
"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9"
|
||||
"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08"
|
||||
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
@ -2629,6 +2883,8 @@ dependencies = [
|
||||
"checksum fixedbitset 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
|
||||
"checksum flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6bd6d6f4752952feb71363cffc9ebac9411b75b87c6ab6058c40c8900cf43c0f"
|
||||
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
|
||||
"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
"checksum fpe 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "21988a326139165b75e3196bc6962ca638e5fb0c95102fbf152a3743174b01e4"
|
||||
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
||||
@ -2636,6 +2892,7 @@ dependencies = [
|
||||
"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef"
|
||||
"checksum futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fcae98ca17d102fd8a3603727b9259fcf7fa4239b603d2142926189bc8999b86"
|
||||
"checksum futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "79564c427afefab1dfb3298535b21eda083ef7935b4f0ecbfcb121f0aec10866"
|
||||
"checksum futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789"
|
||||
"checksum futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "171be33efae63c2d59e6dbba34186fe0d6394fb378069a76dfd80fdcffd43c16"
|
||||
"checksum futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0bae52d6b29cf440e298856fec3965ee6fa71b06aa7495178615953fd669e5f9"
|
||||
"checksum futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d66274fb76985d3c62c886d1da7ac4c0903a8c9f754e8fe0f35a6a6cc39e76"
|
||||
@ -2653,8 +2910,11 @@ dependencies = [
|
||||
"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
|
||||
"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||
"checksum hyper 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96816e1d921eca64d208a85aab4f7798455a8e34229ee5a88c935bdee1b78b14"
|
||||
"checksum hyper-tls 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed"
|
||||
"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
|
||||
"checksum indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d7b3ea5827fcb9d4fda14bf4da5f136f0db2ae9c8f4bd4e2d1c6fde4e6db2"
|
||||
"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
|
||||
"checksum ipnet 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135"
|
||||
"checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
|
||||
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
|
||||
"checksum js-sys 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)" = "367647c532db6f1555d7151e619540ec5f713328235b8c062c6b4f63e84adfe3"
|
||||
@ -2669,8 +2929,11 @@ dependencies = [
|
||||
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||
"checksum log-mdc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7"
|
||||
"checksum log4rs 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "100052474df98158c0738a7d3f4249c99978490178b5f9f68cd835ac57adbd1b"
|
||||
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||
"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
|
||||
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
|
||||
"checksum mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||
"checksum mime_guess 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
|
||||
"checksum miniz_oxide 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6f3f74f726ae935c3f514300cc6773a0c9492abc5e972d42ba0c0ebb88757625"
|
||||
"checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f"
|
||||
"checksum mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3"
|
||||
@ -2678,6 +2941,7 @@ dependencies = [
|
||||
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
|
||||
"checksum miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226"
|
||||
"checksum multimap 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a97fbd5d00e0e37bfb10f433af8f5aaf631e739368dc9fc28286ca81ca4948dc"
|
||||
"checksum native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d"
|
||||
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
|
||||
"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
|
||||
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
|
||||
@ -2687,7 +2951,9 @@ dependencies = [
|
||||
"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
||||
"checksum once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "532c29a261168a45ce28948f9537ddd7a5dd272cc513b3017b1e82a88f962c37"
|
||||
"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
"checksum openssl 0.10.30 (registry+https://github.com/rust-lang/crates.io-index)" = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4"
|
||||
"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
||||
"checksum openssl-sys 0.9.58 (registry+https://github.com/rust-lang/crates.io-index)" = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de"
|
||||
"checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518"
|
||||
"checksum pairing 0.16.0 (git+https://github.com/zerocurrencycoin/librustzcash.git?rev=0883d7f3fc26b4f615b049151e43c112c8c7b920)" = "<none>"
|
||||
"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337"
|
||||
@ -2734,6 +3000,7 @@ dependencies = [
|
||||
"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd"
|
||||
"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
|
||||
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
|
||||
"checksum reqwest 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)" = "e9eaa17ac5d7b838b7503d118fa16ad88f440498bf9ffe5424e621f93190d61e"
|
||||
"checksum ring 0.16.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1ba5a8ec64ee89a76c98c549af81ff14813df09c3e6dc4766c3856da48597a0c"
|
||||
"checksum ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a"
|
||||
"checksum rle-decode-fast 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac"
|
||||
@ -2760,6 +3027,7 @@ dependencies = [
|
||||
"checksum serde-value 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7a663f873dedc4eac1a559d4c6bc0d0b2c34dc5ac4702e105014b8281489e44f"
|
||||
"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
|
||||
"checksum serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)" = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7"
|
||||
"checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
|
||||
"checksum serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35"
|
||||
"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d"
|
||||
"checksum shellwords 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "685f0e9b0efe23d26e60a780d8dcd3ac95e90975814de9bc6f48e5d609b5d0f5"
|
||||
@ -2785,9 +3053,11 @@ dependencies = [
|
||||
"checksum threadpool 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e8dae184447c15d5a6916d973c642aec485105a13cd238192a6927ae3e077d66"
|
||||
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
||||
"checksum tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c1c5676413eaeb1ea35300a0224416f57abc3bd251657e0fafc12c47ff98c060"
|
||||
"checksum tinyvec 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117"
|
||||
"checksum tokio 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d099fa27b9702bed751524694adbe393e18b36b204da91eb1cbbbbb4a5ee2d58"
|
||||
"checksum tokio-macros 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
|
||||
"checksum tokio-rustls 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4adb8b3e5f86b707f1b54e7c15b6de52617a823608ccda98a15d3a24222f265a"
|
||||
"checksum tokio-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343"
|
||||
"checksum tokio-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499"
|
||||
"checksum tonic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4afef9ce97ea39593992cf3fa00ff33b1ad5eb07665b31355df63a690e38c736"
|
||||
"checksum tonic-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0436413ba71545bcc6c2b9a0f9d78d72deb0123c6a75ccdfe7c056f9930f5e52"
|
||||
@ -2813,21 +3083,27 @@ dependencies = [
|
||||
"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382"
|
||||
"checksum typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6"
|
||||
"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
|
||||
"checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||
"checksum unicode-normalization 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977"
|
||||
"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
|
||||
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f"
|
||||
"checksum untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece"
|
||||
"checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
|
||||
"checksum utf8parse 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d"
|
||||
"checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
|
||||
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
|
||||
"checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
"checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e"
|
||||
"checksum want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
|
||||
"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
|
||||
"checksum wasm-bindgen 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)" = "99de4b68939a880d530aed51289a7c7baee154e3ea8ac234b542c49da7134aaf"
|
||||
"checksum wasm-bindgen-backend 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)" = "b58e66a093a7b7571cb76409763c495b8741ac4319ac20acc2b798f6766d92ee"
|
||||
"checksum wasm-bindgen-futures 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3bf1b55e0dc85085cfab2c0c520b977afcf16ac5801ee0de8dde42a4f5649b2a"
|
||||
"checksum wasm-bindgen-macro 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)" = "a80f89daea7b0a67b11f6e9f911422ed039de9963dce00048a653b63d51194bf"
|
||||
"checksum wasm-bindgen-macro-support 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)" = "4f9dbc3734ad6cff6b76b75b7df98c06982becd0055f651465a08f769bca5c61"
|
||||
"checksum wasm-bindgen-shared 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)" = "d907984f8506b3554eab48b8efff723e764ddbf76d4cd4a3fe4196bc00c49a70"
|
||||
@ -2843,6 +3119,7 @@ dependencies = [
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
"checksum winreg 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
|
||||
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
||||
"checksum xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c"
|
||||
"checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "zecwallet-cli"
|
||||
version = "1.3.3"
|
||||
version = "1.4.1"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
|
@ -5,7 +5,7 @@ use std::sync::mpsc::{channel, Sender, Receiver};
|
||||
use log::{info, error};
|
||||
|
||||
use zecwalletlitelib::{commands,
|
||||
lightclient::{LightClient, LightClientConfig},
|
||||
lightclient::{LightClient, LightClientConfig, AddressParameters},
|
||||
};
|
||||
|
||||
pub mod version;
|
||||
@ -243,6 +243,7 @@ pub fn attempt_recover_seed(password: Option<String>) {
|
||||
consensus_branch_id: "000000".to_string(),
|
||||
anchor_offset: 0,
|
||||
data_dir: None,
|
||||
address_params: AddressParameters::new()
|
||||
};
|
||||
|
||||
match LightClient::attempt_recover_seed(&config, password) {
|
||||
|
@ -1 +1 @@
|
||||
pub const VERSION:&str = "1.3.3";
|
||||
pub const VERSION:&str = "1.4.1";
|
||||
|
182
coin-checkpoint.json
Normal file
182
coin-checkpoint.json
Normal file
@ -0,0 +1,182 @@
|
||||
{
|
||||
"coin_list": [
|
||||
{
|
||||
"coin_name": "Pirate",
|
||||
"coin_type": 141,
|
||||
"chain_name": "main",
|
||||
"check_points": [
|
||||
{
|
||||
"height": 200000,
|
||||
"hash": "0000000097934aab530005e99148224c3c863f2c785e4934645972dd7c611f70",
|
||||
"sapling_tree": "01395c63eaba3c8557d9dd0e065d14ded04cfd716483db4fe8562ad9e28527114301af3a227432535ecdfca769b6cd15995aebe73e63e8e60b160ad0bdb6a2000c6e1100016cea12e77e0935812534a7142e83be32928c29064e1d79ba385546ab7470b0500000010555af98501d9db6922f3d58bddb1b4a9388be80edfc0c24fd27b8704c5a3a3d0194c07ff8eb9c31492e765636e013127be3e7d4f57109523b2836525e1a67910800013ce632ebfc9371015d9b417ad17273904b5f3d60d65f346ab33b814b276c0b54013661f29c788af7eac0a51a1e0a26be268b5f27cf7b90a4038d60a894aff07945000001830a9e6160576d1d88b4821c63221e0d3cce2466b2d3e168a33ee9f10113a33a014b1a2284e2536b72e62b802d610494a0bce24f2d4529ecaf62eb5b4bbd9ff5240100400289d1673c3f65f945a1554df84890e42cbb859e46b4b2c4fd61b8c2c63c0000014a069a106e49d04eaf2aaf969d6af60421bdc5d0ae34441cf0ff78fda6ec3670"
|
||||
},
|
||||
{
|
||||
"height": 300000,
|
||||
"hash": "000000001598dc03f64b36169a482dfa3d85a10d42cde06aa6cc6a75707389b2",
|
||||
"sapling_tree": "01d7243618851dc06cc59ec16c6d77d8e352af3793cd21c0a9938372bb7157670500120001945ae5bdc14a29faa8e2fd50f01ef6a3a8da167584ce9e82063d4ed17416196f0103ed7a6d19f12d1c442c0057fd078903ef6cd3e3306044d7c0f8fdd36f6fe06a01447f859be85701d6871e2480b5036ea63b6f47c144cf29436e7294be603e1c3001b4ec68e46936f3aebb175fb332509326e8d2a182d1449d53777b4a7e2c29fa5801cfc6e9fbf8f0ca810b0e45cdcebc6e765faaaeb8fa0be187be895e683f0504540000000152ccf74ce6806b561418faa131c772506c83acd3b896cab2663fcfb94d09a45801d5ad937c911d34f096261bf38ccee013ccf89dd50bfe5e54465e3b9506a8a62a01984f57016d4a2bd43c417aab49679e5dd79daba0e1caa26f1f3ce84cf18a54260000013953b793f6fae2bab55d9ab9f8ae549285e768bd97b55e2720922a13c470084e0000013fe4d9c4934ce4be54bd7e7b988c600405efd8ff50c137f63c386f7eab596a0d"
|
||||
},
|
||||
{
|
||||
"height": 400000,
|
||||
"hash": "000000001352818dcd2bc7e348492b3e56cb271c916f1b66a8a90ceda3d9fbe9",
|
||||
"sapling_tree": "012429808a251b696c180f956a64fc2ee13b4af851e7f5cfaa654b3f5f5c42153f0197a61bbbc3aa2a45f4613f6a3282c434ee1550b798c171c63ae59a6502308a2a12000001e4697ffd5f1a87419a435e978a53a16012277a1d39d6d5734504593deae427100001ea4efe5190d322b85b3833d732596501af6817da72bbb8eedc259b15904bae070001a582013efddcf96c3d72c9ba1b5c32bc97b1432dff68b3107da7dcda7c8b7b560156dd572020bbe81b8f1bbb176778e63c92950e86b2226e3c133546fb1598135b01bcfa566647b39a84cd113bb46477fc2284980e840ee3ab54033d116565307e19015af6d19ef4e29ef65bfec0842cdb2ea5359336a29346deaa01d2995a3a8359200000011cb426f6c21d1357bf4ddecdc57654248074fcd5d1e355ed6337fa41dd02a14a0150cc4fd92f7ddf085f838672a0f5253a084307482303a1ccfa481765ac1137020001cd3803c5699e2cfc085f6fc38299025a577db8830771d06bfdea80b823191c52017323a29b18e9c599bf79c1f090f805a0a3f86465450ada3b146b21fa34337f0b013fe4d9c4934ce4be54bd7e7b988c600405efd8ff50c137f63c386f7eab596a0d"
|
||||
},
|
||||
{
|
||||
"height": 500000,
|
||||
"hash": "000000004a7eeb2b19dc893030a233e27be847597a274f65eab9a3234d296b81",
|
||||
"sapling_tree": "01ec34296f83ea0d9572ec5f22f93827a87c5e8c88940a0a6b1bd83d30c3f5d95801992fe16fb4bdd82a128b8cabd8c6aa6a2ffcb1161107180a6b15fec5763edf01130001bec716bf9d7c54f6393fd473199eb1233ec6c61a269ee88fdea8929f7b07942e00000168c5d60aac03942051cc2e36a7b451dd2f8c793323442a27ca162df48ab1b40c01b815ee2b5c8c170875b49af628f2f6f23c0fd7597cf2b80c6ea4fe051f3ec648015d4865c559604752c8cb00b1fc66cec1a89151da65f7b6bc20a15c52773ce804013c4491c9b831cea59d7a4a126cc09e200666f7f9cbb1c447a5d30e9be91a976101252d1c5f686f68811aaba531e873e980d74d15930c5f6e157159a02ba835b50300013d57a5c61c586346335ac915919205560605c3816a6dca236abc586d1b2479620170234b2851ef69f26c8b51e508cb737bbfe2457a6d324a8c9dacdbe26bc7d230019d7b23ff36c0d584b0b188f0940d251476926504528d0385bbb82edb9ba2d61a01e6f14cbdb62ee41fda3790ce94e90b040c1f93e4a0fd647aed078d6f444e2f1301d3ae907d068398445a3b45841f37f1ac8c8e14837447bcb385d456eb2f25f7220001d50920ce022d5f32ea2fdc88d508990613fb6db97ce9cce1991d11e8aabd70340001634120b271dc798dc6438b8aac6bbd122b784b7229455885db6815c327dbb71d"
|
||||
},
|
||||
{
|
||||
"height": 600000,
|
||||
"hash": "00000000b260ab44b2f41b1bb759ae8b1b20022bd43a1c1240bb6bd3c83ee2e6",
|
||||
"sapling_tree": "019d58cd76a859a73f6150d97ee54b2e952122cb9c5be696abc654bc2dbd38554000130000019de0ed5e32a7e7384f48b93d772bfbfef41a8f97474d381824678d5e148a754701491402dd0d16e1b5799e4c9723f7fc59d20a4a679c7a8d7d8b22120d83acce2b0132c4ba5bab4473c47359e72bf699adb2a2fce8af9c49898d094886e78ede055d019c35312a980415baee034e848e6b975c704819cd2dbda66e755238966c59543801ba20f1ff1e0b59ab349f38e88293c29d1c6e5697b088d806b9e58e9257f8933c00000000016448fec862cc7cbd6a48692c771536e0e39ee19973e16c0ed3c003cba30b234401e34a5f3fca4590e01e114e6d20165310b5579bdf468302e3164e825180f1f941000105a213b81e22f1c69bbec210c6e3670e9f89ebb12762b8073a4645fced3ed6480160eed5dec89f889e7320fb965c3239c81b941ed2dc6edeac426be9a30f2e3b06000114067a827bbc4717638ba7699b53b1a85f6c57f2718d00718d35ec2fc82b672001634120b271dc798dc6438b8aac6bbd122b784b7229455885db6815c327dbb71d"
|
||||
},
|
||||
{
|
||||
"height": 700000,
|
||||
"hash": "000000002db31fb7f11a33614ef482797cb3102d1e262aeda13b1de548a7eace",
|
||||
"sapling_tree": "012f9d950a95832028517d4888343a861abc98abd5066442a1250ecb07b5e8c35900130001268bb80b2c4ff18e760d3de5bc099eb7874eb01da5329890d8df7ea093182242000132080350296a7149b42e2b0da18052e9d4d9acb40be133d00424858bded55747000000000001dfa44511f67c337deaa8dee42ca1830d7f2c7ff5e10d05f400bd573a66909240000000013e62436ed3ed8e064d7a2a65bf1fcca19f9c55fbc0a55f74cc7a8c855071c13e01872bf48c046a0bbf889531a20fcc0e164ac56df137412c097fb7f74a6fd5b50c01f64f5db54428a5c1761b71d223cedb931aa1317c060ffbaf365bb34b90649a07018df33ecf3fe27e1fcf0610e261a81c1decb6e9bbc7ab6790dfed7e16bbd624600114067a827bbc4717638ba7699b53b1a85f6c57f2718d00718d35ec2fc82b672001634120b271dc798dc6438b8aac6bbd122b784b7229455885db6815c327dbb71d"
|
||||
},
|
||||
{
|
||||
"height": 800000,
|
||||
"hash": "00000000c9a88eb08320fb87edd6e4bf3bfe94f51b0580e36229c781c50c8d4b",
|
||||
"sapling_tree": "015143a5be8143b6919b106bcfbab6afc869a190e2bb20f5b07f08c7344f91480d01f551bb24c8670a06d8b4d4febe750bed672b075442dc515c81b49e4519087f50140106128b3a8cf6e007c070f034e98e39ed5a437ee52a11d69b3ca90e7d53985b0801812df5933effc7413082bc51be3af6e211161736abbc66da3dd315b021715d730001112cedd386ee3368a0f37acbcdfc80090dc2ddaff6b556c065b77060d651a253000000000001efc4fd28805071c4527abb96e716cf9356a03d63ccd93446dc7b8b6551f21e2c0000000167ede974bd8509dedd7a30d954206c3848b0c4be1faa36299292515c3c36ff0500000143e0f868a312dd4a0de3ddf6cb0e8d32d5352f9141a7e8cbab037fe584a0c451000001bb1d241c1a45e3e74c0eb695946c8d29860f1775f49a0de0e12370bb5530b538"
|
||||
},
|
||||
{
|
||||
"height": 900000,
|
||||
"hash": "00000000df520299974b31272c8f3d8730a4bbc937ceead899fa97d0ce47282a",
|
||||
"sapling_tree": "0188bd0427c3bd5b36532110620ecdde005a6eb358e347e4ce7a40b159d9128f49014a3a0b486148d6c77c12c41fda416b1c5e8b56b17b7e2f65a03caa1ebf771e45140000013dc1586036b7a27982ae87c1cfbedc8884a1a05a36e13f250648577ec17c676b0179e734f9a483743967cb186b363220d548faee590a6a0f7c90d76942def2f30201cd0f095e44ae57bbb1ffdb4f5dfda5e5a06040cf7c9e0a1ccea75e5aab9cc54b00011ec110437a726ede511b91c9e2a3a2b1ac2cce0dbbfe87624147e805f1efbf6601b3bd52786dee708812a310cbdb875da291a763efa980a5f184410078ede66f70000001f342312d7b8a5a7df7ae79bad872c93f14515f55c59ad6933c03f4ba10d20369000001f28782f329819bb0d60a42e7089efece3881624e5ee39ce0a42bff879ce20b53000000012f76a4b0dabb04694975dce6284a84d7384f426bfe5c0d9fe3f9a1ec766118200001bb1d241c1a45e3e74c0eb695946c8d29860f1775f49a0de0e12370bb5530b538"
|
||||
},
|
||||
{
|
||||
"height": 1000000,
|
||||
"hash": "000000005ba1dab60631d4215aaf814151b20a38f33fde0dca014fefcc2c85d4",
|
||||
"sapling_tree": "01f7086353480471759fa3b062aa8eb578671173cc6afef55ace7bd4df154e12030165a2f53a4cf5cac5055394ef856186d6d83da44d7ab395f0643eafb76ba1722c140185c1f694e3164e3e1d0bd13ff38aaefb5e871f164370913941b91a9888a7253b010fca08fa627434b358b03ba016ebf1f954f42950c57250ca1202a08a261d5543000000018df57daa119052900ef34062ee76fd881edb6bad3967f20746a7e3282fd865640141d1eab8f48fbe4648a22cfbbd8feed57c6766a2b861666cf4893cba85a4495101817aa7f8b002b13fc6e8d5f89d3499477c8fe93b58e9468b32cbdd9687f6d50d0178e6b2c2e4c537aeed7cf0fb689558e6cf6930ac32850894fdcb6b199e4d64630172fe32973342f1ed18348fc16e3a4b115f88cec150ef9954965407cbae5b961e00000000000001cdd4472c9b71ea1d2f222645bb813c8fa2b472ee445dac6245efd124ca259e65012f76a4b0dabb04694975dce6284a84d7384f426bfe5c0d9fe3f9a1ec766118200001bb1d241c1a45e3e74c0eb695946c8d29860f1775f49a0de0e12370bb5530b538"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"coin_name": "Zero",
|
||||
"coin_type": 323,
|
||||
"chain_name": "main",
|
||||
"check_points": [
|
||||
{
|
||||
"height": 500000,
|
||||
"hash": "000000c642fda400a464c50dcf310e65efa2627799b9b0c378524205ba2307b",
|
||||
"sapling_tree": "014819ca94dac152e7caad1c4fe0fb339343d30084775a10d792f50e56d85e893e018a6e949342beea95fce4d646a09b5ee792531779d866d1e082ff80f62c6028030b01f6fb9b8cef82c92b949324abfa1c8afb82c73e4b48ee59b5284f396de74f632001ad515dd43a07ad98474e165b44c6281060904bf116d70ecb96acffa3c2551713000188153113124a17b84f77ee3bee893be274f2686a454f66208f3cce49877572160000000001832e857dfe5e64d324e7f3f9b2017860dade173bba1cdd8709dad497d6d8ed4c0001ffbcedcff498ad3d7bef07a93a8d601cb5b6de9b87f62e7629c6583def950d57"
|
||||
},
|
||||
{
|
||||
"height": 600000,
|
||||
"hash": "000000a0e6efed6a536c2c5063c666102fb2f9ddb4a226b34a0894c723519e48",
|
||||
"sapling_tree": "019c80fa055af06b00b2e62234dc88d0f20eb5bdd029f27db5015ac5421c0d971a01843776c7c19531a4b70fd74a2f5300125d93697fb1df874f01860967043ae90d0e000170203c0ea694d0ec23c2b330e38d8a72be85f7cda2433e228938a08f0d357d5301a2f0efece5d0ef22101f1a88856d8263b813c47843af6fdeed8745ebf479f202012aaa5eb3f4659c96c07961311425f6d2469f8f161abdf1cd6050cc9cc1650d3c016a4d5df54decf298a6b83be00f130cec069ea3ef38c8d97dc8c5867dfe5a862501b801046295a11ecd1497a55348057912ce9cce5114466181feb55036a38ee342014cc662fdebea6b1557428d407f28fafdae278480525959863b4a096981178e0b017443ea389a059650f310567225e4626b97c4f7eb4de7f43acd9c5d69392d086f01d108cf831f1721b23323c94d1112f47da6b6c5162d09e3c4afe8d8618c604565012071da2937f4c0d6bdf355976e110708d23ef8f103704cd84f748dba12d6174000015ea99a4cbe8cd1e9b09149545388d53ac264c0e7206bc3251d62aab090aea72a0001c9ee5f761f861d9babfe591dc31ef10cde7f4c42f7c26fdf07ea409f1155262a"
|
||||
},
|
||||
{
|
||||
"height": 700000,
|
||||
"hash": "000001bee8c5bc29fb8d6c3642662ecb479ec85df648e851edaaa0e090b2c797",
|
||||
"sapling_tree": "01ceff4af4f1b52be90b6c7f47b5163a525406ed5cc9ee162a4553ac78f6cadf5b013f22df3d99d52c4a02b5fdde2963eae56d2fd5eb1b2e361ba3e226edf0277005100127ba111aaaccce8f68b1b6941792030921aec1feac1e00aedcc2b147068499410001a81f9b610ea96322730db5a4ef0564d20b5f48bd1a4c1c6c9686904ab542e24500018d28843019ca5028fd6b2b6a0aa94a1df668cc8f75d6feb9aac84d750e39691000015e186c88c3b0bf1eefa57f0cb0c293524366c8d96dd7a04f987aa196234e2c4900017c27144ffb06b60cda7d646861fca487d6a956633e2792c9ab34d7e59125d46c0000011d3e9aeec6ff15da7578faf9a23d8ea894e7aa1a25b80108faecb7b47a66bc0e0000000166d62135626ff20d77abeef13ec68d89e313de4195995cb0d0516bfd5af17b33"
|
||||
},
|
||||
{
|
||||
"height": 800000,
|
||||
"hash": "000001a76b58c2bd8748849ee402d7bb010c862aa20cb82bacdf1008c5a2e2a6",
|
||||
"sapling_tree": "015eb92aa340b783e007fbc59440f58a692983b4df45fcb4bed15f4b0eafeb3438012ec53175dd0c73cb07731c9be676a94a842dd9594b99f23b80a70b56810e1f53100000000001b9162a75faeddb2c05667a0d842586d5cd6a72aae8fa57b5ca70ebcbfb1c532f01ebd3d41b0abe75ef18b40ebc87aa58ed5b9a334fd298c809b9674f2f8948d767017293df4117ab33229928942df3229b1995e9be887e0ba7d023d5e62a49b8d75e0149fdc53dd29217661abcda93b6b66b4eb240778cf5dd361ec5e8314b9c1c4e6c00011202a0c6e857e95ec95473baaf9b2225506c2fdc59d687d6053bf4bc603f347000000101f34ab1470100f029cbc911a899c038c4e1d3c512ab1237bf2e5acb008f696a00012970748afdd3fe833b33c062a30566cccefd8584db94db7cbdfc929c3a06691d0166d62135626ff20d77abeef13ec68d89e313de4195995cb0d0516bfd5af17b33"
|
||||
},
|
||||
{
|
||||
"height": 850000,
|
||||
"hash": "000001eb727c5b8598c20ed93daf60b3e77ed6a91a2b7a18891468dcdbfe7796",
|
||||
"sapling_tree": "01d7e34604e3bfe16b9d9db00a035b9b9c5908cb2d8617a9cd0b2f0aa985c3784b001000000000000161632e3bbc414fc9da858ec9e31f7045d5f14c733e8e0d6f65c372427dcc4d340001910363995296c76f488586c34fb07f81f13d0131c0e84b40e6d8b6c0647d5505016aa7cae4f5a3ff0acec91c22d76a95eb4d23e31ac7c9c0282739097a07f0150a0001c91f78eedc950040276b03b7bfedbcbbc63f56b17d133df9e05184d6adebb52b01ba027141de6eec6be10307b245741660a32b237b3c3bd545cc429425a817792801cfe97171b0120d477eea190aa3a7e3f8aadf3053cc00298bf845e411b5f8b832012908fa845f893de23b144e6f63ad8dfa4db3a57fe3d9630183154d4651d95909012970748afdd3fe833b33c062a30566cccefd8584db94db7cbdfc929c3a06691d0166d62135626ff20d77abeef13ec68d89e313de4195995cb0d0516bfd5af17b33"
|
||||
},
|
||||
{
|
||||
"height": 900000,
|
||||
"hash": "000007b665259adb7cda27acc031c275419b9bcdd8e2aec276adf13244d2216d",
|
||||
"sapling_tree": "014c32057f5f4a9aa19024bd5da7b90e49ec97592c50b634f36edc2dd1b3c0ab1501554efd44243576b55a50c0dc6015834defa3746296507ab81847a418439ec70511000000019a0fed69087c96dccc24cebfcf604ae006ad71ac6ee2211c1a4f1acd0b06936a0124b2a98dd05896655cce70c149b88da85e367f5ca463c11226bb1f4353d5af3301a674c227b6f9845da40a9e613d8b83a77de4e7bdf50917af41fbb1260e87034601ce1dbe60dcbc2d0ff7414c081b75f7e937327f4cf986d4c054ec66ebbad8a95a000169bd72a5264f2d3a3b0d105f5991524455a46de9f4ebb22f17d776aee55f5a4f015c5a0dd38c70e45e84a3b0af98531d87114f69387d557f36270aa98684ea1a640119bbc12a1f05fb36a5d790665511dc2e79c2830606269629030b3d6ba0e07e02016165ac2b7b2427242a8001b7914066693def577f7ff9d9edafad352079af334f0001406b73dc4c25c134d606f35c9bac4143889427b9b9a3581df5c3d18d3dc27b110000018d94e637ad75835c34c7c278d4659387cb14e10e3bce05d0d3d046b22a259908"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"coin_name": "Arrow",
|
||||
"coin_type": 350,
|
||||
"chain_name": "main",
|
||||
"check_points": [
|
||||
{
|
||||
"height": 100000,
|
||||
"hash": "00000440727d0569ca91813da16bb343cdab607ff29bd7fd2b4103841424b86e",
|
||||
"sapling_tree": "01de542845f8800bf80c889d712795097011faa487b4f72237fed1bfcb2312eb44001201c33b65eb801b8c2368f1f3ee859e884804d407a9c67bf879657f1401f676f4720000000000000000018ff64379eea39b853b31498104f7e4989f28841f04b188c8383b2bf58ca6cf6b0000000190b481ec064fad6e52ef6332e879a80d6c73e15becdbeda509701060f5452915019f5f784e9913925738f59fc12acfbde6482e2690a9e22ccf7c930c1a2b932a550000011be3837c8934c94dff21613b92d45a7dd59c68d5ab3d5d2ec07e478674a95c29"
|
||||
},
|
||||
{
|
||||
"height": 200000,
|
||||
"hash": "0000048e814eb61aca1e36d91bea70e86ab99c3e3b3c13db17b8c4781a0ed5ed",
|
||||
"sapling_tree": "0100ad4feafaf58d132e9971a037765cf43b14cb8f71c1114b88e2235df9b9f50a001200000000010d268aa5c23048141f4c96786a48c1393efd212124ccfac30ebf05d7c061d36b0001e4cba4258445203cf2ad554588ad7eead7d40c531d6ea9ea94523703dfbe7c1a0001e1b7fd9d05af03d6536b4dce0b84a5a73d3fb21476b86e679ea389518c692c630169c056902ead5b9291804303e38c06cf7db5cc336d5e4b082f7af30c0d61c31e01d603c4a182cdc7647f36741c0c94d92138e30fbfff20d9f1c82d0365bb224d51010a70e4812c62bdbc42e4873ce488a88c565fac477a953649129b10c5b3b02a2201469e74bac190384ad3b1810ca37b50ee0b6d8c9531c0d17468cbb06cf857255701964f72ecac0b0d1e0fa05823fc2478ac384843eea567eb8cbf472217c2e8d93e01bef9c243f68c8483505cbb5aabb6a6bb4af982d954ecf7a37ccc24e71f8b3b370001dd392551fb2011ff0b68cff4dff53d0c28439c608777f0ced10898b0b019225a011be3837c8934c94dff21613b92d45a7dd59c68d5ab3d5d2ec07e478674a95c29"
|
||||
},
|
||||
{
|
||||
"height": 300000,
|
||||
"hash": "0000124c3aae4da7332b1e426679554bc22956ab2a98f1be0965e4da1fc98162",
|
||||
"sapling_tree": "0110ee26da7d5be7bf6825e4448da0870bd922a5c0f8ed23e6e3c399585af8ef66001301424bb451606d493f97005b67479288c1fce0e21e43a9bfb8c014ff5353b5780a01989cb30db6d624dc24ed437125f69786dc499772b92cff0773ea2f4ce982505701e1564554d7b08a3a17035c3789bc4ee096ac75ffc96542c575c3a19202c80001013867a898b35d35abb1285b23b6f22adbf75b1af1b8b72f375a5ef6c5364857120001673be5165aa17e35addd2bf835bac0dfde31fb0c3a47ca83cad0995298de4d380001622542899e366ee8787d5fb9c994fdb1916fcb3a02560505ccff5a9f43652d150001fef34c9e247c1a5ea772bc28aff7be95a0544f54556b45a48b0e12e4b3a8601501d823476afa720971583a4c9afed58de9858e7046d122cdf89e7d710638cbf41b00000001777d7d6c5e282fd6dd9576c83a704758ef43d1e6d1aa2002de30f1336fa0fd41000000016bd2bac2a3b188fef2d92d9fb03c0ac854493555327955705052ead280c0b305"
|
||||
},
|
||||
{
|
||||
"height": 400000,
|
||||
"hash": "0000040926ae5aa842b49e899397803d556683c4bcb3d3aa1c7f62d567be0e31",
|
||||
"sapling_tree": "019ce99e6514edc78af5c871670d21e3f4ff4f3a1bfaaa95baa51d8a80d0fc0a49001301a7958d1ff975de39ab2fb2e8f59dde7de63bf2b1f164fb5ab34b9b5af03bfd03000162c3a4d390df65f135a5a99478f9d046a01669c56ef83a397220675ad82e365c00000001018784756690147d6e8dc8406273e2126507bfe48f8d27154c170781cb19ad5d01f965b85e72ed5a2511dc3a89a1700e97b123586f708e4a389e978a3eda274a080159e72782417cd40144ee41014fc180e16f1bdc04dcacb51386f49385f35e73390124bbe2bd6526e088fbe2a48134a41c0bfab70d8fa1d8a3d340d86290a41b0160000001cc24ca9e94ac2c3b18e5059db0b23059e05eecc7df1eaeaa923f7206542b1d180001a58ab7eed196ff752c73a2fdc71d384d2a85435a48cdfaab0dee33e52879e333000139599e48c568060d75b47e2b8131ef65ed8ba90c4f8a27119e133e4bed248e1900016bd2bac2a3b188fef2d92d9fb03c0ac854493555327955705052ead280c0b305"
|
||||
},
|
||||
{
|
||||
"height": 500000,
|
||||
"hash": "00000aedfdfaa17e710f81e236487fddf8462f6dd1301d3c3ec9d169a6ba1708",
|
||||
"sapling_tree": "0110120f7ae17a4a2c13f676713eb36af9117470ab2a7086bfec59b4c974812b1800130000000001ce959c030e6224b8277452d06d3a7c22d3b6be78d3a104679f71abf2083e005f01f46a9ba0d66457bac8d6fd69a619f735228a5e48a816551b1275739aab28042001dbb7a5f226f10e8725c5b2c860e12b0f87067d02695a4e5b69eb1f64518de1440001124625d5255e3093a3ac06a4b8e496249bbda1aa46383e7c1238c354a8bd2c6f0144d9fdfa762c0a3c4fb2865dc0998762d0b59d530de253a4dbf4b43b1f2b1811000001ebf82a2f185f69a856817798110b86598a10ac3c5cf688c365b78203638bdd0f01e5de25ff095ab05bf0d5119f74b926a4a23c0d381cf14059915b836ca95425220000000195eab1bea8465f9206ceeb6b6f2c4f56e93135a83c29f8b3f075de462787a71c016bd2bac2a3b188fef2d92d9fb03c0ac854493555327955705052ead280c0b305"
|
||||
},
|
||||
{
|
||||
"height": 600000,
|
||||
"hash": "000005ae4a0aee2ad937dc2880bd8a4147060920b7ae8e12e805252f0c5a035b",
|
||||
"sapling_tree": "01d1e6cfde67bcea4f25efb19ba11a87bdb4ab087d2340f9c600cd54ec45c6c10900130173c74ae393678399d3b442d9e236ce4219108bdaaf57cc2ef5cbf245af271542000000000152a6c324540dfd3796708df6daccac33f5d41f036e93e18c3486add84a80707300000153bed188faad18989948d3e70e34f4778e68d8583b27b3f587fa0530208f0a48010e2155b7a9534aac47e3f33d8fa205725c4c61fb2e2dcb20f058acac9d0c871301db29d7e726bff3b17535e7ef52be51d134e843129ee3c1ff79e2ac3dd0ac6e480166d42b05ffe37f25ed7d2537421eff6f1e89b3d9083150714cd2bebb084da41e0001fef5ec2db8b515a5ee9aceba979102b94102b11dce86af73b6c59e98bfec07000001626713a3ca0cc0a85acc0453abb0f841ae91da0852801d217b1a06e38fc98050000195eab1bea8465f9206ceeb6b6f2c4f56e93135a83c29f8b3f075de462787a71c016bd2bac2a3b188fef2d92d9fb03c0ac854493555327955705052ead280c0b305"
|
||||
},
|
||||
{
|
||||
"height": 700000,
|
||||
"hash": "00000fbe2c3fab45fd721dd917e1bcbbcb2955dab8b952fd0fb336d7f6d5d07d",
|
||||
"sapling_tree": "016bd603a841e7399b0c62baa12c5110a669b90c7ab62e105a1724f75583ff404e019c4830f4f077de41a0f306d7e2f20ee79eed2a2dd2e810cf0b8681d2eba16341130194b241d58012448481ff64220e9343955f9c86ca97447bac63df64fed5f3a40c0001ac4b08c2897005c6f0a9a88d458a4ed3ba1246e1b2b644c05b04089666c9db6e0000000179d712d869c826202146a4cd3be8db841d98811fcb20eecda58ffc2ecfd9ff2a01ce32489d8bbb115acc692cababeac4524889e49a053ba2442e34fab800747e1b0000000139465362fe215a84e9a3250c6e41ad3993aa97ff6b1862ca916035e07102576b0000000001deb6b753217b1334a197aa04f8cefb2d0ad451b935974a8fd085e38c4627c00e0195eab1bea8465f9206ceeb6b6f2c4f56e93135a83c29f8b3f075de462787a71c016bd2bac2a3b188fef2d92d9fb03c0ac854493555327955705052ead280c0b305"
|
||||
},
|
||||
{
|
||||
"height": 750000,
|
||||
"hash": "00000f3c4642e7a989cc9157f94ec517798de934e785e7faad5b5c0f370808f1",
|
||||
"sapling_tree": "013999c8f027cd56173b9350116cb1074f302be34b2cab01173f026db3b009845a01db16706b2dc87538d980cfc741b493c9fc2c1df4c4b71cb52920f87258d2a46f1300012bde89a922506a4cb8812c1d4fd6f354acc3285c210c235359b17cf6dec1c43d0182e0211307891fb8e5643240d37f99c0893d021d777711fd139c5780a7dabf6d00019846229061ba225f46945e2378ab52f575ac35ea76f29b2d6cec393ae76b264601980fbd9a872b80582668b05c4013504c07e666684575564c0a1a6be0e13c9405000001aee5fc53534fcba010ec5b1f994754bb732fe7c046a2a853e474cb5e898dbb0e0128cce7d7e8ade61a1112eedd49ed00bd6e35ef627b27a867a2a7a77f81e2423f0001cbcf0503220ea54de6ab90a66c8acbe4c1832e3af885fceebe6d54e3f7161758000185d4b6ba8adb1a4553d0c307871d5f9cab973ab884c852ffa1c35dd86949c450000001deb6b753217b1334a197aa04f8cefb2d0ad451b935974a8fd085e38c4627c00e0195eab1bea8465f9206ceeb6b6f2c4f56e93135a83c29f8b3f075de462787a71c016bd2bac2a3b188fef2d92d9fb03c0ac854493555327955705052ead280c0b305"
|
||||
},
|
||||
{
|
||||
"height": 800000,
|
||||
"hash": "00000426f78268a4e71cbc54705609cb19a9e8217af105cf06269ebe559244d3",
|
||||
"sapling_tree": "0164ce05c53926ac410b019e3a082ca90e3619cfc336eced86516619ebf5075d1b011413916a638275c9322586c967b2f959ad4f159002e40c9db2645cc4adea1854130000019949e2984fc6f4db0c2e9dd20599533a971c19310df8c2b81da2e120a578b61000018d47e18cc9e7ed7d87e4f2bfe51d0bb2f86a2d824845ce3a0ea5b77d7b6e37180000000158ed4932d0bb11fb2f0d7fde9f2b67b9062377b5e27e89a011f00b13b5f7ab7101e0df4b8889cb5b5bd1ee044167ab38713fe5c33db974a7aab83abc33a650f54300000171b00058eb87a903b05fbe2e0a21def3e7b91a493b4f82cf3a6182bf7600981f000122a958c261b7782ffc879b6dafc0ebcd84e4063e1a734bf1ce0233d2b2e553570001deb6b753217b1334a197aa04f8cefb2d0ad451b935974a8fd085e38c4627c00e0195eab1bea8465f9206ceeb6b6f2c4f56e93135a83c29f8b3f075de462787a71c016bd2bac2a3b188fef2d92d9fb03c0ac854493555327955705052ead280c0b305"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"coin_name": "Snowgem",
|
||||
"coin_type": 407,
|
||||
"chain_name": "main",
|
||||
"check_points": [
|
||||
{
|
||||
"height": 600000,
|
||||
"hash": "0000021cfe348506fad00cba871b8f4bcdd9a4cca3994f28b1000407df761d92",
|
||||
"sapling_tree": "01b8885c05a55a39182ccf21702b5f1afc4e085ffb7b227ba0263903d2fade4924000f01262398c6bf1ff0d0761573c6d169ba0936c362920f08f865d91ac045857d210301436b88ad5ff516508e5413f842d6f90986d108b97fd300bbac58cea5841a572c0000015fd52934be345c2d5e8bb033b8d401ca8b8d53b85a08f3be4ec938bab02fe52b0000000001c2ba65a11e59ec0e2012557bbfab14613486fe233537714899c2f59eb28f6f0d00000164128302eb13c163024ab80fb84d9f4ca4db784ba11004a92cf171be86ab5a4f016ea201d464d9652ffb48d499159032c061c6e0aa53212ac665678d71db06c911016b2b8fba2646423712fbdb0ae99f11559f6a4e613cec1b243e00106d4285b564"
|
||||
},
|
||||
{
|
||||
"height": 700000,
|
||||
"hash": "000001a6deceb520cc4951f003715efd14fc65c1a97e2737bd3d5a857d8d9d1c",
|
||||
"sapling_tree": "01db06746355db3c7279d2e8e2e8b4b48083073dbd98e69d199c02ae99f9b52748001001f2b57428017c51a6fdd33a454e0764310229e715be77a8acd11ea9e4cc11cd15000001d1d3ff50136a697e82c0c7749606eaaccd5ae963c70b02f72afd1efe06f1c351000001fc54a0b32e0b9d89356c31ee8af141fde0fc31377c15f80b5bd5bf6f83fa6867013600bfaf767e3651212cad52361f7e33e30ac4b153727b82bfdd6d6d4890ac37016757ef71f499ed66bcf05c972afc28c52749837aaf98c1c40de5f10f40a41b500142c52ea9b277d51cda09d116a63f57247fc8490e968872745b3e1f1cf234e15c01d908aebcdc0e7f54fd6e01becaa2b0b45597bbfcb5e3c61d9010270ddbb8d22301e83fc3b4d65947abfb0e3d271d4651067682cbb7c3d69ed136ed347ec170e51301d30332146b322761d13daa3a47eab4632455ce77a9d9148eea089c6c4e29983c01dc06efd080d5e112160ebdb713f2e87e7aacd4c5b154191213b5c9b0c6fd575d0126be96c940954c28df18804ff0f300d2b389e6a293980dfb88b591afcad51c600147964935eeb225f0ca16eb4aab3463eb682f15c71b8954533a1a9a5e4f96250f"
|
||||
},
|
||||
{
|
||||
"height": 800000,
|
||||
"hash": "000003e13ca49245a380c16626b504d67ee40c88292ac754049999626055e324",
|
||||
"sapling_tree": "014863f32fd516d60394cb888756f3c0c835fb0cb49ce3552b2c28aef6648d8073013818e7a0f2cbf7bec395a1f74d1721cd1d2d5ead4b753ff091bf84853fe72333110116a24df31e6440be4ee68841713db890d9aefe9569e39dc1b969a0bb3a678934017b3f2f498e4f7c95836ce7ee26136011302cb98974e9c900b7d7ac94c2761b2c000155b4078e6eed095dfeac264787f527e8cb75d86aba92aff0d954608980f9c43b0000000001b3b755070482c8abee0fc9e38891d4991aa4b0f66d8e713d442faa5c0717f15e0136a324a23bb2efec17e6cbdb0e7896959948844c288e4d706dd0e9ce375a6e67000118bf9ccc1a7bbc5f4d52e1c78cced7bcc4c375f665f1ddd277049c6bea63c61500000001b914fa415ba49dd30d68ac595ee00b5388173dcebb3f806fe10eb4297cb6bc64015f9ac2a5bd6cde35afc56c75d963eddc37d2ab0cbcc50318ecd172114d54b256"
|
||||
},
|
||||
{
|
||||
"height": 900000,
|
||||
"hash": "00002e7c029239e9ff4b4b69d26982d73b745077429daa15bec6b4c79adcd2a",
|
||||
"sapling_tree": "01d698a7747016847b1960d717ff36c07587d9984c843400404852685a418b040301bd5ee8d39bf6a5a39c79ad08aaf1d9fca5d8e546685bdf8fbb86feb83640302c12000001b15e0dd20ad1e978c8f4b58d91d2e20363779c65b7d76ad948a979ea683fb75e00012020a0cf96723eaceaa9eed10980ab8ee46a8a42e663216a800c4855472da4300133aafb383425cf55c5fcdcdc12966e1e72a4e4d240b8b7a7793053c183358f4e0001824e5cfdc5ca1549123176ee140259f4a37f2b88adb1f608533eaf85ee6a983601f63c4ecfc37ff78aef612045f393d6a0e7ecb05a6a3cfc4749684e9347e3be550001155a462414df528ccb79c37cea6f62b1955334bb8308f344b6e79387856cc12f01c099fde65ef63bdbbecc07d946a330f3c9149810f5f8b47f65829dc7f585c01d0000000000013f76df8a6e366fe581830290fbc2d38c1ff3738223722be703077a21de821e4a"
|
||||
},
|
||||
{
|
||||
"height": 1000000,
|
||||
"hash": "000005248da0c4bbfb12e7533e56d10de48f42f81cb6e53958b4eaa70e167a7c",
|
||||
"sapling_tree": "01514a4a719b02ad5fa48fc62837a67909299b4359e9a9567da77464a62bb32101001201f3c5ba035c4d5a43b142b9658556a27795c0b6de52f0a173a71f696ae24e9f43018ce5cd33760e6c35fa52a86ba6baf7e91b156a44936a59617ad9eb017a1269180001a30ec20e9799832892d883c3d6afa42bbf14c6a2dfbb640fb03515afe37b145d01611b391da34cd6a1c37da0f3723ca1c82863e941e5522eed555e04323e0f2e64000191aa03029224e8e1612ed0fd36bb16809456bde5302984c3c2df3c629345610b00000000000100ba7ce2f225579cfc433dc49ac3abf87d770e14ac7b6d8a83a0b727fe4d3f3e00000144dea5d145a65ab8448fab3b8da09a53fc208e3fa020fde61425062ac6282d6c00013f76df8a6e366fe581830290fbc2d38c1ff3738223722be703077a21de821e4a"
|
||||
},
|
||||
{
|
||||
"height": 1100000,
|
||||
"hash": "00000594f507d0cb7b05ccfe7b0ebfce47c7a6495d8adafdc22d467903c0b17a",
|
||||
"sapling_tree": "017c83d99edc3196c2f18b54fb8e5940d35ef9d61a2d00a86b2530a394344f710101c2500eb37784b74b6c39843206e1703d07f70289e02342be61e26bc1afd1721d120000000000000148e14c6fd99ab228980cba18169ec2153d99fbbd42bbc665f9ea51165c2e3f4d01c267b63e65bdf67dbd470cdb7ed642b55d0c2fcf128244edbf39d44ab2764d060001746e6c1942902120dda17a6f4a9a626ba0d7a99f2765b854da19a7f61227516e0001429824fda77242c9043b5aa958e5e205577c24985e81a90484d1083d1d7ab72f01252e989177010fe04c0080794d1590921d722b889ccfc6511080df0b8adb660b000000011c258eb2c93f094ddd349255510d76d693d8a4ac96550348d425e3d5e24eb372013f76df8a6e366fe581830290fbc2d38c1ff3738223722be703077a21de821e4a"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -29,7 +29,7 @@ ring = "0.16.9"
|
||||
libflate = "0.1"
|
||||
subtle = "2"
|
||||
threadpool = "1.8.0"
|
||||
num_cpus = "1.13.0"
|
||||
num_cpus = "1.12.0"
|
||||
|
||||
tonic = { version = "0.2.1", features = ["tls", "tls-roots"] }
|
||||
bytes = "0.4"
|
||||
@ -40,6 +40,11 @@ tokio-rustls = { version = "0.13.0" }
|
||||
webpki = "0.21.0"
|
||||
webpki-roots = "0.18.0"
|
||||
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
reqwest = { version = "0.10.8", features = ["blocking", "json"] }
|
||||
|
||||
[dependencies.bellman]
|
||||
git = "https://github.com/zerocurrencycoin/librustzcash.git"
|
||||
rev = "0883d7f3fc26b4f615b049151e43c112c8c7b920"
|
||||
|
@ -307,16 +307,6 @@ impl Command for EncryptCommand {
|
||||
return self.help();
|
||||
}
|
||||
|
||||
// Refuse to encrypt if the bip39 bug has not been fixed
|
||||
use crate::lightwallet::bugs::BugBip39Derivation;
|
||||
if BugBip39Derivation::has_bug(lightclient) {
|
||||
let mut h = vec![];
|
||||
h.push("It looks like your wallet has the bip39bug. Please run 'fixbip39bug' to fix it");
|
||||
h.push("before encrypting your wallet.");
|
||||
h.push("ERROR: Cannot encrypt while wallet has the bip39bug.");
|
||||
return h.join("\n");
|
||||
}
|
||||
|
||||
let passwd = args[0].to_string();
|
||||
|
||||
match lightclient.wallet.write().unwrap().encrypt(passwd) {
|
||||
@ -624,8 +614,9 @@ impl Command for TransactionsCommand {
|
||||
let mut h = vec![];
|
||||
h.push("List all incoming and outgoing transactions from this wallet");
|
||||
h.push("Usage:");
|
||||
h.push("list");
|
||||
h.push("list [allmemos]");
|
||||
h.push("");
|
||||
h.push("If you include the 'allmemos' argument, all memos are returned in their raw hex format");
|
||||
|
||||
h.join("\n")
|
||||
}
|
||||
@ -634,8 +625,107 @@ impl Command for TransactionsCommand {
|
||||
"List all transactions in the wallet".to_string()
|
||||
}
|
||||
|
||||
fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String {
|
||||
format!("{}", lightclient.do_list_transactions().pretty(2))
|
||||
fn exec(&self, args: &[&str], lightclient: &LightClient) -> String {
|
||||
if args.len() > 1 {
|
||||
return format!("Didn't understand arguments\n{}", self.help());
|
||||
}
|
||||
|
||||
let include_memo_hex = if args.len() == 1 {
|
||||
if args[0] == "allmemos" || args[0] == "true" || args[0] == "yes" {
|
||||
true
|
||||
} else {
|
||||
return format!("Couldn't understand first argument '{}'\n{}", args[0], self.help());
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
format!("{}", lightclient.do_list_transactions(include_memo_hex).pretty(2))
|
||||
}
|
||||
}
|
||||
|
||||
struct ImportCommand {}
|
||||
impl Command for ImportCommand {
|
||||
fn help(&self) -> String {
|
||||
let mut h = vec![];
|
||||
h.push("Import an external spending or viewing key into the wallet");
|
||||
h.push("Usage:");
|
||||
h.push("import <spending_key | viewing_key> <birthday> [norescan]");
|
||||
h.push("OR");
|
||||
h.push("import '{'key': <spending_key or viewing_key>, 'birthday': <birthday>, 'norescan': <true>}'");
|
||||
h.push("");
|
||||
h.push("Birthday is the earliest block number that has transactions belonging to the imported key. Rescanning will start from this block. If not sure, you can specify '0', which will start rescanning from the first sapling block.");
|
||||
h.push("Note that you can import only the full spending (private) key or the full viewing key.");
|
||||
|
||||
h.join("\n")
|
||||
}
|
||||
|
||||
|
||||
fn short_help(&self) -> String {
|
||||
"Import spending or viewing keys into the wallet".to_string()
|
||||
}
|
||||
|
||||
fn exec(&self, args: &[&str], lightclient: &LightClient) -> String {
|
||||
if args.len() == 0 || args.len() > 3 {
|
||||
return format!("Insufficient arguments\n\n{}", self.help());
|
||||
}
|
||||
|
||||
let (key, birthday, rescan) = if args.len() == 1 {
|
||||
// If only one arg, parse it as JSON
|
||||
let json_args = match json::parse(&args[0]) {
|
||||
Ok(j) => j,
|
||||
Err(e) => {
|
||||
let es = format!("Couldn't understand JSON: {}", e);
|
||||
return format!("{}\n{}", es, self.help());
|
||||
}
|
||||
};
|
||||
|
||||
if !json_args.is_object() {
|
||||
return format!("Couldn't parse argument as a JSON object\n{}", self.help());
|
||||
}
|
||||
|
||||
if !json_args.has_key("key") {
|
||||
return format!("'key' field is required in the JSON, containing the spending or viewing key to import\n{}", self.help());
|
||||
}
|
||||
|
||||
if !json_args.has_key("birthday") {
|
||||
return format!("'birthday' field is required in the JSON, containing the birthday of the spending or viewing key\n{}", self.help());
|
||||
}
|
||||
|
||||
(json_args["key"].as_str().unwrap().to_string(), json_args["birthday"].as_u64().unwrap(), !json_args.has_key("norescan"))
|
||||
} else {
|
||||
let key = args[0];
|
||||
let birthday = match args[1].parse::<u64>() {
|
||||
Ok(b) => b,
|
||||
Err(_) => return format!("Couldn't parse {} as birthday. Please specify an integer. Ok to use '0'", args[1]),
|
||||
};
|
||||
|
||||
let rescan = if args.len() == 3 {
|
||||
if args[2] == "norescan" || args[2] == "false" || args[2] == "no" {
|
||||
false
|
||||
} else {
|
||||
return format!("Couldn't undestand the argument '{}'. Please pass 'norescan' to prevent rescanning the wallet", args[2]);
|
||||
}
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
(key.to_string(), birthday, rescan)
|
||||
};
|
||||
|
||||
let r = match lightclient.do_import_key(key, birthday) {
|
||||
Ok(r) => r.pretty(2),
|
||||
Err(e) => return format!("Error: {}", e),
|
||||
};
|
||||
|
||||
if rescan {
|
||||
match lightclient.do_rescan() {
|
||||
Ok(_) => {},
|
||||
Err(e) => return format!("Error: Rescan failed: {}", e),
|
||||
};
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
@ -645,7 +735,7 @@ impl Command for HeightCommand {
|
||||
let mut h = vec![];
|
||||
h.push("Get the latest block height that the wallet is at.");
|
||||
h.push("Usage:");
|
||||
h.push("height [do_sync = true | false]");
|
||||
h.push("height");
|
||||
h.push("");
|
||||
h.push("Pass 'true' (default) to sync to the server to get the latest block height. Pass 'false' to get the latest height in the wallet without checking with the server.");
|
||||
|
||||
@ -656,11 +746,7 @@ impl Command for HeightCommand {
|
||||
"Get the latest block height that the wallet is at".to_string()
|
||||
}
|
||||
|
||||
fn exec(&self, args: &[&str], lightclient: &LightClient) -> String {
|
||||
if args.len() > 1 {
|
||||
return format!("Didn't understand arguments\n{}", self.help());
|
||||
}
|
||||
|
||||
fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String {
|
||||
format!("{}", object! { "height" => lightclient.last_scanned_height()}.pretty(2))
|
||||
}
|
||||
}
|
||||
@ -733,29 +819,6 @@ impl Command for NotesCommand {
|
||||
}
|
||||
}
|
||||
|
||||
struct FixBip39BugCommand {}
|
||||
impl Command for FixBip39BugCommand {
|
||||
fn help(&self) -> String {
|
||||
let mut h = vec![];
|
||||
h.push("Detect if the wallet has the Bip39 derivation bug, and fix it automatically");
|
||||
h.push("Usage:");
|
||||
h.push("fixbip39bug");
|
||||
h.push("");
|
||||
|
||||
h.join("\n")
|
||||
}
|
||||
|
||||
fn short_help(&self) -> String {
|
||||
"Detect if the wallet has the Bip39 derivation bug, and fix it automatically".to_string()
|
||||
}
|
||||
|
||||
fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String {
|
||||
use crate::lightwallet::bugs::BugBip39Derivation;
|
||||
|
||||
BugBip39Derivation::fix_bug(lightclient)
|
||||
}
|
||||
}
|
||||
|
||||
struct QuitCommand {}
|
||||
impl Command for QuitCommand {
|
||||
fn help(&self) -> String {
|
||||
@ -792,6 +855,7 @@ pub fn get_commands() -> Box<HashMap<String, Box<dyn Command>>> {
|
||||
map.insert("balance".to_string(), Box::new(BalanceCommand{}));
|
||||
map.insert("addresses".to_string(), Box::new(AddressCommand{}));
|
||||
map.insert("height".to_string(), Box::new(HeightCommand{}));
|
||||
map.insert("import".to_string(), Box::new(ImportCommand{}));
|
||||
map.insert("export".to_string(), Box::new(ExportCommand{}));
|
||||
map.insert("info".to_string(), Box::new(InfoCommand{}));
|
||||
map.insert("send".to_string(), Box::new(SendCommand{}));
|
||||
@ -805,7 +869,6 @@ pub fn get_commands() -> Box<HashMap<String, Box<dyn Command>>> {
|
||||
map.insert("decrypt".to_string(), Box::new(DecryptCommand{}));
|
||||
map.insert("unlock".to_string(), Box::new(UnlockCommand{}));
|
||||
map.insert("lock".to_string(), Box::new(LockCommand{}));
|
||||
map.insert("fixbip39bug".to_string(), Box::new(FixBip39BugCommand{}));
|
||||
|
||||
Box::new(map)
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
#[macro_use]
|
||||
extern crate rust_embed;
|
||||
|
||||
extern crate serde;
|
||||
extern crate serde_derive;
|
||||
|
||||
pub mod lightclient;
|
||||
pub mod grpcconnector;
|
||||
pub mod lightwallet;
|
||||
|
@ -6,7 +6,7 @@ use std::sync::{Arc, RwLock, Mutex, mpsc::channel};
|
||||
use std::sync::atomic::{AtomicI32, AtomicUsize, Ordering};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fs::File;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use std::cmp::{max, min};
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
@ -18,9 +18,7 @@ use threadpool::ThreadPool;
|
||||
|
||||
use json::{object, array, JsonValue};
|
||||
use zcash_primitives::transaction::{TxId, Transaction};
|
||||
use zcash_client_backend::{
|
||||
constants::testnet, constants::mainnet, constants::regtest, encoding::encode_payment_address,
|
||||
};
|
||||
use zcash_client_backend::{constants::testnet, constants::mainnet, constants::regtest,};
|
||||
|
||||
use log::{info, warn, error, LevelFilter};
|
||||
use log4rs::append::rolling_file::RollingFileAppender;
|
||||
@ -59,6 +57,31 @@ impl WalletStatus {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AddressParameters {
|
||||
pub coin_type: Option<u32>,
|
||||
pub hrp_sapling_extended_spending_key: Option<String>,
|
||||
pub hrp_sapling_extended_full_viewing_key: Option<String>,
|
||||
pub hrp_sapling_payment_address: Option<String>,
|
||||
pub b58_pubkey_address_prefix: Option<[u8; 2]>,
|
||||
pub b58_script_address_prefix: Option<[u8; 2]>,
|
||||
}
|
||||
|
||||
impl AddressParameters {
|
||||
pub fn new() -> Self {
|
||||
AddressParameters {
|
||||
coin_type: Some(323), //Zero Default COIN_TYPE
|
||||
hrp_sapling_extended_spending_key: None,
|
||||
hrp_sapling_extended_full_viewing_key: None,
|
||||
hrp_sapling_payment_address: None,
|
||||
b58_pubkey_address_prefix: None,
|
||||
b58_script_address_prefix: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LightClientConfig {
|
||||
pub server : http::Uri,
|
||||
@ -66,7 +89,8 @@ pub struct LightClientConfig {
|
||||
pub sapling_activation_height : u64,
|
||||
pub consensus_branch_id : String,
|
||||
pub anchor_offset : u32,
|
||||
pub data_dir : Option<String>
|
||||
pub data_dir : Option<String>,
|
||||
pub address_params : AddressParameters
|
||||
}
|
||||
|
||||
impl LightClientConfig {
|
||||
@ -80,6 +104,7 @@ impl LightClientConfig {
|
||||
consensus_branch_id : "".to_string(),
|
||||
anchor_offset : ANCHOR_OFFSET,
|
||||
data_dir : dir,
|
||||
address_params : AddressParameters::new()
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,6 +128,7 @@ impl LightClientConfig {
|
||||
consensus_branch_id : info.consensus_branch_id,
|
||||
anchor_offset : ANCHOR_OFFSET,
|
||||
data_dir : None,
|
||||
address_params : AddressParameters::new()
|
||||
};
|
||||
|
||||
Ok((config, info.block_height))
|
||||
@ -148,6 +174,9 @@ impl LightClientConfig {
|
||||
zcash_data_location = dirs::data_dir().expect("Couldn't determine app data directory!");
|
||||
zcash_data_location.push("Zero");
|
||||
} else {
|
||||
if dirs::home_dir().is_none() {
|
||||
info!("Couldn't determine home dir!");
|
||||
}
|
||||
zcash_data_location = dirs::home_dir().expect("Couldn't determine home directory!");
|
||||
zcash_data_location.push(".zero");
|
||||
};
|
||||
@ -175,6 +204,28 @@ impl LightClientConfig {
|
||||
zcash_data_location.into_boxed_path()
|
||||
}
|
||||
|
||||
pub fn get_zcash_params_path(&self) -> io::Result<Box<Path>> {
|
||||
if dirs::home_dir().is_none() {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "Couldn't determine Home Dir"));
|
||||
}
|
||||
|
||||
let mut zcash_params = self.get_zcash_data_path().into_path_buf();
|
||||
zcash_params.push("..");
|
||||
if cfg!(target_os="macos") || cfg!(target_os="windows") {
|
||||
zcash_params.push("ZcashParams");
|
||||
} else {
|
||||
zcash_params.push(".zcash-params");
|
||||
}
|
||||
|
||||
match std::fs::create_dir_all(zcash_params.clone()) {
|
||||
Ok(_) => Ok(zcash_params.into_boxed_path()),
|
||||
Err(e) => {
|
||||
eprintln!("Couldn't create zcash params directory\n{}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_wallet_path(&self) -> Box<Path> {
|
||||
let mut wallet_location = self.get_zcash_data_path().into_path_buf();
|
||||
wallet_location.push(WALLET_NAME);
|
||||
@ -208,8 +259,8 @@ impl LightClientConfig {
|
||||
log_path.into_boxed_path()
|
||||
}
|
||||
|
||||
pub fn get_initial_state(&self, height: u64) -> Option<(u64, &str, &str)> {
|
||||
checkpoints::get_closest_checkpoint(&self.chain_name, height)
|
||||
pub fn get_initial_state(&self, height: u64) -> Option<(u64, String, String)> {
|
||||
checkpoints::get_closest_checkpoint(&self.chain_name, self.get_coin_type(), height)
|
||||
}
|
||||
|
||||
pub fn get_server_or_default(server: Option<String>) -> http::Uri {
|
||||
@ -226,49 +277,106 @@ impl LightClientConfig {
|
||||
}.parse().unwrap()
|
||||
}
|
||||
|
||||
pub fn set_coin_type(&mut self, param: u32) {
|
||||
self.address_params.coin_type = Some(param);
|
||||
}
|
||||
|
||||
pub fn set_hrp_sapling_extended_spending_key(&mut self, param: String) {
|
||||
self.address_params.hrp_sapling_extended_spending_key = Some(param);
|
||||
}
|
||||
|
||||
pub fn set_hrp_sapling_extended_full_viewing_key(&mut self, param: String) {
|
||||
self.address_params.hrp_sapling_extended_full_viewing_key = Some(param);
|
||||
}
|
||||
|
||||
pub fn set_hrp_sapling_payment_address(&mut self, param: String) {
|
||||
self.address_params.hrp_sapling_payment_address = Some(param);
|
||||
}
|
||||
|
||||
pub fn set_b58_pubkey_address_prefix(&mut self, param: [u8; 2]) {
|
||||
self.address_params.b58_pubkey_address_prefix = Some(param);
|
||||
}
|
||||
|
||||
pub fn set_b58_script_address_prefix(&mut self, param: [u8; 2]) {
|
||||
self.address_params.b58_script_address_prefix = Some(param);
|
||||
}
|
||||
|
||||
pub fn get_coin_type(&self) -> u32 {
|
||||
match &self.chain_name[..] {
|
||||
"main" => mainnet::COIN_TYPE,
|
||||
"test" => testnet::COIN_TYPE,
|
||||
"regtest" => regtest::COIN_TYPE,
|
||||
c => panic!("Unknown chain {}", c)
|
||||
match &self.address_params.coin_type {
|
||||
Some(s) => *s,
|
||||
None =>
|
||||
match &self.chain_name[..] {
|
||||
"main" => mainnet::COIN_TYPE,
|
||||
"test" => testnet::COIN_TYPE,
|
||||
"regtest" => regtest::COIN_TYPE,
|
||||
c => panic!("Unknown chain {}", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hrp_sapling_address(&self) -> &str {
|
||||
match &self.chain_name[..] {
|
||||
"main" => mainnet::HRP_SAPLING_PAYMENT_ADDRESS,
|
||||
"test" => testnet::HRP_SAPLING_PAYMENT_ADDRESS,
|
||||
"regtest" => regtest::HRP_SAPLING_PAYMENT_ADDRESS,
|
||||
c => panic!("Unknown chain {}", c)
|
||||
match &self.address_params.hrp_sapling_payment_address {
|
||||
Some(s) => s,
|
||||
None =>
|
||||
match &self.chain_name[..] {
|
||||
"main" => mainnet::HRP_SAPLING_PAYMENT_ADDRESS,
|
||||
"test" => testnet::HRP_SAPLING_PAYMENT_ADDRESS,
|
||||
"regtest" => regtest::HRP_SAPLING_PAYMENT_ADDRESS,
|
||||
c => panic!("Unknown chain {}", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hrp_sapling_private_key(&self) -> &str {
|
||||
match &self.chain_name[..] {
|
||||
"main" => mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
|
||||
"test" => testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
|
||||
"regtest" => regtest::HRP_SAPLING_EXTENDED_SPENDING_KEY,
|
||||
c => panic!("Unknown chain {}", c)
|
||||
match &self.address_params.hrp_sapling_extended_spending_key {
|
||||
Some(s) => s,
|
||||
None =>
|
||||
match &self.chain_name[..] {
|
||||
"main" => mainnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
|
||||
"test" => testnet::HRP_SAPLING_EXTENDED_SPENDING_KEY,
|
||||
"regtest" => regtest::HRP_SAPLING_EXTENDED_SPENDING_KEY,
|
||||
c => panic!("Unknown chain {}", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hrp_sapling_viewing_key(&self) -> &str {
|
||||
match &self.address_params.hrp_sapling_extended_full_viewing_key {
|
||||
Some(s) => s,
|
||||
None =>
|
||||
match &self.chain_name[..] {
|
||||
"main" => mainnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
|
||||
"test" => testnet::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
|
||||
"regtest" => regtest::HRP_SAPLING_EXTENDED_FULL_VIEWING_KEY,
|
||||
c => panic!("Unknown chain {}", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn base58_pubkey_address(&self) -> [u8; 2] {
|
||||
match &self.chain_name[..] {
|
||||
"main" => mainnet::B58_PUBKEY_ADDRESS_PREFIX,
|
||||
"test" => testnet::B58_PUBKEY_ADDRESS_PREFIX,
|
||||
"regtest" => regtest::B58_PUBKEY_ADDRESS_PREFIX,
|
||||
c => panic!("Unknown chain {}", c)
|
||||
match &self.address_params.b58_pubkey_address_prefix {
|
||||
Some(s) => *s,
|
||||
None =>
|
||||
match &self.chain_name[..] {
|
||||
"main" => mainnet::B58_PUBKEY_ADDRESS_PREFIX,
|
||||
"test" => testnet::B58_PUBKEY_ADDRESS_PREFIX,
|
||||
"regtest" => regtest::B58_PUBKEY_ADDRESS_PREFIX,
|
||||
c => panic!("Unknown chain {}", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn base58_script_address(&self) -> [u8; 2] {
|
||||
match &self.chain_name[..] {
|
||||
"main" => mainnet::B58_SCRIPT_ADDRESS_PREFIX,
|
||||
"test" => testnet::B58_SCRIPT_ADDRESS_PREFIX,
|
||||
"regtest" => regtest::B58_SCRIPT_ADDRESS_PREFIX,
|
||||
c => panic!("Unknown chain {}", c)
|
||||
match &self.address_params.b58_script_address_prefix {
|
||||
Some(s) => *s,
|
||||
None =>
|
||||
match &self.chain_name[..] {
|
||||
"main" => mainnet::B58_SCRIPT_ADDRESS_PREFIX,
|
||||
"test" => testnet::B58_SCRIPT_ADDRESS_PREFIX,
|
||||
"regtest" => regtest::B58_SCRIPT_ADDRESS_PREFIX,
|
||||
c => panic!("Unknown chain {}", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,11 +411,22 @@ impl LightClient {
|
||||
let state = self.config.get_initial_state(height);
|
||||
|
||||
match state {
|
||||
Some((height, hash, tree)) => self.wallet.read().unwrap().set_initial_block(height.try_into().unwrap(), hash, tree),
|
||||
Some((height, hash, tree)) => self.wallet.read().unwrap().set_initial_block(height.try_into().unwrap(), &hash, &tree),
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
|
||||
fn write_file_if_not_exists(dir: &Box<Path>, name: &str, bytes: &[u8]) -> io::Result<()> {
|
||||
let mut file_path = dir.to_path_buf();
|
||||
file_path.push(name);
|
||||
if !file_path.exists() {
|
||||
let mut file = File::create(&file_path)?;
|
||||
file.write_all(bytes)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "embed_params")]
|
||||
fn read_sapling_params(&mut self) {
|
||||
// Read Sapling Params
|
||||
@ -339,6 +458,27 @@ impl LightClient {
|
||||
self.sapling_spend.extend_from_slice(sapling_spend);
|
||||
}
|
||||
|
||||
// Ensure that the sapling params are stored on disk properly as well. Only on desktop
|
||||
if cfg!(all(not(target_os="ios"), not(target_os="android"))) {
|
||||
match self.config.get_zcash_params_path() {
|
||||
Ok(zcash_params_dir) => {
|
||||
// Create the sapling output and spend params files
|
||||
match LightClient::write_file_if_not_exists(&zcash_params_dir, "sapling-output.params", &self.sapling_output) {
|
||||
Ok(_) => {},
|
||||
Err(e) => eprintln!("Warning: Couldn't write the output params!\n{}", e)
|
||||
};
|
||||
|
||||
match LightClient::write_file_if_not_exists(&zcash_params_dir, "sapling-spend.params", &self.sapling_spend) {
|
||||
Ok(_) => {},
|
||||
Err(e) => eprintln!("Warning: Couldn't write the output params!\n{}", e)
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -477,12 +617,6 @@ impl LightClient {
|
||||
info!("Read wallet with birthday {}", lc.wallet.read().unwrap().get_first_tx_block());
|
||||
info!("Created LightClient to {}", &config.server);
|
||||
|
||||
if crate::lightwallet::bugs::BugBip39Derivation::has_bug(&lc) {
|
||||
let m = format!("WARNING!!!\nYour wallet has a bip39derivation bug that's showing incorrect addresses.\nPlease run 'fixbip39bug' to automatically fix the address derivation in your wallet!\nPlease see: https://github.com/adityapk00/zecwallet-light-cli/blob/master/bip39bug.md");
|
||||
info!("{}", m);
|
||||
println!("{}", m);
|
||||
}
|
||||
|
||||
Ok(lc)
|
||||
}
|
||||
|
||||
@ -577,11 +711,12 @@ impl LightClient {
|
||||
let wallet = self.wallet.read().unwrap();
|
||||
// Go over all z addresses
|
||||
let z_keys = wallet.get_z_private_keys().iter()
|
||||
.filter( move |(addr, _)| address.is_none() || address.as_ref() == Some(addr))
|
||||
.map( |(addr, pk)|
|
||||
.filter( move |(addr, _, _)| address.is_none() || address.as_ref() == Some(addr))
|
||||
.map( |(addr, pk, vk)|
|
||||
object!{
|
||||
"address" => addr.clone(),
|
||||
"private_key" => pk.clone()
|
||||
"private_key" => pk.clone(),
|
||||
"viewing_key" => vk.clone(),
|
||||
}
|
||||
).collect::<Vec<JsonValue>>();
|
||||
|
||||
@ -609,9 +744,7 @@ impl LightClient {
|
||||
let wallet = self.wallet.read().unwrap();
|
||||
|
||||
// Collect z addresses
|
||||
let z_addresses = wallet.zaddress.read().unwrap().iter().map( |ad| {
|
||||
encode_payment_address(self.config.hrp_sapling_address(), &ad)
|
||||
}).collect::<Vec<String>>();
|
||||
let z_addresses = wallet.get_all_zaddresses();
|
||||
|
||||
// Collect t addresses
|
||||
let t_addresses = wallet.taddresses.read().unwrap().iter().map( |a| a.clone() )
|
||||
@ -627,12 +760,13 @@ impl LightClient {
|
||||
let wallet = self.wallet.read().unwrap();
|
||||
|
||||
// Collect z addresses
|
||||
let z_addresses = wallet.zaddress.read().unwrap().iter().map( |ad| {
|
||||
let address = encode_payment_address(self.config.hrp_sapling_address(), &ad);
|
||||
let z_addresses = wallet.get_all_zaddresses().iter().map(|zaddress| {
|
||||
object!{
|
||||
"address" => address.clone(),
|
||||
"zbalance" => wallet.zbalance(Some(address.clone())),
|
||||
"verified_zbalance" => wallet.verified_zbalance(Some(address)),
|
||||
"address" => zaddress.clone(),
|
||||
"zbalance" => wallet.zbalance(Some(zaddress.clone())),
|
||||
"verified_zbalance" => wallet.verified_zbalance(Some(zaddress.clone())),
|
||||
"spendable_zbalance" => wallet.spendable_zbalance(Some(zaddress.clone())),
|
||||
"unverified_zbalance" => wallet.unverified_zbalance(Some(zaddress.clone()))
|
||||
}
|
||||
}).collect::<Vec<JsonValue>>();
|
||||
|
||||
@ -650,6 +784,8 @@ impl LightClient {
|
||||
object!{
|
||||
"zbalance" => wallet.zbalance(None),
|
||||
"verified_zbalance" => wallet.verified_zbalance(None),
|
||||
"spendable_zbalance" => wallet.spendable_zbalance(None),
|
||||
"unverified_zbalance" => wallet.unverified_zbalance(None),
|
||||
"tbalance" => wallet.tbalance(None),
|
||||
"z_addresses" => z_addresses,
|
||||
"t_addresses" => t_addresses,
|
||||
@ -769,23 +905,40 @@ impl LightClient {
|
||||
let mut spent_notes : Vec<JsonValue> = vec![];
|
||||
let mut pending_notes: Vec<JsonValue> = vec![];
|
||||
|
||||
let anchor_height: i32 = self.wallet.read().unwrap().get_anchor_height() as i32;
|
||||
|
||||
{
|
||||
// Collect Sapling notes
|
||||
let wallet = self.wallet.read().unwrap();
|
||||
|
||||
// First, collect all extfvk's that are spendable (i.e., we have the private key)
|
||||
let spendable_address: HashSet<String> = wallet.get_all_zaddresses().iter()
|
||||
.filter(|address| wallet.have_spending_key_for_zaddress(address))
|
||||
.map(|address| address.clone())
|
||||
.collect();
|
||||
|
||||
// Collect Sapling notes
|
||||
wallet.txs.read().unwrap().iter()
|
||||
.flat_map( |(txid, wtx)| {
|
||||
let spendable_address = spendable_address.clone();
|
||||
wtx.notes.iter().filter_map(move |nd|
|
||||
if !all_notes && nd.spent.is_some() {
|
||||
None
|
||||
} else {
|
||||
let address = LightWallet::note_address(self.config.hrp_sapling_address(), nd);
|
||||
let spendable = address.is_some() &&
|
||||
spendable_address.contains(&address.clone().unwrap()) &&
|
||||
wtx.block <= anchor_height && nd.spent.is_none() && nd.unconfirmed_spent.is_none();
|
||||
|
||||
Some(object!{
|
||||
"created_in_block" => wtx.block,
|
||||
"datetime" => wtx.datetime,
|
||||
"created_in_txid" => format!("{}", txid),
|
||||
"value" => nd.note.value,
|
||||
"is_change" => nd.is_change,
|
||||
"address" => LightWallet::note_address(self.config.hrp_sapling_address(), nd),
|
||||
"address" => address,
|
||||
"spendable" => spendable,
|
||||
"spent" => nd.spent.map(|spent_txid| format!("{}", spent_txid)),
|
||||
"spent_at_height" => nd.spent_at_height.map(|h| format!("{}", h)),
|
||||
"unconfirmed_spent" => nd.unconfirmed_spent.map(|spent_txid| format!("{}", spent_txid)),
|
||||
})
|
||||
}
|
||||
@ -862,7 +1015,7 @@ impl LightClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_list_transactions(&self) -> JsonValue {
|
||||
pub fn do_list_transactions(&self, include_memo_hex: bool) -> JsonValue {
|
||||
let wallet = self.wallet.read().unwrap();
|
||||
|
||||
// Create a list of TransactionItems from wallet txns
|
||||
@ -883,22 +1036,39 @@ impl LightClient {
|
||||
let mut incoming_json = v.notes.iter()
|
||||
.filter( |nd| !nd.is_change )
|
||||
.enumerate()
|
||||
.map ( |(_i, nd)|
|
||||
object! {
|
||||
.map ( |(_i, nd)| {
|
||||
let mut o = object! {
|
||||
"address" => LightWallet::note_address(self.config.hrp_sapling_address(), nd),
|
||||
"value" => nd.note.value as i64,
|
||||
"memo" => LightWallet::memo_str(&nd.memo),
|
||||
};
|
||||
|
||||
if include_memo_hex {
|
||||
o.insert("memohex", match &nd.memo {
|
||||
Some(m) => hex::encode(m.as_bytes()),
|
||||
_ => "".to_string(),
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
return o;
|
||||
})
|
||||
.collect::<Vec<JsonValue>>();
|
||||
|
||||
let incoming_t_json = v.utxos.iter()
|
||||
.filter(|u| !change_addresses.contains(&u.address))
|
||||
.map( |uo|
|
||||
object! {
|
||||
.map( |uo| {
|
||||
let mut o = object! {
|
||||
"address" => uo.address.clone(),
|
||||
"value" => uo.value.clone() as i64,
|
||||
"memo" => None::<String>,
|
||||
})
|
||||
};
|
||||
|
||||
if include_memo_hex {
|
||||
o.insert("memohex", None::<String>).unwrap();
|
||||
}
|
||||
|
||||
return o;
|
||||
})
|
||||
.collect::<Vec<JsonValue>>();
|
||||
|
||||
for json in incoming_t_json {
|
||||
@ -909,22 +1079,39 @@ impl LightClient {
|
||||
let mut incoming_change_json = v.notes.iter()
|
||||
.filter( |nd| nd.is_change )
|
||||
.enumerate()
|
||||
.map ( |(_i, nd)|
|
||||
object! {
|
||||
.map ( |(_i, nd)| {
|
||||
let mut o = object! {
|
||||
"address" => LightWallet::note_address(self.config.hrp_sapling_address(), nd),
|
||||
"value" => nd.note.value as i64,
|
||||
"memo" => LightWallet::memo_str(&nd.memo),
|
||||
};
|
||||
|
||||
if include_memo_hex {
|
||||
o.insert("memohex", match &nd.memo {
|
||||
Some(m) => hex::encode(m.as_bytes()),
|
||||
_ => "".to_string(),
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
return o;
|
||||
})
|
||||
.collect::<Vec<JsonValue>>();
|
||||
|
||||
let incoming_t_change_json = v.utxos.iter()
|
||||
.filter(|u| change_addresses.contains(&u.address))
|
||||
.map( |uo|
|
||||
object! {
|
||||
.map( |uo| {
|
||||
let mut o = object! {
|
||||
"address" => uo.address.clone(),
|
||||
"value" => uo.value.clone() as i64,
|
||||
"memo" => None::<String>,
|
||||
})
|
||||
};
|
||||
|
||||
if include_memo_hex {
|
||||
o.insert("memohex", None::<String>).unwrap();
|
||||
}
|
||||
|
||||
return o;
|
||||
})
|
||||
.collect::<Vec<JsonValue>>();
|
||||
|
||||
for json in incoming_t_change_json {
|
||||
@ -933,21 +1120,35 @@ impl LightClient {
|
||||
|
||||
// Collect outgoing metadata
|
||||
let outgoing_json = v.outgoing_metadata.iter()
|
||||
.map(|om|
|
||||
object!{
|
||||
.map(|om| {
|
||||
let mut o = object!{
|
||||
"address" => om.address.clone(),
|
||||
"value" => om.value,
|
||||
"memo" => LightWallet::memo_str(&Some(om.memo.clone())),
|
||||
};
|
||||
|
||||
if include_memo_hex {
|
||||
o.insert("memohex", hex::encode(om.memo.as_bytes())).unwrap();
|
||||
}
|
||||
|
||||
return o;
|
||||
})
|
||||
.collect::<Vec<JsonValue>>();
|
||||
|
||||
// Collect outgoing metadata change
|
||||
let outgoing_change_json = v.outgoing_metadata_change.iter()
|
||||
.map(|om|
|
||||
object!{
|
||||
.map(|om| {
|
||||
let mut o = object!{
|
||||
"address" => om.address.clone(),
|
||||
"value" => om.value,
|
||||
"memo" => LightWallet::memo_str(&Some(om.memo.clone())),
|
||||
};
|
||||
|
||||
if include_memo_hex {
|
||||
o.insert("memohex", hex::encode(om.memo.as_bytes())).unwrap();
|
||||
}
|
||||
|
||||
return o;
|
||||
})
|
||||
.collect::<Vec<JsonValue>>();
|
||||
|
||||
@ -979,12 +1180,20 @@ impl LightClient {
|
||||
|
||||
// Collect outgoing metadata
|
||||
let outgoing_json = wtx.outgoing_metadata.iter()
|
||||
.map(|om|
|
||||
object!{
|
||||
.map(|om| {
|
||||
let mut o = object!{
|
||||
"address" => om.address.clone(),
|
||||
"value" => om.value,
|
||||
"memo" => LightWallet::memo_str(&Some(om.memo.clone())),
|
||||
}).collect::<Vec<JsonValue>>();
|
||||
};
|
||||
|
||||
if include_memo_hex {
|
||||
o.insert("memohex", hex::encode(om.memo.as_bytes())).unwrap();
|
||||
}
|
||||
|
||||
return o;
|
||||
})
|
||||
.collect::<Vec<JsonValue>>();
|
||||
|
||||
object! {
|
||||
"block_height" => wtx.block,
|
||||
@ -1017,7 +1226,7 @@ impl LightClient {
|
||||
let new_address = {
|
||||
let wallet = self.wallet.write().unwrap();
|
||||
|
||||
match addr_type {
|
||||
let addr = match addr_type {
|
||||
"z" => wallet.add_zaddr(),
|
||||
"t" => wallet.add_taddr(),
|
||||
_ => {
|
||||
@ -1025,7 +1234,15 @@ impl LightClient {
|
||||
error!("{}", e);
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
if addr.starts_with("Error") {
|
||||
let e = format!("Error creating new address: {}", addr);
|
||||
error!("{}", e);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
addr
|
||||
};
|
||||
|
||||
self.do_save()?;
|
||||
@ -1033,6 +1250,69 @@ impl LightClient {
|
||||
Ok(array![new_address])
|
||||
}
|
||||
|
||||
/// Convinence function to determine what type of key this is and import it
|
||||
pub fn do_import_key(&self, key: String, birthday: u64) -> Result<JsonValue, String> {
|
||||
if key.starts_with(self.config.hrp_sapling_private_key()) {
|
||||
self.do_import_sk(key, birthday)
|
||||
} else if key.starts_with(self.config.hrp_sapling_viewing_key()) {
|
||||
self.do_import_vk(key, birthday)
|
||||
} else {
|
||||
Err(format!("'{}' was not recognized as either a spending key or a viewing key because it didn't start with either '{}' or '{}'",
|
||||
key, self.config.hrp_sapling_private_key(), self.config.hrp_sapling_viewing_key()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Import a new private key
|
||||
pub fn do_import_sk(&self, sk: String, birthday: u64) -> Result<JsonValue, String> {
|
||||
if !self.wallet.read().unwrap().is_unlocked_for_spending() {
|
||||
error!("Wallet is locked");
|
||||
return Err("Wallet is locked".to_string());
|
||||
}
|
||||
|
||||
let new_address = {
|
||||
let mut wallet = self.wallet.write().unwrap();
|
||||
|
||||
let addr = wallet.add_imported_sk(sk, birthday);
|
||||
if addr.starts_with("Error") {
|
||||
let e = format!("Error creating new address{}", addr);
|
||||
error!("{}", e);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
addr
|
||||
};
|
||||
|
||||
self.do_save()?;
|
||||
|
||||
Ok(array![new_address])
|
||||
}
|
||||
|
||||
/// Import a new viewing key
|
||||
pub fn do_import_vk(&self, vk: String, birthday: u64) -> Result<JsonValue, String> {
|
||||
if !self.wallet.read().unwrap().is_unlocked_for_spending() {
|
||||
error!("Wallet is locked");
|
||||
return Err("Wallet is locked".to_string());
|
||||
}
|
||||
|
||||
let new_address = {
|
||||
let mut wallet = self.wallet.write().unwrap();
|
||||
|
||||
let addr = wallet.add_imported_vk(vk, birthday);
|
||||
if addr.starts_with("Error") {
|
||||
let e = format!("Error creating new address{}", addr);
|
||||
error!("{}", e);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
addr
|
||||
};
|
||||
|
||||
self.do_save()?;
|
||||
|
||||
Ok(array![new_address])
|
||||
}
|
||||
|
||||
|
||||
pub fn clear_state(&self) {
|
||||
// First, clear the state from the wallet
|
||||
self.wallet.read().unwrap().clear_blocks();
|
||||
@ -1377,20 +1657,20 @@ impl LightClient {
|
||||
|
||||
info!("Creating transaction");
|
||||
|
||||
let rawtx = self.wallet.write().unwrap().send_to_address(
|
||||
u32::from_str_radix(&self.config.consensus_branch_id, 16).unwrap(),
|
||||
&self.sapling_spend, &self.sapling_output,
|
||||
from, addrs, fee
|
||||
);
|
||||
let result = {
|
||||
let _lock = self.sync_lock.lock().unwrap();
|
||||
|
||||
self.wallet.write().unwrap().send_to_address(
|
||||
u32::from_str_radix(&self.config.consensus_branch_id, 16).unwrap(),
|
||||
&self.sapling_spend, &self.sapling_output,
|
||||
from, addrs, fee,
|
||||
|txbytes| broadcast_raw_tx(&self.get_server_uri(), txbytes)
|
||||
)
|
||||
};
|
||||
|
||||
info!("Transaction Complete");
|
||||
|
||||
|
||||
|
||||
match rawtx {
|
||||
Ok(txbytes) => broadcast_raw_tx(&self.get_server_uri(), txbytes),
|
||||
Err(e) => Err(format!("Error: No Tx to broadcast. Error was: {}", e))
|
||||
}
|
||||
result.map(|(txid, _)| txid)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1433,6 +1713,14 @@ pub mod tests {
|
||||
assert!(!lc.do_new_address("z").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_bad_import() {
|
||||
let lc = super::LightClient::unconnected(TEST_SEED.to_string(), None).unwrap();
|
||||
|
||||
assert!(lc.do_import_sk("bad_priv_key".to_string(), 0).is_err());
|
||||
assert!(lc.do_import_vk("bad_view_key".to_string(), 0).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_addresses() {
|
||||
let lc = super::LightClient::unconnected(TEST_SEED.to_string(), None).unwrap();
|
||||
|
@ -1,50 +1,66 @@
|
||||
pub fn get_closest_checkpoint(chain_name: &str, height: u64) -> Option<(u64, &'static str, &'static str)> {
|
||||
match chain_name {
|
||||
"test" => get_test_checkpoint(height),
|
||||
"main" => get_main_checkpoint(height),
|
||||
_ => None
|
||||
use serde_derive::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct CoinList {
|
||||
pub coin_list: Vec<CheckPoints>
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct CheckPoints {
|
||||
pub coin_name: String,
|
||||
pub coin_type: u32,
|
||||
pub chain_name: String,
|
||||
pub check_points : Vec<CheckPoint>
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct CheckPoint {
|
||||
pub height: u64,
|
||||
pub hash: String,
|
||||
pub sapling_tree: String
|
||||
}
|
||||
|
||||
|
||||
pub fn get_closest_checkpoint(chain_name: &str, coin_type: u32, height: u64) -> Option<(u64, String, String)> {
|
||||
|
||||
let mut checkpoints: Vec<(u64, String, String)> = Vec::new();
|
||||
|
||||
let cps = match reqwest::blocking::get("https://raw.githubusercontent.com/zerocurrencycoin/zerowallet-light-cli/merge_upstream/coin-checkpoint.json") {
|
||||
Ok(s) => {
|
||||
match s.json::<CoinList>() {
|
||||
Ok(j) => Some(j),
|
||||
Err(_) => None
|
||||
}
|
||||
},
|
||||
Err(_) => None
|
||||
};
|
||||
|
||||
match cps {
|
||||
Some(s) => {
|
||||
for coin in 0..s.coin_list.len() {
|
||||
if s.coin_list[coin].coin_type == coin_type && s.coin_list[coin].chain_name == chain_name {
|
||||
for points in 0..s.coin_list[coin].check_points.len() {
|
||||
checkpoints.push((s.coin_list[coin].check_points[points].height
|
||||
, s.coin_list[coin].check_points[points].hash.clone()
|
||||
, s.coin_list[coin].check_points[points].sapling_tree.clone()))
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_test_checkpoint(height: u64) -> Option<(u64, &'static str, &'static str)> {
|
||||
let checkpoints: Vec<(u64, &str, &str)> = vec![
|
||||
// (600000, "0107385846c7451480912c294b6ce1ee1feba6c2619079fd9104f6e71e4d8fe7",
|
||||
// "01690698411e3f8badea7da885e556d7aba365a797e9b20b44ac0946dced14b23c001001ab2a18a5a86aa5d77e43b69071b21770b6fe6b3c26304dcaf7f96c0bb3fed74d000186482712fa0f2e5aa2f2700c4ed49ef360820f323d34e2b447b78df5ec4dfa0401a332e89a21afb073cb1db7d6f07396b56a95e97454b9bca5a63d0ebc575d3a33000000000001c9d3564eff54ebc328eab2e4f1150c3637f4f47516f879a0cfebdf49fe7b1d5201c104705fac60a85596010e41260d07f3a64f38f37a112eaef41cd9d736edc5270145e3d4899fcd7f0f1236ae31eafb3f4b65ad6b11a17eae1729cec09bd3afa01a000000011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
|
||||
// ),
|
||||
// (650000, "003f7e09a357a75c3742af1b7e1189a9038a360cebb9d55e158af94a1c5aa682",
|
||||
// "010113f257f93a40e25cfc8161022f21c06fa2bc7fb03ee9f9399b3b30c636715301ef5b99706e40a19596d758bf7f4fd1b83c3054557bf7fab4801985642c317d41100001b2ad599fd7062af72bea99438dc5d8c3aa66ab52ed7dee3e066c4e762bd4e42b0001599dd114ec6c4c5774929a342d530bf109b131b48db2d20855afa9d37c92d6390000019159393c84b1bf439d142ed2c54ee8d5f7599a8b8f95e4035a75c30b0ec0fa4c0128e3a018bd08b2a98ed8b6995826f5857a9dc2777ce6af86db1ae68b01c3c53d0000000001e3ec5d790cc9acc2586fc6e9ce5aae5f5aba32d33e386165c248c4a03ec8ed670000011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39"
|
||||
// )
|
||||
];
|
||||
|
||||
find_checkpoint(height, checkpoints)
|
||||
}
|
||||
|
||||
|
||||
fn get_main_checkpoint(height: u64) -> Option<(u64, &'static str, &'static str)> {
|
||||
let checkpoints: Vec<(u64, &str, &str)> = vec![
|
||||
(500000, "000000c642fda400a464c50dcf310e65efa2627799b9b0c378524205ba2307b7","014819ca94dac152e7caad1c4fe0fb339343d30084775a10d792f50e56d85e893e018a6e949342beea95fce4d646a09b5ee792531779d866d1e082ff80f62c6028030b01f6fb9b8cef82c92b949324abfa1c8afb82c73e4b48ee59b5284f396de74f632001ad515dd43a07ad98474e165b44c6281060904bf116d70ecb96acffa3c2551713000188153113124a17b84f77ee3bee893be274f2686a454f66208f3cce49877572160000000001832e857dfe5e64d324e7f3f9b2017860dade173bba1cdd8709dad497d6d8ed4c0001ffbcedcff498ad3d7bef07a93a8d601cb5b6de9b87f62e7629c6583def950d57"),
|
||||
(550000, "000003cb1f3c128819bdf84632fd31058014542af64eea8959d5139fc26c21cd","011e084d9028f86423e20c3957b741c21101463fd1318a46f0217800da52b6982601a2689b9659e3b3b2e92e13ebdb2e5c745625f107153e0e8ee17430819a9181240d000001582f5de14bbe65e3b4365deeb42a074b9e89733150f9e6c702c5e73b16eb9e27015355ec98f61a65805b304b5f4f01b6a6b550b0ff90654c3bb14d37ea66d2d25b016e96a1699966f4be5958d39ccf4a80fd9c8bfc791a26b04e64b92b68e74c5d6f00000001bef355e062338cc8b1c728aba1a09a892d15f0cec6e66b1dce3bf683f46fe3710194f4d0df7683fccc0fe400aab264eef1be8273e952e40f94eeff15dd71778f0b00000161d449385412557da75d1a6fd3adb2755c90d379b474ffda2da7996d5880bc20"),
|
||||
(600000, "000000a0e6efed6a536c2c5063c666102fb2f9ddb4a226b34a0894c723519e48","019c80fa055af06b00b2e62234dc88d0f20eb5bdd029f27db5015ac5421c0d971a01843776c7c19531a4b70fd74a2f5300125d93697fb1df874f01860967043ae90d0e000170203c0ea694d0ec23c2b330e38d8a72be85f7cda2433e228938a08f0d357d5301a2f0efece5d0ef22101f1a88856d8263b813c47843af6fdeed8745ebf479f202012aaa5eb3f4659c96c07961311425f6d2469f8f161abdf1cd6050cc9cc1650d3c016a4d5df54decf298a6b83be00f130cec069ea3ef38c8d97dc8c5867dfe5a862501b801046295a11ecd1497a55348057912ce9cce5114466181feb55036a38ee342014cc662fdebea6b1557428d407f28fafdae278480525959863b4a096981178e0b017443ea389a059650f310567225e4626b97c4f7eb4de7f43acd9c5d69392d086f01d108cf831f1721b23323c94d1112f47da6b6c5162d09e3c4afe8d8618c604565012071da2937f4c0d6bdf355976e110708d23ef8f103704cd84f748dba12d6174000015ea99a4cbe8cd1e9b09149545388d53ac264c0e7206bc3251d62aab090aea72a0001c9ee5f761f861d9babfe591dc31ef10cde7f4c42f7c26fdf07ea409f1155262a"),
|
||||
(650000, "00000437dd0e913b3ee3f9f7d785ca02403fe9621bf65e593889f1c74bd67a14","010642a2aac1ea411fac9498ee7ecf27917c8c366ecb61b55ab3d1c4e5608c472e000f0001d1da78af6ff64e1fd6c432a625e9f4abe513911005a9cf06908e0a935632360001834f3ad29fa60183c223520ccbd0f5f555823017f8d4e7f9ab5d79c7c6b6e731016fad879cc5c43e6a05488449772681987372a17c9188d2309ac0d1ea8066b60700013b7dd01550b9f1ea6ae183cee3f9e4d328df9540670b04e2565a1504a16fe247000167ea7809d5a824aa6b6d3a0f5b645d342cc56ab4a439c0a9f1db698864def14d00000173ffa7e42100bee60ec93ca40b73de89f095ec1e9c864d59f8f71fd414afd24001a058f8f22a15871367aa0a46838696747d3ab6cdce77d07d309343955beede68000001034691170b96f4c7750ce0c1b59bf81193290025e7bf2af91d9f372a9a36d10f"),
|
||||
(700000, "000001bee8c5bc29fb8d6c3642662ecb479ec85df648e851edaaa0e090b2c797","01ceff4af4f1b52be90b6c7f47b5163a525406ed5cc9ee162a4553ac78f6cadf5b013f22df3d99d52c4a02b5fdde2963eae56d2fd5eb1b2e361ba3e226edf0277005100127ba111aaaccce8f68b1b6941792030921aec1feac1e00aedcc2b147068499410001a81f9b610ea96322730db5a4ef0564d20b5f48bd1a4c1c6c9686904ab542e24500018d28843019ca5028fd6b2b6a0aa94a1df668cc8f75d6feb9aac84d750e39691000015e186c88c3b0bf1eefa57f0cb0c293524366c8d96dd7a04f987aa196234e2c4900017c27144ffb06b60cda7d646861fca487d6a956633e2792c9ab34d7e59125d46c0000011d3e9aeec6ff15da7578faf9a23d8ea894e7aa1a25b80108faecb7b47a66bc0e0000000166d62135626ff20d77abeef13ec68d89e313de4195995cb0d0516bfd5af17b33"),
|
||||
(750000, "00000154c6a43da617c853c6df9cb1435aedb80e8bbc76d47357c341c264840f","0104e11cbea21d2a11531cb06a16de6a09b85be2a32b68f965940a5418a404677001e3338aca2e46f0757646b59d514ed0d329ede54f0a5017c91b1af4a934c55c3010018d6f7a24db3e70842821fee80ffb0a407b0cf81978ac4c1b567c72545feccc040001dc1e23340b5291e85fceef83b62fcacb8c20043abdb802fd11e09d18725f30670187eaae8febe7b86f61554592e30d15695cd7d300a66b3764f506465a7a17c825012fe6cb8e15ebef0eed22f438771022b80c02af9fb086eb9fbc34ee5d535cd0400000011f8d2789cdf1277c4504f58779685e4cbe0cba2d168d4409126f04f76f7aec610001b9a0a6cf94e6d71bf4a266ea84ef24c4a04361f64c67413aed14591853f2d05300000150a69065c06bad655cdcf191f0f65d8cc1ca8bb1f35426d46c0ccad6b7754c590174c516df639398fc949d34a1b71412454f42a2bc92d6c918df4f7d5582d8ac68000166d62135626ff20d77abeef13ec68d89e313de4195995cb0d0516bfd5af17b33"),
|
||||
(800000, "000001a76b58c2bd8748849ee402d7bb010c862aa20cb82bacdf1008c5a2e2a6","015eb92aa340b783e007fbc59440f58a692983b4df45fcb4bed15f4b0eafeb3438012ec53175dd0c73cb07731c9be676a94a842dd9594b99f23b80a70b56810e1f53100000000001b9162a75faeddb2c05667a0d842586d5cd6a72aae8fa57b5ca70ebcbfb1c532f01ebd3d41b0abe75ef18b40ebc87aa58ed5b9a334fd298c809b9674f2f8948d767017293df4117ab33229928942df3229b1995e9be887e0ba7d023d5e62a49b8d75e0149fdc53dd29217661abcda93b6b66b4eb240778cf5dd361ec5e8314b9c1c4e6c00011202a0c6e857e95ec95473baaf9b2225506c2fdc59d687d6053bf4bc603f347000000101f34ab1470100f029cbc911a899c038c4e1d3c512ab1237bf2e5acb008f696a00012970748afdd3fe833b33c062a30566cccefd8584db94db7cbdfc929c3a06691d0166d62135626ff20d77abeef13ec68d89e313de4195995cb0d0516bfd5af17b33"),
|
||||
(850000, "000001eb727c5b8598c20ed93daf60b3e77ed6a91a2b7a18891468dcdbfe7796","01d7e34604e3bfe16b9d9db00a035b9b9c5908cb2d8617a9cd0b2f0aa985c3784b001000000000000161632e3bbc414fc9da858ec9e31f7045d5f14c733e8e0d6f65c372427dcc4d340001910363995296c76f488586c34fb07f81f13d0131c0e84b40e6d8b6c0647d5505016aa7cae4f5a3ff0acec91c22d76a95eb4d23e31ac7c9c0282739097a07f0150a0001c91f78eedc950040276b03b7bfedbcbbc63f56b17d133df9e05184d6adebb52b01ba027141de6eec6be10307b245741660a32b237b3c3bd545cc429425a817792801cfe97171b0120d477eea190aa3a7e3f8aadf3053cc00298bf845e411b5f8b832012908fa845f893de23b144e6f63ad8dfa4db3a57fe3d9630183154d4651d95909012970748afdd3fe833b33c062a30566cccefd8584db94db7cbdfc929c3a06691d0166d62135626ff20d77abeef13ec68d89e313de4195995cb0d0516bfd5af17b33"),
|
||||
(900000, "000007b665259adb7cda27acc031c275419b9bcdd8e2aec276adf13244d2216d","014c32057f5f4a9aa19024bd5da7b90e49ec97592c50b634f36edc2dd1b3c0ab1501554efd44243576b55a50c0dc6015834defa3746296507ab81847a418439ec70511000000019a0fed69087c96dccc24cebfcf604ae006ad71ac6ee2211c1a4f1acd0b06936a0124b2a98dd05896655cce70c149b88da85e367f5ca463c11226bb1f4353d5af3301a674c227b6f9845da40a9e613d8b83a77de4e7bdf50917af41fbb1260e87034601ce1dbe60dcbc2d0ff7414c081b75f7e937327f4cf986d4c054ec66ebbad8a95a000169bd72a5264f2d3a3b0d105f5991524455a46de9f4ebb22f17d776aee55f5a4f015c5a0dd38c70e45e84a3b0af98531d87114f69387d557f36270aa98684ea1a640119bbc12a1f05fb36a5d790665511dc2e79c2830606269629030b3d6ba0e07e02016165ac2b7b2427242a8001b7914066693def577f7ff9d9edafad352079af334f0001406b73dc4c25c134d606f35c9bac4143889427b9b9a3581df5c3d18d3dc27b110000018d94e637ad75835c34c7c278d4659387cb14e10e3bce05d0d3d046b22a259908"),
|
||||
|
||||
];
|
||||
|
||||
find_checkpoint(height, checkpoints)
|
||||
}
|
||||
|
||||
fn find_checkpoint(height: u64, chkpts: Vec<(u64, &'static str, &'static str)>) -> Option<(u64, &'static str, &'static str)> {
|
||||
fn find_checkpoint(height: u64, chkpts: Vec<(u64, String, String)>) -> Option<(u64, String, String)> {
|
||||
// Find the closest checkpoint
|
||||
let mut heights = chkpts.iter().map(|(h, _, _)| *h as u64).collect::<Vec<_>>();
|
||||
heights.sort();
|
||||
|
||||
match get_first_lower_than(height, heights) {
|
||||
Some(closest_height) => {
|
||||
chkpts.iter().find(|(h, _, _)| *h == closest_height).map(|t| *t)
|
||||
chkpts.iter().find(|(h, _, _)| *h == closest_height).map(|t| (t.0, t.1.clone(), t.2.clone()))
|
||||
},
|
||||
None => None
|
||||
}
|
||||
|
@ -23,8 +23,10 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use pairing::bls12_381::{Bls12};
|
||||
use sha2::{Sha256, Digest};
|
||||
|
||||
use sodiumoxide::crypto::secretbox;
|
||||
|
||||
use zcash_client_backend::{
|
||||
encoding::{encode_payment_address, encode_extended_spending_key},
|
||||
encoding::{encode_payment_address, encode_extended_spending_key, encode_extended_full_viewing_key, decode_extended_spending_key, decode_extended_full_viewing_key},
|
||||
proto::compact_formats::{CompactBlock, CompactOutput},
|
||||
wallet::{WalletShieldedOutput, WalletShieldedSpend}
|
||||
};
|
||||
@ -55,13 +57,15 @@ mod extended_key;
|
||||
mod utils;
|
||||
mod address;
|
||||
mod prover;
|
||||
pub mod bugs;
|
||||
mod walletzkey;
|
||||
|
||||
use data::{BlockData, WalletTx, Utxo, SaplingNoteData, SpendableNote, OutgoingTxMetadata};
|
||||
use extended_key::{KeyIndex, ExtendedPrivKey};
|
||||
use walletzkey::{WalletZKey, WalletZKeyType};
|
||||
|
||||
pub const MAX_REORG: usize = 100;
|
||||
pub const GAP_RULE_UNUSED_ADDRESSES: usize = 5;
|
||||
|
||||
pub const GAP_RULE_UNUSED_ADDRESSES: usize = 0;
|
||||
|
||||
fn now() -> f64 {
|
||||
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs() as f64
|
||||
@ -112,12 +116,9 @@ pub struct LightWallet {
|
||||
|
||||
seed: [u8; 32], // Seed phrase for this wallet. If wallet is locked, this is 0
|
||||
|
||||
// List of keys, actually in this wallet. If the wallet is locked, the `extsks` will be
|
||||
// encrypted (but the fvks are not encrpyted)
|
||||
extsks: Arc<RwLock<Vec<ExtendedSpendingKey>>>,
|
||||
extfvks: Arc<RwLock<Vec<ExtendedFullViewingKey>>>,
|
||||
|
||||
pub zaddress: Arc<RwLock<Vec<PaymentAddress<Bls12>>>>,
|
||||
// List of keys, actually in this wallet. This is a combination of HD keys derived from the seed,
|
||||
// viewing keys and imported spending keys.
|
||||
zkeys: Arc<RwLock<Vec<WalletZKey>>>,
|
||||
|
||||
// Transparent keys. If the wallet is locked, then the secret keys will be encrypted,
|
||||
// but the addresses will be present.
|
||||
@ -143,7 +144,7 @@ pub struct LightWallet {
|
||||
|
||||
impl LightWallet {
|
||||
pub fn serialized_version() -> u64 {
|
||||
return 6;
|
||||
return 8;
|
||||
}
|
||||
|
||||
fn get_taddr_from_bip39seed(config: &LightClientConfig, bip39_seed: &[u8], pos: u32) -> SecretKey {
|
||||
@ -214,13 +215,14 @@ impl LightWallet {
|
||||
let bip39_seed = bip39::Seed::new(&Mnemonic::from_entropy(&seed_bytes, Language::English).unwrap(), "");
|
||||
|
||||
// Derive only the first sk and address
|
||||
let tpk = LightWallet::get_taddr_from_bip39seed(&config, &bip39_seed.as_bytes(), 0);
|
||||
let taddr = LightWallet::address_from_prefix_sk(&config.base58_pubkey_address(), &tpk);
|
||||
// let tpk = LightWallet::get_taddr_from_bip39seed(&config, &bip39_seed.as_bytes(), 0);
|
||||
// let taddr = LightWallet::address_from_prefix_sk(&config.base58_pubkey_address(), &tpk);
|
||||
|
||||
// TODO: We need to monitor addresses, and always keep 1 "free" address, so
|
||||
// users can import a seed phrase and automatically get all used addresses
|
||||
let (extsk, extfvk, address)
|
||||
= LightWallet::get_zaddr_from_bip39seed(&config, &bip39_seed.as_bytes(), 0);
|
||||
let hdkey_num = 0;
|
||||
let (extsk, _, _)
|
||||
= LightWallet::get_zaddr_from_bip39seed(&config, &bip39_seed.as_bytes(), hdkey_num);
|
||||
|
||||
let lw = LightWallet {
|
||||
encrypted: false,
|
||||
@ -228,11 +230,9 @@ impl LightWallet {
|
||||
enc_seed: [0u8; 48],
|
||||
nonce: vec![],
|
||||
seed: seed_bytes,
|
||||
extsks: Arc::new(RwLock::new(vec![extsk])),
|
||||
extfvks: Arc::new(RwLock::new(vec![extfvk])),
|
||||
zaddress: Arc::new(RwLock::new(vec![address])),
|
||||
tkeys: Arc::new(RwLock::new(vec![tpk])),
|
||||
taddresses: Arc::new(RwLock::new(vec![taddr])),
|
||||
zkeys: Arc::new(RwLock::new(vec![WalletZKey::new_hdkey(hdkey_num, extsk)])),
|
||||
tkeys: Arc::new(RwLock::new(vec![])),
|
||||
taddresses: Arc::new(RwLock::new(vec![])),
|
||||
blocks: Arc::new(RwLock::new(vec![])),
|
||||
txs: Arc::new(RwLock::new(HashMap::new())),
|
||||
mempool_txs: Arc::new(RwLock::new(HashMap::new())),
|
||||
@ -242,12 +242,12 @@ impl LightWallet {
|
||||
};
|
||||
|
||||
// If restoring from seed, make sure we are creating 5 addresses for users
|
||||
if seed_phrase.is_some() {
|
||||
for _i in 0..5 {
|
||||
lw.add_taddr();
|
||||
lw.add_zaddr();
|
||||
}
|
||||
}
|
||||
// if seed_phrase.is_some() {
|
||||
// for _i in 0..1 {
|
||||
// lw.add_taddr();
|
||||
// lw.add_zaddr();
|
||||
// }
|
||||
// }
|
||||
|
||||
Ok(lw)
|
||||
}
|
||||
@ -294,22 +294,59 @@ impl LightWallet {
|
||||
let mut seed_bytes = [0u8; 32];
|
||||
reader.read_exact(&mut seed_bytes)?;
|
||||
|
||||
// Read the spending keys
|
||||
let extsks = Vector::read(&mut reader, |r| ExtendedSpendingKey::read(r))?;
|
||||
let zkeys = if version <= 6 {
|
||||
// Up until version 6, the wallet keys were written out individually
|
||||
// Read the spending keys
|
||||
let extsks = Vector::read(&mut reader, |r| ExtendedSpendingKey::read(r))?;
|
||||
|
||||
let extfvks = if version >= 4 {
|
||||
// Read the viewing keys
|
||||
Vector::read(&mut reader, |r| ExtendedFullViewingKey::read(r))?
|
||||
} else {
|
||||
// Calculate the viewing keys
|
||||
extsks.iter().map(|sk| ExtendedFullViewingKey::from(sk))
|
||||
.collect::<Vec<ExtendedFullViewingKey>>()
|
||||
let extfvks = if version >= 4 {
|
||||
// Read the viewing keys
|
||||
Vector::read(&mut reader, |r| ExtendedFullViewingKey::read(r))?
|
||||
} else {
|
||||
// Calculate the viewing keys
|
||||
extsks.iter().map(|sk| ExtendedFullViewingKey::from(sk))
|
||||
.collect::<Vec<ExtendedFullViewingKey>>()
|
||||
};
|
||||
|
||||
// Calculate the addresses
|
||||
let addresses = extfvks.iter().map( |fvk| fvk.default_address().unwrap().1 )
|
||||
.collect::<Vec<PaymentAddress<Bls12>>>();
|
||||
|
||||
// If extsks is of len 0, then this wallet is locked
|
||||
let zkeys_result = if extsks.len() == 0 {
|
||||
// Wallet is locked, so read only the viewing keys.
|
||||
extfvks.iter().zip(addresses.iter()).enumerate().map(|(i, (extfvk, payment_address))| {
|
||||
let zk = WalletZKey::new_locked_hdkey(i as u32, extfvk.clone());
|
||||
if zk.zaddress != *payment_address {
|
||||
Err(io::Error::new(ErrorKind::InvalidData, "Payment address didn't match"))
|
||||
} else {
|
||||
Ok(zk)
|
||||
}
|
||||
}).collect::<Vec<io::Result<WalletZKey>>>()
|
||||
} else {
|
||||
// Wallet is unlocked, read the spending keys as well
|
||||
extsks.into_iter().zip(extfvks.into_iter().zip(addresses.iter())).enumerate()
|
||||
.map(|(i, (extsk, (extfvk, payment_address)))| {
|
||||
let zk = WalletZKey::new_hdkey(i as u32, extsk);
|
||||
if zk.zaddress != *payment_address {
|
||||
return Err(io::Error::new(ErrorKind::InvalidData, "Payment address didn't match"));
|
||||
}
|
||||
|
||||
if zk.extfvk != extfvk {
|
||||
return Err(io::Error::new(ErrorKind::InvalidData, "Full View key didn't match"));
|
||||
}
|
||||
|
||||
Ok(zk)
|
||||
}).collect::<Vec<io::Result<WalletZKey>>>()
|
||||
};
|
||||
|
||||
// Convert vector of results into result of vector, returning an error if any one of the keys failed the checks above
|
||||
zkeys_result.into_iter().collect::<io::Result<_>>()?
|
||||
} else {
|
||||
// After version 6, we read the WalletZKey structs directly
|
||||
Vector::read(&mut reader, |r| WalletZKey::read(r))?
|
||||
};
|
||||
|
||||
// Calculate the addresses
|
||||
let addresses = extfvks.iter().map( |fvk| fvk.default_address().unwrap().1 )
|
||||
.collect::<Vec<PaymentAddress<Bls12>>>();
|
||||
|
||||
let tkeys = Vector::read(&mut reader, |r| {
|
||||
let mut tpk_bytes = [0u8; 32];
|
||||
r.read_exact(&mut tpk_bytes)?;
|
||||
@ -343,15 +380,13 @@ impl LightWallet {
|
||||
|
||||
let birthday = reader.read_u64::<LittleEndian>()?;
|
||||
|
||||
Ok(LightWallet{
|
||||
let lw = LightWallet{
|
||||
encrypted: encrypted,
|
||||
unlocked: !encrypted, // When reading from disk, if wallet is encrypted, it starts off locked.
|
||||
enc_seed: enc_seed,
|
||||
nonce: nonce,
|
||||
seed: seed_bytes,
|
||||
extsks: Arc::new(RwLock::new(extsks)),
|
||||
extfvks: Arc::new(RwLock::new(extfvks)),
|
||||
zaddress: Arc::new(RwLock::new(addresses)),
|
||||
zkeys: Arc::new(RwLock::new(zkeys)),
|
||||
tkeys: Arc::new(RwLock::new(tkeys)),
|
||||
taddresses: Arc::new(RwLock::new(taddresses)),
|
||||
blocks: Arc::new(RwLock::new(blocks)),
|
||||
@ -360,7 +395,14 @@ impl LightWallet {
|
||||
config: config.clone(),
|
||||
birthday,
|
||||
total_scan_duration: Arc::new(RwLock::new(vec![Duration::new(0, 0)])),
|
||||
})
|
||||
};
|
||||
|
||||
// Do a one-time fix of the spent_at_height for older wallets
|
||||
if version <= 7 {
|
||||
lw.fix_spent_at_height();
|
||||
}
|
||||
|
||||
Ok(lw)
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
@ -387,14 +429,9 @@ impl LightWallet {
|
||||
// Flush after writing the seed, so in case of a disaster, we can still recover the seed.
|
||||
writer.flush()?;
|
||||
|
||||
// Write all the spending keys
|
||||
Vector::write(&mut writer, &self.extsks.read().unwrap(),
|
||||
|w, sk| sk.write(w)
|
||||
)?;
|
||||
|
||||
// Write the FVKs
|
||||
Vector::write(&mut writer, &self.extfvks.read().unwrap(),
|
||||
|w, fvk| fvk.write(w)
|
||||
// Write all the wallet's keys
|
||||
Vector::write(&mut writer, &self.zkeys.read().unwrap(),
|
||||
|w, zk| zk.write(w)
|
||||
)?;
|
||||
|
||||
// Write the transparent private keys
|
||||
@ -458,14 +495,20 @@ impl LightWallet {
|
||||
.unwrap_or(&cmp::max(self.birthday, self.config.sapling_activation_height))
|
||||
}
|
||||
|
||||
// Get all z-address private keys. Returns a Vector of (address, privatekey)
|
||||
pub fn get_z_private_keys(&self) -> Vec<(String, String)> {
|
||||
self.extsks.read().unwrap().iter().map(|sk| {
|
||||
(encode_payment_address(self.config.hrp_sapling_address(),
|
||||
&ExtendedFullViewingKey::from(sk).default_address().unwrap().1),
|
||||
encode_extended_spending_key(self.config.hrp_sapling_private_key(), &sk)
|
||||
)
|
||||
}).collect::<Vec<(String, String)>>()
|
||||
// Get all z-address private keys. Returns a Vector of (address, privatekey, viewkey)
|
||||
pub fn get_z_private_keys(&self) -> Vec<(String, String, String)> {
|
||||
let keys = self.zkeys.read().unwrap().iter().map(|k| {
|
||||
let pkey = match k.extsk.clone().map(|extsk| encode_extended_spending_key(self.config.hrp_sapling_private_key(), &extsk)) {
|
||||
Some(pk) => pk,
|
||||
None => "".to_string()
|
||||
};
|
||||
|
||||
let vkey = encode_extended_full_viewing_key(self.config.hrp_sapling_viewing_key(), &k.extfvk);
|
||||
|
||||
(encode_payment_address(self.config.hrp_sapling_address(),&k.zaddress), pkey, vkey)
|
||||
}).collect::<Vec<(String, String, String)>>();
|
||||
|
||||
keys
|
||||
}
|
||||
|
||||
/// Get all t-address private keys. Returns a Vector of (address, secretkey)
|
||||
@ -481,29 +524,34 @@ impl LightWallet {
|
||||
/// NOTE: This does NOT rescan
|
||||
pub fn add_zaddr(&self) -> String {
|
||||
if !self.unlocked {
|
||||
return "".to_string();
|
||||
return "Error: Can't add key while wallet is locked".to_string();
|
||||
}
|
||||
|
||||
let pos = self.extsks.read().unwrap().len() as u32;
|
||||
// Find the highest pos we have
|
||||
let pos = self.zkeys.read().unwrap().iter()
|
||||
.filter(|zk| zk.hdkey_num.is_some())
|
||||
.max_by(|zk1, zk2| zk1.hdkey_num.unwrap().cmp(&zk2.hdkey_num.unwrap()))
|
||||
.map_or(0, |zk| zk.hdkey_num.unwrap() + 1);
|
||||
|
||||
|
||||
let bip39_seed = bip39::Seed::new(&Mnemonic::from_entropy(&self.seed, Language::English).unwrap(), "");
|
||||
|
||||
let (extsk, extfvk, address) =
|
||||
let (extsk, _, _) =
|
||||
LightWallet::get_zaddr_from_bip39seed(&self.config, &bip39_seed.as_bytes(), pos);
|
||||
|
||||
let zaddr = encode_payment_address(self.config.hrp_sapling_address(), &address);
|
||||
self.extsks.write().unwrap().push(extsk);
|
||||
self.extfvks.write().unwrap().push(extfvk);
|
||||
self.zaddress.write().unwrap().push(address);
|
||||
// let zaddr = encode_payment_address(self.config.hrp_sapling_address(), &address);
|
||||
let newkey = WalletZKey::new_hdkey(pos, extsk);
|
||||
self.zkeys.write().unwrap().push(newkey.clone());
|
||||
|
||||
zaddr
|
||||
encode_payment_address(self.config.hrp_sapling_address(), &newkey.zaddress)
|
||||
}
|
||||
|
||||
/// Add a new t address to the wallet. This will derive a new address from the seed
|
||||
/// at the next position.
|
||||
/// NOTE: This is not rescan the wallet
|
||||
/// NOTE: This will not rescan the wallet
|
||||
pub fn add_taddr(&self) -> String {
|
||||
if !self.unlocked {
|
||||
return "".to_string();
|
||||
return "Error: Can't add key while wallet is locked".to_string();
|
||||
}
|
||||
|
||||
let pos = self.tkeys.read().unwrap().len() as u32;
|
||||
@ -518,6 +566,81 @@ impl LightWallet {
|
||||
address
|
||||
}
|
||||
|
||||
// Add a new imported spending key to the wallet
|
||||
/// NOTE: This will not rescan the wallet
|
||||
pub fn add_imported_sk(&mut self, sk: String, birthday: u64) -> String {
|
||||
if self.encrypted {
|
||||
return "Error: Can't import spending key while wallet is encrypted".to_string();
|
||||
}
|
||||
|
||||
// First, try to interpret the key
|
||||
let extsk = match decode_extended_spending_key(self.config.hrp_sapling_private_key(), &sk) {
|
||||
Ok(Some(k)) => k,
|
||||
Ok(None) => return format!("Error: Couldn't decode spending key"),
|
||||
Err(e) => return format!("Error importing spending key: {}", e)
|
||||
};
|
||||
|
||||
// Make sure the key doesn't already exist
|
||||
if self.zkeys.read().unwrap().iter().find(|&wk| wk.extsk.is_some() && wk.extsk.as_ref().unwrap() == &extsk.clone()).is_some() {
|
||||
return "Error: Key already exists".to_string();
|
||||
}
|
||||
|
||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
||||
let zaddress = {
|
||||
let mut zkeys = self.zkeys.write().unwrap();
|
||||
let maybe_existing_zkey = zkeys.iter_mut().find(|wk| wk.extfvk == extfvk);
|
||||
|
||||
// If the viewing key exists, and is now being upgraded to the spending key, replace it in-place
|
||||
if maybe_existing_zkey.is_some() {
|
||||
let mut existing_zkey = maybe_existing_zkey.unwrap();
|
||||
existing_zkey.extsk = Some(extsk);
|
||||
existing_zkey.keytype = WalletZKeyType::ImportedSpendingKey;
|
||||
existing_zkey.zaddress.clone()
|
||||
} else {
|
||||
let newkey = WalletZKey::new_imported_sk(extsk);
|
||||
zkeys.push(newkey.clone());
|
||||
newkey.zaddress
|
||||
}
|
||||
};
|
||||
|
||||
// Adjust wallet birthday
|
||||
if birthday < self.birthday {
|
||||
self.birthday = if birthday < self.config.sapling_activation_height {self.config.sapling_activation_height} else {birthday};
|
||||
}
|
||||
|
||||
encode_payment_address(self.config.hrp_sapling_address(), &zaddress)
|
||||
}
|
||||
|
||||
// Add a new imported viewing key to the wallet
|
||||
/// NOTE: This will not rescan the wallet
|
||||
pub fn add_imported_vk(&mut self, vk: String, birthday: u64) -> String {
|
||||
if !self.unlocked {
|
||||
return "Error: Can't add key while wallet is locked".to_string();
|
||||
}
|
||||
|
||||
// First, try to interpret the key
|
||||
let extfvk = match decode_extended_full_viewing_key(self.config.hrp_sapling_viewing_key(), &vk) {
|
||||
Ok(Some(k)) => k,
|
||||
Ok(None) => return format!("Error: Couldn't decode viewing key"),
|
||||
Err(e) => return format!("Error importing viewing key: {}", e)
|
||||
};
|
||||
|
||||
// Make sure the key doesn't already exist
|
||||
if self.zkeys.read().unwrap().iter().find(|wk| wk.extfvk == extfvk.clone()).is_some() {
|
||||
return "Error: Key already exists".to_string();
|
||||
}
|
||||
|
||||
let newkey = WalletZKey::new_imported_viewkey(extfvk);
|
||||
self.zkeys.write().unwrap().push(newkey.clone());
|
||||
|
||||
// Adjust wallet birthday
|
||||
if birthday < self.birthday {
|
||||
self.birthday = if birthday < self.config.sapling_activation_height {self.config.sapling_activation_height} else {birthday};
|
||||
}
|
||||
|
||||
encode_payment_address(self.config.hrp_sapling_address(), &newkey.zaddress)
|
||||
}
|
||||
|
||||
/// Clears all the downloaded blocks and resets the state back to the initial block.
|
||||
/// After this, the wallet's initial state will need to be set
|
||||
/// and the wallet will need to be rescanned
|
||||
@ -611,6 +734,14 @@ impl LightWallet {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the height of the anchor block
|
||||
pub fn get_anchor_height(&self) -> u32 {
|
||||
match self.get_target_height_and_anchor_offset() {
|
||||
Some((height, anchor_offset)) => height - anchor_offset as u32 - 1,
|
||||
None => return 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn memo_str(memo: &Option<Memo>) -> Option<String> {
|
||||
match memo {
|
||||
Some(memo) => {
|
||||
@ -623,6 +754,12 @@ impl LightWallet {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_all_zaddresses(&self) -> Vec<String> {
|
||||
self.zkeys.read().unwrap().iter().map( |zk| {
|
||||
encode_payment_address(self.config.hrp_sapling_address(), &zk.zaddress)
|
||||
}).collect()
|
||||
}
|
||||
|
||||
pub fn address_from_prefix_sk(prefix: &[u8; 2], sk: &secp256k1::SecretKey) -> String {
|
||||
let secp = secp256k1::Secp256k1::new();
|
||||
let pk = secp256k1::PublicKey::from_secret_key(&secp, &sk);
|
||||
@ -661,8 +798,6 @@ impl LightWallet {
|
||||
}
|
||||
|
||||
pub fn encrypt(&mut self, passwd: String) -> io::Result<()> {
|
||||
use sodiumoxide::crypto::secretbox;
|
||||
|
||||
if self.encrypted {
|
||||
return Err(io::Error::new(ErrorKind::AlreadyExists, "Wallet is already encrypted"));
|
||||
}
|
||||
@ -674,8 +809,12 @@ impl LightWallet {
|
||||
let cipher = secretbox::seal(&self.seed, &nonce, &key);
|
||||
|
||||
self.enc_seed.copy_from_slice(&cipher);
|
||||
self.nonce = vec![];
|
||||
self.nonce.extend_from_slice(nonce.as_ref());
|
||||
self.nonce = nonce.as_ref().to_vec();
|
||||
|
||||
// Encrypt the individual keys
|
||||
self.zkeys.write().unwrap().iter_mut()
|
||||
.map(|k| k.encrypt(&key))
|
||||
.collect::<io::Result<Vec<()>>>()?;
|
||||
|
||||
self.encrypted = true;
|
||||
self.lock()?;
|
||||
@ -695,7 +834,11 @@ impl LightWallet {
|
||||
// Empty the seed and the secret keys
|
||||
self.seed.copy_from_slice(&[0u8; 32]);
|
||||
self.tkeys = Arc::new(RwLock::new(vec![]));
|
||||
self.extsks = Arc::new(RwLock::new(vec![]));
|
||||
|
||||
// Remove all the private key from the zkeys
|
||||
self.zkeys.write().unwrap().iter_mut().map(|zk| {
|
||||
zk.lock()
|
||||
}).collect::<io::Result<Vec<_>>>()?;
|
||||
|
||||
self.unlocked = false;
|
||||
|
||||
@ -703,8 +846,6 @@ impl LightWallet {
|
||||
}
|
||||
|
||||
pub fn unlock(&mut self, passwd: String) -> io::Result<()> {
|
||||
use sodiumoxide::crypto::secretbox;
|
||||
|
||||
if !self.encrypted {
|
||||
return Err(Error::new(ErrorKind::AlreadyExists, "Wallet is not encrypted"));
|
||||
}
|
||||
@ -729,26 +870,6 @@ impl LightWallet {
|
||||
// we need to get the 64 byte bip39 entropy
|
||||
let bip39_seed = bip39::Seed::new(&Mnemonic::from_entropy(&seed, Language::English).unwrap(), "");
|
||||
|
||||
// Sapling keys
|
||||
let mut extsks = vec![];
|
||||
for pos in 0..self.zaddress.read().unwrap().len() {
|
||||
let (extsk, extfvk, address) =
|
||||
LightWallet::get_zaddr_from_bip39seed(&self.config, &bip39_seed.as_bytes(), pos as u32);
|
||||
|
||||
if address != self.zaddress.read().unwrap()[pos] {
|
||||
return Err(io::Error::new(ErrorKind::InvalidData,
|
||||
format!("zaddress mismatch at {}. {:?} vs {:?}", pos, address, self.zaddress.read().unwrap()[pos])));
|
||||
}
|
||||
|
||||
if extfvk != self.extfvks.read().unwrap()[pos] {
|
||||
return Err(io::Error::new(ErrorKind::InvalidData,
|
||||
format!("fvk mismatch at {}. {:?} vs {:?}", pos, extfvk, self.extfvks.read().unwrap()[pos])));
|
||||
}
|
||||
|
||||
// Don't add it to self yet, we'll do that at the end when everything is verified
|
||||
extsks.push(extsk);
|
||||
}
|
||||
|
||||
// Transparent keys
|
||||
let mut tkeys = vec![];
|
||||
for pos in 0..self.taddresses.read().unwrap().len() {
|
||||
@ -763,8 +884,12 @@ impl LightWallet {
|
||||
tkeys.push(sk);
|
||||
}
|
||||
|
||||
// Go over the zkeys, and add the spending keys again
|
||||
self.zkeys.write().unwrap().iter_mut().map(|zk| {
|
||||
zk.unlock(&self.config, bip39_seed.as_bytes(), &key)
|
||||
}).collect::<io::Result<Vec<()>>>()?;
|
||||
|
||||
// Everything checks out, so we'll update our wallet with the decrypted values
|
||||
self.extsks = Arc::new(RwLock::new(extsks));
|
||||
self.tkeys = Arc::new(RwLock::new(tkeys));
|
||||
self.seed.copy_from_slice(&seed);
|
||||
|
||||
@ -786,6 +911,11 @@ impl LightWallet {
|
||||
self.unlock(passwd)?;
|
||||
}
|
||||
|
||||
// Remove encryption from individual zkeys
|
||||
self.zkeys.write().unwrap().iter_mut().map(|zk| {
|
||||
zk.remove_encryption()
|
||||
}).collect::<io::Result<Vec<()>>>()?;
|
||||
|
||||
// Permanantly remove the encryption
|
||||
self.encrypted = false;
|
||||
self.nonce = vec![];
|
||||
@ -847,6 +977,48 @@ impl LightWallet {
|
||||
.sum::<u64>()
|
||||
}
|
||||
|
||||
pub fn unverified_zbalance(&self, addr: Option<String>) -> u64 {
|
||||
let anchor_height = match self.get_target_height_and_anchor_offset() {
|
||||
Some((height, anchor_offset)) => height - anchor_offset as u32 - 1,
|
||||
None => return 0,
|
||||
};
|
||||
|
||||
self.txs
|
||||
.read()
|
||||
.unwrap()
|
||||
.values()
|
||||
.map(|tx| {
|
||||
tx.notes
|
||||
.iter()
|
||||
.filter(|nd| nd.spent.is_none() && nd.unconfirmed_spent.is_none())
|
||||
.filter(|nd| {
|
||||
// Check to see if we have this note's spending key.
|
||||
self.have_spendingkey_for_extfvk(&nd.extfvk)
|
||||
})
|
||||
.filter(|nd| { // TODO, this whole section is shared with verified_balance. Refactor it.
|
||||
match addr.clone() {
|
||||
Some(a) => a == encode_payment_address(
|
||||
self.config.hrp_sapling_address(),
|
||||
&nd.extfvk.fvk.vk
|
||||
.to_payment_address(nd.diversifier, &JUBJUB).unwrap()
|
||||
),
|
||||
None => true
|
||||
}
|
||||
})
|
||||
.map(|nd| {
|
||||
if tx.block as u32 <= anchor_height {
|
||||
// If confirmed, then unconfirmed is 0
|
||||
0
|
||||
} else {
|
||||
// If confirmed but dont have anchor yet, it is unconfirmed
|
||||
nd.note.value
|
||||
}
|
||||
})
|
||||
.sum::<u64>()
|
||||
})
|
||||
.sum::<u64>()
|
||||
}
|
||||
|
||||
pub fn verified_zbalance(&self, addr: Option<String>) -> u64 {
|
||||
let anchor_height = match self.get_target_height_and_anchor_offset() {
|
||||
Some((height, anchor_offset)) => height - anchor_offset as u32 - 1,
|
||||
@ -861,6 +1033,7 @@ impl LightWallet {
|
||||
if tx.block as u32 <= anchor_height {
|
||||
tx.notes
|
||||
.iter()
|
||||
.filter(|nd| nd.spent.is_none() && nd.unconfirmed_spent.is_none())
|
||||
.filter(|nd| { // TODO, this whole section is shared with verified_balance. Refactor it.
|
||||
match addr.clone() {
|
||||
Some(a) => a == encode_payment_address(
|
||||
@ -871,7 +1044,7 @@ impl LightWallet {
|
||||
None => true
|
||||
}
|
||||
})
|
||||
.map(|nd| if nd.spent.is_none() && nd.unconfirmed_spent.is_none() { nd.note.value } else { 0 })
|
||||
.map(|nd| nd.note.value)
|
||||
.sum::<u64>()
|
||||
} else {
|
||||
0
|
||||
@ -880,6 +1053,57 @@ impl LightWallet {
|
||||
.sum::<u64>()
|
||||
}
|
||||
|
||||
pub fn spendable_zbalance(&self, addr: Option<String>) -> u64 {
|
||||
let anchor_height = self.get_anchor_height();
|
||||
|
||||
self.txs
|
||||
.read()
|
||||
.unwrap()
|
||||
.values()
|
||||
.map(|tx| {
|
||||
if tx.block as u32 <= anchor_height {
|
||||
tx.notes
|
||||
.iter()
|
||||
.filter(|nd| nd.spent.is_none() && nd.unconfirmed_spent.is_none())
|
||||
.filter(|nd| {
|
||||
// Check to see if we have this note's spending key.
|
||||
self.have_spendingkey_for_extfvk(&nd.extfvk)
|
||||
})
|
||||
.filter(|nd| { // TODO, this whole section is shared with verified_balance. Refactor it.
|
||||
match addr.clone() {
|
||||
Some(a) => a == encode_payment_address(
|
||||
self.config.hrp_sapling_address(),
|
||||
&nd.extfvk.fvk.vk
|
||||
.to_payment_address(nd.diversifier, &JUBJUB).unwrap()
|
||||
),
|
||||
None => true
|
||||
}
|
||||
})
|
||||
.map(|nd| nd.note.value)
|
||||
.sum::<u64>()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})
|
||||
.sum::<u64>()
|
||||
}
|
||||
|
||||
pub fn have_spendingkey_for_extfvk(&self, extfvk: &ExtendedFullViewingKey) -> bool {
|
||||
match self.zkeys.read().unwrap().iter().find(|zk| zk.extfvk == *extfvk) {
|
||||
None => false,
|
||||
Some(zk) => zk.have_spending_key()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn have_spending_key_for_zaddress(&self, address: &String) -> bool {
|
||||
match self.zkeys.read().unwrap().iter()
|
||||
.find(|zk| encode_payment_address(self.config.hrp_sapling_address(), &zk.zaddress) == *address)
|
||||
{
|
||||
None => false,
|
||||
Some(zk) => zk.have_spending_key()
|
||||
}
|
||||
}
|
||||
|
||||
fn add_toutput_to_wtx(&self, height: i32, timestamp: u64, txid: &TxId, vout: &TxOut, n: u64) {
|
||||
let mut txs = self.txs.write().unwrap();
|
||||
|
||||
@ -945,8 +1169,11 @@ impl LightWallet {
|
||||
// If one of the last 'n' zaddress was used, ensure we add the next HD zaddress to the wallet
|
||||
pub fn ensure_hd_zaddresses(&self, address: &String) {
|
||||
let last_addresses = {
|
||||
self.zaddress.read().unwrap().iter().rev().take(GAP_RULE_UNUSED_ADDRESSES)
|
||||
.map(|s| encode_payment_address(self.config.hrp_sapling_address(), s))
|
||||
self.zkeys.read().unwrap().iter()
|
||||
.filter(|zk| zk.keytype == WalletZKeyType::HdKey)
|
||||
.rev()
|
||||
.take(GAP_RULE_UNUSED_ADDRESSES)
|
||||
.map(|s| encode_payment_address(self.config.hrp_sapling_address(), &s.zaddress))
|
||||
.collect::<Vec<String>>()
|
||||
};
|
||||
|
||||
@ -1071,18 +1298,18 @@ impl LightWallet {
|
||||
|
||||
// Scan shielded sapling outputs to see if anyone of them is us, and if it is, extract the memo
|
||||
for output in tx.shielded_outputs.iter() {
|
||||
let ivks: Vec<_> = self.extfvks.read().unwrap().iter().map(
|
||||
|extfvk| extfvk.fvk.vk.ivk().clone()
|
||||
).collect();
|
||||
let ivks: Vec<_> = self.zkeys.read().unwrap().iter()
|
||||
.map(|zk| zk.extfvk.fvk.vk.ivk()
|
||||
).collect();
|
||||
|
||||
let cmu = output.cmu;
|
||||
let ct = output.enc_ciphertext;
|
||||
|
||||
// Search all of our keys
|
||||
for (_account, ivk) in ivks.iter().enumerate() {
|
||||
for ivk in ivks {
|
||||
let epk_prime = output.ephemeral_key.as_prime_order(&JUBJUB).unwrap();
|
||||
|
||||
let (note, _to, memo) = match try_sapling_note_decryption(ivk, &epk_prime, &cmu, &ct) {
|
||||
let (note, _to, memo) = match try_sapling_note_decryption(&ivk, &epk_prime, &cmu, &ct) {
|
||||
Some(ret) => ret,
|
||||
None => continue,
|
||||
};
|
||||
@ -1098,7 +1325,10 @@ impl LightWallet {
|
||||
.and_then(|t| {
|
||||
t.notes.iter_mut().find(|nd| nd.note == note)
|
||||
}) {
|
||||
None => (),
|
||||
None => {
|
||||
info!("No txid matched for incoming sapling funds while updating memo");
|
||||
()
|
||||
},
|
||||
Some(nd) => {
|
||||
nd.memo = Some(memo)
|
||||
}
|
||||
@ -1133,21 +1363,26 @@ impl LightWallet {
|
||||
for spend in &tx.shielded_spends {
|
||||
let txid = nfs
|
||||
.iter()
|
||||
.find(|(nf, _, _)| &nf[..] == &spend.nullifier[..])
|
||||
.unwrap()
|
||||
.2;
|
||||
let spent_note = txs
|
||||
.get_mut(&txid)
|
||||
.unwrap()
|
||||
.notes
|
||||
.iter_mut()
|
||||
.find(|nd| &nd.nullifier[..] == &spend.nullifier[..])
|
||||
.unwrap();
|
||||
.find(|(nf, _, _)| &nf[..] == &spend.nullifier[..]);
|
||||
|
||||
match txid {
|
||||
Some(id) => {
|
||||
let spent_note = txs
|
||||
.get_mut(&id.2)
|
||||
.unwrap()
|
||||
.notes
|
||||
.iter_mut()
|
||||
.find(|nd| &nd.nullifier[..] == &spend.nullifier[..])
|
||||
.unwrap();
|
||||
|
||||
zinputs.push(encode_payment_address(
|
||||
self.config.hrp_sapling_address(),
|
||||
&spent_note.extfvk.fvk.vk
|
||||
.to_payment_address(spent_note.diversifier, &JUBJUB).unwrap()))
|
||||
},
|
||||
None => {}
|
||||
};
|
||||
|
||||
zinputs.push(encode_payment_address(
|
||||
self.config.hrp_sapling_address(),
|
||||
&spent_note.extfvk.fvk.vk
|
||||
.to_payment_address(spent_note.diversifier, &JUBJUB).unwrap()));
|
||||
|
||||
}
|
||||
|
||||
@ -1156,12 +1391,13 @@ impl LightWallet {
|
||||
|
||||
|
||||
// Search all ovks that we have
|
||||
let ovks: Vec<_> = self.extfvks.read().unwrap().iter().map(
|
||||
|extfvk| extfvk.fvk.ovk.clone()
|
||||
).collect();
|
||||
let ovks: Vec<_> = self.zkeys.read().unwrap().iter()
|
||||
.map(|zk| zk.extfvk.fvk.ovk.clone())
|
||||
.collect();
|
||||
|
||||
for (_account, ovk) in ovks.iter().enumerate() {
|
||||
match try_sapling_output_recovery(ovk,
|
||||
for ovk in ovks {
|
||||
match try_sapling_output_recovery(
|
||||
&ovk,
|
||||
&output.cv,
|
||||
&output.cmu,
|
||||
&output.ephemeral_key.as_prime_order(&JUBJUB).unwrap(),
|
||||
@ -1579,6 +1815,16 @@ impl LightWallet {
|
||||
// Create a write lock
|
||||
let mut txs = self.txs.write().unwrap();
|
||||
|
||||
// Trim the older witnesses
|
||||
txs.values_mut().for_each(|wtx| {
|
||||
wtx.notes
|
||||
.iter_mut()
|
||||
.filter(|nd| nd.spent.is_some() && nd.spent_at_height.is_some() && nd.spent_at_height.unwrap() < height - (MAX_REORG as i32) - 1)
|
||||
.for_each(|nd| {
|
||||
nd.witnesses.clear()
|
||||
})
|
||||
});
|
||||
|
||||
// Create a Vec containing all unspent nullifiers.
|
||||
// Include only the confirmed spent nullifiers, since unconfirmed ones still need to be included
|
||||
// during scan_block below.
|
||||
@ -1614,17 +1860,30 @@ impl LightWallet {
|
||||
|
||||
new_txs = {
|
||||
let nf_refs = nfs.iter().map(|(nf, account, _)| (nf.to_vec(), *account)).collect::<Vec<_>>();
|
||||
let extfvks: Vec<ExtendedFullViewingKey> = self.zkeys.read().unwrap().iter().map(|zk| zk.extfvk.clone()).collect();
|
||||
|
||||
// Create a single mutable slice of all the newly-added witnesses.
|
||||
// Create a single mutable slice of all the wallet's note's witnesses.
|
||||
let mut witness_refs: Vec<_> = txs
|
||||
.values_mut()
|
||||
.map(|tx| tx.notes.iter_mut().filter_map(|nd| nd.witnesses.last_mut()))
|
||||
.map(|tx|
|
||||
tx.notes.iter_mut()
|
||||
.filter_map(|nd|
|
||||
// Note was not spent
|
||||
if nd.spent.is_none() && nd.unconfirmed_spent.is_none() {
|
||||
nd.witnesses.last_mut()
|
||||
} else if nd.spent.is_some() && nd.spent_at_height.is_some() && nd.spent_at_height.unwrap() < height - (MAX_REORG as i32) - 1 {
|
||||
// Note was spent in the last 100 blocks
|
||||
nd.witnesses.last_mut()
|
||||
} else {
|
||||
// If note was old (spent NOT in the last 100 blocks)
|
||||
None
|
||||
}))
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
self.scan_block_internal(
|
||||
block.clone(),
|
||||
&self.extfvks.read().unwrap(),
|
||||
&extfvks,
|
||||
nf_refs,
|
||||
&mut block_data.tree,
|
||||
&mut witness_refs[..],
|
||||
@ -1671,6 +1930,7 @@ impl LightWallet {
|
||||
// Mark the note as spent, and remove the unconfirmed part of it
|
||||
info!("Marked a note as spent");
|
||||
spent_note.spent = Some(tx.txid);
|
||||
spent_note.spent_at_height = Some(height);
|
||||
spent_note.unconfirmed_spent = None::<TxId>;
|
||||
|
||||
total_shielded_value_spent += spent_note.note.value;
|
||||
@ -1687,7 +1947,7 @@ impl LightWallet {
|
||||
// Save notes.
|
||||
for output in tx.shielded_outputs
|
||||
{
|
||||
let new_note = SaplingNoteData::new(&self.extfvks.read().unwrap()[output.account], output);
|
||||
let new_note = SaplingNoteData::new(&self.zkeys.read().unwrap()[output.account].extfvk, output);
|
||||
match LightWallet::note_address(self.config.hrp_sapling_address(), &new_note) {
|
||||
Some(a) => {
|
||||
info!("Received sapling output to {}", a);
|
||||
@ -1733,15 +1993,34 @@ impl LightWallet {
|
||||
Ok(all_txs)
|
||||
}
|
||||
|
||||
pub fn send_to_address(
|
||||
// Add the spent_at_height for each sapling note that has been spent. This field was added in wallet version 8,
|
||||
// so for older wallets, it will need to be added
|
||||
pub fn fix_spent_at_height(&self) {
|
||||
// First, build an index of all the txids and the heights at which they were spent.
|
||||
let spent_txid_map: HashMap<_, _> = self.txs.read().unwrap().iter().map(|(txid, wtx)| (txid.clone(), wtx.block)).collect();
|
||||
|
||||
// Go over all the sapling notes that might need updating
|
||||
self.txs.write().unwrap().values_mut().for_each(|wtx| {
|
||||
wtx.notes.iter_mut()
|
||||
.filter(|nd| nd.spent.is_some() && nd.spent_at_height.is_none())
|
||||
.for_each(|nd| {
|
||||
nd.spent_at_height = spent_txid_map.get(&nd.spent.unwrap()).map(|b| *b);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_to_address<F> (
|
||||
&self,
|
||||
consensus_branch_id: u32,
|
||||
spend_params: &[u8],
|
||||
output_params: &[u8],
|
||||
from: &str,
|
||||
tos: Vec<(&str, u64, Option<String>)>,
|
||||
fee: &u64
|
||||
) -> Result<Box<[u8]>, String> {
|
||||
fee: &u64,
|
||||
broadcast_fn: F
|
||||
) -> Result<(String, Vec<u8>), String>
|
||||
where F: Fn(Box<[u8]>) -> Result<String, String>
|
||||
{
|
||||
if !self.unlocked {
|
||||
return Err("Cannot spend while wallet is locked".to_string());
|
||||
}
|
||||
@ -1788,14 +2067,30 @@ impl LightWallet {
|
||||
|
||||
// Select notes to cover the target value
|
||||
println!("{}: Selecting notes", now() - start_time);
|
||||
let target_value = Amount::from_u64(total_value).unwrap() + Amount::from_u64(*fee).unwrap() ;
|
||||
let notes: Vec<_> = self.txs.read().unwrap().iter()
|
||||
let target_value = Amount::from_u64(total_value).unwrap() + Amount::from_u64(*fee).unwrap();
|
||||
|
||||
// Select the candidate notes that are eligible to be spent
|
||||
let mut candidate_notes: Vec<_> = self.txs.read().unwrap().iter()
|
||||
.map(|(txid, tx)| tx.notes.iter().map(move |note| (*txid, note)))
|
||||
.flatten()
|
||||
.filter(|(_txid, note)|LightWallet::note_address(self.config.hrp_sapling_address(), note).unwrap() == from)
|
||||
.filter_map(|(txid, note)|
|
||||
SpendableNote::from(txid, note, anchor_offset, &self.extsks.read().unwrap()[note.account])
|
||||
)
|
||||
.filter_map(|(txid, note)| {
|
||||
// Filter out notes that are already spent
|
||||
if note.spent.is_some() || note.unconfirmed_spent.is_some() {
|
||||
None
|
||||
} else {
|
||||
// Get the spending key for the selected fvk, if we have it
|
||||
let extsk = self.zkeys.read().unwrap().iter()
|
||||
.find(|zk| zk.extfvk == note.extfvk)
|
||||
.and_then(|zk| zk.extsk.clone());
|
||||
SpendableNote::from(txid, note, anchor_offset, &extsk)
|
||||
}
|
||||
}).collect();
|
||||
|
||||
// Sort by highest value-notes first.
|
||||
candidate_notes.sort_by(|a, b| b.note.value.cmp(&a.note.value));
|
||||
|
||||
// Select the minimum number of notes required to satisfy the target value
|
||||
let notes: Vec<_> = candidate_notes.iter()
|
||||
.scan(0, |running_total, spendable| {
|
||||
let value = spendable.note.value;
|
||||
let ret = if *running_total < u64::from(target_value) {
|
||||
@ -1890,7 +2185,7 @@ impl LightWallet {
|
||||
// Use the ovk belonging to the address being sent from, if not using any notes
|
||||
// use the first address in the wallet for the ovk.
|
||||
let ovk = if notes.len() == 0 {
|
||||
self.extfvks.read().unwrap()[0].fvk.ovk
|
||||
self.zkeys.read().unwrap()[0].extfvk.fvk.ovk
|
||||
} else {
|
||||
ExtendedFullViewingKey::from(¬es[0].extsk).fvk.ovk
|
||||
};
|
||||
@ -1928,13 +2223,16 @@ impl LightWallet {
|
||||
// Compute memo if it exists
|
||||
let encoded_memo = match memo {
|
||||
None => None,
|
||||
Some(s) => match Memo::from_bytes(s.as_bytes()) {
|
||||
None => {
|
||||
let e = format!("Error creating output. Memo {:?} is too long", s);
|
||||
error!("{}", e);
|
||||
return Err(e);
|
||||
},
|
||||
Some(m) => Some(m)
|
||||
Some(s) => {
|
||||
// If the string starts with an "0x", and contains only hex chars ([a-f0-9]+) then
|
||||
// interpret it as a hex
|
||||
match utils::interpret_memo_string(&s) {
|
||||
Ok(m) => Some(m),
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -1970,7 +2268,11 @@ impl LightWallet {
|
||||
println!("{}: Transaction created", now() - start_time);
|
||||
println!("Transaction ID: {}", tx.txid());
|
||||
|
||||
// Create the TX bytes
|
||||
let mut raw_tx = vec![];
|
||||
tx.write(&mut raw_tx).unwrap();
|
||||
|
||||
let txid = broadcast_fn(raw_tx.clone().into_boxed_slice())?;
|
||||
|
||||
// Mark notes as spent.
|
||||
{
|
||||
@ -2008,10 +2310,16 @@ impl LightWallet {
|
||||
None => Memo::default(),
|
||||
Some(s) => {
|
||||
// If the address is not a z-address, then drop the memo
|
||||
if LightWallet::is_shielded_address(&addr.to_string(), &self.config) {
|
||||
Memo::from_bytes(s.as_bytes()).unwrap()
|
||||
} else {
|
||||
if !LightWallet::is_shielded_address(&addr.to_string(), &self.config) {
|
||||
Memo::default()
|
||||
} else {
|
||||
match utils::interpret_memo_string(s) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
Memo::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -2032,10 +2340,7 @@ impl LightWallet {
|
||||
}
|
||||
}
|
||||
|
||||
// Return the encoded transaction, so the caller can send it.
|
||||
let mut raw_tx = vec![];
|
||||
tx.write(&mut raw_tx).unwrap();
|
||||
Ok(raw_tx.into_boxed_slice())
|
||||
Ok((txid, raw_tx))
|
||||
}
|
||||
|
||||
// After some blocks have been mined, we need to remove the Txns from the mempool_tx structure
|
||||
|
@ -68,6 +68,7 @@ pub struct SaplingNoteData {
|
||||
pub(super) witnesses: Vec<IncrementalWitness<Node>>,
|
||||
pub(super) nullifier: [u8; 32],
|
||||
pub spent: Option<TxId>, // If this note was confirmed spent
|
||||
pub spent_at_height: Option<i32>, // The height at which this note was spent
|
||||
pub unconfirmed_spent: Option<TxId>, // If this note was spent in a send, but has not yet been confirmed.
|
||||
pub memo: Option<Memo>,
|
||||
pub is_change: bool,
|
||||
@ -106,7 +107,7 @@ pub fn read_note<R: Read>(mut reader: R) -> io::Result<(u64, Fs)> {
|
||||
|
||||
impl SaplingNoteData {
|
||||
fn serialized_version() -> u64 {
|
||||
1
|
||||
2
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
@ -132,6 +133,7 @@ impl SaplingNoteData {
|
||||
witnesses: vec![witness],
|
||||
nullifier: nf,
|
||||
spent: None,
|
||||
spent_at_height: None,
|
||||
unconfirmed_spent: None,
|
||||
memo: None,
|
||||
is_change: output.is_change,
|
||||
@ -141,10 +143,9 @@ impl SaplingNoteData {
|
||||
// Reading a note also needs the corresponding address to read from.
|
||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||
let version = reader.read_u64::<LittleEndian>()?;
|
||||
assert_eq!(version, SaplingNoteData::serialized_version());
|
||||
|
||||
let account = reader.read_u64::<LittleEndian>()? as usize;
|
||||
|
||||
|
||||
let extfvk = ExtendedFullViewingKey::read(&mut reader)?;
|
||||
|
||||
let mut diversifier_bytes = [0u8; 11];
|
||||
@ -176,6 +177,12 @@ impl SaplingNoteData {
|
||||
Ok(TxId{0: txid_bytes})
|
||||
})?;
|
||||
|
||||
let spent_at_height = if version >=2 {
|
||||
Optional::read(&mut reader, |r| r.read_i32::<LittleEndian>())?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let memo = Optional::read(&mut reader, |r| {
|
||||
let mut memo_bytes = [0u8; 512];
|
||||
r.read_exact(&mut memo_bytes)?;
|
||||
@ -195,6 +202,7 @@ impl SaplingNoteData {
|
||||
witnesses,
|
||||
nullifier,
|
||||
spent,
|
||||
spent_at_height,
|
||||
unconfirmed_spent: None,
|
||||
memo,
|
||||
is_change,
|
||||
@ -222,6 +230,8 @@ impl SaplingNoteData {
|
||||
writer.write_all(&self.nullifier)?;
|
||||
Optional::write(&mut writer, &self.spent, |w, t| w.write_all(&t.0))?;
|
||||
|
||||
Optional::write(&mut writer, &self.spent_at_height, |w, h| w.write_i32::<LittleEndian>(*h))?;
|
||||
|
||||
Optional::write(&mut writer, &self.memo, |w, m| w.write_all(m.as_bytes()))?;
|
||||
|
||||
writer.write_u8(if self.is_change {1} else {0})?;
|
||||
@ -512,9 +522,9 @@ pub struct SpendableNote {
|
||||
}
|
||||
|
||||
impl SpendableNote {
|
||||
pub fn from(txid: TxId, nd: &SaplingNoteData, anchor_offset: usize, extsk: &ExtendedSpendingKey) -> Option<Self> {
|
||||
pub fn from(txid: TxId, nd: &SaplingNoteData, anchor_offset: usize, extsk: &Option<ExtendedSpendingKey>) -> Option<Self> {
|
||||
// Include only notes that haven't been spent, or haven't been included in an unconfirmed spend yet.
|
||||
if nd.spent.is_none() && nd.unconfirmed_spent.is_none() &&
|
||||
if nd.spent.is_none() && nd.unconfirmed_spent.is_none() && extsk.is_some() &&
|
||||
nd.witnesses.len() >= (anchor_offset + 1) {
|
||||
let witness = nd.witnesses.get(nd.witnesses.len() - anchor_offset - 1);
|
||||
|
||||
@ -524,7 +534,7 @@ impl SpendableNote {
|
||||
diversifier: nd.diversifier,
|
||||
note: nd.note.clone(),
|
||||
witness: w.clone(),
|
||||
extsk: extsk.clone(),
|
||||
extsk: extsk.clone().unwrap(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,6 @@
|
||||
use std::io::{self, Read, Write};
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use zcash_primitives::note_encryption::Memo;
|
||||
|
||||
pub fn read_string<R: Read>(mut reader: R) -> io::Result<String> {
|
||||
// Strings are written as <littleendian> len + bytes
|
||||
@ -18,4 +19,26 @@ pub fn write_string<W: Write>(mut writer: W, s: &String) -> io::Result<()> {
|
||||
// Strings are written as len + utf8
|
||||
writer.write_u64::<LittleEndian>(s.as_bytes().len() as u64)?;
|
||||
writer.write_all(s.as_bytes())
|
||||
}
|
||||
|
||||
// Interpret a string or hex-encoded memo, and return a Memo object
|
||||
pub fn interpret_memo_string(memo_str: &String) -> Result<Memo, String> {
|
||||
// If the string starts with an "0x", and contains only hex chars ([a-f0-9]+) then
|
||||
// interpret it as a hex
|
||||
let s_bytes = if memo_str.to_lowercase().starts_with("0x") {
|
||||
match hex::decode(&memo_str[2..memo_str.len()]) {
|
||||
Ok(data) => data,
|
||||
Err(_) => Vec::from(memo_str.as_bytes())
|
||||
}
|
||||
} else {
|
||||
Vec::from(memo_str.as_bytes())
|
||||
};
|
||||
|
||||
match Memo::from_bytes(&s_bytes) {
|
||||
None => {
|
||||
let e = format!("Error creating output. Memo {:?} is too long", memo_str);
|
||||
return Err(e);
|
||||
},
|
||||
Some(m) => Ok(m)
|
||||
}
|
||||
}
|
426
lib/src/lightwallet/walletzkey.rs
Normal file
426
lib/src/lightwallet/walletzkey.rs
Normal file
@ -0,0 +1,426 @@
|
||||
use std::io::{self, Read, Write};
|
||||
use std::io::{Error, ErrorKind};
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use pairing::bls12_381::{Bls12};
|
||||
|
||||
use sodiumoxide::crypto::secretbox;
|
||||
|
||||
use zcash_primitives::{
|
||||
serialize::{Vector, Optional},
|
||||
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
||||
primitives::{PaymentAddress},
|
||||
};
|
||||
|
||||
use crate::lightclient::{LightClientConfig};
|
||||
use crate::lightwallet::LightWallet;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum WalletZKeyType {
|
||||
HdKey = 0,
|
||||
ImportedSpendingKey = 1,
|
||||
ImportedViewKey = 2
|
||||
}
|
||||
|
||||
// A struct that holds z-address private keys or view keys
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct WalletZKey {
|
||||
pub(super) keytype: WalletZKeyType,
|
||||
locked: bool,
|
||||
pub(super) extsk: Option<ExtendedSpendingKey>,
|
||||
pub(super) extfvk: ExtendedFullViewingKey,
|
||||
pub(super) zaddress: PaymentAddress<Bls12>,
|
||||
|
||||
// If this is a HD key, what is the key number
|
||||
pub(super) hdkey_num: Option<u32>,
|
||||
|
||||
// If locked, the encrypted private key is stored here
|
||||
enc_key: Option<Vec<u8>>,
|
||||
nonce: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl WalletZKey {
|
||||
pub fn new_hdkey(hdkey_num: u32, extsk: ExtendedSpendingKey) -> Self {
|
||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
||||
let zaddress = extfvk.default_address().unwrap().1;
|
||||
|
||||
WalletZKey {
|
||||
keytype: WalletZKeyType::HdKey,
|
||||
locked: false,
|
||||
extsk: Some(extsk),
|
||||
extfvk,
|
||||
zaddress,
|
||||
hdkey_num: Some(hdkey_num),
|
||||
enc_key: None,
|
||||
nonce: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_locked_hdkey(hdkey_num: u32, extfvk: ExtendedFullViewingKey) -> Self {
|
||||
let zaddress = extfvk.default_address().unwrap().1;
|
||||
|
||||
WalletZKey {
|
||||
keytype: WalletZKeyType::HdKey,
|
||||
locked: true,
|
||||
extsk: None,
|
||||
extfvk,
|
||||
zaddress,
|
||||
hdkey_num: Some(hdkey_num),
|
||||
enc_key: None,
|
||||
nonce: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_imported_sk(extsk: ExtendedSpendingKey) -> Self {
|
||||
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
||||
let zaddress = extfvk.default_address().unwrap().1;
|
||||
|
||||
WalletZKey {
|
||||
keytype: WalletZKeyType::ImportedSpendingKey,
|
||||
locked: false,
|
||||
extsk: Some(extsk),
|
||||
extfvk,
|
||||
zaddress,
|
||||
hdkey_num: None,
|
||||
enc_key: None,
|
||||
nonce: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_imported_viewkey(extfvk: ExtendedFullViewingKey) -> Self {
|
||||
let zaddress = extfvk.default_address().unwrap().1;
|
||||
|
||||
WalletZKey {
|
||||
keytype: WalletZKeyType::ImportedViewKey,
|
||||
locked: false,
|
||||
extsk: None,
|
||||
extfvk,
|
||||
zaddress,
|
||||
hdkey_num: None,
|
||||
enc_key: None,
|
||||
nonce: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn have_spending_key(&self) -> bool {
|
||||
self.extsk.is_some() || self.enc_key.is_some() || self.hdkey_num.is_some()
|
||||
}
|
||||
|
||||
fn serialized_version() -> u8 {
|
||||
return 1;
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(mut inp: R) -> io::Result<Self> {
|
||||
let version = inp.read_u8()?;
|
||||
assert!(version <= Self::serialized_version());
|
||||
|
||||
let keytype: WalletZKeyType = match inp.read_u32::<LittleEndian>()? {
|
||||
0 => Ok(WalletZKeyType::HdKey),
|
||||
1 => Ok(WalletZKeyType::ImportedSpendingKey),
|
||||
2 => Ok(WalletZKeyType::ImportedViewKey),
|
||||
n => Err(io::Error::new(ErrorKind::InvalidInput, format!("Unknown zkey type {}", n)))
|
||||
}?;
|
||||
|
||||
let locked = inp.read_u8()? > 0;
|
||||
|
||||
let extsk = Optional::read(&mut inp, |r| ExtendedSpendingKey::read(r))?;
|
||||
let extfvk = ExtendedFullViewingKey::read(&mut inp)?;
|
||||
let zaddress = extfvk.default_address().unwrap().1;
|
||||
|
||||
let hdkey_num = Optional::read(&mut inp, |r| r.read_u32::<LittleEndian>())?;
|
||||
|
||||
let enc_key = Optional::read(&mut inp, |r|
|
||||
Vector::read(r, |r| r.read_u8()))?;
|
||||
let nonce = Optional::read(&mut inp, |r|
|
||||
Vector::read(r, |r| r.read_u8()))?;
|
||||
|
||||
Ok(WalletZKey {
|
||||
keytype,
|
||||
locked,
|
||||
extsk,
|
||||
extfvk,
|
||||
zaddress,
|
||||
hdkey_num,
|
||||
enc_key,
|
||||
nonce,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, mut out: W) -> io::Result<()> {
|
||||
out.write_u8(Self::serialized_version())?;
|
||||
|
||||
out.write_u32::<LittleEndian>(self.keytype.clone() as u32)?;
|
||||
|
||||
out.write_u8(self.locked as u8)?;
|
||||
|
||||
Optional::write(&mut out, &self.extsk, |w, sk| ExtendedSpendingKey::write(sk, w))?;
|
||||
|
||||
ExtendedFullViewingKey::write(&self.extfvk, &mut out)?;
|
||||
|
||||
Optional::write(&mut out, &self.hdkey_num, |o, n| o.write_u32::<LittleEndian>(*n))?;
|
||||
|
||||
// Write enc_key
|
||||
Optional::write(&mut out, &self.enc_key, |o, v|
|
||||
Vector::write(o, v, |o,n| o.write_u8(*n)))?;
|
||||
|
||||
// Write nonce
|
||||
Optional::write(&mut out, &self.nonce, |o, v|
|
||||
Vector::write(o, v, |o,n| o.write_u8(*n)))
|
||||
}
|
||||
|
||||
pub fn lock(&mut self) -> io::Result<()> {
|
||||
match self.keytype {
|
||||
WalletZKeyType::HdKey => {
|
||||
// For HD keys, just empty out the keys, since they will be reconstructed from the hdkey_num
|
||||
self.extsk = None;
|
||||
self.locked = true;
|
||||
},
|
||||
WalletZKeyType::ImportedSpendingKey => {
|
||||
// For imported keys, encrypt the key into enckey
|
||||
// assert that we have the encrypted key.
|
||||
if self.enc_key.is_none() {
|
||||
return Err(Error::new(ErrorKind::InvalidInput, "Can't lock when imported key is not encrypted"));
|
||||
}
|
||||
self.extsk = None;
|
||||
self.locked = true;
|
||||
},
|
||||
WalletZKeyType::ImportedViewKey => {
|
||||
// For viewing keys, there is nothing to lock, so just return true
|
||||
self.locked = true;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unlock(&mut self, config: &LightClientConfig, bip39_seed: &[u8], key: &secretbox::Key) -> io::Result<()> {
|
||||
match self.keytype {
|
||||
WalletZKeyType::HdKey => {
|
||||
let (extsk, extfvk, address) =
|
||||
LightWallet::get_zaddr_from_bip39seed(&config, &bip39_seed, self.hdkey_num.unwrap());
|
||||
|
||||
if address != self.zaddress {
|
||||
return Err(io::Error::new(ErrorKind::InvalidData,
|
||||
format!("zaddress mismatch at {}. {:?} vs {:?}", self.hdkey_num.unwrap(), address, self.zaddress)));
|
||||
}
|
||||
|
||||
if extfvk != self.extfvk {
|
||||
return Err(io::Error::new(ErrorKind::InvalidData,
|
||||
format!("fvk mismatch at {}. {:?} vs {:?}", self.hdkey_num.unwrap(), extfvk, self.extfvk)));
|
||||
}
|
||||
|
||||
self.extsk = Some(extsk);
|
||||
},
|
||||
WalletZKeyType::ImportedSpendingKey => {
|
||||
// For imported keys, we need to decrypt from the encrypted key
|
||||
let nonce = secretbox::Nonce::from_slice(&self.nonce.as_ref().unwrap()).unwrap();
|
||||
let extsk_bytes = match secretbox::open(&self.enc_key.as_ref().unwrap(), &nonce, &key) {
|
||||
Ok(s) => s,
|
||||
Err(_) => {return Err(io::Error::new(ErrorKind::InvalidData, "Decryption failed. Is your password correct?"));}
|
||||
};
|
||||
|
||||
self.extsk = Some(ExtendedSpendingKey::read(&extsk_bytes[..])?);
|
||||
},
|
||||
WalletZKeyType::ImportedViewKey => {
|
||||
// Viewing key unlocking is basically a no op
|
||||
}
|
||||
};
|
||||
|
||||
self.locked = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn encrypt(&mut self, key: &secretbox::Key) -> io::Result<()> {
|
||||
match self.keytype {
|
||||
WalletZKeyType::HdKey => {
|
||||
// For HD keys, we don't need to do anything, since the hdnum has all the info to recreate this key
|
||||
},
|
||||
WalletZKeyType::ImportedSpendingKey => {
|
||||
// For imported keys, encrypt the key into enckey
|
||||
let nonce = secretbox::gen_nonce();
|
||||
|
||||
let mut sk_bytes = vec![];
|
||||
self.extsk.as_ref().unwrap().write(&mut sk_bytes)?;
|
||||
|
||||
self.enc_key = Some(secretbox::seal(&sk_bytes, &nonce, &key));
|
||||
self.nonce = Some(nonce.as_ref().to_vec());
|
||||
},
|
||||
WalletZKeyType::ImportedViewKey => {
|
||||
// Encrypting a viewing key is a no-op
|
||||
}
|
||||
}
|
||||
|
||||
// Also lock after encrypt
|
||||
self.lock()
|
||||
}
|
||||
|
||||
pub fn remove_encryption(&mut self) -> io::Result<()> {
|
||||
if self.locked {
|
||||
return Err(Error::new(ErrorKind::InvalidInput, "Can't remove encryption while locked"));
|
||||
}
|
||||
|
||||
match self.keytype {
|
||||
WalletZKeyType::HdKey => {
|
||||
// For HD keys, we don't need to do anything, since the hdnum has all the info to recreate this key
|
||||
Ok(())
|
||||
},
|
||||
WalletZKeyType::ImportedSpendingKey => {
|
||||
self.enc_key = None;
|
||||
self.nonce = None;
|
||||
Ok(())
|
||||
},
|
||||
WalletZKeyType::ImportedViewKey => {
|
||||
// Removing encryption is a no-op for viewing keys
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use zcash_client_backend::{
|
||||
encoding::{encode_payment_address, decode_extended_spending_key, decode_extended_full_viewing_key}
|
||||
};
|
||||
use sodiumoxide::crypto::secretbox;
|
||||
|
||||
use crate::lightclient::LightClientConfig;
|
||||
use super::WalletZKey;
|
||||
|
||||
fn get_config() -> LightClientConfig {
|
||||
LightClientConfig {
|
||||
server: "0.0.0.0:0".parse().unwrap(),
|
||||
chain_name: "main".to_string(),
|
||||
sapling_activation_height: 0,
|
||||
consensus_branch_id: "000000".to_string(),
|
||||
anchor_offset: 0,
|
||||
data_dir: None,
|
||||
address_params: AddressParameters::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize() {
|
||||
let config = get_config();
|
||||
|
||||
// Priv Key's address is "zs1fxgluwznkzm52ux7jkf4st5znwzqay8zyz4cydnyegt2rh9uhr9458z0nk62fdsssx0cqhy6lyv"
|
||||
let privkey = "secret-extended-key-main1q0p44m9zqqqqpqyxfvy5w2vq6ahvxyrwsk2w4h2zleun4cft4llmnsjlv77lhuuknv6x9jgu5g2clf3xq0wz9axxxq8klvv462r5pa32gjuj5uhxnvps6wsrdg6xll05unwks8qpgp4psmvy5e428uxaggn4l29duk82k3sv3njktaaj453fdmfmj2fup8rls4egqxqtj2p5a3yt4070khn99vzxj5ag5qjngc4v2kq0ctl9q2rpc2phu4p3e26egu9w88mchjf83sqgh3cev";
|
||||
|
||||
let esk = decode_extended_spending_key(config.hrp_sapling_private_key(), privkey).unwrap().unwrap();
|
||||
let wzk = WalletZKey::new_imported_sk(esk);
|
||||
assert_eq!(encode_payment_address(config.hrp_sapling_address(), &wzk.zaddress), "zs1fxgluwznkzm52ux7jkf4st5znwzqay8zyz4cydnyegt2rh9uhr9458z0nk62fdsssx0cqhy6lyv".to_string());
|
||||
|
||||
let mut v: Vec<u8> = vec![];
|
||||
// Serialize
|
||||
wzk.write(&mut v).unwrap();
|
||||
// Read it right back
|
||||
let wzk2 = WalletZKey::read(&v[..]).unwrap();
|
||||
|
||||
{
|
||||
assert_eq!(wzk, wzk2);
|
||||
assert_eq!(wzk.extsk, wzk2.extsk);
|
||||
assert_eq!(wzk.extfvk, wzk2.extfvk);
|
||||
assert_eq!(wzk.zaddress, wzk2.zaddress);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encrypt_decrypt_sk() {
|
||||
let config = get_config();
|
||||
|
||||
// Priv Key's address is "zs1fxgluwznkzm52ux7jkf4st5znwzqay8zyz4cydnyegt2rh9uhr9458z0nk62fdsssx0cqhy6lyv"
|
||||
let privkey = "secret-extended-key-main1q0p44m9zqqqqpqyxfvy5w2vq6ahvxyrwsk2w4h2zleun4cft4llmnsjlv77lhuuknv6x9jgu5g2clf3xq0wz9axxxq8klvv462r5pa32gjuj5uhxnvps6wsrdg6xll05unwks8qpgp4psmvy5e428uxaggn4l29duk82k3sv3njktaaj453fdmfmj2fup8rls4egqxqtj2p5a3yt4070khn99vzxj5ag5qjngc4v2kq0ctl9q2rpc2phu4p3e26egu9w88mchjf83sqgh3cev";
|
||||
|
||||
let esk = decode_extended_spending_key(config.hrp_sapling_private_key(), privkey).unwrap().unwrap();
|
||||
let mut wzk = WalletZKey::new_imported_sk(esk);
|
||||
assert_eq!(encode_payment_address(config.hrp_sapling_address(), &wzk.zaddress), "zs1fxgluwznkzm52ux7jkf4st5znwzqay8zyz4cydnyegt2rh9uhr9458z0nk62fdsssx0cqhy6lyv".to_string());
|
||||
|
||||
// Can't lock without encryption
|
||||
assert!(wzk.lock().is_err());
|
||||
|
||||
// Encryption key
|
||||
let key = secretbox::Key::from_slice(&[0; 32]).unwrap();
|
||||
|
||||
// Encrypt, but save the extsk first
|
||||
let orig_extsk = wzk.extsk.clone().unwrap();
|
||||
wzk.encrypt(&key).unwrap();
|
||||
{
|
||||
assert!(wzk.enc_key.is_some());
|
||||
assert!(wzk.nonce.is_some());
|
||||
}
|
||||
|
||||
// Now lock
|
||||
assert!(wzk.lock().is_ok());
|
||||
{
|
||||
assert!(wzk.extsk.is_none());
|
||||
assert_eq!(wzk.locked, true);
|
||||
assert_eq!(wzk.zaddress, wzk.extfvk.default_address().unwrap().1);
|
||||
}
|
||||
|
||||
// Can't remove encryption without unlocking
|
||||
assert!(wzk.remove_encryption().is_err());
|
||||
|
||||
// Unlock
|
||||
assert!(wzk.unlock(&config, &[], &key).is_ok());
|
||||
{
|
||||
assert_eq!(wzk.extsk, Some(orig_extsk));
|
||||
}
|
||||
|
||||
// Remove encryption
|
||||
assert!(wzk.remove_encryption().is_ok());
|
||||
{
|
||||
assert_eq!(wzk.enc_key, None);
|
||||
assert_eq!(wzk.nonce, None);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_encrypt_decrypt_vk() {
|
||||
let config = get_config();
|
||||
|
||||
// Priv Key's address is "zs1va5902apnzlhdu0pw9r9q7ca8s4vnsrp2alr6xndt69jnepn2v2qrj9vg3wfcnjyks5pg65g9dc"
|
||||
let viewkey = "zxviews1qvvx7cqdqyqqpqqte7292el2875kw2fgvnkmlmrufyszlcy8xgstwarnumqye3tr3d9rr3ydjm9zl9464majh4pa3ejkfy779dm38sfnkar67et7ykxkk0z9rfsmf9jclfj2k85xt2exkg4pu5xqyzyxzlqa6x3p9wrd7pwdq2uvyg0sal6zenqgfepsdp8shestvkzxuhm846r2h3m4jvsrpmxl8pfczxq87886k0wdasppffjnd2eh47nlmkdvrk6rgyyl0ekh3ycqtvvje";
|
||||
|
||||
let extfvk = decode_extended_full_viewing_key(config.hrp_sapling_viewing_key(), viewkey).unwrap().unwrap();
|
||||
let mut wzk = WalletZKey::new_imported_viewkey(extfvk);
|
||||
|
||||
assert_eq!(encode_payment_address(config.hrp_sapling_address(), &wzk.zaddress), "zs1va5902apnzlhdu0pw9r9q7ca8s4vnsrp2alr6xndt69jnepn2v2qrj9vg3wfcnjyks5pg65g9dc".to_string());
|
||||
|
||||
// Encryption key
|
||||
let key = secretbox::Key::from_slice(&[0; 32]).unwrap();
|
||||
|
||||
// Encrypt
|
||||
wzk.encrypt(&key).unwrap();
|
||||
{
|
||||
assert!(wzk.enc_key.is_none());
|
||||
assert!(wzk.nonce.is_none());
|
||||
}
|
||||
|
||||
// Now lock
|
||||
assert!(wzk.lock().is_ok());
|
||||
{
|
||||
assert!(wzk.extsk.is_none());
|
||||
assert_eq!(wzk.locked, true);
|
||||
assert_eq!(wzk.zaddress, wzk.extfvk.default_address().unwrap().1);
|
||||
}
|
||||
|
||||
// Can't remove encryption without unlocking
|
||||
assert!(wzk.remove_encryption().is_err());
|
||||
|
||||
// Unlock
|
||||
assert!(wzk.unlock(&config, &[], &key).is_ok());
|
||||
{
|
||||
assert_eq!(wzk.extsk, None);
|
||||
}
|
||||
|
||||
// Remove encryption
|
||||
assert!(wzk.remove_encryption().is_ok());
|
||||
{
|
||||
assert_eq!(wzk.enc_key, None);
|
||||
assert_eq!(wzk.nonce, None);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user