1use crate::schema::epochs;
5use crate::{errors::IndexerError, schema::feature_flags, schema::protocol_configs};
6use diesel::prelude::{AsChangeset, Identifiable};
7use diesel::{Insertable, Queryable, Selectable};
8use sui_json_rpc_types::{EndOfEpochInfo, EpochInfo};
9use sui_types::event::SystemEpochInfoEvent;
10use sui_types::messages_checkpoint::CertifiedCheckpointSummary;
11use sui_types::sui_system_state::sui_system_state_summary::SuiSystemStateSummary;
12
13#[derive(Queryable, Insertable, Debug, Clone, Default)]
14#[diesel(table_name = epochs)]
15pub struct StoredEpochInfo {
16 pub epoch: i64,
17 pub first_checkpoint_id: i64,
18 pub epoch_start_timestamp: i64,
19 pub reference_gas_price: i64,
20 pub protocol_version: i64,
21 pub total_stake: i64,
22 pub storage_fund_balance: i64,
23 pub system_state: Option<Vec<u8>>,
24 pub epoch_total_transactions: Option<i64>,
25 pub last_checkpoint_id: Option<i64>,
26 pub epoch_end_timestamp: Option<i64>,
27 pub storage_fund_reinvestment: Option<i64>,
28 pub storage_charge: Option<i64>,
29 pub storage_rebate: Option<i64>,
30 pub stake_subsidy_amount: Option<i64>,
31 pub total_gas_fees: Option<i64>,
32 pub total_stake_rewards_distributed: Option<i64>,
33 pub leftover_storage_fund_inflow: Option<i64>,
34 pub epoch_commitments: Option<Vec<u8>>,
35 pub system_state_summary_json: Option<serde_json::Value>,
37 pub first_tx_sequence_number: Option<i64>,
39}
40
41#[derive(Insertable, Identifiable, AsChangeset, Clone, Debug)]
42#[diesel(primary_key(epoch))]
43#[diesel(table_name = epochs)]
44pub struct StartOfEpochUpdate {
45 pub epoch: i64,
46 pub first_checkpoint_id: i64,
47 pub first_tx_sequence_number: i64,
48 pub epoch_start_timestamp: i64,
49 pub reference_gas_price: i64,
50 pub protocol_version: i64,
51 pub total_stake: i64,
52 pub storage_fund_balance: i64,
53 pub system_state_summary_json: serde_json::Value,
54}
55
56#[derive(Identifiable, AsChangeset, Clone, Debug)]
57#[diesel(primary_key(epoch))]
58#[diesel(table_name = epochs)]
59pub struct EndOfEpochUpdate {
60 pub epoch: i64,
61 pub epoch_total_transactions: i64,
62 pub last_checkpoint_id: i64,
63 pub epoch_end_timestamp: i64,
64 pub storage_fund_reinvestment: i64,
65 pub storage_charge: i64,
66 pub storage_rebate: i64,
67 pub stake_subsidy_amount: i64,
68 pub total_gas_fees: i64,
69 pub total_stake_rewards_distributed: i64,
70 pub leftover_storage_fund_inflow: i64,
71 pub epoch_commitments: Vec<u8>,
72}
73
74#[derive(Queryable, Insertable, Debug, Clone, Default)]
75#[diesel(table_name = protocol_configs)]
76pub struct StoredProtocolConfig {
77 pub protocol_version: i64,
78 pub config_name: String,
79 pub config_value: Option<String>,
80}
81
82#[derive(Queryable, Insertable, Debug, Clone, Default)]
83#[diesel(table_name = feature_flags)]
84pub struct StoredFeatureFlag {
85 pub protocol_version: i64,
86 pub flag_name: String,
87 pub flag_value: bool,
88}
89
90#[derive(Queryable, Selectable, Clone)]
91#[diesel(table_name = epochs)]
92pub struct QueryableEpochInfo {
93 pub epoch: i64,
94 pub first_checkpoint_id: i64,
95 pub epoch_start_timestamp: i64,
96 pub reference_gas_price: i64,
97 pub protocol_version: i64,
98 pub total_stake: i64,
99 pub storage_fund_balance: i64,
100 pub epoch_total_transactions: Option<i64>,
101 pub first_tx_sequence_number: Option<i64>,
102 pub last_checkpoint_id: Option<i64>,
103 pub epoch_end_timestamp: Option<i64>,
104 pub storage_fund_reinvestment: Option<i64>,
105 pub storage_charge: Option<i64>,
106 pub storage_rebate: Option<i64>,
107 pub stake_subsidy_amount: Option<i64>,
108 pub total_gas_fees: Option<i64>,
109 pub total_stake_rewards_distributed: Option<i64>,
110 pub leftover_storage_fund_inflow: Option<i64>,
111 pub epoch_commitments: Option<Vec<u8>>,
112}
113
114#[derive(Queryable)]
115pub struct QueryableEpochSystemState {
116 pub epoch: i64,
117 pub system_state: Vec<u8>,
118}
119
120#[derive(Default)]
121pub struct EpochStartInfo {
122 pub first_checkpoint_id: u64,
123 pub first_tx_sequence_number: u64,
124 pub total_stake: u64,
125 pub storage_fund_balance: u64,
126}
127
128impl EpochStartInfo {
129 pub fn new(
130 first_checkpoint_id: u64,
131 first_tx_sequence_number: u64,
132 epoch_event_opt: Option<&SystemEpochInfoEvent>,
133 ) -> Self {
134 Self {
135 first_checkpoint_id,
136 first_tx_sequence_number,
137 total_stake: epoch_event_opt.map(|e| e.total_stake).unwrap_or_default(),
138 storage_fund_balance: epoch_event_opt
139 .map(|e| e.storage_fund_balance)
140 .unwrap_or_default(),
141 }
142 }
143}
144
145impl StartOfEpochUpdate {
146 pub fn new(
147 new_system_state_summary: SuiSystemStateSummary,
148 epoch_start_info: EpochStartInfo,
149 ) -> Self {
150 Self {
151 epoch: new_system_state_summary.epoch as i64,
152 system_state_summary_json: serde_json::to_value(new_system_state_summary.clone())
153 .unwrap(),
154 first_checkpoint_id: epoch_start_info.first_checkpoint_id as i64,
155 first_tx_sequence_number: epoch_start_info.first_tx_sequence_number as i64,
156 epoch_start_timestamp: new_system_state_summary.epoch_start_timestamp_ms as i64,
157 reference_gas_price: new_system_state_summary.reference_gas_price as i64,
158 protocol_version: new_system_state_summary.protocol_version as i64,
159 total_stake: epoch_start_info.total_stake as i64,
160 storage_fund_balance: epoch_start_info.storage_fund_balance as i64,
161 }
162 }
163}
164
165#[derive(Default)]
166pub struct EpochEndInfo {
167 pub storage_fund_reinvestment: u64,
168 pub storage_charge: u64,
169 pub storage_rebate: u64,
170 pub leftover_storage_fund_inflow: u64,
171 pub stake_subsidy_amount: u64,
172 pub total_gas_fees: u64,
173 pub total_stake_rewards_distributed: u64,
174}
175
176impl EpochEndInfo {
177 pub fn new(epoch_event_opt: Option<&SystemEpochInfoEvent>) -> Self {
178 epoch_event_opt.map_or_else(Self::default, |epoch_event| Self {
179 storage_fund_reinvestment: epoch_event.storage_fund_reinvestment,
180 storage_charge: epoch_event.storage_charge,
181 storage_rebate: epoch_event.storage_rebate,
182 leftover_storage_fund_inflow: epoch_event.leftover_storage_fund_inflow,
183 stake_subsidy_amount: epoch_event.stake_subsidy_amount,
184 total_gas_fees: epoch_event.total_gas_fees,
185 total_stake_rewards_distributed: epoch_event.total_stake_rewards_distributed,
186 })
187 }
188}
189
190impl EndOfEpochUpdate {
191 pub fn new(
192 last_checkpoint_summary: &CertifiedCheckpointSummary,
193 first_tx_sequence_number: u64,
194 epoch_end_info: EpochEndInfo,
195 ) -> Self {
196 Self {
197 epoch: last_checkpoint_summary.epoch as i64,
198 epoch_total_transactions: (last_checkpoint_summary.network_total_transactions
199 - first_tx_sequence_number) as i64,
200 last_checkpoint_id: *last_checkpoint_summary.sequence_number() as i64,
201 epoch_end_timestamp: last_checkpoint_summary.timestamp_ms as i64,
202 storage_fund_reinvestment: epoch_end_info.storage_fund_reinvestment as i64,
203 storage_charge: epoch_end_info.storage_charge as i64,
204 storage_rebate: epoch_end_info.storage_rebate as i64,
205 leftover_storage_fund_inflow: epoch_end_info.leftover_storage_fund_inflow as i64,
206 stake_subsidy_amount: epoch_end_info.stake_subsidy_amount as i64,
207 total_gas_fees: epoch_end_info.total_gas_fees as i64,
208 total_stake_rewards_distributed: epoch_end_info.total_stake_rewards_distributed as i64,
209 epoch_commitments: bcs::to_bytes(
210 &last_checkpoint_summary
211 .end_of_epoch_data
212 .clone()
213 .unwrap()
214 .epoch_commitments,
215 )
216 .unwrap(),
217 }
218 }
219}
220
221impl StoredEpochInfo {
222 pub fn get_json_system_state_summary(&self) -> Result<SuiSystemStateSummary, IndexerError> {
223 let Some(system_state_summary_json) = self.system_state_summary_json.clone() else {
224 return Err(IndexerError::PersistentStorageDataCorruptionError(
225 "System state summary is null for the given epoch".into(),
226 ));
227 };
228 let system_state_summary: SuiSystemStateSummary =
229 serde_json::from_value(system_state_summary_json).map_err(|_| {
230 IndexerError::PersistentStorageDataCorruptionError(format!(
231 "Failed to deserialize `system_state` for epoch {:?}",
232 self.epoch,
233 ))
234 })?;
235 debug_assert_eq!(system_state_summary.epoch, self.epoch as u64);
236 Ok(system_state_summary)
237 }
238}
239
240impl From<&StoredEpochInfo> for Option<EndOfEpochInfo> {
241 fn from(info: &StoredEpochInfo) -> Option<EndOfEpochInfo> {
242 Some(EndOfEpochInfo {
243 reference_gas_price: (info.reference_gas_price as u64),
244 protocol_version: (info.protocol_version as u64),
245 last_checkpoint_id: info.last_checkpoint_id.map(|v| v as u64)?,
246 total_stake: info.total_stake as u64,
247 storage_fund_balance: info.storage_fund_balance as u64,
248 epoch_end_timestamp: info.epoch_end_timestamp.map(|v| v as u64)?,
249 storage_fund_reinvestment: info.storage_fund_reinvestment.map(|v| v as u64)?,
250 storage_charge: info.storage_charge.map(|v| v as u64)?,
251 storage_rebate: info.storage_rebate.map(|v| v as u64)?,
252 stake_subsidy_amount: info.stake_subsidy_amount.map(|v| v as u64)?,
253 total_gas_fees: info.total_gas_fees.map(|v| v as u64)?,
254 total_stake_rewards_distributed: info
255 .total_stake_rewards_distributed
256 .map(|v| v as u64)?,
257 leftover_storage_fund_inflow: info.leftover_storage_fund_inflow.map(|v| v as u64)?,
258 })
259 }
260}
261
262impl TryFrom<StoredEpochInfo> for EpochInfo {
263 type Error = IndexerError;
264
265 fn try_from(value: StoredEpochInfo) -> Result<Self, Self::Error> {
266 let end_of_epoch_info = (&value).into();
267 let system_state_summary = value.get_json_system_state_summary()?;
268 Ok(EpochInfo {
269 epoch: value.epoch as u64,
270 validators: system_state_summary.active_validators,
271 epoch_total_transactions: value.epoch_total_transactions.unwrap_or(0) as u64,
272 first_checkpoint_id: value.first_checkpoint_id as u64,
273 epoch_start_timestamp: value.epoch_start_timestamp as u64,
274 end_of_epoch_info,
275 reference_gas_price: Some(value.reference_gas_price as u64),
276 })
277 }
278}