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.inner());
731            let LiveObject {
732                object,
733                previous_transaction_checkpoint,
734            } = live_object;
735            let store_object_wrapper =
736                get_store_object(object.clone(), previous_transaction_checkpoint);
737            batch.insert_batch(
738                &perpetual_db.objects,
739                std::iter::once((ObjectKey::from(object.object_ref()), store_object_wrapper)),
740            )?;
741            if !object.is_child_object() {
742                Self::initialize_live_object_markers(
743                    &perpetual_db.live_owned_object_markers,
744                    &mut batch,
745                    &[object.object_ref()],
746                )?;
747            }
748        }
749        let sha3_digest = hasher.finalize().digest;
750        if *expected_sha3_digest != sha3_digest {
751            error!(
752                "Sha does not match! expected: {:?}, actual: {:?}",
753                expected_sha3_digest, sha3_digest
754            );
755            return Err(IotaError::from("Sha does not match"));
756        }
757        batch.write()?;
758        Ok(())
759    }
760
761    pub fn set_epoch_start_configuration(
762        &self,
763        epoch_start_configuration: &EpochStartConfiguration,
764    ) -> IotaResult {
765        self.perpetual_tables
766            .set_epoch_start_configuration(epoch_start_configuration)?;
767        Ok(())
768    }
769
770    pub fn get_epoch_start_configuration(&self) -> IotaResult<Option<EpochStartConfiguration>> {
771        Ok(self.perpetual_tables.epoch_start_configuration.get(&())?)
772    }
773
774    /// 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(|(object_ref, _, _)| ObjectKey(object_ref.object_id, object_ref.version));
1183        write_batch.delete_batch(&self.perpetual_tables.objects, all_new_object_keys.clone())?;
1184
1185        let modified_object_keys = effects
1186            .modified_at_versions()
1187            .into_iter()
1188            .map(|(id, version)| ObjectKey(id, version));
1189
1190        macro_rules! get_objects_and_locks {
1191            ($object_keys: expr) => {
1192                self.perpetual_tables
1193                    .objects
1194                    .multi_get($object_keys.clone())?
1195                    .into_iter()
1196                    .zip($object_keys)
1197                    .filter_map(|(obj_opt, key)| {
1198                        let obj = self
1199                            .perpetual_tables
1200                            .object(
1201                                &key,
1202                                obj_opt.unwrap_or_else(|| {
1203                                    panic!("Older object version not found: {:?}", key)
1204                                }),
1205                            )
1206                            .expect("Matching indirect object not found")?;
1207
1208                        if obj.is_immutable() {
1209                            return None;
1210                        }
1211
1212                        let obj_ref = obj.object_ref();
1213                        Some(obj.is_address_owned().then_some(obj_ref))
1214                    })
1215            };
1216        }
1217
1218        let old_locks = get_objects_and_locks!(modified_object_keys);
1219        let new_locks = get_objects_and_locks!(all_new_object_keys);
1220
1221        let old_locks: Vec<_> = old_locks.flatten().collect();
1222
1223        // Re-create old live markers.
1224        self.initialize_live_object_markers_impl(&mut write_batch, &old_locks)?;
1225
1226        // Delete new live markers
1227        write_batch.delete_batch(
1228            &self.perpetual_tables.live_owned_object_markers,
1229            new_locks.flatten(),
1230        )?;
1231
1232        write_batch.write()?;
1233
1234        Ok(())
1235    }
1236
1237    /// Return the object with version less then or eq to the provided seq
1238    /// number. This is used by indexer to find the correct version of
1239    /// dynamic field child object. We do not store the version of the child
1240    /// object, but because of lamport timestamp, we know the child must
1241    /// have version number less then or eq to the parent.
1242    pub fn find_object_lt_or_eq_version(
1243        &self,
1244        object_id: ObjectId,
1245        version: Version,
1246    ) -> IotaResult<Option<Object>> {
1247        self.perpetual_tables
1248            .find_object_lt_or_eq_version(object_id, version)
1249    }
1250
1251    /// Returns the latest object reference we have for this object_id in the
1252    /// objects table.
1253    ///
1254    /// The method may also return the reference to a deleted object with a
1255    /// digest of ObjectDigest::deleted() or ObjectDigest::wrapped() and
1256    /// lamport version of a transaction that deleted the object.
1257    /// Note that a deleted object may re-appear if the deletion was the result
1258    /// of the object being wrapped in another object.
1259    ///
1260    /// If no entry for the object_id is found, return None.
1261    pub fn get_latest_object_ref_or_tombstone(
1262        &self,
1263        object_id: ObjectId,
1264    ) -> Result<Option<ObjectReference>, IotaError> {
1265        self.perpetual_tables
1266            .get_latest_object_ref_or_tombstone(object_id)
1267    }
1268
1269    /// Returns the latest object reference if and only if the object is still
1270    /// live (i.e. it does not return tombstones)
1271    pub fn get_latest_object_ref_if_alive(
1272        &self,
1273        object_id: ObjectId,
1274    ) -> Result<Option<ObjectReference>, IotaError> {
1275        match self.get_latest_object_ref_or_tombstone(object_id)? {
1276            Some(objref) if objref.digest.is_alive() => Ok(Some(objref)),
1277            _ => Ok(None),
1278        }
1279    }
1280
1281    /// Returns the latest object we have for this object_id in the objects
1282    /// table.
1283    ///
1284    /// If no entry for the object_id is found, return None.
1285    pub fn get_latest_object_or_tombstone(
1286        &self,
1287        object_id: ObjectId,
1288    ) -> Result<Option<(ObjectKey, ObjectOrTombstone)>, IotaError> {
1289        let Some((object_key, store_object)) = self
1290            .perpetual_tables
1291            .get_latest_object_or_tombstone(object_id)?
1292        else {
1293            return Ok(None);
1294        };
1295
1296        if let Some(object_ref) = self
1297            .perpetual_tables
1298            .tombstone_reference(&object_key, &store_object)?
1299        {
1300            return Ok(Some((object_key, ObjectOrTombstone::Tombstone(object_ref))));
1301        }
1302
1303        let object = self
1304            .perpetual_tables
1305            .object(&object_key, store_object)?
1306            .expect("Non tombstone store object could not be converted to object");
1307
1308        Ok(Some((object_key, ObjectOrTombstone::Object(object))))
1309    }
1310
1311    pub fn insert_transaction_and_effects(
1312        &self,
1313        transaction: &VerifiedTransaction,
1314        transaction_effects: &TransactionEffects,
1315    ) -> Result<(), TypedStoreError> {
1316        let mut write_batch = self.perpetual_tables.transactions.batch();
1317        write_batch
1318            .insert_batch(
1319                &self.perpetual_tables.transactions,
1320                [(transaction.digest(), transaction.serializable_ref())],
1321            )?
1322            .insert_batch(
1323                &self.perpetual_tables.effects,
1324                [(transaction_effects.digest(), transaction_effects)],
1325            )?;
1326
1327        write_batch.write()?;
1328        Ok(())
1329    }
1330
1331    pub fn multi_insert_transaction_and_effects<'a>(
1332        &self,
1333        transactions: impl Iterator<Item = &'a VerifiedExecutionData>,
1334    ) -> Result<(), TypedStoreError> {
1335        let mut write_batch = self.perpetual_tables.transactions.batch();
1336        for tx in transactions {
1337            write_batch
1338                .insert_batch(
1339                    &self.perpetual_tables.transactions,
1340                    [(tx.transaction.digest(), tx.transaction.serializable_ref())],
1341                )?
1342                .insert_batch(
1343                    &self.perpetual_tables.effects,
1344                    [(tx.effects.digest(), &tx.effects)],
1345                )?;
1346        }
1347
1348        write_batch.write()?;
1349        Ok(())
1350    }
1351
1352    pub fn multi_get_transaction_blocks(
1353        &self,
1354        tx_digests: &[TransactionDigest],
1355    ) -> Result<Vec<Option<VerifiedTransaction>>, TypedStoreError> {
1356        self.perpetual_tables
1357            .transactions
1358            .multi_get(tx_digests)
1359            .map(|v| v.into_iter().map(|v| v.map(|v| v.into())).collect())
1360    }
1361
1362    pub fn get_transaction_block(
1363        &self,
1364        tx_digest: &TransactionDigest,
1365    ) -> Result<Option<VerifiedTransaction>, TypedStoreError> {
1366        self.perpetual_tables
1367            .transactions
1368            .get(tx_digest)
1369            .map(|v| v.map(|v| v.into()))
1370    }
1371
1372    /// This function reads the DB directly to get the system state object.
1373    /// If reconfiguration is happening at the same time, there is no guarantee
1374    /// whether we would be getting the old or the new system state object.
1375    /// Hence this function should only be called during RPC reads where data
1376    /// race is not a major concern. In general we should avoid this as much
1377    /// as possible. If the intent is for testing, you can use
1378    /// AuthorityState:: get_iota_system_state_object_for_testing.
1379    pub fn get_iota_system_state_object_unsafe(&self) -> IotaResult<IotaSystemState> {
1380        get_iota_system_state(self.perpetual_tables.as_ref())
1381    }
1382
1383    pub fn expensive_check_iota_conservation<T>(
1384        self: &Arc<Self>,
1385        type_layout_store: T,
1386        old_epoch_store: &AuthorityPerEpochStore,
1387        epoch_supply_change: Option<i64>,
1388    ) -> IotaResult
1389    where
1390        T: TypeLayoutStore + Send + Copy,
1391    {
1392        if !self.enable_epoch_iota_conservation_check {
1393            return Ok(());
1394        }
1395
1396        let executor = old_epoch_store.executor();
1397        info!("Starting IOTA conservation check. This may take a while..");
1398        let cur_time = Instant::now();
1399        let mut pending_objects = vec![];
1400        let mut count = 0;
1401        let mut size = 0;
1402        let (mut total_iota, mut total_storage_rebate) = thread::scope(|s| {
1403            let pending_tasks = FuturesUnordered::new();
1404            for o in self.iter_live_object_set() {
1405                let object = o.object;
1406                size += object.object_size_for_gas_metering();
1407                count += 1;
1408                pending_objects.push(object);
1409                if count % 1_000_000 == 0 {
1410                    let mut task_objects = vec![];
1411                    mem::swap(&mut pending_objects, &mut task_objects);
1412                    pending_tasks.push(s.spawn(move || {
1413                        let mut layout_resolver =
1414                            executor.type_layout_resolver(Box::new(type_layout_store));
1415                        let mut total_storage_rebate = 0;
1416                        let mut total_iota = 0;
1417                        for object in task_objects {
1418                            total_storage_rebate += object.storage_rebate;
1419                            // get_total_iota includes storage rebate, however all storage rebate is
1420                            // also stored in the storage fund, so we need to subtract it here.
1421                            total_iota += object.get_total_iota(layout_resolver.as_mut()).unwrap()
1422                                - object.storage_rebate;
1423                        }
1424                        if count % 50_000_000 == 0 {
1425                            info!("Processed {} objects", count);
1426                        }
1427                        (total_iota, total_storage_rebate)
1428                    }));
1429                }
1430            }
1431            pending_tasks.into_iter().fold((0, 0), |init, result| {
1432                let result = result.join().unwrap();
1433                (init.0 + result.0, init.1 + result.1)
1434            })
1435        });
1436        let mut layout_resolver = executor.type_layout_resolver(Box::new(type_layout_store));
1437        for object in pending_objects {
1438            total_storage_rebate += object.storage_rebate;
1439            total_iota +=
1440                object.get_total_iota(layout_resolver.as_mut()).unwrap() - object.storage_rebate;
1441        }
1442        info!(
1443            "Scanned {} live objects, took {:?}",
1444            count,
1445            cur_time.elapsed()
1446        );
1447        self.metrics
1448            .iota_conservation_live_object_count
1449            .set(count as i64);
1450        self.metrics
1451            .iota_conservation_live_object_size
1452            .set(size as i64);
1453        self.metrics
1454            .iota_conservation_check_latency
1455            .set(cur_time.elapsed().as_secs() as i64);
1456
1457        // It is safe to call this function because we are in the middle of
1458        // reconfiguration.
1459        let system_state: IotaSystemStateSummaryV2 = self
1460            .get_iota_system_state_object_unsafe()
1461            .expect("Reading iota system state object cannot fail")
1462            .into_iota_system_state_summary()
1463            .try_into()?;
1464        let storage_fund_balance = system_state.storage_fund_total_object_storage_rebates;
1465        info!(
1466            "Total IOTA amount in the network: {}, storage fund balance: {}, total storage rebate: {} at beginning of epoch {}",
1467            total_iota, storage_fund_balance, total_storage_rebate, system_state.epoch
1468        );
1469
1470        let imbalance = (storage_fund_balance as i64) - (total_storage_rebate as i64);
1471        self.metrics
1472            .iota_conservation_storage_fund
1473            .set(storage_fund_balance as i64);
1474        self.metrics
1475            .iota_conservation_storage_fund_imbalance
1476            .set(imbalance);
1477        self.metrics
1478            .iota_conservation_imbalance
1479            .set((total_iota as i128 - system_state.iota_total_supply as i128) as i64);
1480
1481        if let Some(expected_imbalance) = self
1482            .perpetual_tables
1483            .expected_storage_fund_imbalance
1484            .get(&())
1485            .map_err(|err| {
1486                IotaError::from(
1487                    format!("failed to read expected storage fund imbalance: {err}").as_str(),
1488                )
1489            })?
1490        {
1491            fp_ensure!(
1492                imbalance == expected_imbalance,
1493                IotaError::from(
1494                    format!(
1495                        "Inconsistent state detected at epoch {}: total storage rebate: {}, storage fund balance: {}, expected imbalance: {}",
1496                        system_state.epoch, total_storage_rebate, storage_fund_balance, expected_imbalance
1497                    ).as_str()
1498                )
1499            );
1500        } else {
1501            self.perpetual_tables
1502                .expected_storage_fund_imbalance
1503                .insert(&(), &imbalance)
1504                .map_err(|err| {
1505                    IotaError::from(
1506                        format!("failed to write expected storage fund imbalance: {err}").as_str(),
1507                    )
1508                })?;
1509        }
1510
1511        let total_supply = self
1512            .perpetual_tables
1513            .total_iota_supply
1514            .get(&())
1515            .map_err(|err| {
1516                IotaError::from(format!("failed to read total iota supply: {err}").as_str())
1517            })?;
1518
1519        match total_supply.zip(epoch_supply_change) {
1520            // Only execute the check if both are set and the supply value was set in the last
1521            // epoch. We have to assume the supply changes every epoch and therefore we
1522            // cannot run the check with a supply value from any epoch earlier than the
1523            // last one. This can happen if the check was disabled for some time.
1524            Some((old_supply, epoch_supply_change))
1525                if old_supply.last_check_epoch + 1 == old_epoch_store.epoch() =>
1526            {
1527                let expected_new_supply = if epoch_supply_change >= 0 {
1528                    old_supply
1529                        .total_supply
1530                        .checked_add(epoch_supply_change.unsigned_abs())
1531                        .ok_or_else(|| {
1532                            IotaError::from(
1533                                format!(
1534                                    "Inconsistent state detected at epoch {}: old supply {} + supply change {} overflowed",
1535                                    system_state.epoch, old_supply.total_supply, epoch_supply_change
1536                                ).as_str())
1537                        })?
1538                } else {
1539                    old_supply.total_supply.checked_sub(epoch_supply_change.unsigned_abs()).ok_or_else(|| {
1540                        IotaError::from(
1541                            format!(
1542                                "Inconsistent state detected at epoch {}: old supply {} - supply change {} underflowed",
1543                                system_state.epoch, old_supply.total_supply, epoch_supply_change
1544                            ).as_str())
1545                    })?
1546                };
1547
1548                fp_ensure!(
1549                    total_iota == expected_new_supply,
1550                    IotaError::from(
1551                        format!(
1552                            "Inconsistent state detected at epoch {}: total iota: {}, expecting {}",
1553                            system_state.epoch, total_iota, expected_new_supply
1554                        )
1555                        .as_str()
1556                    )
1557                );
1558
1559                let new_supply = TotalIotaSupplyCheck {
1560                    total_supply: expected_new_supply,
1561                    last_check_epoch: old_epoch_store.epoch(),
1562                };
1563
1564                self.perpetual_tables
1565                    .total_iota_supply
1566                    .insert(&(), &new_supply)
1567                    .map_err(|err| {
1568                        IotaError::from(
1569                            format!("failed to write total iota supply: {err}").as_str(),
1570                        )
1571                    })?;
1572            }
1573            // If either one is None or if the last value is from an older epoch,
1574            // we update the value in the table since we're at genesis and cannot execute the check.
1575            _ => {
1576                info!("Skipping total supply check");
1577
1578                let supply = TotalIotaSupplyCheck {
1579                    total_supply: total_iota,
1580                    last_check_epoch: old_epoch_store.epoch(),
1581                };
1582
1583                self.perpetual_tables
1584                    .total_iota_supply
1585                    .insert(&(), &supply)
1586                    .map_err(|err| {
1587                        IotaError::from(
1588                            format!("failed to write total iota supply: {err}").as_str(),
1589                        )
1590                    })?;
1591
1592                return Ok(());
1593            }
1594        };
1595
1596        Ok(())
1597    }
1598
1599    pub async fn prune_objects_and_compact_for_testing(
1600        &self,
1601        checkpoint_store: &Arc<CheckpointStore>,
1602        grpc_indexes_store: Option<&GrpcIndexesStore>,
1603    ) {
1604        let pruning_config = AuthorityStorePruningConfig {
1605            num_epochs_to_retain: 0,
1606            ..Default::default()
1607        };
1608        let _ = AuthorityStorePruner::prune_objects_for_eligible_epochs(
1609            &self.perpetual_tables,
1610            checkpoint_store,
1611            grpc_indexes_store,
1612            None,
1613            pruning_config,
1614            AuthorityStorePruningMetrics::new_for_test(),
1615            EPOCH_DURATION_MS_FOR_TESTING,
1616            None,
1617        )
1618        .await;
1619        let _ = AuthorityStorePruner::compact(&self.perpetual_tables);
1620    }
1621
1622    #[cfg(test)]
1623    pub async fn prune_objects_immediately_for_testing(
1624        &self,
1625        transaction_effects: Vec<TransactionEffects>,
1626    ) -> anyhow::Result<()> {
1627        let mut wb = self.perpetual_tables.objects.batch();
1628
1629        let mut object_keys_to_prune = vec![];
1630        for effects in &transaction_effects {
1631            for (object_id, seq_number) in effects.modified_at_versions() {
1632                info!("Pruning object {} version {:?}", object_id, seq_number);
1633                object_keys_to_prune.push(ObjectKey(object_id, seq_number));
1634            }
1635        }
1636
1637        wb.delete_batch(&self.perpetual_tables.objects, object_keys_to_prune)?;
1638        wb.write()?;
1639        Ok(())
1640    }
1641
1642    // Counts the number of versions exist in object store for `object_id`. This
1643    // includes tombstone.
1644    #[cfg(msim)]
1645    pub fn count_object_versions(&self, object_id: ObjectId) -> usize {
1646        self.perpetual_tables
1647            .objects
1648            .safe_iter_with_prefix(&object_id)
1649            .collect::<Result<Vec<_>, _>>()
1650            .unwrap()
1651            .len()
1652    }
1653}
1654
1655impl GlobalStateHashStore for AuthorityStore {
1656    fn get_root_state_hash_for_epoch(
1657        &self,
1658        epoch: EpochId,
1659    ) -> IotaResult<Option<(CheckpointSequenceNumber, GlobalStateHash)>> {
1660        self.perpetual_tables
1661            .root_state_hash_by_epoch
1662            .get(&epoch)
1663            .map_err(Into::into)
1664    }
1665
1666    fn get_root_state_hash_for_highest_epoch(
1667        &self,
1668    ) -> IotaResult<Option<(EpochId, (CheckpointSequenceNumber, GlobalStateHash))>> {
1669        Ok(self
1670            .perpetual_tables
1671            .root_state_hash_by_epoch
1672            .safe_range_iter_reversed(..)
1673            .next()
1674            .transpose()?)
1675    }
1676
1677    fn insert_state_hash_for_epoch(
1678        &self,
1679        epoch: EpochId,
1680        last_checkpoint_of_epoch: &CheckpointSequenceNumber,
1681        acc: &GlobalStateHash,
1682    ) -> IotaResult {
1683        self.perpetual_tables
1684            .root_state_hash_by_epoch
1685            .insert(&epoch, &(*last_checkpoint_of_epoch, acc.clone()))?;
1686        self.root_state_notify_read
1687            .notify(&epoch, &(*last_checkpoint_of_epoch, acc.clone()));
1688
1689        Ok(())
1690    }
1691
1692    fn iter_live_object_set(&self) -> Box<dyn Iterator<Item = LiveObject> + '_> {
1693        Box::new(self.perpetual_tables.iter_live_object_set())
1694    }
1695}
1696
1697impl ObjectStore for AuthorityStore {
1698    /// Read an object and return it, or Ok(None) if the object was not found.
1699    fn try_get_object(
1700        &self,
1701        object_id: &ObjectId,
1702    ) -> Result<Option<Object>, iota_types::storage::error::Error> {
1703        self.perpetual_tables.as_ref().try_get_object(object_id)
1704    }
1705
1706    fn try_get_object_by_key(
1707        &self,
1708        object_id: &ObjectId,
1709        version: VersionNumber,
1710    ) -> Result<Option<Object>, iota_types::storage::error::Error> {
1711        self.perpetual_tables
1712            .try_get_object_by_key(object_id, version)
1713    }
1714}
1715
1716/// A wrapper to make Orphan Rule happy
1717pub struct ResolverWrapper {
1718    pub resolver: Arc<dyn BackingPackageStore + Send + Sync>,
1719    pub metrics: Arc<ResolverMetrics>,
1720}
1721
1722impl ResolverWrapper {
1723    pub fn new(
1724        resolver: Arc<dyn BackingPackageStore + Send + Sync>,
1725        metrics: Arc<ResolverMetrics>,
1726    ) -> Self {
1727        metrics.module_cache_size.set(0);
1728        ResolverWrapper { resolver, metrics }
1729    }
1730
1731    fn inc_cache_size_gauge(&self) {
1732        // reset the gauge after a restart of the cache
1733        let current = self.metrics.module_cache_size.get();
1734        self.metrics.module_cache_size.set(current + 1);
1735    }
1736}
1737
1738impl ModuleResolver for ResolverWrapper {
1739    type Error = IotaError;
1740    fn get_module(&self, module_id: &ModuleId) -> Result<Option<Vec<u8>>, Self::Error> {
1741        self.inc_cache_size_gauge();
1742        get_module(&*self.resolver, module_id)
1743    }
1744}
1745
1746pub enum UpdateType {
1747    Transaction(TransactionEffectsDigest),
1748    Genesis,
1749}
1750
1751pub type IotaLockResult = IotaResult<ObjectLockStatus>;
1752
1753#[derive(Debug, PartialEq, Eq)]
1754pub enum ObjectLockStatus {
1755    Initialized,
1756    LockedToTx { locked_by_tx: LockDetails }, // no need to use wrapper, not stored or serialized
1757    LockedAtDifferentVersion { locked_ref: ObjectReference },
1758}