sonobe/folding-schemes-solidity/src/verifiers/mod.rs
folding-schemes-solidity/src/verifiers/mod.rsRaw
1pub mod templates;
2
3#[cfg(test)]
4mod tests {
5 use crate::evm::{compile_solidity, save_solidity, Evm};
6 use crate::verifiers::templates::{Groth16Verifier, KZG10Verifier};
7 use ark_bn254::{Bn254, Fr, G1Projective as G1};
8 use ark_crypto_primitives::snark::{CircuitSpecificSetupSNARK, SNARK};
9 use ark_ec::{AffineRepr, CurveGroup};
10 use ark_ff::{BigInt, BigInteger, PrimeField};
11 use ark_groth16::Groth16;
12 use ark_poly_commit::kzg10::VerifierKey;
13 use ark_r1cs_std::alloc::AllocVar;
14 use ark_r1cs_std::eq::EqGadget;
15 use ark_r1cs_std::fields::fp::FpVar;
16 use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError};
17 use ark_std::rand::{RngCore, SeedableRng};
18 use ark_std::Zero;
19 use ark_std::{test_rng, UniformRand};
20 use askama::Template;
21 use folding_schemes::{
22 commitment::{
23 kzg::{KZGProver, KZGSetup, ProverKey},
24 CommitmentProver,
25 },
26 transcript::{
27 poseidon::{poseidon_test_config, PoseidonTranscript},
28 Transcript,
29 },
30 };
31 use itertools::chain;
32 use std::marker::PhantomData;
33
34 // Function signatures for proof verification on kzg10 and groth16 contracts
35 pub const FUNCTION_SIGNATURE_KZG10_CHECK: [u8; 4] = [0x9e, 0x78, 0xcc, 0xf7];
36 pub const FUNCTION_SIGNATURE_GROTH16_VERIFY_PROOF: [u8; 4] = [0x43, 0x75, 0x3b, 0x4d];
37
38 // Pragma statements for verifiers
39 pub const PRAGMA_GROTH16_VERIFIER: &str = "pragma solidity >=0.7.0 <0.9.0;"; // from snarkjs, avoid changing
40 pub const PRAGMA_KZG10_VERIFIER: &str = "pragma solidity >=0.8.1 <=0.8.4;";
41
42 struct TestAddCircuit<F: PrimeField> {
43 _f: PhantomData<F>,
44 pub x: u8,
45 pub y: u8,
46 pub z: u8,
47 }
48
49 impl<F: PrimeField> ConstraintSynthesizer<F> for TestAddCircuit<F> {
50 fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> Result<(), SynthesisError> {
51 let x = FpVar::<F>::new_witness(cs.clone(), || Ok(F::from(self.x)))?;
52 let y = FpVar::<F>::new_witness(cs.clone(), || Ok(F::from(self.y)))?;
53 let z = FpVar::<F>::new_input(cs.clone(), || Ok(F::from(self.z)))?;
54 let comp_z = x.clone() + y.clone();
55 comp_z.enforce_equal(&z)?;
56 Ok(())
57 }
58 }
59
60 #[test]
61 fn test_groth16_kzg10_decider_template_renders() {
62 let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64());
63 let (x, y, z) = (21, 21, 42);
64 let (_, vk) = {
65 let c = TestAddCircuit::<Fr> {
66 _f: PhantomData,
67 x,
68 y,
69 z,
70 };
71 Groth16::<Bn254>::setup(c, &mut rng).unwrap()
72 };
73 let groth16_template = Groth16Verifier::from(vk, None);
74 let (pk, vk): (ProverKey<G1>, VerifierKey<Bn254>) = KZGSetup::<Bn254>::setup(&mut rng, 5);
75 let kzg10_template = KZG10Verifier::from(&vk, &pk.powers_of_g[..5], None, None);
76 let decider_template = super::templates::Groth16KZG10DeciderVerifier {
77 groth16_verifier: groth16_template,
78 kzg10_verifier: kzg10_template,
79 };
80 save_solidity("decider.sol", &decider_template.render().unwrap());
81 }
82
83 #[test]
84 fn test_groth16_kzg10_decider_template_compiles() {
85 let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64());
86 let (x, y, z) = (21, 21, 42);
87 let (_, vk) = {
88 let c = TestAddCircuit::<Fr> {
89 _f: PhantomData,
90 x,
91 y,
92 z,
93 };
94 Groth16::<Bn254>::setup(c, &mut rng).unwrap()
95 };
96 // we dont specify any pragma values for both verifiers, the pragma from the decider takes over
97 let groth16_template = Groth16Verifier::from(vk, None);
98 let (pk, vk): (ProverKey<G1>, VerifierKey<Bn254>) = KZGSetup::<Bn254>::setup(&mut rng, 5);
99 let kzg10_template = KZG10Verifier::from(&vk, &pk.powers_of_g[..5], None, None);
100 let decider_template = super::templates::Groth16KZG10DeciderVerifier {
101 groth16_verifier: groth16_template,
102 kzg10_verifier: kzg10_template,
103 };
104 let decider_verifier_bytecode =
105 compile_solidity(decider_template.render().unwrap(), "NovaDecider");
106 let mut evm = Evm::default();
107 _ = evm.create(decider_verifier_bytecode);
108 }
109
110 #[test]
111 fn test_groth16_verifier_template_renders() {
112 let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64());
113 let (x, y, z) = (21, 21, 42);
114 let (_, vk) = {
115 let c = TestAddCircuit::<Fr> {
116 _f: PhantomData,
117 x,
118 y,
119 z,
120 };
121 Groth16::<Bn254>::setup(c, &mut rng).unwrap()
122 };
123 let template = Groth16Verifier::from(vk, Some(PRAGMA_GROTH16_VERIFIER.to_string()));
124 save_solidity("groth16_verifier.sol", &template.render().unwrap());
125 _ = template.render().unwrap();
126 }
127
128 #[test]
129 fn test_groth16_verifier_template_compiles() {
130 let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64());
131 let (x, y, z) = (21, 21, 42);
132 let (_, vk) = {
133 let c = TestAddCircuit::<Fr> {
134 _f: PhantomData,
135 x,
136 y,
137 z,
138 };
139 Groth16::<Bn254>::setup(c, &mut rng).unwrap()
140 };
141 let res = Groth16Verifier::from(vk, Some(PRAGMA_GROTH16_VERIFIER.to_string()))
142 .render()
143 .unwrap();
144 let groth16_verifier_bytecode = compile_solidity(res, "Verifier");
145 let mut evm = Evm::default();
146 _ = evm.create(groth16_verifier_bytecode);
147 }
148
149 #[test]
150 fn test_groth16_verifier_accepts_and_rejects_proofs() {
151 let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64());
152 let (x, y, z) = (21, 21, 42);
153 let (pk, vk) = {
154 let c = TestAddCircuit::<Fr> {
155 _f: PhantomData,
156 x,
157 y,
158 z,
159 };
160 Groth16::<Bn254>::setup(c, &mut rng).unwrap()
161 };
162 let c = TestAddCircuit::<Fr> {
163 _f: PhantomData,
164 x,
165 y,
166 z,
167 };
168 let proof = Groth16::<Bn254>::prove(&pk, c, &mut rng).unwrap();
169 let res = Groth16Verifier::from(vk, Some(PRAGMA_GROTH16_VERIFIER.to_string()))
170 .render()
171 .unwrap();
172 save_solidity("groth16_verifier.sol", &res);
173 let groth16_verifier_bytecode = compile_solidity(&res, "Verifier");
174 let mut evm = Evm::default();
175 let verifier_address = evm.create(groth16_verifier_bytecode);
176 let (a_x, a_y) = proof.a.xy().unwrap();
177 let (b_x, b_y) = proof.b.xy().unwrap();
178 let (c_x, c_y) = proof.c.xy().unwrap();
179 let mut calldata: Vec<u8> = chain![
180 FUNCTION_SIGNATURE_GROTH16_VERIFY_PROOF,
181 a_x.into_bigint().to_bytes_be(),
182 a_y.into_bigint().to_bytes_be(),
183 b_x.c1.into_bigint().to_bytes_be(),
184 b_x.c0.into_bigint().to_bytes_be(),
185 b_y.c1.into_bigint().to_bytes_be(),
186 b_y.c0.into_bigint().to_bytes_be(),
187 c_x.into_bigint().to_bytes_be(),
188 c_y.into_bigint().to_bytes_be(),
189 BigInt::from(Fr::from(z)).to_bytes_be(),
190 ]
191 .collect();
192 let (_, output) = evm.call(verifier_address, calldata.clone());
193 assert_eq!(*output.last().unwrap(), 1);
194
195 // change calldata to make it invalid
196 let last_calldata_element = calldata.last_mut().unwrap();
197 *last_calldata_element = 0;
198 let (_, output) = evm.call(verifier_address, calldata);
199 assert_eq!(*output.last().unwrap(), 0);
200 }
201
202 #[test]
203 fn test_kzg_verifier_template_renders() {
204 let rng = &mut test_rng();
205 let n = 10;
206 let (pk, vk): (ProverKey<G1>, VerifierKey<Bn254>) = KZGSetup::<Bn254>::setup(rng, n);
207 let template = KZG10Verifier::from(
208 &vk,
209 &pk.powers_of_g[..5],
210 Some(PRAGMA_KZG10_VERIFIER.to_string()),
211 None,
212 );
213 let res = template.render().unwrap();
214 assert!(res.contains(&vk.g.x.to_string()));
215 }
216
217 #[test]
218 fn test_kzg_verifier_compiles() {
219 let rng = &mut test_rng();
220 let n = 10;
221 let (pk, vk): (ProverKey<G1>, VerifierKey<Bn254>) = KZGSetup::<Bn254>::setup(rng, n);
222 let template = KZG10Verifier::from(
223 &vk,
224 &pk.powers_of_g[..5],
225 Some(PRAGMA_KZG10_VERIFIER.to_string()),
226 None,
227 );
228 let res = template.render().unwrap();
229 let kzg_verifier_bytecode = compile_solidity(res, "KZG10");
230 let mut evm = Evm::default();
231 _ = evm.create(kzg_verifier_bytecode);
232 }
233
234 #[test]
235 fn test_kzg_verifier_accepts_and_rejects_proofs() {
236 let rng = &mut test_rng();
237 let poseidon_config = poseidon_test_config::<Fr>();
238 let transcript_p = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
239 let transcript_v = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
240
241 let n = 10;
242 let (pk, vk): (ProverKey<G1>, VerifierKey<Bn254>) = KZGSetup::<Bn254>::setup(rng, n);
243 let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(rng)).take(n).collect();
244 let cm = KZGProver::<G1>::commit(&pk, &v, &Fr::zero()).unwrap();
245 let (eval, proof) =
246 KZGProver::<G1>::prove(&pk, transcript_p, &cm, &v, &Fr::zero()).unwrap();
247 let template = KZG10Verifier::from(
248 &vk,
249 &pk.powers_of_g[..5],
250 Some(PRAGMA_KZG10_VERIFIER.to_string()),
251 None,
252 );
253 let res = template.render().unwrap();
254 let kzg_verifier_bytecode = compile_solidity(res, "KZG10");
255 let mut evm = Evm::default();
256 let verifier_address = evm.create(kzg_verifier_bytecode);
257
258 let (cm_affine, proof_affine) = (cm.into_affine(), proof.into_affine());
259 let (x_comm, y_comm) = cm_affine.xy().unwrap();
260 let (x_proof, y_proof) = proof_affine.xy().unwrap();
261 let y = eval.into_bigint().to_bytes_be();
262
263 transcript_v.absorb_point(&cm).unwrap();
264 let x = transcript_v.get_challenge();
265
266 let x = x.into_bigint().to_bytes_be();
267 let mut calldata: Vec<u8> = chain![
268 FUNCTION_SIGNATURE_KZG10_CHECK,
269 x_comm.into_bigint().to_bytes_be(),
270 y_comm.into_bigint().to_bytes_be(),
271 x_proof.into_bigint().to_bytes_be(),
272 y_proof.into_bigint().to_bytes_be(),
273 x.clone(),
274 y,
275 ]
276 .collect();
277
278 let (_, output) = evm.call(verifier_address, calldata.clone());
279 assert_eq!(*output.last().unwrap(), 1);
280
281 // change calldata to make it invalid
282 let last_calldata_element = calldata.last_mut().unwrap();
283 *last_calldata_element = 0;
284 let (_, output) = evm.call(verifier_address, calldata);
285 assert_eq!(*output.last().unwrap(), 0);
286 }
287}