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