1use std::{
6 collections::BTreeMap,
7 convert::TryFrom,
8 fmt::{Debug, Display, Formatter},
9 mem::size_of,
10 sync::Arc,
11};
12
13use iota_protocol_config::ProtocolConfig;
14use move_binary_format::CompiledModule;
15use move_bytecode_utils::{layout::TypeLayoutBuilder, module_cache::GetModule};
16use move_core_types::{
17 annotated_value::{MoveStruct, MoveStructLayout, MoveTypeLayout, MoveValue},
18 language_storage::{StructTag, TypeTag},
19};
20use schemars::JsonSchema;
21use serde::{Deserialize, Serialize};
22use serde_with::{Bytes, serde_as};
23
24use self::{balance_traversal::BalanceTraversal, bounded_visitor::BoundedVisitor};
25use crate::{
26 balance::Balance,
27 base_types::{
28 IotaAddress, MoveObjectType, ObjectDigest, ObjectID, ObjectIDParseError, ObjectRef,
29 SequenceNumber, TransactionDigest,
30 },
31 coin::{Coin, CoinMetadata, TreasuryCap},
32 crypto::{default_hash, deterministic_random_account_key},
33 error::{
34 ExecutionError, ExecutionErrorKind, IotaError, IotaResult, UserInputError, UserInputResult,
35 },
36 gas_coin::{GAS, GasCoin},
37 is_system_package,
38 layout_resolver::LayoutResolver,
39 move_package::MovePackage,
40 timelock::timelock::TimeLock,
41};
42
43mod balance_traversal;
44pub mod bounded_visitor;
45
46pub const GAS_VALUE_FOR_TESTING: u64 = 300_000_000_000_000;
47pub const OBJECT_START_VERSION: SequenceNumber = SequenceNumber::from_u64(1);
48
49#[serde_as]
50#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
51pub struct MoveObject {
52 pub(crate) type_: MoveObjectType,
54 pub(crate) version: SequenceNumber,
58 #[serde_as(as = "Bytes")]
60 pub(crate) contents: Vec<u8>,
61}
62
63pub const ID_END_INDEX: usize = ObjectID::LENGTH;
65
66impl MoveObject {
67 pub fn new_from_execution(
70 type_: MoveObjectType,
71 version: SequenceNumber,
72 contents: Vec<u8>,
73 protocol_config: &ProtocolConfig,
74 ) -> Result<Self, ExecutionError> {
75 Self::new_from_execution_with_limit(
76 type_,
77 version,
78 contents,
79 protocol_config.max_move_object_size(),
80 )
81 }
82
83 pub fn new_from_execution_with_limit(
86 type_: MoveObjectType,
87 version: SequenceNumber,
88 contents: Vec<u8>,
89 max_move_object_size: u64,
90 ) -> Result<Self, ExecutionError> {
91 if contents.len() as u64 > max_move_object_size {
92 return Err(ExecutionError::from_kind(
93 ExecutionErrorKind::MoveObjectTooBig {
94 object_size: contents.len() as u64,
95 max_object_size: max_move_object_size,
96 },
97 ));
98 }
99 Ok(Self {
100 type_,
101 version,
102 contents,
103 })
104 }
105
106 pub fn new_gas_coin(version: SequenceNumber, id: ObjectID, value: u64) -> Self {
107 {
109 Self::new_from_execution_with_limit(
110 GasCoin::type_().into(),
111 version,
112 GasCoin::new(id, value).to_bcs_bytes(),
113 256,
114 )
115 .unwrap()
116 }
117 }
118
119 pub fn new_coin(
120 coin_type: MoveObjectType,
121 version: SequenceNumber,
122 id: ObjectID,
123 value: u64,
124 ) -> Self {
125 {
127 Self::new_from_execution_with_limit(
128 coin_type,
129 version,
130 GasCoin::new(id, value).to_bcs_bytes(),
131 256,
132 )
133 .unwrap()
134 }
135 }
136
137 pub fn type_(&self) -> &MoveObjectType {
138 &self.type_
139 }
140
141 pub fn is_type(&self, s: &StructTag) -> bool {
142 self.type_.is(s)
143 }
144
145 pub fn id(&self) -> ObjectID {
146 Self::id_opt(&self.contents).unwrap()
147 }
148
149 pub fn id_opt(contents: &[u8]) -> Result<ObjectID, ObjectIDParseError> {
150 if ID_END_INDEX > contents.len() {
151 return Err(ObjectIDParseError::TryFromSlice);
152 }
153 ObjectID::try_from(&contents[0..ID_END_INDEX])
154 }
155
156 pub fn get_coin_value_unsafe(&self) -> u64 {
161 debug_assert!(self.type_.is_coin());
162 debug_assert!(self.contents.len() == 40);
164
165 u64::from_le_bytes(<[u8; 8]>::try_from(&self.contents[ID_END_INDEX..]).unwrap())
167 }
168
169 pub fn set_coin_value_unsafe(&mut self, value: u64) {
175 debug_assert!(self.type_.is_coin());
176 debug_assert!(self.contents.len() == 40);
178
179 self.contents.splice(ID_END_INDEX.., value.to_le_bytes());
180 }
181
182 pub fn set_clock_timestamp_ms_unsafe(&mut self, timestamp_ms: u64) {
186 assert!(self.is_clock());
187 assert!(self.contents.len() == 40);
189
190 self.contents
191 .splice(ID_END_INDEX.., timestamp_ms.to_le_bytes());
192 }
193
194 pub fn is_coin(&self) -> bool {
195 self.type_.is_coin()
196 }
197
198 pub fn is_staked_iota(&self) -> bool {
199 self.type_.is_staked_iota()
200 }
201
202 pub fn is_clock(&self) -> bool {
203 self.type_.is(&crate::clock::Clock::type_())
204 }
205
206 pub fn version(&self) -> SequenceNumber {
207 self.version
208 }
209
210 #[cfg(test)]
215 pub fn type_specific_contents(&self) -> &[u8] {
216 &self.contents[ID_END_INDEX..]
217 }
218
219 pub fn update_contents(
221 &mut self,
222 new_contents: Vec<u8>,
223 protocol_config: &ProtocolConfig,
224 ) -> Result<(), ExecutionError> {
225 self.update_contents_with_limit(new_contents, protocol_config.max_move_object_size())
226 }
227
228 fn update_contents_with_limit(
229 &mut self,
230 new_contents: Vec<u8>,
231 max_move_object_size: u64,
232 ) -> Result<(), ExecutionError> {
233 if new_contents.len() as u64 > max_move_object_size {
234 return Err(ExecutionError::from_kind(
235 ExecutionErrorKind::MoveObjectTooBig {
236 object_size: new_contents.len() as u64,
237 max_object_size: max_move_object_size,
238 },
239 ));
240 }
241
242 #[cfg(debug_assertions)]
243 let old_id = self.id();
244 self.contents = new_contents;
245
246 #[cfg(debug_assertions)]
248 debug_assert_eq!(self.id(), old_id);
249
250 Ok(())
251 }
252
253 pub fn increment_version_to(&mut self, next: SequenceNumber) {
256 self.version.increment_to(next);
257 }
258
259 pub fn decrement_version_to(&mut self, prev: SequenceNumber) {
260 self.version.decrement_to(prev);
261 }
262
263 pub fn contents(&self) -> &[u8] {
264 &self.contents
265 }
266
267 pub fn into_contents(self) -> Vec<u8> {
268 self.contents
269 }
270
271 pub fn into_type(self) -> MoveObjectType {
272 self.type_
273 }
274
275 pub fn into_inner(self) -> (MoveObjectType, Vec<u8>) {
276 (self.type_, self.contents)
277 }
278
279 pub fn get_layout(&self, resolver: &impl GetModule) -> Result<MoveStructLayout, IotaError> {
284 Self::get_struct_layout_from_struct_tag(self.type_().clone().into(), resolver)
285 }
286
287 pub fn get_struct_layout_from_struct_tag(
288 struct_tag: StructTag,
289 resolver: &impl GetModule,
290 ) -> Result<MoveStructLayout, IotaError> {
291 let type_ = TypeTag::Struct(Box::new(struct_tag));
292 let layout = TypeLayoutBuilder::build_with_types(&type_, resolver).map_err(|e| {
293 IotaError::ObjectSerialization {
294 error: e.to_string(),
295 }
296 })?;
297 match layout {
298 MoveTypeLayout::Struct(l) => Ok(*l),
299 _ => unreachable!(
300 "We called build_with_types on Struct type, should get a struct layout"
301 ),
302 }
303 }
304
305 pub fn to_move_struct(&self, layout: &MoveStructLayout) -> Result<MoveStruct, IotaError> {
307 BoundedVisitor::deserialize_struct(&self.contents, layout).map_err(|e| {
308 IotaError::ObjectSerialization {
309 error: e.to_string(),
310 }
311 })
312 }
313
314 pub fn to_move_struct_with_resolver(
316 &self,
317 resolver: &impl GetModule,
318 ) -> Result<MoveStruct, IotaError> {
319 self.to_move_struct(&self.get_layout(resolver)?)
320 }
321
322 pub fn to_rust<'de, T: Deserialize<'de>>(&'de self) -> Option<T> {
323 bcs::from_bytes(self.contents()).ok()
324 }
325
326 pub fn object_size_for_gas_metering(&self) -> usize {
331 let serialized_type_tag_size =
332 bcs::serialized_size(&self.type_).expect("Serializing type tag should not fail");
333 self.contents.len() + serialized_type_tag_size + 8
335 }
336
337 pub fn get_total_iota(
340 &self,
341 layout_resolver: &mut dyn LayoutResolver,
342 ) -> Result<u64, IotaError> {
343 let balances = self.get_coin_balances(layout_resolver)?;
344 Ok(balances.get(&GAS::type_tag()).copied().unwrap_or(0))
345 }
346}
347
348impl MoveObject {
350 pub fn get_coin_balances(
352 &self,
353 layout_resolver: &mut dyn LayoutResolver,
354 ) -> Result<BTreeMap<TypeTag, u64>, IotaError> {
355 if let Some(type_tag) = self.type_.coin_type_maybe() {
357 let balance = self.get_coin_value_unsafe();
358 Ok(if balance > 0 {
359 BTreeMap::from([(type_tag.clone(), balance)])
360 } else {
361 BTreeMap::default()
362 })
363 } else {
364 let layout = layout_resolver.get_annotated_layout(&self.type_().clone().into())?;
365
366 let mut traversal = BalanceTraversal::default();
367 MoveValue::visit_deserialize(&self.contents, &layout.into_layout(), &mut traversal)
368 .map_err(|e| IotaError::ObjectSerialization {
369 error: e.to_string(),
370 })?;
371
372 Ok(traversal.finish())
373 }
374 }
375}
376
377#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
378pub enum Data {
379 Move(MoveObject),
381 Package(MovePackage),
383 }
385
386impl Data {
387 pub fn try_as_move(&self) -> Option<&MoveObject> {
388 use Data::*;
389 match self {
390 Move(m) => Some(m),
391 Package(_) => None,
392 }
393 }
394
395 pub fn try_as_move_mut(&mut self) -> Option<&mut MoveObject> {
396 use Data::*;
397 match self {
398 Move(m) => Some(m),
399 Package(_) => None,
400 }
401 }
402
403 pub fn try_as_package(&self) -> Option<&MovePackage> {
404 use Data::*;
405 match self {
406 Move(_) => None,
407 Package(p) => Some(p),
408 }
409 }
410
411 pub fn try_as_package_mut(&mut self) -> Option<&mut MovePackage> {
412 use Data::*;
413 match self {
414 Move(_) => None,
415 Package(p) => Some(p),
416 }
417 }
418
419 pub fn try_into_package(self) -> Option<MovePackage> {
420 use Data::*;
421 match self {
422 Move(_) => None,
423 Package(p) => Some(p),
424 }
425 }
426
427 pub fn type_(&self) -> Option<&MoveObjectType> {
428 use Data::*;
429 match self {
430 Move(m) => Some(m.type_()),
431 Package(_) => None,
432 }
433 }
434
435 pub fn struct_tag(&self) -> Option<StructTag> {
436 use Data::*;
437 match self {
438 Move(m) => Some(m.type_().clone().into()),
439 Package(_) => None,
440 }
441 }
442
443 pub fn id(&self) -> ObjectID {
444 match self {
445 Self::Move(v) => v.id(),
446 Self::Package(m) => m.id(),
447 }
448 }
449}
450
451#[derive(
452 Eq, PartialEq, Debug, Clone, Copy, Deserialize, Serialize, Hash, JsonSchema, Ord, PartialOrd,
453)]
454#[cfg_attr(feature = "fuzzing", derive(proptest_derive::Arbitrary))]
455pub enum Owner {
456 AddressOwner(IotaAddress),
458 ObjectOwner(IotaAddress),
461 Shared {
463 initial_shared_version: SequenceNumber,
465 },
466 Immutable,
468}
469
470impl Owner {
471 pub fn get_address_owner_address(&self) -> IotaResult<IotaAddress> {
474 match self {
475 Self::AddressOwner(address) => Ok(*address),
476 Self::Shared { .. } | Self::Immutable | Self::ObjectOwner(_) => {
477 Err(IotaError::UnexpectedOwnerType)
478 }
479 }
480 }
481
482 pub fn get_owner_address(&self) -> IotaResult<IotaAddress> {
486 match self {
487 Self::AddressOwner(address) | Self::ObjectOwner(address) => Ok(*address),
488 Self::Shared { .. } | Self::Immutable => Err(IotaError::UnexpectedOwnerType),
489 }
490 }
491
492 pub fn is_immutable(&self) -> bool {
493 matches!(self, Owner::Immutable)
494 }
495
496 pub fn is_address_owned(&self) -> bool {
497 matches!(self, Owner::AddressOwner(_))
498 }
499
500 pub fn is_child_object(&self) -> bool {
501 matches!(self, Owner::ObjectOwner(_))
502 }
503
504 pub fn is_shared(&self) -> bool {
505 matches!(self, Owner::Shared { .. })
506 }
507}
508
509impl PartialEq<IotaAddress> for Owner {
510 fn eq(&self, other: &IotaAddress) -> bool {
511 match self {
512 Self::AddressOwner(address) => address == other,
513 Self::ObjectOwner(_) | Self::Shared { .. } | Self::Immutable => false,
514 }
515 }
516}
517
518impl PartialEq<ObjectID> for Owner {
519 fn eq(&self, other: &ObjectID) -> bool {
520 let other_id: IotaAddress = (*other).into();
521 match self {
522 Self::ObjectOwner(id) => id == &other_id,
523 Self::AddressOwner(_) | Self::Shared { .. } | Self::Immutable => false,
524 }
525 }
526}
527
528impl Display for Owner {
529 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
530 match self {
531 Self::AddressOwner(address) => {
532 write!(f, "Account Address ( {} )", address)
533 }
534 Self::ObjectOwner(address) => {
535 write!(f, "Object ID: ( {} )", address)
536 }
537 Self::Immutable => {
538 write!(f, "Immutable")
539 }
540 Self::Shared {
541 initial_shared_version,
542 } => {
543 write!(f, "Shared( {} )", initial_shared_version.value())
544 }
545 }
546 }
547}
548
549#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
550#[serde(rename = "Object")]
551pub struct ObjectInner {
552 pub data: Data,
554 pub owner: Owner,
556 pub previous_transaction: TransactionDigest,
558 pub storage_rebate: u64,
562}
563
564#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
565#[serde(from = "ObjectInner")]
566pub struct Object(Arc<ObjectInner>);
567
568impl From<ObjectInner> for Object {
569 fn from(inner: ObjectInner) -> Self {
570 Self(Arc::new(inner))
571 }
572}
573
574impl Object {
575 pub fn into_inner(self) -> ObjectInner {
576 match Arc::try_unwrap(self.0) {
577 Ok(inner) => inner,
578 Err(inner_arc) => (*inner_arc).clone(),
579 }
580 }
581
582 pub fn as_inner(&self) -> &ObjectInner {
583 &self.0
584 }
585
586 pub fn owner(&self) -> &Owner {
587 &self.0.owner
588 }
589
590 pub fn new_from_genesis(
591 data: Data,
592 owner: Owner,
593 previous_transaction: TransactionDigest,
594 ) -> Self {
595 ObjectInner {
596 data,
597 owner,
598 previous_transaction,
599 storage_rebate: 0,
600 }
601 .into()
602 }
603
604 pub fn new_move(o: MoveObject, owner: Owner, previous_transaction: TransactionDigest) -> Self {
606 ObjectInner {
607 data: Data::Move(o),
608 owner,
609 previous_transaction,
610 storage_rebate: 0,
611 }
612 .into()
613 }
614
615 pub fn new_package_from_data(data: Data, previous_transaction: TransactionDigest) -> Self {
616 ObjectInner {
617 data,
618 owner: Owner::Immutable,
619 previous_transaction,
620 storage_rebate: 0,
621 }
622 .into()
623 }
624
625 pub fn new_from_package(package: MovePackage, previous_transaction: TransactionDigest) -> Self {
627 Self::new_package_from_data(Data::Package(package), previous_transaction)
628 }
629
630 pub fn new_package<'p>(
631 modules: &[CompiledModule],
632 previous_transaction: TransactionDigest,
633 max_move_package_size: u64,
634 move_binary_format_version: u32,
635 dependencies: impl IntoIterator<Item = &'p MovePackage>,
636 ) -> Result<Self, ExecutionError> {
637 Ok(Self::new_package_from_data(
638 Data::Package(MovePackage::new_initial(
639 modules,
640 max_move_package_size,
641 move_binary_format_version,
642 dependencies,
643 )?),
644 previous_transaction,
645 ))
646 }
647
648 pub fn new_upgraded_package<'p>(
649 previous_package: &MovePackage,
650 new_package_id: ObjectID,
651 modules: &[CompiledModule],
652 previous_transaction: TransactionDigest,
653 protocol_config: &ProtocolConfig,
654 dependencies: impl IntoIterator<Item = &'p MovePackage>,
655 ) -> Result<Self, ExecutionError> {
656 Ok(Self::new_package_from_data(
657 Data::Package(previous_package.new_upgraded(
658 new_package_id,
659 modules,
660 protocol_config,
661 dependencies,
662 )?),
663 previous_transaction,
664 ))
665 }
666
667 pub fn new_package_for_testing(
668 modules: &[CompiledModule],
669 previous_transaction: TransactionDigest,
670 dependencies: impl IntoIterator<Item = MovePackage>,
671 ) -> Result<Self, ExecutionError> {
672 let dependencies: Vec<_> = dependencies.into_iter().collect();
673 let config = ProtocolConfig::get_for_max_version_UNSAFE();
674 Self::new_package(
675 modules,
676 previous_transaction,
677 config.max_move_package_size(),
678 config.move_binary_format_version(),
679 &dependencies,
680 )
681 }
682
683 pub fn new_system_package(
686 modules: &[CompiledModule],
687 version: SequenceNumber,
688 dependencies: Vec<ObjectID>,
689 previous_transaction: TransactionDigest,
690 ) -> Self {
691 let ret = Self::new_package_from_data(
692 Data::Package(MovePackage::new_system(version, modules, dependencies)),
693 previous_transaction,
694 );
695
696 #[cfg(not(msim))]
697 assert!(ret.is_system_package());
698
699 ret
700 }
701}
702
703impl std::ops::Deref for Object {
704 type Target = ObjectInner;
705 fn deref(&self) -> &Self::Target {
706 &self.0
707 }
708}
709
710impl std::ops::DerefMut for Object {
711 fn deref_mut(&mut self) -> &mut Self::Target {
712 Arc::make_mut(&mut self.0)
713 }
714}
715
716impl ObjectInner {
717 pub fn is_system_package(&self) -> bool {
719 self.is_package() && is_system_package(self.id())
720 }
721
722 pub fn is_immutable(&self) -> bool {
723 self.owner.is_immutable()
724 }
725
726 pub fn is_address_owned(&self) -> bool {
727 self.owner.is_address_owned()
728 }
729
730 pub fn is_child_object(&self) -> bool {
731 self.owner.is_child_object()
732 }
733
734 pub fn is_shared(&self) -> bool {
735 self.owner.is_shared()
736 }
737
738 pub fn get_single_owner(&self) -> Option<IotaAddress> {
739 self.owner.get_owner_address().ok()
740 }
741
742 pub fn get_owner_and_id(&self) -> Option<(Owner, ObjectID)> {
745 Some((self.owner, self.id()))
746 }
747
748 pub fn is_package(&self) -> bool {
751 matches!(&self.data, Data::Package(_))
752 }
753
754 pub fn compute_object_reference(&self) -> ObjectRef {
755 (self.id(), self.version(), self.digest())
756 }
757
758 pub fn digest(&self) -> ObjectDigest {
759 ObjectDigest::new(default_hash(self))
760 }
761
762 pub fn id(&self) -> ObjectID {
763 use Data::*;
764
765 match &self.data {
766 Move(v) => v.id(),
767 Package(m) => m.id(),
768 }
769 }
770
771 pub fn version(&self) -> SequenceNumber {
772 use Data::*;
773
774 match &self.data {
775 Move(o) => o.version(),
776 Package(p) => p.version(),
777 }
778 }
779
780 pub fn type_(&self) -> Option<&MoveObjectType> {
781 self.data.type_()
782 }
783
784 pub fn struct_tag(&self) -> Option<StructTag> {
785 self.data.struct_tag()
786 }
787
788 pub fn is_coin(&self) -> bool {
789 if let Some(move_object) = self.data.try_as_move() {
790 move_object.type_().is_coin()
791 } else {
792 false
793 }
794 }
795
796 pub fn is_gas_coin(&self) -> bool {
797 if let Some(move_object) = self.data.try_as_move() {
798 move_object.type_().is_gas_coin()
799 } else {
800 false
801 }
802 }
803
804 pub fn as_coin_maybe(&self) -> Option<Coin> {
807 if let Some(move_object) = self.data.try_as_move() {
808 let coin: Coin = bcs::from_bytes(move_object.contents()).ok()?;
809 Some(coin)
810 } else {
811 None
812 }
813 }
814
815 pub fn as_timelock_balance_maybe(&self) -> Option<TimeLock<Balance>> {
816 if let Some(move_object) = self.data.try_as_move() {
817 Some(TimeLock::from_bcs_bytes(move_object.contents()).ok()?)
818 } else {
819 None
820 }
821 }
822
823 pub fn coin_type_maybe(&self) -> Option<TypeTag> {
824 if let Some(move_object) = self.data.try_as_move() {
825 move_object.type_().coin_type_maybe()
826 } else {
827 None
828 }
829 }
830
831 pub fn get_coin_value_unsafe(&self) -> u64 {
836 self.data.try_as_move().unwrap().get_coin_value_unsafe()
837 }
838
839 pub fn object_size_for_gas_metering(&self) -> usize {
844 let meta_data_size = size_of::<Owner>() + size_of::<TransactionDigest>() + size_of::<u64>();
845 let data_size = match &self.data {
846 Data::Move(m) => m.object_size_for_gas_metering(),
847 Data::Package(p) => p.object_size_for_gas_metering(),
848 };
849 meta_data_size + data_size
850 }
851
852 pub fn transfer(&mut self, new_owner: IotaAddress) {
854 self.owner = Owner::AddressOwner(new_owner);
855 }
856
857 pub fn get_layout(
862 &self,
863 resolver: &impl GetModule,
864 ) -> Result<Option<MoveStructLayout>, IotaError> {
865 match &self.data {
866 Data::Move(m) => Ok(Some(m.get_layout(resolver)?)),
867 Data::Package(_) => Ok(None),
868 }
869 }
870
871 pub fn get_move_template_type(&self) -> IotaResult<TypeTag> {
875 let move_struct = self.data.struct_tag().ok_or_else(|| IotaError::Type {
876 error: "Object must be a Move object".to_owned(),
877 })?;
878 fp_ensure!(
879 move_struct.type_params.len() == 1,
880 IotaError::Type {
881 error: "Move object struct must have one type parameter".to_owned()
882 }
883 );
884 let type_tag = move_struct.type_params[0].clone();
886 Ok(type_tag)
887 }
888
889 pub fn to_rust<'de, T: Deserialize<'de>>(&'de self) -> Option<T> {
890 self.data.try_as_move().and_then(|data| data.to_rust())
891 }
892}
893
894impl Object {
896 pub fn get_total_iota(
899 &self,
900 layout_resolver: &mut dyn LayoutResolver,
901 ) -> Result<u64, IotaError> {
902 Ok(self.storage_rebate
903 + match &self.data {
904 Data::Move(m) => m.get_total_iota(layout_resolver)?,
905 Data::Package(_) => 0,
906 })
907 }
908
909 pub fn immutable_with_id_for_testing(id: ObjectID) -> Self {
910 let data = Data::Move(MoveObject {
911 type_: GasCoin::type_().into(),
912 version: OBJECT_START_VERSION,
913 contents: GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
914 });
915 ObjectInner {
916 owner: Owner::Immutable,
917 data,
918 previous_transaction: TransactionDigest::genesis_marker(),
919 storage_rebate: 0,
920 }
921 .into()
922 }
923
924 pub fn immutable_for_testing() -> Self {
925 thread_local! {
926 static IMMUTABLE_OBJECT_ID: ObjectID = ObjectID::random();
927 }
928
929 Self::immutable_with_id_for_testing(IMMUTABLE_OBJECT_ID.with(|id| *id))
930 }
931
932 pub fn shared_for_testing() -> Object {
934 let id = ObjectID::random();
935 let obj = MoveObject::new_gas_coin(OBJECT_START_VERSION, id, 10);
936 let owner = Owner::Shared {
937 initial_shared_version: obj.version(),
938 };
939 Object::new_move(obj, owner, TransactionDigest::genesis_marker())
940 }
941
942 pub fn with_id_owner_gas_for_testing(id: ObjectID, owner: IotaAddress, gas: u64) -> Self {
943 let data = Data::Move(MoveObject {
944 type_: GasCoin::type_().into(),
945 version: OBJECT_START_VERSION,
946 contents: GasCoin::new(id, gas).to_bcs_bytes(),
947 });
948 ObjectInner {
949 owner: Owner::AddressOwner(owner),
950 data,
951 previous_transaction: TransactionDigest::genesis_marker(),
952 storage_rebate: 0,
953 }
954 .into()
955 }
956
957 pub fn treasury_cap_for_testing(struct_tag: StructTag, treasury_cap: TreasuryCap) -> Self {
958 let data = Data::Move(MoveObject {
959 type_: TreasuryCap::type_(struct_tag).into(),
960 version: OBJECT_START_VERSION,
961 contents: bcs::to_bytes(&treasury_cap).expect("Failed to serialize"),
962 });
963 ObjectInner {
964 owner: Owner::Immutable,
965 data,
966 previous_transaction: TransactionDigest::genesis_marker(),
967 storage_rebate: 0,
968 }
969 .into()
970 }
971
972 pub fn coin_metadata_for_testing(struct_tag: StructTag, metadata: CoinMetadata) -> Self {
973 let data = Data::Move(MoveObject {
974 type_: CoinMetadata::type_(struct_tag).into(),
975 version: OBJECT_START_VERSION,
976 contents: bcs::to_bytes(&metadata).expect("Failed to serialize"),
977 });
978 ObjectInner {
979 owner: Owner::Immutable,
980 data,
981 previous_transaction: TransactionDigest::genesis_marker(),
982 storage_rebate: 0,
983 }
984 .into()
985 }
986
987 pub fn with_object_owner_for_testing(id: ObjectID, owner: ObjectID) -> Self {
988 let data = Data::Move(MoveObject {
989 type_: GasCoin::type_().into(),
990 version: OBJECT_START_VERSION,
991 contents: GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
992 });
993 ObjectInner {
994 owner: Owner::ObjectOwner(owner.into()),
995 data,
996 previous_transaction: TransactionDigest::genesis_marker(),
997 storage_rebate: 0,
998 }
999 .into()
1000 }
1001
1002 pub fn with_id_owner_for_testing(id: ObjectID, owner: IotaAddress) -> Self {
1003 Self::with_id_owner_gas_for_testing(id, owner, GAS_VALUE_FOR_TESTING)
1005 }
1006
1007 pub fn with_id_owner_version_for_testing(
1008 id: ObjectID,
1009 version: SequenceNumber,
1010 owner: IotaAddress,
1011 ) -> Self {
1012 let data = Data::Move(MoveObject {
1013 type_: GasCoin::type_().into(),
1014 version,
1015 contents: GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
1016 });
1017 ObjectInner {
1018 owner: Owner::AddressOwner(owner),
1019 data,
1020 previous_transaction: TransactionDigest::genesis_marker(),
1021 storage_rebate: 0,
1022 }
1023 .into()
1024 }
1025
1026 pub fn with_owner_for_testing(owner: IotaAddress) -> Self {
1027 Self::with_id_owner_for_testing(ObjectID::random(), owner)
1028 }
1029
1030 pub fn new_gas_with_balance_and_owner_for_testing(value: u64, owner: IotaAddress) -> Self {
1033 let obj = MoveObject::new_gas_coin(OBJECT_START_VERSION, ObjectID::random(), value);
1034 Object::new_move(
1035 obj,
1036 Owner::AddressOwner(owner),
1037 TransactionDigest::genesis_marker(),
1038 )
1039 }
1040
1041 pub fn new_gas_for_testing() -> Self {
1043 let gas_object_id = ObjectID::random();
1044 let (owner, _) = deterministic_random_account_key();
1045 Object::with_id_owner_for_testing(gas_object_id, owner)
1046 }
1047}
1048
1049pub fn generate_test_gas_objects() -> Vec<Object> {
1051 thread_local! {
1052 static GAS_OBJECTS: Vec<Object> = (0..50)
1053 .map(|_| {
1054 let gas_object_id = ObjectID::random();
1055 let (owner, _) = deterministic_random_account_key();
1056 Object::with_id_owner_for_testing(gas_object_id, owner)
1057 })
1058 .collect();
1059 }
1060
1061 GAS_OBJECTS.with(|v| v.clone())
1062}
1063
1064#[derive(Serialize, Deserialize, Debug)]
1065#[serde(tag = "status", content = "details")]
1066pub enum ObjectRead {
1067 NotExists(ObjectID),
1068 Exists(ObjectRef, Object, Option<MoveStructLayout>),
1069 Deleted(ObjectRef),
1070}
1071
1072impl ObjectRead {
1073 pub fn into_object(self) -> UserInputResult<Object> {
1076 match self {
1077 Self::Deleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: oref }),
1078 Self::NotExists(id) => Err(UserInputError::ObjectNotFound {
1079 object_id: id,
1080 version: None,
1081 }),
1082 Self::Exists(_, o, _) => Ok(o),
1083 }
1084 }
1085
1086 pub fn object(&self) -> UserInputResult<&Object> {
1087 match self {
1088 Self::Deleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: *oref }),
1089 Self::NotExists(id) => Err(UserInputError::ObjectNotFound {
1090 object_id: *id,
1091 version: None,
1092 }),
1093 Self::Exists(_, o, _) => Ok(o),
1094 }
1095 }
1096
1097 pub fn object_id(&self) -> ObjectID {
1098 match self {
1099 Self::Deleted(oref) => oref.0,
1100 Self::NotExists(id) => *id,
1101 Self::Exists(oref, _, _) => oref.0,
1102 }
1103 }
1104}
1105
1106impl Display for ObjectRead {
1107 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1108 match self {
1109 Self::Deleted(oref) => {
1110 write!(f, "ObjectRead::Deleted ({:?})", oref)
1111 }
1112 Self::NotExists(id) => {
1113 write!(f, "ObjectRead::NotExists ({:?})", id)
1114 }
1115 Self::Exists(oref, _, _) => {
1116 write!(f, "ObjectRead::Exists ({:?})", oref)
1117 }
1118 }
1119 }
1120}
1121
1122#[derive(Serialize, Deserialize, Debug)]
1123#[serde(tag = "status", content = "details")]
1124pub enum PastObjectRead {
1125 ObjectNotExists(ObjectID),
1127 ObjectDeleted(ObjectRef),
1129 VersionFound(ObjectRef, Object, Option<MoveStructLayout>),
1131 VersionNotFound(ObjectID, SequenceNumber),
1133 VersionTooHigh {
1135 object_id: ObjectID,
1136 asked_version: SequenceNumber,
1137 latest_version: SequenceNumber,
1138 },
1139}
1140
1141impl PastObjectRead {
1142 pub fn into_object(self) -> UserInputResult<Object> {
1144 match self {
1145 Self::ObjectDeleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: oref }),
1146 Self::ObjectNotExists(id) => Err(UserInputError::ObjectNotFound {
1147 object_id: id,
1148 version: None,
1149 }),
1150 Self::VersionFound(_, o, _) => Ok(o),
1151 Self::VersionNotFound(object_id, version) => Err(UserInputError::ObjectNotFound {
1152 object_id,
1153 version: Some(version),
1154 }),
1155 Self::VersionTooHigh {
1156 object_id,
1157 asked_version,
1158 latest_version,
1159 } => Err(UserInputError::ObjectSequenceNumberTooHigh {
1160 object_id,
1161 asked_version,
1162 latest_version,
1163 }),
1164 }
1165 }
1166}
1167
1168impl Display for PastObjectRead {
1169 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1170 match self {
1171 Self::ObjectDeleted(oref) => {
1172 write!(f, "PastObjectRead::ObjectDeleted ({:?})", oref)
1173 }
1174 Self::ObjectNotExists(id) => {
1175 write!(f, "PastObjectRead::ObjectNotExists ({:?})", id)
1176 }
1177 Self::VersionFound(oref, _, _) => {
1178 write!(f, "PastObjectRead::VersionFound ({:?})", oref)
1179 }
1180 Self::VersionNotFound(object_id, version) => {
1181 write!(
1182 f,
1183 "PastObjectRead::VersionNotFound ({:?}, asked sequence number {:?})",
1184 object_id, version
1185 )
1186 }
1187 Self::VersionTooHigh {
1188 object_id,
1189 asked_version,
1190 latest_version,
1191 } => {
1192 write!(
1193 f,
1194 "PastObjectRead::VersionTooHigh ({:?}, asked sequence number {:?}, latest sequence number {:?})",
1195 object_id, asked_version, latest_version
1196 )
1197 }
1198 }
1199 }
1200}
1201
1202#[cfg(test)]
1203mod tests {
1204 use crate::{
1205 base_types::{IotaAddress, ObjectID, TransactionDigest},
1206 gas_coin::GasCoin,
1207 object::{OBJECT_START_VERSION, Object, Owner},
1208 };
1209
1210 #[test]
1213 fn test_object_digest_and_serialized_format() {
1214 let g =
1215 GasCoin::new_for_testing_with_id(ObjectID::ZERO, 123).to_object(OBJECT_START_VERSION);
1216 let o = Object::new_move(
1217 g,
1218 Owner::AddressOwner(IotaAddress::ZERO),
1219 TransactionDigest::ZERO,
1220 );
1221 let bytes = bcs::to_bytes(&o).unwrap();
1222
1223 assert_eq!(
1224 bytes,
1225 [
1226 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,
1227 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,
1228 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,
1229 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,
1230 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1231 ]
1232 );
1233 let objref = format!("{:?}", o.compute_object_reference());
1234 assert_eq!(
1235 objref,
1236 "(0x0000000000000000000000000000000000000000000000000000000000000000, SequenceNumber(1), o#Ba4YyVBcpc9jgX4PMLRoyt9dKLftYVSDvuKbtMr9f4NM)"
1237 );
1238 }
1239
1240 #[test]
1241 fn test_get_coin_value_unsafe() {
1242 fn test_for_value(v: u64) {
1243 let g = GasCoin::new_for_testing(v).to_object(OBJECT_START_VERSION);
1244 assert_eq!(g.get_coin_value_unsafe(), v);
1245 assert_eq!(GasCoin::try_from(&g).unwrap().value(), v);
1246 }
1247
1248 test_for_value(0);
1249 test_for_value(1);
1250 test_for_value(8);
1251 test_for_value(9);
1252 test_for_value(u8::MAX as u64);
1253 test_for_value(u8::MAX as u64 + 1);
1254 test_for_value(u16::MAX as u64);
1255 test_for_value(u16::MAX as u64 + 1);
1256 test_for_value(u32::MAX as u64);
1257 test_for_value(u32::MAX as u64 + 1);
1258 test_for_value(u64::MAX);
1259 }
1260
1261 #[test]
1262 fn test_set_coin_value_unsafe() {
1263 fn test_for_value(v: u64) {
1264 let mut g = GasCoin::new_for_testing(u64::MAX).to_object(OBJECT_START_VERSION);
1265 g.set_coin_value_unsafe(v);
1266 assert_eq!(g.get_coin_value_unsafe(), v);
1267 assert_eq!(GasCoin::try_from(&g).unwrap().value(), v);
1268 assert_eq!(g.version(), OBJECT_START_VERSION);
1269 assert_eq!(g.contents().len(), 40);
1270 }
1271
1272 test_for_value(0);
1273 test_for_value(1);
1274 test_for_value(8);
1275 test_for_value(9);
1276 test_for_value(u8::MAX as u64);
1277 test_for_value(u8::MAX as u64 + 1);
1278 test_for_value(u16::MAX as u64);
1279 test_for_value(u16::MAX as u64 + 1);
1280 test_for_value(u32::MAX as u64);
1281 test_for_value(u32::MAX as u64 + 1);
1282 test_for_value(u64::MAX);
1283 }
1284}