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 pub fn from_contents_and_execution_data(
571 contents: CheckpointContents,
572 execution_data: impl Iterator<Item = ExecutionData>,
573 ) -> Self {
574 let transactions: Vec<_> = execution_data.collect();
575 Self {
576 transactions,
577 user_signatures: contents.into_v1().user_signatures,
578 }
579 }
580 pub fn 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.get_transaction(&tx.transaction)?,
591 store.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}