diff --git a/src/builder.rs b/src/builder.rs index e0370f736..72dae73c9 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -442,12 +442,12 @@ impl OutputInfo { cv_net: &ValueCommitment, nf_old: Nullifier, rng: impl RngCore, - ) -> crate::pczt::Output { - let (note, cmx, encrypted_note) = self.build(cv_net, nf_old, rng); + ) -> crate::pczt::Output { + let (note, cmx, encrypted_note) = self.build::(cv_net, nf_old, rng); crate::pczt::Output { cmx, - encrypted_note, + encrypted_note: encrypted_note.into(), recipient: Some(self.recipient), value: Some(self.value), rseed: Some(*note.rseed()), @@ -532,10 +532,7 @@ impl ActionInfo { ) } - fn build_for_pczt( - self, - mut rng: impl RngCore, - ) -> crate::pczt::Action { + fn build_for_pczt(self, mut rng: impl RngCore) -> crate::pczt::Action { assert_eq!( self.spend.note.asset(), self.output.asset, @@ -545,7 +542,9 @@ impl ActionInfo { let cv_net = ValueCommitment::derive(v_net, self.rcv, self.spend.note.asset()); let spend = self.spend.into_pczt(&mut rng); - let output = self.output.into_pczt(&cv_net, spend.nullifier, &mut rng); + let output = self + .output + .into_pczt::(&cv_net, spend.nullifier, &mut rng); crate::pczt::Action { cv_net, @@ -778,7 +777,7 @@ impl Builder { pub fn build_for_pczt( self, rng: impl RngCore, - ) -> Result<(crate::pczt::Bundle, BundleMetadata), BuildError> { + ) -> Result<(crate::pczt::Bundle, BundleMetadata), BuildError> { build_bundle( rng, self.anchor, @@ -790,7 +789,7 @@ impl Builder { // Create the actions. let actions = pre_actions .into_iter() - .map(|a| a.build_for_pczt(&mut rng)) + .map(|a| a.build_for_pczt::(&mut rng)) .collect::>(); Ok(( diff --git a/src/issuance.rs b/src/issuance.rs index 8ea0fa8f6..396fd82ab 100644 --- a/src/issuance.rs +++ b/src/issuance.rs @@ -217,6 +217,12 @@ impl IssueAction { /// Defines the authorization type of an Issue bundle. pub trait IssueAuth: fmt::Debug + Clone {} +/// Marker type for a bundle that contains no authorizing data. +#[derive(Clone, Debug)] +pub struct EffectsOnly; + +impl IssueAuth for EffectsOnly {} + /// Marker for an unsigned bundle with no nullifier and no sighash injected. #[derive(Debug, Clone)] pub struct AwaitingNullifier; diff --git a/src/pczt.rs b/src/pczt.rs index 27d70577d..f68824fc4 100644 --- a/src/pczt.rs +++ b/src/pczt.rs @@ -7,6 +7,7 @@ use core::fmt; use getset::Getters; use pasta_curves::pallas; +use zcash_note_encryption_zsa::note_bytes::NoteBytes; use zcash_note_encryption_zsa::OutgoingCipherKey; use zip32::ChildIndex; @@ -54,12 +55,12 @@ pub use tx_extractor::{TxExtractorError, Unbound}; /// [the regular `Bundle` struct]: crate::Bundle #[derive(Debug, Getters)] #[getset(get = "pub")] -pub struct Bundle { +pub struct Bundle { /// The Orchard actions in this bundle. /// /// Entries are added by the Constructor, and modified by an Updater, IO Finalizer, /// Signer, Combiner, or Spend Finalizer. - pub(crate) actions: Vec>, + pub(crate) actions: Vec, /// The flags for the Orchard bundle. /// @@ -104,14 +105,14 @@ pub struct Bundle { pub(crate) bsk: Option>, } -impl Bundle { +impl Bundle { /// Returns a mutable reference to the actions in this bundle. /// /// This is used by Signers to apply signatures with [`Action::sign`]. /// /// Note: updating the `Action`s via the returned slice will not update other /// fields of the bundle dependent on them, such as `value_sum` and `bsk`. - pub fn actions_mut(&mut self) -> &mut [Action] { + pub fn actions_mut(&mut self) -> &mut [Action] { &mut self.actions } } @@ -124,7 +125,7 @@ impl Bundle { /// [the regular `Action` struct]: crate::Action #[derive(Debug, Getters)] #[getset(get = "pub")] -pub struct Action { +pub struct Action { /// A commitment to the net value created or consumed by this action. pub(crate) cv_net: ValueCommitment, @@ -132,7 +133,7 @@ pub struct Action { pub(crate) spend: Spend, /// The output half of this action. - pub(crate) output: Output, + pub(crate) output: Output, /// The value commitment randomness. /// @@ -248,7 +249,7 @@ pub struct Spend { /// Information about an Orchard output within a transaction. #[derive(Getters)] #[getset(get = "pub")] -pub struct Output { +pub struct Output { /// A commitment to the new note being created. pub(crate) cmx: ExtractedNoteCommitment, @@ -258,7 +259,7 @@ pub struct Output { /// - `ephemeral_key` /// - `enc_ciphertext` /// - `out_ciphertext` - pub(crate) encrypted_note: TransmittedNoteCiphertext, + pub(crate) encrypted_note: PcztTransmittedNoteCiphertext, /// The address that will receive the output. /// @@ -309,7 +310,7 @@ pub struct Output { pub(crate) proprietary: BTreeMap>, } -impl fmt::Debug for Output { +impl fmt::Debug for Output { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Output") .field("cmx", &self.cmx) @@ -364,6 +365,39 @@ impl Zip32Derivation { } } +/// An encrypted note. +#[derive(Clone, Debug)] +pub struct PcztTransmittedNoteCiphertext { + /// The serialization of the ephemeral public key + pub epk_bytes: [u8; 32], + /// The encrypted note ciphertext + pub enc_ciphertext: Vec, + /// 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 From> for PcztTransmittedNoteCiphertext { + fn from(transmitted: TransmittedNoteCiphertext) -> Self { + PcztTransmittedNoteCiphertext { + epk_bytes: transmitted.epk_bytes, + enc_ciphertext: transmitted.enc_ciphertext.as_ref().to_vec(), + out_ciphertext: transmitted.out_ciphertext, + } + } +} + +impl From for TransmittedNoteCiphertext { + fn from(ciphertext: PcztTransmittedNoteCiphertext) -> Self { + TransmittedNoteCiphertext { + epk_bytes: ciphertext.epk_bytes, + enc_ciphertext: D::NoteCiphertextBytes::from_slice(&ciphertext.enc_ciphertext) + .expect("Failed to parse enc_ciphertext: data may be corrupt or incorrect size"), + out_ciphertext: ciphertext.out_ciphertext, + } + } +} + #[cfg(test)] mod tests { use crate::orchard_flavor::{OrchardFlavor, OrchardVanilla, OrchardZSA}; @@ -418,7 +452,7 @@ mod tests { pczt_bundle.create_proof::(&pk, rng.clone()).unwrap(); // Run the Transaction Extractor role. - let bundle = pczt_bundle.extract::().unwrap().unwrap(); + let bundle = pczt_bundle.extract::().unwrap().unwrap(); let orchard_digest = hash_bundle_txid_data(&bundle); @@ -574,7 +608,7 @@ mod tests { } // Run the Transaction Extractor role. - let bundle = pczt_bundle.extract::().unwrap().unwrap(); + let bundle = pczt_bundle.extract::().unwrap().unwrap(); let orchard_digest = hash_bundle_txid_data(&bundle); diff --git a/src/pczt/io_finalizer.rs b/src/pczt/io_finalizer.rs index 6857b7227..ea8b69354 100644 --- a/src/pczt/io_finalizer.rs +++ b/src/pczt/io_finalizer.rs @@ -4,11 +4,11 @@ use rand::{CryptoRng, RngCore}; use super::SignerError; use crate::{ - bundle::derive_bvk_raw, domain::OrchardDomainCommon, keys::SpendAuthorizingKey, - primitives::redpallas, value::ValueCommitTrapdoor, + bundle::derive_bvk_raw, keys::SpendAuthorizingKey, primitives::redpallas, + value::ValueCommitTrapdoor, }; -impl super::Bundle { +impl super::Bundle { /// Finalizes the IO for this bundle. pub fn finalize_io( &mut self, diff --git a/src/pczt/parse.rs b/src/pczt/parse.rs index 8b7bc4643..1ebd31de8 100644 --- a/src/pczt/parse.rs +++ b/src/pczt/parse.rs @@ -5,29 +5,26 @@ use alloc::vec::Vec; use ff::PrimeField; use incrementalmerkletree::Hashable; use pasta_curves::pallas; -use zcash_note_encryption_zsa::{note_bytes::NoteBytes, OutgoingCipherKey}; +use zcash_note_encryption_zsa::OutgoingCipherKey; use zip32::ChildIndex; -use super::{Action, Bundle, Output, Spend, Zip32Derivation}; +use super::{Action, Bundle, Output, PcztTransmittedNoteCiphertext, Spend, Zip32Derivation}; use crate::{ bundle::Flags, - domain::OrchardDomainCommon, keys::{FullViewingKey, SpendingKey}, - note::{ - AssetBase, ExtractedNoteCommitment, Nullifier, RandomSeed, Rho, TransmittedNoteCiphertext, - }, + note::{AssetBase, ExtractedNoteCommitment, Nullifier, RandomSeed, Rho}, primitives::redpallas::{self, SpendAuth}, tree::{MerkleHashOrchard, MerklePath}, value::{NoteValue, Sign, ValueCommitTrapdoor, ValueCommitment, ValueSum}, Address, Anchor, Proof, NOTE_COMMITMENT_TREE_DEPTH, }; -impl Bundle { +impl Bundle { /// Parses a PCZT bundle from its component parts. /// `value_sum` is represented as `(magnitude, is_negative)`. #[allow(clippy::too_many_arguments)] pub fn parse( - actions: Vec>, + actions: Vec, flags: u8, value_sum: (u64, bool), anchor: [u8; 32], @@ -85,12 +82,12 @@ impl Bundle { } } -impl Action { +impl Action { /// Parses a PCZT action from its component parts. pub fn parse( cv_net: [u8; 32], spend: Spend, - output: Output, + output: Output, rcv: Option<[u8; 32]>, ) -> Result { let cv_net = ValueCommitment::from_bytes(&cv_net) @@ -240,7 +237,7 @@ impl Spend { } } -impl Output { +impl Output { /// Parses a PCZT output from its component parts, and the corresponding `Spend`'s /// nullifier. #[allow(clippy::too_many_arguments)] @@ -262,10 +259,9 @@ impl Output { .into_option() .ok_or(ParseError::InvalidExtractedNoteCommitment)?; - let encrypted_note = TransmittedNoteCiphertext { + let encrypted_note = PcztTransmittedNoteCiphertext { epk_bytes: ephemeral_key, - enc_ciphertext: D::NoteCiphertextBytes::from_slice(enc_ciphertext.as_slice()) - .ok_or(ParseError::InvalidEncCiphertext)?, + enc_ciphertext: enc_ciphertext.to_vec(), out_ciphertext: out_ciphertext .as_slice() .try_into() diff --git a/src/pczt/prover.rs b/src/pczt/prover.rs index 708e4379b..e89fd1199 100644 --- a/src/pczt/prover.rs +++ b/src/pczt/prover.rs @@ -6,13 +6,12 @@ use rand::{CryptoRng, RngCore}; use crate::{ builder::SpendInfo, circuit::{Circuit, Instance, ProvingKey, Witnesses}, - domain::OrchardDomainCommon, note::Rho, orchard_flavor::OrchardFlavor, Note, Proof, }; -impl super::Bundle { +impl super::Bundle { /// Adds a proof to this PCZT bundle. pub fn create_proof( &mut self, diff --git a/src/pczt/signer.rs b/src/pczt/signer.rs index cac4661e1..7f5f8028c 100644 --- a/src/pczt/signer.rs +++ b/src/pczt/signer.rs @@ -1,8 +1,8 @@ use rand::{CryptoRng, RngCore}; -use crate::{domain::OrchardDomainCommon, keys::SpendAuthorizingKey, primitives::redpallas}; +use crate::{keys::SpendAuthorizingKey, primitives::redpallas}; -impl super::Action { +impl super::Action { /// Signs the Orchard spend with the given spend authorizing key. /// /// It is the caller's responsibility to perform any semantic validity checks on the diff --git a/src/pczt/tx_extractor.rs b/src/pczt/tx_extractor.rs index c883e172e..f71a05d3e 100644 --- a/src/pczt/tx_extractor.rs +++ b/src/pczt/tx_extractor.rs @@ -9,13 +9,13 @@ use crate::{ Proof, }; -impl super::Bundle { +impl super::Bundle { /// Extracts the effects of this PCZT bundle as a [regular `Bundle`]. /// /// This is used by the Signer role to produce the transaction sighash. /// /// [regular `Bundle`]: crate::Bundle - pub fn extract_effects>( + pub fn extract_effects, D: OrchardDomainCommon>( &self, ) -> Result>, TxExtractorError> { self.to_tx_data(|_| Ok(()), |_| Ok(EffectsOnly)) @@ -26,7 +26,7 @@ impl super::Bundle { /// This is used by the Transaction Extractor role to produce the final transaction. /// /// [regular `Bundle`]: crate::Bundle - pub fn extract>( + pub fn extract, D: OrchardDomainCommon>( self, ) -> Result>, TxExtractorError> { self.to_tx_data( @@ -52,7 +52,7 @@ impl super::Bundle { } /// Converts this PCZT bundle into a regular bundle with the given authorizations. - fn to_tx_data( + fn to_tx_data( &self, action_auth: F, bundle_auth: G, @@ -60,9 +60,10 @@ impl super::Bundle { where A: Authorization, E: From, - F: Fn(&Action) -> Result<::SpendAuth, E>, + F: Fn(&Action) -> Result<::SpendAuth, E>, G: FnOnce(&Self) -> Result, V: TryFrom, + D: OrchardDomainCommon, { let actions = self .actions @@ -74,7 +75,7 @@ impl super::Bundle { action.spend.nullifier, action.spend.rk.clone(), action.output.cmx, - action.output.encrypted_note.clone(), + action.output.encrypted_note.clone().into(), action.cv_net.clone(), authorization, )) diff --git a/src/pczt/updater.rs b/src/pczt/updater.rs index d7936f854..306c18163 100644 --- a/src/pczt/updater.rs +++ b/src/pczt/updater.rs @@ -1,13 +1,12 @@ use super::{Action, Bundle, Zip32Derivation}; -use crate::domain::OrchardDomainCommon; use alloc::string::String; use alloc::vec::Vec; -impl Bundle { +impl Bundle { /// Updates the bundle with information provided in the given closure. pub fn update_with(&mut self, f: F) -> Result<(), UpdaterError> where - F: FnOnce(Updater<'_, D>) -> Result<(), UpdaterError>, + F: FnOnce(Updater<'_>) -> Result<(), UpdaterError>, { f(Updater(self)) } @@ -15,11 +14,11 @@ impl Bundle { /// An updater for an Orchard PCZT bundle. #[derive(Debug)] -pub struct Updater<'a, D: OrchardDomainCommon>(&'a mut Bundle); +pub struct Updater<'a>(&'a mut Bundle); -impl Updater<'_, D> { +impl Updater<'_> { /// Provides read access to the bundle being updated. - pub fn bundle(&self) -> &Bundle { + pub fn bundle(&self) -> &Bundle { self.0 } @@ -27,7 +26,7 @@ impl Updater<'_, D> { /// closure. pub fn update_action_with(&mut self, index: usize, f: F) -> Result<(), UpdaterError> where - F: FnOnce(ActionUpdater<'_, D>) -> Result<(), UpdaterError>, + F: FnOnce(ActionUpdater<'_>) -> Result<(), UpdaterError>, { f(ActionUpdater( self.0 @@ -40,9 +39,9 @@ impl Updater<'_, D> { /// An updater for an Orchard PCZT action. #[derive(Debug)] -pub struct ActionUpdater<'a, D: OrchardDomainCommon>(&'a mut Action); +pub struct ActionUpdater<'a>(&'a mut Action); -impl ActionUpdater<'_, D> { +impl ActionUpdater<'_> { /// Sets the ZIP 32 derivation path for the spent note's signing key. pub fn set_spend_zip32_derivation(&mut self, derivation: Zip32Derivation) { self.0.spend.zip32_derivation = Some(derivation); diff --git a/src/pczt/verify.rs b/src/pczt/verify.rs index eb80fef18..bae471c61 100644 --- a/src/pczt/verify.rs +++ b/src/pczt/verify.rs @@ -1,12 +1,11 @@ use crate::{ - domain::OrchardDomainCommon, keys::{FullViewingKey, SpendValidatingKey}, note::{ExtractedNoteCommitment, Rho}, value::ValueCommitment, Note, }; -impl super::Action { +impl super::Action { /// Verifies that the `cv_net` field is consistent with the note fields. /// /// Requires that the following optional fields are set: @@ -117,7 +116,7 @@ impl super::Spend { } } -impl super::Output { +impl super::Output { /// Verifies that the `cmx` field is consistent with the note fields. /// /// Requires that the following optional fields are set: