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