1use std::{cell::Cell, marker::PhantomData};
2
3use ark_ff::Zero;
4use mina_curves::pasta::Fp;
5use mina_signer::CompressedPubKey;
6use poseidon::hash::{
7 params::{
8 get_merkle_param_for_height, CODA_RECEIPT_UC, MINA_ACCOUNT_UPDATE_CONS,
9 MINA_ACCOUNT_UPDATE_NODE, MINA_ACCOUNT_UPDATE_STACK_FRAME_CONS, MINA_DERIVE_TOKEN_ID,
10 MINA_SIDELOADED_VK, MINA_ZKAPP_ACCOUNT, MINA_ZKAPP_SEQ_EVENTS,
11 },
12 Inputs,
13};
14
15use crate::{
16 checked_equal_compressed_key, checked_equal_compressed_key_const_and,
17 proofs::{
18 field::{field, Boolean, CircuitVar, FieldWitness, ToBoolean},
19 numbers::{
20 currency::{CheckedAmount, CheckedBalance, CheckedCurrency, CheckedSigned},
21 nat::{CheckedIndex, CheckedNat, CheckedSlot},
22 },
23 to_field_elements::ToFieldElements,
24 transaction::{
25 create_shifted_inner_curve, decompress_var,
26 transaction_snark::{check_timing, checked_chunked_signature_verify, checked_hash},
27 Check, InnerCurve,
28 },
29 witness::Witness,
30 zkapp::{GlobalStateForProof, LedgerWithHash, WithStackHash, ZkappSingleData},
31 },
32 scan_state::{
33 currency::{Amount, SlotSpan, TxnVersion},
34 transaction_logic::{
35 local_state::{StackFrame, StackFrameChecked, StackFrameCheckedFrame, WithLazyHash},
36 zkapp_command::{
37 self, AccountUpdate, AccountUpdateSkeleton, CallForest, CheckAuthorizationResult,
38 ClosedInterval, OrIgnore, SetOrKeep, WithHash,
39 },
40 zkapp_statement::ZkappStatement,
41 TimingValidation, TransactionFailure,
42 },
43 },
44 sparse_ledger::SparseLedger,
45 zkapps::zkapp_logic,
46 Account, AccountId, AppendToInputs, AuthRequired, AuthRequiredEncoded, MyCow, ReceiptChainHash,
47 ToInputs, TokenId, VerificationKeyWire, ZkAppAccount, TXN_VERSION_CURRENT,
48};
49
50use super::{
51 checks::{InSnarkOps, ZkappCheck},
52 intefaces::{
53 AccountIdInterface, AccountInterface, AccountUpdateInterface, ActionsInterface,
54 AmountInterface, BalanceInterface, BoolInterface, BranchEvaluation, BranchInterface,
55 BranchParam, CallForestInterface, CallStackInterface, ControllerInterface,
56 GlobalSlotSinceGenesisInterface, GlobalSlotSpanInterface, GlobalStateInterface,
57 IndexInterface, LedgerInterface, LocalStateInterface, Opt, ReceiptChainHashInterface,
58 SetOrKeepInterface, SignedAmountBranchParam, SignedAmountInterface, StackFrameInterface,
59 StackFrameMakeParams, StackInterface, TokenIdInterface, TransactionCommitmentInterface,
60 TxnVersionInterface, VerificationKeyHashInterface, WitnessGenerator, ZkappApplication,
61 ZkappHandler,
62 },
63};
64
65pub struct ZkappSnark;
66
67impl ZkappApplication for ZkappSnark {
68 type Ledger = LedgerWithHash;
69 type SignedAmount = CheckedSigned<Fp, CheckedAmount<Fp>>;
70 type Amount = SnarkAmount;
71 type Balance = SnarkBalance;
72 type Index = CheckedIndex<Fp>;
73 type GlobalSlotSinceGenesis = CheckedSlot<Fp>;
74 type StackFrame = StackFrameChecked;
75 type CallForest = WithHash<CallForest<AccountUpdate>>;
76 type CallStack = WithHash<Vec<WithStackHash<WithHash<StackFrame>>>>;
77 type GlobalState = GlobalStateForProof;
78 type AccountUpdate =
79 AccountUpdateSkeleton<WithHash<crate::scan_state::transaction_logic::zkapp_command::Body>>;
80 type AccountId = SnarkAccountId;
81 type TokenId = SnarkTokenId;
82 type Bool = CircuitVar<Boolean>;
83 type TransactionCommitment = SnarkTransactionCommitment;
84 type FailureStatusTable = ();
85 type LocalState = zkapp_logic::LocalState<Self>;
86 type Account = SnarkAccount;
87 type VerificationKeyHash = SnarkVerificationKeyHash;
88 type SingleData = ZkappSingleData;
89 type Controller = SnarkController;
90 type TxnVersion = SnarkTxnVersion;
91 type SetOrKeep = SnarkSetOrKeep;
92 type GlobalSlotSpan = SnarkGlobalSlotSpan;
93 type Actions = SnarkActions;
94 type ReceiptChainHash = SnarkReceiptChainHash;
95 type Handler = super::snark::SnarkHandler;
96 type Branch = SnarkBranch;
97 type WitnessGenerator = Witness<Fp>;
98}
99
100impl<F: FieldWitness> WitnessGenerator<F> for Witness<F> {
101 type Bool = SnarkBool;
102
103 fn exists<T>(&mut self, data: T) -> T
104 where
105 T: ToFieldElements<F> + Check<F>,
106 {
107 self.exists(data)
108 }
109 fn exists_no_check<T>(&mut self, data: T) -> T
110 where
111 T: ToFieldElements<F>,
112 {
113 self.exists_no_check(data)
114 }
115 fn exists_no_check_on_bool<T>(&mut self, b: Self::Bool, data: T) -> T
116 where
117 T: ToFieldElements<F>,
118 {
119 match b {
120 CircuitVar::Var(_) => self.exists_no_check(data),
121 CircuitVar::Constant(_) => data,
122 }
123 }
124}
125
126pub struct SnarkHandler;
127
128impl ZkappHandler for SnarkHandler {
129 type Z = ZkappSnark;
130 type AccountUpdate = SnarkAccountUpdate;
131 type Account = SnarkAccount;
132 type Bool = SnarkBool;
133 type W = Witness<Fp>;
134 type GlobalState = GlobalStateForProof;
135
136 fn check_account_precondition(
137 account_update: &Self::AccountUpdate,
138 account: &Self::Account,
139 new_account: Self::Bool,
140 local_state: &mut zkapp_logic::LocalState<ZkappSnark>,
141 w: &mut Self::W,
142 ) {
143 let check = |failure: TransactionFailure, b: Boolean, w: &mut Witness<Fp>| {
144 zkapp_logic::LocalState::<ZkappSnark>::add_check(local_state, failure, b.var(), w);
145 };
146 account_update
147 .body
148 .preconditions
149 .account
150 .zcheck::<InSnarkOps, _>(new_account.as_boolean(), &account.data, check, w);
151 }
152
153 fn check_protocol_state_precondition(
154 protocol_state_predicate: &zkapp_command::ZkAppPreconditions,
155 global_state: &mut Self::GlobalState,
156 w: &mut Self::W,
157 ) -> Self::Bool {
158 protocol_state_predicate
159 .zcheck::<InSnarkOps>(&global_state.protocol_state, w)
160 .var()
161 }
162
163 fn check_valid_while_precondition(
164 valid_while: &zkapp_command::Numeric<crate::scan_state::currency::Slot>,
165 global_state: &mut Self::GlobalState,
166 w: &mut Self::W,
167 ) -> Self::Bool {
168 (valid_while, ClosedInterval::min_max)
169 .zcheck::<InSnarkOps>(&global_state.block_global_slot.to_inner(), w)
170 .var()
171 }
172
173 fn init_account(account_update: &Self::AccountUpdate, account: Self::Account) -> Self::Account {
174 let AccountUpdateSkeleton {
175 body: account_update,
176 authorization: _,
177 } = account_update;
178 let account = Box::new(crate::Account {
179 public_key: account_update.data.public_key.clone(),
180 token_id: account_update.data.token_id.clone(),
181 ..*account.data
182 });
183 let account2 = account.clone();
184 let account = WithLazyHash::new(account, move |w: &mut Witness<Fp>| {
185 let zkapp = MyCow::borrow_or_default(&account2.zkapp);
186 zkapp.checked_hash_with_param(&MINA_ZKAPP_ACCOUNT, w);
187 account2.checked_hash(w)
188 });
189 account
190 }
191}
192
193impl SignedAmountInterface for CheckedSigned<Fp, CheckedAmount<Fp>> {
194 type W = Witness<Fp>;
195 type Bool = SnarkBool;
196 type Amount = SnarkAmount;
197
198 fn zero() -> Self {
199 CheckedSigned::of_unsigned(<CheckedAmount<_> as CheckedCurrency<Fp>>::zero())
200 }
201 fn is_neg(&self) -> Self::Bool {
202 CheckedSigned::is_neg(self).var()
203 }
204 fn equal(&self, other: &Self, w: &mut Self::W) -> Self::Bool {
205 CheckedSigned::const_equal(self, other, w).var()
206 }
207 fn is_non_neg(&self) -> Self::Bool {
208 CheckedSigned::is_pos(self).var()
209 }
210 fn negate(&self) -> Self {
211 CheckedSigned::negate(self.clone())
212 }
213 fn add_flagged(&self, other: &Self, w: &mut Self::W) -> (Self, Self::Bool) {
214 let (value, is_overflow) = CheckedSigned::add_flagged(self, other, w);
215 (value, is_overflow.var())
216 }
217 fn of_unsigned(unsigned: Self::Amount) -> Self {
218 Self::of_unsigned(unsigned)
219 }
220 fn on_if(b: Self::Bool, param: SignedAmountBranchParam<&Self>, w: &mut Self::W) -> Self {
221 let SignedAmountBranchParam { on_true, on_false } = param;
222
223 let amount = match b.as_boolean() {
224 Boolean::True => on_true,
225 Boolean::False => on_false,
226 };
227
228 let sgn = match (on_true.sgn, on_false.sgn) {
230 (CircuitVar::Constant(_), CircuitVar::Constant(_)) => {
231 CircuitVar::Var(*amount.sgn.value())
232 }
233 _ => CircuitVar::Var(w.exists_no_check(*amount.sgn.value())),
234 };
235 w.exists_no_check(&amount.magnitude);
236
237 let value = match (on_true.try_get_value(), on_false.try_get_value()) {
238 (Some(_), Some(_)) => Some(w.exists_no_check(amount.force_value())),
239 _ => None,
240 };
241 Self {
242 value: Cell::new(value),
243 sgn,
244 ..amount.clone()
245 }
246 }
247}
248
249impl AmountInterface for SnarkAmount {
250 type W = Witness<Fp>;
251 type Bool = SnarkBool;
252
253 fn zero() -> Self {
254 <CheckedAmount<_> as CheckedCurrency<Fp>>::zero()
255 }
256 fn of_constant_fee(fee: crate::scan_state::currency::Fee) -> Self {
257 Amount::of_fee(&fee).to_checked()
258 }
259}
260
261type SnarkAccountUpdate =
262 AccountUpdateSkeleton<WithHash<crate::scan_state::transaction_logic::zkapp_command::Body>>;
263type SnarkCallForest = WithHash<CallForest<AccountUpdate>>;
264
265impl CallForestInterface for SnarkCallForest {
266 type W = Witness<Fp>;
267 type AccountUpdate = SnarkAccountUpdate;
268 type Bool = SnarkBool;
269
270 fn empty() -> Self {
271 WithHash {
272 data: CallForest::empty(),
273 hash: Fp::zero(),
274 }
275 }
276 fn is_empty(&self, w: &mut Self::W) -> Self::Bool {
277 let Self { hash, data: _ } = self;
278 let empty = Fp::zero();
279 field::equal(empty, *hash, w).var()
280 }
281 fn pop_exn(&self, w: &mut Self::W) -> ((Self::AccountUpdate, Self), Self) {
282 let Self { data, hash: _ } = self;
283 let hd_r = &data.first().unwrap().elt;
284 let account_update = &hd_r.account_update;
285 let auth = &account_update.authorization;
286 let account_update = w.exists(&account_update.body);
287 let account_update = {
288 let account_update_hash_param =
289 openmina_core::NetworkConfig::global().account_update_hash_param;
290 let hash = account_update.checked_hash_with_param(account_update_hash_param, w);
291 WithHash {
292 data: account_update.clone(),
293 hash,
294 }
295 };
296 let subforest = {
297 let subforest = &hd_r.calls;
298 let subforest_hash = w.exists(subforest.hash());
299 WithHash {
300 data: subforest.clone(),
301 hash: subforest_hash,
302 }
303 };
304 let tl_hash = w.exists(match data.tail().unwrap() {
305 [] => Fp::zero(),
306 [x, ..] => x.stack_hash.get().unwrap(), });
308 let tree_hash = [account_update.hash, subforest.hash]
309 .checked_hash_with_param(&MINA_ACCOUNT_UPDATE_NODE, w);
310 let _hash_cons = [tree_hash, tl_hash].checked_hash_with_param(&MINA_ACCOUNT_UPDATE_CONS, w);
311 let account = Self::AccountUpdate {
312 body: account_update,
313 authorization: auth.clone(),
314 };
315
316 let popped: (Self::AccountUpdate, Self) = (account, subforest);
317 let tail: Self = WithHash {
318 data: CallForest(data.tail().unwrap().to_vec()),
319 hash: tl_hash,
320 };
321
322 (popped, tail)
323 }
324}
325
326impl StackFrameInterface for StackFrameChecked {
327 type Calls = SnarkCallForest;
328 type W = Witness<Fp>;
329 type Bool = SnarkBool;
330
331 fn caller(&self) -> crate::TokenId {
332 let Self {
333 data:
334 StackFrameCheckedFrame {
335 caller,
336 caller_caller: _,
337 calls: _,
338 is_default: _,
339 },
340 ..
341 } = self;
342 caller.clone()
343 }
344 fn caller_caller(&self) -> crate::TokenId {
345 let Self {
346 data:
347 StackFrameCheckedFrame {
348 caller: _,
349 caller_caller,
350 calls: _,
351 is_default: _,
352 },
353 ..
354 } = self;
355 caller_caller.clone()
356 }
357 fn calls(&self) -> &Self::Calls {
358 &self.calls
359 }
360 fn make(params: StackFrameMakeParams<'_, Self::Calls>) -> Self {
361 let StackFrameMakeParams {
362 caller,
363 caller_caller,
364 calls,
365 } = params;
366
367 let frame = StackFrameCheckedFrame {
368 caller,
369 caller_caller,
370 calls: calls.clone(),
371 is_default: false,
372 };
373 Self::of_frame(frame)
374 }
375 fn make_default(params: StackFrameMakeParams<'_, Self::Calls>) -> Self {
376 let StackFrameMakeParams {
377 caller,
378 caller_caller,
379 calls,
380 } = params;
381
382 let frame = StackFrameCheckedFrame {
383 caller,
384 caller_caller,
385 calls: calls.clone(),
386 is_default: true,
387 };
388 Self::of_frame(frame)
389 }
390 fn on_if<F: FnOnce(&mut Self::W) -> Self, F2: FnOnce(&mut Self::W) -> Self>(
391 b: Self::Bool,
392 param: BranchParam<Self, Self::W, F, F2>,
393 w: &mut Self::W,
394 ) -> Self {
395 let BranchParam { on_true, on_false } = param;
396 let on_true = on_true.eval(w);
397 let on_false = on_false.eval(w);
398
399 let data = match b.as_boolean() {
400 Boolean::True => on_true.data.clone(),
401 Boolean::False => on_false.data.clone(),
402 };
403 {
404 let frame: &StackFrameCheckedFrame = &data;
405 w.exists_no_check(frame);
406 }
407 WithLazyHash::new(data, move |w: &mut Witness<Fp>| {
408 let on_false = on_false.hash(w);
409 let on_true = on_true.hash(w);
410 w.exists_no_check(match b.as_boolean() {
411 Boolean::True => on_true,
412 Boolean::False => on_false,
413 })
414 })
415 }
416}
417
418fn call_stack_digest_checked_cons(h: Fp, t: Fp, w: &mut Witness<Fp>) -> Fp {
420 checked_hash(&MINA_ACCOUNT_UPDATE_STACK_FRAME_CONS, &[h, t], w)
421}
422
423impl StackInterface for WithHash<Vec<WithStackHash<WithHash<StackFrame>>>> {
424 type Elt = StackFrameChecked;
425 type W = Witness<Fp>;
426 type Bool = SnarkBool;
427
428 fn empty() -> Self {
429 WithHash {
430 data: Vec::new(),
431 hash: Fp::zero(),
432 }
433 }
434 fn is_empty(&self, w: &mut Self::W) -> Self::Bool {
435 let Self { hash, data: _ } = self;
436 let empty = Fp::zero();
437 field::equal(empty, *hash, w).var()
438 }
439 fn pop(&self, w: &mut Self::W) -> Opt<(Self::Elt, Self)> {
440 let Self { data, hash } = self;
441 let input_is_empty = self.is_empty(w);
442 let hd_r = match data.first() {
443 None => {
444 let data = StackFrame::default();
445 let hash = data.hash();
446 MyCow::Own(WithHash { data, hash })
447 }
448 Some(x) => MyCow::Borrow(&x.elt),
449 };
450 let tl_r = data.get(1..).unwrap_or(&[]);
451 let elt = hd_r.exists_elt(w);
452 let stack = w.exists(match tl_r {
453 [] => Fp::zero(),
454 [x, ..] => x.stack_hash,
455 });
456 let stack_frame_hash = elt.hash(w);
457 let h2 = call_stack_digest_checked_cons(stack_frame_hash, stack, w);
458 let is_equal = field::equal(*hash, h2, w);
459 Boolean::assert_any(&[input_is_empty.as_boolean(), is_equal], w);
460 Opt {
461 is_some: input_is_empty.neg().as_boolean(),
462 data: (
463 elt,
464 Self {
465 data: tl_r.to_vec(),
466 hash: stack,
467 },
468 ),
469 }
470 }
471 fn push(elt: Self::Elt, onto: Self, w: &mut Self::W) -> Self {
472 let Self {
473 data: r_tl,
474 hash: h_tl,
475 } = onto;
476
477 let h = call_stack_digest_checked_cons(elt.hash(w), h_tl, w);
478
479 let r = {
480 let hd = {
481 let frame = elt;
482 let hash = frame.hash(w);
483 let data = StackFrame {
484 caller: frame.data.caller,
485 caller_caller: frame.data.caller_caller,
486 calls: frame.data.calls.data,
487 };
488 WithHash { data, hash }
489 };
490 let tl = r_tl;
491
492 [WithStackHash {
493 elt: hd,
494 stack_hash: h,
495 }]
496 .into_iter()
497 .chain(tl)
498 .collect::<Vec<_>>()
499 };
500
501 Self { data: r, hash: h }
502 }
503}
504impl CallStackInterface for WithHash<Vec<WithStackHash<WithHash<StackFrame>>>> {
505 type StackFrame = StackFrameChecked;
506}
507
508impl GlobalStateInterface for GlobalStateForProof {
509 type Ledger = LedgerWithHash;
510 type W = Witness<Fp>;
511 type Bool = SnarkBool;
512 type SignedAmount = CheckedSigned<Fp, CheckedAmount<Fp>>;
513 type GlobalSlotSinceGenesis = SnarkGlobalSlot;
514
515 fn first_pass_ledger(&self) -> Self::Ledger {
516 self.first_pass_ledger.clone()
517 }
518 fn set_first_pass_ledger(
519 &mut self,
520 should_update: Self::Bool,
521 ledger: &Self::Ledger,
522 w: &mut Self::W,
523 ) {
524 let ledger = match should_update.as_boolean() {
525 Boolean::True => ledger.clone(),
526 Boolean::False => self.first_pass_ledger.clone(),
527 };
528 w.exists_no_check_on_bool(should_update, ledger.hash);
529 self.first_pass_ledger = ledger;
530 }
531 fn second_pass_ledger(&self) -> Self::Ledger {
532 self.second_pass_ledger.clone()
533 }
534 fn set_second_pass_ledger(
535 &mut self,
536 should_update: Self::Bool,
537 ledger: &Self::Ledger,
538 w: &mut Self::W,
539 ) {
540 let ledger = match should_update.as_boolean() {
541 Boolean::True => ledger.clone(),
542 Boolean::False => self.second_pass_ledger.clone(),
543 };
544 w.exists_no_check(ledger.hash);
545 self.second_pass_ledger = ledger;
546 }
547 fn fee_excess(&self) -> Self::SignedAmount {
548 self.fee_excess.clone()
549 }
550 fn supply_increase(&self) -> Self::SignedAmount {
551 self.supply_increase.clone()
552 }
553 fn set_fee_excess(&mut self, fee_excess: Self::SignedAmount) {
554 self.fee_excess = fee_excess;
555 }
556 fn set_supply_increase(&mut self, supply_increase: Self::SignedAmount) {
557 self.supply_increase = supply_increase;
558 }
559 fn block_global_slot(&self) -> Self::GlobalSlotSinceGenesis {
560 self.block_global_slot
561 }
562}
563
564pub type SnarkIndex = CheckedIndex<Fp>;
565
566impl IndexInterface for SnarkIndex {
567 fn zero() -> Self {
568 <CheckedIndex<Fp> as crate::proofs::numbers::nat::CheckedNat<_, 32>>::zero()
569 }
570 fn succ(&self) -> Self {
571 <CheckedIndex<Fp> as crate::proofs::numbers::nat::CheckedNat<_, 32>>::succ(self)
572 }
573}
574
575impl GlobalSlotSinceGenesisInterface for SnarkGlobalSlot {
576 type W = Witness<Fp>;
577 type Bool = SnarkBool;
578
579 fn equal(&self, other: &Self, w: &mut Self::W) -> Self::Bool {
580 <Self as CheckedNat<_, 32>>::equal(self, other, w).var()
581 }
582
583 fn exists_no_check(self, w: &mut Self::W) -> Self {
584 w.exists_no_check(self)
585 }
586}
587
588fn signature_verifies(
589 shifted: &InnerCurve<Fp>,
590 payload_digest: Fp,
591 signature: &mina_signer::Signature,
592 pk: &CompressedPubKey,
593 w: &mut Witness<Fp>,
594) -> Boolean {
595 let pk = decompress_var(pk, w);
596
597 let mut inputs = Inputs::new();
598 inputs.append_field(payload_digest);
599
600 checked_chunked_signature_verify(shifted, &pk, signature, inputs, w)
601}
602
603impl AccountUpdateInterface for SnarkAccountUpdate {
604 type W = Witness<Fp>;
605 type CallForest = SnarkCallForest;
606 type SingleData = ZkappSingleData;
607 type Bool = SnarkBool;
608 type SignedAmount = SnarkSignedAmount;
609 type VerificationKeyHash = SnarkVerificationKeyHash;
610
611 fn body(&self) -> &crate::scan_state::transaction_logic::zkapp_command::Body {
612 let Self {
613 body,
614 authorization: _,
615 } = self;
616 let WithHash { data, hash: _ } = body;
617 data
618 }
619 fn is_proved(&self) -> Self::Bool {
620 self.body()
621 .authorization_kind
622 .is_proved()
623 .to_boolean()
624 .var()
625 }
626 fn is_signed(&self) -> Self::Bool {
627 self.body()
628 .authorization_kind
629 .is_signed()
630 .to_boolean()
631 .var()
632 }
633 fn verification_key_hash(&self) -> Self::VerificationKeyHash {
634 let hash = self.body().authorization_kind.vk_hash();
635 SnarkVerificationKeyHash(hash)
636 }
637 fn check_authorization(
638 &self,
639 will_succeed: Self::Bool,
640 commitment: Fp,
641 calls: &Self::CallForest,
642 single_data: &Self::SingleData,
643 w: &mut Self::W,
644 ) -> CheckAuthorizationResult<Self::Bool> {
645 use crate::{
646 scan_state::transaction_logic::zkapp_statement::TransactionCommitment,
647 ControlTag::{NoneGiven, Proof, Signature},
648 };
649
650 let Self::CallForest {
651 data: _,
652 hash: calls,
653 } = calls;
654 let Self {
655 body: account_update,
656 authorization: control,
657 } = self;
658
659 let auth_type = single_data.spec().auth_type;
660 let proof_verifies = match auth_type {
661 Proof => {
662 let stmt = ZkappStatement {
663 account_update: TransactionCommitment(account_update.hash),
664 calls: TransactionCommitment(*calls),
665 };
666 single_data.set_zkapp_input(stmt);
667 single_data.set_must_verify(will_succeed.as_boolean());
668 Boolean::True.constant()
669 }
670 Signature | NoneGiven => Boolean::False.constant(),
671 };
672 let signature_verifies = match auth_type {
673 NoneGiven | Proof => Boolean::False.constant(),
674 Signature => {
675 use crate::scan_state::transaction_logic::zkapp_command::Control;
676 let signature = w.exists({
677 match control {
678 Control::Signature(s) => MyCow::Borrow(s),
679 Control::NoneGiven => MyCow::Own(mina_signer::Signature::dummy()),
680 Control::Proof(_) => unreachable!(),
681 }
682 });
683 let payload_digest = commitment;
684 let shifted = create_shifted_inner_curve(w);
685 signature_verifies(
686 &shifted,
687 payload_digest,
688 &signature,
689 &account_update.public_key,
690 w,
691 )
692 .var()
693 }
694 };
695 CheckAuthorizationResult {
696 proof_verifies,
697 signature_verifies,
698 }
699 }
700 fn increment_nonce(&self) -> Self::Bool {
701 self.body().increment_nonce.to_boolean().var()
702 }
703 fn use_full_commitment(&self) -> Self::Bool {
704 self.body().use_full_commitment.to_boolean().var()
705 }
706 fn account_precondition_nonce_is_constant(&self, w: &mut Self::W) -> Self::Bool {
707 let nonce = self.body().preconditions.account.nonce();
708 let (is_check, ClosedInterval { lower, upper }) = match nonce {
709 OrIgnore::Check(interval) => (Boolean::True, interval.clone()),
710 OrIgnore::Ignore => (Boolean::False, ClosedInterval::min_max()),
711 };
712 let is_constant = lower.to_checked().equal(&upper.to_checked(), w);
713 is_check.and(&is_constant, w).var()
714 }
715 fn implicit_account_creation_fee(&self) -> Self::Bool {
716 self.body().implicit_account_creation_fee.to_boolean().var()
717 }
718 fn balance_change(&self) -> Self::SignedAmount {
719 self.body().balance_change.to_checked()
720 }
721}
722
723impl LocalStateInterface for zkapp_logic::LocalState<ZkappSnark> {
724 type Z = ZkappSnark;
725 type Bool = SnarkBool;
726 type W = Witness<Fp>;
727
728 fn add_check(
729 local: &mut zkapp_logic::LocalState<Self::Z>,
730 _failure: TransactionFailure,
731 b: Self::Bool,
732 w: &mut Self::W,
733 ) {
734 local.success = local.success.and(&b, w);
735 }
736
737 fn add_new_failure_status_bucket(_local: &mut zkapp_logic::LocalState<Self::Z>) {
738 }
740}
741
742pub enum FlaggedOption<T> {
743 Some(T),
744 None,
745}
746
747impl<T> From<Option<T>> for FlaggedOption<T> {
748 fn from(value: Option<T>) -> Self {
749 match value {
750 Some(v) => Self::Some(v),
751 None => Self::None,
752 }
753 }
754}
755
756impl<T, F> Check<Fp> for (FlaggedOption<&T>, F)
757where
758 T: Check<Fp>,
759 F: Fn() -> T,
760{
761 fn check(&self, w: &mut Witness<Fp>) {
762 let (or_ignore, default_fn) = self;
763 let value = match or_ignore {
764 FlaggedOption::Some(this) => MyCow::Borrow(*this),
765 FlaggedOption::None => MyCow::Own(default_fn()),
766 };
767 value.check(w);
768 }
769}
770
771impl<T, F> ToFieldElements<Fp> for (FlaggedOption<&T>, F)
772where
773 T: ToFieldElements<Fp>,
774 F: Fn() -> T,
775{
776 fn to_field_elements(&self, fields: &mut Vec<Fp>) {
777 let (or_ignore, default_fn) = self;
778
779 match or_ignore {
780 FlaggedOption::Some(this) => {
781 Boolean::True.to_field_elements(fields);
782 this.to_field_elements(fields);
783 }
784 FlaggedOption::None => {
785 Boolean::False.to_field_elements(fields);
786 let default = default_fn();
787 default.to_field_elements(fields);
788 }
789 };
790 }
791}
792
793pub struct AccountUnhashed(pub Box<Account>);
796
797impl ToFieldElements<Fp> for AccountUnhashed {
798 fn to_field_elements(&self, fields: &mut Vec<Fp>) {
799 use crate::VotingFor;
800
801 let Account {
802 public_key,
803 token_id: TokenId(token_id),
804 token_symbol,
805 balance,
806 nonce,
807 receipt_chain_hash: ReceiptChainHash(receipt_chain_hash),
808 delegate,
809 voting_for: VotingFor(voting_for),
810 timing,
811 permissions,
812 zkapp,
813 } = &*self.0;
814
815 public_key.to_field_elements(fields);
817 token_id.to_field_elements(fields);
818 token_symbol.to_field_elements(fields);
819 balance.to_field_elements(fields);
820 nonce.to_field_elements(fields);
821 receipt_chain_hash.to_field_elements(fields);
822 let delegate = MyCow::borrow_or_else(delegate, CompressedPubKey::empty);
823 delegate.to_field_elements(fields);
824 voting_for.to_field_elements(fields);
825 timing.to_field_elements(fields);
826 permissions.to_field_elements(fields);
827 MyCow::borrow_or_else(zkapp, || crate::ZkAppAccount::default().into())
828 .to_field_elements(fields);
829 }
830}
831
832impl Check<Fp> for AccountUnhashed {
833 fn check(&self, w: &mut Witness<Fp>) {
834 let Account {
835 public_key: _,
836 token_id: _,
837 token_symbol,
838 balance,
839 nonce,
840 receipt_chain_hash: _,
841 delegate: _,
842 voting_for: _,
843 timing,
844 permissions,
845 zkapp,
846 } = &*self.0;
847
848 token_symbol.check(w);
849 balance.check(w);
850 nonce.check(w);
851 timing.check(w);
852 permissions.check(w);
853 (
854 FlaggedOption::from(zkapp.as_deref()),
855 crate::ZkAppAccount::default,
856 )
857 .check(w);
858 }
859}
860
861pub type SnarkAccount = WithLazyHash<Box<Account>>;
862
863impl AccountInterface for SnarkAccount {
864 type W = Witness<Fp>;
865 type D = ZkappSingleData;
866 type Bool = SnarkBool;
867 type Balance = SnarkBalance;
868 type GlobalSlot = SnarkGlobalSlot;
869 type VerificationKeyHash = SnarkVerificationKeyHash;
870
871 fn register_verification_key(&self, data: &Self::D, w: &mut Self::W) {
872 use crate::ControlTag::*;
873
874 match data.spec().auth_type {
875 Proof => {
876 let vk = self
877 .zkapp
878 .as_ref()
879 .unwrap()
880 .verification_key
881 .as_ref()
882 .unwrap();
883 let vk = w.exists(vk.vk());
884 vk.checked_hash_with_param(&MINA_SIDELOADED_VK, w);
885 }
886 Signature | NoneGiven => {}
887 }
888 }
889 fn get(&self) -> &crate::Account {
890 let Self { data, .. } = self;
891 data
892 }
893 fn get_mut(&mut self) -> &mut crate::Account {
894 let Self { data, .. } = self;
895 &mut *data
896 }
897 fn set_delegate(&mut self, new: CompressedPubKey) {
898 let Self { data: account, .. } = self;
899 account.delegate = if new == CompressedPubKey::empty() {
900 None
901 } else {
902 Some(new)
903 };
904 }
905 fn zkapp(&self) -> MyCow<'_, ZkAppAccount> {
906 match &self.zkapp {
907 Some(zkapp) => MyCow::Borrow(zkapp),
908 None => MyCow::Own(ZkAppAccount::default()),
909 }
910 }
911 fn zkapp_mut(&mut self) -> &mut ZkAppAccount {
912 self.data.zkapp.as_mut().unwrap()
914 }
915 fn verification_key_hash(&self) -> Self::VerificationKeyHash {
916 let zkapp = self.zkapp();
917 let hash = zkapp
918 .verification_key
919 .as_ref()
920 .map(VerificationKeyWire::hash)
921 .unwrap_or_else(VerificationKeyWire::dummy_hash);
922 SnarkVerificationKeyHash(hash)
923 }
924 fn set_token_id(&mut self, token_id: TokenId) {
925 let Self { data: account, .. } = self;
926 account.token_id = token_id;
927 }
928 fn is_timed(&self) -> Self::Bool {
929 let Self { data: account, .. } = self;
930 account.timing.is_timed().to_boolean().var()
931 }
932 fn balance(&self) -> Self::Balance {
933 let Self { data: account, .. } = self;
934 account.balance.to_checked()
935 }
936 fn set_balance(&mut self, balance: Self::Balance) {
937 self.data.balance = balance.to_inner(); }
939 fn check_timing(
940 &self,
941 txn_global_slot: &Self::GlobalSlot,
942 w: &mut Self::W,
943 ) -> (TimingValidation<Self::Bool>, crate::Timing) {
944 let mut invalid_timing = Option::<Boolean>::None;
945 let timed_balance_check = |b: Boolean, _w: &mut Witness<Fp>| {
946 invalid_timing = Some(b.neg());
947 };
948 let account = self.get();
949 let (_min_balance, timing) =
950 check_timing(account, None, *txn_global_slot, timed_balance_check, w);
951 (
952 TimingValidation::InvalidTiming(invalid_timing.unwrap().var()),
953 timing,
954 )
955 }
956 fn make_zkapp(&mut self) {
957 if self.data.zkapp.is_none() {
958 self.data.zkapp = Some(ZkAppAccount::default().into());
959 }
960 }
961 fn unmake_zkapp(&mut self) {
962 let Some(zkapp) = self.data.zkapp.as_mut() else {
963 panic!("invalid state"); };
965 if **zkapp == ZkAppAccount::default() {
966 self.data.zkapp = None;
967 }
968 }
969 fn proved_state(&self) -> Self::Bool {
970 let zkapp = self.zkapp.as_ref().unwrap(); zkapp.proved_state.to_boolean().var()
972 }
973 fn set_proved_state(&mut self, proved_state: Self::Bool) {
974 let zkapp = self.data.zkapp.as_mut().unwrap(); zkapp.proved_state = proved_state.as_boolean().as_bool();
976 }
977 fn app_state(&self) -> [Fp; 8] {
978 let zkapp = self.zkapp.as_ref().unwrap(); zkapp.app_state
980 }
981 fn last_action_slot(&self) -> Self::GlobalSlot {
982 let zkapp = self.zkapp.as_ref().unwrap(); zkapp.last_action_slot.to_checked()
984 }
985 fn set_last_action_slot(&mut self, slot: Self::GlobalSlot) {
986 let zkapp = self.data.zkapp.as_mut().unwrap(); zkapp.last_action_slot = slot.to_inner();
988 }
989}
990
991fn implied_root(account: &SnarkAccount, incl: &[(Boolean, Fp)], w: &mut Witness<Fp>) -> Fp {
992 incl.iter()
993 .enumerate()
994 .fold(account.hash(w), |accum: Fp, (height, (is_right, h))| {
995 let hashes = match is_right {
996 Boolean::False => [accum, *h],
997 Boolean::True => [*h, accum],
998 };
999 let param = get_merkle_param_for_height(height);
1000 w.exists(hashes);
1001 checked_hash(param, &hashes, w)
1002 })
1003}
1004
1005impl LedgerInterface for LedgerWithHash {
1006 type W = Witness<Fp>;
1007 type AccountUpdate = SnarkAccountUpdate;
1008 type Account = SnarkAccount;
1009 type Bool = SnarkBool;
1010 type InclusionProof = Vec<(Boolean, Fp)>;
1011
1012 fn empty(depth: usize) -> Self {
1013 let mut ledger = <SparseLedger as crate::sparse_ledger::LedgerIntf>::empty(depth);
1014 let hash = ledger.merkle_root();
1015 Self { ledger, hash }
1016 }
1017 fn get_account(
1018 &self,
1019 account_update: &Self::AccountUpdate,
1020 w: &mut Self::W,
1021 ) -> Result<(Self::Account, Self::InclusionProof), String> {
1022 let Self {
1023 ledger,
1024 hash: _root,
1025 } = self;
1026 let idx = ledger.find_index_exn(account_update.body.account_id());
1027 let account = w.exists(AccountUnhashed(ledger.get_exn(&idx)));
1028 let account2 = account.0.clone();
1030 let account = WithLazyHash::new(account.0, move |w: &mut Witness<Fp>| {
1031 let zkapp = MyCow::borrow_or_default(&account2.zkapp);
1032 zkapp.checked_hash_with_param(&MINA_ZKAPP_ACCOUNT, w);
1033 account2.checked_hash(w)
1034 });
1035 let inclusion = w.exists(
1036 ledger
1037 .clone()
1038 .path_exn(idx)
1039 .into_iter()
1040 .map(|path| match path {
1041 crate::MerklePath::Left(h) => (Boolean::False, h),
1042 crate::MerklePath::Right(h) => (Boolean::True, h),
1043 })
1044 .collect::<Vec<_>>(),
1045 );
1046 Ok((account, inclusion))
1047 }
1048 fn set_account(
1049 &mut self,
1050 (a, incl): (Self::Account, Self::InclusionProof),
1051 w: &mut Self::W,
1052 ) -> Result<(), String> {
1053 let Self { ledger, hash } = self;
1054 let new_hash = implied_root(&a, &incl, w);
1055 let idx = ledger.find_index_exn(a.id());
1056 ledger.set_exn(idx, a.data);
1057 *hash = new_hash;
1058 Ok(())
1059 }
1060 fn check_inclusion(
1061 &self,
1062 (account, incl): &(Self::Account, Self::InclusionProof),
1063 w: &mut Self::W,
1064 ) {
1065 implied_root(account, incl, w);
1066 }
1067 fn check_account(
1068 public_key: &mina_signer::CompressedPubKey,
1069 token_id: &TokenId,
1070 account: (&Self::Account, &Self::InclusionProof),
1071 w: &mut Self::W,
1072 ) -> Result<Self::Bool, String> {
1073 let (WithLazyHash { data: account, .. }, _) = account;
1074 let is_new = checked_equal_compressed_key_const_and(
1075 &account.public_key,
1076 &CompressedPubKey::empty(),
1077 w,
1078 );
1079 let is_same = checked_equal_compressed_key(public_key, &account.public_key, w);
1080 Boolean::assert_any(&[is_new, is_same], w);
1081 let is_same_token = field::equal(token_id.0, account.token_id.0, w);
1082 Boolean::assert_any(&[is_new, is_same_token], w);
1083 Ok(is_new.var())
1084 }
1085 fn exists_no_check(self, w: &mut Self::W) -> Self {
1086 w.exists_no_check(self.hash);
1087 self
1088 }
1089 fn exists_no_check_on_bool(self, b: Self::Bool, w: &mut Self::W) -> Self {
1090 w.exists_no_check_on_bool(b, self.hash);
1091 self
1092 }
1093}
1094
1095pub struct SnarkAccountId;
1096pub struct SnarkTokenId;
1097pub type SnarkBool = CircuitVar<Boolean>;
1098pub type SnarkAmount = CheckedAmount<Fp>;
1099pub type SnarkSignedAmount = CheckedSigned<Fp, CheckedAmount<Fp>>;
1100pub type SnarkBalance = CheckedBalance<Fp>;
1101pub struct SnarkTransactionCommitment;
1102pub struct SnarkVerificationKeyHash(Fp);
1103pub struct SnarkController;
1104pub struct SnarkTxnVersion;
1105pub struct SnarkSetOrKeep;
1106pub struct SnarkGlobalSlotSpan;
1107pub struct SnarkActions;
1108pub type SnarkGlobalSlot = CheckedSlot<Fp>;
1109pub struct SnarkReceiptChainHash;
1110
1111impl AccountIdInterface for SnarkAccountId {
1112 type W = Witness<Fp>;
1113
1114 fn derive_token_id(account_id: &AccountId, w: &mut Self::W) -> TokenId {
1115 TokenId(account_id.checked_hash_with_param(&MINA_DERIVE_TOKEN_ID, w))
1116 }
1117}
1118
1119impl TokenIdInterface for SnarkTokenId {
1120 type W = Witness<Fp>;
1121 type Bool = SnarkBool;
1122
1123 fn equal(a: &TokenId, b: &TokenId, w: &mut Self::W) -> Self::Bool {
1124 field::equal(a.0, b.0, w).var()
1125 }
1126}
1127
1128impl VerificationKeyHashInterface for SnarkVerificationKeyHash {
1129 type W = Witness<Fp>;
1130 type Bool = SnarkBool;
1131
1132 fn equal(a: &Self, b: &Self, w: &mut Self::W) -> Self::Bool {
1133 let Self(a) = a;
1134 let Self(b) = b;
1135 field::equal(*a, *b, w).var()
1136 }
1137}
1138
1139impl BoolInterface for SnarkBool {
1140 type W = Witness<Fp>;
1141 type FailureStatusTable = ();
1142
1143 fn as_boolean(&self) -> Boolean {
1144 self.as_boolean()
1145 }
1146 fn of_boolean(b: Boolean) -> Self {
1147 CircuitVar::Var(b)
1148 }
1149 fn true_() -> Self {
1150 CircuitVar::Constant(Boolean::True)
1151 }
1152 fn false_() -> Self {
1153 CircuitVar::Constant(Boolean::False)
1154 }
1155 fn neg(&self) -> Self {
1156 self.neg()
1157 }
1158 fn or(a: Self, b: Self, w: &mut Self::W) -> Self {
1159 a.or(&b, w)
1160 }
1161 fn and(a: Self, b: Self, w: &mut Self::W) -> Self {
1162 a.and(&b, w)
1163 }
1164 fn equal(a: Self, b: Self, w: &mut Self::W) -> Self {
1165 a.equal_bool(&b, w)
1166 }
1167 fn all(bs: &[Self], w: &mut Self::W) -> Self {
1168 SnarkBool::all(bs, w)
1169 }
1170 fn assert_any(bs: &[Self], w: &mut Self::W) -> Result<(), String> {
1171 SnarkBool::assert_any::<Fp>(bs, w);
1172 Ok(())
1173 }
1174 fn assert_with_failure_status_tbl(
1175 _b: Self,
1176 _table: &Self::FailureStatusTable,
1177 ) -> Result<(), String> {
1178 Ok(())
1179 }
1180}
1181
1182impl BalanceInterface for SnarkBalance {
1183 type W = Witness<Fp>;
1184 type Bool = SnarkBool;
1185 type Amount = SnarkAmount;
1186 type SignedAmount = SnarkSignedAmount;
1187
1188 fn add_signed_amount_flagged(
1189 &self,
1190 signed_amount: Self::SignedAmount,
1191 w: &mut Self::W,
1192 ) -> (Self, Self::Bool) {
1193 let (balance, failed) = SnarkBalance::add_signed_amount_flagged(self, signed_amount, w);
1194 (balance, failed.var())
1195 }
1196}
1197
1198impl TransactionCommitmentInterface for SnarkTransactionCommitment {
1199 type AccountUpdate = SnarkAccountUpdate;
1200 type CallForest = SnarkCallForest;
1201 type W = Witness<Fp>;
1202
1203 fn empty() -> Fp {
1204 Fp::zero()
1205 }
1206
1207 fn commitment(account_updates: &Self::CallForest) -> Fp {
1208 let Self::CallForest {
1209 data: _,
1210 hash: account_updates_hash,
1211 } = account_updates;
1212 *account_updates_hash
1213 }
1214
1215 fn full_commitment(
1216 account_updates: &Self::AccountUpdate,
1217 memo_hash: Fp,
1218 commitment: Fp,
1219 w: &mut Self::W,
1220 ) -> Fp {
1221 let fee_payer_hash = account_updates.body.hash;
1222
1223 [memo_hash, fee_payer_hash, commitment]
1224 .checked_hash_with_param(&MINA_ACCOUNT_UPDATE_CONS, w)
1225 }
1226}
1227
1228fn encode_auth(auth: &AuthRequired) -> AuthRequiredEncoded<CircuitVar<Boolean>> {
1229 let AuthRequiredEncoded {
1230 constant,
1231 signature_necessary,
1232 signature_sufficient,
1233 } = auth.encode();
1234
1235 AuthRequiredEncoded {
1236 constant: constant.to_boolean().var(),
1237 signature_necessary: signature_necessary.to_boolean().var(),
1238 signature_sufficient: signature_sufficient.to_boolean().var(),
1239 }
1240}
1241
1242fn eval_no_proof(
1244 auth: &AuthRequired,
1245 signature_verifies: SnarkBool,
1246 w: &mut Witness<Fp>,
1247) -> SnarkBool {
1248 let AuthRequiredEncoded {
1249 constant,
1250 signature_necessary: _,
1251 signature_sufficient,
1252 } = encode_auth(auth);
1253
1254 let a = constant.neg().and(&signature_verifies, w);
1255 let b = constant.or(&a, w);
1256 signature_sufficient.and(&b, w)
1257}
1258
1259fn eval_proof(auth: &AuthRequired, w: &mut Witness<Fp>) -> SnarkBool {
1260 let AuthRequiredEncoded {
1261 constant,
1262 signature_necessary,
1263 signature_sufficient,
1264 } = encode_auth(auth);
1265
1266 let impossible = constant.and(&signature_sufficient.neg(), w);
1267 signature_necessary.neg().and(&impossible.neg(), w)
1268}
1269
1270fn verification_key_perm_fallback_to_signature_with_older_version(
1271 auth: &AuthRequired,
1272 w: &mut Witness<Fp>,
1273) -> AuthRequired {
1274 let AuthRequiredEncoded {
1275 signature_sufficient,
1276 ..
1277 } = encode_auth(auth);
1278
1279 let on_true = SnarkBranch::make(w, |_| AuthRequired::Signature);
1280 let on_false = SnarkBranch::make(w, |_| *auth);
1281
1282 w.on_if(
1283 signature_sufficient.neg(),
1284 BranchParam { on_true, on_false },
1285 )
1286}
1287
1288impl ControllerInterface for SnarkController {
1289 type W = Witness<Fp>;
1290 type Bool = SnarkBool;
1291 type SingleData = ZkappSingleData;
1292
1293 fn check(
1294 _proof_verifies: Self::Bool,
1295 signature_verifies: Self::Bool,
1296 auth: &AuthRequired,
1297 single_data: &Self::SingleData,
1298 w: &mut Self::W,
1299 ) -> Result<Self::Bool, String> {
1300 use crate::ControlTag::{NoneGiven, Proof, Signature};
1301
1302 Ok(match single_data.spec().auth_type {
1303 Proof => eval_proof(auth, w),
1304 Signature | NoneGiven => eval_no_proof(auth, signature_verifies, w),
1305 })
1306 }
1307
1308 fn verification_key_perm_fallback_to_signature_with_older_version(
1309 auth: &AuthRequired,
1310 w: &mut Self::W,
1311 ) -> AuthRequired {
1312 verification_key_perm_fallback_to_signature_with_older_version(auth, w)
1313 }
1314}
1315
1316impl TxnVersionInterface for SnarkTxnVersion {
1317 type W = Witness<Fp>;
1318 type Bool = SnarkBool;
1319
1320 fn equal_to_current(version: TxnVersion, w: &mut Self::W) -> Self::Bool {
1321 let current = TXN_VERSION_CURRENT.to_checked();
1322 let version = version.to_checked();
1323 version.equal(¤t, w).var()
1324 }
1325
1326 fn older_than_current(version: TxnVersion, w: &mut Self::W) -> Self::Bool {
1327 let current = TXN_VERSION_CURRENT.to_checked();
1328 let version = version.to_checked();
1329 version.less_than(¤t, w).var()
1330 }
1331}
1332
1333impl SetOrKeepInterface for SnarkSetOrKeep {
1334 type Bool = SnarkBool;
1335
1336 fn is_keep<T: Clone>(set_or_keep: &SetOrKeep<T>) -> Self::Bool {
1337 match set_or_keep {
1338 SetOrKeep::Set(_) => CircuitVar::Var(Boolean::False),
1339 SetOrKeep::Keep => CircuitVar::Var(Boolean::True),
1340 }
1341 }
1342 fn is_set<T: Clone>(set_or_keep: &SetOrKeep<T>) -> Self::Bool {
1343 match set_or_keep {
1344 SetOrKeep::Set(_) => CircuitVar::Var(Boolean::True),
1345 SetOrKeep::Keep => CircuitVar::Var(Boolean::False),
1346 }
1347 }
1348}
1349
1350impl GlobalSlotSpanInterface for SnarkGlobalSlotSpan {
1351 type W = Witness<Fp>;
1352 type Bool = SnarkBool;
1353 type SlotSpan = SlotSpan;
1354
1355 fn greater_than(this: &Self::SlotSpan, other: &Self::SlotSpan, w: &mut Self::W) -> Self::Bool {
1356 let this = this.to_checked::<Fp>();
1357 let other = other.to_checked::<Fp>();
1358
1359 this.const_greater_than(&other, w).var()
1360 }
1361}
1362
1363impl ActionsInterface for SnarkActions {
1364 type W = Witness<Fp>;
1365 type Bool = SnarkBool;
1366
1367 fn is_empty(actions: &zkapp_command::Actions, w: &mut Self::W) -> Self::Bool {
1368 use zkapp_command::MakeEvents;
1369
1370 let hash = zkapp_command::events_to_field(actions);
1371 field::equal(hash, zkapp_command::Actions::empty_hash(), w).var()
1372 }
1373
1374 fn push_events(event: Fp, actions: &zkapp_command::Actions, w: &mut Self::W) -> Fp {
1375 let hash = zkapp_command::events_to_field(actions);
1376 checked_hash(&MINA_ZKAPP_SEQ_EVENTS, &[event, hash], w)
1377 }
1378}
1379
1380impl ReceiptChainHashInterface for SnarkReceiptChainHash {
1381 type W = Witness<Fp>;
1382 type Index = SnarkIndex;
1383
1384 fn cons_zkapp_command_commitment(
1385 index: Self::Index,
1386 element: Fp,
1387 other: ReceiptChainHash,
1388 w: &mut Self::W,
1389 ) -> ReceiptChainHash {
1390 let mut inputs = Inputs::new();
1391
1392 inputs.append(&index);
1393 inputs.append_field(element);
1394 inputs.append(&other);
1395
1396 ReceiptChainHash(checked_hash(&CODA_RECEIPT_UC, &inputs.to_fields(), w))
1397 }
1398}
1399
1400pub struct SnarkBranch;
1401
1402impl BranchInterface for SnarkBranch {
1403 type W = Witness<Fp>;
1404
1405 fn make<T, F>(w: &mut Self::W, run: F) -> BranchEvaluation<T, Self::W, F>
1406 where
1407 F: FnOnce(&mut Self::W) -> T,
1408 {
1409 BranchEvaluation::Evaluated(run(w), PhantomData)
1411 }
1412}