binius_iop_prover/basefold/
opening.rs1use std::ops::Deref;
7
8use binius_compute::Allocator;
9use binius_field::{BinaryField, PackedField};
10use binius_ip::mlecheck;
11use binius_ip_prover::sumcheck::{
12 common::MleCheckProver, multilinear_eval::multilinear_eval_prover,
13};
14use binius_math::{FieldVec, ntt::AdditiveNTT};
15
16use crate::{fri::FRIFoldProver, merkle_channel::MerkleIPProverChannel};
17
18#[allow(clippy::too_many_arguments)]
47pub fn prove_mlecheck_basefold<A, F, P, NTT, Channel, Data>(
48 witness: FieldVec<P, A>,
49 eval_point: &[F],
50 eval_claim: F,
51 batch_challenge: Option<F>,
52 outer_challenges: &[F],
53 mut fri_folder: FRIFoldProver<'_, F, P, NTT, Channel::Commitment, Data>,
54 channel: &mut Channel,
55 alloc: &A,
56) where
57 A: Allocator,
58 F: BinaryField,
59 P: PackedField<Scalar = F>,
60 NTT: AdditiveNTT<Field = F> + Sync,
61 Channel: MerkleIPProverChannel<F>,
62 Data: Deref<Target = [P]>,
63{
64 let _scope = tracing::debug_span!("Basefold MLE-check ZK (batched)").entered();
65
66 let n_vars = witness.log_len();
67 assert_eq!(eval_point.len(), n_vars);
68 let n_inner = usize::from(batch_challenge.is_some());
72 assert_eq!(n_vars + n_inner + outer_challenges.len(), fri_folder.n_rounds());
73
74 if let Some(gamma) = batch_challenge {
77 fri_folder.receive_challenge(gamma);
78 }
79 for &outer_challenge in outer_challenges {
85 fri_folder.receive_challenge(outer_challenge);
86 }
87
88 let mut sumcheck = multilinear_eval_prover(alloc, witness, eval_point, eval_claim);
89 for _ in 0..n_vars {
90 let mut round_coeffs_vec = sumcheck.execute();
91 let round_coeffs = round_coeffs_vec
92 .pop()
93 .expect("the multilinear-evaluation prover proves exactly one claim");
94
95 channel.send_many(mlecheck::RoundProof::truncate(round_coeffs).coeffs());
98 fri_folder.execute_fold_round(channel);
99
100 let challenge = channel.sample();
101 sumcheck.fold(challenge);
102 fri_folder.receive_challenge(challenge);
103 }
104
105 fri_folder.execute_fold_round(channel);
106 fri_folder.finish_proof(channel);
107}
108
109#[cfg(test)]
110mod test {
111 use anyhow::Result;
112 use binius_compute::GlobalAllocator;
113 use binius_field::{BinaryField, PackedField, PackedGhash1x128b};
114 use binius_hash::{StdDigest, StdHashSuite};
115 use binius_iop::{
116 basefold as verifier_basefold,
117 channel::OracleSpec,
118 merkle_channel::{MerkleIPVerifierChannel, VerifierMerkleTranscriptChannel},
119 };
120 use binius_ip::channel::IPVerifierChannel;
121 use binius_ip_prover::channel::IPProverChannel;
122 use binius_math::{
123 FieldBuffer,
124 inner_product::inner_product_buffers,
125 line::extrapolate_line,
126 multilinear::eq::eq_ind_partial_eval,
127 ntt::{NeighborsLastSingleThread, domain_context::GaoMateerOnTheFly},
128 test_utils::{random_field_buffer, random_scalars},
129 };
130 use binius_transcript::{ProverTranscript, fiat_shamir::HasherChallenger};
131 use binius_utils::rayon::prelude::*;
132 use rand::{SeedableRng, rngs::StdRng};
133
134 use super::prove_mlecheck_basefold;
135 use crate::{
136 fri::{self, FRIFoldProver, MaskedCodeword},
137 merkle_channel::{MerkleIPProverChannel, ProverMerkleTranscriptChannel},
138 merkle_tree::prover::BinaryMerkleTreeProver,
139 };
140
141 type StdChallenger = HasherChallenger<StdDigest>;
142
143 pub const LOG_INV_RATE: usize = 1;
144
145 fn run_mlecheck_basefold_zk_prove_and_verify<F, P>(
152 witness: &FieldBuffer<P>,
153 evaluation_point: &[F],
154 tamper: bool,
155 ) -> Result<()>
156 where
157 F: BinaryField,
158 P: PackedField<Scalar = F>,
159 {
160 let n_vars = evaluation_point.len();
161 assert_eq!(witness.log_len(), n_vars);
162
163 let merkle_prover = BinaryMerkleTreeProver::<F, StdHashSuite>::new();
164
165 let domain_context = GaoMateerOnTheFly::generate(n_vars + 1 + LOG_INV_RATE);
166 let ntt = NeighborsLastSingleThread::new(domain_context);
167
168 let (fri_params, _) = binius_iop::fri::FRIParams::optimal_for_batch(
172 merkle_prover.scheme(),
173 &[OracleSpec::new_zk(n_vars)],
174 LOG_INV_RATE,
175 32,
176 );
177
178 let mut commit_rng = StdRng::seed_from_u64(7);
181 let MaskedCodeword { codeword, mask } = fri::encode_masked(
182 &fri_params,
183 0,
184 &ntt,
185 witness.as_view(),
186 &mut commit_rng,
187 &GlobalAllocator,
188 );
189
190 let mut prover_transcript = ProverTranscript::new(StdChallenger::default());
191 let mut prover_channel =
192 ProverMerkleTranscriptChannel::<_, StdChallenger, _, StdHashSuite>::with_merkle_prover(
193 &mut prover_transcript,
194 merkle_prover,
195 );
196 let codeword_commitment = prover_channel.send_merkle_commitment(codeword.as_view(), 2);
197
198 let batch_challenge: F = IPProverChannel::sample(&mut prover_channel);
200 let mut witness_prime = witness.clone();
201 let gamma_broadcast = P::broadcast(batch_challenge);
202 (witness_prime.as_mut(), mask.as_ref())
203 .into_par_iter()
204 .for_each(|(w, &m)| {
205 *w = extrapolate_line(*w, m, gamma_broadcast);
206 });
207
208 let eval_point_eq = eq_ind_partial_eval::<P>(evaluation_point);
209 let mut eval_claim = inner_product_buffers(&witness_prime, &eval_point_eq);
210 if tamper {
211 eval_claim += F::ONE;
212 }
213
214 let fri_folder =
215 FRIFoldProver::new_batch(&fri_params, &ntt, vec![(codeword, codeword_commitment)]);
216 prove_mlecheck_basefold(
217 witness_prime,
218 evaluation_point,
219 eval_claim,
220 Some(batch_challenge),
221 &[],
222 fri_folder,
223 &mut prover_channel,
224 &GlobalAllocator,
225 );
226 prover_channel.into_transcript();
228
229 let mut verifier_transcript = prover_transcript.into_verifier();
230 let mut verifier_channel =
231 VerifierMerkleTranscriptChannel::<_, StdChallenger, _, StdHashSuite>::new(
232 &mut verifier_transcript,
233 );
234 let retrieved_commitment =
236 verifier_channel.recv_merkle_commitment(2, n_vars + LOG_INV_RATE)?;
237 let batch_challenge_v: F = IPVerifierChannel::sample(&mut verifier_channel);
238
239 verifier_basefold::verify_mlecheck_basefold(
242 &fri_params,
243 &[retrieved_commitment],
244 eval_claim,
245 evaluation_point,
246 Some(batch_challenge_v),
247 &[],
248 &mut verifier_channel,
249 )?;
250
251 Ok(())
252 }
253
254 #[test]
255 fn test_mlecheck_basefold_zk_valid_proof() {
256 type P = PackedGhash1x128b;
257
258 let n_vars = 8;
259 let mut rng = StdRng::seed_from_u64(0);
260 let witness = random_field_buffer::<P>(&mut rng, n_vars);
261 let evaluation_point = random_scalars(&mut rng, n_vars);
262
263 run_mlecheck_basefold_zk_prove_and_verify::<_, P>(&witness, &evaluation_point, false)
264 .unwrap();
265 }
266
267 #[test]
268 fn test_mlecheck_basefold_zk_invalid_proof() {
269 type P = PackedGhash1x128b;
270
271 let n_vars = 8;
272 let mut rng = StdRng::seed_from_u64(0);
273 let witness = random_field_buffer::<P>(&mut rng, n_vars);
274 let evaluation_point = random_scalars(&mut rng, n_vars);
275
276 let result =
277 run_mlecheck_basefold_zk_prove_and_verify::<_, P>(&witness, &evaluation_point, true);
278 assert!(result.is_err());
279 }
280}