diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b4d067..d135ec0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,13 @@ ### Bug fixes +## v0.4.0 + +- Change dependency to version `0.4.0` of other arkwork-rs crates. +- Fiat-Shamir transformation for the AHP uses the Poseidon sponge function. +- Introduced fast_prove and fast_verify methods for PrimeFields that also implement the Absorb trait. +- Added RngCore and CryptographicSponge traits for rng sources. + ## v0.3.0 - Change dependency to version `0.3.0` of other arkworks-rs crates. diff --git a/Cargo.toml b/Cargo.toml index af4c060..1da0b2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ark-marlin" -version = "0.3.0" +version = "0.4.0" authors = [ "Alessandro Chiesa ", "Mary Maller ", @@ -17,28 +17,31 @@ keywords = ["cryptography", "commitments", "zkSNARK"] categories = ["cryptography"] include = ["Cargo.toml", "src", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] license = "MIT/Apache-2.0" -edition = "2018" +edition = "2021" [dependencies] -ark-serialize = { version = "^0.3.0", default-features = false, features = [ "derive" ] } -ark-ff = { version = "^0.3.0", default-features = false } -ark-std = { version = "^0.3.0", default-features = false } -ark-poly = { version = "^0.3.0", default-features = false } -ark-relations = { version = "^0.3.0", default-features = false } -ark-poly-commit = { version = "^0.3.0", default-features = false } +ark-serialize = { version = "^0.4.0", default-features = false, features = [ "derive" ] } +ark-ff = { version = "^0.4.0", default-features = false } +ark-ec = { version = "^0.4.0", default-features = false } +ark-std = { version = "^0.4.0", default-features = false } +ark-poly = { version = "^0.4.0", default-features = false } +ark-relations = { version = "^0.4.0", default-features = false } +ark-poly-commit = { version = "^0.4.0", default-features = false } +ark-crypto-primitives = { version = "^0.4.0", default-features = false, features = [ "r1cs" ] } rayon = { version = "1", optional = true } digest = { version = "0.9" } derivative = { version = "2", features = ["use_core"] } +itertools = "0.11.0" [dev-dependencies] -rand_chacha = { version = "0.3.0", default-features = false } +rand_chacha = { version = "^0.3.0", default-features = false } blake2 = { version = "0.9", default-features = false } -ark-bls12-381 = { version = "^0.3.0", default-features = false, features = [ "curve" ] } -ark-mnt4-298 = { version = "^0.3.0", default-features = false, features = ["r1cs", "curve"] } -ark-mnt6-298 = { version = "^0.3.0", default-features = false, features = ["r1cs"] } -ark-mnt4-753 = { version = "^0.3.0", default-features = false, features = ["r1cs", "curve"] } -ark-mnt6-753 = { version = "^0.3.0", default-features = false, features = ["r1cs"] } +ark-bls12-381 = { version = "^0.4.0", default-features = false, features = [ "curve" ] } +ark-mnt4-298 = { version = "^0.4.0", default-features = false, features = ["r1cs", "curve"] } +ark-mnt6-298 = { version = "^0.4.0", default-features = false, features = ["r1cs"] } +ark-mnt4-753 = { version = "^0.4.0", default-features = false, features = ["r1cs", "curve"] } +ark-mnt6-753 = { version = "^0.4.0", default-features = false, features = ["r1cs"] } [profile.release] opt-level = 3 diff --git a/benches/bench.rs b/benches/bench.rs index 8e3218a..c29a6b3 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -3,8 +3,10 @@ // where N is the number of threads you want to use (N = 1 for single-thread). use ark_bls12_381::{Bls12_381, Fr as BlsFr}; + +use ark_crypto_primitives::sponge::CryptographicSponge; use ark_ff::PrimeField; -use ark_marlin::{Marlin, SimpleHashFiatShamirRng}; +use ark_marlin::{Marlin, SimplePoseidonRng}; use ark_mnt4_298::{Fr as MNT4Fr, MNT4_298}; use ark_mnt4_753::{Fr as MNT4BigFr, MNT4_753}; use ark_mnt6_298::{Fr as MNT6Fr, MNT6_298}; @@ -15,9 +17,8 @@ use ark_relations::{ lc, r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}, }; -use ark_std::{ops::Mul, UniformRand}; -use blake2::Blake2s; -use rand_chacha::ChaChaRng; +use ark_std::{ops::Mul, rand::RngCore}; +use itertools::Itertools; const NUM_PROVE_REPEATITIONS: usize = 10; const NUM_VERIFY_REPEATITIONS: usize = 50; @@ -68,24 +69,40 @@ impl ConstraintSynthesizer for DummyCircuit { macro_rules! marlin_prove_bench { ($bench_name:ident, $bench_field:ty, $bench_pairing_engine:ty) => { - let rng = &mut ark_std::test_rng(); + let mut rng_seed = ark_std::test_rng(); + let mut rng: SimplePoseidonRng<$bench_field> = SimplePoseidonRng::default(); + rng.absorb(&rng_seed.next_u64()); + let (a, b) = rng + .squeeze_field_elements(2) + .iter() + .map(|x: &$bench_field| x.to_owned()) + .collect_tuple() + .unwrap(); let c = DummyCircuit::<$bench_field> { - a: Some(<$bench_field>::rand(rng)), - b: Some(<$bench_field>::rand(rng)), + a: Some(a), + b: Some(b), num_variables: 10, num_constraints: 65536, }; let srs = Marlin::< $bench_field, - SonicKZG10<$bench_pairing_engine, DensePolynomial<$bench_field>>, - SimpleHashFiatShamirRng, - >::universal_setup(65536, 65536, 3 * 65536, rng) + SonicKZG10< + $bench_pairing_engine, + DensePolynomial<$bench_field>, + SimplePoseidonRng<$bench_field>, + >, + SimplePoseidonRng<$bench_field>, + >::universal_setup(65536, 65536, 3 * 65536, &mut rng) .unwrap(); let (pk, _) = Marlin::< $bench_field, - SonicKZG10<$bench_pairing_engine, DensePolynomial<$bench_field>>, - SimpleHashFiatShamirRng, + SonicKZG10< + $bench_pairing_engine, + DensePolynomial<$bench_field>, + SimplePoseidonRng<$bench_field>, + >, + SimplePoseidonRng<$bench_field>, >::index(&srs, c) .unwrap(); @@ -94,9 +111,13 @@ macro_rules! marlin_prove_bench { for _ in 0..NUM_PROVE_REPEATITIONS { let _ = Marlin::< $bench_field, - SonicKZG10<$bench_pairing_engine, DensePolynomial<$bench_field>>, - SimpleHashFiatShamirRng, - >::prove(&pk, c.clone(), rng) + SonicKZG10< + $bench_pairing_engine, + DensePolynomial<$bench_field>, + SimplePoseidonRng<$bench_field>, + >, + SimplePoseidonRng<$bench_field>, + >::prove(&pk, c.clone(), &mut rng) .unwrap(); } @@ -110,31 +131,51 @@ macro_rules! marlin_prove_bench { macro_rules! marlin_verify_bench { ($bench_name:ident, $bench_field:ty, $bench_pairing_engine:ty) => { - let rng = &mut ark_std::test_rng(); + let mut rng_seed = ark_std::test_rng(); + let mut rng: SimplePoseidonRng<$bench_field> = SimplePoseidonRng::default(); + rng.absorb(&rng_seed.next_u64()); + let (a, b) = rng + .squeeze_field_elements(2) + .iter() + .map(|x: &$bench_field| x.to_owned()) + .collect_tuple() + .unwrap(); let c = DummyCircuit::<$bench_field> { - a: Some(<$bench_field>::rand(rng)), - b: Some(<$bench_field>::rand(rng)), + a: Some(a), + b: Some(b), num_variables: 10, num_constraints: 65536, }; let srs = Marlin::< $bench_field, - SonicKZG10<$bench_pairing_engine, DensePolynomial<$bench_field>>, - SimpleHashFiatShamirRng, - >::universal_setup(65536, 65536, 3 * 65536, rng) + SonicKZG10< + $bench_pairing_engine, + DensePolynomial<$bench_field>, + SimplePoseidonRng<$bench_field>, + >, + SimplePoseidonRng<$bench_field>, + >::universal_setup(65536, 65536, 3 * 65536, &mut rng) .unwrap(); let (pk, vk) = Marlin::< $bench_field, - SonicKZG10<$bench_pairing_engine, DensePolynomial<$bench_field>>, - SimpleHashFiatShamirRng, + SonicKZG10< + $bench_pairing_engine, + DensePolynomial<$bench_field>, + SimplePoseidonRng<$bench_field>, + >, + SimplePoseidonRng<$bench_field>, >::index(&srs, c) .unwrap(); let proof = Marlin::< $bench_field, - SonicKZG10<$bench_pairing_engine, DensePolynomial<$bench_field>>, - SimpleHashFiatShamirRng, - >::prove(&pk, c.clone(), rng) + SonicKZG10< + $bench_pairing_engine, + DensePolynomial<$bench_field>, + SimplePoseidonRng<$bench_field>, + >, + SimplePoseidonRng<$bench_field>, + >::prove(&pk, c.clone(), &mut rng) .unwrap(); let v = c.a.unwrap().mul(c.b.unwrap()); @@ -144,9 +185,13 @@ macro_rules! marlin_verify_bench { for _ in 0..NUM_VERIFY_REPEATITIONS { let _ = Marlin::< $bench_field, - SonicKZG10<$bench_pairing_engine, DensePolynomial<$bench_field>>, - SimpleHashFiatShamirRng, - >::verify(&vk, &vec![v], &proof, rng) + SonicKZG10< + $bench_pairing_engine, + DensePolynomial<$bench_field>, + SimplePoseidonRng<$bench_field>, + >, + SimplePoseidonRng<$bench_field>, + >::verify(&vk, &vec![v], &proof, &mut rng) .unwrap(); } diff --git a/src/ahp/constraint_systems.rs b/src/ahp/constraint_systems.rs index ddac9ca..31a0dfb 100644 --- a/src/ahp/constraint_systems.rs +++ b/src/ahp/constraint_systems.rs @@ -6,11 +6,8 @@ use crate::BTreeMap; use ark_ff::{Field, PrimeField}; use ark_poly::{EvaluationDomain, Evaluations as EvaluationsOnDomain, GeneralEvaluationDomain}; use ark_relations::{lc, r1cs::ConstraintSystemRef}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; -use ark_std::{ - cfg_iter_mut, - io::{Read, Write}, -}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::cfg_iter_mut; use derivative::Derivative; /* ************************************************************************* */ @@ -291,13 +288,18 @@ pub(crate) fn make_matrices_square_for_prover(cs: ConstraintSyste #[cfg(test)] mod tests { + use crate::SimplePoseidonRng; + use super::*; + use ark_crypto_primitives::sponge::CryptographicSponge; use ark_relations::r1cs::Matrix; - use ark_std::{collections::BTreeMap, UniformRand}; + use ark_std::collections::BTreeMap; use ark_bls12_381::Fr as F; use ark_ff::{One, Zero}; use ark_poly::EvaluationDomain; + use itertools::Itertools; + use rand_chacha::rand_core::RngCore; fn entry(matrix: &Matrix, row: usize, col: usize) -> F { matrix[row] @@ -372,10 +374,15 @@ mod tests { .zip(output_domain.batch_eval_unnormalized_bivariate_lagrange_poly_with_same_inputs()) .collect(); - let mut rng = ark_std::test_rng(); - let eta_a = F::rand(&mut rng); - let eta_b = F::rand(&mut rng); - let eta_c = F::rand(&mut rng); + let mut rng_seed = ark_std::test_rng(); + let mut rng: SimplePoseidonRng = SimplePoseidonRng::default(); + rng.absorb(&rng_seed.next_u64()); + let (eta_a, eta_b, eta_c) = rng + .squeeze_field_elements(3) + .iter() + .map(|x: &F| x.to_owned()) + .collect_tuple() + .unwrap(); for (k_index, k) in interpolation_domain.elements().enumerate() { let row_val = joint_arith.row.evaluate(&k); let col_val = joint_arith.col.evaluate(&k); diff --git a/src/ahp/indexer.rs b/src/ahp/indexer.rs index b43c580..6aec219 100644 --- a/src/ahp/indexer.rs +++ b/src/ahp/indexer.rs @@ -7,16 +7,13 @@ use crate::ahp::{ AHPForR1CS, Error, LabeledPolynomial, }; use crate::Vec; -use ark_ff::PrimeField; +use ark_ff::{Field, PrimeField}; use ark_poly::{EvaluationDomain, GeneralEvaluationDomain}; use ark_relations::r1cs::{ ConstraintSynthesizer, ConstraintSystem, OptimizationGoal, SynthesisError, SynthesisMode, }; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; -use ark_std::{ - io::{Read, Write}, - marker::PhantomData, -}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::marker::PhantomData; use derivative::Derivative; use crate::ahp::constraint_systems::{ @@ -28,7 +25,7 @@ use crate::ahp::constraint_systems::{ /// entries in any of the constraint matrices. #[derive(Derivative, CanonicalSerialize, CanonicalDeserialize)] #[derivative(Clone(bound = ""), Copy(bound = ""))] -pub struct IndexInfo { +pub struct IndexInfo { /// The total number of variables in the constraint system. pub num_variables: usize, /// The number of constraints. @@ -42,7 +39,7 @@ pub struct IndexInfo { f: PhantomData, } -impl IndexInfo { +impl IndexInfo { /// Construct a new index info pub fn new( num_variables: usize, @@ -60,14 +57,6 @@ impl IndexInfo { } } -impl ark_ff::ToBytes for IndexInfo { - fn write(&self, mut w: W) -> ark_std::io::Result<()> { - (self.num_variables as u64).write(&mut w)?; - (self.num_constraints as u64).write(&mut w)?; - (self.num_non_zero as u64).write(&mut w) - } -} - impl IndexInfo { /// The maximum degree of polynomial required to represent this index in the /// the AHP. diff --git a/src/ahp/mod.rs b/src/ahp/mod.rs index 383a6e8..24e6532 100644 --- a/src/ahp/mod.rs +++ b/src/ahp/mod.rs @@ -334,7 +334,7 @@ mod tests { use ark_ff::{One, UniformRand, Zero}; use ark_poly::{ univariate::{DenseOrSparsePolynomial, DensePolynomial}, - Polynomial, UVPolynomial, + DenseUVPolynomial, Polynomial, }; #[test] @@ -414,11 +414,7 @@ mod tests { divisor .coeffs .iter() - .filter_map(|f| if !f.is_zero() { - Some(f.into_repr()) - } else { - None - }) + .filter(|f| !f.is_zero()) .collect::>() ); @@ -446,11 +442,7 @@ mod tests { quotient .coeffs .iter() - .filter_map(|f| if !f.is_zero() { - Some(f.into_repr()) - } else { - None - }) + .filter(|f| !f.is_zero()) .collect::>() ); diff --git a/src/ahp/prover.rs b/src/ahp/prover.rs index 60039f7..3554da1 100644 --- a/src/ahp/prover.rs +++ b/src/ahp/prover.rs @@ -8,20 +8,24 @@ use crate::ahp::constraint_systems::{ make_matrices_square_for_prover, pad_input_for_indexer_and_prover, unformat_public_input, }; use crate::{ToString, Vec}; +use ark_crypto_primitives::sponge::CryptographicSponge; use ark_ff::{Field, PrimeField, Zero}; use ark_poly::{ - univariate::DensePolynomial, EvaluationDomain, Evaluations as EvaluationsOnDomain, - GeneralEvaluationDomain, Polynomial, UVPolynomial, + univariate::DensePolynomial, DenseUVPolynomial, EvaluationDomain, + Evaluations as EvaluationsOnDomain, GeneralEvaluationDomain, Polynomial, }; use ark_relations::r1cs::{ ConstraintSynthesizer, ConstraintSystem, OptimizationGoal, SynthesisError, }; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; +use ark_serialize::Compress; +use ark_serialize::Validate; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError, Valid}; use ark_std::rand::RngCore; use ark_std::{ cfg_into_iter, cfg_iter, cfg_iter_mut, io::{Read, Write}, }; +use itertools::Itertools; /// State for the AHP prover. pub struct ProverState<'a, F: PrimeField> { @@ -72,81 +76,44 @@ pub enum ProverMsg { FieldElements(Vec), } -impl ark_ff::ToBytes for ProverMsg { - fn write(&self, w: W) -> ark_std::io::Result<()> { - match self { - ProverMsg::EmptyMessage => Ok(()), - ProverMsg::FieldElements(field_elems) => field_elems.write(w), - } - } -} - impl CanonicalSerialize for ProverMsg { - fn serialize(&self, mut writer: W) -> Result<(), SerializationError> { - let res: Option> = match self { - ProverMsg::EmptyMessage => None, - ProverMsg::FieldElements(v) => Some(v.clone()), - }; - res.serialize(&mut writer) - } - - fn serialized_size(&self) -> usize { - let res: Option> = match self { - ProverMsg::EmptyMessage => None, - ProverMsg::FieldElements(v) => Some(v.clone()), - }; - res.serialized_size() - } - - fn serialize_unchecked(&self, mut writer: W) -> Result<(), SerializationError> { - let res: Option> = match self { - ProverMsg::EmptyMessage => None, - ProverMsg::FieldElements(v) => Some(v.clone()), - }; - res.serialize_unchecked(&mut writer) - } - - fn serialize_uncompressed(&self, mut writer: W) -> Result<(), SerializationError> { - let res: Option> = match self { + fn serialize_with_mode( + &self, + writer: W, + compress: Compress, + ) -> Result<(), SerializationError> { + let res = match self { ProverMsg::EmptyMessage => None, ProverMsg::FieldElements(v) => Some(v.clone()), }; - res.serialize_uncompressed(&mut writer) + res.serialize_with_mode(writer, compress)?; + Ok(()) } - fn uncompressed_size(&self) -> usize { + fn serialized_size(&self, compress: Compress) -> usize { let res: Option> = match self { ProverMsg::EmptyMessage => None, ProverMsg::FieldElements(v) => Some(v.clone()), }; - res.uncompressed_size() + res.serialized_size(compress) } } -impl CanonicalDeserialize for ProverMsg { - fn deserialize(mut reader: R) -> Result { - let res = Option::>::deserialize(&mut reader)?; - - if let Some(res) = res { - Ok(ProverMsg::FieldElements(res)) - } else { - Ok(ProverMsg::EmptyMessage) - } - } - - fn deserialize_unchecked(mut reader: R) -> Result { - let res = Option::>::deserialize_unchecked(&mut reader)?; - - if let Some(res) = res { - Ok(ProverMsg::FieldElements(res)) - } else { - Ok(ProverMsg::EmptyMessage) +impl Valid for ProverMsg { + fn check(&self) -> Result<(), SerializationError> { + match self { + ProverMsg::EmptyMessage => Ok(()), + ProverMsg::FieldElements(v) => v.check(), } } - - fn deserialize_uncompressed(mut reader: R) -> Result { - let res = Option::>::deserialize_uncompressed(&mut reader)?; - +} +impl CanonicalDeserialize for ProverMsg { + fn deserialize_with_mode( + reader: R, + compress: Compress, + validate: Validate, + ) -> Result { + let res = Option::>::deserialize_with_mode(reader, compress, validate)?; if let Some(res) = res { Ok(ProverMsg::FieldElements(res)) } else { @@ -306,7 +273,7 @@ impl AHPForR1CS { } /// Output the first round message and the next state. - pub fn prover_first_round<'a, R: RngCore>( + pub fn prover_first_round<'a, R: CryptographicSponge + RngCore>( mut state: ProverState<'a, F>, rng: &mut R, ) -> Result<(ProverMsg, ProverFirstOracles, ProverState<'a, F>), Error> { @@ -347,9 +314,15 @@ impl AHPForR1CS { }) .collect(); + let (f1, f2, f3) = rng + .squeeze_field_elements(3) + .iter() + .map(|x: &F| x.to_owned()) + .collect_tuple() + .unwrap(); let w_poly = &EvaluationsOnDomain::from_vec_and_domain(w_poly_evals, domain_h) .interpolate() - + &(&DensePolynomial::from_coefficients_slice(&[F::rand(rng)]) * &v_H); + + &(&DensePolynomial::from_coefficients_slice(&[f1]) * &v_H); let (w_poly, remainder) = w_poly.divide_by_vanishing_poly(domain_x).unwrap(); assert!(remainder.is_zero()); end_timer!(w_poly_time); @@ -357,13 +330,13 @@ impl AHPForR1CS { let z_a_poly_time = start_timer!(|| "Computing z_A polynomial"); let z_a = state.z_a.clone().unwrap(); let z_a_poly = &EvaluationsOnDomain::from_vec_and_domain(z_a, domain_h).interpolate() - + &(&DensePolynomial::from_coefficients_slice(&[F::rand(rng)]) * &v_H); + + &(&DensePolynomial::from_coefficients_slice(&[f2]) * &v_H); end_timer!(z_a_poly_time); let z_b_poly_time = start_timer!(|| "Computing z_B polynomial"); let z_b = state.z_b.clone().unwrap(); let z_b_poly = &EvaluationsOnDomain::from_vec_and_domain(z_b, domain_h).interpolate() - + &(&DensePolynomial::from_coefficients_slice(&[F::rand(rng)]) * &v_H); + + &(&DensePolynomial::from_coefficients_slice(&[f3]) * &v_H); end_timer!(z_b_poly_time); let mask_poly_time = start_timer!(|| "Computing mask polynomial"); diff --git a/src/ahp/verifier.rs b/src/ahp/verifier.rs index 91bb357..e8b739a 100644 --- a/src/ahp/verifier.rs +++ b/src/ahp/verifier.rs @@ -2,7 +2,9 @@ use crate::ahp::indexer::IndexInfo; use crate::ahp::*; +use ark_crypto_primitives::sponge::CryptographicSponge; use ark_std::rand::RngCore; +use itertools::Itertools; use ark_ff::PrimeField; use ark_poly::{EvaluationDomain, GeneralEvaluationDomain}; @@ -41,7 +43,7 @@ pub struct VerifierSecondMsg { impl AHPForR1CS { /// Output the first message and next round state. - pub fn verifier_first_round( + pub fn verifier_first_round( index_info: IndexInfo, rng: &mut R, ) -> Result<(VerifierFirstMsg, VerifierState), Error> { @@ -49,16 +51,20 @@ impl AHPForR1CS { return Err(Error::NonSquareMatrix); } - let domain_h = GeneralEvaluationDomain::new(index_info.num_constraints) - .ok_or(SynthesisError::PolynomialDegreeTooLarge)?; + let domain_h: GeneralEvaluationDomain = + GeneralEvaluationDomain::new(index_info.num_constraints) + .ok_or(SynthesisError::PolynomialDegreeTooLarge)?; let domain_k = GeneralEvaluationDomain::new(index_info.num_non_zero) .ok_or(SynthesisError::PolynomialDegreeTooLarge)?; - let alpha = domain_h.sample_element_outside_domain(rng); - let eta_a = F::rand(rng); - let eta_b = F::rand(rng); - let eta_c = F::rand(rng); + let alpha = domain_h.sample_element_outside_domain(rng).to_owned(); + let (eta_a, eta_b, eta_c) = rng + .squeeze_field_elements(3) + .iter() + .map(|x: &F| x.to_owned()) + .collect_tuple() + .unwrap(); let msg = VerifierFirstMsg { alpha, @@ -91,11 +97,12 @@ impl AHPForR1CS { } /// Output the third message and next round state. - pub fn verifier_third_round( + pub fn verifier_third_round( mut state: VerifierState, rng: &mut R, ) -> VerifierState { - state.gamma = Some(F::rand(rng)); + let gamma = rng.squeeze_field_elements(1).pop(); + state.gamma = gamma; state } diff --git a/src/data_structures.rs b/src/data_structures.rs index 66b5643..955b244 100644 --- a/src/data_structures.rs +++ b/src/data_structures.rs @@ -1,21 +1,20 @@ use crate::ahp::indexer::*; use crate::ahp::prover::ProverMsg; use crate::Vec; +use ark_crypto_primitives::sponge::CryptographicSponge; use ark_ff::PrimeField; use ark_poly::univariate::DensePolynomial; use ark_poly_commit::{BatchLCProof, PolynomialCommitment}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError}; -use ark_std::{ - format, - io::{Read, Write}, -}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::format; /* ************************************************************************* */ /* ************************************************************************* */ /* ************************************************************************* */ /// The universal public parameters for the argument system. -pub type UniversalSRS = >>::UniversalParams; +pub type UniversalSRS = + , S>>::UniversalParams; /* ************************************************************************* */ /* ************************************************************************* */ @@ -23,7 +22,11 @@ pub type UniversalSRS = /// Verification key for a specific index (i.e., R1CS matrices). #[derive(CanonicalSerialize, CanonicalDeserialize)] -pub struct IndexVerifierKey>> { +pub struct IndexVerifierKey< + F: PrimeField, + PC: PolynomialCommitment, S>, + S: CryptographicSponge, +> { /// Stores information about the size of the index, as well as its field of /// definition. pub index_info: IndexInfo, @@ -33,17 +36,8 @@ pub struct IndexVerifierKey>> ark_ff::ToBytes - for IndexVerifierKey -{ - fn write(&self, mut w: W) -> ark_std::io::Result<()> { - self.index_info.write(&mut w)?; - self.index_comms.write(&mut w) - } -} - -impl>> Clone - for IndexVerifierKey +impl, S>, S: CryptographicSponge> + Clone for IndexVerifierKey { fn clone(&self) -> Self { Self { @@ -54,7 +48,9 @@ impl>> Clone } } -impl>> IndexVerifierKey { +impl, S>, S: CryptographicSponge> + IndexVerifierKey +{ /// Iterate over the commitments to indexed polynomials in `self`. pub fn iter(&self) -> impl Iterator { self.index_comms.iter() @@ -67,9 +63,13 @@ impl>> IndexVerifi /// Proving key for a specific index (i.e., R1CS matrices). #[derive(CanonicalSerialize, CanonicalDeserialize)] -pub struct IndexProverKey>> { +pub struct IndexProverKey< + F: PrimeField, + PC: PolynomialCommitment, S>, + S: CryptographicSponge, +> { /// The index verifier key. - pub index_vk: IndexVerifierKey, + pub index_vk: IndexVerifierKey, /// The randomness for the index polynomial commitments. pub index_comm_rands: Vec, /// The index itself. @@ -78,7 +78,8 @@ pub struct IndexProverKey>> Clone for IndexProverKey +impl, S>, S: CryptographicSponge> + Clone for IndexProverKey where PC::Commitment: Clone, { @@ -98,7 +99,11 @@ where /// A zkSNARK proof. #[derive(CanonicalSerialize, CanonicalDeserialize)] -pub struct Proof>> { +pub struct Proof< + F: PrimeField, + PC: PolynomialCommitment, S>, + S: CryptographicSponge, +> { /// Commitments to the polynomials produced by the AHP prover. pub commitments: Vec>, /// Evaluations of these polynomials. @@ -106,16 +111,18 @@ pub struct Proof>> /// The field elements sent by the prover. pub prover_messages: Vec>, /// An evaluation proof from the polynomial commitment. - pub pc_proof: BatchLCProof, PC>, + pub pc_proof: BatchLCProof, } -impl>> Proof { +impl, S>, S: CryptographicSponge> + Proof +{ /// Construct a new proof. pub fn new( commitments: Vec>, evaluations: Vec, prover_messages: Vec>, - pc_proof: BatchLCProof, PC>, + pc_proof: BatchLCProof, ) -> Self { Self { commitments, @@ -136,19 +143,19 @@ impl>> Proof = self.pc_proof.proof.clone().into(); let num_proofs = proofs.len(); - let size_bytes_proofs = self.pc_proof.proof.serialized_size(); + let size_bytes_proofs = self.pc_proof.proof.compressed_size(); let num_evals = self.evaluations.len(); - let evals_size_in_bytes = self.evaluations.serialized_size(); + let evals_size_in_bytes = self.evaluations.compressed_size(); let num_prover_messages: usize = self .prover_messages .iter() @@ -157,8 +164,8 @@ impl>> Proof elems.len(), }) .sum(); - let prover_msg_size_in_bytes = self.prover_messages.serialized_size(); - let arg_size = self.serialized_size(); + let prover_msg_size_in_bytes = self.prover_messages.compressed_size(); + let arg_size = self.compressed_size(); let stats = format!( "Argument size in bytes: {}\n\n\ Number of commitments without degree bounds: {}\n\ diff --git a/src/lib.rs b/src/lib.rs index 7ca47b4..0992f58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,23 +8,27 @@ //! matrices are square). Furthermore, Marlin only supports instances where the //! public inputs are of size one less than a power of 2 (i.e., 2^n - 1). #![deny(unused_import_braces, unused_qualifications, trivial_casts)] -#![deny(trivial_numeric_casts, private_in_public)] +#![deny(trivial_numeric_casts)] #![deny(stable_features, unreachable_pub, non_shorthand_field_patterns)] #![deny(unused_attributes, unused_imports, unused_mut, missing_docs)] #![deny(renamed_and_removed_lints, stable_features, unused_allocation)] -#![deny(unused_comparisons, bare_trait_objects, unused_must_use, const_err)] +#![deny(unused_comparisons, bare_trait_objects, unused_must_use)] #![forbid(unsafe_code)] #[macro_use] extern crate ark_std; -use ark_ff::{to_bytes, PrimeField, UniformRand}; +use ark_crypto_primitives::absorb; +use ark_crypto_primitives::sponge::{Absorb, CryptographicSponge}; +use ark_ff::PrimeField; use ark_poly::{univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain}; +use ark_poly_commit::challenge::ChallengeGenerator; use ark_poly_commit::Evaluations; use ark_poly_commit::{LabeledCommitment, PCUniversalParams, PolynomialCommitment}; use ark_relations::r1cs::ConstraintSynthesizer; use ark_std::rand::RngCore; +use ark_serialize::CanonicalSerialize; use ark_std::{ collections::BTreeMap, format, @@ -40,11 +44,15 @@ macro_rules! eprintln { ($($arg: tt)*) => {}; } +fn to_bytes(x: &T) -> Vec { + let mut buf = Vec::new(); + x.serialize_compressed(&mut buf).unwrap(); + buf +} /// Implements a Fiat-Shamir based Rng that allows one to incrementally update /// the seed based on new messages in the proof transcript. pub mod rng; -use rng::FiatShamirRng; -pub use rng::SimpleHashFiatShamirRng; +pub use rng::*; mod error; pub use error::*; @@ -57,18 +65,28 @@ pub mod ahp; pub use ahp::AHPForR1CS; use ahp::EvaluationsProvider; +use crate::ahp::prover::ProverMsg; + #[cfg(test)] mod test; +/// Marker trait for when we can use fast absord of field elements /// The compiled argument system. -pub struct Marlin>, FS: FiatShamirRng>( +pub struct Marlin< + F: PrimeField, + PC: PolynomialCommitment, S>, + S: CryptographicSponge, +>( #[doc(hidden)] PhantomData, #[doc(hidden)] PhantomData, - #[doc(hidden)] PhantomData, + #[doc(hidden)] PhantomData, ); -impl>, FS: FiatShamirRng> - Marlin +impl< + F: PrimeField, + PC: PolynomialCommitment, S>, + S: CryptographicSponge + Default + RngCore, + > Marlin { /// The personalization string for this protocol. Used to personalize the /// Fiat-Shamir rng. @@ -81,7 +99,7 @@ impl>, FS: FiatSha num_variables: usize, num_non_zero: usize, rng: &mut R, - ) -> Result, Error> { + ) -> Result, Error> { let max_degree = AHPForR1CS::::max_degree(num_constraints, num_variables, num_non_zero)?; let setup_time = start_timer!(|| { format!( @@ -98,9 +116,9 @@ impl>, FS: FiatSha /// Generate the index-specific (i.e., circuit-specific) prover and verifier /// keys. This is a deterministic algorithm that anyone can rerun. pub fn index>( - srs: &UniversalSRS, + srs: &UniversalSRS, c: C, - ) -> Result<(IndexProverKey, IndexVerifierKey), Error> { + ) -> Result<(IndexProverKey, IndexVerifierKey), Error> { let index_time = start_timer!(|| "Marlin::Index"); // TODO: Add check that c is in the correct mode. @@ -146,20 +164,23 @@ impl>, FS: FiatSha Ok((index_pk, index_vk)) } - /// Create a zkSNARK asserting that the constraint system is satisfied. - pub fn prove, R: RngCore>( - index_pk: &IndexProverKey, + pub fn prove, R: RngCore + CryptographicSponge>( + index_pk: &IndexProverKey, c: C, zk_rng: &mut R, - ) -> Result, Error> { + ) -> Result, Error> { let prover_time = start_timer!(|| "Marlin::Prover"); // Add check that c is in the correct mode. let prover_init_state = AHPForR1CS::prover_init(&index_pk.index, c)?; let public_input = prover_init_state.public_input(); - let mut fs_rng = FS::initialize( - &to_bytes![&Self::PROTOCOL_NAME, &index_pk.index_vk, &public_input].unwrap(), + let mut fs_rng = S::default(); + absorb!( + &mut fs_rng, + &Self::PROTOCOL_NAME, + to_bytes(&index_pk.index_vk), + &to_bytes(&public_input) ); // -------------------------------------------------------------------- @@ -176,8 +197,17 @@ impl>, FS: FiatSha ) .map_err(Error::from_pc_err)?; end_timer!(first_round_comm_time); + let fcinput = first_comms + .iter() + .map(|p| p.commitment().clone()) + .collect::>(); - fs_rng.absorb(&to_bytes![first_comms, prover_first_msg].unwrap()); + match prover_first_msg { + ProverMsg::FieldElements(ref elems) => { + absorb!(&mut fs_rng, &to_bytes(&fcinput), &to_bytes(elems)); + } + ProverMsg::EmptyMessage => fs_rng.absorb(&to_bytes(&fcinput)), + } let (verifier_first_msg, verifier_state) = AHPForR1CS::verifier_first_round(index_pk.index_vk.index_info, &mut fs_rng)?; @@ -198,7 +228,16 @@ impl>, FS: FiatSha .map_err(Error::from_pc_err)?; end_timer!(second_round_comm_time); - fs_rng.absorb(&to_bytes![second_comms, prover_second_msg].unwrap()); + let scinput = second_comms + .iter() + .map(|p| p.commitment().clone()) + .collect::>(); + match prover_second_msg { + ProverMsg::FieldElements(ref elems) => { + absorb!(&mut fs_rng, &to_bytes(&scinput), &to_bytes(elems)); + } + ProverMsg::EmptyMessage => fs_rng.absorb(&to_bytes(&scinput)), + } let (verifier_second_msg, verifier_state) = AHPForR1CS::verifier_second_round(verifier_state, &mut fs_rng); @@ -218,7 +257,16 @@ impl>, FS: FiatSha .map_err(Error::from_pc_err)?; end_timer!(third_round_comm_time); - fs_rng.absorb(&to_bytes![third_comms, prover_third_msg].unwrap()); + let tcinput = third_comms + .iter() + .map(|p| p.commitment().clone()) + .collect::>(); + match prover_third_msg { + ProverMsg::FieldElements(ref elems) => { + absorb!(&mut fs_rng, &to_bytes(&tcinput), &to_bytes(elems)); + } + ProverMsg::EmptyMessage => fs_rng.absorb(&to_bytes(&tcinput)), + } let verifier_state = AHPForR1CS::verifier_third_round(verifier_state, &mut fs_rng); // -------------------------------------------------------------------- @@ -286,8 +334,9 @@ impl>, FS: FiatSha let evaluations = evaluations.into_iter().map(|x| x.1).collect::>(); end_timer!(eval_time); - fs_rng.absorb(&evaluations); - let opening_challenge: F = u128::rand(&mut fs_rng).into(); + fs_rng.absorb(&to_bytes(&evaluations)); + let mut opening_challenge: ChallengeGenerator<_, S> = + ChallengeGenerator::new_multivariate(fs_rng); let pc_proof = PC::open_combinations( &index_pk.committer_key, @@ -295,7 +344,7 @@ impl>, FS: FiatSha polynomials, &labeled_comms, &query_set, - opening_challenge, + &mut opening_challenge, &comm_rands, Some(zk_rng), ) @@ -313,9 +362,9 @@ impl>, FS: FiatSha /// Verify that a proof for the constrain system defined by `C` asserts that /// all constraints are satisfied. pub fn verify( - index_vk: &IndexVerifierKey, + index_vk: &IndexVerifierKey, public_input: &[F], - proof: &Proof, + proof: &Proof, rng: &mut R, ) -> Result> { let verifier_time = start_timer!(|| "Marlin::Verify"); @@ -332,15 +381,370 @@ impl>, FS: FiatSha unpadded_input }; - let mut fs_rng = - FS::initialize(&to_bytes![&Self::PROTOCOL_NAME, &index_vk, &public_input].unwrap()); + let mut fs_rng = S::default(); + absorb!( + &mut fs_rng, + &Self::PROTOCOL_NAME, + &to_bytes(index_vk), + &to_bytes(&public_input) + ); // -------------------------------------------------------------------- // First round let first_comms = &proof.commitments[0]; - fs_rng.absorb(&to_bytes![first_comms, proof.prover_messages[0]].unwrap()); + match &proof.prover_messages[0] { + ProverMsg::FieldElements(ref elems) => { + absorb!(&mut fs_rng, &to_bytes(first_comms), &to_bytes(elems)); + } + ProverMsg::EmptyMessage => fs_rng.absorb(&to_bytes(first_comms)), + } + let (_, verifier_state) = + AHPForR1CS::verifier_first_round(index_vk.index_info, &mut fs_rng)?; + // -------------------------------------------------------------------- + + // -------------------------------------------------------------------- + // Second round + let second_comms = &proof.commitments[1]; + match &proof.prover_messages[1] { + ProverMsg::FieldElements(ref elems) => { + absorb!(&mut fs_rng, &to_bytes(second_comms), &to_bytes(elems)); + } + ProverMsg::EmptyMessage => fs_rng.absorb(&to_bytes(second_comms)), + } + + let (_, verifier_state) = AHPForR1CS::verifier_second_round(verifier_state, &mut fs_rng); + // -------------------------------------------------------------------- + + // -------------------------------------------------------------------- + // Third round + let third_comms = &proof.commitments[2]; + match &proof.prover_messages[2] { + ProverMsg::FieldElements(ref elems) => { + absorb!(&mut fs_rng, &to_bytes(third_comms), &to_bytes(elems)); + } + ProverMsg::EmptyMessage => fs_rng.absorb(&to_bytes(third_comms)), + } + + let verifier_state = AHPForR1CS::verifier_third_round(verifier_state, &mut fs_rng); + // -------------------------------------------------------------------- + + // Collect degree bounds for commitments. Indexed polynomials have *no* + // degree bounds because we know the committed index polynomial has the + // correct degree. + let index_info = index_vk.index_info; + let degree_bounds = vec![None; index_vk.index_comms.len()] + .into_iter() + .chain(AHPForR1CS::prover_first_round_degree_bounds(&index_info)) + .chain(AHPForR1CS::prover_second_round_degree_bounds(&index_info)) + .chain(AHPForR1CS::prover_third_round_degree_bounds(&index_info)) + .collect::>(); + + // Gather commitments in one vector. + let commitments: Vec<_> = index_vk + .iter() + .chain(first_comms) + .chain(second_comms) + .chain(third_comms) + .cloned() + .zip(AHPForR1CS::::polynomial_labels()) + .zip(degree_bounds) + .map(|((c, l), d)| LabeledCommitment::new(l, c, d)) + .collect(); + + let (query_set, verifier_state) = + AHPForR1CS::verifier_query_set(verifier_state, &mut fs_rng); + + fs_rng.absorb(&to_bytes(&proof.evaluations)); + let mut opening_challenge: ChallengeGenerator = + ChallengeGenerator::new_multivariate(fs_rng); + + let mut evaluations = Evaluations::new(); + let mut evaluation_labels = Vec::new(); + for (poly_label, (_, point)) in query_set.iter().cloned() { + if AHPForR1CS::::LC_WITH_ZERO_EVAL.contains(&poly_label.as_ref()) { + evaluations.insert((poly_label, point), F::zero()); + } else { + evaluation_labels.push((poly_label, point)); + } + } + evaluation_labels.sort_by(|a, b| a.0.cmp(&b.0)); + for (q, eval) in evaluation_labels.into_iter().zip(&proof.evaluations) { + evaluations.insert(q, *eval); + } + + let lc_s = AHPForR1CS::construct_linear_combinations( + &public_input, + &evaluations, + &verifier_state, + )?; + + let evaluations_are_correct = PC::check_combinations( + &index_vk.verifier_key, + &lc_s, + &commitments, + &query_set, + &evaluations, + &proof.pc_proof, + &mut opening_challenge, + rng, + ) + .map_err(Error::from_pc_err)?; + + if !evaluations_are_correct { + eprintln!("PC::Check failed"); + } + end_timer!(verifier_time, || format!( + " PC::Check for AHP Verifier linear equations: {}", + evaluations_are_correct + )); + Ok(evaluations_are_correct) + } +} + +impl< + F: PrimeField + Absorb, + PC: PolynomialCommitment, S>, + S: CryptographicSponge + Default + RngCore, + > Marlin +{ + /// Create a zkSNARK asserting that the constraint system is satisfied. + /// Uses fast absorption of field elements into sponge + pub fn fast_prove, R: RngCore + CryptographicSponge>( + index_pk: &IndexProverKey, + c: C, + zk_rng: &mut R, + ) -> Result, Error> { + let prover_time = start_timer!(|| "Marlin::Prover"); + // Add check that c is in the correct mode. + + let prover_init_state = AHPForR1CS::prover_init(&index_pk.index, c)?; + let public_input = prover_init_state.public_input(); + let mut fs_rng = S::default(); + absorb!( + &mut fs_rng, + &Self::PROTOCOL_NAME, + to_bytes(&index_pk.index_vk), + &public_input + ); + + // -------------------------------------------------------------------- + // First round + + let (prover_first_msg, prover_first_oracles, prover_state) = + AHPForR1CS::prover_first_round(prover_init_state, zk_rng)?; + + let first_round_comm_time = start_timer!(|| "Committing to first round polys"); + let (first_comms, first_comm_rands) = PC::commit( + &index_pk.committer_key, + prover_first_oracles.iter(), + Some(zk_rng), + ) + .map_err(Error::from_pc_err)?; + end_timer!(first_round_comm_time); + let fcinput = first_comms + .iter() + .map(|p| p.commitment().clone()) + .collect::>(); + + match prover_first_msg { + ProverMsg::FieldElements(ref elems) => { + absorb!(&mut fs_rng, &to_bytes(&fcinput), &elems); + } + ProverMsg::EmptyMessage => fs_rng.absorb(&to_bytes(&fcinput)), + } + + let (verifier_first_msg, verifier_state) = + AHPForR1CS::verifier_first_round(index_pk.index_vk.index_info, &mut fs_rng)?; + // -------------------------------------------------------------------- + + // -------------------------------------------------------------------- + // Second round + + let (prover_second_msg, prover_second_oracles, prover_state) = + AHPForR1CS::prover_second_round(&verifier_first_msg, prover_state, zk_rng); + + let second_round_comm_time = start_timer!(|| "Committing to second round polys"); + let (second_comms, second_comm_rands) = PC::commit( + &index_pk.committer_key, + prover_second_oracles.iter(), + Some(zk_rng), + ) + .map_err(Error::from_pc_err)?; + end_timer!(second_round_comm_time); + + let scinput = second_comms + .iter() + .map(|p| p.commitment().clone()) + .collect::>(); + match prover_second_msg { + ProverMsg::FieldElements(ref elems) => { + absorb!(&mut fs_rng, &to_bytes(&scinput), elems); + } + ProverMsg::EmptyMessage => fs_rng.absorb(&to_bytes(&scinput)), + } + + let (verifier_second_msg, verifier_state) = + AHPForR1CS::verifier_second_round(verifier_state, &mut fs_rng); + // -------------------------------------------------------------------- + + // -------------------------------------------------------------------- + // Third round + let (prover_third_msg, prover_third_oracles) = + AHPForR1CS::prover_third_round(&verifier_second_msg, prover_state, zk_rng)?; + let third_round_comm_time = start_timer!(|| "Committing to third round polys"); + let (third_comms, third_comm_rands) = PC::commit( + &index_pk.committer_key, + prover_third_oracles.iter(), + Some(zk_rng), + ) + .map_err(Error::from_pc_err)?; + end_timer!(third_round_comm_time); + + let tcinput = third_comms + .iter() + .map(|p| p.commitment().clone()) + .collect::>(); + match prover_third_msg { + ProverMsg::FieldElements(ref elems) => { + absorb!(&mut fs_rng, &to_bytes(&tcinput), elems); + } + ProverMsg::EmptyMessage => fs_rng.absorb(&to_bytes(&tcinput)), + } + + let verifier_state = AHPForR1CS::verifier_third_round(verifier_state, &mut fs_rng); + // -------------------------------------------------------------------- + + // Gather prover polynomials in one vector. + let polynomials: Vec<_> = index_pk + .index + .iter() + .chain(prover_first_oracles.iter()) + .chain(prover_second_oracles.iter()) + .chain(prover_third_oracles.iter()) + .collect(); + + // Gather commitments in one vector. + #[rustfmt::skip] + let commitments = vec![ + first_comms.iter().map(|p| p.commitment().clone()).collect(), + second_comms.iter().map(|p| p.commitment().clone()).collect(), + third_comms.iter().map(|p| p.commitment().clone()).collect(), + ]; + let labeled_comms: Vec<_> = index_pk + .index_vk + .iter() + .cloned() + .zip(&AHPForR1CS::::INDEXER_POLYNOMIALS) + .map(|(c, l)| LabeledCommitment::new(l.to_string(), c, None)) + .chain(first_comms.iter().cloned()) + .chain(second_comms.iter().cloned()) + .chain(third_comms.iter().cloned()) + .collect(); + + // Gather commitment randomness together. + let comm_rands: Vec = index_pk + .index_comm_rands + .clone() + .into_iter() + .chain(first_comm_rands) + .chain(second_comm_rands) + .chain(third_comm_rands) + .collect(); + + // Compute the AHP verifier's query set. + let (query_set, verifier_state) = + AHPForR1CS::verifier_query_set(verifier_state, &mut fs_rng); + let lc_s = AHPForR1CS::construct_linear_combinations( + &public_input, + &polynomials, + &verifier_state, + )?; + + let eval_time = start_timer!(|| "Evaluating linear combinations over query set"); + let mut evaluations = Vec::new(); + for (label, (_, point)) in &query_set { + let lc = lc_s + .iter() + .find(|lc| &lc.label == label) + .ok_or(ahp::Error::MissingEval(label.to_string()))?; + let eval = polynomials.get_lc_eval(&lc, *point)?; + if !AHPForR1CS::::LC_WITH_ZERO_EVAL.contains(&lc.label.as_ref()) { + evaluations.push((label.to_string(), eval)); + } + } + + evaluations.sort_by(|a, b| a.0.cmp(&b.0)); + let evaluations = evaluations.into_iter().map(|x| x.1).collect::>(); + end_timer!(eval_time); + + fs_rng.absorb(&evaluations); + let mut opening_challenge: ChallengeGenerator<_, S> = + ChallengeGenerator::new_multivariate(fs_rng); + + let pc_proof = PC::open_combinations( + &index_pk.committer_key, + &lc_s, + polynomials, + &labeled_comms, + &query_set, + &mut opening_challenge, + &comm_rands, + Some(zk_rng), + ) + .map_err(Error::from_pc_err)?; + + // Gather prover messages together. + let prover_messages = vec![prover_first_msg, prover_second_msg, prover_third_msg]; + + let proof = Proof::new(commitments, evaluations, prover_messages, pc_proof); + proof.print_size_info(); + end_timer!(prover_time); + Ok(proof) + } + + /// Verify that a proof for the constrain system defined by `C` asserts that + /// all constraints are satisfied. + /// Uses fast absorption of field elements into sponge + pub fn fast_verify( + index_vk: &IndexVerifierKey, + public_input: &[F], + proof: &Proof, + rng: &mut R, + ) -> Result> { + let verifier_time = start_timer!(|| "Marlin::Verify"); + + let public_input = { + let domain_x = GeneralEvaluationDomain::::new(public_input.len() + 1).unwrap(); + + let mut unpadded_input = public_input.to_vec(); + unpadded_input.resize( + core::cmp::max(public_input.len(), domain_x.size() - 1), + F::zero(), + ); + + unpadded_input + }; + + let mut fs_rng = S::default(); + absorb!( + &mut fs_rng, + &Self::PROTOCOL_NAME, + &to_bytes(index_vk), + &public_input + ); + + // -------------------------------------------------------------------- + // First round + + let first_comms = &proof.commitments[0]; + match &proof.prover_messages[0] { + ProverMsg::FieldElements(ref elems) => { + absorb!(&mut fs_rng, &to_bytes(first_comms), elems); + } + ProverMsg::EmptyMessage => fs_rng.absorb(&to_bytes(first_comms)), + } let (_, verifier_state) = AHPForR1CS::verifier_first_round(index_vk.index_info, &mut fs_rng)?; // -------------------------------------------------------------------- @@ -348,7 +752,12 @@ impl>, FS: FiatSha // -------------------------------------------------------------------- // Second round let second_comms = &proof.commitments[1]; - fs_rng.absorb(&to_bytes![second_comms, proof.prover_messages[1]].unwrap()); + match &proof.prover_messages[1] { + ProverMsg::FieldElements(ref elems) => { + absorb!(&mut fs_rng, &to_bytes(second_comms), elems); + } + ProverMsg::EmptyMessage => fs_rng.absorb(&to_bytes(second_comms)), + } let (_, verifier_state) = AHPForR1CS::verifier_second_round(verifier_state, &mut fs_rng); // -------------------------------------------------------------------- @@ -356,7 +765,12 @@ impl>, FS: FiatSha // -------------------------------------------------------------------- // Third round let third_comms = &proof.commitments[2]; - fs_rng.absorb(&to_bytes![third_comms, proof.prover_messages[2]].unwrap()); + match &proof.prover_messages[2] { + ProverMsg::FieldElements(ref elems) => { + absorb!(&mut fs_rng, &to_bytes(third_comms), elems); + } + ProverMsg::EmptyMessage => fs_rng.absorb(&to_bytes(third_comms)), + } let verifier_state = AHPForR1CS::verifier_third_round(verifier_state, &mut fs_rng); // -------------------------------------------------------------------- @@ -388,7 +802,8 @@ impl>, FS: FiatSha AHPForR1CS::verifier_query_set(verifier_state, &mut fs_rng); fs_rng.absorb(&proof.evaluations); - let opening_challenge: F = u128::rand(&mut fs_rng).into(); + let mut opening_challenge: ChallengeGenerator = + ChallengeGenerator::new_multivariate(fs_rng); let mut evaluations = Evaluations::new(); let mut evaluation_labels = Vec::new(); @@ -417,7 +832,7 @@ impl>, FS: FiatSha &query_set, &evaluations, &proof.pc_proof, - opening_challenge, + &mut opening_challenge, rng, ) .map_err(Error::from_pc_err)?; diff --git a/src/rng.rs b/src/rng.rs index efea8e5..5a35eef 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -1,80 +1,91 @@ use crate::Vec; -use ark_ff::{FromBytes, ToBytes}; -use ark_std::convert::From; -use ark_std::marker::PhantomData; -use ark_std::rand::{RngCore, SeedableRng}; -use digest::Digest; +use ark_crypto_primitives::sponge::poseidon::{ + find_poseidon_ark_and_mds, PoseidonConfig, PoseidonSponge, +}; +use ark_crypto_primitives::sponge::{Absorb, CryptographicSponge}; +use ark_ff::PrimeField; -/// An RNG suitable for Fiat-Shamir transforms -pub trait FiatShamirRng: RngCore { - /// Create a new `Self` with an initial input - fn initialize<'a, T: 'a + ToBytes>(initial_input: &'a T) -> Self; - /// Absorb new inputs into state - fn absorb<'a, T: 'a + ToBytes>(&mut self, new_input: &'a T); -} +use ark_std::rand::RngCore; /// A simple `FiatShamirRng` that refreshes its seed by hashing together the previous seed /// and the new seed material. -pub struct SimpleHashFiatShamirRng { - r: R, - seed: [u8; 32], - #[doc(hidden)] - digest: PhantomData, -} +/// Exposes a particular instantiation of the Poseidon sponge -impl RngCore for SimpleHashFiatShamirRng { +#[derive(Clone)] +pub struct SimplePoseidonRng(PoseidonSponge); + +impl RngCore for SimplePoseidonRng { #[inline] fn next_u32(&mut self) -> u32 { - self.r.next_u32() + self.0 + .squeeze_bits(32) + .iter() + .rev() + .fold(0, |acc, &bit| (acc << 1) | (bit as u32)) } #[inline] fn next_u64(&mut self) -> u64 { - self.r.next_u64() + self.0 + .squeeze_bits(64) + .iter() + .rev() + .fold(0, |acc, &bit| (acc << 1) | (bit as u64)) } #[inline] fn fill_bytes(&mut self, dest: &mut [u8]) { - self.r.fill_bytes(dest); + dest.copy_from_slice(self.0.squeeze_bytes(dest.len()).as_slice()); } #[inline] fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), ark_std::rand::Error> { - Ok(self.r.fill_bytes(dest)) + Ok(self.fill_bytes(dest)) } } -impl FiatShamirRng for SimpleHashFiatShamirRng -where - R::Seed: From<[u8; 32]>, -{ - /// Create a new `Self` by initializing with a fresh seed. - /// `self.seed = H(initial_input)`. - #[inline] - fn initialize<'a, T: 'a + ToBytes>(initial_input: &'a T) -> Self { - let mut bytes = Vec::new(); - initial_input - .write(&mut bytes) - .expect("failed to convert to bytes"); - let seed = FromBytes::read(D::digest(&bytes).as_ref()).expect("failed to get [u8; 32]"); - let r = R::from_seed(::from(seed)); - Self { - r, - seed: seed, - digest: PhantomData, - } +impl CryptographicSponge for SimplePoseidonRng { + type Config = PoseidonConfig; + + fn new(params: &Self::Config) -> Self { + Self(PoseidonSponge::new(params)) } - /// Refresh `self.seed` with new material. Achieved by setting - /// `self.seed = H(new_input || self.seed)`. - #[inline] - fn absorb<'a, T: 'a + ToBytes>(&mut self, new_input: &'a T) { - let mut bytes = Vec::new(); - new_input - .write(&mut bytes) - .expect("failed to convert to bytes"); - bytes.extend_from_slice(&self.seed); - self.seed = FromBytes::read(D::digest(&bytes).as_ref()).expect("failed to get [u8; 32]"); - self.r = R::from_seed(::from(self.seed)); + fn absorb(&mut self, input: &impl Absorb) { + self.0.absorb(input); + } + + fn squeeze_bytes(&mut self, num_bytes: usize) -> Vec { + self.0.squeeze_bytes(num_bytes) + } + + fn squeeze_bits(&mut self, num_bits: usize) -> Vec { + self.0.squeeze_bits(num_bits) + } +} + +/// Instantiate Poseidon sponge with default parameters +impl Default for SimplePoseidonRng { + fn default() -> Self { + // let default = + // Self(PoseidonSponge::new(&poseidon_parameters_for_test())) + let (alpha, rate, full_rounds, partial_rounds) = (17, 2, 8, 29); + let (ark, mds) = find_poseidon_ark_and_mds( + F::MODULUS_BIT_SIZE as u64, + rate, + full_rounds, + partial_rounds, + 0, + ); + let config = PoseidonConfig { + full_rounds: full_rounds as usize, + partial_rounds: partial_rounds as usize, + alpha: alpha as u64, + ark, + mds, + rate, + capacity: 1, + }; + SimplePoseidonRng(PoseidonSponge::new(&config)) } } diff --git a/src/test.rs b/src/test.rs index fc91e12..5fa1791 100644 --- a/src/test.rs +++ b/src/test.rs @@ -115,28 +115,35 @@ impl ConstraintSynthesizer for OutlineTestCircuit { mod marlin { use super::*; - use crate::{Marlin, SimpleHashFiatShamirRng}; + use crate::rng::SimplePoseidonRng; + use crate::Marlin; + use ark_crypto_primitives::sponge::CryptographicSponge; + use itertools::Itertools; use ark_bls12_381::{Bls12_381, Fr}; - use ark_ff::UniformRand; - use ark_poly::univariate::DensePolynomial; + use ark_poly::polynomial::univariate::DensePolynomial; use ark_poly_commit::marlin_pc::MarlinKZG10; use ark_std::ops::MulAssign; - use blake2::Blake2s; - use rand_chacha::ChaChaRng; + use ark_std::rand::RngCore; - type MultiPC = MarlinKZG10>; - type FS = SimpleHashFiatShamirRng; - type MarlinInst = Marlin; + type S = SimplePoseidonRng; + type MultiPC = MarlinKZG10, S>; + type MarlinInst = Marlin; fn test_circuit(num_constraints: usize, num_variables: usize) { - let rng = &mut ark_std::test_rng(); + let mut rng_seed = ark_std::test_rng(); + let mut rng: SimplePoseidonRng = SimplePoseidonRng::default(); + rng.absorb(&rng_seed.next_u64()); - let universal_srs = MarlinInst::universal_setup(100, 25, 300, rng).unwrap(); + let universal_srs = MarlinInst::universal_setup(100, 25, 300, &mut rng).unwrap(); for _ in 0..100 { - let a = Fr::rand(rng); - let b = Fr::rand(rng); + let (a, b) = rng + .squeeze_field_elements(2) + .iter() + .map(|x: &Fr| x.to_owned()) + .collect_tuple() + .unwrap(); let mut c = a; c.mul_assign(&b); let mut d = c; @@ -152,16 +159,17 @@ mod marlin { let (index_pk, index_vk) = MarlinInst::index(&universal_srs, circ.clone()).unwrap(); println!("Called index"); - let proof = MarlinInst::prove(&index_pk, circ, rng).unwrap(); + let proof = MarlinInst::prove(&index_pk, circ, &mut rng).unwrap(); println!("Called prover"); - assert!(MarlinInst::verify(&index_vk, &[c, d], &proof, rng).unwrap()); + assert!(MarlinInst::verify(&index_vk, &[c, d], &proof, &mut rng).unwrap()); println!("Called verifier"); println!("\nShould not verify (i.e. verifier messages should print below):"); - assert!(!MarlinInst::verify(&index_vk, &[a, a], &proof, rng).unwrap()); + assert!(!MarlinInst::verify(&index_vk, &[a, a], &proof, &mut rng).unwrap()); } } + #[test] fn prove_and_verify_with_tall_matrix_big() { let num_constraints = 100; @@ -205,9 +213,11 @@ mod marlin { #[test] /// Test on a constraint system that will trigger outlining. fn prove_and_test_outlining() { - let rng = &mut ark_std::test_rng(); + let mut rng_seed = ark_std::test_rng(); + let mut rng: SimplePoseidonRng = SimplePoseidonRng::default(); + rng.absorb(&rng_seed.next_u64()); - let universal_srs = MarlinInst::universal_setup(150, 150, 150, rng).unwrap(); + let universal_srs = MarlinInst::universal_setup(150, 150, 150, &mut rng).unwrap(); let circ = OutlineTestCircuit { field_phantom: PhantomData, @@ -216,15 +226,56 @@ mod marlin { let (index_pk, index_vk) = MarlinInst::index(&universal_srs, circ.clone()).unwrap(); println!("Called index"); - let proof = MarlinInst::prove(&index_pk, circ, rng).unwrap(); + let proof = MarlinInst::prove(&index_pk, circ, &mut rng).unwrap(); println!("Called prover"); let mut inputs = Vec::new(); for i in 0..5 { - inputs.push(Fr::from(i as u128)); + inputs.push(Fr::from(i)); } - assert!(MarlinInst::verify(&index_vk, &inputs, &proof, rng).unwrap()); + assert!(MarlinInst::verify(&index_vk, &inputs, &proof, &mut rng).unwrap()); println!("Called verifier"); } + + #[test] + /// Test fast proof and verify + fn fast_prove_and_test() { + let mut rng_seed = ark_std::test_rng(); + let mut rng: SimplePoseidonRng = SimplePoseidonRng::default(); + rng.absorb(&rng_seed.next_u64()); + + let universal_srs = MarlinInst::universal_setup(150, 150, 150, &mut rng).unwrap(); + + for _ in 0..100 { + let (a, b) = rng + .squeeze_field_elements(2) + .iter() + .map(|x: &Fr| x.to_owned()) + .collect_tuple() + .unwrap(); + let mut c = a; + c.mul_assign(&b); + let mut d = c; + d.mul_assign(&b); + + let circ = Circuit { + a: Some(a), + b: Some(b), + num_constraints:20, + num_variables:100, + }; + + let (index_pk, index_vk) = MarlinInst::index(&universal_srs, circ.clone()).unwrap(); + println!("Called index"); + + let proof = MarlinInst::prove(&index_pk, circ, &mut rng).unwrap(); + println!("Called prover"); + + assert!(MarlinInst::verify(&index_vk, &[c, d], &proof, &mut rng).unwrap()); + println!("Called verifier"); + println!("\nShould not verify (i.e. verifier messages should print below):"); + assert!(!MarlinInst::verify(&index_vk, &[a, a], &proof, &mut rng).unwrap()); + }; + } }