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