sui_rpc_api/
reader.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::sync::Arc;
5
6use sui_sdk_types::{EpochId, ValidatorCommittee};
7use sui_types::storage::ObjectKey;
8use sui_types::storage::RpcStateReader;
9use sui_types::storage::error::{Error as StorageError, Result};
10use tap::Pipe;
11
12#[derive(Clone)]
13pub struct StateReader {
14    inner: Arc<dyn RpcStateReader>,
15}
16
17impl StateReader {
18    pub fn new(inner: Arc<dyn RpcStateReader>) -> Self {
19        Self { inner }
20    }
21
22    pub fn inner(&self) -> &Arc<dyn RpcStateReader> {
23        &self.inner
24    }
25
26    #[tracing::instrument(skip(self))]
27    pub fn get_committee(&self, epoch: EpochId) -> Option<ValidatorCommittee> {
28        self.inner
29            .get_committee(epoch)
30            .map(|committee| (*committee).clone().into())
31    }
32
33    #[tracing::instrument(skip(self))]
34    pub fn get_system_state(&self) -> Result<sui_types::sui_system_state::SuiSystemState> {
35        sui_types::sui_system_state::get_sui_system_state(self.inner())
36            .map_err(StorageError::custom)
37            .map_err(StorageError::custom)
38    }
39
40    #[tracing::instrument(skip(self))]
41    pub fn get_system_state_summary(
42        &self,
43    ) -> Result<sui_types::sui_system_state::sui_system_state_summary::SuiSystemStateSummary> {
44        use sui_types::sui_system_state::SuiSystemStateTrait;
45
46        let system_state = self.get_system_state()?;
47        let summary = system_state.into_sui_system_state_summary();
48
49        Ok(summary)
50    }
51
52    pub fn get_authenticator_state(
53        &self,
54    ) -> Result<Option<sui_types::authenticator_state::AuthenticatorStateInner>> {
55        sui_types::authenticator_state::get_authenticator_state(self.inner())
56            .map_err(StorageError::custom)
57    }
58
59    #[tracing::instrument(skip(self))]
60    pub fn get_transaction(
61        &self,
62        digest: sui_sdk_types::Digest,
63    ) -> crate::Result<(
64        sui_types::transaction::TransactionData,
65        Vec<sui_types::signature::GenericSignature>,
66        sui_types::effects::TransactionEffects,
67        Option<sui_types::effects::TransactionEvents>,
68    )> {
69        use sui_types::effects::TransactionEffectsAPI;
70
71        let transaction_digest = digest.into();
72
73        let transaction = (*self
74            .inner()
75            .get_transaction(&transaction_digest)
76            .ok_or(TransactionNotFoundError(digest))?)
77        .clone()
78        .into_inner();
79        let effects = self
80            .inner()
81            .get_transaction_effects(&transaction_digest)
82            .ok_or(TransactionNotFoundError(digest))?;
83        let events = if effects.events_digest().is_some() {
84            self.inner()
85                .get_events(effects.transaction_digest())
86                .ok_or(TransactionNotFoundError(digest))?
87                .pipe(Some)
88        } else {
89            None
90        };
91
92        let transaction = transaction.into_data().into_inner();
93        let signatures = transaction.tx_signatures;
94        let transaction = transaction.intent_message.value;
95
96        Ok((transaction, signatures, effects, events))
97    }
98
99    #[tracing::instrument(skip(self))]
100    pub fn get_transaction_read(
101        &self,
102        digest: sui_sdk_types::Digest,
103    ) -> crate::Result<TransactionRead> {
104        let (transaction, signatures, effects, events) = self.get_transaction(digest)?;
105
106        let checkpoint = self.inner().get_transaction_checkpoint(&(digest.into()));
107
108        let timestamp_ms = if let Some(checkpoint) = checkpoint {
109            self.inner()
110                .get_checkpoint_by_sequence_number(checkpoint)
111                .map(|checkpoint| checkpoint.timestamp_ms)
112        } else {
113            None
114        };
115
116        let unchanged_loaded_runtime_objects = self
117            .inner()
118            .get_unchanged_loaded_runtime_objects(&(digest.into()));
119
120        Ok(TransactionRead {
121            digest,
122            transaction,
123            signatures,
124            effects,
125            events,
126            checkpoint,
127            timestamp_ms,
128            unchanged_loaded_runtime_objects,
129        })
130    }
131
132    pub fn lookup_address_balance(
133        &self,
134        owner: sui_types::base_types::SuiAddress,
135        coin_type: move_core_types::language_storage::StructTag,
136    ) -> Option<u64> {
137        use sui_types::MoveTypeTagTraitGeneric;
138        use sui_types::SUI_ACCUMULATOR_ROOT_OBJECT_ID;
139        use sui_types::accumulator_root::AccumulatorKey;
140        use sui_types::dynamic_field::DynamicFieldKey;
141
142        let balance_type = sui_types::balance::Balance::type_tag(coin_type.into());
143
144        let key = AccumulatorKey { owner };
145        let key_type_tag = AccumulatorKey::get_type_tag(&[balance_type]);
146
147        DynamicFieldKey(SUI_ACCUMULATOR_ROOT_OBJECT_ID, key, key_type_tag)
148            .into_unbounded_id()
149            .unwrap()
150            .load_object(self.inner())
151            .and_then(|o| o.load_value::<u128>().ok())
152            .map(|balance| balance as u64)
153    }
154
155    // Return the lowest available checkpoint watermark for which the RPC service can return proper
156    // responses for.
157    pub fn get_lowest_available_checkpoint(&self) -> Result<u64, crate::RpcError> {
158        // This is the lowest lowest_available_checkpoint from the checkpoint store
159        let lowest_available_checkpoint = self.inner().get_lowest_available_checkpoint()?;
160        // This is the lowest lowest_available_checkpoint from the perpetual store
161        let lowest_available_checkpoint_objects =
162            self.inner().get_lowest_available_checkpoint_objects()?;
163
164        // Return the higher of the two for our lower watermark
165        Ok(lowest_available_checkpoint.max(lowest_available_checkpoint_objects))
166    }
167}
168
169#[derive(Debug)]
170pub struct TransactionRead {
171    pub digest: sui_sdk_types::Digest,
172    pub transaction: sui_types::transaction::TransactionData,
173    pub signatures: Vec<sui_types::signature::GenericSignature>,
174    pub effects: sui_types::effects::TransactionEffects,
175    pub events: Option<sui_types::effects::TransactionEvents>,
176    #[allow(unused)]
177    pub checkpoint: Option<u64>,
178    pub timestamp_ms: Option<u64>,
179    pub unchanged_loaded_runtime_objects: Option<Vec<ObjectKey>>,
180}
181
182#[derive(Debug)]
183pub struct TransactionNotFoundError(pub sui_sdk_types::Digest);
184
185impl std::fmt::Display for TransactionNotFoundError {
186    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187        write!(f, "Transaction {} not found", self.0)
188    }
189}
190
191impl std::error::Error for TransactionNotFoundError {}
192
193impl From<TransactionNotFoundError> for crate::RpcError {
194    fn from(value: TransactionNotFoundError) -> Self {
195        Self::new(tonic::Code::NotFound, value.to_string())
196    }
197}