1use std::convert::TryFrom;
5use std::fmt::{Debug, Display, Formatter};
6use std::sync::Arc;
7
8use move_binary_format::CompiledModule;
9use move_bytecode_utils::layout::TypeLayoutBuilder;
10use move_bytecode_utils::module_cache::GetModule;
11use move_core_types::annotated_value::{MoveStruct, MoveStructLayout, MoveTypeLayout, MoveValue};
12use move_core_types::language_storage::StructTag;
13use move_core_types::language_storage::TypeTag;
14use mysten_common::debug_fatal;
15use once_cell::sync::Lazy;
16use schemars::JsonSchema;
17use serde::{Deserialize, Serialize};
18use serde_with::Bytes;
19use serde_with::serde_as;
20
21use crate::accumulator_root::AccumulatorValue;
22use crate::base_types::{FullObjectID, FullObjectRef, MoveObjectType, ObjectIDParseError};
23use crate::coin::{Coin, CoinMetadata, TreasuryCap};
24use crate::crypto::{default_hash, deterministic_random_account_key};
25use crate::error::{
26 ExecutionError, ExecutionErrorKind, SuiErrorKind, UserInputError, UserInputResult,
27};
28use crate::error::{SuiError, SuiResult};
29use crate::gas_coin::GAS;
30use crate::is_system_package;
31use crate::layout_resolver::LayoutResolver;
32use crate::move_package::MovePackage;
33use crate::{
34 base_types::{
35 ObjectDigest, ObjectID, ObjectRef, SequenceNumber, SuiAddress, TransactionDigest,
36 },
37 gas_coin::GasCoin,
38};
39use sui_protocol_config::ProtocolConfig;
40
41use self::balance_traversal::BalanceTraversal;
42use self::bounded_visitor::BoundedVisitor;
43
44mod balance_traversal;
45pub mod bounded_visitor;
46pub mod option_visitor;
47pub mod rpc_visitor;
48
49pub const GAS_VALUE_FOR_TESTING: u64 = 300_000_000_000_000;
50pub const OBJECT_START_VERSION: SequenceNumber = SequenceNumber::from_u64(1);
51
52#[serde_as]
53#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
54pub struct MoveObject {
55 type_: MoveObjectType,
57 has_public_transfer: bool,
60 version: SequenceNumber,
63 #[serde_as(as = "Bytes")]
65 contents: Vec<u8>,
66}
67
68pub const ID_END_INDEX: usize = ObjectID::LENGTH;
70
71impl MoveObject {
72 pub unsafe fn new_from_execution(
83 type_: MoveObjectType,
84 has_public_transfer: bool,
85 version: SequenceNumber,
86 contents: Vec<u8>,
87 protocol_config: &ProtocolConfig,
88 system_mutation: bool,
89 ) -> Result<Self, ExecutionError> {
90 let bound = if protocol_config.allow_unbounded_system_objects() && system_mutation {
91 if contents.len() as u64 > protocol_config.max_move_object_size() {
92 debug_fatal!(
93 "System created object (ID = {:?}) of type {:?} and size {} exceeds normal max size {}",
94 MoveObject::id_opt(&contents).ok(),
95 type_,
96 contents.len(),
97 protocol_config.max_move_object_size()
98 );
99 }
100 u64::MAX
101 } else {
102 protocol_config.max_move_object_size()
103 };
104 unsafe {
105 Self::new_from_execution_with_limit(
106 type_,
107 has_public_transfer,
108 version,
109 contents,
110 bound,
111 )
112 }
113 }
114
115 pub unsafe fn new_from_execution_with_limit(
118 type_: MoveObjectType,
119 has_public_transfer: bool,
120 version: SequenceNumber,
121 contents: Vec<u8>,
122 max_move_object_size: u64,
123 ) -> Result<Self, ExecutionError> {
124 debug_assert!(!type_.is_gas_coin() || has_public_transfer);
128 if contents.len() as u64 > max_move_object_size {
129 return Err(ExecutionError::from_kind(
130 ExecutionErrorKind::MoveObjectTooBig {
131 object_size: contents.len() as u64,
132 max_object_size: max_move_object_size,
133 },
134 ));
135 }
136 Ok(Self {
137 type_,
138 has_public_transfer,
139 version,
140 contents,
141 })
142 }
143
144 pub fn new_gas_coin(version: SequenceNumber, id: ObjectID, value: u64) -> Self {
145 unsafe {
147 Self::new_from_execution_with_limit(
148 GasCoin::type_().into(),
149 true,
150 version,
151 GasCoin::new(id, value).to_bcs_bytes(),
152 256,
153 )
154 .unwrap()
155 }
156 }
157
158 pub fn new_coin(coin_type: TypeTag, version: SequenceNumber, id: ObjectID, value: u64) -> Self {
159 unsafe {
161 Self::new_from_execution_with_limit(
162 MoveObjectType::coin(coin_type),
163 true,
164 version,
165 Coin::new(id, value).to_bcs_bytes(),
166 256,
167 )
168 .unwrap()
169 }
170 }
171
172 pub fn type_(&self) -> &MoveObjectType {
173 &self.type_
174 }
175
176 pub fn is_type(&self, s: &StructTag) -> bool {
177 self.type_.is(s)
178 }
179
180 pub fn has_public_transfer(&self) -> bool {
181 self.has_public_transfer
182 }
183
184 pub fn id(&self) -> ObjectID {
185 Self::id_opt(&self.contents).unwrap()
186 }
187
188 pub fn id_opt(contents: &[u8]) -> Result<ObjectID, ObjectIDParseError> {
189 if ID_END_INDEX > contents.len() {
190 return Err(ObjectIDParseError::TryFromSliceError);
191 }
192 ObjectID::try_from(&contents[0..ID_END_INDEX])
193 }
194
195 pub fn get_coin_value_unsafe(&self) -> u64 {
200 debug_assert!(self.type_.is_coin());
201 debug_assert!(self.contents.len() == 40);
203
204 u64::from_le_bytes(<[u8; 8]>::try_from(&self.contents[ID_END_INDEX..]).unwrap())
206 }
207
208 pub fn set_coin_value_unsafe(&mut self, value: u64) {
213 debug_assert!(self.type_.is_coin());
214 debug_assert!(self.contents.len() == 40);
216
217 self.contents.splice(ID_END_INDEX.., value.to_le_bytes());
218 }
219
220 pub fn set_clock_timestamp_ms_unsafe(&mut self, timestamp_ms: u64) {
224 assert!(self.is_clock());
225 assert!(self.contents.len() == 40);
227
228 self.contents
229 .splice(ID_END_INDEX.., timestamp_ms.to_le_bytes());
230 }
231
232 pub fn set_contents_unsafe(&mut self, contents: Vec<u8>) {
233 self.contents = contents;
234 }
235
236 pub fn is_coin(&self) -> bool {
237 self.type_.is_coin()
238 }
239
240 pub fn is_staked_sui(&self) -> bool {
241 self.type_.is_staked_sui()
242 }
243
244 pub fn is_clock(&self) -> bool {
245 self.type_.is(&crate::clock::Clock::type_())
246 }
247
248 pub fn version(&self) -> SequenceNumber {
249 self.version
250 }
251
252 pub fn contents_and_type_equal(&self, other: &Self) -> bool {
253 self.contents == other.contents && self.type_ == other.type_
254 }
255
256 #[cfg(test)]
260 pub fn type_specific_contents(&self) -> &[u8] {
261 &self.contents[ID_END_INDEX..]
262 }
263
264 pub(crate) fn update_contents_advance_epoch_safe_mode(
267 &mut self,
268 new_contents: Vec<u8>,
269 protocol_config: &ProtocolConfig,
270 ) -> Result<(), ExecutionError> {
271 if new_contents.len() as u64 > protocol_config.max_move_object_size() {
272 if protocol_config.allow_unbounded_system_objects() {
273 debug_fatal!(
274 "Safe mode object update (ID = {}) of size {} exceeds normal max size {}",
275 self.id(),
276 new_contents.len(),
277 protocol_config.max_move_object_size()
278 )
279 } else {
280 return Err(ExecutionError::from_kind(
281 ExecutionErrorKind::MoveObjectTooBig {
282 object_size: new_contents.len() as u64,
283 max_object_size: protocol_config.max_move_object_size(),
284 },
285 ));
286 }
287 }
288
289 #[cfg(debug_assertions)]
290 let old_id = self.id();
291 self.contents = new_contents;
292
293 #[cfg(debug_assertions)]
295 debug_assert_eq!(self.id(), old_id);
296
297 Ok(())
298 }
299
300 pub fn increment_version_to(&mut self, next: SequenceNumber) {
303 self.version.increment_to(next);
304 }
305
306 pub fn decrement_version_to(&mut self, prev: SequenceNumber) {
307 self.version.decrement_to(prev);
308 }
309
310 pub fn contents(&self) -> &[u8] {
311 &self.contents
312 }
313
314 pub fn into_contents(self) -> Vec<u8> {
315 self.contents
316 }
317
318 pub fn into_type(self) -> MoveObjectType {
319 self.type_
320 }
321
322 pub fn into_inner(self) -> (MoveObjectType, Vec<u8>) {
323 (self.type_, self.contents)
324 }
325
326 pub fn get_layout(&self, resolver: &impl GetModule) -> Result<MoveStructLayout, SuiError> {
330 Self::get_struct_layout_from_struct_tag(self.type_().clone().into(), resolver)
331 }
332
333 pub fn get_struct_layout_from_struct_tag(
334 struct_tag: StructTag,
335 resolver: &impl GetModule,
336 ) -> Result<MoveStructLayout, SuiError> {
337 let type_ = TypeTag::Struct(Box::new(struct_tag));
338 let layout = TypeLayoutBuilder::build_with_types(&type_, resolver).map_err(|e| {
339 SuiErrorKind::ObjectSerializationError {
340 error: e.to_string(),
341 }
342 })?;
343 match layout {
344 MoveTypeLayout::Struct(l) => Ok(*l),
345 _ => unreachable!(
346 "We called build_with_types on Struct type, should get a struct layout"
347 ),
348 }
349 }
350
351 pub fn to_move_struct(&self, layout: &MoveStructLayout) -> Result<MoveStruct, SuiError> {
353 BoundedVisitor::deserialize_struct(&self.contents, layout).map_err(|e| {
354 SuiErrorKind::ObjectSerializationError {
355 error: e.to_string(),
356 }
357 .into()
358 })
359 }
360
361 pub fn to_move_struct_with_resolver(
363 &self,
364 resolver: &impl GetModule,
365 ) -> Result<MoveStruct, SuiError> {
366 self.to_move_struct(&self.get_layout(resolver)?)
367 }
368
369 pub fn to_rust<'de, T: Deserialize<'de>>(&'de self) -> Option<T> {
370 bcs::from_bytes(self.contents()).ok()
371 }
372
373 pub fn object_size_for_gas_metering(&self) -> usize {
378 let serialized_type_tag_size =
379 bcs::serialized_size(&self.type_).expect("Serializing type tag should not fail");
380 self.contents.len() + serialized_type_tag_size + 1 + 8
383 }
384
385 pub fn get_total_sui(&self, layout_resolver: &mut dyn LayoutResolver) -> Result<u64, SuiError> {
387 if self.type_.is_gas_coin() {
388 let balance = self.get_coin_value_unsafe();
389 Ok(balance)
390 } else if self.type_.coin_type_maybe().is_some() {
391 Ok(0)
393 } else if self.type_.is_sui_balance_accumulator_field() {
394 let value = AccumulatorValue::try_from(self)?;
395 let AccumulatorValue::U128(v) = value;
396 assert!(
399 v.value <= u64::MAX as u128,
400 "SUI balance cannot exceed u64::MAX"
401 );
402 Ok(v.value as u64)
403 } else {
404 let layout = layout_resolver.get_annotated_layout(&self.type_().clone().into())?;
405
406 let mut traversal = BalanceTraversal::default();
407 MoveValue::visit_deserialize(&self.contents, &layout.into_layout(), &mut traversal)
408 .map_err(|e| SuiErrorKind::ObjectSerializationError {
409 error: e.to_string(),
410 })?;
411
412 Ok(traversal
413 .finish()
414 .get(&GAS::type_tag())
415 .copied()
416 .unwrap_or(0))
417 }
418 }
419}
420
421#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
422#[allow(clippy::large_enum_variant)]
423pub enum Data {
424 Move(MoveObject),
426 Package(MovePackage),
428 }
430
431impl Data {
432 pub fn try_as_move(&self) -> Option<&MoveObject> {
433 use Data::*;
434 match self {
435 Move(m) => Some(m),
436 Package(_) => None,
437 }
438 }
439
440 pub fn try_as_move_mut(&mut self) -> Option<&mut MoveObject> {
441 use Data::*;
442 match self {
443 Move(m) => Some(m),
444 Package(_) => None,
445 }
446 }
447
448 pub fn try_as_package(&self) -> Option<&MovePackage> {
449 use Data::*;
450 match self {
451 Move(_) => None,
452 Package(p) => Some(p),
453 }
454 }
455
456 pub fn try_as_package_mut(&mut self) -> Option<&mut MovePackage> {
457 use Data::*;
458 match self {
459 Move(_) => None,
460 Package(p) => Some(p),
461 }
462 }
463
464 pub fn try_into_package(self) -> Option<MovePackage> {
465 use Data::*;
466 match self {
467 Move(_) => None,
468 Package(p) => Some(p),
469 }
470 }
471
472 pub fn type_(&self) -> Option<&MoveObjectType> {
473 use Data::*;
474 match self {
475 Move(m) => Some(m.type_()),
476 Package(_) => None,
477 }
478 }
479
480 pub fn struct_tag(&self) -> Option<StructTag> {
481 use Data::*;
482 match self {
483 Move(m) => Some(m.type_().clone().into()),
484 Package(_) => None,
485 }
486 }
487
488 pub fn id(&self) -> ObjectID {
489 match self {
490 Self::Move(v) => v.id(),
491 Self::Package(m) => m.id(),
492 }
493 }
494}
495
496#[derive(
497 Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash, JsonSchema, Ord, PartialOrd,
498)]
499#[cfg_attr(feature = "fuzzing", derive(proptest_derive::Arbitrary))]
500pub enum Owner {
501 AddressOwner(SuiAddress),
503 ObjectOwner(SuiAddress),
506 Shared {
508 initial_shared_version: SequenceNumber,
510 },
511 Immutable,
513 ConsensusAddressOwner {
515 start_version: SequenceNumber,
519 owner: SuiAddress,
521 },
522}
523
524impl Owner {
525 pub fn get_address_owner_address(&self) -> SuiResult<SuiAddress> {
528 match self {
529 Self::AddressOwner(address) => Ok(*address),
530 Self::Shared { .. }
531 | Self::Immutable
532 | Self::ObjectOwner(_)
533 | Self::ConsensusAddressOwner { .. } => Err(SuiErrorKind::UnexpectedOwnerType.into()),
534 }
535 }
536
537 pub fn get_owner_address(&self) -> SuiResult<SuiAddress> {
541 match self {
542 Self::AddressOwner(address)
543 | Self::ObjectOwner(address)
544 | Self::ConsensusAddressOwner { owner: address, .. } => Ok(*address),
545 Self::Shared { .. } | Self::Immutable => Err(SuiErrorKind::UnexpectedOwnerType.into()),
546 }
547 }
548
549 pub fn start_version(&self) -> Option<SequenceNumber> {
552 match self {
553 Self::Shared {
554 initial_shared_version,
555 } => Some(*initial_shared_version),
556 Self::ConsensusAddressOwner { start_version, .. } => Some(*start_version),
557 Self::Immutable | Self::AddressOwner(_) | Self::ObjectOwner(_) => None,
558 }
559 }
560
561 pub fn is_immutable(&self) -> bool {
562 matches!(self, Owner::Immutable)
563 }
564
565 pub fn is_address_owned(&self) -> bool {
566 matches!(self, Owner::AddressOwner(_))
567 }
568
569 pub fn is_child_object(&self) -> bool {
570 matches!(self, Owner::ObjectOwner(_))
571 }
572
573 pub fn is_shared(&self) -> bool {
574 matches!(self, Owner::Shared { .. })
575 }
576
577 pub fn is_consensus(&self) -> bool {
578 matches!(
579 self,
580 Owner::Shared { .. } | Owner::ConsensusAddressOwner { .. }
581 )
582 }
583}
584
585impl PartialEq<ObjectID> for Owner {
586 fn eq(&self, other: &ObjectID) -> bool {
587 let other_id: SuiAddress = (*other).into();
588 match self {
589 Self::ObjectOwner(id) => id == &other_id,
590 Self::AddressOwner(_)
591 | Self::Shared { .. }
592 | Self::Immutable
593 | Self::ConsensusAddressOwner { .. } => false,
594 }
595 }
596}
597
598impl Display for Owner {
599 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
600 match self {
601 Self::AddressOwner(address) => {
602 write!(f, "Account Address ( {} )", address)
603 }
604 Self::ObjectOwner(address) => {
605 write!(f, "Object ID: ( {} )", address)
606 }
607 Self::Immutable => {
608 write!(f, "Immutable")
609 }
610 Self::Shared {
611 initial_shared_version,
612 } => {
613 write!(f, "Shared( {} )", initial_shared_version.value())
614 }
615 Self::ConsensusAddressOwner {
616 start_version,
617 owner,
618 } => {
619 write!(
620 f,
621 "ConsensusAddressOwner( {}, {} )",
622 start_version.value(),
623 owner
624 )
625 }
626 }
627 }
628}
629
630#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
631#[serde(rename = "Object")]
632pub struct ObjectInner {
633 pub data: Data,
635 pub owner: Owner,
637 pub previous_transaction: TransactionDigest,
639 pub storage_rebate: u64,
643}
644
645#[derive(Eq, PartialEq, Clone, Deserialize, Serialize, Hash)]
646#[serde(from = "ObjectInner")]
647pub struct Object(Arc<ObjectInner>);
648
649fn is_object_debug_verbose() -> bool {
650 static SUI_OBJECT_DEBUG_VERBOSE: Lazy<bool> =
651 Lazy::new(|| std::env::var("SUI_OBJECT_DEBUG_VERBOSE").is_ok());
652 *SUI_OBJECT_DEBUG_VERBOSE
653}
654
655impl std::fmt::Debug for Object {
656 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
657 if is_object_debug_verbose() {
658 (*self.0).fmt(f)
660 } else {
661 f.debug_struct("Object")
662 .field("id", &self.id())
663 .field("version", &self.version())
664 .field("owner", &self.owner())
665 .finish()
666 }
667 }
668}
669
670impl From<ObjectInner> for Object {
671 fn from(inner: ObjectInner) -> Self {
672 Self(Arc::new(inner))
673 }
674}
675
676impl Object {
677 pub fn into_inner(self) -> ObjectInner {
678 match Arc::try_unwrap(self.0) {
679 Ok(inner) => inner,
680 Err(inner_arc) => (*inner_arc).clone(),
681 }
682 }
683
684 pub fn as_inner(&self) -> &ObjectInner {
685 &self.0
686 }
687
688 pub fn owner(&self) -> &Owner {
689 &self.0.owner
690 }
691
692 pub fn new_from_genesis(
693 data: Data,
694 owner: Owner,
695 previous_transaction: TransactionDigest,
696 ) -> Self {
697 ObjectInner {
698 data,
699 owner,
700 previous_transaction,
701 storage_rebate: 0,
702 }
703 .into()
704 }
705
706 pub fn new_move(o: MoveObject, owner: Owner, previous_transaction: TransactionDigest) -> Self {
708 ObjectInner {
709 data: Data::Move(o),
710 owner,
711 previous_transaction,
712 storage_rebate: 0,
713 }
714 .into()
715 }
716
717 pub fn new_package_from_data(data: Data, previous_transaction: TransactionDigest) -> Self {
718 ObjectInner {
719 data,
720 owner: Owner::Immutable,
721 previous_transaction,
722 storage_rebate: 0,
723 }
724 .into()
725 }
726
727 pub fn new_from_package(package: MovePackage, previous_transaction: TransactionDigest) -> Self {
729 Self::new_package_from_data(Data::Package(package), previous_transaction)
730 }
731
732 pub fn new_package<'p>(
733 modules: &[CompiledModule],
734 previous_transaction: TransactionDigest,
735 protocol_config: &ProtocolConfig,
736 dependencies: impl IntoIterator<Item = &'p MovePackage>,
737 ) -> Result<Self, ExecutionError> {
738 Ok(Self::new_package_from_data(
739 Data::Package(MovePackage::new_initial(
740 modules,
741 protocol_config,
742 dependencies,
743 )?),
744 previous_transaction,
745 ))
746 }
747
748 pub fn new_upgraded_package<'p>(
749 previous_package: &MovePackage,
750 new_package_id: ObjectID,
751 modules: &[CompiledModule],
752 previous_transaction: TransactionDigest,
753 protocol_config: &ProtocolConfig,
754 dependencies: impl IntoIterator<Item = &'p MovePackage>,
755 ) -> Result<Self, ExecutionError> {
756 Ok(Self::new_package_from_data(
757 Data::Package(previous_package.new_upgraded(
758 new_package_id,
759 modules,
760 protocol_config,
761 dependencies,
762 )?),
763 previous_transaction,
764 ))
765 }
766
767 pub fn new_package_for_testing(
768 modules: &[CompiledModule],
769 previous_transaction: TransactionDigest,
770 dependencies: impl IntoIterator<Item = MovePackage>,
771 ) -> Result<Self, ExecutionError> {
772 let dependencies: Vec<_> = dependencies.into_iter().collect();
773 let config = ProtocolConfig::get_for_max_version_UNSAFE();
774 Self::new_package(modules, previous_transaction, &config, &dependencies)
775 }
776
777 pub fn new_system_package(
780 modules: &[CompiledModule],
781 version: SequenceNumber,
782 dependencies: Vec<ObjectID>,
783 previous_transaction: TransactionDigest,
784 ) -> Self {
785 let ret = Self::new_package_from_data(
786 Data::Package(MovePackage::new_system(version, modules, dependencies)),
787 previous_transaction,
788 );
789
790 #[cfg(not(msim))]
791 assert!(ret.is_system_package());
792
793 ret
794 }
795}
796
797impl std::ops::Deref for Object {
798 type Target = ObjectInner;
799 fn deref(&self) -> &Self::Target {
800 &self.0
801 }
802}
803
804impl std::ops::DerefMut for Object {
805 fn deref_mut(&mut self) -> &mut Self::Target {
806 Arc::make_mut(&mut self.0)
807 }
808}
809
810impl ObjectInner {
811 pub fn is_system_package(&self) -> bool {
813 self.is_package() && is_system_package(self.id())
814 }
815
816 pub fn is_immutable(&self) -> bool {
817 self.owner.is_immutable()
818 }
819
820 pub fn is_address_owned(&self) -> bool {
821 self.owner.is_address_owned()
822 }
823
824 pub fn is_child_object(&self) -> bool {
825 self.owner.is_child_object()
826 }
827
828 pub fn is_shared(&self) -> bool {
829 self.owner.is_shared()
830 }
831
832 pub fn is_consensus(&self) -> bool {
833 self.owner.is_consensus()
834 }
835
836 pub fn get_single_owner(&self) -> Option<SuiAddress> {
837 self.owner.get_owner_address().ok()
838 }
839
840 pub fn get_owner_and_id(&self) -> Option<(Owner, ObjectID)> {
843 Some((self.owner.clone(), self.id()))
844 }
845
846 pub fn is_package(&self) -> bool {
848 matches!(&self.data, Data::Package(_))
849 }
850
851 pub fn compute_object_reference(&self) -> ObjectRef {
852 (self.id(), self.version(), self.digest())
853 }
854
855 pub fn compute_full_object_reference(&self) -> FullObjectRef {
856 FullObjectRef(self.full_id(), self.version(), self.digest())
857 }
858
859 pub fn digest(&self) -> ObjectDigest {
860 ObjectDigest::new(default_hash(self))
861 }
862
863 pub fn id(&self) -> ObjectID {
864 use Data::*;
865
866 match &self.data {
867 Move(v) => v.id(),
868 Package(m) => m.id(),
869 }
870 }
871
872 pub fn full_id(&self) -> FullObjectID {
873 let id = self.id();
874 if let Some(start_version) = self.owner.start_version() {
875 FullObjectID::Consensus((id, start_version))
876 } else {
877 FullObjectID::Fastpath(id)
878 }
879 }
880
881 pub fn version(&self) -> SequenceNumber {
882 use Data::*;
883
884 match &self.data {
885 Move(o) => o.version(),
886 Package(p) => p.version(),
887 }
888 }
889
890 pub fn type_(&self) -> Option<&MoveObjectType> {
891 self.data.type_()
892 }
893
894 pub fn struct_tag(&self) -> Option<StructTag> {
895 self.data.struct_tag()
896 }
897
898 pub fn is_coin(&self) -> bool {
899 if let Some(move_object) = self.data.try_as_move() {
900 move_object.type_().is_coin()
901 } else {
902 false
903 }
904 }
905
906 pub fn is_gas_coin(&self) -> bool {
907 if let Some(move_object) = self.data.try_as_move() {
908 move_object.type_().is_gas_coin()
909 } else {
910 false
911 }
912 }
913
914 pub fn as_coin_maybe(&self) -> Option<Coin> {
917 if let Some(move_object) = self.data.try_as_move() {
918 if move_object.type_().is_coin() {
919 let coin: Coin = bcs::from_bytes(move_object.contents()).ok()?;
920 Some(coin)
921 } else {
922 None
923 }
924 } else {
925 None
926 }
927 }
928
929 pub fn coin_type_maybe(&self) -> Option<TypeTag> {
930 if let Some(move_object) = self.data.try_as_move() {
931 move_object.type_().coin_type_maybe()
932 } else {
933 None
934 }
935 }
936
937 pub fn get_coin_value_unsafe(&self) -> u64 {
942 self.data.try_as_move().unwrap().get_coin_value_unsafe()
943 }
944
945 pub fn object_size_for_gas_metering(&self) -> usize {
950 const DEFAULT_OWNER_SIZE: usize = 40;
951 const TRANSACTION_DIGEST_SIZE: usize = 32;
952 const STORAGE_REBATE_SIZE: usize = 8;
953
954 let meta_data_size = DEFAULT_OWNER_SIZE + TRANSACTION_DIGEST_SIZE + STORAGE_REBATE_SIZE;
955 let data_size = match &self.data {
956 Data::Move(m) => m.object_size_for_gas_metering(),
957 Data::Package(p) => p.object_size_for_gas_metering(),
958 };
959 meta_data_size + data_size
960 }
961
962 pub fn transfer(&mut self, new_owner: SuiAddress) {
964 self.owner = Owner::AddressOwner(new_owner);
965 }
966
967 pub fn get_layout(
971 &self,
972 resolver: &impl GetModule,
973 ) -> Result<Option<MoveStructLayout>, SuiError> {
974 match &self.data {
975 Data::Move(m) => Ok(Some(m.get_layout(resolver)?)),
976 Data::Package(_) => Ok(None),
977 }
978 }
979
980 pub fn get_move_template_type(&self) -> SuiResult<TypeTag> {
984 let move_struct = self
985 .data
986 .struct_tag()
987 .ok_or_else(|| SuiErrorKind::TypeError {
988 error: "Object must be a Move object".to_owned(),
989 })?;
990 fp_ensure!(
991 move_struct.type_params.len() == 1,
992 SuiErrorKind::TypeError {
993 error: "Move object struct must have one type parameter".to_owned()
994 }
995 .into()
996 );
997 let type_tag = move_struct.type_params[0].clone();
999 Ok(type_tag)
1000 }
1001
1002 pub fn to_rust<'de, T: Deserialize<'de>>(&'de self) -> Option<T> {
1003 self.data.try_as_move().and_then(|data| data.to_rust())
1004 }
1005}
1006
1007impl Object {
1009 pub fn get_total_sui(&self, layout_resolver: &mut dyn LayoutResolver) -> Result<u64, SuiError> {
1011 Ok(self.storage_rebate
1012 + match &self.data {
1013 Data::Move(m) => m.get_total_sui(layout_resolver)?,
1014 Data::Package(_) => 0,
1015 })
1016 }
1017
1018 pub fn immutable_with_id_for_testing(id: ObjectID) -> Self {
1019 let data = Data::Move(MoveObject {
1020 type_: GasCoin::type_().into(),
1021 has_public_transfer: true,
1022 version: OBJECT_START_VERSION,
1023 contents: GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
1024 });
1025 ObjectInner {
1026 owner: Owner::Immutable,
1027 data,
1028 previous_transaction: TransactionDigest::genesis_marker(),
1029 storage_rebate: 0,
1030 }
1031 .into()
1032 }
1033
1034 pub fn immutable_for_testing() -> Self {
1035 thread_local! {
1036 static IMMUTABLE_OBJECT_ID: ObjectID = ObjectID::random();
1037 }
1038
1039 Self::immutable_with_id_for_testing(IMMUTABLE_OBJECT_ID.with(|id| *id))
1040 }
1041
1042 pub fn shared_for_testing() -> Object {
1044 let id = ObjectID::random();
1045 let obj = MoveObject::new_gas_coin(OBJECT_START_VERSION, id, 10);
1046 let owner = Owner::Shared {
1047 initial_shared_version: obj.version(),
1048 };
1049 Object::new_move(obj, owner, TransactionDigest::genesis_marker())
1050 }
1051
1052 pub fn with_id_owner_gas_for_testing(id: ObjectID, owner: SuiAddress, gas: u64) -> Self {
1053 let data = Data::Move(MoveObject {
1054 type_: GasCoin::type_().into(),
1055 has_public_transfer: true,
1056 version: OBJECT_START_VERSION,
1057 contents: GasCoin::new(id, gas).to_bcs_bytes(),
1058 });
1059 ObjectInner {
1060 owner: Owner::AddressOwner(owner),
1061 data,
1062 previous_transaction: TransactionDigest::genesis_marker(),
1063 storage_rebate: 0,
1064 }
1065 .into()
1066 }
1067
1068 pub fn treasury_cap_for_testing(struct_tag: StructTag, treasury_cap: TreasuryCap) -> Self {
1069 let data = Data::Move(MoveObject {
1070 type_: TreasuryCap::type_(struct_tag).into(),
1071 has_public_transfer: true,
1072 version: OBJECT_START_VERSION,
1073 contents: bcs::to_bytes(&treasury_cap).expect("Failed to serialize"),
1074 });
1075 ObjectInner {
1076 owner: Owner::Immutable,
1077 data,
1078 previous_transaction: TransactionDigest::genesis_marker(),
1079 storage_rebate: 0,
1080 }
1081 .into()
1082 }
1083
1084 pub fn coin_metadata_for_testing(struct_tag: StructTag, metadata: CoinMetadata) -> Self {
1085 let data = Data::Move(MoveObject {
1086 type_: CoinMetadata::type_(struct_tag).into(),
1087 has_public_transfer: true,
1088 version: OBJECT_START_VERSION,
1089 contents: bcs::to_bytes(&metadata).expect("Failed to serialize"),
1090 });
1091 ObjectInner {
1092 owner: Owner::Immutable,
1093 data,
1094 previous_transaction: TransactionDigest::genesis_marker(),
1095 storage_rebate: 0,
1096 }
1097 .into()
1098 }
1099
1100 pub fn with_object_owner_for_testing(id: ObjectID, owner: ObjectID) -> Self {
1101 let data = Data::Move(MoveObject {
1102 type_: GasCoin::type_().into(),
1103 has_public_transfer: true,
1104 version: OBJECT_START_VERSION,
1105 contents: GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
1106 });
1107 ObjectInner {
1108 owner: Owner::ObjectOwner(owner.into()),
1109 data,
1110 previous_transaction: TransactionDigest::genesis_marker(),
1111 storage_rebate: 0,
1112 }
1113 .into()
1114 }
1115
1116 pub fn with_id_owner_for_testing(id: ObjectID, owner: SuiAddress) -> Self {
1117 Self::with_id_owner_gas_for_testing(id, owner, GAS_VALUE_FOR_TESTING)
1119 }
1120
1121 pub fn with_id_owner_version_for_testing(
1122 id: ObjectID,
1123 version: SequenceNumber,
1124 owner: Owner,
1125 ) -> Self {
1126 let data = Data::Move(MoveObject {
1127 type_: GasCoin::type_().into(),
1128 has_public_transfer: true,
1129 version,
1130 contents: GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
1131 });
1132 ObjectInner {
1133 owner,
1134 data,
1135 previous_transaction: TransactionDigest::genesis_marker(),
1136 storage_rebate: 0,
1137 }
1138 .into()
1139 }
1140
1141 pub fn with_owner_for_testing(owner: SuiAddress) -> Self {
1142 Self::with_id_owner_for_testing(ObjectID::random(), owner)
1143 }
1144
1145 pub fn new_gas_with_balance_and_owner_for_testing(value: u64, owner: SuiAddress) -> Self {
1148 let obj = MoveObject::new_gas_coin(OBJECT_START_VERSION, ObjectID::random(), value);
1149 Object::new_move(
1150 obj,
1151 Owner::AddressOwner(owner),
1152 TransactionDigest::genesis_marker(),
1153 )
1154 }
1155
1156 pub fn new_gas_for_testing() -> Self {
1158 let gas_object_id = ObjectID::random();
1159 let (owner, _) = deterministic_random_account_key();
1160 Object::with_id_owner_for_testing(gas_object_id, owner)
1161 }
1162}
1163
1164pub fn generate_test_gas_objects() -> Vec<Object> {
1166 thread_local! {
1167 static GAS_OBJECTS: Vec<Object> = (0..50)
1168 .map(|_| {
1169 let gas_object_id = ObjectID::random();
1170 let (owner, _) = deterministic_random_account_key();
1171 Object::with_id_owner_for_testing(gas_object_id, owner)
1172 })
1173 .collect();
1174 }
1175
1176 GAS_OBJECTS.with(|v| v.clone())
1177}
1178
1179#[allow(clippy::large_enum_variant)]
1180#[derive(Serialize, Deserialize, Debug)]
1181#[serde(tag = "status", content = "details")]
1182pub enum ObjectRead {
1183 NotExists(ObjectID),
1184 Exists(ObjectRef, Object, Option<MoveStructLayout>),
1185 Deleted(ObjectRef),
1186}
1187
1188impl ObjectRead {
1189 pub fn into_object(self) -> UserInputResult<Object> {
1192 match self {
1193 Self::Deleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: oref }),
1194 Self::NotExists(id) => Err(UserInputError::ObjectNotFound {
1195 object_id: id,
1196 version: None,
1197 }),
1198 Self::Exists(_, o, _) => Ok(o),
1199 }
1200 }
1201
1202 pub fn object(&self) -> UserInputResult<&Object> {
1203 match self {
1204 Self::Deleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: *oref }),
1205 Self::NotExists(id) => Err(UserInputError::ObjectNotFound {
1206 object_id: *id,
1207 version: None,
1208 }),
1209 Self::Exists(_, o, _) => Ok(o),
1210 }
1211 }
1212
1213 pub fn object_id(&self) -> ObjectID {
1214 match self {
1215 Self::Deleted(oref) => oref.0,
1216 Self::NotExists(id) => *id,
1217 Self::Exists(oref, _, _) => oref.0,
1218 }
1219 }
1220}
1221
1222impl Display for ObjectRead {
1223 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1224 match self {
1225 Self::Deleted(oref) => {
1226 write!(f, "ObjectRead::Deleted ({:?})", oref)
1227 }
1228 Self::NotExists(id) => {
1229 write!(f, "ObjectRead::NotExists ({:?})", id)
1230 }
1231 Self::Exists(oref, _, _) => {
1232 write!(f, "ObjectRead::Exists ({:?})", oref)
1233 }
1234 }
1235 }
1236}
1237
1238#[allow(clippy::large_enum_variant)]
1239#[derive(Serialize, Deserialize, Debug)]
1240#[serde(tag = "status", content = "details")]
1241pub enum PastObjectRead {
1242 ObjectNotExists(ObjectID),
1244 ObjectDeleted(ObjectRef),
1246 VersionFound(ObjectRef, Object, Option<MoveStructLayout>),
1248 VersionNotFound(ObjectID, SequenceNumber),
1250 VersionTooHigh {
1252 object_id: ObjectID,
1253 asked_version: SequenceNumber,
1254 latest_version: SequenceNumber,
1255 },
1256}
1257
1258impl PastObjectRead {
1259 pub fn into_object(self) -> UserInputResult<Object> {
1261 match self {
1262 Self::ObjectDeleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: oref }),
1263 Self::ObjectNotExists(id) => Err(UserInputError::ObjectNotFound {
1264 object_id: id,
1265 version: None,
1266 }),
1267 Self::VersionFound(_, o, _) => Ok(o),
1268 Self::VersionNotFound(object_id, version) => Err(UserInputError::ObjectNotFound {
1269 object_id,
1270 version: Some(version),
1271 }),
1272 Self::VersionTooHigh {
1273 object_id,
1274 asked_version,
1275 latest_version,
1276 } => Err(UserInputError::ObjectSequenceNumberTooHigh {
1277 object_id,
1278 asked_version,
1279 latest_version,
1280 }),
1281 }
1282 }
1283}
1284
1285impl Display for PastObjectRead {
1286 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1287 match self {
1288 Self::ObjectDeleted(oref) => {
1289 write!(f, "PastObjectRead::ObjectDeleted ({:?})", oref)
1290 }
1291 Self::ObjectNotExists(id) => {
1292 write!(f, "PastObjectRead::ObjectNotExists ({:?})", id)
1293 }
1294 Self::VersionFound(oref, _, _) => {
1295 write!(f, "PastObjectRead::VersionFound ({:?})", oref)
1296 }
1297 Self::VersionNotFound(object_id, version) => {
1298 write!(
1299 f,
1300 "PastObjectRead::VersionNotFound ({:?}, asked sequence number {:?})",
1301 object_id, version
1302 )
1303 }
1304 Self::VersionTooHigh {
1305 object_id,
1306 asked_version,
1307 latest_version,
1308 } => {
1309 write!(
1310 f,
1311 "PastObjectRead::VersionTooHigh ({:?}, asked sequence number {:?}, latest sequence number {:?})",
1312 object_id, asked_version, latest_version
1313 )
1314 }
1315 }
1316 }
1317}
1318
1319#[cfg(test)]
1320mod tests {
1321 use crate::object::{OBJECT_START_VERSION, Object, Owner};
1322 use crate::{
1323 base_types::{ObjectID, SuiAddress, TransactionDigest},
1324 gas_coin::GasCoin,
1325 };
1326
1327 #[test]
1329 fn test_object_digest_and_serialized_format() {
1330 let g =
1331 GasCoin::new_for_testing_with_id(ObjectID::ZERO, 123).to_object(OBJECT_START_VERSION);
1332 let o = Object::new_move(
1333 g,
1334 Owner::AddressOwner(SuiAddress::ZERO),
1335 TransactionDigest::ZERO,
1336 );
1337 let bytes = bcs::to_bytes(&o).unwrap();
1338
1339 assert_eq!(
1340 bytes,
1341 [
1342 0, 1, 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,
1343 0, 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,
1344 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,
1345 0, 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,
1346 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1347 ]
1348 );
1349
1350 let objref = format!("{:?}", o.compute_object_reference());
1351 assert_eq!(
1352 objref,
1353 "(0x0000000000000000000000000000000000000000000000000000000000000000, SequenceNumber(1), o#59tZq65HVqZjUyNtD7BCGLTD87N5cpayYwEFrtwR4aMz)"
1354 );
1355 }
1356
1357 #[test]
1358 fn test_get_coin_value_unsafe() {
1359 fn test_for_value(v: u64) {
1360 let g = GasCoin::new_for_testing(v).to_object(OBJECT_START_VERSION);
1361 assert_eq!(g.get_coin_value_unsafe(), v);
1362 assert_eq!(GasCoin::try_from(&g).unwrap().value(), v);
1363 }
1364
1365 test_for_value(0);
1366 test_for_value(1);
1367 test_for_value(8);
1368 test_for_value(9);
1369 test_for_value(u8::MAX as u64);
1370 test_for_value(u8::MAX as u64 + 1);
1371 test_for_value(u16::MAX as u64);
1372 test_for_value(u16::MAX as u64 + 1);
1373 test_for_value(u32::MAX as u64);
1374 test_for_value(u32::MAX as u64 + 1);
1375 test_for_value(u64::MAX);
1376 }
1377
1378 #[test]
1379 fn test_set_coin_value_unsafe() {
1380 fn test_for_value(v: u64) {
1381 let mut g = GasCoin::new_for_testing(u64::MAX).to_object(OBJECT_START_VERSION);
1382 g.set_coin_value_unsafe(v);
1383 assert_eq!(g.get_coin_value_unsafe(), v);
1384 assert_eq!(GasCoin::try_from(&g).unwrap().value(), v);
1385 assert_eq!(g.version(), OBJECT_START_VERSION);
1386 assert_eq!(g.contents().len(), 40);
1387 }
1388
1389 test_for_value(0);
1390 test_for_value(1);
1391 test_for_value(8);
1392 test_for_value(9);
1393 test_for_value(u8::MAX as u64);
1394 test_for_value(u8::MAX as u64 + 1);
1395 test_for_value(u16::MAX as u64);
1396 test_for_value(u16::MAX as u64 + 1);
1397 test_for_value(u32::MAX as u64);
1398 test_for_value(u32::MAX as u64 + 1);
1399 test_for_value(u64::MAX);
1400 }
1401}