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