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