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