diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index 5e72254..0000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,8 +0,0 @@ -# The Valar Group librustzcash fork gates all Ironwood (NU7 / V6 transaction) -# functionality behind `cfg(zcash_unstable = "nu6.3")`. Set it for every build -# (including dependencies) so the Ironwood code paths — notably -# `create_orchard_to_ironwood_transaction`, used by `wallet migrate-to-ironwood` -# — are compiled in. The fork registers this cfg via `check-cfg`, so it does not -# produce "unexpected cfg" warnings. -[build] -rustflags = ["--cfg", "zcash_unstable=\"nu6.3\""] diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..8a4f72b --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +version: 2 +updates: + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d0dd277..8ca57f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,10 @@ name: CI checks on: [push, pull_request] +# Restrict the default GITHUB_TOKEN to read-only; jobs that need more opt in below. +permissions: + contents: read + jobs: build: name: Build on ${{ matrix.os }} @@ -33,13 +37,29 @@ jobs: - name: Verify working directory is clean (excluding lockfile) run: git diff --exit-code ':!Cargo.lock' + regtest-support: + name: Build, test, and lint with regtest_support + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build with regtest_support + run: cargo build --features regtest_support + - name: Test with regtest_support + run: cargo test --features regtest_support + - name: Clippy with regtest_support + run: cargo clippy --all-targets --features regtest_support -- -D warnings + clippy: name: Clippy runs-on: ubuntu-latest + # auguwu/clippy-action writes clippy results as check runs. + permissions: + contents: read + checks: write steps: - uses: actions/checkout@v4 - name: Run Clippy - uses: auguwu/clippy-action@1.3.0 + uses: auguwu/clippy-action@1.5.0 with: token: ${{ secrets.GITHUB_TOKEN }} working-directory: ${{ inputs.target }} diff --git a/.github/workflows/speed.yml b/.github/workflows/speed.yml index 639888b..363d427 100644 --- a/.github/workflows/speed.yml +++ b/.github/workflows/speed.yml @@ -22,7 +22,7 @@ jobs: persist-credentials: false - name: Build binary run: cargo build --release - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: zcash-devtool path: | @@ -43,7 +43,7 @@ jobs: steps: - uses: bencherdev/bencher@main - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: zcash-devtool - run: chmod +x zcash-devtool diff --git a/Cargo.lock b/Cargo.lock index ed0060b..e0067fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,9 +32,9 @@ dependencies = [ [[package]] name = "age" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf640be7658959746f1f0f2faab798f6098a9436a8e18e148d18bc9875e13c4b" +checksum = "a07d86e4272c093c88caf7864a2d09af52a5159180848ca4832a3cdbd7d014d5" dependencies = [ "age-core", "base64 0.21.7", @@ -47,7 +47,7 @@ dependencies = [ "lazy_static", "nom 7.1.3", "pin-project", - "rand 0.8.5", + "rand 0.8.6", "rust-embed", "scrypt", "sha2 0.10.9", @@ -70,7 +70,7 @@ dependencies = [ "hkdf", "io_tee", "nom 7.1.3", - "rand 0.8.5", + "rand 0.8.6", "secrecy 0.10.3", "sha2 0.10.9", "tempfile", @@ -160,9 +160,9 @@ dependencies = [ [[package]] name = "amplify_num" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99bcb75a2982047f733547042fc3968c0f460dfcf7d90b90dea3b2744580e9ad" +checksum = "afed304556696656d2d71495e1e5f2c4b524a3fb6eb0f2f3778ffc482a40b8a8" dependencies = [ "wasm-bindgen", ] @@ -245,9 +245,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "arbitrary" @@ -257,9 +257,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" [[package]] name = "arc-swap" -version = "1.9.0" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07d1f37ff60921c83bdfc7407723bdefe89b44b98a9b772f225c8f9d67141a6" +checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" dependencies = [ "rustversion", ] @@ -272,7 +272,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -283,9 +283,9 @@ checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[package]] name = "arti-client" @@ -307,7 +307,7 @@ dependencies = [ "libc", "once_cell", "postage", - "rand 0.9.2", + "rand 0.9.4", "safelog", "serde", "thiserror 2.0.18", @@ -353,9 +353,9 @@ checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" [[package]] name = "asn1-rs" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", @@ -374,7 +374,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "synstructure", ] @@ -386,7 +386,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -397,9 +397,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-compression" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" +checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" dependencies = [ "compression-codecs", "compression-core", @@ -415,7 +415,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -461,6 +461,15 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -469,9 +478,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "av-scenechange" @@ -509,18 +518,18 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d" +checksum = "e7178fe5f7d460b13895ebb9dcb28a3a6216d2df2574a0806cb51b555d297f38" dependencies = [ "arrayvec", ] [[package]] name = "axum" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" dependencies = [ "axum-core", "bytes", @@ -653,8 +662,8 @@ dependencies = [ "quote", "regex", "rustc-hash 1.1.0", - "shlex", - "syn 2.0.117", + "shlex 1.3.0", + "syn 2.0.118", "which 4.4.2", ] @@ -666,7 +675,7 @@ checksum = "568b6890865156d9043af490d4c4081c385dd68ea10acd6ca15733d511e6b51c" dependencies = [ "hmac 0.12.1", "pbkdf2", - "rand 0.8.5", + "rand 0.8.6", "sha2 0.10.9", "unicode-normalization", "zeroize", @@ -732,24 +741,24 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] name = "bitstream-io" -version = "4.9.0" +version = "4.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" +checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f" dependencies = [ - "core2", + "no_std_io2", ] [[package]] name = "bitvec" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" dependencies = [ "funty", "radium", @@ -787,7 +796,7 @@ checksum = "e0b121a9fe0df916e362fb3271088d071159cdf11db0e4182d02152850756eff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -829,9 +838,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" +checksum = "2f3f6da4992df95bbcd9af42a6c7dcb994498fc9048230405f3b36ff7cd3f145" dependencies = [ "borsh-derive", "bytes", @@ -840,15 +849,15 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59" +checksum = "3ae8fb4fb5740e4b2c4884ff95f5f32f5e8479db1e8fd8eb49ddbe09eb09bb7c" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -872,26 +881,26 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79" dependencies = [ "memchr", "regex-automata", - "serde", + "serde_core", ] [[package]] name = "built" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" +checksum = "5c0e531d93d39c34eef561e929e8a7f86d77a5af08aac4f6d6e39976c51858e9" [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "by_address" @@ -941,9 +950,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" [[package]] name = "caret" @@ -983,14 +992,14 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.58" +version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", "jobserver", "libc", - "shlex", + "shlex 2.0.1", ] [[package]] @@ -1046,9 +1055,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "js-sys", @@ -1109,9 +1118,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -1131,14 +1140,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1188,7 +1197,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81411967c50ee9a1fc11365f8c585f863a22a9697c89239c452292c40ba79b0d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.0", "block", "core-foundation 0.10.1", "core-graphics-types", @@ -1209,9 +1218,9 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "compact_str" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" +checksum = "7fd622ebbb56a5b2ccb651b32b911cdeb2a9b4b11776b2473bf26a26a286244e" dependencies = [ "castaway", "cfg-if 1.0.4", @@ -1223,9 +1232,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" +checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf" dependencies = [ "compression-core", "flate2", @@ -1236,9 +1245,9 @@ dependencies = [ [[package]] name = "compression-core" -version = "0.4.31" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" +checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" [[package]] name = "concurrent-queue" @@ -1249,6 +1258,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "const-crc32-nostd" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808ac43170e95b11dd23d78aa9eaac5bea45776a602955552c4e833f3f0f823d" + [[package]] name = "const-oid" version = "0.9.6" @@ -1329,7 +1344,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.0", "core-foundation 0.10.1", "libc", ] @@ -1359,15 +1374,6 @@ dependencies = [ "objc", ] -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - [[package]] name = "corez" version = "0.1.1" @@ -1394,9 +1400,9 @@ dependencies = [ [[package]] name = "crc-catalog" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crc32fast" @@ -1450,6 +1456,12 @@ dependencies = [ "itertools 0.13.0", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -1499,7 +1511,7 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.0", "crossterm_winapi", "futures-core", "mio", @@ -1589,7 +1601,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1656,7 +1668,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1670,7 +1682,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1692,7 +1704,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1703,14 +1715,14 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" [[package]] name = "der" @@ -1764,17 +1776,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd5f2b7218a51c827a11d22d1439b598121fac94bf9b99452e4afffe512d78c9" dependencies = [ "heck", - "indexmap 2.13.0", - "itertools 0.14.0", + "indexmap 2.14.0", + "itertools 0.15.0", "proc-macro-crate", "proc-macro2", "quote", "sha3", "strum 0.27.2", - "syn 2.0.117", + "syn 2.0.118", "void", ] +[[package]] +name = "derive-getters" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74ef43543e701c01ad77d3a5922755c6a1d71b22d942cb8042be4994b380caff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "derive_builder_core_fork_arti" version = "0.11.2" @@ -1825,7 +1848,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.117", + "syn 2.0.118", "unicode-xid", ] @@ -1884,13 +1907,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1922,22 +1945,22 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "dynosaur" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12303417f378f29ba12cb12fc78a9df0d8e16ccb1ad94abf04d48d96bdda532" +checksum = "fdb33e6854ea615b65faf9c40d4ea9c4de9e9fa5d6772ad6ce57950a6da3957d" dependencies = [ "dynosaur_derive", ] [[package]] name = "dynosaur_derive" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0713d5c1d52e774c5cd7bb8b043d7c0fc4f921abfb678556140bfbe6ab2364" +checksum = "5ac0893f103ae7bf50181323d748d505f27b6255777c28b502030bdaf8749f4c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2012,9 +2035,9 @@ dependencies = [ [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "elliptic-curve" @@ -2057,7 +2080,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2069,7 +2092,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2099,7 +2122,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2115,7 +2138,7 @@ dependencies = [ [[package]] name = "equihash" version = "0.3.0" -source = "git+https://github.com/valargroup/librustzcash?rev=5df7f06486c7c1cab317a069809b7147857e37ee#5df7f06486c7c1cab317a069809b7147857e37ee" +source = "git+https://github.com/zcash/librustzcash?rev=4b13be3c2ce6f91a4b386508aed0799358daadb1#4b13be3c2ce6f91a4b386508aed0799358daadb1" dependencies = [ "blake2b_simd", "corez", @@ -2166,7 +2189,7 @@ dependencies = [ [[package]] name = "f4jumble" version = "0.1.1" -source = "git+https://github.com/valargroup/librustzcash?rev=5df7f06486c7c1cab317a069809b7147857e37ee#5df7f06486c7c1cab317a069809b7147857e37ee" +source = "git+https://github.com/zcash/librustzcash?rev=4b13be3c2ce6f91a4b386508aed0799358daadb1#4b13be3c2ce6f91a4b386508aed0799358daadb1" dependencies = [ "blake2b_simd", ] @@ -2185,29 +2208,15 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "fax" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" -dependencies = [ - "fax_derive", -] - -[[package]] -name = "fax_derive" -version = "0.2.0" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" [[package]] name = "fdeflate" @@ -2250,13 +2259,12 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.27" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" dependencies = [ "cfg-if 1.0.4", "libc", - "libredox", ] [[package]] @@ -2399,6 +2407,41 @@ dependencies = [ "num-traits", ] +[[package]] +name = "frost-core" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81ef2787af391c7e8bedc037a3b9ea03dde803fbd93e778e6bb369547800e5cd" +dependencies = [ + "byteorder", + "const-crc32-nostd", + "derive-getters", + "document-features", + "hex", + "itertools 0.14.0", + "postcard", + "rand_core 0.6.4", + "serde", + "serdect", + "thiserror 2.0.18", + "visibility", + "zeroize", + "zeroize_derive", +] + +[[package]] +name = "frost-rerandomized" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4c5cedd2426728adef2c0b1720f57676354c473836d1ccc50d0f0d1c91942b" +dependencies = [ + "derive-getters", + "document-features", + "frost-core", + "hex", + "rand_core 0.6.4", +] + [[package]] name = "fs-mistrust" version = "0.12.0" @@ -2486,7 +2529,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2538,7 +2581,7 @@ dependencies = [ "g2poly", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2597,34 +2640,31 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if 1.0.4", "libc", "r-efi 6.0.0", - "wasip2", - "wasip3", ] [[package]] name = "getset" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf0fc11e47561d47397154977bc219f4cf809b2974facc3ccb3b89e2436f912" +checksum = "6cf442baaabe4213ce7d1239afc26c039180b6456da2cededa316ae2c8a77a77" dependencies = [ - "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "gif" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" dependencies = [ "color_quant", "weezl", @@ -2656,9 +2696,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.13" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" dependencies = [ "atomic-waker", "bytes", @@ -2666,7 +2706,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.13.0", + "indexmap 2.14.0", "slab", "tokio", "tokio-util", @@ -2698,7 +2738,7 @@ dependencies = [ "halo2_proofs", "lazy_static", "pasta_curves", - "rand 0.8.5", + "rand 0.8.6", "sinsemilla", "subtle", "uint", @@ -2739,6 +2779,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -2770,6 +2819,12 @@ dependencies = [ "foldhash 0.2.0", ] +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + [[package]] name = "hashlink" version = "0.10.0" @@ -2779,6 +2834,20 @@ dependencies = [ "hashbrown 0.15.5", ] +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -2841,9 +2910,9 @@ checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" [[package]] name = "http" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" dependencies = [ "bytes", "itoa", @@ -2886,9 +2955,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15" [[package]] name = "humantime-serde" @@ -2911,9 +2980,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.8.1" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" dependencies = [ "atomic-waker", "bytes", @@ -2926,7 +2995,6 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -3014,7 +3082,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.117", + "syn 2.0.118", "unic-langid", ] @@ -3028,7 +3096,7 @@ dependencies = [ "i18n-config", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3055,12 +3123,6 @@ dependencies = [ "cc", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "ident_case" version = "1.0.1" @@ -3103,9 +3165,9 @@ dependencies = [ [[package]] name = "imgref" -version = "1.12.0" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" +checksum = "89194689a993ab15268672e99e7b0e19da2da3268ac682e8f02d29d4d1434cd7" [[package]] name = "incrementalmerkletree" @@ -3115,7 +3177,7 @@ checksum = "30821f91f0fa8660edca547918dc59812893b497d07c1144f326f07fdd94aba9" dependencies = [ "either", "proptest", - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", ] @@ -3142,12 +3204,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -3163,20 +3225,20 @@ dependencies = [ [[package]] name = "inotify" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199" +checksum = "533e68a5842e734946fe159fb03fc9bbbb254f590dd0d8ad321ae5ff7beca2c1" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.0", "inotify-sys", "libc", ] [[package]] name = "inotify-sys" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +checksum = "9ea94e891b3606826e9c998be69ddca42247dad8ad50b1649a5cb7e1c9ae06fd" dependencies = [ "libc", ] @@ -3200,7 +3262,7 @@ dependencies = [ "indoc", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3211,7 +3273,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3235,9 +3297,9 @@ dependencies = [ [[package]] name = "inventory" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8062b737e5389949f477d4760a2ebbff0c366f97798f2419b8d8f366363d3342" +checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" dependencies = [ "rustversion", ] @@ -3302,6 +3364,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -3320,11 +3391,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.92" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc4c90f45aa2e6eacbe8645f77fdea542ac97a494bcd117a67df9ff4d611f995" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ - "once_cell", + "cfg-if 1.0.4", + "futures-util", "wasm-bindgen", ] @@ -3362,9 +3434,9 @@ dependencies = [ [[package]] name = "kqueue" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +checksum = "273c0752728918e0ac4976f2b275b6fefb9ecd400585dec929419f3844cd87b5" dependencies = [ "kqueue-sys", "libc", @@ -3372,11 +3444,11 @@ dependencies = [ [[package]] name = "kqueue-sys" -version = "1.0.4" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.13.0", "libc", ] @@ -3395,12 +3467,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "lebe" version = "0.5.3" @@ -3409,15 +3475,15 @@ checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" [[package]] name = "libc" -version = "0.2.183" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libfuzzer-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" +checksum = "a9fd2f41a1cba099f79a0b6b6c35656cf7c03351a7bae8ff0f28f25270f929d2" dependencies = [ "arbitrary", "cc", @@ -3435,18 +3501,18 @@ dependencies = [ [[package]] name = "liblzma" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6033b77c21d1f56deeae8014eb9fbe7bdf1765185a6c508b5ca82eeaed7f899" +checksum = "45aec2360b3933207e27908049d8e4df4e476b58180afb1e56b2a4fb72efe4ba" dependencies = [ "liblzma-sys", ] [[package]] name = "liblzma-sys" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f2db66f3268487b5033077f266da6777d057949b8f93c8ad82e441df25e6186" +checksum = "a046c7f353ba30f810545151e04f63545833803f5b86ee3ddf1517247fe560a5" dependencies = [ "cc", "libc", @@ -3461,14 +3527,11 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.15" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" dependencies = [ - "bitflags 2.11.0", "libc", - "plain", - "redox_syscall 0.7.3", ] [[package]] @@ -3511,9 +3574,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "loop9" @@ -3569,15 +3632,15 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "memmap2" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" dependencies = [ "libc", ] @@ -3659,9 +3722,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "log", @@ -3735,11 +3798,20 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "no_std_io2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003" +dependencies = [ + "memchr", +] + [[package]] name = "nokhwa" -version = "0.10.10" +version = "0.10.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cae50786bfa1214ed441f98addbea51ca1b9aaa9e4bf5369cda36654b3efaa" +checksum = "9d63f10b450319a0ace7aa8e0e25477d1fdb345313a97e220e886175539a1dbb" dependencies = [ "flume", "image", @@ -3753,19 +3825,20 @@ dependencies = [ [[package]] name = "nokhwa-bindings-linux" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bd666aaa41d14357817bd9a981773a73c4d00b34d344cfc244e47ebd397b1ec" +checksum = "bb67e22201a53322291740ca064b20eaaade7222ef0349f312d9b37b004e1984" dependencies = [ + "libc", "nokhwa-core", "v4l", ] [[package]] name = "nokhwa-bindings-macos" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de78eb4a2d47a68f490899aa0516070d7a972f853ec2bb374ab53be0bd39b60f" +checksum = "f70d3908ea68324e44a6b3a0f885aa59e433fb1f6678839d09e0df7d226fb42d" dependencies = [ "block", "cocoa-foundation", @@ -3780,9 +3853,9 @@ dependencies = [ [[package]] name = "nokhwa-bindings-windows" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899799275c93ef69bbe8cb888cf6f8249abe751cbc50be5299105022aec14a1c" +checksum = "5be28886bad8abcec3655c1f24b965b4cb596a72b23164c910c54439ce55d2a4" dependencies = [ "nokhwa-core", "once_cell", @@ -3791,9 +3864,9 @@ dependencies = [ [[package]] name = "nokhwa-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109975552bbd690894f613bce3d408222911e317197c72b2e8b9a1912dc261ae" +checksum = "b1cba20bebd3bd9ae22f9273ade5bbe49da3e047c8512b53fbaf8b4b9c80d496" dependencies = [ "bytes", "image", @@ -3844,7 +3917,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.0", "inotify", "kqueue", "libc", @@ -3861,7 +3934,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.0", ] [[package]] @@ -3884,9 +3957,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "c863e9ab5e7bf9c99ba75e1050f1e4d624ae87ed3532d6238ffbdc7b585dbbe6" dependencies = [ "num-integer", "num-traits", @@ -3903,7 +3976,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "smallvec", "zeroize", ] @@ -3922,7 +3995,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3995,7 +4068,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4014,7 +4087,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.0", ] [[package]] @@ -4077,8 +4150,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "orchard" -version = "0.14.0" -source = "git+https://github.com/zcash/orchard?rev=0eaea4ebb5aa4ba2b6cf4afa0e1c9b5e8c03273a#0eaea4ebb5aa4ba2b6cf4afa0e1c9b5e8c03273a" +version = "0.15.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8e277dd4b46f5d06deae3ffb8af1a951e8622368f028c2a4d6fe59339566403" dependencies = [ "aes", "bitvec", @@ -4098,7 +4172,7 @@ dependencies = [ "nonempty", "pasta_curves", "proptest", - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", "reddsa", "serde", @@ -4200,7 +4274,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if 1.0.4", "libc", - "redox_syscall 0.5.18", + "redox_syscall", "smallvec", "windows-link 0.2.1", ] @@ -4226,7 +4300,7 @@ dependencies = [ "ff", "group", "lazy_static", - "rand 0.8.5", + "rand 0.8.6", "static_assertions", "subtle", ] @@ -4257,7 +4331,7 @@ dependencies = [ [[package]] name = "pczt" version = "0.7.0" -source = "git+https://github.com/valargroup/librustzcash?rev=5df7f06486c7c1cab317a069809b7147857e37ee#5df7f06486c7c1cab317a069809b7147857e37ee" +source = "git+https://github.com/zcash/librustzcash?rev=4b13be3c2ce6f91a4b386508aed0799358daadb1#4b13be3c2ce6f91a4b386508aed0799358daadb1" dependencies = [ "blake2b_simd", "bls12_381", @@ -4310,7 +4384,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset", - "indexmap 2.13.0", + "indexmap 2.14.0", ] [[package]] @@ -4321,7 +4395,7 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset", "hashbrown 0.15.5", - "indexmap 2.13.0", + "indexmap 2.14.0", ] [[package]] @@ -4352,7 +4426,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared 0.11.3", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -4375,7 +4449,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4388,7 +4462,7 @@ dependencies = [ "phf_shared 0.13.1", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4411,22 +4485,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4435,12 +4509,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "pkcs1" version = "0.7.5" @@ -4464,15 +4532,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "plain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "plotters" @@ -4508,7 +4570,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.0", "crc32fast", "fdeflate", "flate2", @@ -4550,6 +4612,7 @@ dependencies = [ "cobs", "embedded-io 0.4.0", "embedded-io 0.6.1", + "heapless", "serde", ] @@ -4575,7 +4638,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4594,7 +4657,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93980406f12d9f8140ed5abe7155acb10bb1e69ea55c88960b9c2f117445ef96" dependencies = [ "equivalent", - "indexmap 2.13.0", + "indexmap 2.14.0", "serde", ] @@ -4604,7 +4667,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.8+spec-1.1.0", + "toml_edit 0.25.12+spec-1.1.0", ] [[package]] @@ -4626,7 +4689,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4640,21 +4703,21 @@ dependencies = [ [[package]] name = "profiling" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" +checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4665,10 +4728,10 @@ checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.11.0", + "bitflags 2.13.0", "lazy_static", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "rand_chacha 0.3.1", "rand_xorshift", "regex-syntax", @@ -4679,9 +4742,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" dependencies = [ "bytes", "prost-derive", @@ -4689,9 +4752,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7" +checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" dependencies = [ "heck", "itertools 0.14.0", @@ -4702,28 +4765,28 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.117", + "syn 2.0.118", "tempfile", ] [[package]] name = "prost-derive" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "prost-types" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" +checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a" dependencies = [ "prost", ] @@ -4762,9 +4825,9 @@ dependencies = [ [[package]] name = "pxfm" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" +checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" [[package]] name = "qoi" @@ -4795,9 +4858,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quote" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -4822,9 +4885,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -4833,9 +4896,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -4886,7 +4949,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" dependencies = [ "num-traits", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -4924,7 +4987,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.0", "cassowary", "compact_str", "crossterm", @@ -4966,7 +5029,7 @@ dependencies = [ "num-traits", "paste", "profiling", - "rand 0.9.2", + "rand 0.9.4", "rand_chacha 0.9.0", "simd_helpers", "thiserror 2.0.18", @@ -4991,9 +5054,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", @@ -5020,19 +5083,20 @@ dependencies = [ [[package]] name = "reddsa" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78a5191930e84973293aa5f532b513404460cd2216c1cfb76d08748c15b40b02" +checksum = "4784b85c8bfd17b36b86e664e6e504ecdb586001086ee23749e4a633bbb84832" dependencies = [ "blake2b_simd", "byteorder", + "frost-rerandomized", "group", "hex", "jubjub", "pasta_curves", "rand_core 0.6.4", "serde", - "thiserror 1.0.69", + "thiserror 2.0.18", "zeroize", ] @@ -5054,16 +5118,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "redox_syscall" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" -dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.0", ] [[package]] @@ -5094,14 +5149,14 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "regex" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", "memchr", @@ -5122,9 +5177,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "rend" @@ -5233,13 +5288,13 @@ dependencies = [ [[package]] name = "rpassword" -version = "7.4.0" +version = "7.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d4c8b64f049c6721ec8ccec37ddfc3d641c4a7fca57e8f2a89de509c73df39" +checksum = "2da316a15f47e3d053de9cb2c439650bd8fa4aaeb9365f2e5f27f492ff73c196" dependencies = [ "libc", "rtoolbox", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5276,12 +5331,12 @@ dependencies = [ [[package]] name = "rtoolbox" -version = "0.0.3" +version = "0.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cc970b249fbe527d6e02e0a227762c9108b2f49d81094fe357ffc6d14d7f6f" +checksum = "50a0e551c1e27e1731aba276dbeaeac73f53c7cd34d1bda485d02bd1e0f36844" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5290,7 +5345,7 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -5320,7 +5375,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.117", + "syn 2.0.118", "walkdir", ] @@ -5336,15 +5391,15 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.41.0" +version = "1.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ce901f9a19d251159075a4c37af514c3b8ef99c22e02dd8c19161cf397ee94a" +checksum = "be2a24f50780bc85f09cc6ac299bdf1424302742d77221106859c9d8b102126a" dependencies = [ "arrayvec", "borsh", "bytes", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "rkyv", "serde", "serde_json", @@ -5359,9 +5414,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] name = "rustc_version" @@ -5387,7 +5442,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -5400,7 +5455,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.0", "errno", "libc", "linux-raw-sys 0.12.1", @@ -5409,9 +5464,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.37" +version = "0.23.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" dependencies = [ "log", "once_cell", @@ -5424,18 +5479,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.10" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "ring", "rustls-pki-types", @@ -5530,7 +5585,7 @@ dependencies = [ "lazy_static", "memuse", "proptest", - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", "redjubjub", "subtle", @@ -5679,9 +5734,9 @@ checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" @@ -5720,7 +5775,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5735,9 +5790,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -5757,9 +5812,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -5774,7 +5829,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.13.0", + "indexmap 2.14.0", "schemars 0.9.0", "schemars 1.2.1", "serde_core", @@ -5792,7 +5847,17 @@ dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", +] + +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", ] [[package]] @@ -5830,9 +5895,9 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" dependencies = [ "digest 0.10.7", "keccak", @@ -5853,7 +5918,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "359e552886ae54d1642091645980d83f7db465fd9b5b0248e3680713c1773388" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.13.0", "either", "incrementalmerkletree", "tracing", @@ -5876,6 +5941,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + [[package]] name = "signal-hook" version = "0.3.18" @@ -5951,9 +6022,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "slab" @@ -5986,15 +6057,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", "windows-sys 0.61.2", @@ -6113,7 +6184,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6125,7 +6196,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6147,9 +6218,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -6170,7 +6241,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6200,7 +6271,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", "rustix 1.1.4", "windows-sys 0.61.2", @@ -6232,7 +6303,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6243,7 +6314,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6302,9 +6373,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "serde_core", @@ -6338,9 +6409,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.50.0" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -6354,13 +6425,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6375,9 +6446,9 @@ dependencies = [ [[package]] name = "tokio-socks" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" +checksum = "a7e2948f60dbe26b35f2c7fb74ac2854c1fddded0fe9d7548fcc674a246f7615" dependencies = [ "either", "futures-util", @@ -6437,9 +6508,9 @@ version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.0", "serde_core", - "serde_spanned 1.1.0", + "serde_spanned 1.1.1", "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", @@ -6466,9 +6537,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] @@ -6479,7 +6550,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.0", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", @@ -6489,23 +6560,23 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.8+spec-1.1.0" +version = "0.25.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16bff38f1d86c47f9ff0647e6838d7bb362522bdf44006c7068c2b1e606f1f3c" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ - "indexmap 2.13.0", - "toml_datetime 1.1.0+spec-1.1.0", + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.0", + "winnow 1.0.3", ] [[package]] name = "toml_parser" -version = "1.1.0+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.0", + "winnow 1.0.3", ] [[package]] @@ -6516,9 +6587,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tonic" @@ -6561,7 +6632,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6586,7 +6657,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.117", + "syn 2.0.118", "tempfile", "tonic-build", ] @@ -6618,7 +6689,7 @@ dependencies = [ "itertools 0.14.0", "libc", "paste", - "rand 0.9.2", + "rand 0.9.4", "rand_chacha 0.9.0", "serde", "slab", @@ -6651,7 +6722,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79ba1b43f22fab2daee3e0c902f1455b3aed8e086b2d83d8c60b36523b173d25" dependencies = [ "amplify", - "bitflags 2.11.0", + "bitflags 2.13.0", "bytes", "caret", "derive-deftly", @@ -6659,7 +6730,7 @@ dependencies = [ "educe", "itertools 0.14.0", "paste", - "rand 0.9.2", + "rand 0.9.4", "smallvec", "thiserror 2.0.18", "tor-basic-utils", @@ -6704,7 +6775,7 @@ dependencies = [ "futures", "oneshot-fused-workaround", "postage", - "rand 0.9.2", + "rand 0.9.4", "safelog", "serde", "thiserror 2.0.18", @@ -6759,7 +6830,7 @@ dependencies = [ "once_cell", "oneshot-fused-workaround", "pin-project", - "rand 0.9.2", + "rand 0.9.4", "retry-error", "safelog", "serde", @@ -6918,7 +6989,7 @@ dependencies = [ "oneshot-fused-workaround", "paste", "postage", - "rand 0.9.2", + "rand 0.9.4", "rusqlite", "safelog", "scopeguard", @@ -6998,7 +7069,7 @@ dependencies = [ "oneshot-fused-workaround", "pin-project", "postage", - "rand 0.9.2", + "rand 0.9.4", "safelog", "serde", "strum 0.27.2", @@ -7034,7 +7105,7 @@ dependencies = [ "humantime", "itertools 0.14.0", "paste", - "rand 0.9.2", + "rand 0.9.4", "safelog", "serde", "signature", @@ -7059,7 +7130,7 @@ dependencies = [ "derive_more", "downcast-rs", "paste", - "rand 0.9.2", + "rand 0.9.4", "rsa", "signature", "ssh-key", @@ -7090,7 +7161,7 @@ dependencies = [ "humantime", "inventory", "itertools 0.14.0", - "rand 0.9.2", + "rand 0.9.4", "safelog", "serde", "signature", @@ -7156,7 +7227,7 @@ dependencies = [ "educe", "getrandom 0.3.4", "hex", - "rand 0.9.2", + "rand 0.9.4", "rand_chacha 0.9.0", "rand_core 0.6.4", "rand_core 0.9.5", @@ -7230,13 +7301,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "638b4e6507e3786488859d3c463fa73addbad4f788806c6972603727e527672e" dependencies = [ "async-trait", - "bitflags 2.11.0", + "bitflags 2.13.0", "derive_more", "futures", "humantime", "itertools 0.14.0", "num_enum", - "rand 0.9.2", + "rand 0.9.4", "serde", "strum 0.27.2", "thiserror 2.0.18", @@ -7259,7 +7330,7 @@ checksum = "1dbc32d89e7ea2e2799168d0c453061647a727e39fc66f52e1bcb4c38c8dc433" dependencies = [ "amplify", "base64ct", - "bitflags 2.11.0", + "bitflags 2.13.0", "cipher", "derive-deftly", "derive_builder_fork_arti", @@ -7351,7 +7422,7 @@ dependencies = [ "oneshot-fused-workaround", "pin-project", "postage", - "rand 0.9.2", + "rand 0.9.4", "rand_core 0.9.5", "safelog", "slotmap-careful", @@ -7404,7 +7475,7 @@ version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54cc2b365bf5881b4380059e0636cc40e1fa18a1b3b050f78ce322c95139d467" dependencies = [ - "rand 0.9.2", + "rand 0.9.4", "serde", "tor-basic-utils", "tor-linkspec", @@ -7509,7 +7580,7 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", - "indexmap 2.13.0", + "indexmap 2.14.0", "pin-project-lite", "slab", "sync_wrapper", @@ -7551,7 +7622,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7611,7 +7682,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad06847b7afb65c7866a36664b75c40b895e318cea4f71299f013fb22965329d" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7622,7 +7693,7 @@ checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7654,7 +7725,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90" dependencies = [ - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", ] [[package]] @@ -7669,9 +7740,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "uint" @@ -7736,9 +7807,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "unicode-truncate" @@ -7812,11 +7883,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.0" +version = "1.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" dependencies = [ - "getrandom 0.4.2", + "getrandom 0.4.3", "js-sys", "serde_core", "wasm-bindgen", @@ -7879,7 +7950,7 @@ checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -7974,36 +8045,27 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ "wit-bindgen", ] [[package]] name = "wasix" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1757e0d1f8456693c7e5c6c629bdb54884e032aa0bb53c155f6a39f94440d332" +checksum = "ae86f02046da16a333a9129d31451423e1657737ecdafed4193838a5f54c5cfe" dependencies = [ "wasi", ] [[package]] name = "wasm-bindgen" -version = "0.2.115" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6523d69017b7633e396a89c5efab138161ed5aafcbc8d3e5c5a42ae38f50495a" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if 1.0.4", "once_cell", @@ -8015,9 +8077,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.115" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3a6c758eb2f701ed3d052ff5737f5bfe6614326ea7f3bbac7156192dc32e67" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -8025,60 +8087,26 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.115" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "921de2737904886b52bcbb237301552d05969a6f9c40d261eb0533c8b055fedf" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.115" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93e946af942b58934c604527337bad9ae33ba1d5c6900bbb41c2c07c2364a93" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap 2.13.0", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.11.0", - "hashbrown 0.15.5", - "indexmap 2.13.0", - "semver", -] - [[package]] name = "weak-table" version = "0.3.2" @@ -8087,9 +8115,9 @@ checksum = "323f4da9523e9a669e1eaf9c6e763892769b1d38c623913647bfdc1532fe4549" [[package]] name = "web-sys" -version = "0.3.92" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84cde8507f4d7cfcb1185b8cb5890c494ffea65edbe1ba82cfd63661c805ed94" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" dependencies = [ "js-sys", "wasm-bindgen", @@ -8097,9 +8125,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" dependencies = [ "rustls-pki-types", ] @@ -8124,9 +8152,9 @@ dependencies = [ [[package]] name = "which" -version = "8.0.2" +version = "8.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81995fafaaaf6ae47a7d0cc83c67caf92aeb7e5331650ae6ff856f7c0c60c459" +checksum = "48d7cd18d4acb58fb3cdfe9ea54e6cd96a4e7d4cc45c56338b236e82dad47248" dependencies = [ "libc", ] @@ -8261,7 +8289,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -8272,7 +8300,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -8537,100 +8565,18 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen" -version = "0.51.0" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap 2.13.0", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.11.0", - "indexmap 2.13.0", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.13.0", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "wsl" @@ -8704,7 +8650,7 @@ dependencies = [ "pczt", "prost", "qrcode", - "rand 0.8.5", + "rand 0.8.6", "ratatui", "rayon", "ripemd 0.1.3", @@ -8751,8 +8697,8 @@ dependencies = [ [[package]] name = "zcash_address" -version = "0.12.0" -source = "git+https://github.com/valargroup/librustzcash?rev=5df7f06486c7c1cab317a069809b7147857e37ee#5df7f06486c7c1cab317a069809b7147857e37ee" +version = "0.13.0-pre.0" +source = "git+https://github.com/zcash/librustzcash?rev=4b13be3c2ce6f91a4b386508aed0799358daadb1#4b13be3c2ce6f91a4b386508aed0799358daadb1" dependencies = [ "bech32 0.11.1", "bs58", @@ -8765,7 +8711,7 @@ dependencies = [ [[package]] name = "zcash_client_backend" version = "0.23.0" -source = "git+https://github.com/valargroup/librustzcash?rev=5df7f06486c7c1cab317a069809b7147857e37ee#5df7f06486c7c1cab317a069809b7147857e37ee" +source = "git+https://github.com/zcash/librustzcash?rev=4b13be3c2ce6f91a4b386508aed0799358daadb1#4b13be3c2ce6f91a4b386508aed0799358daadb1" dependencies = [ "ambassador", "arti-client", @@ -8798,7 +8744,7 @@ dependencies = [ "postcard", "proptest", "prost", - "rand 0.8.5", + "rand 0.8.6", "rand_chacha 0.3.1", "rand_core 0.6.4", "rayon", @@ -8822,7 +8768,7 @@ dependencies = [ "tracing", "trait-variant", "webpki-roots", - "which 8.0.2", + "which 8.0.4", "zcash_address", "zcash_encoding", "zcash_keys", @@ -8838,11 +8784,11 @@ dependencies = [ [[package]] name = "zcash_client_sqlite" -version = "0.21.0" -source = "git+https://github.com/valargroup/librustzcash?rev=5df7f06486c7c1cab317a069809b7147857e37ee#5df7f06486c7c1cab317a069809b7147857e37ee" +version = "0.21.1" +source = "git+https://github.com/zcash/librustzcash?rev=4b13be3c2ce6f91a4b386508aed0799358daadb1#4b13be3c2ce6f91a4b386508aed0799358daadb1" dependencies = [ "bip32", - "bitflags 2.11.0", + "bitflags 2.13.0", "bs58", "byteorder", "document-features", @@ -8854,7 +8800,7 @@ dependencies = [ "nonempty", "orchard", "prost", - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", "rand_distr", "regex", @@ -8885,7 +8831,7 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.4.0" -source = "git+https://github.com/valargroup/librustzcash?rev=5df7f06486c7c1cab317a069809b7147857e37ee#5df7f06486c7c1cab317a069809b7147857e37ee" +source = "git+https://github.com/zcash/librustzcash?rev=4b13be3c2ce6f91a4b386508aed0799358daadb1#4b13be3c2ce6f91a4b386508aed0799358daadb1" dependencies = [ "corez", "hex", @@ -8894,8 +8840,8 @@ dependencies = [ [[package]] name = "zcash_keys" -version = "0.14.0" -source = "git+https://github.com/valargroup/librustzcash?rev=5df7f06486c7c1cab317a069809b7147857e37ee#5df7f06486c7c1cab317a069809b7147857e37ee" +version = "0.15.0-pre.0" +source = "git+https://github.com/zcash/librustzcash?rev=4b13be3c2ce6f91a4b386508aed0799358daadb1#4b13be3c2ce6f91a4b386508aed0799358daadb1" dependencies = [ "bech32 0.11.1", "bip32", @@ -8937,8 +8883,8 @@ dependencies = [ [[package]] name = "zcash_primitives" -version = "0.28.0" -source = "git+https://github.com/valargroup/librustzcash?rev=5df7f06486c7c1cab317a069809b7147857e37ee#5df7f06486c7c1cab317a069809b7147857e37ee" +version = "0.29.0-pre.0" +source = "git+https://github.com/zcash/librustzcash?rev=4b13be3c2ce6f91a4b386508aed0799358daadb1#4b13be3c2ce6f91a4b386508aed0799358daadb1" dependencies = [ "blake2b_simd", "block-buffer 0.11.0-rc.3", @@ -8968,8 +8914,8 @@ dependencies = [ [[package]] name = "zcash_proofs" -version = "0.28.0" -source = "git+https://github.com/valargroup/librustzcash?rev=5df7f06486c7c1cab317a069809b7147857e37ee#5df7f06486c7c1cab317a069809b7147857e37ee" +version = "0.29.0-pre.0" +source = "git+https://github.com/zcash/librustzcash?rev=4b13be3c2ce6f91a4b386508aed0799358daadb1#4b13be3c2ce6f91a4b386508aed0799358daadb1" dependencies = [ "bellman", "blake2b_simd", @@ -8990,8 +8936,8 @@ dependencies = [ [[package]] name = "zcash_protocol" -version = "0.9.0" -source = "git+https://github.com/valargroup/librustzcash?rev=5df7f06486c7c1cab317a069809b7147857e37ee#5df7f06486c7c1cab317a069809b7147857e37ee" +version = "0.10.0-pre.0" +source = "git+https://github.com/zcash/librustzcash?rev=4b13be3c2ce6f91a4b386508aed0799358daadb1#4b13be3c2ce6f91a4b386508aed0799358daadb1" dependencies = [ "corez", "document-features", @@ -9005,12 +8951,12 @@ dependencies = [ [[package]] name = "zcash_script" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ef9d04e0434a80b62ad06c5a610557be358ef60a98afa5dbc8ecaf19ad72e7" +checksum = "2f872800287d118be71bdf6fe8c869c6a6ff6fb0a5762f68fb2af54c97edf0f2" dependencies = [ "bip32", - "bitflags 2.11.0", + "bitflags 2.13.0", "bounded-vec", "hex", "ripemd 0.1.3", @@ -9031,8 +8977,8 @@ dependencies = [ [[package]] name = "zcash_transparent" -version = "0.8.0" -source = "git+https://github.com/valargroup/librustzcash?rev=5df7f06486c7c1cab317a069809b7147857e37ee#5df7f06486c7c1cab317a069809b7147857e37ee" +version = "0.9.0-pre.0" +source = "git+https://github.com/zcash/librustzcash?rev=4b13be3c2ce6f91a4b386508aed0799358daadb1#4b13be3c2ce6f91a4b386508aed0799358daadb1" dependencies = [ "bip32", "bs58", @@ -9056,55 +9002,55 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.47" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.47" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "serde", "zerofrom", @@ -9126,7 +9072,7 @@ dependencies = [ [[package]] name = "zip321" version = "0.8.0" -source = "git+https://github.com/valargroup/librustzcash?rev=5df7f06486c7c1cab317a069809b7147857e37ee#5df7f06486c7c1cab317a069809b7147857e37ee" +source = "git+https://github.com/zcash/librustzcash?rev=4b13be3c2ce6f91a4b386508aed0799358daadb1#4b13be3c2ce6f91a4b386508aed0799358daadb1" dependencies = [ "base64 0.22.1", "nom 7.1.3", diff --git a/Cargo.toml b/Cargo.toml index ebccc9f..9942f80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,12 @@ rust-version = "1.87" license = "MIT OR Apache-2.0" publish = false +[lints.rust] +# `zcash_unstable` is a `--cfg` set via RUSTFLAGS (not a Cargo feature) by the +# zcash crates to opt in to in-development network upgrades; declared here so the +# regtest/inspect code can gate on NU7 without tripping the `unexpected_cfgs` lint. +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(zcash_unstable, values("nu7"))'] } + [dependencies] anyhow = "1" bip0039 = { version = "0.12", features = ["std", "all-languages"] } @@ -34,17 +40,23 @@ tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } uuid = "1" # Zcash -orchard = { version = "0.14", default-features = false } +# Pinned to the exact pre-release the librustzcash Ironwood RC was cut against +# (a bare "0.15.0-pre.1" would also admit later `-pre.N`). +orchard = { version = "=0.15.0-pre.1", default-features = false } pczt = "0.7" sapling = { package = "sapling-crypto", version = "0.7" } -transparent = { package = "zcash_transparent", version = "0.8", features = ["test-dependencies"] } -zcash_address = "0.12" +transparent = { package = "zcash_transparent", version = "0.9.0-pre.0", features = ["test-dependencies"] } +zcash_address = "0.13.0-pre.0" zcash_client_backend = { version = "0.23", features = ["lightwalletd-tonic-tls-webpki-roots", "orchard", "pczt", "tor", "unstable"] } -zcash_client_sqlite = { version = "0.21", features = ["unstable", "orchard", "serde", "test-dependencies"] } -zcash_keys = { version = "0.14", features = ["orchard", "unstable", "unstable-frost"] } -zcash_primitives = "0.28" -zcash_proofs = { version = "0.28", features = ["bundled-prover"] } -zcash_protocol = { version = "0.9", features = ["local-consensus"] } +# `transparent-inputs` is enabled unconditionally: `zcash_client_backend`'s `pczt` +# feature (always on here) already forces `zcash_client_backend/transparent-inputs`, +# so `zcash_client_sqlite` must match or its `WalletRead`/`WalletWrite` impls fail to +# cover the trait's transparent methods. +zcash_client_sqlite = { version = "0.21", features = ["transparent-inputs", "unstable", "orchard", "serde", "test-dependencies"] } +zcash_keys = { version = "0.15.0-pre.0", features = ["orchard", "unstable", "unstable-frost"] } +zcash_primitives = "0.29.0-pre.0" +zcash_proofs = { version = "0.29.0-pre.0", features = ["bundled-prover"] } +zcash_protocol = { version = "0.10.0-pre.0", features = ["local-consensus"] } zcash_script = "0.4" zip32 = "0.2" zip321 = "0.8" @@ -98,15 +110,10 @@ roaring = { version = "0.11", optional = true } tokio-util = { version = "0.7", optional = true } tui-logger = { version = "0.17", optional = true, features = ["tracing-support"] } -[lints.rust] -# The Ironwood code paths are gated behind `cfg(zcash_unstable = "nu6.3")`, which -# is set for all builds via `.cargo/config.toml`. Register it so `#[cfg(...)]` -# uses in this crate don't trip the `unexpected_cfgs` lint. -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(zcash_unstable, values("nu6.3"))'] } - [features] default = ["transparent-inputs"] qr = ["dep:qrcode"] +regtest_support = ["zcash_protocol/local-consensus"] pczt-qr = ["dep:image", "dep:minicbor", "dep:nokhwa", "dep:qrcode", "dep:rqrr", "dep:ur"] transparent-inputs = [ "zcash_client_sqlite/transparent-inputs", @@ -119,31 +126,28 @@ tui = [ "dep:tui-logger", ] -# Ironwood (NU7 / Zcash Ironwood upgrade) dependency repointing. -# -# The published librustzcash crates cannot create or parse V6/Ironwood -# transactions, so we redirect every librustzcash crate this tool depends on -# (directly or transitively) to the Valar Group `ironwood-integration` fork. -# Because the fork keeps the same crate versions as crates.io, these are -# semver-compatible source replacements and override transitive uses too. -# -# `orchard` must be patched to the exact git revision the fork itself uses -# (pulled by the fork via git, not crates.io); patching it here unifies our -# direct `orchard` dependency with the fork's so there is a single `orchard` -# in the graph. +# Ironwood (NU6.3 / V6 transaction) support: repoint every librustzcash crate we +# depend on (directly or transitively) to the official librustzcash Ironwood +# release-candidate commit. The RC's higher-level crates (zcash_client_backend, +# zcash_client_sqlite, pczt, zip321) keep their crates.io version numbers but +# depend on the `-pre` lower crates, so the crates.io releases can't be used +# directly — a git source is required. `rev 4b13be3c` is the commit the +# `zcash_*-*-pre.*` RC tags point to. # -# NOTE: building against this patch requires the Ironwood feature gate; see -# `.cargo/config.toml`, which sets `--cfg zcash_unstable="nu6.3"`. +# `orchard` is NOT patched: the RC depends on `orchard 0.15.0-pre.1`, which is +# published on crates.io, so our direct dependency and the RC's resolve to the +# same crates.io crate. [patch.crates-io] -orchard = { git = "https://github.com/zcash/orchard", rev = "0eaea4ebb5aa4ba2b6cf4afa0e1c9b5e8c03273a" } -zcash_address = { git = "https://github.com/valargroup/librustzcash", rev = "5df7f06486c7c1cab317a069809b7147857e37ee" } -zcash_encoding = { git = "https://github.com/valargroup/librustzcash", rev = "5df7f06486c7c1cab317a069809b7147857e37ee" } -zcash_protocol = { git = "https://github.com/valargroup/librustzcash", rev = "5df7f06486c7c1cab317a069809b7147857e37ee" } -zip321 = { git = "https://github.com/valargroup/librustzcash", rev = "5df7f06486c7c1cab317a069809b7147857e37ee" } -pczt = { git = "https://github.com/valargroup/librustzcash", rev = "5df7f06486c7c1cab317a069809b7147857e37ee" } -zcash_keys = { git = "https://github.com/valargroup/librustzcash", rev = "5df7f06486c7c1cab317a069809b7147857e37ee" } -zcash_client_backend = { git = "https://github.com/valargroup/librustzcash", rev = "5df7f06486c7c1cab317a069809b7147857e37ee" } -zcash_client_sqlite = { git = "https://github.com/valargroup/librustzcash", rev = "5df7f06486c7c1cab317a069809b7147857e37ee" } -zcash_primitives = { git = "https://github.com/valargroup/librustzcash", rev = "5df7f06486c7c1cab317a069809b7147857e37ee" } -zcash_proofs = { git = "https://github.com/valargroup/librustzcash", rev = "5df7f06486c7c1cab317a069809b7147857e37ee" } -zcash_transparent = { git = "https://github.com/valargroup/librustzcash", rev = "5df7f06486c7c1cab317a069809b7147857e37ee" } +equihash = { git = "https://github.com/zcash/librustzcash", rev = "4b13be3c2ce6f91a4b386508aed0799358daadb1" } +f4jumble = { git = "https://github.com/zcash/librustzcash", rev = "4b13be3c2ce6f91a4b386508aed0799358daadb1" } +zcash_address = { git = "https://github.com/zcash/librustzcash", rev = "4b13be3c2ce6f91a4b386508aed0799358daadb1" } +zcash_encoding = { git = "https://github.com/zcash/librustzcash", rev = "4b13be3c2ce6f91a4b386508aed0799358daadb1" } +zcash_protocol = { git = "https://github.com/zcash/librustzcash", rev = "4b13be3c2ce6f91a4b386508aed0799358daadb1" } +zip321 = { git = "https://github.com/zcash/librustzcash", rev = "4b13be3c2ce6f91a4b386508aed0799358daadb1" } +pczt = { git = "https://github.com/zcash/librustzcash", rev = "4b13be3c2ce6f91a4b386508aed0799358daadb1" } +zcash_keys = { git = "https://github.com/zcash/librustzcash", rev = "4b13be3c2ce6f91a4b386508aed0799358daadb1" } +zcash_client_backend = { git = "https://github.com/zcash/librustzcash", rev = "4b13be3c2ce6f91a4b386508aed0799358daadb1" } +zcash_client_sqlite = { git = "https://github.com/zcash/librustzcash", rev = "4b13be3c2ce6f91a4b386508aed0799358daadb1" } +zcash_primitives = { git = "https://github.com/zcash/librustzcash", rev = "4b13be3c2ce6f91a4b386508aed0799358daadb1" } +zcash_proofs = { git = "https://github.com/zcash/librustzcash", rev = "4b13be3c2ce6f91a4b386508aed0799358daadb1" } +zcash_transparent = { git = "https://github.com/zcash/librustzcash", rev = "4b13be3c2ce6f91a4b386508aed0799358daadb1" } diff --git a/README.md b/README.md index 6a6bdab..d50f396 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,18 @@ cargo run --release -- wallet -w init --name "" -i sync -s zecrocks ``` +To restore an existing wallet from a mnemonic seed phrase, use `restore-mnemonic`: +``` +cargo run --release -- wallet -w restore-mnemonic --name "" -i -n test +cargo run --release -- wallet -w sync +``` +You will be prompted to enter the mnemonic phrase to restore. As with `init`, +the `-i` (identity) parameter specifies an age identity file used to encrypt the +mnemonic phrase, and the file will be generated if it doesn't exist. Unless you +pass `--birthday`, the wallet's birthday defaults to the network's Sapling +activation height so that the entire history in which it could have received +funds is scanned. + Whenever you update the `zcash_client_sqlite` dependency, in order to run necessary migrations: ``` diff --git a/doc/ironwood.md b/doc/ironwood.md index d7ee2ef..9c3fb7f 100644 --- a/doc/ironwood.md +++ b/doc/ironwood.md @@ -1,130 +1,186 @@ -# Ironwood (NU7) support - -This fork of `zcash-devtool` is built to fund and exercise the **Zcash Ironwood** -upgrade (NU7 / V6 transactions). It is used as the *funder* in an end-to-end -harness: it talks to `lightwalletd` over gRPC, and must be able to **create** an -Ironwood-value transaction so downstream components can receive Ironwood funds. - -## What changed in this repo - -1. **Dependencies repointed to the Valar Group fork.** - `Cargo.toml` adds a `[patch.crates-io]` section that redirects every - `librustzcash` crate this tool uses to - [`valargroup/librustzcash`](https://github.com/valargroup/librustzcash) at rev - `5df7f06486c7c1cab317a069809b7147857e37ee` (branch `ironwood-integration`), - and `orchard` to [`zcash/orchard`](https://github.com/zcash/orchard) at rev - `0eaea4ebb5aa4ba2b6cf4afa0e1c9b5e8c03273a` — the exact revision the fork - itself depends on, so there is a single `orchard` in the graph. The fork - keeps the same crate versions as crates.io, so these are semver-compatible - source replacements that also cover transitive uses. - -2. **Ironwood feature gate enabled for all builds.** - `.cargo/config.toml` sets `--cfg zcash_unstable="nu6.3"` via `build.rustflags`. - The fork gates all Ironwood code behind `cfg(zcash_unstable = "nu6.3")`, so - this flag must be present for the Ironwood APIs (and this tool's migration - command) to compile in. No extra environment variable is required — - `cargo build`/`cargo run` in this directory pick the flag up automatically. - -3. **New command: `wallet migrate-to-ironwood`.** - Wraps `zcash_client_backend`'s `create_orchard_to_ironwood_transaction`. It - spends Orchard notes from an account and creates a single Ironwood (V6) output - to that account's internal Orchard receiver, migrating the full selected - Orchard value minus fees (no Orchard change). The signed transaction is then - transmitted to `lightwalletd` exactly like `wallet shield`/`wallet send`. - - ``` - cargo run --release -- wallet -w migrate-to-ironwood \ - [ACCOUNT_UUID] --amount --identity - ``` - - `--amount` is the *minimum* Orchard value to select; the resulting - transaction migrates the full selected value minus fees. - -### Funding flow with Ironwood — `wallet send` already does it - -The pre-Ironwood funder flow is all Orchard, through `lightwalletd`: - -1. derive a transparent address offline → miner sends transparent coinbase to it; -2. after coinbase maturity, `wallet shield` moves transparent → Orchard; -3. `wallet send` moves Orchard → recipient's UA. - -**No new "send to Ironwood" command is needed.** Once NU6.3 is active at the -target height, the high-level path that `wallet send`/`wallet pay` already use -(`propose_transfer` → `create_proposed_transactions` → `build_proposed_transaction`) -automatically emits an **Ironwood (V6) output** to the recipient's Orchard -receiver, with Ironwood change back to the sender. The decision is made by -`zcash_client_backend`'s `orchard_outputs_to_ironwood`, which returns `true` -exactly when `is_nu_active(Nu6_3, target_height)` holds (and a legacy v5 bundle -wasn't explicitly requested via `--version 5`). - -So the Ironwood funding step is simply: +# Ironwood (NU6.3 / V6) support + +`zcash-devtool` tracks the **Zcash Ironwood** upgrade so it can create, inspect, +and exercise **V6 (Ironwood) transactions** end to end — talking to +`lightwalletd` over gRPC as the *funder* in a local harness, and as a general +inspection/PCZT tool. + +Ironwood is the **NU6.3** network upgrade. In the official `librustzcash` +release candidate the network upgrade `NetworkUpgrade::Nu6_3` is a *stable* +(non-`cfg`-gated) variant whose consensus branch ID selects `TxVersion::V6`, so +V6 transactions build and parse on an ordinary `cargo build` — **no +`--cfg zcash_unstable` flag is required**. (The separate `zcash_unstable = "nu7"` +gate covers a *later* upgrade — ZIP-233 and friends — which this tool does not +use.) + +## What this tool depends on + +Ironwood support comes from the official `librustzcash` Ironwood release +candidate, pulled in via `[patch.crates-io]` in `Cargo.toml`: + +- Every `librustzcash` crate we use (directly or transitively) is repointed to + [`zcash/librustzcash`](https://github.com/zcash/librustzcash) at rev + `4b13be3c2ce6f91a4b386508aed0799358daadb1` — the commit the RC tags + (`zcash_primitives-0.29.0-pre.0`, `zcash_keys-0.15.0-pre.0`, …) point to. The + RC's higher-level crates (`zcash_client_backend 0.23`, `zcash_client_sqlite + 0.21`, `pczt 0.7`, `zip321 0.8`) keep their crates.io version numbers but + depend on the `-pre` lower crates, so a git source is required — the crates.io + releases can't be used directly. +- `orchard` is **not** patched: the RC uses `orchard 0.15.0-pre.1` from + crates.io, so our direct dependency (pinned `=0.15.0-pre.1`) and the RC's + resolve to the same crate. + +Release-candidate crate versions (per the librustzcash maintainers): + +| Crate | Version | +|---|---| +| `orchard` | `0.15.0-pre.1` (crates.io) | +| `zcash_primitives` | `0.29.0-pre.0` | +| `zcash_proofs` | `0.29.0-pre.0` | +| `zcash_keys` | `0.15.0-pre.0` | +| `zcash_transparent` | `0.9.0-pre.0` | +| `zcash_protocol` | `0.10.0-pre.0` | +| `zcash_address` | `0.13.0-pre.0` | +| `zcash_history` | `0.5.0-pre.0` | +| `halo2` | no update | + +To move to a newer RC, bump the four `rev = "…"` lines in `[patch.crates-io]` +(and the `orchard`/`-pre` version requirements in `[dependencies]`) in lockstep. + +## When does Ironwood activate? + +V6/Ironwood transactions are produced once the chain tip is **at or above the +NU6.3 activation height** for the network the wallet is configured with. The +network's activation schedule decides this: + +| Network | NU6.3 (Ironwood) activation | Notes | +|---|---|---| +| **mainnet** | not yet scheduled (`None`) | V6 is inactive until NU6.3 is assigned a mainnet height in a future `zcash_protocol` release. Nothing to configure here; the tool follows the schedule baked into `zcash_protocol`. | +| **testnet** | height **4,134,000** | Scheduled in `zcash_protocol`. Point the wallet at a testnet `lightwalletd` (`-n test -s zecrocks`) and it produces V6 transactions once the tip passes 4,134,000. | +| **regtest** | **operator-chosen** | Set via the `--activation-heights` file at `init` (see below). This is how you get a chain where Ironwood is active from the start for local testing. | + +Because activation is driven entirely by the network parameters, **testnet works +today and mainnet will work automatically** once NU6.3 is assigned a mainnet +height upstream — no code change here is needed for the mainnet cutover beyond +bumping to the `zcash_protocol` release that carries the height. + +> **Scope of "support" today.** The tool builds and parses V6 transactions and +> runs against a NU6.3 chain. Post-NU6.3, Orchard outputs use the `orchard_v3` +> circuit. The wallet does **not** yet construct the separate Ironwood value-pool +> bundle (upstream `librustzcash` currently builds V6 transactions with +> `ironwood_anchor: None`); when that lands upstream it will flow through the +> normal `wallet send` / `pay` path with no change here. The low-level pieces are +> already wired: the PCZT prover creates an Ironwood proof when a bundle is +> present (`wallet`/`pczt prove`), `inspect` decodes V6, and `wallet send +> --version 6` is accepted. + +## Using regtest + +Regtest support lives behind the `regtest_support` Cargo feature and is the way +to stand up a local chain where Ironwood is active from an early height. + +**1. Build with the feature:** + +```bash +cargo build --release --features regtest_support +``` +**2. Write an activation-heights file.** A TOML file gives one height per +network upgrade; a missing key means "not active". `nu6_3` is the Ironwood key: + +```toml +# regtest-heights.toml — must match the validator (zebra) and lightwalletd. +overwinter = 1 +sapling = 1 +blossom = 1 +heartwood = 1 +canopy = 1 +nu5 = 1 +nu6 = 1 +nu6_1 = 1 +nu6_2 = 1 +nu6_3 = 1 # Ironwood / V6 active from height 1 ``` -cargo run --release -- wallet -w send --identity + +**3. Initialise the wallet against your local `lightwalletd`.** Regtest has no +hosted server, so an explicit `--server host:port` is required. `-n regtest` +*requires* `--activation-heights`; the heights are persisted into the wallet's +`keys.toml` (as an `[activation_heights]` table) so every later command agrees +with the chain: + +```bash +cargo run --release --features regtest_support -- \ + wallet -w ./regtest-wallet init \ + --name dev -i ./regtest-wallet/dev-key.txt \ + -n regtest --activation-heights ./regtest-heights.toml \ + -s 127.0.0.1:9067 ``` -`wallet migrate-to-ironwood` remains available as an optional Orchard→Ironwood -*self*-migration helper, but it is not the funding mechanism. - -### Non-interactive funder commands - -The automated funder (harness `Funder` helper) drives the wallet non-interactively -and needs two things the interactive flow doesn't provide. Both are ported from -the `zecrocks/zcash-devtool` `claude/sleepy-faraday-T6HUy` branch so the existing -`Funder` works unchanged: - -- **`wallet init --mnemonic `** — initialise from a known seed without the - interactive `rpassword` TTY prompt. If `--mnemonic` is omitted the prompt is - still used. -- **`wallet derive-address --network --mnemonic `** — *offline* - derivation (no wallet dir, no server) of the account's default addresses, - printing: - - ``` - Unified Address: - Transparent Address: - ``` - - This lets the harness learn the funder's transparent address *before* the chain - exists (zebra mines coinbase to it as `miner_address`). The transparent address - is the first account's default receiver — exactly what `create_account` - produces and what `wallet shield` later scans, so coinbase mined to it is - detected. (Mnemonics passed on the command line are visible in the process - list; this is for ephemeral test wallets only.) - -### Network activation is the real gate — use `--network regtest` - -`is_nu_active(Nu6_3, …)` is only `true` if the wallet's *params* schedule NU6.3. -On `consensus::Network::{MainNetwork, TestNetwork}` the fork hardcodes -`Nu6_3 => None`, so on those networks `wallet send` will never route to Ironwood. -Ironwood funding therefore requires running the wallet on a **regtest / -local-consensus** network, added here as `--network regtest` (see -`src/network.rs`). Because regtest has no hosted lightwalletd, pass an explicit -`--server `. - -**Cross-component invariant — activation heights must match.** devtool's regtest -schedule (`src/network.rs::regtest_params`) must be identical to zebra's -`configured_activation_heights` and zecd's `regtest()`, or the consensus branch -IDs diverge and transactions are rejected. The agreed schedule: - -| Upgrade | Height | -|---|---| -| Overwinter … NU5, NU6 | 1 | -| NU6.1, NU6.2 | 4 | -| NU6.3 (Ironwood) | 8 | +`init` reads the mnemonic interactively, but when stdin is not a terminal it +reads one line from stdin instead — so an automated funder can pipe a known +phrase in (`echo "$PHRASE" | cargo run … init …`) without a TTY. + +**4. Fund and transact as usual** (`wallet sync`, `wallet shield`, +`wallet send`, …). Once the tip is at/above the `nu6_3` height, the transactions +the wallet builds are V6. + +### The cross-component invariant -## Companion Docker images (harness, not this repo) +The `--activation-heights` file **must** be identical to the validator's +schedule (zebra's `configured_activation_heights`) and to what `lightwalletd` +serves. Transaction construction derives the consensus branch ID from these +heights; any drift makes the validator reject transactions built while the tip +is inside the drifted window. -The funder is a `lightwalletd` gRPC client, and `lightwalletd` follows a Zebra -node. Both must understand Ironwood/V6 for the end-to-end flow. The harness -extracts stock `zebrad` + `lightwalletd` images today; for Ironwood, build and -publish these two and point the harness at them: +### Offline address derivation for funders -| Image | Build from | Pin | Replaces | -|-------|-----------|-----|----------| -| zebra (Ironwood) | `valargroup/zebra` branch `add-ironwood-v6-value-pool` | `c319f8296be4871f2f2deb1d454d508d3cea5cba` | `zfnd/zebra:5.0.0` | -| lightwalletd (V6 parser) | `valargroup/lightwalletd` branch `adam/lightwalletd-nu7-v6-parser` | `360a37d588756b2fc96c3e4403296cd2b47dbc04` | stock `electriccoinco/lightwalletd` | +`wallet derive-address` derives an account's default unified and transparent +addresses straight from a mnemonic, with **no wallet directory and no server**: -> **Branch-naming trap:** on both `zebra` and `lightwalletd`, the `ironwood-main` -> branch does **not** yet carry the Ironwood feature work — use the branches/pins -> above. Switch to `ironwood-main` only once that work merges down. +```bash +cargo run --release --features regtest_support -- \ + wallet derive-address -n regtest --mnemonic "$PHRASE" +# Unified Address: u1... +# Transparent Address: t1... +``` + +This lets a harness learn the funder's transparent address *before* the chain +exists (e.g. so zebra can mine coinbase to it as `miner_address`). The address +is the first account's default receiver — exactly what `create_account` +produces and what `wallet shield` later scans. (A mnemonic on the command line +is visible in the process list; use this for ephemeral test wallets only.) + +## Companion node / indexer + +The funder is a `lightwalletd` client, and `lightwalletd` follows a Zebra node; +both must understand Ironwood/V6 for the end-to-end flow. Use the official +Ironwood release candidates: + +| Component | Ironwood RC | +|---|---| +| Zebra | [`v6.0.0-rc.0`](https://github.com/ZcashFoundation/zebra/releases/tag/v6.0.0-rc.0) (replaces the older `zfnd/zebra:5.x`) | +| lightwalletd | a build whose transaction parser understands V6 | + +Confirm the exact node/indexer tags against the current librustzcash RC before a +run — the RC crate versions above and the node's supported transaction format +must agree. + +## Changes from the previous (Valar Group fork) branch + +Earlier iterations of this branch used the Valar Group `ironwood-integration` +fork of `librustzcash` and gated Ironwood behind `--cfg zcash_unstable="nu6.3"`. +Moving to the official RC changed the following: + +- **Dependencies** now point at official `librustzcash` (rev `4b13be3c`) instead + of `valargroup/librustzcash`, and `orchard` comes from crates.io. +- **No feature gate.** Ironwood is the stable `Nu6_3` upgrade in the RC, so the + `.cargo/config.toml` that forced `--cfg zcash_unstable="nu6.3"` was removed. + Ordinary `cargo build` compiles the V6 paths. +- **`wallet migrate-to-ironwood` was removed.** It wrapped + `create_orchard_to_ironwood_transaction`, a helper that exists only in the + Valar fork; the official RC has no equivalent (the wallet does not yet + construct Ironwood-pool bundles). Reinstate it only if/when an upstream API + lands. +- **Configurable regtest heights.** Regtest activation heights are supplied per + wallet via `--activation-heights` (merged from upstream `zcash-devtool`) + rather than hardcoded, and now include the `nu6_3` key. diff --git a/doc/walkthrough.md b/doc/walkthrough.md index 376556a..c9fb1f9 100644 --- a/doc/walkthrough.md +++ b/doc/walkthrough.md @@ -102,6 +102,69 @@ network = "test" birthday = 3274265 ``` +Restoring a Wallet from a Mnemonic +---------------------------------- + +If you already have a mnemonic seed phrase — for example one produced by a +previous `init`, or by another wallet — you can recover the wallet with the +`restore-mnemonic` command instead of `init`. This re-derives the wallet's keys +from the phrase and sets up the same set of files and databases. + +We'll restore into a fresh `../restored-wallet` directory. As with `init`, the +`-i` parameter points at an `age` identity file used to encrypt the mnemonic at +rest; if the file doesn't exist yet it will be generated for you (you'll see a +`Generating a new age identity to encrypt the mnemonic phrase` message), and the +public key written to it is what `keys.toml` is encrypted to. + +```bash +λ cargo run --release --all-features -- wallet -w ../restored-wallet restore-mnemonic \ + --name "ZDevRestored" -i ../restored-wallet/dev-key.txt -n test -s zecrocks +``` + +The command then prompts for the phrase to restore. The input is read without +echoing it back to the terminal: + +```bash +Enter mnemonic to restore: +``` + +Paste (or type) the 24-word mnemonic and press Enter. The command derives the +keys, writes the encrypted mnemonic and wallet metadata to `keys.toml`, and +initializes the wallet databases — the same `../restored-wallet` layout you saw +after running `init`: + +```bash +λ ll ../restored-wallet/ +total 352 +-rw-r--r-- 1 ... ... 16384 Mar 3 17:48 blockmeta.sqlite +drwxrwxr-x 2 ... ... 4096 Mar 3 17:48 blocks/ +-rw-r--r-- 1 ... ... 323584 Mar 3 17:48 data.sqlite +-rw------- 1 ... ... 189 Mar 3 16:59 dev-key.txt +-rw-rw-r-- 1 ... ... 786 Mar 3 17:48 keys.toml +drwxrwxr-x 4 ... ... 4096 Mar 3 17:43 tor/ +``` + +A note on the wallet's birthday: `init` assumes a brand-new wallet and defaults +the birthday to roughly the current chain tip, but a restored wallet may have +received funds at any point in its past. For that reason `restore-mnemonic` +defaults the birthday to the network's Sapling activation height, so that a +subsequent `sync` scans the entire history in which the wallet could have +received funds. If you know a more recent birthday height, pass it explicitly to +avoid scanning unnecessary history: + +```bash +λ cargo run --release --all-features -- wallet -w ../restored-wallet restore-mnemonic \ + --name "ZDevRestored" -i ../restored-wallet/dev-key.txt --birthday 3274265 -n test -s zecrocks +``` + +Once restored, the wallet behaves exactly like any other; sync it to discover +its funds: + +```bash +λ cargo run --release --all-features -- wallet -w ../restored-wallet sync -s zecrocks +λ cargo run --release --all-features -- wallet -w ../restored-wallet balance +``` + Receiving Payments ------------------ diff --git a/src/commands/create_multisig_address.rs b/src/commands/create_multisig_address.rs index 3465c6b..e72975f 100644 --- a/src/commands/create_multisig_address.rs +++ b/src/commands/create_multisig_address.rs @@ -53,7 +53,7 @@ impl Command { } = self; let (multisig_script, addr) = multisig_script(threshold, pub_keys)?; - let addr = addr.encode(&network.params()); + let addr = addr.encode(&network); println!("Created multisig address: {addr}"); println!("Redeem script: {}", hex::encode(multisig_script.to_bytes())); diff --git a/src/commands/inspect/keys.rs b/src/commands/inspect/keys.rs index d8bc95c..6a2f57d 100644 --- a/src/commands/inspect/keys.rs +++ b/src/commands/inspect/keys.rs @@ -202,6 +202,8 @@ pub(crate) fn inspect_sapling_extsk(data: Vec, network: NetworkType) { nu6_1: None, nu6_2: None, nu6_3: None, + #[cfg(zcash_unstable = "nu7")] + nu7: None, }), }; eprintln!("- UFVK: {encoded_ufvk}"); @@ -223,6 +225,8 @@ pub(crate) fn inspect_sapling_extsk(data: Vec, network: NetworkType) { nu6_1: None, nu6_2: None, nu6_3: None, + #[cfg(zcash_unstable = "nu7")] + nu7: None, }), }; eprintln!(" - Default address: {encoded_ua}"); diff --git a/src/commands/inspect/keys/view.rs b/src/commands/inspect/keys/view.rs index 9ea5ff7..b545f24 100644 --- a/src/commands/inspect/keys/view.rs +++ b/src/commands/inspect/keys/view.rs @@ -122,6 +122,8 @@ pub(crate) fn inspect_sapling_extfvk(data: Vec, network: NetworkType) { nu6_1: None, nu6_2: None, nu6_3: None, + #[cfg(zcash_unstable = "nu7")] + nu7: None, }), }; eprintln!("- Equivalent UFVK: {encoded_ufvk}"); @@ -143,6 +145,8 @@ pub(crate) fn inspect_sapling_extfvk(data: Vec, network: NetworkType) { nu6_1: None, nu6_2: None, nu6_3: None, + #[cfg(zcash_unstable = "nu7")] + nu7: None, }), }; eprintln!(" - Default address: {encoded_ua}"); diff --git a/src/commands/inspect/transaction.rs b/src/commands/inspect/transaction.rs index 8eb6f59..7920fbb 100644 --- a/src/commands/inspect/transaction.rs +++ b/src/commands/inspect/transaction.rs @@ -144,6 +144,10 @@ pub(crate) fn inspect( TxVersion::V5 | TxVersion::V6 => { eprintln!(" - Consensus branch ID: {:?}", tx.consensus_branch_id()); } + #[cfg(zcash_unstable = "nu7")] + TxVersion::V6 => { + eprintln!(" - Consensus branch ID: {:?}", tx.consensus_branch_id()); + } } let is_coinbase = is_coinbase(&tx); diff --git a/src/commands/pczt.rs b/src/commands/pczt.rs index 1a67dbd..378430d 100644 --- a/src/commands/pczt.rs +++ b/src/commands/pczt.rs @@ -4,6 +4,7 @@ pub(crate) mod combine; pub(crate) mod create; pub(crate) mod create_manual; pub(crate) mod create_max; +pub(crate) mod extract; pub(crate) mod inspect; pub(crate) mod pay_manual; pub(crate) mod prove; @@ -13,6 +14,7 @@ pub(crate) mod send_without_storing; pub(crate) mod shield; pub(crate) mod sign; pub(crate) mod update_with_derivation; +pub(crate) mod update_with_signature; #[cfg(feature = "pczt-qr")] pub(crate) mod qr; @@ -40,8 +42,12 @@ pub(crate) enum Command { Prove(prove::Command), /// Apply signatures to a PCZT Sign(sign::Command), + /// Adds an externally-created signature to a PCZT + UpdateWithSignature(update_with_signature::Command), /// Combine two PCZTs Combine(combine::Command), + /// Extract a finished transaction from a PCZT + Extract(extract::Command), /// Extract a finished transaction and send it Send(send::Command), /// Extract a finished transaction and send it, without storing in the wallet. diff --git a/src/commands/pczt/combine.rs b/src/commands/pczt/combine.rs index 2ae2382..ccd2701 100644 --- a/src/commands/pczt/combine.rs +++ b/src/commands/pczt/combine.rs @@ -34,7 +34,13 @@ impl Command { .combine() .map_err(|e| anyhow!("Failed to combine PCZTs: {:?}", e))?; - stdout().write_all(&pczt.serialize()).await?; + stdout() + .write_all( + &pczt + .serialize() + .map_err(|e| anyhow!("failed to serialize PCZT: {e:?}"))?, + ) + .await?; Ok(()) } diff --git a/src/commands/pczt/create.rs b/src/commands/pczt/create.rs index ee50cfa..1598ad5 100644 --- a/src/commands/pczt/create.rs +++ b/src/commands/pczt/create.rs @@ -119,7 +119,13 @@ impl Command { ) .map_err(error::Error::from)?; - stdout().write_all(&pczt.serialize()).await?; + stdout() + .write_all( + &pczt + .serialize() + .map_err(|e| anyhow!("failed to serialize PCZT: {e:?}"))?, + ) + .await?; Ok(()) } diff --git a/src/commands/pczt/create_manual.rs b/src/commands/pczt/create_manual.rs index db53465..de2f83a 100644 --- a/src/commands/pczt/create_manual.rs +++ b/src/commands/pczt/create_manual.rs @@ -57,7 +57,8 @@ pub(crate) struct Command { #[arg(long)] memo: Option, - /// The network the coins are from: \"test\" or \"main\". + /// The network the coins are from: \"test\", \"main\", or \"regtest\" (requires + /// the `regtest_support` feature). /// /// If unset, uses the network of the provided wallet. #[arg(short, long)] @@ -71,7 +72,7 @@ pub(crate) struct Command { impl Command { pub(crate) async fn run(self, wallet_dir: Option) -> anyhow::Result<()> { let params = if let Some(network) = self.network { - network.params() + network } else { let config = WalletConfig::read(wallet_dir.as_ref())?; config.network() @@ -255,7 +256,13 @@ impl Command { )? .finish(); - stdout().write_all(&pczt.serialize()).await?; + stdout() + .write_all( + &pczt + .serialize() + .map_err(|e| anyhow!("failed to serialize PCZT: {e:?}"))?, + ) + .await?; Ok(()) } diff --git a/src/commands/pczt/create_max.rs b/src/commands/pczt/create_max.rs index 098d0b9..c978758 100644 --- a/src/commands/pczt/create_max.rs +++ b/src/commands/pczt/create_max.rs @@ -1,5 +1,6 @@ use std::str::FromStr; +use anyhow::anyhow; use clap::Args; use rand::rngs::OsRng; use tokio::io::{AsyncWriteExt, stdout}; @@ -79,8 +80,6 @@ impl Command { memo, mode, ConfirmationsPolicy::default(), - // proposed_version: let the proposal pick the transaction version. - None, ) .map_err(error::Error::SendMax)?; @@ -93,7 +92,13 @@ impl Command { ) .map_err(error::Error::from)?; - stdout().write_all(&pczt.serialize()).await?; + stdout() + .write_all( + &pczt + .serialize() + .map_err(|e| anyhow!("failed to serialize PCZT: {e:?}"))?, + ) + .await?; Ok(()) } diff --git a/src/commands/pczt/extract.rs b/src/commands/pczt/extract.rs new file mode 100644 index 0000000..b92f785 --- /dev/null +++ b/src/commands/pczt/extract.rs @@ -0,0 +1,36 @@ +use anyhow::anyhow; +use clap::Args; +use pczt::{ + Pczt, + roles::{spend_finalizer::SpendFinalizer, tx_extractor::TransactionExtractor}, +}; +use tokio::io::{AsyncReadExt, stdin}; +use zcash_proofs::prover::LocalTxProver; + +// Options accepted for the `pczt extract` command +#[derive(Debug, Args)] +pub(crate) struct Command {} + +impl Command { + pub(crate) async fn run(self) -> Result<(), anyhow::Error> { + let mut buf = vec![]; + stdin().read_to_end(&mut buf).await?; + + let pczt = Pczt::parse(&buf).map_err(|e| anyhow!("Failed to read PCZT: {:?}", e))?; + + let prover = LocalTxProver::bundled(); + let (spend_vk, output_vk) = prover.verifying_keys(); + + let finalized = SpendFinalizer::new(pczt) + .finalize_spends() + .map_err(|e| anyhow!("Failed to finalize PCZT spends: {e:?}"))?; + + let tx = TransactionExtractor::new(finalized) + .with_sapling(&spend_vk, &output_vk) + .extract() + .map_err(|e| anyhow!("Failed to extract transaction from PCZT: {e:?}"))?; + let txid = tx.txid(); + println!("{txid}"); + Ok(()) + } +} diff --git a/src/commands/pczt/pay_manual.rs b/src/commands/pczt/pay_manual.rs index e5bfc5f..bb34218 100644 --- a/src/commands/pczt/pay_manual.rs +++ b/src/commands/pczt/pay_manual.rs @@ -56,7 +56,8 @@ pub(crate) struct Command { #[arg(long)] change_address: String, - /// The network the coins are from: \"test\" or \"main\". + /// The network the coins are from: \"test\", \"main\", or \"regtest\" (requires + /// the `regtest_support` feature). /// /// If unset, uses the network of the provided wallet. #[arg(short, long)] @@ -70,7 +71,7 @@ pub(crate) struct Command { impl Command { pub(crate) async fn run(self, wallet_dir: Option) -> anyhow::Result<()> { let params = if let Some(network) = self.network { - network.params() + network } else { let config = WalletConfig::read(wallet_dir.as_ref())?; config.network() @@ -220,9 +221,15 @@ impl Command { &[][..] as &[Infallible], &sapling_output_values[..], ), - // The Orchard `BundleView` is now a 2-tuple of (inputs, outputs); - // the bundle type element was removed in the Ironwood fork. - &(&[][..] as &[Infallible], &orchard_output_values[..]), + // The Orchard `BundleView` tuple's first element is now a + // `BundleVersion` (it was a `BundleType` before Ironwood). Use + // the legacy Orchard action-count estimate, matching + // `zcash_client_backend`'s own `ANY_ORCHARD_BUNDLE_VERSION`. + &( + orchard::bundle::BundleVersion::orchard_v2(), + &[][..] as &[Infallible], + &orchard_output_values[..], + ), None, &(), ) @@ -352,7 +359,13 @@ impl Command { } let pczt = updater.finish(); - stdout().write_all(&pczt.serialize()).await?; + stdout() + .write_all( + &pczt + .serialize() + .map_err(|e| anyhow!("failed to serialize PCZT: {e:?}"))?, + ) + .await?; Ok(()) } diff --git a/src/commands/pczt/prove.rs b/src/commands/pczt/prove.rs index a9c6cb0..c11189e 100644 --- a/src/commands/pczt/prove.rs +++ b/src/commands/pczt/prove.rs @@ -175,9 +175,8 @@ impl Command { // Ironwood (V6) bundles share the post-NU6.3 Orchard circuit, so a single // proving key built for that circuit version serves both the Orchard and // Ironwood proof steps. - let orchard_pk = orchard::circuit::ProvingKey::build( - orchard::circuit::OrchardCircuitVersion::PostNu6_3, - ); + let orchard_pk = + orchard::circuit::ProvingKey::build(orchard::circuit::OrchardCircuitVersion::PostNu6_3); let mut orchard_prover = Prover::new(pczt); if orchard_prover.requires_orchard_proof() { @@ -196,7 +195,13 @@ impl Command { .map_err(|e| anyhow!("Failed to create Sapling proofs: {:?}", e))? .finish(); - stdout().write_all(&pczt.serialize()).await?; + stdout() + .write_all( + &pczt + .serialize() + .map_err(|e| anyhow!("failed to serialize PCZT: {e:?}"))?, + ) + .await?; Ok(()) } diff --git a/src/commands/pczt/qr.rs b/src/commands/pczt/qr.rs index 7ea73e8..46c6922 100644 --- a/src/commands/pczt/qr.rs +++ b/src/commands/pczt/qr.rs @@ -52,7 +52,9 @@ impl Send { let mut pczt_packet = vec![]; minicbor::encode( &ZcashPczt { - data: pczt.serialize(), + data: pczt + .serialize() + .map_err(|e| anyhow!("failed to serialize PCZT: {e:?}"))?, }, &mut pczt_packet, ) @@ -218,7 +220,13 @@ impl Receive { ) .map_err(|e| anyhow!("Failed to read PCZT from QR codes: {:?}", e))?; - stdout().write_all(&pczt.serialize()).await?; + stdout() + .write_all( + &pczt + .serialize() + .map_err(|e| anyhow!("failed to serialize PCZT: {e:?}"))?, + ) + .await?; Ok(()) } diff --git a/src/commands/pczt/redact.rs b/src/commands/pczt/redact.rs index f481710..6eb3758 100644 --- a/src/commands/pczt/redact.rs +++ b/src/commands/pczt/redact.rs @@ -34,7 +34,13 @@ impl Command { })? .finish(); - stdout().write_all(&pczt.serialize()).await?; + stdout() + .write_all( + &pczt + .serialize() + .map_err(|e| anyhow!("failed to serialize PCZT: {e:?}"))?, + ) + .await?; Ok(()) } diff --git a/src/commands/pczt/shield.rs b/src/commands/pczt/shield.rs index 279590f..420cda3 100644 --- a/src/commands/pczt/shield.rs +++ b/src/commands/pczt/shield.rs @@ -110,7 +110,13 @@ impl Command { ) .map_err(error::Error::Shield)?; - stdout().write_all(&pczt.serialize()).await?; + stdout() + .write_all( + &pczt + .serialize() + .map_err(|e| anyhow!("failed to serialize PCZT: {e:?}"))?, + ) + .await?; Ok(()) } diff --git a/src/commands/pczt/sign.rs b/src/commands/pczt/sign.rs index f868d24..2ddc70f 100644 --- a/src/commands/pczt/sign.rs +++ b/src/commands/pczt/sign.rs @@ -202,7 +202,13 @@ impl Command { let pczt = signer.finish(); - stdout().write_all(&pczt.serialize()).await?; + stdout() + .write_all( + &pczt + .serialize() + .map_err(|e| anyhow!("failed to serialize PCZT: {e:?}"))?, + ) + .await?; Ok(()) } diff --git a/src/commands/pczt/update_with_derivation.rs b/src/commands/pczt/update_with_derivation.rs index 0d28149..e606a80 100644 --- a/src/commands/pczt/update_with_derivation.rs +++ b/src/commands/pczt/update_with_derivation.rs @@ -154,7 +154,13 @@ impl Command { let pczt = updater.finish(); - stdout().write_all(&pczt.serialize()).await?; + stdout() + .write_all( + &pczt + .serialize() + .map_err(|e| anyhow!("failed to serialize PCZT: {e:?}"))?, + ) + .await?; Ok(()) } diff --git a/src/commands/pczt/update_with_signature.rs b/src/commands/pczt/update_with_signature.rs new file mode 100644 index 0000000..80f2b0b --- /dev/null +++ b/src/commands/pczt/update_with_signature.rs @@ -0,0 +1,142 @@ +use anyhow::anyhow; +use blake2b_simd::Hash as Blake2bHash; +use clap::Args; +use pczt::{ + Pczt, + roles::{low_level_signer, signer::EffectsOnly}, +}; +use tokio::io::{AsyncReadExt, AsyncWriteExt, stdin, stdout}; +use zcash_primitives::transaction::{ + TransactionData, TxDigests, sighash::SignableInput, sighash_v5::v5_signature_hash, + txid::TxIdDigester, +}; +use zcash_protocol::PoolType; + +// Options accepted for the `pczt update-with-signature` command +#[derive(Debug, Args)] +pub(crate) struct Command { + /// The pool that the signature is for. + #[arg(value_parser = parse_pool_type)] + pool: PoolType, + + /// The index of the transparent input or shielded spend that the signature is for. + index: usize, + + /// The hex-encoded signature. + signature: String, +} + +impl Command { + pub(crate) async fn run(self) -> anyhow::Result<()> { + let sig_bytes = hex::decode(self.signature)?; + + let mut buf = vec![]; + stdin().read_to_end(&mut buf).await?; + + let pczt = Pczt::parse(&buf).map_err(|e| anyhow!("Failed to read PCZT: {:?}", e))?; + + let tx_data = pczt.clone().into_effects().map_err(|e| anyhow!("{e:?}"))?; + let txid_parts = tx_data.digest(TxIdDigester); + + let signer = low_level_signer::Signer::new(pczt); + + let signer = match self.pool { + PoolType::Transparent => { + add_transparent(signer, &tx_data, &txid_parts, self.index, sig_bytes) + } + PoolType::SAPLING => Err(anyhow!("TODO: Maybe support this")), + PoolType::ORCHARD => Err(anyhow!("TODO: Maybe support this")), + } + .map_err(|e| anyhow!("{e:?}"))?; + + let pczt = signer.finish(); + + stdout() + .write_all( + &pczt + .serialize() + .map_err(|e| anyhow!("failed to serialize PCZT: {e:?}"))?, + ) + .await?; + + Ok(()) + } +} + +fn parse_pool_type(s: &str) -> anyhow::Result { + match s { + "transparent" => Ok(PoolType::Transparent), + "sapling" => Ok(PoolType::SAPLING), + "orchard" => Ok(PoolType::ORCHARD), + _ => Err(anyhow!( + "Invalid pool type '{s}', must be one of ['transparent', 'sapling', 'orchard']" + )), + } +} + +fn add_transparent( + signer: low_level_signer::Signer, + tx_data: &TransactionData, + txid_parts: &TxDigests, + index: usize, + sig_bytes: Vec, +) -> anyhow::Result { + // Signature has to have the SighashType appended to it. + let (sighash_type, sig_der) = sig_bytes + .split_last() + .ok_or_else(|| anyhow!("Invalid signature bytes"))?; + let sig = secp256k1::ecdsa::Signature::from_der(sig_der) + .map_err(|_| anyhow!("Invalid signature bytes"))?; + + let mut found = false; + + let signer = signer + .sign_transparent_with(|_, bundle, _| { + if let Some(input) = bundle.inputs_mut().get_mut(index) { + found = true; + if *sighash_type != input.sighash_type().encode() { + return Err(TempError::Parser( + transparent::pczt::ParseError::InvalidSighashType, + )); + } + input.append_signature( + index, + |input| { + v5_signature_hash(tx_data, &SignableInput::Transparent(input), txid_parts) + .as_ref() + .try_into() + .unwrap() + }, + sig, + &secp256k1::Secp256k1::verification_only(), + )?; + } + Ok::<_, TempError>(()) + }) + .map_err(|e| anyhow!("{e:?}"))?; + + if found { + Ok(signer) + } else { + Err(anyhow!("No inputs matched the given derivation path")) + } +} + +#[allow(unused)] +#[derive(Debug)] +enum TempError { + Parser(transparent::pczt::ParseError), + Signer(transparent::pczt::SignerError), +} + +impl From for TempError { + fn from(e: transparent::pczt::ParseError) -> Self { + Self::Parser(e) + } +} + +impl From for TempError { + fn from(e: transparent::pczt::SignerError) -> Self { + Self::Signer(e) + } +} diff --git a/src/commands/wallet.rs b/src/commands/wallet.rs index 239d06b..933657a 100644 --- a/src/commands/wallet.rs +++ b/src/commands/wallet.rs @@ -8,6 +8,7 @@ pub(crate) mod display_mnemonic; pub(crate) mod enhance; pub(crate) mod gen_account; pub(crate) mod gen_addr; +pub(crate) mod get_info; pub(crate) mod import_ufvk; pub(crate) mod init; pub(crate) mod init_fvk; @@ -15,10 +16,10 @@ pub(crate) mod list_accounts; pub(crate) mod list_addresses; pub(crate) mod list_tx; pub(crate) mod list_unspent; -pub(crate) mod migrate; pub(crate) mod pay; pub(crate) mod propose; pub(crate) mod reset; +pub(crate) mod restore_mnemonic; pub(crate) mod send; pub(crate) mod shield; pub(crate) mod sync; @@ -33,6 +34,9 @@ pub(crate) enum Command { /// Initialise a new view-only light wallet InitFvk(init_fvk::Command), + /// Restore a wallet from an existing mnemonic phrase + RestoreMnemonic(restore_mnemonic::Command), + /// Decrypt and display the wallet's mnemonic recovery phrase, if any. DisplayMnemonic(display_mnemonic::Command), @@ -45,6 +49,9 @@ pub(crate) enum Command { /// Upgrade an existing light wallet Upgrade(upgrade::Command), + /// Report server and chain info as JSON (server URI, chain name, chain tip height) + GetInfo(get_info::Command), + /// Scan the chain and sync the wallet Sync(sync::Command), @@ -86,9 +93,6 @@ pub(crate) enum Command { /// Shield transparent funds received by the wallet Shield(shield::Command), - /// Migrate Orchard funds to Ironwood (creates a V6/Ironwood transaction) - MigrateToIronwood(migrate::Command), - /// Propose a transfer of funds to the given address and display the proposal Propose(propose::Command), diff --git a/src/commands/wallet/balance.rs b/src/commands/wallet/balance.rs index 76f8d75..b135c7c 100644 --- a/src/commands/wallet/balance.rs +++ b/src/commands/wallet/balance.rs @@ -27,6 +27,17 @@ pub(crate) struct Command { #[arg(long)] #[arg(value_parser = parse_currency)] convert: Option, + + /// Minimum confirmations required for notes to count as spendable, + /// applied to trusted and untrusted notes alike. Defaults to the + /// standard policy (3 trusted / 10 untrusted). + #[arg(long)] + min_confirmations: Option, + + /// Emit a single-line JSON object with raw zatoshi values instead of the + /// human-readable summary. Ignores `--convert`. + #[arg(long)] + json: bool, } impl Command { @@ -42,18 +53,57 @@ impl Command { UnifiedAddressRequest::AllAvailableKeys, )?; - let printer = if let Some(currency) = self.convert { - let tor = tor_client(wallet_dir.as_ref()).await?; - ValuePrinter::with_exchange_rate(&tor, currency).await? - } else { - ValuePrinter::ZecOnly + // In JSON mode we emit raw zatoshis, so skip the exchange-rate fetch + // (which would otherwise spin up a Tor client) entirely. + let printer = match self.convert { + Some(currency) if !self.json => { + let tor = tor_client(wallet_dir.as_ref()).await?; + ValuePrinter::with_exchange_rate(&tor, currency).await? + } + _ => ValuePrinter::ZecOnly, }; - if let Some(wallet_summary) = db_data.get_wallet_summary(ConfirmationsPolicy::default())? { + let confirmations_policy = self + .min_confirmations + .map_or_else(ConfirmationsPolicy::default, |n| { + ConfirmationsPolicy::new_symmetrical(n, true) + }); + + if self.json { + let wallet_summary = db_data + .get_wallet_summary(confirmations_policy)? + .ok_or_else(|| anyhow!("Insufficient information to build a wallet summary."))?; + let balance = wallet_summary + .account_balances() + .get(&account.id()) + .ok_or_else(|| { + anyhow!( + "Selected account {:?} not found in wallet summary", + account.id() + ) + })?; + + let json = serde_json::json!({ + "total": balance.total().into_u64(), + "sapling_spendable": balance.sapling_balance().spendable_value().into_u64(), + "orchard_spendable": balance.orchard_balance().spendable_value().into_u64(), + "transparent_spendable": balance.unshielded_balance().spendable_value().into_u64(), + "chain_tip_height": u32::from(wallet_summary.chain_tip_height()), + }); + println!("{}", serde_json::to_string(&json)?); + return Ok(()); + } + + if let Some(wallet_summary) = db_data.get_wallet_summary(confirmations_policy)? { let balance = wallet_summary .account_balances() .get(&account.id()) - .ok_or_else(|| anyhow!("Missing account 0"))?; + .ok_or_else(|| { + anyhow!( + "Selected account {:?} not found in wallet summary", + account.id() + ) + })?; println!("{:#?}", wallet_summary); println!("{:?}", address.map(|a| a.encode(¶ms))); diff --git a/src/commands/wallet/derive_address.rs b/src/commands/wallet/derive_address.rs index f001cd2..4b2115f 100644 --- a/src/commands/wallet/derive_address.rs +++ b/src/commands/wallet/derive_address.rs @@ -32,7 +32,10 @@ pub(crate) struct Command { impl Command { pub(crate) fn run(self) -> anyhow::Result<()> { - let params = self.network.params(); + // `data::Network` implements `Parameters` directly; address derivation + // only needs the network *type*, so the regtest activation heights are + // irrelevant here. + let params = self.network; let mnemonic = >::from_phrase(self.mnemonic.trim()) .map_err(|e| anyhow!("invalid mnemonic: {e}"))?; diff --git a/src/commands/wallet/gen_account.rs b/src/commands/wallet/gen_account.rs index f4315da..8640d09 100644 --- a/src/commands/wallet/gen_account.rs +++ b/src/commands/wallet/gen_account.rs @@ -50,6 +50,7 @@ impl Command { let birthday = wallet::init::Command::get_wallet_birthday( client, + ¶ms, chain_tip.saturating_sub(100).into(), None, ) diff --git a/src/commands/wallet/get_info.rs b/src/commands/wallet/get_info.rs new file mode 100644 index 0000000..7a37430 --- /dev/null +++ b/src/commands/wallet/get_info.rs @@ -0,0 +1,37 @@ +use clap::Args; +use zcash_client_backend::proto::service; + +use crate::{config::get_wallet_network, remote::ConnectionArgs}; + +// Options accepted for the `get-info` command +#[derive(Debug, Args)] +pub(crate) struct Command { + #[command(flatten)] + connection: ConnectionArgs, +} + +impl Command { + pub(crate) async fn run(self, wallet_dir: Option) -> Result<(), anyhow::Error> { + let params = get_wallet_network(wallet_dir.as_ref())?; + + // The server we are about to connect to (for the reported URI). + let server_uri = self.connection.server.pick(params)?.uri(); + + let mut client = self.connection.connect(params, wallet_dir.as_ref()).await?; + let info = client + .get_lightd_info(service::Empty {}) + .await? + .into_inner(); + + // Stable, machine-consumed shape (see zcash_local_net's + // `client::zcash_devtool` GetInfoResponse parser). + let json = serde_json::json!({ + "server_uri": server_uri, + "chain_name": info.chain_name, + "chain_tip_height": info.block_height, + }); + println!("{}", serde_json::to_string(&json)?); + + Ok(()) + } +} diff --git a/src/commands/wallet/import_ufvk.rs b/src/commands/wallet/import_ufvk.rs index 263d2e0..36518c8 100644 --- a/src/commands/wallet/import_ufvk.rs +++ b/src/commands/wallet/import_ufvk.rs @@ -12,7 +12,11 @@ use zcash_keys::keys::UnifiedFullViewingKey; use zcash_protocol::consensus; use zip32::fingerprint::SeedFingerprint; -use crate::{data::get_db_paths, error, parse_hex, remote::ConnectionArgs}; +use crate::{ + data::{Network, get_db_paths}, + error, parse_hex, + remote::ConnectionArgs, +}; // Options accepted for the `import-ufvk` command #[derive(Debug, Args)] @@ -46,9 +50,16 @@ impl Command { let ufvk = UnifiedFullViewingKey::parse(&ufvk).map_err(|e| anyhow!("{e}"))?; let params = match network { - consensus::NetworkType::Main => crate::network::Network::mainnet(), - consensus::NetworkType::Test => crate::network::Network::testnet(), - consensus::NetworkType::Regtest => crate::network::Network::regtest(), + consensus::NetworkType::Main => Network::Main, + consensus::NetworkType::Test => Network::Test, + #[cfg(feature = "regtest_support")] + consensus::NetworkType::Regtest => Network::default_regtest(), + #[cfg(not(feature = "regtest_support"))] + consensus::NetworkType::Regtest => { + return Err(anyhow!( + "UFVK is for regtest, which requires the `regtest_support` feature" + )); + } }; let (_, db_data) = get_db_paths(wallet_dir.as_ref()); diff --git a/src/commands/wallet/init.rs b/src/commands/wallet/init.rs index 98eced7..429ec46 100644 --- a/src/commands/wallet/init.rs +++ b/src/commands/wallet/init.rs @@ -6,10 +6,10 @@ use tokio::io::AsyncWriteExt; use tonic::transport::Channel; use zcash_client_backend::{ - data_api::{AccountBirthday, WalletWrite}, + data_api::{AccountBirthday, WalletWrite, chain::ChainState}, proto::service::{self, compact_tx_streamer_client::CompactTxStreamerClient}, }; -use zcash_protocol::consensus::{BlockHeight, Parameters}; +use zcash_protocol::consensus::{BlockHeight, NetworkUpgrade, Parameters}; use crate::{ config::WalletConfig, @@ -33,21 +33,22 @@ pub(crate) struct Command { #[arg(long)] birthday: Option, - /// The mnemonic phrase to initialise the wallet with. - /// - /// If omitted, the phrase is read interactively (press Enter at the prompt to - /// generate a fresh one). Pass this to run non-interactively, e.g. from a test - /// harness. NOTE: a mnemonic passed on the command line is visible in the - /// process list; this is intended for ephemeral test wallets. - #[arg(long)] - mnemonic: Option, - - /// The network the wallet will be used with: \"test\", \"main\", or \"regtest\" (default is \"test\"). - /// Use \"regtest\" for an Ironwood/NU6.3 chain (and pass an explicit --server). + /// The network the wallet will be used with: \"test\", \"main\", or \"regtest\" + /// (requires the `regtest_support` feature). Default is \"test\". #[arg(short, long)] #[arg(value_parser = Network::parse)] network: Network, + /// Required for `-n regtest`: a TOML file giving the validator's + /// activation height per network upgrade (keys: overwinter, sapling, + /// blossom, heartwood, canopy, nu5, nu6, nu6_1, nu6_2, nu6_3; a missing + /// key means the upgrade is inactive). `nu6_3` activates Ironwood (V6 + /// transactions). The heights are persisted in the wallet config so later + /// commands agree. Rejected for main/test. + #[cfg(feature = "regtest_support")] + #[arg(long)] + activation_heights: Option, + #[command(flatten)] connection: ConnectionArgs, } @@ -55,7 +56,28 @@ pub(crate) struct Command { impl Command { pub(crate) async fn run(self, wallet_dir: Option) -> Result<(), anyhow::Error> { let opts = self; - let params = opts.network.params(); + + // Regtest requires explicit activation heights (persisted below so + // later commands agree); they are rejected for main/test. + #[cfg(feature = "regtest_support")] + let params = match opts.network { + Network::Regtest(_) => { + let path = opts.activation_heights.as_ref().ok_or_else(|| { + anyhow::anyhow!("`-n regtest` requires --activation-heights ") + })?; + Network::Regtest(crate::data::load_activation_heights(path)?) + } + other => { + if opts.activation_heights.is_some() { + return Err(anyhow::anyhow!( + "--activation-heights is only valid with `-n regtest`" + )); + } + other + } + }; + #[cfg(not(feature = "regtest_support"))] + let params = opts.network; let mut client = opts.connection.connect(params, wallet_dir.as_ref()).await?; @@ -94,30 +116,35 @@ impl Command { vec![Box::new(recipient) as _] }; - // Parse or create the wallet's mnemonic phrase. A phrase supplied via - // `--mnemonic` is used directly (non-interactive); otherwise we prompt, - // treating an empty response as a request to generate a fresh phrase. - let (mnemonic, recover_until) = if let Some(phrase) = &opts.mnemonic { + // Parse or create the wallet's mnemonic phrase. `rpassword` + // requires a controlling terminal (it prompts and reads via + // /dev/tty, failing with ENXIO when there is none), so when + // stdin is not a terminal — automation piping the phrase in, + // e.g. the `zcash_local_net` test harness — read a line from + // stdin instead. + let phrase = if std::io::IsTerminal::is_terminal(&std::io::stdin()) { + SecretString::new(rpassword::prompt_password( + "Enter mnemonic (or just press Enter to generate a new one):", + )?) + } else { + let mut line = String::new(); + std::io::BufRead::read_line(&mut std::io::stdin().lock(), &mut line)?; + let phrase = SecretString::new(line.trim().to_string()); + line.zeroize(); + phrase + }; + let (mnemonic, recover_until) = if !phrase.expose_secret().is_empty() { ( - >::from_phrase(phrase.trim())?, + >::from_phrase(phrase.expose_secret())?, Some(chain_tip.into()), ) } else { - let phrase = SecretString::new(rpassword::prompt_password( - "Enter mnemonic (or just press Enter to generate a new one):", - )?); - if !phrase.expose_secret().is_empty() { - ( - >::from_phrase(phrase.expose_secret())?, - Some(chain_tip.into()), - ) - } else { - (Mnemonic::generate(Count::Words24), None) - } + (Mnemonic::generate(Count::Words24), None) }; let birthday = Self::get_wallet_birthday( client, + ¶ms, opts.birthday .unwrap_or(chain_tip.saturating_sub(100)) .into(), @@ -131,7 +158,7 @@ impl Command { recipients.iter().map(|r| r.as_ref() as _), &mnemonic, birthday.height(), - opts.network.params(), + params, )?; let seed = { @@ -151,20 +178,57 @@ impl Command { ) } - pub(crate) async fn get_wallet_birthday( + pub(crate) async fn get_wallet_birthday( mut client: CompactTxStreamerClient, + params: &P, birthday_height: BlockHeight, recover_until: Option, ) -> Result { - // Fetch the tree state corresponding to the last block prior to the wallet's - // birthday height. NOTE: THIS APPROACH LEAKS THE BIRTHDAY TO THE SERVER! - let request = service::BlockId { - height: u64::from(birthday_height).saturating_sub(1), - ..Default::default() + // A shielded wallet's birthday cannot meaningfully precede Sapling + // activation: there is no note commitment tree before then, and + // lightwalletd rejects `GetTreeState` for any height below it. + let sapling_activation = params + .activation_height(NetworkUpgrade::Sapling) + .ok_or_else(|| { + anyhow::anyhow!("Sapling activation height is not set for this network") + })?; + let birthday_height = birthday_height.max(sapling_activation); + + // The birthday is defined by the chain state (note commitment tree + // frontiers and block hash) as of the block *prior* to the birthday + // height. + let birthday = if birthday_height == sapling_activation { + // Edge case: the block prior to the birthday is the last pre-Sapling + // block, whose note commitment trees are empty and whose tree state + // the server cannot serve (`GetTreeState` below Sapling activation + // is rejected). Construct an empty chain state directly, taking the + // prior block's hash from the birthday block's `prev_hash` so block + // scanning can still verify chain continuity. + let birthday_block = client + .get_block(service::BlockId { + height: u64::from(birthday_height), + ..Default::default() + }) + .await? + .into_inner(); + AccountBirthday::from_parts( + ChainState::empty( + birthday_height.saturating_sub(1), + birthday_block.prev_hash(), + ), + recover_until, + ) + } else { + // Fetch the tree state corresponding to the last block prior to the + // wallet's birthday height. NOTE: THIS APPROACH LEAKS THE BIRTHDAY + // TO THE SERVER! + let request = service::BlockId { + height: u64::from(birthday_height) - 1, + ..Default::default() + }; + let treestate = client.get_tree_state(request).await?.into_inner(); + AccountBirthday::from_treestate(treestate, recover_until).map_err(error::Error::from)? }; - let treestate = client.get_tree_state(request).await?.into_inner(); - let birthday = AccountBirthday::from_treestate(treestate, recover_until) - .map_err(error::Error::from)?; Ok(birthday) } diff --git a/src/commands/wallet/init_fvk.rs b/src/commands/wallet/init_fvk.rs index bbde9eb..4b3a55e 100644 --- a/src/commands/wallet/init_fvk.rs +++ b/src/commands/wallet/init_fvk.rs @@ -10,7 +10,12 @@ use zcash_keys::{encoding::decode_extfvk_with_network, keys::UnifiedFullViewingK use zcash_protocol::consensus::NetworkType; use zip32::fingerprint::SeedFingerprint; -use crate::{config::WalletConfig, data::init_dbs, parse_hex, remote::ConnectionArgs}; +use crate::{ + config::WalletConfig, + data::{Network, init_dbs}, + parse_hex, + remote::ConnectionArgs, +}; // Options accepted for the `init-fvk` command #[derive(Debug, Args)] @@ -61,9 +66,16 @@ impl Command { )?; let network = match network_type { - NetworkType::Main => crate::network::Network::mainnet(), - NetworkType::Test => crate::network::Network::testnet(), - NetworkType::Regtest => crate::network::Network::regtest(), + NetworkType::Main => Network::Main, + NetworkType::Test => Network::Test, + #[cfg(feature = "regtest_support")] + NetworkType::Regtest => Network::default_regtest(), + #[cfg(not(feature = "regtest_support"))] + NetworkType::Regtest => { + return Err(anyhow!( + "the regtest network requires the `regtest_support` feature" + )); + } }; let mut client = opts @@ -82,6 +94,7 @@ impl Command { let birthday = super::init::Command::get_wallet_birthday( client, + &network, opts.birthday .unwrap_or(chain_tip.saturating_sub(100)) .into(), diff --git a/src/commands/wallet/list_addresses.rs b/src/commands/wallet/list_addresses.rs index 938e04d..b60c4e9 100644 --- a/src/commands/wallet/list_addresses.rs +++ b/src/commands/wallet/list_addresses.rs @@ -1,20 +1,43 @@ -use clap::Args; +use anyhow::anyhow; +use clap::{Args, ValueEnum}; use uuid::Uuid; use zcash_client_backend::data_api::Account; use zcash_client_sqlite::WalletDb; -use zcash_keys::keys::UnifiedAddressRequest; +use zcash_keys::{ + address::{Address, UnifiedAddress}, + keys::UnifiedAddressRequest, +}; use crate::{commands::select_account, config::get_wallet_network, data::get_db_paths}; #[cfg(feature = "qr")] use qrcode::{QrCode, render::unicode}; +/// Which receiver of the account's unified address to emit. +#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)] +enum Receiver { + /// The full unified address (all available receivers). + Unified, + /// The transparent (P2PKH) receiver, as a bare transparent address. + Transparent, + /// The Sapling receiver, as a bare Sapling address. + Sapling, + /// The Orchard receiver, as a unified address carrying only Orchard. + Orchard, +} + // Options accepted for the `list-addresses` command #[derive(Debug, Args)] pub(crate) struct Command { /// The UUID of the account to list addresses for account_id: Option, + /// Which receiver(s) to emit. May be repeated to emit several. Defaults to + /// `unified`, which preserves the full default-address output. Non-unified + /// receivers are printed one per line as `Receiver():
`. + #[arg(long, value_enum)] + receiver: Vec, + /// A flag indicating whether a QR code should be displayed for the address. #[cfg(feature = "qr")] #[arg(long, default_value = "true")] @@ -28,24 +51,66 @@ impl Command { let db_data = WalletDb::for_path(db_data, params, (), ())?; let account = select_account(&db_data, self.account_id)?; - - println!("Account {:?}", account.id()); let (ua, _) = account .uivk() .default_address(UnifiedAddressRequest::AllAvailableKeys)?; - let ua_str = ua.encode(¶ms); - println!(" Default Address: {ua_str}"); - - #[cfg(feature = "qr")] - if self.display_qr { - let code = QrCode::new(ua_str)?; - let ua_qr = code - .render::() - .dark_color(unicode::Dense1x2::Light) - .light_color(unicode::Dense1x2::Dark) - .quiet_zone(true) - .build(); - println!("{}", ua_qr); + + // An empty `--receiver` list defaults to the unified address, preserving + // the historical output. + let receivers = if self.receiver.is_empty() { + &[Receiver::Unified][..] + } else { + &self.receiver[..] + }; + + for receiver in receivers { + match receiver { + Receiver::Unified => { + println!("Account {:?}", account.id()); + let ua_str = ua.encode(¶ms); + println!(" Default Address: {ua_str}"); + + #[cfg(feature = "qr")] + if self.display_qr { + let code = QrCode::new(ua_str)?; + let ua_qr = code + .render::() + .dark_color(unicode::Dense1x2::Light) + .light_color(unicode::Dense1x2::Dark) + .quiet_zone(true) + .build(); + println!("{}", ua_qr); + } + } + Receiver::Transparent => { + let addr = ua + .transparent() + .ok_or_else(|| anyhow!("Account address has no transparent receiver"))?; + println!( + "Receiver(transparent): {}", + Address::from(*addr).encode(¶ms) + ); + } + Receiver::Sapling => { + let addr = ua + .sapling() + .ok_or_else(|| anyhow!("Account address has no sapling receiver"))?; + println!( + "Receiver(sapling): {}", + Address::from(*addr).encode(¶ms) + ); + } + Receiver::Orchard => { + let addr = ua + .orchard() + .ok_or_else(|| anyhow!("Account address has no orchard receiver"))?; + // Orchard receivers have no bare encoding; emit a unified + // address carrying only the Orchard receiver. + let orchard_only = UnifiedAddress::from_receivers(Some(*addr), None, None) + .ok_or_else(|| anyhow!("Failed to encode orchard-only unified address"))?; + println!("Receiver(orchard): {}", orchard_only.encode(¶ms)); + } + } } Ok(()) diff --git a/src/commands/wallet/list_tx.rs b/src/commands/wallet/list_tx.rs index 2fd1c96..61f30f0 100644 --- a/src/commands/wallet/list_tx.rs +++ b/src/commands/wallet/list_tx.rs @@ -24,6 +24,12 @@ pub(crate) struct Command { /// with one record per output row. Defaults to "text". #[arg(short, long)] mode: Option, + + /// Emit a single-line JSON array of `{"txid", "mined_height"}` objects + /// (`mined_height` is null for unmined transactions). Takes precedence + /// over `--mode`. + #[arg(long)] + json: bool, } #[derive(Clone, Copy, Debug)] @@ -98,16 +104,20 @@ impl Command { WHERE txid = :txid", )?; - match mode { - ListMode::Text => { - println!("Transactions:"); - } - ListMode::Csv => { - println!( - "Date,Action,Symbol,Volume,Currency,Account,Total,Price,Fee,FeeCurrency,Memo" - ); + if !self.json { + match mode { + ListMode::Text => { + println!("Transactions:"); + } + ListMode::Csv => { + println!( + "Date,Action,Symbol,Volume,Currency,Account,Total,Price,Fee,FeeCurrency,Memo" + ); + } } } + + let mut json_txs = Vec::new(); for row in stmt_txs.query_and_then( named_params! {":account_uuid": self.account_id }, |row| -> anyhow::Result<_> { @@ -150,7 +160,18 @@ impl Command { }, )? { let tx = row?; - tx.print(mode)?; + if self.json { + json_txs.push(serde_json::json!({ + "txid": tx.txid.to_string(), + "mined_height": tx.mined_height.map(u32::from), + })); + } else { + tx.print(mode)?; + } + } + + if self.json { + println!("{}", serde_json::to_string(&json_txs)?); } Ok(()) diff --git a/src/commands/wallet/migrate.rs b/src/commands/wallet/migrate.rs deleted file mode 100644 index 5a4b2e9..0000000 --- a/src/commands/wallet/migrate.rs +++ /dev/null @@ -1,126 +0,0 @@ -use anyhow::anyhow; -use clap::Args; -use rand::rngs::OsRng; -use secrecy::ExposeSecret; -use uuid::Uuid; - -use zcash_client_backend::{ - data_api::{ - Account, WalletRead, - wallet::{ConfirmationsPolicy, SpendingKeys, create_orchard_to_ironwood_transaction}, - }, - fees::StandardFeeRule, - proto::service, - wallet::OvkPolicy, -}; -use zcash_client_sqlite::{WalletDb, util::SystemClock}; -use zcash_keys::keys::UnifiedSpendingKey; -use zcash_proofs::prover::LocalTxProver; -use zcash_protocol::value::Zatoshis; - -use crate::{ - commands::select_account, config::WalletConfig, data::get_db_paths, error, - remote::ConnectionArgs, -}; - -// Options accepted for the `migrate-to-ironwood` command -#[derive(Debug, Args)] -pub(crate) struct Command { - /// The UUID of the account to migrate Orchard funds from - account_id: Option, - - /// The minimum amount (in zatoshis) of Orchard value to migrate to Ironwood. - /// - /// Note selection picks at least this much value; the resulting transaction - /// then migrates the full selected Orchard value minus fees, so no Orchard - /// change output is created. - #[arg(long)] - amount: u64, - - /// age identity file to decrypt the mnemonic phrase with - #[arg(short, long)] - identity: String, - - #[command(flatten)] - connection: ConnectionArgs, -} - -impl Command { - pub(crate) async fn run(self, wallet_dir: Option) -> Result<(), anyhow::Error> { - let mut config = WalletConfig::read(wallet_dir.as_ref())?; - let params = config.network(); - - let (_, db_data) = get_db_paths(wallet_dir.as_ref()); - let mut db_data = WalletDb::for_path(db_data, params, SystemClock, OsRng)?; - let account = select_account(&db_data, self.account_id)?; - let derivation = account.source().key_derivation().ok_or(anyhow!( - "Cannot spend from view-only accounts; an Ironwood migration requires spending keys" - ))?; - - // Decrypt the mnemonic to access the seed. - let identities = age::IdentityFile::from_file(self.identity)?.into_identities()?; - let seed = config - .decrypt_seed(identities.iter().map(|i| i.as_ref() as _))? - .ok_or(anyhow!("Seed must be present to enable sending"))?; - - let usk = UnifiedSpendingKey::from_seed( - ¶ms, - seed.expose_secret(), - derivation.account_index(), - ) - .map_err(error::Error::from)?; - - let mut client = self.connection.connect(params, wallet_dir.as_ref()).await?; - - // Build, prove, sign, and persist the Orchard -> Ironwood migration - // transaction. This spends only Orchard notes and creates a single - // Ironwood (V6) output to the account's internal Orchard receiver. - println!("Creating Ironwood migration transaction..."); - let prover = LocalTxProver::bundled(); - let amount = Zatoshis::from_u64(self.amount)?; - - let migration = create_orchard_to_ironwood_transaction( - &mut db_data, - ¶ms, - &prover, - &prover, - &SpendingKeys::from_unified_spending_key(usk), - OvkPolicy::Sender, - amount, - None, - &StandardFeeRule::Zip317, - ConfirmationsPolicy::MIN, - ) - .map_err(error::Error::Migrate)?; - - // Send the transaction. - println!( - "Migrating {} zatoshis to Ironwood (fee {} zatoshis)...", - u64::from(migration.migrated_amount()), - u64::from(migration.fee_amount()), - ); - let (txid, raw_tx) = db_data - .get_transaction(migration.txid())? - .map(|tx| { - let mut raw_tx = service::RawTransaction::default(); - tx.write(&mut raw_tx.data).unwrap(); - (tx.txid(), raw_tx) - }) - .ok_or(anyhow!( - "Transaction not found for id {:?}", - migration.txid() - ))?; - let response = client.send_transaction(raw_tx).await?.into_inner(); - - if response.error_code != 0 { - Err(error::Error::SendFailed { - code: response.error_code, - reason: response.error_message, - } - .into()) - } else { - println!("{txid}"); - Ok(()) - } - } -} diff --git a/src/commands/wallet/reset.rs b/src/commands/wallet/reset.rs index 16e46d1..7851eb1 100644 --- a/src/commands/wallet/reset.rs +++ b/src/commands/wallet/reset.rs @@ -59,9 +59,13 @@ impl Command { ) }; - let birthday = - super::init::Command::get_wallet_birthday(client, config.birthday(), Some(chain_tip)) - .await?; + let birthday = super::init::Command::get_wallet_birthday( + client, + ¶ms, + config.birthday(), + Some(chain_tip), + ) + .await?; // Erase the wallet state (excluding key material). erase_wallet_state(wallet_dir.as_ref()).await; diff --git a/src/commands/wallet/restore_mnemonic.rs b/src/commands/wallet/restore_mnemonic.rs new file mode 100644 index 0000000..cc3c4cb --- /dev/null +++ b/src/commands/wallet/restore_mnemonic.rs @@ -0,0 +1,172 @@ +use age::secrecy::ExposeSecret; +use bip0039::{English, Mnemonic}; +use clap::Args; +use secrecy::{ExposeSecret as _, SecretString, SecretVec, Zeroize}; +use tokio::io::AsyncWriteExt; + +use zcash_client_backend::proto::service; +use zcash_protocol::consensus::{NetworkUpgrade, Parameters}; + +use crate::{config::WalletConfig, data::Network, remote::ConnectionArgs}; + +// Options accepted for the `restore-mnemonic` command +#[derive(Debug, Args)] +pub(crate) struct Command { + /// A name for the account + #[arg(long)] + name: String, + + /// age identity file to encrypt the mnemonic phrase to (generated if it doesn't exist) + #[arg(short, long)] + identity: String, + + /// The wallet's birthday. Defaults to the network's Sapling activation height, so the + /// wallet scans the entire history it could possibly have received funds in. + #[arg(long)] + birthday: Option, + + /// The network the wallet will be used with: \"test\", \"main\", or \"regtest\" + /// (requires the `regtest_support` feature). Default is \"test\". + #[arg(short, long)] + #[arg(value_parser = Network::parse)] + network: Network, + + /// Required for `-n regtest`: a TOML file giving the validator's + /// activation height per network upgrade (keys: overwinter, sapling, + /// blossom, heartwood, canopy, nu5, nu6, nu6_1, nu6_2; a missing key + /// means the upgrade is inactive). The heights are persisted in the + /// wallet config so later commands agree. Rejected for main/test. + #[cfg(feature = "regtest_support")] + #[arg(long)] + activation_heights: Option, + + #[command(flatten)] + connection: ConnectionArgs, +} + +impl Command { + pub(crate) async fn run(self, wallet_dir: Option) -> Result<(), anyhow::Error> { + let opts = self; + + // Regtest requires explicit activation heights (persisted below so + // later commands agree); they are rejected for main/test. + #[cfg(feature = "regtest_support")] + let params = match opts.network { + Network::Regtest(_) => { + let path = opts.activation_heights.as_ref().ok_or_else(|| { + anyhow::anyhow!("`-n regtest` requires --activation-heights ") + })?; + Network::Regtest(crate::data::load_activation_heights(path)?) + } + other => { + if opts.activation_heights.is_some() { + return Err(anyhow::anyhow!( + "--activation-heights is only valid with `-n regtest`" + )); + } + other + } + }; + #[cfg(not(feature = "regtest_support"))] + let params = opts.network; + + let mut client = opts.connection.connect(params, wallet_dir.as_ref()).await?; + + // Get the current chain height (for the recover-until height). + let chain_tip: u32 = client + .get_latest_block(service::ChainSpec::default()) + .await? + .into_inner() + .height + .try_into() + .expect("block heights must fit into u32"); + + let recipients = if tokio::fs::try_exists(&opts.identity).await? { + age::IdentityFile::from_file(opts.identity)?.to_recipients()? + } else { + eprintln!("Generating a new age identity to encrypt the mnemonic phrase"); + let identity = age::x25519::Identity::generate(); + let recipient = identity.to_public(); + + // Write it to the provided path so we have it for next time. + let mut f = tokio::fs::File::create_new(opts.identity).await?; + f.write_all( + format!( + "# created: {}\n", + chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true) + ) + .as_bytes(), + ) + .await?; + f.write_all(format!("# public key: {recipient}\n").as_bytes()) + .await?; + f.write_all(format!("{}\n", identity.to_string().expose_secret()).as_bytes()) + .await?; + f.flush().await?; + + vec![Box::new(recipient) as _] + }; + + // Read the mnemonic phrase to restore from. `rpassword` requires a + // controlling terminal (it prompts and reads via /dev/tty, failing + // with ENXIO when there is none), so when stdin is not a terminal — + // automation piping the phrase in — read a line from stdin instead. + let phrase = if std::io::IsTerminal::is_terminal(&std::io::stdin()) { + SecretString::new(rpassword::prompt_password("Enter mnemonic to restore:")?) + } else { + let mut line = String::new(); + std::io::BufRead::read_line(&mut std::io::stdin().lock(), &mut line)?; + let phrase = SecretString::new(line.trim().to_string()); + line.zeroize(); + phrase + }; + if phrase.expose_secret().is_empty() { + return Err(anyhow::anyhow!( + "A mnemonic phrase is required to restore a wallet" + )); + } + let mnemonic = >::from_phrase(phrase.expose_secret())?; + + // Default to the network's Sapling activation height, so a restored + // wallet scans for all funds it could possibly have received, unless + // the caller knows (and provides) a more recent birthday. + let birthday_height = opts.birthday.map(Into::into).unwrap_or_else(|| { + params + .activation_height(NetworkUpgrade::Sapling) + .expect("Sapling activation height is known") + }); + + let birthday = super::init::Command::get_wallet_birthday( + client, + ¶ms, + birthday_height, + Some(chain_tip.into()), + ) + .await?; + + // Save the wallet keys to disk. + WalletConfig::init_with_mnemonic( + wallet_dir.as_ref(), + recipients.iter().map(|r| r.as_ref() as _), + &mnemonic, + birthday.height(), + params, + )?; + + let seed = { + let mut seed = mnemonic.to_seed(""); + let secret = seed.to_vec(); + seed.zeroize(); + SecretVec::new(secret) + }; + + super::init::Command::init_dbs( + params, + wallet_dir.as_ref(), + &opts.name, + &seed, + birthday, + None, + ) + } +} diff --git a/src/commands/wallet/send.rs b/src/commands/wallet/send.rs index 4d662a7..6f266d3 100644 --- a/src/commands/wallet/send.rs +++ b/src/commands/wallet/send.rs @@ -77,6 +77,15 @@ pub(crate) struct Command { #[arg(long)] #[arg(value_parser = crate::commands::wallet::send::parse_tx_version)] tx_version: Option, + + /// Minimum confirmations required for notes to be spendable, + /// applied to trusted and untrusted notes alike. Defaults to the + /// standard policy (3 trusted / 10 untrusted). Lowering this aids + /// regtest automation on shallow chains but weakens transaction + /// distinguishability protections; prefer the default on public + /// networks. + #[arg(long)] + min_confirmations: Option, } pub(crate) trait PaymentContext { @@ -87,6 +96,9 @@ pub(crate) trait PaymentContext { fn min_split_output_value(&self) -> u64; fn require_confirmation(&self) -> bool; fn tx_version(&self) -> Option; + fn confirmations_policy(&self) -> ConfirmationsPolicy { + ConfirmationsPolicy::default() + } } impl PaymentContext for Command { @@ -118,6 +130,14 @@ impl PaymentContext for Command { fn tx_version(&self) -> Option { self.tx_version } + + fn confirmations_policy(&self) -> ConfirmationsPolicy { + self.min_confirmations.map_or_else( + ConfirmationsPolicy::default, + // Allowing zero-conf shielding matches `ConfirmationsPolicy::MIN`. + |n| ConfirmationsPolicy::new_symmetrical(n, true), + ) + } } pub(crate) fn parse_tx_version(s: &str) -> anyhow::Result { @@ -211,7 +231,7 @@ pub(crate) async fn pay( &input_selector, &change_strategy, request, - ConfirmationsPolicy::default(), + context.confirmations_policy(), context.tx_version(), ) .map_err(error::Error::from)?; diff --git a/src/commands/wallet/sync.rs b/src/commands/wallet/sync.rs index 6a0aba3..76c95c3 100644 --- a/src/commands/wallet/sync.rs +++ b/src/commands/wallet/sync.rs @@ -683,8 +683,9 @@ async fn refresh_utxos( Script(script::Code(reply.script)), ), Some(BlockHeight::from(u32::try_from(reply.height)?)), - // recipient_account / recipient_key_scope / funding_account - // are not known from a lightwalletd UTXO reply. + // A UTXO fetched from lightwalletd carries no wallet-account + // context (recipient_account / recipient_key_scope / + // funding_account); the wallet associates it on store. None, None, None, diff --git a/src/commands/wallet/tree/explore.rs b/src/commands/wallet/tree/explore.rs index f9ab808..2a53fd8 100644 --- a/src/commands/wallet/tree/explore.rs +++ b/src/commands/wallet/tree/explore.rs @@ -22,15 +22,12 @@ use tracing::{info, warn}; use zcash_client_backend::data_api::{WalletCommitmentTrees, WalletRead}; use zcash_client_sqlite::{WalletDb, wallet::commitment_tree::SqliteShardStore}; use zcash_primitives::merkle_tree::HashSer; -use zcash_protocol::{ - ShieldedProtocol, - consensus::{BlockHeight, Network}, -}; +use zcash_protocol::{ShieldedProtocol, consensus::BlockHeight}; use crate::{ ShutdownListener, config::get_wallet_network, - data::get_db_paths, + data::{Network, get_db_paths}, tui::{self, Tui}, }; diff --git a/src/commands/zip48/init.rs b/src/commands/zip48/init.rs index e0386f7..f478962 100644 --- a/src/commands/zip48/init.rs +++ b/src/commands/zip48/init.rs @@ -23,7 +23,8 @@ pub(crate) struct Command { #[arg(long, required = false)] new: bool, - /// The network the wallet will be used with: \"test\" or \"main\" + /// The network the wallet will be used with: \"test\", \"main\", or \"regtest\" + /// (requires the `regtest_support` feature) #[arg(short, long)] #[arg(value_parser = Network::parse)] network: Network, @@ -31,7 +32,7 @@ pub(crate) struct Command { impl Command { pub(crate) fn run(self, wallet_dir: Option) -> Result<(), anyhow::Error> { - let params = self.network.params(); + let params = self.network; let recipients = if fs::exists(&self.identity)? { age::IdentityFile::from_file(self.identity)?.to_recipients()? @@ -77,7 +78,7 @@ impl Command { params .activation_height(consensus::NetworkUpgrade::Nu6) .expect("active"), - self.network.params(), + self.network, )?; // Initialise the block and wallet DBs. diff --git a/src/config.rs b/src/config.rs index 4993feb..90c7a69 100644 --- a/src/config.rs +++ b/src/config.rs @@ -9,6 +9,8 @@ use serde::{Deserialize, Serialize}; use zcash_protocol::consensus::{self, BlockHeight, Parameters}; +#[cfg(feature = "regtest_support")] +use crate::data::ActivationHeights; use crate::{ data::{DEFAULT_WALLET_DIR, Network}, error, @@ -17,7 +19,7 @@ use crate::{ const KEYS_FILE: &str = "keys.toml"; pub(crate) struct WalletConfig { - network: crate::network::Network, + network: Network, seed_ciphertext: Option, birthday: BlockHeight, } @@ -28,7 +30,7 @@ impl WalletConfig { recipients: impl Iterator, mnemonic: &Mnemonic, birthday: BlockHeight, - network: crate::network::Network, + network: Network, ) -> Result<(), anyhow::Error> { init_wallet_config( wallet_dir, @@ -41,7 +43,7 @@ impl WalletConfig { pub(crate) fn init_without_mnemonic>( wallet_dir: Option

, birthday: BlockHeight, - network: crate::network::Network, + network: Network, ) -> Result<(), anyhow::Error> { init_wallet_config(wallet_dir, None, birthday, network) } @@ -66,7 +68,7 @@ impl WalletConfig { .transpose() } - pub(crate) fn network(&self) -> crate::network::Network { + pub(crate) fn network(&self) -> Network { self.network } @@ -79,7 +81,7 @@ fn init_wallet_config>( wallet_dir: Option

, mnemonic: Option, birthday: BlockHeight, - network: crate::network::Network, + network: Network, ) -> Result<(), anyhow::Error> { // Create the wallet directory. let wallet_dir = wallet_dir @@ -97,8 +99,13 @@ fn init_wallet_config>( let config = ConfigEncoding { mnemonic, - network: Some(Network::from(network).name().to_string()), + network: Some(network.name().to_string()), birthday: Some(u32::from(birthday)), + #[cfg(feature = "regtest_support")] + activation_heights: match network { + Network::Regtest(local) => Some(ActivationHeights::from_local_network(local)), + _ => None, + }, }; let config_str = toml::to_string(&config) @@ -125,20 +132,31 @@ impl WalletConfig { keys_file.read_to_string(&mut conf_str)?; let config: ConfigEncoding = toml::from_str(&conf_str)?; - let network = config.network.map_or_else( - || Ok(crate::network::Network::testnet()), + let network = config.network.as_deref().map_or_else( + || Ok(Network::Test), |network_name| { - Network::parse(network_name.trim()) - .map(|n| n.params()) - .map_err(|_| error::Error::InvalidKeysFile) + Network::parse(network_name.trim()).map_err(|_| error::Error::InvalidKeysFile) }, )?; - let birthday = config.birthday.map(BlockHeight::from).unwrap_or( + // For regtest, replace the parsed default heights with the ones + // persisted at `init` so this command agrees with the wallet's chain. + #[cfg(feature = "regtest_support")] + let network = match network { + Network::Regtest(_) => Network::Regtest( + config + .activation_heights + .ok_or(error::Error::InvalidKeysFile)? + .to_local_network(), + ), + other => other, + }; + + let birthday = config.birthday.map(BlockHeight::from).unwrap_or_else(|| { network .activation_height(consensus::NetworkUpgrade::Sapling) - .expect("Sapling activation height is known."), - ); + .expect("Sapling activation height is known.") + }); Ok(Self { network, @@ -153,6 +171,9 @@ struct ConfigEncoding { mnemonic: Option, network: Option, birthday: Option, + /// Regtest activation heights, present only for `network = "regtest"`. + #[cfg(feature = "regtest_support")] + activation_heights: Option, } fn encrypt_mnemonic<'a>( @@ -203,7 +224,7 @@ fn decrypt_seed<'a>( pub(crate) fn get_wallet_network>( wallet_dir: Option

, -) -> Result { +) -> Result { Ok(WalletConfig::read(wallet_dir)?.network) } @@ -214,3 +235,51 @@ pub(crate) fn get_wallet_seed<'a, P: AsRef>( let mut config = WalletConfig::read(wallet_dir)?; config.decrypt_seed(identities) } + +#[cfg(all(test, feature = "regtest_support"))] +mod tests { + use super::*; + use zcash_protocol::consensus::NetworkUpgrade; + + /// A regtest wallet config persisted by `init` must carry the activation + /// heights into `keys.toml` and reconstruct them on read, so commands + /// after `init` build transactions against the same chain. + #[test] + fn regtest_activation_heights_persist_and_reload() { + let dir = + std::env::temp_dir().join(format!("zcash-devtool-cfg-test-{}", std::process::id())); + let _ = fs::remove_dir_all(&dir); + let dir = dir.to_str().unwrap().to_string(); + + let heights: ActivationHeights = toml::from_str( + "overwinter = 1\nsapling = 1\nblossom = 1\nheartwood = 1\ncanopy = 1\n\ + nu5 = 2\nnu6 = 2\nnu6_1 = 5\n", + ) + .unwrap(); + let network = Network::Regtest(heights.to_local_network()); + + WalletConfig::init_without_mnemonic(Some(&dir), BlockHeight::from_u32(0), network).unwrap(); + + // The persisted file records the heights as a table. + let keys_toml = fs::read_to_string(Path::new(&dir).join(KEYS_FILE)).unwrap(); + assert!(keys_toml.contains("[activation_heights]"), "{keys_toml}"); + + let reloaded = WalletConfig::read(Some(&dir)).unwrap(); + match reloaded.network { + Network::Regtest(local) => { + assert_eq!( + local.activation_height(NetworkUpgrade::Nu5), + Some(BlockHeight::from_u32(2)) + ); + assert_eq!( + local.activation_height(NetworkUpgrade::Nu6_1), + Some(BlockHeight::from_u32(5)) + ); + assert_eq!(local.activation_height(NetworkUpgrade::Nu6_2), None); + } + other => panic!("expected regtest, got {other:?}"), + } + + fs::remove_dir_all(&dir).unwrap(); + } +} diff --git a/src/data.rs b/src/data.rs index a41e51b..12bf60c 100644 --- a/src/data.rs +++ b/src/data.rs @@ -8,7 +8,9 @@ use zcash_client_sqlite::{FsBlockDb, WalletDb}; use tracing::error; use zcash_client_sqlite::chain::BlockMeta; -use zcash_protocol::consensus::{self, Parameters}; +use zcash_protocol::consensus::{self, BlockHeight, Parameters}; +#[cfg(feature = "regtest_support")] +use zcash_protocol::local_consensus::LocalNetwork; use crate::error; @@ -22,9 +24,11 @@ pub(crate) enum Network { #[default] Test, Main, - /// Regtest / local consensus for the zecd Ironwood harness; NU6.3 (Ironwood) - /// is active per the harness activation schedule. See [`crate::network`]. - Regtest, + /// Regtest, carrying the caller-chosen activation heights. These are + /// fixed at `init` (from `--activation-heights`) and persisted in the + /// wallet config so later commands agree. + #[cfg(feature = "regtest_support")] + Regtest(LocalNetwork), } impl Network { @@ -32,7 +36,12 @@ impl Network { match name { "main" => Ok(Network::Main), "test" => Ok(Network::Test), - "regtest" => Ok(Network::Regtest), + // `-n regtest` on the CLI yields the default heights; `init` + // overrides them from the required `--activation-heights` file + // before persisting. Commands that only need the network *type* + // (e.g. address encoding) are unaffected by the heights. + #[cfg(feature = "regtest_support")] + "regtest" => Ok(Network::Regtest(DEFAULT_REGTEST)), other => Err(format!("Unsupported network: {other}")), } } @@ -41,32 +50,136 @@ impl Network { match self { Network::Test => "test", Network::Main => "main", - Network::Regtest => "regtest", + #[cfg(feature = "regtest_support")] + Network::Regtest(_) => "regtest", } } - /// Returns the consensus parameters for this network. - pub(crate) fn params(self) -> crate::network::Network { - match self { - Network::Test => crate::network::Network::testnet(), - Network::Main => crate::network::Network::mainnet(), - Network::Regtest => crate::network::Network::regtest(), + /// Regtest with the built-in default activation heights, for CLI paths + /// (address encoding, UFVK import) that don't take an explicit set. + #[cfg(feature = "regtest_support")] + pub(crate) fn default_regtest() -> Network { + Network::Regtest(DEFAULT_REGTEST) + } +} + +/// Fallback regtest activation heights for CLI paths that don't take an +/// explicit set (see [`Network::default_regtest`]). Wallets created by +/// `init` do NOT use this — they carry the heights from the required +/// `--activation-heights` file. These defaults match the `zcash_local_net` +/// wallet-funding zebrad fixture (pre-NU5 at 1, everything NU5+ at 2). +/// +/// Transaction construction picks the consensus branch ID from the active +/// heights, so any drift between a wallet's heights and the launched +/// validator's makes the validator reject transactions built while the tip +/// is inside the drifted window. +#[cfg(feature = "regtest_support")] +const DEFAULT_REGTEST: LocalNetwork = LocalNetwork { + overwinter: Some(BlockHeight::from_u32(1)), + sapling: Some(BlockHeight::from_u32(1)), + blossom: Some(BlockHeight::from_u32(1)), + heartwood: Some(BlockHeight::from_u32(1)), + canopy: Some(BlockHeight::from_u32(1)), + nu5: Some(BlockHeight::from_u32(2)), + nu6: Some(BlockHeight::from_u32(2)), + nu6_1: Some(BlockHeight::from_u32(2)), + nu6_2: Some(BlockHeight::from_u32(2)), + // NU6.3 (Ironwood) is a stable network upgrade in the official librustzcash + // release candidate; V6 (Ironwood) transactions activate at its height. + nu6_3: Some(BlockHeight::from_u32(2)), + #[cfg(zcash_unstable = "nu7")] + nu7: Some(BlockHeight::from_u32(2)), +}; + +/// A `LocalNetwork`-shaped set of regtest activation heights, one optional +/// height per network upgrade (a missing entry means "not active"). This is +/// the schema of the `--activation-heights` TOML file and of the +/// `[activation_heights]` table persisted in the wallet config; the two share +/// this type so a wallet's heights round-trip from the file the operator +/// commits to revision control. +#[cfg(feature = "regtest_support")] +#[derive(Clone, Copy, Debug, Default, serde::Deserialize, serde::Serialize)] +#[serde(default, deny_unknown_fields)] +pub(crate) struct ActivationHeights { + pub(crate) overwinter: Option, + pub(crate) sapling: Option, + pub(crate) blossom: Option, + pub(crate) heartwood: Option, + pub(crate) canopy: Option, + pub(crate) nu5: Option, + pub(crate) nu6: Option, + pub(crate) nu6_1: Option, + pub(crate) nu6_2: Option, + pub(crate) nu6_3: Option, + #[cfg(zcash_unstable = "nu7")] + pub(crate) nu7: Option, +} + +#[cfg(feature = "regtest_support")] +impl ActivationHeights { + pub(crate) fn to_local_network(self) -> LocalNetwork { + let h = |v: Option| v.map(BlockHeight::from_u32); + LocalNetwork { + overwinter: h(self.overwinter), + sapling: h(self.sapling), + blossom: h(self.blossom), + heartwood: h(self.heartwood), + canopy: h(self.canopy), + nu5: h(self.nu5), + nu6: h(self.nu6), + nu6_1: h(self.nu6_1), + nu6_2: h(self.nu6_2), + nu6_3: h(self.nu6_3), + #[cfg(zcash_unstable = "nu7")] + nu7: h(self.nu7), + } + } + + pub(crate) fn from_local_network(local: LocalNetwork) -> Self { + let h = |v: Option| v.map(u32::from); + Self { + overwinter: h(local.overwinter), + sapling: h(local.sapling), + blossom: h(local.blossom), + heartwood: h(local.heartwood), + canopy: h(local.canopy), + nu5: h(local.nu5), + nu6: h(local.nu6), + nu6_1: h(local.nu6_1), + nu6_2: h(local.nu6_2), + nu6_3: h(local.nu6_3), + #[cfg(zcash_unstable = "nu7")] + nu7: h(local.nu7), } } } -impl From for Network { - fn from(value: crate::network::Network) -> Self { - match value { - crate::network::Network::Consensus { - params: consensus::Network::MainNetwork, - .. - } => Network::Main, - crate::network::Network::Consensus { - params: consensus::Network::TestNetwork, - .. - } => Network::Test, - crate::network::Network::Regtest(_) => Network::Regtest, +/// Load a `--activation-heights` TOML file into a `LocalNetwork`. +#[cfg(feature = "regtest_support")] +pub(crate) fn load_activation_heights(path: &Path) -> anyhow::Result { + let contents = std::fs::read_to_string(path) + .map_err(|e| anyhow::anyhow!("reading activation-heights file {path:?}: {e}"))?; + let heights: ActivationHeights = toml::from_str(&contents) + .map_err(|e| anyhow::anyhow!("parsing activation-heights file {path:?}: {e}"))?; + Ok(heights.to_local_network()) +} + +impl Parameters for Network { + fn network_type(&self) -> consensus::NetworkType { + match self { + Network::Test => consensus::Network::TestNetwork.network_type(), + Network::Main => consensus::Network::MainNetwork.network_type(), + #[cfg(feature = "regtest_support")] + Network::Regtest(local) => local.network_type(), + } + } + + fn activation_height(&self, nu: consensus::NetworkUpgrade) -> Option { + match self { + Network::Test => consensus::Network::TestNetwork.activation_height(nu), + Network::Main => consensus::Network::MainNetwork.activation_height(nu), + #[cfg(feature = "regtest_support")] + Network::Regtest(local) => local.activation_height(nu), } } } @@ -125,3 +238,47 @@ pub(crate) fn init_dbs( Ok(db_data) } + +#[cfg(all(test, feature = "regtest_support"))] +mod tests { + use super::*; + use consensus::NetworkUpgrade; + + #[test] + fn activation_heights_toml_maps_to_local_network() { + // A missing key (here nu6_2) means the upgrade is inactive. + let toml = "\ +overwinter = 1 +sapling = 1 +blossom = 1 +heartwood = 1 +canopy = 1 +nu5 = 2 +nu6 = 2 +nu6_1 = 2 +"; + let heights: ActivationHeights = toml::from_str(toml).unwrap(); + let local = heights.to_local_network(); + let net = Network::Regtest(local); + + assert_eq!( + net.activation_height(NetworkUpgrade::Sapling), + Some(BlockHeight::from_u32(1)) + ); + assert_eq!( + net.activation_height(NetworkUpgrade::Nu5), + Some(BlockHeight::from_u32(2)) + ); + assert_eq!(net.activation_height(NetworkUpgrade::Nu6_2), None); + } + + #[test] + fn activation_heights_round_trip_through_local_network() { + let original: ActivationHeights = toml::from_str("nu5 = 7\nnu6 = 9\n").unwrap(); + let restored = ActivationHeights::from_local_network(original.to_local_network()); + // Restoring from the LocalNetwork preserves set and unset upgrades. + assert_eq!(restored.nu5, Some(7)); + assert_eq!(restored.nu6, Some(9)); + assert_eq!(restored.sapling, None); + } +} diff --git a/src/error.rs b/src/error.rs index 5a52a94..d486dbc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,7 @@ use zcash_client_sqlite::{ }; use zcash_keys::keys::DerivationError; use zcash_primitives::transaction::fees::zip317; +use zcash_protocol::value::BalanceError; use zip321::Zip321Error; pub(crate) type WalletErrorT = WalletError< @@ -20,10 +21,13 @@ pub(crate) type WalletErrorT = WalletError< ReceivedNoteId, >; +// `propose_send_max_transfer` returns `ProposeSendMaxErrT`, whose +// input-selection error slot is `BalanceError` (not `GreedyInputSelectorError` +// like the greedy `propose_transfer`/`propose_shielding` paths). pub(crate) type SendMaxErrorT = WalletError< SqliteClientError, commitment_tree::Error, - GreedyInputSelectorError, + BalanceError, zip317::FeeError, zip317::FeeError, ReceivedNoteId, @@ -38,19 +42,6 @@ pub(crate) type ShieldErrorT = WalletError< Infallible, >; -// Matches `zcash_client_backend::data_api::wallet::MigrateToIronwoodErrT` for -// `DbT = WalletDb<..>` and `FeeRuleT = StandardFeeRule` (whose `FeeRule::Error` -// is `zip317::FeeError`). The migration helper performs no input selection or -// change derivation, so both of those error slots are `Infallible`. -pub(crate) type MigrateErrorT = WalletError< - SqliteClientError, - commitment_tree::Error, - Infallible, - zip317::FeeError, - Infallible, - ReceivedNoteId, ->; - #[derive(Debug)] pub enum Error { Cache(FsBlockDbError), @@ -60,7 +51,6 @@ pub enum Error { InvalidMemo, InvalidKeysFile, InvalidTreeState, - Migrate(MigrateErrorT), SendFailed { code: i32, reason: String }, SendMax(SendMaxErrorT), Shield(ShieldErrorT), @@ -78,7 +68,6 @@ impl fmt::Display for Error { Error::InvalidMemo => write!(f, "Invalid memo"), Error::InvalidKeysFile => write!(f, "Invalid keys file"), Error::InvalidTreeState => write!(f, "Invalid TreeState received from server"), - Error::Migrate(e) => e.fmt(f), Error::SendFailed { code, reason } => write!(f, "Send failed: ({code}) {reason}"), Error::SendMax(e) => e.fmt(f), Error::Shield(e) => e.fmt(f), diff --git a/src/helpers/pczt/create_manual.rs b/src/helpers/pczt/create_manual.rs index 36e8eb1..b8f678a 100644 --- a/src/helpers/pczt/create_manual.rs +++ b/src/helpers/pczt/create_manual.rs @@ -98,7 +98,7 @@ pub(crate) fn handle_recipient( } pub(crate) fn add_inputs( - builder: &mut Builder<'_, P, U>, + builder: &mut Builder, transparent_inputs: Vec, ) -> anyhow::Result<()> { for input in transparent_inputs.into_iter() { @@ -108,7 +108,7 @@ pub(crate) fn add_inputs( - builder: &mut Builder<'_, P, U>, + builder: &mut Builder, recipient: Address, value: Zatoshis, memo: Option, diff --git a/src/main.rs b/src/main.rs index 6845b60..79eacb4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,6 @@ mod config; mod data; mod error; mod helpers; -mod network; mod remote; mod socks; mod ui; @@ -138,6 +137,9 @@ fn main() -> Result<(), anyhow::Error> { }) => match command { commands::wallet::Command::Init(command) => command.run(wallet_dir).await, commands::wallet::Command::InitFvk(command) => command.run(wallet_dir).await, + commands::wallet::Command::RestoreMnemonic(command) => { + command.run(wallet_dir).await + } commands::wallet::Command::DisplayMnemonic(command) => command.run(wallet_dir), commands::wallet::Command::Reset(command) => command.run(wallet_dir).await, commands::wallet::Command::ImportUfvk(command) => command.run(wallet_dir).await, @@ -152,6 +154,7 @@ fn main() -> Result<(), anyhow::Error> { ) .await } + commands::wallet::Command::GetInfo(command) => command.run(wallet_dir).await, commands::wallet::Command::Enhance(command) => command.run(wallet_dir).await, commands::wallet::Command::Balance(command) => command.run(wallet_dir).await, commands::wallet::Command::GenerateAccount(command) => { @@ -166,9 +169,6 @@ fn main() -> Result<(), anyhow::Error> { commands::wallet::Command::ListTx(command) => command.run(wallet_dir), commands::wallet::Command::ListUnspent(command) => command.run(wallet_dir), commands::wallet::Command::Shield(command) => command.run(wallet_dir).await, - commands::wallet::Command::MigrateToIronwood(command) => { - command.run(wallet_dir).await - } commands::wallet::Command::Propose(command) => command.run(wallet_dir).await, commands::wallet::Command::Pay(command) => command.run(wallet_dir).await, commands::wallet::Command::Send(command) => command.run(wallet_dir).await, @@ -205,7 +205,9 @@ fn main() -> Result<(), anyhow::Error> { commands::pczt::Command::Redact(command) => command.run().await, commands::pczt::Command::Prove(command) => command.run(wallet_dir).await, commands::pczt::Command::Sign(command) => command.run(wallet_dir).await, + commands::pczt::Command::UpdateWithSignature(command) => command.run().await, commands::pczt::Command::Combine(command) => command.run().await, + commands::pczt::Command::Extract(command) => command.run().await, commands::pczt::Command::Send(command) => command.run(wallet_dir).await, commands::pczt::Command::SendWithoutStoring(command) => { command.run(wallet_dir).await diff --git a/src/network.rs b/src/network.rs deleted file mode 100644 index 92775e8..0000000 --- a/src/network.rs +++ /dev/null @@ -1,165 +0,0 @@ -//! The network parameters the wallet operates against. -//! -//! [`zcash_protocol::consensus::Network`] models mainnet and testnet, but its -//! params return `None` for the NU6.3 (Ironwood) activation height on both -//! networks. To exercise Ironwood we need a network on which NU6.3 *is* active, -//! so this [`Network`] wrapper supplies the missing activation height in two -//! ways: -//! -//! - On the production networks ([`Network::testnet`] / [`Network::mainnet`]) it -//! overrides just the NU6.3 height (see [`TESTNET_NU6_3_ACTIVATION`] / -//! [`MAINNET_NU6_3_ACTIVATION`]) while delegating every other upgrade to the -//! upstream schedule. -//! - For local development it provides a full regtest schedule via -//! [`LocalNetwork`] (see [`regtest_params`]). -//! -//! Either way the rest of the tool stays generic over -//! [`consensus::Parameters`]. - -use zcash_protocol::{ - consensus::{self, BlockHeight, NetworkType, NetworkUpgrade, Parameters}, - local_consensus::LocalNetwork, -}; - -/// The regtest activation heights. -/// -/// NOTE: these heights are hardcoded specifically for the **zecd Ironwood -/// regtest harness**. They are not a general-purpose regtest schedule; they -/// exist so that this funder, zebra, and zecd all agree within that harness. -/// -/// They MUST stay identical to the activation schedule the other components in -/// that harness use — zebra's `configured_activation_heights` and zecd's -/// `regtest()` — or the consensus branch IDs (and therefore transaction -/// validity) diverge and transactions get rejected. The agreed harness schedule -/// is: -/// -/// - everything through NU5 and NU6: height 1 -/// - NU6.1 and NU6.2: height 4 -/// - NU6.3 (Ironwood): height 8 -/// -/// If the harness changes any of these (e.g. zebra's `nu6_3` height), update the -/// matching value below in lockstep. -/// -/// The point of activating NU6.3 is that [`Parameters::is_nu_active`] returns -/// `true` for it, which is what causes the high-level transaction-construction -/// path (`propose_transfer` + `create_proposed_transactions`, i.e. `wallet -/// send`) to emit Ironwood (V6) outputs to the recipient once the target height -/// is at or above the NU6.3 activation height. -pub(crate) const fn regtest_params() -> LocalNetwork { - // Heights fixed to match the zecd regtest harness (zebra + zecd + this tool). - let h1 = Some(BlockHeight::from_u32(1)); - let h4 = Some(BlockHeight::from_u32(4)); - let h8 = Some(BlockHeight::from_u32(8)); - LocalNetwork { - overwinter: h1, - sapling: h1, - blossom: h1, - heartwood: h1, - canopy: h1, - nu5: h1, - nu6: h1, - nu6_1: h4, - nu6_2: h4, - #[cfg(zcash_unstable = "nu6.3")] - nu6_3: h8, - } -} - -/// The [`NetworkUpgrade::Nu6_3`] (Ironwood) activation height on the production -/// networks. -/// -/// The librustzcash fork returns `None` for NU6.3 on both testnet and mainnet -/// (its `consensus::Network` params have not yet been updated with the Ironwood -/// schedule — see `zcash_protocol`'s `TestNetwork`/`MainNetwork`), so we supply -/// the heights here. This override is what makes [`Parameters::is_nu_active`] -/// return `true` for NU6.3 on those networks once the chain reaches the given -/// height, which in turn drives the high-level transaction-construction path -/// (`propose_transfer` + `create_proposed_transactions`) to emit Ironwood (V6) -/// outputs. -/// -/// Unlike the regtest schedule (see [`regtest_params`]), these are the *real* -/// production activation heights; update them in lockstep with the Ironwood -/// deployment schedule for each network. -/// -/// - Testnet: Ironwood is live on the NU6.3 testnet, where zebra -/// (6.0.0-rc.0, with configured activation heights) activated NU6.3 at height -/// 4_134_000. -/// - Mainnet: not yet scheduled (`None`); set this once NU6.3 is assigned a -/// mainnet activation height. -#[cfg(zcash_unstable = "nu6.3")] -const TESTNET_NU6_3_ACTIVATION: Option = Some(BlockHeight::from_u32(4_134_000)); -#[cfg(zcash_unstable = "nu6.3")] -const MAINNET_NU6_3_ACTIVATION: Option = None; - -/// The network the wallet operates against. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub(crate) enum Network { - /// A production network (mainnet or testnet). - Consensus { - /// The upstream consensus parameters for this network. - params: consensus::Network, - /// The NU6.3 (Ironwood) activation height for this network. - /// - /// Overrides the upstream `params`, which return `None` for NU6.3 on - /// both production networks. See [`TESTNET_NU6_3_ACTIVATION`] / - /// [`MAINNET_NU6_3_ACTIVATION`]. - #[cfg(zcash_unstable = "nu6.3")] - nu6_3: Option, - }, - /// A regtest / local-consensus network whose activation heights match the - /// zecd Ironwood regtest harness (see [`regtest_params`]). - Regtest(LocalNetwork), -} - -impl Network { - /// Returns the production testnet, with the NU6.3 (Ironwood) activation - /// height supplied by [`TESTNET_NU6_3_ACTIVATION`]. - pub(crate) const fn testnet() -> Self { - Network::Consensus { - params: consensus::Network::TestNetwork, - #[cfg(zcash_unstable = "nu6.3")] - nu6_3: TESTNET_NU6_3_ACTIVATION, - } - } - - /// Returns the production mainnet, with the NU6.3 (Ironwood) activation - /// height supplied by [`MAINNET_NU6_3_ACTIVATION`]. - pub(crate) const fn mainnet() -> Self { - Network::Consensus { - params: consensus::Network::MainNetwork, - #[cfg(zcash_unstable = "nu6.3")] - nu6_3: MAINNET_NU6_3_ACTIVATION, - } - } - - /// Returns the regtest network for the zecd Ironwood harness (see - /// [`regtest_params`] for the activation schedule). - pub(crate) const fn regtest() -> Self { - Network::Regtest(regtest_params()) - } -} - -impl Parameters for Network { - fn network_type(&self) -> NetworkType { - match self { - Network::Consensus { params, .. } => params.network_type(), - Network::Regtest(params) => params.network_type(), - } - } - - fn activation_height(&self, nu: NetworkUpgrade) -> Option { - match self { - // On the production networks, NU6.3 comes from our override (the - // upstream params return `None`); every other upgrade delegates to - // the upstream schedule. - #[cfg(zcash_unstable = "nu6.3")] - Network::Consensus { params, nu6_3 } => match nu { - NetworkUpgrade::Nu6_3 => *nu6_3, - _ => params.activation_height(nu), - }, - #[cfg(not(zcash_unstable = "nu6.3"))] - Network::Consensus { params } => params.activation_height(nu), - Network::Regtest(params) => params.activation_height(nu), - } - } -} diff --git a/src/remote.rs b/src/remote.rs index 3a4319b..52bca60 100644 --- a/src/remote.rs +++ b/src/remote.rs @@ -4,15 +4,14 @@ use anyhow::anyhow; use clap::Args; use tonic::transport::{Channel, ClientTlsConfig, Endpoint, Uri}; +use crate::{ + data::{Network, get_tor_dir}, + socks::SocksConnector, +}; use tracing::{info, warn}; use zcash_client_backend::{ proto::service::compact_tx_streamer_client::CompactTxStreamerClient, tor, }; -use zcash_protocol::consensus::{NetworkType, Parameters}; - -use crate::{data::get_tor_dir, network::Network, socks::SocksConnector}; - -const ECC_TESTNET: &[Server<'_>] = &[Server::fixed("lightwalletd.testnet.electriccoin.co", 9067)]; const YWALLET_MAINNET: &[Server<'_>] = &[ Server::fixed("lwd1.zcash-infra.com", 9067), @@ -36,22 +35,20 @@ const ZEC_ROCKS_TESTNET: &[Server<'_>] = &[Server::fixed("testnet.zec.rocks", 44 #[derive(Clone, Debug)] pub(crate) enum ServerOperator { - Ecc, YWallet, ZecRocks, } impl ServerOperator { - fn servers(&self, network: NetworkType) -> &[Server<'_>] { + fn servers(&self, network: Network) -> &[Server<'_>] { match (self, network) { - (ServerOperator::Ecc, NetworkType::Main) => &[], - (ServerOperator::Ecc, NetworkType::Test) => ECC_TESTNET, - (ServerOperator::YWallet, NetworkType::Main) => YWALLET_MAINNET, - (ServerOperator::YWallet, NetworkType::Test) => &[], - (ServerOperator::ZecRocks, NetworkType::Main) => ZEC_ROCKS_MAINNET, - (ServerOperator::ZecRocks, NetworkType::Test) => ZEC_ROCKS_TESTNET, - // There are no hosted servers for regtest; a custom `--server` is required. - (_, NetworkType::Regtest) => &[], + (ServerOperator::YWallet, Network::Main) => YWALLET_MAINNET, + (ServerOperator::YWallet, Network::Test) => &[], + (ServerOperator::ZecRocks, Network::Main) => ZEC_ROCKS_MAINNET, + (ServerOperator::ZecRocks, Network::Test) => ZEC_ROCKS_TESTNET, + // No operator hosts regtest; users must pass a custom host:port. + #[cfg(feature = "regtest_support")] + (_, Network::Regtest(_)) => &[], } } } @@ -65,7 +62,6 @@ pub(crate) enum Servers { impl Servers { pub(crate) fn parse(s: &str) -> anyhow::Result { match s { - "ecc" => Ok(Self::Hosted(ServerOperator::Ecc)), "ywallet" => Ok(Self::Hosted(ServerOperator::YWallet)), "zecrocks" => Ok(Self::Hosted(ServerOperator::ZecRocks)), _ => s @@ -80,7 +76,7 @@ impl Servers { }) .collect::>() .map(Self::Custom) - .ok_or(anyhow!("'{}' must be one of ['ecc', 'ywallet', 'zecrocks'], or a comma-separated list of host:port", s)), + .ok_or(anyhow!("'{}' must be one of ['ywallet', 'zecrocks'], or a comma-separated list of host:port", s)), } } @@ -88,7 +84,7 @@ impl Servers { // For now just use the first server in the list. match self { Servers::Hosted(server_operator) => server_operator - .servers(network.network_type()) + .servers(network) .first() .ok_or(anyhow!("{:?} doesn't serve {:?}", server_operator, network)), Servers::Custom(servers) => Ok(servers.first().expect("not empty")), @@ -131,6 +127,12 @@ impl Server<'_> { && !self.host.ends_with(".onion") } + /// The server's connection URI (scheme included), as reported by + /// `wallet get-info`. + pub(crate) fn uri(&self) -> String { + self.endpoint() + } + fn endpoint(&self) -> String { format!( "{}://{}:{}", @@ -256,8 +258,8 @@ fn parse_connection_mode(s: &str) -> Result { /// CLI arguments for server connection configuration. #[derive(Debug, Args, Clone)] pub(crate) struct ConnectionArgs { - /// The server to connect to (default is "ecc") - #[arg(short, long, default_value = "ecc", value_parser = Servers::parse)] + /// The server to connect to (default is "zecrocks") + #[arg(short, long, default_value = "zecrocks", value_parser = Servers::parse)] pub(crate) server: Servers, /// Connection mode: "direct", "tor" (default), or "socks5://:" diff --git a/src/ui/proposal.rs b/src/ui/proposal.rs index bb01aa9..9898d8f 100644 --- a/src/ui/proposal.rs +++ b/src/ui/proposal.rs @@ -77,10 +77,7 @@ fn print_payments(request: &TransactionRequest, payment_pools: &BTreeMap( - inputs: &[WalletTransparentOutput], - params: &P, -) { +fn print_transparent_inputs(inputs: &[WalletTransparentOutput], params: &P) { if inputs.is_empty() { return; }