Skip to main content

iota_core/authority/
authority_store.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use 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
121/// The `AuthorityStore` manages the state and operations of an authority's
122/// store. It includes a `mutex_table` to handle concurrent writes to the
123/// database and references to various tables stored in
124/// `AuthorityPerpetualTables`. The struct provides mechanisms for initializing
125/// and accessing locks, managing objects and transactions, and performing
126/// epoch-specific operations. It also includes methods for recovering from
127/// crashes, checking IOTA conservation, and handling object markers and states
128/// during epoch transitions.
129pub struct AuthorityStore {
130    /// Internal vector of locks to manage concurrent writes to the database
131    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    /// Whether to enable expensive IOTA conservation check at epoch boundaries.
139    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    /// Open an authority store by directory path.
149    /// If the store is empty, initialize it using genesis.
150    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    // NB: This must only be called at time of reconfiguration. We take the
217    // execution lock write guard as an argument to ensure that this is the
218    // case.
219    pub fn clear_object_per_epoch_marker_table(
220        &self,
221        _execution_guard: &ExecutionLockWriteGuard<'_>,
222    ) -> IotaResult<()> {
223        // We can safely delete all entries in the per epoch marker table since this is
224        // only called at epoch boundaries (during reconfiguration). Therefore
225        // any entries that currently exist can be removed. Because of this we
226        // can use the `schedule_delete_all` method.
227        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        // TODO: Since we always start at genesis, the committee should be technically
239        // the same as the genesis committee.
240        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        // Only initialize an empty database.
262        if store
263            .database_is_empty()
264            .expect("database read should not fail at init.")
265        {
266            // Initialize with genesis data
267            // First insert genesis objects
268            store
269                .bulk_insert_genesis_objects(genesis.objects())
270                .expect("cannot bulk insert genesis objects");
271
272            // Then insert txn and effects of genesis
273            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            // In the previous step we don't insert the effects to executed_effects yet
286            // because the genesis tx hasn't but will be executed. This is
287            // important for fullnodes to be able to generate indexing data
288            // right now.
289            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            // Initialize with migration data if genesis contained migration transactions
298            if let Some(migration_transactions) = migration_tx_data {
299                // This migration data was validated during the loading into the node (invoked
300                // by the caller of this function)
301                let txs_data = migration_transactions.txs_data();
302
303                // We iterate over the contents of the genesis checkpoint, that includes all
304                // migration transactions execution digest. Thus we cover all transactions that
305                // were considered during the creation of the genesis blob.
306                for (_, execution_digest) in genesis
307                    .checkpoint_contents()
308                    .enumerate_transactions(&genesis.checkpoint())
309                {
310                    let tx_digest = &execution_digest.transaction;
311                    // We can skip the genesis transaction and its data because above it was already
312                    // stored in the perpetual_tables.
313                    if tx_digest == genesis.transaction().digest() {
314                        continue;
315                    }
316                    // Now we can store in the perpetual_tables this migration transaction, together
317                    // with its effects, events and created objects.
318                    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    /// Open authority store without any operations that require
353    /// genesis, such as constructing EpochStartConfiguration
354    /// or inserting genesis objects.
355    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    /// Returns true if we have an effects structure for this transaction digest
385    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    /// Given a list of transaction digests, returns a list of the corresponding
428    /// effects only if they have been executed. For transactions that have
429    /// not been executed, None is returned.
430    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    /// Given a list of transaction digests, returns a list of the corresponding
438    /// effects only if they have been executed. For transactions that have
439    /// not been executed, None is returned.
440    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                // because of the iterator bounds these cannot fail
490                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    /// Returns future containing the state hash for the given epoch
500    /// once available
501    pub async fn notify_read_root_state_hash(
502        &self,
503        epoch: EpochId,
504    ) -> IotaResult<(CheckpointSequenceNumber, GlobalStateHash)> {
505        // We need to register waiters _before_ reading from the database to avoid race
506        // conditions
507        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            // Note that Some() clause also drops registration that is already fulfilled
512            Some(ready) => Either::Left(futures::future::ready(ready)),
513            None => Either::Right(registration),
514        }
515        .await;
516
517        Ok(result)
518    }
519
520    // Implementation of the corresponding method of `CheckpointCache` trait.
521    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    // Implementation of the corresponding method of `CheckpointCache` trait.
541    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    // Implementation of the corresponding method of `CheckpointCache` trait.
552    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    /// Returns true if there are no objects in the database
563    pub fn database_is_empty(&self) -> IotaResult<bool> {
564        self.perpetual_tables.database_is_empty()
565    }
566
567    /// A function that acquires all locks associated with the objects (in order
568    /// to avoid deadlocks).
569    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    /// Get many objects
615    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        // Find the most recent version of the object that was deleted or wrapped.
630        // Return true if the version is >= `version`. Otherwise return false.
631        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                // Make sure object id matches and version is >= `version`
639                let object_data_ok = key.0 == *object_id && key.1 >= version;
640                // Make sure we don't have a stale epoch for some reason (e.g., a revert)
641                let epoch_data_ok = epoch == epoch_id;
642                // Make sure the object was deleted or wrapped.
643                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    // Methods to mutate the store
651
652    /// Insert a genesis object.
653    /// TODO: delete this method entirely (still used by authority_tests.rs)
654    pub(crate) fn insert_genesis_object(&self, object: Object) -> IotaResult {
655        // We only side load objects with a genesis parent transaction.
656        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    /// Insert an object directly into the store, and also update relevant
662    /// tables NOTE: does not handle transaction lock.
663    /// This is used to insert genesis objects
664    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        // Genesis objects are produced by the genesis checkpoint (sequence 0).
672        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        // Update the index
679        if object.single_owner().is_some() {
680            // Only initialize live object markers for address owned objects.
681            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    /// This function should only be used for initializing genesis and should
692    /// remain private.
693    #[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        // Genesis objects are produced by the genesis checkpoint (sequence 0).
699        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.bytes());
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    /// Updates the state resulting from the execution of a certificate.
775    ///
776    /// Internally it checks that all locks for active inputs are at the correct
777    /// version, and then writes objects, certificates, parents and clean up
778    /// locks atomically.
779    ///
780    /// `checkpoint_sequence_number` is stamped onto each newly written object's
781    /// `StoreObjectValueV2.previous_transaction_checkpoint` field.
782    ///
783    /// **Invariant** Every `TransactionOutputs` in `tx_outputs` must belong to
784    /// the checkpoint identified by `checkpoint_sequence_number`.
785    #[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        // test crashing before writing the batch
807        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        // test crashing before notifying
818        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        // Store the certificate indexed by transaction digest
844        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        // Add batched writes for objects and locks.
851        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        // Insert each output object into the stores
870        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        // Write events into the new table keyed off of transaction_digest
881        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        // Note: deletes live object markers for received objects as well (but not for
891        // objects that were in `Receiving` arguments which were not received)
892        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    /// Commits transactions only (not effects or other transaction outputs) to
910    /// the db. See ExecutionCache::persist_transaction for more info
911    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        // Other writers may be attempting to acquire locks on the same objects, so a
929        // mutex is required.
930        // TODO: replace with optimistic db_transactions (i.e. set lock to tx if none)
931        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                // object at that version does not exist
954                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                    // no need to re-write lock
968                    continue;
969                } else {
970                    // TODO: add metrics here
971                    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    /// Gets ObjectLockInfo that represents state of lock on an object.
993    /// Returns UserInputError::ObjectNotFound if cannot find lock record for
994    /// this object
995    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            // object at that version does not exist
1007            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    /// Returns UserInputError::ObjectNotFound if no lock records found for this
1023    /// object.
1024    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    /// Checks multiple object locks exist.
1046    /// Returns UserInputError::ObjectNotFound if cannot find lock record for at
1047    /// least one of the objects.
1048    /// Returns UserInputError::ObjectVersionUnavailableForConsumption if at
1049    /// least one object lock is not initialized     at the given version.
1050    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                // object at that version does not exist
1058                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    /// Initialize live object markers for a given list of ObjectRefs.
1073    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    /// Removes locks for a given list of ObjectRefs.
1100    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    /// This function is called at the end of epoch for each transaction that's
1141    /// executed locally on the validator but didn't make to the last
1142    /// checkpoint. The effects of the execution is reverted here.
1143    /// The following things are reverted:
1144    /// 1. All new object states are deleted.
1145    /// 2. owner_index table change is reverted.
1146    ///
1147    /// NOTE: transaction and effects are intentionally not deleted. It's
1148    /// possible that if this node is behind, the network will execute the
1149    /// transaction in a later epoch. In that case, we need to keep it saved
1150    /// so that when we receive the checkpoint that includes it from state
1151    /// sync, we are able to execute the checkpoint.
1152    /// TODO: implement GC for transactions that are no longer needed.
1153    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        // We should never be reverting shared object transactions.
1162        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(|(changed, _)| {
1183                ObjectKey(changed.reference().object_id, changed.reference().version)
1184            });
1185        write_batch.delete_batch(&self.perpetual_tables.objects, all_new_object_keys.clone())?;
1186
1187        let modified_object_keys = effects
1188            .modified_at_versions()
1189            .into_iter()
1190            .map(|modified| ObjectKey(*modified.object_id(), modified.version()));
1191
1192        macro_rules! get_objects_and_locks {
1193            ($object_keys: expr) => {
1194                self.perpetual_tables
1195                    .objects
1196                    .multi_get($object_keys.clone())?
1197                    .into_iter()
1198                    .zip($object_keys)
1199                    .filter_map(|(obj_opt, key)| {
1200                        let obj = self
1201                            .perpetual_tables
1202                            .object(
1203                                &key,
1204                                obj_opt.unwrap_or_else(|| {
1205                                    panic!("Older object version not found: {:?}", key)
1206                                }),
1207                            )
1208                            .expect("Matching indirect object not found")?;
1209
1210                        if obj.is_immutable() {
1211                            return None;
1212                        }
1213
1214                        let obj_ref = obj.object_ref();
1215                        Some(obj.is_address_owned().then_some(obj_ref))
1216                    })
1217            };
1218        }
1219
1220        let old_locks = get_objects_and_locks!(modified_object_keys);
1221        let new_locks = get_objects_and_locks!(all_new_object_keys);
1222
1223        let old_locks: Vec<_> = old_locks.flatten().collect();
1224
1225        // Re-create old live markers.
1226        self.initialize_live_object_markers_impl(&mut write_batch, &old_locks)?;
1227
1228        // Delete new live markers
1229        write_batch.delete_batch(
1230            &self.perpetual_tables.live_owned_object_markers,
1231            new_locks.flatten(),
1232        )?;
1233
1234        write_batch.write()?;
1235
1236        Ok(())
1237    }
1238
1239    /// Return the object with version less then or eq to the provided seq
1240    /// number. This is used by indexer to find the correct version of
1241    /// dynamic field child object. We do not store the version of the child
1242    /// object, but because of lamport timestamp, we know the child must
1243    /// have version number less then or eq to the parent.
1244    pub fn find_object_lt_or_eq_version(
1245        &self,
1246        object_id: ObjectId,
1247        version: Version,
1248    ) -> IotaResult<Option<Object>> {
1249        self.perpetual_tables
1250            .find_object_lt_or_eq_version(object_id, version)
1251    }
1252
1253    /// Returns the latest object reference we have for this object_id in the
1254    /// objects table.
1255    ///
1256    /// The method may also return the reference to a deleted object with a
1257    /// digest of ObjectDigest::deleted() or ObjectDigest::wrapped() and
1258    /// lamport version of a transaction that deleted the object.
1259    /// Note that a deleted object may re-appear if the deletion was the result
1260    /// of the object being wrapped in another object.
1261    ///
1262    /// If no entry for the object_id is found, return None.
1263    pub fn get_latest_object_ref_or_tombstone(
1264        &self,
1265        object_id: ObjectId,
1266    ) -> Result<Option<ObjectReference>, IotaError> {
1267        self.perpetual_tables
1268            .get_latest_object_ref_or_tombstone(object_id)
1269    }
1270
1271    /// Returns the latest object reference if and only if the object is still
1272    /// live (i.e. it does not return tombstones)
1273    pub fn get_latest_object_ref_if_alive(
1274        &self,
1275        object_id: ObjectId,
1276    ) -> Result<Option<ObjectReference>, IotaError> {
1277        match self.get_latest_object_ref_or_tombstone(object_id)? {
1278            Some(objref) if objref.digest.is_alive() => Ok(Some(objref)),
1279            _ => Ok(None),
1280        }
1281    }
1282
1283    /// Returns the latest object we have for this object_id in the objects
1284    /// table.
1285    ///
1286    /// If no entry for the object_id is found, return None.
1287    pub fn get_latest_object_or_tombstone(
1288        &self,
1289        object_id: ObjectId,
1290    ) -> Result<Option<(ObjectKey, ObjectOrTombstone)>, IotaError> {
1291        let Some((object_key, store_object)) = self
1292            .perpetual_tables
1293            .get_latest_object_or_tombstone(object_id)?
1294        else {
1295            return Ok(None);
1296        };
1297
1298        if let Some(object_ref) = self
1299            .perpetual_tables
1300            .tombstone_reference(&object_key, &store_object)?
1301        {
1302            return Ok(Some((object_key, ObjectOrTombstone::Tombstone(object_ref))));
1303        }
1304
1305        let object = self
1306            .perpetual_tables
1307            .object(&object_key, store_object)?
1308            .expect("Non tombstone store object could not be converted to object");
1309
1310        Ok(Some((object_key, ObjectOrTombstone::Object(object))))
1311    }
1312
1313    pub fn insert_transaction_and_effects(
1314        &self,
1315        transaction: &VerifiedTransaction,
1316        transaction_effects: &TransactionEffects,
1317    ) -> Result<(), TypedStoreError> {
1318        let mut write_batch = self.perpetual_tables.transactions.batch();
1319        write_batch
1320            .insert_batch(
1321                &self.perpetual_tables.transactions,
1322                [(transaction.digest(), transaction.serializable_ref())],
1323            )?
1324            .insert_batch(
1325                &self.perpetual_tables.effects,
1326                [(transaction_effects.digest(), transaction_effects)],
1327            )?;
1328
1329        write_batch.write()?;
1330        Ok(())
1331    }
1332
1333    pub fn multi_insert_transaction_and_effects<'a>(
1334        &self,
1335        transactions: impl Iterator<Item = &'a VerifiedExecutionData>,
1336    ) -> Result<(), TypedStoreError> {
1337        let mut write_batch = self.perpetual_tables.transactions.batch();
1338        for tx in transactions {
1339            write_batch
1340                .insert_batch(
1341                    &self.perpetual_tables.transactions,
1342                    [(tx.transaction.digest(), tx.transaction.serializable_ref())],
1343                )?
1344                .insert_batch(
1345                    &self.perpetual_tables.effects,
1346                    [(tx.effects.digest(), &tx.effects)],
1347                )?;
1348        }
1349
1350        write_batch.write()?;
1351        Ok(())
1352    }
1353
1354    pub fn multi_get_transaction_blocks(
1355        &self,
1356        tx_digests: &[TransactionDigest],
1357    ) -> Result<Vec<Option<VerifiedTransaction>>, TypedStoreError> {
1358        self.perpetual_tables
1359            .transactions
1360            .multi_get(tx_digests)
1361            .map(|v| v.into_iter().map(|v| v.map(|v| v.into())).collect())
1362    }
1363
1364    pub fn get_transaction_block(
1365        &self,
1366        tx_digest: &TransactionDigest,
1367    ) -> Result<Option<VerifiedTransaction>, TypedStoreError> {
1368        self.perpetual_tables
1369            .transactions
1370            .get(tx_digest)
1371            .map(|v| v.map(|v| v.into()))
1372    }
1373
1374    /// This function reads the DB directly to get the system state object.
1375    /// If reconfiguration is happening at the same time, there is no guarantee
1376    /// whether we would be getting the old or the new system state object.
1377    /// Hence this function should only be called during RPC reads where data
1378    /// race is not a major concern. In general we should avoid this as much
1379    /// as possible. If the intent is for testing, you can use
1380    /// AuthorityState:: get_iota_system_state_object_for_testing.
1381    pub fn get_iota_system_state_object_unsafe(&self) -> IotaResult<IotaSystemState> {
1382        get_iota_system_state(self.perpetual_tables.as_ref())
1383    }
1384
1385    pub fn expensive_check_iota_conservation<T>(
1386        self: &Arc<Self>,
1387        type_layout_store: T,
1388        old_epoch_store: &AuthorityPerEpochStore,
1389        epoch_supply_change: Option<i64>,
1390    ) -> IotaResult
1391    where
1392        T: TypeLayoutStore + Send + Copy,
1393    {
1394        if !self.enable_epoch_iota_conservation_check {
1395            return Ok(());
1396        }
1397
1398        let executor = old_epoch_store.executor();
1399        info!("Starting IOTA conservation check. This may take a while..");
1400        let cur_time = Instant::now();
1401        let mut pending_objects = vec![];
1402        let mut count = 0;
1403        let mut size = 0;
1404        let (mut total_iota, mut total_storage_rebate) = thread::scope(|s| {
1405            let pending_tasks = FuturesUnordered::new();
1406            for o in self.iter_live_object_set() {
1407                let object = o.object;
1408                size += object.object_size_for_gas_metering();
1409                count += 1;
1410                pending_objects.push(object);
1411                if count % 1_000_000 == 0 {
1412                    let mut task_objects = vec![];
1413                    mem::swap(&mut pending_objects, &mut task_objects);
1414                    pending_tasks.push(s.spawn(move || {
1415                        let mut layout_resolver =
1416                            executor.type_layout_resolver(Box::new(type_layout_store));
1417                        let mut total_storage_rebate = 0;
1418                        let mut total_iota = 0;
1419                        for object in task_objects {
1420                            total_storage_rebate += object.storage_rebate;
1421                            // get_total_iota includes storage rebate, however all storage rebate is
1422                            // also stored in the storage fund, so we need to subtract it here.
1423                            total_iota += object.get_total_iota(layout_resolver.as_mut()).unwrap()
1424                                - object.storage_rebate;
1425                        }
1426                        if count % 50_000_000 == 0 {
1427                            info!("Processed {} objects", count);
1428                        }
1429                        (total_iota, total_storage_rebate)
1430                    }));
1431                }
1432            }
1433            pending_tasks.into_iter().fold((0, 0), |init, result| {
1434                let result = result.join().unwrap();
1435                (init.0 + result.0, init.1 + result.1)
1436            })
1437        });
1438        let mut layout_resolver = executor.type_layout_resolver(Box::new(type_layout_store));
1439        for object in pending_objects {
1440            total_storage_rebate += object.storage_rebate;
1441            total_iota +=
1442                object.get_total_iota(layout_resolver.as_mut()).unwrap() - object.storage_rebate;
1443        }
1444        info!(
1445            "Scanned {} live objects, took {:?}",
1446            count,
1447            cur_time.elapsed()
1448        );
1449        self.metrics
1450            .iota_conservation_live_object_count
1451            .set(count as i64);
1452        self.metrics
1453            .iota_conservation_live_object_size
1454            .set(size as i64);
1455        self.metrics
1456            .iota_conservation_check_latency
1457            .set(cur_time.elapsed().as_secs() as i64);
1458
1459        // It is safe to call this function because we are in the middle of
1460        // reconfiguration.
1461        let system_state: IotaSystemStateSummaryV2 = self
1462            .get_iota_system_state_object_unsafe()
1463            .expect("Reading iota system state object cannot fail")
1464            .into_iota_system_state_summary()
1465            .try_into()?;
1466        let storage_fund_balance = system_state.storage_fund_total_object_storage_rebates;
1467        info!(
1468            "Total IOTA amount in the network: {}, storage fund balance: {}, total storage rebate: {} at beginning of epoch {}",
1469            total_iota, storage_fund_balance, total_storage_rebate, system_state.epoch
1470        );
1471
1472        let imbalance = (storage_fund_balance as i64) - (total_storage_rebate as i64);
1473        self.metrics
1474            .iota_conservation_storage_fund
1475            .set(storage_fund_balance as i64);
1476        self.metrics
1477            .iota_conservation_storage_fund_imbalance
1478            .set(imbalance);
1479        self.metrics
1480            .iota_conservation_imbalance
1481            .set((total_iota as i128 - system_state.iota_total_supply as i128) as i64);
1482
1483        if let Some(expected_imbalance) = self
1484            .perpetual_tables
1485            .expected_storage_fund_imbalance
1486            .get(&())
1487            .map_err(|err| {
1488                IotaError::from(
1489                    format!("failed to read expected storage fund imbalance: {err}").as_str(),
1490                )
1491            })?
1492        {
1493            fp_ensure!(
1494                imbalance == expected_imbalance,
1495                IotaError::from(
1496                    format!(
1497                        "Inconsistent state detected at epoch {}: total storage rebate: {}, storage fund balance: {}, expected imbalance: {}",
1498                        system_state.epoch, total_storage_rebate, storage_fund_balance, expected_imbalance
1499                    ).as_str()
1500                )
1501            );
1502        } else {
1503            self.perpetual_tables
1504                .expected_storage_fund_imbalance
1505                .insert(&(), &imbalance)
1506                .map_err(|err| {
1507                    IotaError::from(
1508                        format!("failed to write expected storage fund imbalance: {err}").as_str(),
1509                    )
1510                })?;
1511        }
1512
1513        let total_supply = self
1514            .perpetual_tables
1515            .total_iota_supply
1516            .get(&())
1517            .map_err(|err| {
1518                IotaError::from(format!("failed to read total iota supply: {err}").as_str())
1519            })?;
1520
1521        match total_supply.zip(epoch_supply_change) {
1522            // Only execute the check if both are set and the supply value was set in the last
1523            // epoch. We have to assume the supply changes every epoch and therefore we
1524            // cannot run the check with a supply value from any epoch earlier than the
1525            // last one. This can happen if the check was disabled for some time.
1526            Some((old_supply, epoch_supply_change))
1527                if old_supply.last_check_epoch + 1 == old_epoch_store.epoch() =>
1528            {
1529                let expected_new_supply = if epoch_supply_change >= 0 {
1530                    old_supply
1531                        .total_supply
1532                        .checked_add(epoch_supply_change.unsigned_abs())
1533                        .ok_or_else(|| {
1534                            IotaError::from(
1535                                format!(
1536                                    "Inconsistent state detected at epoch {}: old supply {} + supply change {} overflowed",
1537                                    system_state.epoch, old_supply.total_supply, epoch_supply_change
1538                                ).as_str())
1539                        })?
1540                } else {
1541                    old_supply.total_supply.checked_sub(epoch_supply_change.unsigned_abs()).ok_or_else(|| {
1542                        IotaError::from(
1543                            format!(
1544                                "Inconsistent state detected at epoch {}: old supply {} - supply change {} underflowed",
1545                                system_state.epoch, old_supply.total_supply, epoch_supply_change
1546                            ).as_str())
1547                    })?
1548                };
1549
1550                fp_ensure!(
1551                    total_iota == expected_new_supply,
1552                    IotaError::from(
1553                        format!(
1554                            "Inconsistent state detected at epoch {}: total iota: {}, expecting {}",
1555                            system_state.epoch, total_iota, expected_new_supply
1556                        )
1557                        .as_str()
1558                    )
1559                );
1560
1561                let new_supply = TotalIotaSupplyCheck {
1562                    total_supply: expected_new_supply,
1563                    last_check_epoch: old_epoch_store.epoch(),
1564                };
1565
1566                self.perpetual_tables
1567                    .total_iota_supply
1568                    .insert(&(), &new_supply)
1569                    .map_err(|err| {
1570                        IotaError::from(
1571                            format!("failed to write total iota supply: {err}").as_str(),
1572                        )
1573                    })?;
1574            }
1575            // If either one is None or if the last value is from an older epoch,
1576            // we update the value in the table since we're at genesis and cannot execute the check.
1577            _ => {
1578                info!("Skipping total supply check");
1579
1580                let supply = TotalIotaSupplyCheck {
1581                    total_supply: total_iota,
1582                    last_check_epoch: old_epoch_store.epoch(),
1583                };
1584
1585                self.perpetual_tables
1586                    .total_iota_supply
1587                    .insert(&(), &supply)
1588                    .map_err(|err| {
1589                        IotaError::from(
1590                            format!("failed to write total iota supply: {err}").as_str(),
1591                        )
1592                    })?;
1593
1594                return Ok(());
1595            }
1596        };
1597
1598        Ok(())
1599    }
1600
1601    pub async fn prune_objects_and_compact_for_testing(
1602        &self,
1603        checkpoint_store: &Arc<CheckpointStore>,
1604        grpc_indexes_store: Option<&GrpcIndexesStore>,
1605    ) {
1606        let pruning_config = AuthorityStorePruningConfig {
1607            num_epochs_to_retain: 0,
1608            ..Default::default()
1609        };
1610        let _ = AuthorityStorePruner::prune_objects_for_eligible_epochs(
1611            &self.perpetual_tables,
1612            checkpoint_store,
1613            grpc_indexes_store,
1614            pruning_config,
1615            AuthorityStorePruningMetrics::new_for_test(),
1616            EPOCH_DURATION_MS_FOR_TESTING,
1617            None,
1618        )
1619        .await;
1620        let _ = AuthorityStorePruner::compact(&self.perpetual_tables);
1621    }
1622
1623    #[cfg(test)]
1624    pub async fn prune_objects_immediately_for_testing(
1625        &self,
1626        transaction_effects: Vec<TransactionEffects>,
1627    ) -> anyhow::Result<()> {
1628        let mut wb = self.perpetual_tables.objects.batch();
1629
1630        let mut object_keys_to_prune = vec![];
1631        for effects in &transaction_effects {
1632            for modified in effects.modified_at_versions() {
1633                let (object_id, version) = (modified.object_id(), modified.version());
1634                info!("Pruning object {object_id} version {version:?}");
1635                object_keys_to_prune.push(ObjectKey(*object_id, version));
1636            }
1637        }
1638
1639        wb.delete_batch(&self.perpetual_tables.objects, object_keys_to_prune)?;
1640        wb.write()?;
1641        Ok(())
1642    }
1643
1644    // Counts the number of versions exist in object store for `object_id`. This
1645    // includes tombstone.
1646    #[cfg(msim)]
1647    pub fn count_object_versions(&self, object_id: ObjectId) -> usize {
1648        self.perpetual_tables
1649            .objects
1650            .safe_iter_with_prefix(&object_id)
1651            .collect::<Result<Vec<_>, _>>()
1652            .unwrap()
1653            .len()
1654    }
1655}
1656
1657impl GlobalStateHashStore for AuthorityStore {
1658    fn get_root_state_hash_for_epoch(
1659        &self,
1660        epoch: EpochId,
1661    ) -> IotaResult<Option<(CheckpointSequenceNumber, GlobalStateHash)>> {
1662        self.perpetual_tables
1663            .root_state_hash_by_epoch
1664            .get(&epoch)
1665            .map_err(Into::into)
1666    }
1667
1668    fn get_root_state_hash_for_highest_epoch(
1669        &self,
1670    ) -> IotaResult<Option<(EpochId, (CheckpointSequenceNumber, GlobalStateHash))>> {
1671        Ok(self
1672            .perpetual_tables
1673            .root_state_hash_by_epoch
1674            .safe_range_iter_reversed(..)
1675            .next()
1676            .transpose()?)
1677    }
1678
1679    fn insert_state_hash_for_epoch(
1680        &self,
1681        epoch: EpochId,
1682        last_checkpoint_of_epoch: &CheckpointSequenceNumber,
1683        acc: &GlobalStateHash,
1684    ) -> IotaResult {
1685        self.perpetual_tables
1686            .root_state_hash_by_epoch
1687            .insert(&epoch, &(*last_checkpoint_of_epoch, acc.clone()))?;
1688        self.root_state_notify_read
1689            .notify(&epoch, &(*last_checkpoint_of_epoch, acc.clone()));
1690
1691        Ok(())
1692    }
1693
1694    fn iter_live_object_set(&self) -> Box<dyn Iterator<Item = LiveObject> + '_> {
1695        Box::new(self.perpetual_tables.iter_live_object_set())
1696    }
1697}
1698
1699impl ObjectStore for AuthorityStore {
1700    /// Read an object and return it, or Ok(None) if the object was not found.
1701    fn try_get_object(
1702        &self,
1703        object_id: &ObjectId,
1704    ) -> Result<Option<Object>, iota_types::storage::error::Error> {
1705        self.perpetual_tables.as_ref().try_get_object(object_id)
1706    }
1707
1708    fn try_get_object_by_key(
1709        &self,
1710        object_id: &ObjectId,
1711        version: VersionNumber,
1712    ) -> Result<Option<Object>, iota_types::storage::error::Error> {
1713        self.perpetual_tables
1714            .try_get_object_by_key(object_id, version)
1715    }
1716}
1717
1718/// A wrapper to make Orphan Rule happy
1719pub struct ResolverWrapper {
1720    pub resolver: Arc<dyn BackingPackageStore + Send + Sync>,
1721    pub metrics: Arc<ResolverMetrics>,
1722}
1723
1724impl ResolverWrapper {
1725    pub fn new(
1726        resolver: Arc<dyn BackingPackageStore + Send + Sync>,
1727        metrics: Arc<ResolverMetrics>,
1728    ) -> Self {
1729        metrics.module_cache_size.set(0);
1730        ResolverWrapper { resolver, metrics }
1731    }
1732
1733    fn inc_cache_size_gauge(&self) {
1734        // reset the gauge after a restart of the cache
1735        let current = self.metrics.module_cache_size.get();
1736        self.metrics.module_cache_size.set(current + 1);
1737    }
1738}
1739
1740impl ModuleResolver for ResolverWrapper {
1741    type Error = IotaError;
1742    fn get_module(&self, module_id: &ModuleId) -> Result<Option<Vec<u8>>, Self::Error> {
1743        self.inc_cache_size_gauge();
1744        get_module(&*self.resolver, module_id)
1745    }
1746}
1747
1748pub enum UpdateType {
1749    Transaction(TransactionEffectsDigest),
1750    Genesis,
1751}
1752
1753pub type IotaLockResult = IotaResult<ObjectLockStatus>;
1754
1755#[derive(Debug, PartialEq, Eq)]
1756pub enum ObjectLockStatus {
1757    Initialized,
1758    LockedToTx { locked_by_tx: LockDetails }, // no need to use wrapper, not stored or serialized
1759    LockedAtDifferentVersion { locked_ref: ObjectReference },
1760}