From f5e904c5cb119a79d21b98d04c6f625f17f35133 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Wed, 24 Apr 2024 07:27:09 +0200 Subject: [PATCH 1/3] Categorize the randomness received by the Sapling Builder. --- masp_primitives/src/sapling.rs | 16 +- masp_primitives/src/sapling/prover.rs | 21 +- masp_primitives/src/sapling/util.rs | 23 +- masp_primitives/src/transaction/builder.rs | 31 +-- .../transaction/components/sapling/builder.rs | 197 ++++++++++++++---- masp_proofs/src/prover.rs | 6 + masp_proofs/src/sapling/prover.rs | 16 +- 7 files changed, 210 insertions(+), 100 deletions(-) diff --git a/masp_primitives/src/sapling.rs b/masp_primitives/src/sapling.rs index e004c19b..d26834ae 100644 --- a/masp_primitives/src/sapling.rs +++ b/masp_primitives/src/sapling.rs @@ -696,7 +696,7 @@ impl From for u64 { } #[derive(Clone, Debug, Copy)] -pub struct Note { +pub struct Note { /// The asset type that the note represents pub asset_type: AssetType, /// The value of the note @@ -706,7 +706,7 @@ pub struct Note { /// The public key of the address, g_d^ivk pub pk_d: jubjub::SubgroupPoint, /// rseed - pub rseed: Rseed, + pub rseed: R, } impl PartialEq for Note { @@ -826,7 +826,7 @@ impl Note { } } -impl BorshSchema for Note { +impl BorshSchema for Note { fn add_definitions_recursively(definitions: &mut BTreeMap) { let definition = Definition::Struct { fields: Fields::NamedFields(vec![ @@ -834,14 +834,14 @@ impl BorshSchema for Note { ("value".into(), u64::declaration()), ("g_d".into(), <[u8; 32]>::declaration()), ("pk_d".into(), <[u8; 32]>::declaration()), - ("rseed".into(), Rseed::declaration()), + ("rseed".into(), T::declaration()), ]), }; add_definition(Self::declaration(), definition, definitions); AssetType::add_definitions_recursively(definitions); u64::add_definitions_recursively(definitions); <[u8; 32]>::add_definitions_recursively(definitions); - Rseed::add_definitions_recursively(definitions); + T::add_definitions_recursively(definitions); } fn declaration() -> Declaration { @@ -849,7 +849,7 @@ impl BorshSchema for Note { } } -impl BorshSerialize for Note { +impl BorshSerialize for Note { fn serialize(&self, writer: &mut W) -> io::Result<()> { // Write asset type self.asset_type.serialize(writer)?; @@ -865,7 +865,7 @@ impl BorshSerialize for Note { } } -impl BorshDeserialize for Note { +impl BorshDeserialize for Note { fn deserialize_reader(reader: &mut R) -> io::Result { // Read asset type let asset_type = AssetType::deserialize_reader(reader)?; @@ -880,7 +880,7 @@ impl BorshDeserialize for Note { let pk_d = Option::from(jubjub::SubgroupPoint::from_bytes(&pk_d_bytes)) .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "pk_d not in field"))?; // Read note plaintext lead byte - let rseed = Rseed::deserialize_reader(reader)?; + let rseed = T::deserialize_reader(reader)?; // Finally construct note object Ok(Note { asset_type, diff --git a/masp_primitives/src/sapling/prover.rs b/masp_primitives/src/sapling/prover.rs index 946641d1..015b4281 100644 --- a/masp_primitives/src/sapling/prover.rs +++ b/masp_primitives/src/sapling/prover.rs @@ -38,6 +38,7 @@ pub trait TxProver { value: u64, anchor: bls12_381::Scalar, merkle_path: MerklePath, + rcv: jubjub::Fr, ) -> Result<([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint, PublicKey), ()>; /// Create the value commitment and proof for a MASP OutputDescription, @@ -52,6 +53,7 @@ pub trait TxProver { rcm: jubjub::Fr, asset_type: AssetType, value: u64, + rcv: jubjub::Fr, ) -> ([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint); /// Create the value commitment, and proof for a MASP ConvertDescription, @@ -65,6 +67,7 @@ pub trait TxProver { value: u64, anchor: bls12_381::Scalar, merkle_path: MerklePath, + rcv: jubjub::Fr, ) -> Result<([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint), ()>; /// Create the `bindingSig` for a Sapling transaction. All calls to @@ -80,9 +83,6 @@ pub trait TxProver { #[cfg(any(test, feature = "test-dependencies"))] pub mod mock { - use ff::Field; - use rand_core::OsRng; - use crate::{ asset_type::AssetType, constants::SPENDING_KEY_GENERATOR, @@ -115,11 +115,10 @@ pub mod mock { value: u64, _anchor: bls12_381::Scalar, _merkle_path: MerklePath, + rcv: jubjub::Fr, ) -> Result<([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint, PublicKey), ()> { - let mut rng = OsRng; - let cv = asset_type - .value_commitment(value, jubjub::Fr::random(&mut rng)) + .value_commitment(value, rcv) .commitment() .into(); @@ -137,11 +136,10 @@ pub mod mock { _rcm: jubjub::Fr, asset_type: AssetType, value: u64, + rcv: jubjub::Fr, ) -> ([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint) { - let mut rng = OsRng; - let cv = asset_type - .value_commitment(value, jubjub::Fr::random(&mut rng)) + .value_commitment(value, rcv) .commitment() .into(); @@ -155,11 +153,10 @@ pub mod mock { value: u64, _anchor: bls12_381::Scalar, _merkle_path: MerklePath, + rcv: jubjub::Fr, ) -> Result<([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint), ()> { - let mut rng = OsRng; - let cv = allowed_conversion - .value_commitment(value, jubjub::Fr::random(&mut rng)) + .value_commitment(value, rcv) .commitment() .into(); diff --git a/masp_primitives/src/sapling/util.rs b/masp_primitives/src/sapling/util.rs index 34b64bbe..33dd557e 100644 --- a/masp_primitives/src/sapling/util.rs +++ b/masp_primitives/src/sapling/util.rs @@ -1,10 +1,10 @@ use blake2b_simd::Params; -use ff::Field; -use rand_core::{CryptoRng, RngCore}; use crate::consensus::{self, BlockHeight, NetworkUpgrade}; use super::Rseed; +use ff::Field; +use rand_core::{CryptoRng, RngCore}; pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> jubjub::Fr { let mut hasher = Params::new().hash_length(64).personal(persona).to_state(); @@ -19,19 +19,24 @@ pub fn generate_random_rseed( height: BlockHeight, rng: &mut R, ) -> Rseed { - generate_random_rseed_internal(params, height, rng) + if params.is_nu_active(NetworkUpgrade::MASP, height) { + let mut buffer = [0u8; 32]; + rng.fill_bytes(&mut buffer); + Rseed::AfterZip212(buffer) + } else { + Rseed::BeforeZip212(jubjub::Fr::random(rng)) + } } -pub(crate) fn generate_random_rseed_internal( +pub(crate) fn generate_random_rseed_internal( params: &P, height: BlockHeight, - rng: &mut R, + before: jubjub::Fr, + after: [u8; 32], ) -> Rseed { if params.is_nu_active(NetworkUpgrade::MASP, height) { - let mut buffer = [0u8; 32]; - rng.fill_bytes(&mut buffer); - Rseed::AfterZip212(buffer) + Rseed::AfterZip212(after) } else { - Rseed::BeforeZip212(jubjub::Fr::random(rng)) + Rseed::BeforeZip212(before) } } diff --git a/masp_primitives/src/transaction/builder.rs b/masp_primitives/src/transaction/builder.rs index c14e7897..aa97d57f 100644 --- a/masp_primitives/src/transaction/builder.rs +++ b/masp_primitives/src/transaction/builder.rs @@ -21,7 +21,7 @@ use crate::{ amount::{BalanceError, I128Sum, U64Sum, ValueSum, MAX_MONEY}, sapling::{ self, - builder::{SaplingBuilder, SaplingMetadata}, + builder::{Randomness, SaplingBuilder, SaplingMetadata}, }, transparent::{self, builder::TransparentBuilder}, }, @@ -224,7 +224,7 @@ impl Builder { merkle_path: MerklePath, ) -> Result<(), sapling::builder::Error> { self.sapling_builder - .add_spend(&mut self.rng, extsk, diversifier, note, merkle_path) + .add_spend(extsk, diversifier, note, merkle_path) } /// Adds a Sapling note to be spent in this transaction. @@ -254,7 +254,7 @@ impl Builder { return Err(sapling::builder::Error::InvalidAmount); } self.sapling_builder - .add_output(&mut self.rng, ovk, to, asset_type, value, memo) + .add_output(ovk, to, asset_type, value, memo) } /// Adds a transparent coin to be spent in this transaction. @@ -309,6 +309,7 @@ impl Builder { self, prover: &impl TxProver, fee_rule: &FR, + mrng: &mut impl Randomness, ) -> Result<(Transaction, SaplingMetadata), Error> { let fee = fee_rule .fee_required( @@ -319,13 +320,14 @@ impl Builder { self.sapling_builder.outputs().len(), ) .map_err(Error::Fee)?; - self.build_internal(prover, fee) + self.build_internal(prover, fee, mrng) } fn build_internal( self, prover: &impl TxProver, fee: U64Sum, + mrng: &mut impl Randomness, ) -> Result<(Transaction, SaplingMetadata), Error> { let consensus_branch_id = BranchId::for_height(&self.params, self.target_height); @@ -353,6 +355,7 @@ impl Builder { prover, &mut ctx, &mut rng, + mrng, self.target_height, self.progress_notifier.as_ref(), ) @@ -386,7 +389,7 @@ impl Builder { let (sapling_bundle, tx_metadata) = match unauthed_tx .sapling_bundle .map(|b| { - b.apply_signatures(prover, &mut ctx, &mut rng, shielded_sig_commitment.as_ref()) + b.apply_signatures(prover, &mut ctx, &mut rng, mrng, shielded_sig_commitment.as_ref()) }) .transpose() .map_err(Error::SaplingBuild)? @@ -443,7 +446,7 @@ mod testing { use crate::{ consensus::{self, BlockHeight}, sapling::prover::mock::MockTxProver, - transaction::{fees::fixed, Transaction}, + transaction::{builder::Randomness, fees::fixed, Transaction}, }; impl Builder { @@ -460,8 +463,8 @@ mod testing { Self::new_internal(params, rng, height) } - pub fn mock_build(self) -> Result<(Transaction, SaplingMetadata), Error> { - self.build(&MockTxProver, &fixed::FeeRule::standard()) + pub fn mock_build(self, mrng: &mut impl Randomness) -> Result<(Transaction, SaplingMetadata), Error> { + self.build(&MockTxProver, &fixed::FeeRule::standard(), mrng) } } } @@ -558,7 +561,7 @@ mod tests { // Expect a binding signature error, because our inputs aren't valid, but this shows // that a binding signature was attempted assert_eq!( - builder.mock_build(), + builder.mock_build(&mut build_s::RngRandomness::new(OsRng)), Err(Error::SaplingBuild(build_s::Error::BindingSig)) ); } @@ -579,7 +582,7 @@ mod tests { { let builder = Builder::new(TEST_NETWORK, tx_height); assert_eq!( - builder.mock_build(), + builder.mock_build(&mut build_s::RngRandomness::new(OsRng)), Err(Error::InsufficientFunds(I128Sum::from_sum( DEFAULT_FEE.clone() ))) @@ -598,7 +601,7 @@ mod tests { .add_sapling_output(ovk, to, zec(), 50000, MemoBytes::empty()) .unwrap(); assert_eq!( - builder.mock_build(), + builder.mock_build(&mut build_s::RngRandomness::new(OsRng)), Err(Error::InsufficientFunds( I128Sum::from_pair(zec(), 50000).unwrap() + &I128Sum::from_sum(DEFAULT_FEE.clone()) @@ -614,7 +617,7 @@ mod tests { .add_transparent_output(&transparent_address, zec(), 50000) .unwrap(); assert_eq!( - builder.mock_build(), + builder.mock_build(&mut build_s::RngRandomness::new(OsRng)), Err(Error::InsufficientFunds( I128Sum::from_pair(zec(), 50000).unwrap() + &I128Sum::from_sum(DEFAULT_FEE.clone()) @@ -648,7 +651,7 @@ mod tests { .add_transparent_output(&transparent_address, zec(), 20000) .unwrap(); assert_eq!( - builder.mock_build(), + builder.mock_build(&mut build_s::RngRandomness::new(OsRng)), Err(Error::InsufficientFunds( ValueSum::from_pair(zec(), 1).unwrap() )) @@ -683,7 +686,7 @@ mod tests { .add_transparent_output(&transparent_address, zec(), 20000) .unwrap(); assert_eq!( - builder.mock_build(), + builder.mock_build(&mut build_s::RngRandomness::new(OsRng)), Err(Error::SaplingBuild(build_s::Error::BindingSig)) ) } diff --git a/masp_primitives/src/transaction/components/sapling/builder.rs b/masp_primitives/src/transaction/components/sapling/builder.rs index 5ac1d1ae..123da8fb 100644 --- a/masp_primitives/src/transaction/components/sapling/builder.rs +++ b/masp_primitives/src/transaction/components/sapling/builder.rs @@ -20,7 +20,7 @@ use crate::{ redjubjub::{PrivateKey, Signature}, spend_sig_internal, util::generate_random_rseed_internal, - Diversifier, Node, Note, PaymentAddress, + Diversifier, Node, Note, PaymentAddress, Rseed, }, transaction::{ builder::Progress, @@ -42,6 +42,112 @@ use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use std::collections::BTreeMap; use std::io::Write; +pub trait Randomness { + /// Get the commitment value randomness for the ith spend description + fn spend_rcv(&mut self, i: usize) -> jubjub::Fr; + /// Get the commitment value randomness for the ith convert description + fn convert_rcv(&mut self, i: usize) -> jubjub::Fr; + /// Get the commitment value randomness for the ith output description + fn output_rcv(&mut self, i: usize) -> jubjub::Fr; + /// Get the spend authorization randomizer for the ith spend description + fn spend_alpha(&mut self, i: usize) -> jubjub::Fr; + /// Get the Rseed::BeforeZip212 for the ith spend description + fn rseed_before(&mut self, i: usize) -> jubjub::Fr; + /// Get the Rseed::AfterZip212 for the ith spend description + fn rseed_after(&mut self, i: usize) -> [u8; 32]; +} + +/// Pre-generated random parameters for MASPtTransactions +pub struct StoredRandomness { + spend_rcvs: Vec, + convert_rcvs: Vec, + output_rcvs: Vec, + spend_alphas: Vec, + rseed_before: Vec, + rseed_after: Vec<[u8; 32]>, +} + +impl Randomness for StoredRandomness { + fn spend_rcv(&mut self, i: usize) -> jubjub::Fr { + self.spend_rcvs[i] + } + + fn convert_rcv(&mut self, i: usize) -> jubjub::Fr { + self.convert_rcvs[i] + } + + fn output_rcv(&mut self, i: usize) -> jubjub::Fr { + self.output_rcvs[i] + } + + fn spend_alpha(&mut self, i: usize) -> jubjub::Fr { + self.spend_alphas[i] + } + + fn rseed_before(&mut self, i: usize) -> jubjub::Fr { + self.rseed_before[i] + } + + fn rseed_after(&mut self, i: usize) -> [u8; 32] { + self.rseed_after[i] + } +} + +/// Lazily generated random parameters for MASP transactions +pub struct RngRandomness { + rng: R, + spend_rcvs: BTreeMap, + convert_rcvs: BTreeMap, + output_rcvs: BTreeMap, + spend_alphas: BTreeMap, + rseed_before: BTreeMap, + rseed_after: BTreeMap, +} + +impl RngRandomness { + pub fn new(rng: R) -> Self { + Self { + rng, + spend_rcvs: BTreeMap::new(), + convert_rcvs: BTreeMap::new(), + output_rcvs: BTreeMap::new(), + spend_alphas: BTreeMap::new(), + rseed_before: BTreeMap::new(), + rseed_after: BTreeMap::new(), + } + } +} + +impl Randomness for RngRandomness { + fn spend_rcv(&mut self, i: usize) -> jubjub::Fr { + *self.spend_rcvs.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) + } + + fn convert_rcv(&mut self, i: usize) -> jubjub::Fr { + *self.convert_rcvs.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) + } + + fn output_rcv(&mut self, i: usize) -> jubjub::Fr { + *self.output_rcvs.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) + } + + fn spend_alpha(&mut self, i: usize) -> jubjub::Fr { + *self.spend_alphas.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) + } + + fn rseed_before(&mut self, i: usize) -> jubjub::Fr { + *self.rseed_before.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) + } + + fn rseed_after(&mut self, i: usize) -> [u8; 32] { + *self.rseed_after.entry(i).or_insert_with(|| { + let mut buffer = [0u8; 32]; + self.rng.fill_bytes(&mut buffer); + buffer + }) + } +} + /// If there are any shielded inputs, always have at least two shielded outputs, padding /// with dummy outputs if necessary. See . const MIN_SHIELDED_OUTPUTS: usize = 2; @@ -76,7 +182,6 @@ pub struct SpendDescriptionInfo { extsk: Key, diversifier: Diversifier, note: Note, - alpha: jubjub::Fr, merkle_path: MerklePath, } @@ -86,16 +191,14 @@ impl BorshSchema for SpendDescriptionInfo { fields: Fields::NamedFields(vec![ ("extsk".into(), Key::declaration()), ("diversifier".into(), Diversifier::declaration()), - ("note".into(), Note::declaration()), - ("alpha".into(), <[u8; 32]>::declaration()), + ("note".into(), Note::::declaration()), ("merkle_path".into(), MerklePath::<[u8; 32]>::declaration()), ]), }; add_definition(Self::declaration(), definition, definitions); Key::add_definitions_recursively(definitions); Diversifier::add_definitions_recursively(definitions); - Note::add_definitions_recursively(definitions); - <[u8; 32]>::add_definitions_recursively(definitions); + Note::::add_definitions_recursively(definitions); MerklePath::<[u8; 32]>::add_definitions_recursively(definitions); } @@ -109,7 +212,6 @@ impl BorshSerialize for SpendDescriptionInfo { self.extsk.serialize(writer)?; self.diversifier.serialize(writer)?; self.note.serialize(writer)?; - self.alpha.to_bytes().serialize(writer)?; self.merkle_path.serialize(writer) } } @@ -119,15 +221,11 @@ impl BorshDeserialize for SpendDescriptionInfo { let extsk = Key::deserialize_reader(reader)?; let diversifier = Diversifier::deserialize_reader(reader)?; let note = Note::deserialize_reader(reader)?; - let alpha: Option<_> = - jubjub::Fr::from_bytes(&<[u8; 32]>::deserialize_reader(reader)?).into(); - let alpha = alpha.ok_or_else(|| std::io::Error::from(std::io::ErrorKind::InvalidData))?; let merkle_path = MerklePath::::deserialize_reader(reader)?; Ok(SpendDescriptionInfo { extsk, diversifier, note, - alpha, merkle_path, }) } @@ -159,16 +257,13 @@ pub struct SaplingOutputInfo { /// `None` represents the `ovk = ⊥` case. ovk: Option, to: PaymentAddress, - note: Note, + note: Note<()>, memo: MemoBytes, } impl SaplingOutputInfo { #[allow(clippy::too_many_arguments)] - fn new_internal( - params: &P, - rng: &mut R, - target_height: BlockHeight, + fn new_internal( ovk: Option, to: PaymentAddress, asset_type: AssetType, @@ -180,13 +275,11 @@ impl SaplingOutputInfo { return Err(Error::InvalidAmount); } - let rseed = generate_random_rseed_internal(params, target_height, rng); - let note = Note { g_d, pk_d: *to.pk_d(), value, - rseed, + rseed: (), asset_type, }; @@ -203,19 +296,29 @@ impl SaplingOutputInfo { prover: &Pr, ctx: &mut Pr::SaplingProvingContext, rng: &mut R, + rcv: jubjub::Fr, + rseed: Rseed, ) -> OutputDescription { - let encryptor = sapling_note_encryption::

(self.ovk, self.note, self.to, self.memo); + let note = Note { + rseed, + value: self.note.value, + g_d: self.note.g_d, + pk_d: self.note.pk_d, + asset_type: self.note.asset_type, + }; + let encryptor = sapling_note_encryption::

(self.ovk, note, self.to, self.memo); let (zkproof, cv) = prover.output_proof( ctx, *encryptor.esk(), self.to, - self.note.rcm(), + note.rcm(), self.note.asset_type, self.note.value, + rcv, ); - let cmu = self.note.cmu(); + let cmu = note.cmu(); let enc_ciphertext = encryptor.encrypt_note_plaintext(); let out_ciphertext = encryptor.encrypt_outgoing_plaintext(&cv, &cmu, rng); @@ -449,9 +552,8 @@ impl SaplingBuilder

{ /// /// 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( + pub fn add_spend( &mut self, - mut rng: R, extsk: ExtendedSpendingKey, diversifier: Diversifier, note: Note, @@ -468,8 +570,6 @@ impl SaplingBuilder

{ self.spend_anchor = Some(merkle_path.root(node).into()) } - let alpha = jubjub::Fr::random(&mut rng); - self.value_balance += ValueSum::from_pair(note.asset_type, note.value.into()) .map_err(|_| Error::InvalidAmount)?; @@ -477,7 +577,6 @@ impl SaplingBuilder

{ extsk, diversifier, note, - alpha, merkle_path, }); @@ -520,9 +619,8 @@ impl SaplingBuilder

{ /// Adds a Sapling address to send funds to. #[allow(clippy::too_many_arguments)] - pub fn add_output( + pub fn add_output( &mut self, - mut rng: R, ovk: Option, to: PaymentAddress, asset_type: AssetType, @@ -530,9 +628,6 @@ impl SaplingBuilder

{ memo: MemoBytes, ) -> Result<(), Error> { let output = SaplingOutputInfo::new_internal( - &self.params, - &mut rng, - self.target_height, ovk, to, asset_type, @@ -548,11 +643,12 @@ impl SaplingBuilder

{ Ok(()) } - pub fn build( + pub fn build( self, prover: &Pr, ctx: &mut Pr::SaplingProvingContext, mut rng: R, + mrng: &mut S, target_height: BlockHeight, progress_notifier: Option<&Sender>, ) -> Result>, Error> { @@ -616,11 +712,12 @@ impl SaplingBuilder

{ proof_generation_key, spend.diversifier, spend.note.rseed, - spend.alpha, + mrng.spend_alpha(i), spend.note.asset_type, spend.note.value, anchor, spend.merkle_path.clone(), + mrng.spend_rcv(i), ) .map_err(|_| Error::SpendProof)?; @@ -668,6 +765,7 @@ impl SaplingBuilder

{ convert.value, anchor, convert.merkle_path, + mrng.convert_rcv(i), ) .map_err(|_| Error::ConvertProof)?; @@ -699,11 +797,18 @@ impl SaplingBuilder

{ .into_iter() .enumerate() .map(|(i, output)| { + let rseed = generate_random_rseed_internal( + ¶ms, + target_height, + mrng.rseed_before(i), + mrng.rseed_after(i), + ); + let result = if let Some((pos, output)) = output { // Record the post-randomized output location tx_metadata.output_indices[pos] = i; - output.clone().build::(prover, ctx, &mut rng) + output.clone().build::(prover, ctx, &mut rng, mrng.output_rcv(i), rseed) } else { // This is a dummy output let (dummy_to, dummy_note) = { @@ -729,9 +834,6 @@ impl SaplingBuilder

{ } }; - let rseed = - generate_random_rseed_internal(¶ms, target_height, &mut rng); - ( payment_address, Note { @@ -754,6 +856,7 @@ impl SaplingBuilder

{ dummy_note.rcm(), dummy_note.asset_type, dummy_note.value, + mrng.output_rcv(i), ); let cmu = dummy_note.cmu(); @@ -816,11 +919,12 @@ impl SpendDescription { } impl Bundle { - pub fn apply_signatures( + pub fn apply_signatures( self, prover: &Pr, ctx: &mut Pr::SaplingProvingContext, rng: &mut R, + mrng: &mut S, sighash_bytes: &[u8; 32], ) -> Result<(Bundle, SaplingMetadata), Error> { let binding_sig = prover @@ -832,10 +936,11 @@ impl Bundle { shielded_spends: self .shielded_spends .iter() - .map(|spend| { + .enumerate() + .map(|(i, spend)| { spend.apply_signature(spend_sig_internal( PrivateKey(spend.spend_auth_sig.extsk.expsk.ask), - spend.spend_auth_sig.alpha, + mrng.spend_alpha(i), sighash_bytes, rng, )) @@ -915,7 +1020,6 @@ impl SaplingBuilder { extsk: f.map_key(x.extsk), diversifier: x.diversifier, note: x.note, - alpha: x.alpha, merkle_path: x.merkle_path, }) .collect(), @@ -947,7 +1051,7 @@ pub mod testing { zip32::sapling::testing::arb_extended_spending_key, }; - use super::SaplingBuilder; + use super::{SaplingBuilder, RngRandomness}; prop_compose! { fn arb_bundle()(n_notes in 1..30usize)( @@ -965,6 +1069,7 @@ pub mod testing { diversifiers in vec(prop::array::uniform11(any::()).prop_map(Diversifier), n_notes), target_height in arb_branch_id().prop_flat_map(|b| arb_height(b, &TEST_NETWORK)), rng_seed in prop::array::uniform32(any::()), + mrng_seed in prop::array::uniform32(any::()), fake_sighash_bytes in prop::array::uniform32(any::()), ) -> Bundle { let mut builder = SaplingBuilder::new(TEST_NETWORK, target_height.unwrap()); @@ -972,7 +1077,6 @@ pub mod testing { for ((note, path), diversifier) in spendable_notes.into_iter().zip(commitment_trees.into_iter()).zip(diversifiers.into_iter()) { builder.add_spend( - &mut rng, extsk, diversifier, note, @@ -981,19 +1085,22 @@ pub mod testing { } let prover = MockTxProver; + let mut mrng = RngRandomness::new(StdRng::from_seed(mrng_seed)); let bundle = builder.build( &prover, &mut (), &mut rng, + &mut mrng, target_height.unwrap(), - None + None, ).unwrap().unwrap(); let (bundle, _) = bundle.apply_signatures( &prover, &mut (), &mut rng, + &mut mrng, &fake_sighash_bytes, ).unwrap(); diff --git a/masp_proofs/src/prover.rs b/masp_proofs/src/prover.rs index 7f7cba20..adcb7258 100644 --- a/masp_proofs/src/prover.rs +++ b/masp_proofs/src/prover.rs @@ -171,6 +171,7 @@ impl TxProver for LocalTxProver { value: u64, anchor: bls12_381::Scalar, merkle_path: MerklePath, + rcv: jubjub::Fr, ) -> Result<([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint, PublicKey), ()> { let (proof, cv, rk) = ctx.spend_proof( proof_generation_key, @@ -183,6 +184,7 @@ impl TxProver for LocalTxProver { merkle_path, &self.spend_params, &self.spend_vk, + rcv, )?; let mut zkproof = [0u8; GROTH_PROOF_SIZE]; @@ -201,6 +203,7 @@ impl TxProver for LocalTxProver { rcm: jubjub::Fr, asset_type: AssetType, value: u64, + rcv: jubjub::Fr, ) -> ([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint) { let (proof, cv) = ctx.output_proof( esk, @@ -209,6 +212,7 @@ impl TxProver for LocalTxProver { asset_type, value, &self.output_params, + rcv, ); let mut zkproof = [0u8; GROTH_PROOF_SIZE]; @@ -226,6 +230,7 @@ impl TxProver for LocalTxProver { value: u64, anchor: bls12_381::Scalar, merkle_path: MerklePath, + rcv: jubjub::Fr, ) -> Result<([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint), ()> { let (proof, cv) = ctx.convert_proof( allowed_conversion, @@ -234,6 +239,7 @@ impl TxProver for LocalTxProver { merkle_path, &self.convert_params, &self.convert_vk, + rcv, )?; let mut zkproof = [0u8; GROTH_PROOF_SIZE]; diff --git a/masp_proofs/src/sapling/prover.rs b/masp_proofs/src/sapling/prover.rs index 8c13348a..fac2e6d3 100644 --- a/masp_proofs/src/sapling/prover.rs +++ b/masp_proofs/src/sapling/prover.rs @@ -3,7 +3,7 @@ use bellman::{ groth16::{create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof}, }; use bls12_381::Bls12; -use group::{ff::Field, Curve, GroupEncoding}; +use group::{Curve, GroupEncoding}; use masp_primitives::{ asset_type::AssetType, constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR}, @@ -60,13 +60,11 @@ impl SaplingProvingContext { merkle_path: MerklePath, proving_key: &Parameters, verifying_key: &PreparedVerifyingKey, + rcv: jubjub::Fr, ) -> Result<(Proof, jubjub::ExtendedPoint, PublicKey), ()> { // Initialize secure RNG let mut rng = OsRng; - // We create the randomness of the value commitment - let rcv = jubjub::Fr::random(&mut rng); - // Accumulate the value commitment randomness in the context { let mut tmp = rcv; @@ -169,15 +167,11 @@ impl SaplingProvingContext { asset_type: AssetType, value: u64, proving_key: &Parameters, + rcv: jubjub::Fr, ) -> (Proof, jubjub::ExtendedPoint) { // Initialize secure RNG let mut rng = OsRng; - // We construct ephemeral randomness for the value commitment. This - // randomness is not given back to the caller, but the synthetic - // blinding factor `bsk` is accumulated in the context. - let rcv = jubjub::Fr::random(&mut rng); - // Accumulate the value commitment randomness in the context { let mut tmp = rcv.neg(); // Outputs subtract from the total. @@ -223,13 +217,11 @@ impl SaplingProvingContext { merkle_path: MerklePath, proving_key: &Parameters, verifying_key: &PreparedVerifyingKey, + rcv: jubjub::Fr, ) -> Result<(Proof, jubjub::ExtendedPoint), ()> { // Initialize secure RNG let mut rng = OsRng; - // We create the randomness of the value commitment - let rcv = jubjub::Fr::random(&mut rng); - // Accumulate the value commitment randomness in the context { let mut tmp = rcv; From f39e96560fea226c289a79c250468789fa802ce6 Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Fri, 26 Apr 2024 10:17:32 +0200 Subject: [PATCH 2/3] Added more parameters to transaction builders. --- masp_primitives/src/transaction/builder.rs | 86 ++++----- .../transaction/components/sapling/builder.rs | 168 ++++++++++++------ 2 files changed, 148 insertions(+), 106 deletions(-) diff --git a/masp_primitives/src/transaction/builder.rs b/masp_primitives/src/transaction/builder.rs index aa97d57f..469dfc66 100644 --- a/masp_primitives/src/transaction/builder.rs +++ b/masp_primitives/src/transaction/builder.rs @@ -6,7 +6,7 @@ use std::sync::mpsc::Sender; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use rand::{rngs::OsRng, CryptoRng, RngCore}; +use rand::{CryptoRng, RngCore}; use crate::{ asset_type::AssetType, @@ -21,7 +21,7 @@ use crate::{ amount::{BalanceError, I128Sum, U64Sum, ValueSum, MAX_MONEY}, sapling::{ self, - builder::{Randomness, SaplingBuilder, SaplingMetadata}, + builder::{BuildParams, SaplingBuilder, SaplingMetadata}, }, transparent::{self, builder::TransparentBuilder}, }, @@ -116,9 +116,8 @@ impl Progress { /// Generates a [`Transaction`] from its inputs and outputs. #[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema)] -pub struct Builder> { +pub struct Builder> { params: P, - rng: RN, target_height: BlockHeight, expiry_height: BlockHeight, transparent_builder: TransparentBuilder, @@ -127,7 +126,7 @@ pub struct Builder progress_notifier: Option, } -impl Builder { +impl Builder { /// Returns the network parameters that the builder has been configured for. pub fn params(&self) -> &P { &self.params @@ -169,7 +168,7 @@ impl Builder { } } -impl Builder { +impl Builder

{ /// 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. /// @@ -178,32 +177,18 @@ impl Builder { /// The expiry height will be set to the given height plus the default transaction /// expiry delta (20 blocks). pub fn new(params: P, target_height: BlockHeight) -> Self { - Builder::new_with_rng(params, target_height, OsRng) + Self::new_internal(params, target_height) } } -impl Builder { - /// Creates a new `Builder` targeted for inclusion in the block with the given height - /// and randomness source, using default values for general transaction fields. - /// - /// # Default values - /// - /// The expiry height will be set to the given height plus the default transaction - /// expiry delta (20 blocks). - pub fn new_with_rng(params: P, target_height: BlockHeight, rng: R) -> Builder { - Self::new_internal(params, rng, target_height) - } -} - -impl Builder { +impl Builder

{ /// 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, rng: R, target_height: BlockHeight) -> Builder { + fn new_internal(params: P, target_height: BlockHeight) -> Builder

{ Builder { params: params.clone(), - rng, target_height, expiry_height: target_height + DEFAULT_TX_EXPIRY_DELTA, transparent_builder: TransparentBuilder::empty(), @@ -309,7 +294,8 @@ impl Builder { self, prover: &impl TxProver, fee_rule: &FR, - mrng: &mut impl Randomness, + rng: &mut (impl CryptoRng + RngCore), + mrng: &mut impl BuildParams, ) -> Result<(Transaction, SaplingMetadata), Error> { let fee = fee_rule .fee_required( @@ -320,14 +306,15 @@ impl Builder { self.sapling_builder.outputs().len(), ) .map_err(Error::Fee)?; - self.build_internal(prover, fee, mrng) + self.build_internal(prover, fee, rng, mrng) } fn build_internal( self, prover: &impl TxProver, fee: U64Sum, - mrng: &mut impl Randomness, + rng: &mut (impl CryptoRng + RngCore), + mrng: &mut impl BuildParams, ) -> Result<(Transaction, SaplingMetadata), Error> { let consensus_branch_id = BranchId::for_height(&self.params, self.target_height); @@ -347,14 +334,13 @@ impl Builder { let transparent_bundle = self.transparent_builder.build(); - let mut rng = self.rng; let mut ctx = prover.new_sapling_proving_context(); let sapling_bundle = self .sapling_builder .build( prover, &mut ctx, - &mut rng, + rng, mrng, self.target_height, self.progress_notifier.as_ref(), @@ -389,7 +375,7 @@ impl Builder { let (sapling_bundle, tx_metadata) = match unauthed_tx .sapling_bundle .map(|b| { - b.apply_signatures(prover, &mut ctx, &mut rng, mrng, shielded_sig_commitment.as_ref()) + b.apply_signatures(prover, &mut ctx, rng, mrng, shielded_sig_commitment.as_ref()) }) .transpose() .map_err(Error::SaplingBuild)? @@ -413,21 +399,19 @@ impl Builder { } } -pub trait MapBuilder: +pub trait MapBuilder: sapling::builder::MapBuilder { - fn map_rng(&self, s: R1) -> R2; fn map_notifier(&self, s: N1) -> N2; } -impl Builder { - pub fn map_builder>( +impl Builder { + pub fn map_builder>( self, f: F, - ) -> Builder { - Builder:: { + ) -> Builder { + Builder:: { params: f.map_params(self.params), - rng: f.map_rng(self.rng), target_height: self.target_height, expiry_height: self.expiry_height, transparent_builder: self.transparent_builder, @@ -439,17 +423,17 @@ impl Builder { #[cfg(any(test, feature = "test-dependencies"))] mod testing { - use rand::RngCore; + use rand::{CryptoRng, RngCore}; use std::convert::Infallible; use super::{Builder, Error, SaplingMetadata}; use crate::{ consensus::{self, BlockHeight}, sapling::prover::mock::MockTxProver, - transaction::{builder::Randomness, fees::fixed, Transaction}, + transaction::{builder::BuildParams, fees::fixed, Transaction}, }; - impl Builder { + impl Builder

{ /// Creates a new `Builder` targeted for inclusion in the block with the given height /// and randomness source, using default values for general transaction fields. /// @@ -459,12 +443,16 @@ mod testing { /// expiry delta (20 blocks). /// /// WARNING: DO NOT USE IN PRODUCTION - pub fn test_only_new_with_rng(params: P, height: BlockHeight, rng: R) -> Builder { - Self::new_internal(params, rng, height) + pub fn test_only_new_with_rng(params: P, height: BlockHeight) -> Builder

{ + Self::new_internal(params, height) } - pub fn mock_build(self, mrng: &mut impl Randomness) -> Result<(Transaction, SaplingMetadata), Error> { - self.build(&MockTxProver, &fixed::FeeRule::standard(), mrng) + pub fn mock_build( + self, + rng: &mut (impl CryptoRng + RngCore), + mrng: &mut impl BuildParams, + ) -> Result<(Transaction, SaplingMetadata), Error> { + self.build(&MockTxProver, &fixed::FeeRule::standard(), rng, mrng) } } } @@ -561,7 +549,7 @@ mod tests { // Expect a binding signature error, because our inputs aren't valid, but this shows // that a binding signature was attempted assert_eq!( - builder.mock_build(&mut build_s::RngRandomness::new(OsRng)), + builder.mock_build(&mut OsRng, &mut build_s::RngRandomness::new(OsRng)), Err(Error::SaplingBuild(build_s::Error::BindingSig)) ); } @@ -582,7 +570,7 @@ mod tests { { let builder = Builder::new(TEST_NETWORK, tx_height); assert_eq!( - builder.mock_build(&mut build_s::RngRandomness::new(OsRng)), + builder.mock_build(&mut OsRng, &mut build_s::RngRandomness::new(OsRng)), Err(Error::InsufficientFunds(I128Sum::from_sum( DEFAULT_FEE.clone() ))) @@ -601,7 +589,7 @@ mod tests { .add_sapling_output(ovk, to, zec(), 50000, MemoBytes::empty()) .unwrap(); assert_eq!( - builder.mock_build(&mut build_s::RngRandomness::new(OsRng)), + builder.mock_build(&mut OsRng, &mut build_s::RngRandomness::new(OsRng)), Err(Error::InsufficientFunds( I128Sum::from_pair(zec(), 50000).unwrap() + &I128Sum::from_sum(DEFAULT_FEE.clone()) @@ -617,7 +605,7 @@ mod tests { .add_transparent_output(&transparent_address, zec(), 50000) .unwrap(); assert_eq!( - builder.mock_build(&mut build_s::RngRandomness::new(OsRng)), + builder.mock_build(&mut OsRng, &mut build_s::RngRandomness::new(OsRng)), Err(Error::InsufficientFunds( I128Sum::from_pair(zec(), 50000).unwrap() + &I128Sum::from_sum(DEFAULT_FEE.clone()) @@ -651,7 +639,7 @@ mod tests { .add_transparent_output(&transparent_address, zec(), 20000) .unwrap(); assert_eq!( - builder.mock_build(&mut build_s::RngRandomness::new(OsRng)), + builder.mock_build(&mut OsRng, &mut build_s::RngRandomness::new(OsRng)), Err(Error::InsufficientFunds( ValueSum::from_pair(zec(), 1).unwrap() )) @@ -686,7 +674,7 @@ mod tests { .add_transparent_output(&transparent_address, zec(), 20000) .unwrap(); assert_eq!( - builder.mock_build(&mut build_s::RngRandomness::new(OsRng)), + builder.mock_build(&mut OsRng, &mut build_s::RngRandomness::new(OsRng)), Err(Error::SaplingBuild(build_s::Error::BindingSig)) ) } diff --git a/masp_primitives/src/transaction/components/sapling/builder.rs b/masp_primitives/src/transaction/components/sapling/builder.rs index 123da8fb..a0a750a1 100644 --- a/masp_primitives/src/transaction/components/sapling/builder.rs +++ b/masp_primitives/src/transaction/components/sapling/builder.rs @@ -5,7 +5,7 @@ use std::sync::mpsc::Sender; use ff::Field; use group::GroupEncoding; -use rand::{seq::SliceRandom, RngCore}; +use rand::{seq::SliceRandom, CryptoRng, RngCore}; use crate::{ asset_type::AssetType, @@ -20,7 +20,7 @@ use crate::{ redjubjub::{PrivateKey, Signature}, spend_sig_internal, util::generate_random_rseed_internal, - Diversifier, Node, Note, PaymentAddress, Rseed, + Diversifier, Node, Note, PaymentAddress, ProofGenerationKey, Rseed, }, transaction::{ builder::Progress, @@ -42,86 +42,142 @@ use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use std::collections::BTreeMap; use std::io::Write; -pub trait Randomness { +pub trait BuildParams { /// Get the commitment value randomness for the ith spend description fn spend_rcv(&mut self, i: usize) -> jubjub::Fr; + /// Get the spend authorization randomizer for the ith spend description + fn spend_alpha(&mut self, i: usize) -> jubjub::Fr; + /// Get the authorization signature for the ith spend description + fn auth_sig(&mut self, i: usize) -> Option; + /// The proof generation key for the ith spend description + fn proof_generation_key(&mut self, i: usize) -> Option; /// Get the commitment value randomness for the ith convert description fn convert_rcv(&mut self, i: usize) -> jubjub::Fr; /// Get the commitment value randomness for the ith output description fn output_rcv(&mut self, i: usize) -> jubjub::Fr; - /// Get the spend authorization randomizer for the ith spend description - fn spend_alpha(&mut self, i: usize) -> jubjub::Fr; /// Get the Rseed::BeforeZip212 for the ith spend description - fn rseed_before(&mut self, i: usize) -> jubjub::Fr; + fn output_rcm(&mut self, i: usize) -> jubjub::Fr; /// Get the Rseed::AfterZip212 for the ith spend description - fn rseed_after(&mut self, i: usize) -> [u8; 32]; + fn output_rseed(&mut self, i: usize) -> [u8; 32]; +} + +/// Parameters that go into constructing a spend description +pub struct SpendBuildParams { + /// The commitment value randomness + rcv: jubjub::Fr, + /// The spend authorization randomizer + alpha: jubjub::Fr, + /// The authorization signature + auth_sig: Option, + /// The proof generation key + proof_generation_key: Option, +} + +/// Parameters that go into constructing an output description +pub struct ConvertBuildParams { + /// The commitment value randomness + rcv: jubjub::Fr, +} + +/// Parameters that go into constructing an output description +pub struct OutputBuildParams { + /// The commitment value randomness + rcv: jubjub::Fr, + /// The note rcm value + rcm: jubjub::Fr, + /// The note rseed value + rseed: [u8; 32], } /// Pre-generated random parameters for MASPtTransactions -pub struct StoredRandomness { - spend_rcvs: Vec, - convert_rcvs: Vec, - output_rcvs: Vec, - spend_alphas: Vec, - rseed_before: Vec, - rseed_after: Vec<[u8; 32]>, +pub struct StoredBuildParams { + /// The parameters required to construct spend descriptions + spend_params: Vec, + /// The parameters required to construct convert descriptions + convert_params: Vec, + /// The parameters required to construct output descriptions + output_params: Vec, } -impl Randomness for StoredRandomness { +impl BuildParams for StoredBuildParams { fn spend_rcv(&mut self, i: usize) -> jubjub::Fr { - self.spend_rcvs[i] + self.spend_params[i].rcv + } + + fn spend_alpha(&mut self, i: usize) -> jubjub::Fr { + self.spend_params[i].alpha + } + + fn auth_sig(&mut self, i: usize) -> Option { + self.spend_params[i].auth_sig + } + + fn proof_generation_key(&mut self, i: usize) -> Option { + self.spend_params[i].proof_generation_key.clone() } fn convert_rcv(&mut self, i: usize) -> jubjub::Fr { - self.convert_rcvs[i] + self.convert_params[i].rcv } fn output_rcv(&mut self, i: usize) -> jubjub::Fr { - self.output_rcvs[i] + self.output_params[i].rcv } - fn spend_alpha(&mut self, i: usize) -> jubjub::Fr { - self.spend_alphas[i] + fn output_rcm(&mut self, i: usize) -> jubjub::Fr { + self.output_params[i].rcm } - fn rseed_before(&mut self, i: usize) -> jubjub::Fr { - self.rseed_before[i] - } - - fn rseed_after(&mut self, i: usize) -> [u8; 32] { - self.rseed_after[i] + fn output_rseed(&mut self, i: usize) -> [u8; 32] { + self.output_params[i].rseed } } /// Lazily generated random parameters for MASP transactions -pub struct RngRandomness { +pub struct RngBuildParams { rng: R, spend_rcvs: BTreeMap, + spend_alphas: BTreeMap, + auth_sigs: BTreeMap, + proof_generation_key: BTreeMap, convert_rcvs: BTreeMap, output_rcvs: BTreeMap, - spend_alphas: BTreeMap, - rseed_before: BTreeMap, - rseed_after: BTreeMap, + output_rcms: BTreeMap, + output_rseeds: BTreeMap, } -impl RngRandomness { +impl RngBuildParams { pub fn new(rng: R) -> Self { Self { rng, spend_rcvs: BTreeMap::new(), + spend_alphas: BTreeMap::new(), + auth_sigs: BTreeMap::new(), + proof_generation_key: BTreeMap::new(), convert_rcvs: BTreeMap::new(), output_rcvs: BTreeMap::new(), - spend_alphas: BTreeMap::new(), - rseed_before: BTreeMap::new(), - rseed_after: BTreeMap::new(), + output_rcms: BTreeMap::new(), + output_rseeds: BTreeMap::new(), } } } -impl Randomness for RngRandomness { +impl BuildParams for RngBuildParams { fn spend_rcv(&mut self, i: usize) -> jubjub::Fr { *self.spend_rcvs.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) } + + fn spend_alpha(&mut self, i: usize) -> jubjub::Fr { + *self.spend_alphas.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) + } + + fn auth_sig(&mut self, i: usize) -> Option { + self.auth_sigs.get(&i).cloned() + } + + fn proof_generation_key(&mut self, i: usize) -> Option { + self.proof_generation_key.get(&i).cloned() + } fn convert_rcv(&mut self, i: usize) -> jubjub::Fr { *self.convert_rcvs.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) @@ -131,16 +187,12 @@ impl Randomness for RngRandomness { *self.output_rcvs.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) } - fn spend_alpha(&mut self, i: usize) -> jubjub::Fr { - *self.spend_alphas.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) - } - - fn rseed_before(&mut self, i: usize) -> jubjub::Fr { - *self.rseed_before.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) + fn output_rcm(&mut self, i: usize) -> jubjub::Fr { + *self.output_rcms.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) } - fn rseed_after(&mut self, i: usize) -> [u8; 32] { - *self.rseed_after.entry(i).or_insert_with(|| { + fn output_rseed(&mut self, i: usize) -> [u8; 32] { + *self.output_rseeds.entry(i).or_insert_with(|| { let mut buffer = [0u8; 32]; self.rng.fill_bytes(&mut buffer); buffer @@ -643,12 +695,12 @@ impl SaplingBuilder

{ Ok(()) } - pub fn build( + pub fn build( self, prover: &Pr, ctx: &mut Pr::SaplingProvingContext, - mut rng: R, - mrng: &mut S, + rng: &mut (impl CryptoRng + RngCore), + mrng: &mut impl BuildParams, target_height: BlockHeight, progress_notifier: Option<&Sender>, ) -> Result>, Error> { @@ -681,9 +733,9 @@ impl SaplingBuilder

{ } // Randomize order of inputs and outputs - indexed_spends.shuffle(&mut rng); - indexed_converts.shuffle(&mut rng); - indexed_outputs.shuffle(&mut rng); + indexed_spends.shuffle(rng); + indexed_converts.shuffle(rng); + indexed_outputs.shuffle(rng); // Keep track of the total number of steps computed let total_progress = indexed_spends.len() as u32 + indexed_outputs.len() as u32; @@ -800,15 +852,15 @@ impl SaplingBuilder

{ let rseed = generate_random_rseed_internal( ¶ms, target_height, - mrng.rseed_before(i), - mrng.rseed_after(i), + mrng.output_rcm(i), + mrng.output_rseed(i), ); let result = if let Some((pos, output)) = output { // Record the post-randomized output location tx_metadata.output_indices[pos] = i; - output.clone().build::(prover, ctx, &mut rng, mrng.output_rcv(i), rseed) + output.clone().build::(prover, ctx, rng, mrng.output_rcv(i), rseed) } else { // This is a dummy output let (dummy_to, dummy_note) = { @@ -827,7 +879,9 @@ impl SaplingBuilder

{ (diversifier, g_d) }; let (pk_d, payment_address) = loop { - let dummy_ivk = jubjub::Fr::random(&mut rng); + let mut buf = [0; 64]; + rng.fill_bytes(&mut buf); + let dummy_ivk = jubjub::Fr::from_bytes_wide(&buf); let pk_d = g_d * dummy_ivk; if let Some(addr) = PaymentAddress::from_parts(diversifier, pk_d) { break (pk_d, addr); @@ -846,7 +900,7 @@ impl SaplingBuilder

{ ) }; - let esk = dummy_note.generate_or_derive_esk_internal(&mut rng); + let esk = dummy_note.generate_or_derive_esk_internal(rng); let epk = dummy_note.g_d * esk; let (zkproof, cv) = prover.output_proof( @@ -919,7 +973,7 @@ impl SpendDescription { } impl Bundle { - pub fn apply_signatures( + pub fn apply_signatures( self, prover: &Pr, ctx: &mut Pr::SaplingProvingContext, @@ -1051,7 +1105,7 @@ pub mod testing { zip32::sapling::testing::arb_extended_spending_key, }; - use super::{SaplingBuilder, RngRandomness}; + use super::{SaplingBuilder, RngBuildParams}; prop_compose! { fn arb_bundle()(n_notes in 1..30usize)( @@ -1085,7 +1139,7 @@ pub mod testing { } let prover = MockTxProver; - let mut mrng = RngRandomness::new(StdRng::from_seed(mrng_seed)); + let mut mrng = RngBuildParams::new(StdRng::from_seed(mrng_seed)); let bundle = builder.build( &prover, From 5525d73c9cdabff055fea47e292c4921c58a32df Mon Sep 17 00:00:00 2001 From: Murisi Tarusenga Date: Fri, 26 Apr 2024 11:00:34 +0200 Subject: [PATCH 3/3] Group the parameters for the different descriptors. --- masp_primitives/src/sapling.rs | 2 +- masp_primitives/src/sapling/prover.rs | 11 +- masp_primitives/src/transaction/builder.rs | 36 +++-- .../transaction/components/sapling/builder.rs | 148 ++++++++++-------- masp_proofs/src/sapling/prover.rs | 2 + 5 files changed, 113 insertions(+), 86 deletions(-) diff --git a/masp_primitives/src/sapling.rs b/masp_primitives/src/sapling.rs index d26834ae..0cc757b9 100644 --- a/masp_primitives/src/sapling.rs +++ b/masp_primitives/src/sapling.rs @@ -845,7 +845,7 @@ impl BorshSchema for Note { } fn declaration() -> Declaration { - "Note".into() + format!("Note<{}>", T::declaration()) } } diff --git a/masp_primitives/src/sapling/prover.rs b/masp_primitives/src/sapling/prover.rs index 015b4281..e2af4168 100644 --- a/masp_primitives/src/sapling/prover.rs +++ b/masp_primitives/src/sapling/prover.rs @@ -45,6 +45,7 @@ pub trait TxProver { /// while accumulating its value commitment randomness inside the context for later /// use. /// + #[allow(clippy::too_many_arguments)] fn output_proof( &self, ctx: &mut Self::SaplingProvingContext, @@ -117,10 +118,7 @@ pub mod mock { _merkle_path: MerklePath, rcv: jubjub::Fr, ) -> Result<([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint, PublicKey), ()> { - let cv = asset_type - .value_commitment(value, rcv) - .commitment() - .into(); + let cv = asset_type.value_commitment(value, rcv).commitment().into(); let rk = PublicKey(proof_generation_key.ak.into()).randomize(ar, SPENDING_KEY_GENERATOR); @@ -138,10 +136,7 @@ pub mod mock { value: u64, rcv: jubjub::Fr, ) -> ([u8; GROTH_PROOF_SIZE], jubjub::ExtendedPoint) { - let cv = asset_type - .value_commitment(value, rcv) - .commitment() - .into(); + let cv = asset_type.value_commitment(value, rcv).commitment().into(); ([0u8; GROTH_PROOF_SIZE], cv) } diff --git a/masp_primitives/src/transaction/builder.rs b/masp_primitives/src/transaction/builder.rs index 469dfc66..a398ea71 100644 --- a/masp_primitives/src/transaction/builder.rs +++ b/masp_primitives/src/transaction/builder.rs @@ -295,7 +295,7 @@ impl Builder

{ prover: &impl TxProver, fee_rule: &FR, rng: &mut (impl CryptoRng + RngCore), - mrng: &mut impl BuildParams, + bparams: &mut impl BuildParams, ) -> Result<(Transaction, SaplingMetadata), Error> { let fee = fee_rule .fee_required( @@ -306,7 +306,7 @@ impl Builder

{ self.sapling_builder.outputs().len(), ) .map_err(Error::Fee)?; - self.build_internal(prover, fee, rng, mrng) + self.build_internal(prover, fee, rng, bparams) } fn build_internal( @@ -314,7 +314,7 @@ impl Builder

{ prover: &impl TxProver, fee: U64Sum, rng: &mut (impl CryptoRng + RngCore), - mrng: &mut impl BuildParams, + bparams: &mut impl BuildParams, ) -> Result<(Transaction, SaplingMetadata), Error> { let consensus_branch_id = BranchId::for_height(&self.params, self.target_height); @@ -341,7 +341,7 @@ impl Builder

{ prover, &mut ctx, rng, - mrng, + bparams, self.target_height, self.progress_notifier.as_ref(), ) @@ -375,7 +375,13 @@ impl Builder

{ let (sapling_bundle, tx_metadata) = match unauthed_tx .sapling_bundle .map(|b| { - b.apply_signatures(prover, &mut ctx, rng, mrng, shielded_sig_commitment.as_ref()) + b.apply_signatures( + prover, + &mut ctx, + rng, + bparams, + shielded_sig_commitment.as_ref(), + ) }) .transpose() .map_err(Error::SaplingBuild)? @@ -399,9 +405,7 @@ impl Builder

{ } } -pub trait MapBuilder: - sapling::builder::MapBuilder -{ +pub trait MapBuilder: sapling::builder::MapBuilder { fn map_notifier(&self, s: N1) -> N2; } @@ -450,9 +454,9 @@ mod testing { pub fn mock_build( self, rng: &mut (impl CryptoRng + RngCore), - mrng: &mut impl BuildParams, + bparams: &mut impl BuildParams, ) -> Result<(Transaction, SaplingMetadata), Error> { - self.build(&MockTxProver, &fixed::FeeRule::standard(), rng, mrng) + self.build(&MockTxProver, &fixed::FeeRule::standard(), rng, bparams) } } } @@ -549,7 +553,7 @@ mod tests { // Expect a binding signature error, because our inputs aren't valid, but this shows // that a binding signature was attempted assert_eq!( - builder.mock_build(&mut OsRng, &mut build_s::RngRandomness::new(OsRng)), + builder.mock_build(&mut OsRng, &mut build_s::RngBuildParams::new(OsRng)), Err(Error::SaplingBuild(build_s::Error::BindingSig)) ); } @@ -570,7 +574,7 @@ mod tests { { let builder = Builder::new(TEST_NETWORK, tx_height); assert_eq!( - builder.mock_build(&mut OsRng, &mut build_s::RngRandomness::new(OsRng)), + builder.mock_build(&mut OsRng, &mut build_s::RngBuildParams::new(OsRng)), Err(Error::InsufficientFunds(I128Sum::from_sum( DEFAULT_FEE.clone() ))) @@ -589,7 +593,7 @@ mod tests { .add_sapling_output(ovk, to, zec(), 50000, MemoBytes::empty()) .unwrap(); assert_eq!( - builder.mock_build(&mut OsRng, &mut build_s::RngRandomness::new(OsRng)), + builder.mock_build(&mut OsRng, &mut build_s::RngBuildParams::new(OsRng)), Err(Error::InsufficientFunds( I128Sum::from_pair(zec(), 50000).unwrap() + &I128Sum::from_sum(DEFAULT_FEE.clone()) @@ -605,7 +609,7 @@ mod tests { .add_transparent_output(&transparent_address, zec(), 50000) .unwrap(); assert_eq!( - builder.mock_build(&mut OsRng, &mut build_s::RngRandomness::new(OsRng)), + builder.mock_build(&mut OsRng, &mut build_s::RngBuildParams::new(OsRng)), Err(Error::InsufficientFunds( I128Sum::from_pair(zec(), 50000).unwrap() + &I128Sum::from_sum(DEFAULT_FEE.clone()) @@ -639,7 +643,7 @@ mod tests { .add_transparent_output(&transparent_address, zec(), 20000) .unwrap(); assert_eq!( - builder.mock_build(&mut OsRng, &mut build_s::RngRandomness::new(OsRng)), + builder.mock_build(&mut OsRng, &mut build_s::RngBuildParams::new(OsRng)), Err(Error::InsufficientFunds( ValueSum::from_pair(zec(), 1).unwrap() )) @@ -674,7 +678,7 @@ mod tests { .add_transparent_output(&transparent_address, zec(), 20000) .unwrap(); assert_eq!( - builder.mock_build(&mut OsRng, &mut build_s::RngRandomness::new(OsRng)), + builder.mock_build(&mut OsRng, &mut build_s::RngBuildParams::new(OsRng)), Err(Error::SaplingBuild(build_s::Error::BindingSig)) ) } diff --git a/masp_primitives/src/transaction/components/sapling/builder.rs b/masp_primitives/src/transaction/components/sapling/builder.rs index a0a750a1..00f73c85 100644 --- a/masp_primitives/src/transaction/components/sapling/builder.rs +++ b/masp_primitives/src/transaction/components/sapling/builder.rs @@ -42,6 +42,7 @@ use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use std::collections::BTreeMap; use std::io::Write; +/// A subset of the parameters necessary to build a transaction pub trait BuildParams { /// Get the commitment value randomness for the ith spend description fn spend_rcv(&mut self, i: usize) -> jubjub::Fr; @@ -55,13 +56,14 @@ pub trait BuildParams { fn convert_rcv(&mut self, i: usize) -> jubjub::Fr; /// Get the commitment value randomness for the ith output description fn output_rcv(&mut self, i: usize) -> jubjub::Fr; - /// Get the Rseed::BeforeZip212 for the ith spend description + /// Get the note RCM for the ith output description fn output_rcm(&mut self, i: usize) -> jubjub::Fr; - /// Get the Rseed::AfterZip212 for the ith spend description + /// Get the random seed for the ith output description fn output_rseed(&mut self, i: usize) -> [u8; 32]; } /// Parameters that go into constructing a spend description +#[derive(Clone)] pub struct SpendBuildParams { /// The commitment value randomness rcv: jubjub::Fr, @@ -74,29 +76,32 @@ pub struct SpendBuildParams { } /// Parameters that go into constructing an output description +#[derive(Clone, Copy)] pub struct ConvertBuildParams { /// The commitment value randomness rcv: jubjub::Fr, } /// Parameters that go into constructing an output description +#[derive(Clone, Copy)] pub struct OutputBuildParams { /// The commitment value randomness rcv: jubjub::Fr, /// The note rcm value rcm: jubjub::Fr, - /// The note rseed value + /// The note's random seed rseed: [u8; 32], } /// Pre-generated random parameters for MASPtTransactions +#[derive(Default, Clone)] pub struct StoredBuildParams { /// The parameters required to construct spend descriptions - spend_params: Vec, + pub spend_params: Vec, /// The parameters required to construct convert descriptions - convert_params: Vec, + pub convert_params: Vec, /// The parameters required to construct output descriptions - output_params: Vec, + pub output_params: Vec, } impl BuildParams for StoredBuildParams { @@ -115,11 +120,11 @@ impl BuildParams for StoredBuildParams { fn proof_generation_key(&mut self, i: usize) -> Option { self.spend_params[i].proof_generation_key.clone() } - + fn convert_rcv(&mut self, i: usize) -> jubjub::Fr { self.convert_params[i].rcv } - + fn output_rcv(&mut self, i: usize) -> jubjub::Fr { self.output_params[i].rcv } @@ -135,68 +140,93 @@ impl BuildParams for StoredBuildParams { /// Lazily generated random parameters for MASP transactions pub struct RngBuildParams { + /// The RNG used to generate the build parameters rng: R, - spend_rcvs: BTreeMap, - spend_alphas: BTreeMap, - auth_sigs: BTreeMap, - proof_generation_key: BTreeMap, - convert_rcvs: BTreeMap, - output_rcvs: BTreeMap, - output_rcms: BTreeMap, - output_rseeds: BTreeMap, + /// The parameters required to construct spend descriptions + spends: BTreeMap, + /// The parameters required to construct convert descriptions + converts: BTreeMap, + /// The parameters required to construct output descriptions + outputs: BTreeMap, } impl RngBuildParams { + /// Construct a build parameter generator using the given RNG pub fn new(rng: R) -> Self { Self { rng, - spend_rcvs: BTreeMap::new(), - spend_alphas: BTreeMap::new(), - auth_sigs: BTreeMap::new(), - proof_generation_key: BTreeMap::new(), - convert_rcvs: BTreeMap::new(), - output_rcvs: BTreeMap::new(), - output_rcms: BTreeMap::new(), - output_rseeds: BTreeMap::new(), + spends: BTreeMap::new(), + converts: BTreeMap::new(), + outputs: BTreeMap::new(), } } } +impl RngBuildParams { + /// Get the parameters necessary to build the ith spend description + pub fn spend_params(&mut self, i: usize) -> &SpendBuildParams { + self.spends.entry(i).or_insert_with(|| SpendBuildParams { + rcv: jubjub::Fr::random(&mut self.rng), + alpha: jubjub::Fr::random(&mut self.rng), + auth_sig: None, + proof_generation_key: None, + }) + } + + /// Get the parameters necessary to build the ith convert description + pub fn convert_params(&mut self, i: usize) -> &ConvertBuildParams { + self.converts + .entry(i) + .or_insert_with(|| ConvertBuildParams { + rcv: jubjub::Fr::random(&mut self.rng), + }) + } + + /// Get the parameters necessary to build the ith output description + pub fn output_params(&mut self, i: usize) -> &OutputBuildParams { + self.outputs.entry(i).or_insert_with(|| OutputBuildParams { + rcv: jubjub::Fr::random(&mut self.rng), + rcm: jubjub::Fr::random(&mut self.rng), + rseed: { + let mut buffer = [0u8; 32]; + self.rng.fill_bytes(&mut buffer); + buffer + }, + }) + } +} + impl BuildParams for RngBuildParams { fn spend_rcv(&mut self, i: usize) -> jubjub::Fr { - *self.spend_rcvs.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) + self.spend_params(i).rcv } fn spend_alpha(&mut self, i: usize) -> jubjub::Fr { - *self.spend_alphas.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) + self.spend_params(i).alpha } fn auth_sig(&mut self, i: usize) -> Option { - self.auth_sigs.get(&i).cloned() + self.spend_params(i).auth_sig } fn proof_generation_key(&mut self, i: usize) -> Option { - self.proof_generation_key.get(&i).cloned() + self.spend_params(i).proof_generation_key.clone() } - + fn convert_rcv(&mut self, i: usize) -> jubjub::Fr { - *self.convert_rcvs.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) + self.convert_params(i).rcv } - + fn output_rcv(&mut self, i: usize) -> jubjub::Fr { - *self.output_rcvs.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) + self.output_params(i).rcv } fn output_rcm(&mut self, i: usize) -> jubjub::Fr { - *self.output_rcms.entry(i).or_insert_with(|| jubjub::Fr::random(&mut self.rng)) + self.output_params(i).rcm } fn output_rseed(&mut self, i: usize) -> [u8; 32] { - *self.output_rseeds.entry(i).or_insert_with(|| { - let mut buffer = [0u8; 32]; - self.rng.fill_bytes(&mut buffer); - buffer - }) + self.output_params(i).rseed } } @@ -679,13 +709,7 @@ impl SaplingBuilder

{ value: u64, memo: MemoBytes, ) -> Result<(), Error> { - let output = SaplingOutputInfo::new_internal( - ovk, - to, - asset_type, - value, - memo, - )?; + let output = SaplingOutputInfo::new_internal(ovk, to, asset_type, value, memo)?; self.value_balance -= ValueSum::from_pair(asset_type, value.into()).map_err(|_| Error::InvalidAmount)?; @@ -700,7 +724,7 @@ impl SaplingBuilder

{ prover: &Pr, ctx: &mut Pr::SaplingProvingContext, rng: &mut (impl CryptoRng + RngCore), - mrng: &mut impl BuildParams, + bparams: &mut impl BuildParams, target_height: BlockHeight, progress_notifier: Option<&Sender>, ) -> Result>, Error> { @@ -764,12 +788,12 @@ impl SaplingBuilder

{ proof_generation_key, spend.diversifier, spend.note.rseed, - mrng.spend_alpha(i), + bparams.spend_alpha(i), spend.note.asset_type, spend.note.value, anchor, spend.merkle_path.clone(), - mrng.spend_rcv(i), + bparams.spend_rcv(i), ) .map_err(|_| Error::SpendProof)?; @@ -817,7 +841,7 @@ impl SaplingBuilder

{ convert.value, anchor, convert.merkle_path, - mrng.convert_rcv(i), + bparams.convert_rcv(i), ) .map_err(|_| Error::ConvertProof)?; @@ -852,15 +876,17 @@ impl SaplingBuilder

{ let rseed = generate_random_rseed_internal( ¶ms, target_height, - mrng.output_rcm(i), - mrng.output_rseed(i), + bparams.output_rcm(i), + bparams.output_rseed(i), ); - + let result = if let Some((pos, output)) = output { // Record the post-randomized output location tx_metadata.output_indices[pos] = i; - output.clone().build::(prover, ctx, rng, mrng.output_rcv(i), rseed) + output + .clone() + .build::(prover, ctx, rng, bparams.output_rcv(i), rseed) } else { // This is a dummy output let (dummy_to, dummy_note) = { @@ -910,7 +936,7 @@ impl SaplingBuilder

{ dummy_note.rcm(), dummy_note.asset_type, dummy_note.value, - mrng.output_rcv(i), + bparams.output_rcv(i), ); let cmu = dummy_note.cmu(); @@ -978,7 +1004,7 @@ impl Bundle { prover: &Pr, ctx: &mut Pr::SaplingProvingContext, rng: &mut R, - mrng: &mut S, + bparams: &mut S, sighash_bytes: &[u8; 32], ) -> Result<(Bundle, SaplingMetadata), Error> { let binding_sig = prover @@ -994,7 +1020,7 @@ impl Bundle { .map(|(i, spend)| { spend.apply_signature(spend_sig_internal( PrivateKey(spend.spend_auth_sig.extsk.expsk.ask), - mrng.spend_alpha(i), + bparams.spend_alpha(i), sighash_bytes, rng, )) @@ -1105,7 +1131,7 @@ pub mod testing { zip32::sapling::testing::arb_extended_spending_key, }; - use super::{SaplingBuilder, RngBuildParams}; + use super::{RngBuildParams, SaplingBuilder}; prop_compose! { fn arb_bundle()(n_notes in 1..30usize)( @@ -1123,7 +1149,7 @@ pub mod testing { diversifiers in vec(prop::array::uniform11(any::()).prop_map(Diversifier), n_notes), target_height in arb_branch_id().prop_flat_map(|b| arb_height(b, &TEST_NETWORK)), rng_seed in prop::array::uniform32(any::()), - mrng_seed in prop::array::uniform32(any::()), + bparams_seed in prop::array::uniform32(any::()), fake_sighash_bytes in prop::array::uniform32(any::()), ) -> Bundle { let mut builder = SaplingBuilder::new(TEST_NETWORK, target_height.unwrap()); @@ -1139,13 +1165,13 @@ pub mod testing { } let prover = MockTxProver; - let mut mrng = RngBuildParams::new(StdRng::from_seed(mrng_seed)); + let mut bparams = RngBuildParams::new(StdRng::from_seed(bparams_seed)); let bundle = builder.build( &prover, &mut (), &mut rng, - &mut mrng, + &mut bparams, target_height.unwrap(), None, ).unwrap().unwrap(); @@ -1154,7 +1180,7 @@ pub mod testing { &prover, &mut (), &mut rng, - &mut mrng, + &mut bparams, &fake_sighash_bytes, ).unwrap(); diff --git a/masp_proofs/src/sapling/prover.rs b/masp_proofs/src/sapling/prover.rs index fac2e6d3..bff906b6 100644 --- a/masp_proofs/src/sapling/prover.rs +++ b/masp_proofs/src/sapling/prover.rs @@ -159,6 +159,7 @@ impl SaplingProvingContext { /// Create the value commitment and proof for a Sapling OutputDescription, /// while accumulating its value commitment randomness inside the context /// for later use. + #[allow(clippy::too_many_arguments)] pub fn output_proof( &mut self, esk: jubjub::Fr, @@ -209,6 +210,7 @@ impl SaplingProvingContext { /// Create the value commitment and proof for a ConvertDescription, /// while accumulating its value commitment randomness inside the context /// for later use. + #[allow(clippy::too_many_arguments)] pub fn convert_proof( &mut self, allowed_conversion: AllowedConversion,