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