Skip to content

Commit

Permalink
Add a dummy spend to the bundle if the bundle is required to be present.
Browse files Browse the repository at this point in the history
  • Loading branch information
nuttycom committed Dec 21, 2023
1 parent 8dfd617 commit 0998e89
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 14 deletions.
12 changes: 7 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ The entries below are relative to the `zcash_primitives::sapling` module as of
- `OutputInfo`
- `ProverProgress`
- `BundleType`
- `SigningMetadata`
- `bundle` bundle builder function.
- `sapling_crypto::bundle` module:
- The following types moved from
`zcash_primitives::transaction::components::sapling`:
- `Bundle`
- `SpendDescription, SpendDescriptionV5`
- `OutputDescription, OutputDescriptionV5`
- `Authorization, Authorized, MapAuth`
- `Authorization, Authorized`
- `GrothProofBytes`
- `Bundle::<InProgress<Unproven, _>>::create_proofs`
- `Bundle::<InProgress<_, Unsigned>>::prepare`
Expand All @@ -42,9 +43,6 @@ The entries below are relative to the `zcash_primitives::sapling` module as of
- `Bundle::<InProgress<Proven, Unsigned>>::apply_signatures`
- `Bundle::try_map_authorization`
- `TryMapAuth`
- `impl {MapAuth, TryMapAuth} for (FnMut, FnMut, FnMut, FnMut)`
helpers to enable calling `Bundle::{map_authorization, try_map_authorization}`
with a set of closures.
- `testing` module, containing the following functions moved from
`zcash_primitives::transaction::components::sapling::testing`:
- `arb_output_description`
Expand Down Expand Up @@ -93,6 +91,7 @@ The entries below are relative to the `zcash_primitives::sapling` module as of
argument as a `NoteValue` instead of as a bare `u64`.
- `sapling_crypto::builder`:
- `SaplingBuilder` has been renamed to `Builder`
- `MaybeSigned::SigningMetadata` has been renamed to `MaybeSigned::SigningParts`
- `Builder` no longer has a `P: zcash_primitives::consensus::Parameters`
type parameter.
- `Builder::new` now takes a `Zip212Enforcement` argument instead of a
Expand All @@ -118,6 +117,9 @@ The entries below are relative to the `zcash_primitives::sapling` module as of
- `Bundle` now has a second generic parameter `V`.
- `Bundle::value_balance` now returns `&V` instead of
`&zcash_primitives::transaction::components::Amount`.
- `Bundle::map_authorization` now takes a context argument and explicit
functions for each mappable field, rather than a `MapAuth` value, in
order to simplify handling of context values.
- `Authorized::binding_sig` now has type `redjubjub::Signature<Binding>`.
- `Authorized::AuthSig` now has type `redjubjub::Signature<SpendAuth>`.
- `SpendDescription::temporary_zcashd_from_parts` now takes `rk` as
Expand All @@ -128,7 +130,6 @@ The entries below are relative to the `zcash_primitives::sapling` module as of
`redjubjub::Signature<SpendAuth>` instead of
`sapling_crypto::redjubjub::Signature`.
- `testing::arb_bundle` now takes a `value_balance: V` argument.
- `MapAuth` trait methods now take `&mut self` instead of `&self`.
- `sapling_crypto::circuit::ValueCommitmentOpening::value` is now represented as
a `NoteValue` instead of as a bare `u64`.
- `sapling_crypto::keys`:
Expand Down Expand Up @@ -172,6 +173,7 @@ The entries below are relative to the `zcash_primitives::sapling` module as of
- `OutputDescription::read`
- `OutputDescription::{write_v4, write_v5_without_proof}`
- `OutputDescriptionV5::read`
- `MapAuth` trait
- `sapling_crypto::builder`:
- `SpendDescriptionInfo`
- `sapling_crypto::note_encryption::SaplingDomain::for_height` (use
Expand Down
55 changes: 46 additions & 9 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,27 +53,51 @@ impl BundleType {
bundle_required: false,
};

/// Returns the number of logical spends that a builder will produce in constructing a bundle
/// of this type, given the specified numbers of spends and outputs.
///
/// Returns an error if the specified number of spends and outputs is incompatible with
/// this bundle type.
pub fn num_spends(&self, requested_spends: usize) -> Result<usize, &'static str> {
match self {
BundleType::Transactional { bundle_required } => {
Ok(if *bundle_required || requested_spends > 0 {
core::cmp::max(requested_spends, 1)
} else {
0
})
}
BundleType::Coinbase => {
if requested_spends == 0 {
Ok(0)
} else {
Err("Spends not allowed in coinbase bundles")
}
}
}
}

/// Returns the number of logical outputs that a builder will produce in constructing a bundle
/// of this type, given the specified numbers of spends and outputs.
///
/// Returns an error if the specified number of spends and outputs is incompatible with
/// this bundle type.
pub fn num_outputs(
&self,
num_spends: usize,
num_outputs: usize,
requested_spends: usize,
requested_outputs: usize,
) -> Result<usize, &'static str> {
match self {
BundleType::Transactional { bundle_required } => {
Ok(if *bundle_required || num_outputs > 0 {
core::cmp::max(num_outputs, MIN_SHIELDED_OUTPUTS)
Ok(if *bundle_required || requested_outputs > 0 {
core::cmp::max(requested_outputs, MIN_SHIELDED_OUTPUTS)
} else {
0
})
}
BundleType::Coinbase => {
if num_spends == 0 {
Ok(num_outputs)
if requested_spends == 0 {
Ok(requested_outputs)
} else {
Err("Spends not allowed in coinbase bundles")
}
Expand Down Expand Up @@ -550,6 +574,11 @@ pub fn bundle<SP: SpendProver, OP: OutputProver, R: RngCore, V: TryFrom<i64>>(
}
}

let requested_spend_count = spends.len();
let bundle_spend_count = bundle_type
.num_spends(requested_spend_count)
.map_err(|_| Error::BundleTypeNotSatisfiable)?;

let requested_output_count = outputs.len();
let bundle_output_count = bundle_type
.num_outputs(spends.len(), requested_output_count)
Expand All @@ -562,8 +591,14 @@ pub fn bundle<SP: SpendProver, OP: OutputProver, R: RngCore, V: TryFrom<i64>>(
tx_metadata.spend_indices.resize(spends.len(), 0);
tx_metadata.output_indices.resize(requested_output_count, 0);

// Record initial spend positions
let mut indexed_spends: Vec<_> = spends.into_iter().enumerate().collect();
// Create any required dummy spends and record initial spend positions
let mut indexed_spends: Vec<_> = spends
.into_iter()
.chain(iter::repeat_with(|| SpendInfo::dummy(&mut rng)))
.enumerate()
.take(bundle_spend_count)
.collect();

// Create any required dummy outputs and record initial output positions
let mut indexed_outputs: Vec<_> = outputs
.into_iter()
Expand All @@ -582,7 +617,9 @@ pub fn bundle<SP: SpendProver, OP: OutputProver, R: RngCore, V: TryFrom<i64>>(
.enumerate()
.map(|(i, (pos, spend))| {
// Record the post-randomized spend location
tx_metadata.spend_indices[pos] = i;
if pos < requested_spend_count {
tx_metadata.spend_indices[pos] = i;
}

spend.prepare(&mut rng)
})
Expand Down

0 comments on commit 0998e89

Please sign in to comment.