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