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