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 crate::rpc_index::CoinIndexInfo;
9use crate::rpc_index::OwnerIndexInfo;
10use crate::rpc_index::OwnerIndexKey;
11use crate::rpc_index::RpcIndexStore;
12use move_core_types::language_storage::StructTag;
13use parking_lot::Mutex;
14use std::sync::Arc;
15use sui_types::base_types::ObjectID;
16use sui_types::base_types::SequenceNumber;
17use sui_types::base_types::SuiAddress;
18use sui_types::base_types::TransactionDigest;
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::full_checkpoint_content::ObjectSet;
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::ChildObjectResolver;
36use sui_types::storage::CoinInfo;
37use sui_types::storage::DynamicFieldKey;
38use sui_types::storage::LedgerBitmapBucketIterator;
39use sui_types::storage::LedgerTxSeqDigest;
40use sui_types::storage::LedgerTxSeqDigestIterator;
41use sui_types::storage::ObjectStore;
42use sui_types::storage::OwnedObjectInfo;
43use sui_types::storage::RpcIndexes;
44use sui_types::storage::RpcStateReader;
45use sui_types::storage::WriteStore;
46use sui_types::storage::error::Error as StorageError;
47use sui_types::storage::error::Result;
48use sui_types::storage::{ObjectKey, OverlayBackingPackageStore, ReadStore};
49use sui_types::transaction::VerifiedTransaction;
50use tap::Pipe;
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 get_highest_verified_checkpoint(&self) -> Result<VerifiedCheckpoint, StorageError> {
111        self.checkpoint_store
112            .get_highest_verified_checkpoint()
113            .map(|maybe_checkpoint| {
114                maybe_checkpoint
115                    .expect("storage should have been initialized with genesis checkpoint")
116            })
117            .map_err(Into::into)
118    }
119
120    fn get_highest_synced_checkpoint(&self) -> Result<VerifiedCheckpoint, StorageError> {
121        self.checkpoint_store
122            .get_highest_synced_checkpoint()
123            .map(|maybe_checkpoint| {
124                maybe_checkpoint
125                    .expect("storage should have been initialized with genesis checkpoint")
126            })
127            .map_err(Into::into)
128    }
129
130    fn get_lowest_available_checkpoint(&self) -> Result<CheckpointSequenceNumber, StorageError> {
131        if let Some(highest_pruned_cp) = self
132            .checkpoint_store
133            .get_highest_pruned_checkpoint_seq_number()
134            .map_err(Into::<StorageError>::into)?
135        {
136            Ok(highest_pruned_cp + 1)
137        } else {
138            Ok(0)
139        }
140    }
141
142    fn get_full_checkpoint_contents(
143        &self,
144        sequence_number: Option<CheckpointSequenceNumber>,
145        digest: &CheckpointContentsDigest,
146    ) -> Option<VersionedFullCheckpointContents> {
147        #[cfg(debug_assertions)]
148        if let Some(sequence_number) = sequence_number {
149            // When sequence_number is provided as an optimization, we want to ensure that
150            // the sequence number we get from the db matches the one we provided.
151            // Only check this in debug mode though.
152            if let Some(loaded_sequence_number) = self
153                .checkpoint_store
154                .get_sequence_number_by_contents_digest(digest)
155                .expect("db error")
156            {
157                assert_eq!(loaded_sequence_number, sequence_number);
158            }
159        }
160
161        let sequence_number = sequence_number.or_else(|| {
162            self.checkpoint_store
163                .get_sequence_number_by_contents_digest(digest)
164                .expect("db error")
165        });
166        if let Some(sequence_number) = sequence_number {
167            // Note: We don't use `?` here because we want to tolerate
168            // potential db errors due to data corruption.
169            // In that case, we will fallback and construct the contents
170            // from the individual components as if we could not find the
171            // cached full contents.
172            if let Ok(Some(contents)) = self
173                .checkpoint_store
174                .get_full_checkpoint_contents_by_sequence_number(sequence_number)
175                .tap_err(|e| {
176                    error!(
177                        "error getting full checkpoint contents for checkpoint {:?}: {:?}",
178                        sequence_number, e
179                    )
180                })
181            {
182                return Some(contents);
183            }
184        }
185
186        // Otherwise gather it from the individual components.
187        // Note we can't insert the constructed contents into `full_checkpoint_content`,
188        // because it needs to be inserted along with `checkpoint_sequence_by_contents_digest`
189        // and `checkpoint_content`. However at this point it's likely we don't know the
190        // corresponding sequence number yet.
191        self.checkpoint_store
192            .get_checkpoint_contents(digest)
193            .expect("db error")
194            .and_then(|contents| {
195                let mut transactions = Vec::with_capacity(contents.size());
196                for tx in contents.iter() {
197                    if let (Some(t), Some(e)) = (
198                        self.get_transaction(&tx.transaction),
199                        self.cache_traits
200                            .transaction_cache_reader
201                            .get_effects(&tx.effects),
202                    ) {
203                        transactions.push(sui_types::base_types::ExecutionData::new(
204                            (*t).clone().into_inner(),
205                            e,
206                        ))
207                    } else {
208                        return None;
209                    }
210                }
211                Some(
212                    VersionedFullCheckpointContents::from_contents_and_execution_data(
213                        contents,
214                        transactions.into_iter(),
215                    ),
216                )
217            })
218    }
219
220    fn get_committee(&self, epoch: EpochId) -> Option<Arc<Committee>> {
221        self.committee_store.get_committee(&epoch).unwrap()
222    }
223
224    fn get_transaction(&self, digest: &TransactionDigest) -> Option<Arc<VerifiedTransaction>> {
225        self.cache_traits
226            .transaction_cache_reader
227            .get_transaction_block(digest)
228    }
229
230    fn multi_get_transactions(
231        &self,
232        digests: &[TransactionDigest],
233    ) -> Vec<Option<Arc<VerifiedTransaction>>> {
234        self.cache_traits
235            .transaction_cache_reader
236            .multi_get_transaction_blocks(digests)
237    }
238
239    fn get_transaction_effects(&self, digest: &TransactionDigest) -> Option<TransactionEffects> {
240        self.cache_traits
241            .transaction_cache_reader
242            .get_executed_effects(digest)
243    }
244
245    fn multi_get_transaction_effects(
246        &self,
247        digests: &[TransactionDigest],
248    ) -> Vec<Option<TransactionEffects>> {
249        self.cache_traits
250            .transaction_cache_reader
251            .multi_get_executed_effects(digests)
252    }
253
254    fn get_events(&self, digest: &TransactionDigest) -> Option<TransactionEvents> {
255        self.cache_traits
256            .transaction_cache_reader
257            .get_events(digest)
258    }
259
260    fn multi_get_events(&self, digests: &[TransactionDigest]) -> Vec<Option<TransactionEvents>> {
261        self.cache_traits
262            .transaction_cache_reader
263            .multi_get_events(digests)
264    }
265
266    fn get_unchanged_loaded_runtime_objects(
267        &self,
268        digest: &TransactionDigest,
269    ) -> Option<Vec<ObjectKey>> {
270        self.cache_traits
271            .transaction_cache_reader
272            .get_unchanged_loaded_runtime_objects(digest)
273    }
274
275    fn get_transaction_checkpoint(
276        &self,
277        digest: &TransactionDigest,
278    ) -> Option<CheckpointSequenceNumber> {
279        self.cache_traits
280            .checkpoint_cache
281            .deprecated_get_transaction_checkpoint(digest)
282            .map(|(_epoch, checkpoint)| checkpoint)
283    }
284
285    fn get_latest_checkpoint(&self) -> sui_types::storage::error::Result<VerifiedCheckpoint> {
286        self.checkpoint_store
287            .get_highest_executed_checkpoint()
288            .expect("db error")
289            .ok_or_else(|| {
290                sui_types::storage::error::Error::missing("unable to get latest checkpoint")
291            })
292    }
293
294    fn get_checkpoint_contents_by_digest(
295        &self,
296        digest: &CheckpointContentsDigest,
297    ) -> Option<sui_types::messages_checkpoint::CheckpointContents> {
298        self.checkpoint_store
299            .get_checkpoint_contents(digest)
300            .expect("db error")
301    }
302
303    fn get_checkpoint_contents_by_sequence_number(
304        &self,
305        sequence_number: CheckpointSequenceNumber,
306    ) -> Option<sui_types::messages_checkpoint::CheckpointContents> {
307        match self.get_checkpoint_by_sequence_number(sequence_number) {
308            Some(checkpoint) => self.get_checkpoint_contents_by_digest(&checkpoint.content_digest),
309            None => None,
310        }
311    }
312}
313
314impl ObjectStore for RocksDbStore {
315    fn get_object(&self, object_id: &sui_types::base_types::ObjectID) -> Option<Object> {
316        self.cache_traits.object_store.get_object(object_id)
317    }
318
319    fn get_object_by_key(
320        &self,
321        object_id: &sui_types::base_types::ObjectID,
322        version: sui_types::base_types::VersionNumber,
323    ) -> Option<Object> {
324        self.cache_traits
325            .object_store
326            .get_object_by_key(object_id, version)
327    }
328}
329
330impl WriteStore for RocksDbStore {
331    fn insert_checkpoint(
332        &self,
333        checkpoint: &VerifiedCheckpoint,
334    ) -> Result<(), sui_types::storage::error::Error> {
335        if let Some(EndOfEpochData {
336            next_epoch_committee,
337            ..
338        }) = checkpoint.end_of_epoch_data.as_ref()
339        {
340            let next_committee = next_epoch_committee.iter().cloned().collect();
341            let committee =
342                Committee::new(checkpoint.epoch().checked_add(1).unwrap(), next_committee);
343            self.insert_committee(committee)?;
344        }
345
346        self.checkpoint_store
347            .insert_verified_checkpoint(checkpoint)
348            .map_err(Into::into)
349    }
350
351    fn update_highest_synced_checkpoint(
352        &self,
353        checkpoint: &VerifiedCheckpoint,
354    ) -> Result<(), sui_types::storage::error::Error> {
355        let mut locked = self.highest_synced_checkpoint.lock();
356        if locked.is_some() && locked.unwrap() >= checkpoint.sequence_number {
357            return Ok(());
358        }
359        self.checkpoint_store
360            .update_highest_synced_checkpoint(checkpoint)
361            .map_err(sui_types::storage::error::Error::custom)?;
362        *locked = Some(checkpoint.sequence_number);
363        Ok(())
364    }
365
366    fn update_highest_verified_checkpoint(
367        &self,
368        checkpoint: &VerifiedCheckpoint,
369    ) -> Result<(), sui_types::storage::error::Error> {
370        let mut locked = self.highest_verified_checkpoint.lock();
371        if locked.is_some() && locked.unwrap() >= checkpoint.sequence_number {
372            return Ok(());
373        }
374        self.checkpoint_store
375            .update_highest_verified_checkpoint(checkpoint)
376            .map_err(sui_types::storage::error::Error::custom)?;
377        *locked = Some(checkpoint.sequence_number);
378        Ok(())
379    }
380
381    fn insert_checkpoint_contents(
382        &self,
383        checkpoint: &VerifiedCheckpoint,
384        contents: VerifiedCheckpointContents,
385    ) -> Result<(), sui_types::storage::error::Error> {
386        self.cache_traits
387            .state_sync_store
388            .multi_insert_transaction_and_effects(contents.transactions());
389        self.checkpoint_store
390            .insert_verified_checkpoint_contents(checkpoint, contents)
391            .map_err(Into::into)
392    }
393
394    fn insert_committee(
395        &self,
396        new_committee: Committee,
397    ) -> Result<(), sui_types::storage::error::Error> {
398        self.committee_store
399            .insert_new_committee(&new_committee)
400            .unwrap();
401        Ok(())
402    }
403}
404
405pub struct RestReadStore {
406    state: Arc<AuthorityState>,
407    rocks: RocksDbStore,
408}
409
410impl RestReadStore {
411    pub fn new(state: Arc<AuthorityState>, rocks: RocksDbStore) -> Self {
412        Self { state, rocks }
413    }
414
415    fn index(&self) -> sui_types::storage::error::Result<&RpcIndexStore> {
416        self.state
417            .rpc_index
418            .as_deref()
419            .ok_or_else(|| sui_types::storage::error::Error::custom("rest index store is disabled"))
420    }
421}
422
423impl ObjectStore for RestReadStore {
424    fn get_object(&self, object_id: &sui_types::base_types::ObjectID) -> Option<Object> {
425        self.rocks.get_object(object_id)
426    }
427
428    fn get_object_by_key(
429        &self,
430        object_id: &sui_types::base_types::ObjectID,
431        version: sui_types::base_types::VersionNumber,
432    ) -> Option<Object> {
433        self.rocks.get_object_by_key(object_id, version)
434    }
435}
436
437impl ReadStore for RestReadStore {
438    fn get_committee(&self, epoch: EpochId) -> Option<Arc<Committee>> {
439        self.rocks.get_committee(epoch)
440    }
441
442    fn get_latest_checkpoint(&self) -> sui_types::storage::error::Result<VerifiedCheckpoint> {
443        self.rocks.get_latest_checkpoint()
444    }
445
446    fn get_highest_verified_checkpoint(
447        &self,
448    ) -> sui_types::storage::error::Result<VerifiedCheckpoint> {
449        self.rocks.get_highest_verified_checkpoint()
450    }
451
452    fn get_highest_synced_checkpoint(
453        &self,
454    ) -> sui_types::storage::error::Result<VerifiedCheckpoint> {
455        self.rocks.get_highest_synced_checkpoint()
456    }
457
458    fn get_lowest_available_checkpoint(
459        &self,
460    ) -> sui_types::storage::error::Result<CheckpointSequenceNumber> {
461        self.rocks.get_lowest_available_checkpoint()
462    }
463
464    fn get_checkpoint_by_digest(&self, digest: &CheckpointDigest) -> Option<VerifiedCheckpoint> {
465        self.rocks.get_checkpoint_by_digest(digest)
466    }
467
468    fn get_checkpoint_by_sequence_number(
469        &self,
470        sequence_number: CheckpointSequenceNumber,
471    ) -> Option<VerifiedCheckpoint> {
472        self.rocks
473            .get_checkpoint_by_sequence_number(sequence_number)
474    }
475
476    fn get_checkpoint_contents_by_digest(
477        &self,
478        digest: &CheckpointContentsDigest,
479    ) -> Option<sui_types::messages_checkpoint::CheckpointContents> {
480        self.rocks.get_checkpoint_contents_by_digest(digest)
481    }
482
483    fn get_checkpoint_contents_by_sequence_number(
484        &self,
485        sequence_number: CheckpointSequenceNumber,
486    ) -> Option<sui_types::messages_checkpoint::CheckpointContents> {
487        self.rocks
488            .get_checkpoint_contents_by_sequence_number(sequence_number)
489    }
490
491    fn get_transaction(&self, digest: &TransactionDigest) -> Option<Arc<VerifiedTransaction>> {
492        self.rocks.get_transaction(digest)
493    }
494
495    fn multi_get_transactions(
496        &self,
497        digests: &[TransactionDigest],
498    ) -> Vec<Option<Arc<VerifiedTransaction>>> {
499        self.rocks.multi_get_transactions(digests)
500    }
501
502    fn get_transaction_effects(&self, digest: &TransactionDigest) -> Option<TransactionEffects> {
503        self.rocks.get_transaction_effects(digest)
504    }
505
506    fn multi_get_transaction_effects(
507        &self,
508        digests: &[TransactionDigest],
509    ) -> Vec<Option<TransactionEffects>> {
510        self.rocks.multi_get_transaction_effects(digests)
511    }
512
513    fn get_events(&self, digest: &TransactionDigest) -> Option<TransactionEvents> {
514        self.rocks.get_events(digest)
515    }
516
517    fn multi_get_events(&self, digests: &[TransactionDigest]) -> Vec<Option<TransactionEvents>> {
518        self.rocks.multi_get_events(digests)
519    }
520
521    fn get_full_checkpoint_contents(
522        &self,
523        sequence_number: Option<CheckpointSequenceNumber>,
524        digest: &CheckpointContentsDigest,
525    ) -> Option<VersionedFullCheckpointContents> {
526        self.rocks
527            .get_full_checkpoint_contents(sequence_number, digest)
528    }
529
530    fn get_unchanged_loaded_runtime_objects(
531        &self,
532        digest: &TransactionDigest,
533    ) -> Option<Vec<ObjectKey>> {
534        self.rocks.get_unchanged_loaded_runtime_objects(digest)
535    }
536
537    fn get_transaction_checkpoint(
538        &self,
539        digest: &TransactionDigest,
540    ) -> Option<CheckpointSequenceNumber> {
541        self.rocks.get_transaction_checkpoint(digest)
542    }
543}
544
545impl ChildObjectResolver for RestReadStore {
546    fn read_child_object(
547        &self,
548        parent: &ObjectID,
549        child: &ObjectID,
550        child_version_upper_bound: SequenceNumber,
551    ) -> SuiResult<Option<Object>> {
552        Ok(self.get_object(child).and_then(|o| {
553            if o.version() <= child_version_upper_bound
554                && o.owner == Owner::ObjectOwner((*parent).into())
555            {
556                Some(o)
557            } else {
558                None
559            }
560        }))
561    }
562
563    fn get_object_received_at_version(
564        &self,
565        _owner: &ObjectID,
566        _receiving_object_id: &ObjectID,
567        _receive_object_at_version: SequenceNumber,
568        _epoch_id: EpochId,
569    ) -> SuiResult<Option<Object>> {
570        Err(SuiErrorKind::UnsupportedFeatureError {
571            error: "RestReadStore does not support receiving objects".to_string(),
572        }
573        .into())
574    }
575}
576
577impl RpcStateReader for RestReadStore {
578    fn get_lowest_available_checkpoint_objects(
579        &self,
580    ) -> sui_types::storage::error::Result<CheckpointSequenceNumber> {
581        Ok(self
582            .state
583            .get_object_cache_reader()
584            .get_highest_pruned_checkpoint()
585            .map(|cp| cp + 1)
586            .unwrap_or(0))
587    }
588
589    fn get_chain_identifier(&self) -> Result<sui_types::digests::ChainIdentifier> {
590        Ok(self.state.get_chain_identifier())
591    }
592
593    fn indexes(&self) -> Option<&dyn RpcIndexes> {
594        Some(self)
595    }
596
597    fn get_struct_layout_with_overlay(
598        &self,
599        struct_tag: &move_core_types::language_storage::StructTag,
600        overlay: &ObjectSet,
601    ) -> Result<Option<move_core_types::annotated_value::MoveTypeLayout>> {
602        let backing_store = self.state.get_backing_package_store();
603        let overlay_store = OverlayBackingPackageStore::new(overlay, backing_store.as_ref());
604        let epoch_store = self.state.load_epoch_store_one_call_per_task();
605        epoch_store
606            .executor()
607            // TODO(cache) - must read through cache
608            .type_layout_resolver(epoch_store.protocol_config(), Box::new(overlay_store))
609            .get_annotated_layout(struct_tag)
610            .map(|layout| layout.into_layout())
611            .map(Some)
612            .map_err(StorageError::custom)
613    }
614}
615
616impl RpcIndexes for RestReadStore {
617    fn get_epoch_info(&self, epoch: EpochId) -> Result<Option<sui_types::storage::EpochInfo>> {
618        self.index()?
619            .get_epoch_info(epoch)
620            .map_err(StorageError::custom)
621    }
622
623    fn owned_objects_iter(
624        &self,
625        owner: SuiAddress,
626        object_type: Option<StructTag>,
627        cursor: Option<OwnedObjectInfo>,
628    ) -> Result<Box<dyn Iterator<Item = Result<OwnedObjectInfo, TypedStoreError>> + '_>> {
629        let cursor = cursor.map(|cursor| OwnerIndexKey {
630            owner: cursor.owner,
631            object_type: cursor.object_type,
632            inverted_balance: cursor.balance.map(std::ops::Not::not),
633            object_id: cursor.object_id,
634        });
635
636        let iter = self
637            .index()?
638            .owner_iter(owner, object_type, cursor)?
639            .map(|result| {
640                result.map(
641                    |(
642                        OwnerIndexKey {
643                            owner,
644                            object_id,
645                            object_type,
646                            inverted_balance,
647                        },
648                        OwnerIndexInfo { version },
649                    )| {
650                        OwnedObjectInfo {
651                            owner,
652                            object_type,
653                            balance: inverted_balance.map(std::ops::Not::not),
654                            object_id,
655                            version,
656                        }
657                    },
658                )
659            });
660
661        Ok(Box::new(iter) as _)
662    }
663
664    fn dynamic_field_iter(
665        &self,
666        parent: ObjectID,
667        cursor: Option<ObjectID>,
668    ) -> sui_types::storage::error::Result<
669        Box<dyn Iterator<Item = Result<DynamicFieldKey, TypedStoreError>> + '_>,
670    > {
671        let iter = self.index()?.dynamic_field_iter(parent, cursor)?;
672        Ok(Box::new(iter) as _)
673    }
674
675    fn get_coin_info(
676        &self,
677        coin_type: &StructTag,
678    ) -> sui_types::storage::error::Result<Option<CoinInfo>> {
679        self.index()?
680            .get_coin_info(coin_type)?
681            .map(
682                |CoinIndexInfo {
683                     coin_metadata_object_id,
684                     treasury_object_id,
685                     regulated_coin_metadata_object_id,
686                 }| CoinInfo {
687                    coin_metadata_object_id,
688                    treasury_object_id,
689                    regulated_coin_metadata_object_id,
690                },
691            )
692            .pipe(Ok)
693    }
694
695    fn get_balance(
696        &self,
697        owner: &SuiAddress,
698        coin_type: &StructTag,
699    ) -> sui_types::storage::error::Result<Option<BalanceInfo>> {
700        self.index()?
701            .get_balance(owner, coin_type)?
702            .map(|info| info.into())
703            .pipe(Ok)
704    }
705
706    fn balance_iter(
707        &self,
708        owner: &SuiAddress,
709        cursor: Option<(SuiAddress, StructTag)>,
710    ) -> sui_types::storage::error::Result<BalanceIterator<'_>> {
711        let cursor_key =
712            cursor.map(|(owner, coin_type)| crate::rpc_index::BalanceKey { owner, coin_type });
713
714        Ok(Box::new(
715            self.index()?
716                .balance_iter(*owner, cursor_key)?
717                .map(|result| {
718                    result
719                        .map(|(key, info)| (key.coin_type, info.into()))
720                        .map_err(Into::into)
721                }),
722        ))
723    }
724
725    fn package_versions_iter(
726        &self,
727        original_id: ObjectID,
728        cursor: Option<u64>,
729    ) -> sui_types::storage::error::Result<
730        Box<dyn Iterator<Item = Result<(u64, ObjectID), TypedStoreError>> + '_>,
731    > {
732        let iter = self.index()?.package_versions_iter(original_id, cursor)?;
733        Ok(
734            Box::new(iter.map(|result| result.map(|(key, info)| (key.version, info.storage_id))))
735                as _,
736        )
737    }
738
739    fn get_highest_indexed_checkpoint_seq_number(
740        &self,
741    ) -> sui_types::storage::error::Result<Option<CheckpointSequenceNumber>> {
742        self.index()?
743            .get_highest_indexed_checkpoint_seq_number()
744            .map_err(Into::into)
745    }
746
747    fn ledger_tx_seq_digest(&self, tx_seq: u64) -> Result<Option<LedgerTxSeqDigest>> {
748        self.index()?
749            .ledger_tx_seq_digest(tx_seq)
750            .map_err(Into::into)
751    }
752
753    fn ledger_tx_seq_digest_multi_get(
754        &self,
755        tx_seqs: &[u64],
756    ) -> Result<Vec<Option<LedgerTxSeqDigest>>> {
757        self.index()?
758            .ledger_tx_seq_digest_multi_get(tx_seqs)
759            .map_err(Into::into)
760    }
761
762    fn ledger_tx_seq_digest_iter(
763        &self,
764        start: u64,
765        end_exclusive: u64,
766        descending: bool,
767    ) -> Result<LedgerTxSeqDigestIterator<'_>> {
768        self.index()?
769            .ledger_tx_seq_digest_iter(start, end_exclusive, descending)
770            .map_err(Into::into)
771    }
772
773    fn transaction_bitmap_bucket_iter(
774        &self,
775        dimension_key: Vec<u8>,
776        start_bucket: u64,
777        end_bucket_exclusive: u64,
778        descending: bool,
779    ) -> Result<LedgerBitmapBucketIterator<'_>> {
780        self.index()?
781            .transaction_bitmap_bucket_iter(
782                dimension_key,
783                start_bucket,
784                end_bucket_exclusive,
785                descending,
786            )
787            .map_err(Into::into)
788    }
789
790    fn event_bitmap_bucket_iter(
791        &self,
792        dimension_key: Vec<u8>,
793        start_bucket: u64,
794        end_bucket_exclusive: u64,
795        descending: bool,
796    ) -> Result<LedgerBitmapBucketIterator<'_>> {
797        self.index()?
798            .event_bitmap_bucket_iter(
799                dimension_key,
800                start_bucket,
801                end_bucket_exclusive,
802                descending,
803            )
804            .map_err(Into::into)
805    }
806}