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