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