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