Skip to content

Commit

Permalink
Add timelimit into Builder and Bundle
Browse files Browse the repository at this point in the history
  • Loading branch information
ConstanceBeguier committed Oct 10, 2024
1 parent e477b71 commit daccf7c
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 5 deletions.
1 change: 1 addition & 0 deletions benches/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ fn criterion_benchmark<FL: OrchardFlavorBench>(c: &mut Criterion) {
let mut builder = Builder::new(
BundleType::DEFAULT_VANILLA,
Anchor::from_bytes([0; 32]).unwrap(),
None,
);
for _ in 0..num_recipients {
builder
Expand Down
1 change: 1 addition & 0 deletions benches/note_decryption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ fn bench_note_decryption<FL: OrchardFlavorBench>(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.
Expand Down
20 changes: 18 additions & 2 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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",
),
}
}
}
Expand Down Expand Up @@ -518,18 +523,21 @@ pub struct Builder {
burn: HashMap<AssetBase, NoteValue>,
bundle_type: BundleType,
anchor: Anchor,
// When timelimit is set, the Builder will build an ActionGroup (burn must be empty)
timelimit: Option<u64>,
}

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<u64>) -> Self {
Builder {
spends: vec![],
outputs: vec![],
split_notes: HashMap::new(),
burn: HashMap::new(),
bundle_type,
anchor,
timelimit,
}
}

Expand Down Expand Up @@ -674,6 +682,7 @@ impl Builder {
bundle(
rng,
self.anchor,
self.timelimit,
self.bundle_type,
self.spends,
self.outputs,
Expand Down Expand Up @@ -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<V: TryFrom<i64>, FL: OrchardFlavor>(
mut rng: impl RngCore,
anchor: Anchor,
timelimit: Option<u64>,
bundle_type: BundleType,
spends: Vec<SpendInfo>,
outputs: Vec<OutputInfo>,
split_notes: HashMap<AssetBase, SpendInfo>,
burn: HashMap<AssetBase, NoteValue>,
) -> Result<Option<UnauthorizedBundleWithMetadata<V, FL>>, BuildError> {
if timelimit.is_some() && !burn.is_empty() {
return Err(BuildError::TimelimitSetAndBurnNotEmpty);
}
let flags = bundle_type.flags();

let num_requested_spends = spends.len();
Expand Down Expand Up @@ -913,6 +927,7 @@ pub fn bundle<V: TryFrom<i64>, FL: OrchardFlavor>(
result_value_balance,
burn,
anchor,
timelimit,
InProgress {
proof: Unproven { circuits },
sigs: Unauthorized { bsk },
Expand Down Expand Up @@ -1275,7 +1290,7 @@ pub mod testing {
mut self,
) -> Bundle<Authorized, V, FL> {
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();
Expand Down Expand Up @@ -1409,6 +1424,7 @@ mod tests {
let mut builder = Builder::new(
BundleType::DEFAULT_VANILLA,
EMPTY_ROOTS[MERKLE_DEPTH_ORCHARD].into(),
None,
);

builder
Expand Down
11 changes: 11 additions & 0 deletions src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ pub struct Bundle<A: Authorization, V, D: OrchardDomainCommon> {
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<u64>,
/// The authorization for this bundle.
authorization: A,
}
Expand Down Expand Up @@ -239,6 +243,7 @@ impl<A: Authorization, V, D: OrchardDomainCommon> Bundle<A, V, D> {
value_balance: V,
burn: Vec<(AssetBase, NoteValue)>,
anchor: Anchor,
timelimit: Option<u64>,
authorization: A,
) -> Self {
Bundle {
Expand All @@ -247,6 +252,7 @@ impl<A: Authorization, V, D: OrchardDomainCommon> Bundle<A, V, D> {
value_balance,
burn,
anchor,
timelimit,
authorization,
}
}
Expand Down Expand Up @@ -297,6 +303,7 @@ impl<A: Authorization, V, D: OrchardDomainCommon> Bundle<A, V, D> {
value_balance: f(self.value_balance)?,
burn: self.burn,
anchor: self.anchor,
timelimit: self.timelimit,
authorization: self.authorization,
})
}
Expand All @@ -316,6 +323,7 @@ impl<A: Authorization, V, D: OrchardDomainCommon> Bundle<A, V, D> {
flags: self.flags,
value_balance: self.value_balance,
anchor: self.anchor,
timelimit: self.timelimit,
authorization: step(context, authorization),
burn: self.burn,
}
Expand All @@ -340,6 +348,7 @@ impl<A: Authorization, V, D: OrchardDomainCommon> Bundle<A, V, D> {
flags: self.flags,
value_balance: self.value_balance,
anchor: self.anchor,
timelimit: self.timelimit,
authorization: step(context, authorization)?,
burn: self.burn,
})
Expand Down Expand Up @@ -712,6 +721,7 @@ pub mod testing {
balances.into_iter().sum::<Result<ValueSum, _>>().unwrap(),
burn,
anchor,
None,
Unauthorized,
)
}
Expand Down Expand Up @@ -744,6 +754,7 @@ pub mod testing {
balances.into_iter().sum::<Result<ValueSum, _>>().unwrap(),
burn,
anchor,
None,
Authorized {
proof: Proof::new(fake_proof),
binding_signature: sk.sign(rng, &fake_sighash),
Expand Down
4 changes: 3 additions & 1 deletion tests/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ fn bundle_chain<FL: BundleOrchardFlavor>() {
bundle_required: false,
},
anchor,
None,
);
let note_value = NoteValue::from_raw(5000);
assert_eq!(
Expand Down Expand Up @@ -142,6 +143,7 @@ fn bundle_chain<FL: BundleOrchardFlavor>() {
bundle_required: false,
},
anchor,
None,
);

assert!(builder.add_spend(fvk.clone(), note, merkle_path).is_err());
Expand All @@ -151,7 +153,7 @@ fn bundle_chain<FL: BundleOrchardFlavor>() {
let shielded_bundle: Bundle<_, i64, FL> = {
let (merkle_path, anchor) = build_merkle_path(&note);

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(
Expand Down
4 changes: 2 additions & 2 deletions tests/zsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit daccf7c

Please sign in to comment.