diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d0bf145b..db8a8eba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,7 @@ jobs: run: > cargo test --workspace + --all-features - name: Verify working directory is clean run: git diff --exit-code @@ -83,6 +84,7 @@ jobs: run: > cargo test --workspace + --all-features - name: Verify working directory is clean run: git diff --exit-code @@ -109,6 +111,7 @@ jobs: cargo build --workspace --all-targets + --all-features - name: Verify working directory is clean (excluding lockfile) run: git diff --exit-code ':!Cargo.lock' diff --git a/Cargo.lock b/Cargo.lock index 2f428366..f26f4df0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -242,6 +242,18 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -417,6 +429,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.21.7" @@ -444,6 +462,122 @@ dependencies = [ "serde", ] +[[package]] +name = "bc-components" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6077faa2e784ba4d60340f87d210c9c88bffd61384e33fb212895f0a912eb775" +dependencies = [ + "anyhow", + "bc-crypto", + "bc-rand", + "bc-tags", + "bc-ur", + "dcbor", + "hex", + "miniz_oxide 0.7.4", + "pqcrypto-mldsa", + "pqcrypto-mlkem", + "pqcrypto-traits", + "rand_core 0.6.4", + "ssh-agent-client-rs", + "ssh-key", + "sskr", + "url", + "zeroize", +] + +[[package]] +name = "bc-crypto" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8647a24a5edf0ed9ea01f5258e7d092465c6f9b0508b32166c4dccbe1c291c9c" +dependencies = [ + "anyhow", + "argon2", + "bc-rand", + "chacha20poly1305", + "crc32fast", + "ed25519-dalek", + "hex", + "hkdf", + "hmac 0.12.1", + "pbkdf2", + "rand 0.8.5", + "scrypt", + "secp256k1 0.30.0", + "sha2 0.10.8", + "thiserror 1.0.69", + "x25519-dalek", +] + +[[package]] +name = "bc-envelope" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a405ac045fd75c54b31fca2df85c38b73b0824bce893faa77154dbefb47dbad" +dependencies = [ + "anyhow", + "bc-components", + "bc-crypto", + "bc-rand", + "bc-ur", + "bytes", + "dcbor", + "hex", + "itertools 0.11.0", + "known-values", + "paste", + "ssh-key", + "thiserror 1.0.69", +] + +[[package]] +name = "bc-rand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd5f55d05b06624b5b3c2aa7d7e1c6ab06a41e186b66fef68cfd2ad08149ba1" +dependencies = [ + "getrandom 0.2.15", + "lazy_static", + "num-traits 0.2.19", + "rand 0.8.5", + "rand_core 0.6.4", + "rand_xoshiro", +] + +[[package]] +name = "bc-shamir" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b6bdb46e87c24147d929cd78a93316ac118284825babeec47becd00395ee9eb" +dependencies = [ + "bc-crypto", + "bc-rand", + "thiserror 1.0.69", +] + +[[package]] +name = "bc-tags" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f849b1705d71e8e1f744f47e66fdff09191b0c4bc06df5477855f155294407b5" +dependencies = [ + "dcbor", + "paste", +] + +[[package]] +name = "bc-ur" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66cbdfcce5175fc68c809990927653535d9b7c28efc9bc301583867a2c2ffde" +dependencies = [ + "dcbor", + "thiserror 1.0.69", + "ur", +] + [[package]] name = "bech32" version = "0.9.1" @@ -540,6 +674,22 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bip32" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db40d3dfbeab4e031d78c844642fa0caa0b0db11ce1607ac9d2986dff1405c69" +dependencies = [ + "bs58", + "hmac 0.12.1", + "rand_core 0.6.4", + "ripemd 0.1.3", + "secp256k1 0.27.0", + "sha2 0.10.8", + "subtle", + "zeroize", +] + [[package]] name = "bip32" version = "0.6.0-pre.1" @@ -550,12 +700,43 @@ dependencies = [ "hmac 0.13.0-pre.4", "rand_core 0.6.4", "ripemd 0.2.0-pre.4", - "secp256k1", + "secp256k1 0.29.1", "sha2 0.11.0-pre.4", "subtle", "zeroize", ] +[[package]] +name = "bitcoin-io" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" + +[[package]] +name = "bitcoin-private" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" + +[[package]] +name = "bitcoin_hashes" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501" +dependencies = [ + "bitcoin-private", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -658,6 +839,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "bridgetree" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef977c7f8e75aa81fc589064c121ab8d32448b7939d34d58df479aa93e65ea5" +dependencies = [ + "incrementalmerkletree 0.7.1", +] + [[package]] name = "bs58" version = "0.5.1" @@ -1072,6 +1262,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.4.2" @@ -1083,9 +1288,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -1121,6 +1326,18 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -1128,6 +1345,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] @@ -1226,6 +1444,21 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "dcbor" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9ee71342cca725c77c9fc7cb2e043ab41b130f36d53826b31cc6053a2899bf1" +dependencies = [ + "anyhow", + "chrono", + "half", + "hex", + "paste", + "thiserror 1.0.69", + "unicode-normalization", +] + [[package]] name = "deadpool" version = "0.12.2" @@ -1335,6 +1568,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common 0.1.6", "subtle", ] @@ -1382,6 +1616,12 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "doctest-file" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" + [[package]] name = "document-features" version = "0.2.11" @@ -1417,6 +1657,22 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "dsa" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" +dependencies = [ + "digest 0.10.7", + "num-bigint-dig", + "num-traits 0.2.19", + "pkcs8", + "rfc6979", + "sha2 0.10.8", + "signature", + "zeroize", +] + [[package]] name = "dunce" version = "1.0.5" @@ -1429,6 +1685,20 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + [[package]] name = "ed25519" version = "2.2.3" @@ -1440,6 +1710,21 @@ dependencies = [ "signature", ] +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core 0.6.4", + "serde", + "sha2 0.10.8", + "subtle", + "zeroize", +] + [[package]] name = "ed25519-zebra" version = "4.0.3" @@ -1462,6 +1747,25 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "encode_unicode" version = "1.0.0" @@ -1496,7 +1800,7 @@ dependencies = [ [[package]] name = "equihash" version = "0.2.2" -source = "git+https://github.com/zcash/librustzcash.git?rev=8be259c579762f1b0f569453a20c0d0dbeae6c07#8be259c579762f1b0f569453a20c0d0dbeae6c07" +source = "git+https://github.com/zcash/librustzcash.git?rev=10caf455e3f52744b5392af226a408b05721f70f#10caf455e3f52744b5392af226a408b05721f70f" dependencies = [ "blake2b_simd", "core2 0.3.3", @@ -1532,7 +1836,16 @@ dependencies = [ [[package]] name = "f4jumble" version = "0.1.1" -source = "git+https://github.com/zcash/librustzcash.git?rev=8be259c579762f1b0f569453a20c0d0dbeae6c07#8be259c579762f1b0f569453a20c0d0dbeae6c07" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d42773cb15447644d170be20231a3268600e0c4cea8987d013b93ac973d3cf7" +dependencies = [ + "blake2b_simd", +] + +[[package]] +name = "f4jumble" +version = "0.1.1" +source = "git+https://github.com/zcash/librustzcash.git?rev=10caf455e3f52744b5392af226a408b05721f70f#10caf455e3f52744b5392af226a408b05721f70f" dependencies = [ "blake2b_simd", ] @@ -1822,6 +2135,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -1930,6 +2244,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "halo2_gadgets" version = "0.3.1" @@ -2055,6 +2379,15 @@ dependencies = [ "serde", ] +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex-literal" version = "0.4.1" @@ -2512,6 +2845,15 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "incrementalmerkletree" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216c71634ac6f6ed13c2102d64354c0a04dcbdc30e31692c5972d3974d8b6d97" +dependencies = [ + "either", +] + [[package]] name = "incrementalmerkletree" version = "0.8.2" @@ -2558,6 +2900,19 @@ dependencies = [ "generic-array", ] +[[package]] +name = "interprocess" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d941b405bd2322993887859a8ee6ac9134945a24ec5ec763a8a962fc64dfec2d" +dependencies = [ + "doctest-file", + "libc", + "recvmsg", + "widestring", + "windows-sys 0.52.0", +] + [[package]] name = "intl-memoizer" version = "0.5.2" @@ -2612,6 +2967,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.1" @@ -2802,6 +3166,17 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "known-values" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0b88b8b600c157b67d764cc28b56d90dc47b1a18ea8844d99e9773b107cba" +dependencies = [ + "bc-components", + "dcbor", + "paste", +] + [[package]] name = "lazy-regex" version = "3.4.1" @@ -3019,11 +3394,11 @@ dependencies = [ [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] [[package]] @@ -3076,6 +3451,26 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minicbor" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7005aaf257a59ff4de471a9d5538ec868a21586534fff7f85dd97d4043a6139" +dependencies = [ + "minicbor-derive", +] + +[[package]] +name = "minicbor-derive" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1154809406efdb7982841adb6311b3d095b46f78342dd646736122fe6b19e267" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -3145,6 +3540,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonempty" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" + [[package]] name = "nonempty" version = "0.11.0" @@ -3159,12 +3560,11 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" dependencies = [ - "overload", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -3177,6 +3577,23 @@ dependencies = [ "num-traits 0.2.19", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits 0.2.19", + "rand 0.8.5", + "smallvec", + "zeroize", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -3192,6 +3609,17 @@ dependencies = [ "num-traits 0.2.19", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits 0.2.19", +] + [[package]] name = "num-traits" version = "0.1.43" @@ -3297,6 +3725,41 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "orchard" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f4cf75baf85bbd6f15eb919b7e70afdc4a311eef0a3e8a053e65542fe2b58e" +dependencies = [ + "aes", + "bitvec", + "blake2b_simd", + "core2 0.3.3", + "ff", + "fpe", + "getset", + "group", + "halo2_gadgets", + "halo2_poseidon", + "halo2_proofs", + "hex", + "incrementalmerkletree 0.7.1", + "lazy_static", + "memuse", + "nonempty 0.7.0", + "pasta_curves", + "rand 0.8.5", + "reddsa", + "serde", + "sinsemilla", + "subtle", + "tracing", + "visibility", + "zcash_note_encryption", + "zcash_spec 0.1.2", + "zip32 0.1.3", +] + [[package]] name = "orchard" version = "0.11.0" @@ -3315,10 +3778,10 @@ dependencies = [ "halo2_poseidon", "halo2_proofs", "hex", - "incrementalmerkletree", + "incrementalmerkletree 0.8.2", "lazy_static", "memuse", - "nonempty", + "nonempty 0.11.0", "pasta_curves", "rand 0.8.5", "reddsa", @@ -3328,8 +3791,8 @@ dependencies = [ "tracing", "visibility", "zcash_note_encryption", - "zcash_spec", - "zip32", + "zcash_spec 0.2.1", + "zip32 0.2.0", ] [[package]] @@ -3352,18 +3815,50 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "owo-colors" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.8", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.8", +] + +[[package]] +name = "p521" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primeorder", + "rand_core 0.6.4", + "sha2 0.10.8", +] + [[package]] name = "pairing" version = "0.23.0" @@ -3450,6 +3945,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pbkdf2" version = "0.12.2" @@ -3461,6 +3962,15 @@ dependencies = [ "password-hash", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -3565,6 +4075,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -3613,6 +4134,51 @@ dependencies = [ "zerocopy 0.8.24", ] +[[package]] +name = "pqcrypto-internals" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a326caf27cbf2ac291ca7fd56300497ba9e76a8cc6a7d95b7a18b57f22b61d" +dependencies = [ + "cc", + "dunce", + "getrandom 0.3.2", + "libc", +] + +[[package]] +name = "pqcrypto-mldsa" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f812cd126a2582599478a434fea75937b4b05d234c64a49e0cea129e130528" +dependencies = [ + "cc", + "glob", + "libc", + "paste", + "pqcrypto-internals", + "pqcrypto-traits", +] + +[[package]] +name = "pqcrypto-mlkem" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb14d207f3749e8a59a026c22ceaa72d70fff931cfbf4c8d9b08f3fc56dc6e60" +dependencies = [ + "cc", + "glob", + "libc", + "pqcrypto-internals", + "pqcrypto-traits", +] + +[[package]] +name = "pqcrypto-traits" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e851c7654eed9e68d7d27164c454961a616cf8c203d500607ef22c737b51bb" + [[package]] name = "prettyplease" version = "0.2.32" @@ -3623,6 +4189,15 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "primitive-types" version = "0.12.2" @@ -3962,6 +4537,15 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "rayon" version = "1.10.0" @@ -3982,6 +4566,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "recvmsg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" + [[package]] name = "reddsa" version = "0.5.1" @@ -4000,6 +4590,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "redjubjub" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a60db2c3bc9c6fd1e8631fee75abc008841d27144be744951d6b9b75f9b569c" +dependencies = [ + "rand_core 0.6.4", + "reddsa", + "serde", + "thiserror 1.0.69", + "zeroize", +] + [[package]] name = "redjubjub" version = "0.8.0" @@ -4061,17 +4664,8 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] @@ -4082,15 +4676,9 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" version = "0.8.5" @@ -4142,6 +4730,16 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle", +] + [[package]] name = "ring" version = "0.17.14" @@ -4216,6 +4814,27 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rsa" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +dependencies = [ + "const-oid", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-traits 0.2.19", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "sha2 0.10.8", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rtoolbox" version = "0.0.2" @@ -4421,6 +5040,38 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "sapling-crypto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfff8cfce16aeb38da50b8e2ed33c9018f30552beff2210c266662a021b17f38" +dependencies = [ + "aes", + "bellman", + "bitvec", + "blake2b_simd", + "blake2s_simd", + "bls12_381", + "byteorder", + "document-features", + "ff", + "fpe", + "group", + "hex", + "incrementalmerkletree 0.7.1", + "jubjub", + "lazy_static", + "memuse", + "rand 0.8.5", + "rand_core 0.6.4", + "redjubjub 0.7.0", + "subtle", + "tracing", + "zcash_note_encryption", + "zcash_spec 0.1.2", + "zip32 0.1.3", +] + [[package]] name = "sapling-crypto" version = "0.5.0" @@ -4440,18 +5091,18 @@ dependencies = [ "getset", "group", "hex", - "incrementalmerkletree", + "incrementalmerkletree 0.8.2", "jubjub", "lazy_static", "memuse", "rand 0.8.5", "rand_core 0.6.4", - "redjubjub", + "redjubjub 0.8.0", "subtle", "tracing", "zcash_note_encryption", - "zcash_spec", - "zip32", + "zcash_spec 0.2.1", + "zip32 0.2.0", ] [[package]] @@ -4520,16 +5171,59 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" +dependencies = [ + "secp256k1-sys 0.8.2", +] + [[package]] name = "secp256k1" version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ - "secp256k1-sys", + "secp256k1-sys 0.10.1", "serde", ] +[[package]] +name = "secp256k1" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +dependencies = [ + "bitcoin_hashes 0.14.0", + "rand 0.8.5", + "secp256k1-sys 0.10.1", +] + +[[package]] +name = "secp256k1-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4473013577ec77b4ee3668179ef1186df3146e2cf2d927bd200974c6fe60fd99" +dependencies = [ + "cc", +] + [[package]] name = "secp256k1-sys" version = "0.10.1" @@ -4760,7 +5454,7 @@ checksum = "637e95dcd06bc1bb3f86ed9db1e1832a70125f32daae071ef37dcb7701b7d4fe" dependencies = [ "bitflags 2.9.0", "either", - "incrementalmerkletree", + "incrementalmerkletree 0.8.2", "tracing", ] @@ -4785,6 +5479,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ + "digest 0.10.7", "rand_core 0.6.4", ] @@ -4899,6 +5594,76 @@ dependencies = [ "der", ] +[[package]] +name = "ssh-agent-client-rs" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc3c4dd567de6556169a17ce106042cac86a5f5fe7ec9c89c924b57c47707b73" +dependencies = [ + "bytes", + "interprocess", + "signature", + "ssh-encoding", + "ssh-key", + "thiserror 2.0.12", +] + +[[package]] +name = "ssh-cipher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" +dependencies = [ + "cipher", + "ssh-encoding", +] + +[[package]] +name = "ssh-encoding" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" +dependencies = [ + "base64ct", + "pem-rfc7468", + "sha2 0.10.8", +] + +[[package]] +name = "ssh-key" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b86f5297f0f04d08cabaa0f6bff7cb6aec4d9c3b49d87990d63da9d9156a8c3" +dependencies = [ + "dsa", + "ed25519-dalek", + "num-bigint-dig", + "p256", + "p384", + "p521", + "rand_core 0.6.4", + "rsa", + "sec1", + "sha1", + "sha2 0.10.8", + "signature", + "ssh-cipher", + "ssh-encoding", + "subtle", + "zeroize", +] + +[[package]] +name = "sskr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ac11f99ef5cbbbc90a1eeeb4425d53af1e3ef9ff279102c009f643be2058e25" +dependencies = [ + "bc-rand", + "bc-shamir", + "thiserror 1.0.69", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -5492,14 +6257,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "sharded-slab", "smallvec", "thread_local", @@ -5659,6 +6424,19 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ur" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "010f24a953db5d22d0010969ca3bbf40b3857b89f47c0f7be0da4c2d7ded0760" +dependencies = [ + "bitcoin_hashes 0.12.0", + "crc", + "minicbor", + "phf", + "rand_xoshiro", +] + [[package]] name = "url" version = "2.5.4" @@ -5977,6 +6755,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "widestring" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" + [[package]] name = "winapi" version = "0.3.9" @@ -5999,7 +6783,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -6415,7 +7199,7 @@ dependencies = [ "tracing", "url", "zaino-proto", - "zcash_protocol", + "zcash_protocol 0.6.1", "zebra-chain", "zebra-rpc", "zebra-state", @@ -6456,7 +7240,7 @@ dependencies = [ "lazy-regex", "lmdb", "lmdb-sys", - "nonempty", + "nonempty 0.11.0", "primitive-types 0.13.1", "prost", "reqwest", @@ -6473,10 +7257,10 @@ dependencies = [ "whoami", "zaino-fetch", "zaino-proto", - "zcash_address", - "zcash_keys", - "zcash_primitives", - "zcash_protocol", + "zcash_address 0.9.0", + "zcash_keys 0.10.1", + "zcash_primitives 0.24.1", + "zcash_protocol 0.6.1", "zcash_transparent", "zebra-chain", "zebra-rpc", @@ -6490,6 +7274,7 @@ dependencies = [ "abscissa_core", "abscissa_tokio", "age", + "anyhow", "async-trait", "bip0039", "clap", @@ -6509,13 +7294,13 @@ dependencies = [ "hyper", "i18n-embed", "i18n-embed-fl", - "incrementalmerkletree", + "incrementalmerkletree 0.8.2", "jsonrpsee", "jsonrpsee-http-client", "known-folders", "nix", "once_cell", - "orchard", + "orchard 0.11.0", "phf", "quote", "rand 0.8.5", @@ -6524,10 +7309,11 @@ dependencies = [ "rusqlite", "rust-embed", "rust_decimal", - "sapling-crypto", + "sapling-crypto 0.5.0", "schemars", "schemerz", "schemerz-rusqlite", + "secp256k1 0.29.1", "secrecy 0.8.0", "serde", "serde_json", @@ -6549,43 +7335,59 @@ dependencies = [ "zaino-fetch", "zaino-proto", "zaino-state", - "zcash_address", + "zcash_address 0.9.0", "zcash_client_backend", "zcash_client_sqlite", - "zcash_keys", + "zcash_keys 0.10.1", "zcash_note_encryption", - "zcash_primitives", + "zcash_primitives 0.24.1", "zcash_proofs", - "zcash_protocol", + "zcash_protocol 0.6.1", "zcash_transparent", "zebra-chain", "zebra-rpc", "zebra-state", - "zip32", + "zewif", + "zewif-zcashd", + "zip32 0.2.0", +] + +[[package]] +name = "zcash_address" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32b380113014b136aec579ea1c07fef747a818b9ac97d91daa0ec3b7a642bc0" +dependencies = [ + "bech32 0.11.0", + "bs58", + "core2 0.3.3", + "f4jumble 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zcash_encoding 0.2.2", + "zcash_protocol 0.4.3", ] [[package]] name = "zcash_address" version = "0.9.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=8be259c579762f1b0f569453a20c0d0dbeae6c07#8be259c579762f1b0f569453a20c0d0dbeae6c07" +source = "git+https://github.com/zcash/librustzcash.git?rev=10caf455e3f52744b5392af226a408b05721f70f#10caf455e3f52744b5392af226a408b05721f70f" dependencies = [ "bech32 0.11.0", "bs58", "core2 0.3.3", - "f4jumble", - "zcash_encoding", - "zcash_protocol", + "f4jumble 0.1.1 (git+https://github.com/zcash/librustzcash.git?rev=10caf455e3f52744b5392af226a408b05721f70f)", + "zcash_encoding 0.3.0", + "zcash_protocol 0.6.1", ] [[package]] name = "zcash_client_backend" version = "0.19.1" -source = "git+https://github.com/zcash/librustzcash.git?rev=8be259c579762f1b0f569453a20c0d0dbeae6c07#8be259c579762f1b0f569453a20c0d0dbeae6c07" +source = "git+https://github.com/zcash/librustzcash.git?rev=10caf455e3f52744b5392af226a408b05721f70f#10caf455e3f52744b5392af226a408b05721f70f" dependencies = [ "async-trait", "base64 0.22.1", "bech32 0.11.0", - "bip32", + "bip32 0.6.0-pre.1", "bls12_381", "bs58", "byteorder", @@ -6596,16 +7398,17 @@ dependencies = [ "group", "hex", "hyper-util", - "incrementalmerkletree", + "incrementalmerkletree 0.8.2", "memuse", - "nonempty", - "orchard", + "nonempty 0.11.0", + "orchard 0.11.0", "pasta_curves", "percent-encoding", "prost", "rand_core 0.6.4", "rayon", - "sapling-crypto", + "sapling-crypto 0.5.0", + "secp256k1 0.29.1", "secrecy 0.8.0", "shardtree", "subtle", @@ -6615,42 +7418,44 @@ dependencies = [ "tonic-build 0.13.0", "tracing", "which 7.0.2", - "zcash_address", - "zcash_encoding", - "zcash_keys", + "zcash_address 0.9.0", + "zcash_encoding 0.3.0", + "zcash_keys 0.10.1", "zcash_note_encryption", - "zcash_primitives", - "zcash_protocol", + "zcash_primitives 0.24.1", + "zcash_protocol 0.6.1", "zcash_transparent", - "zip32", + "zip32 0.2.0", "zip321", ] [[package]] name = "zcash_client_sqlite" version = "0.17.3" -source = "git+https://github.com/zcash/librustzcash.git?rev=8be259c579762f1b0f569453a20c0d0dbeae6c07#8be259c579762f1b0f569453a20c0d0dbeae6c07" +source = "git+https://github.com/zcash/librustzcash.git?rev=10caf455e3f52744b5392af226a408b05721f70f#10caf455e3f52744b5392af226a408b05721f70f" dependencies = [ - "bip32", + "bip32 0.6.0-pre.1", "bitflags 2.9.0", "bs58", "byteorder", "document-features", "group", - "incrementalmerkletree", + "hex", + "incrementalmerkletree 0.8.2", "jubjub", "maybe-rayon", - "nonempty", - "orchard", + "nonempty 0.11.0", + "orchard 0.11.0", "prost", "rand 0.8.5", "rand_core 0.6.4", "rand_distr", "regex", "rusqlite", - "sapling-crypto", + "sapling-crypto 0.5.0", "schemerz", "schemerz-rusqlite", + "secp256k1 0.29.1", "secrecy 0.8.0", "shardtree", "static_assertions", @@ -6658,61 +7463,104 @@ dependencies = [ "time", "tracing", "uuid", - "zcash_address", + "zcash_address 0.9.0", "zcash_client_backend", - "zcash_encoding", - "zcash_keys", - "zcash_primitives", - "zcash_protocol", + "zcash_encoding 0.3.0", + "zcash_keys 0.10.1", + "zcash_primitives 0.24.1", + "zcash_protocol 0.6.1", "zcash_transparent", - "zip32", + "zip32 0.2.0", +] + +[[package]] +name = "zcash_encoding" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3654116ae23ab67dd1f849b01f8821a8a156f884807ff665eac109bf28306c4d" +dependencies = [ + "core2 0.3.3", + "nonempty 0.7.0", ] [[package]] name = "zcash_encoding" version = "0.3.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=8be259c579762f1b0f569453a20c0d0dbeae6c07#8be259c579762f1b0f569453a20c0d0dbeae6c07" +source = "git+https://github.com/zcash/librustzcash.git?rev=10caf455e3f52744b5392af226a408b05721f70f#10caf455e3f52744b5392af226a408b05721f70f" dependencies = [ "core2 0.3.3", - "nonempty", + "nonempty 0.11.0", ] [[package]] name = "zcash_history" version = "0.4.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=8be259c579762f1b0f569453a20c0d0dbeae6c07#8be259c579762f1b0f569453a20c0d0dbeae6c07" +source = "git+https://github.com/zcash/librustzcash.git?rev=10caf455e3f52744b5392af226a408b05721f70f#10caf455e3f52744b5392af226a408b05721f70f" dependencies = [ "blake2b_simd", "byteorder", "primitive-types 0.12.2", ] +[[package]] +name = "zcash_keys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca7dfe792eb5b7d13bdcf78575116d968a5b66ffaa9a66e8673071d47d25e540" +dependencies = [ + "bech32 0.9.1", + "bip32 0.5.3", + "blake2b_simd", + "bls12_381", + "bs58", + "document-features", + "group", + "memuse", + "nonempty 0.7.0", + "orchard 0.10.2", + "rand_core 0.6.4", + "sapling-crypto 0.3.0", + "secrecy 0.8.0", + "subtle", + "tracing", + "zcash_address 0.6.3", + "zcash_encoding 0.2.2", + "zcash_primitives 0.19.1", + "zcash_protocol 0.4.3", + "zip32 0.1.3", +] + [[package]] name = "zcash_keys" version = "0.10.1" -source = "git+https://github.com/zcash/librustzcash.git?rev=8be259c579762f1b0f569453a20c0d0dbeae6c07#8be259c579762f1b0f569453a20c0d0dbeae6c07" +source = "git+https://github.com/zcash/librustzcash.git?rev=10caf455e3f52744b5392af226a408b05721f70f#10caf455e3f52744b5392af226a408b05721f70f" dependencies = [ "bech32 0.11.0", - "bip32", + "bip0039", + "bip32 0.6.0-pre.1", "blake2b_simd", "bls12_381", "bs58", + "byteorder", "core2 0.3.3", "document-features", "group", "memuse", - "nonempty", - "orchard", + "nonempty 0.11.0", + "orchard 0.11.0", "rand_core 0.6.4", - "sapling-crypto", + "regex", + "sapling-crypto 0.5.0", + "secp256k1 0.29.1", "secrecy 0.8.0", "subtle", "tracing", - "zcash_address", - "zcash_encoding", - "zcash_protocol", + "zcash_address 0.9.0", + "zcash_encoding 0.3.0", + "zcash_protocol 0.6.1", "zcash_transparent", - "zip32", + "zeroize", + "zip32 0.2.0", ] [[package]] @@ -6730,10 +7578,49 @@ dependencies = [ [[package]] name = "zcash_primitives" -version = "0.24.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=8be259c579762f1b0f569453a20c0d0dbeae6c07#8be259c579762f1b0f569453a20c0d0dbeae6c07" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d66e2f114bf81094bc5bf74860048ea1ae3911c424567cb9cb4d0cbb71ec7a" +dependencies = [ + "aes", + "bip32 0.5.3", + "blake2b_simd", + "bs58", + "byteorder", + "document-features", + "equihash", + "ff", + "fpe", + "group", + "hex", + "incrementalmerkletree 0.7.1", + "jubjub", + "memuse", + "nonempty 0.7.0", + "orchard 0.10.2", + "rand 0.8.5", + "rand_core 0.6.4", + "redjubjub 0.7.0", + "ripemd 0.1.3", + "sapling-crypto 0.3.0", + "secp256k1 0.27.0", + "sha2 0.10.8", + "subtle", + "tracing", + "zcash_address 0.6.3", + "zcash_encoding 0.2.2", + "zcash_note_encryption", + "zcash_protocol 0.4.3", + "zcash_spec 0.1.2", + "zip32 0.1.3", +] + +[[package]] +name = "zcash_primitives" +version = "0.24.1" +source = "git+https://github.com/zcash/librustzcash.git?rev=10caf455e3f52744b5392af226a408b05721f70f#10caf455e3f52744b5392af226a408b05721f70f" dependencies = [ - "bip32", + "bip32 0.6.0-pre.1", "blake2b_simd", "block-buffer 0.11.0-rc.3", "bs58", @@ -6746,33 +7633,33 @@ dependencies = [ "getset", "group", "hex", - "incrementalmerkletree", + "incrementalmerkletree 0.8.2", "jubjub", "memuse", - "nonempty", - "orchard", + "nonempty 0.11.0", + "orchard 0.11.0", "rand 0.8.5", "rand_core 0.6.4", - "redjubjub", + "redjubjub 0.8.0", "ripemd 0.1.3", - "sapling-crypto", - "secp256k1", + "sapling-crypto 0.5.0", + "secp256k1 0.29.1", "sha2 0.10.8", "subtle", "tracing", - "zcash_address", - "zcash_encoding", + "zcash_address 0.9.0", + "zcash_encoding 0.3.0", "zcash_note_encryption", - "zcash_protocol", - "zcash_spec", + "zcash_protocol 0.6.1", + "zcash_spec 0.2.1", "zcash_transparent", - "zip32", + "zip32 0.2.0", ] [[package]] name = "zcash_proofs" version = "0.24.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=8be259c579762f1b0f569453a20c0d0dbeae6c07#8be259c579762f1b0f569453a20c0d0dbeae6c07" +source = "git+https://github.com/zcash/librustzcash.git?rev=10caf455e3f52744b5392af226a408b05721f70f#10caf455e3f52744b5392af226a408b05721f70f" dependencies = [ "bellman", "blake2b_simd", @@ -6784,18 +7671,30 @@ dependencies = [ "known-folders", "lazy_static", "rand_core 0.6.4", - "redjubjub", - "sapling-crypto", + "redjubjub 0.8.0", + "sapling-crypto 0.5.0", "tracing", "wagyu-zcash-parameters", "xdg", - "zcash_primitives", + "zcash_primitives 0.24.1", +] + +[[package]] +name = "zcash_protocol" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cb36b15b5a1be70b30c32ce40372dead6561df8a467e297f96b892873a63a2" +dependencies = [ + "core2 0.3.3", + "document-features", + "hex", + "memuse", ] [[package]] name = "zcash_protocol" version = "0.6.1" -source = "git+https://github.com/zcash/librustzcash.git?rev=8be259c579762f1b0f569453a20c0d0dbeae6c07#8be259c579762f1b0f569453a20c0d0dbeae6c07" +source = "git+https://github.com/zcash/librustzcash.git?rev=10caf455e3f52744b5392af226a408b05721f70f#10caf455e3f52744b5392af226a408b05721f70f" dependencies = [ "core2 0.3.3", "document-features", @@ -6814,13 +7713,22 @@ dependencies = [ "cc", "enum_primitive", "ripemd 0.1.3", - "secp256k1", + "secp256k1 0.29.1", "sha-1", "sha2 0.10.8", "thiserror 2.0.12", "tracing", ] +[[package]] +name = "zcash_spec" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cede95491c2191d3e278cab76e097a44b17fde8d6ca0d4e3a22cf4807b2d857" +dependencies = [ + "blake2b_simd", +] + [[package]] name = "zcash_spec" version = "0.2.1" @@ -6833,9 +7741,9 @@ dependencies = [ [[package]] name = "zcash_transparent" version = "0.4.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=8be259c579762f1b0f569453a20c0d0dbeae6c07#8be259c579762f1b0f569453a20c0d0dbeae6c07" +source = "git+https://github.com/zcash/librustzcash.git?rev=10caf455e3f52744b5392af226a408b05721f70f#10caf455e3f52744b5392af226a408b05721f70f" dependencies = [ - "bip32", + "bip32 0.6.0-pre.1", "blake2b_simd", "bs58", "core2 0.3.3", @@ -6843,14 +7751,14 @@ dependencies = [ "getset", "hex", "ripemd 0.1.3", - "secp256k1", + "secp256k1 0.29.1", "sha2 0.10.8", "subtle", - "zcash_address", - "zcash_encoding", - "zcash_protocol", - "zcash_spec", - "zip32", + "zcash_address 0.9.0", + "zcash_encoding 0.3.0", + "zcash_protocol 0.6.1", + "zcash_spec 0.2.1", + "zip32 0.2.0", ] [[package]] @@ -6876,20 +7784,20 @@ dependencies = [ "halo2_proofs", "hex", "humantime", - "incrementalmerkletree", + "incrementalmerkletree 0.8.2", "itertools 0.14.0", "jubjub", "lazy_static", "num-integer", - "orchard", + "orchard 0.11.0", "primitive-types 0.12.2", "rand_core 0.6.4", "rayon", "reddsa", - "redjubjub", + "redjubjub 0.8.0", "ripemd 0.1.3", - "sapling-crypto", - "secp256k1", + "sapling-crypto 0.5.0", + "secp256k1 0.29.1", "serde", "serde-big-array", "serde_json", @@ -6903,12 +7811,12 @@ dependencies = [ "tracing", "uint 0.10.0", "x25519-dalek", - "zcash_address", - "zcash_encoding", + "zcash_address 0.9.0", + "zcash_encoding 0.3.0", "zcash_history", "zcash_note_encryption", - "zcash_primitives", - "zcash_protocol", + "zcash_primitives 0.24.1", + "zcash_protocol 0.6.1", "zcash_transparent", ] @@ -6930,10 +7838,10 @@ dependencies = [ "metrics", "mset", "once_cell", - "orchard", + "orchard 0.11.0", "rand 0.8.5", "rayon", - "sapling-crypto", + "sapling-crypto 0.5.0", "serde", "thiserror 2.0.12", "tokio", @@ -7036,10 +7944,10 @@ dependencies = [ "tower 0.4.13", "tracing", "which 8.0.0", - "zcash_address", - "zcash_keys", - "zcash_primitives", - "zcash_protocol", + "zcash_address 0.9.0", + "zcash_keys 0.10.1", + "zcash_primitives 0.24.1", + "zcash_protocol 0.6.1", "zcash_transparent", "zebra-chain", "zebra-consensus", @@ -7056,7 +7964,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a76a2e972e414caa3635b8c2d21f20c21a71c69f76b37bf7419d97ed0c2277e7" dependencies = [ "thiserror 2.0.12", - "zcash_primitives", + "zcash_primitives 0.24.1", "zcash_script", "zebra-chain", ] @@ -7198,6 +8106,58 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "zewif" +version = "0.1.0" +source = "git+https://github.com/zcash/zewif.git?rev=f84f80612813ba00a0a8a9a5f060bd217fa981cc#f84f80612813ba00a0a8a9a5f060bd217fa981cc" +dependencies = [ + "anyhow", + "bc-components", + "bc-crypto", + "bc-envelope", + "chrono", + "dcbor", + "hex", +] + +[[package]] +name = "zewif-zcashd" +version = "0.1.0" +source = "git+https://github.com/zcash/zewif-zcashd.git?rev=02a98d6236e24819904e084180da9ba0f5c9b5d0#02a98d6236e24819904e084180da9ba0f5c9b5d0" +dependencies = [ + "anyhow", + "bitflags 2.9.0", + "bridgetree", + "byteorder", + "chrono", + "hex", + "incrementalmerkletree 0.7.1", + "orchard 0.10.2", + "ripemd 0.1.3", + "sapling-crypto 0.3.0", + "sha2 0.10.8", + "uuid", + "zcash_address 0.6.3", + "zcash_encoding 0.2.2", + "zcash_keys 0.4.1", + "zcash_primitives 0.19.1", + "zcash_protocol 0.4.3", + "zewif", + "zip32 0.1.3", +] + +[[package]] +name = "zip32" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9943793abf9060b68e1889012dafbd5523ab5b125c0fcc24802d69182f2ac9" +dependencies = [ + "blake2b_simd", + "memuse", + "subtle", + "zcash_spec 0.1.2", +] + [[package]] name = "zip32" version = "0.2.0" @@ -7207,17 +8167,17 @@ dependencies = [ "blake2b_simd", "memuse", "subtle", - "zcash_spec", + "zcash_spec 0.2.1", ] [[package]] name = "zip321" version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash.git?rev=8be259c579762f1b0f569453a20c0d0dbeae6c07#8be259c579762f1b0f569453a20c0d0dbeae6c07" +source = "git+https://github.com/zcash/librustzcash.git?rev=10caf455e3f52744b5392af226a408b05721f70f#10caf455e3f52744b5392af226a408b05721f70f" dependencies = [ "base64 0.22.1", "nom", "percent-encoding", - "zcash_address", - "zcash_protocol", + "zcash_address 0.9.0", + "zcash_protocol 0.6.1", ] diff --git a/Cargo.toml b/Cargo.toml index c365f1a6..bc86f03a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,9 +85,10 @@ zcash_protocol = "0.6" orchard = "0.11" sapling = { package = "sapling-crypto", version = "0.5" } transparent = { package = "zcash_transparent", version = "0.4" } -zcash_keys = "0.10" +zcash_keys = { version = "0.10", features = ["transparent-inputs", "sapling", "orchard", "transparent-key-encoding"] } zcash_primitives = "0.24" zcash_proofs = "0.24" +secp256k1 = "0.29" # Zcash chain state zaino-fetch = "0.1" @@ -101,7 +102,7 @@ zebra-state = "2.0" deadpool = "0.12" deadpool-sqlite = "0.9" deadpool-sync = "0.1" -incrementalmerkletree = "0.8" +incrementalmerkletree = "0.8.2" rusqlite = { version = "0.32", features = ["time"] } schemerz = "0.2" schemerz-rusqlite = "0.320.0" @@ -113,6 +114,12 @@ zcash_client_backend = "0.19" zcash_client_sqlite = "0.17" zcash_note_encryption = "0.4" zip32 = "0.2" +bip32 = "0.2" + +# Zcashd wallet migration +zewif = { version = "0.1" } +zewif-zcashd = { version = "0.1" } +anyhow = "1.0" # lightwalletd (temporary) tonic = "0.13" @@ -123,17 +130,20 @@ age = { git = "https://github.com/str4d/rage.git", rev = "84dc1e9f641994388f107c abscissa_core = { git = "https://github.com/iqlusioninc/abscissa.git", rev = "fdb60678fb3a883decf63d6d3ebc512abd20406f" } abscissa_tokio = { git = "https://github.com/iqlusioninc/abscissa.git", rev = "fdb60678fb3a883decf63d6d3ebc512abd20406f" } -equihash = { git = "https://github.com/zcash/librustzcash.git", rev = "8be259c579762f1b0f569453a20c0d0dbeae6c07" } -transparent = { package = "zcash_transparent", git = "https://github.com/zcash/librustzcash.git", rev = "8be259c579762f1b0f569453a20c0d0dbeae6c07" } -zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "8be259c579762f1b0f569453a20c0d0dbeae6c07" } -zcash_client_backend = { git = "https://github.com/zcash/librustzcash.git", rev = "8be259c579762f1b0f569453a20c0d0dbeae6c07" } -zcash_client_sqlite = { git = "https://github.com/zcash/librustzcash.git", rev = "8be259c579762f1b0f569453a20c0d0dbeae6c07" } -zcash_encoding = { git = "https://github.com/zcash/librustzcash.git", rev = "8be259c579762f1b0f569453a20c0d0dbeae6c07" } -zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "8be259c579762f1b0f569453a20c0d0dbeae6c07" } -zcash_keys = { git = "https://github.com/zcash/librustzcash.git", rev = "8be259c579762f1b0f569453a20c0d0dbeae6c07" } -zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "8be259c579762f1b0f569453a20c0d0dbeae6c07" } -zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "8be259c579762f1b0f569453a20c0d0dbeae6c07" } -zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "8be259c579762f1b0f569453a20c0d0dbeae6c07" } +equihash = { git = "https://github.com/zcash/librustzcash.git", rev = "10caf455e3f52744b5392af226a408b05721f70f" } +transparent = { package = "zcash_transparent", git = "https://github.com/zcash/librustzcash.git", rev = "10caf455e3f52744b5392af226a408b05721f70f" } +zcash_address = { git = "https://github.com/zcash/librustzcash.git", rev = "10caf455e3f52744b5392af226a408b05721f70f" } +zcash_client_backend = { git = "https://github.com/zcash/librustzcash.git", rev = "10caf455e3f52744b5392af226a408b05721f70f" } +zcash_client_sqlite = { git = "https://github.com/zcash/librustzcash.git", rev = "10caf455e3f52744b5392af226a408b05721f70f" } +zcash_encoding = { git = "https://github.com/zcash/librustzcash.git", rev = "10caf455e3f52744b5392af226a408b05721f70f" } +zcash_history = { git = "https://github.com/zcash/librustzcash.git", rev = "10caf455e3f52744b5392af226a408b05721f70f" } +zcash_keys = { git = "https://github.com/zcash/librustzcash.git", rev = "10caf455e3f52744b5392af226a408b05721f70f" } +zcash_primitives = { git = "https://github.com/zcash/librustzcash.git", rev = "10caf455e3f52744b5392af226a408b05721f70f" } +zcash_proofs = { git = "https://github.com/zcash/librustzcash.git", rev = "10caf455e3f52744b5392af226a408b05721f70f" } +zcash_protocol = { git = "https://github.com/zcash/librustzcash.git", rev = "10caf455e3f52744b5392af226a408b05721f70f" } + +zewif = { git = "https://github.com/zcash/zewif.git", rev = "f84f80612813ba00a0a8a9a5f060bd217fa981cc" } +zewif-zcashd = { git = "https://github.com/zcash/zewif-zcashd.git", rev = "02a98d6236e24819904e084180da9ba0f5c9b5d0" } zaino-fetch = { git = "https://github.com/Electric-Coin-Company/zaino.git", rev = "1004733f3303b1c48b6df6db610c54401554683c" } zaino-proto = { git = "https://github.com/Electric-Coin-Company/zaino.git", rev = "1004733f3303b1c48b6df6db610c54401554683c" } diff --git a/deny.toml b/deny.toml index ea26c15d..6b32d6a0 100644 --- a/deny.toml +++ b/deny.toml @@ -20,11 +20,22 @@ allow = [ ] exceptions = [ { name = "arrayref", allow = ["BSD-2-Clause"] }, + { name = "bc-components", allow = ["BSD-2-Clause-Patent"] }, + { name = "bc-crypto", allow = ["BSD-2-Clause-Patent"] }, + { name = "bc-envelope", allow = ["BSD-2-Clause-Patent"] }, + { name = "bc-rand", allow = ["BSD-2-Clause-Patent"] }, + { name = "bc-shamir", allow = ["BSD-2-Clause-Patent"] }, + { name = "bc-tags", allow = ["BSD-2-Clause-Patent"] }, + { name = "bc-ur", allow = ["BSD-2-Clause-Patent"] }, { name = "bindgen", allow = ["BSD-3-Clause"] }, + { name = "bitcoin-private", allow = ["CC0-1.0"] }, + { name = "bitcoin_hashes", allow = ["CC0-1.0"] }, { name = "const_format", allow = ["Zlib"] }, { name = "const_format_proc_macros", allow = ["Zlib"] }, { name = "curve25519-dalek", allow = ["BSD-3-Clause"] }, { name = "dcbor", allow = ["BSD-2-Clause-Patent"] }, + { name = "doctest-file", allow = ["0BSD"] }, + { name = "ed25519-dalek", allow = ["BSD-3-Clause"] }, { name = "human_bytes", allow = ["BSD-2-Clause"] }, { name = "icu_collections", allow = ["Unicode-3.0"] }, { name = "icu_locid", allow = ["Unicode-3.0"] }, @@ -36,15 +47,18 @@ exceptions = [ { name = "icu_properties_data", allow = ["Unicode-3.0"] }, { name = "icu_provider", allow = ["Unicode-3.0"] }, { name = "icu_provider_macros", allow = ["Unicode-3.0"] }, + { name = "known-values", allow = ["BSD-2-Clause-Patent"] }, { name = "libloading", allow = ["ISC"] }, { name = "litemap", allow = ["Unicode-3.0"] }, { name = "matchit", allow = ["BSD-3-Clause"] }, - # Copyleft license. Temporary exception until Zebra stops depending on `dirs`. + { name = "minicbor", allow = ["BlueOak-1.0.0"] }, { name = "option-ext", allow = ["MPL-2.0"] }, + { name = "recvmsg", allow = ["0BSD"] }, { name = "ring", allow = ["ISC"] }, { name = "rustls-webpki", allow = ["ISC"] }, { name = "secp256k1", allow = ["CC0-1.0"] }, { name = "secp256k1-sys", allow = ["CC0-1.0"] }, + { name = "sskr", allow = ["BSD-2-Clause-Patent"] }, { name = "subtle", allow = ["BSD-3-Clause"] }, { name = "tinystr", allow = ["Unicode-3.0"] }, { name = "unicode-ident", allow = ["Unicode-3.0"] }, diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index 192639ae..ae0e4435 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -248,7 +248,7 @@ importable = false [[trusted.equihash]] criteria = "safe-to-deploy" -user-id = 6289 +user-id = 6289 # Jack Grigg (str4d) start = "2020-06-26" end = "2026-03-22" @@ -260,7 +260,7 @@ end = "2026-03-04" [[trusted.f4jumble]] criteria = "safe-to-deploy" -user-id = 6289 +user-id = 6289 # Jack Grigg (str4d) start = "2021-09-22" end = "2026-03-04" @@ -416,7 +416,7 @@ end = "2026-03-04" [[trusted.zcash_history]] criteria = "safe-to-deploy" -user-id = 6289 +user-id = 6289 # Jack Grigg (str4d) start = "2024-03-01" end = "2026-04-08" diff --git a/supply-chain/config.toml b/supply-chain/config.toml index fe136250..44f03370 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -22,6 +22,15 @@ url = "https://raw.githubusercontent.com/mozilla/supply-chain/main/audits.toml" [imports.zcash] url = "https://raw.githubusercontent.com/zcash/rust-ecosystem/main/supply-chain/audits.toml" +[policy.abscissa_core] +audit-as-crates-io = true + +[policy.abscissa_derive] +audit-as-crates-io = true + +[policy.abscissa_tokio] +audit-as-crates-io = true + [policy.age] audit-as-crates-io = true @@ -104,15 +113,15 @@ audit-as-crates-io = true audit-as-crates-io = true [[exemptions.abscissa_core]] -version = "0.8.1" +version = "0.8.2@git:fdb60678fb3a883decf63d6d3ebc512abd20406f" criteria = "safe-to-deploy" [[exemptions.abscissa_derive]] -version = "0.8.0" +version = "0.8.2@git:fdb60678fb3a883decf63d6d3ebc512abd20406f" criteria = "safe-to-deploy" [[exemptions.abscissa_tokio]] -version = "0.8.0" +version = "0.8.0@git:fdb60678fb3a883decf63d6d3ebc512abd20406f" criteria = "safe-to-deploy" [[exemptions.addr2line]] @@ -143,10 +152,6 @@ criteria = "safe-to-deploy" version = "1.1.3" criteria = "safe-to-deploy" -[[exemptions.allocator-api2]] -version = "0.2.21" -criteria = "safe-to-deploy" - [[exemptions.anstream]] version = "0.6.18" criteria = "safe-to-deploy" @@ -171,6 +176,10 @@ criteria = "safe-to-deploy" version = "1.7.1" criteria = "safe-to-deploy" +[[exemptions.argon2]] +version = "0.5.3" +criteria = "safe-to-deploy" + [[exemptions.async-stream]] version = "0.3.6" criteria = "safe-to-deploy" @@ -211,10 +220,42 @@ criteria = "safe-to-deploy" version = "0.3.71" criteria = "safe-to-deploy" +[[exemptions.base16ct]] +version = "0.2.0" +criteria = "safe-to-deploy" + [[exemptions.base64ct]] version = "1.7.3" criteria = "safe-to-deploy" +[[exemptions.bc-components]] +version = "0.24.0" +criteria = "safe-to-deploy" + +[[exemptions.bc-crypto]] +version = "0.9.0" +criteria = "safe-to-deploy" + +[[exemptions.bc-envelope]] +version = "0.33.0" +criteria = "safe-to-deploy" + +[[exemptions.bc-rand]] +version = "0.4.0" +criteria = "safe-to-deploy" + +[[exemptions.bc-shamir]] +version = "0.8.0" +criteria = "safe-to-deploy" + +[[exemptions.bc-tags]] +version = "0.5.0" +criteria = "safe-to-deploy" + +[[exemptions.bc-ur]] +version = "0.12.0" +criteria = "safe-to-deploy" + [[exemptions.bech32]] version = "0.8.1" criteria = "safe-to-deploy" @@ -243,10 +284,30 @@ criteria = "safe-to-deploy" version = "0.12.0" criteria = "safe-to-deploy" +[[exemptions.bip32]] +version = "0.5.3" +criteria = "safe-to-deploy" + [[exemptions.bip32]] version = "0.6.0-pre.1" criteria = "safe-to-deploy" +[[exemptions.bitcoin-io]] +version = "0.1.3" +criteria = "safe-to-deploy" + +[[exemptions.bitcoin-private]] +version = "0.1.0" +criteria = "safe-to-deploy" + +[[exemptions.bitcoin_hashes]] +version = "0.12.0" +criteria = "safe-to-deploy" + +[[exemptions.bitcoin_hashes]] +version = "0.14.0" +criteria = "safe-to-deploy" + [[exemptions.bitflags-serde-legacy]] version = "0.1.1" criteria = "safe-to-deploy" @@ -279,6 +340,10 @@ criteria = "safe-to-deploy" version = "0.8.0" criteria = "safe-to-deploy" +[[exemptions.bridgetree]] +version = "0.6.0" +criteria = "safe-to-deploy" + [[exemptions.bs58]] version = "0.5.1" criteria = "safe-to-deploy" @@ -415,6 +480,14 @@ criteria = "safe-to-deploy" version = "0.2.17" criteria = "safe-to-deploy" +[[exemptions.crc]] +version = "3.3.0" +criteria = "safe-to-deploy" + +[[exemptions.crc-catalog]] +version = "2.4.0" +criteria = "safe-to-deploy" + [[exemptions.crossbeam-channel]] version = "0.5.14" criteria = "safe-to-deploy" @@ -431,6 +504,10 @@ criteria = "safe-to-deploy" version = "0.8.20" criteria = "safe-to-deploy" +[[exemptions.crypto-bigint]] +version = "0.5.5" +criteria = "safe-to-deploy" + [[exemptions.crypto-common]] version = "0.2.0-rc.1" criteria = "safe-to-deploy" @@ -463,6 +540,10 @@ criteria = "safe-to-deploy" version = "6.1.0" criteria = "safe-to-deploy" +[[exemptions.dcbor]] +version = "0.22.0" +criteria = "safe-to-deploy" + [[exemptions.deadpool]] version = "0.12.2" criteria = "safe-to-deploy" @@ -511,6 +592,10 @@ criteria = "safe-to-deploy" version = "0.4.1" criteria = "safe-to-deploy" +[[exemptions.doctest-file]] +version = "1.0.0" +criteria = "safe-to-deploy" + [[exemptions.documented]] version = "0.3.0" criteria = "safe-to-deploy" @@ -519,22 +604,38 @@ criteria = "safe-to-deploy" version = "0.9.1" criteria = "safe-to-deploy" +[[exemptions.dsa]] +version = "0.6.3" +criteria = "safe-to-deploy" + [[exemptions.dunce]] version = "1.0.5" -criteria = "safe-to-run" +criteria = "safe-to-deploy" [[exemptions.dyn-clone]] version = "1.0.19" criteria = "safe-to-deploy" +[[exemptions.ecdsa]] +version = "0.16.9" +criteria = "safe-to-deploy" + [[exemptions.ed25519]] version = "2.2.1" criteria = "safe-to-deploy" +[[exemptions.ed25519-dalek]] +version = "2.2.0" +criteria = "safe-to-deploy" + [[exemptions.ed25519-zebra]] version = "3.0.0" criteria = "safe-to-deploy" +[[exemptions.elliptic-curve]] +version = "0.13.8" +criteria = "safe-to-deploy" + [[exemptions.encode_unicode]] version = "1.0.0" criteria = "safe-to-deploy" @@ -552,7 +653,7 @@ version = "0.7.1" criteria = "safe-to-deploy" [[exemptions.equihash]] -version = "0.2.2@git:8be259c579762f1b0f569453a20c0d0dbeae6c07" +version = "0.2.2@git:10caf455e3f52744b5392af226a408b05721f70f" criteria = "safe-to-deploy" [[exemptions.eyre]] @@ -560,7 +661,7 @@ version = "0.6.12" criteria = "safe-to-deploy" [[exemptions.f4jumble]] -version = "0.1.1@git:8be259c579762f1b0f569453a20c0d0dbeae6c07" +version = "0.1.1@git:10caf455e3f52744b5392af226a408b05721f70f" criteria = "safe-to-deploy" [[exemptions.fallible-iterator]] @@ -667,6 +768,10 @@ criteria = "safe-to-deploy" version = "0.4.7" criteria = "safe-to-deploy" +[[exemptions.half]] +version = "2.6.0" +criteria = "safe-to-deploy" + [[exemptions.halo2_gadgets]] version = "0.3.1" criteria = "safe-to-deploy" @@ -703,6 +808,10 @@ criteria = "safe-to-deploy" version = "0.5.0" criteria = "safe-to-deploy" +[[exemptions.hex-conservative]] +version = "0.2.1" +criteria = "safe-to-deploy" + [[exemptions.hex-literal]] version = "0.4.1" criteria = "safe-to-deploy" @@ -807,6 +916,10 @@ criteria = "safe-to-deploy" version = "1.9.3" criteria = "safe-to-deploy" +[[exemptions.interprocess]] +version = "2.2.3" +criteria = "safe-to-deploy" + [[exemptions.intl-memoizer]] version = "0.5.2" criteria = "safe-to-deploy" @@ -835,6 +948,10 @@ criteria = "safe-to-deploy" version = "0.10.3" criteria = "safe-to-deploy" +[[exemptions.itertools]] +version = "0.11.0" +criteria = "safe-to-deploy" + [[exemptions.itertools]] version = "0.13.0" criteria = "safe-to-deploy" @@ -875,6 +992,10 @@ criteria = "safe-to-deploy" version = "0.10.0" criteria = "safe-to-deploy" +[[exemptions.known-values]] +version = "0.7.0" +criteria = "safe-to-deploy" + [[exemptions.lazy-regex]] version = "3.4.1" criteria = "safe-to-deploy" @@ -947,6 +1068,10 @@ criteria = "safe-to-deploy" version = "1.11.1+lz4-1.10.0" criteria = "safe-to-deploy" +[[exemptions.matchers]] +version = "0.2.0" +criteria = "safe-to-deploy" + [[exemptions.matchit]] version = "0.7.3" criteria = "safe-to-deploy" @@ -971,6 +1096,14 @@ criteria = "safe-to-deploy" version = "0.3.17" criteria = "safe-to-deploy" +[[exemptions.minicbor]] +version = "0.19.1" +criteria = "safe-to-deploy" + +[[exemptions.minicbor-derive]] +version = "0.13.0" +criteria = "safe-to-deploy" + [[exemptions.minimal-lexical]] version = "0.2.1" criteria = "safe-to-deploy" @@ -995,10 +1128,22 @@ criteria = "safe-to-deploy" version = "0.15.0" criteria = "safe-to-deploy" +[[exemptions.nonempty]] +version = "0.7.0" +criteria = "safe-to-deploy" + +[[exemptions.nu-ansi-term]] +version = "0.50.1" +criteria = "safe-to-deploy" + [[exemptions.num-bigint]] version = "0.4.6" criteria = "safe-to-deploy" +[[exemptions.num-bigint-dig]] +version = "0.8.4" +criteria = "safe-to-deploy" + [[exemptions.num-traits]] version = "0.1.43" criteria = "safe-to-deploy" @@ -1047,6 +1192,18 @@ criteria = "safe-to-run" version = "3.5.0" criteria = "safe-to-deploy" +[[exemptions.p256]] +version = "0.13.2" +criteria = "safe-to-deploy" + +[[exemptions.p384]] +version = "0.13.1" +criteria = "safe-to-deploy" + +[[exemptions.p521]] +version = "0.13.3" +criteria = "safe-to-deploy" + [[exemptions.pairing]] version = "0.23.0" criteria = "safe-to-deploy" @@ -1075,10 +1232,18 @@ criteria = "safe-to-deploy" version = "0.5.1" criteria = "safe-to-deploy" +[[exemptions.paste]] +version = "1.0.10" +criteria = "safe-to-deploy" + [[exemptions.pbkdf2]] version = "0.12.2" criteria = "safe-to-deploy" +[[exemptions.pem-rfc7468]] +version = "0.7.0" +criteria = "safe-to-deploy" + [[exemptions.petgraph]] version = "0.7.1" criteria = "safe-to-deploy" @@ -1111,6 +1276,10 @@ criteria = "safe-to-deploy" version = "0.6.0" criteria = "safe-to-deploy" +[[exemptions.pkcs1]] +version = "0.7.5" +criteria = "safe-to-deploy" + [[exemptions.pkcs8]] version = "0.10.2" criteria = "safe-to-deploy" @@ -1131,10 +1300,30 @@ criteria = "safe-to-deploy" version = "0.2.21" criteria = "safe-to-deploy" +[[exemptions.pqcrypto-internals]] +version = "0.2.11" +criteria = "safe-to-deploy" + +[[exemptions.pqcrypto-mldsa]] +version = "0.1.2" +criteria = "safe-to-deploy" + +[[exemptions.pqcrypto-mlkem]] +version = "0.1.1" +criteria = "safe-to-deploy" + +[[exemptions.pqcrypto-traits]] +version = "0.3.5" +criteria = "safe-to-deploy" + [[exemptions.prettyplease]] version = "0.2.32" criteria = "safe-to-deploy" +[[exemptions.primeorder]] +version = "0.13.6" +criteria = "safe-to-deploy" + [[exemptions.primitive-types]] version = "0.12.2" criteria = "safe-to-deploy" @@ -1223,6 +1412,14 @@ criteria = "safe-to-deploy" version = "0.2.0" criteria = "safe-to-deploy" +[[exemptions.rand_xoshiro]] +version = "0.6.0" +criteria = "safe-to-deploy" + +[[exemptions.recvmsg]] +version = "1.0.0" +criteria = "safe-to-deploy" + [[exemptions.reddsa]] version = "0.5.1" criteria = "safe-to-deploy" @@ -1247,22 +1444,18 @@ criteria = "safe-to-deploy" version = "1.11.1" criteria = "safe-to-deploy" -[[exemptions.regex-automata]] -version = "0.1.10" -criteria = "safe-to-deploy" - [[exemptions.regex-automata]] version = "0.4.8" criteria = "safe-to-deploy" -[[exemptions.regex-syntax]] -version = "0.6.29" -criteria = "safe-to-deploy" - [[exemptions.reqwest]] version = "0.12.15" criteria = "safe-to-deploy" +[[exemptions.rfc6979]] +version = "0.4.0" +criteria = "safe-to-deploy" + [[exemptions.ring]] version = "0.17.14" criteria = "safe-to-deploy" @@ -1295,6 +1488,10 @@ criteria = "safe-to-deploy" version = "7.3.1" criteria = "safe-to-deploy" +[[exemptions.rsa]] +version = "0.9.8" +criteria = "safe-to-deploy" + [[exemptions.rtoolbox]] version = "0.0.2" criteria = "safe-to-deploy" @@ -1379,10 +1576,26 @@ criteria = "safe-to-deploy" version = "0.11.0" criteria = "safe-to-deploy" +[[exemptions.sec1]] +version = "0.7.3" +criteria = "safe-to-deploy" + +[[exemptions.secp256k1]] +version = "0.26.0" +criteria = "safe-to-deploy" + [[exemptions.secp256k1]] version = "0.29.1" criteria = "safe-to-deploy" +[[exemptions.secp256k1]] +version = "0.30.0" +criteria = "safe-to-deploy" + +[[exemptions.secp256k1-sys]] +version = "0.8.2" +criteria = "safe-to-deploy" + [[exemptions.secp256k1-sys]] version = "0.10.1" criteria = "safe-to-deploy" @@ -1487,6 +1700,26 @@ criteria = "safe-to-deploy" version = "0.7.3" criteria = "safe-to-deploy" +[[exemptions.ssh-agent-client-rs]] +version = "1.1.1" +criteria = "safe-to-deploy" + +[[exemptions.ssh-cipher]] +version = "0.2.0" +criteria = "safe-to-deploy" + +[[exemptions.ssh-encoding]] +version = "0.2.0" +criteria = "safe-to-deploy" + +[[exemptions.ssh-key]] +version = "0.6.7" +criteria = "safe-to-deploy" + +[[exemptions.sskr]] +version = "0.8.0" +criteria = "safe-to-deploy" + [[exemptions.syn]] version = "1.0.109" criteria = "safe-to-deploy" @@ -1643,6 +1876,10 @@ criteria = "safe-to-deploy" version = "0.9.0" criteria = "safe-to-deploy" +[[exemptions.ur]] +version = "0.4.1" +criteria = "safe-to-deploy" + [[exemptions.uuid]] version = "1.16.0" criteria = "safe-to-deploy" @@ -1723,6 +1960,10 @@ criteria = "safe-to-deploy" version = "1.6.0" criteria = "safe-to-deploy" +[[exemptions.widestring]] +version = "1.2.0" +criteria = "safe-to-deploy" + [[exemptions.winapi]] version = "0.3.9" criteria = "safe-to-deploy" @@ -1784,43 +2025,51 @@ version = "0.1.2@git:1004733f3303b1c48b6df6db610c54401554683c" criteria = "safe-to-deploy" [[exemptions.zcash_address]] -version = "0.9.0@git:8be259c579762f1b0f569453a20c0d0dbeae6c07" +version = "0.9.0@git:10caf455e3f52744b5392af226a408b05721f70f" criteria = "safe-to-deploy" [[exemptions.zcash_client_backend]] -version = "0.19.1@git:8be259c579762f1b0f569453a20c0d0dbeae6c07" +version = "0.19.1@git:10caf455e3f52744b5392af226a408b05721f70f" criteria = "safe-to-deploy" [[exemptions.zcash_client_sqlite]] -version = "0.17.3@git:8be259c579762f1b0f569453a20c0d0dbeae6c07" +version = "0.17.3@git:10caf455e3f52744b5392af226a408b05721f70f" +criteria = "safe-to-deploy" + +[[exemptions.zcash_encoding]] +version = "0.2.2" criteria = "safe-to-deploy" [[exemptions.zcash_encoding]] -version = "0.3.0@git:8be259c579762f1b0f569453a20c0d0dbeae6c07" +version = "0.3.0@git:10caf455e3f52744b5392af226a408b05721f70f" criteria = "safe-to-deploy" [[exemptions.zcash_history]] -version = "0.4.0@git:8be259c579762f1b0f569453a20c0d0dbeae6c07" +version = "0.4.0@git:10caf455e3f52744b5392af226a408b05721f70f" criteria = "safe-to-deploy" [[exemptions.zcash_keys]] -version = "0.10.1@git:8be259c579762f1b0f569453a20c0d0dbeae6c07" +version = "0.10.1@git:10caf455e3f52744b5392af226a408b05721f70f" criteria = "safe-to-deploy" [[exemptions.zcash_primitives]] -version = "0.24.0@git:8be259c579762f1b0f569453a20c0d0dbeae6c07" +version = "0.24.1@git:10caf455e3f52744b5392af226a408b05721f70f" criteria = "safe-to-deploy" [[exemptions.zcash_proofs]] -version = "0.24.0@git:8be259c579762f1b0f569453a20c0d0dbeae6c07" +version = "0.24.0@git:10caf455e3f52744b5392af226a408b05721f70f" criteria = "safe-to-deploy" [[exemptions.zcash_protocol]] -version = "0.6.1@git:8be259c579762f1b0f569453a20c0d0dbeae6c07" +version = "0.6.1@git:10caf455e3f52744b5392af226a408b05721f70f" +criteria = "safe-to-deploy" + +[[exemptions.zcash_spec]] +version = "0.1.2" criteria = "safe-to-deploy" [[exemptions.zcash_transparent]] -version = "0.4.0@git:8be259c579762f1b0f569453a20c0d0dbeae6c07" +version = "0.4.0@git:10caf455e3f52744b5392af226a408b05721f70f" criteria = "safe-to-deploy" [[exemptions.zebra-chain]] @@ -1867,6 +2116,10 @@ criteria = "safe-to-deploy" version = "0.8.24" criteria = "safe-to-deploy" +[[exemptions.zip32]] +version = "0.1.3" +criteria = "safe-to-deploy" + [[exemptions.zip321]] -version = "0.5.0@git:8be259c579762f1b0f569453a20c0d0dbeae6c07" +version = "0.5.0@git:10caf455e3f52744b5392af226a408b05721f70f" criteria = "safe-to-deploy" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 357129b1..06ddf9c0 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -15,6 +15,20 @@ user-id = 3788 user-login = "emilio" user-name = "Emilio Cobos Álvarez" +[[publisher.f4jumble]] +version = "0.1.1" +when = "2024-12-13" +user-id = 6289 +user-login = "str4d" +user-name = "Jack Grigg" + +[[publisher.incrementalmerkletree]] +version = "0.7.1" +when = "2024-12-16" +user-id = 169181 +user-login = "nuttycom" +user-name = "Kris Nuttycombe" + [[publisher.incrementalmerkletree]] version = "0.8.2" when = "2025-02-01" @@ -22,6 +36,13 @@ user-id = 169181 user-login = "nuttycom" user-name = "Kris Nuttycombe" +[[publisher.orchard]] +version = "0.10.2" +when = "2025-05-08" +user-id = 169181 +user-login = "nuttycom" +user-name = "Kris Nuttycombe" + [[publisher.orchard]] version = "0.11.0" when = "2025-02-21" @@ -29,6 +50,13 @@ user-id = 169181 user-login = "nuttycom" user-name = "Kris Nuttycombe" +[[publisher.sapling-crypto]] +version = "0.3.0" +when = "2024-10-02" +user-id = 169181 +user-login = "nuttycom" +user-name = "Kris Nuttycombe" + [[publisher.sapling-crypto]] version = "0.5.0" when = "2025-02-21" @@ -322,6 +350,34 @@ when = "2025-02-05" user-id = 73222 user-login = "wasmtime-publish" +[[publisher.zcash_address]] +version = "0.6.3" +when = "2025-05-07" +user-id = 169181 +user-login = "nuttycom" +user-name = "Kris Nuttycombe" + +[[publisher.zcash_keys]] +version = "0.4.1" +when = "2025-05-09" +user-id = 169181 +user-login = "nuttycom" +user-name = "Kris Nuttycombe" + +[[publisher.zcash_primitives]] +version = "0.19.1" +when = "2025-05-09" +user-id = 169181 +user-login = "nuttycom" +user-name = "Kris Nuttycombe" + +[[publisher.zcash_protocol]] +version = "0.4.3" +when = "2024-12-17" +user-id = 169181 +user-login = "nuttycom" +user-name = "Kris Nuttycombe" + [[publisher.zcash_script]] version = "0.3.2" when = "2025-06-24" @@ -368,6 +424,15 @@ criteria = "safe-to-deploy" version = "2.0.0" notes = "Fork of the original `adler` crate, zero unsfae code, works in `no_std`, does what it says on th tin." +[[audits.bytecode-alliance.audits.allocator-api2]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +delta = "0.2.18 -> 0.2.20" +notes = """ +The changes appear to be reasonable updates from Rust's stdlib imported into +`allocator-api2`'s copy of this code. +""" + [[audits.bytecode-alliance.audits.anyhow]] who = "Pat Hickey " criteria = "safe-to-deploy" @@ -608,23 +673,6 @@ criteria = "safe-to-deploy" delta = "0.4.22 -> 0.4.27" notes = "Lots of minor updates to macros and such, nothing touching `unsafe`" -[[audits.bytecode-alliance.audits.matchers]] -who = "Pat Hickey " -criteria = "safe-to-deploy" -version = "0.1.0" - -[[audits.bytecode-alliance.audits.nu-ansi-term]] -who = "Pat Hickey " -criteria = "safe-to-deploy" -version = "0.46.0" -notes = "one use of unsafe to call windows specific api to get console handle." - -[[audits.bytecode-alliance.audits.overload]] -who = "Pat Hickey " -criteria = "safe-to-deploy" -version = "0.1.1" -notes = "small crate, only defines macro-rules!, nicely documented as well" - [[audits.bytecode-alliance.audits.percent-encoding]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -1129,6 +1177,12 @@ version = "0.1.46" notes = "Contains no unsafe" aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" +[[audits.google.audits.num-iter]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +version = "0.1.43" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + [[audits.google.audits.num-traits]] who = "Manish Goregaokar " criteria = "safe-to-deploy" @@ -2007,6 +2061,16 @@ who = "David Cook " criteria = "safe-to-deploy" version = "0.12.1" +[[audits.isrg.audits.num-iter]] +who = "David Cook " +criteria = "safe-to-deploy" +delta = "0.1.43 -> 0.1.44" + +[[audits.isrg.audits.num-iter]] +who = "David Cook " +criteria = "safe-to-deploy" +delta = "0.1.44 -> 0.1.45" + [[audits.isrg.audits.once_cell]] who = "J.C. Jones " criteria = "safe-to-deploy" @@ -2133,6 +2197,18 @@ end = "2024-06-16" notes = "Maintained by Henri Sivonen who works at Mozilla." aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.allocator-api2]] +who = "Nicolas Silva " +criteria = "safe-to-deploy" +version = "0.2.18" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + +[[audits.mozilla.audits.allocator-api2]] +who = "Mike Hommey " +criteria = "safe-to-deploy" +delta = "0.2.20 -> 0.2.21" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + [[audits.mozilla.audits.android-tzdata]] who = "Mark Hammond " criteria = "safe-to-deploy" @@ -2235,6 +2311,13 @@ delta = "0.1.1 -> 0.2.1" notes = "Very minor changes." aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.crossbeam-channel]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +delta = "0.5.14 -> 0.5.15" +notes = "Fixes a regression from an earlier version which could lead to a double free" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + [[audits.mozilla.audits.crunchy]] who = "Erich Gubler " criteria = "safe-to-deploy" @@ -2638,6 +2721,12 @@ criteria = "safe-to-deploy" version = "0.2.0" aggregated-from = "https://raw.githubusercontent.com/mozilla/cargo-vet/main/supply-chain/audits.toml" +[[audits.mozilla.audits.paste]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +delta = "1.0.10 -> 1.0.15" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + [[audits.mozilla.audits.percent-encoding]] who = "Valentin Gosu " criteria = "safe-to-deploy" @@ -2923,6 +3012,12 @@ criteria = "safe-to-deploy" delta = "0.3.17 -> 0.3.19" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.tracing-subscriber]] +who = "Mark Hammond " +criteria = "safe-to-deploy" +delta = "0.3.19 -> 0.3.20" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + [[audits.mozilla.audits.unic-langid]] who = "Zibi Braniecki " criteria = "safe-to-deploy" @@ -3427,6 +3522,12 @@ delta = "0.4.0 -> 0.4.1" notes = "Changes to `Command` usage are to add support for `RUSTC_WRAPPER`." aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" +[[audits.zcash.audits.secp256k1]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.26.0 -> 0.27.0" +aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" + [[audits.zcash.audits.sharded-slab]] who = "Jack Grigg " criteria = "safe-to-deploy" diff --git a/zallet/Cargo.toml b/zallet/Cargo.toml index 3e854c0e..dddf0e61 100644 --- a/zallet/Cargo.toml +++ b/zallet/Cargo.toml @@ -72,6 +72,14 @@ assets = [ ["../README.md", "usr/share/doc/zallet/README.md", "644"], ] +[[test]] +name = "cli_tests" +required-features = ["zcashd-import"] + +[[test]] +name = "zallet_acceptance" +path = "tests/acceptance.rs" + [dependencies] abscissa_core.workspace = true abscissa_tokio.workspace = true @@ -91,7 +99,7 @@ http-body-util.workspace = true hyper.workspace = true i18n-embed = { workspace = true, features = ["desktop-requester"] } i18n-embed-fl.workspace = true -incrementalmerkletree.workspace = true +incrementalmerkletree = { workspace = true, features = ["legacy-api"] } jsonrpsee = { workspace = true, features = ["macros", "server"] } known-folders.workspace = true nix = { workspace = true, features = ["signal"] } @@ -106,6 +114,7 @@ sapling.workspace = true schemars.workspace = true schemerz.workspace = true schemerz-rusqlite.workspace = true +secp256k1.workspace = true secrecy.workspace = true serde.workspace = true serde_json.workspace = true @@ -136,7 +145,7 @@ zcash_client_sqlite = { workspace = true, features = [ "orchard", "transparent-inputs", ] } -zcash_keys.workspace = true +zcash_keys = { workspace = true, features = ["zcashd-compat", "unstable"] } zcash_note_encryption.workspace = true zcash_primitives.workspace = true zcash_proofs = { workspace = true, features = ["bundled-prover"] } @@ -145,6 +154,9 @@ zebra-chain.workspace = true zebra-rpc.workspace = true zebra-state.workspace = true zip32.workspace = true +zewif = { workspace = true, optional = true } +zewif-zcashd = { workspace = true, optional = true } +anyhow.workspace = true console-subscriber = { workspace = true, optional = true } jsonrpsee-http-client = { workspace = true, optional = true } @@ -183,6 +195,22 @@ rpc-cli = ["jsonrpsee/async-client", "dep:jsonrpsee-http-client"] ## https://github.com/tokio-rs/console/blob/main/console-subscriber/README.md#enabling-tokio-instrumentation tokio-console = ["dep:console-subscriber", "tokio/tracing"] +## Allows `zallet` to provide transparent key import functionality, and to work +## with a wallet imported via the `migrate-zcashd-wallet` command. +transparent-key-import = [ + "zcash_client_backend/transparent-key-import", + "zcash_client_sqlite/transparent-key-import", +] + +## Allows `zallet` to import zcashd wallets. +zcashd-import = [ + "transparent-key-import", + "dep:zewif", + "dep:zewif-zcashd", + "zcash_client_backend/zcashd-compat", + "zcash_client_sqlite/zcashd-compat", +] + [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = [ 'cfg(outside_buildscript)', diff --git a/zallet/i18n/en-US/zallet.ftl b/zallet/i18n/en-US/zallet.ftl index 1be9febd..b7698d94 100644 --- a/zallet/i18n/en-US/zallet.ftl +++ b/zallet/i18n/en-US/zallet.ftl @@ -112,6 +112,43 @@ err-migrate-unknown-zcashd-option = Unknown {-zcashd} option '{$option}' err-failed-seed-fingerprinting = Zallet was unable to import invalid seed data, likely due to the seed having an invalid length. +err-migrate-wallet-bdb-parse = + An error occurred in parsing the {-zcashd} wallet file at '{$path}': '{$err}' +err-migrate-wallet-db-dump = + An error occurred in extracting wallet data from '{$path}': '{$err}' +err-migrate-wallet-seed-absent = + The {-zcashd} wallet file did not contain HD seed information. Wallets from + prior to the Sapling network upgrade are not supported by this migration + tool. +err-migrate-wallet-invalid-mnemonic = + The {-zcashd} wallet file contained invalid mnemonic seed phrase data and + may be corrupt: '{$err}' +err-migrate-wallet-key-decoding= + The {-zcashd} wallet file contained invalid mnemonic transparent secret key + data and may be corrupt: '{$err}' +err-migrate-wallet-key-data= + The {-zcashd} wallet file contained invalid key data and may be corrupt: + '{$err}' +err-migrate-wallet-network-mismatch = + The {-zcashd} wallet being imported is for the '{$wallet_network}' network, + but this {-zallet} instance is configured for '{$zallet_network}' +err-migrate-wallet-regtest = + Migration of regtest wallets is not yet supported. +err-migrate-wallet-storage = + An database error occurred in wallet migration. This is indicative of a + programming error; please report the following error to (TBD): '{$err}' +err-migrate-wallet-invalid-chain-data = + Invalid chain data was encountered in wallet migration. This is indicative of a + programming error; please report the following error to (TBD): '{$err}' +err-migrate-wallet-key-decoding = + An error occurred decoding key material: '{$err}'. +err-migrate-wallet-tx-fetch = + An error occurred fetching transaction data: '{$err}'. +err-migrate-wallet-data-parse= + An error occurred parsing zcashd wallet data: '{$err}'. +err-migrate-wallet-invalid-account-id = + Error encountered in wallet migration: '{$account_id}' is not a valid ZIP + 32 account identifier. err-ux-A = Did {-zallet} not do what you expected? Could the error be more useful? err-ux-B = Tell us diff --git a/zallet/src/cli.rs b/zallet/src/cli.rs index 46e2030c..69934f4b 100644 --- a/zallet/src/cli.rs +++ b/zallet/src/cli.rs @@ -51,9 +51,13 @@ pub(crate) enum ZalletCmd { ExampleConfig(ExampleConfigCmd), /// Generate a `zallet.toml` config from an existing `zcash.conf` file. - #[cfg(zallet_build = "wallet")] + #[cfg(all(zallet_build = "wallet", feature = "zcashd-import"))] MigrateZcashConf(MigrateZcashConfCmd), + /// Add the keys and transactions of a zcashd wallet.dat file to the wallet database. + #[cfg(all(zallet_build = "wallet", feature = "zcashd-import"))] + MigrateZcashdWallet(MigrateZcashdWalletCmd), + /// Initialize wallet encryption. #[cfg(zallet_build = "wallet")] InitWalletEncryption(InitWalletEncryptionCmd), @@ -101,19 +105,19 @@ pub(crate) struct ExampleConfigCmd { } /// `migrate-zcash-conf` subcommand -#[cfg(zallet_build = "wallet")] +#[cfg(all(zallet_build = "wallet", feature = "zcashd-import"))] #[derive(Debug, Parser)] #[cfg_attr(outside_buildscript, derive(Command))] pub(crate) struct MigrateZcashConfCmd { /// Specify `zcashd` configuration file. /// - /// Relative paths will be prefixed by `datadir` location. + /// Relative paths will be prefixed by `zcashd_datadir` location. #[arg(long, default_value = "zcash.conf")] pub(crate) conf: PathBuf, /// Specify `zcashd` data directory (this path cannot use '~'). #[arg(long)] - pub(crate) datadir: Option, + pub(crate) zcashd_datadir: Option, /// Allow a migration when warnings are present. #[arg(long)] @@ -135,6 +139,33 @@ pub(crate) struct MigrateZcashConfCmd { pub(crate) this_is_alpha_code_and_you_will_need_to_redo_the_migration_later: bool, } +/// `migrate-zcashd-wallet` subcommand +#[cfg(all(zallet_build = "wallet", feature = "zcashd-import"))] +#[derive(Debug, Parser)] +#[cfg_attr(outside_buildscript, derive(Command))] +pub(crate) struct MigrateZcashdWalletCmd { + /// Specify location of the `zcashd` `wallet.dat` file. + /// + /// Relative paths will be prefixed by `zcashd_datadir` location. + #[arg(long, default_value = "wallet.dat")] + pub(crate) path: PathBuf, + + /// Specify `zcashd` data directory (this path cannot use '~'). + #[arg(long)] + pub(crate) zcashd_datadir: Option, + + /// Buffer wallet transactions in-memory in the process of performing the wallet restore. For + /// very active wallets, this might exceed the available memory on your machine, so enable this + /// with caution. + #[arg(long)] + pub(crate) buffer_wallet_transactions: bool, + + /// Allow a migration when warnings are present. If set to `false`, any warning will be treated + /// as an error and cause the migration to abort. + #[arg(long)] + pub(crate) allow_warnings: bool, +} + /// `init-wallet-encryption` subcommand #[cfg(zallet_build = "wallet")] #[derive(Debug, Parser)] diff --git a/zallet/src/commands.rs b/zallet/src/commands.rs index 80d70cac..54fa917e 100644 --- a/zallet/src/commands.rs +++ b/zallet/src/commands.rs @@ -30,8 +30,10 @@ mod generate_mnemonic; mod import_mnemonic; #[cfg(zallet_build = "wallet")] mod init_wallet_encryption; -#[cfg(zallet_build = "wallet")] +#[cfg(all(zallet_build = "wallet", feature = "zcashd-import"))] mod migrate_zcash_conf; +#[cfg(all(zallet_build = "wallet", feature = "zcashd-import"))] +mod migrate_zcashd_wallet; #[cfg(feature = "rpc-cli")] pub(crate) mod rpc_cli; diff --git a/zallet/src/commands/generate_mnemonic.rs b/zallet/src/commands/generate_mnemonic.rs index 94772661..66a5e8b4 100644 --- a/zallet/src/commands/generate_mnemonic.rs +++ b/zallet/src/commands/generate_mnemonic.rs @@ -1,7 +1,6 @@ use abscissa_core::Runnable; use bip0039::{Count, English, Mnemonic}; use rand::{RngCore, rngs::OsRng}; -use secrecy::SecretString; use crate::{ cli::GenerateMnemonicCmd, @@ -30,9 +29,7 @@ impl AsyncRunnable for GenerateMnemonicCmd { let mnemonic = Mnemonic::::from_entropy(entropy) .expect("valid entropy length won't fail to generate the mnemonic"); - keystore - .encrypt_and_store_mnemonic(&SecretString::new(mnemonic.into_phrase())) - .await?; + keystore.encrypt_and_store_mnemonic(mnemonic).await?; Ok(()) } diff --git a/zallet/src/commands/import_mnemonic.rs b/zallet/src/commands/import_mnemonic.rs index 506d89d1..6aa2f726 100644 --- a/zallet/src/commands/import_mnemonic.rs +++ b/zallet/src/commands/import_mnemonic.rs @@ -26,9 +26,7 @@ impl AsyncRunnable for ImportMnemonicCmd { let mnemonic = Mnemonic::::from_phrase(phrase.expose_secret()) .map_err(|e| ErrorKind::Generic.context(e))?; - let seedfp = keystore - .encrypt_and_store_mnemonic(&SecretString::new(mnemonic.into_phrase())) - .await?; + let seedfp = keystore.encrypt_and_store_mnemonic(mnemonic).await?; println!("Seed fingerprint: {seedfp}"); diff --git a/zallet/src/commands/migrate_zcash_conf.rs b/zallet/src/commands/migrate_zcash_conf.rs index 3be9a912..959e3fea 100644 --- a/zallet/src/commands/migrate_zcash_conf.rs +++ b/zallet/src/commands/migrate_zcash_conf.rs @@ -22,10 +22,10 @@ use crate::{ impl AsyncRunnable for MigrateZcashConfCmd { async fn run(&self) -> Result<(), Error> { let conf = if self.conf.is_relative() { - if let Some(datadir) = self.datadir.as_ref() { + if let Some(datadir) = self.zcashd_datadir.as_ref() { datadir.join(&self.conf) } else { - default_data_dir() + zcashd_default_data_dir() .ok_or(ErrorKind::Generic)? .join(&self.conf) } @@ -178,7 +178,7 @@ impl Runnable for MigrateZcashConfCmd { } } -fn default_data_dir() -> Option { +pub(crate) fn zcashd_default_data_dir() -> Option { #[cfg(windows)] { use known_folders::{KnownFolder, get_known_folder_path}; diff --git a/zallet/src/commands/migrate_zcashd_wallet.rs b/zallet/src/commands/migrate_zcashd_wallet.rs new file mode 100644 index 00000000..46154823 --- /dev/null +++ b/zallet/src/commands/migrate_zcashd_wallet.rs @@ -0,0 +1,698 @@ +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; + +use abscissa_core::Runnable; + +use bip0039::{English, Mnemonic}; +use secp256k1::PublicKey; +use secrecy::SecretVec; +use shardtree::error::ShardTreeError; +use transparent::address::TransparentAddress; +use zaino_proto::proto::service::TxFilter; +use zaino_state::{FetchServiceError, LightWalletIndexer}; +use zcash_client_backend::data_api::{ + Account as _, AccountBirthday, AccountPurpose, WalletRead, WalletWrite as _, Zip32Derivation, + wallet::decrypt_and_store_transaction, +}; +use zcash_client_sqlite::error::SqliteClientError; +use zcash_keys::{ + encoding::AddressCodec, + keys::{ + DerivationError, UnifiedFullViewingKey, + zcashd::{PathParseError, ZcashdHdDerivation}, + }, +}; +use zcash_primitives::transaction::Transaction; +use zcash_protocol::consensus::{BlockHeight, BranchId, NetworkType, Parameters}; +use zewif_zcashd::{BDBDump, ZcashdDump, ZcashdParser, ZcashdWallet}; +use zip32::{AccountId, fingerprint::SeedFingerprint}; + +use crate::{ + cli::MigrateZcashdWalletCmd, + components::{chain_view::ChainView, database::Database, keystore::KeyStore}, + error::{Error, ErrorKind}, + fl, + prelude::*, + rosetta::to_chainstate, +}; + +use super::{AsyncRunnable, migrate_zcash_conf}; + +/// The ZIP 32 account identifier of the zcashd account used for maintaining legacy `getnewaddress` +/// and `z_getnewaddress` semantics after the zcashd v4.7.0 upgrade to support using +/// mnemonic-sourced HD derivation for all addresses in the wallet. +pub const ZCASHD_LEGACY_ACCOUNT: AccountId = AccountId::const_from_u32(0x7FFFFFFF); +/// A source string to identify an account as being derived from the randomly generated binary HD +/// seed used for Sapling key generation prior to the zcashd v4.7.0 upgrade. +pub const ZCASHD_LEGACY_SOURCE: &str = "zcashd_legacy"; +/// A source string to identify an account as being derived from the mnemonic HD seed used for +/// key derivation after the zcashd v4.7.0 upgrade. +pub const ZCASHD_MNEMONIC_SOURCE: &str = "zcashd_mnemonic"; + +impl AsyncRunnable for MigrateZcashdWalletCmd { + async fn run(&self) -> Result<(), Error> { + let config = APP.config(); + + // Start monitoring the chain. + let (chain_view, _chain_indexer_task_handle) = ChainView::new(&config).await?; + let db = Database::open(&config).await?; + let keystore = KeyStore::new(&config, db.clone())?; + + let wallet_path = if self.path.is_relative() { + if let Some(datadir) = self.zcashd_datadir.as_ref() { + datadir.join(&self.path) + } else { + migrate_zcash_conf::zcashd_default_data_dir() + .ok_or(ErrorKind::Generic)? + .join(&self.path) + } + } else { + self.path.to_path_buf() + }; + + let wallet = Self::dump_wallet(&wallet_path, self.allow_warnings)?; + + Self::migrate_zcashd_wallet( + db, + keystore, + chain_view, + wallet, + self.buffer_wallet_transactions, + ) + .await?; + + Ok(()) + } +} + +impl MigrateZcashdWalletCmd { + fn dump_wallet(path: &Path, allow_warnings: bool) -> Result { + info!("Parsing zcashd wallet at {}", path.display()); + + let db_dump = BDBDump::from_file(path).map_err(|e| MigrateError::Zewif { + error_type: ZewifError::BdbDump, + wallet_path: path.to_path_buf(), + error: e, + })?; + + let zcashd_dump = ZcashdDump::from_bdb_dump(&db_dump, allow_warnings).map_err(|e| { + MigrateError::Zewif { + error_type: ZewifError::ZcashdDump, + wallet_path: path.to_path_buf(), + error: e, + } + })?; + + let (zcashd_wallet, _unparsed_keys) = + ZcashdParser::parse_dump(&zcashd_dump, !allow_warnings).map_err(|e| { + MigrateError::Zewif { + error_type: ZewifError::ZcashdDump, + wallet_path: path.to_path_buf(), + error: e, + } + })?; + + info!("Wallet version: {}", zcashd_wallet.client_version()); + + Ok(zcashd_wallet) + } + + async fn chain_tip( + chain: &C, + ) -> Result, MigrateError> + where + MigrateError: From, + { + let tip_height = chain.get_latest_block().await?.height; + let chain_tip = if tip_height == 0 { + None + } else { + // TODO: this error should go away when we have a better chain data API + Some(BlockHeight::try_from(tip_height).map_err(|e| { + ErrorKind::Generic.context(fl!( + "err-migrate-wallet-invalid-chain-data", + err = e.to_string() + )) + })?) + }; + + Ok(chain_tip) + } + + async fn get_birthday( + chain: &C, + birthday_height: BlockHeight, + recover_until: Option, + ) -> Result + where + MigrateError: From, + { + // Fetch the tree state corresponding to the last block prior to the wallet's + // birthday height. + let chain_state = to_chainstate( + chain + .get_tree_state(zaino_proto::proto::service::BlockId { + height: u64::from(birthday_height.saturating_sub(1)), + hash: vec![], + }) + .await?, + )?; + + Ok(AccountBirthday::from_parts(chain_state, recover_until)) + } + + fn check_network( + zewif_network: zewif::Network, + network_type: NetworkType, + ) -> Result { + match (zewif_network, network_type) { + (zewif::Network::Main, NetworkType::Main) => Ok(()), + (zewif::Network::Test, NetworkType::Test) => Ok(()), + (zewif::Network::Regtest, NetworkType::Regtest) => Ok(()), + (wallet_network, db_network) => Err(MigrateError::NetworkMismatch { + wallet_network, + db_network, + }), + }?; + + Ok(network_type) + } + + fn parse_mnemonic(mnemonic: &str) -> Result, bip0039::Error> { + (!mnemonic.is_empty()) + .then(|| Mnemonic::::from_phrase(mnemonic)) + .transpose() + } + + async fn migrate_zcashd_wallet( + db: Database, + keystore: KeyStore, + chain_view: ChainView, + wallet: ZcashdWallet, + buffer_wallet_transactions: bool, + ) -> Result<(), MigrateError> { + let mut db_data = db.handle().await?; + let network_params = *db_data.params(); + Self::check_network(wallet.network(), network_params.network_type())?; + + // Obtain information about the current state of the chain, so that we can set the recovery + // height properly. + let chain_subscriber = chain_view.subscribe().await?.inner(); + let chain_tip = Self::chain_tip(&chain_subscriber).await?; + let sapling_activation = network_params + .activation_height(zcash_protocol::consensus::NetworkUpgrade::Sapling) + .expect("Sapling activation height is defined."); + + // Collect an index from txid to block height for all transactions known to the wallet that + // appear in the main chain. + let mut tx_heights = HashMap::new(); + for (txid, _) in wallet.transactions().iter() { + let tx_filter = TxFilter { + hash: txid.as_ref().to_vec(), + ..Default::default() + }; + #[allow(unused_must_use)] + match chain_subscriber.get_transaction(tx_filter).await { + Ok(raw_tx) => { + let tx_height = + BlockHeight::from(u32::try_from(raw_tx.height).map_err(|e| { + // TODO: this error should go away when we have a better chain data API + ErrorKind::Generic.context(fl!( + "err-migrate-wallet-invalid-chain-data", + err = e.to_string() + )) + })?); + tx_heights.insert( + txid, + (tx_height, buffer_wallet_transactions.then_some(raw_tx)), + ); + } + Err(FetchServiceError::TonicStatusError(status)) + if (status.code() as isize) == (tonic::Code::NotFound as isize) => + { + // Ignore any transactions that are not in the main chain. + } + other => { + // FIXME: we should be able to propagate this error, but at present Zaino is + // returning all sorts of errors as 500s. + dbg!(other); + } + } + } + info!("Wallet contains {} transactions", tx_heights.len()); + + // Since zcashd scans in linear order, we can reliably choose the earliest wallet + // transaction's mined height as the birthday height, so long as it is in the "stable" + // range. We don't have a good source of individual per-account birthday information at + // this point; once we've imported all of the transaction data into the wallet then we'll + // be able to choose per-account birthdays without difficulty. + let wallet_birthday = Self::get_birthday( + &chain_subscriber, + // Fall back to the chain tip height, and then Sapling activation as a last resort. If + // we have a birthday height, max() that with sapling activation; that will be the + // minimum possible wallet birthday that is relevant to future recovery scenarios. + tx_heights + .values() + .map(|(h, _)| h) + .min() + .copied() + .or(chain_tip) + .map_or(sapling_activation, |h| std::cmp::max(h, sapling_activation)), + chain_tip, + ) + .await?; + info!( + "Setting the wallet birthday to height {}", + wallet_birthday.height(), + ); + + let mnemonic_seed_data = match Self::parse_mnemonic(wallet.bip39_mnemonic().mnemonic())? { + Some(m) => Some(( + SecretVec::new(m.to_seed("").to_vec()), + keystore.encrypt_and_store_mnemonic(m).await?, + )), + None => None, + }; + + let legacy_transparent_account_uuid = if let Some((seed, _)) = mnemonic_seed_data.as_ref() { + // If there are any legacy transparent keys, create the legacy account. + if !wallet.keys().is_empty() { + let (account, _) = db_data.import_account_hd( + &format!( + "zcashd post-v4.7.0 legacy transparent account {}", + u32::from(ZCASHD_LEGACY_ACCOUNT), + ), + seed, + ZCASHD_LEGACY_ACCOUNT, + &wallet_birthday, + Some(ZCASHD_MNEMONIC_SOURCE), + )?; + + Some(account.id()) + } else { + None + } + } else { + None + }; + + let mnemonic_seed_fp = mnemonic_seed_data.as_ref().map(|(_, fp)| *fp); + + let legacy_seed_data = match wallet.legacy_hd_seed() { + Some(d) => Some(( + SecretVec::new(d.seed_data().to_vec()), + keystore + .encrypt_and_store_legacy_seed(&SecretVec::new(d.seed_data().to_vec())) + .await?, + )), + None => None, + }; + let legacy_transparent_account_uuid = + match (legacy_transparent_account_uuid, legacy_seed_data.as_ref()) { + (Some(uuid), _) => { + // We already had a mnemonic seed and have created the mnemonic-based legacy + // account, so we don't need to do anything. + Some(uuid) + } + (None, Some((seed, _))) if !wallet.keys().is_empty() => { + // In this case, we have the legacy seed, but no mnemonic seed was ever derived + // from it, so this is a pre-v4.7.0 wallet. We construct the mnemonic in the same + // fashion as zcashd, by using the legacy seed as entropy in the generation of the + // mnemonic seed, and then import that seed and the associated legacy account so + // that we have an account to act as the "bucket of funds" for the transparent keys + // derived from system randomness. + let mnemonic = zcash_keys::keys::zcashd::derive_mnemonic(seed) + .ok_or(ErrorKind::Generic.context(fl!("err-failed-seed-fingerprinting")))?; + + let seed = SecretVec::new(mnemonic.to_seed("").to_vec()); + keystore.encrypt_and_store_mnemonic(mnemonic).await?; + let (account, _) = db_data.import_account_hd( + &format!( + "zcashd post-v4.7.0 legacy transparent account {}", + u32::from(ZCASHD_LEGACY_ACCOUNT), + ), + &seed, + ZCASHD_LEGACY_ACCOUNT, + &wallet_birthday, + Some(ZCASHD_MNEMONIC_SOURCE), + )?; + + Some(account.id()) + } + _ => None, + }; + + let legacy_seed_fp = legacy_seed_data.map(|(_, fp)| fp); + + // Add unified accounts. The only source of unified accounts in zcashd is derivation from + // the mnemonic seed. + if wallet.unified_accounts().account_metadata.is_empty() { + info!("Wallet contains no unified accounts (z_getnewaccount was never used)"); + } else { + info!( + "Importing {} unified accounts (created with z_getnewaccount)", + wallet.unified_accounts().account_metadata.len() + ); + } + for (_, account) in wallet.unified_accounts().account_metadata.iter() { + // The only way that a unified account could be created in zcashd was + // to be derived from the mnemonic seed, so we can safely unwrap here. + let (seed, seed_fp) = mnemonic_seed_data + .as_ref() + .expect("mnemonic seed should be present"); + + assert_eq!( + SeedFingerprint::from_bytes(*account.seed_fingerprint().as_bytes()), + *seed_fp + ); + + let zip32_account_id = AccountId::try_from(account.zip32_account_id()) + .map_err(|_| MigrateError::AccountIdInvalid(account.zip32_account_id()))?; + + if db_data + .get_derived_account(&Zip32Derivation::new(*seed_fp, zip32_account_id, None))? + .is_none() + { + db_data.import_account_hd( + &format!( + "zcashd imported unified account {}", + account.zip32_account_id() + ), + seed, + zip32_account_id, + &wallet_birthday, + Some(ZCASHD_MNEMONIC_SOURCE), + )?; + } + } + + // Sapling keys may originate from: + // * The legacy HD seed, under a standard ZIP 32 key path + // * The mnemonic HD seed, under a standard ZIP 32 key path + // * The mnemonic HD seed, under the "legacy" account with an additional hardened path element + // * Zcashd Sapling spending key import + info!("Importing legacy Sapling keys"); // TODO: Expose how many there are in zewif-zcashd. + for (idx, key) in wallet.sapling_keys().keypairs().enumerate() { + // `zewif_zcashd` parses to an earlier version of the `sapling` types, so we + // must roundtrip through the byte representation into the version we need. + let extsk = sapling::zip32::ExtendedSpendingKey::from_bytes(&key.extsk().to_bytes()) + .map_err(|_| ()) //work around missing Debug impl + .expect("Sapling extsk encoding is stable across sapling-crypto versions"); + #[allow(deprecated)] + let extfvk = extsk.to_extended_full_viewing_key(); + let ufvk = + UnifiedFullViewingKey::from_sapling_extended_full_viewing_key(extfvk.clone())?; + + let key_seed_fp = key + .metadata() + .seed_fp() + .map(|seed_fp_bytes| SeedFingerprint::from_bytes(*seed_fp_bytes.as_bytes())); + + let derivation = key + .metadata() + .hd_keypath() + .map(|keypath| ZcashdHdDerivation::parse_hd_path(&network_params, keypath)) + .transpose()? + .zip(key_seed_fp) + .map(|(derivation, key_seed_fp)| match derivation { + ZcashdHdDerivation::Zip32 { account_id } => { + Zip32Derivation::new(key_seed_fp, account_id, None) + } + ZcashdHdDerivation::Post470LegacySapling { address_index } => { + Zip32Derivation::new( + key_seed_fp, + ZCASHD_LEGACY_ACCOUNT, + Some(address_index), + ) + } + }); + + // If the key is not associated with either of the seeds, treat it as a standalone + // imported key + if key_seed_fp != mnemonic_seed_fp && key_seed_fp != legacy_seed_fp { + keystore + .encrypt_and_store_standalone_sapling_key(&extsk) + .await?; + } + + let account_exists = match key_seed_fp.as_ref() { + Some(fp) => db_data + .get_derived_account(&Zip32Derivation::new( + *fp, + ZCASHD_LEGACY_ACCOUNT, + derivation.as_ref().and_then(|d| d.legacy_address_index()), + ))? + .is_some(), + None => db_data.get_account_for_ufvk(&ufvk)?.is_some(), + }; + + if !account_exists { + db_data.import_account_ufvk( + &format!("zcashd legacy sapling {}", idx), + &ufvk, + &wallet_birthday, + AccountPurpose::Spending { derivation }, + Some(ZCASHD_LEGACY_SOURCE), + )?; + } + } + + // TODO: Move this into zewif-zcashd once we're out of dependency version hell. + fn convert_key( + key: &zewif_zcashd::zcashd_wallet::transparent::KeyPair, + ) -> Result { + // Check the encoding of the pubkey + let _ = PublicKey::from_slice(key.pubkey().as_slice())?; + let compressed = key.pubkey().is_compressed(); + + let key = zcash_keys::keys::transparent::Key::der_decode( + &SecretVec::new(key.privkey().data().to_vec()), + compressed, + ) + .map_err(|_| { + ErrorKind::Generic.context(fl!( + "err-migrate-wallet-key-decoding", + err = "failed DER decoding" + )) + })?; + + Ok(key) + } + + info!("Importing legacy standalone transparent keys"); // TODO: Expose how many there are in zewif-zcashd. + for (i, key) in wallet.keys().keypairs().enumerate() { + let key = convert_key(key)?; + let pubkey = key.pubkey(); + debug!( + "[{i}] Importing key for address {}", + TransparentAddress::from_pubkey(&pubkey).encode(&network_params), + ); + + keystore + .encrypt_and_store_standalone_transparent_key(&key) + .await?; + + db_data.import_standalone_transparent_pubkey( + legacy_transparent_account_uuid.ok_or(MigrateError::SeedNotAvailable)?, + pubkey, + )?; + } + + // Since we've retrieved the raw transaction data anyway, preemptively store it for faster + // access to balance & to set priorities in the scan queue. + if buffer_wallet_transactions { + info!("Importing transactions"); + for (h, raw_tx) in tx_heights.values() { + let branch_id = BranchId::for_height(&network_params, *h); + if let Some(raw_tx) = raw_tx { + let tx = Transaction::read(&raw_tx.data[..], branch_id)?; + db_data.with_mut(|mut db| { + decrypt_and_store_transaction(&network_params, &mut db, &tx, Some(*h)) + })?; + } + } + } else { + info!("Not importing transactions (--buffer-wallet-transactions not set)"); + } + + Ok(()) + } +} + +impl Runnable for MigrateZcashdWalletCmd { + fn run(&self) { + self.run_on_runtime(); + } +} + +#[derive(Debug)] +pub(crate) enum ZewifError { + BdbDump, + ZcashdDump, +} + +#[derive(Debug)] +#[allow(dead_code)] +pub(crate) enum MigrateError { + Wrapped(Error), + Zewif { + error_type: ZewifError, + wallet_path: PathBuf, + error: anyhow::Error, + }, + SeedNotAvailable, + MnemonicInvalid(bip0039::Error), + KeyError(secp256k1::Error), + NetworkMismatch { + wallet_network: zewif::Network, + db_network: NetworkType, + }, + NetworkNotSupported(NetworkType), + Database(SqliteClientError), + Tree(ShardTreeError), + Io(std::io::Error), + Fetch(Box), + KeyDerivation(DerivationError), + HdPath(PathParseError), + AccountIdInvalid(u32), +} + +impl From for Error { + fn from(value: MigrateError) -> Self { + match value { + MigrateError::Wrapped(e) => e, + MigrateError::Zewif { + error_type, + wallet_path, + error, + } => Error::from(match error_type { + ZewifError::BdbDump => ErrorKind::Generic.context(fl!( + "err-migrate-wallet-bdb-parse", + path = wallet_path.to_str(), + err = error.to_string() + )), + ZewifError::ZcashdDump => ErrorKind::Generic.context(fl!( + "err-migrate-wallet-db-dump", + path = wallet_path.to_str(), + err = error.to_string() + )), + }), + MigrateError::SeedNotAvailable => { + Error::from(ErrorKind::Generic.context(fl!("err-migrate-wallet-seed-absent"))) + } + MigrateError::MnemonicInvalid(error) => Error::from(ErrorKind::Generic.context(fl!( + "err-migrate-wallet-invalid-mnemonic", + err = error.to_string() + ))), + MigrateError::KeyError(error) => Error::from(ErrorKind::Generic.context(fl!( + "err-migrate-wallet-key-decoding", + err = error.to_string() + ))), + MigrateError::NetworkMismatch { + wallet_network, + db_network, + } => Error::from(ErrorKind::Generic.context(fl!( + "err-migrate-wallet-network-mismatch", + wallet_network = String::from(wallet_network), + zallet_network = match db_network { + NetworkType::Main => "main", + NetworkType::Test => "test", + NetworkType::Regtest => "regtest", + } + ))), + MigrateError::NetworkNotSupported(_) => { + Error::from(ErrorKind::Generic.context(fl!("err-migrate-wallet-regtest"))) + } + MigrateError::Database(sqlite_client_error) => { + Error::from(ErrorKind::Generic.context(fl!( + "err-migrate-wallet-storage", + err = sqlite_client_error.to_string() + ))) + } + MigrateError::Tree(e) => Error::from( + ErrorKind::Generic + .context(fl!("err-migrate-wallet-data-parse", err = e.to_string())), + ), + MigrateError::Io(e) => Error::from( + ErrorKind::Generic + .context(fl!("err-migrate-wallet-data-parse", err = e.to_string())), + ), + MigrateError::Fetch(e) => Error::from( + ErrorKind::Generic.context(fl!("err-migrate-wallet-tx-fetch", err = e.to_string())), + ), + MigrateError::KeyDerivation(e) => Error::from( + ErrorKind::Generic.context(fl!("err-migrate-wallet-key-data", err = e.to_string())), + ), + MigrateError::HdPath(err) => Error::from(ErrorKind::Generic.context(fl!( + "err-migrate-wallet-data-parse", + err = format!("{:?}", err) + ))), + MigrateError::AccountIdInvalid(id) => Error::from(ErrorKind::Generic.context(fl!( + "err-migrate-wallet-invalid-account-id", + account_id = id + ))), + } + } +} + +impl From> for MigrateError { + fn from(e: ShardTreeError) -> Self { + Self::Tree(e) + } +} + +impl From for MigrateError { + fn from(e: SqliteClientError) -> Self { + Self::Database(e) + } +} + +impl From for MigrateError { + fn from(value: bip0039::Error) -> Self { + Self::MnemonicInvalid(value) + } +} + +impl From for MigrateError { + fn from(value: Error) -> Self { + MigrateError::Wrapped(value) + } +} + +impl From> for MigrateError { + fn from(value: abscissa_core::error::Context) -> Self { + MigrateError::Wrapped(value.into()) + } +} + +impl From for MigrateError { + fn from(value: std::io::Error) -> Self { + MigrateError::Io(value) + } +} + +impl From for MigrateError { + fn from(value: FetchServiceError) -> Self { + MigrateError::Fetch(Box::new(value)) + } +} + +impl From for MigrateError { + fn from(value: DerivationError) -> Self { + MigrateError::KeyDerivation(value) + } +} + +impl From for MigrateError { + fn from(value: PathParseError) -> Self { + MigrateError::HdPath(value) + } +} + +impl From for MigrateError { + fn from(value: secp256k1::Error) -> Self { + MigrateError::KeyError(value) + } +} diff --git a/zallet/src/components/database.rs b/zallet/src/components/database.rs index af967392..aed35513 100644 --- a/zallet/src/components/database.rs +++ b/zallet/src/components/database.rs @@ -70,7 +70,7 @@ impl Database { // any changes (including migrations, some of which make use of the network // params), to avoid leaving the database in an inconsistent state. We can // assume the presence of this table, as it's added by the initial migrations. - handle.with_raw(|conn| { + handle.with_raw(|conn, _| { let wallet_network_type = conn .query_row( "SELECT network_type FROM ext_zallet_db_wallet_metadata", @@ -127,7 +127,7 @@ impl Database { // an easy way to detect whether any migrations actually ran, so we check whether // the most recent entry matches the current version tuple, and only record an // entry if it doesn't. - handle.with_raw_mut(|conn| { + handle.with_raw_mut(|conn, _| { #[allow(clippy::const_is_empty)] let (git_revision, clean) = (!crate::build::COMMIT_HASH.is_empty()) .then_some((crate::build::COMMIT_HASH, crate::build::GIT_CLEAN)) diff --git a/zallet/src/components/database/connection.rs b/zallet/src/components/database/connection.rs index 358fc704..6c63e8a2 100644 --- a/zallet/src/components/database/connection.rs +++ b/zallet/src/components/database/connection.rs @@ -98,7 +98,7 @@ impl DbConnection { &self.params } - fn with( + pub(crate) fn with( &self, f: impl FnOnce(WalletDb<&rusqlite::Connection, Network, SystemClock, OsRng>) -> T, ) -> T { @@ -128,17 +128,20 @@ impl DbConnection { }) } - pub(crate) fn with_raw(&self, f: impl FnOnce(&rusqlite::Connection) -> T) -> T { + pub(crate) fn with_raw(&self, f: impl FnOnce(&rusqlite::Connection, &Network) -> T) -> T { tokio::task::block_in_place(|| { let _guard = self.lock.read().unwrap(); - f(self.inner.lock().unwrap().as_ref()) + f(self.inner.lock().unwrap().as_ref(), &self.params) }) } - pub(crate) fn with_raw_mut(&self, f: impl FnOnce(&mut rusqlite::Connection) -> T) -> T { + pub(crate) fn with_raw_mut( + &self, + f: impl FnOnce(&mut rusqlite::Connection, &Network) -> T, + ) -> T { tokio::task::block_in_place(|| { let _guard = self.lock.write().unwrap(); - f(self.inner.lock().unwrap().as_mut()) + f(self.inner.lock().unwrap().as_mut(), &self.params) }) } } @@ -307,8 +310,11 @@ impl WalletRead for DbConnection { &self, account: Self::AccountId, include_change: bool, + include_standalone: bool, ) -> Result>, Self::Error> { - self.with(|db_data| db_data.get_transparent_receivers(account, include_change)) + self.with(|db_data| { + db_data.get_transparent_receivers(account, include_change, include_standalone) + }) } fn get_transparent_balances( @@ -474,6 +480,15 @@ impl WalletWrite for DbConnection { }) } + #[cfg(feature = "zcashd-import")] + fn import_standalone_transparent_pubkey( + &mut self, + account: Self::AccountId, + pubkey: secp256k1::PublicKey, + ) -> Result<(), Self::Error> { + self.with_mut(|mut db_data| db_data.import_standalone_transparent_pubkey(account, pubkey)) + } + fn get_next_available_address( &mut self, account: Self::AccountId, diff --git a/zallet/src/components/database/tests.rs b/zallet/src/components/database/tests.rs index 8a255a49..216e75ef 100644 --- a/zallet/src/components/database/tests.rs +++ b/zallet/src/components/database/tests.rs @@ -57,6 +57,10 @@ fn verify_schema() { keystore::db::TABLE_LEGACY_SEEDS, #[cfg(zallet_build = "wallet")] keystore::db::TABLE_MNEMONICS, + #[cfg(zallet_build = "wallet")] + keystore::db::TABLE_STANDALONE_SAPLING_KEYS, + #[cfg(zallet_build = "wallet")] + keystore::db::TABLE_STANDALONE_TRANSPARENT_KEYS, ], ); diff --git a/zallet/src/components/json_rpc/methods/get_address_for_account.rs b/zallet/src/components/json_rpc/methods/get_address_for_account.rs index 9eda58fd..eaeab132 100644 --- a/zallet/src/components/json_rpc/methods/get_address_for_account.rs +++ b/zallet/src/components/json_rpc/methods/get_address_for_account.rs @@ -165,42 +165,63 @@ pub(crate) async fn call( }) } +fn map_address_generation_error( + e: AddressGenerationError, + account: &JsonValue, +) -> ErrorObjectOwned { + match e { + AddressGenerationError::InvalidTransparentChildIndex(diversifier_index) => { + LegacyCode::Wallet.with_message(format!( + "Error: diversifier index {} cannot generate an address with a transparent receiver.", + u128::from(diversifier_index), + )) + } + AddressGenerationError::InvalidSaplingDiversifierIndex(diversifier_index) => { + LegacyCode::Wallet.with_message(format!( + "Error: diversifier index {} cannot generate an address with a Sapling receiver.", + u128::from(diversifier_index), + )) + } + AddressGenerationError::DiversifierSpaceExhausted => LegacyCode::Wallet.with_static( + "Error: ran out of diversifier indices. Generate a new account with z_getnewaccount" + ), + AddressGenerationError::ReceiverTypeNotSupported(typecode) => match typecode { + unified::Typecode::P2sh => LegacyCode::Wallet.with_static( + "Error: P2SH addresses can not be created using this RPC method.", + ), + _ => LegacyCode::Wallet.with_message(format!( + "Error: receiver type {typecode:?} is not supported.", + )) + } + AddressGenerationError::KeyNotAvailable(typecode) => { + LegacyCode::Wallet.with_message(format!( + "Error: account {account} cannot generate a receiver component with type {typecode:?}.", + )) + } + AddressGenerationError::ShieldedReceiverRequired => { + LegacyCode::Wallet.with_static( + "Error: cannot generate an address containing no shielded receivers." + ) + } + AddressGenerationError::UnsupportedTransparentKeyScope(s) => { + LegacyCode::Wallet.with_message(format!( + "Error: Address generation is not supported for transparent key scope {s:?}", + )) + } + AddressGenerationError::Bip32DerivationError(e) => { + LegacyCode::Wallet.with_message(format!( + "An error occurred in BIP 32 address derivation: {e}" + )) + } + } +} + fn map_sqlite_error(e: SqliteClientError, account: &JsonValue) -> ErrorObjectOwned { match e { SqliteClientError::TransparentDerivation(error) => LegacyCode::Wallet.with_message(format!( "Error: failed to derive a transparent component: {error}", )), - SqliteClientError::AddressGeneration(e) => match e { - AddressGenerationError::InvalidTransparentChildIndex(diversifier_index) => { - LegacyCode::Wallet.with_message(format!( - "Error: diversifier index {} cannot generate an address with a transparent receiver.", - u128::from(diversifier_index), - )) - } - AddressGenerationError::InvalidSaplingDiversifierIndex(diversifier_index) => { - LegacyCode::Wallet.with_message(format!( - "Error: diversifier index {} cannot generate an address with a Sapling receiver.", - u128::from(diversifier_index), - )) - } - AddressGenerationError::DiversifierSpaceExhausted => LegacyCode::Wallet.with_static( - "Error: ran out of diversifier indices. Generate a new account with z_getnewaccount" - ), - AddressGenerationError::ReceiverTypeNotSupported(typecode) => match typecode { - unified::Typecode::P2sh => LegacyCode::Wallet.with_static( - "Error: P2SH addresses can not be created using this RPC method.", - ), - _ => LegacyCode::Wallet.with_message(format!( - "Error: receiver type {typecode:?} is not supported.", - )) - }, - AddressGenerationError::KeyNotAvailable(typecode) => LegacyCode::Wallet.with_message(format!( - "Error: account {account} cannot generate a receiver component with type {typecode:?}.", - )), - AddressGenerationError::ShieldedReceiverRequired => LegacyCode::Wallet.with_static( - "Error: cannot generate an address containing no shielded receivers." - ), - }, + SqliteClientError::AddressGeneration(e) => map_address_generation_error(e, account), SqliteClientError::AccountUnknown => LegacyCode::Wallet.with_message(format!( "Error: account {account} has not been generated by z_getnewaccount." )), diff --git a/zallet/src/components/json_rpc/methods/list_addresses.rs b/zallet/src/components/json_rpc/methods/list_addresses.rs index fbc3fe15..d0f2bc20 100644 --- a/zallet/src/components/json_rpc/methods/list_addresses.rs +++ b/zallet/src/components/json_rpc/methods/list_addresses.rs @@ -158,20 +158,20 @@ pub(crate) fn call(wallet: &DbConnection) -> Response { let addr = address_info.address(); match addr { Address::Transparent(_) | Address::Tex(_) => { - match address_info.transparent_key_scope() { - Some(TransparentKeyScope::EXTERNAL) => { + match address_info.source().transparent_key_scope() { + Some(&TransparentKeyScope::EXTERNAL) => { transparent_addresses.push(addr.encode(wallet.params())); } - Some(TransparentKeyScope::INTERNAL) => { + Some(&TransparentKeyScope::INTERNAL) => { transparent_change_addresses.push(addr.encode(wallet.params())); } - Some(TransparentKeyScope::EPHEMERAL) => { + Some(&TransparentKeyScope::EPHEMERAL) => { transparent_ephemeral_addresses.push(addr.encode(wallet.params())); } _ => { error!( "Unexpected {:?} for address {}", - address_info.transparent_key_scope(), + address_info.source().transparent_key_scope(), addr.encode(wallet.params()), ); return Err(RpcErrorCode::InternalError.into()); @@ -179,21 +179,39 @@ pub(crate) fn call(wallet: &DbConnection) -> Response { } } Address::Sapling(_) => sapling_addresses.push(addr.encode(wallet.params())), - Address::Unified(addr) => unified_addresses.push(UnifiedAddress { - diversifier_index: address_info.diversifier_index().into(), - receiver_types: addr - .receiver_types() - .into_iter() - .map(|r| match r { - unified::Typecode::P2pkh => "p2pkh".into(), - unified::Typecode::P2sh => "p2sh".into(), - unified::Typecode::Sapling => "sapling".into(), - unified::Typecode::Orchard => "orchard".into(), - unified::Typecode::Unknown(typecode) => format!("unknown({typecode})"), - }) - .collect(), - address: addr.encode(wallet.params()), - }), + Address::Unified(addr) => { + let address = addr.encode(wallet.params()); + unified_addresses.push(UnifiedAddress { + diversifier_index: match address_info.source() { + zcash_client_backend::data_api::AddressSource::Derived { + diversifier_index, + .. + } => diversifier_index.into(), + #[cfg(feature = "transparent-key-import")] + zcash_client_backend::data_api::AddressSource::Standalone => { + error!( + "Unified address {} lacks HD derivation information.", + address + ); + return Err(RpcErrorCode::InternalError.into()); + } + }, + receiver_types: addr + .receiver_types() + .into_iter() + .map(|r| match r { + unified::Typecode::P2pkh => "p2pkh".into(), + unified::Typecode::P2sh => "p2sh".into(), + unified::Typecode::Sapling => "sapling".into(), + unified::Typecode::Orchard => "orchard".into(), + unified::Typecode::Unknown(typecode) => { + format!("unknown({typecode})") + } + }) + .collect(), + address, + }) + } } } diff --git a/zallet/src/components/json_rpc/methods/list_unspent.rs b/zallet/src/components/json_rpc/methods/list_unspent.rs index 00ec43c0..30b37e0e 100644 --- a/zallet/src/components/json_rpc/methods/list_unspent.rs +++ b/zallet/src/components/json_rpc/methods/list_unspent.rs @@ -214,7 +214,7 @@ pub(crate) fn call( let is_watch_only = !matches!(account.purpose(), AccountPurpose::Spending { .. }); let utxos = wallet - .get_transparent_receivers(account_id, true) + .get_transparent_receivers(account_id, true, true) .map_err(|e| { RpcError::owned( LegacyCode::Database.into(), @@ -250,7 +250,7 @@ pub(crate) fn call( Some(format!("{e}")), ) })? - .is_some_and(|m| m.scope() == TransparentKeyScope::INTERNAL); + .is_some_and(|m| m.scope() == Some(TransparentKeyScope::INTERNAL)); unspent_outputs.push(UnspentOutput { txid: utxo.outpoint().txid().to_string(), diff --git a/zallet/src/components/json_rpc/methods/view_transaction.rs b/zallet/src/components/json_rpc/methods/view_transaction.rs index f24de09b..a4326736 100644 --- a/zallet/src/components/json_rpc/methods/view_transaction.rs +++ b/zallet/src/components/json_rpc/methods/view_transaction.rs @@ -351,7 +351,7 @@ pub(crate) async fn call( }; wallet - .with_raw(|conn| { + .with_raw(|conn, _| { conn.query_row( &format!( "SELECT txid, {output_prefix}_index, accounts.uuid, address, value @@ -391,7 +391,7 @@ pub(crate) async fn call( fallback_addr: impl FnOnce() -> Option, ) -> RpcResult> { Ok(wallet - .with_raw(|conn| { + .with_raw(|conn, _| { conn.query_row( "SELECT to_address FROM sent_notes @@ -497,7 +497,10 @@ pub(crate) async fn call( account_uuid, Some(address.encode(wallet.params())), wallet_scope.is_none(), - matches!(wallet_scope, Some(TransparentKeyScope::INTERNAL)), + // The outer `Some` indicates that we have address metadata; the inner + // `Option` is `None` for addresses associated with imported transparent + // spending keys. + wallet_scope == Some(Some(TransparentKeyScope::INTERNAL)), ) } }; @@ -794,7 +797,7 @@ pub(crate) async fn call( .map_err(|e| LegacyCode::Database.with_message(format!("Failed to compute fee: {e}")))?; #[cfg(zallet_build = "wallet")] - let accounts = wallet.with_raw(|conn| { + let accounts = wallet.with_raw(|conn, _| { let mut stmt = conn .prepare( "SELECT account_uuid, account_balance_delta diff --git a/zallet/src/components/json_rpc/methods/z_send_many.rs b/zallet/src/components/json_rpc/methods/z_send_many.rs index c41cf837..988f1114 100644 --- a/zallet/src/components/json_rpc/methods/z_send_many.rs +++ b/zallet/src/components/json_rpc/methods/z_send_many.rs @@ -10,6 +10,8 @@ use serde::{Deserialize, Serialize}; use serde_json::json; use zaino_state::FetchServiceSubscriber; use zcash_address::{ZcashAddress, unified}; +use zcash_client_backend::data_api::wallet::SpendingKeys; +use zcash_client_backend::proposal::Proposal; use zcash_client_backend::{ data_api::{ Account, @@ -22,7 +24,7 @@ use zcash_client_backend::{ wallet::OvkPolicy, zip321::{Payment, TransactionRequest}, }; -use zcash_client_sqlite::AccountUuid; +use zcash_client_sqlite::ReceivedNoteId; use zcash_keys::{address::Address, keys::UnifiedSpendingKey}; use zcash_proofs::prover::LocalTxProver; use zcash_protocol::{ @@ -79,7 +81,7 @@ pub(super) const PARAM_PRIVACY_POLICY_DESC: &str = #[allow(clippy::too_many_arguments)] pub(crate) async fn call( - wallet: DbHandle, + mut wallet: DbHandle, keystore: KeyStore, chain: FetchServiceSubscriber, fromaddress: String, @@ -160,10 +162,6 @@ pub(crate) async fn call( get_account_for_address(wallet.as_ref(), &address) } }?; - let derivation = account.source().key_derivation().ok_or_else(|| { - LegacyCode::InvalidAddressOrKey - .with_static("Invalid from address, no payment source found for address.") - })?; let privacy_policy = match privacy_policy.as_deref() { Some("LegacyCompat") => Err(LegacyCode::InvalidParameter @@ -190,64 +188,6 @@ pub(crate) async fn call( } }; - // Fetch spending key last, to avoid a keystore decryption if unnecessary. - let seed = keystore - .decrypt_seed(derivation.seed_fingerprint()) - .await - .map_err(|e| match e.kind() { - // TODO: Improve internal error types. - crate::error::ErrorKind::Generic if e.to_string() == "Wallet is locked" => { - LegacyCode::WalletUnlockNeeded.with_message(e.to_string()) - } - _ => LegacyCode::Database.with_message(e.to_string()), - })?; - let usk = UnifiedSpendingKey::from_seed( - wallet.params(), - seed.expose_secret(), - derivation.account_index(), - ) - .map_err(|e| LegacyCode::InvalidAddressOrKey.with_message(e.to_string()))?; - - Ok(( - Some(ContextInfo::new( - "z_sendmany", - json!({ - "fromaddress": fromaddress, - "amounts": amounts, - "minconf": minconf - }), - )), - run( - wallet, - chain, - account.id(), - request, - confirmations_policy, - privacy_policy, - usk, - ), - )) -} - -/// Construct and send the transaction, returning the resulting txid. -/// Errors in transaction construction will throw. -/// -/// Notes: -/// 1. #1159 Currently there is no limit set on the number of elements, which could -/// make the tx too large. -/// 2. #1360 Note selection is not optimal. -/// 3. #1277 Spendable notes are not locked, so an operation running in parallel -/// could also try to use them. -async fn run( - mut wallet: DbHandle, - chain: FetchServiceSubscriber, - spend_from_account: AccountUuid, - request: TransactionRequest, - confirmations_policy: ConfirmationsPolicy, - privacy_policy: PrivacyPolicy, - // TODO: Support legacy transparent pool of funds. https://github.com/zcash/wallet/issues/138 - usk: UnifiedSpendingKey, -) -> RpcResult { let params = *wallet.params(); // TODO: Fetch the real maximums within the account so we can detect correctly. @@ -318,6 +258,7 @@ async fn run( DustOutputPolicy::default(), APP.config().note_management.split_policy(), ); + // TODO: Once `zcash_client_backend` supports spending transparent coins arbitrarily, // consider using the privacy policy here to avoid selecting incompatible funds. This // would match what `zcashd` did more closely (though we might instead decide to let @@ -328,7 +269,7 @@ async fn run( let proposal = propose_transfer::<_, _, _, _, Infallible>( wallet.as_mut(), ¶ms, - spend_from_account, + account.id(), &input_selector, &change_strategy, request, @@ -382,15 +323,102 @@ async fn run( } } - let prover = LocalTxProver::bundled(); + let derivation = account.source().key_derivation().ok_or_else(|| { + LegacyCode::InvalidAddressOrKey + .with_static("Invalid from address, no payment source found for address.") + })?; + + // Fetch spending key last, to avoid a keystore decryption if unnecessary. + let seed = keystore + .decrypt_seed(derivation.seed_fingerprint()) + .await + .map_err(|e| match e.kind() { + // TODO: Improve internal error types. + crate::error::ErrorKind::Generic if e.to_string() == "Wallet is locked" => { + LegacyCode::WalletUnlockNeeded.with_message(e.to_string()) + } + _ => LegacyCode::Database.with_message(e.to_string()), + })?; + let usk = UnifiedSpendingKey::from_seed( + wallet.params(), + seed.expose_secret(), + derivation.account_index(), + ) + .map_err(|e| LegacyCode::InvalidAddressOrKey.with_message(e.to_string()))?; + + #[cfg(feature = "transparent-key-import")] + let standalone_keys = { + let mut keys = std::collections::HashMap::new(); + for step in proposal.steps() { + for input in step.transparent_inputs() { + if let Some(address) = input.txout().script_pubkey().address() { + let secret_key = keystore + .decrypt_standalone_transparent_key(&address) + .await + .map_err(|e| match e.kind() { + // TODO: Improve internal error types. + crate::error::ErrorKind::Generic + if e.to_string() == "Wallet is locked" => + { + LegacyCode::WalletUnlockNeeded.with_message(e.to_string()) + } + _ => LegacyCode::Database.with_message(e.to_string()), + })?; + keys.insert(address, secret_key); + } + } + } + keys + }; + + // TODO: verify that the proposal satisfies the requested privacy policy + + Ok(( + Some(ContextInfo::new( + "z_sendmany", + json!({ + "fromaddress": fromaddress, + "amounts": amounts, + "minconf": minconf + }), + )), + run( + wallet, + chain, + proposal, + SpendingKeys::new( + usk, + #[cfg(feature = "zcashd-import")] + standalone_keys, + ), + ), + )) +} +/// Construct and send the transaction, returning the resulting txid. +/// Errors in transaction construction will throw. +/// +/// Notes: +/// 1. #1159 Currently there is no limit set on the number of elements, which could +/// make the tx too large. +/// 2. #1360 Note selection is not optimal. +/// 3. #1277 Spendable notes are not locked, so an operation running in parallel +/// could also try to use them. +async fn run( + mut wallet: DbHandle, + chain: FetchServiceSubscriber, + proposal: Proposal, + spending_keys: SpendingKeys, +) -> RpcResult { + let prover = LocalTxProver::bundled(); let (wallet, txids) = crate::spawn_blocking!("z_sendmany prover", move || { + let params = *wallet.params(); create_proposed_transactions::<_, _, Infallible, _, Infallible, _>( wallet.as_mut(), ¶ms, &prover, &prover, - &usk, + &spending_keys, OvkPolicy::Sender, &proposal, ) diff --git a/zallet/src/components/keystore.rs b/zallet/src/components/keystore.rs index fb603e38..2fe1cf31 100644 --- a/zallet/src/components/keystore.rs +++ b/zallet/src/components/keystore.rs @@ -125,14 +125,22 @@ use tokio::{ }; use zip32::fingerprint::SeedFingerprint; +use crate::network::Network; use crate::{ config::ZalletConfig, error::{Error, ErrorKind}, - fl, }; use super::database::Database; +#[cfg(feature = "zcashd-import")] +use { + crate::fl, + sapling::zip32::{DiversifiableFullViewingKey, ExtendedSpendingKey}, + transparent::address::TransparentAddress, + zcash_keys::address::Address, +}; + pub(super) mod db; type RelockTask = (SystemTime, JoinHandle<()>); @@ -364,14 +372,14 @@ impl KeyStore { async fn with_db( &self, - f: impl FnOnce(&rusqlite::Connection) -> Result, + f: impl FnOnce(&rusqlite::Connection, &Network) -> Result, ) -> Result { self.db.handle().await?.with_raw(f) } async fn with_db_mut( &self, - f: impl FnOnce(&mut rusqlite::Connection) -> Result, + f: impl FnOnce(&mut rusqlite::Connection, &Network) -> Result, ) -> Result { self.db.handle().await?.with_raw_mut(f) } @@ -394,7 +402,7 @@ impl KeyStore { let now = ::time::OffsetDateTime::now_utc(); - self.with_db_mut(|conn| { + self.with_db_mut(|conn, _| { let mut stmt = conn .prepare( "INSERT INTO ext_zallet_keystore_age_recipients @@ -419,7 +427,7 @@ impl KeyStore { /// Fetches the age recipients for this wallet from the database. async fn recipients(&self) -> Result>, Error> { - self.with_db(|conn| { + self.with_db(|conn, _| { let mut stmt = conn .prepare( "SELECT recipient @@ -452,7 +460,7 @@ impl KeyStore { /// Lists the fingerprint of every seed available in the keystore. pub(crate) async fn list_seed_fingerprints(&self) -> Result, Error> { - self.with_db(|conn| { + self.with_db(|conn, _| { let mut stmt = conn .prepare( "SELECT hd_seed_fingerprint @@ -475,7 +483,7 @@ impl KeyStore { pub(crate) async fn list_legacy_seed_fingerprints( &self, ) -> Result, Error> { - self.with_db(|conn| { + self.with_db(|conn, _| { let mut stmt = conn .prepare( "SELECT hd_seed_fingerprint @@ -496,22 +504,19 @@ impl KeyStore { pub(crate) async fn encrypt_and_store_mnemonic( &self, - mnemonic: &SecretString, + mnemonic: Mnemonic, ) -> Result { let recipients = self.recipients().await?; - let seed_bytes = SecretVec::new( - Mnemonic::::from_phrase(mnemonic.expose_secret()) - .map_err(|e| ErrorKind::Generic.context(e))? - .to_seed("") - .to_vec(), - ); + let seed_bytes = SecretVec::new(mnemonic.to_seed("").to_vec()); let seed_fp = SeedFingerprint::from_seed(seed_bytes.expose_secret()).expect("valid length"); + // Take ownership of the memory of the mnemonic to ensure it will be correctly zeroized on drop + let mnemonic = SecretString::new(mnemonic.into_phrase()); let encrypted_mnemonic = encrypt_string(&recipients, mnemonic.expose_secret()) .map_err(|e| ErrorKind::Generic.context(e))?; - self.with_db_mut(|conn| { + self.with_db_mut(|conn, _| { conn.execute( "INSERT INTO ext_zallet_keystore_mnemonics VALUES (:hd_seed_fingerprint, :encrypted_mnemonic) @@ -529,7 +534,7 @@ impl KeyStore { Ok(seed_fp) } - #[allow(dead_code)] + #[cfg(feature = "zcashd-import")] pub(crate) async fn encrypt_and_store_legacy_seed( &self, legacy_seed: &SecretVec, @@ -542,7 +547,7 @@ impl KeyStore { let encrypted_legacy_seed = encrypt_legacy_seed_bytes(&recipients, legacy_seed) .map_err(|e| ErrorKind::Generic.context(e))?; - self.with_db_mut(|conn| { + self.with_db_mut(|conn, _| { conn.execute( "INSERT INTO ext_zallet_keystore_legacy_seeds VALUES (:hd_seed_fingerprint, :encrypted_legacy_seed) @@ -560,6 +565,64 @@ impl KeyStore { Ok(legacy_seed_fp) } + #[cfg(feature = "zcashd-import")] + pub(crate) async fn encrypt_and_store_standalone_sapling_key( + &self, + sapling_key: &ExtendedSpendingKey, + ) -> Result { + let recipients = self.recipients().await?; + + let dfvk = sapling_key.to_diversifiable_full_viewing_key(); + let encrypted_sapling_extsk = encrypt_standalone_sapling_key(&recipients, sapling_key) + .map_err(|e| ErrorKind::Generic.context(e))?; + + self.with_db_mut(|conn, _| { + conn.execute( + "INSERT INTO ext_zallet_keystore_standalone_sapling_keys + VALUES (:dfvk, :encrypted_sapling_extsk) + ON CONFLICT (dfvk) DO NOTHING ", + named_params! { + ":dfvk": &dfvk.to_bytes(), + ":encrypted_sapling_extsk": encrypted_sapling_extsk, + }, + ) + .map_err(|e| ErrorKind::Generic.context(e))?; + Ok(()) + }) + .await?; + + Ok(dfvk) + } + + #[cfg(feature = "zcashd-import")] + pub(crate) async fn encrypt_and_store_standalone_transparent_key( + &self, + key: &zcash_keys::keys::transparent::Key, + ) -> Result<(), Error> { + let recipients = self.recipients().await?; + + let encrypted_transparent_key = + encrypt_standalone_transparent_privkey(&recipients, key.secret()) + .map_err(|e| ErrorKind::Generic.context(e))?; + + self.with_db_mut(|conn, _| { + conn.execute( + "INSERT INTO ext_zallet_keystore_standalone_transparent_keys + VALUES (:pubkey, :encrypted_key_bytes) + ON CONFLICT (pubkey) DO NOTHING ", + named_params! { + ":pubkey": &key.pubkey().serialize(), + ":encrypted_key_bytes": encrypted_transparent_key, + }, + ) + .map_err(|e| ErrorKind::Generic.context(e))?; + Ok(()) + }) + .await?; + + Ok(()) + } + /// Decrypts the mnemonic phrase corresponding to the given seed fingerprint. async fn decrypt_mnemonic(&self, seed_fp: &SeedFingerprint) -> Result { // Acquire a read lock on the identities for decryption. @@ -569,7 +632,7 @@ impl KeyStore { } let encrypted_mnemonic = self - .with_db(|conn| { + .with_db(|conn, _| { Ok(conn .query_row( "SELECT encrypted_mnemonic @@ -603,6 +666,42 @@ impl KeyStore { Ok(seed) } + + #[cfg(feature = "zcashd-import")] + pub(crate) async fn decrypt_standalone_transparent_key( + &self, + address: &TransparentAddress, + ) -> Result { + // Acquire a read lock on the identities for decryption. + let identities = self.identities.read().await; + if identities.is_empty() { + return Err(ErrorKind::Generic.context("Wallet is locked").into()); + } + + let encrypted_key_bytes = self + .with_db(|conn, network| { + let addr_str = Address::Transparent(*address).encode(network); + let encrypted_key_bytes = conn + .query_row( + "SELECT encrypted_key_bytes + FROM ext_zallet_keystore_standalone_transparent_keys ztk + JOIN addresses a ON ztk.pubkey = a.imported_transparent_receiver_pubkey + WHERE a.cached_transparent_receiver_address = :address", + named_params! { + ":address": addr_str, + }, + |row| row.get::<_, Vec>("encrypted_key_bytes"), + ) + .map_err(|e| ErrorKind::Generic.context(e))?; + Ok(encrypted_key_bytes) + }) + .await?; + + let secret_key = + decrypt_standalone_transparent_privkey(&identities, &encrypted_key_bytes[..])?; + + Ok(secret_key) + } } fn encrypt_string( @@ -619,20 +718,6 @@ fn encrypt_string( Ok(ciphertext) } -fn encrypt_legacy_seed_bytes( - recipients: &[Box], - seed: &SecretVec, -) -> Result, age::EncryptError> { - let encryptor = age::Encryptor::with_recipients(recipients.iter().map(|r| r.as_ref() as _))?; - - let mut ciphertext = Vec::with_capacity(seed.expose_secret().len()); - let mut writer = encryptor.wrap_output(&mut ciphertext)?; - writer.write_all(seed.expose_secret())?; - writer.finish()?; - - Ok(ciphertext) -} - fn decrypt_string( identities: &[Box], ciphertext: &[u8], @@ -657,3 +742,73 @@ fn decrypt_string( Ok(mnemonic) } + +#[cfg(any(feature = "transparent-key-import", feature = "zcashd-import"))] +fn encrypt_secret( + recipients: &[Box], + secret: &SecretVec, +) -> Result, age::EncryptError> { + let encryptor = age::Encryptor::with_recipients(recipients.iter().map(|r| r.as_ref() as _))?; + + let mut ciphertext = Vec::with_capacity(secret.expose_secret().len()); + let mut writer = encryptor.wrap_output(&mut ciphertext)?; + writer.write_all(secret.expose_secret())?; + writer.finish()?; + + Ok(ciphertext) +} + +#[cfg(feature = "zcashd-import")] +fn encrypt_legacy_seed_bytes( + recipients: &[Box], + seed: &SecretVec, +) -> Result, age::EncryptError> { + encrypt_secret(recipients, seed) +} + +#[cfg(feature = "zcashd-import")] +fn encrypt_standalone_sapling_key( + recipients: &[Box], + key: &ExtendedSpendingKey, +) -> Result, age::EncryptError> { + let secret = SecretVec::new(key.to_bytes().to_vec()); + encrypt_secret(recipients, &secret) +} + +#[cfg(feature = "transparent-key-import")] +fn encrypt_standalone_transparent_privkey( + recipients: &[Box], + key: &secp256k1::SecretKey, +) -> Result, age::EncryptError> { + let secret = SecretVec::new(key.secret_bytes().to_vec()); + encrypt_secret(recipients, &secret) +} + +#[cfg(feature = "transparent-key-import")] +fn decrypt_standalone_transparent_privkey( + identities: &[Box], + ciphertext: &[u8], +) -> Result { + let decryptor = age::Decryptor::new(ciphertext).map_err(|e| ErrorKind::Generic.context(e))?; + + // The plaintext is always shorter than the ciphertext. Over-allocating the initial + // string ensures that no internal re-allocations occur that might leave plaintext + // bytes strewn around the heap. + let mut buf = Vec::with_capacity(ciphertext.len()); + let res = decryptor + .decrypt(identities.iter().map(|i| i.as_ref() as _)) + .map_err(|e| ErrorKind::Generic.context(e))? + .read_to_end(&mut buf); + + // We intentionally do not use `?` on the decryption expression because doing so in + // the case of a partial failure could result in part of the secret data being read + // into `buf`, which would not then be properly zeroized. Instead, we take ownership + // of the buffer in construction of a `SecretVec` to ensure that the memory is + // zeroed out when we raise the error on the following line. + let buf_secret = SecretVec::new(buf); + res.map_err(|e| ErrorKind::Generic.context(e))?; + let secret_key = secp256k1::SecretKey::from_slice(buf_secret.expose_secret()) + .map_err(|e| ErrorKind::Generic.context(e))?; + + Ok(secret_key) +} diff --git a/zallet/src/components/keystore/db.rs b/zallet/src/components/keystore/db.rs index d2e2ecc0..1cee8372 100644 --- a/zallet/src/components/keystore/db.rs +++ b/zallet/src/components/keystore/db.rs @@ -53,7 +53,7 @@ CREATE TABLE ext_zallet_keystore_mnemonics ( ) "#; -/// Stores encrypted raw HD seeds. +/// Stores encrypted raw HD seeds. These are likely to only be produced via `zcashd` wallet import. /// /// ### Columns /// @@ -70,3 +70,41 @@ CREATE TABLE ext_zallet_keystore_legacy_seeds ( encrypted_legacy_seed BLOB NOT NULL ) "#; + +/// Stores encrypted standalone Sapling spending keys. +/// +/// ### Columns +/// +/// - `dfvk` is the [`DiversifiableFullViewingKey`] derived from the spending key. +/// - `encrypted_sapling_extsk` is a [ZIP 32]-encoded [`ExtendedFullViewingKey`] in an +/// [age encrypted file]. +/// +/// [ZIP 32]: https://zips.z.cash/zip-0032 +/// [`DiversifiableFullViewingKey`]: sapling::zip32::DiversifiableFullViewingKey +/// [`ExtendedFullViewingKey`]: sapling::zip32::ExtendedFullViewingKey +/// [age encrypted file]: https://c2sp.org/age#encrypted-file-format +pub(crate) const TABLE_STANDALONE_SAPLING_KEYS: &str = r#" +CREATE TABLE ext_zallet_keystore_standalone_sapling_keys ( + dfvk BLOB NOT NULL UNIQUE, + encrypted_sapling_extsk BLOB NOT NULL +) +"#; + +/// Stores encrypted standalone transparent secret keys. +/// +/// ### Columns +/// +/// - `pubkey` is the [`PublicKey`] derived from the spending key. +/// - `encrypted_transparent_privkey` is a [`SecretKey`] serialized in its compressed form in an +/// [age encrypted file]. +/// +/// [ZIP 32]: https://zips.z.cash/zip-0032 +/// [`Publickey`]: secp256k1::PublicKey +/// [`SecretKey`]: secp256k1::SecretKey +/// [age encrypted file]: https://c2sp.org/age#encrypted-file-format +pub(crate) const TABLE_STANDALONE_TRANSPARENT_KEYS: &str = r#" +CREATE TABLE ext_zallet_keystore_standalone_transparent_keys ( + pubkey BLOB NOT NULL UNIQUE, + encrypted_transparent_privkey BLOB NOT NULL +) +"#; diff --git a/zallet/src/components/keystore/db/migrations/initial_setup.rs b/zallet/src/components/keystore/db/migrations/initial_setup.rs index d4f46cfa..dc1bf3c6 100644 --- a/zallet/src/components/keystore/db/migrations/initial_setup.rs +++ b/zallet/src/components/keystore/db/migrations/initial_setup.rs @@ -38,6 +38,14 @@ impl RusqliteMigration for Migration { CREATE TABLE ext_zallet_keystore_legacy_seeds ( hd_seed_fingerprint BLOB NOT NULL UNIQUE, encrypted_legacy_seed BLOB NOT NULL + ); + CREATE TABLE ext_zallet_keystore_standalone_sapling_keys ( + dfvk BLOB NOT NULL UNIQUE, + encrypted_sapling_extsk BLOB NOT NULL + ); + CREATE TABLE ext_zallet_keystore_standalone_transparent_keys ( + pubkey BLOB NOT NULL UNIQUE, + encrypted_transparent_privkey BLOB NOT NULL );", )?; Ok(()) diff --git a/zallet/src/components/sync.rs b/zallet/src/components/sync.rs index 31cb2185..01ee4f6f 100644 --- a/zallet/src/components/sync.rs +++ b/zallet/src/components/sync.rs @@ -432,7 +432,7 @@ async fn poll_transparent( let addresses = db_data .get_account_ids()? .into_iter() - .map(|account| db_data.get_transparent_receivers(account, true)) + .map(|account| db_data.get_transparent_receivers(account, true, true)) .collect::, _>>()? .into_iter() .flat_map(|m| m.into_keys().map(|addr| addr.encode(params))) @@ -493,6 +493,10 @@ async fn data_requests( for request in requests { match request { TransactionDataRequest::GetStatus(txid) => { + if txid.is_null() { + continue; + } + info!("Getting status of {txid}"); let status = match chain.get_raw_transaction(txid.to_string(), Some(1)).await { // TODO: Zaino should have a Rust API for fetching tx details, @@ -521,6 +525,10 @@ async fn data_requests( db_data.set_transaction_status(txid, status)?; } TransactionDataRequest::Enhancement(txid) => { + if txid.is_null() { + continue; + } + info!("Enhancing {txid}"); let tx = match chain.get_raw_transaction(txid.to_string(), Some(1)).await { // TODO: Zaino should have a Rust API for fetching tx details, diff --git a/zallet/src/lib.rs b/zallet/src/lib.rs index aaf13179..a818b5d1 100644 --- a/zallet/src/lib.rs +++ b/zallet/src/lib.rs @@ -27,6 +27,9 @@ pub mod network; mod prelude; mod task; +#[cfg(feature = "zcashd-import")] +mod rosetta; + // Needed for the `Component` derive to work. use abscissa_core::{Application, Version, component}; diff --git a/zallet/src/rosetta.rs b/zallet/src/rosetta.rs new file mode 100644 index 00000000..19f6ab61 --- /dev/null +++ b/zallet/src/rosetta.rs @@ -0,0 +1,69 @@ +//! Conversion utilities for adapting between `zebra`, `zaino`, and `zcash_client_backend` types. + +use incrementalmerkletree::frontier::CommitmentTree; +use orchard::tree::MerkleHashOrchard; +use sapling::Node; +use std::io; +use zcash_client_backend::data_api::chain::ChainState; +use zcash_primitives::{block::BlockHash, merkle_tree::read_commitment_tree}; + +pub(crate) fn to_chainstate( + ts: zaino_proto::proto::service::TreeState, +) -> Result { + let mut hash_bytes = hex::decode(&ts.hash).map_err(|e| { + io::Error::new( + io::ErrorKind::InvalidData, + format!("Block hash is not valid hex: {:?}", e), + ) + })?; + // Zcashd hex strings for block hashes are byte-reversed. + hash_bytes.reverse(); + + Ok(ChainState::new( + ts.height + .try_into() + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Invalid block height"))?, + BlockHash::try_from_slice(&hash_bytes).ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidData, "Invalid block hash length.") + })?, + sapling_tree(&ts.sapling_tree)?.to_frontier(), + orchard_tree(&ts.orchard_tree)?.to_frontier(), + )) +} + +/// Deserializes and returns the Sapling note commitment tree field of the tree state. +pub(crate) fn sapling_tree( + sapling_tree_str: &str, +) -> io::Result> { + if sapling_tree_str.is_empty() { + Ok(CommitmentTree::empty()) + } else { + let sapling_tree_bytes = hex::decode(sapling_tree_str).map_err(|e| { + io::Error::new( + io::ErrorKind::InvalidData, + format!("Hex decoding of Sapling tree bytes failed: {:?}", e), + ) + })?; + read_commitment_tree::( + &sapling_tree_bytes[..], + ) + } +} + +pub fn orchard_tree( + orchard_tree_str: &str, +) -> io::Result> { + if orchard_tree_str.is_empty() { + Ok(CommitmentTree::empty()) + } else { + let orchard_tree_bytes = hex::decode(orchard_tree_str).map_err(|e| { + io::Error::new( + io::ErrorKind::InvalidData, + format!("Hex decoding of Orchard tree bytes failed: {:?}", e), + ) + })?; + read_commitment_tree::( + &orchard_tree_bytes[..], + ) + } +} diff --git a/zallet/tests/acceptance.rs b/zallet/tests/acceptance.rs index 5ed1b07e..52b6e175 100644 --- a/zallet/tests/acceptance.rs +++ b/zallet/tests/acceptance.rs @@ -18,7 +18,11 @@ use once_cell::sync::Lazy; use tempfile::tempdir; #[cfg(zallet_build = "wallet")] -use {abscissa_core::fs::File, age::secrecy::ExposeSecret, std::io::Write}; +use { + abscissa_core::fs::File, + age::secrecy::ExposeSecret, + std::io::{BufRead, Write}, +}; /// Executes your application binary via `cargo run`. /// @@ -97,8 +101,7 @@ fn setup_new_wallet() { .capture_stderr() .run(); let stderr = cmd.stderr(); - stderr.expect_regex(".*Finished.*"); - stderr.expect_regex(".*Running.*"); + wait_until_running(stderr); stderr.expect_regex(".*Creating empty database.*"); cmd.wait().unwrap().expect_code(0); } @@ -112,8 +115,7 @@ fn setup_new_wallet() { .capture_stderr() .run(); let stderr = cmd.stderr(); - stderr.expect_regex(".*Finished.*"); - stderr.expect_regex(".*Running.*"); + wait_until_running(stderr); stderr.expect_regex(".*Applying latest database migrations.*"); cmd.wait().unwrap().expect_code(0); } @@ -129,3 +131,11 @@ fn setup_new_wallet() { cmd.wait().unwrap().expect_code(1); } } + +#[cfg(zallet_build = "wallet")] +fn wait_until_running(stderr: &mut Stderr) { + let mut buf = String::new(); + while !buf.contains("Running") { + stderr.read_line(&mut buf).unwrap(); + } +} diff --git a/zallet/tests/cmd/migrate_zcash_conf_mainnet.toml b/zallet/tests/cmd/migrate_zcash_conf_mainnet.toml index 6e0ad0e1..81a68420 100644 --- a/zallet/tests/cmd/migrate_zcash_conf_mainnet.toml +++ b/zallet/tests/cmd/migrate_zcash_conf_mainnet.toml @@ -1,5 +1,5 @@ bin.name = "zallet" -args = "migrate-zcash-conf --datadir ./ -o - --this-is-alpha-code-and-you-will-need-to-redo-the-migration-later" +args = "migrate-zcash-conf --zcashd-datadir ./ -o - --this-is-alpha-code-and-you-will-need-to-redo-the-migration-later" stdin = "" stdout = """ # Zallet configuration file diff --git a/zallet/tests/cmd/migrate_zcash_conf_regtest.toml b/zallet/tests/cmd/migrate_zcash_conf_regtest.toml index f24ae072..941ac75f 100644 --- a/zallet/tests/cmd/migrate_zcash_conf_regtest.toml +++ b/zallet/tests/cmd/migrate_zcash_conf_regtest.toml @@ -1,5 +1,5 @@ bin.name = "zallet" -args = "migrate-zcash-conf --datadir ./ -o - --this-is-alpha-code-and-you-will-need-to-redo-the-migration-later" +args = "migrate-zcash-conf --zcashd-datadir ./ -o - --this-is-alpha-code-and-you-will-need-to-redo-the-migration-later" stdin = "" stdout = """ # Zallet configuration file diff --git a/zallet/tests/cmd/migrate_zcash_conf_testnet.toml b/zallet/tests/cmd/migrate_zcash_conf_testnet.toml index aa7d3ac9..c18445f5 100644 --- a/zallet/tests/cmd/migrate_zcash_conf_testnet.toml +++ b/zallet/tests/cmd/migrate_zcash_conf_testnet.toml @@ -1,5 +1,5 @@ bin.name = "zallet" -args = "migrate-zcash-conf --datadir ./ -o - --this-is-alpha-code-and-you-will-need-to-redo-the-migration-later" +args = "migrate-zcash-conf --zcashd-datadir ./ -o - --this-is-alpha-code-and-you-will-need-to-redo-the-migration-later" stdin = "" stdout = """ # Zallet configuration file