rust_kzg_bn254_prover/
kzg.rs1use ark_bn254::{Fr, G1Affine, G1Projective};
2use ark_ec::{CurveGroup, VariableBaseMSM};
3use ark_poly::{EvaluationDomain, GeneralEvaluationDomain};
4use ark_std::{ops::Div, Zero};
5use num_traits::ToPrimitive;
6use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
7use rust_kzg_bn254_primitives::{
8 blob::Blob,
9 errors::KzgError,
10 helpers,
11 polynomial::{PolynomialCoeffForm, PolynomialEvalForm},
12};
13
14use crate::srs::SRS;
15
16#[derive(Debug, PartialEq, Clone)]
26pub struct KZG {
27 expanded_roots_of_unity: Vec<Fr>,
28}
29
30impl Default for KZG {
31 fn default() -> Self {
32 Self::new()
33 }
34}
35
36impl KZG {
37 pub fn new() -> Self {
38 Self {
39 expanded_roots_of_unity: vec![],
40 }
41 }
42
43 pub fn calculate_and_store_roots_of_unity(
66 &mut self,
67 length_of_data_after_padding: u64,
68 ) -> Result<(), KzgError> {
69 let roots_of_unity = helpers::calculate_roots_of_unity(length_of_data_after_padding)?;
70 self.expanded_roots_of_unity = roots_of_unity;
71 Ok(())
72 }
73
74 pub fn get_roots_of_unities(&self) -> Vec<Fr> {
75 self.expanded_roots_of_unity.clone()
76 }
77
78 pub fn get_nth_root_of_unity(&self, i: usize) -> Option<&Fr> {
80 self.expanded_roots_of_unity.get(i)
81 }
82
83 pub fn commit_eval_form(
85 &self,
86 polynomial: &PolynomialEvalForm,
87 srs: &SRS,
88 ) -> Result<G1Affine, KzgError> {
89 if polynomial.len() > srs.g1.len() {
90 return Err(KzgError::SerializationError(
91 "polynomial length is not correct".to_string(),
92 ));
93 }
94
95 let bases = self.g1_ifft(polynomial.len(), srs)?;
98
99 match G1Projective::msm(&bases, polynomial.evaluations()) {
100 Ok(res) => Ok(res.into_affine()),
101 Err(err) => Err(KzgError::CommitError(err.to_string())),
102 }
103 }
104
105 pub fn commit_coeff_form(
107 &self,
108 polynomial: &PolynomialCoeffForm,
109 srs: &SRS,
110 ) -> Result<G1Affine, KzgError> {
111 if polynomial.len() > srs.g1.len() {
112 return Err(KzgError::SerializationError(
113 "polynomial length is not correct".to_string(),
114 ));
115 }
116 let bases = srs.g1[..polynomial.len()].to_vec();
119
120 match G1Projective::msm(&bases, polynomial.coeffs()) {
121 Ok(res) => Ok(res.into_affine()),
122 Err(err) => Err(KzgError::CommitError(err.to_string())),
123 }
124 }
125
126 fn compute_proof_impl(
128 &self,
129 polynomial: &PolynomialEvalForm,
130 z_fr: &Fr,
131 srs: &SRS,
132 ) -> Result<G1Affine, KzgError> {
133 if polynomial.len() != self.expanded_roots_of_unity.len() {
135 return Err(KzgError::GenericError(
136 "inconsistent length between blob and root of unities".to_string(),
137 ));
138 }
139
140 let eval_fr = polynomial.evaluations();
141 let mut poly_shift: Vec<Fr> = Vec::with_capacity(eval_fr.len());
143
144 let y_fr = helpers::evaluate_polynomial_in_evaluation_form(polynomial, z_fr)?;
147
148 for fr in eval_fr {
151 poly_shift.push(*fr - y_fr);
152 }
153
154 let mut denom_poly = Vec::<Fr>::with_capacity(self.expanded_roots_of_unity.len());
156 for root_of_unity in self.expanded_roots_of_unity.iter().take(eval_fr.len()) {
157 denom_poly.push(*root_of_unity - z_fr);
158 }
159
160 let mut quotient_poly = Vec::<Fr>::with_capacity(self.expanded_roots_of_unity.len());
162
163 for i in 0..self.expanded_roots_of_unity.len() {
165 if denom_poly[i].is_zero() {
166 quotient_poly.push(self.compute_quotient_eval_on_domain(z_fr, eval_fr, &y_fr));
169 } else {
170 quotient_poly.push(poly_shift[i].div(denom_poly[i]));
172 }
173 }
174
175 let quotient_poly_eval_form = PolynomialEvalForm::new(quotient_poly);
176 self.commit_eval_form("ient_poly_eval_form, srs)
177 }
178
179 pub fn commit_blob(&self, blob: &Blob, srs: &SRS) -> Result<G1Affine, KzgError> {
182 let polynomial = blob.to_polynomial_eval_form();
183 self.commit_eval_form(&polynomial, srs)
184 }
185
186 pub fn compute_proof_with_known_z_fr_index(
187 &self,
188 polynomial: &PolynomialEvalForm,
189 index: u64,
190 srs: &SRS,
191 ) -> Result<G1Affine, KzgError> {
192 let usized_index = index.to_usize().ok_or(KzgError::GenericError(
194 "Index conversion to usize failed".to_string(),
195 ))?;
196
197 let z_fr = self
199 .get_nth_root_of_unity(usized_index)
200 .ok_or_else(|| KzgError::GenericError("Root of unity not found".to_string()))?;
201
202 self.compute_proof(polynomial, z_fr, srs)
206 }
207
208 pub fn compute_proof(
215 &self,
216 polynomial: &PolynomialEvalForm,
217 z_fr: &Fr,
218 srs: &SRS,
219 ) -> Result<G1Affine, KzgError> {
220 if polynomial.len() != self.expanded_roots_of_unity.len() {
222 return Err(KzgError::GenericError(
223 "inconsistent length between blob and root of unities".to_string(),
224 ));
225 }
226
227 self.compute_proof_impl(polynomial, z_fr, srs)
233 }
234
235 pub fn compute_quotient_eval_on_domain(&self, z_fr: &Fr, eval_fr: &[Fr], value_fr: &Fr) -> Fr {
237 let mut quotient = Fr::zero();
238 let mut fi: Fr = Fr::zero();
239 let mut numerator: Fr = Fr::zero();
240 let mut denominator: Fr = Fr::zero();
241 let mut temp: Fr = Fr::zero();
242
243 self.expanded_roots_of_unity
244 .iter()
245 .enumerate()
246 .for_each(|(i, omega_i)| {
247 if *omega_i == *z_fr {
248 return;
249 }
250 fi = eval_fr[i] - value_fr;
251 numerator = fi * omega_i;
252 denominator = z_fr - omega_i;
253 denominator *= z_fr;
254 temp = numerator.div(denominator);
255 quotient += temp;
256 });
257
258 quotient
259 }
260
261 pub fn g1_ifft(&self, length: usize, srs: &SRS) -> Result<Vec<G1Affine>, KzgError> {
263 if !length.is_power_of_two() {
265 return Err(KzgError::FFTError(
266 "length provided is not a power of 2".to_string(),
267 ));
268 }
269
270 let points_projective: Vec<G1Projective> = srs.g1[..length]
271 .par_iter()
272 .map(|&p| G1Projective::from(p))
273 .collect();
274 let ifft_result: Vec<_> = GeneralEvaluationDomain::<Fr>::new(length)
275 .ok_or(KzgError::FFTError(
276 "Could not perform IFFT due to domain consturction error".to_string(),
277 ))?
278 .ifft(&points_projective)
279 .par_iter()
280 .map(|p| p.into_affine())
281 .collect();
282
283 Ok(ifft_result)
284 }
285
286 pub fn compute_blob_proof(
288 &self,
289 blob: &Blob,
290 commitment: &G1Affine,
291 srs: &SRS,
292 ) -> Result<G1Affine, KzgError> {
293 if !commitment.is_on_curve() || !commitment.is_in_correct_subgroup_assuming_on_curve() {
296 return Err(KzgError::NotOnCurveError(
297 "commitment not on curve".to_string(),
298 ));
299 }
300
301 let blob_poly = blob.to_polynomial_eval_form();
304
305 let evaluation_challenge = helpers::compute_challenge(blob, commitment)?;
308
309 self.compute_proof_impl(&blob_poly, &evaluation_challenge, srs)
313 }
314}