Skip to main content

sui_core/execution_cache/
object_locks.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use mysten_common::ZipDebugEqIteratorExt;
5
6#[cfg(test)]
7use crate::authority::authority_per_epoch_store::AuthorityPerEpochStore;
8use sui_types::base_types::{ObjectID, ObjectRef};
9#[cfg(test)]
10use sui_types::digests::TransactionDigest;
11use sui_types::error::{SuiErrorKind, SuiResult, UserInputError};
12use sui_types::object::Object;
13use sui_types::storage::ObjectStore;
14use tracing::{debug, instrument};
15
16use super::writeback_cache::WritebackCache;
17
18pub(super) struct ObjectLocks {}
19
20impl ObjectLocks {
21    pub fn new() -> Self {
22        Self {}
23    }
24
25    #[cfg(test)]
26    pub(crate) fn get_transaction_lock(
27        &self,
28        obj_ref: &ObjectRef,
29        epoch_store: &AuthorityPerEpochStore,
30    ) -> SuiResult<Option<TransactionDigest>> {
31        epoch_store.tables()?.get_locked_transaction(obj_ref)
32    }
33
34    pub(crate) fn clear(&self) {
35        // No-op: pre-consensus locking is disabled, so there's no in-memory lock state to clear.
36        // Lock state is managed in the database via post-consensus locking.
37    }
38
39    fn verify_live_object(obj_ref: &ObjectRef, live_object: &Object) -> SuiResult {
40        debug_assert_eq!(obj_ref.0, live_object.id());
41        if obj_ref.1 != live_object.version() {
42            debug!(
43                "object version unavailable for consumption: {:?} (current: {})",
44                obj_ref,
45                live_object.version()
46            );
47            return Err(SuiErrorKind::UserInputError {
48                error: UserInputError::ObjectVersionUnavailableForConsumption {
49                    provided_obj_ref: *obj_ref,
50                    current_version: live_object.version(),
51                },
52            }
53            .into());
54        }
55
56        let live_digest = live_object.digest();
57        if obj_ref.2 != live_digest {
58            return Err(SuiErrorKind::UserInputError {
59                error: UserInputError::InvalidObjectDigest {
60                    object_id: obj_ref.0,
61                    expected_digest: live_digest,
62                },
63            }
64            .into());
65        }
66
67        Ok(())
68    }
69
70    fn multi_get_objects_must_exist(
71        cache: &WritebackCache,
72        object_ids: &[ObjectID],
73    ) -> SuiResult<Vec<Object>> {
74        let objects = cache.multi_get_objects(object_ids);
75        let mut result = Vec::with_capacity(objects.len());
76        for (i, object) in objects.into_iter().enumerate() {
77            if let Some(object) = object {
78                result.push(object);
79            } else {
80                return Err(SuiErrorKind::UserInputError {
81                    error: UserInputError::ObjectNotFound {
82                        object_id: object_ids[i],
83                        version: None,
84                    },
85                }
86                .into());
87            }
88        }
89        Ok(result)
90    }
91
92    /// Validates owned object versions and digests without acquiring locks.
93    /// Used to validate objects before signing, since locking happens post-consensus.
94    #[instrument(level = "debug", skip_all)]
95    pub(crate) fn validate_owned_object_versions(
96        cache: &WritebackCache,
97        owned_input_objects: &[ObjectRef],
98    ) -> SuiResult {
99        let object_ids = owned_input_objects.iter().map(|o| o.0).collect::<Vec<_>>();
100        let live_objects = Self::multi_get_objects_must_exist(cache, &object_ids)?;
101
102        // Validate that all objects are live and versions/digests match
103        for (obj_ref, live_object) in owned_input_objects.iter().zip_debug_eq(live_objects.iter()) {
104            Self::verify_live_object(obj_ref, live_object)?;
105        }
106
107        Ok(())
108    }
109}