iota_types/
object.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use 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    /// The type of this object. Immutable
53    pub(crate) type_: MoveObjectType,
54    /// Number that increases each time a tx takes this object as a mutable
55    /// input This is a lamport timestamp, not a sequentially increasing
56    /// version
57    pub(crate) version: SequenceNumber,
58    /// BCS bytes of a Move struct value
59    #[serde_as(as = "Bytes")]
60    pub(crate) contents: Vec<u8>,
61}
62
63/// Index marking the end of the object's ID + the beginning of its version
64pub const ID_END_INDEX: usize = ObjectID::LENGTH;
65
66impl MoveObject {
67    /// Creates a new Move object of type `type_` with BCS encoded bytes in
68    /// `contents`.
69    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    /// Creates a new Move object of type `type_` with BCS encoded bytes in
84    /// `contents`. It allows to set a `max_move_object_size` for that.
85    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        // unwrap safe because coins are always smaller than the max object size
108        {
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        // unwrap safe because coins are always smaller than the max object size
121        {
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    /// Return the `value: u64` field of a `Coin<T>` type.
152    /// Useful for reading the coin without deserializing the object into a Move
153    /// value It is the caller's responsibility to check that `self` is a
154    /// coin--this function may panic or do something unexpected otherwise.
155    pub fn get_coin_value_unsafe(&self) -> u64 {
156        debug_assert!(self.type_.is_coin());
157        // 32 bytes for object ID, 8 for balance
158        debug_assert!(self.contents.len() == 40);
159
160        // unwrap safe because we checked that it is a coin
161        u64::from_le_bytes(<[u8; 8]>::try_from(&self.contents[ID_END_INDEX..]).unwrap())
162    }
163
164    /// Update the `value: u64` field of a `Coin<T>` type.
165    /// Useful for updating the coin without deserializing the object into a
166    /// Move value It is the caller's responsibility to check that `self` is
167    /// a coin--this function may panic or do something unexpected
168    /// otherwise.
169    pub fn set_coin_value_unsafe(&mut self, value: u64) {
170        debug_assert!(self.type_.is_coin());
171        // 32 bytes for object ID, 8 for balance
172        debug_assert!(self.contents.len() == 40);
173
174        self.contents.splice(ID_END_INDEX.., value.to_le_bytes());
175    }
176
177    /// Update the `timestamp_ms: u64` field of the `Clock` type.
178    ///
179    /// Panics if the object isn't a `Clock`.
180    pub fn set_clock_timestamp_ms_unsafe(&mut self, timestamp_ms: u64) {
181        assert!(self.is_clock());
182        // 32 bytes for object ID, 8 for timestamp
183        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    /// Contents of the object that are specific to its type--i.e., not its ID
206    /// and version, which all objects have For example if the object was
207    /// declared as `struct S has key { id: ID, f1: u64, f2: bool },
208    /// this returns the slice containing `f1` and `f2`.
209    #[cfg(test)]
210    pub fn type_specific_contents(&self) -> &[u8] {
211        &self.contents[ID_END_INDEX..]
212    }
213
214    /// Update the contents of this object but does not increment its version
215    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        // Update should not modify ID
242        #[cfg(debug_assertions)]
243        debug_assert_eq!(self.id(), old_id);
244
245        Ok(())
246    }
247
248    /// Sets the version of this object to a new value which is assumed to be
249    /// higher (and checked to be higher in debug).
250    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    /// Get a `MoveStructLayout` for `self`.
275    /// The `resolver` value must contain the module that declares `self.type_`
276    /// and the (transitive) dependencies of `self.type_` in order for this
277    /// to succeed. Failure will result in an `ObjectSerializationError`
278    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    /// Convert `self` to the JSON representation dictated by `layout`.
301    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    /// Convert `self` to the JSON representation dictated by `layout`.
310    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    /// Approximate size of the object in bytes. This is used for gas metering.
322    /// For the type tag field, we serialize it on the spot to get the accurate
323    /// size. This should not be very expensive since the type tag is
324    /// usually simple, and we only do this once per object being mutated.
325    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        // + 8 for `version`
329        self.contents.len() + serialized_type_tag_size + 8
330    }
331
332    /// Get the total amount of IOTA embedded in `self`. Intended for testing
333    /// purposes
334    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
343// Helpers for extracting Coin<T> balances for all T
344impl MoveObject {
345    /// Get the total balances for all `Coin<T>` embedded in `self`.
346    pub fn get_coin_balances(
347        &self,
348        layout_resolver: &mut dyn LayoutResolver,
349    ) -> Result<BTreeMap<TypeTag, u64>, IotaError> {
350        // Fast path without deserialization.
351        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    /// An object whose governing logic lives in a published Move module
375    Move(MoveObject),
376    /// Map from each module name to raw serialized Move module bytes
377    Package(MovePackage),
378    // ... IOTA "native" types go here
379}
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    /// Object is exclusively owned by a single address, and is mutable.
452    AddressOwner(IotaAddress),
453    /// Object is exclusively owned by a single object, and is mutable.
454    /// The object ID is converted to IotaAddress as IotaAddress is universal.
455    ObjectOwner(IotaAddress),
456    /// Object is shared, can be used by any address, and is mutable.
457    Shared {
458        /// The version at which the object became shared
459        initial_shared_version: SequenceNumber,
460    },
461    /// Object is immutable, and hence ownership doesn't matter.
462    Immutable,
463}
464
465impl Owner {
466    // NOTE: only return address of AddressOwner, otherwise return error,
467    // ObjectOwner's address is converted from object id, thus we will skip it.
468    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    // NOTE: this function will return address of both AddressOwner and ObjectOwner,
478    // address of ObjectOwner is converted from object id, even though the type is
479    // IotaAddress.
480    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    /// The meat of the object
548    pub data: Data,
549    /// The owner that unlocks this object
550    pub owner: Owner,
551    /// The digest of the transaction that created or last mutated this object
552    pub previous_transaction: TransactionDigest,
553    /// The amount of IOTA we would rebate if this object gets deleted.
554    /// This number is re-calculated each time the object is mutated based on
555    /// the present storage gas price.
556    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    /// Create a new Move object
600    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    // Note: this will panic if `modules` is empty
621    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    /// Create a system package which is not subject to size limits. Panics if
671    /// the object ID is not a known system package.
672    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    /// Returns true if the object is a system package.
705    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    // It's a common pattern to retrieve both the owner and object ID
730    // together, if it's owned by a singler owner.
731    pub fn get_owner_and_id(&self) -> Option<(Owner, ObjectID)> {
732        Some((self.owner, self.id()))
733    }
734
735    /// Return true if this object is a Move package, false if it is a Move
736    /// value
737    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    // TODO: use `MoveObj::get_balance_unsafe` instead.
792    // context: https://github.com/iotaledger/iota/pull/10679#discussion_r1165877816
793    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    /// Return the `value: u64` field of a `Coin<T>` type.
819    /// Useful for reading the coin without deserializing the object into a Move
820    /// value It is the caller's responsibility to check that `self` is a
821    /// coin--this function may panic or do something unexpected otherwise.
822    pub fn get_coin_value_unsafe(&self) -> u64 {
823        self.data.try_as_move().unwrap().get_coin_value_unsafe()
824    }
825
826    /// Approximate size of the object in bytes. This is used for gas metering.
827    /// This will be slgihtly different from the serialized size, but
828    /// we also don't want to serialize the object just to get the size.
829    /// This approximation should be good enough for gas metering.
830    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    /// Change the owner of `self` to `new_owner`.
840    pub fn transfer(&mut self, new_owner: IotaAddress) {
841        self.owner = Owner::AddressOwner(new_owner);
842    }
843
844    /// Get a `MoveStructLayout` for `self`.
845    /// The `resolver` value must contain the module that declares `self.type_`
846    /// and the (transitive) dependencies of `self.type_` in order for this
847    /// to succeed. Failure will result in an `ObjectSerializationError`
848    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    /// Treat the object type as a Move struct with one type parameter,
859    /// like this: `S<T>`.
860    /// Returns the inner parameter type `T`.
861    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        // Index access safe due to checks above.
872        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
881// Testing-related APIs.
882impl Object {
883    /// Get the total amount of IOTA embedded in `self`, including both Move
884    /// objects and the storage rebate
885    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    /// Make a new random test shared object.
920    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        // For testing, we provide sufficient gas by default.
991        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    /// Generate a new gas coin worth `value` with a random object ID and owner
1018    /// For testing purposes only
1019    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    /// Generate a new gas coin object with default balance and random owner.
1029    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
1036/// Make a few test gas objects (all with the same random owner).
1037pub 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    /// Returns the object value if there is any, otherwise an Err if
1061    /// the object does not exist or is deleted.
1062    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    /// The object does not exist
1113    ObjectNotExists(ObjectID),
1114    /// The object is found to be deleted with this version
1115    ObjectDeleted(ObjectRef),
1116    /// The object exists and is found with this version
1117    VersionFound(ObjectRef, Object, Option<MoveStructLayout>),
1118    /// The object exists but not found with this version
1119    VersionNotFound(ObjectID, SequenceNumber),
1120    /// The asked object version is higher than the latest
1121    VersionTooHigh {
1122        object_id: ObjectID,
1123        asked_version: SequenceNumber,
1124        latest_version: SequenceNumber,
1125    },
1126}
1127
1128impl PastObjectRead {
1129    /// Returns the object value if there is any, otherwise an Err
1130    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    // Ensure that object digest computation and bcs serialized format are not
1196    // inadvertently changed.
1197    #[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}