1use std::{cell::RefCell, rc::Rc};
2
3use ark_ff::{BigInteger256, Zero};
4use kimchi::proof::PointEvaluations;
5use mina_curves::pasta::{Fp, Fq};
6use mina_p2p_messages::{bigint::InvalidBigInt, v2};
7use poly_commitment::ipa::OpeningProof;
8use poseidon::hash::{
9 hash_with_kimchi,
10 params::{MINA_ACCOUNT_UPDATE_CONS, MINA_PROTO_STATE_BODY},
11};
12
13use crate::{
14 proofs::{
15 constants::{
16 make_step_zkapp_data, StepMergeProof, StepZkappOptSignedOptSignedProof,
17 WrapZkappOptSignedProof, WrapZkappProof,
18 },
19 field::{Boolean, CircuitVar, FieldWitness, ToBoolean},
20 merge::{generate_merge_proof, MergeParams},
21 public_input::{messages::MessagesForNextWrapProof, prepared_statement::DeferredValues},
22 step::{
23 extract_recursion_challenges, step, InductiveRule, OptFlag, PreviousProofStatement,
24 StepParams, StepProof,
25 },
26 transaction::{ProofWithPublic, ReducedMessagesForNextStepProof},
27 unfinalized::{AllEvals, EvalsWithPublicInput},
28 verification::prev_evals_to_p2p,
29 verifiers::make_zkapp_verifier_index,
30 wrap::{self, WrapParams, WrapProofState, WrapStatement},
31 zkapp::group::{State, ZkappCommandIntermediateState},
32 },
33 scan_state::{
34 currency::{Amount, Index, Signed, Slot},
35 fee_excess::FeeExcess,
36 pending_coinbase::{self, Stack, StackState},
37 scan_state::transaction_snark::{Registers, SokDigest, SokMessage, Statement},
38 transaction_logic::{
39 local_state::{
40 LocalState, LocalStateEnv, LocalStateSkeleton, StackFrame, StackFrameChecked,
41 },
42 protocol_state::{protocol_state_body_view, GlobalStateSkeleton},
43 zkapp_command::{AccountUpdate, CallForest, Control, WithHash, ZkAppCommand},
44 zkapp_statement::{TransactionCommitment, ZkappStatement},
45 TransactionFailure,
46 },
47 },
48 sparse_ledger::SparseLedger,
49 zkapps::{
50 snark::ZkappSnark,
51 zkapp_logic::{self, ApplyZkappParams},
52 },
53 AccountId, ControlTag, ToInputs, TokenId,
54};
55
56use self::group::SegmentBasic;
57
58use super::{
59 block::ProtocolStateBody,
60 constants::{
61 ForWrapData, ProofConstants, StepZkappOptSignedProof, StepZkappProvedProof,
62 WrapZkappProvedProof,
63 },
64 field::GroupAffine,
65 numbers::{
66 currency::{CheckedAmount, CheckedSigned},
67 nat::{CheckedIndex, CheckedSlot},
68 },
69 provers::devnet_circuit_directory,
70 to_field_elements::ToFieldElements,
71 transaction::{dummy_constraints, Check, Prover},
72 witness::Witness,
73 wrap::WrapProof,
74};
75
76pub struct ZkappParams<'a> {
77 pub statement: &'a v2::MinaStateBlockchainStateValueStableV2LedgerProofStatement,
78 pub tx_witness: &'a v2::TransactionWitnessStableV2,
79 pub message: &'a SokMessage,
80 pub step_opt_signed_opt_signed_prover: &'a Prover<Fp>,
81 pub step_opt_signed_prover: &'a Prover<Fp>,
82 pub step_proof_prover: &'a Prover<Fp>,
83 pub merge_step_prover: &'a Prover<Fp>,
84 pub tx_wrap_prover: &'a Prover<Fq>,
85
86 pub opt_signed_path: Option<&'a str>,
88 pub proved_path: Option<&'a str>,
90}
91
92pub mod group {
93 use super::*;
94
95 #[derive(Debug)]
96 pub enum SegmentBasic {
97 OptSignedOptSigned,
98 OptSigned,
99 Proved,
100 }
101
102 impl SegmentBasic {
103 pub fn of_controls(controls: &[&Control]) -> Self {
104 use Control::{NoneGiven, Proof, Signature};
105
106 match controls {
107 [Proof(_)] => Self::Proved,
108 [Signature(_) | NoneGiven] => Self::OptSigned,
109 [Signature(_) | NoneGiven, Signature(_) | NoneGiven] => Self::OptSignedOptSigned,
110 _ => panic!("Unsupported combination"),
111 }
112 }
113 }
114
115 #[derive(Debug)]
116 pub enum Kind {
117 New,
118 Same,
119 TwoNew,
120 }
121
122 #[derive(Debug)]
123 pub struct State<GlobalState, LocalState> {
124 pub global: GlobalState,
125 pub local: LocalState,
126 }
127
128 #[derive(Debug)]
129 pub struct ZkappCommandIntermediateState<GlobalState, LocalState, ConnectingLedgerHash> {
130 pub kind: Kind,
131 pub spec: SegmentBasic,
132 pub state_before: State<GlobalState, LocalState>,
133 pub state_after: State<GlobalState, LocalState>,
134 pub connecting_ledger: ConnectingLedgerHash,
135 }
136
137 fn intermediate_state<GlobalState, LocalState, ConnectingLedgerHash>(
138 kind: Kind,
139 spec: SegmentBasic,
140 before: &(GlobalState, LocalState, ConnectingLedgerHash),
141 after: &(GlobalState, LocalState, ConnectingLedgerHash),
142 ) -> ZkappCommandIntermediateState<GlobalState, LocalState, ConnectingLedgerHash>
143 where
144 GlobalState: Clone,
145 LocalState: Clone,
146 ConnectingLedgerHash: Clone,
147 {
148 let (global_before, local_before, _) = before;
149 let (global_after, local_after, connecting_ledger) = after;
150 ZkappCommandIntermediateState {
151 kind,
152 spec,
153 state_before: State {
154 global: global_before.clone(),
155 local: local_before.clone(),
156 },
157 state_after: State {
158 global: global_after.clone(),
159 local: local_after.clone(),
160 },
161 connecting_ledger: connecting_ledger.clone(),
162 }
163 }
164
165 pub fn group_by_zkapp_command_rev<'a, I, GlobalState, LocalState, ConnectingLedgerHash>(
167 zkapp_command: I,
168 stmtss: Vec<Vec<(GlobalState, LocalState, ConnectingLedgerHash)>>,
169 ) -> Vec<ZkappCommandIntermediateState<GlobalState, LocalState, ConnectingLedgerHash>>
170 where
171 I: IntoIterator<Item = &'a ZkAppCommand>,
172 GlobalState: Clone,
173 LocalState: Clone,
174 ConnectingLedgerHash: Clone,
175 {
176 let all_account_updates_list = zkapp_command
177 .into_iter()
178 .map(|zkapp_command| zkapp_command.all_account_updates_list());
179
180 let zkapp_account_updatess = std::iter::once(vec![])
181 .chain(all_account_updates_list)
182 .collect::<Vec<_>>();
183
184 let mut acc = Vec::<
185 ZkappCommandIntermediateState<GlobalState, LocalState, ConnectingLedgerHash>,
186 >::with_capacity(32);
187
188 let zkapp_account_updatess = zkapp_account_updatess
190 .iter()
191 .map(|v| v.as_slice())
192 .collect::<Vec<_>>();
193 let stmtss = stmtss.iter().map(|v| v.as_slice()).collect::<Vec<_>>();
194
195 #[rustfmt::skip]
196 fn group_by_zkapp_command_rev_impl<G, L, C>(
197 zkapp_commands: &[&[AccountUpdate]],
198 stmtss: &[&[(G, L, C)]],
199 acc: &mut Vec<ZkappCommandIntermediateState<G, L, C>>,
200 )
201 where
202 G: Clone,
203 L: Clone,
204 C: Clone,
205 {
206 use Kind::{New, Same, TwoNew};
207 use Control::{Proof, Signature, NoneGiven};
208
209 let to_spec = |c: &Control| SegmentBasic::of_controls(&[c]);
210 let to_specs = |c1: &Control, c2: &Control| SegmentBasic::of_controls(&[c1, c2]);
211
212 fn prepend<'a, T: ?Sized>(value: &'a T, list: &[&'a T]) -> Vec<&'a T> {
213 let mut new_list = Vec::with_capacity(list.len() + 1);
215 new_list.push(value);
216 new_list.extend(list);
217 new_list
218 }
219
220 match (zkapp_commands, stmtss) {
223 ([] | [[]], [ _ ]) => {
224 },
226 ([[ AccountUpdate { authorization: a1, .. } ]], [[ before, after ]]) => {
227 acc.push(intermediate_state(Same, to_spec(a1), before, after));
229 }
230 ([[], [AccountUpdate { authorization: a1, .. }]], [[ _ ], [ before, after ]]) => {
231 acc.push(intermediate_state(New, to_spec(a1), before, after));
233 }
234 ([[AccountUpdate { authorization: a1 @ Proof(_), .. }, zkapp_command @ ..], zkapp_commands @ ..],
235 [stmts @ [ before, after, ..], stmtss @ .. ]
236 ) => {
237 let stmts = &stmts[1..];
239 let zkapp_commands = prepend(zkapp_command, zkapp_commands);
240 let stmtss = prepend(stmts, stmtss);
241
242 acc.push(intermediate_state(Same, to_spec(a1), before, after));
243 group_by_zkapp_command_rev_impl(zkapp_commands.as_slice(), stmtss.as_slice(), acc);
244 }
245 ([[], [AccountUpdate { authorization: a1 @ Proof(_), .. }, zkapp_command @ .. ], zkapp_commands @ ..],
246 [[ _ ], stmts @ [ before, after, ..], stmtss @ ..]
247 ) => {
248 let stmts = &stmts[1..];
250 let zkapp_commands = prepend(zkapp_command, zkapp_commands);
251 let stmtss = prepend(stmts, stmtss);
252
253 acc.push(intermediate_state(New, to_spec(a1), before, after));
254 group_by_zkapp_command_rev_impl(zkapp_commands.as_slice(), stmtss.as_slice(), acc);
255 }
256 ([zkapp_command @ [AccountUpdate { authorization: a1, .. }, AccountUpdate { authorization: Proof(_), .. }, ..], zkapp_commands @ ..],
257 [stmts @ [before, after, ..], stmtss @ ..]
258 ) => {
259 let stmts = &stmts[1..];
261 let zkapp_command = &zkapp_command[1..];
262 let zkapp_commands = prepend(zkapp_command, zkapp_commands);
263 let stmtss = prepend(stmts, stmtss);
264
265 acc.push(intermediate_state(Same, to_spec(a1), before, after));
266 group_by_zkapp_command_rev_impl(zkapp_commands.as_slice(), stmtss.as_slice(), acc);
267 }
268 (zkapp_commands @ [[AccountUpdate { authorization: a1, .. }], zkapp_command @ [], [AccountUpdate { authorization: Proof(_), .. }, ..], ..],
269 [stmts @ [before, after, ..], stmtss @ ..]
270 ) => {
271 let stmts = &stmts[1..];
273 let zkapp_commands = &zkapp_commands[2..];
274 let zkapp_commands = prepend(*zkapp_command, zkapp_commands);
275 let stmtss = prepend(stmts, stmtss);
276
277 acc.push(intermediate_state(Same, to_spec(a1), before, after));
278 group_by_zkapp_command_rev_impl(zkapp_commands.as_slice(), stmtss.as_slice(), acc);
279 }
280 ([[AccountUpdate { authorization: a1 @ (Signature(_) | NoneGiven), .. },
281 AccountUpdate { authorization: a2 @ (Signature(_) | NoneGiven), .. },
282 zkapp_command @ ..], zkapp_commands @ ..],
283 [stmts @ [before, _, after, ..], stmtss @ ..]
284 ) => {
285 let stmts = &stmts[2..];
287 let zkapp_commands = prepend(zkapp_command, zkapp_commands);
288 let stmtss = prepend(stmts, stmtss);
289
290 acc.push(intermediate_state(Same, to_specs(a1, a2), before, after));
291 group_by_zkapp_command_rev_impl(zkapp_commands.as_slice(), stmtss.as_slice(), acc);
292 }
293 ([[], zkapp_command @ [AccountUpdate { authorization: a1, .. }, AccountUpdate { authorization: Proof(_), .. }, ..], zkapp_commands @ ..],
294 [[ _ ], stmts @ [before, after, ..], stmtss @ ..]
295 ) => {
296 let stmts = &stmts[1..];
298 let zkapp_command = &zkapp_command[1..];
299 let zkapp_commands = prepend(zkapp_command, zkapp_commands);
300 let stmtss = prepend(stmts, stmtss);
301
302 acc.push(intermediate_state(New, to_spec(a1), before, after));
303 group_by_zkapp_command_rev_impl(zkapp_commands.as_slice(), stmtss.as_slice(), acc);
304 }
305 ([[], [AccountUpdate { authorization: a1 @ (Signature(_) | NoneGiven), .. },
306 AccountUpdate { authorization: a2 @ (Signature(_) | NoneGiven), .. },
307 zkapp_command @ ..], zkapp_commands @ ..],
308 [[ _ ], stmts @ [before, _, after, ..], stmtss @ ..] ) => {
309 let stmts = &stmts[2..];
311 let zkapp_commands = prepend(zkapp_command, zkapp_commands);
312 let stmtss = prepend(stmts, stmtss);
313
314 acc.push(intermediate_state(New, to_specs(a1, a2), before, after));
315 group_by_zkapp_command_rev_impl(zkapp_commands.as_slice(), stmtss.as_slice(), acc);
316 }
317 ([[AccountUpdate { authorization: a1 @ (Signature(_) | NoneGiven), .. }],
318 [AccountUpdate { authorization: a2 @ (Signature(_) | NoneGiven), .. }, zkapp_command @ ..],
319 zkapp_commands @ ..],
320 [[before, _after1], stmts @ [_before2, after, ..], stmtss @ .. ]
321 ) => {
322 let stmts = &stmts[1..];
324 let zkapp_commands = prepend(zkapp_command, zkapp_commands);
325 let stmtss = prepend(stmts, stmtss);
326
327 acc.push(intermediate_state(New, to_specs(a1, a2), before, after));
328 group_by_zkapp_command_rev_impl(zkapp_commands.as_slice(), stmtss.as_slice(), acc);
329 }
330 (zkapp_commands @ [[], [AccountUpdate { authorization: a1, .. }, zkapp_command @ ..],
331 [AccountUpdate { authorization: Proof(_), .. }, ..], ..],
332 stmtss @ [[ _ ], [before], stmts @ [after], _, ..]
333 ) => {
334 let stmtss = &stmtss[3..];
336 let zkapp_commands = &zkapp_commands[2..];
337 let zkapp_commands = prepend(zkapp_command, zkapp_commands);
338 let stmtss = prepend(*stmts, stmtss);
339
340 acc.push(intermediate_state(New, to_spec(a1), before, after));
341 group_by_zkapp_command_rev_impl(zkapp_commands.as_slice(), stmtss.as_slice(), acc);
342 }
343 ([[], [AccountUpdate { authorization: a1 @ (Signature(_) | NoneGiven), .. }],
344 [AccountUpdate { authorization: a2 @ (Signature(_) | NoneGiven), .. }, zkapp_command @ ..],
345 zkapp_commands @ ..],
346 [[ _ ], [before, _after1], stmts @ [_before2, after, ..], stmtss @ ..]
347 ) => {
348 let stmts = &stmts[1..];
350 let zkapp_commands = prepend(zkapp_command, zkapp_commands);
351 let stmtss = prepend(stmts, stmtss);
352
353 acc.push(intermediate_state(TwoNew, to_specs(a1, a2), before, after));
354 group_by_zkapp_command_rev_impl(zkapp_commands.as_slice(), stmtss.as_slice(), acc);
355 }
356 ([[AccountUpdate { authorization: a1, .. }]], [[before, after, ..], ..]) => {
357 acc.push(intermediate_state(Same, to_spec(a1), before, after));
359 }
360 ([[], [AccountUpdate { authorization: a1, .. }], [], ..], [[ _ ], [before, after, ..], ..]) => {
361 acc.push(intermediate_state(New, to_spec(a1), before, after));
363 }
364 _ => panic!("invalid")
365 }
366 }
367
368 group_by_zkapp_command_rev_impl(&zkapp_account_updatess, &stmtss, &mut acc);
369 acc
370 }
371}
372
373pub type StartData = StartDataSkeleton<
374 ZkAppCommand, bool, >;
377
378#[derive(Clone, Debug)]
379pub struct StartDataSkeleton<AccountUpdates, WillSucceed> {
380 pub account_updates: AccountUpdates,
381 pub memo_hash: Fp,
382 pub will_succeed: WillSucceed,
383}
384
385#[derive(Debug, Clone, PartialEq)]
387pub struct WithStackHash<T> {
388 pub elt: T,
389 pub stack_hash: Fp,
390}
391
392fn accumulate_call_stack_hashes(
393 hash_frame: impl Fn(&WithHash<StackFrame>) -> Fp,
394 frames: &[WithHash<StackFrame>],
395) -> Vec<WithStackHash<WithHash<StackFrame>>> {
396 match frames {
397 [] => vec![],
398 [f, fs @ ..] => {
399 let h_f = hash_frame(f);
400 let mut tl = accumulate_call_stack_hashes(hash_frame, fs);
401 let h_tl = match tl.as_slice() {
402 [] => Fp::zero(),
403 [t, ..] => t.stack_hash,
404 };
405
406 tl.insert(
407 0,
408 WithStackHash {
409 elt: f.clone(),
410 stack_hash: hash_with_kimchi(&MINA_ACCOUNT_UPDATE_CONS, &[h_f, h_tl]),
411 },
412 );
413
414 tl
415 }
416 }
417}
418
419pub type LocalStateForWitness = LocalStateSkeleton<
420 SparseLedger, StackFrame, WithHash<Vec<WithStackHash<WithHash<StackFrame>>>>, TransactionCommitment, Signed<Amount>, Vec<Vec<TransactionFailure>>, bool, Index, >;
429
430#[derive(Debug)]
431pub struct ZkappCommandSegmentWitness<'a> {
432 pub global_first_pass_ledger: SparseLedger,
433 pub global_second_pass_ledger: SparseLedger,
434 pub local_state_init: LocalStateForWitness,
435 pub start_zkapp_command: Vec<StartData>,
436 pub state_body: &'a v2::MinaStateProtocolStateBodyValueStableV2,
437 pub init_stack: Stack,
438 pub block_global_slot: Slot,
439}
440
441pub struct ZkappCommandsWithContext<'a> {
442 pub pending_coinbase_init_stack: pending_coinbase::Stack,
443 pub pending_coinbase_of_statement: pending_coinbase::StackState,
444 pub first_pass_ledger: SparseLedger,
445 pub second_pass_ledger: SparseLedger,
446 pub connecting_ledger_hash: Fp,
447 pub zkapp_command: &'a ZkAppCommand,
448}
449
450pub struct ZkappCommandWitnessesParams<'a> {
451 pub global_slot: Slot,
452 pub state_body: &'a v2::MinaStateProtocolStateBodyValueStableV2,
453 pub fee_excess: Signed<Amount>,
454 pub zkapp_commands_with_context: Vec<ZkappCommandsWithContext<'a>>,
455}
456
457pub fn zkapp_command_witnesses_exn(
458 params: ZkappCommandWitnessesParams<'_>,
459) -> Result<
460 Vec<(
461 ZkappCommandSegmentWitness<'_>,
462 group::SegmentBasic,
463 Statement<SokDigest>,
464 )>,
465 InvalidBigInt,
466> {
467 let ZkappCommandWitnessesParams {
468 global_slot,
469 state_body,
470 fee_excess,
471 zkapp_commands_with_context,
472 } = params;
473
474 let supply_increase = Signed::<Amount>::zero();
475 let state_view = protocol_state_body_view(state_body)?;
476
477 let (_, _, will_succeeds, mut states) = zkapp_commands_with_context.iter().fold(
478 (fee_excess, supply_increase, vec![], vec![]),
479 |acc, v| {
480 let (fee_excess, supply_increase, mut will_succeeds, mut statess) = acc;
481
482 let ZkappCommandsWithContext {
483 pending_coinbase_init_stack: _,
484 pending_coinbase_of_statement: _,
485 first_pass_ledger,
486 second_pass_ledger,
487 connecting_ledger_hash,
488 zkapp_command,
489 } = v;
490
491 let mut states = Vec::with_capacity(16);
492 let txn_applied = {
493 let partial_txn = first_pass_ledger
494 .clone()
495 .apply_zkapp_first_pass_unchecked_with_states(
496 &mut states,
497 global_slot,
498 &state_view,
499 fee_excess,
500 supply_increase,
501 second_pass_ledger,
502 zkapp_command,
503 )
504 .unwrap();
505
506 second_pass_ledger
507 .clone()
508 .apply_zkapp_second_pass_unchecked_with_states(&mut states, partial_txn)
509 .unwrap()
510 };
511
512 let will_succeed = txn_applied.command.status.is_applied();
513
514 let states_with_connecting_ledger = states
515 .iter()
516 .cloned()
517 .map(|(global, local)| (global, local, *connecting_ledger_hash))
518 .collect::<Vec<_>>();
519
520 let final_state = {
521 let (global_state, _local_state, _connecting_ledger) =
522 states_with_connecting_ledger.last().unwrap();
523 global_state
524 };
525
526 let fee_excess = final_state.fee_excess;
527 let supply_increase = final_state.supply_increase;
528 will_succeeds.push(will_succeed);
529 statess.push(states_with_connecting_ledger);
530
531 (fee_excess, supply_increase, will_succeeds, statess)
532 },
533 );
534
535 states.insert(0, vec![states[0][0].clone()]);
536 let states = group::group_by_zkapp_command_rev(
537 zkapp_commands_with_context.iter().map(|v| v.zkapp_command),
538 states,
539 );
540
541 let (mut commitment, mut full_commitment) = {
542 let LocalState {
543 transaction_commitment,
544 full_transaction_commitment,
545 ..
546 } = LocalState::dummy();
547 (
548 TransactionCommitment(transaction_commitment),
549 TransactionCommitment(full_transaction_commitment),
550 )
551 };
552
553 let remaining_zkapp_command = {
554 let zkapp_commands = zkapp_commands_with_context
555 .iter()
556 .zip(will_succeeds)
557 .map(|(v, will_succeed)| {
558 let ZkappCommandsWithContext {
559 pending_coinbase_init_stack,
560 pending_coinbase_of_statement,
561 first_pass_ledger: _,
562 second_pass_ledger: _,
563 connecting_ledger_hash: _,
564 zkapp_command,
565 } = v;
566
567 (
568 pending_coinbase_init_stack,
569 pending_coinbase_of_statement,
570 StartData {
571 account_updates: (*zkapp_command).clone(),
572 memo_hash: zkapp_command.memo.hash(),
573 will_succeed,
574 },
575 )
576 })
577 .collect::<Vec<_>>();
578
579 zkapp_commands
580 };
581 let mut remaining_zkapp_command = remaining_zkapp_command.as_slice();
582
583 let mut pending_coinbase_init_stack = Stack::empty();
584 let mut pending_coinbase_stack_state = StackState {
585 source: Stack::empty(),
586 target: Stack::empty(),
587 };
588
589 let w = Vec::with_capacity(32);
590 let result = states.into_iter().fold(w, |mut witnesses, s| {
591 let ZkappCommandIntermediateState {
592 kind,
593 spec,
594 state_before:
595 State {
596 global: source_global,
597 local: mut source_local,
598 },
599 state_after:
600 State {
601 global: target_global,
602 local: mut target_local,
603 },
604 connecting_ledger,
605 } = s;
606
607 source_local.failure_status_tbl = vec![];
608 target_local.failure_status_tbl = vec![];
609
610 let current_commitment = commitment;
611 let current_full_commitment = full_commitment;
612
613 let (
614 start_zkapp_command,
615 next_commitment,
616 next_full_commitment,
617 pending_coinbase_init_stack,
618 pending_coinbase_stack_state,
619 ) = {
620 type TC = TransactionCommitment;
621
622 let empty_if_last = |mk: Box<dyn Fn() -> (TC, TC) + '_>| -> (TC, TC) {
623 let calls = target_local.stack_frame.calls.0.as_slice();
624 let call_stack = target_local.call_stack.0.as_slice();
625
626 match (calls, call_stack) {
627 ([], []) => (TC::empty(), TC::empty()),
628 _ => mk(),
629 }
630 };
631
632 let mk_next_commitment = |zkapp_command: &ZkAppCommand| {
633 empty_if_last(Box::new(|| {
634 let next_commitment = zkapp_command.commitment();
635 let memo_hash = zkapp_command.memo.hash();
636 let fee_payer_hash =
637 AccountUpdate::of_fee_payer(zkapp_command.fee_payer.clone()).digest();
638 let next_full_commitment =
639 next_commitment.create_complete(memo_hash, fee_payer_hash);
640
641 (next_commitment, next_full_commitment)
642 }))
643 };
644
645 match kind {
646 group::Kind::Same => {
647 let (next_commitment, next_full_commitment) =
648 empty_if_last(Box::new(|| (current_commitment, current_full_commitment)));
649 (
650 Vec::new(),
651 next_commitment,
652 next_full_commitment,
653 pending_coinbase_init_stack.clone(),
654 pending_coinbase_stack_state.clone(),
655 )
656 }
657 group::Kind::New => match remaining_zkapp_command {
658 [v, rest @ ..] => {
659 let (
660 pending_coinbase_init_stack1,
661 pending_coinbase_stack_state1,
662 zkapp_command,
663 ) = v;
664
665 let (commitment2, full_commitment2) =
666 mk_next_commitment(&zkapp_command.account_updates);
667
668 remaining_zkapp_command = rest;
669 commitment = commitment2;
670 full_commitment = full_commitment2;
671 pending_coinbase_init_stack = (*pending_coinbase_init_stack1).clone();
672 pending_coinbase_stack_state = (*pending_coinbase_stack_state1).clone();
673
674 (
675 vec![zkapp_command.clone()],
676 commitment2,
677 full_commitment2,
678 pending_coinbase_init_stack.clone(),
679 pending_coinbase_stack_state.clone(),
680 )
681 }
682 _ => panic!("Not enough remaining zkapp_command"),
683 },
684 group::Kind::TwoNew => match remaining_zkapp_command {
685 [v1, v2, rest @ ..] => {
686 let (
687 pending_coinbase_init_stack1,
688 pending_coinbase_stack_state1,
689 zkapp_command1,
690 ) = v1;
691 let (
692 _pending_coinbase_init_stack2,
693 pending_coinbase_stack_state2,
694 zkapp_command2,
695 ) = v2;
696
697 let (commitment2, full_commitment2) =
698 mk_next_commitment(&zkapp_command2.account_updates);
699
700 remaining_zkapp_command = rest;
701 commitment = commitment2;
702 full_commitment = full_commitment2;
703 pending_coinbase_init_stack = (*pending_coinbase_init_stack1).clone();
704 pending_coinbase_stack_state = StackState {
705 target: pending_coinbase_stack_state2.target.clone(),
706 ..(*pending_coinbase_stack_state1).clone()
707 };
708
709 (
710 vec![zkapp_command1.clone(), zkapp_command2.clone()],
711 commitment2,
712 full_commitment2,
713 pending_coinbase_init_stack.clone(),
714 pending_coinbase_stack_state.clone(),
715 )
716 }
717 _ => panic!("Not enough remaining zkapp_command"),
718 },
719 }
720 };
721
722 let hash_local_state = |local: &LocalStateEnv<SparseLedger>| {
723 let call_stack = local
724 .call_stack
725 .iter()
726 .map(|v| WithHash::of_data(v.clone(), |v| v.digest()))
727 .collect::<Vec<_>>();
728 let call_stack = accumulate_call_stack_hashes(|x| x.hash, &call_stack);
729
730 let LocalStateEnv {
731 stack_frame,
732 call_stack: _,
733 transaction_commitment,
734 full_transaction_commitment,
735 excess,
736 supply_increase,
737 ledger,
738 success,
739 account_update_index,
740 failure_status_tbl,
741 will_succeed,
742 } = local.clone();
743
744 LocalStateForWitness {
745 stack_frame,
746 call_stack: WithHash {
747 data: call_stack,
748 hash: Fp::zero(), },
750 transaction_commitment: TransactionCommitment(transaction_commitment),
751 full_transaction_commitment: TransactionCommitment(full_transaction_commitment),
752 excess,
753 supply_increase,
754 ledger,
755 success,
756 account_update_index,
757 failure_status_tbl,
758 will_succeed,
759 }
760 };
761
762 let source_local = LocalStateForWitness {
763 transaction_commitment: current_commitment,
764 full_transaction_commitment: current_full_commitment,
765 ..hash_local_state(&source_local)
766 };
767
768 let target_local = LocalStateForWitness {
769 transaction_commitment: next_commitment,
770 full_transaction_commitment: next_full_commitment,
771 ..hash_local_state(&target_local)
772 };
773
774 let w = ZkappCommandSegmentWitness {
775 global_first_pass_ledger: source_global.first_pass_ledger.clone(),
776 global_second_pass_ledger: source_global.second_pass_ledger.clone(),
777 local_state_init: source_local.clone(),
778 start_zkapp_command,
779 state_body,
780 init_stack: pending_coinbase_init_stack,
781 block_global_slot: global_slot,
782 };
783
784 let fee_excess = {
785 let fee_excess = target_global
786 .fee_excess
787 .add(&source_global.fee_excess.negate())
788 .expect("unexpected fee excess");
789 FeeExcess {
790 fee_token_l: TokenId::default(),
791 fee_excess_l: fee_excess.to_fee(),
792 fee_token_r: TokenId::default(),
793 fee_excess_r: Signed::zero(),
794 }
795 };
796
797 let supply_increase = target_global
798 .supply_increase
799 .add(&source_global.supply_increase.negate())
800 .expect("unexpected supply increase");
801
802 let call_stack_hash = |s: &Vec<WithStackHash<WithHash<StackFrame>>>| {
803 s.first().map(|v| v.stack_hash).unwrap_or_else(Fp::zero)
804 };
805
806 let statement = {
807 let target_first_pass_ledger_root =
808 target_global.first_pass_ledger.clone().merkle_root();
809
810 let (source_local_ledger, target_local_ledger) = (
811 source_local.ledger.clone().merkle_root(),
812 target_local.ledger.clone().merkle_root(),
813 );
814
815 Statement::<SokDigest> {
816 source: Registers {
817 first_pass_ledger: source_global.first_pass_ledger.clone().merkle_root(),
818 second_pass_ledger: source_global.second_pass_ledger.clone().merkle_root(),
819 pending_coinbase_stack: pending_coinbase_stack_state.source.clone(),
820 local_state: {
821 let LocalStateForWitness {
822 stack_frame,
823 call_stack,
824 transaction_commitment,
825 full_transaction_commitment,
826 excess,
827 supply_increase,
828 ledger: _,
829 success,
830 account_update_index,
831 failure_status_tbl,
832 will_succeed,
833 } = source_local;
834
835 LocalState {
836 stack_frame: stack_frame.digest(),
837 call_stack: call_stack_hash(&call_stack),
838 transaction_commitment: transaction_commitment.0,
839 full_transaction_commitment: full_transaction_commitment.0,
840 ledger: source_local_ledger,
841 excess,
842 supply_increase,
843 success,
844 account_update_index,
845 failure_status_tbl,
846 will_succeed,
847 }
848 },
849 },
850 target: Registers {
851 first_pass_ledger: target_first_pass_ledger_root,
852 second_pass_ledger: target_global.second_pass_ledger.clone().merkle_root(),
853 pending_coinbase_stack: pending_coinbase_stack_state.target.clone(),
854 local_state: {
855 let LocalStateForWitness {
856 stack_frame,
857 call_stack,
858 transaction_commitment,
859 full_transaction_commitment,
860 excess,
861 supply_increase,
862 ledger: _,
863 success,
864 account_update_index,
865 failure_status_tbl,
866 will_succeed,
867 } = target_local;
868
869 LocalState {
870 stack_frame: stack_frame.digest(),
871 call_stack: call_stack_hash(&call_stack),
872 transaction_commitment: transaction_commitment.0,
873 full_transaction_commitment: full_transaction_commitment.0,
874 ledger: target_local_ledger,
875 excess,
876 supply_increase,
877 success,
878 account_update_index,
879 failure_status_tbl,
880 will_succeed,
881 }
882 },
883 },
884 connecting_ledger_left: connecting_ledger,
885 connecting_ledger_right: connecting_ledger,
886 supply_increase,
887 fee_excess,
888 sok_digest: SokDigest::default(),
889 }
890 };
891
892 witnesses.insert(0, (w, spec, statement));
893 witnesses
894 });
895
896 Ok(result)
897}
898
899#[derive(Clone, Debug)]
900pub enum IsStart {
901 Yes,
902 No,
903 ComputeInCircuit,
904}
905
906#[derive(Clone, Debug)]
907pub struct Spec {
908 pub auth_type: ControlTag,
909 pub is_start: IsStart,
910}
911
912fn basic_spec(s: &SegmentBasic) -> Box<[Spec]> {
913 let opt_signed = || Spec {
914 auth_type: ControlTag::Signature,
915 is_start: IsStart::ComputeInCircuit,
916 };
917
918 match s {
919 SegmentBasic::OptSignedOptSigned => Box::new([opt_signed(), opt_signed()]),
920 SegmentBasic::OptSigned => Box::new([opt_signed()]),
921 SegmentBasic::Proved => Box::new([Spec {
922 auth_type: ControlTag::Proof,
923 is_start: IsStart::No,
924 }]),
925 }
926}
927
928fn read_witnesses<F: FieldWitness>(path: &str) -> Vec<F> {
929 let f = std::fs::read_to_string(
930 std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
931 .join(devnet_circuit_directory())
932 .join("witnesses")
933 .join(path),
934 )
936 .unwrap();
937
938 let fps = f
939 .lines()
940 .filter(|s| !s.is_empty())
941 .map(|s| F::from_str(s).unwrap_or_else(|_| panic!()))
942 .collect::<Vec<_>>();
943
944 fps
945}
946
947struct CheckProtocolStateParams<'a> {
948 pending_coinbase_stack_init: Stack,
949 pending_coinbase_stack_before: Stack,
950 pending_coinbase_stack_after: Stack,
951 block_global_slot: CheckedSlot<Fp>,
952 state_body: &'a ProtocolStateBody,
953}
954
955fn check_protocol_state(params: CheckProtocolStateParams, w: &mut Witness<Fp>) {
956 let CheckProtocolStateParams {
957 pending_coinbase_stack_init,
958 pending_coinbase_stack_before,
959 pending_coinbase_stack_after,
960 block_global_slot,
961 state_body,
962 } = params;
963
964 let state_body_hash = state_body.checked_hash_with_param(&MINA_PROTO_STATE_BODY, w);
965 let global_slot = block_global_slot;
966 let computed_pending_coinbase_stack_after =
967 pending_coinbase_stack_init.checked_push_state(state_body_hash, global_slot, w);
968
969 let _correct_coinbase_target_stack =
970 computed_pending_coinbase_stack_after.equal_var(&pending_coinbase_stack_after, w);
971
972 let _valid_init_state = {
973 let equal_source = pending_coinbase_stack_init.equal_var(&pending_coinbase_stack_before, w);
974
975 let equal_source_with_state =
976 computed_pending_coinbase_stack_after.equal_var(&pending_coinbase_stack_before, w);
977
978 equal_source.or(&equal_source_with_state, w)
979 };
980}
981
982#[derive(Clone)]
984pub struct LedgerWithHash {
985 pub ledger: SparseLedger,
986 pub hash: Fp,
987}
988
989impl ToFieldElements<Fp> for LedgerWithHash {
990 fn to_field_elements(&self, fields: &mut Vec<Fp>) {
991 let Self { ledger: _, hash } = self;
992 hash.to_field_elements(fields);
993 }
994}
995
996impl Check<Fp> for LedgerWithHash {
997 fn check(&self, _w: &mut Witness<Fp>) {
998 }
1000}
1001
1002pub struct ZkappSingleData {
1003 spec: Spec,
1004 zkapp_input: Rc<RefCell<Option<ZkappStatement>>>,
1005 must_verify: Rc<RefCell<Boolean>>,
1006}
1007
1008impl ZkappSingleData {
1009 pub fn spec(&self) -> &Spec {
1010 &self.spec
1011 }
1012 pub fn set_zkapp_input(&self, x: ZkappStatement) {
1013 let mut zkapp_input = self.zkapp_input.borrow_mut();
1014 zkapp_input.replace(x);
1015 }
1016 pub fn set_must_verify(&self, x: Boolean) {
1017 let mut must_verify = self.must_verify.borrow_mut();
1018 *must_verify = x;
1019 }
1020}
1021
1022pub type LocalStateForProof = LocalStateSkeleton<
1023 LedgerWithHash, StackFrameChecked, WithHash<Vec<WithStackHash<WithHash<StackFrame>>>>, Fp, CheckedSigned<Fp, CheckedAmount<Fp>>, (), Boolean, CheckedIndex<Fp>, >;
1032
1033pub type GlobalStateForProof = GlobalStateSkeleton<
1034 LedgerWithHash, CheckedSigned<Fp, CheckedAmount<Fp>>, CheckedSlot<Fp>, >;
1038
1039pub type StartDataForProof = StartDataSkeleton<
1040 WithHash<CallForest<AccountUpdate>>, CircuitVar<Boolean>, >;
1043
1044fn zkapp_main(
1045 statement: Statement<SokDigest>,
1046 witness: &ZkappCommandSegmentWitness,
1047 spec: &[Spec],
1048 w: &mut Witness<Fp>,
1049) -> Result<(Option<ZkappStatement>, Boolean), InvalidBigInt> {
1050 w.exists(&statement);
1051
1052 dummy_constraints(w);
1053 let state_body: &ProtocolStateBody = &w.exists(witness.state_body.try_into()?);
1054 let block_global_slot = w.exists(witness.block_global_slot).to_checked();
1055 let pending_coinbase_stack_init = w.exists(witness.init_stack.clone());
1056
1057 check_protocol_state(
1058 CheckProtocolStateParams {
1059 pending_coinbase_stack_init,
1060 pending_coinbase_stack_before: statement.source.pending_coinbase_stack.clone(),
1061 pending_coinbase_stack_after: statement.target.pending_coinbase_stack.clone(),
1062 block_global_slot,
1063 state_body,
1064 },
1065 w,
1066 );
1067
1068 let (mut global, mut local) = {
1069 let g = GlobalStateForProof {
1070 first_pass_ledger: LedgerWithHash {
1071 ledger: witness.global_first_pass_ledger.clone(),
1072 hash: statement.source.first_pass_ledger,
1073 },
1074 second_pass_ledger: LedgerWithHash {
1075 ledger: witness.global_second_pass_ledger.clone(),
1076 hash: statement.source.second_pass_ledger,
1077 },
1078 fee_excess: CheckedSigned::constant_zero(),
1079 supply_increase: CheckedSigned::constant_zero(),
1080 protocol_state: state_body.view(),
1081 block_global_slot,
1082 };
1083
1084 let l = zkapp_logic::LocalState::<ZkappSnark> {
1085 stack_frame: witness
1086 .local_state_init
1087 .stack_frame
1088 .unhash(statement.source.local_state.stack_frame, w),
1089 call_stack: WithHash {
1090 hash: statement.source.local_state.call_stack,
1091 data: witness.local_state_init.call_stack.data.clone(),
1092 },
1093 transaction_commitment: statement.source.local_state.transaction_commitment,
1094 full_transaction_commitment: statement.source.local_state.full_transaction_commitment,
1095 excess: statement.source.local_state.excess.to_checked(),
1096 supply_increase: statement.source.local_state.supply_increase.to_checked(),
1097 ledger: LedgerWithHash {
1098 ledger: witness.local_state_init.ledger.copy_content(),
1099 hash: statement.source.local_state.ledger,
1100 },
1101 success: statement.source.local_state.success.to_boolean().var(),
1102 account_update_index: statement
1103 .source
1104 .local_state
1105 .account_update_index
1106 .to_checked(),
1107 failure_status_tbl: (),
1108 will_succeed: statement.source.local_state.will_succeed.to_boolean().var(),
1109 };
1110
1111 (g, l)
1112 };
1113
1114 let init_fee_excess = global.fee_excess.clone();
1115
1116 let mut start_zkapp_command = witness.start_zkapp_command.as_slice();
1117 let zkapp_input = Rc::new(RefCell::new(None));
1118 let must_verify = Rc::new(RefCell::new(Boolean::True));
1119
1120 spec.iter().rev().for_each(|account_update_spec| {
1121 enum StartOrSkip<T> {
1122 Start(T),
1123 Skip,
1124 }
1125
1126 let mut finish = |v: StartOrSkip<&StartData>, (global_state, local_state)| {
1127 let ps = match v {
1128 StartOrSkip::Skip => CallForest::empty(),
1129 StartOrSkip::Start(p) => p.account_updates.all_account_updates(),
1130 };
1131
1132 let h = w.exists(ps.hash());
1133
1134 let will_succeed = w.exists(match v {
1136 StartOrSkip::Start(p) => p.will_succeed.to_boolean().constant(),
1137 StartOrSkip::Skip => Boolean::False.constant(),
1138 });
1139 let memo_hash = w.exists(match v {
1140 StartOrSkip::Skip => Fp::zero(),
1141 StartOrSkip::Start(p) => p.memo_hash,
1142 });
1143
1144 let start_data = StartDataForProof {
1145 account_updates: WithHash { data: ps, hash: h },
1146 memo_hash,
1147 will_succeed,
1148 };
1149
1150 {
1151 let is_start = match account_update_spec.is_start {
1152 IsStart::Yes => zkapp_logic::IsStart::Yes(start_data),
1153 IsStart::No => zkapp_logic::IsStart::No,
1154 IsStart::ComputeInCircuit => zkapp_logic::IsStart::Compute(start_data),
1155 };
1156
1157 let single_data = ZkappSingleData {
1158 spec: account_update_spec.clone(),
1159 zkapp_input: Rc::clone(&zkapp_input),
1160 must_verify: Rc::clone(&must_verify),
1161 };
1162
1163 zkapp_logic::apply::<ZkappSnark>(
1164 ApplyZkappParams {
1165 is_start,
1166 global_state,
1167 local_state,
1168 single_data,
1169 },
1170 w,
1171 )
1172 }
1173 };
1174
1175 let new_acc = match account_update_spec.is_start {
1176 IsStart::No => {
1177 let is_start = zkapp_logic::IsStart::No;
1178
1179 let single_data = ZkappSingleData {
1180 spec: account_update_spec.clone(),
1181 zkapp_input: Rc::clone(&zkapp_input),
1182 must_verify: Rc::clone(&must_verify),
1183 };
1184
1185 zkapp_logic::apply::<ZkappSnark>(
1186 ApplyZkappParams {
1187 is_start,
1188 global_state: &mut global,
1189 local_state: &mut local,
1190 single_data,
1191 },
1192 w,
1193 )
1194 }
1195 IsStart::ComputeInCircuit => {
1196 let v = match start_zkapp_command {
1197 [] => StartOrSkip::Skip,
1198 [p, ps @ ..] => {
1199 let should_pop = local.stack_frame.data.calls.data.is_empty();
1200
1201 if should_pop {
1202 start_zkapp_command = ps;
1203 StartOrSkip::Start(p)
1204 } else {
1205 StartOrSkip::Skip
1206 }
1207 }
1208 };
1209 finish(v, (&mut global, &mut local))
1210 }
1211 IsStart::Yes => {
1212 assert!(local.stack_frame.data.calls.data.is_empty());
1213
1214 let v = match start_zkapp_command {
1215 [] => unreachable!(),
1216 [p, ps @ ..] => {
1217 start_zkapp_command = ps;
1218 StartOrSkip::Start(p)
1219 }
1220 };
1221 finish(v, (&mut global, &mut local))
1222 }
1223 };
1224
1225 new_acc.unwrap() });
1227
1228 let on_true = local.stack_frame.hash(w);
1229 let _local_state_ledger = w.exists_no_check(match local.success.as_boolean() {
1230 Boolean::True => on_true,
1231 Boolean::False => statement.target.local_state.stack_frame,
1232 });
1233
1234 w.exists_no_check(local.excess.force_value());
1237 w.exists_no_check(local.supply_increase.force_value());
1238
1239 w.exists_no_check(global.supply_increase.force_value());
1241
1242 w.exists_no_check(global.fee_excess.force_value());
1244 w.exists_no_check(init_fee_excess.force_value());
1245
1246 let zkapp_input = Rc::into_inner(zkapp_input).unwrap().into_inner();
1247 let must_verify = Rc::into_inner(must_verify).unwrap().into_inner();
1248
1249 Ok((zkapp_input, must_verify))
1250}
1251
1252pub struct LedgerProof {
1253 pub statement: Statement<SokDigest>,
1254 pub proof: WrapProof,
1255}
1256
1257impl From<&LedgerProof> for v2::LedgerProofProdStableV2 {
1258 fn from(value: &LedgerProof) -> Self {
1259 let LedgerProof { statement, proof } = value;
1260 Self(v2::TransactionSnarkStableV2 {
1261 statement: statement.into(),
1262 proof: v2::TransactionSnarkProofStableV2(proof.into()),
1263 })
1264 }
1265}
1266
1267fn first_account_update<'a>(witness: &'a ZkappCommandSegmentWitness) -> Option<&'a AccountUpdate> {
1268 match witness.local_state_init.stack_frame.calls.0.as_slice() {
1269 [] => witness
1270 .start_zkapp_command
1271 .iter()
1272 .find_map(|s| s.account_updates.account_updates.first())
1273 .map(|v| &v.elt.account_update),
1274 [first, ..] => Some(&first.elt.account_update),
1275 }
1276}
1277
1278fn account_update_proof(p: &AccountUpdate) -> Option<&v2::PicklesProofProofsVerifiedMaxStableV2> {
1279 match &p.authorization {
1280 Control::Proof(proof) => Some(proof),
1281 Control::Signature(_) | Control::NoneGiven => None,
1282 }
1283}
1284
1285fn snapp_proof_data<'a>(
1286 witness: &'a ZkappCommandSegmentWitness,
1287) -> Option<(
1288 &'a v2::PicklesProofProofsVerifiedMaxStableV2,
1289 crate::VerificationKey,
1290)> {
1291 let p = first_account_update(witness)?;
1292 let pi = account_update_proof(p)?;
1293 let vk = {
1294 let account_id = AccountId::create(p.body.public_key.clone(), p.body.token_id.clone());
1295 let addr = witness.local_state_init.ledger.find_index_exn(account_id);
1296 let account = witness.local_state_init.ledger.get_exn(&addr);
1297 account
1298 .zkapp
1299 .and_then(|z| z.verification_key)
1300 .expect("No verification key found in the account")
1301 };
1302 Some((pi, vk.vk().clone()))
1303}
1304
1305fn of_zkapp_command_segment_exn<StepConstants, WrapConstants>(
1306 statement: Statement<SokDigest>,
1307 zkapp_witness: &ZkappCommandSegmentWitness,
1308 spec: &SegmentBasic,
1309 step_prover: &Prover<Fp>,
1310 tx_wrap_prover: &Prover<Fq>,
1311 fps_path: Option<String>,
1312 fqs_path: Option<String>,
1313) -> anyhow::Result<LedgerProof>
1314where
1315 StepConstants: ProofConstants,
1316 WrapConstants: ProofConstants + ForWrapData,
1317{
1318 use SegmentBasic::*;
1319
1320 let s = basic_spec(spec);
1321 let mut w = Witness::new::<StepConstants>();
1322
1323 if let Some(path) = fps_path.as_ref() {
1324 w.ocaml_aux = read_witnesses(path);
1325 };
1326
1327 let (zkapp_input, must_verify) = zkapp_main(statement.clone(), zkapp_witness, &s, &mut w)?;
1328
1329 let StepProof {
1330 statement: step_statement,
1331 prev_evals,
1332 proof_with_public: proof,
1333 } = match spec {
1334 OptSignedOptSigned | OptSigned => step::<StepConstants, 0>(
1335 StepParams {
1336 app_state: Rc::new(statement.clone()),
1337 rule: InductiveRule::empty(),
1338 for_step_datas: [],
1339 indexes: [],
1340 prev_challenge_polynomial_commitments: vec![],
1341 hack_feature_flags: OptFlag::No,
1342 step_prover,
1343 wrap_prover: tx_wrap_prover,
1344 only_verify_constraints: false,
1345 },
1346 &mut w,
1347 )?,
1348 Proved => {
1349 let (proof, vk) = snapp_proof_data(zkapp_witness).unwrap();
1350 let proof = proof.into();
1351
1352 let dlog_plonk_index_cvar = vk.wrap_index.to_cvar(CircuitVar::Var);
1353 let verifier_index = make_zkapp_verifier_index(&vk);
1354
1355 let zkapp_data = make_step_zkapp_data(&dlog_plonk_index_cvar);
1356 let for_step_datas = [&zkapp_data];
1357
1358 let indexes = [(&verifier_index, &dlog_plonk_index_cvar)];
1359 let prev_challenge_polynomial_commitments = extract_recursion_challenges(&[&proof])?;
1360
1361 step::<StepConstants, 1>(
1362 StepParams {
1363 app_state: Rc::new(statement.clone()),
1364 rule: InductiveRule {
1365 previous_proof_statements: [PreviousProofStatement {
1366 public_input: Rc::new(zkapp_input.unwrap()),
1367 proof: &proof,
1368 proof_must_verify: must_verify.var(),
1369 }],
1370 public_output: (),
1371 auxiliary_output: (),
1372 },
1373 for_step_datas,
1374 indexes,
1375 prev_challenge_polynomial_commitments,
1376 hack_feature_flags: OptFlag::Maybe,
1377 step_prover,
1378 wrap_prover: tx_wrap_prover,
1379 only_verify_constraints: false,
1380 },
1381 &mut w,
1382 )?
1383 }
1384 };
1385
1386 let dlog_plonk_index = super::merge::dlog_plonk_index(tx_wrap_prover);
1387
1388 let mut w: Witness<Fq> = Witness::new::<WrapConstants>();
1389
1390 if let Some(path) = fqs_path.as_ref() {
1391 w.ocaml_aux = read_witnesses(path);
1392 };
1393
1394 let proof = wrap::wrap::<WrapConstants>(
1395 WrapParams {
1396 app_state: Rc::new(statement.clone()),
1397 proof_with_public: &proof,
1398 step_statement,
1399 prev_evals: &prev_evals,
1400 dlog_plonk_index: &dlog_plonk_index,
1401 step_prover_index: &step_prover.index,
1402 wrap_prover: tx_wrap_prover,
1403 },
1404 &mut w,
1405 )?;
1406
1407 Ok(LedgerProof { statement, proof })
1408}
1409
1410impl From<&WrapProof> for v2::PicklesProofProofsVerified2ReprStableV2 {
1411 fn from(value: &WrapProof) -> Self {
1412 let WrapProof {
1413 proof:
1414 ProofWithPublic {
1415 proof:
1416 kimchi::proof::ProverProof {
1417 commitments:
1418 kimchi::proof::ProverCommitments {
1419 w_comm,
1420 z_comm,
1421 t_comm,
1422 lookup,
1423 },
1424 proof:
1425 OpeningProof {
1426 lr,
1427 delta,
1428 z1,
1429 z2,
1430 sg,
1431 },
1432 evals,
1433 ft_eval1,
1434 prev_challenges: _,
1435 },
1436 public_input: _,
1437 },
1438 statement:
1439 WrapStatement {
1440 proof_state,
1441 messages_for_next_step_proof,
1442 },
1443 prev_evals,
1444 } = value;
1445
1446 assert!(lookup.is_none());
1447
1448 use mina_p2p_messages::{bigint::BigInt, pseq::PaddedSeq};
1449 use std::array;
1450
1451 let to_tuple = |g: &GroupAffine<Fp>| -> (BigInt, BigInt) { (g.x.into(), g.y.into()) };
1452
1453 v2::PicklesProofProofsVerified2ReprStableV2 {
1454 statement: v2::PicklesProofProofsVerified2ReprStableV2Statement {
1455 proof_state: {
1456 let WrapProofState {
1457 deferred_values:
1458 DeferredValues {
1459 plonk,
1460 combined_inner_product: _,
1461 b: _,
1462 xi: _,
1463 bulletproof_challenges,
1464 branch_data,
1465 },
1466 sponge_digest_before_evaluations,
1467 messages_for_next_wrap_proof:
1468 MessagesForNextWrapProof {
1469 challenge_polynomial_commitment,
1470 old_bulletproof_challenges,
1471 },
1472 } = proof_state;
1473
1474 v2::PicklesProofProofsVerified2ReprStableV2StatementProofState {
1475 deferred_values: {
1476 let to_padded = |v: [u64; 2]| -> PaddedSeq<
1477 v2::LimbVectorConstantHex64StableV1,
1478 2,
1479 > {
1480 use v2::LimbVectorConstantHex64StableV1 as V;
1481 PaddedSeq([V(v[0].into()), V(v[1].into())])
1482 };
1483
1484 v2::PicklesProofProofsVerified2ReprStableV2StatementProofStateDeferredValues {
1485 plonk: v2::PicklesProofProofsVerified2ReprStableV2StatementProofStateDeferredValuesPlonk {
1486 alpha: v2::PicklesReducedMessagesForNextProofOverSameFieldWrapChallengesVectorStableV2AChallenge {
1487 inner: to_padded(plonk.alpha),
1488 },
1489 beta: to_padded(plonk.beta),
1490 gamma: to_padded(plonk.gamma),
1491 zeta: v2::PicklesReducedMessagesForNextProofOverSameFieldWrapChallengesVectorStableV2AChallenge {
1492 inner: to_padded(plonk.zeta),
1493 },
1494 joint_combiner: plonk.lookup.map(|joint_combiner| {
1496 v2::PicklesReducedMessagesForNextProofOverSameFieldWrapChallengesVectorStableV2AChallenge {
1497 inner: to_padded(joint_combiner),
1498 }
1499 }),
1500 feature_flags: (&plonk.feature_flags).into(),
1501 },
1502 bulletproof_challenges: PaddedSeq(array::from_fn(|i| {
1503 v2::PicklesReducedMessagesForNextProofOverSameFieldWrapChallengesVectorStableV2A {
1504 prechallenge: v2::PicklesReducedMessagesForNextProofOverSameFieldWrapChallengesVectorStableV2AChallenge {
1505 inner: {
1506 let bigint: BigInteger256 = bulletproof_challenges[i].into();
1507 let bigint = bigint.0;
1508 PaddedSeq([v2::LimbVectorConstantHex64StableV1(bigint[0].into()), v2::LimbVectorConstantHex64StableV1(bigint[1].into())])
1509 },
1510 },
1511 }
1512 })),
1513 branch_data: branch_data.clone(),
1514 }
1515 },
1516 sponge_digest_before_evaluations:
1517 v2::CompositionTypesDigestConstantStableV1({
1518 let bigint: BigInteger256 =
1519 (*sponge_digest_before_evaluations).into();
1520 let bigint = bigint.0;
1521 PaddedSeq(
1522 bigint
1523 .each_ref()
1524 .map(|v| v2::LimbVectorConstantHex64StableV1(v.into())),
1525 )
1526 }),
1527 messages_for_next_wrap_proof:
1528 v2::PicklesProofProofsVerified2ReprStableV2MessagesForNextWrapProof {
1529 challenge_polynomial_commitment: {
1530 let GroupAffine::<Fq> { x, y, .. } =
1531 challenge_polynomial_commitment.to_affine();
1532 (x.into(), y.into())
1533 },
1534 old_bulletproof_challenges: PaddedSeq(array::from_fn(|i| {
1535 v2::PicklesReducedMessagesForNextProofOverSameFieldWrapChallengesVectorStableV2(
1536 PaddedSeq(array::from_fn(|j| v2::PicklesReducedMessagesForNextProofOverSameFieldWrapChallengesVectorStableV2A {
1537 prechallenge: v2::PicklesReducedMessagesForNextProofOverSameFieldWrapChallengesVectorStableV2AChallenge {
1538 inner: {
1539 let bigint: BigInteger256 = old_bulletproof_challenges[i][j].into();
1540 let bigint = bigint.0;
1541 PaddedSeq([v2::LimbVectorConstantHex64StableV1(bigint[0].into()), v2::LimbVectorConstantHex64StableV1(bigint[1].into())])
1542 },
1543 },
1544 }))
1545 )
1546 })),
1547 },
1548 }
1549 },
1550 messages_for_next_step_proof: {
1551 let ReducedMessagesForNextStepProof {
1552 app_state: _,
1553 challenge_polynomial_commitments,
1554 old_bulletproof_challenges,
1555 } = messages_for_next_step_proof;
1556
1557 v2::PicklesProofProofsVerified2ReprStableV2MessagesForNextStepProof {
1558 app_state: (),
1559 challenge_polynomial_commitments: challenge_polynomial_commitments.iter().map(|curve| {
1560 let GroupAffine::<Fp> { x, y, .. } = curve.to_affine();
1561 (x.into(), y.into())
1562 }).collect(),
1563 old_bulletproof_challenges: old_bulletproof_challenges.iter().map(|v| {
1564 PaddedSeq(array::from_fn(|i| v2::PicklesReducedMessagesForNextProofOverSameFieldWrapChallengesVectorStableV2A {
1565 prechallenge: v2::PicklesReducedMessagesForNextProofOverSameFieldWrapChallengesVectorStableV2AChallenge {
1566 inner: {
1567 let bigint: BigInteger256 = v[i].into();
1568 let bigint = bigint.0;
1569 PaddedSeq([v2::LimbVectorConstantHex64StableV1(bigint[0].into()), v2::LimbVectorConstantHex64StableV1(bigint[1].into())])
1570 },
1571 },
1572 }))
1573 }).collect(),
1574 }
1575 },
1576 },
1577 prev_evals: {
1578 let AllEvals {
1579 ft_eval1,
1580 evals:
1581 EvalsWithPublicInput {
1582 evals,
1583 public_input,
1584 },
1585 } = prev_evals;
1586
1587 v2::PicklesProofProofsVerified2ReprStableV2PrevEvals {
1588 evals: v2::PicklesProofProofsVerified2ReprStableV2PrevEvalsEvals {
1589 public_input: {
1590 let (a, b) = public_input;
1591 assert_eq!(a.len(), 1);
1592 assert_eq!(b.len(), 1);
1593 (a[0].into(), b[0].into())
1594 },
1595 evals: prev_evals_to_p2p(evals),
1596 },
1597 ft_eval1: ft_eval1.into(),
1598 }
1599 },
1600 proof: v2::PicklesWrapWireProofStableV1 {
1601 commitments: v2::PicklesWrapWireProofCommitmentsStableV1 {
1602 w_comm: PaddedSeq(w_comm.each_ref().map(|w| to_tuple(&w.chunks[0]))),
1603 z_comm: to_tuple(&z_comm.chunks[0]),
1604 t_comm: PaddedSeq(array::from_fn(|i| to_tuple(&t_comm.chunks[i]))),
1605 },
1606 evaluations: {
1607 let kimchi::proof::ProofEvaluations {
1608 w,
1609 z,
1610 s,
1611 coefficients,
1612 generic_selector,
1613 poseidon_selector,
1614 complete_add_selector,
1615 mul_selector,
1616 emul_selector,
1617 endomul_scalar_selector,
1618 ..
1619 } = evals;
1620
1621 let to_tuple = |point: &PointEvaluations<Vec<Fq>>| -> (BigInt, BigInt) {
1622 (point.zeta[0].into(), point.zeta_omega[0].into())
1623 };
1624
1625 v2::PicklesWrapWireProofEvaluationsStableV1 {
1626 w: PaddedSeq(w.each_ref().map(to_tuple)),
1627 coefficients: PaddedSeq(coefficients.each_ref().map(to_tuple)),
1628 z: to_tuple(z),
1629 s: PaddedSeq(s.each_ref().map(to_tuple)),
1630 generic_selector: to_tuple(generic_selector),
1631 poseidon_selector: to_tuple(poseidon_selector),
1632 complete_add_selector: to_tuple(complete_add_selector),
1633 mul_selector: to_tuple(mul_selector),
1634 emul_selector: to_tuple(emul_selector),
1635 endomul_scalar_selector: to_tuple(endomul_scalar_selector),
1636 }
1637 },
1638 ft_eval1: ft_eval1.into(),
1639 bulletproof: v2::PicklesWrapWireProofStableV1Bulletproof {
1640 lr: lr.iter().map(|(a, b)| (to_tuple(a), to_tuple(b))).collect(),
1641 z_1: z1.into(),
1642 z_2: z2.into(),
1643 delta: to_tuple(delta),
1644 challenge_polynomial_commitment: to_tuple(sg),
1645 },
1646 },
1647 }
1648 }
1649}
1650
1651fn of_zkapp_command_segment(
1652 statement: Statement<SokDigest>,
1653 zkapp_witness: &ZkappCommandSegmentWitness,
1654 spec: &SegmentBasic,
1655 step_opt_signed_opt_signed_prover: &Prover<Fp>,
1656 step_opt_signed_prover: &Prover<Fp>,
1657 step_proof_prover: &Prover<Fp>,
1658 tx_wrap_prover: &Prover<Fq>,
1659 opt_signed_path: Option<&str>,
1660 proved_path: Option<&str>,
1661) -> anyhow::Result<LedgerProof> {
1662 let (step_prover, step_path, wrap_path) = match spec {
1663 SegmentBasic::OptSignedOptSigned => (step_opt_signed_opt_signed_prover, None, None),
1664 SegmentBasic::OptSigned => {
1665 let fps_path = opt_signed_path.map(|p| format!("{}_fps.txt", p));
1666 let fqs_path = opt_signed_path.map(|p| format!("{}_fqs.txt", p));
1667 (step_opt_signed_prover, fps_path, fqs_path)
1668 }
1669 SegmentBasic::Proved => {
1670 let fps_path = proved_path.map(|p| format!("{}_fps.txt", p));
1671 let fqs_path = proved_path.map(|p| format!("{}_fqs.txt", p));
1672 (step_proof_prover, fps_path, fqs_path)
1673 }
1674 };
1675
1676 let of_zkapp_command_segment_exn = match spec {
1677 SegmentBasic::OptSignedOptSigned => {
1678 of_zkapp_command_segment_exn::<StepZkappOptSignedOptSignedProof, WrapZkappProof>
1679 }
1680 SegmentBasic::OptSigned => {
1681 of_zkapp_command_segment_exn::<StepZkappOptSignedProof, WrapZkappOptSignedProof>
1682 }
1683 SegmentBasic::Proved => {
1684 of_zkapp_command_segment_exn::<StepZkappProvedProof, WrapZkappProvedProof>
1685 }
1686 };
1687
1688 of_zkapp_command_segment_exn(
1689 statement,
1690 zkapp_witness,
1691 spec,
1692 step_prover,
1693 tx_wrap_prover,
1694 step_path,
1695 wrap_path,
1696 )
1697}
1698
1699pub fn generate_zkapp_proof(params: ZkappParams) -> anyhow::Result<LedgerProof> {
1700 let ZkappParams {
1701 statement,
1702 tx_witness,
1703 message,
1704 step_opt_signed_opt_signed_prover,
1705 step_opt_signed_prover,
1706 step_proof_prover,
1707 tx_wrap_prover,
1708 merge_step_prover,
1709 opt_signed_path,
1710 proved_path,
1711 } = params;
1712
1713 let zkapp = match &tx_witness.transaction {
1714 v2::MinaTransactionTransactionStableV2::Command(cmd) => {
1715 let v2::MinaBaseUserCommandStableV2::ZkappCommand(zkapp_command) = &**cmd else {
1716 unreachable!();
1717 };
1718 zkapp_command
1719 }
1720 _ => unreachable!(),
1721 };
1722 let zkapp_command: ZkAppCommand = zkapp.try_into()?;
1723
1724 let witnesses_specs_stmts = zkapp_command_witnesses_exn(ZkappCommandWitnessesParams {
1725 global_slot: Slot::from_u32(tx_witness.block_global_slot.as_u32()),
1726 state_body: &tx_witness.protocol_state_body,
1727 fee_excess: Signed::zero(),
1728 zkapp_commands_with_context: vec![ZkappCommandsWithContext {
1729 pending_coinbase_init_stack: (&tx_witness.init_stack).try_into()?,
1730 pending_coinbase_of_statement: pending_coinbase::StackState {
1731 source: (&statement.source.pending_coinbase_stack).try_into()?,
1732 target: (&statement.target.pending_coinbase_stack).try_into()?,
1733 },
1734 first_pass_ledger: (&tx_witness.first_pass_ledger).try_into()?,
1735 second_pass_ledger: (&tx_witness.second_pass_ledger).try_into()?,
1736 connecting_ledger_hash: statement.connecting_ledger_left.to_field()?,
1737 zkapp_command: &zkapp_command,
1738 }],
1739 })?;
1740
1741 let of_zkapp_command_segment = |statement: Statement<SokDigest>,
1742 zkapp_witness: &ZkappCommandSegmentWitness<'_>,
1743 spec: &SegmentBasic| {
1744 of_zkapp_command_segment(
1745 statement,
1746 zkapp_witness,
1747 spec,
1748 step_opt_signed_opt_signed_prover,
1749 step_opt_signed_prover,
1750 step_proof_prover,
1751 tx_wrap_prover,
1752 opt_signed_path,
1753 proved_path,
1754 )
1755 };
1756
1757 let sok_digest = message.digest();
1758 let mut witnesses_specs_stmts = witnesses_specs_stmts.into_iter().rev();
1759 let (zkapp_witness, spec, statement) = witnesses_specs_stmts.next().unwrap(); let first_proof = of_zkapp_command_segment(
1762 statement.with_digest(sok_digest.clone()),
1763 &zkapp_witness,
1764 &spec,
1765 );
1766
1767 witnesses_specs_stmts.fold(
1768 first_proof,
1769 |prev_proof, (zkapp_witness, spec, statement)| {
1770 let prev_proof = prev_proof?;
1771 let curr_proof = of_zkapp_command_segment(
1772 statement.with_digest(sok_digest.clone()),
1773 &zkapp_witness,
1774 &spec,
1775 )?;
1776
1777 merge_zkapp_proofs(
1778 prev_proof,
1779 curr_proof,
1780 message,
1781 merge_step_prover,
1782 tx_wrap_prover,
1783 )
1784 },
1785 )
1786}
1787
1788fn merge_zkapp_proofs(
1789 prev: LedgerProof,
1790 curr: LedgerProof,
1791 message: &SokMessage,
1792 merge_step_prover: &Prover<Fp>,
1793 tx_wrap_prover: &Prover<Fq>,
1794) -> anyhow::Result<LedgerProof> {
1795 let merged_statement = prev
1796 .statement
1797 .clone()
1798 .without_digest()
1799 .merge(&curr.statement.clone().without_digest())
1800 .unwrap();
1801
1802 let prev: v2::LedgerProofProdStableV2 = (&prev).into();
1803 let curr: v2::LedgerProofProdStableV2 = (&curr).into();
1804
1805 let mut w: Witness<Fp> = Witness::new::<StepMergeProof>();
1806
1807 let sok_digest = message.digest();
1808 let statement_with_sok = merged_statement.clone().with_digest(sok_digest);
1809
1810 let wrap_proof = generate_merge_proof(
1811 MergeParams {
1812 statement: merged_statement,
1813 proofs: &[prev, curr],
1814 message,
1815 step_prover: merge_step_prover,
1816 wrap_prover: tx_wrap_prover,
1817 only_verify_constraints: false,
1818 expected_step_proof: None,
1819 ocaml_wrap_witness: None,
1820 },
1821 &mut w,
1822 )?;
1823
1824 Ok(LedgerProof {
1825 statement: statement_with_sok,
1826 proof: wrap_proof,
1827 })
1828}