diff --git a/CHANGELOG.md b/CHANGELOG.md index aa45607..c8fcaa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,9 +96,12 @@ The entries below are relative to the `zcash_primitives::sapling` module as of type parameter. - `Builder::new` now takes a `Zip212Enforcement` argument instead of a `P: zcash_primitives::consensus::Parameters` argument and a target height. + It also now takes as an argument the Sapling anchor to be used for all + spends in the bundle. - `Builder::add_spend` now takes `extsk` by reference. Also, it no longer takes a `diversifier` argument as the diversifier may be obtained - from the note. + from the note. All calls to `add_spend` are now required to use an anchor + that corresponds to the anchor provided at builder construction. - `Builder::add_output` now takes an `Option<[u8; 512]>` memo instead of a `MemoBytes`. - `Builder::build` no longer takes a prover, proving context, progress diff --git a/src/builder.rs b/src/builder.rs index af95b12..74671a7 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -34,12 +34,24 @@ const MIN_SHIELDED_OUTPUTS: usize = 2; pub enum BundleType { /// A transactional bundle will be padded if necessary to contain at least 2 outputs, /// irrespective of whether any genuine outputs are required. - Transactional { anchor: Node }, + Transactional { + /// A flag that, when set to `true`, indicates that the resulting bundle should be produced + /// with the minimum required outputs irrespective of whether any outputs have been + /// requested; if no explicit outputs have been added, all of the outputs in the resulting + /// bundle will be dummies. + outputs_required: bool, + }, /// A coinbase bundle is required to have no spends. No output padding is performed. Coinbase, } impl BundleType { + /// The default bundle type has all flags enabled, and does not require a bundle to be produced + /// if no spends or outputs have been added to the bundle. + pub const DEFAULT: BundleType = BundleType::Transactional { + outputs_required: false, + }; + /// 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. /// @@ -51,8 +63,12 @@ impl BundleType { num_outputs: usize, ) -> Result { match self { - BundleType::Transactional { .. } => { - Ok(core::cmp::max(num_outputs, MIN_SHIELDED_OUTPUTS)) + BundleType::Transactional { outputs_required } => { + Ok(if *outputs_required || num_outputs > 0 { + core::cmp::max(num_outputs, MIN_SHIELDED_OUTPUTS) + } else { + 0 + }) } BundleType::Coinbase => { if num_spends == 0 { @@ -130,12 +146,12 @@ impl SpendInfo { self.note.value() } - fn has_matching_anchor(&self, anchor: Node) -> bool { + fn has_matching_anchor(&self, anchor: &Node) -> bool { if self.note.value() == NoteValue::ZERO { true } else { let node = Node::from_cmu(&self.note.cmu()); - self.merkle_path.root(node) == anchor + &self.merkle_path.root(node) == anchor } } @@ -368,16 +384,22 @@ pub struct Builder { outputs: Vec, zip212_enforcement: Zip212Enforcement, bundle_type: BundleType, + anchor: Node, } impl Builder { - pub fn new(zip212_enforcement: Zip212Enforcement, bundle_type: BundleType) -> Self { + pub fn new( + zip212_enforcement: Zip212Enforcement, + bundle_type: BundleType, + anchor: Node, + ) -> Self { Builder { value_balance: ValueSum::zero(), spends: vec![], outputs: vec![], zip212_enforcement, bundle_type, + anchor, } } @@ -422,8 +444,8 @@ impl Builder { // Consistency check: all anchors must equal the first one match self.bundle_type { - BundleType::Transactional { anchor } => { - if !spend.has_matching_anchor(anchor) { + BundleType::Transactional { .. } => { + if !spend.has_matching_anchor(&self.anchor) { return Err(Error::AnchorMismatch); } } @@ -465,10 +487,11 @@ impl Builder { ) -> Result, SaplingMetadata)>, Error> { bundle::( rng, - self.spends, - self.outputs, self.bundle_type, self.zip212_enforcement, + self.anchor, + self.spends, + self.outputs, ) } } @@ -477,15 +500,16 @@ impl Builder { /// and outputs. pub fn bundle>( mut rng: R, - spends: Vec, - outputs: Vec, bundle_type: BundleType, zip212_enforcement: Zip212Enforcement, + anchor: Node, + spends: Vec, + outputs: Vec, ) -> Result, SaplingMetadata)>, Error> { match bundle_type { - BundleType::Transactional { anchor } => { + BundleType::Transactional { .. } => { for spend in &spends { - if !spend.has_matching_anchor(anchor) { + if !spend.has_matching_anchor(&anchor) { return Err(Error::AnchorMismatch); } } @@ -1011,8 +1035,7 @@ pub mod testing { Node::from_scalar(*tree.root(node).inner()) }, ); - let mut builder = - Builder::new(zip212_enforcement, BundleType::Transactional { anchor }); + let mut builder = Builder::new(zip212_enforcement, BundleType::DEFAULT, anchor); let mut rng = StdRng::from_seed(rng_seed); for (note, path) in spendable_notes