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};
82 use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge};
83 use ark_ec::{AffineRepr, CurveGroup};
84 use ark_ff::{BigInteger, PrimeField};
85 use ark_std::rand::{RngCore, SeedableRng};
86 use ark_std::Zero;
87 use ark_std::{test_rng, UniformRand};
88 use askama::Template;
89 use itertools::chain;
90
91 use folding_schemes::{
92 commitment::{kzg::KZG, CommitmentScheme},
93 transcript::{poseidon::poseidon_canonical_config, Transcript},
94 };
95
96 use super::KZG10Verifier;
97 use crate::verifiers::tests::{setup, DEFAULT_SETUP_LEN};
98
99 const FUNCTION_SELECTOR_KZG10_CHECK: [u8; 4] = [0x9e, 0x78, 0xcc, 0xf7];
100
101 #[test]
102 fn kzg_vk_serde_roundtrip() {
103 let (_, pk, vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
104
105 let kzg_vk = KZG10VerifierKey::from((vk, pk.powers_of_g[0..3].to_vec()));
106 let mut bytes = vec![];
107 kzg_vk.serialize_protocol_verifier_key(&mut bytes).unwrap();
108 let obtained_kzg_vk =
109 KZG10VerifierKey::deserialize_protocol_verifier_key(bytes.as_slice()).unwrap();
110
111 assert_eq!(kzg_vk, obtained_kzg_vk)
112 }
113
114 #[test]
115 fn kzg_verifier_compiles() {
116 let (_, kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
117 let kzg_vk = KZG10VerifierKey::from((kzg_vk.clone(), kzg_pk.powers_of_g[0..3].to_vec()));
118
119 let res = HeaderInclusion::<KZG10Verifier>::builder()
120 .template(kzg_vk)
121 .build()
122 .render()
123 .unwrap();
124
125 let kzg_verifier_bytecode = compile_solidity(res, "KZG10");
126 let mut evm = Evm::default();
127 _ = evm.create(kzg_verifier_bytecode);
128 }
129
130 #[test]
131 fn kzg_verifier_accepts_and_rejects_proofs() {
132 let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64());
133 let poseidon_config = poseidon_canonical_config::<Fr>();
134 let transcript_p = &mut PoseidonSponge::<Fr>::new(&poseidon_config);
135 let transcript_v = &mut PoseidonSponge::<Fr>::new(&poseidon_config);
136
137 let (_, kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
138 let kzg_vk = KZG10VerifierKey::from((kzg_vk.clone(), kzg_pk.powers_of_g[0..3].to_vec()));
139
140 let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng))
141 .take(DEFAULT_SETUP_LEN)
142 .collect();
143 let cm = KZG::<Bn254>::commit(&kzg_pk, &v, &Fr::zero()).unwrap();
144 let proof = KZG::<Bn254>::prove(&kzg_pk, transcript_p, &cm, &v, &Fr::zero(), None).unwrap();
145 let template = HeaderInclusion::<KZG10Verifier>::builder()
146 .template(kzg_vk)
147 .build()
148 .render()
149 .unwrap();
150
151 let kzg_verifier_bytecode = compile_solidity(template, "KZG10");
152 let mut evm = Evm::default();
153 let verifier_address = evm.create(kzg_verifier_bytecode);
154
155 let (cm_affine, proof_affine) = (cm.into_affine(), proof.proof.into_affine());
156 let (x_comm, y_comm) = cm_affine.xy().unwrap();
157 let (x_proof, y_proof) = proof_affine.xy().unwrap();
158 let y = proof.eval.into_bigint().to_bytes_be();
159
160 transcript_v.absorb_nonnative(&cm);
161 let x = transcript_v.get_challenge();
162
163 let x = x.into_bigint().to_bytes_be();
164 let mut calldata: Vec<u8> = chain![
165 FUNCTION_SELECTOR_KZG10_CHECK,
166 x_comm.into_bigint().to_bytes_be(),
167 y_comm.into_bigint().to_bytes_be(),
168 x_proof.into_bigint().to_bytes_be(),
169 y_proof.into_bigint().to_bytes_be(),
170 x.clone(),
171 y,
172 ]
173 .collect();
174
175 let (_, output) = evm.call(verifier_address, calldata.clone());
176 assert_eq!(*output.last().unwrap(), 1);
177
178 // change calldata to make it invalid
179 let last_calldata_element = calldata.last_mut().unwrap();
180 *last_calldata_element = 0;
181 let (_, output) = evm.call(verifier_address, calldata);
182 assert_eq!(*output.last().unwrap(), 0);
183 }
184}