1use crate::{
2 loader::{LoadedScalar, Loader},
3 pcs::{self, AccumulationScheme, AccumulatorEncoding, PolynomialCommitmentScheme},
4 util::{
5 arithmetic::{CurveAffine, Field, Rotation},
6 msm::Msm,
7 transcript::TranscriptRead,
8 Itertools,
9 },
10 verifier::plonk::protocol::{
11 CommonPolynomial::Lagrange, CommonPolynomialEvaluation, LinearizationStrategy,
12 PlonkProtocol, Query,
13 },
14 Error,
15};
16use std::{collections::BTreeMap, iter};
17
18#[derive(Clone, Debug)]
21pub struct PlonkProof<C, L, AS>
22where
23 C: CurveAffine,
24 L: Loader<C>,
25 AS: AccumulationScheme<C, L> + PolynomialCommitmentScheme<C, L, Output = AS::Accumulator>,
26{
27 pub committed_instances: Option<Vec<L::LoadedEcPoint>>,
29 pub witnesses: Vec<L::LoadedEcPoint>,
31 pub challenges: Vec<L::LoadedScalar>,
33 pub quotients: Vec<L::LoadedEcPoint>,
35 pub z: L::LoadedScalar,
37 pub evaluations: Vec<L::LoadedScalar>,
39 pub pcs: <AS as PolynomialCommitmentScheme<C, L>>::Proof,
41 pub old_accumulators: Vec<AS::Accumulator>,
43}
44
45impl<C, L, AS> PlonkProof<C, L, AS>
46where
47 C: CurveAffine,
48 L: Loader<C>,
49 AS: AccumulationScheme<C, L> + PolynomialCommitmentScheme<C, L, Output = AS::Accumulator>,
50{
51 pub fn read<T, AE>(
53 svk: &<AS as PolynomialCommitmentScheme<C, L>>::VerifyingKey,
54 protocol: &PlonkProtocol<C, L>,
55 instances: &[Vec<L::LoadedScalar>],
56 transcript: &mut T,
57 ) -> Result<Self, Error>
58 where
59 T: TranscriptRead<C, L>,
60 AE: AccumulatorEncoding<C, L, Accumulator = AS::Accumulator>,
61 {
62 if let Some(transcript_initial_state) = &protocol.transcript_initial_state {
63 transcript.common_scalar(transcript_initial_state)?;
64 }
65
66 if protocol.num_instance != instances.iter().map(|instances| instances.len()).collect_vec()
67 {
68 return Err(Error::InvalidInstances);
69 }
70
71 let committed_instances = if let Some(ick) = &protocol.instance_committing_key {
72 let loader = transcript.loader();
73 let bases =
74 ick.bases.iter().map(|value| loader.ec_point_load_const(value)).collect_vec();
75 let constant = ick.constant.as_ref().map(|value| loader.ec_point_load_const(value));
76
77 let committed_instances = instances
78 .iter()
79 .map(|instances| {
80 instances
81 .iter()
82 .zip(bases.iter())
83 .map(|(scalar, base)| Msm::<C, L>::base(base) * scalar)
84 .chain(constant.as_ref().map(Msm::base))
85 .sum::<Msm<_, _>>()
86 .evaluate(None)
87 })
88 .collect_vec();
89 for committed_instance in committed_instances.iter() {
90 transcript.common_ec_point(committed_instance)?;
91 }
92
93 Some(committed_instances)
94 } else {
95 for instances in instances.iter() {
96 for instance in instances.iter() {
97 transcript.common_scalar(instance)?;
98 }
99 }
100
101 None
102 };
103
104 let (witnesses, challenges) = {
105 let (witnesses, challenges) = protocol
106 .num_witness
107 .iter()
108 .zip(protocol.num_challenge.iter())
109 .map(|(&n, &m)| {
110 Ok((transcript.read_n_ec_points(n)?, transcript.squeeze_n_challenges(m)))
111 })
112 .collect::<Result<Vec<_>, Error>>()?
113 .into_iter()
114 .unzip::<_, _, Vec<_>, Vec<_>>();
115
116 (
117 witnesses.into_iter().flatten().collect_vec(),
118 challenges.into_iter().flatten().collect_vec(),
119 )
120 };
121
122 let quotients = transcript.read_n_ec_points(protocol.quotient.num_chunk())?;
123
124 let z = transcript.squeeze_challenge();
125 let evaluations = transcript.read_n_scalars(protocol.evaluations.len())?;
126
127 let pcs = <AS as PolynomialCommitmentScheme<C, L>>::read_proof(
128 svk,
129 &Self::empty_queries(protocol),
130 transcript,
131 )?;
132
133 let old_accumulators = protocol
134 .accumulator_indices
135 .iter()
136 .map(|accumulator_indices| {
137 AE::from_repr(
138 &accumulator_indices.iter().map(|&(i, j)| &instances[i][j]).collect_vec(),
139 )
140 })
141 .collect::<Result<Vec<_>, _>>()?;
142
143 Ok(Self {
144 committed_instances,
145 witnesses,
146 challenges,
147 quotients,
148 z,
149 evaluations,
150 pcs,
151 old_accumulators,
152 })
153 }
154
155 pub fn empty_queries(protocol: &PlonkProtocol<C, L>) -> Vec<pcs::Query<Rotation>> {
157 protocol.queries.iter().map(|query| pcs::Query::new(query.poly, query.rotation)).collect()
159 }
160
161 pub(super) fn queries(
162 &self,
163 protocol: &PlonkProtocol<C, L>,
164 mut evaluations: BTreeMap<Query, L::LoadedScalar>,
165 ) -> Vec<pcs::Query<Rotation, L::LoadedScalar>> {
166 if protocol.queries.is_empty() {
167 return vec![];
168 }
169 let loader = evaluations[&protocol.queries[0]].loader();
170 let rotations =
171 protocol.queries.iter().map(|query| query.rotation).sorted().dedup().collect_vec();
172 let loaded_shifts = if let Some(domain) = protocol.domain_as_witness.as_ref() {
173 BTreeMap::from_iter(
175 rotations.into_iter().map(|rotation| (rotation, domain.rotate_one(rotation))),
176 )
177 } else {
178 BTreeMap::from_iter(rotations.into_iter().map(|rotation| {
179 (
180 rotation,
181 loader.load_const(&protocol.domain.rotate_scalar(C::Scalar::ONE, rotation)),
182 )
183 }))
184 };
185 Self::empty_queries(protocol)
186 .into_iter()
187 .zip(protocol.queries.iter().map(|query| evaluations.remove(query).unwrap()))
188 .map(|(query, eval)| {
189 let shift = loaded_shifts[&query.shift].clone();
190 query.with_evaluation(shift, eval)
191 })
192 .collect()
193 }
194
195 pub(super) fn commitments<'a>(
196 &'a self,
197 protocol: &'a PlonkProtocol<C, L>,
198 common_poly_eval: &CommonPolynomialEvaluation<C, L>,
199 evaluations: &mut BTreeMap<Query, L::LoadedScalar>,
200 ) -> Result<Vec<Msm<'a, C, L>>, Error> {
201 let loader = common_poly_eval.zn().loader();
202 let mut commitments = iter::empty()
203 .chain(protocol.preprocessed.iter().map(Msm::base))
204 .chain(
205 self.committed_instances
206 .as_ref()
207 .map(|committed_instances| {
208 committed_instances.iter().map(Msm::base).collect_vec()
209 })
210 .unwrap_or_else(|| {
211 iter::repeat_with(Default::default)
212 .take(protocol.num_instance.len())
213 .collect_vec()
214 }),
215 )
216 .chain(self.witnesses.iter().map(Msm::base))
217 .collect_vec();
218
219 let numerator = protocol.quotient.numerator.evaluate(
220 &|scalar| Ok(Msm::constant(loader.load_const(&scalar))),
221 &|poly| Ok(Msm::constant(common_poly_eval.get(poly).clone())),
222 &|query| {
223 evaluations
224 .get(&query)
225 .cloned()
226 .map(Msm::constant)
227 .or_else(|| {
228 (query.rotation == Rotation::cur())
229 .then(|| commitments.get(query.poly).cloned())
230 .flatten()
231 })
232 .ok_or_else(|| Error::InvalidProtocol(format!("Missing query {query:?}")))
233 },
234 &|index| {
235 self.challenges
236 .get(index)
237 .cloned()
238 .map(Msm::constant)
239 .ok_or_else(|| Error::InvalidProtocol(format!("Missing challenge {index}")))
240 },
241 &|a| Ok(-a?),
242 &|a, b| Ok(a? + b?),
243 &|a, b| {
244 let (a, b) = (a?, b?);
245 match (a.size(), b.size()) {
246 (0, _) => Ok(b * &a.try_into_constant().unwrap()),
247 (_, 0) => Ok(a * &b.try_into_constant().unwrap()),
248 (_, _) => Err(Error::InvalidProtocol("Invalid linearization".to_string())),
249 }
250 },
251 &|a, scalar| Ok(a? * &loader.load_const(&scalar)),
252 )?;
253
254 let quotient_query = Query::new(
255 protocol.preprocessed.len() + protocol.num_instance.len() + self.witnesses.len(),
256 Rotation::cur(),
257 );
258 let quotient = common_poly_eval
259 .zn()
260 .pow_const(protocol.quotient.chunk_degree as u64)
261 .powers(self.quotients.len())
262 .into_iter()
263 .zip(self.quotients.iter().map(Msm::base))
264 .map(|(coeff, chunk)| chunk * &coeff)
265 .sum::<Msm<_, _>>();
266 match protocol.linearization {
267 Some(LinearizationStrategy::WithoutConstant) => {
268 let linearization_query = Query::new(quotient_query.poly + 1, Rotation::cur());
269 let (msm, constant) = numerator.split();
270 commitments.push(quotient);
271 commitments.push(msm);
272 evaluations.insert(
273 quotient_query,
274 (constant.unwrap_or_else(|| loader.load_zero())
275 + evaluations.get(&linearization_query).unwrap())
276 * common_poly_eval.zn_minus_one_inv(),
277 );
278 }
279 Some(LinearizationStrategy::MinusVanishingTimesQuotient) => {
280 let (msm, constant) =
281 (numerator - quotient * common_poly_eval.zn_minus_one()).split();
282 commitments.push(msm);
283 evaluations.insert(quotient_query, constant.unwrap_or_else(|| loader.load_zero()));
284 }
285 None => {
286 commitments.push(quotient);
287 evaluations.insert(
288 quotient_query,
289 numerator.try_into_constant().ok_or_else(|| {
290 Error::InvalidProtocol("Invalid linearization".to_string())
291 })? * common_poly_eval.zn_minus_one_inv(),
292 );
293 }
294 }
295
296 Ok(commitments)
297 }
298
299 pub(super) fn evaluations(
300 &self,
301 protocol: &PlonkProtocol<C, L>,
302 instances: &[Vec<L::LoadedScalar>],
303 common_poly_eval: &CommonPolynomialEvaluation<C, L>,
304 ) -> Result<BTreeMap<Query, L::LoadedScalar>, Error> {
305 let loader = common_poly_eval.zn().loader();
306 let instance_evals = protocol.instance_committing_key.is_none().then(|| {
307 let offset = protocol.preprocessed.len();
308 let queries = {
309 let range = offset..offset + protocol.num_instance.len();
310 protocol
311 .quotient
312 .numerator
313 .used_query()
314 .into_iter()
315 .filter(move |query| range.contains(&query.poly))
316 };
317 queries
318 .map(move |query| {
319 let instances = instances[query.poly - offset].iter();
320 let l_i_minus_r = (-query.rotation.0..)
321 .map(|i_minus_r| common_poly_eval.get(Lagrange(i_minus_r)));
322 let eval = loader.sum_products(&instances.zip(l_i_minus_r).collect_vec());
323 (query, eval)
324 })
325 .collect_vec()
326 });
327
328 let evals = iter::empty()
329 .chain(instance_evals.into_iter().flatten())
330 .chain(protocol.evaluations.iter().cloned().zip(self.evaluations.iter().cloned()))
331 .collect();
332
333 Ok(evals)
334 }
335}