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