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 mysten_common::ZipDebugEqIteratorExt;
7use sui_sdk_types::{EpochId, ValidatorCommittee};
8use sui_types::base_types::TransactionDigest;
9use sui_types::effects::TransactionEffectsAPI;
10use sui_types::storage::ObjectKey;
11use sui_types::storage::RpcStateReader;
12use sui_types::storage::error::{Error as StorageError, Result};
13use tap::Pipe;
14
15#[derive(Clone)]
16pub struct StateReader {
17    inner: Arc<dyn RpcStateReader>,
18}
19
20impl StateReader {
21    pub fn new(inner: Arc<dyn RpcStateReader>) -> Self {
22        Self { inner }
23    }
24
25    pub fn inner(&self) -> &Arc<dyn RpcStateReader> {
26        &self.inner
27    }
28
29    #[tracing::instrument(skip(self))]
30    pub fn get_committee(&self, epoch: EpochId) -> Option<ValidatorCommittee> {
31        self.inner
32            .get_committee(epoch)
33            .map(|committee| (*committee).clone().into())
34    }
35
36    #[tracing::instrument(skip(self))]
37    pub fn get_system_state(&self) -> Result<sui_types::sui_system_state::SuiSystemState> {
38        sui_types::sui_system_state::get_sui_system_state(self.inner())
39            .map_err(StorageError::custom)
40            .map_err(StorageError::custom)
41    }
42
43    #[tracing::instrument(skip(self))]
44    pub fn get_display_object_v2_by_type(
45        &self,
46        object_type: &move_core_types::language_storage::StructTag,
47    ) -> Option<sui_types::display_registry::Display> {
48        let object_id =
49            sui_types::display_registry::display_object_id(object_type.clone().into()).ok()?;
50
51        let object = self.inner.get_object(&object_id)?;
52
53        let move_object = object.data.try_as_move()?;
54
55        bcs::from_bytes(move_object.contents()).ok()
56    }
57
58    #[tracing::instrument(skip(self))]
59    pub fn get_system_state_summary(
60        &self,
61    ) -> Result<sui_types::sui_system_state::sui_system_state_summary::SuiSystemStateSummary> {
62        use sui_types::sui_system_state::SuiSystemStateTrait;
63
64        let system_state = self.get_system_state()?;
65        let summary = system_state.into_sui_system_state_summary();
66
67        Ok(summary)
68    }
69
70    pub fn get_authenticator_state(
71        &self,
72    ) -> Result<Option<sui_types::authenticator_state::AuthenticatorStateInner>> {
73        sui_types::authenticator_state::get_authenticator_state(self.inner())
74            .map_err(StorageError::custom)
75    }
76
77    #[tracing::instrument(skip(self))]
78    pub fn get_transaction(
79        &self,
80        digest: sui_sdk_types::Digest,
81    ) -> crate::Result<(
82        sui_types::transaction::TransactionData,
83        Vec<sui_types::signature::GenericSignature>,
84        sui_types::effects::TransactionEffects,
85        Option<sui_types::effects::TransactionEvents>,
86    )> {
87        let transaction_digest = digest.into();
88
89        let transaction = (*self
90            .inner()
91            .get_transaction(&transaction_digest)
92            .ok_or(TransactionNotFoundError(digest))?)
93        .clone()
94        .into_inner();
95        let effects = self
96            .inner()
97            .get_transaction_effects(&transaction_digest)
98            .ok_or(TransactionNotFoundError(digest))?;
99        let events = if effects.events_digest().is_some() {
100            self.inner()
101                .get_events(effects.transaction_digest())
102                .ok_or(TransactionNotFoundError(digest))?
103                .pipe(Some)
104        } else {
105            None
106        };
107
108        let transaction = transaction.into_data().into_inner();
109        let signatures = transaction.tx_signatures;
110        let transaction = transaction.intent_message.value;
111
112        Ok((transaction, signatures, effects, events))
113    }
114
115    pub fn multi_get_transaction_reads(
116        &self,
117        digests: &[sui_sdk_types::Digest],
118    ) -> crate::Result<Vec<TransactionRead>> {
119        let transaction_digests = digests
120            .iter()
121            .copied()
122            .map(Into::into)
123            .collect::<Vec<TransactionDigest>>();
124        let transactions = self.inner().multi_get_transactions(&transaction_digests);
125        let effects = self
126            .inner()
127            .multi_get_transaction_effects(&transaction_digests);
128        let events = self.inner().multi_get_events(&transaction_digests);
129
130        let mut reads = Vec::with_capacity(digests.len());
131        for (((digest, transaction_digest), transaction), (effects, events)) in digests
132            .iter()
133            .copied()
134            .zip_debug_eq(transaction_digests)
135            .zip_debug_eq(transactions)
136            .zip_debug_eq(effects.into_iter().zip_debug_eq(events))
137        {
138            let transaction = (*transaction.ok_or(TransactionNotFoundError(digest))?)
139                .clone()
140                .into_inner();
141            let effects = effects.ok_or(TransactionNotFoundError(digest))?;
142            let events = if effects.events_digest().is_some() {
143                events.ok_or(TransactionNotFoundError(digest))?.pipe(Some)
144            } else {
145                None
146            };
147
148            let transaction = transaction.into_data().into_inner();
149            let signatures = transaction.tx_signatures;
150            let transaction = transaction.intent_message.value;
151
152            let checkpoint = self.inner().get_transaction_checkpoint(&transaction_digest);
153            let timestamp_ms = if let Some(checkpoint) = checkpoint {
154                self.inner()
155                    .get_checkpoint_by_sequence_number(checkpoint)
156                    .map(|checkpoint| checkpoint.timestamp_ms)
157            } else {
158                None
159            };
160
161            let unchanged_loaded_runtime_objects = self
162                .inner()
163                .get_unchanged_loaded_runtime_objects(&transaction_digest);
164
165            reads.push(TransactionRead {
166                digest,
167                transaction,
168                signatures,
169                effects,
170                events,
171                checkpoint,
172                timestamp_ms,
173                unchanged_loaded_runtime_objects,
174            });
175        }
176
177        Ok(reads)
178    }
179
180    pub fn multi_get_events(
181        &self,
182        digests: &[TransactionDigest],
183    ) -> Vec<Option<sui_types::effects::TransactionEvents>> {
184        self.inner().multi_get_events(digests)
185    }
186
187    #[tracing::instrument(skip(self))]
188    pub fn get_transaction_read(
189        &self,
190        digest: sui_sdk_types::Digest,
191    ) -> crate::Result<TransactionRead> {
192        let (transaction, signatures, effects, events) = self.get_transaction(digest)?;
193
194        let checkpoint = self.inner().get_transaction_checkpoint(&(digest.into()));
195
196        let timestamp_ms = if let Some(checkpoint) = checkpoint {
197            self.inner()
198                .get_checkpoint_by_sequence_number(checkpoint)
199                .map(|checkpoint| checkpoint.timestamp_ms)
200        } else {
201            None
202        };
203
204        let unchanged_loaded_runtime_objects = self
205            .inner()
206            .get_unchanged_loaded_runtime_objects(&(digest.into()));
207
208        Ok(TransactionRead {
209            digest,
210            transaction,
211            signatures,
212            effects,
213            events,
214            checkpoint,
215            timestamp_ms,
216            unchanged_loaded_runtime_objects,
217        })
218    }
219
220    pub fn lookup_address_balance(
221        &self,
222        owner: sui_types::base_types::SuiAddress,
223        coin_type: move_core_types::language_storage::StructTag,
224    ) -> Option<u64> {
225        use sui_types::MoveTypeTagTraitGeneric;
226        use sui_types::SUI_ACCUMULATOR_ROOT_OBJECT_ID;
227        use sui_types::accumulator_root::AccumulatorKey;
228        use sui_types::dynamic_field::DynamicFieldKey;
229
230        let balance_type = sui_types::balance::Balance::type_tag(coin_type.into());
231
232        let key = AccumulatorKey { owner };
233        let key_type_tag = AccumulatorKey::get_type_tag(&[balance_type]);
234
235        DynamicFieldKey(SUI_ACCUMULATOR_ROOT_OBJECT_ID, key, key_type_tag)
236            .into_unbounded_id()
237            .unwrap()
238            .load_object(self.inner())
239            .and_then(|o| o.load_value::<u128>().ok())
240            .map(|balance| balance as u64)
241    }
242
243    // Return the lowest available checkpoint watermark for which the RPC service can return proper
244    // responses for.
245    pub fn get_lowest_available_checkpoint(&self) -> Result<u64, crate::RpcError> {
246        // This is the lowest lowest_available_checkpoint from the checkpoint store
247        let lowest_available_checkpoint = self.inner().get_lowest_available_checkpoint()?;
248        // This is the lowest lowest_available_checkpoint from the perpetual store
249        let lowest_available_checkpoint_objects =
250            self.inner().get_lowest_available_checkpoint_objects()?;
251
252        // Return the higher of the two for our lower watermark
253        Ok(lowest_available_checkpoint.max(lowest_available_checkpoint_objects))
254    }
255}
256
257#[derive(Debug)]
258pub struct TransactionRead {
259    pub digest: sui_sdk_types::Digest,
260    pub transaction: sui_types::transaction::TransactionData,
261    pub signatures: Vec<sui_types::signature::GenericSignature>,
262    pub effects: sui_types::effects::TransactionEffects,
263    pub events: Option<sui_types::effects::TransactionEvents>,
264    #[allow(unused)]
265    pub checkpoint: Option<u64>,
266    pub timestamp_ms: Option<u64>,
267    pub unchanged_loaded_runtime_objects: Option<Vec<ObjectKey>>,
268}
269
270#[derive(Debug)]
271pub struct TransactionNotFoundError(pub sui_sdk_types::Digest);
272
273impl std::fmt::Display for TransactionNotFoundError {
274    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275        write!(f, "Transaction {} not found", self.0)
276    }
277}
278
279impl std::error::Error for TransactionNotFoundError {}
280
281impl From<TransactionNotFoundError> for crate::RpcError {
282    fn from(value: TransactionNotFoundError) -> Self {
283        Self::new(tonic::Code::NotFound, value.to_string())
284    }
285}
286
287pub struct DisplayStore<'s> {
288    state: &'s StateReader,
289}
290
291impl<'s> DisplayStore<'s> {
292    pub fn new(state: &'s StateReader) -> Self {
293        Self { state }
294    }
295}
296
297#[async_trait::async_trait]
298impl sui_display::v2::Store for DisplayStore<'_> {
299    async fn latest(
300        &self,
301        id: move_core_types::account_address::AccountAddress,
302    ) -> anyhow::Result<Option<(move_core_types::annotated_value::MoveTypeLayout, Vec<u8>)>> {
303        let Some(object) = self.state.inner().get_object(&id.into()) else {
304            return Ok(None);
305        };
306
307        let Some(move_object) = object.data.try_as_move() else {
308            return Ok(None);
309        };
310
311        let object_type = move_object.type_().clone().into();
312
313        let Some(layout) = self.state.inner().get_struct_layout(&object_type)? else {
314            return Ok(None);
315        };
316
317        Ok(Some((layout, move_object.contents().to_vec())))
318    }
319}