openvm_stark_backend/prover/cpu/
opener.rs
1use std::fmt::Debug;
2
3use itertools::Itertools;
4use p3_commit::{Pcs, PolynomialSpace};
5use tracing::instrument;
6
7use crate::{
8 config::{Domain, PcsProof, PcsProverData, StarkGenericConfig},
9 proof::{AdjacentOpenedValues, OpenedValues, OpeningProof},
10};
11
12pub struct OpeningProver<'pcs, SC: StarkGenericConfig> {
13 pcs: &'pcs SC::Pcs,
14 zeta: SC::Challenge,
15}
16
17impl<'pcs, SC: StarkGenericConfig> OpeningProver<'pcs, SC> {
18 pub fn new(pcs: &'pcs SC::Pcs, zeta: SC::Challenge) -> Self {
19 Self { pcs, zeta }
20 }
21
22 #[instrument(name = "PCS opening proofs", skip_all)]
28 pub fn open(
29 &self,
30 challenger: &mut SC::Challenger,
31 preprocessed: Vec<(&PcsProverData<SC>, Domain<SC>)>,
34 main: Vec<(&PcsProverData<SC>, Vec<Domain<SC>>)>,
37 after_challenge: Vec<(&PcsProverData<SC>, Vec<Domain<SC>>)>,
39 quotient_data: &PcsProverData<SC>,
41 quotient_degrees: &[u8],
43 ) -> OpeningProof<PcsProof<SC>, SC::Challenge> {
44 let preprocessed: Vec<_> = preprocessed
45 .into_iter()
46 .map(|(data, domain)| (data, vec![domain]))
47 .collect();
48
49 let zeta = self.zeta;
50 let mut rounds = preprocessed
51 .iter()
52 .chain(main.iter())
53 .chain(after_challenge.iter())
54 .map(|(data, domains)| {
55 let points_per_mat = domains
56 .iter()
57 .map(|domain| vec![zeta, domain.next_point(zeta).unwrap()])
58 .collect_vec();
59 (*data, points_per_mat)
60 })
61 .collect_vec();
62
63 let num_chunks = quotient_degrees.iter().sum::<u8>() as usize;
65 let quotient_opening_points = vec![vec![zeta]; num_chunks];
66 rounds.push((quotient_data, quotient_opening_points));
67
68 let (mut opening_values, opening_proof) = self.pcs.open(rounds, challenger);
69
70 let mut quotient_openings = opening_values.pop().expect("Should have quotient opening");
72
73 let num_after_challenge = after_challenge.len();
74 let after_challenge_openings = opening_values
75 .split_off(opening_values.len() - num_after_challenge)
76 .into_iter()
77 .map(collect_trace_openings)
78 .collect_vec();
79 assert_eq!(
80 after_challenge_openings.len(),
81 num_after_challenge,
82 "Incorrect number of after challenge trace openings"
83 );
84
85 let main_openings = opening_values
86 .split_off(preprocessed.len())
87 .into_iter()
88 .map(collect_trace_openings)
89 .collect_vec();
90 assert_eq!(
91 main_openings.len(),
92 main.len(),
93 "Incorrect number of main trace openings"
94 );
95
96 let preprocessed_openings = opening_values
97 .into_iter()
98 .map(|values| {
99 let mut openings = collect_trace_openings(values);
100 openings
101 .pop()
102 .expect("Preprocessed trace should be opened at 1 point")
103 })
104 .collect_vec();
105 assert_eq!(
106 preprocessed_openings.len(),
107 preprocessed.len(),
108 "Incorrect number of preprocessed trace openings"
109 );
110
111 let quotient_openings = quotient_degrees
113 .iter()
114 .map(|&chunk_size| {
115 quotient_openings
116 .drain(..chunk_size as usize)
117 .map(|mut op| {
118 op.pop()
119 .expect("quotient chunk should be opened at 1 point")
120 })
121 .collect_vec()
122 })
123 .collect_vec();
124
125 OpeningProof {
126 proof: opening_proof,
127 values: OpenedValues {
128 preprocessed: preprocessed_openings,
129 main: main_openings,
130 after_challenge: after_challenge_openings,
131 quotient: quotient_openings,
132 },
133 }
134 }
135}
136
137fn collect_trace_openings<Challenge: Debug>(
138 ops: Vec<Vec<Vec<Challenge>>>,
139) -> Vec<AdjacentOpenedValues<Challenge>> {
140 ops.into_iter()
141 .map(|op| {
142 let [local, next] = op.try_into().expect("Should have 2 openings");
143 AdjacentOpenedValues { local, next }
144 })
145 .collect()
146}