1use ark_ec::{
10 models::short_weierstrass::Affine as SWJAffine, short_weierstrass::SWCurveConfig, AffineRepr,
11 CurveGroup, VariableBaseMSM,
12};
13use ark_ff::{BigInteger, Field, One, PrimeField, Zero};
14use ark_poly::univariate::DensePolynomial;
15use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
16use groupmap::{BWParameters, GroupMap};
17use mina_poseidon::{sponge::ScalarChallenge, FqSponge};
18use o1_utils::{field_helpers::product, ExtendedDensePolynomial as _};
19use rayon::prelude::*;
20use serde::{de::Visitor, Deserialize, Serialize};
21use serde_with::{
22 de::DeserializeAsWrap, ser::SerializeAsWrap, serde_as, DeserializeAs, SerializeAs,
23};
24use std::{
25 iter::Iterator,
26 marker::PhantomData,
27 ops::{Add, AddAssign, Sub},
28};
29
30#[serde_as]
43#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
44#[serde(bound = "C: CanonicalDeserialize + CanonicalSerialize")]
45pub struct PolyComm<C> {
46 #[serde_as(as = "Vec<o1_utils::serialization::SerdeAs>")]
47 pub chunks: Vec<C>,
48}
49
50impl<C> PolyComm<C>
51where
52 C: CommitmentCurve,
53{
54 pub fn chunk_commitment(&self, zeta_n: C::ScalarField) -> Self {
56 let mut res = C::Group::zero();
57 for chunk in self.chunks.iter().rev() {
61 res *= zeta_n;
62 res.add_assign(chunk);
63 }
64
65 PolyComm {
66 chunks: vec![res.into_affine()],
67 }
68 }
69}
70
71impl<F> PolyComm<F>
72where
73 F: Field,
74{
75 pub fn chunk_blinding(&self, zeta_n: F) -> F {
77 let mut res = F::zero();
78 for chunk in self.chunks.iter().rev() {
82 res *= zeta_n;
83 res += chunk
84 }
85 res
86 }
87}
88
89impl<'a, G> IntoIterator for &'a PolyComm<G> {
90 type Item = &'a G;
91 type IntoIter = std::slice::Iter<'a, G>;
92
93 fn into_iter(self) -> Self::IntoIter {
94 self.chunks.iter()
95 }
96}
97
98#[derive(Clone, Debug, Serialize, Deserialize)]
100pub struct BlindedCommitment<G>
101where
102 G: CommitmentCurve,
103{
104 pub commitment: PolyComm<G>,
105 pub blinders: PolyComm<G::ScalarField>,
106}
107
108impl<T> PolyComm<T> {
109 pub fn new(chunks: Vec<T>) -> Self {
110 Self { chunks }
111 }
112}
113
114impl<T, U> SerializeAs<PolyComm<T>> for PolyComm<U>
115where
116 U: SerializeAs<T>,
117{
118 fn serialize_as<S>(source: &PolyComm<T>, serializer: S) -> Result<S::Ok, S::Error>
119 where
120 S: serde::Serializer,
121 {
122 serializer.collect_seq(
123 source
124 .chunks
125 .iter()
126 .map(|e| SerializeAsWrap::<T, U>::new(e)),
127 )
128 }
129}
130
131impl<'de, T, U> DeserializeAs<'de, PolyComm<T>> for PolyComm<U>
132where
133 U: DeserializeAs<'de, T>,
134{
135 fn deserialize_as<D>(deserializer: D) -> Result<PolyComm<T>, D::Error>
136 where
137 D: serde::Deserializer<'de>,
138 {
139 struct SeqVisitor<T, U> {
140 marker: PhantomData<(T, U)>,
141 }
142
143 impl<'de, T, U> Visitor<'de> for SeqVisitor<T, U>
144 where
145 U: DeserializeAs<'de, T>,
146 {
147 type Value = PolyComm<T>;
148
149 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
150 formatter.write_str("a sequence")
151 }
152
153 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
154 where
155 A: serde::de::SeqAccess<'de>,
156 {
157 #[allow(clippy::redundant_closure_call)]
158 let mut chunks = vec![];
159
160 while let Some(value) = seq
161 .next_element()?
162 .map(|v: DeserializeAsWrap<T, U>| v.into_inner())
163 {
164 chunks.push(value);
165 }
166
167 Ok(PolyComm::new(chunks))
168 }
169 }
170
171 let visitor = SeqVisitor::<T, U> {
172 marker: PhantomData,
173 };
174 deserializer.deserialize_seq(visitor)
175 }
176}
177
178impl<A: Copy + Clone + CanonicalDeserialize + CanonicalSerialize> PolyComm<A> {
179 pub fn map<B, F>(&self, mut f: F) -> PolyComm<B>
180 where
181 F: FnMut(A) -> B,
182 B: CanonicalDeserialize + CanonicalSerialize,
183 {
184 let chunks = self.chunks.iter().map(|x| f(*x)).collect();
185 PolyComm::new(chunks)
186 }
187
188 pub fn len(&self) -> usize {
190 self.chunks.len()
191 }
192
193 pub fn is_empty(&self) -> bool {
195 self.chunks.is_empty()
196 }
197
198 pub fn zip<B: Copy + CanonicalDeserialize + CanonicalSerialize>(
201 &self,
202 other: &PolyComm<B>,
203 ) -> Option<PolyComm<(A, B)>> {
204 if self.chunks.len() != other.chunks.len() {
205 return None;
206 }
207 let chunks = self
208 .chunks
209 .iter()
210 .zip(other.chunks.iter())
211 .map(|(x, y)| (*x, *y))
212 .collect();
213 Some(PolyComm::new(chunks))
214 }
215
216 pub fn get_first_chunk(&self) -> A {
220 self.chunks[0]
221 }
222}
223
224pub fn shift_scalar<G: AffineRepr>(x: G::ScalarField) -> G::ScalarField
256where
257 G::BaseField: PrimeField,
258{
259 let n1 = <G::ScalarField as PrimeField>::MODULUS;
260 let n2 = <G::ScalarField as PrimeField>::BigInt::from_bits_le(
261 &<G::BaseField as PrimeField>::MODULUS.to_bits_le()[..],
262 );
263 let two: G::ScalarField = (2u64).into();
264 let two_pow = two.pow([<G::ScalarField as PrimeField>::MODULUS_BIT_SIZE as u64]);
265 if n1 < n2 {
266 (x - (two_pow + G::ScalarField::one())) / two
267 } else {
268 x - two_pow
269 }
270}
271
272impl<'a, C: AffineRepr> Add<&'a PolyComm<C>> for &PolyComm<C> {
273 type Output = PolyComm<C>;
274
275 fn add(self, other: &'a PolyComm<C>) -> PolyComm<C> {
276 let mut chunks = vec![];
277 let n1 = self.chunks.len();
278 let n2 = other.chunks.len();
279 for i in 0..std::cmp::max(n1, n2) {
280 let pt = if i < n1 && i < n2 {
281 (self.chunks[i] + other.chunks[i]).into_affine()
282 } else if i < n1 {
283 self.chunks[i]
284 } else {
285 other.chunks[i]
286 };
287 chunks.push(pt);
288 }
289 PolyComm::new(chunks)
290 }
291}
292
293impl<'a, C: AffineRepr + Sub<Output = C::Group>> Sub<&'a PolyComm<C>> for &PolyComm<C> {
294 type Output = PolyComm<C>;
295
296 fn sub(self, other: &'a PolyComm<C>) -> PolyComm<C> {
297 let mut chunks = vec![];
298 let n1 = self.chunks.len();
299 let n2 = other.chunks.len();
300 for i in 0..std::cmp::max(n1, n2) {
301 let pt = if i < n1 && i < n2 {
302 (self.chunks[i] - other.chunks[i]).into_affine()
303 } else if i < n1 {
304 self.chunks[i]
305 } else {
306 other.chunks[i]
307 };
308 chunks.push(pt);
309 }
310 PolyComm::new(chunks)
311 }
312}
313
314impl<C: AffineRepr> PolyComm<C> {
315 pub fn scale(&self, c: C::ScalarField) -> PolyComm<C> {
316 PolyComm {
317 chunks: self.chunks.iter().map(|g| g.mul(c).into_affine()).collect(),
318 }
319 }
320
321 pub fn multi_scalar_mul(com: &[&PolyComm<C>], elm: &[C::ScalarField]) -> Self {
329 assert_eq!(com.len(), elm.len());
330
331 if com.is_empty() || elm.is_empty() {
332 return Self::new(vec![C::zero()]);
333 }
334
335 let all_scalars: Vec<_> = elm.iter().map(|s| s.into_bigint()).collect();
336
337 let elems_size = Iterator::max(com.iter().map(|c| c.chunks.len())).unwrap();
338
339 let chunks = (0..elems_size)
340 .map(|chunk| {
341 let (points, scalars): (Vec<_>, Vec<_>) = com
342 .iter()
343 .zip(&all_scalars)
344 .filter_map(|(com, scalar)| com.chunks.get(chunk).map(|c| (c, scalar)))
346 .unzip();
347
348 let subchunk_size = std::cmp::max(points.len() / 2, 1);
353
354 points
355 .into_par_iter()
356 .chunks(subchunk_size)
357 .zip(scalars.into_par_iter().chunks(subchunk_size))
358 .map(|(psc, ssc)| C::Group::msm_bigint(&psc, &ssc).into_affine())
359 .reduce(C::zero, |x, y| (x + y).into())
360 })
361 .collect();
362
363 Self::new(chunks)
364 }
365}
366
367pub fn b_poly<F: Field>(chals: &[F], x: F) -> F {
397 let k = chals.len();
398
399 let mut pow_twos = vec![x];
400
401 for i in 1..k {
402 pow_twos.push(pow_twos[i - 1].square());
403 }
404
405 product((0..k).map(|i| F::one() + (chals[i] * pow_twos[k - 1 - i])))
406}
407
408pub fn b_poly_coefficients<F: Field>(chals: &[F]) -> Vec<F> {
435 let rounds = chals.len();
436 let s_length = 1 << rounds;
437 let mut s = vec![F::one(); s_length];
438 let mut k: usize = 0;
439 let mut pow: usize = 1;
440 for i in 1..s_length {
441 k += if i == pow { 1 } else { 0 };
442 pow <<= if i == pow { 1 } else { 0 };
443 s[i] = s[i - (pow >> 1)] * chals[rounds - 1 - (k - 1)];
444 }
445 s
446}
447
448pub fn squeeze_prechallenge<
449 const FULL_ROUNDS: usize,
450 Fq: Field,
451 G,
452 Fr: Field,
453 EFqSponge: FqSponge<Fq, G, Fr, FULL_ROUNDS>,
454>(
455 sponge: &mut EFqSponge,
456) -> ScalarChallenge<Fr> {
457 ScalarChallenge::new(sponge.challenge())
458}
459
460pub fn squeeze_challenge<
461 const FULL_ROUNDS: usize,
462 Fq: Field,
463 G,
464 Fr: PrimeField,
465 EFqSponge: FqSponge<Fq, G, Fr, FULL_ROUNDS>,
466>(
467 endo_r: &Fr,
468 sponge: &mut EFqSponge,
469) -> Fr {
470 squeeze_prechallenge(sponge).to_field(endo_r)
471}
472
473pub fn absorb_commitment<
474 const FULL_ROUNDS: usize,
475 Fq: Field,
476 G: Clone,
477 Fr: PrimeField,
478 EFqSponge: FqSponge<Fq, G, Fr, FULL_ROUNDS>,
479>(
480 sponge: &mut EFqSponge,
481 commitment: &PolyComm<G>,
482) {
483 sponge.absorb_g(&commitment.chunks);
484}
485
486pub trait CommitmentCurve: AffineRepr + Sub<Output = Self::Group> {
490 type Params: SWCurveConfig;
491 type Map: GroupMap<Self::BaseField>;
492
493 fn to_coordinates(&self) -> Option<(Self::BaseField, Self::BaseField)>;
494 fn of_coordinates(x: Self::BaseField, y: Self::BaseField) -> Self;
495}
496
497pub trait EndoCurve: CommitmentCurve {
501 fn combine_one(g1: &[Self], g2: &[Self], x2: Self::ScalarField) -> Vec<Self> {
503 crate::combine::window_combine(g1, g2, Self::ScalarField::one(), x2)
504 }
505
506 fn combine_one_endo(
508 endo_r: Self::ScalarField,
509 _endo_q: Self::BaseField,
510 g1: &[Self],
511 g2: &[Self],
512 x2: ScalarChallenge<Self::ScalarField>,
513 ) -> Vec<Self> {
514 crate::combine::window_combine(g1, g2, Self::ScalarField::one(), x2.to_field(&endo_r))
515 }
516
517 fn combine(
518 g1: &[Self],
519 g2: &[Self],
520 x1: Self::ScalarField,
521 x2: Self::ScalarField,
522 ) -> Vec<Self> {
523 crate::combine::window_combine(g1, g2, x1, x2)
524 }
525}
526
527impl<P: SWCurveConfig + Clone> CommitmentCurve for SWJAffine<P> {
528 type Params = P;
529 type Map = BWParameters<P>;
530
531 fn to_coordinates(&self) -> Option<(Self::BaseField, Self::BaseField)> {
532 if self.infinity {
533 None
534 } else {
535 Some((self.x, self.y))
536 }
537 }
538
539 fn of_coordinates(x: P::BaseField, y: P::BaseField) -> SWJAffine<P> {
540 SWJAffine::<P>::new_unchecked(x, y)
541 }
542}
543
544impl<P: SWCurveConfig + Clone> EndoCurve for SWJAffine<P> {
545 fn combine_one(g1: &[Self], g2: &[Self], x2: Self::ScalarField) -> Vec<Self> {
546 crate::combine::affine_window_combine_one(g1, g2, x2)
547 }
548
549 fn combine_one_endo(
550 _endo_r: Self::ScalarField,
551 endo_q: Self::BaseField,
552 g1: &[Self],
553 g2: &[Self],
554 x2: ScalarChallenge<Self::ScalarField>,
555 ) -> Vec<Self> {
556 crate::combine::affine_window_combine_one_endo(endo_q, g1, g2, x2)
557 }
558
559 fn combine(
560 g1: &[Self],
561 g2: &[Self],
562 x1: Self::ScalarField,
563 x2: Self::ScalarField,
564 ) -> Vec<Self> {
565 crate::combine::affine_window_combine(g1, g2, x1, x2)
566 }
567}
568
569#[allow(clippy::type_complexity)]
590pub fn combined_inner_product<F: PrimeField>(
591 polyscale: &F,
592 evalscale: &F,
593 polys: &[Vec<Vec<F>>],
595) -> F {
596 let mut res = F::zero();
598 let mut polyscale_i = F::one();
600
601 for evals_tr in polys.iter().filter(|evals_tr| !evals_tr[0].is_empty()) {
602 let evals: Vec<_> = (0..evals_tr[0].len())
606 .map(|i| evals_tr.iter().map(|v| v[i]).collect::<Vec<_>>())
607 .collect();
608
609 for eval in &evals {
618 let term = DensePolynomial::<F>::eval_polynomial(eval, *evalscale);
620 res += &(polyscale_i * term);
621 polyscale_i *= polyscale;
622 }
623 }
624 res
625}
626
627pub struct Evaluation<G>
629where
630 G: AffineRepr,
631{
632 pub commitment: PolyComm<G>,
637
638 pub evaluations: Vec<Vec<G::ScalarField>>,
644}
645
646pub struct BatchEvaluationProof<'a, G, EFqSponge, OpeningProof, const FULL_ROUNDS: usize>
648where
649 G: AffineRepr,
650 EFqSponge: FqSponge<G::BaseField, G, G::ScalarField, FULL_ROUNDS>,
651{
652 pub sponge: EFqSponge,
655 pub evaluations: Vec<Evaluation<G>>,
658 pub evaluation_points: Vec<G::ScalarField>,
661 pub polyscale: G::ScalarField,
664 pub evalscale: G::ScalarField,
666 pub opening: &'a OpeningProof,
668 pub combined_inner_product: G::ScalarField,
669}
670
671pub fn combine_commitments<G: CommitmentCurve>(
689 evaluations: &[Evaluation<G>],
690 scalars: &mut Vec<G::ScalarField>,
691 points: &mut Vec<G>,
692 polyscale: G::ScalarField,
693 rand_base: G::ScalarField,
694) {
695 let mut polyscale_i = G::ScalarField::one();
697
698 for Evaluation { commitment, .. } in evaluations.iter().filter(|x| !x.commitment.is_empty()) {
699 for comm_ch in &commitment.chunks {
701 scalars.push(rand_base * polyscale_i);
702 points.push(*comm_ch);
703
704 polyscale_i *= polyscale;
706 }
707 }
708}
709
710#[cfg(feature = "ocaml_types")]
711#[allow(non_local_definitions)]
712pub mod caml {
713 use super::PolyComm;
715 use ark_ec::AffineRepr;
716
717 #[derive(Clone, Debug, ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
718 pub struct CamlPolyComm<CamlG> {
719 pub unshifted: Vec<CamlG>,
720 pub shifted: Option<CamlG>,
721 }
722
723 impl<G, CamlG> From<PolyComm<G>> for CamlPolyComm<CamlG>
726 where
727 G: AffineRepr,
728 CamlG: From<G>,
729 {
730 fn from(polycomm: PolyComm<G>) -> Self {
731 Self {
732 unshifted: polycomm.chunks.into_iter().map(CamlG::from).collect(),
733 shifted: None,
734 }
735 }
736 }
737
738 impl<'a, G, CamlG> From<&'a PolyComm<G>> for CamlPolyComm<CamlG>
739 where
740 G: AffineRepr,
741 CamlG: From<G> + From<&'a G>,
742 {
743 fn from(polycomm: &'a PolyComm<G>) -> Self {
744 Self {
745 unshifted: polycomm.chunks.iter().map(Into::<CamlG>::into).collect(),
746 shifted: None,
747 }
748 }
749 }
750
751 impl<G, CamlG> From<CamlPolyComm<CamlG>> for PolyComm<G>
752 where
753 G: AffineRepr + From<CamlG>,
754 {
755 fn from(camlpolycomm: CamlPolyComm<CamlG>) -> PolyComm<G> {
756 assert!(
757 camlpolycomm.shifted.is_none(),
758 "mina#14628: Shifted commitments are deprecated and must not be used"
759 );
760 PolyComm {
761 chunks: camlpolycomm
762 .unshifted
763 .into_iter()
764 .map(Into::<G>::into)
765 .collect(),
766 }
767 }
768 }
769
770 impl<'a, G, CamlG> From<&'a CamlPolyComm<CamlG>> for PolyComm<G>
771 where
772 G: AffineRepr + From<&'a CamlG> + From<CamlG>,
773 {
774 fn from(camlpolycomm: &'a CamlPolyComm<CamlG>) -> PolyComm<G> {
775 assert!(
776 camlpolycomm.shifted.is_none(),
777 "mina#14628: Shifted commitments are deprecated and must not be used"
778 );
779 PolyComm {
780 chunks: camlpolycomm.unshifted.iter().map(Into::into).collect(),
782 }
783 }
784 }
785}
786
787#[cfg(test)]
788mod tests {
789 use super::*;
790 use mina_curves::pasta::Fp;
791 use std::str::FromStr;
792
793 #[test]
801 fn test_b_poly_regression() {
802 let chals: Vec<Fp> = (2..=16).map(|i| Fp::from(i as u64)).collect();
804
805 let zeta = Fp::from(17u64);
806 let zeta_omega = Fp::from(19u64);
807
808 let b_at_zeta = b_poly(&chals, zeta);
809 let b_at_zeta_omega = b_poly(&chals, zeta_omega);
810
811 let expected_at_zeta = Fp::from_str(
813 "21115683812642620361045381629886583866877919362491419134086003378733605776328",
814 )
815 .unwrap();
816 let expected_at_zeta_omega = Fp::from_str(
817 "2298325069360593860729719174291433577456794311517767070156020442825391962511",
818 )
819 .unwrap();
820
821 assert_eq!(b_at_zeta, expected_at_zeta, "b(zeta) mismatch");
822 assert_eq!(
823 b_at_zeta_omega, expected_at_zeta_omega,
824 "b(zeta*omega) mismatch"
825 );
826 }
827
828 #[test]
833 fn test_b_poly_coefficients_regression() {
834 let chals: Vec<Fp> = vec![
836 Fp::from(2u64),
837 Fp::from(3u64),
838 Fp::from(5u64),
839 Fp::from(7u64),
840 ];
841
842 let coeffs = b_poly_coefficients(&chals);
843
844 assert_eq!(coeffs.len(), 16, "Should have 2^4 = 16 coefficients");
845
846 let expected: Vec<Fp> = vec![
855 Fp::from(1u64), Fp::from(7u64), Fp::from(5u64), Fp::from(35u64), Fp::from(3u64), Fp::from(21u64), Fp::from(15u64), Fp::from(105u64), Fp::from(2u64), Fp::from(14u64), Fp::from(10u64), Fp::from(70u64), Fp::from(6u64), Fp::from(42u64), Fp::from(30u64), Fp::from(210u64), ];
872
873 assert_eq!(coeffs, expected, "Coefficients mismatch");
874 }
875
876 #[test]
879 fn test_b_poly_consistency() {
880 let chals: Vec<Fp> = (2..=10).map(|i| Fp::from(i as u64)).collect();
881 let coeffs = b_poly_coefficients(&chals);
882
883 for x_val in [1u64, 7, 13, 42, 100] {
885 let x = Fp::from(x_val);
886
887 let b_product = b_poly(&chals, x);
889
890 let mut b_coeffs = Fp::zero();
892 let mut x_pow = Fp::one();
893 for coeff in &coeffs {
894 b_coeffs += *coeff * x_pow;
895 x_pow *= x;
896 }
897
898 assert_eq!(
899 b_product, b_coeffs,
900 "b_poly and b_poly_coefficients inconsistent at x={}",
901 x_val
902 );
903 }
904 }
905}