diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14c13b13b..e3da9b415 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Run tests - run: cargo test --verbose + run: cargo test --workspace --verbose - name: Verify working directory is clean run: git diff --exit-code @@ -32,7 +32,7 @@ jobs: - name: Remove lockfile to build with latest dependencies run: rm Cargo.lock - name: Build crate - run: cargo build --all-features --verbose + run: cargo build --workspace --all-features --verbose - name: Verify working directory is clean (excluding lockfile) run: git diff --exit-code ':!Cargo.lock' @@ -42,7 +42,7 @@ jobs: strategy: matrix: target: - - wasm32-wasi + - wasm32-wasip1 - thumbv7em-none-eabihf steps: - uses: actions/checkout@v4 @@ -67,9 +67,9 @@ jobs: - name: Add lazy_static with the spin_no_std feature working-directory: ./ci-build run: cargo add lazy_static --no-default-features --features "spin_no_std" - - name: Add typenum with the no_std feature + - name: Add typenum with no default features working-directory: ./ci-build - run: cargo add typenum --no-default-features --features "no_std" + run: cargo add typenum --no-default-features - name: Show Cargo.toml for the synthetic crate working-directory: ./ci-build run: cat Cargo.toml @@ -89,7 +89,7 @@ jobs: - uses: actions/checkout@v4 # Build benchmarks to prevent bitrot - name: Build benchmarks - run: cargo build --benches + run: cargo build --workspace --benches book: name: Book tests @@ -113,7 +113,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Generate coverage report - run: cargo tarpaulin --engine llvm --all-features --release --timeout 600 --out xml + run: cargo tarpaulin --workspace --engine llvm --all-features --release --timeout 600 --out xml - name: Upload coverage to Codecov uses: codecov/codecov-action@v3.1.4 @@ -126,7 +126,7 @@ jobs: # Requires #![deny(rustdoc::broken_intra_doc_links)] in crates. - run: sudo apt-get -y install libfontconfig1-dev - name: Check intra-doc links - run: cargo doc --all-features --document-private-items + run: cargo doc --workspace --all-features --document-private-items fmt: name: Rustfmt diff --git a/.github/workflows/lints-stable.yml b/.github/workflows/lints-stable.yml index 3ea0ac8a2..3877879d6 100644 --- a/.github/workflows/lints-stable.yml +++ b/.github/workflows/lints-stable.yml @@ -23,7 +23,7 @@ jobs: strategy: matrix: target: - - wasm32-wasi + - wasm32-wasip1 steps: - uses: actions/checkout@v4 with: @@ -47,9 +47,9 @@ jobs: - name: Add lazy_static with the spin_no_std feature working-directory: ./ci-build run: cargo add lazy_static --no-default-features --features "spin_no_std" - - name: Add typenum with the no_std feature + - name: Add typenum with no default features working-directory: ./ci-build - run: cargo add typenum --no-default-features --features "no_std" + run: cargo add typenum --no-default-features - name: Show Cargo.toml for the synthetic crate working-directory: ./ci-build run: cat Cargo.toml diff --git a/CHANGELOG.md b/CHANGELOG.md index 96126a262..214657654 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,11 @@ and this project adheres to Rust's notion of ## [Unreleased] ### Changed +- MSRV is now 1.85.1 +- Migrated from yanked `core2` library to `corez` - `orchard::pczt::Bundle::extract` now takes its `self` argument by reference instead of by value. +- `orchard::zip32::Error` has added variant `MaxDerivationDepth` ## [0.12.0] - 2025-12-05 diff --git a/Cargo.lock b/Cargo.lock index 3e7f607b4..5dc68d402 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -422,13 +422,10 @@ dependencies = [ ] [[package]] -name = "core2" -version = "0.3.3" +name = "corez" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239fa3ae9b63c2dc74bd3fa852d4792b8b305ae64eeede946265b6af62f1fff3" -dependencies = [ - "memchr", -] +checksum = "4df6f98652d30167eaeea34d77b730e07c8caba6df17bd4551842b9b8da01deb" [[package]] name = "cpp_demangle" @@ -1439,11 +1436,22 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "orchard" version = "0.12.0" +dependencies = [ + "incrementalmerkletree", + "orchard_internal", + "rand", + "shardtree", + "zcash_note_encryption", +] + +[[package]] +name = "orchard_internal" +version = "0.12.0" dependencies = [ "aes", "bitvec", "blake2b_simd", - "core2", + "corez", "criterion", "ff", "fpe", diff --git a/Cargo.toml b/Cargo.toml index 9f962c8fd..31ab4da09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,20 +1,25 @@ +[workspace] +members = [".", "public"] + +[workspace.dependencies] +__impl = { version = "=0.12.0", path = ".", package = "orchard_internal", default-features = false } + [package] -name = "orchard" +name = "orchard_internal" version = "0.12.0" authors = [ - "Sean Bowe", - "Jack Grigg ", + "Sean Bowe ", + "Jack Grigg ", "Daira-Emma Hopwood ", "Ying Tong Lai", - "Kris Nuttycombe ", + "Kris Nuttycombe ", ] edition = "2021" -rust-version = "1.70" +rust-version = "1.85.1" description = "The Orchard shielded transaction protocol" license = "MIT OR Apache-2.0" repository = "https://github.com/zcash/orchard" -documentation = "https://docs.rs/orchard" -readme = "README.md" +readme = "INTERNAL-README.md" categories = ["cryptography::cryptocurrencies"] keywords = ["zcash"] @@ -59,7 +64,7 @@ getset = "0.1" tracing = { version = "0.1", default-features = false } # No-std support -core2 = { version = "0.3", default-features = false, features = ["alloc"] } +corez = { version = "0.1.1", default-features = false, features = ["alloc"] } # Developer tooling dependencies image = { version = "0.24", optional = true } @@ -83,7 +88,7 @@ bench = false [features] default = ["circuit", "multicore", "std"] -std = ["core2/std", "group/wnaf-memuse", "reddsa/std"] +std = ["corez/std", "group/wnaf-memuse", "reddsa/std"] circuit = ["dep:halo2_gadgets", "dep:halo2_proofs", "std"] unstable-frost = [] multicore = ["halo2_proofs?/multicore"] diff --git a/INTERNAL-README.md b/INTERNAL-README.md new file mode 100644 index 000000000..32da28f87 --- /dev/null +++ b/INTERNAL-README.md @@ -0,0 +1,3 @@ +# orchard_internal + +This is the implementation crate for the Orchard shielded transaction protocol. It is not intended to be used directly. Depend on the [`orchard`](https://crates.io/crates/orchard) crate instead, which re-exports the public API from this crate. diff --git a/LICENSE-MIT b/LICENSE-MIT index 3a617be2b..b369e0615 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,6 +1,7 @@ The MIT License (MIT) -Copyright (c) 2020-2023 The Electric Coin Company +Copyright (c) 2020-2025 The Electric Coin Company +Copyright (c) 2026 Zcash Open Development Lab Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 5eb787f7c..eca52b606 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # orchard [![Crates.io](https://img.shields.io/crates/v/orchard.svg)](https://crates.io/crates/orchard) # -Requires Rust 1.66+. +Requires Rust 1.85.1+. ## Documentation @@ -10,13 +10,9 @@ Requires Rust 1.66+. ## `no_std` compatibility In order to take advantage of `no_std` builds, downstream users of this crate -must enable: - -* the `spin_no_std` feature of the `lazy_static` crate; and -* the `no_std` feature of the `typenum` crate. - -This is needed because the `--no-default-features` builds of these crates still -rely on `std`. +must enable the `spin_no_std` feature of the `lazy_static` crate. This is +needed because the `--no-default-features` build of `lazy_static` still relies +on `std`. ## License diff --git a/benches/circuit.rs b/benches/circuit.rs index 22f43eb51..79ef9e9a2 100644 --- a/benches/circuit.rs +++ b/benches/circuit.rs @@ -6,7 +6,7 @@ use criterion::{BenchmarkId, Criterion}; #[cfg(unix)] use pprof::criterion::{Output, PProfProfiler}; -use orchard::{ +use orchard_internal::{ builder::{Builder, BundleType}, circuit::{ProvingKey, VerifyingKey}, keys::{FullViewingKey, Scope, SpendingKey}, diff --git a/benches/note_decryption.rs b/benches/note_decryption.rs index 920ccbffd..519336b6a 100644 --- a/benches/note_decryption.rs +++ b/benches/note_decryption.rs @@ -1,5 +1,5 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; -use orchard::{ +use orchard_internal::{ builder::{Builder, BundleType}, circuit::ProvingKey, keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendingKey}, diff --git a/benches/small.rs b/benches/small.rs index daeec5cbe..14b8d9ce1 100644 --- a/benches/small.rs +++ b/benches/small.rs @@ -1,5 +1,5 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use orchard::keys::{FullViewingKey, Scope, SpendingKey}; +use orchard_internal::keys::{FullViewingKey, Scope, SpendingKey}; fn key_derivation(c: &mut Criterion) { // Meaningless random spending key. diff --git a/public/Cargo.toml b/public/Cargo.toml new file mode 100644 index 000000000..174a42669 --- /dev/null +++ b/public/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "orchard" +version = "0.12.0" +authors = [ + "Sean Bowe ", + "Jack Grigg ", + "Daira-Emma Hopwood ", + "Ying Tong Lai", + "Kris Nuttycombe ", +] +edition = "2021" +rust-version = "1.70" +description = "The Orchard shielded transaction protocol" +license = "MIT OR Apache-2.0" +repository = "https://github.com/zcash/orchard" +documentation = "https://docs.rs/orchard" +readme = "README.md" +categories = ["cryptography::cryptocurrencies"] +keywords = ["zcash"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs", "--html-in-header", "katex-header.html"] + +[dependencies] +__impl = { workspace = true } + +[features] +default = ["circuit", "multicore", "std"] +std = ["__impl/std"] +circuit = ["__impl/circuit"] +multicore = ["__impl/multicore"] +unstable-frost = ["__impl/unstable-frost"] +dev-graph = ["__impl/dev-graph"] +test-dependencies = ["__impl/test-dependencies"] + +[dev-dependencies] +incrementalmerkletree = "0.8.1" +rand = "0.8" +shardtree = "0.6" +zcash_note_encryption = "0.4" diff --git a/public/LICENSE-APACHE b/public/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/public/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/public/LICENSE-MIT b/public/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/public/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/public/README.md b/public/README.md new file mode 120000 index 000000000..32d46ee88 --- /dev/null +++ b/public/README.md @@ -0,0 +1 @@ +../README.md \ No newline at end of file diff --git a/public/katex-header.html b/public/katex-header.html new file mode 120000 index 000000000..810e68dcb --- /dev/null +++ b/public/katex-header.html @@ -0,0 +1 @@ +../katex-header.html \ No newline at end of file diff --git a/public/src/lib.rs b/public/src/lib.rs new file mode 100644 index 000000000..081e156b4 --- /dev/null +++ b/public/src/lib.rs @@ -0,0 +1,253 @@ +//! # orchard +//! +//! ## Nomenclature +//! +//! All types in the `orchard` crate, unless otherwise specified, are Orchard-specific +//! types. For example, [`Address`] is documented as being a shielded payment address; we +//! implicitly mean it is an Orchard payment address (as opposed to e.g. a Sapling payment +//! address, which is also shielded). + +#![no_std] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![deny(rustdoc::broken_intra_doc_links)] +#![deny(missing_debug_implementations)] +#![deny(missing_docs)] +#![forbid(unsafe_code)] + +// Root-level re-exports. +#[doc(inline)] +pub use __impl::Action; +#[doc(inline)] +pub use __impl::Address; +#[doc(inline)] +pub use __impl::Anchor; +#[doc(inline)] +pub use __impl::Bundle; +#[doc(inline)] +pub use __impl::Note; +#[doc(inline)] +pub use __impl::Proof; +#[doc(inline)] +pub use __impl::NOTE_COMMITMENT_TREE_DEPTH; + +/// Logic for building Orchard components of transactions. +pub mod builder { + #[doc(inline)] + pub use __impl::builder::{ + BuildError, Builder, BundleMetadata, BundleType, InProgress, InProgressSignatures, + InputView, MaybeSigned, OutputError, OutputInfo, OutputView, PartiallyAuthorized, + SigningMetadata, SigningParts, SpendError, SpendInfo, Unauthorized, + }; + + #[cfg(feature = "circuit")] + #[doc(inline)] + pub use __impl::builder::{bundle, UnauthorizedBundle, Unproven}; + + /// Generators for property testing. + #[cfg(all(feature = "circuit", feature = "test-dependencies"))] + #[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))] + pub mod testing { + #[doc(inline)] + pub use __impl::builder::testing::{arb_bundle, arb_bundle_with_key}; + } +} + +/// Structs related to bundles of Orchard actions. +pub mod bundle { + #[doc(inline)] + pub use __impl::bundle::{ + Authorization, Authorized, Bundle, BundleAuthorizingCommitment, BundleCommitment, + EffectsOnly, Flags, + }; + + #[cfg(feature = "circuit")] + #[doc(inline)] + pub use __impl::bundle::BatchValidator; + + /// Utility functions for computing bundle commitments + pub mod commitments { + #[doc(inline)] + pub use __impl::bundle::commitments::{hash_bundle_auth_empty, hash_bundle_txid_empty}; + } + + /// Generators for property testing. + #[cfg(feature = "test-dependencies")] + #[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))] + pub mod testing { + #[doc(inline)] + pub use __impl::bundle::testing::{ + arb_action, arb_action_n, arb_bundle, arb_flags, arb_unauthorized_action, + arb_unauthorized_action_n, arb_unauthorized_bundle, Unauthorized, + }; + } +} + +/// The Orchard Action circuit implementation. +#[cfg(feature = "circuit")] +#[cfg_attr(docsrs, doc(cfg(feature = "circuit")))] +pub mod circuit { + #[doc(inline)] + pub use __impl::circuit::{Circuit, Config, Instance, Proof, ProvingKey, VerifyingKey}; + + /// Gadgets used in the Orchard circuit. + pub mod gadget {} +} + +/// Key structures for Orchard. +pub mod keys { + #[doc(inline)] + pub use __impl::keys::{ + DiversifiedTransmissionKey, Diversifier, DiversifierIndex, EphemeralPublicKey, + EphemeralSecretKey, FullViewingKey, IncomingViewingKey, OutgoingViewingKey, + PreparedEphemeralPublicKey, PreparedIncomingViewingKey, Scope, SharedSecret, + SpendAuthorizingKey, SpendValidatingKey, SpendingKey, + }; + + /// Generators for property testing. + #[cfg(feature = "test-dependencies")] + #[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))] + pub mod testing { + #[doc(inline)] + pub use __impl::keys::testing::{arb_diversifier_index, arb_esk, arb_spending_key}; + } +} + +/// Data structures used for note construction. +pub mod note { + #[doc(inline)] + pub use __impl::note::{ + ExtractedNoteCommitment, Note, NoteCommitment, Nullifier, RandomSeed, Rho, + TransmittedNoteCiphertext, + }; + + /// Generators for property testing. + #[cfg(feature = "test-dependencies")] + #[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))] + pub mod testing { + #[doc(inline)] + pub use __impl::note::testing::arb_note; + } +} + +/// In-band secret distribution for Orchard bundles. +pub mod note_encryption { + #[doc(inline)] + pub use __impl::note_encryption::{CompactAction, OrchardDomain, OrchardNoteEncryption}; + + /// Utilities for constructing test data. + #[cfg(feature = "test-dependencies")] + #[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))] + pub mod testing { + #[doc(inline)] + pub use __impl::note_encryption::testing::fake_compact_action; + } +} + +/// PCZT support for Orchard. +pub mod pczt { + #[doc(inline)] + pub use __impl::pczt::{ + Action, ActionUpdater, Bundle, IoFinalizerError, Output, ParseError, SignerError, Spend, + TxExtractorError, Unbound, Updater, UpdaterError, VerifyError, Zip32Derivation, + }; + + #[cfg(feature = "circuit")] + #[doc(inline)] + pub use __impl::pczt::ProverError; +} + +/// Primitives used in the Orchard protocol. +pub mod primitives { + /// A minimal RedPallas implementation for use in Zcash. + pub mod redpallas { + #[doc(inline)] + pub use __impl::primitives::redpallas::{ + Binding, SigType, Signature, SigningKey, SpendAuth, VerificationKey, + }; + + #[cfg(feature = "std")] + #[doc(inline)] + pub use __impl::primitives::redpallas::batch; + + /// Generators for property testing. + #[cfg(feature = "test-dependencies")] + #[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))] + pub mod testing { + #[doc(inline)] + pub use __impl::primitives::redpallas::testing::{ + arb_binding_signing_key, arb_binding_verification_key, arb_spendauth_signing_key, + arb_spendauth_verification_key, + }; + } + } +} + +/// Types related to Orchard note commitment trees and anchors. +pub mod tree { + #[doc(inline)] + pub use __impl::tree::{Anchor, MerkleHashOrchard, MerklePath}; + + /// Test utilities available under the `test-dependencies` feature flag. + #[cfg(feature = "test-dependencies")] + #[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))] + pub mod testing {} +} + +/// Monetary values within the Orchard shielded pool. +/// +/// Values are represented in three places within the Orchard protocol: +/// - [`NoteValue`](value::NoteValue), the value of an individual note. It is an unsigned +/// 64-bit integer (with maximum value [`MAX_NOTE_VALUE`](value::MAX_NOTE_VALUE)), and is +/// serialized in a note plaintext. +/// - [`ValueSum`](value::ValueSum), the sum of note values within an Orchard [`Action`] or +/// [`Bundle`]. It is a signed 64-bit integer (with range +/// [`VALUE_SUM_RANGE`](value::VALUE_SUM_RANGE)). +/// - `valueBalanceOrchard`, which is a signed 63-bit integer. This is represented +/// by a user-defined type parameter on [`Bundle`], returned by +/// [`Bundle::value_balance`] and [`Builder::value_balance`](builder::Builder::value_balance). +/// +/// If your specific instantiation of the Orchard protocol requires a smaller bound on +/// valid note values (for example, Zcash's `MAX_MONEY` fits into a 51-bit integer), you +/// should enforce this in two ways: +/// +/// - 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_output`](builder::Builder::add_output). +/// +/// # Caution! +/// +/// An `i64` is _not_ a signed 64-bit integer! The [Rust documentation] calls `i64` the +/// 64-bit signed integer type, which is true in the sense that its encoding in memory +/// takes up 64 bits. Numerically, however, `i64` is a signed 63-bit integer. +/// +/// Fortunately, users of this crate should never need to construct +/// [`ValueSum`](value::ValueSum) directly; you should only need to interact with +/// [`NoteValue`](value::NoteValue) (which can be safely constructed from a `u64`) and +/// `valueBalanceOrchard` (which can be represented as an `i64`). +/// +/// [Rust documentation]: https://doc.rust-lang.org/stable/std/primitive.i64.html +pub mod value { + #[doc(inline)] + pub use __impl::value::{ + BalanceError, NoteValue, Sign, ValueCommitTrapdoor, ValueCommitment, ValueSum, + MAX_NOTE_VALUE, VALUE_SUM_RANGE, + }; + + /// Generators for property testing. + #[cfg(feature = "test-dependencies")] + #[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))] + pub mod testing { + #[doc(inline)] + pub use __impl::value::testing::{ + arb_note_value, arb_note_value_bounded, arb_positive_note_value, arb_scalar, + arb_trapdoor, arb_value_sum, arb_value_sum_bounded, + }; + } +} + +/// Key structures for Orchard. +pub mod zip32 { + #[doc(inline)] + pub use __impl::zip32::{ChildIndex, Error}; +} diff --git a/tests/builder.rs b/public/tests/builder.rs similarity index 100% rename from tests/builder.rs rename to public/tests/builder.rs diff --git a/rust-toolchain.toml b/rust-toolchain.toml index cad9254a9..0542ef5eb 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.70.0" +channel = "1.85.1" components = [ "clippy", "rustfmt" ] diff --git a/src/action.rs b/src/action.rs index 04bcc2089..b951167f2 100644 --- a/src/action.rs +++ b/src/action.rs @@ -121,7 +121,7 @@ impl DynamicUsage for Action> { /// Generators for property testing. #[cfg(any(test, feature = "test-dependencies"))] #[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))] -pub(crate) mod testing { +pub mod testing { use rand::{rngs::StdRng, SeedableRng}; use reddsa::orchard::SpendAuth; diff --git a/src/address.rs b/src/address.rs index 6b643ad35..5c5dfdfae 100644 --- a/src/address.rs +++ b/src/address.rs @@ -10,6 +10,7 @@ use crate::{ /// # Examples /// /// ``` +/// # use orchard_internal as orchard; /// use orchard::keys::{SpendingKey, FullViewingKey, Scope}; /// /// let sk = SpendingKey::from_bytes([7; 32]).unwrap(); @@ -22,7 +23,8 @@ pub struct Address { } impl Address { - pub(crate) fn from_parts(d: Diversifier, pk_d: DiversifiedTransmissionKey) -> Self { + /// Constructs an [`Address`] from a diversifier and a diversified transmission key. + pub fn from_parts(d: Diversifier, pk_d: DiversifiedTransmissionKey) -> Self { // We assume here that pk_d is correctly-derived from d. We ensure this for // internal APIs. For parsing from raw byte encodings, we assume that users aren't // modifying internals of encoded address formats. If they do, that can result in @@ -35,11 +37,13 @@ impl Address { self.d } - pub(crate) fn g_d(&self) -> NonIdentityPallasPoint { + /// Returns the diversified base point for this address. + pub fn g_d(&self) -> NonIdentityPallasPoint { diversify_hash(self.d.as_array()) } - pub(crate) fn pk_d(&self) -> &DiversifiedTransmissionKey { + /// Returns the diversified transmission key for this address. + pub fn pk_d(&self) -> &DiversifiedTransmissionKey { &self.pk_d } @@ -79,7 +83,7 @@ pub mod testing { prop_compose! { /// Generates an arbitrary payment address. - pub(crate) fn arb_address()(sk in arb_spending_key(), j in arb_diversifier_index()) -> Address { + pub fn arb_address()(sk in arb_spending_key(), j in arb_diversifier_index()) -> Address { let fvk = FullViewingKey::from(&sk); fvk.address_at(j, Scope::External) } diff --git a/src/builder.rs b/src/builder.rs index 363a136c4..801332f19 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -229,11 +229,16 @@ impl std::error::Error for OutputError {} /// Information about a specific note to be spent in an [`Action`]. #[derive(Debug)] pub struct SpendInfo { - pub(crate) dummy_sk: Option, - pub(crate) fvk: FullViewingKey, - pub(crate) scope: Scope, - pub(crate) note: Note, - pub(crate) merkle_path: MerklePath, + /// The dummy spending key, if this is a dummy spend. + pub dummy_sk: Option, + /// The full viewing key that owns the note. + pub fvk: FullViewingKey, + /// The scope of the full viewing key. + pub scope: Scope, + /// The note to be spent. + pub note: Note, + /// The Merkle path to the note in the commitment tree. + pub merkle_path: MerklePath, } impl SpendInfo { diff --git a/src/bundle.rs b/src/bundle.rs index e8edd30ae..3184bc522 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -74,7 +74,7 @@ const FLAGS_EXPECTED_UNSET: u8 = !(FLAG_SPENDS_ENABLED | FLAG_OUTPUTS_ENABLED); impl Flags { /// Construct a set of flags from its constituent parts - pub(crate) const fn from_parts(spends_enabled: bool, outputs_enabled: bool) -> Self { + pub const fn from_parts(spends_enabled: bool, outputs_enabled: bool) -> Self { Flags { spends_enabled, outputs_enabled, @@ -298,8 +298,9 @@ impl Bundle { }) } + /// Returns the circuit instances for each action in this bundle. #[cfg(feature = "circuit")] - pub(crate) fn to_instances(&self) -> Vec { + pub fn to_instances(&self) -> Vec { self.actions .iter() .map(|a| a.to_instance(self.flags, self.anchor)) diff --git a/src/bundle/commitments.rs b/src/bundle/commitments.rs index fee2419db..3973bf607 100644 --- a/src/bundle/commitments.rs +++ b/src/bundle/commitments.rs @@ -27,7 +27,7 @@ fn hasher(personal: &[u8; 16]) -> State { /// personalized with ZCASH_ORCHARD_ACTIONS_HASH_PERSONALIZATION /// /// [zip244]: https://zips.z.cash/zip-0244 -pub(crate) fn hash_bundle_txid_data>( +pub fn hash_bundle_txid_data>( bundle: &Bundle, ) -> Blake2bHash { let mut h = hasher(ZCASH_ORCHARD_HASH_PERSONALIZATION); @@ -71,7 +71,7 @@ pub fn hash_bundle_txid_empty() -> Blake2bHash { /// Identifier Non-Malleability][zip244] /// /// [zip244]: https://zips.z.cash/zip-0244 -pub(crate) fn hash_bundle_auth_data(bundle: &Bundle) -> Blake2bHash { +pub fn hash_bundle_auth_data(bundle: &Bundle) -> Blake2bHash { let mut h = hasher(ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION); h.update(bundle.authorization().proof().as_ref()); for action in bundle.actions().iter() { diff --git a/src/circuit.rs b/src/circuit.rs index 06474cc9e..be886ee62 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -58,9 +58,11 @@ use halo2_gadgets::{ utilities::lookup_range_check::{LookupRangeCheck, LookupRangeCheckConfig}, }; -mod commit_ivk; +/// CommitIvk chip for deriving incoming viewing keys in-circuit. +pub mod commit_ivk; pub mod gadget; -mod note_commit; +/// NoteCommit chip for computing note commitments in-circuit. +pub mod note_commit; pub use crate::Proof; @@ -101,25 +103,44 @@ pub struct Config { /// The Orchard Action circuit. #[derive(Clone, Debug, Default)] pub struct Circuit { - pub(crate) path: Value<[MerkleHashOrchard; MERKLE_DEPTH_ORCHARD]>, - pub(crate) pos: Value, - pub(crate) g_d_old: Value, - pub(crate) pk_d_old: Value, - pub(crate) v_old: Value, - pub(crate) rho_old: Value, - pub(crate) psi_old: Value, - pub(crate) rcm_old: Value, - pub(crate) cm_old: Value, - pub(crate) alpha: Value, - pub(crate) ak: Value, - pub(crate) nk: Value, - pub(crate) rivk: Value, - pub(crate) g_d_new: Value, - pub(crate) pk_d_new: Value, - pub(crate) v_new: Value, - pub(crate) psi_new: Value, - pub(crate) rcm_new: Value, - pub(crate) rcv: Value, + /// Merkle authentication path for the spent note. + pub path: Value<[MerkleHashOrchard; MERKLE_DEPTH_ORCHARD]>, + /// Position of the spent note in the commitment tree. + pub pos: Value, + /// Diversified base for the old (spent) note's address. + pub g_d_old: Value, + /// Diversified transmission key for the old note's address. + pub pk_d_old: Value, + /// Value of the old (spent) note. + pub v_old: Value, + /// Uniqueness value (rho) for the old note. + pub rho_old: Value, + /// Psi value derived from the old note's random seed and rho. + pub psi_old: Value, + /// Note commitment trapdoor for the old note. + pub rcm_old: Value, + /// Commitment to the old note. + pub cm_old: Value, + /// Randomizer for the spend authorization key. + pub alpha: Value, + /// Spend validating key. + pub ak: Value, + /// Nullifier deriving key. + pub nk: Value, + /// Randomness for `CommitIvk`. + pub rivk: Value, + /// Diversified base for the new (output) note's address. + pub g_d_new: Value, + /// Diversified transmission key for the new note's address. + pub pk_d_new: Value, + /// Value of the new (output) note. + pub v_new: Value, + /// Psi value derived from the new note's random seed and rho. + pub psi_new: Value, + /// Note commitment trapdoor for the new note. + pub rcm_new: Value, + /// Value commitment trapdoor. + pub rcv: Value, } impl Circuit { @@ -148,7 +169,9 @@ impl Circuit { .then(|| Self::from_action_context_unchecked(spend, output_note, alpha, rcv)) } - pub(crate) fn from_action_context_unchecked( + /// Like [`Circuit::from_action_context`], but without checking that the output + /// note's rho matches the spent note's nullifier. + pub fn from_action_context_unchecked( spend: SpendInfo, output_note: Note, alpha: pallas::Scalar, @@ -759,8 +782,10 @@ impl plonk::Circuit for Circuit { /// The verifying key for the Orchard Action circuit. #[derive(Debug)] pub struct VerifyingKey { - pub(crate) params: halo2_proofs::poly::commitment::Params, - pub(crate) vk: plonk::VerifyingKey, + /// The polynomial commitment parameters. + pub params: halo2_proofs::poly::commitment::Params, + /// The verifying key. + pub vk: plonk::VerifyingKey, } impl VerifyingKey { @@ -798,13 +823,20 @@ impl ProvingKey { /// Public inputs to the Orchard Action circuit. #[derive(Clone, Debug)] pub struct Instance { - pub(crate) anchor: Anchor, - pub(crate) cv_net: ValueCommitment, - pub(crate) nf_old: Nullifier, - pub(crate) rk: VerificationKey, - pub(crate) cmx: ExtractedNoteCommitment, - pub(crate) enable_spend: bool, - pub(crate) enable_output: bool, + /// The Merkle tree anchor. + pub anchor: Anchor, + /// The net value commitment. + pub cv_net: ValueCommitment, + /// The nullifier of the spent note. + pub nf_old: Nullifier, + /// The randomized spend validating key. + pub rk: VerificationKey, + /// The x-coordinate of the note commitment for the output note. + pub cmx: ExtractedNoteCommitment, + /// Whether spending is enabled for this action. + pub enable_spend: bool, + /// Whether output is enabled for this action. + pub enable_output: bool, } impl Instance { diff --git a/src/circuit/commit_ivk.rs b/src/circuit/commit_ivk.rs index 351cb4681..c1e949ac9 100644 --- a/src/circuit/commit_ivk.rs +++ b/src/circuit/commit_ivk.rs @@ -15,19 +15,22 @@ use halo2_gadgets::{ utilities::{bool_check, RangeConstrained}, }; +/// Configuration for the [`CommitIvkChip`]. #[derive(Clone, Debug)] pub struct CommitIvkConfig { q_commit_ivk: Selector, advices: [Column; 10], } +/// Chip implementing `CommitIvk` in-circuit. #[derive(Clone, Debug)] pub struct CommitIvkChip { config: CommitIvkConfig, } impl CommitIvkChip { - pub(in crate::circuit) fn configure( + /// Configures the chip's gate and column assignments. + pub fn configure( meta: &mut ConstraintSystem, advices: [Column; 10], ) -> CommitIvkConfig { @@ -222,12 +225,14 @@ impl CommitIvkChip { config } - pub(in crate::circuit) fn construct(config: CommitIvkConfig) -> Self { + /// Constructs the chip from a [`CommitIvkConfig`]. + pub fn construct(config: CommitIvkConfig) -> Self { Self { config } } } -pub(in crate::circuit) mod gadgets { +/// Gadget functions for `CommitIvk` operations. +pub mod gadgets { use halo2_gadgets::utilities::{ lookup_range_check::{LookupRangeCheck, LookupRangeCheckConfig}, RangeConstrained, @@ -241,7 +246,7 @@ pub(in crate::circuit) mod gadgets { /// [Section 5.4.8.4 Sinsemilla commitments]: https://zips.z.cash/protocol/protocol.pdf#concretesinsemillacommit #[allow(non_snake_case)] #[allow(clippy::type_complexity)] - pub(in crate::circuit) fn commit_ivk( + pub fn commit_ivk( sinsemilla_chip: SinsemillaChip< OrchardHashDomains, OrchardCommitDomains, diff --git a/src/circuit/gadget.rs b/src/circuit/gadget.rs index 12c3c90e6..2f49bc999 100644 --- a/src/circuit/gadget.rs +++ b/src/circuit/gadget.rs @@ -24,60 +24,71 @@ use halo2_proofs::{ plonk::{self, Advice, Assigned, Column}, }; -pub(in crate::circuit) mod add_chip; +/// Addition chip for constraining `a + b = c` in-circuit. +pub mod add_chip; impl super::Config { - pub(super) fn add_chip(&self) -> add_chip::AddChip { + /// Constructs the addition chip. + pub fn add_chip(&self) -> add_chip::AddChip { add_chip::AddChip::construct(self.add_config.clone()) } - pub(super) fn commit_ivk_chip(&self) -> CommitIvkChip { + /// Constructs the CommitIvk chip. + pub fn commit_ivk_chip(&self) -> CommitIvkChip { CommitIvkChip::construct(self.commit_ivk_config.clone()) } - pub(super) fn ecc_chip(&self) -> EccChip { + /// Constructs the ECC chip. + pub fn ecc_chip(&self) -> EccChip { EccChip::construct(self.ecc_config.clone()) } - pub(super) fn sinsemilla_chip_1( + /// Constructs the first Sinsemilla chip. + pub fn sinsemilla_chip_1( &self, ) -> SinsemillaChip { SinsemillaChip::construct(self.sinsemilla_config_1.clone()) } - pub(super) fn sinsemilla_chip_2( + /// Constructs the second Sinsemilla chip. + pub fn sinsemilla_chip_2( &self, ) -> SinsemillaChip { SinsemillaChip::construct(self.sinsemilla_config_2.clone()) } - pub(super) fn merkle_chip_1( + /// Constructs the first Merkle chip. + pub fn merkle_chip_1( &self, ) -> MerkleChip { MerkleChip::construct(self.merkle_config_1.clone()) } - pub(super) fn merkle_chip_2( + /// Constructs the second Merkle chip. + pub fn merkle_chip_2( &self, ) -> MerkleChip { MerkleChip::construct(self.merkle_config_2.clone()) } - pub(super) fn poseidon_chip(&self) -> PoseidonChip { + /// Constructs the Poseidon chip. + pub fn poseidon_chip(&self) -> PoseidonChip { PoseidonChip::construct(self.poseidon_config.clone()) } - pub(super) fn note_commit_chip_new(&self) -> NoteCommitChip { + /// Constructs the NoteCommit chip for the new note. + pub fn note_commit_chip_new(&self) -> NoteCommitChip { NoteCommitChip::construct(self.new_note_commit_config.clone()) } - pub(super) fn note_commit_chip_old(&self) -> NoteCommitChip { + /// Constructs the NoteCommit chip for the old note. + pub fn note_commit_chip_old(&self) -> NoteCommitChip { NoteCommitChip::construct(self.old_note_commit_config.clone()) } } /// An instruction set for adding two circuit words (field elements). -pub(in crate::circuit) trait AddInstruction: Chip { +pub trait AddInstruction: Chip { /// Constraints `a + b` and returns the sum. fn add( &self, @@ -92,7 +103,7 @@ pub(in crate::circuit) trait AddInstruction: Chip { /// Usages of this helper are technically superfluous, as the single-cell region is only /// ever used in equality constraints. We could eliminate them with a /// [write-on-copy abstraction](https://github.com/zcash/halo2/issues/334). -pub(in crate::circuit) fn assign_free_advice( +pub fn assign_free_advice( mut layouter: impl Layouter, column: Column, value: Value, @@ -109,7 +120,7 @@ where /// `ValueCommit^Orchard` from [Section 5.4.8.3 Homomorphic Pedersen commitments (Sapling and Orchard)]. /// /// [Section 5.4.8.3 Homomorphic Pedersen commitments (Sapling and Orchard)]: https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit -pub(in crate::circuit) fn value_commit_orchard< +pub fn value_commit_orchard< EccChip: EccInstructions< pallas::Affine, FixedPoints = OrchardFixedBases, @@ -145,7 +156,7 @@ pub(in crate::circuit) fn value_commit_orchard< /// /// [Section 4.16: Note Commitments and Nullifiers]: https://zips.z.cash/protocol/protocol.pdf#commitmentsandnullifiers #[allow(clippy::too_many_arguments)] -pub(in crate::circuit) fn derive_nullifier< +pub fn derive_nullifier< PoseidonChip: PoseidonSpongeInstructions, 3, 2>, AddChip: AddInstruction, EccChip: EccInstructions< @@ -199,5 +210,5 @@ pub(in crate::circuit) fn derive_nullifier< .map(|res| res.extract_p()) } -pub(in crate::circuit) use crate::circuit::commit_ivk::gadgets::commit_ivk; -pub(in crate::circuit) use crate::circuit::note_commit::gadgets::note_commit; +pub use crate::circuit::commit_ivk::gadgets::commit_ivk; +pub use crate::circuit::note_commit::gadgets::note_commit; diff --git a/src/circuit/gadget/add_chip.rs b/src/circuit/gadget/add_chip.rs index 0973a3f3b..e9780a7b9 100644 --- a/src/circuit/gadget/add_chip.rs +++ b/src/circuit/gadget/add_chip.rs @@ -7,8 +7,9 @@ use pasta_curves::pallas; use super::AddInstruction; +/// Configuration for the addition chip. #[derive(Clone, Debug)] -pub(in crate::circuit) struct AddConfig { +pub struct AddConfig { a: Column, b: Column, c: Column, @@ -16,7 +17,8 @@ pub(in crate::circuit) struct AddConfig { } /// A chip implementing a single addition constraint `c = a + b` on a single row. -pub(in crate::circuit) struct AddChip { +#[derive(Debug)] +pub struct AddChip { config: AddConfig, } @@ -34,7 +36,8 @@ impl Chip for AddChip { } impl AddChip { - pub(in crate::circuit) fn configure( + /// Configures the addition chip with the given advice columns. + pub fn configure( meta: &mut ConstraintSystem, a: Column, b: Column, @@ -53,7 +56,8 @@ impl AddChip { AddConfig { a, b, c, q_add } } - pub(in crate::circuit) fn construct(config: AddConfig) -> Self { + /// Constructs an addition chip from the given config. + pub fn construct(config: AddConfig) -> Self { Self { config } } } diff --git a/src/circuit/note_commit.rs b/src/circuit/note_commit.rs index bba540f5e..a564eea96 100644 --- a/src/circuit/note_commit.rs +++ b/src/circuit/note_commit.rs @@ -1412,6 +1412,7 @@ impl YCanonicity { } } +/// Configuration for the [`NoteCommitChip`]. #[allow(non_snake_case)] #[derive(Clone, Debug)] pub struct NoteCommitConfig { @@ -1431,15 +1432,17 @@ pub struct NoteCommitConfig { SinsemillaConfig, } +/// Chip implementing `NoteCommit` in-circuit. #[derive(Clone, Debug)] pub struct NoteCommitChip { config: NoteCommitConfig, } impl NoteCommitChip { + /// Configures the chip's gates, Sinsemilla instances, and canonicity checks. #[allow(non_snake_case)] #[allow(clippy::many_single_char_names)] - pub(in crate::circuit) fn configure( + pub fn configure( meta: &mut ConstraintSystem, advices: [Column; 10], sinsemilla_config: SinsemillaConfig< @@ -1558,20 +1561,23 @@ impl NoteCommitChip { } } - pub(in crate::circuit) fn construct(config: NoteCommitConfig) -> Self { + /// Constructs the chip from a [`NoteCommitConfig`]. + pub fn construct(config: NoteCommitConfig) -> Self { Self { config } } } -pub(in crate::circuit) mod gadgets { +/// Gadget functions for `NoteCommit` operations. +pub mod gadgets { use halo2_proofs::circuit::{Chip, Value}; use super::*; + /// Computes the note commitment in-circuit. #[allow(clippy::many_single_char_names)] #[allow(clippy::type_complexity)] #[allow(clippy::too_many_arguments)] - pub(in crate::circuit) fn note_commit( + pub fn note_commit( mut layouter: impl Layouter, chip: SinsemillaChip, ecc_chip: EccChip, diff --git a/src/constants.rs b/src/constants.rs index 62548f9c1..8e61aa088 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -13,20 +13,20 @@ pub const MERKLE_DEPTH_ORCHARD: usize = 32; /// The Pallas scalar field modulus is $q = 2^{254} + \mathsf{t_q}$. /// -pub(crate) const T_Q: u128 = 45560315531506369815346746415080538113; +pub const T_Q: u128 = 45560315531506369815346746415080538113; /// The Pallas base field modulus is $p = 2^{254} + \mathsf{t_p}$. /// -pub(crate) const T_P: u128 = 45560315531419706090280762371685220353; +pub const T_P: u128 = 45560315531419706090280762371685220353; /// $\ell^\mathsf{Orchard}_\mathsf{base}$ -pub(crate) const L_ORCHARD_BASE: usize = 255; +pub const L_ORCHARD_BASE: usize = 255; /// $\ell^\mathsf{Orchard}_\mathsf{scalar}$ -pub(crate) const L_ORCHARD_SCALAR: usize = 255; +pub const L_ORCHARD_SCALAR: usize = 255; /// $\ell_\mathsf{value}$ -pub(crate) const L_VALUE: usize = 64; +pub const L_VALUE: usize = 64; /// SWU hash-to-curve personalization for the group hash for key diversification pub const KEY_DIVERSIFICATION_PERSONALIZATION: &str = "z.cash:Orchard-gd"; diff --git a/src/constants/fixed_bases.rs b/src/constants/fixed_bases.rs index 0e3a73906..ee94571f0 100644 --- a/src/constants/fixed_bases.rs +++ b/src/constants/fixed_bases.rs @@ -47,12 +47,10 @@ pub const FIXED_BASE_WINDOW_SIZE: usize = 3; pub const H: usize = 1 << FIXED_BASE_WINDOW_SIZE; /// Number of windows for a full-width scalar -pub const NUM_WINDOWS: usize = - (L_ORCHARD_SCALAR + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE; +pub const NUM_WINDOWS: usize = L_ORCHARD_SCALAR.div_ceil(FIXED_BASE_WINDOW_SIZE); /// Number of windows for a short signed scalar -pub const NUM_WINDOWS_SHORT: usize = - (L_VALUE + FIXED_BASE_WINDOW_SIZE - 1) / FIXED_BASE_WINDOW_SIZE; +pub const NUM_WINDOWS_SHORT: usize = L_VALUE.div_ceil(FIXED_BASE_WINDOW_SIZE); #[derive(Copy, Clone, Debug, Eq, PartialEq)] // A sum type for both full-width and short bases. This enables us to use the diff --git a/src/constants/sinsemilla.rs b/src/constants/sinsemilla.rs index 34843fcca..b3b05f8a9 100644 --- a/src/constants/sinsemilla.rs +++ b/src/constants/sinsemilla.rs @@ -23,7 +23,7 @@ pub const INV_TWO_POW_K: [u8; 32] = [ pub const C: usize = 253; /// $\ell^\mathsf{Orchard}_\mathsf{Merkle}$ -pub(crate) const L_ORCHARD_MERKLE: usize = 255; +pub const L_ORCHARD_MERKLE: usize = 255; /// SWU hash-to-curve personalization for the Merkle CRH generator pub const MERKLE_CRH_PERSONALIZATION: &str = "z.cash:Orchard-MerkleCRH"; @@ -64,7 +64,7 @@ pub const Q_MERKLE_CRH: ([u8; 32], [u8; 32]) = ( ], ); -pub(crate) fn lebs2ip_k(bits: &[bool]) -> u32 { +pub fn lebs2ip_k(bits: &[bool]) -> u32 { assert!(bits.len() == K); bits.iter() .enumerate() @@ -73,7 +73,7 @@ pub(crate) fn lebs2ip_k(bits: &[bool]) -> u32 { /// The sequence of K bits in little-endian order representing an integer /// up to `2^K` - 1. -pub(crate) fn i2lebsp_k(int: usize) -> [bool; K] { +pub fn i2lebsp_k(int: usize) -> [bool; K] { assert!(int < (1 << K)); i2lebsp(int as u64) } diff --git a/src/constants/util.rs b/src/constants/util.rs index e9876ca0d..1f484da40 100644 --- a/src/constants/util.rs +++ b/src/constants/util.rs @@ -6,7 +6,7 @@ pub fn gen_const_array( gen_const_array_with_default(Default::default(), closure) } -pub(crate) fn gen_const_array_with_default( +pub fn gen_const_array_with_default( default_value: Output, closure: impl FnMut(usize) -> Output, ) -> [Output; LEN] { diff --git a/src/keys.rs b/src/keys.rs index ab96e9f84..6cebc0aff 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -1,7 +1,7 @@ //! Key structures for Orchard. use alloc::vec::Vec; -use core2::io::{self, Read, Write}; +use corez::io::{self, Read, Write}; use ::zip32::{AccountId, ChildIndex}; use aes::Aes256; @@ -54,7 +54,7 @@ impl SpendingKey { /// derived according to [ZIP 32]. /// /// [ZIP 32]: https://zips.z.cash/zip-0032 - pub(crate) fn random(rng: &mut impl RngCore) -> Self { + pub fn random(rng: &mut impl RngCore) -> Self { loop { let mut bytes = [0; 32]; rng.fill_bytes(&mut bytes); @@ -121,7 +121,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 { + pub fn derive_inner(sk: &SpendingKey) -> pallas::Scalar { to_scalar(PrfExpand::ORCHARD_ASK.with(&sk.0)) } @@ -188,7 +188,7 @@ impl SpendValidatingKey { /// Converts this spend validating key to its serialized form, /// I2LEOSP_256(ak). #[cfg_attr(feature = "unstable-frost", visibility::make(pub))] - pub(crate) fn to_bytes(&self) -> [u8; 32] { + pub fn to_bytes(&self) -> [u8; 32] { // This is correct because the wrapped point must have ỹ = 0, and // so the point repr is the same as I2LEOSP of its x-coordinate. let b = <[u8; 32]>::from(&self.0); @@ -200,7 +200,7 @@ impl SpendValidatingKey { /// /// Returns `None` if the given slice does not contain a valid spend validating key. #[cfg_attr(feature = "unstable-frost", visibility::make(pub))] - pub(crate) fn from_bytes(bytes: &[u8]) -> Option { + pub fn from_bytes(bytes: &[u8]) -> Option { <[u8; 32]>::try_from(bytes) .ok() .and_then(|b| { @@ -226,10 +226,11 @@ impl SpendValidatingKey { /// [`Note`]: crate::note::Note /// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents #[derive(Copy, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub(crate) struct NullifierDerivingKey(pallas::Base); +pub struct NullifierDerivingKey(pallas::Base); impl NullifierDerivingKey { - pub(crate) fn inner(&self) -> pallas::Base { + /// Returns the inner base field element. + pub fn inner(&self) -> pallas::Base { self.0 } } @@ -241,16 +242,18 @@ impl From<&SpendingKey> for NullifierDerivingKey { } impl NullifierDerivingKey { - pub(crate) fn prf_nf(&self, rho: pallas::Base) -> pallas::Base { + /// Computes PRF^nf on the given rho value. + pub fn prf_nf(&self, rho: pallas::Base) -> pallas::Base { prf_nf(self.0, rho) } /// Converts this nullifier deriving key to its serialized form. - pub(crate) fn to_bytes(self) -> [u8; 32] { + pub fn to_bytes(self) -> [u8; 32] { <[u8; 32]>::from(self.0) } - pub(crate) fn from_bytes(bytes: &[u8]) -> Option { + /// Parses a nullifier deriving key from a byte slice. + pub fn from_bytes(bytes: &[u8]) -> Option { let nk_bytes = <[u8; 32]>::try_from(bytes).ok()?; let nk = pallas::Base::from_repr(nk_bytes).map(NullifierDerivingKey); if nk.is_some().into() { @@ -267,7 +270,7 @@ impl NullifierDerivingKey { /// /// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents #[derive(Copy, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub(crate) struct CommitIvkRandomness(pallas::Scalar); +pub struct CommitIvkRandomness(pallas::Scalar); impl From<&SpendingKey> for CommitIvkRandomness { fn from(sk: &SpendingKey) -> Self { @@ -276,16 +279,18 @@ impl From<&SpendingKey> for CommitIvkRandomness { } impl CommitIvkRandomness { - pub(crate) fn inner(&self) -> pallas::Scalar { + /// Returns the inner scalar value. + pub fn inner(&self) -> pallas::Scalar { self.0 } - /// Converts this nullifier deriving key to its serialized form. - pub(crate) fn to_bytes(self) -> [u8; 32] { + /// Converts this commit-ivk randomness to its serialized form. + pub fn to_bytes(self) -> [u8; 32] { <[u8; 32]>::from(self.0) } - pub(crate) fn from_bytes(bytes: &[u8]) -> Option { + /// Parses commit-ivk randomness from a byte slice. + pub fn from_bytes(bytes: &[u8]) -> Option { let rivk_bytes = <[u8; 32]>::try_from(bytes).ok()?; let rivk = pallas::Scalar::from_repr(rivk_bytes).map(CommitIvkRandomness); if rivk.is_some().into() { @@ -334,12 +339,13 @@ impl From for SpendValidatingKey { } impl FullViewingKey { - pub(crate) fn nk(&self) -> &NullifierDerivingKey { + /// Returns the nullifier deriving key for this full viewing key. + pub fn nk(&self) -> &NullifierDerivingKey { &self.nk } /// Returns either `rivk` or `rivk_internal` based on `scope`. - pub(crate) fn rivk(&self, scope: Scope) -> CommitIvkRandomness { + pub fn rivk(&self, scope: Scope) -> CommitIvkRandomness { match scope { Scope::External => self.rivk, Scope::Internal => { @@ -475,7 +481,7 @@ impl FullViewingKey { /// /// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub(crate) struct DiversifierKey([u8; 32]); +pub struct DiversifierKey([u8; 32]); impl DiversifierKey { /// Returns the diversifier at the given index. @@ -745,7 +751,8 @@ impl AsRef<[u8; 32]> for OutgoingViewingKey { pub struct DiversifiedTransmissionKey(NonIdentityPallasPoint); impl DiversifiedTransmissionKey { - pub(crate) fn inner(&self) -> NonIdentityPallasPoint { + /// Returns the inner `NonIdentityPallasPoint`. + pub fn inner(&self) -> NonIdentityPallasPoint { self.0 } } @@ -754,18 +761,18 @@ impl DiversifiedTransmissionKey { /// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents]. /// /// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents - pub(crate) fn derive(ivk: &PreparedIncomingViewingKey, d: &Diversifier) -> Self { + pub fn derive(ivk: &PreparedIncomingViewingKey, d: &Diversifier) -> Self { let g_d = PreparedNonIdentityBase::new(diversify_hash(d.as_array())); DiversifiedTransmissionKey(ka_orchard_prepared(&ivk.0, &g_d)) } /// $abst_P(bytes)$ - pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption { + pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { NonIdentityPallasPoint::from_bytes(bytes).map(DiversifiedTransmissionKey) } /// $repr_P(self)$ - pub(crate) fn to_bytes(self) -> [u8; 32] { + pub fn to_bytes(self) -> [u8; 32] { self.0.to_bytes() } } @@ -789,7 +796,7 @@ impl ConditionallySelectable for DiversifiedTransmissionKey { /// /// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement #[derive(Debug)] -pub struct EphemeralSecretKey(pub(crate) NonZeroPallasScalar); +pub struct EphemeralSecretKey(pub NonZeroPallasScalar); impl ConstantTimeEq for EphemeralSecretKey { fn ct_eq(&self, other: &Self) -> subtle::Choice { @@ -798,15 +805,18 @@ impl ConstantTimeEq for EphemeralSecretKey { } impl EphemeralSecretKey { - pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption { + /// Parses an ephemeral secret key from its byte representation. + pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { NonZeroPallasScalar::from_bytes(bytes).map(EphemeralSecretKey) } - pub(crate) fn derive_public(&self, g_d: NonIdentityPallasPoint) -> EphemeralPublicKey { + /// Derives the corresponding ephemeral public key from a diversified base. + pub fn derive_public(&self, g_d: NonIdentityPallasPoint) -> EphemeralPublicKey { EphemeralPublicKey(ka_orchard(&self.0, &g_d)) } - pub(crate) fn agree(&self, pk_d: &DiversifiedTransmissionKey) -> SharedSecret { + /// Performs key agreement with a diversified transmission key. + pub fn agree(&self, pk_d: &DiversifiedTransmissionKey) -> SharedSecret { SharedSecret(ka_orchard(&self.0, &pk_d.0)) } } @@ -825,15 +835,18 @@ impl EphemeralSecretKey { pub struct EphemeralPublicKey(NonIdentityPallasPoint); impl EphemeralPublicKey { - pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption { + /// Parses an ephemeral public key from its byte representation. + pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { NonIdentityPallasPoint::from_bytes(bytes).map(EphemeralPublicKey) } - pub(crate) fn to_bytes(&self) -> EphemeralKeyBytes { + /// Serializes this ephemeral public key to bytes. + pub fn to_bytes(&self) -> EphemeralKeyBytes { EphemeralKeyBytes(self.0.to_bytes()) } - pub(crate) fn agree(&self, ivk: &IncomingViewingKey) -> SharedSecret { + /// Performs key agreement with an incoming viewing key. + pub fn agree(&self, ivk: &IncomingViewingKey) -> SharedSecret { SharedSecret(ka_orchard(&ivk.ivk.0, &self.0)) } } @@ -843,11 +856,13 @@ impl EphemeralPublicKey { pub struct PreparedEphemeralPublicKey(PreparedNonIdentityBase); impl PreparedEphemeralPublicKey { - pub(crate) fn new(epk: EphemeralPublicKey) -> Self { + /// Prepares an ephemeral public key for trial decryption. + pub fn new(epk: EphemeralPublicKey) -> Self { PreparedEphemeralPublicKey(PreparedNonIdentityBase::new(epk.0)) } - pub(crate) fn agree(&self, ivk: &PreparedIncomingViewingKey) -> SharedSecret { + /// Performs key agreement with a prepared incoming viewing key. + pub fn agree(&self, ivk: &PreparedIncomingViewingKey) -> SharedSecret { SharedSecret(ka_orchard_prepared(&ivk.0, &self.0)) } } @@ -863,12 +878,12 @@ pub struct SharedSecret(NonIdentityPallasPoint); impl SharedSecret { /// For checking test vectors only. #[cfg(test)] - pub(crate) fn to_bytes(&self) -> [u8; 32] { + pub fn to_bytes(&self) -> [u8; 32] { self.0.to_bytes() } /// Only for use in batched note encryption. - pub(crate) fn batch_to_affine( + pub fn batch_to_affine( shared_secrets: Vec>, ) -> impl Iterator> { // Filter out the positions for which ephemeral_key was not a valid encoding. @@ -891,12 +906,12 @@ impl SharedSecret { /// Defined in [Zcash Protocol Spec § 5.4.5.6: Orchard Key Agreement][concreteorchardkdf]. /// /// [concreteorchardkdf]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkdf - pub(crate) fn kdf_orchard(self, ephemeral_key: &EphemeralKeyBytes) -> Blake2bHash { + pub fn kdf_orchard(self, ephemeral_key: &EphemeralKeyBytes) -> Blake2bHash { Self::kdf_orchard_inner(self.0.to_affine(), ephemeral_key) } /// Only for direct use in batched note encryption. - pub(crate) fn kdf_orchard_inner( + pub fn kdf_orchard_inner( secret: pallas::Affine, ephemeral_key: &EphemeralKeyBytes, ) -> Blake2bHash { @@ -948,7 +963,7 @@ pub mod testing { prop_compose! { /// Generate a uniformly distributed Orchard diversifier key. - pub(crate) fn arb_diversifier_key()( + pub fn arb_diversifier_key()( dk_bytes in prop::array::uniform32(prop::num::u8::ANY) ) -> DiversifierKey { DiversifierKey::from_bytes(dk_bytes) diff --git a/src/lib.rs b/src/lib.rs index 3a52af5c6..d86232309 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,6 @@ -//! # orchard +//! Implementation crate for the Orchard shielded transaction protocol. //! -//! ## Nomenclature -//! -//! All types in the `orchard` crate, unless otherwise specified, are Orchard-specific -//! types. For example, [`Address`] is documented as being a shielded payment address; we -//! implicitly mean it is an Orchard payment address (as opposed to e.g. a Sapling payment -//! address, which is also shielded). +//! This crate is re-exported by the public `orchard` crate. Depend on `orchard` instead. #![no_std] #![cfg_attr(docsrs, feature(doc_cfg))] @@ -15,7 +10,7 @@ #![deny(rustdoc::broken_intra_doc_links)] #![deny(missing_debug_implementations)] #![deny(missing_docs)] -#![deny(unsafe_code)] +#![forbid(unsafe_code)] #[macro_use] extern crate alloc; @@ -31,13 +26,15 @@ pub mod builder; pub mod bundle; #[cfg(feature = "circuit")] pub mod circuit; -mod constants; +#[allow(missing_docs)] +pub mod constants; pub mod keys; pub mod note; pub mod note_encryption; pub mod pczt; pub mod primitives; -mod spec; +#[allow(missing_docs)] +pub mod spec; pub mod tree; pub mod value; pub mod zip32; diff --git a/src/note.rs b/src/note.rs index 6a9c70b5a..36cb12742 100644 --- a/src/note.rs +++ b/src/note.rs @@ -15,10 +15,12 @@ use crate::{ Address, }; -pub(crate) mod commitment; -pub use self::commitment::{ExtractedNoteCommitment, NoteCommitment}; +/// Note commitment types. +pub mod commitment; +pub use self::commitment::{ExtractedNoteCommitment, NoteCommitTrapdoor, NoteCommitment}; -pub(crate) mod nullifier; +/// Nullifier types and derivation. +pub mod nullifier; pub use self::nullifier::Nullifier; /// The randomness used to construct a note. @@ -50,11 +52,12 @@ impl Rho { /// of the note being spent in the [`Action`] under construction. /// /// [`Action`]: crate::action::Action - pub(crate) fn from_nf_old(nf: Nullifier) -> Self { + pub fn from_nf_old(nf: Nullifier) -> Self { Rho(nf.0) } - pub(crate) fn into_inner(self) -> pallas::Base { + /// Consumes `self` and returns the inner field element. + pub fn into_inner(self) -> pallas::Base { self.0 } } @@ -64,7 +67,8 @@ impl Rho { pub struct RandomSeed([u8; 32]); impl RandomSeed { - pub(crate) fn random(rng: &mut impl RngCore, rho: &Rho) -> Self { + /// Generates a random seed for a note, retrying until a valid one is found. + pub fn random(rng: &mut impl RngCore, rho: &Rho) -> Self { loop { let mut bytes = [0; 32]; rng.fill_bytes(&mut bytes); @@ -92,7 +96,8 @@ 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: &Rho) -> pallas::Base { + /// Derives the psi value for this random seed and rho. + pub fn psi(&self, rho: &Rho) -> pallas::Base { to_base(PrfExpand::PSI.with(&self.0, &rho.to_bytes())) } @@ -116,7 +121,8 @@ 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: &Rho) -> commitment::NoteCommitTrapdoor { + /// Derives the note commitment trapdoor for this random seed and rho. + pub fn rcm(&self, rho: &Rho) -> commitment::NoteCommitTrapdoor { commitment::NoteCommitTrapdoor(to_scalar( PrfExpand::ORCHARD_RCM.with(&self.0, &rho.to_bytes()), )) @@ -186,12 +192,7 @@ impl Note { /// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend]. /// /// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend - pub(crate) fn new( - recipient: Address, - value: NoteValue, - rho: Rho, - mut rng: impl RngCore, - ) -> Self { + pub fn new(recipient: Address, value: NoteValue, rho: Rho, mut rng: impl RngCore) -> Self { loop { let note = Note::from_parts(recipient, value, rho, RandomSeed::random(&mut rng, &rho)); if note.is_some().into() { @@ -205,10 +206,7 @@ impl Note { /// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes]. /// /// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes - pub(crate) fn dummy( - rng: &mut impl RngCore, - rho: Option, - ) -> (SpendingKey, FullViewingKey, Self) { + pub fn dummy(rng: &mut impl RngCore, rho: Option) -> (SpendingKey, FullViewingKey, Self) { let sk = SpendingKey::random(rng); let fvk: FullViewingKey = (&sk).into(); let recipient = fvk.address_at(0u32, Scope::External); @@ -239,7 +237,7 @@ impl Note { } /// Derives the ephemeral secret key for this note. - pub(crate) fn esk(&self) -> EphemeralSecretKey { + pub fn esk(&self) -> EphemeralSecretKey { EphemeralSecretKey(self.rseed.esk(&self.rho)) } @@ -327,7 +325,7 @@ pub mod testing { prop_compose! { /// Generate an arbitrary random seed - pub(crate) fn arb_rseed()(elems in prop::array::uniform32(prop::num::u8::ANY)) -> RandomSeed { + pub fn arb_rseed()(elems in prop::array::uniform32(prop::num::u8::ANY)) -> RandomSeed { RandomSeed(elems) } } diff --git a/src/note/commitment.rs b/src/note/commitment.rs index 5d0125874..92cd9bd32 100644 --- a/src/note/commitment.rs +++ b/src/note/commitment.rs @@ -11,11 +11,13 @@ use crate::{ value::NoteValue, }; +/// The trapdoor for a note commitment. #[derive(Clone, Debug)] -pub(crate) struct NoteCommitTrapdoor(pub(super) pallas::Scalar); +pub struct NoteCommitTrapdoor(pub(super) pallas::Scalar); impl NoteCommitTrapdoor { - pub(crate) fn inner(&self) -> pallas::Scalar { + /// Returns the inner scalar value. + pub fn inner(&self) -> pallas::Scalar { self.0 } } @@ -25,7 +27,8 @@ impl NoteCommitTrapdoor { pub struct NoteCommitment(pub(super) pallas::Point); impl NoteCommitment { - pub(crate) fn inner(&self) -> pallas::Point { + /// Returns the inner Pallas curve point. + pub fn inner(&self) -> pallas::Point { self.0 } } @@ -87,7 +90,8 @@ impl From for ExtractedNoteCommitment { } impl ExtractedNoteCommitment { - pub(crate) fn inner(&self) -> pallas::Base { + /// Returns the inner field element. + pub fn inner(&self) -> pallas::Base { self.0 } } diff --git a/src/note/nullifier.rs b/src/note/nullifier.rs index 15ac3699f..5c5aed63c 100644 --- a/src/note/nullifier.rs +++ b/src/note/nullifier.rs @@ -12,7 +12,7 @@ use crate::{ /// A unique nullifier for a note. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct Nullifier(pub(crate) pallas::Base); +pub struct Nullifier(pub pallas::Base); // We know that `pallas::Base` doesn't allocate internally. memuse::impl_no_dynamic_usage!(Nullifier); @@ -30,7 +30,7 @@ impl Nullifier { /// /// Instead of explicitly sampling for a unique nullifier, we rely here on the size of /// the base field to make the chance of sampling a colliding nullifier negligible. - pub(crate) fn dummy(rng: &mut impl RngCore) -> Self { + pub fn dummy(rng: &mut impl RngCore) -> Self { Nullifier(extract_p(&pallas::Point::random(rng))) } diff --git a/src/note_encryption.rs b/src/note_encryption.rs index 2228b8030..a1dd36137 100644 --- a/src/note_encryption.rs +++ b/src/note_encryption.rs @@ -27,7 +27,7 @@ const PRF_OCK_ORCHARD_PERSONALIZATION: &[u8; 16] = b"Zcash_Orchardock"; /// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs]. /// /// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs -pub(crate) fn prf_ock_orchard( +pub fn prf_ock_orchard( ovk: &OutgoingViewingKey, cv: &ValueCommitment, cmx_bytes: &[u8; 32], diff --git a/src/pczt.rs b/src/pczt.rs index 854cd00ae..e1806b586 100644 --- a/src/pczt.rs +++ b/src/pczt.rs @@ -56,36 +56,36 @@ pub struct Bundle { /// /// Entries are added by the Constructor, and modified by an Updater, IO Finalizer, /// Signer, Combiner, or Spend Finalizer. - pub(crate) actions: Vec, + pub actions: Vec, /// The flags for the Orchard bundle. /// /// This is set by the Creator. The Constructor MUST only add spends and outputs that /// are consistent with these flags (i.e. are dummies as appropriate). - pub(crate) flags: Flags, + pub flags: Flags, /// The sum of the values of all `actions`. /// /// This is initialized by the Creator, and updated by the Constructor as spends or /// outputs are added to the PCZT. It enables per-spend and per-output values to be /// redacted from the PCZT after they are no longer necessary. - pub(crate) value_sum: ValueSum, + pub value_sum: ValueSum, /// The Orchard anchor for this transaction. /// /// Set by the Creator. - pub(crate) anchor: Anchor, + pub anchor: Anchor, /// The Orchard bundle proof. /// /// This is `None` until it is set by the Prover. - pub(crate) zkproof: Option, + pub zkproof: Option, /// The Orchard binding signature signing key. /// /// - This is `None` until it is set by the IO Finalizer. /// - The Transaction Extractor uses this to produce the binding signature. - pub(crate) bsk: Option>, + pub bsk: Option>, } impl Bundle { @@ -110,13 +110,13 @@ impl Bundle { #[getset(get = "pub")] pub struct Action { /// A commitment to the net value created or consumed by this action. - pub(crate) cv_net: ValueCommitment, + pub cv_net: ValueCommitment, /// The spend half of this action. - pub(crate) spend: Spend, + pub spend: Spend, /// The output half of this action. - pub(crate) output: Output, + pub output: Output, /// The value commitment randomness. /// @@ -127,7 +127,7 @@ pub struct Action { /// /// This opens `cv` for all participants. For Signers who don't need this information, /// or after proofs / signatures have been applied, this can be redacted. - pub(crate) rcv: Option, + pub rcv: Option, } /// Information about an Orchard spend within a transaction. @@ -135,21 +135,21 @@ pub struct Action { #[getset(get = "pub")] pub struct Spend { /// The nullifier of the note being spent. - pub(crate) nullifier: Nullifier, + pub nullifier: Nullifier, /// The randomized verification key for the note being spent. - pub(crate) rk: redpallas::VerificationKey, + pub rk: redpallas::VerificationKey, /// The spend authorization signature. /// /// This is set by the Signer. - pub(crate) spend_auth_sig: Option>, + pub spend_auth_sig: Option>, /// The address that received the note being spent. /// /// - This is set by the Constructor (or Updater?). /// - This is required by the Prover. - pub(crate) recipient: Option
, + pub recipient: Option
, /// The value of the input being spent. /// @@ -159,7 +159,7 @@ pub struct Spend { /// /// This exposes the input value to all participants. For Signers who don't need this /// information, or after signatures have been applied, this can be redacted. - pub(crate) value: Option, + pub value: Option, /// The rho value for the note being spent. /// @@ -168,25 +168,25 @@ pub struct Spend { // // TODO: This could be merged with `rseed` into a tuple. `recipient` and `value` are // separate because they might need to be independently redacted. (For which role?) - pub(crate) rho: Option, + pub rho: Option, /// The seed randomness for the note being spent. /// /// - This is set by the Constructor. /// - This is required by the Prover. - pub(crate) rseed: Option, + pub rseed: Option, /// The full viewing key that received the note being spent. /// /// - This is set by the Updater. /// - This is required by the Prover. - pub(crate) fvk: Option, + pub fvk: Option, /// A witness from the note to the bundle's anchor. /// /// - This is set by the Updater. /// - This is required by the Prover. - pub(crate) witness: Option, + pub witness: Option, /// The spend authorization randomizer. /// @@ -194,21 +194,21 @@ pub struct Spend { /// - This is required by the Signer for creating `spend_auth_sig`, and may be used to /// validate `rk`. /// - After`zkproof` / `spend_auth_sig` has been set, this can be redacted. - pub(crate) alpha: Option, + pub alpha: Option, /// The ZIP 32 derivation path at which the spending key can be found for the note /// being spent. - pub(crate) zip32_derivation: Option, + pub zip32_derivation: Option, /// The spending key for this spent note, if it is a dummy note. /// /// - This is chosen by the Constructor. /// - This is required by the IO Finalizer, and is cleared by it once used. /// - Signers MUST reject PCZTs that contain `dummy_sk` values. - pub(crate) dummy_sk: Option, + pub dummy_sk: Option, /// Proprietary fields related to the note being spent. - pub(crate) proprietary: BTreeMap>, + pub proprietary: BTreeMap>, } /// Information about an Orchard output within a transaction. @@ -216,7 +216,7 @@ pub struct Spend { #[getset(get = "pub")] pub struct Output { /// A commitment to the new note being created. - pub(crate) cmx: ExtractedNoteCommitment, + pub cmx: ExtractedNoteCommitment, /// The transmitted note ciphertext. /// @@ -224,7 +224,7 @@ pub struct Output { /// - `ephemeral_key` /// - `enc_ciphertext` /// - `out_ciphertext` - pub(crate) encrypted_note: TransmittedNoteCiphertext, + pub encrypted_note: TransmittedNoteCiphertext, /// The address that will receive the output. /// @@ -233,7 +233,7 @@ pub struct Output { /// - The Signer can use `recipient` and `rseed` (if present) to verify that /// `enc_ciphertext` is correctly encrypted (and contains a note plaintext matching /// the public commitments), and to confirm the value of the memo. - pub(crate) recipient: Option
, + pub recipient: Option
, /// The value of the output. /// @@ -242,7 +242,7 @@ pub struct Output { /// /// This exposes the value to all participants. For Signers who don't need this /// information, we can drop the values and compress the rcvs into the bsk global. - pub(crate) value: Option, + pub value: Option, /// The seed randomness for the output. /// @@ -251,7 +251,7 @@ pub struct Output { /// - The Signer can use `recipient` and `rseed` (if present) to verify that /// `enc_ciphertext` is correctly encrypted (and contains a note plaintext matching /// the public commitments), and to confirm the value of the memo. - pub(crate) rseed: Option, + pub rseed: Option, /// The `ock` value used to encrypt `out_ciphertext`. /// @@ -259,20 +259,20 @@ pub struct Output { /// /// This may be `None` if the Constructor added the output using an OVK policy of /// "None", to make the output unrecoverable from the chain by the sender. - pub(crate) ock: Option, + pub ock: Option, /// The ZIP 32 derivation path at which the spending key can be found for the output. - pub(crate) zip32_derivation: Option, + pub zip32_derivation: Option, /// The user-facing address to which this output is being sent, if any. /// /// - This is set by an Updater. /// - Signers must parse this address (if present) and confirm that it contains /// `recipient` (either directly, or e.g. as a receiver within a Unified Address). - pub(crate) user_address: Option, + pub user_address: Option, /// Proprietary fields related to the note being created. - pub(crate) proprietary: BTreeMap>, + pub proprietary: BTreeMap>, } impl fmt::Debug for Output { diff --git a/src/primitives/redpallas.rs b/src/primitives/redpallas.rs index 23074d945..434f1580e 100644 --- a/src/primitives/redpallas.rs +++ b/src/primitives/redpallas.rs @@ -115,7 +115,7 @@ impl Ord for VerificationKey { impl VerificationKey { /// Used in the note encryption tests. #[cfg(test)] - pub(crate) fn dummy() -> Self { + pub fn dummy() -> Self { VerificationKey((&reddsa::SigningKey::new(OsRng)).into()) } @@ -172,9 +172,11 @@ impl From<&Signature> for [u8; 64] { } } -pub(crate) mod private { +/// Sealed trait for signature types. +pub mod private { use super::{Binding, SpendAuth}; + /// Sealed trait to prevent external implementations of [`SigType`](super::SigType). pub trait Sealed {} impl Sealed for SpendAuth {} diff --git a/src/spec.rs b/src/spec.rs index c7194562b..acf922119 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -20,11 +20,11 @@ use crate::constants::{ KEY_DIVERSIFICATION_PERSONALIZATION, L_ORCHARD_BASE, }; -pub(crate) use zcash_spec::PrfExpand; +pub use zcash_spec::PrfExpand; /// A Pallas point that is guaranteed to not be the identity. #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub(crate) struct NonIdentityPallasPoint(pallas::Point); +pub struct NonIdentityPallasPoint(pallas::Point); impl Default for NonIdentityPallasPoint { fn default() -> Self { @@ -39,7 +39,7 @@ impl ConditionallySelectable for NonIdentityPallasPoint { } impl NonIdentityPallasPoint { - pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption { + pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { pallas::Point::from_bytes(bytes) .and_then(|p| CtOption::new(NonIdentityPallasPoint(p), !p.is_identity())) } @@ -55,7 +55,7 @@ impl Deref for NonIdentityPallasPoint { /// An integer in [1..q_P]. #[derive(Clone, Copy, Debug)] -pub(crate) struct NonZeroPallasBase(pallas::Base); +pub struct NonZeroPallasBase(pallas::Base); impl Default for NonZeroPallasBase { fn default() -> Self { @@ -70,15 +70,15 @@ impl ConditionallySelectable for NonZeroPallasBase { } impl NonZeroPallasBase { - pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption { + pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { pallas::Base::from_repr(*bytes).and_then(NonZeroPallasBase::from_base) } - pub(crate) fn to_bytes(self) -> [u8; 32] { + pub fn to_bytes(self) -> [u8; 32] { self.0.to_repr() } - pub(crate) fn from_base(b: pallas::Base) -> CtOption { + pub fn from_base(b: pallas::Base) -> CtOption { CtOption::new(NonZeroPallasBase(b), !b.is_zero()) } @@ -95,7 +95,7 @@ impl NonZeroPallasBase { /// An integer in [1..r_P]. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub(crate) struct NonZeroPallasScalar(pallas::Scalar); +pub struct NonZeroPallasScalar(pallas::Scalar); impl Default for NonZeroPallasScalar { fn default() -> Self { @@ -116,11 +116,11 @@ impl ConditionallySelectable for NonZeroPallasScalar { } impl NonZeroPallasScalar { - pub(crate) fn from_bytes(bytes: &[u8; 32]) -> CtOption { + pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { pallas::Scalar::from_repr(*bytes).and_then(NonZeroPallasScalar::from_scalar) } - pub(crate) fn from_scalar(s: pallas::Scalar) -> CtOption { + pub fn from_scalar(s: pallas::Scalar) -> CtOption { CtOption::new(NonZeroPallasScalar(s), !s.is_zero()) } @@ -146,16 +146,16 @@ impl Deref for NonZeroPallasScalar { const PREPARED_WINDOW_SIZE: usize = 4; #[derive(Clone, Debug)] -pub(crate) struct PreparedNonIdentityBase(WnafBase); +pub struct PreparedNonIdentityBase(WnafBase); impl PreparedNonIdentityBase { - pub(crate) fn new(base: NonIdentityPallasPoint) -> Self { + pub fn new(base: NonIdentityPallasPoint) -> Self { PreparedNonIdentityBase(WnafBase::new(base.0)) } } #[derive(Clone, Debug)] -pub(crate) struct PreparedNonZeroScalar(WnafScalar); +pub struct PreparedNonZeroScalar(WnafScalar); #[cfg(feature = "std")] impl DynamicUsage for PreparedNonZeroScalar { @@ -169,7 +169,7 @@ impl DynamicUsage for PreparedNonZeroScalar { } impl PreparedNonZeroScalar { - pub(crate) fn new(scalar: &NonZeroPallasScalar) -> Self { + pub fn new(scalar: &NonZeroPallasScalar) -> Self { PreparedNonZeroScalar(WnafScalar::new(scalar)) } } @@ -179,7 +179,7 @@ impl PreparedNonZeroScalar { /// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents]. /// /// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents -pub(crate) fn to_base(x: [u8; 64]) -> pallas::Base { +pub fn to_base(x: [u8; 64]) -> pallas::Base { pallas::Base::from_uniform_bytes(&x) } @@ -188,7 +188,7 @@ pub(crate) fn to_base(x: [u8; 64]) -> pallas::Base { /// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents]. /// /// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents -pub(crate) fn to_scalar(x: [u8; 64]) -> pallas::Scalar { +pub fn to_scalar(x: [u8; 64]) -> pallas::Scalar { pallas::Scalar::from_uniform_bytes(&x) } @@ -196,14 +196,14 @@ pub(crate) fn to_scalar(x: [u8; 64]) -> pallas::Scalar { /// /// This requires no modular reduction because Pallas' base field is smaller than its /// scalar field. -pub(crate) fn mod_r_p(x: pallas::Base) -> pallas::Scalar { +pub fn mod_r_p(x: pallas::Base) -> pallas::Scalar { pallas::Scalar::from_repr(x.to_repr()).unwrap() } /// Defined in [Zcash Protocol Spec § 5.4.8.4: Sinsemilla commitments][concretesinsemillacommit]. /// /// [concretesinsemillacommit]: https://zips.z.cash/protocol/protocol.pdf#concretesinsemillacommit -pub(crate) fn commit_ivk( +pub fn commit_ivk( ak: &pallas::Base, nk: &pallas::Base, rivk: &pallas::Scalar, @@ -222,7 +222,7 @@ pub(crate) fn commit_ivk( /// Defined in [Zcash Protocol Spec § 5.4.1.6: DiversifyHash^Sapling and DiversifyHash^Orchard Hash Functions][concretediversifyhash]. /// /// [concretediversifyhash]: https://zips.z.cash/protocol/nu5.pdf#concretediversifyhash -pub(crate) fn diversify_hash(d: &[u8; 11]) -> NonIdentityPallasPoint { +pub fn diversify_hash(d: &[u8; 11]) -> NonIdentityPallasPoint { let hasher = pallas::Point::hash_to_curve(KEY_DIVERSIFICATION_PERSONALIZATION); let g_d = hasher(d); // If the identity occurs, we replace it with a different fixed point. @@ -235,7 +235,7 @@ pub(crate) fn diversify_hash(d: &[u8; 11]) -> NonIdentityPallasPoint { /// Defined in [Zcash Protocol Spec § 5.4.2: Pseudo Random Functions][concreteprfs]. /// /// [concreteprfs]: https://zips.z.cash/protocol/nu5.pdf#concreteprfs -pub(crate) fn prf_nf(nk: pallas::Base, rho: pallas::Base) -> pallas::Base { +pub fn prf_nf(nk: pallas::Base, rho: pallas::Base) -> pallas::Base { poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init() .hash([nk, rho]) } @@ -243,10 +243,7 @@ pub(crate) fn prf_nf(nk: pallas::Base, rho: pallas::Base) -> pallas::Base { /// Defined in [Zcash Protocol Spec § 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement]. /// /// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement -pub(crate) fn ka_orchard( - sk: &NonZeroPallasScalar, - b: &NonIdentityPallasPoint, -) -> NonIdentityPallasPoint { +pub fn ka_orchard(sk: &NonZeroPallasScalar, b: &NonIdentityPallasPoint) -> NonIdentityPallasPoint { ka_orchard_prepared( &PreparedNonZeroScalar::new(sk), &PreparedNonIdentityBase::new(*b), @@ -256,7 +253,7 @@ pub(crate) fn ka_orchard( /// Defined in [Zcash Protocol Spec § 5.4.5.5: Orchard Key Agreement][concreteorchardkeyagreement]. /// /// [concreteorchardkeyagreement]: https://zips.z.cash/protocol/nu5.pdf#concreteorchardkeyagreement -pub(crate) fn ka_orchard_prepared( +pub fn ka_orchard_prepared( sk: &PreparedNonZeroScalar, b: &PreparedNonIdentityBase, ) -> NonIdentityPallasPoint { @@ -268,7 +265,7 @@ pub(crate) fn ka_orchard_prepared( /// Defined in [Zcash Protocol Spec § 5.4.9.7: Coordinate Extractor for Pallas][concreteextractorpallas]. /// /// [concreteextractorpallas]: https://zips.z.cash/protocol/nu5.pdf#concreteextractorpallas -pub(crate) fn extract_p(point: &pallas::Point) -> pallas::Base { +pub fn extract_p(point: &pallas::Point) -> pallas::Base { point .to_affine() .coordinates() @@ -281,7 +278,7 @@ pub(crate) fn extract_p(point: &pallas::Point) -> pallas::Base { /// Defined in [Zcash Protocol Spec § 5.4.9.7: Coordinate Extractor for Pallas][concreteextractorpallas]. /// /// [concreteextractorpallas]: https://zips.z.cash/protocol/nu5.pdf#concreteextractorpallas -pub(crate) fn extract_p_bottom(point: CtOption) -> CtOption { +pub fn extract_p_bottom(point: CtOption) -> CtOption { point.map(|p| extract_p(&p)) } diff --git a/src/test_vectors.rs b/src/test_vectors.rs index b1ef91eec..1403d91c1 100644 --- a/src/test_vectors.rs +++ b/src/test_vectors.rs @@ -1,5 +1,5 @@ -pub(crate) mod commitment_tree; -pub(crate) mod keys; -pub(crate) mod merkle_path; -pub(crate) mod note_encryption; -pub(crate) mod zip32; +pub mod commitment_tree; +pub mod keys; +pub mod merkle_path; +pub mod note_encryption; +pub mod zip32; diff --git a/src/test_vectors/commitment_tree.rs b/src/test_vectors/commitment_tree.rs index d9532c589..7001c9324 100644 --- a/src/test_vectors/commitment_tree.rs +++ b/src/test_vectors/commitment_tree.rs @@ -1,8 +1,8 @@ -pub(crate) struct TestVector { +pub struct TestVector { pub empty_roots: [[u8; 32]; 33], } -pub(crate) fn test_vectors() -> TestVector { +pub fn test_vectors() -> TestVector { // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/orchard_empty_roots.py TestVector { empty_roots: [ diff --git a/src/test_vectors/keys.rs b/src/test_vectors/keys.rs index 5551a268d..d6140538b 100644 --- a/src/test_vectors/keys.rs +++ b/src/test_vectors/keys.rs @@ -2,29 +2,29 @@ use alloc::vec::Vec; -pub(crate) struct TestVector { - pub(crate) sk: [u8; 32], - pub(crate) ask: [u8; 32], - pub(crate) ak: [u8; 32], - pub(crate) nk: [u8; 32], - pub(crate) rivk: [u8; 32], - pub(crate) ivk: [u8; 32], - pub(crate) ovk: [u8; 32], - pub(crate) dk: [u8; 32], - pub(crate) default_d: [u8; 11], - pub(crate) default_pk_d: [u8; 32], - pub(crate) internal_rivk: [u8; 32], - pub(crate) internal_ivk: [u8; 32], - pub(crate) internal_ovk: [u8; 32], - pub(crate) internal_dk: [u8; 32], - pub(crate) note_v: u64, - pub(crate) note_rho: [u8; 32], - pub(crate) note_rseed: [u8; 32], - pub(crate) note_cmx: [u8; 32], - pub(crate) note_nf: [u8; 32], +pub struct TestVector { + pub sk: [u8; 32], + pub ask: [u8; 32], + pub ak: [u8; 32], + pub nk: [u8; 32], + pub rivk: [u8; 32], + pub ivk: [u8; 32], + pub ovk: [u8; 32], + pub dk: [u8; 32], + pub default_d: [u8; 11], + pub default_pk_d: [u8; 32], + pub internal_rivk: [u8; 32], + pub internal_ivk: [u8; 32], + pub internal_ovk: [u8; 32], + pub internal_dk: [u8; 32], + pub note_v: u64, + pub note_rho: [u8; 32], + pub note_rseed: [u8; 32], + pub note_cmx: [u8; 32], + pub note_nf: [u8; 32], } -pub(crate) fn test_vectors() -> Vec { +pub fn test_vectors() -> Vec { // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/orchard_key_components.py vec![ TestVector { diff --git a/src/test_vectors/merkle_path.rs b/src/test_vectors/merkle_path.rs index dc3a26fd2..0c8043c19 100644 --- a/src/test_vectors/merkle_path.rs +++ b/src/test_vectors/merkle_path.rs @@ -1,12 +1,12 @@ use alloc::vec::Vec; -pub(crate) struct TestVector { - pub(crate) leaves: [[u8; 32]; 16], - pub(crate) paths: [[[u8; 32]; 4]; 16], - pub(crate) root: [u8; 32], +pub struct TestVector { + pub leaves: [[u8; 32]; 16], + pub paths: [[[u8; 32]; 4]; 16], + pub root: [u8; 32], } -pub(crate) fn test_vectors() -> Vec { +pub fn test_vectors() -> Vec { // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/orchard_merkle_tree.py vec![ TestVector { diff --git a/src/test_vectors/note_encryption.rs b/src/test_vectors/note_encryption.rs index fc7c5ad5f..5e0f82c40 100644 --- a/src/test_vectors/note_encryption.rs +++ b/src/test_vectors/note_encryption.rs @@ -2,29 +2,29 @@ use alloc::vec::Vec; -pub(crate) struct TestVector { - pub(crate) incoming_viewing_key: [u8; 64], - pub(crate) ovk: [u8; 32], - pub(crate) default_d: [u8; 11], - pub(crate) default_pk_d: [u8; 32], - pub(crate) v: u64, - pub(crate) rseed: [u8; 32], - pub(crate) memo: [u8; 512], - pub(crate) cv_net: [u8; 32], - pub(crate) nf_old: [u8; 32], - pub(crate) cmx: [u8; 32], - pub(crate) esk: [u8; 32], - pub(crate) ephemeral_key: [u8; 32], - pub(crate) shared_secret: [u8; 32], - pub(crate) k_enc: [u8; 32], - pub(crate) p_enc: [u8; 564], - pub(crate) c_enc: [u8; 580], - pub(crate) ock: [u8; 32], - pub(crate) op: [u8; 64], - pub(crate) c_out: [u8; 80], +pub struct TestVector { + pub incoming_viewing_key: [u8; 64], + pub ovk: [u8; 32], + pub default_d: [u8; 11], + pub default_pk_d: [u8; 32], + pub v: u64, + pub rseed: [u8; 32], + pub memo: [u8; 512], + pub cv_net: [u8; 32], + pub nf_old: [u8; 32], + pub cmx: [u8; 32], + pub esk: [u8; 32], + pub ephemeral_key: [u8; 32], + pub shared_secret: [u8; 32], + pub k_enc: [u8; 32], + pub p_enc: [u8; 564], + pub c_enc: [u8; 580], + pub ock: [u8; 32], + pub op: [u8; 64], + pub c_out: [u8; 80], } -pub(crate) fn test_vectors() -> Vec { +pub fn test_vectors() -> Vec { // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/orchard_note_encryption.py vec![ TestVector { diff --git a/src/test_vectors/zip32.rs b/src/test_vectors/zip32.rs index 755294dc9..ae8a8f212 100644 --- a/src/test_vectors/zip32.rs +++ b/src/test_vectors/zip32.rs @@ -1,14 +1,14 @@ //! Test vectors for Orchard ZIP 32 key derivation. -pub(crate) struct TestVector { - pub(crate) sk: [u8; 32], - pub(crate) c: [u8; 32], - pub(crate) xsk: [u8; 73], - pub(crate) fp: [u8; 32], +pub struct TestVector { + pub sk: [u8; 32], + pub c: [u8; 32], + pub xsk: [u8; 73], + pub fp: [u8; 32], } // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/orchard_zip32.py -pub(crate) const TEST_VECTORS: &[TestVector] = &[ +pub const TEST_VECTORS: &[TestVector] = &[ TestVector { sk: [ 0x7e, 0xee, 0x3c, 0x10, 0x17, 0x87, 0x09, 0x90, 0xa3, 0xdd, 0x68, 0x91, 0xb8, 0x2f, diff --git a/src/tree.rs b/src/tree.rs index dee5b3ac8..61b02f0ab 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -27,7 +27,8 @@ use subtle::{Choice, ConditionallySelectable, CtOption}; // lazy_static! { static ref UNCOMMITTED_ORCHARD: pallas::Base = pallas::Base::from(2); - pub(crate) static ref EMPTY_ROOTS: Vec = { + /// Pre-computed empty roots for each level of the Orchard note commitment tree. + pub static ref EMPTY_ROOTS: Vec = { iter::empty() .chain(Some(MerkleHashOrchard::empty_leaf())) .chain( @@ -68,7 +69,8 @@ impl Anchor { Anchor(MerkleHashOrchard::empty_root(Level::from(MERKLE_DEPTH_ORCHARD as u8)).0) } - pub(crate) fn inner(&self) -> pallas::Base { + /// Returns the inner base field element. + pub fn inner(&self) -> pallas::Base { self.0 } @@ -115,7 +117,8 @@ impl From> for MerklePa impl MerklePath { /// Generates a dummy Merkle path for use in dummy spent notes. - pub(crate) fn dummy(mut rng: &mut impl RngCore) -> Self { + /// Generates a dummy Merkle path for use in padded note slots. + pub fn dummy(mut rng: &mut impl RngCore) -> Self { MerklePath { position: rng.next_u32(), auth_path: [(); MERKLE_DEPTH_ORCHARD] @@ -124,7 +127,7 @@ impl MerklePath { } /// Instantiates a new Merkle path given a leaf position and authentication path. - pub(crate) fn new(position: u32, auth_path: [pallas::Base; MERKLE_DEPTH_ORCHARD]) -> Self { + pub fn new(position: u32, auth_path: [pallas::Base; MERKLE_DEPTH_ORCHARD]) -> Self { Self::from_parts(position, auth_path.map(MerkleHashOrchard)) } @@ -182,8 +185,8 @@ impl MerkleHashOrchard { MerkleHashOrchard(value.inner()) } - /// Only used in the circuit. - pub(crate) fn inner(&self) -> pallas::Base { + /// Returns the inner base field element. + pub fn inner(&self) -> pallas::Base { self.0 } diff --git a/src/value.rs b/src/value.rs index 053ad5838..27b61fc12 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,41 +1,4 @@ -//! Monetary values within the Orchard shielded pool. -//! -//! Values are represented in three places within the Orchard protocol: -//! - [`NoteValue`], the value of an individual note. It is an unsigned 64-bit integer -//! (with maximum value [`MAX_NOTE_VALUE`]), and is serialized in a note plaintext. -//! - [`ValueSum`], the sum of note values within an Orchard [`Action`] or [`Bundle`]. -//! It is a signed 64-bit integer (with range [`VALUE_SUM_RANGE`]). -//! - `valueBalanceOrchard`, which is a signed 63-bit integer. This is represented -//! by a user-defined type parameter on [`Bundle`], returned by -//! [`Bundle::value_balance`] and [`Builder::value_balance`]. -//! -//! If your specific instantiation of the Orchard protocol requires a smaller bound on -//! valid note values (for example, Zcash's `MAX_MONEY` fits into a 51-bit integer), you -//! should enforce this in two ways: -//! -//! - 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_output`]. -//! -//! Inside the circuit, note values are constrained to be unsigned 64-bit integers. -//! -//! # Caution! -//! -//! An `i64` is _not_ a signed 64-bit integer! The [Rust documentation] calls `i64` the -//! 64-bit signed integer type, which is true in the sense that its encoding in memory -//! takes up 64 bits. Numerically, however, `i64` is a signed 63-bit integer. -//! -//! Fortunately, users of this crate should never need to construct [`ValueSum`] directly; -//! you should only need to interact with [`NoteValue`] (which can be safely constructed -//! from a `u64`) and `valueBalanceOrchard` (which can be represented as an `i64`). -//! -//! [`Action`]: crate::action::Action -//! [`Bundle`]: crate::bundle::Bundle -//! [`Bundle::value_balance`]: crate::bundle::Bundle::value_balance -//! [`Builder::value_balance`]: crate::builder::Builder::value_balance -//! [`Builder::add_output`]: crate::builder::Builder::add_output -//! [Rust documentation]: https://doc.rust-lang.org/stable/std/primitive.i64.html +//! Monetary value types and value commitments for the Orchard shielded pool. use core::fmt::{self, Debug}; use core::iter::Sum; @@ -100,7 +63,8 @@ impl std::error::Error for BalanceError {} pub struct NoteValue(u64); impl NoteValue { - pub(crate) fn zero() -> Self { + /// Returns a zero note value. + pub fn zero() -> Self { // Default for u64 is zero. Default::default() } @@ -118,15 +82,18 @@ impl NoteValue { NoteValue(value) } - pub(crate) fn from_bytes(bytes: [u8; 8]) -> Self { + /// Deserializes a note value from little-endian bytes. + pub fn from_bytes(bytes: [u8; 8]) -> Self { NoteValue(u64::from_le_bytes(bytes)) } - pub(crate) fn to_bytes(self) -> [u8; 8] { + /// Serializes this note value to little-endian bytes. + pub fn to_bytes(self) -> [u8; 8] { self.0.to_le_bytes() } - pub(crate) fn to_le_bits(self) -> BitArray<[u8; 8], Lsb0> { + /// Returns the little-endian bit representation of this note value. + pub fn to_le_bits(self) -> BitArray<[u8; 8], Lsb0> { BitArray::<_, Lsb0>::new(self.0.to_le_bytes()) } } @@ -166,7 +133,8 @@ pub enum Sign { pub struct ValueSum(i128); impl ValueSum { - pub(crate) fn zero() -> Self { + /// Returns a zero value sum. + pub fn zero() -> Self { // Default for i128 is zero. Default::default() } @@ -177,12 +145,12 @@ impl ValueSum { /// in `Bundle::binding_validating_key`, where we are converting from the user-defined /// `valueBalance` type that enforces any additional constraints on the value's valid /// range. - pub(crate) fn from_raw(value: i64) -> Self { + pub fn from_raw(value: i64) -> Self { ValueSum(value as i128) } /// Constructs a value sum from its magnitude and sign. - pub(crate) fn from_magnitude_sign(magnitude: u64, sign: Sign) -> Self { + pub fn from_magnitude_sign(magnitude: u64, sign: Sign) -> Self { Self(match sign { Sign::Positive => magnitude as i128, Sign::Negative => -(magnitude as i128), @@ -250,7 +218,8 @@ impl TryFrom for i64 { pub struct ValueCommitTrapdoor(pallas::Scalar); impl ValueCommitTrapdoor { - pub(crate) fn inner(&self) -> pallas::Scalar { + /// Returns the inner scalar value. + pub fn inner(&self) -> pallas::Scalar { self.0 } @@ -297,16 +266,17 @@ impl<'a> Sum<&'a ValueCommitTrapdoor> for ValueCommitTrapdoor { impl ValueCommitTrapdoor { /// Generates a new value commitment trapdoor. - pub(crate) fn random(rng: impl RngCore) -> Self { + pub fn random(rng: impl RngCore) -> Self { ValueCommitTrapdoor(pallas::Scalar::random(rng)) } /// Returns the zero trapdoor, which provides no blinding. - pub(crate) fn zero() -> Self { + pub fn zero() -> Self { ValueCommitTrapdoor(pallas::Scalar::zero()) } - pub(crate) fn into_bsk(self) -> redpallas::SigningKey { + /// Converts this trapdoor into a RedPallas binding signing key. + pub fn into_bsk(self) -> redpallas::SigningKey { // TODO: impl From for redpallas::SigningKey. self.0.to_repr().try_into().unwrap() } @@ -370,7 +340,8 @@ impl ValueCommitment { ValueCommitment(V * value + R * rcv.0) } - pub(crate) fn into_bvk(self) -> redpallas::VerificationKey { + /// Converts this commitment into a RedPallas binding verification key. + pub fn into_bvk(self) -> redpallas::VerificationKey { // TODO: impl From for redpallas::VerificationKey. self.0.to_bytes().try_into().unwrap() } @@ -386,7 +357,7 @@ impl ValueCommitment { } /// x-coordinate of this value commitment. - pub(crate) fn x(&self) -> pallas::Base { + pub fn x(&self) -> pallas::Base { if self.0 == pallas::Point::identity() { pallas::Base::zero() } else { @@ -395,7 +366,7 @@ impl ValueCommitment { } /// y-coordinate of this value commitment. - pub(crate) fn y(&self) -> pallas::Base { + pub fn y(&self) -> pallas::Base { if self.0 == pallas::Point::identity() { pallas::Base::zero() } else { diff --git a/src/zip32.rs b/src/zip32.rs index 1454e7930..0666302af 100644 --- a/src/zip32.rs +++ b/src/zip32.rs @@ -28,6 +28,8 @@ pub enum Error { InvalidSpendingKey, /// A child index in a derivation path exceeded 2^31 InvalidChildIndex(u32), + /// Derivation depth would exceed 255 + MaxDerivationDepth, } impl fmt::Display for Error { @@ -137,7 +139,7 @@ impl hardened_only::Context for Orchard { /// /// [orchardextendedkeys]: https://zips.z.cash/zip-0032#orchard-extended-keys #[derive(Debug, Clone)] -pub(crate) struct ExtendedSpendingKey { +pub struct ExtendedSpendingKey { depth: u8, parent_fvk_tag: FvkTag, child_index: KeyIndex, @@ -203,6 +205,8 @@ impl ExtendedSpendingKey { /// /// Discards index if it results in an invalid sk fn derive_child(&self, index: ChildIndex) -> Result { + let depth = self.depth.checked_add(1).ok_or(Error::MaxDerivationDepth)?; + let child_i = self.inner.derive_child(index); let sk = SpendingKey::from_bytes(*child_i.parts().0); @@ -213,7 +217,7 @@ impl ExtendedSpendingKey { let fvk: FullViewingKey = self.into(); Ok(Self { - depth: self.depth + 1, + depth, parent_fvk_tag: FvkFingerprint::from(&fvk).tag(), child_index: KeyIndex::child(index), inner: child_i, @@ -246,6 +250,22 @@ mod tests { assert!(xsk_5.is_ok()); } + #[test] + fn derive_child_depth_overflow() { + let seed = [0; 32]; + let mut xsk = ExtendedSpendingKey::master(&seed).unwrap(); + + let i_5 = ChildIndex::hardened(5); + for _ in 0..255 { + xsk = xsk.derive_child(i_5).unwrap(); + } + assert_eq!(xsk.depth, 255); + assert!(matches!( + xsk.derive_child(i_5), + Err(Error::MaxDerivationDepth) + )); + } + #[test] fn path() { let seed = [0; 32];