Skip to main content

sui_core/
storage.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::authority::AuthorityState;
5use crate::checkpoints::CheckpointStore;
6use crate::epoch::committee_store::CommitteeStore;
7use crate::execution_cache::ExecutionCacheTraitPointers;
8use move_core_types::language_storage::StructTag;
9use parking_lot::Mutex;
10use std::sync::Arc;
11use sui_rpc_store::RpcStoreReader;
12use sui_types::base_types::ExecutionData;
13use sui_types::base_types::ExecutionDigests;
14use sui_types::base_types::ObjectID;
15use sui_types::base_types::SequenceNumber;
16use sui_types::base_types::SuiAddress;
17use sui_types::base_types::TransactionDigest;
18use sui_types::base_types::VerifiedExecutionData;
19use sui_types::committee::Committee;
20use sui_types::committee::EpochId;
21use sui_types::effects::{TransactionEffects, TransactionEvents};
22use sui_types::error::{SuiErrorKind, SuiResult};
23use sui_types::messages_checkpoint::CheckpointContents;
24use sui_types::messages_checkpoint::CheckpointContentsDigest;
25use sui_types::messages_checkpoint::CheckpointDigest;
26use sui_types::messages_checkpoint::CheckpointSequenceNumber;
27use sui_types::messages_checkpoint::EndOfEpochData;
28use sui_types::messages_checkpoint::VerifiedCheckpoint;
29use sui_types::messages_checkpoint::VerifiedCheckpointContents;
30use sui_types::messages_checkpoint::VersionedFullCheckpointContents;
31use sui_types::object::Object;
32use sui_types::object::Owner;
33use sui_types::storage::BalanceInfo;
34use sui_types::storage::BalanceIterator;
35use sui_types::storage::CoinInfo;
36use sui_types::storage::DynamicFieldKey;
37use sui_types::storage::LedgerBitmapBucketIterator;
38use sui_types::storage::LedgerTxSeqDigest;
39use sui_types::storage::LedgerTxSeqDigestIterator;
40use sui_types::storage::ObjectStore;
41use sui_types::storage::OwnedObjectInfo;
42use sui_types::storage::RpcIndexes;
43use sui_types::storage::RpcStateReader;
44use sui_types::storage::RuntimeObjectResolver;
45use sui_types::storage::WriteStore;
46use sui_types::storage::error::Error as StorageError;
47use sui_types::storage::error::Result;
48use sui_types::storage::{BackingPackageStore, PackageObject};
49use sui_types::storage::{ObjectKey, ReadStore};
50use sui_types::transaction::VerifiedTransaction;
51use tap::TapFallible;
52use tracing::error;
53use typed_store::TypedStoreError;
54
55#[derive(Clone)]
56pub struct RocksDbStore {
57    cache_traits: ExecutionCacheTraitPointers,
58
59    committee_store: Arc<CommitteeStore>,
60    checkpoint_store: Arc<CheckpointStore>,
61    // in memory checkpoint watermark sequence numbers
62    highest_verified_checkpoint: Arc<Mutex<Option<u64>>>,
63    highest_synced_checkpoint: Arc<Mutex<Option<u64>>>,
64}
65
66impl RocksDbStore {
67    pub fn new(
68        cache_traits: ExecutionCacheTraitPointers,
69        committee_store: Arc<CommitteeStore>,
70        checkpoint_store: Arc<CheckpointStore>,
71    ) -> Self {
72        Self {
73            cache_traits,
74            committee_store,
75            checkpoint_store,
76            highest_verified_checkpoint: Arc::new(Mutex::new(None)),
77            highest_synced_checkpoint: Arc::new(Mutex::new(None)),
78        }
79    }
80
81    pub fn get_objects(&self, object_keys: &[ObjectKey]) -> Vec<Option<Object>> {
82        self.cache_traits
83            .object_cache_reader
84            .multi_get_objects_by_key(object_keys)
85    }
86
87    pub fn get_last_executed_checkpoint(&self) -> Option<VerifiedCheckpoint> {
88        self.checkpoint_store
89            .get_highest_executed_checkpoint()
90            .expect("db error")
91    }
92}
93
94impl ReadStore for RocksDbStore {
95    fn get_checkpoint_by_digest(&self, digest: &CheckpointDigest) -> Option<VerifiedCheckpoint> {
96        self.checkpoint_store
97            .get_checkpoint_by_digest(digest)
98            .expect("db error")
99    }
100
101    fn get_checkpoint_by_sequence_number(
102        &self,
103        sequence_number: CheckpointSequenceNumber,
104    ) -> Option<VerifiedCheckpoint> {
105        self.checkpoint_store
106            .get_checkpoint_by_sequence_number(sequence_number)
107            .expect("db error")
108    }
109
110    fn multi_get_checkpoint_by_sequence_number(
111        &self,
112        sequence_numbers: &[CheckpointSequenceNumber],
113    ) -> Vec<Option<VerifiedCheckpoint>> {
114        self.checkpoint_store
115            .multi_get_checkpoint_by_sequence_number(sequence_numbers)
116            .expect("db error")
117    }
118
119    fn get_highest_verified_checkpoint(&self) -> Result<VerifiedCheckpoint, StorageError> {
120        self.checkpoint_store
121            .get_highest_verified_checkpoint()
122            .map(|maybe_checkpoint| {
123                maybe_checkpoint
124                    .expect("storage should have been initialized with genesis checkpoint")
125            })
126            .map_err(Into::into)
127    }
128
129    fn get_highest_synced_checkpoint(&self) -> Result<VerifiedCheckpoint, StorageError> {
130        self.checkpoint_store
131            .get_highest_synced_checkpoint()
132            .map(|maybe_checkpoint| {
133                maybe_checkpoint
134                    .expect("storage should have been initialized with genesis checkpoint")
135            })
136            .map_err(Into::into)
137    }
138
139    fn get_lowest_available_checkpoint(&self) -> Result<CheckpointSequenceNumber, StorageError> {
140        if let Some(highest_pruned_cp) = self
141            .checkpoint_store
142            .get_highest_pruned_checkpoint_seq_number()
143            .map_err(Into::<StorageError>::into)?
144        {
145            Ok(highest_pruned_cp + 1)
146        } else {
147            Ok(0)
148        }
149    }
150
151    fn get_full_checkpoint_contents(
152        &self,
153        sequence_number: Option<CheckpointSequenceNumber>,
154        digest: &CheckpointContentsDigest,
155    ) -> Option<VersionedFullCheckpointContents> {
156        #[cfg(debug_assertions)]
157        if let Some(sequence_number) = sequence_number {
158            // When sequence_number is provided as an optimization, we want to ensure that
159            // the sequence number we get from the db matches the one we provided.
160            // Only check this in debug mode though.
161            if let Some(loaded_sequence_number) = self
162                .checkpoint_store
163                .get_sequence_number_by_contents_digest(digest)
164                .expect("db error")
165            {
166                assert_eq!(loaded_sequence_number, sequence_number);
167            }
168        }
169
170        let sequence_number = sequence_number.or_else(|| {
171            self.checkpoint_store
172                .get_sequence_number_by_contents_digest(digest)
173                .expect("db error")
174        });
175        if let Some(sequence_number) = sequence_number {
176            // Note: We don't use `?` here because we want to tolerate
177            // potential db errors due to data corruption.
178            // In that case, we will fallback and construct the contents
179            // from the individual components as if we could not find the
180            // cached full contents.
181            if let Ok(Some(contents)) = self
182                .checkpoint_store
183                .get_full_checkpoint_contents_by_sequence_number(sequence_number)
184                .tap_err(|e| {
185                    error!(
186                        "error getting full checkpoint contents for checkpoint {:?}: {:?}",
187                        sequence_number, e
188                    )
189                })
190            {
191                return Some(contents);
192            }
193        }
194
195        // Otherwise gather it from the individual components.
196        // Note we can't insert the constructed contents into `full_checkpoint_content`,
197        // because it needs to be inserted along with `checkpoint_sequence_by_contents_digest`
198        // and `checkpoint_content`. However at this point it's likely we don't know the
199        // corresponding sequence number yet.
200        self.checkpoint_store
201            .get_checkpoint_contents(digest)
202            .expect("db error")
203            .and_then(|contents| {
204                let mut transactions = Vec::with_capacity(contents.size());
205                for tx in contents.iter() {
206                    if let (Some(t), Some(e)) = (
207                        self.get_transaction(&tx.transaction),
208                        self.cache_traits
209                            .transaction_cache_reader
210                            .get_effects(&tx.effects),
211                    ) {
212                        transactions.push(sui_types::base_types::ExecutionData::new(
213                            (*t).clone().into_inner(),
214                            e,
215                        ))
216                    } else {
217                        return None;
218                    }
219                }
220                Some(
221                    VersionedFullCheckpointContents::from_contents_and_execution_data(
222                        contents,
223                        transactions.into_iter(),
224                    ),
225                )
226            })
227    }
228
229    fn get_committee(&self, epoch: EpochId) -> Option<Arc<Committee>> {
230        self.committee_store.get_committee(&epoch).unwrap()
231    }
232
233    fn get_transaction(&self, digest: &TransactionDigest) -> Option<Arc<VerifiedTransaction>> {
234        self.cache_traits
235            .transaction_cache_reader
236            .get_transaction_block(digest)
237    }
238
239    fn multi_get_transactions(
240        &self,
241        digests: &[TransactionDigest],
242    ) -> Vec<Option<Arc<VerifiedTransaction>>> {
243        self.cache_traits
244            .transaction_cache_reader
245            .multi_get_transaction_blocks(digests)
246    }
247
248    fn get_transaction_effects(&self, digest: &TransactionDigest) -> Option<TransactionEffects> {
249        self.cache_traits
250            .transaction_cache_reader
251            .get_executed_effects(digest)
252    }
253
254    fn multi_get_transaction_effects(
255        &self,
256        digests: &[TransactionDigest],
257    ) -> Vec<Option<TransactionEffects>> {
258        self.cache_traits
259            .transaction_cache_reader
260            .multi_get_executed_effects(digests)
261    }
262
263    fn get_execution_data(&self, digests: &ExecutionDigests) -> Option<ExecutionData> {
264        let reader = &self.cache_traits.transaction_cache_reader;
265        let transaction = reader.get_transaction_block(&digests.transaction)?;
266        let effects = reader.get_effects(&digests.effects)?;
267        Some(ExecutionData::new(
268            (*transaction).clone().into_inner(),
269            effects,
270        ))
271    }
272
273    fn get_events(&self, digest: &TransactionDigest) -> Option<TransactionEvents> {
274        self.cache_traits
275            .transaction_cache_reader
276            .get_events(digest)
277    }
278
279    fn multi_get_events(&self, digests: &[TransactionDigest]) -> Vec<Option<TransactionEvents>> {
280        self.cache_traits
281            .transaction_cache_reader
282            .multi_get_events(digests)
283    }
284
285    fn get_unchanged_loaded_runtime_objects(
286        &self,
287        digest: &TransactionDigest,
288    ) -> Option<Vec<ObjectKey>> {
289        self.cache_traits
290            .transaction_cache_reader
291            .get_unchanged_loaded_runtime_objects(digest)
292    }
293
294    fn multi_get_unchanged_loaded_runtime_objects(
295        &self,
296        digests: &[TransactionDigest],
297    ) -> Vec<Option<Vec<ObjectKey>>> {
298        self.cache_traits
299            .transaction_cache_reader
300            .multi_get_unchanged_loaded_runtime_objects(digests)
301    }
302
303    fn get_transaction_checkpoint(
304        &self,
305        digest: &TransactionDigest,
306    ) -> Option<CheckpointSequenceNumber> {
307        self.cache_traits
308            .checkpoint_cache
309            .deprecated_get_transaction_checkpoint(digest)
310            .map(|(_epoch, checkpoint)| checkpoint)
311    }
312
313    fn get_latest_checkpoint(&self) -> sui_types::storage::error::Result<VerifiedCheckpoint> {
314        self.checkpoint_store
315            .get_highest_executed_checkpoint()
316            .expect("db error")
317            .ok_or_else(|| {
318                sui_types::storage::error::Error::missing("unable to get latest checkpoint")
319            })
320    }
321
322    fn get_checkpoint_contents_by_digest(
323        &self,
324        digest: &CheckpointContentsDigest,
325    ) -> Option<sui_types::messages_checkpoint::CheckpointContents> {
326        self.checkpoint_store
327            .get_checkpoint_contents(digest)
328            .expect("db error")
329    }
330
331    fn get_checkpoint_contents_by_sequence_number(
332        &self,
333        sequence_number: CheckpointSequenceNumber,
334    ) -> Option<sui_types::messages_checkpoint::CheckpointContents> {
335        match self.get_checkpoint_by_sequence_number(sequence_number) {
336            Some(checkpoint) => self.get_checkpoint_contents_by_digest(&checkpoint.content_digest),
337            None => None,
338        }
339    }
340}
341
342impl ObjectStore for RocksDbStore {
343    fn get_object(&self, object_id: &sui_types::base_types::ObjectID) -> Option<Object> {
344        self.cache_traits.object_store.get_object(object_id)
345    }
346
347    fn get_object_by_key(
348        &self,
349        object_id: &sui_types::base_types::ObjectID,
350        version: sui_types::base_types::VersionNumber,
351    ) -> Option<Object> {
352        self.cache_traits
353            .object_store
354            .get_object_by_key(object_id, version)
355    }
356
357    fn multi_get_objects_by_key(&self, object_keys: &[ObjectKey]) -> Vec<Option<Object>> {
358        self.cache_traits
359            .object_cache_reader
360            .multi_get_objects_by_key(object_keys)
361    }
362}
363
364impl WriteStore for RocksDbStore {
365    fn insert_checkpoint(
366        &self,
367        checkpoint: &VerifiedCheckpoint,
368    ) -> Result<(), sui_types::storage::error::Error> {
369        if let Some(EndOfEpochData {
370            next_epoch_committee,
371            ..
372        }) = checkpoint.end_of_epoch_data.as_ref()
373        {
374            let next_committee = next_epoch_committee.iter().cloned().collect();
375            let committee =
376                Committee::new(checkpoint.epoch().checked_add(1).unwrap(), next_committee);
377            self.insert_committee(committee)?;
378        }
379
380        self.checkpoint_store
381            .insert_verified_checkpoint(checkpoint)
382            .map_err(Into::into)
383    }
384
385    fn update_highest_synced_checkpoint(
386        &self,
387        checkpoint: &VerifiedCheckpoint,
388    ) -> Result<(), sui_types::storage::error::Error> {
389        let mut locked = self.highest_synced_checkpoint.lock();
390        if locked.is_some() && locked.unwrap() >= checkpoint.sequence_number {
391            return Ok(());
392        }
393        self.checkpoint_store
394            .update_highest_synced_checkpoint(checkpoint)
395            .map_err(sui_types::storage::error::Error::custom)?;
396        *locked = Some(checkpoint.sequence_number);
397        Ok(())
398    }
399
400    fn update_highest_verified_checkpoint(
401        &self,
402        checkpoint: &VerifiedCheckpoint,
403    ) -> Result<(), sui_types::storage::error::Error> {
404        let mut locked = self.highest_verified_checkpoint.lock();
405        if locked.is_some() && locked.unwrap() >= checkpoint.sequence_number {
406            return Ok(());
407        }
408        self.checkpoint_store
409            .update_highest_verified_checkpoint(checkpoint)
410            .map_err(sui_types::storage::error::Error::custom)?;
411        *locked = Some(checkpoint.sequence_number);
412        Ok(())
413    }
414
415    fn insert_checkpoint_contents(
416        &self,
417        checkpoint: &VerifiedCheckpoint,
418        contents: VerifiedCheckpointContents,
419    ) -> Result<(), sui_types::storage::error::Error> {
420        self.cache_traits
421            .state_sync_store
422            .multi_insert_transaction_and_effects(contents.transactions());
423        self.checkpoint_store
424            .insert_verified_checkpoint_contents(checkpoint, contents)
425            .map_err(Into::into)
426    }
427
428    fn insert_transactions_and_effects(
429        &self,
430        data: &[VerifiedExecutionData],
431    ) -> Result<(), sui_types::storage::error::Error> {
432        self.cache_traits
433            .state_sync_store
434            .multi_insert_transaction_and_effects(data);
435        Ok(())
436    }
437
438    fn insert_checkpoint_contents_digests(
439        &self,
440        checkpoint: &VerifiedCheckpoint,
441        contents: CheckpointContents,
442    ) -> Result<(), sui_types::storage::error::Error> {
443        debug_assert!(
444            contents
445                .iter()
446                .all(|digests| self.get_execution_data(digests).is_some()),
447            "checkpoint {} contents inserted before its transactions and effects",
448            checkpoint.sequence_number()
449        );
450        self.checkpoint_store
451            .insert_checkpoint_contents_digests(checkpoint, &contents)
452            .map_err(Into::into)
453    }
454
455    fn insert_committee(
456        &self,
457        new_committee: Committee,
458    ) -> Result<(), sui_types::storage::error::Error> {
459        self.committee_store
460            .insert_new_committee(&new_committee)
461            .unwrap();
462        Ok(())
463    }
464}
465
466pub struct RestReadStore {
467    state: Arc<AuthorityState>,
468    rocks: RocksDbStore,
469}
470
471impl RestReadStore {
472    pub fn new(state: Arc<AuthorityState>, rocks: RocksDbStore) -> Self {
473        Self { state, rocks }
474    }
475}
476
477impl ObjectStore for RestReadStore {
478    fn get_object(&self, object_id: &sui_types::base_types::ObjectID) -> Option<Object> {
479        self.rocks.get_object(object_id)
480    }
481
482    fn get_object_by_key(
483        &self,
484        object_id: &sui_types::base_types::ObjectID,
485        version: sui_types::base_types::VersionNumber,
486    ) -> Option<Object> {
487        self.rocks.get_object_by_key(object_id, version)
488    }
489
490    fn multi_get_objects_by_key(&self, object_keys: &[ObjectKey]) -> Vec<Option<Object>> {
491        self.rocks.multi_get_objects_by_key(object_keys)
492    }
493}
494
495impl ReadStore for RestReadStore {
496    fn get_committee(&self, epoch: EpochId) -> Option<Arc<Committee>> {
497        self.rocks.get_committee(epoch)
498    }
499
500    fn get_latest_checkpoint(&self) -> sui_types::storage::error::Result<VerifiedCheckpoint> {
501        self.rocks.get_latest_checkpoint()
502    }
503
504    fn get_highest_verified_checkpoint(
505        &self,
506    ) -> sui_types::storage::error::Result<VerifiedCheckpoint> {
507        self.rocks.get_highest_verified_checkpoint()
508    }
509
510    fn get_highest_synced_checkpoint(
511        &self,
512    ) -> sui_types::storage::error::Result<VerifiedCheckpoint> {
513        self.rocks.get_highest_synced_checkpoint()
514    }
515
516    fn get_lowest_available_checkpoint(
517        &self,
518    ) -> sui_types::storage::error::Result<CheckpointSequenceNumber> {
519        self.rocks.get_lowest_available_checkpoint()
520    }
521
522    fn get_checkpoint_by_digest(&self, digest: &CheckpointDigest) -> Option<VerifiedCheckpoint> {
523        self.rocks.get_checkpoint_by_digest(digest)
524    }
525
526    fn get_checkpoint_by_sequence_number(
527        &self,
528        sequence_number: CheckpointSequenceNumber,
529    ) -> Option<VerifiedCheckpoint> {
530        self.rocks
531            .get_checkpoint_by_sequence_number(sequence_number)
532    }
533
534    fn multi_get_checkpoint_by_sequence_number(
535        &self,
536        sequence_numbers: &[CheckpointSequenceNumber],
537    ) -> Vec<Option<VerifiedCheckpoint>> {
538        self.rocks
539            .multi_get_checkpoint_by_sequence_number(sequence_numbers)
540    }
541
542    fn get_checkpoint_contents_by_digest(
543        &self,
544        digest: &CheckpointContentsDigest,
545    ) -> Option<sui_types::messages_checkpoint::CheckpointContents> {
546        self.rocks.get_checkpoint_contents_by_digest(digest)
547    }
548
549    fn get_checkpoint_contents_by_sequence_number(
550        &self,
551        sequence_number: CheckpointSequenceNumber,
552    ) -> Option<sui_types::messages_checkpoint::CheckpointContents> {
553        self.rocks
554            .get_checkpoint_contents_by_sequence_number(sequence_number)
555    }
556
557    fn get_transaction(&self, digest: &TransactionDigest) -> Option<Arc<VerifiedTransaction>> {
558        self.rocks.get_transaction(digest)
559    }
560
561    fn multi_get_transactions(
562        &self,
563        digests: &[TransactionDigest],
564    ) -> Vec<Option<Arc<VerifiedTransaction>>> {
565        self.rocks.multi_get_transactions(digests)
566    }
567
568    fn get_transaction_effects(&self, digest: &TransactionDigest) -> Option<TransactionEffects> {
569        self.rocks.get_transaction_effects(digest)
570    }
571
572    fn multi_get_transaction_effects(
573        &self,
574        digests: &[TransactionDigest],
575    ) -> Vec<Option<TransactionEffects>> {
576        self.rocks.multi_get_transaction_effects(digests)
577    }
578
579    fn get_events(&self, digest: &TransactionDigest) -> Option<TransactionEvents> {
580        self.rocks.get_events(digest)
581    }
582
583    fn multi_get_events(&self, digests: &[TransactionDigest]) -> Vec<Option<TransactionEvents>> {
584        self.rocks.multi_get_events(digests)
585    }
586
587    fn get_full_checkpoint_contents(
588        &self,
589        sequence_number: Option<CheckpointSequenceNumber>,
590        digest: &CheckpointContentsDigest,
591    ) -> Option<VersionedFullCheckpointContents> {
592        self.rocks
593            .get_full_checkpoint_contents(sequence_number, digest)
594    }
595
596    fn get_unchanged_loaded_runtime_objects(
597        &self,
598        digest: &TransactionDigest,
599    ) -> Option<Vec<ObjectKey>> {
600        self.rocks.get_unchanged_loaded_runtime_objects(digest)
601    }
602
603    fn multi_get_unchanged_loaded_runtime_objects(
604        &self,
605        digests: &[TransactionDigest],
606    ) -> Vec<Option<Vec<ObjectKey>>> {
607        self.rocks
608            .multi_get_unchanged_loaded_runtime_objects(digests)
609    }
610
611    fn get_transaction_checkpoint(
612        &self,
613        digest: &TransactionDigest,
614    ) -> Option<CheckpointSequenceNumber> {
615        self.rocks.get_transaction_checkpoint(digest)
616    }
617}
618
619impl BackingPackageStore for RestReadStore {
620    fn get_package_object(&self, _package_id: &ObjectID) -> SuiResult<Option<PackageObject>> {
621        Err(SuiErrorKind::UnsupportedFeatureError {
622            error: "RestReadStore does not support loading package objects".to_string(),
623        }
624        .into())
625    }
626}
627
628impl RuntimeObjectResolver for RestReadStore {
629    fn read_child_object(
630        &self,
631        parent: &ObjectID,
632        child: &ObjectID,
633        child_version_upper_bound: SequenceNumber,
634    ) -> SuiResult<Option<Object>> {
635        Ok(self.get_object(child).filter(|o| {
636            o.version() <= child_version_upper_bound
637                && o.owner == Owner::ObjectOwner((*parent).into())
638        }))
639    }
640
641    fn get_object_received_at_version(
642        &self,
643        _owner: &ObjectID,
644        _receiving_object_id: &ObjectID,
645        _receive_object_at_version: SequenceNumber,
646        _epoch_id: EpochId,
647    ) -> SuiResult<Option<Object>> {
648        Err(SuiErrorKind::UnsupportedFeatureError {
649            error: "RestReadStore does not support receiving objects".to_string(),
650        }
651        .into())
652    }
653}
654
655impl RpcStateReader for RestReadStore {
656    fn get_lowest_available_checkpoint_objects(
657        &self,
658    ) -> sui_types::storage::error::Result<CheckpointSequenceNumber> {
659        Ok(self
660            .state
661            .get_object_cache_reader()
662            .get_highest_pruned_checkpoint()
663            .map(|cp| cp + 1)
664            .unwrap_or(0))
665    }
666
667    fn get_chain_identifier(&self) -> Result<sui_types::digests::ChainIdentifier> {
668        Ok(self.state.get_chain_identifier())
669    }
670
671    fn indexes(&self) -> Option<&dyn RpcIndexes> {
672        // The legacy `rpc-index` backend has been removed; a node serving
673        // reads through `RestReadStore` exposes no index surface. Index
674        // reads are served by the embedded rpc-store via `RpcStoreReadStore`.
675        None
676    }
677
678    fn layout_package_store(&self) -> Box<dyn BackingPackageStore + Send + Sync + '_> {
679        Box::new(self.state.get_backing_package_store().as_ref())
680    }
681}
682
683/// Read store backed by the embedded [`sui_rpc_store`] indexer.
684///
685/// Like [`RestReadStore`] it serves the `sui-rpc-api` trait stack, but it
686/// additionally exposes the index surface (which [`RestReadStore`] no
687/// longer does). This wrapper composes two backends:
688///
689/// - **Raw chain data** — objects, transactions, effects, events,
690///   checkpoints, committees, and child-object resolution — is served
691///   from the validator's perpetual / checkpoint stores
692///   ([`RocksDbStore`]), exactly like [`RestReadStore`]. The embedded
693///   rpc-store does not duplicate this data.
694/// - **The index surface** ([`RpcIndexes`]) — owner / type / balance /
695///   coin / package-version listings, epoch info, and the
696///   ledger-history bitmaps — is served from the
697///   [`RpcStoreReader`].
698///
699/// The object/state available range is the intersection of the two
700/// backends' ranges (`max` of their lower bounds): a consistent read
701/// at checkpoint `C` needs both the object bytes (perpetual store) and
702/// the index rows (rpc-store) at `C`. Ledger-history-specific
703/// availability (bounded by the history backfill watermark) is exposed
704/// separately.
705pub struct RpcStoreReadStore {
706    state: Arc<AuthorityState>,
707    rocks: RocksDbStore,
708    reader: RpcStoreReader,
709}
710
711impl RpcStoreReadStore {
712    pub fn new(state: Arc<AuthorityState>, rocks: RocksDbStore, reader: RpcStoreReader) -> Self {
713        Self {
714            state,
715            rocks,
716            reader,
717        }
718    }
719}
720
721impl ObjectStore for RpcStoreReadStore {
722    fn get_object(&self, object_id: &ObjectID) -> Option<Object> {
723        self.rocks.get_object(object_id)
724    }
725
726    fn get_object_by_key(&self, object_id: &ObjectID, version: SequenceNumber) -> Option<Object> {
727        self.rocks.get_object_by_key(object_id, version)
728    }
729
730    fn multi_get_objects_by_key(&self, object_keys: &[ObjectKey]) -> Vec<Option<Object>> {
731        self.rocks.multi_get_objects_by_key(object_keys)
732    }
733}
734
735impl ReadStore for RpcStoreReadStore {
736    fn get_committee(&self, epoch: EpochId) -> Option<Arc<Committee>> {
737        self.rocks.get_committee(epoch)
738    }
739
740    fn get_latest_checkpoint(&self) -> Result<VerifiedCheckpoint> {
741        let latest = self.rocks.get_latest_checkpoint()?;
742        // Bound the reported tip to what the live-object index has committed.
743        // The embedded indexer follows the tip asynchronously, so without this
744        // the rpc-api could surface a checkpoint -- and the transactions in it
745        // -- whose indexed state (owned objects, balances, coins) is not yet
746        // readable, breaking read-after-write consistency. The history cohort
747        // backfills independently and bounds the ledger-history APIs
748        // separately, so it does not constrain this tip.
749        match self.reader.highest_live_committed_checkpoint()? {
750            Some(indexed) if indexed < latest.sequence_number => self
751                .rocks
752                .get_checkpoint_by_sequence_number(indexed)
753                .ok_or_else(|| {
754                    StorageError::missing(format!(
755                        "live-indexed checkpoint {indexed} missing from the checkpoint store"
756                    ))
757                }),
758            Some(_) => Ok(latest),
759            // Fail closed: no live watermark means the index surface is
760            // empty -- a fresh node whose indexer has not committed its
761            // first checkpoint yet. Reporting the executed tip here would
762            // advertise checkpoints whose indexed state is not readable,
763            // the exact inconsistency the bound above exists to prevent.
764            // The window closes with the live cohort's first commit,
765            // moments after startup.
766            None => Err(StorageError::missing(
767                "the embedded rpc-store's live index has no committed checkpoint yet",
768            )),
769        }
770    }
771
772    fn get_highest_verified_checkpoint(&self) -> Result<VerifiedCheckpoint> {
773        self.rocks.get_highest_verified_checkpoint()
774    }
775
776    fn get_highest_synced_checkpoint(&self) -> Result<VerifiedCheckpoint> {
777        self.rocks.get_highest_synced_checkpoint()
778    }
779
780    fn get_lowest_available_checkpoint(&self) -> Result<CheckpointSequenceNumber> {
781        // A consistent read needs both the raw chain data (perpetual
782        // store) and the index rows (rpc-store), so the available range
783        // starts at the higher of the two lower bounds.
784        let perpetual = self.rocks.get_lowest_available_checkpoint()?;
785        let rpc_store = self.reader.get_lowest_available_checkpoint()?;
786        Ok(perpetual.max(rpc_store))
787    }
788
789    fn get_checkpoint_by_digest(&self, digest: &CheckpointDigest) -> Option<VerifiedCheckpoint> {
790        self.rocks.get_checkpoint_by_digest(digest)
791    }
792
793    fn get_checkpoint_by_sequence_number(
794        &self,
795        sequence_number: CheckpointSequenceNumber,
796    ) -> Option<VerifiedCheckpoint> {
797        self.rocks
798            .get_checkpoint_by_sequence_number(sequence_number)
799    }
800
801    fn multi_get_checkpoint_by_sequence_number(
802        &self,
803        sequence_numbers: &[CheckpointSequenceNumber],
804    ) -> Vec<Option<VerifiedCheckpoint>> {
805        self.rocks
806            .multi_get_checkpoint_by_sequence_number(sequence_numbers)
807    }
808
809    fn get_checkpoint_contents_by_digest(
810        &self,
811        digest: &CheckpointContentsDigest,
812    ) -> Option<sui_types::messages_checkpoint::CheckpointContents> {
813        self.rocks.get_checkpoint_contents_by_digest(digest)
814    }
815
816    fn get_checkpoint_contents_by_sequence_number(
817        &self,
818        sequence_number: CheckpointSequenceNumber,
819    ) -> Option<sui_types::messages_checkpoint::CheckpointContents> {
820        self.rocks
821            .get_checkpoint_contents_by_sequence_number(sequence_number)
822    }
823
824    fn get_transaction(&self, digest: &TransactionDigest) -> Option<Arc<VerifiedTransaction>> {
825        self.rocks.get_transaction(digest)
826    }
827
828    fn multi_get_transactions(
829        &self,
830        digests: &[TransactionDigest],
831    ) -> Vec<Option<Arc<VerifiedTransaction>>> {
832        self.rocks.multi_get_transactions(digests)
833    }
834
835    fn get_transaction_effects(&self, digest: &TransactionDigest) -> Option<TransactionEffects> {
836        self.rocks.get_transaction_effects(digest)
837    }
838
839    fn multi_get_transaction_effects(
840        &self,
841        digests: &[TransactionDigest],
842    ) -> Vec<Option<TransactionEffects>> {
843        self.rocks.multi_get_transaction_effects(digests)
844    }
845
846    fn get_events(&self, digest: &TransactionDigest) -> Option<TransactionEvents> {
847        self.rocks.get_events(digest)
848    }
849
850    fn multi_get_events(&self, digests: &[TransactionDigest]) -> Vec<Option<TransactionEvents>> {
851        self.rocks.multi_get_events(digests)
852    }
853
854    fn get_full_checkpoint_contents(
855        &self,
856        sequence_number: Option<CheckpointSequenceNumber>,
857        digest: &CheckpointContentsDigest,
858    ) -> Option<VersionedFullCheckpointContents> {
859        self.rocks
860            .get_full_checkpoint_contents(sequence_number, digest)
861    }
862
863    fn get_unchanged_loaded_runtime_objects(
864        &self,
865        digest: &TransactionDigest,
866    ) -> Option<Vec<ObjectKey>> {
867        self.rocks.get_unchanged_loaded_runtime_objects(digest)
868    }
869
870    fn multi_get_unchanged_loaded_runtime_objects(
871        &self,
872        digests: &[TransactionDigest],
873    ) -> Vec<Option<Vec<ObjectKey>>> {
874        self.rocks
875            .multi_get_unchanged_loaded_runtime_objects(digests)
876    }
877
878    fn get_transaction_checkpoint(
879        &self,
880        digest: &TransactionDigest,
881    ) -> Option<CheckpointSequenceNumber> {
882        self.rocks.get_transaction_checkpoint(digest)
883    }
884}
885
886impl BackingPackageStore for RpcStoreReadStore {
887    fn get_package_object(&self, _package_id: &ObjectID) -> SuiResult<Option<PackageObject>> {
888        Err(SuiErrorKind::UnsupportedFeatureError {
889            error: "RpcStoreReadStore does not support loading package objects".to_string(),
890        }
891        .into())
892    }
893}
894
895impl RuntimeObjectResolver for RpcStoreReadStore {
896    fn read_child_object(
897        &self,
898        parent: &ObjectID,
899        child: &ObjectID,
900        child_version_upper_bound: SequenceNumber,
901    ) -> SuiResult<Option<Object>> {
902        Ok(self.get_object(child).filter(|o| {
903            o.version() <= child_version_upper_bound
904                && o.owner == Owner::ObjectOwner((*parent).into())
905        }))
906    }
907
908    fn get_object_received_at_version(
909        &self,
910        _owner: &ObjectID,
911        _receiving_object_id: &ObjectID,
912        _receive_object_at_version: SequenceNumber,
913        _epoch_id: EpochId,
914    ) -> SuiResult<Option<Object>> {
915        Err(SuiErrorKind::UnsupportedFeatureError {
916            error: "RpcStoreReadStore does not support receiving objects".to_string(),
917        }
918        .into())
919    }
920}
921
922impl RpcStateReader for RpcStoreReadStore {
923    fn get_lowest_available_checkpoint_objects(&self) -> Result<CheckpointSequenceNumber> {
924        let perpetual = self
925            .state
926            .get_object_cache_reader()
927            .get_highest_pruned_checkpoint()
928            .map(|cp| cp + 1)
929            .unwrap_or(0);
930        let rpc_store = self.reader.get_lowest_available_checkpoint_objects()?;
931        Ok(perpetual.max(rpc_store))
932    }
933
934    fn get_chain_identifier(&self) -> Result<sui_types::digests::ChainIdentifier> {
935        Ok(self.state.get_chain_identifier())
936    }
937
938    fn indexes(&self) -> Option<&dyn RpcIndexes> {
939        Some(self)
940    }
941
942    fn get_highest_executed_checkpoint_seq_number(&self) -> Result<CheckpointSequenceNumber> {
943        // The raw executed tip, read straight from the checkpoint store. Unlike
944        // `get_latest_checkpoint`, this is not bounded to the live-object index
945        // frontier -- the health check measures how far that frontier trails
946        // the executed tip, so it must see the unbounded value.
947        Ok(*self.rocks.get_latest_checkpoint()?.sequence_number())
948    }
949
950    fn layout_package_store(&self) -> Box<dyn BackingPackageStore + Send + Sync + '_> {
951        Box::new(self.state.get_backing_package_store().as_ref())
952    }
953}
954
955impl RpcIndexes for RpcStoreReadStore {
956    fn get_epoch_info(&self, epoch: EpochId) -> Result<Option<sui_types::storage::EpochInfo>> {
957        self.reader.get_epoch_info(epoch)
958    }
959
960    fn owned_objects_iter(
961        &self,
962        owner: SuiAddress,
963        object_type: Option<StructTag>,
964        cursor: Option<OwnedObjectInfo>,
965    ) -> Result<Box<dyn Iterator<Item = Result<OwnedObjectInfo, TypedStoreError>> + '_>> {
966        self.reader.owned_objects_iter(owner, object_type, cursor)
967    }
968
969    fn dynamic_field_iter(
970        &self,
971        parent: ObjectID,
972        cursor: Option<DynamicFieldKey>,
973    ) -> Result<Box<dyn Iterator<Item = Result<DynamicFieldKey, TypedStoreError>> + '_>> {
974        self.reader.dynamic_field_iter(parent, cursor)
975    }
976
977    fn get_coin_info(&self, coin_type: &StructTag) -> Result<Option<CoinInfo>> {
978        self.reader.get_coin_info(coin_type)
979    }
980
981    fn get_balance(
982        &self,
983        owner: &SuiAddress,
984        coin_type: &StructTag,
985    ) -> Result<Option<BalanceInfo>> {
986        self.reader.get_balance(owner, coin_type)
987    }
988
989    fn balance_iter(
990        &self,
991        owner: &SuiAddress,
992        cursor: Option<(SuiAddress, StructTag)>,
993    ) -> Result<BalanceIterator<'_>> {
994        self.reader.balance_iter(owner, cursor)
995    }
996
997    fn package_versions_iter(
998        &self,
999        original_id: ObjectID,
1000        cursor: Option<u64>,
1001    ) -> Result<Box<dyn Iterator<Item = Result<(u64, ObjectID), TypedStoreError>> + '_>> {
1002        self.reader.package_versions_iter(original_id, cursor)
1003    }
1004
1005    fn get_package_version_storage_id(
1006        &self,
1007        original_id: ObjectID,
1008        version: u64,
1009    ) -> Result<Option<ObjectID>> {
1010        self.reader
1011            .get_package_version_storage_id(original_id, version)
1012    }
1013
1014    fn get_package_at_checkpoint(
1015        &self,
1016        original_id: ObjectID,
1017        checkpoint: CheckpointSequenceNumber,
1018    ) -> Result<Option<(u64, ObjectID)>> {
1019        self.reader
1020            .get_package_at_checkpoint(original_id, checkpoint)
1021    }
1022
1023    fn get_highest_indexed_checkpoint_seq_number(
1024        &self,
1025    ) -> Result<Option<CheckpointSequenceNumber>> {
1026        self.reader.get_highest_indexed_checkpoint_seq_number()
1027    }
1028
1029    fn get_highest_live_indexed_checkpoint_seq_number(
1030        &self,
1031    ) -> Result<Option<CheckpointSequenceNumber>> {
1032        self.reader.get_highest_live_indexed_checkpoint_seq_number()
1033    }
1034
1035    fn ledger_tx_seq_digest(&self, tx_seq: u64) -> Result<Option<LedgerTxSeqDigest>> {
1036        self.reader.ledger_tx_seq_digest(tx_seq)
1037    }
1038
1039    fn ledger_tx_seq_digest_multi_get(
1040        &self,
1041        tx_seqs: &[u64],
1042    ) -> Result<Vec<Option<LedgerTxSeqDigest>>> {
1043        self.reader.ledger_tx_seq_digest_multi_get(tx_seqs)
1044    }
1045
1046    fn ledger_tx_seq_digest_iter(
1047        &self,
1048        start: u64,
1049        end_exclusive: u64,
1050        descending: bool,
1051    ) -> Result<LedgerTxSeqDigestIterator<'_>> {
1052        self.reader
1053            .ledger_tx_seq_digest_iter(start, end_exclusive, descending)
1054    }
1055
1056    fn transaction_bitmap_bucket_iter(
1057        &self,
1058        dimension_key: Vec<u8>,
1059        start_bucket: u64,
1060        end_bucket_exclusive: u64,
1061        descending: bool,
1062    ) -> Result<LedgerBitmapBucketIterator<'_>> {
1063        self.reader.transaction_bitmap_bucket_iter(
1064            dimension_key,
1065            start_bucket,
1066            end_bucket_exclusive,
1067            descending,
1068        )
1069    }
1070
1071    fn event_bitmap_bucket_iter(
1072        &self,
1073        dimension_key: Vec<u8>,
1074        start_bucket: u64,
1075        end_bucket_exclusive: u64,
1076        descending: bool,
1077    ) -> Result<LedgerBitmapBucketIterator<'_>> {
1078        self.reader.event_bitmap_bucket_iter(
1079            dimension_key,
1080            start_bucket,
1081            end_bucket_exclusive,
1082            descending,
1083        )
1084    }
1085}