1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// Copyright 2020 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0

//! Module contains the Account data type
//! and associated types

pub mod permissions;
pub mod timing;
pub mod token_permissions;
pub mod token_symbol;
pub mod zkapp;

use crate::{types::*, *};

pub use self::zkapp::{ZkAppOptionHashableWrapper, ZkAppUriOptionHashableWrapper};
use mina_serialization_types_macros::AutoFrom;
pub use permissions::{AuthRequired, Permissions, PermissionsLegacy};
pub use timing::Timing;
pub use token_permissions::TokenPermissions;
pub use token_symbol::TokenSymbol;
pub use zkapp::{ZkApp, ZkAppUri};

use mina_crypto::hash::{ChainHash, StateHash};
use mina_hasher::ROInput;
use mina_serialization_types::account::*;
use proof_systems::mina_signer::CompressedPubKey;

/// An account identified by its public key and token ID. Multiple accounts may
/// where the same public key if multiple tokens exist
///
/// Accounts can also be Snapps in which case snapp data is required and proofs must
/// be provided to perform certain actions
#[derive(Clone, Debug, AutoFrom)]
#[auto_from(mina_serialization_types::account::Account)]
pub struct AccountLegacy {
    /// Account public key
    pub public_key: CompressedPubKey,
    /// Account token ID
    pub token_id: TokenId,
    /// Permission associated with the given token
    pub token_permissions: TokenPermissions,
    /// Balance of token held by account
    pub balance: Amount,
    /// Nonce (incremented with each tx to prevent replay)
    pub nonce: AccountNonce,
    /// ?
    pub receipt_chain_hash: ChainHash,
    /// Delegate for staking purposes
    pub delegate: Option<CompressedPubKey>,
    /// The state hash this account is voting for
    pub voting_for: StateHash,
    /// Any timing limitations places on this accounts balance
    /// Used for vesting
    pub timing: Timing,
    /// Level of permission required to do different account actions
    pub permissions: PermissionsLegacy,
    /// TODO: This should contain a Snapp account data once we have something to test against
    pub snapp: Option<()>,
}

impl BinProtSerializationType<'_> for AccountLegacy {
    type T = AccountV1;
}

// This implementation is not complete because we have switched to berkeley net
impl mina_hasher::Hashable for AccountLegacy {
    type D = ();

    // Uncomment these fields once they have implemented Hashable trait
    // and add unit tests when it's complete
    fn to_roinput(&self) -> ROInput {
        ROInput::new()
            // .append_hashable(self.public_key)
            .append_hashable(&self.token_id)
            // .append_hashable(self.token_permissions)
            .append_hashable(&self.balance)
        // .append_hashable(self.nonce)
        // .append_hashable(self.receipt_chain_hash)
        // .append_hashable(self.delegate)
        // .append_hashable(self.voting_for)
        // .append_hashable(self.timing)
        // .append_hashable(self.permissions)
        // .append_hashable(self.snapp)
    }

    fn domain_string(_: Self::D) -> Option<String> {
        Some("CodaAccount".into())
    }
}

/// TODO
#[derive(Clone, Debug, AutoFrom)]
#[auto_from(mina_serialization_types::account::AccountV0)]
pub struct Account {
    /// Account public key
    pub public_key: CompressedPubKey,
    /// Account token ID
    pub token_id: TokenId,
    /// Balance of token held by account
    pub balance: Amount,
    /// Permission associated with the given token
    pub token_permissions: TokenPermissions,
    /// Token Symbol
    pub token_symbol: TokenSymbol,
    /// Nonce (incremented with each tx to prevent replay)
    pub nonce: AccountNonce,
    /// ?
    pub receipt_chain_hash: ChainHash,
    /// Delegate for staking purposes
    pub delegate: Option<CompressedPubKey>,
    /// The state hash this account is voting for
    pub voting_for: StateHash,
    /// Any timing limitations places on this accounts balance
    /// Used for vesting
    pub timing: Timing,
    /// Level of permission required to do different account actions
    pub permissions: Permissions,
    /// TODO: This should contain a Snapp account data once we have something to test against
    pub zkapp: Option<ZkApp>,
    /// TODO: This should contain a Snapp account data once we have something to test against
    pub zkapp_uri: Option<ZkAppUri>,
}

impl mina_hasher::Hashable for Account {
    type D = ();

    fn to_roinput(&self) -> ROInput {
        self.roinput()
    }

    fn domain_string(_: Self::D) -> Option<String> {
        Some("CodaAccount".into())
    }
}

impl ToChunkedROInput for Account {
    fn to_chunked_roinput(&self) -> ChunkedROInput {
        ChunkedROInput::new()
            .append_chunked(&ZkAppUriOptionHashableWrapper(&self.zkapp_uri))
            .append_chunked(&ZkAppOptionHashableWrapper(&self.zkapp))
            .append_chunked(&self.permissions)
            .append_chunked(&self.timing)
            .append_chunked(&self.voting_for)
            .append_chunked(&CompressedPubKeyOptionHashableWrapper(&self.delegate))
            .append_chunked(&self.receipt_chain_hash)
            .append_chunked(&self.nonce)
            .append_chunked(&self.balance)
            .append_chunked(&self.token_symbol)
            .append_chunked(&self.token_permissions)
            .append_chunked(&self.token_id)
            .append_chunked(&CompressedPubKeyHashableWrapper(&self.public_key))
    }
}

// TODO: No test coverage yet because there're new hash algo changes again
// that we are not able to follow anymore.
// Genesis ledger test data needs to be updated and tests have to be fixed first
impl FromGraphQLJson for Account {
    fn from_graphql_json(json: &serde_json::Value) -> anyhow::Result<Self>
    where
        Self: Sized,
    {
        let is_disabled = json["isDisabled"].as_bool().unwrap_or_default();
        let is_token_owner = json["isTokenOwner"].as_bool().unwrap_or_default();
        Ok(Self {
            public_key: CompressedPubKey::from_address(
                json["publicKey"].as_str().unwrap_or_default(),
            )?,
            // FIXME: wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf
            token_id: TokenId(json["token"].as_str().unwrap_or_default().parse()?),
            balance: Amount(
                json["balance"]["total"]
                    .as_str()
                    .unwrap_or_default()
                    .parse()?,
            ),
            token_permissions: if is_token_owner {
                TokenPermissions::TokenOwned {
                    disable_new_accounts: is_disabled,
                }
            } else {
                TokenPermissions::NotOwned {
                    account_disabled: is_disabled,
                }
            },
            // FIXME: figure out what this is
            token_symbol: Default::default(),
            nonce: AccountNonce(json["nonce"].as_str().unwrap_or_default().parse()?),
            receipt_chain_hash: ChainHash::from_str(
                json["receiptChainHash"].as_str().unwrap_or_default(),
            )?,
            delegate: match json["delegate"].as_str() {
                Some(s) => Some(CompressedPubKey::from_address(s)?),
                _ => None,
            },
            voting_for: StateHash::from_str(json["votingFor"].as_str().unwrap_or_default())?,
            timing: Timing::from_graphql_json(&json["timing"])?,
            permissions: Permissions::from_graphql_json(&json["permissions"])?,
            // FIXME: struct to be defined
            zkapp: None,
            // FIXME: struct to be defined
            zkapp_uri: None,
        })
    }
}

impl BinProtSerializationType<'_> for Account {
    type T = AccountV0;
}