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