p3_uni_stark/
verifier.rs
1use alloc::vec;
2use alloc::vec::Vec;
3
4use itertools::Itertools;
5use p3_air::{Air, BaseAir};
6use p3_challenger::{CanObserve, CanSample, FieldChallenger};
7use p3_commit::{Pcs, PolynomialSpace};
8use p3_field::{Field, FieldAlgebra, FieldExtensionAlgebra};
9use p3_matrix::dense::RowMajorMatrixView;
10use p3_matrix::stack::VerticalPair;
11use p3_util::zip_eq::zip_eq;
12use tracing::instrument;
13
14use crate::symbolic_builder::{get_log_quotient_degree, SymbolicAirBuilder};
15use crate::{PcsError, Proof, StarkGenericConfig, Val, VerifierConstraintFolder};
16
17#[instrument(skip_all)]
18pub fn verify<SC, A>(
19 config: &SC,
20 air: &A,
21 challenger: &mut SC::Challenger,
22 proof: &Proof<SC>,
23 public_values: &Vec<Val<SC>>,
24) -> Result<(), VerificationError<PcsError<SC>>>
25where
26 SC: StarkGenericConfig,
27 A: Air<SymbolicAirBuilder<Val<SC>>> + for<'a> Air<VerifierConstraintFolder<'a, SC>>,
28{
29 let Proof {
30 commitments,
31 opened_values,
32 opening_proof,
33 degree_bits,
34 } = proof;
35
36 let degree = 1 << degree_bits;
37 let log_quotient_degree = get_log_quotient_degree::<Val<SC>, A>(air, 0, public_values.len());
38 let quotient_degree = 1 << log_quotient_degree;
39
40 let pcs = config.pcs();
41 let trace_domain = pcs.natural_domain_for_degree(degree);
42 let quotient_domain =
43 trace_domain.create_disjoint_domain(1 << (degree_bits + log_quotient_degree));
44 let quotient_chunks_domains = quotient_domain.split_domains(quotient_degree);
45
46 let air_width = <A as BaseAir<Val<SC>>>::width(air);
47 let valid_shape = opened_values.trace_local.len() == air_width
48 && opened_values.trace_next.len() == air_width
49 && opened_values.quotient_chunks.len() == quotient_degree
50 && opened_values
51 .quotient_chunks
52 .iter()
53 .all(|qc| qc.len() == <SC::Challenge as FieldExtensionAlgebra<Val<SC>>>::D);
54 if !valid_shape {
55 return Err(VerificationError::InvalidProofShape);
56 }
57
58 challenger.observe(Val::<SC>::from_canonical_usize(proof.degree_bits));
60 challenger.observe(commitments.trace.clone());
67 challenger.observe_slice(public_values);
68 let alpha: SC::Challenge = challenger.sample_ext_element();
69 challenger.observe(commitments.quotient_chunks.clone());
70
71 let zeta: SC::Challenge = challenger.sample();
72 let zeta_next = trace_domain.next_point(zeta).unwrap();
73
74 pcs.verify(
75 vec![
76 (
77 commitments.trace.clone(),
78 vec![(
79 trace_domain,
80 vec![
81 (zeta, opened_values.trace_local.clone()),
82 (zeta_next, opened_values.trace_next.clone()),
83 ],
84 )],
85 ),
86 (
87 commitments.quotient_chunks.clone(),
88 zip_eq(
89 quotient_chunks_domains.iter(),
90 &opened_values.quotient_chunks,
91 VerificationError::InvalidProofShape,
92 )?
93 .map(|(domain, values)| (*domain, vec![(zeta, values.clone())]))
94 .collect_vec(),
95 ),
96 ],
97 opening_proof,
98 challenger,
99 )
100 .map_err(VerificationError::InvalidOpeningArgument)?;
101
102 let zps = quotient_chunks_domains
103 .iter()
104 .enumerate()
105 .map(|(i, domain)| {
106 quotient_chunks_domains
107 .iter()
108 .enumerate()
109 .filter(|(j, _)| *j != i)
110 .map(|(_, other_domain)| {
111 other_domain.zp_at_point(zeta)
112 * other_domain.zp_at_point(domain.first_point()).inverse()
113 })
114 .product::<SC::Challenge>()
115 })
116 .collect_vec();
117
118 let quotient = opened_values
119 .quotient_chunks
120 .iter()
121 .enumerate()
122 .map(|(ch_i, ch)| {
123 zps[ch_i]
124 * ch.iter()
125 .enumerate()
126 .map(|(e_i, &c)| SC::Challenge::monomial(e_i) * c)
127 .sum::<SC::Challenge>()
128 })
129 .sum::<SC::Challenge>();
130
131 let sels = trace_domain.selectors_at_point(zeta);
132
133 let main = VerticalPair::new(
134 RowMajorMatrixView::new_row(&opened_values.trace_local),
135 RowMajorMatrixView::new_row(&opened_values.trace_next),
136 );
137
138 let mut folder = VerifierConstraintFolder {
139 main,
140 public_values,
141 is_first_row: sels.is_first_row,
142 is_last_row: sels.is_last_row,
143 is_transition: sels.is_transition,
144 alpha,
145 accumulator: SC::Challenge::ZERO,
146 };
147 air.eval(&mut folder);
148 let folded_constraints = folder.accumulator;
149
150 if folded_constraints * sels.inv_zeroifier != quotient {
153 return Err(VerificationError::OodEvaluationMismatch);
154 }
155
156 Ok(())
157}
158
159#[derive(Debug)]
160pub enum VerificationError<PcsErr> {
161 InvalidProofShape,
162 InvalidOpeningArgument(PcsErr),
164 OodEvaluationMismatch,
167}