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_types::{base_types::ObjectInfo, object::Owner};
9use tracing::info;
10use typed_store::traits::Map;
11
12use crate::{
13    authority::authority_store_tables::LiveObject,
14    jsonrpc_index::{CoinInfo, IndexStore},
15    state_accumulator::AccumulatorStore,
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 AccumulatorStore, 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 object in store.iter_live_object_set() {
29        let LiveObject::Normal(object) = object else {
30            continue;
31        };
32        let Owner::AddressOwner(owner) = object.owner else {
33            continue;
34        };
35
36        // Owner Index Calculation
37        let owner_index_key = (owner, object.id());
38        let object_info = ObjectInfo::new(&object.compute_object_reference(), &object);
39
40        owner_index.insert(owner_index_key, object_info);
41
42        // Coin Index Calculation
43        if let Some(type_tag) = object.coin_type_maybe() {
44            let info =
45                CoinInfo::from_object(&object).expect("already checked that this is a coin type");
46            let key = (owner, type_tag.to_string(), object.id());
47
48            coin_index.insert(key, info);
49        }
50    }
51
52    tracing::info!("Live objects set is prepared, about to verify indexes");
53
54    // Verify Owner Index
55    for item in indexes.tables().owner_index().safe_iter() {
56        let (key, info) = item?;
57        let calculated_info = owner_index.remove(&key).ok_or_else(|| {
58            anyhow!(
59                "owner_index: found extra, unexpected entry {:?}",
60                (&key, &info)
61            )
62        })?;
63
64        if calculated_info != info {
65            bail!(
66                "owner_index: entry {key:?} is different: expected {calculated_info:?} found {info:?}"
67            );
68        }
69    }
70
71    if !owner_index.is_empty() {
72        bail!("owner_index: is missing entries: {owner_index:?}");
73    }
74    tracing::info!("Owner index is good");
75
76    // Verify Coin Index
77    for item in indexes.tables().coin_index().safe_iter() {
78        let (key, info) = item?;
79        let calculated_info = coin_index.remove(&key).ok_or_else(|| {
80            anyhow!(
81                "coin_index: found extra, unexpected entry {:?}",
82                (&key, &info)
83            )
84        })?;
85
86        if calculated_info != info {
87            bail!(
88                "coin_index: entry {key:?} is different: expected {calculated_info:?} found {info:?}"
89            );
90        }
91    }
92    tracing::info!("Coin index is good");
93
94    if !coin_index.is_empty() {
95        bail!("coin_index: is missing entries: {coin_index:?}");
96    }
97
98    info!("Finished running index verification checks");
99
100    Ok(())
101}