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