sonobe/solidity-verifiers/src/verifiers/g16.rs
solidity-verifiers/src/verifiers/g16.rsRaw
1use crate::utils::encoding::{g1_to_fq_repr, g2_to_fq_repr};
2use crate::utils::encoding::{G1Repr, G2Repr};
3use crate::utils::HeaderInclusion;
4use crate::{ProtocolVerifierKey, GPL3_SDPX_IDENTIFIER};
5use ark_bn254::Bn254;
6use ark_groth16::VerifyingKey as ArkVerifyingKey;
7use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
8use askama::Template;
9
10use super::PRAGMA_GROTH16_VERIFIER;
11
12#[derive(Template, Default)]
13#[template(path = "groth16_verifier.askama.sol", ext = "sol")]
14pub struct Groth16Verifier {
15 /// The `alpha * G`, where `G` is the generator of `G1`.
16 pub vkey_alpha_g1: G1Repr,
17 /// The `alpha * H`, where `H` is the generator of `G2`.
18 pub vkey_beta_g2: G2Repr,
19 /// The `gamma * H`, where `H` is the generator of `G2`.
20 pub vkey_gamma_g2: G2Repr,
21 /// The `delta * H`, where `H` is the generator of `G2`.
22 pub vkey_delta_g2: G2Repr,
23 /// Length of the `gamma_abc_g1` vector.
24 pub gamma_abc_len: usize,
25 /// The `gamma^{-1} * (beta * a_i + alpha * b_i + c_i) * H`, where `H` is the generator of `E::G1`.
26 pub gamma_abc_g1: Vec<G1Repr>,
27}
28
29impl From<Groth16VerifierKey> for Groth16Verifier {
30 fn from(g16_vk: Groth16VerifierKey) -> Self {
31 Self {
32 vkey_alpha_g1: g1_to_fq_repr(g16_vk.0.alpha_g1),
33 vkey_beta_g2: g2_to_fq_repr(g16_vk.0.beta_g2),
34 vkey_gamma_g2: g2_to_fq_repr(g16_vk.0.gamma_g2),
35 vkey_delta_g2: g2_to_fq_repr(g16_vk.0.delta_g2),
36 gamma_abc_len: g16_vk.0.gamma_abc_g1.len(),
37 gamma_abc_g1: g16_vk
38 .0
39 .gamma_abc_g1
40 .iter()
41 .copied()
42 .map(g1_to_fq_repr)
43 .collect(),
44 }
45 }
46}
47
48// Ideally this would be linked to the `Decider` trait in FoldingSchemes.
49// For now, this is the easiest as NovaCycleFold isn't clear target from where we can get all it's needed arguments.
50#[derive(CanonicalDeserialize, CanonicalSerialize, Clone, PartialEq, Debug)]
51pub struct Groth16VerifierKey(pub(crate) ArkVerifyingKey<Bn254>);
52
53impl From<ArkVerifyingKey<Bn254>> for Groth16VerifierKey {
54 fn from(value: ArkVerifyingKey<Bn254>) -> Self {
55 Self(value)
56 }
57}
58
59impl ProtocolVerifierKey for Groth16VerifierKey {
60 const PROTOCOL_NAME: &'static str = "Groth16";
61
62 fn render_as_template(self, pragma: Option<String>) -> Vec<u8> {
63 HeaderInclusion::<Groth16Verifier>::builder()
64 .sdpx(GPL3_SDPX_IDENTIFIER.to_string())
65 .pragma_version(pragma.unwrap_or(PRAGMA_GROTH16_VERIFIER.to_string()))
66 .template(self)
67 .build()
68 .render()
69 .unwrap()
70 .into_bytes()
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::Groth16VerifierKey;
77 use crate::{
78 evm::{compile_solidity, save_solidity, Evm},
79 ProtocolVerifierKey,
80 };
81 use ark_bn254::{Bn254, Fr};
82 use ark_crypto_primitives::snark::SNARK;
83 use ark_ec::AffineRepr;
84 use ark_ff::{BigInt, BigInteger, PrimeField};
85 use ark_groth16::Groth16;
86 use ark_std::rand::{RngCore, SeedableRng};
87 use ark_std::test_rng;
88 use askama::Template;
89 use itertools::chain;
90
91 use super::Groth16Verifier;
92 use crate::verifiers::tests::{setup, DEFAULT_SETUP_LEN};
93
94 pub const FUNCTION_SELECTOR_GROTH16_VERIFY_PROOF: [u8; 4] = [0x43, 0x75, 0x3b, 0x4d];
95
96 #[test]
97 fn groth16_vk_serde_roundtrip() {
98 let (_, _, _, _, vk, _) = setup(DEFAULT_SETUP_LEN);
99
100 let g16_vk = Groth16VerifierKey::from(vk);
101 let mut bytes = vec![];
102 g16_vk.serialize_protocol_verifier_key(&mut bytes).unwrap();
103 let obtained_g16_vk =
104 Groth16VerifierKey::deserialize_protocol_verifier_key(bytes.as_slice()).unwrap();
105
106 assert_eq!(g16_vk, obtained_g16_vk)
107 }
108
109 #[test]
110 fn test_groth16_verifier_accepts_and_rejects_proofs() {
111 let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64());
112 let (_, _, _, g16_pk, g16_vk, circuit) = setup(DEFAULT_SETUP_LEN);
113 let g16_vk = Groth16VerifierKey::from(g16_vk);
114
115 let proof = Groth16::<Bn254>::prove(&g16_pk, circuit, &mut rng).unwrap();
116 let res = Groth16Verifier::from(g16_vk).render().unwrap();
117 save_solidity("groth16_verifier.sol", &res);
118 let groth16_verifier_bytecode = compile_solidity(&res, "Verifier");
119 let mut evm = Evm::default();
120 let verifier_address = evm.create(groth16_verifier_bytecode);
121 let (a_x, a_y) = proof.a.xy().unwrap();
122 let (b_x, b_y) = proof.b.xy().unwrap();
123 let (c_x, c_y) = proof.c.xy().unwrap();
124 let mut calldata: Vec<u8> = chain![
125 FUNCTION_SELECTOR_GROTH16_VERIFY_PROOF,
126 a_x.into_bigint().to_bytes_be(),
127 a_y.into_bigint().to_bytes_be(),
128 b_x.c1.into_bigint().to_bytes_be(),
129 b_x.c0.into_bigint().to_bytes_be(),
130 b_y.c1.into_bigint().to_bytes_be(),
131 b_y.c0.into_bigint().to_bytes_be(),
132 c_x.into_bigint().to_bytes_be(),
133 c_y.into_bigint().to_bytes_be(),
134 BigInt::from(Fr::from(circuit.z)).to_bytes_be(),
135 ]
136 .collect();
137 let (_, output) = evm.call(verifier_address, calldata.clone());
138 assert_eq!(*output.last().unwrap(), 1);
139
140 // change calldata to make it invalid
141 let last_calldata_element = calldata.last_mut().unwrap();
142 *last_calldata_element = 0;
143 let (_, output) = evm.call(verifier_address, calldata);
144 assert_eq!(*output.last().unwrap(), 0);
145 }
146}