1use std::{
6 fmt::{Debug, Display, Formatter},
7 slice::Iter,
8 time::{Duration, SystemTime, UNIX_EPOCH},
9};
10
11use anyhow::Result;
12use fastcrypto::hash::MultisetHash;
13use iota_protocol_config::ProtocolConfig;
14use iota_sdk_types::{
15 RandomnessRound,
16 crypto::{Intent, IntentScope},
17 gas::GasCostSummary,
18 validator::ValidatorCommitteeMember,
19};
20use once_cell::sync::OnceCell;
21#[cfg(not(target_arch = "wasm32"))]
22use prometheus_filtered::Histogram;
23use serde::{Deserialize, Serialize};
24#[cfg(not(target_arch = "wasm32"))]
25use tap::TapFallible;
26use tracing::instrument;
27#[cfg(not(target_arch = "wasm32"))]
28use tracing::warn;
29
30pub use crate::digests::{CheckpointContentsDigest, CheckpointDigest};
31use crate::{
32 base_types::{ExecutionData, ExecutionDigests, VerifiedExecutionData, random_object_ref},
33 committee::{Committee, EpochId},
34 crypto::{
35 AccountKeyPair, AggregateAuthoritySignature, AuthoritySignInfo, AuthoritySignInfoTrait,
36 AuthorityStrongQuorumSignInfo, default_hash, get_key_pair,
37 },
38 digests::Digest,
39 effects::{TestEffectsBuilder, TransactionEffectsAPI},
40 error::{IotaError, IotaResult},
41 global_state_hash::GlobalStateHash,
42 message_envelope::{Envelope, Message, TrustedEnvelope, VerifiedEnvelope},
43 signature::GenericSignature,
44 storage::ReadStore,
45 transaction::{Transaction, TransactionData, TransactionDataAPI},
46};
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>,
58 pub request_content: bool,
61 pub certified: bool,
63}
64
65#[expect(clippy::large_enum_variant)]
66#[derive(Clone, Debug, Serialize, Deserialize)]
67pub enum CheckpointSummaryResponse {
68 Certified(CertifiedCheckpointSummary),
69 Pending(CheckpointSummary),
70}
71
72impl CheckpointSummaryResponse {
73 pub fn content_digest(&self) -> CheckpointContentsDigest {
74 match self {
75 Self::Certified(s) => s.content_digest,
76 Self::Pending(s) => s.content_digest,
77 }
78 }
79}
80
81#[derive(Clone, Debug, Serialize, Deserialize)]
82pub struct CheckpointResponse {
83 pub checkpoint: Option<CheckpointSummaryResponse>,
84 pub contents: Option<CheckpointContents>,
85}
86
87pub use iota_sdk_types::checkpoint::CheckpointCommitment;
90
91#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
94pub struct ECMHLiveObjectSetDigest {
95 pub digest: Digest,
96}
97
98impl From<fastcrypto::hash::Digest<32>> for ECMHLiveObjectSetDigest {
99 fn from(digest: fastcrypto::hash::Digest<32>) -> Self {
100 Self {
101 digest: Digest::new(digest.digest),
102 }
103 }
104}
105
106impl Default for ECMHLiveObjectSetDigest {
107 fn default() -> Self {
108 GlobalStateHash::default().digest().into()
109 }
110}
111
112pub use iota_sdk_types::checkpoint::EndOfEpochData;
113
114#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
115pub struct CheckpointSummary {
116 pub epoch: EpochId,
117 pub sequence_number: CheckpointSequenceNumber,
118 pub network_total_transactions: u64,
121 pub content_digest: CheckpointContentsDigest,
122 pub previous_digest: Option<CheckpointDigest>,
123 pub epoch_rolling_gas_cost_summary: GasCostSummary,
126
127 pub timestamp_ms: CheckpointTimestamp,
132
133 pub checkpoint_commitments: Vec<CheckpointCommitment>,
136
137 pub end_of_epoch_data: Option<EndOfEpochData>,
139
140 pub version_specific_data: Vec<u8>,
148}
149
150impl Message for CheckpointSummary {
151 type DigestType = CheckpointDigest;
152 const SCOPE: IntentScope = IntentScope::CheckpointSummary;
153
154 fn digest(&self) -> Self::DigestType {
155 CheckpointDigest::new(default_hash(self))
156 }
157}
158
159impl CheckpointSummary {
160 pub fn new(
161 protocol_config: &ProtocolConfig,
162 epoch: EpochId,
163 sequence_number: CheckpointSequenceNumber,
164 network_total_transactions: u64,
165 transactions: &CheckpointContents,
166 previous_digest: Option<CheckpointDigest>,
167 epoch_rolling_gas_cost_summary: GasCostSummary,
168 end_of_epoch_data: Option<EndOfEpochData>,
169 timestamp_ms: CheckpointTimestamp,
170 randomness_rounds: Vec<RandomnessRound>,
171 ) -> CheckpointSummary {
172 let content_digest = *transactions.digest();
173
174 let version_specific_data = match protocol_config
175 .checkpoint_summary_version_specific_data_as_option()
176 {
177 None | Some(0) => Vec::new(),
178 Some(1) => bcs::to_bytes(&CheckpointVersionSpecificData::V1(
179 CheckpointVersionSpecificDataV1 { randomness_rounds },
180 ))
181 .expect("version specific data should serialize"),
182 _ => unimplemented!("unrecognized version_specific_data version for CheckpointSummary"),
183 };
184
185 Self {
186 epoch,
187 sequence_number,
188 network_total_transactions,
189 content_digest,
190 previous_digest,
191 epoch_rolling_gas_cost_summary,
192 end_of_epoch_data,
193 timestamp_ms,
194 version_specific_data,
195 checkpoint_commitments: Default::default(),
196 }
197 }
198
199 pub fn verify_epoch(&self, epoch: EpochId) -> IotaResult {
200 fp_ensure!(
201 self.epoch == epoch,
202 IotaError::WrongEpoch {
203 expected_epoch: epoch,
204 actual_epoch: self.epoch,
205 }
206 );
207 Ok(())
208 }
209
210 pub fn sequence_number(&self) -> &CheckpointSequenceNumber {
211 &self.sequence_number
212 }
213
214 pub fn timestamp(&self) -> SystemTime {
215 UNIX_EPOCH + Duration::from_millis(self.timestamp_ms)
216 }
217
218 pub fn next_epoch_committee(&self) -> Option<&[ValidatorCommitteeMember]> {
219 self.end_of_epoch_data
220 .as_ref()
221 .map(|e| e.next_epoch_committee.as_slice())
222 }
223
224 #[cfg(not(target_arch = "wasm32"))]
225 pub fn report_checkpoint_age(&self, metrics: &Histogram) {
226 SystemTime::now()
227 .duration_since(self.timestamp())
228 .map(|latency| {
229 metrics.observe(latency.as_secs_f64());
230 })
231 .tap_err(|err| {
232 warn!(
233 checkpoint_seq = self.sequence_number,
234 "unable to compute checkpoint age: {}", err
235 )
236 })
237 .ok();
238 }
239
240 pub fn is_last_checkpoint_of_epoch(&self) -> bool {
241 self.end_of_epoch_data.is_some()
242 }
243
244 pub fn version_specific_data(
245 &self,
246 config: &ProtocolConfig,
247 ) -> Result<Option<CheckpointVersionSpecificData>> {
248 match config.checkpoint_summary_version_specific_data_as_option() {
249 None | Some(0) => Ok(None),
250 Some(1) => Ok(Some(bcs::from_bytes(&self.version_specific_data)?)),
251 _ => unimplemented!("unrecognized version_specific_data version in CheckpointSummary"),
252 }
253 }
254}
255
256impl Display for CheckpointSummary {
257 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
258 write!(
259 f,
260 "CheckpointSummary {{ epoch: {:?}, seq: {:?}, content_digest: {},
261 epoch_rolling_gas_cost_summary: {:?}}}",
262 self.epoch,
263 self.sequence_number,
264 self.content_digest,
265 self.epoch_rolling_gas_cost_summary,
266 )
267 }
268}
269
270pub type CheckpointSummaryEnvelope<S> = Envelope<CheckpointSummary, S>;
279pub type CertifiedCheckpointSummary = CheckpointSummaryEnvelope<AuthorityStrongQuorumSignInfo>;
280pub type SignedCheckpointSummary = CheckpointSummaryEnvelope<AuthoritySignInfo>;
281
282pub type VerifiedCheckpoint = VerifiedEnvelope<CheckpointSummary, AuthorityStrongQuorumSignInfo>;
283pub type TrustedCheckpoint = TrustedEnvelope<CheckpointSummary, AuthorityStrongQuorumSignInfo>;
284
285impl CertifiedCheckpointSummary {
286 #[instrument(level = "trace", skip_all)]
287 pub fn verify_authority_signatures(&self, committee: &Committee) -> IotaResult {
288 self.data().verify_epoch(self.auth_sig().epoch)?;
289 self.auth_sig().verify_secure(
290 self.data(),
291 Intent::iota_app(IntentScope::CheckpointSummary),
292 committee,
293 )
294 }
295
296 pub fn try_into_verified(self, committee: &Committee) -> IotaResult<VerifiedCheckpoint> {
297 self.verify_authority_signatures(committee)?;
298 Ok(VerifiedCheckpoint::new_from_verified(self))
299 }
300
301 pub fn verify_with_contents(
302 &self,
303 committee: &Committee,
304 contents: Option<&CheckpointContents>,
305 ) -> IotaResult {
306 self.verify_authority_signatures(committee)?;
307
308 if let Some(contents) = contents {
309 let content_digest = *contents.digest();
310 fp_ensure!(
311 content_digest == self.data().content_digest,
312 IotaError::GenericAuthority {
313 error: format!(
314 "Checkpoint contents digest mismatch: summary={:?}, received content digest {:?}, received {} transactions",
315 self.data(),
316 content_digest,
317 contents.size()
318 )
319 }
320 );
321 }
322
323 Ok(())
324 }
325
326 pub fn into_summary_and_sequence(self) -> (CheckpointSequenceNumber, CheckpointSummary) {
327 let summary = self.into_data();
328 (summary.sequence_number, summary)
329 }
330
331 pub fn get_validator_signature(self) -> AggregateAuthoritySignature {
332 self.auth_sig().signature.clone()
333 }
334}
335
336impl SignedCheckpointSummary {
337 #[instrument(level = "trace", skip_all)]
338 pub fn verify_authority_signatures(&self, committee: &Committee) -> IotaResult {
339 self.data().verify_epoch(self.auth_sig().epoch)?;
340 self.auth_sig().verify_secure(
341 self.data(),
342 Intent::iota_app(IntentScope::CheckpointSummary),
343 committee,
344 )
345 }
346
347 pub fn try_into_verified(
348 self,
349 committee: &Committee,
350 ) -> IotaResult<VerifiedEnvelope<CheckpointSummary, AuthoritySignInfo>> {
351 self.verify_authority_signatures(committee)?;
352 Ok(VerifiedEnvelope::<CheckpointSummary, AuthoritySignInfo>::new_from_verified(self))
353 }
354}
355
356impl VerifiedCheckpoint {
357 pub fn into_summary_and_sequence(self) -> (CheckpointSequenceNumber, CheckpointSummary) {
358 self.into_inner().into_summary_and_sequence()
359 }
360}
361
362#[derive(Clone, Debug, Serialize, Deserialize)]
365pub struct CheckpointSignatureMessage {
366 pub summary: SignedCheckpointSummary,
367}
368
369impl CheckpointSignatureMessage {
370 pub fn verify(&self, committee: &Committee) -> IotaResult {
371 self.summary.verify_authority_signatures(committee)
372 }
373}
374
375#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
376pub enum CheckpointContents {
377 V1(CheckpointContentsV1),
378}
379
380#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
385pub struct CheckpointContentsV1 {
386 #[serde(skip)]
387 digest: OnceCell<CheckpointContentsDigest>,
388
389 transactions: Vec<ExecutionDigests>,
390 user_signatures: Vec<Vec<GenericSignature>>,
394}
395
396impl CheckpointContents {
397 pub fn new_with_digests_and_signatures(
398 contents: impl IntoIterator<Item = ExecutionDigests>,
399 user_signatures: Vec<Vec<GenericSignature>>,
400 ) -> Self {
401 let transactions: Vec<_> = contents.into_iter().collect();
402 assert_eq!(transactions.len(), user_signatures.len());
403 Self::V1(CheckpointContentsV1 {
404 digest: Default::default(),
405 transactions,
406 user_signatures,
407 })
408 }
409
410 pub fn new_with_causally_ordered_execution_data<'a>(
411 contents: impl IntoIterator<Item = &'a VerifiedExecutionData>,
412 ) -> Self {
413 let (transactions, user_signatures): (Vec<_>, Vec<_>) = contents
414 .into_iter()
415 .map(|data| {
416 (
417 data.digests(),
418 data.transaction.inner().data().tx_signatures().to_owned(),
419 )
420 })
421 .unzip();
422 assert_eq!(transactions.len(), user_signatures.len());
423 Self::V1(CheckpointContentsV1 {
424 digest: Default::default(),
425 transactions,
426 user_signatures,
427 })
428 }
429
430 pub fn new_with_digests_only_for_tests(
431 contents: impl IntoIterator<Item = ExecutionDigests>,
432 ) -> Self {
433 let transactions: Vec<_> = contents.into_iter().collect();
434 let user_signatures = transactions.iter().map(|_| vec![]).collect();
435 Self::V1(CheckpointContentsV1 {
436 digest: Default::default(),
437 transactions,
438 user_signatures,
439 })
440 }
441
442 fn as_v1(&self) -> &CheckpointContentsV1 {
443 match self {
444 Self::V1(v) => v,
445 }
446 }
447
448 fn into_v1(self) -> CheckpointContentsV1 {
449 match self {
450 Self::V1(v) => v,
451 }
452 }
453
454 pub fn iter(&self) -> Iter<'_, ExecutionDigests> {
455 self.as_v1().transactions.iter()
456 }
457
458 pub fn into_iter_with_signatures(
459 self,
460 ) -> impl Iterator<Item = (ExecutionDigests, Vec<GenericSignature>)> {
461 let CheckpointContentsV1 {
462 transactions,
463 user_signatures,
464 ..
465 } = self.into_v1();
466
467 transactions.into_iter().zip(user_signatures)
468 }
469
470 pub fn enumerate_transactions(
475 &self,
476 ckpt: &CheckpointSummary,
477 ) -> impl Iterator<Item = (u64, &ExecutionDigests)> {
478 let start = ckpt.network_total_transactions - self.size() as u64;
479
480 (0u64..)
481 .zip(self.iter())
482 .map(move |(i, digests)| (i + start, digests))
483 }
484
485 pub fn into_inner(self) -> Vec<ExecutionDigests> {
486 self.into_v1().transactions
487 }
488
489 pub fn inner(&self) -> &[ExecutionDigests] {
490 &self.as_v1().transactions
491 }
492
493 pub fn size(&self) -> usize {
494 self.as_v1().transactions.len()
495 }
496
497 pub fn digest(&self) -> &CheckpointContentsDigest {
498 self.as_v1()
499 .digest
500 .get_or_init(|| CheckpointContentsDigest::new(default_hash(self)))
501 }
502}
503
504#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
511pub struct FullCheckpointContents {
512 transactions: Vec<ExecutionData>,
513 user_signatures: Vec<Vec<GenericSignature>>,
517}
518
519impl FullCheckpointContents {
520 pub fn new_with_causally_ordered_transactions<T>(contents: T) -> Self
521 where
522 T: IntoIterator<Item = ExecutionData>,
523 {
524 let (transactions, user_signatures): (Vec<_>, Vec<_>) = contents
525 .into_iter()
526 .map(|data| {
527 let sig = data.transaction.data().tx_signatures().to_owned();
528 (data, sig)
529 })
530 .unzip();
531 assert_eq!(transactions.len(), user_signatures.len());
532 Self {
533 transactions,
534 user_signatures,
535 }
536 }
537
538 pub fn from_contents_and_execution_data(
539 contents: CheckpointContents,
540 execution_data: impl Iterator<Item = ExecutionData>,
541 ) -> Self {
542 let transactions: Vec<_> = execution_data.collect();
543 Self {
544 transactions,
545 user_signatures: contents.into_v1().user_signatures,
546 }
547 }
548
549 pub fn try_from_checkpoint_contents<S>(
550 store: S,
551 contents: CheckpointContents,
552 ) -> Result<Option<Self>, crate::storage::error::Error>
553 where
554 S: ReadStore,
555 {
556 let mut transactions = Vec::with_capacity(contents.size());
557 for tx in contents.iter() {
558 if let (Some(t), Some(e)) = (
559 store.try_get_transaction(&tx.transaction)?,
560 store.try_get_transaction_effects(&tx.transaction)?,
561 ) {
562 transactions.push(ExecutionData::new((*t).clone().into_inner(), e))
563 } else {
564 return Ok(None);
565 }
566 }
567 Ok(Some(Self {
568 transactions,
569 user_signatures: contents.into_v1().user_signatures,
570 }))
571 }
572
573 pub fn iter(&self) -> Iter<'_, ExecutionData> {
574 self.transactions.iter()
575 }
576
577 pub fn verify_digests(&self, digest: CheckpointContentsDigest) -> Result<()> {
581 let self_digest = *self.checkpoint_contents().digest();
582 fp_ensure!(
583 digest == self_digest,
584 anyhow::anyhow!(
585 "checkpoint contents digest {self_digest} does not match expected digest {digest}"
586 )
587 );
588 for tx in self.iter() {
589 let transaction_digest = tx.transaction.digest();
590 fp_ensure!(
591 tx.effects.transaction_digest() == transaction_digest,
592 anyhow::anyhow!(
593 "transaction digest {transaction_digest} does not match expected digest {}",
594 tx.effects.transaction_digest()
595 )
596 );
597 }
598 Ok(())
599 }
600
601 pub fn checkpoint_contents(&self) -> CheckpointContents {
602 CheckpointContents::V1(CheckpointContentsV1 {
603 digest: Default::default(),
604 transactions: self.transactions.iter().map(|tx| tx.digests()).collect(),
605 user_signatures: self.user_signatures.clone(),
606 })
607 }
608
609 pub fn into_checkpoint_contents(self) -> CheckpointContents {
610 CheckpointContents::V1(CheckpointContentsV1 {
611 digest: Default::default(),
612 transactions: self
613 .transactions
614 .into_iter()
615 .map(|tx| tx.digests())
616 .collect(),
617 user_signatures: self.user_signatures,
618 })
619 }
620
621 pub fn size(&self) -> usize {
622 self.transactions.len()
623 }
624
625 pub fn random_for_testing() -> Self {
626 let (a, key): (_, AccountKeyPair) = get_key_pair();
627 let transaction = Transaction::from_data_and_signer(
628 TransactionData::new_transfer(
629 a,
630 random_object_ref(),
631 a,
632 random_object_ref(),
633 100000000000,
634 100,
635 ),
636 vec![&key],
637 );
638 let effects = TestEffectsBuilder::new(transaction.data()).build();
639 let exe_data = ExecutionData {
640 transaction,
641 effects,
642 };
643 FullCheckpointContents::new_with_causally_ordered_transactions(vec![exe_data])
644 }
645}
646
647impl IntoIterator for FullCheckpointContents {
648 type Item = ExecutionData;
649 type IntoIter = std::vec::IntoIter<Self::Item>;
650
651 fn into_iter(self) -> Self::IntoIter {
652 self.transactions.into_iter()
653 }
654}
655
656#[derive(Clone, Debug, PartialEq, Eq)]
657pub struct VerifiedCheckpointContents {
658 transactions: Vec<VerifiedExecutionData>,
659 user_signatures: Vec<Vec<GenericSignature>>,
663}
664
665impl VerifiedCheckpointContents {
666 pub fn new_unchecked(contents: FullCheckpointContents) -> Self {
667 Self {
668 transactions: contents
669 .transactions
670 .into_iter()
671 .map(VerifiedExecutionData::new_unchecked)
672 .collect(),
673 user_signatures: contents.user_signatures,
674 }
675 }
676
677 pub fn iter(&self) -> Iter<'_, VerifiedExecutionData> {
678 self.transactions.iter()
679 }
680
681 pub fn transactions(&self) -> &[VerifiedExecutionData] {
682 &self.transactions
683 }
684
685 pub fn into_inner(self) -> FullCheckpointContents {
686 FullCheckpointContents {
687 transactions: self
688 .transactions
689 .into_iter()
690 .map(|tx| tx.into_inner())
691 .collect(),
692 user_signatures: self.user_signatures,
693 }
694 }
695
696 pub fn into_checkpoint_contents(self) -> CheckpointContents {
697 self.into_inner().into_checkpoint_contents()
698 }
699
700 pub fn into_checkpoint_contents_digest(self) -> CheckpointContentsDigest {
701 *self.into_inner().into_checkpoint_contents().digest()
702 }
703
704 pub fn num_of_transactions(&self) -> usize {
705 self.transactions.len()
706 }
707}
708
709#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
712pub enum CheckpointVersionSpecificData {
713 V1(CheckpointVersionSpecificDataV1),
714}
715
716impl CheckpointVersionSpecificData {
717 pub fn as_v1(&self) -> &CheckpointVersionSpecificDataV1 {
718 match self {
719 Self::V1(v) => v,
720 }
721 }
722
723 pub fn into_v1(self) -> CheckpointVersionSpecificDataV1 {
724 match self {
725 Self::V1(v) => v,
726 }
727 }
728
729 pub fn empty_for_tests() -> CheckpointVersionSpecificData {
730 CheckpointVersionSpecificData::V1(CheckpointVersionSpecificDataV1 {
731 randomness_rounds: Vec::new(),
732 })
733 }
734}
735
736#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
737pub struct CheckpointVersionSpecificDataV1 {
738 pub randomness_rounds: Vec<RandomnessRound>,
741}
742
743#[cfg(test)]
744mod tests {
745 use fastcrypto::traits::KeyPair;
746 use rand::{SeedableRng, prelude::StdRng};
747
748 use super::*;
749 use crate::{
750 digests::{ConsensusCommitDigest, TransactionDigest, TransactionEffectsDigest},
751 transaction::VerifiedTransaction,
752 utils::make_committee_key,
753 };
754
755 const RNG_SEED: [u8; 32] = [
757 21, 23, 199, 200, 234, 250, 252, 178, 94, 15, 202, 178, 62, 186, 88, 137, 233, 192, 130,
758 157, 179, 179, 65, 9, 31, 249, 221, 123, 225, 112, 199, 247,
759 ];
760
761 #[test]
762 fn test_signed_checkpoint() {
763 let mut rng = StdRng::from_seed(RNG_SEED);
764 let (keys, committee) = make_committee_key(&mut rng);
765 let (_, committee2) = make_committee_key(&mut rng);
766
767 let set = CheckpointContents::new_with_digests_only_for_tests([ExecutionDigests::random()]);
768
769 let signed_checkpoints: Vec<_> = keys
772 .iter()
773 .map(|k| {
774 let name = k.public().into();
775
776 SignedCheckpointSummary::new(
777 committee.epoch,
778 CheckpointSummary::new(
779 &ProtocolConfig::get_for_max_version_UNSAFE(),
780 committee.epoch,
781 1,
782 0,
783 &set,
784 None,
785 GasCostSummary::default(),
786 None,
787 0,
788 Vec::new(),
789 ),
790 k,
791 name,
792 )
793 })
794 .collect();
795
796 signed_checkpoints.iter().for_each(|c| {
797 c.verify_authority_signatures(&committee)
798 .expect("signature ok")
799 });
800
801 signed_checkpoints
803 .iter()
804 .for_each(|c| assert!(c.verify_authority_signatures(&committee2).is_err()));
805 }
806
807 #[test]
808 fn test_certified_checkpoint() {
809 let mut rng = StdRng::from_seed(RNG_SEED);
810 let (keys, committee) = make_committee_key(&mut rng);
811
812 let set = CheckpointContents::new_with_digests_only_for_tests([ExecutionDigests::random()]);
813
814 let summary = CheckpointSummary::new(
815 &ProtocolConfig::get_for_max_version_UNSAFE(),
816 committee.epoch,
817 1,
818 0,
819 &set,
820 None,
821 GasCostSummary::default(),
822 None,
823 0,
824 Vec::new(),
825 );
826
827 let sign_infos: Vec<_> = keys
828 .iter()
829 .map(|k| {
830 let name = k.public().into();
831
832 SignedCheckpointSummary::sign(committee.epoch, &summary, k, name)
833 })
834 .collect();
835
836 let checkpoint_cert =
837 CertifiedCheckpointSummary::new(summary, sign_infos, &committee).expect("Cert is OK");
838
839 assert!(
841 checkpoint_cert
842 .verify_with_contents(&committee, Some(&set))
843 .is_ok()
844 );
845
846 let signed_checkpoints: Vec<_> = keys
848 .iter()
849 .map(|k| {
850 let name = k.public().into();
851 let set = CheckpointContents::new_with_digests_only_for_tests([
852 ExecutionDigests::random(),
853 ]);
854
855 SignedCheckpointSummary::new(
856 committee.epoch,
857 CheckpointSummary::new(
858 &ProtocolConfig::get_for_max_version_UNSAFE(),
859 committee.epoch,
860 1,
861 0,
862 &set,
863 None,
864 GasCostSummary::default(),
865 None,
866 0,
867 Vec::new(),
868 ),
869 k,
870 name,
871 )
872 })
873 .collect();
874
875 let summary = signed_checkpoints[0].data().clone();
876 let sign_infos = signed_checkpoints
877 .into_iter()
878 .map(|v| v.into_sig())
879 .collect();
880 assert!(
881 CertifiedCheckpointSummary::new(summary, sign_infos, &committee)
882 .unwrap()
883 .verify_authority_signatures(&committee)
884 .is_err()
885 )
886 }
887
888 fn generate_test_checkpoint_summary_from_digest(
893 digest: TransactionDigest,
894 ) -> CheckpointSummary {
895 CheckpointSummary::new(
896 &ProtocolConfig::get_for_max_version_UNSAFE(),
897 1,
898 2,
899 10,
900 &CheckpointContents::new_with_digests_only_for_tests([ExecutionDigests::new(
901 digest,
902 TransactionEffectsDigest::ZERO,
903 )]),
904 None,
905 GasCostSummary::default(),
906 None,
907 100,
908 Vec::new(),
909 )
910 }
911
912 #[test]
915 fn test_checkpoint_summary_with_different_consensus_digest() {
916 {
919 let t1 = VerifiedTransaction::new_consensus_commit_prologue_v1(
920 1,
921 2,
922 100,
923 ConsensusCommitDigest::default(),
924 Vec::new(),
925 );
926 let t2 = VerifiedTransaction::new_consensus_commit_prologue_v1(
927 1,
928 2,
929 100,
930 ConsensusCommitDigest::default(),
931 Vec::new(),
932 );
933 let c1 = generate_test_checkpoint_summary_from_digest(*t1.digest());
934 let c2 = generate_test_checkpoint_summary_from_digest(*t2.digest());
935 assert_eq!(c1.digest(), c2.digest());
936 }
937
938 {
941 let t1 = VerifiedTransaction::new_consensus_commit_prologue_v1(
942 1,
943 2,
944 100,
945 ConsensusCommitDigest::default(),
946 Vec::new(),
947 );
948 let t2 = VerifiedTransaction::new_consensus_commit_prologue_v1(
949 1,
950 2,
951 100,
952 ConsensusCommitDigest::random(),
953 Vec::new(),
954 );
955 let c1 = generate_test_checkpoint_summary_from_digest(*t1.digest());
956 let c2 = generate_test_checkpoint_summary_from_digest(*t2.digest());
957 assert_ne!(c1.digest(), c2.digest());
958 }
959 }
960}