From b79f4210ae0423a343b5218b4ecd1741001a08d8 Mon Sep 17 00:00:00 2001 From: Dmitry Demin Date: Sun, 21 Dec 2025 18:39:03 +0000 Subject: [PATCH 1/8] Address the following review comments: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit r2585521988 r2585536670 r2585544143 r2585546655 r2585548116 r2585631816 r2585636883 r2599516192 r2599562825 r2599565711 r2599584010 r2599589396 r2599594554 r2599594936 r2599745324 r2602373392 r2602378140 r2602410770 r2602415043 r2602416292 r2602418341 r2602421894 r2602426619 r2602532807 r2602541601 r2602556915 r2602570477 (see note r2602938883 (see note) Note: r2602570477 and r2602938883 — we renamed the generic parameter P to Pr only where it conflicts with P for Proof. Should we use Pr for OrchardPrimitives everywhere else for consistency? --- src/builder.rs | 75 ++++++++----------- src/bundle.rs | 32 ++++---- src/primitives/compact_action.rs | 6 +- src/primitives/orchard_domain.rs | 21 +++++- .../zcash_note_encryption_domain.rs | 3 +- src/test_vectors/zip32.rs | 3 +- tests/builder.rs | 6 +- 7 files changed, 75 insertions(+), 71 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 850534350..37c831731 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -61,9 +61,9 @@ pub enum BundleType { impl BundleType { /// The default bundle type has all flags enabled, ZSA disabled, and does not require a bundle - /// to be produced. + /// to be produced if no spends or outputs have been added to the bundle. pub const DEFAULT_VANILLA: BundleType = BundleType::Transactional { - flags: Flags::ENABLED_WITHOUT_ZSA, + flags: Flags::ENABLED, bundle_required: false, }; @@ -123,7 +123,7 @@ impl BundleType { pub fn flags(&self) -> Flags { match self { BundleType::Transactional { flags, .. } => *flags, - BundleType::Coinbase => Flags::SPENDS_DISABLED_WITHOUT_ZSA, + BundleType::Coinbase => Flags::SPENDS_DISABLED, } } } @@ -474,7 +474,18 @@ struct ActionInfo { } impl ActionInfo { + /// Creates an `ActionInfo` with a random `rcv`. + /// + /// # Panics + /// + /// Panics if the asset types of the spent and output notes do not match. fn new(spend: SpendInfo, output: OutputInfo, rng: impl RngCore) -> Self { + assert_eq!( + spend.note.asset(), + output.asset, + "spend and recipient note types must be equal" + ); + ActionInfo { spend, output, @@ -483,6 +494,7 @@ impl ActionInfo { } /// Returns the value sum for this action. + /// /// Split notes do not contribute to the value sum. fn value_sum(&self) -> ValueSum { let spent_value = if self.spend.split_flag { @@ -499,21 +511,11 @@ impl ActionInfo { /// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend]. /// /// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend - /// - /// # Panics - /// - /// Panics if the asset types of the spent and output notes do not match. #[cfg(feature = "circuit")] fn build( self, mut rng: impl RngCore, ) -> (Action, Witnesses) { - assert_eq!( - self.spend.note.asset(), - self.output.asset, - "spend and recipient note types must be equal" - ); - let v_net = self.value_sum(); let cv_net = ValueCommitment::derive(v_net, self.rcv, self.output.asset); @@ -537,11 +539,6 @@ impl ActionInfo { } fn build_for_pczt(self, mut rng: impl RngCore) -> crate::pczt::Action { - assert_eq!( - self.spend.note.asset(), - self.output.asset, - "spend and recipient note types must be equal" - ); let v_net = self.value_sum(); let cv_net = ValueCommitment::derive(v_net, self.rcv, self.spend.note.asset()); @@ -619,6 +616,7 @@ pub type UnauthorizedBundleWithMetadata = (UnauthorizedBundle, Bun /// A builder for constructing an Orchard [`Bundle`] by specifying notes to spend, outputs to /// receive, and assets to burn. +/// /// This builder provides a structured way to incrementally assemble the components of a bundle. #[derive(Debug)] pub struct Builder { @@ -696,7 +694,7 @@ impl Builder { Ok(()) } - /// Add an instruction to burn a given amount of a specific asset. + /// Adds an instruction to burn a given amount of a specific asset. pub fn add_burn(&mut self, asset: AssetBase, value: NoteValue) -> Result<(), BuildError> { use alloc::collections::btree_map::Entry; @@ -1004,8 +1002,9 @@ fn build_bundle( .take(num_asset_pre_actions) .collect::>(); - // Shuffle the spends and outputs, so that the position does not reveal any - // information about the content. + // Shuffle the spends and outputs, so that learning the position of a + // specific spent note or output note doesn't reveal anything on its own + // about the meaning of that note in the transaction context. indexed_spends.shuffle(&mut rng); indexed_outputs.shuffle(&mut rng); @@ -1025,8 +1024,8 @@ fn build_bundle( .take(MIN_ACTIONS.saturating_sub(indexed_spends_outputs.len())), ); - // Shuffle the spends and outputs, so that the position does not reveal any information - // about the content. + // We shuffled the spends and outputs within each `AssetBase` above; now we + // shuffle the actions to achieve a similar property across `AssetBase`s. indexed_spends_outputs.shuffle(&mut rng); let mut bundle_meta = BundleMetadata::new(num_requested_spends, num_requested_outputs); @@ -1058,18 +1057,7 @@ fn build_bundle( .try_fold(ValueSum::zero(), |acc, action| acc + action.value_sum()) .ok_or(OverflowError)?; - let burn_vec = burn - .into_iter() - .map(|(asset, value)| { - Ok(( - asset, - NoteValue::from_raw( - u64::try_from(i128::from(value)) - .map_err(|_| BuildError::ValueSum(OverflowError))?, - ), - )) - }) - .collect::, BuildError>>()?; + let burn_vec = burn.into_iter().collect(); finisher( pre_actions, @@ -1222,7 +1210,7 @@ impl MaybeSigned { } } -impl Bundle, V, P> { +impl Bundle, V, Pr> { /// Loads the sighash into this bundle, preparing it for signing. /// /// This API ensures that all signatures are created over the same sighash. @@ -1230,7 +1218,7 @@ impl Bundle Bundle, V, P> { + ) -> Bundle, V, Pr> { self.map_authorization( &mut rng, |rng, _, SigningMetadata { dummy_ask, parts }| { @@ -1238,7 +1226,7 @@ impl Bundle Bundle Bundle, V, P> { } } -impl - Bundle, V, P> -{ +impl Bundle, V, Pr> { /// Signs this bundle with the given [`SpendAuthorizingKey`]. /// /// This will apply versioned signatures for all notes controlled by this spending key. @@ -1293,7 +1279,7 @@ impl |rng, partial, maybe| match maybe { MaybeSigned::SigningMetadata(parts) if parts.ak == expected_ak => { MaybeSigned::Signature(VerSpendAuthSig::new( - P::default_sighash_version(), + Pr::default_sighash_version(), ask.randomize(&parts.alpha).sign(rng, &partial.sigs.sighash), )) } @@ -1302,6 +1288,7 @@ impl |_, partial| partial, ) } + /// Appends externally computed versioned signatures. /// /// Each versioned signature will be applied to the one input for which it is valid. An error @@ -1475,7 +1462,7 @@ pub mod testing { } /// `BuilderArb` adapts `arb_...` functions for both Vanilla and ZSA Orchard protocol variations - /// in property-based testing, addressing proptest crate limitations. + /// in property-based testing, addressing proptest crate limitations. #[derive(Debug)] pub struct BuilderArb { phantom: core::marker::PhantomData

, diff --git a/src/bundle.rs b/src/bundle.rs index edc19f3e5..7f9027a11 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -1,8 +1,8 @@ //! Structs related to bundles of Orchard actions. -pub mod burn_validation; use alloc::vec::Vec; +pub mod burn_validation; pub mod commitments; #[cfg(feature = "circuit")] @@ -70,10 +70,10 @@ pub struct Flags { /// guaranteed to be dummy notes. If `true`, the created notes may be either real or /// dummy notes. outputs_enabled: bool, - /// Flag denoting whether ZSA transaction is enabled. + /// Flag denoting whether ZSA functionality is enabled in the transaction. /// - /// If `false`, all notes within [`Action`]s in the transaction's [`Bundle`] are - /// guaranteed to be notes with native asset. + /// If `false`, all notes within [`Action`]s in the transaction's [`Bundle`] are + /// guaranteed to be notes with native asset. If `true`, `Action`s may use any asset. zsa_enabled: bool, } @@ -97,7 +97,7 @@ impl Flags { } /// The flag set with both spends and outputs enabled and ZSA disabled. - pub const ENABLED_WITHOUT_ZSA: Flags = Flags { + pub const ENABLED: Flags = Flags { spends_enabled: true, outputs_enabled: true, zsa_enabled: false, @@ -111,7 +111,7 @@ impl Flags { }; /// The flag set with spends and ZSA disabled. - pub const SPENDS_DISABLED_WITHOUT_ZSA: Flags = Flags { + pub const SPENDS_DISABLED: Flags = Flags { spends_enabled: false, outputs_enabled: true, zsa_enabled: false, @@ -149,10 +149,10 @@ impl Flags { self.outputs_enabled } - /// Flag denoting whether ZSA transaction is enabled. + /// Flag denoting whether ZSA functionality is enabled in the transaction. /// - /// If `false`, all notes within [`Action`]s in the transaction's [`Bundle`] are - /// guaranteed to be notes with native asset. + /// If `false`, all notes within [`Action`]s in the transaction's [`Bundle`] are + /// guaranteed to be notes with native asset. If `true`, `Action`s may use any asset. pub fn zsa_enabled(&self) -> bool { self.zsa_enabled } @@ -470,16 +470,20 @@ pub(crate) fn derive_bvk, P: OrchardPrimitives>( value_balance: V, burn: &[(AssetBase, NoteValue)], ) -> redpallas::VerificationKey { - let cv_nets: Vec<_> = actions.into_iter().map(|a| a.cv_net().clone()).collect(); - derive_bvk_raw(&cv_nets, ValueSum::from_raw(value_balance.into()), burn) + derive_bvk_raw( + actions.into_iter().map(|a| a.cv_net()), + ValueSum::from_raw(value_balance.into()), + burn, + ) } -pub(crate) fn derive_bvk_raw( - cv_nets: &[ValueCommitment], +pub(crate) fn derive_bvk_raw<'a>( + cv_nets: impl IntoIterator, value_balance: ValueSum, burn: &[(AssetBase, NoteValue)], ) -> redpallas::VerificationKey { - (cv_nets.iter().sum::() + // https://p.z.cash/TCR:bad-txns-orchard-binding-signature-invalid?partial + (cv_nets.into_iter().sum::() - ValueCommitment::derive( value_balance, ValueCommitTrapdoor::zero(), diff --git a/src/primitives/compact_action.rs b/src/primitives/compact_action.rs index c0cdd742d..e2efcf67e 100644 --- a/src/primitives/compact_action.rs +++ b/src/primitives/compact_action.rs @@ -1,7 +1,5 @@ //! Defines actions for Orchard shielded outputs and compact action for light clients. -// Review hint: this file is largely derived from src/note_encryption.rs - use core::fmt; use zcash_note_encryption::{note_bytes::NoteBytes, EphemeralKeyBytes, ShieldedOutput}; @@ -34,7 +32,7 @@ impl ShieldedOutput> for Action P::CompactNoteCiphertextBytes::from_slice( &self.encrypted_note().enc_ciphertext.as_ref()[..P::COMPACT_NOTE_SIZE], ) - .unwrap() + .expect("P::CompactNoteCiphertextBytes should have size P::COMPACT_NOTE_SIZE") } } @@ -85,7 +83,7 @@ impl ShieldedOutput> for CompactAction

} fn enc_ciphertext_compact(&self) -> P::CompactNoteCiphertextBytes { - P::CompactNoteCiphertextBytes::from_slice(self.enc_ciphertext.as_ref()).unwrap() + self.enc_ciphertext } } diff --git a/src/primitives/orchard_domain.rs b/src/primitives/orchard_domain.rs index da49de040..6faf13c70 100644 --- a/src/primitives/orchard_domain.rs +++ b/src/primitives/orchard_domain.rs @@ -1,7 +1,5 @@ //! Orchard-specific note encryption domain. -// Review hint: this file is largely derived from src/note_encryption.rs - use crate::{ action::Action, note::Rho, primitives::compact_action::CompactAction, primitives::orchard_primitives::OrchardPrimitives, @@ -15,6 +13,15 @@ pub struct OrchardDomain { phantom: core::marker::PhantomData

, } +impl memuse::DynamicUsage for OrchardDomain

{ + fn dynamic_usage(&self) -> usize { + self.rho.dynamic_usage() + } + fn dynamic_usage_bounds(&self) -> (usize, Option) { + self.rho.dynamic_usage_bounds() + } +} + impl OrchardDomain

{ /// Constructs a domain that can be used to trial-decrypt this action's output note. pub fn for_action(act: &Action) -> Self { @@ -24,6 +31,14 @@ impl OrchardDomain

{ } } + /// Constructs a domain that can be used to trial-decrypt a PCZT action's output note. + pub fn for_pczt_action(act: &crate::pczt::Action) -> Self { + Self { + rho: Rho::from_nf_old(act.spend().nullifier), + phantom: Default::default(), + } + } + /// Constructs a domain that can be used to trial-decrypt this compact action's output note. pub fn for_compact_action(act: &CompactAction

) -> Self { Self { @@ -34,7 +49,7 @@ impl OrchardDomain

{ /// Constructs a domain from a rho. #[cfg(test)] - pub fn for_rho(rho: Rho) -> Self { + pub(crate) fn for_rho(rho: Rho) -> Self { Self { rho, phantom: Default::default(), diff --git a/src/primitives/zcash_note_encryption_domain.rs b/src/primitives/zcash_note_encryption_domain.rs index aecd43a31..183fce1ea 100644 --- a/src/primitives/zcash_note_encryption_domain.rs +++ b/src/primitives/zcash_note_encryption_domain.rs @@ -36,8 +36,7 @@ const NOTE_VALUE_OFFSET: usize = NOTE_DIVERSIFIER_OFFSET + NOTE_DIVERSIFIER_SIZE const NOTE_RSEED_OFFSET: usize = NOTE_VALUE_OFFSET + NOTE_VALUE_SIZE; /// The size of a Vanilla compact note. -pub(super) const COMPACT_NOTE_SIZE_VANILLA: usize = - NOTE_VERSION_SIZE + NOTE_DIVERSIFIER_SIZE + NOTE_VALUE_SIZE + NOTE_RSEED_SIZE; +pub(super) const COMPACT_NOTE_SIZE_VANILLA: usize = NOTE_RSEED_OFFSET + NOTE_RSEED_SIZE; /// The size of the encoding of a ZSA asset. const ZSA_ASSET_SIZE: usize = 32; diff --git a/src/test_vectors/zip32.rs b/src/test_vectors/zip32.rs index c388858f4..755294dc9 100644 --- a/src/test_vectors/zip32.rs +++ b/src/test_vectors/zip32.rs @@ -1,4 +1,4 @@ -// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/orchard_zip32.py +//! Test vectors for Orchard ZIP 32 key derivation. pub(crate) struct TestVector { pub(crate) sk: [u8; 32], @@ -7,6 +7,7 @@ pub(crate) struct TestVector { pub(crate) fp: [u8; 32], } +// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/orchard_zip32.py pub(crate) const TEST_VECTORS: &[TestVector] = &[ TestVector { sk: [ diff --git a/tests/builder.rs b/tests/builder.rs index 3cf55faa8..177b741ea 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -74,7 +74,7 @@ trait BundleOrchardFlavor: OrchardFlavor { impl BundleOrchardFlavor for OrchardVanilla { const DEFAULT_BUNDLE_TYPE: BundleType = BundleType::DEFAULT_VANILLA; - const SPENDS_DISABLED_FLAGS: Flags = Flags::SPENDS_DISABLED_WITHOUT_ZSA; + const SPENDS_DISABLED_FLAGS: Flags = Flags::SPENDS_DISABLED; } impl BundleOrchardFlavor for OrchardZSA { @@ -154,9 +154,9 @@ fn bundle_chain() -> ([u8; 32], [u8; 32]) { let mut builder = Builder::new( BundleType::Transactional { - // Intentionally testing with SPENDS_DISABLED_WITHOUT_ZSA as SPENDS_DISABLED_WITH_ZSA is already + // Intentionally testing with SPENDS_DISABLED as SPENDS_DISABLED_WITH_ZSA is already // tested above (for OrchardZSA case). Both should work. - flags: Flags::SPENDS_DISABLED_WITHOUT_ZSA, + flags: Flags::SPENDS_DISABLED, bundle_required: false, }, anchor, From ebee4ae716c85714d70389109abbb1fad570ef05 Mon Sep 17 00:00:00 2001 From: Dmitry Demin Date: Mon, 5 Jan 2026 13:04:31 +0000 Subject: [PATCH 2/8] Rename BundleType::DEFAULT_VANILLA back to DEFAULT (r2602373392) --- benches/circuit.rs | 5 +---- benches/note_decryption.rs | 5 +---- src/builder.rs | 4 ++-- src/bundle/commitments.rs | 4 ++-- src/pczt.rs | 4 ++-- tests/builder.rs | 2 +- 6 files changed, 9 insertions(+), 15 deletions(-) diff --git a/benches/circuit.rs b/benches/circuit.rs index 579a34675..6e3708f94 100644 --- a/benches/circuit.rs +++ b/benches/circuit.rs @@ -31,10 +31,7 @@ fn criterion_benchmark(c: &mut Criterion) { let pk = ProvingKey::build::(); let create_bundle = |num_recipients| { - let mut builder = Builder::new( - BundleType::DEFAULT_VANILLA, - Anchor::from_bytes([0; 32]).unwrap(), - ); + let mut builder = Builder::new(BundleType::DEFAULT, Anchor::from_bytes([0; 32]).unwrap()); for _ in 0..num_recipients { builder .add_output( diff --git a/benches/note_decryption.rs b/benches/note_decryption.rs index 8f2ce8373..f6247afca 100644 --- a/benches/note_decryption.rs +++ b/benches/note_decryption.rs @@ -50,10 +50,7 @@ fn bench_note_decryption(c: &mut Criterion) { .collect(); let bundle = { - let mut builder = Builder::new( - BundleType::DEFAULT_VANILLA, - Anchor::from_bytes([0; 32]).unwrap(), - ); + let mut builder = Builder::new(BundleType::DEFAULT, Anchor::from_bytes([0; 32]).unwrap()); // The builder pads to two actions, and shuffles their order. Add two recipients // so the first action is always decryptable. builder diff --git a/src/builder.rs b/src/builder.rs index 37c831731..fa02f7ba4 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -62,7 +62,7 @@ pub enum BundleType { impl BundleType { /// The default bundle type has all flags enabled, ZSA disabled, and does not require a bundle /// to be produced if no spends or outputs have been added to the bundle. - pub const DEFAULT_VANILLA: BundleType = BundleType::Transactional { + pub const DEFAULT: BundleType = BundleType::Transactional { flags: Flags::ENABLED, bundle_required: false, }; @@ -1562,7 +1562,7 @@ mod tests { let recipient = fvk.address_at(0u32, Scope::External); let mut builder = Builder::new( - BundleType::DEFAULT_VANILLA, + BundleType::DEFAULT, EMPTY_ROOTS[MERKLE_DEPTH_ORCHARD].into(), ); diff --git a/src/bundle/commitments.rs b/src/bundle/commitments.rs index 2a11a308b..57badc9be 100644 --- a/src/bundle/commitments.rs +++ b/src/bundle/commitments.rs @@ -236,7 +236,7 @@ mod tests { /// to ensure consistency. #[test] fn test_hash_bundle_txid_data_for_orchard_vanilla() { - let bundle = generate_bundle::(BundleType::DEFAULT_VANILLA); + let bundle = generate_bundle::(BundleType::DEFAULT); let sighash = hash_bundle_txid_data(&bundle); assert_eq!( sighash.to_hex().as_str(), @@ -272,7 +272,7 @@ mod tests { /// reference value to ensure consistency. #[test] fn test_hash_bundle_auth_data_for_orchard_vanilla() { - let bundle = generate_auth_bundle::(BundleType::DEFAULT_VANILLA); + let bundle = generate_auth_bundle::(BundleType::DEFAULT); let orchard_auth_digest = hash_bundle_auth_data(&bundle, &BTreeMap::new()); assert_eq!( orchard_auth_digest.to_hex().as_str(), diff --git a/src/pczt.rs b/src/pczt.rs index 8a924cf70..2f2b0a5bb 100644 --- a/src/pczt.rs +++ b/src/pczt.rs @@ -363,7 +363,7 @@ mod tests { // Run the Creator and Constructor roles. let mut builder = Builder::new( - BundleType::DEFAULT_VANILLA, + BundleType::DEFAULT, EMPTY_ROOTS[MERKLE_DEPTH_ORCHARD].into(), ); builder @@ -450,7 +450,7 @@ mod tests { }; // Run the Creator and Constructor roles. - let mut builder = Builder::new(BundleType::DEFAULT_VANILLA, anchor); + let mut builder = Builder::new(BundleType::DEFAULT, anchor); builder .add_spend(fvk.clone(), note, merkle_path.into()) .unwrap(); diff --git a/tests/builder.rs b/tests/builder.rs index 177b741ea..3d61bebe4 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -73,7 +73,7 @@ trait BundleOrchardFlavor: OrchardFlavor { } impl BundleOrchardFlavor for OrchardVanilla { - const DEFAULT_BUNDLE_TYPE: BundleType = BundleType::DEFAULT_VANILLA; + const DEFAULT_BUNDLE_TYPE: BundleType = BundleType::DEFAULT; const SPENDS_DISABLED_FLAGS: Flags = Flags::SPENDS_DISABLED; } From bdab0a4071517102ab7db3a92f560e67a5e24c2e Mon Sep 17 00:00:00 2001 From: Dmitry Demin Date: Mon, 5 Jan 2026 13:59:07 +0000 Subject: [PATCH 3/8] Rename P: OrchardPrimitives to Pr everywhere in the code --- src/action.rs | 32 ++++++------ src/builder.rs | 18 +++---- src/bundle.rs | 50 +++++++++---------- src/bundle/batch.rs | 4 +- src/bundle/commitments.rs | 16 +++--- src/note.rs | 6 +-- src/primitives/compact_action.rs | 40 +++++++-------- src/primitives/orchard_domain.rs | 12 ++--- .../zcash_note_encryption_domain.rs | 34 ++++++------- tests/builder.rs | 6 +-- 10 files changed, 111 insertions(+), 107 deletions(-) diff --git a/src/action.rs b/src/action.rs index e48eae5b8..5125a0b3b 100644 --- a/src/action.rs +++ b/src/action.rs @@ -13,7 +13,7 @@ use crate::{ /// This both creates a note (adding a commitment to the global ledger), and consumes /// some note created prior to this action (adding a nullifier to the global ledger). #[derive(Debug, Clone)] -pub struct Action { +pub struct Action { /// The nullifier of the note being spent. nf: Nullifier, /// The randomized verification key for the note being spent. @@ -21,20 +21,20 @@ pub struct Action { /// A commitment to the new note being created. cmx: ExtractedNoteCommitment, /// The transmitted note ciphertext. - encrypted_note: TransmittedNoteCiphertext

, + encrypted_note: TransmittedNoteCiphertext, /// A commitment to the net value created or consumed by this action. cv_net: ValueCommitment, /// The authorization for this action. authorization: A, } -impl Action { +impl Action { /// Constructs an `Action` from its constituent parts. pub fn from_parts( nf: Nullifier, rk: redpallas::VerificationKey, cmx: ExtractedNoteCommitment, - encrypted_note: TransmittedNoteCiphertext

, + encrypted_note: TransmittedNoteCiphertext, cv_net: ValueCommitment, authorization: A, ) -> Self { @@ -64,7 +64,7 @@ impl Action { } /// Returns the encrypted note ciphertext. - pub fn encrypted_note(&self) -> &TransmittedNoteCiphertext

{ + pub fn encrypted_note(&self) -> &TransmittedNoteCiphertext { &self.encrypted_note } @@ -84,7 +84,7 @@ impl Action { } /// Transitions this action from one authorization state to another. - pub fn map(self, step: impl FnOnce(A) -> U) -> Action { + pub fn map(self, step: impl FnOnce(A) -> U) -> Action { Action { nf: self.nf, rk: self.rk, @@ -96,7 +96,7 @@ impl Action { } /// Transitions this action from one authorization state to another. - pub fn try_map(self, step: impl FnOnce(A) -> Result) -> Result, E> { + pub fn try_map(self, step: impl FnOnce(A) -> Result) -> Result, E> { Ok(Action { nf: self.nf, rk: self.rk, @@ -108,7 +108,7 @@ impl Action { } } -impl DynamicUsage for Action { +impl DynamicUsage for Action { #[inline(always)] fn dynamic_usage(&self) -> usize { 0 @@ -150,20 +150,20 @@ pub(crate) mod testing { /// `ActionArb` adapts `arb_...` functions for both Vanilla and ZSA Orchard protocol flavors /// in property-based testing, addressing proptest crate limitations. #[derive(Debug)] - pub struct ActionArb { - phantom: core::marker::PhantomData

, + pub struct ActionArb { + phantom: core::marker::PhantomData, } - impl ActionArb

{ + impl ActionArb { fn encrypt_note( note: Note, memo: Vec, cmx: &ExtractedNoteCommitment, cv_net: &ValueCommitment, rng: &mut R, - ) -> TransmittedNoteCiphertext

{ + ) -> TransmittedNoteCiphertext { let encryptor = - NoteEncryption::>::new(None, note, memo.try_into().unwrap()); + NoteEncryption::>::new(None, note, memo.try_into().unwrap()); TransmittedNoteCiphertext { epk_bytes: encryptor.epk().to_bytes().0, @@ -181,7 +181,7 @@ pub(crate) mod testing { asset in arb_asset_base(), rng_seed in prop::array::uniform32(prop::num::u8::ANY), memo in prop::collection::vec(prop::num::u8::ANY, 512), - ) -> Action<(), P> { + ) -> Action<(), Pr> { let cmx = ExtractedNoteCommitment::from(note.commitment()); let cv_net = ValueCommitment::derive( spend_value - output_value, @@ -213,7 +213,7 @@ pub(crate) mod testing { fake_sighash in prop::array::uniform32(prop::num::u8::ANY), asset in arb_asset_base(), memo in prop::collection::vec(prop::num::u8::ANY, 512), - ) -> Action { + ) -> Action { let cmx = ExtractedNoteCommitment::from(note.commitment()); let cv_net = ValueCommitment::derive( spend_value - output_value, @@ -231,7 +231,7 @@ pub(crate) mod testing { cmx, encrypted_note, cv_net, - authorization: VerSpendAuthSig::new(P::default_sighash_version(), sk.sign(rng, &fake_sighash)), + authorization: VerSpendAuthSig::new(Pr::default_sighash_version(), sk.sign(rng, &fake_sighash)), } } } diff --git a/src/builder.rs b/src/builder.rs index fa02f7ba4..6f2ca22b2 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -417,18 +417,18 @@ impl OutputInfo { /// Defined in [Zcash Protocol Spec § 4.7.3: Sending Notes (Orchard)][orchardsend]. /// /// [orchardsend]: https://zips.z.cash/protocol/nu5.pdf#orchardsend - fn build( + fn build( &self, cv_net: &ValueCommitment, nf_old: Nullifier, mut rng: impl RngCore, - ) -> (Note, ExtractedNoteCommitment, TransmittedNoteCiphertext

) { + ) -> (Note, ExtractedNoteCommitment, TransmittedNoteCiphertext) { let rho = Rho::from_nf_old(nf_old); let note = Note::new(self.recipient, self.value, self.asset, rho, &mut rng); let cm_new = note.commitment(); let cmx = cm_new.into(); - let encryptor = NoteEncryption::>::new(self.ovk.clone(), note, self.memo); + let encryptor = NoteEncryption::>::new(self.ovk.clone(), note, self.memo); let encrypted_note = TransmittedNoteCiphertext { epk_bytes: encryptor.epk().to_bytes().0, @@ -1248,7 +1248,7 @@ impl Bundle } } -impl Bundle, V, P> { +impl Bundle, V, Pr> { /// Applies signatures to this bundle, in order to authorize it. /// /// This is a helper method that wraps [`Bundle::prepare`], [`Bundle::sign`], and @@ -1258,7 +1258,7 @@ impl Bundle, V, P> { mut rng: R, sighash: [u8; 32], signing_keys: &[SpendAuthorizingKey], - ) -> Result, BuildError> { + ) -> Result, BuildError> { signing_keys .iter() .fold(self.prepare(&mut rng, sighash), |partial, ask| { @@ -1330,11 +1330,11 @@ impl Bundle Bundle, V, P> { +impl Bundle, V, Pr> { /// Finalizes this bundle, enabling it to be included in a transaction. /// /// Returns an error if any signatures are missing. - pub fn finalize(self) -> Result, BuildError> { + pub fn finalize(self) -> Result, BuildError> { self.try_map_authorization( &mut (), |_, _, maybe| maybe.finalize(), @@ -1464,8 +1464,8 @@ pub mod testing { /// `BuilderArb` adapts `arb_...` functions for both Vanilla and ZSA Orchard protocol variations /// in property-based testing, addressing proptest crate limitations. #[derive(Debug)] - pub struct BuilderArb { - phantom: core::marker::PhantomData

, + pub struct BuilderArb { + phantom: core::marker::PhantomData, } impl BuilderArb { diff --git a/src/bundle.rs b/src/bundle.rs index 7f9027a11..14ff81d88 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -38,7 +38,7 @@ use crate::{ use crate::circuit::{Instance, VerifyingKey}; #[cfg(feature = "circuit")] -impl Action { +impl Action { /// Prepares the public instance for this action, for creating and verifying the /// bundle proof. pub fn to_instance(&self, flags: Flags, anchor: Anchor) -> Instance { @@ -203,9 +203,9 @@ pub trait Authorization: fmt::Debug { /// A bundle of actions to be applied to the ledger. #[derive(Clone)] -pub struct Bundle { +pub struct Bundle { /// The list of actions that make up this bundle. - actions: NonEmpty>, + actions: NonEmpty>, /// Orchard-specific transaction-level flags for this bundle. flags: Flags, /// The net value moved out of the Orchard shielded pool. @@ -225,11 +225,11 @@ pub struct Bundle { authorization: A, } -impl fmt::Debug for Bundle { +impl fmt::Debug for Bundle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// Helper struct for debug-printing actions without exposing `NonEmpty`. - struct Actions<'a, A, P: OrchardPrimitives>(&'a NonEmpty>); - impl fmt::Debug for Actions<'_, A, P> { + struct Actions<'a, A, Pr: OrchardPrimitives>(&'a NonEmpty>); + impl fmt::Debug for Actions<'_, A, Pr> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.0.iter()).finish() } @@ -245,10 +245,10 @@ impl fmt::Debug for Bundl } } -impl Bundle { +impl Bundle { /// Constructs a `Bundle` from its constituent parts. pub fn from_parts( - actions: NonEmpty>, + actions: NonEmpty>, flags: Flags, value_balance: V, burn: Vec<(AssetBase, NoteValue)>, @@ -268,7 +268,7 @@ impl Bundle { } /// Returns the list of actions that make up this bundle. - pub fn actions(&self) -> &NonEmpty> { + pub fn actions(&self) -> &NonEmpty> { &self.actions } @@ -311,7 +311,7 @@ impl Bundle { pub fn try_map_value_balance Result>( self, f: F, - ) -> Result, E> { + ) -> Result, E> { Ok(Bundle { actions: self.actions, flags: self.flags, @@ -329,7 +329,7 @@ impl Bundle { context: &mut R, mut spend_auth: impl FnMut(&mut R, &A, A::SpendAuth) -> U::SpendAuth, step: impl FnOnce(&mut R, A) -> U, - ) -> Bundle { + ) -> Bundle { let authorization = self.authorization; Bundle { actions: self @@ -350,7 +350,7 @@ impl Bundle { context: &mut R, mut spend_auth: impl FnMut(&mut R, &A, A::SpendAuth) -> Result, step: impl FnOnce(&mut R, A) -> Result, - ) -> Result, E> { + ) -> Result, E> { let authorization = self.authorization; let new_actions = self .actions @@ -465,8 +465,8 @@ impl Bundle { } } -pub(crate) fn derive_bvk, P: OrchardPrimitives>( - actions: &NonEmpty>, +pub(crate) fn derive_bvk, Pr: OrchardPrimitives>( + actions: &NonEmpty>, value_balance: V, burn: &[(AssetBase, NoteValue)], ) -> redpallas::VerificationKey { @@ -498,7 +498,7 @@ pub(crate) fn derive_bvk_raw<'a>( .into_bvk() } -impl, P: OrchardPrimitives> Bundle { +impl, Pr: OrchardPrimitives> Bundle { /// Computes a commitment to the effects of this bundle, suitable for inclusion within /// a transaction ID. pub fn commitment(&self) -> BundleCommitment { @@ -553,7 +553,7 @@ impl Authorized { } } -impl Bundle { +impl Bundle { /// Computes a commitment to the authorizing data within for this bundle. /// /// This together with `Bundle::commitment` bind the entire bundle. @@ -577,7 +577,7 @@ impl Bundle { } #[cfg(feature = "std")] -impl DynamicUsage for Bundle { +impl DynamicUsage for Bundle { fn dynamic_usage(&self) -> usize { self.actions.tail.dynamic_usage() + self.value_balance.dynamic_usage() @@ -655,16 +655,16 @@ pub mod testing { /// `BundleArb` adapts `arb_...` functions for both Vanilla and ZSA Orchard protocol variations /// in property-based testing, addressing proptest crate limitations. #[derive(Debug)] - pub struct BundleArb { - phantom: std::marker::PhantomData

, + pub struct BundleArb { + phantom: std::marker::PhantomData, } - impl BundleArb

{ + impl BundleArb { /// Generate an unauthorized action having spend and output values less than MAX_NOTE_VALUE / n_actions. pub fn arb_unauthorized_action_n( n_actions: usize, flags: Flags, - ) -> impl Strategy)> { + ) -> impl Strategy)> { let spend_value_gen = if flags.spends_enabled { Strategy::boxed(arb_note_value_bounded(MAX_NOTE_VALUE / n_actions as u64)) } else { @@ -689,7 +689,7 @@ pub mod testing { pub fn arb_action_n( n_actions: usize, flags: Flags, - ) -> impl Strategy)> { + ) -> impl Strategy)> { let spend_value_gen = if flags.spends_enabled { Strategy::boxed(arb_note_value_bounded(MAX_NOTE_VALUE / n_actions as u64)) } else { @@ -750,7 +750,7 @@ pub mod testing { anchor in Self::arb_base().prop_map(Anchor::from), flags in Just(flags), burn in vec(Self::arb_asset_to_burn(), 1usize..10) - ) -> Bundle { + ) -> Bundle { let (balances, actions): (Vec, Vec>) = acts.into_iter().unzip(); Bundle::from_parts( @@ -781,7 +781,7 @@ pub mod testing { fake_sighash in prop::array::uniform32(prop::num::u8::ANY), flags in Just(flags), burn in vec(Self::arb_asset_to_burn(), 1usize..10) - ) -> Bundle { + ) -> Bundle { let (balances, actions): (Vec, Vec, >) = acts.into_iter().unzip(); let rng = StdRng::from_seed(rng_seed); @@ -793,7 +793,7 @@ pub mod testing { anchor, Authorized { proof: Proof::new(fake_proof), - binding_signature: VerBindingSig::new(P::default_sighash_version(), sk.sign(rng, &fake_sighash)), + binding_signature: VerBindingSig::new(Pr::default_sighash_version(), sk.sign(rng, &fake_sighash)), }, ) } diff --git a/src/bundle/batch.rs b/src/bundle/batch.rs index 481dadd17..6d679787b 100644 --- a/src/bundle/batch.rs +++ b/src/bundle/batch.rs @@ -40,9 +40,9 @@ impl BatchValidator { } /// Adds the proof and RedPallas signatures from the given bundle to the validator. - pub fn add_bundle, P: OrchardPrimitives>( + pub fn add_bundle, Pr: OrchardPrimitives>( &mut self, - bundle: &Bundle, + bundle: &Bundle, sighash: [u8; 32], ) { for action in bundle.actions().iter() { diff --git a/src/bundle/commitments.rs b/src/bundle/commitments.rs index 57badc9be..4e369fbb7 100644 --- a/src/bundle/commitments.rs +++ b/src/bundle/commitments.rs @@ -46,10 +46,14 @@ pub(crate) fn hasher(personal: &[u8; 16]) -> State { /// /// [zip244]: https://zips.z.cash/zip-0244 /// [zip246]: https://zips.z.cash/zip-0246 -pub(crate) fn hash_bundle_txid_data, P: OrchardPrimitives>( - bundle: &Bundle, +pub(crate) fn hash_bundle_txid_data< + A: Authorization, + V: Copy + Into, + Pr: OrchardPrimitives, +>( + bundle: &Bundle, ) -> Blake2bHash { - P::hash_bundle_txid_data(bundle) + Pr::hash_bundle_txid_data(bundle) } /// Construct the commitment for the absent bundle as defined in @@ -73,11 +77,11 @@ pub fn hash_bundle_txid_empty() -> Blake2bHash { /// /// [zip244]: https://zips.z.cash/zip-0244 /// [zip246]: https://zips.z.cash/zip-0246 -pub(crate) fn hash_bundle_auth_data( - bundle: &Bundle, +pub(crate) fn hash_bundle_auth_data( + bundle: &Bundle, sighash_version_map: &BTreeMap>, ) -> Blake2bHash { - P::hash_bundle_auth_data(bundle, sighash_version_map) + Pr::hash_bundle_auth_data(bundle, sighash_version_map) } /// Construct the `orchard_auth_digest` commitment for an absent bundle as defined in diff --git a/src/note.rs b/src/note.rs index 67808b81f..8f4e144d9 100644 --- a/src/note.rs +++ b/src/note.rs @@ -401,17 +401,17 @@ pub(crate) fn rho_for_issuance_note( /// An encrypted note. #[derive(Clone)] -pub struct TransmittedNoteCiphertext { +pub struct TransmittedNoteCiphertext { /// The serialization of the ephemeral public key pub epk_bytes: [u8; 32], /// The encrypted note ciphertext - pub enc_ciphertext: P::NoteCiphertextBytes, + pub enc_ciphertext: Pr::NoteCiphertextBytes, /// An encrypted value that allows the holder of the outgoing cipher /// key for the note to recover the note plaintext. pub out_ciphertext: [u8; 80], } -impl fmt::Debug for TransmittedNoteCiphertext

{ +impl fmt::Debug for TransmittedNoteCiphertext { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("TransmittedNoteCiphertext") .field("epk_bytes", &self.epk_bytes) diff --git a/src/primitives/compact_action.rs b/src/primitives/compact_action.rs index e2efcf67e..f48c7a64c 100644 --- a/src/primitives/compact_action.rs +++ b/src/primitives/compact_action.rs @@ -11,7 +11,7 @@ use crate::{ use super::{orchard_domain::OrchardDomain, orchard_primitives::OrchardPrimitives}; -impl ShieldedOutput> for Action { +impl ShieldedOutput> for Action { fn ephemeral_key(&self) -> EphemeralKeyBytes { EphemeralKeyBytes(self.encrypted_note().epk_bytes) } @@ -24,13 +24,13 @@ impl ShieldedOutput> for Action self.cmx().to_bytes() } - fn enc_ciphertext(&self) -> Option<&P::NoteCiphertextBytes> { + fn enc_ciphertext(&self) -> Option<&Pr::NoteCiphertextBytes> { Some(&self.encrypted_note().enc_ciphertext) } - fn enc_ciphertext_compact(&self) -> P::CompactNoteCiphertextBytes { - P::CompactNoteCiphertextBytes::from_slice( - &self.encrypted_note().enc_ciphertext.as_ref()[..P::COMPACT_NOTE_SIZE], + fn enc_ciphertext_compact(&self) -> Pr::CompactNoteCiphertextBytes { + Pr::CompactNoteCiphertextBytes::from_slice( + &self.encrypted_note().enc_ciphertext.as_ref()[..Pr::COMPACT_NOTE_SIZE], ) .expect("P::CompactNoteCiphertextBytes should have size P::COMPACT_NOTE_SIZE") } @@ -38,24 +38,24 @@ impl ShieldedOutput> for Action /// A compact Action for light clients. #[derive(Clone)] -pub struct CompactAction { +pub struct CompactAction { nullifier: Nullifier, cmx: ExtractedNoteCommitment, ephemeral_key: EphemeralKeyBytes, - enc_ciphertext: P::CompactNoteCiphertextBytes, + enc_ciphertext: Pr::CompactNoteCiphertextBytes, } -impl fmt::Debug for CompactAction

{ +impl fmt::Debug for CompactAction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "CompactAction") } } -impl From<&Action> for CompactAction

+impl From<&Action> for CompactAction where - Action: ShieldedOutput>, + Action: ShieldedOutput>, { - fn from(action: &Action) -> Self { + fn from(action: &Action) -> Self { CompactAction { nullifier: *action.nullifier(), cmx: *action.cmx(), @@ -65,7 +65,7 @@ where } } -impl ShieldedOutput> for CompactAction

{ +impl ShieldedOutput> for CompactAction { fn ephemeral_key(&self) -> EphemeralKeyBytes { EphemeralKeyBytes(self.ephemeral_key.0) } @@ -78,22 +78,22 @@ impl ShieldedOutput> for CompactAction

self.cmx.to_bytes() } - fn enc_ciphertext(&self) -> Option<&P::NoteCiphertextBytes> { + fn enc_ciphertext(&self) -> Option<&Pr::NoteCiphertextBytes> { None } - fn enc_ciphertext_compact(&self) -> P::CompactNoteCiphertextBytes { + fn enc_ciphertext_compact(&self) -> Pr::CompactNoteCiphertextBytes { self.enc_ciphertext } } -impl CompactAction

{ +impl CompactAction { /// Create a CompactAction from its constituent parts pub fn from_parts( nullifier: Nullifier, cmx: ExtractedNoteCommitment, ephemeral_key: EphemeralKeyBytes, - enc_ciphertext: P::CompactNoteCiphertextBytes, + enc_ciphertext: Pr::CompactNoteCiphertextBytes, ) -> Self { Self { nullifier, @@ -139,13 +139,13 @@ pub mod testing { /// Creates a fake `CompactAction` paying the given recipient the specified value. /// /// Returns the `CompactAction` and the new note. - pub fn fake_compact_action( + pub fn fake_compact_action( rng: &mut R, nf_old: Nullifier, recipient: Address, value: NoteValue, ovk: Option, - ) -> (CompactAction

, Note) { + ) -> (CompactAction, Note) { let rho = Rho::from_nf_old(nf_old); let rseed = { loop { @@ -158,9 +158,9 @@ pub mod testing { } }; let note = Note::from_parts(recipient, value, AssetBase::native(), rho, rseed).unwrap(); - let encryptor = NoteEncryption::>::new(ovk, note, [0u8; MEMO_SIZE]); + let encryptor = NoteEncryption::>::new(ovk, note, [0u8; MEMO_SIZE]); let cmx = ExtractedNoteCommitment::from(note.commitment()); - let ephemeral_key = OrchardDomain::

::epk_bytes(encryptor.epk()); + let ephemeral_key = OrchardDomain::::epk_bytes(encryptor.epk()); let enc_ciphertext = encryptor.encrypt_note_plaintext(); ( diff --git a/src/primitives/orchard_domain.rs b/src/primitives/orchard_domain.rs index 6faf13c70..e9cfeb38e 100644 --- a/src/primitives/orchard_domain.rs +++ b/src/primitives/orchard_domain.rs @@ -7,13 +7,13 @@ use crate::{ /// Orchard-specific note encryption logic. #[derive(Debug, Clone)] -pub struct OrchardDomain { +pub struct OrchardDomain { /// A parameter needed to generate the nullifier. pub rho: Rho, - phantom: core::marker::PhantomData

, + phantom: core::marker::PhantomData, } -impl memuse::DynamicUsage for OrchardDomain

{ +impl memuse::DynamicUsage for OrchardDomain { fn dynamic_usage(&self) -> usize { self.rho.dynamic_usage() } @@ -22,9 +22,9 @@ impl memuse::DynamicUsage for OrchardDomain

{ } } -impl OrchardDomain

{ +impl OrchardDomain { /// Constructs a domain that can be used to trial-decrypt this action's output note. - pub fn for_action(act: &Action) -> Self { + pub fn for_action(act: &Action) -> Self { Self { rho: act.rho(), phantom: Default::default(), @@ -40,7 +40,7 @@ impl OrchardDomain

{ } /// Constructs a domain that can be used to trial-decrypt this compact action's output note. - pub fn for_compact_action(act: &CompactAction

) -> Self { + pub fn for_compact_action(act: &CompactAction) -> Self { Self { rho: act.rho(), phantom: Default::default(), diff --git a/src/primitives/zcash_note_encryption_domain.rs b/src/primitives/zcash_note_encryption_domain.rs index 183fce1ea..a53b8a0c5 100644 --- a/src/primitives/zcash_note_encryption_domain.rs +++ b/src/primitives/zcash_note_encryption_domain.rs @@ -92,9 +92,9 @@ pub(super) fn parse_note_version(plaintext: &[u8]) -> Option { /// Parses the note plaintext (excluding the memo) and extracts the note and address if valid. /// Domain-specific requirements: /// - If the note version is 3, the `plaintext` must contain a valid encoding of a ZSA asset type. -pub(super) fn parse_note_plaintext_without_memo( +pub(super) fn parse_note_plaintext_without_memo( rho: Rho, - plaintext: &P::CompactNotePlaintextBytes, + plaintext: &Pr::CompactNotePlaintextBytes, get_validated_pk_d: F, ) -> Option<(Note, Address)> where @@ -124,7 +124,7 @@ where let pk_d = get_validated_pk_d(&diversifier)?; let recipient = Address::from_parts(diversifier, pk_d); - let asset = P::extract_asset(plaintext)?; + let asset = Pr::extract_asset(plaintext)?; let note = Option::from(Note::from_parts(recipient, value, asset, rho, rseed))?; Some((note, recipient)) @@ -146,7 +146,7 @@ pub(super) fn build_base_note_plaintext_bytes( np } -impl Domain for OrchardDomain

{ +impl Domain for OrchardDomain { type EphemeralSecretKey = EphemeralSecretKey; type EphemeralPublicKey = EphemeralPublicKey; type PreparedEphemeralPublicKey = PreparedEphemeralPublicKey; @@ -162,10 +162,10 @@ impl Domain for OrchardDomain

{ type ExtractedCommitmentBytes = [u8; 32]; type Memo = Memo; - type NotePlaintextBytes = P::NotePlaintextBytes; - type NoteCiphertextBytes = P::NoteCiphertextBytes; - type CompactNotePlaintextBytes = P::CompactNotePlaintextBytes; - type CompactNoteCiphertextBytes = P::CompactNoteCiphertextBytes; + type NotePlaintextBytes = Pr::NotePlaintextBytes; + type NoteCiphertextBytes = Pr::NoteCiphertextBytes; + type CompactNotePlaintextBytes = Pr::CompactNotePlaintextBytes; + type CompactNoteCiphertextBytes = Pr::CompactNoteCiphertextBytes; fn derive_esk(note: &Self::Note) -> Option { Some(note.esk()) @@ -204,8 +204,8 @@ impl Domain for OrchardDomain

{ secret.kdf_orchard(ephemeral_key) } - fn note_plaintext_bytes(note: &Self::Note, memo: &Self::Memo) -> P::NotePlaintextBytes { - P::build_note_plaintext_bytes(note, memo) + fn note_plaintext_bytes(note: &Self::Note, memo: &Self::Memo) -> Pr::NotePlaintextBytes { + Pr::build_note_plaintext_bytes(note, memo) } fn derive_ock( @@ -242,9 +242,9 @@ impl Domain for OrchardDomain

{ fn parse_note_plaintext_without_memo_ivk( &self, ivk: &Self::IncomingViewingKey, - plaintext: &P::CompactNotePlaintextBytes, + plaintext: &Pr::CompactNotePlaintextBytes, ) -> Option<(Self::Note, Self::Recipient)> { - parse_note_plaintext_without_memo::(self.rho, plaintext, |diversifier| { + parse_note_plaintext_without_memo::(self.rho, plaintext, |diversifier| { Some(DiversifiedTransmissionKey::derive(ivk, diversifier)) }) } @@ -252,16 +252,16 @@ impl Domain for OrchardDomain

{ fn parse_note_plaintext_without_memo_ovk( &self, pk_d: &Self::DiversifiedTransmissionKey, - plaintext: &P::CompactNotePlaintextBytes, + plaintext: &Pr::CompactNotePlaintextBytes, ) -> Option<(Self::Note, Self::Recipient)> { - parse_note_plaintext_without_memo::(self.rho, plaintext, |_| Some(*pk_d)) + parse_note_plaintext_without_memo::(self.rho, plaintext, |_| Some(*pk_d)) } fn split_plaintext_at_memo( &self, - plaintext: &P::NotePlaintextBytes, + plaintext: &Pr::NotePlaintextBytes, ) -> Option<(Self::CompactNotePlaintextBytes, Self::Memo)> { - let (compact, memo) = plaintext.as_ref().split_at(P::COMPACT_NOTE_SIZE); + let (compact, memo) = plaintext.as_ref().split_at(Pr::COMPACT_NOTE_SIZE); Some(( Self::CompactNotePlaintextBytes::from_slice(compact)?, memo.try_into().ok()?, @@ -278,7 +278,7 @@ impl Domain for OrchardDomain

{ } } -impl BatchDomain for OrchardDomain

{ +impl BatchDomain for OrchardDomain { fn batch_kdf<'a>( items: impl Iterator, &'a EphemeralKeyBytes)>, ) -> Vec> { diff --git a/tests/builder.rs b/tests/builder.rs index 3d61bebe4..c9e2f853e 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -16,8 +16,8 @@ use rand::SeedableRng; use shardtree::{store::memory::MemoryShardStore, ShardTree}; use zcash_note_encryption::try_note_decryption; -pub fn verify_bundle( - bundle: &Bundle, +pub fn verify_bundle( + bundle: &Bundle, vk: &VerifyingKey, verify_proof: bool, ) { @@ -29,7 +29,7 @@ pub fn verify_bundle( for action in bundle.actions() { assert_eq!( action.authorization().version(), - &P::default_sighash_version() + &Pr::default_sighash_version() ); assert_eq!( action.rk().verify(&sighash, action.authorization().sig()), From 17de30c5d7a49a2f2bee6aed24bbd99f67fe170d Mon Sep 17 00:00:00 2001 From: Dmitry Demin Date: Mon, 5 Jan 2026 14:15:46 +0000 Subject: [PATCH 4/8] Remove assert_eq from the ActionInfo constructor and return it to build and build_for_pczt methods --- src/builder.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 6f2ca22b2..f050dc3b4 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -480,12 +480,6 @@ impl ActionInfo { /// /// Panics if the asset types of the spent and output notes do not match. fn new(spend: SpendInfo, output: OutputInfo, rng: impl RngCore) -> Self { - assert_eq!( - spend.note.asset(), - output.asset, - "spend and recipient note types must be equal" - ); - ActionInfo { spend, output, @@ -516,6 +510,12 @@ impl ActionInfo { self, mut rng: impl RngCore, ) -> (Action, Witnesses) { + assert_eq!( + self.spend.note.asset(), + self.output.asset, + "spend and recipient note types must be equal" + ); + let v_net = self.value_sum(); let cv_net = ValueCommitment::derive(v_net, self.rcv, self.output.asset); @@ -539,6 +539,11 @@ impl ActionInfo { } fn build_for_pczt(self, mut rng: impl RngCore) -> crate::pczt::Action { + assert_eq!( + self.spend.note.asset(), + self.output.asset, + "spend and recipient note types must be equal" + ); let v_net = self.value_sum(); let cv_net = ValueCommitment::derive(v_net, self.rcv, self.spend.note.asset()); From 927f40eb6f130fbd8074abab00934ce5758b23f3 Mon Sep 17 00:00:00 2001 From: Dmitry Demin Date: Mon, 5 Jan 2026 14:22:16 +0000 Subject: [PATCH 5/8] Rename remaining P: OrchardPrimitives usages to Pr --- src/primitives/compact_action.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/primitives/compact_action.rs b/src/primitives/compact_action.rs index f48c7a64c..ffe5f170e 100644 --- a/src/primitives/compact_action.rs +++ b/src/primitives/compact_action.rs @@ -32,7 +32,7 @@ impl ShieldedOutput> for Action Date: Tue, 6 Jan 2026 10:36:32 +0100 Subject: [PATCH 6/8] Revert "Remove assert_eq from the ActionInfo constructor and return it to build and build_for_pczt methods" This reverts commit 17de30c5d7a49a2f2bee6aed24bbd99f67fe170d. --- src/builder.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index f050dc3b4..6f2ca22b2 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -480,6 +480,12 @@ impl ActionInfo { /// /// Panics if the asset types of the spent and output notes do not match. fn new(spend: SpendInfo, output: OutputInfo, rng: impl RngCore) -> Self { + assert_eq!( + spend.note.asset(), + output.asset, + "spend and recipient note types must be equal" + ); + ActionInfo { spend, output, @@ -510,12 +516,6 @@ impl ActionInfo { self, mut rng: impl RngCore, ) -> (Action, Witnesses) { - assert_eq!( - self.spend.note.asset(), - self.output.asset, - "spend and recipient note types must be equal" - ); - let v_net = self.value_sum(); let cv_net = ValueCommitment::derive(v_net, self.rcv, self.output.asset); @@ -539,11 +539,6 @@ impl ActionInfo { } fn build_for_pczt(self, mut rng: impl RngCore) -> crate::pczt::Action { - assert_eq!( - self.spend.note.asset(), - self.output.asset, - "spend and recipient note types must be equal" - ); let v_net = self.value_sum(); let cv_net = ValueCommitment::derive(v_net, self.rcv, self.spend.note.asset()); From ee6931c865622c4f3ebd49f39c7b642aff91cb04 Mon Sep 17 00:00:00 2001 From: Constance Beguier Date: Tue, 6 Jan 2026 10:38:12 +0100 Subject: [PATCH 7/8] Check spend and output assets are equal in build_for_pczt --- src/builder.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/builder.rs b/src/builder.rs index 6f2ca22b2..0f1ef9e14 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -539,6 +539,12 @@ impl ActionInfo { } fn build_for_pczt(self, mut rng: impl RngCore) -> crate::pczt::Action { + assert_eq!( + self.spend.note.asset(), + self.output.asset, + "spend and recipient note types must be equal" + ); + let v_net = self.value_sum(); let cv_net = ValueCommitment::derive(v_net, self.rcv, self.spend.note.asset()); From caea7c8c36a99d31599750485bf746ccde389619 Mon Sep 17 00:00:00 2001 From: Constance Beguier Date: Tue, 6 Jan 2026 10:45:44 +0100 Subject: [PATCH 8/8] Remove check that spend and output assets are equal in build_for_pczt Inside ActionInfo::build_for_pczt, we check that - spend asset is equal to the native asset in self.spend.into_pczt(...) - output asset is equal to the native asset in self.output.into_pczt(...) --- src/builder.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 0f1ef9e14..6f2ca22b2 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -539,12 +539,6 @@ impl ActionInfo { } fn build_for_pczt(self, mut rng: impl RngCore) -> crate::pczt::Action { - assert_eq!( - self.spend.note.asset(), - self.output.asset, - "spend and recipient note types must be equal" - ); - let v_net = self.value_sum(); let cv_net = ValueCommitment::derive(v_net, self.rcv, self.spend.note.asset());