iota_core/
verify_indexes.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{collections::BTreeMap, sync::Arc};
6
7use anyhow::{Result, anyhow, bail};
8use iota_storage::{IndexStore, indexes::CoinInfo};
9use iota_types::{base_types::ObjectInfo, object::Owner};
10use tracing::info;
11use typed_store::traits::Map;
12
13use crate::{authority::authority_store_tables::LiveObject, state_accumulator::AccumulatorStore};
14
15/// This is a very expensive function that verifies some of the secondary
16/// indexes. This is done by iterating through the live object set and
17/// recalculating these secodary indexes.
18pub fn verify_indexes(store: &dyn AccumulatorStore, indexes: Arc<IndexStore>) -> Result<()> {
19    info!("Begin running index verification checks");
20
21    let mut owner_index = BTreeMap::new();
22    let mut coin_index = BTreeMap::new();
23
24    tracing::info!("Reading live objects set");
25    for object in store.iter_live_object_set() {
26        let LiveObject::Normal(object) = object else {
27            continue;
28        };
29        let Owner::AddressOwner(owner) = object.owner else {
30            continue;
31        };
32
33        // Owner Index Calculation
34        let owner_index_key = (owner, object.id());
35        let object_info = ObjectInfo::new(&object.compute_object_reference(), &object);
36
37        owner_index.insert(owner_index_key, object_info);
38
39        // Coin Index Calculation
40        if let Some(type_tag) = object.coin_type_maybe() {
41            let info =
42                CoinInfo::from_object(&object).expect("already checked that this is a coin type");
43            let key = (owner, type_tag.to_string(), object.id());
44
45            coin_index.insert(key, info);
46        }
47    }
48
49    tracing::info!("Live objects set is prepared, about to verify indexes");
50
51    // Verify Owner Index
52    for (key, info) in indexes.tables().owner_index().unbounded_iter() {
53        let calculated_info = owner_index.remove(&key).ok_or_else(|| {
54            anyhow!(
55                "owner_index: found extra, unexpected entry {:?}",
56                (&key, &info)
57            )
58        })?;
59
60        if calculated_info != info {
61            bail!(
62                "owner_index: entry {key:?} is different: expected {calculated_info:?} found {info:?}"
63            );
64        }
65    }
66
67    if !owner_index.is_empty() {
68        bail!("owner_index: is missing entries: {owner_index:?}");
69    }
70    tracing::info!("Owner index is good");
71
72    // Verify Coin Index
73    for (key, info) in indexes.tables().coin_index().unbounded_iter() {
74        let calculated_info = coin_index.remove(&key).ok_or_else(|| {
75            anyhow!(
76                "coin_index: found extra, unexpected entry {:?}",
77                (&key, &info)
78            )
79        })?;
80
81        if calculated_info != info {
82            bail!(
83                "coin_index: entry {key:?} is different: expected {calculated_info:?} found {info:?}"
84            );
85        }
86    }
87    tracing::info!("Coin index is good");
88
89    if !coin_index.is_empty() {
90        bail!("coin_index: is missing entries: {coin_index:?}");
91    }
92
93    info!("Finished running index verification checks");
94
95    Ok(())
96}