diff --git a/src/issuance.rs b/src/issuance.rs index 88719eb72..d312b0dbe 100644 --- a/src/issuance.rs +++ b/src/issuance.rs @@ -7,8 +7,8 @@ use std::fmt; use crate::bundle::commitments::{hash_issue_bundle_auth_data, hash_issue_bundle_txid_data}; use crate::issuance::Error::{ - IssueActionAlreadyFinalized, IssueActionIncorrectAssetBase, IssueActionNotFound, - IssueActionPreviouslyFinalizedAssetBase, IssueBundleIkMismatchAssetBase, + IssueActionNotFound, IssueActionPreviouslyFinalizedAssetBase, + IssueActionWithoutNoteNotFinalized, IssueBundleIkMismatchAssetBase, IssueBundleInvalidSignature, ValueSumOverflow, WrongAssetDescSize, }; use crate::keys::{IssuanceAuthorizingKey, IssuanceValidatingKey}; @@ -30,7 +30,7 @@ pub struct IssueBundle { /// The issuer key for the note being created. ik: IssuanceValidatingKey, /// The list of issue actions that make up this bundle. - actions: Vec, + actions: NonEmpty, /// The authorization for this action. authorization: T, } @@ -43,26 +43,23 @@ pub struct IssueAction { /// Asset description for verification. asset_desc: String, /// The newly issued notes. - notes: NonEmpty, + notes: Vec, /// `finalize` will prevent further issuance of the same asset type. finalize: bool, } -impl IssueAction { - /// Constructs a new `IssueAction`. - pub fn new(asset_desc: String, note: &Note) -> Self { - IssueAction { - asset_desc, - notes: NonEmpty { - head: *note, - tail: vec![], - }, - finalize: false, - } - } +/// The parameters required to add a Note into an IssueAction. +#[derive(Debug)] +pub struct IssueInfo { + /// The recipient of the funds. + pub recipient: Address, + /// The value of this note. + pub value: NoteValue, +} +impl IssueAction { /// Constructs a new `IssueAction`. - pub fn new_with_finalize(asset_desc: String, note: &Note, flags: u8) -> Option { + pub fn new_with_flags(asset_desc: String, notes: Vec, flags: u8) -> Option { let finalize = match flags { 0b0000_0000 => false, 0b0000_0001 => true, @@ -70,16 +67,13 @@ impl IssueAction { }; Some(IssueAction { asset_desc, - notes: NonEmpty { - head: *note, - tail: vec![], - }, + notes, finalize, }) } /// Constructs an `IssueAction` from its constituent parts. - pub fn from_parts(asset_desc: String, notes: NonEmpty, finalize: bool) -> Self { + pub fn from_parts(asset_desc: String, notes: Vec, finalize: bool) -> Self { IssueAction { asset_desc, notes, @@ -93,7 +87,7 @@ impl IssueAction { } /// Returns the issued notes. - pub fn notes(&self) -> &NonEmpty { + pub fn notes(&self) -> &Vec { &self.notes } @@ -121,35 +115,39 @@ impl IssueAction { /// /// This function may return an error in any of the following cases: /// - /// * `IssueActionIncorrectAssetBase`: If the asset type of any note in the `IssueAction` is - /// not equal to the asset type of the first note. - /// /// * `ValueSumOverflow`: If the total amount value of all notes in the `IssueAction` overflows. /// /// * `IssueBundleIkMismatchAssetBase`: If the provided `ik` is not used to derive the /// `AssetBase` for **all** internal notes. + /// + /// * `IssueActionWithoutNoteNotFinalized`:If the `IssueAction` contains no note and is not finalized. fn verify_supply(&self, ik: &IssuanceValidatingKey) -> Result<(AssetBase, AssetSupply), Error> { + if self.notes.is_empty() && !self.is_finalized() { + return Err(IssueActionWithoutNoteNotFinalized); + } + + let issue_asset = AssetBase::derive(ik, &self.asset_desc); + // Calculate the value of the asset as a sum of values of all its notes - // and ensure all note types are equal - let (asset, value_sum) = self.notes.iter().try_fold( - (self.notes().head.asset(), ValueSum::zero()), - |(asset, value_sum), ¬e| { - // All assets should have the same `AssetBase` + // and ensure all note types are equal the asset derived from asset_desc and ik. + let value_sum = self + .notes + .iter() + .try_fold(ValueSum::zero(), |value_sum, ¬e| { + // All assets should be derived correctly note.asset() - .eq(&asset) + .eq(&issue_asset) .then(|| ()) - .ok_or(IssueActionIncorrectAssetBase)?; + .ok_or(IssueBundleIkMismatchAssetBase)?; // The total amount should not overflow - Ok((asset, (value_sum + note.value()).ok_or(ValueSumOverflow)?)) - }, - )?; + (value_sum + note.value()).ok_or(ValueSumOverflow) + })?; - // Return the asset and its supply (or an error if the asset was not properly derived) - asset - .eq(&AssetBase::derive(ik, &self.asset_desc)) - .then(|| Ok((asset, AssetSupply::new(value_sum, self.is_finalized())))) - .ok_or(IssueBundleIkMismatchAssetBase)? + Ok(( + issue_asset, + AssetSupply::new(value_sum, self.is_finalized()), + )) } /// Serialize `finalize` flag to a byte @@ -199,7 +197,7 @@ impl IssueBundle { &self.ik } /// Return the actions for a given `IssueBundle`. - pub fn actions(&self) -> &Vec { + pub fn actions(&self) -> &NonEmpty { &self.actions } /// Return the notes from all actions for a given `IssueBundle`. @@ -238,7 +236,7 @@ impl IssueBundle { /// Constructs an `IssueBundle` from its constituent parts. pub fn from_parts( ik: IssuanceValidatingKey, - actions: Vec, + actions: NonEmpty, authorization: T, ) -> Self { IssueBundle { @@ -251,27 +249,77 @@ impl IssueBundle { impl IssueBundle { /// Constructs a new `IssueBundle`. - pub fn new(ik: IssuanceValidatingKey) -> IssueBundle { - IssueBundle { - ik, - actions: Vec::new(), - authorization: Unauthorized, + /// + /// If issue_info is None, the new `IssueBundle` will contain one `IssueAction` without notes + /// and with `finalize` set to true. + /// Otherwise, the new `IssueBundle` will contain one `IssueAction with one note created from + /// issue_info values and with `finalize` set to false. In this created note, rho will be + /// randomly sampled, similar to dummy note generation. + /// + /// # Errors + /// + /// This function may return an error in any of the following cases: + /// + /// * `WrongAssetDescSize`: If `asset_desc` is empty or longer than 512 bytes. + pub fn new( + ik: IssuanceValidatingKey, + asset_desc: String, + issue_info: Option, + mut rng: impl RngCore, + ) -> Result<(IssueBundle, AssetBase), Error> { + if !is_asset_desc_of_valid_size(&asset_desc) { + return Err(WrongAssetDescSize); } + + let asset = AssetBase::derive(&ik, &asset_desc); + + let action = match issue_info { + None => IssueAction { + asset_desc, + notes: vec![], + finalize: true, + }, + Some(issue_info) => { + let note = Note::new( + issue_info.recipient, + issue_info.value, + asset, + Nullifier::dummy(&mut rng), + &mut rng, + ); + + IssueAction { + asset_desc, + notes: vec![note], + finalize: false, + } + } + }; + + Ok(( + IssueBundle { + ik, + actions: NonEmpty::new(action), + authorization: Unauthorized, + }, + asset, + )) } /// Add a new note to the `IssueBundle`. /// /// Rho will be randomly sampled, similar to dummy note generation. /// - /// # Panics + /// # Errors /// - /// Panics if `asset_desc` is empty or longer than 512 bytes. + /// This function may return an error in any of the following cases: + /// + /// * `WrongAssetDescSize`: If `asset_desc` is empty or longer than 512 bytes. pub fn add_recipient( &mut self, asset_desc: String, recipient: Address, value: NoteValue, - finalize: bool, mut rng: impl RngCore, ) -> Result { if !is_asset_desc_of_valid_size(&asset_desc) { @@ -288,26 +336,25 @@ impl IssueBundle { &mut rng, ); - match self + let action = self .actions .iter_mut() - .find(|issue_action| issue_action.asset_desc.eq(&asset_desc)) - { - // Append to an existing IssueAction. + .find(|issue_action| issue_action.asset_desc.eq(&asset_desc)); + + match action { Some(action) => { - if action.finalize { - return Err(IssueActionAlreadyFinalized); - }; + // Append to an existing IssueAction. action.notes.push(note); - finalize.then(|| action.finalize = true); } - // Insert a new IssueAction. None => { - let mut action = IssueAction::new(asset_desc, ¬e); - finalize.then(|| action.finalize = true); - self.actions.push(action); + // Insert a new IssueAction. + self.actions.push(IssueAction { + asset_desc, + notes: vec![note], + finalize: false, + }); } - } + }; Ok(asset) } @@ -430,7 +477,6 @@ impl IssueBundle { /// asset in the bundle is incorrect. /// * `IssueActionPreviouslyFinalizedAssetBase`: This error occurs if the asset has already been /// finalized (inserted into the `finalized` collection). -/// * `IssueActionIncorrectAssetBase`: This error occurs if any note has an incorrect note type. /// * `ValueSumOverflow`: This error occurs if an overflow happens during the calculation of /// the value sum for the notes in the asset. /// * `IssueBundleIkMismatchAssetBase`: This error is raised if the `AssetBase` derived from @@ -473,16 +519,14 @@ pub fn verify_issue_bundle( /// Errors produced during the issuance process #[derive(Debug, PartialEq, Eq)] pub enum Error { - /// Unable to add note to the IssueAction since it has already been finalized. - IssueActionAlreadyFinalized, /// The requested IssueAction not exists in the bundle. IssueActionNotFound, - /// Not all `AssetBase`s are the same inside the action. - IssueActionIncorrectAssetBase, /// The provided `isk` and the driven `ik` does not match at least one note type. IssueBundleIkMismatchAssetBase, /// `asset_desc` should be between 1 and 512 bytes. WrongAssetDescSize, + /// The `IssueAction` is not finalized but contains no notes. + IssueActionWithoutNoteNotFinalized, /// Verification errors: /// Invalid signature. @@ -499,18 +543,9 @@ impl std::error::Error for Error {} impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - IssueActionAlreadyFinalized => { - write!( - f, - "unable to add note to the IssueAction since it has already been finalized" - ) - } IssueActionNotFound => { write!(f, "the requested IssueAction not exists in the bundle.") } - IssueActionIncorrectAssetBase => { - write!(f, "not all `AssetBase`s are the same inside the action") - } IssueBundleIkMismatchAssetBase => { write!( f, @@ -520,6 +555,12 @@ impl fmt::Display for Error { WrongAssetDescSize => { write!(f, "`asset_desc` should be between 1 and 512 bytes") } + IssueActionWithoutNoteNotFinalized => { + write!( + f, + "this `IssueAction` contains no notes but is not finalized" + ) + } IssueBundleInvalidSignature(_) => { write!(f, "invalid signature") } @@ -538,11 +579,10 @@ impl fmt::Display for Error { #[cfg(test)] mod tests { - use super::{AssetSupply, IssueBundle}; + use super::{AssetSupply, IssueBundle, IssueInfo}; use crate::issuance::Error::{ - IssueActionAlreadyFinalized, IssueActionIncorrectAssetBase, IssueActionNotFound, - IssueActionPreviouslyFinalizedAssetBase, IssueBundleIkMismatchAssetBase, - IssueBundleInvalidSignature, WrongAssetDescSize, + IssueActionNotFound, IssueActionPreviouslyFinalizedAssetBase, + IssueBundleIkMismatchAssetBase, IssueBundleInvalidSignature, WrongAssetDescSize, }; use crate::issuance::{verify_issue_bundle, IssueAction, Signed}; use crate::keys::{ @@ -551,11 +591,9 @@ mod tests { use crate::note::{AssetBase, Nullifier}; use crate::value::{NoteValue, ValueSum}; use crate::{Address, Note}; - use nonempty::NonEmpty; use rand::rngs::OsRng; use rand::RngCore; use reddsa::Error::InvalidSignature; - use std::borrow::BorrowMut; use std::collections::HashSet; fn setup_params() -> ( @@ -611,14 +649,7 @@ mod tests { ( ik, asset, - IssueAction::from_parts( - note1_asset_desc.into(), - NonEmpty { - head: note1, - tail: vec![note2], - }, - finalize, - ), + IssueAction::from_parts(note1_asset_desc.into(), vec![note1, note2], finalize), ) } @@ -661,7 +692,7 @@ mod tests { assert_eq!( action.verify_supply(&ik), - Err(IssueActionIncorrectAssetBase) + Err(IssueBundleIkMismatchAssetBase) ); } @@ -680,48 +711,55 @@ mod tests { fn issue_bundle_basic() { let (rng, _, ik, recipient, _) = setup_params(); - let mut bundle = IssueBundle::new(ik); - let str = String::from("Halo"); let str2 = String::from("Halo2"); assert_eq!( - bundle - .add_recipient( - String::from_utf8(vec![b'X'; 513]).unwrap(), + IssueBundle::new( + ik.clone(), + String::from_utf8(vec![b'X'; 513]).unwrap(), + Some(IssueInfo { recipient, - NoteValue::unsplittable(), - true, - rng, - ) - .unwrap_err(), + value: NoteValue::unsplittable() + }), + rng, + ) + .unwrap_err(), WrongAssetDescSize ); assert_eq!( - bundle - .add_recipient( - "".to_string(), + IssueBundle::new( + ik.clone(), + "".to_string(), + Some(IssueInfo { recipient, - NoteValue::unsplittable(), - true, - rng, - ) - .unwrap_err(), + value: NoteValue::unsplittable() + }), + rng, + ) + .unwrap_err(), WrongAssetDescSize ); - let asset = bundle - .add_recipient(str.clone(), recipient, NoteValue::from_raw(5), false, rng) - .unwrap(); + let (mut bundle, asset) = IssueBundle::new( + ik, + str.clone(), + Some(IssueInfo { + recipient, + value: NoteValue::from_raw(5), + }), + rng, + ) + .unwrap(); let another_asset = bundle - .add_recipient(str, recipient, NoteValue::from_raw(10), false, rng) + .add_recipient(str, recipient, NoteValue::from_raw(10), rng) .unwrap(); assert_eq!(asset, another_asset); let third_asset = bundle - .add_recipient(str2.clone(), recipient, NoteValue::from_raw(15), false, rng) + .add_recipient(str2.clone(), recipient, NoteValue::from_raw(15), rng) .unwrap(); assert_ne!(asset, third_asset); @@ -730,53 +768,39 @@ mod tests { let action = bundle.get_action_by_type(asset).unwrap(); assert_eq!(action.notes.len(), 2); - assert_eq!(action.notes.first().value().inner(), 5); - assert_eq!(action.notes.first().asset(), asset); - assert_eq!(action.notes.first().recipient(), recipient); + assert_eq!(action.notes.first().unwrap().value().inner(), 5); + assert_eq!(action.notes.first().unwrap().asset(), asset); + assert_eq!(action.notes.first().unwrap().recipient(), recipient); - assert_eq!(action.notes.tail().first().unwrap().value().inner(), 10); - assert_eq!(action.notes.tail().first().unwrap().asset(), asset); - assert_eq!(action.notes.tail().first().unwrap().recipient(), recipient); + assert_eq!(action.notes.get(1).unwrap().value().inner(), 10); + assert_eq!(action.notes.get(1).unwrap().asset(), asset); + assert_eq!(action.notes.get(1).unwrap().recipient(), recipient); let action2 = bundle.get_action(str2).unwrap(); assert_eq!(action2.notes.len(), 1); - assert_eq!(action2.notes().first().value().inner(), 15); - assert_eq!(action2.notes().first().asset(), third_asset); + assert_eq!(action2.notes().first().unwrap().value().inner(), 15); + assert_eq!(action2.notes().first().unwrap().asset(), third_asset); } #[test] fn issue_bundle_finalize_asset() { let (rng, _, ik, recipient, _) = setup_params(); - let mut bundle = IssueBundle::new(ik); - - bundle - .add_recipient( - String::from("NFT"), + let (mut bundle, _) = IssueBundle::new( + ik, + String::from("NFT"), + Some(IssueInfo { recipient, - NoteValue::from_raw(u64::MIN), - false, - rng, - ) - .expect("Should properly add recipient"); + value: NoteValue::from_raw(u64::MIN), + }), + rng, + ) + .expect("Should properly add recipient"); bundle .finalize_action(String::from("NFT")) .expect("Should finalize properly"); - assert_eq!( - bundle - .add_recipient( - String::from("NFT"), - recipient, - NoteValue::unsplittable(), - false, - rng, - ) - .unwrap_err(), - IssueActionAlreadyFinalized - ); - assert_eq!( bundle .finalize_action(String::from("Another NFT")) @@ -795,46 +819,22 @@ mod tests { bundle.finalize_action("".to_string()).unwrap_err(), WrongAssetDescSize ); - - bundle - .add_recipient( - String::from("Another NFT"), - recipient, - NoteValue::unsplittable(), - true, - rng, - ) - .expect("should add and finalize"); - - assert_eq!( - bundle - .add_recipient( - String::from("Another NFT"), - recipient, - NoteValue::unsplittable(), - true, - rng, - ) - .unwrap_err(), - IssueActionAlreadyFinalized - ); } #[test] fn issue_bundle_prepare() { let (rng, _, ik, recipient, sighash) = setup_params(); - let mut bundle = IssueBundle::new(ik); - - bundle - .add_recipient( - String::from("Frost"), + let (bundle, _) = IssueBundle::new( + ik, + String::from("Frost"), + Some(IssueInfo { recipient, - NoteValue::from_raw(5), - false, - rng, - ) - .unwrap(); + value: NoteValue::from_raw(5), + }), + rng, + ) + .unwrap(); let prepared = bundle.prepare(sighash); assert_eq!(prepared.authorization().sighash, sighash); @@ -844,17 +844,16 @@ mod tests { fn issue_bundle_sign() { let (rng, isk, ik, recipient, sighash) = setup_params(); - let mut bundle = IssueBundle::new(ik.clone()); - - bundle - .add_recipient( - String::from("Sign"), + let (bundle, _) = IssueBundle::new( + ik.clone(), + String::from("Sign"), + Some(IssueInfo { recipient, - NoteValue::from_raw(5), - false, - rng, - ) - .unwrap(); + value: NoteValue::from_raw(5), + }), + rng, + ) + .unwrap(); let signed = bundle.prepare(sighash).sign(rng, &isk).unwrap(); @@ -866,17 +865,16 @@ mod tests { fn issue_bundle_invalid_isk_for_signature() { let (rng, _, ik, recipient, _) = setup_params(); - let mut bundle = IssueBundle::new(ik); - - bundle - .add_recipient( - String::from("IssueBundle"), + let (bundle, _) = IssueBundle::new( + ik, + String::from("IssueBundle"), + Some(IssueInfo { recipient, - NoteValue::from_raw(5), - false, - rng, - ) - .unwrap(); + value: NoteValue::from_raw(5), + }), + rng, + ) + .unwrap(); let wrong_isk: IssuanceAuthorizingKey = (&SpendingKey::random(&mut OsRng)).into(); @@ -892,18 +890,17 @@ mod tests { fn issue_bundle_incorrect_asset_for_signature() { let (mut rng, isk, ik, recipient, _) = setup_params(); - let mut bundle = IssueBundle::new(ik); - - // Add "normal" note - bundle - .add_recipient( - String::from("IssueBundle"), + // Create a bundle with "normal" note + let (mut bundle, _) = IssueBundle::new( + ik, + String::from("IssueBundle"), + Some(IssueInfo { recipient, - NoteValue::from_raw(5), - false, - rng, - ) - .unwrap(); + value: NoteValue::from_raw(5), + }), + rng, + ) + .unwrap(); // Add "bad" note let note = Note::new( @@ -913,37 +910,30 @@ mod tests { Nullifier::dummy(&mut rng), &mut rng, ); - bundle - .actions - .first_mut() - .unwrap() - .notes - .borrow_mut() - .push(note); + bundle.actions.first_mut().notes.push(note); let err = bundle .prepare([0; 32]) .sign(rng, &isk) .expect_err("should not be able to sign"); - assert_eq!(err, IssueActionIncorrectAssetBase); + assert_eq!(err, IssueBundleIkMismatchAssetBase); } #[test] fn issue_bundle_verify() { let (rng, isk, ik, recipient, sighash) = setup_params(); - let mut bundle = IssueBundle::new(ik); - - bundle - .add_recipient( - String::from("Verify"), + let (bundle, _) = IssueBundle::new( + ik, + String::from("Verify"), + Some(IssueInfo { recipient, - NoteValue::from_raw(5), - false, - rng, - ) - .unwrap(); + value: NoteValue::from_raw(5), + }), + rng, + ) + .unwrap(); let signed = bundle.prepare(sighash).sign(rng, &isk).unwrap(); let prev_finalized = &mut HashSet::new(); @@ -959,16 +949,19 @@ mod tests { fn issue_bundle_verify_with_finalize() { let (rng, isk, ik, recipient, sighash) = setup_params(); - let mut bundle = IssueBundle::new(ik.clone()); + let (mut bundle, _) = IssueBundle::new( + ik.clone(), + String::from("Verify with finalize"), + Some(IssueInfo { + recipient, + value: NoteValue::from_raw(7), + }), + rng, + ) + .unwrap(); bundle - .add_recipient( - String::from("Verify with finalize"), - recipient, - NoteValue::from_raw(7), - true, - rng, - ) + .finalize_action(String::from("Verify with finalize")) .unwrap(); let signed = bundle.prepare(sighash).sign(rng, &isk).unwrap(); @@ -986,8 +979,6 @@ mod tests { fn issue_bundle_verify_with_supply_info() { let (rng, isk, ik, recipient, sighash) = setup_params(); - let mut bundle = IssueBundle::new(ik.clone()); - let asset1_desc = "Verify with supply info 1"; let asset2_desc = "Verify with supply info 2"; let asset3_desc = "Verify with supply info 3"; @@ -996,42 +987,44 @@ mod tests { let asset2_base = AssetBase::derive(&ik, &String::from(asset2_desc)); let asset3_base = AssetBase::derive(&ik, &String::from(asset3_desc)); - bundle - .add_recipient( - String::from(asset1_desc), + let (mut bundle, _) = IssueBundle::new( + ik, + String::from(asset1_desc), + Some(IssueInfo { recipient, - NoteValue::from_raw(7), - false, - rng, - ) - .unwrap(); + value: NoteValue::from_raw(7), + }), + rng, + ) + .unwrap(); bundle .add_recipient( String::from(asset1_desc), recipient, NoteValue::from_raw(8), - true, rng, ) .unwrap(); + bundle.finalize_action(String::from(asset1_desc)).unwrap(); + bundle .add_recipient( String::from(asset2_desc), recipient, NoteValue::from_raw(10), - true, rng, ) .unwrap(); + bundle.finalize_action(String::from(asset2_desc)).unwrap(); + bundle .add_recipient( String::from(asset3_desc), recipient, NoteValue::from_raw(5), - false, rng, ) .unwrap(); @@ -1069,17 +1062,16 @@ mod tests { fn issue_bundle_verify_fail_previously_finalized() { let (rng, isk, ik, recipient, sighash) = setup_params(); - let mut bundle = IssueBundle::new(ik.clone()); - - bundle - .add_recipient( - String::from("already final"), + let (bundle, _) = IssueBundle::new( + ik.clone(), + String::from("already final"), + Some(IssueInfo { recipient, - NoteValue::from_raw(5), - false, - rng, - ) - .unwrap(); + value: NoteValue::from_raw(5), + }), + rng, + ) + .unwrap(); let signed = bundle.prepare(sighash).sign(rng, &isk).unwrap(); let prev_finalized = &mut HashSet::new(); @@ -1105,17 +1097,16 @@ mod tests { let (mut rng, isk, ik, recipient, sighash) = setup_params(); - let mut bundle = IssueBundle::new(ik); - - bundle - .add_recipient( - String::from("bad sig"), + let (bundle, _) = IssueBundle::new( + ik, + String::from("bad sig"), + Some(IssueInfo { recipient, - NoteValue::from_raw(5), - false, - rng, - ) - .unwrap(); + value: NoteValue::from_raw(5), + }), + rng, + ) + .unwrap(); let wrong_isk: IssuanceAuthorizingKey = (&SpendingKey::random(&mut rng)).into(); @@ -1136,17 +1127,16 @@ mod tests { #[test] fn issue_bundle_verify_fail_wrong_sighash() { let (rng, isk, ik, recipient, random_sighash) = setup_params(); - let mut bundle = IssueBundle::new(ik); - - bundle - .add_recipient( - String::from("Asset description"), + let (bundle, _) = IssueBundle::new( + ik, + String::from("Asset description"), + Some(IssueInfo { recipient, - NoteValue::from_raw(5), - false, - rng, - ) - .unwrap(); + value: NoteValue::from_raw(5), + }), + rng, + ) + .unwrap(); let sighash: [u8; 32] = bundle.commitment().into(); let signed = bundle.prepare(sighash).sign(rng, &isk).unwrap(); @@ -1162,17 +1152,16 @@ mod tests { fn issue_bundle_verify_fail_incorrect_asset_description() { let (mut rng, isk, ik, recipient, sighash) = setup_params(); - let mut bundle = IssueBundle::new(ik); - - bundle - .add_recipient( - String::from("Asset description"), + let (bundle, _) = IssueBundle::new( + ik, + String::from("Asset description"), + Some(IssueInfo { recipient, - NoteValue::from_raw(5), - false, - rng, - ) - .unwrap(); + value: NoteValue::from_raw(5), + }), + rng, + ) + .unwrap(); let mut signed = bundle.prepare(sighash).sign(rng, &isk).unwrap(); @@ -1185,19 +1174,13 @@ mod tests { &mut rng, ); - signed - .actions - .first_mut() - .unwrap() - .notes - .borrow_mut() - .push(note); + signed.actions.first_mut().notes.push(note); let prev_finalized = &HashSet::new(); assert_eq!( verify_issue_bundle(&signed, sighash, prev_finalized).unwrap_err(), - IssueActionIncorrectAssetBase + IssueBundleIkMismatchAssetBase ); } @@ -1207,17 +1190,16 @@ mod tests { let (mut rng, isk, ik, recipient, sighash) = setup_params(); - let mut bundle = IssueBundle::new(ik); - - bundle - .add_recipient( - String::from(asset_description), + let (bundle, _) = IssueBundle::new( + ik, + String::from(asset_description), + Some(IssueInfo { recipient, - NoteValue::from_raw(5), - false, - rng, - ) - .unwrap(); + value: NoteValue::from_raw(5), + }), + rng, + ) + .unwrap(); let mut signed = bundle.prepare(sighash).sign(rng, &isk).unwrap(); @@ -1234,7 +1216,7 @@ mod tests { &mut rng, ); - signed.actions.first_mut().unwrap().notes = NonEmpty::new(note); + signed.actions.first_mut().notes = vec![note]; let prev_finalized = &HashSet::new(); @@ -1255,17 +1237,16 @@ mod tests { let (rng, isk, ik, recipient, sighash) = setup_params(); - let mut bundle = IssueBundle::new(ik); - - bundle - .add_recipient( - String::from("Asset description"), + let (bundle, _) = IssueBundle::new( + ik, + String::from("Asset description"), + Some(IssueInfo { recipient, - NoteValue::from_raw(5), - false, - rng, - ) - .unwrap(); + value: NoteValue::from_raw(5), + }), + rng, + ) + .unwrap(); let mut signed = bundle.prepare(sighash).sign(rng, &isk).unwrap(); let prev_finalized = HashSet::new(); @@ -1274,7 +1255,6 @@ mod tests { signed .actions .first_mut() - .unwrap() .modify_descr(String::from_utf8(vec![b'X'; 513]).unwrap()); assert_eq!( @@ -1283,11 +1263,7 @@ mod tests { ); // 2. Try empty description - signed - .actions - .first_mut() - .unwrap() - .modify_descr("".to_string()); + signed.actions.first_mut().modify_descr("".to_string()); assert_eq!( verify_issue_bundle(&signed, sighash, &prev_finalized).unwrap_err(), @@ -1301,14 +1277,17 @@ mod tests { let (_, _, note) = Note::dummy(&mut rng, None, AssetBase::native()); let action = - IssueAction::new_with_finalize(String::from("Asset description"), ¬e, 0u8).unwrap(); + IssueAction::new_with_flags(String::from("Asset description"), vec![note], 0u8) + .unwrap(); assert_eq!(action.flags(), 0b0000_0000); let action = - IssueAction::new_with_finalize(String::from("Asset description"), ¬e, 1u8).unwrap(); + IssueAction::new_with_flags(String::from("Asset description"), vec![note], 1u8) + .unwrap(); assert_eq!(action.flags(), 0b0000_0001); - let action = IssueAction::new_with_finalize(String::from("Asset description"), ¬e, 2u8); + let action = + IssueAction::new_with_flags(String::from("Asset description"), vec![note], 2u8); assert!(action.is_none()); } } @@ -1321,6 +1300,7 @@ pub mod testing { use crate::keys::testing::{arb_issuance_authorizing_key, arb_issuance_validating_key}; use crate::note::asset_base::testing::zsa_asset_id; use crate::note::testing::arb_zsa_note; + use nonempty::NonEmpty; use proptest::collection::vec; use proptest::prelude::*; use proptest::prop_compose; @@ -1335,7 +1315,11 @@ pub mod testing { ( note in arb_zsa_note(asset), )-> IssueAction { - IssueAction::new(asset_desc.clone(), ¬e) + IssueAction{ + asset_desc: asset_desc.clone(), + notes: vec![note], + finalize: false, + } } } @@ -1346,6 +1330,7 @@ pub mod testing { actions in vec(arb_issue_action("asset_desc".to_string()), n_actions), ik in arb_issuance_validating_key() ) -> IssueBundle { + let actions = NonEmpty::from_vec(actions).unwrap(); IssueBundle { ik, actions, @@ -1363,6 +1348,7 @@ pub mod testing { ik in arb_issuance_validating_key(), fake_sighash in prop::array::uniform32(prop::num::u8::ANY) ) -> IssueBundle { + let actions = NonEmpty::from_vec(actions).unwrap(); IssueBundle { ik, actions, @@ -1383,7 +1369,7 @@ pub mod testing { fake_sighash in prop::array::uniform32(prop::num::u8::ANY) ) -> IssueBundle { let rng = StdRng::from_seed(rng_seed); - + let actions = NonEmpty::from_vec(actions).unwrap(); IssueBundle { ik, actions, diff --git a/tests/zsa.rs b/tests/zsa.rs index 17c0a4b60..af30ba5f1 100644 --- a/tests/zsa.rs +++ b/tests/zsa.rs @@ -4,7 +4,7 @@ use crate::builder::verify_bundle; use bridgetree::BridgeTree; use incrementalmerkletree::Hashable; use orchard::bundle::Authorized; -use orchard::issuance::{verify_issue_bundle, IssueBundle, Signed, Unauthorized}; +use orchard::issuance::{verify_issue_bundle, IssueBundle, IssueInfo, Signed, Unauthorized}; use orchard::keys::{IssuanceAuthorizingKey, IssuanceValidatingKey}; use orchard::note::{AssetBase, ExtractedNoteCommitment}; use orchard::note_encryption_v3::OrchardDomainV3; @@ -138,23 +138,25 @@ pub fn build_merkle_path_with_two_leaves( fn issue_zsa_notes(asset_descr: &str, keys: &Keychain) -> (Note, Note) { let mut rng = OsRng; // Create a issuance bundle - let mut unauthorized = IssueBundle::new(keys.ik().clone()); + let unauthorized_asset = IssueBundle::new( + keys.ik().clone(), + asset_descr.to_string(), + Some(IssueInfo { + recipient: keys.recipient, + value: NoteValue::from_raw(40), + }), + &mut rng, + ); + + assert!(unauthorized_asset.is_ok()); + + let (mut unauthorized, _) = unauthorized_asset.unwrap(); - assert!(unauthorized - .add_recipient( - asset_descr.to_string(), - keys.recipient, - NoteValue::from_raw(40), - false, - &mut rng, - ) - .is_ok()); assert!(unauthorized .add_recipient( asset_descr.to_string(), keys.recipient, NoteValue::from_raw(2), - false, &mut rng, ) .is_ok());