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))]
34#[non_exhaustive]
35pub enum CheckpointCommitment {
36 EcmhLiveObjectSet { digest: Digest },
39
40 CheckpointArtifacts { digest: Digest },
42}
43
44#[derive(Clone, Debug, PartialEq, Eq)]
56#[cfg_attr(
57 feature = "serde",
58 derive(serde_derive::Serialize, serde_derive::Deserialize)
59)]
60#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
61pub struct EndOfEpochData {
62 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=1).lift()))]
64 pub next_epoch_committee: Vec<ValidatorCommitteeMember>,
65
66 pub next_epoch_protocol_version: ProtocolVersion,
68
69 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=1).lift()))]
71 pub epoch_commitments: Vec<CheckpointCommitment>,
72}
73
74#[derive(Clone, Debug, PartialEq, Eq)]
111#[cfg_attr(
112 feature = "serde",
113 derive(serde_derive::Serialize, serde_derive::Deserialize)
114)]
115#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
116pub struct CheckpointSummary {
117 pub epoch: EpochId,
119
120 pub sequence_number: CheckpointSequenceNumber,
122
123 pub network_total_transactions: u64,
126
127 pub content_digest: Digest,
129
130 pub previous_digest: Option<Digest>,
134
135 pub epoch_rolling_gas_cost_summary: GasCostSummary,
138
139 pub timestamp_ms: CheckpointTimestamp,
143
144 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=1).lift()))]
146 pub checkpoint_commitments: Vec<CheckpointCommitment>,
147
148 pub end_of_epoch_data: Option<EndOfEpochData>,
150
151 pub version_specific_data: Vec<u8>,
156}
157
158#[derive(Clone, Debug, PartialEq)]
159#[cfg_attr(
160 feature = "serde",
161 derive(serde_derive::Serialize, serde_derive::Deserialize)
162)]
163#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
164pub struct SignedCheckpointSummary {
165 pub checkpoint: CheckpointSummary,
166 pub signature: ValidatorAggregatedSignature,
167}
168
169#[derive(Clone, Debug, PartialEq, Eq)]
187#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
188pub struct CheckpointContents(
189 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))]
190 Vec<CheckpointTransactionInfo>,
191);
192
193impl CheckpointContents {
194 pub fn new(transactions: Vec<CheckpointTransactionInfo>) -> Self {
195 Self(transactions)
196 }
197
198 pub fn transactions(&self) -> &[CheckpointTransactionInfo] {
199 &self.0
200 }
201
202 pub fn into_v1(self) -> Vec<CheckpointTransactionInfo> {
203 self.0
204 }
205}
206
207#[derive(Clone, Debug, PartialEq, Eq)]
209#[cfg_attr(
210 feature = "serde",
211 derive(serde_derive::Serialize, serde_derive::Deserialize)
212)]
213#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
214pub struct CheckpointTransactionInfo {
215 pub transaction: Digest,
216 pub effects: Digest,
217 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))]
218 pub signatures: Vec<UserSignature>,
219}
220
221#[derive(Clone, Debug, PartialEq)]
222#[cfg_attr(
223 feature = "serde",
224 derive(serde_derive::Serialize, serde_derive::Deserialize)
225)]
226#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
227pub struct CheckpointData {
228 pub checkpoint_summary: SignedCheckpointSummary,
229 pub checkpoint_contents: CheckpointContents,
230 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=1).lift()))]
231 pub transactions: Vec<CheckpointTransaction>,
232}
233
234#[derive(Clone, Debug, PartialEq, Eq)]
235#[cfg_attr(
236 feature = "serde",
237 derive(serde_derive::Serialize, serde_derive::Deserialize)
238)]
239#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
240pub struct CheckpointTransaction {
241 #[cfg_attr(
243 feature = "serde",
244 serde(with = "::serde_with::As::<crate::_serde::SignedTransactionWithIntentMessage>")
245 )]
246 pub transaction: SignedTransaction,
247 pub effects: TransactionEffects,
249 pub events: Option<TransactionEvents>,
251 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=1).lift()))]
253 pub input_objects: Vec<Object>,
254 #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=1).lift()))]
256 pub output_objects: Vec<Object>,
257}
258
259#[cfg(feature = "serde")]
260#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
261mod serialization {
262 use super::*;
263
264 use serde::Deserialize;
265 use serde::Deserializer;
266 use serde::Serialize;
267 use serde::Serializer;
268
269 impl Serialize for CheckpointContents {
270 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
271 where
272 S: Serializer,
273 {
274 use serde::ser::SerializeSeq;
275 use serde::ser::SerializeTupleVariant;
276
277 #[derive(serde_derive::Serialize)]
278 struct Digests<'a> {
279 transaction: &'a Digest,
280 effects: &'a Digest,
281 }
282
283 struct DigestSeq<'a>(&'a CheckpointContents);
284 impl Serialize for DigestSeq<'_> {
285 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
286 where
287 S: Serializer,
288 {
289 let mut seq = serializer.serialize_seq(Some(self.0 .0.len()))?;
290 for txn in &self.0 .0 {
291 let digests = Digests {
292 transaction: &txn.transaction,
293 effects: &txn.effects,
294 };
295 seq.serialize_element(&digests)?;
296 }
297 seq.end()
298 }
299 }
300
301 struct SignatureSeq<'a>(&'a CheckpointContents);
302 impl Serialize for SignatureSeq<'_> {
303 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
304 where
305 S: Serializer,
306 {
307 let mut seq = serializer.serialize_seq(Some(self.0 .0.len()))?;
308 for txn in &self.0 .0 {
309 seq.serialize_element(&txn.signatures)?;
310 }
311 seq.end()
312 }
313 }
314
315 let mut s = serializer.serialize_tuple_variant("CheckpointContents", 0, "V1", 2)?;
316 s.serialize_field(&DigestSeq(self))?;
317 s.serialize_field(&SignatureSeq(self))?;
318 s.end()
319 }
320 }
321
322 #[derive(serde_derive::Deserialize)]
323 struct ExecutionDigests {
324 transaction: Digest,
325 effects: Digest,
326 }
327
328 #[derive(serde_derive::Deserialize)]
329 struct BinaryContentsV1 {
330 digests: Vec<ExecutionDigests>,
331 signatures: Vec<Vec<UserSignature>>,
332 }
333
334 #[derive(serde_derive::Deserialize)]
335 enum BinaryContents {
336 V1(BinaryContentsV1),
337 }
338
339 impl<'de> Deserialize<'de> for CheckpointContents {
340 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
341 where
342 D: Deserializer<'de>,
343 {
344 let BinaryContents::V1(BinaryContentsV1 {
345 digests,
346 signatures,
347 }) = Deserialize::deserialize(deserializer)?;
348
349 if digests.len() != signatures.len() {
350 return Err(serde::de::Error::custom(
351 "must have same number of signatures as transactions",
352 ));
353 }
354
355 Ok(Self(
356 digests
357 .into_iter()
358 .zip(signatures)
359 .map(
360 |(
361 ExecutionDigests {
362 transaction,
363 effects,
364 },
365 signatures,
366 )| CheckpointTransactionInfo {
367 transaction,
368 effects,
369 signatures,
370 },
371 )
372 .collect(),
373 ))
374 }
375 }
376
377 #[cfg(test)]
378 mod test {
379 use super::*;
380 use base64ct::Base64;
381 use base64ct::Encoding;
382
383 #[cfg(target_arch = "wasm32")]
384 use wasm_bindgen_test::wasm_bindgen_test as test;
385
386 #[test]
387 fn signed_checkpoint_fixture() {
388 const FIXTURES: &[&str] = &[
389 "CgAAAAAAAAAUAAAAAAAAABUAAAAAAAAAIJ6CIMG/6Un4MKNM8h+R9r8bQ6dNTk0WZxBMUQH1XFQBASCWUVucdQkje+4YbXVpvQZcg74nndL1NK7ccj1dDR04agAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAKAAAAAAAAAKOonlp6Vf8dJEjQYa/VyigZruaZwSwu3u/ZZVCsdrS1iaGPIAERZcNnfM75tOh10hI6MAAAAQAAAAAAAAAQAAAAAAA=",
390 "AgAAAAAAAAAFAAAAAAAAAAYAAAAAAAAAIINaPEm+WRQV2vGcPR9fe6fYhxl48GpqB+DqDYQqRHkuASBe+6BDLHSRCMiWqBkvVMqWXPWUsZnpc2gbOVdre3vnowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAQFgqGJldzxWMt2CZow1QiLmDf0RdLE6udu0bVdc1xaExX37NByF27rDH5C1DF+mkpLdA6YZnXMvuUw+zoWo71qe2DTdIDU4AcNaSUE3OoEHceuT+fBa6dMib3yDkkhmOZLyECcAAAAAAAAkAAAAAAAAAAAAAgAAAAAAAACvljn+1LWFSpu3PGx4BlIlVZq7blFK+fV7SOPEU0z9nz7lgkv8a12EA9R0tGm8hEYSOjAAAAEAAAAAAAAAEAAAAAAA",
391 "AAAAAAAAAAACAAAAAAAAAAgAAAAAAAAAIJBUX7gl7mh+M/NoHcFa3oR3I+5BFublxXc33/GPUZ79ASAyWbpVsiA3AaeLJkcLPhQy4QKHM66TkJNFPJLaVqfoJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjf/gOJgBAAABASDxpIv3q2qAdsaAF16ZXzTSU8tRSJ5ylwIRyOzYsdeZigACAAAAAAAAAAAAALkSpPtV6n0lfTq6upYfSk7ZWw8avL3vaG/tU6s2ELoUKK3ucADvyjsDGNVKkhhGkhI6MAAAAQAAAAAAAAAQAAAAAAA="
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}