1use std::{
6 collections::BTreeMap,
7 fmt::{Debug, Display, Formatter},
8 mem::size_of,
9 sync::Arc,
10};
11
12use iota_protocol_config::ProtocolConfig;
13pub use iota_sdk_types::{MoveStruct as MoveObject, ObjectData as Data, Owner};
14use iota_sdk_types::{StructTag, TypeTag};
15use move_binary_format::CompiledModule;
16use move_bytecode_utils::{layout::TypeLayoutBuilder, module_cache::GetModule};
17use move_core_types::annotated_value::{MoveStruct, MoveStructLayout, MoveTypeLayout, MoveValue};
18use serde::{Deserialize, Serialize};
19
20use self::{balance_traversal::BalanceTraversal, bounded_visitor::BoundedVisitor};
21use crate::{
22 balance::Balance,
23 base_types::{
24 IotaAddress, MoveObjectType, ObjectDigest, ObjectID, ObjectRef, SequenceNumber,
25 TransactionDigest,
26 },
27 coin::{Coin, CoinMetadata, TreasuryCap},
28 crypto::{default_hash, 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::{MovePackage, 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) {
190 assert!(self.struct_tag().is_clock());
191 assert!(self.contents().len() == 40);
193
194 let mut new_contents = self.contents().to_vec();
195 new_contents[ID_END_INDEX..].copy_from_slice(×tamp_ms.to_le_bytes());
196 self.set_contents(new_contents).unwrap();
197 }
198
199 fn update_contents(
201 &mut self,
202 new_contents: Vec<u8>,
203 protocol_config: &ProtocolConfig,
204 ) -> Result<(), ExecutionError> {
205 self.update_contents_with_limit(new_contents, protocol_config.max_move_object_size())
206 }
207
208 fn update_contents_with_limit(
209 &mut self,
210 new_contents: Vec<u8>,
211 max_move_object_size: u64,
212 ) -> Result<(), ExecutionError> {
213 if new_contents.len() as u64 > max_move_object_size {
214 return Err(ExecutionError::from_kind(
215 ExecutionErrorKind::ObjectTooBig {
216 object_size: new_contents.len() as u64,
217 max_object_size: max_move_object_size,
218 },
219 ));
220 }
221
222 #[cfg(debug_assertions)]
223 let old_id = self.id();
224
225 self.set_contents(new_contents)
226 .map_err(ExecutionError::invariant_violation)?;
227
228 #[cfg(debug_assertions)]
230 debug_assert_eq!(self.id(), old_id);
231
232 Ok(())
233 }
234
235 fn increment_version_to(&mut self, next: SequenceNumber) {
238 debug_assert!(
239 self.version() < next,
240 "Not an increment: {} to {next}",
241 self.version()
242 );
243 self.set_version(next);
244 }
245
246 fn decrement_version_to(&mut self, prev: SequenceNumber) {
248 debug_assert!(
249 prev < self.version(),
250 "Not a decrement: {} to {prev}",
251 self.version()
252 );
253 self.set_version(prev);
254 }
255
256 fn get_layout(&self, resolver: &impl GetModule) -> Result<MoveStructLayout, IotaError> {
262 Self::get_struct_layout_from_struct_tag(self.struct_tag().clone(), resolver)
263 }
264
265 fn get_struct_layout_from_struct_tag(
266 struct_tag: StructTag,
267 resolver: &impl GetModule,
268 ) -> Result<MoveStructLayout, IotaError> {
269 let type_ = TypeTag::Struct(Box::new(struct_tag));
270 let layout = TypeLayoutBuilder::build_with_types(&type_tag_sdk_to_core(&type_), resolver)
271 .map_err(|e| IotaError::ObjectSerialization {
272 error: e.to_string(),
273 })?;
274 match layout {
275 MoveTypeLayout::Struct(l) => Ok(*l),
276 _ => unreachable!(
277 "We called build_with_types on Struct type, should get a struct layout"
278 ),
279 }
280 }
281
282 fn to_move_struct(&self, layout: &MoveStructLayout) -> Result<MoveStruct, IotaError> {
284 BoundedVisitor::deserialize_struct(self.contents(), layout).map_err(|e| {
285 IotaError::ObjectSerialization {
286 error: e.to_string(),
287 }
288 })
289 }
290
291 fn object_size_for_gas_metering(&self) -> usize {
296 let serialized_type_tag_size =
297 bcs::serialized_size(self.object_type()).expect("Serializing type tag should not fail");
298 self.contents().len() + serialized_type_tag_size + 8
300 }
301
302 fn get_total_iota(&self, layout_resolver: &mut dyn LayoutResolver) -> Result<u64, IotaError> {
305 let balances = self.get_coin_balances(layout_resolver)?;
306 Ok(balances.get(&GAS::type_tag()).copied().unwrap_or(0))
307 }
308
309 fn get_coin_balances(
311 &self,
312 layout_resolver: &mut dyn LayoutResolver,
313 ) -> Result<BTreeMap<TypeTag, u64>, IotaError> {
314 if let Some(type_tag) = self.object_type().coin_type_opt() {
316 let balance = self.get_coin_value_unchecked();
317 Ok(if balance > 0 {
318 BTreeMap::from([(type_tag.clone(), balance)])
319 } else {
320 BTreeMap::default()
321 })
322 } else {
323 let layout = layout_resolver.get_annotated_layout(self.struct_tag())?;
324
325 let mut traversal = BalanceTraversal::default();
326 MoveValue::visit_deserialize(self.contents(), &layout.into_layout(), &mut traversal)
327 .map_err(|e| IotaError::ObjectSerialization {
328 error: e.to_string(),
329 })?;
330
331 Ok(traversal.finish())
332 }
333 }
334}
335
336#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
337#[serde(rename = "Object")]
338pub struct ObjectInner {
339 pub data: Data,
341 pub owner: Owner,
343 pub previous_transaction: TransactionDigest,
345 pub storage_rebate: u64,
349}
350
351#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
352#[serde(from = "ObjectInner")]
353pub struct Object(Arc<ObjectInner>);
354
355impl From<ObjectInner> for Object {
356 fn from(inner: ObjectInner) -> Self {
357 Self(Arc::new(inner))
358 }
359}
360
361impl Object {
362 pub fn into_inner(self) -> ObjectInner {
363 match Arc::try_unwrap(self.0) {
364 Ok(inner) => inner,
365 Err(inner_arc) => (*inner_arc).clone(),
366 }
367 }
368
369 pub fn as_inner(&self) -> &ObjectInner {
370 &self.0
371 }
372
373 pub fn owner(&self) -> &Owner {
374 &self.0.owner
375 }
376
377 pub fn new_from_genesis(
378 data: Data,
379 owner: Owner,
380 previous_transaction: TransactionDigest,
381 ) -> Self {
382 ObjectInner {
383 data,
384 owner,
385 previous_transaction,
386 storage_rebate: 0,
387 }
388 .into()
389 }
390
391 pub fn new_move(o: MoveObject, owner: Owner, previous_transaction: TransactionDigest) -> Self {
393 ObjectInner {
394 data: Data::Struct(o),
395 owner,
396 previous_transaction,
397 storage_rebate: 0,
398 }
399 .into()
400 }
401
402 pub fn new_package_from_data(data: Data, previous_transaction: TransactionDigest) -> Self {
403 ObjectInner {
404 data,
405 owner: Owner::Immutable,
406 previous_transaction,
407 storage_rebate: 0,
408 }
409 .into()
410 }
411
412 pub fn new_from_package(package: MovePackage, previous_transaction: TransactionDigest) -> Self {
414 Self::new_package_from_data(Data::Package(package), previous_transaction)
415 }
416
417 pub fn new_package<'p>(
418 modules: &[CompiledModule],
419 previous_transaction: TransactionDigest,
420 protocol_config: &ProtocolConfig,
421 dependencies: impl IntoIterator<Item = &'p MovePackage>,
422 ) -> Result<Self, ExecutionError> {
423 Ok(Self::new_package_from_data(
424 Data::Package(MovePackage::new_initial(
425 modules,
426 protocol_config,
427 dependencies,
428 )?),
429 previous_transaction,
430 ))
431 }
432
433 pub fn new_upgraded_package<'p>(
434 previous_package: &MovePackage,
435 new_package_id: ObjectID,
436 modules: &[CompiledModule],
437 previous_transaction: TransactionDigest,
438 protocol_config: &ProtocolConfig,
439 dependencies: impl IntoIterator<Item = &'p MovePackage>,
440 ) -> Result<Self, ExecutionError> {
441 Ok(Self::new_package_from_data(
442 Data::Package(previous_package.new_upgraded(
443 new_package_id,
444 modules,
445 protocol_config,
446 dependencies,
447 )?),
448 previous_transaction,
449 ))
450 }
451
452 pub fn new_package_for_testing(
453 modules: &[CompiledModule],
454 previous_transaction: TransactionDigest,
455 dependencies: impl IntoIterator<Item = MovePackage>,
456 ) -> Result<Self, ExecutionError> {
457 let dependencies: Vec<_> = dependencies.into_iter().collect();
458 let config = ProtocolConfig::get_for_max_version_UNSAFE();
459 Self::new_package(modules, previous_transaction, &config, &dependencies)
460 }
461
462 pub fn new_system_package(
465 modules: &[CompiledModule],
466 version: SequenceNumber,
467 dependencies: Vec<ObjectID>,
468 previous_transaction: TransactionDigest,
469 ) -> Self {
470 let ret = Self::new_package_from_data(
471 Data::Package(MovePackage::new_system(version, modules, dependencies)),
472 previous_transaction,
473 );
474
475 #[cfg(not(msim))]
476 assert!(ret.is_system_package());
477
478 ret
479 }
480}
481
482impl std::ops::Deref for Object {
483 type Target = ObjectInner;
484 fn deref(&self) -> &Self::Target {
485 &self.0
486 }
487}
488
489impl std::ops::DerefMut for Object {
490 fn deref_mut(&mut self) -> &mut Self::Target {
491 Arc::make_mut(&mut self.0)
492 }
493}
494
495impl ObjectInner {
496 pub fn is_system_package(&self) -> bool {
498 self.is_package() && self.id().is_system_package()
499 }
500
501 pub fn is_immutable(&self) -> bool {
502 self.owner.is_immutable()
503 }
504
505 pub fn is_address_owned(&self) -> bool {
506 self.owner.is_address()
507 }
508
509 pub fn is_child_object(&self) -> bool {
510 self.owner.is_object()
511 }
512
513 pub fn is_shared(&self) -> bool {
514 self.owner.is_shared()
515 }
516
517 pub fn get_single_owner(&self) -> Option<IotaAddress> {
518 self.owner.address_or_object().copied()
519 }
520
521 pub fn get_owner_and_id(&self) -> Option<(Owner, ObjectID)> {
524 Some((self.owner, self.id()))
525 }
526
527 pub fn is_package(&self) -> bool {
530 matches!(&self.data, Data::Package(_))
531 }
532
533 pub fn compute_object_reference(&self) -> ObjectRef {
534 ObjectRef::new(self.id(), self.version(), self.digest())
535 }
536
537 pub fn digest(&self) -> ObjectDigest {
538 ObjectDigest::new(default_hash(self))
539 }
540
541 pub fn id(&self) -> ObjectID {
542 use Data::*;
543
544 match &self.data {
545 Struct(v) => v.id(),
546 Package(m) => m.id(),
547 }
548 }
549
550 pub fn version(&self) -> SequenceNumber {
551 use Data::*;
552
553 match &self.data {
554 Struct(o) => o.version(),
555 Package(p) => p.version(),
556 }
557 }
558
559 pub fn type_(&self) -> Option<&MoveObjectType> {
560 self.data.object_type()
561 }
562
563 pub fn struct_tag(&self) -> Option<StructTag> {
564 self.data.struct_tag()
565 }
566
567 pub fn is_coin(&self) -> bool {
568 if let Some(move_object) = self.data.as_struct_opt() {
569 move_object.struct_tag().is_coin()
570 } else {
571 false
572 }
573 }
574
575 pub fn is_gas_coin(&self) -> bool {
576 if let Some(move_object) = self.data.as_struct_opt() {
577 move_object.struct_tag().is_gas_coin()
578 } else {
579 false
580 }
581 }
582
583 pub fn as_coin_maybe(&self) -> Option<Coin> {
586 if let Some(move_object) = self.data.as_struct_opt() {
587 let coin: Coin = bcs::from_bytes(move_object.contents()).ok()?;
588 Some(coin)
589 } else {
590 None
591 }
592 }
593
594 pub fn as_timelock_balance_maybe(&self) -> Option<TimeLock<Balance>> {
595 if let Some(move_object) = self.data.as_struct_opt() {
596 Some(TimeLock::from_bcs_bytes(move_object.contents()).ok()?)
597 } else {
598 None
599 }
600 }
601
602 pub fn coin_type_opt(&self) -> Option<&TypeTag> {
603 if let Some(move_object) = self.data.as_struct_opt() {
604 move_object.struct_tag().coin_type_opt()
605 } else {
606 None
607 }
608 }
609
610 pub fn get_coin_value_unchecked(&self) -> u64 {
615 self.data
616 .as_struct_opt()
617 .unwrap()
618 .get_coin_value_unchecked()
619 }
620
621 pub fn object_size_for_gas_metering(&self) -> usize {
626 let meta_data_size = size_of::<Owner>() + size_of::<TransactionDigest>() + size_of::<u64>();
627 let data_size = match &self.data {
628 Data::Struct(m) => m.object_size_for_gas_metering(),
629 Data::Package(p) => p.size(),
630 };
631 meta_data_size + data_size
632 }
633
634 pub fn transfer(&mut self, new_owner: IotaAddress) {
636 self.owner = Owner::Address(new_owner);
637 }
638
639 pub fn get_layout(
645 &self,
646 resolver: &impl GetModule,
647 ) -> Result<Option<MoveStructLayout>, IotaError> {
648 match &self.data {
649 Data::Struct(m) => Ok(Some(m.get_layout(resolver)?)),
650 Data::Package(_) => Ok(None),
651 }
652 }
653
654 pub fn get_move_template_type(&self) -> IotaResult<TypeTag> {
658 let move_struct = self.data.struct_tag().ok_or_else(|| IotaError::Type {
659 error: "Object must be a Move object".to_owned(),
660 })?;
661 fp_ensure!(
662 move_struct.type_params().len() == 1,
663 IotaError::Type {
664 error: "Move object struct must have one type parameter".to_owned()
665 }
666 );
667 let type_tag = move_struct.type_params()[0].clone();
669 Ok(type_tag)
670 }
671
672 pub fn to_rust<'de, T: Deserialize<'de>>(&'de self) -> Option<T> {
673 self.data.as_struct_opt().and_then(|data| data.to_rust())
674 }
675}
676
677impl Object {
679 pub fn get_total_iota(
682 &self,
683 layout_resolver: &mut dyn LayoutResolver,
684 ) -> Result<u64, IotaError> {
685 Ok(self.storage_rebate
686 + match &self.data {
687 Data::Struct(m) => m.get_total_iota(layout_resolver)?,
688 Data::Package(_) => 0,
689 })
690 }
691
692 pub fn immutable_with_id_for_testing(id: ObjectID) -> Self {
693 let data = Data::Struct(
694 MoveObject::new(
695 StructTag::new_gas_coin().into(),
696 OBJECT_START_VERSION,
697 GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
698 )
699 .unwrap(),
700 );
701 ObjectInner {
702 owner: Owner::Immutable,
703 data,
704 previous_transaction: TransactionDigest::GENESIS_MARKER,
705 storage_rebate: 0,
706 }
707 .into()
708 }
709
710 pub fn immutable_for_testing() -> Self {
711 thread_local! {
712 static IMMUTABLE_OBJECT_ID: ObjectID = ObjectID::random();
713 }
714
715 Self::immutable_with_id_for_testing(IMMUTABLE_OBJECT_ID.with(|id| *id))
716 }
717
718 pub fn shared_for_testing() -> Object {
720 let id = ObjectID::random();
721 let obj = MoveObject::new_gas_coin(OBJECT_START_VERSION, id, 10);
722 let owner = Owner::Shared(obj.version());
723 Object::new_move(obj, owner, TransactionDigest::GENESIS_MARKER)
724 }
725
726 pub fn with_id_owner_gas_for_testing(id: ObjectID, owner: IotaAddress, gas: u64) -> Self {
727 let data = Data::Struct(
728 MoveObject::new(
729 StructTag::new_gas_coin().into(),
730 OBJECT_START_VERSION,
731 GasCoin::new(id, gas).to_bcs_bytes(),
732 )
733 .unwrap(),
734 );
735 ObjectInner {
736 owner: Owner::Address(owner),
737 data,
738 previous_transaction: TransactionDigest::GENESIS_MARKER,
739 storage_rebate: 0,
740 }
741 .into()
742 }
743
744 pub fn treasury_cap_for_testing(struct_tag: StructTag, treasury_cap: TreasuryCap) -> Self {
745 let data = Data::Struct(
746 MoveObject::new(
747 StructTag::new_treasury_cap(struct_tag).into(),
748 OBJECT_START_VERSION,
749 bcs::to_bytes(&treasury_cap).expect("Failed to serialize"),
750 )
751 .unwrap(),
752 );
753 ObjectInner {
754 owner: Owner::Immutable,
755 data,
756 previous_transaction: TransactionDigest::GENESIS_MARKER,
757 storage_rebate: 0,
758 }
759 .into()
760 }
761
762 pub fn coin_metadata_for_testing(struct_tag: StructTag, metadata: CoinMetadata) -> Self {
763 let data = Data::Struct(
764 MoveObject::new(
765 StructTag::new_coin_metadata(struct_tag).into(),
766 OBJECT_START_VERSION,
767 bcs::to_bytes(&metadata).expect("Failed to serialize"),
768 )
769 .unwrap(),
770 );
771 ObjectInner {
772 owner: Owner::Immutable,
773 data,
774 previous_transaction: TransactionDigest::GENESIS_MARKER,
775 storage_rebate: 0,
776 }
777 .into()
778 }
779
780 pub fn with_object_owner_for_testing(id: ObjectID, owner: ObjectID) -> Self {
781 let data = Data::Struct(
782 MoveObject::new(
783 StructTag::new_gas_coin().into(),
784 OBJECT_START_VERSION,
785 GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
786 )
787 .unwrap(),
788 );
789 ObjectInner {
790 owner: Owner::Object(owner),
791 data,
792 previous_transaction: TransactionDigest::GENESIS_MARKER,
793 storage_rebate: 0,
794 }
795 .into()
796 }
797
798 pub fn with_id_owner_for_testing(id: ObjectID, owner: IotaAddress) -> Self {
799 Self::with_id_owner_gas_for_testing(id, owner, GAS_VALUE_FOR_TESTING)
801 }
802
803 pub fn with_id_owner_version_for_testing(
804 id: ObjectID,
805 version: SequenceNumber,
806 owner: Owner,
807 ) -> Self {
808 let data = Data::Struct(
809 MoveObject::new(
810 StructTag::new_gas_coin().into(),
811 version,
812 GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
813 )
814 .unwrap(),
815 );
816 ObjectInner {
817 owner,
818 data,
819 previous_transaction: TransactionDigest::GENESIS_MARKER,
820 storage_rebate: 0,
821 }
822 .into()
823 }
824
825 pub fn with_owner_for_testing(owner: IotaAddress) -> Self {
826 Self::with_id_owner_for_testing(ObjectID::random(), owner)
827 }
828
829 pub fn new_gas_with_balance_and_owner_for_testing(value: u64, owner: IotaAddress) -> Self {
832 let obj = MoveObject::new_gas_coin(OBJECT_START_VERSION, ObjectID::random(), value);
833 Object::new_move(
834 obj,
835 Owner::Address(owner),
836 TransactionDigest::GENESIS_MARKER,
837 )
838 }
839
840 pub fn new_gas_for_testing() -> Self {
842 let gas_object_id = ObjectID::random();
843 let (owner, _) = deterministic_random_account_key();
844 Object::with_id_owner_for_testing(gas_object_id, owner)
845 }
846}
847
848pub fn generate_test_gas_objects() -> Vec<Object> {
850 thread_local! {
851 static GAS_OBJECTS: Vec<Object> = (0..50)
852 .map(|_| {
853 let gas_object_id = ObjectID::random();
854 let (owner, _) = deterministic_random_account_key();
855 Object::with_id_owner_for_testing(gas_object_id, owner)
856 })
857 .collect();
858 }
859
860 GAS_OBJECTS.with(|v| v.clone())
861}
862
863#[derive(Serialize, Deserialize, Debug)]
864#[serde(tag = "status", content = "details")]
865pub enum ObjectRead {
866 NotExists(ObjectID),
867 Exists(ObjectRef, Object, Option<MoveStructLayout>),
868 Deleted(ObjectRef),
869}
870
871impl ObjectRead {
872 pub fn into_object(self) -> UserInputResult<Object> {
875 match self {
876 Self::Deleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: oref }),
877 Self::NotExists(id) => Err(UserInputError::ObjectNotFound {
878 object_id: id,
879 version: None,
880 }),
881 Self::Exists(_, o, _) => Ok(o),
882 }
883 }
884
885 pub fn object(&self) -> UserInputResult<&Object> {
886 match self {
887 Self::Deleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: *oref }),
888 Self::NotExists(id) => Err(UserInputError::ObjectNotFound {
889 object_id: *id,
890 version: None,
891 }),
892 Self::Exists(_, o, _) => Ok(o),
893 }
894 }
895
896 pub fn object_id(&self) -> ObjectID {
897 match self {
898 Self::Deleted(oref) => oref.object_id,
899 Self::NotExists(id) => *id,
900 Self::Exists(oref, _, _) => oref.object_id,
901 }
902 }
903}
904
905impl Display for ObjectRead {
906 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
907 match self {
908 Self::Deleted(oref) => {
909 write!(f, "ObjectRead::Deleted ({oref:?})")
910 }
911 Self::NotExists(id) => {
912 write!(f, "ObjectRead::NotExists ({id:?})")
913 }
914 Self::Exists(oref, _, _) => {
915 write!(f, "ObjectRead::Exists ({oref:?})")
916 }
917 }
918 }
919}
920
921#[derive(Serialize, Deserialize, Debug)]
922#[serde(tag = "status", content = "details")]
923pub enum PastObjectRead {
924 ObjectNotExists(ObjectID),
926 ObjectDeleted(ObjectRef),
928 VersionFound(ObjectRef, Object, Option<MoveStructLayout>),
930 VersionNotFound(ObjectID, SequenceNumber),
932 VersionTooHigh {
934 object_id: ObjectID,
935 asked_version: SequenceNumber,
936 latest_version: SequenceNumber,
937 },
938}
939
940impl PastObjectRead {
941 pub fn into_object(self) -> UserInputResult<Object> {
943 match self {
944 Self::ObjectDeleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: oref }),
945 Self::ObjectNotExists(id) => Err(UserInputError::ObjectNotFound {
946 object_id: id,
947 version: None,
948 }),
949 Self::VersionFound(_, o, _) => Ok(o),
950 Self::VersionNotFound(object_id, version) => Err(UserInputError::ObjectNotFound {
951 object_id,
952 version: Some(version),
953 }),
954 Self::VersionTooHigh {
955 object_id,
956 asked_version,
957 latest_version,
958 } => Err(UserInputError::ObjectSequenceNumberTooHigh {
959 object_id,
960 asked_version,
961 latest_version,
962 }),
963 }
964 }
965}
966
967impl Display for PastObjectRead {
968 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
969 match self {
970 Self::ObjectDeleted(oref) => {
971 write!(f, "PastObjectRead::ObjectDeleted ({oref:?})")
972 }
973 Self::ObjectNotExists(id) => {
974 write!(f, "PastObjectRead::ObjectNotExists ({id:?})")
975 }
976 Self::VersionFound(oref, _, _) => {
977 write!(f, "PastObjectRead::VersionFound ({oref:?})")
978 }
979 Self::VersionNotFound(object_id, version) => {
980 write!(
981 f,
982 "PastObjectRead::VersionNotFound ({object_id:?}, asked sequence number {version:?})"
983 )
984 }
985 Self::VersionTooHigh {
986 object_id,
987 asked_version,
988 latest_version,
989 } => {
990 write!(
991 f,
992 "PastObjectRead::VersionTooHigh ({object_id:?}, asked sequence number {asked_version:?}, latest sequence number {latest_version:?})"
993 )
994 }
995 }
996 }
997}
998
999#[cfg(test)]
1000mod tests {
1001 use crate::{
1002 base_types::{IotaAddress, ObjectID, TransactionDigest},
1003 gas_coin::GasCoin,
1004 object::{MoveObjectExt, OBJECT_START_VERSION, Object, Owner},
1005 };
1006
1007 #[test]
1010 fn test_object_digest_and_serialized_format() {
1011 let g =
1012 GasCoin::new_for_testing_with_id(ObjectID::ZERO, 123).to_object(OBJECT_START_VERSION);
1013 let o = Object::new_move(
1014 g,
1015 Owner::Address(IotaAddress::ZERO),
1016 TransactionDigest::ZERO,
1017 );
1018 let bytes = bcs::to_bytes(&o).unwrap();
1019
1020 assert_eq!(
1021 bytes,
1022 [
1023 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,
1024 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,
1025 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,
1026 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,
1027 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1028 ]
1029 );
1030 let objref = o.compute_object_reference();
1031
1032 assert_eq!(objref.object_id, ObjectID::ZERO);
1033 assert_eq!(objref.version, 1);
1034 assert_eq!(
1035 objref.digest.to_string(),
1036 "Ba4YyVBcpc9jgX4PMLRoyt9dKLftYVSDvuKbtMr9f4NM"
1037 );
1038 }
1039
1040 #[test]
1041 fn test_get_coin_value_unchecked() {
1042 fn test_for_value(v: u64) {
1043 let g = GasCoin::new_for_testing(v).to_object(OBJECT_START_VERSION);
1044 assert_eq!(g.get_coin_value_unchecked(), v);
1045 assert_eq!(GasCoin::try_from(&g).unwrap().value(), v);
1046 }
1047
1048 test_for_value(0);
1049 test_for_value(1);
1050 test_for_value(8);
1051 test_for_value(9);
1052 test_for_value(u8::MAX as u64);
1053 test_for_value(u8::MAX as u64 + 1);
1054 test_for_value(u16::MAX as u64);
1055 test_for_value(u16::MAX as u64 + 1);
1056 test_for_value(u32::MAX as u64);
1057 test_for_value(u32::MAX as u64 + 1);
1058 test_for_value(u64::MAX);
1059 }
1060
1061 #[test]
1062 fn test_set_coin_value_unchecked() {
1063 fn test_for_value(v: u64) {
1064 let mut g = GasCoin::new_for_testing(u64::MAX).to_object(OBJECT_START_VERSION);
1065 g.set_coin_value_unchecked(v);
1066 assert_eq!(g.get_coin_value_unchecked(), v);
1067 assert_eq!(GasCoin::try_from(&g).unwrap().value(), v);
1068 assert_eq!(g.version(), OBJECT_START_VERSION);
1069 assert_eq!(g.contents().len(), 40);
1070 }
1071
1072 test_for_value(0);
1073 test_for_value(1);
1074 test_for_value(8);
1075 test_for_value(9);
1076 test_for_value(u8::MAX as u64);
1077 test_for_value(u8::MAX as u64 + 1);
1078 test_for_value(u16::MAX as u64);
1079 test_for_value(u16::MAX as u64 + 1);
1080 test_for_value(u32::MAX as u64);
1081 test_for_value(u32::MAX as u64 + 1);
1082 test_for_value(u64::MAX);
1083 }
1084}