Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
88f04ac
Copy circuit files from remove_orchard_primitives branch
ConstanceBeguier Aug 5, 2026
085b763
Re-apply last modifications from upsatream
ConstanceBeguier Aug 5, 2026
d27629e
Copy src/note/asset_base.rs from remove_orchard_primitives
ConstanceBeguier Aug 5, 2026
08a7d86
Add TODO ZSA
ConstanceBeguier Aug 5, 2026
cebb133
Remove tests from circuit_zsa file
ConstanceBeguier Aug 5, 2026
668ce6e
Copy constants/nullifier_l.rs from remove_orchard_primitives
ConstanceBeguier Aug 5, 2026
1a623a8
Add patch for halo2 in Cargo.toml
ConstanceBeguier Aug 5, 2026
1b3e89f
Copy constants/sinsemilla.rs from remove_orchard_primitives
ConstanceBeguier Aug 5, 2026
1f4edbf
Add patch for sinsemilla in Cargo.toml
ConstanceBeguier Aug 5, 2026
35eff9a
Add NoteCommitTrapdoor::new function
ConstanceBeguier Aug 5, 2026
a88102a
Update visibility of NoteCommitment::derive
ConstanceBeguier Aug 5, 2026
c3dd652
Add asset and rseed_split_note into Note
ConstanceBeguier Aug 5, 2026
605b6e1
Update expected_proof_size function
ConstanceBeguier Aug 5, 2026
4556f5b
Fix from_action_context_common
ConstanceBeguier Aug 5, 2026
a95e381
Add split_flag into SpendInfo
ConstanceBeguier Aug 5, 2026
ff93f2a
Add zsa_enabled flag
ConstanceBeguier Aug 5, 2026
5879844
Remove unused imports
ConstanceBeguier Aug 5, 2026
9a25cc8
Update flags
ConstanceBeguier Aug 5, 2026
3fa3865
Remove some TODO ZSA
ConstanceBeguier Aug 5, 2026
c7e5ad4
Fix circuit_data paths
ConstanceBeguier Aug 5, 2026
a25dcac
Add asset into NoteCommitment::derive function
ConstanceBeguier Aug 5, 2026
da5c21a
Add asset into OutputInfo
ConstanceBeguier Aug 5, 2026
42c2409
Reduce diff with upstream
ConstanceBeguier Aug 5, 2026
6f2a9c4
Move OchardCircuitVersion out of circuit module to avoid feature gating
ConstanceBeguier Aug 5, 2026
fa0db48
Reduce visibility of some structures
ConstanceBeguier Aug 6, 2026
fc22c99
Update changelogs
ConstanceBeguier Aug 6, 2026
c5ca73f
Circuit configure unify (#270)
PaulLaux Aug 24, 2026
21bfb22
Add some checks on enable_zsa
ConstanceBeguier Aug 25, 2026
8335a20
Update nu6_3_flags_parsing_handles_the_cross_address_bit test
ConstanceBeguier Aug 25, 2026
c630436
update visibility of derive_nullifier when feature=unstable-voting-ci…
ConstanceBeguier Aug 25, 2026
d22b81d
Add check in ActionInfo::build_for_pczt
ConstanceBeguier Aug 25, 2026
56476c0
Update comment
ConstanceBeguier Aug 25, 2026
00d93c7
Remove AssetBase::hash
ConstanceBeguier Aug 25, 2026
a5917f4
Restore ValueCommit::derive and add ValueCommit::derive_with_asset
ConstanceBeguier Aug 25, 2026
59fdc71
Add non_echaustive on OrchardCircuitVersion
ConstanceBeguier Aug 25, 2026
a111d57
Add non_exhaustive on OrchardHashDomains
ConstanceBeguier Aug 25, 2026
14833f9
Add ZSA circuit tests
ConstanceBeguier Aug 25, 2026
f731f05
Reduce diff with upstream in circuit.rs
ConstanceBeguier Aug 25, 2026
dde720e
Reduce diff with upstream
ConstanceBeguier Aug 25, 2026
fccc31c
Add a test to pin zsa circuit description
ConstanceBeguier Aug 25, 2026
c4eabd0
Update changelog
ConstanceBeguier Aug 25, 2026
6a36957
Check that spend and output asset match
ConstanceBeguier Aug 25, 2026
033664f
Add TODO ZSA
ConstanceBeguier Aug 25, 2026
dea3298
Update RestrictionUnsupportedByKey comment
ConstanceBeguier Aug 25, 2026
4861917
update comment for OrchardCircuitVersion::ZSA
ConstanceBeguier Aug 25, 2026
6ac887e
Remove orchard_sinsemilla_chip.rs file
ConstanceBeguier Aug 26, 2026
ff917a0
Create a constant for zatoshi asset base
ConstanceBeguier Aug 26, 2026
ecd69c5
Zsa circuit v1 add (#274)
PaulLaux Aug 26, 2026
7b7005b
Update Note::nullifier implementation and add tests
ConstanceBeguier Aug 26, 2026
356d6ef
Add assert to check (split_flag, rseed_split_note)
ConstanceBeguier Aug 26, 2026
e92ae6b
Add Note::psi_nf
ConstanceBeguier Aug 26, 2026
aae1a8f
Update circuit_zsa tests
ConstanceBeguier Aug 26, 2026
bd89e5d
Update changelog
ConstanceBeguier Aug 26, 2026
b7a6bee
Update changelog
ConstanceBeguier Aug 26, 2026
828308b
Update changelog
ConstanceBeguier Aug 26, 2026
c6dd0db
Document the split_flag consistency panic
PaulLaux Aug 26, 2026
2b4c83b
Test the split-note branch of Note::nullifier
PaulLaux Aug 26, 2026
1cc6b4e
Update comments
ConstanceBeguier Aug 26, 2026
ffa23b3
Doc+new test (#275)
PaulLaux Aug 27, 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
75 changes: 75 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,81 @@ and this project adheres to Rust's notion of

## [Unreleased]

### Added
- `orchard::note::AssetBase`, the ZSA note type identifier:
- `AssetBase::zatoshi`, the asset used by every Orchard note built through this crate's
public APIs today.
- `AssetBase::{from_bytes, to_bytes}`, (de)serializing an `AssetBase`.
- `AssetBase::is_zatoshi`, testing whether it is the zatoshi asset.
- `AssetBase::cv_base`, the base point used to derive a value commitment for notes of this
asset.
- `orchard::Note::asset`, returning a note's `AssetBase` (currently always
`AssetBase::zatoshi()`).
- `impl subtle::ConditionallySelectable for orchard::note::RandomSeed`.
- `orchard::bundle::Flags::zsa_enabled`, the accessor for the new ZSA flag bit. No public
constructor can currently set it to `true`.
- `orchard::circuit_version` module, containing `OrchardCircuitVersion` (moved out of
`orchard::circuit`; see Changed) with a new `OrchardCircuitVersion::ZSA` variant selecting
the ZSA Action circuit.
- `orchard::value::ValueCommitment::derive_with_asset`, deriving a value commitment for an
arbitrary `AssetBase`.
- `unstable-voting-circuits`-only (not covered by the crate's semver guarantees):
- Constants backing the new ZSA note commitment domain and split-note nullifier derivation:
- `orchard::constants::fixed_bases::NOTE_ZSA_COMMITMENT_PERSONALIZATION`
- `orchard::constants::sinsemilla::Q_NOTE_ZSA_COMMITMENT_M_GENERATOR`
- `orchard::constants::sinsemilla::{OrchardHashDomains::NoteZsaCommit,
OrchardCommitDomains::NoteZsaCommit}` variants
- `orchard::constants::nullifier_l` module, with the `NULLIFIER_L` constant used to derive
a split note's nullifier
- The types carrying the ZSA-specific inputs that the gadgets now take (see Changed):
- `orchard::circuit::derive_nullifier::ZsaNullifierParams`
- `orchard::circuit::note_commit::ZsaNoteCommitParams`

### Changed
- The following already-opaque public structs gained new private fields for future ZSA
support (none has a public accessor beyond `Note::asset`, listed under Added):
- `orchard::note::Note`: `asset`, `rseed_split_note`
- `orchard::builder::SpendInfo`: `split_flag`
- `orchard::builder::OutputInfo`: `asset`
- `orchard::circuit::Instance`: `enable_zsa`
- `orchard::Proof::expected_proof_size` now takes an `OrchardCircuitVersion` argument, since
the ZSA circuit's proof size differs from the other circuit versions'.
- `OrchardCircuitVersion` moved from `orchard::circuit::OrchardCircuitVersion` to
`orchard::circuit_version::OrchardCircuitVersion`. Neither it nor
`orchard::bundle::BundleVersion::circuit_version` (the method that returns it) require the
`circuit` feature anymore, so that `orchard::Proof::expected_proof_size` can take
an `OrchardCircuitVersion` argument without pulling in the `circuit` feature.
- `OrchardCircuitVersion` is now `#[non_exhaustive]`: downstream crates matching on it must
add a wildcard arm.
- `orchard::circuit::Circuit` no longer implements `halo2_proofs::plonk::Circuit`. It still
carries the witnesses of a single action, but the `plonk::Circuit` implementations now live
on the crate-internal `CircuitVanilla` and `CircuitZsa` types, one per circuit variation.
`Proof::create`, `ProvingKey::build`, and `VerifyingKey::build` dispatch to the right one
internally, so callers do not need to select it themselves.
- `orchard::Note::nullifier` now implements the split-note nullifier derivation of ZIP 226: a
note carrying a split seed derives $\psi$ from that seed, and its nullifier is offset by
$\mathcal{L}^{\mathsf{Orchard}}$. No public constructor produces a note with a split seed,
so the nullifier of every note that can be built today is unchanged.
- `orchard::circuit::Config` is now generic over the `halo2_gadgets` lookup-range-check
strategy.
- `unstable-voting-circuits`-only (not covered by the crate's semver guarantees):
- `orchard::circuit::note_commit::{NoteCommitConfig, NoteCommitChip}` are now generic over the
same lookup-range-check strategy, and `NoteCommitChip::configure` takes an additional
`is_zsa_circuit: bool` argument.
- Three gadgets moved out of `orchard::circuit::gadget`, each into a module of its own. Their
behavior is unchanged, but the old paths no longer exist (paths below are relative to
`orchard::circuit`):
- `gadget::derive_nullifier` → `derive_nullifier::gadgets::derive_nullifier`, plus a trailing
`zsa_params: Option<ZsaNullifierParams>` argument (`None` for vanilla behavior).
- `gadget::commit_ivk` → `commit_ivk::gadgets::commit_ivk`, now generic over the
lookup-range-check strategy and the Sinsemilla chip.
- `gadget::note_commit` → `note_commit::gadgets::note_commit`, now generic over the
lookup-range-check strategy, plus a trailing
`zsa_params: Option<ZsaNoteCommitParams>` argument (`None` for vanilla behavior).
- `orchard::constants::{OrchardHashDomains, OrchardCommitDomains}` both gained a
`NoteZsaCommit` variant (listed under Added) and are now `#[non_exhaustive]`, so downstream
matches on either must add a wildcard arm.

## [0.15.5] - 2026-08-02

### Changed
Expand Down
24 changes: 16 additions & 8 deletions Cargo.lock

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

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,8 @@ debug = true

[profile.bench]
debug = true

[patch.crates-io]
sinsemilla = { git = "https://github.com/zcash/sinsemilla", rev = "aabb707e862bc3d7b803c77d14e5a771bcee3e8c" }
halo2_gadgets = { git = "https://github.com/QED-it/halo2", rev = "62fac855a606dc81e327c5b4dfc2277e3366eebb" }
halo2_proofs = { git = "https://github.com/QED-it/halo2", rev = "62fac855a606dc81e327c5b4dfc2277e3366eebb" }
3 changes: 2 additions & 1 deletion benches/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use pprof::criterion::{Output, PProfProfiler};
use orchard::{
builder::{Builder, BundleType},
bundle::BundleVersion,
circuit::{OrchardCircuitVersion, ProvingKey, VerifyingKey},
circuit::{ProvingKey, VerifyingKey},
circuit_version::OrchardCircuitVersion,
keys::{FullViewingKey, Scope, SpendingKey},
value::NoteValue,
Anchor, Bundle,
Expand Down
3 changes: 2 additions & 1 deletion benches/note_decryption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Through
use orchard::{
builder::{Builder, BundleType},
bundle::BundleVersion,
circuit::{OrchardCircuitVersion, ProvingKey},
circuit::ProvingKey,
circuit_version::OrchardCircuitVersion,
keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendingKey},
note_encryption::{CompactAction, OrchardDomain},
value::NoteValue,
Expand Down
14 changes: 9 additions & 5 deletions src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ pub(crate) mod testing {
use crate::{
note::{
commitment::ExtractedNoteCommitment, nullifier::testing::arb_nullifier,
testing::arb_note, TransmittedNoteCiphertext,
testing::arb_note, AssetBase, TransmittedNoteCiphertext,
},
note_encryption::{OrchardDomain, OrchardNoteEncryption},
primitives::redpallas::{self, testing::arb_valid_spendauth_keypair},
Expand Down Expand Up @@ -235,9 +235,11 @@ pub(crate) mod testing {
rng_seed in prop::array::uniform32(prop::num::u8::ANY),
) -> Action<()> {
let cmx = ExtractedNoteCommitment::from(note.commitment());
let cv_net = ValueCommitment::derive(
let cv_net = ValueCommitment::derive_with_asset(
spend_value - output_value,
ValueCommitTrapdoor::zero()
ValueCommitTrapdoor::zero(),
// TODO ZSA: asset should be a param, not hardcoded here
AssetBase::zatoshi()
);
let encrypted_note =
encrypted_note_for(note, &cv_net, &cmx, StdRng::from_seed(rng_seed));
Expand All @@ -263,9 +265,11 @@ pub(crate) mod testing {
fake_sighash in prop::array::uniform32(prop::num::u8::ANY),
) -> Action<redpallas::Signature<SpendAuth>> {
let cmx = ExtractedNoteCommitment::from(note.commitment());
let cv_net = ValueCommitment::derive(
let cv_net = ValueCommitment::derive_with_asset(
spend_value - output_value,
ValueCommitTrapdoor::zero()
ValueCommitTrapdoor::zero(),
// TODO ZSA: asset should be a param, not hardcoded here
AssetBase::zatoshi()
);

let encrypted_note =
Expand Down
45 changes: 39 additions & 6 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ use crate::{
FullViewingKey, OutgoingViewingKey, Scope, SpendAuthorizingKey, SpendValidatingKey,
SpendingKey,
},
note::{ExtractedNoteCommitment, Note, NoteVersion, Nullifier, Rho, TransmittedNoteCiphertext},
note::{
AssetBase, ExtractedNoteCommitment, Note, NoteVersion, Nullifier, Rho,
TransmittedNoteCiphertext,
},
note_encryption::OrchardNoteEncryption,
primitives::redpallas::{self, Binding, SpendAuth},
tree::{Anchor, MerklePath},
Expand All @@ -29,7 +32,8 @@ use crate::{
use {
crate::{
action::Action,
circuit::{Circuit, Instance, OrchardCircuitVersion, ProvingKey},
circuit::{Circuit, Instance, ProvingKey},
circuit_version::OrchardCircuitVersion,
},
nonempty::NonEmpty,
};
Expand Down Expand Up @@ -352,6 +356,9 @@ pub struct SpendInfo {
pub(crate) scope: Scope,
pub(crate) note: Note,
pub(crate) merkle_path: Option<MerklePath>,
// If `split_flag` is true, the spend note's value is not counted in the action's `ValueSum`.
// TODO ZSA: `ActionInfo::value_sum` does not implement this yet.
pub(crate) split_flag: bool,
}

impl SpendInfo {
Expand All @@ -372,6 +379,7 @@ impl SpendInfo {
scope,
note,
merkle_path: Some(merkle_path),
split_flag: false,
})
}

Expand All @@ -392,6 +400,7 @@ impl SpendInfo {
scope,
note,
merkle_path: None,
split_flag: false,
})
}

Expand All @@ -410,6 +419,7 @@ impl SpendInfo {
scope: Scope::External,
note,
merkle_path,
split_flag: false,
}
}

Expand Down Expand Up @@ -484,6 +494,7 @@ pub struct OutputInfo {
ovk: Option<OutgoingViewingKey>,
recipient: Address,
value: NoteValue,
asset: AssetBase,
memo: [u8; 512],
note_version: NoteVersion,
/// When set, `build` fills `enc_ciphertext` with random bytes instead of encrypting the
Expand Down Expand Up @@ -514,6 +525,8 @@ impl OutputInfo {
ovk,
recipient,
value,
// TODO ZSA: asset should be a param, not hardcoded here
asset: AssetBase::zatoshi(),
memo,
note_version,
randomized_ciphertext: false,
Expand All @@ -536,6 +549,8 @@ impl OutputInfo {
ovk: None,
recipient,
value: NoteValue::ZERO,
// TODO ZSA: asset should be a param, not hardcoded here
asset: AssetBase::zatoshi(),
memo: [0u8; 512],
note_version,
randomized_ciphertext: matches!(spent_scope, Scope::External),
Expand Down Expand Up @@ -679,7 +694,18 @@ struct ActionInfo {
}

impl ActionInfo {
/// # Panics
///
/// Panics if the spent and output notes do not have the same asset. The circuit
/// witnesses a single `asset`, used for both note commitments and for the value
/// commitment, so an action cannot mix assets.
fn new(spend: SpendInfo, output: OutputInfo, rng: impl RngCore) -> Self {
assert_eq!(
spend.note.asset(),
output.asset,
"an action's spent and output notes must have the same asset"
);

ActionInfo {
spend,
output,
Expand All @@ -706,7 +732,8 @@ impl ActionInfo {
circuit_version: OrchardCircuitVersion,
) -> (Action<SigningMetadata>, Circuit) {
let v_net = self.value_sum();
let cv_net = ValueCommitment::derive(v_net, self.rcv.clone());
let cv_net =
ValueCommitment::derive_with_asset(v_net, self.rcv.clone(), self.spend.note.asset());

let (nf_old, ak, alpha, rk) = self.spend.build(&mut rng);
let (note, cmx, encrypted_note) = self.output.build(&cv_net, nf_old, &mut rng);
Expand Down Expand Up @@ -738,8 +765,11 @@ impl ActionInfo {
}

fn build_for_pczt(self, mut rng: impl RngCore) -> crate::pczt::Action {
// ZSA notes are not yet supported by PCZT.
assert_eq!(self.spend.note.asset(), AssetBase::zatoshi());
let v_net = self.value_sum();
let cv_net = ValueCommitment::derive(v_net, self.rcv.clone());
let cv_net =
ValueCommitment::derive_with_asset(v_net, self.rcv.clone(), self.spend.note.asset());

let spend = self.spend.into_pczt(&mut rng);
let output = self.output.into_pczt(&cv_net, spend.nullifier, &mut rng);
Expand Down Expand Up @@ -1427,6 +1457,7 @@ fn build_bundle<B, R: RngCore>(
scope,
note,
merkle_path: Some(MerklePath::dummy(&mut rng)),
split_flag: false,
};
pairs.push((None, Some(chg_idx), spend, output));
}
Expand Down Expand Up @@ -1880,7 +1911,8 @@ pub mod testing {
use crate::{
address::testing::arb_address,
bundle::{Authorized, Bundle, BundleVersion, TxVersion},
circuit::{OrchardCircuitVersion, ProvingKey},
circuit::ProvingKey,
circuit_version::OrchardCircuitVersion,
keys::{
testing::arb_spending_key, FullViewingKey, Scope, SpendAuthorizingKey, SpendingKey,
},
Expand Down Expand Up @@ -2166,7 +2198,8 @@ mod tests {
use crate::{
builder::{BundleType, SpendError},
bundle::{Authorized, Bundle, BundleVersion, Flags, TxVersion},
circuit::{OrchardCircuitVersion, ProvingKey},
circuit::ProvingKey,
circuit_version::OrchardCircuitVersion,
constants::MERKLE_DEPTH_ORCHARD,
keys::{
FullViewingKey, PreparedIncomingViewingKey, Scope, SpendAuthorizingKey, SpendingKey,
Expand Down
Loading