1use 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::{CoinIndexKey2, CoinInfo, 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::accumulator_root::AccumulatorKey;
23use sui_types::balance::Balance;
24use sui_types::base_types::{
25 MoveObjectType, ObjectID, ObjectInfo, ObjectRef, SequenceNumber, SuiAddress,
26};
27use sui_types::bridge::Bridge;
28use sui_types::coin_reservation;
29use sui_types::committee::{Committee, EpochId};
30use sui_types::digests::{ChainIdentifier, TransactionDigest};
31use sui_types::dynamic_field::DynamicFieldInfo;
32use sui_types::effects::TransactionEffects;
33use sui_types::error::{SuiError, SuiErrorKind, SuiResult, UserInputError};
34use sui_types::event::EventID;
35use sui_types::governance::StakedSui;
36use sui_types::messages_checkpoint::{
37 CheckpointContents, CheckpointContentsDigest, CheckpointDigest, CheckpointSequenceNumber,
38 VerifiedCheckpoint,
39};
40use sui_types::object::{MoveObject, Object, ObjectRead, Owner, PastObjectRead};
41use sui_types::storage::{BackingPackageStore, ObjectStore, WriteKind};
42use sui_types::sui_serde::BigInt;
43use sui_types::sui_system_state::SuiSystemState;
44use sui_types::transaction::{Transaction, TransactionData, TransactionKind};
45use thiserror::Error;
46use tokio::task::JoinError;
47
48use crate::ObjectProvider;
49#[cfg(test)]
50use mockall::automock;
51use typed_store_error::TypedStoreError;
52
53pub type StateReadResult<T = ()> = Result<T, StateReadError>;
54
55#[cfg_attr(test, automock)]
57#[async_trait]
58pub trait StateRead: Send + Sync {
59 async fn multi_get(
60 &self,
61 transactions: &[TransactionDigest],
62 effects: &[TransactionDigest],
63 ) -> StateReadResult<KVStoreTransactionData>;
64
65 fn get_object_read(&self, object_id: &ObjectID) -> StateReadResult<ObjectRead>;
66
67 fn get_past_object_read(
68 &self,
69 object_id: &ObjectID,
70 version: SequenceNumber,
71 ) -> StateReadResult<PastObjectRead>;
72
73 async fn get_object(&self, object_id: &ObjectID) -> StateReadResult<Option<Object>>;
74
75 fn load_epoch_store_one_call_per_task(&self) -> Guard<Arc<AuthorityPerEpochStore>>;
76
77 fn get_dynamic_fields(
78 &self,
79 owner: ObjectID,
80 cursor: Option<ObjectID>,
81 limit: usize,
82 ) -> StateReadResult<Vec<(ObjectID, DynamicFieldInfo)>>;
83
84 fn get_cache_reader(&self) -> &Arc<dyn ObjectCacheRead>;
85
86 fn get_object_store(&self) -> &Arc<dyn ObjectStore + Send + Sync>;
87
88 fn get_backing_package_store(&self) -> &Arc<dyn BackingPackageStore + Send + Sync>;
89
90 fn get_owner_objects(
91 &self,
92 owner: SuiAddress,
93 cursor: Option<ObjectID>,
94 filter: Option<SuiObjectDataFilter>,
95 ) -> StateReadResult<Vec<ObjectInfo>>;
96
97 async fn query_events(
98 &self,
99 kv_store: &Arc<TransactionKeyValueStore>,
100 query: EventFilter,
101 cursor: Option<EventID>,
103 limit: usize,
104 descending: bool,
105 ) -> StateReadResult<Vec<SuiEvent>>;
106
107 #[allow(clippy::type_complexity)]
109 async fn dry_exec_transaction(
110 &self,
111 transaction: TransactionData,
112 ) -> StateReadResult<(
113 DryRunTransactionBlockResponse,
114 BTreeMap<ObjectID, (ObjectRef, Object, WriteKind)>,
115 TransactionEffects,
116 Option<ObjectID>,
117 )>;
118
119 async fn dev_inspect_transaction_block(
120 &self,
121 sender: SuiAddress,
122 transaction_kind: TransactionKind,
123 gas_price: Option<u64>,
124 gas_budget: Option<u64>,
125 gas_sponsor: Option<SuiAddress>,
126 gas_objects: Option<Vec<ObjectRef>>,
127 show_raw_txn_data_and_effects: Option<bool>,
128 skip_checks: Option<bool>,
129 ) -> StateReadResult<DevInspectResults>;
130
131 fn get_subscription_handler(&self) -> Arc<SubscriptionHandler>;
133
134 fn get_owner_objects_with_limit(
135 &self,
136 owner: SuiAddress,
137 cursor: Option<ObjectID>,
138 limit: usize,
139 filter: Option<SuiObjectDataFilter>,
140 ) -> StateReadResult<Vec<ObjectInfo>>;
141
142 async fn get_transactions(
143 &self,
144 kv_store: &Arc<TransactionKeyValueStore>,
145 filter: Option<TransactionFilter>,
146 cursor: Option<TransactionDigest>,
147 limit: Option<usize>,
148 reverse: bool,
149 ) -> StateReadResult<Vec<TransactionDigest>>;
150
151 fn get_dynamic_field_object_id(
152 &self,
153 owner: ObjectID,
154 name_type: TypeTag,
155 name_bcs_bytes: &[u8],
156 ) -> StateReadResult<Option<ObjectID>>;
157
158 async fn get_staked_sui(&self, owner: SuiAddress) -> StateReadResult<Vec<StakedSui>>;
160 fn get_system_state(&self) -> StateReadResult<SuiSystemState>;
161 fn get_or_latest_committee(&self, epoch: Option<BigInt<u64>>) -> StateReadResult<Committee>;
162
163 fn get_bridge(&self) -> StateReadResult<Bridge>;
165
166 fn find_publish_txn_digest(&self, package_id: ObjectID) -> StateReadResult<TransactionDigest>;
168 fn get_owned_coins(
169 &self,
170 owner: SuiAddress,
171 cursor: (String, u64, ObjectID),
172 limit: usize,
173 one_coin_type_only: bool,
174 ) -> StateReadResult<Vec<SuiCoin>>;
175 async fn get_executed_transaction_and_effects(
176 &self,
177 digest: TransactionDigest,
178 kv_store: Arc<TransactionKeyValueStore>,
179 ) -> StateReadResult<(Transaction, TransactionEffects)>;
180 async fn get_balance(
181 &self,
182 owner: SuiAddress,
183 coin_type: TypeTag,
184 ) -> StateReadResult<TotalBalance>;
185 async fn get_all_balance(
186 &self,
187 owner: SuiAddress,
188 ) -> StateReadResult<Arc<HashMap<TypeTag, TotalBalance>>>;
189
190 fn get_verified_checkpoint_by_sequence_number(
192 &self,
193 sequence_number: CheckpointSequenceNumber,
194 ) -> StateReadResult<VerifiedCheckpoint>;
195
196 fn get_checkpoint_contents(
197 &self,
198 digest: CheckpointContentsDigest,
199 ) -> StateReadResult<CheckpointContents>;
200
201 fn get_verified_checkpoint_summary_by_digest(
202 &self,
203 digest: CheckpointDigest,
204 ) -> StateReadResult<VerifiedCheckpoint>;
205
206 fn deprecated_multi_get_transaction_checkpoint(
207 &self,
208 digests: &[TransactionDigest],
209 ) -> StateReadResult<Vec<Option<(EpochId, CheckpointSequenceNumber)>>>;
210
211 fn deprecated_get_transaction_checkpoint(
212 &self,
213 digest: &TransactionDigest,
214 ) -> StateReadResult<Option<(EpochId, CheckpointSequenceNumber)>>;
215
216 fn multi_get_checkpoint_by_sequence_number(
217 &self,
218 sequence_numbers: &[CheckpointSequenceNumber],
219 ) -> StateReadResult<Vec<Option<VerifiedCheckpoint>>>;
220
221 fn get_total_transaction_blocks(&self) -> StateReadResult<u64>;
222
223 fn get_checkpoint_by_sequence_number(
224 &self,
225 sequence_number: CheckpointSequenceNumber,
226 ) -> StateReadResult<Option<VerifiedCheckpoint>>;
227
228 fn get_latest_checkpoint_sequence_number(&self) -> StateReadResult<CheckpointSequenceNumber>;
229
230 fn get_chain_identifier(&self) -> StateReadResult<ChainIdentifier>;
231}
232
233#[async_trait]
234impl StateRead for AuthorityState {
235 async fn multi_get(
236 &self,
237 transactions: &[TransactionDigest],
238 effects: &[TransactionDigest],
239 ) -> StateReadResult<KVStoreTransactionData> {
240 Ok(
241 <AuthorityState as TransactionKeyValueStoreTrait>::multi_get(
242 self,
243 transactions,
244 effects,
245 )
246 .await?,
247 )
248 }
249
250 fn get_object_read(&self, object_id: &ObjectID) -> StateReadResult<ObjectRead> {
251 let result = self.get_object_read(object_id)?;
252
253 if let ObjectRead::NotExists(object_id) = result
256 && self
257 .load_epoch_store_one_call_per_task()
258 .protocol_config()
259 .enable_coin_reservation_obj_refs()
260 {
261 let chain_identifier = self.get_chain_identifier();
262 let unmasked_id = coin_reservation::mask_or_unmask_id(object_id, chain_identifier);
263
264 if let ObjectRead::Exists(_, object, _) = self.get_object_read(&unmasked_id)? {
266 let accumulator_version = object.version();
267 let Some(move_object) = object.data.try_as_move() else {
268 return Ok(ObjectRead::NotExists(object_id));
270 };
271 let Some(currency_type) =
272 move_object.type_().balance_accumulator_field_type_maybe()
273 else {
274 return Ok(ObjectRead::NotExists(object_id));
276 };
277
278 let balance_type = Balance::type_tag(currency_type.clone());
279
280 let (AccumulatorKey { owner }, value) = move_object.try_into()?;
281
282 let Some((object_ref, balance, previous_transaction)) =
283 self.get_address_balance_coin_info(owner, balance_type)?
284 else {
285 return Ok(ObjectRead::NotExists(object_id));
286 };
287
288 debug_assert_eq!(balance, value.as_u128().map(|v| v as u64).unwrap_or(0));
289
290 let coin = Object::new_move(
292 MoveObject::new_coin(currency_type, accumulator_version, object_id, balance),
293 Owner::AddressOwner(owner),
294 previous_transaction,
295 );
296
297 let layout = self.get_object_layout(&coin)?;
298 return Ok(ObjectRead::Exists(object_ref, coin, layout));
299 }
300
301 return Ok(ObjectRead::NotExists(object_id));
302 }
303
304 Ok(result)
305 }
306
307 async fn get_object(&self, object_id: &ObjectID) -> StateReadResult<Option<Object>> {
308 Ok(self.get_object(object_id))
309 }
310
311 fn get_past_object_read(
312 &self,
313 object_id: &ObjectID,
314 version: SequenceNumber,
315 ) -> StateReadResult<PastObjectRead> {
316 Ok(self.get_past_object_read(object_id, version)?)
317 }
318
319 fn load_epoch_store_one_call_per_task(&self) -> Guard<Arc<AuthorityPerEpochStore>> {
320 self.load_epoch_store_one_call_per_task()
321 }
322
323 fn get_dynamic_fields(
324 &self,
325 owner: ObjectID,
326 cursor: Option<ObjectID>,
327 limit: usize,
328 ) -> StateReadResult<Vec<(ObjectID, DynamicFieldInfo)>> {
329 Ok(self.get_dynamic_fields(owner, cursor, limit)?)
330 }
331
332 fn get_cache_reader(&self) -> &Arc<dyn ObjectCacheRead> {
333 self.get_object_cache_reader()
334 }
335
336 fn get_object_store(&self) -> &Arc<dyn ObjectStore + Send + Sync> {
337 self.get_object_store()
338 }
339
340 fn get_backing_package_store(&self) -> &Arc<dyn BackingPackageStore + Send + Sync> {
341 self.get_backing_package_store()
342 }
343
344 fn get_owner_objects(
345 &self,
346 owner: SuiAddress,
347 cursor: Option<ObjectID>,
348 filter: Option<SuiObjectDataFilter>,
349 ) -> StateReadResult<Vec<ObjectInfo>> {
350 Ok(self
351 .get_owner_objects_iterator(owner, cursor, filter)?
352 .collect())
353 }
354
355 async fn query_events(
356 &self,
357 kv_store: &Arc<TransactionKeyValueStore>,
358 query: EventFilter,
359 cursor: Option<EventID>,
361 limit: usize,
362 descending: bool,
363 ) -> StateReadResult<Vec<SuiEvent>> {
364 Ok(self
365 .query_events(kv_store, query, cursor, limit, descending)
366 .await?)
367 }
368
369 #[allow(clippy::type_complexity)]
370 async fn dry_exec_transaction(
371 &self,
372 transaction: TransactionData,
373 ) -> StateReadResult<(
374 DryRunTransactionBlockResponse,
375 BTreeMap<ObjectID, (ObjectRef, Object, WriteKind)>,
376 TransactionEffects,
377 Option<ObjectID>,
378 )> {
379 Ok(self.dry_exec_transaction(transaction).await?)
380 }
381
382 async fn dev_inspect_transaction_block(
383 &self,
384 sender: SuiAddress,
385 transaction_kind: TransactionKind,
386 gas_price: Option<u64>,
387 gas_budget: Option<u64>,
388 gas_sponsor: Option<SuiAddress>,
389 gas_objects: Option<Vec<ObjectRef>>,
390 show_raw_txn_data_and_effects: Option<bool>,
391 skip_checks: Option<bool>,
392 ) -> StateReadResult<DevInspectResults> {
393 Ok(self
394 .dev_inspect_transaction_block(
395 sender,
396 transaction_kind,
397 gas_price,
398 gas_budget,
399 gas_sponsor,
400 gas_objects,
401 show_raw_txn_data_and_effects,
402 skip_checks,
403 )
404 .await?)
405 }
406
407 fn get_subscription_handler(&self) -> Arc<SubscriptionHandler> {
408 self.subscription_handler.clone()
409 }
410
411 fn get_owner_objects_with_limit(
412 &self,
413 owner: SuiAddress,
414 cursor: Option<ObjectID>,
415 limit: usize,
416 filter: Option<SuiObjectDataFilter>,
417 ) -> StateReadResult<Vec<ObjectInfo>> {
418 Ok(self.get_owner_objects(owner, cursor, limit, filter)?)
419 }
420
421 async fn get_transactions(
422 &self,
423 kv_store: &Arc<TransactionKeyValueStore>,
424 filter: Option<TransactionFilter>,
425 cursor: Option<TransactionDigest>,
426 limit: Option<usize>,
427 reverse: bool,
428 ) -> StateReadResult<Vec<TransactionDigest>> {
429 Ok(self
430 .get_transactions(kv_store, filter, cursor, limit, reverse)
431 .await?)
432 }
433
434 fn get_dynamic_field_object_id(
435 &self,
437 owner: ObjectID,
438 name_type: TypeTag,
439 name_bcs_bytes: &[u8],
440 ) -> StateReadResult<Option<ObjectID>> {
441 Ok(self.get_dynamic_field_object_id(owner, name_type, name_bcs_bytes)?)
442 }
443
444 async fn get_staked_sui(&self, owner: SuiAddress) -> StateReadResult<Vec<StakedSui>> {
445 Ok(self.get_move_objects(owner, MoveObjectType::staked_sui())?)
446 }
447 fn get_system_state(&self) -> StateReadResult<SuiSystemState> {
448 Ok(self
449 .get_object_cache_reader()
450 .get_sui_system_state_object_unsafe()?)
451 }
452 fn get_or_latest_committee(&self, epoch: Option<BigInt<u64>>) -> StateReadResult<Committee> {
453 Ok(self
454 .committee_store()
455 .get_or_latest_committee(epoch.map(|e| *e))?)
456 }
457
458 fn get_bridge(&self) -> StateReadResult<Bridge> {
459 self.get_cache_reader()
460 .get_bridge_object_unsafe()
461 .map_err(|err| err.into())
462 }
463
464 fn find_publish_txn_digest(&self, package_id: ObjectID) -> StateReadResult<TransactionDigest> {
465 Ok(self.find_publish_txn_digest(package_id)?)
466 }
467 fn get_owned_coins(
468 &self,
469 owner: SuiAddress,
470 cursor: (String, u64, ObjectID),
471 limit: usize,
472 one_coin_type_only: bool,
473 ) -> StateReadResult<Vec<SuiCoin>> {
474 fn to_sui_coin(key: CoinIndexKey2, info: CoinInfo) -> SuiCoin {
478 SuiCoin {
479 coin_type: key.coin_type,
480 coin_object_id: key.object_id,
481 version: info.version,
482 digest: info.digest,
483 balance: info.balance,
484 previous_transaction: info.previous_transaction,
485 }
486 }
487
488 fn obj_ref_to_sui_coin(
489 coin_type: String,
490 obj_ref: ObjectRef,
491 balance: u64,
492 previous_transaction: TransactionDigest,
493 ) -> SuiCoin {
494 SuiCoin {
495 coin_type,
496 coin_object_id: obj_ref.0,
497 version: obj_ref.1,
498 digest: obj_ref.2,
499 balance,
500 previous_transaction,
501 }
502 }
503
504 let coin_reservations_enabled = self
506 .load_epoch_store_one_call_per_task()
507 .protocol_config()
508 .enable_coin_reservation_obj_refs();
509
510 let fake_coins: HashMap<String, SuiCoin> = if !coin_reservations_enabled {
511 HashMap::new()
512 } else if one_coin_type_only {
513 let balance_type_tag = sui_types::parse_sui_type_tag(&cursor.0)
514 .map_err(|e| anyhow::anyhow!("Invalid coin type: {} - {}", cursor.0, e))?;
515 let balance_type = Balance::type_tag(balance_type_tag);
516 self.get_address_balance_coin_info(owner, balance_type)?
517 .map(|(obj_ref, balance, prev_tx)| {
518 HashMap::from([(
519 cursor.0.clone(),
520 obj_ref_to_sui_coin(cursor.0.clone(), obj_ref, balance, prev_tx),
521 )])
522 })
523 .unwrap_or_default()
524 } else {
525 self.get_all_address_balance_coin_infos(owner)?
526 .into_iter()
527 .map(|(coin_type, (obj_ref, balance, prev_tx))| {
528 (
529 coin_type.clone(),
530 obj_ref_to_sui_coin(coin_type, obj_ref, balance, prev_tx),
531 )
532 })
533 .collect()
534 };
535
536 let cursor_at_fake = fake_coins.values().any(|c| c.coin_object_id == cursor.2);
538
539 let (real_cursor, skip_first_real) = if cursor_at_fake {
541 ((cursor.0.clone(), 0, ObjectID::ZERO), true)
542 } else {
543 (cursor.clone(), false)
544 };
545
546 let real_coins_iter = self.get_owned_coins_iterator_with_cursor(
547 owner,
548 real_cursor.clone(),
549 limit + 1,
550 one_coin_type_only,
551 )?;
552
553 let mut fake_emitted: HashMap<String, bool> = HashMap::new();
555
556 let mut emit_fake_before_reals = false;
561 if cursor.2 != ObjectID::ZERO && !cursor_at_fake && fake_coins.contains_key(&cursor.0) {
562 let first_real_id = self
564 .get_owned_coins_iterator_with_cursor(
565 owner,
566 (cursor.0.clone(), 0, ObjectID::ZERO),
567 1,
568 one_coin_type_only,
569 )?
570 .next()
571 .map(|(k, _)| k.object_id);
572
573 if first_real_id == Some(cursor.2) {
574 emit_fake_before_reals = true;
576 } else {
577 fake_emitted.insert(cursor.0.clone(), true);
579 }
580 }
581
582 let mut result = Vec::with_capacity(limit);
583
584 if emit_fake_before_reals && let Some(fake) = fake_coins.get(&cursor.0) {
586 result.push(fake.clone());
587 fake_emitted.insert(cursor.0.clone(), true);
588 }
589
590 let mut seen_first_real: HashMap<String, bool> = HashMap::new();
591 let mut skipped_first = false;
592
593 for (key, info) in real_coins_iter {
594 if result.len() >= limit {
595 break;
596 }
597
598 let coin = to_sui_coin(key, info);
599 let coin_type = &coin.coin_type;
600 let is_first_real = !seen_first_real.get(coin_type).copied().unwrap_or(false);
601
602 if skip_first_real && !skipped_first && coin_type == &real_cursor.0 {
604 skipped_first = true;
605 seen_first_real.insert(coin_type.clone(), true);
606 continue;
607 }
608
609 result.push(coin.clone());
611 seen_first_real.insert(coin_type.clone(), true);
612
613 if is_first_real && !fake_emitted.get(coin_type).copied().unwrap_or(false) {
615 if let Some(fake) = fake_coins.get(coin_type)
616 && result.len() < limit
617 {
618 result.push(fake.clone());
619 }
620 fake_emitted.insert(coin_type.clone(), true);
621 }
622 }
623
624 if cursor.2 == ObjectID::ZERO {
627 for (coin_type, fake) in fake_coins {
628 if result.len() >= limit {
629 break;
630 }
631 if !fake_emitted.get(&coin_type).copied().unwrap_or(false) {
632 result.push(fake);
633 }
634 }
635 }
636
637 Ok(result)
638 }
639
640 async fn get_executed_transaction_and_effects(
641 &self,
642 digest: TransactionDigest,
643 kv_store: Arc<TransactionKeyValueStore>,
644 ) -> StateReadResult<(Transaction, TransactionEffects)> {
645 Ok(self
646 .get_executed_transaction_and_effects(digest, kv_store)
647 .await?)
648 }
649
650 async fn get_balance(
651 &self,
652 owner: SuiAddress,
653 coin_type: TypeTag,
654 ) -> StateReadResult<TotalBalance> {
655 let indexes = self.indexes.clone();
656 let child_object_resolver = self.get_child_object_resolver().clone();
657 Ok(
658 tokio::task::spawn_blocking(move || -> SuiResult<TotalBalance> {
659 let address_balance =
660 get_balance(owner, child_object_resolver.as_ref(), coin_type.clone())?;
661 let coin_balance = indexes
662 .as_ref()
663 .ok_or(SuiErrorKind::IndexStoreNotAvailable)?
664 .get_coin_object_balance(owner, coin_type)?;
665 let mut total_balance = coin_balance;
666 if address_balance > 0 {
667 total_balance.balance += address_balance as i128;
668 total_balance.num_coins += 1;
669 }
670 total_balance.address_balance = address_balance;
671 Ok(total_balance)
672 })
673 .await
674 .map_err(|e: JoinError| {
675 SuiError(Box::new(SuiErrorKind::ExecutionError(e.to_string())))
676 })??,
677 )
678 }
679
680 async fn get_all_balance(
681 &self,
682 owner: SuiAddress,
683 ) -> StateReadResult<Arc<HashMap<TypeTag, TotalBalance>>> {
684 let indexes = self.indexes.clone();
685 let child_object_resolver = self.get_child_object_resolver().clone();
686 Ok(tokio::task::spawn_blocking(
687 move || -> SuiResult<Arc<HashMap<TypeTag, TotalBalance>>> {
688 let indexes = indexes
689 .as_ref()
690 .ok_or(SuiErrorKind::IndexStoreNotAvailable)?;
691 let address_balances =
692 get_all_balances_for_owner(owner, child_object_resolver.as_ref(), indexes)?;
693 let coin_balances = (*indexes.get_all_coin_object_balances(owner)?).clone();
694 let mut all_balances = coin_balances;
695 for (coin_type, balance) in address_balances {
696 let existing_balance = all_balances.entry(coin_type).or_insert(TotalBalance {
697 balance: 0,
698 num_coins: 0,
699 address_balance: 0,
700 });
701 existing_balance.balance += balance as i128;
702 existing_balance.num_coins += 1;
703 existing_balance.address_balance = balance;
704 }
705 Ok(Arc::new(all_balances))
706 },
707 )
708 .await
709 .map_err(|e: JoinError| {
710 SuiError(Box::new(SuiErrorKind::ExecutionError(e.to_string())))
711 })??)
712 }
713
714 fn get_verified_checkpoint_by_sequence_number(
715 &self,
716 sequence_number: CheckpointSequenceNumber,
717 ) -> StateReadResult<VerifiedCheckpoint> {
718 Ok(self.get_verified_checkpoint_by_sequence_number(sequence_number)?)
719 }
720
721 fn get_checkpoint_contents(
722 &self,
723 digest: CheckpointContentsDigest,
724 ) -> StateReadResult<CheckpointContents> {
725 Ok(self.get_checkpoint_contents(digest)?)
726 }
727
728 fn get_verified_checkpoint_summary_by_digest(
729 &self,
730 digest: CheckpointDigest,
731 ) -> StateReadResult<VerifiedCheckpoint> {
732 Ok(self.get_verified_checkpoint_summary_by_digest(digest)?)
733 }
734
735 fn deprecated_multi_get_transaction_checkpoint(
736 &self,
737 digests: &[TransactionDigest],
738 ) -> StateReadResult<Vec<Option<(EpochId, CheckpointSequenceNumber)>>> {
739 Ok(self
740 .get_checkpoint_cache()
741 .deprecated_multi_get_transaction_checkpoint(digests))
742 }
743
744 fn deprecated_get_transaction_checkpoint(
745 &self,
746 digest: &TransactionDigest,
747 ) -> StateReadResult<Option<(EpochId, CheckpointSequenceNumber)>> {
748 Ok(self
749 .get_checkpoint_cache()
750 .deprecated_get_transaction_checkpoint(digest))
751 }
752
753 fn multi_get_checkpoint_by_sequence_number(
754 &self,
755 sequence_numbers: &[CheckpointSequenceNumber],
756 ) -> StateReadResult<Vec<Option<VerifiedCheckpoint>>> {
757 Ok(self.multi_get_checkpoint_by_sequence_number(sequence_numbers)?)
758 }
759
760 fn get_total_transaction_blocks(&self) -> StateReadResult<u64> {
761 Ok(self.get_total_transaction_blocks()?)
762 }
763
764 fn get_checkpoint_by_sequence_number(
765 &self,
766 sequence_number: CheckpointSequenceNumber,
767 ) -> StateReadResult<Option<VerifiedCheckpoint>> {
768 Ok(self.get_checkpoint_by_sequence_number(sequence_number)?)
769 }
770
771 fn get_latest_checkpoint_sequence_number(&self) -> StateReadResult<CheckpointSequenceNumber> {
772 Ok(self.get_latest_checkpoint_sequence_number()?)
773 }
774
775 fn get_chain_identifier(&self) -> StateReadResult<ChainIdentifier> {
776 Ok(self.get_chain_identifier())
777 }
778}
779
780#[async_trait]
783impl<S: ?Sized + StateRead> ObjectProvider for Arc<S> {
784 type Error = StateReadError;
785
786 async fn get_object(
787 &self,
788 id: &ObjectID,
789 version: &SequenceNumber,
790 ) -> Result<Object, Self::Error> {
791 Ok(self.get_past_object_read(id, *version)?.into_object()?)
792 }
793
794 async fn find_object_lt_or_eq_version(
795 &self,
796 id: &ObjectID,
797 version: &SequenceNumber,
798 ) -> Result<Option<Object>, Self::Error> {
799 Ok(self
800 .get_cache_reader()
801 .find_object_lt_or_eq_version(*id, *version))
802 }
803}
804
805#[async_trait]
806impl<S: ?Sized + StateRead> ObjectProvider for (Arc<S>, Arc<TransactionKeyValueStore>) {
807 type Error = StateReadError;
808
809 async fn get_object(
810 &self,
811 id: &ObjectID,
812 version: &SequenceNumber,
813 ) -> Result<Object, Self::Error> {
814 let object_read = self.0.get_past_object_read(id, *version)?;
815 match object_read {
816 PastObjectRead::ObjectNotExists(_) | PastObjectRead::VersionNotFound(..) => {
817 match self.1.get_object(*id, *version).await? {
818 Some(object) => Ok(object),
819 None => Ok(PastObjectRead::VersionNotFound(*id, *version).into_object()?),
820 }
821 }
822 _ => Ok(object_read.into_object()?),
823 }
824 }
825
826 async fn find_object_lt_or_eq_version(
827 &self,
828 id: &ObjectID,
829 version: &SequenceNumber,
830 ) -> Result<Option<Object>, Self::Error> {
831 Ok(self
832 .0
833 .get_cache_reader()
834 .find_object_lt_or_eq_version(*id, *version))
835 }
836}
837
838#[derive(Debug, Error)]
839pub enum StateReadInternalError {
840 #[error(transparent)]
841 SuiError(#[from] SuiError),
842 #[error(transparent)]
843 JoinError(#[from] JoinError),
844 #[error(transparent)]
845 Anyhow(#[from] anyhow::Error),
846}
847
848impl From<SuiErrorKind> for StateReadInternalError {
849 fn from(e: SuiErrorKind) -> Self {
850 StateReadInternalError::SuiError(SuiError::from(e))
851 }
852}
853
854#[derive(Debug, Error)]
855pub enum StateReadClientError {
856 #[error(transparent)]
857 SuiError(#[from] SuiError),
858 #[error(transparent)]
859 UserInputError(#[from] UserInputError),
860}
861
862impl From<SuiErrorKind> for StateReadClientError {
863 fn from(e: SuiErrorKind) -> Self {
864 StateReadClientError::SuiError(SuiError::from(e))
865 }
866}
867
868#[derive(Debug, Error)]
873pub enum StateReadError {
874 #[error(transparent)]
876 Internal(#[from] StateReadInternalError),
877
878 #[error(transparent)]
880 Client(#[from] StateReadClientError),
881}
882
883impl From<SuiErrorKind> for StateReadError {
884 fn from(e: SuiErrorKind) -> Self {
885 match e {
886 SuiErrorKind::IndexStoreNotAvailable
887 | SuiErrorKind::TransactionNotFound { .. }
888 | SuiErrorKind::UnsupportedFeatureError { .. }
889 | SuiErrorKind::UserInputError { .. }
890 | SuiErrorKind::WrongMessageVersion { .. } => StateReadError::Client(e.into()),
891 _ => StateReadError::Internal(e.into()),
892 }
893 }
894}
895
896impl From<SuiError> for StateReadError {
897 fn from(e: SuiError) -> Self {
898 e.into_inner().into()
899 }
900}
901
902impl From<UserInputError> for StateReadError {
903 fn from(e: UserInputError) -> Self {
904 StateReadError::Client(e.into())
905 }
906}
907
908impl From<JoinError> for StateReadError {
909 fn from(e: JoinError) -> Self {
910 StateReadError::Internal(e.into())
911 }
912}
913
914impl From<anyhow::Error> for StateReadError {
915 fn from(e: anyhow::Error) -> Self {
916 StateReadError::Internal(e.into())
917 }
918}
919
920impl From<TypedStoreError> for StateReadError {
921 fn from(e: TypedStoreError) -> Self {
922 let error: SuiError = e.into();
923 StateReadError::Internal(error.into())
924 }
925}