iota_sdk_types/
checkpoint.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2025 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use super::{
6    Digest, GasCostSummary, Object, SignedTransaction, TransactionEffects, TransactionEvents,
7    UserSignature, ValidatorAggregatedSignature, ValidatorCommitteeMember,
8};
9
10pub type CheckpointSequenceNumber = u64;
11pub type CheckpointTimestamp = u64;
12pub type EpochId = u64;
13pub type StakeUnit = u64;
14pub type ProtocolVersion = u64;
15
16/// A commitment made by a checkpoint.
17///
18/// # BCS
19///
20/// The BCS serialized form for this type is defined by the following ABNF:
21///
22/// ```text
23/// ; CheckpointCommitment is an enum and each variant is prefixed with its index
24/// checkpoint-commitment = ecmh-live-object-set
25/// ecmh-live-object-set = %x00 digest
26/// ```
27#[derive(Clone, Debug, PartialEq, Eq)]
28#[cfg_attr(
29    feature = "schemars",
30    derive(schemars::JsonSchema),
31    schemars(tag = "type", rename_all = "snake_case")
32)]
33#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
34pub enum CheckpointCommitment {
35    /// An Elliptic Curve Multiset Hash attesting to the set of Objects that
36    /// compose the live state of the IOTA blockchain.
37    EcmhLiveObjectSet { digest: Digest },
38    // Other commitment types (e.g. merkle roots) go here.
39}
40
41impl CheckpointCommitment {
42    crate::def_is!(EcmhLiveObjectSet);
43
44    pub fn as_ecmh_live_object_set_digest(&self) -> Digest {
45        let Self::EcmhLiveObjectSet { digest } = self;
46        *digest
47    }
48}
49
50/// Data, which when included in a [`CheckpointSummary`], signals the end of an
51/// `Epoch`.
52///
53/// # BCS
54///
55/// The BCS serialized form for this type is defined by the following ABNF:
56///
57/// ```text
58/// end-of-epoch-data = (vector validator-committee-member) ; next_epoch_committee
59///                     u64                                 ; next_epoch_protocol_version
60///                     (vector checkpoint-commitment)      ; epoch_commitments
61/// ```
62#[derive(Clone, Debug, PartialEq, Eq)]
63#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
64#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
65#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
66pub struct EndOfEpochData {
67    /// The set of Validators that will be in the ValidatorCommittee for the
68    /// next epoch.
69    pub next_epoch_committee: Vec<ValidatorCommitteeMember>,
70    /// The protocol version that is in effect during the next epoch.
71    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
72    #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))]
73    pub next_epoch_protocol_version: ProtocolVersion,
74    /// Commitments to epoch specific state (e.g. live object set)
75    pub epoch_commitments: Vec<CheckpointCommitment>,
76    /// The number of tokens that were minted (if positive) or burnt (if
77    /// negative) in this epoch.
78    pub epoch_supply_change: i64,
79}
80
81/// A header for a Checkpoint on the IOTA blockchain.
82///
83/// On the IOTA network, checkpoints define the history of the blockchain. They
84/// are quite similar to the concept of blocks used by other blockchains like
85/// Bitcoin or Ethereum. The IOTA blockchain, however, forms checkpoints after
86/// transaction execution has already happened to provide a certified history of
87/// the chain, instead of being formed before execution.
88///
89/// Checkpoints commit to a variety of state including but not limited to:
90/// - The hash of the previous checkpoint.
91/// - The set of transaction digests, their corresponding effects digests, as
92///   well as the set of user signatures which authorized its execution.
93/// - The object's produced by a transaction.
94/// - The set of live objects that make up the current state of the chain.
95/// - On epoch transitions, the next validator committee.
96///
97/// `CheckpointSummary`s themselves don't directly include all of the above
98/// information but they are the top-level type by which all the above are
99/// committed to transitively via cryptographic hashes included in the summary.
100/// `CheckpointSummary`s are signed and certified by a quorum of the validator
101/// committee in a given epoch in order to allow verification of the chain's
102/// state.
103///
104/// # BCS
105///
106/// The BCS serialized form for this type is defined by the following ABNF:
107///
108/// ```text
109/// checkpoint-summary = u64                            ; epoch
110///                      u64                            ; sequence_number
111///                      u64                            ; network_total_transactions
112///                      digest                         ; content_digest
113///                      (option digest)                ; previous_digest
114///                      gas-cost-summary               ; epoch_rolling_gas_cost_summary
115///                      u64                            ; timestamp_ms
116///                      (vector checkpoint-commitment) ; checkpoint_commitments
117///                      (option end-of-epoch-data)     ; end_of_epoch_data
118///                      bytes                          ; version_specific_data
119/// ```
120#[derive(Clone, Debug, PartialEq, Eq)]
121#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
122#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
123pub struct CheckpointSummary {
124    /// Epoch that this checkpoint belongs to.
125    #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))]
126    pub epoch: EpochId,
127    /// The height of this checkpoint.
128    #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))]
129    pub sequence_number: CheckpointSequenceNumber,
130    /// Total number of transactions committed since genesis, including those in
131    /// this checkpoint.
132    #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))]
133    pub network_total_transactions: u64,
134    /// The hash of the [`CheckpointContents`] for this checkpoint.
135    pub content_digest: Digest,
136    /// The hash of the previous `CheckpointSummary`.
137    ///
138    /// This will be only be `None` for the first, or genesis checkpoint.
139    pub previous_digest: Option<Digest>,
140    /// The running total gas costs of all transactions included in the current
141    /// epoch so far until this checkpoint.
142    pub epoch_rolling_gas_cost_summary: GasCostSummary,
143    /// Timestamp of the checkpoint - number of milliseconds from the Unix epoch
144    /// Checkpoint timestamps are monotonic, but not strongly monotonic -
145    /// subsequent checkpoints can have same timestamp if they originate
146    /// from the same underlining consensus commit
147    #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))]
148    pub timestamp_ms: CheckpointTimestamp,
149    /// Commitments to checkpoint-specific state.
150    #[cfg_attr(
151        feature = "schemars",
152        schemars(with = "Option<Vec<CheckpointCommitment>>")
153    )]
154    pub checkpoint_commitments: Vec<CheckpointCommitment>,
155    /// Extra data only present in the final checkpoint of an epoch.
156    pub end_of_epoch_data: Option<EndOfEpochData>,
157    /// CheckpointSummary is not an evolvable structure - it must be readable by
158    /// any version of the code. Therefore, in order to allow extensions to
159    /// be added to CheckpointSummary, we allow opaque data to be added to
160    /// checkpoints which can be deserialized based on the current
161    /// protocol version.
162    #[cfg_attr(
163        feature = "schemars",
164        schemars(with = "Option<crate::_schemars::Base64>")
165    )]
166    pub version_specific_data: Vec<u8>,
167}
168
169#[derive(Clone, Debug, PartialEq)]
170#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
171#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
172#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
173pub struct SignedCheckpointSummary {
174    pub checkpoint: CheckpointSummary,
175    pub signature: ValidatorAggregatedSignature,
176}
177
178/// The committed to contents of a checkpoint.
179///
180/// `CheckpointContents` contains a list of digests of Transactions, their
181/// effects, and the user signatures that authorized their execution included in
182/// a checkpoint.
183///
184/// # BCS
185///
186/// The BCS serialized form for this type is defined by the following ABNF:
187///
188/// ```text
189/// checkpoint-contents = %x00 checkpoint-contents-v1 ; variant 0
190///
191/// checkpoint-contents-v1 = (vector (digest digest)) ; vector of transaction and effect digests
192///                          (vector (vector bcs-user-signature)) ; set of user signatures for each
193///                                                               ; transaction. MUST be the same
194///                                                               ; length as the vector of digests
195/// ```
196#[derive(Clone, Debug, PartialEq, Eq)]
197#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
198#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
199pub struct CheckpointContents(
200    #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))]
201    pub  Vec<CheckpointTransactionInfo>,
202);
203
204impl CheckpointContents {
205    pub fn new(transactions: Vec<CheckpointTransactionInfo>) -> Self {
206        Self(transactions)
207    }
208
209    pub fn transactions(&self) -> &[CheckpointTransactionInfo] {
210        &self.0
211    }
212
213    pub fn into_v1(self) -> Vec<CheckpointTransactionInfo> {
214        self.0
215    }
216}
217
218/// Transaction information committed to in a checkpoint
219#[derive(Clone, Debug, PartialEq, Eq)]
220#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
221#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
222#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
223pub struct CheckpointTransactionInfo {
224    pub transaction: Digest,
225    pub effects: Digest,
226    #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))]
227    pub signatures: Vec<UserSignature>,
228}
229
230#[derive(Clone, Debug, PartialEq)]
231#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
232#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
233#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
234pub struct CheckpointData {
235    pub checkpoint_summary: SignedCheckpointSummary,
236    pub checkpoint_contents: CheckpointContents,
237    #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=1).lift()))]
238    pub transactions: Vec<CheckpointTransaction>,
239}
240
241#[derive(Clone, Debug, PartialEq, Eq)]
242#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
243#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
244#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
245pub struct CheckpointTransaction {
246    /// The input Transaction
247    #[cfg_attr(
248        feature = "serde",
249        serde(with = "::serde_with::As::<crate::_serde::SignedTransactionWithIntentMessage>")
250    )]
251    #[cfg_attr(feature = "schemars", schemars(with = "SignedTransaction"))]
252    pub transaction: SignedTransaction,
253    /// The effects produced by executing this transaction
254    pub effects: TransactionEffects,
255    /// The events, if any, emitted by this transaction during execution
256    pub events: Option<TransactionEvents>,
257    /// The state of all inputs to this transaction as they were prior to
258    /// execution.
259    #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))]
260    pub input_objects: Vec<Object>,
261    /// The state of all output objects created or mutated by this transaction.
262    #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))]
263    pub output_objects: Vec<Object>,
264}
265
266#[cfg(feature = "serde")]
267#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
268mod serialization {
269    use serde::{Deserialize, Deserializer, Serialize, Serializer};
270
271    use super::*;
272
273    #[derive(serde::Serialize)]
274    struct ReadableCheckpointSummaryRef<'a> {
275        #[serde(with = "crate::_serde::ReadableDisplay")]
276        epoch: &'a EpochId,
277        #[serde(with = "crate::_serde::ReadableDisplay")]
278        sequence_number: &'a CheckpointSequenceNumber,
279        #[serde(with = "crate::_serde::ReadableDisplay")]
280        network_total_transactions: &'a u64,
281        content_digest: &'a Digest,
282        #[serde(skip_serializing_if = "Option::is_none")]
283        previous_digest: &'a Option<Digest>,
284        epoch_rolling_gas_cost_summary: &'a GasCostSummary,
285        #[serde(with = "crate::_serde::ReadableDisplay")]
286        timestamp_ms: &'a CheckpointTimestamp,
287        #[serde(skip_serializing_if = "Vec::is_empty")]
288        checkpoint_commitments: &'a Vec<CheckpointCommitment>,
289        #[serde(skip_serializing_if = "Option::is_none")]
290        end_of_epoch_data: &'a Option<EndOfEpochData>,
291        #[serde(skip_serializing_if = "Vec::is_empty")]
292        #[serde(with = "::serde_with::As::<crate::_serde::Base64Encoded>")]
293        version_specific_data: &'a Vec<u8>,
294    }
295
296    #[derive(serde::Deserialize)]
297    struct ReadableCheckpointSummary {
298        #[serde(with = "crate::_serde::ReadableDisplay")]
299        epoch: EpochId,
300        #[serde(with = "crate::_serde::ReadableDisplay")]
301        sequence_number: CheckpointSequenceNumber,
302        #[serde(with = "crate::_serde::ReadableDisplay")]
303        network_total_transactions: u64,
304        content_digest: Digest,
305        #[serde(default)]
306        previous_digest: Option<Digest>,
307        epoch_rolling_gas_cost_summary: GasCostSummary,
308        #[serde(with = "crate::_serde::ReadableDisplay")]
309        timestamp_ms: CheckpointTimestamp,
310        #[serde(default)]
311        checkpoint_commitments: Vec<CheckpointCommitment>,
312        #[serde(default)]
313        end_of_epoch_data: Option<EndOfEpochData>,
314        #[serde(default)]
315        #[serde(with = "::serde_with::As::<crate::_serde::Base64Encoded>")]
316        version_specific_data: Vec<u8>,
317    }
318
319    #[derive(serde::Serialize)]
320    struct BinaryCheckpointSummaryRef<'a> {
321        epoch: &'a EpochId,
322        sequence_number: &'a CheckpointSequenceNumber,
323        network_total_transactions: &'a u64,
324        content_digest: &'a Digest,
325        previous_digest: &'a Option<Digest>,
326        epoch_rolling_gas_cost_summary: &'a GasCostSummary,
327        timestamp_ms: &'a CheckpointTimestamp,
328        checkpoint_commitments: &'a Vec<CheckpointCommitment>,
329        end_of_epoch_data: &'a Option<EndOfEpochData>,
330        version_specific_data: &'a Vec<u8>,
331    }
332
333    #[derive(serde::Deserialize)]
334    struct BinaryCheckpointSummary {
335        epoch: EpochId,
336        sequence_number: CheckpointSequenceNumber,
337        network_total_transactions: u64,
338        content_digest: Digest,
339        previous_digest: Option<Digest>,
340        epoch_rolling_gas_cost_summary: GasCostSummary,
341        timestamp_ms: CheckpointTimestamp,
342        checkpoint_commitments: Vec<CheckpointCommitment>,
343        end_of_epoch_data: Option<EndOfEpochData>,
344        version_specific_data: Vec<u8>,
345    }
346
347    impl Serialize for CheckpointSummary {
348        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
349        where
350            S: Serializer,
351        {
352            let Self {
353                epoch,
354                sequence_number,
355                network_total_transactions,
356                content_digest,
357                previous_digest,
358                epoch_rolling_gas_cost_summary,
359                timestamp_ms,
360                checkpoint_commitments,
361                end_of_epoch_data,
362                version_specific_data,
363            } = self;
364
365            if serializer.is_human_readable() {
366                let readable = ReadableCheckpointSummaryRef {
367                    epoch,
368                    sequence_number,
369                    network_total_transactions,
370                    content_digest,
371                    previous_digest,
372                    epoch_rolling_gas_cost_summary,
373                    timestamp_ms,
374                    checkpoint_commitments,
375                    end_of_epoch_data,
376                    version_specific_data,
377                };
378                readable.serialize(serializer)
379            } else {
380                let binary = BinaryCheckpointSummaryRef {
381                    epoch,
382                    sequence_number,
383                    network_total_transactions,
384                    content_digest,
385                    previous_digest,
386                    epoch_rolling_gas_cost_summary,
387                    timestamp_ms,
388                    checkpoint_commitments,
389                    end_of_epoch_data,
390                    version_specific_data,
391                };
392                binary.serialize(serializer)
393            }
394        }
395    }
396
397    impl<'de> Deserialize<'de> for CheckpointSummary {
398        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
399        where
400            D: Deserializer<'de>,
401        {
402            if deserializer.is_human_readable() {
403                let ReadableCheckpointSummary {
404                    epoch,
405                    sequence_number,
406                    network_total_transactions,
407                    content_digest,
408                    previous_digest,
409                    epoch_rolling_gas_cost_summary,
410                    timestamp_ms,
411                    checkpoint_commitments,
412                    end_of_epoch_data,
413                    version_specific_data,
414                } = Deserialize::deserialize(deserializer)?;
415                Ok(Self {
416                    epoch,
417                    sequence_number,
418                    network_total_transactions,
419                    content_digest,
420                    previous_digest,
421                    epoch_rolling_gas_cost_summary,
422                    timestamp_ms,
423                    checkpoint_commitments,
424                    end_of_epoch_data,
425                    version_specific_data,
426                })
427            } else {
428                let BinaryCheckpointSummary {
429                    epoch,
430                    sequence_number,
431                    network_total_transactions,
432                    content_digest,
433                    previous_digest,
434                    epoch_rolling_gas_cost_summary,
435                    timestamp_ms,
436                    checkpoint_commitments,
437                    end_of_epoch_data,
438                    version_specific_data,
439                } = Deserialize::deserialize(deserializer)?;
440                Ok(Self {
441                    epoch,
442                    sequence_number,
443                    network_total_transactions,
444                    content_digest,
445                    previous_digest,
446                    epoch_rolling_gas_cost_summary,
447                    timestamp_ms,
448                    checkpoint_commitments,
449                    end_of_epoch_data,
450                    version_specific_data,
451                })
452            }
453        }
454    }
455
456    impl Serialize for CheckpointContents {
457        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
458        where
459            S: Serializer,
460        {
461            use serde::ser::{SerializeSeq, SerializeTupleVariant};
462
463            if serializer.is_human_readable() {
464                serializer.serialize_newtype_struct("CheckpointContents", &self.0)
465            } else {
466                #[derive(serde::Serialize)]
467                struct Digests<'a> {
468                    transaction: &'a Digest,
469                    effects: &'a Digest,
470                }
471
472                struct DigestSeq<'a>(&'a CheckpointContents);
473                impl Serialize for DigestSeq<'_> {
474                    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
475                    where
476                        S: Serializer,
477                    {
478                        let mut seq = serializer.serialize_seq(Some(self.0.0.len()))?;
479                        for txn in &self.0.0 {
480                            let digests = Digests {
481                                transaction: &txn.transaction,
482                                effects: &txn.effects,
483                            };
484                            seq.serialize_element(&digests)?;
485                        }
486                        seq.end()
487                    }
488                }
489
490                struct SignatureSeq<'a>(&'a CheckpointContents);
491                impl Serialize for SignatureSeq<'_> {
492                    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
493                    where
494                        S: Serializer,
495                    {
496                        let mut seq = serializer.serialize_seq(Some(self.0.0.len()))?;
497                        for txn in &self.0.0 {
498                            seq.serialize_element(&txn.signatures)?;
499                        }
500                        seq.end()
501                    }
502                }
503
504                let mut s = serializer.serialize_tuple_variant("CheckpointContents", 0, "V1", 2)?;
505                s.serialize_field(&DigestSeq(self))?;
506                s.serialize_field(&SignatureSeq(self))?;
507                s.end()
508            }
509        }
510    }
511
512    #[derive(serde::Deserialize)]
513    struct ExecutionDigests {
514        transaction: Digest,
515        effects: Digest,
516    }
517
518    #[derive(serde::Deserialize)]
519    struct BinaryContentsV1 {
520        digests: Vec<ExecutionDigests>,
521        signatures: Vec<Vec<UserSignature>>,
522    }
523
524    #[derive(serde::Deserialize)]
525    enum BinaryContents {
526        V1(BinaryContentsV1),
527    }
528
529    impl<'de> Deserialize<'de> for CheckpointContents {
530        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
531        where
532            D: Deserializer<'de>,
533        {
534            if deserializer.is_human_readable() {
535                let contents: Vec<CheckpointTransactionInfo> =
536                    Deserialize::deserialize(deserializer)?;
537                Ok(Self(contents))
538            } else {
539                let BinaryContents::V1(BinaryContentsV1 {
540                    digests,
541                    signatures,
542                }) = Deserialize::deserialize(deserializer)?;
543
544                if digests.len() != signatures.len() {
545                    return Err(serde::de::Error::custom(
546                        "must have same number of signatures as transactions",
547                    ));
548                }
549
550                Ok(Self(
551                    digests
552                        .into_iter()
553                        .zip(signatures)
554                        .map(
555                            |(
556                                ExecutionDigests {
557                                    transaction,
558                                    effects,
559                                },
560                                signatures,
561                            )| CheckpointTransactionInfo {
562                                transaction,
563                                effects,
564                                signatures,
565                            },
566                        )
567                        .collect(),
568                ))
569            }
570        }
571    }
572
573    #[derive(serde::Serialize, serde::Deserialize)]
574    #[serde(tag = "type", rename_all = "snake_case")]
575    enum ReadableCommitment {
576        EcmhLiveObjectSet { digest: Digest },
577    }
578
579    #[derive(serde::Serialize, serde::Deserialize)]
580    enum BinaryCommitment {
581        EcmhLiveObjectSet { digest: Digest },
582    }
583
584    impl Serialize for CheckpointCommitment {
585        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
586        where
587            S: Serializer,
588        {
589            if serializer.is_human_readable() {
590                let readable = match *self {
591                    CheckpointCommitment::EcmhLiveObjectSet { digest } => {
592                        ReadableCommitment::EcmhLiveObjectSet { digest }
593                    }
594                };
595                readable.serialize(serializer)
596            } else {
597                let binary = match *self {
598                    CheckpointCommitment::EcmhLiveObjectSet { digest } => {
599                        BinaryCommitment::EcmhLiveObjectSet { digest }
600                    }
601                };
602                binary.serialize(serializer)
603            }
604        }
605    }
606
607    impl<'de> Deserialize<'de> for CheckpointCommitment {
608        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
609        where
610            D: Deserializer<'de>,
611        {
612            if deserializer.is_human_readable() {
613                Ok(match ReadableCommitment::deserialize(deserializer)? {
614                    ReadableCommitment::EcmhLiveObjectSet { digest } => {
615                        Self::EcmhLiveObjectSet { digest }
616                    }
617                })
618            } else {
619                Ok(match BinaryCommitment::deserialize(deserializer)? {
620                    BinaryCommitment::EcmhLiveObjectSet { digest } => {
621                        Self::EcmhLiveObjectSet { digest }
622                    }
623                })
624            }
625        }
626    }
627
628    #[cfg(test)]
629    mod tests {
630        use base64ct::{Base64, Encoding};
631        #[cfg(target_arch = "wasm32")]
632        use wasm_bindgen_test::wasm_bindgen_test as test;
633
634        use super::*;
635
636        #[test]
637        fn signed_checkpoint_fixture() {
638            // Checkpoint summaries created from a local network (iota start command)
639            // http://localhost:9000/api/v1/checkpoints to see the list of checkpoints
640            // To get the data of checkpoint 1 as base64, use:
641            // curl -s http://localhost:9000/api/v1/checkpoints/1 -H "Accept: application/bcs" | base64
642            const FIXTURES: &[&str] = &[
643                "AAAAAAAAAAABAAAAAAAAAAIAAAAAAAAAIBqk0HxZmh1Bym2oL/3TlEnvb0FZbMJ594JGx2ZX9w2oASBCLJ9nhRE2EUG3C/XMPTdJTbK/1GjM585faUsOUQhFYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC9f941lwEAAAAAAgAAAAAAAAAAAACx8KVNWdScdFfM3RDAC41byY37f2pdIhrjGI8SQVY7Vel7TCBQ/kvuRdINIrazvwgUOjAAAAEAAAAAAAEAEAAAAAAAAQA=",
644                "DQAAAAAAAAB4DgAAAAAAAEo/AAAAAAAAIGJzt6qiBfbQHQufWpLivtr60pLRjm9dy7ulx34XrVVTASCV+2EoRe+2oCMWuVWVtl3ZIEdyaJgPhs+mCXiNtq6YygAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADyWus1lwEAAAABAmCNz/bRVQQKZW9IGbExEbUsV0aoa6cvOV+6/i7DhH0egUDmJKdR/fa18gULxyBc+dMABMkLDHQK/9Mmzmc8wrI6LSTVPir+sobfxmj9QGAInW0rF7eZ3Tb5DTMuVKejONSIEwAAAAAAAGCZ8l72H4AyuRRjZGCYLFzG8TTvHdrnZlfyy/7B6/yNCXN0CA32/PDcuLxLDY4K9dgOu/8rTFmfVPQtYxLfwxQnYHjBzDR+u77FGYviWFE/OGuTDQLCdJqAPiMwlV69GhCIEwAAAAAAAAkAAAAAAAAAAQAgIeRTzjDpjnTS3fkN3QCskISnmr5Z49j8JKFBGGuQjcAA8IoalbkCAAoAAWsAAAAAAAAADQAAAAAAAAC4F4HnXo6T6kpusCM8Gm7uXzE44DhcL0Faldy/mECSwlxBrcy4taqwhCdfgWVMmAsUOjAAAAEAAAAAAAEAEAAAAAAAAQA=",
645            ];
646
647            for fixture in FIXTURES {
648                let bcs = Base64::decode_vec(fixture).unwrap();
649
650                let checkpoint: SignedCheckpointSummary = bcs::from_bytes(&bcs).unwrap();
651                let bytes = bcs::to_bytes(&checkpoint).unwrap();
652                assert_eq!(bcs, bytes);
653                let json = serde_json::to_string_pretty(&checkpoint).unwrap();
654                println!("{json}");
655            }
656        }
657
658        #[test]
659        fn contents_fixture() {
660            let fixture = "AAEgp6oAB8Qadn8+FqtdqeDIp8ViQNOZpMKs44MN0N5y7zIgqn5dKR1+8poL0pLNwRo/2knMnodwMTEDhqYL03kdewQBAWEAgpORkfH6ewjfFQYZJhmjkYq0/B3Set4mLJX/G0wUPb/V4H41gJipYu4I6ToyixnEuPQWxHKLckhNn+0UmI+pAJ9GegzEh0q2HWABmFMpFoPw0229dCfzWNOhHW5bes4H";
661
662            let bcs = Base64::decode_vec(fixture).unwrap();
663
664            let contents: CheckpointContents = bcs::from_bytes(&bcs).unwrap();
665            let bytes = bcs::to_bytes(&contents).unwrap();
666            assert_eq!(bcs, bytes);
667            let json = serde_json::to_string_pretty(&contents).unwrap();
668            println!("{json}");
669        }
670    }
671}