sui_core/execution_cache/
object_locks.rs

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