1use 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 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 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 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 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(¤cy_codes),
160 self.get_preburn_queue_balances(¤cy_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 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 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 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}