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