1use alloc::{vec, vec::Vec};
10use ark_ec::{
11 models::short_weierstrass::Affine as SWJAffine, short_weierstrass::SWCurveConfig, AffineRepr,
12 CurveGroup, VariableBaseMSM,
13};
14use ark_ff::{BigInteger, Field, One, PrimeField, Zero};
15use ark_poly::univariate::DensePolynomial;
16use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
17use core::{
18 iter::Iterator,
19 marker::PhantomData,
20 ops::{Add, AddAssign, Sub},
21};
22use groupmap::{BWParameters, GroupMap};
23use mina_poseidon::{sponge::ScalarChallenge, FqSponge};
24use o1_utils::{field_helpers::product, ExtendedDensePolynomial as _};
25#[cfg(feature = "parallel")]
26use rayon::prelude::*;
27use serde::{de::Visitor, Deserialize, Serialize};
28use serde_with::{
29 de::DeserializeAsWrap, ser::SerializeAsWrap, serde_as, DeserializeAs, SerializeAs,
30};
31
32#[serde_as]
45#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
46#[serde(bound = "C: CanonicalDeserialize + CanonicalSerialize")]
47pub struct PolyComm<C> {
48 #[serde_as(as = "Vec<o1_utils::serialization::SerdeAs>")]
49 pub chunks: Vec<C>,
50}
51
52impl<C> PolyComm<C>
53where
54 C: CommitmentCurve,
55{
56 #[must_use]
58 pub fn chunk_commitment(&self, zeta_n: C::ScalarField) -> Self {
59 let mut res = C::Group::zero();
60 for chunk in self.chunks.iter().rev() {
64 res *= zeta_n;
65 res.add_assign(chunk);
66 }
67
68 Self {
69 chunks: vec![res.into_affine()],
70 }
71 }
72}
73
74impl<F> PolyComm<F>
75where
76 F: Field,
77{
78 pub fn chunk_blinding(&self, zeta_n: F) -> F {
80 let mut res = F::zero();
81 for chunk in self.chunks.iter().rev() {
85 res *= zeta_n;
86 res += chunk;
87 }
88 res
89 }
90}
91
92impl<G> PolyComm<G> {
93 pub fn iter(&self) -> core::slice::Iter<'_, G> {
95 self.chunks.iter()
96 }
97}
98
99impl<'a, G> IntoIterator for &'a PolyComm<G> {
100 type Item = &'a G;
101 type IntoIter = core::slice::Iter<'a, G>;
102
103 fn into_iter(self) -> Self::IntoIter {
104 self.chunks.iter()
105 }
106}
107
108#[derive(Clone, Debug, Serialize, Deserialize)]
110pub struct BlindedCommitment<G>
111where
112 G: CommitmentCurve,
113{
114 pub commitment: PolyComm<G>,
115 pub blinders: PolyComm<G::ScalarField>,
116}
117
118impl<T> PolyComm<T> {
119 #[must_use]
120 pub const fn new(chunks: Vec<T>) -> Self {
121 Self { chunks }
122 }
123}
124
125impl<T, U> SerializeAs<PolyComm<T>> for PolyComm<U>
126where
127 U: SerializeAs<T>,
128{
129 fn serialize_as<S>(source: &PolyComm<T>, serializer: S) -> Result<S::Ok, S::Error>
130 where
131 S: serde::Serializer,
132 {
133 serializer.collect_seq(
134 source
135 .chunks
136 .iter()
137 .map(|e| SerializeAsWrap::<T, U>::new(e)),
138 )
139 }
140}
141
142impl<'de, T, U> DeserializeAs<'de, PolyComm<T>> for PolyComm<U>
143where
144 U: DeserializeAs<'de, T>,
145{
146 fn deserialize_as<D>(deserializer: D) -> Result<PolyComm<T>, D::Error>
147 where
148 D: serde::Deserializer<'de>,
149 {
150 struct SeqVisitor<T, U> {
151 marker: PhantomData<(T, U)>,
152 }
153
154 impl<'de, T, U> Visitor<'de> for SeqVisitor<T, U>
155 where
156 U: DeserializeAs<'de, T>,
157 {
158 type Value = PolyComm<T>;
159
160 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
161 formatter.write_str("a sequence")
162 }
163
164 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
165 where
166 A: serde::de::SeqAccess<'de>,
167 {
168 #[allow(clippy::redundant_closure_call)]
169 let mut chunks = vec![];
170
171 while let Some(value) = seq
172 .next_element()?
173 .map(|v: DeserializeAsWrap<T, U>| v.into_inner())
174 {
175 chunks.push(value);
176 }
177
178 Ok(PolyComm::new(chunks))
179 }
180 }
181
182 let visitor = SeqVisitor::<T, U> {
183 marker: PhantomData,
184 };
185 deserializer.deserialize_seq(visitor)
186 }
187}
188
189impl<A: Copy + Clone + CanonicalDeserialize + CanonicalSerialize> PolyComm<A> {
190 pub fn map<B, F>(&self, mut f: F) -> PolyComm<B>
191 where
192 F: FnMut(A) -> B,
193 B: CanonicalDeserialize + CanonicalSerialize,
194 {
195 let chunks = self.chunks.iter().map(|x| f(*x)).collect();
196 PolyComm::new(chunks)
197 }
198
199 #[must_use]
201 #[allow(clippy::missing_const_for_fn)]
202 pub fn len(&self) -> usize {
203 self.chunks.len()
204 }
205
206 #[must_use]
208 #[allow(clippy::missing_const_for_fn)]
209 pub fn is_empty(&self) -> bool {
210 self.chunks.is_empty()
211 }
212
213 #[must_use]
216 pub fn zip<B: Copy + CanonicalDeserialize + CanonicalSerialize>(
217 &self,
218 other: &PolyComm<B>,
219 ) -> Option<PolyComm<(A, B)>> {
220 if self.chunks.len() != other.chunks.len() {
221 return None;
222 }
223 let chunks = self
224 .chunks
225 .iter()
226 .zip(other.chunks.iter())
227 .map(|(x, y)| (*x, *y))
228 .collect();
229 Some(PolyComm::new(chunks))
230 }
231
232 #[must_use]
237 pub fn get_first_chunk(&self) -> A {
238 self.chunks[0]
239 }
240}
241
242pub fn shift_scalar<G: AffineRepr>(x: G::ScalarField) -> G::ScalarField
274where
275 G::BaseField: PrimeField,
276{
277 let n1 = <G::ScalarField as PrimeField>::MODULUS;
278 let n2 = <G::ScalarField as PrimeField>::BigInt::from_bits_le(
279 &<G::BaseField as PrimeField>::MODULUS.to_bits_le()[..],
280 );
281 let two: G::ScalarField = (2u64).into();
282 let two_pow = two.pow([u64::from(<G::ScalarField as PrimeField>::MODULUS_BIT_SIZE)]);
283 if n1 < n2 {
284 (x - (two_pow + G::ScalarField::one())) / two
285 } else {
286 x - two_pow
287 }
288}
289
290impl<'a, C: AffineRepr> Add<&'a PolyComm<C>> for &PolyComm<C> {
291 type Output = PolyComm<C>;
292
293 fn add(self, other: &'a PolyComm<C>) -> PolyComm<C> {
294 let mut chunks = vec![];
295 let n1 = self.chunks.len();
296 let n2 = other.chunks.len();
297 for i in 0..core::cmp::max(n1, n2) {
298 let pt = if i < n1 && i < n2 {
299 (self.chunks[i] + other.chunks[i]).into_affine()
300 } else if i < n1 {
301 self.chunks[i]
302 } else {
303 other.chunks[i]
304 };
305 chunks.push(pt);
306 }
307 PolyComm::new(chunks)
308 }
309}
310
311impl<'a, C: AffineRepr + Sub<Output = C::Group>> Sub<&'a PolyComm<C>> for &PolyComm<C> {
312 type Output = PolyComm<C>;
313
314 fn sub(self, other: &'a PolyComm<C>) -> PolyComm<C> {
315 let mut chunks = vec![];
316 let n1 = self.chunks.len();
317 let n2 = other.chunks.len();
318 for i in 0..core::cmp::max(n1, n2) {
319 let pt = if i < n1 && i < n2 {
320 (self.chunks[i] - other.chunks[i]).into_affine()
321 } else if i < n1 {
322 self.chunks[i]
323 } else {
324 other.chunks[i]
325 };
326 chunks.push(pt);
327 }
328 PolyComm::new(chunks)
329 }
330}
331
332impl<C: AffineRepr> PolyComm<C> {
333 #[must_use]
334 pub fn scale(&self, c: C::ScalarField) -> Self {
335 Self {
336 chunks: self.chunks.iter().map(|g| g.mul(c).into_affine()).collect(),
337 }
338 }
339
340 #[must_use]
350 pub fn multi_scalar_mul(com: &[&Self], elm: &[C::ScalarField]) -> Self {
351 assert_eq!(com.len(), elm.len());
352
353 if com.is_empty() || elm.is_empty() {
354 return Self::new(vec![C::zero()]);
355 }
356
357 let all_scalars: Vec<_> = elm.iter().map(|s| s.into_bigint()).collect();
358
359 let elems_size = Iterator::max(com.iter().map(|c| c.chunks.len())).unwrap();
360
361 let chunks = (0..elems_size)
362 .map(|chunk| {
363 let (points, scalars): (Vec<_>, Vec<_>) = com
364 .iter()
365 .zip(&all_scalars)
366 .filter_map(|(com, scalar)| com.chunks.get(chunk).map(|c| (c, scalar)))
368 .unzip();
369
370 {
375 #[cfg(feature = "parallel")]
376 {
377 let subchunk_size = core::cmp::max(points.len() / 2, 1);
378 points
379 .into_par_iter()
380 .chunks(subchunk_size)
381 .zip(scalars.into_par_iter().chunks(subchunk_size))
382 .map(|(psc, ssc)| C::Group::msm_bigint(&psc, &ssc).into_affine())
383 .reduce(C::zero, |x, y| (x + y).into())
384 }
385 #[cfg(not(feature = "parallel"))]
386 {
387 C::Group::msm_bigint(&points, &scalars).into_affine()
388 }
389 }
390 })
391 .collect();
392
393 Self::new(chunks)
394 }
395}
396
397pub fn b_poly<F: Field>(chals: &[F], x: F) -> F {
427 let k = chals.len();
428
429 let mut pow_twos = vec![x];
430
431 for i in 1..k {
432 pow_twos.push(pow_twos[i - 1].square());
433 }
434
435 product((0..k).map(|i| F::one() + (chals[i] * pow_twos[k - 1 - i])))
436}
437
438pub fn b_poly_coefficients<F: Field>(chals: &[F]) -> Vec<F> {
465 let rounds = chals.len();
466 let s_length = 1 << rounds;
467 let mut s = vec![F::one(); s_length];
468 let mut k: usize = 0;
469 let mut pow: usize = 1;
470 for i in 1..s_length {
471 k += usize::from(i == pow);
472 pow <<= u32::from(i == pow);
473 s[i] = s[i - (pow >> 1)] * chals[rounds - 1 - (k - 1)];
474 }
475 s
476}
477
478pub fn squeeze_prechallenge<
479 const FULL_ROUNDS: usize,
480 Fq: Field,
481 G,
482 Fr: Field,
483 EFqSponge: FqSponge<Fq, G, Fr, FULL_ROUNDS>,
484>(
485 sponge: &mut EFqSponge,
486) -> ScalarChallenge<Fr> {
487 ScalarChallenge::new(sponge.challenge())
488}
489
490pub fn squeeze_challenge<
491 const FULL_ROUNDS: usize,
492 Fq: Field,
493 G,
494 Fr: PrimeField,
495 EFqSponge: FqSponge<Fq, G, Fr, FULL_ROUNDS>,
496>(
497 endo_r: &Fr,
498 sponge: &mut EFqSponge,
499) -> Fr {
500 squeeze_prechallenge(sponge).to_field(endo_r)
501}
502
503pub fn absorb_commitment<
504 const FULL_ROUNDS: usize,
505 Fq: Field,
506 G: Clone,
507 Fr: PrimeField,
508 EFqSponge: FqSponge<Fq, G, Fr, FULL_ROUNDS>,
509>(
510 sponge: &mut EFqSponge,
511 commitment: &PolyComm<G>,
512) {
513 sponge.absorb_g(&commitment.chunks);
514}
515
516pub trait CommitmentCurve: AffineRepr + Sub<Output = Self::Group> {
521 type Params: SWCurveConfig;
522 type Map: GroupMap<Self::BaseField>;
523
524 fn to_coordinates(&self) -> Option<(Self::BaseField, Self::BaseField)>;
525 fn of_coordinates(x: Self::BaseField, y: Self::BaseField) -> Self;
526}
527
528pub trait EndoCurve: CommitmentCurve {
533 fn combine_one(g1: &[Self], g2: &[Self], x2: Self::ScalarField) -> Vec<Self> {
535 crate::combine::window_combine(g1, g2, Self::ScalarField::one(), x2)
536 }
537
538 fn combine_one_endo(
540 endo_r: Self::ScalarField,
541 _endo_q: Self::BaseField,
542 g1: &[Self],
543 g2: &[Self],
544 x2: &ScalarChallenge<Self::ScalarField>,
545 ) -> Vec<Self> {
546 crate::combine::window_combine(g1, g2, Self::ScalarField::one(), x2.to_field(&endo_r))
547 }
548
549 fn combine(
550 g1: &[Self],
551 g2: &[Self],
552 x1: Self::ScalarField,
553 x2: Self::ScalarField,
554 ) -> Vec<Self> {
555 crate::combine::window_combine(g1, g2, x1, x2)
556 }
557}
558
559impl<P: SWCurveConfig + Clone> CommitmentCurve for SWJAffine<P> {
560 type Params = P;
561 type Map = BWParameters<P>;
562
563 fn to_coordinates(&self) -> Option<(Self::BaseField, Self::BaseField)> {
564 if self.infinity {
565 None
566 } else {
567 Some((self.x, self.y))
568 }
569 }
570
571 fn of_coordinates(x: P::BaseField, y: P::BaseField) -> Self {
572 Self::new_unchecked(x, y)
573 }
574}
575
576impl<P: SWCurveConfig + Clone> EndoCurve for SWJAffine<P> {
577 fn combine_one(g1: &[Self], g2: &[Self], x2: Self::ScalarField) -> Vec<Self> {
578 crate::combine::affine_window_combine_one(g1, g2, x2)
579 }
580
581 fn combine_one_endo(
582 _endo_r: Self::ScalarField,
583 endo_q: Self::BaseField,
584 g1: &[Self],
585 g2: &[Self],
586 x2: &ScalarChallenge<Self::ScalarField>,
587 ) -> Vec<Self> {
588 crate::combine::affine_window_combine_one_endo(endo_q, g1, g2, x2)
589 }
590
591 fn combine(
592 g1: &[Self],
593 g2: &[Self],
594 x1: Self::ScalarField,
595 x2: Self::ScalarField,
596 ) -> Vec<Self> {
597 crate::combine::affine_window_combine(g1, g2, x1, x2)
598 }
599}
600
601#[allow(clippy::type_complexity)]
622pub fn combined_inner_product<F: PrimeField>(
623 polyscale: &F,
624 evalscale: &F,
625 polys: &[Vec<Vec<F>>],
627) -> F {
628 let mut res = F::zero();
630 let mut polyscale_i = F::one();
632
633 for evals_tr in polys.iter().filter(|evals_tr| !evals_tr[0].is_empty()) {
634 let evals: Vec<_> = (0..evals_tr[0].len())
638 .map(|i| evals_tr.iter().map(|v| v[i]).collect::<Vec<_>>())
639 .collect();
640
641 for eval in &evals {
650 let term = DensePolynomial::<F>::eval_polynomial(eval, *evalscale);
652 res += &(polyscale_i * term);
653 polyscale_i *= polyscale;
654 }
655 }
656 res
657}
658
659pub struct Evaluation<G>
661where
662 G: AffineRepr,
663{
664 pub commitment: PolyComm<G>,
670
671 pub evaluations: Vec<Vec<G::ScalarField>>,
679}
680
681pub struct BatchEvaluationProof<'a, G, EFqSponge, OpeningProof, const FULL_ROUNDS: usize>
683where
684 G: AffineRepr,
685 EFqSponge: FqSponge<G::BaseField, G, G::ScalarField, FULL_ROUNDS>,
686{
687 pub sponge: EFqSponge,
690 pub evaluations: Vec<Evaluation<G>>,
693 pub evaluation_points: Vec<G::ScalarField>,
696 pub polyscale: G::ScalarField,
699 pub evalscale: G::ScalarField,
701 pub opening: &'a OpeningProof,
703 pub combined_inner_product: G::ScalarField,
704}
705
706pub fn combine_commitments<G: CommitmentCurve>(
725 evaluations: &[Evaluation<G>],
726 scalars: &mut Vec<G::ScalarField>,
727 points: &mut Vec<G>,
728 polyscale: G::ScalarField,
729 rand_base: G::ScalarField,
730) {
731 let mut polyscale_i = G::ScalarField::one();
733
734 for Evaluation { commitment, .. } in evaluations.iter().filter(|x| !x.commitment.is_empty()) {
735 for comm_ch in &commitment.chunks {
737 scalars.push(rand_base * polyscale_i);
738 points.push(*comm_ch);
739
740 polyscale_i *= polyscale;
742 }
743 }
744}
745
746#[cfg(feature = "ocaml_types")]
747#[allow(non_local_definitions)]
748pub mod caml {
749 use super::PolyComm;
751 use ark_ec::AffineRepr;
752
753 #[derive(Clone, Debug, ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
754 pub struct CamlPolyComm<CamlG> {
755 pub unshifted: Vec<CamlG>,
756 pub shifted: Option<CamlG>,
757 }
758
759 impl<G, CamlG> From<PolyComm<G>> for CamlPolyComm<CamlG>
762 where
763 G: AffineRepr,
764 CamlG: From<G>,
765 {
766 fn from(polycomm: PolyComm<G>) -> Self {
767 Self {
768 unshifted: polycomm.chunks.into_iter().map(CamlG::from).collect(),
769 shifted: None,
770 }
771 }
772 }
773
774 impl<'a, G, CamlG> From<&'a PolyComm<G>> for CamlPolyComm<CamlG>
775 where
776 G: AffineRepr,
777 CamlG: From<G> + From<&'a G>,
778 {
779 fn from(polycomm: &'a PolyComm<G>) -> Self {
780 Self {
781 unshifted: polycomm.chunks.iter().map(Into::<CamlG>::into).collect(),
782 shifted: None,
783 }
784 }
785 }
786
787 impl<G, CamlG> From<CamlPolyComm<CamlG>> for PolyComm<G>
788 where
789 G: AffineRepr + From<CamlG>,
790 {
791 fn from(camlpolycomm: CamlPolyComm<CamlG>) -> Self {
792 assert!(
793 camlpolycomm.shifted.is_none(),
794 "mina#14628: Shifted commitments are deprecated and must not be used"
795 );
796 Self {
797 chunks: camlpolycomm
798 .unshifted
799 .into_iter()
800 .map(Into::<G>::into)
801 .collect(),
802 }
803 }
804 }
805
806 impl<'a, G, CamlG> From<&'a CamlPolyComm<CamlG>> for PolyComm<G>
807 where
808 G: AffineRepr + From<&'a CamlG> + From<CamlG>,
809 {
810 fn from(camlpolycomm: &'a CamlPolyComm<CamlG>) -> Self {
811 assert!(
812 camlpolycomm.shifted.is_none(),
813 "mina#14628: Shifted commitments are deprecated and must not be used"
814 );
815 Self {
816 chunks: camlpolycomm.unshifted.iter().map(Into::into).collect(),
818 }
819 }
820 }
821}
822
823#[cfg(test)]
824mod tests {
825 use super::*;
826 use core::str::FromStr;
827 use mina_curves::pasta::Fp;
828
829 #[test]
837 fn test_b_poly_regression() {
838 let chals: Vec<Fp> = (2u64..=16).map(Fp::from).collect();
840
841 let zeta = Fp::from(17u64);
842 let zeta_omega = Fp::from(19u64);
843
844 let b_at_zeta = b_poly(&chals, zeta);
845 let b_at_zeta_omega = b_poly(&chals, zeta_omega);
846
847 let expected_at_zeta = Fp::from_str(
849 "21115683812642620361045381629886583866877919362491419134086003378733605776328",
850 )
851 .unwrap();
852 let expected_at_zeta_omega = Fp::from_str(
853 "2298325069360593860729719174291433577456794311517767070156020442825391962511",
854 )
855 .unwrap();
856
857 assert_eq!(b_at_zeta, expected_at_zeta, "b(zeta) mismatch");
858 assert_eq!(
859 b_at_zeta_omega, expected_at_zeta_omega,
860 "b(zeta*omega) mismatch"
861 );
862 }
863
864 #[test]
869 fn test_b_poly_coefficients_regression() {
870 let chals: Vec<Fp> = vec![
872 Fp::from(2u64),
873 Fp::from(3u64),
874 Fp::from(5u64),
875 Fp::from(7u64),
876 ];
877
878 let coeffs = b_poly_coefficients(&chals);
879
880 assert_eq!(coeffs.len(), 16, "Should have 2^4 = 16 coefficients");
881
882 let expected: Vec<Fp> = vec![
891 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), ];
908
909 assert_eq!(coeffs, expected, "Coefficients mismatch");
910 }
911
912 #[test]
915 fn test_b_poly_consistency() {
916 let chals: Vec<Fp> = (2u64..=10).map(Fp::from).collect();
917 let coeffs = b_poly_coefficients(&chals);
918
919 for x_val in [1u64, 7, 13, 42, 100] {
921 let x = Fp::from(x_val);
922
923 let b_product = b_poly(&chals, x);
925
926 let mut b_coeffs = Fp::zero();
928 let mut x_pow = Fp::one();
929 for coeff in &coeffs {
930 b_coeffs += *coeff * x_pow;
931 x_pow *= x;
932 }
933
934 assert_eq!(
935 b_product, b_coeffs,
936 "b_poly and b_poly_coefficients inconsistent at x={x_val}"
937 );
938 }
939 }
940}