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 #[must_use]
56 pub fn chunk_commitment(&self, zeta_n: C::ScalarField) -> Self {
57 let mut res = C::Group::zero();
58 for chunk in self.chunks.iter().rev() {
62 res *= zeta_n;
63 res.add_assign(chunk);
64 }
65
66 Self {
67 chunks: vec![res.into_affine()],
68 }
69 }
70}
71
72impl<F> PolyComm<F>
73where
74 F: Field,
75{
76 pub fn chunk_blinding(&self, zeta_n: F) -> F {
78 let mut res = F::zero();
79 for chunk in self.chunks.iter().rev() {
83 res *= zeta_n;
84 res += chunk;
85 }
86 res
87 }
88}
89
90impl<G> PolyComm<G> {
91 pub fn iter(&self) -> std::slice::Iter<'_, G> {
93 self.chunks.iter()
94 }
95}
96
97impl<'a, G> IntoIterator for &'a PolyComm<G> {
98 type Item = &'a G;
99 type IntoIter = std::slice::Iter<'a, G>;
100
101 fn into_iter(self) -> Self::IntoIter {
102 self.chunks.iter()
103 }
104}
105
106#[derive(Clone, Debug, Serialize, Deserialize)]
108pub struct BlindedCommitment<G>
109where
110 G: CommitmentCurve,
111{
112 pub commitment: PolyComm<G>,
113 pub blinders: PolyComm<G::ScalarField>,
114}
115
116impl<T> PolyComm<T> {
117 #[must_use]
118 pub const fn new(chunks: Vec<T>) -> Self {
119 Self { chunks }
120 }
121}
122
123impl<T, U> SerializeAs<PolyComm<T>> for PolyComm<U>
124where
125 U: SerializeAs<T>,
126{
127 fn serialize_as<S>(source: &PolyComm<T>, serializer: S) -> Result<S::Ok, S::Error>
128 where
129 S: serde::Serializer,
130 {
131 serializer.collect_seq(
132 source
133 .chunks
134 .iter()
135 .map(|e| SerializeAsWrap::<T, U>::new(e)),
136 )
137 }
138}
139
140impl<'de, T, U> DeserializeAs<'de, PolyComm<T>> for PolyComm<U>
141where
142 U: DeserializeAs<'de, T>,
143{
144 fn deserialize_as<D>(deserializer: D) -> Result<PolyComm<T>, D::Error>
145 where
146 D: serde::Deserializer<'de>,
147 {
148 struct SeqVisitor<T, U> {
149 marker: PhantomData<(T, U)>,
150 }
151
152 impl<'de, T, U> Visitor<'de> for SeqVisitor<T, U>
153 where
154 U: DeserializeAs<'de, T>,
155 {
156 type Value = PolyComm<T>;
157
158 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
159 formatter.write_str("a sequence")
160 }
161
162 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
163 where
164 A: serde::de::SeqAccess<'de>,
165 {
166 #[allow(clippy::redundant_closure_call)]
167 let mut chunks = vec![];
168
169 while let Some(value) = seq
170 .next_element()?
171 .map(|v: DeserializeAsWrap<T, U>| v.into_inner())
172 {
173 chunks.push(value);
174 }
175
176 Ok(PolyComm::new(chunks))
177 }
178 }
179
180 let visitor = SeqVisitor::<T, U> {
181 marker: PhantomData,
182 };
183 deserializer.deserialize_seq(visitor)
184 }
185}
186
187impl<A: Copy + Clone + CanonicalDeserialize + CanonicalSerialize> PolyComm<A> {
188 pub fn map<B, F>(&self, mut f: F) -> PolyComm<B>
189 where
190 F: FnMut(A) -> B,
191 B: CanonicalDeserialize + CanonicalSerialize,
192 {
193 let chunks = self.chunks.iter().map(|x| f(*x)).collect();
194 PolyComm::new(chunks)
195 }
196
197 #[must_use]
199 pub const fn len(&self) -> usize {
200 self.chunks.len()
201 }
202
203 #[must_use]
205 pub const fn is_empty(&self) -> bool {
206 self.chunks.is_empty()
207 }
208
209 #[must_use]
212 pub fn zip<B: Copy + CanonicalDeserialize + CanonicalSerialize>(
213 &self,
214 other: &PolyComm<B>,
215 ) -> Option<PolyComm<(A, B)>> {
216 if self.chunks.len() != other.chunks.len() {
217 return None;
218 }
219 let chunks = self
220 .chunks
221 .iter()
222 .zip(other.chunks.iter())
223 .map(|(x, y)| (*x, *y))
224 .collect();
225 Some(PolyComm::new(chunks))
226 }
227
228 #[must_use]
233 pub fn get_first_chunk(&self) -> A {
234 self.chunks[0]
235 }
236}
237
238pub fn shift_scalar<G: AffineRepr>(x: G::ScalarField) -> G::ScalarField
270where
271 G::BaseField: PrimeField,
272{
273 let n1 = <G::ScalarField as PrimeField>::MODULUS;
274 let n2 = <G::ScalarField as PrimeField>::BigInt::from_bits_le(
275 &<G::BaseField as PrimeField>::MODULUS.to_bits_le()[..],
276 );
277 let two: G::ScalarField = (2u64).into();
278 let two_pow = two.pow([u64::from(<G::ScalarField as PrimeField>::MODULUS_BIT_SIZE)]);
279 if n1 < n2 {
280 (x - (two_pow + G::ScalarField::one())) / two
281 } else {
282 x - two_pow
283 }
284}
285
286impl<'a, C: AffineRepr> Add<&'a PolyComm<C>> for &PolyComm<C> {
287 type Output = PolyComm<C>;
288
289 fn add(self, other: &'a PolyComm<C>) -> PolyComm<C> {
290 let mut chunks = vec![];
291 let n1 = self.chunks.len();
292 let n2 = other.chunks.len();
293 for i in 0..std::cmp::max(n1, n2) {
294 let pt = if i < n1 && i < n2 {
295 (self.chunks[i] + other.chunks[i]).into_affine()
296 } else if i < n1 {
297 self.chunks[i]
298 } else {
299 other.chunks[i]
300 };
301 chunks.push(pt);
302 }
303 PolyComm::new(chunks)
304 }
305}
306
307impl<'a, C: AffineRepr + Sub<Output = C::Group>> Sub<&'a PolyComm<C>> for &PolyComm<C> {
308 type Output = PolyComm<C>;
309
310 fn sub(self, other: &'a PolyComm<C>) -> PolyComm<C> {
311 let mut chunks = vec![];
312 let n1 = self.chunks.len();
313 let n2 = other.chunks.len();
314 for i in 0..std::cmp::max(n1, n2) {
315 let pt = if i < n1 && i < n2 {
316 (self.chunks[i] - other.chunks[i]).into_affine()
317 } else if i < n1 {
318 self.chunks[i]
319 } else {
320 other.chunks[i]
321 };
322 chunks.push(pt);
323 }
324 PolyComm::new(chunks)
325 }
326}
327
328impl<C: AffineRepr> PolyComm<C> {
329 #[must_use]
330 pub fn scale(&self, c: C::ScalarField) -> Self {
331 Self {
332 chunks: self.chunks.iter().map(|g| g.mul(c).into_affine()).collect(),
333 }
334 }
335
336 #[must_use]
346 pub fn multi_scalar_mul(com: &[&Self], elm: &[C::ScalarField]) -> Self {
347 assert_eq!(com.len(), elm.len());
348
349 if com.is_empty() || elm.is_empty() {
350 return Self::new(vec![C::zero()]);
351 }
352
353 let all_scalars: Vec<_> = elm.iter().map(|s| s.into_bigint()).collect();
354
355 let elems_size = Iterator::max(com.iter().map(|c| c.chunks.len())).unwrap();
356
357 let chunks = (0..elems_size)
358 .map(|chunk| {
359 let (points, scalars): (Vec<_>, Vec<_>) = com
360 .iter()
361 .zip(&all_scalars)
362 .filter_map(|(com, scalar)| com.chunks.get(chunk).map(|c| (c, scalar)))
364 .unzip();
365
366 let subchunk_size = std::cmp::max(points.len() / 2, 1);
371
372 points
373 .into_par_iter()
374 .chunks(subchunk_size)
375 .zip(scalars.into_par_iter().chunks(subchunk_size))
376 .map(|(psc, ssc)| C::Group::msm_bigint(&psc, &ssc).into_affine())
377 .reduce(C::zero, |x, y| (x + y).into())
378 })
379 .collect();
380
381 Self::new(chunks)
382 }
383}
384
385pub fn b_poly<F: Field>(chals: &[F], x: F) -> F {
415 let k = chals.len();
416
417 let mut pow_twos = vec![x];
418
419 for i in 1..k {
420 pow_twos.push(pow_twos[i - 1].square());
421 }
422
423 product((0..k).map(|i| F::one() + (chals[i] * pow_twos[k - 1 - i])))
424}
425
426pub fn b_poly_coefficients<F: Field>(chals: &[F]) -> Vec<F> {
453 let rounds = chals.len();
454 let s_length = 1 << rounds;
455 let mut s = vec![F::one(); s_length];
456 let mut k: usize = 0;
457 let mut pow: usize = 1;
458 for i in 1..s_length {
459 k += usize::from(i == pow);
460 pow <<= u32::from(i == pow);
461 s[i] = s[i - (pow >> 1)] * chals[rounds - 1 - (k - 1)];
462 }
463 s
464}
465
466pub fn squeeze_prechallenge<
467 const FULL_ROUNDS: usize,
468 Fq: Field,
469 G,
470 Fr: Field,
471 EFqSponge: FqSponge<Fq, G, Fr, FULL_ROUNDS>,
472>(
473 sponge: &mut EFqSponge,
474) -> ScalarChallenge<Fr> {
475 ScalarChallenge::new(sponge.challenge())
476}
477
478pub fn squeeze_challenge<
479 const FULL_ROUNDS: usize,
480 Fq: Field,
481 G,
482 Fr: PrimeField,
483 EFqSponge: FqSponge<Fq, G, Fr, FULL_ROUNDS>,
484>(
485 endo_r: &Fr,
486 sponge: &mut EFqSponge,
487) -> Fr {
488 squeeze_prechallenge(sponge).to_field(endo_r)
489}
490
491pub fn absorb_commitment<
492 const FULL_ROUNDS: usize,
493 Fq: Field,
494 G: Clone,
495 Fr: PrimeField,
496 EFqSponge: FqSponge<Fq, G, Fr, FULL_ROUNDS>,
497>(
498 sponge: &mut EFqSponge,
499 commitment: &PolyComm<G>,
500) {
501 sponge.absorb_g(&commitment.chunks);
502}
503
504pub trait CommitmentCurve: AffineRepr + Sub<Output = Self::Group> {
509 type Params: SWCurveConfig;
510 type Map: GroupMap<Self::BaseField>;
511
512 fn to_coordinates(&self) -> Option<(Self::BaseField, Self::BaseField)>;
513 fn of_coordinates(x: Self::BaseField, y: Self::BaseField) -> Self;
514}
515
516pub trait EndoCurve: CommitmentCurve {
521 fn combine_one(g1: &[Self], g2: &[Self], x2: Self::ScalarField) -> Vec<Self> {
523 crate::combine::window_combine(g1, g2, Self::ScalarField::one(), x2)
524 }
525
526 fn combine_one_endo(
528 endo_r: Self::ScalarField,
529 _endo_q: Self::BaseField,
530 g1: &[Self],
531 g2: &[Self],
532 x2: &ScalarChallenge<Self::ScalarField>,
533 ) -> Vec<Self> {
534 crate::combine::window_combine(g1, g2, Self::ScalarField::one(), x2.to_field(&endo_r))
535 }
536
537 fn combine(
538 g1: &[Self],
539 g2: &[Self],
540 x1: Self::ScalarField,
541 x2: Self::ScalarField,
542 ) -> Vec<Self> {
543 crate::combine::window_combine(g1, g2, x1, x2)
544 }
545}
546
547impl<P: SWCurveConfig + Clone> CommitmentCurve for SWJAffine<P> {
548 type Params = P;
549 type Map = BWParameters<P>;
550
551 fn to_coordinates(&self) -> Option<(Self::BaseField, Self::BaseField)> {
552 if self.infinity {
553 None
554 } else {
555 Some((self.x, self.y))
556 }
557 }
558
559 fn of_coordinates(x: P::BaseField, y: P::BaseField) -> Self {
560 Self::new_unchecked(x, y)
561 }
562}
563
564impl<P: SWCurveConfig + Clone> EndoCurve for SWJAffine<P> {
565 fn combine_one(g1: &[Self], g2: &[Self], x2: Self::ScalarField) -> Vec<Self> {
566 crate::combine::affine_window_combine_one(g1, g2, x2)
567 }
568
569 fn combine_one_endo(
570 _endo_r: Self::ScalarField,
571 endo_q: Self::BaseField,
572 g1: &[Self],
573 g2: &[Self],
574 x2: &ScalarChallenge<Self::ScalarField>,
575 ) -> Vec<Self> {
576 crate::combine::affine_window_combine_one_endo(endo_q, g1, g2, x2)
577 }
578
579 fn combine(
580 g1: &[Self],
581 g2: &[Self],
582 x1: Self::ScalarField,
583 x2: Self::ScalarField,
584 ) -> Vec<Self> {
585 crate::combine::affine_window_combine(g1, g2, x1, x2)
586 }
587}
588
589#[allow(clippy::type_complexity)]
610pub fn combined_inner_product<F: PrimeField>(
611 polyscale: &F,
612 evalscale: &F,
613 polys: &[Vec<Vec<F>>],
615) -> F {
616 let mut res = F::zero();
618 let mut polyscale_i = F::one();
620
621 for evals_tr in polys.iter().filter(|evals_tr| !evals_tr[0].is_empty()) {
622 let evals: Vec<_> = (0..evals_tr[0].len())
626 .map(|i| evals_tr.iter().map(|v| v[i]).collect::<Vec<_>>())
627 .collect();
628
629 for eval in &evals {
638 let term = DensePolynomial::<F>::eval_polynomial(eval, *evalscale);
640 res += &(polyscale_i * term);
641 polyscale_i *= polyscale;
642 }
643 }
644 res
645}
646
647pub struct Evaluation<G>
649where
650 G: AffineRepr,
651{
652 pub commitment: PolyComm<G>,
658
659 pub evaluations: Vec<Vec<G::ScalarField>>,
667}
668
669pub struct BatchEvaluationProof<'a, G, EFqSponge, OpeningProof, const FULL_ROUNDS: usize>
671where
672 G: AffineRepr,
673 EFqSponge: FqSponge<G::BaseField, G, G::ScalarField, FULL_ROUNDS>,
674{
675 pub sponge: EFqSponge,
678 pub evaluations: Vec<Evaluation<G>>,
681 pub evaluation_points: Vec<G::ScalarField>,
684 pub polyscale: G::ScalarField,
687 pub evalscale: G::ScalarField,
689 pub opening: &'a OpeningProof,
691 pub combined_inner_product: G::ScalarField,
692}
693
694pub fn combine_commitments<G: CommitmentCurve>(
713 evaluations: &[Evaluation<G>],
714 scalars: &mut Vec<G::ScalarField>,
715 points: &mut Vec<G>,
716 polyscale: G::ScalarField,
717 rand_base: G::ScalarField,
718) {
719 let mut polyscale_i = G::ScalarField::one();
721
722 for Evaluation { commitment, .. } in evaluations.iter().filter(|x| !x.commitment.is_empty()) {
723 for comm_ch in &commitment.chunks {
725 scalars.push(rand_base * polyscale_i);
726 points.push(*comm_ch);
727
728 polyscale_i *= polyscale;
730 }
731 }
732}
733
734#[cfg(feature = "ocaml_types")]
735#[allow(non_local_definitions)]
736pub mod caml {
737 use super::PolyComm;
739 use ark_ec::AffineRepr;
740
741 #[derive(Clone, Debug, ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
742 pub struct CamlPolyComm<CamlG> {
743 pub unshifted: Vec<CamlG>,
744 pub shifted: Option<CamlG>,
745 }
746
747 impl<G, CamlG> From<PolyComm<G>> for CamlPolyComm<CamlG>
750 where
751 G: AffineRepr,
752 CamlG: From<G>,
753 {
754 fn from(polycomm: PolyComm<G>) -> Self {
755 Self {
756 unshifted: polycomm.chunks.into_iter().map(CamlG::from).collect(),
757 shifted: None,
758 }
759 }
760 }
761
762 impl<'a, G, CamlG> From<&'a PolyComm<G>> for CamlPolyComm<CamlG>
763 where
764 G: AffineRepr,
765 CamlG: From<G> + From<&'a G>,
766 {
767 fn from(polycomm: &'a PolyComm<G>) -> Self {
768 Self {
769 unshifted: polycomm.chunks.iter().map(Into::<CamlG>::into).collect(),
770 shifted: None,
771 }
772 }
773 }
774
775 impl<G, CamlG> From<CamlPolyComm<CamlG>> for PolyComm<G>
776 where
777 G: AffineRepr + From<CamlG>,
778 {
779 fn from(camlpolycomm: CamlPolyComm<CamlG>) -> Self {
780 assert!(
781 camlpolycomm.shifted.is_none(),
782 "mina#14628: Shifted commitments are deprecated and must not be used"
783 );
784 Self {
785 chunks: camlpolycomm
786 .unshifted
787 .into_iter()
788 .map(Into::<G>::into)
789 .collect(),
790 }
791 }
792 }
793
794 impl<'a, G, CamlG> From<&'a CamlPolyComm<CamlG>> for PolyComm<G>
795 where
796 G: AffineRepr + From<&'a CamlG> + From<CamlG>,
797 {
798 fn from(camlpolycomm: &'a CamlPolyComm<CamlG>) -> Self {
799 assert!(
800 camlpolycomm.shifted.is_none(),
801 "mina#14628: Shifted commitments are deprecated and must not be used"
802 );
803 Self {
804 chunks: camlpolycomm.unshifted.iter().map(Into::into).collect(),
806 }
807 }
808 }
809}
810
811#[cfg(test)]
812mod tests {
813 use super::*;
814 use mina_curves::pasta::Fp;
815 use std::str::FromStr;
816
817 #[test]
825 fn test_b_poly_regression() {
826 let chals: Vec<Fp> = (2u64..=16).map(Fp::from).collect();
828
829 let zeta = Fp::from(17u64);
830 let zeta_omega = Fp::from(19u64);
831
832 let b_at_zeta = b_poly(&chals, zeta);
833 let b_at_zeta_omega = b_poly(&chals, zeta_omega);
834
835 let expected_at_zeta = Fp::from_str(
837 "21115683812642620361045381629886583866877919362491419134086003378733605776328",
838 )
839 .unwrap();
840 let expected_at_zeta_omega = Fp::from_str(
841 "2298325069360593860729719174291433577456794311517767070156020442825391962511",
842 )
843 .unwrap();
844
845 assert_eq!(b_at_zeta, expected_at_zeta, "b(zeta) mismatch");
846 assert_eq!(
847 b_at_zeta_omega, expected_at_zeta_omega,
848 "b(zeta*omega) mismatch"
849 );
850 }
851
852 #[test]
857 fn test_b_poly_coefficients_regression() {
858 let chals: Vec<Fp> = vec![
860 Fp::from(2u64),
861 Fp::from(3u64),
862 Fp::from(5u64),
863 Fp::from(7u64),
864 ];
865
866 let coeffs = b_poly_coefficients(&chals);
867
868 assert_eq!(coeffs.len(), 16, "Should have 2^4 = 16 coefficients");
869
870 let expected: Vec<Fp> = vec![
879 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), ];
896
897 assert_eq!(coeffs, expected, "Coefficients mismatch");
898 }
899
900 #[test]
903 fn test_b_poly_consistency() {
904 let chals: Vec<Fp> = (2u64..=10).map(Fp::from).collect();
905 let coeffs = b_poly_coefficients(&chals);
906
907 for x_val in [1u64, 7, 13, 42, 100] {
909 let x = Fp::from(x_val);
910
911 let b_product = b_poly(&chals, x);
913
914 let mut b_coeffs = Fp::zero();
916 let mut x_pow = Fp::one();
917 for coeff in &coeffs {
918 b_coeffs += *coeff * x_pow;
919 x_pow *= x;
920 }
921
922 assert_eq!(
923 b_product, b_coeffs,
924 "b_poly and b_poly_coefficients inconsistent at x={x_val}"
925 );
926 }
927 }
928}