1use iota_protocol_config::ProtocolVersion;
6use iota_sdk_types::{
7 CheckpointCommitment, CheckpointContents, CheckpointDigest, CheckpointSummary, Digest,
8 EndOfEpochData, GasCostSummary, TransactionDigest, ValidatorCommitteeMember,
9};
10use iota_types::{
11 base_types::AuthorityName,
12 committee::{EpochId, StakeUnit},
13 crypto::AggregateAuthoritySignature,
14 iota_serde::BigInt,
15 messages_checkpoint::{
16 CheckpointContentsExt, CheckpointSequenceNumber, CheckpointTimestamp,
17 ECMHLiveObjectSetDigest,
18 },
19};
20use schemars::JsonSchema;
21use serde::{Deserialize, Serialize};
22use serde_with::{DeserializeAs, DisplayFromStr, SerializeAs, serde_as};
23
24use crate::{
25 IotaAuthorityPublicKeyBytes, Page,
26 iota_gas_cost_summary::IotaGasCostSummary,
27 iota_primitives::{
28 Base58 as Base58Schema, Base64 as Base64Schema, ProtocolVersion as ProtocolVersionSchema,
29 },
30};
31pub type CheckpointPage = Page<Checkpoint, BigInt<u64>>;
32
33#[serde_as]
34#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize, PartialEq, Eq)]
35#[serde(rename_all = "camelCase")]
36pub struct Checkpoint {
37 #[schemars(with = "String")]
39 #[serde_as(as = "DisplayFromStr")]
40 pub epoch: EpochId,
41 #[schemars(with = "String")]
43 #[serde_as(as = "DisplayFromStr")]
44 pub sequence_number: CheckpointSequenceNumber,
45 #[serde_as(as = "Base58Schema")]
47 #[schemars(with = "Base58Schema")]
48 pub digest: CheckpointDigest,
49 #[schemars(with = "String")]
52 #[serde_as(as = "DisplayFromStr")]
53 pub network_total_transactions: u64,
54 #[serde(skip_serializing_if = "Option::is_none")]
56 #[serde_as(as = "Option<Base58Schema>")]
57 #[schemars(with = "Option<Base58Schema>")]
58 pub previous_digest: Option<CheckpointDigest>,
59 #[schemars(with = "IotaGasCostSummary")]
62 #[serde_as(as = "IotaGasCostSummary")]
63 pub epoch_rolling_gas_cost_summary: GasCostSummary,
64 #[schemars(with = "String")]
69 #[serde_as(as = "DisplayFromStr")]
70 pub timestamp_ms: CheckpointTimestamp,
71 #[serde(skip_serializing_if = "Option::is_none")]
73 #[schemars(with = "Option<EndOfEpochDataSchema>")]
74 #[serde_as(as = "Option<EndOfEpochDataSchema>")]
75 pub end_of_epoch_data: Option<EndOfEpochData>,
76 #[serde_as(as = "Vec<Base58Schema>")]
78 #[schemars(with = "Vec<Base58Schema>")]
79 pub transactions: Vec<TransactionDigest>,
80
81 #[schemars(with = "Vec<CheckpointCommitmentSchema>")]
83 #[serde_as(as = "Vec<CheckpointCommitmentSchema>")]
84 pub checkpoint_commitments: Vec<CheckpointCommitment>,
85 #[schemars(with = "Base64Schema")]
87 pub validator_signature: AggregateAuthoritySignature,
88}
89
90impl
91 From<(
92 CheckpointSummary,
93 CheckpointContents,
94 AggregateAuthoritySignature,
95 )> for Checkpoint
96{
97 fn from(
98 (summary, contents, signature): (
99 CheckpointSummary,
100 CheckpointContents,
101 AggregateAuthoritySignature,
102 ),
103 ) -> Self {
104 let digest = summary.digest();
105 let CheckpointSummary {
106 epoch,
107 sequence_number,
108 network_total_transactions,
109 previous_digest,
110 epoch_rolling_gas_cost_summary,
111 timestamp_ms,
112 end_of_epoch_data,
113 ..
114 } = summary;
115
116 Checkpoint {
117 epoch,
118 sequence_number,
119 digest,
120 network_total_transactions,
121 previous_digest,
122 epoch_rolling_gas_cost_summary,
123 timestamp_ms,
124 end_of_epoch_data,
125 transactions: contents.iter().map(|digest| digest.transaction).collect(),
126 checkpoint_commitments: Default::default(),
130 validator_signature: signature,
131 }
132 }
133}
134
135#[serde_as]
136#[derive(Serialize, Deserialize, JsonSchema)]
137#[serde(rename_all = "camelCase", rename = "EndOfEpochData")]
138pub struct EndOfEpochDataSchema {
139 #[schemars(with = "Vec<(IotaAuthorityPublicKeyBytes, String)>")]
148 #[serde_as(as = "Vec<(_, DisplayFromStr)>")]
149 pub next_epoch_committee: Vec<(AuthorityName, StakeUnit)>,
150
151 #[schemars(with = "ProtocolVersionSchema")]
154 #[serde_as(as = "ProtocolVersionSchema")]
155 pub next_epoch_protocol_version: ProtocolVersion,
156
157 pub epoch_commitments: Vec<CheckpointCommitmentSchema>,
159
160 pub epoch_supply_change: i64,
163}
164
165impl SerializeAs<EndOfEpochData> for EndOfEpochDataSchema {
166 fn serialize_as<S>(source: &EndOfEpochData, serializer: S) -> Result<S::Ok, S::Error>
167 where
168 S: serde::Serializer,
169 {
170 let iota_data = EndOfEpochDataSchema::from(source.clone());
171 iota_data.serialize(serializer)
172 }
173}
174
175impl<'de> DeserializeAs<'de, EndOfEpochData> for EndOfEpochDataSchema {
176 fn deserialize_as<D>(deserializer: D) -> Result<EndOfEpochData, D::Error>
177 where
178 D: serde::Deserializer<'de>,
179 {
180 let iota_data = EndOfEpochDataSchema::deserialize(deserializer)?;
181 Ok(iota_data.into())
182 }
183}
184
185impl From<EndOfEpochDataSchema> for EndOfEpochData {
186 fn from(iota_data: EndOfEpochDataSchema) -> Self {
187 let EndOfEpochDataSchema {
188 next_epoch_committee,
189 next_epoch_protocol_version,
190 epoch_commitments,
191 epoch_supply_change,
192 } = iota_data;
193 EndOfEpochData {
194 next_epoch_committee: next_epoch_committee
195 .into_iter()
196 .map(|(public_key, stake)| ValidatorCommitteeMember {
197 public_key: public_key.into(),
198 stake,
199 })
200 .collect(),
201 next_epoch_protocol_version: next_epoch_protocol_version.as_u64(),
202 epoch_commitments: epoch_commitments.into_iter().map(Into::into).collect(),
203 epoch_supply_change,
204 }
205 }
206}
207
208impl From<EndOfEpochData> for EndOfEpochDataSchema {
209 fn from(data: EndOfEpochData) -> Self {
210 let EndOfEpochData {
211 next_epoch_committee,
212 next_epoch_protocol_version,
213 epoch_commitments,
214 epoch_supply_change,
215 } = data;
216 EndOfEpochDataSchema {
217 next_epoch_committee: next_epoch_committee
218 .into_iter()
219 .map(|member| (member.public_key.into(), member.stake))
220 .collect(),
221 next_epoch_protocol_version: ProtocolVersion::new(next_epoch_protocol_version),
222 epoch_commitments: epoch_commitments.into_iter().map(Into::into).collect(),
223 epoch_supply_change,
224 }
225 }
226}
227
228#[serde_as]
229#[derive(Serialize, Deserialize, JsonSchema)]
230#[schemars(rename = "CheckpointCommitment")]
231pub enum CheckpointCommitmentSchema {
232 ECMHLiveObjectSetDigest(
233 #[schemars(with = "ECMHLiveObjectSetDigestSchema")]
234 #[serde_as(as = "ECMHLiveObjectSetDigestSchema")]
235 ECMHLiveObjectSetDigest,
236 ),
237}
238
239impl SerializeAs<CheckpointCommitment> for CheckpointCommitmentSchema {
240 fn serialize_as<S>(source: &CheckpointCommitment, serializer: S) -> Result<S::Ok, S::Error>
241 where
242 S: serde::Serializer,
243 {
244 let iota_commitment = CheckpointCommitmentSchema::from(source.clone());
245 iota_commitment.serialize(serializer)
246 }
247}
248
249impl<'de> DeserializeAs<'de, CheckpointCommitment> for CheckpointCommitmentSchema {
250 fn deserialize_as<D>(deserializer: D) -> Result<CheckpointCommitment, D::Error>
251 where
252 D: serde::Deserializer<'de>,
253 {
254 let iota_commitment = CheckpointCommitmentSchema::deserialize(deserializer)?;
255 Ok(iota_commitment.into())
256 }
257}
258
259impl From<CheckpointCommitmentSchema> for CheckpointCommitment {
260 fn from(iota_commitment: CheckpointCommitmentSchema) -> Self {
261 match iota_commitment {
262 CheckpointCommitmentSchema::ECMHLiveObjectSetDigest(digest) => {
263 CheckpointCommitment::EcmhLiveObjectSet {
264 digest: digest.digest,
265 }
266 }
267 }
268 }
269}
270
271impl From<CheckpointCommitment> for CheckpointCommitmentSchema {
272 fn from(commitment: CheckpointCommitment) -> Self {
273 match commitment {
274 CheckpointCommitment::EcmhLiveObjectSet { digest } => {
275 CheckpointCommitmentSchema::ECMHLiveObjectSetDigest(ECMHLiveObjectSetDigest {
276 digest,
277 })
278 }
279 _ => unimplemented!("a new CheckpointCommitment variant was added and must be handled"),
280 }
281 }
282}
283
284#[derive(Serialize, Deserialize, JsonSchema)]
287#[serde(rename = "ECMHLiveObjectSetDigest")]
288pub struct ECMHLiveObjectSetDigestSchema {
289 #[schemars(with = "[u8; 32]")]
290 pub digest: Digest,
291}
292
293impl SerializeAs<ECMHLiveObjectSetDigest> for ECMHLiveObjectSetDigestSchema {
294 fn serialize_as<S>(source: &ECMHLiveObjectSetDigest, serializer: S) -> Result<S::Ok, S::Error>
295 where
296 S: serde::Serializer,
297 {
298 let iota_digest = ECMHLiveObjectSetDigestSchema::from(source.clone());
299 iota_digest.serialize(serializer)
300 }
301}
302
303impl<'de> DeserializeAs<'de, ECMHLiveObjectSetDigest> for ECMHLiveObjectSetDigestSchema {
304 fn deserialize_as<D>(deserializer: D) -> Result<ECMHLiveObjectSetDigest, D::Error>
305 where
306 D: serde::Deserializer<'de>,
307 {
308 let iota_digest = ECMHLiveObjectSetDigestSchema::deserialize(deserializer)?;
309 Ok(iota_digest.into())
310 }
311}
312
313impl From<ECMHLiveObjectSetDigestSchema> for ECMHLiveObjectSetDigest {
314 fn from(iota_digest: ECMHLiveObjectSetDigestSchema) -> Self {
315 Self {
316 digest: iota_digest.digest,
317 }
318 }
319}
320
321impl From<ECMHLiveObjectSetDigest> for ECMHLiveObjectSetDigestSchema {
322 fn from(digest: ECMHLiveObjectSetDigest) -> Self {
323 Self {
324 digest: digest.digest,
325 }
326 }
327}
328
329#[serde_as]
330#[derive(Clone, Copy, Debug, JsonSchema, Serialize, Deserialize)]
331#[serde(untagged)]
332pub enum CheckpointId {
333 SequenceNumber(
334 #[schemars(with = "String")]
335 #[serde_as(as = "DisplayFromStr")]
336 CheckpointSequenceNumber,
337 ),
338 Digest(
339 #[serde_as(as = "Base58Schema")]
340 #[schemars(with = "Base58Schema")]
341 CheckpointDigest,
342 ),
343}
344
345impl From<CheckpointSequenceNumber> for CheckpointId {
346 fn from(seq: CheckpointSequenceNumber) -> Self {
347 Self::SequenceNumber(seq)
348 }
349}
350
351impl From<CheckpointDigest> for CheckpointId {
352 fn from(digest: CheckpointDigest) -> Self {
353 Self::Digest(digest)
354 }
355}
356
357impl std::fmt::Display for CheckpointId {
358 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
359 match self {
360 CheckpointId::SequenceNumber(seq) => write!(f, "SequenceNumber({seq})"),
361 CheckpointId::Digest(digest) => write!(f, "Digest({digest})"),
362 }
363 }
364}