1use std::{
6 collections::BTreeMap,
7 fmt::{Debug, Display, Formatter},
8 mem::size_of,
9 sync::Arc,
10};
11
12use iota_protocol_config::ProtocolConfig;
13use iota_sdk_types::{
14 Address, MoveObjectType, ObjectData, ObjectId, Owner, StructTag, TypeTag,
15 move_package::MovePackage,
16};
17pub use iota_sdk_types::{MoveStruct as MoveObject, Object as ObjectInner};
18use move_binary_format::CompiledModule;
19use move_bytecode_utils::{layout::TypeLayoutBuilder, module_cache::GetModule};
20use move_core_types::annotated_value::{MoveStruct, MoveStructLayout, MoveTypeLayout, MoveValue};
21use serde::{Deserialize, Serialize};
22
23use self::{balance_traversal::BalanceTraversal, bounded_visitor::BoundedVisitor};
24use crate::{
25 balance::Balance,
26 base_types::{ObjectRef, SequenceNumber, TransactionDigest},
27 coin::{Coin, CoinMetadata, TreasuryCap},
28 crypto::deterministic_random_account_key,
29 error::{
30 ExecutionError, ExecutionErrorKind, IotaError, IotaResult, UserInputError, UserInputResult,
31 },
32 gas_coin::{GAS, GasCoin},
33 iota_sdk_types_conversions::type_tag_sdk_to_core,
34 layout_resolver::LayoutResolver,
35 move_package::MovePackageExt,
36 timelock::timelock::TimeLock,
37};
38
39mod balance_traversal;
40pub mod bounded_visitor;
41pub mod option_visitor;
42
43pub const GAS_VALUE_FOR_TESTING: u64 = 300_000_000_000_000;
44pub const OBJECT_START_VERSION: SequenceNumber = SequenceNumber::from_u64(1);
45
46pub const ID_END_INDEX: usize = ObjectId::LENGTH;
48
49mod move_object_ext {
50 pub trait Sealed {}
51 impl Sealed for super::MoveObject {}
52}
53
54pub trait MoveObjectExt: Sized + move_object_ext::Sealed {
55 fn new_from_execution(
56 tag: StructTag,
57 version: SequenceNumber,
58 contents: Vec<u8>,
59 protocol_config: &ProtocolConfig,
60 ) -> Result<Self, ExecutionError>;
61 fn new_from_execution_with_limit(
62 tag: StructTag,
63 version: SequenceNumber,
64 contents: Vec<u8>,
65 max_move_object_size: u64,
66 ) -> Result<Self, ExecutionError>;
67 fn new_gas_coin(version: SequenceNumber, id: ObjectId, value: u64) -> Self;
68 fn new_coin(coin_type: TypeTag, version: SequenceNumber, id: ObjectId, value: u64) -> Self;
69 fn get_coin_value_unchecked(&self) -> u64;
70 fn set_coin_value_unchecked(&mut self, value: u64);
71 fn set_clock_timestamp_ms_unchecked(&mut self, timestamp_ms: u64);
72 fn update_contents(
73 &mut self,
74 new_contents: Vec<u8>,
75 protocol_config: &ProtocolConfig,
76 ) -> Result<(), ExecutionError>;
77 fn update_contents_with_limit(
78 &mut self,
79 new_contents: Vec<u8>,
80 max_move_object_size: u64,
81 ) -> Result<(), ExecutionError>;
82 fn increment_version_to(&mut self, next: SequenceNumber);
83 fn decrement_version_to(&mut self, prev: SequenceNumber);
84 fn get_layout(&self, resolver: &impl GetModule) -> Result<MoveStructLayout, IotaError>;
85 fn get_struct_layout_from_struct_tag(
86 struct_tag: StructTag,
87 resolver: &impl GetModule,
88 ) -> Result<MoveStructLayout, IotaError>;
89 fn to_move_struct(&self, layout: &MoveStructLayout) -> Result<MoveStruct, IotaError>;
90 fn object_size_for_gas_metering(&self) -> usize;
91 fn get_total_iota(&self, layout_resolver: &mut dyn LayoutResolver) -> Result<u64, IotaError>;
92 fn get_coin_balances(
93 &self,
94 layout_resolver: &mut dyn LayoutResolver,
95 ) -> Result<BTreeMap<TypeTag, u64>, IotaError>;
96}
97
98impl MoveObjectExt for MoveObject {
99 fn new_from_execution(
102 tag: StructTag,
103 version: SequenceNumber,
104 contents: Vec<u8>,
105 protocol_config: &ProtocolConfig,
106 ) -> Result<Self, ExecutionError> {
107 Self::new_from_execution_with_limit(
108 tag,
109 version,
110 contents,
111 protocol_config.max_move_object_size(),
112 )
113 }
114
115 fn new_from_execution_with_limit(
118 tag: StructTag,
119 version: SequenceNumber,
120 contents: Vec<u8>,
121 max_move_object_size: u64,
122 ) -> Result<Self, ExecutionError> {
123 if contents.len() as u64 > max_move_object_size {
124 return Err(ExecutionError::from_kind(
125 ExecutionErrorKind::ObjectTooBig {
126 object_size: contents.len() as u64,
127 max_object_size: max_move_object_size,
128 },
129 ));
130 }
131 Self::new(tag.into(), version, contents).map_err(ExecutionError::invariant_violation)
132 }
133
134 fn new_gas_coin(version: SequenceNumber, id: ObjectId, value: u64) -> Self {
135 Self::new_from_execution_with_limit(
138 StructTag::new_gas_coin(),
139 version,
140 GasCoin::new(id, value).to_bcs_bytes(),
141 256,
142 )
143 .unwrap()
144 }
145
146 fn new_coin(coin_type: TypeTag, version: SequenceNumber, id: ObjectId, value: u64) -> Self {
147 Self::new_from_execution_with_limit(
150 StructTag::new_coin(coin_type),
151 version,
152 Coin::new(id, value).to_bcs_bytes(),
153 256,
154 )
155 .unwrap()
156 }
157
158 fn get_coin_value_unchecked(&self) -> u64 {
163 debug_assert!(self.object_type().is_coin());
164 debug_assert!(self.contents().len() == 40);
166
167 u64::from_le_bytes(<[u8; 8]>::try_from(&self.contents()[ID_END_INDEX..]).unwrap())
169 }
170
171 fn set_coin_value_unchecked(&mut self, value: u64) {
177 debug_assert!(self.object_type().is_coin());
178 debug_assert!(self.contents().len() == 40);
180
181 let mut new_contents = self.contents().to_vec();
182 new_contents[ID_END_INDEX..].copy_from_slice(&value.to_le_bytes());
183 self.set_contents(new_contents).unwrap();
184 }
185
186 fn set_clock_timestamp_ms_unchecked(&mut self, timestamp_ms: u64) {
192 debug_assert!(self.struct_tag().is_clock());
193 debug_assert!(self.contents().len() == 40);
195
196 let mut new_contents = self.contents().to_vec();
197 new_contents[ID_END_INDEX..].copy_from_slice(×tamp_ms.to_le_bytes());
198 self.set_contents(new_contents).unwrap();
199 }
200
201 fn update_contents(
203 &mut self,
204 new_contents: Vec<u8>,
205 protocol_config: &ProtocolConfig,
206 ) -> Result<(), ExecutionError> {
207 self.update_contents_with_limit(new_contents, protocol_config.max_move_object_size())
208 }
209
210 fn update_contents_with_limit(
211 &mut self,
212 new_contents: Vec<u8>,
213 max_move_object_size: u64,
214 ) -> Result<(), ExecutionError> {
215 if new_contents.len() as u64 > max_move_object_size {
216 return Err(ExecutionError::from_kind(
217 ExecutionErrorKind::ObjectTooBig {
218 object_size: new_contents.len() as u64,
219 max_object_size: max_move_object_size,
220 },
221 ));
222 }
223
224 #[cfg(debug_assertions)]
225 let old_id = self.id();
226
227 self.set_contents(new_contents)
228 .map_err(ExecutionError::invariant_violation)?;
229
230 #[cfg(debug_assertions)]
232 debug_assert_eq!(self.id(), old_id);
233
234 Ok(())
235 }
236
237 fn increment_version_to(&mut self, next: SequenceNumber) {
240 debug_assert!(
241 self.version() < next,
242 "Not an increment: {} to {next}",
243 self.version()
244 );
245 self.set_version(next);
246 }
247
248 fn decrement_version_to(&mut self, prev: SequenceNumber) {
250 debug_assert!(
251 prev < self.version(),
252 "Not a decrement: {} to {prev}",
253 self.version()
254 );
255 self.set_version(prev);
256 }
257
258 fn get_layout(&self, resolver: &impl GetModule) -> Result<MoveStructLayout, IotaError> {
264 Self::get_struct_layout_from_struct_tag(self.struct_tag().clone(), resolver)
265 }
266
267 fn get_struct_layout_from_struct_tag(
268 struct_tag: StructTag,
269 resolver: &impl GetModule,
270 ) -> Result<MoveStructLayout, IotaError> {
271 let type_ = TypeTag::Struct(Box::new(struct_tag));
272 let layout = TypeLayoutBuilder::build_with_types(&type_tag_sdk_to_core(&type_), resolver)
273 .map_err(|e| IotaError::ObjectSerialization {
274 error: e.to_string(),
275 })?;
276 match layout {
277 MoveTypeLayout::Struct(l) => Ok(*l),
278 _ => unreachable!(
279 "We called build_with_types on Struct type, should get a struct layout"
280 ),
281 }
282 }
283
284 fn to_move_struct(&self, layout: &MoveStructLayout) -> Result<MoveStruct, IotaError> {
286 BoundedVisitor::deserialize_struct(self.contents(), layout).map_err(|e| {
287 IotaError::ObjectSerialization {
288 error: e.to_string(),
289 }
290 })
291 }
292
293 fn object_size_for_gas_metering(&self) -> usize {
298 let serialized_type_tag_size =
299 bcs::serialized_size(self.object_type()).expect("Serializing type tag should not fail");
300 self.contents().len() + serialized_type_tag_size + 8
302 }
303
304 fn get_total_iota(&self, layout_resolver: &mut dyn LayoutResolver) -> Result<u64, IotaError> {
307 let balances = self.get_coin_balances(layout_resolver)?;
308 Ok(balances.get(&GAS::type_tag()).copied().unwrap_or(0))
309 }
310
311 fn get_coin_balances(
313 &self,
314 layout_resolver: &mut dyn LayoutResolver,
315 ) -> Result<BTreeMap<TypeTag, u64>, IotaError> {
316 if let Some(type_tag) = self.object_type().coin_type_opt() {
318 let balance = self.get_coin_value_unchecked();
319 Ok(if balance > 0 {
320 BTreeMap::from([(type_tag.clone(), balance)])
321 } else {
322 BTreeMap::default()
323 })
324 } else {
325 let layout = layout_resolver.get_annotated_layout(self.struct_tag())?;
326
327 let mut traversal = BalanceTraversal::default();
328 MoveValue::visit_deserialize(self.contents(), &layout.into_layout(), &mut traversal)
329 .map_err(|e| IotaError::ObjectSerialization {
330 error: e.to_string(),
331 })?;
332
333 Ok(traversal.finish())
334 }
335 }
336}
337
338#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
339#[serde(from = "ObjectInner")]
340pub struct Object(Arc<ObjectInner>);
341
342impl From<ObjectInner> for Object {
343 fn from(inner: ObjectInner) -> Self {
344 Self(Arc::new(inner))
345 }
346}
347
348impl Object {
349 pub fn into_inner(self) -> ObjectInner {
350 match Arc::try_unwrap(self.0) {
351 Ok(inner) => inner,
352 Err(inner_arc) => (*inner_arc).clone(),
353 }
354 }
355
356 pub fn as_inner(&self) -> &ObjectInner {
357 &self.0
358 }
359
360 pub fn new_from_genesis(
361 data: ObjectData,
362 owner: Owner,
363 previous_transaction: TransactionDigest,
364 ) -> Self {
365 ObjectInner {
366 data,
367 owner,
368 previous_transaction,
369 storage_rebate: 0,
370 }
371 .into()
372 }
373
374 pub fn new_move(o: MoveObject, owner: Owner, previous_transaction: TransactionDigest) -> Self {
376 ObjectInner {
377 data: ObjectData::Struct(o),
378 owner,
379 previous_transaction,
380 storage_rebate: 0,
381 }
382 .into()
383 }
384
385 pub fn new_package_from_data(
386 data: ObjectData,
387 previous_transaction: TransactionDigest,
388 ) -> Self {
389 ObjectInner {
390 data,
391 owner: Owner::Immutable,
392 previous_transaction,
393 storage_rebate: 0,
394 }
395 .into()
396 }
397
398 pub fn new_from_package(package: MovePackage, previous_transaction: TransactionDigest) -> Self {
400 Self::new_package_from_data(ObjectData::Package(package), previous_transaction)
401 }
402
403 pub fn new_package<'p>(
404 modules: &[CompiledModule],
405 previous_transaction: TransactionDigest,
406 protocol_config: &ProtocolConfig,
407 dependencies: impl IntoIterator<Item = &'p MovePackage>,
408 ) -> Result<Self, ExecutionError> {
409 Ok(Self::new_package_from_data(
410 ObjectData::Package(MovePackage::new_initial(
411 modules,
412 protocol_config,
413 dependencies,
414 )?),
415 previous_transaction,
416 ))
417 }
418
419 pub fn new_upgraded_package<'p>(
420 previous_package: &MovePackage,
421 new_package_id: ObjectId,
422 modules: &[CompiledModule],
423 previous_transaction: TransactionDigest,
424 protocol_config: &ProtocolConfig,
425 dependencies: impl IntoIterator<Item = &'p MovePackage>,
426 ) -> Result<Self, ExecutionError> {
427 Ok(Self::new_package_from_data(
428 ObjectData::Package(previous_package.new_upgraded(
429 new_package_id,
430 modules,
431 protocol_config,
432 dependencies,
433 )?),
434 previous_transaction,
435 ))
436 }
437
438 pub fn new_package_for_testing(
439 modules: &[CompiledModule],
440 previous_transaction: TransactionDigest,
441 dependencies: impl IntoIterator<Item = MovePackage>,
442 ) -> Result<Self, ExecutionError> {
443 let dependencies: Vec<_> = dependencies.into_iter().collect();
444 let config = ProtocolConfig::get_for_max_version_UNSAFE();
445 Self::new_package(modules, previous_transaction, &config, &dependencies)
446 }
447
448 pub fn new_system_package(
451 modules: &[CompiledModule],
452 version: SequenceNumber,
453 dependencies: Vec<ObjectId>,
454 previous_transaction: TransactionDigest,
455 ) -> Self {
456 let ret = Self::new_package_from_data(
457 ObjectData::Package(MovePackage::new_system(version, modules, dependencies)),
458 previous_transaction,
459 );
460
461 #[cfg(not(msim))]
462 assert!(ret.is_system_package());
463
464 ret
465 }
466}
467
468impl std::ops::Deref for Object {
469 type Target = ObjectInner;
470 fn deref(&self) -> &Self::Target {
471 &self.0
472 }
473}
474
475impl std::ops::DerefMut for Object {
476 fn deref_mut(&mut self) -> &mut Self::Target {
477 Arc::make_mut(&mut self.0)
478 }
479}
480
481impl Object {
482 pub fn type_(&self) -> Option<&MoveObjectType> {
483 self.data.opt_object_type()
484 }
485
486 pub fn is_coin(&self) -> bool {
487 if let Some(move_object) = self.data.as_opt_struct() {
488 move_object.struct_tag().is_coin()
489 } else {
490 false
491 }
492 }
493
494 pub fn as_coin_maybe(&self) -> Option<Coin> {
497 if let Some(move_object) = self.data.as_opt_struct() {
498 let coin: Coin = bcs::from_bytes(move_object.contents()).ok()?;
499 Some(coin)
500 } else {
501 None
502 }
503 }
504
505 pub fn as_timelock_balance_maybe(&self) -> Option<TimeLock<Balance>> {
506 if let Some(move_object) = self.data.as_opt_struct() {
507 Some(TimeLock::from_bcs_bytes(move_object.contents()).ok()?)
508 } else {
509 None
510 }
511 }
512
513 pub fn get_coin_value_unchecked(&self) -> u64 {
518 self.data
519 .as_opt_struct()
520 .unwrap()
521 .get_coin_value_unchecked()
522 }
523
524 pub fn object_size_for_gas_metering(&self) -> usize {
529 let meta_data_size = size_of::<Owner>() + size_of::<TransactionDigest>() + size_of::<u64>();
530 let data_size = match &self.data {
531 ObjectData::Struct(m) => m.object_size_for_gas_metering(),
532 ObjectData::Package(p) => p.size(),
533 };
534 meta_data_size + data_size
535 }
536
537 pub fn get_layout(
543 &self,
544 resolver: &impl GetModule,
545 ) -> Result<Option<MoveStructLayout>, IotaError> {
546 match &self.data {
547 ObjectData::Struct(m) => Ok(Some(m.get_layout(resolver)?)),
548 ObjectData::Package(_) => Ok(None),
549 }
550 }
551
552 pub fn get_move_template_type(&self) -> IotaResult<TypeTag> {
556 let move_struct = self.data.opt_struct_tag().ok_or_else(|| IotaError::Type {
557 error: "Object must be a Move object".to_owned(),
558 })?;
559 fp_ensure!(
560 move_struct.type_params().len() == 1,
561 IotaError::Type {
562 error: "Move object struct must have one type parameter".to_owned()
563 }
564 );
565 let type_tag = move_struct.type_params()[0].clone();
567 Ok(type_tag)
568 }
569}
570
571impl Object {
573 pub fn get_total_iota(
576 &self,
577 layout_resolver: &mut dyn LayoutResolver,
578 ) -> Result<u64, IotaError> {
579 Ok(self.storage_rebate
580 + match &self.data {
581 ObjectData::Struct(m) => m.get_total_iota(layout_resolver)?,
582 ObjectData::Package(_) => 0,
583 })
584 }
585
586 pub fn immutable_with_id_for_testing(id: ObjectId) -> Self {
587 let data = ObjectData::Struct(
588 MoveObject::new(
589 StructTag::new_gas_coin().into(),
590 OBJECT_START_VERSION,
591 GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
592 )
593 .unwrap(),
594 );
595 ObjectInner {
596 owner: Owner::Immutable,
597 data,
598 previous_transaction: TransactionDigest::GENESIS_MARKER,
599 storage_rebate: 0,
600 }
601 .into()
602 }
603
604 pub fn immutable_for_testing() -> Self {
605 thread_local! {
606 static IMMUTABLE_OBJECT_ID: ObjectId = ObjectId::random();
607 }
608
609 Self::immutable_with_id_for_testing(IMMUTABLE_OBJECT_ID.with(|id| *id))
610 }
611
612 pub fn shared_for_testing() -> Object {
614 let id = ObjectId::random();
615 let obj = MoveObject::new_gas_coin(OBJECT_START_VERSION, id, 10);
616 let owner = Owner::Shared(obj.version());
617 Object::new_move(obj, owner, TransactionDigest::GENESIS_MARKER)
618 }
619
620 pub fn with_id_owner_gas_for_testing(id: ObjectId, owner: Address, gas: u64) -> Self {
621 let data = ObjectData::Struct(
622 MoveObject::new(
623 StructTag::new_gas_coin().into(),
624 OBJECT_START_VERSION,
625 GasCoin::new(id, gas).to_bcs_bytes(),
626 )
627 .unwrap(),
628 );
629 ObjectInner {
630 owner: Owner::Address(owner),
631 data,
632 previous_transaction: TransactionDigest::GENESIS_MARKER,
633 storage_rebate: 0,
634 }
635 .into()
636 }
637
638 pub fn treasury_cap_for_testing(struct_tag: StructTag, treasury_cap: TreasuryCap) -> Self {
639 let data = ObjectData::Struct(
640 MoveObject::new(
641 StructTag::new_treasury_cap(struct_tag).into(),
642 OBJECT_START_VERSION,
643 bcs::to_bytes(&treasury_cap).expect("Failed to serialize"),
644 )
645 .unwrap(),
646 );
647 ObjectInner {
648 owner: Owner::Immutable,
649 data,
650 previous_transaction: TransactionDigest::GENESIS_MARKER,
651 storage_rebate: 0,
652 }
653 .into()
654 }
655
656 pub fn coin_metadata_for_testing(struct_tag: StructTag, metadata: CoinMetadata) -> Self {
657 let data = ObjectData::Struct(
658 MoveObject::new(
659 StructTag::new_coin_metadata(struct_tag).into(),
660 OBJECT_START_VERSION,
661 bcs::to_bytes(&metadata).expect("Failed to serialize"),
662 )
663 .unwrap(),
664 );
665 ObjectInner {
666 owner: Owner::Immutable,
667 data,
668 previous_transaction: TransactionDigest::GENESIS_MARKER,
669 storage_rebate: 0,
670 }
671 .into()
672 }
673
674 pub fn with_object_owner_for_testing(id: ObjectId, owner: ObjectId) -> Self {
675 let data = ObjectData::Struct(
676 MoveObject::new(
677 StructTag::new_gas_coin().into(),
678 OBJECT_START_VERSION,
679 GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
680 )
681 .unwrap(),
682 );
683 ObjectInner {
684 owner: Owner::Object(owner),
685 data,
686 previous_transaction: TransactionDigest::GENESIS_MARKER,
687 storage_rebate: 0,
688 }
689 .into()
690 }
691
692 pub fn with_id_owner_for_testing(id: ObjectId, owner: Address) -> Self {
693 Self::with_id_owner_gas_for_testing(id, owner, GAS_VALUE_FOR_TESTING)
695 }
696
697 pub fn with_id_owner_version_for_testing(
698 id: ObjectId,
699 version: SequenceNumber,
700 owner: Owner,
701 ) -> Self {
702 let data = ObjectData::Struct(
703 MoveObject::new(
704 StructTag::new_gas_coin().into(),
705 version,
706 GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
707 )
708 .unwrap(),
709 );
710 ObjectInner {
711 owner,
712 data,
713 previous_transaction: TransactionDigest::GENESIS_MARKER,
714 storage_rebate: 0,
715 }
716 .into()
717 }
718
719 pub fn with_owner_for_testing(owner: Address) -> Self {
720 Self::with_id_owner_for_testing(ObjectId::random(), owner)
721 }
722
723 pub fn new_gas_with_balance_and_owner_for_testing(value: u64, owner: Address) -> Self {
726 let obj = MoveObject::new_gas_coin(OBJECT_START_VERSION, ObjectId::random(), value);
727 Object::new_move(
728 obj,
729 Owner::Address(owner),
730 TransactionDigest::GENESIS_MARKER,
731 )
732 }
733
734 pub fn new_gas_for_testing() -> Self {
736 let gas_object_id = ObjectId::random();
737 let (owner, _) = deterministic_random_account_key();
738 Object::with_id_owner_for_testing(gas_object_id, owner)
739 }
740}
741
742pub fn generate_test_gas_objects() -> Vec<Object> {
744 thread_local! {
745 static GAS_OBJECTS: Vec<Object> = (0..50)
746 .map(|_| {
747 let gas_object_id = ObjectId::random();
748 let (owner, _) = deterministic_random_account_key();
749 Object::with_id_owner_for_testing(gas_object_id, owner)
750 })
751 .collect();
752 }
753
754 GAS_OBJECTS.with(|v| v.clone())
755}
756
757#[derive(Serialize, Deserialize, Debug)]
758#[serde(tag = "status", content = "details")]
759pub enum ObjectRead {
760 NotExists(ObjectId),
761 Exists(ObjectRef, Object, Option<MoveStructLayout>),
762 Deleted(ObjectRef),
763}
764
765impl ObjectRead {
766 pub fn into_object(self) -> UserInputResult<Object> {
769 match self {
770 Self::Deleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: oref }),
771 Self::NotExists(id) => Err(UserInputError::ObjectNotFound {
772 object_id: id,
773 version: None,
774 }),
775 Self::Exists(_, o, _) => Ok(o),
776 }
777 }
778
779 pub fn object(&self) -> UserInputResult<&Object> {
780 match self {
781 Self::Deleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: *oref }),
782 Self::NotExists(id) => Err(UserInputError::ObjectNotFound {
783 object_id: *id,
784 version: None,
785 }),
786 Self::Exists(_, o, _) => Ok(o),
787 }
788 }
789
790 pub fn object_id(&self) -> ObjectId {
791 match self {
792 Self::Deleted(oref) => oref.object_id,
793 Self::NotExists(id) => *id,
794 Self::Exists(oref, _, _) => oref.object_id,
795 }
796 }
797}
798
799impl Display for ObjectRead {
800 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
801 match self {
802 Self::Deleted(oref) => {
803 write!(f, "ObjectRead::Deleted ({oref:?})")
804 }
805 Self::NotExists(id) => {
806 write!(f, "ObjectRead::NotExists ({id})")
807 }
808 Self::Exists(oref, _, _) => {
809 write!(f, "ObjectRead::Exists ({oref:?})")
810 }
811 }
812 }
813}
814
815#[derive(Serialize, Deserialize, Debug)]
816#[serde(tag = "status", content = "details")]
817pub enum PastObjectRead {
818 ObjectNotExists(ObjectId),
820 ObjectDeleted(ObjectRef),
822 VersionFound(ObjectRef, Object, Option<MoveStructLayout>),
824 VersionNotFound(ObjectId, SequenceNumber),
826 VersionTooHigh {
828 object_id: ObjectId,
829 asked_version: SequenceNumber,
830 latest_version: SequenceNumber,
831 },
832}
833
834impl PastObjectRead {
835 pub fn into_object(self) -> UserInputResult<Object> {
837 match self {
838 Self::ObjectDeleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: oref }),
839 Self::ObjectNotExists(id) => Err(UserInputError::ObjectNotFound {
840 object_id: id,
841 version: None,
842 }),
843 Self::VersionFound(_, o, _) => Ok(o),
844 Self::VersionNotFound(object_id, version) => Err(UserInputError::ObjectNotFound {
845 object_id,
846 version: Some(version),
847 }),
848 Self::VersionTooHigh {
849 object_id,
850 asked_version,
851 latest_version,
852 } => Err(UserInputError::ObjectSequenceNumberTooHigh {
853 object_id,
854 asked_version,
855 latest_version,
856 }),
857 }
858 }
859}
860
861impl Display for PastObjectRead {
862 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
863 match self {
864 Self::ObjectDeleted(oref) => {
865 write!(f, "PastObjectRead::ObjectDeleted ({oref:?})")
866 }
867 Self::ObjectNotExists(id) => {
868 write!(f, "PastObjectRead::ObjectNotExists ({id})")
869 }
870 Self::VersionFound(oref, _, _) => {
871 write!(f, "PastObjectRead::VersionFound ({oref:?})")
872 }
873 Self::VersionNotFound(object_id, version) => {
874 write!(
875 f,
876 "PastObjectRead::VersionNotFound ({object_id}, asked sequence number {version:?})"
877 )
878 }
879 Self::VersionTooHigh {
880 object_id,
881 asked_version,
882 latest_version,
883 } => {
884 write!(
885 f,
886 "PastObjectRead::VersionTooHigh ({object_id}, asked sequence number {asked_version:?}, latest sequence number {latest_version:?})"
887 )
888 }
889 }
890 }
891}
892
893#[cfg(test)]
894mod tests {
895 use iota_sdk_types::{Address, ObjectId};
896
897 use crate::{
898 base_types::TransactionDigest,
899 gas_coin::GasCoin,
900 object::{MoveObjectExt, OBJECT_START_VERSION, Object, Owner},
901 };
902
903 #[test]
906 fn test_object_digest_and_serialized_format() {
907 let g =
908 GasCoin::new_for_testing_with_id(ObjectId::ZERO, 123).to_object(OBJECT_START_VERSION);
909 let o = Object::new_move(g, Owner::Address(Address::ZERO), TransactionDigest::ZERO);
910 let bytes = bcs::to_bytes(&o).unwrap();
911
912 assert_eq!(
913 bytes,
914 [
915 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
916 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
917 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
918 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
919 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
920 ]
921 );
922 let objref = o.object_ref();
923
924 assert_eq!(objref.object_id, ObjectId::ZERO);
925 assert_eq!(objref.version, 1);
926 assert_eq!(
927 objref.digest.to_string(),
928 "Ba4YyVBcpc9jgX4PMLRoyt9dKLftYVSDvuKbtMr9f4NM"
929 );
930 }
931
932 #[test]
933 fn test_get_coin_value_unchecked() {
934 fn test_for_value(v: u64) {
935 let g = GasCoin::new_for_testing(v).to_object(OBJECT_START_VERSION);
936 assert_eq!(g.get_coin_value_unchecked(), v);
937 assert_eq!(GasCoin::try_from(&g).unwrap().value(), v);
938 }
939
940 test_for_value(0);
941 test_for_value(1);
942 test_for_value(8);
943 test_for_value(9);
944 test_for_value(u8::MAX as u64);
945 test_for_value(u8::MAX as u64 + 1);
946 test_for_value(u16::MAX as u64);
947 test_for_value(u16::MAX as u64 + 1);
948 test_for_value(u32::MAX as u64);
949 test_for_value(u32::MAX as u64 + 1);
950 test_for_value(u64::MAX);
951 }
952
953 #[test]
954 fn test_set_coin_value_unchecked() {
955 fn test_for_value(v: u64) {
956 let mut g = GasCoin::new_for_testing(u64::MAX).to_object(OBJECT_START_VERSION);
957 g.set_coin_value_unchecked(v);
958 assert_eq!(g.get_coin_value_unchecked(), v);
959 assert_eq!(GasCoin::try_from(&g).unwrap().value(), v);
960 assert_eq!(g.version(), OBJECT_START_VERSION);
961 assert_eq!(g.contents().len(), 40);
962 }
963
964 test_for_value(0);
965 test_for_value(1);
966 test_for_value(8);
967 test_for_value(9);
968 test_for_value(u8::MAX as u64);
969 test_for_value(u8::MAX as u64 + 1);
970 test_for_value(u16::MAX as u64);
971 test_for_value(u16::MAX as u64 + 1);
972 test_for_value(u32::MAX as u64);
973 test_for_value(u32::MAX as u64 + 1);
974 test_for_value(u64::MAX);
975 }
976}