Skip to main content

iota_types/storage/
mod.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5pub mod error;
6mod object_store_trait;
7mod read_store;
8mod shared_in_memory_store;
9mod write_store;
10
11use std::{
12    cell::RefCell,
13    collections::BTreeMap,
14    fmt::{Display, Formatter},
15    rc::Rc,
16    sync::Arc,
17};
18
19use iota_sdk_types::{
20    ObjectId, ObjectReference, SenderSignedTransaction, TransactionDigest, TransactionEffects,
21    Version, WriteKind, move_package::MovePackage,
22};
23use itertools::Itertools;
24use move_binary_format::CompiledModule;
25use move_core_types::language_storage::ModuleId;
26pub use object_store_trait::ObjectStore;
27pub use read_store::{
28    AccountOwnedObjectInfo, CoinInfo, DynamicFieldIteratorItem, DynamicFieldKey, EpochInfo,
29    EpochInfoV1Entry, EpochInfoV2, OwnedObjectCursor, OwnedObjectIteratorItem, PackageVersionInfo,
30    PackageVersionIteratorItem, PackageVersionKey, ReadStore, TransactionInfo,
31};
32use serde::{Deserialize, Serialize};
33use serde_with::serde_as;
34pub use shared_in_memory_store::{SharedInMemoryStore, SingleCheckpointSharedInMemoryStore};
35pub use write_store::WriteStore;
36
37use crate::{
38    auth_context::AuthContext,
39    base_types::VersionNumber,
40    committee::EpochId,
41    effects::{TransactionEffectsAPI, TransactionEffectsExt},
42    error::{ExecutionError, IotaError, IotaResult},
43    execution::{DynamicallyLoadedObjectMetadata, ExecutionResults},
44    iota_sdk_types_conversions::identifier_core_to_sdk,
45    object::Object,
46    storage::error::Error as StorageError,
47    transaction::{SenderSignedTransactionAPI, TransactionAPI},
48};
49
50/// A potential input to a transaction.
51#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
52pub enum InputKey {
53    VersionedObject { id: ObjectId, version: Version },
54    Package { id: ObjectId },
55}
56
57impl InputKey {
58    pub fn id(&self) -> ObjectId {
59        match self {
60            InputKey::VersionedObject { id, .. } => *id,
61            InputKey::Package { id } => *id,
62        }
63    }
64
65    pub fn version(&self) -> Option<Version> {
66        match self {
67            InputKey::VersionedObject { version, .. } => Some(*version),
68            InputKey::Package { .. } => None,
69        }
70    }
71
72    pub fn is_cancelled(&self) -> bool {
73        match self {
74            InputKey::VersionedObject { version, .. } => version.is_canceled(),
75            InputKey::Package { .. } => false,
76        }
77    }
78}
79
80impl From<&Object> for InputKey {
81    fn from(obj: &Object) -> Self {
82        if obj.is_package() {
83            InputKey::Package { id: obj.id() }
84        } else {
85            InputKey::VersionedObject {
86                id: obj.id(),
87                version: obj.version(),
88            }
89        }
90    }
91}
92
93#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
94pub enum DeleteKind {
95    /// An object is provided in the call input, and gets deleted.
96    Normal,
97    /// An object is not provided in the call input, but gets unwrapped
98    /// from another object, and then gets deleted.
99    UnwrapThenDelete,
100    /// An object is provided in the call input, and gets wrapped into another
101    /// object.
102    Wrap,
103}
104
105#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
106pub enum MarkerValue {
107    /// An object was received at the given version in the transaction and is no
108    /// longer able to be received at that version in subsequent
109    /// transactions.
110    Received,
111    /// An owned object was deleted (or wrapped) at the given version, and is no
112    /// longer able to be accessed or used in subsequent transactions.
113    OwnedDeleted,
114    /// A shared object was deleted by the transaction and is no longer able to
115    /// be accessed or used in subsequent transactions.
116    SharedDeleted(TransactionDigest),
117}
118
119/// DeleteKind together with the old sequence number prior to the deletion, if
120/// available. For normal deletion and wrap, we always will consult the object
121/// store to obtain the old sequence number. For UnwrapThenDelete however,
122/// we will not consult the object store,
123/// and hence won't have the old sequence number.
124#[derive(Debug)]
125pub enum DeleteKindWithOldVersion {
126    Normal(Version),
127    UnwrapThenDelete,
128    Wrap(Version),
129}
130
131impl DeleteKindWithOldVersion {
132    pub fn old_version(&self) -> Option<Version> {
133        match self {
134            DeleteKindWithOldVersion::Normal(version) | DeleteKindWithOldVersion::Wrap(version) => {
135                Some(*version)
136            }
137            DeleteKindWithOldVersion::UnwrapThenDelete => None,
138        }
139    }
140
141    pub fn to_delete_kind(&self) -> DeleteKind {
142        match self {
143            DeleteKindWithOldVersion::Normal(_) => DeleteKind::Normal,
144            DeleteKindWithOldVersion::UnwrapThenDelete => DeleteKind::UnwrapThenDelete,
145            DeleteKindWithOldVersion::Wrap(_) => DeleteKind::Wrap,
146        }
147    }
148}
149
150#[derive(Debug)]
151pub enum ObjectChange {
152    Write(Object, WriteKind),
153    // DeleteKind together with the old sequence number prior to the deletion, if available.
154    Delete(DeleteKindWithOldVersion),
155}
156
157pub trait StorageView: Storage + ChildObjectResolver {}
158impl<T: Storage + ChildObjectResolver> StorageView for T {}
159
160/// An abstraction of the (possibly distributed) store for objects. This
161/// API only allows for the retrieval of objects, not any state changes
162pub trait ChildObjectResolver {
163    /// `child` must have an `Object` ownership equal to `parent`.
164    fn read_child_object(
165        &self,
166        parent: &ObjectId,
167        child: &ObjectId,
168        child_version_upper_bound: Version,
169    ) -> IotaResult<Option<Object>>;
170
171    /// `receiving_object_id` must have an `Address` ownership equal to
172    /// `owner`. `get_object_received_at_version` must be the exact version
173    /// at which the object will be received, and it cannot have been
174    /// previously received at that version. NB: An object not existing at
175    /// that version, and not having valid access to the object will be treated
176    /// exactly the same and `Ok(None)` must be returned.
177    fn get_object_received_at_version(
178        &self,
179        owner: &ObjectId,
180        receiving_object_id: &ObjectId,
181        receive_object_at_version: Version,
182        epoch_id: EpochId,
183    ) -> IotaResult<Option<Object>>;
184}
185
186pub struct DenyListResult {
187    /// Ok if all regulated coin owners are allowed.
188    /// Err if any regulated coin owner is denied (returning the error for first
189    /// one denied).
190    pub result: Result<(), ExecutionError>,
191    /// The number of non-gas-coin owners in the transaction results
192    pub num_non_gas_coin_owners: u64,
193}
194
195/// An abstraction of the (possibly distributed) store for objects, and (soon)
196/// events and transactions
197pub trait Storage {
198    fn reset(&mut self);
199
200    fn read_object(&self, id: &ObjectId) -> Option<&Object>;
201
202    fn record_execution_results(&mut self, results: ExecutionResults);
203
204    fn save_loaded_runtime_objects(
205        &mut self,
206        loaded_runtime_objects: BTreeMap<ObjectId, DynamicallyLoadedObjectMetadata>,
207    );
208
209    fn save_wrapped_object_containers(
210        &mut self,
211        wrapped_object_containers: BTreeMap<ObjectId, ObjectId>,
212    );
213
214    /// Check coin denylist during execution,
215    /// and the number of non-gas-coin owners.
216    fn check_coin_deny_list(&self, written_objects: &BTreeMap<ObjectId, Object>) -> DenyListResult;
217
218    fn read_auth_context(&self) -> Option<Rc<RefCell<AuthContext>>>;
219}
220
221pub type PackageFetchResults<Package> = Result<Vec<Package>, Vec<ObjectId>>;
222
223#[derive(Clone, Debug)]
224pub struct PackageObject {
225    package_object: Object,
226}
227
228impl PackageObject {
229    pub fn new(package_object: Object) -> Self {
230        assert!(package_object.is_package());
231        Self { package_object }
232    }
233
234    pub fn object(&self) -> &Object {
235        &self.package_object
236    }
237
238    pub fn move_package(&self) -> &MovePackage {
239        self.package_object.data.as_opt_package().unwrap()
240    }
241}
242
243impl From<PackageObject> for Object {
244    fn from(package_object_arc: PackageObject) -> Self {
245        package_object_arc.package_object
246    }
247}
248
249pub trait BackingPackageStore {
250    fn get_package_object(&self, package_id: &ObjectId) -> IotaResult<Option<PackageObject>>;
251}
252
253impl<S: ?Sized + BackingPackageStore> BackingPackageStore for Box<S> {
254    fn get_package_object(&self, package_id: &ObjectId) -> IotaResult<Option<PackageObject>> {
255        BackingPackageStore::get_package_object(self.as_ref(), package_id)
256    }
257}
258
259impl<S: ?Sized + BackingPackageStore> BackingPackageStore for Arc<S> {
260    fn get_package_object(&self, package_id: &ObjectId) -> IotaResult<Option<PackageObject>> {
261        BackingPackageStore::get_package_object(self.as_ref(), package_id)
262    }
263}
264
265impl<S: ?Sized + BackingPackageStore> BackingPackageStore for &S {
266    fn get_package_object(&self, package_id: &ObjectId) -> IotaResult<Option<PackageObject>> {
267        BackingPackageStore::get_package_object(*self, package_id)
268    }
269}
270
271impl<S: ?Sized + BackingPackageStore> BackingPackageStore for &mut S {
272    fn get_package_object(&self, package_id: &ObjectId) -> IotaResult<Option<PackageObject>> {
273        BackingPackageStore::get_package_object(*self, package_id)
274    }
275}
276
277pub fn load_package_object_from_object_store(
278    store: &impl ObjectStore,
279    package_id: &ObjectId,
280) -> IotaResult<Option<PackageObject>> {
281    let package = store.try_get_object(package_id)?;
282    if let Some(obj) = &package {
283        fp_ensure!(
284            obj.is_package(),
285            IotaError::BadObjectType {
286                error: format!("Package expected, Move object found: {package_id}"),
287            }
288        );
289    }
290    Ok(package.map(PackageObject::new))
291}
292
293/// Returns Ok(<package object for each package id in `package_ids`>) if all
294/// package IDs in `package_id` were found. If any package in `package_ids` was
295/// not found it returns a list of any package ids that are unable to be
296/// found>).
297pub fn get_package_objects<'a>(
298    store: &impl BackingPackageStore,
299    package_ids: impl IntoIterator<Item = &'a ObjectId>,
300) -> IotaResult<PackageFetchResults<PackageObject>> {
301    let packages: Vec<Result<_, _>> = package_ids
302        .into_iter()
303        .map(|id| match store.get_package_object(id) {
304            Ok(None) => Ok(Err(*id)),
305            Ok(Some(o)) => Ok(Ok(o)),
306            Err(x) => Err(x),
307        })
308        .collect::<IotaResult<_>>()?;
309
310    let (fetched, failed_to_fetch): (Vec<_>, Vec<_>) = packages.into_iter().partition_result();
311    if !failed_to_fetch.is_empty() {
312        Ok(Err(failed_to_fetch))
313    } else {
314        Ok(Ok(fetched))
315    }
316}
317
318pub fn get_module(
319    store: impl BackingPackageStore,
320    module_id: &ModuleId,
321) -> Result<Option<Vec<u8>>, IotaError> {
322    Ok(store
323        .get_package_object(&ObjectId::new(module_id.address().into_bytes()))?
324        .and_then(|package| {
325            package
326                .move_package()
327                .serialized_module_map()
328                .get(&identifier_core_to_sdk(module_id.name()))
329                .cloned()
330        }))
331}
332
333pub fn get_module_by_id<S: BackingPackageStore>(
334    store: &S,
335    id: &ModuleId,
336) -> anyhow::Result<Option<CompiledModule>, IotaError> {
337    Ok(get_module(store, id)?
338        .map(|bytes| CompiledModule::deserialize_with_defaults(&bytes).unwrap()))
339}
340
341/// A `BackingPackageStore` that resolves packages from a backing store, but
342/// also includes any packages that were published in the current transaction
343/// execution. This can be used to resolve Move modules right after transaction
344/// execution, but newly published packages have not yet been committed to the
345/// backing store on a fullnode.
346pub struct PostExecutionPackageResolver {
347    backing_store: Arc<dyn BackingPackageStore>,
348    new_packages: BTreeMap<ObjectId, PackageObject>,
349}
350
351impl PostExecutionPackageResolver {
352    pub fn new(
353        backing_store: Arc<dyn BackingPackageStore>,
354        output_objects: &Option<Vec<Object>>,
355    ) -> Self {
356        let new_packages = output_objects
357            .iter()
358            .flatten()
359            .filter_map(|o| {
360                if o.is_package() {
361                    Some((o.id(), PackageObject::new(o.clone())))
362                } else {
363                    None
364                }
365            })
366            .collect();
367        Self {
368            backing_store,
369            new_packages,
370        }
371    }
372}
373
374impl BackingPackageStore for PostExecutionPackageResolver {
375    fn get_package_object(&self, package_id: &ObjectId) -> IotaResult<Option<PackageObject>> {
376        if let Some(package) = self.new_packages.get(package_id) {
377            Ok(Some(package.clone()))
378        } else {
379            self.backing_store.get_package_object(package_id)
380        }
381    }
382}
383
384impl<S: ChildObjectResolver> ChildObjectResolver for std::sync::Arc<S> {
385    fn read_child_object(
386        &self,
387        parent: &ObjectId,
388        child: &ObjectId,
389        child_version_upper_bound: Version,
390    ) -> IotaResult<Option<Object>> {
391        ChildObjectResolver::read_child_object(
392            self.as_ref(),
393            parent,
394            child,
395            child_version_upper_bound,
396        )
397    }
398    fn get_object_received_at_version(
399        &self,
400        owner: &ObjectId,
401        receiving_object_id: &ObjectId,
402        receive_object_at_version: Version,
403        epoch_id: EpochId,
404    ) -> IotaResult<Option<Object>> {
405        ChildObjectResolver::get_object_received_at_version(
406            self.as_ref(),
407            owner,
408            receiving_object_id,
409            receive_object_at_version,
410            epoch_id,
411        )
412    }
413}
414
415impl<S: ChildObjectResolver> ChildObjectResolver for &S {
416    fn read_child_object(
417        &self,
418        parent: &ObjectId,
419        child: &ObjectId,
420        child_version_upper_bound: Version,
421    ) -> IotaResult<Option<Object>> {
422        ChildObjectResolver::read_child_object(*self, parent, child, child_version_upper_bound)
423    }
424    fn get_object_received_at_version(
425        &self,
426        owner: &ObjectId,
427        receiving_object_id: &ObjectId,
428        receive_object_at_version: Version,
429        epoch_id: EpochId,
430    ) -> IotaResult<Option<Object>> {
431        ChildObjectResolver::get_object_received_at_version(
432            *self,
433            owner,
434            receiving_object_id,
435            receive_object_at_version,
436            epoch_id,
437        )
438    }
439}
440
441impl<S: ChildObjectResolver> ChildObjectResolver for &mut S {
442    fn read_child_object(
443        &self,
444        parent: &ObjectId,
445        child: &ObjectId,
446        child_version_upper_bound: Version,
447    ) -> IotaResult<Option<Object>> {
448        ChildObjectResolver::read_child_object(*self, parent, child, child_version_upper_bound)
449    }
450    fn get_object_received_at_version(
451        &self,
452        owner: &ObjectId,
453        receiving_object_id: &ObjectId,
454        receive_object_at_version: Version,
455        epoch_id: EpochId,
456    ) -> IotaResult<Option<Object>> {
457        ChildObjectResolver::get_object_received_at_version(
458            *self,
459            owner,
460            receiving_object_id,
461            receive_object_at_version,
462            epoch_id,
463        )
464    }
465}
466
467// The primary key type for object storage.
468#[serde_as]
469#[derive(Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize, Debug)]
470pub struct ObjectKey(pub ObjectId, pub VersionNumber);
471
472impl ObjectKey {
473    pub const ZERO: ObjectKey = ObjectKey(ObjectId::ZERO, VersionNumber::MIN_VALID_INCL);
474
475    pub fn max_for_id(id: &ObjectId) -> Self {
476        Self(*id, VersionNumber::MAX_VALID_EXCL)
477    }
478
479    pub fn min_for_id(id: &ObjectId) -> Self {
480        Self(*id, VersionNumber::MIN_VALID_INCL)
481    }
482}
483
484impl From<ObjectReference> for ObjectKey {
485    fn from(object_ref: ObjectReference) -> Self {
486        ObjectKey::from(&object_ref)
487    }
488}
489
490impl From<&ObjectReference> for ObjectKey {
491    fn from(object_ref: &ObjectReference) -> Self {
492        Self(object_ref.object_id, object_ref.version)
493    }
494}
495
496#[derive(Clone)]
497pub enum ObjectOrTombstone {
498    Object(Object),
499    Tombstone(ObjectReference),
500}
501
502impl ObjectOrTombstone {
503    pub fn as_objref(&self) -> ObjectReference {
504        match self {
505            ObjectOrTombstone::Object(obj) => obj.object_ref(),
506            ObjectOrTombstone::Tombstone(obref) => *obref,
507        }
508    }
509}
510
511impl From<Object> for ObjectOrTombstone {
512    fn from(object: Object) -> Self {
513        ObjectOrTombstone::Object(object)
514    }
515}
516
517/// Fetch the `ObjectKey`s (IDs and versions) for non-shared input objects.
518/// Includes owned, and immutable objects as well as the gas objects, but not
519/// move packages or shared objects.
520pub fn transaction_non_shared_input_object_keys(
521    tx: &SenderSignedTransaction,
522) -> IotaResult<Vec<ObjectKey>> {
523    use crate::transaction::InputObjectKind as I;
524    Ok(tx
525        .input_objects()?
526        .into_iter()
527        .filter_map(|object| match object {
528            I::MovePackage(_) | I::SharedMoveObject { .. } => None,
529            I::ImmOrOwnedMoveObject(obj) => Some(obj.into()),
530        })
531        .collect())
532}
533
534pub fn transaction_receiving_object_keys(tx: &SenderSignedTransaction) -> Vec<ObjectKey> {
535    tx.transaction()
536        .receiving_objects()
537        .into_iter()
538        .map(|oref| oref.into())
539        .collect()
540}
541
542impl Display for DeleteKind {
543    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
544        match self {
545            DeleteKind::Wrap => write!(f, "Wrap"),
546            DeleteKind::Normal => write!(f, "Normal"),
547            DeleteKind::UnwrapThenDelete => write!(f, "UnwrapThenDelete"),
548        }
549    }
550}
551
552pub trait BackingStore: BackingPackageStore + ChildObjectResolver + ObjectStore {
553    fn as_object_store(&self) -> &dyn ObjectStore;
554}
555
556impl<T> BackingStore for T
557where
558    T: BackingPackageStore,
559    T: ChildObjectResolver,
560    T: ObjectStore,
561{
562    fn as_object_store(&self) -> &dyn ObjectStore {
563        self
564    }
565}
566
567pub fn get_transaction_input_objects(
568    object_store: &dyn ObjectStore,
569    effects: &TransactionEffects,
570) -> Result<Vec<Object>, StorageError> {
571    let input_object_keys = effects
572        .modified_at_versions()
573        .into_iter()
574        .map(|modified| ObjectKey(modified.object_id, modified.version))
575        .collect::<Vec<_>>();
576
577    let input_objects = object_store
578        .multi_get_objects_by_key(&input_object_keys)
579        .into_iter()
580        .enumerate()
581        .map(|(idx, maybe_object)| {
582            maybe_object.ok_or_else(|| {
583                StorageError::missing(format!(
584                    "missing input object key {:?} from tx {}",
585                    input_object_keys[idx],
586                    effects.transaction_digest()
587                ))
588            })
589        })
590        .collect::<Result<Vec<_>, _>>()?;
591    Ok(input_objects)
592}
593
594pub fn get_transaction_output_objects(
595    object_store: &dyn ObjectStore,
596    effects: &TransactionEffects,
597) -> Result<Vec<Object>, StorageError> {
598    let output_object_keys = effects
599        .all_changed_objects()
600        .into_iter()
601        .map(|(changed, _kind)| ObjectKey::from(changed.reference))
602        .collect::<Vec<_>>();
603
604    let output_objects = object_store
605        .multi_get_objects_by_key(&output_object_keys)
606        .into_iter()
607        .enumerate()
608        .map(|(idx, maybe_object)| {
609            maybe_object.ok_or_else(|| {
610                StorageError::missing(format!(
611                    "missing output object key {:?} from tx {}",
612                    output_object_keys[idx],
613                    effects.transaction_digest()
614                ))
615            })
616        })
617        .collect::<Result<Vec<_>, _>>()?;
618    Ok(output_objects)
619}
620
621/// Extend a simulation's input objects with the runtime-loaded objects the
622/// effects record as modified, read from `object_store` at their pre-state
623/// versions.
624pub fn extend_input_objects_with_loaded_runtime_objects(
625    input_objects: &mut BTreeMap<ObjectId, Object>,
626    effects: &TransactionEffects,
627    loaded_runtime_objects: &BTreeMap<ObjectId, DynamicallyLoadedObjectMetadata>,
628    object_store: &dyn ObjectStore,
629) {
630    let modified_at: BTreeMap<_, _> = effects
631        .modified_at_versions()
632        .into_iter()
633        .map(|modified| (modified.object_id, modified.version))
634        .collect();
635    for (id, metadata) in loaded_runtime_objects {
636        if input_objects.contains_key(id) || modified_at.get(id) != Some(&metadata.version) {
637            continue;
638        }
639        if let Some(object) = object_store.get_object_by_key(id, metadata.version) {
640            input_objects.insert(*id, object);
641        }
642    }
643}