Skip to content

Commit

Permalink
Add builder::PaddingRule for explicit control over output padding.
Browse files Browse the repository at this point in the history
  • Loading branch information
nuttycom committed Dec 12, 2023
1 parent dfc2442 commit b4ae506
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 9 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ The entries below are relative to the `zcash_primitives::sapling` module as of
generic parameters and returns `(UnauthorizedBundle, SaplingMetadata)`. The
caller can then use `Bundle::<InProgress<Unproven, _>>::create_proofs` to
create spend and output proofs for the bundle.
- `SaplingBuilder::bundle_output_count` has been changed to use the padding
rule to compute its result. As part of the extraction from `zcash_primitives`,
the behavior of the `build` method changed such that the implementation of
`bundle_output_count` no longer actually reflected the behavior of the builder.
- `Error` has new error variants:
- `Error::DuplicateSignature`
- `Error::InvalidExternalSignature`
Expand Down
47 changes: 38 additions & 9 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,36 @@ use crate::{
/// with dummy outputs if necessary. See <https://github.com/zcash/zcash/issues/3615>.
const MIN_SHIELDED_OUTPUTS: usize = 2;

/// An enumeration of rules for construction of dummy outputs that may be applied to Sapling bundle
/// construction.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PaddingRule {
/// A rule that requires that at least the specified number of Sapling outputs is constructed,
/// irrespective of whether any genuine outputs are being included. If no padding is to be
/// performed, `Require(0)` may be used.
Require(usize),
/// A rule that requires that at least the specified number of Sapling outputs are constructed,
/// iff any genuine outputs are being included.
PadTo(usize),
}

impl PaddingRule {
pub const DEFAULT: PaddingRule = PaddingRule::PadTo(MIN_SHIELDED_OUTPUTS);

pub fn num_outputs(&self, real_outputs: usize) -> usize {
match self {
PaddingRule::Require(n) => std::cmp::max(real_outputs, *n),
PaddingRule::PadTo(n) => {
if real_outputs == 0 {
0
} else {
std::cmp::max(real_outputs, *n)
}
}
}
}
}

#[derive(Debug, PartialEq, Eq)]
pub enum Error {
AnchorMismatch,
Expand Down Expand Up @@ -289,16 +319,18 @@ pub struct SaplingBuilder {
spends: Vec<SpendDescriptionInfo>,
outputs: Vec<SaplingOutputInfo>,
zip212_enforcement: Zip212Enforcement,
padding_rule: PaddingRule,
}

impl SaplingBuilder {
pub fn new(zip212_enforcement: Zip212Enforcement) -> Self {
pub fn new(zip212_enforcement: Zip212Enforcement, padding_rule: PaddingRule) -> Self {
SaplingBuilder {
anchor: None,
value_balance: ValueSum::zero(),
spends: vec![],
outputs: vec![],
zip212_enforcement,
padding_rule,
}
}

Expand All @@ -319,11 +351,7 @@ impl SaplingBuilder {
/// This may be larger than the number of outputs that have been added to the builder,
/// depending on whether padding is going to be applied.
pub fn bundle_output_count(&self) -> usize {
// This matches the padding behaviour in `Self::build`.
match self.spends.len() {
0 => self.outputs.len(),
_ => std::cmp::max(MIN_SHIELDED_OUTPUTS, self.outputs.len()),
}
self.padding_rule.num_outputs(self.outputs.len())
}

/// Returns the net value represented by the spends and outputs added to this builder,
Expand Down Expand Up @@ -407,6 +435,7 @@ impl SaplingBuilder {
mut rng: R,
) -> Result<Option<(UnauthorizedBundle<V>, SaplingMetadata)>, Error> {
let value_balance = self.try_value_balance()?;
let bundle_output_count = self.bundle_output_count();

// Record initial positions of spends and outputs
let mut indexed_spends: Vec<_> = self.spends.into_iter().enumerate().collect();
Expand All @@ -425,7 +454,7 @@ impl SaplingBuilder {

// Pad Sapling outputs
if !indexed_spends.is_empty() {
while indexed_outputs.len() < MIN_SHIELDED_OUTPUTS {
while indexed_outputs.len() < bundle_output_count {
indexed_outputs.push(None);
}
}
Expand Down Expand Up @@ -876,7 +905,7 @@ pub mod testing {
frontier::testing::arb_commitment_tree, witness::IncrementalWitness,
};

use super::SaplingBuilder;
use super::{PaddingRule, SaplingBuilder};

#[allow(dead_code)]
fn arb_bundle<V: fmt::Debug + From<i64>>(
Expand All @@ -902,7 +931,7 @@ pub mod testing {
})
.prop_map(
move |(extsk, spendable_notes, commitment_trees, rng_seed, fake_sighash_bytes)| {
let mut builder = SaplingBuilder::new(zip212_enforcement);
let mut builder = SaplingBuilder::new(zip212_enforcement, PaddingRule::DEFAULT);
let mut rng = StdRng::from_seed(rng_seed);

for (note, path) in spendable_notes
Expand Down

0 comments on commit b4ae506

Please sign in to comment.