openvm_stark_backend/verifier/
constraints.rs1use std::marker::PhantomData;
2
3use itertools::Itertools;
4use p3_commit::PolynomialSpace;
5use p3_field::{BasedVectorSpace, Field, PrimeCharacteristicRing};
6use p3_matrix::{dense::RowMajorMatrixView, stack::VerticalPair};
7use tracing::instrument;
8
9use super::{
10 error::VerificationError,
11 folder::{GenericVerifierConstraintFolder, VerifierConstraintFolder},
12};
13use crate::{
14 air_builders::symbolic::SymbolicExpressionDag,
15 config::{Domain, StarkGenericConfig, Val},
16 proof::AdjacentOpenedValues,
17};
18
19#[allow(clippy::too_many_arguments)]
20#[instrument(skip_all, level = "trace")]
21pub fn verify_single_rap_constraints<SC>(
22 constraints: &SymbolicExpressionDag<Val<SC>>,
23 preprocessed_values: Option<&AdjacentOpenedValues<SC::Challenge>>,
24 partitioned_main_values: Vec<&AdjacentOpenedValues<SC::Challenge>>,
25 after_challenge_values: Vec<&AdjacentOpenedValues<SC::Challenge>>,
26 quotient_chunks: &[Vec<SC::Challenge>],
27 domain: Domain<SC>, qc_domains: &[Domain<SC>],
29 zeta: SC::Challenge,
30 alpha: SC::Challenge,
31 challenges: &[Vec<SC::Challenge>],
32 public_values: &[Val<SC>],
33 exposed_values_after_challenge: &[Vec<SC::Challenge>],
34) -> Result<(), VerificationError>
35where
36 SC: StarkGenericConfig,
37{
38 let zps = qc_domains
39 .iter()
40 .enumerate()
41 .map(|(i, domain)| {
42 qc_domains
43 .iter()
44 .enumerate()
45 .filter(|(j, _)| *j != i)
46 .map(|(_, other_domain)| {
47 other_domain.vanishing_poly_at_point(zeta)
48 * other_domain
49 .vanishing_poly_at_point(domain.first_point())
50 .inverse()
51 })
52 .product::<SC::Challenge>()
53 })
54 .collect_vec();
55
56 let quotient = quotient_chunks
57 .iter()
58 .enumerate()
59 .map(|(ch_i, ch)| {
60 ch.iter()
61 .enumerate()
62 .map(|(e_i, &c)| {
63 zps[ch_i]
64 * SC::Challenge::ith_basis_element(e_i)
65 .expect("basis element index out of bounds")
66 * c
67 })
68 .sum::<SC::Challenge>()
69 })
70 .sum::<SC::Challenge>();
71
72 let unflatten = |v: &[SC::Challenge]| {
73 v.chunks_exact(SC::Challenge::DIMENSION)
74 .map(|chunk| {
75 chunk
76 .iter()
77 .enumerate()
78 .map(|(e_i, &c)| {
79 SC::Challenge::ith_basis_element(e_i)
80 .expect("basis element index out of bounds")
81 * c
82 })
83 .sum()
84 })
85 .collect::<Vec<SC::Challenge>>()
86 };
87
88 let sels = domain.selectors_at_point(zeta);
89
90 let (preprocessed_local, preprocessed_next) = preprocessed_values
91 .as_ref()
92 .map(|values| (values.local.as_slice(), values.next.as_slice()))
93 .unwrap_or((&[], &[]));
94 let preprocessed = VerticalPair::new(
95 RowMajorMatrixView::new_row(preprocessed_local),
96 RowMajorMatrixView::new_row(preprocessed_next),
97 );
98
99 let partitioned_main: Vec<_> = partitioned_main_values
100 .into_iter()
101 .map(|values| {
102 VerticalPair::new(
103 RowMajorMatrixView::new_row(&values.local),
104 RowMajorMatrixView::new_row(&values.next),
105 )
106 })
107 .collect();
108
109 let after_challenge_ext_values: Vec<_> = after_challenge_values
110 .into_iter()
111 .map(|values| {
112 let [local, next] = [&values.local, &values.next]
113 .map(|flattened_ext_values| unflatten(flattened_ext_values));
114 (local, next)
115 })
116 .collect();
117 let after_challenge = after_challenge_ext_values
118 .iter()
119 .map(|(local, next)| {
120 VerticalPair::new(
121 RowMajorMatrixView::new_row(local),
122 RowMajorMatrixView::new_row(next),
123 )
124 })
125 .collect();
126
127 let mut folder: VerifierConstraintFolder<'_, SC> = GenericVerifierConstraintFolder {
128 preprocessed,
129 partitioned_main,
130 after_challenge,
131 is_first_row: sels.is_first_row,
132 is_last_row: sels.is_last_row,
133 is_transition: sels.is_transition,
134 alpha,
135 accumulator: SC::Challenge::ZERO,
136 challenges,
137 public_values,
138 exposed_values_after_challenge,
139 _marker: PhantomData,
140 };
141 folder.eval_constraints(constraints);
142
143 let folded_constraints = folder.accumulator;
144 if folded_constraints * sels.inv_vanishing != quotient {
147 return Err(VerificationError::OodEvaluationMismatch);
148 }
149
150 Ok(())
151}