1use crate::provider::msm::batch_add;
3#[cfg(feature = "io")]
4use crate::provider::ptau::{read_points, write_points, PtauFileError};
5use crate::traits::evm_serde::EvmCompatSerde;
6use crate::{
7 errors::NovaError,
8 gadgets::utils::to_bignat_repr,
9 provider::traits::{DlogGroup, DlogGroupExt},
10 traits::{
11 commitment::{CommitmentEngineTrait, CommitmentTrait, Len},
12 AbsorbInRO2Trait, AbsorbInROTrait, Engine, Group, ROTrait, TranscriptReprTrait,
13 },
14};
15use core::{
16 fmt::Debug,
17 marker::PhantomData,
18 ops::{Add, Mul, MulAssign, Range},
19};
20use ff::Field;
21use num_integer::Integer;
22use num_traits::ToPrimitive;
23use rayon::prelude::*;
24use serde::{Deserialize, Serialize};
25use serde_with::serde_as;
26
27#[cfg(feature = "io")]
28const KEY_FILE_HEAD: [u8; 12] = *b"PEDERSEN_KEY";
29
30#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
32pub struct CommitmentKey<E: Engine>
33where
34 E::GE: DlogGroup,
35{
36 ck: Vec<<E::GE as DlogGroup>::AffineGroupElement>,
37 h: <E::GE as DlogGroup>::AffineGroupElement,
38}
39
40impl<E: Engine> CommitmentKey<E>
41where
42 E::GE: DlogGroup,
43{
44 pub fn generators(&self) -> &[<E::GE as DlogGroup>::AffineGroupElement] {
46 &self.ck
47 }
48}
49
50impl<E: Engine> Len for CommitmentKey<E>
51where
52 E::GE: DlogGroup,
53{
54 fn length(&self) -> usize {
55 self.ck.len()
56 }
57}
58
59#[serde_as]
61#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
62#[serde(bound = "")]
63pub struct DerandKey<E: Engine>
64where
65 E::GE: DlogGroup,
66{
67 #[serde_as(as = "EvmCompatSerde")]
68 h: <E::GE as DlogGroup>::AffineGroupElement,
69}
70
71#[serde_as]
73#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
74#[serde(bound = "")]
75pub struct Commitment<E: Engine>
76where
77 E::GE: DlogGroup,
78{
79 #[serde_as(as = "EvmCompatSerde")]
80 pub(crate) comm: E::GE,
81}
82
83impl<E: Engine> CommitmentTrait<E> for Commitment<E>
84where
85 E::GE: DlogGroup,
86{
87 fn to_coordinates(&self) -> (E::Base, E::Base, bool) {
88 self.comm.to_coordinates()
89 }
90}
91
92impl<E: Engine> Default for Commitment<E>
93where
94 E::GE: DlogGroup,
95{
96 fn default() -> Self {
97 Commitment {
98 comm: E::GE::zero(),
99 }
100 }
101}
102
103impl<E: Engine> TranscriptReprTrait<E::GE> for Commitment<E>
104where
105 E::GE: DlogGroup,
106{
107 fn to_transcript_bytes(&self) -> Vec<u8> {
108 let (x, y, is_infinity) = self.comm.to_coordinates();
109 let is_infinity_byte = (!is_infinity).into();
110 [
111 x.to_transcript_bytes(),
112 y.to_transcript_bytes(),
113 [is_infinity_byte].to_vec(),
114 ]
115 .concat()
116 }
117}
118
119impl<E: Engine> AbsorbInROTrait<E> for Commitment<E>
120where
121 E::GE: DlogGroup,
122{
123 fn absorb_in_ro(&self, ro: &mut E::RO) {
124 let (x, y, is_infinity) = self.comm.to_coordinates();
125 let (_, b, _, _) = E::GE::group_params();
129 if b != E::Base::ZERO {
130 let (x, y) = if is_infinity {
131 (E::Base::ZERO, E::Base::ZERO)
132 } else {
133 (x, y)
134 };
135 ro.absorb(x);
136 ro.absorb(y);
137 } else {
138 ro.absorb(x);
139 ro.absorb(y);
140 ro.absorb(if is_infinity {
141 E::Base::ONE
142 } else {
143 E::Base::ZERO
144 });
145 }
146 }
147}
148
149impl<E: Engine> AbsorbInRO2Trait<E> for Commitment<E>
150where
151 E::GE: DlogGroup,
152{
153 fn absorb_in_ro2(&self, ro: &mut E::RO2) {
154 let (x, y, is_infinity) = self.comm.to_coordinates();
155 let (_, b, _, _) = E::GE::group_params();
157 let (x, y) = if b != E::Base::ZERO && is_infinity {
158 (E::Base::ZERO, E::Base::ZERO)
159 } else {
160 (x, y)
161 };
162
163 let limbs_x = to_bignat_repr(&x);
165 let limbs_y = to_bignat_repr(&y);
166
167 for limb in limbs_x.iter().chain(limbs_y.iter()) {
168 ro.absorb(*limb);
169 }
170 if b == E::Base::ZERO {
172 ro.absorb(if is_infinity {
173 E::Scalar::ONE
174 } else {
175 E::Scalar::ZERO
176 });
177 }
178 }
179}
180
181impl<E: Engine> MulAssign<E::Scalar> for Commitment<E>
182where
183 E::GE: DlogGroup,
184{
185 fn mul_assign(&mut self, scalar: E::Scalar) {
186 *self = Commitment {
187 comm: self.comm * scalar,
188 };
189 }
190}
191
192impl<'b, E: Engine> Mul<&'b E::Scalar> for &'_ Commitment<E>
193where
194 E::GE: DlogGroup,
195{
196 type Output = Commitment<E>;
197 fn mul(self, scalar: &'b E::Scalar) -> Commitment<E> {
198 Commitment {
199 comm: self.comm * scalar,
200 }
201 }
202}
203
204impl<E: Engine> Mul<E::Scalar> for Commitment<E>
205where
206 E::GE: DlogGroup,
207{
208 type Output = Commitment<E>;
209
210 fn mul(self, scalar: E::Scalar) -> Commitment<E> {
211 Commitment {
212 comm: self.comm * scalar,
213 }
214 }
215}
216
217impl<E: Engine> Add for Commitment<E>
218where
219 E::GE: DlogGroup,
220{
221 type Output = Commitment<E>;
222
223 fn add(self, other: Commitment<E>) -> Commitment<E> {
224 Commitment {
225 comm: self.comm + other.comm,
226 }
227 }
228}
229
230#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
232pub struct CommitmentEngine<E: Engine> {
233 _p: PhantomData<E>,
234}
235
236impl<E: Engine> CommitmentKey<E>
237where
238 E::GE: DlogGroup,
239{
240 pub fn to_coordinates(&self) -> Vec<(E::Base, E::Base)> {
250 self
251 .ck
252 .par_iter()
253 .map(|c| {
254 let (x, y, is_infinity) = <E::GE as DlogGroup>::group(c).to_coordinates();
255 assert!(!is_infinity);
256 (x, y)
257 })
258 .collect()
259 }
260}
261
262impl<E: Engine> CommitmentEngineTrait<E> for CommitmentEngine<E>
263where
264 E::GE: DlogGroupExt,
265{
266 type CommitmentKey = CommitmentKey<E>;
267 type Commitment = Commitment<E>;
268 type DerandKey = DerandKey<E>;
269
270 fn setup(label: &'static [u8], n: usize) -> Result<Self::CommitmentKey, NovaError> {
271 let gens = E::GE::from_label(label, n.next_power_of_two() + 1);
272
273 let (h, ck) = gens.split_first().unwrap();
274
275 Ok(Self::CommitmentKey {
276 ck: ck.to_vec(),
277 h: *h,
278 })
279 }
280
281 fn derand_key(ck: &Self::CommitmentKey) -> Self::DerandKey {
282 Self::DerandKey { h: ck.h }
283 }
284
285 fn commit(ck: &Self::CommitmentKey, v: &[E::Scalar], r: &E::Scalar) -> Self::Commitment {
286 assert!(ck.ck.len() >= v.len());
287
288 Commitment {
289 comm: E::GE::vartime_multiscalar_mul(v, &ck.ck[..v.len()])
290 + <E::GE as DlogGroup>::group(&ck.h) * r,
291 }
292 }
293
294 fn commit_small<T: Integer + Into<u64> + Copy + Sync + ToPrimitive>(
295 ck: &Self::CommitmentKey,
296 v: &[T],
297 r: &E::Scalar,
298 ) -> Self::Commitment {
299 assert!(ck.ck.len() >= v.len());
300
301 Commitment {
302 comm: E::GE::vartime_multiscalar_mul_small(v, &ck.ck[..v.len()])
303 + <E::GE as DlogGroup>::group(&ck.h) * r,
304 }
305 }
306
307 fn commit_small_range<T: Integer + Into<u64> + Copy + Sync + ToPrimitive>(
308 ck: &Self::CommitmentKey,
309 v: &[T],
310 r: &<E as Engine>::Scalar,
311 range: Range<usize>,
312 max_num_bits: usize,
313 ) -> Self::Commitment {
314 let bases = &ck.ck[range.clone()];
315 let scalars = &v[range];
316
317 assert!(bases.len() == scalars.len());
318
319 let mut res =
320 E::GE::vartime_multiscalar_mul_small_with_max_num_bits(scalars, bases, max_num_bits);
321
322 if r != &E::Scalar::ZERO {
323 res += <E::GE as DlogGroup>::group(&ck.h) * r;
324 }
325
326 Commitment { comm: res }
327 }
328
329 fn derandomize(
330 dk: &Self::DerandKey,
331 commit: &Self::Commitment,
332 r: &E::Scalar,
333 ) -> Self::Commitment {
334 Commitment {
335 comm: commit.comm - <E::GE as DlogGroup>::group(&dk.h) * r,
336 }
337 }
338
339 #[cfg(feature = "io")]
340 fn load_setup(
341 reader: &mut (impl std::io::Read + std::io::Seek),
342 _label: &'static [u8],
343 n: usize,
344 ) -> Result<Self::CommitmentKey, PtauFileError> {
345 let num = n.next_power_of_two();
346 {
347 let mut head = [0u8; 12];
348 reader.read_exact(&mut head)?;
349 if head != KEY_FILE_HEAD {
350 return Err(PtauFileError::InvalidHead);
351 }
352 }
353
354 let points = read_points(reader, num + 1)?;
355
356 let (first, second) = points.split_at(1);
357
358 Ok(Self::CommitmentKey {
359 ck: second.to_vec(),
360 h: first[0],
361 })
362 }
363
364 fn ck_to_coordinates(ck: &Self::CommitmentKey) -> Vec<(E::Base, E::Base)> {
365 ck.to_coordinates()
366 }
367
368 fn ck_to_group_elements(ck: &Self::CommitmentKey) -> Vec<E::GE> {
369 ck.ck
370 .par_iter()
371 .map(|g| {
372 let ge = E::GE::group(g);
373 assert!(
374 ge != E::GE::zero(),
375 "CommitmentKey contains a generator at infinity"
376 );
377 ge
378 })
379 .collect()
380 }
381
382 #[cfg(feature = "io")]
383 fn save_setup(
384 ck: &Self::CommitmentKey,
385 writer: &mut impl std::io::Write,
386 ) -> Result<(), PtauFileError> {
387 writer.write_all(&KEY_FILE_HEAD)?;
388 let mut points = Vec::with_capacity(ck.ck.len() + 1);
389 points.push(ck.h);
390 points.extend(ck.ck.iter().cloned());
391 write_points(writer, points)
392 }
393
394 fn commit_sparse_binary(
395 ck: &Self::CommitmentKey,
396 non_zero_indices: &[usize],
397 r: &<E as Engine>::Scalar,
398 ) -> Self::Commitment {
399 let comm = batch_add(&ck.ck, non_zero_indices);
400 let mut comm = <E::GE as DlogGroup>::group(&comm.into());
401
402 if r != &E::Scalar::ZERO {
403 comm += <E::GE as DlogGroup>::group(&ck.h) * r;
404 }
405
406 Commitment { comm }
407 }
408}
409
410pub trait CommitmentKeyExtTrait<E: Engine>
412where
413 E::GE: DlogGroup,
414{
415 fn split_at(&self, n: usize) -> (Self, Self)
417 where
418 Self: Sized;
419
420 fn combine(&self, other: &Self) -> Self;
422
423 fn fold(&self, w1: &E::Scalar, w2: &E::Scalar) -> Self;
425
426 fn scale(&self, r: &E::Scalar) -> Self;
428
429 fn reinterpret_commitments_as_ck(
431 c: &[<E::CE as CommitmentEngineTrait<E>>::Commitment],
432 ) -> Result<Self, NovaError>
433 where
434 Self: Sized;
435}
436
437impl<E: Engine<CE = CommitmentEngine<E>>> CommitmentKeyExtTrait<E> for CommitmentKey<E>
438where
439 E::GE: DlogGroupExt,
440{
441 fn split_at(&self, n: usize) -> (CommitmentKey<E>, CommitmentKey<E>) {
442 (
443 CommitmentKey {
444 ck: self.ck[0..n].to_vec(),
445 h: self.h,
446 },
447 CommitmentKey {
448 ck: self.ck[n..].to_vec(),
449 h: self.h,
450 },
451 )
452 }
453
454 fn combine(&self, other: &CommitmentKey<E>) -> CommitmentKey<E> {
455 let ck = {
456 let mut c = self.ck.clone();
457 c.extend(other.ck.clone());
458 c
459 };
460 CommitmentKey { ck, h: self.h }
461 }
462
463 fn fold(&self, w1: &E::Scalar, w2: &E::Scalar) -> CommitmentKey<E> {
465 let w = vec![*w1, *w2];
466 let (L, R) = self.split_at(self.ck.len() / 2);
467
468 let ck = (0..self.ck.len() / 2)
469 .into_par_iter()
470 .map(|i| {
471 let bases = [L.ck[i], R.ck[i]].to_vec();
472 E::GE::vartime_multiscalar_mul(&w, &bases).affine()
473 })
474 .collect();
475
476 CommitmentKey { ck, h: self.h }
477 }
478
479 fn scale(&self, r: &E::Scalar) -> Self {
481 let ck_scaled = self
482 .ck
483 .clone()
484 .into_par_iter()
485 .map(|g| E::GE::vartime_multiscalar_mul(&[*r], &[g]).affine())
486 .collect();
487
488 CommitmentKey {
489 ck: ck_scaled,
490 h: self.h,
491 }
492 }
493
494 fn reinterpret_commitments_as_ck(c: &[Commitment<E>]) -> Result<Self, NovaError> {
496 let ck = (0..c.len())
497 .into_par_iter()
498 .map(|i| c[i].comm.affine())
499 .collect();
500
501 Ok(CommitmentKey {
503 ck,
504 h: E::GE::zero().affine(), })
508 }
509}
510
511#[cfg(feature = "io")]
512#[cfg(test)]
513mod tests {
514 use super::*;
515
516 use crate::{provider::GrumpkinEngine, CommitmentKey};
517 use std::{fs::File, io::BufWriter};
518
519 type E = GrumpkinEngine;
520
521 #[test]
522 fn test_key_save_load() {
523 let path = "/tmp/pedersen_test.keys";
524
525 const LABEL: &[u8; 4] = b"test";
526
527 let keys = CommitmentEngine::<E>::setup(LABEL, 100).unwrap();
528
529 CommitmentEngine::save_setup(&keys, &mut BufWriter::new(File::create(path).unwrap())).unwrap();
530
531 let keys_read = CommitmentEngine::load_setup(&mut File::open(path).unwrap(), LABEL, 100);
532
533 assert!(keys_read.is_ok());
534 let keys_read: CommitmentKey<E> = keys_read.unwrap();
535 assert_eq!(keys_read.ck.len(), keys.ck.len());
536 assert_eq!(keys_read.h, keys.h);
537 assert_eq!(keys_read.ck, keys.ck);
538 }
539}