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