Skip to main content

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