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]
### 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
### Changed
- MSRV is now 1.65.0.
@ -22,8 +27,8 @@ and this project adheres to Rust's notion of
- `orchard::builder`:
- `{SpendInfo::new, InputView, OutputView}`
- `Builder::{spends, outputs}`
- `SpendError`
- `OutputError`
- `SpendError`
- `OutputError`
### Changed
- MSRV is now 1.60.0.

View File

@ -32,7 +32,7 @@ fn criterion_benchmark(c: &mut Criterion) {
);
for _ in 0..num_recipients {
builder
.add_recipient(None, recipient, NoteValue::from_raw(10), None)
.add_output(None, recipient, NoteValue::from_raw(10), None)
.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
// so the first action is always decryptable.
builder
.add_recipient(None, recipient, NoteValue::from_raw(10), None)
.add_output(None, recipient, NoteValue::from_raw(10), None)
.unwrap();
builder
.add_recipient(None, recipient, NoteValue::from_raw(10), None)
.add_output(None, recipient, NoteValue::from_raw(10), None)
.unwrap();
let bundle: Bundle<_, i64> = builder.build(rng).unwrap();
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)]
struct RecipientInfo {
struct OutputInfo {
ovk: Option<OutgoingViewingKey>,
recipient: Address,
value: NoteValue,
memo: Option<[u8; 512]>,
}
impl RecipientInfo {
impl OutputInfo {
/// Defined in [Zcash Protocol Spec § 4.8.3: Dummy Notes (Orchard)][orcharddummynotes].
///
/// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes
@ -174,7 +174,7 @@ impl RecipientInfo {
let fvk: FullViewingKey = (&SpendingKey::random(rng)).into();
let recipient = fvk.address_at(0u32, Scope::External);
RecipientInfo {
OutputInfo {
ovk: None,
recipient,
value: NoteValue::zero(),
@ -187,12 +187,12 @@ impl RecipientInfo {
#[derive(Debug)]
struct ActionInfo {
spend: SpendInfo,
output: RecipientInfo,
output: OutputInfo,
rcv: ValueCommitTrapdoor,
}
impl ActionInfo {
fn new(spend: SpendInfo, output: RecipientInfo, rng: impl RngCore) -> Self {
fn new(spend: SpendInfo, output: OutputInfo, rng: impl RngCore) -> Self {
ActionInfo {
spend,
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.
#[derive(Debug)]
pub struct Builder {
spends: Vec<SpendInfo>,
recipients: Vec<RecipientInfo>,
outputs: Vec<OutputInfo>,
flags: Flags,
anchor: Anchor,
}
@ -271,7 +271,7 @@ impl Builder {
pub fn new(flags: Flags, anchor: Anchor) -> Self {
Builder {
spends: vec![],
recipients: vec![],
outputs: vec![],
flags,
anchor,
}
@ -323,7 +323,7 @@ impl Builder {
}
/// Adds an address which will receive funds in this transaction.
pub fn add_recipient(
pub fn add_output(
&mut self,
ovk: Option<OutgoingViewingKey>,
recipient: Address,
@ -334,7 +334,7 @@ impl Builder {
return Err(OutputError);
}
self.recipients.push(RecipientInfo {
self.outputs.push(OutputInfo {
ovk,
recipient,
value,
@ -353,7 +353,7 @@ impl Builder {
/// Returns the action output components that will be produced by the
/// transaction being constructed
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,
@ -372,16 +372,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.
@ -389,11 +389,11 @@ impl Builder {
mut self,
mut rng: impl RngCore,
) -> 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 num_spends = self.spends.len();
let num_recipients = self.recipients.len();
let num_actions = [num_spends, num_recipients, MIN_ACTIONS]
let num_outputs = self.outputs.len();
let num_actions = [num_spends, num_outputs, MIN_ACTIONS]
.iter()
.max()
.cloned()
@ -402,21 +402,20 @@ impl Builder {
self.spends.extend(
iter::repeat_with(|| SpendInfo::dummy(&mut rng)).take(num_actions - num_spends),
);
self.recipients.extend(
iter::repeat_with(|| RecipientInfo::dummy(&mut rng))
.take(num_actions - num_recipients),
self.outputs.extend(
iter::repeat_with(|| OutputInfo::dummy(&mut rng)).take(num_actions - num_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()
};
@ -749,7 +748,7 @@ pub trait OutputView {
fn value<V: From<u64>>(&self) -> V;
}
impl OutputView for RecipientInfo {
impl OutputView for OutputInfo {
fn value<V: From<u64>>(&self) -> V {
V::from(self.value.inner())
}
@ -793,7 +792,7 @@ pub mod testing {
sk: SpendingKey,
anchor: Anchor,
notes: Vec<(Note, MerklePath)>,
recipient_amounts: Vec<(Address, NoteValue)>,
output_amounts: Vec<(Address, NoteValue)>,
}
impl<R: RngCore + CryptoRng> ArbitraryBundleInputs<R> {
@ -807,12 +806,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();
}
@ -834,7 +833,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,
)
(
@ -843,12 +842,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<StdRng> {
@ -873,7 +872,7 @@ pub mod testing {
sk,
anchor: frontier.root().into(),
notes: notes_and_auth_paths,
recipient_amounts
output_amounts
}
}
}
@ -922,7 +921,7 @@ mod tests {
);
builder
.add_recipient(None, recipient, NoteValue::from_raw(5000), None)
.add_output(None, recipient, NoteValue::from_raw(5000), None)
.unwrap();
let balance: i64 = builder.value_balance().unwrap();
assert_eq!(balance, -5000);

View File

@ -16,7 +16,7 @@
//! - Define your `valueBalanceOrchard` type to enforce your valid value range. This can
//! be checked in its `TryFrom<i64>` implementation.
//! - 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.
//!
@ -34,7 +34,7 @@
//! [`Bundle`]: crate::bundle::Bundle
//! [`Bundle::value_balance`]: crate::bundle::Bundle::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
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);
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();
@ -86,7 +86,7 @@ fn bundle_chain() {
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_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();