1use std::{collections::HashMap, rc::Rc, str::FromStr, sync::Arc};
2
3use anyhow::Context;
4use ark_ec::{short_weierstrass_jacobian::GroupProjective, AffineCurve, ProjectiveCurve};
5use ark_ff::{fields::arithmetic::InvalidBigInt, BigInteger256, Field, PrimeField};
6use kimchi::{
7 circuits::{gate::CircuitGate, wires::COLUMNS},
8 proof::RecursionChallenge,
9};
10use mina_curves::pasta::{Fp, Fq};
11use mina_p2p_messages::v2::{
12 self, ConsensusProofOfStakeDataEpochDataNextValueVersionedValueStableV1,
13 ConsensusProofOfStakeDataEpochDataStakingValueVersionedValueStableV1, CurrencyAmountStableV1,
14 MinaBaseEpochLedgerValueStableV1, MinaBaseFeeExcessStableV1,
15 MinaBaseProtocolConstantsCheckedValueStableV1, MinaNumbersGlobalSlotSinceGenesisMStableV1,
16 MinaNumbersGlobalSlotSinceHardForkMStableV1,
17 MinaStateBlockchainStateValueStableV2LedgerProofStatement,
18 MinaStateBlockchainStateValueStableV2LedgerProofStatementSource,
19 MinaStateBlockchainStateValueStableV2SignedAmount,
20 MinaTransactionLogicZkappCommandLogicLocalStateValueStableV1, SgnStableV1, SignedAmount,
21 TokenFeeExcess, UnsignedExtendedUInt32StableV1,
22 UnsignedExtendedUInt64Int64ForVersionTagsStableV1,
23};
24use mina_poseidon::constants::PlonkSpongeConstantsKimchi;
25use mina_signer::{CompressedPubKey, PubKey};
26
27use crate::{
28 decompress_pk, gen_keypair,
29 proofs::{
30 constants::{StepTransactionProof, WrapTransactionProof},
31 unfinalized::AllEvals,
32 util::sha256_sum,
33 wrap::{self, WrapParams},
34 },
35 scan_state::{
36 currency::{self, Sgn},
37 fee_excess::FeeExcess,
38 pending_coinbase,
39 scan_state::transaction_snark::{Registers, SokDigest, SokMessage, Statement},
40 transaction_logic::{local_state::LocalState, transaction_union_payload},
41 },
42 verifier::get_srs_mut,
43 Account, AppendToInputs, MyCow, ReceiptChainHash, TimingAsRecord, TokenId, TokenSymbol,
44};
45
46use super::{
47 constants::ProofConstants,
48 field::{field, Boolean, CircuitVar, FieldWitness, GroupAffine, ToBoolean},
49 public_input::messages::{dummy_ipa_step_sg, MessagesForNextWrapProof},
50 step,
51 step::{InductiveRule, OptFlag, StepProof},
52 to_field_elements::{ToFieldElements, ToFieldElementsDebug},
53 unfinalized::Unfinalized,
54 witness::Witness,
55 wrap::WrapProof,
56 ProverIndex,
57};
58
59pub trait Check<F: FieldWitness> {
60 fn check(&self, w: &mut Witness<F>);
61}
62
63struct FieldBitsIterator {
64 index: usize,
65 bigint: [u64; 4],
66}
67
68impl Iterator for FieldBitsIterator {
69 type Item = bool;
70
71 fn next(&mut self) -> Option<Self::Item> {
72 let index = self.index;
73 self.index += 1;
74
75 let limb_index = index / 64;
76 let bit_index = index % 64;
77
78 let limb = self.bigint.get(limb_index)?;
79 Some(limb & (1 << bit_index) != 0)
80 }
81}
82
83pub fn bigint_to_bits<const NBITS: usize>(bigint: BigInteger256) -> [bool; NBITS] {
84 let mut bits = FieldBitsIterator {
85 index: 0,
86 bigint: bigint.to_64x4(),
87 }
88 .take(NBITS);
89 std::array::from_fn(|_| bits.next().unwrap())
90}
91
92pub fn field_to_bits<F, const NBITS: usize>(field: F) -> [bool; NBITS]
93where
94 F: Field + Into<BigInteger256>,
95{
96 let bigint: BigInteger256 = field.into();
97 bigint_to_bits(bigint)
98}
99
100fn bigint_to_bits2(bigint: BigInteger256, nbits: usize) -> Box<[bool]> {
102 FieldBitsIterator {
103 index: 0,
104 bigint: bigint.to_64x4(),
105 }
106 .take(nbits)
107 .collect()
108}
109
110pub fn field_to_bits2<F>(field: F, nbits: usize) -> Box<[bool]>
112where
113 F: Field + Into<BigInteger256>,
114{
115 let bigint: BigInteger256 = field.into();
116 bigint_to_bits2(bigint, nbits)
117}
118
119fn bits_msb<F, const NBITS: usize>(field: F) -> [bool; NBITS]
120where
121 F: Field + Into<BigInteger256>,
122{
123 let mut bits = field_to_bits::<F, NBITS>(field);
124 bits.reverse();
125 bits
126}
127
128pub fn endos<F>() -> (F, F::Scalar)
129where
130 F: FieldWitness,
131{
132 use poly_commitment::srs::endos;
133
134 cache!((F, F::Scalar), endos::<GroupAffine<F>>())
136}
137
138pub fn make_group<F>(x: F, y: F) -> GroupAffine<F>
139where
140 F: FieldWitness,
141{
142 GroupAffine::<F>::new(x, y, false)
143}
144
145pub mod scalar_challenge {
146 use super::*;
147
148 pub fn to_field_checked_prime<F, const NBITS: usize>(scalar: F, w: &mut Witness<F>) -> (F, F, F)
151 where
152 F: FieldWitness,
153 {
154 let zero = F::zero();
155 let one = F::one();
156 let neg_one = one.neg();
157
158 let a_array = [zero, zero, neg_one, one];
159 let a_func = |n: u64| a_array[n as usize];
160
161 let b_array = [neg_one, one, zero, zero];
162 let b_func = |n: u64| b_array[n as usize];
163
164 let bits_msb: [bool; NBITS] = bits_msb::<_, NBITS>(scalar);
165
166 let nybbles_per_row = 8;
167 let bits_per_row = 2 * nybbles_per_row;
168 assert_eq!((NBITS % bits_per_row), 0);
169 let rows = NBITS / bits_per_row;
170
171 let nybbles_by_row: Vec<Vec<u64>> = (0..rows)
174 .map(|i| {
175 (0..nybbles_per_row)
176 .map(|j| {
177 let bit = (bits_per_row * i) + (2 * j);
178 let b0 = bits_msb[bit + 1] as u64;
179 let b1 = bits_msb[bit] as u64;
180 b0 + (2 * b1)
181 })
182 .collect()
183 })
184 .collect();
185
186 let two: F = 2u64.into();
187 let mut a = two;
188 let mut b = two;
189 let mut n = F::zero();
190
191 for nybbles_by_row in nybbles_by_row.iter().take(rows) {
192 let n0 = n;
193 let a0 = a;
194 let b0 = b;
195
196 let xs: Vec<F> = (0..nybbles_per_row)
197 .map(|j| w.exists(F::from(nybbles_by_row[j])))
198 .collect();
199
200 let n8: F = w.exists(xs.iter().fold(n0, |accum, x| accum.double().double() + x));
201
202 let a8: F = w.exists(
203 nybbles_by_row
204 .iter()
205 .fold(a0, |accum, x| accum.double() + a_func(*x)),
206 );
207
208 let b8: F = w.exists(
209 nybbles_by_row
210 .iter()
211 .fold(b0, |accum, x| accum.double() + b_func(*x)),
212 );
213
214 n = n8;
215 a = a8;
216 b = b8;
217 }
218
219 (a, b, n)
220 }
221
222 pub fn to_field_checked<F, const NBITS: usize>(scalar: F, endo: F, w: &mut Witness<F>) -> F
224 where
225 F: FieldWitness,
226 {
227 let (a, b, _n) = to_field_checked_prime::<F, NBITS>(scalar, w);
228 (a * endo) + b
229 }
230
231 pub fn endo<F, F2, const NBITS: usize>(
233 t: GroupAffine<F>,
234 scalar: F2,
235 w: &mut Witness<F>,
236 ) -> GroupAffine<F>
237 where
238 F: FieldWitness,
239 F2: FieldWitness,
240 {
241 endo_cvar::<F, F2, NBITS>(CircuitVar::Var(t), scalar, w)
242 }
243
244 pub fn endo_cvar<F, F2, const NBITS: usize>(
246 t: CircuitVar<GroupAffine<F>>,
247 scalar: F2,
248 w: &mut Witness<F>,
249 ) -> GroupAffine<F>
250 where
251 F: FieldWitness,
252 F2: FieldWitness,
253 {
254 let bits: [bool; NBITS] = bits_msb::<F2, NBITS>(scalar);
255
256 let bits_per_row = 4;
257 let rows = NBITS / bits_per_row;
258
259 let GroupAffine::<F> { x: xt, y: yt, .. } = *t.value();
260 let (endo_base, _) = endos::<F>();
261
262 let mut acc = {
263 let tmp = match t {
266 CircuitVar::Var(_) => w.exists(xt * endo_base),
267 CircuitVar::Constant(_) => xt * endo_base,
268 };
269 let p = w.add_fast(*t.value(), make_group::<F>(tmp, yt));
270 w.add_fast(p, p)
271 };
272
273 let mut n_acc = F::zero();
274 for i in 0..rows {
275 let n_acc_prev = n_acc;
276 let b1 = w.exists(F::from(bits[i * bits_per_row]));
277 let b2 = w.exists(F::from(bits[(i * bits_per_row) + 1]));
278 let b3 = w.exists(F::from(bits[(i * bits_per_row) + 2]));
279 let b4 = w.exists(F::from(bits[(i * bits_per_row) + 3]));
280
281 let GroupAffine::<F> { x: xp, y: yp, .. } = acc;
282 let xq1 = w.exists((F::one() + ((endo_base - F::one()) * b1)) * xt);
283 let yq1 = w.exists((b2.double() - F::one()) * yt);
284 let s1 = w.exists((yq1 - yp) / (xq1 - xp));
285 let s1_squared = w.exists(s1.square());
286 let s2 = w.exists((yp.double() / (xp.double() + xq1 - s1_squared)) - s1);
287 let xr = w.exists(xq1 + s2.square() - s1_squared);
288 let yr = w.exists(((xp - xr) * s2) - yp);
289 let xq2 = w.exists((F::one() + ((endo_base - F::one()) * b3)) * xt);
290 let yq2 = w.exists((b4.double() - F::one()) * yt);
291 let s3 = w.exists((yq2 - yr) / (xq2 - xr));
292 let s3_squared = w.exists(s3.square());
293 let s4 = w.exists((yr.double() / (xr.double() + xq2 - s3_squared)) - s3);
294 let xs = w.exists(xq2 + s4.square() - s3_squared);
295 let ys = w.exists(((xr - xs) * s4) - yr);
296
297 acc = make_group::<F>(xs, ys);
298 n_acc =
299 w.exists((((n_acc_prev.double() + b1).double() + b2).double() + b3).double() + b4);
300 }
301
302 acc
303 }
304
305 pub fn endo_inv<F, F2, const NBITS: usize>(
307 t: GroupAffine<F>,
308 chal: F2,
309 w: &mut Witness<F>,
310 ) -> GroupAffine<F>
311 where
312 F: FieldWitness,
313 F2: FieldWitness,
314 {
315 use crate::proofs::public_input::scalar_challenge::ScalarChallenge;
316 use ark_ff::One;
317
318 let (_, e) = endos::<F>();
319
320 let res = w.exists({
321 let chal = ScalarChallenge::from(chal).to_field(&e);
322 InnerCurve::<F>::of_affine(t).scale(<F::Scalar>::one() / chal)
323 });
324 let _ = endo::<F, F2, NBITS>(res.to_affine(), chal, w);
325 res.to_affine()
326 }
327}
328
329pub fn add_fast<F>(
330 p1: GroupAffine<F>,
331 p2: GroupAffine<F>,
332 check_finite: Option<bool>,
333 w: &mut Witness<F>,
334) -> GroupAffine<F>
335where
336 F: FieldWitness,
337{
338 let GroupAffine::<F> { x: x1, y: y1, .. } = p1;
339 let GroupAffine::<F> { x: x2, y: y2, .. } = p2;
340 let check_finite = check_finite.unwrap_or(true);
341
342 let bool_to_field = |b: bool| if b { F::one() } else { F::zero() };
343
344 let same_x_bool = x1 == x2;
345 let _same_x = w.exists(bool_to_field(same_x_bool));
346
347 let _inf = if check_finite {
348 F::zero()
349 } else {
350 w.exists(bool_to_field(same_x_bool && y1 != y2))
351 };
352
353 let _inf_z = w.exists({
354 if y1 == y2 {
355 F::zero()
356 } else if same_x_bool {
357 (y2 - y1).inverse().unwrap()
358 } else {
359 F::zero()
360 }
361 });
362
363 let _x21_inv = w.exists({
364 if same_x_bool {
365 F::zero()
366 } else {
367 (x2 - x1).inverse().unwrap()
368 }
369 });
370
371 let s = w.exists({
372 if same_x_bool {
373 let x1_squared = x1.square();
374 (x1_squared + x1_squared + x1_squared) / (y1 + y1)
375 } else {
376 (y2 - y1) / (x2 - x1)
377 }
378 });
379
380 let x3 = w.exists(s.square() - (x1 + x2));
381 let y3 = w.exists(s * (x1 - x3) - y1);
382
383 make_group::<F>(x3, y3)
384}
385
386fn fold_map<T, Acc, U>(
387 iter: impl Iterator<Item = T>,
388 init: Acc,
389 mut fun: impl FnMut(Acc, T) -> (Acc, U),
390) -> (Acc, Vec<U>) {
391 let mut acc = Some(init);
392 let result = iter
393 .map(|x| {
394 let (new_acc, y) = fun(acc.take().unwrap(), x);
395 acc = Some(new_acc);
396 y
397 })
398 .collect::<Vec<_>>();
399 (acc.unwrap(), result)
400}
401
402pub mod plonk_curve_ops {
403 use crate::proofs::public_input::plonk_checks::ShiftingValue;
404
405 use super::*;
406
407 const BITS_PER_CHUNK: usize = 5;
408
409 pub fn scale_fast<F, F2, const NBITS: usize>(
411 base: GroupAffine<F>,
412 shifted_value: F2::Shifting,
413 w: &mut Witness<F>,
414 ) -> GroupAffine<F>
415 where
416 F: FieldWitness,
417 F2: FieldWitness,
418 {
419 let (r, _bits) = scale_fast_unpack::<F, F2, NBITS>(base, shifted_value, w);
420 r
421 }
422
423 pub fn scale_fast_unpack<F, F2, const NBITS: usize>(
426 base: GroupAffine<F>,
427 shifted: F2::Shifting,
428 w: &mut Witness<F>,
429 ) -> (GroupAffine<F>, [bool; NBITS])
430 where
431 F: FieldWitness,
432 F2: FieldWitness,
433 {
434 let scalar = shifted.shifted_raw();
435 let GroupAffine::<F> {
436 x: x_base,
437 y: y_base,
438 ..
439 } = base;
440
441 let chunks: usize = NBITS / BITS_PER_CHUNK;
442 assert_eq!(NBITS % BITS_PER_CHUNK, 0);
443
444 let bits_msb: [bool; NBITS] = w.exists(bits_msb::<F2, NBITS>(scalar));
445 let mut acc = w.add_fast(base, base);
446 let mut n_acc = F::zero();
447
448 for chunk in 0..chunks {
449 let bs: [bool; BITS_PER_CHUNK] =
450 std::array::from_fn(|i| bits_msb[(chunk * BITS_PER_CHUNK) + i]);
451
452 let n_acc_prev = n_acc;
453
454 n_acc = w.exists(
455 bs.iter()
456 .fold(n_acc_prev, |acc, b| acc.double() + F::from(*b)),
457 );
458
459 let (_, v) = fold_map(bs.iter(), acc, |acc, b| {
460 let GroupAffine::<F> {
461 x: x_acc, y: y_acc, ..
462 } = acc;
463 let b: F = F::from(*b);
464
465 let s1: F =
466 w.exists((y_acc - (y_base * (b.double() - F::one()))) / (x_acc - x_base));
467 let s1_squared = w.exists(s1.square());
468 let s2 = w.exists((y_acc.double() / (x_acc.double() + x_base - s1_squared)) - s1);
469
470 let x_res = w.exists(x_base + s2.square() - s1_squared);
471 let y_res = w.exists(((x_acc - x_res) * s2) - y_acc);
472 let acc = make_group(x_res, y_res);
473
474 (acc, (acc, s1))
475 });
476
477 let (mut accs, _slopes): (Vec<_>, Vec<_>) = v.into_iter().unzip();
478
479 accs.insert(0, acc);
480 acc = accs.last().cloned().unwrap();
481 }
482
483 let bits_lsb = {
484 let mut bits_msb = bits_msb;
485 bits_msb.reverse();
486 bits_msb
487 };
488
489 (acc, bits_lsb)
490 }
491}
492
493#[derive(Clone, Debug, PartialEq, Eq)]
494pub struct PlonkVerificationKeyEvals<F: FieldWitness> {
495 pub sigma: [InnerCurve<F>; 7],
496 pub coefficients: [InnerCurve<F>; 15],
497 pub generic: InnerCurve<F>,
498 pub psm: InnerCurve<F>,
499 pub complete_add: InnerCurve<F>,
500 pub mul: InnerCurve<F>,
501 pub emul: InnerCurve<F>,
502 pub endomul_scalar: InnerCurve<F>,
503}
504
505impl<'de> serde::Deserialize<'de> for PlonkVerificationKeyEvals<Fp> {
506 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
507 where
508 D: serde::Deserializer<'de>,
509 {
510 match v2::MinaBaseVerificationKeyWireStableV1WrapIndex::deserialize(deserializer) {
511 Ok(value) => value.try_into().map_err(serde::de::Error::custom),
512 Err(e) => Err(e),
513 }
514 }
515}
516
517impl serde::Serialize for PlonkVerificationKeyEvals<Fp> {
518 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
519 where
520 S: serde::Serializer,
521 {
522 let v: v2::MinaBaseVerificationKeyWireStableV1WrapIndex = self.into();
523 v.serialize(serializer)
524 }
525}
526
527#[derive(Clone, Debug)]
529pub struct CircuitPlonkVerificationKeyEvals<F: FieldWitness> {
530 pub sigma: [CircuitVar<GroupAffine<F>>; 7],
531 pub coefficients: [CircuitVar<GroupAffine<F>>; 15],
532 pub generic: CircuitVar<GroupAffine<F>>,
533 pub psm: CircuitVar<GroupAffine<F>>,
534 pub complete_add: CircuitVar<GroupAffine<F>>,
535 pub mul: CircuitVar<GroupAffine<F>>,
536 pub emul: CircuitVar<GroupAffine<F>>,
537 pub endomul_scalar: CircuitVar<GroupAffine<F>>,
538}
539
540impl CircuitPlonkVerificationKeyEvals<Fp> {
541 pub fn to_non_cvar(&self) -> PlonkVerificationKeyEvals<Fp> {
542 let Self {
543 sigma,
544 coefficients,
545 generic,
546 psm,
547 complete_add,
548 mul,
549 emul,
550 endomul_scalar,
551 } = self;
552
553 let c = |c: &CircuitVar<GroupAffine<Fp>>| InnerCurve::<Fp>::of_affine(*c.value());
554
555 PlonkVerificationKeyEvals::<Fp> {
556 sigma: sigma.each_ref().map(c),
557 coefficients: coefficients.each_ref().map(c),
558 generic: c(generic),
559 psm: c(psm),
560 complete_add: c(complete_add),
561 mul: c(mul),
562 emul: c(emul),
563 endomul_scalar: c(endomul_scalar),
564 }
565 }
566}
567
568impl PlonkVerificationKeyEvals<Fp> {
569 pub fn to_cvar(
570 &self,
571 cvar: impl Fn(GroupAffine<Fp>) -> CircuitVar<GroupAffine<Fp>>,
572 ) -> CircuitPlonkVerificationKeyEvals<Fp> {
573 let Self {
574 sigma,
575 coefficients,
576 generic,
577 psm,
578 complete_add,
579 mul,
580 emul,
581 endomul_scalar,
582 } = self;
583
584 let cvar = |c: &InnerCurve<Fp>| cvar(c.to_affine());
585
586 CircuitPlonkVerificationKeyEvals::<Fp> {
587 sigma: sigma.each_ref().map(cvar),
588 coefficients: coefficients.each_ref().map(cvar),
589 generic: cvar(generic),
590 psm: cvar(psm),
591 complete_add: cvar(complete_add),
592 mul: cvar(mul),
593 emul: cvar(emul),
594 endomul_scalar: cvar(endomul_scalar),
595 }
596 }
597
598 #[allow(clippy::inherent_to_string)]
600 fn to_string(&self) -> String {
601 let Self {
602 sigma,
603 coefficients,
604 generic,
605 psm,
606 complete_add,
607 mul,
608 emul,
609 endomul_scalar,
610 } = self;
611
612 let mut string = String::with_capacity(1_000);
613
614 use crate::util::FpExt;
615
616 let mut inner_to_s = |c: &InnerCurve<Fp>| {
617 let GroupAffine::<Fp> { x, y, .. } = c.to_affine();
618 string.push_str(&format!("{}\n", x.to_decimal()));
619 string.push_str(&format!("{}\n", y.to_decimal()));
620 };
621
622 sigma.iter().for_each(&mut inner_to_s);
623 coefficients.iter().for_each(&mut inner_to_s);
624 inner_to_s(generic);
625 inner_to_s(psm);
626 inner_to_s(complete_add);
627 inner_to_s(mul);
628 inner_to_s(emul);
629 inner_to_s(endomul_scalar);
630
631 string.trim().to_string()
632 }
633
634 fn from_string(s: &str) -> Self {
636 let mut s = s.lines();
637
638 let mut to_inner = || {
639 let a = s.next().unwrap();
640 let b = s.next().unwrap();
641
642 let a = Fp::from_str(a).unwrap();
643 let b = Fp::from_str(b).unwrap();
644
645 InnerCurve::<Fp>::of_affine(make_group(a, b))
646 };
647
648 Self {
649 sigma: std::array::from_fn(|_| to_inner()),
650 coefficients: std::array::from_fn(|_| to_inner()),
651 generic: to_inner(),
652 psm: to_inner(),
653 complete_add: to_inner(),
654 mul: to_inner(),
655 emul: to_inner(),
656 endomul_scalar: to_inner(),
657 }
658 }
659
660 pub fn rand() -> Self {
661 Self {
662 sigma: [
663 InnerCurve::rand(),
664 InnerCurve::rand(),
665 InnerCurve::rand(),
666 InnerCurve::rand(),
667 InnerCurve::rand(),
668 InnerCurve::rand(),
669 InnerCurve::rand(),
670 ],
671 coefficients: [
672 InnerCurve::rand(),
673 InnerCurve::rand(),
674 InnerCurve::rand(),
675 InnerCurve::rand(),
676 InnerCurve::rand(),
677 InnerCurve::rand(),
678 InnerCurve::rand(),
679 InnerCurve::rand(),
680 InnerCurve::rand(),
681 InnerCurve::rand(),
682 InnerCurve::rand(),
683 InnerCurve::rand(),
684 InnerCurve::rand(),
685 InnerCurve::rand(),
686 InnerCurve::rand(),
687 ],
688 generic: InnerCurve::rand(),
689 psm: InnerCurve::rand(),
690 complete_add: InnerCurve::rand(),
691 mul: InnerCurve::rand(),
692 emul: InnerCurve::rand(),
693 endomul_scalar: InnerCurve::rand(),
694 }
695 }
696}
697
698impl crate::ToInputs for PlonkVerificationKeyEvals<Fp> {
699 fn to_inputs(&self, inputs: &mut ::poseidon::hash::Inputs) {
700 let Self {
701 sigma,
702 coefficients,
703 generic,
704 psm,
705 complete_add,
706 mul,
707 emul,
708 endomul_scalar,
709 } = self;
710
711 let mut to_input = |v: &InnerCurve<Fp>| {
712 let GroupAffine::<Fp> { x, y, .. } = v.to_affine();
713 inputs.append(&x);
714 inputs.append(&y);
715 };
716
717 for s in sigma.iter() {
718 to_input(s);
719 }
720 for c in coefficients.iter() {
721 to_input(c);
722 }
723 to_input(generic);
724 to_input(psm);
725 to_input(complete_add);
726 to_input(mul);
727 to_input(emul);
728 to_input(endomul_scalar);
729 }
730}
731
732impl<F: FieldWitness, T: Check<F>> Check<F> for &T {
734 fn check(&self, w: &mut Witness<F>) {
735 (*self).check(w)
736 }
737}
738
739impl<F: FieldWitness, T: Check<F> + Clone> Check<F> for std::borrow::Cow<'_, T> {
740 fn check(&self, w: &mut Witness<F>) {
741 let this: &T = self.as_ref();
742 this.check(w)
743 }
744}
745
746impl<F: FieldWitness> Check<F> for PlonkVerificationKeyEvals<F> {
747 fn check(&self, w: &mut Witness<F>) {
748 let Self {
749 sigma,
750 coefficients,
751 generic,
752 psm,
753 complete_add,
754 mul,
755 emul,
756 endomul_scalar,
757 } = self;
758
759 sigma.iter().for_each(|s| s.check(w));
760 coefficients.iter().for_each(|c| c.check(w));
761 generic.check(w);
762 psm.check(w);
763 complete_add.check(w);
764 mul.check(w);
765 emul.check(w);
766 endomul_scalar.check(w);
767 }
768}
769
770impl<F: FieldWitness> Check<F> for SgnStableV1 {
771 fn check(&self, _w: &mut Witness<F>) {
772 }
774}
775
776impl<F: FieldWitness> Check<F> for bool {
777 fn check(&self, _w: &mut Witness<F>) {
778 }
780}
781
782impl<F: FieldWitness> Check<F> for mina_signer::Signature {
783 fn check(&self, _w: &mut Witness<F>) {
784 }
786}
787
788impl<F: FieldWitness, T: Check<F>> Check<F> for MyCow<'_, T> {
789 fn check(&self, w: &mut Witness<F>) {
790 let this: &T = self;
791 this.check(w);
792 }
793}
794
795impl<F: FieldWitness> Check<F> for Fp {
796 fn check(&self, _w: &mut Witness<F>) {
797 }
799}
800
801impl<F: FieldWitness> Check<F> for Fq {
802 fn check(&self, _w: &mut Witness<F>) {
803 }
805}
806
807impl<F: FieldWitness, const N: usize> Check<F> for crate::address::raw::Address<N> {
808 fn check(&self, _w: &mut Witness<F>) {
809 }
811}
812
813impl<F: FieldWitness, T: Check<F>, const N: usize> Check<F> for [T; N] {
814 fn check(&self, w: &mut Witness<F>) {
815 self.iter().for_each(|v| v.check(w));
816 }
817}
818
819impl<F: FieldWitness> Check<F> for CurrencyAmountStableV1 {
820 fn check(&self, w: &mut Witness<F>) {
821 const NBITS: usize = u64::BITS as usize;
822
823 let amount: u64 = self.as_u64();
824 assert_eq!(NBITS, std::mem::size_of_val(&amount) * 8);
825
826 let amount: F = amount.into();
827 scalar_challenge::to_field_checked_prime::<F, NBITS>(amount, w);
828 }
829}
830
831impl<F: FieldWitness> Check<F> for SignedAmount {
832 fn check(&self, w: &mut Witness<F>) {
833 let Self { magnitude, sgn } = self;
834 magnitude.check(w);
835 sgn.check(w);
836 }
837}
838
839impl<F: FieldWitness, T: currency::Magnitude + Check<F>> Check<F> for currency::Signed<T> {
840 fn check(&self, w: &mut Witness<F>) {
841 let Self { magnitude, sgn } = self;
842 magnitude.check(w);
843 sgn.check(w);
844 }
845}
846
847impl<F: FieldWitness> Check<F> for MinaStateBlockchainStateValueStableV2SignedAmount {
848 fn check(&self, w: &mut Witness<F>) {
849 let Self { magnitude, sgn } = self;
850 magnitude.check(w);
851 sgn.check(w);
852 }
853}
854
855impl<F: FieldWitness> Check<F> for UnsignedExtendedUInt32StableV1 {
856 fn check(&self, w: &mut Witness<F>) {
857 let number: u32 = self.as_u32();
858 number.check(w);
859 }
860}
861
862impl<F: FieldWitness> Check<F> for u32 {
863 fn check(&self, w: &mut Witness<F>) {
864 const NBITS: usize = u32::BITS as usize;
865
866 let number: u32 = *self;
867 assert_eq!(NBITS, std::mem::size_of_val(&number) * 8);
868
869 let number: F = number.into();
870 scalar_challenge::to_field_checked_prime::<F, NBITS>(number, w);
871 }
872}
873
874impl<F: FieldWitness> Check<F> for MinaStateBlockchainStateValueStableV2LedgerProofStatementSource {
875 fn check(&self, w: &mut Witness<F>) {
876 let Self {
877 first_pass_ledger: _,
878 second_pass_ledger: _,
879 pending_coinbase_stack: _,
880 local_state:
881 MinaTransactionLogicZkappCommandLogicLocalStateValueStableV1 {
882 stack_frame: _,
883 call_stack: _,
884 transaction_commitment: _,
885 full_transaction_commitment: _,
886 excess,
887 supply_increase,
888 ledger: _,
889 success,
890 account_update_index,
891 failure_status_tbl: _,
892 will_succeed,
893 },
894 } = self;
895
896 excess.check(w);
897 supply_increase.check(w);
898 success.check(w);
899 account_update_index.check(w);
900 will_succeed.check(w);
901 }
902}
903
904impl<F: FieldWitness> Check<F> for Registers {
905 fn check(&self, w: &mut Witness<F>) {
906 let Self {
907 first_pass_ledger: _,
908 second_pass_ledger: _,
909 pending_coinbase_stack: _,
910 local_state:
911 LocalState {
912 stack_frame: _,
913 call_stack: _,
914 transaction_commitment: _,
915 full_transaction_commitment: _,
916 excess,
917 supply_increase,
918 ledger: _,
919 success,
920 account_update_index,
921 failure_status_tbl: _,
922 will_succeed,
923 },
924 } = self;
925
926 excess.check(w);
927 supply_increase.check(w);
928 success.check(w);
929 account_update_index.check(w);
930 will_succeed.check(w);
931 }
932}
933
934impl<F: FieldWitness> Check<F> for MinaStateBlockchainStateValueStableV2LedgerProofStatement {
935 fn check(&self, w: &mut Witness<F>) {
936 let Self {
937 source,
938 target,
939 connecting_ledger_left: _,
940 connecting_ledger_right: _,
941 supply_increase,
942 fee_excess,
943 sok_digest: _,
944 } = self;
945
946 source.check(w);
947 target.check(w);
948 supply_increase.check(w);
949 fee_excess.check(w);
950 }
951}
952
953impl<F: FieldWitness, T> Check<F> for Statement<T> {
954 fn check(&self, w: &mut Witness<F>) {
955 let Self {
956 source,
957 target,
958 connecting_ledger_left: _,
959 connecting_ledger_right: _,
960 supply_increase,
961 fee_excess,
962 sok_digest: _,
963 } = self;
964
965 source.check(w);
966 target.check(w);
967 supply_increase.check(w);
968 fee_excess.check(w);
969 }
970}
971
972impl<F: FieldWitness> Check<F> for MinaBaseFeeExcessStableV1 {
973 fn check(&self, w: &mut Witness<F>) {
974 let Self(
975 TokenFeeExcess {
976 token: _fee_token_l,
977 amount: fee_excess_l,
978 },
979 TokenFeeExcess {
980 token: _fee_token_r,
981 amount: fee_excess_r,
982 },
983 ) = self;
984
985 fee_excess_l.check(w);
986 fee_excess_r.check(w);
987 }
988}
989
990impl<F: FieldWitness> Check<F> for FeeExcess {
991 fn check(&self, w: &mut Witness<F>) {
992 let Self {
993 fee_token_l: _,
994 fee_excess_l,
995 fee_token_r: _,
996 fee_excess_r,
997 } = self;
998
999 fee_excess_l.check(w);
1000 fee_excess_r.check(w);
1001 }
1002}
1003
1004impl<F: FieldWitness> Check<F> for UnsignedExtendedUInt64Int64ForVersionTagsStableV1 {
1005 fn check(&self, w: &mut Witness<F>) {
1006 const NBITS: usize = u64::BITS as usize;
1007
1008 let number: u64 = self.as_u64();
1009 assert_eq!(NBITS, std::mem::size_of_val(&number) * 8);
1010
1011 let number: F = number.into();
1012 scalar_challenge::to_field_checked_prime::<F, NBITS>(number, w);
1013 }
1014}
1015
1016impl<F: FieldWitness> Check<F> for MinaNumbersGlobalSlotSinceGenesisMStableV1 {
1017 fn check(&self, w: &mut Witness<F>) {
1018 let Self::SinceGenesis(global_slot) = self;
1019 global_slot.check(w);
1020 }
1021}
1022
1023impl<F: FieldWitness> Check<F> for MinaNumbersGlobalSlotSinceHardForkMStableV1 {
1024 fn check(&self, w: &mut Witness<F>) {
1025 let Self::SinceHardFork(global_slot) = self;
1026 global_slot.check(w);
1027 }
1028}
1029
1030impl<F: FieldWitness> Check<F>
1031 for ConsensusProofOfStakeDataEpochDataStakingValueVersionedValueStableV1
1032{
1033 fn check(&self, w: &mut Witness<F>) {
1034 let Self {
1035 ledger:
1036 MinaBaseEpochLedgerValueStableV1 {
1037 hash: _,
1038 total_currency,
1039 },
1040 seed: _,
1041 start_checkpoint: _,
1042 lock_checkpoint: _,
1043 epoch_length,
1044 } = self;
1045
1046 total_currency.check(w);
1047 epoch_length.check(w);
1048 }
1049}
1050
1051impl<F: FieldWitness> Check<F>
1052 for ConsensusProofOfStakeDataEpochDataNextValueVersionedValueStableV1
1053{
1054 fn check(&self, w: &mut Witness<F>) {
1055 let Self {
1056 ledger:
1057 MinaBaseEpochLedgerValueStableV1 {
1058 hash: _,
1059 total_currency,
1060 },
1061 seed: _,
1062 start_checkpoint: _,
1063 lock_checkpoint: _,
1064 epoch_length,
1065 } = self;
1066
1067 total_currency.check(w);
1068 epoch_length.check(w);
1069 }
1070}
1071
1072impl<F: FieldWitness> Check<F>
1073 for crate::scan_state::transaction_logic::protocol_state::EpochData<Fp>
1074{
1075 fn check(&self, w: &mut Witness<F>) {
1076 let Self {
1077 ledger:
1078 crate::scan_state::transaction_logic::protocol_state::EpochLedger {
1079 hash: _,
1080 total_currency,
1081 },
1082 seed: _,
1083 start_checkpoint: _,
1084 lock_checkpoint: _,
1085 epoch_length,
1086 } = self;
1087
1088 total_currency.check(w);
1089 epoch_length.check(w);
1090 }
1091}
1092
1093impl Check<Fp> for crate::proofs::block::consensus::GlobalSlot {
1094 fn check(&self, w: &mut Witness<Fp>) {
1095 let Self {
1096 slot_number,
1097 slots_per_epoch,
1098 } = self;
1099
1100 slot_number.check(w);
1101 slots_per_epoch.check(w);
1102 }
1103}
1104
1105impl<F: FieldWitness> Check<F> for super::block::BlockchainState {
1106 fn check(&self, w: &mut Witness<F>) {
1107 let Self {
1108 staged_ledger_hash: _,
1109 genesis_ledger_hash: _,
1110 ledger_proof_statement:
1111 Statement {
1112 source,
1113 target,
1114 connecting_ledger_left: _,
1115 connecting_ledger_right: _,
1116 supply_increase,
1117 fee_excess,
1118 sok_digest: _,
1119 },
1120 timestamp,
1121 body_reference: _,
1122 } = self;
1123
1124 source.check(w);
1125 target.check(w);
1126 supply_increase.check(w);
1127 fee_excess.check(w);
1128 timestamp.check(w);
1129 }
1130}
1131
1132impl Check<Fp> for super::block::ProtocolStateBody {
1133 fn check(&self, w: &mut Witness<Fp>) {
1134 let Self {
1135 genesis_state_hash: _,
1136 blockchain_state,
1137 consensus_state:
1138 super::block::consensus::ConsensusState {
1139 blockchain_length,
1140 epoch_count,
1141 min_window_density,
1142 sub_window_densities,
1143 last_vrf_output: _,
1144 total_currency,
1145 curr_global_slot_since_hard_fork,
1146 global_slot_since_genesis,
1147 staking_epoch_data,
1148 next_epoch_data,
1149 has_ancestor_in_same_checkpoint_window,
1150 block_stake_winner: _,
1151 block_creator: _,
1152 coinbase_receiver: _,
1153 supercharge_coinbase,
1154 },
1155 constants:
1156 MinaBaseProtocolConstantsCheckedValueStableV1 {
1157 k,
1158 slots_per_epoch,
1159 slots_per_sub_window,
1160 grace_period_slots,
1161 delta,
1162 genesis_state_timestamp,
1163 },
1164 } = self;
1165
1166 blockchain_state.check(w);
1167
1168 blockchain_length.check(w);
1169 epoch_count.check(w);
1170 min_window_density.check(w);
1171 for sub_window_density in sub_window_densities {
1173 sub_window_density.check(w);
1174 }
1175 total_currency.check(w);
1176 curr_global_slot_since_hard_fork.check(w);
1177 global_slot_since_genesis.check(w);
1178 staking_epoch_data.check(w);
1179 next_epoch_data.check(w);
1180 has_ancestor_in_same_checkpoint_window.check(w);
1181 supercharge_coinbase.check(w);
1182 k.check(w);
1183 slots_per_epoch.check(w);
1184 slots_per_sub_window.check(w);
1185 grace_period_slots.check(w);
1186 delta.check(w);
1187 genesis_state_timestamp.check(w);
1188 }
1189}
1190
1191#[derive(
1199 Clone,
1200 derive_more::Add,
1201 derive_more::Sub,
1202 derive_more::Neg,
1203 derive_more::Mul,
1204 derive_more::Div,
1205 PartialEq,
1206 Eq,
1207)]
1208pub struct InnerCurve<F: FieldWitness> {
1209 inner: F::Projective,
1212}
1213
1214impl<F: FieldWitness> std::fmt::Debug for InnerCurve<F> {
1215 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1216 let GroupAffine::<F> { x, y, .. } = self.to_affine();
1219 f.debug_struct("InnerCurve")
1220 .field("x", &x)
1221 .field("y", &y)
1222 .finish()
1223 }
1224}
1225
1226impl crate::ToInputs for InnerCurve<Fp> {
1227 fn to_inputs(&self, inputs: &mut ::poseidon::hash::Inputs) {
1228 let GroupAffine::<Fp> { x, y, .. } = self.to_affine();
1229 inputs.append_field(x);
1230 inputs.append_field(y);
1231 }
1232}
1233
1234impl<F: FieldWitness> From<(F, F)> for InnerCurve<F> {
1235 fn from((x, y): (F, F)) -> Self {
1236 Self::of_affine(make_group(x, y))
1237 }
1238}
1239
1240impl<F: FieldWitness> InnerCurve<F> {
1241 pub fn one() -> Self {
1242 let inner = F::Projective::prime_subgroup_generator();
1243 Self { inner }
1244 }
1245
1246 fn double(&self) -> Self {
1247 Self {
1248 inner: self.inner.double(),
1249 }
1250 }
1251
1252 fn scale<S>(&self, scale: S) -> Self
1253 where
1254 S: Into<BigInteger256>,
1255 {
1256 let scale: BigInteger256 = scale.into();
1257 let scale = scale.to_64x4();
1258 Self {
1259 inner: self.inner.mul(scale),
1260 }
1261 }
1262
1263 fn add_fast(&self, other: Self, w: &mut Witness<F>) -> Self {
1264 let result = w.add_fast(self.to_affine(), other.to_affine());
1265 Self::of_affine(result)
1266 }
1267
1268 pub fn to_affine(&self) -> GroupAffine<F> {
1269 let affine: F::Affine = self.inner.into_affine();
1271 let affine: GroupAffine<F> = affine.into();
1272 assert!(!affine.infinity);
1275 affine
1276 }
1277
1278 pub fn of_affine(affine: GroupAffine<F>) -> Self {
1279 let inner: GroupProjective<F::Parameters> = affine.into_projective();
1281 let inner: F::Projective = inner.into();
1282 Self { inner }
1283 }
1284
1285 pub fn random() -> Self {
1286 let mut rng = get_rng();
1288
1289 let proj: GroupProjective<F::Parameters> = ark_ff::UniformRand::rand(&mut rng);
1291 let proj: F::Projective = proj.into();
1292
1293 let proj2 = proj;
1294 LATEST_RANDOM.set(Box::new(move || {
1295 let this = Self { inner: proj2 };
1296 format!("{:#?}", this.to_affine())
1297 }));
1298
1299 Self { inner: proj }
1300 }
1301}
1302
1303use std::cell::RefCell;
1304
1305thread_local! {
1306 static LATEST_RANDOM: RefCell<Box<dyn Fn() -> String>> = RefCell::new(Box::new(String::new));
1307}
1308
1309impl InnerCurve<Fp> {
1310 pub fn rand() -> Self {
1312 let kp = gen_keypair();
1313 let point = kp.public.into_point();
1314 assert!(point.is_on_curve());
1315 Self::of_affine(point)
1316 }
1317}
1318
1319pub fn create_shifted_inner_curve<F>(w: &mut Witness<F>) -> InnerCurve<F>
1321where
1322 F: FieldWitness,
1323{
1324 w.exists(InnerCurve::<F>::random())
1325}
1326
1327impl<F: FieldWitness> Check<F> for InnerCurve<F> {
1328 fn check(&self, w: &mut Witness<F>) {
1330 self.to_affine().check(w);
1331 }
1332}
1333
1334impl<F: FieldWitness> Check<F> for GroupAffine<F> {
1335 fn check(&self, w: &mut Witness<F>) {
1337 let GroupAffine::<F> { x, y: _, .. } = self;
1338 let x2 = field::square(*x, w);
1339 let _x3 = field::mul(x2, *x, w);
1340 }
1342}
1343
1344impl<F: FieldWitness> Check<F> for transaction_union_payload::Tag {
1345 fn check(&self, _w: &mut Witness<F>) {
1346 }
1350}
1351
1352impl<F: FieldWitness> Check<F> for transaction_union_payload::TransactionUnion {
1353 fn check(&self, w: &mut Witness<F>) {
1354 use transaction_union_payload::{Body, Common, TransactionUnionPayload};
1355
1356 let Self {
1357 payload:
1358 TransactionUnionPayload {
1359 common:
1360 Common {
1361 fee,
1362 fee_token: _,
1363 fee_payer_pk: _,
1364 nonce,
1365 valid_until,
1366 memo: _,
1367 },
1368 body:
1369 Body {
1370 tag,
1371 source_pk: _,
1372 receiver_pk: _,
1373 token_id: _,
1374 amount,
1375 },
1376 },
1377 signer: _,
1378 signature: _,
1379 } = self;
1380
1381 fee.check(w);
1382 nonce.check(w);
1383 valid_until.check(w);
1384 tag.check(w);
1385 amount.check(w);
1386 }
1387}
1388
1389impl<F: FieldWitness> Check<F> for pending_coinbase::StateStack {
1390 fn check(&self, _w: &mut Witness<F>) {
1391 }
1393}
1394
1395impl<F: FieldWitness> Check<F> for pending_coinbase::Stack {
1396 fn check(&self, _w: &mut Witness<F>) {
1397 }
1399}
1400
1401impl<F: FieldWitness> Check<F> for TokenSymbol {
1402 fn check(&self, w: &mut Witness<F>) {
1403 let field: F = self.to_field();
1404 scalar_challenge::to_field_checked_prime::<F, 48>(field, w);
1405 }
1406}
1407
1408impl<F: FieldWitness> Check<F> for Box<Account> {
1409 fn check(&self, w: &mut Witness<F>) {
1410 let Account {
1411 public_key: _,
1412 token_id: _,
1413 token_symbol,
1414 balance,
1415 nonce,
1416 receipt_chain_hash: _,
1417 delegate: _,
1418 voting_for: _,
1419 timing,
1420 permissions,
1421 zkapp: _,
1422 } = &**self;
1423
1424 token_symbol.check(w);
1425 balance.check(w);
1426 nonce.check(w);
1427 timing.check(w);
1428 permissions.check(w);
1429 }
1430}
1431
1432impl<F: FieldWitness> Check<F> for crate::Timing {
1433 fn check(&self, w: &mut Witness<F>) {
1434 let TimingAsRecord {
1435 is_timed: _,
1436 initial_minimum_balance,
1437 cliff_time,
1438 cliff_amount,
1439 vesting_period,
1440 vesting_increment,
1441 } = self.to_record();
1442
1443 initial_minimum_balance.check(w);
1444 cliff_time.check(w);
1445 cliff_amount.check(w);
1446 vesting_period.check(w);
1447 vesting_increment.check(w);
1448 }
1449}
1450
1451impl<F: FieldWitness> Check<F> for crate::MerklePath {
1452 fn check(&self, _w: &mut Witness<F>) {
1453 }
1455}
1456
1457impl<F: FieldWitness, T: Check<F>> Check<F> for Vec<T> {
1458 fn check(&self, w: &mut Witness<F>) {
1459 self.iter().for_each(|v| v.check(w))
1460 }
1461}
1462
1463impl<F: FieldWitness, T: Check<F>> Check<F> for Box<[T]> {
1464 fn check(&self, w: &mut Witness<F>) {
1465 self.iter().for_each(|v| v.check(w))
1466 }
1467}
1468
1469impl<F: FieldWitness, A: Check<F>, B: Check<F>> Check<F> for (A, B) {
1470 fn check(&self, w: &mut Witness<F>) {
1471 let (a, b) = self;
1472 a.check(w);
1473 b.check(w);
1474 }
1475}
1476
1477impl<F: FieldWitness> Check<F> for ReceiptChainHash {
1478 fn check(&self, _w: &mut Witness<F>) {
1479 }
1481}
1482
1483impl<F: FieldWitness> Check<F> for Sgn {
1484 fn check(&self, _w: &mut Witness<F>) {
1485 }
1487}
1488
1489impl<F: FieldWitness> Check<F> for CompressedPubKey {
1490 fn check(&self, _w: &mut Witness<F>) {
1491 }
1493}
1494
1495impl<F: FieldWitness> Check<F> for TokenId {
1496 fn check(&self, _w: &mut Witness<F>) {
1497 }
1499}
1500
1501impl<F: FieldWitness> Check<F> for v2::MinaBasePendingCoinbaseStackVersionedStableV1 {
1502 fn check(&self, _w: &mut Witness<F>) {
1503 }
1505}
1506
1507impl<F: FieldWitness> Check<F> for &[AllEvals<F>] {
1508 fn check(&self, _w: &mut Witness<F>) {
1509 }
1511}
1512
1513pub fn dummy_constraints<F>(w: &mut Witness<F>)
1514where
1515 F: FieldWitness,
1516{
1517 use crate::proofs::public_input::plonk_checks::ShiftingValue;
1518
1519 let x: F = w.exists(F::from(3u64));
1520 let g: InnerCurve<F> = w.exists(InnerCurve::<F>::one());
1521
1522 let _ = w.to_field_checked_prime::<16>(x);
1523
1524 let _ = plonk_curve_ops::scale_fast::<F, F, 5>(g.to_affine(), F::Shifting::of_raw(x), w);
1526 let _ = plonk_curve_ops::scale_fast::<F, F, 5>(g.to_affine(), F::Shifting::of_raw(x), w);
1527 let _ = scalar_challenge::endo::<F, F, 4>(g.to_affine(), x, w);
1528}
1529
1530pub mod legacy_input {
1531 use crate::scan_state::transaction_logic::transaction_union_payload::{
1532 Body, Common, TransactionUnionPayload,
1533 };
1534 use ::poseidon::hash::legacy;
1535
1536 use super::*;
1537
1538 pub struct BitsIterator<const N: usize> {
1539 pub index: usize,
1540 pub number: [u8; N],
1541 }
1542
1543 impl<const N: usize> Iterator for BitsIterator<N> {
1544 type Item = bool;
1545
1546 fn next(&mut self) -> Option<Self::Item> {
1547 let index = self.index;
1548 self.index += 1;
1549
1550 let limb_index = index / 8;
1551 let bit_index = index % 8;
1552
1553 let limb = self.number.get(limb_index)?;
1554 Some(limb & (1 << bit_index) != 0)
1555 }
1556 }
1557
1558 pub fn bits_iter<N: Into<u64>, const NBITS: usize>(number: N) -> impl Iterator<Item = bool> {
1559 let number: u64 = number.into();
1560 BitsIterator {
1561 index: 0,
1562 number: number.to_ne_bytes(),
1563 }
1564 .take(NBITS)
1565 }
1566
1567 pub fn to_bits<N: Into<u64>, const NBITS: usize>(number: N) -> [bool; NBITS] {
1568 let mut iter = bits_iter::<N, NBITS>(number);
1569 std::array::from_fn(|_| iter.next().unwrap())
1570 }
1571
1572 pub trait CheckedLegacyInput<F: FieldWitness> {
1573 fn to_checked_legacy_input(&self, inputs: &mut legacy::Inputs<F>, w: &mut Witness<F>);
1574
1575 fn to_checked_legacy_input_owned(&self, w: &mut Witness<F>) -> legacy::Inputs<F> {
1576 let mut inputs = legacy::Inputs::new();
1577 self.to_checked_legacy_input(&mut inputs, w);
1578 inputs
1579 }
1580 }
1581
1582 const LEGACY_DEFAULT_TOKEN: [bool; 64] = {
1583 let mut default = [false; 64];
1584 default[0] = true;
1585 default
1586 };
1587
1588 impl CheckedLegacyInput<Fp> for TransactionUnionPayload {
1589 fn to_checked_legacy_input(&self, inputs: &mut legacy::Inputs<Fp>, w: &mut Witness<Fp>) {
1590 let Self {
1591 common:
1592 Common {
1593 fee,
1594 fee_payer_pk,
1595 nonce,
1596 valid_until,
1597 memo,
1598 fee_token: _,
1599 },
1600 body:
1601 Body {
1602 tag,
1603 source_pk,
1604 receiver_pk,
1605 token_id: _,
1606 amount,
1607 },
1608 } = self;
1609
1610 let fee_token = &LEGACY_DEFAULT_TOKEN;
1611
1612 let nonce = w.exists(nonce.to_bits());
1614 let valid_until = w.exists(valid_until.to_bits());
1615 let fee = w.exists(fee.to_bits());
1616 inputs.append_bits(&fee);
1617 inputs.append_bits(fee_token);
1618 inputs.append_field(fee_payer_pk.x);
1619 inputs.append_bit(fee_payer_pk.is_odd);
1620 inputs.append_bits(&nonce);
1621 inputs.append_bits(&valid_until);
1622 inputs.append_bits(&memo.to_bits());
1623
1624 let amount = w.exists(amount.to_bits());
1626 inputs.append_bits(&tag.to_bits());
1627 inputs.append_field(source_pk.x);
1628 inputs.append_bit(source_pk.is_odd);
1629 inputs.append_field(receiver_pk.x);
1630 inputs.append_bit(receiver_pk.is_odd);
1631 inputs.append_bits(fee_token);
1632 inputs.append_bits(&amount);
1633 inputs.append_bit(false);
1634 }
1635 }
1636}
1637
1638pub mod poseidon {
1639 use std::marker::PhantomData;
1640
1641 use ::poseidon::{PlonkSpongeConstantsKimchi, SpongeConstants, SpongeParams, SpongeState};
1642
1643 use super::*;
1644
1645 #[derive(Clone)]
1646 pub struct Sponge<F: FieldWitness, C: SpongeConstants = PlonkSpongeConstantsKimchi> {
1647 pub state: [F; 3],
1648 pub sponge_state: SpongeState,
1649 params: &'static SpongeParams<F>,
1650 nabsorb: usize,
1651 _constants: PhantomData<C>,
1652 }
1653
1654 impl<F, C> Default for Sponge<F, C>
1655 where
1656 F: FieldWitness,
1657 C: SpongeConstants,
1658 {
1659 fn default() -> Self {
1660 Self::new()
1661 }
1662 }
1663
1664 impl<F, C> Sponge<F, C>
1665 where
1666 F: FieldWitness,
1667 C: SpongeConstants,
1668 {
1669 pub fn new_with_state_params(state: [F; 3], params: &'static SpongeParams<F>) -> Self {
1670 Self {
1671 state,
1672 sponge_state: SpongeState::Absorbed(0),
1673 params,
1674 nabsorb: 0,
1675 _constants: PhantomData,
1676 }
1677 }
1678
1679 pub fn new_with_state(state: [F; 3]) -> Self {
1680 Self::new_with_state_params(state, F::get_params())
1681 }
1682
1683 pub fn new() -> Self {
1684 Self::new_with_state([F::zero(); 3])
1685 }
1686
1687 fn absorb_empty(&mut self, w: &mut Witness<F>) {
1688 match self.sponge_state {
1689 SpongeState::Absorbed(n) => {
1690 if n == C::SPONGE_RATE {
1691 self.poseidon_block_cipher(true, w);
1692 self.sponge_state = SpongeState::Absorbed(1);
1693 } else {
1694 self.sponge_state = SpongeState::Absorbed(n + 1);
1695 }
1696 }
1697 SpongeState::Squeezed(_n) => {
1698 self.sponge_state = SpongeState::Absorbed(1);
1699 }
1700 }
1701 }
1702
1703 pub fn absorb(&mut self, x: &[F], w: &mut Witness<F>) {
1704 if x.is_empty() {
1705 self.absorb_empty(w);
1706 return;
1707 }
1708
1709 let mut first = true;
1712
1713 for x in x.iter() {
1714 match self.sponge_state {
1715 SpongeState::Absorbed(n) => {
1716 if n == C::SPONGE_RATE {
1717 self.poseidon_block_cipher(first, w);
1719 self.sponge_state = SpongeState::Absorbed(1);
1720 self.state[0].add_assign(x);
1721 w.exists(self.state[0]); first = false;
1723 } else {
1724 self.sponge_state = SpongeState::Absorbed(n + 1);
1726 self.state[n].add_assign(x);
1727 w.exists(self.state[n]); }
1729 }
1730 SpongeState::Squeezed(_n) => {
1731 self.state[0].add_assign(x);
1732 w.exists(self.state[0]); self.sponge_state = SpongeState::Absorbed(1);
1734 }
1735 }
1736 }
1737 }
1738
1739 pub fn absorb2(&mut self, x: &[F], w: &mut Witness<F>) {
1740 if x.is_empty() {
1741 self.absorb_empty(w);
1742 return;
1743 }
1744
1745 let mut first = true;
1748
1749 for x in x.iter() {
1750 match self.sponge_state {
1751 SpongeState::Absorbed(n) => {
1752 if n == C::SPONGE_RATE {
1753 self.poseidon_block_cipher(first, w);
1755 self.sponge_state = SpongeState::Absorbed(1);
1756 self.state[0].add_assign(x);
1757 w.exists(self.state[0]); first = false;
1759 } else {
1760 self.sponge_state = SpongeState::Absorbed(n + 1);
1762 self.state[n].add_assign(x);
1763 if self.nabsorb > 2 {
1764 w.exists(self.state[n]); }
1766 }
1767 }
1768 SpongeState::Squeezed(_n) => {
1769 self.state[0].add_assign(x);
1771 w.exists(self.state[0]); self.sponge_state = SpongeState::Absorbed(1);
1773 }
1774 }
1775 self.nabsorb += 1;
1776 }
1777 }
1778
1779 pub fn absorb3(&mut self, x: &[F], w: &mut Witness<F>) {
1780 if x.is_empty() {
1781 self.absorb_empty(w);
1782 return;
1783 }
1784
1785 let mut first = true;
1788
1789 for x in x.iter() {
1790 match self.sponge_state {
1791 SpongeState::Absorbed(n) => {
1792 if n == C::SPONGE_RATE {
1793 self.poseidon_block_cipher(first, w);
1795 self.sponge_state = SpongeState::Absorbed(1);
1796 self.state[0].add_assign(x);
1797 if self.nabsorb > 2 {
1798 w.exists(self.state[0]); }
1800 first = false;
1801 } else {
1802 self.sponge_state = SpongeState::Absorbed(n + 1);
1804 self.state[n].add_assign(x);
1805 if self.nabsorb > 2 {
1806 w.exists(self.state[n]); }
1808 }
1809 }
1810 SpongeState::Squeezed(_n) => {
1811 self.state[0].add_assign(x);
1813 w.exists(self.state[0]); self.sponge_state = SpongeState::Absorbed(1);
1815 }
1816 }
1817 self.nabsorb += 1;
1818 }
1819 }
1820
1821 pub fn squeeze(&mut self, w: &mut Witness<F>) -> F {
1822 match self.sponge_state {
1823 SpongeState::Squeezed(n) => {
1824 if n == C::SPONGE_RATE {
1825 self.poseidon_block_cipher(false, w);
1826 self.sponge_state = SpongeState::Squeezed(1);
1827 self.state[0]
1828 } else {
1829 self.sponge_state = SpongeState::Squeezed(n + 1);
1830 self.state[n]
1831 }
1832 }
1833 SpongeState::Absorbed(_n) => {
1834 self.poseidon_block_cipher(false, w);
1835 self.sponge_state = SpongeState::Squeezed(1);
1836 self.state[0]
1837 }
1838 }
1839 }
1840
1841 pub fn poseidon_block_cipher(&mut self, first: bool, w: &mut Witness<F>) {
1842 if C::PERM_HALF_ROUNDS_FULL == 0 {
1843 if C::PERM_INITIAL_ARK {
1844 for (i, x) in self.params.round_constants[0].iter().enumerate() {
1847 self.state[i].add_assign(x);
1848 }
1849 w.exists(self.state[0]); w.exists(self.state[1]); if !first {
1852 w.exists(self.state[2]); }
1854 for r in 0..C::PERM_ROUNDS_FULL {
1856 self.full_round(r + 1, first && r == 0, w);
1857 }
1858 } else {
1859 w.exists(self.state);
1862 for r in 0..C::PERM_ROUNDS_FULL {
1863 self.full_round(r, first, w);
1864 }
1865 }
1866 } else {
1867 unimplemented!()
1868 }
1869 }
1870
1871 pub fn full_round(&mut self, r: usize, first: bool, w: &mut Witness<F>) {
1872 for (index, state_i) in self.state.iter_mut().enumerate() {
1873 let push_witness = !(first && index == 2);
1874 *state_i = sbox::<F, C>(*state_i, push_witness, w);
1875 }
1876 self.state = apply_mds_matrix::<F, C>(self.params, &self.state);
1877 for (i, x) in self.params.round_constants[r].iter().enumerate() {
1878 self.state[i].add_assign(x);
1879 if C::PERM_SBOX == 5 {
1880 w.exists(self.state[i]); }
1883 }
1884 if C::PERM_SBOX == 7 {
1885 w.exists(self.state);
1887 }
1888 }
1889 }
1890
1891 pub fn sbox<F: FieldWitness, C: SpongeConstants>(
1892 x: F,
1893 push_witness: bool,
1894 w: &mut Witness<F>,
1895 ) -> F {
1896 if C::PERM_SBOX == 5 {
1897 let res = x;
1900 let res = res * res;
1901 if push_witness {
1902 w.exists(res); }
1904 let res = res * res;
1905 if push_witness {
1906 w.exists(res); }
1908 let res = res * x;
1909 if push_witness {
1910 w.exists(res); }
1912 res
1913 } else if C::PERM_SBOX == 7 {
1914 let mut res = x.square();
1917 res *= x;
1918 let res = res.square();
1919 res * x
1920 } else {
1921 unimplemented!()
1922 }
1923 }
1924
1925 fn apply_mds_matrix<F: Field, C: SpongeConstants>(
1926 params: &SpongeParams<F>,
1927 state: &[F; 3],
1928 ) -> [F; 3] {
1929 if C::PERM_FULL_MDS {
1930 std::array::from_fn(|i| {
1931 state
1932 .iter()
1933 .zip(params.mds[i].iter())
1934 .fold(F::zero(), |x, (s, &m)| m * s + x)
1935 })
1936 } else {
1937 [
1938 state[0] + state[2],
1939 state[0] + state[1],
1940 state[1] + state[2],
1941 ]
1942 }
1943 }
1944}
1945
1946fn double_group<F: FieldWitness>(group: GroupAffine<F>, w: &mut Witness<F>) -> GroupAffine<F> {
1947 let GroupAffine::<F> { x: ax, y: ay, .. } = group;
1948 let ax: F = ax;
1949 let ay: F = ay;
1950
1951 let x_squared = w.exists(ax.square());
1952 let lambda = w.exists({
1953 (x_squared + x_squared + x_squared + F::PARAMS.a) * (ay + ay).inverse().unwrap()
1954 });
1955 let bx = w.exists(lambda.square() - (ax + ax));
1956 let by = w.exists((lambda * (ax - bx)) - ay);
1957
1958 make_group(bx, by)
1959}
1960
1961fn group_to_witness<F: FieldWitness>(group: GroupAffine<F>, w: &mut Witness<F>) -> GroupAffine<F> {
1963 let GroupAffine::<F> { x, y, .. } = &group;
1965 w.exists(*x);
1966 w.exists(*y);
1967 group
1968}
1969
1970pub fn scale_non_constant<F: FieldWitness, const N: usize>(
1971 mut g: GroupAffine<F>,
1972 bits: &[bool; N],
1973 init: &InnerCurve<F>,
1974 w: &mut Witness<F>,
1975) -> GroupAffine<F> {
1976 let mut acc = init.to_affine();
1977
1978 for b in bits {
1979 acc = {
1980 let add_pt = w.add_fast(acc, g);
1981 let dont_add_pt = acc;
1982 if *b {
1983 group_to_witness(add_pt, w)
1984 } else {
1985 group_to_witness(dont_add_pt, w)
1986 }
1987 };
1988 g = double_group(g, w);
1989 }
1990
1991 acc
1992}
1993
1994fn lookup_point<F: FieldWitness>(
1995 (b0, b1): (bool, bool),
1996 (t1, t2, t3, t4): (InnerCurve<F>, InnerCurve<F>, InnerCurve<F>, InnerCurve<F>),
1997 w: &mut Witness<F>,
1998) -> (F, F) {
1999 let b0_and_b1 = w.exists(F::from(b0 && b1));
2002 let b0 = F::from(b0);
2003 let b1 = F::from(b1);
2004 let lookup_one = |a1: F, a2: F, a3: F, a4: F| -> F {
2005 a1 + ((a2 - a1) * b0) + ((a3 - a1) * b1) + ((a4 + a1 - a2 - a3) * b0_and_b1)
2006 };
2007 let GroupAffine::<F> { x: x1, y: y1, .. } = t1.to_affine();
2008 let GroupAffine::<F> { x: x2, y: y2, .. } = t2.to_affine();
2009 let GroupAffine::<F> { x: x3, y: y3, .. } = t3.to_affine();
2010 let GroupAffine::<F> { x: x4, y: y4, .. } = t4.to_affine();
2011
2012 (lookup_one(x1, x2, x3, x4), lookup_one(y1, y2, y3, y4))
2013}
2014
2015fn lookup_single_bit<F: FieldWitness>(b: bool, (t1, t2): (InnerCurve<F>, InnerCurve<F>)) -> (F, F) {
2016 let lookup_one = |a1: F, a2: F| a1 + (F::from(b) * (a2 - a1));
2017
2018 let GroupAffine::<F> { x: x1, y: y1, .. } = t1.to_affine();
2019 let GroupAffine::<F> { x: x2, y: y2, .. } = t2.to_affine();
2020
2021 (lookup_one(x1, x2), lookup_one(y1, y2))
2022}
2023
2024pub fn scale_known<F: FieldWitness, const N: usize>(
2025 t: GroupAffine<F>,
2026 bits: &[bool; N],
2027 init: &InnerCurve<F>,
2028 w: &mut Witness<F>,
2029) -> GroupAffine<F> {
2030 let sigma = InnerCurve::of_affine(t);
2031 let n = bits.len();
2032 let sigma_count = (n + 1) / 2;
2033
2034 let to_term = |two_to_the_i: InnerCurve<F>,
2035 two_to_the_i_plus_1: InnerCurve<F>,
2036 bits: (bool, bool),
2037 w: &mut Witness<F>| {
2038 let sigma0 = sigma.clone();
2039 let sigma1 = sigma.clone();
2040 let sigma2 = sigma.clone();
2041 let sigma3 = sigma.clone();
2042 lookup_point(
2043 bits,
2044 (
2045 sigma0,
2046 (sigma1 + two_to_the_i.clone()),
2047 (sigma2 + two_to_the_i_plus_1.clone()),
2048 (sigma3 + two_to_the_i + two_to_the_i_plus_1),
2049 ),
2050 w,
2051 )
2052 };
2053
2054 let mut acc = init.to_affine();
2055 let mut two_to_the_i = sigma.clone();
2056 for chunk in bits.chunks(2) {
2057 match chunk {
2058 [b_i] => {
2059 let (term_x, term_y) =
2060 lookup_single_bit(*b_i, (sigma.clone(), sigma.clone() + two_to_the_i.clone()));
2061 let [term_y, term_x] = w.exists([term_y, term_x]);
2062 acc = w.add_fast(acc, make_group(term_x, term_y));
2063 }
2064 [b_i, b_i_plus_1] => {
2065 let two_to_the_i_plus_1 = two_to_the_i.double().to_affine();
2066 let (term_x, term_y) = to_term(
2067 two_to_the_i.clone(),
2068 InnerCurve::of_affine(two_to_the_i_plus_1),
2069 (*b_i, *b_i_plus_1),
2070 w,
2071 );
2072 let [term_y, term_x] = w.exists([term_y, term_x]);
2073 acc = w.add_fast(acc, make_group(term_x, term_y));
2074 two_to_the_i = InnerCurve::of_affine(two_to_the_i_plus_1).double();
2075 }
2076 _ => unreachable!(), }
2078 }
2079
2080 let result_with_shift = acc;
2081 let unshift = std::ops::Neg::neg(sigma).scale(sigma_count as u64);
2082
2083 w.add_fast(result_with_shift, unshift.to_affine())
2084}
2085
2086#[derive(Debug)]
2087enum ExprBinary<T> {
2088 Lit(T),
2089 And(T, Box<ExprBinary<T>>),
2090 Or(T, Box<ExprBinary<T>>),
2091}
2092
2093#[derive(Debug)]
2094enum ExprNary<T> {
2095 Lit(T),
2096 And(Vec<ExprNary<T>>),
2097 Or(Vec<ExprNary<T>>),
2098}
2099
2100fn lt_binary<F: FieldWitness>(xs: &[bool], ys: &[bool]) -> ExprBinary<Boolean> {
2101 match (xs, ys) {
2102 ([], []) => ExprBinary::Lit(Boolean::False),
2103 ([_x], [false]) => ExprBinary::Lit(Boolean::False),
2104 ([x], [true]) => ExprBinary::Lit(x.to_boolean().neg()),
2105 ([x1, _x2], [true, false]) => ExprBinary::Lit(x1.to_boolean().neg()),
2106 ([_x1, _x2], [false, false]) => ExprBinary::Lit(Boolean::False),
2107 ([x, xs @ ..], [false, ys @ ..]) => {
2108 ExprBinary::And(x.to_boolean().neg(), Box::new(lt_binary::<F>(xs, ys)))
2109 }
2110 ([x, xs @ ..], [true, ys @ ..]) => {
2111 ExprBinary::Or(x.to_boolean().neg(), Box::new(lt_binary::<F>(xs, ys)))
2112 }
2113 _ => panic!("unequal length"),
2114 }
2115}
2116
2117fn of_binary<F: FieldWitness>(expr: &ExprBinary<Boolean>) -> ExprNary<Boolean> {
2118 match expr {
2119 ExprBinary::Lit(x) => ExprNary::Lit(*x),
2120 ExprBinary::And(x, t) => match &**t {
2121 ExprBinary::And(y, t) => ExprNary::And(vec![
2122 ExprNary::Lit(*x),
2123 ExprNary::Lit(*y),
2124 of_binary::<F>(t),
2125 ]),
2126 _ => ExprNary::And(vec![ExprNary::Lit(*x), of_binary::<F>(t)]),
2127 },
2128 ExprBinary::Or(x, t) => match &**t {
2129 ExprBinary::Or(y, t) => ExprNary::Or(vec![
2130 ExprNary::Lit(*x),
2131 ExprNary::Lit(*y),
2132 of_binary::<F>(t),
2133 ]),
2134 _ => ExprNary::Or(vec![ExprNary::Lit(*x), of_binary::<F>(t)]),
2135 },
2136 }
2137}
2138
2139impl ExprNary<Boolean> {
2140 fn eval<F: FieldWitness>(&self, w: &mut Witness<F>) -> Boolean {
2141 match self {
2142 ExprNary::Lit(x) => *x,
2143 ExprNary::And(xs) => {
2144 let xs: Vec<_> = xs.iter().map(|x| Self::eval::<F>(x, w)).collect();
2145 Boolean::all::<F>(&xs, w)
2146 }
2147 ExprNary::Or(xs) => {
2148 let xs: Vec<_> = xs.iter().map(|x| Self::eval::<F>(x, w)).collect();
2149 Boolean::any::<F>(&xs, w)
2150 }
2151 }
2152 }
2153}
2154
2155fn lt_bitstring_value<F: FieldWitness>(
2156 xs: &[bool; 255],
2157 ys: &[bool; 255],
2158 w: &mut Witness<F>,
2159) -> Boolean {
2160 let value = of_binary::<F>(<_binary::<F>(xs, ys));
2161 value.eval(w)
2162}
2163
2164fn unpack_full<F: FieldWitness>(x: F, w: &mut Witness<F>) -> [bool; 255] {
2165 let bits_lsb = w.exists(field_to_bits::<F, 255>(x));
2166
2167 let bits_msb = {
2168 let mut bits = bits_lsb;
2169 bits.reverse(); bits
2171 };
2172
2173 let size_msb = {
2174 let mut size = bigint_to_bits::<255>(F::SIZE);
2175 size.reverse(); size
2177 };
2178
2179 lt_bitstring_value::<F>(&bits_msb, &size_msb, w);
2180
2181 bits_lsb
2182}
2183
2184fn is_even<F: FieldWitness>(y: F, w: &mut Witness<F>) -> Boolean {
2185 let bits_msb = {
2186 let mut bits = w.exists(field_to_bits::<F, 255>(y));
2187 bits.reverse(); bits
2189 };
2190
2191 let size_msb = {
2192 let mut size = bigint_to_bits::<255>(F::SIZE);
2193 size.reverse(); size
2195 };
2196
2197 lt_bitstring_value::<F>(&bits_msb, &size_msb, w)
2198}
2199
2200pub struct CompressedPubKeyVar<F: FieldWitness> {
2201 pub x: F,
2202 pub is_odd: bool,
2203}
2204
2205pub fn compress_var<F: FieldWitness>(
2206 v: &GroupAffine<F>,
2207 w: &mut Witness<F>,
2208) -> CompressedPubKeyVar<F> {
2209 let GroupAffine::<F> { x, y, .. } = v;
2210
2211 let is_odd = {
2212 let bits = unpack_full(*y, w);
2213 bits[0]
2214 };
2215
2216 CompressedPubKeyVar { x: *x, is_odd }
2217}
2218
2219pub fn decompress_var(pk: &CompressedPubKey, w: &mut Witness<Fp>) -> PubKey {
2220 let CompressedPubKey { x, is_odd: _ } = pk;
2221 let GroupAffine::<Fp> { y, .. } = decompress_pk(pk).unwrap().into_point();
2222
2223 w.exists(y);
2224
2225 let point = make_group(*x, y);
2226 point.check(w);
2227
2228 let _is_odd2 = {
2229 let bits = unpack_full(y, w);
2230 bits[0]
2231 };
2232 PubKey::from_point_unsafe(point)
2233}
2234
2235pub mod transaction_snark {
2236 use std::ops::Neg;
2237
2238 use crate::{
2239 checked_equal_compressed_key, checked_equal_compressed_key_const_and,
2240 checked_verify_merkle_path,
2241 proofs::{
2242 block::ProtocolStateBody,
2243 numbers::{
2244 currency::{
2245 CheckedAmount, CheckedBalance, CheckedCurrency, CheckedFee, CheckedSigned,
2246 },
2247 nat::{CheckedNat, CheckedSlot, CheckedSlotSpan},
2248 },
2249 transaction::legacy_input::CheckedLegacyInput,
2250 },
2251 scan_state::{
2252 fee_excess::CheckedFeeExcess,
2253 transaction_logic::{checked_cons_signed_command_payload, Coinbase},
2254 },
2255 sparse_ledger::SparseLedger,
2256 zkapps::intefaces::{SignedAmountBranchParam, SignedAmountInterface},
2257 AccountId, PermissionTo, PermsConst, Timing, TimingAsRecordChecked, ToInputs,
2258 };
2259 use ::poseidon::hash::{params::MINA_PROTO_STATE_BODY, Inputs, LazyParam};
2260 use ark_ff::Zero;
2261
2262 use crate::scan_state::{
2263 currency,
2264 transaction_logic::transaction_union_payload::{TransactionUnion, TransactionUnionPayload},
2265 };
2266 use ::poseidon::hash::legacy;
2267 use mina_signer::Signature;
2268 use openmina_core::constants::constraint_constants;
2269
2270 use super::*;
2271
2272 mod user_command_failure {
2273 use crate::scan_state::{
2274 currency::Magnitude,
2275 transaction_logic::{
2276 timing_error_to_user_command_status, validate_timing, TransactionFailure,
2277 },
2278 };
2279
2280 use super::*;
2281
2282 const NUM_FIELDS: usize = 8;
2283
2284 pub struct Failure {
2285 pub predicate_failed: bool, pub source_not_present: bool, pub receiver_not_present: bool, pub amount_insufficient_to_create: bool, pub token_cannot_create: bool, pub source_insufficient_balance: bool, pub source_minimum_balance_violation: bool, pub source_bad_timing: bool, }
2294
2295 impl<F: FieldWitness> ToFieldElements<F> for Failure {
2296 fn to_field_elements(&self, fields: &mut Vec<F>) {
2297 let list = self.to_list();
2298 list.to_field_elements(fields)
2299 }
2300 }
2301
2302 impl<F: FieldWitness> Check<F> for Failure {
2303 fn check(&self, _w: &mut Witness<F>) {
2304 }
2306 }
2307
2308 impl Failure {
2309 fn empty() -> Self {
2310 Self {
2311 predicate_failed: false,
2312 source_not_present: false,
2313 receiver_not_present: false,
2314 amount_insufficient_to_create: false,
2315 token_cannot_create: false,
2316 source_insufficient_balance: false,
2317 source_minimum_balance_violation: false,
2318 source_bad_timing: false,
2319 }
2320 }
2321
2322 pub fn to_list(&self) -> [Boolean; NUM_FIELDS] {
2323 let Self {
2324 predicate_failed,
2325 source_not_present,
2326 receiver_not_present,
2327 amount_insufficient_to_create,
2328 token_cannot_create,
2329 source_insufficient_balance,
2330 source_minimum_balance_violation,
2331 source_bad_timing,
2332 } = self;
2333
2334 [
2335 predicate_failed.to_boolean(),
2336 source_not_present.to_boolean(),
2337 receiver_not_present.to_boolean(),
2338 amount_insufficient_to_create.to_boolean(),
2339 token_cannot_create.to_boolean(),
2340 source_insufficient_balance.to_boolean(),
2341 source_minimum_balance_violation.to_boolean(),
2342 source_bad_timing.to_boolean(),
2343 ]
2344 }
2345 }
2346
2347 pub fn compute_as_prover<F: FieldWitness>(
2348 txn_global_slot: CheckedSlot<F>,
2349 txn: &TransactionUnion,
2350 sparse_ledger: &SparseLedger,
2351 w: &mut Witness<F>,
2352 ) -> Failure {
2353 w.exists(compute_as_prover_impl(
2354 txn_global_slot.to_inner(),
2355 txn,
2356 sparse_ledger,
2357 ))
2358 }
2359
2360 fn compute_as_prover_impl(
2363 txn_global_slot: currency::Slot,
2364 txn: &TransactionUnion,
2365 sparse_ledger: &SparseLedger,
2366 ) -> Failure {
2367 use transaction_union_payload::Tag::*;
2368
2369 let _fee_token = &txn.payload.common.fee_token;
2370 let token = &txn.payload.body.token_id;
2371 let fee_payer =
2372 AccountId::create(txn.payload.common.fee_payer_pk.clone(), token.clone());
2373 let source = AccountId::create(txn.payload.body.source_pk.clone(), token.clone());
2374 let receiver = AccountId::create(txn.payload.body.receiver_pk.clone(), token.clone());
2375
2376 let mut fee_payer_account = sparse_ledger.get_account(&fee_payer);
2377 let source_account = sparse_ledger.get_account(&source);
2378 let receiver_account = sparse_ledger.get_account(&receiver);
2379
2380 let TransactionUnion {
2382 payload,
2383 signer: _,
2384 signature: _,
2385 } = txn;
2386
2387 if let FeeTransfer | Coinbase = payload.body.tag {
2388 return Failure::empty();
2389 };
2390
2391 fee_payer_account.balance = fee_payer_account
2392 .balance
2393 .sub_amount(currency::Amount::of_fee(&payload.common.fee))
2394 .unwrap();
2395
2396 let predicate_failed = if payload.common.fee_payer_pk == payload.body.source_pk {
2397 false
2398 } else {
2399 match payload.body.tag {
2400 Payment | StakeDelegation => true,
2401 FeeTransfer | Coinbase => panic!(), }
2403 };
2404
2405 match payload.body.tag {
2406 FeeTransfer | Coinbase => panic!(), StakeDelegation => {
2408 let receiver_account = if receiver == fee_payer {
2409 &fee_payer_account
2410 } else {
2411 &receiver_account
2412 };
2413
2414 let receiver_not_present = {
2415 let id = receiver_account.id();
2416 if id.is_empty() {
2417 true
2418 } else if receiver == id {
2419 false
2420 } else {
2421 panic!("bad receiver account ID")
2422 }
2423 };
2424
2425 let source_account = if source == fee_payer {
2426 &fee_payer_account
2427 } else {
2428 &source_account
2429 };
2430
2431 let source_not_present = {
2432 let id = source_account.id();
2433 if id.is_empty() {
2434 true
2435 } else if source == id {
2436 false
2437 } else {
2438 panic!("bad source account ID")
2439 }
2440 };
2441
2442 Failure {
2443 predicate_failed,
2444 source_not_present,
2445 receiver_not_present,
2446 amount_insufficient_to_create: false,
2447 token_cannot_create: false,
2448 source_insufficient_balance: false,
2449 source_minimum_balance_violation: false,
2450 source_bad_timing: false,
2451 }
2452 }
2453 Payment => {
2454 let receiver_account = if receiver == fee_payer {
2455 &fee_payer_account
2456 } else {
2457 &receiver_account
2458 };
2459
2460 let receiver_needs_creating = {
2461 let id = receiver_account.id();
2462 if id.is_empty() {
2463 true
2464 } else if id == receiver {
2465 false
2466 } else {
2467 panic!("bad receiver account ID");
2468 }
2469 };
2470
2471 let token_is_default = true;
2472 let token_cannot_create = receiver_needs_creating && !token_is_default;
2473
2474 let amount_insufficient_to_create = {
2475 let creation_amount =
2476 currency::Amount::from_u64(constraint_constants().account_creation_fee);
2477 receiver_needs_creating
2478 && payload.body.amount.checked_sub(&creation_amount).is_none()
2479 };
2480
2481 let fee_payer_is_source = fee_payer == source;
2482 let source_account = if fee_payer_is_source {
2483 &fee_payer_account
2484 } else {
2485 &source_account
2486 };
2487
2488 let source_not_present = {
2489 let id = source_account.id();
2490 if id.is_empty() {
2491 true
2492 } else if source == id {
2493 false
2494 } else {
2495 panic!("bad source account ID");
2496 }
2497 };
2498
2499 let source_insufficient_balance = !fee_payer_is_source
2500 && if source == receiver {
2501 receiver_needs_creating
2502 } else {
2503 source_account.balance.to_amount() < payload.body.amount
2504 };
2505
2506 let timing_or_error =
2507 validate_timing(source_account, payload.body.amount, &txn_global_slot);
2508
2509 let source_minimum_balance_violation = matches!(
2510 timing_error_to_user_command_status(timing_or_error.clone()),
2511 Err(TransactionFailure::SourceMinimumBalanceViolation),
2512 );
2513
2514 let source_bad_timing = !fee_payer_is_source
2515 && !source_insufficient_balance
2516 && timing_or_error.is_err();
2517
2518 Failure {
2519 predicate_failed,
2520 source_not_present,
2521 receiver_not_present: false,
2522 amount_insufficient_to_create,
2523 token_cannot_create,
2524 source_insufficient_balance,
2525 source_minimum_balance_violation,
2526 source_bad_timing,
2527 }
2528 }
2529 }
2530 }
2531 }
2532
2533 pub fn checked_legacy_hash(
2534 param: &LazyParam,
2535 inputs: legacy::Inputs<Fp>,
2536 w: &mut Witness<Fp>,
2537 ) -> Fp {
2538 use ::poseidon::{fp_legacy::params, PlonkSpongeConstantsLegacy as Constants};
2539
2540 let initial_state: [Fp; 3] = param.state();
2541 let mut sponge =
2542 poseidon::Sponge::<Fp, Constants>::new_with_state_params(initial_state, params());
2543 sponge.absorb(&inputs.to_fields(), w);
2544 sponge.squeeze(w)
2545 }
2546
2547 pub fn checked_hash(param: &LazyParam, inputs: &[Fp], w: &mut Witness<Fp>) -> Fp {
2548 let initial_state: [Fp; 3] = param.state();
2549 let mut sponge = poseidon::Sponge::<Fp>::new_with_state(initial_state);
2550 sponge.absorb(inputs, w);
2551 sponge.squeeze(w)
2552 }
2553
2554 pub fn checked_hash3(param: &LazyParam, inputs: &[Fp], w: &mut Witness<Fp>) -> Fp {
2555 let initial_state: [Fp; 3] = param.state();
2556 let mut sponge = poseidon::Sponge::<Fp>::new_with_state(initial_state);
2557 sponge.absorb3(inputs, w);
2558 sponge.squeeze(w)
2559 }
2560
2561 fn checked_legacy_signature_hash(
2562 mut inputs: legacy::Inputs<Fp>,
2563 signer: &PubKey,
2564 signature: &Signature,
2565 w: &mut Witness<Fp>,
2566 ) -> [bool; 255] {
2567 let GroupAffine::<Fp> { x: px, y: py, .. } = signer.point();
2568 let Signature { rx, s: _ } = signature;
2569
2570 inputs.append_field(*px);
2571 inputs.append_field(*py);
2572 inputs.append_field(*rx);
2573 let signature_prefix = openmina_core::NetworkConfig::global().legacy_signature_prefix;
2574 let hash = checked_legacy_hash(signature_prefix, inputs, w);
2575
2576 w.exists(field_to_bits::<_, 255>(hash))
2577 }
2578
2579 pub fn checked_legacy_signature_verify(
2580 shifted: &InnerCurve<Fp>,
2581 signer: &PubKey,
2582 signature: &Signature,
2583 inputs: legacy::Inputs<Fp>,
2584 w: &mut Witness<Fp>,
2585 ) -> Boolean {
2586 let hash = checked_legacy_signature_hash(inputs, signer, signature, w);
2587 checked_signature_verify_impl(shifted, signer, signature, &hash, w)
2588 }
2589
2590 pub fn checked_signature_verify_impl(
2591 shifted: &InnerCurve<Fp>,
2592 signer: &PubKey,
2593 signature: &Signature,
2594 hash: &[bool; 255],
2595 w: &mut Witness<Fp>,
2596 ) -> Boolean {
2597 let public_key = {
2599 let GroupAffine::<Fp> { x, y, .. } = signer.point();
2600 let y = w.exists(y.neg()); make_group::<Fp>(*x, y)
2602 };
2603
2604 let e_pk = scale_non_constant::<Fp, 255>(public_key, hash, shifted, w);
2605
2606 let Signature { rx: _, s } = signature;
2607 let bits: [bool; 255] = field_to_bits::<_, 255>(*s);
2608 let one: GroupAffine<Fp> = InnerCurve::<Fp>::one().to_affine();
2609 let s_g_e_pk = scale_known(one, &bits, &InnerCurve::of_affine(e_pk), w);
2610
2611 let GroupAffine::<Fp> { x: rx, y: ry, .. } = {
2612 let neg_shifted = shifted.to_affine().neg();
2613 w.exists(neg_shifted.y);
2614 w.add_fast(neg_shifted, s_g_e_pk)
2615 };
2616
2617 let y_even = is_even(ry, w);
2618 let r_correct = field::equal(signature.rx, rx, w);
2619
2620 y_even.and(&r_correct, w)
2621 }
2622
2623 fn checked_chunked_signature_hash(
2624 mut inputs: Inputs,
2625 signer: &PubKey,
2626 signature: &Signature,
2627 w: &mut Witness<Fp>,
2628 ) -> [bool; 255] {
2629 let GroupAffine::<Fp> { x: px, y: py, .. } = signer.point();
2630 let Signature { rx, s: _ } = signature;
2631
2632 inputs.append_field(*px);
2633 inputs.append_field(*py);
2634 inputs.append_field(*rx);
2635 let signature_prefix = openmina_core::NetworkConfig::global().signature_prefix;
2636 let hash = checked_hash(signature_prefix, &inputs.to_fields(), w);
2637
2638 w.exists(field_to_bits::<_, 255>(hash))
2639 }
2640
2641 pub fn checked_chunked_signature_verify(
2642 shifted: &InnerCurve<Fp>,
2643 signer: &PubKey,
2644 signature: &Signature,
2645 inputs: Inputs,
2646 w: &mut Witness<Fp>,
2647 ) -> Boolean {
2648 let hash = checked_chunked_signature_hash(inputs, signer, signature, w);
2649 checked_signature_verify_impl(shifted, signer, signature, &hash, w)
2650 }
2651
2652 fn check_signature(
2653 shifted: &InnerCurve<Fp>,
2654 payload: &TransactionUnionPayload,
2655 is_user_command: Boolean,
2656 signer: &PubKey,
2657 signature: &Signature,
2658 w: &mut Witness<Fp>,
2659 ) {
2660 let inputs = payload.to_checked_legacy_input_owned(w);
2661 let verifies = checked_legacy_signature_verify(shifted, signer, signature, inputs, w);
2662 Boolean::assert_any(&[is_user_command.neg(), verifies][..], w);
2663 }
2664
2665 fn add_burned_tokens<F: FieldWitness>(
2666 acc_burned_tokens: CheckedAmount<F>,
2667 amount: CheckedAmount<F>,
2668 is_coinbase_or_fee_transfer: Boolean,
2669 update_account: Boolean,
2670 is_const_add_flagged: bool,
2671 w: &mut Witness<F>,
2672 ) -> CheckedAmount<F> {
2673 let accumulate_burned_tokens =
2674 Boolean::all(&[is_coinbase_or_fee_transfer, update_account.neg()], w);
2675
2676 let (amt, overflow) = if is_const_add_flagged {
2677 acc_burned_tokens.const_add_flagged(&amount, w)
2678 } else {
2679 acc_burned_tokens.add_flagged(&amount, w)
2680 };
2681
2682 Boolean::assert_any(&[accumulate_burned_tokens.neg(), overflow.neg()], w);
2683
2684 w.exists_no_check(match accumulate_burned_tokens {
2685 Boolean::True => amt,
2686 Boolean::False => acc_burned_tokens,
2687 })
2688 }
2689
2690 pub fn checked_min_balance_at_slot<F: FieldWitness>(
2691 global_slot: &CheckedSlot<F>,
2692 cliff_time: &CheckedSlot<F>,
2693 cliff_amount: &CheckedAmount<F>,
2694 vesting_period: &CheckedSlotSpan<F>,
2695 vesting_increment: &CheckedAmount<F>,
2696 initial_minimum_balance: &CheckedBalance<F>,
2697 w: &mut Witness<F>,
2698 ) -> CheckedBalance<F> {
2699 let before_cliff = global_slot.less_than(cliff_time, w);
2700
2701 let else_value = {
2702 let (_, slot_diff) = global_slot.diff_or_zero(cliff_time, w);
2703
2704 let cliff_decrement = cliff_amount;
2705 let min_balance_less_cliff_decrement =
2706 initial_minimum_balance.sub_amount_or_zero(cliff_decrement, w);
2707
2708 let (num_periods, _) = slot_diff.div_mod(vesting_period, w);
2709
2710 let vesting_decrement = CheckedAmount::from_field(field::mul(
2711 num_periods.to_field(),
2712 vesting_increment.to_field(),
2713 w,
2714 ));
2715
2716 min_balance_less_cliff_decrement.sub_amount_or_zero(&vesting_decrement, w)
2717 };
2718
2719 w.exists_no_check(match before_cliff {
2720 Boolean::True => *initial_minimum_balance,
2721 Boolean::False => else_value,
2722 })
2723 }
2724
2725 pub fn check_timing<F: FieldWitness, Fun>(
2726 account: &Account,
2727 txn_amount: Option<&CheckedAmount<F>>,
2728 txn_global_slot: CheckedSlot<F>,
2729 mut timed_balance_check: Fun,
2730 w: &mut Witness<F>,
2731 ) -> (CheckedBalance<F>, Timing)
2732 where
2733 Fun: FnMut(Boolean, &mut Witness<F>),
2734 {
2735 let TimingAsRecordChecked {
2736 is_timed,
2737 initial_minimum_balance,
2738 cliff_time,
2739 cliff_amount,
2740 vesting_period,
2741 vesting_increment,
2742 } = account.timing.to_record_checked::<F>();
2743
2744 let curr_min_balance = checked_min_balance_at_slot(
2745 &txn_global_slot,
2746 &cliff_time,
2747 &cliff_amount,
2748 &vesting_period,
2749 &vesting_increment,
2750 &initial_minimum_balance,
2751 w,
2752 );
2753
2754 let account_balance = account.balance.to_checked();
2755 let proposed_balance = match txn_amount {
2756 Some(txn_amount) => {
2757 let (proposed_balance, _underflow) =
2758 account_balance.sub_amount_flagged(txn_amount, w);
2759 proposed_balance
2760 }
2761 None => account_balance,
2762 };
2763
2764 let sufficient_timed_balance = proposed_balance.gte(&curr_min_balance, w);
2765
2766 {
2767 let ok = Boolean::any(&[is_timed.neg(), sufficient_timed_balance], w);
2768 timed_balance_check(ok, w);
2769 }
2770
2771 let is_timed_balance_zero = field::equal(curr_min_balance.to_field(), F::zero(), w);
2772
2773 let is_untimed = is_timed.neg().or(&is_timed_balance_zero, w);
2774
2775 let timing = w.exists_no_check(match is_untimed {
2776 Boolean::True => Timing::Untimed,
2777 Boolean::False => account.timing.clone(),
2778 });
2779
2780 (curr_min_balance, timing)
2781 }
2782
2783 #[allow(unused_assignments)]
2784 fn apply_tagged_transaction(
2785 shifted: &InnerCurve<Fp>,
2786 _fee_payment_root: Fp,
2787 global_slot: currency::Slot,
2788 pending_coinbase_init: &pending_coinbase::Stack,
2789 pending_coinbase_stack_before: &pending_coinbase::Stack,
2790 pending_coinbase_after: &pending_coinbase::Stack,
2791 state_body: &ProtocolStateBody,
2792 tx: &TransactionUnion,
2793 sparse_ledger: &SparseLedger,
2794 w: &mut Witness<Fp>,
2795 ) -> anyhow::Result<(
2796 Fp,
2797 CheckedSigned<Fp, CheckedAmount<Fp>>,
2798 CheckedSigned<Fp, CheckedAmount<Fp>>,
2799 )> {
2800 let TransactionUnion {
2801 payload,
2802 signer,
2803 signature,
2804 } = tx;
2805
2806 let global_slot = global_slot.to_checked();
2807
2808 let mut ledger = sparse_ledger.copy_content();
2809
2810 let tag = payload.body.tag.clone();
2811 let is_user_command = tag.is_user_command();
2812
2813 check_signature(shifted, payload, is_user_command, signer, signature, w);
2814
2815 let _signer_pk = compress_var(signer.point(), w);
2816
2817 let is_payment = tag.is_payment();
2818 let is_stake_delegation = tag.is_stake_delegation();
2819 let is_fee_transfer = tag.is_fee_transfer();
2820 let is_coinbase = tag.is_coinbase();
2821
2822 let fee_token = &payload.common.fee_token;
2823 let fee_token_default = field::equal(fee_token.0, TokenId::default().0, w);
2824
2825 let token = &payload.body.token_id;
2826 let token_default = field::equal(token.0, TokenId::default().0, w);
2827
2828 Boolean::assert_any(
2829 &[
2830 fee_token_default,
2831 is_payment,
2832 is_stake_delegation,
2833 is_fee_transfer,
2834 ],
2835 w,
2836 );
2837
2838 Boolean::assert_any(
2839 &[
2840 is_payment,
2841 is_stake_delegation,
2842 is_fee_transfer,
2843 is_coinbase,
2844 ],
2845 w,
2846 );
2847
2848 let current_global_slot = global_slot;
2849 let user_command_failure =
2850 user_command_failure::compute_as_prover(current_global_slot, tx, &ledger, w);
2851
2852 let user_command_fails = Boolean::any(&user_command_failure.to_list(), w);
2853 let fee = payload.common.fee.to_checked();
2854 let receiver = AccountId::create(payload.body.receiver_pk.clone(), token.clone());
2855 let source = AccountId::create(payload.body.source_pk.clone(), token.clone());
2856 let nonce = payload.common.nonce.to_checked();
2857 let fee_payer = AccountId::create(payload.common.fee_payer_pk.clone(), fee_token.clone());
2858
2859 fee_payer.checked_equal(&source, w);
2860 current_global_slot.lte(&payload.common.valid_until.to_checked(), w);
2861
2862 let state_body_hash = state_body.checked_hash_with_param(&MINA_PROTO_STATE_BODY, w);
2863
2864 let pending_coinbase_stack_with_state =
2865 pending_coinbase_init.checked_push_state(state_body_hash, current_global_slot, w);
2866
2867 let computed_pending_coinbase_stack_after = {
2868 let coinbase = Coinbase {
2869 receiver: receiver.public_key.clone(),
2870 amount: payload.body.amount,
2871 fee_transfer: None,
2872 };
2873
2874 let stack_prime = pending_coinbase_stack_with_state.checked_push_coinbase(coinbase, w);
2875
2876 w.exists(match is_coinbase {
2877 Boolean::True => stack_prime,
2878 Boolean::False => pending_coinbase_stack_with_state.clone(),
2879 })
2880 };
2881
2882 let _correct_coinbase_target_stack =
2883 computed_pending_coinbase_stack_after.equal_var(pending_coinbase_after, w);
2884
2885 let _valid_init_state = {
2886 let equal_source = pending_coinbase_init.equal_var(pending_coinbase_stack_before, w);
2887
2888 let equal_source_with_state =
2889 pending_coinbase_stack_with_state.equal_var(pending_coinbase_stack_before, w);
2890
2891 equal_source.or(&equal_source_with_state, w)
2892 };
2893
2894 Boolean::assert_any(&[is_user_command, user_command_fails.neg()], w);
2895
2896 let _predicate_result = {
2897 let is_own_account = checked_equal_compressed_key(
2898 &payload.common.fee_payer_pk,
2899 &payload.body.source_pk,
2900 w,
2901 );
2902 let predicate_result = Boolean::False;
2903
2904 is_own_account.const_or(&predicate_result)
2905 };
2906
2907 let account_creation_amount =
2908 currency::Amount::from_u64(constraint_constants().account_creation_fee).to_checked();
2909 let is_zero_fee = fee.equal(&CheckedFee::zero(), w);
2910
2911 let is_coinbase_or_fee_transfer = is_user_command.neg();
2912
2913 let can_create_fee_payer_account = {
2914 let fee_may_be_charged = token_default.or(&is_zero_fee, w);
2915 is_coinbase_or_fee_transfer.and(&fee_may_be_charged, w)
2916 };
2917
2918 let mut burned_tokens = CheckedAmount::<Fp>::zero();
2919 let zero_fee = CheckedSigned::zero();
2920 let mut new_account_fees = zero_fee.clone();
2921
2922 let root_after_fee_payer_update = {
2923 let index = ledger.find_index_exn(fee_payer.clone());
2924 w.exists(index.to_bits());
2925
2926 let account = ledger.get_exn(&index);
2927 let path = ledger.path_exn(index.clone());
2928
2929 let (account, path) = w.exists((account, path));
2930 checked_verify_merkle_path(&account, &path, w);
2931
2932 let is_empty_and_writeable = {
2934 let is_writable = can_create_fee_payer_account;
2935 let account_already_there = account.id().checked_equal(&fee_payer, w);
2936 let account_not_there = checked_equal_compressed_key_const_and(
2937 &account.public_key,
2938 &CompressedPubKey::empty(),
2939 w,
2940 );
2941 let not_there_but_writeable = account_not_there.and(&is_writable, w);
2942 Boolean::assert_any(&[account_already_there, not_there_but_writeable], w);
2943 not_there_but_writeable
2944 };
2945
2946 let next = {
2948 let next_nonce = match is_user_command {
2950 Boolean::True => account.nonce.incr().to_checked::<Fp>(),
2951 Boolean::False => account.nonce.to_checked(),
2952 };
2953
2954 let account_nonce = account.nonce.to_checked();
2955 let nonce_matches = nonce.equal(&account_nonce, w);
2956 Boolean::assert_any(&[is_user_command.neg(), nonce_matches], w);
2957
2958 let current = &account.receipt_chain_hash;
2959 let r = checked_cons_signed_command_payload(payload, current.clone(), w);
2960 let receipt_chain_hash = w.exists(match is_user_command {
2961 Boolean::True => r,
2962 Boolean::False => current.clone(),
2963 });
2964
2965 let _permitted_to_access = account.checked_has_permission_to(
2966 PermsConst {
2967 and_const: false,
2968 or_const: false,
2969 },
2970 Some(is_user_command),
2971 PermissionTo::Access,
2972 w,
2973 );
2974 let permitted_to_increment_nonce = account.checked_has_permission_to(
2975 PermsConst {
2976 and_const: true,
2977 or_const: false,
2978 },
2979 None,
2980 PermissionTo::IncrementNonce,
2981 w,
2982 );
2983 let permitted_to_send = account.checked_has_permission_to(
2984 PermsConst {
2985 and_const: true,
2986 or_const: false,
2987 },
2988 None,
2989 PermissionTo::Send,
2990 w,
2991 );
2992 let permitted_to_receive = account.checked_has_permission_to(
2993 PermsConst {
2994 and_const: true,
2995 or_const: true,
2996 },
2997 None,
2998 PermissionTo::Receive,
2999 w,
3000 );
3001
3002 Boolean::assert_any(&[is_user_command.neg(), permitted_to_increment_nonce], w);
3003 Boolean::assert_any(&[is_user_command.neg(), permitted_to_send], w);
3004
3005 let update_account = {
3006 let receiving_allowed =
3007 Boolean::all(&[is_coinbase_or_fee_transfer, permitted_to_receive], w);
3008 Boolean::any(&[is_user_command, receiving_allowed], w)
3009 };
3010
3011 let is_empty_and_writeable =
3012 Boolean::all(&[is_empty_and_writeable, is_zero_fee.neg()], w);
3013
3014 let should_pay_to_create = is_empty_and_writeable;
3015
3016 let amount = {
3017 let fee_payer_amount = {
3018 let sgn = match is_user_command {
3019 Boolean::True => Sgn::Neg,
3020 Boolean::False => Sgn::Pos,
3021 };
3022 CheckedSigned::create(
3023 CheckedAmount::of_fee(&fee),
3024 CircuitVar::Constant(sgn),
3025 None,
3026 )
3027 };
3028
3029 let account_creation_fee = {
3030 let magnitude = if should_pay_to_create.as_bool() {
3032 account_creation_amount
3033 } else {
3034 CheckedAmount::zero()
3035 };
3036 CheckedSigned::create(magnitude, CircuitVar::Constant(Sgn::Neg), None)
3037 };
3038
3039 new_account_fees = account_creation_fee.clone();
3040
3041 account_creation_fee.set_value(); fee_payer_amount.add(&account_creation_fee, w)
3043 };
3044
3045 {
3046 let amt = add_burned_tokens::<Fp>(
3047 burned_tokens,
3048 CheckedAmount::of_fee(&fee),
3049 is_coinbase_or_fee_transfer,
3050 update_account,
3051 true,
3052 w,
3053 );
3054 burned_tokens = amt;
3055 }
3056
3057 let txn_global_slot = current_global_slot;
3058 let timing = {
3059 let txn_amount = w.exists_no_check(match amount.sgn.value() {
3060 Sgn::Neg => amount.magnitude,
3061 Sgn::Pos => CheckedAmount::zero(),
3062 });
3063
3064 let timed_balance_check = |_ok: Boolean, _w: &mut Witness<Fp>| {};
3065
3066 let (_, timing) = check_timing(
3067 &account,
3068 Some(&txn_amount),
3069 txn_global_slot,
3070 timed_balance_check,
3071 w,
3072 );
3073
3074 w.exists_no_check(match update_account {
3075 Boolean::True => timing,
3076 Boolean::False => account.timing.clone(),
3077 })
3078 };
3079
3080 let balance = {
3081 let account_balance = account.balance.to_checked();
3082 let updated_balance = account_balance.add_signed_amount(amount, w);
3083 w.exists_no_check(match update_account {
3084 Boolean::True => updated_balance,
3085 Boolean::False => account_balance,
3086 })
3087 };
3088 let public_key = w.exists(match is_empty_and_writeable {
3089 Boolean::True => fee_payer.public_key.clone(),
3090 Boolean::False => account.public_key.clone(),
3091 });
3092 let token_id = w.exists(match is_empty_and_writeable {
3093 Boolean::True => fee_payer.token_id.clone(),
3094 Boolean::False => account.token_id.clone(),
3095 });
3096 let delegate = w.exists(match is_empty_and_writeable {
3097 Boolean::True => fee_payer.public_key.clone(),
3098 Boolean::False => account
3099 .delegate
3100 .clone()
3101 .unwrap_or_else(CompressedPubKey::empty),
3102 });
3103
3104 Box::new(Account {
3105 public_key,
3106 token_id,
3107 token_symbol: account.token_symbol,
3108 balance: balance.to_inner(),
3109 nonce: next_nonce.to_inner(),
3110 receipt_chain_hash,
3111 delegate: if delegate == CompressedPubKey::empty() {
3112 None
3113 } else {
3114 Some(delegate)
3115 },
3116 voting_for: account.voting_for,
3117 timing,
3118 permissions: account.permissions,
3119 zkapp: account.zkapp,
3120 })
3121 };
3122
3123 ledger.set_exn(index, next.clone());
3124 checked_verify_merkle_path(&next, &path, w)
3125 };
3126
3127 let receiver_increase = {
3128 let base_amount = {
3129 let zero_transfer = is_stake_delegation;
3130 w.exists_no_check(match zero_transfer {
3131 Boolean::True => CheckedAmount::zero(),
3132 Boolean::False => payload.body.amount.to_checked(),
3133 })
3134 };
3135
3136 let coinbase_receiver_fee = w.exists_no_check(match is_coinbase {
3137 Boolean::True => CheckedAmount::of_fee(&fee),
3138 Boolean::False => CheckedAmount::zero(),
3139 });
3140
3141 base_amount.sub(&coinbase_receiver_fee, w)
3142 };
3143
3144 let mut receiver_overflow = Boolean::False;
3145 let mut receiver_balance_update_permitted = Boolean::True;
3146
3147 let _root_after_receiver_update = {
3148 let index = ledger.find_index_exn(receiver.clone());
3149 w.exists(index.to_bits());
3150
3151 let account = ledger.get_exn(&index);
3152 let path = ledger.path_exn(index.clone());
3153
3154 let (account, path) = w.exists((account, path));
3155 checked_verify_merkle_path(&account, &path, w);
3156
3157 let is_empty_and_writeable = {
3159 let aid = &receiver;
3160 let account_already_there = account.id().checked_equal(aid, w);
3161 let account_not_there = checked_equal_compressed_key_const_and(
3162 &account.public_key,
3163 &CompressedPubKey::empty(),
3164 w,
3165 );
3166
3167 Boolean::assert_any(&[account_already_there, account_not_there], w);
3168
3169 account_not_there
3170 };
3171
3172 let next = {
3174 let permitted_to_access = account.checked_has_permission_to(
3175 PermsConst {
3176 and_const: true,
3177 or_const: true,
3178 },
3179 Some(Boolean::False),
3180 PermissionTo::Access,
3181 w,
3182 );
3183 let permitted_to_receive = account
3184 .checked_has_permission_to(
3185 PermsConst {
3186 and_const: true,
3187 or_const: true,
3188 },
3189 None,
3190 PermissionTo::Receive,
3191 w,
3192 )
3193 .and(&permitted_to_access, w);
3194
3195 let payment_or_internal_command =
3196 Boolean::any(&[is_payment, is_coinbase_or_fee_transfer], w);
3197
3198 let update_account = Boolean::any(
3199 &[payment_or_internal_command.neg(), permitted_to_receive],
3200 w,
3201 )
3202 .and(&permitted_to_access, w);
3203
3204 receiver_balance_update_permitted = permitted_to_receive;
3205
3206 let is_empty_failure = {
3207 let must_not_be_empty = is_stake_delegation;
3208 is_empty_and_writeable.and(&must_not_be_empty, w)
3209 };
3210
3211 let is_empty_and_writeable =
3214 Boolean::all(&[is_empty_and_writeable, is_empty_failure.neg()], w);
3215
3216 let should_pay_to_create = is_empty_and_writeable;
3217
3218 {
3219 let token_should_not_create = should_pay_to_create.and(&token_default.neg(), w);
3220
3221 let _token_cannot_create = token_should_not_create.and(&is_user_command, w);
3222 }
3223
3224 let balance = {
3225 let receiver_amount = {
3226 let account_creation_fee = match should_pay_to_create {
3227 Boolean::True => account_creation_amount,
3228 Boolean::False => CheckedAmount::zero(),
3229 };
3230
3231 let account_creation_fee_neg =
3232 CheckedSigned::of_unsigned(account_creation_fee).negate();
3233
3234 account_creation_fee_neg.set_value(); new_account_fees.set_value(); let new_account_fees_total =
3237 account_creation_fee_neg.add(&new_account_fees, w);
3238 new_account_fees = new_account_fees_total;
3239
3240 let (amount_for_new_account, _underflow) =
3241 receiver_increase.sub_flagged(&account_creation_fee, w);
3242
3243 w.exists_no_check(match user_command_fails {
3244 Boolean::True => CheckedAmount::zero(),
3245 Boolean::False => amount_for_new_account,
3246 })
3247 };
3248
3249 let account_balance = account.balance.to_checked();
3250 let (balance, overflow) =
3251 account_balance.add_amount_flagged(&receiver_amount, w);
3252
3253 Boolean::assert_any(&[is_user_command, overflow.neg()], w);
3254
3255 receiver_overflow = overflow;
3256
3257 w.exists_no_check(match overflow {
3258 Boolean::True => account_balance,
3259 Boolean::False => balance,
3260 })
3261 };
3262
3263 {
3264 let amt = add_burned_tokens::<Fp>(
3265 burned_tokens,
3266 receiver_increase,
3267 is_coinbase_or_fee_transfer,
3268 permitted_to_receive,
3269 false,
3270 w,
3271 );
3272 burned_tokens = amt;
3273 }
3274
3275 let user_command_fails = receiver_overflow.or(&user_command_fails, w);
3276
3277 let is_empty_and_writeable = Boolean::all(
3278 &[
3279 is_empty_and_writeable,
3280 user_command_fails.neg(),
3281 update_account,
3282 ],
3283 w,
3284 );
3285
3286 let balance = w.exists_no_check(match update_account {
3287 Boolean::True => balance,
3288 Boolean::False => account.balance.to_checked(),
3289 });
3290
3291 let may_delegate = is_empty_and_writeable.and(&token_default, w);
3292
3293 let delegate = w.exists(match may_delegate {
3294 Boolean::True => receiver.public_key.clone(),
3295 Boolean::False => account
3296 .delegate
3297 .clone()
3298 .unwrap_or_else(CompressedPubKey::empty),
3299 });
3300
3301 let public_key = w.exists(match is_empty_and_writeable {
3302 Boolean::True => receiver.public_key.clone(),
3303 Boolean::False => account.public_key.clone(),
3304 });
3305
3306 let token_id = w.exists(match is_empty_and_writeable {
3307 Boolean::True => token.clone(),
3308 Boolean::False => account.token_id.clone(),
3309 });
3310
3311 Box::new(Account {
3312 public_key,
3313 token_id,
3314 token_symbol: account.token_symbol,
3315 balance: balance.to_inner(),
3316 nonce: account.nonce,
3317 receipt_chain_hash: account.receipt_chain_hash,
3318 delegate: if delegate == CompressedPubKey::empty() {
3319 None
3320 } else {
3321 Some(delegate)
3322 },
3323 voting_for: account.voting_for,
3324 timing: account.timing,
3325 permissions: account.permissions,
3326 zkapp: account.zkapp,
3327 })
3328 };
3329
3330 ledger.set_exn(index, next.clone());
3331 checked_verify_merkle_path(&next, &path, w)
3332 };
3333
3334 let user_command_fails = receiver_overflow.or(&user_command_fails, w);
3335 let fee_payer_is_source = fee_payer.checked_equal(&source, w);
3336
3337 let root_after_source_update = {
3338 let index = ledger.find_index_exn(source.clone());
3339 w.exists(index.to_bits());
3340
3341 let account = ledger.get_exn(&index);
3342 let path = ledger.path_exn(index.clone());
3343
3344 let (account, path) = w.exists((account, path));
3345 checked_verify_merkle_path(&account, &path, w);
3346
3347 let _is_empty_and_writeable = {
3349 let is_writable = user_command_failure.source_not_present.to_boolean();
3350 let account_already_there = account.id().checked_equal(&source, w);
3351 let account_not_there = checked_equal_compressed_key_const_and(
3352 &account.public_key,
3353 &CompressedPubKey::empty(),
3354 w,
3355 );
3356 let not_there_but_writeable = account_not_there.and(&is_writable, w);
3357 Boolean::assert_any(&[account_already_there, not_there_but_writeable], w);
3358 not_there_but_writeable
3359 };
3360
3361 let next = {
3363 let bool_to_field = |b: bool| b.to_boolean().to_field::<Fp>();
3364 let _num_failures = field::const_add(
3365 bool_to_field(user_command_failure.source_insufficient_balance),
3366 bool_to_field(user_command_failure.source_bad_timing),
3367 );
3368 let _not_fee_payer_is_source = fee_payer_is_source.neg();
3369
3370 let permitted_to_access = account.checked_has_permission_to(
3371 PermsConst {
3372 and_const: false,
3373 or_const: false,
3374 },
3375 Some(is_user_command),
3376 PermissionTo::Access,
3377 w,
3378 );
3379 let permitted_to_update_delegate = account.checked_has_permission_to(
3380 PermsConst {
3381 and_const: true,
3382 or_const: false,
3383 },
3384 None,
3385 PermissionTo::SetDelegate,
3386 w,
3387 );
3388 let permitted_to_send = account.checked_has_permission_to(
3389 PermsConst {
3390 and_const: true,
3391 or_const: false,
3392 },
3393 None,
3394 PermissionTo::Send,
3395 w,
3396 );
3397 let permitted_to_receive = account.checked_has_permission_to(
3398 PermsConst {
3399 and_const: true,
3400 or_const: true,
3401 },
3402 None,
3403 PermissionTo::Receive,
3404 w,
3405 );
3406
3407 let payment_permitted = Boolean::all(
3408 &[
3409 is_payment,
3410 permitted_to_access,
3411 permitted_to_send,
3412 receiver_balance_update_permitted,
3413 ],
3414 w,
3415 );
3416
3417 let update_account = {
3418 let delegation_permitted =
3419 Boolean::all(&[is_stake_delegation, permitted_to_update_delegate], w);
3420
3421 let fee_receiver_update_permitted =
3422 Boolean::all(&[is_coinbase_or_fee_transfer, permitted_to_receive], w);
3423
3424 Boolean::any(
3425 &[
3426 payment_permitted,
3427 delegation_permitted,
3428 fee_receiver_update_permitted,
3429 ],
3430 w,
3431 )
3432 .and(&permitted_to_access, w)
3433 };
3434
3435 let amount = w.exists_no_check(match payment_permitted {
3436 Boolean::True => payload.body.amount.to_checked(),
3437 Boolean::False => CheckedAmount::zero(),
3438 });
3439
3440 let txn_global_slot = current_global_slot;
3441
3442 let timing = {
3443 let timed_balance_check = |ok: Boolean, w: &mut Witness<Fp>| {
3444 let _not_ok = ok.neg().and(
3445 &user_command_failure
3446 .source_insufficient_balance
3447 .to_boolean(),
3448 w,
3449 );
3450 };
3451
3452 let (_, timing) = check_timing(
3453 &account,
3454 Some(&amount),
3455 txn_global_slot,
3456 timed_balance_check,
3457 w,
3458 );
3459
3460 w.exists_no_check(match update_account {
3461 Boolean::True => timing,
3462 Boolean::False => account.timing.clone(),
3463 })
3464 };
3465
3466 let (balance, _underflow) =
3467 account.balance.to_checked().sub_amount_flagged(&amount, w);
3468
3469 let delegate = {
3470 let may_delegate = Boolean::all(&[is_stake_delegation, update_account], w);
3471
3472 w.exists(match may_delegate {
3473 Boolean::True => receiver.public_key,
3474 Boolean::False => account
3475 .delegate
3476 .clone()
3477 .unwrap_or_else(CompressedPubKey::empty),
3478 })
3479 };
3480
3481 Box::new(Account {
3482 public_key: account.public_key,
3483 token_id: account.token_id,
3484 token_symbol: account.token_symbol,
3485 balance: balance.to_inner(),
3486 nonce: account.nonce,
3487 receipt_chain_hash: account.receipt_chain_hash,
3488 delegate: if delegate == CompressedPubKey::empty() {
3489 None
3490 } else {
3491 Some(delegate)
3492 },
3493 voting_for: account.voting_for,
3494 timing,
3495 permissions: account.permissions,
3496 zkapp: account.zkapp,
3497 })
3498 };
3499
3500 ledger.set_exn(index, next.clone());
3501 checked_verify_merkle_path(&next, &path, w)
3502 };
3503
3504 let fee_excess = {
3505 let then_value = CheckedSigned::of_unsigned(CheckedAmount::zero());
3506
3507 let else_value = {
3508 let amount_fee = CheckedAmount::of_fee(&payload.common.fee.to_checked());
3509
3510 let user_command_excess = CheckedSigned::of_unsigned(amount_fee);
3511
3512 let (fee_transfer_excess, fee_transfer_excess_overflowed) = {
3513 let (magnitude, overflow) =
3514 payload.body.amount.to_checked().add_flagged(&amount_fee, w);
3515 (
3516 CheckedSigned::create(magnitude, CircuitVar::Constant(Sgn::Neg), None),
3517 overflow,
3518 )
3519 };
3520
3521 Boolean::assert_any(
3522 &[is_fee_transfer.neg(), fee_transfer_excess_overflowed.neg()],
3523 w,
3524 );
3525
3526 CheckedSigned::on_if(
3527 is_fee_transfer.var(),
3528 SignedAmountBranchParam {
3529 on_true: &fee_transfer_excess,
3530 on_false: &user_command_excess,
3531 },
3532 w,
3533 )
3534 };
3535
3536 CheckedSigned::on_if(
3537 is_coinbase.var(),
3538 SignedAmountBranchParam {
3539 on_true: &then_value,
3540 on_false: &else_value,
3541 },
3542 w,
3543 )
3544 };
3545
3546 let supply_increase = {
3547 let expected_supply_increase = CheckedSigned::on_if(
3548 is_coinbase.var(),
3549 SignedAmountBranchParam {
3550 on_true: &CheckedSigned::of_unsigned(payload.body.amount.to_checked()),
3551 on_false: &CheckedSigned::of_unsigned(CheckedAmount::zero()),
3552 },
3553 w,
3554 );
3555
3556 let (amt0, _overflow0) = expected_supply_increase
3557 .add_flagged(&CheckedSigned::of_unsigned(burned_tokens).negate(), w);
3558
3559 let new_account_fees_total = CheckedSigned::on_if(
3560 user_command_fails.var(),
3561 SignedAmountBranchParam {
3562 on_true: &zero_fee,
3563 on_false: &new_account_fees,
3564 },
3565 w,
3566 );
3567
3568 let (amt, _overflow) = amt0.add_flagged(&new_account_fees_total, w);
3569
3570 amt
3571 };
3572
3573 let final_root = w.exists_no_check(match user_command_fails {
3574 Boolean::True => root_after_fee_payer_update,
3575 Boolean::False => root_after_source_update,
3576 });
3577
3578 Ok((final_root, fee_excess, supply_increase))
3579 }
3580
3581 pub fn assert_equal_local_state<F: FieldWitness>(
3582 t1: &LocalState,
3583 t2: &LocalState,
3584 w: &mut Witness<F>,
3585 ) {
3586 t1.excess.to_checked::<F>().value(w);
3587 t2.excess.to_checked::<F>().value(w);
3588
3589 t1.supply_increase.to_checked::<F>().value(w);
3590 t2.supply_increase.to_checked::<F>().value(w);
3591 }
3592
3593 pub fn main(
3594 statement_with_sok: &Statement<SokDigest>,
3595 tx_witness: &v2::TransactionWitnessStableV2,
3596 w: &mut Witness<Fp>,
3597 ) -> anyhow::Result<()> {
3598 let tx: crate::scan_state::transaction_logic::Transaction =
3599 (&tx_witness.transaction).try_into()?;
3600 let tx = transaction_union_payload::TransactionUnion::of_transaction(&tx);
3601
3602 dummy_constraints(w);
3603 let shifted = create_shifted_inner_curve(w);
3604
3605 let tx = w.exists(&tx);
3606 let pending_coinbase_init: pending_coinbase::Stack =
3607 w.exists((&tx_witness.init_stack).try_into()?);
3608 let state_body: ProtocolStateBody = w.exists((&tx_witness.protocol_state_body).try_into()?);
3609 let global_slot: currency::Slot = w.exists((&tx_witness.block_global_slot).into());
3610
3611 let sparse_ledger: SparseLedger = (&tx_witness.first_pass_ledger).try_into()?;
3612
3613 let (_fee_payment_root_after, fee_excess, _supply_increase) = apply_tagged_transaction(
3614 &shifted,
3615 statement_with_sok.source.first_pass_ledger,
3616 currency::Slot::from_u32(global_slot.as_u32()),
3617 &pending_coinbase_init,
3618 &statement_with_sok.source.pending_coinbase_stack,
3619 &statement_with_sok.target.pending_coinbase_stack,
3620 &state_body,
3621 tx,
3622 &sparse_ledger,
3623 w,
3624 )?;
3625
3626 let _fee_excess = {
3627 let fee_excess_zero = {
3628 let fee_excess = w.exists(fee_excess.force_value());
3629 field::equal(fee_excess, Fp::zero(), w)
3630 };
3631
3632 let fee_token_l = w.exists_no_check(match fee_excess_zero {
3633 Boolean::True => TokenId::default(),
3634 Boolean::False => tx.payload.common.fee_token.clone(),
3635 });
3636
3637 CheckedFeeExcess {
3638 fee_token_l,
3639 fee_excess_l: fee_excess.to_fee(),
3640 fee_token_r: TokenId::default(),
3641 fee_excess_r: CheckedSigned::zero(),
3642 }
3643 };
3644
3645 assert_equal_local_state(
3646 &statement_with_sok.source.local_state,
3647 &statement_with_sok.target.local_state,
3648 w,
3649 );
3650
3651 {
3653 let supply_increase = statement_with_sok.supply_increase;
3654 w.exists_no_check(supply_increase.to_checked::<Fp>().force_value());
3655
3656 let FeeExcess {
3657 fee_token_l: _,
3658 fee_excess_l,
3659 fee_token_r: _,
3660 fee_excess_r,
3661 } = statement_with_sok.fee_excess;
3662
3663 w.exists_no_check(fee_excess_l.to_checked::<Fp>().force_value());
3664 w.exists_no_check(fee_excess_r.to_checked::<Fp>().force_value());
3665 }
3666
3667 Ok(())
3668 }
3669}
3670
3671pub fn get_messages_for_next_wrap_proof_padded() -> Vec<Fp> {
3672 let hash = messages_for_next_wrap_proof_padding();
3673 vec![hash, hash]
3674}
3675
3676pub fn messages_for_next_wrap_proof_padding() -> Fp {
3677 cache_one!(Fp, {
3678 let msg = MessagesForNextWrapProof {
3679 challenge_polynomial_commitment: InnerCurve::from(dummy_ipa_step_sg()),
3680 old_bulletproof_challenges: vec![], };
3682 let hash: [u64; 4] = msg.hash();
3683 Fp::try_from(BigInteger256::from_64x4(hash)).unwrap() })
3685}
3686
3687pub fn checked_hash2<F: FieldWitness>(inputs: &[F], w: &mut Witness<F>) -> F {
3688 let mut sponge = poseidon::Sponge::<F>::new();
3689 sponge.absorb2(inputs, w);
3690 sponge.squeeze(w)
3691}
3692
3693pub fn checked_hash3<F: FieldWitness>(inputs: &[F], w: &mut Witness<F>) -> F {
3694 let mut sponge = poseidon::Sponge::<F>::new();
3695 sponge.absorb(inputs, w);
3696 sponge.squeeze(w)
3697}
3698
3699pub struct StepMainProofState {
3700 pub unfinalized_proofs: Vec<Unfinalized>,
3701 pub messages_for_next_step_proof: Fp,
3702}
3703
3704pub struct StepMainStatement {
3705 pub proof_state: StepMainProofState,
3706 pub messages_for_next_wrap_proof: Vec<Fp>,
3707}
3708
3709#[derive(Clone, Debug)]
3710pub struct StepProofState {
3711 pub unfinalized_proofs: Vec<Unfinalized>,
3712 pub messages_for_next_step_proof: ReducedMessagesForNextStepProof,
3713}
3714
3715#[derive(Debug)]
3716pub struct StepStatement {
3717 pub proof_state: StepProofState,
3718 pub messages_for_next_wrap_proof: Vec<MessagesForNextWrapProof>,
3719}
3720
3721#[derive(Debug)]
3722pub struct StepStatementWithHash {
3723 pub proof_state: StepProofState,
3724 pub messages_for_next_wrap_proof: Vec<[u64; 4]>,
3725}
3726
3727#[derive(Clone, Debug)]
3728pub struct ReducedMessagesForNextStepProof {
3729 pub app_state: Rc<dyn ToFieldElementsDebug>,
3730 pub challenge_polynomial_commitments: Vec<InnerCurve<Fp>>,
3731 pub old_bulletproof_challenges: Vec<[Fp; 16]>,
3732}
3733
3734#[derive(Clone, Debug)]
3735pub struct MessagesForNextStepProof<'a> {
3736 pub app_state: Rc<dyn ToFieldElementsDebug>,
3737 pub dlog_plonk_index: &'a PlonkVerificationKeyEvals<Fp>,
3738 pub challenge_polynomial_commitments: Vec<InnerCurve<Fp>>,
3739 pub old_bulletproof_challenges: Vec<[Fp; 16]>,
3740}
3741
3742impl MessagesForNextStepProof<'_> {
3743 pub fn hash(&self) -> [u64; 4] {
3746 let fields: Vec<Fp> = self.to_fields();
3747 let field: Fp = ::poseidon::hash::hash_fields(&fields);
3748
3749 let bigint: BigInteger256 = field.into_repr();
3750 bigint.to_64x4()
3751 }
3752
3753 pub fn to_fields(&self) -> Vec<Fp> {
3756 const NFIELDS: usize = 93; let mut fields = Vec::with_capacity(NFIELDS);
3759
3760 let push_curve = |fields: &mut Vec<Fp>, curve: &InnerCurve<Fp>| {
3761 let GroupAffine::<Fp> { x, y, .. } = curve.to_affine();
3762 fields.push(x);
3763 fields.push(y);
3764 };
3765
3766 {
3769 let index = &self.dlog_plonk_index;
3770
3771 for curve in &index.sigma {
3772 push_curve(&mut fields, curve);
3773 }
3774 for curve in &index.coefficients {
3775 push_curve(&mut fields, curve);
3776 }
3777 push_curve(&mut fields, &index.generic);
3778 push_curve(&mut fields, &index.psm);
3779 push_curve(&mut fields, &index.complete_add);
3780 push_curve(&mut fields, &index.mul);
3781 push_curve(&mut fields, &index.emul);
3782 push_curve(&mut fields, &index.endomul_scalar);
3783 }
3784
3785 self.app_state.to_field_elements(&mut fields);
3786
3787 let commitments = &self.challenge_polynomial_commitments;
3788 let old_challenges = &self.old_bulletproof_challenges;
3789 for (commitments, old) in commitments.iter().zip(old_challenges) {
3790 push_curve(&mut fields, commitments);
3791 fields.extend_from_slice(old);
3792 }
3793
3794 fields
3795 }
3796}
3797
3798#[derive(Clone, Debug, PartialEq)]
3800pub enum V {
3801 External(usize),
3802 Internal(usize),
3803}
3804
3805pub type InternalVars<F> = HashMap<usize, (Vec<(F, V)>, Option<F>)>;
3806
3807pub fn compute_witness<C: ProofConstants, F: FieldWitness>(
3808 prover: &Prover<F>,
3809 w: &Witness<F>,
3810) -> [Vec<F>; COLUMNS] {
3811 #[cfg(test)]
3812 {
3813 eprintln!("compute_witness {:?}", std::any::type_name::<C>());
3815 assert_eq!(C::ROWS, prover.rows_rev.len() + C::PRIMARY_LEN);
3816 assert_eq!(C::AUX_LEN, w.aux().len());
3817 }
3818
3819 if !w.ocaml_aux.is_empty() {
3820 assert_eq!(w.aux().len(), w.ocaml_aux.len());
3821 };
3822
3823 let external_values = |i: usize| {
3824 if i < C::PRIMARY_LEN {
3825 w.primary[i]
3826 } else {
3827 w.aux()[i - C::PRIMARY_LEN]
3828 }
3829 };
3830
3831 let mut internal_values = HashMap::<usize, F>::with_capacity(13_000);
3832 let public_input_size = C::PRIMARY_LEN;
3833 let num_rows = C::ROWS;
3834
3835 let mut res: [_; COLUMNS] = std::array::from_fn(|_| vec![F::zero(); num_rows]);
3836
3837 for i in 0..public_input_size {
3839 res[0][i] = external_values(i);
3840 }
3841
3842 let compute = |(lc, c): &(Vec<(F, V)>, Option<F>), internal_values: &HashMap<_, _>| {
3843 lc.iter().fold(c.unwrap_or_else(F::zero), |acc, (s, x)| {
3844 let x = match x {
3845 V::External(x) => external_values(*x),
3846 V::Internal(x) => internal_values.get(x).copied().unwrap(),
3847 };
3848 acc + (*s * x)
3849 })
3850 };
3851
3852 for (i_after_input, cols) in prover.rows_rev.iter().rev().enumerate() {
3853 let row_idx = i_after_input + public_input_size;
3854 for (col_idx, var) in cols.iter().enumerate() {
3855 match var {
3857 None => (),
3858 Some(V::External(var)) => {
3859 res[col_idx][row_idx] = external_values(*var);
3860 }
3861 Some(V::Internal(var)) => {
3862 let lc = prover.internal_vars.get(var).unwrap();
3863 let value = compute(lc, &internal_values);
3864 res[col_idx][row_idx] = value;
3865 internal_values.insert(*var, value);
3866 }
3867 }
3868 }
3869 }
3870
3871 res
3872}
3873
3874pub fn make_prover_index<C: ProofConstants, F: FieldWitness>(
3875 gates: Vec<CircuitGate<F>>,
3876 verifier_index: Option<Arc<super::VerifierIndex<F>>>,
3877) -> ProverIndex<F> {
3878 use kimchi::circuits::constraints::ConstraintSystem;
3879
3880 let public = C::PRIMARY_LEN;
3881 let prev_challenges = C::PREVIOUS_CHALLENGES;
3882
3883 let cs = ConstraintSystem::<F>::create(gates)
3884 .public(public)
3885 .prev_challenges(prev_challenges)
3886 .build()
3887 .unwrap();
3888
3889 let (endo_q, _endo_r) = endos::<F>();
3890
3891 let srs: poly_commitment::srs::SRS<F::OtherCurve> = {
3893 let srs = get_srs_mut::<F>();
3894 let mut srs = srs.lock().unwrap();
3895 srs.add_lagrange_basis(cs.domain.d1);
3896 srs.clone()
3897 };
3898
3899 let mut index = ProverIndex::<F>::create(cs, endo_q, Arc::new(srs));
3900 index.verifier_index = verifier_index;
3901
3902 index.compute_verifier_index_digest::<F::FqSponge>();
3904 index
3905}
3906
3907#[cfg(test)]
3910fn get_rng() -> rand::rngs::StdRng {
3911 <rand::rngs::StdRng as rand::SeedableRng>::seed_from_u64(0)
3912}
3913#[cfg(not(test))]
3914fn get_rng() -> rand::rngs::OsRng {
3915 rand::rngs::OsRng
3916}
3917
3918#[derive(Debug, thiserror::Error)]
3919pub enum ProofError {
3920 #[error("kimchi error: {0:?}")]
3921 ProvingError(#[from] kimchi::error::ProverError),
3922 #[error("kimchi error with context: {0:?}")]
3923 ProvingErrorWithContext(#[from] debug::KimchiProofError),
3924 #[error("constraint not satisfield: {0}")]
3925 ConstraintsNotSatisfied(String),
3926 #[error("invalid bigint")]
3927 InvalidBigint(#[from] InvalidBigInt),
3928 #[error("Constraints ok")]
3931 ConstraintsOk,
3932}
3933
3934pub(super) struct CreateProofParams<'a, F: FieldWitness> {
3935 pub(super) prover: &'a Prover<F>,
3936 pub(super) prev_challenges: Vec<RecursionChallenge<F::OtherCurve>>,
3937 pub(super) only_verify_constraints: bool,
3938}
3939
3940pub struct ProofWithPublic<F: FieldWitness> {
3944 pub proof: super::ProverProof<F>,
3945 pub public_input: Vec<F>,
3946}
3947impl<F: FieldWitness> ProofWithPublic<F> {
3948 pub fn public_evals(&self) -> Option<&kimchi::proof::PointEvaluations<Vec<F>>> {
3949 self.proof.evals.public.as_ref()
3950 }
3951}
3952
3953pub(super) fn create_proof<C: ProofConstants, F: FieldWitness>(
3954 params: CreateProofParams<F>,
3955 w: &Witness<F>,
3956) -> anyhow::Result<ProofWithPublic<F>> {
3957 type EFrSponge<F> = mina_poseidon::sponge::DefaultFrSponge<F, PlonkSpongeConstantsKimchi>;
3958
3959 let CreateProofParams {
3960 prover,
3961 prev_challenges,
3962 only_verify_constraints,
3963 } = params;
3964
3965 let computed_witness: [Vec<F>; COLUMNS] = compute_witness::<C, _>(prover, w);
3966 let prover_index: &ProverIndex<F> = &prover.index;
3967
3968 let public_input = computed_witness[0][..prover_index.cs.public].to_vec();
3970
3971 if only_verify_constraints {
3972 prover_index
3973 .verify(&computed_witness, &public_input)
3974 .map_err(|e| {
3975 ProofError::ConstraintsNotSatisfied(format!("incorrect witness: {:?}", e))
3976 })?;
3977
3978 return Err(ProofError::ConstraintsOk.into());
3981 }
3982
3983 let mut rng = get_rng();
3985
3986 let now = redux::Instant::now();
3987 let group_map = kimchi::groupmap::GroupMap::<F::Scalar>::setup();
3988 let proof = kimchi::proof::ProverProof::create_recursive::<F::FqSponge, EFrSponge<F>>(
3989 &group_map,
3990 computed_witness,
3991 &[],
3992 prover_index,
3993 prev_challenges.clone(),
3994 None,
3995 &mut rng,
3996 )
3997 .map_err(|e| {
3998 use kimchi::groupmap::GroupMap;
3999
4000 let prev_challenges_hash = debug::hash_prev_challenge::<F>(&prev_challenges);
4001 let witness_primary_hash = debug::hash_slice(&w.primary);
4002 let witness_aux_hash = debug::hash_slice(w.aux());
4003 let group_map_hash = debug::hash_slice(&group_map.composition());
4004
4005 dbg!(
4006 &prev_challenges_hash,
4007 &witness_primary_hash,
4008 &witness_aux_hash,
4009 &group_map_hash
4010 );
4011
4012 let context = debug::KimchiProofError {
4013 inner_error: e.to_string(),
4014 witness_primary: w.primary.iter().map(|f| (*f).into()).collect(),
4015 witness_aux: w.aux().iter().map(|f| (*f).into()).collect(),
4016 witness_primary_hash,
4018 witness_aux_hash,
4019 prev_challenges_hash,
4020 group_map_hash,
4021 latest_random: LATEST_RANDOM.with_borrow(|fun| (fun)()),
4022 };
4023
4024 ProofError::ProvingErrorWithContext(context)
4025 })
4026 .context("create_recursive")?;
4027
4028 eprintln!("proof_elapsed={:?}", now.elapsed());
4029
4030 Ok(ProofWithPublic {
4031 proof,
4032 public_input,
4033 })
4034}
4035
4036pub mod debug {
4037 use super::*;
4038
4039 use mina_p2p_messages::{bigint::BigInt, binprot};
4040 use sha2::Digest;
4041
4042 fn hash_field<F: FieldWitness>(state: &mut sha2::Sha256, f: &F) {
4043 for limb in f.montgomery_form_ref() {
4044 state.update(limb.to_le_bytes());
4045 }
4046 }
4047
4048 fn hash_field_slice<F: FieldWitness>(state: &mut sha2::Sha256, slice: &[F]) {
4049 state.update(slice.len().to_le_bytes());
4050 for f in slice.iter().flat_map(|f| f.montgomery_form_ref()) {
4051 state.update(f.to_le_bytes());
4052 }
4053 }
4054
4055 pub(super) fn hash_slice<F: FieldWitness>(slice: &[F]) -> String {
4056 let mut hasher = sha2::Sha256::new();
4057 hash_field_slice(&mut hasher, slice);
4058 hex::encode(hasher.finalize())
4059 }
4060
4061 pub(super) fn hash_prev_challenge<F: FieldWitness>(
4062 prevs: &[RecursionChallenge<F::OtherCurve>],
4063 ) -> String {
4064 use poly_commitment::commitment::CommitmentCurve;
4065 use sha2::Digest;
4066 let mut hasher = sha2::Sha256::new();
4067 for RecursionChallenge { chals, comm } in prevs {
4068 hash_field_slice(&mut hasher, chals);
4069 let poly_commitment::PolyComm { elems } = comm;
4070 for elem in elems {
4071 match elem.to_coordinates() {
4072 None => {
4073 hasher.update([0]);
4074 }
4075 Some((c1, c2)) => {
4076 hasher.update([1]);
4077 hash_field(&mut hasher, &c1);
4078 hash_field(&mut hasher, &c2);
4079 }
4080 }
4081 }
4082 }
4083 hex::encode(hasher.finalize())
4084 }
4085
4086 #[derive(Clone)]
4087 pub struct KimchiProofError {
4088 pub inner_error: String,
4089 pub witness_primary: Vec<BigInt>,
4090 pub witness_aux: Vec<BigInt>,
4091 pub witness_primary_hash: String,
4094 pub witness_aux_hash: String,
4095 pub prev_challenges_hash: String,
4096 pub group_map_hash: String,
4097 pub latest_random: String,
4098 }
4099
4100 impl binprot::BinProtWrite for KimchiProofError {
4102 fn binprot_write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
4103 let Self {
4104 inner_error,
4105 witness_primary,
4106 witness_aux,
4107 witness_primary_hash,
4108 witness_aux_hash,
4109 prev_challenges_hash,
4110 group_map_hash,
4111 latest_random,
4112 } = self;
4113 let inner_error: &[u8] = inner_error.as_bytes();
4114 let witness_primary_hash: &[u8] = witness_primary_hash.as_bytes();
4115 let witness_aux_hash: &[u8] = witness_aux_hash.as_bytes();
4116 let prev_challenges_hash: &[u8] = prev_challenges_hash.as_bytes();
4117 let group_map_hash: &[u8] = group_map_hash.as_bytes();
4118 let latest_random: &[u8] = latest_random.as_bytes();
4119 binprot::BinProtWrite::binprot_write(&inner_error, w)?;
4120 binprot::BinProtWrite::binprot_write(witness_primary, w)?;
4121 binprot::BinProtWrite::binprot_write(witness_aux, w)?;
4122 binprot::BinProtWrite::binprot_write(&witness_primary_hash, w)?;
4123 binprot::BinProtWrite::binprot_write(&witness_aux_hash, w)?;
4124 binprot::BinProtWrite::binprot_write(&prev_challenges_hash, w)?;
4125 binprot::BinProtWrite::binprot_write(&group_map_hash, w)?;
4126 binprot::BinProtWrite::binprot_write(&latest_random, w)?;
4127 Ok(())
4128 }
4129 }
4130 impl binprot::BinProtRead for KimchiProofError {
4132 fn binprot_read<R: std::io::Read + ?Sized>(r: &mut R) -> Result<Self, binprot::Error>
4133 where
4134 Self: Sized,
4135 {
4136 let to_string = |bytes: Vec<u8>| -> String { String::from_utf8(bytes).unwrap() };
4137 let inner_error: Vec<u8> = binprot::BinProtRead::binprot_read(r)?;
4138 let witness_primary: Vec<BigInt> = binprot::BinProtRead::binprot_read(r)?;
4139 let witness_aux: Vec<BigInt> = binprot::BinProtRead::binprot_read(r)?;
4140 let witness_primary_hash: Vec<u8> = binprot::BinProtRead::binprot_read(r)?;
4141 let witness_aux_hash: Vec<u8> = binprot::BinProtRead::binprot_read(r)?;
4142 let prev_challenges_hash: Vec<u8> = binprot::BinProtRead::binprot_read(r)?;
4143 let group_map_hash: Vec<u8> = binprot::BinProtRead::binprot_read(r)?;
4144 let latest_random: Vec<u8> = binprot::BinProtRead::binprot_read(r)?;
4145 Ok(Self {
4146 inner_error: to_string(inner_error),
4147 witness_primary,
4148 witness_aux,
4149 witness_primary_hash: to_string(witness_primary_hash),
4150 witness_aux_hash: to_string(witness_aux_hash),
4151 prev_challenges_hash: to_string(prev_challenges_hash),
4152 group_map_hash: to_string(group_map_hash),
4153 latest_random: to_string(latest_random),
4154 })
4155 }
4156 }
4157
4158 impl core::fmt::Display for KimchiProofError {
4159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4160 f.write_fmt(format_args!("{:?}", self))
4161 }
4162 }
4163
4164 impl std::error::Error for KimchiProofError {}
4165
4166 impl core::fmt::Debug for KimchiProofError {
4167 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4168 let Self {
4169 inner_error,
4170 witness_primary,
4171 witness_aux,
4172 witness_primary_hash,
4173 witness_aux_hash,
4174 prev_challenges_hash,
4175 group_map_hash,
4176 latest_random,
4177 } = self;
4178
4179 f.debug_struct("KimchiProofError")
4181 .field("inner_error", inner_error)
4182 .field("witness_primary", &witness_primary.len())
4183 .field("witness_aux", &witness_aux.len())
4184 .field("witness_primary_hash", &witness_primary_hash)
4185 .field("witness_aux_hash", &witness_aux_hash)
4186 .field("prev_challenges_hash", &prev_challenges_hash)
4187 .field("group_map_hash", &group_map_hash)
4188 .field("latest_random", &latest_random)
4189 .finish()
4190 }
4191 }
4192}
4193
4194#[derive(Clone)]
4195pub struct Prover<F: FieldWitness> {
4196 pub internal_vars: InternalVars<F>,
4198 pub rows_rev: Vec<Vec<Option<V>>>,
4200 pub index: ProverIndex<F>,
4201}
4202
4203pub struct TransactionParams<'a> {
4204 pub statement: &'a MinaStateBlockchainStateValueStableV2LedgerProofStatement,
4205 pub tx_witness: &'a v2::TransactionWitnessStableV2,
4206 pub message: &'a SokMessage,
4207 pub tx_step_prover: &'a Prover<Fp>,
4208 pub tx_wrap_prover: &'a Prover<Fq>,
4209 pub only_verify_constraints: bool,
4212 pub expected_step_proof: Option<&'static str>,
4214 pub ocaml_wrap_witness: Option<Vec<Fq>>,
4216}
4217
4218pub(super) fn generate_tx_proof(
4219 params: TransactionParams,
4220 w: &mut Witness<Fp>,
4221) -> anyhow::Result<WrapProof> {
4222 let TransactionParams {
4223 statement,
4224 tx_witness,
4225 message,
4226 tx_step_prover,
4227 tx_wrap_prover,
4228 only_verify_constraints,
4229 expected_step_proof,
4230 ocaml_wrap_witness,
4231 } = params;
4232
4233 let statement: Statement<()> = statement.try_into()?;
4234 let sok_digest = message.digest();
4235 let statement_with_sok = statement.with_digest(sok_digest);
4236
4237 let dlog_plonk_index =
4238 PlonkVerificationKeyEvals::from(&**tx_wrap_prover.index.verifier_index.as_ref().unwrap());
4239
4240 let statement_with_sok = Rc::new(w.exists(statement_with_sok));
4241 transaction_snark::main(&statement_with_sok, tx_witness, w)?;
4242
4243 let StepProof {
4244 statement: step_statement,
4245 prev_evals,
4246 proof_with_public,
4247 } = step::step::<StepTransactionProof, 0>(
4248 step::StepParams {
4249 app_state: Rc::clone(&statement_with_sok) as _,
4250 rule: InductiveRule::empty(),
4251 for_step_datas: [],
4252 indexes: [],
4253 prev_challenge_polynomial_commitments: vec![],
4254 hack_feature_flags: OptFlag::No,
4255 step_prover: tx_step_prover,
4256 wrap_prover: tx_wrap_prover,
4257 only_verify_constraints,
4258 },
4259 w,
4260 )?;
4261
4262 if let Some(expected) = expected_step_proof {
4263 let proof_json = serde_json::to_vec(&proof_with_public.proof).unwrap();
4264 assert_eq!(sha256_sum(&proof_json), expected);
4265 };
4266
4267 let mut w = Witness::new::<WrapTransactionProof>();
4268
4269 if let Some(ocaml_aux) = ocaml_wrap_witness {
4270 w.ocaml_aux = ocaml_aux;
4271 };
4272
4273 wrap::wrap::<WrapTransactionProof>(
4274 WrapParams {
4275 app_state: statement_with_sok,
4276 proof_with_public: &proof_with_public,
4277 step_statement,
4278 prev_evals: &prev_evals,
4279 dlog_plonk_index: &dlog_plonk_index,
4280 step_prover_index: &tx_step_prover.index,
4281 wrap_prover: tx_wrap_prover,
4282 },
4283 &mut w,
4284 )
4285}
4286
4287#[cfg(test)]
4288mod tests_with_wasm {
4289 #[cfg(target_family = "wasm")]
4290 use wasm_bindgen_test::wasm_bindgen_test as test;
4291
4292 use super::*;
4293 #[test]
4294 fn test_to_field_checked() {
4295 let mut witness = Witness::empty();
4296 let f = Fp::from_str("1866").unwrap();
4297
4298 let res = scalar_challenge::to_field_checked_prime::<_, 32>(f, &mut witness);
4299
4300 assert_eq!(res, (131085.into(), 65636.into(), 1866.into()));
4301 assert_eq!(
4302 witness.aux(),
4303 &[
4304 0.into(),
4305 0.into(),
4306 0.into(),
4307 0.into(),
4308 0.into(),
4309 0.into(),
4310 0.into(),
4311 0.into(),
4312 0.into(),
4313 512.into(),
4314 257.into(),
4315 0.into(),
4316 0.into(),
4317 1.into(),
4318 3.into(),
4319 1.into(),
4320 0.into(),
4321 2.into(),
4322 2.into(),
4323 1866.into(),
4324 131085.into(),
4325 65636.into(),
4326 ]
4327 );
4328 }
4329}
4330
4331#[cfg(test)]
4332pub(super) mod tests {
4333 use std::path::Path;
4334
4335 use ::poseidon::hash::params::MINA_ZKAPP_EVENT;
4336 use mina_p2p_messages::binprot::{
4337 self,
4338 macros::{BinProtRead, BinProtWrite},
4339 BinProtRead,
4340 };
4341
4342 use crate::{
4343 proofs::{
4344 block::{generate_block_proof, BlockParams},
4345 constants::{StepBlockProof, StepMergeProof},
4346 merge::{generate_merge_proof, MergeParams},
4347 provers::{devnet_circuit_directory, BlockProver, TransactionProver, ZkappProver},
4348 util::sha256_sum,
4349 zkapp::{generate_zkapp_proof, LedgerProof, ZkappParams},
4350 },
4351 scan_state::scan_state::transaction_snark::SokMessage,
4352 };
4353
4354 use super::*;
4355
4356 type SnarkWorkSpec =
4357 mina_p2p_messages::v2::SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponseA0Instances;
4358
4359 #[derive(Debug, BinProtRead, BinProtWrite)]
4361 pub enum ExternalSnarkWorkerRequest {
4362 AwaitReadiness,
4364 PerformJob(mina_p2p_messages::v2::SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponse),
4366 }
4367
4368 pub fn panic_in_ci() {
4369 fn is_ci() -> bool {
4370 std::env::var("CI").is_ok()
4371 }
4372 assert!(!is_ci(), "missing circuit files !");
4373 }
4374
4375 fn read_binprot<T, R>(mut r: R) -> T
4376 where
4377 T: binprot::BinProtRead,
4378 R: std::io::Read,
4379 {
4380 use std::io::Read;
4381
4382 let mut len_buf = [0; std::mem::size_of::<u64>()];
4383 r.read_exact(&mut len_buf).unwrap();
4384 let len = u64::from_le_bytes(len_buf);
4385
4386 let mut buf = Vec::with_capacity(len as usize);
4387 let mut r = r.take(len);
4388 r.read_to_end(&mut buf).unwrap();
4389
4390 let mut read = buf.as_slice();
4391 T::binprot_read(&mut read).unwrap()
4392 }
4393
4394 fn read_witnesses<F: FieldWitness>(filename: &str) -> Result<Vec<F>, ()> {
4395 let f = std::fs::read_to_string(
4396 std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
4397 .join(devnet_circuit_directory())
4398 .join("witnesses")
4399 .join(filename),
4400 )
4401 .unwrap();
4402 f.lines()
4403 .filter(|s| !s.is_empty())
4404 .map(|s| F::from_str(s).map_err(|_| ()))
4405 .collect()
4406 }
4407
4408 #[allow(unused)]
4409 #[test]
4410 #[ignore]
4411 fn test_convert_requests() {
4412 use binprot::BinProtWrite;
4413 use mina_p2p_messages::v2::*;
4414
4415 fn write_binprot<T: BinProtWrite, W: std::io::Write>(spec: T, mut w: W) {
4416 let mut buf = Vec::new();
4417 spec.binprot_write(&mut buf).unwrap();
4418 let len = (buf.len() as u64).to_le_bytes();
4419 w.write_all(&len).unwrap();
4420 w.write_all(&buf).unwrap();
4421 }
4422
4423 let path = Path::new(env!("CARGO_MANIFEST_DIR"))
4424 .join(devnet_circuit_directory())
4425 .join("tests");
4426
4427 let entries = std::fs::read_dir(path)
4428 .unwrap()
4429 .map(|res| res.map(|e| e.path()))
4430 .collect::<Result<Vec<_>, std::io::Error>>()
4431 .unwrap();
4432
4433 let prover = CompressedPubKey::from_address(
4434 "B62qpK6TcG4sWdtT3BzdbWHiK3RJMj3Zbo9mqwBos7cVsydPMCj5wZx",
4435 )
4436 .unwrap();
4437 let fee = crate::scan_state::currency::Fee::from_u64(1_000_000);
4438
4439 for filename in entries {
4440 if !filename.ends_with(".bin") {
4441 continue;
4442 }
4443 let bytes = std::fs::read(&filename).unwrap();
4444
4445 let single: SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponseA0Single =
4452 binprot::BinProtRead::binprot_read(&mut bytes.as_slice()).unwrap();
4453 let instances =
4454 SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponseA0Instances::One(single);
4455 let job = SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponseA0 {
4456 instances,
4457 fee: (&fee).into(),
4458 };
4459 let job =
4460 SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponse(Some((job, (&prover).into())));
4461 let job = ExternalSnarkWorkerRequest::PerformJob(job);
4462
4463 let path = Path::new("/tmp")
4464 .join("requests")
4465 .join(filename.file_name().unwrap());
4466 let mut file = std::fs::File::create(path).unwrap();
4467 write_binprot(job, &mut file);
4468 file.sync_all().unwrap();
4469 }
4470 }
4471
4472 fn extract_request(
4473 mut bytes: &[u8],
4474 ) -> (
4475 v2::MinaStateSnarkedLedgerStateStableV2,
4476 v2::TransactionWitnessStableV2,
4477 SokMessage,
4478 ) {
4479 use mina_p2p_messages::v2::*;
4480
4481 let v: ExternalSnarkWorkerRequest = read_binprot(&mut bytes);
4482
4483 let ExternalSnarkWorkerRequest::PerformJob(job) = v else {
4484 panic!()
4485 };
4486 let SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponse(Some((a, prover))) = job else {
4487 panic!()
4488 };
4489 let SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponseA0Instances::One(single) = a.instances
4490 else {
4491 panic!()
4492 };
4493 let SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponseA0Single::Transition(
4494 statement,
4495 tx_witness,
4496 ) = single
4497 else {
4498 panic!()
4499 };
4500
4501 let prover: CompressedPubKey = (&prover).try_into().unwrap();
4502 let fee = crate::scan_state::currency::Fee::from_u64(a.fee.as_u64());
4503
4504 let message = SokMessage { fee, prover };
4505
4506 (statement, tx_witness, message)
4507 }
4508
4509 fn extract_merge(
4510 mut bytes: &[u8],
4511 ) -> (
4512 v2::MinaStateSnarkedLedgerStateStableV2,
4513 [v2::LedgerProofProdStableV2; 2],
4514 SokMessage,
4515 ) {
4516 use mina_p2p_messages::v2::*;
4517
4518 let v: ExternalSnarkWorkerRequest = read_binprot(&mut bytes);
4519
4520 let ExternalSnarkWorkerRequest::PerformJob(job) = v else {
4521 panic!()
4522 };
4523 let SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponse(Some((a, prover))) = job else {
4524 panic!()
4525 };
4526 let SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponseA0Instances::One(single) = a.instances
4527 else {
4528 panic!()
4529 };
4530 let SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponseA0Single::Merge(merge) = single else {
4531 panic!()
4532 };
4533
4534 let (statement, p1, p2) = *merge;
4535
4536 let prover: CompressedPubKey = (&prover).try_into().unwrap();
4537 let fee = crate::scan_state::currency::Fee::from_u64(a.fee.as_u64());
4538
4539 let message = SokMessage { fee, prover };
4540
4541 (statement, [p1, p2], message)
4542 }
4543
4544 #[allow(unused)]
4545 #[test]
4546 fn test_make_verifier_index() {
4547 BlockProver::make(None, None);
4548 TransactionProver::make(None);
4549
4550 eprintln!("OK");
4568
4569 }
4609
4610 #[test]
4611 fn test_regular_tx() {
4612 let Ok(data) =
4613 std::fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join(devnet_circuit_directory()).join("tests").join("command-0-1.bin"))
4616 else {
4621 eprintln!("request not found");
4622 panic_in_ci();
4623 return;
4624 };
4625
4626 let (statement, tx_witness, message) = extract_request(&data);
4627 let TransactionProver {
4628 tx_step_prover,
4629 tx_wrap_prover,
4630 merge_step_prover: _,
4631 } = TransactionProver::make(None);
4632
4633 let mut witnesses: Witness<Fp> = Witness::new::<StepTransactionProof>();
4634 let WrapProof { proof, .. } = generate_tx_proof(
4637 TransactionParams {
4638 statement: &statement,
4639 tx_witness: &tx_witness,
4640 message: &message,
4641 tx_step_prover: &tx_step_prover,
4642 tx_wrap_prover: &tx_wrap_prover,
4643 only_verify_constraints: false,
4644 expected_step_proof: None,
4645 ocaml_wrap_witness: None,
4646 },
4647 &mut witnesses,
4648 )
4649 .unwrap();
4650
4651 let proof_json = serde_json::to_vec(&proof.proof).unwrap();
4652 let sum = sha256_sum(&proof_json);
4653 dbg!(sum);
4654 }
4655
4656 #[test]
4657 fn test_hash_empty_event_checked() {
4658 const EXPECTED: &str =
4660 "6963060754718463299978089777716994949151371320681588566338620419071140958308";
4661
4662 let mut w = Witness::empty();
4663 let hash = transaction_snark::checked_hash(&MINA_ZKAPP_EVENT, &[], &mut w);
4664 assert_eq!(hash, Fp::from_str(EXPECTED).unwrap());
4665
4666 let mut w = Witness::empty();
4667 let hash = transaction_snark::checked_hash3(&MINA_ZKAPP_EVENT, &[], &mut w);
4668 assert_eq!(hash, Fp::from_str(EXPECTED).unwrap());
4669 }
4670
4671 #[allow(unused)]
4673 #[test]
4674 #[ignore]
4675 fn test_read_requests() {
4676 let path = Path::new(env!("CARGO_MANIFEST_DIR"))
4677 .join(devnet_circuit_directory())
4678 .join("tests");
4679
4680 let mut files = Vec::with_capacity(1000);
4681
4682 for index in 0..285 {
4683 for j in 0..2 {
4684 let filename = format!("command-{index}-{j}.bin");
4685 let file_path = path.join(filename);
4686 if file_path.exists() {
4687 files.push(file_path);
4688 }
4689 }
4690 }
4691
4692 for (index, file) in files.iter().enumerate() {
4693 use mina_p2p_messages::v2::*;
4694
4695 let bytes = std::fs::read(file).unwrap();
4696
4697 let v: ExternalSnarkWorkerRequest = read_binprot(&mut bytes.as_slice());
4698 let ExternalSnarkWorkerRequest::PerformJob(job) = v else {
4699 panic!()
4700 };
4701 let SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponse(Some((a, _prover))) = job else {
4702 panic!()
4703 };
4704 let SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponseA0Instances::One(single) =
4705 a.instances
4706 else {
4707 panic!()
4708 };
4709
4710 let (_stmt, witness) = match single {
4711 mina_p2p_messages::v2::SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponseA0Single::Transition(stmt, witness) => (stmt, witness),
4712 mina_p2p_messages::v2::SnarkWorkerWorkerRpcsVersionedGetWorkV2TResponseA0Single::Merge(_) => todo!(),
4713 };
4714
4715 match &witness.transaction {
4716 MinaTransactionTransactionStableV2::Command(cmd) => match &**cmd {
4717 mina_p2p_messages::v2::MinaBaseUserCommandStableV2::SignedCommand(_) => {
4718 eprintln!("[{}] signed: {:?}", index, file)
4719 }
4720 mina_p2p_messages::v2::MinaBaseUserCommandStableV2::ZkappCommand(z) => {
4721 eprintln!("[{}] zkapp: {:?}", index, file);
4722 }
4725 },
4726 MinaTransactionTransactionStableV2::FeeTransfer(_) => {
4727 eprintln!("[{}] fee_transfer", index)
4728 }
4729 MinaTransactionTransactionStableV2::Coinbase(_) => {
4730 eprintln!("[{}] coinbase", index)
4731 }
4732 }
4733 }
4734 }
4735
4736 #[test]
4737 fn test_merge_proof() {
4738 let Ok(data) =
4739 std::fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join(devnet_circuit_directory()).join("tests").join("merge-100-0.bin"))
4742 else {
4747 eprintln!("request not found");
4748 panic_in_ci();
4749 return;
4750 };
4751
4752 let (statement, proofs, message) = extract_merge(&data);
4753 let TransactionProver {
4754 tx_step_prover: _,
4755 tx_wrap_prover,
4756 merge_step_prover,
4757 } = TransactionProver::make(None);
4758
4759 let mut witnesses: Witness<Fp> = Witness::new::<StepMergeProof>();
4760 let WrapProof { proof, .. } = generate_merge_proof(
4763 MergeParams {
4764 statement: (&*statement).try_into().unwrap(),
4765 proofs: &proofs,
4766 message: &message,
4767 step_prover: &merge_step_prover,
4768 wrap_prover: &tx_wrap_prover,
4769 only_verify_constraints: false,
4770 expected_step_proof: None,
4771 ocaml_wrap_witness: None,
4775 },
4777 &mut witnesses,
4778 )
4779 .unwrap();
4780 let proof_json = serde_json::to_vec(&proof.proof).unwrap();
4781
4782 let _sum = dbg!(sha256_sum(&proof_json));
4783 }
4784
4785 #[test]
4786 fn test_proof_zkapp_sig() {
4787 let Ok(data) = std::fs::read(
4788 Path::new(env!("CARGO_MANIFEST_DIR"))
4789 .join(devnet_circuit_directory())
4790 .join("tests")
4791 .join("command-1-0.bin"),
4792 ) else {
4793 eprintln!("request not found");
4794 panic_in_ci();
4795 return;
4796 };
4797
4798 let (statement, tx_witness, message) = extract_request(&data);
4799
4800 let ZkappProver {
4801 tx_wrap_prover,
4802 merge_step_prover,
4803 step_opt_signed_opt_signed_prover,
4804 step_opt_signed_prover,
4805 step_proof_prover,
4806 } = ZkappProver::make(None);
4807
4808 dbg!(step_opt_signed_opt_signed_prover.rows_rev.len());
4809 let LedgerProof { proof, .. } = generate_zkapp_proof(ZkappParams {
4812 statement: &statement,
4813 tx_witness: &tx_witness,
4814 message: &message,
4815 step_opt_signed_opt_signed_prover: &step_opt_signed_opt_signed_prover,
4816 step_opt_signed_prover: &step_opt_signed_prover,
4817 step_proof_prover: &step_proof_prover,
4818 merge_step_prover: &merge_step_prover,
4819 tx_wrap_prover: &tx_wrap_prover,
4820 opt_signed_path: None,
4821 proved_path: None,
4823 })
4824 .unwrap();
4825
4826 let proof_json = serde_json::to_vec(&proof.proof.proof).unwrap();
4827 let _sum = dbg!(sha256_sum(&proof_json));
4828 }
4829
4830 #[test]
4831 fn test_proof_zkapp_proof() {
4832 let Ok(data) = std::fs::read(
4833 Path::new(env!("CARGO_MANIFEST_DIR"))
4834 .join(devnet_circuit_directory())
4835 .join("tests")
4836 .join("zkapp-command-with-proof-128-1.bin"),
4837 ) else {
4838 eprintln!("request not found");
4839 panic_in_ci();
4840 return;
4841 };
4842
4843 let (statement, tx_witness, message) = extract_request(&data);
4844
4845 let ZkappProver {
4846 tx_wrap_prover,
4847 merge_step_prover,
4848 step_opt_signed_opt_signed_prover,
4849 step_opt_signed_prover,
4850 step_proof_prover,
4851 } = ZkappProver::make(None);
4852
4853 let LedgerProof { proof, .. } = generate_zkapp_proof(ZkappParams {
4854 statement: &statement,
4855 tx_witness: &tx_witness,
4856 message: &message,
4857 step_opt_signed_opt_signed_prover: &step_opt_signed_opt_signed_prover,
4858 step_opt_signed_prover: &step_opt_signed_prover,
4859 step_proof_prover: &step_proof_prover,
4860 merge_step_prover: &merge_step_prover,
4861 tx_wrap_prover: &tx_wrap_prover,
4862 opt_signed_path: None,
4863 proved_path: None,
4864 })
4867 .unwrap();
4868
4869 let proof_json = serde_json::to_vec(&proof.proof.proof).unwrap();
4870 let _sum = dbg!(sha256_sum(&proof_json));
4871 }
4872
4873 #[test]
4874 fn test_block_proof() {
4875 let Ok(data) = std::fs::read(
4876 Path::new(env!("CARGO_MANIFEST_DIR"))
4877 .join(devnet_circuit_directory())
4878 .join("tests")
4879 .join("block_input-2483246-0.bin"),
4880 ) else {
4881 eprintln!("request not found");
4882 panic_in_ci();
4883 return;
4884 };
4885
4886 let blockchain_input: v2::ProverExtendBlockchainInputStableV2 =
4887 read_binprot(&mut data.as_slice());
4888
4889 let BlockProver {
4890 block_step_prover,
4891 block_wrap_prover,
4892 tx_wrap_prover,
4893 } = BlockProver::make(None, None);
4894 let mut witnesses: Witness<Fp> = Witness::new::<StepBlockProof>();
4895 let WrapProof { proof, .. } = generate_block_proof(
4898 BlockParams {
4899 input: &blockchain_input,
4900 block_step_prover: &block_step_prover,
4901 block_wrap_prover: &block_wrap_prover,
4902 tx_wrap_prover: &tx_wrap_prover,
4903 only_verify_constraints: false,
4904 expected_step_proof: None,
4905 ocaml_wrap_witness: None,
4906 },
4911 &mut witnesses,
4912 )
4913 .unwrap();
4914
4915 let proof_json = serde_json::to_vec(&proof.proof).unwrap();
4916 let _sum = dbg!(sha256_sum(&proof_json));
4917 }
4918
4919 #[test]
4920 #[ignore]
4921 fn make_rsa_key() {
4922 use rsa::{
4923 pkcs1::{EncodeRsaPrivateKey, EncodeRsaPublicKey},
4924 pkcs8::LineEnding::LF,
4925 RsaPrivateKey, RsaPublicKey,
4926 };
4927
4928 let mut rng = rand::thread_rng();
4929 let bits = 2048;
4930 let priv_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
4931 let pub_key = RsaPublicKey::from(&priv_key);
4932
4933 let priv_key = priv_key.to_pkcs1_pem(LF).unwrap();
4934 let priv_key: &str = priv_key.as_ref();
4935 println!("private:\n{}", priv_key);
4936
4937 let pub_key = pub_key.to_pkcs1_pem(LF).unwrap();
4938 println!("public:\n{}", pub_key);
4939 }
4940
4941 #[test]
4942 fn add_private_key_to_block_proof_input() {
4943 use mina_p2p_messages::binprot::BinProtWrite;
4944 use rsa::{pkcs1::DecodeRsaPrivateKey, Pkcs1v15Encrypt};
4945
4946 #[derive(binprot::macros::BinProtRead)]
4947 struct DumpBlockProof {
4948 input: Box<v2::ProverExtendBlockchainInputStableV2>,
4949 key: Vec<u8>,
4950 error: Vec<u8>,
4951 }
4952
4953 let rsa_private_key = {
4954 let Ok(home) = std::env::var("HOME") else {
4955 eprintln!("$HOME not set");
4956 return;
4957 };
4958 let Ok(string) = std::fs::read_to_string(format!("{home}/.openmina/debug/rsa.priv"))
4959 else {
4960 eprintln!("Missing private key");
4961 return;
4962 };
4963 rsa::RsaPrivateKey::from_pkcs1_pem(&string).unwrap()
4964 };
4965
4966 let DumpBlockProof {
4967 mut input,
4968 key,
4969 error,
4970 } = {
4971 let Ok(data) = std::fs::read("/tmp/block_proof.binprot") else {
4972 eprintln!("Missing block proof");
4973 return;
4974 };
4975 DumpBlockProof::binprot_read(&mut data.as_slice()).unwrap()
4976 };
4977 eprintln!("error was: {}", String::from_utf8_lossy(&error));
4978
4979 let producer_private_key = {
4980 let producer_private_key = rsa_private_key.decrypt(Pkcs1v15Encrypt, &key).unwrap();
4981 v2::SignatureLibPrivateKeyStableV1::binprot_read(&mut producer_private_key.as_slice())
4982 .unwrap()
4983 };
4984
4985 input.prover_state.producer_private_key = producer_private_key;
4986
4987 let mut file = std::fs::File::create("/tmp/block_proof_with_key.binprot").unwrap();
4988 input.binprot_write(&mut file).unwrap();
4989 file.sync_all().unwrap();
4990 eprintln!("saved to /tmp/block_proof_with_key.binprot");
4991 }
4992
4993 #[test]
4994 fn test_proofs() {
4995 let base_dir = Path::new(env!("CARGO_MANIFEST_DIR"))
4996 .join(devnet_circuit_directory())
4997 .join("tests");
4998
4999 if !base_dir.exists() {
5000 eprintln!("{:?} not found", base_dir);
5001 panic_in_ci();
5002 return;
5003 }
5004
5005 let BlockProver {
5006 block_step_prover,
5007 block_wrap_prover,
5008 tx_wrap_prover: _,
5009 } = BlockProver::make(None, None);
5010 let TransactionProver { tx_step_prover, .. } = TransactionProver::make(None);
5011 let ZkappProver {
5012 tx_wrap_prover,
5013 merge_step_prover,
5014 step_opt_signed_opt_signed_prover: zkapp_step_opt_signed_opt_signed_prover,
5015 step_opt_signed_prover: zkapp_step_opt_signed_prover,
5016 step_proof_prover: zkapp_step_proof_prover,
5017 } = ZkappProver::make(None);
5018
5019 #[rustfmt::skip]
5021 let zkapp_cases = [
5022 ("command-1-0.bin", None, None, "b5295e34d8f4b0f349fc48c4f46e9bd400c1f3e551deab75e3fc541282f6d714"),
5024 ("zkapp-command-with-proof-128-1.bin", None, None, "daa090e212c9fcd4e0c7fa70de4708878a6ac0186238d6ca094129fad1cfa5c2"),
5026 ];
5029
5030 for (file, opt_signed_path, proved_path, expected_sum) in zkapp_cases {
5031 let data = std::fs::read(base_dir.join(file)).unwrap();
5032 let (statement, tx_witness, message) = extract_request(&data);
5033
5034 let LedgerProof { proof, .. } = generate_zkapp_proof(ZkappParams {
5035 statement: &statement,
5036 tx_witness: &tx_witness,
5037 message: &message,
5038 step_opt_signed_opt_signed_prover: &zkapp_step_opt_signed_opt_signed_prover,
5039 step_opt_signed_prover: &zkapp_step_opt_signed_prover,
5040 step_proof_prover: &zkapp_step_proof_prover,
5041 merge_step_prover: &merge_step_prover,
5042 tx_wrap_prover: &tx_wrap_prover,
5043 opt_signed_path,
5044 proved_path,
5045 })
5046 .unwrap();
5047
5048 let proof_json = serde_json::to_vec(&proof.proof.proof).unwrap();
5049 let sum = dbg!(sha256_sum(&proof_json));
5050
5051 assert_eq!(sum, expected_sum);
5052 }
5053
5054 #[allow(clippy::single_element_loop)]
5056 for (filename, fps_filename) in [
5057 ("block_input-2483246-0.bin", None),
5058 ] {
5060 let data = std::fs::read(base_dir.join(filename)).unwrap();
5061
5062 let blockchain_input: v2::ProverExtendBlockchainInputStableV2 =
5063 read_binprot(&mut data.as_slice());
5064
5065 let mut witnesses: Witness<Fp> = Witness::new::<StepBlockProof>();
5066 if let Some(filename) = fps_filename {
5067 witnesses.ocaml_aux = read_witnesses(filename).unwrap();
5068 };
5069
5070 let WrapProof { proof, .. } = generate_block_proof(
5071 BlockParams {
5072 input: &blockchain_input,
5073 block_step_prover: &block_step_prover,
5074 block_wrap_prover: &block_wrap_prover,
5075 tx_wrap_prover: &tx_wrap_prover,
5076 only_verify_constraints: false,
5077 expected_step_proof: None,
5078 ocaml_wrap_witness: None,
5079 },
5084 &mut witnesses,
5085 )
5086 .unwrap();
5087 let proof_json = serde_json::to_vec(&proof.proof).unwrap();
5088
5089 let sum = sha256_sum(&proof_json);
5090 assert_eq!(
5091 sum,
5092 "2488ef14831ce4bf196ec381fe955312b3c0946354af1c2bcedffb38c1072147"
5093 );
5094 }
5095
5096 {
5098 let data = std::fs::read(base_dir.join("merge-100-0.bin")).unwrap();
5099
5100 let (statement, proofs, message) = extract_merge(&data);
5101
5102 let mut witnesses: Witness<Fp> = Witness::new::<StepMergeProof>();
5103 let WrapProof { proof, .. } = generate_merge_proof(
5106 MergeParams {
5107 statement: (&*statement).try_into().unwrap(),
5108 proofs: &proofs,
5109 message: &message,
5110 step_prover: &merge_step_prover,
5111 wrap_prover: &tx_wrap_prover,
5112 only_verify_constraints: false,
5113 expected_step_proof: None,
5114 ocaml_wrap_witness: None,
5115 },
5120 &mut witnesses,
5121 )
5122 .unwrap();
5123
5124 let proof_json = serde_json::to_vec(&proof.proof).unwrap();
5125
5126 let sum = dbg!(sha256_sum(&proof_json));
5127 assert_eq!(
5128 sum,
5129 "da069a6752ca677fdb2e26e643c26d4e28f6e52210547d6ed99f9dd7fd324803"
5130 );
5131 }
5132
5133 #[rustfmt::skip]
5136 let requests = [
5137 ("command-0-1.bin", "cbcb54861c5c65b7d454e7add9a780fa574f5145aa225387a287abe612925abb"),
5138 ];
5160
5161 for (file, expected_sum) in requests {
5162 let data = std::fs::read(base_dir.join(file)).unwrap();
5163 let (statement, tx_witness, message) = extract_request(&data);
5164
5165 let mut witnesses: Witness<Fp> = Witness::new::<StepTransactionProof>();
5166
5167 if file == "request_payment_0_rampup4.bin" {
5168 witnesses.ocaml_aux = read_witnesses("fps_rampup4.txt").unwrap();
5169 }
5170
5171 let WrapProof { proof, .. } = generate_tx_proof(
5172 TransactionParams {
5173 statement: &statement,
5174 tx_witness: &tx_witness,
5175 message: &message,
5176 tx_step_prover: &tx_step_prover,
5177 tx_wrap_prover: &tx_wrap_prover,
5178 only_verify_constraints: false,
5179 expected_step_proof: None,
5180 ocaml_wrap_witness: None,
5181 },
5182 &mut witnesses,
5183 )
5184 .unwrap();
5185
5186 let proof_json = serde_json::to_vec(&proof.proof).unwrap();
5187 let sum = dbg!(sha256_sum(&proof_json));
5188
5189 if dbg!(&sum) != expected_sum {
5190 eprintln!("Wrong proof: {:?}", file);
5191 eprintln!("got sum: {:?}", sum);
5192 eprintln!("expected: {:?}", expected_sum);
5193 panic!()
5194 }
5195 }
5196 }
5197}