From daccf7c1071391a8c259a872f631edfdd10d1497 Mon Sep 17 00:00:00 2001 From: Constance Beguier Date: Thu, 3 Oct 2024 15:13:45 +0200 Subject: [PATCH] Add timelimit into Builder and Bundle --- benches/circuit.rs | 1 + benches/note_decryption.rs | 1 + src/builder.rs | 20 ++++++++++++++++++-- src/bundle.rs | 11 +++++++++++ tests/builder.rs | 4 +++- tests/zsa.rs | 4 ++-- 6 files changed, 36 insertions(+), 5 deletions(-) diff --git a/benches/circuit.rs b/benches/circuit.rs index 668965c4d..d99f3230f 100644 --- a/benches/circuit.rs +++ b/benches/circuit.rs @@ -34,6 +34,7 @@ fn criterion_benchmark(c: &mut Criterion) { let mut builder = Builder::new( BundleType::DEFAULT_VANILLA, Anchor::from_bytes([0; 32]).unwrap(), + None, ); for _ in 0..num_recipients { builder diff --git a/benches/note_decryption.rs b/benches/note_decryption.rs index d17b01e41..cedc9952a 100644 --- a/benches/note_decryption.rs +++ b/benches/note_decryption.rs @@ -53,6 +53,7 @@ fn bench_note_decryption(c: &mut Criterion) { let mut builder = Builder::new( BundleType::DEFAULT_VANILLA, Anchor::from_bytes([0; 32]).unwrap(), + None, ); // The builder pads to two actions, and shuffles their order. Add two recipients // so the first action is always decryptable. diff --git a/src/builder.rs b/src/builder.rs index f7f8e0f2d..cdf81f5f0 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -149,6 +149,8 @@ pub enum BuildError { BurnDuplicateAsset, /// There is no available split note for this asset. NoSplitNoteAvailable, + /// Timelimit is set (thus it is an ActionGroup builder) but burn is not empty. + TimelimitSetAndBurnNotEmpty, } impl Display for BuildError { @@ -172,6 +174,9 @@ impl Display for BuildError { BurnZero => f.write_str("Burning is not possible for zero values"), BurnDuplicateAsset => f.write_str("Duplicate assets are not allowed when burning"), NoSplitNoteAvailable => f.write_str("No split note has been provided for this asset"), + TimelimitSetAndBurnNotEmpty => f.write_str( + "Timelimit is set (thus it is an ActionGroup builder) but burn is not empty", + ), } } } @@ -518,11 +523,13 @@ pub struct Builder { burn: HashMap, bundle_type: BundleType, anchor: Anchor, + // When timelimit is set, the Builder will build an ActionGroup (burn must be empty) + timelimit: Option, } impl Builder { /// Constructs a new empty builder for an Orchard bundle. - pub fn new(bundle_type: BundleType, anchor: Anchor) -> Self { + pub fn new(bundle_type: BundleType, anchor: Anchor, timelimit: Option) -> Self { Builder { spends: vec![], outputs: vec![], @@ -530,6 +537,7 @@ impl Builder { burn: HashMap::new(), bundle_type, anchor, + timelimit, } } @@ -674,6 +682,7 @@ impl Builder { bundle( rng, self.anchor, + self.timelimit, self.bundle_type, self.spends, self.outputs, @@ -755,15 +764,20 @@ fn pad_spend( /// The returned bundle will have no proof or signatures; these can be applied with /// [`Bundle::create_proof`] and [`Bundle::apply_signatures`] respectively. #[allow(clippy::type_complexity)] +#[allow(clippy::too_many_arguments)] pub fn bundle, FL: OrchardFlavor>( mut rng: impl RngCore, anchor: Anchor, + timelimit: Option, bundle_type: BundleType, spends: Vec, outputs: Vec, split_notes: HashMap, burn: HashMap, ) -> Result>, BuildError> { + if timelimit.is_some() && !burn.is_empty() { + return Err(BuildError::TimelimitSetAndBurnNotEmpty); + } let flags = bundle_type.flags(); let num_requested_spends = spends.len(); @@ -913,6 +927,7 @@ pub fn bundle, FL: OrchardFlavor>( result_value_balance, burn, anchor, + timelimit, InProgress { proof: Unproven { circuits }, sigs: Unauthorized { bsk }, @@ -1275,7 +1290,7 @@ pub mod testing { mut self, ) -> Bundle { let fvk = FullViewingKey::from(&self.sk); - let mut builder = Builder::new(BundleType::DEFAULT_ZSA, self.anchor); + let mut builder = Builder::new(BundleType::DEFAULT_ZSA, self.anchor, None); for (note, path) in self.notes.into_iter() { builder.add_spend(fvk.clone(), note, path).unwrap(); @@ -1409,6 +1424,7 @@ mod tests { let mut builder = Builder::new( BundleType::DEFAULT_VANILLA, EMPTY_ROOTS[MERKLE_DEPTH_ORCHARD].into(), + None, ); builder diff --git a/src/bundle.rs b/src/bundle.rs index de69c3613..7b221a9ca 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -207,6 +207,10 @@ pub struct Bundle { burn: Vec<(AssetBase, NoteValue)>, /// The root of the Orchard commitment tree that this bundle commits to. anchor: Anchor, + /// The timelimit for this Bundle (which is an ActionGroup). + /// + /// Burn must be empty when timelimit is set. + timelimit: Option, /// The authorization for this bundle. authorization: A, } @@ -239,6 +243,7 @@ impl Bundle { value_balance: V, burn: Vec<(AssetBase, NoteValue)>, anchor: Anchor, + timelimit: Option, authorization: A, ) -> Self { Bundle { @@ -247,6 +252,7 @@ impl Bundle { value_balance, burn, anchor, + timelimit, authorization, } } @@ -297,6 +303,7 @@ impl Bundle { value_balance: f(self.value_balance)?, burn: self.burn, anchor: self.anchor, + timelimit: self.timelimit, authorization: self.authorization, }) } @@ -316,6 +323,7 @@ impl Bundle { flags: self.flags, value_balance: self.value_balance, anchor: self.anchor, + timelimit: self.timelimit, authorization: step(context, authorization), burn: self.burn, } @@ -340,6 +348,7 @@ impl Bundle { flags: self.flags, value_balance: self.value_balance, anchor: self.anchor, + timelimit: self.timelimit, authorization: step(context, authorization)?, burn: self.burn, }) @@ -712,6 +721,7 @@ pub mod testing { balances.into_iter().sum::>().unwrap(), burn, anchor, + None, Unauthorized, ) } @@ -744,6 +754,7 @@ pub mod testing { balances.into_iter().sum::>().unwrap(), burn, anchor, + None, Authorized { proof: Proof::new(fake_proof), binding_signature: sk.sign(rng, &fake_sighash), diff --git a/tests/builder.rs b/tests/builder.rs index d1e50de64..790d19305 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -87,6 +87,7 @@ fn bundle_chain() { bundle_required: false, }, anchor, + None, ); let note_value = NoteValue::from_raw(5000); assert_eq!( @@ -142,6 +143,7 @@ fn bundle_chain() { bundle_required: false, }, anchor, + None, ); assert!(builder.add_spend(fvk.clone(), note, merkle_path).is_err()); @@ -151,7 +153,7 @@ fn bundle_chain() { let shielded_bundle: Bundle<_, i64, FL> = { let (merkle_path, anchor) = build_merkle_path(¬e); - let mut builder = Builder::new(FL::DEFAULT_BUNDLE_TYPE, anchor); + let mut builder = Builder::new(FL::DEFAULT_BUNDLE_TYPE, anchor, None); assert_eq!(builder.add_spend(fvk, note, merkle_path), Ok(())); assert_eq!( builder.add_output( diff --git a/tests/zsa.rs b/tests/zsa.rs index fced143a0..1dbc4ee86 100644 --- a/tests/zsa.rs +++ b/tests/zsa.rs @@ -186,7 +186,7 @@ fn create_native_note(keys: &Keychain) -> Note { // Use the empty tree. let anchor = MerkleHashOrchard::empty_root(32.into()).into(); - let mut builder = Builder::new(BundleType::Coinbase, anchor); + let mut builder = Builder::new(BundleType::Coinbase, anchor, None); assert_eq!( builder.add_output( None, @@ -241,7 +241,7 @@ fn build_and_verify_bundle( ) -> Result<(), String> { let rng = OsRng; let shielded_bundle: Bundle<_, i64, OrchardZSA> = { - let mut builder = Builder::new(BundleType::DEFAULT_ZSA, anchor); + let mut builder = Builder::new(BundleType::DEFAULT_ZSA, anchor, None); spends .iter()