diem_json_rpc_types/
views.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4use anyhow::{ensure, format_err, Error, Result};
5use diem_crypto::hash::{CryptoHash, HashValue};
6use diem_transaction_builder::{error_explain, stdlib::ScriptCall};
7use diem_types::{
8    account_config::{
9        AccountResource, AccountRole, AdminTransactionEvent, BalanceResource, BaseUrlRotationEvent,
10        BurnEvent, CancelBurnEvent, ComplianceKeyRotationEvent, CreateAccountEvent,
11        CurrencyInfoResource, DesignatedDealerPreburns, FreezingBit, Limit, MintEvent,
12        NewBlockEvent, NewEpochEvent, PreburnEvent, ReceivedMintEvent, ReceivedPaymentEvent,
13        SentPaymentEvent, ToXDXExchangeRateUpdateEvent, VASPDomainEvent,
14    },
15    account_state::AccountState,
16    account_state_blob::{default_protocol::AccountStateWithProof, AccountStateBlob},
17    contract_event::{
18        default_protocol::{EventByVersionWithProof, EventWithProof},
19        ContractEvent,
20    },
21    diem_id_identifier::DiemIdVaspDomainIdentifier,
22    event::EventKey,
23    proof::default_protocol::{
24        AccountStateProof, AccumulatorConsistencyProof, SparseMerkleProof,
25        TransactionAccumulatorProof, TransactionInfoListWithProof, TransactionInfoWithProof,
26    },
27    state_proof::StateProof,
28    transaction::{
29        default_protocol::{AccountTransactionsWithProof, TransactionListWithProof},
30        Script, ScriptFunction, Transaction, TransactionArgument, TransactionInfoTrait,
31        TransactionPayload,
32    },
33    vm_status::KeptVMStatus,
34};
35use hex::FromHex;
36use move_core_types::{
37    account_address::AccountAddress,
38    identifier::Identifier,
39    language_storage::{StructTag, TypeTag},
40    move_resource::MoveStructType,
41    vm_status::AbortLocation,
42};
43use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};
44use std::{
45    collections::BTreeMap,
46    convert::{TryFrom, TryInto},
47};
48
49#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
50pub struct AmountView {
51    pub amount: u64,
52    pub currency: String,
53}
54
55impl AmountView {
56    fn new(amount: u64, currency: &str) -> Self {
57        Self {
58            amount,
59            currency: currency.to_string(),
60        }
61    }
62}
63
64#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
65#[serde(tag = "type")]
66pub enum AccountRoleView {
67    #[serde(rename = "child_vasp")]
68    ChildVASP { parent_vasp_address: AccountAddress },
69    #[serde(rename = "parent_vasp")]
70    ParentVASP {
71        human_name: String,
72        base_url: String,
73        expiration_time: u64,
74        compliance_key: BytesView,
75        num_children: u64,
76        compliance_key_rotation_events_key: EventKey,
77        base_url_rotation_events_key: EventKey,
78        #[serde(skip_serializing_if = "Option::is_none")]
79        vasp_domains: Option<Vec<DiemIdVaspDomainIdentifier>>,
80    },
81    #[serde(rename = "designated_dealer")]
82    DesignatedDealer {
83        human_name: String,
84        base_url: String,
85        expiration_time: u64,
86        compliance_key: BytesView,
87        preburn_balances: Vec<AmountView>,
88        received_mint_events_key: EventKey,
89        compliance_key_rotation_events_key: EventKey,
90        base_url_rotation_events_key: EventKey,
91        #[serde(skip_serializing_if = "Option::is_none")]
92        preburn_queues: Option<Vec<PreburnQueueView>>,
93    },
94    #[serde(rename = "treasury_compliance")]
95    TreasuryCompliance {
96        #[serde(skip_serializing_if = "Option::is_none")]
97        vasp_domain_events_key: Option<EventKey>,
98    },
99    /// The Unknown variant is deserialized by the client if it sees
100    /// a variant that it doesn't know about
101    #[serde(rename = "unknown")]
102    #[serde(other)]
103    Unknown,
104}
105
106impl AccountRoleView {
107    pub(crate) fn convert_preburn_balances(
108        preburn_balances: DesignatedDealerPreburns,
109    ) -> (Vec<AmountView>, Option<Vec<PreburnQueueView>>) {
110        match preburn_balances {
111            DesignatedDealerPreburns::Preburn(preburn_balances) => {
112                let preburn_balances: Vec<_> = preburn_balances
113                    .iter()
114                    .map(|(currency_code, balance)| {
115                        AmountView::new(balance.coin(), currency_code.as_str())
116                    })
117                    .collect();
118                let preburn_queues = preburn_balances
119                    .iter()
120                    .cloned()
121                    .map(|amt_view| {
122                        PreburnQueueView::new(
123                            amt_view.currency.clone(),
124                            vec![PreburnWithMetadataView {
125                                preburn: amt_view,
126                                metadata: None,
127                            }],
128                        )
129                    })
130                    .collect();
131                (preburn_balances, Some(preburn_queues))
132            }
133            DesignatedDealerPreburns::PreburnQueue(preburn_queues) => {
134                let preburn_balances = preburn_queues
135                    .iter()
136                    .map(|(currency_code, preburns)| {
137                        let total_balance =
138                            preburns.preburns().iter().fold(0, |acc: u64, preburn| {
139                                acc.checked_add(preburn.preburn().coin()).unwrap()
140                            });
141                        AmountView::new(total_balance, currency_code.as_str())
142                    })
143                    .collect();
144                let preburn_queues = preburn_queues
145                    .into_iter()
146                    .map(|(currency_code, preburns)| {
147                        PreburnQueueView::new(
148                            currency_code.to_string(),
149                            preburns
150                                .preburns()
151                                .iter()
152                                .map(|preburn| PreburnWithMetadataView {
153                                    preburn: AmountView::new(
154                                        preburn.preburn().coin(),
155                                        currency_code.as_str(),
156                                    ),
157                                    metadata: Some(BytesView::new(preburn.metadata())),
158                                })
159                                .collect::<Vec<_>>(),
160                        )
161                    })
162                    .collect();
163                (preburn_balances, Some(preburn_queues))
164            }
165        }
166    }
167}
168
169#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
170pub struct AccountView {
171    pub address: AccountAddress,
172    pub balances: Vec<AmountView>,
173    pub sequence_number: u64,
174    pub authentication_key: BytesView,
175    pub sent_events_key: EventKey,
176    pub received_events_key: EventKey,
177    pub delegated_key_rotation_capability: bool,
178    pub delegated_withdrawal_capability: bool,
179    pub is_frozen: bool,
180    pub role: AccountRoleView,
181    // the transaction version of the account data
182    #[serde(skip_serializing_if = "Option::is_none")]
183    pub version: Option<u64>,
184}
185
186impl AccountView {
187    pub fn new(
188        address: AccountAddress,
189        account: &AccountResource,
190        balances: BTreeMap<Identifier, BalanceResource>,
191        account_role: AccountRole,
192        freezing_bit: FreezingBit,
193        version: u64,
194    ) -> Self {
195        Self {
196            address,
197            balances: balances
198                .iter()
199                .map(|(currency_code, balance)| {
200                    AmountView::new(balance.coin(), currency_code.as_str())
201                })
202                .collect(),
203            sequence_number: account.sequence_number(),
204            authentication_key: BytesView::from(account.authentication_key()),
205            sent_events_key: *account.sent_events().key(),
206            received_events_key: *account.received_events().key(),
207            delegated_key_rotation_capability: account.has_delegated_key_rotation_capability(),
208            delegated_withdrawal_capability: account.has_delegated_withdrawal_capability(),
209            is_frozen: freezing_bit.is_frozen(),
210            role: AccountRoleView::from(account_role),
211            version: Some(version),
212        }
213    }
214
215    pub fn try_from_account_state(
216        address: AccountAddress,
217        account_state: AccountState,
218        version: u64,
219    ) -> Result<Self> {
220        let account_resource = account_state
221            .get_account_resource()?
222            .ok_or_else(|| format_err!("invalid account state: no account resource"))?;
223        let freezing_bit = account_state
224            .get_freezing_bit()?
225            .ok_or_else(|| format_err!("invalid account state: no freezing bit"))?;
226        let account_role = account_state
227            .get_account_role()?
228            .ok_or_else(|| format_err!("invalid account state: no account role"))?;
229        let balances = account_state.get_balance_resources()?;
230
231        Ok(AccountView::new(
232            address,
233            &account_resource,
234            balances,
235            account_role,
236            freezing_bit,
237            version,
238        ))
239    }
240}
241
242#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
243pub struct PreburnQueueView {
244    pub currency: String,
245    pub preburns: Vec<PreburnWithMetadataView>,
246}
247
248#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
249pub struct PreburnWithMetadataView {
250    pub preburn: AmountView,
251    #[serde(skip_serializing_if = "Option::is_none")]
252    pub metadata: Option<BytesView>,
253}
254
255impl PreburnQueueView {
256    pub fn new(currency: String, preburns: Vec<PreburnWithMetadataView>) -> Self {
257        Self { currency, preburns }
258    }
259}
260
261#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
262pub struct EventView {
263    pub key: EventKey,
264    pub sequence_number: u64,
265    pub transaction_version: u64,
266    pub data: EventDataView,
267}
268
269impl TryFrom<(u64, ContractEvent)> for EventView {
270    type Error = Error;
271
272    fn try_from((txn_version, event): (u64, ContractEvent)) -> Result<Self> {
273        Ok(EventView {
274            key: *event.key(),
275            sequence_number: event.sequence_number(),
276            transaction_version: txn_version,
277            data: event.try_into()?,
278        })
279    }
280}
281
282#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
283pub struct EventWithProofView {
284    pub event_with_proof: BytesView,
285}
286
287impl TryFrom<&EventWithProofView> for EventWithProof {
288    type Error = Error;
289
290    fn try_from(view: &EventWithProofView) -> Result<Self> {
291        Ok(bcs::from_bytes(&view.event_with_proof)?)
292    }
293}
294
295impl TryFrom<&EventWithProof> for EventWithProofView {
296    type Error = Error;
297
298    fn try_from(event: &EventWithProof) -> Result<Self> {
299        Ok(Self {
300            event_with_proof: BytesView::from(bcs::to_bytes(event)?),
301        })
302    }
303}
304
305#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
306pub struct EventByVersionWithProofView {
307    #[serde(skip_serializing_if = "Option::is_none")]
308    pub lower_bound_incl: Option<EventWithProofView>,
309
310    #[serde(skip_serializing_if = "Option::is_none")]
311    pub upper_bound_excl: Option<EventWithProofView>,
312}
313
314impl TryFrom<&EventByVersionWithProofView> for EventByVersionWithProof {
315    type Error = Error;
316
317    fn try_from(view: &EventByVersionWithProofView) -> Result<Self, Self::Error> {
318        Ok(Self::new(
319            view.lower_bound_incl
320                .as_ref()
321                .map(EventWithProof::try_from)
322                .transpose()?,
323            view.upper_bound_excl
324                .as_ref()
325                .map(EventWithProof::try_from)
326                .transpose()?,
327        ))
328    }
329}
330
331impl TryFrom<&EventByVersionWithProof> for EventByVersionWithProofView {
332    type Error = Error;
333
334    fn try_from(proof: &EventByVersionWithProof) -> Result<Self, Self::Error> {
335        Ok(Self {
336            lower_bound_incl: proof
337                .lower_bound_incl
338                .as_ref()
339                .map(EventWithProofView::try_from)
340                .transpose()?,
341            upper_bound_excl: proof
342                .upper_bound_excl
343                .as_ref()
344                .map(EventWithProofView::try_from)
345                .transpose()?,
346        })
347    }
348}
349
350#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
351#[serde(tag = "type")]
352pub enum EventDataView {
353    #[serde(rename = "burn")]
354    Burn {
355        amount: AmountView,
356        preburn_address: AccountAddress,
357    },
358    #[serde(rename = "cancelburn")]
359    CancelBurn {
360        amount: AmountView,
361        preburn_address: AccountAddress,
362    },
363    #[serde(rename = "mint")]
364    Mint { amount: AmountView },
365    #[serde(rename = "to_xdx_exchange_rate_update")]
366    ToXDXExchangeRateUpdate {
367        currency_code: String,
368        new_to_xdx_exchange_rate: f32,
369    },
370    #[serde(rename = "preburn")]
371    Preburn {
372        amount: AmountView,
373        preburn_address: AccountAddress,
374    },
375    #[serde(rename = "receivedpayment")]
376    ReceivedPayment {
377        amount: AmountView,
378        sender: AccountAddress,
379        receiver: AccountAddress,
380        metadata: BytesView,
381    },
382    #[serde(rename = "sentpayment")]
383    SentPayment {
384        amount: AmountView,
385        receiver: AccountAddress,
386        sender: AccountAddress,
387        metadata: BytesView,
388    },
389    #[serde(rename = "admintransaction")]
390    AdminTransaction { committed_timestamp_secs: u64 },
391    #[serde(rename = "newepoch")]
392    NewEpoch { epoch: u64 },
393    #[serde(rename = "newblock")]
394    NewBlock {
395        round: u64,
396        proposer: AccountAddress,
397        proposed_time: u64,
398    },
399    #[serde(rename = "receivedmint")]
400    ReceivedMint {
401        amount: AmountView,
402        destination_address: AccountAddress,
403    },
404    #[serde(rename = "compliancekeyrotation")]
405    ComplianceKeyRotation {
406        new_compliance_public_key: BytesView,
407        time_rotated_seconds: u64,
408    },
409    #[serde(rename = "baseurlrotation")]
410    BaseUrlRotation {
411        new_base_url: String,
412        time_rotated_seconds: u64,
413    },
414    #[serde(rename = "createaccount")]
415    CreateAccount {
416        created_address: AccountAddress,
417        role_id: u64,
418    },
419    #[serde(rename = "vaspdomain")]
420    VASPDomain {
421        // Whether a domain was added or removed
422        removed: bool,
423        // VASP Domain string of the account
424        domain: DiemIdVaspDomainIdentifier,
425        // On-chain account address
426        address: AccountAddress,
427    },
428    #[serde(rename = "unknown")]
429    Unknown {
430        #[serde(skip_serializing_if = "Option::is_none")]
431        bytes: Option<BytesView>,
432    },
433
434    // used by client to deserialize server response
435    #[serde(other)]
436    UnknownToClient,
437}
438
439impl TryFrom<ContractEvent> for EventDataView {
440    type Error = Error;
441
442    fn try_from(event: ContractEvent) -> Result<Self> {
443        let data = if event.type_tag() == &TypeTag::Struct(ReceivedPaymentEvent::struct_tag()) {
444            let received_event = ReceivedPaymentEvent::try_from(&event)?;
445            let amount_view = AmountView::new(
446                received_event.amount(),
447                received_event.currency_code().as_str(),
448            );
449            EventDataView::ReceivedPayment {
450                amount: amount_view,
451                sender: received_event.sender(),
452                receiver: event.key().get_creator_address(),
453                metadata: BytesView::from(received_event.metadata()),
454            }
455        } else if event.type_tag() == &TypeTag::Struct(SentPaymentEvent::struct_tag()) {
456            let sent_event = SentPaymentEvent::try_from(&event)?;
457            let amount_view =
458                AmountView::new(sent_event.amount(), sent_event.currency_code().as_str());
459            EventDataView::SentPayment {
460                amount: amount_view,
461                receiver: sent_event.receiver(),
462                sender: event.key().get_creator_address(),
463                metadata: BytesView::from(sent_event.metadata()),
464            }
465        } else if event.type_tag() == &TypeTag::Struct(PreburnEvent::struct_tag()) {
466            let preburn_event = PreburnEvent::try_from(&event)?;
467            let amount_view = AmountView::new(
468                preburn_event.amount(),
469                preburn_event.currency_code().as_str(),
470            );
471            EventDataView::Preburn {
472                amount: amount_view,
473                preburn_address: preburn_event.preburn_address(),
474            }
475        } else if event.type_tag() == &TypeTag::Struct(BurnEvent::struct_tag()) {
476            let burn_event = BurnEvent::try_from(&event)?;
477            let amount_view =
478                AmountView::new(burn_event.amount(), burn_event.currency_code().as_str());
479            EventDataView::Burn {
480                amount: amount_view,
481                preburn_address: burn_event.preburn_address(),
482            }
483        } else if event.type_tag() == &TypeTag::Struct(CancelBurnEvent::struct_tag()) {
484            let cancel_burn_event = CancelBurnEvent::try_from(&event)?;
485            let amount_view = AmountView::new(
486                cancel_burn_event.amount(),
487                cancel_burn_event.currency_code().as_str(),
488            );
489            EventDataView::CancelBurn {
490                amount: amount_view,
491                preburn_address: cancel_burn_event.preburn_address(),
492            }
493        } else if event.type_tag() == &TypeTag::Struct(ToXDXExchangeRateUpdateEvent::struct_tag()) {
494            let update_event = ToXDXExchangeRateUpdateEvent::try_from(&event)?;
495            EventDataView::ToXDXExchangeRateUpdate {
496                currency_code: update_event.currency_code().to_string(),
497                new_to_xdx_exchange_rate: update_event.new_to_xdx_exchange_rate(),
498            }
499        } else if event.type_tag() == &TypeTag::Struct(MintEvent::struct_tag()) {
500            let mint_event = MintEvent::try_from(&event)?;
501            let amount_view =
502                AmountView::new(mint_event.amount(), mint_event.currency_code().as_str());
503            EventDataView::Mint {
504                amount: amount_view,
505            }
506        } else if event.type_tag() == &TypeTag::Struct(ReceivedMintEvent::struct_tag()) {
507            let received_mint_event = ReceivedMintEvent::try_from(&event)?;
508            let amount_view = AmountView::new(
509                received_mint_event.amount(),
510                received_mint_event.currency_code().as_str(),
511            );
512            EventDataView::ReceivedMint {
513                amount: amount_view,
514                destination_address: received_mint_event.destination_address(),
515            }
516        } else if event.type_tag() == &TypeTag::Struct(ComplianceKeyRotationEvent::struct_tag()) {
517            let rotation_event = ComplianceKeyRotationEvent::try_from(&event)?;
518            EventDataView::ComplianceKeyRotation {
519                new_compliance_public_key: rotation_event.new_compliance_public_key().into(),
520                time_rotated_seconds: rotation_event.time_rotated_seconds(),
521            }
522        } else if event.type_tag() == &TypeTag::Struct(BaseUrlRotationEvent::struct_tag()) {
523            let rotation_event = BaseUrlRotationEvent::try_from(&event)?;
524            String::from_utf8(rotation_event.new_base_url().to_vec())
525                .map(|new_base_url| EventDataView::BaseUrlRotation {
526                    new_base_url,
527                    time_rotated_seconds: rotation_event.time_rotated_seconds(),
528                })
529                .map_err(|_| format_err!("Unable to parse BaseUrlRotationEvent"))?
530        } else if event.type_tag() == &TypeTag::Struct(NewBlockEvent::struct_tag()) {
531            let new_block_event = NewBlockEvent::try_from(&event)?;
532            EventDataView::NewBlock {
533                proposer: new_block_event.proposer(),
534                round: new_block_event.round(),
535                proposed_time: new_block_event.proposed_time(),
536            }
537        } else if event.type_tag() == &TypeTag::Struct(NewEpochEvent::struct_tag()) {
538            let new_epoch_event = NewEpochEvent::try_from(&event)?;
539            EventDataView::NewEpoch {
540                epoch: new_epoch_event.epoch(),
541            }
542        } else if event.type_tag() == &TypeTag::Struct(CreateAccountEvent::struct_tag()) {
543            let create_account_event = CreateAccountEvent::try_from(&event)?;
544            let created_address = create_account_event.created();
545            let role_id = create_account_event.role_id();
546            EventDataView::CreateAccount {
547                created_address,
548                role_id,
549            }
550        } else if event.type_tag() == &TypeTag::Struct(AdminTransactionEvent::struct_tag()) {
551            let admin_transaction_event = AdminTransactionEvent::try_from(&event)?;
552            EventDataView::AdminTransaction {
553                committed_timestamp_secs: admin_transaction_event.committed_timestamp_secs(),
554            }
555        } else if event.type_tag() == &TypeTag::Struct(VASPDomainEvent::struct_tag()) {
556            let vasp_domain_event = VASPDomainEvent::try_from(&event)?;
557            EventDataView::VASPDomain {
558                removed: vasp_domain_event.removed(),
559                domain: vasp_domain_event.domain().domain().clone(),
560                address: vasp_domain_event.address(),
561            }
562        } else {
563            EventDataView::Unknown {
564                bytes: Some(event.event_data().into()),
565            }
566        };
567
568        Ok(data)
569    }
570}
571
572#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
573pub struct MetadataView {
574    pub version: u64,
575    pub accumulator_root_hash: HashValue,
576    pub timestamp: u64,
577    pub chain_id: u8,
578    #[serde(skip_serializing_if = "Option::is_none")]
579    pub script_hash_allow_list: Option<Vec<HashValue>>,
580    #[serde(skip_serializing_if = "Option::is_none")]
581    pub module_publishing_allowed: Option<bool>,
582    #[serde(skip_serializing_if = "Option::is_none")]
583    pub diem_version: Option<u64>,
584    #[serde(skip_serializing_if = "Option::is_none")]
585    pub dual_attestation_limit: Option<u64>,
586}
587
588impl MetadataView {
589    pub fn new(
590        version: u64,
591        accumulator_root_hash: HashValue,
592        timestamp: u64,
593        chain_id: u8,
594    ) -> Self {
595        Self {
596            version,
597            accumulator_root_hash,
598            timestamp,
599            chain_id,
600            script_hash_allow_list: None,
601            module_publishing_allowed: None,
602            diem_version: None,
603            dual_attestation_limit: None,
604        }
605    }
606
607    pub fn with_diem_root(&mut self, diem_root: &AccountState) -> Result<()> {
608        if let Some(vm_publishing_option) = diem_root.get_vm_publishing_option()? {
609            self.script_hash_allow_list = Some(vm_publishing_option.script_allow_list);
610            self.module_publishing_allowed = Some(vm_publishing_option.is_open_module);
611        }
612        if let Some(diem_version) = diem_root.get_diem_version()? {
613            self.diem_version = Some(diem_version.major);
614        }
615        if let Some(limit) = diem_root.get_resource::<Limit>()? {
616            self.dual_attestation_limit = Some(limit.micro_xdx_limit);
617        }
618        Ok(())
619    }
620}
621
622#[derive(Clone, PartialEq)]
623pub struct BytesView(Box<[u8]>);
624
625impl BytesView {
626    pub fn new<T: Into<Box<[u8]>>>(bytes: T) -> Self {
627        Self(bytes.into())
628    }
629
630    pub fn into_inner(self) -> Box<[u8]> {
631        self.0
632    }
633
634    pub fn inner(&self) -> &[u8] {
635        &self.0
636    }
637}
638
639impl std::ops::Deref for BytesView {
640    type Target = [u8];
641
642    fn deref(&self) -> &Self::Target {
643        &self.0
644    }
645}
646
647impl std::convert::AsRef<[u8]> for BytesView {
648    fn as_ref(&self) -> &[u8] {
649        self.inner()
650    }
651}
652
653impl std::fmt::Display for BytesView {
654    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
655        for byte in self.inner() {
656            write!(f, "{:02x}", byte)?;
657        }
658        Ok(())
659    }
660}
661
662impl std::fmt::Debug for BytesView {
663    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
664        write!(f, "BytesView(\"{}\")", self)
665    }
666}
667
668impl From<&[u8]> for BytesView {
669    fn from(bytes: &[u8]) -> Self {
670        Self(bytes.into())
671    }
672}
673
674impl From<Vec<u8>> for BytesView {
675    fn from(bytes: Vec<u8>) -> Self {
676        Self(bytes.into_boxed_slice())
677    }
678}
679
680impl<'de> Deserialize<'de> for BytesView {
681    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
682    where
683        D: Deserializer<'de>,
684    {
685        let s = <String>::deserialize(deserializer)?;
686        <Vec<u8>>::from_hex(s)
687            .map_err(D::Error::custom)
688            .map(Into::into)
689    }
690}
691
692impl Serialize for BytesView {
693    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
694    where
695        S: Serializer,
696    {
697        hex::encode(self).serialize(serializer)
698    }
699}
700
701#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
702pub struct MoveAbortExplanationView {
703    pub category: String,
704    pub category_description: String,
705    pub reason: String,
706    pub reason_description: String,
707}
708
709impl std::fmt::Display for MoveAbortExplanationView {
710    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
711        writeln!(f, "Error Category: {}", self.category)?;
712        writeln!(f, "\tCategory Description: {}", self.category_description)?;
713        writeln!(f, "Error Reason: {}", self.reason)?;
714        writeln!(f, "\tReason Description: {}", self.reason_description)
715    }
716}
717
718#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
719#[serde(tag = "type")]
720#[serde(rename_all = "snake_case")]
721pub enum VMStatusView {
722    Executed,
723    OutOfGas,
724    MoveAbort {
725        location: String,
726        abort_code: u64,
727        #[serde(skip_serializing_if = "Option::is_none")]
728        explanation: Option<MoveAbortExplanationView>,
729    },
730    ExecutionFailure {
731        location: String,
732        function_index: u16,
733        code_offset: u16,
734    },
735    MiscellaneousError,
736    VerificationError,
737    DeserializationError,
738    PublishingFailure,
739    #[serde(other)]
740    Unknown,
741}
742
743impl VMStatusView {
744    pub fn is_executed(&self) -> bool {
745        matches!(self, Self::Executed)
746    }
747}
748
749impl std::fmt::Display for VMStatusView {
750    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
751        match self {
752            VMStatusView::Executed => write!(f, "Executed"),
753            VMStatusView::OutOfGas => write!(f, "Out of Gas"),
754            VMStatusView::MoveAbort {
755                location,
756                abort_code,
757                explanation,
758            } => {
759                write!(f, "Move Abort: {} at {}", abort_code, location)?;
760                if let Some(explanation) = explanation {
761                    write!(f, "\nExplanation:\n{}", explanation)?
762                }
763                Ok(())
764            }
765            VMStatusView::ExecutionFailure {
766                location,
767                function_index,
768                code_offset,
769            } => write!(
770                f,
771                "Execution failure: {} {} {}",
772                location, function_index, code_offset
773            ),
774            VMStatusView::MiscellaneousError => write!(f, "Miscellaneous Error"),
775            VMStatusView::VerificationError => write!(f, "Verification Error"),
776            VMStatusView::DeserializationError => write!(f, "Deserialization Error"),
777            VMStatusView::PublishingFailure => write!(f, "Publishing Failure"),
778            VMStatusView::Unknown => write!(f, "Unknown Error"),
779        }
780    }
781}
782
783impl From<&KeptVMStatus> for VMStatusView {
784    fn from(status: &KeptVMStatus) -> Self {
785        match status {
786            KeptVMStatus::Executed => VMStatusView::Executed,
787            KeptVMStatus::OutOfGas => VMStatusView::OutOfGas,
788            KeptVMStatus::MoveAbort(loc, abort_code) => {
789                let explanation = if let AbortLocation::Module(module_id) = loc {
790                    error_explain::get_explanation(module_id, *abort_code).map(|context| {
791                        MoveAbortExplanationView {
792                            category: context.category.code_name,
793                            category_description: context.category.code_description,
794                            reason: context.reason.code_name,
795                            reason_description: context.reason.code_description,
796                        }
797                    })
798                } else {
799                    None
800                };
801
802                VMStatusView::MoveAbort {
803                    explanation,
804                    location: loc.to_string(),
805                    abort_code: *abort_code,
806                }
807            }
808            KeptVMStatus::ExecutionFailure {
809                location,
810                function,
811                code_offset,
812            } => VMStatusView::ExecutionFailure {
813                location: location.to_string(),
814                function_index: *function,
815                code_offset: *code_offset,
816            },
817            KeptVMStatus::MiscellaneousError => VMStatusView::MiscellaneousError,
818        }
819    }
820}
821
822#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
823pub struct TransactionView {
824    pub version: u64,
825    pub transaction: TransactionDataView,
826    pub hash: HashValue,
827    pub bytes: BytesView,
828    pub events: Vec<EventView>,
829    pub vm_status: VMStatusView,
830    pub gas_used: u64,
831}
832
833impl TransactionView {
834    pub fn try_from_tx_and_events<T: TransactionInfoTrait>(
835        version: u64,
836        tx: Transaction,
837        tx_info: T,
838        events: Vec<ContractEvent>,
839    ) -> Result<Self> {
840        let events = events
841            .into_iter()
842            .map(|event| EventView::try_from((version, event)))
843            .collect::<Result<Vec<_>>>()?;
844
845        Ok(TransactionView {
846            version,
847            hash: tx.hash(),
848            bytes: BytesView::new(bcs::to_bytes(&tx)?),
849            transaction: TransactionDataView::from(tx),
850            events,
851            vm_status: VMStatusView::from(tx_info.status()),
852            gas_used: tx_info.gas_used(),
853        })
854    }
855}
856
857#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
858pub struct TransactionListView(pub Vec<TransactionView>);
859
860impl TransactionListView {
861    pub fn empty() -> Self {
862        Self(Vec::new())
863    }
864}
865
866impl TryFrom<TransactionListWithProof> for TransactionListView {
867    type Error = Error;
868
869    fn try_from(txs: TransactionListWithProof) -> Result<Self, Self::Error> {
870        if txs.transactions.is_empty() {
871            return Ok(Self::empty());
872        }
873        let start_version = txs
874            .first_transaction_version
875            .ok_or_else(|| format_err!("expected a start version since tx list non-empty"))?;
876
877        let transactions = txs.transactions;
878        let transaction_infos = txs.proof.transaction_infos;
879
880        ensure!(
881            transaction_infos.len() == transactions.len(),
882            "expected same number of transaction_infos as transactions, \
883             received {} transaction_infos and {} transactions",
884            transaction_infos.len(),
885            transactions.len(),
886        );
887
888        let event_lists = if let Some(event_lists) = txs.events {
889            ensure!(
890                event_lists.len() == transactions.len(),
891                "expected same number of event lists as transactions, \
892                 received {} event lists and {} transactions",
893                event_lists.len(),
894                transactions.len(),
895            );
896            event_lists
897        } else {
898            vec![Vec::new(); transactions.len()]
899        };
900
901        let tx_iter = transactions.into_iter();
902        let infos_iter = transaction_infos.into_iter();
903        let event_lists_iter = event_lists.into_iter();
904
905        let iter = tx_iter.enumerate().zip(infos_iter).zip(event_lists_iter);
906
907        let tx_list = iter
908            .map(|(((offset, tx), tx_info), tx_events)| {
909                let version = start_version + offset as u64;
910                TransactionView::try_from_tx_and_events(version, tx, tx_info, tx_events)
911            })
912            .collect::<Result<Vec<_>>>()?;
913
914        Ok(TransactionListView(tx_list))
915    }
916}
917
918impl TryFrom<AccountTransactionsWithProof> for TransactionListView {
919    type Error = Error;
920
921    fn try_from(acct_txs: AccountTransactionsWithProof) -> Result<Self, Self::Error> {
922        let txs = acct_txs
923            .into_inner()
924            .into_iter()
925            .map(|tx| {
926                TransactionView::try_from_tx_and_events(
927                    tx.version,
928                    tx.transaction,
929                    tx.proof.transaction_info,
930                    tx.events.unwrap_or_default(),
931                )
932            })
933            .collect::<Result<Vec<_>>>()?;
934        Ok(TransactionListView(txs))
935    }
936}
937
938#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
939pub struct TransactionsWithProofsView {
940    pub serialized_transactions: Vec<BytesView>,
941    #[serde(skip_serializing_if = "Option::is_none")]
942    pub serialized_events: Option<BytesView>,
943    pub proofs: TransactionsProofsView,
944}
945
946impl TransactionsWithProofsView {
947    pub fn try_into_txn_list_with_proof(
948        &self,
949        start_version: u64,
950    ) -> Result<TransactionListWithProof> {
951        let transactions = self
952            .serialized_transactions
953            .iter()
954            .map(|tx| bcs::from_bytes::<Transaction>(tx.as_ref()))
955            .collect::<Result<Vec<_>, bcs::Error>>()?;
956
957        let events = self
958            .serialized_events
959            .as_ref()
960            .map(|events| bcs::from_bytes::<Vec<Vec<ContractEvent>>>(events.as_ref()))
961            .transpose()?;
962
963        let first_transaction_version = if !transactions.is_empty() {
964            Some(start_version)
965        } else {
966            None
967        };
968
969        Ok(TransactionListWithProof {
970            transactions,
971            events,
972            first_transaction_version,
973            proof: TransactionInfoListWithProof::try_from(&self.proofs)?,
974        })
975    }
976}
977
978impl TryFrom<&TransactionListWithProof> for TransactionsWithProofsView {
979    type Error = Error;
980
981    fn try_from(txs: &TransactionListWithProof) -> Result<Self, Self::Error> {
982        let serialized_transactions = txs
983            .transactions
984            .iter()
985            .map(|tx| bcs::to_bytes(tx).map(BytesView::new))
986            .collect::<Result<Vec<_>, bcs::Error>>()?;
987
988        let serialized_events = txs
989            .events
990            .as_ref()
991            .map(|events| bcs::to_bytes(events).map(BytesView::new))
992            .transpose()?;
993
994        Ok(TransactionsWithProofsView {
995            serialized_transactions,
996            serialized_events,
997            proofs: TransactionsProofsView::try_from(&txs.proof)?,
998        })
999    }
1000}
1001
1002#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
1003pub struct TransactionsProofsView {
1004    pub ledger_info_to_transaction_infos_proof: BytesView,
1005    pub transaction_infos: BytesView,
1006}
1007
1008impl TryFrom<&TransactionInfoListWithProof> for TransactionsProofsView {
1009    type Error = Error;
1010
1011    fn try_from(proof: &TransactionInfoListWithProof) -> Result<Self, Self::Error> {
1012        Ok(TransactionsProofsView {
1013            ledger_info_to_transaction_infos_proof: BytesView::new(bcs::to_bytes(
1014                &proof.ledger_info_to_transaction_infos_proof,
1015            )?),
1016            transaction_infos: BytesView::new(bcs::to_bytes(&proof.transaction_infos)?),
1017        })
1018    }
1019}
1020
1021impl TryFrom<&TransactionsProofsView> for TransactionInfoListWithProof {
1022    type Error = Error;
1023
1024    fn try_from(view: &TransactionsProofsView) -> Result<Self, Self::Error> {
1025        Ok(TransactionInfoListWithProof {
1026            ledger_info_to_transaction_infos_proof: bcs::from_bytes(
1027                &view.ledger_info_to_transaction_infos_proof,
1028            )?,
1029            transaction_infos: bcs::from_bytes(&view.transaction_infos)?,
1030        })
1031    }
1032}
1033
1034#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
1035pub struct AccountTransactionsWithProofView {
1036    pub serialized_txns_with_proofs: Vec<BytesView>,
1037}
1038
1039impl TryFrom<&AccountTransactionsWithProof> for AccountTransactionsWithProofView {
1040    type Error = Error;
1041
1042    fn try_from(txns: &AccountTransactionsWithProof) -> Result<Self, Self::Error> {
1043        Ok(Self {
1044            serialized_txns_with_proofs: txns
1045                .inner()
1046                .iter()
1047                .map(|txn_with_proof| Ok(BytesView::new(bcs::to_bytes(txn_with_proof)?)))
1048                .collect::<Result<Vec<_>>>()?,
1049        })
1050    }
1051}
1052
1053impl TryFrom<&AccountTransactionsWithProofView> for AccountTransactionsWithProof {
1054    type Error = Error;
1055
1056    fn try_from(view: &AccountTransactionsWithProofView) -> Result<Self, Self::Error> {
1057        Ok(Self::new(
1058            view.serialized_txns_with_proofs
1059                .iter()
1060                .map(|txn_bytes| bcs::from_bytes(txn_bytes.as_ref()))
1061                .collect::<Result<Vec<_>, _>>()?,
1062        ))
1063    }
1064}
1065
1066#[allow(clippy::large_enum_variant)]
1067#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
1068#[serde(tag = "type")]
1069pub enum TransactionDataView {
1070    #[serde(rename = "blockmetadata")]
1071    BlockMetadata { timestamp_usecs: u64 },
1072    #[serde(rename = "writeset")]
1073    WriteSet {},
1074    #[serde(rename = "user")]
1075    UserTransaction {
1076        sender: AccountAddress,
1077        signature_scheme: String,
1078        signature: BytesView,
1079        public_key: BytesView,
1080        #[serde(skip_serializing_if = "Option::is_none")]
1081        secondary_signers: Option<Vec<AccountAddress>>,
1082        #[serde(skip_serializing_if = "Option::is_none")]
1083        secondary_signature_schemes: Option<Vec<String>>,
1084        #[serde(skip_serializing_if = "Option::is_none")]
1085        secondary_signatures: Option<Vec<BytesView>>,
1086        #[serde(skip_serializing_if = "Option::is_none")]
1087        secondary_public_keys: Option<Vec<BytesView>>,
1088        sequence_number: u64,
1089        chain_id: u8,
1090        max_gas_amount: u64,
1091        gas_unit_price: u64,
1092        gas_currency: String,
1093        expiration_timestamp_secs: u64,
1094        script_hash: HashValue,
1095        script_bytes: BytesView,
1096        script: ScriptView,
1097    },
1098    #[serde(rename = "unknown")]
1099    #[serde(other)]
1100    UnknownTransaction,
1101}
1102
1103impl From<Transaction> for TransactionDataView {
1104    fn from(tx: Transaction) -> Self {
1105        match tx {
1106            Transaction::BlockMetadata(t) => TransactionDataView::BlockMetadata {
1107                timestamp_usecs: t.timestamp_usec(),
1108            },
1109            Transaction::GenesisTransaction(_) => TransactionDataView::WriteSet {},
1110            Transaction::UserTransaction(t) => {
1111                let script_hash = match t.payload() {
1112                    TransactionPayload::Script(s) => HashValue::sha3_256_of(s.code()),
1113                    _ => HashValue::zero(),
1114                };
1115
1116                let script_bytes: BytesView = match t.payload() {
1117                    TransactionPayload::Script(s) => bcs::to_bytes(s).unwrap_or_default(),
1118                    TransactionPayload::ScriptFunction(s) => bcs::to_bytes(s).unwrap_or_default(),
1119                    _ => vec![],
1120                }
1121                .into();
1122
1123                let script: ScriptView = match t.payload() {
1124                    TransactionPayload::Script(s) => ScriptView::from(s),
1125                    TransactionPayload::ScriptFunction(s) => ScriptView::from(s),
1126                    _ => ScriptView::unknown(),
1127                };
1128
1129                TransactionDataView::UserTransaction {
1130                    sender: t.sender(),
1131                    signature_scheme: t.authenticator().sender().scheme().to_string(),
1132                    signature: t.authenticator().sender().signature_bytes().into(),
1133                    public_key: t.authenticator().sender().public_key_bytes().into(),
1134                    secondary_signers: Some(t.authenticator().secondary_signer_addreses()),
1135                    secondary_signature_schemes: Some(
1136                        t.authenticator()
1137                            .secondary_signers()
1138                            .iter()
1139                            .map(|account_auth| account_auth.scheme().to_string())
1140                            .collect(),
1141                    ),
1142                    secondary_signatures: Some(
1143                        t.authenticator()
1144                            .secondary_signers()
1145                            .iter()
1146                            .map(|account_auth| account_auth.signature_bytes().into())
1147                            .collect(),
1148                    ),
1149                    secondary_public_keys: Some(
1150                        t.authenticator()
1151                            .secondary_signers()
1152                            .iter()
1153                            .map(|account_auth| account_auth.public_key_bytes().into())
1154                            .collect(),
1155                    ),
1156                    sequence_number: t.sequence_number(),
1157                    chain_id: t.chain_id().id(),
1158                    max_gas_amount: t.max_gas_amount(),
1159                    gas_unit_price: t.gas_unit_price(),
1160                    gas_currency: t.gas_currency_code().to_string(),
1161                    expiration_timestamp_secs: t.expiration_timestamp_secs(),
1162                    script_hash,
1163                    script_bytes,
1164                    script,
1165                }
1166            }
1167        }
1168    }
1169}
1170
1171#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
1172pub struct ScriptView {
1173    // script name / type
1174    pub r#type: String,
1175
1176    // script code bytes
1177    #[serde(skip_serializing_if = "Option::is_none")]
1178    pub code: Option<BytesView>,
1179    // script arguments, converted into string with type information
1180    #[serde(skip_serializing_if = "Option::is_none")]
1181    pub arguments: Option<Vec<String>>,
1182    // script function arguments, converted into hex encoded BCS bytes
1183    #[serde(skip_serializing_if = "Option::is_none")]
1184    pub arguments_bcs: Option<Vec<BytesView>>,
1185    // script type arguments, converted into string
1186    #[serde(skip_serializing_if = "Option::is_none")]
1187    pub type_arguments: Option<Vec<String>>,
1188
1189    // the following fields are legacy fields: maybe removed in the future
1190    // please move to use above fields
1191
1192    // peer_to_peer_transaction, other known script name or unknown
1193    // because of a bug, we never rendered mint_transaction
1194    // this is deprecated, please switch to use field `name` which is script name
1195
1196    // peer_to_peer_transaction
1197    #[serde(skip_serializing_if = "Option::is_none")]
1198    pub receiver: Option<AccountAddress>,
1199    #[serde(skip_serializing_if = "Option::is_none")]
1200    pub amount: Option<u64>,
1201    #[serde(skip_serializing_if = "Option::is_none")]
1202    pub currency: Option<String>,
1203    #[serde(skip_serializing_if = "Option::is_none")]
1204    pub metadata: Option<BytesView>,
1205    #[serde(skip_serializing_if = "Option::is_none")]
1206    pub metadata_signature: Option<BytesView>,
1207
1208    // Script functions
1209    // The address that the module is published under
1210    #[serde(skip_serializing_if = "Option::is_none")]
1211    pub module_address: Option<AccountAddress>,
1212    // The name of the module that the called function is defined in
1213    #[serde(skip_serializing_if = "Option::is_none")]
1214    pub module_name: Option<String>,
1215    // The (unqualified) name of the function being called.
1216    #[serde(skip_serializing_if = "Option::is_none")]
1217    pub function_name: Option<String>,
1218}
1219
1220impl ScriptView {
1221    pub fn unknown() -> Self {
1222        ScriptView {
1223            r#type: "unknown".to_string(),
1224            ..Default::default()
1225        }
1226    }
1227}
1228
1229impl From<&Script> for ScriptView {
1230    fn from(script: &Script) -> Self {
1231        let name = ScriptCall::decode(script)
1232            .map(|script_call| script_call.name().to_owned())
1233            .unwrap_or_else(|| "unknown".to_owned());
1234        let ty_args: Vec<String> = script
1235            .ty_args()
1236            .iter()
1237            .map(|type_tag| match type_tag {
1238                TypeTag::Struct(StructTag { module, .. }) => module.to_string(),
1239                tag => format!("{}", tag),
1240            })
1241            .collect();
1242        let mut view = ScriptView {
1243            r#type: name.clone(),
1244            code: Some(script.code().into()),
1245            arguments: Some(
1246                script
1247                    .args()
1248                    .iter()
1249                    .map(|arg| format!("{:?}", &arg))
1250                    .collect(),
1251            ),
1252            type_arguments: Some(ty_args.clone()),
1253            ..Default::default()
1254        };
1255
1256        // handle legacy fields, backward compatible
1257        if name == "peer_to_peer_with_metadata" {
1258            if let [TransactionArgument::Address(receiver), TransactionArgument::U64(amount), TransactionArgument::U8Vector(metadata), TransactionArgument::U8Vector(metadata_signature)] =
1259                script.args()
1260            {
1261                view.receiver = Some(*receiver);
1262                view.amount = Some(*amount);
1263                view.currency = Some(
1264                    ty_args
1265                        .get(0)
1266                        .unwrap_or(&"unknown_currency".to_string())
1267                        .to_string(),
1268                );
1269                view.metadata = Some(BytesView::new(metadata.as_ref()));
1270                view.metadata_signature = Some(BytesView::new(metadata_signature.as_ref()));
1271            }
1272        }
1273
1274        view
1275    }
1276}
1277
1278impl From<&ScriptFunction> for ScriptView {
1279    fn from(script: &ScriptFunction) -> Self {
1280        let ty_args: Vec<String> = script
1281            .ty_args()
1282            .iter()
1283            .map(|type_tag| match type_tag {
1284                TypeTag::Struct(StructTag { module, .. }) => module.to_string(),
1285                tag => format!("{}", tag),
1286            })
1287            .collect();
1288        ScriptView {
1289            r#type: "script_function".to_string(),
1290            module_address: Some(*script.module().address()),
1291            module_name: Some(script.module().name().to_string()),
1292            function_name: Some(script.function().to_string()),
1293            arguments_bcs: Some(
1294                script
1295                    .args()
1296                    .iter()
1297                    .map(|arg| BytesView::from(arg.as_ref()))
1298                    .collect(),
1299            ),
1300            type_arguments: Some(ty_args),
1301            ..Default::default()
1302        }
1303    }
1304}
1305
1306impl From<AccountRole> for AccountRoleView {
1307    fn from(role: AccountRole) -> Self {
1308        match role {
1309            AccountRole::Unknown => AccountRoleView::Unknown,
1310            AccountRole::ChildVASP(child_vasp) => AccountRoleView::ChildVASP {
1311                parent_vasp_address: child_vasp.parent_vasp_addr(),
1312            },
1313            AccountRole::ParentVASP {
1314                vasp,
1315                credential,
1316                vasp_domains,
1317            } => {
1318                let domains = vasp_domains.map(|vasp_domains| vasp_domains.get_domains_list());
1319                AccountRoleView::ParentVASP {
1320                    human_name: credential.human_name().to_string(),
1321                    base_url: credential.base_url().to_string(),
1322                    expiration_time: credential.expiration_date(),
1323                    compliance_key: BytesView::from(credential.compliance_public_key()),
1324                    num_children: vasp.num_children(),
1325                    compliance_key_rotation_events_key: *credential
1326                        .compliance_key_rotation_events()
1327                        .key(),
1328                    base_url_rotation_events_key: *credential.base_url_rotation_events().key(),
1329                    vasp_domains: domains,
1330                }
1331            }
1332            AccountRole::DesignatedDealer {
1333                dd_credential,
1334                preburn_balances,
1335                designated_dealer,
1336            } => {
1337                let (preburn_balances, preburn_queues) =
1338                    AccountRoleView::convert_preburn_balances(preburn_balances);
1339                AccountRoleView::DesignatedDealer {
1340                    human_name: dd_credential.human_name().to_string(),
1341                    base_url: dd_credential.base_url().to_string(),
1342                    expiration_time: dd_credential.expiration_date(),
1343                    compliance_key: BytesView::from(dd_credential.compliance_public_key()),
1344                    preburn_balances,
1345                    received_mint_events_key: *designated_dealer.received_mint_events().key(),
1346                    compliance_key_rotation_events_key: *dd_credential
1347                        .compliance_key_rotation_events()
1348                        .key(),
1349                    base_url_rotation_events_key: *dd_credential.base_url_rotation_events().key(),
1350                    preburn_queues,
1351                }
1352            }
1353            AccountRole::TreasuryCompliance {
1354                vasp_domain_manager,
1355            } => AccountRoleView::TreasuryCompliance {
1356                vasp_domain_events_key: Some(*vasp_domain_manager.vasp_domain_events().key()),
1357            },
1358        }
1359    }
1360}
1361
1362#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
1363pub struct CurrencyInfoView {
1364    pub code: String,
1365    pub scaling_factor: u64,
1366    pub fractional_part: u64,
1367    pub to_xdx_exchange_rate: f32,
1368    pub mint_events_key: EventKey,
1369    pub burn_events_key: EventKey,
1370    pub preburn_events_key: EventKey,
1371    pub cancel_burn_events_key: EventKey,
1372    pub exchange_rate_update_events_key: EventKey,
1373}
1374
1375impl From<&CurrencyInfoResource> for CurrencyInfoView {
1376    fn from(info: &CurrencyInfoResource) -> CurrencyInfoView {
1377        CurrencyInfoView {
1378            code: info.currency_code().to_string(),
1379            scaling_factor: info.scaling_factor(),
1380            fractional_part: info.fractional_part(),
1381            to_xdx_exchange_rate: info.exchange_rate(),
1382            mint_events_key: *info.mint_events().key(),
1383            burn_events_key: *info.burn_events().key(),
1384            preburn_events_key: *info.preburn_events().key(),
1385            cancel_burn_events_key: *info.cancel_burn_events().key(),
1386            exchange_rate_update_events_key: *info.exchange_rate_update_events().key(),
1387        }
1388    }
1389}
1390
1391#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
1392pub struct StateProofView {
1393    pub ledger_info_with_signatures: BytesView,
1394    pub epoch_change_proof: BytesView,
1395    pub ledger_consistency_proof: BytesView,
1396}
1397
1398impl TryFrom<&StateProof> for StateProofView {
1399    type Error = Error;
1400
1401    fn try_from(proof: &StateProof) -> Result<Self, Self::Error> {
1402        Ok(Self {
1403            ledger_info_with_signatures: BytesView::new(bcs::to_bytes(
1404                proof.latest_ledger_info_w_sigs(),
1405            )?),
1406            epoch_change_proof: BytesView::new(bcs::to_bytes(proof.epoch_changes())?),
1407            ledger_consistency_proof: BytesView::new(bcs::to_bytes(&proof.consistency_proof())?),
1408        })
1409    }
1410}
1411
1412impl TryFrom<&StateProofView> for StateProof {
1413    type Error = Error;
1414
1415    fn try_from(view: &StateProofView) -> Result<Self, Self::Error> {
1416        Ok(Self::new(
1417            bcs::from_bytes(view.ledger_info_with_signatures.inner())?,
1418            bcs::from_bytes(view.epoch_change_proof.inner())?,
1419            bcs::from_bytes(view.ledger_consistency_proof.inner())?,
1420        ))
1421    }
1422}
1423
1424#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
1425pub struct AccumulatorConsistencyProofView {
1426    pub ledger_consistency_proof: BytesView,
1427}
1428
1429impl TryFrom<&AccumulatorConsistencyProof> for AccumulatorConsistencyProofView {
1430    type Error = Error;
1431
1432    fn try_from(proof: &AccumulatorConsistencyProof) -> Result<Self, Self::Error> {
1433        Ok(Self {
1434            ledger_consistency_proof: BytesView::new(bcs::to_bytes(proof)?),
1435        })
1436    }
1437}
1438
1439impl TryFrom<&AccumulatorConsistencyProofView> for AccumulatorConsistencyProof {
1440    type Error = Error;
1441
1442    fn try_from(view: &AccumulatorConsistencyProofView) -> Result<Self, Self::Error> {
1443        Ok(bcs::from_bytes(view.ledger_consistency_proof.as_ref())?)
1444    }
1445}
1446
1447#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
1448pub struct AccountStateWithProofView {
1449    pub version: u64,
1450    #[serde(skip_serializing_if = "Option::is_none")]
1451    pub blob: Option<BytesView>,
1452    pub proof: AccountStateProofView,
1453}
1454
1455impl TryFrom<AccountStateWithProof> for AccountStateWithProofView {
1456    type Error = Error;
1457
1458    fn try_from(
1459        account_state_with_proof: AccountStateWithProof,
1460    ) -> Result<AccountStateWithProofView, Error> {
1461        let blob = if let Some(account_blob) = account_state_with_proof.blob {
1462            Some(BytesView::new(bcs::to_bytes(&account_blob)?))
1463        } else {
1464            None
1465        };
1466        Ok(AccountStateWithProofView {
1467            version: account_state_with_proof.version,
1468            blob,
1469            proof: AccountStateProofView::try_from(account_state_with_proof.proof)?,
1470        })
1471    }
1472}
1473
1474impl TryFrom<&AccountStateWithProofView> for AccountStateWithProof {
1475    type Error = Error;
1476
1477    fn try_from(
1478        account_state_with_proof_view: &AccountStateWithProofView,
1479    ) -> Result<AccountStateWithProof, Self::Error> {
1480        let blob = if let Some(blob_view) = &account_state_with_proof_view.blob {
1481            Some(bcs::from_bytes(blob_view.as_ref())?)
1482        } else {
1483            None
1484        };
1485        let version = account_state_with_proof_view.version;
1486        let proof = AccountStateProof::try_from(&account_state_with_proof_view.proof)?;
1487        Ok(AccountStateWithProof::new(version, blob, proof))
1488    }
1489}
1490
1491#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
1492pub struct AccountStateProofView {
1493    pub ledger_info_to_transaction_info_proof: BytesView,
1494    pub transaction_info: BytesView,
1495    pub transaction_info_to_account_proof: BytesView,
1496}
1497
1498impl TryFrom<AccountStateProof> for AccountStateProofView {
1499    type Error = Error;
1500
1501    fn try_from(account_state_proof: AccountStateProof) -> Result<AccountStateProofView, Error> {
1502        Ok(AccountStateProofView {
1503            ledger_info_to_transaction_info_proof: BytesView::new(bcs::to_bytes(
1504                account_state_proof
1505                    .transaction_info_with_proof()
1506                    .ledger_info_to_transaction_info_proof(),
1507            )?),
1508            transaction_info: BytesView::new(bcs::to_bytes(
1509                account_state_proof
1510                    .transaction_info_with_proof()
1511                    .transaction_info(),
1512            )?),
1513            transaction_info_to_account_proof: BytesView::new(bcs::to_bytes(
1514                account_state_proof.transaction_info_to_account_proof(),
1515            )?),
1516        })
1517    }
1518}
1519
1520impl TryFrom<&AccountStateProofView> for AccountStateProof {
1521    type Error = Error;
1522
1523    fn try_from(
1524        account_state_proof_view: &AccountStateProofView,
1525    ) -> Result<AccountStateProof, Self::Error> {
1526        let ledger_info_to_transaction_info_proof: TransactionAccumulatorProof = bcs::from_bytes(
1527            account_state_proof_view
1528                .ledger_info_to_transaction_info_proof
1529                .as_ref(),
1530        )?;
1531        let transaction_info = bcs::from_bytes(account_state_proof_view.transaction_info.as_ref())?;
1532        let transaction_info_with_proof =
1533            TransactionInfoWithProof::new(ledger_info_to_transaction_info_proof, transaction_info);
1534        let transaction_info_to_account_proof: SparseMerkleProof<AccountStateBlob> =
1535            bcs::from_bytes(
1536                account_state_proof_view
1537                    .transaction_info_to_account_proof
1538                    .as_ref(),
1539            )?;
1540        Ok(AccountStateProof::new(
1541            transaction_info_with_proof,
1542            transaction_info_to_account_proof,
1543        ))
1544    }
1545}
1546
1547#[cfg(test)]
1548mod tests {
1549    use crate::views::{
1550        AccountRoleView, AccountStateWithProofView, AmountView, EventDataView, MetadataView,
1551        PreburnWithMetadataView, TransactionDataView, VMStatusView,
1552    };
1553    use diem_types::{contract_event::ContractEvent, event::EventKey};
1554    use move_core_types::language_storage::TypeTag;
1555    use serde_json::json;
1556    use std::{convert::TryInto, str::FromStr};
1557
1558    #[test]
1559    fn test_unknown_event_data() {
1560        let data = hex::decode("0000000000000000000000000000000000000000000000dd").unwrap();
1561        let ev = ContractEvent::new(
1562            EventKey::from_str("0000000000000000000000000000000000000000000000dd").unwrap(),
1563            0,
1564            TypeTag::Bool,
1565            data.clone(),
1566        );
1567        if let EventDataView::Unknown { bytes } = ev.try_into().unwrap() {
1568            assert_eq!(bytes.unwrap(), data.into());
1569        } else {
1570            panic!("expect unknown event data");
1571        }
1572    }
1573
1574    #[test]
1575    fn test_serialize_preburn_with_metadata_view() {
1576        let view = PreburnWithMetadataView {
1577            preburn: AmountView {
1578                amount: 1,
1579                currency: "XUS".to_string(),
1580            },
1581            metadata: None,
1582        };
1583        let value = serde_json::to_value(&view).unwrap();
1584        assert_eq!(
1585            value,
1586            json!({
1587                "preburn": {
1588                    "amount": 1,
1589                    "currency": "XUS"
1590                }
1591            })
1592        );
1593    }
1594
1595    #[test]
1596    fn account_role_view_unknown() {
1597        let json = json!({
1598            "type": "NewVariant",
1599            "new-field": 5,
1600        });
1601
1602        let actual: AccountRoleView = serde_json::from_value(json).unwrap();
1603        let expected = AccountRoleView::Unknown;
1604        assert_eq!(actual, expected);
1605    }
1606
1607    #[test]
1608    fn event_data_view_unknown() {
1609        let json = json!({
1610            "type": "NewVariant",
1611            "new-field": 5,
1612        });
1613
1614        let actual: EventDataView = serde_json::from_value(json).unwrap();
1615        let expected = EventDataView::UnknownToClient;
1616        assert_eq!(actual, expected);
1617    }
1618
1619    #[test]
1620    fn vm_status_view_unknown() {
1621        let json = json!({
1622            "type": "NewVariant",
1623            "new-field": 5,
1624        });
1625
1626        let actual: VMStatusView = serde_json::from_value(json).unwrap();
1627        let expected = VMStatusView::Unknown;
1628        assert_eq!(actual, expected);
1629    }
1630
1631    #[test]
1632    fn transaction_data_view_unknown() {
1633        let json = json!({
1634            "type": "NewVariant",
1635            "new-field": 5,
1636        });
1637
1638        let actual: TransactionDataView = serde_json::from_value(json).unwrap();
1639        let expected = TransactionDataView::UnknownTransaction;
1640        assert_eq!(actual, expected);
1641    }
1642
1643    #[test]
1644    fn should_not_serailize_null_fields_in_metadata_view() {
1645        let json = json!({
1646            "version": 64,
1647            "accumulator_root_hash": "cd66d0003f04f14f9c5433549935ac7f49a5c593f7dd56c0339768f3113ecdd9",
1648            "timestamp": 123,
1649            "chain_id": 8
1650        });
1651        let actual: MetadataView = serde_json::from_value(json.clone()).unwrap();
1652        let serialized = serde_json::to_value(actual).unwrap();
1653
1654        assert_eq!(serialized, json);
1655    }
1656
1657    #[test]
1658    fn should_not_serailize_null_fields_in_parent_vasp_account_role_view() {
1659        let json = json!({
1660            "base_url": "",
1661            "base_url_rotation_events_key": "0200000000000000000000000000000000000000000000dd",
1662            "compliance_key": "",
1663            "compliance_key_rotation_events_key": "0100000000000000000000000000000000000000000000dd",
1664            "expiration_time": 18446744073709551615_u64,
1665            "human_name": "name",
1666            "num_children": 0,
1667            "type": "parent_vasp",
1668        });
1669
1670        let actual: AccountRoleView = serde_json::from_value(json.clone()).unwrap();
1671        let serialized = serde_json::to_value(actual).unwrap();
1672
1673        assert_eq!(serialized, json);
1674    }
1675
1676    #[test]
1677    fn should_not_serailize_null_fields_in_dd_account_role_view() {
1678        let json = json!({
1679            "base_url": "",
1680            "base_url_rotation_events_key": "0200000000000000000000000000000000000000000000dd",
1681            "compliance_key": "",
1682            "compliance_key_rotation_events_key": "0100000000000000000000000000000000000000000000dd",
1683            "expiration_time": 18446744073709551615_u64,
1684            "human_name": "name",
1685            "type": "designated_dealer",
1686            "preburn_balances": [],
1687            "received_mint_events_key": "0300000000000000000000000000000000000000000000dd",
1688        });
1689
1690        let actual: AccountRoleView = serde_json::from_value(json.clone()).unwrap();
1691        let serialized = serde_json::to_value(actual).unwrap();
1692
1693        assert_eq!(serialized, json);
1694    }
1695
1696    #[test]
1697    fn should_not_serailize_null_fields_in_unknown_event_data_view() {
1698        let json = json!({
1699            "type": "unknown"
1700        });
1701
1702        let actual: EventDataView = serde_json::from_value(json.clone()).unwrap();
1703        let serialized = serde_json::to_value(actual).unwrap();
1704
1705        assert_eq!(serialized, json);
1706    }
1707
1708    #[test]
1709    fn should_not_serialize_null_fields_in_account_state_with_proof_view() {
1710        let json = json!({
1711            "version": 111,
1712            "proof": {
1713                "ledger_info_to_transaction_info_proof": "",
1714                "transaction_info": "",
1715                "transaction_info_to_account_proof": ""
1716            }
1717        });
1718
1719        let actual: AccountStateWithProofView = serde_json::from_value(json.clone()).unwrap();
1720        let serialized = serde_json::to_value(actual).unwrap();
1721
1722        assert_eq!(serialized, json);
1723    }
1724
1725    #[test]
1726    fn should_not_serialize_null_fields_in_vm_status_move_abort_view() {
1727        let json = json!({
1728            "type": "move_abort",
1729            "location": "loc",
1730            "abort_code": 12,
1731        });
1732
1733        let actual: VMStatusView = serde_json::from_value(json.clone()).unwrap();
1734        let serialized = serde_json::to_value(actual).unwrap();
1735
1736        assert_eq!(serialized, json);
1737    }
1738}