1use std::sync::Arc;
6
7use iota_node_storage::{GrpcIndexes, GrpcStateReader};
8use iota_sdk_types::{
9 CheckpointContentsDigest, CheckpointDigest, StructTag, TransactionDigest, TransactionEffects,
10 TransactionEvents,
11 checkpoint::{CheckpointContents, EndOfEpochData},
12};
13use iota_types::{
14 committee::{Committee, EpochId},
15 error::IotaError,
16 messages_checkpoint::{
17 CheckpointContentsExt, CheckpointSequenceNumber, FullCheckpointContents,
18 VerifiedCheckpoint, VerifiedCheckpointContents,
19 },
20 object::Object,
21 storage::{
22 ObjectKey, ObjectStore, ReadStore, WriteStore,
23 error::{Error as StorageError, Result},
24 },
25 transaction::VerifiedTransaction,
26};
27use parking_lot::Mutex;
28use tracing::instrument;
29
30use crate::{
31 authority::AuthorityState, checkpoints::CheckpointStore,
32 epoch::committee_store::CommitteeStore, execution_cache::ExecutionCacheTraitPointers,
33 grpc_indexes::GrpcIndexesStore,
34};
35
36#[derive(Clone)]
37pub struct RocksDbStore {
38 cache_traits: ExecutionCacheTraitPointers,
39
40 committee_store: Arc<CommitteeStore>,
41 checkpoint_store: Arc<CheckpointStore>,
42 highest_verified_checkpoint: Arc<Mutex<Option<u64>>>,
47 highest_synced_checkpoint: Arc<Mutex<Option<u64>>>,
48}
49
50impl RocksDbStore {
51 pub fn new(
52 cache_traits: ExecutionCacheTraitPointers,
53 committee_store: Arc<CommitteeStore>,
54 checkpoint_store: Arc<CheckpointStore>,
55 ) -> Self {
56 Self {
57 cache_traits,
58 committee_store,
59 checkpoint_store,
60 highest_verified_checkpoint: Arc::new(Mutex::new(None)),
61 highest_synced_checkpoint: Arc::new(Mutex::new(None)),
62 }
63 }
64
65 pub fn get_objects(&self, object_keys: &[ObjectKey]) -> Result<Vec<Option<Object>>, IotaError> {
66 self.cache_traits
67 .object_cache_reader
68 .try_multi_get_objects_by_key(object_keys)
69 }
70
71 pub fn get_last_executed_checkpoint(&self) -> Result<Option<VerifiedCheckpoint>, IotaError> {
72 Ok(self.checkpoint_store.get_highest_executed_checkpoint()?)
73 }
74
75 fn update_highest_synced_checkpoints(
78 &self,
79 checkpoints: &[VerifiedCheckpoint],
80 ) -> Result<(), iota_types::storage::error::Error> {
81 let Some(last) = checkpoints.last() else {
82 return Ok(());
83 };
84 let mut locked = self.highest_synced_checkpoint.lock();
85 if locked.is_some_and(|seq| seq >= last.sequence_number) {
86 return Ok(());
87 }
88 self.checkpoint_store
89 .multi_update_highest_synced_checkpoint(checkpoints)
90 .map_err(iota_types::storage::error::Error::custom)?;
91 *locked = locked.max(Some(last.sequence_number));
92 Ok(())
93 }
94}
95
96impl ReadStore for RocksDbStore {
97 fn try_get_checkpoint_by_digest(
98 &self,
99 digest: &CheckpointDigest,
100 ) -> Result<Option<VerifiedCheckpoint>, StorageError> {
101 self.checkpoint_store
102 .get_checkpoint_by_digest(digest)
103 .map_err(Into::into)
104 }
105
106 fn try_get_checkpoint_by_sequence_number(
107 &self,
108 sequence_number: CheckpointSequenceNumber,
109 ) -> Result<Option<VerifiedCheckpoint>, StorageError> {
110 self.checkpoint_store
111 .get_checkpoint_by_sequence_number(sequence_number)
112 .map_err(Into::into)
113 }
114
115 fn try_get_highest_verified_checkpoint(&self) -> Result<VerifiedCheckpoint, StorageError> {
116 self.checkpoint_store
117 .get_highest_verified_checkpoint()
118 .map(|maybe_checkpoint| {
119 maybe_checkpoint
120 .expect("storage should have been initialized with genesis checkpoint")
121 })
122 .map_err(Into::into)
123 }
124
125 fn try_get_highest_verified_checkpoint_seq_number(
126 &self,
127 ) -> Result<CheckpointSequenceNumber, StorageError> {
128 Ok(self
129 .checkpoint_store
130 .get_highest_verified_checkpoint_seq_number()?
131 .expect("storage should have been initialized with genesis checkpoint"))
132 }
133
134 fn try_get_highest_synced_checkpoint(&self) -> Result<VerifiedCheckpoint, StorageError> {
135 self.checkpoint_store
136 .get_highest_synced_checkpoint()
137 .map(|maybe_checkpoint| {
138 maybe_checkpoint
139 .expect("storage should have been initialized with genesis checkpoint")
140 })
141 .map_err(Into::into)
142 }
143
144 fn try_get_highest_synced_checkpoint_seq_number(
145 &self,
146 ) -> Result<CheckpointSequenceNumber, StorageError> {
147 Ok(self
148 .checkpoint_store
149 .get_highest_synced_checkpoint_seq_number()?
150 .expect("storage should have been initialized with genesis checkpoint"))
151 }
152
153 fn try_get_lowest_available_checkpoint(
154 &self,
155 ) -> Result<CheckpointSequenceNumber, StorageError> {
156 if let Some(highest_pruned_cp) = self
157 .checkpoint_store
158 .get_highest_pruned_checkpoint_seq_number()
159 .map_err(Into::<StorageError>::into)?
160 {
161 Ok(highest_pruned_cp + 1)
162 } else {
163 Ok(0)
164 }
165 }
166
167 fn try_get_full_checkpoint_contents_by_sequence_number(
168 &self,
169 sequence_number: CheckpointSequenceNumber,
170 ) -> Result<Option<FullCheckpointContents>, StorageError> {
171 Ok(self
172 .checkpoint_store
173 .get_full_checkpoint_contents_by_sequence_number(sequence_number)
174 .map(|contents| contents.as_ref().clone()))
175 }
176
177 fn try_get_full_checkpoint_contents(
178 &self,
179 digest: &CheckpointContentsDigest,
180 ) -> Result<Option<FullCheckpointContents>, StorageError> {
181 if let Some(contents) = self
184 .checkpoint_store
185 .get_full_checkpoint_contents_by_digest(digest)
186 {
187 return Ok(Some(contents.as_ref().clone()));
188 }
189
190 self.checkpoint_store
192 .get_checkpoint_contents(digest)
193 .map_err(iota_types::storage::error::Error::custom)?
194 .map(|contents| {
195 let mut transactions = Vec::with_capacity(contents.len());
196 for tx in contents.iter() {
197 if let (Some(t), Some(e)) = (
198 self.try_get_transaction(&tx.transaction)?,
199 self.cache_traits
200 .transaction_cache_reader
201 .try_get_effects(&tx.effects)
202 .map_err(iota_types::storage::error::Error::custom)?,
203 ) {
204 transactions.push(iota_types::base_types::ExecutionData::new(
205 (*t).clone().into_inner(),
206 e,
207 ))
208 } else {
209 return Result::<
210 Option<FullCheckpointContents>,
211 iota_types::storage::error::Error,
212 >::Ok(None);
213 }
214 }
215 Ok(Some(
216 FullCheckpointContents::from_contents_and_execution_data(
217 contents,
218 transactions.into_iter(),
219 ),
220 ))
221 })
222 .transpose()
223 .map(|contents| contents.flatten())
224 .map_err(iota_types::storage::error::Error::custom)
225 }
226
227 fn try_get_committee(
228 &self,
229 epoch: EpochId,
230 ) -> Result<Option<Arc<Committee>>, iota_types::storage::error::Error> {
231 Ok(self.committee_store.get_committee(&epoch).unwrap())
232 }
233
234 fn try_get_transaction(
235 &self,
236 digest: &TransactionDigest,
237 ) -> Result<Option<Arc<VerifiedTransaction>>, StorageError> {
238 self.cache_traits
239 .transaction_cache_reader
240 .try_get_transaction_block(digest)
241 .map_err(StorageError::custom)
242 }
243
244 fn try_get_transaction_effects(
245 &self,
246 digest: &TransactionDigest,
247 ) -> Result<Option<TransactionEffects>, StorageError> {
248 self.cache_traits
249 .transaction_cache_reader
250 .try_get_executed_effects(digest)
251 .map_err(StorageError::custom)
252 }
253
254 fn try_get_events(
255 &self,
256 digest: &TransactionDigest,
257 ) -> Result<Option<TransactionEvents>, StorageError> {
258 self.cache_traits
259 .transaction_cache_reader
260 .try_get_events(digest)
261 .map_err(StorageError::custom)
262 }
263
264 fn try_get_latest_checkpoint(&self) -> iota_types::storage::error::Result<VerifiedCheckpoint> {
265 self.checkpoint_store
266 .get_highest_executed_checkpoint()
267 .map_err(iota_types::storage::error::Error::custom)?
268 .ok_or_else(|| {
269 iota_types::storage::error::Error::missing("unable to get latest checkpoint")
270 })
271 }
272
273 fn try_get_checkpoint_contents_by_digest(
274 &self,
275 digest: &CheckpointContentsDigest,
276 ) -> iota_types::storage::error::Result<Option<CheckpointContents>> {
277 self.checkpoint_store
278 .get_checkpoint_contents(digest)
279 .map_err(iota_types::storage::error::Error::custom)
280 }
281
282 fn try_get_checkpoint_contents_by_sequence_number(
283 &self,
284 sequence_number: CheckpointSequenceNumber,
285 ) -> iota_types::storage::error::Result<Option<CheckpointContents>> {
286 match self.try_get_checkpoint_by_sequence_number(sequence_number) {
287 Ok(Some(checkpoint)) => {
288 self.try_get_checkpoint_contents_by_digest(&checkpoint.contents_digest)
289 }
290 Ok(None) => Ok(None),
291 Err(e) => Err(e),
292 }
293 }
294}
295
296impl ObjectStore for RocksDbStore {
297 fn try_get_object(
298 &self,
299 object_id: &iota_sdk_types::ObjectId,
300 ) -> iota_types::storage::error::Result<Option<Object>> {
301 self.cache_traits.object_store.try_get_object(object_id)
302 }
303
304 fn try_get_object_by_key(
305 &self,
306 object_id: &iota_sdk_types::ObjectId,
307 version: iota_types::base_types::VersionNumber,
308 ) -> iota_types::storage::error::Result<Option<Object>> {
309 self.cache_traits
310 .object_store
311 .try_get_object_by_key(object_id, version)
312 }
313}
314
315impl WriteStore for RocksDbStore {
316 #[instrument(level = "trace", skip_all)]
317 fn try_insert_checkpoint(
318 &self,
319 checkpoint: &VerifiedCheckpoint,
320 ) -> Result<(), iota_types::storage::error::Error> {
321 if let Some(EndOfEpochData {
322 next_epoch_committee,
323 ..
324 }) = checkpoint.end_of_epoch_data.as_ref()
325 {
326 let committee = Committee::from_committee_members(
327 checkpoint.epoch().checked_add(1).unwrap(),
328 next_epoch_committee,
329 );
330 self.try_insert_committee(committee)?;
331 }
332
333 self.checkpoint_store
334 .insert_certified_checkpoint(checkpoint)
335 .map_err(iota_types::storage::error::Error::custom)?;
336 self.try_update_highest_verified_checkpoint(checkpoint)
341 }
342
343 fn try_update_highest_verified_checkpoint(
344 &self,
345 checkpoint: &VerifiedCheckpoint,
346 ) -> Result<(), iota_types::storage::error::Error> {
347 let mut locked = self.highest_verified_checkpoint.lock();
348 if locked.is_some() && locked.unwrap() >= checkpoint.sequence_number {
349 return Ok(());
350 }
351 self.checkpoint_store
352 .update_highest_verified_checkpoint(checkpoint)
353 .map_err(iota_types::storage::error::Error::custom)?;
354 *locked = locked.max(Some(checkpoint.sequence_number));
355 Ok(())
356 }
357
358 fn try_update_highest_synced_checkpoint(
359 &self,
360 checkpoint: &VerifiedCheckpoint,
361 ) -> Result<(), iota_types::storage::error::Error> {
362 self.update_highest_synced_checkpoints(std::slice::from_ref(checkpoint))
363 }
364
365 fn try_insert_checkpoint_contents(
366 &self,
367 checkpoint: &VerifiedCheckpoint,
368 contents: VerifiedCheckpointContents,
369 ) -> Result<(), iota_types::storage::error::Error> {
370 self.cache_traits
371 .state_sync_store
372 .try_multi_insert_transaction_and_effects(contents.transactions())
373 .map_err(iota_types::storage::error::Error::custom)?;
374 self.checkpoint_store
375 .insert_verified_checkpoint_contents(checkpoint, contents)
376 .map_err(Into::into)
377 }
378
379 fn try_insert_committee(
380 &self,
381 new_committee: Committee,
382 ) -> Result<(), iota_types::storage::error::Error> {
383 self.committee_store
384 .insert_new_committee(&new_committee)
385 .unwrap();
386 Ok(())
387 }
388
389 fn try_get_highest_executed_checkpoint_seq_number(
390 &self,
391 ) -> Result<Option<CheckpointSequenceNumber>, iota_types::storage::error::Error> {
392 self.checkpoint_store
393 .get_highest_executed_checkpoint_seq_number()
394 .map_err(Into::into)
395 }
396
397 async fn wait_for_executed_checkpoint(&self, sequence_number: CheckpointSequenceNumber) {
398 self.checkpoint_store
399 .notify_read_executed_checkpoint(sequence_number)
400 .await;
401 }
402
403 fn try_insert_synced_checkpoints(
404 &self,
405 checkpoints: Vec<(VerifiedCheckpoint, VerifiedCheckpointContents)>,
406 ) -> Result<(), iota_types::storage::error::Error> {
407 let summaries: Vec<VerifiedCheckpoint> = checkpoints
408 .iter()
409 .map(|(checkpoint, _)| checkpoint.clone())
410 .collect();
411 let Some(last) = summaries.last() else {
412 return Ok(());
413 };
414
415 for checkpoint in &summaries {
416 if let Some(EndOfEpochData {
417 next_epoch_committee,
418 ..
419 }) = checkpoint.end_of_epoch_data.as_ref()
420 {
421 let committee = Committee::from_committee_members(
422 checkpoint.epoch().checked_add(1).unwrap(),
423 next_epoch_committee,
424 );
425 self.try_insert_committee(committee)?;
426 }
427 }
428
429 self.checkpoint_store
430 .multi_insert_certified_checkpoints(&summaries)?;
431 self.try_update_highest_verified_checkpoint(last)?;
432
433 for (_, contents) in &checkpoints {
436 self.cache_traits
437 .state_sync_store
438 .try_multi_insert_transaction_and_effects(contents.transactions())
439 .map_err(iota_types::storage::error::Error::custom)?;
440 }
441 self.checkpoint_store
442 .multi_insert_verified_checkpoint_contents(checkpoints)?;
443
444 self.update_highest_synced_checkpoints(&summaries)
445 }
446}
447
448pub struct GrpcReadStore {
449 state: Arc<AuthorityState>,
450 rocks: RocksDbStore,
451}
452
453impl GrpcReadStore {
454 pub fn new(state: Arc<AuthorityState>, rocks: RocksDbStore) -> Self {
455 Self { state, rocks }
456 }
457
458 fn grpc_indexes_store(&self) -> iota_types::storage::error::Result<&GrpcIndexesStore> {
459 self.state.grpc_indexes_store.as_deref().ok_or_else(|| {
460 iota_types::storage::error::Error::custom("gRPC index store is disabled")
461 })
462 }
463}
464
465impl ObjectStore for GrpcReadStore {
466 fn try_get_object(
467 &self,
468 object_id: &iota_sdk_types::ObjectId,
469 ) -> iota_types::storage::error::Result<Option<Object>> {
470 self.rocks.try_get_object(object_id)
471 }
472
473 fn try_get_object_by_key(
474 &self,
475 object_id: &iota_sdk_types::ObjectId,
476 version: iota_types::base_types::VersionNumber,
477 ) -> iota_types::storage::error::Result<Option<Object>> {
478 self.rocks.try_get_object_by_key(object_id, version)
479 }
480}
481
482impl ReadStore for GrpcReadStore {
483 fn try_get_committee(
484 &self,
485 epoch: EpochId,
486 ) -> iota_types::storage::error::Result<Option<Arc<Committee>>> {
487 self.rocks.try_get_committee(epoch)
488 }
489
490 fn try_get_latest_checkpoint(&self) -> iota_types::storage::error::Result<VerifiedCheckpoint> {
491 self.rocks.try_get_latest_checkpoint()
492 }
493
494 fn try_get_highest_verified_checkpoint(
495 &self,
496 ) -> iota_types::storage::error::Result<VerifiedCheckpoint> {
497 self.rocks.try_get_highest_verified_checkpoint()
498 }
499
500 fn try_get_highest_verified_checkpoint_seq_number(
501 &self,
502 ) -> iota_types::storage::error::Result<CheckpointSequenceNumber> {
503 self.rocks.try_get_highest_verified_checkpoint_seq_number()
504 }
505
506 fn try_get_highest_synced_checkpoint(
507 &self,
508 ) -> iota_types::storage::error::Result<VerifiedCheckpoint> {
509 self.rocks.try_get_highest_synced_checkpoint()
510 }
511
512 fn try_get_highest_synced_checkpoint_seq_number(
513 &self,
514 ) -> iota_types::storage::error::Result<CheckpointSequenceNumber> {
515 self.rocks.try_get_highest_synced_checkpoint_seq_number()
516 }
517
518 fn try_get_lowest_available_checkpoint(
519 &self,
520 ) -> iota_types::storage::error::Result<CheckpointSequenceNumber> {
521 self.rocks.try_get_lowest_available_checkpoint()
522 }
523
524 fn try_get_checkpoint_by_digest(
525 &self,
526 digest: &CheckpointDigest,
527 ) -> iota_types::storage::error::Result<Option<VerifiedCheckpoint>> {
528 self.rocks.try_get_checkpoint_by_digest(digest)
529 }
530
531 fn try_get_checkpoint_by_sequence_number(
532 &self,
533 sequence_number: CheckpointSequenceNumber,
534 ) -> iota_types::storage::error::Result<Option<VerifiedCheckpoint>> {
535 self.rocks
536 .try_get_checkpoint_by_sequence_number(sequence_number)
537 }
538
539 fn try_get_checkpoint_contents_by_digest(
540 &self,
541 digest: &CheckpointContentsDigest,
542 ) -> iota_types::storage::error::Result<Option<CheckpointContents>> {
543 self.rocks.try_get_checkpoint_contents_by_digest(digest)
544 }
545
546 fn try_get_checkpoint_contents_by_sequence_number(
547 &self,
548 sequence_number: CheckpointSequenceNumber,
549 ) -> iota_types::storage::error::Result<Option<CheckpointContents>> {
550 self.rocks
551 .try_get_checkpoint_contents_by_sequence_number(sequence_number)
552 }
553
554 fn try_get_transaction(
555 &self,
556 digest: &TransactionDigest,
557 ) -> iota_types::storage::error::Result<Option<Arc<VerifiedTransaction>>> {
558 self.rocks.try_get_transaction(digest)
559 }
560
561 fn try_get_transaction_effects(
562 &self,
563 digest: &TransactionDigest,
564 ) -> iota_types::storage::error::Result<Option<TransactionEffects>> {
565 self.rocks.try_get_transaction_effects(digest)
566 }
567
568 fn try_get_events(
569 &self,
570 digest: &TransactionDigest,
571 ) -> iota_types::storage::error::Result<Option<TransactionEvents>> {
572 self.rocks.try_get_events(digest)
573 }
574
575 fn try_get_full_checkpoint_contents_by_sequence_number(
576 &self,
577 sequence_number: CheckpointSequenceNumber,
578 ) -> iota_types::storage::error::Result<Option<FullCheckpointContents>> {
579 self.rocks
580 .try_get_full_checkpoint_contents_by_sequence_number(sequence_number)
581 }
582
583 fn try_get_full_checkpoint_contents(
584 &self,
585 digest: &CheckpointContentsDigest,
586 ) -> iota_types::storage::error::Result<Option<FullCheckpointContents>> {
587 self.rocks.try_get_full_checkpoint_contents(digest)
588 }
589}
590
591impl GrpcStateReader for GrpcReadStore {
592 fn get_lowest_available_checkpoint_objects(
593 &self,
594 ) -> iota_types::storage::error::Result<CheckpointSequenceNumber> {
595 Ok(self
596 .state
597 .get_object_cache_reader()
598 .try_get_highest_pruned_checkpoint()
599 .map_err(StorageError::custom)?
600 .map(|cp| cp + 1)
601 .unwrap_or(0))
602 }
603
604 fn get_chain_identifier(&self) -> Result<iota_types::digests::ChainIdentifier> {
605 Ok(self.state.get_chain_identifier())
606 }
607
608 fn get_epoch_last_checkpoint(
609 &self,
610 epoch_id: EpochId,
611 ) -> iota_types::storage::error::Result<Option<VerifiedCheckpoint>> {
612 self.rocks
613 .checkpoint_store
614 .get_epoch_last_checkpoint(epoch_id)
615 .map_err(iota_types::storage::error::Error::custom)
616 }
617
618 fn get_epoch_info(
619 &self,
620 epoch: EpochId,
621 ) -> iota_types::storage::error::Result<Option<iota_types::storage::EpochInfoV2>> {
622 self.rocks
623 .checkpoint_store
624 .get_epoch_info(epoch)
625 .map_err(iota_types::storage::error::Error::custom)
626 }
627
628 fn grpc_indexes(&self) -> Option<&dyn GrpcIndexes> {
629 self.grpc_indexes_store().ok().map(|index| index as _)
630 }
631
632 fn get_struct_layout(
633 &self,
634 struct_tag: &StructTag,
635 ) -> Result<Option<move_core_types::annotated_value::MoveTypeLayout>> {
636 self.state
637 .load_epoch_store_one_call_per_task()
638 .executor()
639 .type_layout_resolver(Box::new(self.state.get_backing_package_store().as_ref()))
641 .get_annotated_layout(struct_tag)
642 .map(|layout| layout.into_layout())
643 .map(Some)
644 .map_err(StorageError::custom)
645 }
646}