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