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