1use crate::base_types::{
5 ExecutionData, ExecutionDigests, FullObjectRef, VerifiedExecutionData, random_object_ref,
6};
7use crate::base_types::{ObjectID, SequenceNumber};
8use crate::committee::{EpochId, ProtocolVersion, StakeUnit};
9use crate::crypto::{
10 AccountKeyPair, AggregateAuthoritySignature, AuthoritySignInfo, AuthoritySignInfoTrait,
11 AuthorityStrongQuorumSignInfo, RandomnessRound, default_hash, get_key_pair,
12};
13use crate::digests::{CheckpointArtifactsDigest, Digest, ObjectDigest};
14use crate::effects::{TestEffectsBuilder, TransactionEffects, TransactionEffectsAPI};
15use crate::error::SuiResult;
16use crate::full_checkpoint_content::{Checkpoint, CheckpointData};
17use crate::gas::GasCostSummary;
18use crate::global_state_hash::GlobalStateHash;
19use crate::message_envelope::{Envelope, Message, TrustedEnvelope, VerifiedEnvelope};
20use crate::signature::GenericSignature;
21use crate::sui_serde::AsProtocolVersion;
22use crate::sui_serde::BigInt;
23use crate::sui_serde::Readable;
24use crate::transaction::{Transaction, TransactionData};
25use crate::{base_types::AuthorityName, committee::Committee, error::SuiErrorKind};
26use anyhow::Result;
27use fastcrypto::hash::Blake2b256;
28use fastcrypto::hash::MultisetHash;
29use fastcrypto::merkle::MerkleTree;
30use mysten_common::ZipDebugEqIteratorExt;
31use mysten_metrics::histogram::Histogram as MystenHistogram;
32use once_cell::sync::OnceCell;
33use prometheus::Histogram;
34use schemars::JsonSchema;
35use serde::{Deserialize, Serialize};
36use serde_with::serde_as;
37use shared_crypto::intent::{Intent, IntentScope};
38use std::collections::{BTreeMap, BTreeSet};
39use std::fmt::{Debug, Display, Formatter};
40use std::slice::Iter;
41use std::time::{Duration, SystemTime, UNIX_EPOCH};
42use sui_protocol_config::ProtocolConfig;
43use tap::TapFallible;
44use tracing::warn;
45
46pub use crate::digests::CheckpointContentsDigest;
47pub use crate::digests::CheckpointDigest;
48
49pub type CheckpointSequenceNumber = u64;
50pub type CheckpointTimestamp = u64;
51
52#[derive(Clone, Debug, Serialize, Deserialize)]
53pub struct CheckpointRequest {
54 pub sequence_number: Option<CheckpointSequenceNumber>,
57 pub request_content: bool,
60}
61
62#[derive(Clone, Debug, Serialize, Deserialize)]
63pub struct CheckpointRequestV2 {
64 pub sequence_number: Option<CheckpointSequenceNumber>,
68 pub request_content: bool,
71 pub certified: bool,
73}
74
75#[allow(clippy::large_enum_variant)]
76#[derive(Clone, Debug, Serialize, Deserialize)]
77pub enum CheckpointSummaryResponse {
78 Certified(CertifiedCheckpointSummary),
79 Pending(CheckpointSummary),
80}
81
82impl CheckpointSummaryResponse {
83 pub fn content_digest(&self) -> CheckpointContentsDigest {
84 match self {
85 Self::Certified(s) => s.content_digest,
86 Self::Pending(s) => s.content_digest,
87 }
88 }
89}
90
91#[allow(clippy::large_enum_variant)]
92#[derive(Clone, Debug, Serialize, Deserialize)]
93pub struct CheckpointResponse {
94 pub checkpoint: Option<CertifiedCheckpointSummary>,
95 pub contents: Option<CheckpointContents>,
96}
97
98#[allow(clippy::large_enum_variant)]
99#[derive(Clone, Debug, Serialize, Deserialize)]
100pub struct CheckpointResponseV2 {
101 pub checkpoint: Option<CheckpointSummaryResponse>,
102 pub contents: Option<CheckpointContents>,
103}
104
105#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
109pub struct ECMHLiveObjectSetDigest {
110 #[schemars(with = "[u8; 32]")]
111 pub digest: Digest,
112}
113
114impl From<fastcrypto::hash::Digest<32>> for ECMHLiveObjectSetDigest {
115 fn from(digest: fastcrypto::hash::Digest<32>) -> Self {
116 Self {
117 digest: Digest::new(digest.digest),
118 }
119 }
120}
121
122impl Default for ECMHLiveObjectSetDigest {
123 fn default() -> Self {
124 GlobalStateHash::default().digest().into()
125 }
126}
127
128#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
132pub enum CheckpointArtifact {
133 ObjectStates(BTreeMap<ObjectID, (SequenceNumber, ObjectDigest)>),
136 }
138
139impl CheckpointArtifact {
140 pub fn digest(&self) -> SuiResult<Digest> {
141 match self {
142 Self::ObjectStates(object_states) => {
143 let tree = MerkleTree::<Blake2b256>::build_from_unserialized(
144 object_states
145 .iter()
146 .map(|(id, (seq, digest))| (id, seq, digest)),
147 )
148 .map_err(|e| SuiErrorKind::GenericAuthorityError {
149 error: format!("Failed to build Merkle tree: {}", e),
150 })?;
151 let root = tree.root().bytes();
152 Ok(Digest::new(root))
153 }
154 }
155 }
156
157 pub fn artifact_type(&self) -> &'static str {
158 match self {
159 Self::ObjectStates(_) => "ObjectStates",
160 }
162 }
163}
164
165#[derive(Debug)]
166pub struct CheckpointArtifacts {
167 artifacts: BTreeSet<CheckpointArtifact>,
169}
170
171impl CheckpointArtifacts {
172 pub fn new() -> Self {
173 Self {
174 artifacts: BTreeSet::new(),
175 }
176 }
177
178 pub fn add_artifact(&mut self, artifact: CheckpointArtifact) -> SuiResult<()> {
179 if self
180 .artifacts
181 .iter()
182 .any(|existing| existing.artifact_type() == artifact.artifact_type())
183 {
184 return Err(SuiErrorKind::GenericAuthorityError {
185 error: format!("Artifact {} already exists", artifact.artifact_type()),
186 }
187 .into());
188 }
189 self.artifacts.insert(artifact);
190 Ok(())
191 }
192
193 pub fn from_object_states(
194 object_states: BTreeMap<ObjectID, (SequenceNumber, ObjectDigest)>,
195 ) -> Self {
196 CheckpointArtifacts {
197 artifacts: BTreeSet::from([CheckpointArtifact::ObjectStates(object_states)]),
198 }
199 }
200
201 pub fn object_states(&self) -> SuiResult<&BTreeMap<ObjectID, (SequenceNumber, ObjectDigest)>> {
203 self.artifacts
204 .iter()
205 .find(|artifact| matches!(artifact, CheckpointArtifact::ObjectStates(_)))
206 .map(|artifact| match artifact {
207 CheckpointArtifact::ObjectStates(states) => states,
208 })
209 .ok_or(
210 SuiErrorKind::GenericAuthorityError {
211 error: "Object states not found in checkpoint artifacts".to_string(),
212 }
213 .into(),
214 )
215 }
216
217 pub fn digest(&self) -> SuiResult<CheckpointArtifactsDigest> {
218 let digests = self
220 .artifacts
221 .iter()
222 .map(|a| a.digest())
223 .collect::<Result<Vec<_>, _>>()?;
224
225 CheckpointArtifactsDigest::from_artifact_digests(digests)
226 }
227}
228
229impl Default for CheckpointArtifacts {
230 fn default() -> Self {
231 Self::new()
232 }
233}
234
235impl From<&[&TransactionEffects]> for CheckpointArtifacts {
236 fn from(effects: &[&TransactionEffects]) -> Self {
237 let mut latest_object_states = BTreeMap::new();
238 for e in effects {
239 for (id, seq, digest) in e.written() {
240 if let Some((old_seq, _)) = latest_object_states.insert(id, (seq, digest)) {
241 assert!(
242 old_seq < seq,
243 "Object states should be monotonically increasing"
244 );
245 }
246 }
247 }
248
249 CheckpointArtifacts::from_object_states(latest_object_states)
250 }
251}
252
253impl From<&[TransactionEffects]> for CheckpointArtifacts {
254 fn from(effects: &[TransactionEffects]) -> Self {
255 let effect_refs: Vec<&TransactionEffects> = effects.iter().collect();
256 Self::from(effect_refs.as_slice())
257 }
258}
259
260impl From<&CheckpointData> for CheckpointArtifacts {
261 fn from(checkpoint_data: &CheckpointData) -> Self {
262 let effects = checkpoint_data
263 .transactions
264 .iter()
265 .map(|tx| &tx.effects)
266 .collect::<Vec<_>>();
267
268 Self::from(effects.as_slice())
269 }
270}
271
272impl From<&Checkpoint> for CheckpointArtifacts {
273 fn from(checkpoint: &Checkpoint) -> Self {
274 let effects = checkpoint
275 .transactions
276 .iter()
277 .map(|tx| &tx.effects)
278 .collect::<Vec<_>>();
279
280 Self::from(effects.as_slice())
281 }
282}
283
284#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
285pub enum CheckpointCommitment {
286 ECMHLiveObjectSetDigest(ECMHLiveObjectSetDigest),
287 CheckpointArtifactsDigest(CheckpointArtifactsDigest),
288}
289
290impl From<ECMHLiveObjectSetDigest> for CheckpointCommitment {
291 fn from(d: ECMHLiveObjectSetDigest) -> Self {
292 Self::ECMHLiveObjectSetDigest(d)
293 }
294}
295
296impl From<CheckpointArtifactsDigest> for CheckpointCommitment {
297 fn from(d: CheckpointArtifactsDigest) -> Self {
298 Self::CheckpointArtifactsDigest(d)
299 }
300}
301
302#[serde_as]
303#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
304#[serde(rename_all = "camelCase")]
305pub struct EndOfEpochData {
306 #[schemars(with = "Vec<(AuthorityName, BigInt<u64>)>")]
314 #[serde_as(as = "Vec<(_, Readable<BigInt<u64>, _>)>")]
315 pub next_epoch_committee: Vec<(AuthorityName, StakeUnit)>,
316
317 #[schemars(with = "AsProtocolVersion")]
320 #[serde_as(as = "Readable<AsProtocolVersion, _>")]
321 pub next_epoch_protocol_version: ProtocolVersion,
322
323 pub epoch_commitments: Vec<CheckpointCommitment>,
325}
326
327#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
328pub struct CheckpointSummary {
329 pub epoch: EpochId,
330 pub sequence_number: CheckpointSequenceNumber,
331 pub network_total_transactions: u64,
334 pub content_digest: CheckpointContentsDigest,
335 pub previous_digest: Option<CheckpointDigest>,
336 pub epoch_rolling_gas_cost_summary: GasCostSummary,
339
340 pub timestamp_ms: CheckpointTimestamp,
344
345 pub checkpoint_commitments: Vec<CheckpointCommitment>,
348
349 pub end_of_epoch_data: Option<EndOfEpochData>,
351
352 pub version_specific_data: Vec<u8>,
359}
360
361impl Message for CheckpointSummary {
362 type DigestType = CheckpointDigest;
363 const SCOPE: IntentScope = IntentScope::CheckpointSummary;
364
365 fn digest(&self) -> Self::DigestType {
366 CheckpointDigest::new(default_hash(self))
367 }
368}
369
370impl CheckpointSummary {
371 pub fn new(
372 protocol_config: &ProtocolConfig,
373 epoch: EpochId,
374 sequence_number: CheckpointSequenceNumber,
375 network_total_transactions: u64,
376 transactions: &CheckpointContents,
377 previous_digest: Option<CheckpointDigest>,
378 epoch_rolling_gas_cost_summary: GasCostSummary,
379 end_of_epoch_data: Option<EndOfEpochData>,
380 timestamp_ms: CheckpointTimestamp,
381 randomness_rounds: Vec<RandomnessRound>,
382 checkpoint_commitments: Vec<CheckpointCommitment>,
383 ) -> CheckpointSummary {
384 let content_digest = *transactions.digest();
385
386 let version_specific_data = match protocol_config
387 .checkpoint_summary_version_specific_data_as_option()
388 {
389 None | Some(0) => Vec::new(),
390 Some(1) => bcs::to_bytes(&CheckpointVersionSpecificData::V1(
391 CheckpointVersionSpecificDataV1 { randomness_rounds },
392 ))
393 .expect("version specific data should serialize"),
394 _ => unimplemented!("unrecognized version_specific_data version for CheckpointSummary"),
395 };
396
397 Self {
398 epoch,
399 sequence_number,
400 network_total_transactions,
401 content_digest,
402 previous_digest,
403 epoch_rolling_gas_cost_summary,
404 end_of_epoch_data,
405 timestamp_ms,
406 version_specific_data,
407 checkpoint_commitments,
408 }
409 }
410
411 pub fn verify_epoch(&self, epoch: EpochId) -> SuiResult {
412 fp_ensure!(
413 self.epoch == epoch,
414 SuiErrorKind::WrongEpoch {
415 expected_epoch: epoch,
416 actual_epoch: self.epoch,
417 }
418 .into()
419 );
420 Ok(())
421 }
422
423 pub fn sequence_number(&self) -> &CheckpointSequenceNumber {
424 &self.sequence_number
425 }
426
427 pub fn timestamp(&self) -> SystemTime {
428 UNIX_EPOCH + Duration::from_millis(self.timestamp_ms)
429 }
430
431 pub fn next_epoch_committee(&self) -> Option<&[(AuthorityName, StakeUnit)]> {
432 self.end_of_epoch_data
433 .as_ref()
434 .map(|e| e.next_epoch_committee.as_slice())
435 }
436
437 pub fn report_checkpoint_age(&self, metrics: &Histogram, metrics_deprecated: &MystenHistogram) {
438 SystemTime::now()
439 .duration_since(self.timestamp())
440 .map(|latency| {
441 metrics.observe(latency.as_secs_f64());
442 metrics_deprecated.report(latency.as_millis() as u64);
443 })
444 .tap_err(|err| {
445 warn!(
446 checkpoint_seq = self.sequence_number,
447 "unable to compute checkpoint age: {}", err
448 )
449 })
450 .ok();
451 }
452
453 pub fn is_last_checkpoint_of_epoch(&self) -> bool {
454 self.end_of_epoch_data.is_some()
455 }
456
457 pub fn version_specific_data(
458 &self,
459 config: &ProtocolConfig,
460 ) -> Result<Option<CheckpointVersionSpecificData>> {
461 match config.checkpoint_summary_version_specific_data_as_option() {
462 None | Some(0) => Ok(None),
463 Some(1) => Ok(Some(bcs::from_bytes(&self.version_specific_data)?)),
464 _ => unimplemented!("unrecognized version_specific_data version in CheckpointSummary"),
465 }
466 }
467
468 pub fn checkpoint_artifacts_digest(&self) -> SuiResult<&CheckpointArtifactsDigest> {
469 self.checkpoint_commitments
470 .iter()
471 .find_map(|c| match c {
472 CheckpointCommitment::CheckpointArtifactsDigest(digest) => Some(digest),
473 _ => None,
474 })
475 .ok_or(
476 SuiErrorKind::GenericAuthorityError {
477 error: "Checkpoint artifacts digest not found in checkpoint commitments"
478 .to_string(),
479 }
480 .into(),
481 )
482 }
483}
484
485impl Display for CheckpointSummary {
486 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
487 write!(
488 f,
489 "CheckpointSummary {{ epoch: {:?}, seq: {:?}, content_digest: {},
490 epoch_rolling_gas_cost_summary: {:?}}}",
491 self.epoch,
492 self.sequence_number,
493 self.content_digest,
494 self.epoch_rolling_gas_cost_summary,
495 )
496 }
497}
498
499pub type CheckpointSummaryEnvelope<S> = Envelope<CheckpointSummary, S>;
508pub type CertifiedCheckpointSummary = CheckpointSummaryEnvelope<AuthorityStrongQuorumSignInfo>;
509pub type SignedCheckpointSummary = CheckpointSummaryEnvelope<AuthoritySignInfo>;
510
511pub type VerifiedCheckpoint = VerifiedEnvelope<CheckpointSummary, AuthorityStrongQuorumSignInfo>;
512pub type TrustedCheckpoint = TrustedEnvelope<CheckpointSummary, AuthorityStrongQuorumSignInfo>;
513
514impl CertifiedCheckpointSummary {
515 pub fn verify_authority_signatures(&self, committee: &Committee) -> SuiResult {
516 self.data().verify_epoch(self.auth_sig().epoch)?;
517 self.auth_sig().verify_secure(
518 self.data(),
519 Intent::sui_app(IntentScope::CheckpointSummary),
520 committee,
521 )
522 }
523
524 pub fn try_into_verified(self, committee: &Committee) -> SuiResult<VerifiedCheckpoint> {
525 self.verify_authority_signatures(committee)?;
526 Ok(VerifiedCheckpoint::new_from_verified(self))
527 }
528
529 pub fn verify_with_contents(
530 &self,
531 committee: &Committee,
532 contents: Option<&CheckpointContents>,
533 ) -> SuiResult {
534 self.verify_authority_signatures(committee)?;
535
536 if let Some(contents) = contents {
537 let content_digest = *contents.digest();
538 fp_ensure!(
539 content_digest == self.data().content_digest,
540 SuiErrorKind::GenericAuthorityError{error:format!("Checkpoint contents digest mismatch: summary={:?}, received content digest {:?}, received {} transactions", self.data(), content_digest, contents.size())}.into()
541 );
542 }
543
544 Ok(())
545 }
546
547 pub fn into_summary_and_sequence(self) -> (CheckpointSequenceNumber, CheckpointSummary) {
548 let summary = self.into_data();
549 (summary.sequence_number, summary)
550 }
551
552 pub fn get_validator_signature(self) -> AggregateAuthoritySignature {
553 self.auth_sig().signature.clone()
554 }
555}
556
557impl SignedCheckpointSummary {
558 pub fn verify_authority_signatures(&self, committee: &Committee) -> SuiResult {
559 self.data().verify_epoch(self.auth_sig().epoch)?;
560 self.auth_sig().verify_secure(
561 self.data(),
562 Intent::sui_app(IntentScope::CheckpointSummary),
563 committee,
564 )
565 }
566
567 pub fn try_into_verified(
568 self,
569 committee: &Committee,
570 ) -> SuiResult<VerifiedEnvelope<CheckpointSummary, AuthoritySignInfo>> {
571 self.verify_authority_signatures(committee)?;
572 Ok(VerifiedEnvelope::<CheckpointSummary, AuthoritySignInfo>::new_from_verified(self))
573 }
574}
575
576impl VerifiedCheckpoint {
577 pub fn into_summary_and_sequence(self) -> (CheckpointSequenceNumber, CheckpointSummary) {
578 self.into_inner().into_summary_and_sequence()
579 }
580}
581
582#[derive(Clone, Debug, Serialize, Deserialize)]
584pub struct CheckpointSignatureMessage {
585 pub summary: SignedCheckpointSummary,
586}
587
588impl CheckpointSignatureMessage {
589 pub fn verify(&self, committee: &Committee) -> SuiResult {
590 self.summary.verify_authority_signatures(committee)
591 }
592}
593
594#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
595pub enum CheckpointContents {
596 V1(CheckpointContentsV1),
597 V2(CheckpointContentsV2),
598}
599
600#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
605pub struct CheckpointContentsV1 {
606 #[serde(skip)]
607 digest: OnceCell<CheckpointContentsDigest>,
608
609 transactions: Vec<ExecutionDigests>,
610 user_signatures: Vec<Vec<GenericSignature>>,
614}
615
616#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
617pub struct CheckpointContentsV2 {
618 #[serde(skip)]
619 digest: OnceCell<CheckpointContentsDigest>,
620
621 transactions: Vec<CheckpointTransactionContents>,
622}
623
624#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
625pub struct CheckpointTransactionContents {
626 pub digest: ExecutionDigests,
627
628 pub user_signatures: Vec<(GenericSignature, Option<SequenceNumber>)>,
632}
633
634impl CheckpointContents {
635 pub fn new_with_digests_and_signatures<T>(
636 contents: T,
637 user_signatures: Vec<Vec<GenericSignature>>,
638 ) -> Self
639 where
640 T: IntoIterator<Item = ExecutionDigests>,
641 {
642 let transactions: Vec<_> = contents.into_iter().collect();
643 assert_eq!(transactions.len(), user_signatures.len());
644 Self::V1(CheckpointContentsV1 {
645 digest: Default::default(),
646 transactions,
647 user_signatures,
648 })
649 }
650
651 pub fn new_v2(
652 effects: &[TransactionEffects],
653 signatures: Vec<Vec<(GenericSignature, Option<SequenceNumber>)>>,
654 ) -> Self {
655 assert_eq!(effects.len(), signatures.len());
656 Self::V2(CheckpointContentsV2 {
657 digest: Default::default(),
658 transactions: effects
659 .iter()
660 .zip_debug_eq(signatures)
661 .map(|(e, s)| CheckpointTransactionContents {
662 digest: e.execution_digests(),
663 user_signatures: s,
664 })
665 .collect(),
666 })
667 }
668
669 pub fn new_with_causally_ordered_execution_data<'a, T>(contents: T) -> Self
670 where
671 T: IntoIterator<Item = &'a VerifiedExecutionData>,
672 {
673 let (transactions, user_signatures): (Vec<_>, Vec<_>) = contents
674 .into_iter()
675 .map(|data| {
676 (
677 data.digests(),
678 data.transaction.inner().data().tx_signatures().to_owned(),
679 )
680 })
681 .unzip();
682 assert_eq!(transactions.len(), user_signatures.len());
683 Self::V1(CheckpointContentsV1 {
684 digest: Default::default(),
685 transactions,
686 user_signatures,
687 })
688 }
689
690 pub fn new_with_digests_only_for_tests<T>(contents: T) -> Self
691 where
692 T: IntoIterator<Item = ExecutionDigests>,
693 {
694 let transactions: Vec<_> = contents.into_iter().collect();
695 let user_signatures = transactions.iter().map(|_| vec![]).collect();
696 Self::V1(CheckpointContentsV1 {
697 digest: Default::default(),
698 transactions,
699 user_signatures,
700 })
701 }
702
703 fn into_v1(self) -> CheckpointContentsV1 {
704 let digest = *self.digest();
705 match self {
706 Self::V1(c) => c,
707 Self::V2(c) => CheckpointContentsV1 {
708 digest: OnceCell::with_value(digest),
710 transactions: c.transactions.iter().map(|t| t.digest).collect(),
711 user_signatures: c
712 .transactions
713 .iter()
714 .map(|t| {
715 t.user_signatures
716 .iter()
717 .map(|(s, _)| s.to_owned())
718 .collect()
719 })
720 .collect(),
721 },
722 }
723 }
724
725 pub fn iter(&self) -> impl DoubleEndedIterator<Item = &ExecutionDigests> + '_ {
726 match self {
727 Self::V1(v) => itertools::Either::Left(v.transactions.iter()),
728 Self::V2(v) => itertools::Either::Right(v.transactions.iter().map(|t| &t.digest)),
729 }
730 }
731
732 pub fn into_iter_with_signatures(
733 self,
734 ) -> impl Iterator<Item = (ExecutionDigests, Vec<GenericSignature>)> {
735 let CheckpointContentsV1 {
736 transactions,
737 user_signatures,
738 ..
739 } = self.into_v1();
740
741 transactions.into_iter().zip_debug_eq(user_signatures)
742 }
743
744 pub fn enumerate_transactions(
749 &self,
750 ckpt: &CheckpointSummary,
751 ) -> impl Iterator<Item = (u64, &ExecutionDigests)> {
752 let start = ckpt.network_total_transactions - self.size() as u64;
753
754 self.iter()
755 .enumerate()
756 .map(move |(i, digests)| (i as u64 + start, digests))
757 }
758
759 pub fn into_inner(self) -> Vec<ExecutionDigests> {
760 self.into_v1().transactions
761 }
762
763 pub fn inner(&self) -> CheckpointContentsView<'_> {
764 match self {
765 Self::V1(c) => CheckpointContentsView::V1 {
766 transactions: &c.transactions,
767 user_signatures: &c.user_signatures,
768 },
769 Self::V2(c) => CheckpointContentsView::V2(&c.transactions),
770 }
771 }
772
773 pub fn size(&self) -> usize {
774 match self {
775 Self::V1(c) => c.transactions.len(),
776 Self::V2(c) => c.transactions.len(),
777 }
778 }
779
780 pub fn digest(&self) -> &CheckpointContentsDigest {
781 match self {
782 Self::V1(c) => c
783 .digest
784 .get_or_init(|| CheckpointContentsDigest::new(default_hash(self))),
785 Self::V2(c) => c
786 .digest
787 .get_or_init(|| CheckpointContentsDigest::new(default_hash(self))),
788 }
789 }
790}
791
792pub enum CheckpointContentsView<'a> {
794 V1 {
795 transactions: &'a [ExecutionDigests],
796 user_signatures: &'a [Vec<GenericSignature>],
797 },
798 V2(&'a [CheckpointTransactionContents]),
799}
800
801impl CheckpointContentsView<'_> {
802 pub fn len(&self) -> usize {
803 match self {
804 Self::V1 { transactions, .. } => transactions.len(),
805 Self::V2(v) => v.len(),
806 }
807 }
808
809 pub fn is_empty(&self) -> bool {
810 self.len() == 0
811 }
812
813 pub fn get_digests(&self, index: usize) -> Option<&ExecutionDigests> {
814 match self {
815 Self::V1 { transactions, .. } => transactions.get(index),
816 Self::V2(v) => v.get(index).map(|t| &t.digest),
817 }
818 }
819
820 pub fn first_digests(&self) -> Option<&ExecutionDigests> {
821 self.get_digests(0)
822 }
823
824 pub fn digests_iter(
825 &self,
826 ) -> impl DoubleEndedIterator<Item = &ExecutionDigests> + ExactSizeIterator {
827 match self {
828 Self::V1 { transactions, .. } => itertools::Either::Left(transactions.iter()),
829 Self::V2(v) => itertools::Either::Right(v.iter().map(|t| &t.digest)),
830 }
831 }
832
833 pub fn user_signatures(
836 &self,
837 index: usize,
838 ) -> Option<Vec<(GenericSignature, Option<SequenceNumber>)>> {
839 match self {
840 Self::V1 {
841 user_signatures, ..
842 } => user_signatures
843 .get(index)
844 .map(|sigs| sigs.iter().map(|sig| (sig.clone(), None)).collect()),
845 Self::V2(v) => v.get(index).map(|t| t.user_signatures.clone()),
846 }
847 }
848
849 pub fn iter(
850 &self,
851 ) -> impl Iterator<
852 Item = (
853 &ExecutionDigests,
854 impl Iterator<Item = (&GenericSignature, Option<SequenceNumber>)>,
855 ),
856 > {
857 match self {
858 Self::V1 {
859 transactions,
860 user_signatures,
861 } => itertools::Either::Left(
862 transactions
863 .iter()
864 .zip_debug_eq(user_signatures.iter())
865 .map(|(digests, signatures)| {
866 let signatures_iter =
867 itertools::Either::Left(signatures.iter().map(|s| (s, None)));
868 (digests, signatures_iter)
869 }),
870 ),
871 Self::V2(v) => itertools::Either::Right(v.iter().map(|t| {
872 (
873 &t.digest,
874 itertools::Either::Right(t.user_signatures.iter().map(|(s, v)| (s, *v))),
875 )
876 })),
877 }
878 }
879}
880
881impl std::ops::Index<usize> for CheckpointContentsView<'_> {
882 type Output = ExecutionDigests;
883
884 fn index(&self, index: usize) -> &Self::Output {
885 match self {
886 Self::V1 { transactions, .. } => &transactions[index],
887 Self::V2(v) => &v[index].digest,
888 }
889 }
890}
891
892#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
896pub enum VersionedFullCheckpointContents {
897 V1(FullCheckpointContents),
898 V2(FullCheckpointContentsV2),
899}
900
901impl VersionedFullCheckpointContents {
902 pub fn from_contents_and_execution_data(
903 contents: CheckpointContents,
904 execution_data: impl Iterator<Item = ExecutionData>,
905 ) -> Self {
906 let transactions: Vec<_> = execution_data.collect();
907 match contents {
908 CheckpointContents::V1(v1) => Self::V1(FullCheckpointContents {
909 transactions,
910 user_signatures: v1.user_signatures,
911 }),
912 CheckpointContents::V2(v2) => Self::V2(FullCheckpointContentsV2 {
913 transactions,
914 user_signatures: v2
915 .transactions
916 .into_iter()
917 .map(|tx| tx.user_signatures)
918 .collect(),
919 }),
920 }
921 }
922
923 pub fn verify_digests(&self, digest: CheckpointContentsDigest) -> Result<()> {
926 let self_digest = *self.checkpoint_contents().digest();
927 fp_ensure!(
928 digest == self_digest,
929 anyhow::anyhow!(
930 "checkpoint contents digest {self_digest} does not match expected digest {digest}"
931 )
932 );
933 for tx in self.iter() {
934 let transaction_digest = tx.transaction.digest();
935 fp_ensure!(
936 tx.effects.transaction_digest() == transaction_digest,
937 anyhow::anyhow!(
938 "transaction digest {transaction_digest} does not match expected digest {}",
939 tx.effects.transaction_digest()
940 )
941 );
942 }
943 Ok(())
944 }
945
946 pub fn into_v1(self) -> FullCheckpointContents {
947 match self {
948 Self::V1(c) => c,
949 Self::V2(c) => FullCheckpointContents {
950 transactions: c.transactions,
951 user_signatures: c
952 .user_signatures
953 .into_iter()
954 .map(|sigs| sigs.into_iter().map(|(sig, _)| sig).collect())
955 .collect(),
956 },
957 }
958 }
959
960 pub fn into_checkpoint_contents(self) -> CheckpointContents {
961 match self {
962 Self::V1(c) => c.into_checkpoint_contents(),
963 Self::V2(c) => c.into_checkpoint_contents(),
964 }
965 }
966
967 pub fn checkpoint_contents(&self) -> CheckpointContents {
968 match self {
969 Self::V1(c) => c.checkpoint_contents(),
970 Self::V2(c) => c.checkpoint_contents(),
971 }
972 }
973
974 pub fn iter(&self) -> Iter<'_, ExecutionData> {
975 match self {
976 Self::V1(c) => c.iter(),
977 Self::V2(c) => c.transactions.iter(),
978 }
979 }
980
981 pub fn size(&self) -> usize {
982 match self {
983 Self::V1(c) => c.transactions.len(),
984 Self::V2(c) => c.transactions.len(),
985 }
986 }
987}
988
989#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
990pub struct FullCheckpointContentsV2 {
991 transactions: Vec<ExecutionData>,
992 user_signatures: Vec<Vec<(GenericSignature, Option<SequenceNumber>)>>,
993}
994
995impl FullCheckpointContentsV2 {
996 pub fn checkpoint_contents(&self) -> CheckpointContents {
997 CheckpointContents::V2(CheckpointContentsV2 {
998 digest: Default::default(),
999 transactions: self
1000 .transactions
1001 .iter()
1002 .zip_debug_eq(&self.user_signatures)
1003 .map(|(tx, sigs)| CheckpointTransactionContents {
1004 digest: tx.digests(),
1005 user_signatures: sigs.clone(),
1006 })
1007 .collect(),
1008 })
1009 }
1010
1011 pub fn into_checkpoint_contents(self) -> CheckpointContents {
1012 CheckpointContents::V2(CheckpointContentsV2 {
1013 digest: Default::default(),
1014 transactions: self
1015 .transactions
1016 .into_iter()
1017 .zip_debug_eq(self.user_signatures)
1018 .map(|(tx, sigs)| CheckpointTransactionContents {
1019 digest: tx.digests(),
1020 user_signatures: sigs,
1021 })
1022 .collect(),
1023 })
1024 }
1025}
1026
1027#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1029pub struct FullCheckpointContents {
1030 transactions: Vec<ExecutionData>,
1031 user_signatures: Vec<Vec<GenericSignature>>,
1035}
1036
1037impl FullCheckpointContents {
1038 pub fn new_with_causally_ordered_transactions<T>(contents: T) -> Self
1039 where
1040 T: IntoIterator<Item = ExecutionData>,
1041 {
1042 let (transactions, user_signatures): (Vec<_>, Vec<_>) = contents
1043 .into_iter()
1044 .map(|data| {
1045 let sig = data.transaction.data().tx_signatures().to_owned();
1046 (data, sig)
1047 })
1048 .unzip();
1049 assert_eq!(transactions.len(), user_signatures.len());
1050 Self {
1051 transactions,
1052 user_signatures,
1053 }
1054 }
1055
1056 pub fn iter(&self) -> Iter<'_, ExecutionData> {
1057 self.transactions.iter()
1058 }
1059
1060 pub fn checkpoint_contents(&self) -> CheckpointContents {
1061 CheckpointContents::V1(CheckpointContentsV1 {
1062 digest: Default::default(),
1063 transactions: self.transactions.iter().map(|tx| tx.digests()).collect(),
1064 user_signatures: self.user_signatures.clone(),
1065 })
1066 }
1067
1068 pub fn into_checkpoint_contents(self) -> CheckpointContents {
1069 CheckpointContents::V1(CheckpointContentsV1 {
1070 digest: Default::default(),
1071 transactions: self
1072 .transactions
1073 .into_iter()
1074 .map(|tx| tx.digests())
1075 .collect(),
1076 user_signatures: self.user_signatures,
1077 })
1078 }
1079
1080 pub fn random_for_testing() -> Self {
1081 Self::random_with_transactions_for_testing(1)
1082 }
1083
1084 pub fn random_with_transactions_for_testing(num_transactions: usize) -> Self {
1085 let exe_data = (0..num_transactions).map(|_| {
1086 let (a, key): (_, AccountKeyPair) = get_key_pair();
1087 let transaction = Transaction::from_data_and_signer(
1088 TransactionData::new_transfer(
1089 a,
1090 FullObjectRef::from_fastpath_ref(random_object_ref()),
1091 a,
1092 random_object_ref(),
1093 100000000000,
1094 100,
1095 ),
1096 vec![&key],
1097 );
1098 let effects = TestEffectsBuilder::new(transaction.data()).build();
1099 ExecutionData {
1100 transaction,
1101 effects,
1102 }
1103 });
1104 FullCheckpointContents::new_with_causally_ordered_transactions(exe_data)
1105 }
1106}
1107
1108impl IntoIterator for VersionedFullCheckpointContents {
1109 type Item = ExecutionData;
1110 type IntoIter = std::vec::IntoIter<Self::Item>;
1111
1112 fn into_iter(self) -> Self::IntoIter {
1113 match self {
1114 Self::V1(c) => c.transactions.into_iter(),
1115 Self::V2(c) => c.transactions.into_iter(),
1116 }
1117 }
1118}
1119
1120#[derive(Clone, Debug, PartialEq, Eq)]
1121pub enum VerifiedUserSignatures {
1122 V1(Vec<Vec<GenericSignature>>),
1123 V2(Vec<Vec<(GenericSignature, Option<SequenceNumber>)>>),
1124}
1125
1126#[derive(Clone, Debug, PartialEq, Eq)]
1127pub struct VerifiedCheckpointContents {
1128 transactions: Vec<VerifiedExecutionData>,
1129 user_signatures: VerifiedUserSignatures,
1133}
1134
1135impl VerifiedCheckpointContents {
1136 pub fn new_unchecked(contents: VersionedFullCheckpointContents) -> Self {
1137 match contents {
1138 VersionedFullCheckpointContents::V1(c) => Self {
1139 transactions: c
1140 .transactions
1141 .into_iter()
1142 .map(VerifiedExecutionData::new_unchecked)
1143 .collect(),
1144 user_signatures: VerifiedUserSignatures::V1(c.user_signatures),
1145 },
1146 VersionedFullCheckpointContents::V2(c) => Self {
1147 transactions: c
1148 .transactions
1149 .into_iter()
1150 .map(VerifiedExecutionData::new_unchecked)
1151 .collect(),
1152 user_signatures: VerifiedUserSignatures::V2(c.user_signatures),
1153 },
1154 }
1155 }
1156
1157 pub fn iter(&self) -> Iter<'_, VerifiedExecutionData> {
1158 self.transactions.iter()
1159 }
1160
1161 pub fn transactions(&self) -> &[VerifiedExecutionData] {
1162 &self.transactions
1163 }
1164
1165 pub fn into_inner(self) -> VersionedFullCheckpointContents {
1166 let transactions: Vec<_> = self
1167 .transactions
1168 .into_iter()
1169 .map(|tx| tx.into_inner())
1170 .collect();
1171
1172 match self.user_signatures {
1173 VerifiedUserSignatures::V1(user_signatures) => {
1174 VersionedFullCheckpointContents::V1(FullCheckpointContents {
1175 transactions,
1176 user_signatures,
1177 })
1178 }
1179 VerifiedUserSignatures::V2(user_signatures) => {
1180 VersionedFullCheckpointContents::V2(FullCheckpointContentsV2 {
1181 transactions,
1182 user_signatures,
1183 })
1184 }
1185 }
1186 }
1187
1188 pub fn into_checkpoint_contents(self) -> CheckpointContents {
1189 self.into_inner().into_checkpoint_contents()
1190 }
1191
1192 pub fn into_checkpoint_contents_digest(self) -> CheckpointContentsDigest {
1193 *self.into_inner().into_checkpoint_contents().digest()
1194 }
1195
1196 pub fn num_of_transactions(&self) -> usize {
1197 self.transactions.len()
1198 }
1199}
1200
1201#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1203pub enum CheckpointVersionSpecificData {
1204 V1(CheckpointVersionSpecificDataV1),
1205}
1206
1207impl CheckpointVersionSpecificData {
1208 pub fn as_v1(&self) -> &CheckpointVersionSpecificDataV1 {
1209 match self {
1210 Self::V1(v) => v,
1211 }
1212 }
1213
1214 pub fn into_v1(self) -> CheckpointVersionSpecificDataV1 {
1215 match self {
1216 Self::V1(v) => v,
1217 }
1218 }
1219
1220 pub fn empty_for_tests() -> CheckpointVersionSpecificData {
1221 CheckpointVersionSpecificData::V1(CheckpointVersionSpecificDataV1 {
1222 randomness_rounds: Vec::new(),
1223 })
1224 }
1225}
1226
1227#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
1228pub struct CheckpointVersionSpecificDataV1 {
1229 pub randomness_rounds: Vec<RandomnessRound>,
1231}
1232
1233#[cfg(test)]
1234mod tests {
1235 use crate::digests::{ConsensusCommitDigest, TransactionDigest, TransactionEffectsDigest};
1236 use crate::messages_consensus::ConsensusDeterminedVersionAssignments;
1237 use crate::transaction::VerifiedTransaction;
1238 use fastcrypto::traits::KeyPair;
1239 use rand::SeedableRng;
1240 use rand::prelude::StdRng;
1241
1242 use super::*;
1243 use crate::utils::make_committee_key;
1244
1245 const RNG_SEED: [u8; 32] = [
1247 21, 23, 199, 200, 234, 250, 252, 178, 94, 15, 202, 178, 62, 186, 88, 137, 233, 192, 130,
1248 157, 179, 179, 65, 9, 31, 249, 221, 123, 225, 112, 199, 247,
1249 ];
1250
1251 #[test]
1252 fn test_signed_checkpoint() {
1253 let mut rng = StdRng::from_seed(RNG_SEED);
1254 let (keys, committee) = make_committee_key(&mut rng);
1255 let (_, committee2) = make_committee_key(&mut rng);
1256
1257 let set = CheckpointContents::new_with_digests_only_for_tests([ExecutionDigests::random()]);
1258
1259 let signed_checkpoints: Vec<_> = keys
1262 .iter()
1263 .map(|k| {
1264 let name = k.public().into();
1265
1266 SignedCheckpointSummary::new(
1267 committee.epoch,
1268 CheckpointSummary::new(
1269 &ProtocolConfig::get_for_max_version_UNSAFE(),
1270 committee.epoch,
1271 1,
1272 0,
1273 &set,
1274 None,
1275 GasCostSummary::default(),
1276 None,
1277 0,
1278 Vec::new(),
1279 Vec::new(),
1280 ),
1281 k,
1282 name,
1283 )
1284 })
1285 .collect();
1286
1287 signed_checkpoints.iter().for_each(|c| {
1288 c.verify_authority_signatures(&committee)
1289 .expect("signature ok")
1290 });
1291
1292 signed_checkpoints
1294 .iter()
1295 .for_each(|c| assert!(c.verify_authority_signatures(&committee2).is_err()));
1296 }
1297
1298 #[test]
1299 fn test_certified_checkpoint() {
1300 let mut rng = StdRng::from_seed(RNG_SEED);
1301 let (keys, committee) = make_committee_key(&mut rng);
1302
1303 let set = CheckpointContents::new_with_digests_only_for_tests([ExecutionDigests::random()]);
1304
1305 let summary = CheckpointSummary::new(
1306 &ProtocolConfig::get_for_max_version_UNSAFE(),
1307 committee.epoch,
1308 1,
1309 0,
1310 &set,
1311 None,
1312 GasCostSummary::default(),
1313 None,
1314 0,
1315 Vec::new(),
1316 Vec::new(),
1317 );
1318
1319 let sign_infos: Vec<_> = keys
1320 .iter()
1321 .map(|k| {
1322 let name = k.public().into();
1323
1324 SignedCheckpointSummary::sign(committee.epoch, &summary, k, name)
1325 })
1326 .collect();
1327
1328 let checkpoint_cert =
1329 CertifiedCheckpointSummary::new(summary, sign_infos, &committee).expect("Cert is OK");
1330
1331 assert!(
1333 checkpoint_cert
1334 .verify_with_contents(&committee, Some(&set))
1335 .is_ok()
1336 );
1337
1338 let signed_checkpoints: Vec<_> = keys
1340 .iter()
1341 .map(|k| {
1342 let name = k.public().into();
1343 let set = CheckpointContents::new_with_digests_only_for_tests([
1344 ExecutionDigests::random(),
1345 ]);
1346
1347 SignedCheckpointSummary::new(
1348 committee.epoch,
1349 CheckpointSummary::new(
1350 &ProtocolConfig::get_for_max_version_UNSAFE(),
1351 committee.epoch,
1352 1,
1353 0,
1354 &set,
1355 None,
1356 GasCostSummary::default(),
1357 None,
1358 0,
1359 Vec::new(),
1360 Vec::new(),
1361 ),
1362 k,
1363 name,
1364 )
1365 })
1366 .collect();
1367
1368 let summary = signed_checkpoints[0].data().clone();
1369 let sign_infos = signed_checkpoints
1370 .into_iter()
1371 .map(|v| v.into_sig())
1372 .collect();
1373 assert!(
1374 CertifiedCheckpointSummary::new(summary, sign_infos, &committee)
1375 .unwrap()
1376 .verify_authority_signatures(&committee)
1377 .is_err()
1378 )
1379 }
1380
1381 fn generate_test_checkpoint_summary_from_digest(
1385 digest: TransactionDigest,
1386 ) -> CheckpointSummary {
1387 CheckpointSummary::new(
1388 &ProtocolConfig::get_for_max_version_UNSAFE(),
1389 1,
1390 2,
1391 10,
1392 &CheckpointContents::new_with_digests_only_for_tests([ExecutionDigests::new(
1393 digest,
1394 TransactionEffectsDigest::ZERO,
1395 )]),
1396 None,
1397 GasCostSummary::default(),
1398 None,
1399 100,
1400 Vec::new(),
1401 Vec::new(),
1402 )
1403 }
1404
1405 #[test]
1407 fn test_checkpoint_summary_with_different_consensus_digest() {
1408 {
1410 let t1 = VerifiedTransaction::new_consensus_commit_prologue_v3(
1411 1,
1412 2,
1413 100,
1414 ConsensusCommitDigest::default(),
1415 ConsensusDeterminedVersionAssignments::empty_for_testing(),
1416 );
1417 let t2 = VerifiedTransaction::new_consensus_commit_prologue_v3(
1418 1,
1419 2,
1420 100,
1421 ConsensusCommitDigest::default(),
1422 ConsensusDeterminedVersionAssignments::empty_for_testing(),
1423 );
1424 let c1 = generate_test_checkpoint_summary_from_digest(*t1.digest());
1425 let c2 = generate_test_checkpoint_summary_from_digest(*t2.digest());
1426 assert_eq!(c1.digest(), c2.digest());
1427 }
1428
1429 {
1431 let t1 = VerifiedTransaction::new_consensus_commit_prologue_v3(
1432 1,
1433 2,
1434 100,
1435 ConsensusCommitDigest::default(),
1436 ConsensusDeterminedVersionAssignments::empty_for_testing(),
1437 );
1438 let t2 = VerifiedTransaction::new_consensus_commit_prologue_v3(
1439 1,
1440 2,
1441 100,
1442 ConsensusCommitDigest::random(),
1443 ConsensusDeterminedVersionAssignments::empty_for_testing(),
1444 );
1445 let c1 = generate_test_checkpoint_summary_from_digest(*t1.digest());
1446 let c2 = generate_test_checkpoint_summary_from_digest(*t2.digest());
1447 assert_ne!(c1.digest(), c2.digest());
1448 }
1449 }
1450
1451 #[test]
1452 fn test_artifacts() {
1453 let mut artifacts = CheckpointArtifacts::new();
1454 let o = CheckpointArtifact::ObjectStates(BTreeMap::new());
1455 assert!(artifacts.add_artifact(o.clone()).is_ok());
1456 assert!(artifacts.add_artifact(o.clone()).is_err());
1457 }
1458}