Skip to content

Commit bd81617

Browse files
authored
Zsa Swap Utils (#141)
This PR contains a set of additions/changes that allow ZSA Swaps to be implemented in librustzcash: - Adds Swap BundleType and Flag - Adds AuthorizedWithProof trait that defines proof() method for both Authorized and ActionGroupAuthorized - Adds utility methods e.g. from_parts() for SwapBundle, is_empty() in Builder - Changes visibility of some methods e.g. dummy() for testing in librustzcash - Removes ActionGroup structure
1 parent e3da0ad commit bd81617

13 files changed

Lines changed: 205 additions & 265 deletions

File tree

benches/circuit.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use pprof::criterion::{Output, PProfProfiler};
88

99
use orchard::{
1010
builder::{Builder, BundleType},
11+
bundle::Authorization,
1112
circuit::{ProvingKey, VerifyingKey},
1213
keys::{FullViewingKey, Scope, SpendingKey},
1314
note::AssetBase,
@@ -86,7 +87,13 @@ fn criterion_benchmark<FL: OrchardFlavorBench>(c: &mut Criterion) {
8687
.unwrap();
8788
assert!(bundle.verify_proof(&vk).is_ok());
8889
group.bench_function(BenchmarkId::new("bundle", num_recipients), |b| {
89-
b.iter(|| bundle.authorization().proof().verify(&vk, &instances));
90+
b.iter(|| {
91+
bundle
92+
.authorization()
93+
.proof()
94+
.unwrap()
95+
.verify(&vk, &instances)
96+
});
9097
});
9198
}
9299
}

rustfmt.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
reorder_imports = false

src/builder.rs

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ use crate::{
2626
},
2727
note::{AssetBase, Note, Rho, TransmittedNoteCiphertext},
2828
orchard_flavor::{Flavor, OrchardFlavor, OrchardVanilla, OrchardZSA},
29-
primitives::redpallas::{self, Binding, SpendAuth},
30-
swap_bundle::{ActionGroup, ActionGroupAuthorized},
29+
primitives::redpallas::{self, Binding, SpendAuth, SigningKey},
30+
swap_bundle::ActionGroupAuthorized,
3131
tree::{Anchor, MerklePath},
3232
value::{self, NoteValue, OverflowError, ValueCommitTrapdoor, ValueCommitment, ValueSum},
3333
};
@@ -65,10 +65,16 @@ impl BundleType {
6565
bundle_required: false,
6666
};
6767

68+
/// The default bundle with all flags enabled, including Asset Swaps.
69+
pub const DEFAULT_SWAP: BundleType = BundleType::Transactional {
70+
flags: Flags::ENABLED_WITH_SWAPS,
71+
bundle_required: false,
72+
};
73+
6874
/// The DISABLED bundle type does not permit any bundle to be produced, and when used in the
6975
/// builder will prevent any spends or outputs from being added.
7076
pub const DISABLED: BundleType = BundleType::Transactional {
71-
flags: Flags::from_parts(false, false, false),
77+
flags: Flags::from_parts(false, false, false, false),
7278
bundle_required: false,
7379
};
7480

@@ -513,12 +519,6 @@ impl BundleMetadata {
513519
/// A tuple containing an in-progress bundle with no proofs or signatures, and its associated metadata.
514520
pub type UnauthorizedBundleWithMetadata<V, FL> = (UnauthorizedBundle<V, FL>, BundleMetadata);
515521

516-
/// A tuple containing an in-progress action group with no proofs or signatures, and its associated metadata.
517-
pub type UnauthorizedActionGroupWithMetadata<V> = (
518-
ActionGroup<InProgress<Unproven, Unauthorized>, V>,
519-
BundleMetadata,
520-
);
521-
522522
/// A builder that constructs a [`Bundle`] from a set of notes to be spent, and outputs
523523
/// to receive funds.
524524
#[derive(Debug)]
@@ -544,6 +544,14 @@ impl Builder {
544544
}
545545
}
546546

547+
/// Returns true if the builder is empty.
548+
pub fn is_empty(&self) -> bool {
549+
self.spends.is_empty()
550+
&& self.outputs.is_empty()
551+
&& self.burn.is_empty()
552+
&& self.reference_notes.is_empty()
553+
}
554+
547555
/// Adds a note to be spent in this transaction.
548556
///
549557
/// - `note` is a spendable note, obtained by trial-decrypting an [`Action`] using the
@@ -690,19 +698,20 @@ impl Builder {
690698
self.bundle_type,
691699
self.spends,
692700
self.outputs,
701+
0,
693702
SpecificBuilderParams::BundleParams(self.burn),
694703
)
695704
}
696705

697706
/// Builds an action group containing the given spent and output notes.
698707
///
699708
/// The returned action group will have no proof or signatures; these can be applied with
700-
/// [`ActionGroup::create_proof`] and [`ActionGroup::apply_signatures`] respectively.
709+
/// [`Bundle::create_proof`] and [`Bundle::apply_signatures`] respectively.
701710
pub fn build_action_group<V: TryFrom<i64>>(
702711
self,
703712
rng: impl RngCore,
704-
timelimit: u32,
705-
) -> Result<UnauthorizedActionGroupWithMetadata<V>, BuildError> {
713+
expiry_height: u32,
714+
) -> Result<UnauthorizedBundleWithMetadata<V, OrchardZSA>, BuildError> {
706715
if !self.burn.is_empty() {
707716
return Err(BuildError::BurnNotEmptyInActionGroup);
708717
}
@@ -712,14 +721,9 @@ impl Builder {
712721
self.bundle_type,
713722
self.spends,
714723
self.outputs,
724+
expiry_height,
715725
SpecificBuilderParams::ActionGroupParams(self.reference_notes),
716726
)
717-
.map(|(action_group, metadata)| {
718-
(
719-
ActionGroup::from_parts(action_group, timelimit, None),
720-
metadata,
721-
)
722-
})
723727
}
724728
}
725729

@@ -813,6 +817,7 @@ pub fn bundle<V: TryFrom<i64>, FL: OrchardFlavor>(
813817
bundle_type: BundleType,
814818
spends: Vec<SpendInfo>,
815819
outputs: Vec<OutputInfo>,
820+
expiry_height: u32,
816821
specific_params: SpecificBuilderParams,
817822
) -> Result<UnauthorizedBundleWithMetadata<V, FL>, BuildError> {
818823
let flags = bundle_type.flags();
@@ -979,6 +984,7 @@ pub fn bundle<V: TryFrom<i64>, FL: OrchardFlavor>(
979984
result_value_balance,
980985
burn,
981986
anchor,
987+
expiry_height,
982988
InProgress {
983989
proof: Unproven {
984990
witnesses,
@@ -1019,6 +1025,10 @@ impl<P, S: InProgressSignatures> InProgress<P, S> {
10191025

10201026
impl<P: fmt::Debug, S: InProgressSignatures> Authorization for InProgress<P, S> {
10211027
type SpendAuth = S::SpendAuth;
1028+
1029+
fn proof(&self) -> Option<&Proof> {
1030+
None
1031+
}
10221032
}
10231033

10241034
/// Marker for a bundle without a proof.
@@ -1119,8 +1129,8 @@ pub struct SigningMetadata {
11191129
/// If this action is spending a dummy note, this field holds that note's spend
11201130
/// authorizing key.
11211131
///
1122-
/// These keys are used automatically in [`Bundle<Unauthorized>::prepare`] or
1123-
/// [`Bundle<Unauthorized>::apply_signatures`] to sign dummy spends.
1132+
/// These keys are used automatically in [`Bundle<Unauthorized, _, _>::prepare`] or
1133+
/// [`Bundle<Unauthorized, _, _>::apply_signatures`] to sign dummy spends.
11241134
dummy_ask: Option<SpendAuthorizingKey>,
11251135
parts: SigningParts,
11261136
}
@@ -1256,23 +1266,15 @@ impl<V, D: OrchardDomainCommon> Bundle<InProgress<Proof, Unauthorized>, V, D> {
12561266
})
12571267
.finalize()
12581268
}
1259-
}
12601269

1261-
impl<V, D: OrchardDomainCommon> Bundle<InProgress<Proof, Unauthorized>, V, D> {
1262-
/// Applies signatures to this action group, in order to authorize it.
1270+
/// Applies signatures to this bundle as an action group, in order to authorize it.
12631271
#[allow(clippy::type_complexity)]
12641272
pub fn apply_signatures_for_action_group<R: RngCore + CryptoRng>(
12651273
self,
12661274
mut rng: R,
12671275
action_group_digest: [u8; 32],
12681276
signing_keys: &[SpendAuthorizingKey],
1269-
) -> Result<
1270-
(
1271-
redpallas::SigningKey<Binding>,
1272-
Bundle<ActionGroupAuthorized, V, D>,
1273-
),
1274-
BuildError,
1275-
> {
1277+
) -> Result<(Bundle<ActionGroupAuthorized, V, D>, SigningKey<Binding>), BuildError> {
12761278
signing_keys
12771279
.iter()
12781280
.fold(
@@ -1395,20 +1397,14 @@ impl<V, D: OrchardDomainCommon> Bundle<InProgress<Proof, ActionGroupPartiallyAut
13951397
#[allow(clippy::type_complexity)]
13961398
pub fn finalize(
13971399
self,
1398-
) -> Result<
1399-
(
1400-
redpallas::SigningKey<Binding>,
1401-
Bundle<ActionGroupAuthorized, V, D>,
1402-
),
1403-
BuildError,
1404-
> {
1400+
) -> Result<(Bundle<ActionGroupAuthorized, V, D>, SigningKey<Binding>), BuildError> {
14051401
let bsk = self.authorization().sigs.bsk;
14061402
self.try_map_authorization(
14071403
&mut (),
14081404
|_, _, maybe| maybe.finalize(),
14091405
|_, partial| Ok(ActionGroupAuthorized::from_parts(partial.proof)),
14101406
)
1411-
.map(|bundle| (bsk, bundle))
1407+
.map(|bundle| (bundle, bsk))
14121408
}
14131409
}
14141410

0 commit comments

Comments
 (0)