From 9be2ba2b92eff2f8ac32dac1b3410d8352018b14 Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Fri, 8 Nov 2024 13:13:48 +0100 Subject: [PATCH 01/20] Add reference_notes getter --- src/issuance.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/issuance.rs b/src/issuance.rs index 531db1811..a7badb6eb 100644 --- a/src/issuance.rs +++ b/src/issuance.rs @@ -304,6 +304,11 @@ impl IssueBundle { } } + /// Returns the reference notes created in this bundle. + pub fn reference_notes(&self) -> &HashMap { + &self.reference_notes + } + /// Computes a commitment to the effects of this bundle, suitable for inclusion within /// a transaction ID. pub fn commitment(&self) -> IssueBundleCommitment { From a92459446af67b421a7b5578e1376d5be2ad29f9 Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Mon, 11 Nov 2024 09:03:15 +0100 Subject: [PATCH 02/20] Add Swap bundle type --- src/builder.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/builder.rs b/src/builder.rs index 53d2971f2..bc30fda48 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -65,6 +65,12 @@ impl BundleType { bundle_required: false, }; + /// The default bundle with all flags enabled, including ZSA and Swaps. // TODO swap flag? + pub const DEFAULT_SWAP: BundleType = BundleType::Transactional { + flags: Flags::ENABLED_WITH_ZSA, + bundle_required: false, + }; + /// The DISABLED bundle type does not permit any bundle to be produced, and when used in the /// builder will prevent any spends or outputs from being added. pub const DISABLED: BundleType = BundleType::Transactional { From 6aba06f824ea78bc33137202f415f59fab1b59f0 Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Mon, 11 Nov 2024 09:55:13 +0100 Subject: [PATCH 03/20] Add Clone to SwapBundle --- src/swap_bundle.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/swap_bundle.rs b/src/swap_bundle.rs index 0f188873b..ae8a6ac65 100644 --- a/src/swap_bundle.rs +++ b/src/swap_bundle.rs @@ -17,7 +17,7 @@ use crate::{ use k256::elliptic_curve::rand_core::{CryptoRng, RngCore}; /// An action group. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ActionGroup { /// The action group main content. action_group: Bundle, @@ -118,7 +118,7 @@ impl> ActionGroup { } /// A swap bundle to be applied to the ledger. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct SwapBundle { /// The list of action groups that make up this swap bundle. action_groups: Vec>, @@ -211,6 +211,13 @@ impl SwapBundle { pub fn binding_signature(&self) -> &redpallas::Signature { &self.binding_signature } + + /// The net value moved out of this swap. + /// + /// This is the sum of Orchard spends minus the sum of Orchard outputs. + pub fn value_balance(&self) -> &V { + &self.value_balance + } } impl> SwapBundle { From d6fbeb9da77e0056f62a80992543c2accbb61b56 Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:38:42 +0100 Subject: [PATCH 04/20] Add is_empty method --- src/builder.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/builder.rs b/src/builder.rs index bc30fda48..538cea414 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -550,6 +550,14 @@ impl Builder { } } + /// Returns true if the builder is empty. + pub fn is_empty(&self) -> bool { + self.spends.is_empty() + && self.outputs.is_empty() + && self.burn.is_empty() + && self.reference_notes.is_empty() + } + /// Adds a note to be spent in this transaction. /// /// - `note` is a spendable note, obtained by trial-decrypting an [`Action`] using the From 53018a5f359a4fc04efd995cddf059de16d80ecb Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:56:42 +0100 Subject: [PATCH 05/20] Remove reference notes getter --- src/issuance.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/issuance.rs b/src/issuance.rs index a7badb6eb..531db1811 100644 --- a/src/issuance.rs +++ b/src/issuance.rs @@ -304,11 +304,6 @@ impl IssueBundle { } } - /// Returns the reference notes created in this bundle. - pub fn reference_notes(&self) -> &HashMap { - &self.reference_notes - } - /// Computes a commitment to the effects of this bundle, suitable for inclusion within /// a transaction ID. pub fn commitment(&self) -> IssueBundleCommitment { From fef2d1860a70ba9615b3ec605aa64d538666a7f5 Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Fri, 14 Feb 2025 15:35:49 +0100 Subject: [PATCH 06/20] Add from_parts to SwapBundle --- src/swap_bundle.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/swap_bundle.rs b/src/swap_bundle.rs index ae8a6ac65..a7283d42b 100644 --- a/src/swap_bundle.rs +++ b/src/swap_bundle.rs @@ -130,6 +130,23 @@ pub struct SwapBundle { binding_signature: redpallas::Signature, } +impl SwapBundle { + + /// Constructs a `SwapBundle` from its constituent parts. + pub fn from_parts( + action_groups: Vec>, + value_balance: V, + binding_signature: redpallas::Signature, + ) -> Self { + SwapBundle { + action_groups, + value_balance, + binding_signature, + } + } +} + + impl + std::iter::Sum> SwapBundle { /// Constructs a `SwapBundle` from its action groups. pub fn new( From f1a50e68da8771b85656fb5b9af63d5967ea9395 Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Fri, 14 Feb 2025 17:33:46 +0100 Subject: [PATCH 07/20] Add AuthorizedWithProof trait --- src/bundle.rs | 18 +++++++++++++----- src/bundle/batch.rs | 2 +- src/domain/orchard_domain_vanilla.rs | 14 +++++++------- src/domain/orchard_domain_zsa.rs | 2 +- src/swap_bundle.rs | 7 ++++--- 5 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/bundle.rs b/src/bundle.rs index 4f67591a8..b5907ba0b 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -190,6 +190,12 @@ pub trait Authorization: fmt::Debug { type SpendAuth: fmt::Debug + Clone; } +/// Defines the authorization type of an Orchard bundle with a proof. +pub trait AuthorizedWithProof: Authorization { + /// Return the proof component of the authorizing data. + fn proof(&self) -> &Proof; +} + /// A bundle of actions to be applied to the ledger. #[derive(Clone)] pub struct Bundle { @@ -510,17 +516,19 @@ impl Authorized { } } - /// Return the proof component of the authorizing data. - pub fn proof(&self) -> &Proof { - &self.proof - } - /// Return the binding signature. pub fn binding_signature(&self) -> &redpallas::Signature { &self.binding_signature } } +impl AuthorizedWithProof for Authorized { + /// Return the proof component of the authorizing data. + fn proof(&self) -> &Proof { + &self.proof + } +} + impl Bundle { /// Computes a commitment to the authorizing data within for this bundle. /// diff --git a/src/bundle/batch.rs b/src/bundle/batch.rs index cc9811c18..db6de75f7 100644 --- a/src/bundle/batch.rs +++ b/src/bundle/batch.rs @@ -3,7 +3,7 @@ use pasta_curves::vesta; use rand::{CryptoRng, RngCore}; use tracing::debug; -use super::{Authorized, Bundle}; +use super::{Authorized, AuthorizedWithProof, Bundle}; use crate::{ circuit::VerifyingKey, diff --git a/src/domain/orchard_domain_vanilla.rs b/src/domain/orchard_domain_vanilla.rs index 69cd9a54c..7f2937b21 100644 --- a/src/domain/orchard_domain_vanilla.rs +++ b/src/domain/orchard_domain_vanilla.rs @@ -4,6 +4,13 @@ use blake2b_simd::Hash as Blake2bHash; use zcash_note_encryption_zsa::note_bytes::NoteBytesData; +use super::{ + orchard_domain::OrchardDomainCommon, + zcash_note_encryption_domain::{ + build_base_note_plaintext_bytes, Memo, COMPACT_NOTE_SIZE_VANILLA, NOTE_VERSION_BYTE_V2, + }, +}; +use crate::bundle::AuthorizedWithProof; use crate::{ bundle::{ commitments::{ @@ -16,13 +23,6 @@ use crate::{ Bundle, }; -use super::{ - orchard_domain::OrchardDomainCommon, - zcash_note_encryption_domain::{ - build_base_note_plaintext_bytes, Memo, COMPACT_NOTE_SIZE_VANILLA, NOTE_VERSION_BYTE_V2, - }, -}; - impl OrchardDomainCommon for OrchardVanilla { const COMPACT_NOTE_SIZE: usize = COMPACT_NOTE_SIZE_VANILLA; diff --git a/src/domain/orchard_domain_zsa.rs b/src/domain/orchard_domain_zsa.rs index 0c003de6c..f4fe31dd9 100644 --- a/src/domain/orchard_domain_zsa.rs +++ b/src/domain/orchard_domain_zsa.rs @@ -7,7 +7,7 @@ use zcash_note_encryption_zsa::note_bytes::NoteBytesData; use crate::bundle::commitments::{ ZCASH_ORCHARD_ACTION_GROUPS_SIGS_HASH_PERSONALIZATION, ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION, }; -use crate::bundle::Authorized; +use crate::bundle::{Authorized, AuthorizedWithProof}; use crate::{ bundle::{ commitments::{ diff --git a/src/swap_bundle.rs b/src/swap_bundle.rs index a7283d42b..5552ad21b 100644 --- a/src/swap_bundle.rs +++ b/src/swap_bundle.rs @@ -14,6 +14,7 @@ use crate::{ Proof, }; +use crate::bundle::AuthorizedWithProof; use k256::elliptic_curve::rand_core::{CryptoRng, RngCore}; /// An action group. @@ -131,7 +132,6 @@ pub struct SwapBundle { } impl SwapBundle { - /// Constructs a `SwapBundle` from its constituent parts. pub fn from_parts( action_groups: Vec>, @@ -146,7 +146,6 @@ impl SwapBundle { } } - impl + std::iter::Sum> SwapBundle { /// Constructs a `SwapBundle` from its action groups. pub fn new( @@ -202,9 +201,11 @@ impl ActionGroupAuthorized { pub fn from_parts(proof: Proof) -> Self { ActionGroupAuthorized { proof } } +} +impl AuthorizedWithProof for ActionGroupAuthorized { /// Return the proof component of the authorizing data. - pub fn proof(&self) -> &Proof { + fn proof(&self) -> &Proof { &self.proof } } From edcdbe63e98d936b4aa491e47c54717e4f4d67a3 Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Fri, 14 Feb 2025 17:53:56 +0100 Subject: [PATCH 08/20] Add SpendAuth to AuthorizedWithProof --- src/bundle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bundle.rs b/src/bundle.rs index b5907ba0b..14335b2a0 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -191,7 +191,7 @@ pub trait Authorization: fmt::Debug { } /// Defines the authorization type of an Orchard bundle with a proof. -pub trait AuthorizedWithProof: Authorization { +pub trait AuthorizedWithProof: Authorization> { /// Return the proof component of the authorizing data. fn proof(&self) -> &Proof; } From 07b94c8edf2ef36d5bde05b67624f4fef1a6c06c Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Sat, 15 Feb 2025 15:05:46 +0100 Subject: [PATCH 09/20] Change visibility of dummy methods --- src/note.rs | 2 +- src/tree.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/note.rs b/src/note.rs index a08469ef1..407968fa8 100644 --- a/src/note.rs +++ b/src/note.rs @@ -245,7 +245,7 @@ impl Note { /// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes]. /// /// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes - pub(crate) fn dummy( + pub fn dummy( rng: &mut impl RngCore, rho: Option, asset: AssetBase, diff --git a/src/tree.rs b/src/tree.rs index a10de6ea5..e8e13978f 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -114,7 +114,7 @@ impl From> for MerklePa impl MerklePath { /// Generates a dummy Merkle path for use in dummy spent notes. - pub(crate) fn dummy(mut rng: &mut impl RngCore) -> Self { + pub fn dummy(mut rng: &mut impl RngCore) -> Self { MerklePath { position: rng.next_u32(), auth_path: [(); MERKLE_DEPTH_ORCHARD] From 80b17dbe732113a087d9aba7f21272a757b9aab3 Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Sat, 15 Feb 2025 19:39:53 +0100 Subject: [PATCH 10/20] Remove redundant bundle type --- src/builder.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 538cea414..f05bc3b8f 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -65,12 +65,6 @@ impl BundleType { bundle_required: false, }; - /// The default bundle with all flags enabled, including ZSA and Swaps. // TODO swap flag? - pub const DEFAULT_SWAP: BundleType = BundleType::Transactional { - flags: Flags::ENABLED_WITH_ZSA, - bundle_required: false, - }; - /// The DISABLED bundle type does not permit any bundle to be produced, and when used in the /// builder will prevent any spends or outputs from being added. pub const DISABLED: BundleType = BundleType::Transactional { From f28965106e0dacc2fff642cd1e4afb36f1b63a88 Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Tue, 18 Feb 2025 20:42:05 +0100 Subject: [PATCH 11/20] Add Flag and BundleType for swaps --- src/builder.rs | 8 +++++++- src/bundle.rs | 35 +++++++++++++++++++++++++++------- src/circuit/circuit_vanilla.rs | 3 ++- src/circuit/circuit_zsa.rs | 3 ++- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index f05bc3b8f..0bce79aa0 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -65,10 +65,16 @@ impl BundleType { bundle_required: false, }; + /// The default bundle with all flags enabled, including Asset Swaps. + pub const DEFAULT_SWAP: BundleType = BundleType::Transactional { + flags: Flags::ENABLED_WITH_SWAPS, + bundle_required: false, + }; + /// The DISABLED bundle type does not permit any bundle to be produced, and when used in the /// builder will prevent any spends or outputs from being added. pub const DISABLED: BundleType = BundleType::Transactional { - flags: Flags::from_parts(false, false, false), + flags: Flags::from_parts(false, false, false, false), bundle_required: false, }; diff --git a/src/bundle.rs b/src/bundle.rs index 14335b2a0..75d1ae21d 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -64,12 +64,17 @@ pub struct Flags { /// If `false`, all notes within [`Action`]s in the transaction's [`Bundle`] are /// guaranteed to be notes with native asset. zsa_enabled: bool, + /// Flag denoting whether Asset Swaps are enabled. + /// + /// If `false`, [`Bundle`] is guaranteed to contain only one ['ActionGroup']. + swaps_enabled: bool, } const FLAG_SPENDS_ENABLED: u8 = 0b0000_0001; const FLAG_OUTPUTS_ENABLED: u8 = 0b0000_0010; const FLAG_ZSA_ENABLED: u8 = 0b0000_0100; -const FLAGS_EXPECTED_UNSET: u8 = !(FLAG_SPENDS_ENABLED | FLAG_OUTPUTS_ENABLED | FLAG_ZSA_ENABLED); +const FLAG_SWAPS_ENABLED: u8 = 0b0000_1000; +const FLAGS_EXPECTED_UNSET: u8 = !(FLAG_SPENDS_ENABLED | FLAG_OUTPUTS_ENABLED | FLAG_ZSA_ENABLED | FLAG_SWAPS_ENABLED); impl Flags { /// Construct a set of flags from its constituent parts @@ -77,40 +82,54 @@ impl Flags { spends_enabled: bool, outputs_enabled: bool, zsa_enabled: bool, + swaps_enabled: bool, ) -> Self { Flags { spends_enabled, outputs_enabled, zsa_enabled, + swaps_enabled, } } - /// The flag set with both spends and outputs enabled and ZSA disabled. + /// The flag set with both spends and outputs enabled. ZSA and swaps are disabled. pub const ENABLED_WITHOUT_ZSA: Flags = Flags { spends_enabled: true, outputs_enabled: true, zsa_enabled: false, + swaps_enabled: false, }; - /// The flags set with spends, outputs and ZSA enabled. + /// The flags set with spends, outputs and ZSA enabled. Swaps are disabled. pub const ENABLED_WITH_ZSA: Flags = Flags { spends_enabled: true, outputs_enabled: true, zsa_enabled: true, + swaps_enabled: false, + }; + + /// The flags set with spends, outputs, ZSA and swaps enabled. + pub const ENABLED_WITH_SWAPS: Flags = Flags { + spends_enabled: true, + outputs_enabled: true, + zsa_enabled: true, + swaps_enabled: true, }; - /// The flag set with spends and ZSA disabled. + /// The flag set with spends, ZSA and swaps disabled. pub const SPENDS_DISABLED_WITHOUT_ZSA: Flags = Flags { spends_enabled: false, outputs_enabled: true, zsa_enabled: false, + swaps_enabled: false, }; - /// The flag set with spends disabled and ZSA enabled. + /// The flag set with spends disabled and ZSA enabled. Swaps are disabled. pub const SPENDS_DISABLED_WITH_ZSA: Flags = Flags { spends_enabled: false, outputs_enabled: true, zsa_enabled: true, + swaps_enabled: false, }; /// The flag set with outputs disabled. @@ -118,6 +137,7 @@ impl Flags { spends_enabled: true, outputs_enabled: false, zsa_enabled: false, + swaps_enabled: false, }; /// Flag denoting whether Orchard spends are enabled in the transaction. @@ -177,6 +197,7 @@ impl Flags { spends_enabled: value & FLAG_SPENDS_ENABLED != 0, outputs_enabled: value & FLAG_OUTPUTS_ENABLED != 0, zsa_enabled: value & FLAG_ZSA_ENABLED != 0, + swaps_enabled: value & FLAG_SWAPS_ENABLED != 0, }) } else { None @@ -695,8 +716,8 @@ pub mod testing { prop_compose! { /// Create an arbitrary set of flags. - pub fn arb_flags()(spends_enabled in prop::bool::ANY, outputs_enabled in prop::bool::ANY, zsa_enabled in prop::bool::ANY) -> Flags { - Flags::from_parts(spends_enabled, outputs_enabled, zsa_enabled) + pub fn arb_flags()(spends_enabled in prop::bool::ANY, outputs_enabled in prop::bool::ANY, zsa_enabled in prop::bool::ANY, swaps_enabled in prop::bool::ANY) -> Flags { + Flags::from_parts(spends_enabled, outputs_enabled, zsa_enabled, swaps_enabled) } } diff --git a/src/circuit/circuit_vanilla.rs b/src/circuit/circuit_vanilla.rs index 4fcb95ce2..a19e03470 100644 --- a/src/circuit/circuit_vanilla.rs +++ b/src/circuit/circuit_vanilla.rs @@ -806,13 +806,14 @@ mod tests { let enable_spend = read_bool(&mut r); let enable_output = read_bool(&mut r); let enable_zsa = false; + let enable_swaps = false; let instance = Instance::from_parts( anchor, cv_net, nf_old, rk, cmx, - Flags::from_parts(enable_spend, enable_output, enable_zsa), + Flags::from_parts(enable_spend, enable_output, enable_zsa, enable_swaps), ); let mut proof_bytes = vec![]; diff --git a/src/circuit/circuit_zsa.rs b/src/circuit/circuit_zsa.rs index d87180a60..e6d3bb471 100644 --- a/src/circuit/circuit_zsa.rs +++ b/src/circuit/circuit_zsa.rs @@ -1050,13 +1050,14 @@ mod tests { let enable_spend = read_bool(&mut r); let enable_output = read_bool(&mut r); let enable_zsa = read_bool(&mut r); + let enable_swaps = false; let instance = Instance::from_parts( anchor, cv_net, nf_old, rk, cmx, - Flags::from_parts(enable_spend, enable_output, enable_zsa), + Flags::from_parts(enable_spend, enable_output, enable_zsa, enable_swaps), ); let mut proof_bytes = vec![]; From 15738e96637fe9351f035982e7a0c740d7c7da4a Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Wed, 19 Feb 2025 10:22:03 +0100 Subject: [PATCH 12/20] Format --- src/bundle.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bundle.rs b/src/bundle.rs index 75d1ae21d..074d5f501 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -74,7 +74,8 @@ const FLAG_SPENDS_ENABLED: u8 = 0b0000_0001; const FLAG_OUTPUTS_ENABLED: u8 = 0b0000_0010; const FLAG_ZSA_ENABLED: u8 = 0b0000_0100; const FLAG_SWAPS_ENABLED: u8 = 0b0000_1000; -const FLAGS_EXPECTED_UNSET: u8 = !(FLAG_SPENDS_ENABLED | FLAG_OUTPUTS_ENABLED | FLAG_ZSA_ENABLED | FLAG_SWAPS_ENABLED); +const FLAGS_EXPECTED_UNSET: u8 = + !(FLAG_SPENDS_ENABLED | FLAG_OUTPUTS_ENABLED | FLAG_ZSA_ENABLED | FLAG_SWAPS_ENABLED); impl Flags { /// Construct a set of flags from its constituent parts From 4fb1e5339a1b7c84b0b2c928ad36eab2054e653c Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Wed, 19 Feb 2025 10:28:49 +0100 Subject: [PATCH 13/20] Add missing import --- benches/circuit.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/benches/circuit.rs b/benches/circuit.rs index 029f80b55..c15a1ef7b 100644 --- a/benches/circuit.rs +++ b/benches/circuit.rs @@ -6,6 +6,7 @@ use criterion::{BenchmarkId, Criterion}; #[cfg(unix)] use pprof::criterion::{Output, PProfProfiler}; +use orchard::bundle::AuthorizedWithProof; use orchard::{ builder::{Builder, BundleType}, circuit::{ProvingKey, VerifyingKey}, From 5594483d4d5c35d2a1a0ed8f408f149ff4484986 Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Mon, 24 Feb 2025 19:00:41 +0100 Subject: [PATCH 14/20] Revert "Change visibility of dummy methods" This reverts commit 07b94c8edf2ef36d5bde05b67624f4fef1a6c06c. --- src/note.rs | 2 +- src/tree.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/note.rs b/src/note.rs index 407968fa8..a08469ef1 100644 --- a/src/note.rs +++ b/src/note.rs @@ -245,7 +245,7 @@ impl Note { /// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes]. /// /// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes - pub fn dummy( + pub(crate) fn dummy( rng: &mut impl RngCore, rho: Option, asset: AssetBase, diff --git a/src/tree.rs b/src/tree.rs index e8e13978f..a10de6ea5 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -114,7 +114,7 @@ impl From> for MerklePa impl MerklePath { /// Generates a dummy Merkle path for use in dummy spent notes. - pub fn dummy(mut rng: &mut impl RngCore) -> Self { + pub(crate) fn dummy(mut rng: &mut impl RngCore) -> Self { MerklePath { position: rng.next_u32(), auth_path: [(); MERKLE_DEPTH_ORCHARD] From 3987f5b167e1ace40f02fcfaaf9c96f3862870d1 Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Thu, 27 Feb 2025 09:54:35 +0100 Subject: [PATCH 15/20] Disable existing imports reordering --- rustfmt.toml | 1 + src/domain/orchard_domain_vanilla.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 rustfmt.toml diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..0465e5e47 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +reorder_imports = false \ No newline at end of file diff --git a/src/domain/orchard_domain_vanilla.rs b/src/domain/orchard_domain_vanilla.rs index 7f2937b21..a7ad15e7a 100644 --- a/src/domain/orchard_domain_vanilla.rs +++ b/src/domain/orchard_domain_vanilla.rs @@ -4,12 +4,6 @@ use blake2b_simd::Hash as Blake2bHash; use zcash_note_encryption_zsa::note_bytes::NoteBytesData; -use super::{ - orchard_domain::OrchardDomainCommon, - zcash_note_encryption_domain::{ - build_base_note_plaintext_bytes, Memo, COMPACT_NOTE_SIZE_VANILLA, NOTE_VERSION_BYTE_V2, - }, -}; use crate::bundle::AuthorizedWithProof; use crate::{ bundle::{ @@ -22,6 +16,12 @@ use crate::{ orchard_flavor::OrchardVanilla, Bundle, }; +use super::{ + orchard_domain::OrchardDomainCommon, + zcash_note_encryption_domain::{ + build_base_note_plaintext_bytes, Memo, COMPACT_NOTE_SIZE_VANILLA, NOTE_VERSION_BYTE_V2, + }, +}; impl OrchardDomainCommon for OrchardVanilla { const COMPACT_NOTE_SIZE: usize = COMPACT_NOTE_SIZE_VANILLA; From b92bd14e67e11440f5d3de775790e97cc82d2ee3 Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Sat, 1 Mar 2025 18:50:25 +0100 Subject: [PATCH 16/20] Remove ActionGroup structure --- src/builder.rs | 50 ++++---------- src/bundle.rs | 16 ++++- src/bundle/commitments.rs | 13 ++-- src/swap_bundle.rs | 138 +++++--------------------------------- tests/builder.rs | 9 +-- tests/zsa.rs | 56 ++++++++++------ 6 files changed, 86 insertions(+), 196 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 0bce79aa0..09acbe171 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -26,8 +26,8 @@ use crate::{ }, note::{AssetBase, Note, Rho, TransmittedNoteCiphertext}, orchard_flavor::{Flavor, OrchardFlavor, OrchardVanilla, OrchardZSA}, - primitives::redpallas::{self, Binding, SpendAuth}, - swap_bundle::{ActionGroup, ActionGroupAuthorized}, + primitives::redpallas::{self, Binding, SpendAuth, SigningKey}, + swap_bundle::ActionGroupAuthorized, tree::{Anchor, MerklePath}, value::{self, NoteValue, OverflowError, ValueCommitTrapdoor, ValueCommitment, ValueSum}, }; @@ -519,12 +519,6 @@ impl BundleMetadata { /// A tuple containing an in-progress bundle with no proofs or signatures, and its associated metadata. pub type UnauthorizedBundleWithMetadata = (UnauthorizedBundle, BundleMetadata); -/// A tuple containing an in-progress action group with no proofs or signatures, and its associated metadata. -pub type UnauthorizedActionGroupWithMetadata = ( - ActionGroup, V>, - BundleMetadata, -); - /// A builder that constructs a [`Bundle`] from a set of notes to be spent, and outputs /// to receive funds. #[derive(Debug)] @@ -704,6 +698,7 @@ impl Builder { self.bundle_type, self.spends, self.outputs, + 0, SpecificBuilderParams::BundleParams(self.burn), ) } @@ -715,8 +710,8 @@ impl Builder { pub fn build_action_group>( self, rng: impl RngCore, - timelimit: u32, - ) -> Result, BuildError> { + expiry_height: u32, + ) -> Result, BuildError> { if !self.burn.is_empty() { return Err(BuildError::BurnNotEmptyInActionGroup); } @@ -726,14 +721,9 @@ impl Builder { self.bundle_type, self.spends, self.outputs, + expiry_height, SpecificBuilderParams::ActionGroupParams(self.reference_notes), ) - .map(|(action_group, metadata)| { - ( - ActionGroup::from_parts(action_group, timelimit, None), - metadata, - ) - }) } } @@ -827,6 +817,7 @@ pub fn bundle, FL: OrchardFlavor>( bundle_type: BundleType, spends: Vec, outputs: Vec, + expiry_height: u32, specific_params: SpecificBuilderParams, ) -> Result, BuildError> { let flags = bundle_type.flags(); @@ -993,6 +984,7 @@ pub fn bundle, FL: OrchardFlavor>( result_value_balance, burn, anchor, + expiry_height, InProgress { proof: Unproven { witnesses, @@ -1133,8 +1125,8 @@ pub struct SigningMetadata { /// If this action is spending a dummy note, this field holds that note's spend /// authorizing key. /// - /// These keys are used automatically in [`Bundle::prepare`] or - /// [`Bundle::apply_signatures`] to sign dummy spends. + /// These keys are used automatically in [`Bundle::prepare`] or + /// [`Bundle::apply_signatures`] to sign dummy spends. dummy_ask: Option, parts: SigningParts, } @@ -1270,23 +1262,15 @@ impl Bundle, V, D> { }) .finalize() } -} -impl Bundle, V, D> { - /// Applies signatures to this action group, in order to authorize it. + /// Applies signatures to this bundle as an action group, in order to authorize it. #[allow(clippy::type_complexity)] pub fn apply_signatures_for_action_group( self, mut rng: R, action_group_digest: [u8; 32], signing_keys: &[SpendAuthorizingKey], - ) -> Result< - ( - redpallas::SigningKey, - Bundle, - ), - BuildError, - > { + ) -> Result<(Bundle, SigningKey), BuildError> { signing_keys .iter() .fold( @@ -1409,20 +1393,14 @@ impl Bundle Result< - ( - redpallas::SigningKey, - Bundle, - ), - BuildError, - > { + ) -> Result<(Bundle, SigningKey), BuildError> { let bsk = self.authorization().sigs.bsk; self.try_map_authorization( &mut (), |_, _, maybe| maybe.finalize(), |_, partial| Ok(ActionGroupAuthorized::from_parts(partial.proof)), ) - .map(|bundle| (bsk, bundle)) + .map(|bundle| (bundle, bsk)) } } diff --git a/src/bundle.rs b/src/bundle.rs index 074d5f501..8d83a2b1a 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -16,7 +16,7 @@ use zcash_note_encryption_zsa::{try_note_decryption, try_output_recovery_with_ov use crate::{ action::Action, address::Address, - bundle::commitments::{hash_bundle_auth_data, hash_bundle_txid_data}, + bundle::commitments::{hash_bundle_auth_data, hash_bundle_txid_data, hash_action_group}, circuit::{Instance, Proof, VerifyingKey}, domain::{OrchardDomain, OrchardDomainCommon}, keys::{IncomingViewingKey, OutgoingViewingKey, PreparedIncomingViewingKey}, @@ -26,6 +26,7 @@ use crate::{ tree::Anchor, value::{NoteValue, ValueCommitTrapdoor, ValueCommitment, ValueSum}, }; +use crate::orchard_flavor::OrchardZSA; impl Action { /// Prepares the public instance for this action, for creating and verifying the @@ -270,6 +271,7 @@ impl Bundle { value_balance: V, burn: Vec<(AssetBase, NoteValue)>, anchor: Anchor, + expiry_height: u32, authorization: A, ) -> Self { Bundle { @@ -278,7 +280,7 @@ impl Bundle { value_balance, burn, anchor, - expiry_height: 0, + expiry_height, authorization, } } @@ -518,6 +520,14 @@ impl, FL: OrchardFlavor> Bundle } } +impl> Bundle { + /// Computes a commitment to the effects of this bundle, + /// assuming that the bundle represents an action group inside a swap bundle. + pub fn action_group_commitment(&self) -> BundleCommitment { + BundleCommitment(hash_action_group(self)) + } +} + /// Authorizing data for a bundle of actions, ready to be committed to the ledger. #[derive(Debug, Clone)] pub struct Authorized { @@ -753,6 +763,7 @@ pub mod testing { balances.into_iter().sum::>().unwrap(), burn, anchor, + 0, Unauthorized, ) } @@ -785,6 +796,7 @@ pub mod testing { balances.into_iter().sum::>().unwrap(), burn, anchor, + 0, Authorized { proof: Proof::new(fake_proof), binding_signature: sk.sign(rng, &fake_sighash), diff --git a/src/bundle/commitments.rs b/src/bundle/commitments.rs index 1d5a3053e..250c3fbca 100644 --- a/src/bundle/commitments.rs +++ b/src/bundle/commitments.rs @@ -7,7 +7,6 @@ use crate::{ domain::OrchardDomainCommon, issuance::{IssueAuth, IssueBundle, Signed}, orchard_flavor::OrchardZSA, - swap_bundle::ActionGroup, }; // TODO remove @@ -64,13 +63,13 @@ pub fn hash_bundle_txid_empty() -> Blake2bHash { /// /// [zip228]: https://zips.z.cash/zip-0228 pub(crate) fn hash_action_group>( - action_group: &ActionGroup, + action_group: &Bundle, ) -> Blake2bHash { let mut agh = hasher(ZCASH_ORCHARD_ACTION_GROUPS_HASH_PERSONALIZATION); let mut ch = hasher(ZCASH_ORCHARD_ACTIONS_COMPACT_HASH_PERSONALIZATION); let mut mh = hasher(ZCASH_ORCHARD_ACTIONS_MEMOS_HASH_PERSONALIZATION); let mut nh = hasher(ZCASH_ORCHARD_ACTIONS_NONCOMPACT_HASH_PERSONALIZATION); - for action in action_group.action_group().actions().iter() { + for action in action_group.actions().iter() { ch.update(&action.nullifier().to_bytes()); ch.update(&action.cmx().to_bytes()); ch.update(&action.encrypted_note().epk_bytes); @@ -95,9 +94,9 @@ pub(crate) fn hash_action_group>( agh.update(ch.finalize().as_bytes()); agh.update(mh.finalize().as_bytes()); agh.update(nh.finalize().as_bytes()); - agh.update(&[action_group.action_group().flags().to_byte()]); - agh.update(&action_group.action_group().anchor().to_bytes()); - agh.update(&action_group.timelimit().to_le_bytes()); + agh.update(&[action_group.flags().to_byte()]); + agh.update(&action_group.anchor().to_bytes()); + agh.update(&action_group.expiry_height().to_le_bytes()); agh.finalize() } @@ -106,7 +105,7 @@ pub(crate) fn hash_action_group>( /// /// [zip228]: https://zips.z.cash/zip-0228 pub(crate) fn hash_swap_bundle>( - action_groups: Vec<&ActionGroup>, + action_groups: Vec<&Bundle>, value_balance: V, ) -> Blake2bHash { let mut h = hasher(ZCASH_ORCHARD_HASH_PERSONALIZATION); diff --git a/src/swap_bundle.rs b/src/swap_bundle.rs index 5552ad21b..c7486263b 100644 --- a/src/swap_bundle.rs +++ b/src/swap_bundle.rs @@ -1,12 +1,10 @@ //! Structs related to swap bundles. use crate::{ - builder::{BuildError, InProgress, InProgressSignatures, Unauthorized, Unproven}, - bundle::commitments::{hash_action_group, hash_swap_bundle}, + bundle::commitments::hash_swap_bundle, bundle::{derive_bvk, Authorization, Bundle, BundleCommitment}, - circuit::{ProvingKey, VerifyingKey}, + circuit::VerifyingKey, domain::OrchardDomainCommon, - keys::SpendAuthorizingKey, note::AssetBase, orchard_flavor::OrchardZSA, primitives::redpallas::{self, Binding, SpendAuth}, @@ -17,112 +15,11 @@ use crate::{ use crate::bundle::AuthorizedWithProof; use k256::elliptic_curve::rand_core::{CryptoRng, RngCore}; -/// An action group. -#[derive(Debug, Clone)] -pub struct ActionGroup { - /// The action group main content. - action_group: Bundle, - /// The action group timelimit. - timelimit: u32, - /// The binding signature key for the action group. - /// - /// During the building of the action group, this key is not set. - /// Once the action group is finalized (it contains a spend authorizing signature for each - /// action and a proof), the key is set. - bsk: Option>, -} - -impl ActionGroup { - /// Constructs an `ActionGroup` from its constituent parts. - pub fn from_parts( - action_group: Bundle, - timelimit: u32, - bsk: Option>, - ) -> Self { - ActionGroup { - action_group, - timelimit, - bsk, - } - } - - /// Returns the action group's main content. - pub fn action_group(&self) -> &Bundle { - &self.action_group - } - - /// Returns the action group's timelimit. - pub fn timelimit(&self) -> u32 { - self.timelimit - } - - /// Returns the action group's binding signature key. - pub fn bsk(&self) -> Option<&redpallas::SigningKey> { - self.bsk.as_ref() - } - - /// Remove bsk from this action group - /// - /// When creating a SwapBundle from a list of action groups, we evaluate the binding signature - /// by signing the sighash with the sum of the bsk of each action group. - /// Then, we remove the bsk of each action group as it is no longer needed. - fn remove_bsk(&mut self) { - self.bsk = None; - } -} - -impl ActionGroup, V> { - /// Creates the proof for this action group. - pub fn create_proof( - self, - pk: &ProvingKey, - mut rng: impl RngCore, - ) -> Result, V>, BuildError> { - let new_action_group = self.action_group.create_proof(pk, &mut rng)?; - Ok(ActionGroup { - action_group: new_action_group, - timelimit: self.timelimit, - bsk: self.bsk, - }) - } -} - -impl ActionGroup, V> { - /// Applies signatures to this action group, in order to authorize it. - pub fn apply_signatures( - self, - mut rng: R, - action_group_digest: [u8; 32], - signing_keys: &[SpendAuthorizingKey], - ) -> Result, BuildError> { - let (bsk, action_group) = signing_keys - .iter() - .fold( - self.action_group - .prepare_for_action_group(&mut rng, action_group_digest), - |partial, ask| partial.sign(&mut rng, ask), - ) - .finalize()?; - Ok(ActionGroup { - action_group, - timelimit: self.timelimit, - bsk: Some(bsk), - }) - } -} - -impl> ActionGroup { - /// Computes a commitment to the content of this action group. - pub fn commitment(&self) -> BundleCommitment { - BundleCommitment(hash_action_group(self)) - } -} - /// A swap bundle to be applied to the ledger. #[derive(Debug, Clone)] pub struct SwapBundle { /// The list of action groups that make up this swap bundle. - action_groups: Vec>, + action_groups: Vec>, /// The net value moved out of this swap. /// /// This is the sum of Orchard spends minus the sum of Orchard outputs. @@ -134,7 +31,7 @@ pub struct SwapBundle { impl SwapBundle { /// Constructs a `SwapBundle` from its constituent parts. pub fn from_parts( - action_groups: Vec>, + action_groups: Vec>, value_balance: V, binding_signature: redpallas::Signature, ) -> Self { @@ -147,25 +44,20 @@ impl SwapBundle { } impl + std::iter::Sum> SwapBundle { - /// Constructs a `SwapBundle` from its action groups. + /// Constructs a `SwapBundle` from its action groups and respective binding signature keys. + /// Keys should go in the same order as the action groups. pub fn new( rng: R, - mut action_groups: Vec>, + action_groups: Vec>, + bsks: Vec>, ) -> Self { + assert_eq!(action_groups.len(), bsks.len()); // Evaluate the swap value balance by summing the value balance of each action group. - let value_balance = action_groups - .iter() - .map(|a| *a.action_group().value_balance()) - .sum(); + let value_balance = action_groups.iter().map(|a| *a.value_balance()).sum(); // Evaluate the swap bsk by summing the bsk of each action group. - let bsk = action_groups - .iter_mut() - .map(|ag| { - let bsk = ValueCommitTrapdoor::from_bsk(ag.bsk.unwrap()); - // Remove the bsk of each action group as it is no longer needed. - ag.remove_bsk(); - bsk - }) + let bsk = bsks + .into_iter() + .map(ValueCommitTrapdoor::from_bsk) .sum::() .into_bsk(); // Evaluate the swap sighash @@ -221,7 +113,7 @@ impl Bundle { impl SwapBundle { /// Returns the list of action groups that make up this swap bundle. - pub fn action_groups(&self) -> &Vec> { + pub fn action_groups(&self) -> &Vec> { &self.action_groups } @@ -253,7 +145,7 @@ impl> SwapBundle { let actions = self .action_groups .iter() - .flat_map(|ag| ag.action_group().actions()) + .flat_map(|ag| ag.actions()) .collect::>(); derive_bvk( actions, diff --git a/tests/builder.rs b/tests/builder.rs index 2361041ac..905a8d9aa 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -8,7 +8,7 @@ use orchard::{ keys::{FullViewingKey, PreparedIncomingViewingKey, Scope, SpendAuthorizingKey, SpendingKey}, note::{AssetBase, ExtractedNoteCommitment}, orchard_flavor::{OrchardFlavor, OrchardVanilla, OrchardZSA}, - swap_bundle::{ActionGroup, ActionGroupAuthorized, SwapBundle}, + swap_bundle::{ActionGroupAuthorized, SwapBundle}, tree::{MerkleHashOrchard, MerklePath}, value::NoteValue, Anchor, Bundle, Note, @@ -43,8 +43,6 @@ pub fn verify_swap_bundle(swap_bundle: &SwapBundle, vks: Vec<&VerifyingKey> assert_eq!(vks.len(), swap_bundle.action_groups().len()); for (action_group, vk) in swap_bundle.action_groups().iter().zip(vks.iter()) { verify_action_group(action_group, vk); - // Verify that bsk is None - assert!(action_group.bsk().is_none()); } let sighash: [u8; 32] = swap_bundle.commitment().into(); @@ -59,13 +57,12 @@ pub fn verify_swap_bundle(swap_bundle: &SwapBundle, vks: Vec<&VerifyingKey> // - verify the proof // - verify the signature on each action pub fn verify_action_group( - action_group: &ActionGroup, + action_group_bundle: &Bundle, vk: &VerifyingKey, ) { - let action_group_bundle = action_group.action_group(); assert!(matches!(action_group_bundle.verify_proof(vk), Ok(()))); - let action_group_digest: [u8; 32] = action_group.commitment().into(); + let action_group_digest: [u8; 32] = action_group_bundle.action_group_commitment().into(); for action in action_group_bundle.actions() { assert_eq!( action diff --git a/tests/zsa.rs b/tests/zsa.rs index 6ce84409e..1f2b4e079 100644 --- a/tests/zsa.rs +++ b/tests/zsa.rs @@ -13,7 +13,8 @@ use orchard::{ keys::{IssuanceAuthorizingKey, IssuanceValidatingKey}, note::{AssetBase, ExtractedNoteCommitment, Nullifier}, orchard_flavor::OrchardZSA, - swap_bundle::{ActionGroup, ActionGroupAuthorized, SwapBundle}, + primitives::redpallas::{Binding, SigningKey}, + swap_bundle::{ActionGroupAuthorized, SwapBundle}, tree::{MerkleHashOrchard, MerklePath}, value::NoteValue, Address, Anchor, Bundle, Note, ReferenceKeys, @@ -103,13 +104,20 @@ fn build_and_sign_action_group( mut rng: OsRng, pk: &ProvingKey, sk: &SpendingKey, -) -> ActionGroup { +) -> ( + Bundle, + SigningKey, +) { let unauthorized = builder.build_action_group(&mut rng, timelimit).unwrap().0; - let action_group_digest = unauthorized.commitment().into(); + let action_group_digest = unauthorized.action_group_commitment().into(); let proven = unauthorized.create_proof(pk, &mut rng).unwrap(); proven - .apply_signatures(rng, action_group_digest, &[SpendAuthorizingKey::from(sk)]) + .apply_signatures_for_action_group( + rng, + action_group_digest, + &[SpendAuthorizingKey::from(sk)], + ) .unwrap() } @@ -287,6 +295,7 @@ fn build_and_verify_bundle( Ok(()) } +#[allow(clippy::type_complexity)] fn build_and_verify_action_group( spends: Vec<&TestSpendInfo>, outputs: Vec, @@ -295,9 +304,15 @@ fn build_and_verify_action_group( timelimit: u32, expected_num_actions: usize, keys: &Keychain, -) -> Result, String> { +) -> Result< + ( + Bundle, + SigningKey, + ), + String, +> { let rng = OsRng; - let shielded_action_group: ActionGroup<_, i64> = { + let (shielded_action_group, bsk) = { let mut builder = Builder::new(BundleType::DEFAULT_ZSA, anchor); spends @@ -327,14 +342,9 @@ fn build_and_verify_action_group( }; verify_action_group(&shielded_action_group, &keys.vk); - assert_eq!( - shielded_action_group.action_group().actions().len(), - expected_num_actions - ); - assert!(verify_unique_spent_nullifiers( - shielded_action_group.action_group() - )); - Ok(shielded_action_group) + assert_eq!(shielded_action_group.actions().len(), expected_num_actions); + assert!(verify_unique_spent_nullifiers(&shielded_action_group)); + Ok((shielded_action_group, bsk)) } fn verify_unique_spent_nullifiers(bundle: &Bundle) -> bool { @@ -792,7 +802,7 @@ fn action_group_and_swap_bundle() { { // 1. Create and verify ActionGroup for user1 - let action_group1 = build_and_verify_action_group( + let (action_group1, bsk1) = build_and_verify_action_group( vec![ &asset1_spend1, // 40 asset1 &asset1_spend2, // 2 asset1 @@ -832,7 +842,7 @@ fn action_group_and_swap_bundle() { .unwrap(); // 2. Create and verify ActionGroup for user2 - let action_group2 = build_and_verify_action_group( + let (action_group2, bsk2) = build_and_verify_action_group( vec![ &asset2_spend1, // 40 asset2 &asset2_spend2, // 2 asset2 @@ -871,7 +881,7 @@ fn action_group_and_swap_bundle() { .unwrap(); // 3. Matcher fees action group - let action_group_matcher = build_and_verify_action_group( + let (action_group_matcher, bsk_matcher) = build_and_verify_action_group( // The matcher spends nothing. vec![], // The matcher receives 5 ZEC as a fee from user1 and user2. @@ -894,6 +904,7 @@ fn action_group_and_swap_bundle() { let swap_bundle = SwapBundle::new( OsRng, vec![action_group1, action_group2, action_group_matcher], + vec![bsk1, bsk2, bsk_matcher], ); verify_swap_bundle(&swap_bundle, vec![&keys1.vk, &keys2.vk, &matcher_keys.vk]); } @@ -912,7 +923,7 @@ fn action_group_and_swap_bundle() { { // 1. Create and verify ActionGroup for user1 - let action_group1 = build_and_verify_action_group( + let (action_group1, bsk1) = build_and_verify_action_group( vec![ &asset1_spend1, // 40 asset1 &asset1_spend2, // 2 asset1 @@ -942,7 +953,7 @@ fn action_group_and_swap_bundle() { .unwrap(); // 2. Create and verify ActionGroup for user2 - let action_group2 = build_and_verify_action_group( + let (action_group2, bsk2) = build_and_verify_action_group( vec![ &user2_native_note1_spend, // 100 ZEC &user2_native_note2_spend, // 100 ZEC @@ -973,7 +984,7 @@ fn action_group_and_swap_bundle() { .unwrap(); // 3. Matcher fees action group - let action_group_matcher = build_and_verify_action_group( + let (action_group_matcher, bsk_matcher) = build_and_verify_action_group( // The matcher spends nothing. vec![], // The matcher receives 10 ZEC as a fee from user2. @@ -996,6 +1007,7 @@ fn action_group_and_swap_bundle() { let swap_bundle = SwapBundle::new( OsRng, vec![action_group1, action_group2, action_group_matcher], + vec![bsk1, bsk2, bsk_matcher], ); verify_swap_bundle(&swap_bundle, vec![&keys1.vk, &keys2.vk, &matcher_keys.vk]); } @@ -1004,7 +1016,7 @@ fn action_group_and_swap_bundle() { // User1 would like to send 30 asset1 to User2 { // 1. Create and verify ActionGroup - let action_group = build_and_verify_action_group( + let (action_group, bsk1) = build_and_verify_action_group( vec![ &asset1_spend1, // 40 asset1 &asset1_spend2, // 2 asset1 @@ -1031,7 +1043,7 @@ fn action_group_and_swap_bundle() { .unwrap(); // 2. Create a SwapBundle from the previous ActionGroup - let swap_bundle = SwapBundle::new(OsRng, vec![action_group]); + let swap_bundle = SwapBundle::new(OsRng, vec![action_group], vec![bsk1]); verify_swap_bundle(&swap_bundle, vec![&keys1.vk]); } } From 0d64df0e7775f374f40524581a0ee0e45405c840 Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Sat, 1 Mar 2025 19:48:34 +0100 Subject: [PATCH 17/20] Move proof method to Authorization --- benches/circuit.rs | 10 ++++++++-- src/builder.rs | 4 ++++ src/bundle.rs | 23 ++++++++++++----------- src/bundle/batch.rs | 4 ++-- src/domain/orchard_domain_vanilla.rs | 3 +-- src/domain/orchard_domain_zsa.rs | 4 ++-- src/swap_bundle.rs | 14 ++++++-------- 7 files changed, 35 insertions(+), 27 deletions(-) diff --git a/benches/circuit.rs b/benches/circuit.rs index c15a1ef7b..e5cf933a7 100644 --- a/benches/circuit.rs +++ b/benches/circuit.rs @@ -6,9 +6,9 @@ use criterion::{BenchmarkId, Criterion}; #[cfg(unix)] use pprof::criterion::{Output, PProfProfiler}; -use orchard::bundle::AuthorizedWithProof; use orchard::{ builder::{Builder, BundleType}, + bundle::Authorization, circuit::{ProvingKey, VerifyingKey}, keys::{FullViewingKey, Scope, SpendingKey}, note::AssetBase, @@ -87,7 +87,13 @@ fn criterion_benchmark(c: &mut Criterion) { .unwrap(); assert!(bundle.verify_proof(&vk).is_ok()); group.bench_function(BenchmarkId::new("bundle", num_recipients), |b| { - b.iter(|| bundle.authorization().proof().verify(&vk, &instances)); + b.iter(|| { + bundle + .authorization() + .proof() + .unwrap() + .verify(&vk, &instances) + }); }); } } diff --git a/src/builder.rs b/src/builder.rs index 09acbe171..543cfeaf6 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1025,6 +1025,10 @@ impl InProgress { impl Authorization for InProgress { type SpendAuth = S::SpendAuth; + + fn proof(&self) -> Option<&Proof> { + None + } } /// Marker for a bundle without a proof. diff --git a/src/bundle.rs b/src/bundle.rs index 8d83a2b1a..4334809ce 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -211,12 +211,9 @@ impl Flags { pub trait Authorization: fmt::Debug { /// The authorization type of an Orchard action. type SpendAuth: fmt::Debug + Clone; -} -/// Defines the authorization type of an Orchard bundle with a proof. -pub trait AuthorizedWithProof: Authorization> { /// Return the proof component of the authorizing data. - fn proof(&self) -> &Proof; + fn proof(&self) -> Option<&Proof>; } /// A bundle of actions to be applied to the ledger. @@ -537,6 +534,11 @@ pub struct Authorized { impl Authorization for Authorized { type SpendAuth = redpallas::Signature; + + /// Return the proof component of the authorizing data. + fn proof(&self) -> Option<&Proof> { + Some(&self.proof) + } } impl Authorized { @@ -554,13 +556,6 @@ impl Authorized { } } -impl AuthorizedWithProof for Authorized { - /// Return the proof component of the authorizing data. - fn proof(&self) -> &Proof { - &self.proof - } -} - impl Bundle { /// Computes a commitment to the authorizing data within for this bundle. /// @@ -573,6 +568,7 @@ impl Bundle { pub fn verify_proof(&self, vk: &VerifyingKey) -> Result<(), halo2_proofs::plonk::Error> { self.authorization() .proof() + .unwrap() .verify(vk, &self.to_instances()) } } @@ -654,6 +650,11 @@ pub mod testing { impl Authorization for Unauthorized { type SpendAuth = (); + + /// Return the proof component of the authorizing data. + fn proof(&self) -> Option<&Proof> { + None + } } /// `BundleArb` adapts `arb_...` functions for both Vanilla and ZSA Orchard protocol variations diff --git a/src/bundle/batch.rs b/src/bundle/batch.rs index db6de75f7..34fc3ba0b 100644 --- a/src/bundle/batch.rs +++ b/src/bundle/batch.rs @@ -3,7 +3,7 @@ use pasta_curves::vesta; use rand::{CryptoRng, RngCore}; use tracing::debug; -use super::{Authorized, AuthorizedWithProof, Bundle}; +use super::{Authorized, Bundle}; use crate::{ circuit::VerifyingKey, @@ -58,7 +58,7 @@ impl BatchValidator { bundle .authorization() - .proof() + .proof .add_to_batch(&mut self.proofs, bundle.to_instances()); } diff --git a/src/domain/orchard_domain_vanilla.rs b/src/domain/orchard_domain_vanilla.rs index a7ad15e7a..e524977bc 100644 --- a/src/domain/orchard_domain_vanilla.rs +++ b/src/domain/orchard_domain_vanilla.rs @@ -4,7 +4,6 @@ use blake2b_simd::Hash as Blake2bHash; use zcash_note_encryption_zsa::note_bytes::NoteBytesData; -use crate::bundle::AuthorizedWithProof; use crate::{ bundle::{ commitments::{ @@ -66,7 +65,7 @@ impl OrchardDomainCommon for OrchardVanilla { /// [zip244]: https://zips.z.cash/zip-0244 fn hash_bundle_auth_data(bundle: &Bundle) -> Blake2bHash { let mut h = hasher(ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION); - h.update(bundle.authorization().proof().as_ref()); + h.update(bundle.authorization().proof().unwrap().as_ref()); for action in bundle.actions().iter() { h.update(&<[u8; 64]>::from(action.authorization())); } diff --git a/src/domain/orchard_domain_zsa.rs b/src/domain/orchard_domain_zsa.rs index f4fe31dd9..5a36b8c5e 100644 --- a/src/domain/orchard_domain_zsa.rs +++ b/src/domain/orchard_domain_zsa.rs @@ -7,7 +7,7 @@ use zcash_note_encryption_zsa::note_bytes::NoteBytesData; use crate::bundle::commitments::{ ZCASH_ORCHARD_ACTION_GROUPS_SIGS_HASH_PERSONALIZATION, ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION, }; -use crate::bundle::{Authorized, AuthorizedWithProof}; +use crate::bundle::Authorized; use crate::{ bundle::{ commitments::{ @@ -91,7 +91,7 @@ impl OrchardDomainCommon for OrchardZSA { fn hash_bundle_auth_data(bundle: &Bundle) -> Blake2bHash { let mut h = hasher(ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION); let mut agh = hasher(ZCASH_ORCHARD_ACTION_GROUPS_SIGS_HASH_PERSONALIZATION); - agh.update(bundle.authorization().proof().as_ref()); + agh.update(bundle.authorization().proof().unwrap().as_ref()); for action in bundle.actions().iter() { agh.update(&<[u8; 64]>::from(action.authorization())); } diff --git a/src/swap_bundle.rs b/src/swap_bundle.rs index c7486263b..1cbe1dbc8 100644 --- a/src/swap_bundle.rs +++ b/src/swap_bundle.rs @@ -12,7 +12,6 @@ use crate::{ Proof, }; -use crate::bundle::AuthorizedWithProof; use k256::elliptic_curve::rand_core::{CryptoRng, RngCore}; /// A swap bundle to be applied to the ledger. @@ -86,6 +85,11 @@ pub struct ActionGroupAuthorized { impl Authorization for ActionGroupAuthorized { type SpendAuth = redpallas::Signature; + + /// Return the proof component of the authorizing data. + fn proof(&self) -> Option<&Proof> { + Some(&self.proof) + } } impl ActionGroupAuthorized { @@ -95,18 +99,12 @@ impl ActionGroupAuthorized { } } -impl AuthorizedWithProof for ActionGroupAuthorized { - /// Return the proof component of the authorizing data. - fn proof(&self) -> &Proof { - &self.proof - } -} - impl Bundle { /// Verifies the proof for this bundle. pub fn verify_proof(&self, vk: &VerifyingKey) -> Result<(), halo2_proofs::plonk::Error> { self.authorization() .proof() + .unwrap() .verify(vk, &self.to_instances()) } } From 695b604bd0d827428ce2c95cdf3703c3bace8c5a Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Thu, 6 Mar 2025 17:45:05 +0100 Subject: [PATCH 18/20] Deduplicate action group hash --- src/bundle/commitments.rs | 27 +-------------------------- src/domain/orchard_domain_zsa.rs | 17 +++++------------ 2 files changed, 6 insertions(+), 38 deletions(-) diff --git a/src/bundle/commitments.rs b/src/bundle/commitments.rs index 250c3fbca..bfaf11188 100644 --- a/src/bundle/commitments.rs +++ b/src/bundle/commitments.rs @@ -66,34 +66,9 @@ pub(crate) fn hash_action_group>( action_group: &Bundle, ) -> Blake2bHash { let mut agh = hasher(ZCASH_ORCHARD_ACTION_GROUPS_HASH_PERSONALIZATION); - let mut ch = hasher(ZCASH_ORCHARD_ACTIONS_COMPACT_HASH_PERSONALIZATION); - let mut mh = hasher(ZCASH_ORCHARD_ACTIONS_MEMOS_HASH_PERSONALIZATION); - let mut nh = hasher(ZCASH_ORCHARD_ACTIONS_NONCOMPACT_HASH_PERSONALIZATION); - for action in action_group.actions().iter() { - ch.update(&action.nullifier().to_bytes()); - ch.update(&action.cmx().to_bytes()); - ch.update(&action.encrypted_note().epk_bytes); - ch.update( - &action.encrypted_note().enc_ciphertext.as_ref()[..OrchardZSA::COMPACT_NOTE_SIZE], - ); - - mh.update( - &action.encrypted_note().enc_ciphertext.as_ref() - [OrchardZSA::COMPACT_NOTE_SIZE..OrchardZSA::COMPACT_NOTE_SIZE + MEMO_SIZE], - ); - nh.update(&action.cv_net().to_bytes()); - nh.update(&<[u8; 32]>::from(action.rk())); - nh.update( - &action.encrypted_note().enc_ciphertext.as_ref() - [OrchardZSA::COMPACT_NOTE_SIZE + MEMO_SIZE..], - ); - nh.update(&action.encrypted_note().out_ciphertext); - } + OrchardZSA::update_hash_with_actions(&mut agh, action_group); - agh.update(ch.finalize().as_bytes()); - agh.update(mh.finalize().as_bytes()); - agh.update(nh.finalize().as_bytes()); agh.update(&[action_group.flags().to_byte()]); agh.update(&action_group.anchor().to_bytes()); agh.update(&action_group.expiry_height().to_le_bytes()); diff --git a/src/domain/orchard_domain_zsa.rs b/src/domain/orchard_domain_zsa.rs index 5a36b8c5e..46b8bfabb 100644 --- a/src/domain/orchard_domain_zsa.rs +++ b/src/domain/orchard_domain_zsa.rs @@ -5,14 +5,14 @@ use blake2b_simd::Hash as Blake2bHash; use zcash_note_encryption_zsa::note_bytes::NoteBytesData; use crate::bundle::commitments::{ - ZCASH_ORCHARD_ACTION_GROUPS_SIGS_HASH_PERSONALIZATION, ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION, + hash_action_group, ZCASH_ORCHARD_ACTION_GROUPS_SIGS_HASH_PERSONALIZATION, + ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION, }; use crate::bundle::Authorized; use crate::{ bundle::{ commitments::{ - hasher, ZCASH_ORCHARD_ACTION_GROUPS_HASH_PERSONALIZATION, - ZCASH_ORCHARD_HASH_PERSONALIZATION, ZCASH_ORCHARD_ZSA_BURN_HASH_PERSONALIZATION, + hasher, ZCASH_ORCHARD_HASH_PERSONALIZATION, ZCASH_ORCHARD_ZSA_BURN_HASH_PERSONALIZATION, }, Authorization, }, @@ -63,15 +63,8 @@ impl OrchardDomainCommon for OrchardZSA { bundle: &Bundle, ) -> Blake2bHash { let mut h = hasher(ZCASH_ORCHARD_HASH_PERSONALIZATION); - let mut agh = hasher(ZCASH_ORCHARD_ACTION_GROUPS_HASH_PERSONALIZATION); - - Self::update_hash_with_actions(&mut agh, bundle); - - agh.update(&[bundle.flags().to_byte()]); - agh.update(&bundle.anchor().to_bytes()); - agh.update(&bundle.expiry_height().to_le_bytes()); - - h.update(agh.finalize().as_bytes()); + let action_group_hash = hash_action_group(bundle); + h.update(action_group_hash.as_bytes()); let mut burn_hasher = hasher(ZCASH_ORCHARD_ZSA_BURN_HASH_PERSONALIZATION); for burn_item in bundle.burn() { From 0b9a7cb5310b3caabdebbeaef96b3fd6bd2615a5 Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Sun, 9 Mar 2025 17:47:49 +0100 Subject: [PATCH 19/20] Fix formating --- rustfmt.toml | 2 +- src/domain/orchard_domain_vanilla.rs | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/rustfmt.toml b/rustfmt.toml index 0465e5e47..fa091418c 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1 +1 @@ -reorder_imports = false \ No newline at end of file +reorder_imports = false diff --git a/src/domain/orchard_domain_vanilla.rs b/src/domain/orchard_domain_vanilla.rs index e524977bc..f35963729 100644 --- a/src/domain/orchard_domain_vanilla.rs +++ b/src/domain/orchard_domain_vanilla.rs @@ -4,6 +4,13 @@ use blake2b_simd::Hash as Blake2bHash; use zcash_note_encryption_zsa::note_bytes::NoteBytesData; +use super::{ + orchard_domain::OrchardDomainCommon, + zcash_note_encryption_domain::{ + build_base_note_plaintext_bytes, Memo, COMPACT_NOTE_SIZE_VANILLA, NOTE_VERSION_BYTE_V2, + }, +}; + use crate::{ bundle::{ commitments::{ @@ -15,12 +22,6 @@ use crate::{ orchard_flavor::OrchardVanilla, Bundle, }; -use super::{ - orchard_domain::OrchardDomainCommon, - zcash_note_encryption_domain::{ - build_base_note_plaintext_bytes, Memo, COMPACT_NOTE_SIZE_VANILLA, NOTE_VERSION_BYTE_V2, - }, -}; impl OrchardDomainCommon for OrchardVanilla { const COMPACT_NOTE_SIZE: usize = COMPACT_NOTE_SIZE_VANILLA; From b7dc5d5632528e0bf8ed9b81af4f7e267875ed0f Mon Sep 17 00:00:00 2001 From: alexeykoren <2365507+alexeykoren@users.noreply.github.com> Date: Sun, 9 Mar 2025 17:53:34 +0100 Subject: [PATCH 20/20] Fix comments --- src/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/builder.rs b/src/builder.rs index 543cfeaf6..6565724fa 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -706,7 +706,7 @@ impl Builder { /// Builds an action group containing the given spent and output notes. /// /// The returned action group will have no proof or signatures; these can be applied with - /// [`ActionGroup::create_proof`] and [`ActionGroup::apply_signatures`] respectively. + /// [`Bundle::create_proof`] and [`Bundle::apply_signatures`] respectively. pub fn build_action_group>( self, rng: impl RngCore,