Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a232188
Move circuit-dependent test utils behind the `circuit` feature.
nuttycom Jan 25, 2025
116c6e3
Work around missing dependencies on updated ubuntu-latest
nuttycom Jan 29, 2025
2fa54f9
Avoid unused import warnings in downstream crates.
nuttycom Jan 28, 2025
92bd43a
Fix additional problems with missing feature flags in test code.
nuttycom May 8, 2025
cc05aee
Update `subtle` dependency to version `2.6`
nuttycom May 8, 2025
ca77cd8
Release orchard version 0.10.2
nuttycom May 8, 2025
a41d126
Merge pull request #465 from nuttycom/release/0.10.2
nuttycom May 8, 2025
22d4630
pczt: Support applying external spendAuthSigs to Spends
str4d Nov 21, 2025
a799082
Merge pull request #472 from zcash/pczt-external-sigs
nuttycom Nov 25, 2025
88cf5f9
Migrate to published `halo2_gadgets 0.4`
str4d Dec 4, 2025
b2e0a42
Make all error enums `#[non_exhaustive]`
str4d Dec 4, 2025
9bf58f0
Merge remote-tracking branch 'origin/hotfix/0.10.x' into more-release…
str4d Dec 4, 2025
fcf1cb1
Migrated all error types to be `#[non_exhaustive]` enums
str4d Dec 5, 2025
3661d41
`impl std::error::Error` for all error types
str4d Dec 5, 2025
ddbff98
Merge pull request #473 from zcash/more-release-prep
nuttycom Dec 5, 2025
648ef34
orchard 0.12.0
str4d Dec 5, 2025
17f835d
Merge pull request #474 from zcash/release-0.12.0
str4d Dec 5, 2025
49d01cc
CI: Use pinned dependencies where possible for `build-nostd`
str4d Mar 2, 2026
ecc1fb6
Merge pull request #478 from zcash/fix-ci
nuttycom Mar 2, 2026
b7e95f5
Make pczt::Bundle::extract take `self` by reference.
nuttycom Feb 27, 2026
6b12c77
Fix clippy lints.
nuttycom Feb 27, 2026
64599b8
Merge pull request #477 from zcash/pczt_extract_reference
nuttycom Mar 3, 2026
d45da40
Merge branch 'zcash-main' into merge_upstream_20260309
ConstanceBeguier Mar 9, 2026
4b39a10
Fix issues
ConstanceBeguier Mar 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ jobs:
- name: Show Cargo.toml for the synthetic crate
working-directory: ./ci-build
run: cat Cargo.toml
- name: Copy pinned dependencies into synthetic crate
run: cp crate_root/Cargo.lock ci-build/
- name: Add target
working-directory: ./ci-build
run: rustup target add ${{ matrix.target }}
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/lints-stable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ jobs:
- name: Show Cargo.toml for the synthetic crate
working-directory: ./ci-build
run: cat Cargo.toml
- name: Copy pinned dependencies into synthetic crate
run: cp crate_root/Cargo.lock ci-build/
- name: Add target
working-directory: ./ci-build
run: rustup target add ${{ matrix.target }}
Expand Down
50 changes: 50 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,56 @@ and this project adheres to Rust's notion of

## [Unreleased]

### Changed
- `orchard::pczt::Bundle::extract` now takes its `self` argument by
reference instead of by value.

## [0.12.0] - 2025-12-05

### 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

### Added
Expand Down
12 changes: 9 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "orchard"
version = "0.11.0"
version = "0.12.0"
authors = [
"Sean Bowe <sean@electriccoin.co>",
"Sean Bowe",
"Jack Grigg <jack@electriccoin.co>",
"Daira-Emma Hopwood <daira@jacaranda.org>",
"Ying Tong Lai",
Expand Down Expand Up @@ -42,7 +42,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"
Expand Down
36 changes: 23 additions & 13 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::{
primitives::{OrchardDomain, OrchardPrimitives},
sighash_kind::{OrchardBindingSig, OrchardSighashKind, OrchardSpendAuthSig},
tree::{Anchor, MerklePath},
value::{self, NoteValue, OverflowError, ValueCommitTrapdoor, ValueCommitment, ValueSum},
value::{self, BalanceError, NoteValue, ValueCommitTrapdoor, ValueCommitment, ValueSum},
Proof,
};

Expand Down Expand Up @@ -129,6 +129,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,
Expand All @@ -143,7 +144,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`
Expand Down Expand Up @@ -191,14 +192,15 @@ impl From<halo2_proofs::plonk::Error> for BuildError {
}
}

impl From<value::OverflowError> for BuildError {
fn from(e: value::OverflowError) -> Self {
impl From<value::BalanceError> 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,
Expand All @@ -222,13 +224,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",
})
}
}

Expand Down Expand Up @@ -673,7 +682,7 @@ impl Builder {
) -> Result<(), OutputError> {
let flags = self.bundle_type.flags();
if !flags.outputs_enabled() {
return Err(OutputError);
return Err(OutputError::OutputsDisabled);
}

self.outputs
Expand Down Expand Up @@ -730,7 +739,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<V: TryFrom<i64>>(&self) -> Result<V, value::OverflowError> {
pub fn value_balance<V: TryFrom<i64>>(&self) -> Result<V, value::BalanceError> {
let value_balance = self
.spends
.iter()
Expand All @@ -743,8 +752,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.
Expand Down Expand Up @@ -888,7 +898,7 @@ pub fn bundle<V: TryFrom<i64>, FL: OrchardFlavor>(
i64::try_from(value_balance).map_err(BuildError::ValueSum)?;

let result_value_balance = V::try_from(zatoshi_value_balance)
.map_err(|_| BuildError::ValueSum(value::OverflowError))?;
.map_err(|_| BuildError::ValueSum(value::BalanceError::Overflow))?;

// Compute the transaction binding signing key.
let bsk = pre_actions
Expand Down Expand Up @@ -1076,7 +1086,7 @@ fn build_bundle<B, R: RngCore>(
.iter()
.filter(|action| action.spend.note.asset().is_zatoshi().into())
.try_fold(ValueSum::zero(), |acc, action| acc + action.value_sum())
.ok_or(OverflowError)?;
.ok_or(BalanceError::Overflow)?;

let burn_vec = burn.into_iter().collect();

Expand Down
4 changes: 4 additions & 0 deletions src/pczt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,10 @@ mod tests {
for action in pczt_bundle.actions_mut() {
if action.spend.zip32_derivation.as_ref() == Some(&zip32_derivation) {
action.sign(sighash, &ask, rng).unwrap();

// We can also apply the signature as an external signature.
let signature = action.spend().spend_auth_sig().clone().expect("signed");
action.apply_signature(sighash, signature).unwrap();
}
}

Expand Down
24 changes: 24 additions & 0 deletions src/pczt/io_finalizer.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::fmt;

use alloc::vec::Vec;

use rand::{CryptoRng, RngCore};
Expand Down Expand Up @@ -61,6 +63,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),
Expand All @@ -70,3 +73,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 {}
34 changes: 34 additions & 0 deletions src/pczt/parse.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::fmt;

use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
Expand Down Expand Up @@ -298,6 +300,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,
Expand Down Expand Up @@ -338,3 +341,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 {}
40 changes: 40 additions & 0 deletions src/pczt/prover.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::fmt;

use alloc::vec::Vec;

use halo2_proofs::plonk;
Expand Down Expand Up @@ -114,6 +116,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,
Expand Down Expand Up @@ -142,3 +145,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 {}
Loading
Loading