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