From a027021f52cf57e89fd1708b5f4a680fbe3bb4c3 Mon Sep 17 00:00:00 2001 From: Srinath Setty Date: Thu, 15 Feb 2024 18:34:37 -0800 Subject: [PATCH] Improvements to error handling and naming (#309) * rename and introduce checks about length * introduce a test about public IO --- benches/compressed-snark.rs | 3 +- benches/recursive-snark.rs | 3 +- benches/sha256.rs | 3 +- examples/and.rs | 3 +- examples/minroot.rs | 3 +- src/errors.rs | 13 ++--- src/lib.rs | 87 +++++++++++++++++++++++++++++---- src/provider/bn256_grumpkin.rs | 25 +++++----- src/provider/hyperkzg.rs | 89 +++++++++++++++------------------- src/provider/ipa_pc.rs | 2 +- src/provider/pasta.rs | 22 ++++++--- src/provider/pedersen.rs | 17 +++---- src/provider/secp_secq.rs | 1 + src/provider/traits.rs | 43 ++++++++-------- src/r1cs/mod.rs | 5 -- 15 files changed, 193 insertions(+), 126 deletions(-) diff --git a/benches/compressed-snark.rs b/benches/compressed-snark.rs index ab6614b4..c0412431 100644 --- a/benches/compressed-snark.rs +++ b/benches/compressed-snark.rs @@ -72,7 +72,8 @@ fn bench_compressed_snark_internal, S2: RelaxedR1C &c_secondary, &*S1::ck_floor(), &*S2::ck_floor(), - ); + ) + .unwrap(); // Produce prover and verifier keys for CompressedSNARK let (pk, vk) = CompressedSNARK::<_, _, _, _, S1, S2>::setup(&pp).unwrap(); diff --git a/benches/recursive-snark.rs b/benches/recursive-snark.rs index 254ee1da..72beaa16 100644 --- a/benches/recursive-snark.rs +++ b/benches/recursive-snark.rs @@ -75,7 +75,8 @@ fn bench_recursive_snark(c: &mut Criterion) { &c_secondary, &*default_ck_hint(), &*default_ck_hint(), - ); + ) + .unwrap(); // Bench time to produce a recursive SNARK; // we execute a certain number of warm-up steps since executing diff --git a/benches/sha256.rs b/benches/sha256.rs index c4f41038..dfd03fc3 100644 --- a/benches/sha256.rs +++ b/benches/sha256.rs @@ -163,7 +163,8 @@ fn bench_recursive_snark(c: &mut Criterion) { &ttc, &*default_ck_hint(), &*default_ck_hint(), - ); + ) + .unwrap(); let circuit_secondary = TrivialCircuit::default(); let z0_primary = vec![::Scalar::from(2u64)]; diff --git a/examples/and.rs b/examples/and.rs index 6755bc1b..33b50bb7 100644 --- a/examples/and.rs +++ b/examples/and.rs @@ -231,7 +231,8 @@ fn main() { &circuit_secondary, &*S1::ck_floor(), &*S2::ck_floor(), - ); + ) + .unwrap(); println!("PublicParams::setup, took {:?} ", start.elapsed()); println!( diff --git a/examples/minroot.rs b/examples/minroot.rs index 914b80a9..7a589b95 100644 --- a/examples/minroot.rs +++ b/examples/minroot.rs @@ -175,7 +175,8 @@ fn main() { &circuit_secondary, &*S1::ck_floor(), &*S2::ck_floor(), - ); + ) + .unwrap(); println!("PublicParams::setup, took {:?} ", start.elapsed()); println!( diff --git a/src/errors.rs b/src/errors.rs index e2ff3d02..8a0912bc 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -9,9 +9,10 @@ pub enum NovaError { /// returned if the supplied row or col in (row,col,val) tuple is out of range #[error("InvalidIndex")] InvalidIndex, - /// returned if the supplied input is not even-sized - #[error("OddInputLength")] - OddInputLength, + /// returned if the step circuit calls inputize or alloc_io in its synthesize method + /// instead of passing output with the return value + #[error("InvalidStepCircuitIO")] + InvalidStepCircuitIO, /// returned if the supplied input is not of the right length #[error("InvalidInputLength")] InvalidInputLength, @@ -33,9 +34,9 @@ pub enum NovaError { /// returned if the provided number of steps is zero #[error("InvalidNumSteps")] InvalidNumSteps, - /// returned when an invalid inner product argument is provided - #[error("InvalidIPA")] - InvalidIPA, + /// returned when an invalid PCS evaluation argument is provided + #[error("InvalidPCS")] + InvalidPCS, /// returned when an invalid sum-check proof is provided #[error("InvalidSumcheckProof")] InvalidSumcheckProof, diff --git a/src/lib.rs b/src/lib.rs index 522bec21..a999f127 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,7 +143,7 @@ where c_secondary: &C2, ck_hint1: &CommitmentKeyHint, ck_hint2: &CommitmentKeyHint, - ) -> Self { + ) -> Result { let augmented_circuit_params_primary = NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, true); let augmented_circuit_params_secondary = @@ -181,7 +181,11 @@ where let _ = circuit_secondary.synthesize(&mut cs); let (r1cs_shape_secondary, ck_secondary) = cs.r1cs_shape(ck_hint2); - PublicParams { + if r1cs_shape_primary.num_io != 2 || r1cs_shape_secondary.num_io != 2 { + return Err(NovaError::InvalidStepCircuitIO); + } + + Ok(PublicParams { F_arity_primary, F_arity_secondary, ro_consts_primary, @@ -196,7 +200,7 @@ where augmented_circuit_params_secondary, digest: OnceCell::new(), _p: Default::default(), - } + }) } /// Retrieve the digest of the public parameters. @@ -924,7 +928,7 @@ mod tests { // this tests public parameters with a size specifically intended for a spark-compressed SNARK let ck_hint1 = &*SPrime::>::ck_floor(); let ck_hint2 = &*SPrime::>::ck_floor(); - let pp = PublicParams::::setup(circuit1, circuit2, ck_hint1, ck_hint2); + let pp = PublicParams::::setup(circuit1, circuit2, ck_hint1, ck_hint2).unwrap(); let digest_str = pp .digest() @@ -978,7 +982,8 @@ mod tests { &test_circuit2, &*default_ck_hint(), &*default_ck_hint(), - ); + ) + .unwrap(); let num_steps = 1; @@ -1032,7 +1037,8 @@ mod tests { &circuit_secondary, &*default_ck_hint(), &*default_ck_hint(), - ); + ) + .unwrap(); let num_steps = 3; @@ -1114,7 +1120,8 @@ mod tests { &circuit_secondary, &*default_ck_hint(), &*default_ck_hint(), - ); + ) + .unwrap(); let num_steps = 3; @@ -1213,7 +1220,8 @@ mod tests { &circuit_secondary, &*SPrime::::ck_floor(), &*SPrime::::ck_floor(), - ); + ) + .unwrap(); let num_steps = 3; @@ -1375,7 +1383,8 @@ mod tests { &circuit_secondary, &*default_ck_hint(), &*default_ck_hint(), - ); + ) + .unwrap(); let num_steps = 3; @@ -1452,7 +1461,8 @@ mod tests { &test_circuit2, &*default_ck_hint(), &*default_ck_hint(), - ); + ) + .unwrap(); let num_steps = 1; @@ -1497,4 +1507,61 @@ mod tests { test_ivc_base_with::(); test_ivc_base_with::(); } + + fn test_setup_with() + where + E1: Engine::Scalar>, + E2: Engine::Scalar>, + { + #[derive(Clone, Debug, Default)] + struct CircuitWithInputize { + _p: PhantomData, + } + + impl StepCircuit for CircuitWithInputize { + fn arity(&self) -> usize { + 1 + } + + fn synthesize>( + &self, + cs: &mut CS, + z: &[AllocatedNum], + ) -> Result>, SynthesisError> { + let x = &z[0]; + let y = x.square(cs.namespace(|| "x_sq"))?; + y.inputize(cs.namespace(|| "y"))?; // inputize y + Ok(vec![y]) + } + } + + // produce public parameters with trivial secondary + let circuit = CircuitWithInputize::<::Scalar>::default(); + let pp = + PublicParams::, TrivialCircuit>::setup( + &circuit, + &TrivialCircuit::default(), + &*default_ck_hint(), + &*default_ck_hint(), + ); + assert!(pp.is_err()); + assert_eq!(pp.err(), Some(NovaError::InvalidStepCircuitIO)); + + // produce public parameters with the trivial primary + let circuit = CircuitWithInputize::::default(); + let pp = + PublicParams::, CircuitWithInputize>::setup( + &TrivialCircuit::default(), + &circuit, + &*default_ck_hint(), + &*default_ck_hint(), + ); + assert!(pp.is_err()); + assert_eq!(pp.err(), Some(NovaError::InvalidStepCircuitIO)); + } + + #[test] + fn test_setup() { + test_setup_with::(); + } } diff --git a/src/provider/bn256_grumpkin.rs b/src/provider/bn256_grumpkin.rs index d15779d6..bb753155 100644 --- a/src/provider/bn256_grumpkin.rs +++ b/src/provider/bn256_grumpkin.rs @@ -1,5 +1,6 @@ //! This module implements the Nova traits for `bn256::Point`, `bn256::Scalar`, `grumpkin::Point`, `grumpkin::Scalar`. use crate::{ + errors::NovaError, impl_traits, provider::traits::{CompressedGroup, DlogGroup, PairingGroup}, traits::{Group, PrimeFieldExt, TranscriptReprTrait}, @@ -56,7 +57,7 @@ impl PairingGroup for Bn256Point { type GT = Gt; fn pairing(p: &Self, q: &Self::G2) -> Self::GT { - pairing(&p.to_affine(), &q.to_affine()) + pairing(&p.affine(), &q.affine()) } } @@ -84,20 +85,17 @@ impl Group for G2 { impl DlogGroup for G2 { type CompressedGroupElement = G2Compressed; - type PreprocessedGroupElement = G2Affine; + type AffineGroupElement = G2Affine; - fn vartime_multiscalar_mul( - scalars: &[Self::Scalar], - bases: &[Self::PreprocessedGroupElement], - ) -> Self { + fn vartime_multiscalar_mul(scalars: &[Self::Scalar], bases: &[Self::AffineGroupElement]) -> Self { best_multiexp(scalars, bases) } - fn preprocessed(&self) -> Self::PreprocessedGroupElement { + fn affine(&self) -> Self::AffineGroupElement { self.to_affine() } - fn group(p: &Self::PreprocessedGroupElement) -> Self { + fn group(p: &Self::AffineGroupElement) -> Self { G2::from(*p) } @@ -105,7 +103,7 @@ impl DlogGroup for G2 { self.to_bytes() } - fn from_label(_label: &'static [u8], _n: usize) -> Vec { + fn from_label(_label: &'static [u8], _n: usize) -> Vec { unimplemented!() } @@ -131,8 +129,13 @@ impl TranscriptReprTrait for G2Compressed { impl CompressedGroup for G2Compressed { type GroupElement = G2; - fn decompress(&self) -> Option { - Some(G2::from_bytes(self).unwrap()) + fn decompress(&self) -> Result { + let d = G2::from_bytes(self); + if d.is_some().into() { + Ok(d.unwrap()) + } else { + Err(NovaError::DecompressionError) + } } } diff --git a/src/provider/hyperkzg.rs b/src/provider/hyperkzg.rs index 04f955ec..6afc9a53 100644 --- a/src/provider/hyperkzg.rs +++ b/src/provider/hyperkzg.rs @@ -27,10 +27,10 @@ use rayon::prelude::*; use serde::{Deserialize, Serialize}; /// Alias to points on G1 that are in preprocessed form -type G1 = <::GE as DlogGroup>::PreprocessedGroupElement; +type G1Affine = <::GE as DlogGroup>::AffineGroupElement; /// Alias to points on G1 that are in preprocessed form -type G2 = <<::GE as PairingGroup>::G2 as DlogGroup>::PreprocessedGroupElement; +type G2Affine = <<::GE as PairingGroup>::G2 as DlogGroup>::AffineGroupElement; /// KZG commitment key #[derive(Debug, Clone, Serialize, Deserialize)] @@ -38,8 +38,8 @@ pub struct CommitmentKey where E::GE: PairingGroup, { - ck: Vec<::PreprocessedGroupElement>, - tau_H: <::G2 as DlogGroup>::PreprocessedGroupElement, // needed only for the verifier key + ck: Vec<::AffineGroupElement>, + tau_H: <::G2 as DlogGroup>::AffineGroupElement, // needed only for the verifier key } impl Len for CommitmentKey @@ -90,13 +90,8 @@ where } fn decompress(c: &Self::CompressedCommitment) -> Result { - let comm = <::GE as DlogGroup>::CompressedGroupElement::decompress(&c.comm); - if comm.is_none() { - return Err(NovaError::DecompressionError); - } - Ok(Commitment { - comm: comm.unwrap(), - }) + let comm = <::GE as DlogGroup>::CompressedGroupElement::decompress(&c.comm)?; + Ok(Commitment { comm }) } } @@ -235,12 +230,12 @@ where powers_of_tau.insert(i, powers_of_tau[i - 1] * tau); } - let ck: Vec> = (0..num_gens) + let ck: Vec> = (0..num_gens) .into_par_iter() - .map(|i| (::gen() * powers_of_tau[i]).preprocessed()) + .map(|i| (::gen() * powers_of_tau[i]).affine()) .collect(); - let tau_H = (<::G2 as DlogGroup>::gen() * tau).preprocessed(); + let tau_H = (<::G2 as DlogGroup>::gen() * tau).affine(); Self::CommitmentKey { ck, tau_H } } @@ -267,9 +262,9 @@ pub struct VerifierKey where E::GE: PairingGroup, { - G: G1, - H: G2, - tau_H: G2, + G: G1Affine, + H: G2Affine, + tau_H: G2Affine, } /// Provides an implementation of a polynomial evaluation argument @@ -279,8 +274,8 @@ pub struct EvaluationArgument where E::GE: PairingGroup, { - com: Vec>, - w: Vec>, + com: Vec>, + w: Vec>, v: Vec>, } @@ -297,7 +292,7 @@ where { // This impl block defines helper functions that are not a part of // EvaluationEngineTrait, but that we will use to implement the trait methods. - fn compute_challenge(com: &[G1], transcript: &mut ::TE) -> E::Scalar { + fn compute_challenge(com: &[G1Affine], transcript: &mut ::TE) -> E::Scalar { transcript.absorb(b"c", &com.to_vec().as_slice()); transcript.squeeze(b"c").unwrap() @@ -327,7 +322,7 @@ where q_powers } - fn verifier_second_challenge(W: &[G1], transcript: &mut ::TE) -> E::Scalar { + fn verifier_second_challenge(W: &[G1Affine], transcript: &mut ::TE) -> E::Scalar { transcript.absorb(b"W", &W.to_vec().as_slice()); transcript.squeeze(b"d").unwrap() @@ -351,8 +346,8 @@ where }; let vk = VerifierKey { - G: E::GE::gen().preprocessed(), - H: <::G2 as DlogGroup>::gen().preprocessed(), + G: E::GE::gen().affine(), + H: <::G2 as DlogGroup>::gen().affine(), tau_H: ck.tau_H.clone(), }; @@ -371,7 +366,7 @@ where let x: Vec = point.to_vec(); //////////////// begin helper closures ////////// - let kzg_open = |f: &[E::Scalar], u: E::Scalar| -> G1 { + let kzg_open = |f: &[E::Scalar], u: E::Scalar| -> G1Affine { // On input f(x) and u compute the witness polynomial used to prove // that f(u) = v. The main part of this is to compute the // division (f(x) - f(u)) / (x - u), but we don't use a general @@ -400,13 +395,13 @@ where let h = compute_witness_polynomial(f, u); - E::CE::commit(ck, &h).comm.preprocessed() + E::CE::commit(ck, &h).comm.affine() }; let kzg_open_batch = |f: &[Vec], u: &[E::Scalar], transcript: &mut ::TE| - -> (Vec>, Vec>) { + -> (Vec>, Vec>) { let poly_eval = |f: &[E::Scalar], u: E::Scalar| -> E::Scalar { let mut v = f[0]; let mut u_power = E::Scalar::ONE; @@ -463,7 +458,7 @@ where let w = u .into_par_iter() .map(|ui| kzg_open(&B, *ui)) - .collect::>>(); + .collect::>>(); // The prover computes the challenge to keep the transcript in the same // state as that of the verifier @@ -497,9 +492,9 @@ where // We do not need to commit to the first polynomial as it is already committed. // Compute commitments in parallel - let com: Vec> = (1..polys.len()) + let com: Vec> = (1..polys.len()) .into_par_iter() - .map(|i| E::CE::commit(ck, &polys[i]).comm.preprocessed()) + .map(|i| E::CE::commit(ck, &polys[i]).comm.affine()) .collect(); // Phase 2 @@ -527,10 +522,9 @@ where let y = P_of_x; // vk is hashed in transcript already, so we do not add it here - let kzg_verify_batch = |vk: &VerifierKey, - C: &Vec>, - W: &Vec>, + C: &Vec>, + W: &Vec>, u: &Vec, v: &Vec>, transcript: &mut ::TE| @@ -545,9 +539,9 @@ where let d_1 = d_0 * d_0; // Shorthand to convert from preprocessed G1 elements to non-preprocessed - let from_ppG1 = |P: &G1| ::group(P); + let from_ppG1 = |P: &G1Affine| ::group(P); // Shorthand to convert from preprocessed G2 elements to non-preprocessed - let from_ppG2 = |P: &G2| <::G2 as DlogGroup>::group(P); + let from_ppG2 = |P: &G2Affine| <::G2 as DlogGroup>::group(P); assert_eq!(t, 3); assert_eq!(W.len(), 3); @@ -623,7 +617,7 @@ where if r == E::Scalar::ZERO || C.comm == E::GE::zero() { return Err(NovaError::ProofVerifyError); } - com.insert(0, C.comm.preprocessed()); // set com_0 = C, shifts other commitments to the right + com.insert(0, C.comm.affine()); // set com_0 = C, shifts other commitments to the right let u = vec![r, -r, r * r]; @@ -670,7 +664,6 @@ mod tests { spartan::polys::multilinear::MultilinearPolynomial, }; use bincode::Options; - use group::Curve; use rand::SeedableRng; type E = Bn256EngineKZG; @@ -728,7 +721,7 @@ mod tests { } #[test] - fn test_hyperkzg() { + fn test_hyperkzg_small() { let n = 4; // poly = [1, 2, 1, 4] @@ -760,23 +753,19 @@ mod tests { ); let post_c_v = verifier_transcript.squeeze(b"c").unwrap(); - // check if the prover transcript and verifier transcript are kept in the - // same state + // check if the prover transcript and verifier transcript are kept in the same state assert_eq!(post_c_p, post_c_v); - let my_options = bincode::DefaultOptions::new() + let proof_bytes = bincode::DefaultOptions::new() .with_big_endian() - .with_fixint_encoding(); - let mut output_bytes = my_options.serialize(&vk).unwrap(); - output_bytes.append(&mut my_options.serialize(&C.compress()).unwrap()); - output_bytes.append(&mut my_options.serialize(&point).unwrap()); - output_bytes.append(&mut my_options.serialize(&eval).unwrap()); - output_bytes.append(&mut my_options.serialize(&proof).unwrap()); - println!("total output = {} bytes", output_bytes.len()); + .with_fixint_encoding() + .serialize(&proof) + .unwrap(); + assert_eq!(proof_bytes.len(), 368); // Change the proof and expect verification to fail let mut bad_proof = proof.clone(); - bad_proof.com[0] = (bad_proof.com[0] + bad_proof.com[0]).to_affine(); + bad_proof.v[0] = bad_proof.v[1].clone(); let mut verifier_transcript2 = Keccak256Transcript::new(b"TestEval"); assert!(EvaluationEngine::verify( &vk, @@ -790,7 +779,7 @@ mod tests { } #[test] - fn test_hyperkzg_more() { + fn test_hyperkzg_large() { // test the hyperkzg prover and verifier with random instances (derived from a seed) for ell in [4, 5, 6] { let mut rng = rand::rngs::StdRng::seed_from_u64(ell as u64); @@ -819,7 +808,7 @@ mod tests { // Change the proof and expect verification to fail let mut bad_proof = proof.clone(); - bad_proof.com[0] = (bad_proof.com[0] + bad_proof.com[1]).to_affine(); + bad_proof.v[0] = bad_proof.v[1].clone(); let mut verifier_tr2 = Keccak256Transcript::new(b"TestEval"); assert!( EvaluationEngine::verify(&vk, &mut verifier_tr2, &C, &point, &eval, &bad_proof).is_err() diff --git a/src/provider/ipa_pc.rs b/src/provider/ipa_pc.rs index 874d5497..37ab4b24 100644 --- a/src/provider/ipa_pc.rs +++ b/src/provider/ipa_pc.rs @@ -403,7 +403,7 @@ where if P_hat == CE::::commit(&ck_hat.combine(&ck_c), &[self.a_hat, self.a_hat * b_hat]) { Ok(()) } else { - Err(NovaError::InvalidIPA) + Err(NovaError::InvalidPCS) } } } diff --git a/src/provider/pasta.rs b/src/provider/pasta.rs index e529af49..199a38bf 100644 --- a/src/provider/pasta.rs +++ b/src/provider/pasta.rs @@ -1,5 +1,6 @@ //! This module implements the Nova traits for `pallas::Point`, `pallas::Scalar`, `vesta::Point`, `vesta::Scalar`. use crate::{ + errors::NovaError, provider::traits::{CompressedGroup, DlogGroup}, traits::{Group, PrimeFieldExt, TranscriptReprTrait}, }; @@ -70,11 +71,11 @@ macro_rules! impl_traits { impl DlogGroup for $name::Point { type CompressedGroupElement = $name_compressed; - type PreprocessedGroupElement = $name::Affine; + type AffineGroupElement = $name::Affine; fn vartime_multiscalar_mul( scalars: &[Self::Scalar], - bases: &[Self::PreprocessedGroupElement], + bases: &[Self::AffineGroupElement], ) -> Self { #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] if scalars.len() >= 128 { @@ -86,11 +87,11 @@ macro_rules! impl_traits { best_multiexp(scalars, bases) } - fn preprocessed(&self) -> Self::PreprocessedGroupElement { + fn affine(&self) -> Self::AffineGroupElement { self.to_affine() } - fn group(p: &Self::PreprocessedGroupElement) -> Self { + fn group(p: &Self::AffineGroupElement) -> Self { $name::Point::from(*p) } @@ -98,7 +99,7 @@ macro_rules! impl_traits { $name_compressed::new(self.to_bytes()) } - fn from_label(label: &'static [u8], n: usize) -> Vec { + fn from_label(label: &'static [u8], n: usize) -> Vec { let mut shake = Shake256::default(); shake.update(label); let mut reader = shake.finalize_xof(); @@ -153,7 +154,7 @@ macro_rules! impl_traits { } fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) { - let coordinates = self.to_affine().coordinates(); + let coordinates = self.affine().coordinates(); if coordinates.is_some().unwrap_u8() == 1 { (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false) } else { @@ -172,8 +173,13 @@ macro_rules! impl_traits { impl CompressedGroup for $name_compressed { type GroupElement = $name::Point; - fn decompress(&self) -> Option<$name::Point> { - Some($name_curve::from_bytes(&self.repr).unwrap()) + fn decompress(&self) -> Result<$name::Point, NovaError> { + let d = $name_curve::from_bytes(&self.repr); + if d.is_some().into() { + Ok(d.unwrap()) + } else { + Err(NovaError::DecompressionError) + } } } diff --git a/src/provider/pedersen.rs b/src/provider/pedersen.rs index 828ee3e6..d1c03ce5 100644 --- a/src/provider/pedersen.rs +++ b/src/provider/pedersen.rs @@ -23,7 +23,7 @@ where E: Engine, E::GE: DlogGroup, { - ck: Vec<::PreprocessedGroupElement>, + ck: Vec<::AffineGroupElement>, } impl Len for CommitmentKey @@ -72,13 +72,8 @@ where } fn decompress(c: &Self::CompressedCommitment) -> Result { - let comm = <::GE as DlogGroup>::CompressedGroupElement::decompress(&c.comm); - if comm.is_none() { - return Err(NovaError::DecompressionError); - } - Ok(Commitment { - comm: comm.unwrap(), - }) + let comm = <::GE as DlogGroup>::CompressedGroupElement::decompress(&c.comm)?; + Ok(Commitment { comm }) } } @@ -281,7 +276,7 @@ where .into_par_iter() .map(|i| { let bases = [L.ck[i].clone(), R.ck[i].clone()].to_vec(); - E::GE::vartime_multiscalar_mul(&w, &bases).preprocessed() + E::GE::vartime_multiscalar_mul(&w, &bases).affine() }) .collect(); @@ -294,7 +289,7 @@ where .ck .clone() .into_par_iter() - .map(|g| E::GE::vartime_multiscalar_mul(&[*r], &[g]).preprocessed()) + .map(|g| E::GE::vartime_multiscalar_mul(&[*r], &[g]).affine()) .collect(); CommitmentKey { ck: ck_scaled } @@ -308,7 +303,7 @@ where .collect::>, NovaError>>()?; let ck = (0..d.len()) .into_par_iter() - .map(|i| d[i].comm.preprocessed()) + .map(|i| d[i].comm.affine()) .collect(); Ok(CommitmentKey { ck }) } diff --git a/src/provider/secp_secq.rs b/src/provider/secp_secq.rs index 3f2ee1d3..8b1786cd 100644 --- a/src/provider/secp_secq.rs +++ b/src/provider/secp_secq.rs @@ -1,5 +1,6 @@ //! This module implements the Nova traits for `secp::Point`, `secp::Scalar`, `secq::Point`, `secq::Scalar`. use crate::{ + errors::NovaError, impl_traits, provider::traits::{CompressedGroup, DlogGroup}, traits::{Group, PrimeFieldExt, TranscriptReprTrait}, diff --git a/src/provider/traits.rs b/src/provider/traits.rs index c8b246d9..28367b01 100644 --- a/src/provider/traits.rs +++ b/src/provider/traits.rs @@ -1,4 +1,7 @@ -use crate::traits::{commitment::ScalarMul, Group, TranscriptReprTrait}; +use crate::{ + errors::NovaError, + traits::{commitment::ScalarMul, Group, TranscriptReprTrait}, +}; use core::{ fmt::Debug, ops::{Add, AddAssign, Sub, SubAssign}, @@ -22,7 +25,7 @@ pub trait CompressedGroup: type GroupElement: DlogGroup; /// Decompresses the compressed group element - fn decompress(&self) -> Option; + fn decompress(&self) -> Result; } /// A helper trait for types with a group operation. @@ -58,7 +61,7 @@ pub trait DlogGroup: type CompressedGroupElement: CompressedGroup; /// A type representing preprocessed group element - type PreprocessedGroupElement: Clone + type AffineGroupElement: Clone + Debug + PartialEq + Eq @@ -69,22 +72,19 @@ pub trait DlogGroup: + TranscriptReprTrait; /// A method to compute a multiexponentation - fn vartime_multiscalar_mul( - scalars: &[Self::Scalar], - bases: &[Self::PreprocessedGroupElement], - ) -> Self; + fn vartime_multiscalar_mul(scalars: &[Self::Scalar], bases: &[Self::AffineGroupElement]) -> Self; /// Produce a vector of group elements using a static label - fn from_label(label: &'static [u8], n: usize) -> Vec; + fn from_label(label: &'static [u8], n: usize) -> Vec; /// Compresses the group element fn compress(&self) -> Self::CompressedGroupElement; /// Produces a preprocessed element - fn preprocessed(&self) -> Self::PreprocessedGroupElement; + fn affine(&self) -> Self::AffineGroupElement; /// Returns a group element from a preprocessed group element - fn group(p: &Self::PreprocessedGroupElement) -> Self; + fn group(p: &Self::AffineGroupElement) -> Self; /// Returns an element that is the additive identity of the group fn zero() -> Self; @@ -139,20 +139,20 @@ macro_rules! impl_traits { impl DlogGroup for $name::Point { type CompressedGroupElement = $name_compressed; - type PreprocessedGroupElement = $name::Affine; + type AffineGroupElement = $name::Affine; fn vartime_multiscalar_mul( scalars: &[Self::Scalar], - bases: &[Self::PreprocessedGroupElement], + bases: &[Self::AffineGroupElement], ) -> Self { best_multiexp(scalars, bases) } - fn preprocessed(&self) -> Self::PreprocessedGroupElement { + fn affine(&self) -> Self::AffineGroupElement { self.to_affine() } - fn group(p: &Self::PreprocessedGroupElement) -> Self { + fn group(p: &Self::AffineGroupElement) -> Self { $name::Point::from(*p) } @@ -160,7 +160,7 @@ macro_rules! impl_traits { self.to_bytes() } - fn from_label(label: &'static [u8], n: usize) -> Vec { + fn from_label(label: &'static [u8], n: usize) -> Vec { let mut shake = Shake256::default(); shake.update(label); let mut reader = shake.finalize_xof(); @@ -215,9 +215,9 @@ macro_rules! impl_traits { } fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) { - let coordinates = self.to_affine().coordinates(); + let coordinates = self.affine().coordinates(); if coordinates.is_some().unwrap_u8() == 1 - && ($name_curve_affine::identity() != self.to_affine()) + && ($name_curve_affine::identity() != self.affine()) { (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false) } else { @@ -242,8 +242,13 @@ macro_rules! impl_traits { impl CompressedGroup for $name_compressed { type GroupElement = $name::Point; - fn decompress(&self) -> Option<$name::Point> { - Some($name_curve::from_bytes(&self).unwrap()) + fn decompress(&self) -> Result<$name::Point, NovaError> { + let d = $name_curve::from_bytes(&self); + if d.is_some().into() { + Ok(d.unwrap()) + } else { + Err(NovaError::DecompressionError) + } } } diff --git a/src/r1cs/mod.rs b/src/r1cs/mod.rs index 39d6ec84..56ef1394 100644 --- a/src/r1cs/mod.rs +++ b/src/r1cs/mod.rs @@ -127,11 +127,6 @@ impl R1CSShape { is_valid(num_cons, num_vars, num_io, &B)?; is_valid(num_cons, num_vars, num_io, &C)?; - // We require the number of public inputs/outputs to be even - if num_io % 2 != 0 { - return Err(NovaError::OddInputLength); - } - Ok(R1CSShape { num_cons, num_vars,