From b7f6d5fc517735ac846726df8169850ac6ca1ccb Mon Sep 17 00:00:00 2001 From: Artem Storozhuk Date: Wed, 28 Feb 2024 17:21:50 +0200 Subject: [PATCH] chore: Move solidity-specific unit-test for IPA into separate module --- src/provider/ipa_pc.rs | 134 ++--------------------------------- src/provider/mod.rs | 3 +- src/provider/tests/ipa_pc.rs | 129 +++++++++++++++++++++++++++++++++ src/provider/tests/mod.rs | 124 ++++++++++++++++++++++++++++++++ src/provider/util/mod.rs | 123 -------------------------------- 5 files changed, 260 insertions(+), 253 deletions(-) create mode 100644 src/provider/tests/ipa_pc.rs create mode 100644 src/provider/tests/mod.rs diff --git a/src/provider/ipa_pc.rs b/src/provider/ipa_pc.rs index 36aab9edc..fb2f4b195 100644 --- a/src/provider/ipa_pc.rs +++ b/src/provider/ipa_pc.rs @@ -28,8 +28,8 @@ pub struct ProverKey { #[derive(Debug, Serialize)] #[serde(bound = "")] pub struct VerifierKey { - ck_v: Arc>, - ck_s: CommitmentKey, + pub(in crate::provider) ck_v: Arc>, + pub(in crate::provider) ck_s: CommitmentKey, } impl SimpleDigestible for VerifierKey {} @@ -149,9 +149,9 @@ impl InnerProductWitness { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(bound = "")] pub struct InnerProductArgument { - L_vec: Vec>, - R_vec: Vec>, - a_hat: E::Scalar, + pub(in crate::provider) L_vec: Vec>, + pub(in crate::provider) R_vec: Vec>, + pub(in crate::provider) a_hat: E::Scalar, } impl InnerProductArgument @@ -401,132 +401,8 @@ where #[cfg(test)] mod test { use crate::provider::ipa_pc::EvaluationEngine; - use crate::provider::util::solidity_compatibility_utils::{ - ec_points_to_json, field_elements_to_json, generate_pcs_solidity_unit_test_data, - }; use crate::provider::util::test_utils::prove_verify_from_num_vars; - use crate::provider::GrumpkinEngine; - use group::Curve; - - use crate::provider::pedersen::{CommitmentKey, CommitmentKeyExtTrait}; - use handlebars::Handlebars; - use serde_json::json; - use serde_json::{Map, Value}; - - static IPA_COMPATIBILITY_UNIT_TESTING_TEMPLATE: &str = " -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.16; -import \"@std/Test.sol\"; -import \"src/blocks/grumpkin/Grumpkin.sol\"; -import \"src/blocks/EqPolynomial.sol\"; -import \"src/Utilities.sol\"; -import \"src/blocks/IpaPcs.sol\"; - -contract IpaTest is Test { -function composeIpaInput() public pure returns (InnerProductArgument.IpaInputGrumpkin memory) { -Grumpkin.GrumpkinAffinePoint[] memory ck_v = new Grumpkin.GrumpkinAffinePoint[]({{ len ck_v }}); -{{ #each ck_v }} ck_v[{{ i }}]=Grumpkin.GrumpkinAffinePoint({{ x }}, {{y}});\n {{ /each }} - -Grumpkin.GrumpkinAffinePoint[] memory ck_s = new Grumpkin.GrumpkinAffinePoint[]({{ len ck_s }}); -{{ #each ck_s }} ck_s[{{ i }}]=Grumpkin.GrumpkinAffinePoint({{ x }}, {{y}});\n {{ /each }} - -uint256[] memory point = new uint256[]({{ len point }}); -{{ #each point }} point[{{ i }}]={{ val }};\n {{ /each }} - -Grumpkin.GrumpkinAffinePoint[] memory L_vec = new Grumpkin.GrumpkinAffinePoint[]({{ len L_vec }}); -{{ #each L_vec }} L_vec[{{ i }}]=Grumpkin.GrumpkinAffinePoint({{ x }}, {{y}});\n {{ /each }} - -Grumpkin.GrumpkinAffinePoint[] memory R_vec = new Grumpkin.GrumpkinAffinePoint[]({{ len R_vec }}); -{{ #each R_vec }} R_vec[{{ i }}]=Grumpkin.GrumpkinAffinePoint({{ x }}, {{y}});\n {{ /each }} - -uint256 a_hat = {{ a_hat }}; - -// InnerProductInstance -Grumpkin.GrumpkinAffinePoint memory commitment = Grumpkin.GrumpkinAffinePoint({{ commitment_x }}, {{ commitment_y }}); - -uint256 eval = {{ eval }}; - -return InnerProductArgument.IpaInputGrumpkin(ck_v, ck_s, point, L_vec, R_vec, commitment, eval, a_hat); -} - -function testIpaGrumpkinVerification_{{ num_vars }}_Variables() public { -InnerProductArgument.IpaInputGrumpkin memory input = composeIpaInput(); -assertTrue(InnerProductArgument.verifyGrumpkin(input, getTranscript())); -} - -function getTranscript() public pure returns (KeccakTranscriptLib.KeccakTranscript memory) { -// b\"TestEval\" in Rust -uint8[] memory label = new uint8[](8); -label[0] = 0x54; -label[1] = 0x65; -label[2] = 0x73; -label[3] = 0x74; -label[4] = 0x45; -label[5] = 0x76; -label[6] = 0x61; -label[7] = 0x6c; - -KeccakTranscriptLib.KeccakTranscript memory keccak_transcript = KeccakTranscriptLib.instantiate(label); -return keccak_transcript; -} -} -"; - - // To generate Solidity unit-test: - // cargo test test_solidity_compatibility_ipa --release -- --ignored --nocapture > ipa.t.sol - #[test] - #[ignore] - fn test_solidity_compatibility_ipa() { - let num_vars = 2; - - // Secondary part of verification is IPA over Grumpkin - let (commitment, point, eval, proof, vk) = - generate_pcs_solidity_unit_test_data::<_, EvaluationEngine>(num_vars); - - let num_vars_string = format!("{}", num_vars); - let eval_string = format!("{:?}", eval); - let commitment_x_string = format!("{:?}", commitment.comm.to_affine().x); - let commitment_y_string = format!("{:?}", commitment.comm.to_affine().y); - let proof_a_hat_string = format!("{:?}", proof.a_hat); - - let r_vec = CommitmentKey::::reinterpret_commitments_as_ck(&proof.R_vec) - .expect("can't reinterpred R_vec"); - let l_vec = CommitmentKey::::reinterpret_commitments_as_ck(&proof.L_vec) - .expect("can't reinterpred L_vec"); - - let r_vec_array = ec_points_to_json::(&r_vec.ck); - let l_vec_array = ec_points_to_json::(&l_vec.ck); - let point_array = field_elements_to_json::(&point); - let ckv_array = ec_points_to_json::(&vk.ck_v.ck); - let cks_array = ec_points_to_json::(&vk.ck_s.ck); - - let mut map = Map::new(); - map.insert("num_vars".to_string(), Value::String(num_vars_string)); - map.insert("eval".to_string(), Value::String(eval_string)); - map.insert( - "commitment_x".to_string(), - Value::String(commitment_x_string), - ); - map.insert( - "commitment_y".to_string(), - Value::String(commitment_y_string), - ); - map.insert("R_vec".to_string(), Value::Array(r_vec_array)); - map.insert("L_vec".to_string(), Value::Array(l_vec_array)); - map.insert("a_hat".to_string(), Value::String(proof_a_hat_string)); - map.insert("point".to_string(), Value::Array(point_array)); - map.insert("ck_v".to_string(), Value::Array(ckv_array)); - map.insert("ck_s".to_string(), Value::Array(cks_array)); - - let mut reg = Handlebars::new(); - reg - .register_template_string("ipa.t.sol", IPA_COMPATIBILITY_UNIT_TESTING_TEMPLATE) - .expect("can't register template"); - - let solidity_unit_test_source = reg.render("ipa.t.sol", &json!(map)).expect("can't render"); - println!("{}", solidity_unit_test_source); - } #[test] fn test_multiple_polynomial_size() { diff --git a/src/provider/mod.rs b/src/provider/mod.rs index 87ca35203..12f82ae46 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -19,6 +19,7 @@ pub(crate) mod util; // crate-private modules mod keccak; +mod tests; use crate::{ provider::{ @@ -167,7 +168,7 @@ impl CurveCycleEquipped for PallasEngine { } #[cfg(test)] -mod tests { +mod test { use crate::provider::{ bn256_grumpkin::{bn256, grumpkin}, secp_secq::{secp256k1, secq256k1}, diff --git a/src/provider/tests/ipa_pc.rs b/src/provider/tests/ipa_pc.rs new file mode 100644 index 000000000..bc61206dd --- /dev/null +++ b/src/provider/tests/ipa_pc.rs @@ -0,0 +1,129 @@ +#[cfg(test)] +mod test { + use crate::provider::ipa_pc::EvaluationEngine; + use crate::provider::tests::solidity_compatibility_utils::{ + ec_points_to_json, field_elements_to_json, generate_pcs_solidity_unit_test_data, + }; + + use crate::provider::GrumpkinEngine; + use group::Curve; + + use crate::provider::pedersen::{CommitmentKey, CommitmentKeyExtTrait}; + use handlebars::Handlebars; + use serde_json::json; + use serde_json::{Map, Value}; + + static IPA_COMPATIBILITY_UNIT_TESTING_TEMPLATE: &str = " +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.16; +import \"@std/Test.sol\"; +import \"src/blocks/grumpkin/Grumpkin.sol\"; +import \"src/blocks/EqPolynomial.sol\"; +import \"src/Utilities.sol\"; +import \"src/blocks/IpaPcs.sol\"; + +contract IpaTest is Test { +function composeIpaInput() public pure returns (InnerProductArgument.IpaInputGrumpkin memory) { +Grumpkin.GrumpkinAffinePoint[] memory ck_v = new Grumpkin.GrumpkinAffinePoint[]({{ len ck_v }}); +{{ #each ck_v }} ck_v[{{ i }}]=Grumpkin.GrumpkinAffinePoint({{ x }}, {{y}});\n {{ /each }} + +Grumpkin.GrumpkinAffinePoint[] memory ck_s = new Grumpkin.GrumpkinAffinePoint[]({{ len ck_s }}); +{{ #each ck_s }} ck_s[{{ i }}]=Grumpkin.GrumpkinAffinePoint({{ x }}, {{y}});\n {{ /each }} + +uint256[] memory point = new uint256[]({{ len point }}); +{{ #each point }} point[{{ i }}]={{ val }};\n {{ /each }} + +Grumpkin.GrumpkinAffinePoint[] memory L_vec = new Grumpkin.GrumpkinAffinePoint[]({{ len L_vec }}); +{{ #each L_vec }} L_vec[{{ i }}]=Grumpkin.GrumpkinAffinePoint({{ x }}, {{y}});\n {{ /each }} + +Grumpkin.GrumpkinAffinePoint[] memory R_vec = new Grumpkin.GrumpkinAffinePoint[]({{ len R_vec }}); +{{ #each R_vec }} R_vec[{{ i }}]=Grumpkin.GrumpkinAffinePoint({{ x }}, {{y}});\n {{ /each }} + +uint256 a_hat = {{ a_hat }}; + +// InnerProductInstance +Grumpkin.GrumpkinAffinePoint memory commitment = Grumpkin.GrumpkinAffinePoint({{ commitment_x }}, {{ commitment_y }}); + +uint256 eval = {{ eval }}; + +return InnerProductArgument.IpaInputGrumpkin(ck_v, ck_s, point, L_vec, R_vec, commitment, eval, a_hat); +} + +function testIpaGrumpkinVerification_{{ num_vars }}_Variables() public { +InnerProductArgument.IpaInputGrumpkin memory input = composeIpaInput(); +assertTrue(InnerProductArgument.verifyGrumpkin(input, getTranscript())); +} + +function getTranscript() public pure returns (KeccakTranscriptLib.KeccakTranscript memory) { +// b\"TestEval\" in Rust +uint8[] memory label = new uint8[](8); +label[0] = 0x54; +label[1] = 0x65; +label[2] = 0x73; +label[3] = 0x74; +label[4] = 0x45; +label[5] = 0x76; +label[6] = 0x61; +label[7] = 0x6c; + +KeccakTranscriptLib.KeccakTranscript memory keccak_transcript = KeccakTranscriptLib.instantiate(label); +return keccak_transcript; +} +} +"; + + // To generate Solidity unit-test: + // cargo test test_solidity_compatibility_ipa --release -- --ignored --nocapture > ipa.t.sol + #[test] + #[ignore] + fn test_solidity_compatibility_ipa() { + let num_vars = 2; + + // Secondary part of verification is IPA over Grumpkin + let (commitment, point, eval, proof, vk) = + generate_pcs_solidity_unit_test_data::<_, EvaluationEngine>(num_vars); + + let num_vars_string = format!("{}", num_vars); + let eval_string = format!("{:?}", eval); + let commitment_x_string = format!("{:?}", commitment.comm.to_affine().x); + let commitment_y_string = format!("{:?}", commitment.comm.to_affine().y); + let proof_a_hat_string = format!("{:?}", proof.a_hat); + + let r_vec = CommitmentKey::::reinterpret_commitments_as_ck(&proof.R_vec) + .expect("can't reinterpred R_vec"); + let l_vec = CommitmentKey::::reinterpret_commitments_as_ck(&proof.L_vec) + .expect("can't reinterpred L_vec"); + + let r_vec_array = ec_points_to_json::(&r_vec.ck); + let l_vec_array = ec_points_to_json::(&l_vec.ck); + let point_array = field_elements_to_json::(&point); + let ckv_array = ec_points_to_json::(&vk.ck_v.ck); + let cks_array = ec_points_to_json::(&vk.ck_s.ck); + + let mut map = Map::new(); + map.insert("num_vars".to_string(), Value::String(num_vars_string)); + map.insert("eval".to_string(), Value::String(eval_string)); + map.insert( + "commitment_x".to_string(), + Value::String(commitment_x_string), + ); + map.insert( + "commitment_y".to_string(), + Value::String(commitment_y_string), + ); + map.insert("R_vec".to_string(), Value::Array(r_vec_array)); + map.insert("L_vec".to_string(), Value::Array(l_vec_array)); + map.insert("a_hat".to_string(), Value::String(proof_a_hat_string)); + map.insert("point".to_string(), Value::Array(point_array)); + map.insert("ck_v".to_string(), Value::Array(ckv_array)); + map.insert("ck_s".to_string(), Value::Array(cks_array)); + + let mut reg = Handlebars::new(); + reg + .register_template_string("ipa.t.sol", IPA_COMPATIBILITY_UNIT_TESTING_TEMPLATE) + .expect("can't register template"); + + let solidity_unit_test_source = reg.render("ipa.t.sol", &json!(map)).expect("can't render"); + println!("{}", solidity_unit_test_source); + } +} diff --git a/src/provider/tests/mod.rs b/src/provider/tests/mod.rs new file mode 100644 index 000000000..39fafa52a --- /dev/null +++ b/src/provider/tests/mod.rs @@ -0,0 +1,124 @@ +mod ipa_pc; + +#[cfg(test)] +pub mod solidity_compatibility_utils { + use crate::provider::traits::DlogGroup; + use crate::spartan::polys::multilinear::MultilinearPolynomial; + use crate::traits::{ + commitment::CommitmentEngineTrait, evaluation::EvaluationEngineTrait, Engine, + }; + use group::prime::PrimeCurve; + use group::prime::PrimeCurveAffine; + use rand::rngs::StdRng; + use serde_json::{Map, Value}; + use std::sync::Arc; + + pub(crate) fn generate_pcs_solidity_unit_test_data>( + num_vars: usize, + ) -> ( + >::Commitment, + Vec, + E::Scalar, + EE::EvaluationArgument, + EE::VerifierKey, + ) { + use rand_core::SeedableRng; + + let mut rng = rand::rngs::StdRng::seed_from_u64(num_vars as u64); + + let (poly, point, eval) = + crate::provider::util::test_utils::random_poly_with_eval::(num_vars, &mut rng); + + // Mock commitment key. + let ck = E::CE::setup(b"test", 1 << num_vars); + let ck_arc = Arc::new(ck.clone()); + // Commits to the provided vector using the provided generators. + let commitment = E::CE::commit(&ck_arc, poly.evaluations()); + + let (proof, vk) = prove_verify_solidity::(ck_arc, &commitment, &poly, &point, &eval); + + (commitment, point, eval, proof, vk) + } + + fn prove_verify_solidity>( + ck: Arc<<::CE as CommitmentEngineTrait>::CommitmentKey>, + commitment: &<::CE as CommitmentEngineTrait>::Commitment, + poly: &MultilinearPolynomial<::Scalar>, + point: &[::Scalar], + eval: &::Scalar, + ) -> (EE::EvaluationArgument, EE::VerifierKey) { + use crate::traits::TranscriptEngineTrait; + + // Generate Prover and verifier key for given commitment key. + let ock = ck.clone(); + let (prover_key, verifier_key) = EE::setup(ck); + + // Generate proof. + let mut prover_transcript = E::TE::new(b"TestEval"); + let proof: EE::EvaluationArgument = EE::prove( + &*ock, + &prover_key, + &mut prover_transcript, + commitment, + poly.evaluations(), + point, + eval, + ) + .unwrap(); + let pcp = prover_transcript.squeeze(b"c").unwrap(); + + // Verify proof. + let mut verifier_transcript = E::TE::new(b"TestEval"); + EE::verify( + &verifier_key, + &mut verifier_transcript, + commitment, + point, + eval, + &proof, + ) + .unwrap(); + let pcv = verifier_transcript.squeeze(b"c").unwrap(); + + // Check if the prover transcript and verifier transcript are kept in the same state. + assert_eq!(pcp, pcv); + + (proof, verifier_key) + } + + pub(crate) fn field_elements_to_json(field_elements: &[E::Scalar]) -> Vec { + let mut value_vector = vec![]; + field_elements.iter().enumerate().for_each(|(i, fe)| { + let mut value = Map::new(); + value.insert("i".to_string(), Value::String(i.to_string())); + value.insert("val".to_string(), Value::String(format!("{:?}", fe))); + value_vector.push(Value::Object(value)); + }); + value_vector + } + + pub(crate) fn ec_points_to_json(ec_points: &[::Affine]) -> Vec + where + E: Engine, + E::GE: DlogGroup, + { + let mut value_vector = vec![]; + ec_points.iter().enumerate().for_each(|(i, ec_point)| { + let mut value = Map::new(); + let coordinates_info = ec_point.to_curve().to_coordinates(); + let not_infinity = !coordinates_info.2; + assert!(not_infinity); + value.insert("i".to_string(), Value::String(i.to_string())); + value.insert( + "x".to_string(), + Value::String(format!("{:?}", coordinates_info.0)), + ); + value.insert( + "y".to_string(), + Value::String(format!("{:?}", coordinates_info.1)), + ); + value_vector.push(Value::Object(value)); + }); + value_vector + } +} diff --git a/src/provider/util/mod.rs b/src/provider/util/mod.rs index 0d07304ac..c2d3483ae 100644 --- a/src/provider/util/mod.rs +++ b/src/provider/util/mod.rs @@ -205,126 +205,3 @@ pub mod test_utils { } } } - -#[cfg(test)] -pub mod solidity_compatibility_utils { - use crate::provider::traits::DlogGroup; - use crate::spartan::polys::multilinear::MultilinearPolynomial; - use crate::traits::{ - commitment::CommitmentEngineTrait, evaluation::EvaluationEngineTrait, Engine, - }; - use group::prime::PrimeCurve; - use group::prime::PrimeCurveAffine; - use rand::rngs::StdRng; - use serde_json::{Map, Value}; - use std::sync::Arc; - - pub(crate) fn generate_pcs_solidity_unit_test_data>( - num_vars: usize, - ) -> ( - >::Commitment, - Vec, - E::Scalar, - EE::EvaluationArgument, - EE::VerifierKey, - ) { - use rand_core::SeedableRng; - - let mut rng = rand::rngs::StdRng::seed_from_u64(num_vars as u64); - - let (poly, point, eval) = - crate::provider::util::test_utils::random_poly_with_eval::(num_vars, &mut rng); - - // Mock commitment key. - let ck = E::CE::setup(b"test", 1 << num_vars); - let ck_arc = Arc::new(ck.clone()); - // Commits to the provided vector using the provided generators. - let commitment = E::CE::commit(&ck_arc, poly.evaluations()); - - let (proof, vk) = prove_verify_solidity::(ck_arc, &commitment, &poly, &point, &eval); - - (commitment, point, eval, proof, vk) - } - - fn prove_verify_solidity>( - ck: Arc<<::CE as CommitmentEngineTrait>::CommitmentKey>, - commitment: &<::CE as CommitmentEngineTrait>::Commitment, - poly: &MultilinearPolynomial<::Scalar>, - point: &[::Scalar], - eval: &::Scalar, - ) -> (EE::EvaluationArgument, EE::VerifierKey) { - use crate::traits::TranscriptEngineTrait; - - // Generate Prover and verifier key for given commitment key. - let ock = ck.clone(); - let (prover_key, verifier_key) = EE::setup(ck); - - // Generate proof. - let mut prover_transcript = E::TE::new(b"TestEval"); - let proof: EE::EvaluationArgument = EE::prove( - &*ock, - &prover_key, - &mut prover_transcript, - commitment, - poly.evaluations(), - point, - eval, - ) - .unwrap(); - let pcp = prover_transcript.squeeze(b"c").unwrap(); - - // Verify proof. - let mut verifier_transcript = E::TE::new(b"TestEval"); - EE::verify( - &verifier_key, - &mut verifier_transcript, - commitment, - point, - eval, - &proof, - ) - .unwrap(); - let pcv = verifier_transcript.squeeze(b"c").unwrap(); - - // Check if the prover transcript and verifier transcript are kept in the same state. - assert_eq!(pcp, pcv); - - (proof, verifier_key) - } - - pub(crate) fn field_elements_to_json(field_elements: &[E::Scalar]) -> Vec { - let mut value_vector = vec![]; - field_elements.iter().enumerate().for_each(|(i, fe)| { - let mut value = Map::new(); - value.insert("i".to_string(), Value::String(i.to_string())); - value.insert("val".to_string(), Value::String(format!("{:?}", fe))); - value_vector.push(Value::Object(value)); - }); - value_vector - } - - pub(crate) fn ec_points_to_json(ec_points: &[::Affine]) -> Vec - where - E: Engine, - E::GE: DlogGroup, - { - let mut value_vector = vec![]; - ec_points.iter().enumerate().for_each(|(i, ec_point)| { - let mut value = Map::new(); - let coordinates_info = ec_point.to_curve().to_coordinates(); - let not_infinity = !coordinates_info.2; - assert!(not_infinity); - value.insert("i".to_string(), Value::String(i.to_string())); - value.insert( - "x".to_string(), - Value::String(format!("{:?}", coordinates_info.0)), - ); - value.insert( - "y".to_string(), - Value::String(format!("{:?}", coordinates_info.1)), - ); - value_vector.push(Value::Object(value)); - }); - value_vector - } -}