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    fmt::{Debug, Display, Formatter},
8    mem::size_of,
9    sync::Arc,
10};
11
12use iota_protocol_config::ProtocolConfig;
13pub use iota_sdk_types::{MoveStruct as MoveObject, ObjectData as Data, Owner};
14use iota_sdk_types::{StructTag, TypeTag};
15use move_binary_format::CompiledModule;
16use move_bytecode_utils::{layout::TypeLayoutBuilder, module_cache::GetModule};
17use move_core_types::annotated_value::{MoveStruct, MoveStructLayout, MoveTypeLayout, MoveValue};
18use serde::{Deserialize, Serialize};
19
20use self::{balance_traversal::BalanceTraversal, bounded_visitor::BoundedVisitor};
21use crate::{
22    balance::Balance,
23    base_types::{
24        IotaAddress, MoveObjectType, ObjectDigest, ObjectID, ObjectRef, SequenceNumber,
25        TransactionDigest,
26    },
27    coin::{Coin, CoinMetadata, TreasuryCap},
28    crypto::{default_hash, deterministic_random_account_key},
29    error::{
30        ExecutionError, ExecutionErrorKind, IotaError, IotaResult, UserInputError, UserInputResult,
31    },
32    gas_coin::{GAS, GasCoin},
33    iota_sdk_types_conversions::type_tag_sdk_to_core,
34    layout_resolver::LayoutResolver,
35    move_package::{MovePackage, MovePackageExt},
36    timelock::timelock::TimeLock,
37};
38
39mod balance_traversal;
40pub mod bounded_visitor;
41pub mod option_visitor;
42
43pub const GAS_VALUE_FOR_TESTING: u64 = 300_000_000_000_000;
44pub const OBJECT_START_VERSION: SequenceNumber = SequenceNumber::from_u64(1);
45
46/// Index marking the end of the object's ID + the beginning of its version
47pub const ID_END_INDEX: usize = ObjectID::LENGTH;
48
49mod move_object_ext {
50    pub trait Sealed {}
51    impl Sealed for super::MoveObject {}
52}
53
54pub trait MoveObjectExt: Sized + move_object_ext::Sealed {
55    fn new_from_execution(
56        tag: StructTag,
57        version: SequenceNumber,
58        contents: Vec<u8>,
59        protocol_config: &ProtocolConfig,
60    ) -> Result<Self, ExecutionError>;
61    fn new_from_execution_with_limit(
62        tag: StructTag,
63        version: SequenceNumber,
64        contents: Vec<u8>,
65        max_move_object_size: u64,
66    ) -> Result<Self, ExecutionError>;
67    fn new_gas_coin(version: SequenceNumber, id: ObjectID, value: u64) -> Self;
68    fn new_coin(coin_type: TypeTag, version: SequenceNumber, id: ObjectID, value: u64) -> Self;
69    fn get_coin_value_unchecked(&self) -> u64;
70    fn set_coin_value_unchecked(&mut self, value: u64);
71    fn set_clock_timestamp_ms_unchecked(&mut self, timestamp_ms: u64);
72    fn update_contents(
73        &mut self,
74        new_contents: Vec<u8>,
75        protocol_config: &ProtocolConfig,
76    ) -> Result<(), ExecutionError>;
77    fn update_contents_with_limit(
78        &mut self,
79        new_contents: Vec<u8>,
80        max_move_object_size: u64,
81    ) -> Result<(), ExecutionError>;
82    fn increment_version_to(&mut self, next: SequenceNumber);
83    fn decrement_version_to(&mut self, prev: SequenceNumber);
84    fn get_layout(&self, resolver: &impl GetModule) -> Result<MoveStructLayout, IotaError>;
85    fn get_struct_layout_from_struct_tag(
86        struct_tag: StructTag,
87        resolver: &impl GetModule,
88    ) -> Result<MoveStructLayout, IotaError>;
89    fn to_move_struct(&self, layout: &MoveStructLayout) -> Result<MoveStruct, IotaError>;
90    fn object_size_for_gas_metering(&self) -> usize;
91    fn get_total_iota(&self, layout_resolver: &mut dyn LayoutResolver) -> Result<u64, IotaError>;
92    fn get_coin_balances(
93        &self,
94        layout_resolver: &mut dyn LayoutResolver,
95    ) -> Result<BTreeMap<TypeTag, u64>, IotaError>;
96}
97
98impl MoveObjectExt for MoveObject {
99    /// Creates a new Move object of type `tag` with BCS encoded bytes in
100    /// `contents`.
101    fn new_from_execution(
102        tag: StructTag,
103        version: SequenceNumber,
104        contents: Vec<u8>,
105        protocol_config: &ProtocolConfig,
106    ) -> Result<Self, ExecutionError> {
107        Self::new_from_execution_with_limit(
108            tag,
109            version,
110            contents,
111            protocol_config.max_move_object_size(),
112        )
113    }
114
115    /// Creates a new Move object of type `tag` with BCS encoded bytes in
116    /// `contents`. It allows to set a `max_move_object_size` for that.
117    fn new_from_execution_with_limit(
118        tag: StructTag,
119        version: SequenceNumber,
120        contents: Vec<u8>,
121        max_move_object_size: u64,
122    ) -> Result<Self, ExecutionError> {
123        if contents.len() as u64 > max_move_object_size {
124            return Err(ExecutionError::from_kind(
125                ExecutionErrorKind::ObjectTooBig {
126                    object_size: contents.len() as u64,
127                    max_object_size: max_move_object_size,
128                },
129            ));
130        }
131        Self::new(tag.into(), version, contents).map_err(ExecutionError::invariant_violation)
132    }
133
134    fn new_gas_coin(version: SequenceNumber, id: ObjectID, value: u64) -> Self {
135        // unwrap safe because coins are always smaller than the max object size
136
137        Self::new_from_execution_with_limit(
138            StructTag::new_gas_coin(),
139            version,
140            GasCoin::new(id, value).to_bcs_bytes(),
141            256,
142        )
143        .unwrap()
144    }
145
146    fn new_coin(coin_type: TypeTag, version: SequenceNumber, id: ObjectID, value: u64) -> Self {
147        // unwrap safe because coins are always smaller than the max object size
148
149        Self::new_from_execution_with_limit(
150            StructTag::new_coin(coin_type),
151            version,
152            Coin::new(id, value).to_bcs_bytes(),
153            256,
154        )
155        .unwrap()
156    }
157
158    /// Return the `value: u64` field of a `Coin<T>` type.
159    /// Useful for reading the coin without deserializing the object into a Move
160    /// value. It is the caller's responsibility to check that `self` is a coin.
161    /// This function may panic or do something unexpected otherwise.
162    fn get_coin_value_unchecked(&self) -> u64 {
163        debug_assert!(self.object_type().is_coin());
164        // 32 bytes for object ID, 8 for balance
165        debug_assert!(self.contents().len() == 40);
166
167        // unwrap safe because we checked that it is a coin
168        u64::from_le_bytes(<[u8; 8]>::try_from(&self.contents()[ID_END_INDEX..]).unwrap())
169    }
170
171    /// Update the `value: u64` field of a `Coin<T>` type.
172    /// Useful for updating the coin without deserializing the object into a
173    /// Move value. It is the caller's responsibility to check that `self` is a
174    /// coin.
175    /// This function may panic or do something unexpected otherwise.
176    fn set_coin_value_unchecked(&mut self, value: u64) {
177        debug_assert!(self.object_type().is_coin());
178        // 32 bytes for object ID, 8 for balance
179        debug_assert!(self.contents().len() == 40);
180
181        let mut new_contents = self.contents().to_vec();
182        new_contents[ID_END_INDEX..].copy_from_slice(&value.to_le_bytes());
183        self.set_contents(new_contents).unwrap();
184    }
185
186    /// Update the `timestamp_ms: u64` field of the `Clock` type.
187    ///
188    /// Panics if the object isn't a `Clock`.
189    fn set_clock_timestamp_ms_unchecked(&mut self, timestamp_ms: u64) {
190        assert!(self.struct_tag().is_clock());
191        // 32 bytes for object ID, 8 for timestamp
192        assert!(self.contents().len() == 40);
193
194        let mut new_contents = self.contents().to_vec();
195        new_contents[ID_END_INDEX..].copy_from_slice(&timestamp_ms.to_le_bytes());
196        self.set_contents(new_contents).unwrap();
197    }
198
199    /// Update the contents of this object but does not increment its version
200    fn update_contents(
201        &mut self,
202        new_contents: Vec<u8>,
203        protocol_config: &ProtocolConfig,
204    ) -> Result<(), ExecutionError> {
205        self.update_contents_with_limit(new_contents, protocol_config.max_move_object_size())
206    }
207
208    fn update_contents_with_limit(
209        &mut self,
210        new_contents: Vec<u8>,
211        max_move_object_size: u64,
212    ) -> Result<(), ExecutionError> {
213        if new_contents.len() as u64 > max_move_object_size {
214            return Err(ExecutionError::from_kind(
215                ExecutionErrorKind::ObjectTooBig {
216                    object_size: new_contents.len() as u64,
217                    max_object_size: max_move_object_size,
218                },
219            ));
220        }
221
222        #[cfg(debug_assertions)]
223        let old_id = self.id();
224
225        self.set_contents(new_contents)
226            .map_err(ExecutionError::invariant_violation)?;
227
228        // Update should not modify ID
229        #[cfg(debug_assertions)]
230        debug_assert_eq!(self.id(), old_id);
231
232        Ok(())
233    }
234
235    /// Sets the version of this object to a new value which is assumed to be
236    /// higher (and checked to be higher in debug).
237    fn increment_version_to(&mut self, next: SequenceNumber) {
238        debug_assert!(
239            self.version() < next,
240            "Not an increment: {} to {next}",
241            self.version()
242        );
243        self.set_version(next);
244    }
245
246    /// Sets the version to a lower value (checked in debug).
247    fn decrement_version_to(&mut self, prev: SequenceNumber) {
248        debug_assert!(
249            prev < self.version(),
250            "Not a decrement: {} to {prev}",
251            self.version()
252        );
253        self.set_version(prev);
254    }
255
256    /// Get a `MoveStructLayout` for `self`.
257    /// The `resolver` value must contain the module that declares
258    /// `self.object_type` and the (transitive) dependencies of
259    /// `self.object_type` in order for this to succeed. Failure will result
260    /// in an `ObjectSerializationError`
261    fn get_layout(&self, resolver: &impl GetModule) -> Result<MoveStructLayout, IotaError> {
262        Self::get_struct_layout_from_struct_tag(self.struct_tag().clone(), resolver)
263    }
264
265    fn get_struct_layout_from_struct_tag(
266        struct_tag: StructTag,
267        resolver: &impl GetModule,
268    ) -> Result<MoveStructLayout, IotaError> {
269        let type_ = TypeTag::Struct(Box::new(struct_tag));
270        let layout = TypeLayoutBuilder::build_with_types(&type_tag_sdk_to_core(&type_), resolver)
271            .map_err(|e| IotaError::ObjectSerialization {
272            error: e.to_string(),
273        })?;
274        match layout {
275            MoveTypeLayout::Struct(l) => Ok(*l),
276            _ => unreachable!(
277                "We called build_with_types on Struct type, should get a struct layout"
278            ),
279        }
280    }
281
282    /// Convert `self` to the JSON representation dictated by `layout`.
283    fn to_move_struct(&self, layout: &MoveStructLayout) -> Result<MoveStruct, IotaError> {
284        BoundedVisitor::deserialize_struct(self.contents(), layout).map_err(|e| {
285            IotaError::ObjectSerialization {
286                error: e.to_string(),
287            }
288        })
289    }
290
291    /// Approximate size of the object in bytes. This is used for gas metering.
292    /// For the type tag field, we serialize it on the spot to get the accurate
293    /// size. This should not be very expensive since the type tag is
294    /// usually simple, and we only do this once per object being mutated.
295    fn object_size_for_gas_metering(&self) -> usize {
296        let serialized_type_tag_size =
297            bcs::serialized_size(self.object_type()).expect("Serializing type tag should not fail");
298        // + 8 for `version`
299        self.contents().len() + serialized_type_tag_size + 8
300    }
301
302    /// Get the total amount of IOTA embedded in `self`. Intended for testing
303    /// purposes
304    fn get_total_iota(&self, layout_resolver: &mut dyn LayoutResolver) -> Result<u64, IotaError> {
305        let balances = self.get_coin_balances(layout_resolver)?;
306        Ok(balances.get(&GAS::type_tag()).copied().unwrap_or(0))
307    }
308
309    /// Get the total balances for all `Coin<T>` embedded in `self`.
310    fn get_coin_balances(
311        &self,
312        layout_resolver: &mut dyn LayoutResolver,
313    ) -> Result<BTreeMap<TypeTag, u64>, IotaError> {
314        // Fast path without deserialization.
315        if let Some(type_tag) = self.object_type().coin_type_opt() {
316            let balance = self.get_coin_value_unchecked();
317            Ok(if balance > 0 {
318                BTreeMap::from([(type_tag.clone(), balance)])
319            } else {
320                BTreeMap::default()
321            })
322        } else {
323            let layout = layout_resolver.get_annotated_layout(self.struct_tag())?;
324
325            let mut traversal = BalanceTraversal::default();
326            MoveValue::visit_deserialize(self.contents(), &layout.into_layout(), &mut traversal)
327                .map_err(|e| IotaError::ObjectSerialization {
328                    error: e.to_string(),
329                })?;
330
331            Ok(traversal.finish())
332        }
333    }
334}
335
336#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
337#[serde(rename = "Object")]
338pub struct ObjectInner {
339    /// The meat of the object
340    pub data: Data,
341    /// The owner that unlocks this object
342    pub owner: Owner,
343    /// The digest of the transaction that created or last mutated this object
344    pub previous_transaction: TransactionDigest,
345    /// The amount of IOTA we would rebate if this object gets deleted.
346    /// This number is re-calculated each time the object is mutated based on
347    /// the present storage gas price.
348    pub storage_rebate: u64,
349}
350
351#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
352#[serde(from = "ObjectInner")]
353pub struct Object(Arc<ObjectInner>);
354
355impl From<ObjectInner> for Object {
356    fn from(inner: ObjectInner) -> Self {
357        Self(Arc::new(inner))
358    }
359}
360
361impl Object {
362    pub fn into_inner(self) -> ObjectInner {
363        match Arc::try_unwrap(self.0) {
364            Ok(inner) => inner,
365            Err(inner_arc) => (*inner_arc).clone(),
366        }
367    }
368
369    pub fn as_inner(&self) -> &ObjectInner {
370        &self.0
371    }
372
373    pub fn owner(&self) -> &Owner {
374        &self.0.owner
375    }
376
377    pub fn new_from_genesis(
378        data: Data,
379        owner: Owner,
380        previous_transaction: TransactionDigest,
381    ) -> Self {
382        ObjectInner {
383            data,
384            owner,
385            previous_transaction,
386            storage_rebate: 0,
387        }
388        .into()
389    }
390
391    /// Create a new Move object
392    pub fn new_move(o: MoveObject, owner: Owner, previous_transaction: TransactionDigest) -> Self {
393        ObjectInner {
394            data: Data::Struct(o),
395            owner,
396            previous_transaction,
397            storage_rebate: 0,
398        }
399        .into()
400    }
401
402    pub fn new_package_from_data(data: Data, previous_transaction: TransactionDigest) -> Self {
403        ObjectInner {
404            data,
405            owner: Owner::Immutable,
406            previous_transaction,
407            storage_rebate: 0,
408        }
409        .into()
410    }
411
412    // Note: this will panic if `modules` is empty
413    pub fn new_from_package(package: MovePackage, previous_transaction: TransactionDigest) -> Self {
414        Self::new_package_from_data(Data::Package(package), previous_transaction)
415    }
416
417    pub fn new_package<'p>(
418        modules: &[CompiledModule],
419        previous_transaction: TransactionDigest,
420        protocol_config: &ProtocolConfig,
421        dependencies: impl IntoIterator<Item = &'p MovePackage>,
422    ) -> Result<Self, ExecutionError> {
423        Ok(Self::new_package_from_data(
424            Data::Package(MovePackage::new_initial(
425                modules,
426                protocol_config,
427                dependencies,
428            )?),
429            previous_transaction,
430        ))
431    }
432
433    pub fn new_upgraded_package<'p>(
434        previous_package: &MovePackage,
435        new_package_id: ObjectID,
436        modules: &[CompiledModule],
437        previous_transaction: TransactionDigest,
438        protocol_config: &ProtocolConfig,
439        dependencies: impl IntoIterator<Item = &'p MovePackage>,
440    ) -> Result<Self, ExecutionError> {
441        Ok(Self::new_package_from_data(
442            Data::Package(previous_package.new_upgraded(
443                new_package_id,
444                modules,
445                protocol_config,
446                dependencies,
447            )?),
448            previous_transaction,
449        ))
450    }
451
452    pub fn new_package_for_testing(
453        modules: &[CompiledModule],
454        previous_transaction: TransactionDigest,
455        dependencies: impl IntoIterator<Item = MovePackage>,
456    ) -> Result<Self, ExecutionError> {
457        let dependencies: Vec<_> = dependencies.into_iter().collect();
458        let config = ProtocolConfig::get_for_max_version_UNSAFE();
459        Self::new_package(modules, previous_transaction, &config, &dependencies)
460    }
461
462    /// Create a system package which is not subject to size limits. Panics if
463    /// the object ID is not a known system package.
464    pub fn new_system_package(
465        modules: &[CompiledModule],
466        version: SequenceNumber,
467        dependencies: Vec<ObjectID>,
468        previous_transaction: TransactionDigest,
469    ) -> Self {
470        let ret = Self::new_package_from_data(
471            Data::Package(MovePackage::new_system(version, modules, dependencies)),
472            previous_transaction,
473        );
474
475        #[cfg(not(msim))]
476        assert!(ret.is_system_package());
477
478        ret
479    }
480}
481
482impl std::ops::Deref for Object {
483    type Target = ObjectInner;
484    fn deref(&self) -> &Self::Target {
485        &self.0
486    }
487}
488
489impl std::ops::DerefMut for Object {
490    fn deref_mut(&mut self) -> &mut Self::Target {
491        Arc::make_mut(&mut self.0)
492    }
493}
494
495impl ObjectInner {
496    /// Returns true if the object is a system package.
497    pub fn is_system_package(&self) -> bool {
498        self.is_package() && self.id().is_system_package()
499    }
500
501    pub fn is_immutable(&self) -> bool {
502        self.owner.is_immutable()
503    }
504
505    pub fn is_address_owned(&self) -> bool {
506        self.owner.is_address()
507    }
508
509    pub fn is_child_object(&self) -> bool {
510        self.owner.is_object()
511    }
512
513    pub fn is_shared(&self) -> bool {
514        self.owner.is_shared()
515    }
516
517    pub fn get_single_owner(&self) -> Option<IotaAddress> {
518        self.owner.address_or_object().copied()
519    }
520
521    // It's a common pattern to retrieve both the owner and object ID
522    // together, if it's owned by a single owner.
523    pub fn get_owner_and_id(&self) -> Option<(Owner, ObjectID)> {
524        Some((self.owner, self.id()))
525    }
526
527    /// Return true if this object is a Move package, false if it is a Move
528    /// value
529    pub fn is_package(&self) -> bool {
530        matches!(&self.data, Data::Package(_))
531    }
532
533    pub fn compute_object_reference(&self) -> ObjectRef {
534        ObjectRef::new(self.id(), self.version(), self.digest())
535    }
536
537    pub fn digest(&self) -> ObjectDigest {
538        ObjectDigest::new(default_hash(self))
539    }
540
541    pub fn id(&self) -> ObjectID {
542        use Data::*;
543
544        match &self.data {
545            Struct(v) => v.id(),
546            Package(m) => m.id(),
547        }
548    }
549
550    pub fn version(&self) -> SequenceNumber {
551        use Data::*;
552
553        match &self.data {
554            Struct(o) => o.version(),
555            Package(p) => p.version(),
556        }
557    }
558
559    pub fn type_(&self) -> Option<&MoveObjectType> {
560        self.data.object_type()
561    }
562
563    pub fn struct_tag(&self) -> Option<StructTag> {
564        self.data.struct_tag()
565    }
566
567    pub fn is_coin(&self) -> bool {
568        if let Some(move_object) = self.data.as_struct_opt() {
569            move_object.struct_tag().is_coin()
570        } else {
571            false
572        }
573    }
574
575    pub fn is_gas_coin(&self) -> bool {
576        if let Some(move_object) = self.data.as_struct_opt() {
577            move_object.struct_tag().is_gas_coin()
578        } else {
579            false
580        }
581    }
582
583    // TODO: use `MoveObj::get_balance_unsafe` instead.
584    // context: https://github.com/iotaledger/iota/pull/10679#discussion_r1165877816
585    pub fn as_coin_maybe(&self) -> Option<Coin> {
586        if let Some(move_object) = self.data.as_struct_opt() {
587            let coin: Coin = bcs::from_bytes(move_object.contents()).ok()?;
588            Some(coin)
589        } else {
590            None
591        }
592    }
593
594    pub fn as_timelock_balance_maybe(&self) -> Option<TimeLock<Balance>> {
595        if let Some(move_object) = self.data.as_struct_opt() {
596            Some(TimeLock::from_bcs_bytes(move_object.contents()).ok()?)
597        } else {
598            None
599        }
600    }
601
602    pub fn coin_type_opt(&self) -> Option<&TypeTag> {
603        if let Some(move_object) = self.data.as_struct_opt() {
604            move_object.struct_tag().coin_type_opt()
605        } else {
606            None
607        }
608    }
609
610    /// Return the `value: u64` field of a `Coin<T>` type.
611    /// Useful for reading the coin without deserializing the object into a Move
612    /// value It is the caller's responsibility to check that `self` is a
613    /// coin--this function may panic or do something unexpected otherwise.
614    pub fn get_coin_value_unchecked(&self) -> u64 {
615        self.data
616            .as_struct_opt()
617            .unwrap()
618            .get_coin_value_unchecked()
619    }
620
621    /// Approximate size of the object in bytes. This is used for gas metering.
622    /// This will be slightly different from the serialized size, but
623    /// we also don't want to serialize the object just to get the size.
624    /// This approximation should be good enough for gas metering.
625    pub fn object_size_for_gas_metering(&self) -> usize {
626        let meta_data_size = size_of::<Owner>() + size_of::<TransactionDigest>() + size_of::<u64>();
627        let data_size = match &self.data {
628            Data::Struct(m) => m.object_size_for_gas_metering(),
629            Data::Package(p) => p.size(),
630        };
631        meta_data_size + data_size
632    }
633
634    /// Change the owner of `self` to `new_owner`.
635    pub fn transfer(&mut self, new_owner: IotaAddress) {
636        self.owner = Owner::Address(new_owner);
637    }
638
639    /// Get a `MoveStructLayout` for `self`.
640    /// The `resolver` value must contain the module that declares
641    /// `self.object_type` and the (transitive) dependencies of
642    /// `self.object_type` in order for this to succeed. Failure will result
643    /// in an `ObjectSerializationError`
644    pub fn get_layout(
645        &self,
646        resolver: &impl GetModule,
647    ) -> Result<Option<MoveStructLayout>, IotaError> {
648        match &self.data {
649            Data::Struct(m) => Ok(Some(m.get_layout(resolver)?)),
650            Data::Package(_) => Ok(None),
651        }
652    }
653
654    /// Treat the object type as a Move struct with one type parameter,
655    /// like this: `S<T>`.
656    /// Returns the inner parameter type `T`.
657    pub fn get_move_template_type(&self) -> IotaResult<TypeTag> {
658        let move_struct = self.data.struct_tag().ok_or_else(|| IotaError::Type {
659            error: "Object must be a Move object".to_owned(),
660        })?;
661        fp_ensure!(
662            move_struct.type_params().len() == 1,
663            IotaError::Type {
664                error: "Move object struct must have one type parameter".to_owned()
665            }
666        );
667        // Index access safe due to checks above.
668        let type_tag = move_struct.type_params()[0].clone();
669        Ok(type_tag)
670    }
671
672    pub fn to_rust<'de, T: Deserialize<'de>>(&'de self) -> Option<T> {
673        self.data.as_struct_opt().and_then(|data| data.to_rust())
674    }
675}
676
677// Testing-related APIs.
678impl Object {
679    /// Get the total amount of IOTA embedded in `self`, including both Move
680    /// objects and the storage rebate
681    pub fn get_total_iota(
682        &self,
683        layout_resolver: &mut dyn LayoutResolver,
684    ) -> Result<u64, IotaError> {
685        Ok(self.storage_rebate
686            + match &self.data {
687                Data::Struct(m) => m.get_total_iota(layout_resolver)?,
688                Data::Package(_) => 0,
689            })
690    }
691
692    pub fn immutable_with_id_for_testing(id: ObjectID) -> Self {
693        let data = Data::Struct(
694            MoveObject::new(
695                StructTag::new_gas_coin().into(),
696                OBJECT_START_VERSION,
697                GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
698            )
699            .unwrap(),
700        );
701        ObjectInner {
702            owner: Owner::Immutable,
703            data,
704            previous_transaction: TransactionDigest::GENESIS_MARKER,
705            storage_rebate: 0,
706        }
707        .into()
708    }
709
710    pub fn immutable_for_testing() -> Self {
711        thread_local! {
712            static IMMUTABLE_OBJECT_ID: ObjectID = ObjectID::random();
713        }
714
715        Self::immutable_with_id_for_testing(IMMUTABLE_OBJECT_ID.with(|id| *id))
716    }
717
718    /// Make a new random test shared object.
719    pub fn shared_for_testing() -> Object {
720        let id = ObjectID::random();
721        let obj = MoveObject::new_gas_coin(OBJECT_START_VERSION, id, 10);
722        let owner = Owner::Shared(obj.version());
723        Object::new_move(obj, owner, TransactionDigest::GENESIS_MARKER)
724    }
725
726    pub fn with_id_owner_gas_for_testing(id: ObjectID, owner: IotaAddress, gas: u64) -> Self {
727        let data = Data::Struct(
728            MoveObject::new(
729                StructTag::new_gas_coin().into(),
730                OBJECT_START_VERSION,
731                GasCoin::new(id, gas).to_bcs_bytes(),
732            )
733            .unwrap(),
734        );
735        ObjectInner {
736            owner: Owner::Address(owner),
737            data,
738            previous_transaction: TransactionDigest::GENESIS_MARKER,
739            storage_rebate: 0,
740        }
741        .into()
742    }
743
744    pub fn treasury_cap_for_testing(struct_tag: StructTag, treasury_cap: TreasuryCap) -> Self {
745        let data = Data::Struct(
746            MoveObject::new(
747                StructTag::new_treasury_cap(struct_tag).into(),
748                OBJECT_START_VERSION,
749                bcs::to_bytes(&treasury_cap).expect("Failed to serialize"),
750            )
751            .unwrap(),
752        );
753        ObjectInner {
754            owner: Owner::Immutable,
755            data,
756            previous_transaction: TransactionDigest::GENESIS_MARKER,
757            storage_rebate: 0,
758        }
759        .into()
760    }
761
762    pub fn coin_metadata_for_testing(struct_tag: StructTag, metadata: CoinMetadata) -> Self {
763        let data = Data::Struct(
764            MoveObject::new(
765                StructTag::new_coin_metadata(struct_tag).into(),
766                OBJECT_START_VERSION,
767                bcs::to_bytes(&metadata).expect("Failed to serialize"),
768            )
769            .unwrap(),
770        );
771        ObjectInner {
772            owner: Owner::Immutable,
773            data,
774            previous_transaction: TransactionDigest::GENESIS_MARKER,
775            storage_rebate: 0,
776        }
777        .into()
778    }
779
780    pub fn with_object_owner_for_testing(id: ObjectID, owner: ObjectID) -> Self {
781        let data = Data::Struct(
782            MoveObject::new(
783                StructTag::new_gas_coin().into(),
784                OBJECT_START_VERSION,
785                GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
786            )
787            .unwrap(),
788        );
789        ObjectInner {
790            owner: Owner::Object(owner),
791            data,
792            previous_transaction: TransactionDigest::GENESIS_MARKER,
793            storage_rebate: 0,
794        }
795        .into()
796    }
797
798    pub fn with_id_owner_for_testing(id: ObjectID, owner: IotaAddress) -> Self {
799        // For testing, we provide sufficient gas by default.
800        Self::with_id_owner_gas_for_testing(id, owner, GAS_VALUE_FOR_TESTING)
801    }
802
803    pub fn with_id_owner_version_for_testing(
804        id: ObjectID,
805        version: SequenceNumber,
806        owner: Owner,
807    ) -> Self {
808        let data = Data::Struct(
809            MoveObject::new(
810                StructTag::new_gas_coin().into(),
811                version,
812                GasCoin::new(id, GAS_VALUE_FOR_TESTING).to_bcs_bytes(),
813            )
814            .unwrap(),
815        );
816        ObjectInner {
817            owner,
818            data,
819            previous_transaction: TransactionDigest::GENESIS_MARKER,
820            storage_rebate: 0,
821        }
822        .into()
823    }
824
825    pub fn with_owner_for_testing(owner: IotaAddress) -> Self {
826        Self::with_id_owner_for_testing(ObjectID::random(), owner)
827    }
828
829    /// Generate a new gas coin worth `value` with a random object ID and owner
830    /// For testing purposes only
831    pub fn new_gas_with_balance_and_owner_for_testing(value: u64, owner: IotaAddress) -> Self {
832        let obj = MoveObject::new_gas_coin(OBJECT_START_VERSION, ObjectID::random(), value);
833        Object::new_move(
834            obj,
835            Owner::Address(owner),
836            TransactionDigest::GENESIS_MARKER,
837        )
838    }
839
840    /// Generate a new gas coin object with default balance and random owner.
841    pub fn new_gas_for_testing() -> Self {
842        let gas_object_id = ObjectID::random();
843        let (owner, _) = deterministic_random_account_key();
844        Object::with_id_owner_for_testing(gas_object_id, owner)
845    }
846}
847
848/// Make a few test gas objects (all with the same random owner).
849pub fn generate_test_gas_objects() -> Vec<Object> {
850    thread_local! {
851        static GAS_OBJECTS: Vec<Object> = (0..50)
852            .map(|_| {
853                let gas_object_id = ObjectID::random();
854                let (owner, _) = deterministic_random_account_key();
855                Object::with_id_owner_for_testing(gas_object_id, owner)
856            })
857            .collect();
858    }
859
860    GAS_OBJECTS.with(|v| v.clone())
861}
862
863#[derive(Serialize, Deserialize, Debug)]
864#[serde(tag = "status", content = "details")]
865pub enum ObjectRead {
866    NotExists(ObjectID),
867    Exists(ObjectRef, Object, Option<MoveStructLayout>),
868    Deleted(ObjectRef),
869}
870
871impl ObjectRead {
872    /// Returns the object value if there is any, otherwise an Err if
873    /// the object does not exist or is deleted.
874    pub fn into_object(self) -> UserInputResult<Object> {
875        match self {
876            Self::Deleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: oref }),
877            Self::NotExists(id) => Err(UserInputError::ObjectNotFound {
878                object_id: id,
879                version: None,
880            }),
881            Self::Exists(_, o, _) => Ok(o),
882        }
883    }
884
885    pub fn object(&self) -> UserInputResult<&Object> {
886        match self {
887            Self::Deleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: *oref }),
888            Self::NotExists(id) => Err(UserInputError::ObjectNotFound {
889                object_id: *id,
890                version: None,
891            }),
892            Self::Exists(_, o, _) => Ok(o),
893        }
894    }
895
896    pub fn object_id(&self) -> ObjectID {
897        match self {
898            Self::Deleted(oref) => oref.object_id,
899            Self::NotExists(id) => *id,
900            Self::Exists(oref, _, _) => oref.object_id,
901        }
902    }
903}
904
905impl Display for ObjectRead {
906    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
907        match self {
908            Self::Deleted(oref) => {
909                write!(f, "ObjectRead::Deleted ({oref:?})")
910            }
911            Self::NotExists(id) => {
912                write!(f, "ObjectRead::NotExists ({id:?})")
913            }
914            Self::Exists(oref, _, _) => {
915                write!(f, "ObjectRead::Exists ({oref:?})")
916            }
917        }
918    }
919}
920
921#[derive(Serialize, Deserialize, Debug)]
922#[serde(tag = "status", content = "details")]
923pub enum PastObjectRead {
924    /// The object does not exist
925    ObjectNotExists(ObjectID),
926    /// The object is found to be deleted with this version
927    ObjectDeleted(ObjectRef),
928    /// The object exists and is found with this version
929    VersionFound(ObjectRef, Object, Option<MoveStructLayout>),
930    /// The object exists but not found with this version
931    VersionNotFound(ObjectID, SequenceNumber),
932    /// The asked object version is higher than the latest
933    VersionTooHigh {
934        object_id: ObjectID,
935        asked_version: SequenceNumber,
936        latest_version: SequenceNumber,
937    },
938}
939
940impl PastObjectRead {
941    /// Returns the object value if there is any, otherwise an Err
942    pub fn into_object(self) -> UserInputResult<Object> {
943        match self {
944            Self::ObjectDeleted(oref) => Err(UserInputError::ObjectDeleted { object_ref: oref }),
945            Self::ObjectNotExists(id) => Err(UserInputError::ObjectNotFound {
946                object_id: id,
947                version: None,
948            }),
949            Self::VersionFound(_, o, _) => Ok(o),
950            Self::VersionNotFound(object_id, version) => Err(UserInputError::ObjectNotFound {
951                object_id,
952                version: Some(version),
953            }),
954            Self::VersionTooHigh {
955                object_id,
956                asked_version,
957                latest_version,
958            } => Err(UserInputError::ObjectSequenceNumberTooHigh {
959                object_id,
960                asked_version,
961                latest_version,
962            }),
963        }
964    }
965}
966
967impl Display for PastObjectRead {
968    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
969        match self {
970            Self::ObjectDeleted(oref) => {
971                write!(f, "PastObjectRead::ObjectDeleted ({oref:?})")
972            }
973            Self::ObjectNotExists(id) => {
974                write!(f, "PastObjectRead::ObjectNotExists ({id:?})")
975            }
976            Self::VersionFound(oref, _, _) => {
977                write!(f, "PastObjectRead::VersionFound ({oref:?})")
978            }
979            Self::VersionNotFound(object_id, version) => {
980                write!(
981                    f,
982                    "PastObjectRead::VersionNotFound ({object_id:?}, asked sequence number {version:?})"
983                )
984            }
985            Self::VersionTooHigh {
986                object_id,
987                asked_version,
988                latest_version,
989            } => {
990                write!(
991                    f,
992                    "PastObjectRead::VersionTooHigh ({object_id:?}, asked sequence number {asked_version:?}, latest sequence number {latest_version:?})"
993                )
994            }
995        }
996    }
997}
998
999#[cfg(test)]
1000mod tests {
1001    use crate::{
1002        base_types::{IotaAddress, ObjectID, TransactionDigest},
1003        gas_coin::GasCoin,
1004        object::{MoveObjectExt, OBJECT_START_VERSION, Object, Owner},
1005    };
1006
1007    // Ensure that object digest computation and bcs serialized format are not
1008    // inadvertently changed.
1009    #[test]
1010    fn test_object_digest_and_serialized_format() {
1011        let g =
1012            GasCoin::new_for_testing_with_id(ObjectID::ZERO, 123).to_object(OBJECT_START_VERSION);
1013        let o = Object::new_move(
1014            g,
1015            Owner::Address(IotaAddress::ZERO),
1016            TransactionDigest::ZERO,
1017        );
1018        let bytes = bcs::to_bytes(&o).unwrap();
1019
1020        assert_eq!(
1021            bytes,
1022            [
1023                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,
1024                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,
1025                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,
1026                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,
1027                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1028            ]
1029        );
1030        let objref = o.compute_object_reference();
1031
1032        assert_eq!(objref.object_id, ObjectID::ZERO);
1033        assert_eq!(objref.version, 1);
1034        assert_eq!(
1035            objref.digest.to_string(),
1036            "Ba4YyVBcpc9jgX4PMLRoyt9dKLftYVSDvuKbtMr9f4NM"
1037        );
1038    }
1039
1040    #[test]
1041    fn test_get_coin_value_unchecked() {
1042        fn test_for_value(v: u64) {
1043            let g = GasCoin::new_for_testing(v).to_object(OBJECT_START_VERSION);
1044            assert_eq!(g.get_coin_value_unchecked(), v);
1045            assert_eq!(GasCoin::try_from(&g).unwrap().value(), v);
1046        }
1047
1048        test_for_value(0);
1049        test_for_value(1);
1050        test_for_value(8);
1051        test_for_value(9);
1052        test_for_value(u8::MAX as u64);
1053        test_for_value(u8::MAX as u64 + 1);
1054        test_for_value(u16::MAX as u64);
1055        test_for_value(u16::MAX as u64 + 1);
1056        test_for_value(u32::MAX as u64);
1057        test_for_value(u32::MAX as u64 + 1);
1058        test_for_value(u64::MAX);
1059    }
1060
1061    #[test]
1062    fn test_set_coin_value_unchecked() {
1063        fn test_for_value(v: u64) {
1064            let mut g = GasCoin::new_for_testing(u64::MAX).to_object(OBJECT_START_VERSION);
1065            g.set_coin_value_unchecked(v);
1066            assert_eq!(g.get_coin_value_unchecked(), v);
1067            assert_eq!(GasCoin::try_from(&g).unwrap().value(), v);
1068            assert_eq!(g.version(), OBJECT_START_VERSION);
1069            assert_eq!(g.contents().len(), 40);
1070        }
1071
1072        test_for_value(0);
1073        test_for_value(1);
1074        test_for_value(8);
1075        test_for_value(9);
1076        test_for_value(u8::MAX as u64);
1077        test_for_value(u8::MAX as u64 + 1);
1078        test_for_value(u16::MAX as u64);
1079        test_for_value(u16::MAX as u64 + 1);
1080        test_for_value(u32::MAX as u64);
1081        test_for_value(u32::MAX as u64 + 1);
1082        test_for_value(u64::MAX);
1083    }
1084}