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