Skip to main content

iota_json_rpc_types/
iota_checkpoint.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use iota_protocol_config::ProtocolVersion;
6use iota_sdk_types::gas::GasCostSummary;
7use iota_types::{
8    base_types::{AuthorityName, TransactionDigest},
9    committee::{EpochId, StakeUnit},
10    crypto::AggregateAuthoritySignature,
11    digests::{CheckpointDigest, Digest},
12    iota_serde::BigInt,
13    message_envelope::Message,
14    messages_checkpoint::{
15        CheckpointCommitment, CheckpointContents, CheckpointSequenceNumber, CheckpointSummary,
16        CheckpointTimestamp, ECMHLiveObjectSetDigest, EndOfEpochData,
17    },
18};
19use schemars::JsonSchema;
20use serde::{Deserialize, Serialize};
21use serde_with::{DeserializeAs, DisplayFromStr, SerializeAs, serde_as};
22
23use crate::{
24    IotaAuthorityPublicKeyBytes, Page,
25    iota_gas_cost_summary::IotaGasCostSummary,
26    iota_primitives::{
27        Base58 as Base58Schema, Base64 as Base64Schema, ProtocolVersion as ProtocolVersionSchema,
28    },
29};
30pub type CheckpointPage = Page<Checkpoint, BigInt<u64>>;
31
32#[serde_as]
33#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize, PartialEq, Eq)]
34#[serde(rename_all = "camelCase")]
35pub struct Checkpoint {
36    /// Checkpoint's epoch ID
37    #[schemars(with = "String")]
38    #[serde_as(as = "DisplayFromStr")]
39    pub epoch: EpochId,
40    /// Checkpoint sequence number
41    #[schemars(with = "String")]
42    #[serde_as(as = "DisplayFromStr")]
43    pub sequence_number: CheckpointSequenceNumber,
44    /// Checkpoint digest
45    #[serde_as(as = "Base58Schema")]
46    #[schemars(with = "Base58Schema")]
47    pub digest: CheckpointDigest,
48    /// Total number of transactions committed since genesis, including those in
49    /// this checkpoint.
50    #[schemars(with = "String")]
51    #[serde_as(as = "DisplayFromStr")]
52    pub network_total_transactions: u64,
53    /// Digest of the previous checkpoint
54    #[serde(skip_serializing_if = "Option::is_none")]
55    #[serde_as(as = "Option<Base58Schema>")]
56    #[schemars(with = "Option<Base58Schema>")]
57    pub previous_digest: Option<CheckpointDigest>,
58    /// The running total gas costs of all transactions included in the current
59    /// epoch so far until this checkpoint.
60    #[schemars(with = "IotaGasCostSummary")]
61    #[serde_as(as = "IotaGasCostSummary")]
62    pub epoch_rolling_gas_cost_summary: GasCostSummary,
63    /// Timestamp of the checkpoint - number of milliseconds from the Unix epoch
64    /// Checkpoint timestamps are monotonic, but not strongly monotonic -
65    /// subsequent checkpoints can have same timestamp if they originate
66    /// from the same underlining consensus commit
67    #[schemars(with = "String")]
68    #[serde_as(as = "DisplayFromStr")]
69    pub timestamp_ms: CheckpointTimestamp,
70    /// Present only on the final checkpoint of the epoch.
71    #[serde(skip_serializing_if = "Option::is_none")]
72    #[schemars(with = "Option<EndOfEpochDataSchema>")]
73    #[serde_as(as = "Option<EndOfEpochDataSchema>")]
74    pub end_of_epoch_data: Option<EndOfEpochData>,
75    /// Transaction digests
76    #[serde_as(as = "Vec<Base58Schema>")]
77    #[schemars(with = "Vec<Base58Schema>")]
78    pub transactions: Vec<TransactionDigest>,
79
80    /// Commitments to checkpoint state
81    #[schemars(with = "Vec<CheckpointCommitmentSchema>")]
82    #[serde_as(as = "Vec<CheckpointCommitmentSchema>")]
83    pub checkpoint_commitments: Vec<CheckpointCommitment>,
84    /// Validator Signature
85    #[schemars(with = "Base64Schema")]
86    pub validator_signature: AggregateAuthoritySignature,
87}
88
89impl
90    From<(
91        CheckpointSummary,
92        CheckpointContents,
93        AggregateAuthoritySignature,
94    )> for Checkpoint
95{
96    fn from(
97        (summary, contents, signature): (
98            CheckpointSummary,
99            CheckpointContents,
100            AggregateAuthoritySignature,
101        ),
102    ) -> Self {
103        let digest = summary.digest();
104        let CheckpointSummary {
105            epoch,
106            sequence_number,
107            network_total_transactions,
108            previous_digest,
109            epoch_rolling_gas_cost_summary,
110            timestamp_ms,
111            end_of_epoch_data,
112            ..
113        } = summary;
114
115        Checkpoint {
116            epoch,
117            sequence_number,
118            digest,
119            network_total_transactions,
120            previous_digest,
121            epoch_rolling_gas_cost_summary,
122            timestamp_ms,
123            end_of_epoch_data,
124            transactions: contents.iter().map(|digest| digest.transaction).collect(),
125            // TODO: populate commitment for rpc clients. Most likely, rpc clients don't need this
126            // info (if they need it, they need to get signed BCS data anyway in order to trust
127            // it).
128            checkpoint_commitments: Default::default(),
129            validator_signature: signature,
130        }
131    }
132}
133
134#[serde_as]
135#[derive(Serialize, Deserialize, JsonSchema)]
136#[serde(rename_all = "camelCase", rename = "EndOfEpochData")]
137pub struct EndOfEpochDataSchema {
138    /// next_epoch_committee is `Some` if and only if the current checkpoint is
139    /// the last checkpoint of an epoch.
140    /// Therefore next_epoch_committee can be used to pick the last checkpoint
141    /// of an epoch, which is often useful to get epoch level summary stats
142    /// like total gas cost of an epoch, or the total number of transactions
143    /// from genesis to the end of an epoch. The committee is stored as a
144    /// vector of validator pub key and stake pairs. The vector
145    /// should be sorted based on the Committee data structure.
146    #[schemars(with = "Vec<(IotaAuthorityPublicKeyBytes, String)>")]
147    #[serde_as(as = "Vec<(_, DisplayFromStr)>")]
148    pub next_epoch_committee: Vec<(AuthorityName, StakeUnit)>,
149
150    /// The protocol version that is in effect during the epoch that starts
151    /// immediately after this checkpoint.
152    #[schemars(with = "ProtocolVersionSchema")]
153    #[serde_as(as = "ProtocolVersionSchema")]
154    pub next_epoch_protocol_version: ProtocolVersion,
155
156    /// Commitments to epoch specific state (e.g. live object set)
157    pub epoch_commitments: Vec<CheckpointCommitmentSchema>,
158
159    /// The number of tokens that were minted (if positive) or burnt (if
160    /// negative) in this epoch.
161    pub epoch_supply_change: i64,
162}
163
164impl SerializeAs<EndOfEpochData> for EndOfEpochDataSchema {
165    fn serialize_as<S>(source: &EndOfEpochData, serializer: S) -> Result<S::Ok, S::Error>
166    where
167        S: serde::Serializer,
168    {
169        let iota_data = EndOfEpochDataSchema::from(source.clone());
170        iota_data.serialize(serializer)
171    }
172}
173
174impl<'de> DeserializeAs<'de, EndOfEpochData> for EndOfEpochDataSchema {
175    fn deserialize_as<D>(deserializer: D) -> Result<EndOfEpochData, D::Error>
176    where
177        D: serde::Deserializer<'de>,
178    {
179        let iota_data = EndOfEpochDataSchema::deserialize(deserializer)?;
180        Ok(iota_data.into())
181    }
182}
183
184impl From<EndOfEpochDataSchema> for EndOfEpochData {
185    fn from(iota_data: EndOfEpochDataSchema) -> Self {
186        let EndOfEpochDataSchema {
187            next_epoch_committee,
188            next_epoch_protocol_version,
189            epoch_commitments,
190            epoch_supply_change,
191        } = iota_data;
192        EndOfEpochData {
193            next_epoch_committee: next_epoch_committee.into_iter().collect(),
194            next_epoch_protocol_version,
195            epoch_commitments: epoch_commitments.into_iter().map(Into::into).collect(),
196            epoch_supply_change,
197        }
198    }
199}
200
201impl From<EndOfEpochData> for EndOfEpochDataSchema {
202    fn from(data: EndOfEpochData) -> Self {
203        let EndOfEpochData {
204            next_epoch_committee,
205            next_epoch_protocol_version,
206            epoch_commitments,
207            epoch_supply_change,
208        } = data;
209        EndOfEpochDataSchema {
210            next_epoch_committee: next_epoch_committee.into_iter().collect(),
211            next_epoch_protocol_version,
212            epoch_commitments: epoch_commitments.into_iter().map(Into::into).collect(),
213            epoch_supply_change,
214        }
215    }
216}
217
218#[serde_as]
219#[derive(Serialize, Deserialize, JsonSchema)]
220#[schemars(rename = "CheckpointCommitment")]
221pub enum CheckpointCommitmentSchema {
222    ECMHLiveObjectSetDigest(
223        #[schemars(with = "ECMHLiveObjectSetDigestSchema")]
224        #[serde_as(as = "ECMHLiveObjectSetDigestSchema")]
225        ECMHLiveObjectSetDigest,
226    ),
227}
228
229impl SerializeAs<CheckpointCommitment> for CheckpointCommitmentSchema {
230    fn serialize_as<S>(source: &CheckpointCommitment, serializer: S) -> Result<S::Ok, S::Error>
231    where
232        S: serde::Serializer,
233    {
234        let iota_commitment = CheckpointCommitmentSchema::from(source.clone());
235        iota_commitment.serialize(serializer)
236    }
237}
238
239impl<'de> DeserializeAs<'de, CheckpointCommitment> for CheckpointCommitmentSchema {
240    fn deserialize_as<D>(deserializer: D) -> Result<CheckpointCommitment, D::Error>
241    where
242        D: serde::Deserializer<'de>,
243    {
244        let iota_commitment = CheckpointCommitmentSchema::deserialize(deserializer)?;
245        Ok(iota_commitment.into())
246    }
247}
248
249impl From<CheckpointCommitmentSchema> for CheckpointCommitment {
250    fn from(iota_commitment: CheckpointCommitmentSchema) -> Self {
251        match iota_commitment {
252            CheckpointCommitmentSchema::ECMHLiveObjectSetDigest(digest) => {
253                CheckpointCommitment::ECMHLiveObjectSetDigest(digest)
254            }
255        }
256    }
257}
258
259impl From<CheckpointCommitment> for CheckpointCommitmentSchema {
260    fn from(commitment: CheckpointCommitment) -> Self {
261        match commitment {
262            CheckpointCommitment::ECMHLiveObjectSetDigest(digest) => {
263                CheckpointCommitmentSchema::ECMHLiveObjectSetDigest(digest)
264            }
265        }
266    }
267}
268
269/// The Sha256 digest of an EllipticCurveMultisetHash committing to the live
270/// object set.
271#[derive(Serialize, Deserialize, JsonSchema)]
272#[serde(rename = "ECMHLiveObjectSetDigest")]
273pub struct ECMHLiveObjectSetDigestSchema {
274    #[schemars(with = "[u8; 32]")]
275    pub digest: Digest,
276}
277
278impl SerializeAs<ECMHLiveObjectSetDigest> for ECMHLiveObjectSetDigestSchema {
279    fn serialize_as<S>(source: &ECMHLiveObjectSetDigest, serializer: S) -> Result<S::Ok, S::Error>
280    where
281        S: serde::Serializer,
282    {
283        let iota_digest = ECMHLiveObjectSetDigestSchema::from(source.clone());
284        iota_digest.serialize(serializer)
285    }
286}
287
288impl<'de> DeserializeAs<'de, ECMHLiveObjectSetDigest> for ECMHLiveObjectSetDigestSchema {
289    fn deserialize_as<D>(deserializer: D) -> Result<ECMHLiveObjectSetDigest, D::Error>
290    where
291        D: serde::Deserializer<'de>,
292    {
293        let iota_digest = ECMHLiveObjectSetDigestSchema::deserialize(deserializer)?;
294        Ok(iota_digest.into())
295    }
296}
297
298impl From<ECMHLiveObjectSetDigestSchema> for ECMHLiveObjectSetDigest {
299    fn from(iota_digest: ECMHLiveObjectSetDigestSchema) -> Self {
300        Self {
301            digest: iota_digest.digest,
302        }
303    }
304}
305
306impl From<ECMHLiveObjectSetDigest> for ECMHLiveObjectSetDigestSchema {
307    fn from(digest: ECMHLiveObjectSetDigest) -> Self {
308        Self {
309            digest: digest.digest,
310        }
311    }
312}
313
314#[serde_as]
315#[derive(Clone, Copy, Debug, JsonSchema, Serialize, Deserialize)]
316#[serde(untagged)]
317pub enum CheckpointId {
318    SequenceNumber(
319        #[schemars(with = "String")]
320        #[serde_as(as = "DisplayFromStr")]
321        CheckpointSequenceNumber,
322    ),
323    Digest(
324        #[serde_as(as = "Base58Schema")]
325        #[schemars(with = "Base58Schema")]
326        CheckpointDigest,
327    ),
328}
329
330impl From<CheckpointSequenceNumber> for CheckpointId {
331    fn from(seq: CheckpointSequenceNumber) -> Self {
332        Self::SequenceNumber(seq)
333    }
334}
335
336impl From<CheckpointDigest> for CheckpointId {
337    fn from(digest: CheckpointDigest) -> Self {
338        Self::Digest(digest)
339    }
340}