use crate::utils::encoding::{g1_to_fq_repr, g2_to_fq_repr}; use crate::utils::encoding::{G1Repr, G2Repr}; use crate::utils::HeaderInclusion; use crate::{ProtocolVerifierKey, MIT_SDPX_IDENTIFIER}; use ark_bn254::{Bn254, G1Affine}; use ark_poly_commit::kzg10::VerifierKey; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use askama::Template; use super::PRAGMA_KZG10_VERIFIER; #[derive(Template, Default)] #[template(path = "kzg10_verifier.askama.sol", ext = "sol")] pub struct KZG10Verifier { /// The generator of `G1`. pub(crate) g1: G1Repr, /// The generator of `G2`. pub(crate) g2: G2Repr, /// The verification key pub(crate) vk: G2Repr, /// Length of the trusted setup vector. pub(crate) g1_crs_len: usize, /// The trusted setup vector. pub(crate) g1_crs: Vec, } impl From for KZG10Verifier { fn from(data: KZG10VerifierKey) -> Self { Self { g1: g1_to_fq_repr(data.vk.g), g2: g2_to_fq_repr(data.vk.h), vk: g2_to_fq_repr(data.vk.beta_h), g1_crs_len: data.g1_crs_batch_points.len(), g1_crs: data .g1_crs_batch_points .iter() .map(|g1| g1_to_fq_repr(*g1)) .collect(), } } } #[derive(CanonicalDeserialize, CanonicalSerialize, Clone, PartialEq, Debug)] pub struct KZG10VerifierKey { pub vk: VerifierKey, pub g1_crs_batch_points: Vec, } impl From<(VerifierKey, Vec)> for KZG10VerifierKey { fn from(value: (VerifierKey, Vec)) -> Self { Self { vk: value.0, g1_crs_batch_points: value.1, } } } impl ProtocolVerifierKey for KZG10VerifierKey { const PROTOCOL_NAME: &'static str = "KZG"; fn render_as_template(self, pragma: Option) -> Vec { HeaderInclusion::::builder() .sdpx(MIT_SDPX_IDENTIFIER.to_string()) .pragma_version(pragma.unwrap_or(PRAGMA_KZG10_VERIFIER.to_string())) .template(self) .build() .render() .unwrap() .into_bytes() } } #[cfg(test)] mod tests { use super::KZG10VerifierKey; use crate::{ evm::{compile_solidity, Evm}, utils::HeaderInclusion, ProtocolVerifierKey, }; use ark_bn254::{Bn254, Fr}; use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge}; use ark_ec::{AffineRepr, CurveGroup}; use ark_ff::{BigInteger, PrimeField}; use ark_std::rand::{RngCore, SeedableRng}; use ark_std::Zero; use ark_std::{test_rng, UniformRand}; use askama::Template; use itertools::chain; use folding_schemes::{ commitment::{kzg::KZG, CommitmentScheme}, transcript::{poseidon::poseidon_canonical_config, Transcript}, }; use super::KZG10Verifier; use crate::verifiers::tests::{setup, DEFAULT_SETUP_LEN}; const FUNCTION_SELECTOR_KZG10_CHECK: [u8; 4] = [0x9e, 0x78, 0xcc, 0xf7]; #[test] fn kzg_vk_serde_roundtrip() { let (_, pk, vk, _, _, _) = setup(DEFAULT_SETUP_LEN); let kzg_vk = KZG10VerifierKey::from((vk, pk.powers_of_g[0..3].to_vec())); let mut bytes = vec![]; kzg_vk.serialize_protocol_verifier_key(&mut bytes).unwrap(); let obtained_kzg_vk = KZG10VerifierKey::deserialize_protocol_verifier_key(bytes.as_slice()).unwrap(); assert_eq!(kzg_vk, obtained_kzg_vk) } #[test] fn kzg_verifier_compiles() { let (_, kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN); let kzg_vk = KZG10VerifierKey::from((kzg_vk.clone(), kzg_pk.powers_of_g[0..3].to_vec())); let res = HeaderInclusion::::builder() .template(kzg_vk) .build() .render() .unwrap(); let kzg_verifier_bytecode = compile_solidity(res, "KZG10"); let mut evm = Evm::default(); _ = evm.create(kzg_verifier_bytecode); } #[test] fn kzg_verifier_accepts_and_rejects_proofs() { let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64()); let poseidon_config = poseidon_canonical_config::(); let transcript_p = &mut PoseidonSponge::::new(&poseidon_config); let transcript_v = &mut PoseidonSponge::::new(&poseidon_config); let (_, kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN); let kzg_vk = KZG10VerifierKey::from((kzg_vk.clone(), kzg_pk.powers_of_g[0..3].to_vec())); let v: Vec = std::iter::repeat_with(|| Fr::rand(&mut rng)) .take(DEFAULT_SETUP_LEN) .collect(); let cm = KZG::::commit(&kzg_pk, &v, &Fr::zero()).unwrap(); let proof = KZG::::prove(&kzg_pk, transcript_p, &cm, &v, &Fr::zero(), None).unwrap(); let template = HeaderInclusion::::builder() .template(kzg_vk) .build() .render() .unwrap(); let kzg_verifier_bytecode = compile_solidity(template, "KZG10"); let mut evm = Evm::default(); let verifier_address = evm.create(kzg_verifier_bytecode); let (cm_affine, proof_affine) = (cm.into_affine(), proof.proof.into_affine()); let (x_comm, y_comm) = cm_affine.xy().unwrap(); let (x_proof, y_proof) = proof_affine.xy().unwrap(); let y = proof.eval.into_bigint().to_bytes_be(); transcript_v.absorb_nonnative(&cm); let x = transcript_v.get_challenge(); let x = x.into_bigint().to_bytes_be(); let mut calldata: Vec = chain![ FUNCTION_SELECTOR_KZG10_CHECK, x_comm.into_bigint().to_bytes_be(), y_comm.into_bigint().to_bytes_be(), x_proof.into_bigint().to_bytes_be(), y_proof.into_bigint().to_bytes_be(), x.clone(), y, ] .collect(); let (_, output) = evm.call(verifier_address, calldata.clone()); assert_eq!(*output.last().unwrap(), 1); // change calldata to make it invalid let last_calldata_element = calldata.last_mut().unwrap(); *last_calldata_element = 0; let (_, output) = evm.call(verifier_address, calldata); assert_eq!(*output.last().unwrap(), 0); } }