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 once_cell::sync::OnceCell;
15use prometheus::Histogram;
16use schemars::JsonSchema;
17use serde::{Deserialize, Serialize};
18use serde_with::serde_as;
19use shared_crypto::intent::{Intent, IntentScope};
20use tap::TapFallible;
21use tracing::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 pub fn verify_authority_signatures(&self, committee: &Committee) -> IotaResult {
322 self.data().verify_epoch(self.auth_sig().epoch)?;
323 self.auth_sig().verify_secure(
324 self.data(),
325 Intent::iota_app(IntentScope::CheckpointSummary),
326 committee,
327 )
328 }
329
330 pub fn try_into_verified(self, committee: &Committee) -> IotaResult<VerifiedCheckpoint> {
331 self.verify_authority_signatures(committee)?;
332 Ok(VerifiedCheckpoint::new_from_verified(self))
333 }
334
335 pub fn verify_with_contents(
336 &self,
337 committee: &Committee,
338 contents: Option<&CheckpointContents>,
339 ) -> IotaResult {
340 self.verify_authority_signatures(committee)?;
341
342 if let Some(contents) = contents {
343 let content_digest = *contents.digest();
344 fp_ensure!(
345 content_digest == self.data().content_digest,
346 IotaError::GenericAuthority {
347 error: format!(
348 "Checkpoint contents digest mismatch: summary={:?}, received content digest {:?}, received {} transactions",
349 self.data(),
350 content_digest,
351 contents.size()
352 )
353 }
354 );
355 }
356
357 Ok(())
358 }
359
360 pub fn into_summary_and_sequence(self) -> (CheckpointSequenceNumber, CheckpointSummary) {
361 let summary = self.into_data();
362 (summary.sequence_number, summary)
363 }
364
365 pub fn get_validator_signature(self) -> AggregateAuthoritySignature {
366 self.auth_sig().signature.clone()
367 }
368}
369
370impl SignedCheckpointSummary {
371 pub fn verify_authority_signatures(&self, committee: &Committee) -> IotaResult {
372 self.data().verify_epoch(self.auth_sig().epoch)?;
373 self.auth_sig().verify_secure(
374 self.data(),
375 Intent::iota_app(IntentScope::CheckpointSummary),
376 committee,
377 )
378 }
379
380 pub fn try_into_verified(
381 self,
382 committee: &Committee,
383 ) -> IotaResult<VerifiedEnvelope<CheckpointSummary, AuthoritySignInfo>> {
384 self.verify_authority_signatures(committee)?;
385 Ok(VerifiedEnvelope::<CheckpointSummary, AuthoritySignInfo>::new_from_verified(self))
386 }
387}
388
389impl VerifiedCheckpoint {
390 pub fn into_summary_and_sequence(self) -> (CheckpointSequenceNumber, CheckpointSummary) {
391 self.into_inner().into_summary_and_sequence()
392 }
393}
394
395#[derive(Clone, Debug, Serialize, Deserialize)]
398pub struct CheckpointSignatureMessage {
399 pub summary: SignedCheckpointSummary,
400}
401
402impl CheckpointSignatureMessage {
403 pub fn verify(&self, committee: &Committee) -> IotaResult {
404 self.summary.verify_authority_signatures(committee)
405 }
406}
407
408#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
409pub enum CheckpointContents {
410 V1(CheckpointContentsV1),
411}
412
413#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
418pub struct CheckpointContentsV1 {
419 #[serde(skip)]
420 digest: OnceCell<CheckpointContentsDigest>,
421
422 transactions: Vec<ExecutionDigests>,
423 user_signatures: Vec<Vec<GenericSignature>>,
427}
428
429impl CheckpointContents {
430 pub fn new_with_digests_and_signatures(
431 contents: impl IntoIterator<Item = ExecutionDigests>,
432 user_signatures: Vec<Vec<GenericSignature>>,
433 ) -> Self {
434 let transactions: Vec<_> = contents.into_iter().collect();
435 assert_eq!(transactions.len(), user_signatures.len());
436 Self::V1(CheckpointContentsV1 {
437 digest: Default::default(),
438 transactions,
439 user_signatures,
440 })
441 }
442
443 pub fn new_with_causally_ordered_execution_data<'a>(
444 contents: impl IntoIterator<Item = &'a VerifiedExecutionData>,
445 ) -> Self {
446 let (transactions, user_signatures): (Vec<_>, Vec<_>) = contents
447 .into_iter()
448 .map(|data| {
449 (
450 data.digests(),
451 data.transaction.inner().data().tx_signatures().to_owned(),
452 )
453 })
454 .unzip();
455 assert_eq!(transactions.len(), user_signatures.len());
456 Self::V1(CheckpointContentsV1 {
457 digest: Default::default(),
458 transactions,
459 user_signatures,
460 })
461 }
462
463 pub fn new_with_digests_only_for_tests(
464 contents: impl IntoIterator<Item = ExecutionDigests>,
465 ) -> Self {
466 let transactions: Vec<_> = contents.into_iter().collect();
467 let user_signatures = transactions.iter().map(|_| vec![]).collect();
468 Self::V1(CheckpointContentsV1 {
469 digest: Default::default(),
470 transactions,
471 user_signatures,
472 })
473 }
474
475 fn as_v1(&self) -> &CheckpointContentsV1 {
476 match self {
477 Self::V1(v) => v,
478 }
479 }
480
481 fn into_v1(self) -> CheckpointContentsV1 {
482 match self {
483 Self::V1(v) => v,
484 }
485 }
486
487 pub fn iter(&self) -> Iter<'_, ExecutionDigests> {
488 self.as_v1().transactions.iter()
489 }
490
491 pub fn into_iter_with_signatures(
492 self,
493 ) -> impl Iterator<Item = (ExecutionDigests, Vec<GenericSignature>)> {
494 let CheckpointContentsV1 {
495 transactions,
496 user_signatures,
497 ..
498 } = self.into_v1();
499
500 transactions.into_iter().zip(user_signatures)
501 }
502
503 pub fn enumerate_transactions(
508 &self,
509 ckpt: &CheckpointSummary,
510 ) -> impl Iterator<Item = (u64, &ExecutionDigests)> {
511 let start = ckpt.network_total_transactions - self.size() as u64;
512
513 (0u64..)
514 .zip(self.iter())
515 .map(move |(i, digests)| (i + start, digests))
516 }
517
518 pub fn into_inner(self) -> Vec<ExecutionDigests> {
519 self.into_v1().transactions
520 }
521
522 pub fn inner(&self) -> &[ExecutionDigests] {
523 &self.as_v1().transactions
524 }
525
526 pub fn size(&self) -> usize {
527 self.as_v1().transactions.len()
528 }
529
530 pub fn digest(&self) -> &CheckpointContentsDigest {
531 self.as_v1()
532 .digest
533 .get_or_init(|| CheckpointContentsDigest::new(default_hash(self)))
534 }
535}
536
537#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
544pub struct FullCheckpointContents {
545 transactions: Vec<ExecutionData>,
546 user_signatures: Vec<Vec<GenericSignature>>,
550}
551
552impl FullCheckpointContents {
553 pub fn new_with_causally_ordered_transactions<T>(contents: T) -> Self
554 where
555 T: IntoIterator<Item = ExecutionData>,
556 {
557 let (transactions, user_signatures): (Vec<_>, Vec<_>) = contents
558 .into_iter()
559 .map(|data| {
560 let sig = data.transaction.data().tx_signatures().to_owned();
561 (data, sig)
562 })
563 .unzip();
564 assert_eq!(transactions.len(), user_signatures.len());
565 Self {
566 transactions,
567 user_signatures,
568 }
569 }
570
571 pub fn from_contents_and_execution_data(
572 contents: CheckpointContents,
573 execution_data: impl Iterator<Item = ExecutionData>,
574 ) -> Self {
575 let transactions: Vec<_> = execution_data.collect();
576 Self {
577 transactions,
578 user_signatures: contents.into_v1().user_signatures,
579 }
580 }
581
582 pub fn try_from_checkpoint_contents<S>(
583 store: S,
584 contents: CheckpointContents,
585 ) -> Result<Option<Self>, crate::storage::error::Error>
586 where
587 S: ReadStore,
588 {
589 let mut transactions = Vec::with_capacity(contents.size());
590 for tx in contents.iter() {
591 if let (Some(t), Some(e)) = (
592 store.try_get_transaction(&tx.transaction)?,
593 store.try_get_transaction_effects(&tx.transaction)?,
594 ) {
595 transactions.push(ExecutionData::new((*t).clone().into_inner(), e))
596 } else {
597 return Ok(None);
598 }
599 }
600 Ok(Some(Self {
601 transactions,
602 user_signatures: contents.into_v1().user_signatures,
603 }))
604 }
605
606 pub fn iter(&self) -> Iter<'_, ExecutionData> {
607 self.transactions.iter()
608 }
609
610 pub fn verify_digests(&self, digest: CheckpointContentsDigest) -> Result<()> {
614 let self_digest = *self.checkpoint_contents().digest();
615 fp_ensure!(
616 digest == self_digest,
617 anyhow::anyhow!(
618 "checkpoint contents digest {self_digest} does not match expected digest {digest}"
619 )
620 );
621 for tx in self.iter() {
622 let transaction_digest = tx.transaction.digest();
623 fp_ensure!(
624 tx.effects.transaction_digest() == transaction_digest,
625 anyhow::anyhow!(
626 "transaction digest {transaction_digest} does not match expected digest {}",
627 tx.effects.transaction_digest()
628 )
629 );
630 }
631 Ok(())
632 }
633
634 pub fn checkpoint_contents(&self) -> CheckpointContents {
635 CheckpointContents::V1(CheckpointContentsV1 {
636 digest: Default::default(),
637 transactions: self.transactions.iter().map(|tx| tx.digests()).collect(),
638 user_signatures: self.user_signatures.clone(),
639 })
640 }
641
642 pub fn into_checkpoint_contents(self) -> CheckpointContents {
643 CheckpointContents::V1(CheckpointContentsV1 {
644 digest: Default::default(),
645 transactions: self
646 .transactions
647 .into_iter()
648 .map(|tx| tx.digests())
649 .collect(),
650 user_signatures: self.user_signatures,
651 })
652 }
653
654 pub fn size(&self) -> usize {
655 self.transactions.len()
656 }
657
658 pub fn random_for_testing() -> Self {
659 let (a, key): (_, AccountKeyPair) = get_key_pair();
660 let transaction = Transaction::from_data_and_signer(
661 TransactionData::new_transfer(
662 a,
663 random_object_ref(),
664 a,
665 random_object_ref(),
666 100000000000,
667 100,
668 ),
669 vec![&key],
670 );
671 let effects = TestEffectsBuilder::new(transaction.data()).build();
672 let exe_data = ExecutionData {
673 transaction,
674 effects,
675 };
676 FullCheckpointContents::new_with_causally_ordered_transactions(vec![exe_data])
677 }
678}
679
680impl IntoIterator for FullCheckpointContents {
681 type Item = ExecutionData;
682 type IntoIter = std::vec::IntoIter<Self::Item>;
683
684 fn into_iter(self) -> Self::IntoIter {
685 self.transactions.into_iter()
686 }
687}
688
689#[derive(Clone, Debug, PartialEq, Eq)]
690pub struct VerifiedCheckpointContents {
691 transactions: Vec<VerifiedExecutionData>,
692 user_signatures: Vec<Vec<GenericSignature>>,
696}
697
698impl VerifiedCheckpointContents {
699 pub fn new_unchecked(contents: FullCheckpointContents) -> Self {
700 Self {
701 transactions: contents
702 .transactions
703 .into_iter()
704 .map(VerifiedExecutionData::new_unchecked)
705 .collect(),
706 user_signatures: contents.user_signatures,
707 }
708 }
709
710 pub fn iter(&self) -> Iter<'_, VerifiedExecutionData> {
711 self.transactions.iter()
712 }
713
714 pub fn transactions(&self) -> &[VerifiedExecutionData] {
715 &self.transactions
716 }
717
718 pub fn into_inner(self) -> FullCheckpointContents {
719 FullCheckpointContents {
720 transactions: self
721 .transactions
722 .into_iter()
723 .map(|tx| tx.into_inner())
724 .collect(),
725 user_signatures: self.user_signatures,
726 }
727 }
728
729 pub fn into_checkpoint_contents(self) -> CheckpointContents {
730 self.into_inner().into_checkpoint_contents()
731 }
732
733 pub fn into_checkpoint_contents_digest(self) -> CheckpointContentsDigest {
734 *self.into_inner().into_checkpoint_contents().digest()
735 }
736
737 pub fn num_of_transactions(&self) -> usize {
738 self.transactions.len()
739 }
740}
741
742#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
745pub enum CheckpointVersionSpecificData {
746 V1(CheckpointVersionSpecificDataV1),
747}
748
749impl CheckpointVersionSpecificData {
750 pub fn as_v1(&self) -> &CheckpointVersionSpecificDataV1 {
751 match self {
752 Self::V1(v) => v,
753 }
754 }
755
756 pub fn into_v1(self) -> CheckpointVersionSpecificDataV1 {
757 match self {
758 Self::V1(v) => v,
759 }
760 }
761
762 pub fn empty_for_tests() -> CheckpointVersionSpecificData {
763 CheckpointVersionSpecificData::V1(CheckpointVersionSpecificDataV1 {
764 randomness_rounds: Vec::new(),
765 })
766 }
767}
768
769#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
770pub struct CheckpointVersionSpecificDataV1 {
771 pub randomness_rounds: Vec<RandomnessRound>,
774}
775
776#[cfg(test)]
777mod tests {
778 use fastcrypto::traits::KeyPair;
779 use rand::{SeedableRng, prelude::StdRng};
780
781 use super::*;
782 use crate::{
783 digests::{ConsensusCommitDigest, TransactionDigest, TransactionEffectsDigest},
784 transaction::VerifiedTransaction,
785 utils::make_committee_key,
786 };
787
788 const RNG_SEED: [u8; 32] = [
790 21, 23, 199, 200, 234, 250, 252, 178, 94, 15, 202, 178, 62, 186, 88, 137, 233, 192, 130,
791 157, 179, 179, 65, 9, 31, 249, 221, 123, 225, 112, 199, 247,
792 ];
793
794 #[test]
795 fn test_signed_checkpoint() {
796 let mut rng = StdRng::from_seed(RNG_SEED);
797 let (keys, committee) = make_committee_key(&mut rng);
798 let (_, committee2) = make_committee_key(&mut rng);
799
800 let set = CheckpointContents::new_with_digests_only_for_tests([ExecutionDigests::random()]);
801
802 let signed_checkpoints: Vec<_> = keys
805 .iter()
806 .map(|k| {
807 let name = k.public().into();
808
809 SignedCheckpointSummary::new(
810 committee.epoch,
811 CheckpointSummary::new(
812 &ProtocolConfig::get_for_max_version_UNSAFE(),
813 committee.epoch,
814 1,
815 0,
816 &set,
817 None,
818 GasCostSummary::default(),
819 None,
820 0,
821 Vec::new(),
822 ),
823 k,
824 name,
825 )
826 })
827 .collect();
828
829 signed_checkpoints.iter().for_each(|c| {
830 c.verify_authority_signatures(&committee)
831 .expect("signature ok")
832 });
833
834 signed_checkpoints
836 .iter()
837 .for_each(|c| assert!(c.verify_authority_signatures(&committee2).is_err()));
838 }
839
840 #[test]
841 fn test_certified_checkpoint() {
842 let mut rng = StdRng::from_seed(RNG_SEED);
843 let (keys, committee) = make_committee_key(&mut rng);
844
845 let set = CheckpointContents::new_with_digests_only_for_tests([ExecutionDigests::random()]);
846
847 let summary = CheckpointSummary::new(
848 &ProtocolConfig::get_for_max_version_UNSAFE(),
849 committee.epoch,
850 1,
851 0,
852 &set,
853 None,
854 GasCostSummary::default(),
855 None,
856 0,
857 Vec::new(),
858 );
859
860 let sign_infos: Vec<_> = keys
861 .iter()
862 .map(|k| {
863 let name = k.public().into();
864
865 SignedCheckpointSummary::sign(committee.epoch, &summary, k, name)
866 })
867 .collect();
868
869 let checkpoint_cert =
870 CertifiedCheckpointSummary::new(summary, sign_infos, &committee).expect("Cert is OK");
871
872 assert!(
874 checkpoint_cert
875 .verify_with_contents(&committee, Some(&set))
876 .is_ok()
877 );
878
879 let signed_checkpoints: Vec<_> = keys
881 .iter()
882 .map(|k| {
883 let name = k.public().into();
884 let set = CheckpointContents::new_with_digests_only_for_tests([
885 ExecutionDigests::random(),
886 ]);
887
888 SignedCheckpointSummary::new(
889 committee.epoch,
890 CheckpointSummary::new(
891 &ProtocolConfig::get_for_max_version_UNSAFE(),
892 committee.epoch,
893 1,
894 0,
895 &set,
896 None,
897 GasCostSummary::default(),
898 None,
899 0,
900 Vec::new(),
901 ),
902 k,
903 name,
904 )
905 })
906 .collect();
907
908 let summary = signed_checkpoints[0].data().clone();
909 let sign_infos = signed_checkpoints
910 .into_iter()
911 .map(|v| v.into_sig())
912 .collect();
913 assert!(
914 CertifiedCheckpointSummary::new(summary, sign_infos, &committee)
915 .unwrap()
916 .verify_authority_signatures(&committee)
917 .is_err()
918 )
919 }
920
921 fn generate_test_checkpoint_summary_from_digest(
926 digest: TransactionDigest,
927 ) -> CheckpointSummary {
928 CheckpointSummary::new(
929 &ProtocolConfig::get_for_max_version_UNSAFE(),
930 1,
931 2,
932 10,
933 &CheckpointContents::new_with_digests_only_for_tests([ExecutionDigests::new(
934 digest,
935 TransactionEffectsDigest::ZERO,
936 )]),
937 None,
938 GasCostSummary::default(),
939 None,
940 100,
941 Vec::new(),
942 )
943 }
944
945 #[test]
948 fn test_checkpoint_summary_with_different_consensus_digest() {
949 {
952 let t1 = VerifiedTransaction::new_consensus_commit_prologue_v1(
953 1,
954 2,
955 100,
956 ConsensusCommitDigest::default(),
957 Vec::new(),
958 );
959 let t2 = VerifiedTransaction::new_consensus_commit_prologue_v1(
960 1,
961 2,
962 100,
963 ConsensusCommitDigest::default(),
964 Vec::new(),
965 );
966 let c1 = generate_test_checkpoint_summary_from_digest(*t1.digest());
967 let c2 = generate_test_checkpoint_summary_from_digest(*t2.digest());
968 assert_eq!(c1.digest(), c2.digest());
969 }
970
971 {
974 let t1 = VerifiedTransaction::new_consensus_commit_prologue_v1(
975 1,
976 2,
977 100,
978 ConsensusCommitDigest::default(),
979 Vec::new(),
980 );
981 let t2 = VerifiedTransaction::new_consensus_commit_prologue_v1(
982 1,
983 2,
984 100,
985 ConsensusCommitDigest::random(),
986 Vec::new(),
987 );
988 let c1 = generate_test_checkpoint_summary_from_digest(*t1.digest());
989 let c2 = generate_test_checkpoint_summary_from_digest(*t2.digest());
990 assert_ne!(c1.digest(), c2.digest());
991 }
992 }
993}