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