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