iota_indexer/models/
checkpoints.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use diesel::prelude::*;
6use iota_json_rpc_types::Checkpoint as RpcCheckpoint;
7use iota_types::{base_types::TransactionDigest, digests::CheckpointDigest, gas::GasCostSummary};
8
9use crate::{
10    errors::IndexerError,
11    schema::{chain_identifier, checkpoints, pruner_cp_watermark},
12    types::IndexedCheckpoint,
13};
14
15#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)]
16#[diesel(table_name = chain_identifier)]
17pub struct StoredChainIdentifier {
18    pub checkpoint_digest: Vec<u8>,
19}
20
21#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)]
22#[diesel(table_name = checkpoints)]
23pub struct StoredCheckpoint {
24    pub sequence_number: i64,
25    pub checkpoint_digest: Vec<u8>,
26    pub epoch: i64,
27    pub network_total_transactions: i64,
28    pub previous_checkpoint_digest: Option<Vec<u8>>,
29    pub end_of_epoch: bool,
30    pub tx_digests: Vec<Option<Vec<u8>>>,
31    pub timestamp_ms: i64,
32    pub total_gas_cost: i64,
33    pub computation_cost: i64,
34    pub storage_cost: i64,
35    pub storage_rebate: i64,
36    pub non_refundable_storage_fee: i64,
37    pub checkpoint_commitments: Vec<u8>,
38    pub validator_signature: Vec<u8>,
39    pub end_of_epoch_data: Option<Vec<u8>>,
40    pub min_tx_sequence_number: Option<i64>,
41    pub max_tx_sequence_number: Option<i64>,
42    pub computation_cost_burned: Option<i64>,
43}
44
45impl StoredCheckpoint {
46    /// Get or derive the `computation_cost_burned`.
47    pub fn computation_cost_burned(&self) -> u64 {
48        self.computation_cost_burned
49            .unwrap_or(self.computation_cost) as u64
50    }
51}
52
53impl From<&IndexedCheckpoint> for StoredCheckpoint {
54    fn from(c: &IndexedCheckpoint) -> Self {
55        Self {
56            sequence_number: c.sequence_number as i64,
57            checkpoint_digest: c.checkpoint_digest.into_inner().to_vec(),
58            epoch: c.epoch as i64,
59            tx_digests: c
60                .tx_digests
61                .iter()
62                .map(|tx| Some(tx.into_inner().to_vec()))
63                .collect(),
64            network_total_transactions: c.network_total_transactions as i64,
65            previous_checkpoint_digest: c
66                .previous_checkpoint_digest
67                .as_ref()
68                .map(|d| (*d).into_inner().to_vec()),
69            timestamp_ms: c.timestamp_ms as i64,
70            total_gas_cost: c.total_gas_cost,
71            computation_cost: c.computation_cost as i64,
72            computation_cost_burned: Some(c.computation_cost_burned as i64),
73            storage_cost: c.storage_cost as i64,
74            storage_rebate: c.storage_rebate as i64,
75            non_refundable_storage_fee: c.non_refundable_storage_fee as i64,
76            checkpoint_commitments: bcs::to_bytes(&c.checkpoint_commitments).unwrap(),
77            validator_signature: bcs::to_bytes(&c.validator_signature).unwrap(),
78            end_of_epoch_data: c
79                .end_of_epoch_data
80                .as_ref()
81                .map(|d| bcs::to_bytes(d).unwrap()),
82            end_of_epoch: c.end_of_epoch_data.is_some(),
83            min_tx_sequence_number: Some(c.min_tx_sequence_number as i64),
84            max_tx_sequence_number: Some(c.max_tx_sequence_number as i64),
85        }
86    }
87}
88
89impl TryFrom<StoredCheckpoint> for RpcCheckpoint {
90    type Error = IndexerError;
91    fn try_from(checkpoint: StoredCheckpoint) -> Result<RpcCheckpoint, IndexerError> {
92        let computation_cost_burned = checkpoint.computation_cost_burned();
93        let parsed_digest = CheckpointDigest::try_from(checkpoint.checkpoint_digest.clone())
94            .map_err(|e| {
95                IndexerError::PersistentStorageDataCorruption(format!(
96                    "Failed to decode checkpoint digest: {:?} with err: {:?}",
97                    checkpoint.checkpoint_digest, e
98                ))
99            })?;
100
101        let parsed_previous_digest: Option<CheckpointDigest> = checkpoint
102            .previous_checkpoint_digest
103            .map(|digest| {
104                CheckpointDigest::try_from(digest.clone()).map_err(|e| {
105                    IndexerError::PersistentStorageDataCorruption(format!(
106                        "Failed to decode previous checkpoint digest: {:?} with err: {:?}",
107                        digest, e
108                    ))
109                })
110            })
111            .transpose()?;
112
113        let transactions: Vec<TransactionDigest> = {
114            {
115                checkpoint
116                    .tx_digests
117                    .into_iter()
118                    .map(|tx_digest| match tx_digest {
119                        None => Err(IndexerError::PersistentStorageDataCorruption(
120                            "tx_digests should not contain null elements".to_string(),
121                        )),
122                        Some(tx_digest) => TransactionDigest::try_from(tx_digest.as_slice())
123                            .map_err(|e| {
124                                IndexerError::PersistentStorageDataCorruption(format!(
125                                    "Failed to decode transaction digest: {:?} with err: {:?}",
126                                    tx_digest, e
127                                ))
128                            }),
129                    })
130                    .collect::<Result<Vec<TransactionDigest>, IndexerError>>()?
131            }
132        };
133        let validator_signature =
134            bcs::from_bytes(&checkpoint.validator_signature).map_err(|e| {
135                IndexerError::PersistentStorageDataCorruption(format!(
136                    "Failed to decode validator signature: {:?} with err: {:?}",
137                    checkpoint.validator_signature, e
138                ))
139            })?;
140
141        let checkpoint_commitments =
142            bcs::from_bytes(&checkpoint.checkpoint_commitments).map_err(|e| {
143                IndexerError::PersistentStorageDataCorruption(format!(
144                    "Failed to decode checkpoint commitments: {:?} with err: {:?}",
145                    checkpoint.checkpoint_commitments, e
146                ))
147            })?;
148
149        let end_of_epoch_data = checkpoint
150            .end_of_epoch_data
151            .as_ref()
152            .map(|data| {
153                bcs::from_bytes(data).map_err(|e| {
154                    IndexerError::PersistentStorageDataCorruption(format!(
155                        "Failed to decode end of epoch data: {:?} with err: {:?}",
156                        data, e
157                    ))
158                })
159            })
160            .transpose()?;
161
162        Ok(RpcCheckpoint {
163            epoch: checkpoint.epoch as u64,
164            sequence_number: checkpoint.sequence_number as u64,
165            digest: parsed_digest,
166            previous_digest: parsed_previous_digest,
167            end_of_epoch_data,
168            epoch_rolling_gas_cost_summary: GasCostSummary {
169                computation_cost: checkpoint.computation_cost as u64,
170                computation_cost_burned,
171                storage_cost: checkpoint.storage_cost as u64,
172                storage_rebate: checkpoint.storage_rebate as u64,
173                non_refundable_storage_fee: checkpoint.non_refundable_storage_fee as u64,
174            },
175            network_total_transactions: checkpoint.network_total_transactions as u64,
176            timestamp_ms: checkpoint.timestamp_ms as u64,
177            transactions,
178            validator_signature,
179            checkpoint_commitments,
180        })
181    }
182}
183
184#[derive(Queryable, Insertable, Selectable, Debug, Clone, Default)]
185#[diesel(table_name = pruner_cp_watermark)]
186pub struct StoredCpTx {
187    pub checkpoint_sequence_number: i64,
188    pub min_tx_sequence_number: i64,
189    pub max_tx_sequence_number: i64,
190}
191
192impl From<&IndexedCheckpoint> for StoredCpTx {
193    fn from(c: &IndexedCheckpoint) -> Self {
194        Self {
195            checkpoint_sequence_number: c.sequence_number as i64,
196            min_tx_sequence_number: c.min_tx_sequence_number as i64,
197            max_tx_sequence_number: c.max_tx_sequence_number as i64,
198        }
199    }
200}