diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 353f23ae4..b2430047c 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -13,11 +13,7 @@ jobs: name: Performance regression check runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.65.0 - override: true + - uses: actions/checkout@v4 - name: Run benchmark run: cargo bench -- --output-format bencher | tee output.txt - name: Store benchmark result diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml index 666080296..f5fd6cc82 100644 --- a/.github/workflows/book.yml +++ b/.github/workflows/book.yml @@ -7,20 +7,18 @@ on: jobs: deploy: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - - name: Setup mdBook - uses: peaceiris/actions-mdbook@v1 + - uses: actions/checkout@v4 with: - mdbook-version: '0.4.5' + fetch-depth: 0 - - name: Install mdbook-katex - uses: actions-rs/cargo@v1 - with: - command: install - args: mdbook-katex + - name: Install mdbook and mdbook-katex + run: | + mkdir mdbook + curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.35/mdbook-v0.4.35-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook + curl -sSL https://github.com/lzanini/mdbook-katex/releases/download/v0.5.8pub/mdbook-katex-v0.5.8-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook + echo `pwd`/mdbook >> $GITHUB_PATH - name: Build Orchard book run: mdbook build book/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be39382b4..2e6a6c3ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,18 +11,31 @@ jobs: os: [ubuntu-latest, windows-latest, macOS-latest] steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.65.0 - override: true + - uses: actions/checkout@v4 - name: Run tests - uses: actions-rs/cargo@v1 - with: - command: test - args: --verbose + run: cargo test --verbose + - name: Verify working directory is clean + run: git diff --exit-code - build: + build-latest: + name: Latest build on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macOS-latest] + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + id: toolchain + - run: rustup override set ${{steps.toolchain.outputs.name}} + - name: Remove lockfile to build with latest dependencies + run: rm Cargo.lock + - name: Build crate + run: cargo build --all-features --verbose + - name: Verify working directory is clean (excluding lockfile) + run: git diff --exit-code ':!Cargo.lock' + + build-nodefault: name: Build target ${{ matrix.target }} runs-on: ubuntu-latest strategy: @@ -31,7 +44,7 @@ jobs: - wasm32-wasi steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Add target run: rustup target add ${{ matrix.target }} - run: cargo fetch @@ -41,98 +54,53 @@ jobs: bitrot: name: Bitrot check runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.65.0 - override: true + - uses: actions/checkout@v4 # Build benchmarks to prevent bitrot - name: Build benchmarks - uses: actions-rs/cargo@v1 - with: - command: build - args: --benches + run: cargo build --benches book: name: Book tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.65.0 - override: true - - name: Setup mdBook - uses: peaceiris/actions-mdbook@v1 - with: - mdbook-version: '0.4.5' + - uses: actions/checkout@v4 + - name: Install mdbook + run: | + mkdir mdbook + curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.35/mdbook-v0.4.35-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook + echo `pwd`/mdbook >> $GITHUB_PATH - name: Test Orchard book run: mdbook test book/ -# disabled due to performance issues: -# codecov: -# name: Code coverage -# runs-on: ubuntu-latest -# -# steps: -# - uses: actions/checkout@v2 -# # Use stable for this to ensure that cargo-tarpaulin can be built. -# - uses: actions-rs/toolchain@v1 -# with: -# toolchain: stable -# override: true -# - name: Install cargo-tarpaulin -# uses: actions-rs/cargo@v1 -# with: -# command: install -# args: cargo-tarpaulin -# - name: Generate coverage report -# uses: actions-rs/cargo@v1 -# with: -# command: tarpaulin -# args: --all-features --timeout 1200 --out Xml -# - name: Upload coverage to Codecov -# uses: codecov/codecov-action@v1 -# with: -# token: ${{secrets.CODECOV_TOKEN}} + codecov: + name: Code coverage + runs-on: ubuntu-latest + container: + image: xd009642/tarpaulin:develop-nightly + options: --security-opt seccomp=unconfined + + steps: + - uses: actions/checkout@v4 + - name: Generate coverage report + run: cargo tarpaulin --engine llvm --all-features --release --timeout 600 --out xml + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3.1.4 doc-links: name: Intra-doc links runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.65.0 - override: true - - name: cargo fetch - uses: actions-rs/cargo@v1 - with: - command: fetch - - # Ensure intra-documentation links all resolve correctly - # Requires #![deny(intra_doc_link_resolution_failure)] in crates. + - uses: actions/checkout@v4 + - run: cargo fetch + # Requires #![deny(rustdoc::broken_intra_doc_links)] in crates. - name: Check intra-doc links - uses: actions-rs/cargo@v1 - with: - command: doc - args: --document-private-items + run: cargo doc --all-features --document-private-items fmt: name: Rustfmt timeout-minutes: 30 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.65.0 - override: true - - run: rustup component add rustfmt - - uses: actions-rs/cargo@v1 - with: - command: fmt - args: -- --check + - uses: actions/checkout@v4 + - run: cargo fmt -- --check diff --git a/.github/workflows/lints-beta.yml b/.github/workflows/lints-beta.yml index cb8bc66a9..fbc46d143 100644 --- a/.github/workflows/lints-beta.yml +++ b/.github/workflows/lints-beta.yml @@ -11,17 +11,16 @@ jobs: runs-on: ubuntu-latest continue-on-error: true steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@beta + id: toolchain with: - toolchain: beta components: clippy - override: true - - name: Run Clippy (beta) - uses: actions-rs/clippy-check@v1 + - run: rustup override set ${{steps.toolchain.outputs.name}} + - name: Run Clippy + uses: auguwu/clippy-action@1.3.0 continue-on-error: true with: - name: Clippy (beta) token: ${{ secrets.GITHUB_TOKEN }} - args: --all-features --all-targets - + working-directory: ${{ inputs.target }} + warn: clippy::all diff --git a/.github/workflows/lints-stable.yml b/.github/workflows/lints-stable.yml index 36ae77e06..328c4fc9d 100644 --- a/.github/workflows/lints-stable.yml +++ b/.github/workflows/lints-stable.yml @@ -5,19 +5,14 @@ on: pull_request jobs: clippy: - name: Clippy (1.65.0) + name: Clippy (MSRV) timeout-minutes: 30 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.65.0 - components: clippy - override: true + - uses: actions/checkout@v4 - name: Run Clippy - uses: actions-rs/clippy-check@v1 + uses: auguwu/clippy-action@1.3.0 with: - name: Clippy (1.65.0) token: ${{ secrets.GITHUB_TOKEN }} - args: --all-features --all-targets -- -D warnings + working-directory: ${{ inputs.target }} + deny: warnings diff --git a/.gitignore b/.gitignore index f4ca20140..874a5ed83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ /target **/*.rs.bk -Cargo.lock .vscode .idea action-circuit-layout.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 05ac44298..edf7a5608 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,84 @@ and this project adheres to Rust's notion of ## [Unreleased] +## [0.8.0] - 2024-03-25 + +### Added +- `orchard::note::Rho` +- `orchard::action::Action::rho` +- `orchard::note_encryption::CompactAction::rho` +- `orchard::note_encryption::OrchardDomain::for_compact_action` +- Additions under the `test-dependencies` feature flag: + - `orchard::tree::MerkleHashOrchard::random` + - `impl Distribution for Standard` + +### Changed +- The following methods have their `Nullifier`-typed argument or return value + now take or return `note::Rho` instead: + - `orchard::note::RandomSeed::from_bytes` + - `orchard::note::Note::from_parts` + - `orchard::note::Note::rho` + +### Removed +- `orchard::note_encryption::OrchardDomain::for_nullifier` (use `for_action` + or `for_compact_action` instead). + +## [0.7.1] - 2024-02-29 +### Added +- `impl subtle::ConstantTimeEq for orchard::note::Nullifier` +- `orchard::note_encryption`: + - `CompactAction::cmx` + - `impl Clone for CompactAction` + +## [0.7.0] - 2024-01-26 +### Licensing +- The license for this crate is now "MIT OR Apache-2.0". The license + exception that applied to the Zcash and Zebra projects, other projects + designed to integrate with Zcash, and certain forks of Zcash, is no longer + necessary. For clarity, this is intended to be a strict relaxation of the + previous licensing, i.e. it permits all usage that was previously possible + with or without use of the license exception. + +### Added +- `orchard::builder`: + - `bundle` + - `BundleMetadata` + - `BundleType` + - `OutputInfo` +- `orchard::bundle::Flags::{ENABLED, SPENDS_DISABLED, OUTPUTS_DISABLED}` +- `orchard::tree::Anchor::empty_tree` + +### Changed +- Migrated to the `zip32` crate. The following types have been replaced by the + equivalent ones in that crate are now re-exported from there: + - `orchard::keys::{DiversifierIndex, Scope}` + - `orchard::zip32::ChildIndex` +- `orchard::builder`: + - `Builder::new` now takes the bundle type to be used in bundle construction, + instead of taking the flags and anchor separately. + - `Builder::add_recipient` has been renamed to `add_output` in order to + clarify than more than one output of a given transaction may be sent to the + same recipient. + - `Builder::build` now takes an additional `BundleType` argument that + specifies how actions should be padded, instead of using hardcoded padding. + It also now returns a `Result, BundleMetadata)>, ...>` + instead of a `Result, ...>`. + - `BuildError` has additional variants: + - `SpendsDisabled` + - `OutputsDisabled` + - `AnchorMismatch` + - `SpendInfo::new` now returns a `Result` instead of an + `Option`. +- `orchard::keys::SpendingKey::from_zip32_seed` now takes a `zip32::AccountId`. + +### Removed +- `orchard::bundle::Flags::from_parts` + +## [0.6.0] - 2023-09-08 +### Changed +- MSRV is now 1.65.0. +- Migrated to `incrementalmerkletree 0.5`. + ## [0.5.0] - 2023-06-06 ### Changed - Migrated to `zcash_note_encryption 0.4`, `incrementalmerkletree 0.4`, `bridgetree 0.3`. @@ -17,8 +95,17 @@ and this project adheres to Rust's notion of - `orchard::builder`: - `{SpendInfo::new, InputView, OutputView}` - `Builder::{spends, outputs}` - - `SpendError` - - `OutputError` + - `SpendError` + - `OutputError` +- `orchard::keys`: + - `PreparedEphemeralPublicKey` + - `PreparedIncomingViewingKey` +- impls of `memuse::DynamicUsage` for: + - `orchard::note::Nullifier` + - `orchard::note_encryption::OrchardDomain` +- impls of `Eq` for: + - `orchard::zip32::ChildIndex` + - `orchard::value::ValueSum` ### Changed - MSRV is now 1.60.0. @@ -39,20 +126,11 @@ and this project adheres to Rust's notion of ### Added - `orchard::Proof::add_to_batch` - `orchard::address::Address::diversifier` -- `orchard::keys:`: - - `Diversifier::from_bytes` - - `PreparedEphemeralPublicKey` - - `PreparedIncomingViewingKey` +- `orchard::keys::Diversifier::from_bytes` - `orchard::note`: - `RandomSeed` - `Note::{from_parts, rseed}` - - `impl memuse::DynamicUsage for Nullifier` -- `orchard::note_encryption`: - - `impl memuse::DynamicUsage for OrchardDomain` - `orchard::circuit::Circuit::from_action_context` -- impls of `Eq` for: - - `orchard::zip32::ChildIndex` - - `orchard::value::ValueSum` ### Changed - Migrated to `zcash_note_encryption 0.2`. diff --git a/COPYING b/COPYING deleted file mode 100644 index 35d86449a..000000000 --- a/COPYING +++ /dev/null @@ -1,40 +0,0 @@ -Copyright 2020-2022 The Electric Coin Company - -This package ("Original Work") is licensed under the terms of the Bootstrap Open -Source License, version 1.0, or at your option, any later version ("BOSL"). See -the file ./LICENSE-BOSL for the terms of the Bootstrap Open Source Licence, -version 1.0. - -Only if this Original Work is included as part of the distribution of one of the -following (each, the "Project"): - -- The Zcash projects published by the Electric Coin Company; -- The Zebra project published by the Zcash Foundation; -- A project that is designed to integrate with Zcash and provides additional - functionality or utility to the Zcash network and holders of the ZEC coin; or -- A blockchain that descends from the Zcash blockchain and that is forked - within 100 blocks of the current block height of the Zcash blockchain at the - time of the code fork; - -then License is granted to use the Original Work under the BOSL as modified by -the following clarification and special exception. This exception applies only -to the Original Work when linked or combined with the Project and not to the -Original Work when linked, combined, or included in or with any other software -or project or on a standalone basis. - - Under the terms of the BOSL, linking or combining this Original Work with - the Project creates a Derivative Work based upon the Original Work and the - terms of the BOSL thus apply to both the Original Work and that Derivative - Work. As a special exception to the BOSL, and to allow this Original Work to - be linked and combined with the Project without having to apply the BOSL to - the other portions of the Project, you are granted permission to link or - combine this Original Work with the Project and to copy and distribute the - resulting work ("Resulting Work") under the open source license applicable - to the Project ("Project License"), provided that any portions of this - Original Work included in the Resulting Work remain subject to the BOSL. For - clarity, you may continue to treat all other portions of the Project under - the Project License, provided that you comply with the BOSL with respect to - the Original Work. If you modify this Original Work, your version of the - Original Work must remain under the BOSL. You may also extend this exception - to your version, but you are not obligated to do so. If you do not wish to - do so, delete this exception statement from your version. diff --git a/COPYING.md b/COPYING.md new file mode 100644 index 000000000..5afe29ddf --- /dev/null +++ b/COPYING.md @@ -0,0 +1,25 @@ +# License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +# Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. + +# Note on license change + +The licensing of this crate changed from the Bootstrap Open Source License +to the above "MIT OR Apache-2.0" dual license in release 0.7.0. The license +exception that applied to the Zcash and Zebra projects, other projects +designed to integrate with Zcash, and certain forks of Zcash, is no longer +necessary. For clarity, this is intended to be a strict relaxation of the +previous licensing, i.e. it permits all usage that was previously possible +with or without use of the license exception. diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..e2e8971ed --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2687 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anyhow" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2b_simd" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bls12_381" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc6d6292be3a19e6379786dac800f551e5865a5bb51ebbe3064ab80433f403" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "bridgetree" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbfcb6c5a091e80cb3d3b0c1a7f126af4631cd5065b1f9929b139f1be8f3fb62" +dependencies = [ + "incrementalmerkletree", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytemuck" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cc" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.5", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "bitflags 1.3.2", + "clap_lex", + "indexmap 1.9.3", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "const-cstr" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "constant_time_eq" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "core-text" +version = "19.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + +[[package]] +name = "cpp_demangle" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "criterion" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" +dependencies = [ + "anes", + "atty", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.3", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derive_builder" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0" +dependencies = [ + "darling", + "derive_builder_core", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "dwrote" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "bitvec", + "rand_core", + "subtle", +] + +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-ord" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e" + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin 0.9.8", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "font-kit" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21fe28504d371085fae9ac7a3450f0b289ab71e07c8e57baa3fb68b9e57d6ce5" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "core-foundation", + "core-graphics", + "core-text", + "dirs-next", + "dwrote", + "float-ord", + "freetype", + "lazy_static", + "libc", + "log", + "pathfinder_geometry", + "pathfinder_simd", + "walkdir", + "winapi", + "yeslogic-fontconfig-sys", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "fpe" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c4b37de5ae15812a764c958297cfc50f5c010438f60c6ce75d11b802abd404" +dependencies = [ + "cbc", + "cipher", + "libm", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "freetype" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efc8599a3078adf8edeb86c71e9f8fa7d88af5ca31e806a867756081f90f5d83" +dependencies = [ + "freetype-sys", + "libc", +] + +[[package]] +name = "freetype-sys" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66ee28c39a43d89fbed8b4798fb4ba56722cfd2b5af81f9326c27614ba88ecd5" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "memuse", + "rand_core", + "subtle", +] + +[[package]] +name = "half" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +dependencies = [ + "crunchy", +] + +[[package]] +name = "halo2_gadgets" +version = "0.3.0" +source = "git+https://github.com/QED-it/halo2?branch=zsa1#5f436dc3387665fe3201d381791a62a8233b2171" +dependencies = [ + "arrayvec", + "bitvec", + "ff", + "group", + "halo2_proofs", + "lazy_static", + "pasta_curves", + "proptest", + "rand", + "subtle", + "uint", +] + +[[package]] +name = "halo2_legacy_pdqsort" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47716fe1ae67969c5e0b2ef826f32db8c3be72be325e1aa3c1951d06b5575ec5" + +[[package]] +name = "halo2_proofs" +version = "0.3.0" +source = "git+https://github.com/QED-it/halo2?branch=zsa1#5f436dc3387665fe3201d381791a62a8233b2171" +dependencies = [ + "blake2b_simd", + "ff", + "group", + "halo2_legacy_pdqsort", + "maybe-rayon", + "pasta_curves", + "plotters", + "rand_core", + "tabbycat", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif 0.13.1", + "jpeg-decoder", + "num-traits", + "png", + "qoi", + "tiff", +] + +[[package]] +name = "incrementalmerkletree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1872810fb725b06b8c153dde9e86f3ec26747b9b60096da7a869883b549cbe" +dependencies = [ + "either", + "proptest", + "rand", + "rand_core", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "inferno" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "321f0f839cd44a4686e9504b0a62b4d69a50b62072144c71c68f5873c167b8d9" +dependencies = [ + "ahash", + "crossbeam-channel", + "crossbeam-utils", + "dashmap", + "indexmap 2.2.6", + "is-terminal", + "itoa", + "log", + "num-format", + "once_cell", + "quick-xml", + "rgb", + "str_stack", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +dependencies = [ + "rayon", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jubjub" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8499f7a74008aafbecb2a2e608a3e13e4dd3e84df198b604451efe93f2de6e61" +dependencies = [ + "bitvec", + "bls12_381", + "ff", + "group", + "rand_core", + "subtle", +] + +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets 0.52.5", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memuse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2145869435ace5ea6ea3d35f59be559317ec9a0d04e1812d5f185a87b6d36f1a" +dependencies = [ + "nonempty", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + +[[package]] +name = "nonempty" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "itoa", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "orchard" +version = "0.8.0" +dependencies = [ + "aes", + "bitvec", + "blake2b_simd", + "bridgetree", + "criterion", + "ff", + "fpe", + "group", + "halo2_gadgets", + "halo2_proofs", + "hex", + "image", + "incrementalmerkletree", + "inferno", + "k256", + "lazy_static", + "memuse", + "nonempty", + "pasta_curves", + "plotters", + "pprof", + "proptest", + "rand", + "reddsa", + "serde", + "subtle", + "tracing", + "zcash_note_encryption", + "zcash_spec", + "zip32", +] + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "pasta_curves" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095" +dependencies = [ + "blake2b_simd", + "ff", + "group", + "lazy_static", + "rand", + "static_assertions", + "subtle", +] + +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebf45976c56919841273f2a0fc684c28437e2f304e264557d9c72be5d5a718be" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "chrono", + "font-kit", + "image", + "lazy_static", + "num-traits", + "pathfinder_geometry", + "plotters-backend", + "plotters-bitmap", + "plotters-svg", + "ttf-parser", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-bitmap" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cebbe1f70205299abc69e8b295035bb52a6a70ee35474ad10011f0a4efb8543" +dependencies = [ + "gif 0.12.0", + "image", + "plotters-backend", +] + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "pprof" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196ded5d4be535690899a4631cc9f18cdc41b7ebf24a79400f46f48e49a11059" +dependencies = [ + "backtrace", + "cfg-if", + "criterion", + "findshlibs", + "inferno", + "libc", + "log", + "nix", + "once_cell", + "parking_lot", + "smallvec", + "symbolic-demangle", + "tempfile", + "thiserror", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags 2.5.0", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-xml" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "reddsa" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78a5191930e84973293aa5f532b513404460cd2216c1cfb76d08748c15b40b02" +dependencies = [ + "blake2b_simd", + "byteorder", + "group", + "hex", + "jubjub", + "pasta_curves", + "rand_core", + "serde", + "thiserror", + "zeroize", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rgb" +version = "0.8.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" + +[[package]] +name = "serde" +version = "1.0.198" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.198" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "serde_json" +version = "1.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "str_stack" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" + +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "symbolic-common" +version = "10.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b55cdc318ede251d0957f07afe5fed912119b8c1bc5a7804151826db999e737" +dependencies = [ + "debugid", + "memmap2", + "stable_deref_trait", + "uuid", +] + +[[package]] +name = "symbolic-demangle" +version = "10.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79be897be8a483a81fff6a3a4e195b4ac838ef73ca42d348b3f722da9902e489" +dependencies = [ + "cpp_demangle", + "rustc-demangle", + "symbolic-common", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tabbycat" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c45590f0f859197b4545be1b17b2bc3cc7bb075f7d1cc0ea1dc6521c0bf256a3" +dependencies = [ + "anyhow", + "derive_builder", + "regex", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" + +[[package]] +name = "thiserror" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "ttf-parser" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.60", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yeslogic-fontconfig-sys" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2bbd69036d397ebbff671b1b8e4d918610c181c5a16073b96f984a38d08c386" +dependencies = [ + "const-cstr", + "dlib", + "once_cell", + "pkg-config", +] + +[[package]] +name = "zcash_note_encryption" +version = "0.4.0" +source = "git+https://github.com/QED-it/zcash_note_encryption?branch=zsa1#8b6b31dcea4a883a606425c4b644fca213e78b3b" +dependencies = [ + "chacha20", + "chacha20poly1305", + "cipher", + "rand_core", + "subtle", +] + +[[package]] +name = "zcash_spec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3bf58b673cb3dacd8ae09ba345998923a197ab0da70d6239d8e8838949e9b" +dependencies = [ + "blake2b_simd", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "zip32" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4226d0aee9c9407c27064dfeec9d7b281c917de3374e1e5a2e2cfad9e09de19e" +dependencies = [ + "blake2b_simd", + "memuse", + "subtle", +] + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml index 74dae4945..378fb872d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,17 @@ [package] name = "orchard" -version = "0.5.0" +version = "0.8.0" authors = [ "Sean Bowe ", "Jack Grigg ", - "Daira Hopwood ", - "Ying Tong Lai ", + "Daira-Emma Hopwood ", + "Ying Tong Lai", "Kris Nuttycombe ", ] edition = "2021" rust-version = "1.65" description = "The Orchard shielded transaction protocol" -license-file = "LICENSE-BOSL" +license = "MIT OR Apache-2.0" repository = "https://github.com/zcash/orchard" documentation = "https://docs.rs/orchard" readme = "README.md" @@ -44,6 +44,8 @@ serde = { version = "1.0", features = ["derive"] } subtle = "2.3" zcash_note_encryption_zsa = { package = "zcash_note_encryption", version = "0.4", git = "https://github.com/QED-it/zcash_note_encryption", branch = "zsa1" } incrementalmerkletree = "0.5" +zcash_spec = "0.1" +zip32 = "0.1" # Logging tracing = "0.1" @@ -60,12 +62,12 @@ hex = "0.4" proptest = "1.0.0" zcash_note_encryption_zsa = { package = "zcash_note_encryption", version = "0.4", git = "https://github.com/QED-it/zcash_note_encryption", branch = "zsa1", features = ["pre-zip-212"] } incrementalmerkletree = { version = "0.5", features = ["test-dependencies"] } -ahash = "=0.8.6" #Pinned: 0.8.7 depends on Rust 1.72 -half = "=2.2.1" #Pinned: 2.3.1 requires Rust 1.70 +#ahash = "=0.8.6" #Pinned: 0.8.7 depends on Rust 1.72 +#half = "=2.2.1" #Pinned: 2.3.1 requires Rust 1.70 [target.'cfg(unix)'.dev-dependencies] -inferno = "0.11" #Pinned -clap = "=4.2.0" #Pinned: Used by inferno. Later version requires Rust 1.70 +inferno = { version = "0.11", default-features = false, features = ["multithreaded", "nameattr"] } +#clap = "=4.2.0" #Pinned: Used by inferno. Later version requires Rust 1.70 pprof = { version = "0.11", features = ["criterion", "flamegraph"] } [lib] diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 000000000..1e5006dc1 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/LICENSE-BOSL b/LICENSE-BOSL deleted file mode 100644 index 7abc6ba26..000000000 --- a/LICENSE-BOSL +++ /dev/null @@ -1,176 +0,0 @@ -======================================================= -Bootstrap Open Source Licence ("BOSL") v. 1.0 -======================================================= -This Bootstrap Open Source Licence (the "License") applies to any original work -of authorship (the "Original Work") whose owner (the "Licensor") has placed the -following licensing notice adjacent to the copyright notice for the Original -Work: - -*Licensed under the Bootstrap Open Source Licence version 1.0* - -1. **Grant of Copyright License.** Licensor grants You a worldwide, - royalty-free, non-exclusive, sublicensable license, for the duration of the - copyright in the Original Work, to do the following: - - a. to reproduce the Original Work in copies, either alone or as part of - a collective work; - - b. to translate, adapt, alter, transform, modify, or arrange the - Original Work, thereby creating derivative works ("Derivative Works") - based upon the Original Work; - - c. to distribute or communicate copies of the Original Work and - Derivative Works to the public, provided that prior to any such - distribution or communication You first place a machine-readable copy - of the Source Code of the Original Work and such Derivative Works that - You intend to distribute or communicate in an information repository - reasonably calculated to permit inexpensive and convenient access - thereto by the public (“Information Repository”) for as long as You - continue to distribute or communicate said copies, accompanied by an - irrevocable offer to license said copies to the public free of charge - under this License, said offer valid starting no later than 12 months - after You first distribute or communicate said copies; - - d. to perform the Original Work publicly; and - - e. to display the Original Work publicly. - -2. **Grant of Patent License.** Licensor grants You a worldwide, royalty-free, -non-exclusive, sublicensable license, under patent claims owned or controlled -by the Licensor that are embodied in the Original Work as furnished by the -Licensor, for the duration of the patents, to make, use, sell, offer for sale, -have made, and import the Original Work and Derivative Works. - -3. **Grant of Source Code License.** The "Source Code" for a work means the -preferred form of the work for making modifications to it and all available -documentation describing how to modify the work. Licensor agrees to provide a -machine-readable copy of the Source Code of the Original Work along with each -copy of the Original Work that Licensor distributes. Licensor reserves the -right to satisfy this obligation by placing a machine-readable copy of said -Source Code in an Information Repository for as long as Licensor continues to -distribute the Original Work. - -4. **Exclusions From License Grant.** Neither the names of Licensor, nor the -names of any contributors to the Original Work, nor any of their trademarks or -service marks, may be used to endorse or promote products derived from this -Original Work without express prior permission of the Licensor. Except as -expressly stated herein, nothing in this License grants any license to -Licensor's trademarks, copyrights, patents, trade secrets or any other -intellectual property. No patent license is granted to make, use, sell, offer -for sale, have made, or import embodiments of any patent claims other than the -licensed claims defined in Section 2. No license is granted to the trademarks -of Licensor even if such marks are included in the Original Work. Nothing in -this License shall be interpreted to prohibit Licensor from licensing under -terms different from this License any Original Work that Licensor otherwise -would have a right to license. - -5. **External Deployment.** The term "External Deployment" means the use, -distribution, or communication of the Original Work or Derivative Works in any -way such that the Original Work or Derivative Works may be used by anyone other -than You, whether those works are distributed or communicated to those persons -or made available as an application intended for use over a network. As an -express condition for the grants of license hereunder, You must treat any -External Deployment by You of the Original Work or a Derivative Work as a -distribution under section 1(c). - -6. **Attribution Rights.** You must retain, in the Source Code of any -Derivative Works that You create, all copyright, patent, or trademark notices -from the Source Code of the Original Work, as well as any notices of licensing -and any descriptive text identified therein as an "Attribution Notice." You -must cause the Source Code for any Derivative Works that You create to carry a -prominent Attribution Notice reasonably calculated to inform recipients that -You have modified the Original Work. - -7. **Warranty of Provenance and Disclaimer of Warranty.** Licensor warrants -that the copyright in and to the Original Work and the patent rights granted -herein by Licensor are owned by the Licensor or are sublicensed to You under -the terms of this License with the permission of the contributor(s) of those -copyrights and patent rights. Except as expressly stated in the immediately -preceding sentence, the Original Work is provided under this License on an "AS -IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without -limitation, the warranties of non-infringement, merchantability or fitness for -a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS -WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this -License. No license to the Original Work is granted by this License except -under this disclaimer. - -8. **Limitation of Liability.** Under no circumstances and under no legal -theory, whether in tort (including negligence), contract, or otherwise, shall -the Licensor be liable to anyone for any indirect, special, incidental, or -consequential damages of any character arising as a result of this License or -the use of the Original Work including, without limitation, damages for loss of -goodwill, work stoppage, computer failure or malfunction, or any and all other -commercial damages or losses. This limitation of liability shall not apply to -the extent applicable law prohibits such limitation. - -9. **Acceptance and Termination.** If, at any time, You expressly assented to -this License, that assent indicates your clear and irrevocable acceptance of -this License and all of its terms and conditions. If You distribute or -communicate copies of the Original Work or a Derivative Work, You must make a -reasonable effort under the circumstances to obtain the express assent of -recipients to the terms of this License. This License conditions your rights to -undertake the activities listed in Section 1, including your right to create -Derivative Works based upon the Original Work, and doing so without honoring -these terms and conditions is prohibited by copyright law and international -treaty. Nothing in this License is intended to affect copyright exceptions and -limitations (including 'fair use' or 'fair dealing'). This License shall -terminate immediately and You may no longer exercise any of the rights granted -to You by this License upon your failure to honor the conditions in Section -1(c). - -10. **Termination for Patent Action.** This License shall terminate -automatically and You may no longer exercise any of the rights granted to You -by this License as of the date You commence an action, including a cross-claim -or counterclaim, against Licensor or any licensee alleging that the Original -Work infringes a patent. This termination provision shall not apply for an -action alleging patent infringement by combinations of the Original Work with -other software or hardware. - -11. **Jurisdiction, Venue and Governing Law.** Any action or suit relating to -this License may be brought only in the courts of a jurisdiction wherein the -Licensor resides or in which Licensor conducts its primary business, and under -the laws of that jurisdiction excluding its conflict-of-law provisions. The -application of the United Nations Convention on Contracts for the International -Sale of Goods is expressly excluded. Any use of the Original Work outside the -scope of this License or after its termination shall be subject to the -requirements and penalties of copyright or patent law in the appropriate -jurisdiction. This section shall survive the termination of this License. - -12. **Attorneys' Fees.** In any action to enforce the terms of this License or -seeking damages relating thereto, the prevailing party shall be entitled to -recover its costs and expenses, including, without limitation, reasonable -attorneys' fees and costs incurred in connection with such action, including -any appeal of such action. This section shall survive the termination of this -License. - -13. **Miscellaneous.** If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent necessary to -make it enforceable. - -14. **Definition of "You" in This License.** "You" throughout this License, -whether in upper or lower case, means an individual or a legal entity -exercising rights under, and complying with all of the terms of, this License. -For legal entities, "You" includes any entity that controls, is controlled by, -or is under common control with you. For purposes of this definition, "control" -means (i) the power, direct or indirect, to cause the direction or management -of such entity, whether by contract or otherwise, or (ii) ownership of fifty -percent (50%) or more of the outstanding shares, or (iii) beneficial ownership -of such entity. - -15. **Right to Use.** You may use the Original Work in all ways not otherwise -restricted or conditioned by this License or by law, and Licensor promises not -to interfere with or be responsible for such uses by You. - -16. **Modification of This License.** This License is Copyright © 2021-2022 Electric Coin Company. -Permission is granted to copy, distribute, or communicate this -License without modification. Nothing in this License permits You to modify -this License as applied to the Original Work or to Derivative Works. However, -You may modify the text of this License and copy, distribute or communicate -your modified version (the "Modified License") and apply it to other original -works of authorship subject to the following conditions: (i) You may not -indicate in any way that your Modified License is the "Bootstrap Open Source -Licence" or "BOSL" and you may not use those names in the name of your Modified -License; and (ii) You must replace the notice specified in the first paragraph -above with the notice "Licensed under " or with -a notice of your own that is not confusingly similar to the notice in this -License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 000000000..3a617be2b --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020-2023 The Electric Coin Company + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index 95c3782e6..8e610ad66 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,18 @@ Requires Rust 1.65+. ## License -Copyright 2020-2022 The Electric Coin Company. +Copyright 2020-2023 The Electric Coin Company. -You may use this package under the Bootstrap Open Source Licence, version 1.0, -or at your option, any later version. See the file [`COPYING`](COPYING) for -more details, and [`LICENSE-BOSL`](LICENSE-BOSL) for the terms of the Bootstrap -Open Source Licence, version 1.0. +All code in this workspace is licensed under either of -The purpose of the BOSL is to allow commercial improvements to the package -while ensuring that all improvements are open source. See -[here](https://electriccoin.co/blog/introducing-tgppl-a-radically-new-type-of-open-source-license/) -for why the BOSL exists. + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual licensed as above, without any additional terms or +conditions. diff --git a/benches/circuit.rs b/benches/circuit.rs index f26cc3507..080411607 100644 --- a/benches/circuit.rs +++ b/benches/circuit.rs @@ -8,8 +8,7 @@ use pprof::criterion::{Output, PProfProfiler}; use orchard::note::AssetBase; use orchard::{ - builder::Builder, - bundle::Flags, + builder::{Builder, BundleType}, circuit::{ProvingKey, VerifyingKey}, keys::{FullViewingKey, Scope, SpendingKey}, value::NoteValue, @@ -28,12 +27,12 @@ fn criterion_benchmark(c: &mut Criterion) { let create_bundle = |num_recipients| { let mut builder = Builder::new( - Flags::from_parts(true, true, false), + BundleType::DEFAULT_VANILLA, Anchor::from_bytes([0; 32]).unwrap(), ); for _ in 0..num_recipients { builder - .add_recipient( + .add_output( None, recipient, NoteValue::from_raw(10), @@ -42,7 +41,7 @@ fn criterion_benchmark(c: &mut Criterion) { ) .unwrap(); } - let bundle: Bundle<_, i64> = builder.build(rng).unwrap(); + let bundle: Bundle<_, i64> = builder.build(rng).unwrap().unwrap().0; let instances: Vec<_> = bundle .actions() diff --git a/benches/note_decryption.rs b/benches/note_decryption.rs index ba0f62112..577cc20a0 100644 --- a/benches/note_decryption.rs +++ b/benches/note_decryption.rs @@ -1,7 +1,6 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use orchard::{ - builder::Builder, - bundle::Flags, + builder::{Builder, BundleType}, circuit::ProvingKey, keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendingKey}, note::AssetBase, @@ -47,13 +46,13 @@ fn bench_note_decryption(c: &mut Criterion) { let bundle = { let mut builder = Builder::new( - Flags::from_parts(true, true, false), + BundleType::DEFAULT_VANILLA, Anchor::from_bytes([0; 32]).unwrap(), ); // The builder pads to two actions, and shuffles their order. Add two recipients // so the first action is always decryptable. builder - .add_recipient( + .add_output( None, recipient, NoteValue::from_raw(10), @@ -62,7 +61,7 @@ fn bench_note_decryption(c: &mut Criterion) { ) .unwrap(); builder - .add_recipient( + .add_output( None, recipient, NoteValue::from_raw(10), @@ -70,7 +69,7 @@ fn bench_note_decryption(c: &mut Criterion) { None, ) .unwrap(); - let bundle: Bundle<_, i64> = builder.build(rng).unwrap(); + let bundle: Bundle<_, i64> = builder.build(rng).unwrap().unwrap().0; bundle .create_proof(&pk, rng) .unwrap() diff --git a/src/action.rs b/src/action.rs index 58b273f17..5f3c9f9ac 100644 --- a/src/action.rs +++ b/src/action.rs @@ -1,19 +1,15 @@ use memuse::DynamicUsage; use crate::{ - note::{ExtractedNoteCommitment, Nullifier, TransmittedNoteCiphertext}, + note::{ExtractedNoteCommitment, Nullifier, Rho, TransmittedNoteCiphertext}, primitives::redpallas::{self, SpendAuth}, value::ValueCommitment, }; /// An action applied to the global ledger. /// -/// Externally, this both creates a note (adding a commitment to the global ledger), -/// and consumes some note created prior to this action (adding a nullifier to the -/// global ledger). -/// -/// Internally, this may both consume a note and create a note, or it may do only one of -/// the two. TODO: Determine which is more efficient (circuit size vs bundle size). +/// This both creates a note (adding a commitment to the global ledger), and consumes +/// some note created prior to this action (adding a nullifier to the global ledger). #[derive(Debug, Clone)] pub struct Action { /// The nullifier of the note being spent. @@ -70,6 +66,11 @@ impl Action { &self.encrypted_note } + /// Obtains the [`Rho`] value that was used to construct the new note being created. + pub fn rho(&self) -> Rho { + Rho::from_nf_old(self.nf) + } + /// Returns the commitment to the net value created or consumed by this action. pub fn cv_net(&self) -> &ValueCommitment { &self.cv_net diff --git a/src/builder.rs b/src/builder.rs index 5e51162e5..ff3459012 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -10,17 +10,16 @@ use nonempty::NonEmpty; use pasta_curves::pallas; use rand::{prelude::SliceRandom, CryptoRng, RngCore}; -use crate::note::AssetBase; use crate::{ action::Action, address::Address, - bundle::{Authorization, Authorized, Bundle, Flags}, + bundle::{derive_bvk, Authorization, Authorized, Bundle, Flags}, circuit::{Circuit, Instance, Proof, ProvingKey}, keys::{ FullViewingKey, OutgoingViewingKey, Scope, SpendAuthorizingKey, SpendValidatingKey, SpendingKey, }, - note::{Note, TransmittedNoteCiphertext}, + note::{AssetBase, Note, Rho, TransmittedNoteCiphertext}, note_encryption_v3::OrchardNoteEncryption, primitives::redpallas::{self, Binding, SpendAuth}, tree::{Anchor, MerklePath}, @@ -29,9 +28,101 @@ use crate::{ const MIN_ACTIONS: usize = 2; +/// An enumeration of rules for Orchard bundle construction. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum BundleType { + /// A transactional bundle will be padded if necessary to contain at least 2 actions, + /// irrespective of whether any genuine actions are required. + Transactional { + /// The flags that control whether spends and/or outputs are enabled for the bundle. + flags: Flags, + /// A flag that, when set to `true`, indicates that a bundle should be produced even if no + /// spends or outputs have been added to the bundle; in such a circumstance, all of the + /// actions in the resulting bundle will be dummies. + bundle_required: bool, + }, + /// A coinbase bundle is required to have no non-dummy spends. No padding is performed. + Coinbase, +} + +impl BundleType { + /// The default bundle type has all flags enabled, ZSA disabled, and does not require a bundle + /// to be produced. + pub const DEFAULT_VANILLA: BundleType = BundleType::Transactional { + flags: Flags::ENABLED_WITHOUT_ZSA, + bundle_required: false, + }; + + /// The default bundle with all flags enabled, including ZSA. + pub const DEFAULT_ZSA: BundleType = BundleType::Transactional { + flags: Flags::ENABLED_WITH_ZSA, + bundle_required: false, + }; + + /// The DISABLED bundle type does not permit any bundle to be produced, and when used in the + /// builder will prevent any spends or outputs from being added. + pub const DISABLED: BundleType = BundleType::Transactional { + flags: Flags::from_parts(false, false, false), + bundle_required: false, + }; + + /// Returns the number of logical actions that builder will produce in constructing a bundle + /// of this type, given the specified numbers of spends and outputs. + /// + /// Returns an error if the specified number of spends and outputs is incompatible with + /// this bundle type. + pub fn num_actions( + &self, + num_spends: usize, + num_outputs: usize, + ) -> Result { + let num_requested_actions = core::cmp::max(num_spends, num_outputs); + + match self { + BundleType::Transactional { + flags, + bundle_required, + } => { + if !flags.spends_enabled() && num_spends > 0 { + Err("Spends are disabled, so num_spends must be zero") + } else if !flags.outputs_enabled() && num_outputs > 0 { + Err("Outputs are disabled, so num_outputs must be zero") + } else { + Ok(if *bundle_required || num_requested_actions > 0 { + core::cmp::max(num_requested_actions, MIN_ACTIONS) + } else { + 0 + }) + } + } + BundleType::Coinbase => { + if num_spends > 0 { + Err("Coinbase bundles have spends disabled, so num_spends must be zero") + } else { + Ok(num_outputs) + } + } + } + } + + /// Returns the set of flags and the anchor that will be used for bundle construction. + pub fn flags(&self) -> Flags { + match self { + BundleType::Transactional { flags, .. } => *flags, + BundleType::Coinbase => Flags::SPENDS_DISABLED, + } + } +} + /// An error type for the kinds of errors that can occur during bundle construction. #[derive(Debug)] pub enum BuildError { + /// Spends are disabled for the provided bundle type. + SpendsDisabled, + /// Spends are disabled for the provided bundle type. + OutputsDisabled, + /// The anchor provided to this builder doesn't match the Merkle path used to add a spend. + AnchorMismatch, /// A bundle could not be built because required signatures were missing. MissingSignatures, /// An error occurred in the process of producing a proof for a bundle. @@ -44,6 +135,8 @@ pub enum BuildError { /// A signature is valid for more than one input. This should never happen if `alpha` /// is sampled correctly, and indicates a critical failure in randomness generation. DuplicateSignature, + /// The bundle being constructed violated the construction rules for the requested bundle type. + BundleTypeNotSatisfiable, } impl Display for BuildError { @@ -55,12 +148,32 @@ impl Display for BuildError { ValueSum(_) => f.write_str("Overflow occurred during value construction"), InvalidExternalSignature => f.write_str("External signature was invalid"), DuplicateSignature => f.write_str("Signature valid for more than one input"), + BundleTypeNotSatisfiable => { + f.write_str("Bundle structure did not conform to requested bundle type.") + } + SpendsDisabled => f.write_str("Spends are not enabled for the requested bundle type."), + OutputsDisabled => f.write_str("Spends are not enabled for the requested bundle type."), + AnchorMismatch => { + f.write_str("All spends must share the anchor requested for the transaction.") + } } } } impl std::error::Error for BuildError {} +impl From for BuildError { + fn from(e: halo2_proofs::plonk::Error) -> Self { + BuildError::Proof(e) + } +} + +impl From for BuildError { + fn from(e: value::OverflowError) -> Self { + BuildError::ValueSum(e) + } +} + /// An error type for adding a spend to the builder. #[derive(Debug, PartialEq, Eq)] pub enum SpendError { @@ -97,18 +210,6 @@ impl Display for OutputError { impl std::error::Error for OutputError {} -impl From for BuildError { - fn from(e: halo2_proofs::plonk::Error) -> Self { - BuildError::Proof(e) - } -} - -impl From for BuildError { - fn from(e: value::OverflowError) -> Self { - BuildError::ValueSum(e) - } -} - /// Information about a specific note to be spent in an [`Action`]. #[derive(Debug, Clone)] pub struct SpendInfo { @@ -184,33 +285,58 @@ impl SpendInfo { split_flag: true, } } + + fn has_matching_anchor(&self, anchor: &Anchor) -> bool { + if self.note.value() == NoteValue::zero() { + true + } else { + let cm = self.note.commitment(); + let path_root = self.merkle_path.root(cm.into()); + &path_root == anchor + } + } } -/// Information about a specific recipient to receive funds in an [`Action`]. +/// Information about a specific output to receive funds in an [`Action`]. #[derive(Debug, Clone)] -struct RecipientInfo { +pub struct OutputInfo { ovk: Option, recipient: Address, value: NoteValue, asset: AssetBase, - memo: Option<[u8; 512]>, + memo: [u8; 512], } -impl RecipientInfo { +impl OutputInfo { + /// Constructs a new OutputInfo from its constituent parts. + pub fn new( + ovk: Option, + recipient: Address, + value: NoteValue, + asset: AssetBase, + memo: Option<[u8; 512]>, + ) -> Self { + Self { + ovk, + recipient, + value, + asset, + memo: memo.unwrap_or_else(|| { + let mut memo = [0; 512]; + memo[0] = 0xf6; + memo + }), + } + } + /// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes]. /// /// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes - fn dummy(rng: &mut impl RngCore, asset: AssetBase) -> Self { + pub fn dummy(rng: &mut impl RngCore, asset: AssetBase) -> Self { let fvk: FullViewingKey = (&SpendingKey::random(rng)).into(); let recipient = fvk.address_at(0u32, Scope::External); - RecipientInfo { - ovk: None, - recipient, - value: NoteValue::zero(), - asset, - memo: None, - } + Self::new(None, recipient, NoteValue::zero(), asset, None) } } @@ -218,12 +344,12 @@ impl RecipientInfo { #[derive(Debug)] struct ActionInfo { spend: SpendInfo, - output: RecipientInfo, + output: OutputInfo, rcv: ValueCommitTrapdoor, } impl ActionInfo { - fn new(spend: SpendInfo, output: RecipientInfo, rng: impl RngCore) -> Self { + fn new(spend: SpendInfo, output: OutputInfo, rng: impl RngCore) -> Self { ActionInfo { spend, output, @@ -264,6 +390,7 @@ impl ActionInfo { let cv_net = ValueCommitment::derive(v_net, self.rcv, asset); let nf_old = self.spend.note.nullifier(&self.spend.fvk); + let rho = Rho::from_nf_old(nf_old); let ak: SpendValidatingKey = self.spend.fvk.clone().into(); let alpha = pallas::Scalar::random(&mut rng); let rk = ak.randomize(&alpha); @@ -272,21 +399,13 @@ impl ActionInfo { self.output.recipient, self.output.value, self.output.asset, - nf_old, + rho, &mut rng, ); let cm_new = note.commitment(); let cmx = cm_new.into(); - let encryptor = OrchardNoteEncryption::new( - self.output.ovk, - note, - self.output.memo.unwrap_or_else(|| { - let mut memo = [0; 512]; - memo[0] = 0xf6; - memo - }), - ); + let encryptor = OrchardNoteEncryption::new(self.output.ovk, note, self.output.memo); let encrypted_note = TransmittedNoteCiphertext { epk_bytes: encryptor.epk().to_bytes().0, @@ -311,25 +430,80 @@ impl ActionInfo { } } -/// A builder that constructs a [`Bundle`] from a set of notes to be spent, and recipients +/// Type alias for an in-progress bundle that has no proofs or signatures. +/// +/// This is returned by [`Builder::build`]. +pub type UnauthorizedBundle = Bundle, V>; + +/// Metadata about a bundle created by [`bundle`] or [`Builder::build`] that is not +/// necessarily recoverable from the bundle itself. +/// +/// This includes information about how [`Action`]s within the bundle are ordered (after +/// padding and randomization) relative to the order in which spends and outputs were +/// provided (to [`bundle`]), or the order in which [`Builder`] mutations were performed. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BundleMetadata { + spend_indices: Vec, + output_indices: Vec, +} + +impl BundleMetadata { + fn new(num_requested_spends: usize, num_requested_outputs: usize) -> Self { + BundleMetadata { + spend_indices: vec![0; num_requested_spends], + output_indices: vec![0; num_requested_outputs], + } + } + + /// Returns the metadata for a [`Bundle`] that contains only dummy actions, if any. + pub fn empty() -> Self { + Self::new(0, 0) + } + + /// Returns the index within the bundle of the [`Action`] corresponding to the `n`-th + /// spend specified in bundle construction. If a [`Builder`] was used, this refers to + /// the spend added by the `n`-th call to [`Builder::add_spend`]. + /// + /// For the purpose of improving indistinguishability, actions are padded and note + /// positions are randomized when building bundles. This means that the bundle + /// consumer cannot assume that e.g. the first spend they added corresponds to the + /// first action in the bundle. + pub fn spend_action_index(&self, n: usize) -> Option { + self.spend_indices.get(n).copied() + } + + /// Returns the index within the bundle of the [`Action`] corresponding to the `n`-th + /// output specified in bundle construction. If a [`Builder`] was used, this refers to + /// the output added by the `n`-th call to [`Builder::add_output`]. + /// + /// For the purpose of improving indistinguishability, actions are padded and note + /// positions are randomized when building bundles. This means that the bundle + /// consumer cannot assume that e.g. the first output they added corresponds to the + /// first action in the bundle. + pub fn output_action_index(&self, n: usize) -> Option { + self.output_indices.get(n).copied() + } +} + +/// A builder that constructs a [`Bundle`] from a set of notes to be spent, and outputs /// to receive funds. #[derive(Debug)] pub struct Builder { spends: Vec, - recipients: Vec, + outputs: Vec, burn: HashMap, - flags: Flags, + bundle_type: BundleType, anchor: Anchor, } impl Builder { /// Constructs a new empty builder for an Orchard bundle. - pub fn new(flags: Flags, anchor: Anchor) -> Self { + pub fn new(bundle_type: BundleType, anchor: Anchor) -> Self { Builder { spends: vec![], - recipients: vec![], + outputs: vec![], burn: HashMap::new(), - flags, + bundle_type, anchor, } } @@ -352,36 +526,25 @@ impl Builder { note: Note, merkle_path: MerklePath, ) -> Result<(), SpendError> { - if !self.flags.spends_enabled() { + let flags = self.bundle_type.flags(); + if !flags.spends_enabled() { return Err(SpendError::SpendsDisabled); } + let spend = SpendInfo::new(fvk, note, merkle_path, false).ok_or(SpendError::FvkMismatch)?; + // Consistency check: all anchors must be equal. - let cm = note.commitment(); - let path_root = merkle_path.root(cm.into()); - if path_root != self.anchor { + if !spend.has_matching_anchor(&self.anchor) { return Err(SpendError::AnchorMismatch); } - // Check if note is internal or external. - let scope = fvk - .scope_for_address(¬e.recipient()) - .ok_or(SpendError::FvkMismatch)?; - - self.spends.push(SpendInfo { - dummy_sk: None, - fvk, - scope, - note, - merkle_path, - split_flag: false, - }); + self.spends.push(spend); Ok(()) } /// Adds an address which will receive funds in this transaction. - pub fn add_recipient( + pub fn add_output( &mut self, ovk: Option, recipient: Address, @@ -389,17 +552,13 @@ impl Builder { asset: AssetBase, memo: Option<[u8; 512]>, ) -> Result<(), OutputError> { - if !self.flags.outputs_enabled() { + let flags = self.bundle_type.flags(); + if !flags.outputs_enabled() { return Err(OutputError); } - self.recipients.push(RecipientInfo { - ovk, - recipient, - value, - asset, - memo, - }); + self.outputs + .push(OutputInfo::new(ovk, recipient, value, asset, memo)); Ok(()) } @@ -429,7 +588,7 @@ impl Builder { /// Returns the action output components that will be produced by the /// transaction being constructed pub fn outputs(&self) -> &Vec { - &self.recipients + &self.outputs } /// The net value of the bundle to be built. The value of all spends, @@ -448,172 +607,256 @@ impl Builder { .iter() .map(|spend| spend.note.value() - NoteValue::zero()) .chain( - self.recipients + self.outputs .iter() - .map(|recipient| NoteValue::zero() - recipient.value), + .map(|output| NoteValue::zero() - output.value), ) .fold(Some(ValueSum::zero()), |acc, note_value| acc? + note_value) .ok_or(OverflowError)?; i64::try_from(value_balance).and_then(|i| V::try_from(i).map_err(|_| value::OverflowError)) } - /// Returns the number of actions to add to this bundle in order to contain at least MIN_ACTION actions. - fn num_missing_actions(&self) -> usize { - let num_actions = [self.spends.len(), self.recipients.len()] - .iter() - .max() - .cloned() - .unwrap(); - if num_actions < MIN_ACTIONS { - MIN_ACTIONS - num_actions - } else { - 0 - } - } - - /// Builds a bundle containing the given spent notes and recipients. + /// Builds a bundle containing the given spent notes and outputs. /// /// The returned bundle will have no proof or signatures; these can be applied with /// [`Bundle::create_proof`] and [`Bundle::apply_signatures`] respectively. - pub fn build + Copy + Into>( + pub fn build>( self, - mut rng: impl RngCore, - ) -> Result, V>, BuildError> { - let mut pre_actions: Vec<_> = Vec::new(); - - // Pair up the spends and recipients, extending with dummy values as necessary. - for (asset, (mut spends, mut recipients)) in - partition_by_asset(&self.spends, &self.recipients, &mut rng) - { - let num_spends = spends.len(); - let num_recipients = recipients.len(); - let mut num_actions = [num_spends, num_recipients].iter().max().cloned().unwrap(); - // We might have to add dummy/split actions only for the first asset to reach MIN_ACTIONS. - pre_actions - .is_empty() - .then(|| num_actions += self.num_missing_actions()); - - let first_spend = spends.first().cloned(); - - spends.extend( - iter::repeat_with(|| pad_spend(first_spend.as_ref(), asset, &mut rng)) - .take(num_actions - num_spends), - ); - - // Extend the recipients with dummy values. - recipients.extend( - iter::repeat_with(|| RecipientInfo::dummy(&mut rng, asset)) - .take(num_actions - num_recipients), - ); - - // Shuffle the spends and recipients, so that learning the position of a - // specific spent note or output note doesn't reveal anything on its own about - // the meaning of that note in the transaction context. - spends.shuffle(&mut rng); - recipients.shuffle(&mut rng); - - assert_eq!(spends.len(), recipients.len()); - pre_actions.extend( - spends - .into_iter() - .zip(recipients.into_iter()) - .map(|(spend, recipient)| ActionInfo::new(spend, recipient, &mut rng)), - ); - } - - // Move some things out of self that we will need. - let flags = self.flags; - let anchor = self.anchor; - - // Determine the value balance for this bundle, ensuring it is valid. - let native_value_balance: V = pre_actions - .iter() - .filter(|action| action.spend.note.asset().is_native().into()) - .fold(Some(ValueSum::zero()), |acc, action| { - acc? + action.value_sum() - }) - .ok_or(OverflowError)? - .into()?; - - // Compute the transaction binding signing key. - let bsk = pre_actions - .iter() - .map(|a| &a.rcv) - .sum::() - .into_bsk(); - - // Create the actions. - let (actions, circuits): (Vec<_>, Vec<_>) = - pre_actions.into_iter().map(|a| a.build(&mut rng)).unzip(); - - let bundle = Bundle::from_parts( - NonEmpty::from_vec(actions).unwrap(), - flags, - native_value_balance, - self.burn - .into_iter() - .map(|(asset, value)| Ok((asset, value.into()?))) - .collect::>()?, - anchor, - InProgress { - proof: Unproven { circuits }, - sigs: Unauthorized { bsk }, - }, - ); - - assert_eq!( - redpallas::VerificationKey::from(&bundle.authorization().sigs.bsk), - bundle.binding_validating_key() - ); - Ok(bundle) + rng: impl RngCore, + ) -> Result, BundleMetadata)>, BuildError> { + bundle( + rng, + self.anchor, + self.bundle_type, + self.spends, + self.outputs, + self.burn, + ) } } +/// The index of the attached spend or output in the bundle. +/// None indicates a dummy note. +/// The index is used to track the position of the note in the bundle. +type MetadataIdx = Option; + /// Partition a list of spends and recipients by note types. /// Method creates single dummy ZEC note if spends and recipients are both empty. +#[allow(clippy::type_complexity)] fn partition_by_asset( spends: &[SpendInfo], - recipients: &[RecipientInfo], + outputs: &[OutputInfo], rng: &mut impl RngCore, -) -> HashMap, Vec)> { +) -> HashMap< + AssetBase, + ( + Vec<(SpendInfo, MetadataIdx)>, + Vec<(OutputInfo, MetadataIdx)>, + ), +> { let mut hm = HashMap::new(); - for s in spends { + for (i, s) in spends.iter().enumerate() { hm.entry(s.note.asset()) .or_insert((vec![], vec![])) .0 - .push(s.clone()); + .push((s.clone(), Some(i))); } - for r in recipients { - hm.entry(r.asset) + for (i, o) in outputs.iter().enumerate() { + hm.entry(o.asset) .or_insert((vec![], vec![])) .1 - .push(r.clone()) + .push((o.clone(), Some(i))); } if hm.is_empty() { - let dummy_spend = SpendInfo::dummy(AssetBase::native(), rng); - hm.insert(dummy_spend.note.asset(), (vec![dummy_spend], vec![])); + let dummy_spend = pad_spend(None, AssetBase::native(), rng); + // dummy_spend should not be included in the indexing and marked as None. + hm.insert( + dummy_spend.note.asset(), + (vec![(dummy_spend, None)], vec![]), + ); } hm } -/// Returns a dummy/split notes to extend the spends. +/// Returns the appropriate SpendInfo for padding. fn pad_spend(spend: Option<&SpendInfo>, asset: AssetBase, mut rng: impl RngCore) -> SpendInfo { if asset.is_native().into() { // For native asset, extends with dummy notes SpendInfo::dummy(asset, &mut rng) } else { // For ZSA asset, extends with - // - dummy notes if first spend is empty + // - dummy note if SpendInfo is None // - split notes otherwise. let dummy = SpendInfo::dummy(asset, &mut rng); spend.map_or_else(|| dummy, |s| s.create_split_spend(&mut rng)) } } +/// Builds a bundle containing the given spent notes and outputs. +/// +/// The returned bundle will have no proof or signatures; these can be applied with +/// [`Bundle::create_proof`] and [`Bundle::apply_signatures`] respectively. +pub fn bundle>( + mut rng: impl RngCore, + anchor: Anchor, + bundle_type: BundleType, + spends: Vec, + outputs: Vec, + burn: HashMap, +) -> Result, BundleMetadata)>, BuildError> { + let flags = bundle_type.flags(); + + let num_requested_spends = spends.len(); + if !flags.spends_enabled() && num_requested_spends > 0 { + return Err(BuildError::SpendsDisabled); + } + + for spend in &spends { + if !spend.has_matching_anchor(&anchor) { + return Err(BuildError::AnchorMismatch); + } + } + + let num_requested_outputs = outputs.len(); + if !flags.outputs_enabled() && num_requested_outputs > 0 { + return Err(BuildError::OutputsDisabled); + } + + // Pair up the spends and outputs, extending with dummy values as necessary. + let (pre_actions, bundle_meta) = { + // Use Vec::with_capacity().extend(...) instead of .collect() to avoid reallocations, + // as we can estimate the vector size beforehand. + let mut indexed_spends_outputs = + Vec::with_capacity(spends.len().max(outputs.len()).max(MIN_ACTIONS)); + + indexed_spends_outputs.extend( + partition_by_asset(&spends, &outputs, &mut rng) + .into_iter() + .flat_map(|(asset, (spends, outputs))| { + let num_asset_pre_actions = spends.len().max(outputs.len()); + + let first_spend = spends.first().map(|(s, _)| s.clone()); + + let mut indexed_spends = spends + .into_iter() + .chain(iter::repeat_with(|| { + (pad_spend(first_spend.as_ref(), asset, &mut rng), None) + })) + .take(num_asset_pre_actions) + .collect::>(); + + let mut indexed_outputs = outputs + .into_iter() + .chain(iter::repeat_with(|| { + (OutputInfo::dummy(&mut rng, asset), None) + })) + .take(num_asset_pre_actions) + .collect::>(); + + // Shuffle the spends and outputs, so that learning the position of a + // specific spent note or output note doesn't reveal anything on its own about + // the meaning of that note in the transaction context. + indexed_spends.shuffle(&mut rng); + indexed_outputs.shuffle(&mut rng); + + assert_eq!(indexed_spends.len(), indexed_outputs.len()); + + indexed_spends.into_iter().zip(indexed_outputs) + }), + ); + + indexed_spends_outputs.extend( + iter::repeat_with(|| { + ( + (pad_spend(None, AssetBase::native(), &mut rng), None), + (OutputInfo::dummy(&mut rng, AssetBase::native()), None), + ) + }) + .take(MIN_ACTIONS.saturating_sub(indexed_spends_outputs.len())), + ); + + let mut bundle_meta = BundleMetadata::new(num_requested_spends, num_requested_outputs); + let pre_actions = indexed_spends_outputs + .into_iter() + .enumerate() + .map(|(action_idx, ((spend, spend_idx), (output, out_idx)))| { + // Record the post-randomization spend location + if let Some(spend_idx) = spend_idx { + bundle_meta.spend_indices[spend_idx] = action_idx; + } + + // Record the post-randomization output location + if let Some(out_idx) = out_idx { + bundle_meta.output_indices[out_idx] = action_idx; + } + + ActionInfo::new(spend, output, &mut rng) + }) + .collect::>(); + + (pre_actions, bundle_meta) + }; + + // Determine the value balance for this bundle, ensuring it is valid. + let native_value_balance: i64 = pre_actions + .iter() + .filter(|action| action.spend.note.asset().is_native().into()) + .fold(Some(ValueSum::zero()), |acc, action| { + acc? + action.value_sum() + }) + .ok_or(OverflowError)? + .into()?; + + let result_value_balance = V::try_from(native_value_balance) + .map_err(|_| BuildError::ValueSum(value::OverflowError))?; + + // Compute the transaction binding signing key. + let bsk = pre_actions + .iter() + .map(|a| &a.rcv) + .sum::() + .into_bsk(); + + // Create the actions. + let (actions, circuits): (Vec<_>, Vec<_>) = + pre_actions.into_iter().map(|a| a.build(&mut rng)).unzip(); + + // Verify that bsk and bvk are consistent. + let bvk = derive_bvk( + &actions, + native_value_balance, + burn.iter() + .flat_map(|(asset, value)| -> Result<_, BuildError> { Ok((*asset, (*value).into()?)) }), + ); + assert_eq!(redpallas::VerificationKey::from(&bsk), bvk); + + let burn = burn + .into_iter() + .map(|(asset, value)| Ok((asset, value.into()?))) + .collect::, BuildError>>()?; + + Ok(NonEmpty::from_vec(actions).map(|actions| { + ( + Bundle::from_parts( + actions, + flags, + result_value_balance, + burn, + anchor, + InProgress { + proof: Unproven { circuits }, + sigs: Unauthorized { bsk }, + }, + ), + bundle_meta, + ) + })) +} + /// Marker trait representing bundle signatures in the process of being created. pub trait InProgressSignatures: fmt::Debug { /// The authorization type of an Orchard action in the process of being authorized. @@ -897,7 +1140,7 @@ pub trait OutputView { fn value>(&self) -> V; } -impl OutputView for RecipientInfo { +impl OutputView for OutputInfo { fn value>(&self) -> V { V::from(self.value.inner()) } @@ -917,7 +1160,7 @@ pub mod testing { use crate::note::AssetBase; use crate::{ address::testing::arb_address, - bundle::{Authorized, Bundle, Flags}, + bundle::{Authorized, Bundle}, circuit::ProvingKey, keys::{testing::arb_spending_key, FullViewingKey, SpendAuthorizingKey, SpendingKey}, note::testing::arb_note, @@ -926,7 +1169,7 @@ pub mod testing { Address, Note, }; - use super::Builder; + use super::{Builder, BundleType}; /// An intermediate type used for construction of arbitrary /// bundle values. This type is required because of a limitation @@ -942,26 +1185,25 @@ pub mod testing { sk: SpendingKey, anchor: Anchor, notes: Vec<(Note, MerklePath)>, - recipient_amounts: Vec<(Address, NoteValue, AssetBase)>, + output_amounts: Vec<(Address, NoteValue, AssetBase)>, } impl ArbitraryBundleInputs { /// Create a bundle from the set of arbitrary bundle inputs. fn into_bundle + Copy + Into>(mut self) -> Bundle { let fvk = FullViewingKey::from(&self.sk); - let flags = Flags::from_parts(true, true, true); - let mut builder = Builder::new(flags, self.anchor); + let mut builder = Builder::new(BundleType::DEFAULT_ZSA, self.anchor); for (note, path) in self.notes.into_iter() { builder.add_spend(fvk.clone(), note, path).unwrap(); } - for (addr, value, asset) in self.recipient_amounts.into_iter() { + for (addr, value, asset) in self.output_amounts.into_iter() { let scope = fvk.scope_for_address(&addr).unwrap(); let ovk = fvk.to_ovk(scope); builder - .add_recipient(Some(ovk.clone()), addr, value, asset, None) + .add_output(Some(ovk.clone()), addr, value, asset, None) .unwrap(); } @@ -969,6 +1211,8 @@ pub mod testing { builder .build(&mut self.rng) .unwrap() + .unwrap() + .0 .create_proof(&pk, &mut self.rng) .unwrap() .prepare(&mut self.rng, [0; 32]) @@ -983,7 +1227,7 @@ pub mod testing { fn arb_bundle_inputs(sk: SpendingKey) ( n_notes in 1usize..30, - n_recipients in 1..30, + n_outputs in 1..30, ) ( @@ -992,14 +1236,14 @@ pub mod testing { arb_positive_note_value(MAX_NOTE_VALUE / n_notes as u64).prop_flat_map(arb_note), n_notes ), - recipient_amounts in vec( + output_amounts in vec( arb_address().prop_flat_map(move |a| { - arb_positive_note_value(MAX_NOTE_VALUE / n_recipients as u64) + arb_positive_note_value(MAX_NOTE_VALUE / n_outputs as u64) .prop_map(move |v| { (a,v, AssetBase::native()) }) }), - n_recipients as usize, + n_outputs as usize, ), rng_seed in prop::array::uniform32(prop::num::u8::ANY) ) -> ArbitraryBundleInputs { @@ -1024,7 +1268,7 @@ pub mod testing { sk, anchor: frontier.root().into(), notes: notes_and_auth_paths, - recipient_amounts + output_amounts } } } @@ -1052,7 +1296,8 @@ mod tests { use super::Builder; use crate::note::AssetBase; use crate::{ - bundle::{Authorized, Bundle, Flags}, + builder::BundleType, + bundle::{Authorized, Bundle}, circuit::ProvingKey, constants::MERKLE_DEPTH_ORCHARD, keys::{FullViewingKey, Scope, SpendingKey}, @@ -1070,12 +1315,12 @@ mod tests { let recipient = fvk.address_at(0u32, Scope::External); let mut builder = Builder::new( - Flags::from_parts(true, true, false), + BundleType::DEFAULT_VANILLA, EMPTY_ROOTS[MERKLE_DEPTH_ORCHARD].into(), ); builder - .add_recipient( + .add_output( None, recipient, NoteValue::from_raw(5000), @@ -1089,6 +1334,8 @@ mod tests { let bundle: Bundle = builder .build(&mut rng) .unwrap() + .unwrap() + .0 .create_proof(&pk, &mut rng) .unwrap() .prepare(rng, [0; 32]) diff --git a/src/bundle.rs b/src/bundle.rs index 60cd3d75f..385e5fec1 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -45,7 +45,7 @@ impl Action { } /// Orchard-specific flags. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Flags { /// Flag denoting whether Orchard spends are enabled in the transaction. /// @@ -73,7 +73,11 @@ const FLAGS_EXPECTED_UNSET: u8 = !(FLAG_SPENDS_ENABLED | FLAG_OUTPUTS_ENABLED | impl Flags { /// Construct a set of flags from its constituent parts - pub fn from_parts(spends_enabled: bool, outputs_enabled: bool, zsa_enabled: bool) -> Self { + pub(crate) const fn from_parts( + spends_enabled: bool, + outputs_enabled: bool, + zsa_enabled: bool, + ) -> Self { Flags { spends_enabled, outputs_enabled, @@ -81,6 +85,34 @@ impl Flags { } } + /// The flag set with both spends and outputs enabled and ZSA disabled. + pub const ENABLED_WITHOUT_ZSA: Flags = Flags { + spends_enabled: true, + outputs_enabled: true, + zsa_enabled: false, + }; + + /// The flags set with spends, outputs and ZSA enabled. + pub const ENABLED_WITH_ZSA: Flags = Flags { + spends_enabled: true, + outputs_enabled: true, + zsa_enabled: true, + }; + + /// The flag set with spends disabled. + pub const SPENDS_DISABLED: Flags = Flags { + spends_enabled: false, + outputs_enabled: true, + zsa_enabled: false, + }; + + /// The flag set with outputs disabled. + pub const OUTPUTS_DISABLED: Flags = Flags { + spends_enabled: true, + outputs_enabled: false, + zsa_enabled: false, + }; + /// Flag denoting whether Orchard spends are enabled in the transaction. /// /// If `false`, spent notes within [`Action`]s in the transaction's [`Bundle`] are @@ -132,12 +164,13 @@ impl Flags { /// /// [txencoding]: https://zips.z.cash/protocol/protocol.pdf#txnencoding pub fn from_byte(value: u8) -> Option { + // https://p.z.cash/TCR:bad-txns-v5-reserved-bits-nonzero if value & FLAGS_EXPECTED_UNSET == 0 { - Some(Self::from_parts( - value & FLAG_SPENDS_ENABLED != 0, - value & FLAG_OUTPUTS_ENABLED != 0, - value & FLAG_ZSA_ENABLED != 0, - )) + Some(Self { + spends_enabled: value & FLAG_SPENDS_ENABLED != 0, + outputs_enabled: value & FLAG_OUTPUTS_ENABLED != 0, + zsa_enabled: value & FLAG_ZSA_ENABLED != 0, + }) } else { None } @@ -402,6 +435,33 @@ impl Bundle { } } +pub(crate) fn derive_bvk<'a, A: 'a, V: Clone + Into>( + actions: impl IntoIterator>, + value_balance: V, + burn: impl Iterator, +) -> redpallas::VerificationKey { + // https://p.z.cash/TCR:bad-txns-orchard-binding-signature-invalid?partial + (actions + .into_iter() + .map(|a| a.cv_net()) + .sum::() + - ValueCommitment::derive( + ValueSum::from_raw(value_balance.into()), + ValueCommitTrapdoor::zero(), + AssetBase::native(), + ) + - burn + .map(|(asset, value)| { + ValueCommitment::derive( + ValueSum::from_raw(value.into()), + ValueCommitTrapdoor::zero(), + asset, + ) + }) + .sum::()) + .into_bvk() +} + impl> Bundle { /// Computes a commitment to the effects of this bundle, suitable for inclusion within /// a transaction ID. @@ -414,29 +474,7 @@ impl> Bundle { /// This can be used to validate the [`Authorized::binding_signature`] returned from /// [`Bundle::authorization`]. pub fn binding_validating_key(&self) -> redpallas::VerificationKey { - (self - .actions - .iter() - .map(|a| a.cv_net()) - .sum::() - - ValueCommitment::derive( - ValueSum::from_raw(self.value_balance.into()), - ValueCommitTrapdoor::zero(), - AssetBase::native(), - ) - - self - .burn - .clone() - .into_iter() - .map(|(asset, value)| { - ValueCommitment::derive( - ValueSum::from_raw(value.into()), - ValueCommitTrapdoor::zero(), - asset, - ) - }) - .sum::()) - .into_bvk() + derive_bvk(&self.actions, self.value_balance, self.burn.iter().cloned()) } } diff --git a/src/bundle/batch.rs b/src/bundle/batch.rs index c60d0cd55..6626b9162 100644 --- a/src/bundle/batch.rs +++ b/src/bundle/batch.rs @@ -67,6 +67,8 @@ impl BatchValidator { /// figure out which of the accumulated bundles might be invalid; if that information /// is desired, construct separate [`BatchValidator`]s for sub-batches of the bundles. pub fn validate(self, vk: &VerifyingKey, rng: R) -> bool { + // https://p.z.cash/TCR:bad-txns-orchard-binding-signature-invalid?partial + if self.signatures.is_empty() { // An empty batch is always valid, but is not free to run; skip it. // Note that a transaction has at least a binding signature, so if diff --git a/src/circuit.rs b/src/circuit.rs index 0134ead77..bfa78dce9 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -38,7 +38,7 @@ use crate::{ note::{ commitment::{NoteCommitTrapdoor, NoteCommitment}, nullifier::Nullifier, - AssetBase, ExtractedNoteCommitment, Note, + AssetBase, ExtractedNoteCommitment, Note, Rho, }, primitives::redpallas::{SpendAuth, VerificationKey}, spec::NonIdentityPallasPoint, @@ -114,7 +114,7 @@ pub struct Circuit { pub(crate) g_d_old: Value, pub(crate) pk_d_old: Value, pub(crate) v_old: Value, - pub(crate) rho_old: Value, + pub(crate) rho_old: Value, pub(crate) psi_old: Value, pub(crate) rcm_old: Value, pub(crate) cm_old: Value, @@ -155,7 +155,7 @@ impl Circuit { alpha: pallas::Scalar, rcv: ValueCommitTrapdoor, ) -> Option { - (spend.note.nullifier(&spend.fvk) == output_note.rho()) + (Rho::from_nf_old(spend.note.nullifier(&spend.fvk)) == output_note.rho()) .then(|| Self::from_action_context_unchecked(spend, output_note, alpha, rcv)) } @@ -507,7 +507,7 @@ impl plonk::Circuit for Circuit { let rho_old = assign_free_advice( layouter.namespace(|| "witness rho_old"), config.advices[0], - self.rho_old.map(|rho| rho.0), + self.rho_old.map(|rho| rho.into_inner()), )?; // Witness cm_old @@ -1198,7 +1198,7 @@ mod tests { use crate::primitives::redpallas::VerificationKey; use crate::{ keys::{FullViewingKey, Scope, SpendValidatingKey, SpendingKey}, - note::{Note, NoteCommitment}, + note::{Note, NoteCommitment, Rho}, tree::MerklePath, value::{NoteValue, ValueCommitTrapdoor, ValueCommitment}, }; @@ -1214,7 +1214,11 @@ mod tests { let alpha = pallas::Scalar::random(&mut rng); let rk = ak.randomize(&alpha); - let (_, _, output_note) = Note::dummy(&mut rng, Some(nf_old), AssetBase::native()); + let (_, _, output_note) = Note::dummy( + &mut rng, + Some(Rho::from_nf_old(nf_old)), + AssetBase::native(), + ); let cmx = output_note.commitment().into(); let value = spent_note.value() - output_note.value(); @@ -1494,7 +1498,7 @@ mod tests { let sk = SpendingKey::random(&mut rng); let fvk: FullViewingKey = (&sk).into(); let sender_address = fvk.address_at(0u32, Scope::External); - let rho_old = Nullifier::dummy(&mut rng); + let rho_old = Rho::from_nf_old(Nullifier::dummy(&mut rng)); let note = Note::new( sender_address, NoteValue::from_raw(40), @@ -1538,7 +1542,13 @@ mod tests { let fvk: FullViewingKey = (&sk).into(); let sender_address = fvk.address_at(0u32, Scope::External); - Note::new(sender_address, output_value, asset_base, nf_old, &mut rng) + Note::new( + sender_address, + output_value, + asset_base, + Rho::from_nf_old(nf_old), + &mut rng, + ) }; let cmx = output_note.commitment().into(); diff --git a/src/constants.rs b/src/constants.rs index 39fd3df50..8a9b76f23 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -7,7 +7,7 @@ pub use fixed_bases::{NullifierK, OrchardFixedBases, OrchardFixedBasesFull, Valu pub use sinsemilla::{OrchardCommitDomains, OrchardHashDomains}; /// $\mathsf{MerkleDepth^{Orchard}}$ -pub(crate) const MERKLE_DEPTH_ORCHARD: usize = 32; +pub const MERKLE_DEPTH_ORCHARD: usize = 32; /// The Pallas scalar field modulus is $q = 2^{254} + \mathsf{t_q}$. /// diff --git a/src/constants/fixed_bases/commit_ivk_r.rs b/src/constants/fixed_bases/commit_ivk_r.rs index 27b3dcca4..7bb8a9486 100644 --- a/src/constants/fixed_bases/commit_ivk_r.rs +++ b/src/constants/fixed_bases/commit_ivk_r.rs @@ -1,7 +1,7 @@ use group::ff::PrimeField; use pasta_curves::{arithmetic::CurveAffine, pallas}; -/// Generator used in SinsemillaCommit randomness for IVK commitment +/// Generator used in SinsemillaCommit randomness for IVK commitment. pub const GENERATOR: ([u8; 32], [u8; 32]) = ( [ 24, 161, 248, 95, 110, 72, 35, 152, 199, 237, 26, 211, 226, 127, 149, 2, 72, 137, 128, 64, @@ -13,7 +13,8 @@ pub const GENERATOR: ([u8; 32], [u8; 32]) = ( ], ); -/// Full-width z-values for GENERATOR +/// Full-width z-values for GENERATOR. +/// These can be reproduced by [`halo2_gadgets::ecc::chip::constants::find_zs_and_us`]. pub const Z: [u64; super::NUM_WINDOWS] = [ 18172, 17390, 61749, 65182, 33835, 155942, 26189, 52444, 40096, 139582, 99218, 20669, 291337, 12465, 132211, 75527, 68003, 95835, 237325, 21348, 35494, 215451, 49456, 6332, 99036, 224845, @@ -24,7 +25,8 @@ pub const Z: [u64; super::NUM_WINDOWS] = [ 7870, 143575, 13058, 27070, 30734, 41157, 2955, ]; -/// Full-width u-values for GENERATOR +/// Full-width u-values for GENERATOR. +/// These can be reproduced by [`halo2_gadgets::ecc::chip::constants::find_zs_and_us`]. pub const U: [[[u8; 32]; super::H]; super::NUM_WINDOWS] = [ [ [ diff --git a/src/constants/fixed_bases/note_commit_r.rs b/src/constants/fixed_bases/note_commit_r.rs index 436a26239..ddf5c43ba 100644 --- a/src/constants/fixed_bases/note_commit_r.rs +++ b/src/constants/fixed_bases/note_commit_r.rs @@ -1,7 +1,7 @@ use group::ff::PrimeField; use pasta_curves::{arithmetic::CurveAffine, pallas}; -/// Generator used in SinsemillaCommit randomness for note commitment +/// Generator used in SinsemillaCommit randomness for note commitment. pub const GENERATOR: ([u8; 32], [u8; 32]) = ( [ 19, 110, 252, 15, 72, 44, 2, 44, 124, 164, 20, 252, 92, 197, 158, 35, 242, 61, 111, 147, @@ -13,7 +13,8 @@ pub const GENERATOR: ([u8; 32], [u8; 32]) = ( ], ); -/// Full-width z-values for GENERATOR +/// Full-width z-values for GENERATOR. +/// These can be reproduced by [`halo2_gadgets::ecc::chip::constants::find_zs_and_us`]. pub const Z: [u64; super::NUM_WINDOWS] = [ 253356, 149209, 114903, 10575, 6973, 30969, 55415, 206450, 18453, 24528, 13099, 213949, 29959, 49929, 80867, 17465, 43715, 80241, 55983, 132629, 66101, 24136, 31372, 107975, 161748, 24107, @@ -24,7 +25,8 @@ pub const Z: [u64; super::NUM_WINDOWS] = [ 41201, 40964, 8563, 36035, 6334, 176, ]; -/// Full-width u-values for GENERATOR +/// Full-width u-values for GENERATOR. +/// These can be reproduced by [`halo2_gadgets::ecc::chip::constants::find_zs_and_us`]. pub const U: [[[u8; 32]; super::H]; super::NUM_WINDOWS] = [ [ [ diff --git a/src/constants/fixed_bases/nullifier_k.rs b/src/constants/fixed_bases/nullifier_k.rs index 8e1338d92..a6a09440a 100644 --- a/src/constants/fixed_bases/nullifier_k.rs +++ b/src/constants/fixed_bases/nullifier_k.rs @@ -1,6 +1,7 @@ use group::ff::PrimeField; use pasta_curves::{arithmetic::CurveAffine, pallas}; +/// Generator used as $\mathcal{K}^{\mathsf{Orchard}}$ in DeriveNullifier. pub const GENERATOR: ([u8; 32], [u8; 32]) = ( [ 117, 202, 71, 228, 167, 106, 111, 211, 155, 219, 181, 204, 146, 177, 126, 94, 207, 201, @@ -12,7 +13,8 @@ pub const GENERATOR: ([u8; 32], [u8; 32]) = ( ], ); -/// Full-width z-values for GENERATOR +/// Full-width z-values for GENERATOR. +/// These can be reproduced by [`halo2_gadgets::ecc::chip::constants::find_zs_and_us`]. pub const Z: [u64; super::NUM_WINDOWS] = [ 34374, 173069, 40776, 220066, 45494, 37762, 5245, 11979, 33386, 238556, 128731, 12128, 89982, 85351, 9804, 12820, 80455, 100009, 24382, 17854, 26367, 7067, 102106, 64293, 114999, 172304, @@ -24,6 +26,7 @@ pub const Z: [u64; super::NUM_WINDOWS] = [ ]; /// Full-width u-values for GENERATOR +/// These can be reproduced by [`halo2_gadgets::ecc::chip::constants::find_zs_and_us`]. pub const U: [[[u8; 32]; super::H]; super::NUM_WINDOWS] = [ [ [ diff --git a/src/constants/fixed_bases/spend_auth_g.rs b/src/constants/fixed_bases/spend_auth_g.rs index b4a48e9f4..f53d7aaab 100644 --- a/src/constants/fixed_bases/spend_auth_g.rs +++ b/src/constants/fixed_bases/spend_auth_g.rs @@ -1,8 +1,7 @@ use group::ff::PrimeField; use pasta_curves::{arithmetic::CurveAffine, pallas}; -/// The value commitment is used to check balance between inputs and outputs. The value is -/// placed over this generator. +/// Generator used as $\mathcal{G}^{\mathsf{Orchard}}$ in Spend authorization signatures. pub const GENERATOR: ([u8; 32], [u8; 32]) = ( [ 99, 201, 117, 184, 132, 114, 26, 141, 12, 161, 112, 123, 227, 12, 127, 12, 95, 68, 95, 62, @@ -14,7 +13,8 @@ pub const GENERATOR: ([u8; 32], [u8; 32]) = ( ], ); -/// Full-width z-values for GENERATOR +/// Full-width z-values for GENERATOR. +/// These can be reproduced by [`halo2_gadgets::ecc::chip::constants::find_zs_and_us`]. pub const Z: [u64; super::NUM_WINDOWS] = [ 49707, 15701, 45931, 163127, 41654, 212130, 34473, 25205, 4118, 10240, 12264, 22866, 203610, 18808, 13851, 62448, 62380, 94497, 39496, 73216, 32037, 32774, 61690, 39173, 74580, 84678, @@ -25,7 +25,8 @@ pub const Z: [u64; super::NUM_WINDOWS] = [ 100797, 80349, 87315, 77372, 96572, 18965, ]; -/// Full-width u-values for GENERATOR +/// Full-width u-values for GENERATOR. +/// These can be reproduced by [`halo2_gadgets::ecc::chip::constants::find_zs_and_us`]. pub const U: [[[u8; 32]; super::H]; super::NUM_WINDOWS] = [ [ [ diff --git a/src/constants/fixed_bases/value_commit_r.rs b/src/constants/fixed_bases/value_commit_r.rs index 088cebe79..9e4b2edce 100644 --- a/src/constants/fixed_bases/value_commit_r.rs +++ b/src/constants/fixed_bases/value_commit_r.rs @@ -1,8 +1,8 @@ use group::ff::PrimeField; use pasta_curves::{arithmetic::CurveAffine, pallas}; -/// The value commitment is used to check balance between inputs and outputs. The value is -/// placed over this generator. +/// The value commitment is a homomorphic Pedersen commitment used to check balance between inputs +/// and outputs. This is its randomness base, $\mathcal{R}^{\mathsf{Orchard}}$. pub const GENERATOR: ([u8; 32], [u8; 32]) = ( [ 145, 90, 60, 136, 104, 198, 195, 14, 47, 128, 144, 238, 69, 215, 110, 64, 72, 32, 141, 234, @@ -14,7 +14,8 @@ pub const GENERATOR: ([u8; 32], [u8; 32]) = ( ], ); -/// Full-width z-values for GENERATOR +/// Full-width z-values for GENERATOR. +/// These can be reproduced by [`halo2_gadgets::ecc::chip::constants::find_zs_and_us`]. pub const Z: [u64; super::NUM_WINDOWS] = [ 181916, 22148, 340526, 80718, 104958, 86894, 43381, 1060, 82130, 4741, 55897, 4304, 114469, 20503, 25001, 62408, 52978, 35893, 72071, 154369, 67304, 7299, 27960, 42929, 51869, 89967, @@ -25,7 +26,8 @@ pub const Z: [u64; super::NUM_WINDOWS] = [ 22632, 163228, 12997, 4461, 32320, 13430, ]; -/// Full-width u-values for GENERATOR +/// Full-width u-values for GENERATOR. +/// These can be reproduced by [`halo2_gadgets::ecc::chip::constants::find_zs_and_us`]. pub const U: [[[u8; 32]; super::H]; super::NUM_WINDOWS] = [ [ [ diff --git a/src/constants/fixed_bases/value_commit_v.rs b/src/constants/fixed_bases/value_commit_v.rs index 998e087f6..30f79ba19 100644 --- a/src/constants/fixed_bases/value_commit_v.rs +++ b/src/constants/fixed_bases/value_commit_v.rs @@ -1,8 +1,8 @@ use group::ff::PrimeField; use pasta_curves::{arithmetic::CurveAffine, pallas}; -/// The value commitment is used to check balance between inputs and outputs. The value is -/// placed over this generator. +/// The value commitment is a homomorphic Pedersen commitment used to check balance between inputs +/// and outputs. This is its value base, $\mathcal{V}^{\mathsf{Orchard}}$. pub const GENERATOR: ([u8; 32], [u8; 32]) = ( [ 103, 67, 249, 58, 110, 189, 167, 42, 140, 124, 90, 43, 127, 163, 4, 254, 50, 178, 155, 79, @@ -14,13 +14,15 @@ pub const GENERATOR: ([u8; 32], [u8; 32]) = ( ], ); -/// Short signed z-values for GENERATOR +/// Short signed z-values for GENERATOR. +/// These can be reproduced by [`halo2_gadgets::ecc::chip::constants::find_zs_and_us`]. pub const Z_SHORT: [u64; super::NUM_WINDOWS_SHORT] = [ 163547, 76040, 88852, 128479, 54088, 89871, 39598, 144309, 43471, 102492, 741, 55288, 33756, 77312, 12095, 48253, 45718, 202901, 33132, 71081, 152108, 169712, ]; -/// Short signed u-values for GENERATOR +/// Short signed u-values for GENERATOR. +/// These can be reproduced by [`halo2_gadgets::ecc::chip::constants::find_zs_and_us`]. pub const U_SHORT: [[[u8; 32]; super::H]; super::NUM_WINDOWS_SHORT] = [ [ [ diff --git a/src/issuance.rs b/src/issuance.rs index f2eec90c6..4fade4b74 100644 --- a/src/issuance.rs +++ b/src/issuance.rs @@ -15,7 +15,7 @@ use crate::issuance::Error::{ }; use crate::keys::{IssuanceAuthorizingKey, IssuanceValidatingKey}; use crate::note::asset_base::is_asset_desc_of_valid_size; -use crate::note::{AssetBase, Nullifier}; +use crate::note::{AssetBase, Nullifier, Rho}; use crate::value::{NoteValue, ValueSum}; use crate::{Address, Note}; @@ -287,7 +287,7 @@ impl IssueBundle { issue_info.recipient, issue_info.value, asset, - Nullifier::dummy(&mut rng), + Rho::from_nf_old(Nullifier::dummy(&mut rng)), &mut rng, ); @@ -335,7 +335,7 @@ impl IssueBundle { recipient, value, asset, - Nullifier::dummy(&mut rng), + Rho::from_nf_old(Nullifier::dummy(&mut rng)), &mut rng, ); @@ -597,7 +597,7 @@ mod tests { use crate::keys::{ FullViewingKey, IssuanceAuthorizingKey, IssuanceValidatingKey, Scope, SpendingKey, }; - use crate::note::{AssetBase, Nullifier}; + use crate::note::{AssetBase, Nullifier, Rho}; use crate::value::{NoteValue, ValueSum}; use crate::{Address, Note}; use group::{Group, GroupEncoding}; @@ -644,7 +644,7 @@ mod tests { recipient, NoteValue::from_raw(note1_value), asset, - Nullifier::dummy(&mut rng), + Rho::from_nf_old(Nullifier::dummy(&mut rng)), &mut rng, ); @@ -652,7 +652,7 @@ mod tests { recipient, NoteValue::from_raw(note2_value), note2_asset, - Nullifier::dummy(&mut rng), + Rho::from_nf_old(Nullifier::dummy(&mut rng)), &mut rng, ); @@ -679,7 +679,7 @@ mod tests { recipient, NoteValue::from_raw(note1_value), identity_point(), - Nullifier::dummy(&mut rng), + Rho::from_nf_old(Nullifier::dummy(&mut rng)), &mut rng, ); @@ -687,7 +687,7 @@ mod tests { recipient, NoteValue::from_raw(note2_value), identity_point(), - Nullifier::dummy(&mut rng), + Rho::from_nf_old(Nullifier::dummy(&mut rng)), &mut rng, ); @@ -963,7 +963,7 @@ mod tests { recipient, NoteValue::from_raw(5), AssetBase::derive(bundle.ik(), "zsa_asset"), - Nullifier::dummy(&mut rng), + Rho::from_nf_old(Nullifier::dummy(&mut rng)), &mut rng, ); bundle.actions.first_mut().notes.push(note); @@ -1226,7 +1226,7 @@ mod tests { recipient, NoteValue::from_raw(5), AssetBase::derive(signed.ik(), "zsa_asset"), - Nullifier::dummy(&mut rng), + Rho::from_nf_old(Nullifier::dummy(&mut rng)), &mut rng, ); @@ -1267,7 +1267,7 @@ mod tests { recipient, NoteValue::from_raw(55), AssetBase::derive(&incorrect_ik, asset_description), - Nullifier::dummy(&mut rng), + Rho::from_nf_old(Nullifier::dummy(&mut rng)), &mut rng, ); diff --git a/src/keys.rs b/src/keys.rs index 418c57413..3c76a73f6 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -1,6 +1,5 @@ //! Key structures for Orchard. -use core::mem; use std::{ fmt::{Debug, Formatter}, io::{self, Read, Write}, @@ -36,11 +35,15 @@ use crate::{ PreparedNonIdentityBase, PreparedNonZeroScalar, PrfExpand, }, zip32::{ - self, ChildIndex, ExtendedSpendingKey, ZIP32_ORCHARD_PERSONALIZATION, + self, ExtendedSpendingKey, ZIP32_ORCHARD_PERSONALIZATION, ZIP32_ORCHARD_PERSONALIZATION_FOR_ISSUANCE, }, }; +// Preserve '::' which specifies the EXTERNAL 'zip32' crate +#[rustfmt::skip] +pub use ::zip32::{AccountId, ChildIndex, DiversifierIndex, Scope}; + const KDF_ORCHARD_PERSONALIZATION: &[u8; 16] = b"Zcash_OrchardKDF"; const ZIP32_PURPOSE: u32 = 32; const ZIP32_PURPOSE_FOR_ISSUANCE: u32 = 227; @@ -106,13 +109,17 @@ impl SpendingKey { pub fn from_zip32_seed( seed: &[u8], coin_type: u32, - account: u32, + account: AccountId, ) -> Result { + if coin_type >= (1 << 31) { + return Err(zip32::Error::InvalidChildIndex(coin_type)); + } + // Call zip32 logic let path = &[ - ChildIndex::try_from(ZIP32_PURPOSE)?, - ChildIndex::try_from(coin_type)?, - ChildIndex::try_from(account)?, + ChildIndex::hardened(ZIP32_PURPOSE), + ChildIndex::hardened(coin_type), + ChildIndex::hardened(account.into()), ]; ExtendedSpendingKey::from_path(seed, path, ZIP32_ORCHARD_PERSONALIZATION) .map(|esk| esk.sk()) @@ -131,7 +138,7 @@ pub struct SpendAuthorizingKey(redpallas::SigningKey); impl SpendAuthorizingKey { /// Derives ask from sk. Internal use only, does not enforce all constraints. fn derive_inner(sk: &SpendingKey) -> pallas::Scalar { - to_scalar(PrfExpand::OrchardAsk.expand(&sk.0)) + to_scalar(PrfExpand::ORCHARD_ASK.with(&sk.0)) } /// Randomizes this spend authorizing key with the given `randomizer`. @@ -271,9 +278,9 @@ impl IssuanceAuthorizingKey { ) -> Result { // Call zip32 logic let path = &[ - ChildIndex::try_from(ZIP32_PURPOSE_FOR_ISSUANCE)?, - ChildIndex::try_from(coin_type)?, - ChildIndex::try_from(account)?, + ChildIndex::hardened(ZIP32_PURPOSE_FOR_ISSUANCE), + ChildIndex::hardened(coin_type), + ChildIndex::hardened(account), ]; // we are reusing zip32 logic for deriving the key, zip32 should be updated as discussed @@ -364,7 +371,7 @@ impl NullifierDerivingKey { impl From<&SpendingKey> for NullifierDerivingKey { fn from(sk: &SpendingKey) -> Self { - NullifierDerivingKey(to_base(PrfExpand::OrchardNk.expand(&sk.0))) + NullifierDerivingKey(to_base(PrfExpand::ORCHARD_NK.with(&sk.0))) } } @@ -399,7 +406,7 @@ pub(crate) struct CommitIvkRandomness(pallas::Scalar); impl From<&SpendingKey> for CommitIvkRandomness { fn from(sk: &SpendingKey) -> Self { - CommitIvkRandomness(to_scalar(PrfExpand::OrchardRivk.expand(&sk.0))) + CommitIvkRandomness(to_scalar(PrfExpand::ORCHARD_RIVK.with(&sk.0))) } } @@ -424,24 +431,6 @@ impl CommitIvkRandomness { } } -/// The scope of a viewing key or address. -/// -/// A "scope" narrows the visibility or usage to a level below "full". -/// -/// Consistent usage of `Scope` enables the user to provide consistent views over a wallet -/// to other people. For example, a user can give an external [`IncomingViewingKey`] to a -/// merchant terminal, enabling it to only detect "real" transactions from customers and -/// not internal transactions from the wallet. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Scope { - /// A scope used for wallet-external operations, namely deriving addresses to give to - /// other users in order to receive funds. - External, - /// A scope used for wallet-internal operations, such as creating change notes, - /// auto-shielding, and note management. - Internal, -} - /// A key that provides the capability to view incoming and outgoing transactions. /// /// This key is useful anywhere you need to maintain accurate balance, but do not want the @@ -493,7 +482,7 @@ impl FullViewingKey { let ak = self.ak.to_bytes(); let nk = self.nk.to_bytes(); CommitIvkRandomness(to_scalar( - PrfExpand::OrchardRivkInternal.with_ad_slices(&k, &[&ak, &nk]), + PrfExpand::ORCHARD_RIVK_INTERNAL.with(&k, &ak, &nk), )) } } @@ -505,7 +494,7 @@ impl FullViewingKey { fn derive_dk_ovk(&self) -> (DiversifierKey, OutgoingViewingKey) { let k = self.rivk.0.to_repr(); let b = [(&self.ak.0).into(), self.nk.0.to_repr()]; - let r = PrfExpand::OrchardDkOvk.with_ad_slices(&k, &[&b[0][..], &b[1][..]]); + let r = PrfExpand::ORCHARD_DK_OVK.with(&k, &b[0], &b[1]); ( DiversifierKey(r[..32].try_into().unwrap()), OutgoingViewingKey(r[32..].try_into().unwrap()), @@ -623,44 +612,15 @@ impl FullViewingKey { #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub(crate) struct DiversifierKey([u8; 32]); -/// The index for a particular diversifier. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct DiversifierIndex([u8; 11]); - -macro_rules! di_from { - ($n:ident) => { - impl From<$n> for DiversifierIndex { - fn from(j: $n) -> Self { - let mut j_bytes = [0; 11]; - j_bytes[..mem::size_of::<$n>()].copy_from_slice(&j.to_le_bytes()); - DiversifierIndex(j_bytes) - } - } - }; -} -di_from!(u32); -di_from!(u64); -di_from!(usize); - -impl From<[u8; 11]> for DiversifierIndex { - fn from(j_bytes: [u8; 11]) -> Self { - DiversifierIndex(j_bytes) - } -} - -impl DiversifierIndex { - /// Returns the raw bytes of the diversifier index. - pub fn to_bytes(&self) -> &[u8; 11] { - &self.0 - } -} - impl DiversifierKey { /// Returns the diversifier at the given index. pub fn get(&self, j: impl Into) -> Diversifier { let ff = FF1::::new(&self.0, 2).expect("valid radix"); let enc = ff - .encrypt(&[], &BinaryNumeralString::from_bytes_le(&j.into().0[..])) + .encrypt( + &[], + &BinaryNumeralString::from_bytes_le(j.into().as_bytes()), + ) .unwrap(); Diversifier(enc.to_bytes_le().try_into().unwrap()) } @@ -1168,7 +1128,7 @@ mod tests { }; use crate::note::AssetBase; use crate::{ - note::{ExtractedNoteCommitment, Nullifier, RandomSeed}, + note::{ExtractedNoteCommitment, RandomSeed, Rho}, value::NoteValue, Note, }; @@ -1268,7 +1228,7 @@ mod tests { let addr = fvk.address(diversifier, Scope::External); assert_eq!(&addr.pk_d().to_bytes(), &tv.default_pk_d); - let rho = Nullifier::from_bytes(&tv.note_rho).unwrap(); + let rho = Rho::from_bytes(&tv.note_rho).unwrap(); let note = Note::from_parts( addr, NoteValue::from_raw(tv.note_v), diff --git a/src/lib.rs b/src/lib.rs index 417116df2..65e4a5835 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,5 +41,6 @@ pub use action::Action; pub use address::Address; pub use bundle::Bundle; pub use circuit::Proof; +pub use constants::MERKLE_DEPTH_ORCHARD as NOTE_COMMITMENT_TREE_DEPTH; pub use note::Note; pub use tree::Anchor; diff --git a/src/note.rs b/src/note.rs index ca2e83e11..f5b8d30a0 100644 --- a/src/note.rs +++ b/src/note.rs @@ -1,6 +1,8 @@ //! Data structures used for note construction. use core::fmt; +use memuse::DynamicUsage; +use ff::PrimeField; use group::GroupEncoding; use pasta_curves::pallas; use rand::RngCore; @@ -19,6 +21,44 @@ pub use self::commitment::{ExtractedNoteCommitment, NoteCommitment}; pub(crate) mod nullifier; pub use self::nullifier::Nullifier; +/// The randomness used to construct a note. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Rho(pallas::Base); + +// We know that `pallas::Base` doesn't allocate internally. +memuse::impl_no_dynamic_usage!(Rho); + +impl Rho { + /// Deserialize the rho value from a byte array. + /// + /// This should only be used in cases where the components of a `Note` are being serialized and + /// stored individually. Use [`Action::rho`] or [`CompactAction::rho`] to obtain the [`Rho`] + /// value otherwise. + /// + /// [`Action::rho`]: crate::action::Action::rho + /// [`CompactAction::rho`]: crate::note_encryption_v3::CompactAction::rho + pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { + pallas::Base::from_repr(*bytes).map(Rho) + } + + /// Serialize the rho value to its canonical byte representation. + pub fn to_bytes(self) -> [u8; 32] { + self.0.to_repr() + } + + /// Constructs the [`Rho`] value to be used to construct a new note from the revealed nullifier + /// of the note being spent in the [`Action`] under construction. + /// + /// [`Action`]: crate::action::Action + pub(crate) fn from_nf_old(nf: Nullifier) -> Self { + Rho(nf.0) + } + + pub(crate) fn into_inner(self) -> pallas::Base { + self.0 + } +} + pub(crate) mod asset_base; pub use self::asset_base::AssetBase; @@ -27,7 +67,7 @@ pub use self::asset_base::AssetBase; pub struct RandomSeed([u8; 32]); impl RandomSeed { - pub(crate) fn random(rng: &mut impl RngCore, rho: &Nullifier) -> Self { + pub(crate) fn random(rng: &mut impl RngCore, rho: &Rho) -> Self { loop { let mut bytes = [0; 32]; rng.fill_bytes(&mut bytes); @@ -38,10 +78,10 @@ impl RandomSeed { } } - /// Reads a note's random seed from bytes, given the note's nullifier. + /// Reads a note's random seed from bytes, given the note's rho value. /// - /// Returns `None` if the nullifier is not for the same note as the seed. - pub fn from_bytes(rseed: [u8; 32], rho: &Nullifier) -> CtOption { + /// Returns `None` if the rho value is not for the same note as the seed. + pub fn from_bytes(rseed: [u8; 32], rho: &Rho) -> CtOption { let rseed = RandomSeed(rseed); let esk = rseed.esk_inner(rho); CtOption::new(rseed, esk.is_some()) @@ -55,23 +95,23 @@ impl RandomSeed { /// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend]. /// /// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend - pub(crate) fn psi(&self, rho: &Nullifier) -> pallas::Base { - to_base(PrfExpand::Psi.with_ad(&self.0, &rho.to_bytes()[..])) + pub(crate) fn psi(&self, rho: &Rho) -> pallas::Base { + to_base(PrfExpand::PSI.with(&self.0, &rho.to_bytes())) } /// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend]. /// /// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend - fn esk_inner(&self, rho: &Nullifier) -> CtOption { + fn esk_inner(&self, rho: &Rho) -> CtOption { NonZeroPallasScalar::from_scalar(to_scalar( - PrfExpand::Esk.with_ad(&self.0, &rho.to_bytes()[..]), + PrfExpand::ORCHARD_ESK.with(&self.0, &rho.to_bytes()), )) } /// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend]. /// /// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend - fn esk(&self, rho: &Nullifier) -> NonZeroPallasScalar { + fn esk(&self, rho: &Rho) -> NonZeroPallasScalar { // We can't construct a RandomSeed for which this unwrap fails. self.esk_inner(rho).unwrap() } @@ -79,9 +119,9 @@ impl RandomSeed { /// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend]. /// /// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend - pub(crate) fn rcm(&self, rho: &Nullifier) -> commitment::NoteCommitTrapdoor { + pub(crate) fn rcm(&self, rho: &Rho) -> commitment::NoteCommitTrapdoor { commitment::NoteCommitTrapdoor(to_scalar( - PrfExpand::Rcm.with_ad(&self.0, &rho.to_bytes()[..]), + PrfExpand::ORCHARD_RCM.with(&self.0, &rho.to_bytes()), )) } } @@ -108,11 +148,11 @@ pub struct Note { asset: AssetBase, /// A unique creation ID for this note. /// - /// This is set to the nullifier of the note that was spent in the [`Action`] that - /// created this note. + /// This is produced from the nullifier of the note that will be spent in the [`Action`] that + /// creates this note. /// /// [`Action`]: crate::action::Action - rho: Nullifier, + rho: Rho, /// The seed randomness for various note components. rseed: RandomSeed, /// The seed randomness for split notes. @@ -150,7 +190,7 @@ impl Note { recipient: Address, value: NoteValue, asset: AssetBase, - rho: Nullifier, + rho: Rho, rseed: RandomSeed, ) -> CtOption { let note = Note { @@ -173,7 +213,7 @@ impl Note { recipient: Address, value: NoteValue, asset: AssetBase, - rho: Nullifier, + rho: Rho, mut rng: impl RngCore, ) -> Self { loop { @@ -197,7 +237,7 @@ impl Note { /// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes pub(crate) fn dummy( rng: &mut impl RngCore, - rho: Option, + rho: Option, asset: AssetBase, ) -> (SpendingKey, FullViewingKey, Self) { let sk = SpendingKey::random(rng); @@ -208,7 +248,7 @@ impl Note { recipient, NoteValue::zero(), asset, - rho.unwrap_or_else(|| Nullifier::dummy(rng)), + rho.unwrap_or_else(|| Rho::from_nf_old(Nullifier::dummy(rng))), rng, ); @@ -246,7 +286,7 @@ impl Note { } /// Returns rho of this note. - pub fn rho(&self) -> Nullifier { + pub fn rho(&self) -> Rho { self.rho } @@ -343,7 +383,7 @@ pub mod testing { use subtle::CtOption; - use super::{Note, RandomSeed}; + use super::{Note, RandomSeed, Rho}; prop_compose! { /// Generate an arbitrary random seed @@ -356,7 +396,7 @@ pub mod testing { /// Generate an arbitrary note pub fn arb_note(value: NoteValue)( recipient in arb_address(), - rho in arb_nullifier(), + rho in arb_nullifier().prop_map(Rho::from_nf_old), rseed in arb_rseed(), asset in arb_asset_base(), ) -> Note { @@ -376,7 +416,7 @@ pub mod testing { pub fn arb_native_note()( recipient in arb_address(), value in arb_note_value(), - rho in arb_nullifier(), + rho in arb_nullifier().prop_map(Rho::from_nf_old), rseed in arb_rseed(), ) -> Note { Note { @@ -395,7 +435,7 @@ pub mod testing { pub fn arb_zsa_note(asset: AssetBase)( recipient in arb_address(), value in arb_note_value(), - rho in arb_nullifier(), + rho in arb_nullifier().prop_map(Rho::from_nf_old), rseed in arb_rseed(), ) -> Note { Note { diff --git a/src/note/nullifier.rs b/src/note/nullifier.rs index 51769fdbf..6c41a87a2 100644 --- a/src/note/nullifier.rs +++ b/src/note/nullifier.rs @@ -3,7 +3,7 @@ use halo2_proofs::arithmetic::CurveExt; use memuse::DynamicUsage; use pasta_curves::pallas; use rand::RngCore; -use subtle::{Choice, ConditionallySelectable, CtOption}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use super::NoteCommitment; use crate::{ @@ -70,6 +70,12 @@ impl Nullifier { } } +impl ConstantTimeEq for Nullifier { + fn ct_eq(&self, other: &Self) -> subtle::Choice { + self.0.ct_eq(&other.0) + } +} + /// Generators for property testing. #[cfg(any(test, feature = "test-dependencies"))] #[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))] diff --git a/src/note_encryption.rs b/src/note_encryption.rs index 6063f9c49..2ac88014c 100644 --- a/src/note_encryption.rs +++ b/src/note_encryption.rs @@ -15,7 +15,7 @@ use crate::{ DiversifiedTransmissionKey, Diversifier, EphemeralPublicKey, EphemeralSecretKey, OutgoingViewingKey, PreparedEphemeralPublicKey, PreparedIncomingViewingKey, SharedSecret, }, - note::{ExtractedNoteCommitment, Nullifier, RandomSeed}, + note::{ExtractedNoteCommitment, Nullifier, RandomSeed, Rho}, value::{NoteValue, ValueCommitment}, Address, Note, }; @@ -172,7 +172,7 @@ where /// Orchard-specific note encryption logic. #[derive(Debug)] pub struct OrchardDomainV2 { - rho: Nullifier, + rho: Rho, } impl memuse::DynamicUsage for OrchardDomainV2 { @@ -188,14 +188,12 @@ impl memuse::DynamicUsage for OrchardDomainV2 { impl OrchardDomainV2 { /// Constructs a domain that can be used to trial-decrypt this action's output note. pub fn for_action(act: &Action) -> Self { - OrchardDomainV2 { - rho: *act.nullifier(), - } + Self { rho: act.rho() } } - /// Constructs a domain from a nullifier. - pub fn for_nullifier(nullifier: Nullifier) -> Self { - OrchardDomainV2 { rho: nullifier } + /// Constructs a domain that can be used to trial-decrypt this action's output note. + pub fn for_compact_action(act: &CompactAction) -> Self { + Self { rho: act.rho() } } } @@ -375,6 +373,7 @@ impl ShieldedOutput for Action { } /// A compact Action for light clients. +#[derive(Clone)] pub struct CompactAction { nullifier: Nullifier, cmx: ExtractedNoteCommitment, @@ -440,10 +439,74 @@ impl CompactAction { } } - ///Returns the nullifier of the note being spent. + /// Returns the nullifier of the note being spent. pub fn nullifier(&self) -> Nullifier { self.nullifier } + + /// Returns the commitment to the new note being created. + pub fn cmx(&self) -> ExtractedNoteCommitment { + self.cmx + } + + /// Obtains the [`Rho`] value that was used to construct the new note being created. + pub fn rho(&self) -> Rho { + Rho::from_nf_old(self.nullifier) + } +} + +/// Utilities for constructing test data. +#[cfg(feature = "test-dependencies")] +pub mod testing { + use rand::RngCore; + use zcash_note_encryption::Domain; + + use crate::{ + keys::OutgoingViewingKey, + note::{ExtractedNoteCommitment, Nullifier, RandomSeed, Rho}, + value::NoteValue, + Address, Note, + }; + + use super::{CompactAction, OrchardDomain, OrchardNoteEncryption}; + + /// Creates a fake `CompactAction` paying the given recipient the specified value. + /// + /// Returns the `CompactAction` and the new note. + pub fn fake_compact_action( + rng: &mut R, + nf_old: Nullifier, + recipient: Address, + value: NoteValue, + ovk: Option, + ) -> (CompactAction, Note) { + let rho = Rho::from_nf_old(nf_old); + let rseed = { + loop { + let mut bytes = [0; 32]; + rng.fill_bytes(&mut bytes); + let rseed = RandomSeed::from_bytes(bytes, &rho); + if rseed.is_some().into() { + break rseed.unwrap(); + } + } + }; + let note = Note::from_parts(recipient, value, rho, rseed).unwrap(); + let encryptor = OrchardNoteEncryption::new(ovk, note, [0u8; 512]); + let cmx = ExtractedNoteCommitment::from(note.commitment()); + let ephemeral_key = OrchardDomain::epk_bytes(encryptor.epk()); + let enc_ciphertext = encryptor.encrypt_note_plaintext(); + + ( + CompactAction { + nullifier: nf_old, + cmx, + ephemeral_key, + enc_ciphertext: enc_ciphertext.as_ref()[..52].try_into().unwrap(), + }, + note, + ) + } } #[cfg(test)] @@ -464,7 +527,7 @@ mod tests { OutgoingViewingKey, PreparedIncomingViewingKey, }, note::{ - testing::arb_native_note, ExtractedNoteCommitment, Nullifier, RandomSeed, + testing::arb_native_note, ExtractedNoteCommitment, Nullifier, RandomSeed, Rho, TransmittedNoteCiphertext, }, note_encryption::{ @@ -524,7 +587,8 @@ mod tests { // Received Action let cv_net = ValueCommitment::from_bytes(&tv.cv_net).unwrap(); - let rho = Nullifier::from_bytes(&tv.rho).unwrap(); + let nf_old = Nullifier::from_bytes(&tv.nf_old).unwrap(); + let rho = Rho::from_nf_old(nf_old); let cmx = ExtractedNoteCommitment::from_bytes(&tv.cmx).unwrap(); let esk = EphemeralSecretKey::from_bytes(&tv.esk).unwrap(); @@ -552,8 +616,8 @@ mod tests { assert_eq!(ExtractedNoteCommitment::from(note.commitment()), cmx); let action = Action::from_parts( - // rho is the nullifier in the receiving Action. - rho, + // nf_old is the nullifier revealed by the receiving Action. + nf_old, // We don't need a valid rk for this test. redpallas::VerificationKey::dummy(), cmx, diff --git a/src/note_encryption_v3.rs b/src/note_encryption_v3.rs index ceff0cbe2..8fb68dbd0 100644 --- a/src/note_encryption_v3.rs +++ b/src/note_encryption_v3.rs @@ -15,7 +15,7 @@ use crate::{ DiversifiedTransmissionKey, Diversifier, EphemeralPublicKey, EphemeralSecretKey, OutgoingViewingKey, PreparedEphemeralPublicKey, PreparedIncomingViewingKey, SharedSecret, }, - note::{ExtractedNoteCommitment, Nullifier, RandomSeed}, + note::{ExtractedNoteCommitment, Nullifier, RandomSeed, Rho}, value::{NoteValue, ValueCommitment}, Address, Note, }; @@ -75,6 +75,18 @@ impl AsRef<[u8]> for NoteCiphertextBytes { } } +impl AsMut<[u8]> for NoteCiphertextBytes { + fn as_mut(&mut self) -> &mut [u8] { + self.0.as_mut() + } +} + +impl From<(&[u8], &[u8])> for NoteCiphertextBytes { + fn from(s: (&[u8], &[u8])) -> Self { + Self([s.0, s.1].concat().try_into().unwrap()) + } +} + impl From<&[u8]> for NoteCiphertextBytes { fn from(s: &[u8]) -> Self where @@ -177,20 +189,18 @@ where /// Orchard-specific note encryption logic. #[derive(Debug)] pub struct OrchardDomainV3 { - rho: Nullifier, + rho: Rho, } impl OrchardDomainV3 { /// Constructs a domain that can be used to trial-decrypt this action's output note. pub fn for_action(act: &Action) -> Self { - OrchardDomainV3 { - rho: *act.nullifier(), - } + Self { rho: act.rho() } } - /// Constructs a domain from a nullifier. - pub fn for_nullifier(nullifier: Nullifier) -> Self { - OrchardDomainV3 { rho: nullifier } + /// Constructs a domain that can be used to trial-decrypt this action's output note. + pub fn for_compact_action(act: &CompactAction) -> Self { + Self { rho: act.rho() } } } @@ -443,6 +453,11 @@ impl CompactAction { pub fn nullifier(&self) -> Nullifier { self.nullifier } + + /// Obtains the [`Rho`] value that was used to construct the new note being created. + pub fn rho(&self) -> Rho { + Rho::from_nf_old(self.nullifier) + } } #[cfg(test)] @@ -465,7 +480,7 @@ mod tests { OutgoingViewingKey, PreparedIncomingViewingKey, }, note::{ - testing::arb_note, AssetBase, ExtractedNoteCommitment, Nullifier, RandomSeed, + testing::arb_note, AssetBase, ExtractedNoteCommitment, Nullifier, RandomSeed, Rho, TransmittedNoteCiphertext, }, primitives::redpallas, @@ -522,7 +537,8 @@ mod tests { // Received Action let cv_net = ValueCommitment::from_bytes(&tv.cv_net).unwrap(); - let rho = Nullifier::from_bytes(&tv.rho).unwrap(); + let nf_old = Nullifier::from_bytes(&tv.rho).unwrap(); + let rho = Rho::from_nf_old(nf_old); let cmx = ExtractedNoteCommitment::from_bytes(&tv.cmx).unwrap(); let esk = EphemeralSecretKey::from_bytes(&tv.esk).unwrap(); @@ -554,7 +570,7 @@ mod tests { let action = Action::from_parts( // rho is the nullifier in the receiving Action. - rho, + nf_old, // We don't need a valid rk for this test. redpallas::VerificationKey::dummy(), cmx, diff --git a/src/spec.rs b/src/spec.rs index 698b7a394..7f0224bff 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -16,8 +16,7 @@ use crate::constants::{ KEY_DIVERSIFICATION_PERSONALIZATION, L_ORCHARD_BASE, }; -mod prf_expand; -pub(crate) use prf_expand::PrfExpand; +pub(crate) use zcash_spec::PrfExpand; /// A Pallas point that is guaranteed to not be the identity. #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/src/test_vectors/note_encryption.rs b/src/test_vectors/note_encryption.rs index 7a5a73a56..b4a1f3137 100644 --- a/src/test_vectors/note_encryption.rs +++ b/src/test_vectors/note_encryption.rs @@ -8,7 +8,7 @@ pub(crate) struct TestVector { pub(crate) rseed: [u8; 32], pub(crate) memo: [u8; 512], pub(crate) cv_net: [u8; 32], - pub(crate) rho: [u8; 32], + pub(crate) nf_old: [u8; 32], pub(crate) cmx: [u8; 32], pub(crate) esk: [u8; 32], pub(crate) ephemeral_key: [u8; 32], @@ -95,10 +95,11 @@ pub(crate) fn test_vectors() -> Vec { 0x42, 0xc2, 0x38, 0x51, 0x38, 0x15, 0x30, 0x2d, 0xf0, 0xf4, 0x83, 0x04, 0x21, 0xa6, 0xc1, 0x3e, 0x71, 0x01, ], - rho: [ - 0xca, 0x1f, 0xeb, 0x30, 0xca, 0x11, 0x17, 0x76, 0xc0, 0x41, 0x74, 0x66, 0xbd, 0x69, - 0xb3, 0xd2, 0x13, 0x88, 0x2e, 0xef, 0x55, 0xe6, 0x0b, 0x6d, 0x9e, 0x2a, 0x98, 0xe7, - 0x05, 0xee, 0xf3, 0x27, + // FIXME: used instead of rho, which was possibly modified in zsa1 version + nf_old: [ + 0xc5, 0x96, 0xfb, 0xd3, 0x2e, 0xbb, 0xcb, 0xad, 0xae, 0x60, 0xd2, 0x85, 0xc7, 0xd7, + 0x5f, 0xa8, 0x36, 0xf9, 0xd2, 0xfa, 0x86, 0x10, 0x0a, 0xb8, 0x58, 0xea, 0x2d, 0xe1, + 0xf1, 0x1c, 0x83, 0x06, ], cmx: [ 0x23, 0x75, 0x7c, 0x51, 0x58, 0x21, 0xcb, 0xc1, 0x84, 0x3c, 0x9a, 0x45, 0x7b, 0x7e, @@ -304,10 +305,11 @@ pub(crate) fn test_vectors() -> Vec { 0xa2, 0x17, 0x64, 0x10, 0x4a, 0x23, 0xea, 0xf6, 0xba, 0x49, 0x6c, 0xb9, 0xb8, 0xe8, 0x25, 0x7a, 0xd8, 0xb3, ], - rho: [ - 0xc1, 0xe1, 0x59, 0x5b, 0x8d, 0xe7, 0x55, 0x97, 0x66, 0xe5, 0xa6, 0x72, 0x5f, 0x5b, - 0xe5, 0x74, 0x2f, 0x43, 0xbf, 0x40, 0x62, 0x3b, 0x71, 0x49, 0xca, 0xe2, 0x67, 0x5c, - 0x4d, 0xb2, 0xc7, 0x31, + // FIXME: used instead of rho, which was possibly modified in zsa1 version + nf_old: [ + 0x33, 0x88, 0xda, 0x05, 0x06, 0xda, 0x9e, 0xa2, 0xd5, 0x16, 0x73, 0x9b, 0x95, 0x1c, + 0x7c, 0xc0, 0x58, 0x53, 0x36, 0xb4, 0x4d, 0xf9, 0xb3, 0xb5, 0x0e, 0x48, 0x93, 0xe4, + 0xb1, 0x84, 0x92, 0x11, ], cmx: [ 0x59, 0xb6, 0xf3, 0xd4, 0x03, 0x22, 0x3d, 0x6c, 0xe4, 0x3d, 0xed, 0xae, 0xe2, 0x35, @@ -513,10 +515,11 @@ pub(crate) fn test_vectors() -> Vec { 0x9d, 0x5e, 0x64, 0x07, 0x19, 0xbc, 0xa5, 0xc8, 0xed, 0x49, 0x99, 0x97, 0x34, 0xe6, 0xc5, 0xb3, 0x73, 0x3e, ], - rho: [ - 0xc8, 0x8d, 0x00, 0x84, 0x84, 0xc5, 0xd7, 0x98, 0x20, 0xab, 0x68, 0xc6, 0x7d, 0x08, - 0x36, 0x72, 0xb0, 0x7f, 0x72, 0x7d, 0x44, 0xd0, 0xcd, 0x14, 0x73, 0x88, 0x00, 0xf8, - 0x25, 0xb9, 0xff, 0x16, + // FIXME: used instead of rho, which was possibly modified in zsa1 version + nf_old: [ + 0xbe, 0xf8, 0xcf, 0x16, 0x98, 0xe4, 0x78, 0x47, 0xd3, 0x8e, 0x1a, 0xaa, 0x88, 0x86, + 0x10, 0x77, 0xcd, 0xb5, 0xad, 0x4c, 0xf6, 0x6f, 0xe4, 0x2f, 0xd6, 0x52, 0x57, 0x81, + 0xb6, 0xd3, 0x4f, 0x1e, ], cmx: [ 0x0b, 0x74, 0x59, 0x61, 0x6f, 0xc6, 0x93, 0x95, 0xe6, 0x44, 0x36, 0xcf, 0x4a, 0xe9, @@ -722,10 +725,11 @@ pub(crate) fn test_vectors() -> Vec { 0xfa, 0xde, 0x94, 0xfa, 0x0b, 0x46, 0xe3, 0xb1, 0xa5, 0x73, 0x34, 0x99, 0x34, 0xe2, 0x32, 0xb5, 0x0e, 0x96, ], - rho: [ - 0xa9, 0x0a, 0x9b, 0x8a, 0xb1, 0x35, 0x9d, 0xc9, 0x6b, 0xda, 0xe9, 0x0e, 0x52, 0x74, - 0x78, 0x8c, 0xb0, 0xc4, 0x26, 0xef, 0xf2, 0x60, 0x43, 0x61, 0x85, 0x39, 0x8b, 0xff, - 0xf5, 0x0e, 0x92, 0x37, + // FIXME: used instead of rho, which was possibly modified in zsa1 version + nf_old: [ + 0x18, 0x89, 0x8e, 0x75, 0x21, 0x7b, 0x32, 0x9b, 0x3a, 0x56, 0x7b, 0x09, 0x37, 0x89, + 0xa4, 0xd8, 0x19, 0xcd, 0xb0, 0x34, 0x88, 0xb8, 0x10, 0xda, 0x22, 0x0c, 0x3f, 0x59, + 0xba, 0x03, 0x39, 0x26, ], cmx: [ 0x05, 0xb5, 0xe3, 0x20, 0x76, 0xda, 0xe0, 0x94, 0x83, 0x35, 0xac, 0x3d, 0x65, 0x1c, @@ -931,10 +935,11 @@ pub(crate) fn test_vectors() -> Vec { 0x72, 0x95, 0x89, 0xbe, 0x69, 0xd8, 0x28, 0xbe, 0x54, 0x30, 0x69, 0x16, 0x41, 0x3c, 0xd2, 0x50, 0x21, 0x17, ], - rho: [ - 0x8d, 0x67, 0xe3, 0xba, 0x4d, 0xbc, 0x9d, 0xa5, 0xe8, 0x38, 0x23, 0xa1, 0x23, 0x11, - 0x63, 0x96, 0x51, 0xa4, 0xff, 0xa9, 0x5f, 0x27, 0xc1, 0x83, 0x0d, 0x91, 0xd8, 0xb7, - 0x3c, 0xfb, 0xf1, 0x31, + // FIXME: used instead of rho, which was possibly modified in zsa1 version + nf_old: [ + 0x23, 0x39, 0xa8, 0x95, 0x29, 0xcf, 0x35, 0x7b, 0x06, 0x7d, 0xd2, 0x8b, 0xe4, 0x06, + 0x6e, 0x16, 0x23, 0x6d, 0xc5, 0xd7, 0x87, 0x06, 0x14, 0x9a, 0x72, 0x8c, 0x3e, 0x3d, + 0x9d, 0xc1, 0x08, 0x1c, ], cmx: [ 0xea, 0x7c, 0x13, 0xf7, 0xe1, 0x20, 0x5e, 0x78, 0xc8, 0xce, 0x4e, 0xe4, 0xfd, 0xcd, @@ -1140,10 +1145,11 @@ pub(crate) fn test_vectors() -> Vec { 0x6e, 0x62, 0xe4, 0xed, 0xc7, 0x86, 0xd9, 0xe0, 0xb2, 0x7d, 0x26, 0x62, 0x8b, 0x79, 0xda, 0x6b, 0x15, 0x14, ], - rho: [ - 0x9a, 0x09, 0xe4, 0x72, 0xe8, 0xe9, 0x96, 0xfc, 0xc3, 0x0e, 0xd5, 0x23, 0x72, 0x08, - 0xdb, 0xb0, 0x01, 0x71, 0x32, 0x0e, 0x6b, 0xea, 0x43, 0x91, 0x86, 0x00, 0x9d, 0xad, - 0x21, 0x38, 0xab, 0x29, + // FIXME: used instead of rho, which was possibly modified in zsa1 version + nf_old: [ + 0xe5, 0xd0, 0x8c, 0x40, 0x26, 0x3e, 0x4a, 0x2a, 0x56, 0x96, 0xda, 0x21, 0x0d, 0x8e, + 0x9a, 0x77, 0xf0, 0xaf, 0xc4, 0xc6, 0x8a, 0x6d, 0xda, 0x38, 0xe2, 0x85, 0xf4, 0xe3, + 0xef, 0x13, 0xb8, 0x17, ], cmx: [ 0x18, 0xfc, 0xbd, 0x40, 0xac, 0xf1, 0xa7, 0xf4, 0xd6, 0x09, 0x87, 0x9a, 0x5f, 0x5e, @@ -1349,10 +1355,11 @@ pub(crate) fn test_vectors() -> Vec { 0x0a, 0x12, 0x01, 0x2d, 0x63, 0xc0, 0x09, 0xc6, 0x77, 0x44, 0xba, 0xe0, 0xd5, 0x83, 0x88, 0xff, 0xee, 0x2f, ], - rho: [ - 0x54, 0x3e, 0xa7, 0x11, 0x56, 0xc9, 0xa6, 0xf8, 0x04, 0x1f, 0xa7, 0x7e, 0xc1, 0xc5, - 0xaf, 0x90, 0x28, 0x8f, 0x27, 0x20, 0xf1, 0x3f, 0xf0, 0x93, 0xc6, 0x86, 0x26, 0x6b, - 0x92, 0xd7, 0xa0, 0x24, + // FIXME: used instead of rho, which was possibly modified in zsa1 version + nf_old: [ + 0xe9, 0x16, 0x93, 0xc3, 0x7d, 0x04, 0x37, 0x5e, 0x67, 0xc5, 0x71, 0x5a, 0x39, 0xc9, + 0x79, 0x4a, 0x4e, 0xcd, 0x08, 0x38, 0xe2, 0x35, 0x1f, 0xd7, 0xcd, 0x93, 0xa1, 0x55, + 0x7f, 0x01, 0x02, 0x3e, ], cmx: [ 0x1d, 0x51, 0xea, 0x92, 0xfa, 0x43, 0x55, 0x0a, 0x0e, 0xdd, 0xea, 0x23, 0x6e, 0x17, @@ -1558,10 +1565,11 @@ pub(crate) fn test_vectors() -> Vec { 0xcd, 0x51, 0x00, 0x89, 0x08, 0xa6, 0xcd, 0xd0, 0xaa, 0x02, 0x1b, 0x88, 0x8b, 0x98, 0xe2, 0x3c, 0x39, 0x11, ], - rho: [ - 0xbd, 0xda, 0xe8, 0xdf, 0xf1, 0x20, 0x5e, 0x04, 0x96, 0x8f, 0xae, 0x1f, 0xd9, 0xbe, - 0x51, 0xd8, 0x25, 0xf5, 0xd8, 0x78, 0x1d, 0x93, 0x3d, 0x0f, 0x5b, 0xce, 0x9c, 0xa8, - 0x3e, 0xe8, 0xed, 0x20, + // FIXME: used instead of rho, which was possibly modified in zsa1 version + nf_old: [ + 0x35, 0x6f, 0xc7, 0x2e, 0x1b, 0xf1, 0xe3, 0xa2, 0xa5, 0x9a, 0xa9, 0xe4, 0x75, 0x15, + 0x5c, 0xf7, 0x43, 0xf8, 0xfd, 0xf0, 0xd1, 0x5b, 0x4c, 0xc4, 0x02, 0x60, 0xd0, 0xd0, + 0x9a, 0xda, 0x04, 0x08, ], cmx: [ 0xbe, 0x43, 0xee, 0x84, 0x70, 0x70, 0x75, 0xac, 0x48, 0x08, 0xd0, 0x97, 0x54, 0x07, @@ -1767,10 +1775,11 @@ pub(crate) fn test_vectors() -> Vec { 0x7e, 0xed, 0xd2, 0xa7, 0x06, 0x44, 0x07, 0x34, 0x78, 0x41, 0x01, 0xae, 0x2d, 0x8e, 0x87, 0xe5, 0x05, 0xad, ], - rho: [ - 0xc2, 0x79, 0xfa, 0x9d, 0x1c, 0x84, 0x11, 0x93, 0xd3, 0x32, 0xf8, 0xcc, 0xf4, 0xd0, - 0xb1, 0xe4, 0x56, 0x01, 0xa8, 0xaf, 0x66, 0x76, 0xd7, 0x62, 0xfb, 0xa7, 0x31, 0x33, - 0x45, 0x89, 0x35, 0x14, + // FIXME: used instead of rho, which was possibly modified in zsa1 version + nf_old: [ + 0x32, 0x91, 0x87, 0x35, 0x66, 0x3f, 0x34, 0xad, 0xa0, 0x22, 0x8a, 0xea, 0x4a, 0xcc, + 0x19, 0x2a, 0x12, 0x3f, 0xcf, 0xa0, 0x60, 0x46, 0x89, 0xf9, 0x1a, 0xcb, 0xe9, 0x38, + 0x31, 0xe4, 0x8c, 0x0c, ], cmx: [ 0x6d, 0x29, 0x97, 0xd1, 0xce, 0x0a, 0x94, 0x9a, 0x63, 0x70, 0x0f, 0x46, 0x1b, 0x57, @@ -1976,10 +1985,11 @@ pub(crate) fn test_vectors() -> Vec { 0xbd, 0xb9, 0x6f, 0x1c, 0xe0, 0x57, 0xc3, 0x30, 0xd1, 0xcc, 0xba, 0x2f, 0x7d, 0xa8, 0x71, 0x55, 0x00, 0xb5, ], - rho: [ - 0xea, 0x38, 0x44, 0x75, 0x9a, 0x9a, 0x1c, 0xc5, 0x28, 0xb2, 0x95, 0xce, 0x70, 0x13, - 0x7a, 0x85, 0xf9, 0xf0, 0x8e, 0x41, 0xa5, 0xc7, 0xc1, 0xca, 0xc1, 0x55, 0xa6, 0x69, - 0xa3, 0x18, 0x53, 0x3e, + // FIXME: used instead of rho, which was possibly modified in zsa1 version + nf_old: [ + 0x3b, 0x37, 0x96, 0x78, 0x0c, 0x0a, 0xec, 0x14, 0xed, 0x28, 0x74, 0xb5, 0x23, 0x06, + 0xe1, 0xc3, 0xd5, 0xde, 0x45, 0x93, 0xc6, 0x69, 0xaf, 0x1c, 0xaf, 0x11, 0xbc, 0xb4, + 0xd3, 0x5c, 0x60, 0x12, ], cmx: [ 0x6a, 0xba, 0x28, 0x10, 0x5b, 0xc0, 0x72, 0xc5, 0x2a, 0xb8, 0xa3, 0x14, 0x79, 0x7f, diff --git a/src/tree.rs b/src/tree.rs index 4f4b5c3cb..a10de6ea5 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -58,12 +58,19 @@ impl From for Anchor { } impl Anchor { + /// The anchor of the empty Orchard note commitment tree. + /// + /// This anchor does not correspond to any valid anchor for a spend, so it + /// may only be used for coinbase bundles or in circumstances where Orchard + /// functionality is not active. + pub fn empty_tree() -> Anchor { + Anchor(MerkleHashOrchard::empty_root(Level::from(MERKLE_DEPTH_ORCHARD as u8)).0) + } + pub(crate) fn inner(&self) -> pallas::Base { self.0 } -} -impl Anchor { /// Parses an Orchard anchor from a byte encoding. pub fn from_bytes(bytes: [u8; 32]) -> CtOption { pallas::Base::from_repr(bytes).map(Anchor) @@ -253,6 +260,31 @@ impl<'de> Deserialize<'de> for MerkleHashOrchard { } } +/// Test utilities available under the `test-dependencies` feature flag. +#[cfg(feature = "test-dependencies")] +pub mod testing { + use ff::Field; + use rand::{ + distributions::{Distribution, Standard}, + RngCore, + }; + + use super::MerkleHashOrchard; + + impl MerkleHashOrchard { + /// Return a random fake `MerkleHashOrchard`. + pub fn random(rng: &mut impl RngCore) -> Self { + Standard.sample(rng) + } + } + + impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> MerkleHashOrchard { + MerkleHashOrchard(pasta_curves::Fp::random(rng)) + } + } +} + #[cfg(test)] mod tests { use { diff --git a/src/value.rs b/src/value.rs index ae3862726..b156770d2 100644 --- a/src/value.rs +++ b/src/value.rs @@ -16,7 +16,7 @@ //! - Define your `valueBalanceOrchard` type to enforce your valid value range. This can //! be checked in its `TryFrom` implementation. //! - Define your own "amount" type for note values, and convert it to `NoteValue` prior -//! to calling [`Builder::add_recipient`]. +//! to calling [`Builder::add_output`]. //! //! Inside the circuit, note values are constrained to be unsigned 64-bit integers. //! @@ -34,7 +34,7 @@ //! [`Bundle`]: crate::bundle::Bundle //! [`Bundle::value_balance`]: crate::bundle::Bundle::value_balance //! [`Builder::value_balance`]: crate::builder::Builder::value_balance -//! [`Builder::add_recipient`]: crate::builder::Builder::add_recipient +//! [`Builder::add_output`]: crate::builder::Builder::add_output //! [Rust documentation]: https://doc.rust-lang.org/stable/std/primitive.i64.html use core::fmt::{self, Debug}; @@ -85,7 +85,7 @@ impl fmt::Display for OverflowError { impl std::error::Error for OverflowError {} /// The non-negative value of an individual Orchard note. -#[derive(Clone, Copy, Debug, Default)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct NoteValue(u64); impl NoteValue { diff --git a/src/zip32.rs b/src/zip32.rs index 12427bf8d..2a036ec9b 100644 --- a/src/zip32.rs +++ b/src/zip32.rs @@ -3,13 +3,16 @@ use core::fmt; use blake2b_simd::Params as Blake2bParams; -use subtle::{Choice, ConstantTimeEq}; +use subtle::{Choice, ConstantTimeEq, CtOption}; +use zip32::ChainCode; use crate::{ keys::{FullViewingKey, SpendingKey}, spec::PrfExpand, }; +pub use zip32::ChildIndex; + const ZIP32_ORCHARD_FVFP_PERSONALIZATION: &[u8; 16] = b"ZcashOrchardFVFP"; /// Personalization for the master extended spending key pub const ZIP32_ORCHARD_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Orchard"; @@ -67,27 +70,55 @@ impl FvkTag { } } -/// A hardened child index for a derived key. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct ChildIndex(u32); +/// The derivation index associated with a key. +/// +/// Master keys are never derived via the ZIP 32 child derivation process, but they have +/// an index in their encoding. This type allows the encoding to be represented, while +/// also enabling the derivation methods to only accept [`ChildIndex`]. +#[derive(Clone, Copy, Debug)] +struct KeyIndex(CtOption); + +impl ConstantTimeEq for KeyIndex { + fn ct_eq(&self, other: &Self) -> Choice { + // We use a `CtOption` above instead of an enum so that we can implement this. + self.0.ct_eq(&other.0) + } +} + +impl PartialEq for KeyIndex { + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).into() + } +} + +impl Eq for KeyIndex {} -impl TryFrom for ChildIndex { - type Error = Error; +impl KeyIndex { + fn master() -> Self { + Self(CtOption::new(ChildIndex::hardened(0), 0.into())) + } + + fn child(i: ChildIndex) -> Self { + Self(CtOption::new(i, 1.into())) + } - /// `index` must be less than 2^31 - fn try_from(index: u32) -> Result { - if index < (1 << 31) { - Ok(Self(index + (1 << 31))) + fn new(depth: u8, i: u32) -> Option { + match (depth == 0, i) { + (true, 0) => Some(KeyIndex::master()), + (false, _) => ChildIndex::from_index(i).map(KeyIndex::child), + _ => None, + } + } + + fn index(&self) -> u32 { + if self.0.is_some().into() { + self.0.unwrap().index() } else { - Err(Error::InvalidChildIndex(32)) + 0 } } } -/// The chain code forming the second half of an Orchard extended key. -#[derive(Debug, Copy, Clone, PartialEq)] -struct ChainCode([u8; 32]); - /// An Orchard extended spending key. /// /// Defined in [ZIP32: Orchard extended keys][orchardextendedkeys]. @@ -97,7 +128,7 @@ struct ChainCode([u8; 32]); pub(crate) struct ExtendedSpendingKey { depth: u8, parent_fvk_tag: FvkTag, - child_index: ChildIndex, + child_index: KeyIndex, chain_code: ChainCode, sk: SpendingKey, } @@ -106,8 +137,8 @@ impl ConstantTimeEq for ExtendedSpendingKey { fn ct_eq(&self, rhs: &Self) -> Choice { self.depth.ct_eq(&rhs.depth) & self.parent_fvk_tag.0.ct_eq(&rhs.parent_fvk_tag.0) - & self.child_index.0.ct_eq(&rhs.child_index.0) - & self.chain_code.0.ct_eq(&rhs.chain_code.0) + & self.child_index.ct_eq(&rhs.child_index) + & self.chain_code.ct_eq(&rhs.chain_code) & self.sk.ct_eq(&rhs.sk) } } @@ -160,13 +191,13 @@ impl ExtendedSpendingKey { let sk_m = sk_m.unwrap(); // I_R is used as the master chain code c_m. - let c_m = ChainCode(I[32..].try_into().unwrap()); + let c_m = ChainCode::new(I[32..].try_into().unwrap()); // For the master extended spending key, depth is 0, parent_fvk_tag is 4 zero bytes, and i is 0. Ok(Self { depth: 0, parent_fvk_tag: FvkTag([0; 4]), - child_index: ChildIndex(0), + child_index: KeyIndex::master(), chain_code: c_m, sk: sk_m, }) @@ -181,9 +212,10 @@ impl ExtendedSpendingKey { /// Discards index if it results in an invalid sk fn derive_child(&self, index: ChildIndex) -> Result { // I := PRF^Expand(c_par, [0x81] || sk_par || I2LEOSP(i)) - let I: [u8; 64] = PrfExpand::OrchardZip32Child.with_ad_slices( - &self.chain_code.0, - &[self.sk.to_bytes(), &index.0.to_le_bytes()], + let I: [u8; 64] = PrfExpand::ORCHARD_ZIP32_CHILD.with( + self.chain_code.as_bytes(), + self.sk.to_bytes(), + &index.index().to_le_bytes(), ); // I_L is used as the child spending key sk_i. @@ -194,14 +226,14 @@ impl ExtendedSpendingKey { let sk_i = sk_i.unwrap(); // I_R is used as the child chain code c_i. - let c_i = ChainCode(I[32..].try_into().unwrap()); + let c_i = ChainCode::new(I[32..].try_into().unwrap()); let fvk: FullViewingKey = self.into(); Ok(Self { depth: self.depth + 1, parent_fvk_tag: FvkFingerprint::from(&fvk).tag(), - child_index: index, + child_index: KeyIndex::child(index), chain_code: c_i, sk: sk_i, }) @@ -222,8 +254,8 @@ mod tests { let seed = [0; 32]; let xsk_m = ExtendedSpendingKey::master(&seed, ZIP32_ORCHARD_PERSONALIZATION).unwrap(); - let i_5 = 5; - let xsk_5 = xsk_m.derive_child(i_5.try_into().unwrap()); + let i_5 = ChildIndex::hardened(5); + let xsk_5 = xsk_m.derive_child(i_5); assert!(xsk_5.is_ok()); } @@ -233,22 +265,22 @@ mod tests { let seed = [0; 32]; let xsk_m = ExtendedSpendingKey::master(&seed, ZIP32_ORCHARD_PERSONALIZATION).unwrap(); - let xsk_5h = xsk_m.derive_child(5.try_into().unwrap()).unwrap(); + let xsk_5h = xsk_m.derive_child(ChildIndex::hardened(5)).unwrap(); assert!(bool::from( ExtendedSpendingKey::from_path( &seed, - &[5.try_into().unwrap()], + &[ChildIndex::hardened(5)], ZIP32_ORCHARD_PERSONALIZATION ) .unwrap() .ct_eq(&xsk_5h) )); - let xsk_5h_7 = xsk_5h.derive_child(7.try_into().unwrap()).unwrap(); + let xsk_5h_7 = xsk_5h.derive_child(ChildIndex::hardened(7)).unwrap(); assert!(bool::from( ExtendedSpendingKey::from_path( &seed, - &[5.try_into().unwrap(), 7.try_into().unwrap()], + &[ChildIndex::hardened(5), ChildIndex::hardened(7)], ZIP32_ORCHARD_PERSONALIZATION ) .unwrap() diff --git a/tests/builder.rs b/tests/builder.rs index 903ab3eca..845fa2367 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -1,7 +1,7 @@ use bridgetree::BridgeTree; use incrementalmerkletree::Hashable; use orchard::{ - builder::Builder, + builder::{Builder, BundleType}, bundle::{Authorized, Flags}, circuit::{ProvingKey, VerifyingKey}, keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendAuthorizingKey, SpendingKey}, @@ -62,18 +62,32 @@ fn bundle_chain() { // Use the empty tree. let anchor = MerkleHashOrchard::empty_root(32.into()).into(); - let mut builder = Builder::new(Flags::from_parts(false, true, false), anchor); + let mut builder = Builder::new( + BundleType::Transactional { + flags: Flags::SPENDS_DISABLED, + bundle_required: false, + }, + anchor, + ); + let note_value = NoteValue::from_raw(5000); assert_eq!( - builder.add_recipient( - None, - recipient, - NoteValue::from_raw(5000), - AssetBase::native(), - None - ), + builder.add_output(None, recipient, note_value, AssetBase::native(), None), Ok(()) ); - let unauthorized = builder.build(&mut rng).unwrap(); + let (unauthorized, bundle_meta) = builder.build(&mut rng).unwrap().unwrap(); + + assert_eq!( + unauthorized + .decrypt_output_with_key( + bundle_meta + .output_action_index(0) + .expect("Output 0 can be found"), + &fvk.to_ivk(Scope::External) + ) + .map(|(note, _, _)| note.value()), + Some(note_value) + ); + let sighash = unauthorized.commitment().into(); let proven = unauthorized.create_proof(&pk, &mut rng).unwrap(); proven.apply_signatures(rng, sighash, &[]).unwrap() @@ -96,10 +110,10 @@ fn bundle_chain() { let (merkle_path, anchor) = build_merkle_path(¬e); - let mut builder = Builder::new(Flags::from_parts(true, true, false), anchor); + let mut builder = Builder::new(BundleType::DEFAULT_VANILLA, anchor); assert_eq!(builder.add_spend(fvk, note, merkle_path), Ok(())); assert_eq!( - builder.add_recipient( + builder.add_output( None, recipient, NoteValue::from_raw(5000), @@ -108,7 +122,7 @@ fn bundle_chain() { ), Ok(()) ); - let unauthorized = builder.build(&mut rng).unwrap(); + let (unauthorized, _) = builder.build(&mut rng).unwrap().unwrap(); let sighash = unauthorized.commitment().into(); let proven = unauthorized.create_proof(&pk, &mut rng).unwrap(); proven diff --git a/tests/zsa.rs b/tests/zsa.rs index 40b75f80c..4d6826b08 100644 --- a/tests/zsa.rs +++ b/tests/zsa.rs @@ -10,8 +10,7 @@ use orchard::note::{AssetBase, ExtractedNoteCommitment}; use orchard::note_encryption_v3::OrchardDomainV3; use orchard::tree::{MerkleHashOrchard, MerklePath}; use orchard::{ - builder::Builder, - bundle::Flags, + builder::{Builder, BundleType}, circuit::{ProvingKey, VerifyingKey}, keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendAuthorizingKey, SpendingKey}, value::NoteValue, @@ -86,7 +85,7 @@ fn build_and_sign_bundle( pk: &ProvingKey, sk: &SpendingKey, ) -> Bundle { - let unauthorized = builder.build(&mut rng).unwrap(); + let unauthorized = builder.build(&mut rng).unwrap().unwrap().0; let sighash = unauthorized.commitment().into(); let proven = unauthorized.create_proof(pk, &mut rng).unwrap(); proven @@ -184,9 +183,9 @@ fn create_native_note(keys: &Keychain) -> Note { // Use the empty tree. let anchor = MerkleHashOrchard::empty_root(32.into()).into(); - let mut builder = Builder::new(Flags::from_parts(false, true, false), anchor); + let mut builder = Builder::new(BundleType::Coinbase, anchor); assert_eq!( - builder.add_recipient( + builder.add_output( None, keys.recipient, NoteValue::from_raw(100), @@ -195,7 +194,7 @@ fn create_native_note(keys: &Keychain) -> Note { ), Ok(()) ); - let unauthorized = builder.build(&mut rng).unwrap(); + let unauthorized = builder.build(&mut rng).unwrap().unwrap().0; let sighash = unauthorized.commitment().into(); let proven = unauthorized.create_proof(keys.pk(), &mut rng).unwrap(); proven.apply_signatures(rng, sighash, &[]).unwrap() @@ -239,7 +238,7 @@ fn build_and_verify_bundle( ) -> Result<(), String> { let rng = OsRng; let shielded_bundle: Bundle<_, i64> = { - let mut builder = Builder::new(Flags::from_parts(true, true, true), anchor); + let mut builder = Builder::new(BundleType::DEFAULT_ZSA, anchor); spends .iter() @@ -250,7 +249,7 @@ fn build_and_verify_bundle( outputs .iter() .try_for_each(|output| { - builder.add_recipient(None, keys.recipient, output.value, output.asset, None) + builder.add_output(None, keys.recipient, output.value, output.asset, None) }) .map_err(|err| err.to_string())?; assets_to_burn