1use std::{iter, mem, sync::Arc, thread};
6
7use either::Either;
8use fastcrypto::hash::{HashFunction, Sha3_256};
9use futures::stream::FuturesUnordered;
10use iota_common::sync::notify_read::NotifyRead;
11use iota_config::{migration_tx_data::MigrationTxData, node::AuthorityStorePruningConfig};
12use iota_genesis_common::MigrationTxDataExt;
13use iota_macros::fail_point_arg;
14use iota_sdk_types::{TransactionEffects, TransactionEvents, Version};
15use iota_storage::mutex_table::{MutexGuard, MutexTable};
16use iota_types::{
17 base_types::VerifiedExecutionData,
18 effects::TransactionEffectsExt,
19 error::UserInputError,
20 execution::TypeLayoutStore,
21 fp_bail, fp_ensure,
22 global_state_hash::GlobalStateHash,
23 iota_system_state::{
24 get_iota_system_state, iota_system_state_summary::IotaSystemStateSummaryV2,
25 },
26 messages_checkpoint::CheckpointContentsExt,
27 storage::{
28 BackingPackageStore, MarkerValue, ObjectKey, ObjectOrTombstone, ObjectStore, get_module,
29 },
30};
31use itertools::izip;
32use move_core_types::resolver::ModuleResolver;
33use tokio::time::Instant;
34use tracing::{debug, info, trace};
35use typed_store::{
36 TypedStoreError,
37 rocks::{DBBatch, DBMap},
38 traits::Map,
39};
40
41use super::{
42 authority_store_tables::{AuthorityPerpetualTables, LiveObject},
43 *,
44};
45use crate::{
46 authority::{
47 authority_per_epoch_store::{AuthorityPerEpochStore, LockDetails},
48 authority_store_pruner::{
49 AuthorityStorePruner, AuthorityStorePruningMetrics, EPOCH_DURATION_MS_FOR_TESTING,
50 },
51 authority_store_tables::TotalIotaSupplyCheck,
52 authority_store_types::{StoreObject, StoreObjectWrapper, get_store_object},
53 epoch_start_configuration::{EpochFlag, EpochStartConfiguration},
54 },
55 global_state_hasher::GlobalStateHashStore,
56 grpc_indexes::GrpcIndexesStore,
57 transaction_outputs::TransactionOutputs,
58};
59
60const NUM_SHARDS: usize = 4096;
61
62struct AuthorityStoreMetrics {
63 iota_conservation_check_latency: IntGauge,
64 iota_conservation_live_object_count: IntGauge,
65 iota_conservation_live_object_size: IntGauge,
66 iota_conservation_imbalance: IntGauge,
67 iota_conservation_storage_fund: IntGauge,
68 iota_conservation_storage_fund_imbalance: IntGauge,
69 epoch_flags: IntGaugeVec,
70}
71
72impl AuthorityStoreMetrics {
73 pub fn new(registry: &Registry) -> Self {
74 Self {
75 iota_conservation_check_latency: register_int_gauge_with_registry!(
76 "iota_conservation_check_latency",
77 "Number of seconds took to scan all live objects in the store for IOTA conservation check",
78 registry;
79 MetricLevel::Warn,
80 ).unwrap(),
81 iota_conservation_live_object_count: register_int_gauge_with_registry!(
82 "iota_conservation_live_object_count",
83 "Number of live objects in the store",
84 registry;
85 MetricLevel::Warn,
86 ).unwrap(),
87 iota_conservation_live_object_size: register_int_gauge_with_registry!(
88 "iota_conservation_live_object_size",
89 "Size in bytes of live objects in the store",
90 registry;
91 MetricLevel::Warn,
92 ).unwrap(),
93 iota_conservation_imbalance: register_int_gauge_with_registry!(
94 "iota_conservation_imbalance",
95 "Total amount of IOTA in the network - 10B * 10^9. This delta shows the amount of imbalance",
96 registry;
97 MetricLevel::Warn,
98 ).unwrap(),
99 iota_conservation_storage_fund: register_int_gauge_with_registry!(
100 "iota_conservation_storage_fund",
101 "Storage Fund pool balance (only includes the storage fund proper that represents object storage)",
102 registry;
103 MetricLevel::Warn,
104 ).unwrap(),
105 iota_conservation_storage_fund_imbalance: register_int_gauge_with_registry!(
106 "iota_conservation_storage_fund_imbalance",
107 "Imbalance of storage fund, computed with storage_fund_balance - total_object_storage_rebates",
108 registry;
109 MetricLevel::Warn,
110 ).unwrap(),
111 epoch_flags: register_int_gauge_vec_with_registry!(
112 "epoch_flags",
113 "Local flags of the currently running epoch",
114 &["flag"],
115 registry,
116 ).unwrap(),
117 }
118 }
119}
120
121pub struct AuthorityStore {
130 mutex_table: MutexTable<ObjectDigest>,
132
133 pub(crate) perpetual_tables: Arc<AuthorityPerpetualTables>,
134
135 pub(crate) root_state_notify_read:
136 NotifyRead<EpochId, (CheckpointSequenceNumber, GlobalStateHash)>,
137
138 enable_epoch_iota_conservation_check: bool,
140
141 metrics: AuthorityStoreMetrics,
142}
143
144pub type ExecutionLockReadGuard<'a> = tokio::sync::RwLockReadGuard<'a, EpochId>;
145pub type ExecutionLockWriteGuard<'a> = tokio::sync::RwLockWriteGuard<'a, EpochId>;
146
147impl AuthorityStore {
148 pub async fn open(
151 perpetual_tables: Arc<AuthorityPerpetualTables>,
152 genesis: &Genesis,
153 config: &NodeConfig,
154 registry: &Registry,
155 migration_tx_data: Option<&MigrationTxData>,
156 ) -> IotaResult<Arc<Self>> {
157 let enable_epoch_iota_conservation_check = config
158 .expensive_safety_check_config
159 .enable_epoch_iota_conservation_check();
160
161 let epoch_start_configuration = if perpetual_tables.database_is_empty()? {
162 info!("Creating new epoch start config from genesis");
163
164 #[cfg_attr(not(any(msim, fail_points)), expect(unused_mut))]
165 let mut initial_epoch_flags = EpochFlag::default_flags_for_new_epoch(config);
166 fail_point_arg!("initial_epoch_flags", |flags: Vec<EpochFlag>| {
167 info!("Setting initial epoch flags to {:?}", flags);
168 initial_epoch_flags = flags;
169 });
170
171 let epoch_start_configuration = EpochStartConfiguration::new(
172 genesis.iota_system_object().into_epoch_start_state(),
173 *genesis.checkpoint().digest(),
174 &genesis.objects(),
175 initial_epoch_flags,
176 )?;
177 perpetual_tables.set_epoch_start_configuration(&epoch_start_configuration)?;
178 epoch_start_configuration
179 } else {
180 info!("Loading epoch start config from DB");
181 perpetual_tables
182 .epoch_start_configuration
183 .get(&())?
184 .expect("Epoch start configuration must be set in non-empty DB")
185 };
186 let cur_epoch = perpetual_tables.get_recovery_epoch_at_restart()?;
187 info!("Epoch start config: {:?}", epoch_start_configuration);
188 info!("Cur epoch: {:?}", cur_epoch);
189 let this = Self::open_inner(
190 genesis,
191 perpetual_tables,
192 enable_epoch_iota_conservation_check,
193 registry,
194 migration_tx_data,
195 )
196 .await?;
197 this.update_epoch_flags_metrics(&[], epoch_start_configuration.flags());
198 Ok(this)
199 }
200
201 pub fn update_epoch_flags_metrics(&self, old: &[EpochFlag], new: &[EpochFlag]) {
202 for flag in old {
203 self.metrics
204 .epoch_flags
205 .with_label_values(&[&flag.to_string()])
206 .set(0);
207 }
208 for flag in new {
209 self.metrics
210 .epoch_flags
211 .with_label_values(&[&flag.to_string()])
212 .set(1);
213 }
214 }
215
216 pub fn clear_object_per_epoch_marker_table(
220 &self,
221 _execution_guard: &ExecutionLockWriteGuard<'_>,
222 ) -> IotaResult<()> {
223 Ok(self
228 .perpetual_tables
229 .object_per_epoch_marker_table
230 .schedule_delete_all()?)
231 }
232
233 pub async fn open_with_committee_for_testing(
234 perpetual_tables: Arc<AuthorityPerpetualTables>,
235 committee: &Committee,
236 genesis: &Genesis,
237 ) -> IotaResult<Arc<Self>> {
238 assert_eq!(committee.epoch, 0);
241 Self::open_inner(genesis, perpetual_tables, true, &Registry::new(), None).await
242 }
243
244 async fn open_inner(
245 genesis: &Genesis,
246 perpetual_tables: Arc<AuthorityPerpetualTables>,
247 enable_epoch_iota_conservation_check: bool,
248 registry: &Registry,
249 migration_tx_data: Option<&MigrationTxData>,
250 ) -> IotaResult<Arc<Self>> {
251 let store = Arc::new(Self {
252 mutex_table: MutexTable::new(NUM_SHARDS),
253 perpetual_tables,
254 root_state_notify_read: NotifyRead::<
255 EpochId,
256 (CheckpointSequenceNumber, GlobalStateHash),
257 >::new(),
258 enable_epoch_iota_conservation_check,
259 metrics: AuthorityStoreMetrics::new(registry),
260 });
261 if store
263 .database_is_empty()
264 .expect("database read should not fail at init.")
265 {
266 store
269 .bulk_insert_genesis_objects(genesis.objects())
270 .expect("cannot bulk insert genesis objects");
271
272 let transaction = VerifiedTransaction::new_unchecked(genesis.transaction().clone());
274 store
275 .perpetual_tables
276 .transactions
277 .insert(transaction.digest(), transaction.serializable_ref())
278 .expect("cannot insert genesis transaction");
279 store
280 .perpetual_tables
281 .effects
282 .insert(&genesis.effects().digest(), genesis.effects())
283 .expect("cannot insert genesis effects");
284
285 if genesis.effects().events_digest().is_some() {
290 store
291 .perpetual_tables
292 .events_2
293 .insert(transaction.digest(), genesis.events())
294 .unwrap();
295 }
296
297 if let Some(migration_transactions) = migration_tx_data {
299 let txs_data = migration_transactions.txs_data();
302
303 for (_, execution_digest) in genesis
307 .checkpoint_contents()
308 .enumerate_transactions(&genesis.checkpoint())
309 {
310 let tx_digest = &execution_digest.transaction;
311 if tx_digest == genesis.transaction().digest() {
314 continue;
315 }
316 let Some((tx, effects, events)) = txs_data.get(tx_digest) else {
319 panic!("tx digest not found in migrated objects blob");
320 };
321 let transaction = VerifiedTransaction::new_unchecked(tx.clone());
322 let objects = migration_transactions
323 .objects_by_tx_digest(*tx_digest)
324 .expect("the migration data is corrupted");
325 store
326 .bulk_insert_genesis_objects(&objects)
327 .expect("cannot bulk insert migrated objects");
328 store
329 .perpetual_tables
330 .transactions
331 .insert(transaction.digest(), transaction.serializable_ref())
332 .expect("cannot insert migration transaction");
333 store
334 .perpetual_tables
335 .effects
336 .insert(&effects.digest(), effects)
337 .expect("cannot insert migration effects");
338 if effects.events_digest().is_some() {
339 store
340 .perpetual_tables
341 .events_2
342 .insert(transaction.digest(), events)
343 .unwrap();
344 }
345 }
346 }
347 }
348
349 Ok(store)
350 }
351
352 pub fn open_no_genesis(
356 perpetual_tables: Arc<AuthorityPerpetualTables>,
357 enable_epoch_iota_conservation_check: bool,
358 registry: &Registry,
359 ) -> IotaResult<Arc<Self>> {
360 let store = Arc::new(Self {
361 mutex_table: MutexTable::new(NUM_SHARDS),
362 perpetual_tables,
363 root_state_notify_read: NotifyRead::<
364 EpochId,
365 (CheckpointSequenceNumber, GlobalStateHash),
366 >::new(),
367 enable_epoch_iota_conservation_check,
368 metrics: AuthorityStoreMetrics::new(registry),
369 });
370 Ok(store)
371 }
372
373 pub fn get_recovery_epoch_at_restart(&self) -> IotaResult<EpochId> {
374 self.perpetual_tables.get_recovery_epoch_at_restart()
375 }
376
377 pub fn get_effects(
378 &self,
379 effects_digest: &TransactionEffectsDigest,
380 ) -> IotaResult<Option<TransactionEffects>> {
381 Ok(self.perpetual_tables.effects.get(effects_digest)?)
382 }
383
384 pub fn effects_exists(&self, effects_digest: &TransactionEffectsDigest) -> IotaResult<bool> {
386 self.perpetual_tables
387 .effects
388 .contains_key(effects_digest)
389 .map_err(|e| e.into())
390 }
391
392 pub fn get_events(
393 &self,
394 digest: &TransactionDigest,
395 ) -> Result<Option<TransactionEvents>, TypedStoreError> {
396 self.perpetual_tables.events_2.get(digest)
397 }
398
399 pub fn multi_get_events(
400 &self,
401 event_digests: &[TransactionDigest],
402 ) -> IotaResult<Vec<Option<TransactionEvents>>> {
403 Ok(event_digests
404 .iter()
405 .map(|digest| self.get_events(digest))
406 .collect::<Result<Vec<_>, _>>()?)
407 }
408
409 pub fn multi_get_effects<'a>(
410 &self,
411 effects_digests: impl Iterator<Item = &'a TransactionEffectsDigest>,
412 ) -> Result<Vec<Option<TransactionEffects>>, TypedStoreError> {
413 self.perpetual_tables.effects.multi_get(effects_digests)
414 }
415
416 pub fn get_executed_effects(
417 &self,
418 tx_digest: &TransactionDigest,
419 ) -> Result<Option<TransactionEffects>, TypedStoreError> {
420 let effects_digest = self.perpetual_tables.executed_effects.get(tx_digest)?;
421 match effects_digest {
422 Some(digest) => Ok(self.perpetual_tables.effects.get(&digest)?),
423 None => Ok(None),
424 }
425 }
426
427 pub fn multi_get_executed_effects_digests(
431 &self,
432 digests: &[TransactionDigest],
433 ) -> Result<Vec<Option<TransactionEffectsDigest>>, TypedStoreError> {
434 self.perpetual_tables.executed_effects.multi_get(digests)
435 }
436
437 pub fn multi_get_executed_effects(
441 &self,
442 digests: &[TransactionDigest],
443 ) -> Result<Vec<Option<TransactionEffects>>, TypedStoreError> {
444 let executed_effects_digests = self.perpetual_tables.executed_effects.multi_get(digests)?;
445 let effects = self.multi_get_effects(executed_effects_digests.iter().flatten())?;
446 let mut tx_to_effects_map = effects
447 .into_iter()
448 .flatten()
449 .map(|effects| (*effects.transaction_digest(), effects))
450 .collect::<HashMap<_, _>>();
451 Ok(digests
452 .iter()
453 .map(|digest| tx_to_effects_map.remove(digest))
454 .collect())
455 }
456
457 pub fn is_tx_already_executed(&self, digest: &TransactionDigest) -> IotaResult<bool> {
458 Ok(self
459 .perpetual_tables
460 .executed_effects
461 .contains_key(digest)?)
462 }
463
464 pub fn get_marker_value(
465 &self,
466 object_id: &ObjectId,
467 version: &Version,
468 epoch_id: EpochId,
469 ) -> IotaResult<Option<MarkerValue>> {
470 let object_key = (epoch_id, ObjectKey(*object_id, *version));
471 Ok(self
472 .perpetual_tables
473 .object_per_epoch_marker_table
474 .get(&object_key)?)
475 }
476
477 pub fn get_latest_marker(
478 &self,
479 object_id: &ObjectId,
480 epoch_id: EpochId,
481 ) -> IotaResult<Option<(Version, MarkerValue)>> {
482 let marker_entry = self
483 .perpetual_tables
484 .object_per_epoch_marker_table
485 .safe_iter_with_prefix_reversed(&(epoch_id, *object_id))
486 .next();
487 match marker_entry {
488 Some(Ok(((epoch, key), marker))) => {
489 assert_eq!(epoch, epoch_id);
491 assert_eq!(key.0, *object_id);
492 Ok(Some((key.1, marker)))
493 }
494 Some(Err(e)) => Err(e.into()),
495 None => Ok(None),
496 }
497 }
498
499 pub async fn notify_read_root_state_hash(
502 &self,
503 epoch: EpochId,
504 ) -> IotaResult<(CheckpointSequenceNumber, GlobalStateHash)> {
505 let registration = self.root_state_notify_read.register_one(&epoch);
508 let hash = self.perpetual_tables.root_state_hash_by_epoch.get(&epoch)?;
509
510 let result = match hash {
511 Some(ready) => Either::Left(futures::future::ready(ready)),
513 None => Either::Right(registration),
514 }
515 .await;
516
517 Ok(result)
518 }
519
520 pub(crate) fn insert_finalized_transactions_perpetual_checkpoints(
522 &self,
523 digests: &[TransactionDigest],
524 epoch: EpochId,
525 sequence: CheckpointSequenceNumber,
526 ) -> IotaResult {
527 let mut batch = self
528 .perpetual_tables
529 .executed_transactions_to_checkpoint
530 .batch();
531 batch.insert_batch(
532 &self.perpetual_tables.executed_transactions_to_checkpoint,
533 digests.iter().map(|d| (*d, (epoch, sequence))),
534 )?;
535 batch.write()?;
536 trace!("Transactions {digests:?} finalized at checkpoint {sequence} epoch {epoch}");
537 Ok(())
538 }
539
540 pub(crate) fn get_transaction_perpetual_checkpoint(
542 &self,
543 digest: &TransactionDigest,
544 ) -> IotaResult<Option<(EpochId, CheckpointSequenceNumber)>> {
545 Ok(self
546 .perpetual_tables
547 .executed_transactions_to_checkpoint
548 .get(digest)?)
549 }
550
551 pub(crate) fn multi_get_transactions_perpetual_checkpoints(
553 &self,
554 digests: &[TransactionDigest],
555 ) -> IotaResult<Vec<Option<(EpochId, CheckpointSequenceNumber)>>> {
556 Ok(self
557 .perpetual_tables
558 .executed_transactions_to_checkpoint
559 .multi_get(digests)?)
560 }
561
562 pub fn database_is_empty(&self) -> IotaResult<bool> {
564 self.perpetual_tables.database_is_empty()
565 }
566
567 fn acquire_locks(&self, input_objects: &[ObjectReference]) -> Vec<MutexGuard> {
570 self.mutex_table
571 .acquire_locks(input_objects.iter().map(|object_ref| object_ref.digest))
572 }
573
574 pub fn object_exists_by_key(
575 &self,
576 object_id: &ObjectId,
577 version: VersionNumber,
578 ) -> IotaResult<bool> {
579 Ok(self
580 .perpetual_tables
581 .objects
582 .contains_key(&ObjectKey(*object_id, version))?)
583 }
584
585 pub fn multi_object_exists_by_key(&self, object_keys: &[ObjectKey]) -> IotaResult<Vec<bool>> {
586 Ok(self
587 .perpetual_tables
588 .objects
589 .multi_contains_keys(object_keys.to_vec())?
590 .into_iter()
591 .collect())
592 }
593
594 pub fn multi_get_objects_by_key(
595 &self,
596 object_keys: &[ObjectKey],
597 ) -> Result<Vec<Option<Object>>, IotaError> {
598 let wrappers = self
599 .perpetual_tables
600 .objects
601 .multi_get(object_keys.to_vec())?;
602 let mut ret = vec![];
603
604 for (idx, w) in wrappers.into_iter().enumerate() {
605 ret.push(
606 w.map(|object| self.perpetual_tables.object(&object_keys[idx], object))
607 .transpose()?
608 .flatten(),
609 );
610 }
611 Ok(ret)
612 }
613
614 pub fn get_objects(&self, objects: &[ObjectId]) -> Result<Vec<Option<Object>>, IotaError> {
616 let mut result = Vec::new();
617 for id in objects {
618 result.push(self.try_get_object(id)?);
619 }
620 Ok(result)
621 }
622
623 pub fn have_deleted_owned_object_at_version_or_after(
624 &self,
625 object_id: &ObjectId,
626 version: VersionNumber,
627 epoch_id: EpochId,
628 ) -> Result<bool, IotaError> {
629 let marker_entry = self
632 .perpetual_tables
633 .object_per_epoch_marker_table
634 .safe_iter_with_prefix_reversed(&(epoch_id, *object_id))
635 .next();
636 match marker_entry.transpose()? {
637 Some(((epoch, key), marker)) => {
638 let object_data_ok = key.0 == *object_id && key.1 >= version;
640 let epoch_data_ok = epoch == epoch_id;
642 let mark_data_ok = marker == MarkerValue::OwnedDeleted;
644 Ok(object_data_ok && epoch_data_ok && mark_data_ok)
645 }
646 None => Ok(false),
647 }
648 }
649
650 pub(crate) fn insert_genesis_object(&self, object: Object) -> IotaResult {
655 debug_assert!(object.previous_transaction == TransactionDigest::GENESIS_MARKER);
657 let object_ref = object.object_ref();
658 self.insert_genesis_object_direct(object_ref, &object)
659 }
660
661 fn insert_genesis_object_direct(
665 &self,
666 object_ref: ObjectReference,
667 object: &Object,
668 ) -> IotaResult {
669 let mut write_batch = self.perpetual_tables.objects.batch();
670
671 let store_object = get_store_object(object.clone(), Some(0));
673 write_batch.insert_batch(
674 &self.perpetual_tables.objects,
675 std::iter::once((ObjectKey::from(object_ref), store_object)),
676 )?;
677
678 if object.single_owner().is_some() {
680 if !object.is_child_object() {
682 self.initialize_live_object_markers_impl(&mut write_batch, &[object_ref])?;
683 }
684 }
685
686 write_batch.write()?;
687
688 Ok(())
689 }
690
691 #[instrument(level = "debug", skip_all)]
694 pub(crate) fn bulk_insert_genesis_objects(&self, objects: &[Object]) -> IotaResult<()> {
695 let mut batch = self.perpetual_tables.objects.batch();
696 let ref_and_objects: Vec<_> = objects.iter().map(|o| (o.object_ref(), o)).collect();
697
698 batch.insert_batch(
700 &self.perpetual_tables.objects,
701 ref_and_objects.iter().map(|(oref, o)| {
702 (
703 ObjectKey::from(oref),
704 get_store_object((*o).clone(), Some(0)),
705 )
706 }),
707 )?;
708
709 let non_child_object_refs: Vec<_> = ref_and_objects
710 .iter()
711 .filter(|(_, object)| !object.is_child_object())
712 .map(|(oref, _)| *oref)
713 .collect();
714
715 self.initialize_live_object_markers_impl(&mut batch, &non_child_object_refs)?;
716
717 batch.write()?;
718
719 Ok(())
720 }
721
722 pub fn bulk_insert_live_objects(
723 perpetual_db: &AuthorityPerpetualTables,
724 live_objects: impl Iterator<Item = LiveObject>,
725 expected_sha3_digest: &[u8; 32],
726 ) -> IotaResult<()> {
727 let mut hasher = Sha3_256::default();
728 let mut batch = perpetual_db.objects.batch();
729 for live_object in live_objects {
730 hasher.update(live_object.object_reference().digest.inner());
731 let LiveObject {
732 object,
733 previous_transaction_checkpoint,
734 } = live_object;
735 let store_object_wrapper =
736 get_store_object(object.clone(), previous_transaction_checkpoint);
737 batch.insert_batch(
738 &perpetual_db.objects,
739 std::iter::once((ObjectKey::from(object.object_ref()), store_object_wrapper)),
740 )?;
741 if !object.is_child_object() {
742 Self::initialize_live_object_markers(
743 &perpetual_db.live_owned_object_markers,
744 &mut batch,
745 &[object.object_ref()],
746 )?;
747 }
748 }
749 let sha3_digest = hasher.finalize().digest;
750 if *expected_sha3_digest != sha3_digest {
751 error!(
752 "Sha does not match! expected: {:?}, actual: {:?}",
753 expected_sha3_digest, sha3_digest
754 );
755 return Err(IotaError::from("Sha does not match"));
756 }
757 batch.write()?;
758 Ok(())
759 }
760
761 pub fn set_epoch_start_configuration(
762 &self,
763 epoch_start_configuration: &EpochStartConfiguration,
764 ) -> IotaResult {
765 self.perpetual_tables
766 .set_epoch_start_configuration(epoch_start_configuration)?;
767 Ok(())
768 }
769
770 pub fn get_epoch_start_configuration(&self) -> IotaResult<Option<EpochStartConfiguration>> {
771 Ok(self.perpetual_tables.epoch_start_configuration.get(&())?)
772 }
773
774 #[instrument(level = "debug", skip_all)]
786 pub fn build_db_batch(
787 &self,
788 epoch_id: EpochId,
789 checkpoint_sequence_number: CheckpointSequenceNumber,
790 tx_outputs: &[Arc<TransactionOutputs>],
791 ) -> IotaResult<DBBatch> {
792 let mut written = Vec::with_capacity(tx_outputs.len());
793 for outputs in tx_outputs {
794 written.extend(outputs.written.values().cloned());
795 }
796
797 let mut write_batch = self.perpetual_tables.transactions.batch();
798 for outputs in tx_outputs {
799 self.write_one_transaction_outputs(
800 &mut write_batch,
801 epoch_id,
802 checkpoint_sequence_number,
803 outputs,
804 )?;
805 }
806 fail_point!("crash");
808
809 trace!(
810 "built batch for committed transactions: {:?}",
811 tx_outputs
812 .iter()
813 .map(|tx| tx.transaction.digest())
814 .collect::<Vec<_>>()
815 );
816
817 fail_point!("crash");
819
820 Ok(write_batch)
821 }
822
823 fn write_one_transaction_outputs(
824 &self,
825 write_batch: &mut DBBatch,
826 epoch_id: EpochId,
827 checkpoint_sequence_number: CheckpointSequenceNumber,
828 tx_outputs: &TransactionOutputs,
829 ) -> IotaResult {
830 let TransactionOutputs {
831 transaction,
832 effects,
833 markers,
834 wrapped,
835 deleted,
836 written,
837 events,
838 live_object_markers_to_delete,
839 new_live_object_markers_to_init,
840 ..
841 } = tx_outputs;
842
843 let transaction_digest = transaction.digest();
845 write_batch.insert_batch(
846 &self.perpetual_tables.transactions,
847 iter::once((transaction_digest, transaction.serializable_ref())),
848 )?;
849
850 let effects_digest = effects.digest();
852
853 write_batch.insert_batch(
854 &self.perpetual_tables.object_per_epoch_marker_table,
855 markers
856 .iter()
857 .map(|(key, marker_value)| ((epoch_id, *key), *marker_value)),
858 )?;
859
860 write_batch.insert_batch(
861 &self.perpetual_tables.objects,
862 deleted
863 .iter()
864 .map(|key| (key, StoreObject::Deleted))
865 .chain(wrapped.iter().map(|key| (key, StoreObject::Wrapped)))
866 .map(|(key, store_object)| (key, StoreObjectWrapper::from(store_object))),
867 )?;
868
869 let new_objects = written.iter().map(|(id, new_object)| {
871 let version = new_object.version();
872 trace!(?id, ?version, "writing object");
873 let store_object =
874 get_store_object(new_object.clone(), Some(checkpoint_sequence_number));
875 (ObjectKey(*id, version), store_object)
876 });
877
878 write_batch.insert_batch(&self.perpetual_tables.objects, new_objects)?;
879
880 if effects.events_digest().is_some() {
882 write_batch.insert_batch(
883 &self.perpetual_tables.events_2,
884 [(transaction_digest, events)],
885 )?;
886 }
887
888 self.initialize_live_object_markers_impl(write_batch, new_live_object_markers_to_init)?;
889
890 self.delete_live_object_markers(write_batch, live_object_markers_to_delete)?;
893
894 write_batch
895 .insert_batch(
896 &self.perpetual_tables.effects,
897 [(effects_digest, effects.clone())],
898 )?
899 .insert_batch(
900 &self.perpetual_tables.executed_effects,
901 [(transaction_digest, effects_digest)],
902 )?;
903
904 debug!(effects_digest = ?effects.digest(), "commit_transaction finished");
905
906 Ok(())
907 }
908
909 pub(crate) fn persist_transaction(&self, tx: &VerifiedExecutableTransaction) -> IotaResult {
912 let mut batch = self.perpetual_tables.transactions.batch();
913 batch.insert_batch(
914 &self.perpetual_tables.transactions,
915 [(tx.digest(), tx.clone().into_unsigned().serializable_ref())],
916 )?;
917 batch.write()?;
918 Ok(())
919 }
920
921 pub fn acquire_transaction_locks(
922 &self,
923 epoch_store: &AuthorityPerEpochStore,
924 owned_input_objects: &[ObjectReference],
925 transaction: VerifiedSignedTransaction,
926 ) -> IotaResult {
927 let tx_digest = *transaction.digest();
928 let _mutexes = self.acquire_locks(owned_input_objects);
932
933 trace!(?owned_input_objects, "acquire_transaction_locks");
934 let mut locks_to_write = Vec::new();
935
936 let live_object_markers = self
937 .perpetual_tables
938 .live_owned_object_markers
939 .multi_get(owned_input_objects)?;
940
941 let epoch_tables = epoch_store.tables()?;
942
943 let locks = epoch_tables.multi_get_locked_transactions(owned_input_objects)?;
944
945 assert_eq!(locks.len(), live_object_markers.len());
946
947 for (live_marker, lock, obj_ref) in izip!(
948 live_object_markers.into_iter(),
949 locks.into_iter(),
950 owned_input_objects
951 ) {
952 if live_marker.is_none() {
953 let latest_live_version =
955 self.get_latest_live_version_for_object_id(obj_ref.object_id)?;
956 fp_bail!(
957 UserInputError::ObjectVersionUnavailableForConsumption {
958 provided_obj_ref: *obj_ref,
959 current_version: latest_live_version.version
960 }
961 .into()
962 );
963 };
964
965 if let Some(previous_tx_digest) = &lock {
966 if previous_tx_digest == &tx_digest {
967 continue;
969 } else {
970 info!(prev_tx_digest = ?previous_tx_digest,
972 cur_tx_digest = ?tx_digest,
973 "Cannot acquire lock: conflicting transaction!");
974 return Err(IotaError::ObjectLockConflict {
975 obj_ref: *obj_ref,
976 pending_transaction: *previous_tx_digest,
977 });
978 }
979 }
980
981 locks_to_write.push((*obj_ref, tx_digest));
982 }
983
984 if !locks_to_write.is_empty() {
985 trace!(?locks_to_write, "Writing locks");
986 epoch_tables.write_transaction_locks(transaction, locks_to_write.into_iter())?;
987 }
988
989 Ok(())
990 }
991
992 pub(crate) fn get_lock(
996 &self,
997 obj_ref: ObjectReference,
998 epoch_store: &AuthorityPerEpochStore,
999 ) -> IotaLockResult {
1000 if self
1001 .perpetual_tables
1002 .live_owned_object_markers
1003 .get(&obj_ref)?
1004 .is_none()
1005 {
1006 return Ok(ObjectLockStatus::LockedAtDifferentVersion {
1008 locked_ref: self.get_latest_live_version_for_object_id(obj_ref.object_id)?,
1009 });
1010 }
1011
1012 let tables = epoch_store.tables()?;
1013 if let Some(tx_digest) = tables.get_locked_transaction(&obj_ref)? {
1014 Ok(ObjectLockStatus::LockedToTx {
1015 locked_by_tx: tx_digest,
1016 })
1017 } else {
1018 Ok(ObjectLockStatus::Initialized)
1019 }
1020 }
1021
1022 pub(crate) fn get_latest_live_version_for_object_id(
1025 &self,
1026 object_id: ObjectId,
1027 ) -> IotaResult<ObjectReference> {
1028 let mut iterator = self
1029 .perpetual_tables
1030 .live_owned_object_markers
1031 .safe_iter_with_prefix_reversed(&object_id);
1032 Ok(iterator
1033 .next()
1034 .transpose()?
1035 .filter(|&value| value.0.object_id == object_id)
1036 .ok_or_else(|| {
1037 IotaError::from(UserInputError::ObjectNotFound {
1038 object_id,
1039 version: None,
1040 })
1041 })?
1042 .0)
1043 }
1044
1045 pub fn check_owned_objects_are_live(&self, objects: &[ObjectReference]) -> IotaResult {
1051 let live_markers = self
1052 .perpetual_tables
1053 .live_owned_object_markers
1054 .multi_get(objects)?;
1055 for (live_marker, obj_ref) in live_markers.into_iter().zip(objects) {
1056 if live_marker.is_none() {
1057 let latest_live_version =
1059 self.get_latest_live_version_for_object_id(obj_ref.object_id)?;
1060 fp_bail!(
1061 UserInputError::ObjectVersionUnavailableForConsumption {
1062 provided_obj_ref: *obj_ref,
1063 current_version: latest_live_version.version
1064 }
1065 .into()
1066 );
1067 }
1068 }
1069 Ok(())
1070 }
1071
1072 fn initialize_live_object_markers_impl(
1074 &self,
1075 write_batch: &mut DBBatch,
1076 objects: &[ObjectReference],
1077 ) -> IotaResult {
1078 AuthorityStore::initialize_live_object_markers(
1079 &self.perpetual_tables.live_owned_object_markers,
1080 write_batch,
1081 objects,
1082 )
1083 }
1084
1085 pub fn initialize_live_object_markers(
1086 live_object_marker_table: &DBMap<ObjectReference, ()>,
1087 write_batch: &mut DBBatch,
1088 objects: &[ObjectReference],
1089 ) -> IotaResult {
1090 trace!(?objects, "initialize_live_object_markers");
1091
1092 write_batch.insert_batch(
1093 live_object_marker_table,
1094 objects.iter().map(|obj_ref| (obj_ref, ())),
1095 )?;
1096 Ok(())
1097 }
1098
1099 fn delete_live_object_markers(
1101 &self,
1102 write_batch: &mut DBBatch,
1103 objects: &[ObjectReference],
1104 ) -> IotaResult {
1105 trace!(?objects, "delete_live_object_markers");
1106 write_batch.delete_batch(
1107 &self.perpetual_tables.live_owned_object_markers,
1108 objects.iter(),
1109 )?;
1110 Ok(())
1111 }
1112
1113 #[cfg(test)]
1114 pub(crate) fn reset_locks_and_live_markers_for_test(
1115 &self,
1116 transactions: &[TransactionDigest],
1117 objects: &[ObjectReference],
1118 epoch_store: &AuthorityPerEpochStore,
1119 ) {
1120 for tx in transactions {
1121 epoch_store.delete_signed_transaction_for_test(tx);
1122 epoch_store.delete_object_locks_for_test(objects);
1123 }
1124
1125 let mut batch = self.perpetual_tables.live_owned_object_markers.batch();
1126 batch
1127 .delete_batch(
1128 &self.perpetual_tables.live_owned_object_markers,
1129 objects.iter(),
1130 )
1131 .unwrap();
1132 batch.write().unwrap();
1133
1134 let mut batch = self.perpetual_tables.live_owned_object_markers.batch();
1135 self.initialize_live_object_markers_impl(&mut batch, objects)
1136 .unwrap();
1137 batch.write().unwrap();
1138 }
1139
1140 pub fn revert_state_update(&self, tx_digest: &TransactionDigest) -> IotaResult {
1154 let Some(effects) = self.get_executed_effects(tx_digest)? else {
1155 info!("Not reverting {:?} as it was not executed", tx_digest);
1156 return Ok(());
1157 };
1158
1159 info!(?tx_digest, ?effects, "reverting transaction");
1160
1161 assert!(effects.input_shared_objects().is_empty());
1163
1164 let mut write_batch = self.perpetual_tables.transactions.batch();
1165 write_batch.delete_batch(
1166 &self.perpetual_tables.executed_effects,
1167 iter::once(tx_digest),
1168 )?;
1169 if effects.events_digest().is_some() {
1170 write_batch.delete_batch(&self.perpetual_tables.events_2, [tx_digest])?;
1171 }
1172
1173 let tombstones = effects
1174 .all_tombstones()
1175 .into_iter()
1176 .map(|(id, version)| ObjectKey(id, version));
1177 write_batch.delete_batch(&self.perpetual_tables.objects, tombstones)?;
1178
1179 let all_new_object_keys = effects
1180 .all_changed_objects()
1181 .into_iter()
1182 .map(|(object_ref, _, _)| ObjectKey(object_ref.object_id, object_ref.version));
1183 write_batch.delete_batch(&self.perpetual_tables.objects, all_new_object_keys.clone())?;
1184
1185 let modified_object_keys = effects
1186 .modified_at_versions()
1187 .into_iter()
1188 .map(|(id, version)| ObjectKey(id, version));
1189
1190 macro_rules! get_objects_and_locks {
1191 ($object_keys: expr) => {
1192 self.perpetual_tables
1193 .objects
1194 .multi_get($object_keys.clone())?
1195 .into_iter()
1196 .zip($object_keys)
1197 .filter_map(|(obj_opt, key)| {
1198 let obj = self
1199 .perpetual_tables
1200 .object(
1201 &key,
1202 obj_opt.unwrap_or_else(|| {
1203 panic!("Older object version not found: {:?}", key)
1204 }),
1205 )
1206 .expect("Matching indirect object not found")?;
1207
1208 if obj.is_immutable() {
1209 return None;
1210 }
1211
1212 let obj_ref = obj.object_ref();
1213 Some(obj.is_address_owned().then_some(obj_ref))
1214 })
1215 };
1216 }
1217
1218 let old_locks = get_objects_and_locks!(modified_object_keys);
1219 let new_locks = get_objects_and_locks!(all_new_object_keys);
1220
1221 let old_locks: Vec<_> = old_locks.flatten().collect();
1222
1223 self.initialize_live_object_markers_impl(&mut write_batch, &old_locks)?;
1225
1226 write_batch.delete_batch(
1228 &self.perpetual_tables.live_owned_object_markers,
1229 new_locks.flatten(),
1230 )?;
1231
1232 write_batch.write()?;
1233
1234 Ok(())
1235 }
1236
1237 pub fn find_object_lt_or_eq_version(
1243 &self,
1244 object_id: ObjectId,
1245 version: Version,
1246 ) -> IotaResult<Option<Object>> {
1247 self.perpetual_tables
1248 .find_object_lt_or_eq_version(object_id, version)
1249 }
1250
1251 pub fn get_latest_object_ref_or_tombstone(
1262 &self,
1263 object_id: ObjectId,
1264 ) -> Result<Option<ObjectReference>, IotaError> {
1265 self.perpetual_tables
1266 .get_latest_object_ref_or_tombstone(object_id)
1267 }
1268
1269 pub fn get_latest_object_ref_if_alive(
1272 &self,
1273 object_id: ObjectId,
1274 ) -> Result<Option<ObjectReference>, IotaError> {
1275 match self.get_latest_object_ref_or_tombstone(object_id)? {
1276 Some(objref) if objref.digest.is_alive() => Ok(Some(objref)),
1277 _ => Ok(None),
1278 }
1279 }
1280
1281 pub fn get_latest_object_or_tombstone(
1286 &self,
1287 object_id: ObjectId,
1288 ) -> Result<Option<(ObjectKey, ObjectOrTombstone)>, IotaError> {
1289 let Some((object_key, store_object)) = self
1290 .perpetual_tables
1291 .get_latest_object_or_tombstone(object_id)?
1292 else {
1293 return Ok(None);
1294 };
1295
1296 if let Some(object_ref) = self
1297 .perpetual_tables
1298 .tombstone_reference(&object_key, &store_object)?
1299 {
1300 return Ok(Some((object_key, ObjectOrTombstone::Tombstone(object_ref))));
1301 }
1302
1303 let object = self
1304 .perpetual_tables
1305 .object(&object_key, store_object)?
1306 .expect("Non tombstone store object could not be converted to object");
1307
1308 Ok(Some((object_key, ObjectOrTombstone::Object(object))))
1309 }
1310
1311 pub fn insert_transaction_and_effects(
1312 &self,
1313 transaction: &VerifiedTransaction,
1314 transaction_effects: &TransactionEffects,
1315 ) -> Result<(), TypedStoreError> {
1316 let mut write_batch = self.perpetual_tables.transactions.batch();
1317 write_batch
1318 .insert_batch(
1319 &self.perpetual_tables.transactions,
1320 [(transaction.digest(), transaction.serializable_ref())],
1321 )?
1322 .insert_batch(
1323 &self.perpetual_tables.effects,
1324 [(transaction_effects.digest(), transaction_effects)],
1325 )?;
1326
1327 write_batch.write()?;
1328 Ok(())
1329 }
1330
1331 pub fn multi_insert_transaction_and_effects<'a>(
1332 &self,
1333 transactions: impl Iterator<Item = &'a VerifiedExecutionData>,
1334 ) -> Result<(), TypedStoreError> {
1335 let mut write_batch = self.perpetual_tables.transactions.batch();
1336 for tx in transactions {
1337 write_batch
1338 .insert_batch(
1339 &self.perpetual_tables.transactions,
1340 [(tx.transaction.digest(), tx.transaction.serializable_ref())],
1341 )?
1342 .insert_batch(
1343 &self.perpetual_tables.effects,
1344 [(tx.effects.digest(), &tx.effects)],
1345 )?;
1346 }
1347
1348 write_batch.write()?;
1349 Ok(())
1350 }
1351
1352 pub fn multi_get_transaction_blocks(
1353 &self,
1354 tx_digests: &[TransactionDigest],
1355 ) -> Result<Vec<Option<VerifiedTransaction>>, TypedStoreError> {
1356 self.perpetual_tables
1357 .transactions
1358 .multi_get(tx_digests)
1359 .map(|v| v.into_iter().map(|v| v.map(|v| v.into())).collect())
1360 }
1361
1362 pub fn get_transaction_block(
1363 &self,
1364 tx_digest: &TransactionDigest,
1365 ) -> Result<Option<VerifiedTransaction>, TypedStoreError> {
1366 self.perpetual_tables
1367 .transactions
1368 .get(tx_digest)
1369 .map(|v| v.map(|v| v.into()))
1370 }
1371
1372 pub fn get_iota_system_state_object_unsafe(&self) -> IotaResult<IotaSystemState> {
1380 get_iota_system_state(self.perpetual_tables.as_ref())
1381 }
1382
1383 pub fn expensive_check_iota_conservation<T>(
1384 self: &Arc<Self>,
1385 type_layout_store: T,
1386 old_epoch_store: &AuthorityPerEpochStore,
1387 epoch_supply_change: Option<i64>,
1388 ) -> IotaResult
1389 where
1390 T: TypeLayoutStore + Send + Copy,
1391 {
1392 if !self.enable_epoch_iota_conservation_check {
1393 return Ok(());
1394 }
1395
1396 let executor = old_epoch_store.executor();
1397 info!("Starting IOTA conservation check. This may take a while..");
1398 let cur_time = Instant::now();
1399 let mut pending_objects = vec![];
1400 let mut count = 0;
1401 let mut size = 0;
1402 let (mut total_iota, mut total_storage_rebate) = thread::scope(|s| {
1403 let pending_tasks = FuturesUnordered::new();
1404 for o in self.iter_live_object_set() {
1405 let object = o.object;
1406 size += object.object_size_for_gas_metering();
1407 count += 1;
1408 pending_objects.push(object);
1409 if count % 1_000_000 == 0 {
1410 let mut task_objects = vec![];
1411 mem::swap(&mut pending_objects, &mut task_objects);
1412 pending_tasks.push(s.spawn(move || {
1413 let mut layout_resolver =
1414 executor.type_layout_resolver(Box::new(type_layout_store));
1415 let mut total_storage_rebate = 0;
1416 let mut total_iota = 0;
1417 for object in task_objects {
1418 total_storage_rebate += object.storage_rebate;
1419 total_iota += object.get_total_iota(layout_resolver.as_mut()).unwrap()
1422 - object.storage_rebate;
1423 }
1424 if count % 50_000_000 == 0 {
1425 info!("Processed {} objects", count);
1426 }
1427 (total_iota, total_storage_rebate)
1428 }));
1429 }
1430 }
1431 pending_tasks.into_iter().fold((0, 0), |init, result| {
1432 let result = result.join().unwrap();
1433 (init.0 + result.0, init.1 + result.1)
1434 })
1435 });
1436 let mut layout_resolver = executor.type_layout_resolver(Box::new(type_layout_store));
1437 for object in pending_objects {
1438 total_storage_rebate += object.storage_rebate;
1439 total_iota +=
1440 object.get_total_iota(layout_resolver.as_mut()).unwrap() - object.storage_rebate;
1441 }
1442 info!(
1443 "Scanned {} live objects, took {:?}",
1444 count,
1445 cur_time.elapsed()
1446 );
1447 self.metrics
1448 .iota_conservation_live_object_count
1449 .set(count as i64);
1450 self.metrics
1451 .iota_conservation_live_object_size
1452 .set(size as i64);
1453 self.metrics
1454 .iota_conservation_check_latency
1455 .set(cur_time.elapsed().as_secs() as i64);
1456
1457 let system_state: IotaSystemStateSummaryV2 = self
1460 .get_iota_system_state_object_unsafe()
1461 .expect("Reading iota system state object cannot fail")
1462 .into_iota_system_state_summary()
1463 .try_into()?;
1464 let storage_fund_balance = system_state.storage_fund_total_object_storage_rebates;
1465 info!(
1466 "Total IOTA amount in the network: {}, storage fund balance: {}, total storage rebate: {} at beginning of epoch {}",
1467 total_iota, storage_fund_balance, total_storage_rebate, system_state.epoch
1468 );
1469
1470 let imbalance = (storage_fund_balance as i64) - (total_storage_rebate as i64);
1471 self.metrics
1472 .iota_conservation_storage_fund
1473 .set(storage_fund_balance as i64);
1474 self.metrics
1475 .iota_conservation_storage_fund_imbalance
1476 .set(imbalance);
1477 self.metrics
1478 .iota_conservation_imbalance
1479 .set((total_iota as i128 - system_state.iota_total_supply as i128) as i64);
1480
1481 if let Some(expected_imbalance) = self
1482 .perpetual_tables
1483 .expected_storage_fund_imbalance
1484 .get(&())
1485 .map_err(|err| {
1486 IotaError::from(
1487 format!("failed to read expected storage fund imbalance: {err}").as_str(),
1488 )
1489 })?
1490 {
1491 fp_ensure!(
1492 imbalance == expected_imbalance,
1493 IotaError::from(
1494 format!(
1495 "Inconsistent state detected at epoch {}: total storage rebate: {}, storage fund balance: {}, expected imbalance: {}",
1496 system_state.epoch, total_storage_rebate, storage_fund_balance, expected_imbalance
1497 ).as_str()
1498 )
1499 );
1500 } else {
1501 self.perpetual_tables
1502 .expected_storage_fund_imbalance
1503 .insert(&(), &imbalance)
1504 .map_err(|err| {
1505 IotaError::from(
1506 format!("failed to write expected storage fund imbalance: {err}").as_str(),
1507 )
1508 })?;
1509 }
1510
1511 let total_supply = self
1512 .perpetual_tables
1513 .total_iota_supply
1514 .get(&())
1515 .map_err(|err| {
1516 IotaError::from(format!("failed to read total iota supply: {err}").as_str())
1517 })?;
1518
1519 match total_supply.zip(epoch_supply_change) {
1520 Some((old_supply, epoch_supply_change))
1525 if old_supply.last_check_epoch + 1 == old_epoch_store.epoch() =>
1526 {
1527 let expected_new_supply = if epoch_supply_change >= 0 {
1528 old_supply
1529 .total_supply
1530 .checked_add(epoch_supply_change.unsigned_abs())
1531 .ok_or_else(|| {
1532 IotaError::from(
1533 format!(
1534 "Inconsistent state detected at epoch {}: old supply {} + supply change {} overflowed",
1535 system_state.epoch, old_supply.total_supply, epoch_supply_change
1536 ).as_str())
1537 })?
1538 } else {
1539 old_supply.total_supply.checked_sub(epoch_supply_change.unsigned_abs()).ok_or_else(|| {
1540 IotaError::from(
1541 format!(
1542 "Inconsistent state detected at epoch {}: old supply {} - supply change {} underflowed",
1543 system_state.epoch, old_supply.total_supply, epoch_supply_change
1544 ).as_str())
1545 })?
1546 };
1547
1548 fp_ensure!(
1549 total_iota == expected_new_supply,
1550 IotaError::from(
1551 format!(
1552 "Inconsistent state detected at epoch {}: total iota: {}, expecting {}",
1553 system_state.epoch, total_iota, expected_new_supply
1554 )
1555 .as_str()
1556 )
1557 );
1558
1559 let new_supply = TotalIotaSupplyCheck {
1560 total_supply: expected_new_supply,
1561 last_check_epoch: old_epoch_store.epoch(),
1562 };
1563
1564 self.perpetual_tables
1565 .total_iota_supply
1566 .insert(&(), &new_supply)
1567 .map_err(|err| {
1568 IotaError::from(
1569 format!("failed to write total iota supply: {err}").as_str(),
1570 )
1571 })?;
1572 }
1573 _ => {
1576 info!("Skipping total supply check");
1577
1578 let supply = TotalIotaSupplyCheck {
1579 total_supply: total_iota,
1580 last_check_epoch: old_epoch_store.epoch(),
1581 };
1582
1583 self.perpetual_tables
1584 .total_iota_supply
1585 .insert(&(), &supply)
1586 .map_err(|err| {
1587 IotaError::from(
1588 format!("failed to write total iota supply: {err}").as_str(),
1589 )
1590 })?;
1591
1592 return Ok(());
1593 }
1594 };
1595
1596 Ok(())
1597 }
1598
1599 pub async fn prune_objects_and_compact_for_testing(
1600 &self,
1601 checkpoint_store: &Arc<CheckpointStore>,
1602 grpc_indexes_store: Option<&GrpcIndexesStore>,
1603 ) {
1604 let pruning_config = AuthorityStorePruningConfig {
1605 num_epochs_to_retain: 0,
1606 ..Default::default()
1607 };
1608 let _ = AuthorityStorePruner::prune_objects_for_eligible_epochs(
1609 &self.perpetual_tables,
1610 checkpoint_store,
1611 grpc_indexes_store,
1612 None,
1613 pruning_config,
1614 AuthorityStorePruningMetrics::new_for_test(),
1615 EPOCH_DURATION_MS_FOR_TESTING,
1616 None,
1617 )
1618 .await;
1619 let _ = AuthorityStorePruner::compact(&self.perpetual_tables);
1620 }
1621
1622 #[cfg(test)]
1623 pub async fn prune_objects_immediately_for_testing(
1624 &self,
1625 transaction_effects: Vec<TransactionEffects>,
1626 ) -> anyhow::Result<()> {
1627 let mut wb = self.perpetual_tables.objects.batch();
1628
1629 let mut object_keys_to_prune = vec![];
1630 for effects in &transaction_effects {
1631 for (object_id, seq_number) in effects.modified_at_versions() {
1632 info!("Pruning object {} version {:?}", object_id, seq_number);
1633 object_keys_to_prune.push(ObjectKey(object_id, seq_number));
1634 }
1635 }
1636
1637 wb.delete_batch(&self.perpetual_tables.objects, object_keys_to_prune)?;
1638 wb.write()?;
1639 Ok(())
1640 }
1641
1642 #[cfg(msim)]
1645 pub fn count_object_versions(&self, object_id: ObjectId) -> usize {
1646 self.perpetual_tables
1647 .objects
1648 .safe_iter_with_prefix(&object_id)
1649 .collect::<Result<Vec<_>, _>>()
1650 .unwrap()
1651 .len()
1652 }
1653}
1654
1655impl GlobalStateHashStore for AuthorityStore {
1656 fn get_root_state_hash_for_epoch(
1657 &self,
1658 epoch: EpochId,
1659 ) -> IotaResult<Option<(CheckpointSequenceNumber, GlobalStateHash)>> {
1660 self.perpetual_tables
1661 .root_state_hash_by_epoch
1662 .get(&epoch)
1663 .map_err(Into::into)
1664 }
1665
1666 fn get_root_state_hash_for_highest_epoch(
1667 &self,
1668 ) -> IotaResult<Option<(EpochId, (CheckpointSequenceNumber, GlobalStateHash))>> {
1669 Ok(self
1670 .perpetual_tables
1671 .root_state_hash_by_epoch
1672 .safe_range_iter_reversed(..)
1673 .next()
1674 .transpose()?)
1675 }
1676
1677 fn insert_state_hash_for_epoch(
1678 &self,
1679 epoch: EpochId,
1680 last_checkpoint_of_epoch: &CheckpointSequenceNumber,
1681 acc: &GlobalStateHash,
1682 ) -> IotaResult {
1683 self.perpetual_tables
1684 .root_state_hash_by_epoch
1685 .insert(&epoch, &(*last_checkpoint_of_epoch, acc.clone()))?;
1686 self.root_state_notify_read
1687 .notify(&epoch, &(*last_checkpoint_of_epoch, acc.clone()));
1688
1689 Ok(())
1690 }
1691
1692 fn iter_live_object_set(&self) -> Box<dyn Iterator<Item = LiveObject> + '_> {
1693 Box::new(self.perpetual_tables.iter_live_object_set())
1694 }
1695}
1696
1697impl ObjectStore for AuthorityStore {
1698 fn try_get_object(
1700 &self,
1701 object_id: &ObjectId,
1702 ) -> Result<Option<Object>, iota_types::storage::error::Error> {
1703 self.perpetual_tables.as_ref().try_get_object(object_id)
1704 }
1705
1706 fn try_get_object_by_key(
1707 &self,
1708 object_id: &ObjectId,
1709 version: VersionNumber,
1710 ) -> Result<Option<Object>, iota_types::storage::error::Error> {
1711 self.perpetual_tables
1712 .try_get_object_by_key(object_id, version)
1713 }
1714}
1715
1716pub struct ResolverWrapper {
1718 pub resolver: Arc<dyn BackingPackageStore + Send + Sync>,
1719 pub metrics: Arc<ResolverMetrics>,
1720}
1721
1722impl ResolverWrapper {
1723 pub fn new(
1724 resolver: Arc<dyn BackingPackageStore + Send + Sync>,
1725 metrics: Arc<ResolverMetrics>,
1726 ) -> Self {
1727 metrics.module_cache_size.set(0);
1728 ResolverWrapper { resolver, metrics }
1729 }
1730
1731 fn inc_cache_size_gauge(&self) {
1732 let current = self.metrics.module_cache_size.get();
1734 self.metrics.module_cache_size.set(current + 1);
1735 }
1736}
1737
1738impl ModuleResolver for ResolverWrapper {
1739 type Error = IotaError;
1740 fn get_module(&self, module_id: &ModuleId) -> Result<Option<Vec<u8>>, Self::Error> {
1741 self.inc_cache_size_gauge();
1742 get_module(&*self.resolver, module_id)
1743 }
1744}
1745
1746pub enum UpdateType {
1747 Transaction(TransactionEffectsDigest),
1748 Genesis,
1749}
1750
1751pub type IotaLockResult = IotaResult<ObjectLockStatus>;
1752
1753#[derive(Debug, PartialEq, Eq)]
1754pub enum ObjectLockStatus {
1755 Initialized,
1756 LockedToTx { locked_by_tx: LockDetails }, LockedAtDifferentVersion { locked_ref: ObjectReference },
1758}