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)]
565#[serde(from = "ObjectInner")]
566pub struct Object(Arc<ObjectInner>);
567
568impl From<ObjectInner> for Object {
569    fn from(inner: ObjectInner) -> Self {
570        Self(Arc::new(inner))
571    }
572}
573
574impl Object {
575    pub fn into_inner(self) -> ObjectInner {
576        match Arc::try_unwrap(self.0) {
577            Ok(inner) => inner,
578            Err(inner_arc) => (*inner_arc).clone(),
579        }
580    }
581
582    pub fn as_inner(&self) -> &ObjectInner {
583        &self.0
584    }
585
586    pub fn owner(&self) -> &Owner {
587        &self.0.owner
588    }
589
590    pub fn new_from_genesis(
591        data: Data,
592        owner: Owner,
593        previous_transaction: TransactionDigest,
594    ) -> Self {
595        ObjectInner {
596            data,
597            owner,
598            previous_transaction,
599            storage_rebate: 0,
600        }
601        .into()
602    }
603
604    /// Create a new Move object
605    pub fn new_move(o: MoveObject, owner: Owner, previous_transaction: TransactionDigest) -> Self {
606        ObjectInner {
607            data: Data::Move(o),
608            owner,
609            previous_transaction,
610            storage_rebate: 0,
611        }
612        .into()
613    }
614
615    pub fn new_package_from_data(data: Data, previous_transaction: TransactionDigest) -> Self {
616        ObjectInner {
617            data,
618            owner: Owner::Immutable,
619            previous_transaction,
620            storage_rebate: 0,
621        }
622        .into()
623    }
624
625    // Note: this will panic if `modules` is empty
626    pub fn new_from_package(package: MovePackage, previous_transaction: TransactionDigest) -> Self {
627        Self::new_package_from_data(Data::Package(package), previous_transaction)
628    }
629
630    pub fn new_package<'p>(
631        modules: &[CompiledModule],
632        previous_transaction: TransactionDigest,
633        max_move_package_size: u64,
634        move_binary_format_version: u32,
635        dependencies: impl IntoIterator<Item = &'p MovePackage>,
636    ) -> Result<Self, ExecutionError> {
637        Ok(Self::new_package_from_data(
638            Data::Package(MovePackage::new_initial(
639                modules,
640                max_move_package_size,
641                move_binary_format_version,
642                dependencies,
643            )?),
644            previous_transaction,
645        ))
646    }
647
648    pub fn new_upgraded_package<'p>(
649        previous_package: &MovePackage,
650        new_package_id: ObjectID,
651        modules: &[CompiledModule],
652        previous_transaction: TransactionDigest,
653        protocol_config: &ProtocolConfig,
654        dependencies: impl IntoIterator<Item = &'p MovePackage>,
655    ) -> Result<Self, ExecutionError> {
656        Ok(Self::new_package_from_data(
657            Data::Package(previous_package.new_upgraded(
658                new_package_id,
659                modules,
660                protocol_config,
661                dependencies,
662            )?),
663            previous_transaction,
664        ))
665    }
666
667    pub fn new_package_for_testing(
668        modules: &[CompiledModule],
669        previous_transaction: TransactionDigest,
670        dependencies: impl IntoIterator<Item = MovePackage>,
671    ) -> Result<Self, ExecutionError> {
672        let dependencies: Vec<_> = dependencies.into_iter().collect();
673        let config = ProtocolConfig::get_for_max_version_UNSAFE();
674        Self::new_package(
675            modules,
676            previous_transaction,
677            config.max_move_package_size(),
678            config.move_binary_format_version(),
679            &dependencies,
680        )
681    }
682
683    /// Create a system package which is not subject to size limits. Panics if
684    /// the object ID is not a known system package.
685    pub fn new_system_package(
686        modules: &[CompiledModule],
687        version: SequenceNumber,
688        dependencies: Vec<ObjectID>,
689        previous_transaction: TransactionDigest,
690    ) -> Self {
691        let ret = Self::new_package_from_data(
692            Data::Package(MovePackage::new_system(version, modules, dependencies)),
693            previous_transaction,
694        );
695
696        #[cfg(not(msim))]
697        assert!(ret.is_system_package());
698
699        ret
700    }
701}
702
703impl std::ops::Deref for Object {
704    type Target = ObjectInner;
705    fn deref(&self) -> &Self::Target {
706        &self.0
707    }
708}
709
710impl std::ops::DerefMut for Object {
711    fn deref_mut(&mut self) -> &mut Self::Target {
712        Arc::make_mut(&mut self.0)
713    }
714}
715
716impl ObjectInner {
717    /// Returns true if the object is a system package.
718    pub fn is_system_package(&self) -> bool {
719        self.is_package() && is_system_package(self.id())
720    }
721
722    pub fn is_immutable(&self) -> bool {
723        self.owner.is_immutable()
724    }
725
726    pub fn is_address_owned(&self) -> bool {
727        self.owner.is_address_owned()
728    }
729
730    pub fn is_child_object(&self) -> bool {
731        self.owner.is_child_object()
732    }
733
734    pub fn is_shared(&self) -> bool {
735        self.owner.is_shared()
736    }
737
738    pub fn get_single_owner(&self) -> Option<IotaAddress> {
739        self.owner.get_owner_address().ok()
740    }
741
742    // It's a common pattern to retrieve both the owner and object ID
743    // together, if it's owned by a singler owner.
744    pub fn get_owner_and_id(&self) -> Option<(Owner, ObjectID)> {
745        Some((self.owner, self.id()))
746    }
747
748    /// Return true if this object is a Move package, false if it is a Move
749    /// value
750    pub fn is_package(&self) -> bool {
751        matches!(&self.data, Data::Package(_))
752    }
753
754    pub fn compute_object_reference(&self) -> ObjectRef {
755        (self.id(), self.version(), self.digest())
756    }
757
758    pub fn digest(&self) -> ObjectDigest {
759        ObjectDigest::new(default_hash(self))
760    }
761
762    pub fn id(&self) -> ObjectID {
763        use Data::*;
764
765        match &self.data {
766            Move(v) => v.id(),
767            Package(m) => m.id(),
768        }
769    }
770
771    pub fn version(&self) -> SequenceNumber {
772        use Data::*;
773
774        match &self.data {
775            Move(o) => o.version(),
776            Package(p) => p.version(),
777        }
778    }
779
780    pub fn type_(&self) -> Option<&MoveObjectType> {
781        self.data.type_()
782    }
783
784    pub fn struct_tag(&self) -> Option<StructTag> {
785        self.data.struct_tag()
786    }
787
788    pub fn is_coin(&self) -> bool {
789        if let Some(move_object) = self.data.try_as_move() {
790            move_object.type_().is_coin()
791        } else {
792            false
793        }
794    }
795
796    pub fn is_gas_coin(&self) -> bool {
797        if let Some(move_object) = self.data.try_as_move() {
798            move_object.type_().is_gas_coin()
799        } else {
800            false
801        }
802    }
803
804    // TODO: use `MoveObj::get_balance_unsafe` instead.
805    // context: https://github.com/iotaledger/iota/pull/10679#discussion_r1165877816
806    pub fn as_coin_maybe(&self) -> Option<Coin> {
807        if let Some(move_object) = self.data.try_as_move() {
808            let coin: Coin = bcs::from_bytes(move_object.contents()).ok()?;
809            Some(coin)
810        } else {
811            None
812        }
813    }
814
815    pub fn as_timelock_balance_maybe(&self) -> Option<TimeLock<Balance>> {
816        if let Some(move_object) = self.data.try_as_move() {
817            Some(TimeLock::from_bcs_bytes(move_object.contents()).ok()?)
818        } else {
819            None
820        }
821    }
822
823    pub fn coin_type_maybe(&self) -> Option<TypeTag> {
824        if let Some(move_object) = self.data.try_as_move() {
825            move_object.type_().coin_type_maybe()
826        } else {
827            None
828        }
829    }
830
831    /// Return the `value: u64` field of a `Coin<T>` type.
832    /// Useful for reading the coin without deserializing the object into a Move
833    /// value It is the caller's responsibility to check that `self` is a
834    /// coin--this function may panic or do something unexpected otherwise.
835    pub fn get_coin_value_unsafe(&self) -> u64 {
836        self.data.try_as_move().unwrap().get_coin_value_unsafe()
837    }
838
839    /// Approximate size of the object in bytes. This is used for gas metering.
840    /// This will be slgihtly different from the serialized size, but
841    /// we also don't want to serialize the object just to get the size.
842    /// This approximation should be good enough for gas metering.
843    pub fn object_size_for_gas_metering(&self) -> usize {
844        let meta_data_size = size_of::<Owner>() + size_of::<TransactionDigest>() + size_of::<u64>();
845        let data_size = match &self.data {
846            Data::Move(m) => m.object_size_for_gas_metering(),
847            Data::Package(p) => p.object_size_for_gas_metering(),
848        };
849        meta_data_size + data_size
850    }
851
852    /// Change the owner of `self` to `new_owner`.
853    pub fn transfer(&mut self, new_owner: IotaAddress) {
854        self.owner = Owner::AddressOwner(new_owner);
855    }
856
857    /// Get a `MoveStructLayout` for `self`.
858    /// The `resolver` value must contain the module that declares `self.type_`
859    /// and the (transitive) dependencies of `self.type_` in order for this
860    /// to succeed. Failure will result in an `ObjectSerializationError`
861    pub fn get_layout(
862        &self,
863        resolver: &impl GetModule,
864    ) -> Result<Option<MoveStructLayout>, IotaError> {
865        match &self.data {
866            Data::Move(m) => Ok(Some(m.get_layout(resolver)?)),
867            Data::Package(_) => Ok(None),
868        }
869    }
870
871    /// Treat the object type as a Move struct with one type parameter,
872    /// like this: `S<T>`.
873    /// Returns the inner parameter type `T`.
874    pub fn get_move_template_type(&self) -> IotaResult<TypeTag> {
875        let move_struct = self.data.struct_tag().ok_or_else(|| IotaError::Type {
876            error: "Object must be a Move object".to_owned(),
877        })?;
878        fp_ensure!(
879            move_struct.type_params.len() == 1,
880            IotaError::Type {
881                error: "Move object struct must have one type parameter".to_owned()
882            }
883        );
884        // Index access safe due to checks above.
885        let type_tag = move_struct.type_params[0].clone();
886        Ok(type_tag)
887    }
888
889    pub fn to_rust<'de, T: Deserialize<'de>>(&'de self) -> Option<T> {
890        self.data.try_as_move().and_then(|data| data.to_rust())
891    }
892}
893
894// Testing-related APIs.
895impl Object {
896    /// Get the total amount of IOTA embedded in `self`, including both Move
897    /// objects and the storage rebate
898    pub fn get_total_iota(
899        &self,
900        layout_resolver: &mut dyn LayoutResolver,
901    ) -> Result<u64, IotaError> {
902        Ok(self.storage_rebate
903            + match &self.data {
904                Data::Move(m) => m.get_total_iota(layout_resolver)?,
905                Data::Package(_) => 0,
906            })
907    }
908
909    pub fn immutable_with_id_for_testing(id: ObjectID) -> Self {
910        let data = Data::Move(MoveObject {
911            type_: GasCoin::type_().into(),
912            version: OBJECT_START_VERSION,
913            contents: GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
914        });
915        ObjectInner {
916            owner: Owner::Immutable,
917            data,
918            previous_transaction: TransactionDigest::genesis_marker(),
919            storage_rebate: 0,
920        }
921        .into()
922    }
923
924    pub fn immutable_for_testing() -> Self {
925        thread_local! {
926            static IMMUTABLE_OBJECT_ID: ObjectID = ObjectID::random();
927        }
928
929        Self::immutable_with_id_for_testing(IMMUTABLE_OBJECT_ID.with(|id| *id))
930    }
931
932    /// Make a new random test shared object.
933    pub fn shared_for_testing() -> Object {
934        let id = ObjectID::random();
935        let obj = MoveObject::new_gas_coin(OBJECT_START_VERSION, id, 10);
936        let owner = Owner::Shared {
937            initial_shared_version: obj.version(),
938        };
939        Object::new_move(obj, owner, TransactionDigest::genesis_marker())
940    }
941
942    pub fn with_id_owner_gas_for_testing(id: ObjectID, owner: IotaAddress, gas: u64) -> Self {
943        let data = Data::Move(MoveObject {
944            type_: GasCoin::type_().into(),
945            version: OBJECT_START_VERSION,
946            contents: GasCoin::new(id, gas).to_bcs_bytes(),
947        });
948        ObjectInner {
949            owner: Owner::AddressOwner(owner),
950            data,
951            previous_transaction: TransactionDigest::genesis_marker(),
952            storage_rebate: 0,
953        }
954        .into()
955    }
956
957    pub fn treasury_cap_for_testing(struct_tag: StructTag, treasury_cap: TreasuryCap) -> Self {
958        let data = Data::Move(MoveObject {
959            type_: TreasuryCap::type_(struct_tag).into(),
960            version: OBJECT_START_VERSION,
961            contents: bcs::to_bytes(&treasury_cap).expect("Failed to serialize"),
962        });
963        ObjectInner {
964            owner: Owner::Immutable,
965            data,
966            previous_transaction: TransactionDigest::genesis_marker(),
967            storage_rebate: 0,
968        }
969        .into()
970    }
971
972    pub fn coin_metadata_for_testing(struct_tag: StructTag, metadata: CoinMetadata) -> Self {
973        let data = Data::Move(MoveObject {
974            type_: CoinMetadata::type_(struct_tag).into(),
975            version: OBJECT_START_VERSION,
976            contents: bcs::to_bytes(&metadata).expect("Failed to serialize"),
977        });
978        ObjectInner {
979            owner: Owner::Immutable,
980            data,
981            previous_transaction: TransactionDigest::genesis_marker(),
982            storage_rebate: 0,
983        }
984        .into()
985    }
986
987    pub fn with_object_owner_for_testing(id: ObjectID, owner: ObjectID) -> Self {
988        let data = Data::Move(MoveObject {
989            type_: GasCoin::type_().into(),
990            version: OBJECT_START_VERSION,
991            contents: GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
992        });
993        ObjectInner {
994            owner: Owner::ObjectOwner(owner.into()),
995            data,
996            previous_transaction: TransactionDigest::genesis_marker(),
997            storage_rebate: 0,
998        }
999        .into()
1000    }
1001
1002    pub fn with_id_owner_for_testing(id: ObjectID, owner: IotaAddress) -> Self {
1003        // For testing, we provide sufficient gas by default.
1004        Self::with_id_owner_gas_for_testing(id, owner, GAS_VALUE_FOR_TESTING)
1005    }
1006
1007    pub fn with_id_owner_version_for_testing(
1008        id: ObjectID,
1009        version: SequenceNumber,
1010        owner: IotaAddress,
1011    ) -> Self {
1012        let data = Data::Move(MoveObject {
1013            type_: GasCoin::type_().into(),
1014            version,
1015            contents: GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
1016        });
1017        ObjectInner {
1018            owner: Owner::AddressOwner(owner),
1019            data,
1020            previous_transaction: TransactionDigest::genesis_marker(),
1021            storage_rebate: 0,
1022        }
1023        .into()
1024    }
1025
1026    pub fn with_owner_for_testing(owner: IotaAddress) -> Self {
1027        Self::with_id_owner_for_testing(ObjectID::random(), owner)
1028    }
1029
1030    /// Generate a new gas coin worth `value` with a random object ID and owner
1031    /// For testing purposes only
1032    pub fn new_gas_with_balance_and_owner_for_testing(value: u64, owner: IotaAddress) -> Self {
1033        let obj = MoveObject::new_gas_coin(OBJECT_START_VERSION, ObjectID::random(), value);
1034        Object::new_move(
1035            obj,
1036            Owner::AddressOwner(owner),
1037            TransactionDigest::genesis_marker(),
1038        )
1039    }
1040
1041    /// Generate a new gas coin object with default balance and random owner.
1042    pub fn new_gas_for_testing() -> Self {
1043        let gas_object_id = ObjectID::random();
1044        let (owner, _) = deterministic_random_account_key();
1045        Object::with_id_owner_for_testing(gas_object_id, owner)
1046    }
1047}
1048
1049/// Make a few test gas objects (all with the same random owner).
1050pub fn generate_test_gas_objects() -> Vec<Object> {
1051    thread_local! {
1052        static GAS_OBJECTS: Vec<Object> = (0..50)
1053            .map(|_| {
1054                let gas_object_id = ObjectID::random();
1055                let (owner, _) = deterministic_random_account_key();
1056                Object::with_id_owner_for_testing(gas_object_id, owner)
1057            })
1058            .collect();
1059    }
1060
1061    GAS_OBJECTS.with(|v| v.clone())
1062}
1063
1064#[derive(Serialize, Deserialize, Debug)]
1065#[serde(tag = "status", content = "details")]
1066pub enum ObjectRead {
1067    NotExists(ObjectID),
1068    Exists(ObjectRef, Object, Option<MoveStructLayout>),
1069    Deleted(ObjectRef),
1070}
1071
1072impl ObjectRead {
1073    /// Returns the object value if there is any, otherwise an Err if
1074    /// the object does not exist or is deleted.
1075    pub fn into_object(self) -> UserInputResult<Object> {
1076        match self {
1077            Self::Deleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: oref }),
1078            Self::NotExists(id) => Err(UserInputError::ObjectNotFound {
1079                object_id: id,
1080                version: None,
1081            }),
1082            Self::Exists(_, o, _) => Ok(o),
1083        }
1084    }
1085
1086    pub fn object(&self) -> UserInputResult<&Object> {
1087        match self {
1088            Self::Deleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: *oref }),
1089            Self::NotExists(id) => Err(UserInputError::ObjectNotFound {
1090                object_id: *id,
1091                version: None,
1092            }),
1093            Self::Exists(_, o, _) => Ok(o),
1094        }
1095    }
1096
1097    pub fn object_id(&self) -> ObjectID {
1098        match self {
1099            Self::Deleted(oref) => oref.0,
1100            Self::NotExists(id) => *id,
1101            Self::Exists(oref, _, _) => oref.0,
1102        }
1103    }
1104}
1105
1106impl Display for ObjectRead {
1107    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1108        match self {
1109            Self::Deleted(oref) => {
1110                write!(f, "ObjectRead::Deleted ({:?})", oref)
1111            }
1112            Self::NotExists(id) => {
1113                write!(f, "ObjectRead::NotExists ({:?})", id)
1114            }
1115            Self::Exists(oref, _, _) => {
1116                write!(f, "ObjectRead::Exists ({:?})", oref)
1117            }
1118        }
1119    }
1120}
1121
1122#[derive(Serialize, Deserialize, Debug)]
1123#[serde(tag = "status", content = "details")]
1124pub enum PastObjectRead {
1125    /// The object does not exist
1126    ObjectNotExists(ObjectID),
1127    /// The object is found to be deleted with this version
1128    ObjectDeleted(ObjectRef),
1129    /// The object exists and is found with this version
1130    VersionFound(ObjectRef, Object, Option<MoveStructLayout>),
1131    /// The object exists but not found with this version
1132    VersionNotFound(ObjectID, SequenceNumber),
1133    /// The asked object version is higher than the latest
1134    VersionTooHigh {
1135        object_id: ObjectID,
1136        asked_version: SequenceNumber,
1137        latest_version: SequenceNumber,
1138    },
1139}
1140
1141impl PastObjectRead {
1142    /// Returns the object value if there is any, otherwise an Err
1143    pub fn into_object(self) -> UserInputResult<Object> {
1144        match self {
1145            Self::ObjectDeleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: oref }),
1146            Self::ObjectNotExists(id) => Err(UserInputError::ObjectNotFound {
1147                object_id: id,
1148                version: None,
1149            }),
1150            Self::VersionFound(_, o, _) => Ok(o),
1151            Self::VersionNotFound(object_id, version) => Err(UserInputError::ObjectNotFound {
1152                object_id,
1153                version: Some(version),
1154            }),
1155            Self::VersionTooHigh {
1156                object_id,
1157                asked_version,
1158                latest_version,
1159            } => Err(UserInputError::ObjectSequenceNumberTooHigh {
1160                object_id,
1161                asked_version,
1162                latest_version,
1163            }),
1164        }
1165    }
1166}
1167
1168impl Display for PastObjectRead {
1169    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1170        match self {
1171            Self::ObjectDeleted(oref) => {
1172                write!(f, "PastObjectRead::ObjectDeleted ({:?})", oref)
1173            }
1174            Self::ObjectNotExists(id) => {
1175                write!(f, "PastObjectRead::ObjectNotExists ({:?})", id)
1176            }
1177            Self::VersionFound(oref, _, _) => {
1178                write!(f, "PastObjectRead::VersionFound ({:?})", oref)
1179            }
1180            Self::VersionNotFound(object_id, version) => {
1181                write!(
1182                    f,
1183                    "PastObjectRead::VersionNotFound ({:?}, asked sequence number {:?})",
1184                    object_id, version
1185                )
1186            }
1187            Self::VersionTooHigh {
1188                object_id,
1189                asked_version,
1190                latest_version,
1191            } => {
1192                write!(
1193                    f,
1194                    "PastObjectRead::VersionTooHigh ({:?}, asked sequence number {:?}, latest sequence number {:?})",
1195                    object_id, asked_version, latest_version
1196                )
1197            }
1198        }
1199    }
1200}
1201
1202#[cfg(test)]
1203mod tests {
1204    use crate::{
1205        base_types::{IotaAddress, ObjectID, TransactionDigest},
1206        gas_coin::GasCoin,
1207        object::{OBJECT_START_VERSION, Object, Owner},
1208    };
1209
1210    // Ensure that object digest computation and bcs serialized format are not
1211    // inadvertently changed.
1212    #[test]
1213    fn test_object_digest_and_serialized_format() {
1214        let g =
1215            GasCoin::new_for_testing_with_id(ObjectID::ZERO, 123).to_object(OBJECT_START_VERSION);
1216        let o = Object::new_move(
1217            g,
1218            Owner::AddressOwner(IotaAddress::ZERO),
1219            TransactionDigest::ZERO,
1220        );
1221        let bytes = bcs::to_bytes(&o).unwrap();
1222
1223        assert_eq!(
1224            bytes,
1225            [
1226                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,
1227                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,
1228                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,
1229                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,
1230                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1231            ]
1232        );
1233        let objref = format!("{:?}", o.compute_object_reference());
1234        assert_eq!(
1235            objref,
1236            "(0x0000000000000000000000000000000000000000000000000000000000000000, SequenceNumber(1), o#Ba4YyVBcpc9jgX4PMLRoyt9dKLftYVSDvuKbtMr9f4NM)"
1237        );
1238    }
1239
1240    #[test]
1241    fn test_get_coin_value_unsafe() {
1242        fn test_for_value(v: u64) {
1243            let g = GasCoin::new_for_testing(v).to_object(OBJECT_START_VERSION);
1244            assert_eq!(g.get_coin_value_unsafe(), v);
1245            assert_eq!(GasCoin::try_from(&g).unwrap().value(), v);
1246        }
1247
1248        test_for_value(0);
1249        test_for_value(1);
1250        test_for_value(8);
1251        test_for_value(9);
1252        test_for_value(u8::MAX as u64);
1253        test_for_value(u8::MAX as u64 + 1);
1254        test_for_value(u16::MAX as u64);
1255        test_for_value(u16::MAX as u64 + 1);
1256        test_for_value(u32::MAX as u64);
1257        test_for_value(u32::MAX as u64 + 1);
1258        test_for_value(u64::MAX);
1259    }
1260
1261    #[test]
1262    fn test_set_coin_value_unsafe() {
1263        fn test_for_value(v: u64) {
1264            let mut g = GasCoin::new_for_testing(u64::MAX).to_object(OBJECT_START_VERSION);
1265            g.set_coin_value_unsafe(v);
1266            assert_eq!(g.get_coin_value_unsafe(), v);
1267            assert_eq!(GasCoin::try_from(&g).unwrap().value(), v);
1268            assert_eq!(g.version(), OBJECT_START_VERSION);
1269            assert_eq!(g.contents().len(), 40);
1270        }
1271
1272        test_for_value(0);
1273        test_for_value(1);
1274        test_for_value(8);
1275        test_for_value(9);
1276        test_for_value(u8::MAX as u64);
1277        test_for_value(u8::MAX as u64 + 1);
1278        test_for_value(u16::MAX as u64);
1279        test_for_value(u16::MAX as u64 + 1);
1280        test_for_value(u32::MAX as u64);
1281        test_for_value(u32::MAX as u64 + 1);
1282        test_for_value(u64::MAX);
1283    }
1284}