1use 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 #[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 #[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 removed: bool,
423 domain: DiemIdVaspDomainIdentifier,
425 address: AccountAddress,
427 },
428 #[serde(rename = "unknown")]
429 Unknown {
430 #[serde(skip_serializing_if = "Option::is_none")]
431 bytes: Option<BytesView>,
432 },
433
434 #[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 pub r#type: String,
1175
1176 #[serde(skip_serializing_if = "Option::is_none")]
1178 pub code: Option<BytesView>,
1179 #[serde(skip_serializing_if = "Option::is_none")]
1181 pub arguments: Option<Vec<String>>,
1182 #[serde(skip_serializing_if = "Option::is_none")]
1184 pub arguments_bcs: Option<Vec<BytesView>>,
1185 #[serde(skip_serializing_if = "Option::is_none")]
1187 pub type_arguments: Option<Vec<String>>,
1188
1189 #[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 #[serde(skip_serializing_if = "Option::is_none")]
1211 pub module_address: Option<AccountAddress>,
1212 #[serde(skip_serializing_if = "Option::is_none")]
1214 pub module_name: Option<String>,
1215 #[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 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}