diff --git a/CHANGELOG.md b/CHANGELOG.md index df8fae4bd..af6986296 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ and this project adheres to Rust's notion of - Migrated to `incrementalmerkletree 0.5`. - `orchard::builder::Builder::new` now takes an additional `PaddingRule` argument that specifies how actions should be padded, instead of using hardcoded padding. +- `orchard::builder::Builder::add_recipient` has been renamed to `add_output` + in order to clarify than more than one output of a given transaction may be + sent to the same recipient. ## [0.5.0] - 2023-06-06 ### Changed diff --git a/src/builder.rs b/src/builder.rs index f182414eb..2132d8cc2 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -49,9 +49,9 @@ impl PaddingRule { /// Returns the number of logical actions that the builder will add to the bundle /// as a consequence of this padding rule, given the specified number of spends - /// and recipients. - pub fn num_actions(&self, num_spends: usize, num_recipients: usize) -> usize { - let num_real_actions = core::cmp::max(num_spends, num_recipients); + /// and outputs. + pub fn num_actions(&self, num_spends: usize, num_outputs: usize) -> usize { + let num_real_actions = core::cmp::max(num_spends, num_outputs); match self { PaddingRule::Require(n) => core::cmp::max(num_real_actions, *n), @@ -197,11 +197,11 @@ impl SpendInfo { } } -/// Information about a specific recipient to receive funds in an [`Action`]. +/// Information about a specific output to receive funds in an [`Action`]. #[derive(Debug)] struct RecipientInfo { ovk: Option, - recipient: Address, + output: Address, value: NoteValue, memo: Option<[u8; 512]>, } @@ -212,11 +212,11 @@ impl RecipientInfo { /// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes fn dummy(rng: &mut impl RngCore) -> Self { let fvk: FullViewingKey = (&SpendingKey::random(rng)).into(); - let recipient = fvk.address_at(0u32, Scope::External); + let output = fvk.address_at(0u32, Scope::External); RecipientInfo { ovk: None, - recipient, + output, value: NoteValue::zero(), memo: None, } @@ -259,7 +259,7 @@ impl ActionInfo { let alpha = pallas::Scalar::random(&mut rng); let rk = ak.randomize(&alpha); - let note = Note::new(self.output.recipient, self.output.value, nf_old, &mut rng); + let note = Note::new(self.output.output, self.output.value, nf_old, &mut rng); let cm_new = note.commitment(); let cmx = cm_new.into(); @@ -296,12 +296,12 @@ impl ActionInfo { } } -/// A builder that constructs a [`Bundle`] from a set of notes to be spent, and recipients +/// A builder that constructs a [`Bundle`] from a set of notes to be spent, and outputs /// to receive funds. #[derive(Debug)] pub struct Builder { spends: Vec, - recipients: Vec, + outputs: Vec, flags: Flags, anchor: Anchor, padding_rule: PaddingRule, @@ -312,7 +312,7 @@ impl Builder { pub fn new(flags: Flags, anchor: Anchor, padding_rule: PaddingRule) -> Self { Builder { spends: vec![], - recipients: vec![], + outputs: vec![], flags, anchor, padding_rule, @@ -365,10 +365,10 @@ impl Builder { } /// Adds an address which will receive funds in this transaction. - pub fn add_recipient( + pub fn add_output( &mut self, ovk: Option, - recipient: Address, + output: Address, value: NoteValue, memo: Option<[u8; 512]>, ) -> Result<(), OutputError> { @@ -376,9 +376,9 @@ impl Builder { return Err(OutputError); } - self.recipients.push(RecipientInfo { + self.outputs.push(RecipientInfo { ovk, - recipient, + output, value, memo, }); @@ -395,7 +395,7 @@ impl Builder { /// Returns the action output components that will be produced by the /// transaction being constructed pub fn outputs(&self) -> &Vec { - &self.recipients + &self.outputs } /// The net value of the bundle to be built. The value of all spends, @@ -414,16 +414,16 @@ impl Builder { .iter() .map(|spend| spend.note.value() - NoteValue::zero()) .chain( - self.recipients + self.outputs .iter() - .map(|recipient| NoteValue::zero() - recipient.value), + .map(|output| NoteValue::zero() - output.value), ) .fold(Some(ValueSum::zero()), |acc, note_value| acc? + note_value) .ok_or(OverflowError)?; i64::try_from(value_balance).and_then(|i| V::try_from(i).map_err(|_| value::OverflowError)) } - /// Builds a bundle containing the given spent notes and recipients. + /// Builds a bundle containing the given spent notes and outputs. /// /// The returned bundle will have no proof or signatures; these can be applied with /// [`Bundle::create_proof`] and [`Bundle::apply_signatures`] respectively. @@ -432,32 +432,32 @@ impl Builder { mut rng: impl RngCore, ) -> Result, V>, BuildError> { let num_real_spends = self.spends.len(); - let num_real_recipients = self.recipients.len(); + let num_real_outputs = self.outputs.len(); let num_actions = self .padding_rule - .num_actions(num_real_spends, num_real_recipients); + .num_actions(num_real_spends, num_real_outputs); - // Pair up the spends and recipients, extending with dummy values as necessary. + // Pair up the spends and outputs, extending with dummy values as necessary. let pre_actions: Vec<_> = { self.spends.extend( iter::repeat_with(|| SpendInfo::dummy(&mut rng)) .take(num_actions - num_real_spends), ); - self.recipients.extend( + self.outputs.extend( iter::repeat_with(|| RecipientInfo::dummy(&mut rng)) - .take(num_actions - num_real_recipients), + .take(num_actions - num_real_outputs), ); - // Shuffle the spends and recipients, so that learning the position of a + // Shuffle the spends and outputs, so that learning the position of a // specific spent note or output note doesn't reveal anything on its own about // the meaning of that note in the transaction context. self.spends.shuffle(&mut rng); - self.recipients.shuffle(&mut rng); + self.outputs.shuffle(&mut rng); self.spends .into_iter() - .zip(self.recipients.into_iter()) - .map(|(spend, recipient)| ActionInfo::new(spend, recipient, &mut rng)) + .zip(self.outputs.into_iter()) + .map(|(spend, output)| ActionInfo::new(spend, output, &mut rng)) .collect() }; @@ -834,7 +834,7 @@ pub mod testing { sk: SpendingKey, anchor: Anchor, notes: Vec<(Note, MerklePath)>, - recipient_amounts: Vec<(Address, NoteValue)>, + output_amounts: Vec<(Address, NoteValue)>, } impl ArbitraryBundleInputs { @@ -848,12 +848,12 @@ pub mod testing { builder.add_spend(fvk.clone(), note, path).unwrap(); } - for (addr, value) in self.recipient_amounts.into_iter() { + for (addr, value) in self.output_amounts.into_iter() { let scope = fvk.scope_for_address(&addr).unwrap(); let ovk = fvk.to_ovk(scope); builder - .add_recipient(Some(ovk.clone()), addr, value, None) + .add_output(Some(ovk.clone()), addr, value, None) .unwrap(); } @@ -875,7 +875,7 @@ pub mod testing { fn arb_bundle_inputs(sk: SpendingKey) ( n_notes in 1usize..30, - n_recipients in 1..30, + n_outputs in 1..30, ) ( @@ -884,12 +884,12 @@ pub mod testing { arb_positive_note_value(MAX_NOTE_VALUE / n_notes as u64).prop_flat_map(arb_note), n_notes ), - recipient_amounts in vec( + output_amounts in vec( arb_address().prop_flat_map(move |a| { - arb_positive_note_value(MAX_NOTE_VALUE / n_recipients as u64) + arb_positive_note_value(MAX_NOTE_VALUE / n_outputs as u64) .prop_map(move |v| (a, v)) }), - n_recipients as usize + n_outputs as usize ), rng_seed in prop::array::uniform32(prop::num::u8::ANY) ) -> ArbitraryBundleInputs { @@ -914,7 +914,7 @@ pub mod testing { sk, anchor: frontier.root().into(), notes: notes_and_auth_paths, - recipient_amounts + output_amounts } } } @@ -956,7 +956,7 @@ mod tests { let sk = SpendingKey::random(&mut rng); let fvk = FullViewingKey::from(&sk); - let recipient = fvk.address_at(0u32, Scope::External); + let output = fvk.address_at(0u32, Scope::External); let mut builder = Builder::new( Flags::from_parts(true, true), @@ -965,7 +965,7 @@ mod tests { ); builder - .add_recipient(None, recipient, NoteValue::from_raw(5000), None) + .add_output(None, output, NoteValue::from_raw(5000), None) .unwrap(); let balance: i64 = builder.value_balance().unwrap(); assert_eq!(balance, -5000); diff --git a/tests/builder.rs b/tests/builder.rs index 956c3321d..d33e2d9a9 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -45,7 +45,7 @@ fn bundle_chain() { let mut builder = Builder::new(Flags::from_parts(false, true), anchor, PaddingRule::DEFAULT); assert_eq!( - builder.add_recipient(None, recipient, NoteValue::from_raw(5000), None), + builder.add_output(None, recipient, NoteValue::from_raw(5000), None), Ok(()) ); let unauthorized = builder.build(&mut rng).unwrap(); @@ -87,7 +87,7 @@ fn bundle_chain() { let mut builder = Builder::new(Flags::from_parts(true, true), anchor, PaddingRule::DEFAULT); assert_eq!(builder.add_spend(fvk, note, merkle_path), Ok(())); assert_eq!( - builder.add_recipient(None, recipient, NoteValue::from_raw(5000), None), + builder.add_output(None, recipient, NoteValue::from_raw(5000), None), Ok(()) ); let unauthorized = builder.build(&mut rng).unwrap();