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 =
171 EndOfEpochDataSchema::try_from(source.clone()).map_err(serde::ser::Error::custom)?;
172 iota_data.serialize(serializer)
173 }
174}
175
176impl<'de> DeserializeAs<'de, EndOfEpochData> for EndOfEpochDataSchema {
177 fn deserialize_as<D>(deserializer: D) -> Result<EndOfEpochData, D::Error>
178 where
179 D: serde::Deserializer<'de>,
180 {
181 let iota_data = EndOfEpochDataSchema::deserialize(deserializer)?;
182 Ok(iota_data.into())
183 }
184}
185
186impl From<EndOfEpochDataSchema> for EndOfEpochData {
187 fn from(iota_data: EndOfEpochDataSchema) -> Self {
188 let EndOfEpochDataSchema {
189 next_epoch_committee,
190 next_epoch_protocol_version,
191 epoch_commitments,
192 epoch_supply_change,
193 } = iota_data;
194 EndOfEpochData {
195 next_epoch_committee: next_epoch_committee
196 .into_iter()
197 .map(|(public_key, stake)| ValidatorCommitteeMember {
198 public_key: public_key.into(),
199 stake,
200 })
201 .collect(),
202 next_epoch_protocol_version: next_epoch_protocol_version.as_u64(),
203 epoch_commitments: epoch_commitments.into_iter().map(Into::into).collect(),
204 epoch_supply_change,
205 }
206 }
207}
208
209impl TryFrom<EndOfEpochData> for EndOfEpochDataSchema {
210 type Error = anyhow::Error;
211
212 fn try_from(data: EndOfEpochData) -> Result<Self, Self::Error> {
213 let EndOfEpochData {
214 next_epoch_committee,
215 next_epoch_protocol_version,
216 epoch_commitments,
217 epoch_supply_change,
218 } = data;
219 Ok(EndOfEpochDataSchema {
220 next_epoch_committee: next_epoch_committee
221 .into_iter()
222 .map(|member| (member.public_key.into(), member.stake))
223 .collect(),
224 next_epoch_protocol_version: ProtocolVersion::new(next_epoch_protocol_version),
225 epoch_commitments: epoch_commitments
226 .into_iter()
227 .map(CheckpointCommitmentSchema::try_from)
228 .collect::<Result<_, _>>()?,
229 epoch_supply_change,
230 })
231 }
232}
233
234#[serde_as]
235#[derive(Serialize, Deserialize, JsonSchema)]
236#[schemars(rename = "CheckpointCommitment")]
237pub enum CheckpointCommitmentSchema {
238 ECMHLiveObjectSetDigest(
239 #[schemars(with = "ECMHLiveObjectSetDigestSchema")]
240 #[serde_as(as = "ECMHLiveObjectSetDigestSchema")]
241 ECMHLiveObjectSetDigest,
242 ),
243}
244
245impl SerializeAs<CheckpointCommitment> for CheckpointCommitmentSchema {
246 fn serialize_as<S>(source: &CheckpointCommitment, serializer: S) -> Result<S::Ok, S::Error>
247 where
248 S: serde::Serializer,
249 {
250 let iota_commitment = CheckpointCommitmentSchema::try_from(source.clone())
251 .map_err(serde::ser::Error::custom)?;
252 iota_commitment.serialize(serializer)
253 }
254}
255
256impl<'de> DeserializeAs<'de, CheckpointCommitment> for CheckpointCommitmentSchema {
257 fn deserialize_as<D>(deserializer: D) -> Result<CheckpointCommitment, D::Error>
258 where
259 D: serde::Deserializer<'de>,
260 {
261 let iota_commitment = CheckpointCommitmentSchema::deserialize(deserializer)?;
262 Ok(iota_commitment.into())
263 }
264}
265
266impl From<CheckpointCommitmentSchema> for CheckpointCommitment {
267 fn from(iota_commitment: CheckpointCommitmentSchema) -> Self {
268 match iota_commitment {
269 CheckpointCommitmentSchema::ECMHLiveObjectSetDigest(digest) => {
270 CheckpointCommitment::EcmhLiveObjectSet {
271 digest: digest.digest,
272 }
273 }
274 }
275 }
276}
277
278impl TryFrom<CheckpointCommitment> for CheckpointCommitmentSchema {
279 type Error = anyhow::Error;
280
281 fn try_from(commitment: CheckpointCommitment) -> Result<Self, Self::Error> {
282 Ok(match commitment {
283 CheckpointCommitment::EcmhLiveObjectSet { digest } => {
284 CheckpointCommitmentSchema::ECMHLiveObjectSetDigest(ECMHLiveObjectSetDigest {
285 digest,
286 })
287 }
288 _ => anyhow::bail!("unknown CheckpointCommitment variant"),
289 })
290 }
291}
292
293#[derive(Serialize, Deserialize, JsonSchema)]
296#[serde(rename = "ECMHLiveObjectSetDigest")]
297pub struct ECMHLiveObjectSetDigestSchema {
298 #[schemars(with = "[u8; 32]")]
299 pub digest: Digest,
300}
301
302impl SerializeAs<ECMHLiveObjectSetDigest> for ECMHLiveObjectSetDigestSchema {
303 fn serialize_as<S>(source: &ECMHLiveObjectSetDigest, serializer: S) -> Result<S::Ok, S::Error>
304 where
305 S: serde::Serializer,
306 {
307 let iota_digest = ECMHLiveObjectSetDigestSchema::from(source.clone());
308 iota_digest.serialize(serializer)
309 }
310}
311
312impl<'de> DeserializeAs<'de, ECMHLiveObjectSetDigest> for ECMHLiveObjectSetDigestSchema {
313 fn deserialize_as<D>(deserializer: D) -> Result<ECMHLiveObjectSetDigest, D::Error>
314 where
315 D: serde::Deserializer<'de>,
316 {
317 let iota_digest = ECMHLiveObjectSetDigestSchema::deserialize(deserializer)?;
318 Ok(iota_digest.into())
319 }
320}
321
322impl From<ECMHLiveObjectSetDigestSchema> for ECMHLiveObjectSetDigest {
323 fn from(iota_digest: ECMHLiveObjectSetDigestSchema) -> Self {
324 Self {
325 digest: iota_digest.digest,
326 }
327 }
328}
329
330impl From<ECMHLiveObjectSetDigest> for ECMHLiveObjectSetDigestSchema {
331 fn from(digest: ECMHLiveObjectSetDigest) -> Self {
332 Self {
333 digest: digest.digest,
334 }
335 }
336}
337
338#[serde_as]
339#[derive(Clone, Copy, Debug, JsonSchema, Serialize, Deserialize)]
340#[serde(untagged)]
341pub enum CheckpointId {
342 SequenceNumber(
343 #[schemars(with = "String")]
344 #[serde_as(as = "DisplayFromStr")]
345 CheckpointSequenceNumber,
346 ),
347 Digest(
348 #[serde_as(as = "Base58Schema")]
349 #[schemars(with = "Base58Schema")]
350 CheckpointDigest,
351 ),
352}
353
354impl From<CheckpointSequenceNumber> for CheckpointId {
355 fn from(seq: CheckpointSequenceNumber) -> Self {
356 Self::SequenceNumber(seq)
357 }
358}
359
360impl From<CheckpointDigest> for CheckpointId {
361 fn from(digest: CheckpointDigest) -> Self {
362 Self::Digest(digest)
363 }
364}
365
366impl std::fmt::Display for CheckpointId {
367 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
368 match self {
369 CheckpointId::SequenceNumber(seq) => write!(f, "SequenceNumber({seq})"),
370 CheckpointId::Digest(digest) => write!(f, "Digest({digest})"),
371 }
372 }
373}