From 1d1260394ab325deb1b5479a99f17b9e06270453 Mon Sep 17 00:00:00 2001 From: Lucas Meier Date: Thu, 19 Oct 2023 23:52:46 -0700 Subject: [PATCH] Proof-Setup: Make benchmarks run over all circuits Probably the more useful benchmarks to run --- crates/crypto/proof-setup/Cargo.toml | 2 +- crates/crypto/proof-setup/benches/all.rs | 73 +++++++------------ crates/crypto/proof-setup/src/all.rs | 40 +--------- crates/crypto/proof-setup/src/lib.rs | 1 + .../crypto/proof-setup/src/parallel_utils.rs | 36 +++++++++ .../crypto/proof-setup/src/single/phase1.rs | 66 +++++++++-------- 6 files changed, 101 insertions(+), 117 deletions(-) create mode 100644 crates/crypto/proof-setup/src/parallel_utils.rs diff --git a/crates/crypto/proof-setup/Cargo.toml b/crates/crypto/proof-setup/Cargo.toml index 83fa90a75c..e9c6659d83 100644 --- a/crates/crypto/proof-setup/Cargo.toml +++ b/crates/crypto/proof-setup/Cargo.toml @@ -37,4 +37,4 @@ harness = false [features] default = [] -parallel = ["ark-ec/parallel", "ark-ff/parallel", "ark-groth16/parallel", "decaf377/parallel", "rayon"] +parallel = ["ark-ec/parallel", "ark-ff/parallel", "ark-groth16/parallel", "decaf377/parallel", "rayon", "penumbra-shielded-pool/parallel"] diff --git a/crates/crypto/proof-setup/benches/all.rs b/crates/crypto/proof-setup/benches/all.rs index 66a66aadfc..7326b74dbe 100644 --- a/crates/crypto/proof-setup/benches/all.rs +++ b/crates/crypto/proof-setup/benches/all.rs @@ -1,24 +1,16 @@ -use ark_ec::pairing::Pairing; -use ark_relations::r1cs::ConstraintMatrices; use criterion::{criterion_group, criterion_main, Criterion}; -use decaf377::Bls12_377; -use rand_core::OsRng; - -use penumbra_dex::swap_claim::proof::SwapClaimCircuit; -use penumbra_proof_params::generate_constraint_matrices; -use penumbra_proof_setup::single::{ - circuit_degree, - log::{ContributionHash, Hashable}, - transition, ExtraTransitionInformation, Phase2CRSElements, Phase2Contribution, - Phase2RawContribution, {Phase1CRSElements, Phase1Contribution, Phase1RawContribution}, +use penumbra_proof_setup::all::{ + transition, AllExtraTransitionInformation, Phase1CeremonyCRS, Phase1CeremonyContribution, + Phase1RawCeremonyContribution, Phase2CeremonyCRS, Phase2CeremonyContribution, + Phase2RawCeremonyContribution, }; -fn run_phase1(parent: ContributionHash, old: &Phase1CRSElements) -> Phase1Contribution { - Phase1Contribution::make(&mut OsRng, parent, old) +fn run_phase1(old: &Phase1CeremonyCRS) -> Phase1CeremonyContribution { + Phase1CeremonyContribution::make(old) } -fn check_phase1(contribution: &Phase1RawContribution, parent: &Phase1CRSElements) -> bool { - let validated_contribution = contribution.validate(&mut OsRng); +fn check_phase1(contribution: Phase1RawCeremonyContribution, parent: &Phase1CeremonyCRS) -> bool { + let validated_contribution = contribution.validate(); if validated_contribution.is_none() { return false; } @@ -28,22 +20,21 @@ fn check_phase1(contribution: &Phase1RawContribution, parent: &Phase1CRSElements } fn run_phase_transition( - phase1: &Phase1CRSElements, - circuit: &ConstraintMatrices<::ScalarField>, -) -> anyhow::Result<(ExtraTransitionInformation, Phase2CRSElements)> { - transition(phase1, circuit) + phase1: &Phase1CeremonyCRS, +) -> anyhow::Result<(AllExtraTransitionInformation, Phase2CeremonyCRS)> { + transition(phase1) } -fn run_phase2(parent: ContributionHash, old: &Phase2CRSElements) -> Phase2Contribution { - Phase2Contribution::make(&mut OsRng, parent, old) +fn run_phase2(old: &Phase2CeremonyCRS) -> Phase2CeremonyContribution { + Phase2CeremonyContribution::make(old) } fn check_phase2( - contribution: Phase2RawContribution, - root: &Phase2CRSElements, - parent: &Phase2CRSElements, + contribution: Phase2RawCeremonyContribution, + root: &Phase2CeremonyCRS, + parent: &Phase2CeremonyCRS, ) -> bool { - let validated_contribution = contribution.validate(&mut OsRng, root); + let validated_contribution = contribution.validate(root); if validated_contribution.is_none() { return false; } @@ -53,31 +44,21 @@ fn check_phase2( } fn benchmarks(c: &mut Criterion) { - // Generate contribution for degree = 37,061, which gets rounded up to next power of 2. - // (size of largest proof) - let matrices = generate_constraint_matrices::(); - let d = circuit_degree(&matrices).expect("failed to calculate circuit degree"); - - let root = Phase1CRSElements::root(d); - let root_hash = root.hash(); - - let phase1out = run_phase1(root_hash, &root); - c.bench_function("phase 1", |b| b.iter(|| run_phase1(root_hash, &root))); + let root = Phase1CeremonyCRS::root().unwrap(); + let phase1out = run_phase1(&root); + c.bench_function("phase 1", |b| b.iter(|| run_phase1(&root))); c.bench_function("phase 1 check", |b| { - b.iter(|| check_phase1(&phase1out.clone().into(), &root)) + b.iter(|| check_phase1(phase1out.clone().into(), &root)) }); - let (_, phase2root) = run_phase_transition(&phase1out.new_elements, &matrices) - .expect("failed to perform transition"); - let phase2root_hash = phase2root.hash(); + let (_, phase2root) = + run_phase_transition(&phase1out.new_elements()).expect("failed to perform transition"); c.bench_function("phase transition", |b| { - b.iter(|| run_phase_transition(&phase1out.new_elements, &matrices)) + b.iter(|| run_phase_transition(&phase1out.new_elements())) }); - let phase2out = run_phase2(phase2root_hash, &phase2root); - c.bench_function("phase 2", |b| { - b.iter(|| run_phase2(phase2root_hash, &phase2root)) - }); + let phase2out = run_phase2(&phase2root); + c.bench_function("phase 2", |b| b.iter(|| run_phase2(&phase2root))); c.bench_function("phase 2 check", |b| { b.iter(|| check_phase2(phase2out.clone().into(), &phase2root, &phase2root)) }); @@ -85,7 +66,7 @@ fn benchmarks(c: &mut Criterion) { criterion_group! { name = benches; - config = Criterion::default().sample_size(10); + config = Criterion::default().sample_size(2); targets = benchmarks } criterion_main!(benches); diff --git a/crates/crypto/proof-setup/src/all.rs b/crates/crypto/proof-setup/src/all.rs index 01dea1e522..f802c0c5bf 100644 --- a/crates/crypto/proof-setup/src/all.rs +++ b/crates/crypto/proof-setup/src/all.rs @@ -4,6 +4,7 @@ //! along with the corresponding protobufs. use std::array; +use crate::parallel_utils::{flatten_results, transform, transform_parallel}; use crate::single::{ self, circuit_degree, group::F, log::ContributionHash, DLogProof, ExtraTransitionInformation, LinkingProof, Phase1CRSElements, Phase1Contribution, Phase1RawCRSElements, @@ -42,43 +43,6 @@ fn from_bytes_unchecked(data: &[u8]) -> Result { pub const NUM_CIRCUITS: usize = 7; -fn transform(data: [A; NUM_CIRCUITS], f: impl Fn(A) -> B) -> [B; NUM_CIRCUITS] { - match data.into_iter().map(f).collect::>().try_into() { - Ok(x) => x, - _ => panic!("The size of the iterator should not have changed"), - } -} - -#[cfg(not(feature = "parallel"))] -fn transform_parallel(data: [A; NUM_CIRCUITS], f: impl Fn(A) -> B) -> [B; NUM_CIRCUITS] { - transform(data, f) -} - -#[cfg(feature = "parallel")] -fn transform_parallel( - data: [A; N], - f: impl Fn(A) -> B + Sync + Send, -) -> [B; N] { - use rayon::prelude::*; - let out: Vec = data.into_par_iter().map(f).collect(); - // Note: we do it this way because we don't have a Debug implementation for B - match out.try_into() { - Ok(x) => x, - _ => panic!("The size of the iterator should not have changed"), - } -} - -fn flatten_results(data: [Result; N]) -> Result<[A; N]> { - let mut buf = Vec::with_capacity(N); - for x in data { - buf.push(x?); - } - match buf.try_into() { - Ok(x) => Ok(x), - _ => panic!("The size of the iterator should not have changed"), - } -} - /// Generate all of the circuits as matrices. fn circuits() -> [ConstraintMatrices; NUM_CIRCUITS] { [ @@ -596,7 +560,7 @@ impl Phase1RawCeremonyContribution { /// step you want to do. pub fn validate(self) -> Option { let out = transform_parallel(self.0, |x| { - x.validate(&mut OsRng).ok_or(anyhow!("failed to validate")) + x.validate().ok_or(anyhow!("failed to validate")) }); Some(Phase1CeremonyContribution(flatten_results(out).ok()?)) } diff --git a/crates/crypto/proof-setup/src/lib.rs b/crates/crypto/proof-setup/src/lib.rs index a778ed0249..30bebdba6b 100644 --- a/crates/crypto/proof-setup/src/lib.rs +++ b/crates/crypto/proof-setup/src/lib.rs @@ -1,2 +1,3 @@ pub mod all; +mod parallel_utils; pub mod single; diff --git a/crates/crypto/proof-setup/src/parallel_utils.rs b/crates/crypto/proof-setup/src/parallel_utils.rs new file mode 100644 index 0000000000..96dd9d8361 --- /dev/null +++ b/crates/crypto/proof-setup/src/parallel_utils.rs @@ -0,0 +1,36 @@ +pub fn transform(data: [A; N], f: impl Fn(A) -> B) -> [B; N] { + match data.into_iter().map(f).collect::>().try_into() { + Ok(x) => x, + _ => panic!("The size of the iterator should not have changed"), + } +} + +#[cfg(not(feature = "parallel"))] +pub fn transform_parallel(data: [A; N], f: impl Fn(A) -> B) -> [B; N] { + transform(data, f) +} + +#[cfg(feature = "parallel")] +pub fn transform_parallel( + data: [A; N], + f: impl Fn(A) -> B + Sync + Send, +) -> [B; N] { + use rayon::prelude::*; + let out: Vec = data.into_par_iter().map(f).collect(); + // Note: we do it this way because we don't have a Debug implementation for B + match out.try_into() { + Ok(x) => x, + _ => panic!("The size of the iterator should not have changed"), + } +} + +pub fn flatten_results(data: [Result; N]) -> Result<[A; N], E> { + let mut buf = Vec::with_capacity(N); + for x in data { + buf.push(x?); + } + match buf.try_into() { + Ok(x) => Ok(x), + _ => panic!("The size of the iterator should not have changed"), + } +} diff --git a/crates/crypto/proof-setup/src/single/phase1.rs b/crates/crypto/proof-setup/src/single/phase1.rs index 626b2cd261..129ad2ff34 100644 --- a/crates/crypto/proof-setup/src/single/phase1.rs +++ b/crates/crypto/proof-setup/src/single/phase1.rs @@ -3,6 +3,7 @@ use ark_ff::{One, UniformRand, Zero}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use rand_core::{CryptoRngCore, OsRng}; +use crate::parallel_utils::transform_parallel; use crate::single::dlog; use crate::single::group::{ pairing, BatchedPairingChecker11, BatchedPairingChecker12, GroupHasher, F, G1, G2, @@ -71,7 +72,7 @@ impl RawCRSElements { /// This checks if the structure of the elements uses the secret scalars /// hidden behind the group elements correctly. #[must_use] - pub fn validate(self, rng: &mut R) -> Option { + pub fn validate(self) -> Option { // 0. Check that we can extract a valid degree out of these elements. let d = self.get_degree()?; // 1. Check that the elements committing to the secret values are not 0. @@ -85,31 +86,22 @@ impl RawCRSElements { } // 2. Check that the two beta commitments match. // 3. Check that the x values match on both groups. - let mut checker = BatchedPairingChecker12::new(G2::generator(), G1::generator()); - checker.add(self.beta_1, self.beta_2); + let mut checker0 = BatchedPairingChecker12::new(G2::generator(), G1::generator()); + checker0.add(self.beta_1, self.beta_2); for (&x_1_i, &x_2_i) in self.x_1.iter().zip(self.x_2.iter()) { - checker.add(x_1_i, x_2_i); - } - if !checker.check(rng) { - return None; + checker0.add(x_1_i, x_2_i); } // 4. Check that alpha and x are connected in alpha_x. - let mut checker = BatchedPairingChecker12::new(G2::generator(), self.alpha_1); + let mut checker1 = BatchedPairingChecker12::new(G2::generator(), self.alpha_1); for (&alpha_x_i, &x_i) in self.alpha_x_1.iter().zip(self.x_2.iter()) { - checker.add(alpha_x_i, x_i); - } - if !checker.check(rng) { - return None; + checker1.add(alpha_x_i, x_i); } // // 5. Check that beta and x are connected in beta_x. - let mut checker = BatchedPairingChecker12::new(G2::generator(), self.beta_1); + let mut checker2 = BatchedPairingChecker12::new(G2::generator(), self.beta_1); for (&beta_x_i, &x_i) in self.beta_x_1.iter().zip(self.x_2.iter()) { - checker.add(beta_x_i, x_i); - } - if !checker.check(rng) { - return None; + checker2.add(beta_x_i, x_i); } if !self .x_2 @@ -120,11 +112,21 @@ impl RawCRSElements { return None; } // 6. Check that the x_i are the correct powers of x. - let mut checker = BatchedPairingChecker11::new(self.x_2[1], G2::generator()); + let mut checker3 = BatchedPairingChecker11::new(self.x_2[1], G2::generator()); for (&x_i, &x_i_plus_1) in self.x_1.iter().zip(self.x_1.iter().skip(1)) { - checker.add(x_i, x_i_plus_1); + checker3.add(x_i, x_i_plus_1); } - if !checker.check(&mut OsRng) { + // "神だけが私を裁ける" + if transform_parallel( + [Ok(checker0), Ok(checker1), Ok(checker2), Err(checker3)], + |x| match x { + Ok(x) => x.check(&mut OsRng), + Err(x) => x.check(&mut OsRng), + }, + ) + .iter() + .any(|x| !x) + { return None; } @@ -240,10 +242,10 @@ pub struct RawContribution { impl RawContribution { /// Validate this raw contribution, potentially producing a valid one. - pub fn validate(&self, rng: &mut R) -> Option { + pub fn validate(&self) -> Option { self.new_elements .to_owned() - .validate(rng) + .validate() .map(|new_elements| Contribution { parent: self.parent, new_elements, @@ -453,7 +455,7 @@ impl Phase for Phase1 { _root: &Self::CRSElements, contribution: &Self::RawContribution, ) -> Option { - contribution.validate(&mut OsRng) + contribution.validate() } fn is_linked_to(contribution: &Self::Contribution, elements: &Self::CRSElements) -> bool { @@ -502,27 +504,27 @@ mod test { #[test] fn test_root_crs_is_valid() { let root = CRSElements::root(D); - assert!(root.raw.validate(&mut OsRng).is_some()); + assert!(root.raw.validate().is_some()); } #[test] fn test_nontrivial_crs_is_valid() { let crs = non_trivial_crs(); - assert!(crs.validate(&mut OsRng).is_some()); + assert!(crs.validate().is_some()); } #[test] fn test_changing_alpha_makes_crs_invalid() { let mut crs = non_trivial_crs(); crs.alpha_1 = G1::generator(); - assert!(crs.validate(&mut OsRng).is_none()); + assert!(crs.validate().is_none()); } #[test] fn test_changing_beta_makes_crs_invalid() { let mut crs = non_trivial_crs(); crs.beta_1 = G1::generator(); - assert!(crs.validate(&mut OsRng).is_none()); + assert!(crs.validate().is_none()); } #[test] @@ -532,11 +534,11 @@ mod test { let x = F::rand(&mut OsRng); let crs0 = make_crs(F::zero(), beta, x); - assert!(crs0.validate(&mut OsRng).is_none()); + assert!(crs0.validate().is_none()); let crs1 = make_crs(alpha, F::zero(), x); - assert!(crs1.validate(&mut OsRng).is_none()); + assert!(crs1.validate().is_none()); let crs2 = make_crs(alpha, beta, F::zero()); - assert!(crs2.validate(&mut OsRng).is_none()); + assert!(crs2.validate().is_none()); } #[test] @@ -559,7 +561,7 @@ mod test { alpha_x_1: vec![G1::generator() * alpha, G1::generator() * (alpha * x)], beta_x_1: vec![G1::generator() * beta, G1::generator() * (beta * x)], }; - assert!(crs.validate(&mut OsRng).is_none()); + assert!(crs.validate().is_none()); } #[test] @@ -570,7 +572,7 @@ mod test { ContributionHash([0u8; CONTRIBUTION_HASH_SIZE]), &root, ); - assert!(contribution.new_elements.raw.validate(&mut OsRng).is_some()); + assert!(contribution.new_elements.raw.validate().is_some()); } #[test]