diem_types/
account_state.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8use crate::{
9    access_path::Path,
10    account_address::AccountAddress,
11    account_config::{
12        type_tag_for_currency_code, AccountResource, AccountRole,
13        BalanceResource, ChainIdResource, ChildVASP, Credential,
14        CurrencyInfoResource, DesignatedDealer, DesignatedDealerPreburns,
15        FreezingBit, ParentVASP, PreburnQueueResource, PreburnResource,
16    },
17    block_metadata::DiemBlockResource,
18    diem_timestamp::DiemTimestampResource,
19    on_chain_config::{
20        ConfigurationResource, DiemVersion, OnChainConfig,
21        RegisteredCurrencies, VMPublishingOption, ValidatorSet,
22    },
23    validator_config::{
24        ValidatorConfigResource, ValidatorOperatorConfigResource,
25    },
26};
27use anyhow::{format_err, Error, Result};
28use move_core_types::{identifier::Identifier, move_resource::MoveResource};
29use serde::{de::DeserializeOwned, Deserialize, Serialize};
30use std::{collections::btree_map::BTreeMap, convert::TryFrom, fmt};
31
32#[derive(Default, Deserialize, PartialEq, Serialize)]
33pub struct AccountState(BTreeMap<Vec<u8>, Vec<u8>>);
34
35impl AccountState {
36    // By design and do not remove
37    pub fn get_account_address(&self) -> Result<Option<AccountAddress>> {
38        self.get_account_resource().map(|opt_ar| {
39            opt_ar.map(|ar| ar.sent_events().key().get_creator_address())
40        })
41    }
42
43    pub fn get_account_resource(&self) -> Result<Option<AccountResource>> {
44        self.get_resource::<AccountResource>()
45    }
46
47    pub fn get_balance_resources(
48        &self, currency_codes: &[Identifier],
49    ) -> Result<BTreeMap<Identifier, BalanceResource>> {
50        currency_codes
51            .iter()
52            .filter_map(|currency_code| {
53                let currency_type_tag =
54                    type_tag_for_currency_code(currency_code.to_owned());
55                // TODO: update this to use BalanceResource::resource_path once
56                // that takes type parameters
57                self.get_resource_impl(&BalanceResource::access_path_for(
58                    currency_type_tag,
59                ))
60                .transpose()
61                .map(|balance| balance.map(|b| (currency_code.to_owned(), b)))
62            })
63            .collect()
64    }
65
66    pub fn get_preburn_balances(
67        &self, currency_codes: &[Identifier],
68    ) -> Result<BTreeMap<Identifier, PreburnResource>> {
69        currency_codes
70            .iter()
71            .filter_map(|currency_code| {
72                let currency_type_tag =
73                    type_tag_for_currency_code(currency_code.to_owned());
74                // TODO: update this to use PreburnResource::resource_path once
75                // that takes type parameters
76                self.get_resource_impl(&PreburnResource::access_path_for(
77                    currency_type_tag,
78                ))
79                .transpose()
80                .map(|preburn_balance| {
81                    preburn_balance.map(|b| (currency_code.to_owned(), b))
82                })
83            })
84            .collect()
85    }
86
87    pub fn get_preburn_queue_balances(
88        &self, currency_codes: &[Identifier],
89    ) -> Result<BTreeMap<Identifier, PreburnQueueResource>> {
90        currency_codes
91            .iter()
92            .filter_map(|currency_code| {
93                let currency_type_tag =
94                    type_tag_for_currency_code(currency_code.to_owned());
95                // TODO: update this to use PreburnQueueResource::resource_path
96                // once that takes type parameters
97                self.get_resource_impl(&PreburnQueueResource::access_path_for(
98                    currency_type_tag,
99                ))
100                .transpose()
101                .map(|preburn_balance| {
102                    preburn_balance.map(|b| (currency_code.to_owned(), b))
103                })
104            })
105            .collect()
106    }
107
108    pub fn get_chain_id_resource(&self) -> Result<Option<ChainIdResource>> {
109        self.get_resource::<ChainIdResource>()
110    }
111
112    pub fn get_configuration_resource(
113        &self,
114    ) -> Result<Option<ConfigurationResource>> {
115        self.get_resource::<ConfigurationResource>()
116    }
117
118    pub fn get_diem_timestamp_resource(
119        &self,
120    ) -> Result<Option<DiemTimestampResource>> {
121        self.get_resource::<DiemTimestampResource>()
122    }
123
124    pub fn get_validator_config_resource(
125        &self,
126    ) -> Result<Option<ValidatorConfigResource>> {
127        self.get_resource::<ValidatorConfigResource>()
128    }
129
130    pub fn get_validator_operator_config_resource(
131        &self,
132    ) -> Result<Option<ValidatorOperatorConfigResource>> {
133        self.get_resource::<ValidatorOperatorConfigResource>()
134    }
135
136    pub fn get_freezing_bit(&self) -> Result<Option<FreezingBit>> {
137        self.get_resource::<FreezingBit>()
138    }
139
140    pub fn get_account_role(
141        &self, currency_codes: &[Identifier],
142    ) -> Result<Option<AccountRole>> {
143        if self.0.contains_key(&ParentVASP::resource_path()) {
144            match (
145                self.get_resource::<ParentVASP>(),
146                self.get_resource::<Credential>(),
147            ) {
148                (Ok(Some(vasp)), Ok(Some(credential))) => {
149                    Ok(Some(AccountRole::ParentVASP { vasp, credential }))
150                }
151                _ => Ok(None),
152            }
153        } else if self.0.contains_key(&ChildVASP::resource_path()) {
154            self.get_resource::<ChildVASP>()
155                .map(|r_opt| r_opt.map(AccountRole::ChildVASP))
156        } else if self.0.contains_key(&DesignatedDealer::resource_path()) {
157            match (
158                self.get_resource::<Credential>(),
159                self.get_preburn_balances(&currency_codes),
160                self.get_preburn_queue_balances(&currency_codes),
161                self.get_resource::<DesignatedDealer>(),
162            ) {
163                (
164                    Ok(Some(dd_credential)),
165                    Ok(preburn_balances),
166                    Ok(preburn_queues),
167                    Ok(Some(designated_dealer)),
168                ) => {
169                    let preburn_balances = if preburn_balances.is_empty()
170                        && !preburn_queues.is_empty()
171                    {
172                        DesignatedDealerPreburns::PreburnQueue(preburn_queues)
173                    } else if !preburn_balances.is_empty()
174                        && preburn_queues.is_empty()
175                    {
176                        DesignatedDealerPreburns::Preburn(preburn_balances)
177                    } else {
178                        return Ok(None);
179                    };
180                    Ok(Some(AccountRole::DesignatedDealer {
181                        dd_credential,
182                        preburn_balances,
183                        designated_dealer,
184                    }))
185                }
186                _ => Ok(None),
187            }
188        } else {
189            // TODO: add role_id to Unknown
190            Ok(Some(AccountRole::Unknown))
191        }
192    }
193
194    pub fn get_validator_set(&self) -> Result<Option<ValidatorSet>> {
195        self.get_config::<ValidatorSet>()
196    }
197
198    pub fn get_diem_version(&self) -> Result<Option<DiemVersion>> {
199        self.get_config::<DiemVersion>()
200    }
201
202    pub fn get_vm_publishing_option(
203        &self,
204    ) -> Result<Option<VMPublishingOption>> {
205        self.0
206            .get(&VMPublishingOption::CONFIG_ID.access_path().path)
207            .map(|bytes| VMPublishingOption::deserialize_into_config(bytes))
208            .transpose()
209            .map_err(Into::into)
210    }
211
212    pub fn get_registered_currency_info_resources(
213        &self,
214    ) -> Result<Vec<CurrencyInfoResource>> {
215        let currencies: Option<RegisteredCurrencies> = self.get_config()?;
216        match currencies {
217            Some(currencies) => {
218                let codes = currencies.currency_codes();
219                let mut resources = vec![];
220                for code in codes {
221                    let access_path =
222                        CurrencyInfoResource::resource_path_for(code.clone());
223                    let info: CurrencyInfoResource = self
224                        .get_resource_impl(&access_path.path)?
225                        .ok_or_else(|| {
226                            format_err!(
227                                "currency info resource not found: {}",
228                                code
229                            )
230                        })?;
231                    resources.push(info);
232                }
233                Ok(resources)
234            }
235            None => Ok(vec![]),
236        }
237    }
238
239    pub fn get_diem_block_resource(&self) -> Result<Option<DiemBlockResource>> {
240        self.get_resource::<DiemBlockResource>()
241    }
242
243    pub fn get(&self, key: &[u8]) -> Option<&Vec<u8>> { self.0.get(key) }
244
245    pub fn get_resource_impl<T: DeserializeOwned>(
246        &self, key: &[u8],
247    ) -> Result<Option<T>> {
248        self.0
249            .get(key)
250            .map(|bytes| bcs::from_bytes(bytes))
251            .transpose()
252            .map_err(Into::into)
253    }
254
255    pub fn insert(&mut self, key: Vec<u8>, value: Vec<u8>) -> Option<Vec<u8>> {
256        self.0.insert(key, value)
257    }
258
259    pub fn remove(&mut self, key: &[u8]) -> Option<Vec<u8>> {
260        self.0.remove(key)
261    }
262
263    pub fn iter(
264        &self,
265    ) -> impl std::iter::Iterator<Item = (&Vec<u8>, &Vec<u8>)> {
266        self.0.iter()
267    }
268
269    pub fn get_config<T: OnChainConfig>(&self) -> Result<Option<T>> {
270        self.get_resource_impl(&T::CONFIG_ID.access_path().path)
271    }
272
273    pub fn get_resource<T: MoveResource + DeserializeOwned>(
274        &self,
275    ) -> Result<Option<T>> {
276        self.get_resource_impl(&T::struct_tag().access_vector())
277    }
278
279    /// Return an iterator over the module values stored under this account
280    pub fn get_modules(&self) -> impl Iterator<Item = &Vec<u8>> {
281        self.0.iter().filter_map(|(k, v)| {
282            match Path::try_from(k).expect("Invalid access path") {
283                Path::Code(_) => Some(v),
284                Path::Resource(_) => None,
285            }
286        })
287    }
288}
289
290impl fmt::Debug for AccountState {
291    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292        // TODO: add support for other types of resources
293        let account_resource_str = self
294            .get_account_resource()
295            .map(|account_resource_opt| format!("{:#?}", account_resource_opt))
296            .unwrap_or_else(|e| format!("parse error: {:#?}", e));
297
298        let diem_timestamp_str = self
299            .get_diem_timestamp_resource()
300            .map(|diem_timestamp_opt| format!("{:#?}", diem_timestamp_opt))
301            .unwrap_or_else(|e| format!("parse: {:#?}", e));
302
303        let validator_config_str = self
304            .get_validator_config_resource()
305            .map(|validator_config_opt| format!("{:#?}", validator_config_opt))
306            .unwrap_or_else(|e| format!("parse error: {:#?}", e));
307
308        let validator_set_str = self
309            .get_validator_set()
310            .map(|validator_set_opt| format!("{:#?}", validator_set_opt))
311            .unwrap_or_else(|e| format!("parse error: {:#?}", e));
312
313        write!(
314            f,
315            "{{ \n \
316             AccountResource {{ {} }} \n \
317             DiemTimestamp {{ {} }} \n \
318             ValidatorConfig {{ {} }} \n \
319             ValidatorSet {{ {} }} \n \
320             }}",
321            account_resource_str,
322            diem_timestamp_str,
323            validator_config_str,
324            validator_set_str,
325        )
326    }
327}
328
329impl TryFrom<(&AccountResource, &BalanceResource)> for AccountState {
330    type Error = Error;
331
332    fn try_from(
333        (account_resource, balance_resource): (
334            &AccountResource,
335            &BalanceResource,
336        ),
337    ) -> Result<Self> {
338        let mut btree_map: BTreeMap<Vec<u8>, Vec<u8>> = BTreeMap::new();
339        btree_map.insert(
340            AccountResource::resource_path(),
341            bcs::to_bytes(account_resource)?,
342        );
343        btree_map.insert(
344            BalanceResource::resource_path(),
345            bcs::to_bytes(balance_resource)?,
346        );
347
348        Ok(Self(btree_map))
349    }
350}