1#[cfg(feature = "parallel")]
4use rayon::prelude::*;
5use std::marker::PhantomData;
6
7use crypto_primitives::{ConstIntSemiring, FromPrimitiveWithConfig, PrimeField, Semiring};
8use thiserror::Error;
9use zinc_poly::mle::{DenseMultilinearExtension, dense::project_coeffs};
10use zinc_transcript::traits::{ConstTranscribable, Transcript};
11use zinc_utils::{
12 cfg_into_iter, from_ref::FromRef, inner_transparent_field::InnerTransparentField,
13 projectable_to_field::ProjectableToField,
14};
15
16use crate::sumcheck::{
17 MLSumcheck, SumCheckError, SumcheckProof, prover::ProverState, verifier::Subclaim,
18};
19
20pub struct RFSumcheck<F, R>(PhantomData<(F, R)>);
21
22#[derive(Clone, Debug, PartialEq)]
23pub struct RFSumcheckProof<F: PrimeField, R>(pub SumcheckProof<F>, PhantomData<R>);
24
25impl<F: PrimeField, R> RFSumcheckProof<F, R> {
26 pub fn new(inner: SumcheckProof<F>) -> Self {
27 Self(inner, Default::default())
28 }
29}
30
31pub struct RFProverState<F: PrimeField, R>(pub ProverState<F>, PhantomData<R>);
32
33impl<F: PrimeField, R> RFProverState<F, R> {
34 pub fn new(sumcheck_prover_state: ProverState<F>) -> Self {
35 Self(sumcheck_prover_state, Default::default())
36 }
37}
38
39impl<F: FromPrimitiveWithConfig, R: Semiring + ProjectableToField<F>> RFSumcheck<F, R> {
40 pub fn prove_as_subprotocol(
66 transcript: &mut impl Transcript,
67 mles: Vec<DenseMultilinearExtension<R>>,
68 mles_f: Vec<DenseMultilinearExtension<F::Inner>>,
69 nvars: usize,
70 degree: usize,
71 comb_fn: impl Fn(&F, &[F]) -> F + Send + Sync,
72 field_cfg: &F::Config,
73 ) -> (RFSumcheckProof<F, R>, RFProverState<F, R>)
74 where
75 F: InnerTransparentField,
76 F::Inner: ConstTranscribable + ConstIntSemiring + FromRef<F::Inner>,
77 F::Modulus: ConstTranscribable,
78 {
79 let projecting_element: F = transcript.get_field_challenge(field_cfg);
80
81 let field_mles = cfg_into_iter!(mles)
82 .map(|mle| project_coeffs(mle, &projecting_element))
83 .chain(mles_f)
84 .collect();
85
86 let (proof, state) = MLSumcheck::prove_as_subprotocol(
87 transcript,
88 field_mles,
89 nvars,
90 degree,
91 |x| comb_fn(&projecting_element, x),
92 field_cfg,
93 );
94
95 (RFSumcheckProof::new(proof), RFProverState::new(state))
96 }
97
98 pub fn verify_as_subprotocol(
112 transcript: &mut impl Transcript,
113 num_vars: usize,
114 degree: usize,
115 proof: &RFSumcheckProof<F, R>,
116 field_cfg: F::Config,
117 ) -> Result<Subclaim<F>, RFSumcheckError<F>>
118 where
119 F::Inner: ConstTranscribable + ConstIntSemiring,
120 F::Modulus: ConstTranscribable,
121 {
122 let _ = transcript.get_field_challenge::<F>(&field_cfg);
126
127 let subclaim =
128 MLSumcheck::verify_as_subprotocol(transcript, num_vars, degree, &proof.0, &field_cfg)?;
129
130 Ok(subclaim)
131 }
132}
133
134#[derive(Error, Debug)]
135pub enum RFSumcheckError<F: PrimeField> {
136 #[error("underlying sumcheck error: {0}")]
137 SumCheckError(SumCheckError<F>),
138}
139
140impl<F: PrimeField> From<SumCheckError<F>> for RFSumcheckError<F> {
141 fn from(value: SumCheckError<F>) -> Self {
142 Self::SumCheckError(value)
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use crypto_primitives::{Field, FromWithConfig, crypto_bigint_monty::MontyField};
149 use num_traits::Zero;
150 use rand::{Rng, rng};
151 use zinc_poly::{
152 mle::DenseMultilinearExtension, univariate::binary::BinaryPoly, utils::build_eq_x_r_inner,
153 };
154 use zinc_primality::MillerRabin;
155 use zinc_transcript::{Blake3Transcript, traits::Transcript};
156
157 use crate::random_field_sumcheck::RFSumcheck;
158
159 const LIMBS: usize = 4;
160
161 type F = MontyField<LIMBS>;
162
163 #[test]
164 fn test_simple_product_random_field_sumcheck() {
165 let witness_size = 1 << 3;
166 let mut rng = rng();
167 let a: Vec<u32> = (0..witness_size).map(|_| rng.random()).collect();
168 let b: Vec<u32> = (0..witness_size).map(|_| rng.random()).collect();
169 let c: Vec<u32> = (0..witness_size).map(|_| rng.random()).collect();
170
171 let nvars = zinc_utils::log2(witness_size) as usize;
172
173 let a: DenseMultilinearExtension<BinaryPoly<32>> =
174 DenseMultilinearExtension::from_evaluations_vec(
175 nvars,
176 a.into_iter().map(BinaryPoly::from).collect(),
177 BinaryPoly::zero(),
178 );
179
180 let b: DenseMultilinearExtension<BinaryPoly<32>> =
181 DenseMultilinearExtension::from_evaluations_vec(
182 nvars,
183 b.into_iter().map(BinaryPoly::from).collect(),
184 BinaryPoly::zero(),
185 );
186
187 let c: DenseMultilinearExtension<BinaryPoly<32>> =
188 DenseMultilinearExtension::from_evaluations_vec(
189 nvars,
190 c.into_iter().map(BinaryPoly::from).collect(),
191 BinaryPoly::zero(),
192 );
193
194 let mut transcript = Blake3Transcript::new();
195
196 let field_cfg = transcript.get_random_field_cfg::<F, <F as Field>::Inner, MillerRabin>();
197
198 let eq_r = build_eq_x_r_inner(&vec![F::from_with_cfg(2u32, &field_cfg); nvars], &field_cfg)
199 .expect("Failed to build eq_r");
200
201 let proof = (RFSumcheck::<F, _>::prove_as_subprotocol(
202 &mut transcript.clone(),
203 vec![a, b, c],
204 vec![eq_r],
205 nvars,
206 3,
207 |_x, vals| (&vals[0] * &vals[1] - &vals[2]) * &vals[3],
208 &field_cfg,
209 ))
210 .0;
211
212 assert!(
213 RFSumcheck::<F, _>::verify_as_subprotocol(
214 &mut transcript,
215 nvars,
216 3,
217 &proof,
218 field_cfg,
219 )
220 .is_ok()
221 )
222 }
223}