sui_core/
execution_cache.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::accumulators::funds_read::AccountFundsRead;
5use crate::authority::AuthorityStore;
6use crate::authority::authority_per_epoch_store::AuthorityPerEpochStore;
7use crate::authority::authority_store::{ExecutionLockWriteGuard, SuiLockResult};
8use crate::authority::backpressure::BackpressureManager;
9use crate::authority::epoch_start_configuration::EpochFlag;
10use crate::authority::epoch_start_configuration::EpochStartConfiguration;
11use crate::global_state_hasher::GlobalStateHashStore;
12use crate::transaction_outputs::TransactionOutputs;
13use either::Either;
14use itertools::Itertools;
15use mysten_common::fatal;
16use sui_types::accumulator_event::AccumulatorEvent;
17use sui_types::bridge::Bridge;
18
19use futures::{FutureExt, future::BoxFuture};
20use prometheus::Registry;
21use std::collections::HashSet;
22use std::path::Path;
23use std::sync::Arc;
24use sui_config::ExecutionCacheConfig;
25use sui_protocol_config::ProtocolVersion;
26use sui_types::base_types::{FullObjectID, VerifiedExecutionData};
27use sui_types::digests::{TransactionDigest, TransactionEffectsDigest};
28use sui_types::effects::{TransactionEffects, TransactionEvents};
29use sui_types::error::{SuiError, SuiErrorKind, SuiResult, UserInputError};
30use sui_types::executable_transaction::VerifiedExecutableTransaction;
31use sui_types::messages_checkpoint::CheckpointSequenceNumber;
32use sui_types::object::Object;
33use sui_types::storage::{
34    BackingPackageStore, BackingStore, ChildObjectResolver, FullObjectKey, MarkerValue, ObjectKey,
35    ObjectOrTombstone, ObjectStore, PackageObject, ParentSync,
36};
37use sui_types::sui_system_state::SuiSystemState;
38use sui_types::transaction::{VerifiedSignedTransaction, VerifiedTransaction};
39use sui_types::{
40    base_types::{EpochId, ObjectID, ObjectRef, SequenceNumber},
41    object::Owner,
42    storage::InputKey,
43};
44use tracing::instrument;
45use typed_store::rocks::DBBatch;
46
47pub(crate) mod cache_types;
48pub mod metrics;
49mod object_locks;
50pub mod writeback_cache;
51
52pub use writeback_cache::WritebackCache;
53
54use metrics::ExecutionCacheMetrics;
55
56// If you have Arc<ExecutionCache>, you cannot return a reference to it as
57// an &Arc<dyn ExecutionCacheRead> (for example), because the trait object is a fat pointer.
58// So, in order to be able to return &Arc<dyn T>, we create all the converted trait objects
59// (aka fat pointers) up front and return references to them.
60#[derive(Clone)]
61pub struct ExecutionCacheTraitPointers {
62    pub object_cache_reader: Arc<dyn ObjectCacheRead>,
63    pub transaction_cache_reader: Arc<dyn TransactionCacheRead>,
64    pub cache_writer: Arc<dyn ExecutionCacheWrite>,
65    pub backing_store: Arc<dyn BackingStore + Send + Sync>,
66    pub child_object_resolver: Arc<dyn ChildObjectResolver + Send + Sync>,
67    pub backing_package_store: Arc<dyn BackingPackageStore + Send + Sync>,
68    pub object_store: Arc<dyn ObjectStore + Send + Sync>,
69    pub reconfig_api: Arc<dyn ExecutionCacheReconfigAPI>,
70    pub global_state_hash_store: Arc<dyn GlobalStateHashStore>,
71    pub checkpoint_cache: Arc<dyn CheckpointCache>,
72    pub state_sync_store: Arc<dyn StateSyncAPI>,
73    pub cache_commit: Arc<dyn ExecutionCacheCommit>,
74    pub testing_api: Arc<dyn TestingAPI>,
75    pub account_funds_read: Arc<dyn AccountFundsRead>,
76}
77
78impl ExecutionCacheTraitPointers {
79    pub fn new<T>(cache: Arc<T>) -> Self
80    where
81        T: ObjectCacheRead
82            + TransactionCacheRead
83            + ExecutionCacheWrite
84            + BackingStore
85            + BackingPackageStore
86            + ObjectStore
87            + ExecutionCacheReconfigAPI
88            + GlobalStateHashStore
89            + CheckpointCache
90            + StateSyncAPI
91            + ExecutionCacheCommit
92            + TestingAPI
93            + AccountFundsRead
94            + 'static,
95    {
96        Self {
97            object_cache_reader: cache.clone(),
98            transaction_cache_reader: cache.clone(),
99            cache_writer: cache.clone(),
100            backing_store: cache.clone(),
101            child_object_resolver: cache.clone(),
102            backing_package_store: cache.clone(),
103            object_store: cache.clone(),
104            reconfig_api: cache.clone(),
105            global_state_hash_store: cache.clone(),
106            checkpoint_cache: cache.clone(),
107            state_sync_store: cache.clone(),
108            cache_commit: cache.clone(),
109            testing_api: cache.clone(),
110            account_funds_read: cache.clone(),
111        }
112    }
113}
114
115pub fn build_execution_cache(
116    cache_config: &ExecutionCacheConfig,
117    prometheus_registry: &Registry,
118    store: &Arc<AuthorityStore>,
119    backpressure_manager: Arc<BackpressureManager>,
120) -> ExecutionCacheTraitPointers {
121    let execution_cache_metrics = Arc::new(ExecutionCacheMetrics::new(prometheus_registry));
122
123    ExecutionCacheTraitPointers::new(
124        WritebackCache::new(
125            cache_config,
126            store.clone(),
127            execution_cache_metrics,
128            backpressure_manager,
129        )
130        .into(),
131    )
132}
133
134/// Should only be used for sui-tool or tests. Nodes must use build_execution_cache which
135/// uses the epoch_start_config to prevent cache impl from switching except at epoch boundaries.
136pub fn build_execution_cache_from_env(
137    prometheus_registry: &Registry,
138    store: &Arc<AuthorityStore>,
139) -> ExecutionCacheTraitPointers {
140    let execution_cache_metrics = Arc::new(ExecutionCacheMetrics::new(prometheus_registry));
141
142    ExecutionCacheTraitPointers::new(
143        WritebackCache::new(
144            &Default::default(),
145            store.clone(),
146            execution_cache_metrics,
147            BackpressureManager::new_for_tests(),
148        )
149        .into(),
150    )
151}
152
153pub type Batch = (Vec<Arc<TransactionOutputs>>, DBBatch);
154
155pub trait ExecutionCacheCommit: Send + Sync {
156    /// Build a DBBatch containing the given transaction outputs.
157    fn build_db_batch(&self, epoch: EpochId, digests: &[TransactionDigest]) -> Batch;
158
159    /// Durably commit the outputs of the given transactions to the database.
160    /// Will be called by CheckpointExecutor to ensure that transaction outputs are
161    /// written durably before marking a checkpoint as finalized.
162    fn commit_transaction_outputs(
163        &self,
164        epoch: EpochId,
165        batch: Batch,
166        digests: &[TransactionDigest],
167    );
168
169    /// Durably commit a transaction to the database. Used to store any transactions
170    /// that cannot be reconstructed at start-up by consensus replay. Currently the only
171    /// case of this is RandomnessStateUpdate.
172    fn persist_transaction(&self, transaction: &VerifiedExecutableTransaction);
173
174    // Number of pending uncommitted transactions
175    fn approximate_pending_transaction_count(&self) -> u64;
176}
177
178pub trait ObjectCacheRead: Send + Sync {
179    fn get_package_object(&self, id: &ObjectID) -> SuiResult<Option<PackageObject>>;
180    fn force_reload_system_packages(&self, system_package_ids: &[ObjectID]);
181
182    fn get_object(&self, id: &ObjectID) -> Option<Object>;
183
184    fn get_objects(&self, objects: &[ObjectID]) -> Vec<Option<Object>> {
185        let mut ret = Vec::with_capacity(objects.len());
186        for object_id in objects {
187            ret.push(self.get_object(object_id));
188        }
189        ret
190    }
191
192    fn get_latest_object_ref_or_tombstone(&self, object_id: ObjectID) -> Option<ObjectRef>;
193
194    fn get_latest_object_or_tombstone(
195        &self,
196        object_id: ObjectID,
197    ) -> Option<(ObjectKey, ObjectOrTombstone)>;
198
199    fn get_object_by_key(&self, object_id: &ObjectID, version: SequenceNumber) -> Option<Object>;
200
201    fn multi_get_objects_by_key(&self, object_keys: &[ObjectKey]) -> Vec<Option<Object>>;
202
203    fn object_exists_by_key(&self, object_id: &ObjectID, version: SequenceNumber) -> bool;
204
205    fn multi_object_exists_by_key(&self, object_keys: &[ObjectKey]) -> Vec<bool>;
206
207    /// Load a list of objects from the store by object reference.
208    /// If they exist in the store, they are returned directly.
209    /// If any object missing, we try to figure out the best error to return.
210    /// If the object we are asking is currently locked at a future version, we know this
211    /// transaction is out-of-date and we return a ObjectVersionUnavailableForConsumption,
212    /// which indicates this is not retriable.
213    /// Otherwise, we return a ObjectNotFound error, which indicates this is retriable.
214    fn multi_get_objects_with_more_accurate_error_return(
215        &self,
216        object_refs: &[ObjectRef],
217    ) -> Result<Vec<Object>, SuiError> {
218        let objects = self
219            .multi_get_objects_by_key(&object_refs.iter().map(ObjectKey::from).collect::<Vec<_>>());
220        let mut result = Vec::new();
221        for (object_opt, object_ref) in objects.into_iter().zip(object_refs) {
222            match object_opt {
223                None => {
224                    let live_objref = self._get_live_objref(object_ref.0)?;
225                    let error = if live_objref.1 >= object_ref.1 {
226                        UserInputError::ObjectVersionUnavailableForConsumption {
227                            provided_obj_ref: *object_ref,
228                            current_version: live_objref.1,
229                        }
230                    } else {
231                        UserInputError::ObjectNotFound {
232                            object_id: object_ref.0,
233                            version: Some(object_ref.1),
234                        }
235                    };
236                    return Err(SuiErrorKind::UserInputError { error }.into());
237                }
238                Some(object) => {
239                    result.push(object);
240                }
241            }
242        }
243        assert_eq!(result.len(), object_refs.len());
244        Ok(result)
245    }
246
247    /// Used by execution scheduler to determine if input objects are ready. Distinct from multi_get_object_by_key
248    /// because it also consults markers to handle the case where an object will never become available (e.g.
249    /// because it has been received by some other transaction already).
250    fn multi_input_objects_available(
251        &self,
252        keys: &[InputKey],
253        receiving_objects: &HashSet<InputKey>,
254        epoch: EpochId,
255    ) -> Vec<bool> {
256        let mut results = vec![false; keys.len()];
257        let non_canceled_keys = keys.iter().enumerate().filter(|(idx, key)| {
258            if key.is_cancelled() {
259                // Shared objects in canceled transactions are always available.
260                results[*idx] = true;
261                false
262            } else {
263                true
264            }
265        });
266        let (move_object_keys, package_object_keys): (Vec<_>, Vec<_>) = non_canceled_keys
267            .partition_map(|(idx, key)| match key {
268                InputKey::VersionedObject { id, version } => Either::Left((idx, (id, version))),
269                InputKey::Package { id } => Either::Right((idx, id)),
270            });
271
272        for ((idx, (id, version)), has_key) in move_object_keys.iter().zip(
273            self.multi_object_exists_by_key(
274                &move_object_keys
275                    .iter()
276                    .map(|(_, k)| ObjectKey(k.0.id(), *k.1))
277                    .collect::<Vec<_>>(),
278            )
279            .into_iter(),
280        ) {
281            // If the key exists at the specified version, then the object is available.
282            if has_key {
283                results[*idx] = true;
284            } else if receiving_objects.contains(&InputKey::VersionedObject {
285                id: **id,
286                version: **version,
287            }) {
288                // There could be a more recent version of this object, and the object at the
289                // specified version could have already been pruned. In such a case `has_key` will
290                // be false, but since this is a receiving object we should mark it as available if
291                // we can determine that an object with a version greater than or equal to the
292                // specified version exists or was deleted. We will then let mark it as available
293                // to let the transaction through so it can fail at execution.
294                let is_available = self
295                    .get_object(&id.id())
296                    .map(|obj| obj.version() >= **version)
297                    .unwrap_or(false)
298                    || self.fastpath_stream_ended_at_version_or_after(id.id(), **version, epoch);
299                results[*idx] = is_available;
300            } else {
301                // If the object is an already-removed consensus object, mark it as available if the
302                // version for that object is in the marker table.
303                let is_consensus_stream_ended = self
304                    .get_consensus_stream_end_tx_digest(FullObjectKey::new(**id, **version), epoch)
305                    .is_some();
306                results[*idx] = is_consensus_stream_ended;
307            }
308        }
309
310        package_object_keys.into_iter().for_each(|(idx, id)| {
311            // get_package_object() only errors when the object is not a package, so returning false on error.
312            // Error is possible when this gets called on uncertified transactions.
313            results[idx] = self.get_package_object(id).is_ok_and(|p| p.is_some());
314        });
315
316        results
317    }
318
319    fn multi_input_objects_available_cache_only(&self, keys: &[InputKey]) -> Vec<bool>;
320
321    /// Return the object with version less then or eq to the provided seq number.
322    /// This is used by indexer to find the correct version of dynamic field child object.
323    /// We do not store the version of the child object, but because of lamport timestamp,
324    /// we know the child must have version number less then or eq to the parent.
325    fn find_object_lt_or_eq_version(
326        &self,
327        object_id: ObjectID,
328        version: SequenceNumber,
329    ) -> Option<Object>;
330
331    fn get_lock(&self, obj_ref: ObjectRef, epoch_store: &AuthorityPerEpochStore) -> SuiLockResult;
332
333    // This method is considered "private" - only used by multi_get_objects_with_more_accurate_error_return
334    fn _get_live_objref(&self, object_id: ObjectID) -> SuiResult<ObjectRef>;
335
336    // Check that the given set of objects are live at the given version. This is used as a
337    // safety check before execution, and could potentially be deleted or changed to a debug_assert
338    fn check_owned_objects_are_live(&self, owned_object_refs: &[ObjectRef]) -> SuiResult;
339
340    fn get_sui_system_state_object_unsafe(&self) -> SuiResult<SuiSystemState>;
341
342    fn get_bridge_object_unsafe(&self) -> SuiResult<Bridge>;
343
344    // Marker methods
345
346    /// Get the marker at a specific version
347    fn get_marker_value(&self, object_key: FullObjectKey, epoch_id: EpochId)
348    -> Option<MarkerValue>;
349
350    /// Get the latest marker for a given object.
351    fn get_latest_marker(
352        &self,
353        object_id: FullObjectID,
354        epoch_id: EpochId,
355    ) -> Option<(SequenceNumber, MarkerValue)>;
356
357    /// If the given consensus object stream was ended, return related
358    /// version and transaction digest.
359    fn get_last_consensus_stream_end_info(
360        &self,
361        object_id: FullObjectID,
362        epoch_id: EpochId,
363    ) -> Option<(SequenceNumber, TransactionDigest)> {
364        match self.get_latest_marker(object_id, epoch_id) {
365            Some((version, MarkerValue::ConsensusStreamEnded(digest))) => Some((version, digest)),
366            _ => None,
367        }
368    }
369
370    /// If the given consensus object stream was ended at the specified version,
371    /// return related transaction digest.
372    fn get_consensus_stream_end_tx_digest(
373        &self,
374        object_key: FullObjectKey,
375        epoch_id: EpochId,
376    ) -> Option<TransactionDigest> {
377        match self.get_marker_value(object_key, epoch_id) {
378            Some(MarkerValue::ConsensusStreamEnded(digest)) => Some(digest),
379            _ => None,
380        }
381    }
382
383    fn have_received_object_at_version(
384        &self,
385        object_key: FullObjectKey,
386        epoch_id: EpochId,
387    ) -> bool {
388        matches!(
389            self.get_marker_value(object_key, epoch_id),
390            Some(MarkerValue::Received)
391        )
392    }
393
394    fn fastpath_stream_ended_at_version_or_after(
395        &self,
396        object_id: ObjectID,
397        version: SequenceNumber,
398        epoch_id: EpochId,
399    ) -> bool {
400        let full_id = FullObjectID::Fastpath(object_id); // function explicitly assumes "fastpath"
401        matches!(
402            self.get_latest_marker(full_id, epoch_id),
403            Some((marker_version, MarkerValue::FastpathStreamEnded)) if marker_version >= version
404        )
405    }
406
407    /// Return the watermark for the highest checkpoint for which we've pruned objects.
408    fn get_highest_pruned_checkpoint(&self) -> Option<CheckpointSequenceNumber>;
409
410    /// Given a list of input and receiving objects for a transaction,
411    /// wait until all of them become available, so that the transaction
412    /// can start execution.
413    /// `input_and_receiving_keys` contains both input objects and receiving
414    /// input objects, including canceled objects.
415    /// TODO: Eventually this can return the objects read results,
416    /// so that execution does not need to load them again.
417    fn notify_read_input_objects<'a>(
418        &'a self,
419        input_and_receiving_keys: &'a [InputKey],
420        receiving_keys: &'a HashSet<InputKey>,
421        epoch: EpochId,
422    ) -> BoxFuture<'a, ()>;
423}
424
425pub trait TransactionCacheRead: Send + Sync {
426    fn multi_get_transaction_blocks(
427        &self,
428        digests: &[TransactionDigest],
429    ) -> Vec<Option<Arc<VerifiedTransaction>>>;
430
431    fn get_transaction_block(
432        &self,
433        digest: &TransactionDigest,
434    ) -> Option<Arc<VerifiedTransaction>> {
435        self.multi_get_transaction_blocks(&[*digest])
436            .pop()
437            .expect("multi-get must return correct number of items")
438    }
439
440    #[instrument(level = "trace", skip_all)]
441    fn get_transactions_and_serialized_sizes(
442        &self,
443        digests: &[TransactionDigest],
444    ) -> SuiResult<Vec<Option<(VerifiedTransaction, usize)>>> {
445        let txns = self.multi_get_transaction_blocks(digests);
446        txns.into_iter()
447            .map(|txn| {
448                txn.map(|txn| {
449                    // Note: if the transaction is read from the db, we are wasting some
450                    // effort relative to reading the raw bytes from the db instead of
451                    // calling serialized_size. However, transactions should usually be
452                    // fetched from cache.
453                    match txn.serialized_size() {
454                        Ok(size) => Ok(((*txn).clone(), size)),
455                        Err(e) => Err(e),
456                    }
457                })
458                .transpose()
459            })
460            .collect::<Result<Vec<_>, _>>()
461    }
462
463    fn multi_get_executed_effects_digests(
464        &self,
465        digests: &[TransactionDigest],
466    ) -> Vec<Option<TransactionEffectsDigest>>;
467
468    fn is_tx_already_executed(&self, digest: &TransactionDigest) -> bool {
469        self.multi_get_executed_effects_digests(&[*digest])
470            .pop()
471            .expect("multi-get must return correct number of items")
472            .is_some()
473    }
474
475    fn multi_get_executed_effects(
476        &self,
477        digests: &[TransactionDigest],
478    ) -> Vec<Option<TransactionEffects>> {
479        let effects_digests = self.multi_get_executed_effects_digests(digests);
480        assert_eq!(effects_digests.len(), digests.len());
481
482        let mut results = vec![None; digests.len()];
483        let mut fetch_digests = Vec::with_capacity(digests.len());
484        let mut fetch_indices = Vec::with_capacity(digests.len());
485
486        for (i, digest) in effects_digests.into_iter().enumerate() {
487            if let Some(digest) = digest {
488                fetch_digests.push(digest);
489                fetch_indices.push(i);
490            }
491        }
492
493        let effects = self.multi_get_effects(&fetch_digests);
494        for (i, effects) in fetch_indices.into_iter().zip(effects.into_iter()) {
495            results[i] = effects;
496        }
497
498        results
499    }
500
501    fn get_executed_effects(&self, digest: &TransactionDigest) -> Option<TransactionEffects> {
502        self.multi_get_executed_effects(&[*digest])
503            .pop()
504            .expect("multi-get must return correct number of items")
505    }
506
507    fn transaction_executed_in_last_epoch(
508        &self,
509        digest: &TransactionDigest,
510        current_epoch: EpochId,
511    ) -> bool;
512
513    fn multi_get_effects(
514        &self,
515        digests: &[TransactionEffectsDigest],
516    ) -> Vec<Option<TransactionEffects>>;
517
518    fn get_effects(&self, digest: &TransactionEffectsDigest) -> Option<TransactionEffects> {
519        self.multi_get_effects(&[*digest])
520            .pop()
521            .expect("multi-get must return correct number of items")
522    }
523
524    fn multi_get_events(&self, digests: &[TransactionDigest]) -> Vec<Option<TransactionEvents>>;
525
526    fn get_events(&self, digest: &TransactionDigest) -> Option<TransactionEvents> {
527        self.multi_get_events(&[*digest])
528            .pop()
529            .expect("multi-get must return correct number of items")
530    }
531
532    fn get_unchanged_loaded_runtime_objects(
533        &self,
534        digest: &TransactionDigest,
535    ) -> Option<Vec<ObjectKey>>;
536
537    fn take_accumulator_events(&self, digest: &TransactionDigest) -> Option<Vec<AccumulatorEvent>>;
538
539    fn notify_read_executed_effects_digests<'a>(
540        &'a self,
541        task_name: &'static str,
542        digests: &'a [TransactionDigest],
543    ) -> BoxFuture<'a, Vec<TransactionEffectsDigest>>;
544
545    /// Wait until the effects of the given transactions are available and return them.
546    /// WARNING: If calling this on a transaction that could be reverted, you must be
547    /// sure that this function cannot be called during reconfiguration. The best way to
548    /// do this is to wrap your future in EpochStore::within_alive_epoch. Holding an
549    /// ExecutionLockReadGuard would also prevent reconfig from happening while waiting,
550    /// but this is very dangerous, as it could prevent reconfiguration from ever
551    /// occurring!
552    fn notify_read_executed_effects<'a>(
553        &'a self,
554        task_name: &'static str,
555        digests: &'a [TransactionDigest],
556    ) -> BoxFuture<'a, Vec<TransactionEffects>> {
557        async move {
558            let digests = self
559                .notify_read_executed_effects_digests(task_name, digests)
560                .await;
561            // once digests are available, effects must be present as well
562            self.multi_get_effects(&digests)
563                .into_iter()
564                .map(|e| e.unwrap_or_else(|| fatal!("digests must exist")))
565                .collect()
566        }
567        .boxed()
568    }
569
570    /// Get the execution outputs of a mysticeti fastpath certified transaction, if it exists.
571    fn get_mysticeti_fastpath_outputs(
572        &self,
573        tx_digest: &TransactionDigest,
574    ) -> Option<Arc<TransactionOutputs>>;
575
576    /// Wait until the outputs of the given transactions are available
577    /// in the temporary buffer holding mysticeti fastpath outputs.
578    fn notify_read_fastpath_transaction_outputs<'a>(
579        &'a self,
580        tx_digests: &'a [TransactionDigest],
581    ) -> BoxFuture<'a, Vec<Arc<TransactionOutputs>>>;
582}
583
584pub trait ExecutionCacheWrite: Send + Sync {
585    /// Write the output of a transaction.
586    ///
587    /// Because of the child object consistency rule (readers that observe parents must observe all
588    /// children of that parent, up to the parent's version bound), implementations of this method
589    /// must not write any top-level (address-owned or shared) objects before they have written all
590    /// of the object-owned objects (i.e. child objects) in the `objects` list.
591    ///
592    /// In the future, we may modify this method to expose finer-grained information about
593    /// parent/child relationships. (This may be especially necessary for distributed object
594    /// storage, but is unlikely to be an issue before we tackle that problem).
595    ///
596    /// This function may evict the mutable input objects (and successfully received objects) of
597    /// transaction from the cache, since they cannot be read by any other transaction.
598    ///
599    /// Any write performed by this method immediately notifies any waiter that has previously
600    /// called notify_read_objects_for_execution or notify_read_objects_for_signing for the object
601    /// in question.
602    fn write_transaction_outputs(&self, epoch_id: EpochId, tx_outputs: Arc<TransactionOutputs>);
603
604    /// Write the output of a Mysticeti fastpath certified transaction.
605    /// Such output cannot be written to the dirty cache right away because
606    /// the transaction may end up rejected by consensus later. We need to make sure
607    /// that it is not visible to any subsequent transaction until we observe it
608    /// from consensus or checkpoints.
609    fn write_fastpath_transaction_outputs(&self, tx_outputs: Arc<TransactionOutputs>);
610
611    /// Attempt to acquire object locks for all of the owned input locks.
612    fn acquire_transaction_locks(
613        &self,
614        epoch_store: &AuthorityPerEpochStore,
615        owned_input_objects: &[ObjectRef],
616        tx_digest: TransactionDigest,
617        signed_transaction: Option<VerifiedSignedTransaction>,
618    ) -> SuiResult;
619
620    /// Validate owned object versions and digests without acquiring locks.
621    /// Used when preconsensus locking is disabled to validate objects without locking,
622    /// since locking happens post-consensus in that mode.
623    fn validate_owned_object_versions(&self, owned_input_objects: &[ObjectRef]) -> SuiResult;
624
625    /// Write an object entry directly to the cache for testing.
626    /// This allows us to write an object without constructing the entire
627    /// transaction outputs.
628    #[cfg(test)]
629    fn write_object_entry_for_test(&self, object: Object);
630}
631
632pub trait CheckpointCache: Send + Sync {
633    // TODO: In addition to the deprecated methods below, this will eventually include access
634    // to the CheckpointStore
635
636    // DEPRECATED METHODS
637    fn deprecated_get_transaction_checkpoint(
638        &self,
639        digest: &TransactionDigest,
640    ) -> Option<(EpochId, CheckpointSequenceNumber)>;
641
642    fn deprecated_multi_get_transaction_checkpoint(
643        &self,
644        digests: &[TransactionDigest],
645    ) -> Vec<Option<(EpochId, CheckpointSequenceNumber)>>;
646
647    fn deprecated_insert_finalized_transactions(
648        &self,
649        digests: &[TransactionDigest],
650        epoch: EpochId,
651        sequence: CheckpointSequenceNumber,
652    );
653}
654
655pub trait ExecutionCacheReconfigAPI: Send + Sync {
656    fn insert_genesis_object(&self, object: Object);
657    fn bulk_insert_genesis_objects(&self, objects: &[Object]);
658
659    fn set_epoch_start_configuration(&self, epoch_start_config: &EpochStartConfiguration);
660
661    fn update_epoch_flags_metrics(&self, old: &[EpochFlag], new: &[EpochFlag]);
662
663    fn clear_state_end_of_epoch(&self, execution_guard: &ExecutionLockWriteGuard<'_>);
664
665    fn expensive_check_sui_conservation(
666        &self,
667        old_epoch_store: &AuthorityPerEpochStore,
668    ) -> SuiResult;
669
670    fn checkpoint_db(&self, path: &Path) -> SuiResult;
671
672    /// This is a temporary method to be used when we enable simplified_unwrap_then_delete.
673    /// It re-accumulates state hash for the new epoch if simplified_unwrap_then_delete is enabled.
674    fn maybe_reaccumulate_state_hash(
675        &self,
676        cur_epoch_store: &AuthorityPerEpochStore,
677        new_protocol_version: ProtocolVersion,
678    );
679
680    /// Reconfigure the cache itself.
681    /// TODO: this is only needed for ProxyCache to switch between cache impls. It can be removed
682    /// once WritebackCache is the sole cache impl.
683    fn reconfigure_cache<'a>(
684        &'a self,
685        epoch_start_config: &'a EpochStartConfiguration,
686    ) -> BoxFuture<'a, ()>;
687}
688
689// StateSyncAPI is for writing any data that was not the result of transaction execution,
690// but that arrived via state sync. The fact that it came via state sync implies that it
691// is certified output, and can be immediately persisted to the store.
692pub trait StateSyncAPI: Send + Sync {
693    fn insert_transaction_and_effects(
694        &self,
695        transaction: &VerifiedTransaction,
696        transaction_effects: &TransactionEffects,
697    );
698
699    fn multi_insert_transaction_and_effects(
700        &self,
701        transactions_and_effects: &[VerifiedExecutionData],
702    );
703}
704
705pub trait TestingAPI: Send + Sync {
706    fn database_for_testing(&self) -> Arc<AuthorityStore>;
707
708    fn cache_for_testing(&self) -> &WritebackCache;
709}
710
711macro_rules! implement_storage_traits {
712    ($implementor: ident) => {
713        impl ObjectStore for $implementor {
714            fn get_object(&self, object_id: &ObjectID) -> Option<Object> {
715                ObjectCacheRead::get_object(self, object_id)
716            }
717
718            fn get_object_by_key(
719                &self,
720                object_id: &ObjectID,
721                version: sui_types::base_types::VersionNumber,
722            ) -> Option<Object> {
723                ObjectCacheRead::get_object_by_key(self, object_id, version)
724            }
725        }
726
727        impl ChildObjectResolver for $implementor {
728            fn read_child_object(
729                &self,
730                parent: &ObjectID,
731                child: &ObjectID,
732                child_version_upper_bound: SequenceNumber,
733            ) -> SuiResult<Option<Object>> {
734                let Some(child_object) =
735                    self.find_object_lt_or_eq_version(*child, child_version_upper_bound)
736                else {
737                    return Ok(None);
738                };
739
740                let parent = *parent;
741                if child_object.owner != Owner::ObjectOwner(parent.into()) {
742                    return Err(SuiErrorKind::InvalidChildObjectAccess {
743                        object: *child,
744                        given_parent: parent,
745                        actual_owner: child_object.owner.clone(),
746                    }
747                    .into());
748                }
749                Ok(Some(child_object))
750            }
751
752            fn get_object_received_at_version(
753                &self,
754                owner: &ObjectID,
755                receiving_object_id: &ObjectID,
756                receive_object_at_version: SequenceNumber,
757                epoch_id: EpochId,
758            ) -> SuiResult<Option<Object>> {
759                let Some(recv_object) = ObjectCacheRead::get_object_by_key(
760                    self,
761                    receiving_object_id,
762                    receive_object_at_version,
763                ) else {
764                    return Ok(None);
765                };
766
767                // Check for:
768                // * Invalid access -- treat as the object does not exist. Or;
769                // * If we've already received the object at the version -- then treat it as though it doesn't exist.
770                // These two cases must remain indisguishable to the caller otherwise we risk forks in
771                // transaction replay due to possible reordering of transactions during replay.
772                if recv_object.owner != Owner::AddressOwner((*owner).into())
773                    || self.have_received_object_at_version(
774                        // TODO: Add support for receiving consensus objects. For now this assumes fastpath.
775                        FullObjectKey::new(
776                            FullObjectID::new(*receiving_object_id, None),
777                            receive_object_at_version,
778                        ),
779                        epoch_id,
780                    )
781                {
782                    return Ok(None);
783                }
784
785                Ok(Some(recv_object))
786            }
787        }
788
789        impl BackingPackageStore for $implementor {
790            fn get_package_object(
791                &self,
792                package_id: &ObjectID,
793            ) -> SuiResult<Option<PackageObject>> {
794                ObjectCacheRead::get_package_object(self, package_id)
795            }
796        }
797
798        impl ParentSync for $implementor {
799            fn get_latest_parent_entry_ref_deprecated(
800                &self,
801                object_id: ObjectID,
802            ) -> Option<ObjectRef> {
803                ObjectCacheRead::get_latest_object_ref_or_tombstone(self, object_id)
804            }
805        }
806    };
807}
808
809// Implement traits for a cache implementation that always go directly to the store.
810macro_rules! implement_passthrough_traits {
811    ($implementor: ident) => {
812        impl CheckpointCache for $implementor {
813            fn deprecated_get_transaction_checkpoint(
814                &self,
815                digest: &TransactionDigest,
816            ) -> Option<(EpochId, CheckpointSequenceNumber)> {
817                self.store
818                    .deprecated_get_transaction_checkpoint(digest)
819                    .expect("db error")
820            }
821
822            fn deprecated_multi_get_transaction_checkpoint(
823                &self,
824                digests: &[TransactionDigest],
825            ) -> Vec<Option<(EpochId, CheckpointSequenceNumber)>> {
826                self.store
827                    .deprecated_multi_get_transaction_checkpoint(digests)
828                    .expect("db error")
829            }
830
831            fn deprecated_insert_finalized_transactions(
832                &self,
833                digests: &[TransactionDigest],
834                epoch: EpochId,
835                sequence: CheckpointSequenceNumber,
836            ) {
837                self.store
838                    .deprecated_insert_finalized_transactions(digests, epoch, sequence)
839                    .expect("db error");
840            }
841        }
842
843        impl ExecutionCacheReconfigAPI for $implementor {
844            fn insert_genesis_object(&self, object: Object) {
845                self.insert_genesis_object_impl(object)
846            }
847
848            fn bulk_insert_genesis_objects(&self, objects: &[Object]) {
849                self.bulk_insert_genesis_objects_impl(objects)
850            }
851
852            fn set_epoch_start_configuration(&self, epoch_start_config: &EpochStartConfiguration) {
853                self.store
854                    .set_epoch_start_configuration(epoch_start_config)
855                    .expect("db error");
856            }
857
858            fn update_epoch_flags_metrics(&self, old: &[EpochFlag], new: &[EpochFlag]) {
859                self.store.update_epoch_flags_metrics(old, new)
860            }
861
862            fn clear_state_end_of_epoch(&self, execution_guard: &ExecutionLockWriteGuard<'_>) {
863                self.clear_state_end_of_epoch_impl(execution_guard)
864            }
865
866            fn expensive_check_sui_conservation(
867                &self,
868                old_epoch_store: &AuthorityPerEpochStore,
869            ) -> SuiResult {
870                self.store
871                    .expensive_check_sui_conservation(self, old_epoch_store)
872            }
873
874            fn checkpoint_db(&self, path: &std::path::Path) -> SuiResult {
875                self.store.perpetual_tables.checkpoint_db(path)
876            }
877
878            fn maybe_reaccumulate_state_hash(
879                &self,
880                cur_epoch_store: &AuthorityPerEpochStore,
881                new_protocol_version: ProtocolVersion,
882            ) {
883                self.store
884                    .maybe_reaccumulate_state_hash(cur_epoch_store, new_protocol_version)
885            }
886
887            fn reconfigure_cache<'a>(
888                &'a self,
889                _: &'a EpochStartConfiguration,
890            ) -> BoxFuture<'a, ()> {
891                // Since we now use WritebackCache directly at startup (if the epoch flag is set),
892                // this can be called at reconfiguration time. It is a no-op.
893                // TODO: remove this once we completely remove ProxyCache.
894                std::future::ready(()).boxed()
895            }
896        }
897
898        impl TestingAPI for $implementor {
899            fn database_for_testing(&self) -> Arc<AuthorityStore> {
900                self.store.clone()
901            }
902
903            fn cache_for_testing(&self) -> &WritebackCache {
904                self
905            }
906        }
907    };
908}
909
910use implement_passthrough_traits;
911
912implement_storage_traits!(WritebackCache);
913
914pub trait ExecutionCacheAPI:
915    ObjectCacheRead
916    + ExecutionCacheWrite
917    + ExecutionCacheCommit
918    + ExecutionCacheReconfigAPI
919    + CheckpointCache
920    + StateSyncAPI
921{
922}