Skip to content

Commit

Permalink
Allow Transaction Builder to be called with inconsistent keys.
Browse files Browse the repository at this point in the history
  • Loading branch information
murisi committed Sep 10, 2024
1 parent e6451ec commit 7b2316a
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 76 deletions.
9 changes: 9 additions & 0 deletions masp_primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,12 @@ pub use num_traits;

#[cfg(test)]
mod test_vectors;

#[cfg(not(feature = "arbitrary"))]
pub trait MaybeArbitrary<'a> {}

#[cfg(feature = "arbitrary")]
pub trait MaybeArbitrary<'a>: arbitrary::Arbitrary<'a> {}

#[cfg(feature = "arbitrary")]
impl<'a, T: for<'b> arbitrary::Arbitrary<'b>> MaybeArbitrary<'a> for T {}
10 changes: 7 additions & 3 deletions masp_primitives/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ use self::{
},
txid::{to_txid, BlockTxCommitmentDigester, TxIdDigester},
};
use crate::MaybeArbitrary;
use borsh::schema::add_definition;
use borsh::schema::Fields;
use borsh::schema::{Declaration, Definition};
use std::marker::PhantomData;
use std::ops::RangeInclusive;

#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
Expand Down Expand Up @@ -224,11 +226,13 @@ impl Authorization for Authorized {
type SaplingAuth = sapling::Authorized;
}

pub struct Unauthorized;
pub struct Unauthorized<K: crate::zip32::ExtendedKey>(PhantomData<K>);

impl Authorization for Unauthorized {
impl<K: crate::zip32::ExtendedKey + PartialEq + Clone + Debug + for<'a> MaybeArbitrary<'a>>
Authorization for Unauthorized<K>
{
type TransparentAuth = transparent::builder::Unauthorized;
type SaplingAuth = sapling::builder::Unauthorized;
type SaplingAuth = sapling::builder::Unauthorized<K>;
}

/// A MASP transaction.
Expand Down
21 changes: 15 additions & 6 deletions masp_primitives/src/transaction/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ use crate::{
txid::TxIdDigester,
Transaction, TransactionData, TransparentAddress, TxVersion, Unauthorized,
},
zip32::ExtendedSpendingKey,
zip32::{ExtendedKey, ExtendedSpendingKey},
MaybeArbitrary,
};

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

impl<P: consensus::Parameters> Builder<P> {
impl<
P: consensus::Parameters,
K: ExtendedKey + std::fmt::Debug + Clone + PartialEq + for<'a> MaybeArbitrary<'a>,
> Builder<P, K>
{
/// Creates a new `Builder` targeted for inclusion in the block with the given height,
/// using default values for general transaction fields and the default OS random.
///
Expand All @@ -181,12 +186,16 @@ impl<P: consensus::Parameters> Builder<P> {
}
}

impl<P: consensus::Parameters> Builder<P> {
impl<
P: consensus::Parameters,
K: ExtendedKey + std::fmt::Debug + Clone + PartialEq + for<'a> MaybeArbitrary<'a>,
> Builder<P, K>
{
/// Common utility function for builder construction.
///
/// WARNING: THIS MUST REMAIN PRIVATE AS IT ALLOWS CONSTRUCTION
/// OF BUILDERS WITH NON-CryptoRng RNGs
fn new_internal(params: P, target_height: BlockHeight) -> Builder<P> {
fn new_internal(params: P, target_height: BlockHeight) -> Builder<P, K> {
Builder {
params: params.clone(),
target_height,
Expand All @@ -203,7 +212,7 @@ impl<P: consensus::Parameters> Builder<P> {
/// paths for previous Sapling notes.
pub fn add_sapling_spend(
&mut self,
extsk: ExtendedSpendingKey,
extsk: K,
diversifier: Diversifier,
note: Note,
merkle_path: MerklePath<Node>,
Expand Down Expand Up @@ -347,7 +356,7 @@ impl<P: consensus::Parameters> Builder<P> {
)
.map_err(Error::SaplingBuild)?;

let unauthed_tx: TransactionData<Unauthorized> = TransactionData {
let unauthed_tx: TransactionData<Unauthorized<K>> = TransactionData {
version,
consensus_branch_id: BranchId::for_height(&self.params, self.target_height),
lock_time: 0,
Expand Down
49 changes: 35 additions & 14 deletions masp_primitives/src/transaction/components/sapling/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use ff::PrimeField;
use group::GroupEncoding;
use rand::{seq::SliceRandom, CryptoRng, RngCore};

use crate::MaybeArbitrary;
use crate::{
asset_type::AssetType,
consensus::{self, BlockHeight},
Expand All @@ -33,15 +34,17 @@ use crate::{
},
},
},
zip32::ExtendedSpendingKey,
zip32::{ExtendedKey, ExtendedSpendingKey},
};
use borsh::schema::add_definition;
use borsh::schema::Declaration;
use borsh::schema::Definition;
use borsh::schema::Fields;
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use std::collections::BTreeMap;
use std::fmt::Debug;
use std::io::Write;
use std::marker::PhantomData;

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

#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub struct Unauthorized {
pub struct Unauthorized<K: ExtendedKey> {
tx_metadata: SaplingMetadata,
phantom: PhantomData<K>,
}

impl std::fmt::Debug for Unauthorized {
impl<K: ExtendedKey> std::fmt::Debug for Unauthorized<K> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "Unauthorized")
}
}

impl Authorization for Unauthorized {
impl<K: ExtendedKey + Clone + Debug + PartialEq + for<'a> MaybeArbitrary<'a>> Authorization
for Unauthorized<K>
{
type Proof = GrothProofBytes;
type AuthSig = SpendDescriptionInfo;
type AuthSig = SpendDescriptionInfo<K>;
}

impl<P, K> SaplingBuilder<P, K> {
Expand Down Expand Up @@ -826,14 +832,18 @@ impl<P, K> SaplingBuilder<P, K> {
}
}

impl<P: consensus::Parameters> SaplingBuilder<P> {
impl<
P: consensus::Parameters,
K: ExtendedKey + Debug + Clone + PartialEq + for<'a> MaybeArbitrary<'a>,
> SaplingBuilder<P, K>
{
/// Adds a Sapling note to be spent in this transaction.
///
/// Returns an error if the given Merkle path does not have the same anchor as the
/// paths for previous Sapling notes.
pub fn add_spend(
&mut self,
extsk: ExtendedSpendingKey,
extsk: K,
diversifier: Diversifier,
note: Note,
merkle_path: MerklePath<Node>,
Expand Down Expand Up @@ -922,7 +932,7 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
bparams: &mut impl BuildParams,
target_height: BlockHeight,
progress_notifier: Option<&Sender<Progress>>,
) -> Result<Option<Bundle<Unauthorized>>, Error> {
) -> Result<Option<Bundle<Unauthorized<K>>>, Error> {
// Record initial positions of spends and outputs
let value_balance = self.value_balance();
let params = self.params;
Expand Down Expand Up @@ -961,7 +971,8 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
let mut progress = 0u32;

// Create Sapling SpendDescriptions
let shielded_spends: Vec<SpendDescription<Unauthorized>> = if !indexed_spends.is_empty() {
let shielded_spends: Vec<SpendDescription<Unauthorized<K>>> = if !indexed_spends.is_empty()
{
let anchor = self
.spend_anchor
.expect("MASP Spend anchor must be set if MASP spends are present.");
Expand All @@ -970,7 +981,10 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
.into_iter()
.enumerate()
.map(|(i, (pos, spend))| {
let proof_generation_key = spend.extsk.expsk.proof_generation_key();
let proof_generation_key = spend
.extsk
.to_proof_generation_key()
.expect("Proof generation key must be known for each MASP spend.");

let nullifier = spend.note.nf(
&proof_generation_key.to_viewing_key().nk,
Expand Down Expand Up @@ -1172,15 +1186,20 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
shielded_converts,
shielded_outputs,
value_balance,
authorization: Unauthorized { tx_metadata },
authorization: Unauthorized {
tx_metadata,
phantom: PhantomData,
},
})
};

Ok(bundle)
}
}

impl SpendDescription<Unauthorized> {
impl<K: ExtendedKey + Debug + Clone + PartialEq + for<'a> MaybeArbitrary<'a>>
SpendDescription<Unauthorized<K>>
{
pub fn apply_signature(&self, spend_auth_sig: Signature) -> SpendDescription<Authorized> {
SpendDescription {
cv: self.cv,
Expand All @@ -1193,7 +1212,9 @@ impl SpendDescription<Unauthorized> {
}
}

impl Bundle<Unauthorized> {
impl<K: ExtendedKey + Debug + Clone + PartialEq + for<'a> MaybeArbitrary<'a>>
Bundle<Unauthorized<K>>
{
pub fn apply_signatures<Pr: TxProver, R: RngCore, S: BuildParams>(
self,
prover: &Pr,
Expand All @@ -1214,7 +1235,7 @@ impl Bundle<Unauthorized> {
.enumerate()
.map(|(i, spend)| {
spend.apply_signature(spend_sig_internal(
PrivateKey(spend.spend_auth_sig.extsk.expsk.ask),
PrivateKey(spend.spend_auth_sig.extsk.to_spending_key().expect("Spend authorization key must be known for each MASP spend.").expsk.ask),
bparams.spend_alpha(i),
sighash_bytes,
rng,
Expand Down
4 changes: 2 additions & 2 deletions masp_primitives/src/zip32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ use borsh::{BorshDeserialize, BorshSerialize};
#[deprecated(note = "Please use the types exported from the `zip32::sapling` module instead.")]
pub use sapling::{
sapling_address, sapling_default_address, sapling_derive_internal_fvk, sapling_find_address,
DiversifiableFullViewingKey, ExtendedFullViewingKey, ExtendedSpendingKey,
ZIP32_SAPLING_FVFP_PERSONALIZATION, ZIP32_SAPLING_INT_PERSONALIZATION,
DiversifiableFullViewingKey, ExtendedFullViewingKey, ExtendedKey, ExtendedSpendingKey,
PseudoExtendedKey, ZIP32_SAPLING_FVFP_PERSONALIZATION, ZIP32_SAPLING_INT_PERSONALIZATION,
ZIP32_SAPLING_MASTER_PERSONALIZATION,
};
use std::io::{Read, Write};
Expand Down
Loading

0 comments on commit 7b2316a

Please sign in to comment.