midnight_circuits/verifier/
accumulator.rs1use std::collections::BTreeMap;
31
32use ff::Field;
33use group::Group;
34use midnight_proofs::{
35 circuit::{Layouter, Value},
36 plonk::Error,
37 poly::{
38 kzg::{
39 msm::{DualMSM, MSMKZG},
40 params::ParamsVerifierKZG,
41 },
42 CommitmentLabel,
43 },
44};
45use num_bigint::BigUint;
46use num_traits::One;
47
48#[cfg(not(feature = "truncated-challenges"))]
49use crate::verifier::utils::powers;
50#[cfg(feature = "truncated-challenges")]
51use crate::verifier::utils::{truncate_off_circuit, truncated_powers};
52use crate::{
53 instructions::{hash::HashCPU, HashInstructions, PublicInputInstructions},
54 types::{AssignedBit, InnerValue, Instantiable},
55 verifier::{
56 fixed_commitment_name,
57 msm::{AssignedMsm, Msm},
58 perm_commitment_name,
59 utils::AssignedBoundedScalar,
60 SelfEmulation,
61 },
62};
63
64#[derive(Clone, Debug)]
70pub struct Accumulator<S: SelfEmulation> {
71 lhs: Msm<S>,
72 rhs: Msm<S>,
73}
74
75#[derive(Clone, Debug)]
77pub struct AssignedAccumulator<C: SelfEmulation> {
78 pub(crate) lhs: AssignedMsm<C>,
79 pub(crate) rhs: AssignedMsm<C>,
80}
81
82impl<S: SelfEmulation> Accumulator<S> {
83 pub fn from_dual_msm(
91 dual_msm: DualMSM<S::Engine>,
92 prefix: &str,
93 fixed_bases: &BTreeMap<String, S::C>,
94 ) -> Self {
95 let (lhs, rhs) = dual_msm.split();
96
97 let process_msm = |msm: Vec<(&CommitmentLabel, &S::F, &S::C)>| {
98 let mut bases = Vec::with_capacity(msm.len());
99 let mut scalars = Vec::with_capacity(msm.len());
100 let mut fixed_base_scalars = BTreeMap::new();
101 for (label, scalar, base) in msm {
102 match label {
103 CommitmentLabel::Fixed(i) => {
104 let name = fixed_commitment_name(prefix, *i);
105 assert_eq!(fixed_bases.get(&name), Some(base));
106 fixed_base_scalars.insert(name, *scalar);
107 }
108 CommitmentLabel::Permutation(i) => {
109 let name = perm_commitment_name(prefix, *i);
110 assert_eq!(fixed_bases.get(&name), Some(base));
111 fixed_base_scalars.insert(name, *scalar);
112 }
113 CommitmentLabel::Custom(s) if s == "-G" => {
114 assert_eq!(fixed_bases.get(s), Some(base));
115 fixed_base_scalars.insert("-G".into(), *scalar);
116 }
117 _ => {
118 bases.push(*base);
119 scalars.push(*scalar);
120 }
121 }
122 }
123 (bases, scalars, fixed_base_scalars)
124 };
125
126 let (lhs_bases, lhs_scalars, lhs_fixed_base_scalars) = process_msm(lhs);
127 let (rhs_bases, rhs_scalars, rhs_fixed_base_scalars) = process_msm(rhs);
128
129 Accumulator {
130 lhs: Msm::new(&lhs_bases, &lhs_scalars, &lhs_fixed_base_scalars),
131 rhs: Msm::new(&rhs_bases, &rhs_scalars, &rhs_fixed_base_scalars),
132 }
133 }
134
135 pub fn check(
139 &self,
140 params: &ParamsVerifierKZG<S::Engine>,
141 fixed_bases: &BTreeMap<String, S::C>,
142 ) -> bool {
143 let lhs = MSMKZG::<S::Engine>::from_base(&self.lhs.eval(fixed_bases));
144 let rhs = MSMKZG::<S::Engine>::from_base(&self.rhs.eval(fixed_bases));
145 DualMSM::new(lhs, rhs).check(params)
146 }
147
148 pub fn trivial(fixed_base_names: &[String]) -> Self {
156 let zero_fixed = fixed_base_names.iter().map(|n| (n.clone(), S::F::ZERO)).collect();
157 Accumulator {
158 lhs: Msm::new(&[S::C::identity()], &[S::F::ONE], &BTreeMap::new()),
159 rhs: Msm::new(&[S::C::identity()], &[S::F::ONE], &zero_fixed),
160 }
161 }
162
163 pub fn new(lhs: Msm<S>, rhs: Msm<S>) -> Self {
165 Accumulator { lhs, rhs }
166 }
167
168 pub fn lhs(&self) -> Msm<S> {
170 self.lhs.clone()
171 }
172
173 pub fn rhs(&self) -> Msm<S> {
175 self.rhs.clone()
176 }
177
178 pub fn resolve_fixed_bases(&mut self, fixed_bases: &BTreeMap<String, S::C>) {
190 self.lhs.resolve_fixed_bases(fixed_bases);
191 self.rhs.resolve_fixed_bases(fixed_bases);
192 }
193
194 pub fn collapse(&mut self) {
200 self.lhs.collapse();
201 self.rhs.collapse();
202 }
203
204 pub fn accumulate(accs: &[Self]) -> Self {
207 let hash_input =
208 accs.iter().flat_map(AssignedAccumulator::as_public_input).collect::<Vec<_>>();
209
210 let r = <S::SpongeChip as HashCPU<S::F, S::F>>::hash(&hash_input);
211 let rs = (0..accs.len()).map(|i| r.pow([i as u64]));
212 #[cfg(feature = "truncated-challenges")]
213 let rs = rs.map(truncate_off_circuit).collect::<Vec<_>>();
214
215 let mut acc = accs[0].clone();
216 for (other, ri) in accs.iter().zip(rs).skip(1) {
217 acc.lhs = acc.lhs.accumulate_with_r(&other.lhs, ri);
218 acc.rhs = acc.rhs.accumulate_with_r(&other.rhs, ri);
219 }
220
221 acc
222 }
223}
224
225impl<S: SelfEmulation> InnerValue for AssignedAccumulator<S> {
226 type Element = Accumulator<S>;
227
228 fn value(&self) -> Value<Accumulator<S>> {
229 (self.lhs.value())
230 .zip(self.rhs.value())
231 .map(|(lhs, rhs)| Accumulator { lhs, rhs })
232 }
233}
234
235impl<S: SelfEmulation> Instantiable<S::F> for AssignedAccumulator<S> {
236 fn as_public_input(acc: &Accumulator<S>) -> Vec<S::F> {
237 [
238 AssignedMsm::as_public_input(&acc.lhs),
239 AssignedMsm::as_public_input(&acc.rhs),
240 ]
241 .into_iter()
242 .flatten()
243 .collect()
244 }
245
246 fn from_public_input(_fields: &[S::F]) -> Option<Accumulator<S>> {
247 unimplemented!("Size of inner MSMs cannot be known from public input format.")
248 }
249}
250
251impl<S: SelfEmulation> AssignedAccumulator<S> {
252 pub fn as_public_input_with_committed_scalars(acc: &Accumulator<S>) -> (Vec<S::F>, Vec<S::F>) {
259 let (rhs_scalars, rhs_committed_scalars) =
260 AssignedMsm::as_public_input_with_committed_scalars(&acc.rhs);
261
262 let normal_instance = [AssignedMsm::as_public_input(&acc.lhs), rhs_scalars]
263 .into_iter()
264 .flatten()
265 .collect();
266
267 (normal_instance, rhs_committed_scalars)
268 }
269}
270
271impl<S: SelfEmulation> AssignedAccumulator<S> {
272 #[allow(clippy::too_many_arguments)]
278 pub fn assign(
279 layouter: &mut impl Layouter<S::F>,
280 curve_chip: &S::CurveChip,
281 scalar_chip: &S::ScalarChip,
282 lhs_len: usize,
283 rhs_len: usize,
284 lhs_fixed_base_names: &[String],
285 rhs_fixed_base_names: &[String],
286 acc_val: Value<Accumulator<S>>,
287 ) -> Result<Self, Error> {
288 let (acc_lhs_val, acc_rhs_val) = acc_val.map(|acc| (acc.lhs, acc.rhs)).unzip();
289 Ok(AssignedAccumulator::new(
290 AssignedMsm::<S>::assign(
291 layouter,
292 curve_chip,
293 scalar_chip,
294 lhs_len,
295 lhs_fixed_base_names,
296 acc_lhs_val,
297 )?,
298 AssignedMsm::<S>::assign(
299 layouter,
300 curve_chip,
301 scalar_chip,
302 rhs_len,
303 rhs_fixed_base_names,
304 acc_rhs_val,
305 )?,
306 ))
307 }
308
309 pub fn new(lhs: AssignedMsm<S>, rhs: AssignedMsm<S>) -> Self {
311 Self { lhs, rhs }
312 }
313
314 pub fn scale_by_bit(
318 layouter: &mut impl Layouter<S::F>,
319 scalar_chip: &S::ScalarChip,
320 cond: &AssignedBit<S::F>,
321 acc: &mut Self,
322 ) -> Result<(), Error> {
323 let cond_as_bounded = AssignedBoundedScalar {
324 scalar: cond.clone().into(),
325 bound: BigUint::one(),
326 };
327 acc.lhs.scale(layouter, scalar_chip, &cond_as_bounded)?;
328 acc.rhs.scale(layouter, scalar_chip, &cond_as_bounded)
329 }
330
331 pub fn collapse(
345 &mut self,
346 layouter: &mut impl Layouter<S::F>,
347 curve_chip: &S::CurveChip,
348 scalar_chip: &S::ScalarChip,
349 ) -> Result<(), Error> {
350 self.lhs.collapse(layouter, curve_chip, scalar_chip)?;
351 self.rhs.collapse(layouter, curve_chip, scalar_chip)
352 }
353
354 pub fn resolve_fixed_bases(&mut self, fixed_bases: &BTreeMap<String, S::AssignedPoint>) {
366 self.lhs.resolve_fixed_bases(fixed_bases);
367 self.rhs.resolve_fixed_bases(fixed_bases);
368 }
369
370 pub fn accumulate(
373 layouter: &mut impl Layouter<S::F>,
374 acc_pi_chip: &impl PublicInputInstructions<S::F, AssignedAccumulator<S>>,
375 scalar_chip: &S::ScalarChip,
376 sponge_chip: &S::SpongeChip,
377 accs: &[Self],
378 ) -> Result<Self, Error> {
379 let hash_input = accs
380 .iter()
381 .map(|acc| acc_pi_chip.as_public_input(layouter, acc))
382 .collect::<Result<Vec<_>, Error>>()?
383 .into_iter()
384 .flatten()
385 .collect::<Vec<_>>();
386
387 let r = sponge_chip.hash(layouter, &hash_input)?;
388 #[cfg(feature = "truncated-challenges")]
389 let rs = truncated_powers::<S::F>(layouter, scalar_chip, &r, accs.len())?;
390 #[cfg(not(feature = "truncated-challenges"))]
391 let rs = powers::<S::F>(layouter, scalar_chip, &r, accs.len())?
392 .iter()
393 .map(|ri| AssignedBoundedScalar::new(ri, None))
394 .collect::<Vec<_>>();
395
396 let mut acc = accs[0].clone();
397 for (other, ri) in accs.iter().zip(rs).skip(1) {
398 acc.lhs = acc.lhs.accumulate_with_r(layouter, scalar_chip, &other.lhs, &ri)?;
399 acc.rhs = acc.rhs.accumulate_with_r(layouter, scalar_chip, &other.rhs, &ri)?;
400 }
401
402 Ok(acc)
403 }
404}