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