1use super::Digest;
2use super::GasCostSummary;
3use super::Object;
4use super::SignedTransaction;
5use super::TransactionEffects;
6use super::TransactionEvents;
7use super::UserSignature;
8use super::ValidatorAggregatedSignature;
9use super::ValidatorCommitteeMember;
10
11pub type CheckpointSequenceNumber = u64;
12pub type CheckpointTimestamp = u64;
13pub type EpochId = u64;
14pub type StakeUnit = u64;
15pub type ProtocolVersion = u64;
16
17#[derive(Clone, Debug, PartialEq, Eq)]
29#[cfg_attr(
30 feature = "serde",
31 derive(serde_derive::Serialize, serde_derive::Deserialize)
32)]
33#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
34pub enum CheckpointCommitment {
35 EcmhLiveObjectSet { digest: Digest },
38 }
40
41#[derive(Clone, Debug, PartialEq, Eq)]
53#[cfg_attr(
54 feature = "serde",
55 derive(serde_derive::Serialize, serde_derive::Deserialize)
56)]
57#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
58pub struct EndOfEpochData {
59 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=1).lift()))]
61 pub next_epoch_committee: Vec<ValidatorCommitteeMember>,
62
63 pub next_epoch_protocol_version: ProtocolVersion,
65
66 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=1).lift()))]
68 pub epoch_commitments: Vec<CheckpointCommitment>,
69}
70
71#[derive(Clone, Debug, PartialEq, Eq)]
108#[cfg_attr(
109 feature = "serde",
110 derive(serde_derive::Serialize, serde_derive::Deserialize)
111)]
112#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
113pub struct CheckpointSummary {
114 pub epoch: EpochId,
116
117 pub sequence_number: CheckpointSequenceNumber,
119
120 pub network_total_transactions: u64,
123
124 pub content_digest: Digest,
126
127 pub previous_digest: Option<Digest>,
131
132 pub epoch_rolling_gas_cost_summary: GasCostSummary,
135
136 pub timestamp_ms: CheckpointTimestamp,
140
141 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=1).lift()))]
143 pub checkpoint_commitments: Vec<CheckpointCommitment>,
144
145 pub end_of_epoch_data: Option<EndOfEpochData>,
147
148 pub version_specific_data: Vec<u8>,
153}
154
155#[derive(Clone, Debug, PartialEq)]
156#[cfg_attr(
157 feature = "serde",
158 derive(serde_derive::Serialize, serde_derive::Deserialize)
159)]
160#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
161pub struct SignedCheckpointSummary {
162 pub checkpoint: CheckpointSummary,
163 pub signature: ValidatorAggregatedSignature,
164}
165
166#[derive(Clone, Debug, PartialEq, Eq)]
184#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
185pub struct CheckpointContents(
186 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))]
187 Vec<CheckpointTransactionInfo>,
188);
189
190impl CheckpointContents {
191 pub fn new(transactions: Vec<CheckpointTransactionInfo>) -> Self {
192 Self(transactions)
193 }
194
195 pub fn transactions(&self) -> &[CheckpointTransactionInfo] {
196 &self.0
197 }
198
199 pub fn into_v1(self) -> Vec<CheckpointTransactionInfo> {
200 self.0
201 }
202}
203
204#[derive(Clone, Debug, PartialEq, Eq)]
206#[cfg_attr(
207 feature = "serde",
208 derive(serde_derive::Serialize, serde_derive::Deserialize)
209)]
210#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
211pub struct CheckpointTransactionInfo {
212 pub transaction: Digest,
213 pub effects: Digest,
214 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))]
215 pub signatures: Vec<UserSignature>,
216}
217
218#[derive(Clone, Debug, PartialEq)]
219#[cfg_attr(
220 feature = "serde",
221 derive(serde_derive::Serialize, serde_derive::Deserialize)
222)]
223#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
224pub struct CheckpointData {
225 pub checkpoint_summary: SignedCheckpointSummary,
226 pub checkpoint_contents: CheckpointContents,
227 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=1).lift()))]
228 pub transactions: Vec<CheckpointTransaction>,
229}
230
231#[derive(Clone, Debug, PartialEq, Eq)]
232#[cfg_attr(
233 feature = "serde",
234 derive(serde_derive::Serialize, serde_derive::Deserialize)
235)]
236#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
237pub struct CheckpointTransaction {
238 #[cfg_attr(
240 feature = "serde",
241 serde(with = "::serde_with::As::<crate::_serde::SignedTransactionWithIntentMessage>")
242 )]
243 pub transaction: SignedTransaction,
244 pub effects: TransactionEffects,
246 pub events: Option<TransactionEvents>,
248 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=1).lift()))]
250 pub input_objects: Vec<Object>,
251 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=1).lift()))]
253 pub output_objects: Vec<Object>,
254}
255
256#[cfg(feature = "serde")]
257#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
258mod serialization {
259 use super::*;
260
261 use serde::Deserialize;
262 use serde::Deserializer;
263 use serde::Serialize;
264 use serde::Serializer;
265
266 impl Serialize for CheckpointContents {
267 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
268 where
269 S: Serializer,
270 {
271 use serde::ser::SerializeSeq;
272 use serde::ser::SerializeTupleVariant;
273
274 #[derive(serde_derive::Serialize)]
275 struct Digests<'a> {
276 transaction: &'a Digest,
277 effects: &'a Digest,
278 }
279
280 struct DigestSeq<'a>(&'a CheckpointContents);
281 impl Serialize for DigestSeq<'_> {
282 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
283 where
284 S: Serializer,
285 {
286 let mut seq = serializer.serialize_seq(Some(self.0 .0.len()))?;
287 for txn in &self.0 .0 {
288 let digests = Digests {
289 transaction: &txn.transaction,
290 effects: &txn.effects,
291 };
292 seq.serialize_element(&digests)?;
293 }
294 seq.end()
295 }
296 }
297
298 struct SignatureSeq<'a>(&'a CheckpointContents);
299 impl Serialize for SignatureSeq<'_> {
300 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
301 where
302 S: Serializer,
303 {
304 let mut seq = serializer.serialize_seq(Some(self.0 .0.len()))?;
305 for txn in &self.0 .0 {
306 seq.serialize_element(&txn.signatures)?;
307 }
308 seq.end()
309 }
310 }
311
312 let mut s = serializer.serialize_tuple_variant("CheckpointContents", 0, "V1", 2)?;
313 s.serialize_field(&DigestSeq(self))?;
314 s.serialize_field(&SignatureSeq(self))?;
315 s.end()
316 }
317 }
318
319 #[derive(serde_derive::Deserialize)]
320 struct ExecutionDigests {
321 transaction: Digest,
322 effects: Digest,
323 }
324
325 #[derive(serde_derive::Deserialize)]
326 struct BinaryContentsV1 {
327 digests: Vec<ExecutionDigests>,
328 signatures: Vec<Vec<UserSignature>>,
329 }
330
331 #[derive(serde_derive::Deserialize)]
332 enum BinaryContents {
333 V1(BinaryContentsV1),
334 }
335
336 impl<'de> Deserialize<'de> for CheckpointContents {
337 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
338 where
339 D: Deserializer<'de>,
340 {
341 let BinaryContents::V1(BinaryContentsV1 {
342 digests,
343 signatures,
344 }) = Deserialize::deserialize(deserializer)?;
345
346 if digests.len() != signatures.len() {
347 return Err(serde::de::Error::custom(
348 "must have same number of signatures as transactions",
349 ));
350 }
351
352 Ok(Self(
353 digests
354 .into_iter()
355 .zip(signatures)
356 .map(
357 |(
358 ExecutionDigests {
359 transaction,
360 effects,
361 },
362 signatures,
363 )| CheckpointTransactionInfo {
364 transaction,
365 effects,
366 signatures,
367 },
368 )
369 .collect(),
370 ))
371 }
372 }
373
374 #[cfg(test)]
375 mod test {
376 use super::*;
377 use base64ct::Base64;
378 use base64ct::Encoding;
379
380 #[cfg(target_arch = "wasm32")]
381 use wasm_bindgen_test::wasm_bindgen_test as test;
382
383 #[test]
384 fn signed_checkpoint_fixture() {
385 const FIXTURES: &[&str] = &[
386 "CgAAAAAAAAAUAAAAAAAAABUAAAAAAAAAIJ6CIMG/6Un4MKNM8h+R9r8bQ6dNTk0WZxBMUQH1XFQBASCWUVucdQkje+4YbXVpvQZcg74nndL1NK7ccj1dDR04agAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAKAAAAAAAAAKOonlp6Vf8dJEjQYa/VyigZruaZwSwu3u/ZZVCsdrS1iaGPIAERZcNnfM75tOh10hI6MAAAAQAAAAAAAAAQAAAAAAA=",
387 "AgAAAAAAAAAFAAAAAAAAAAYAAAAAAAAAIINaPEm+WRQV2vGcPR9fe6fYhxl48GpqB+DqDYQqRHkuASBe+6BDLHSRCMiWqBkvVMqWXPWUsZnpc2gbOVdre3vnowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAQFgqGJldzxWMt2CZow1QiLmDf0RdLE6udu0bVdc1xaExX37NByF27rDH5C1DF+mkpLdA6YZnXMvuUw+zoWo71qe2DTdIDU4AcNaSUE3OoEHceuT+fBa6dMib3yDkkhmOZLyECcAAAAAAAAkAAAAAAAAAAAAAgAAAAAAAACvljn+1LWFSpu3PGx4BlIlVZq7blFK+fV7SOPEU0z9nz7lgkv8a12EA9R0tGm8hEYSOjAAAAEAAAAAAAAAEAAAAAAA",
388 ];
389
390 for fixture in FIXTURES {
391 let bcs = Base64::decode_vec(fixture).unwrap();
392
393 let checkpoint: SignedCheckpointSummary = bcs::from_bytes(&bcs).unwrap();
394 let bytes = bcs::to_bytes(&checkpoint).unwrap();
395 assert_eq!(bcs, bytes);
396 let json = serde_json::to_string_pretty(&checkpoint).unwrap();
397 println!("{json}");
398 }
399 }
400
401 #[test]
402 fn contents_fixture() {
403 let fixture ="AAEgp6oAB8Qadn8+FqtdqeDIp8ViQNOZpMKs44MN0N5y7zIgqn5dKR1+8poL0pLNwRo/2knMnodwMTEDhqYL03kdewQBAWEAgpORkfH6ewjfFQYZJhmjkYq0/B3Set4mLJX/G0wUPb/V4H41gJipYu4I6ToyixnEuPQWxHKLckhNn+0UmI+pAJ9GegzEh0q2HWABmFMpFoPw0229dCfzWNOhHW5bes4H";
404
405 let bcs = Base64::decode_vec(fixture).unwrap();
406
407 let contents: CheckpointContents = bcs::from_bytes(&bcs).unwrap();
408 let bytes = bcs::to_bytes(&contents).unwrap();
409 assert_eq!(bcs, bytes);
410 let json = serde_json::to_string_pretty(&contents).unwrap();
411 println!("{json}");
412 }
413 }
414}