Skip to content

Commit f2b0cae

Browse files
committed
Allow Transaction Builder to be called with inconsistent keys.
1 parent e6451ec commit f2b0cae

File tree

8 files changed

+236
-99
lines changed

8 files changed

+236
-99
lines changed

masp_primitives/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,15 @@ pub use num_traits;
3434

3535
#[cfg(test)]
3636
mod test_vectors;
37+
38+
#[cfg(not(feature = "arbitrary"))]
39+
pub trait MaybeArbitrary<'a> {}
40+
41+
#[cfg(not(feature = "arbitrary"))]
42+
impl<'a, T> MaybeArbitrary<'a> for T {}
43+
44+
#[cfg(feature = "arbitrary")]
45+
pub trait MaybeArbitrary<'a>: arbitrary::Arbitrary<'a> {}
46+
47+
#[cfg(feature = "arbitrary")]
48+
impl<'a, T: for<'b> arbitrary::Arbitrary<'b>> MaybeArbitrary<'a> for T {}

masp_primitives/src/transaction.rs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@ use self::{
3434
},
3535
txid::{to_txid, BlockTxCommitmentDigester, TxIdDigester},
3636
};
37+
use crate::MaybeArbitrary;
3738
use borsh::schema::add_definition;
3839
use borsh::schema::Fields;
3940
use borsh::schema::{Declaration, Definition};
41+
use std::marker::PhantomData;
4042
use std::ops::RangeInclusive;
4143

4244
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
@@ -195,23 +197,17 @@ impl BorshSchema for TxVersion {
195197

196198
/// Authorization state for a bundle of transaction data.
197199
pub trait Authorization {
198-
#[cfg(not(feature = "arbitrary"))]
199-
type TransparentAuth: transparent::Authorization + PartialEq + BorshDeserialize + BorshSerialize;
200-
#[cfg(not(feature = "arbitrary"))]
201-
type SaplingAuth: sapling::Authorization + PartialEq + BorshDeserialize + BorshSerialize;
202-
203-
#[cfg(feature = "arbitrary")]
204200
type TransparentAuth: transparent::Authorization
205201
+ PartialEq
206202
+ BorshDeserialize
207203
+ BorshSerialize
208-
+ for<'a> arbitrary::Arbitrary<'a>;
209-
#[cfg(feature = "arbitrary")]
204+
+ for<'a> MaybeArbitrary<'a>;
205+
210206
type SaplingAuth: sapling::Authorization
211207
+ PartialEq
212208
+ BorshDeserialize
213209
+ BorshSerialize
214-
+ for<'a> arbitrary::Arbitrary<'a>;
210+
+ for<'a> MaybeArbitrary<'a>;
215211
}
216212
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
217213
pub struct Unproven;
@@ -224,11 +220,13 @@ impl Authorization for Authorized {
224220
type SaplingAuth = sapling::Authorized;
225221
}
226222

227-
pub struct Unauthorized;
223+
pub struct Unauthorized<K: crate::zip32::ExtendedKey>(PhantomData<K>);
228224

229-
impl Authorization for Unauthorized {
225+
impl<K: crate::zip32::ExtendedKey + PartialEq + Clone + Debug + for<'a> MaybeArbitrary<'a>>
226+
Authorization for Unauthorized<K>
227+
{
230228
type TransparentAuth = transparent::builder::Unauthorized;
231-
type SaplingAuth = sapling::builder::Unauthorized;
229+
type SaplingAuth = sapling::builder::Unauthorized<K>;
232230
}
233231

234232
/// A MASP transaction.

masp_primitives/src/transaction/builder.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ use crate::{
3030
txid::TxIdDigester,
3131
Transaction, TransactionData, TransparentAddress, TxVersion, Unauthorized,
3232
},
33-
zip32::ExtendedSpendingKey,
33+
zip32::{ExtendedKey, ExtendedSpendingKey},
34+
MaybeArbitrary,
3435
};
3536

3637
#[cfg(feature = "transparent-inputs")]
@@ -168,7 +169,11 @@ impl<P, K, N> Builder<P, K, N> {
168169
}
169170
}
170171

171-
impl<P: consensus::Parameters> Builder<P> {
172+
impl<
173+
P: consensus::Parameters,
174+
K: ExtendedKey + std::fmt::Debug + Clone + PartialEq + for<'a> MaybeArbitrary<'a>,
175+
> Builder<P, K>
176+
{
172177
/// Creates a new `Builder` targeted for inclusion in the block with the given height,
173178
/// using default values for general transaction fields and the default OS random.
174179
///
@@ -181,12 +186,16 @@ impl<P: consensus::Parameters> Builder<P> {
181186
}
182187
}
183188

184-
impl<P: consensus::Parameters> Builder<P> {
189+
impl<
190+
P: consensus::Parameters,
191+
K: ExtendedKey + std::fmt::Debug + Clone + PartialEq + for<'a> MaybeArbitrary<'a>,
192+
> Builder<P, K>
193+
{
185194
/// Common utility function for builder construction.
186195
///
187196
/// WARNING: THIS MUST REMAIN PRIVATE AS IT ALLOWS CONSTRUCTION
188197
/// OF BUILDERS WITH NON-CryptoRng RNGs
189-
fn new_internal(params: P, target_height: BlockHeight) -> Builder<P> {
198+
fn new_internal(params: P, target_height: BlockHeight) -> Builder<P, K> {
190199
Builder {
191200
params: params.clone(),
192201
target_height,
@@ -203,7 +212,7 @@ impl<P: consensus::Parameters> Builder<P> {
203212
/// paths for previous Sapling notes.
204213
pub fn add_sapling_spend(
205214
&mut self,
206-
extsk: ExtendedSpendingKey,
215+
extsk: K,
207216
diversifier: Diversifier,
208217
note: Note,
209218
merkle_path: MerklePath<Node>,
@@ -347,7 +356,7 @@ impl<P: consensus::Parameters> Builder<P> {
347356
)
348357
.map_err(Error::SaplingBuild)?;
349358

350-
let unauthed_tx: TransactionData<Unauthorized> = TransactionData {
359+
let unauthed_tx: TransactionData<Unauthorized<K>> = TransactionData {
351360
version,
352361
consensus_branch_id: BranchId::for_height(&self.params, self.target_height),
353362
lock_time: 0,

masp_primitives/src/transaction/components/sapling.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use crate::{
2525
redjubjub::{self, PublicKey, Signature},
2626
Nullifier,
2727
},
28+
MaybeArbitrary,
2829
};
2930

3031
use super::{amount::I128Sum, GROTH_PROOF_SIZE};
@@ -35,15 +36,8 @@ pub mod builder;
3536
pub mod fees;
3637

3738
pub trait Authorization: Debug {
38-
#[cfg(not(feature = "arbitrary"))]
39-
type Proof: Clone + Debug + PartialEq + Hash;
40-
#[cfg(not(feature = "arbitrary"))]
41-
type AuthSig: Clone + Debug + PartialEq;
42-
43-
#[cfg(feature = "arbitrary")]
44-
type Proof: Clone + Debug + PartialEq + Hash + for<'a> arbitrary::Arbitrary<'a>;
45-
#[cfg(feature = "arbitrary")]
46-
type AuthSig: Clone + Debug + PartialEq + for<'a> arbitrary::Arbitrary<'a>;
39+
type Proof: Clone + Debug + PartialEq + Hash + for<'a> MaybeArbitrary<'a>;
40+
type AuthSig: Clone + Debug + PartialEq + for<'a> MaybeArbitrary<'a>;
4741
}
4842

4943
#[derive(Debug, Copy, Clone, PartialEq, Eq)]

masp_primitives/src/transaction/components/sapling/builder.rs

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use ff::PrimeField;
88
use group::GroupEncoding;
99
use rand::{seq::SliceRandom, CryptoRng, RngCore};
1010

11+
use crate::MaybeArbitrary;
1112
use crate::{
1213
asset_type::AssetType,
1314
consensus::{self, BlockHeight},
@@ -33,15 +34,17 @@ use crate::{
3334
},
3435
},
3536
},
36-
zip32::ExtendedSpendingKey,
37+
zip32::{ExtendedKey, ExtendedSpendingKey},
3738
};
3839
use borsh::schema::add_definition;
3940
use borsh::schema::Declaration;
4041
use borsh::schema::Definition;
4142
use borsh::schema::Fields;
4243
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
4344
use std::collections::BTreeMap;
45+
use std::fmt::Debug;
4446
use std::io::Write;
47+
use std::marker::PhantomData;
4548

4649
/// A subset of the parameters necessary to build a transaction
4750
pub trait BuildParams {
@@ -777,19 +780,22 @@ impl<P: BorshDeserialize, Key: BorshDeserialize> BorshDeserialize for SaplingBui
777780

778781
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
779782
#[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
780-
pub struct Unauthorized {
783+
pub struct Unauthorized<K: ExtendedKey> {
781784
tx_metadata: SaplingMetadata,
785+
phantom: PhantomData<K>,
782786
}
783787

784-
impl std::fmt::Debug for Unauthorized {
788+
impl<K: ExtendedKey> std::fmt::Debug for Unauthorized<K> {
785789
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
786790
write!(f, "Unauthorized")
787791
}
788792
}
789793

790-
impl Authorization for Unauthorized {
794+
impl<K: ExtendedKey + Clone + Debug + PartialEq + for<'a> MaybeArbitrary<'a>> Authorization
795+
for Unauthorized<K>
796+
{
791797
type Proof = GrothProofBytes;
792-
type AuthSig = SpendDescriptionInfo;
798+
type AuthSig = SpendDescriptionInfo<K>;
793799
}
794800

795801
impl<P, K> SaplingBuilder<P, K> {
@@ -826,14 +832,18 @@ impl<P, K> SaplingBuilder<P, K> {
826832
}
827833
}
828834

829-
impl<P: consensus::Parameters> SaplingBuilder<P> {
835+
impl<
836+
P: consensus::Parameters,
837+
K: ExtendedKey + Debug + Clone + PartialEq + for<'a> MaybeArbitrary<'a>,
838+
> SaplingBuilder<P, K>
839+
{
830840
/// Adds a Sapling note to be spent in this transaction.
831841
///
832842
/// Returns an error if the given Merkle path does not have the same anchor as the
833843
/// paths for previous Sapling notes.
834844
pub fn add_spend(
835845
&mut self,
836-
extsk: ExtendedSpendingKey,
846+
extsk: K,
837847
diversifier: Diversifier,
838848
note: Note,
839849
merkle_path: MerklePath<Node>,
@@ -922,7 +932,7 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
922932
bparams: &mut impl BuildParams,
923933
target_height: BlockHeight,
924934
progress_notifier: Option<&Sender<Progress>>,
925-
) -> Result<Option<Bundle<Unauthorized>>, Error> {
935+
) -> Result<Option<Bundle<Unauthorized<K>>>, Error> {
926936
// Record initial positions of spends and outputs
927937
let value_balance = self.value_balance();
928938
let params = self.params;
@@ -961,7 +971,8 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
961971
let mut progress = 0u32;
962972

963973
// Create Sapling SpendDescriptions
964-
let shielded_spends: Vec<SpendDescription<Unauthorized>> = if !indexed_spends.is_empty() {
974+
let shielded_spends: Vec<SpendDescription<Unauthorized<K>>> = if !indexed_spends.is_empty()
975+
{
965976
let anchor = self
966977
.spend_anchor
967978
.expect("MASP Spend anchor must be set if MASP spends are present.");
@@ -970,7 +981,10 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
970981
.into_iter()
971982
.enumerate()
972983
.map(|(i, (pos, spend))| {
973-
let proof_generation_key = spend.extsk.expsk.proof_generation_key();
984+
let proof_generation_key = spend
985+
.extsk
986+
.to_proof_generation_key()
987+
.expect("Proof generation key must be known for each MASP spend.");
974988

975989
let nullifier = spend.note.nf(
976990
&proof_generation_key.to_viewing_key().nk,
@@ -1172,15 +1186,20 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
11721186
shielded_converts,
11731187
shielded_outputs,
11741188
value_balance,
1175-
authorization: Unauthorized { tx_metadata },
1189+
authorization: Unauthorized {
1190+
tx_metadata,
1191+
phantom: PhantomData,
1192+
},
11761193
})
11771194
};
11781195

11791196
Ok(bundle)
11801197
}
11811198
}
11821199

1183-
impl SpendDescription<Unauthorized> {
1200+
impl<K: ExtendedKey + Debug + Clone + PartialEq + for<'a> MaybeArbitrary<'a>>
1201+
SpendDescription<Unauthorized<K>>
1202+
{
11841203
pub fn apply_signature(&self, spend_auth_sig: Signature) -> SpendDescription<Authorized> {
11851204
SpendDescription {
11861205
cv: self.cv,
@@ -1193,7 +1212,9 @@ impl SpendDescription<Unauthorized> {
11931212
}
11941213
}
11951214

1196-
impl Bundle<Unauthorized> {
1215+
impl<K: ExtendedKey + Debug + Clone + PartialEq + for<'a> MaybeArbitrary<'a>>
1216+
Bundle<Unauthorized<K>>
1217+
{
11971218
pub fn apply_signatures<Pr: TxProver, R: RngCore, S: BuildParams>(
11981219
self,
11991220
prover: &Pr,
@@ -1214,7 +1235,7 @@ impl Bundle<Unauthorized> {
12141235
.enumerate()
12151236
.map(|(i, spend)| {
12161237
spend.apply_signature(spend_sig_internal(
1217-
PrivateKey(spend.spend_auth_sig.extsk.expsk.ask),
1238+
PrivateKey(spend.spend_auth_sig.extsk.to_spending_key().expect("Spend authorization key must be known for each MASP spend.").expsk.ask),
12181239
bparams.spend_alpha(i),
12191240
sighash_bytes,
12201241
rng,

masp_primitives/src/transaction/components/transparent.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::io::{self, Read, Write};
66

77
use crate::asset_type::AssetType;
88
use crate::transaction::TransparentAddress;
9+
use crate::MaybeArbitrary;
910
use borsh::schema::add_definition;
1011
use borsh::schema::Declaration;
1112
use borsh::schema::Definition;
@@ -18,11 +19,7 @@ pub mod builder;
1819
pub mod fees;
1920

2021
pub trait Authorization: fmt::Debug {
21-
#[cfg(not(feature = "arbitrary"))]
22-
type TransparentSig: fmt::Debug + Clone + PartialEq;
23-
24-
#[cfg(feature = "arbitrary")]
25-
type TransparentSig: fmt::Debug + Clone + PartialEq + for<'a> arbitrary::Arbitrary<'a>;
22+
type TransparentSig: fmt::Debug + Clone + PartialEq + for<'a> MaybeArbitrary<'a>;
2623
}
2724

2825
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]

masp_primitives/src/zip32.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ use borsh::{BorshDeserialize, BorshSerialize};
1919
#[deprecated(note = "Please use the types exported from the `zip32::sapling` module instead.")]
2020
pub use sapling::{
2121
sapling_address, sapling_default_address, sapling_derive_internal_fvk, sapling_find_address,
22-
DiversifiableFullViewingKey, ExtendedFullViewingKey, ExtendedSpendingKey,
23-
ZIP32_SAPLING_FVFP_PERSONALIZATION, ZIP32_SAPLING_INT_PERSONALIZATION,
22+
DiversifiableFullViewingKey, ExtendedFullViewingKey, ExtendedKey, ExtendedSpendingKey,
23+
PseudoExtendedKey, ZIP32_SAPLING_FVFP_PERSONALIZATION, ZIP32_SAPLING_INT_PERSONALIZATION,
2424
ZIP32_SAPLING_MASTER_PERSONALIZATION,
2525
};
2626
use std::io::{Read, Write};

0 commit comments

Comments
 (0)