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