mina_tree/account/
account.rs

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