Skip to content

Commit

Permalink
Rename Builder::add_recipient to add_output.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
nuttycom and daira committed Dec 12, 2023
1 parent e049bcc commit 0f790ca
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 40 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
76 changes: 38 additions & 38 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -197,24 +197,24 @@ 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
fn dummy(rng: &mut impl RngCore) -> Self {
let fvk: FullViewingKey = (&SpendingKey::random(rng)).into();
let recipient = fvk.address_at(0u32, Scope::External);

RecipientInfo {
OutputInfo {
ovk: None,
recipient,
value: NoteValue::zero(),
Expand All @@ -227,12 +227,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,
Expand Down Expand Up @@ -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<SpendInfo>,
recipients: Vec<RecipientInfo>,
outputs: Vec<OutputInfo>,
flags: Flags,
anchor: Anchor,
padding_rule: PaddingRule,
Expand All @@ -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,
Expand Down Expand Up @@ -365,7 +365,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,
Expand All @@ -376,7 +376,7 @@ impl Builder {
return Err(OutputError);
}

self.recipients.push(RecipientInfo {
self.outputs.push(OutputInfo {
ovk,
recipient,
value,
Expand All @@ -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<impl OutputView> {
&self.recipients
&self.outputs
}

/// The net value of the bundle to be built. The value of all spends,
Expand All @@ -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.
Expand All @@ -432,32 +432,32 @@ impl Builder {
mut rng: impl RngCore,
) -> Result<Bundle<InProgress<Unproven, Unauthorized>, 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(
iter::repeat_with(|| RecipientInfo::dummy(&mut rng))
.take(num_actions - num_real_recipients),
self.outputs.extend(
iter::repeat_with(|| OutputInfo::dummy(&mut rng))
.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()
};

Expand Down Expand Up @@ -790,7 +790,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())
}
Expand Down Expand Up @@ -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<R: RngCore + CryptoRng> ArbitraryBundleInputs<R> {
Expand All @@ -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();
}

Expand All @@ -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,

)
(
Expand All @@ -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<StdRng> {
Expand All @@ -914,7 +914,7 @@ pub mod testing {
sk,
anchor: frontier.root().into(),
notes: notes_and_auth_paths,
recipient_amounts
output_amounts
}
}
}
Expand Down Expand Up @@ -965,7 +965,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);
Expand Down
4 changes: 2 additions & 2 deletions tests/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 0f790ca

Please sign in to comment.