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