mina_tree/account/
account.rs

1use crate::{
2    gen_compressed,
3    proofs::{
4        field::{Boolean, FieldWitness, ToBoolean},
5        numbers::{
6            currency::{CheckedBalance, CheckedCurrency},
7            nat::CheckedSlot,
8        },
9        to_field_elements::ToFieldElements,
10        transaction::{
11            make_group, transaction_snark::checked_min_balance_at_slot, Check, InnerCurve,
12            PlonkVerificationKeyEvals,
13        },
14        witness::Witness,
15    },
16    scan_state::currency::{Amount, Balance, Magnitude, Nonce, Slot, TxnVersion},
17    zkapps::snark::FlaggedOption,
18    AppendToInputs as _, MerklePath, MyCow, ToInputs,
19};
20use ark_ff::{BigInteger256, One, UniformRand, Zero};
21use mina_core::constants::PROTOCOL_VERSION;
22use mina_curves::pasta::Fp;
23use mina_p2p_messages::{
24    binprot::{BinProtRead, BinProtWrite},
25    v2,
26};
27use mina_signer::CompressedPubKey;
28use once_cell::sync::{Lazy, OnceCell};
29use poseidon::hash::{
30    hash_noinputs, hash_with_kimchi,
31    params::{
32        get_merkle_param_for_height, MINA_ACCOUNT, MINA_DERIVE_TOKEN_ID, MINA_SIDELOADED_VK,
33        MINA_ZKAPP_ACCOUNT, MINA_ZKAPP_URI, NO_INPUT_ZKAPP_ACTION_STATE_EMPTY_ELT,
34    },
35    Inputs,
36};
37use rand::{prelude::ThreadRng, seq::SliceRandom, Rng};
38use serde::{Deserialize, Serialize};
39use std::{io::Cursor, str::FromStr, sync::Arc};
40
41use super::common::*;
42
43/// Mina_numbers.Txn_version.current
44pub const TXN_VERSION_CURRENT: TxnVersion =
45    TxnVersion::from_u32(PROTOCOL_VERSION.transaction.as_u64() as u32);
46
47#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
48#[serde(into = "v2::MinaBaseTokenIdStableV2")]
49#[serde(try_from = "v2::MinaBaseTokenIdStableV2")]
50pub struct TokenId(pub Fp);
51
52impl std::fmt::Debug for TokenId {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        use crate::FpExt;
55        f.write_fmt(format_args!("TokenId({})", self.0.to_decimal()))
56    }
57}
58
59impl Default for TokenId {
60    fn default() -> Self {
61        Self(Fp::one())
62    }
63}
64
65impl From<u64> for TokenId {
66    fn from(num: u64) -> Self {
67        TokenId(Fp::from(num))
68    }
69}
70
71impl TokenId {
72    pub fn is_default(&self) -> bool {
73        self == &Self::default()
74    }
75}
76
77// <https://github.com/MinaProtocol/mina/blob/develop/src/lib/mina_base/account.ml#L93>
78#[derive(Clone, PartialEq, Eq, derive_more::From)]
79pub struct TokenSymbol(pub Vec<u8>);
80
81impl std::fmt::Debug for TokenSymbol {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        let s = String::from_utf8_lossy(self.as_bytes());
84        let s: &str = &s;
85        f.debug_tuple("TokenSymbol").field(&s).finish()
86    }
87}
88
89impl TokenSymbol {
90    pub fn gen() -> Self {
91        let mut rng = rand::thread_rng();
92
93        let sym: u32 = rng.gen();
94        let mut sym = sym.to_string();
95        sym.truncate(6);
96
97        Self(sym.into_bytes())
98    }
99
100    pub fn len(&self) -> usize {
101        self.0.len()
102    }
103
104    pub fn is_empty(&self) -> bool {
105        self.0.is_empty()
106    }
107
108    pub fn as_bytes(&self) -> &[u8] {
109        &self.0
110    }
111
112    pub fn to_bytes(&self, bytes: &mut [u8]) {
113        if self.0.is_empty() {
114            return;
115        }
116        let len = self.0.len();
117        let s: &[u8] = self.as_bytes();
118        bytes[..len].copy_from_slice(&s[..len.min(6)]);
119    }
120
121    pub fn to_field<F: FieldWitness>(&self) -> F {
122        let mut s = <[u8; 32]>::default();
123        self.to_bytes(&mut s);
124
125        // There is an assumption that a token symbol is on 6 bytes, therefore
126        // it will be smaller than the order
127        F::from_le_bytes_mod_order(&s)
128    }
129}
130
131#[allow(clippy::derivable_impls)]
132impl Default for TokenSymbol {
133    fn default() -> Self {
134        // empty string
135        // <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/account.ml#L133>
136        Self(Vec::new())
137    }
138}
139
140impl From<&mina_p2p_messages::string::TokenSymbol> for TokenSymbol {
141    fn from(value: &mina_p2p_messages::string::TokenSymbol) -> Self {
142        Self(value.as_ref().to_vec())
143    }
144}
145
146impl From<&TokenSymbol> for mina_p2p_messages::string::TokenSymbol {
147    fn from(value: &TokenSymbol) -> Self {
148        value.0.as_slice().into()
149    }
150}
151
152impl ToInputs for TokenSymbol {
153    fn to_inputs(&self, inputs: &mut Inputs) {
154        // <https://github.com/MinaProtocol/mina/blob/2fac5d806a06af215dbab02f7b154b4f032538b7/src/lib/mina_base/account.ml#L97>
155        //assert!(self.len() <= 6);
156
157        let mut s = <[u8; 6]>::default();
158        self.to_bytes(&mut s);
159        inputs.append_u48(s);
160    }
161}
162
163#[derive(Clone, Debug, PartialEq, Eq)]
164pub struct SetVerificationKey<Controller> {
165    pub auth: Controller,
166    pub txn_version: TxnVersion,
167}
168
169// <https://github.com/MinaProtocol/mina/blob/develop/src/lib/mina_base/permissions.mli#L49>
170#[derive(Clone, Debug, PartialEq, Eq)]
171pub struct Permissions<Controller> {
172    pub edit_state: Controller,
173    pub access: Controller,
174    pub send: Controller,
175    pub receive: Controller,
176    pub set_delegate: Controller,
177    pub set_permissions: Controller,
178    pub set_verification_key: SetVerificationKey<Controller>,
179    pub set_zkapp_uri: Controller,
180    pub edit_action_state: Controller,
181    pub set_token_symbol: Controller,
182    pub increment_nonce: Controller,
183    pub set_voting_for: Controller,
184    pub set_timing: Controller,
185}
186
187pub enum AuthOrVersion<'a, T> {
188    Auth(&'a T),
189    Version(TxnVersion),
190}
191
192impl Permissions<AuthRequired> {
193    pub fn iter_as_bits<F>(&self, mut fun: F)
194    where
195        F: FnMut(AuthOrVersion<'_, bool>),
196    {
197        let Self {
198            edit_state,
199            access,
200            send,
201            receive,
202            set_delegate,
203            set_permissions,
204            set_verification_key:
205                SetVerificationKey {
206                    auth: set_verification_key_auth,
207                    txn_version,
208                },
209            set_zkapp_uri,
210            edit_action_state,
211            set_token_symbol,
212            increment_nonce,
213            set_voting_for,
214            set_timing,
215        } = self;
216
217        for auth in [
218            AuthOrVersion::Auth(edit_state),
219            AuthOrVersion::Auth(access),
220            AuthOrVersion::Auth(send),
221            AuthOrVersion::Auth(receive),
222            AuthOrVersion::Auth(set_delegate),
223            AuthOrVersion::Auth(set_permissions),
224            AuthOrVersion::Auth(set_verification_key_auth),
225            AuthOrVersion::Version(*txn_version),
226            AuthOrVersion::Auth(set_zkapp_uri),
227            AuthOrVersion::Auth(edit_action_state),
228            AuthOrVersion::Auth(set_token_symbol),
229            AuthOrVersion::Auth(increment_nonce),
230            AuthOrVersion::Auth(set_voting_for),
231            AuthOrVersion::Auth(set_timing),
232        ] {
233            match auth {
234                AuthOrVersion::Auth(auth) => {
235                    for bit in auth.encode().to_bits() {
236                        fun(AuthOrVersion::Auth(&bit));
237                    }
238                }
239                AuthOrVersion::Version(version) => {
240                    fun(AuthOrVersion::Version(version));
241                }
242            }
243        }
244    }
245}
246
247impl ToInputs for Permissions<AuthRequired> {
248    fn to_inputs(&self, inputs: &mut Inputs) {
249        self.iter_as_bits(|bit| match bit {
250            AuthOrVersion::Auth(bit) => inputs.append_bool(*bit),
251            AuthOrVersion::Version(version) => inputs.append(&version),
252        });
253    }
254}
255
256impl<F: FieldWitness> Check<F> for Permissions<AuthRequired> {
257    fn check(&self, w: &mut Witness<F>) {
258        let Self {
259            edit_state: _,
260            access: _,
261            send: _,
262            receive: _,
263            set_delegate: _,
264            set_permissions: _,
265            set_verification_key:
266                SetVerificationKey {
267                    auth: _,
268                    txn_version,
269                },
270            set_zkapp_uri: _,
271            edit_action_state: _,
272            set_token_symbol: _,
273            increment_nonce: _,
274            set_voting_for: _,
275            set_timing: _,
276        } = self;
277
278        txn_version.check(w);
279    }
280}
281
282impl Default for Permissions<AuthRequired> {
283    fn default() -> Self {
284        Self::user_default()
285    }
286}
287
288impl Permissions<AuthRequired> {
289    pub fn user_default() -> Self {
290        use AuthRequired::*;
291        Self {
292            edit_state: Signature,
293            send: Signature,
294            receive: None,
295            set_delegate: Signature,
296            set_permissions: Signature,
297            set_verification_key: SetVerificationKey {
298                auth: Signature,
299                txn_version: TXN_VERSION_CURRENT,
300            },
301            set_zkapp_uri: Signature,
302            edit_action_state: Signature,
303            set_token_symbol: Signature,
304            increment_nonce: Signature,
305            set_voting_for: Signature,
306            set_timing: Signature,
307            access: None,
308        }
309    }
310
311    pub fn empty() -> Self {
312        use AuthRequired::*;
313        Self {
314            edit_state: None,
315            send: None,
316            receive: None,
317            access: None,
318            set_delegate: None,
319            set_permissions: None,
320            set_verification_key: SetVerificationKey {
321                auth: None,
322                txn_version: TXN_VERSION_CURRENT,
323            },
324            set_zkapp_uri: None,
325            edit_action_state: None,
326            set_token_symbol: None,
327            increment_nonce: None,
328            set_voting_for: None,
329            set_timing: None,
330        }
331    }
332
333    /// <https://github.com/MinaProtocol/mina/blob/3753a8593cc1577bcf4da16620daf9946d88e8e5/src/lib/mina_base/permissions.ml#L385>
334    pub fn gen(auth_tag: ControlTag) -> Self {
335        let mut rng = rand::thread_rng();
336
337        let auth_required_gen = match auth_tag {
338            ControlTag::Proof => AuthRequired::gen_for_proof_authorization,
339            ControlTag::Signature => AuthRequired::gen_for_signature_authorization,
340            ControlTag::NoneGiven => AuthRequired::gen_for_none_given_authorization,
341        };
342
343        Self {
344            edit_state: auth_required_gen(&mut rng),
345            send: auth_required_gen(&mut rng),
346            receive: auth_required_gen(&mut rng),
347            set_delegate: auth_required_gen(&mut rng),
348            set_permissions: auth_required_gen(&mut rng),
349            set_verification_key: SetVerificationKey {
350                auth: auth_required_gen(&mut rng),
351                txn_version: TXN_VERSION_CURRENT,
352            },
353            set_zkapp_uri: auth_required_gen(&mut rng),
354            edit_action_state: auth_required_gen(&mut rng),
355            set_token_symbol: auth_required_gen(&mut rng),
356            increment_nonce: auth_required_gen(&mut rng),
357            set_voting_for: auth_required_gen(&mut rng),
358            set_timing: auth_required_gen(&mut rng),
359            access: {
360                // Access permission is significantly more restrictive, do not arbitrarily
361                // set it when tests may not be intending to exercise it.
362                AuthRequired::gen_for_none_given_authorization(&mut rng)
363            },
364        }
365    }
366}
367
368#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
369pub enum ProofVerified {
370    N0,
371    N1,
372    N2,
373}
374
375impl ProofVerified {
376    /// <https://github.com/MinaProtocol/mina/blob/47a269c2e917775b34a83775b8a55fcc44830831/src/lib/pickles_base/proofs_verified.ml#L17>
377    pub fn to_int(&self) -> usize {
378        match self {
379            ProofVerified::N0 => 0,
380            ProofVerified::N1 => 1,
381            ProofVerified::N2 => 2,
382        }
383    }
384}
385
386impl ToInputs for ProofVerified {
387    /// <https://github.com/MinaProtocol/mina/blob/436023ba41c43a50458a551b7ef7a9ae61670b25/src/lib/pickles_base/proofs_verified.ml#L125>
388    fn to_inputs(&self, inputs: &mut Inputs) {
389        let bits = match self {
390            ProofVerified::N0 => [true, false, false],
391            ProofVerified::N1 => [false, true, false],
392            ProofVerified::N2 => [false, false, true],
393        };
394
395        for bit in bits {
396            inputs.append_bool(bit);
397        }
398    }
399}
400
401// One_hot
402impl ToFieldElements<Fp> for ProofVerified {
403    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
404        use Boolean::{False, True};
405
406        let bits = match self {
407            ProofVerified::N0 => [True, False, False],
408            ProofVerified::N1 => [False, True, False],
409            ProofVerified::N2 => [False, False, True],
410        };
411
412        bits.to_field_elements(fields);
413    }
414}
415
416#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
417pub struct VerificationKey {
418    pub max_proofs_verified: ProofVerified,
419    pub actual_wrap_domain_size: ProofVerified,
420    pub wrap_index: Box<PlonkVerificationKeyEvals<Fp>>,
421    // `wrap_vk` is not used for hash inputs
422    pub wrap_vk: Option<()>,
423}
424
425impl Check<Fp> for VerificationKey {
426    fn check(&self, w: &mut Witness<Fp>) {
427        let Self {
428            max_proofs_verified: _,
429            actual_wrap_domain_size: _,
430            wrap_index,
431            wrap_vk: _,
432        } = self;
433
434        wrap_index.check(w);
435    }
436}
437
438impl ToFieldElements<Fp> for VerificationKey {
439    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
440        let Self {
441            max_proofs_verified,
442            actual_wrap_domain_size,
443            wrap_index,
444            wrap_vk: _,
445        } = self;
446
447        let PlonkVerificationKeyEvals {
448            sigma,
449            coefficients,
450            generic,
451            psm,
452            complete_add,
453            mul,
454            emul,
455            endomul_scalar,
456        } = wrap_index.as_ref();
457
458        max_proofs_verified.to_field_elements(fields);
459        actual_wrap_domain_size.to_field_elements(fields);
460
461        sigma.to_field_elements(fields);
462        coefficients.to_field_elements(fields);
463        generic.to_field_elements(fields);
464        psm.to_field_elements(fields);
465        complete_add.to_field_elements(fields);
466        mul.to_field_elements(fields);
467        emul.to_field_elements(fields);
468        endomul_scalar.to_field_elements(fields);
469    }
470}
471
472impl ToInputs for VerificationKey {
473    fn to_inputs(&self, inputs: &mut Inputs) {
474        let Self {
475            max_proofs_verified,
476            actual_wrap_domain_size,
477            wrap_index,
478            wrap_vk: _,
479        } = self;
480
481        let PlonkVerificationKeyEvals {
482            sigma,
483            coefficients,
484            generic,
485            psm,
486            complete_add,
487            mul,
488            emul,
489            endomul_scalar,
490        } = wrap_index.as_ref();
491
492        inputs.append(max_proofs_verified);
493        inputs.append(actual_wrap_domain_size);
494
495        for sigma in sigma {
496            inputs.append(sigma);
497        }
498        for coefficients in coefficients {
499            inputs.append(coefficients);
500        }
501        inputs.append(generic);
502        inputs.append(psm);
503        inputs.append(complete_add);
504        inputs.append(mul);
505        inputs.append(emul);
506        inputs.append(endomul_scalar);
507    }
508}
509
510impl VerificationKey {
511    /// <https://github.com/MinaProtocol/mina/blob/436023ba41c43a50458a551b7ef7a9ae61670b25/src/lib/pickles/side_loaded_verification_key.ml#L310>
512    pub fn dummy() -> Arc<Self> {
513        static VK: OnceCell<Arc<VerificationKey>> = OnceCell::new();
514
515        VK.get_or_init(|| {
516            let g = InnerCurve::of_affine(make_group(
517                Fp::one(),
518                Fp::from_str(
519                    "12418654782883325593414442427049395787963493412651469444558597405572177144507",
520                )
521                .unwrap(),
522            ));
523            Arc::new(Self {
524                max_proofs_verified: ProofVerified::N2,
525                actual_wrap_domain_size: ProofVerified::N2,
526                wrap_index: PlonkVerificationKeyEvals {
527                    sigma: std::array::from_fn(|_| g.clone()),
528                    coefficients: std::array::from_fn(|_| g.clone()),
529                    generic: g.clone(),
530                    psm: g.clone(),
531                    complete_add: g.clone(),
532                    mul: g.clone(),
533                    emul: g.clone(),
534                    endomul_scalar: g,
535                }
536                .into(),
537                wrap_vk: None,
538            })
539        })
540        .clone()
541    }
542
543    pub fn digest(&self) -> Fp {
544        self.hash()
545    }
546
547    pub fn hash(&self) -> Fp {
548        self.hash_with_param(&MINA_SIDELOADED_VK)
549    }
550
551    pub fn gen() -> Self {
552        let mut rng = rand::thread_rng();
553
554        VerificationKey {
555            max_proofs_verified: {
556                let n: u64 = rng.gen();
557
558                if n.is_multiple_of(3) {
559                    ProofVerified::N2
560                } else if n.is_multiple_of(2) {
561                    ProofVerified::N1
562                } else {
563                    ProofVerified::N0
564                }
565            },
566            wrap_index: PlonkVerificationKeyEvals::rand().into(),
567            wrap_vk: None,
568            actual_wrap_domain_size: {
569                let n: u64 = rng.gen();
570
571                if n.is_multiple_of(3) {
572                    ProofVerified::N2
573                } else if n.is_multiple_of(2) {
574                    ProofVerified::N1
575                } else {
576                    ProofVerified::N0
577                }
578            },
579        }
580    }
581}
582
583#[derive(Clone, PartialEq, Eq, derive_more::From)]
584pub struct ZkAppUri(Vec<u8>);
585
586impl std::fmt::Debug for ZkAppUri {
587    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
588        let s = String::from_utf8_lossy(&self.0);
589        let s: &str = &s;
590        f.debug_tuple("ZkAppUri").field(&s).finish()
591    }
592}
593
594fn default_zkapp_uri_hash() -> Fp {
595    static HASH: Lazy<Fp> = Lazy::new(|| {
596        let mut inputs = Inputs::new();
597        inputs.append(&Fp::zero());
598        inputs.append(&Fp::zero());
599        hash_with_kimchi(&MINA_ZKAPP_URI, &inputs.to_fields())
600    });
601    *HASH
602}
603
604impl ZkAppUri {
605    #[allow(clippy::new_without_default)]
606    pub fn new() -> Self {
607        Self(Vec::new())
608    }
609
610    pub fn gen() -> Self {
611        let mut rng = rand::thread_rng();
612
613        let zkapp_uri: u64 = rng.gen();
614        let zkapp_uri = zkapp_uri.to_string();
615
616        Self(zkapp_uri.into_bytes())
617    }
618
619    fn opt_to_field(opt: Option<&ZkAppUri>) -> Fp {
620        let Some(zkapp_uri) = opt else {
621            return default_zkapp_uri_hash();
622        };
623        let mut inputs = Inputs::new();
624        for c in zkapp_uri.0.as_slice() {
625            for j in 0..8 {
626                inputs.append_bool((c & (1 << j)) != 0);
627            }
628        }
629        inputs.append_bool(true);
630        hash_with_kimchi(&MINA_ZKAPP_URI, &inputs.to_fields())
631    }
632}
633
634impl ToFieldElements<Fp> for Option<&ZkAppUri> {
635    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
636        let field_zkapp_uri = ZkAppUri::opt_to_field(*self);
637        field_zkapp_uri.to_field_elements(fields);
638    }
639}
640
641impl ToInputs for Option<&ZkAppUri> {
642    /// <https://github.com/MinaProtocol/mina/blob/3fe924c80a4d01f418b69f27398f5f93eb652514/src/lib/mina_base/zkapp_account.ml#L313>
643    fn to_inputs(&self, inputs: &mut Inputs) {
644        let field_zkapp_uri = ZkAppUri::opt_to_field(*self);
645        inputs.append(&field_zkapp_uri);
646    }
647}
648
649impl std::ops::Deref for ZkAppUri {
650    type Target = [u8];
651
652    fn deref(&self) -> &Self::Target {
653        &self.0
654    }
655}
656
657impl From<&mina_p2p_messages::string::ZkAppUri> for ZkAppUri {
658    fn from(value: &mina_p2p_messages::string::ZkAppUri) -> Self {
659        Self(value.as_ref().to_vec())
660    }
661}
662
663impl From<&ZkAppUri> for mina_p2p_messages::string::ZkAppUri {
664    fn from(value: &ZkAppUri) -> Self {
665        Self::from(value.0.clone())
666    }
667}
668
669/// Alternative to `Rc<Cell<Option<Fp>>>` that is `Send`
670// TODO: Use atomics here, instead of mutex
671#[derive(Clone, Debug)]
672pub struct MutableFp {
673    fp: Arc<std::sync::Mutex<Option<Fp>>>,
674}
675
676// We skip some hashing during fuzzing to speedup things, but we still need to catch potential in transaction application.
677// The fuzzer will call apply_transactions with GLOBAL_SKIP_PARTIAL_EQ disabled and enable it for everything else.
678#[cfg(feature = "fuzzing")]
679pub static GLOBAL_SKIP_PARTIAL_EQ: Lazy<std::sync::RwLock<bool>> =
680    Lazy::new(|| std::sync::RwLock::new(false));
681
682impl Eq for MutableFp {}
683
684impl PartialEq for MutableFp {
685    fn eq(&self, other: &Self) -> bool {
686        #[cfg(feature = "fuzzing")]
687        if *GLOBAL_SKIP_PARTIAL_EQ.read().unwrap() {
688            return true;
689        }
690
691        self.get().unwrap() == other.get().unwrap()
692    }
693}
694
695impl MutableFp {
696    pub fn empty() -> Self {
697        Self {
698            fp: Arc::new(std::sync::Mutex::new(None)),
699        }
700    }
701    pub fn new(fp: Fp) -> Self {
702        Self {
703            fp: Arc::new(std::sync::Mutex::new(Some(fp))),
704        }
705    }
706    pub fn get(&self) -> Option<Fp> {
707        *self.fp.lock().unwrap()
708    }
709    pub fn set(&self, fp: Fp) {
710        *self.fp.lock().unwrap() = Some(fp)
711    }
712}
713
714#[derive(Clone, Debug)]
715pub struct VerificationKeyWire {
716    vk: VerificationKey,
717    hash: MutableFp,
718}
719
720impl Eq for VerificationKeyWire {}
721
722impl PartialEq for VerificationKeyWire {
723    fn eq(&self, other: &Self) -> bool {
724        match (self.hash.get(), other.hash.get()) {
725            (Some(this), Some(other)) => this == other,
726            _ => self.vk == other.vk,
727        }
728    }
729}
730
731impl VerificationKeyWire {
732    pub fn new(vk: VerificationKey) -> Self {
733        Self {
734            vk,
735            hash: MutableFp::empty(),
736        }
737    }
738
739    pub fn with_hash(vk: VerificationKey, hash: Fp) -> Self {
740        Self {
741            vk,
742            hash: MutableFp::new(hash),
743        }
744    }
745
746    pub fn hash(&self) -> Fp {
747        let Self { vk, hash } = self;
748        if let Some(hash) = hash.get() {
749            return hash;
750        }
751        let vk_hash = vk.hash();
752        hash.set(vk_hash);
753        vk_hash
754    }
755
756    pub fn vk(&self) -> &VerificationKey {
757        let Self { vk, hash: _ } = self;
758        vk
759    }
760
761    pub fn dummy_hash() -> Fp {
762        static DUMMY: OnceCell<Arc<Fp>> = OnceCell::new();
763        **DUMMY.get_or_init(|| {
764            let vk = VerificationKey::dummy();
765            Arc::new(vk.hash())
766        })
767    }
768
769    pub fn dummy() -> Self {
770        Self {
771            vk: (*VerificationKey::dummy()).clone(),
772            hash: MutableFp::new(Self::dummy_hash()),
773        }
774    }
775
776    pub fn gen() -> Self {
777        Self::new(VerificationKey::gen())
778    }
779}
780
781// <https://github.com/MinaProtocol/mina/blob/develop/src/lib/mina_base/zkapp_account.ml#L148-L170>
782#[derive(Clone, Debug, PartialEq, Eq)]
783pub struct ZkAppAccount {
784    pub app_state: [Fp; 8],
785    pub verification_key: Option<VerificationKeyWire>,
786    // pub verification_key: Option<WithHash<VerificationKey>>, // TODO
787    pub zkapp_version: u32,
788    pub action_state: [Fp; 5],
789    pub last_action_slot: Slot,
790    pub proved_state: bool,
791    pub zkapp_uri: ZkAppUri,
792}
793
794impl ToInputs for ZkAppAccount {
795    fn to_inputs(&self, inputs: &mut Inputs) {
796        let Self {
797            app_state,
798            verification_key,
799            zkapp_version,
800            action_state,
801            last_action_slot,
802            proved_state,
803            zkapp_uri,
804        } = self;
805
806        // Self::zkapp_uri
807        inputs.append(&Some(zkapp_uri));
808
809        inputs.append_bool(*proved_state);
810        inputs.append_u32(last_action_slot.as_u32());
811        for fp in action_state {
812            inputs.append_field(*fp);
813        }
814        inputs.append_u32(*zkapp_version);
815        let vk_hash = verification_key
816            .as_ref()
817            .map(VerificationKeyWire::hash)
818            .unwrap_or_else(VerificationKeyWire::dummy_hash);
819        inputs.append_field(vk_hash);
820        for fp in app_state {
821            inputs.append_field(*fp);
822        }
823    }
824}
825
826impl ToFieldElements<Fp> for ZkAppAccount {
827    fn to_field_elements(&self, fields: &mut Vec<Fp>) {
828        let Self {
829            app_state,
830            verification_key,
831            zkapp_version,
832            action_state,
833            last_action_slot,
834            proved_state,
835            zkapp_uri,
836        } = self;
837
838        app_state.to_field_elements(fields);
839        (
840            FlaggedOption::from(
841                verification_key
842                    .as_ref()
843                    .map(VerificationKeyWire::hash)
844                    .as_ref(),
845            ),
846            VerificationKeyWire::dummy_hash,
847        )
848            .to_field_elements(fields);
849        Fp::from(*zkapp_version).to_field_elements(fields);
850        action_state.to_field_elements(fields);
851        last_action_slot.to_field_elements(fields);
852        proved_state.to_field_elements(fields);
853        Some(zkapp_uri).to_field_elements(fields);
854    }
855}
856
857impl Check<Fp> for ZkAppAccount {
858    fn check(&self, w: &mut Witness<Fp>) {
859        let Self {
860            app_state: _,
861            verification_key: _,
862            zkapp_version,
863            action_state: _,
864            last_action_slot,
865            proved_state: _,
866            zkapp_uri: _,
867        } = self;
868
869        zkapp_version.check(w);
870        last_action_slot.check(w);
871    }
872}
873
874impl Default for ZkAppAccount {
875    fn default() -> Self {
876        Self {
877            app_state: [Fp::zero(); 8],
878            verification_key: None,
879            zkapp_version: 0,
880            action_state: {
881                let empty = Self::empty_action_state();
882                [empty, empty, empty, empty, empty]
883            },
884            last_action_slot: Slot::zero(),
885            proved_state: false,
886            zkapp_uri: ZkAppUri::new(),
887        }
888    }
889}
890
891impl ZkAppAccount {
892    pub fn hash(&self) -> Fp {
893        self.hash_with_param(&MINA_ZKAPP_ACCOUNT)
894    }
895
896    /// empty_state_element
897    pub fn empty_action_state() -> Fp {
898        hash_noinputs(&NO_INPUT_ZKAPP_ACTION_STATE_EMPTY_ELT)
899    }
900
901    pub fn is_default(&self) -> bool {
902        self == &Self::default()
903    }
904}
905
906/// An `AccountId` implementing `Ord` & `PartialOrd`, reproducing OCaml ordering.
907///
908/// We compare them using `BigInteger256`, not `Fp`.
909/// This is a different type than `AccountId` because we want to keep the
910/// `BigInteger256` values around, without re-computing them every time
911/// `<Self as PartialOrd>::partial_cmp` is called.
912///
913/// So far this is used only when `AccountId` are used as keys in BTreeMap, in zkapp application.
914#[derive(Clone, Eq)]
915pub struct AccountIdOrderable {
916    // Keep the values as `BigInteger256`. This avoid re-computing them
917    // every time `<Self as PartialOrd>::partial_cmp` is called
918    bigint_public_key_x: BigInteger256,
919    bigint_public_key_is_odd: bool,
920    bigint_token_id: BigInteger256,
921    // We keep the original values, to convert back into `AccountId` without computing
922    public_key: CompressedPubKey,
923    token_id: TokenId,
924}
925
926impl PartialEq for AccountIdOrderable {
927    fn eq(&self, other: &Self) -> bool {
928        let Self {
929            bigint_public_key_x: self_x,
930            bigint_public_key_is_odd: self_is_odd,
931            bigint_token_id: self_token_id,
932            public_key: _,
933            token_id: _,
934        } = self;
935        let Self {
936            bigint_public_key_x: other_x,
937            bigint_public_key_is_odd: other_is_odd,
938            bigint_token_id: other_token_id,
939            public_key: _,
940            token_id: _,
941        } = other;
942
943        self_x == other_x && self_is_odd == other_is_odd && self_token_id == other_token_id
944    }
945}
946impl Ord for AccountIdOrderable {
947    /// Ignore `Self::public_key` and `Self::token_id`
948    /// We only use their bigint representations
949    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
950        let Self {
951            bigint_public_key_x: self_x,
952            bigint_public_key_is_odd: self_is_odd,
953            bigint_token_id: self_token_id,
954            public_key: _,
955            token_id: _,
956        } = self;
957
958        let Self {
959            bigint_public_key_x: other_x,
960            bigint_public_key_is_odd: other_is_odd,
961            bigint_token_id: other_token_id,
962            public_key: _,
963            token_id: _,
964        } = other;
965
966        match self_x.cmp(other_x) {
967            std::cmp::Ordering::Equal => {}
968            non_eq => return non_eq,
969        }
970        match self_is_odd.cmp(other_is_odd) {
971            std::cmp::Ordering::Equal => {}
972            non_eq => return non_eq,
973        }
974        self_token_id.cmp(other_token_id)
975    }
976}
977
978impl PartialOrd for AccountIdOrderable {
979    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
980        Some(self.cmp(other))
981    }
982}
983
984impl From<AccountId> for AccountIdOrderable {
985    fn from(value: AccountId) -> Self {
986        let AccountId {
987            public_key,
988            token_id,
989        } = value;
990        let CompressedPubKey { x, is_odd } = &public_key;
991
992        Self {
993            bigint_public_key_x: (*x).into(),
994            bigint_public_key_is_odd: *is_odd,
995            bigint_token_id: token_id.0.into(),
996            public_key,
997            token_id,
998        }
999    }
1000}
1001
1002impl From<AccountIdOrderable> for AccountId {
1003    fn from(value: AccountIdOrderable) -> Self {
1004        let AccountIdOrderable {
1005            bigint_public_key_x: _,
1006            bigint_public_key_is_odd: _,
1007            bigint_token_id: _,
1008            public_key,
1009            token_id,
1010        } = value;
1011
1012        Self {
1013            public_key,
1014            token_id,
1015        }
1016    }
1017}
1018
1019#[derive(Clone, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
1020#[serde(into = "v2::MinaBaseAccountIdStableV2")]
1021#[serde(try_from = "v2::MinaBaseAccountIdStableV2")]
1022pub struct AccountId {
1023    pub public_key: CompressedPubKey,
1024    pub token_id: TokenId,
1025}
1026
1027impl ToInputs for AccountId {
1028    fn to_inputs(&self, inputs: &mut Inputs) {
1029        let Self {
1030            public_key,
1031            token_id,
1032        } = self;
1033        inputs.append(public_key);
1034        inputs.append(token_id);
1035    }
1036}
1037
1038impl AccountId {
1039    pub fn empty() -> Self {
1040        Self {
1041            public_key: CompressedPubKey::empty(),
1042            token_id: TokenId::default(),
1043        }
1044    }
1045
1046    pub fn is_empty(&self) -> bool {
1047        self == &Self::empty()
1048    }
1049
1050    pub fn derive_token_id(&self) -> TokenId {
1051        // TODO: Use `ToInputs`
1052        let is_odd_field = match self.public_key.is_odd {
1053            true => Fp::one(),
1054            false => Fp::zero(),
1055        };
1056
1057        TokenId(hash_with_kimchi(
1058            &MINA_DERIVE_TOKEN_ID,
1059            &[self.public_key.x, self.token_id.0, is_odd_field],
1060        ))
1061    }
1062
1063    pub fn new(public_key: CompressedPubKey, token_id: TokenId) -> Self {
1064        Self {
1065            public_key,
1066            token_id,
1067        }
1068    }
1069
1070    pub fn new_with_default_token(public_key: CompressedPubKey) -> Self {
1071        Self::new(public_key, TokenId::default())
1072    }
1073
1074    pub fn create(public_key: CompressedPubKey, token_id: TokenId) -> Self {
1075        Self::new(public_key, token_id)
1076    }
1077
1078    pub fn rand() -> Self {
1079        let mut rng = rand::thread_rng();
1080
1081        Self {
1082            public_key: gen_compressed(),
1083            token_id: TokenId(Fp::rand(&mut rng)),
1084        }
1085    }
1086
1087    pub fn deserialize(bytes: &[u8]) -> Self {
1088        let mut cursor = Cursor::new(bytes);
1089        AccountId::binprot_read(&mut cursor).unwrap()
1090    }
1091
1092    pub fn serialize(&self) -> Vec<u8> {
1093        let mut bytes = Vec::with_capacity(10000);
1094        self.binprot_write(&mut bytes).unwrap();
1095        bytes
1096    }
1097
1098    pub fn checked_equal(&self, other: &Self, w: &mut Witness<Fp>) -> Boolean {
1099        use crate::proofs::field::field;
1100
1101        // public_key
1102        let pk_equal = checked_equal_compressed_key(&self.public_key, &other.public_key, w);
1103
1104        // token_id
1105        let tid_equal = field::equal(self.token_id.0, other.token_id.0, w);
1106
1107        // both
1108        pk_equal.and(&tid_equal, w)
1109    }
1110}
1111
1112pub fn checked_equal_compressed_key(
1113    a: &CompressedPubKey,
1114    b: &CompressedPubKey,
1115    w: &mut Witness<Fp>,
1116) -> Boolean {
1117    use crate::proofs::field::field;
1118
1119    let x_eq = field::equal(a.x, b.x, w);
1120    let odd_eq = Boolean::equal(&a.is_odd.to_boolean(), &b.is_odd.to_boolean(), w);
1121    x_eq.and(&odd_eq, w)
1122}
1123
1124// TODO: Dedup with above
1125pub fn checked_equal_compressed_key_const_and(
1126    a: &CompressedPubKey,
1127    b: &CompressedPubKey,
1128    w: &mut Witness<Fp>,
1129) -> Boolean {
1130    use crate::proofs::field::field;
1131
1132    if b == &CompressedPubKey::empty() {
1133        let x_eq = field::equal(a.x, b.x, w);
1134        let odd_eq = Boolean::const_equal(&a.is_odd.to_boolean(), &b.is_odd.to_boolean());
1135        x_eq.and(&odd_eq, w)
1136    } else {
1137        let x_eq = field::equal(a.x, b.x, w);
1138        let odd_eq = Boolean::equal(&a.is_odd.to_boolean(), &b.is_odd.to_boolean(), w);
1139        x_eq.const_and(&odd_eq)
1140    }
1141}
1142
1143impl std::fmt::Debug for AccountId {
1144    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1145        f.debug_struct("AccountId")
1146            .field("public_key", &self.public_key)
1147            .field("token_id", &self.token_id)
1148            .finish()
1149    }
1150}
1151
1152impl std::hash::Hash for AccountId {
1153    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1154        self.public_key.x.hash(state);
1155        self.public_key.is_odd.hash(state);
1156        self.token_id.hash(state);
1157    }
1158}
1159
1160impl PartialEq for AccountId {
1161    fn eq(&self, other: &Self) -> bool {
1162        self.public_key.x == other.public_key.x
1163            && self.public_key.is_odd == other.public_key.is_odd
1164            && self.token_id.0 == other.token_id.0
1165    }
1166}
1167
1168#[derive(Debug)]
1169pub enum PermissionTo {
1170    Access,
1171    Send,
1172    Receive,
1173    SetDelegate,
1174    IncrementNonce,
1175}
1176
1177#[derive(Copy, Clone, Debug)]
1178pub enum ControlTag {
1179    Proof,
1180    Signature,
1181    NoneGiven,
1182}
1183
1184impl ControlTag {
1185    pub fn gen(rng: &mut ThreadRng) -> Self {
1186        // Match will fail when a variant added
1187        match Self::NoneGiven {
1188            ControlTag::Proof => {}
1189            ControlTag::Signature => {}
1190            ControlTag::NoneGiven => {}
1191        };
1192
1193        [Self::Proof, Self::Signature, Self::NoneGiven]
1194            .choose(rng)
1195            .copied()
1196            .unwrap()
1197    }
1198}
1199
1200pub fn check_permission(auth: AuthRequired, tag: ControlTag) -> bool {
1201    use AuthRequired::*;
1202    use ControlTag as Tag;
1203
1204    match (auth, tag) {
1205        (Impossible, _) => false,
1206        (None, _) => true,
1207        (Proof, Tag::Proof) => true,
1208        (Signature, Tag::Signature) => true,
1209        // The signatures and proofs have already been checked by this point.
1210        (Either, Tag::Proof | Tag::Signature) => true,
1211        (Signature, Tag::Proof) => false,
1212        (Proof, Tag::Signature) => false,
1213        (Proof | Signature | Either, Tag::NoneGiven) => false,
1214        (Both, _) => unimplemented!("check_permission with `Both` Not implemented in OCaml"),
1215    }
1216}
1217
1218// TODO: Dedup with the one in `snark.rs`
1219pub fn eval_no_proof<F: FieldWitness>(
1220    auth: AuthRequired,
1221    signature_verifies: Boolean,
1222    is_and_const: bool,
1223    is_or_const: bool,
1224    w: &mut Witness<F>,
1225) -> Boolean {
1226    // TODO: Remove this hack with `is_const`
1227
1228    let AuthRequiredEncoded {
1229        constant,
1230        signature_necessary: _,
1231        signature_sufficient,
1232    } = auth.encode();
1233
1234    let constant = constant.to_boolean();
1235    let signature_sufficient = signature_sufficient.to_boolean();
1236
1237    let a = if is_and_const {
1238        constant.neg().const_and(&signature_verifies)
1239    } else {
1240        constant.neg().and(&signature_verifies, w)
1241    };
1242    let b = if is_or_const {
1243        constant.const_or(&a)
1244    } else {
1245        constant.or(&a, w)
1246    };
1247    signature_sufficient.and(&b, w)
1248}
1249
1250pub struct PermsConst {
1251    pub and_const: bool,
1252    pub or_const: bool,
1253}
1254
1255/// Represents a Mina account stored in the ledger.
1256///
1257/// An account is the fundamental unit of state in the Mina ledger. Each
1258/// account is uniquely identified by a public key and token ID pair, and
1259/// contains the account's balance, transaction nonce, and various protocol
1260/// metadata.
1261///
1262/// Accounts can be regular user accounts holding the native Mina token, or
1263/// more complex entities such as custom token accounts or zkApp accounts with
1264/// programmable smart contract functionality.
1265///
1266/// The account structure is designed to support:
1267/// - Token transfers and balance tracking
1268/// - Stake delegation for consensus participation
1269/// - Time-locked vesting schedules
1270/// - Flexible permission policies for different operations
1271/// - Zero-knowledge application state and verification
1272///
1273/// OCaml reference: src/lib/mina_base/account.ml L:201-224
1274/// Commit: fc6be4c58091c761f827c858229c2edf9519e941
1275/// Last verified: 2025-10-13
1276#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1277#[serde(into = "v2::MinaBaseAccountBinableArgStableV2")]
1278#[serde(try_from = "v2::MinaBaseAccountBinableArgStableV2")]
1279pub struct Account {
1280    /// The public key that controls this account. This is the address used
1281    /// to identify the account and authorize transactions.
1282    pub public_key: CompressedPubKey,
1283
1284    /// The token type this account holds. The default token (value 1)
1285    /// represents the native Mina token. Custom tokens have different
1286    /// values derived from their creator's account.
1287    pub token_id: TokenId,
1288
1289    /// The symbol for a custom token that this account can create or manage.
1290    /// Note: this describes a token ID owned by this account (i.e., a token
1291    /// this account can create), not the token type used by this account's
1292    /// balance.
1293    pub token_symbol: TokenSymbol,
1294
1295    /// The current balance of the account in the smallest unit (nanomina for
1296    /// the default token, or the smallest unit for custom tokens).
1297    pub balance: Balance,
1298
1299    /// The sequence number for transactions from this account. Incremented by
1300    /// [`pay_fee_impl`](crate::scan_state::transaction_logic::transaction_partially_applied::pay_fee_impl)
1301    /// for signed commands and by the zkApp application logic in
1302    /// [`apply`](crate::zkapps::zkapp_logic::apply) when processing account
1303    /// updates.
1304    pub nonce: Nonce,
1305
1306    /// A hash chain of all transaction receipts involving this account,
1307    /// providing a tamper-evident history. Updated by
1308    /// [`cons_signed_command_payload`](crate::scan_state::transaction_logic::transaction_union_payload::cons_signed_command_payload)
1309    /// for signed commands and
1310    /// [`cons_zkapp_command_commitment`](crate::scan_state::transaction_logic::transaction_union_payload::cons_zkapp_command_commitment)
1311    /// for zkApp commands.
1312    pub receipt_chain_hash: ReceiptChainHash,
1313
1314    /// The public key of the account to which this account delegates its
1315    /// stake for consensus. If `None`, the account delegates to itself (or
1316    /// cannot delegate if using a custom token). Delegation allows accounts
1317    /// to participate in consensus without running a block producer.
1318    pub delegate: Option<CompressedPubKey>,
1319
1320    /// The state hash this account is voting for in the consensus protocol.
1321    /// Updated through zkApp account updates in
1322    /// [`apply`](crate::zkapps::zkapp_logic::apply).
1323    pub voting_for: VotingFor,
1324
1325    /// The vesting schedule for this account's funds. Either `Untimed`
1326    /// (fully liquid) or `Timed` with parameters defining when and how
1327    /// tokens unlock over time. Updated by
1328    /// [`pay_fee_impl`](crate::scan_state::transaction_logic::transaction_partially_applied::pay_fee_impl)
1329    /// for signed commands and through zkApp account updates in
1330    /// [`apply`](crate::zkapps::zkapp_logic::apply).
1331    pub timing: Timing,
1332
1333    /// The permissions controlling what operations can be performed on this
1334    /// account and what authorization is required (none, signature, proof,
1335    /// or both). Updated through zkApp account updates in
1336    /// [`apply`](crate::zkapps::zkapp_logic::apply).
1337    pub permissions: Permissions<AuthRequired>,
1338
1339    /// zkApp-specific account data. If `Some`, this account is a zkApp
1340    /// (zero-knowledge application) with additional state including app
1341    /// state fields, a verification key, action state, and other zkApp
1342    /// metadata. Regular accounts have `None`. Managed through
1343    /// [`AccountInterface::make_zkapp`](crate::zkapps::interfaces::AccountInterface::make_zkapp)
1344    /// and
1345    /// [`AccountInterface::unmake_zkapp`](crate::zkapps::interfaces::AccountInterface::unmake_zkapp).
1346    pub zkapp: Option<Box<ZkAppAccount>>,
1347}
1348
1349impl Account {
1350    #[cfg(test)]
1351    pub fn create() -> Self {
1352        let pubkey = CompressedPubKey::from_address(
1353            "B62qnzbXmRNo9q32n4SNu2mpB8e7FYYLH8NmaX6oFCBYjjQ8SbD7uzV",
1354        )
1355        .unwrap();
1356
1357        Self {
1358            public_key: pubkey.clone(),
1359            token_id: TokenId::default(),
1360            token_symbol: TokenSymbol::default(),
1361            balance: Balance::from_u64(10101),
1362            nonce: Nonce::zero(),
1363            receipt_chain_hash: ReceiptChainHash::empty(),
1364            delegate: Some(pubkey),
1365            voting_for: VotingFor::dummy(),
1366            timing: Timing::Untimed,
1367            permissions: Permissions::user_default(),
1368            zkapp: None,
1369        }
1370    }
1371
1372    pub fn create_with(account_id: AccountId, balance: Balance) -> Self {
1373        let delegate = if account_id.token_id.is_default() {
1374            // Only allow delegation if this account is for the default token.
1375            Some(account_id.public_key.clone())
1376        } else {
1377            None
1378        };
1379
1380        Self {
1381            public_key: account_id.public_key,
1382            token_id: account_id.token_id,
1383            token_symbol: TokenSymbol::default(),
1384            balance,
1385            nonce: Nonce::zero(),
1386            receipt_chain_hash: ReceiptChainHash::empty(),
1387            delegate,
1388            voting_for: VotingFor::dummy(),
1389            timing: Timing::Untimed,
1390            permissions: Permissions::user_default(),
1391            zkapp: None,
1392        }
1393    }
1394
1395    pub fn delegate_or_empty(&self) -> MyCow<'_, CompressedPubKey> {
1396        MyCow::borrow_or_else(&self.delegate, CompressedPubKey::empty)
1397    }
1398
1399    pub fn zkapp_or_empty(&self) -> MyCow<'_, Box<ZkAppAccount>> {
1400        MyCow::borrow_or_else(&self.zkapp, Box::<ZkAppAccount>::default)
1401    }
1402
1403    pub fn initialize(account_id: &AccountId) -> Self {
1404        Self::create_with(account_id.clone(), Balance::zero())
1405    }
1406
1407    pub fn deserialize(bytes: &[u8]) -> Self {
1408        let mut cursor = Cursor::new(bytes);
1409        Account::binprot_read(&mut cursor).unwrap()
1410    }
1411
1412    pub fn serialize(&self) -> Vec<u8> {
1413        let mut bytes = Vec::with_capacity(10000);
1414        self.binprot_write(&mut bytes).unwrap();
1415        bytes
1416    }
1417
1418    pub fn empty() -> Self {
1419        Self {
1420            public_key: CompressedPubKey {
1421                x: Fp::zero(),
1422                is_odd: false,
1423            },
1424            token_id: TokenId::default(),
1425            token_symbol: TokenSymbol::default(),
1426            balance: Balance::zero(),
1427            nonce: Nonce::zero(),
1428            receipt_chain_hash: ReceiptChainHash::empty(),
1429            delegate: None,
1430            voting_for: VotingFor::dummy(),
1431            timing: Timing::Untimed,
1432            permissions: Permissions::user_default(),
1433            zkapp: None,
1434        }
1435    }
1436
1437    pub fn id(&self) -> AccountId {
1438        AccountId {
1439            public_key: self.public_key.clone(),
1440            token_id: self.token_id.clone(),
1441        }
1442    }
1443
1444    pub fn has_locked_tokens(&self, global_slot: Slot) -> bool {
1445        match self.timing {
1446            Timing::Untimed => false,
1447            Timing::Timed { .. } => {
1448                let curr_min_balance = self.min_balance_at_slot(global_slot);
1449
1450                !curr_min_balance.is_zero()
1451            }
1452        }
1453    }
1454
1455    pub fn has_locked_tokens_checked(
1456        &self,
1457        global_slot: &CheckedSlot<Fp>,
1458        w: &mut Witness<Fp>,
1459    ) -> Boolean {
1460        let TimingAsRecordChecked {
1461            is_timed: _,
1462            initial_minimum_balance,
1463            cliff_time,
1464            cliff_amount,
1465            vesting_period,
1466            vesting_increment,
1467        } = self.timing.to_record_checked::<Fp>();
1468
1469        let cur_min_balance = checked_min_balance_at_slot(
1470            global_slot,
1471            &cliff_time,
1472            &cliff_amount,
1473            &vesting_period,
1474            &vesting_increment,
1475            &initial_minimum_balance,
1476            w,
1477        );
1478
1479        let zero_min_balance = CheckedBalance::zero().equal(&cur_min_balance, w);
1480        zero_min_balance.neg()
1481    }
1482
1483    pub fn liquid_balance_at_slot(&self, global_slot: Slot) -> Balance {
1484        match self.timing {
1485            Timing::Untimed => self.balance,
1486            Timing::Timed { .. } => self
1487                .balance
1488                .sub_amount(self.min_balance_at_slot(global_slot).to_amount())
1489                .unwrap(),
1490        }
1491    }
1492
1493    pub fn min_balance_at_slot(&self, global_slot: Slot) -> Balance {
1494        match self.timing {
1495            Timing::Untimed => Balance::zero(),
1496            Timing::Timed {
1497                initial_minimum_balance,
1498                cliff_time,
1499                cliff_amount,
1500                vesting_period,
1501                vesting_increment,
1502            } => {
1503                if global_slot < cliff_time {
1504                    initial_minimum_balance
1505                } else if vesting_period.is_zero() {
1506                    // If vesting period is zero then everything vests immediately at the cliff
1507                    Balance::zero()
1508                } else {
1509                    match initial_minimum_balance.sub_amount(cliff_amount) {
1510                        None => Balance::zero(),
1511                        Some(min_balance_past_cliff) => {
1512                            // take advantage of fact that global slots are uint32's
1513
1514                            let num_periods = (global_slot.as_u32() - cliff_time.as_u32())
1515                                / vesting_period.as_u32();
1516                            let num_periods: u64 = num_periods.into();
1517
1518                            let vesting_decrement = {
1519                                let vesting_increment = vesting_increment.as_u64();
1520
1521                                if u64::MAX
1522                                    .checked_div(num_periods)
1523                                    .map(|res| {
1524                                        matches!(
1525                                            res.cmp(&vesting_increment),
1526                                            std::cmp::Ordering::Less
1527                                        )
1528                                    })
1529                                    .unwrap_or(false)
1530                                {
1531                                    // The vesting decrement will overflow, use [max_int] instead.
1532                                    Amount::from_u64(u64::MAX)
1533                                } else {
1534                                    Amount::from_u64(
1535                                        num_periods.checked_mul(vesting_increment).unwrap(),
1536                                    )
1537                                }
1538                            };
1539
1540                            match min_balance_past_cliff.sub_amount(vesting_decrement) {
1541                                None => Balance::zero(),
1542                                Some(amount) => amount,
1543                            }
1544                        }
1545                    }
1546                }
1547            }
1548        }
1549    }
1550
1551    /// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_base/account.ml#L794>
1552    pub fn has_permission_to(&self, control: ControlTag, to: PermissionTo) -> bool {
1553        match to {
1554            PermissionTo::Access => check_permission(self.permissions.access, control),
1555            PermissionTo::Send => check_permission(self.permissions.send, control),
1556            PermissionTo::Receive => check_permission(self.permissions.receive, control),
1557            PermissionTo::SetDelegate => check_permission(self.permissions.set_delegate, control),
1558            PermissionTo::IncrementNonce => {
1559                check_permission(self.permissions.increment_nonce, control)
1560            }
1561        }
1562    }
1563
1564    pub fn checked_has_permission_to<F: FieldWitness>(
1565        &self,
1566        consts: PermsConst,
1567        signature_verifies: Option<Boolean>,
1568        to: PermissionTo,
1569        w: &mut Witness<F>,
1570    ) -> Boolean {
1571        let signature_verifies = match signature_verifies {
1572            Some(signature_verifies) => signature_verifies,
1573            None => match to {
1574                PermissionTo::Send => Boolean::True,
1575                PermissionTo::Receive => Boolean::False,
1576                PermissionTo::SetDelegate => Boolean::True,
1577                PermissionTo::IncrementNonce => Boolean::True,
1578                PermissionTo::Access => {
1579                    panic!("signature_verifies argument must be given for access permission")
1580                }
1581            },
1582        };
1583
1584        let auth = match to {
1585            PermissionTo::Send => self.permissions.send,
1586            PermissionTo::Receive => self.permissions.receive,
1587            PermissionTo::SetDelegate => self.permissions.set_delegate,
1588            PermissionTo::IncrementNonce => self.permissions.increment_nonce,
1589            PermissionTo::Access => self.permissions.access,
1590        };
1591
1592        eval_no_proof(
1593            auth,
1594            signature_verifies,
1595            consts.and_const,
1596            consts.or_const,
1597            w,
1598        )
1599    }
1600
1601    /// [true] iff account has permissions set that enable them to transfer Mina (assuming the command is signed)
1602    pub fn has_permission_to_send(&self) -> bool {
1603        self.has_permission_to(ControlTag::Signature, PermissionTo::Access)
1604            && self.has_permission_to(ControlTag::Signature, PermissionTo::Send)
1605    }
1606
1607    /// [true] iff account has permissions set that enable them to receive Mina
1608    pub fn has_permission_to_receive(&self) -> bool {
1609        self.has_permission_to(ControlTag::NoneGiven, PermissionTo::Access)
1610            && self.has_permission_to(ControlTag::NoneGiven, PermissionTo::Receive)
1611    }
1612
1613    /// [true] iff account has permissions set that enable them to set their delegate (assuming the command is signed)
1614    pub fn has_permission_to_set_delegate(&self) -> bool {
1615        self.has_permission_to(ControlTag::Signature, PermissionTo::Access)
1616            && self.has_permission_to(ControlTag::Signature, PermissionTo::SetDelegate)
1617    }
1618
1619    /// [true] iff account has permissions set that enable them to increment their nonce (assuming the command is signed)
1620    pub fn has_permission_to_increment_nonce(&self) -> bool {
1621        self.has_permission_to(ControlTag::Signature, PermissionTo::Access)
1622            && self.has_permission_to(ControlTag::Signature, PermissionTo::IncrementNonce)
1623    }
1624
1625    pub fn hash(&self) -> Fp {
1626        self.hash_with_param(&MINA_ACCOUNT)
1627    }
1628
1629    pub fn checked_hash(&self, w: &mut Witness<Fp>) -> Fp {
1630        use crate::proofs::transaction::transaction_snark::checked_hash;
1631
1632        let inputs = self.to_inputs_owned();
1633
1634        checked_hash(&MINA_ACCOUNT, &inputs.to_fields(), w)
1635    }
1636
1637    pub fn rand() -> Self {
1638        let mut rng = rand::thread_rng();
1639        let rng = &mut rng;
1640
1641        let symbol: u64 = rng.gen();
1642        let mut symbol = symbol.to_string();
1643        symbol.truncate(6);
1644
1645        let zkapp_uri: u64 = rng.gen();
1646        let mut zkapp_uri = zkapp_uri.to_string();
1647        zkapp_uri.truncate(6);
1648
1649        let gen_perm = |rng: &mut ThreadRng| {
1650            let n: u64 = rng.gen();
1651            if n.is_multiple_of(5) {
1652                AuthRequired::Either
1653            } else if n.is_multiple_of(4) {
1654                AuthRequired::Impossible
1655            } else if n.is_multiple_of(3) {
1656                AuthRequired::None
1657            } else if n.is_multiple_of(2) {
1658                AuthRequired::Proof
1659            } else {
1660                AuthRequired::Signature
1661            }
1662        };
1663
1664        Self {
1665            public_key: gen_compressed(),
1666            token_id: TokenId(Fp::rand(rng)),
1667            token_symbol: TokenSymbol(symbol.into_bytes()),
1668            balance: rng.gen(),
1669            nonce: rng.gen(),
1670            receipt_chain_hash: ReceiptChainHash(Fp::rand(rng)),
1671            delegate: if rng.gen() {
1672                Some(gen_compressed())
1673            } else {
1674                None
1675            },
1676            voting_for: VotingFor(Fp::rand(rng)),
1677            timing: if rng.gen() {
1678                Timing::Untimed
1679            } else {
1680                Timing::Timed {
1681                    initial_minimum_balance: rng.gen(),
1682                    cliff_time: rng.gen(),
1683                    cliff_amount: rng.gen(),
1684                    vesting_period: rng.gen(),
1685                    vesting_increment: rng.gen(),
1686                }
1687            },
1688            permissions: Permissions {
1689                edit_state: gen_perm(rng),
1690                send: gen_perm(rng),
1691                receive: gen_perm(rng),
1692                set_delegate: gen_perm(rng),
1693                set_permissions: gen_perm(rng),
1694                set_verification_key: SetVerificationKey {
1695                    auth: gen_perm(rng),
1696                    txn_version: TXN_VERSION_CURRENT, // TODO: Make the version random ?
1697                },
1698                set_zkapp_uri: gen_perm(rng),
1699                edit_action_state: gen_perm(rng),
1700                set_token_symbol: gen_perm(rng),
1701                increment_nonce: gen_perm(rng),
1702                set_voting_for: gen_perm(rng),
1703                access: gen_perm(rng),
1704                set_timing: gen_perm(rng),
1705            },
1706            zkapp: if rng.gen() {
1707                Some(
1708                    ZkAppAccount {
1709                        app_state: [
1710                            Fp::rand(rng),
1711                            Fp::rand(rng),
1712                            Fp::rand(rng),
1713                            Fp::rand(rng),
1714                            Fp::rand(rng),
1715                            Fp::rand(rng),
1716                            Fp::rand(rng),
1717                            Fp::rand(rng),
1718                        ],
1719                        verification_key: if rng.gen() {
1720                            Some(VerificationKeyWire::gen())
1721                        } else {
1722                            None
1723                        },
1724                        zkapp_version: rng.gen(),
1725                        action_state: [
1726                            Fp::rand(rng),
1727                            Fp::rand(rng),
1728                            Fp::rand(rng),
1729                            Fp::rand(rng),
1730                            Fp::rand(rng),
1731                        ],
1732                        last_action_slot: rng.gen(),
1733                        proved_state: rng.gen(),
1734                        zkapp_uri: ZkAppUri(zkapp_uri.into_bytes()),
1735                    }
1736                    .into(),
1737                )
1738            } else {
1739                None
1740            },
1741        }
1742    }
1743}
1744
1745pub fn default_zkapp_hash() -> Fp {
1746    static HASH: Lazy<Fp> = Lazy::new(|| {
1747        let default = ZkAppAccount::default();
1748        default.hash()
1749    });
1750    *HASH
1751}
1752
1753impl ToInputs for Account {
1754    fn to_inputs(&self, inputs: &mut Inputs) {
1755        let Self {
1756            public_key,
1757            token_id,
1758            token_symbol,
1759            balance,
1760            nonce,
1761            receipt_chain_hash,
1762            delegate,
1763            voting_for,
1764            timing,
1765            permissions,
1766            zkapp,
1767        } = self;
1768
1769        // Self::zkapp
1770        let field_zkapp = match zkapp.as_ref() {
1771            Some(zkapp) => zkapp.hash(),
1772            None => default_zkapp_hash(),
1773        };
1774        inputs.append(&field_zkapp);
1775        inputs.append(permissions);
1776
1777        // Self::timing
1778        let TimingAsRecord {
1779            is_timed,
1780            initial_minimum_balance,
1781            cliff_time,
1782            cliff_amount,
1783            vesting_period,
1784            vesting_increment,
1785        } = timing.to_record();
1786        inputs.append_bool(is_timed);
1787        inputs.append_u64(initial_minimum_balance.as_u64());
1788        inputs.append_u32(cliff_time.as_u32());
1789        inputs.append_u64(cliff_amount.as_u64());
1790        inputs.append_u32(vesting_period.as_u32());
1791        inputs.append_u64(vesting_increment.as_u64());
1792
1793        // Self::voting_for
1794        inputs.append_field(voting_for.0);
1795        // Self::delegate
1796        let delegate = MyCow::borrow_or_else(delegate, CompressedPubKey::empty);
1797        inputs.append(delegate.as_ref());
1798        // Self::receipt_chain_hash
1799        inputs.append_field(receipt_chain_hash.0);
1800        // Self::nonce
1801        inputs.append_u32(nonce.as_u32());
1802        // Self::balance
1803        inputs.append_u64(balance.as_u64());
1804        // Self::token_symbol
1805        // <https://github.com/MinaProtocol/mina/blob/2fac5d806a06af215dbab02f7b154b4f032538b7/src/lib/mina_base/account.ml#L97>
1806        assert!(token_symbol.len() <= 6);
1807        inputs.append(token_symbol);
1808        // Self::token_id
1809        inputs.append_field(token_id.0);
1810        // Self::public_key
1811        inputs.append(public_key);
1812    }
1813}
1814
1815fn verify_merkle_path(account: &Account, merkle_path: &[MerklePath]) -> Fp {
1816    let account_hash = account.hash();
1817
1818    merkle_path
1819        .iter()
1820        .enumerate()
1821        .fold(account_hash, |accum, (height, path)| {
1822            let hashes = match path {
1823                MerklePath::Left(right) => [accum, *right],
1824                MerklePath::Right(left) => [*left, accum],
1825            };
1826            let param = get_merkle_param_for_height(height);
1827            hash_with_kimchi(param, &hashes)
1828        })
1829}
1830
1831/// `implied_root` in OCaml
1832pub fn checked_verify_merkle_path(
1833    account: &Account,
1834    merkle_path: &[MerklePath],
1835    w: &mut Witness<Fp>,
1836) -> Fp {
1837    use crate::proofs::transaction::transaction_snark::checked_hash;
1838
1839    let account_hash = account.checked_hash(w);
1840
1841    merkle_path
1842        .iter()
1843        .enumerate()
1844        .fold(account_hash, |accum, (height, path)| {
1845            let hashes = match path {
1846                MerklePath::Left(right) => [accum, *right],
1847                MerklePath::Right(left) => [*left, accum],
1848            };
1849            w.exists(hashes);
1850
1851            let param = get_merkle_param_for_height(height);
1852            checked_hash(param, &hashes, w)
1853        })
1854}
1855
1856#[cfg(test)]
1857mod tests {
1858    use o1_utils::FieldHelpers;
1859
1860    #[cfg(target_family = "wasm")]
1861    use wasm_bindgen_test::wasm_bindgen_test as test;
1862
1863    use super::*;
1864
1865    #[test]
1866    fn test_size_account() {
1867        const SIZE_WITH_9LIMBS: usize = 296;
1868
1869        #[cfg(not(target_family = "wasm"))]
1870        const SIZE: usize = 280;
1871
1872        // FIXME: was 2496bytes before zkapp got boxed, what should be the size now?
1873        #[cfg(target_family = "wasm")]
1874        const SIZE: usize = 280;
1875
1876        if std::mem::size_of::<BigInteger256>() == 9 * 4 {
1877            assert_eq!(std::mem::size_of::<Account>(), SIZE_WITH_9LIMBS);
1878        } else {
1879            assert_eq!(std::mem::size_of::<Account>(), SIZE);
1880        }
1881    }
1882
1883    #[test]
1884    fn test_hash_account() {
1885        let acc = Account::create();
1886        let hash = acc.hash();
1887
1888        elog!("account_hash={}", hash);
1889        elog!("account_hash={}", hash.to_hex());
1890
1891        assert_eq!(
1892            hash.to_hex(),
1893            "7018596b7a10344908c7582482b1401a3cbdd40212beb428baf629b84ceb7f0b"
1894        );
1895
1896        let acc = Account {
1897            public_key: CompressedPubKey::from_address(
1898                "B62qnzbXmRNo9q32n4SNu2mpB8e7FYYLH8NmaX6oFCBYjjQ8SbD7uzV",
1899            )
1900            .unwrap(),
1901            token_id: TokenId::default(),
1902            token_symbol: TokenSymbol::from("seb".to_string().into_bytes()),
1903            balance: Balance::from_u64(10101),
1904            nonce: Nonce::from_u32(62772),
1905            receipt_chain_hash: ReceiptChainHash::empty(),
1906            delegate: None,
1907            voting_for: VotingFor::dummy(),
1908            timing: Timing::Untimed,
1909            permissions: Permissions::user_default(),
1910            zkapp: None,
1911        };
1912
1913        assert_eq!(
1914            acc.hash().to_hex(),
1915            "8cb53d374b844227d4c63b2dcf198312f8fcb4b60392fee5b165243508d16e32"
1916        );
1917    }
1918
1919    #[test]
1920    fn test_hash_genesis_winner_account() {
1921        let acc = Account {
1922            public_key: CompressedPubKey::from_address(
1923                "B62qiy32p8kAKnny8ZFwoMhYpBppM1DWVCqAPBYNcXnsAHhnfAAuXgg",
1924            )
1925            .unwrap(),
1926            token_id: TokenId::default(),
1927            token_symbol: TokenSymbol::default(),
1928            balance: Balance::from_u64(20000001000),
1929            nonce: Nonce::from_u32(0),
1930            receipt_chain_hash: ReceiptChainHash::parse_str(
1931                "2mzbV7WevxLuchs2dAMY4vQBS6XttnCUF8Hvks4XNBQ5qiSGGBQe",
1932            )
1933            .unwrap(),
1934            delegate: Some(
1935                CompressedPubKey::from_address(
1936                    "B62qiy32p8kAKnny8ZFwoMhYpBppM1DWVCqAPBYNcXnsAHhnfAAuXgg",
1937                )
1938                .unwrap(),
1939            ),
1940            voting_for: VotingFor::parse_str(
1941                "3NK2tkzqqK5spR2sZ7tujjqPksL45M3UUrcA4WhCkeiPtnugyE2x",
1942            )
1943            .unwrap(),
1944            timing: Timing::Untimed,
1945            permissions: Permissions {
1946                edit_state: AuthRequired::Signature,
1947                access: AuthRequired::None,
1948                send: AuthRequired::Signature,
1949                receive: AuthRequired::None,
1950                set_delegate: AuthRequired::Signature,
1951                set_permissions: AuthRequired::Signature,
1952                set_verification_key: SetVerificationKey {
1953                    auth: AuthRequired::Signature,
1954                    txn_version: TxnVersion::from_u32(2),
1955                },
1956                set_zkapp_uri: AuthRequired::Signature,
1957                edit_action_state: AuthRequired::Signature,
1958                set_token_symbol: AuthRequired::Signature,
1959                increment_nonce: AuthRequired::Signature,
1960                set_voting_for: AuthRequired::Signature,
1961                set_timing: AuthRequired::Signature,
1962            },
1963            zkapp: None,
1964        };
1965
1966        println!("{:?}", acc);
1967
1968        assert_eq!(
1969            mina_p2p_messages::v2::LedgerHash::from_fp(acc.hash()).to_string(),
1970            "jwnEz6CjzSYowUsvw5gKpuTkRjBY5dEtc6YmQj1U5d2k5KZzTmc"
1971        );
1972    }
1973
1974    #[test]
1975    fn test_dummy_sideloaded_verification_key() {
1976        assert_eq!(
1977            VerificationKey::dummy().hash().to_hex(),
1978            "d6da18e4091fbcd86843604fb8ff2d9613e76fa16c49b0263a1566a8e7188007"
1979        );
1980    }
1981
1982    #[test]
1983    fn test_rand() {
1984        for _ in 0..1000 {
1985            let rand = Account::rand();
1986            let hash = rand.hash();
1987
1988            let bytes = Account::serialize(&rand);
1989            let rand2: Account = Account::deserialize(&bytes);
1990
1991            assert_eq!(hash, rand2.hash());
1992        }
1993    }
1994
1995    #[cfg(not(target_family = "wasm"))] // Use multiple threads
1996    #[test]
1997    fn test_rand_tree() {
1998        // use rayon::prelude::*;
1999
2000        // let mut db = Database::<V2>::create(20);
2001        // let mut accounts = Vec::with_capacity(1000);
2002
2003        // const NACCOUNTS: usize = 1000;
2004
2005        // for _ in 0..NACCOUNTS {
2006        //     let rand = Account::rand();
2007        //     accounts.push(rand);
2008        // }
2009
2010        // let pool = rayon::ThreadPoolBuilder::new()
2011        //     .num_threads(16)
2012        //     .build()
2013        //     .unwrap();
2014
2015        // let now = redux::Instant::now();
2016        // let hashes = pool.install(|| {
2017        //     accounts
2018        //         .par_iter()
2019        //         .map(|acc| acc.hash())
2020        //         .collect::<Vec<_>>()
2021        // });
2022
2023        // assert_eq!(hashes.len(), NACCOUNTS);
2024        // elog!(
2025        //     "elapsed to hash accounts in 16 threads: {:?}",
2026        //     now.elapsed(),
2027        // );
2028
2029        // let mut hashes = Vec::with_capacity(accounts.len());
2030        // let now = redux::Instant::now();
2031        // for account in accounts.iter() {
2032        //     hashes.push(account.hash());
2033        // }
2034        // assert_eq!(hashes.len(), NACCOUNTS);
2035        // elog!("elapsed to hash accounts in 1 thread: {:?}", now.elapsed(),);
2036
2037        // let now = redux::Instant::now();
2038        // for account in accounts.into_iter() {
2039        //     let id = account.id();
2040        //     db.get_or_create_account(id, account).unwrap();
2041        // }
2042        // assert_eq!(db.naccounts(), NACCOUNTS);
2043        // elog!("elapsed to insert in tree: {:?}", now.elapsed());
2044
2045        // let now = redux::Instant::now();
2046        // db.root_hash();
2047        // elog!("root hash computed in {:?}", now.elapsed());
2048    }
2049}