mina_tree/account/
account.rs

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