sonobe/solidity-verifiers/src/verifiers/kzg.rs
solidity-verifiers/src/verifiers/kzg.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, MIT_SDPX_IDENTIFIER};
5use ark_bn254::{Bn254, G1Affine};
6use ark_poly_commit::kzg10::VerifierKey;
7use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
8use askama::Template;
9
10use super::PRAGMA_KZG10_VERIFIER;
11
12#[derive(Template, Default)]
13#[template(path = "kzg10_verifier.askama.sol", ext = "sol")]
14pub struct KZG10Verifier {
15 /// The generator of `G1`.
16 pub(crate) g1: G1Repr,
17 /// The generator of `G2`.
18 pub(crate) g2: G2Repr,
19 /// The verification key
20 pub(crate) vk: G2Repr,
21 /// Length of the trusted setup vector.
22 pub(crate) g1_crs_len: usize,
23 /// The trusted setup vector.
24 pub(crate) g1_crs: Vec<G1Repr>,
25}
26
27impl From<KZG10VerifierKey> for KZG10Verifier {
28 fn from(data: KZG10VerifierKey) -> Self {
29 Self {
30 g1: g1_to_fq_repr(data.vk.g),
31 g2: g2_to_fq_repr(data.vk.h),
32 vk: g2_to_fq_repr(data.vk.beta_h),
33 g1_crs_len: data.g1_crs_batch_points.len(),
34 g1_crs: data
35 .g1_crs_batch_points
36 .iter()
37 .map(|g1| g1_to_fq_repr(*g1))
38 .collect(),
39 }
40 }
41}
42
43#[derive(CanonicalDeserialize, CanonicalSerialize, Clone, PartialEq, Debug)]
44pub struct KZG10VerifierKey {
45 pub vk: VerifierKey<Bn254>,
46 pub g1_crs_batch_points: Vec<G1Affine>,
47}
48
49impl From<(VerifierKey<Bn254>, Vec<G1Affine>)> for KZG10VerifierKey {
50 fn from(value: (VerifierKey<Bn254>, Vec<G1Affine>)) -> Self {
51 Self {
52 vk: value.0,
53 g1_crs_batch_points: value.1,
54 }
55 }
56}
57
58impl ProtocolVerifierKey for KZG10VerifierKey {
59 const PROTOCOL_NAME: &'static str = "KZG";
60
61 fn render_as_template(self, pragma: Option<String>) -> Vec<u8> {
62 HeaderInclusion::<KZG10Verifier>::builder()
63 .sdpx(MIT_SDPX_IDENTIFIER.to_string())
64 .pragma_version(pragma.unwrap_or(PRAGMA_KZG10_VERIFIER.to_string()))
65 .template(self)
66 .build()
67 .render()
68 .unwrap()
69 .into_bytes()
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::KZG10VerifierKey;
76 use crate::{
77 evm::{compile_solidity, Evm},
78 utils::HeaderInclusion,
79 ProtocolVerifierKey,
80 };
81 use ark_bn254::{Bn254, Fr, G1Projective as G1};
82 use ark_ec::{AffineRepr, CurveGroup};
83 use ark_ff::{BigInteger, PrimeField};
84 use ark_std::rand::{RngCore, SeedableRng};
85 use ark_std::Zero;
86 use ark_std::{test_rng, UniformRand};
87 use askama::Template;
88 use itertools::chain;
89
90 use folding_schemes::{
91 commitment::{kzg::KZG, CommitmentScheme},
92 transcript::{
93 poseidon::{poseidon_canonical_config, PoseidonTranscript},
94 Transcript,
95 },
96 };
97
98 use super::KZG10Verifier;
99 use crate::verifiers::tests::{setup, DEFAULT_SETUP_LEN};
100
101 const FUNCTION_SELECTOR_KZG10_CHECK: [u8; 4] = [0x9e, 0x78, 0xcc, 0xf7];
102
103 #[test]
104 fn kzg_vk_serde_roundtrip() {
105 let (_, pk, vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
106
107 let kzg_vk = KZG10VerifierKey::from((vk, pk.powers_of_g[0..3].to_vec()));
108 let mut bytes = vec![];
109 kzg_vk.serialize_protocol_verifier_key(&mut bytes).unwrap();
110 let obtained_kzg_vk =
111 KZG10VerifierKey::deserialize_protocol_verifier_key(bytes.as_slice()).unwrap();
112
113 assert_eq!(kzg_vk, obtained_kzg_vk)
114 }
115
116 #[test]
117 fn kzg_verifier_compiles() {
118 let (_, kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
119 let kzg_vk = KZG10VerifierKey::from((kzg_vk.clone(), kzg_pk.powers_of_g[0..3].to_vec()));
120
121 let res = HeaderInclusion::<KZG10Verifier>::builder()
122 .template(kzg_vk)
123 .build()
124 .render()
125 .unwrap();
126
127 let kzg_verifier_bytecode = compile_solidity(res, "KZG10");
128 let mut evm = Evm::default();
129 _ = evm.create(kzg_verifier_bytecode);
130 }
131
132 #[test]
133 fn kzg_verifier_accepts_and_rejects_proofs() {
134 let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64());
135 let poseidon_config = poseidon_canonical_config::<Fr>();
136 let transcript_p = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
137 let transcript_v = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
138
139 let (_, kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
140 let kzg_vk = KZG10VerifierKey::from((kzg_vk.clone(), kzg_pk.powers_of_g[0..3].to_vec()));
141
142 let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng))
143 .take(DEFAULT_SETUP_LEN)
144 .collect();
145 let cm = KZG::<Bn254>::commit(&kzg_pk, &v, &Fr::zero()).unwrap();
146 let proof = KZG::<Bn254>::prove(&kzg_pk, transcript_p, &cm, &v, &Fr::zero(), None).unwrap();
147 let template = HeaderInclusion::<KZG10Verifier>::builder()
148 .template(kzg_vk)
149 .build()
150 .render()
151 .unwrap();
152
153 let kzg_verifier_bytecode = compile_solidity(template, "KZG10");
154 let mut evm = Evm::default();
155 let verifier_address = evm.create(kzg_verifier_bytecode);
156
157 let (cm_affine, proof_affine) = (cm.into_affine(), proof.proof.into_affine());
158 let (x_comm, y_comm) = cm_affine.xy().unwrap();
159 let (x_proof, y_proof) = proof_affine.xy().unwrap();
160 let y = proof.eval.into_bigint().to_bytes_be();
161
162 transcript_v.absorb_point(&cm).unwrap();
163 let x = transcript_v.get_challenge();
164
165 let x = x.into_bigint().to_bytes_be();
166 let mut calldata: Vec<u8> = chain![
167 FUNCTION_SELECTOR_KZG10_CHECK,
168 x_comm.into_bigint().to_bytes_be(),
169 y_comm.into_bigint().to_bytes_be(),
170 x_proof.into_bigint().to_bytes_be(),
171 y_proof.into_bigint().to_bytes_be(),
172 x.clone(),
173 y,
174 ]
175 .collect();
176
177 let (_, output) = evm.call(verifier_address, calldata.clone());
178 assert_eq!(*output.last().unwrap(), 1);
179
180 // change calldata to make it invalid
181 let last_calldata_element = calldata.last_mut().unwrap();
182 *last_calldata_element = 0;
183 let (_, output) = evm.call(verifier_address, calldata);
184 assert_eq!(*output.last().unwrap(), 0);
185 }
186}