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)]
565pub struct Object(Arc<ObjectInner>);
566
567impl From<ObjectInner> for Object {
568 fn from(inner: ObjectInner) -> Self {
569 Self(Arc::new(inner))
570 }
571}
572
573impl Object {
574 pub fn into_inner(self) -> ObjectInner {
575 match Arc::try_unwrap(self.0) {
576 Ok(inner) => inner,
577 Err(inner_arc) => (*inner_arc).clone(),
578 }
579 }
580
581 pub fn as_inner(&self) -> &ObjectInner {
582 &self.0
583 }
584
585 pub fn owner(&self) -> &Owner {
586 &self.0.owner
587 }
588
589 pub fn new_from_genesis(
590 data: Data,
591 owner: Owner,
592 previous_transaction: TransactionDigest,
593 ) -> Self {
594 ObjectInner {
595 data,
596 owner,
597 previous_transaction,
598 storage_rebate: 0,
599 }
600 .into()
601 }
602
603 pub fn new_move(o: MoveObject, owner: Owner, previous_transaction: TransactionDigest) -> Self {
605 ObjectInner {
606 data: Data::Move(o),
607 owner,
608 previous_transaction,
609 storage_rebate: 0,
610 }
611 .into()
612 }
613
614 pub fn new_package_from_data(data: Data, previous_transaction: TransactionDigest) -> Self {
615 ObjectInner {
616 data,
617 owner: Owner::Immutable,
618 previous_transaction,
619 storage_rebate: 0,
620 }
621 .into()
622 }
623
624 pub fn new_from_package(package: MovePackage, previous_transaction: TransactionDigest) -> Self {
626 Self::new_package_from_data(Data::Package(package), previous_transaction)
627 }
628
629 pub fn new_package<'p>(
630 modules: &[CompiledModule],
631 previous_transaction: TransactionDigest,
632 max_move_package_size: u64,
633 move_binary_format_version: u32,
634 dependencies: impl IntoIterator<Item = &'p MovePackage>,
635 ) -> Result<Self, ExecutionError> {
636 Ok(Self::new_package_from_data(
637 Data::Package(MovePackage::new_initial(
638 modules,
639 max_move_package_size,
640 move_binary_format_version,
641 dependencies,
642 )?),
643 previous_transaction,
644 ))
645 }
646
647 pub fn new_upgraded_package<'p>(
648 previous_package: &MovePackage,
649 new_package_id: ObjectID,
650 modules: &[CompiledModule],
651 previous_transaction: TransactionDigest,
652 protocol_config: &ProtocolConfig,
653 dependencies: impl IntoIterator<Item = &'p MovePackage>,
654 ) -> Result<Self, ExecutionError> {
655 Ok(Self::new_package_from_data(
656 Data::Package(previous_package.new_upgraded(
657 new_package_id,
658 modules,
659 protocol_config,
660 dependencies,
661 )?),
662 previous_transaction,
663 ))
664 }
665
666 pub fn new_package_for_testing(
667 modules: &[CompiledModule],
668 previous_transaction: TransactionDigest,
669 dependencies: impl IntoIterator<Item = MovePackage>,
670 ) -> Result<Self, ExecutionError> {
671 let dependencies: Vec<_> = dependencies.into_iter().collect();
672 let config = ProtocolConfig::get_for_max_version_UNSAFE();
673 Self::new_package(
674 modules,
675 previous_transaction,
676 config.max_move_package_size(),
677 config.move_binary_format_version(),
678 &dependencies,
679 )
680 }
681
682 pub fn new_system_package(
685 modules: &[CompiledModule],
686 version: SequenceNumber,
687 dependencies: Vec<ObjectID>,
688 previous_transaction: TransactionDigest,
689 ) -> Self {
690 let ret = Self::new_package_from_data(
691 Data::Package(MovePackage::new_system(version, modules, dependencies)),
692 previous_transaction,
693 );
694
695 #[cfg(not(msim))]
696 assert!(ret.is_system_package());
697
698 ret
699 }
700}
701
702impl std::ops::Deref for Object {
703 type Target = ObjectInner;
704 fn deref(&self) -> &Self::Target {
705 &self.0
706 }
707}
708
709impl std::ops::DerefMut for Object {
710 fn deref_mut(&mut self) -> &mut Self::Target {
711 Arc::make_mut(&mut self.0)
712 }
713}
714
715impl ObjectInner {
716 pub fn is_system_package(&self) -> bool {
718 self.is_package() && is_system_package(self.id())
719 }
720
721 pub fn is_immutable(&self) -> bool {
722 self.owner.is_immutable()
723 }
724
725 pub fn is_address_owned(&self) -> bool {
726 self.owner.is_address_owned()
727 }
728
729 pub fn is_child_object(&self) -> bool {
730 self.owner.is_child_object()
731 }
732
733 pub fn is_shared(&self) -> bool {
734 self.owner.is_shared()
735 }
736
737 pub fn get_single_owner(&self) -> Option<IotaAddress> {
738 self.owner.get_owner_address().ok()
739 }
740
741 pub fn get_owner_and_id(&self) -> Option<(Owner, ObjectID)> {
744 Some((self.owner, self.id()))
745 }
746
747 pub fn is_package(&self) -> bool {
750 matches!(&self.data, Data::Package(_))
751 }
752
753 pub fn compute_object_reference(&self) -> ObjectRef {
754 (self.id(), self.version(), self.digest())
755 }
756
757 pub fn digest(&self) -> ObjectDigest {
758 ObjectDigest::new(default_hash(self))
759 }
760
761 pub fn id(&self) -> ObjectID {
762 use Data::*;
763
764 match &self.data {
765 Move(v) => v.id(),
766 Package(m) => m.id(),
767 }
768 }
769
770 pub fn version(&self) -> SequenceNumber {
771 use Data::*;
772
773 match &self.data {
774 Move(o) => o.version(),
775 Package(p) => p.version(),
776 }
777 }
778
779 pub fn type_(&self) -> Option<&MoveObjectType> {
780 self.data.type_()
781 }
782
783 pub fn struct_tag(&self) -> Option<StructTag> {
784 self.data.struct_tag()
785 }
786
787 pub fn is_coin(&self) -> bool {
788 if let Some(move_object) = self.data.try_as_move() {
789 move_object.type_().is_coin()
790 } else {
791 false
792 }
793 }
794
795 pub fn is_gas_coin(&self) -> bool {
796 if let Some(move_object) = self.data.try_as_move() {
797 move_object.type_().is_gas_coin()
798 } else {
799 false
800 }
801 }
802
803 pub fn as_coin_maybe(&self) -> Option<Coin> {
806 if let Some(move_object) = self.data.try_as_move() {
807 let coin: Coin = bcs::from_bytes(move_object.contents()).ok()?;
808 Some(coin)
809 } else {
810 None
811 }
812 }
813
814 pub fn as_timelock_balance_maybe(&self) -> Option<TimeLock<Balance>> {
815 if let Some(move_object) = self.data.try_as_move() {
816 Some(TimeLock::from_bcs_bytes(move_object.contents()).ok()?)
817 } else {
818 None
819 }
820 }
821
822 pub fn coin_type_maybe(&self) -> Option<TypeTag> {
823 if let Some(move_object) = self.data.try_as_move() {
824 move_object.type_().coin_type_maybe()
825 } else {
826 None
827 }
828 }
829
830 pub fn get_coin_value_unsafe(&self) -> u64 {
835 self.data.try_as_move().unwrap().get_coin_value_unsafe()
836 }
837
838 pub fn object_size_for_gas_metering(&self) -> usize {
843 let meta_data_size = size_of::<Owner>() + size_of::<TransactionDigest>() + size_of::<u64>();
844 let data_size = match &self.data {
845 Data::Move(m) => m.object_size_for_gas_metering(),
846 Data::Package(p) => p.object_size_for_gas_metering(),
847 };
848 meta_data_size + data_size
849 }
850
851 pub fn transfer(&mut self, new_owner: IotaAddress) {
853 self.owner = Owner::AddressOwner(new_owner);
854 }
855
856 pub fn get_layout(
861 &self,
862 resolver: &impl GetModule,
863 ) -> Result<Option<MoveStructLayout>, IotaError> {
864 match &self.data {
865 Data::Move(m) => Ok(Some(m.get_layout(resolver)?)),
866 Data::Package(_) => Ok(None),
867 }
868 }
869
870 pub fn get_move_template_type(&self) -> IotaResult<TypeTag> {
874 let move_struct = self.data.struct_tag().ok_or_else(|| IotaError::Type {
875 error: "Object must be a Move object".to_owned(),
876 })?;
877 fp_ensure!(
878 move_struct.type_params.len() == 1,
879 IotaError::Type {
880 error: "Move object struct must have one type parameter".to_owned()
881 }
882 );
883 let type_tag = move_struct.type_params[0].clone();
885 Ok(type_tag)
886 }
887
888 pub fn to_rust<'de, T: Deserialize<'de>>(&'de self) -> Option<T> {
889 self.data.try_as_move().and_then(|data| data.to_rust())
890 }
891}
892
893impl Object {
895 pub fn get_total_iota(
898 &self,
899 layout_resolver: &mut dyn LayoutResolver,
900 ) -> Result<u64, IotaError> {
901 Ok(self.storage_rebate
902 + match &self.data {
903 Data::Move(m) => m.get_total_iota(layout_resolver)?,
904 Data::Package(_) => 0,
905 })
906 }
907
908 pub fn immutable_with_id_for_testing(id: ObjectID) -> Self {
909 let data = Data::Move(MoveObject {
910 type_: GasCoin::type_().into(),
911 version: OBJECT_START_VERSION,
912 contents: GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
913 });
914 ObjectInner {
915 owner: Owner::Immutable,
916 data,
917 previous_transaction: TransactionDigest::genesis_marker(),
918 storage_rebate: 0,
919 }
920 .into()
921 }
922
923 pub fn immutable_for_testing() -> Self {
924 thread_local! {
925 static IMMUTABLE_OBJECT_ID: ObjectID = ObjectID::random();
926 }
927
928 Self::immutable_with_id_for_testing(IMMUTABLE_OBJECT_ID.with(|id| *id))
929 }
930
931 pub fn shared_for_testing() -> Object {
933 let id = ObjectID::random();
934 let obj = MoveObject::new_gas_coin(OBJECT_START_VERSION, id, 10);
935 let owner = Owner::Shared {
936 initial_shared_version: obj.version(),
937 };
938 Object::new_move(obj, owner, TransactionDigest::genesis_marker())
939 }
940
941 pub fn with_id_owner_gas_for_testing(id: ObjectID, owner: IotaAddress, gas: u64) -> Self {
942 let data = Data::Move(MoveObject {
943 type_: GasCoin::type_().into(),
944 version: OBJECT_START_VERSION,
945 contents: GasCoin::new(id, gas).to_bcs_bytes(),
946 });
947 ObjectInner {
948 owner: Owner::AddressOwner(owner),
949 data,
950 previous_transaction: TransactionDigest::genesis_marker(),
951 storage_rebate: 0,
952 }
953 .into()
954 }
955
956 pub fn treasury_cap_for_testing(struct_tag: StructTag, treasury_cap: TreasuryCap) -> Self {
957 let data = Data::Move(MoveObject {
958 type_: TreasuryCap::type_(struct_tag).into(),
959 version: OBJECT_START_VERSION,
960 contents: bcs::to_bytes(&treasury_cap).expect("Failed to serialize"),
961 });
962 ObjectInner {
963 owner: Owner::Immutable,
964 data,
965 previous_transaction: TransactionDigest::genesis_marker(),
966 storage_rebate: 0,
967 }
968 .into()
969 }
970
971 pub fn coin_metadata_for_testing(struct_tag: StructTag, metadata: CoinMetadata) -> Self {
972 let data = Data::Move(MoveObject {
973 type_: CoinMetadata::type_(struct_tag).into(),
974 version: OBJECT_START_VERSION,
975 contents: bcs::to_bytes(&metadata).expect("Failed to serialize"),
976 });
977 ObjectInner {
978 owner: Owner::Immutable,
979 data,
980 previous_transaction: TransactionDigest::genesis_marker(),
981 storage_rebate: 0,
982 }
983 .into()
984 }
985
986 pub fn with_object_owner_for_testing(id: ObjectID, owner: ObjectID) -> Self {
987 let data = Data::Move(MoveObject {
988 type_: GasCoin::type_().into(),
989 version: OBJECT_START_VERSION,
990 contents: GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
991 });
992 ObjectInner {
993 owner: Owner::ObjectOwner(owner.into()),
994 data,
995 previous_transaction: TransactionDigest::genesis_marker(),
996 storage_rebate: 0,
997 }
998 .into()
999 }
1000
1001 pub fn with_id_owner_for_testing(id: ObjectID, owner: IotaAddress) -> Self {
1002 Self::with_id_owner_gas_for_testing(id, owner, GAS_VALUE_FOR_TESTING)
1004 }
1005
1006 pub fn with_id_owner_version_for_testing(
1007 id: ObjectID,
1008 version: SequenceNumber,
1009 owner: IotaAddress,
1010 ) -> Self {
1011 let data = Data::Move(MoveObject {
1012 type_: GasCoin::type_().into(),
1013 version,
1014 contents: GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
1015 });
1016 ObjectInner {
1017 owner: Owner::AddressOwner(owner),
1018 data,
1019 previous_transaction: TransactionDigest::genesis_marker(),
1020 storage_rebate: 0,
1021 }
1022 .into()
1023 }
1024
1025 pub fn with_owner_for_testing(owner: IotaAddress) -> Self {
1026 Self::with_id_owner_for_testing(ObjectID::random(), owner)
1027 }
1028
1029 pub fn new_gas_with_balance_and_owner_for_testing(value: u64, owner: IotaAddress) -> Self {
1032 let obj = MoveObject::new_gas_coin(OBJECT_START_VERSION, ObjectID::random(), value);
1033 Object::new_move(
1034 obj,
1035 Owner::AddressOwner(owner),
1036 TransactionDigest::genesis_marker(),
1037 )
1038 }
1039
1040 pub fn new_gas_for_testing() -> Self {
1042 let gas_object_id = ObjectID::random();
1043 let (owner, _) = deterministic_random_account_key();
1044 Object::with_id_owner_for_testing(gas_object_id, owner)
1045 }
1046}
1047
1048pub fn generate_test_gas_objects() -> Vec<Object> {
1050 thread_local! {
1051 static GAS_OBJECTS: Vec<Object> = (0..50)
1052 .map(|_| {
1053 let gas_object_id = ObjectID::random();
1054 let (owner, _) = deterministic_random_account_key();
1055 Object::with_id_owner_for_testing(gas_object_id, owner)
1056 })
1057 .collect();
1058 }
1059
1060 GAS_OBJECTS.with(|v| v.clone())
1061}
1062
1063#[derive(Serialize, Deserialize, Debug)]
1064#[serde(tag = "status", content = "details")]
1065pub enum ObjectRead {
1066 NotExists(ObjectID),
1067 Exists(ObjectRef, Object, Option<MoveStructLayout>),
1068 Deleted(ObjectRef),
1069}
1070
1071impl ObjectRead {
1072 pub fn into_object(self) -> UserInputResult<Object> {
1075 match self {
1076 Self::Deleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: oref }),
1077 Self::NotExists(id) => Err(UserInputError::ObjectNotFound {
1078 object_id: id,
1079 version: None,
1080 }),
1081 Self::Exists(_, o, _) => Ok(o),
1082 }
1083 }
1084
1085 pub fn object(&self) -> UserInputResult<&Object> {
1086 match self {
1087 Self::Deleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: *oref }),
1088 Self::NotExists(id) => Err(UserInputError::ObjectNotFound {
1089 object_id: *id,
1090 version: None,
1091 }),
1092 Self::Exists(_, o, _) => Ok(o),
1093 }
1094 }
1095
1096 pub fn object_id(&self) -> ObjectID {
1097 match self {
1098 Self::Deleted(oref) => oref.0,
1099 Self::NotExists(id) => *id,
1100 Self::Exists(oref, _, _) => oref.0,
1101 }
1102 }
1103}
1104
1105impl Display for ObjectRead {
1106 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1107 match self {
1108 Self::Deleted(oref) => {
1109 write!(f, "ObjectRead::Deleted ({:?})", oref)
1110 }
1111 Self::NotExists(id) => {
1112 write!(f, "ObjectRead::NotExists ({:?})", id)
1113 }
1114 Self::Exists(oref, _, _) => {
1115 write!(f, "ObjectRead::Exists ({:?})", oref)
1116 }
1117 }
1118 }
1119}
1120
1121#[derive(Serialize, Deserialize, Debug)]
1122#[serde(tag = "status", content = "details")]
1123pub enum PastObjectRead {
1124 ObjectNotExists(ObjectID),
1126 ObjectDeleted(ObjectRef),
1128 VersionFound(ObjectRef, Object, Option<MoveStructLayout>),
1130 VersionNotFound(ObjectID, SequenceNumber),
1132 VersionTooHigh {
1134 object_id: ObjectID,
1135 asked_version: SequenceNumber,
1136 latest_version: SequenceNumber,
1137 },
1138}
1139
1140impl PastObjectRead {
1141 pub fn into_object(self) -> UserInputResult<Object> {
1143 match self {
1144 Self::ObjectDeleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: oref }),
1145 Self::ObjectNotExists(id) => Err(UserInputError::ObjectNotFound {
1146 object_id: id,
1147 version: None,
1148 }),
1149 Self::VersionFound(_, o, _) => Ok(o),
1150 Self::VersionNotFound(object_id, version) => Err(UserInputError::ObjectNotFound {
1151 object_id,
1152 version: Some(version),
1153 }),
1154 Self::VersionTooHigh {
1155 object_id,
1156 asked_version,
1157 latest_version,
1158 } => Err(UserInputError::ObjectSequenceNumberTooHigh {
1159 object_id,
1160 asked_version,
1161 latest_version,
1162 }),
1163 }
1164 }
1165}
1166
1167impl Display for PastObjectRead {
1168 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1169 match self {
1170 Self::ObjectDeleted(oref) => {
1171 write!(f, "PastObjectRead::ObjectDeleted ({:?})", oref)
1172 }
1173 Self::ObjectNotExists(id) => {
1174 write!(f, "PastObjectRead::ObjectNotExists ({:?})", id)
1175 }
1176 Self::VersionFound(oref, _, _) => {
1177 write!(f, "PastObjectRead::VersionFound ({:?})", oref)
1178 }
1179 Self::VersionNotFound(object_id, version) => {
1180 write!(
1181 f,
1182 "PastObjectRead::VersionNotFound ({:?}, asked sequence number {:?})",
1183 object_id, version
1184 )
1185 }
1186 Self::VersionTooHigh {
1187 object_id,
1188 asked_version,
1189 latest_version,
1190 } => {
1191 write!(
1192 f,
1193 "PastObjectRead::VersionTooHigh ({:?}, asked sequence number {:?}, latest sequence number {:?})",
1194 object_id, asked_version, latest_version
1195 )
1196 }
1197 }
1198 }
1199}
1200
1201#[test]
1204fn test_object_digest_and_serialized_format() {
1205 let g = GasCoin::new_for_testing_with_id(ObjectID::ZERO, 123).to_object(OBJECT_START_VERSION);
1206 let o = Object::new_move(
1207 g,
1208 Owner::AddressOwner(IotaAddress::ZERO),
1209 TransactionDigest::ZERO,
1210 );
1211 let bytes = bcs::to_bytes(&o).unwrap();
1212
1213 assert_eq!(
1214 bytes,
1215 [
1216 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, 0, 0,
1217 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, 0, 0, 0,
1218 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, 32, 0,
1219 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, 0,
1220 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1221 ]
1222 );
1223
1224 let objref = format!("{:?}", o.compute_object_reference());
1225 assert_eq!(
1226 objref,
1227 "(0x0000000000000000000000000000000000000000000000000000000000000000, SequenceNumber(1), o#Ba4YyVBcpc9jgX4PMLRoyt9dKLftYVSDvuKbtMr9f4NM)"
1228 );
1229}
1230
1231#[test]
1232fn test_get_coin_value_unsafe() {
1233 fn test_for_value(v: u64) {
1234 let g = GasCoin::new_for_testing(v).to_object(OBJECT_START_VERSION);
1235 assert_eq!(g.get_coin_value_unsafe(), v);
1236 assert_eq!(GasCoin::try_from(&g).unwrap().value(), v);
1237 }
1238
1239 test_for_value(0);
1240 test_for_value(1);
1241 test_for_value(8);
1242 test_for_value(9);
1243 test_for_value(u8::MAX as u64);
1244 test_for_value(u8::MAX as u64 + 1);
1245 test_for_value(u16::MAX as u64);
1246 test_for_value(u16::MAX as u64 + 1);
1247 test_for_value(u32::MAX as u64);
1248 test_for_value(u32::MAX as u64 + 1);
1249 test_for_value(u64::MAX);
1250}
1251
1252#[test]
1253fn test_set_coin_value_unsafe() {
1254 fn test_for_value(v: u64) {
1255 let mut g = GasCoin::new_for_testing(u64::MAX).to_object(OBJECT_START_VERSION);
1256 g.set_coin_value_unsafe(v);
1257 assert_eq!(g.get_coin_value_unsafe(), v);
1258 assert_eq!(GasCoin::try_from(&g).unwrap().value(), v);
1259 assert_eq!(g.version(), OBJECT_START_VERSION);
1260 assert_eq!(g.contents().len(), 40);
1261 }
1262
1263 test_for_value(0);
1264 test_for_value(1);
1265 test_for_value(8);
1266 test_for_value(9);
1267 test_for_value(u8::MAX as u64);
1268 test_for_value(u8::MAX as u64 + 1);
1269 test_for_value(u16::MAX as u64);
1270 test_for_value(u16::MAX as u64 + 1);
1271 test_for_value(u32::MAX as u64);
1272 test_for_value(u32::MAX as u64 + 1);
1273 test_for_value(u64::MAX);
1274}