sui_json_rpc/
authority_state.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use arc_swap::Guard;
5use async_trait::async_trait;
6use move_core_types::language_storage::TypeTag;
7use std::collections::{BTreeMap, HashMap};
8use std::sync::Arc;
9use sui_core::accumulators::balances::{get_all_balances_for_owner, get_balance};
10use sui_core::authority::AuthorityState;
11use sui_core::authority::authority_per_epoch_store::AuthorityPerEpochStore;
12use sui_core::execution_cache::ObjectCacheRead;
13use sui_core::jsonrpc_index::TotalBalance;
14use sui_core::subscription_handler::SubscriptionHandler;
15use sui_json_rpc_types::{
16    Coin as SuiCoin, DevInspectResults, DryRunTransactionBlockResponse, EventFilter, SuiEvent,
17    SuiObjectDataFilter, TransactionFilter,
18};
19use sui_storage::key_value_store::{
20    KVStoreTransactionData, TransactionKeyValueStore, TransactionKeyValueStoreTrait,
21};
22use sui_types::base_types::{
23    MoveObjectType, ObjectID, ObjectInfo, ObjectRef, SequenceNumber, SuiAddress,
24};
25use sui_types::bridge::Bridge;
26use sui_types::committee::{Committee, EpochId};
27use sui_types::digests::{ChainIdentifier, TransactionDigest};
28use sui_types::dynamic_field::DynamicFieldInfo;
29use sui_types::effects::TransactionEffects;
30use sui_types::error::{SuiError, SuiErrorKind, SuiResult, UserInputError};
31use sui_types::event::EventID;
32use sui_types::governance::StakedSui;
33use sui_types::messages_checkpoint::{
34    CheckpointContents, CheckpointContentsDigest, CheckpointDigest, CheckpointSequenceNumber,
35    VerifiedCheckpoint,
36};
37use sui_types::object::{Object, ObjectRead, PastObjectRead};
38use sui_types::storage::{BackingPackageStore, ObjectStore, WriteKind};
39use sui_types::sui_serde::BigInt;
40use sui_types::sui_system_state::SuiSystemState;
41use sui_types::transaction::{Transaction, TransactionData, TransactionKind};
42use thiserror::Error;
43use tokio::task::JoinError;
44
45use crate::ObjectProvider;
46#[cfg(test)]
47use mockall::automock;
48use typed_store_error::TypedStoreError;
49
50pub type StateReadResult<T = ()> = Result<T, StateReadError>;
51
52/// Trait for AuthorityState methods commonly used by at least two api.
53#[cfg_attr(test, automock)]
54#[async_trait]
55pub trait StateRead: Send + Sync {
56    async fn multi_get(
57        &self,
58        transactions: &[TransactionDigest],
59        effects: &[TransactionDigest],
60    ) -> StateReadResult<KVStoreTransactionData>;
61
62    fn get_object_read(&self, object_id: &ObjectID) -> StateReadResult<ObjectRead>;
63
64    fn get_past_object_read(
65        &self,
66        object_id: &ObjectID,
67        version: SequenceNumber,
68    ) -> StateReadResult<PastObjectRead>;
69
70    async fn get_object(&self, object_id: &ObjectID) -> StateReadResult<Option<Object>>;
71
72    fn load_epoch_store_one_call_per_task(&self) -> Guard<Arc<AuthorityPerEpochStore>>;
73
74    fn get_dynamic_fields(
75        &self,
76        owner: ObjectID,
77        cursor: Option<ObjectID>,
78        limit: usize,
79    ) -> StateReadResult<Vec<(ObjectID, DynamicFieldInfo)>>;
80
81    fn get_cache_reader(&self) -> &Arc<dyn ObjectCacheRead>;
82
83    fn get_object_store(&self) -> &Arc<dyn ObjectStore + Send + Sync>;
84
85    fn get_backing_package_store(&self) -> &Arc<dyn BackingPackageStore + Send + Sync>;
86
87    fn get_owner_objects(
88        &self,
89        owner: SuiAddress,
90        cursor: Option<ObjectID>,
91        filter: Option<SuiObjectDataFilter>,
92    ) -> StateReadResult<Vec<ObjectInfo>>;
93
94    async fn query_events(
95        &self,
96        kv_store: &Arc<TransactionKeyValueStore>,
97        query: EventFilter,
98        // If `Some`, the query will start from the next item after the specified cursor
99        cursor: Option<EventID>,
100        limit: usize,
101        descending: bool,
102    ) -> StateReadResult<Vec<SuiEvent>>;
103
104    // transaction_execution_api
105    #[allow(clippy::type_complexity)]
106    async fn dry_exec_transaction(
107        &self,
108        transaction: TransactionData,
109        transaction_digest: TransactionDigest,
110    ) -> StateReadResult<(
111        DryRunTransactionBlockResponse,
112        BTreeMap<ObjectID, (ObjectRef, Object, WriteKind)>,
113        TransactionEffects,
114        Option<ObjectID>,
115    )>;
116
117    async fn dev_inspect_transaction_block(
118        &self,
119        sender: SuiAddress,
120        transaction_kind: TransactionKind,
121        gas_price: Option<u64>,
122        gas_budget: Option<u64>,
123        gas_sponsor: Option<SuiAddress>,
124        gas_objects: Option<Vec<ObjectRef>>,
125        show_raw_txn_data_and_effects: Option<bool>,
126        skip_checks: Option<bool>,
127    ) -> StateReadResult<DevInspectResults>;
128
129    // indexer_api
130    fn get_subscription_handler(&self) -> Arc<SubscriptionHandler>;
131
132    fn get_owner_objects_with_limit(
133        &self,
134        owner: SuiAddress,
135        cursor: Option<ObjectID>,
136        limit: usize,
137        filter: Option<SuiObjectDataFilter>,
138    ) -> StateReadResult<Vec<ObjectInfo>>;
139
140    async fn get_transactions(
141        &self,
142        kv_store: &Arc<TransactionKeyValueStore>,
143        filter: Option<TransactionFilter>,
144        cursor: Option<TransactionDigest>,
145        limit: Option<usize>,
146        reverse: bool,
147    ) -> StateReadResult<Vec<TransactionDigest>>;
148
149    fn get_dynamic_field_object_id(
150        &self,
151        owner: ObjectID,
152        name_type: TypeTag,
153        name_bcs_bytes: &[u8],
154    ) -> StateReadResult<Option<ObjectID>>;
155
156    // governance_api
157    async fn get_staked_sui(&self, owner: SuiAddress) -> StateReadResult<Vec<StakedSui>>;
158    fn get_system_state(&self) -> StateReadResult<SuiSystemState>;
159    fn get_or_latest_committee(&self, epoch: Option<BigInt<u64>>) -> StateReadResult<Committee>;
160
161    // bridge_api
162    fn get_bridge(&self) -> StateReadResult<Bridge>;
163
164    // coin_api
165    fn find_publish_txn_digest(&self, package_id: ObjectID) -> StateReadResult<TransactionDigest>;
166    fn get_owned_coins(
167        &self,
168        owner: SuiAddress,
169        cursor: (String, u64, ObjectID),
170        limit: usize,
171        one_coin_type_only: bool,
172    ) -> StateReadResult<Vec<SuiCoin>>;
173    async fn get_executed_transaction_and_effects(
174        &self,
175        digest: TransactionDigest,
176        kv_store: Arc<TransactionKeyValueStore>,
177    ) -> StateReadResult<(Transaction, TransactionEffects)>;
178    async fn get_balance(
179        &self,
180        owner: SuiAddress,
181        coin_type: TypeTag,
182    ) -> StateReadResult<TotalBalance>;
183    async fn get_all_balance(
184        &self,
185        owner: SuiAddress,
186    ) -> StateReadResult<Arc<HashMap<TypeTag, TotalBalance>>>;
187
188    // read_api
189    fn get_verified_checkpoint_by_sequence_number(
190        &self,
191        sequence_number: CheckpointSequenceNumber,
192    ) -> StateReadResult<VerifiedCheckpoint>;
193
194    fn get_checkpoint_contents(
195        &self,
196        digest: CheckpointContentsDigest,
197    ) -> StateReadResult<CheckpointContents>;
198
199    fn get_verified_checkpoint_summary_by_digest(
200        &self,
201        digest: CheckpointDigest,
202    ) -> StateReadResult<VerifiedCheckpoint>;
203
204    fn deprecated_multi_get_transaction_checkpoint(
205        &self,
206        digests: &[TransactionDigest],
207    ) -> StateReadResult<Vec<Option<(EpochId, CheckpointSequenceNumber)>>>;
208
209    fn deprecated_get_transaction_checkpoint(
210        &self,
211        digest: &TransactionDigest,
212    ) -> StateReadResult<Option<(EpochId, CheckpointSequenceNumber)>>;
213
214    fn multi_get_checkpoint_by_sequence_number(
215        &self,
216        sequence_numbers: &[CheckpointSequenceNumber],
217    ) -> StateReadResult<Vec<Option<VerifiedCheckpoint>>>;
218
219    fn get_total_transaction_blocks(&self) -> StateReadResult<u64>;
220
221    fn get_checkpoint_by_sequence_number(
222        &self,
223        sequence_number: CheckpointSequenceNumber,
224    ) -> StateReadResult<Option<VerifiedCheckpoint>>;
225
226    fn get_latest_checkpoint_sequence_number(&self) -> StateReadResult<CheckpointSequenceNumber>;
227
228    fn get_chain_identifier(&self) -> StateReadResult<ChainIdentifier>;
229}
230
231#[async_trait]
232impl StateRead for AuthorityState {
233    async fn multi_get(
234        &self,
235        transactions: &[TransactionDigest],
236        effects: &[TransactionDigest],
237    ) -> StateReadResult<KVStoreTransactionData> {
238        Ok(
239            <AuthorityState as TransactionKeyValueStoreTrait>::multi_get(
240                self,
241                transactions,
242                effects,
243            )
244            .await?,
245        )
246    }
247
248    fn get_object_read(&self, object_id: &ObjectID) -> StateReadResult<ObjectRead> {
249        Ok(self.get_object_read(object_id)?)
250    }
251
252    async fn get_object(&self, object_id: &ObjectID) -> StateReadResult<Option<Object>> {
253        Ok(self.get_object(object_id).await)
254    }
255
256    fn get_past_object_read(
257        &self,
258        object_id: &ObjectID,
259        version: SequenceNumber,
260    ) -> StateReadResult<PastObjectRead> {
261        Ok(self.get_past_object_read(object_id, version)?)
262    }
263
264    fn load_epoch_store_one_call_per_task(&self) -> Guard<Arc<AuthorityPerEpochStore>> {
265        self.load_epoch_store_one_call_per_task()
266    }
267
268    fn get_dynamic_fields(
269        &self,
270        owner: ObjectID,
271        cursor: Option<ObjectID>,
272        limit: usize,
273    ) -> StateReadResult<Vec<(ObjectID, DynamicFieldInfo)>> {
274        Ok(self.get_dynamic_fields(owner, cursor, limit)?)
275    }
276
277    fn get_cache_reader(&self) -> &Arc<dyn ObjectCacheRead> {
278        self.get_object_cache_reader()
279    }
280
281    fn get_object_store(&self) -> &Arc<dyn ObjectStore + Send + Sync> {
282        self.get_object_store()
283    }
284
285    fn get_backing_package_store(&self) -> &Arc<dyn BackingPackageStore + Send + Sync> {
286        self.get_backing_package_store()
287    }
288
289    fn get_owner_objects(
290        &self,
291        owner: SuiAddress,
292        cursor: Option<ObjectID>,
293        filter: Option<SuiObjectDataFilter>,
294    ) -> StateReadResult<Vec<ObjectInfo>> {
295        Ok(self
296            .get_owner_objects_iterator(owner, cursor, filter)?
297            .collect())
298    }
299
300    async fn query_events(
301        &self,
302        kv_store: &Arc<TransactionKeyValueStore>,
303        query: EventFilter,
304        // If `Some`, the query will start from the next item after the specified cursor
305        cursor: Option<EventID>,
306        limit: usize,
307        descending: bool,
308    ) -> StateReadResult<Vec<SuiEvent>> {
309        Ok(self
310            .query_events(kv_store, query, cursor, limit, descending)
311            .await?)
312    }
313
314    #[allow(clippy::type_complexity)]
315    async fn dry_exec_transaction(
316        &self,
317        transaction: TransactionData,
318        transaction_digest: TransactionDigest,
319    ) -> StateReadResult<(
320        DryRunTransactionBlockResponse,
321        BTreeMap<ObjectID, (ObjectRef, Object, WriteKind)>,
322        TransactionEffects,
323        Option<ObjectID>,
324    )> {
325        Ok(self
326            .dry_exec_transaction(transaction, transaction_digest)
327            .await?)
328    }
329
330    async fn dev_inspect_transaction_block(
331        &self,
332        sender: SuiAddress,
333        transaction_kind: TransactionKind,
334        gas_price: Option<u64>,
335        gas_budget: Option<u64>,
336        gas_sponsor: Option<SuiAddress>,
337        gas_objects: Option<Vec<ObjectRef>>,
338        show_raw_txn_data_and_effects: Option<bool>,
339        skip_checks: Option<bool>,
340    ) -> StateReadResult<DevInspectResults> {
341        Ok(self
342            .dev_inspect_transaction_block(
343                sender,
344                transaction_kind,
345                gas_price,
346                gas_budget,
347                gas_sponsor,
348                gas_objects,
349                show_raw_txn_data_and_effects,
350                skip_checks,
351            )
352            .await?)
353    }
354
355    fn get_subscription_handler(&self) -> Arc<SubscriptionHandler> {
356        self.subscription_handler.clone()
357    }
358
359    fn get_owner_objects_with_limit(
360        &self,
361        owner: SuiAddress,
362        cursor: Option<ObjectID>,
363        limit: usize,
364        filter: Option<SuiObjectDataFilter>,
365    ) -> StateReadResult<Vec<ObjectInfo>> {
366        Ok(self.get_owner_objects(owner, cursor, limit, filter)?)
367    }
368
369    async fn get_transactions(
370        &self,
371        kv_store: &Arc<TransactionKeyValueStore>,
372        filter: Option<TransactionFilter>,
373        cursor: Option<TransactionDigest>,
374        limit: Option<usize>,
375        reverse: bool,
376    ) -> StateReadResult<Vec<TransactionDigest>> {
377        Ok(self
378            .get_transactions(kv_store, filter, cursor, limit, reverse)
379            .await?)
380    }
381
382    fn get_dynamic_field_object_id(
383        // indexer
384        &self,
385        owner: ObjectID,
386        name_type: TypeTag,
387        name_bcs_bytes: &[u8],
388    ) -> StateReadResult<Option<ObjectID>> {
389        Ok(self.get_dynamic_field_object_id(owner, name_type, name_bcs_bytes)?)
390    }
391
392    async fn get_staked_sui(&self, owner: SuiAddress) -> StateReadResult<Vec<StakedSui>> {
393        Ok(self
394            .get_move_objects(owner, MoveObjectType::staked_sui())
395            .await?)
396    }
397    fn get_system_state(&self) -> StateReadResult<SuiSystemState> {
398        Ok(self
399            .get_object_cache_reader()
400            .get_sui_system_state_object_unsafe()?)
401    }
402    fn get_or_latest_committee(&self, epoch: Option<BigInt<u64>>) -> StateReadResult<Committee> {
403        Ok(self
404            .committee_store()
405            .get_or_latest_committee(epoch.map(|e| *e))?)
406    }
407
408    fn get_bridge(&self) -> StateReadResult<Bridge> {
409        self.get_cache_reader()
410            .get_bridge_object_unsafe()
411            .map_err(|err| err.into())
412    }
413
414    fn find_publish_txn_digest(&self, package_id: ObjectID) -> StateReadResult<TransactionDigest> {
415        Ok(self.find_publish_txn_digest(package_id)?)
416    }
417    fn get_owned_coins(
418        &self,
419        owner: SuiAddress,
420        cursor: (String, u64, ObjectID),
421        limit: usize,
422        one_coin_type_only: bool,
423    ) -> StateReadResult<Vec<SuiCoin>> {
424        Ok(self
425            .get_owned_coins_iterator_with_cursor(owner, cursor, limit, one_coin_type_only)?
426            .map(|(key, coin)| SuiCoin {
427                coin_type: key.coin_type,
428                coin_object_id: key.object_id,
429                version: coin.version,
430                digest: coin.digest,
431                balance: coin.balance,
432                previous_transaction: coin.previous_transaction,
433            })
434            .collect())
435    }
436
437    async fn get_executed_transaction_and_effects(
438        &self,
439        digest: TransactionDigest,
440        kv_store: Arc<TransactionKeyValueStore>,
441    ) -> StateReadResult<(Transaction, TransactionEffects)> {
442        Ok(self
443            .get_executed_transaction_and_effects(digest, kv_store)
444            .await?)
445    }
446
447    async fn get_balance(
448        &self,
449        owner: SuiAddress,
450        coin_type: TypeTag,
451    ) -> StateReadResult<TotalBalance> {
452        let indexes = self.indexes.clone();
453        let child_object_resolver = self.get_child_object_resolver().clone();
454        Ok(
455            tokio::task::spawn_blocking(move || -> SuiResult<TotalBalance> {
456                let address_balance =
457                    get_balance(owner, child_object_resolver.as_ref(), coin_type.clone())?;
458                let coin_balance = indexes
459                    .as_ref()
460                    .ok_or(SuiErrorKind::IndexStoreNotAvailable)?
461                    .get_coin_object_balance(owner, coin_type)?;
462                let mut total_balance = coin_balance;
463                if address_balance > 0 {
464                    total_balance.balance += address_balance as i128;
465                    total_balance.num_coins += 1;
466                }
467                Ok(total_balance)
468            })
469            .await
470            .map_err(|e: JoinError| {
471                SuiError(Box::new(SuiErrorKind::ExecutionError(e.to_string())))
472            })??,
473        )
474    }
475
476    async fn get_all_balance(
477        &self,
478        owner: SuiAddress,
479    ) -> StateReadResult<Arc<HashMap<TypeTag, TotalBalance>>> {
480        let indexes = self.indexes.clone();
481        let child_object_resolver = self.get_child_object_resolver().clone();
482        Ok(tokio::task::spawn_blocking(
483            move || -> SuiResult<Arc<HashMap<TypeTag, TotalBalance>>> {
484                let indexes = indexes
485                    .as_ref()
486                    .ok_or(SuiErrorKind::IndexStoreNotAvailable)?;
487                let address_balances = get_all_balances_for_owner(
488                    owner,
489                    child_object_resolver.as_ref(),
490                    indexes.tables(),
491                    usize::MAX,
492                    None,
493                )?;
494                let coin_balances = (*indexes.get_all_coin_object_balances(owner)?).clone();
495                let mut all_balances = coin_balances;
496                for (coin_type, balance) in address_balances {
497                    let existing_balance = all_balances.entry(coin_type).or_insert(TotalBalance {
498                        balance: 0,
499                        num_coins: 0,
500                    });
501                    existing_balance.balance += balance as i128;
502                    existing_balance.num_coins += 1;
503                }
504                Ok(Arc::new(all_balances))
505            },
506        )
507        .await
508        .map_err(|e: JoinError| {
509            SuiError(Box::new(SuiErrorKind::ExecutionError(e.to_string())))
510        })??)
511    }
512
513    fn get_verified_checkpoint_by_sequence_number(
514        &self,
515        sequence_number: CheckpointSequenceNumber,
516    ) -> StateReadResult<VerifiedCheckpoint> {
517        Ok(self.get_verified_checkpoint_by_sequence_number(sequence_number)?)
518    }
519
520    fn get_checkpoint_contents(
521        &self,
522        digest: CheckpointContentsDigest,
523    ) -> StateReadResult<CheckpointContents> {
524        Ok(self.get_checkpoint_contents(digest)?)
525    }
526
527    fn get_verified_checkpoint_summary_by_digest(
528        &self,
529        digest: CheckpointDigest,
530    ) -> StateReadResult<VerifiedCheckpoint> {
531        Ok(self.get_verified_checkpoint_summary_by_digest(digest)?)
532    }
533
534    fn deprecated_multi_get_transaction_checkpoint(
535        &self,
536        digests: &[TransactionDigest],
537    ) -> StateReadResult<Vec<Option<(EpochId, CheckpointSequenceNumber)>>> {
538        Ok(self
539            .get_checkpoint_cache()
540            .deprecated_multi_get_transaction_checkpoint(digests))
541    }
542
543    fn deprecated_get_transaction_checkpoint(
544        &self,
545        digest: &TransactionDigest,
546    ) -> StateReadResult<Option<(EpochId, CheckpointSequenceNumber)>> {
547        Ok(self
548            .get_checkpoint_cache()
549            .deprecated_get_transaction_checkpoint(digest))
550    }
551
552    fn multi_get_checkpoint_by_sequence_number(
553        &self,
554        sequence_numbers: &[CheckpointSequenceNumber],
555    ) -> StateReadResult<Vec<Option<VerifiedCheckpoint>>> {
556        Ok(self.multi_get_checkpoint_by_sequence_number(sequence_numbers)?)
557    }
558
559    fn get_total_transaction_blocks(&self) -> StateReadResult<u64> {
560        Ok(self.get_total_transaction_blocks()?)
561    }
562
563    fn get_checkpoint_by_sequence_number(
564        &self,
565        sequence_number: CheckpointSequenceNumber,
566    ) -> StateReadResult<Option<VerifiedCheckpoint>> {
567        Ok(self.get_checkpoint_by_sequence_number(sequence_number)?)
568    }
569
570    fn get_latest_checkpoint_sequence_number(&self) -> StateReadResult<CheckpointSequenceNumber> {
571        Ok(self.get_latest_checkpoint_sequence_number()?)
572    }
573
574    fn get_chain_identifier(&self) -> StateReadResult<ChainIdentifier> {
575        Ok(self.get_chain_identifier())
576    }
577}
578
579/// This implementation allows `S` to be a dynamically sized type (DST) that implements ObjectProvider
580/// Valid as `S` is referenced only, and memory management is handled by `Arc`
581#[async_trait]
582impl<S: ?Sized + StateRead> ObjectProvider for Arc<S> {
583    type Error = StateReadError;
584
585    async fn get_object(
586        &self,
587        id: &ObjectID,
588        version: &SequenceNumber,
589    ) -> Result<Object, Self::Error> {
590        Ok(self.get_past_object_read(id, *version)?.into_object()?)
591    }
592
593    async fn find_object_lt_or_eq_version(
594        &self,
595        id: &ObjectID,
596        version: &SequenceNumber,
597    ) -> Result<Option<Object>, Self::Error> {
598        Ok(self
599            .get_cache_reader()
600            .find_object_lt_or_eq_version(*id, *version))
601    }
602}
603
604#[async_trait]
605impl<S: ?Sized + StateRead> ObjectProvider for (Arc<S>, Arc<TransactionKeyValueStore>) {
606    type Error = StateReadError;
607
608    async fn get_object(
609        &self,
610        id: &ObjectID,
611        version: &SequenceNumber,
612    ) -> Result<Object, Self::Error> {
613        let object_read = self.0.get_past_object_read(id, *version)?;
614        match object_read {
615            PastObjectRead::ObjectNotExists(_) | PastObjectRead::VersionNotFound(..) => {
616                match self.1.get_object(*id, *version).await? {
617                    Some(object) => Ok(object),
618                    None => Ok(PastObjectRead::VersionNotFound(*id, *version).into_object()?),
619                }
620            }
621            _ => Ok(object_read.into_object()?),
622        }
623    }
624
625    async fn find_object_lt_or_eq_version(
626        &self,
627        id: &ObjectID,
628        version: &SequenceNumber,
629    ) -> Result<Option<Object>, Self::Error> {
630        Ok(self
631            .0
632            .get_cache_reader()
633            .find_object_lt_or_eq_version(*id, *version))
634    }
635}
636
637#[derive(Debug, Error)]
638pub enum StateReadInternalError {
639    #[error(transparent)]
640    SuiError(#[from] SuiError),
641    #[error(transparent)]
642    JoinError(#[from] JoinError),
643    #[error(transparent)]
644    Anyhow(#[from] anyhow::Error),
645}
646
647impl From<SuiErrorKind> for StateReadInternalError {
648    fn from(e: SuiErrorKind) -> Self {
649        StateReadInternalError::SuiError(SuiError::from(e))
650    }
651}
652
653#[derive(Debug, Error)]
654pub enum StateReadClientError {
655    #[error(transparent)]
656    SuiError(#[from] SuiError),
657    #[error(transparent)]
658    UserInputError(#[from] UserInputError),
659}
660
661impl From<SuiErrorKind> for StateReadClientError {
662    fn from(e: SuiErrorKind) -> Self {
663        StateReadClientError::SuiError(SuiError::from(e))
664    }
665}
666
667/// `StateReadError` is the error type for callers to work with.
668/// It captures all possible errors that can occur while reading state, classifying them into two categories.
669/// Unless `StateReadError` is the final error state before returning to caller, the app may still want error context.
670/// This context is preserved in `Internal` and `Client` variants.
671#[derive(Debug, Error)]
672pub enum StateReadError {
673    // sui_json_rpc::Error will do the final conversion to generic error message
674    #[error(transparent)]
675    Internal(#[from] StateReadInternalError),
676
677    // Client errors
678    #[error(transparent)]
679    Client(#[from] StateReadClientError),
680}
681
682impl From<SuiErrorKind> for StateReadError {
683    fn from(e: SuiErrorKind) -> Self {
684        match e {
685            SuiErrorKind::IndexStoreNotAvailable
686            | SuiErrorKind::TransactionNotFound { .. }
687            | SuiErrorKind::UnsupportedFeatureError { .. }
688            | SuiErrorKind::UserInputError { .. }
689            | SuiErrorKind::WrongMessageVersion { .. } => StateReadError::Client(e.into()),
690            _ => StateReadError::Internal(e.into()),
691        }
692    }
693}
694
695impl From<SuiError> for StateReadError {
696    fn from(e: SuiError) -> Self {
697        e.into_inner().into()
698    }
699}
700
701impl From<UserInputError> for StateReadError {
702    fn from(e: UserInputError) -> Self {
703        StateReadError::Client(e.into())
704    }
705}
706
707impl From<JoinError> for StateReadError {
708    fn from(e: JoinError) -> Self {
709        StateReadError::Internal(e.into())
710    }
711}
712
713impl From<anyhow::Error> for StateReadError {
714    fn from(e: anyhow::Error) -> Self {
715        StateReadError::Internal(e.into())
716    }
717}
718
719impl From<TypedStoreError> for StateReadError {
720    fn from(e: TypedStoreError) -> Self {
721        let error: SuiError = e.into();
722        StateReadError::Internal(error.into())
723    }
724}