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