Skip to main content

sui_types/storage/
mod.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4pub mod error;
5mod object_store_trait;
6mod read_store;
7mod shared_in_memory_store;
8mod write_store;
9
10use crate::SUI_ACCUMULATOR_ROOT_OBJECT_ID;
11use crate::base_types::{
12    ConsensusObjectSequenceKey, ConsensusObjectVersion, FullObjectID, FullObjectRef, SuiAddress,
13    SystemObjectVersions, TransactionDigest, VersionNumber,
14};
15use crate::committee::EpochId;
16use crate::effects::InputConsensusObject;
17use crate::effects::{TransactionEffects, TransactionEffectsAPI};
18use crate::error::{ExecutionError, SuiError, SuiErrorKind};
19use crate::execution::{DynamicallyLoadedObjectMetadata, ExecutionResults};
20use crate::full_checkpoint_content::ObjectSet;
21use crate::message_envelope::Message;
22use crate::move_package::MovePackage;
23use crate::storage::error::Error as StorageError;
24use crate::transaction::TransactionData;
25use crate::transaction::{SenderSignedData, TransactionDataAPI};
26use crate::{
27    base_types::{ObjectID, ObjectRef, SequenceNumber},
28    error::SuiResult,
29    object::Object,
30};
31use itertools::Itertools;
32use move_binary_format::CompiledModule;
33use move_core_types::language_storage::{ModuleId, TypeTag};
34use move_core_types::resolver::SerializedPackage;
35pub use object_store_trait::ObjectStore;
36pub use read_store::BalanceInfo;
37pub use read_store::BalanceIterator;
38pub use read_store::CoinInfo;
39pub use read_store::DynamicFieldIndexInfo;
40pub use read_store::DynamicFieldIteratorItem;
41pub use read_store::DynamicFieldKey;
42pub use read_store::EpochInfo;
43pub use read_store::LedgerBitmapBucket;
44pub use read_store::LedgerBitmapBucketIter;
45pub use read_store::LedgerBitmapBucketIterator;
46pub use read_store::LedgerTxSeqDigest;
47pub use read_store::LedgerTxSeqDigestIterator;
48pub use read_store::OwnedObjectInfo;
49pub use read_store::ReadStore;
50pub use read_store::RpcIndexes;
51pub use read_store::RpcStateReader;
52pub use read_store::TransactionInfo;
53use serde::{Deserialize, Serialize};
54use serde_with::serde_as;
55pub use shared_in_memory_store::SharedInMemoryStore;
56pub use shared_in_memory_store::SingleCheckpointSharedInMemoryStore;
57use std::collections::{BTreeMap, BTreeSet};
58use std::fmt::{Display, Formatter};
59use std::sync::Arc;
60pub use write_store::WriteStore;
61
62/// A potential input to a transaction.
63#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
64pub enum InputKey {
65    VersionedObject {
66        id: FullObjectID,
67        version: SequenceNumber,
68    },
69    Package {
70        id: ObjectID,
71    },
72}
73
74impl InputKey {
75    pub fn id(&self) -> FullObjectID {
76        match self {
77            InputKey::VersionedObject { id, .. } => *id,
78            InputKey::Package { id } => FullObjectID::Fastpath(*id),
79        }
80    }
81
82    pub fn version(&self) -> Option<SequenceNumber> {
83        match self {
84            InputKey::VersionedObject { version, .. } => Some(*version),
85            InputKey::Package { .. } => None,
86        }
87    }
88
89    pub fn is_cancelled(&self) -> bool {
90        match self {
91            InputKey::VersionedObject { version, .. } => version.is_cancelled(),
92            InputKey::Package { .. } => false,
93        }
94    }
95}
96
97impl From<&Object> for InputKey {
98    fn from(obj: &Object) -> Self {
99        if obj.is_package() {
100            InputKey::Package { id: obj.id() }
101        } else {
102            InputKey::VersionedObject {
103                id: obj.full_id(),
104                version: obj.version(),
105            }
106        }
107    }
108}
109
110#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
111pub enum WriteKind {
112    /// The object was in storage already but has been modified
113    Mutate,
114    /// The object was created in this transaction
115    Create,
116    /// The object was previously wrapped in another object, but has been restored to storage
117    Unwrap,
118}
119
120#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
121pub enum DeleteKind {
122    /// An object is provided in the call input, and gets deleted.
123    Normal,
124    /// An object is not provided in the call input, but gets unwrapped
125    /// from another object, and then gets deleted.
126    UnwrapThenDelete,
127    /// An object is provided in the call input, and gets wrapped into another object.
128    Wrap,
129}
130
131#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
132pub enum MarkerValue {
133    /// An object was received at the given version in the transaction and is no longer able
134    /// to be received at that version in subequent transactions.
135    Received,
136    /// A fastpath object was deleted, wrapped, or transferred to consensus at the given
137    /// version, and is no longer able to be accessed or used in subsequent transactions via
138    /// fastpath unless/until it is returned to fastpath.
139    FastpathStreamEnded,
140    /// A consensus object was deleted or removed from consensus by the transaction and is no longer
141    /// able to be accessed or used in subsequent transactions with the same initial shared version.
142    ConsensusStreamEnded(TransactionDigest),
143}
144
145/// DeleteKind together with the old sequence number prior to the deletion, if available.
146/// For normal deletion and wrap, we always will consult the object store to obtain the old sequence number.
147/// For UnwrapThenDelete however, in the old protocol where simplified_unwrap_then_delete is false,
148/// we will consult the object store to obtain the old sequence number, which latter will be put in
149/// modified_at_versions; in the new protocol where simplified_unwrap_then_delete is true,
150/// we will not consult the object store, and hence won't have the old sequence number.
151#[derive(Debug)]
152pub enum DeleteKindWithOldVersion {
153    Normal(SequenceNumber),
154    // This variant will be deprecated when we turn on simplified_unwrap_then_delete.
155    UnwrapThenDeleteDEPRECATED(SequenceNumber),
156    UnwrapThenDelete,
157    Wrap(SequenceNumber),
158}
159
160impl DeleteKindWithOldVersion {
161    pub fn old_version(&self) -> Option<SequenceNumber> {
162        match self {
163            DeleteKindWithOldVersion::Normal(version)
164            | DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(version)
165            | DeleteKindWithOldVersion::Wrap(version) => Some(*version),
166            DeleteKindWithOldVersion::UnwrapThenDelete => None,
167        }
168    }
169
170    pub fn to_delete_kind(&self) -> DeleteKind {
171        match self {
172            DeleteKindWithOldVersion::Normal(_) => DeleteKind::Normal,
173            DeleteKindWithOldVersion::UnwrapThenDeleteDEPRECATED(_)
174            | DeleteKindWithOldVersion::UnwrapThenDelete => DeleteKind::UnwrapThenDelete,
175            DeleteKindWithOldVersion::Wrap(_) => DeleteKind::Wrap,
176        }
177    }
178}
179
180#[derive(Debug)]
181pub enum ObjectChange {
182    Write(Object, WriteKind),
183    // DeleteKind together with the old sequence number prior to the deletion, if available.
184    Delete(DeleteKindWithOldVersion),
185}
186
187pub trait StorageView: Storage + ParentSync + RuntimeObjectResolver {}
188impl<T: Storage + ParentSync + RuntimeObjectResolver> StorageView for T {}
189
190/// An abstraction of the (possibly distributed) store for objects. This
191/// API only allows for the retrieval of objects, not any state changes
192pub trait RuntimeObjectResolver: BackingPackageStore {
193    /// `child` must have an `ObjectOwner` ownership equal to `owner`.
194    fn read_child_object(
195        &self,
196        parent: &ObjectID,
197        child: &ObjectID,
198        child_version_upper_bound: SequenceNumber,
199    ) -> SuiResult<Option<Object>>;
200
201    /// `receiving_object_id` must have an `AddressOwner` ownership equal to `owner`.
202    /// `get_object_received_at_version` must be the exact version at which the object will be received,
203    /// and it cannot have been previously received at that version. NB: An object not existing at
204    /// that version, and not having valid access to the object will be treated exactly the same
205    /// and `Ok(None)` must be returned.
206    fn get_object_received_at_version(
207        &self,
208        owner: &ObjectID,
209        receiving_object_id: &ObjectID,
210        receive_object_at_version: SequenceNumber,
211        epoch_id: EpochId,
212    ) -> SuiResult<Option<Object>>;
213
214    /// Get's the package at the given version. Returns `Some(package)` only if the `package_id` is
215    /// a `MovePackage` with the given `package_version`. Returns `None` in all other cases.
216    ///
217    /// Since the has the _possibility_ of doing unsequenced reads of object IDs it is important
218    /// here that:
219    /// * If the package object does not exist; or
220    /// * If the package object exists but is not a Move package; or
221    /// * If the package object exists and is a Move package, but the version is not the supplied version.
222    ///
223    /// All return the same error.
224    ///
225    /// To be extra careful, we simply return `None` in all cases unless the object is a package
226    /// with the exact version supplied, and let the caller decide how to handle it.
227    fn get_package_at_version(
228        &self,
229        package_id: &ObjectID,
230        package_version: SequenceNumber,
231    ) -> Option<MovePackage> {
232        let move_pkg = self
233            .get_package_object(package_id)
234            .ok()
235            .flatten()?
236            .into_move_package();
237        if move_pkg.version() == package_version {
238            Some(move_pkg)
239        } else {
240            None
241        }
242    }
243}
244
245pub struct DenyListResult {
246    /// Ok if all regulated coin owners are allowed.
247    /// Err if any regulated coin owner is denied (returning the error for first one denied).
248    pub result: Result<(), ExecutionError>,
249    /// The number of non-gas-coin owners in the transaction results
250    pub num_non_gas_coin_owners: u64,
251}
252
253/// An abstraction of the (possibly distributed) store for objects, and (soon) events and transactions
254pub trait Storage {
255    fn reset(&mut self);
256
257    fn read_object(&self, id: &ObjectID) -> Option<&Object>;
258
259    fn record_execution_results(&mut self, results: ExecutionResults)
260    -> Result<(), ExecutionError>;
261
262    fn save_loaded_runtime_objects(
263        &mut self,
264        loaded_runtime_objects: BTreeMap<ObjectID, DynamicallyLoadedObjectMetadata>,
265    );
266
267    fn save_wrapped_object_containers(
268        &mut self,
269        wrapped_object_containers: BTreeMap<ObjectID, ObjectID>,
270    );
271
272    /// Given the set of all coin types and owners that are receiving the coins during execution,
273    /// Check coin denylist v2, and return the number of non-gas-coin owners.
274    fn check_coin_deny_list(
275        &self,
276        receiving_funds_type_and_owners: BTreeMap<TypeTag, BTreeSet<SuiAddress>>,
277    ) -> DenyListResult;
278
279    fn record_generated_object_ids(&mut self, generated_ids: BTreeSet<ObjectID>);
280}
281
282pub type PackageFetchResults<Package> = Result<Vec<Package>, Vec<ObjectID>>;
283
284#[derive(Clone, Debug)]
285pub struct PackageObject {
286    package_object: Object,
287}
288
289impl PackageObject {
290    pub fn new(package_object: Object) -> Self {
291        assert!(package_object.is_package());
292        Self { package_object }
293    }
294
295    pub fn object(&self) -> &Object {
296        &self.package_object
297    }
298
299    pub fn move_package(&self) -> &MovePackage {
300        self.package_object.data.try_as_package().unwrap()
301    }
302
303    pub fn into_move_package(self) -> MovePackage {
304        self.package_object
305            .into_inner()
306            .data
307            .try_into_package()
308            .unwrap()
309    }
310}
311
312impl From<PackageObject> for Object {
313    fn from(package_object_arc: PackageObject) -> Self {
314        package_object_arc.package_object
315    }
316}
317
318pub trait BackingPackageStore {
319    fn get_package_object(&self, package_id: &ObjectID) -> SuiResult<Option<PackageObject>>;
320}
321
322impl<S: ?Sized + BackingPackageStore> BackingPackageStore for Box<S> {
323    fn get_package_object(&self, package_id: &ObjectID) -> SuiResult<Option<PackageObject>> {
324        BackingPackageStore::get_package_object(self.as_ref(), package_id)
325    }
326}
327
328impl<S: ?Sized + BackingPackageStore> BackingPackageStore for Arc<S> {
329    fn get_package_object(&self, package_id: &ObjectID) -> SuiResult<Option<PackageObject>> {
330        BackingPackageStore::get_package_object(self.as_ref(), package_id)
331    }
332}
333
334impl<S: ?Sized + BackingPackageStore> BackingPackageStore for &S {
335    fn get_package_object(&self, package_id: &ObjectID) -> SuiResult<Option<PackageObject>> {
336        BackingPackageStore::get_package_object(*self, package_id)
337    }
338}
339
340impl<S: ?Sized + BackingPackageStore> BackingPackageStore for &mut S {
341    fn get_package_object(&self, package_id: &ObjectID) -> SuiResult<Option<PackageObject>> {
342        BackingPackageStore::get_package_object(*self, package_id)
343    }
344}
345
346/// A BackingPackageStore that overlays objects on top of a backing store.
347/// This allows resolving packages from a set of objects (e.g., output objects from a transaction)
348/// before falling back to the backing store.
349pub struct OverlayBackingPackageStore<'a, S> {
350    overlay: &'a ObjectSet,
351    backing: S,
352}
353
354impl<'a, S> OverlayBackingPackageStore<'a, S> {
355    pub fn new(overlay: &'a ObjectSet, backing: S) -> Self {
356        Self { overlay, backing }
357    }
358}
359
360impl<S: BackingPackageStore> BackingPackageStore for OverlayBackingPackageStore<'_, S> {
361    fn get_package_object(&self, package_id: &ObjectID) -> SuiResult<Option<PackageObject>> {
362        // First check the overlay for the object
363        for obj in self.overlay.iter() {
364            if &obj.id() == package_id {
365                // Found in overlay - check if it's a package
366                fp_ensure!(
367                    obj.is_package(),
368                    SuiErrorKind::BadObjectType {
369                        error: format!("Package expected, Move object found: {package_id}"),
370                    }
371                    .into()
372                );
373                return Ok(Some(PackageObject::new(obj.clone())));
374            }
375        }
376        // Not in overlay, fall back to the backing store
377        self.backing.get_package_object(package_id)
378    }
379}
380
381pub fn load_package_object_from_object_store(
382    store: &impl ObjectStore,
383    package_id: &ObjectID,
384) -> SuiResult<Option<PackageObject>> {
385    let package = store.get_object(package_id);
386    if let Some(obj) = &package {
387        fp_ensure!(
388            obj.is_package(),
389            SuiErrorKind::BadObjectType {
390                error: format!("Package expected, Move object found: {package_id}"),
391            }
392            .into()
393        );
394    }
395    Ok(package.map(PackageObject::new))
396}
397
398/// Returns Ok(<package object for each package id in `package_ids`>) if all package IDs in
399/// `package_id` were found. If any package in `package_ids` was not found it returns a list
400/// of any package ids that are unable to be found>).
401pub fn get_package_objects<'a>(
402    store: &impl BackingPackageStore,
403    package_ids: impl IntoIterator<Item = &'a ObjectID>,
404) -> SuiResult<PackageFetchResults<PackageObject>> {
405    let packages: Vec<Result<_, _>> = package_ids
406        .into_iter()
407        .map(|id| match store.get_package_object(id) {
408            Ok(None) => Ok(Err(*id)),
409            Ok(Some(o)) => Ok(Ok(o)),
410            Err(x) => Err(x),
411        })
412        .collect::<SuiResult<_>>()?;
413
414    let (fetched, failed_to_fetch): (Vec<_>, Vec<_>) = packages.into_iter().partition_result();
415    if !failed_to_fetch.is_empty() {
416        Ok(Err(failed_to_fetch))
417    } else {
418        Ok(Ok(fetched))
419    }
420}
421
422pub fn get_module(
423    store: impl BackingPackageStore,
424    module_id: &ModuleId,
425) -> Result<Option<Vec<u8>>, SuiError> {
426    Ok(store
427        .get_package_object(&ObjectID::from(*module_id.address()))?
428        .and_then(|package| {
429            package
430                .move_package()
431                .serialized_module_map()
432                .get(module_id.name().as_str())
433                .cloned()
434        }))
435}
436
437pub fn get_package(
438    store: impl BackingPackageStore,
439    id: &ObjectID,
440) -> SuiResult<Option<SerializedPackage>> {
441    store
442        .get_package_object(id)?
443        .map(|package| package.move_package().into_serialized_move_package())
444        .transpose()
445}
446
447pub fn get_module_by_id<S: BackingPackageStore>(
448    store: &S,
449    id: &ModuleId,
450) -> anyhow::Result<Option<CompiledModule>, SuiError> {
451    Ok(get_module(store, id)?
452        .map(|bytes| CompiledModule::deserialize_with_defaults(&bytes).unwrap()))
453}
454
455/// A `BackingPackageStore` that resolves packages from a backing store, but also includes any
456/// packages that were published in the current transaction execution. This can be used to resolve
457/// Move modules right after transaction execution, but newly published packages have not yet been
458/// committed to the backing store on a fullnode.
459pub struct PostExecutionPackageResolver {
460    backing_store: Arc<dyn BackingPackageStore>,
461    new_packages: BTreeMap<ObjectID, PackageObject>,
462}
463
464impl PostExecutionPackageResolver {
465    pub fn new(
466        backing_store: Arc<dyn BackingPackageStore>,
467        output_objects: &Option<Vec<Object>>,
468    ) -> Self {
469        let new_packages = output_objects
470            .iter()
471            .flatten()
472            .filter_map(|o| {
473                if o.is_package() {
474                    Some((o.id(), PackageObject::new(o.clone())))
475                } else {
476                    None
477                }
478            })
479            .collect();
480        Self {
481            backing_store,
482            new_packages,
483        }
484    }
485}
486
487impl BackingPackageStore for PostExecutionPackageResolver {
488    fn get_package_object(&self, package_id: &ObjectID) -> SuiResult<Option<PackageObject>> {
489        if let Some(package) = self.new_packages.get(package_id) {
490            Ok(Some(package.clone()))
491        } else {
492            self.backing_store.get_package_object(package_id)
493        }
494    }
495}
496
497pub trait ParentSync {
498    /// This function is only called by older protocol versions.
499    /// It creates an explicit dependency to tombstones, which is not desired.
500    fn get_latest_parent_entry_ref_deprecated(&self, object_id: ObjectID) -> Option<ObjectRef>;
501}
502
503impl<S: ParentSync> ParentSync for std::sync::Arc<S> {
504    fn get_latest_parent_entry_ref_deprecated(&self, object_id: ObjectID) -> Option<ObjectRef> {
505        ParentSync::get_latest_parent_entry_ref_deprecated(self.as_ref(), object_id)
506    }
507}
508
509impl<S: ParentSync> ParentSync for &S {
510    fn get_latest_parent_entry_ref_deprecated(&self, object_id: ObjectID) -> Option<ObjectRef> {
511        ParentSync::get_latest_parent_entry_ref_deprecated(*self, object_id)
512    }
513}
514
515impl<S: ParentSync> ParentSync for &mut S {
516    fn get_latest_parent_entry_ref_deprecated(&self, object_id: ObjectID) -> Option<ObjectRef> {
517        ParentSync::get_latest_parent_entry_ref_deprecated(*self, object_id)
518    }
519}
520
521impl<S: RuntimeObjectResolver> RuntimeObjectResolver for std::sync::Arc<S> {
522    fn read_child_object(
523        &self,
524        parent: &ObjectID,
525        child: &ObjectID,
526        child_version_upper_bound: SequenceNumber,
527    ) -> SuiResult<Option<Object>> {
528        RuntimeObjectResolver::read_child_object(
529            self.as_ref(),
530            parent,
531            child,
532            child_version_upper_bound,
533        )
534    }
535    fn get_object_received_at_version(
536        &self,
537        owner: &ObjectID,
538        receiving_object_id: &ObjectID,
539        receive_object_at_version: SequenceNumber,
540        epoch_id: EpochId,
541    ) -> SuiResult<Option<Object>> {
542        RuntimeObjectResolver::get_object_received_at_version(
543            self.as_ref(),
544            owner,
545            receiving_object_id,
546            receive_object_at_version,
547            epoch_id,
548        )
549    }
550}
551
552impl<S: RuntimeObjectResolver> RuntimeObjectResolver for &S {
553    fn read_child_object(
554        &self,
555        parent: &ObjectID,
556        child: &ObjectID,
557        child_version_upper_bound: SequenceNumber,
558    ) -> SuiResult<Option<Object>> {
559        RuntimeObjectResolver::read_child_object(*self, parent, child, child_version_upper_bound)
560    }
561    fn get_object_received_at_version(
562        &self,
563        owner: &ObjectID,
564        receiving_object_id: &ObjectID,
565        receive_object_at_version: SequenceNumber,
566        epoch_id: EpochId,
567    ) -> SuiResult<Option<Object>> {
568        RuntimeObjectResolver::get_object_received_at_version(
569            *self,
570            owner,
571            receiving_object_id,
572            receive_object_at_version,
573            epoch_id,
574        )
575    }
576}
577
578impl<S: RuntimeObjectResolver> RuntimeObjectResolver for &mut S {
579    fn read_child_object(
580        &self,
581        parent: &ObjectID,
582        child: &ObjectID,
583        child_version_upper_bound: SequenceNumber,
584    ) -> SuiResult<Option<Object>> {
585        RuntimeObjectResolver::read_child_object(*self, parent, child, child_version_upper_bound)
586    }
587    fn get_object_received_at_version(
588        &self,
589        owner: &ObjectID,
590        receiving_object_id: &ObjectID,
591        receive_object_at_version: SequenceNumber,
592        epoch_id: EpochId,
593    ) -> SuiResult<Option<Object>> {
594        RuntimeObjectResolver::get_object_received_at_version(
595            *self,
596            owner,
597            receiving_object_id,
598            receive_object_at_version,
599            epoch_id,
600        )
601    }
602}
603
604#[serde_as]
605#[derive(Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize, Debug)]
606pub struct ObjectKey(pub ObjectID, pub VersionNumber);
607
608impl ObjectKey {
609    pub const ZERO: ObjectKey = ObjectKey(ObjectID::ZERO, VersionNumber::MIN);
610
611    pub fn max_for_id(id: &ObjectID) -> Self {
612        Self(*id, VersionNumber::MAX)
613    }
614
615    pub fn min_for_id(id: &ObjectID) -> Self {
616        Self(*id, VersionNumber::MIN)
617    }
618}
619
620impl From<ObjectRef> for ObjectKey {
621    fn from(object_ref: ObjectRef) -> Self {
622        ObjectKey::from(&object_ref)
623    }
624}
625
626impl From<&ObjectRef> for ObjectKey {
627    fn from(object_ref: &ObjectRef) -> Self {
628        Self(object_ref.0, object_ref.1)
629    }
630}
631
632#[serde_as]
633#[derive(Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize, Debug)]
634pub struct ConsensusObjectKey(pub ConsensusObjectSequenceKey, pub VersionNumber);
635
636/// FullObjectKey represents a unique object a specific version. For fastpath objects, this
637/// is the same as ObjectKey. For consensus objects, this includes the start version, which
638/// may change if an object is transferred out of and back into consensus.
639#[serde_as]
640#[derive(Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize, Debug)]
641pub enum FullObjectKey {
642    Fastpath(ObjectKey),
643    Consensus(ConsensusObjectKey),
644}
645
646impl FullObjectKey {
647    pub fn max_for_id(id: &FullObjectID) -> Self {
648        match id {
649            FullObjectID::Fastpath(object_id) => Self::Fastpath(ObjectKey::max_for_id(object_id)),
650            FullObjectID::Consensus(consensus_object_sequence_key) => Self::Consensus(
651                ConsensusObjectKey(*consensus_object_sequence_key, VersionNumber::MAX),
652            ),
653        }
654    }
655
656    pub fn min_for_id(id: &FullObjectID) -> Self {
657        match id {
658            FullObjectID::Fastpath(object_id) => Self::Fastpath(ObjectKey::min_for_id(object_id)),
659            FullObjectID::Consensus(consensus_object_sequence_key) => Self::Consensus(
660                ConsensusObjectKey(*consensus_object_sequence_key, VersionNumber::MIN),
661            ),
662        }
663    }
664
665    pub fn new(object_id: FullObjectID, version: VersionNumber) -> Self {
666        match object_id {
667            FullObjectID::Fastpath(object_id) => Self::Fastpath(ObjectKey(object_id, version)),
668            FullObjectID::Consensus(consensus_object_sequence_key) => {
669                Self::Consensus(ConsensusObjectKey(consensus_object_sequence_key, version))
670            }
671        }
672    }
673
674    pub fn id(&self) -> FullObjectID {
675        match self {
676            FullObjectKey::Fastpath(object_key) => FullObjectID::Fastpath(object_key.0),
677            FullObjectKey::Consensus(consensus_object_key) => {
678                FullObjectID::Consensus(consensus_object_key.0)
679            }
680        }
681    }
682
683    pub fn version(&self) -> VersionNumber {
684        match self {
685            FullObjectKey::Fastpath(object_key) => object_key.1,
686            FullObjectKey::Consensus(consensus_object_key) => consensus_object_key.1,
687        }
688    }
689}
690
691impl From<FullObjectRef> for FullObjectKey {
692    fn from(object_ref: FullObjectRef) -> Self {
693        FullObjectKey::from(&object_ref)
694    }
695}
696
697impl From<&FullObjectRef> for FullObjectKey {
698    fn from(object_ref: &FullObjectRef) -> Self {
699        FullObjectKey::new(object_ref.0, object_ref.1)
700    }
701}
702
703#[derive(Clone)]
704pub enum ObjectOrTombstone {
705    Object(Object),
706    Tombstone(ObjectRef),
707}
708
709impl ObjectOrTombstone {
710    pub fn as_objref(&self) -> ObjectRef {
711        match self {
712            ObjectOrTombstone::Object(obj) => obj.compute_object_reference(),
713            ObjectOrTombstone::Tombstone(obref) => *obref,
714        }
715    }
716}
717
718impl From<Object> for ObjectOrTombstone {
719    fn from(object: Object) -> Self {
720        ObjectOrTombstone::Object(object)
721    }
722}
723
724/// Fetch the `ObjectKey`s (IDs and versions) for non-shared input objects.  Includes owned,
725/// and immutable objects as well as the gas objects, but not move packages or shared objects.
726pub fn transaction_non_shared_input_object_keys(
727    tx: &SenderSignedData,
728) -> SuiResult<Vec<ObjectKey>> {
729    use crate::transaction::InputObjectKind as I;
730    Ok(tx
731        .intent_message()
732        .value
733        .input_objects()?
734        .into_iter()
735        .filter_map(|object| match object {
736            I::MovePackage(_) | I::SharedMoveObject { .. } => None,
737            I::ImmOrOwnedMoveObject(obj) => Some(obj.into()),
738        })
739        .collect())
740}
741
742pub fn transaction_receiving_object_keys(tx: &SenderSignedData) -> Vec<ObjectKey> {
743    tx.intent_message()
744        .value
745        .receiving_objects()
746        .into_iter()
747        .map(|oref| oref.into())
748        .collect()
749}
750
751impl Display for DeleteKind {
752    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
753        match self {
754            DeleteKind::Wrap => write!(f, "Wrap"),
755            DeleteKind::Normal => write!(f, "Normal"),
756            DeleteKind::UnwrapThenDelete => write!(f, "UnwrapThenDelete"),
757        }
758    }
759}
760
761pub trait BackingStore: RuntimeObjectResolver + ObjectStore + ParentSync {}
762
763impl<T> BackingStore for T
764where
765    T: RuntimeObjectResolver,
766    T: ObjectStore,
767    T: ParentSync,
768{
769}
770
771pub fn get_transaction_input_objects(
772    object_store: &dyn ObjectStore,
773    effects: &TransactionEffects,
774) -> Result<Vec<Object>, StorageError> {
775    let input_object_keys = effects
776        .modified_at_versions()
777        .into_iter()
778        .map(|(object_id, version)| ObjectKey(object_id, version))
779        .collect::<Vec<_>>();
780
781    let input_objects = object_store
782        .multi_get_objects_by_key(&input_object_keys)
783        .into_iter()
784        .enumerate()
785        .map(|(idx, maybe_object)| {
786            maybe_object.ok_or_else(|| {
787                StorageError::custom(format!(
788                    "missing input object key {:?} from tx {} effects {}",
789                    input_object_keys[idx],
790                    effects.transaction_digest(),
791                    effects.digest()
792                ))
793            })
794        })
795        .collect::<Result<Vec<_>, _>>()?;
796    Ok(input_objects)
797}
798
799pub fn get_transaction_output_objects(
800    object_store: &dyn ObjectStore,
801    effects: &TransactionEffects,
802) -> Result<Vec<Object>, StorageError> {
803    let output_object_keys = effects
804        .all_changed_objects()
805        .into_iter()
806        .map(|(object_ref, _owner, _kind)| ObjectKey::from(object_ref))
807        .collect::<Vec<_>>();
808
809    let output_objects = object_store
810        .multi_get_objects_by_key(&output_object_keys)
811        .into_iter()
812        .enumerate()
813        .map(|(idx, maybe_object)| {
814            maybe_object.ok_or_else(|| {
815                StorageError::custom(format!(
816                    "missing output object key {:?} from tx {} effects {}",
817                    output_object_keys[idx],
818                    effects.transaction_digest(),
819                    effects.digest()
820                ))
821            })
822        })
823        .collect::<Result<Vec<_>, _>>()?;
824    Ok(output_objects)
825}
826
827impl SystemObjectVersions {
828    /// Obtains pinned system object versions from effects, queries the store for the initial shared versions.
829    pub fn from_effects(effects: &TransactionEffects, store: &dyn ObjectStore) -> Self {
830        let accumulator_version = effects
831            .accessed_consensus_objects()
832            .into_iter()
833            .find_map(|ico| match ico {
834                InputConsensusObject::Mutate((id, version, _))
835                | InputConsensusObject::ReadOnly((id, version, _))
836                    if id == SUI_ACCUMULATOR_ROOT_OBJECT_ID =>
837                {
838                    Some(version)
839                }
840                _ => None,
841            })
842            .map(|version| {
843                let initial_shared_version = store
844                    .get_object(&SUI_ACCUMULATOR_ROOT_OBJECT_ID)
845                    .and_then(|object| object.owner().start_version())
846                    // unwrap safe because if effects contain the accumulator root object, it must
847                    // exist in the store and is a shared object.
848                    .unwrap();
849                ConsensusObjectVersion {
850                    initial_shared_version,
851                    version,
852                }
853            });
854        Self::new(accumulator_version)
855    }
856
857    /// Before execution, get the latest versions of the implicitly read system objects from the store,
858    /// and use these versions as the exact version to read during execution.
859    /// This is used only in environments where there is no consensus to assign versions, e.g. simulacrum and dry-run.
860    pub fn from_latest_in_store(store: &dyn ObjectStore) -> Self {
861        let accumulator_version = store
862            .get_object(&SUI_ACCUMULATOR_ROOT_OBJECT_ID)
863            .map(|object| {
864                let initial_shared_version = object
865                    .owner()
866                    .start_version()
867                    .expect("accumulator root must be a consensus object");
868                ConsensusObjectVersion {
869                    initial_shared_version,
870                    version: object.version(),
871                }
872            });
873        Self::new(accumulator_version)
874    }
875}
876
877// Returns a set of the ObjectKey's of objects read or written by this transaction
878pub fn get_transaction_object_set(
879    transaction: &TransactionData,
880    effects: &TransactionEffects,
881    unchanged_loaded_runtime_objects: &[ObjectKey],
882) -> BTreeSet<ObjectKey> {
883    // enumerate the full set of input objects in order to properly capture immutable objects that
884    // may not appear in the effects.
885    //
886    // This excludes packages
887    let input_objects = transaction
888        .input_objects()
889        .expect("txn was executed and must have valid input objects")
890        .into_iter()
891        .filter_map(|input| {
892            input
893                .version()
894                .map(|version| ObjectKey(input.object_id(), version))
895        });
896
897    // The full set of output/written objects as well as any of their initial versions
898    let modified_set = effects
899        .object_changes()
900        .into_iter()
901        .flat_map(|change| {
902            [
903                change
904                    .input_version
905                    .map(|version| ObjectKey(change.id, version)),
906                change
907                    .output_version
908                    .map(|version| ObjectKey(change.id, version)),
909            ]
910        })
911        .flatten();
912
913    // The set of unchanged consensus objects
914    let unchanged_consensus =
915        effects
916            .unchanged_consensus_objects()
917            .into_iter()
918            .flat_map(|unchanged| {
919                if let crate::effects::UnchangedConsensusKind::ReadOnlyRoot((version, _)) =
920                    unchanged.1
921                {
922                    Some(ObjectKey(unchanged.0, version))
923                } else {
924                    None
925                }
926            });
927
928    input_objects
929        .chain(modified_set)
930        .chain(unchanged_consensus)
931        .chain(unchanged_loaded_runtime_objects.iter().copied())
932        .collect()
933}
934
935// A BackingStore to pass to execution in order to track all objects loaded during execution.
936//
937// Today this is used to very accurately track the objects that were loaded but unchanged during
938// execution.
939pub struct TrackingBackingStore<'a> {
940    inner: &'a dyn crate::storage::BackingStore,
941    read_objects: std::cell::RefCell<ObjectSet>,
942}
943
944impl<'a> TrackingBackingStore<'a> {
945    pub fn new(inner: &'a dyn crate::storage::BackingStore) -> Self {
946        Self {
947            inner,
948            read_objects: Default::default(),
949        }
950    }
951
952    pub fn into_read_objects(self) -> ObjectSet {
953        self.read_objects.into_inner()
954    }
955
956    fn track_object(&self, object: &Object) {
957        self.read_objects.borrow_mut().insert(object.clone());
958    }
959}
960
961impl BackingPackageStore for TrackingBackingStore<'_> {
962    fn get_package_object(
963        &self,
964        package_id: &ObjectID,
965    ) -> crate::error::SuiResult<Option<PackageObject>> {
966        self.inner.get_package_object(package_id).inspect(|o| {
967            o.as_ref()
968                .inspect(|package| self.track_object(package.object()));
969        })
970    }
971}
972
973impl RuntimeObjectResolver for TrackingBackingStore<'_> {
974    fn read_child_object(
975        &self,
976        parent: &ObjectID,
977        child: &ObjectID,
978        child_version_upper_bound: SequenceNumber,
979    ) -> crate::error::SuiResult<Option<Object>> {
980        self.inner
981            .read_child_object(parent, child, child_version_upper_bound)
982            .inspect(|o| {
983                o.as_ref().inspect(|object| self.track_object(object));
984            })
985    }
986
987    fn get_object_received_at_version(
988        &self,
989        owner: &ObjectID,
990        receiving_object_id: &ObjectID,
991        receive_object_at_version: SequenceNumber,
992        epoch_id: crate::committee::EpochId,
993    ) -> crate::error::SuiResult<Option<Object>> {
994        self.inner
995            .get_object_received_at_version(
996                owner,
997                receiving_object_id,
998                receive_object_at_version,
999                epoch_id,
1000            )
1001            .inspect(|o| {
1002                o.as_ref().inspect(|object| self.track_object(object));
1003            })
1004    }
1005}
1006
1007impl crate::storage::ObjectStore for TrackingBackingStore<'_> {
1008    fn get_object(&self, object_id: &ObjectID) -> Option<Object> {
1009        self.inner
1010            .get_object(object_id)
1011            .inspect(|o| self.track_object(o))
1012    }
1013
1014    fn load_implicitly_read_system_object(
1015        &self,
1016        object_id: &ObjectID,
1017        version: crate::base_types::ConsensusObjectVersion,
1018    ) -> Option<Object> {
1019        self.inner
1020            .load_implicitly_read_system_object(object_id, version)
1021    }
1022
1023    fn get_object_by_key(
1024        &self,
1025        object_id: &ObjectID,
1026        version: crate::base_types::VersionNumber,
1027    ) -> Option<Object> {
1028        self.inner
1029            .get_object_by_key(object_id, version)
1030            .inspect(|o| self.track_object(o))
1031    }
1032}
1033
1034impl ParentSync for TrackingBackingStore<'_> {
1035    fn get_latest_parent_entry_ref_deprecated(
1036        &self,
1037        object_id: ObjectID,
1038    ) -> Option<crate::base_types::ObjectRef> {
1039        self.inner.get_latest_parent_entry_ref_deprecated(object_id)
1040    }
1041}