mina_tree/zkapps/
snark.rs

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        // TODO: This should be moved in a `Sgn::on_if`
229        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(), // Never fail, it was already hashed
307        });
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
418/// Call_stack_digest.Checked.cons
419fn 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        // nothing
739    }
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
793// dummy_vk_hash
794
795pub 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        // Important: Any changes here probably needs the same changes in `Account`
816        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        // `unwrap`: `make_zkapp` is supposed to be called before `zkapp_mut`
913        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(); // TODO: Overflow ?
938    }
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"); // `unmake_zkapp` must be called after `make_zkapp`
964        };
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(); // `make_zkapp` was already call
971        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(); // `make_zkapp` was already call
975        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(); // `make_zkapp` was already call
979        zkapp.app_state
980    }
981    fn last_action_slot(&self) -> Self::GlobalSlot {
982        let zkapp = self.zkapp.as_ref().unwrap(); // `make_zkapp` was already call
983        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(); // `make_zkapp` was already call
987        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        // TODO: Don't clone here
1029        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
1242// TODO: Dedup with the one in `account.rs`
1243fn 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(&current, 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(&current, 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        // We run the closure as soon as `SnarkBranch::make` is called
1410        BranchEvaluation::Evaluated(run(w), PhantomData)
1411    }
1412}