1use std::io::{Error, Read, Result, Write};
2
3use derivative::Derivative;
4use p3_field::PrimeCharacteristicRing;
5use serde::{Deserialize, Serialize};
6
7use crate::{
8 codec::{vec_with_capped_capacity, DecodableConfig, Decode, EncodableConfig, Encode},
9 StarkProtocolConfig,
10};
11
12#[derive(Derivative, Serialize, Deserialize)]
13#[derivative(
14 Clone(bound = ""),
15 Debug(bound = ""),
16 PartialEq(bound = ""),
17 Eq(bound = "")
18)]
19#[serde(bound = "")]
20pub struct Proof<SC: StarkProtocolConfig> {
21 pub common_main_commit: SC::Digest,
23
24 pub trace_vdata: Vec<Option<TraceVData<SC>>>,
28
29 pub public_values: Vec<Vec<SC::F>>,
32
33 pub gkr_proof: GkrProof<SC>,
34 pub batch_constraint_proof: BatchConstraintProof<SC>,
35 pub stacking_proof: StackingProof<SC>,
36 pub whir_proof: WhirProof<SC>,
37}
38
39#[derive(Derivative, Serialize, Deserialize)]
40#[derivative(
41 Clone(bound = ""),
42 Debug(bound = ""),
43 PartialEq(bound = ""),
44 Eq(bound = ""),
45 Default(bound = "")
46)]
47#[serde(bound = "")]
48pub struct TraceVData<SC: StarkProtocolConfig> {
49 pub log_height: usize,
55 pub cached_commitments: Vec<SC::Digest>,
59}
60
61#[derive(Derivative, Serialize, Deserialize)]
62#[derivative(
63 Clone(bound = ""),
64 Debug(bound = ""),
65 PartialEq(bound = ""),
66 Eq(bound = "")
67)]
68#[serde(bound = "")]
69pub struct GkrProof<SC: StarkProtocolConfig> {
70 pub logup_pow_witness: SC::F,
72 pub q0_claim: SC::EF,
79 pub claims_per_layer: Vec<GkrLayerClaims<SC>>,
81 pub sumcheck_polys: Vec<Vec<[SC::EF; 3]>>,
84}
85
86#[derive(Derivative, Serialize, Deserialize)]
87#[derivative(
88 Clone(bound = ""),
89 Debug(bound = ""),
90 PartialEq(bound = ""),
91 Eq(bound = "")
92)]
93#[serde(bound = "")]
94pub struct GkrLayerClaims<SC: StarkProtocolConfig> {
95 pub p_xi_0: SC::EF,
96 pub p_xi_1: SC::EF,
97 pub q_xi_0: SC::EF,
98 pub q_xi_1: SC::EF,
99}
100
101#[derive(Derivative, Serialize, Deserialize)]
102#[derivative(
103 Clone(bound = ""),
104 Debug(bound = ""),
105 PartialEq(bound = ""),
106 Eq(bound = "")
107)]
108#[serde(bound = "")]
109pub struct BatchConstraintProof<SC: StarkProtocolConfig> {
110 pub numerator_term_per_air: Vec<SC::EF>,
113 pub denominator_term_per_air: Vec<SC::EF>,
116
117 pub univariate_round_coeffs: Vec<SC::EF>,
119 pub sumcheck_round_polys: Vec<Vec<SC::EF>>,
121
122 pub column_openings: Vec<Vec<Vec<SC::EF>>>,
134}
135
136pub fn column_openings_by_rot<'a, EF: PrimeCharacteristicRing + Copy + 'a>(
137 openings: &'a [EF],
138 need_rot: bool,
139) -> Box<dyn Iterator<Item = (EF, EF)> + 'a> {
140 if need_rot {
141 Box::new(openings.chunks_exact(2).map(|chunk| (chunk[0], chunk[1])))
142 } else {
143 Box::new(openings.iter().map(|&claim| (claim, EF::ZERO)))
144 }
145}
146
147#[derive(Derivative, Serialize, Deserialize)]
148#[derivative(
149 Clone(bound = ""),
150 Debug(bound = ""),
151 PartialEq(bound = ""),
152 Eq(bound = "")
153)]
154#[serde(bound = "")]
155pub struct StackingProof<SC: StarkProtocolConfig> {
156 pub univariate_round_coeffs: Vec<SC::EF>,
158 pub sumcheck_round_polys: Vec<[SC::EF; 2]>,
160 pub stacking_openings: Vec<Vec<SC::EF>>,
162}
163
164pub type MerkleProof<Digest> = Vec<Digest>;
165
166#[derive(Derivative, Serialize, Deserialize)]
169#[derivative(
170 Clone(bound = ""),
171 Debug(bound = ""),
172 PartialEq(bound = ""),
173 Eq(bound = "")
174)]
175#[serde(bound = "")]
176pub struct WhirProof<SC: StarkProtocolConfig> {
177 pub mu_pow_witness: SC::F,
179 pub whir_sumcheck_polys: Vec<[SC::EF; 2]>,
182 pub codeword_commits: Vec<SC::Digest>,
184 pub ood_values: Vec<SC::EF>,
186 pub folding_pow_witnesses: Vec<SC::F>,
189 pub query_phase_pow_witnesses: Vec<SC::F>,
191 pub initial_round_opened_rows: Vec<Vec<Vec<Vec<SC::F>>>>,
194 pub initial_round_merkle_proofs: Vec<Vec<MerkleProof<SC::Digest>>>,
195 pub codeword_opened_values: Vec<Vec<Vec<SC::EF>>>,
197 pub codeword_merkle_proofs: Vec<Vec<MerkleProof<SC::Digest>>>,
198 pub final_poly: Vec<SC::EF>,
200}
201
202impl<SC: EncodableConfig> Encode for TraceVData<SC> {
205 fn encode<W: Write>(&self, writer: &mut W) -> Result<()> {
206 self.log_height.encode(writer)?;
207 SC::encode_digest_slice(&self.cached_commitments, writer)
208 }
209}
210
211impl<SC: EncodableConfig> Encode for GkrLayerClaims<SC> {
212 fn encode<W: Write>(&self, writer: &mut W) -> Result<()> {
213 SC::encode_extension_field(&self.p_xi_0, writer)?;
214 SC::encode_extension_field(&self.p_xi_1, writer)?;
215 SC::encode_extension_field(&self.q_xi_0, writer)?;
216 SC::encode_extension_field(&self.q_xi_1, writer)?;
217 Ok(())
218 }
219}
220
221pub(crate) const CODEC_VERSION: u32 = 3;
225
226impl<SC: EncodableConfig> Encode for Proof<SC> {
227 fn encode<W: Write>(&self, writer: &mut W) -> Result<()> {
228 CODEC_VERSION.encode(writer)?;
230 SC::encode_digest(&self.common_main_commit, writer)?;
231
232 let num_airs: usize = self.trace_vdata.len();
235 num_airs.encode(writer)?;
236 for chunk in self.trace_vdata.chunks(8) {
237 let mut ret = 0u8;
238 for (i, vdata) in chunk.iter().enumerate() {
239 ret |= (vdata.is_some() as u8) << (i as u8);
240 }
241 ret.encode(writer)?;
242 }
243 for vdata in self.trace_vdata.iter().flatten() {
244 vdata.encode(writer)?;
245 }
246
247 self.public_values.len().encode(writer)?;
249 for pv in &self.public_values {
250 SC::encode_base_field_slice(pv, writer)?;
251 }
252 self.gkr_proof.encode(writer)?;
253 self.batch_constraint_proof.encode(writer)?;
254 self.stacking_proof.encode(writer)?;
255 self.whir_proof.encode(writer)
256 }
257}
258
259impl<SC: EncodableConfig> Encode for GkrProof<SC> {
260 fn encode<W: Write>(&self, writer: &mut W) -> Result<()> {
261 SC::encode_base_field(&self.logup_pow_witness, writer)?;
262 SC::encode_extension_field(&self.q0_claim, writer)?;
263 self.claims_per_layer.encode(writer)?;
264 for round in &self.sumcheck_polys {
267 for arr in round {
268 SC::encode_extension_field_iter(arr.iter(), writer)?;
269 }
270 }
271 Ok(())
272 }
273}
274
275impl<SC: EncodableConfig> Encode for BatchConstraintProof<SC> {
276 fn encode<W: Write>(&self, writer: &mut W) -> Result<()> {
277 SC::encode_extension_field_slice(&self.numerator_term_per_air, writer)?;
279 SC::encode_extension_field_iter(self.denominator_term_per_air.iter(), writer)?;
280
281 SC::encode_extension_field_slice(&self.univariate_round_coeffs, writer)?;
282
283 let n_max = self.sumcheck_round_polys.len();
285 n_max.encode(writer)?;
286 if n_max > 0 {
287 self.sumcheck_round_polys[0].len().encode(writer)?;
288 for round_polys in &self.sumcheck_round_polys {
289 SC::encode_extension_field_iter(round_polys.iter(), writer)?;
290 }
291 }
292
293 for part_col_openings in &self.column_openings {
296 part_col_openings.len().encode(writer)?;
298 for col_opening in part_col_openings {
299 SC::encode_extension_field_slice(col_opening, writer)?;
300 }
301 }
302 Ok(())
303 }
304}
305
306impl<SC: EncodableConfig> Encode for StackingProof<SC> {
307 fn encode<W: Write>(&self, writer: &mut W) -> Result<()> {
308 SC::encode_extension_field_slice(&self.univariate_round_coeffs, writer)?;
309 self.sumcheck_round_polys.len().encode(writer)?;
311 for arr in &self.sumcheck_round_polys {
312 SC::encode_extension_field_iter(arr.iter(), writer)?;
313 }
314 self.stacking_openings.len().encode(writer)?;
316 for opening in &self.stacking_openings {
317 SC::encode_extension_field_slice(opening, writer)?;
318 }
319 Ok(())
320 }
321}
322
323impl<SC: EncodableConfig> Encode for WhirProof<SC> {
324 fn encode<W: Write>(&self, writer: &mut W) -> Result<()> {
325 SC::encode_base_field(&self.mu_pow_witness, writer)?;
326 self.whir_sumcheck_polys.len().encode(writer)?;
328 for arr in &self.whir_sumcheck_polys {
329 SC::encode_extension_field_iter(arr.iter(), writer)?;
330 }
331 let num_whir_sumcheck_rounds = self.whir_sumcheck_polys.len();
332
333 SC::encode_digest_slice(&self.codeword_commits, writer)?;
335 SC::encode_extension_field_iter(self.ood_values.iter(), writer)?;
336 let num_whir_rounds = self.codeword_commits.len() + 1;
337 if !num_whir_sumcheck_rounds.is_multiple_of(num_whir_rounds) {
338 return Err(Error::new(
339 std::io::ErrorKind::InvalidData,
340 "num_whir_sumcheck_rounds must be a multiple of num_whir_rounds",
341 ));
342 }
343 assert_eq!(num_whir_rounds, self.query_phase_pow_witnesses.len());
344 SC::encode_base_field_iter(self.folding_pow_witnesses.iter(), writer)?;
345 SC::encode_base_field_iter(self.query_phase_pow_witnesses.iter(), writer)?;
346
347 let num_commits = self.initial_round_opened_rows.len();
348 assert!(num_commits > 0);
349 num_commits.encode(writer)?;
350 let initial_num_whir_queries = self.initial_round_opened_rows[0].len();
351 initial_num_whir_queries.encode(writer)?;
352
353 if initial_num_whir_queries > 0 {
354 let merkle_depth = self.initial_round_merkle_proofs[0][0].len();
355 merkle_depth.encode(writer)?;
356
357 let widths: Vec<usize> = self
360 .initial_round_opened_rows
361 .iter()
362 .map(|commit_rows| {
363 commit_rows
365 .first()
366 .and_then(|q| q.first())
367 .map(|row| row.len())
368 .unwrap_or(0)
369 })
370 .collect();
371
372 for w in &widths {
374 w.encode(writer)?;
375 }
376
377 for (commit_rows, &width) in self.initial_round_opened_rows.iter().zip(&widths) {
379 debug_assert_eq!(commit_rows.len(), initial_num_whir_queries);
380 for query_rows in commit_rows {
381 for row in query_rows {
382 debug_assert_eq!(row.len(), width);
383 SC::encode_base_field_iter(row.iter(), writer)?;
384 }
385 }
386 }
387
388 for merkle_proofs in &self.initial_round_merkle_proofs {
389 for proof in merkle_proofs {
390 SC::encode_digest_iter(proof.iter(), writer)?;
391 }
392 }
393 }
394
395 for non_init_round in &self.codeword_opened_values {
397 let num_queries = non_init_round.len();
398 num_queries.encode(writer)?;
399 for query_vals in non_init_round {
401 SC::encode_extension_field_iter(query_vals.iter(), writer)?;
402 }
403 }
404
405 let mut first_merkle_depth = 0;
408 if num_whir_rounds > 1 && initial_num_whir_queries > 0 {
409 first_merkle_depth = self.codeword_merkle_proofs[0][0].len();
410 }
411 first_merkle_depth.encode(writer)?;
412 for round_proofs in &self.codeword_merkle_proofs {
413 for proof in round_proofs {
414 SC::encode_digest_iter(proof.iter(), writer)?;
415 }
416 }
417
418 SC::encode_extension_field_slice(&self.final_poly, writer)
419 }
420}
421
422impl<SC: DecodableConfig> Decode for TraceVData<SC> {
425 fn decode<R: Read>(reader: &mut R) -> Result<Self> {
426 Ok(Self {
427 log_height: usize::decode(reader)?,
428 cached_commitments: SC::decode_digest_vec(reader)?,
429 })
430 }
431}
432
433impl<SC: DecodableConfig> Decode for GkrLayerClaims<SC> {
434 fn decode<R: Read>(reader: &mut R) -> Result<Self> {
435 Ok(Self {
436 p_xi_0: SC::decode_extension_field(reader)?,
437 p_xi_1: SC::decode_extension_field(reader)?,
438 q_xi_0: SC::decode_extension_field(reader)?,
439 q_xi_1: SC::decode_extension_field(reader)?,
440 })
441 }
442}
443
444impl<SC: DecodableConfig> Decode for Proof<SC> {
445 fn decode<R: Read>(reader: &mut R) -> Result<Self> {
446 let codec_version = u32::decode(reader)?;
448 if codec_version != CODEC_VERSION {
449 return Err(Error::other(format!(
450 "CODEC_VERSION mismatch, expected: {}, actual: {}",
451 CODEC_VERSION, codec_version
452 )));
453 }
454 let common_main_commit = SC::decode_digest(reader)?;
455
456 let num_airs = usize::decode(reader)?;
457 let bitmap_len = num_airs.div_ceil(8);
458 let mut bitmap: Vec<u8> = vec_with_capped_capacity(bitmap_len);
459 for _ in 0..bitmap_len {
460 bitmap.push(u8::decode(reader)?);
461 }
462 let mut trace_vdata = vec_with_capped_capacity(num_airs);
463 for byte in bitmap {
464 for i in 0u8..8 {
465 if trace_vdata.len() >= num_airs {
466 if byte >> i != 0 {
468 return Err(Error::other("trace_vdata bitmap padding bits set"));
469 }
470 break;
471 }
472 if byte & (1u8 << i) != 0 {
473 trace_vdata.push(Some(TraceVData::decode(reader)?));
474 } else {
475 trace_vdata.push(None);
476 }
477 }
478 }
479
480 let num_pvs = usize::decode(reader)?;
482 let mut public_values = vec_with_capped_capacity(num_pvs);
483 for _ in 0..num_pvs {
484 public_values.push(SC::decode_base_field_vec(reader)?);
485 }
486
487 Ok(Self {
488 common_main_commit,
489 trace_vdata,
490 public_values,
491 gkr_proof: GkrProof::decode(reader)?,
492 batch_constraint_proof: BatchConstraintProof::decode(reader)?,
493 stacking_proof: StackingProof::decode(reader)?,
494 whir_proof: WhirProof::decode(reader)?,
495 })
496 }
497}
498
499impl<SC: DecodableConfig> Decode for GkrProof<SC> {
500 fn decode<R: Read>(reader: &mut R) -> Result<Self> {
501 let logup_pow_witness = SC::decode_base_field(reader)?;
502 let q0_claim = SC::decode_extension_field(reader)?;
503 let claims_per_layer = Vec::<GkrLayerClaims<SC>>::decode(reader)?;
504
505 let num_sumcheck_polys = claims_per_layer.len().saturating_sub(1);
506 let mut sumcheck_polys = vec_with_capped_capacity(num_sumcheck_polys);
507 for round_idx_minus_one in 0..num_sumcheck_polys {
508 let n = round_idx_minus_one + 1;
509 let mut round = vec_with_capped_capacity(n);
510 for _ in 0..n {
511 round.push([
512 SC::decode_extension_field(reader)?,
513 SC::decode_extension_field(reader)?,
514 SC::decode_extension_field(reader)?,
515 ]);
516 }
517 sumcheck_polys.push(round);
518 }
519
520 Ok(Self {
521 logup_pow_witness,
522 q0_claim,
523 claims_per_layer,
524 sumcheck_polys,
525 })
526 }
527}
528
529impl<SC: DecodableConfig> Decode for BatchConstraintProof<SC> {
530 fn decode<R: Read>(reader: &mut R) -> Result<Self> {
531 let numerator_term_per_air = SC::decode_extension_field_vec(reader)?;
532 let num_present_airs = numerator_term_per_air.len();
533 let denominator_term_per_air = SC::decode_extension_field_n(reader, num_present_airs)?;
534
535 let univariate_round_coeffs = SC::decode_extension_field_vec(reader)?;
536
537 let n_max = usize::decode(reader)?;
538 let mut sumcheck_round_polys = vec_with_capped_capacity(n_max);
539 if n_max > 0 {
540 let max_degree_plus_one = usize::decode(reader)?;
541 for _ in 0..n_max {
542 sumcheck_round_polys
543 .push(SC::decode_extension_field_n(reader, max_degree_plus_one)?);
544 }
545 }
546
547 let mut column_openings = vec_with_capped_capacity(num_present_airs);
548 for _ in 0..num_present_airs {
549 let num_parts = usize::decode(reader)?;
551 let mut parts = vec_with_capped_capacity(num_parts);
552 for _ in 0..num_parts {
553 parts.push(SC::decode_extension_field_vec(reader)?);
554 }
555 column_openings.push(parts);
556 }
557
558 Ok(Self {
559 numerator_term_per_air,
560 denominator_term_per_air,
561 univariate_round_coeffs,
562 sumcheck_round_polys,
563 column_openings,
564 })
565 }
566}
567
568impl<SC: DecodableConfig> Decode for StackingProof<SC> {
569 fn decode<R: Read>(reader: &mut R) -> Result<Self> {
570 let univariate_round_coeffs = SC::decode_extension_field_vec(reader)?;
571 let num_rounds = usize::decode(reader)?;
573 let mut sumcheck_round_polys = vec_with_capped_capacity(num_rounds);
574 for _ in 0..num_rounds {
575 sumcheck_round_polys.push([
576 SC::decode_extension_field(reader)?,
577 SC::decode_extension_field(reader)?,
578 ]);
579 }
580 let num_openings = usize::decode(reader)?;
582 let mut stacking_openings = vec_with_capped_capacity(num_openings);
583 for _ in 0..num_openings {
584 stacking_openings.push(SC::decode_extension_field_vec(reader)?);
585 }
586 Ok(Self {
587 univariate_round_coeffs,
588 sumcheck_round_polys,
589 stacking_openings,
590 })
591 }
592}
593
594impl<SC: DecodableConfig> Decode for WhirProof<SC> {
595 fn decode<R: Read>(reader: &mut R) -> Result<Self> {
596 let mu_pow_witness = SC::decode_base_field(reader)?;
597 let num_whir_sumcheck_rounds = usize::decode(reader)?;
599 let mut whir_sumcheck_polys = vec_with_capped_capacity(num_whir_sumcheck_rounds);
600 for _ in 0..num_whir_sumcheck_rounds {
601 whir_sumcheck_polys.push([
602 SC::decode_extension_field(reader)?,
603 SC::decode_extension_field(reader)?,
604 ]);
605 }
606
607 let codeword_commits = SC::decode_digest_vec(reader)?;
608 let num_whir_rounds = codeword_commits.len() + 1;
609 if num_whir_sumcheck_rounds % num_whir_rounds != 0 {
610 return Err(Error::new(
611 std::io::ErrorKind::InvalidData,
612 "num_whir_sumcheck_rounds must be a multiple of num_whir_rounds",
613 ));
614 }
615 let k_whir = num_whir_sumcheck_rounds / num_whir_rounds;
616 let ood_values = SC::decode_extension_field_n(reader, num_whir_rounds - 1)?;
617 let folding_pow_witnesses = SC::decode_base_field_n(reader, num_whir_sumcheck_rounds)?;
618 let query_phase_pow_witnesses = SC::decode_base_field_n(reader, num_whir_rounds)?;
619
620 let num_commits = usize::decode(reader)?;
621 assert!(num_commits > 0);
622 let initial_num_whir_queries = usize::decode(reader)?;
623 let k_whir_exp = 1 << k_whir;
624 let mut merkle_depth = 0;
625 if initial_num_whir_queries > 0 {
626 merkle_depth = usize::decode(reader)?;
627 }
628
629 let mut widths = vec_with_capped_capacity(num_commits);
630 if initial_num_whir_queries > 0 {
631 for _ in 0..num_commits {
632 widths.push(usize::decode(reader)?);
633 }
634 }
635
636 let decoded_widths = widths.len();
637 let mut initial_round_opened_rows = vec_with_capped_capacity(num_commits);
638 for width in widths.into_iter().chain(std::iter::repeat_n(
639 0,
640 num_commits.saturating_sub(decoded_widths),
641 )) {
642 let mut opened_rows = vec_with_capped_capacity(initial_num_whir_queries);
643 for _ in 0..initial_num_whir_queries {
644 let mut rows = vec_with_capped_capacity(k_whir_exp);
646 for _ in 0..k_whir_exp {
647 rows.push(SC::decode_base_field_n(reader, width)?);
648 }
649 opened_rows.push(rows);
650 }
651 initial_round_opened_rows.push(opened_rows);
652 }
653
654 let mut initial_round_merkle_proofs = vec_with_capped_capacity(num_commits);
655 for _ in 0..num_commits {
656 let mut merkle_proofs = vec_with_capped_capacity(initial_num_whir_queries);
657 for _ in 0..initial_num_whir_queries {
658 merkle_proofs.push(SC::decode_digest_n(reader, merkle_depth)?);
659 }
660 initial_round_merkle_proofs.push(merkle_proofs);
661 }
662
663 let mut codeword_opened_values = vec_with_capped_capacity(num_whir_rounds - 1);
664 for _ in 0..num_whir_rounds - 1 {
665 let num_queries = usize::decode(reader)?;
666 let mut opened_values = vec_with_capped_capacity(num_queries);
667 for _ in 0..num_queries {
668 opened_values.push(SC::decode_extension_field_n(reader, k_whir_exp)?);
669 }
670 codeword_opened_values.push(opened_values);
671 }
672
673 merkle_depth = usize::decode(reader)?;
674 let mut codeword_merkle_proofs = vec_with_capped_capacity(num_whir_rounds - 1);
675 for opened_values in codeword_opened_values.iter() {
676 let num_queries = opened_values.len();
677 let mut merkle_proof: Vec<_> = vec_with_capped_capacity(num_queries);
678 for _ in 0..num_queries {
679 merkle_proof.push(SC::decode_digest_n(reader, merkle_depth)?);
680 }
681 codeword_merkle_proofs.push(merkle_proof);
682 merkle_depth -= 1;
683 }
684
685 let final_poly = SC::decode_extension_field_vec(reader)?;
686
687 Ok(Self {
688 mu_pow_witness,
689 whir_sumcheck_polys,
690 codeword_commits,
691 ood_values,
692 folding_pow_witnesses,
693 query_phase_pow_witnesses,
694 initial_round_opened_rows,
695 initial_round_merkle_proofs,
696 codeword_opened_values,
697 codeword_merkle_proofs,
698 final_poly,
699 })
700 }
701}