diff --git a/CHANGELOG.md b/CHANGELOG.md index 510f9d44b..749598bc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,47 @@ and this project adheres to Rust's notion of ### Added - `orchard::pczt::Action::apply_signature` +- `orchard::value::BalanceError` +- `impl std::error::Error` for the following errors: + - `orchard::pczt`: + - `IoFinalizerError` + - `ParseError` + - `ProverError` + - `SignerError` + - `TxExtractorError` + - `UpdaterError` + - `VerifyError` + - `orchard::zip32::Error` ### Changed +- `orchard::builder::BuildError::ValueSum` variant now contains + `orchard::value::BalanceError`. - `orchard::pczt::SignerError` has added variants: - `InvalidExternalSignature` +- All error enums in this crate are now `#[non_exhaustive]`, to allow future + error variants to be added without a SemVer break: + - `orchard::builder`: + - `BuildError` + - `SpendError` + - `orchard::pczt`: + - `IoFinalizerError` + - `ParseError` + - `ProverError` + - `SignerError` + - `TxExtractorError` + - `UpdaterError` + - `VerifyError` + - `orchard::zip32::Error` +- `orchard::builder::OutputError` has been changed from a zero-sized struct to + a `#[non_exhaustive]` enum with (for now) a single variant. + +### Removed +- `orchard::value::OverflowError` (use `BalanceError` instead). + +## [0.10.2] - 2025-05-08 + +### Fixed +- Fixes problems in test compilation under `--no-default-features` ## [0.11.0] - 2025-02-20 diff --git a/Cargo.lock b/Cargo.lock index e09110f36..0d05dade1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -975,14 +975,15 @@ dependencies = [ [[package]] name = "halo2_gadgets" -version = "0.3.1" -source = "git+https://github.com/zcash/halo2?rev=2308caf68c48c02468b66cfc452dad54e355e32f#2308caf68c48c02468b66cfc452dad54e355e32f" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45824ce0dd12e91ec0c68ebae2a7ed8ae19b70946624c849add59f1d1a62a143" dependencies = [ "arrayvec", "bitvec", "ff", "group", - "halo2_poseidon 0.1.0 (git+https://github.com/zcash/halo2?rev=2308caf68c48c02468b66cfc452dad54e355e32f)", + "halo2_poseidon", "halo2_proofs", "lazy_static", "pasta_curves", @@ -1011,21 +1012,11 @@ dependencies = [ "pasta_curves", ] -[[package]] -name = "halo2_poseidon" -version = "0.1.0" -source = "git+https://github.com/zcash/halo2?rev=2308caf68c48c02468b66cfc452dad54e355e32f#2308caf68c48c02468b66cfc452dad54e355e32f" -dependencies = [ - "bitvec", - "ff", - "group", - "pasta_curves", -] - [[package]] name = "halo2_proofs" -version = "0.3.1" -source = "git+https://github.com/zcash/halo2?rev=2308caf68c48c02468b66cfc452dad54e355e32f#2308caf68c48c02468b66cfc452dad54e355e32f" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05713f117155643ce10975e0bee44a274bcda2f4bb5ef29a999ad67c1fa8d4d3" dependencies = [ "blake2b_simd", "ff", @@ -1459,7 +1450,7 @@ dependencies = [ "getset", "group", "halo2_gadgets", - "halo2_poseidon 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "halo2_poseidon", "halo2_proofs", "hex", "image", diff --git a/Cargo.toml b/Cargo.toml index 07b1a66e9..9d4b330d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ nonempty = { version = "0.11", default-features = false } poseidon = { package = "halo2_poseidon", version = "0.1" } serde = { version = "1.0", default-features = false, features = ["derive"] } sinsemilla = "0.1" -subtle = { version = "2.3", default-features = false } +subtle = { version = "2.6", default-features = false } zcash_note_encryption = "0.4" incrementalmerkletree = "0.8.1" zcash_spec = "0.2.1" @@ -49,7 +49,7 @@ zip32 = { version = "0.2.0", default-features = false } visibility = "0.1.1" # Circuit -halo2_gadgets = { version = "0.3", optional = true, default-features = false } +halo2_gadgets = { version = "0.4", optional = true, default-features = false } halo2_proofs = { version = "0.3", optional = true, default-features = false, features = ["batch", "floor-planner-v1-legacy-pdqsort"] } # Boilerplate @@ -67,7 +67,7 @@ plotters = { version = "0.3.0", optional = true } [dev-dependencies] criterion = "0.4" # 0.5 depends on clap 4 which has MSRV 1.70 -halo2_gadgets = { version = "0.3", features = ["test-dependencies"] } +halo2_gadgets = { version = "0.4", features = ["test-dependencies"] } hex = "0.4" proptest = ">=1.0.0, <1.7.0" zcash_note_encryption = { version = "0.4", features = ["pre-zip-212"] } @@ -107,7 +107,3 @@ debug = true [profile.bench] debug = true - -[patch.crates-io] -halo2_gadgets = { git = "https://github.com/zcash/halo2", rev = "2308caf68c48c02468b66cfc452dad54e355e32f" } -halo2_proofs = { git = "https://github.com/zcash/halo2", rev = "2308caf68c48c02468b66cfc452dad54e355e32f" } diff --git a/src/builder.rs b/src/builder.rs index 5566e3a39..363a136c4 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -20,7 +20,7 @@ use crate::{ note_encryption::OrchardNoteEncryption, primitives::redpallas::{self, Binding, SpendAuth}, tree::{Anchor, MerklePath}, - value::{self, NoteValue, OverflowError, ValueCommitTrapdoor, ValueCommitment, ValueSum}, + value::{self, BalanceError, NoteValue, ValueCommitTrapdoor, ValueCommitment, ValueSum}, Proof, }; @@ -117,6 +117,7 @@ impl BundleType { /// An error type for the kinds of errors that can occur during bundle construction. #[derive(Debug)] +#[non_exhaustive] pub enum BuildError { /// Spends are disabled for the provided bundle type. SpendsDisabled, @@ -131,7 +132,7 @@ pub enum BuildError { Proof(halo2_proofs::plonk::Error), /// An overflow error occurred while attempting to construct the value /// for a bundle. - ValueSum(value::OverflowError), + ValueSum(value::BalanceError), /// External signature is not valid. InvalidExternalSignature, /// A signature is valid for more than one input. This should never happen if `alpha` @@ -173,14 +174,15 @@ impl From for BuildError { } } -impl From for BuildError { - fn from(e: value::OverflowError) -> Self { +impl From for BuildError { + fn from(e: value::BalanceError) -> Self { BuildError::ValueSum(e) } } /// An error type for adding a spend to the builder. #[derive(Debug, PartialEq, Eq)] +#[non_exhaustive] pub enum SpendError { /// Spends aren't enabled for this builder. SpendsDisabled, @@ -204,13 +206,20 @@ impl fmt::Display for SpendError { #[cfg(feature = "std")] impl std::error::Error for SpendError {} -/// The only error that can occur here is if outputs are disabled for this builder. +/// An error type for adding an output to the builder. #[derive(Debug, PartialEq, Eq)] -pub struct OutputError; +#[non_exhaustive] +pub enum OutputError { + /// Outputs aren't enabled for this builder. + OutputsDisabled, +} impl fmt::Display for OutputError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Outputs are not enabled for this builder") + use OutputError::*; + f.write_str(match self { + OutputsDisabled => "Outputs are not enabled for this builder", + }) } } @@ -594,7 +603,7 @@ impl Builder { ) -> Result<(), OutputError> { let flags = self.bundle_type.flags(); if !flags.outputs_enabled() { - return Err(OutputError); + return Err(OutputError::OutputsDisabled); } self.outputs @@ -625,7 +634,7 @@ impl Builder { /// /// [added]: https://zips.z.cash/protocol/protocol.pdf#orchardbalance /// [must not have a negative value]: https://zips.z.cash/protocol/protocol.pdf#transactions - pub fn value_balance>(&self) -> Result { + pub fn value_balance>(&self) -> Result { let value_balance = self .spends .iter() @@ -636,8 +645,9 @@ impl Builder { .map(|output| NoteValue::zero() - output.value), ) .try_fold(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)) + .ok_or(BalanceError::Overflow)?; + i64::try_from(value_balance) + .and_then(|i| V::try_from(i).map_err(|_| value::BalanceError::Overflow)) } /// Builds a bundle containing the given spent notes and outputs. @@ -715,7 +725,7 @@ pub fn bundle>( let result_value_balance: V = i64::try_from(value_balance) .map_err(BuildError::ValueSum) .and_then(|i| { - V::try_from(i).map_err(|_| BuildError::ValueSum(value::OverflowError)) + V::try_from(i).map_err(|_| BuildError::ValueSum(value::BalanceError::Overflow)) })?; // Compute the transaction binding signing key. @@ -833,7 +843,7 @@ fn build_bundle( let value_balance = pre_actions .iter() .try_fold(ValueSum::zero(), |acc, action| acc + action.value_sum()) - .ok_or(OverflowError)?; + .ok_or(BalanceError::Overflow)?; finisher(pre_actions, flags, value_balance, bundle_meta, rng) } @@ -1271,7 +1281,7 @@ pub mod testing { } } -#[cfg(test)] +#[cfg(all(test, feature = "circuit"))] mod tests { use rand::rngs::OsRng; diff --git a/src/constants/sinsemilla.rs b/src/constants/sinsemilla.rs index 84198f39e..34843fcca 100644 --- a/src/constants/sinsemilla.rs +++ b/src/constants/sinsemilla.rs @@ -132,7 +132,7 @@ impl CommitDomains for Or } } -#[cfg(test)] +#[cfg(all(test, feature = "circuit"))] mod tests { use super::*; use crate::constants::{ diff --git a/src/pczt.rs b/src/pczt.rs index e4872f900..854cd00ae 100644 --- a/src/pczt.rs +++ b/src/pczt.rs @@ -330,7 +330,7 @@ impl Zip32Derivation { } } -#[cfg(test)] +#[cfg(all(test, feature = "circuit"))] mod tests { use ff::{Field, PrimeField}; use incrementalmerkletree::{Marking, Retention}; diff --git a/src/pczt/io_finalizer.rs b/src/pczt/io_finalizer.rs index 6f9a7ec9c..73f7d00d8 100644 --- a/src/pczt/io_finalizer.rs +++ b/src/pczt/io_finalizer.rs @@ -1,3 +1,5 @@ +use core::fmt; + use alloc::vec::Vec; use rand::{CryptoRng, RngCore}; @@ -60,6 +62,7 @@ impl super::Bundle { /// Errors that can occur while finalizing the I/O for a PCZT bundle. #[derive(Debug)] +#[non_exhaustive] pub enum IoFinalizerError { /// An error occurred while signing a dummy spend. DummySignature(SignerError), @@ -69,3 +72,24 @@ pub enum IoFinalizerError { /// inconsistent. ValueCommitMismatch, } + +impl fmt::Display for IoFinalizerError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + IoFinalizerError::DummySignature(e) => { + write!(f, "An error occurred while signing a dummy spend: {e}") + } + IoFinalizerError::MissingValueCommitTrapdoor => write!( + f, + "The IO Finalizer role requires all `rcv` fields to be set" + ), + IoFinalizerError::ValueCommitMismatch => write!( + f, + "`cv_net`, `rcv`, and `value_sum` within the Orchard bundle are inconsistent." + ), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for IoFinalizerError {} diff --git a/src/pczt/parse.rs b/src/pczt/parse.rs index f36e09de6..e8e06c10c 100644 --- a/src/pczt/parse.rs +++ b/src/pczt/parse.rs @@ -1,3 +1,5 @@ +use core::fmt; + use alloc::collections::BTreeMap; use alloc::string::String; use alloc::vec::Vec; @@ -294,6 +296,7 @@ impl Zip32Derivation { /// Errors that can occur while parsing a PCZT bundle. #[derive(Debug)] +#[non_exhaustive] pub enum ParseError { /// An invalid anchor was provided. InvalidAnchor, @@ -334,3 +337,34 @@ pub enum ParseError { /// The provided `flags` field had unexpected bits set. UnexpectedFlagBitsSet, } + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ParseError::InvalidAnchor => write!(f, "invalid anchor"), + ParseError::InvalidBindingSignatureSigningKey => write!(f, "invalid `bsk`"), + ParseError::InvalidDummySpendingKey => write!(f, "invalid `dummy_sk`"), + ParseError::InvalidEncCiphertext => write!(f, "invalid `enc_ciphertext`"), + ParseError::InvalidExtractedNoteCommitment => write!(f, "invalid `cmx`"), + ParseError::InvalidFullViewingKey => write!(f, "invalid `fvk`"), + ParseError::InvalidNullifier => write!(f, "invalid `nullifier`"), + ParseError::InvalidOutCiphertext => write!(f, "invalid `out_ciphertext`"), + ParseError::InvalidRandomizedKey => write!(f, "invalid `rk`"), + ParseError::InvalidRandomSeed => write!(f, "invalid `rseed`"), + ParseError::InvalidRecipient => write!(f, "invalid `recipient`"), + ParseError::InvalidRho => write!(f, "invalid `rho`"), + ParseError::InvalidSpendAuthRandomizer => write!(f, "invalid `alpha`"), + ParseError::InvalidValueCommitment => write!(f, "invalid `cv_net`"), + ParseError::InvalidValueCommitTrapdoor => write!(f, "invalid `rcv`"), + ParseError::InvalidWitness => write!(f, "invalid `witness`"), + ParseError::InvalidZip32Derivation => write!(f, "invalid `zip32_derivation`"), + ParseError::MissingRho => { + write!(f, "`rho` must be provided whenever `rseed` is provided") + } + ParseError::UnexpectedFlagBitsSet => write!(f, "`flags` field had unexpected bits set"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ParseError {} diff --git a/src/pczt/prover.rs b/src/pczt/prover.rs index 73ce0c863..b585997d3 100644 --- a/src/pczt/prover.rs +++ b/src/pczt/prover.rs @@ -1,3 +1,5 @@ +use core::fmt; + use alloc::vec::Vec; use halo2_proofs::plonk; @@ -108,6 +110,7 @@ impl super::Bundle { /// Errors that can occur while creating Orchard proofs for a PCZT. #[derive(Debug)] +#[non_exhaustive] pub enum ProverError { /// The output note's components do not produce a valid note commitment. InvalidOutputNote, @@ -136,3 +139,40 @@ pub enum ProverError { /// The provided `fvk` does not own the spent note. WrongFvkForNote, } + +impl fmt::Display for ProverError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ProverError::InvalidOutputNote => write!(f, "output note is invalid"), + ProverError::InvalidSpendNote => write!(f, "spent note is invalid"), + ProverError::MissingFullViewingKey => { + write!(f, "`fvk` must be set for the Prover role") + } + ProverError::MissingRandomSeed => { + write!(f, "`rseed` fields must be set for the Prover role") + } + ProverError::MissingRecipient => { + write!(f, "`recipient` fields must be set for the Prover role") + } + ProverError::MissingRho => write!(f, "`rho` must be set for the Prover role"), + ProverError::MissingSpendAuthRandomizer => { + write!(f, "`alpha` must be set for the Prover role") + } + ProverError::MissingValue => { + write!(f, "`value` fields must be set for the Prover role") + } + ProverError::MissingValueCommitTrapdoor => { + write!(f, "`rcv` must be set for the Prover role") + } + ProverError::MissingWitness => write!(f, "`witness` must be set for the Prover role"), + ProverError::ProofFailed(e) => write!(f, "Failed to create proof: {e}"), + ProverError::RhoMismatch => { + write!(f, "output's `rho` does not match spent note's nullifier") + } + ProverError::WrongFvkForNote => write!(f, "`fvk` does not own the action's spent note"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ProverError {} diff --git a/src/pczt/signer.rs b/src/pczt/signer.rs index 4ba3480b5..f31977710 100644 --- a/src/pczt/signer.rs +++ b/src/pczt/signer.rs @@ -1,3 +1,5 @@ +use core::fmt; + use rand::{CryptoRng, RngCore}; use crate::{ @@ -54,6 +56,7 @@ impl super::Action { /// Errors that can occur while signing an Orchard action in a PCZT. #[derive(Debug)] +#[non_exhaustive] pub enum SignerError { /// A provided external signature was not valid for the action's spend. InvalidExternalSignature, @@ -62,3 +65,22 @@ pub enum SignerError { /// The provided `ask` does not own the action's spent note. WrongSpendAuthorizingKey, } + +impl fmt::Display for SignerError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SignerError::InvalidExternalSignature => { + write!(f, "External signature is invalid for the action's spend") + } + SignerError::MissingSpendAuthRandomizer => { + write!(f, "`alpha` must be set for the Signer role") + } + SignerError::WrongSpendAuthorizingKey => { + write!(f, "provided `ask` does not own the action's spent note") + } + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for SignerError {} diff --git a/src/pczt/tx_extractor.rs b/src/pczt/tx_extractor.rs index f2daf9e87..e80d60249 100644 --- a/src/pczt/tx_extractor.rs +++ b/src/pczt/tx_extractor.rs @@ -1,3 +1,5 @@ +use core::fmt; + use nonempty::NonEmpty; use rand::{CryptoRng, RngCore}; @@ -104,6 +106,7 @@ impl super::Bundle { /// Errors that can occur while extracting a regular Orchard bundle from a PCZT bundle. #[derive(Debug)] +#[non_exhaustive] pub enum TxExtractorError { /// The Transaction Extractor role requires `bsk` to be set. MissingBindingSignatureSigningKey, @@ -115,6 +118,30 @@ pub enum TxExtractorError { ValueSumOutOfRange, } +impl fmt::Display for TxExtractorError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TxExtractorError::MissingBindingSignatureSigningKey => { + write!(f, "`bsk` must be set for the Transaction Extractor role") + } + TxExtractorError::MissingProof => write!( + f, + "Orchard `zkproof` must be set for the Transaction Extractor role" + ), + TxExtractorError::MissingSpendAuthSig => write!( + f, + "`spend_auth_sig` fields must all be set for the Transaction Extractor role" + ), + TxExtractorError::ValueSumOutOfRange => { + write!(f, "value sum does not fit into a `valueBalance`") + } + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TxExtractorError {} + /// Authorizing data for a bundle of actions that is just missing a binding signature. #[derive(Debug)] pub struct Unbound { diff --git a/src/pczt/updater.rs b/src/pczt/updater.rs index 5d032b1f0..b1214af44 100644 --- a/src/pczt/updater.rs +++ b/src/pczt/updater.rs @@ -1,3 +1,5 @@ +use core::fmt; + use alloc::string::String; use alloc::vec::Vec; @@ -71,7 +73,19 @@ impl ActionUpdater<'_> { /// Errors that can occur while updating an Orchard bundle in a PCZT. #[derive(Debug)] +#[non_exhaustive] pub enum UpdaterError { /// An out-of-bounds index was provided when looking up an action. InvalidIndex, } + +impl fmt::Display for UpdaterError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + UpdaterError::InvalidIndex => write!(f, "Action index is out-of-bounds"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for UpdaterError {} diff --git a/src/pczt/verify.rs b/src/pczt/verify.rs index 463125a25..49249a448 100644 --- a/src/pczt/verify.rs +++ b/src/pczt/verify.rs @@ -1,3 +1,5 @@ +use core::fmt; + use crate::{ keys::{FullViewingKey, SpendValidatingKey}, note::{ExtractedNoteCommitment, Rho}, @@ -143,6 +145,7 @@ impl super::Output { /// Errors that can occur while verifying a PCZT bundle. #[derive(Debug)] +#[non_exhaustive] pub enum VerifyError { /// The output note's components do not produce the expected `cmx`. InvalidExtractedNoteCommitment, @@ -175,3 +178,44 @@ pub enum VerifyError { /// The provided `fvk` does not own the spent note. WrongFvkForNote, } + +impl fmt::Display for VerifyError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + VerifyError::InvalidExtractedNoteCommitment => { + write!(f, "output note doesn't match `cmx`") + } + VerifyError::InvalidNullifier => write!(f, "spent note doesn't match `nullifier`"), + VerifyError::InvalidOutputNote => write!(f, "invalid output note"), + VerifyError::InvalidRandomizedVerificationKey => { + write!(f, "spend's `fvk` and `alpha` do not match `rk`") + } + VerifyError::InvalidSpendNote => write!(f, "invalid spent note"), + VerifyError::InvalidValueCommitment => { + write!(f, "`cv_net` doesn't match the note values and `rcv`") + } + VerifyError::MismatchedFullViewingKey => { + write!(f, "Provided full viewing key doesn't match the `fvk` field") + } + VerifyError::MissingFullViewingKey => write!(f, "`fvk` missing for dummy note"), + VerifyError::MissingRandomSeed => { + write!(f, "`rseed` missing for `nullifier` verification") + } + VerifyError::MissingRecipient => { + write!(f, "`recipient` missing for `nullifier` verification") + } + VerifyError::MissingRho => write!(f, "`rho` missing for `nullifier` verification"), + VerifyError::MissingSpendAuthRandomizer => { + write!(f, "`alpha` missing for `rk` verification") + } + VerifyError::MissingValue => write!(f, "`value` missing"), + VerifyError::MissingValueCommitTrapdoor => { + write!(f, "`rcv` missing for `cv_net` verification") + } + VerifyError::WrongFvkForNote => write!(f, "`fvk` does not own the action's spent note"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for VerifyError {} diff --git a/src/spec.rs b/src/spec.rs index f1c3e4459..c7194562b 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -318,13 +318,15 @@ pub fn i2lebsp(int: u64) -> [bool; NUM_BITS] { mod tests { use super::{i2lebsp, lebs2ip}; - use group::Group; - use halo2_proofs::arithmetic::CurveExt; - use pasta_curves::pallas; use rand::{rngs::OsRng, RngCore}; #[test] + #[cfg(feature = "circuit")] fn diversify_hash_substitution() { + use group::Group; + use halo2_proofs::arithmetic::CurveExt; + use pasta_curves::pallas; + assert!(!bool::from( pallas::Point::hash_to_curve("z.cash:Orchard-gd")(&[]).is_identity() )); diff --git a/src/value.rs b/src/value.rs index 516bf8e18..67ac8787f 100644 --- a/src/value.rs +++ b/src/value.rs @@ -71,18 +71,29 @@ pub const MAX_NOTE_VALUE: u64 = u64::MAX; pub const VALUE_SUM_RANGE: RangeInclusive = -(MAX_NOTE_VALUE as i128)..=MAX_NOTE_VALUE as i128; -/// A value operation overflowed. +/// A type for balance violations in amount addition and subtraction +/// (overflow and underflow of allowed ranges). #[derive(Debug)] -pub struct OverflowError; +#[non_exhaustive] +pub enum BalanceError { + /// Two values were added or subtracted, and the result overflowed the valid range for + /// the value. + /// + /// Normally this range is [`VALUE_SUM_RANGE`], but when interacting with value + /// balances it may be `i64`. + Overflow, +} -impl fmt::Display for OverflowError { +impl fmt::Display for BalanceError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Orchard value operation overflowed") + match self { + Self::Overflow => write!(f, "Orchard value operation overflowed"), + } } } #[cfg(feature = "std")] -impl std::error::Error for OverflowError {} +impl std::error::Error for BalanceError {} /// The non-negative value of an individual Orchard note. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] @@ -212,25 +223,25 @@ impl Add for ValueSum { } } -impl<'a> Sum<&'a ValueSum> for Result { +impl<'a> Sum<&'a ValueSum> for Result { fn sum>(mut iter: I) -> Self { iter.try_fold(ValueSum(0), |acc, v| acc + *v) - .ok_or(OverflowError) + .ok_or(BalanceError::Overflow) } } -impl Sum for Result { +impl Sum for Result { fn sum>(mut iter: I) -> Self { iter.try_fold(ValueSum(0), |acc, v| acc + v) - .ok_or(OverflowError) + .ok_or(BalanceError::Overflow) } } impl TryFrom for i64 { - type Error = OverflowError; + type Error = BalanceError; fn try_from(v: ValueSum) -> Result { - i64::try_from(v.0).map_err(|_| OverflowError) + i64::try_from(v.0).map_err(|_| BalanceError::Overflow) } } @@ -464,7 +475,7 @@ mod tests { use super::{ testing::{arb_note_value_bounded, arb_trapdoor, arb_value_sum_bounded}, - OverflowError, ValueCommitTrapdoor, ValueCommitment, ValueSum, MAX_NOTE_VALUE, + BalanceError, ValueCommitTrapdoor, ValueCommitment, ValueSum, MAX_NOTE_VALUE, }; use crate::primitives::redpallas; @@ -480,7 +491,7 @@ mod tests { let value_balance = values .iter() .map(|(value, _)| value) - .sum::>() + .sum::>() .expect("we generate values that won't overflow"); let bsk = values diff --git a/src/zip32.rs b/src/zip32.rs index 93b1cf5c8..1454e7930 100644 --- a/src/zip32.rs +++ b/src/zip32.rs @@ -22,6 +22,7 @@ const ZIP32_ORCHARD_FVFP_PERSONALIZATION: &[u8; 16] = b"ZcashOrchardFVFP"; /// Errors produced in derivation of extended spending keys #[derive(Debug, PartialEq, Eq)] +#[non_exhaustive] pub enum Error { /// A seed resulted in an invalid spending key InvalidSpendingKey, @@ -35,7 +36,8 @@ impl fmt::Display for Error { } } -//impl std::error::Error for Error {} +#[cfg(feature = "std")] +impl std::error::Error for Error {} /// An Orchard full viewing key fingerprint struct FvkFingerprint([u8; 32]); diff --git a/tests/builder.rs b/tests/builder.rs index dbfd53e0a..85025ab76 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "circuit")] + use incrementalmerkletree::{Hashable, Marking, Retention}; use orchard::{ builder::{Builder, BundleType},