mina_tree/scan_state/transaction_logic/zkapp_command/
mod.rs

1use super::{
2    protocol_state::{self, ProtocolStateView},
3    zkapp_statement::TransactionCommitment,
4    Memo, TransactionFailure, TransactionStatus, WithStatus,
5};
6use crate::{
7    dummy, gen_compressed, gen_keypair,
8    hash::AppendToInputs,
9    proofs::{
10        field::{Boolean, ToBoolean},
11        to_field_elements::ToFieldElements,
12        transaction::Check,
13        witness::Witness,
14    },
15    scan_state::{
16        currency::{
17            Amount, Balance, Fee, Length, Magnitude, MinMax, Nonce, Sgn, Signed, Slot, SlotSpan,
18        },
19        fee_excess::FeeExcess,
20        GenesisConstant, GENESIS_CONSTANT,
21    },
22    zkapps::checks::{ZkappCheck, ZkappCheckOps},
23    AccountId, AuthRequired, ControlTag, MutableFp, MyCow, Permissions, SetVerificationKey,
24    ToInputs, TokenId, TokenSymbol, VerificationKey, VerificationKeyWire, VotingFor, ZkAppAccount,
25    ZkAppUri,
26};
27use ark_ff::{UniformRand, Zero};
28use itertools::Itertools;
29use mina_curves::pasta::Fp;
30use mina_p2p_messages::v2::MinaBaseZkappCommandTStableV1WireStableV1AccountUpdatesA;
31use mina_signer::{CompressedPubKey, Signature};
32use poseidon::hash::{
33    hash_noinputs, hash_with_kimchi,
34    params::{
35        MINA_ACCOUNT_UPDATE_CONS, MINA_ACCOUNT_UPDATE_NODE, MINA_ZKAPP_EVENT, MINA_ZKAPP_EVENTS,
36        MINA_ZKAPP_SEQ_EVENTS, NO_INPUT_MINA_ZKAPP_ACTIONS_EMPTY, NO_INPUT_MINA_ZKAPP_EVENTS_EMPTY,
37    },
38    Inputs,
39};
40use rand::{seq::SliceRandom, Rng};
41use std::sync::Arc;
42
43pub mod from_applied_sequence;
44pub mod from_unapplied_sequence;
45pub mod valid;
46pub mod verifiable;
47pub mod zkapp_weight;
48
49#[derive(Debug, Clone, PartialEq)]
50pub struct Event(pub Vec<Fp>);
51
52impl Event {
53    pub fn empty() -> Self {
54        Self(Vec::new())
55    }
56
57    pub fn hash(&self) -> Fp {
58        hash_with_kimchi(&MINA_ZKAPP_EVENT, &self.0[..])
59    }
60
61    pub fn len(&self) -> usize {
62        let Self(list) = self;
63        list.len()
64    }
65}
66
67/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/account_update.ml#L834>
68#[derive(Debug, Clone, PartialEq)]
69pub struct Events(pub Vec<Event>);
70
71/// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_account.ml#L155>
72#[derive(Debug, Clone, PartialEq)]
73pub struct Actions(pub Vec<Event>);
74
75pub fn gen_events() -> Vec<Event> {
76    let mut rng = rand::thread_rng();
77
78    let n = rng.gen_range(0..=5);
79
80    (0..=n)
81        .map(|_| {
82            let n = rng.gen_range(0..=3);
83            let event = (0..=n).map(|_| Fp::rand(&mut rng)).collect();
84            Event(event)
85        })
86        .collect()
87}
88
89use poseidon::hash::LazyParam;
90
91/// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_account.ml#L23>
92pub trait MakeEvents {
93    const DERIVER_NAME: (); // Unused here for now
94
95    fn get_salt_phrase() -> &'static LazyParam;
96
97    fn get_hash_prefix() -> &'static LazyParam;
98
99    fn events(&self) -> &[Event];
100
101    fn empty_hash() -> Fp;
102}
103
104/// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_account.ml#L100>
105impl MakeEvents for Events {
106    const DERIVER_NAME: () = ();
107
108    fn get_salt_phrase() -> &'static LazyParam {
109        &NO_INPUT_MINA_ZKAPP_EVENTS_EMPTY
110    }
111
112    fn get_hash_prefix() -> &'static poseidon::hash::LazyParam {
113        &MINA_ZKAPP_EVENTS
114    }
115
116    fn events(&self) -> &[Event] {
117        self.0.as_slice()
118    }
119
120    fn empty_hash() -> Fp {
121        cache_one!(Fp, events_to_field(&Events::empty()))
122    }
123}
124
125/// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_account.ml#L156>
126impl MakeEvents for Actions {
127    const DERIVER_NAME: () = ();
128
129    fn get_salt_phrase() -> &'static LazyParam {
130        &NO_INPUT_MINA_ZKAPP_ACTIONS_EMPTY
131    }
132
133    fn get_hash_prefix() -> &'static poseidon::hash::LazyParam {
134        &MINA_ZKAPP_SEQ_EVENTS
135    }
136
137    fn events(&self) -> &[Event] {
138        self.0.as_slice()
139    }
140
141    fn empty_hash() -> Fp {
142        cache_one!(Fp, events_to_field(&Actions::empty()))
143    }
144}
145
146/// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_account.ml#L52>
147pub fn events_to_field<E>(e: &E) -> Fp
148where
149    E: MakeEvents,
150{
151    let init = hash_noinputs(E::get_salt_phrase());
152
153    e.events().iter().rfold(init, |accum, elem| {
154        hash_with_kimchi(E::get_hash_prefix(), &[accum, elem.hash()])
155    })
156}
157
158impl ToInputs for Events {
159    fn to_inputs(&self, inputs: &mut Inputs) {
160        inputs.append(&events_to_field(self));
161    }
162}
163
164impl ToInputs for Actions {
165    fn to_inputs(&self, inputs: &mut Inputs) {
166        inputs.append(&events_to_field(self));
167    }
168}
169
170impl ToFieldElements<Fp> for Events {
171    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
172        events_to_field(self).to_field_elements(fields);
173    }
174}
175
176impl ToFieldElements<Fp> for Actions {
177    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
178        events_to_field(self).to_field_elements(fields);
179    }
180}
181
182/// Note: It's a different one than in the normal `Account`
183///
184/// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/account_update.ml#L163>
185#[derive(Clone, Debug, PartialEq, Eq)]
186pub struct Timing {
187    pub initial_minimum_balance: Balance,
188    pub cliff_time: Slot,
189    pub cliff_amount: Amount,
190    pub vesting_period: SlotSpan,
191    pub vesting_increment: Amount,
192}
193
194impl Timing {
195    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/account_update.ml#L208>
196    fn dummy() -> Self {
197        Self {
198            initial_minimum_balance: Balance::zero(),
199            cliff_time: Slot::zero(),
200            cliff_amount: Amount::zero(),
201            vesting_period: SlotSpan::zero(),
202            vesting_increment: Amount::zero(),
203        }
204    }
205
206    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/transaction_logic/mina_transaction_logic.ml#L1278>
207    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/account_update.ml#L228>
208    pub fn of_account_timing(timing: crate::account::Timing) -> Option<Self> {
209        match timing {
210            crate::Timing::Untimed => None,
211            crate::Timing::Timed {
212                initial_minimum_balance,
213                cliff_time,
214                cliff_amount,
215                vesting_period,
216                vesting_increment,
217            } => Some(Self {
218                initial_minimum_balance,
219                cliff_time,
220                cliff_amount,
221                vesting_period,
222                vesting_increment,
223            }),
224        }
225    }
226
227    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/account_update.ml#L219>
228    pub fn to_account_timing(self) -> crate::account::Timing {
229        let Self {
230            initial_minimum_balance,
231            cliff_time,
232            cliff_amount,
233            vesting_period,
234            vesting_increment,
235        } = self;
236
237        crate::account::Timing::Timed {
238            initial_minimum_balance,
239            cliff_time,
240            cliff_amount,
241            vesting_period,
242            vesting_increment,
243        }
244    }
245}
246
247impl ToFieldElements<Fp> for Timing {
248    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
249        let Self {
250            initial_minimum_balance,
251            cliff_time,
252            cliff_amount,
253            vesting_period,
254            vesting_increment,
255        } = self;
256
257        initial_minimum_balance.to_field_elements(fields);
258        cliff_time.to_field_elements(fields);
259        cliff_amount.to_field_elements(fields);
260        vesting_period.to_field_elements(fields);
261        vesting_increment.to_field_elements(fields);
262    }
263}
264
265impl Check<Fp> for Timing {
266    fn check(&self, w: &mut Witness<Fp>) {
267        let Self {
268            initial_minimum_balance,
269            cliff_time,
270            cliff_amount,
271            vesting_period,
272            vesting_increment,
273        } = self;
274
275        initial_minimum_balance.check(w);
276        cliff_time.check(w);
277        cliff_amount.check(w);
278        vesting_period.check(w);
279        vesting_increment.check(w);
280    }
281}
282
283impl ToInputs for Timing {
284    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/account_update.ml#L199>
285    fn to_inputs(&self, inputs: &mut Inputs) {
286        let Timing {
287            initial_minimum_balance,
288            cliff_time,
289            cliff_amount,
290            vesting_period,
291            vesting_increment,
292        } = self;
293
294        inputs.append_u64(initial_minimum_balance.as_u64());
295        inputs.append_u32(cliff_time.as_u32());
296        inputs.append_u64(cliff_amount.as_u64());
297        inputs.append_u32(vesting_period.as_u32());
298        inputs.append_u64(vesting_increment.as_u64());
299    }
300}
301
302impl Events {
303    pub fn empty() -> Self {
304        Self(Vec::new())
305    }
306
307    pub fn is_empty(&self) -> bool {
308        self.0.is_empty()
309    }
310
311    pub fn push_event(acc: Fp, event: Event) -> Fp {
312        hash_with_kimchi(Self::get_hash_prefix(), &[acc, event.hash()])
313    }
314
315    pub fn push_events(&self, acc: Fp) -> Fp {
316        let hash = self
317            .0
318            .iter()
319            .rfold(hash_noinputs(Self::get_salt_phrase()), |acc, e| {
320                Self::push_event(acc, e.clone())
321            });
322        hash_with_kimchi(Self::get_hash_prefix(), &[acc, hash])
323    }
324}
325
326impl Actions {
327    pub fn empty() -> Self {
328        Self(Vec::new())
329    }
330
331    pub fn is_empty(&self) -> bool {
332        self.0.is_empty()
333    }
334
335    pub fn push_event(acc: Fp, event: Event) -> Fp {
336        hash_with_kimchi(Self::get_hash_prefix(), &[acc, event.hash()])
337    }
338
339    pub fn push_events(&self, acc: Fp) -> Fp {
340        let hash = self
341            .0
342            .iter()
343            .rfold(hash_noinputs(Self::get_salt_phrase()), |acc, e| {
344                Self::push_event(acc, e.clone())
345            });
346        hash_with_kimchi(Self::get_hash_prefix(), &[acc, hash])
347    }
348}
349
350/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/zkapp_basic.ml#L100>
351#[derive(Debug, Clone, PartialEq, Eq)]
352pub enum SetOrKeep<T: Clone> {
353    Set(T),
354    Keep,
355}
356
357impl<T: Clone> SetOrKeep<T> {
358    fn map<'a, F, U>(&'a self, fun: F) -> SetOrKeep<U>
359    where
360        F: FnOnce(&'a T) -> U,
361        U: Clone,
362    {
363        match self {
364            SetOrKeep::Set(v) => SetOrKeep::Set(fun(v)),
365            SetOrKeep::Keep => SetOrKeep::Keep,
366        }
367    }
368
369    pub fn into_map<F, U>(self, fun: F) -> SetOrKeep<U>
370    where
371        F: FnOnce(T) -> U,
372        U: Clone,
373    {
374        match self {
375            SetOrKeep::Set(v) => SetOrKeep::Set(fun(v)),
376            SetOrKeep::Keep => SetOrKeep::Keep,
377        }
378    }
379
380    pub fn set_or_keep(&self, x: T) -> T {
381        match self {
382            Self::Set(data) => data.clone(),
383            Self::Keep => x,
384        }
385    }
386
387    pub fn is_keep(&self) -> bool {
388        match self {
389            Self::Keep => true,
390            Self::Set(_) => false,
391        }
392    }
393
394    pub fn is_set(&self) -> bool {
395        !self.is_keep()
396    }
397
398    pub fn gen<F>(mut fun: F) -> Self
399    where
400        F: FnMut() -> T,
401    {
402        let mut rng = rand::thread_rng();
403
404        if rng.gen() {
405            Self::Set(fun())
406        } else {
407            Self::Keep
408        }
409    }
410}
411
412impl<T, F> ToInputs for (&SetOrKeep<T>, F)
413where
414    T: ToInputs,
415    T: Clone,
416    F: Fn() -> T,
417{
418    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_basic.ml#L223>
419    fn to_inputs(&self, inputs: &mut Inputs) {
420        let (set_or_keep, default_fn) = self;
421
422        match set_or_keep {
423            SetOrKeep::Set(this) => {
424                inputs.append_bool(true);
425                this.to_inputs(inputs);
426            }
427            SetOrKeep::Keep => {
428                inputs.append_bool(false);
429                let default = default_fn();
430                default.to_inputs(inputs);
431            }
432        }
433    }
434}
435
436impl<T, F> ToFieldElements<Fp> for (&SetOrKeep<T>, F)
437where
438    T: ToFieldElements<Fp>,
439    T: Clone,
440    F: Fn() -> T,
441{
442    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
443        let (set_or_keep, default_fn) = self;
444
445        match set_or_keep {
446            SetOrKeep::Set(this) => {
447                Boolean::True.to_field_elements(fields);
448                this.to_field_elements(fields);
449            }
450            SetOrKeep::Keep => {
451                Boolean::False.to_field_elements(fields);
452                let default = default_fn();
453                default.to_field_elements(fields);
454            }
455        }
456    }
457}
458
459impl<T, F> Check<Fp> for (&SetOrKeep<T>, F)
460where
461    T: Check<Fp>,
462    T: Clone,
463    F: Fn() -> T,
464{
465    fn check(&self, w: &mut Witness<Fp>) {
466        let (set_or_keep, default_fn) = self;
467        let value = match set_or_keep {
468            SetOrKeep::Set(this) => MyCow::Borrow(this),
469            SetOrKeep::Keep => MyCow::Own(default_fn()),
470        };
471        value.check(w);
472    }
473}
474
475#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
476pub struct WithHash<T, H = Fp> {
477    pub data: T,
478    pub hash: H,
479}
480
481impl<T, H: Ord> Ord for WithHash<T, H> {
482    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
483        self.hash.cmp(&other.hash)
484    }
485}
486
487impl<T, H: PartialOrd> PartialOrd for WithHash<T, H> {
488    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
489        self.hash.partial_cmp(&other.hash)
490    }
491}
492
493impl<T, H: Eq> Eq for WithHash<T, H> {}
494
495impl<T, H: PartialEq> PartialEq for WithHash<T, H> {
496    fn eq(&self, other: &Self) -> bool {
497        self.hash == other.hash
498    }
499}
500
501impl<T, Hash: std::hash::Hash> std::hash::Hash for WithHash<T, Hash> {
502    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
503        let Self { data: _, hash } = self;
504        hash.hash(state);
505    }
506}
507
508impl<T> ToFieldElements<Fp> for WithHash<T> {
509    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
510        let Self { data: _, hash } = self;
511        hash.to_field_elements(fields);
512    }
513}
514
515impl<T> std::ops::Deref for WithHash<T> {
516    type Target = T;
517
518    fn deref(&self) -> &Self::Target {
519        &self.data
520    }
521}
522
523impl<T> WithHash<T> {
524    pub fn of_data(data: T, hash_data: impl Fn(&T) -> Fp) -> Self {
525        let hash = hash_data(&data);
526        Self { data, hash }
527    }
528}
529
530/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/account_update.ml#L319>
531#[derive(Debug, Clone, PartialEq, Eq)]
532pub struct Update {
533    pub app_state: [SetOrKeep<Fp>; 8],
534    pub delegate: SetOrKeep<CompressedPubKey>,
535    pub verification_key: SetOrKeep<VerificationKeyWire>,
536    pub permissions: SetOrKeep<Permissions<AuthRequired>>,
537    pub zkapp_uri: SetOrKeep<ZkAppUri>,
538    pub token_symbol: SetOrKeep<TokenSymbol>,
539    pub timing: SetOrKeep<Timing>,
540    pub voting_for: SetOrKeep<VotingFor>,
541}
542
543impl ToFieldElements<Fp> for Update {
544    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
545        let Self {
546            app_state,
547            delegate,
548            verification_key,
549            permissions,
550            zkapp_uri,
551            token_symbol,
552            timing,
553            voting_for,
554        } = self;
555
556        for s in app_state {
557            (s, Fp::zero).to_field_elements(fields);
558        }
559        (delegate, CompressedPubKey::empty).to_field_elements(fields);
560        (&verification_key.map(|w| w.hash()), Fp::zero).to_field_elements(fields);
561        (permissions, Permissions::empty).to_field_elements(fields);
562        (&zkapp_uri.map(Some), || Option::<&ZkAppUri>::None).to_field_elements(fields);
563        (token_symbol, TokenSymbol::default).to_field_elements(fields);
564        (timing, Timing::dummy).to_field_elements(fields);
565        (voting_for, VotingFor::dummy).to_field_elements(fields);
566    }
567}
568
569impl Update {
570    /// <https://github.com/MinaProtocol/mina/blob/3753a8593cc1577bcf4da16620daf9946d88e8e5/src/lib/mina_base/account_update.ml#L460>
571    pub fn noop() -> Self {
572        Self {
573            app_state: std::array::from_fn(|_| SetOrKeep::Keep),
574            delegate: SetOrKeep::Keep,
575            verification_key: SetOrKeep::Keep,
576            permissions: SetOrKeep::Keep,
577            zkapp_uri: SetOrKeep::Keep,
578            token_symbol: SetOrKeep::Keep,
579            timing: SetOrKeep::Keep,
580            voting_for: SetOrKeep::Keep,
581        }
582    }
583
584    /// <https://github.com/MinaProtocol/mina/blob/3753a8593cc1577bcf4da16620daf9946d88e8e5/src/lib/mina_base/account_update.ml#L472>
585    pub fn dummy() -> Self {
586        Self::noop()
587    }
588
589    /// <https://github.com/MinaProtocol/mina/blob/3753a8593cc1577bcf4da16620daf9946d88e8e5/src/lib/mina_base/account_update.ml#L338>
590    pub fn gen(
591        token_account: Option<bool>,
592        zkapp_account: Option<bool>,
593        vk: Option<&VerificationKeyWire>,
594        permissions_auth: Option<crate::ControlTag>,
595    ) -> Self {
596        let mut rng = rand::thread_rng();
597
598        let token_account = token_account.unwrap_or(false);
599        let zkapp_account = zkapp_account.unwrap_or(false);
600
601        let app_state: [_; 8] = std::array::from_fn(|_| SetOrKeep::gen(|| Fp::rand(&mut rng)));
602
603        let delegate = if !token_account {
604            SetOrKeep::gen(|| gen_keypair().public.into_compressed())
605        } else {
606            SetOrKeep::Keep
607        };
608
609        let verification_key = if zkapp_account {
610            SetOrKeep::gen(|| match vk {
611                None => VerificationKeyWire::dummy(),
612                Some(vk) => vk.clone(),
613            })
614        } else {
615            SetOrKeep::Keep
616        };
617
618        let permissions = match permissions_auth {
619            None => SetOrKeep::Keep,
620            Some(auth_tag) => SetOrKeep::Set(Permissions::gen(auth_tag)),
621        };
622
623        let zkapp_uri = SetOrKeep::gen(|| {
624            ZkAppUri::from(
625                [
626                    "https://www.example.com",
627                    "https://www.minaprotocol.com",
628                    "https://www.gurgle.com",
629                    "https://faceplant.com",
630                ]
631                .choose(&mut rng)
632                .unwrap()
633                .to_string()
634                .into_bytes(),
635            )
636        });
637
638        let token_symbol = SetOrKeep::gen(|| {
639            TokenSymbol::from(
640                ["MINA", "TOKEN1", "TOKEN2", "TOKEN3", "TOKEN4", "TOKEN5"]
641                    .choose(&mut rng)
642                    .unwrap()
643                    .to_string()
644                    .into_bytes(),
645            )
646        });
647
648        let voting_for = SetOrKeep::gen(|| VotingFor(Fp::rand(&mut rng)));
649
650        let timing = SetOrKeep::Keep;
651
652        Self {
653            app_state,
654            delegate,
655            verification_key,
656            permissions,
657            zkapp_uri,
658            token_symbol,
659            timing,
660            voting_for,
661        }
662    }
663}
664
665// TODO: This could be std::ops::Range ?
666/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/zkapp_precondition.ml#L23>
667#[derive(Debug, Clone, PartialEq)]
668pub struct ClosedInterval<T> {
669    pub lower: T,
670    pub upper: T,
671}
672
673impl<T> ClosedInterval<T>
674where
675    T: MinMax,
676{
677    pub fn min_max() -> Self {
678        Self {
679            lower: T::min(),
680            upper: T::max(),
681        }
682    }
683}
684
685impl<T> ToInputs for ClosedInterval<T>
686where
687    T: ToInputs,
688{
689    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_precondition.ml#L37>
690    fn to_inputs(&self, inputs: &mut Inputs) {
691        let ClosedInterval { lower, upper } = self;
692
693        lower.to_inputs(inputs);
694        upper.to_inputs(inputs);
695    }
696}
697
698impl<T> ToFieldElements<Fp> for ClosedInterval<T>
699where
700    T: ToFieldElements<Fp>,
701{
702    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
703        let ClosedInterval { lower, upper } = self;
704
705        lower.to_field_elements(fields);
706        upper.to_field_elements(fields);
707    }
708}
709
710impl<T> Check<Fp> for ClosedInterval<T>
711where
712    T: Check<Fp>,
713{
714    fn check(&self, w: &mut Witness<Fp>) {
715        let ClosedInterval { lower, upper } = self;
716        lower.check(w);
717        upper.check(w);
718    }
719}
720
721impl<T> ClosedInterval<T>
722where
723    T: PartialOrd,
724{
725    pub fn is_constant(&self) -> bool {
726        self.lower == self.upper
727    }
728
729    /// <https://github.com/MinaProtocol/mina/blob/d7d4aa4d650eb34b45a42b29276554802683ce15/src/lib/mina_base/zkapp_precondition.ml#L30>
730    pub fn gen<F>(mut fun: F) -> Self
731    where
732        F: FnMut() -> T,
733    {
734        let a1 = fun();
735        let a2 = fun();
736
737        if a1 <= a2 {
738            Self {
739                lower: a1,
740                upper: a2,
741            }
742        } else {
743            Self {
744                lower: a2,
745                upper: a1,
746            }
747        }
748    }
749}
750
751/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/zkapp_basic.ml#L232>
752#[derive(Debug, Clone, PartialEq)]
753pub enum OrIgnore<T> {
754    Check(T),
755    Ignore,
756}
757
758impl<T, F> ToInputs for (&OrIgnore<T>, F)
759where
760    T: ToInputs,
761    F: Fn() -> T,
762{
763    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_precondition.ml#L414>
764    fn to_inputs(&self, inputs: &mut Inputs) {
765        let (or_ignore, default_fn) = self;
766
767        match or_ignore {
768            OrIgnore::Check(this) => {
769                inputs.append_bool(true);
770                this.to_inputs(inputs);
771            }
772            OrIgnore::Ignore => {
773                inputs.append_bool(false);
774                let default = default_fn();
775                default.to_inputs(inputs);
776            }
777        }
778    }
779}
780
781impl<T, F> ToFieldElements<Fp> for (&OrIgnore<T>, F)
782where
783    T: ToFieldElements<Fp>,
784    F: Fn() -> T,
785{
786    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
787        let (or_ignore, default_fn) = self;
788
789        match or_ignore {
790            OrIgnore::Check(this) => {
791                Boolean::True.to_field_elements(fields);
792                this.to_field_elements(fields);
793            }
794            OrIgnore::Ignore => {
795                Boolean::False.to_field_elements(fields);
796                let default = default_fn();
797                default.to_field_elements(fields);
798            }
799        };
800    }
801}
802
803impl<T, F> Check<Fp> for (&OrIgnore<T>, F)
804where
805    T: Check<Fp>,
806    F: Fn() -> T,
807{
808    fn check(&self, w: &mut Witness<Fp>) {
809        let (or_ignore, default_fn) = self;
810        let value = match or_ignore {
811            OrIgnore::Check(this) => MyCow::Borrow(this),
812            OrIgnore::Ignore => MyCow::Own(default_fn()),
813        };
814        value.check(w);
815    }
816}
817
818impl<T> OrIgnore<T> {
819    /// <https://github.com/MinaProtocol/mina/blob/d7d4aa4d650eb34b45a42b29276554802683ce15/src/lib/mina_base/zkapp_basic.ml#L239>
820    pub fn gen<F>(mut fun: F) -> Self
821    where
822        F: FnMut() -> T,
823    {
824        let mut rng = rand::thread_rng();
825
826        if rng.gen() {
827            Self::Check(fun())
828        } else {
829            Self::Ignore
830        }
831    }
832
833    pub fn map<F, V>(&self, fun: F) -> OrIgnore<V>
834    where
835        F: Fn(&T) -> V,
836    {
837        match self {
838            OrIgnore::Check(v) => OrIgnore::Check(fun(v)),
839            OrIgnore::Ignore => OrIgnore::Ignore,
840        }
841    }
842}
843
844impl<T> OrIgnore<ClosedInterval<T>>
845where
846    T: PartialOrd,
847{
848    /// <https://github.com/MinaProtocol/mina/blob/3753a8593cc1577bcf4da16620daf9946d88e8e5/src/lib/mina_base/zkapp_precondition.ml#L294>
849    pub fn is_constant(&self) -> bool {
850        match self {
851            OrIgnore::Check(interval) => interval.lower == interval.upper,
852            OrIgnore::Ignore => false,
853        }
854    }
855}
856
857/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/zkapp_precondition.ml#L439>
858pub type Hash<T> = OrIgnore<T>;
859
860/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/zkapp_precondition.ml#L298>
861pub type EqData<T> = OrIgnore<T>;
862
863/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/zkapp_precondition.ml#L178>
864pub type Numeric<T> = OrIgnore<ClosedInterval<T>>;
865
866/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/epoch_ledger.ml#L9>
867#[derive(Debug, Clone, PartialEq)]
868pub struct EpochLedger {
869    pub hash: Hash<Fp>,
870    pub total_currency: Numeric<Amount>,
871}
872
873/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/zkapp_precondition.ml#L797>
874#[derive(Debug, Clone, PartialEq)]
875pub struct EpochData {
876    pub(crate) ledger: EpochLedger,
877    pub seed: Hash<Fp>,
878    pub start_checkpoint: Hash<Fp>,
879    pub lock_checkpoint: Hash<Fp>,
880    pub epoch_length: Numeric<Length>,
881}
882
883#[cfg(feature = "fuzzing")]
884impl EpochData {
885    pub fn new(
886        ledger: EpochLedger,
887        seed: Hash<Fp>,
888        start_checkpoint: Hash<Fp>,
889        lock_checkpoint: Hash<Fp>,
890        epoch_length: Numeric<Length>,
891    ) -> Self {
892        EpochData {
893            ledger,
894            seed,
895            start_checkpoint,
896            lock_checkpoint,
897            epoch_length,
898        }
899    }
900
901    pub fn ledger_mut(&mut self) -> &mut EpochLedger {
902        &mut self.ledger
903    }
904}
905
906impl ToInputs for EpochData {
907    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_precondition.ml#L875>
908    fn to_inputs(&self, inputs: &mut Inputs) {
909        let EpochData {
910            ledger,
911            seed,
912            start_checkpoint,
913            lock_checkpoint,
914            epoch_length,
915        } = self;
916
917        {
918            let EpochLedger {
919                hash,
920                total_currency,
921            } = ledger;
922
923            inputs.append(&(hash, Fp::zero));
924            inputs.append(&(total_currency, ClosedInterval::min_max));
925        }
926
927        inputs.append(&(seed, Fp::zero));
928        inputs.append(&(start_checkpoint, Fp::zero));
929        inputs.append(&(lock_checkpoint, Fp::zero));
930        inputs.append(&(epoch_length, ClosedInterval::min_max));
931    }
932}
933
934impl ToFieldElements<Fp> for EpochData {
935    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
936        let EpochData {
937            ledger,
938            seed,
939            start_checkpoint,
940            lock_checkpoint,
941            epoch_length,
942        } = self;
943
944        {
945            let EpochLedger {
946                hash,
947                total_currency,
948            } = ledger;
949
950            (hash, Fp::zero).to_field_elements(fields);
951            (total_currency, ClosedInterval::min_max).to_field_elements(fields);
952        }
953
954        (seed, Fp::zero).to_field_elements(fields);
955        (start_checkpoint, Fp::zero).to_field_elements(fields);
956        (lock_checkpoint, Fp::zero).to_field_elements(fields);
957        (epoch_length, ClosedInterval::min_max).to_field_elements(fields);
958    }
959}
960
961impl Check<Fp> for EpochData {
962    fn check(&self, w: &mut Witness<Fp>) {
963        let EpochData {
964            ledger,
965            seed,
966            start_checkpoint,
967            lock_checkpoint,
968            epoch_length,
969        } = self;
970
971        {
972            let EpochLedger {
973                hash,
974                total_currency,
975            } = ledger;
976
977            (hash, Fp::zero).check(w);
978            (total_currency, ClosedInterval::min_max).check(w);
979        }
980
981        (seed, Fp::zero).check(w);
982        (start_checkpoint, Fp::zero).check(w);
983        (lock_checkpoint, Fp::zero).check(w);
984        (epoch_length, ClosedInterval::min_max).check(w);
985    }
986}
987
988impl EpochData {
989    pub fn gen() -> Self {
990        let mut rng = rand::thread_rng();
991
992        EpochData {
993            ledger: EpochLedger {
994                hash: OrIgnore::gen(|| Fp::rand(&mut rng)),
995                total_currency: OrIgnore::gen(|| ClosedInterval::gen(|| rng.gen())),
996            },
997            seed: OrIgnore::gen(|| Fp::rand(&mut rng)),
998            start_checkpoint: OrIgnore::gen(|| Fp::rand(&mut rng)),
999            lock_checkpoint: OrIgnore::gen(|| Fp::rand(&mut rng)),
1000            epoch_length: OrIgnore::gen(|| ClosedInterval::gen(|| rng.gen())),
1001        }
1002    }
1003}
1004
1005/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/zkapp_precondition.ml#L977>
1006#[derive(Debug, Clone, PartialEq)]
1007pub struct ZkAppPreconditions {
1008    pub snarked_ledger_hash: Hash<Fp>,
1009    pub blockchain_length: Numeric<Length>,
1010    pub min_window_density: Numeric<Length>,
1011    pub total_currency: Numeric<Amount>,
1012    pub global_slot_since_genesis: Numeric<Slot>,
1013    pub staking_epoch_data: EpochData,
1014    pub next_epoch_data: EpochData,
1015}
1016
1017impl ZkAppPreconditions {
1018    pub fn zcheck<Ops: ZkappCheckOps>(
1019        &self,
1020        s: &ProtocolStateView,
1021        w: &mut Witness<Fp>,
1022    ) -> Boolean {
1023        let Self {
1024            snarked_ledger_hash,
1025            blockchain_length,
1026            min_window_density,
1027            total_currency,
1028            global_slot_since_genesis,
1029            staking_epoch_data,
1030            next_epoch_data,
1031        } = self;
1032
1033        // NOTE: Here the 2nd element in the tuples is the default value of `OrIgnore`
1034
1035        let epoch_data =
1036            |epoch_data: &EpochData, view: &protocol_state::EpochData<Fp>, w: &mut Witness<Fp>| {
1037                let EpochData {
1038                    ledger:
1039                        EpochLedger {
1040                            hash,
1041                            total_currency,
1042                        },
1043                    seed: _,
1044                    start_checkpoint,
1045                    lock_checkpoint,
1046                    epoch_length,
1047                } = epoch_data;
1048                // Reverse to match OCaml order of the list, while still executing `zcheck`
1049                // in correct order
1050                [
1051                    (epoch_length, ClosedInterval::min_max).zcheck::<Ops>(&view.epoch_length, w),
1052                    (lock_checkpoint, Fp::zero).zcheck::<Ops>(&view.lock_checkpoint, w),
1053                    (start_checkpoint, Fp::zero).zcheck::<Ops>(&view.start_checkpoint, w),
1054                    (total_currency, ClosedInterval::min_max)
1055                        .zcheck::<Ops>(&view.ledger.total_currency, w),
1056                    (hash, Fp::zero).zcheck::<Ops>(&view.ledger.hash, w),
1057                ]
1058            };
1059
1060        let next_epoch_data = epoch_data(next_epoch_data, &s.next_epoch_data, w);
1061        let staking_epoch_data = epoch_data(staking_epoch_data, &s.staking_epoch_data, w);
1062
1063        // Reverse to match OCaml order of the list, while still executing `zcheck`
1064        // in correct order
1065        let bools = [
1066            (global_slot_since_genesis, ClosedInterval::min_max)
1067                .zcheck::<Ops>(&s.global_slot_since_genesis, w),
1068            (total_currency, ClosedInterval::min_max).zcheck::<Ops>(&s.total_currency, w),
1069            (min_window_density, ClosedInterval::min_max).zcheck::<Ops>(&s.min_window_density, w),
1070            (blockchain_length, ClosedInterval::min_max).zcheck::<Ops>(&s.blockchain_length, w),
1071            (snarked_ledger_hash, Fp::zero).zcheck::<Ops>(&s.snarked_ledger_hash, w),
1072        ]
1073        .into_iter()
1074        .rev()
1075        .chain(staking_epoch_data.into_iter().rev())
1076        .chain(next_epoch_data.into_iter().rev());
1077
1078        Ops::boolean_all(bools, w)
1079    }
1080
1081    /// <https://github.com/MinaProtocol/mina/blob/3753a8593cc1577bcf4da16620daf9946d88e8e5/src/lib/mina_base/zkapp_precondition.ml#L1303>
1082    pub fn accept() -> Self {
1083        let epoch_data = || EpochData {
1084            ledger: EpochLedger {
1085                hash: OrIgnore::Ignore,
1086                total_currency: OrIgnore::Ignore,
1087            },
1088            seed: OrIgnore::Ignore,
1089            start_checkpoint: OrIgnore::Ignore,
1090            lock_checkpoint: OrIgnore::Ignore,
1091            epoch_length: OrIgnore::Ignore,
1092        };
1093
1094        Self {
1095            snarked_ledger_hash: OrIgnore::Ignore,
1096            blockchain_length: OrIgnore::Ignore,
1097            min_window_density: OrIgnore::Ignore,
1098            total_currency: OrIgnore::Ignore,
1099            global_slot_since_genesis: OrIgnore::Ignore,
1100            staking_epoch_data: epoch_data(),
1101            next_epoch_data: epoch_data(),
1102        }
1103    }
1104}
1105
1106impl ToInputs for ZkAppPreconditions {
1107    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_precondition.ml#L1052>
1108    fn to_inputs(&self, inputs: &mut Inputs) {
1109        let ZkAppPreconditions {
1110            snarked_ledger_hash,
1111            blockchain_length,
1112            min_window_density,
1113            total_currency,
1114            global_slot_since_genesis,
1115            staking_epoch_data,
1116            next_epoch_data,
1117        } = &self;
1118
1119        inputs.append(&(snarked_ledger_hash, Fp::zero));
1120        inputs.append(&(blockchain_length, ClosedInterval::min_max));
1121        inputs.append(&(min_window_density, ClosedInterval::min_max));
1122        inputs.append(&(total_currency, ClosedInterval::min_max));
1123        inputs.append(&(global_slot_since_genesis, ClosedInterval::min_max));
1124        inputs.append(staking_epoch_data);
1125        inputs.append(next_epoch_data);
1126    }
1127}
1128
1129impl ToFieldElements<Fp> for ZkAppPreconditions {
1130    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
1131        let Self {
1132            snarked_ledger_hash,
1133            blockchain_length,
1134            min_window_density,
1135            total_currency,
1136            global_slot_since_genesis,
1137            staking_epoch_data,
1138            next_epoch_data,
1139        } = self;
1140
1141        (snarked_ledger_hash, Fp::zero).to_field_elements(fields);
1142        (blockchain_length, ClosedInterval::min_max).to_field_elements(fields);
1143        (min_window_density, ClosedInterval::min_max).to_field_elements(fields);
1144        (total_currency, ClosedInterval::min_max).to_field_elements(fields);
1145        (global_slot_since_genesis, ClosedInterval::min_max).to_field_elements(fields);
1146        staking_epoch_data.to_field_elements(fields);
1147        next_epoch_data.to_field_elements(fields);
1148    }
1149}
1150
1151impl Check<Fp> for ZkAppPreconditions {
1152    fn check(&self, w: &mut Witness<Fp>) {
1153        let Self {
1154            snarked_ledger_hash,
1155            blockchain_length,
1156            min_window_density,
1157            total_currency,
1158            global_slot_since_genesis,
1159            staking_epoch_data,
1160            next_epoch_data,
1161        } = self;
1162
1163        (snarked_ledger_hash, Fp::zero).check(w);
1164        (blockchain_length, ClosedInterval::min_max).check(w);
1165        (min_window_density, ClosedInterval::min_max).check(w);
1166        (total_currency, ClosedInterval::min_max).check(w);
1167        (global_slot_since_genesis, ClosedInterval::min_max).check(w);
1168        staking_epoch_data.check(w);
1169        next_epoch_data.check(w);
1170    }
1171}
1172
1173/// <https://github.com/MinaProtocol/mina/blob/da6ba9a52e71d03ec6b6803b01f6d249eebc1ccb/src/lib/mina_base/zkapp_basic.ml#L401>
1174fn invalid_public_key() -> CompressedPubKey {
1175    CompressedPubKey {
1176        x: Fp::zero(),
1177        is_odd: false,
1178    }
1179}
1180
1181/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/zkapp_precondition.ml#L478>
1182#[derive(Debug, Clone, PartialEq)]
1183pub struct Account {
1184    pub balance: Numeric<Balance>,
1185    pub nonce: Numeric<Nonce>,
1186    pub receipt_chain_hash: Hash<Fp>, // TODO: Should be type `ReceiptChainHash`
1187    pub delegate: EqData<CompressedPubKey>,
1188    pub state: [EqData<Fp>; 8],
1189    pub action_state: EqData<Fp>,
1190    pub proved_state: EqData<bool>,
1191    pub is_new: EqData<bool>,
1192}
1193
1194impl Account {
1195    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_precondition.ml#L525>
1196    pub fn accept() -> Self {
1197        Self {
1198            balance: Numeric::Ignore,
1199            nonce: Numeric::Ignore,
1200            receipt_chain_hash: Hash::Ignore,
1201            delegate: EqData::Ignore,
1202            state: std::array::from_fn(|_| EqData::Ignore),
1203            action_state: EqData::Ignore,
1204            proved_state: EqData::Ignore,
1205            is_new: EqData::Ignore,
1206        }
1207    }
1208}
1209
1210impl Account {
1211    fn zchecks<Ops: ZkappCheckOps>(
1212        &self,
1213        account: &crate::Account,
1214        new_account: Boolean,
1215        w: &mut Witness<Fp>,
1216    ) -> Vec<(TransactionFailure, Boolean)> {
1217        use TransactionFailure::*;
1218
1219        let Self {
1220            balance,
1221            nonce,
1222            receipt_chain_hash,
1223            delegate,
1224            state,
1225            action_state,
1226            proved_state,
1227            is_new,
1228        } = self;
1229
1230        let zkapp_account = account.zkapp_or_empty();
1231        let is_new = is_new.map(ToBoolean::to_boolean);
1232        let proved_state = proved_state.map(ToBoolean::to_boolean);
1233
1234        // NOTE: Here we need to execute all `zcheck` in the exact same order than OCaml
1235        // so we execute them in reverse order (compared to OCaml): OCaml evaluates from right
1236        // to left.
1237        // We then have to reverse the resulting vector, to match OCaml resulting list.
1238
1239        // NOTE 2: Here the 2nd element in the tuples is the default value of `OrIgnore`
1240        let mut checks: Vec<(TransactionFailure, _)> = [
1241            (
1242                AccountIsNewPreconditionUnsatisfied,
1243                (&is_new, || Boolean::False).zcheck::<Ops>(&new_account, w),
1244            ),
1245            (
1246                AccountProvedStatePreconditionUnsatisfied,
1247                (&proved_state, || Boolean::False)
1248                    .zcheck::<Ops>(&zkapp_account.proved_state.to_boolean(), w),
1249            ),
1250        ]
1251        .into_iter()
1252        .chain({
1253            let bools = state
1254                .iter()
1255                .zip(&zkapp_account.app_state)
1256                .enumerate()
1257                // Reversed to enforce right-to-left order application of `f` like in OCaml
1258                .rev()
1259                .map(|(i, (s, account_s))| {
1260                    let b = (s, Fp::zero).zcheck::<Ops>(account_s, w);
1261                    (AccountAppStatePreconditionUnsatisfied(i as u64), b)
1262                })
1263                .collect::<Vec<_>>();
1264            // Not reversed again because we are constructing these results in
1265            // reverse order to match the OCaml evaluation order.
1266            bools.into_iter()
1267        })
1268        .chain([
1269            {
1270                let bools: Vec<_> = zkapp_account
1271                    .action_state
1272                    .iter()
1273                    // Reversed to enforce right-to-left order application of `f` like in OCaml
1274                    .rev()
1275                    .map(|account_s| {
1276                        (action_state, ZkAppAccount::empty_action_state).zcheck::<Ops>(account_s, w)
1277                    })
1278                    .collect();
1279                (
1280                    AccountActionStatePreconditionUnsatisfied,
1281                    Ops::boolean_any(bools, w),
1282                )
1283            },
1284            (
1285                AccountDelegatePreconditionUnsatisfied,
1286                (delegate, CompressedPubKey::empty).zcheck::<Ops>(&*account.delegate_or_empty(), w),
1287            ),
1288            (
1289                AccountReceiptChainHashPreconditionUnsatisfied,
1290                (receipt_chain_hash, Fp::zero).zcheck::<Ops>(&account.receipt_chain_hash.0, w),
1291            ),
1292            (
1293                AccountNoncePreconditionUnsatisfied,
1294                (nonce, ClosedInterval::min_max).zcheck::<Ops>(&account.nonce, w),
1295            ),
1296            (
1297                AccountBalancePreconditionUnsatisfied,
1298                (balance, ClosedInterval::min_max).zcheck::<Ops>(&account.balance, w),
1299            ),
1300        ])
1301        .collect::<Vec<_>>();
1302
1303        checks.reverse();
1304        checks
1305    }
1306}
1307
1308/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/account_update.ml#L613>
1309#[derive(Debug, Clone, PartialEq)]
1310pub struct AccountPreconditions(pub Account);
1311
1312impl ToInputs for AccountPreconditions {
1313    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/account_update.ml#L635>
1314    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_precondition.ml#L568>
1315    fn to_inputs(&self, inputs: &mut Inputs) {
1316        let Account {
1317            balance,
1318            nonce,
1319            receipt_chain_hash,
1320            delegate,
1321            state,
1322            action_state,
1323            proved_state,
1324            is_new,
1325        } = &self.0;
1326
1327        inputs.append(&(balance, ClosedInterval::min_max));
1328        inputs.append(&(nonce, ClosedInterval::min_max));
1329        inputs.append(&(receipt_chain_hash, Fp::zero));
1330        inputs.append(&(delegate, CompressedPubKey::empty));
1331        for s in state.iter() {
1332            inputs.append(&(s, Fp::zero));
1333        }
1334        // <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_account.ml#L168>
1335        inputs.append(&(action_state, ZkAppAccount::empty_action_state));
1336        inputs.append(&(proved_state, || false));
1337        inputs.append(&(is_new, || false));
1338    }
1339}
1340
1341impl ToFieldElements<Fp> for AccountPreconditions {
1342    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
1343        let Account {
1344            balance,
1345            nonce,
1346            receipt_chain_hash,
1347            delegate,
1348            state,
1349            action_state,
1350            proved_state,
1351            is_new,
1352        } = &self.0;
1353
1354        (balance, ClosedInterval::min_max).to_field_elements(fields);
1355        (nonce, ClosedInterval::min_max).to_field_elements(fields);
1356        (receipt_chain_hash, Fp::zero).to_field_elements(fields);
1357        (delegate, CompressedPubKey::empty).to_field_elements(fields);
1358        state.iter().for_each(|s| {
1359            (s, Fp::zero).to_field_elements(fields);
1360        });
1361        (action_state, ZkAppAccount::empty_action_state).to_field_elements(fields);
1362        (proved_state, || false).to_field_elements(fields);
1363        (is_new, || false).to_field_elements(fields);
1364    }
1365}
1366
1367impl Check<Fp> for AccountPreconditions {
1368    fn check(&self, w: &mut Witness<Fp>) {
1369        let Account {
1370            balance,
1371            nonce,
1372            receipt_chain_hash,
1373            delegate,
1374            state,
1375            action_state,
1376            proved_state,
1377            is_new,
1378        } = &self.0;
1379
1380        (balance, ClosedInterval::min_max).check(w);
1381        (nonce, ClosedInterval::min_max).check(w);
1382        (receipt_chain_hash, Fp::zero).check(w);
1383        (delegate, CompressedPubKey::empty).check(w);
1384        state.iter().for_each(|s| {
1385            (s, Fp::zero).check(w);
1386        });
1387        (action_state, ZkAppAccount::empty_action_state).check(w);
1388        (proved_state, || false).check(w);
1389        (is_new, || false).check(w);
1390    }
1391}
1392
1393impl AccountPreconditions {
1394    pub fn with_nonce(nonce: Nonce) -> Self {
1395        use OrIgnore::{Check, Ignore};
1396        AccountPreconditions(Account {
1397            balance: Ignore,
1398            nonce: Check(ClosedInterval {
1399                lower: nonce,
1400                upper: nonce,
1401            }),
1402            receipt_chain_hash: Ignore,
1403            delegate: Ignore,
1404            state: std::array::from_fn(|_| EqData::Ignore),
1405            action_state: Ignore,
1406            proved_state: Ignore,
1407            is_new: Ignore,
1408        })
1409    }
1410
1411    pub fn nonce(&self) -> Numeric<Nonce> {
1412        self.0.nonce.clone()
1413    }
1414
1415    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/account_update.ml#L635>
1416    pub fn to_full(&self) -> MyCow<'_, Account> {
1417        MyCow::Borrow(&self.0)
1418    }
1419
1420    pub fn zcheck<Ops, Fun>(
1421        &self,
1422        new_account: Boolean,
1423        account: &crate::Account,
1424        mut check: Fun,
1425        w: &mut Witness<Fp>,
1426    ) where
1427        Ops: ZkappCheckOps,
1428        Fun: FnMut(TransactionFailure, Boolean, &mut Witness<Fp>),
1429    {
1430        let this = self.to_full();
1431        for (failure, passed) in this.zchecks::<Ops>(account, new_account, w) {
1432            check(failure, passed, w);
1433        }
1434    }
1435}
1436
1437/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/account_update.ml#L758>
1438#[derive(Debug, Clone, PartialEq)]
1439pub struct Preconditions {
1440    pub network: ZkAppPreconditions,
1441    pub account: AccountPreconditions,
1442    pub valid_while: Numeric<Slot>,
1443}
1444
1445#[cfg(feature = "fuzzing")]
1446impl Preconditions {
1447    pub fn new(
1448        network: ZkAppPreconditions,
1449        account: AccountPreconditions,
1450        valid_while: Numeric<Slot>,
1451    ) -> Self {
1452        Self {
1453            network,
1454            account,
1455            valid_while,
1456        }
1457    }
1458
1459    pub fn network_mut(&mut self) -> &mut ZkAppPreconditions {
1460        &mut self.network
1461    }
1462}
1463
1464impl ToFieldElements<Fp> for Preconditions {
1465    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
1466        let Self {
1467            network,
1468            account,
1469            valid_while,
1470        } = self;
1471
1472        network.to_field_elements(fields);
1473        account.to_field_elements(fields);
1474        (valid_while, ClosedInterval::min_max).to_field_elements(fields);
1475    }
1476}
1477
1478impl Check<Fp> for Preconditions {
1479    fn check(&self, w: &mut Witness<Fp>) {
1480        let Self {
1481            network,
1482            account,
1483            valid_while,
1484        } = self;
1485
1486        network.check(w);
1487        account.check(w);
1488        (valid_while, ClosedInterval::min_max).check(w);
1489    }
1490}
1491
1492impl ToInputs for Preconditions {
1493    /// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_base/account_update.ml#L1148>
1494    fn to_inputs(&self, inputs: &mut Inputs) {
1495        let Self {
1496            network,
1497            account,
1498            valid_while,
1499        } = self;
1500
1501        inputs.append(network);
1502        inputs.append(account);
1503        inputs.append(&(valid_while, ClosedInterval::min_max));
1504    }
1505}
1506
1507/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/account_update.ml#L27>
1508#[derive(Debug, Clone, PartialEq, Eq)]
1509pub enum AuthorizationKind {
1510    NoneGiven,
1511    Signature,
1512    Proof(Fp), // hash
1513}
1514
1515impl AuthorizationKind {
1516    pub fn vk_hash(&self) -> Fp {
1517        match self {
1518            AuthorizationKind::NoneGiven | AuthorizationKind::Signature => {
1519                VerificationKey::dummy().hash()
1520            }
1521            AuthorizationKind::Proof(hash) => *hash,
1522        }
1523    }
1524
1525    pub fn is_proved(&self) -> bool {
1526        match self {
1527            AuthorizationKind::Proof(_) => true,
1528            AuthorizationKind::NoneGiven => false,
1529            AuthorizationKind::Signature => false,
1530        }
1531    }
1532
1533    pub fn is_signed(&self) -> bool {
1534        match self {
1535            AuthorizationKind::Proof(_) => false,
1536            AuthorizationKind::NoneGiven => false,
1537            AuthorizationKind::Signature => true,
1538        }
1539    }
1540
1541    fn to_structured(&self) -> ([bool; 2], Fp) {
1542        // bits: [is_signed, is_proved]
1543        let bits = match self {
1544            AuthorizationKind::NoneGiven => [false, false],
1545            AuthorizationKind::Signature => [true, false],
1546            AuthorizationKind::Proof(_) => [false, true],
1547        };
1548        let field = self.vk_hash();
1549        (bits, field)
1550    }
1551}
1552
1553impl ToInputs for AuthorizationKind {
1554    /// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_base/account_update.ml#L142>
1555    fn to_inputs(&self, inputs: &mut Inputs) {
1556        let (bits, field) = self.to_structured();
1557
1558        for bit in bits {
1559            inputs.append_bool(bit);
1560        }
1561        inputs.append_field(field);
1562    }
1563}
1564
1565impl ToFieldElements<Fp> for AuthorizationKind {
1566    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
1567        self.to_structured().to_field_elements(fields);
1568    }
1569}
1570
1571/// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_base/account_update.ml#L1311>
1572#[derive(Debug, Clone, PartialEq)]
1573pub struct Body {
1574    pub public_key: CompressedPubKey,
1575    pub token_id: TokenId,
1576    pub update: Update,
1577    pub balance_change: Signed<Amount>,
1578    pub increment_nonce: bool,
1579    pub events: Events,
1580    pub actions: Actions,
1581    pub call_data: Fp,
1582    pub preconditions: Preconditions,
1583    pub use_full_commitment: bool,
1584    pub implicit_account_creation_fee: bool,
1585    pub may_use_token: MayUseToken,
1586    pub authorization_kind: AuthorizationKind,
1587}
1588
1589impl ToInputs for Body {
1590    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/account_update.ml#L1297>
1591    fn to_inputs(&self, inputs: &mut Inputs) {
1592        let Self {
1593            public_key,
1594            token_id,
1595            update,
1596            balance_change,
1597            increment_nonce,
1598            events,
1599            actions,
1600            call_data,
1601            preconditions,
1602            use_full_commitment,
1603            implicit_account_creation_fee,
1604            may_use_token,
1605            authorization_kind,
1606        } = self;
1607
1608        inputs.append(public_key);
1609        inputs.append(token_id);
1610
1611        // `Body::update`
1612        {
1613            let Update {
1614                app_state,
1615                delegate,
1616                verification_key,
1617                permissions,
1618                zkapp_uri,
1619                token_symbol,
1620                timing,
1621                voting_for,
1622            } = update;
1623
1624            for state in app_state {
1625                inputs.append(&(state, Fp::zero));
1626            }
1627
1628            inputs.append(&(delegate, CompressedPubKey::empty));
1629            inputs.append(&(&verification_key.map(|w| w.hash()), Fp::zero));
1630            inputs.append(&(permissions, Permissions::empty));
1631            inputs.append(&(&zkapp_uri.map(Some), || Option::<&ZkAppUri>::None));
1632            inputs.append(&(token_symbol, TokenSymbol::default));
1633            inputs.append(&(timing, Timing::dummy));
1634            inputs.append(&(voting_for, VotingFor::dummy));
1635        }
1636
1637        inputs.append(balance_change);
1638        inputs.append(increment_nonce);
1639        inputs.append(events);
1640        inputs.append(actions);
1641        inputs.append(call_data);
1642        inputs.append(preconditions);
1643        inputs.append(use_full_commitment);
1644        inputs.append(implicit_account_creation_fee);
1645        inputs.append(may_use_token);
1646        inputs.append(authorization_kind);
1647    }
1648}
1649
1650impl ToFieldElements<Fp> for Body {
1651    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
1652        let Self {
1653            public_key,
1654            token_id,
1655            update,
1656            balance_change,
1657            increment_nonce,
1658            events,
1659            actions,
1660            call_data,
1661            preconditions,
1662            use_full_commitment,
1663            implicit_account_creation_fee,
1664            may_use_token,
1665            authorization_kind,
1666        } = self;
1667
1668        public_key.to_field_elements(fields);
1669        token_id.to_field_elements(fields);
1670        update.to_field_elements(fields);
1671        balance_change.to_field_elements(fields);
1672        increment_nonce.to_field_elements(fields);
1673        events.to_field_elements(fields);
1674        actions.to_field_elements(fields);
1675        call_data.to_field_elements(fields);
1676        preconditions.to_field_elements(fields);
1677        use_full_commitment.to_field_elements(fields);
1678        implicit_account_creation_fee.to_field_elements(fields);
1679        may_use_token.to_field_elements(fields);
1680        authorization_kind.to_field_elements(fields);
1681    }
1682}
1683
1684impl Check<Fp> for Body {
1685    fn check(&self, w: &mut Witness<Fp>) {
1686        let Self {
1687            public_key: _,
1688            token_id: _,
1689            update:
1690                Update {
1691                    app_state: _,
1692                    delegate: _,
1693                    verification_key: _,
1694                    permissions,
1695                    zkapp_uri: _,
1696                    token_symbol,
1697                    timing,
1698                    voting_for: _,
1699                },
1700            balance_change,
1701            increment_nonce: _,
1702            events: _,
1703            actions: _,
1704            call_data: _,
1705            preconditions,
1706            use_full_commitment: _,
1707            implicit_account_creation_fee: _,
1708            may_use_token,
1709            authorization_kind: _,
1710        } = self;
1711
1712        (permissions, Permissions::empty).check(w);
1713        (token_symbol, TokenSymbol::default).check(w);
1714        (timing, Timing::dummy).check(w);
1715        balance_change.check(w);
1716
1717        preconditions.check(w);
1718        may_use_token.check(w);
1719    }
1720}
1721
1722impl Body {
1723    pub fn account_id(&self) -> AccountId {
1724        let Self {
1725            public_key,
1726            token_id,
1727            ..
1728        } = self;
1729        AccountId::create(public_key.clone(), token_id.clone())
1730    }
1731}
1732
1733/// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_base/account_update.ml#L1284>
1734#[derive(Debug, Clone, PartialEq)]
1735pub struct BodySimple {
1736    pub public_key: CompressedPubKey,
1737    pub token_id: TokenId,
1738    pub update: Update,
1739    pub balance_change: Signed<Amount>,
1740    pub increment_nonce: bool,
1741    pub events: Events,
1742    pub actions: Actions,
1743    pub call_data: Fp,
1744    pub call_depth: usize,
1745    pub preconditions: Preconditions,
1746    pub use_full_commitment: bool,
1747    pub implicit_account_creation_fee: bool,
1748    pub may_use_token: MayUseToken,
1749    pub authorization_kind: AuthorizationKind,
1750}
1751
1752/// Notes:
1753/// The type in OCaml is this one:
1754/// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/pickles/proof.ml#L401>
1755///
1756/// For now we use the type from `mina_p2p_messages`, but we need to use our own.
1757/// Lots of inner types are (BigInt, Bigint) which should be replaced with `Pallas<_>` etc.
1758/// Also, in OCaml it has custom `{to/from}_binable` implementation.
1759///
1760/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/pickles/pickles_intf.ml#L316>
1761pub type SideLoadedProof = Arc<mina_p2p_messages::v2::PicklesProofProofsVerifiedMaxStableV2>;
1762
1763/// Authorization methods for zkApp account updates.
1764///
1765/// Defines how an account update is authorized to modify an account's state.
1766///
1767/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/control.ml#L11>
1768#[derive(Clone, PartialEq)]
1769pub enum Control {
1770    /// Verified by a zero-knowledge proof against the account's verification
1771    /// key.
1772    Proof(SideLoadedProof),
1773    /// Signed by the account's private key.
1774    Signature(Signature),
1775    /// No authorization (only valid for certain operations).
1776    NoneGiven,
1777}
1778
1779impl std::fmt::Debug for Control {
1780    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1781        match self {
1782            Self::Proof(_) => f.debug_tuple("Proof").field(&"_").finish(),
1783            Self::Signature(arg0) => f.debug_tuple("Signature").field(arg0).finish(),
1784            Self::NoneGiven => write!(f, "NoneGiven"),
1785        }
1786    }
1787}
1788
1789impl Control {
1790    /// <https://github.com/MinaProtocol/mina/blob/d7d4aa4d650eb34b45a42b29276554802683ce15/src/lib/mina_base/control.ml#L81>
1791    pub fn tag(&self) -> crate::ControlTag {
1792        match self {
1793            Control::Proof(_) => crate::ControlTag::Proof,
1794            Control::Signature(_) => crate::ControlTag::Signature,
1795            Control::NoneGiven => crate::ControlTag::NoneGiven,
1796        }
1797    }
1798
1799    pub fn dummy_of_tag(tag: ControlTag) -> Self {
1800        match tag {
1801            ControlTag::Proof => Self::Proof(dummy::sideloaded_proof()),
1802            ControlTag::Signature => Self::Signature(Signature::dummy()),
1803            ControlTag::NoneGiven => Self::NoneGiven,
1804        }
1805    }
1806
1807    pub fn dummy(&self) -> Self {
1808        Self::dummy_of_tag(self.tag())
1809    }
1810}
1811
1812#[derive(Clone, Debug, PartialEq)]
1813pub enum MayUseToken {
1814    /// No permission to use any token other than the default Mina
1815    /// token
1816    No,
1817    /// Has permission to use the token owned by the direct parent of
1818    /// this account update, which may be inherited by child account
1819    /// updates.
1820    ParentsOwnToken,
1821    /// Inherit the token permission available to the parent.
1822    InheritFromParent,
1823}
1824
1825impl MayUseToken {
1826    pub fn parents_own_token(&self) -> bool {
1827        matches!(self, Self::ParentsOwnToken)
1828    }
1829
1830    pub fn inherit_from_parent(&self) -> bool {
1831        matches!(self, Self::InheritFromParent)
1832    }
1833
1834    fn to_bits(&self) -> [bool; 2] {
1835        // [ parents_own_token; inherit_from_parent ]
1836        match self {
1837            MayUseToken::No => [false, false],
1838            MayUseToken::ParentsOwnToken => [true, false],
1839            MayUseToken::InheritFromParent => [false, true],
1840        }
1841    }
1842}
1843
1844impl ToInputs for MayUseToken {
1845    fn to_inputs(&self, inputs: &mut Inputs) {
1846        for bit in self.to_bits() {
1847            inputs.append_bool(bit);
1848        }
1849    }
1850}
1851
1852impl ToFieldElements<Fp> for MayUseToken {
1853    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
1854        for bit in self.to_bits() {
1855            bit.to_field_elements(fields);
1856        }
1857    }
1858}
1859
1860impl Check<Fp> for MayUseToken {
1861    fn check(&self, w: &mut Witness<Fp>) {
1862        use crate::proofs::field::field;
1863
1864        let [parents_own_token, inherit_from_parent] = self.to_bits();
1865        let [parents_own_token, inherit_from_parent] = [
1866            parents_own_token.to_boolean(),
1867            inherit_from_parent.to_boolean(),
1868        ];
1869
1870        let sum = parents_own_token.to_field::<Fp>() + inherit_from_parent.to_field::<Fp>();
1871        let _sum_squared = field::mul(sum, sum, w);
1872    }
1873}
1874
1875pub struct CheckAuthorizationResult<Bool> {
1876    pub proof_verifies: Bool,
1877    pub signature_verifies: Bool,
1878}
1879
1880/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/account_update.ml#L1437>
1881pub type AccountUpdate = AccountUpdateSkeleton<Body>;
1882
1883#[derive(Debug, Clone, PartialEq)]
1884pub struct AccountUpdateSkeleton<Body> {
1885    pub body: Body,
1886    pub authorization: Control,
1887}
1888
1889/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/account_update.ml#L1395>
1890#[derive(Debug, Clone, PartialEq)]
1891pub struct AccountUpdateSimple {
1892    pub body: BodySimple,
1893    pub authorization: Control,
1894}
1895
1896impl ToInputs for AccountUpdate {
1897    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/account_update.ml#L1297>
1898    fn to_inputs(&self, inputs: &mut Inputs) {
1899        // Only the body is used
1900        let Self {
1901            body,
1902            authorization: _,
1903        } = self;
1904
1905        inputs.append(body);
1906    }
1907}
1908
1909impl AccountUpdate {
1910    /// <https://github.com/MinaProtocol/mina/blob/3753a8593cc1577bcf4da16620daf9946d88e8e5/src/lib/mina_base/account_update.ml#L1538>
1911    /// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_base/account_update.ml#L1465>
1912    pub fn of_fee_payer(fee_payer: FeePayer) -> Self {
1913        let FeePayer {
1914            body:
1915                FeePayerBody {
1916                    public_key,
1917                    fee,
1918                    valid_until,
1919                    nonce,
1920                },
1921            authorization,
1922        } = fee_payer;
1923
1924        Self {
1925            body: Body {
1926                public_key,
1927                token_id: TokenId::default(),
1928                update: Update::noop(),
1929                balance_change: Signed {
1930                    magnitude: Amount::of_fee(&fee),
1931                    sgn: Sgn::Neg,
1932                },
1933                increment_nonce: true,
1934                events: Events::empty(),
1935                actions: Actions::empty(),
1936                call_data: Fp::zero(),
1937                preconditions: Preconditions {
1938                    network: {
1939                        let mut network = ZkAppPreconditions::accept();
1940
1941                        let valid_util = valid_until.unwrap_or_else(Slot::max);
1942                        network.global_slot_since_genesis = OrIgnore::Check(ClosedInterval {
1943                            lower: Slot::zero(),
1944                            upper: valid_util,
1945                        });
1946
1947                        network
1948                    },
1949                    account: AccountPreconditions::with_nonce(nonce),
1950                    valid_while: Numeric::Ignore,
1951                },
1952                use_full_commitment: true,
1953                authorization_kind: AuthorizationKind::Signature,
1954                implicit_account_creation_fee: true,
1955                may_use_token: MayUseToken::No,
1956            },
1957            authorization: Control::Signature(authorization),
1958        }
1959    }
1960
1961    /// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/mina_base/account_update.ml#L1535>
1962    pub fn account_id(&self) -> AccountId {
1963        AccountId::new(self.body.public_key.clone(), self.body.token_id.clone())
1964    }
1965
1966    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/account_update.ml#L1327>
1967    pub fn digest(&self) -> Fp {
1968        self.hash_with_param(mina_core::NetworkConfig::global().account_update_hash_param)
1969    }
1970
1971    pub fn timing(&self) -> SetOrKeep<Timing> {
1972        self.body.update.timing.clone()
1973    }
1974
1975    pub fn may_use_parents_own_token(&self) -> bool {
1976        self.body.may_use_token.parents_own_token()
1977    }
1978
1979    pub fn may_use_token_inherited_from_parent(&self) -> bool {
1980        self.body.may_use_token.inherit_from_parent()
1981    }
1982
1983    pub fn public_key(&self) -> CompressedPubKey {
1984        self.body.public_key.clone()
1985    }
1986
1987    pub fn token_id(&self) -> TokenId {
1988        self.body.token_id.clone()
1989    }
1990
1991    pub fn increment_nonce(&self) -> bool {
1992        self.body.increment_nonce
1993    }
1994
1995    pub fn implicit_account_creation_fee(&self) -> bool {
1996        self.body.implicit_account_creation_fee
1997    }
1998
1999    // commitment and calls argument are ignored here, only used in the transaction snark
2000    pub fn check_authorization(
2001        &self,
2002        _will_succeed: bool,
2003        _commitment: Fp,
2004        _calls: CallForest<AccountUpdate>,
2005    ) -> CheckAuthorizationResult<bool> {
2006        match self.authorization {
2007            Control::Signature(_) => CheckAuthorizationResult {
2008                proof_verifies: false,
2009                signature_verifies: true,
2010            },
2011            Control::Proof(_) => CheckAuthorizationResult {
2012                proof_verifies: true,
2013                signature_verifies: false,
2014            },
2015            Control::NoneGiven => CheckAuthorizationResult {
2016                proof_verifies: false,
2017                signature_verifies: false,
2018            },
2019        }
2020    }
2021
2022    pub fn permissions(&self) -> SetOrKeep<Permissions<AuthRequired>> {
2023        self.body.update.permissions.clone()
2024    }
2025
2026    pub fn app_state(&self) -> [SetOrKeep<Fp>; 8] {
2027        self.body.update.app_state.clone()
2028    }
2029
2030    pub fn zkapp_uri(&self) -> SetOrKeep<ZkAppUri> {
2031        self.body.update.zkapp_uri.clone()
2032    }
2033
2034    /*
2035    pub fn token_symbol(&self) -> SetOrKeep<[u8; 6]> {
2036        self.body.update.token_symbol.clone()
2037    }
2038    */
2039
2040    pub fn token_symbol(&self) -> SetOrKeep<TokenSymbol> {
2041        self.body.update.token_symbol.clone()
2042    }
2043
2044    pub fn delegate(&self) -> SetOrKeep<CompressedPubKey> {
2045        self.body.update.delegate.clone()
2046    }
2047
2048    pub fn voting_for(&self) -> SetOrKeep<VotingFor> {
2049        self.body.update.voting_for.clone()
2050    }
2051
2052    pub fn verification_key(&self) -> SetOrKeep<VerificationKeyWire> {
2053        self.body.update.verification_key.clone()
2054    }
2055
2056    pub fn valid_while_precondition(&self) -> OrIgnore<ClosedInterval<Slot>> {
2057        self.body.preconditions.valid_while.clone()
2058    }
2059
2060    pub fn actions(&self) -> Actions {
2061        self.body.actions.clone()
2062    }
2063
2064    pub fn balance_change(&self) -> Signed<Amount> {
2065        self.body.balance_change
2066    }
2067    pub fn use_full_commitment(&self) -> bool {
2068        self.body.use_full_commitment
2069    }
2070
2071    pub fn protocol_state_precondition(&self) -> ZkAppPreconditions {
2072        self.body.preconditions.network.clone()
2073    }
2074
2075    pub fn account_precondition(&self) -> AccountPreconditions {
2076        self.body.preconditions.account.clone()
2077    }
2078
2079    pub fn is_proved(&self) -> bool {
2080        match &self.body.authorization_kind {
2081            AuthorizationKind::Proof(_) => true,
2082            AuthorizationKind::Signature | AuthorizationKind::NoneGiven => false,
2083        }
2084    }
2085
2086    pub fn is_signed(&self) -> bool {
2087        match &self.body.authorization_kind {
2088            AuthorizationKind::Signature => true,
2089            AuthorizationKind::Proof(_) | AuthorizationKind::NoneGiven => false,
2090        }
2091    }
2092
2093    /// <https://github.com/MinaProtocol/mina/blob/436023ba41c43a50458a551b7ef7a9ae61670b25/src/lib/transaction_logic/mina_transaction_logic.ml#L1708>
2094    pub fn verification_key_hash(&self) -> Option<Fp> {
2095        match &self.body.authorization_kind {
2096            AuthorizationKind::Proof(vk_hash) => Some(*vk_hash),
2097            _ => None,
2098        }
2099    }
2100
2101    /// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_base/account_update.ml#L1333>
2102    pub fn of_simple(simple: &AccountUpdateSimple) -> Self {
2103        let AccountUpdateSimple {
2104            body:
2105                BodySimple {
2106                    public_key,
2107                    token_id,
2108                    update,
2109                    balance_change,
2110                    increment_nonce,
2111                    events,
2112                    actions,
2113                    call_data,
2114                    call_depth: _,
2115                    preconditions,
2116                    use_full_commitment,
2117                    implicit_account_creation_fee,
2118                    may_use_token,
2119                    authorization_kind,
2120                },
2121            authorization,
2122        } = simple.clone();
2123
2124        Self {
2125            body: Body {
2126                public_key,
2127                token_id,
2128                update,
2129                balance_change,
2130                increment_nonce,
2131                events,
2132                actions,
2133                call_data,
2134                preconditions,
2135                use_full_commitment,
2136                implicit_account_creation_fee,
2137                may_use_token,
2138                authorization_kind,
2139            },
2140            authorization,
2141        }
2142    }
2143
2144    /// Usage: Random `AccountUpdate` to compare hashes with OCaml
2145    pub fn rand() -> Self {
2146        let mut rng = rand::thread_rng();
2147        let rng = &mut rng;
2148
2149        Self {
2150            body: Body {
2151                public_key: gen_compressed(),
2152                token_id: TokenId(Fp::rand(rng)),
2153                update: Update {
2154                    app_state: std::array::from_fn(|_| SetOrKeep::gen(|| Fp::rand(rng))),
2155                    delegate: SetOrKeep::gen(gen_compressed),
2156                    verification_key: SetOrKeep::gen(VerificationKeyWire::gen),
2157                    permissions: SetOrKeep::gen(|| {
2158                        let auth_tag = [
2159                            ControlTag::NoneGiven,
2160                            ControlTag::Proof,
2161                            ControlTag::Signature,
2162                        ]
2163                        .choose(rng)
2164                        .unwrap();
2165
2166                        Permissions::gen(*auth_tag)
2167                    }),
2168                    zkapp_uri: SetOrKeep::gen(ZkAppUri::gen),
2169                    token_symbol: SetOrKeep::gen(TokenSymbol::gen),
2170                    timing: SetOrKeep::gen(|| Timing {
2171                        initial_minimum_balance: rng.gen(),
2172                        cliff_time: rng.gen(),
2173                        cliff_amount: rng.gen(),
2174                        vesting_period: rng.gen(),
2175                        vesting_increment: rng.gen(),
2176                    }),
2177                    voting_for: SetOrKeep::gen(|| VotingFor(Fp::rand(rng))),
2178                },
2179                balance_change: Signed::gen(),
2180                increment_nonce: rng.gen(),
2181                events: Events(gen_events()),
2182                actions: Actions(gen_events()),
2183                call_data: Fp::rand(rng),
2184                preconditions: Preconditions {
2185                    network: ZkAppPreconditions {
2186                        snarked_ledger_hash: OrIgnore::gen(|| Fp::rand(rng)),
2187                        blockchain_length: OrIgnore::gen(|| ClosedInterval::gen(|| rng.gen())),
2188                        min_window_density: OrIgnore::gen(|| ClosedInterval::gen(|| rng.gen())),
2189                        total_currency: OrIgnore::gen(|| ClosedInterval::gen(|| rng.gen())),
2190                        global_slot_since_genesis: OrIgnore::gen(|| {
2191                            ClosedInterval::gen(|| rng.gen())
2192                        }),
2193                        staking_epoch_data: EpochData::gen(),
2194                        next_epoch_data: EpochData::gen(),
2195                    },
2196                    account: AccountPreconditions(Account {
2197                        balance: OrIgnore::gen(|| ClosedInterval::gen(|| rng.gen())),
2198                        nonce: OrIgnore::gen(|| ClosedInterval::gen(|| rng.gen())),
2199                        receipt_chain_hash: OrIgnore::gen(|| Fp::rand(rng)),
2200                        delegate: OrIgnore::gen(gen_compressed),
2201                        state: std::array::from_fn(|_| OrIgnore::gen(|| Fp::rand(rng))),
2202                        action_state: OrIgnore::gen(|| Fp::rand(rng)),
2203                        proved_state: OrIgnore::gen(|| rng.gen()),
2204                        is_new: OrIgnore::gen(|| rng.gen()),
2205                    }),
2206                    valid_while: OrIgnore::gen(|| ClosedInterval::gen(|| rng.gen())),
2207                },
2208                use_full_commitment: rng.gen(),
2209                implicit_account_creation_fee: rng.gen(),
2210                may_use_token: {
2211                    match MayUseToken::No {
2212                        MayUseToken::No => (),
2213                        MayUseToken::ParentsOwnToken => (),
2214                        MayUseToken::InheritFromParent => (),
2215                    };
2216
2217                    [
2218                        MayUseToken::No,
2219                        MayUseToken::InheritFromParent,
2220                        MayUseToken::ParentsOwnToken,
2221                    ]
2222                    .choose(rng)
2223                    .cloned()
2224                    .unwrap()
2225                },
2226                authorization_kind: {
2227                    match AuthorizationKind::NoneGiven {
2228                        AuthorizationKind::NoneGiven => (),
2229                        AuthorizationKind::Signature => (),
2230                        AuthorizationKind::Proof(_) => (),
2231                    };
2232
2233                    [
2234                        AuthorizationKind::NoneGiven,
2235                        AuthorizationKind::Signature,
2236                        AuthorizationKind::Proof(Fp::rand(rng)),
2237                    ]
2238                    .choose(rng)
2239                    .cloned()
2240                    .unwrap()
2241                },
2242            },
2243            authorization: {
2244                match Control::NoneGiven {
2245                    Control::Proof(_) => (),
2246                    Control::Signature(_) => (),
2247                    Control::NoneGiven => (),
2248                };
2249
2250                match rng.gen_range(0..3) {
2251                    0 => Control::NoneGiven,
2252                    1 => Control::Signature(Signature::dummy()),
2253                    _ => Control::Proof(dummy::sideloaded_proof()),
2254                }
2255            },
2256        }
2257    }
2258}
2259
2260/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/zkapp_command.ml#L49>
2261#[derive(Debug, Clone, PartialEq)]
2262pub struct Tree<AccUpdate: Clone + AccountUpdateRef> {
2263    pub account_update: AccUpdate,
2264    pub account_update_digest: MutableFp,
2265    pub calls: CallForest<AccUpdate>,
2266}
2267
2268impl<AccUpdate: Clone + AccountUpdateRef> Tree<AccUpdate> {
2269    // TODO: Cache this result somewhere ?
2270    pub fn digest(&self) -> Fp {
2271        let stack_hash = match self.calls.0.first() {
2272            Some(e) => e.stack_hash.get().expect("Must call `ensure_hashed`"),
2273            None => Fp::zero(),
2274        };
2275        let account_update_digest = self.account_update_digest.get().unwrap();
2276        hash_with_kimchi(
2277            &MINA_ACCOUNT_UPDATE_NODE,
2278            &[account_update_digest, stack_hash],
2279        )
2280    }
2281
2282    fn fold<F>(&self, init: Vec<AccountId>, f: &mut F) -> Vec<AccountId>
2283    where
2284        F: FnMut(Vec<AccountId>, &AccUpdate) -> Vec<AccountId>,
2285    {
2286        self.calls.fold(f(init, &self.account_update), f)
2287    }
2288}
2289
2290/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/with_stack_hash.ml#L6>
2291#[derive(Debug, Clone)]
2292pub struct WithStackHash<AccUpdate: Clone + AccountUpdateRef> {
2293    pub elt: Tree<AccUpdate>,
2294    pub stack_hash: MutableFp,
2295}
2296
2297impl<AccUpdate: Clone + AccountUpdateRef + PartialEq> PartialEq for WithStackHash<AccUpdate> {
2298    fn eq(&self, other: &Self) -> bool {
2299        self.elt == other.elt && self.stack_hash == other.stack_hash
2300    }
2301}
2302
2303/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/zkapp_command.ml#L345>
2304#[derive(Debug, Clone, PartialEq)]
2305pub struct CallForest<AccUpdate: Clone + AccountUpdateRef>(pub Vec<WithStackHash<AccUpdate>>);
2306
2307impl<Data: Clone + AccountUpdateRef> Default for CallForest<Data> {
2308    fn default() -> Self {
2309        Self::new()
2310    }
2311}
2312
2313#[derive(Clone)]
2314struct CallForestContext {
2315    caller: TokenId,
2316    this: TokenId,
2317}
2318
2319pub trait AccountUpdateRef {
2320    fn account_update_ref(&self) -> &AccountUpdate;
2321}
2322impl AccountUpdateRef for AccountUpdate {
2323    fn account_update_ref(&self) -> &AccountUpdate {
2324        self
2325    }
2326}
2327impl<T> AccountUpdateRef for (AccountUpdate, T) {
2328    fn account_update_ref(&self) -> &AccountUpdate {
2329        let (this, _) = self;
2330        this
2331    }
2332}
2333impl AccountUpdateRef for AccountUpdateSimple {
2334    fn account_update_ref(&self) -> &AccountUpdate {
2335        // AccountUpdateSimple are first converted into `AccountUpdate`
2336        unreachable!()
2337    }
2338}
2339
2340impl<AccUpdate: Clone + AccountUpdateRef> CallForest<AccUpdate> {
2341    pub fn new() -> Self {
2342        Self(Vec::new())
2343    }
2344
2345    pub fn empty() -> Self {
2346        Self::new()
2347    }
2348
2349    pub fn is_empty(&self) -> bool {
2350        self.0.is_empty()
2351    }
2352
2353    // In OCaml push/pop to the head is cheap because they work with lists.
2354    // In Rust we use vectors so we will push/pop to the tail.
2355    // To work with the elements as if they were in the original order we need to iterate backwards
2356    pub fn iter(&self) -> impl Iterator<Item = &WithStackHash<AccUpdate>> {
2357        self.0.iter() //.rev()
2358    }
2359    // Warning: Update this if we ever change the order
2360    pub fn first(&self) -> Option<&WithStackHash<AccUpdate>> {
2361        self.0.first()
2362    }
2363    // Warning: Update this if we ever change the order
2364    pub fn tail(&self) -> Option<&[WithStackHash<AccUpdate>]> {
2365        self.0.get(1..)
2366    }
2367
2368    pub fn hash(&self) -> Fp {
2369        self.ensure_hashed();
2370        /*
2371        for x in self.0.iter() {
2372            println!("hash: {:?}", x.stack_hash);
2373        }
2374        */
2375
2376        if let Some(x) = self.first() {
2377            x.stack_hash.get().unwrap() // Never fail, we called `ensure_hashed`
2378        } else {
2379            Fp::zero()
2380        }
2381    }
2382
2383    fn cons_tree(&self, tree: Tree<AccUpdate>) -> Self {
2384        self.ensure_hashed();
2385
2386        let hash = tree.digest();
2387        let h_tl = self.hash();
2388
2389        let stack_hash = hash_with_kimchi(&MINA_ACCOUNT_UPDATE_CONS, &[hash, h_tl]);
2390        let node = WithStackHash::<AccUpdate> {
2391            elt: tree,
2392            stack_hash: MutableFp::new(stack_hash),
2393        };
2394        let mut forest = Vec::with_capacity(self.0.len() + 1);
2395        forest.push(node);
2396        forest.extend(self.0.iter().cloned());
2397
2398        Self(forest)
2399    }
2400
2401    pub fn pop_exn(&self) -> ((AccUpdate, CallForest<AccUpdate>), CallForest<AccUpdate>) {
2402        if self.0.is_empty() {
2403            panic!()
2404        }
2405
2406        let Tree::<AccUpdate> {
2407            account_update,
2408            calls,
2409            ..
2410        } = self.0[0].elt.clone();
2411        (
2412            (account_update, calls),
2413            CallForest(Vec::from_iter(self.0[1..].iter().cloned())),
2414        )
2415    }
2416
2417    /// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/mina_base/zkapp_command.ml#L68>
2418    fn fold_impl<'a, A, F>(&'a self, init: A, fun: &mut F) -> A
2419    where
2420        F: FnMut(A, &'a AccUpdate) -> A,
2421    {
2422        let mut accum = init;
2423        for elem in self.iter() {
2424            accum = fun(accum, &elem.elt.account_update);
2425            accum = elem.elt.calls.fold_impl(accum, fun);
2426        }
2427        accum
2428    }
2429
2430    pub fn fold<'a, A, F>(&'a self, init: A, mut fun: F) -> A
2431    where
2432        F: FnMut(A, &'a AccUpdate) -> A,
2433    {
2434        self.fold_impl(init, &mut fun)
2435    }
2436
2437    pub fn exists<'a, F>(&'a self, mut fun: F) -> bool
2438    where
2439        F: FnMut(&'a AccUpdate) -> bool,
2440    {
2441        self.fold(false, |acc, x| acc || fun(x))
2442    }
2443
2444    fn map_to_impl<F, AnotherAccUpdate: Clone + AccountUpdateRef>(
2445        &self,
2446        fun: &F,
2447    ) -> CallForest<AnotherAccUpdate>
2448    where
2449        F: Fn(&AccUpdate) -> AnotherAccUpdate,
2450    {
2451        CallForest::<AnotherAccUpdate>(
2452            self.iter()
2453                .map(|item| WithStackHash::<AnotherAccUpdate> {
2454                    elt: Tree::<AnotherAccUpdate> {
2455                        account_update: fun(&item.elt.account_update),
2456                        account_update_digest: item.elt.account_update_digest.clone(),
2457                        calls: item.elt.calls.map_to_impl(fun),
2458                    },
2459                    stack_hash: item.stack_hash.clone(),
2460                })
2461                .collect(),
2462        )
2463    }
2464
2465    #[must_use]
2466    pub fn map_to<F, AnotherAccUpdate: Clone + AccountUpdateRef>(
2467        &self,
2468        fun: F,
2469    ) -> CallForest<AnotherAccUpdate>
2470    where
2471        F: Fn(&AccUpdate) -> AnotherAccUpdate,
2472    {
2473        self.map_to_impl(&fun)
2474    }
2475
2476    fn map_with_trees_to_impl<F, AnotherAccUpdate: Clone + AccountUpdateRef>(
2477        &self,
2478        fun: &F,
2479    ) -> CallForest<AnotherAccUpdate>
2480    where
2481        F: Fn(&AccUpdate, &Tree<AccUpdate>) -> AnotherAccUpdate,
2482    {
2483        CallForest::<AnotherAccUpdate>(
2484            self.iter()
2485                .map(|item| {
2486                    let account_update = fun(&item.elt.account_update, &item.elt);
2487
2488                    WithStackHash::<AnotherAccUpdate> {
2489                        elt: Tree::<AnotherAccUpdate> {
2490                            account_update,
2491                            account_update_digest: item.elt.account_update_digest.clone(),
2492                            calls: item.elt.calls.map_with_trees_to_impl(fun),
2493                        },
2494                        stack_hash: item.stack_hash.clone(),
2495                    }
2496                })
2497                .collect(),
2498        )
2499    }
2500
2501    #[must_use]
2502    pub fn map_with_trees_to<F, AnotherAccUpdate: Clone + AccountUpdateRef>(
2503        &self,
2504        fun: F,
2505    ) -> CallForest<AnotherAccUpdate>
2506    where
2507        F: Fn(&AccUpdate, &Tree<AccUpdate>) -> AnotherAccUpdate,
2508    {
2509        self.map_with_trees_to_impl(&fun)
2510    }
2511
2512    fn try_map_to_impl<F, E, AnotherAccUpdate: Clone + AccountUpdateRef>(
2513        &self,
2514        fun: &mut F,
2515    ) -> Result<CallForest<AnotherAccUpdate>, E>
2516    where
2517        F: FnMut(&AccUpdate) -> Result<AnotherAccUpdate, E>,
2518    {
2519        Ok(CallForest::<AnotherAccUpdate>(
2520            self.iter()
2521                .map(|item| {
2522                    Ok(WithStackHash::<AnotherAccUpdate> {
2523                        elt: Tree::<AnotherAccUpdate> {
2524                            account_update: fun(&item.elt.account_update)?,
2525                            account_update_digest: item.elt.account_update_digest.clone(),
2526                            calls: item.elt.calls.try_map_to_impl(fun)?,
2527                        },
2528                        stack_hash: item.stack_hash.clone(),
2529                    })
2530                })
2531                .collect::<Result<_, E>>()?,
2532        ))
2533    }
2534
2535    pub fn try_map_to<F, E, AnotherAccUpdate: Clone + AccountUpdateRef>(
2536        &self,
2537        mut fun: F,
2538    ) -> Result<CallForest<AnotherAccUpdate>, E>
2539    where
2540        F: FnMut(&AccUpdate) -> Result<AnotherAccUpdate, E>,
2541    {
2542        self.try_map_to_impl(&mut fun)
2543    }
2544
2545    fn to_account_updates_impl(&self, accounts: &mut Vec<AccUpdate>) {
2546        // TODO: Check iteration order in OCaml
2547        for elem in self.iter() {
2548            accounts.push(elem.elt.account_update.clone());
2549            elem.elt.calls.to_account_updates_impl(accounts);
2550        }
2551    }
2552
2553    /// <https://github.com/MinaProtocol/mina/blob/3753a8593cc1577bcf4da16620daf9946d88e8e5/src/lib/mina_base/zkapp_command.ml#L436>
2554    pub fn to_account_updates(&self) -> Vec<AccUpdate> {
2555        let mut accounts = Vec::with_capacity(128);
2556        self.to_account_updates_impl(&mut accounts);
2557        accounts
2558    }
2559
2560    fn to_zkapp_command_with_hashes_list_impl(&self, output: &mut Vec<(AccUpdate, Fp)>) {
2561        self.iter().for_each(|item| {
2562            let WithStackHash { elt, stack_hash } = item;
2563            let Tree {
2564                account_update,
2565                account_update_digest: _,
2566                calls,
2567            } = elt;
2568            output.push((account_update.clone(), stack_hash.get().unwrap())); // Never fail, we called `ensure_hashed`
2569            calls.to_zkapp_command_with_hashes_list_impl(output);
2570        });
2571    }
2572
2573    pub fn to_zkapp_command_with_hashes_list(&self) -> Vec<(AccUpdate, Fp)> {
2574        self.ensure_hashed();
2575
2576        let mut output = Vec::with_capacity(128);
2577        self.to_zkapp_command_with_hashes_list_impl(&mut output);
2578        output
2579    }
2580
2581    pub fn ensure_hashed(&self) {
2582        let Some(first) = self.first() else {
2583            return;
2584        };
2585        if first.stack_hash.get().is_none() {
2586            self.accumulate_hashes();
2587        }
2588    }
2589}
2590
2591impl<AccUpdate: Clone + AccountUpdateRef> CallForest<AccUpdate> {
2592    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_command.ml#L583>
2593    pub fn accumulate_hashes(&self) {
2594        /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_command.ml#L293>
2595        fn cons(hash: Fp, h_tl: Fp) -> Fp {
2596            hash_with_kimchi(&MINA_ACCOUNT_UPDATE_CONS, &[hash, h_tl])
2597        }
2598
2599        /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_command.ml#L561>
2600        fn hash<AccUpdate: Clone + AccountUpdateRef>(
2601            elem: Option<&WithStackHash<AccUpdate>>,
2602        ) -> Fp {
2603            match elem {
2604                Some(next) => next.stack_hash.get().unwrap(), // Never fail, we hash them from reverse below
2605                None => Fp::zero(),
2606            }
2607        }
2608
2609        // We traverse the list in reverse here (to get same behavior as OCaml recursivity)
2610        // Note that reverse here means 0 to last, see `CallForest::iter` for explaination
2611        //
2612        // We use indexes to make the borrow checker happy
2613
2614        for index in (0..self.0.len()).rev() {
2615            let elem = &self.0[index];
2616            let WithStackHash {
2617                elt:
2618                    Tree::<AccUpdate> {
2619                        account_update,
2620                        account_update_digest,
2621                        calls,
2622                        ..
2623                    },
2624                ..
2625            } = elem;
2626
2627            calls.accumulate_hashes();
2628            account_update_digest.set(account_update.account_update_ref().digest());
2629
2630            let node_hash = elem.elt.digest();
2631            let hash = hash(self.0.get(index + 1));
2632
2633            self.0[index].stack_hash.set(cons(node_hash, hash));
2634        }
2635    }
2636}
2637
2638impl CallForest<AccountUpdate> {
2639    pub fn cons(
2640        &self,
2641        calls: Option<CallForest<AccountUpdate>>,
2642        account_update: AccountUpdate,
2643    ) -> Self {
2644        let account_update_digest = account_update.digest();
2645
2646        let tree = Tree::<AccountUpdate> {
2647            account_update,
2648            account_update_digest: MutableFp::new(account_update_digest),
2649            calls: calls.unwrap_or_else(|| CallForest(Vec::new())),
2650        };
2651        self.cons_tree(tree)
2652    }
2653
2654    pub fn accumulate_hashes_predicated(&mut self) {
2655        // Note: There seems to be no difference with `accumulate_hashes`
2656        self.accumulate_hashes();
2657    }
2658
2659    /// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_base/zkapp_command.ml#L830>
2660    pub fn of_wire(&mut self, _wired: &[MinaBaseZkappCommandTStableV1WireStableV1AccountUpdatesA]) {
2661        self.accumulate_hashes();
2662    }
2663
2664    /// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_base/zkapp_command.ml#L840>
2665    pub fn to_wire(&self, _wired: &mut [MinaBaseZkappCommandTStableV1WireStableV1AccountUpdatesA]) {
2666        // self.remove_callers(wired);
2667    }
2668}
2669
2670impl CallForest<(AccountUpdate, Option<WithHash<VerificationKey>>)> {
2671    // Don't implement `{from,to}_wire` because the binprot types contain the hashes
2672
2673    // /// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_base/zkapp_command.ml#L830>
2674    // pub fn of_wire(
2675    //     &mut self,
2676    //     _wired: &[v2::MinaBaseZkappCommandVerifiableStableV1AccountUpdatesA],
2677    // ) {
2678    //     self.accumulate_hashes(&|(account_update, _vk_opt)| account_update.digest());
2679    // }
2680
2681    // /// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_base/zkapp_command.ml#L840>
2682    // pub fn to_wire(
2683    //     &self,
2684    //     _wired: &mut [MinaBaseZkappCommandTStableV1WireStableV1AccountUpdatesA],
2685    // ) {
2686    //     // self.remove_callers(wired);
2687    // }
2688}
2689
2690/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/account_update.ml#L1081>
2691#[derive(Debug, Clone, PartialEq, Eq)]
2692pub struct FeePayerBody {
2693    pub public_key: CompressedPubKey,
2694    pub fee: Fee,
2695    pub valid_until: Option<Slot>,
2696    pub nonce: Nonce,
2697}
2698
2699/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/account_update.ml#L1484>
2700#[derive(Debug, Clone, PartialEq, Eq)]
2701pub struct FeePayer {
2702    pub body: FeePayerBody,
2703    pub authorization: Signature,
2704}
2705
2706/// <https://github.com/MinaProtocol/mina/blob/2ee6e004ba8c6a0541056076aab22ea162f7eb3a/src/lib/mina_base/zkapp_command.ml#L959>
2707#[derive(Debug, Clone, PartialEq)]
2708pub struct ZkAppCommand {
2709    pub fee_payer: FeePayer,
2710    pub account_updates: CallForest<AccountUpdate>,
2711    pub memo: Memo,
2712}
2713
2714#[derive(Debug, Clone, PartialEq, Hash, Eq, Ord, PartialOrd)]
2715pub enum AccessedOrNot {
2716    Accessed,
2717    NotAccessed,
2718}
2719
2720impl ZkAppCommand {
2721    pub fn fee_payer(&self) -> AccountId {
2722        let public_key = self.fee_payer.body.public_key.clone();
2723        AccountId::new(public_key, self.fee_token())
2724    }
2725
2726    pub fn fee_token(&self) -> TokenId {
2727        TokenId::default()
2728    }
2729
2730    pub fn fee(&self) -> Fee {
2731        self.fee_payer.body.fee
2732    }
2733
2734    pub fn fee_excess(&self) -> FeeExcess {
2735        FeeExcess::of_single((self.fee_token(), Signed::<Fee>::of_unsigned(self.fee())))
2736    }
2737
2738    fn fee_payer_account_update(&self) -> &FeePayer {
2739        let Self { fee_payer, .. } = self;
2740        fee_payer
2741    }
2742
2743    pub fn applicable_at_nonce(&self) -> Nonce {
2744        self.fee_payer_account_update().body.nonce
2745    }
2746
2747    pub fn weight(&self) -> u64 {
2748        let Self {
2749            fee_payer,
2750            account_updates,
2751            memo,
2752        } = self;
2753        [
2754            zkapp_weight::fee_payer(fee_payer),
2755            zkapp_weight::account_updates(account_updates),
2756            zkapp_weight::memo(memo),
2757        ]
2758        .iter()
2759        .sum()
2760    }
2761
2762    pub fn has_zero_vesting_period(&self) -> bool {
2763        self.account_updates
2764            .exists(|account_update| match &account_update.body.update.timing {
2765                SetOrKeep::Keep => false,
2766                SetOrKeep::Set(Timing { vesting_period, .. }) => vesting_period.is_zero(),
2767            })
2768    }
2769
2770    pub fn is_incompatible_version(&self) -> bool {
2771        self.account_updates.exists(|account_update| {
2772            match &account_update.body.update.permissions {
2773                SetOrKeep::Keep => false,
2774                SetOrKeep::Set(Permissions {
2775                    set_verification_key,
2776                    ..
2777                }) => {
2778                    let SetVerificationKey {
2779                        auth: _,
2780                        txn_version,
2781                    } = set_verification_key;
2782                    *txn_version != crate::TXN_VERSION_CURRENT
2783                }
2784            }
2785        })
2786    }
2787
2788    fn zkapp_cost(
2789        proof_segments: usize,
2790        signed_single_segments: usize,
2791        signed_pair_segments: usize,
2792    ) -> f64 {
2793        // (*10.26*np + 10.08*n2 + 9.14*n1 < 69.45*)
2794        let GenesisConstant {
2795            zkapp_proof_update_cost: proof_cost,
2796            zkapp_signed_pair_update_cost: signed_pair_cost,
2797            zkapp_signed_single_update_cost: signed_single_cost,
2798            ..
2799        } = GENESIS_CONSTANT;
2800
2801        (proof_cost * (proof_segments as f64))
2802            + (signed_pair_cost * (signed_pair_segments as f64))
2803            + (signed_single_cost * (signed_single_segments as f64))
2804    }
2805
2806    /// Zkapp_command transactions are filtered using this predicate
2807    /// - when adding to the transaction pool
2808    /// - in incoming blocks
2809    pub fn valid_size(&self) -> Result<(), String> {
2810        use crate::proofs::zkapp::group::{SegmentBasic, ZkappCommandIntermediateState};
2811
2812        let Self {
2813            account_updates,
2814            fee_payer: _,
2815            memo: _,
2816        } = self;
2817
2818        let events_elements = |events: &[Event]| -> usize { events.iter().map(Event::len).sum() };
2819
2820        let mut n_account_updates = 0;
2821        let (mut num_event_elements, mut num_action_elements) = (0, 0);
2822
2823        account_updates.fold((), |_, account_update| {
2824            num_event_elements += events_elements(account_update.body.events.events());
2825            num_action_elements += events_elements(account_update.body.actions.events());
2826            n_account_updates += 1;
2827        });
2828
2829        let group = std::iter::repeat(((), (), ()))
2830            .take(n_account_updates + 2) // + 2 to prepend two. See OCaml
2831            .collect::<Vec<_>>();
2832
2833        let groups = crate::proofs::zkapp::group::group_by_zkapp_command_rev::<_, (), (), ()>(
2834            [self],
2835            vec![vec![((), (), ())], group],
2836        );
2837
2838        let (mut proof_segments, mut signed_single_segments, mut signed_pair_segments) = (0, 0, 0);
2839
2840        for ZkappCommandIntermediateState { spec, .. } in &groups {
2841            match spec {
2842                SegmentBasic::Proved => proof_segments += 1,
2843                SegmentBasic::OptSigned => signed_single_segments += 1,
2844                SegmentBasic::OptSignedOptSigned => signed_pair_segments += 1,
2845            }
2846        }
2847
2848        let GenesisConstant {
2849            zkapp_transaction_cost_limit: cost_limit,
2850            max_event_elements,
2851            max_action_elements,
2852            ..
2853        } = GENESIS_CONSTANT;
2854
2855        let zkapp_cost_within_limit =
2856            Self::zkapp_cost(proof_segments, signed_single_segments, signed_pair_segments)
2857                < cost_limit;
2858        let valid_event_elements = num_event_elements <= max_event_elements;
2859        let valid_action_elements = num_action_elements <= max_action_elements;
2860
2861        if zkapp_cost_within_limit && valid_event_elements && valid_action_elements {
2862            return Ok(());
2863        }
2864
2865        let err = [
2866            (zkapp_cost_within_limit, "zkapp transaction too expensive"),
2867            (valid_event_elements, "too many event elements"),
2868            (valid_action_elements, "too many action elements"),
2869        ]
2870        .iter()
2871        .filter(|(b, _s)| !b)
2872        .map(|(_b, s)| s)
2873        .join(";");
2874
2875        Err(err)
2876    }
2877
2878    /// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_base/zkapp_command.ml#L997>
2879    pub fn account_access_statuses(
2880        &self,
2881        status: &TransactionStatus,
2882    ) -> Vec<(AccountId, AccessedOrNot)> {
2883        use AccessedOrNot::*;
2884        use TransactionStatus::*;
2885
2886        // always `Accessed` for fee payer
2887        let init = vec![(self.fee_payer(), Accessed)];
2888
2889        let status_sym = match status {
2890            Applied => Accessed,
2891            Failed(_) => NotAccessed,
2892        };
2893
2894        let ids = self
2895            .account_updates
2896            .fold(init, |mut accum, account_update| {
2897                accum.push((account_update.account_id(), status_sym.clone()));
2898                accum
2899            });
2900        // WARNING: the code previous to merging latest changes wasn't doing the "rev()" call. Check this in case of errors.
2901        ids.iter()
2902            .unique() /*.rev()*/
2903            .cloned()
2904            .collect()
2905    }
2906
2907    /// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_base/zkapp_command.ml#L1006>
2908    pub fn accounts_referenced(&self) -> Vec<AccountId> {
2909        self.account_access_statuses(&TransactionStatus::Applied)
2910            .into_iter()
2911            .map(|(id, _status)| id)
2912            .collect()
2913    }
2914
2915    /// <https://github.com/MinaProtocol/mina/blob/05c2f73d0f6e4f1341286843814ce02dcb3919e0/src/lib/mina_base/zkapp_command.ml#L1346>
2916    pub fn of_verifiable(verifiable: verifiable::ZkAppCommand) -> Self {
2917        Self {
2918            fee_payer: verifiable.fee_payer,
2919            account_updates: verifiable.account_updates.map_to(|(acc, _)| acc.clone()),
2920            memo: verifiable.memo,
2921        }
2922    }
2923
2924    /// <https://github.com/MinaProtocol/mina/blob/3753a8593cc1577bcf4da16620daf9946d88e8e5/src/lib/mina_base/zkapp_command.ml#L1386>
2925    pub fn account_updates_hash(&self) -> Fp {
2926        self.account_updates.hash()
2927    }
2928
2929    /// <https://github.com/MinaProtocol/mina/blob/02c9d453576fa47f78b2c388fb2e0025c47d991c/src/lib/mina_base/zkapp_command.ml#L989>
2930    pub fn extract_vks(&self) -> Vec<(AccountId, VerificationKeyWire)> {
2931        self.account_updates
2932            .fold(Vec::with_capacity(256), |mut acc, p| {
2933                if let SetOrKeep::Set(vk) = &p.body.update.verification_key {
2934                    acc.push((p.account_id(), vk.clone()));
2935                };
2936                acc
2937            })
2938    }
2939
2940    pub fn all_account_updates(&self) -> CallForest<AccountUpdate> {
2941        let p = &self.fee_payer;
2942
2943        let mut fee_payer = AccountUpdate::of_fee_payer(p.clone());
2944        fee_payer.authorization = Control::Signature(p.authorization.clone());
2945
2946        self.account_updates.cons(None, fee_payer)
2947    }
2948
2949    pub fn all_account_updates_list(&self) -> Vec<AccountUpdate> {
2950        let mut account_updates = Vec::with_capacity(16);
2951        account_updates.push(AccountUpdate::of_fee_payer(self.fee_payer.clone()));
2952
2953        self.account_updates.fold(account_updates, |mut acc, u| {
2954            acc.push(u.clone());
2955            acc
2956        })
2957    }
2958
2959    pub fn commitment(&self) -> TransactionCommitment {
2960        let account_updates_hash = self.account_updates_hash();
2961        TransactionCommitment::create(account_updates_hash)
2962    }
2963}
2964
2965pub struct MaybeWithStatus<T> {
2966    pub cmd: T,
2967    pub status: Option<TransactionStatus>,
2968}
2969
2970impl<T> From<WithStatus<T>> for MaybeWithStatus<T> {
2971    fn from(value: WithStatus<T>) -> Self {
2972        let WithStatus { data, status } = value;
2973        Self {
2974            cmd: data,
2975            status: Some(status),
2976        }
2977    }
2978}
2979
2980impl<T> From<MaybeWithStatus<T>> for WithStatus<T> {
2981    fn from(value: MaybeWithStatus<T>) -> Self {
2982        let MaybeWithStatus { cmd, status } = value;
2983        Self {
2984            data: cmd,
2985            status: status.unwrap(),
2986        }
2987    }
2988}
2989
2990impl<T> MaybeWithStatus<T> {
2991    pub fn cmd(&self) -> &T {
2992        &self.cmd
2993    }
2994    pub fn is_failed(&self) -> bool {
2995        self.status
2996            .as_ref()
2997            .map(TransactionStatus::is_failed)
2998            .unwrap_or(false)
2999    }
3000    pub fn map<V, F>(self, fun: F) -> MaybeWithStatus<V>
3001    where
3002        F: FnOnce(T) -> V,
3003    {
3004        MaybeWithStatus {
3005            cmd: fun(self.cmd),
3006            status: self.status,
3007        }
3008    }
3009}
3010
3011pub trait ToVerifiableCache {
3012    fn find(&self, account_id: &AccountId, vk_hash: &Fp) -> Option<&VerificationKeyWire>;
3013    fn add(&mut self, account_id: AccountId, vk: VerificationKeyWire);
3014}
3015
3016pub trait ToVerifiableStrategy {
3017    type Cache: ToVerifiableCache;
3018
3019    fn create_all(
3020        cmd: &ZkAppCommand,
3021        is_failed: bool,
3022        cache: &mut Self::Cache,
3023    ) -> Result<verifiable::ZkAppCommand, String> {
3024        let verified_cmd = verifiable::create(cmd, is_failed, |vk_hash, account_id| {
3025            cache
3026                .find(account_id, &vk_hash)
3027                .cloned()
3028                .or_else(|| {
3029                    cmd.extract_vks()
3030                        .iter()
3031                        .find(|(id, _)| account_id == id)
3032                        .map(|(_, key)| key.clone())
3033                })
3034                .ok_or_else(|| format!("verification key not found in cache: {:?}", vk_hash))
3035        })?;
3036        if !is_failed {
3037            for (account_id, vk) in cmd.extract_vks() {
3038                cache.add(account_id, vk);
3039            }
3040        }
3041        Ok(verified_cmd)
3042    }
3043}