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