1pub(crate) mod causal_order;
5pub mod checkpoint_executor;
6mod checkpoint_output;
7mod metrics;
8
9use crate::accumulators::{self, AccumulatorSettlementTxBuilder};
10use crate::authority::AuthorityState;
11use crate::authority_client::{AuthorityAPI, make_network_authority_clients_with_network_config};
12use crate::checkpoints::causal_order::CausalOrder;
13use crate::checkpoints::checkpoint_output::CertifiedCheckpointOutput;
14pub use crate::checkpoints::checkpoint_output::{
15 CheckpointOutput, LogCheckpointOutput, SendCheckpointToStateSync, SubmitCheckpointToConsensus,
16};
17pub use crate::checkpoints::metrics::CheckpointMetrics;
18use crate::consensus_manager::ReplayWaiter;
19use crate::execution_cache::TransactionCacheRead;
20
21use crate::global_state_hasher::GlobalStateHasher;
22use crate::stake_aggregator::{InsertResult, MultiStakeAggregator};
23use consensus_core::CommitRef;
24use diffy::create_patch;
25use itertools::Itertools;
26use mysten_common::ZipDebugEqIteratorExt;
27use mysten_common::random::get_rng;
28use mysten_common::sync::notify_read::{CHECKPOINT_BUILDER_NOTIFY_READ_TASK_NAME, NotifyRead};
29use mysten_common::{assert_reachable, debug_fatal, fatal, in_antithesis};
30use mysten_metrics::{MonitoredFutureExt, monitored_scope, spawn_monitored_task};
31use parking_lot::Mutex;
32use pin_project_lite::pin_project;
33use serde::{Deserialize, Serialize};
34use sui_macros::fail_point_arg;
35use sui_network::default_mysten_network_config;
36use sui_types::accumulator_metadata;
37use sui_types::base_types::{ConciseableName, SequenceNumber};
38use sui_types::execution::ExecutionTimeObservationKey;
39use sui_types::messages_checkpoint::{
40 CheckpointArtifacts, CheckpointCommitment, VersionedFullCheckpointContents,
41};
42use sui_types::sui_system_state::epoch_start_sui_system_state::EpochStartSystemStateTrait;
43use tokio::sync::{mpsc, watch};
44#[cfg(not(tidehunter))]
45use typed_store::rocks::{DBOptions, ReadWriteOptions, default_db_options};
46
47use crate::authority::authority_per_epoch_store::AuthorityPerEpochStore;
48use crate::authority::authority_store_pruner::PrunerWatermarks;
49use crate::consensus_handler::SequencedConsensusTransactionKey;
50use rand::seq::SliceRandom;
51use std::collections::{BTreeMap, HashMap, HashSet};
52use std::fs::File;
53use std::future::Future;
54use std::io::Write;
55use std::path::Path;
56use std::pin::Pin;
57use std::sync::Arc;
58use std::sync::Weak;
59use std::task::{Context, Poll};
60use std::time::{Duration, SystemTime};
61use sui_protocol_config::ProtocolVersion;
62use sui_types::base_types::{AuthorityName, EpochId, TransactionDigest};
63use sui_types::committee::StakeUnit;
64use sui_types::crypto::AuthorityStrongQuorumSignInfo;
65use sui_types::digests::{
66 CheckpointContentsDigest, CheckpointDigest, Digest, TransactionEffectsDigest,
67};
68use sui_types::effects::{TransactionEffects, TransactionEffectsAPI};
69use sui_types::error::{SuiErrorKind, SuiResult};
70use sui_types::gas::GasCostSummary;
71use sui_types::message_envelope::Message;
72use sui_types::messages_checkpoint::{
73 CertifiedCheckpointSummary, CheckpointContents, CheckpointResponseV2, CheckpointSequenceNumber,
74 CheckpointSignatureMessage, CheckpointSummary, CheckpointSummaryResponse, CheckpointTimestamp,
75 EndOfEpochData, FullCheckpointContents, TrustedCheckpoint, VerifiedCheckpoint,
76 VerifiedCheckpointContents,
77};
78use sui_types::messages_checkpoint::{CheckpointRequestV2, SignedCheckpointSummary};
79use sui_types::messages_consensus::ConsensusTransactionKey;
80use sui_types::signature::GenericSignature;
81use sui_types::sui_system_state::{SuiSystemState, SuiSystemStateTrait};
82use sui_types::transaction::{
83 TransactionDataAPI, TransactionKey, TransactionKind, VerifiedTransaction,
84};
85use tokio::sync::Notify;
86use tracing::{debug, error, info, instrument, trace, warn};
87use typed_store::DBMapUtils;
88use typed_store::Map;
89use typed_store::{
90 TypedStoreError,
91 rocks::{DBMap, MetricConf},
92};
93
94const TRANSACTION_FORK_DETECTED_KEY: u8 = 0;
95
96pub type CheckpointHeight = u64;
97
98pub struct EpochStats {
99 pub checkpoint_count: u64,
100 pub transaction_count: u64,
101 pub total_gas_reward: u64,
102}
103
104#[derive(Clone, Debug)]
105pub struct PendingCheckpointInfo {
106 pub timestamp_ms: CheckpointTimestamp,
107 pub last_of_epoch: bool,
108 pub checkpoint_height: CheckpointHeight,
109 pub consensus_commit_ref: CommitRef,
111 pub rejected_transactions_digest: Digest,
112 pub checkpoint_seq: CheckpointSequenceNumber,
114}
115
116#[derive(Clone, Debug, Default)]
117pub struct CheckpointRoots {
118 pub tx_roots: Vec<TransactionKey>,
119 pub settlement_root: Option<TransactionKey>,
120 pub height: CheckpointHeight,
121}
122
123#[derive(Clone, Debug)]
126pub struct PendingCheckpoint {
127 pub roots: Vec<CheckpointRoots>,
128 pub details: PendingCheckpointInfo,
129}
130
131#[derive(Clone, Debug, Serialize, Deserialize)]
132pub struct BuilderCheckpointSummary {
133 pub summary: CheckpointSummary,
134 pub checkpoint_height: Option<CheckpointHeight>,
136 pub position_in_commit: usize,
139}
140
141#[derive(DBMapUtils)]
142#[cfg_attr(tidehunter, tidehunter)]
143pub struct CheckpointStoreTables {
144 pub(crate) checkpoint_content: DBMap<CheckpointContentsDigest, CheckpointContents>,
146
147 pub(crate) checkpoint_sequence_by_contents_digest:
149 DBMap<CheckpointContentsDigest, CheckpointSequenceNumber>,
150
151 #[default_options_override_fn = "full_checkpoint_content_table_default_config"]
155 full_checkpoint_content: DBMap<CheckpointSequenceNumber, FullCheckpointContents>,
158
159 pub(crate) certified_checkpoints: DBMap<CheckpointSequenceNumber, TrustedCheckpoint>,
161 pub(crate) checkpoint_by_digest: DBMap<CheckpointDigest, TrustedCheckpoint>,
163
164 pub(crate) locally_computed_checkpoints: DBMap<CheckpointSequenceNumber, CheckpointSummary>,
168
169 epoch_last_checkpoint_map: DBMap<EpochId, CheckpointSequenceNumber>,
171
172 pub(crate) watermarks: DBMap<CheckpointWatermark, (CheckpointSequenceNumber, CheckpointDigest)>,
175
176 pub(crate) transaction_fork_detected: DBMap<
178 u8,
179 (
180 TransactionDigest,
181 TransactionEffectsDigest,
182 TransactionEffectsDigest,
183 ),
184 >,
185 #[default_options_override_fn = "full_checkpoint_content_table_default_config"]
186 full_checkpoint_content_v2: DBMap<CheckpointSequenceNumber, VersionedFullCheckpointContents>,
187}
188
189#[cfg(not(tidehunter))]
190fn full_checkpoint_content_table_default_config() -> DBOptions {
191 DBOptions {
192 options: default_db_options().options,
193 rw_options: ReadWriteOptions::default().set_log_value_hash(true),
197 }
198}
199
200impl CheckpointStoreTables {
201 #[cfg(not(tidehunter))]
202 pub fn new(path: &Path, metric_name: &'static str, _: Arc<PrunerWatermarks>) -> Self {
203 Self::open_tables_read_write(path.to_path_buf(), MetricConf::new(metric_name), None, None)
204 }
205
206 #[cfg(tidehunter)]
207 pub fn new(
208 path: &Path,
209 metric_name: &'static str,
210 pruner_watermarks: Arc<PrunerWatermarks>,
211 ) -> Self {
212 tracing::warn!("Checkpoint DB using tidehunter");
213 use crate::authority::authority_store_pruner::apply_relocation_filter;
214 use typed_store::tidehunter_util::{
215 Decision, KeySpaceConfig, KeyType, ThConfig, default_cells_per_mutex,
216 default_max_dirty_keys, default_mutex_count, default_value_cache_size,
217 };
218 let mutexes = default_mutex_count();
219 let u64_sequence_key = KeyType::from_prefix_bits(6 * 8);
220 let override_dirty_keys_config = KeySpaceConfig::new()
221 .with_max_dirty_keys(16 * default_max_dirty_keys())
222 .with_value_cache_size(default_value_cache_size());
223 let config_u64 = ThConfig::new_with_config(
224 8,
225 mutexes,
226 u64_sequence_key,
227 override_dirty_keys_config.clone(),
228 );
229 let digest_config = ThConfig::new_with_rm_prefix(
230 32,
231 mutexes,
232 KeyType::uniform(default_cells_per_mutex()),
233 KeySpaceConfig::default(),
234 vec![0, 0, 0, 0, 0, 0, 0, 32],
235 );
236 let watermarks_config = KeySpaceConfig::new()
237 .with_value_cache_size(10)
238 .disable_unload();
239 let lru_config = KeySpaceConfig::new().with_value_cache_size(100);
240 let configs = vec![
241 (
242 "checkpoint_content",
243 digest_config.clone().with_config(
244 KeySpaceConfig::new().with_relocation_filter(|_, _| Decision::Remove),
245 ),
246 ),
247 (
248 "checkpoint_sequence_by_contents_digest",
249 digest_config.clone().with_config(apply_relocation_filter(
250 KeySpaceConfig::default(),
251 pruner_watermarks.checkpoint_id.clone(),
252 |sequence_number: CheckpointSequenceNumber| sequence_number,
253 false,
254 )),
255 ),
256 (
257 "full_checkpoint_content",
258 config_u64.clone().with_config(apply_relocation_filter(
259 override_dirty_keys_config.clone(),
260 pruner_watermarks.checkpoint_id.clone(),
261 |sequence_number: CheckpointSequenceNumber| sequence_number,
262 true,
263 )),
264 ),
265 ("certified_checkpoints", config_u64.clone()),
266 (
267 "checkpoint_by_digest",
268 digest_config.clone().with_config(apply_relocation_filter(
269 lru_config,
270 pruner_watermarks.epoch_id.clone(),
271 |checkpoint: TrustedCheckpoint| checkpoint.inner().epoch,
272 false,
273 )),
274 ),
275 (
276 "locally_computed_checkpoints",
277 config_u64.clone().with_config(apply_relocation_filter(
278 override_dirty_keys_config.clone(),
279 pruner_watermarks.checkpoint_id.clone(),
280 |checkpoint_id: CheckpointSequenceNumber| checkpoint_id,
281 true,
282 )),
283 ),
284 ("epoch_last_checkpoint_map", config_u64.clone()),
285 (
286 "watermarks",
287 ThConfig::new_with_config(4, 1, KeyType::uniform(1), watermarks_config.clone()),
288 ),
289 (
290 "transaction_fork_detected",
291 ThConfig::new_with_config(
292 1,
293 1,
294 KeyType::uniform(1),
295 watermarks_config.with_relocation_filter(|_, _| Decision::Remove),
296 ),
297 ),
298 (
299 "full_checkpoint_content_v2",
300 config_u64.clone().with_config(apply_relocation_filter(
301 override_dirty_keys_config.clone(),
302 pruner_watermarks.checkpoint_id.clone(),
303 |sequence_number: CheckpointSequenceNumber| sequence_number,
304 true,
305 )),
306 ),
307 ];
308 Self::open_tables_read_write(
309 path.to_path_buf(),
310 MetricConf::new(metric_name),
311 configs
312 .into_iter()
313 .map(|(cf, config)| (cf.to_string(), config))
314 .collect(),
315 )
316 }
317
318 #[cfg(not(tidehunter))]
319 pub fn open_readonly(path: &Path) -> CheckpointStoreTablesReadOnly {
320 Self::get_read_only_handle(
321 path.to_path_buf(),
322 None,
323 None,
324 MetricConf::new("checkpoint_readonly"),
325 )
326 }
327
328 #[cfg(tidehunter)]
329 pub fn open_readonly(path: &Path) -> Self {
330 Self::new(path, "checkpoint", Arc::new(PrunerWatermarks::default()))
331 }
332}
333
334pub struct CheckpointStore {
335 pub(crate) tables: CheckpointStoreTables,
336 synced_checkpoint_notify_read: NotifyRead<CheckpointSequenceNumber, VerifiedCheckpoint>,
337 executed_checkpoint_notify_read: NotifyRead<CheckpointSequenceNumber, VerifiedCheckpoint>,
338}
339
340impl CheckpointStore {
341 pub fn new(path: &Path, pruner_watermarks: Arc<PrunerWatermarks>) -> Arc<Self> {
342 let tables = CheckpointStoreTables::new(path, "checkpoint", pruner_watermarks);
343 Arc::new(Self {
344 tables,
345 synced_checkpoint_notify_read: NotifyRead::new(),
346 executed_checkpoint_notify_read: NotifyRead::new(),
347 })
348 }
349
350 pub fn new_for_tests() -> Arc<Self> {
351 let ckpt_dir = mysten_common::tempdir().unwrap();
352 CheckpointStore::new(ckpt_dir.path(), Arc::new(PrunerWatermarks::default()))
353 }
354
355 pub fn new_for_db_checkpoint_handler(path: &Path) -> Arc<Self> {
356 let tables = CheckpointStoreTables::new(
357 path,
358 "db_checkpoint",
359 Arc::new(PrunerWatermarks::default()),
360 );
361 Arc::new(Self {
362 tables,
363 synced_checkpoint_notify_read: NotifyRead::new(),
364 executed_checkpoint_notify_read: NotifyRead::new(),
365 })
366 }
367
368 #[cfg(not(tidehunter))]
369 pub fn open_readonly(path: &Path) -> CheckpointStoreTablesReadOnly {
370 CheckpointStoreTables::open_readonly(path)
371 }
372
373 #[cfg(tidehunter)]
374 pub fn open_readonly(path: &Path) -> CheckpointStoreTables {
375 CheckpointStoreTables::open_readonly(path)
376 }
377
378 #[instrument(level = "info", skip_all)]
379 pub fn insert_genesis_checkpoint(
380 &self,
381 checkpoint: VerifiedCheckpoint,
382 contents: CheckpointContents,
383 epoch_store: &AuthorityPerEpochStore,
384 ) {
385 assert_eq!(
386 checkpoint.epoch(),
387 0,
388 "can't call insert_genesis_checkpoint with a checkpoint not in epoch 0"
389 );
390 assert_eq!(
391 *checkpoint.sequence_number(),
392 0,
393 "can't call insert_genesis_checkpoint with a checkpoint that doesn't have a sequence number of 0"
394 );
395
396 match self.get_checkpoint_by_sequence_number(0).unwrap() {
398 Some(existing_checkpoint) => {
399 assert_eq!(existing_checkpoint.digest(), checkpoint.digest())
400 }
401 None => {
402 if epoch_store.epoch() == checkpoint.epoch {
403 epoch_store
404 .put_genesis_checkpoint_in_builder(checkpoint.data())
405 .unwrap();
406 } else {
407 debug!(
408 validator_epoch =% epoch_store.epoch(),
409 genesis_epoch =% checkpoint.epoch(),
410 "Not inserting checkpoint builder data for genesis checkpoint",
411 );
412 }
413 self.insert_checkpoint_contents(contents).unwrap();
414 self.insert_verified_checkpoint(&checkpoint).unwrap();
415 self.update_highest_synced_checkpoint(&checkpoint).unwrap();
416 }
417 }
418 }
419
420 pub fn get_checkpoint_by_digest(
421 &self,
422 digest: &CheckpointDigest,
423 ) -> Result<Option<VerifiedCheckpoint>, TypedStoreError> {
424 self.tables
425 .checkpoint_by_digest
426 .get(digest)
427 .map(|maybe_checkpoint| maybe_checkpoint.map(|c| c.into()))
428 }
429
430 pub fn get_checkpoint_by_sequence_number(
431 &self,
432 sequence_number: CheckpointSequenceNumber,
433 ) -> Result<Option<VerifiedCheckpoint>, TypedStoreError> {
434 self.tables
435 .certified_checkpoints
436 .get(&sequence_number)
437 .map(|maybe_checkpoint| maybe_checkpoint.map(|c| c.into()))
438 }
439
440 pub fn get_locally_computed_checkpoint(
441 &self,
442 sequence_number: CheckpointSequenceNumber,
443 ) -> Result<Option<CheckpointSummary>, TypedStoreError> {
444 self.tables
445 .locally_computed_checkpoints
446 .get(&sequence_number)
447 }
448
449 pub fn multi_get_locally_computed_checkpoints(
450 &self,
451 sequence_numbers: &[CheckpointSequenceNumber],
452 ) -> Result<Vec<Option<CheckpointSummary>>, TypedStoreError> {
453 let checkpoints = self
454 .tables
455 .locally_computed_checkpoints
456 .multi_get(sequence_numbers)?;
457
458 Ok(checkpoints)
459 }
460
461 pub fn get_sequence_number_by_contents_digest(
462 &self,
463 digest: &CheckpointContentsDigest,
464 ) -> Result<Option<CheckpointSequenceNumber>, TypedStoreError> {
465 self.tables
466 .checkpoint_sequence_by_contents_digest
467 .get(digest)
468 }
469
470 pub fn delete_contents_digest_sequence_number_mapping(
471 &self,
472 digest: &CheckpointContentsDigest,
473 ) -> Result<(), TypedStoreError> {
474 self.tables
475 .checkpoint_sequence_by_contents_digest
476 .remove(digest)
477 }
478
479 pub fn get_latest_certified_checkpoint(
480 &self,
481 ) -> Result<Option<VerifiedCheckpoint>, TypedStoreError> {
482 Ok(self
483 .tables
484 .certified_checkpoints
485 .reversed_safe_iter_with_bounds(None, None)?
486 .next()
487 .transpose()?
488 .map(|(_, v)| v.into()))
489 }
490
491 pub fn get_latest_locally_computed_checkpoint(
492 &self,
493 ) -> Result<Option<CheckpointSummary>, TypedStoreError> {
494 Ok(self
495 .tables
496 .locally_computed_checkpoints
497 .reversed_safe_iter_with_bounds(None, None)?
498 .next()
499 .transpose()?
500 .map(|(_, v)| v))
501 }
502
503 pub fn multi_get_checkpoint_by_sequence_number(
504 &self,
505 sequence_numbers: &[CheckpointSequenceNumber],
506 ) -> Result<Vec<Option<VerifiedCheckpoint>>, TypedStoreError> {
507 let checkpoints = self
508 .tables
509 .certified_checkpoints
510 .multi_get(sequence_numbers)?
511 .into_iter()
512 .map(|maybe_checkpoint| maybe_checkpoint.map(|c| c.into()))
513 .collect();
514
515 Ok(checkpoints)
516 }
517
518 pub fn multi_get_checkpoint_content(
519 &self,
520 contents_digest: &[CheckpointContentsDigest],
521 ) -> Result<Vec<Option<CheckpointContents>>, TypedStoreError> {
522 self.tables.checkpoint_content.multi_get(contents_digest)
523 }
524
525 pub fn get_highest_verified_checkpoint(
526 &self,
527 ) -> Result<Option<VerifiedCheckpoint>, TypedStoreError> {
528 let highest_verified = if let Some(highest_verified) = self
529 .tables
530 .watermarks
531 .get(&CheckpointWatermark::HighestVerified)?
532 {
533 highest_verified
534 } else {
535 return Ok(None);
536 };
537 self.get_checkpoint_by_digest(&highest_verified.1)
538 }
539
540 pub fn get_highest_synced_checkpoint(
541 &self,
542 ) -> Result<Option<VerifiedCheckpoint>, TypedStoreError> {
543 let highest_synced = if let Some(highest_synced) = self
544 .tables
545 .watermarks
546 .get(&CheckpointWatermark::HighestSynced)?
547 {
548 highest_synced
549 } else {
550 return Ok(None);
551 };
552 self.get_checkpoint_by_digest(&highest_synced.1)
553 }
554
555 pub fn get_highest_synced_checkpoint_seq_number(
556 &self,
557 ) -> Result<Option<CheckpointSequenceNumber>, TypedStoreError> {
558 if let Some(highest_synced) = self
559 .tables
560 .watermarks
561 .get(&CheckpointWatermark::HighestSynced)?
562 {
563 Ok(Some(highest_synced.0))
564 } else {
565 Ok(None)
566 }
567 }
568
569 pub fn get_highest_executed_checkpoint_seq_number(
570 &self,
571 ) -> Result<Option<CheckpointSequenceNumber>, TypedStoreError> {
572 if let Some(highest_executed) = self
573 .tables
574 .watermarks
575 .get(&CheckpointWatermark::HighestExecuted)?
576 {
577 Ok(Some(highest_executed.0))
578 } else {
579 Ok(None)
580 }
581 }
582
583 pub fn get_highest_executed_checkpoint(
584 &self,
585 ) -> Result<Option<VerifiedCheckpoint>, TypedStoreError> {
586 let highest_executed = if let Some(highest_executed) = self
587 .tables
588 .watermarks
589 .get(&CheckpointWatermark::HighestExecuted)?
590 {
591 highest_executed
592 } else {
593 return Ok(None);
594 };
595 self.get_checkpoint_by_digest(&highest_executed.1)
596 }
597
598 pub fn get_highest_pruned_checkpoint_seq_number(
599 &self,
600 ) -> Result<Option<CheckpointSequenceNumber>, TypedStoreError> {
601 self.tables
602 .watermarks
603 .get(&CheckpointWatermark::HighestPruned)
604 .map(|watermark| watermark.map(|w| w.0))
605 }
606
607 pub fn get_checkpoint_contents(
608 &self,
609 digest: &CheckpointContentsDigest,
610 ) -> Result<Option<CheckpointContents>, TypedStoreError> {
611 self.tables.checkpoint_content.get(digest)
612 }
613
614 pub fn get_full_checkpoint_contents_by_sequence_number(
615 &self,
616 seq: CheckpointSequenceNumber,
617 ) -> Result<Option<VersionedFullCheckpointContents>, TypedStoreError> {
618 self.tables.full_checkpoint_content_v2.get(&seq)
619 }
620
621 fn prune_local_summaries(&self) -> SuiResult {
622 if let Some((last_local_summary, _)) = self
623 .tables
624 .locally_computed_checkpoints
625 .reversed_safe_iter_with_bounds(None, None)?
626 .next()
627 .transpose()?
628 {
629 let mut batch = self.tables.locally_computed_checkpoints.batch();
630 batch.schedule_delete_range(
631 &self.tables.locally_computed_checkpoints,
632 &0,
633 &last_local_summary,
634 )?;
635 batch.write()?;
636 info!("Pruned local summaries up to {:?}", last_local_summary);
637 }
638 Ok(())
639 }
640
641 pub fn clear_locally_computed_checkpoints_from(
642 &self,
643 from_seq: CheckpointSequenceNumber,
644 ) -> SuiResult {
645 let keys: Vec<_> = self
646 .tables
647 .locally_computed_checkpoints
648 .safe_iter_with_bounds(Some(from_seq), None)
649 .map(|r| r.map(|(k, _)| k))
650 .collect::<Result<_, _>>()?;
651 if let Some(&last_local_summary) = keys.last() {
652 let mut batch = self.tables.locally_computed_checkpoints.batch();
653 batch
654 .delete_batch(&self.tables.locally_computed_checkpoints, keys.iter())
655 .expect("Failed to delete locally computed checkpoints");
656 batch
657 .write()
658 .expect("Failed to delete locally computed checkpoints");
659 warn!(
660 from_seq,
661 last_local_summary,
662 "Cleared locally_computed_checkpoints from {} (inclusive) through {} (inclusive)",
663 from_seq,
664 last_local_summary
665 );
666 }
667 Ok(())
668 }
669
670 fn check_for_checkpoint_fork(
671 &self,
672 local_checkpoint: &CheckpointSummary,
673 verified_checkpoint: &VerifiedCheckpoint,
674 ) {
675 if local_checkpoint != verified_checkpoint.data() {
676 let verified_contents = self
677 .get_checkpoint_contents(&verified_checkpoint.content_digest)
678 .map(|opt_contents| {
679 opt_contents
680 .map(|contents| format!("{:?}", contents))
681 .unwrap_or_else(|| {
682 format!(
683 "Verified checkpoint contents not found, digest: {:?}",
684 verified_checkpoint.content_digest,
685 )
686 })
687 })
688 .map_err(|e| {
689 format!(
690 "Failed to get verified checkpoint contents, digest: {:?} error: {:?}",
691 verified_checkpoint.content_digest, e
692 )
693 })
694 .unwrap_or_else(|err_msg| err_msg);
695
696 let local_contents = self
697 .get_checkpoint_contents(&local_checkpoint.content_digest)
698 .map(|opt_contents| {
699 opt_contents
700 .map(|contents| format!("{:?}", contents))
701 .unwrap_or_else(|| {
702 format!(
703 "Local checkpoint contents not found, digest: {:?}",
704 local_checkpoint.content_digest
705 )
706 })
707 })
708 .map_err(|e| {
709 format!(
710 "Failed to get local checkpoint contents, digest: {:?} error: {:?}",
711 local_checkpoint.content_digest, e
712 )
713 })
714 .unwrap_or_else(|err_msg| err_msg);
715
716 error!(
718 verified_checkpoint = ?verified_checkpoint.data(),
719 ?verified_contents,
720 ?local_checkpoint,
721 ?local_contents,
722 "Local checkpoint fork detected!",
723 );
724
725 if let Err(e) = self.record_checkpoint_fork_detected(
727 *local_checkpoint.sequence_number(),
728 local_checkpoint.digest(),
729 ) {
730 error!("Failed to record checkpoint fork in database: {:?}", e);
731 }
732
733 fail_point_arg!(
734 "kill_checkpoint_fork_node",
735 |checkpoint_overrides: std::sync::Arc<
736 std::sync::Mutex<std::collections::BTreeMap<u64, String>>,
737 >| {
738 #[cfg(msim)]
739 {
740 if let Ok(mut overrides) = checkpoint_overrides.lock() {
741 overrides.insert(
742 local_checkpoint.sequence_number,
743 verified_checkpoint.digest().to_string(),
744 );
745 }
746 tracing::error!(
747 fatal = true,
748 "Fork recovery test: killing node due to checkpoint fork for sequence number: {}, using verified digest: {}",
749 local_checkpoint.sequence_number(),
750 verified_checkpoint.digest()
751 );
752 sui_simulator::task::shutdown_current_node();
753 }
754 }
755 );
756
757 fatal!(
758 "Local checkpoint fork detected for sequence number: {}",
759 local_checkpoint.sequence_number()
760 );
761 }
762 }
763
764 pub fn insert_certified_checkpoint(
770 &self,
771 checkpoint: &VerifiedCheckpoint,
772 ) -> Result<(), TypedStoreError> {
773 debug!(
774 checkpoint_seq = checkpoint.sequence_number(),
775 "Inserting certified checkpoint",
776 );
777 let mut batch = self.tables.certified_checkpoints.batch();
778 batch
779 .insert_batch(
780 &self.tables.certified_checkpoints,
781 [(checkpoint.sequence_number(), checkpoint.serializable_ref())],
782 )?
783 .insert_batch(
784 &self.tables.checkpoint_by_digest,
785 [(checkpoint.digest(), checkpoint.serializable_ref())],
786 )?;
787 if checkpoint.next_epoch_committee().is_some() {
788 batch.insert_batch(
789 &self.tables.epoch_last_checkpoint_map,
790 [(&checkpoint.epoch(), checkpoint.sequence_number())],
791 )?;
792 }
793 batch.write()?;
794
795 if let Some(local_checkpoint) = self
796 .tables
797 .locally_computed_checkpoints
798 .get(checkpoint.sequence_number())?
799 {
800 self.check_for_checkpoint_fork(&local_checkpoint, checkpoint);
801 }
802
803 Ok(())
804 }
805
806 #[instrument(level = "debug", skip_all)]
809 pub fn insert_verified_checkpoint(
810 &self,
811 checkpoint: &VerifiedCheckpoint,
812 ) -> Result<(), TypedStoreError> {
813 self.insert_certified_checkpoint(checkpoint)?;
814 self.update_highest_verified_checkpoint(checkpoint)
815 }
816
817 pub fn update_highest_verified_checkpoint(
818 &self,
819 checkpoint: &VerifiedCheckpoint,
820 ) -> Result<(), TypedStoreError> {
821 if Some(*checkpoint.sequence_number())
822 > self
823 .get_highest_verified_checkpoint()?
824 .map(|x| *x.sequence_number())
825 {
826 debug!(
827 checkpoint_seq = checkpoint.sequence_number(),
828 "Updating highest verified checkpoint",
829 );
830 self.tables.watermarks.insert(
831 &CheckpointWatermark::HighestVerified,
832 &(*checkpoint.sequence_number(), *checkpoint.digest()),
833 )?;
834 }
835
836 Ok(())
837 }
838
839 pub fn update_highest_synced_checkpoint(
840 &self,
841 checkpoint: &VerifiedCheckpoint,
842 ) -> Result<(), TypedStoreError> {
843 let seq = *checkpoint.sequence_number();
844 debug!(checkpoint_seq = seq, "Updating highest synced checkpoint",);
845 self.tables.watermarks.insert(
846 &CheckpointWatermark::HighestSynced,
847 &(seq, *checkpoint.digest()),
848 )?;
849 self.synced_checkpoint_notify_read.notify(&seq, checkpoint);
850 Ok(())
851 }
852
853 async fn notify_read_checkpoint_watermark<F>(
854 &self,
855 notify_read: &NotifyRead<CheckpointSequenceNumber, VerifiedCheckpoint>,
856 seq: CheckpointSequenceNumber,
857 get_watermark: F,
858 ) -> VerifiedCheckpoint
859 where
860 F: Fn() -> Option<CheckpointSequenceNumber>,
861 {
862 notify_read
863 .read("notify_read_checkpoint_watermark", &[seq], |seqs| {
864 let seq = seqs[0];
865 let Some(highest) = get_watermark() else {
866 return vec![None];
867 };
868 if highest < seq {
869 return vec![None];
870 }
871 let checkpoint = self
872 .get_checkpoint_by_sequence_number(seq)
873 .expect("db error")
874 .expect("checkpoint not found");
875 vec![Some(checkpoint)]
876 })
877 .await
878 .into_iter()
879 .next()
880 .unwrap()
881 }
882
883 pub async fn notify_read_synced_checkpoint(
884 &self,
885 seq: CheckpointSequenceNumber,
886 ) -> VerifiedCheckpoint {
887 self.notify_read_checkpoint_watermark(&self.synced_checkpoint_notify_read, seq, || {
888 self.get_highest_synced_checkpoint_seq_number()
889 .expect("db error")
890 })
891 .await
892 }
893
894 pub async fn notify_read_executed_checkpoint(
895 &self,
896 seq: CheckpointSequenceNumber,
897 ) -> VerifiedCheckpoint {
898 self.notify_read_checkpoint_watermark(&self.executed_checkpoint_notify_read, seq, || {
899 self.get_highest_executed_checkpoint_seq_number()
900 .expect("db error")
901 })
902 .await
903 }
904
905 pub fn update_highest_executed_checkpoint(
906 &self,
907 checkpoint: &VerifiedCheckpoint,
908 ) -> Result<(), TypedStoreError> {
909 if let Some(seq_number) = self.get_highest_executed_checkpoint_seq_number()? {
910 if seq_number >= *checkpoint.sequence_number() {
911 return Ok(());
912 }
913 assert_eq!(
914 seq_number + 1,
915 *checkpoint.sequence_number(),
916 "Cannot update highest executed checkpoint to {} when current highest executed checkpoint is {}",
917 checkpoint.sequence_number(),
918 seq_number
919 );
920 }
921 let seq = *checkpoint.sequence_number();
922 debug!(checkpoint_seq = seq, "Updating highest executed checkpoint",);
923 self.tables.watermarks.insert(
924 &CheckpointWatermark::HighestExecuted,
925 &(seq, *checkpoint.digest()),
926 )?;
927 self.executed_checkpoint_notify_read
928 .notify(&seq, checkpoint);
929 Ok(())
930 }
931
932 pub fn update_highest_pruned_checkpoint(
933 &self,
934 checkpoint: &VerifiedCheckpoint,
935 ) -> Result<(), TypedStoreError> {
936 self.tables.watermarks.insert(
937 &CheckpointWatermark::HighestPruned,
938 &(*checkpoint.sequence_number(), *checkpoint.digest()),
939 )
940 }
941
942 pub fn set_highest_executed_checkpoint_subtle(
947 &self,
948 checkpoint: &VerifiedCheckpoint,
949 ) -> Result<(), TypedStoreError> {
950 self.tables.watermarks.insert(
951 &CheckpointWatermark::HighestExecuted,
952 &(*checkpoint.sequence_number(), *checkpoint.digest()),
953 )
954 }
955
956 pub fn insert_checkpoint_contents(
957 &self,
958 contents: CheckpointContents,
959 ) -> Result<(), TypedStoreError> {
960 debug!(
961 checkpoint_seq = ?contents.digest(),
962 "Inserting checkpoint contents",
963 );
964 self.tables
965 .checkpoint_content
966 .insert(contents.digest(), &contents)
967 }
968
969 pub fn insert_verified_checkpoint_contents(
970 &self,
971 checkpoint: &VerifiedCheckpoint,
972 full_contents: VerifiedCheckpointContents,
973 ) -> Result<(), TypedStoreError> {
974 let mut batch = self.tables.full_checkpoint_content_v2.batch();
975 batch.insert_batch(
976 &self.tables.checkpoint_sequence_by_contents_digest,
977 [(&checkpoint.content_digest, checkpoint.sequence_number())],
978 )?;
979 let full_contents = full_contents.into_inner();
980 batch.insert_batch(
981 &self.tables.full_checkpoint_content_v2,
982 [(checkpoint.sequence_number(), &full_contents)],
983 )?;
984
985 let contents = full_contents.into_checkpoint_contents();
986 assert_eq!(&checkpoint.content_digest, contents.digest());
987
988 batch.insert_batch(
989 &self.tables.checkpoint_content,
990 [(contents.digest(), &contents)],
991 )?;
992
993 batch.write()
994 }
995
996 pub fn delete_full_checkpoint_contents(
997 &self,
998 seq: CheckpointSequenceNumber,
999 ) -> Result<(), TypedStoreError> {
1000 self.tables.full_checkpoint_content.remove(&seq)?;
1001 self.tables.full_checkpoint_content_v2.remove(&seq)
1002 }
1003
1004 pub fn get_epoch_last_checkpoint(
1005 &self,
1006 epoch_id: EpochId,
1007 ) -> SuiResult<Option<VerifiedCheckpoint>> {
1008 let seq = self.get_epoch_last_checkpoint_seq_number(epoch_id)?;
1009 let checkpoint = match seq {
1010 Some(seq) => self.get_checkpoint_by_sequence_number(seq)?,
1011 None => None,
1012 };
1013 Ok(checkpoint)
1014 }
1015
1016 pub fn get_epoch_last_checkpoint_seq_number(
1017 &self,
1018 epoch_id: EpochId,
1019 ) -> SuiResult<Option<CheckpointSequenceNumber>> {
1020 let seq = self.tables.epoch_last_checkpoint_map.get(&epoch_id)?;
1021 Ok(seq)
1022 }
1023
1024 pub fn get_epoch_first_checkpoint_seq(
1027 &self,
1028 epoch: EpochId,
1029 ) -> SuiResult<Option<CheckpointSequenceNumber>> {
1030 if epoch == 0 {
1031 return Ok(Some(0));
1032 }
1033 Ok(self
1034 .tables
1035 .epoch_last_checkpoint_map
1036 .get(&(epoch - 1))?
1037 .map(|s| s + 1))
1038 }
1039
1040 pub fn list_checkpoints_from_seq(
1042 &self,
1043 start: Option<CheckpointSequenceNumber>,
1044 limit: usize,
1045 ) -> Result<Vec<(CheckpointSequenceNumber, VerifiedCheckpoint)>, TypedStoreError> {
1046 self.tables
1047 .certified_checkpoints
1048 .safe_iter_with_bounds(start, None)
1049 .take(limit)
1050 .map(|r| r.map(|(seq, cp)| (seq, cp.into())))
1051 .collect()
1052 }
1053
1054 pub fn list_epoch_last_checkpoints(
1056 &self,
1057 start: Option<EpochId>,
1058 limit: usize,
1059 ) -> Result<Vec<(EpochId, CheckpointSequenceNumber)>, TypedStoreError> {
1060 self.tables
1061 .epoch_last_checkpoint_map
1062 .safe_iter_with_bounds(start, None)
1063 .take(limit)
1064 .collect()
1065 }
1066
1067 pub fn list_checkpoint_digests(
1069 &self,
1070 start: Option<CheckpointDigest>,
1071 limit: usize,
1072 ) -> Result<Vec<CheckpointDigest>, TypedStoreError> {
1073 self.tables
1074 .checkpoint_by_digest
1075 .safe_iter_with_bounds(start, None)
1076 .take(limit)
1077 .map(|r| r.map(|(d, _)| d))
1078 .collect()
1079 }
1080
1081 pub fn list_checkpoint_contents_digests(
1083 &self,
1084 start: Option<CheckpointContentsDigest>,
1085 limit: usize,
1086 ) -> Result<Vec<CheckpointContentsDigest>, TypedStoreError> {
1087 self.tables
1088 .checkpoint_content
1089 .safe_iter_with_bounds(start, None)
1090 .take(limit)
1091 .map(|r| r.map(|(d, _)| d))
1092 .collect()
1093 }
1094
1095 pub fn list_epoch_checkpoints(
1097 &self,
1098 epoch: EpochId,
1099 start_seq: Option<CheckpointSequenceNumber>,
1100 limit: usize,
1101 ) -> Result<Vec<(CheckpointSequenceNumber, VerifiedCheckpoint)>, TypedStoreError> {
1102 let Some(last_seq) = self.tables.epoch_last_checkpoint_map.get(&epoch)? else {
1103 return Ok(vec![]);
1104 };
1105 let first_seq = if epoch == 0 {
1107 0
1108 } else {
1109 self.tables
1110 .epoch_last_checkpoint_map
1111 .get(&(epoch - 1))?
1112 .map(|s| s + 1)
1113 .unwrap_or(0)
1114 };
1115 let start = start_seq.map(|s| s.max(first_seq)).unwrap_or(first_seq);
1116 self.tables
1117 .certified_checkpoints
1118 .safe_iter_with_bounds(Some(start), Some(last_seq + 1))
1119 .take(limit)
1120 .map(|r| r.map(|(seq, cp)| (seq, cp.into())))
1121 .collect()
1122 }
1123
1124 pub fn insert_epoch_last_checkpoint(
1125 &self,
1126 epoch_id: EpochId,
1127 checkpoint: &VerifiedCheckpoint,
1128 ) -> SuiResult {
1129 self.tables
1130 .epoch_last_checkpoint_map
1131 .insert(&epoch_id, checkpoint.sequence_number())?;
1132 Ok(())
1133 }
1134
1135 pub fn get_epoch_state_commitments(
1136 &self,
1137 epoch: EpochId,
1138 ) -> SuiResult<Option<Vec<CheckpointCommitment>>> {
1139 let commitments = self.get_epoch_last_checkpoint(epoch)?.map(|checkpoint| {
1140 checkpoint
1141 .end_of_epoch_data
1142 .as_ref()
1143 .expect("Last checkpoint of epoch expected to have EndOfEpochData")
1144 .epoch_commitments
1145 .clone()
1146 });
1147 Ok(commitments)
1148 }
1149
1150 pub fn get_epoch_stats(
1152 &self,
1153 epoch: EpochId,
1154 last_checkpoint: &CheckpointSummary,
1155 ) -> Option<EpochStats> {
1156 let (first_checkpoint, prev_epoch_network_transactions) = if epoch == 0 {
1157 (0, 0)
1158 } else if let Ok(Some(checkpoint)) = self.get_epoch_last_checkpoint(epoch - 1) {
1159 (
1160 checkpoint.sequence_number + 1,
1161 checkpoint.network_total_transactions,
1162 )
1163 } else {
1164 return None;
1165 };
1166 Some(EpochStats {
1167 checkpoint_count: last_checkpoint.sequence_number - first_checkpoint + 1,
1168 transaction_count: last_checkpoint.network_total_transactions
1169 - prev_epoch_network_transactions,
1170 total_gas_reward: last_checkpoint
1171 .epoch_rolling_gas_cost_summary
1172 .computation_cost,
1173 })
1174 }
1175
1176 pub fn checkpoint_db(&self, path: &Path) -> SuiResult {
1177 self.tables
1179 .checkpoint_content
1180 .checkpoint_db(path)
1181 .map_err(Into::into)
1182 }
1183
1184 pub fn delete_highest_executed_checkpoint_test_only(&self) -> Result<(), TypedStoreError> {
1185 let mut wb = self.tables.watermarks.batch();
1186 wb.delete_batch(
1187 &self.tables.watermarks,
1188 std::iter::once(CheckpointWatermark::HighestExecuted),
1189 )?;
1190 wb.write()?;
1191 Ok(())
1192 }
1193
1194 pub fn reset_db_for_execution_since_genesis(&self) -> SuiResult {
1195 self.delete_highest_executed_checkpoint_test_only()?;
1196 Ok(())
1197 }
1198
1199 pub fn record_checkpoint_fork_detected(
1200 &self,
1201 checkpoint_seq: CheckpointSequenceNumber,
1202 checkpoint_digest: CheckpointDigest,
1203 ) -> Result<(), TypedStoreError> {
1204 info!(
1205 checkpoint_seq = checkpoint_seq,
1206 checkpoint_digest = ?checkpoint_digest,
1207 "Recording checkpoint fork detection in database"
1208 );
1209 self.tables.watermarks.insert(
1210 &CheckpointWatermark::CheckpointForkDetected,
1211 &(checkpoint_seq, checkpoint_digest),
1212 )
1213 }
1214
1215 pub fn get_checkpoint_fork_detected(
1216 &self,
1217 ) -> Result<Option<(CheckpointSequenceNumber, CheckpointDigest)>, TypedStoreError> {
1218 self.tables
1219 .watermarks
1220 .get(&CheckpointWatermark::CheckpointForkDetected)
1221 }
1222
1223 pub fn clear_checkpoint_fork_detected(&self) -> Result<(), TypedStoreError> {
1224 self.tables
1225 .watermarks
1226 .remove(&CheckpointWatermark::CheckpointForkDetected)
1227 }
1228
1229 pub fn record_transaction_fork_detected(
1230 &self,
1231 tx_digest: TransactionDigest,
1232 expected_effects_digest: TransactionEffectsDigest,
1233 actual_effects_digest: TransactionEffectsDigest,
1234 ) -> Result<(), TypedStoreError> {
1235 info!(
1236 tx_digest = ?tx_digest,
1237 expected_effects_digest = ?expected_effects_digest,
1238 actual_effects_digest = ?actual_effects_digest,
1239 "Recording transaction fork detection in database"
1240 );
1241 self.tables.transaction_fork_detected.insert(
1242 &TRANSACTION_FORK_DETECTED_KEY,
1243 &(tx_digest, expected_effects_digest, actual_effects_digest),
1244 )
1245 }
1246
1247 pub fn get_transaction_fork_detected(
1248 &self,
1249 ) -> Result<
1250 Option<(
1251 TransactionDigest,
1252 TransactionEffectsDigest,
1253 TransactionEffectsDigest,
1254 )>,
1255 TypedStoreError,
1256 > {
1257 self.tables
1258 .transaction_fork_detected
1259 .get(&TRANSACTION_FORK_DETECTED_KEY)
1260 }
1261
1262 pub fn clear_transaction_fork_detected(&self) -> Result<(), TypedStoreError> {
1263 self.tables
1264 .transaction_fork_detected
1265 .remove(&TRANSACTION_FORK_DETECTED_KEY)
1266 }
1267}
1268
1269#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
1270pub enum CheckpointWatermark {
1271 HighestVerified,
1272 HighestSynced,
1273 HighestExecuted,
1274 HighestPruned,
1275 CheckpointForkDetected,
1276}
1277
1278struct CheckpointStateHasher {
1279 epoch_store: Arc<AuthorityPerEpochStore>,
1280 hasher: Weak<GlobalStateHasher>,
1281 receive_from_builder: mpsc::Receiver<(CheckpointSequenceNumber, Vec<TransactionEffects>)>,
1282}
1283
1284impl CheckpointStateHasher {
1285 fn new(
1286 epoch_store: Arc<AuthorityPerEpochStore>,
1287 hasher: Weak<GlobalStateHasher>,
1288 receive_from_builder: mpsc::Receiver<(CheckpointSequenceNumber, Vec<TransactionEffects>)>,
1289 ) -> Self {
1290 Self {
1291 epoch_store,
1292 hasher,
1293 receive_from_builder,
1294 }
1295 }
1296
1297 async fn run(self) {
1298 let Self {
1299 epoch_store,
1300 hasher,
1301 mut receive_from_builder,
1302 } = self;
1303 while let Some((seq, effects)) = receive_from_builder.recv().await {
1304 let Some(hasher) = hasher.upgrade() else {
1305 info!("Object state hasher was dropped, stopping checkpoint accumulation");
1306 break;
1307 };
1308 hasher
1309 .accumulate_checkpoint(&effects, seq, &epoch_store)
1310 .expect("epoch ended while accumulating checkpoint");
1311 }
1312 }
1313}
1314
1315#[derive(Debug)]
1316pub enum CheckpointBuilderError {
1317 ChangeEpochTxAlreadyExecuted,
1318 SystemPackagesMissing,
1319 Retry(anyhow::Error),
1320}
1321
1322impl<SuiError: std::error::Error + Send + Sync + 'static> From<SuiError>
1323 for CheckpointBuilderError
1324{
1325 fn from(e: SuiError) -> Self {
1326 Self::Retry(e.into())
1327 }
1328}
1329
1330pub type CheckpointBuilderResult<T = ()> = Result<T, CheckpointBuilderError>;
1331
1332pub struct CheckpointBuilder {
1333 state: Arc<AuthorityState>,
1334 store: Arc<CheckpointStore>,
1335 epoch_store: Arc<AuthorityPerEpochStore>,
1336 notify: Arc<Notify>,
1337 notify_aggregator: Arc<Notify>,
1338 last_built: watch::Sender<CheckpointSequenceNumber>,
1339 effects_store: Arc<dyn TransactionCacheRead>,
1340 global_state_hasher: Weak<GlobalStateHasher>,
1341 send_to_hasher: mpsc::Sender<(CheckpointSequenceNumber, Vec<TransactionEffects>)>,
1342 output: Box<dyn CheckpointOutput>,
1343 metrics: Arc<CheckpointMetrics>,
1344}
1345
1346pub struct CheckpointAggregator {
1347 store: Arc<CheckpointStore>,
1348 epoch_store: Arc<AuthorityPerEpochStore>,
1349 notify: Arc<Notify>,
1350 receiver: mpsc::UnboundedReceiver<CheckpointSignatureMessage>,
1351 pending: BTreeMap<CheckpointSequenceNumber, Vec<CheckpointSignatureMessage>>,
1352 current: Option<CheckpointSignatureAggregator>,
1353 output: Box<dyn CertifiedCheckpointOutput>,
1354 state: Arc<AuthorityState>,
1355 metrics: Arc<CheckpointMetrics>,
1356}
1357
1358pub struct CheckpointSignatureAggregator {
1360 summary: CheckpointSummary,
1361 digest: CheckpointDigest,
1362 signatures_by_digest: MultiStakeAggregator<CheckpointDigest, CheckpointSummary, true>,
1364 store: Arc<CheckpointStore>,
1365 state: Arc<AuthorityState>,
1366 metrics: Arc<CheckpointMetrics>,
1367}
1368
1369impl CheckpointBuilder {
1370 fn new(
1371 state: Arc<AuthorityState>,
1372 store: Arc<CheckpointStore>,
1373 epoch_store: Arc<AuthorityPerEpochStore>,
1374 notify: Arc<Notify>,
1375 effects_store: Arc<dyn TransactionCacheRead>,
1376 global_state_hasher: Weak<GlobalStateHasher>,
1378 send_to_hasher: mpsc::Sender<(CheckpointSequenceNumber, Vec<TransactionEffects>)>,
1380 output: Box<dyn CheckpointOutput>,
1381 notify_aggregator: Arc<Notify>,
1382 last_built: watch::Sender<CheckpointSequenceNumber>,
1383 metrics: Arc<CheckpointMetrics>,
1384 ) -> Self {
1385 Self {
1386 state,
1387 store,
1388 epoch_store,
1389 notify,
1390 effects_store,
1391 global_state_hasher,
1392 send_to_hasher,
1393 output,
1394 notify_aggregator,
1395 last_built,
1396 metrics,
1397 }
1398 }
1399
1400 async fn run(mut self, consensus_replay_waiter: Option<ReplayWaiter>) {
1408 if let Some(replay_waiter) = consensus_replay_waiter {
1409 info!("Waiting for consensus commits to replay ...");
1410 replay_waiter.wait_for_replay().await;
1411 info!("Consensus commits finished replaying");
1412 }
1413 info!("Starting CheckpointBuilder");
1414 loop {
1415 match self.maybe_build_checkpoints().await {
1416 Ok(()) => {}
1417 err @ Err(
1418 CheckpointBuilderError::ChangeEpochTxAlreadyExecuted
1419 | CheckpointBuilderError::SystemPackagesMissing,
1420 ) => {
1421 info!("CheckpointBuilder stopping: {:?}", err);
1422 return;
1423 }
1424 Err(CheckpointBuilderError::Retry(inner)) => {
1425 let msg = format!("{:?}", inner);
1426 debug_fatal!("Error while making checkpoint, will retry in 1s: {}", msg);
1427 tokio::time::sleep(Duration::from_secs(1)).await;
1428 self.metrics.checkpoint_errors.inc();
1429 continue;
1430 }
1431 }
1432
1433 self.notify.notified().await;
1434 }
1435 }
1436
1437 async fn maybe_build_checkpoints(&mut self) -> CheckpointBuilderResult {
1438 let _scope = monitored_scope("BuildCheckpoints");
1439
1440 let last_height = self
1442 .epoch_store
1443 .last_built_checkpoint_builder_summary()
1444 .expect("epoch should not have ended")
1445 .and_then(|s| s.checkpoint_height);
1446
1447 for (height, pending) in self
1448 .epoch_store
1449 .get_pending_checkpoints(last_height)
1450 .expect("unexpected epoch store error")
1451 {
1452 debug!(checkpoint_commit_height = height, "Making checkpoint");
1453
1454 let seq = self.make_checkpoint(pending).await?;
1455
1456 self.last_built.send_if_modified(|cur| {
1457 if seq > *cur {
1459 *cur = seq;
1460 true
1461 } else {
1462 false
1463 }
1464 });
1465
1466 tokio::task::yield_now().await;
1469 }
1470
1471 Ok(())
1472 }
1473
1474 #[instrument(level = "debug", skip_all, fields(height = pending.details.checkpoint_height))]
1475 async fn make_checkpoint(
1476 &mut self,
1477 pending: PendingCheckpoint,
1478 ) -> CheckpointBuilderResult<CheckpointSequenceNumber> {
1479 let _scope = monitored_scope("CheckpointBuilder::make_checkpoint");
1480
1481 let details = pending.details.clone();
1482
1483 let highest_executed_sequence = self
1484 .store
1485 .get_highest_executed_checkpoint_seq_number()
1486 .expect("db error")
1487 .unwrap_or(0);
1488
1489 let (poll_count, result) = poll_count(self.resolve_checkpoint_transactions(pending)).await;
1490 let (sorted_tx_effects_included_in_checkpoint, all_roots) = result?;
1491
1492 let new_checkpoint = self
1493 .create_checkpoint(
1494 sorted_tx_effects_included_in_checkpoint,
1495 &details,
1496 &all_roots,
1497 )
1498 .await?;
1499 let sequence = *new_checkpoint.0.sequence_number();
1500 let digest = new_checkpoint.0.digest();
1501 if sequence <= highest_executed_sequence && poll_count > 1 {
1502 debug_fatal!(
1503 "resolve_checkpoint_transactions should be instantaneous when executed checkpoint is ahead of checkpoint builder"
1504 );
1505 }
1506
1507 self.write_checkpoint(details.checkpoint_height, new_checkpoint)
1508 .await?;
1509 info!(
1510 seq = sequence,
1511 %digest,
1512 height = details.checkpoint_height,
1513 commit = %details.consensus_commit_ref,
1514 "Made new checkpoint"
1515 );
1516
1517 Ok(sequence)
1518 }
1519
1520 #[instrument(level = "debug", skip_all)]
1523 async fn resolve_checkpoint_transactions(
1524 &self,
1525 pending: PendingCheckpoint,
1526 ) -> SuiResult<(Vec<TransactionEffects>, HashSet<TransactionDigest>)> {
1527 let _scope = monitored_scope("CheckpointBuilder::resolve_checkpoint_transactions");
1528
1529 debug!(
1530 checkpoint_commit_height = pending.details.checkpoint_height,
1531 "Resolving checkpoint transactions for pending checkpoint.",
1532 );
1533
1534 trace!(
1535 "roots for pending checkpoint {:?}: {:?}",
1536 pending.details.checkpoint_height, pending.roots,
1537 );
1538
1539 assert!(
1540 self.epoch_store
1541 .protocol_config()
1542 .prepend_prologue_tx_in_consensus_commit_in_checkpoints()
1543 );
1544
1545 let mut all_effects: Vec<TransactionEffects> = Vec::new();
1546 let mut all_root_digests: Vec<TransactionDigest> = Vec::new();
1547
1548 for checkpoint_roots in &pending.roots {
1549 let tx_roots = &checkpoint_roots.tx_roots;
1550
1551 self.metrics
1552 .checkpoint_roots_count
1553 .inc_by(tx_roots.len() as u64);
1554
1555 let root_digests = self
1556 .epoch_store
1557 .notify_read_tx_key_to_digest(tx_roots)
1558 .in_monitored_scope("CheckpointNotifyDigests")
1559 .await?;
1560
1561 all_root_digests.extend(root_digests.iter().cloned());
1562
1563 let root_effects = self
1564 .effects_store
1565 .notify_read_executed_effects(
1566 CHECKPOINT_BUILDER_NOTIFY_READ_TASK_NAME,
1567 &root_digests,
1568 )
1569 .in_monitored_scope("CheckpointNotifyRead")
1570 .await;
1571 let consensus_commit_prologue =
1572 self.extract_consensus_commit_prologue(&root_digests, &root_effects)?;
1573
1574 let _scope = monitored_scope("CheckpointBuilder::causal_sort");
1575 let ccp_digest = consensus_commit_prologue.map(|(d, _)| d);
1576 let mut sorted = CausalOrder::causal_sort_with_ccp(root_effects, ccp_digest);
1577
1578 if let Some(settlement_key) = &checkpoint_roots.settlement_root {
1579 let checkpoint_seq = pending.details.checkpoint_seq;
1580 let tx_index_offset = all_effects.len() as u64;
1581 let effects = self
1582 .resolve_settlement_effects(
1583 *settlement_key,
1584 &sorted,
1585 checkpoint_roots.height,
1586 checkpoint_seq,
1587 tx_index_offset,
1588 )
1589 .await;
1590 sorted.extend(effects);
1591 }
1592
1593 #[cfg(msim)]
1594 {
1595 self.expensive_consensus_commit_prologue_invariants_check(&root_digests, &sorted);
1596 }
1597
1598 all_effects.extend(sorted);
1599 }
1600 Ok((all_effects, all_root_digests.into_iter().collect()))
1601 }
1602
1603 async fn resolve_settlement_effects(
1608 &self,
1609 settlement_key: TransactionKey,
1610 sorted_root_effects: &[TransactionEffects],
1611 checkpoint_height: CheckpointHeight,
1612 checkpoint_seq: CheckpointSequenceNumber,
1613 tx_index_offset: u64,
1614 ) -> Vec<TransactionEffects> {
1615 let epoch = self.epoch_store.epoch();
1616 let accumulator_root_obj_initial_shared_version = self
1617 .epoch_store
1618 .epoch_start_config()
1619 .accumulator_root_obj_initial_shared_version()
1620 .expect("accumulator root object must exist");
1621
1622 let builder = AccumulatorSettlementTxBuilder::new(
1623 None,
1624 sorted_root_effects,
1625 checkpoint_seq,
1626 tx_index_offset,
1627 );
1628
1629 let settlement_digests: Vec<_> = builder
1630 .build_tx(
1631 self.epoch_store.protocol_config(),
1632 epoch,
1633 accumulator_root_obj_initial_shared_version,
1634 checkpoint_height,
1635 checkpoint_seq,
1636 )
1637 .into_iter()
1638 .map(|tx| *VerifiedTransaction::new_system_transaction(tx).digest())
1639 .collect();
1640
1641 debug!(
1642 ?settlement_digests,
1643 ?settlement_key,
1644 "reading settlement effects from cache"
1645 );
1646
1647 let settlement_effects = wait_for_effects_with_retry(
1648 self.effects_store.as_ref(),
1649 "CheckpointBuilder::settlement_effects",
1650 &settlement_digests,
1651 settlement_key,
1652 )
1653 .await;
1654 let (accounts_created, accounts_deleted) =
1655 accumulators::count_accumulator_object_changes(&settlement_effects);
1656 self.metrics
1657 .report_accumulator_account_changes(accounts_created, accounts_deleted);
1658
1659 let barrier_digest = *VerifiedTransaction::new_system_transaction(
1660 accumulators::build_accumulator_barrier_tx(
1661 epoch,
1662 accumulator_root_obj_initial_shared_version,
1663 checkpoint_height,
1664 &settlement_effects,
1665 ),
1666 )
1667 .digest();
1668
1669 let barrier_effects = wait_for_effects_with_retry(
1670 self.effects_store.as_ref(),
1671 "CheckpointBuilder::barrier_effects",
1672 &[barrier_digest],
1673 settlement_key,
1674 )
1675 .await;
1676
1677 for fx in settlement_effects.iter().chain(barrier_effects.iter()) {
1683 assert!(
1684 fx.status().is_ok(),
1685 "settlement transaction cannot fail (digest: {:?}) {:#?}",
1686 fx.transaction_digest(),
1687 fx
1688 );
1689 }
1690
1691 settlement_effects
1692 .into_iter()
1693 .chain(barrier_effects)
1694 .collect()
1695 }
1696
1697 fn extract_consensus_commit_prologue(
1700 &self,
1701 root_digests: &[TransactionDigest],
1702 root_effects: &[TransactionEffects],
1703 ) -> SuiResult<Option<(TransactionDigest, TransactionEffects)>> {
1704 let _scope = monitored_scope("CheckpointBuilder::extract_consensus_commit_prologue");
1705 if root_digests.is_empty() {
1706 return Ok(None);
1707 }
1708
1709 let first_tx = self
1713 .state
1714 .get_transaction_cache_reader()
1715 .get_transaction_block(&root_digests[0])
1716 .expect("Transaction block must exist");
1717
1718 Ok(first_tx
1719 .transaction_data()
1720 .is_consensus_commit_prologue()
1721 .then(|| {
1722 assert_eq!(first_tx.digest(), root_effects[0].transaction_digest());
1723 (*first_tx.digest(), root_effects[0].clone())
1724 }))
1725 }
1726
1727 #[instrument(level = "debug", skip_all)]
1728 async fn write_checkpoint(
1729 &mut self,
1730 height: CheckpointHeight,
1731 new_checkpoint: (CheckpointSummary, CheckpointContents),
1732 ) -> SuiResult {
1733 let _scope = monitored_scope("CheckpointBuilder::write_checkpoint");
1734 let mut batch = self.store.tables.checkpoint_content.batch();
1735
1736 let (summary, contents) = &new_checkpoint;
1737 debug!(
1738 checkpoint_commit_height = height,
1739 checkpoint_seq = summary.sequence_number,
1740 contents_digest = ?contents.digest(),
1741 "writing checkpoint",
1742 );
1743
1744 if let Some(previously_computed_summary) = self
1745 .store
1746 .tables
1747 .locally_computed_checkpoints
1748 .get(&summary.sequence_number)?
1749 && previously_computed_summary.digest() != summary.digest()
1750 {
1751 fatal!(
1752 "Checkpoint {} was previously built with a different result: previously_computed_summary {:?} vs current_summary {:?}",
1753 summary.sequence_number,
1754 previously_computed_summary.digest(),
1755 summary.digest()
1756 );
1757 }
1758
1759 self.metrics
1760 .transactions_included_in_checkpoint
1761 .inc_by(contents.size() as u64);
1762 let sequence_number = summary.sequence_number;
1763 self.metrics
1764 .last_constructed_checkpoint
1765 .set(sequence_number as i64);
1766
1767 batch.insert_batch(
1768 &self.store.tables.checkpoint_content,
1769 [(contents.digest(), contents)],
1770 )?;
1771
1772 batch.insert_batch(
1773 &self.store.tables.locally_computed_checkpoints,
1774 [(sequence_number, summary)],
1775 )?;
1776
1777 batch.write()?;
1778
1779 self.output
1781 .checkpoint_created(summary, contents, &self.epoch_store, &self.store)
1782 .await?;
1783
1784 if let Some(certified_checkpoint) = self
1785 .store
1786 .tables
1787 .certified_checkpoints
1788 .get(summary.sequence_number())?
1789 {
1790 self.store
1791 .check_for_checkpoint_fork(summary, &certified_checkpoint.into());
1792 }
1793
1794 self.notify_aggregator.notify_one();
1795 self.epoch_store
1796 .process_constructed_checkpoint(height, new_checkpoint.0);
1797 Ok(())
1798 }
1799
1800 fn load_last_built_checkpoint_summary(
1801 epoch_store: &AuthorityPerEpochStore,
1802 store: &CheckpointStore,
1803 ) -> SuiResult<Option<(CheckpointSequenceNumber, CheckpointSummary)>> {
1804 let mut last_checkpoint = epoch_store.last_built_checkpoint_summary()?;
1805 if last_checkpoint.is_none() {
1806 let epoch = epoch_store.epoch();
1807 if epoch > 0 {
1808 let previous_epoch = epoch - 1;
1809 let last_verified = store.get_epoch_last_checkpoint(previous_epoch)?;
1810 last_checkpoint = last_verified.map(VerifiedCheckpoint::into_summary_and_sequence);
1811 if let Some((ref seq, _)) = last_checkpoint {
1812 debug!(
1813 "No checkpoints in builder DB, taking checkpoint from previous epoch with sequence {seq}"
1814 );
1815 } else {
1816 panic!("Can not find last checkpoint for previous epoch {previous_epoch}");
1818 }
1819 }
1820 }
1821 Ok(last_checkpoint)
1822 }
1823
1824 #[instrument(level = "debug", skip_all)]
1825 async fn create_checkpoint(
1826 &self,
1827 all_effects: Vec<TransactionEffects>,
1828 details: &PendingCheckpointInfo,
1829 all_roots: &HashSet<TransactionDigest>,
1830 ) -> CheckpointBuilderResult<(CheckpointSummary, CheckpointContents)> {
1831 let _scope = monitored_scope("CheckpointBuilder::create_checkpoint");
1832
1833 let last_checkpoint =
1834 Self::load_last_built_checkpoint_summary(&self.epoch_store, &self.store)?;
1835 let last_checkpoint_seq = last_checkpoint.as_ref().map(|(seq, _)| *seq);
1836 debug!(
1837 checkpoint_commit_height = details.checkpoint_height,
1838 next_checkpoint_seq = last_checkpoint_seq.unwrap_or_default() + 1,
1839 checkpoint_timestamp = details.timestamp_ms,
1840 "Creating checkpoint for {} transactions",
1841 all_effects.len(),
1842 );
1843
1844 let all_digests: Vec<_> = all_effects
1845 .iter()
1846 .map(|effect| *effect.transaction_digest())
1847 .collect();
1848 let transaction_blocks = self
1849 .state
1850 .get_transaction_cache_reader()
1851 .multi_get_transaction_blocks(&all_digests);
1852 let mut transactions = Vec::with_capacity(all_effects.len());
1853 let mut transaction_keys = Vec::with_capacity(all_effects.len());
1854 let mut randomness_rounds = BTreeMap::new();
1855 {
1856 let _guard = monitored_scope("CheckpointBuilder::wait_for_transactions_sequenced");
1857 debug!(
1858 ?last_checkpoint_seq,
1859 "Waiting for {:?} certificates to appear in consensus",
1860 all_effects.len()
1861 );
1862
1863 for (effects, transaction) in all_effects
1864 .iter()
1865 .zip_debug_eq(transaction_blocks.into_iter())
1866 {
1867 let transaction = transaction
1868 .unwrap_or_else(|| panic!("Could not find executed transaction {:?}", effects));
1869 match transaction.inner().transaction_data().kind() {
1870 TransactionKind::ConsensusCommitPrologue(_)
1871 | TransactionKind::ConsensusCommitPrologueV2(_)
1872 | TransactionKind::ConsensusCommitPrologueV3(_)
1873 | TransactionKind::ConsensusCommitPrologueV4(_)
1874 | TransactionKind::AuthenticatorStateUpdate(_) => {
1875 }
1878 TransactionKind::ProgrammableSystemTransaction(_) => {
1879 }
1881 TransactionKind::ChangeEpoch(_)
1882 | TransactionKind::Genesis(_)
1883 | TransactionKind::EndOfEpochTransaction(_) => {
1884 fatal!(
1885 "unexpected transaction in checkpoint effects: {:?}",
1886 transaction
1887 );
1888 }
1889 TransactionKind::RandomnessStateUpdate(rsu) => {
1890 randomness_rounds
1891 .insert(*effects.transaction_digest(), rsu.randomness_round);
1892 }
1893 TransactionKind::ProgrammableTransaction(_) => {
1894 let digest = *effects.transaction_digest();
1898 if !all_roots.contains(&digest) {
1899 transaction_keys.push(SequencedConsensusTransactionKey::External(
1900 ConsensusTransactionKey::Certificate(digest),
1901 ));
1902 }
1903 }
1904 }
1905 transactions.push((*transaction).clone());
1906 }
1907
1908 self.epoch_store
1909 .consensus_messages_processed_notify(transaction_keys)
1910 .await?;
1911 }
1912
1913 let signatures = self
1914 .epoch_store
1915 .user_signatures_for_checkpoint(&transactions, &all_digests);
1916 debug!(
1917 ?last_checkpoint_seq,
1918 "Received {} checkpoint user signatures from consensus",
1919 signatures.len()
1920 );
1921
1922 let end_of_epoch_observation_keys: Option<Vec<_>> = if details.last_of_epoch {
1923 Some(
1924 transactions
1925 .iter()
1926 .flat_map(|tx| {
1927 if let TransactionKind::ProgrammableTransaction(ptb) =
1928 tx.transaction_data().kind()
1929 {
1930 itertools::Either::Left(
1931 ptb.commands
1932 .iter()
1933 .map(ExecutionTimeObservationKey::from_command),
1934 )
1935 } else {
1936 itertools::Either::Right(std::iter::empty())
1937 }
1938 })
1939 .collect(),
1940 )
1941 } else {
1942 None
1943 };
1944
1945 let epoch = self.epoch_store.epoch();
1946 let first_checkpoint_of_epoch = last_checkpoint
1947 .as_ref()
1948 .map(|(_, c)| c.epoch != epoch)
1949 .unwrap_or(true);
1950 if first_checkpoint_of_epoch {
1951 self.epoch_store
1952 .record_epoch_first_checkpoint_creation_time_metric();
1953 }
1954 let last_checkpoint_of_epoch = details.last_of_epoch;
1955
1956 let sequence_number = details.checkpoint_seq;
1957 let mut timestamp_ms = details.timestamp_ms;
1958 if let Some((_, last_checkpoint)) = &last_checkpoint
1959 && last_checkpoint.timestamp_ms > timestamp_ms
1960 {
1961 debug!(
1963 "Decrease of checkpoint timestamp, possibly due to epoch change. Sequence: {}, previous: {}, current: {}",
1964 sequence_number, last_checkpoint.timestamp_ms, timestamp_ms,
1965 );
1966 if self
1967 .epoch_store
1968 .protocol_config()
1969 .enforce_checkpoint_timestamp_monotonicity()
1970 {
1971 timestamp_ms = last_checkpoint.timestamp_ms;
1972 }
1973 }
1974
1975 let mut effects = all_effects;
1976 let mut signatures = signatures;
1977 let epoch_rolling_gas_cost_summary =
1978 self.get_epoch_total_gas_cost(last_checkpoint.as_ref().map(|(_, c)| c), &effects);
1979
1980 let end_of_epoch_data = if last_checkpoint_of_epoch {
1981 let system_state_obj = self
1982 .augment_epoch_last_checkpoint(
1983 &epoch_rolling_gas_cost_summary,
1984 timestamp_ms,
1985 &mut effects,
1986 &mut signatures,
1987 sequence_number,
1988 end_of_epoch_observation_keys.expect(
1989 "end_of_epoch_observation_keys must be populated for the last checkpoint",
1990 ),
1991 last_checkpoint_seq.unwrap_or_default(),
1992 )
1993 .await?;
1994
1995 let committee = system_state_obj
1996 .get_current_epoch_committee()
1997 .committee()
1998 .clone();
1999
2000 let root_state_digest = {
2003 let state_acc = self
2004 .global_state_hasher
2005 .upgrade()
2006 .expect("No checkpoints should be getting built after local configuration");
2007 let acc = state_acc.accumulate_checkpoint(
2008 &effects,
2009 sequence_number,
2010 &self.epoch_store,
2011 )?;
2012
2013 state_acc
2014 .wait_for_previous_running_root(&self.epoch_store, sequence_number)
2015 .await?;
2016
2017 state_acc.accumulate_running_root(&self.epoch_store, sequence_number, Some(acc))?;
2018 state_acc
2019 .digest_epoch(self.epoch_store.clone(), sequence_number)
2020 .await?
2021 };
2022 self.metrics.highest_accumulated_epoch.set(epoch as i64);
2023 info!("Epoch {epoch} root state hash digest: {root_state_digest:?}");
2024
2025 let epoch_commitments = if self
2026 .epoch_store
2027 .protocol_config()
2028 .commit_root_state_digest()
2029 {
2030 vec![root_state_digest.into()]
2031 } else {
2032 vec![]
2033 };
2034
2035 Some(EndOfEpochData {
2036 next_epoch_committee: committee.voting_rights,
2037 next_epoch_protocol_version: ProtocolVersion::new(
2038 system_state_obj.protocol_version(),
2039 ),
2040 epoch_commitments,
2041 })
2042 } else {
2043 self.send_to_hasher
2044 .send((sequence_number, effects.clone()))
2045 .await?;
2046
2047 None
2048 };
2049 let contents = if self.epoch_store.protocol_config().address_aliases() {
2050 CheckpointContents::new_v2(&effects, signatures)
2051 } else {
2052 CheckpointContents::new_with_digests_and_signatures(
2053 effects.iter().map(TransactionEffects::execution_digests),
2054 signatures
2055 .into_iter()
2056 .map(|sigs| sigs.into_iter().map(|(s, _)| s).collect())
2057 .collect(),
2058 )
2059 };
2060
2061 let num_txns = contents.size() as u64;
2062
2063 let network_total_transactions = last_checkpoint
2064 .as_ref()
2065 .map(|(_, c)| c.network_total_transactions + num_txns)
2066 .unwrap_or(num_txns);
2067
2068 let previous_digest = last_checkpoint.as_ref().map(|(_, c)| c.digest());
2069
2070 let matching_randomness_rounds: Vec<_> = effects
2071 .iter()
2072 .filter_map(|e| randomness_rounds.get(e.transaction_digest()))
2073 .copied()
2074 .collect();
2075
2076 let checkpoint_commitments = if self
2077 .epoch_store
2078 .protocol_config()
2079 .include_checkpoint_artifacts_digest_in_summary()
2080 {
2081 let artifacts = CheckpointArtifacts::from(&effects[..]);
2082 let artifacts_digest = artifacts.digest()?;
2083 vec![artifacts_digest.into()]
2084 } else {
2085 Default::default()
2086 };
2087
2088 let summary = CheckpointSummary::new(
2089 self.epoch_store.protocol_config(),
2090 epoch,
2091 sequence_number,
2092 network_total_transactions,
2093 &contents,
2094 previous_digest,
2095 epoch_rolling_gas_cost_summary,
2096 end_of_epoch_data,
2097 timestamp_ms,
2098 matching_randomness_rounds,
2099 checkpoint_commitments,
2100 );
2101 summary.report_checkpoint_age(
2102 &self.metrics.last_created_checkpoint_age,
2103 &self.metrics.last_created_checkpoint_age_ms,
2104 );
2105 if last_checkpoint_of_epoch {
2106 info!(
2107 checkpoint_seq = sequence_number,
2108 "creating last checkpoint of epoch {}", epoch
2109 );
2110 if let Some(stats) = self.store.get_epoch_stats(epoch, &summary) {
2111 self.epoch_store
2112 .report_epoch_metrics_at_last_checkpoint(stats);
2113 }
2114 }
2115
2116 Ok((summary, contents))
2117 }
2118
2119 fn get_epoch_total_gas_cost(
2120 &self,
2121 last_checkpoint: Option<&CheckpointSummary>,
2122 cur_checkpoint_effects: &[TransactionEffects],
2123 ) -> GasCostSummary {
2124 let (previous_epoch, previous_gas_costs) = last_checkpoint
2125 .map(|c| (c.epoch, c.epoch_rolling_gas_cost_summary.clone()))
2126 .unwrap_or_default();
2127 let current_gas_costs = GasCostSummary::new_from_txn_effects(cur_checkpoint_effects.iter());
2128 if previous_epoch == self.epoch_store.epoch() {
2129 GasCostSummary::new(
2131 previous_gas_costs.computation_cost + current_gas_costs.computation_cost,
2132 previous_gas_costs.storage_cost + current_gas_costs.storage_cost,
2133 previous_gas_costs.storage_rebate + current_gas_costs.storage_rebate,
2134 previous_gas_costs.non_refundable_storage_fee
2135 + current_gas_costs.non_refundable_storage_fee,
2136 )
2137 } else {
2138 current_gas_costs
2139 }
2140 }
2141
2142 #[instrument(level = "error", skip_all)]
2143 async fn augment_epoch_last_checkpoint(
2144 &self,
2145 epoch_total_gas_cost: &GasCostSummary,
2146 epoch_start_timestamp_ms: CheckpointTimestamp,
2147 checkpoint_effects: &mut Vec<TransactionEffects>,
2148 signatures: &mut Vec<Vec<(GenericSignature, Option<SequenceNumber>)>>,
2149 checkpoint: CheckpointSequenceNumber,
2150 end_of_epoch_observation_keys: Vec<ExecutionTimeObservationKey>,
2151 last_checkpoint: CheckpointSequenceNumber,
2154 ) -> CheckpointBuilderResult<SuiSystemState> {
2155 let (system_state, effects) = self
2156 .state
2157 .create_and_execute_advance_epoch_tx(
2158 &self.epoch_store,
2159 epoch_total_gas_cost,
2160 checkpoint,
2161 epoch_start_timestamp_ms,
2162 end_of_epoch_observation_keys,
2163 last_checkpoint,
2164 )
2165 .await?;
2166 checkpoint_effects.push(effects);
2167 signatures.push(vec![]);
2168 Ok(system_state)
2169 }
2170
2171 #[cfg(msim)]
2174 fn expensive_consensus_commit_prologue_invariants_check(
2175 &self,
2176 root_digests: &[TransactionDigest],
2177 sorted: &[TransactionEffects],
2178 ) {
2179 let root_txs = self
2181 .state
2182 .get_transaction_cache_reader()
2183 .multi_get_transaction_blocks(root_digests);
2184 let ccps = root_txs
2185 .iter()
2186 .filter_map(|tx| {
2187 if let Some(tx) = tx {
2188 if tx.transaction_data().is_consensus_commit_prologue() {
2189 Some(tx)
2190 } else {
2191 None
2192 }
2193 } else {
2194 None
2195 }
2196 })
2197 .collect::<Vec<_>>();
2198
2199 assert!(ccps.len() <= 1);
2201
2202 let txs = self
2204 .state
2205 .get_transaction_cache_reader()
2206 .multi_get_transaction_blocks(
2207 &sorted
2208 .iter()
2209 .map(|tx| tx.transaction_digest().clone())
2210 .collect::<Vec<_>>(),
2211 );
2212
2213 if ccps.len() == 0 {
2214 for tx in txs.iter() {
2217 if let Some(tx) = tx {
2218 assert!(!tx.transaction_data().is_consensus_commit_prologue());
2219 }
2220 }
2221 } else {
2222 assert!(
2224 txs[0]
2225 .as_ref()
2226 .unwrap()
2227 .transaction_data()
2228 .is_consensus_commit_prologue()
2229 );
2230
2231 assert_eq!(ccps[0].digest(), txs[0].as_ref().unwrap().digest());
2232
2233 for tx in txs.iter().skip(1) {
2234 if let Some(tx) = tx {
2235 assert!(!tx.transaction_data().is_consensus_commit_prologue());
2236 }
2237 }
2238 }
2239 }
2240}
2241
2242async fn wait_for_effects_with_retry(
2243 effects_store: &dyn TransactionCacheRead,
2244 task_name: &'static str,
2245 digests: &[TransactionDigest],
2246 tx_key: TransactionKey,
2247) -> Vec<TransactionEffects> {
2248 let delay = if in_antithesis() {
2249 15
2251 } else {
2252 5
2253 };
2254 loop {
2255 match tokio::time::timeout(Duration::from_secs(delay), async {
2256 effects_store
2257 .notify_read_executed_effects(task_name, digests)
2258 .await
2259 })
2260 .await
2261 {
2262 Ok(effects) => break effects,
2263 Err(_) => {
2264 debug_fatal!(
2265 "Timeout waiting for transactions to be executed {:?}, retrying...",
2266 tx_key
2267 );
2268 }
2269 }
2270 }
2271}
2272
2273impl CheckpointAggregator {
2274 fn new(
2275 tables: Arc<CheckpointStore>,
2276 epoch_store: Arc<AuthorityPerEpochStore>,
2277 notify: Arc<Notify>,
2278 receiver: mpsc::UnboundedReceiver<CheckpointSignatureMessage>,
2279 output: Box<dyn CertifiedCheckpointOutput>,
2280 state: Arc<AuthorityState>,
2281 metrics: Arc<CheckpointMetrics>,
2282 ) -> Self {
2283 Self {
2284 store: tables,
2285 epoch_store,
2286 notify,
2287 receiver,
2288 pending: BTreeMap::new(),
2289 current: None,
2290 output,
2291 state,
2292 metrics,
2293 }
2294 }
2295
2296 async fn run(mut self) {
2297 info!("Starting CheckpointAggregator");
2298 loop {
2299 while let Ok(sig) = self.receiver.try_recv() {
2301 self.pending
2302 .entry(sig.summary.sequence_number)
2303 .or_default()
2304 .push(sig);
2305 }
2306
2307 if let Err(e) = self.run_and_notify().await {
2308 error!(
2309 "Error while aggregating checkpoint, will retry in 1s: {:?}",
2310 e
2311 );
2312 self.metrics.checkpoint_errors.inc();
2313 tokio::time::sleep(Duration::from_secs(1)).await;
2314 continue;
2315 }
2316
2317 tokio::select! {
2318 Some(sig) = self.receiver.recv() => {
2319 self.pending
2320 .entry(sig.summary.sequence_number)
2321 .or_default()
2322 .push(sig);
2323 }
2324 _ = self.notify.notified() => {}
2325 _ = tokio::time::sleep(Duration::from_secs(1)) => {}
2326 }
2327 }
2328 }
2329
2330 async fn run_and_notify(&mut self) -> SuiResult {
2331 let summaries = self.run_inner()?;
2332 for summary in summaries {
2333 self.output.certified_checkpoint_created(&summary).await?;
2334 }
2335 Ok(())
2336 }
2337
2338 fn run_inner(&mut self) -> SuiResult<Vec<CertifiedCheckpointSummary>> {
2339 let _scope = monitored_scope("CheckpointAggregator");
2340 let mut result = vec![];
2341 'outer: loop {
2342 let next_to_certify = self.next_checkpoint_to_certify()?;
2343 self.pending.retain(|&seq, _| seq >= next_to_certify);
2346 let current = if let Some(current) = &mut self.current {
2347 if current.summary.sequence_number < next_to_certify {
2353 assert_reachable!("skip checkpoint certification");
2354 self.current = None;
2355 continue;
2356 }
2357 current
2358 } else {
2359 let Some(summary) = self
2360 .epoch_store
2361 .get_built_checkpoint_summary(next_to_certify)?
2362 else {
2363 return Ok(result);
2364 };
2365 self.current = Some(CheckpointSignatureAggregator {
2366 digest: summary.digest(),
2367 summary,
2368 signatures_by_digest: MultiStakeAggregator::new(
2369 self.epoch_store.committee().clone(),
2370 ),
2371 store: self.store.clone(),
2372 state: self.state.clone(),
2373 metrics: self.metrics.clone(),
2374 });
2375 self.current.as_mut().unwrap()
2376 };
2377
2378 let seq = current.summary.sequence_number;
2379 let sigs = self.pending.remove(&seq).unwrap_or_default();
2380 if sigs.is_empty() {
2381 trace!(
2382 checkpoint_seq =? seq,
2383 "Not enough checkpoint signatures",
2384 );
2385 return Ok(result);
2386 }
2387 for data in sigs {
2388 trace!(
2389 checkpoint_seq = seq,
2390 "Processing signature for checkpoint (digest: {:?}) from {:?}",
2391 current.summary.digest(),
2392 data.summary.auth_sig().authority.concise()
2393 );
2394 self.metrics
2395 .checkpoint_participation
2396 .with_label_values(&[&format!(
2397 "{:?}",
2398 data.summary.auth_sig().authority.concise()
2399 )])
2400 .inc();
2401 if let Ok(auth_signature) = current.try_aggregate(data) {
2402 debug!(
2403 checkpoint_seq = seq,
2404 "Successfully aggregated signatures for checkpoint (digest: {:?})",
2405 current.summary.digest(),
2406 );
2407 let summary = VerifiedCheckpoint::new_unchecked(
2408 CertifiedCheckpointSummary::new_from_data_and_sig(
2409 current.summary.clone(),
2410 auth_signature,
2411 ),
2412 );
2413
2414 self.store.insert_certified_checkpoint(&summary)?;
2415 self.metrics.last_certified_checkpoint.set(seq as i64);
2416 current.summary.report_checkpoint_age(
2417 &self.metrics.last_certified_checkpoint_age,
2418 &self.metrics.last_certified_checkpoint_age_ms,
2419 );
2420 result.push(summary.into_inner());
2421 self.current = None;
2422 continue 'outer;
2423 }
2424 }
2425 break;
2426 }
2427 Ok(result)
2428 }
2429
2430 fn next_checkpoint_to_certify(&self) -> SuiResult<CheckpointSequenceNumber> {
2431 Ok(self
2432 .store
2433 .tables
2434 .certified_checkpoints
2435 .reversed_safe_iter_with_bounds(None, None)?
2436 .next()
2437 .transpose()?
2438 .map(|(seq, _)| seq + 1)
2439 .unwrap_or_default())
2440 }
2441}
2442
2443impl CheckpointSignatureAggregator {
2444 #[allow(clippy::result_unit_err)]
2445 pub fn try_aggregate(
2446 &mut self,
2447 data: CheckpointSignatureMessage,
2448 ) -> Result<AuthorityStrongQuorumSignInfo, ()> {
2449 let their_digest = *data.summary.digest();
2450 let (_, signature) = data.summary.into_data_and_sig();
2451 let author = signature.authority;
2452 let envelope =
2453 SignedCheckpointSummary::new_from_data_and_sig(self.summary.clone(), signature);
2454 match self.signatures_by_digest.insert(their_digest, envelope) {
2455 InsertResult::Failed { error }
2457 if matches!(
2458 error.as_inner(),
2459 SuiErrorKind::StakeAggregatorRepeatedSigner {
2460 conflicting_sig: false,
2461 ..
2462 },
2463 ) =>
2464 {
2465 Err(())
2466 }
2467 InsertResult::Failed { error } => {
2468 warn!(
2469 checkpoint_seq = self.summary.sequence_number,
2470 "Failed to aggregate new signature from validator {:?}: {:?}",
2471 author.concise(),
2472 error
2473 );
2474 self.check_for_split_brain();
2475 Err(())
2476 }
2477 InsertResult::QuorumReached(cert) => {
2478 if their_digest != self.digest {
2481 self.metrics.remote_checkpoint_forks.inc();
2482 warn!(
2483 checkpoint_seq = self.summary.sequence_number,
2484 "Validator {:?} has mismatching checkpoint digest {}, we have digest {}",
2485 author.concise(),
2486 their_digest,
2487 self.digest
2488 );
2489 return Err(());
2490 }
2491 Ok(cert)
2492 }
2493 InsertResult::NotEnoughVotes {
2494 bad_votes: _,
2495 bad_authorities: _,
2496 } => {
2497 self.check_for_split_brain();
2498 Err(())
2499 }
2500 }
2501 }
2502
2503 fn check_for_split_brain(&self) {
2507 debug!(
2508 checkpoint_seq = self.summary.sequence_number,
2509 "Checking for split brain condition"
2510 );
2511 if self.signatures_by_digest.quorum_unreachable() {
2512 let all_unique_values = self.signatures_by_digest.get_all_unique_values();
2518 let digests_by_stake_messages = all_unique_values
2519 .iter()
2520 .sorted_by_key(|(_, (_, stake))| -(*stake as i64))
2521 .map(|(digest, (_authorities, total_stake))| {
2522 format!("{:?} (total stake: {})", digest, total_stake)
2523 })
2524 .collect::<Vec<String>>();
2525 fail_point_arg!("kill_split_brain_node", |(
2526 checkpoint_overrides,
2527 forked_authorities,
2528 ): (
2529 std::sync::Arc<std::sync::Mutex<std::collections::BTreeMap<u64, String>>>,
2530 std::sync::Arc<std::sync::Mutex<std::collections::HashSet<AuthorityName>>>,
2531 )| {
2532 #[cfg(msim)]
2533 {
2534 if let (Ok(mut overrides), Ok(forked_authorities_set)) =
2535 (checkpoint_overrides.lock(), forked_authorities.lock())
2536 {
2537 let correct_digest = all_unique_values
2539 .iter()
2540 .find(|(_, (authorities, _))| {
2541 authorities
2543 .iter()
2544 .any(|auth| !forked_authorities_set.contains(auth))
2545 })
2546 .map(|(digest, _)| digest.to_string())
2547 .unwrap_or_else(|| {
2548 all_unique_values
2550 .iter()
2551 .max_by_key(|(_, (_, stake))| *stake)
2552 .map(|(digest, _)| digest.to_string())
2553 .unwrap_or_else(|| self.digest.to_string())
2554 });
2555
2556 overrides.insert(self.summary.sequence_number, correct_digest.clone());
2557
2558 tracing::error!(
2559 fatal = true,
2560 "Fork recovery test: detected split-brain for sequence number: {}, using digest: {}",
2561 self.summary.sequence_number,
2562 correct_digest
2563 );
2564 }
2565 }
2566 });
2567
2568 debug_fatal!(
2569 "Split brain detected in checkpoint signature aggregation for checkpoint {:?}. Remaining stake: {:?}, Digests by stake: {:?}",
2570 self.summary.sequence_number,
2571 self.signatures_by_digest.uncommitted_stake(),
2572 digests_by_stake_messages
2573 );
2574 self.metrics.split_brain_checkpoint_forks.inc();
2575
2576 let all_unique_values = self.signatures_by_digest.get_all_unique_values();
2577 let local_summary = self.summary.clone();
2578 let state = self.state.clone();
2579 let tables = self.store.clone();
2580
2581 tokio::spawn(async move {
2582 diagnose_split_brain(all_unique_values, local_summary, state, tables).await;
2583 });
2584 }
2585 }
2586}
2587
2588async fn diagnose_split_brain(
2594 all_unique_values: BTreeMap<CheckpointDigest, (Vec<AuthorityName>, StakeUnit)>,
2595 local_summary: CheckpointSummary,
2596 state: Arc<AuthorityState>,
2597 tables: Arc<CheckpointStore>,
2598) {
2599 debug!(
2600 checkpoint_seq = local_summary.sequence_number,
2601 "Running split brain diagnostics..."
2602 );
2603 let time = SystemTime::now();
2604 let digest_to_validator = all_unique_values
2606 .iter()
2607 .filter_map(|(digest, (validators, _))| {
2608 if *digest != local_summary.digest() {
2609 let random_validator = validators.choose(&mut get_rng()).unwrap();
2610 Some((*digest, *random_validator))
2611 } else {
2612 None
2613 }
2614 })
2615 .collect::<HashMap<_, _>>();
2616 if digest_to_validator.is_empty() {
2617 panic!(
2618 "Given split brain condition, there should be at \
2619 least one validator that disagrees with local signature"
2620 );
2621 }
2622
2623 let epoch_store = state.load_epoch_store_one_call_per_task();
2624 let committee = epoch_store
2625 .epoch_start_state()
2626 .get_sui_committee_with_network_metadata();
2627 let network_config = default_mysten_network_config();
2628 let network_clients =
2629 make_network_authority_clients_with_network_config(&committee, &network_config);
2630
2631 let response_futures = digest_to_validator
2633 .values()
2634 .cloned()
2635 .map(|validator| {
2636 let client = network_clients
2637 .get(&validator)
2638 .expect("Failed to get network client");
2639 let request = CheckpointRequestV2 {
2640 sequence_number: Some(local_summary.sequence_number),
2641 request_content: true,
2642 certified: false,
2643 };
2644 client.handle_checkpoint_v2(request)
2645 })
2646 .collect::<Vec<_>>();
2647
2648 let digest_name_pair = digest_to_validator.iter();
2649 let response_data = futures::future::join_all(response_futures)
2650 .await
2651 .into_iter()
2652 .zip_debug_eq(digest_name_pair)
2653 .filter_map(|(response, (digest, name))| match response {
2654 Ok(response) => match response {
2655 CheckpointResponseV2 {
2656 checkpoint: Some(CheckpointSummaryResponse::Pending(summary)),
2657 contents: Some(contents),
2658 } => Some((*name, *digest, summary, contents)),
2659 CheckpointResponseV2 {
2660 checkpoint: Some(CheckpointSummaryResponse::Certified(_)),
2661 contents: _,
2662 } => {
2663 panic!("Expected pending checkpoint, but got certified checkpoint");
2664 }
2665 CheckpointResponseV2 {
2666 checkpoint: None,
2667 contents: _,
2668 } => {
2669 error!(
2670 "Summary for checkpoint {:?} not found on validator {:?}",
2671 local_summary.sequence_number, name
2672 );
2673 None
2674 }
2675 CheckpointResponseV2 {
2676 checkpoint: _,
2677 contents: None,
2678 } => {
2679 error!(
2680 "Contents for checkpoint {:?} not found on validator {:?}",
2681 local_summary.sequence_number, name
2682 );
2683 None
2684 }
2685 },
2686 Err(e) => {
2687 error!(
2688 "Failed to get checkpoint contents from validator for fork diagnostics: {:?}",
2689 e
2690 );
2691 None
2692 }
2693 })
2694 .collect::<Vec<_>>();
2695
2696 let local_checkpoint_contents = tables
2697 .get_checkpoint_contents(&local_summary.content_digest)
2698 .unwrap_or_else(|_| {
2699 panic!(
2700 "Could not find checkpoint contents for digest {:?}",
2701 local_summary.digest()
2702 )
2703 })
2704 .unwrap_or_else(|| {
2705 panic!(
2706 "Could not find local full checkpoint contents for checkpoint {:?}, digest {:?}",
2707 local_summary.sequence_number,
2708 local_summary.digest()
2709 )
2710 });
2711 let local_contents_text = format!("{local_checkpoint_contents:?}");
2712
2713 let local_summary_text = format!("{local_summary:?}");
2714 let local_validator = state.name.concise();
2715 let diff_patches = response_data
2716 .iter()
2717 .map(|(name, other_digest, other_summary, contents)| {
2718 let other_contents_text = format!("{contents:?}");
2719 let other_summary_text = format!("{other_summary:?}");
2720 let (local_transactions, local_effects): (Vec<_>, Vec<_>) = local_checkpoint_contents
2721 .enumerate_transactions(&local_summary)
2722 .map(|(_, exec_digest)| (exec_digest.transaction, exec_digest.effects))
2723 .unzip();
2724 let (other_transactions, other_effects): (Vec<_>, Vec<_>) = contents
2725 .enumerate_transactions(other_summary)
2726 .map(|(_, exec_digest)| (exec_digest.transaction, exec_digest.effects))
2727 .unzip();
2728 let summary_patch = create_patch(&local_summary_text, &other_summary_text);
2729 let contents_patch = create_patch(&local_contents_text, &other_contents_text);
2730 let local_transactions_text = format!("{local_transactions:#?}");
2731 let other_transactions_text = format!("{other_transactions:#?}");
2732 let transactions_patch =
2733 create_patch(&local_transactions_text, &other_transactions_text);
2734 let local_effects_text = format!("{local_effects:#?}");
2735 let other_effects_text = format!("{other_effects:#?}");
2736 let effects_patch = create_patch(&local_effects_text, &other_effects_text);
2737 let seq_number = local_summary.sequence_number;
2738 let local_digest = local_summary.digest();
2739 let other_validator = name.concise();
2740 format!(
2741 "Checkpoint: {seq_number:?}\n\
2742 Local validator (original): {local_validator:?}, digest: {local_digest:?}\n\
2743 Other validator (modified): {other_validator:?}, digest: {other_digest:?}\n\n\
2744 Summary Diff: \n{summary_patch}\n\n\
2745 Contents Diff: \n{contents_patch}\n\n\
2746 Transactions Diff: \n{transactions_patch}\n\n\
2747 Effects Diff: \n{effects_patch}",
2748 )
2749 })
2750 .collect::<Vec<_>>()
2751 .join("\n\n\n");
2752
2753 let header = format!(
2754 "Checkpoint Fork Dump - Authority {local_validator:?}: \n\
2755 Datetime: {:?}",
2756 time
2757 );
2758 let fork_logs_text = format!("{header}\n\n{diff_patches}\n\n");
2759 let path = tempfile::tempdir()
2760 .expect("Failed to create tempdir")
2761 .keep()
2762 .join(Path::new("checkpoint_fork_dump.txt"));
2763 let mut file = File::create(path).unwrap();
2764 write!(file, "{}", fork_logs_text).unwrap();
2765 debug!("{}", fork_logs_text);
2766}
2767
2768pub trait CheckpointServiceNotify {
2769 fn notify_checkpoint_signature(&self, info: &CheckpointSignatureMessage) -> SuiResult;
2770
2771 fn notify_checkpoint(&self) -> SuiResult;
2772}
2773
2774#[allow(clippy::large_enum_variant)]
2775enum CheckpointServiceState {
2776 Unstarted(
2777 (
2778 CheckpointBuilder,
2779 CheckpointAggregator,
2780 CheckpointStateHasher,
2781 ),
2782 ),
2783 Started,
2784}
2785
2786impl CheckpointServiceState {
2787 fn take_unstarted(
2788 &mut self,
2789 ) -> (
2790 CheckpointBuilder,
2791 CheckpointAggregator,
2792 CheckpointStateHasher,
2793 ) {
2794 let mut state = CheckpointServiceState::Started;
2795 std::mem::swap(self, &mut state);
2796
2797 match state {
2798 CheckpointServiceState::Unstarted((builder, aggregator, hasher)) => {
2799 (builder, aggregator, hasher)
2800 }
2801 CheckpointServiceState::Started => panic!("CheckpointServiceState is already started"),
2802 }
2803 }
2804}
2805
2806pub struct CheckpointService {
2807 tables: Arc<CheckpointStore>,
2808 notify_builder: Arc<Notify>,
2809 signature_sender: mpsc::UnboundedSender<CheckpointSignatureMessage>,
2810 highest_currently_built_seq_tx: watch::Sender<CheckpointSequenceNumber>,
2812 highest_previously_built_seq: CheckpointSequenceNumber,
2815 metrics: Arc<CheckpointMetrics>,
2816 state: Mutex<CheckpointServiceState>,
2817}
2818
2819impl CheckpointService {
2820 #[allow(clippy::disallowed_methods)]
2826 pub fn build(
2827 state: Arc<AuthorityState>,
2828 checkpoint_store: Arc<CheckpointStore>,
2829 epoch_store: Arc<AuthorityPerEpochStore>,
2830 effects_store: Arc<dyn TransactionCacheRead>,
2831 global_state_hasher: Weak<GlobalStateHasher>,
2832 checkpoint_output: Box<dyn CheckpointOutput>,
2833 certified_checkpoint_output: Box<dyn CertifiedCheckpointOutput>,
2834 metrics: Arc<CheckpointMetrics>,
2835 ) -> Arc<Self> {
2836 info!("Starting checkpoint service");
2837 Self::initialize_accumulator_account_metrics(&state, &epoch_store, &metrics);
2838 let notify_builder = Arc::new(Notify::new());
2839 let notify_aggregator = Arc::new(Notify::new());
2840
2841 let highest_previously_built_seq = checkpoint_store
2843 .get_latest_locally_computed_checkpoint()
2844 .expect("failed to get latest locally computed checkpoint")
2845 .map(|s| s.sequence_number)
2846 .unwrap_or(0);
2847
2848 let highest_currently_built_seq =
2849 CheckpointBuilder::load_last_built_checkpoint_summary(&epoch_store, &checkpoint_store)
2850 .expect("epoch should not have ended")
2851 .map(|(seq, _)| seq)
2852 .unwrap_or(0);
2853
2854 let (highest_currently_built_seq_tx, _) = watch::channel(highest_currently_built_seq);
2855
2856 let (signature_sender, signature_receiver) = mpsc::unbounded_channel();
2857
2858 let aggregator = CheckpointAggregator::new(
2859 checkpoint_store.clone(),
2860 epoch_store.clone(),
2861 notify_aggregator.clone(),
2862 signature_receiver,
2863 certified_checkpoint_output,
2864 state.clone(),
2865 metrics.clone(),
2866 );
2867
2868 let (send_to_hasher, receive_from_builder) = mpsc::channel(16);
2869
2870 let ckpt_state_hasher = CheckpointStateHasher::new(
2871 epoch_store.clone(),
2872 global_state_hasher.clone(),
2873 receive_from_builder,
2874 );
2875
2876 let builder = CheckpointBuilder::new(
2877 state.clone(),
2878 checkpoint_store.clone(),
2879 epoch_store.clone(),
2880 notify_builder.clone(),
2881 effects_store,
2882 global_state_hasher,
2883 send_to_hasher,
2884 checkpoint_output,
2885 notify_aggregator.clone(),
2886 highest_currently_built_seq_tx.clone(),
2887 metrics.clone(),
2888 );
2889
2890 Arc::new(Self {
2891 tables: checkpoint_store,
2892 notify_builder,
2893 signature_sender,
2894 highest_currently_built_seq_tx,
2895 highest_previously_built_seq,
2896 metrics,
2897 state: Mutex::new(CheckpointServiceState::Unstarted((
2898 builder,
2899 aggregator,
2900 ckpt_state_hasher,
2901 ))),
2902 })
2903 }
2904
2905 fn initialize_accumulator_account_metrics(
2906 state: &AuthorityState,
2907 epoch_store: &AuthorityPerEpochStore,
2908 metrics: &CheckpointMetrics,
2909 ) {
2910 if !epoch_store.protocol_config().enable_accumulators() {
2911 return;
2912 }
2913
2914 let object_store = state.get_object_store();
2915 match accumulator_metadata::get_accumulator_object_count(object_store.as_ref()) {
2916 Ok(Some(count)) => metrics.initialize_accumulator_accounts_live(count),
2917 Ok(None) => {}
2918 Err(e) => fatal!("failed to initialize accumulator account metrics: {e}"),
2919 }
2920 }
2921
2922 pub async fn spawn(
2930 &self,
2931 epoch_store: Arc<AuthorityPerEpochStore>,
2932 consensus_replay_waiter: Option<ReplayWaiter>,
2933 ) {
2934 let (builder, aggregator, state_hasher) = self.state.lock().take_unstarted();
2935
2936 let last_persisted_builder_seq = epoch_store
2946 .last_persisted_checkpoint_builder_summary()
2947 .expect("epoch should not have ended")
2948 .map(|s| s.summary.sequence_number);
2949
2950 let last_executed_seq = self
2951 .tables
2952 .get_highest_executed_checkpoint()
2953 .expect("Failed to get highest executed checkpoint")
2954 .map(|checkpoint| *checkpoint.sequence_number());
2955
2956 if let Some(last_committed_seq) = last_persisted_builder_seq.max(last_executed_seq) {
2957 if let Err(e) = builder
2958 .epoch_store
2959 .clear_state_hashes_after_checkpoint(last_committed_seq)
2960 {
2961 error!(
2962 "Failed to clear state hashes after checkpoint {}: {:?}",
2963 last_committed_seq, e
2964 );
2965 } else {
2966 info!(
2967 "Cleared state hashes after checkpoint {} to ensure consistent ECMH computation",
2968 last_committed_seq
2969 );
2970 }
2971 }
2972
2973 let (builder_finished_tx, builder_finished_rx) = tokio::sync::oneshot::channel();
2974
2975 let state_hasher_task = spawn_monitored_task!(state_hasher.run());
2976 let aggregator_task = spawn_monitored_task!(aggregator.run());
2977
2978 spawn_monitored_task!(async move {
2979 epoch_store
2980 .within_alive_epoch(async move {
2981 builder.run(consensus_replay_waiter).await;
2982 builder_finished_tx.send(()).ok();
2983 })
2984 .await
2985 .ok();
2986
2987 state_hasher_task
2989 .await
2990 .expect("state hasher should exit normally");
2991
2992 aggregator_task.abort();
2995 aggregator_task.await.ok();
2996 });
2997
2998 if tokio::time::timeout(Duration::from_secs(120), async move {
3004 tokio::select! {
3005 _ = builder_finished_rx => { debug!("CheckpointBuilder finished"); }
3006 _ = self.wait_for_rebuilt_checkpoints() => (),
3007 }
3008 })
3009 .await
3010 .is_err()
3011 {
3012 debug_fatal!("Timed out waiting for checkpoints to be rebuilt");
3013 }
3014 }
3015}
3016
3017impl CheckpointService {
3018 pub async fn wait_for_rebuilt_checkpoints(&self) {
3024 let highest_previously_built_seq = self.highest_previously_built_seq;
3025 let mut rx = self.highest_currently_built_seq_tx.subscribe();
3026 let mut highest_currently_built_seq = *rx.borrow_and_update();
3027 info!(
3028 "Waiting for checkpoints to be rebuilt, previously built seq: {highest_previously_built_seq}, currently built seq: {highest_currently_built_seq}"
3029 );
3030 loop {
3031 if highest_currently_built_seq >= highest_previously_built_seq {
3032 info!("Checkpoint rebuild complete");
3033 break;
3034 }
3035 rx.changed().await.unwrap();
3036 highest_currently_built_seq = *rx.borrow_and_update();
3037 }
3038 }
3039
3040 #[cfg(test)]
3041 fn write_and_notify_checkpoint_for_testing(
3042 &self,
3043 epoch_store: &AuthorityPerEpochStore,
3044 checkpoint: PendingCheckpoint,
3045 ) -> SuiResult {
3046 use crate::authority::authority_per_epoch_store::consensus_quarantine::ConsensusCommitOutput;
3047
3048 let mut output = ConsensusCommitOutput::new(0);
3049 epoch_store.write_pending_checkpoint(&mut output, &checkpoint)?;
3050 output.set_default_commit_stats_for_testing();
3051 epoch_store.push_consensus_output_for_tests(output);
3052 self.notify_checkpoint()?;
3053 Ok(())
3054 }
3055}
3056
3057impl CheckpointServiceNotify for CheckpointService {
3058 fn notify_checkpoint_signature(&self, info: &CheckpointSignatureMessage) -> SuiResult {
3059 let sequence = info.summary.sequence_number;
3060 let signer = info.summary.auth_sig().authority.concise();
3061
3062 if let Some(highest_verified_checkpoint) = self
3063 .tables
3064 .get_highest_verified_checkpoint()?
3065 .map(|x| *x.sequence_number())
3066 && sequence <= highest_verified_checkpoint
3067 {
3068 trace!(
3069 checkpoint_seq = sequence,
3070 "Ignore checkpoint signature from {} - already certified", signer,
3071 );
3072 self.metrics
3073 .last_ignored_checkpoint_signature_received
3074 .set(sequence as i64);
3075 return Ok(());
3076 }
3077 trace!(
3078 checkpoint_seq = sequence,
3079 "Received checkpoint signature, digest {} from {}",
3080 info.summary.digest(),
3081 signer,
3082 );
3083 self.metrics
3084 .last_received_checkpoint_signatures
3085 .with_label_values(&[&signer.to_string()])
3086 .set(sequence as i64);
3087 self.signature_sender.send(info.clone()).ok();
3088 Ok(())
3089 }
3090
3091 fn notify_checkpoint(&self) -> SuiResult {
3092 self.notify_builder.notify_one();
3093 Ok(())
3094 }
3095}
3096
3097pub struct CheckpointServiceNoop {}
3099impl CheckpointServiceNotify for CheckpointServiceNoop {
3100 fn notify_checkpoint_signature(&self, _: &CheckpointSignatureMessage) -> SuiResult {
3101 Ok(())
3102 }
3103
3104 fn notify_checkpoint(&self) -> SuiResult {
3105 Ok(())
3106 }
3107}
3108
3109impl PendingCheckpoint {
3110 pub fn height(&self) -> CheckpointHeight {
3111 self.details.checkpoint_height
3112 }
3113
3114 pub(crate) fn num_roots(&self) -> usize {
3115 self.roots.iter().map(|r| r.tx_roots.len()).sum()
3116 }
3117}
3118
3119pin_project! {
3120 pub struct PollCounter<Fut> {
3121 #[pin]
3122 future: Fut,
3123 count: usize,
3124 }
3125}
3126
3127impl<Fut> PollCounter<Fut> {
3128 pub fn new(future: Fut) -> Self {
3129 Self { future, count: 0 }
3130 }
3131
3132 pub fn count(&self) -> usize {
3133 self.count
3134 }
3135}
3136
3137impl<Fut: Future> Future for PollCounter<Fut> {
3138 type Output = (usize, Fut::Output);
3139
3140 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
3141 let this = self.project();
3142 *this.count += 1;
3143 match this.future.poll(cx) {
3144 Poll::Ready(output) => Poll::Ready((*this.count, output)),
3145 Poll::Pending => Poll::Pending,
3146 }
3147 }
3148}
3149
3150fn poll_count<Fut>(future: Fut) -> PollCounter<Fut> {
3151 PollCounter::new(future)
3152}
3153
3154#[cfg(test)]
3155mod tests {
3156 use super::*;
3157 use crate::authority::test_authority_builder::TestAuthorityBuilder;
3158 use futures::FutureExt as _;
3159 use futures::future::BoxFuture;
3160 use std::collections::HashMap;
3161 use std::ops::Deref;
3162 use sui_macros::sim_test;
3163 use sui_protocol_config::{Chain, ProtocolConfig};
3164 use sui_types::accumulator_event::AccumulatorEvent;
3165 use sui_types::base_types::{SequenceNumber, TransactionEffectsDigest};
3166 use sui_types::crypto::Signature;
3167 use sui_types::effects::{TransactionEffects, TransactionEvents};
3168 use sui_types::messages_checkpoint::SignedCheckpointSummary;
3169 use sui_types::transaction::VerifiedTransaction;
3170 use tokio::sync::mpsc;
3171
3172 #[tokio::test]
3173 async fn test_clear_locally_computed_checkpoints_from_deletes_inclusive_range() {
3174 let store = CheckpointStore::new_for_tests();
3175 let protocol = sui_protocol_config::ProtocolConfig::get_for_max_version_UNSAFE();
3176 for seq in 70u64..=80u64 {
3177 let contents =
3178 sui_types::messages_checkpoint::CheckpointContents::new_with_digests_only_for_tests(
3179 [sui_types::base_types::ExecutionDigests::new(
3180 sui_types::digests::TransactionDigest::random(),
3181 sui_types::digests::TransactionEffectsDigest::ZERO,
3182 )],
3183 );
3184 let summary = sui_types::messages_checkpoint::CheckpointSummary::new(
3185 &protocol,
3186 0,
3187 seq,
3188 0,
3189 &contents,
3190 None,
3191 sui_types::gas::GasCostSummary::default(),
3192 None,
3193 0,
3194 Vec::new(),
3195 Vec::new(),
3196 );
3197 store
3198 .tables
3199 .locally_computed_checkpoints
3200 .insert(&seq, &summary)
3201 .unwrap();
3202 }
3203
3204 store
3205 .clear_locally_computed_checkpoints_from(76)
3206 .expect("clear should succeed");
3207
3208 assert!(
3210 store
3211 .tables
3212 .locally_computed_checkpoints
3213 .get(&75)
3214 .unwrap()
3215 .is_some()
3216 );
3217 assert!(
3218 store
3219 .tables
3220 .locally_computed_checkpoints
3221 .get(&76)
3222 .unwrap()
3223 .is_none()
3224 );
3225
3226 for seq in 70u64..76u64 {
3227 assert!(
3228 store
3229 .tables
3230 .locally_computed_checkpoints
3231 .get(&seq)
3232 .unwrap()
3233 .is_some()
3234 );
3235 }
3236 for seq in 76u64..=80u64 {
3237 assert!(
3238 store
3239 .tables
3240 .locally_computed_checkpoints
3241 .get(&seq)
3242 .unwrap()
3243 .is_none()
3244 );
3245 }
3246 }
3247
3248 #[tokio::test]
3249 async fn test_fork_detection_storage() {
3250 let store = CheckpointStore::new_for_tests();
3251 let seq_num = 42;
3253 let digest = CheckpointDigest::random();
3254
3255 assert!(store.get_checkpoint_fork_detected().unwrap().is_none());
3256
3257 store
3258 .record_checkpoint_fork_detected(seq_num, digest)
3259 .unwrap();
3260
3261 let retrieved = store.get_checkpoint_fork_detected().unwrap();
3262 assert!(retrieved.is_some());
3263 let (retrieved_seq, retrieved_digest) = retrieved.unwrap();
3264 assert_eq!(retrieved_seq, seq_num);
3265 assert_eq!(retrieved_digest, digest);
3266
3267 store.clear_checkpoint_fork_detected().unwrap();
3268 assert!(store.get_checkpoint_fork_detected().unwrap().is_none());
3269
3270 let tx_digest = TransactionDigest::random();
3272 let expected_effects = TransactionEffectsDigest::random();
3273 let actual_effects = TransactionEffectsDigest::random();
3274
3275 assert!(store.get_transaction_fork_detected().unwrap().is_none());
3276
3277 store
3278 .record_transaction_fork_detected(tx_digest, expected_effects, actual_effects)
3279 .unwrap();
3280
3281 let retrieved = store.get_transaction_fork_detected().unwrap();
3282 assert!(retrieved.is_some());
3283 let (retrieved_tx, retrieved_expected, retrieved_actual) = retrieved.unwrap();
3284 assert_eq!(retrieved_tx, tx_digest);
3285 assert_eq!(retrieved_expected, expected_effects);
3286 assert_eq!(retrieved_actual, actual_effects);
3287
3288 store.clear_transaction_fork_detected().unwrap();
3289 assert!(store.get_transaction_fork_detected().unwrap().is_none());
3290 }
3291
3292 #[sim_test]
3293 pub async fn checkpoint_builder_test() {
3294 telemetry_subscribers::init_for_testing();
3295
3296 let mut protocol_config =
3297 ProtocolConfig::get_for_version(ProtocolVersion::max(), Chain::Unknown);
3298 protocol_config.disable_accumulators_for_testing();
3299 let state = TestAuthorityBuilder::new()
3300 .with_protocol_config(protocol_config)
3301 .build()
3302 .await;
3303
3304 let dummy_tx = VerifiedTransaction::new_authenticator_state_update(
3305 0,
3306 0,
3307 vec![],
3308 SequenceNumber::new(),
3309 );
3310
3311 for i in 0..20 {
3312 state
3313 .database_for_testing()
3314 .perpetual_tables
3315 .transactions
3316 .insert(&d(i), dummy_tx.serializable_ref())
3317 .unwrap();
3318 }
3319
3320 let mut store = HashMap::<TransactionDigest, TransactionEffects>::new();
3321 commit_cert_for_test(
3322 &mut store,
3323 state.clone(),
3324 d(1),
3325 vec![d(2), d(3)],
3326 GasCostSummary::new(11, 12, 11, 1),
3327 );
3328 commit_cert_for_test(
3329 &mut store,
3330 state.clone(),
3331 d(2),
3332 vec![d(3), d(4)],
3333 GasCostSummary::new(21, 22, 21, 1),
3334 );
3335 commit_cert_for_test(
3336 &mut store,
3337 state.clone(),
3338 d(3),
3339 vec![],
3340 GasCostSummary::new(31, 32, 31, 1),
3341 );
3342 commit_cert_for_test(
3343 &mut store,
3344 state.clone(),
3345 d(4),
3346 vec![],
3347 GasCostSummary::new(41, 42, 41, 1),
3348 );
3349 for i in [5, 6, 7, 10, 11, 12, 13] {
3350 commit_cert_for_test(
3351 &mut store,
3352 state.clone(),
3353 d(i),
3354 vec![],
3355 GasCostSummary::new(41, 42, 41, 1),
3356 );
3357 }
3358 for i in [15, 16, 17] {
3359 commit_cert_for_test(
3360 &mut store,
3361 state.clone(),
3362 d(i),
3363 vec![],
3364 GasCostSummary::new(51, 52, 51, 1),
3365 );
3366 }
3367 let all_digests: Vec<_> = store.keys().copied().collect();
3368 for digest in all_digests {
3369 let signature = Signature::Ed25519SuiSignature(Default::default()).into();
3370 state
3371 .epoch_store_for_testing()
3372 .test_insert_user_signature(digest, vec![(signature, None)]);
3373 }
3374
3375 let (output, mut result) = mpsc::channel::<(CheckpointContents, CheckpointSummary)>(10);
3376 let (certified_output, mut certified_result) =
3377 mpsc::channel::<CertifiedCheckpointSummary>(10);
3378 let store = Arc::new(store);
3379
3380 let ckpt_dir = tempfile::tempdir().unwrap();
3381 let checkpoint_store =
3382 CheckpointStore::new(ckpt_dir.path(), Arc::new(PrunerWatermarks::default()));
3383 let epoch_store = state.epoch_store_for_testing();
3384
3385 let global_state_hasher = Arc::new(GlobalStateHasher::new_for_tests(
3386 state.get_global_state_hash_store().clone(),
3387 ));
3388
3389 let checkpoint_service = CheckpointService::build(
3390 state.clone(),
3391 checkpoint_store,
3392 epoch_store.clone(),
3393 store,
3394 Arc::downgrade(&global_state_hasher),
3395 Box::new(output),
3396 Box::new(certified_output),
3397 CheckpointMetrics::new_for_tests(),
3398 );
3399 checkpoint_service.spawn(epoch_store.clone(), None).await;
3400
3401 checkpoint_service
3402 .write_and_notify_checkpoint_for_testing(&epoch_store, p(0, vec![4], 0))
3403 .unwrap();
3404 checkpoint_service
3405 .write_and_notify_checkpoint_for_testing(&epoch_store, p(1, vec![1, 3], 2000))
3406 .unwrap();
3407 checkpoint_service
3408 .write_and_notify_checkpoint_for_testing(&epoch_store, p(2, vec![10, 11, 12, 13], 3000))
3409 .unwrap();
3410 checkpoint_service
3411 .write_and_notify_checkpoint_for_testing(&epoch_store, p(3, vec![15, 16, 17], 4000))
3412 .unwrap();
3413 checkpoint_service
3414 .write_and_notify_checkpoint_for_testing(&epoch_store, p(4, vec![5], 4001))
3415 .unwrap();
3416 checkpoint_service
3417 .write_and_notify_checkpoint_for_testing(&epoch_store, p(5, vec![6], 5000))
3418 .unwrap();
3419
3420 let (c1c, c1s) = result.recv().await.unwrap();
3421 let (c2c, c2s) = result.recv().await.unwrap();
3422
3423 let c1t = c1c.iter().map(|d| d.transaction).collect::<Vec<_>>();
3424 let c2t = c2c.iter().map(|d| d.transaction).collect::<Vec<_>>();
3425 assert_eq!(c1t, vec![d(4)]);
3426 assert_eq!(c1s.previous_digest, None);
3427 assert_eq!(c1s.sequence_number, 0);
3428 assert_eq!(
3429 c1s.epoch_rolling_gas_cost_summary,
3430 GasCostSummary::new(41, 42, 41, 1)
3431 );
3432
3433 assert_eq!(c2t, vec![d(3), d(1)]);
3435 assert_eq!(c2s.previous_digest, Some(c1s.digest()));
3436 assert_eq!(c2s.sequence_number, 1);
3437 assert_eq!(
3438 c2s.epoch_rolling_gas_cost_summary,
3439 GasCostSummary::new(83, 86, 83, 3)
3440 );
3441
3442 let (c3c, c3s) = result.recv().await.unwrap();
3445 let c3t = c3c.iter().map(|d| d.transaction).collect::<Vec<_>>();
3446 assert_eq!(c3s.sequence_number, 2);
3447 assert_eq!(c3s.previous_digest, Some(c2s.digest()));
3448 assert_eq!(c3t, vec![d(10), d(11), d(12), d(13)]);
3449
3450 let (c4c, c4s) = result.recv().await.unwrap();
3451 let c4t = c4c.iter().map(|d| d.transaction).collect::<Vec<_>>();
3452 assert_eq!(c4s.sequence_number, 3);
3453 assert_eq!(c4s.previous_digest, Some(c3s.digest()));
3454 assert_eq!(c4t, vec![d(15), d(16), d(17)]);
3455
3456 let (c5c, c5s) = result.recv().await.unwrap();
3457 let c5t = c5c.iter().map(|d| d.transaction).collect::<Vec<_>>();
3458 assert_eq!(c5s.sequence_number, 4);
3459 assert_eq!(c5s.previous_digest, Some(c4s.digest()));
3460 assert_eq!(c5t, vec![d(5)]);
3461
3462 let (c6c, c6s) = result.recv().await.unwrap();
3463 let c6t = c6c.iter().map(|d| d.transaction).collect::<Vec<_>>();
3464 assert_eq!(c6s.sequence_number, 5);
3465 assert_eq!(c6s.previous_digest, Some(c5s.digest()));
3466 assert_eq!(c6t, vec![d(6)]);
3467
3468 let c1ss = SignedCheckpointSummary::new(c1s.epoch, c1s, state.secret.deref(), state.name);
3469 let c2ss = SignedCheckpointSummary::new(c2s.epoch, c2s, state.secret.deref(), state.name);
3470
3471 checkpoint_service
3472 .notify_checkpoint_signature(&CheckpointSignatureMessage { summary: c2ss })
3473 .unwrap();
3474 checkpoint_service
3475 .notify_checkpoint_signature(&CheckpointSignatureMessage { summary: c1ss })
3476 .unwrap();
3477
3478 let c1sc = certified_result.recv().await.unwrap();
3479 let c2sc = certified_result.recv().await.unwrap();
3480 assert_eq!(c1sc.sequence_number, 0);
3481 assert_eq!(c2sc.sequence_number, 1);
3482 }
3483
3484 impl TransactionCacheRead for HashMap<TransactionDigest, TransactionEffects> {
3485 fn notify_read_executed_effects_may_fail(
3486 &self,
3487 _: &str,
3488 digests: &[TransactionDigest],
3489 ) -> BoxFuture<'_, SuiResult<Vec<TransactionEffects>>> {
3490 std::future::ready(Ok(digests
3491 .iter()
3492 .map(|d| self.get(d).expect("effects not found").clone())
3493 .collect()))
3494 .boxed()
3495 }
3496
3497 fn notify_read_executed_effects_digests(
3498 &self,
3499 _: &str,
3500 digests: &[TransactionDigest],
3501 ) -> BoxFuture<'_, Vec<TransactionEffectsDigest>> {
3502 std::future::ready(
3503 digests
3504 .iter()
3505 .map(|d| {
3506 self.get(d)
3507 .map(|fx| fx.digest())
3508 .expect("effects not found")
3509 })
3510 .collect(),
3511 )
3512 .boxed()
3513 }
3514
3515 fn multi_get_executed_effects(
3516 &self,
3517 digests: &[TransactionDigest],
3518 ) -> Vec<Option<TransactionEffects>> {
3519 digests.iter().map(|d| self.get(d).cloned()).collect()
3520 }
3521
3522 fn multi_get_transaction_blocks(
3528 &self,
3529 _: &[TransactionDigest],
3530 ) -> Vec<Option<Arc<VerifiedTransaction>>> {
3531 unimplemented!()
3532 }
3533
3534 fn multi_get_executed_effects_digests(
3535 &self,
3536 _: &[TransactionDigest],
3537 ) -> Vec<Option<TransactionEffectsDigest>> {
3538 unimplemented!()
3539 }
3540
3541 fn multi_get_effects(
3542 &self,
3543 _: &[TransactionEffectsDigest],
3544 ) -> Vec<Option<TransactionEffects>> {
3545 unimplemented!()
3546 }
3547
3548 fn multi_get_events(&self, _: &[TransactionDigest]) -> Vec<Option<TransactionEvents>> {
3549 unimplemented!()
3550 }
3551
3552 fn take_accumulator_events(&self, _: &TransactionDigest) -> Option<Vec<AccumulatorEvent>> {
3553 unimplemented!()
3554 }
3555
3556 fn get_unchanged_loaded_runtime_objects(
3557 &self,
3558 _digest: &TransactionDigest,
3559 ) -> Option<Vec<sui_types::storage::ObjectKey>> {
3560 unimplemented!()
3561 }
3562
3563 fn transaction_executed_in_last_epoch(&self, _: &TransactionDigest, _: EpochId) -> bool {
3564 unimplemented!()
3565 }
3566 }
3567
3568 #[async_trait::async_trait]
3569 impl CheckpointOutput for mpsc::Sender<(CheckpointContents, CheckpointSummary)> {
3570 async fn checkpoint_created(
3571 &self,
3572 summary: &CheckpointSummary,
3573 contents: &CheckpointContents,
3574 _epoch_store: &Arc<AuthorityPerEpochStore>,
3575 _checkpoint_store: &Arc<CheckpointStore>,
3576 ) -> SuiResult {
3577 self.try_send((contents.clone(), summary.clone())).unwrap();
3578 Ok(())
3579 }
3580 }
3581
3582 #[async_trait::async_trait]
3583 impl CertifiedCheckpointOutput for mpsc::Sender<CertifiedCheckpointSummary> {
3584 async fn certified_checkpoint_created(
3585 &self,
3586 summary: &CertifiedCheckpointSummary,
3587 ) -> SuiResult {
3588 self.try_send(summary.clone()).unwrap();
3589 Ok(())
3590 }
3591 }
3592
3593 fn p(i: u64, t: Vec<u8>, timestamp_ms: u64) -> PendingCheckpoint {
3594 PendingCheckpoint {
3595 roots: vec![CheckpointRoots {
3596 tx_roots: t
3597 .into_iter()
3598 .map(|t| TransactionKey::Digest(d(t)))
3599 .collect(),
3600 settlement_root: None,
3601 height: i,
3602 }],
3603 details: PendingCheckpointInfo {
3604 timestamp_ms,
3605 last_of_epoch: false,
3606 checkpoint_height: i,
3607 consensus_commit_ref: CommitRef::default(),
3608 rejected_transactions_digest: Digest::default(),
3609 checkpoint_seq: i,
3610 },
3611 }
3612 }
3613
3614 fn d(i: u8) -> TransactionDigest {
3615 let mut bytes: [u8; 32] = Default::default();
3616 bytes[0] = i;
3617 TransactionDigest::new(bytes)
3618 }
3619
3620 fn e(
3621 transaction_digest: TransactionDigest,
3622 dependencies: Vec<TransactionDigest>,
3623 gas_used: GasCostSummary,
3624 ) -> TransactionEffects {
3625 let mut effects = TransactionEffects::default();
3626 *effects.transaction_digest_mut_for_testing() = transaction_digest;
3627 *effects.dependencies_mut_for_testing() = dependencies;
3628 *effects.gas_cost_summary_mut_for_testing() = gas_used;
3629 effects
3630 }
3631
3632 fn commit_cert_for_test(
3633 store: &mut HashMap<TransactionDigest, TransactionEffects>,
3634 state: Arc<AuthorityState>,
3635 digest: TransactionDigest,
3636 dependencies: Vec<TransactionDigest>,
3637 gas_used: GasCostSummary,
3638 ) {
3639 let epoch_store = state.epoch_store_for_testing();
3640 let effects = e(digest, dependencies, gas_used);
3641 store.insert(digest, effects.clone());
3642 epoch_store.insert_executed_in_epoch(&digest);
3643 }
3644}