Rename `Builder::add_recipient` to `add_output`.

The term `recipient` is commonly used to refer to the address to which a
note is sent; however, a bundle may include multiple outputs to the same
recipient. This change is intended to clarify this usage.

Co-authored-by: Daira Emma Hopwood <daira@jacaranda.org>
This commit is contained in:
Kris Nuttycombe 2023-12-12 11:12:10 -07:00
parent 003619bad4
commit 06cb76168e
6 changed files with 48 additions and 44 deletions

View File

@ -7,6 +7,11 @@ and this project adheres to Rust's notion of
## [Unreleased] ## [Unreleased]
### Changed
- `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.6.0] - 2023-09-08 ## [0.6.0] - 2023-09-08
### Changed ### Changed
- MSRV is now 1.65.0. - MSRV is now 1.65.0.

View File

@ -32,7 +32,7 @@ fn criterion_benchmark(c: &mut Criterion) {
); );
for _ in 0..num_recipients { for _ in 0..num_recipients {
builder builder
.add_recipient(None, recipient, NoteValue::from_raw(10), None) .add_output(None, recipient, NoteValue::from_raw(10), None)
.unwrap(); .unwrap();
} }
let bundle: Bundle<_, i64> = builder.build(rng).unwrap(); let bundle: Bundle<_, i64> = builder.build(rng).unwrap();

View File

@ -52,10 +52,10 @@ fn bench_note_decryption(c: &mut Criterion) {
// The builder pads to two actions, and shuffles their order. Add two recipients // The builder pads to two actions, and shuffles their order. Add two recipients
// so the first action is always decryptable. // so the first action is always decryptable.
builder builder
.add_recipient(None, recipient, NoteValue::from_raw(10), None) .add_output(None, recipient, NoteValue::from_raw(10), None)
.unwrap(); .unwrap();
builder builder
.add_recipient(None, recipient, NoteValue::from_raw(10), None) .add_output(None, recipient, NoteValue::from_raw(10), None)
.unwrap(); .unwrap();
let bundle: Bundle<_, i64> = builder.build(rng).unwrap(); let bundle: Bundle<_, i64> = builder.build(rng).unwrap();
bundle bundle

View File

@ -157,16 +157,16 @@ 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)] #[derive(Debug)]
struct RecipientInfo { struct OutputInfo {
ovk: Option<OutgoingViewingKey>, ovk: Option<OutgoingViewingKey>,
recipient: Address, recipient: Address,
value: NoteValue, value: NoteValue,
memo: Option<[u8; 512]>, memo: Option<[u8; 512]>,
} }
impl RecipientInfo { impl OutputInfo {
/// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes]. /// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes].
/// ///
/// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes /// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes
@ -174,7 +174,7 @@ impl RecipientInfo {
let fvk: FullViewingKey = (&SpendingKey::random(rng)).into(); let fvk: FullViewingKey = (&SpendingKey::random(rng)).into();
let recipient = fvk.address_at(0u32, Scope::External); let recipient = fvk.address_at(0u32, Scope::External);
RecipientInfo { OutputInfo {
ovk: None, ovk: None,
recipient, recipient,
value: NoteValue::zero(), value: NoteValue::zero(),
@ -187,12 +187,12 @@ impl RecipientInfo {
#[derive(Debug)] #[derive(Debug)]
struct ActionInfo { struct ActionInfo {
spend: SpendInfo, spend: SpendInfo,
output: RecipientInfo, output: OutputInfo,
rcv: ValueCommitTrapdoor, rcv: ValueCommitTrapdoor,
} }
impl ActionInfo { impl ActionInfo {
fn new(spend: SpendInfo, output: RecipientInfo, rng: impl RngCore) -> Self { fn new(spend: SpendInfo, output: OutputInfo, rng: impl RngCore) -> Self {
ActionInfo { ActionInfo {
spend, spend,
output, output,
@ -256,12 +256,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. /// to receive funds.
#[derive(Debug)] #[derive(Debug)]
pub struct Builder { pub struct Builder {
spends: Vec<SpendInfo>, spends: Vec<SpendInfo>,
recipients: Vec<RecipientInfo>, outputs: Vec<OutputInfo>,
flags: Flags, flags: Flags,
anchor: Anchor, anchor: Anchor,
} }
@ -271,7 +271,7 @@ impl Builder {
pub fn new(flags: Flags, anchor: Anchor) -> Self { pub fn new(flags: Flags, anchor: Anchor) -> Self {
Builder { Builder {
spends: vec![], spends: vec![],
recipients: vec![], outputs: vec![],
flags, flags,
anchor, anchor,
} }
@ -323,7 +323,7 @@ impl Builder {
} }
/// Adds an address which will receive funds in this transaction. /// Adds an address which will receive funds in this transaction.
pub fn add_recipient( pub fn add_output(
&mut self, &mut self,
ovk: Option<OutgoingViewingKey>, ovk: Option<OutgoingViewingKey>,
recipient: Address, recipient: Address,
@ -334,7 +334,7 @@ impl Builder {
return Err(OutputError); return Err(OutputError);
} }
self.recipients.push(RecipientInfo { self.outputs.push(OutputInfo {
ovk, ovk,
recipient, recipient,
value, value,
@ -353,7 +353,7 @@ impl Builder {
/// Returns the action output components that will be produced by the /// Returns the action output components that will be produced by the
/// transaction being constructed /// transaction being constructed
pub fn outputs(&self) -> &Vec<impl OutputView> { pub fn outputs(&self) -> &Vec<impl OutputView> {
&self.recipients &self.outputs
} }
/// The net value of the bundle to be built. The value of all spends, /// The net value of the bundle to be built. The value of all spends,
@ -372,16 +372,16 @@ impl Builder {
.iter() .iter()
.map(|spend| spend.note.value() - NoteValue::zero()) .map(|spend| spend.note.value() - NoteValue::zero())
.chain( .chain(
self.recipients self.outputs
.iter() .iter()
.map(|recipient| NoteValue::zero() - recipient.value), .map(|output| NoteValue::zero() - output.value),
) )
.fold(Some(ValueSum::zero()), |acc, note_value| acc? + note_value) .fold(Some(ValueSum::zero()), |acc, note_value| acc? + note_value)
.ok_or(OverflowError)?; .ok_or(OverflowError)?;
i64::try_from(value_balance).and_then(|i| V::try_from(i).map_err(|_| value::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 /// The returned bundle will have no proof or signatures; these can be applied with
/// [`Bundle::create_proof`] and [`Bundle::apply_signatures`] respectively. /// [`Bundle::create_proof`] and [`Bundle::apply_signatures`] respectively.
@ -389,11 +389,11 @@ impl Builder {
mut self, mut self,
mut rng: impl RngCore, mut rng: impl RngCore,
) -> Result<Bundle<InProgress<Unproven, Unauthorized>, V>, BuildError> { ) -> Result<Bundle<InProgress<Unproven, Unauthorized>, V>, BuildError> {
// 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<_> = { let pre_actions: Vec<_> = {
let num_spends = self.spends.len(); let num_spends = self.spends.len();
let num_recipients = self.recipients.len(); let num_outputs = self.outputs.len();
let num_actions = [num_spends, num_recipients, MIN_ACTIONS] let num_actions = [num_spends, num_outputs, MIN_ACTIONS]
.iter() .iter()
.max() .max()
.cloned() .cloned()
@ -402,21 +402,20 @@ impl Builder {
self.spends.extend( self.spends.extend(
iter::repeat_with(|| SpendInfo::dummy(&mut rng)).take(num_actions - num_spends), iter::repeat_with(|| SpendInfo::dummy(&mut rng)).take(num_actions - num_spends),
); );
self.recipients.extend( self.outputs.extend(
iter::repeat_with(|| RecipientInfo::dummy(&mut rng)) iter::repeat_with(|| OutputInfo::dummy(&mut rng)).take(num_actions - num_outputs),
.take(num_actions - num_recipients),
); );
// 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 // specific spent note or output note doesn't reveal anything on its own about
// the meaning of that note in the transaction context. // the meaning of that note in the transaction context.
self.spends.shuffle(&mut rng); self.spends.shuffle(&mut rng);
self.recipients.shuffle(&mut rng); self.outputs.shuffle(&mut rng);
self.spends self.spends
.into_iter() .into_iter()
.zip(self.recipients.into_iter()) .zip(self.outputs.into_iter())
.map(|(spend, recipient)| ActionInfo::new(spend, recipient, &mut rng)) .map(|(spend, output)| ActionInfo::new(spend, output, &mut rng))
.collect() .collect()
}; };
@ -749,7 +748,7 @@ pub trait OutputView {
fn value<V: From<u64>>(&self) -> V; fn value<V: From<u64>>(&self) -> V;
} }
impl OutputView for RecipientInfo { impl OutputView for OutputInfo {
fn value<V: From<u64>>(&self) -> V { fn value<V: From<u64>>(&self) -> V {
V::from(self.value.inner()) V::from(self.value.inner())
} }
@ -793,7 +792,7 @@ pub mod testing {
sk: SpendingKey, sk: SpendingKey,
anchor: Anchor, anchor: Anchor,
notes: Vec<(Note, MerklePath)>, notes: Vec<(Note, MerklePath)>,
recipient_amounts: Vec<(Address, NoteValue)>, output_amounts: Vec<(Address, NoteValue)>,
} }
impl<R: RngCore + CryptoRng> ArbitraryBundleInputs<R> { impl<R: RngCore + CryptoRng> ArbitraryBundleInputs<R> {
@ -807,12 +806,12 @@ pub mod testing {
builder.add_spend(fvk.clone(), note, path).unwrap(); 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 scope = fvk.scope_for_address(&addr).unwrap();
let ovk = fvk.to_ovk(scope); let ovk = fvk.to_ovk(scope);
builder builder
.add_recipient(Some(ovk.clone()), addr, value, None) .add_output(Some(ovk.clone()), addr, value, None)
.unwrap(); .unwrap();
} }
@ -834,7 +833,7 @@ pub mod testing {
fn arb_bundle_inputs(sk: SpendingKey) fn arb_bundle_inputs(sk: SpendingKey)
( (
n_notes in 1usize..30, n_notes in 1usize..30,
n_recipients in 1..30, n_outputs in 1..30,
) )
( (
@ -843,12 +842,12 @@ pub mod testing {
arb_positive_note_value(MAX_NOTE_VALUE / n_notes as u64).prop_flat_map(arb_note), arb_positive_note_value(MAX_NOTE_VALUE / n_notes as u64).prop_flat_map(arb_note),
n_notes n_notes
), ),
recipient_amounts in vec( output_amounts in vec(
arb_address().prop_flat_map(move |a| { 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)) .prop_map(move |v| (a, v))
}), }),
n_recipients as usize n_outputs as usize
), ),
rng_seed in prop::array::uniform32(prop::num::u8::ANY) rng_seed in prop::array::uniform32(prop::num::u8::ANY)
) -> ArbitraryBundleInputs<StdRng> { ) -> ArbitraryBundleInputs<StdRng> {
@ -873,7 +872,7 @@ pub mod testing {
sk, sk,
anchor: frontier.root().into(), anchor: frontier.root().into(),
notes: notes_and_auth_paths, notes: notes_and_auth_paths,
recipient_amounts output_amounts
} }
} }
} }
@ -922,7 +921,7 @@ mod tests {
); );
builder builder
.add_recipient(None, recipient, NoteValue::from_raw(5000), None) .add_output(None, recipient, NoteValue::from_raw(5000), None)
.unwrap(); .unwrap();
let balance: i64 = builder.value_balance().unwrap(); let balance: i64 = builder.value_balance().unwrap();
assert_eq!(balance, -5000); assert_eq!(balance, -5000);

View File

@ -16,7 +16,7 @@
//! - Define your `valueBalanceOrchard` type to enforce your valid value range. This can //! - Define your `valueBalanceOrchard` type to enforce your valid value range. This can
//! be checked in its `TryFrom<i64>` implementation. //! be checked in its `TryFrom<i64>` implementation.
//! - Define your own "amount" type for note values, and convert it to `NoteValue` prior //! - Define your own "amount" type for note values, and convert it to `NoteValue` prior
//! to calling [`Builder::add_recipient`]. //! to calling [`Builder::add_output`].
//! //!
//! Inside the circuit, note values are constrained to be unsigned 64-bit integers. //! Inside the circuit, note values are constrained to be unsigned 64-bit integers.
//! //!
@ -34,7 +34,7 @@
//! [`Bundle`]: crate::bundle::Bundle //! [`Bundle`]: crate::bundle::Bundle
//! [`Bundle::value_balance`]: crate::bundle::Bundle::value_balance //! [`Bundle::value_balance`]: crate::bundle::Bundle::value_balance
//! [`Builder::value_balance`]: crate::builder::Builder::value_balance //! [`Builder::value_balance`]: crate::builder::Builder::value_balance
//! [`Builder::add_recipient`]: crate::builder::Builder::add_recipient //! [`Builder::add_output`]: crate::builder::Builder::add_output
//! [Rust documentation]: https://doc.rust-lang.org/stable/std/primitive.i64.html //! [Rust documentation]: https://doc.rust-lang.org/stable/std/primitive.i64.html
use core::fmt::{self, Debug}; use core::fmt::{self, Debug};

View File

@ -44,7 +44,7 @@ fn bundle_chain() {
let mut builder = Builder::new(Flags::from_parts(false, true), anchor); let mut builder = Builder::new(Flags::from_parts(false, true), anchor);
assert_eq!( assert_eq!(
builder.add_recipient(None, recipient, NoteValue::from_raw(5000), None), builder.add_output(None, recipient, NoteValue::from_raw(5000), None),
Ok(()) Ok(())
); );
let unauthorized = builder.build(&mut rng).unwrap(); let unauthorized = builder.build(&mut rng).unwrap();
@ -86,7 +86,7 @@ fn bundle_chain() {
let mut builder = Builder::new(Flags::from_parts(true, true), anchor); let mut builder = Builder::new(Flags::from_parts(true, true), anchor);
assert_eq!(builder.add_spend(fvk, note, merkle_path), Ok(())); assert_eq!(builder.add_spend(fvk, note, merkle_path), Ok(()));
assert_eq!( assert_eq!(
builder.add_recipient(None, recipient, NoteValue::from_raw(5000), None), builder.add_output(None, recipient, NoteValue::from_raw(5000), None),
Ok(()) Ok(())
); );
let unauthorized = builder.build(&mut rng).unwrap(); let unauthorized = builder.build(&mut rng).unwrap();