Skip to main content

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: <https://github.com/MinaProtocol/mina/blob/fc6be4c58091c761f827c858229c2edf9519e941/src/lib/mina_base/account.ml#L201-L224>
1274#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1275#[serde(into = "v2::MinaBaseAccountBinableArgStableV2")]
1276#[serde(try_from = "v2::MinaBaseAccountBinableArgStableV2")]
1277pub struct Account {
1278    /// The public key that controls this account. This is the address used
1279    /// to identify the account and authorize transactions.
1280    pub public_key: CompressedPubKey,
1281
1282    /// The token type this account holds. The default token (value 1)
1283    /// represents the native Mina token. Custom tokens have different
1284    /// values derived from their creator's account.
1285    pub token_id: TokenId,
1286
1287    /// The symbol for a custom token that this account can create or manage.
1288    /// Note: this describes a token ID owned by this account (i.e., a token
1289    /// this account can create), not the token type used by this account's
1290    /// balance.
1291    pub token_symbol: TokenSymbol,
1292
1293    /// The current balance of the account in the smallest unit (nanomina for
1294    /// the default token, or the smallest unit for custom tokens).
1295    pub balance: Balance,
1296
1297    /// The sequence number for transactions from this account. Incremented by
1298    /// [`pay_fee_impl`](crate::scan_state::transaction_logic::transaction_partially_applied::pay_fee_impl)
1299    /// for signed commands and by the zkApp application logic in
1300    /// [`apply`](crate::zkapps::zkapp_logic::apply) when processing account
1301    /// updates.
1302    pub nonce: Nonce,
1303
1304    /// A hash chain of all transaction receipts involving this account,
1305    /// providing a tamper-evident history. Updated by
1306    /// [`cons_signed_command_payload`](crate::scan_state::transaction_logic::transaction_union_payload::cons_signed_command_payload)
1307    /// for signed commands and
1308    /// [`cons_zkapp_command_commitment`](crate::scan_state::transaction_logic::transaction_union_payload::cons_zkapp_command_commitment)
1309    /// for zkApp commands.
1310    pub receipt_chain_hash: ReceiptChainHash,
1311
1312    /// The public key of the account to which this account delegates its
1313    /// stake for consensus. If `None`, the account delegates to itself (or
1314    /// cannot delegate if using a custom token). Delegation allows accounts
1315    /// to participate in consensus without running a block producer.
1316    pub delegate: Option<CompressedPubKey>,
1317
1318    /// The state hash this account is voting for in the consensus protocol.
1319    /// Updated through zkApp account updates in
1320    /// [`apply`](crate::zkapps::zkapp_logic::apply).
1321    pub voting_for: VotingFor,
1322
1323    /// The vesting schedule for this account's funds. Either `Untimed`
1324    /// (fully liquid) or `Timed` with parameters defining when and how
1325    /// tokens unlock over time. Updated by
1326    /// [`pay_fee_impl`](crate::scan_state::transaction_logic::transaction_partially_applied::pay_fee_impl)
1327    /// for signed commands and through zkApp account updates in
1328    /// [`apply`](crate::zkapps::zkapp_logic::apply).
1329    pub timing: Timing,
1330
1331    /// The permissions controlling what operations can be performed on this
1332    /// account and what authorization is required (none, signature, proof,
1333    /// or both). Updated through zkApp account updates in
1334    /// [`apply`](crate::zkapps::zkapp_logic::apply).
1335    pub permissions: Permissions<AuthRequired>,
1336
1337    /// zkApp-specific account data. If `Some`, this account is a zkApp
1338    /// (zero-knowledge application) with additional state including app
1339    /// state fields, a verification key, action state, and other zkApp
1340    /// metadata. Regular accounts have `None`. Managed through
1341    /// [`AccountInterface::make_zkapp`](crate::zkapps::interfaces::AccountInterface::make_zkapp)
1342    /// and
1343    /// [`AccountInterface::unmake_zkapp`](crate::zkapps::interfaces::AccountInterface::unmake_zkapp).
1344    pub zkapp: Option<Box<ZkAppAccount>>,
1345}
1346
1347impl Account {
1348    #[cfg(test)]
1349    pub fn create() -> Self {
1350        let pubkey = CompressedPubKey::from_address(
1351            "B62qnzbXmRNo9q32n4SNu2mpB8e7FYYLH8NmaX6oFCBYjjQ8SbD7uzV",
1352        )
1353        .unwrap();
1354
1355        Self {
1356            public_key: pubkey.clone(),
1357            token_id: TokenId::default(),
1358            token_symbol: TokenSymbol::default(),
1359            balance: Balance::from_u64(10101),
1360            nonce: Nonce::zero(),
1361            receipt_chain_hash: ReceiptChainHash::empty(),
1362            delegate: Some(pubkey),
1363            voting_for: VotingFor::dummy(),
1364            timing: Timing::Untimed,
1365            permissions: Permissions::user_default(),
1366            zkapp: None,
1367        }
1368    }
1369
1370    pub fn create_with(account_id: AccountId, balance: Balance) -> Self {
1371        let delegate = if account_id.token_id.is_default() {
1372            // Only allow delegation if this account is for the default token.
1373            Some(account_id.public_key.clone())
1374        } else {
1375            None
1376        };
1377
1378        Self {
1379            public_key: account_id.public_key,
1380            token_id: account_id.token_id,
1381            token_symbol: TokenSymbol::default(),
1382            balance,
1383            nonce: Nonce::zero(),
1384            receipt_chain_hash: ReceiptChainHash::empty(),
1385            delegate,
1386            voting_for: VotingFor::dummy(),
1387            timing: Timing::Untimed,
1388            permissions: Permissions::user_default(),
1389            zkapp: None,
1390        }
1391    }
1392
1393    pub fn delegate_or_empty(&self) -> MyCow<'_, CompressedPubKey> {
1394        MyCow::borrow_or_else(&self.delegate, CompressedPubKey::empty)
1395    }
1396
1397    pub fn zkapp_or_empty(&self) -> MyCow<'_, Box<ZkAppAccount>> {
1398        MyCow::borrow_or_else(&self.zkapp, Box::<ZkAppAccount>::default)
1399    }
1400
1401    pub fn initialize(account_id: &AccountId) -> Self {
1402        Self::create_with(account_id.clone(), Balance::zero())
1403    }
1404
1405    pub fn deserialize(bytes: &[u8]) -> Self {
1406        let mut cursor = Cursor::new(bytes);
1407        Account::binprot_read(&mut cursor).unwrap()
1408    }
1409
1410    pub fn serialize(&self) -> Vec<u8> {
1411        let mut bytes = Vec::with_capacity(10000);
1412        self.binprot_write(&mut bytes).unwrap();
1413        bytes
1414    }
1415
1416    pub fn empty() -> Self {
1417        Self {
1418            public_key: CompressedPubKey {
1419                x: Fp::zero(),
1420                is_odd: false,
1421            },
1422            token_id: TokenId::default(),
1423            token_symbol: TokenSymbol::default(),
1424            balance: Balance::zero(),
1425            nonce: Nonce::zero(),
1426            receipt_chain_hash: ReceiptChainHash::empty(),
1427            delegate: None,
1428            voting_for: VotingFor::dummy(),
1429            timing: Timing::Untimed,
1430            permissions: Permissions::user_default(),
1431            zkapp: None,
1432        }
1433    }
1434
1435    pub fn id(&self) -> AccountId {
1436        AccountId {
1437            public_key: self.public_key.clone(),
1438            token_id: self.token_id.clone(),
1439        }
1440    }
1441
1442    pub fn has_locked_tokens(&self, global_slot: Slot) -> bool {
1443        match self.timing {
1444            Timing::Untimed => false,
1445            Timing::Timed { .. } => {
1446                let curr_min_balance = self.min_balance_at_slot(global_slot);
1447
1448                !curr_min_balance.is_zero()
1449            }
1450        }
1451    }
1452
1453    pub fn has_locked_tokens_checked(
1454        &self,
1455        global_slot: &CheckedSlot<Fp>,
1456        w: &mut Witness<Fp>,
1457    ) -> Boolean {
1458        let TimingAsRecordChecked {
1459            is_timed: _,
1460            initial_minimum_balance,
1461            cliff_time,
1462            cliff_amount,
1463            vesting_period,
1464            vesting_increment,
1465        } = self.timing.to_record_checked::<Fp>();
1466
1467        let cur_min_balance = checked_min_balance_at_slot(
1468            global_slot,
1469            &cliff_time,
1470            &cliff_amount,
1471            &vesting_period,
1472            &vesting_increment,
1473            &initial_minimum_balance,
1474            w,
1475        );
1476
1477        let zero_min_balance = CheckedBalance::zero().equal(&cur_min_balance, w);
1478        zero_min_balance.neg()
1479    }
1480
1481    pub fn liquid_balance_at_slot(&self, global_slot: Slot) -> Balance {
1482        match self.timing {
1483            Timing::Untimed => self.balance,
1484            Timing::Timed { .. } => self
1485                .balance
1486                .sub_amount(self.min_balance_at_slot(global_slot).to_amount())
1487                .unwrap(),
1488        }
1489    }
1490
1491    pub fn min_balance_at_slot(&self, global_slot: Slot) -> Balance {
1492        match self.timing {
1493            Timing::Untimed => Balance::zero(),
1494            Timing::Timed {
1495                initial_minimum_balance,
1496                cliff_time,
1497                cliff_amount,
1498                vesting_period,
1499                vesting_increment,
1500            } => {
1501                if global_slot < cliff_time {
1502                    initial_minimum_balance
1503                } else if vesting_period.is_zero() {
1504                    // If vesting period is zero then everything vests immediately at the cliff
1505                    Balance::zero()
1506                } else {
1507                    match initial_minimum_balance.sub_amount(cliff_amount) {
1508                        None => Balance::zero(),
1509                        Some(min_balance_past_cliff) => {
1510                            // take advantage of fact that global slots are uint32's
1511
1512                            let num_periods = (global_slot.as_u32() - cliff_time.as_u32())
1513                                / vesting_period.as_u32();
1514                            let num_periods: u64 = num_periods.into();
1515
1516                            let vesting_decrement = {
1517                                let vesting_increment = vesting_increment.as_u64();
1518
1519                                if u64::MAX
1520                                    .checked_div(num_periods)
1521                                    .map(|res| {
1522                                        matches!(
1523                                            res.cmp(&vesting_increment),
1524                                            std::cmp::Ordering::Less
1525                                        )
1526                                    })
1527                                    .unwrap_or(false)
1528                                {
1529                                    // The vesting decrement will overflow, use [max_int] instead.
1530                                    Amount::from_u64(u64::MAX)
1531                                } else {
1532                                    Amount::from_u64(
1533                                        num_periods.checked_mul(vesting_increment).unwrap(),
1534                                    )
1535                                }
1536                            };
1537
1538                            match min_balance_past_cliff.sub_amount(vesting_decrement) {
1539                                None => Balance::zero(),
1540                                Some(amount) => amount,
1541                            }
1542                        }
1543                    }
1544                }
1545            }
1546        }
1547    }
1548
1549    /// <https://github.com/MinaProtocol/mina/blob/2ff0292b637684ce0372e7b8e23ec85404dc5091/src/lib/mina_base/account.ml#L794>
1550    pub fn has_permission_to(&self, control: ControlTag, to: PermissionTo) -> bool {
1551        match to {
1552            PermissionTo::Access => check_permission(self.permissions.access, control),
1553            PermissionTo::Send => check_permission(self.permissions.send, control),
1554            PermissionTo::Receive => check_permission(self.permissions.receive, control),
1555            PermissionTo::SetDelegate => check_permission(self.permissions.set_delegate, control),
1556            PermissionTo::IncrementNonce => {
1557                check_permission(self.permissions.increment_nonce, control)
1558            }
1559        }
1560    }
1561
1562    pub fn checked_has_permission_to<F: FieldWitness>(
1563        &self,
1564        consts: PermsConst,
1565        signature_verifies: Option<Boolean>,
1566        to: PermissionTo,
1567        w: &mut Witness<F>,
1568    ) -> Boolean {
1569        let signature_verifies = match signature_verifies {
1570            Some(signature_verifies) => signature_verifies,
1571            None => match to {
1572                PermissionTo::Send => Boolean::True,
1573                PermissionTo::Receive => Boolean::False,
1574                PermissionTo::SetDelegate => Boolean::True,
1575                PermissionTo::IncrementNonce => Boolean::True,
1576                PermissionTo::Access => {
1577                    panic!("signature_verifies argument must be given for access permission")
1578                }
1579            },
1580        };
1581
1582        let auth = match to {
1583            PermissionTo::Send => self.permissions.send,
1584            PermissionTo::Receive => self.permissions.receive,
1585            PermissionTo::SetDelegate => self.permissions.set_delegate,
1586            PermissionTo::IncrementNonce => self.permissions.increment_nonce,
1587            PermissionTo::Access => self.permissions.access,
1588        };
1589
1590        eval_no_proof(
1591            auth,
1592            signature_verifies,
1593            consts.and_const,
1594            consts.or_const,
1595            w,
1596        )
1597    }
1598
1599    /// [true] iff account has permissions set that enable them to transfer Mina (assuming the command is signed)
1600    pub fn has_permission_to_send(&self) -> bool {
1601        self.has_permission_to(ControlTag::Signature, PermissionTo::Access)
1602            && self.has_permission_to(ControlTag::Signature, PermissionTo::Send)
1603    }
1604
1605    /// [true] iff account has permissions set that enable them to receive Mina
1606    pub fn has_permission_to_receive(&self) -> bool {
1607        self.has_permission_to(ControlTag::NoneGiven, PermissionTo::Access)
1608            && self.has_permission_to(ControlTag::NoneGiven, PermissionTo::Receive)
1609    }
1610
1611    /// [true] iff account has permissions set that enable them to set their delegate (assuming the command is signed)
1612    pub fn has_permission_to_set_delegate(&self) -> bool {
1613        self.has_permission_to(ControlTag::Signature, PermissionTo::Access)
1614            && self.has_permission_to(ControlTag::Signature, PermissionTo::SetDelegate)
1615    }
1616
1617    /// [true] iff account has permissions set that enable them to increment their nonce (assuming the command is signed)
1618    pub fn has_permission_to_increment_nonce(&self) -> bool {
1619        self.has_permission_to(ControlTag::Signature, PermissionTo::Access)
1620            && self.has_permission_to(ControlTag::Signature, PermissionTo::IncrementNonce)
1621    }
1622
1623    pub fn hash(&self) -> Fp {
1624        self.hash_with_param(&MINA_ACCOUNT)
1625    }
1626
1627    pub fn checked_hash(&self, w: &mut Witness<Fp>) -> Fp {
1628        use crate::proofs::transaction::transaction_snark::checked_hash;
1629
1630        let inputs = self.to_inputs_owned();
1631
1632        checked_hash(&MINA_ACCOUNT, &inputs.to_fields(), w)
1633    }
1634
1635    pub fn rand() -> Self {
1636        let mut rng = rand::thread_rng();
1637        let rng = &mut rng;
1638
1639        let symbol: u64 = rng.gen();
1640        let mut symbol = symbol.to_string();
1641        symbol.truncate(6);
1642
1643        let zkapp_uri: u64 = rng.gen();
1644        let mut zkapp_uri = zkapp_uri.to_string();
1645        zkapp_uri.truncate(6);
1646
1647        let gen_perm = |rng: &mut ThreadRng| {
1648            let n: u64 = rng.gen();
1649            if n.is_multiple_of(5) {
1650                AuthRequired::Either
1651            } else if n.is_multiple_of(4) {
1652                AuthRequired::Impossible
1653            } else if n.is_multiple_of(3) {
1654                AuthRequired::None
1655            } else if n.is_multiple_of(2) {
1656                AuthRequired::Proof
1657            } else {
1658                AuthRequired::Signature
1659            }
1660        };
1661
1662        Self {
1663            public_key: gen_compressed(),
1664            token_id: TokenId(Fp::rand(rng)),
1665            token_symbol: TokenSymbol(symbol.into_bytes()),
1666            balance: rng.gen(),
1667            nonce: rng.gen(),
1668            receipt_chain_hash: ReceiptChainHash(Fp::rand(rng)),
1669            delegate: if rng.gen() {
1670                Some(gen_compressed())
1671            } else {
1672                None
1673            },
1674            voting_for: VotingFor(Fp::rand(rng)),
1675            timing: if rng.gen() {
1676                Timing::Untimed
1677            } else {
1678                Timing::Timed {
1679                    initial_minimum_balance: rng.gen(),
1680                    cliff_time: rng.gen(),
1681                    cliff_amount: rng.gen(),
1682                    vesting_period: rng.gen(),
1683                    vesting_increment: rng.gen(),
1684                }
1685            },
1686            permissions: Permissions {
1687                edit_state: gen_perm(rng),
1688                send: gen_perm(rng),
1689                receive: gen_perm(rng),
1690                set_delegate: gen_perm(rng),
1691                set_permissions: gen_perm(rng),
1692                set_verification_key: SetVerificationKey {
1693                    auth: gen_perm(rng),
1694                    txn_version: TXN_VERSION_CURRENT, // TODO: Make the version random ?
1695                },
1696                set_zkapp_uri: gen_perm(rng),
1697                edit_action_state: gen_perm(rng),
1698                set_token_symbol: gen_perm(rng),
1699                increment_nonce: gen_perm(rng),
1700                set_voting_for: gen_perm(rng),
1701                access: gen_perm(rng),
1702                set_timing: gen_perm(rng),
1703            },
1704            zkapp: if rng.gen() {
1705                Some(
1706                    ZkAppAccount {
1707                        app_state: [
1708                            Fp::rand(rng),
1709                            Fp::rand(rng),
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                        ],
1717                        verification_key: if rng.gen() {
1718                            Some(VerificationKeyWire::gen())
1719                        } else {
1720                            None
1721                        },
1722                        zkapp_version: rng.gen(),
1723                        action_state: [
1724                            Fp::rand(rng),
1725                            Fp::rand(rng),
1726                            Fp::rand(rng),
1727                            Fp::rand(rng),
1728                            Fp::rand(rng),
1729                        ],
1730                        last_action_slot: rng.gen(),
1731                        proved_state: rng.gen(),
1732                        zkapp_uri: ZkAppUri(zkapp_uri.into_bytes()),
1733                    }
1734                    .into(),
1735                )
1736            } else {
1737                None
1738            },
1739        }
1740    }
1741}
1742
1743pub fn default_zkapp_hash() -> Fp {
1744    static HASH: Lazy<Fp> = Lazy::new(|| {
1745        let default = ZkAppAccount::default();
1746        default.hash()
1747    });
1748    *HASH
1749}
1750
1751impl ToInputs for Account {
1752    fn to_inputs(&self, inputs: &mut Inputs) {
1753        let Self {
1754            public_key,
1755            token_id,
1756            token_symbol,
1757            balance,
1758            nonce,
1759            receipt_chain_hash,
1760            delegate,
1761            voting_for,
1762            timing,
1763            permissions,
1764            zkapp,
1765        } = self;
1766
1767        // Self::zkapp
1768        let field_zkapp = match zkapp.as_ref() {
1769            Some(zkapp) => zkapp.hash(),
1770            None => default_zkapp_hash(),
1771        };
1772        inputs.append(&field_zkapp);
1773        inputs.append(permissions);
1774
1775        // Self::timing
1776        let TimingAsRecord {
1777            is_timed,
1778            initial_minimum_balance,
1779            cliff_time,
1780            cliff_amount,
1781            vesting_period,
1782            vesting_increment,
1783        } = timing.to_record();
1784        inputs.append_bool(is_timed);
1785        inputs.append_u64(initial_minimum_balance.as_u64());
1786        inputs.append_u32(cliff_time.as_u32());
1787        inputs.append_u64(cliff_amount.as_u64());
1788        inputs.append_u32(vesting_period.as_u32());
1789        inputs.append_u64(vesting_increment.as_u64());
1790
1791        // Self::voting_for
1792        inputs.append_field(voting_for.0);
1793        // Self::delegate
1794        let delegate = MyCow::borrow_or_else(delegate, CompressedPubKey::empty);
1795        inputs.append(delegate.as_ref());
1796        // Self::receipt_chain_hash
1797        inputs.append_field(receipt_chain_hash.0);
1798        // Self::nonce
1799        inputs.append_u32(nonce.as_u32());
1800        // Self::balance
1801        inputs.append_u64(balance.as_u64());
1802        // Self::token_symbol
1803        // <https://github.com/MinaProtocol/mina/blob/2fac5d806a06af215dbab02f7b154b4f032538b7/src/lib/mina_base/account.ml#L97>
1804        assert!(token_symbol.len() <= 6);
1805        inputs.append(token_symbol);
1806        // Self::token_id
1807        inputs.append_field(token_id.0);
1808        // Self::public_key
1809        inputs.append(public_key);
1810    }
1811}
1812
1813fn verify_merkle_path(account: &Account, merkle_path: &[MerklePath]) -> Fp {
1814    let account_hash = account.hash();
1815
1816    merkle_path
1817        .iter()
1818        .enumerate()
1819        .fold(account_hash, |accum, (height, path)| {
1820            let hashes = match path {
1821                MerklePath::Left(right) => [accum, *right],
1822                MerklePath::Right(left) => [*left, accum],
1823            };
1824            let param = get_merkle_param_for_height(height);
1825            hash_with_kimchi(param, &hashes)
1826        })
1827}
1828
1829/// `implied_root` in OCaml
1830pub fn checked_verify_merkle_path(
1831    account: &Account,
1832    merkle_path: &[MerklePath],
1833    w: &mut Witness<Fp>,
1834) -> Fp {
1835    use crate::proofs::transaction::transaction_snark::checked_hash;
1836
1837    let account_hash = account.checked_hash(w);
1838
1839    merkle_path
1840        .iter()
1841        .enumerate()
1842        .fold(account_hash, |accum, (height, path)| {
1843            let hashes = match path {
1844                MerklePath::Left(right) => [accum, *right],
1845                MerklePath::Right(left) => [*left, accum],
1846            };
1847            w.exists(hashes);
1848
1849            let param = get_merkle_param_for_height(height);
1850            checked_hash(param, &hashes, w)
1851        })
1852}
1853
1854#[cfg(test)]
1855mod tests {
1856    use o1_utils::FieldHelpers;
1857
1858    #[cfg(target_family = "wasm")]
1859    use wasm_bindgen_test::wasm_bindgen_test as test;
1860
1861    use super::*;
1862
1863    #[test]
1864    fn test_size_account() {
1865        const SIZE_WITH_9LIMBS: usize = 296;
1866
1867        #[cfg(not(target_family = "wasm"))]
1868        const SIZE: usize = 280;
1869
1870        // FIXME: was 2496bytes before zkapp got boxed, what should be the size now?
1871        #[cfg(target_family = "wasm")]
1872        const SIZE: usize = 280;
1873
1874        if std::mem::size_of::<BigInteger256>() == 9 * 4 {
1875            assert_eq!(std::mem::size_of::<Account>(), SIZE_WITH_9LIMBS);
1876        } else {
1877            assert_eq!(std::mem::size_of::<Account>(), SIZE);
1878        }
1879    }
1880
1881    #[test]
1882    fn test_hash_account() {
1883        let acc = Account::create();
1884        let hash = acc.hash();
1885
1886        elog!("account_hash={}", hash);
1887        elog!("account_hash={}", hash.to_hex());
1888
1889        assert_eq!(
1890            hash.to_hex(),
1891            "7018596b7a10344908c7582482b1401a3cbdd40212beb428baf629b84ceb7f0b"
1892        );
1893
1894        let acc = Account {
1895            public_key: CompressedPubKey::from_address(
1896                "B62qnzbXmRNo9q32n4SNu2mpB8e7FYYLH8NmaX6oFCBYjjQ8SbD7uzV",
1897            )
1898            .unwrap(),
1899            token_id: TokenId::default(),
1900            token_symbol: TokenSymbol::from("seb".to_string().into_bytes()),
1901            balance: Balance::from_u64(10101),
1902            nonce: Nonce::from_u32(62772),
1903            receipt_chain_hash: ReceiptChainHash::empty(),
1904            delegate: None,
1905            voting_for: VotingFor::dummy(),
1906            timing: Timing::Untimed,
1907            permissions: Permissions::user_default(),
1908            zkapp: None,
1909        };
1910
1911        assert_eq!(
1912            acc.hash().to_hex(),
1913            "8cb53d374b844227d4c63b2dcf198312f8fcb4b60392fee5b165243508d16e32"
1914        );
1915    }
1916
1917    #[test]
1918    fn test_hash_genesis_winner_account() {
1919        let acc = Account {
1920            public_key: CompressedPubKey::from_address(
1921                "B62qiy32p8kAKnny8ZFwoMhYpBppM1DWVCqAPBYNcXnsAHhnfAAuXgg",
1922            )
1923            .unwrap(),
1924            token_id: TokenId::default(),
1925            token_symbol: TokenSymbol::default(),
1926            balance: Balance::from_u64(20000001000),
1927            nonce: Nonce::from_u32(0),
1928            receipt_chain_hash: ReceiptChainHash::parse_str(
1929                "2mzbV7WevxLuchs2dAMY4vQBS6XttnCUF8Hvks4XNBQ5qiSGGBQe",
1930            )
1931            .unwrap(),
1932            delegate: Some(
1933                CompressedPubKey::from_address(
1934                    "B62qiy32p8kAKnny8ZFwoMhYpBppM1DWVCqAPBYNcXnsAHhnfAAuXgg",
1935                )
1936                .unwrap(),
1937            ),
1938            voting_for: VotingFor::parse_str(
1939                "3NK2tkzqqK5spR2sZ7tujjqPksL45M3UUrcA4WhCkeiPtnugyE2x",
1940            )
1941            .unwrap(),
1942            timing: Timing::Untimed,
1943            permissions: Permissions {
1944                edit_state: AuthRequired::Signature,
1945                access: AuthRequired::None,
1946                send: AuthRequired::Signature,
1947                receive: AuthRequired::None,
1948                set_delegate: AuthRequired::Signature,
1949                set_permissions: AuthRequired::Signature,
1950                set_verification_key: SetVerificationKey {
1951                    auth: AuthRequired::Signature,
1952                    txn_version: TxnVersion::from_u32(2),
1953                },
1954                set_zkapp_uri: AuthRequired::Signature,
1955                edit_action_state: AuthRequired::Signature,
1956                set_token_symbol: AuthRequired::Signature,
1957                increment_nonce: AuthRequired::Signature,
1958                set_voting_for: AuthRequired::Signature,
1959                set_timing: AuthRequired::Signature,
1960            },
1961            zkapp: None,
1962        };
1963
1964        println!("{:?}", acc);
1965
1966        assert_eq!(
1967            mina_p2p_messages::v2::LedgerHash::from_fp(acc.hash()).to_string(),
1968            "jwnEz6CjzSYowUsvw5gKpuTkRjBY5dEtc6YmQj1U5d2k5KZzTmc"
1969        );
1970    }
1971
1972    #[test]
1973    fn test_dummy_sideloaded_verification_key() {
1974        assert_eq!(
1975            VerificationKey::dummy().hash().to_hex(),
1976            "d6da18e4091fbcd86843604fb8ff2d9613e76fa16c49b0263a1566a8e7188007"
1977        );
1978    }
1979
1980    #[test]
1981    fn test_rand() {
1982        for _ in 0..1000 {
1983            let rand = Account::rand();
1984            let hash = rand.hash();
1985
1986            let bytes = Account::serialize(&rand);
1987            let rand2: Account = Account::deserialize(&bytes);
1988
1989            assert_eq!(hash, rand2.hash());
1990        }
1991    }
1992
1993    #[cfg(not(target_family = "wasm"))] // Use multiple threads
1994    #[test]
1995    fn test_rand_tree() {
1996        // use rayon::prelude::*;
1997
1998        // let mut db = Database::<V2>::create(20);
1999        // let mut accounts = Vec::with_capacity(1000);
2000
2001        // const NACCOUNTS: usize = 1000;
2002
2003        // for _ in 0..NACCOUNTS {
2004        //     let rand = Account::rand();
2005        //     accounts.push(rand);
2006        // }
2007
2008        // let pool = rayon::ThreadPoolBuilder::new()
2009        //     .num_threads(16)
2010        //     .build()
2011        //     .unwrap();
2012
2013        // let now = redux::Instant::now();
2014        // let hashes = pool.install(|| {
2015        //     accounts
2016        //         .par_iter()
2017        //         .map(|acc| acc.hash())
2018        //         .collect::<Vec<_>>()
2019        // });
2020
2021        // assert_eq!(hashes.len(), NACCOUNTS);
2022        // elog!(
2023        //     "elapsed to hash accounts in 16 threads: {:?}",
2024        //     now.elapsed(),
2025        // );
2026
2027        // let mut hashes = Vec::with_capacity(accounts.len());
2028        // let now = redux::Instant::now();
2029        // for account in accounts.iter() {
2030        //     hashes.push(account.hash());
2031        // }
2032        // assert_eq!(hashes.len(), NACCOUNTS);
2033        // elog!("elapsed to hash accounts in 1 thread: {:?}", now.elapsed(),);
2034
2035        // let now = redux::Instant::now();
2036        // for account in accounts.into_iter() {
2037        //     let id = account.id();
2038        //     db.get_or_create_account(id, account).unwrap();
2039        // }
2040        // assert_eq!(db.naccounts(), NACCOUNTS);
2041        // elog!("elapsed to insert in tree: {:?}", now.elapsed());
2042
2043        // let now = redux::Instant::now();
2044        // db.root_hash();
2045        // elog!("root hash computed in {:?}", now.elapsed());
2046    }
2047}