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