Merge pull request #5699 from nuttycom/feature/wallet_orchard-merge_master

Merge master branch back to feature/wallet_orchard
This commit is contained in:
Kris Nuttycombe 2022-03-17 18:25:28 -06:00 committed by GitHub
commit cf84e1e79d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1231 additions and 160 deletions

View File

@ -1,3 +1,6 @@
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
[source.crates-io]
replace-with = "vendored-sources"

1
.gitignore vendored
View File

@ -3,6 +3,7 @@
*.exe
src/bitcoin
src/zcashd
src/zcashd-wallet-tool
src/zcash-cli
src/zcash-gtest
src/zcash-tx

316
Cargo.lock generated
View File

@ -2,6 +2,21 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aead"
version = "0.4.3"
@ -17,7 +32,7 @@ version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"cipher",
"cpufeatures",
"opaque-debug",
@ -52,6 +67,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "159bb86af3a200e19a068f4224eae4c8bb2d0fa054c7e5d1cacd5cef95e684cd"
[[package]]
name = "arrayref"
version = "0.3.6"
@ -85,6 +106,21 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f"
dependencies = [
"addr2line",
"cc",
"cfg-if 1.0.0",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "base64ct"
version = "1.0.1"
@ -136,7 +172,7 @@ checksum = "d0830ae4cc96b0617cc912970c2b17e89456fecbf55e8eed53a956f37ab50c41"
dependencies = [
"hmac",
"pbkdf2",
"rand",
"rand 0.8.5",
"sha2",
"unicode-normalization",
"zeroize",
@ -284,6 +320,12 @@ version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -296,7 +338,7 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01b72a433d0cf2aef113ba70f62634c56fddb0f244e6377185c56a7cadbd8f91"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"cipher",
"cpufeatures",
"zeroize",
@ -324,6 +366,19 @@ dependencies = [
"generic-array",
]
[[package]]
name = "clearscreen"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7ed49b0e894fe6264a58496c7ec4e9d3c46f66b59efae527cd5bee429d0a418"
dependencies = [
"nix",
"terminfo",
"thiserror",
"which",
"winapi",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
@ -345,7 +400,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"crossbeam-utils",
]
@ -355,7 +410,7 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"crossbeam-epoch",
"crossbeam-utils",
]
@ -366,7 +421,7 @@ version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"crossbeam-utils",
"lazy_static",
"memoffset",
@ -379,7 +434,7 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"lazy_static",
]
@ -428,7 +483,7 @@ version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"num_cpus",
]
@ -460,6 +515,16 @@ dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
dependencies = [
"cfg-if 0.1.10",
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.3.6"
@ -601,7 +666,7 @@ version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
@ -612,11 +677,17 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"libc",
"wasi 0.10.2+wasi-snapshot-preview1",
]
[[package]]
name = "gimli"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
[[package]]
name = "group"
version = "0.11.0"
@ -629,6 +700,26 @@ dependencies = [
"subtle",
]
[[package]]
name = "gumdrop"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46571f5d540478cf70d2a42dd0d6d8e9f4b9cc7531544b93311e657b86568a0b"
dependencies = [
"gumdrop_derive",
]
[[package]]
name = "gumdrop_derive"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915ef07c710d84733522461de2a734d4d62a3fd39a4d4f404c2f385ef8618d05"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "halo2"
version = "0.1.0-beta.1"
@ -639,7 +730,7 @@ dependencies = [
"ff",
"group",
"pasta_curves",
"rand",
"rand 0.8.5",
"rayon",
]
@ -769,7 +860,7 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -835,13 +926,17 @@ checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db"
name = "librustzcash"
version = "0.2.0"
dependencies = [
"anyhow",
"backtrace",
"bellman",
"blake2b_simd 1.0.0",
"blake2s_simd 1.0.0",
"bls12_381",
"byteorder",
"clearscreen",
"ed25519-zebra",
"group",
"gumdrop",
"hyper",
"incrementalmerkletree",
"ipnet",
@ -852,10 +947,13 @@ dependencies = [
"metrics-exporter-prometheus",
"nonempty",
"orchard",
"rand 0.8.5",
"rand_core 0.6.3",
"secp256k1",
"secrecy",
"subtle",
"thiserror",
"time",
"tokio",
"tracing",
"tracing-appender",
@ -885,7 +983,7 @@ version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -992,6 +1090,16 @@ dependencies = [
"sketches-ddsketch",
]
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [
"adler",
"autocfg",
]
[[package]]
name = "mio"
version = "0.8.0"
@ -1023,6 +1131,29 @@ dependencies = [
"smallvec",
]
[[package]]
name = "nix"
version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf"
dependencies = [
"bitflags",
"cc",
"cfg-if 1.0.0",
"libc",
"memoffset",
]
[[package]]
name = "nom"
version = "5.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
dependencies = [
"memchr",
"version_check",
]
[[package]]
name = "nonempty"
version = "0.7.0"
@ -1087,6 +1218,15 @@ dependencies = [
"libc",
]
[[package]]
name = "object"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.9.0"
@ -1118,7 +1258,7 @@ dependencies = [
"memuse",
"nonempty",
"pasta_curves",
"rand",
"rand 0.8.5",
"reddsa",
"serde",
"subtle",
@ -1160,7 +1300,7 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall",
@ -1189,7 +1329,7 @@ dependencies = [
"ff",
"group",
"lazy_static",
"rand",
"rand 0.8.5",
"static_assertions",
"subtle",
]
@ -1204,6 +1344,44 @@ dependencies = [
"password-hash",
]
[[package]]
name = "phf"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_codegen"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
dependencies = [
"phf_generator",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
dependencies = [
"phf_shared",
"rand 0.7.3",
]
[[package]]
name = "phf_shared"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
dependencies = [
"siphasher",
]
[[package]]
name = "pin-project"
version = "1.0.10"
@ -1303,6 +1481,20 @@ dependencies = [
"nibble_vec",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom 0.1.16",
"libc",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc",
"rand_pcg",
]
[[package]]
name = "rand"
version = "0.8.5"
@ -1310,10 +1502,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_chacha 0.3.1",
"rand_core 0.6.3",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core 0.5.1",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
@ -1342,6 +1544,24 @@ dependencies = [
"getrandom 0.2.5",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_pcg"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "raw-cpuid"
version = "10.2.0"
@ -1463,6 +1683,12 @@ dependencies = [
"digest 0.10.3",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "scopeguard"
version = "1.1.0"
@ -1487,6 +1713,15 @@ dependencies = [
"cc",
]
[[package]]
name = "secrecy"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e"
dependencies = [
"zeroize",
]
[[package]]
name = "serde"
version = "1.0.136"
@ -1514,7 +1749,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
dependencies = [
"block-buffer 0.9.0",
"cfg-if",
"cfg-if 1.0.0",
"cpufeatures",
"digest 0.9.0",
"opaque-debug",
@ -1529,6 +1764,12 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "siphasher"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a86232ab60fa71287d7f2ddae4a7073f6b7aac33631c3015abb556f08c6d0a3e"
[[package]]
name = "sketches-ddsketch"
version = "0.1.2"
@ -1547,7 +1788,7 @@ version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"libc",
"winapi",
]
@ -1609,6 +1850,19 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "terminfo"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76971977e6121664ec1b960d1313aacfa75642adc93b9d4d53b247bd4cb1747e"
dependencies = [
"dirs",
"fnv",
"nom",
"phf",
"phf_codegen",
]
[[package]]
name = "thiserror"
version = "1.0.30"
@ -1647,8 +1901,15 @@ dependencies = [
"itoa 1.0.1",
"libc",
"num_threads",
"time-macros",
]
[[package]]
name = "time-macros"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6"
[[package]]
name = "tinyvec"
version = "1.5.1"
@ -1701,7 +1962,7 @@ version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6c650a8ef0cd2dd93736f033d21cbd1224c5a967aa0c258d00fcf7dafef9b9f"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
@ -1839,7 +2100,7 @@ version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"wasm-bindgen-macro",
]
@ -1897,6 +2158,17 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "which"
version = "4.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a5a7e487e921cf220206864a94a89b6c6905bfc19f1057fa26a4cb360e5c1d2"
dependencies = [
"either",
"lazy_static",
"libc",
]
[[package]]
name = "winapi"
version = "0.3.9"
@ -1995,7 +2267,7 @@ dependencies = [
"memuse",
"nonempty",
"orchard",
"rand",
"rand 0.8.5",
"rand_core 0.6.3",
"ripemd",
"secp256k1",

View File

@ -25,6 +25,10 @@ name = "rustzcash"
path = "src/rust/src/rustzcash.rs"
crate-type = ["staticlib"]
[[bin]]
name = "zcashd-wallet-tool"
path = "src/rust/bin/wallet_tool.rs"
[dependencies]
bellman = "0.11"
blake2b_simd = "1"
@ -61,10 +65,20 @@ metrics-exporter-prometheus = "0.6"
thiserror = "1"
tokio = { version = "1.0", features = ["rt", "net", "time", "macros"] }
# Wallet tool
# (also depends on thiserror, tracing, and tracing-subscriber with "env-filter" and "fmt" features)
anyhow = "1.0"
backtrace = "0.3"
clearscreen = "1.0"
gumdrop = "0.8"
rand = "0.8"
secrecy = "0.8"
time = { version = "0.3", features = ["formatting", "macros"] }
[dependencies.tracing-subscriber]
version = "0.3"
default-features = false
features = ["ansi", "env-filter", "time"]
features = ["ansi", "env-filter", "fmt", "time"]
[profile.release]
lto = true

View File

@ -48,6 +48,12 @@ $(BITCOIND_BIN): FORCE
$(BITCOIN_CLI_BIN): FORCE
$(MAKE) -C src $(@F)
check-security: FORCE
$(MAKE) -C src check-security
check-symbols: FORCE
$(MAKE) -C src check-symbols
if USE_LCOV
baseline.info:

View File

@ -189,8 +189,12 @@ def identify_executable(executable):
return None
if __name__ == '__main__':
args = sys.argv[1:]
allow_no_canary = "--allow-no-canary" in args
files = [arg for arg in args if not arg.startswith("--")]
retval = 0
for filename in sys.argv[1:]:
for filename in files:
try:
etype = identify_executable(filename)
if etype is None:
@ -201,6 +205,8 @@ if __name__ == '__main__':
failed = []
warning = []
for (name, func) in CHECKS[etype]:
if name == "Canary" and allow_no_canary:
continue
if not func(filename):
if name in NONFATAL:
warning.append(name)

View File

@ -1,6 +1,6 @@
zcash_packages := libsodium utfcpp
packages := boost libevent zeromq $(zcash_packages) googletest
native_packages := native_clang native_ccache native_rust
native_packages := native_clang native_ccache native_rust
ifneq (,$(wildcard /etc/arch-release))
native_packages += native_libtinfo

View File

@ -13,23 +13,23 @@ be generated on load of the wallet, or the first time the wallet is unlocked,
and is available via the `z_exportwallet` RPC call. All new addresses produced
by the wallet are now derived from this seed using the HD wallet functionality
described in ZIP 32 and ZIP 316. For users upgrading an existing Zcashd wallet,
it is recommended that the wallet be backed up prior to upgrading to the 4.5.2
it is recommended that the wallet be backed up prior to upgrading to the 4.7.0
Zcashd release.
Following the upgrade to 4.5.2, Zcashd will require that the user confirm that
Following the upgrade to 4.7.0, Zcashd will require that the user confirm that
they have backed up their new emergency recovery phrase, which may be obtained
from the output of the `z_exportwallet` RPC call. This confirmation can be
performed manually using the `zcashd-wallet-tool` utility that is supplied with
this release. The wallet will not allow the generation of new addresses until
this confirmation has been performed. It is recommended that after this
upgrade, that funds tied to preexisting addresses be migrated to newly
generated addresses so that all wallet funds are recoverable using the
emergency recovery phrase going forward. If you choose not to migrate funds in
this fashion, you will continue to need to securely back up the entire
`wallet.dat` file to ensure that you do not lose access to existing funds;
EXISTING FUNDS WILL NOT BE RECOVERABLE USING THE EMERGENCY RECOVERY PHRASE
UNLESS THEY HAVE BEEN MOVED TO A NEWLY GENERATED ADDRESS FOLLOWING THE 4.5.2
UPGRADE.
performed manually using the `zcashd-wallet-tool` utility that is supplied
with this release (built or installed in the same directory as `zcashd`).
The wallet will not allow the generation of new addresses until this
confirmation has been performed. It is recommended that after this upgrade,
that funds tied to preexisting addresses be migrated to newly generated
addresses so that all wallet funds are recoverable using the emergency
recovery phrase going forward. If you choose not to migrate funds in this
fashion, you will continue to need to securely back up the entire `wallet.dat`
file to ensure that you do not lose access to existing funds; EXISTING FUNDS
WILL NOT BE RECOVERABLE USING THE EMERGENCY RECOVERY PHRASE UNLESS THEY HAVE
BEEN MOVED TO A NEWLY GENERATED ADDRESS FOLLOWING THE 4.7.0 UPGRADE.
New RPC Methods
---------------

View File

@ -46,6 +46,17 @@ RE_RPATH_RUNPATH = re.compile('No RPATH.*No RUNPATH')
RE_FORTIFY_AVAILABLE = re.compile('FORTIFY_SOURCE support available.*Yes')
RE_FORTIFY_USED = re.compile('Binary compiled with FORTIFY_SOURCE support.*Yes')
CXX_BINARIES = [
'src/zcashd',
'src/zcash-cli',
'src/zcash-gtest',
'src/zcash-tx',
'src/test/test_bitcoin',
]
RUST_BINARIES = [
'src/zcashd-wallet-tool',
]
def test_rpath_runpath(filename):
output = subprocess.check_output(
[repofile('qa/zcash/checksec.sh'), '--file=' + repofile(filename)]
@ -86,21 +97,14 @@ def check_security_hardening():
if not magic.startswith(b'\x7fELF'):
return ret
ret &= test_rpath_runpath('src/zcashd')
ret &= test_rpath_runpath('src/zcash-cli')
ret &= test_rpath_runpath('src/zcash-gtest')
ret &= test_rpath_runpath('src/zcash-tx')
ret &= test_rpath_runpath('src/test/test_bitcoin')
for bin in CXX_BINARIES + RUST_BINARIES:
ret &= test_rpath_runpath(bin)
# NOTE: checksec.sh does not reliably determine whether FORTIFY_SOURCE
# is enabled for the entire binary. See issue #915.
# FORTIFY_SOURCE does mostly nothing for Clang before 10, which we don't
# pin yet, so we disable these tests.
# ret &= test_fortify_source('src/zcashd')
# ret &= test_fortify_source('src/zcash-cli')
# ret &= test_fortify_source('src/zcash-gtest')
# ret &= test_fortify_source('src/zcash-tx')
# ret &= test_fortify_source('src/test/test_bitcoin')
# FORTIFY_SOURCE is not applicable to Rust binaries.
for bin in CXX_BINARIES:
ret &= test_fortify_source(bin)
return ret

View File

@ -29,6 +29,9 @@ LIBSECP256K1=secp256k1/libsecp256k1.la
LIBUNIVALUE=univalue/libunivalue.la
LIBZCASH=libzcash.a
WALLET_TOOL_BIN=zcashd-wallet-tool$(EXEEXT)
WALLET_TOOL_BUILD=$(top_builddir)/target/$(RUST_TARGET)/release/zcashd-wallet-tool$(EXEEXT)
if ENABLE_ZMQ
LIBBITCOIN_ZMQ=libbitcoin_zmq.a
endif
@ -47,7 +50,7 @@ endif
# - Note that this does not prevent the secp256k1-sys vendored code from being built; this
# requires https://github.com/rust-bitcoin/rust-secp256k1/issues/380 to be addressed.
RUST_ENV_VARS = RUSTC="$(RUSTC)" TERM=dumb RUSTFLAGS="--cfg=rust_secp_no_symbol_renaming"
RUST_BUILD_OPTS = --lib --release --target $(RUST_TARGET)
RUST_BUILD_OPTS = --release --target $(RUST_TARGET) --manifest-path $(top_srcdir)/Cargo.toml
rust_verbose = $(rust_verbose_@AM_V@)
rust_verbose_ = $(rust_verbose_@AM_DEFAULT_V@)
@ -72,10 +75,16 @@ $(CARGO_CONFIGURED): $(top_srcdir)/.cargo/config.offline
$(AM_V_at)touch $@
endif
cargo-build: $(CARGO_CONFIGURED)
$(RUST_ENV_VARS) $(CARGO) build $(RUST_BUILD_OPTS) $(rust_verbose) --manifest-path $(top_srcdir)/Cargo.toml
cargo-build-lib: $(CARGO_CONFIGURED)
$(RUST_ENV_VARS) $(CARGO) build --lib $(RUST_BUILD_OPTS) $(rust_verbose)
$(LIBRUSTZCASH): cargo-build
cargo-build-bins: $(CARGO_CONFIGURED)
$(RUST_ENV_VARS) $(CARGO) build --bins $(RUST_BUILD_OPTS) $(rust_verbose)
$(WALLET_TOOL_BIN): cargo-build-bins
$(AM_V_at)cp $(WALLET_TOOL_BUILD) $@
$(LIBRUSTZCASH): cargo-build-lib
$(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*)
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F)
@ -99,6 +108,7 @@ lib_LTLIBRARIES = $(LIBZCASH_SCRIPT)
bin_PROGRAMS =
noinst_PROGRAMS =
bin_SCRIPTS =
TESTS =
BENCHMARKS =
@ -108,6 +118,7 @@ endif
if BUILD_BITCOIN_UTILS
bin_PROGRAMS += zcash-cli zcash-tx
bin_SCRIPTS += $(WALLET_TOOL_BIN)
endif
LIBZCASH_H = \
@ -116,7 +127,7 @@ LIBZCASH_H = \
zcash/Address.hpp \
zcash/address/transparent.h \
zcash/address/mnemonic.h \
zcash/address/orchard.h \
zcash/address/orchard.hpp \
zcash/address/sapling.hpp \
zcash/address/sprout.hpp \
zcash/address/unified.h \
@ -129,7 +140,7 @@ LIBZCASH_H = \
zcash/util.h \
zcash/Zcash.h
.PHONY: FORCE cargo-build check-symbols check-security
.PHONY: FORCE cargo-build-lib cargo-build-bins check-symbols check-security
# bitcoin core #
BITCOIN_CORE_H = \
addrdb.h \
@ -602,7 +613,7 @@ CTAES_DIST += crypto/ctaes/ctaes.h
CTAES_DIST += crypto/ctaes/README.md
CTAES_DIST += crypto/ctaes/test.c
CLEANFILES = *.gcda *.gcno */*.gcno wallet/*/*.gcno
CLEANFILES = *.gcda *.gcno */*.gcno wallet/*/*.gcno $(bin_SCRIPTS)
DISTCLEANFILES = obj/build.h
@ -626,16 +637,17 @@ clean-local:
$(AM_V_CXX) $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CXXFLAGS) $(AM_CXXFLAGS) $(PIE_FLAGS) $(CXXFLAGS) -c -o $@ $<
check-symbols: $(bin_PROGRAMS)
check-symbols: $(bin_PROGRAMS) $(bin_SCRIPTS)
if GLIBC_BACK_COMPAT
@echo "Checking glibc back compat of [$(bin_PROGRAMS)]..."
$(AM_V_at) READELF=$(READELF) CPPFILT=$(CPPFILT) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
@echo "Checking glibc back compat of [$(bin_PROGRAMS) $(bin_SCRIPTS)]..."
$(AM_V_at) READELF=$(READELF) CPPFILT=$(CPPFILT) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS) $(bin_SCRIPTS)
endif
check-security: $(bin_PROGRAMS)
check-security: $(bin_PROGRAMS) $(bin_SCRIPTS)
if HARDEN
@echo "Checking binary security of [$(bin_PROGRAMS)]..."
@echo "Checking binary security of [$(bin_PROGRAMS) $(bin_SCRIPTS)]..."
$(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py $(bin_PROGRAMS)
$(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py --allow-no-canary $(bin_SCRIPTS)
endif
%.pb.cc %.pb.h: %.proto

View File

@ -39,7 +39,7 @@ std::string HelpMessageCli()
strUsage += HelpMessageGroup(_("Options:"));
strUsage += HelpMessageOpt("-?", _("This help message"));
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory (this path cannot use '~')"));
strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). If first extra argument is `walletpassphrase` then the first line(password) will not be echoed."));
AppendParamsHelpMessages(strUsage);
strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT));

View File

@ -633,7 +633,7 @@ public:
static_assert(equihash_parameters_acceptable(N, K));
consensus.nEquihashN = N;
consensus.nEquihashK = K;
consensus.powLimit = uint256S("0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f");
consensus.powLimit = uint256S("0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"); // if this is any larger, the for loop in GetNextWorkRequired can overflow bnTot
consensus.nPowAveragingWindow = 17;
assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow);
consensus.nPowMaxAdjustDown = 0; // Turn off adjustment down

View File

@ -2,6 +2,7 @@
#include "key.h"
#include "pubkey.h"
#include "util.h"
#include "utiltest.h"
#include "librustzcash.h"
#include <sodium.h>
@ -19,26 +20,6 @@ int main(int argc, char **argv) {
assert(sodium_init() != -1);
ECC_Start();
fs::path sapling_spend = ZC_GetParamsDir() / "sapling-spend.params";
fs::path sapling_output = ZC_GetParamsDir() / "sapling-output.params";
fs::path sprout_groth16 = ZC_GetParamsDir() / "sprout-groth16.params";
static_assert(
sizeof(fs::path::value_type) == sizeof(codeunit),
"librustzcash not configured correctly");
auto sapling_spend_str = sapling_spend.native();
auto sapling_output_str = sapling_output.native();
auto sprout_groth16_str = sprout_groth16.native();
librustzcash_init_zksnark_params(
reinterpret_cast<const codeunit*>(sapling_spend_str.c_str()),
sapling_spend_str.length(),
reinterpret_cast<const codeunit*>(sapling_output_str.c_str()),
sapling_output_str.length(),
reinterpret_cast<const codeunit*>(sprout_groth16_str.c_str()),
sprout_groth16_str.length()
);
testing::InitGoogleMock(&argc, argv);
// The "threadsafe" style is necessary for correct operation of death/exit

View File

@ -1141,6 +1141,8 @@ TEST(ChecktransactionTests, InvalidShieldedCoinbase) {
}
TEST(ChecktransactionTests, HeartwoodAcceptsShieldedCoinbase) {
LoadProofParameters();
RegtestActivateHeartwood(false, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
auto chainparams = Params();
@ -1224,6 +1226,8 @@ TEST(ChecktransactionTests, HeartwoodAcceptsShieldedCoinbase) {
// bindingSig from https://zips.z.cash/protocol/protocol.pdf#txnencoding are
// applied to coinbase transactions.
TEST(ChecktransactionTests, HeartwoodEnforcesSaplingRulesOnShieldedCoinbase) {
LoadProofParameters();
RegtestActivateHeartwood(false, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
auto chainparams = Params();

View File

@ -50,6 +50,8 @@ TEST(RecursiveDynamicUsageTests, TestTransactionJoinSplit)
TEST(RecursiveDynamicUsageTests, TestTransactionSaplingToSapling)
{
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
auto sk = libzcash::SaplingSpendingKey::random();
@ -69,6 +71,8 @@ TEST(RecursiveDynamicUsageTests, TestTransactionSaplingToSapling)
TEST(RecursiveDynamicUsageTests, TestTransactionTransparentToSapling)
{
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
CBasicKeyStore keystore;
@ -90,6 +94,8 @@ TEST(RecursiveDynamicUsageTests, TestTransactionTransparentToSapling)
TEST(RecursiveDynamicUsageTests, TestTransactionSaplingToTransparent)
{
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
CBasicKeyStore keystore;

View File

@ -11,6 +11,7 @@
#include "primitives/transaction.h"
#include "proof_verifier.h"
#include "transaction_builder.h"
#include "utiltest.h"
#include "zcash/JoinSplit.hpp"
#include "zcash/Note.hpp"
#include "zcash/NoteEncryption.hpp"
@ -309,6 +310,8 @@ void increment_note_witnesses(
TEST(Joinsplit, FullApiTest)
{
LoadProofParameters();
{
std::vector<SproutWitness> witnesses;
SproutMerkleTree tree;

View File

@ -110,6 +110,8 @@ TEST(MempoolLimitTests, WeightedTxTreeCheckSizeAfterDropping)
TEST(MempoolLimitTests, WeightedTxInfoFromTx)
{
LoadProofParameters();
// The transaction creation is based on the test:
// test_transaction_builder.cpp/TEST(TransactionBuilder, SetFee)
auto consensusParams = RegtestActivateSapling();

View File

@ -19,6 +19,8 @@
TEST(TransactionBuilder, TransparentToSapling)
{
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
CBasicKeyStore keystore;
@ -58,6 +60,8 @@ TEST(TransactionBuilder, TransparentToSapling)
}
TEST(TransactionBuilder, SaplingToSapling) {
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
auto sk = libzcash::SaplingSpendingKey::random();
@ -95,6 +99,8 @@ TEST(TransactionBuilder, SaplingToSapling) {
}
TEST(TransactionBuilder, SaplingToSprout) {
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
auto sk = libzcash::SaplingSpendingKey::random();
@ -133,6 +139,8 @@ TEST(TransactionBuilder, SaplingToSprout) {
}
TEST(TransactionBuilder, SproutToSproutAndSapling) {
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
auto sk = libzcash::SaplingSpendingKey::random();
@ -263,6 +271,8 @@ TEST(TransactionBuilder, RejectsInvalidTransparentOutput)
TEST(TransactionBuilder, FailsWithNegativeChange)
{
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
// Generate dummy Sapling address
@ -307,6 +317,8 @@ TEST(TransactionBuilder, FailsWithNegativeChange)
TEST(TransactionBuilder, ChangeOutput)
{
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
// Generate dummy Sapling address
@ -386,6 +398,8 @@ TEST(TransactionBuilder, ChangeOutput)
TEST(TransactionBuilder, SetFee)
{
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
// Generate dummy Sapling address

View File

@ -341,7 +341,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-daemon", _("Run in the background as a daemon and accept commands"));
#endif
}
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory (this path cannot use '~')"));
strUsage += HelpMessageOpt("-paramsdir=<dir>", _("Specify Zcash network parameters directory"));
strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache));
strUsage += HelpMessageOpt("-debuglogfile=<file>", strprintf(_("Specify location of debug log file: this can be an absolute path or a path relative to the data directory (default: %s)"), DEFAULT_DEBUGLOGFILE));

View File

@ -290,7 +290,7 @@ std::optional<CExtKey> CExtKey::Derive(unsigned int _nChild) const {
}
}
CExtKey CExtKey::Master(const unsigned char *seed, unsigned int nSeedLen) {
std::optional<CExtKey> CExtKey::Master(const unsigned char *seed, unsigned int nSeedLen) {
CExtKey xk;
static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'};
std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
@ -301,7 +301,11 @@ CExtKey CExtKey::Master(const unsigned char *seed, unsigned int nSeedLen) {
xk.nChild = 0;
memset(xk.vchFingerprint, 0, sizeof(xk.vchFingerprint));
return xk;
if (xk.key.IsValid()) {
return xk;
} else {
return std::nullopt;
}
}
CExtPubKey CExtKey::Neuter() const {

View File

@ -166,7 +166,7 @@ struct CExtKey {
a.key == b.key;
}
static CExtKey Master(const unsigned char* seed, unsigned int nSeedLen);
static std::optional<CExtKey> Master(const unsigned char* seed, unsigned int nSeedLen);
void Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const;
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]);

View File

@ -6,6 +6,11 @@ the `zcashd` full node.
The FFI API does not have any stability guarantees, and will change as required
by `zcashd`.
# zcashd-wallet-tool
`zcashd-wallet-tool` is a command-line tool that allows confirming to a `zcashd`
node that the emergency recovery phrase of the node's wallet has been backed up.
## License
Licensed under either of

646
src/rust/bin/wallet_tool.rs Normal file
View File

@ -0,0 +1,646 @@
use std::cmp::min;
use std::env::{self, consts::EXE_EXTENSION};
use std::ffi::OsStr;
use std::fs::File;
use std::io::{self, BufRead, Stdin, Write};
use std::iter;
use std::panic;
use std::path::{Path, PathBuf};
use std::process::{self, ChildStdin, Command, Output, Stdio};
use std::str::from_utf8;
use std::time::SystemTime;
use anyhow::{self, Context};
use backtrace::Backtrace;
use gumdrop::{Options, ParsingStyle};
use rand::{thread_rng, Rng};
use secrecy::{ExposeSecret, SecretString};
use thiserror::Error;
use time::macros::format_description;
use time::OffsetDateTime;
use tracing::debug;
use tracing_subscriber::{fmt, EnvFilter};
#[derive(Debug, Options)]
struct CliOptions {
#[options(no_short, help = "Print this help output")]
help: bool,
#[options(
no_short,
help = "Specify configuration filename, relative to the data directory (default: zcash.conf)",
meta = "FILENAME"
)]
conf: Option<String>,
#[options(
no_short,
help = "Specify data directory (this path cannot use '~')",
meta = "PATH"
)]
datadir: Option<String>,
#[options(no_short, help = "Use the test chain")]
testnet: bool,
#[options(
no_short,
help = "Send commands to node running on IPADDR (default: 127.0.0.1)",
meta = "IPADDR"
)]
rpcconnect: Option<String>,
#[options(
no_short,
help = "Connect to JSON-RPC on PORT (default: 8232 or testnet: 18232)",
meta = "PORT"
)]
rpcport: Option<u16>,
#[options(
no_short,
help = "Username for JSON-RPC connections",
meta = "USERNAME"
)]
rpcuser: Option<String>,
#[options(
no_short,
help = "Password for JSON-RPC connections",
meta = "PASSWORD"
)]
rpcpassword: Option<String>,
#[options(
no_short,
help = "Timeout in seconds during HTTP requests, or 0 for no timeout. (default: 900)",
meta = "SECONDS"
)]
rpcclienttimeout: Option<u32>,
}
impl CliOptions {
fn to_zcash_cli_options(&self) -> Vec<String> {
iter::empty::<String>()
.chain(self.conf.as_ref().map(|o| format!("-conf={}", o)))
.chain(self.datadir.as_ref().map(|o| format!("-datadir={}", o)))
.chain(if self.testnet {
Some("-testnet".into())
} else {
None
})
.chain(
self.rpcconnect
.as_ref()
.map(|o| format!("-rpcconnect={}", o)),
)
.chain(self.rpcport.as_ref().map(|o| format!("-rpcport={}", o)))
.chain(self.rpcuser.as_ref().map(|o| format!("-rpcuser={}", o)))
.chain(
self.rpcpassword
.as_ref()
.map(|o| format!("-rpcpassword={}", o)),
)
.chain(
self.rpcclienttimeout
.as_ref()
.map(|o| format!("-rpcclienttimeout={}", o)),
)
.collect()
}
}
#[derive(Debug, Error)]
enum WalletToolError {
#[error("zcash-cli executable not found")]
ZcashCliNotFound,
#[error("Unexpected response from zcash-cli or zcashd")]
UnexpectedResponse,
#[error("Could not connect to zcashd")]
ZcashdConnection,
#[error("zcashd -exportdir option not set")]
ExportDirNotSet,
#[error("Could not parse a recovery phrase from the export file")]
RecoveryPhraseNotFound,
#[error("Unexpected EOF in input")]
UnexpectedEof,
}
pub fn main() {
// Log to stderr, configured by the RUST_LOG environment variable.
fmt()
.with_writer(io::stderr)
.with_env_filter(EnvFilter::from_default_env())
.init();
// Allow either Bitcoin-style or GNU-style arguments.
let mut args = env::args();
let command = args.next().expect("argv[0] should exist");
let args: Vec<_> = args
.map(|s| {
if s.starts_with('-') && !s.starts_with("--") {
format!("-{}", s)
} else {
s
}
})
.collect();
const USAGE_NOTE: &str = concat!(
"Options can be given in GNU style (`--conf=CONF` or `--conf CONF`),\n",
"or in Bitcoin style with a single hyphen (`-conf=CONF`).\n\n",
"The environment variable RUST_LOG controls debug output, e.g.\n",
"`RUST_LOG=debug`.\n",
);
let opts = CliOptions::parse_args(&args, ParsingStyle::default()).unwrap_or_else(|e| {
eprintln!(
"{}: {}\n\nUsage: {} [OPTIONS]\n\n{}\n\n{}",
command,
e,
command,
CliOptions::usage(),
USAGE_NOTE,
);
process::exit(2);
});
if opts.help_requested() {
println!(
"Usage: {} [OPTIONS]\n\n{}\n\n{}",
command,
opts.self_usage(),
USAGE_NOTE
);
process::exit(0);
}
if let Err(e) = run(&opts) {
eprintln!("{}: {}", command, e);
process::exit(1);
}
}
fn run(opts: &CliOptions) -> anyhow::Result<()> {
let cli_options: Vec<String> = opts.to_zcash_cli_options();
println!(concat!(
"To reduce the risk of loss of funds, we're going to confirm that the\n",
"zcashd wallet is backed up reliably.\n\n",
" 👛 ➜ 🗃️ \n"
));
println!("Checking that we can connect to zcashd...");
let zcash_cli = zcash_cli_path()?;
// Pass an invalid filename, "\x01", and use the error message to distinguish
// whether zcashd is running with the -exportdir option, running without that
// option, or not running / cannot connect.
let mut cli_args = cli_options.clone();
cli_args.extend_from_slice(&["z_exportwallet".to_string(), "\x01".to_string()]);
let out = exec(&zcash_cli, &cli_args, None)?;
let cli_err: Vec<_> = from_utf8(&out.stderr)
.with_context(|| "Output from zcash-cli was not UTF-8")?
.lines()
.map(|s| s.trim_end_matches('\r'))
.collect();
debug!("stderr {:?}", cli_err);
if !cli_err.is_empty() {
if cli_err[0].starts_with("Error reading configuration file") {
println!(
"\nNo, we could not read the zcashd configuration file, expected to be at\n{:?}.",
Path::new(opts.datadir.as_ref().map_or("~/.zcash", |s| &s[..])).join(Path::new(
opts.conf.as_ref().map_or("zcash.conf", |s| &s[..])
)),
);
println!(concat!(
"If it is not at that path, please try again with the '-datadir' and/or\n",
"'-conf' options set correctly (see '--help' for details). Also make sure\n",
"that the current user has permission to read the configuration file.\n",
));
return Err(WalletToolError::ZcashdConnection.into());
}
if cli_err[0].starts_with("error: couldn't connect") {
println!(concat!(
"\nNo, we could not connect. zcashd might not be running; in that case\n",
"please start it. The '-exportdir' option should be set to the absolute\n",
"path of the directory you want to save the wallet export file to.\n\n",
"(Don't forget to restart zcashd without '-exportdir' after finishing\n",
"the backup, if running it long-term with that option is not desired\n",
"or would be a security hazard in your environment.)\n\n",
"If you believe zcashd is running, it might be using an unexpected port,\n",
"address, or authentication options for the RPC interface, for example.\n",
"In that case try to connect to it using zcash-cli, and if successful,\n",
"use the same connection options for zcashd-wallet-tool (see '--help' for\n",
"accepted options) as for zcash-cli.\n"
));
return Err(WalletToolError::ZcashdConnection.into());
}
if cli_err[0] == "error code: -28" {
println!(concat!(
"\nNo, we could not connect. zcashd seems to be initializing; please try\n",
"again once it has finished.\n",
));
return Err(WalletToolError::ZcashdConnection.into());
}
}
const REMINDER_MSG: &str = concat!(
"\n\nPlease start or restart zcashd with '-exportdir' set to the absolute\n",
"path of the directory you want to save the wallet export file to.\n",
"(Don't forget to restart zcashd without '-exportdir' after finishing\n",
"the backup, if running it long-term with that option is not desired\n",
"or would be a security hazard in your environment.)\n",
);
if cli_err.len() >= 3
&& cli_err[0] == "error code: -4"
&& cli_err[2].contains("zcashd -exportdir")
{
println!(
"\nIt looks like zcashd is running without the '-exportdir' option.{}",
REMINDER_MSG
);
return Err(WalletToolError::ExportDirNotSet.into());
}
if !(cli_err.len() >= 3
&& cli_err[0] == "error code: -4"
&& cli_err[2].starts_with("Filename is invalid"))
{
println!(
"\nThere was an unexpected response from zcash-cli or zcashd:\n> {}{}",
cli_err.join("\n> "),
REMINDER_MSG,
);
return Err(WalletToolError::UnexpectedResponse.into());
}
println!("Yes, and it is running with the '-exportdir' option as required.");
let mut stdin = io::stdin();
let base = default_filename_base();
let mut r = 0u32;
let out = loop {
let default_filename = if r != 0 {
format!("{}r{}", base, r)
} else {
base.to_string()
};
println!(
concat!(
"\nEnter the filename for the wallet export file, using only characters\n",
"a-z, A-Z and 0-9 (default '{}')."
),
default_filename
);
let response = prompt(&mut stdin)?;
let response = strip(&response);
let filename = if response.is_empty() {
r = r.saturating_add(1);
&default_filename
} else {
response
};
debug!("Using filename {:?}", filename);
let mut cli_args = cli_options.clone();
cli_args.extend_from_slice(&["z_exportwallet".to_string(), filename.to_string()]);
let out = exec(&zcash_cli, &cli_args, None)?;
let cli_err: Vec<_> = from_utf8(&out.stderr)
.with_context(|| "Output from zcash-cli was not UTF-8")?
.lines()
.map(|s| s.trim_end_matches('\r'))
.collect();
debug!("stderr {:?}", cli_err);
if cli_err.len() >= 3
&& cli_err[0] == "error code: -8"
&& cli_err[1] == "error message:"
&& cli_err[2].contains("overwrite existing")
{
println!(concat!(
"That file already exists. Please pick a unique filename in the\n",
"directory specified by the '-exportdir' option to zcashd."
));
continue;
} else {
break out;
}
};
let cli_out: Vec<_> = from_utf8(&out.stdout)
.with_context(|| "Output from zcash-cli was not UTF-8")?
.lines()
.map(|s| s.trim_end_matches('\r'))
.collect();
debug!("stdout {:?}", cli_out);
if cli_out.is_empty() {
return Err(WalletToolError::UnexpectedResponse.into());
}
let export_path = cli_out[0];
println!("\nSaved the export file to '{}'.", export_path);
println!("IMPORTANT: This file contains secrets that allow spending all wallet funds.\n");
let export_file = File::open(export_path)
.with_context(|| format!("Could not open {:?} for reading", export_path))?;
// TODO: ensure the buffer will be zeroized (#5650)
let phrase_line: Vec<_> = io::BufReader::new(export_file)
.lines()
.map(|line| line.map(SecretString::new))
.filter(|s| {
s.as_ref()
.map(|t| t.expose_secret().starts_with("# - recovery_phrase=\""))
.unwrap_or(false)
})
.collect();
let phrase = match &phrase_line[..] {
[Ok(line)] => line
.expose_secret()
.trim_start_matches("# - recovery_phrase=\"")
.trim_end_matches('"'),
_ => return Err(WalletToolError::RecoveryPhraseNotFound.into()),
};
// This panic hook allows us to make a best effort to clear the screen (and then print
// another reminder about secrets in the export file) even if a panic occurs.
let old_hook = panic::take_hook();
{
let export_path = export_path.to_string();
panic::set_hook(Box::new(move |panic_info| {
clear_and_show_cautions(&export_path);
let s = panic_info.payload().downcast_ref::<&str>().unwrap_or(&"");
eprintln!("\nPanic: {}\n{:?}", s, Backtrace::new());
}));
}
let res = (|| -> anyhow::Result<()> {
println!("The recovery phrase is:\n");
const WORDS_PER_LINE: usize = 3;
let words: Vec<_> = phrase.split(' ').collect();
let max_len = words.iter().map(|w| w.len()).max().unwrap_or(0);
for (i, word) in words.iter().enumerate() {
print!("{0:2}: {1:2$}", i + 1, word, max_len + 2);
if (i + 1) % WORDS_PER_LINE == 0 {
println!();
}
}
if words.len() % WORDS_PER_LINE != 0 {
println!();
}
println!(concat!(
"\nPlease write down this phrase (including the numbering of words) on\n",
"something durable that you will keep in a secure location.\n",
"Press Enter when finished; then the phrase will disappear and you'll be\n",
"asked to re-enter a selection of words from it."
));
let mut stdin = io::stdin();
prompt(&mut stdin)?;
// The only reliable and portable way to make sure the recovery phrase
// is no longer displayed is to clear the whole terminal (including
// scrollback, if possible). The text is only printed if clearing fails.
try_to_clear(concat!(
"\n\n\n\n\n\n\n\n\n\n\n\n",
"Please adjust the terminal window so that you can't see the\n",
"recovery phrase above. After finishing the backup, close the\n",
"terminal window or clear it"
));
println!("\nNow we're going to confirm that you backed up the recovery phrase.");
let mut rng = thread_rng();
let mut unconfirmed: Vec<usize> = (0..words.len()).collect();
for _ in 0..min(3, words.len()) {
let index: usize = rng.gen_range(0..unconfirmed.len());
let n = unconfirmed[index];
unconfirmed[index] = unconfirmed[unconfirmed.len() - 1];
unconfirmed.pop().expect("should be nonempty");
loop {
println!("\nPlease enter the {} word:", ordinal(n + 1));
let line = prompt(&mut stdin)?;
if words[n] == strip(&line) {
break;
}
println!("That's not correct, please try again.");
}
}
Ok(())
})();
panic::set_hook(old_hook);
clear_and_show_cautions(export_path);
res?;
let mut cli_args = cli_options;
cli_args.extend_from_slice(&["-stdin".to_string(), "walletconfirmbackup".to_string()]);
exec(&zcash_cli, &cli_args, Some(phrase))
.and_then(|out| {
let cli_err: Vec<_> = from_utf8(&out.stderr)
.with_context(|| "Output from zcash-cli was not UTF-8")?
.lines()
.map(|s| s.trim_end_matches('\r'))
.collect();
debug!("stderr {:?}", cli_err);
if !cli_err.is_empty() {
if cli_err[0].starts_with("error: couldn't connect") {
println!("\nWe could not connect to zcashd; it may have exited.");
return Err(WalletToolError::ZcashdConnection.into());
} else {
println!(
"\nThere was an unexpected response from zcash-cli or zcashd:\n> {}",
cli_err.join("\n> "),
);
return Err(WalletToolError::UnexpectedResponse.into());
}
}
println!(concat!(
"\nThe backup of the emergency recovery phrase for the zcashd\n",
"wallet has been successfully confirmed 🙂. You can now use the\n",
"zcashd RPC methods that create keys and addresses in that wallet.\n\n",
"If you use other wallets, their recovery information will need\n",
"to be backed up separately.\n"
));
Ok(())
})
.map_err(|e| {
println!(concat!(
"\nzcash-wallet-tool was unable to communicate to zcashd that the\n",
"backup was confirmed. This can happen if zcashd stopped, in which\n",
"case you should try again. If zcashd is still running, please seek\n",
"help or try to use 'zcash-cli -stdin walletconfirmbackup' manually.\n"
));
e
})?;
Ok(())
}
const MAX_USER_INPUT_LEN: usize = 100;
fn prompt(input: &mut Stdin) -> anyhow::Result<SecretString> {
let mut buf = String::with_capacity(MAX_USER_INPUT_LEN);
let res = input
.read_line(&mut buf)
.with_context(|| "Error reading from stdin");
// Ensure the buffer is zeroized even on error.
let line = SecretString::new(buf);
res.and_then(|_| {
if line.expose_secret().ends_with('\n') {
Ok(line)
} else {
Err(WalletToolError::UnexpectedEof.into())
}
})
}
fn strip(input: &SecretString) -> &str {
input
.expose_secret()
.trim_end_matches(|c| c == '\r' || c == '\n')
.trim()
}
fn ordinal(num: usize) -> String {
let suffix = if (11..=13).contains(&(num % 100)) {
"th"
} else {
match num % 10 {
1 => "st",
2 => "nd",
3 => "rd",
_ => "th",
}
};
format!("{}{}", num, suffix)
}
fn zcash_cli_path() -> anyhow::Result<PathBuf> {
// First look for `zcash_cli[.exe]` as a sibling of the executable.
let mut exe = env::current_exe()
.with_context(|| "Cannot determine the path of the running executable")?;
exe.set_file_name("zcash-cli");
exe.set_extension(EXE_EXTENSION);
debug!("Testing for zcash-cli at {:?}", exe);
if exe.exists() {
return Ok(exe);
}
// If not found there, look in `../src/zcash_cli[.exe]` provided
// that `src` is a sibling of `target`.
exe.pop(); // strip filename
exe.pop(); // ..
if exe.file_name() != Some(OsStr::new("target")) {
// or in `../../src/zcash_cli[.exe]` under the same proviso
exe.pop(); // ../..
if exe.file_name() != Some(OsStr::new("target")) {
return Err(WalletToolError::ZcashCliNotFound.into());
}
}
// Replace 'target/' with 'src/'.
exe.set_file_name("src");
exe.push("zcash-cli");
exe.set_extension(EXE_EXTENSION);
debug!("Testing for zcash-cli at {:?}", exe);
if !exe.exists() {
return Err(WalletToolError::ZcashCliNotFound.into());
}
Ok(exe)
}
fn exec(exe_path: &Path, args: &[String], stdin: Option<&str>) -> anyhow::Result<Output> {
debug!("Running {:?} {:?}", exe_path, args);
let mut cmd = Command::new(exe_path);
let cli = cmd.args(args);
match stdin {
None => Ok(cli.output()?),
Some(data) => {
let mut cli_process = cli
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
cli_process
.stdin
.take()
.with_context(|| "Could not open pipe to zcash-cli's stdin")
.and_then(|mut pipe: ChildStdin| -> anyhow::Result<()> {
pipe.write_all(data.as_bytes())?;
pipe.write_all("\n".as_bytes())?;
Ok(())
})
.with_context(|| "Could not write to zcash-cli's stdin")?;
Ok(cli_process.wait_with_output()?)
}
}
}
fn default_filename_base() -> String {
let format = format_description!("export[year][month][day]");
// We use the UTC date because there is a security issue in obtaining the local date
// from either `chrono` or `time`: <https://github.com/chronotope/chrono/issues/602>.
// We could use the approach in
// <https://github.com/ArekPiekarz/rusty-tax-break/commit/3aac8f0c26fd96b7365619509a544f78b59627fe>
// if it were important, but it isn't worth the dependency on `tz-rs`.
OffsetDateTime::from(SystemTime::now())
.format(&format)
.unwrap_or_else(|_| "export".to_string())
}
fn clear_and_show_cautions(export_path: &str) {
try_to_clear(concat!(
"\nCAUTION: This terminal window might be showing secrets (or have\n",
"them in the scrollback). Please copy any useful information and\n",
"then close it, or clear it"
));
println!(
concat!(
"\nIMPORTANT: Secrets that allow spending all zcashd wallet funds\n",
"have been left in the file '{}'.\n\n",
"Don't forget to restart zcashd without '-exportdir', if running it\n",
"long-term with that option is not desired or would be a security\n",
"hazard in your environment.\n\n",
"When choosing a location for the physical backup of your emergency\n",
"recovery phrase, please make sure to consider both risk of theft,\n",
"and your long-term ability to remember where it is kept."
),
export_path,
);
}
fn try_to_clear(error_blurb: &str) {
if let Err(e) = clearscreen::clear() {
eprintln!("Unable to clear screen: {}.", e);
#[cfg(target_os = "windows")]
const HOW_TO_CLEAR: &str = "using 'cls'";
#[cfg(target_os = "macos")]
const HOW_TO_CLEAR: &str = "by pressing Command + K";
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
const HOW_TO_CLEAR: &str = "using 'clear'";
println!("{} {}.", error_blurb, HOW_TO_CLEAR);
}
}

View File

@ -29,6 +29,7 @@ use std::fs::File;
use std::io::BufReader;
use std::path::{Path, PathBuf};
use std::slice;
use std::sync::Once;
use subtle::CtOption;
use tracing::{error, info};
@ -88,6 +89,7 @@ mod test_harness_ffi;
#[cfg(test)]
mod tests;
static PROOF_PARAMETERS_LOADED: Once = Once::new();
static mut SAPLING_SPEND_VK: Option<PreparedVerifyingKey<Bls12>> = None;
static mut SAPLING_OUTPUT_VK: Option<PreparedVerifyingKey<Bls12>> = None;
static mut SPROUT_GROTH16_VK: Option<PreparedVerifyingKey<Bls12>> = None;
@ -131,64 +133,66 @@ pub extern "C" fn librustzcash_init_zksnark_params(
#[cfg(target_os = "windows")] sprout_path: *const u16,
sprout_path_len: usize,
) {
#[cfg(not(target_os = "windows"))]
let (spend_path, output_path, sprout_path) = {
(
OsStr::from_bytes(unsafe { slice::from_raw_parts(spend_path, spend_path_len) }),
OsStr::from_bytes(unsafe { slice::from_raw_parts(output_path, output_path_len) }),
if sprout_path.is_null() {
None
} else {
Some(OsStr::from_bytes(unsafe {
slice::from_raw_parts(sprout_path, sprout_path_len)
}))
},
)
};
PROOF_PARAMETERS_LOADED.call_once(|| {
#[cfg(not(target_os = "windows"))]
let (spend_path, output_path, sprout_path) = {
(
OsStr::from_bytes(unsafe { slice::from_raw_parts(spend_path, spend_path_len) }),
OsStr::from_bytes(unsafe { slice::from_raw_parts(output_path, output_path_len) }),
if sprout_path.is_null() {
None
} else {
Some(OsStr::from_bytes(unsafe {
slice::from_raw_parts(sprout_path, sprout_path_len)
}))
},
)
};
#[cfg(target_os = "windows")]
let (spend_path, output_path, sprout_path) = {
(
OsString::from_wide(unsafe { slice::from_raw_parts(spend_path, spend_path_len) }),
OsString::from_wide(unsafe { slice::from_raw_parts(output_path, output_path_len) }),
if sprout_path.is_null() {
None
} else {
Some(OsString::from_wide(unsafe {
slice::from_raw_parts(sprout_path, sprout_path_len)
}))
},
)
};
#[cfg(target_os = "windows")]
let (spend_path, output_path, sprout_path) = {
(
OsString::from_wide(unsafe { slice::from_raw_parts(spend_path, spend_path_len) }),
OsString::from_wide(unsafe { slice::from_raw_parts(output_path, output_path_len) }),
if sprout_path.is_null() {
None
} else {
Some(OsString::from_wide(unsafe {
slice::from_raw_parts(sprout_path, sprout_path_len)
}))
},
)
};
let (spend_path, output_path, sprout_path) = (
Path::new(&spend_path),
Path::new(&output_path),
sprout_path.as_ref().map(Path::new),
);
let (spend_path, output_path, sprout_path) = (
Path::new(&spend_path),
Path::new(&output_path),
sprout_path.as_ref().map(Path::new),
);
// Load params
let params = load_parameters(spend_path, output_path, sprout_path);
// Load params
let params = load_parameters(spend_path, output_path, sprout_path);
// Generate Orchard parameters.
info!(target: "main", "Loading Orchard parameters");
let orchard_pk = orchard::circuit::ProvingKey::build();
let orchard_vk = orchard::circuit::VerifyingKey::build();
// Generate Orchard parameters.
info!(target: "main", "Loading Orchard parameters");
let orchard_pk = orchard::circuit::ProvingKey::build();
let orchard_vk = orchard::circuit::VerifyingKey::build();
// Caller is responsible for calling this function once, so
// these global mutations are safe.
unsafe {
SAPLING_SPEND_PARAMS = Some(params.spend_params);
SAPLING_OUTPUT_PARAMS = Some(params.output_params);
SPROUT_GROTH16_PARAMS_PATH = sprout_path.map(|p| p.to_owned());
// Caller is responsible for calling this function once, so
// these global mutations are safe.
unsafe {
SAPLING_SPEND_PARAMS = Some(params.spend_params);
SAPLING_OUTPUT_PARAMS = Some(params.output_params);
SPROUT_GROTH16_PARAMS_PATH = sprout_path.map(|p| p.to_owned());
SAPLING_SPEND_VK = Some(params.spend_vk);
SAPLING_OUTPUT_VK = Some(params.output_vk);
SPROUT_GROTH16_VK = params.sprout_vk;
SAPLING_SPEND_VK = Some(params.spend_vk);
SAPLING_OUTPUT_VK = Some(params.output_vk);
SPROUT_GROTH16_VK = params.sprout_vk;
ORCHARD_PK = Some(orchard_pk);
ORCHARD_VK = Some(orchard_vk);
}
ORCHARD_PK = Some(orchard_pk);
ORCHARD_VK = Some(orchard_vk);
}
});
}
/// Writes the "uncommitted" note value for empty leaves of the Merkle tree.

View File

@ -80,7 +80,7 @@ TestVector test2 =
void RunTest(const TestVector &test) {
std::vector<unsigned char> seed = ParseHex(test.strHexMaster);
CExtKey key = CExtKey::Master(&seed[0], seed.size());
CExtKey key = CExtKey::Master(&seed[0], seed.size()).value();
CExtPubKey pubkey = key.Neuter();
KeyIO keyIO(Params());
for (const TestDerivation &derive : test.vDerive) {

View File

@ -358,3 +358,27 @@ CWalletTx GetValidSaplingReceive(const Consensus::Params& consensusParams,
CWalletTx wtx {NULL, tx};
return wtx;
}
void LoadProofParameters() {
fs::path sapling_spend = ZC_GetParamsDir() / "sapling-spend.params";
fs::path sapling_output = ZC_GetParamsDir() / "sapling-output.params";
fs::path sprout_groth16 = ZC_GetParamsDir() / "sprout-groth16.params";
static_assert(
sizeof(fs::path::value_type) == sizeof(codeunit),
"librustzcash not configured correctly");
auto sapling_spend_str = sapling_spend.native();
auto sapling_output_str = sapling_output.native();
auto sprout_groth16_str = sprout_groth16.native();
librustzcash_init_zksnark_params(
reinterpret_cast<const codeunit*>(sapling_spend_str.c_str()),
sapling_spend_str.length(),
reinterpret_cast<const codeunit*>(sapling_output_str.c_str()),
sapling_output_str.length(),
reinterpret_cast<const codeunit*>(sprout_groth16_str.c_str()),
sprout_groth16_str.length()
);
}

View File

@ -76,4 +76,6 @@ CWalletTx GetValidSaplingReceive(const Consensus::Params& consensusParams,
const libzcash::SaplingExtendedSpendingKey &sk,
CAmount value);
void LoadProofParameters();
#endif // ZCASH_UTILTEST_H

View File

@ -398,6 +398,8 @@ TEST(WalletTests, SetSproutNoteAddrsInCWalletTx) {
}
TEST(WalletTests, SetSaplingNoteAddrsInCWalletTx) {
LoadProofParameters();
std::vector<libzcash::Zip212Enabled> zip_212_enabled = {libzcash::Zip212Enabled::BeforeZip212, libzcash::Zip212Enabled::AfterZip212};
const Consensus::Params& (*activations [])() = {RegtestActivateSapling, RegtestActivateCanopy};
void (*deactivations [])() = {RegtestDeactivateSapling, RegtestDeactivateCanopy};
@ -547,6 +549,8 @@ TEST(WalletTests, GetSproutNoteNullifier) {
}
TEST(WalletTests, FindMySaplingNotes) {
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
TestWallet wallet(Params());
LOCK(wallet.cs_wallet);
@ -676,6 +680,8 @@ TEST(WalletTests, GetConflictedSproutNotes) {
// Generate note A and spend to create note B, from which we spend to create two conflicting transactions
TEST(WalletTests, GetConflictedSaplingNotes) {
LoadProofParameters();
std::vector<libzcash::Zip212Enabled> zip_212_enabled = {libzcash::Zip212Enabled::BeforeZip212, libzcash::Zip212Enabled::AfterZip212};
const Consensus::Params& (*activations [])() = {RegtestActivateSapling, RegtestActivateCanopy};
void (*deactivations [])() = {RegtestDeactivateSapling, RegtestDeactivateCanopy};
@ -843,6 +849,8 @@ TEST(WalletTests, SproutNullifierIsSpent) {
}
TEST(WalletTests, SaplingNullifierIsSpent) {
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
TestWallet wallet(Params());
LOCK2(cs_main, wallet.cs_wallet);
@ -928,6 +936,8 @@ TEST(WalletTests, NavigateFromSproutNullifierToNote) {
}
TEST(WalletTests, NavigateFromSaplingNullifierToNote) {
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
TestWallet wallet(Params());
LOCK2(cs_main, wallet.cs_wallet);
@ -1051,6 +1061,8 @@ TEST(WalletTests, SpentSproutNoteIsFromMe) {
// Create note A, spend A to create note B, spend and verify note B is from me.
TEST(WalletTests, SpentSaplingNoteIsFromMe) {
LoadProofParameters();
std::vector<libzcash::Zip212Enabled> zip_212_enabled = {libzcash::Zip212Enabled::BeforeZip212, libzcash::Zip212Enabled::AfterZip212};
const Consensus::Params& (*activations [])() = {RegtestActivateSapling, RegtestActivateCanopy};
void (*deactivations [])() = {RegtestDeactivateSapling, RegtestDeactivateCanopy};
@ -1881,6 +1893,8 @@ TEST(WalletTests, UpdatedSproutNoteData) {
}
TEST(WalletTests, UpdatedSaplingNoteData) {
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
TestWallet wallet(Params());
LOCK2(cs_main, wallet.cs_wallet);
@ -2024,6 +2038,8 @@ TEST(WalletTests, MarkAffectedSproutTransactionsDirty) {
}
TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) {
LoadProofParameters();
auto consensusParams = RegtestActivateSapling();
TestWallet wallet(Params());
LOCK2(cs_main, wallet.cs_wallet);

View File

@ -1750,7 +1750,11 @@ UniValue walletconfirmbackup(const UniValue& params, bool fHelp)
if (fHelp || params.size() != 1)
throw runtime_error(
"walletconfirmbackup \"emergency recovery phrase\"\n"
"\nNotify the wallet that the user has backed up the emergency recovery phrase,\n"
"\nCAUTION: This is an internal method that is not intended to be called directly by\n"
"users. Please use the zcashd-wallet-tool utility (built or installed in the same directory\n"
"as zcashd) instead. In particular, this method should not be used from zcash-cli, in order\n"
"to avoid exposing the recovery phrase on the command line.\n\n"
"Notify the wallet that the user has backed up the emergency recovery phrase,\n"
"which can be obtained by making a call to z_exportwallet. The zcashd embedded wallet\n"
"requires confirmation that the emergency recovery phrase has been backed up before it\n"
"will permit new spending keys or addresses to be generated.\n"

View File

@ -3508,7 +3508,10 @@ void CWallet::GenerateNewSeed(Language language)
{
LOCK(cs_wallet);
auto seed = MnemonicSeed::Random(BIP44CoinType(), language, WALLET_MNEMONIC_ENTROPY_LENGTH);
auto legacySeed = GetLegacyHDSeed();
auto seed = legacySeed.has_value() ?
MnemonicSeed::FromLegacySeed(legacySeed.value(), BIP44CoinType(), language) :
MnemonicSeed::Random(BIP44CoinType(), language, WALLET_MNEMONIC_ENTROPY_LENGTH);
int64_t nCreationTime = GetTime();

View File

@ -9,31 +9,60 @@
using namespace libzcash;
std::optional<MnemonicSeed> MnemonicSeed::FromEntropy(const RawHDSeed& entropy, uint32_t bip44CoinType, Language language) {
const char* phrase = zip339_entropy_to_phrase(language, entropy.data(), entropy.size());
SecureString mnemonic(phrase);
zip339_free_phrase(phrase);
// The phrase returned from zip339_entropy_to_phrase should always be a
// valid UTF-8 string; this `.value()` unwrap will correctly throw a
// `std::bad_optional_access` exception if that invariant does not hold.
auto seed = MnemonicSeed::ForPhrase(language, mnemonic).value();
// Verify that the seed data is valid entropy for unified spending keys at
// account 0 and at both the public & private chain levels for account 0x7FFFFFFF.
// It is not necessary to check for a valid diversified Sapling address at
// account 0x7FFFFFFF because derivation via the legacy path can simply search
// for a valid diversifier; unlike in the unified spending key case, diversifier
// indices don't need to line up with anything.
if (ZcashdUnifiedSpendingKey::ForAccount(seed, bip44CoinType, 0).has_value() &&
transparent::AccountKey::ForAccount(seed, bip44CoinType, ZCASH_LEGACY_ACCOUNT).has_value()) {
return seed;
} else {
return std::nullopt;
}
}
MnemonicSeed MnemonicSeed::Random(uint32_t bip44CoinType, Language language, size_t entropyLen)
{
assert(entropyLen >= 32);
while (true) { // loop until we find usable entropy
RawHDSeed entropy(entropyLen, 0);
GetRandBytes(entropy.data(), entropyLen);
const char* phrase = zip339_entropy_to_phrase(language, entropy.data(), entropyLen);
SecureString mnemonic(phrase);
zip339_free_phrase(phrase);
// The phrase returned from zip339_entropy_to_phrase should always be a
// valid UTF-8 string; this `.value()` unwrap will correctly throw a
// `std::bad_optional_access` exception if that invariant does not hold.
auto seed = MnemonicSeed::ForPhrase(language, mnemonic).value();
// Verify that the seed data is valid entropy for unified spending keys at
// account 0 and at both the public & private chain levels for account 0x7FFFFFFF.
// It is not necessary to check for a valid diversified Sapling address at
// account 0x7FFFFFFF because derivation via the legacy path can simply search
// for a valid diversifier; unlike in the unified spending key case, diversifier
// indices don't need to line up with anything.
if (ZcashdUnifiedSpendingKey::ForAccount(seed, bip44CoinType, 0).has_value() &&
transparent::AccountKey::ForAccount(seed, bip44CoinType, ZCASH_LEGACY_ACCOUNT).has_value()) {
return seed;
auto seed = MnemonicSeed::FromEntropy(entropy, bip44CoinType, language);
if (seed.has_value()) {
return seed.value();
}
}
}
MnemonicSeed MnemonicSeed::FromLegacySeed(const HDSeed& legacySeed, uint32_t bip44CoinType, Language language)
{
auto rawSeed = legacySeed.RawSeed();
if (rawSeed.size() != 32) {
throw std::runtime_error("Mnemonic seed derivation is only supported for 32-byte legacy seeds.");
}
for (int nonce = 0; nonce < 256; nonce++) {
auto seed = MnemonicSeed::FromEntropy(rawSeed, bip44CoinType, language);
if (seed.has_value()) {
return seed.value();
} else {
rawSeed[0]++;
}
}
throw std::runtime_error("Failed to find a valid mnemonic seed that could be derived from the legacy seed.");
}

View File

@ -37,6 +37,10 @@ public:
*/
static MnemonicSeed Random(uint32_t bip44CoinType, Language language = English, size_t entropyLen = 32);
static MnemonicSeed FromLegacySeed(const HDSeed& legacySeed, uint32_t bip44CoinType, Language language = English);
static std::optional<MnemonicSeed> FromEntropy(const RawHDSeed& entropy, uint32_t bip44CoinType, Language language = English);
static std::string LanguageName(Language language) {
switch (language) {
case English:

View File

@ -74,10 +74,11 @@ std::optional<AccountKey> AccountKey::ForAccount(
AccountId accountId) {
auto rawSeed = seed.RawSeed();
auto m = CExtKey::Master(rawSeed.data(), rawSeed.size());
if (!m.has_value()) return std::nullopt;
// We use a fixed keypath scheme of m/44'/coin_type'/account'
// Derive m/44'
auto m_44h = m.Derive(44 | HARDENED_KEY_LIMIT);
auto m_44h = m.value().Derive(44 | HARDENED_KEY_LIMIT);
if (!m_44h.has_value()) return std::nullopt;
// Derive m/44'/coin_type'

View File

@ -80,6 +80,7 @@ clean_dirs __pycache__
clean_exe src/bench/bench_bitcoin
clean_exe src/zcash-cli
clean_exe src/zcashd
clean_exe src/zcashd-wallet-tool
clean_exe src/zcash-gtest
clean_exe src/zcash-tx
clean_exe src/test/test_bitcoin