1use std::collections::{btree_map::Entry, BTreeMap};
20
21use ff::Field;
22use midnight_curves::msm::msm_best;
23use midnight_proofs::{
24 circuit::{Layouter, Value},
25 plonk::Error,
26};
27
28use crate::{
29 field::AssignedNative,
30 instructions::PublicInputInstructions,
31 types::{InnerValue, Instantiable},
32 verifier::{
33 types::SelfEmulation,
34 utils::{
35 add_bounded_scalars, assign_bounded_scalars, mul_bounded_scalars, AssignedBoundedScalar,
36 },
37 },
38};
39
40#[derive(Clone, Debug)]
50pub struct Msm<S: SelfEmulation> {
51 bases: Vec<S::C>,
52 scalars: Vec<S::F>,
53 fixed_base_scalars: BTreeMap<String, S::F>,
54}
55
56#[derive(Clone, Debug)]
60pub struct AssignedMsm<S: SelfEmulation> {
61 bases: Vec<S::AssignedPoint>,
62 pub(crate) scalars: Vec<AssignedBoundedScalar<S::F>>,
63 fixed_base_scalars: BTreeMap<String, AssignedBoundedScalar<S::F>>,
64}
65
66impl<S: SelfEmulation> PartialEq for AssignedMsm<S> {
67 fn eq(&self, other: &Self) -> bool {
68 self.bases == other.bases
69 && self.scalars == other.scalars
70 && self.fixed_base_scalars == other.fixed_base_scalars
71 }
72}
73
74impl<S: SelfEmulation> Eq for AssignedMsm<S> {}
75
76impl<S: SelfEmulation> Msm<S> {
77 pub fn new(
84 bases: &[S::C],
85 scalars: &[S::F],
86 fixed_base_scalars: &BTreeMap<String, S::F>,
87 ) -> Self {
88 assert_eq!(bases.len(), scalars.len());
89 Msm {
90 bases: bases.to_vec(),
91 scalars: scalars.to_vec(),
92 fixed_base_scalars: fixed_base_scalars.clone(),
93 }
94 }
95
96 pub fn bases(&self) -> Vec<S::C> {
98 self.bases.clone()
99 }
100
101 pub fn scalars(&self) -> Vec<S::F> {
103 self.scalars.clone()
104 }
105
106 pub fn fixed_base_scalars(&self) -> BTreeMap<String, S::F> {
108 self.fixed_base_scalars.clone()
109 }
110
111 pub fn from_terms(bases: &[S::C], scalars: &[S::F]) -> Self {
118 assert_eq!(bases.len(), scalars.len());
119 Msm {
120 bases: bases.to_vec(),
121 scalars: scalars.to_vec(),
122 fixed_base_scalars: BTreeMap::new(),
123 }
124 }
125
126 pub fn collapse(&mut self) {
132 let affine_bases: Vec<S::G1Affine> = self.bases.iter().map(|&b| b.into()).collect();
133 let collapsed_base = msm_best(&self.scalars, &affine_bases);
134
135 self.bases = vec![collapsed_base];
136 self.scalars = vec![S::F::ONE];
137 }
138
139 pub fn resolve_fixed_bases(&mut self, fixed_bases: &BTreeMap<String, S::C>) {
150 for (name, scalar) in &self.fixed_base_scalars {
151 let base = fixed_bases.get(name).unwrap_or_else(|| panic!("Base not provided: {name}"));
152 self.bases.push(*base);
153 self.scalars.push(*scalar);
154 }
155 self.fixed_base_scalars.clear();
156 }
157
158 pub fn eval(&self, fixed_bases: &BTreeMap<String, S::C>) -> S::C {
169 let mut bases = self.bases.clone();
170 let mut scalars = self.scalars.clone();
171
172 for (key, scalar) in self.fixed_base_scalars.iter() {
173 let base = fixed_bases.get(key).unwrap_or_else(|| panic!("Base not provided: {key}"));
174 bases.push(*base);
175 scalars.push(*scalar);
176 }
177
178 let affine_bases: Vec<S::G1Affine> = bases.iter().map(|&b| b.into()).collect();
179 msm_best(&scalars, &affine_bases)
180 }
181
182 pub fn accumulate_with_r(&self, other: &Self, r: S::F) -> Self {
186 let mut acc = self.clone();
187
188 acc.bases.extend(other.bases.clone());
189 acc.scalars.extend(other.scalars.iter().map(|s| *s * r));
190
191 for (key, value) in other.fixed_base_scalars.clone() {
192 let r_times_value = r * value;
193 acc.fixed_base_scalars
194 .entry(key)
195 .and_modify(|e| *e += r_times_value)
196 .or_insert(r_times_value);
197 }
198
199 acc
200 }
201}
202
203impl<S: SelfEmulation> InnerValue for AssignedMsm<S> {
204 type Element = Msm<S>;
205
206 fn value(&self) -> Value<Self::Element> {
207 let bases: Value<Vec<S::C>> = Value::from_iter(self.bases.iter().map(|base| base.value()));
208
209 let scalars: Value<Vec<S::F>> =
210 Value::from_iter(self.scalars.iter().map(|s| s.scalar.value().copied()));
211
212 let fixed_based_scalars: Value<BTreeMap<String, S::F>> = Value::from_iter(
213 self.fixed_base_scalars
214 .iter()
215 .map(|(name, s)| s.scalar.value().map(|s| (name.clone(), *s))),
216 );
217
218 scalars
219 .zip(bases)
220 .zip(fixed_based_scalars)
221 .map(|((scalars, bases), fixed_base_scalars)| Msm {
222 bases,
223 scalars,
224 fixed_base_scalars,
225 })
226 }
227}
228
229impl<S: SelfEmulation> Instantiable<S::F> for AssignedMsm<S> {
230 fn as_public_input(msm: &Msm<S>) -> Vec<S::F> {
231 [
232 msm.bases.iter().flat_map(S::AssignedPoint::as_public_input).collect::<Vec<_>>(),
233 msm.scalars.clone(),
234 msm.fixed_base_scalars.values().copied().collect::<Vec<_>>(),
235 ]
236 .into_iter()
237 .flatten()
238 .collect::<Vec<_>>()
239 }
240
241 fn from_public_input(_fields: &[S::F]) -> Option<Msm<S>> {
242 unimplemented!("not invertible: the flat encoding loses structural metadata.")
243 }
244}
245
246impl<S: SelfEmulation> AssignedMsm<S> {
247 pub fn as_public_input_with_committed_scalars(msm: &Msm<S>) -> (Vec<S::F>, Vec<S::F>) {
254 let normal_instance =
255 msm.bases.iter().flat_map(S::AssignedPoint::as_public_input).collect();
256
257 let committed_instance = [
258 msm.scalars.clone(),
259 msm.fixed_base_scalars.values().copied().collect(),
260 ]
261 .concat();
262
263 (normal_instance, committed_instance)
264 }
265}
266
267impl<S: SelfEmulation> AssignedMsm<S> {
268 pub(crate) fn in_circuit_as_public_input(
269 &self,
270 layouter: &mut impl Layouter<S::F>,
271 curve_chip: &S::CurveChip,
272 ) -> Result<Vec<AssignedNative<S::F>>, Error> {
273 Ok([
274 self.bases
275 .iter()
276 .map(|base| curve_chip.as_public_input(layouter, base))
277 .collect::<Result<Vec<_>, Error>>()?
278 .into_iter()
279 .flatten()
280 .collect::<Vec<_>>(),
281 self.scalars.iter().map(|s| s.clone().scalar).collect::<Vec<_>>(),
282 self.fixed_base_scalars.values().map(|s| s.clone().scalar).collect::<Vec<_>>(),
283 ]
284 .into_iter()
285 .flatten()
286 .collect())
287 }
288
289 pub(crate) fn constrain_as_public_input(
290 &self,
291 layouter: &mut impl Layouter<S::F>,
292 curve_chip: &S::CurveChip,
293 scalar_chip: &S::ScalarChip,
294 ) -> Result<(), Error> {
295 self.bases
296 .iter()
297 .try_for_each(|base| curve_chip.constrain_as_public_input(layouter, base))?;
298
299 self.scalars
300 .iter()
301 .try_for_each(|s| scalar_chip.constrain_as_public_input(layouter, &s.clone().scalar))?;
302
303 self.fixed_base_scalars
304 .values()
305 .try_for_each(|s| scalar_chip.constrain_as_public_input(layouter, &s.clone().scalar))
306 }
307
308 pub(crate) fn constrain_as_public_input_with_committed_scalars(
309 &self,
310 layouter: &mut impl Layouter<S::F>,
311 curve_chip: &S::CurveChip,
312 scalar_chip: &S::ScalarChip,
313 ) -> Result<(), Error> {
314 self.bases
315 .iter()
316 .try_for_each(|base| curve_chip.constrain_as_public_input(layouter, base))?;
317
318 self.scalars.iter().try_for_each(|s| {
319 let mut a = S::F::ZERO;
320 s.scalar.clone().value().map(|v| a = *v);
321 S::constrain_scalar_as_committed_public_input(layouter, scalar_chip, &s.scalar)
322 })?;
323
324 self.fixed_base_scalars.values().try_for_each(|s| {
325 S::constrain_scalar_as_committed_public_input(layouter, scalar_chip, &s.scalar)
326 })
327 }
328}
329
330impl<S: SelfEmulation> AssignedMsm<S> {
331 pub fn assign(
345 layouter: &mut impl Layouter<S::F>,
346 curve_chip: &S::CurveChip,
347 scalar_chip: &S::ScalarChip,
348 len: usize,
349 fixed_base_names: &[String],
350 msm_value: Value<Msm<S>>,
351 ) -> Result<Self, Error> {
352 let bases_val = msm_value.as_ref().map(|msm| msm.bases.clone()).transpose_vec(len);
353
354 let scalars_val = msm_value.as_ref().map(|msm| msm.scalars.clone()).transpose_vec(len);
355
356 let fixed_base_scalars_val = msm_value
357 .as_ref()
358 .map(|msm| {
359 msm.fixed_base_scalars.iter().map(|s| *s.1).collect::<Vec<_>>()
362 })
363 .transpose_vec(fixed_base_names.len());
364
365 let mut fixed_base_names = fixed_base_names.to_vec();
367 fixed_base_names.sort();
368
369 let bases = bases_val
370 .iter()
371 .map(|p| S::assign_without_subgroup_check(layouter, curve_chip, *p))
372 .collect::<Result<Vec<_>, Error>>()?;
373 let scalars = assign_bounded_scalars(layouter, scalar_chip, &scalars_val)?;
374 let fixed_base_scalars: BTreeMap<String, AssignedBoundedScalar<S::F>> = {
375 let scalars = assign_bounded_scalars(layouter, scalar_chip, &fixed_base_scalars_val)?;
376 fixed_base_names.iter().cloned().zip(scalars).collect()
377 };
378
379 Ok(AssignedMsm {
380 scalars,
381 bases,
382 fixed_base_scalars,
383 })
384 }
385
386 pub fn empty() -> Self {
389 Self {
390 scalars: vec![],
391 bases: vec![],
392 fixed_base_scalars: BTreeMap::new(),
393 }
394 }
395
396 pub fn from_term(scalar: &AssignedBoundedScalar<S::F>, base: &S::AssignedPoint) -> Self {
398 Self {
399 scalars: vec![scalar.clone()],
400 bases: vec![base.clone()],
401 fixed_base_scalars: BTreeMap::new(),
402 }
403 }
404
405 pub fn from_fixed_term(scalar: &AssignedBoundedScalar<S::F>, base_name: &str) -> Self {
407 Self {
408 scalars: vec![],
409 bases: vec![],
410 fixed_base_scalars: [(base_name.to_string(), scalar.clone())].into_iter().collect(),
411 }
412 }
413
414 pub fn add_term(&mut self, scalar: &AssignedBoundedScalar<S::F>, base: &S::AssignedPoint) {
416 self.scalars.push(scalar.clone());
417 self.bases.push(base.clone());
418 }
419
420 pub fn add_msm(
422 &mut self,
423 layouter: &mut impl Layouter<S::F>,
424 scalar_chip: &S::ScalarChip,
425 other: &Self,
426 ) -> Result<(), Error> {
427 self.scalars.extend(other.scalars.clone());
428 self.bases.extend(other.bases.clone());
429
430 for (key, value) in other.fixed_base_scalars.clone() {
431 match self.fixed_base_scalars.entry(key) {
432 Entry::Occupied(mut occ) => {
433 *occ.get_mut() = add_bounded_scalars(layouter, scalar_chip, occ.get(), &value)?;
434 }
435 Entry::Vacant(vac) => {
436 vac.insert(value);
437 }
438 }
439 }
440
441 Ok(())
442 }
443
444 pub fn collapse(
450 &mut self,
451 layouter: &mut impl Layouter<S::F>,
452 curve_chip: &S::CurveChip,
453 scalar_chip: &S::ScalarChip,
454 ) -> Result<(), Error> {
455 let scalars = self
456 .scalars
457 .iter()
458 .map(|s| (s.scalar.clone(), s.bound.bits() as usize))
459 .collect::<Vec<_>>();
460
461 let collapsed_base = S::msm(layouter, curve_chip, &scalars, &self.bases)?;
462
463 self.bases = vec![collapsed_base];
464 self.scalars = vec![AssignedBoundedScalar::one(layouter, scalar_chip)?];
465
466 Ok(())
467 }
468
469 pub fn resolve_fixed_bases(&mut self, fixed_bases: &BTreeMap<String, S::AssignedPoint>) {
480 for (name, scalar) in &self.fixed_base_scalars {
481 let base = fixed_bases
482 .get(name)
483 .unwrap_or_else(|| panic!("Fixed base not provided: {name}"));
484 self.bases.push(base.clone());
485 self.scalars.push(scalar.clone());
486 }
487 self.fixed_base_scalars.clear();
488 }
489
490 pub fn scale(
494 &mut self,
495 layouter: &mut impl Layouter<S::F>,
496 scalar_chip: &S::ScalarChip,
497 r: &AssignedBoundedScalar<S::F>,
498 ) -> Result<(), Error> {
499 self.scalars = (self.scalars.iter())
500 .map(|s| mul_bounded_scalars(layouter, scalar_chip, s, r))
501 .collect::<Result<Vec<_>, Error>>()?;
502
503 for s in self.fixed_base_scalars.values_mut() {
504 *s = mul_bounded_scalars(layouter, scalar_chip, s, r)?;
505 }
506
507 Ok(())
508 }
509
510 pub fn accumulate_with_r(
514 &self,
515 layouter: &mut impl Layouter<S::F>,
516 scalar_chip: &S::ScalarChip,
517 other: &Self,
518 r: &AssignedBoundedScalar<S::F>,
519 ) -> Result<Self, Error> {
520 let mut other = other.clone();
521 other.scale(layouter, scalar_chip, r)?;
522
523 let mut acc = self.clone();
524 acc.add_msm(layouter, scalar_chip, &other)?;
525
526 Ok(acc)
527 }
528}