sui_indexer/models/
checkpoints.rs

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