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