iota_indexer/models/
epoch.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use diesel::{Insertable, Queryable, Selectable};
6use iota_json_rpc_types::{EndOfEpochInfo, EpochInfo};
7use iota_types::iota_system_state::iota_system_state_summary::{
8    IotaSystemStateSummary, IotaSystemStateSummaryV1,
9};
10
11use crate::{
12    errors::IndexerError,
13    schema::{epochs, feature_flags, protocol_configs},
14    types::{IndexedEpochInfo, IotaSystemStateSummaryView},
15};
16
17#[derive(Queryable, Insertable, Debug, Clone, Default)]
18#[diesel(table_name = epochs)]
19#[diesel(check_for_backend(diesel::pg::Pg))]
20pub struct StoredEpochInfo {
21    pub epoch: i64,
22    pub first_checkpoint_id: i64,
23    pub epoch_start_timestamp: i64,
24    pub reference_gas_price: i64,
25    pub protocol_version: i64,
26    pub total_stake: i64,
27    pub storage_fund_balance: i64,
28    pub system_state: Vec<u8>,
29    pub epoch_total_transactions: Option<i64>,
30    pub last_checkpoint_id: Option<i64>,
31    pub epoch_end_timestamp: Option<i64>,
32    pub storage_charge: Option<i64>,
33    pub storage_rebate: Option<i64>,
34    pub total_gas_fees: Option<i64>,
35    pub total_stake_rewards_distributed: Option<i64>,
36    pub epoch_commitments: Option<Vec<u8>>,
37    pub burnt_tokens_amount: Option<i64>,
38    pub minted_tokens_amount: Option<i64>,
39}
40
41#[derive(Queryable, Insertable, Debug, Clone, Default)]
42#[diesel(table_name = protocol_configs)]
43pub struct StoredProtocolConfig {
44    pub protocol_version: i64,
45    pub config_name: String,
46    pub config_value: Option<String>,
47}
48
49#[derive(Queryable, Insertable, Debug, Clone, Default)]
50#[diesel(table_name = feature_flags)]
51pub struct StoredFeatureFlag {
52    pub protocol_version: i64,
53    pub flag_name: String,
54    pub flag_value: bool,
55}
56
57#[derive(Queryable, Selectable, Clone)]
58#[diesel(table_name = epochs)]
59#[diesel(check_for_backend(diesel::pg::Pg))]
60pub struct QueryableEpochInfo {
61    pub epoch: i64,
62    pub first_checkpoint_id: i64,
63    pub epoch_start_timestamp: i64,
64    pub reference_gas_price: i64,
65    pub protocol_version: i64,
66    pub total_stake: i64,
67    pub storage_fund_balance: i64,
68    pub epoch_total_transactions: Option<i64>,
69    pub last_checkpoint_id: Option<i64>,
70    pub epoch_end_timestamp: Option<i64>,
71    pub storage_charge: Option<i64>,
72    pub storage_rebate: Option<i64>,
73    pub total_gas_fees: Option<i64>,
74    pub total_stake_rewards_distributed: Option<i64>,
75    pub epoch_commitments: Option<Vec<u8>>,
76    pub burnt_tokens_amount: Option<i64>,
77    pub minted_tokens_amount: Option<i64>,
78}
79
80#[derive(Queryable)]
81pub struct QueryableEpochSystemState {
82    pub epoch: i64,
83    pub system_state: Vec<u8>,
84}
85
86impl StoredEpochInfo {
87    pub fn from_epoch_beginning_info(e: &IndexedEpochInfo) -> Self {
88        Self {
89            epoch: e.epoch as i64,
90            first_checkpoint_id: e.first_checkpoint_id as i64,
91            epoch_start_timestamp: e.epoch_start_timestamp as i64,
92            reference_gas_price: e.reference_gas_price as i64,
93            protocol_version: e.protocol_version as i64,
94            total_stake: e.total_stake as i64,
95            storage_fund_balance: e.storage_fund_balance as i64,
96            system_state: e.system_state.clone(),
97            ..Default::default()
98        }
99    }
100
101    pub fn from_epoch_end_info(e: &IndexedEpochInfo) -> Self {
102        Self {
103            epoch: e.epoch as i64,
104            system_state: e.system_state.clone(),
105            epoch_total_transactions: e.epoch_total_transactions.map(|v| v as i64),
106            last_checkpoint_id: e.last_checkpoint_id.map(|v| v as i64),
107            epoch_end_timestamp: e.epoch_end_timestamp.map(|v| v as i64),
108            storage_charge: e.storage_charge.map(|v| v as i64),
109            storage_rebate: e.storage_rebate.map(|v| v as i64),
110            total_gas_fees: e.total_gas_fees.map(|v| v as i64),
111            total_stake_rewards_distributed: e.total_stake_rewards_distributed.map(|v| v as i64),
112            epoch_commitments: e
113                .epoch_commitments
114                .as_ref()
115                .map(|v| bcs::to_bytes(&v).unwrap()),
116
117            // For the following fields:
118            // we don't update these columns when persisting EndOfEpoch data.
119            // However if the data is partial, diesel would interpret them
120            // as Null and hence cause errors.
121            first_checkpoint_id: 0,
122            epoch_start_timestamp: 0,
123            reference_gas_price: 0,
124            protocol_version: 0,
125            total_stake: 0,
126            storage_fund_balance: 0,
127            burnt_tokens_amount: e.burnt_tokens_amount.map(|v| v as i64),
128            minted_tokens_amount: e.minted_tokens_amount.map(|v| v as i64),
129        }
130    }
131}
132
133impl From<&StoredEpochInfo> for Option<EndOfEpochInfo> {
134    fn from(info: &StoredEpochInfo) -> Option<EndOfEpochInfo> {
135        Some(EndOfEpochInfo {
136            reference_gas_price: (info.reference_gas_price as u64),
137            protocol_version: (info.protocol_version as u64),
138            last_checkpoint_id: info.last_checkpoint_id.map(|v| v as u64)?,
139            total_stake: info.total_stake as u64,
140            storage_fund_balance: info.storage_fund_balance as u64,
141            epoch_end_timestamp: info.epoch_end_timestamp.map(|v| v as u64)?,
142            storage_charge: info.storage_charge.map(|v| v as u64)?,
143            storage_rebate: info.storage_rebate.map(|v| v as u64)?,
144            total_gas_fees: info.total_gas_fees.map(|v| v as u64)?,
145            total_stake_rewards_distributed: info
146                .total_stake_rewards_distributed
147                .map(|v| v as u64)?,
148            burnt_tokens_amount: info.burnt_tokens_amount.map(|v| v as u64)?,
149            minted_tokens_amount: info.minted_tokens_amount.map(|v| v as u64)?,
150        })
151    }
152}
153
154impl TryFrom<&StoredEpochInfo> for IotaSystemStateSummary {
155    type Error = IndexerError;
156
157    fn try_from(value: &StoredEpochInfo) -> Result<Self, Self::Error> {
158        IotaSystemStateSummaryV1::try_from(value)
159            .map(Into::into)
160            .or_else(|_| {
161                bcs::from_bytes(&value.system_state).map_err(|_| {
162                    IndexerError::PersistentStorageDataCorruption(
163                        "failed to deserialize `system_state`".into(),
164                    )
165                })
166            })
167    }
168}
169
170impl TryFrom<&StoredEpochInfo> for IotaSystemStateSummaryV1 {
171    type Error = IndexerError;
172
173    fn try_from(value: &StoredEpochInfo) -> Result<Self, Self::Error> {
174        bcs::from_bytes(&value.system_state).map_err(|_| {
175            IndexerError::PersistentStorageDataCorruption(
176                "failed to deserialize `system_state`".into(),
177            )
178        })
179    }
180}
181
182impl TryFrom<StoredEpochInfo> for EpochInfo {
183    type Error = IndexerError;
184
185    fn try_from(value: StoredEpochInfo) -> Result<Self, Self::Error> {
186        let epoch = value.epoch as u64;
187        let end_of_epoch_info = (&value).into();
188        let system_state = IotaSystemStateSummary::try_from(&value).map_err(|_| {
189            IndexerError::PersistentStorageDataCorruption(format!(
190                "failed to deserialize `system_state` for epoch {epoch}",
191            ))
192        })?;
193        Ok(EpochInfo {
194            epoch: value.epoch as u64,
195            validators: system_state.active_validators().to_vec(),
196            epoch_total_transactions: value.epoch_total_transactions.unwrap_or(0) as u64,
197            first_checkpoint_id: value.first_checkpoint_id as u64,
198            epoch_start_timestamp: value.epoch_start_timestamp as u64,
199            end_of_epoch_info,
200            reference_gas_price: Some(value.reference_gas_price as u64),
201            committee_members: system_state.to_committee_members(),
202        })
203    }
204}