sui_core/
consensus_commit_summary.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Shared helper for db-shell's consensus-commit summary, used by both the
5//! in-process admin API (sui-node) and the direct RocksDB backend (sui-tool).
6
7use consensus_core::{
8    BlockAPI, CommitAPI, CommitIndex, CommitRange, CommitRef, TrustedCommit,
9    storage::{Store as ConsensusStore, rocksdb_store::RocksDBStore},
10};
11use itertools::Itertools;
12use sui_types::messages_consensus::ConsensusTransaction;
13
14/// Summary of a single consensus commit. `tx_keys` entries for rejected
15/// transactions are tagged with a `[rejected]` suffix.
16pub struct ConsensusCommitSummary {
17    pub commit: TrustedCommit,
18    pub tx_keys: Vec<String>,
19    /// Refs of blocks not found in storage; typically indicates corruption or a bug.
20    pub missing_blocks: Vec<String>,
21}
22
23/// Build a summary for the commit at `index`. Returns `None` if not found.
24pub fn build_consensus_commit_summary(
25    cs: &RocksDBStore,
26    index: CommitIndex,
27) -> anyhow::Result<Option<ConsensusCommitSummary>> {
28    let commits = cs
29        .scan_commits(CommitRange::new(index..=index))
30        .map_err(|e| anyhow::anyhow!("{e}"))?;
31    let Some(commit) = commits.into_iter().next() else {
32        return Ok(None);
33    };
34
35    let commit_ref: CommitRef = commit.reference();
36    let block_refs: Vec<_> = commit.blocks().to_vec();
37    let blocks = cs
38        .read_blocks(&block_refs)
39        .map_err(|e| anyhow::anyhow!("{e}"))?;
40    let rejected = cs
41        .read_rejected_transactions(commit_ref)
42        .map_err(|e| anyhow::anyhow!("{e}"))?
43        .unwrap_or_default();
44
45    let mut tx_keys: Vec<String> = Vec::new();
46    let mut missing_blocks: Vec<String> = Vec::new();
47    for (block_ref, block_opt) in block_refs.iter().zip_eq(blocks) {
48        let Some(block) = block_opt else {
49            missing_blocks.push(format!("{block_ref:?}"));
50            continue;
51        };
52        let rejected_indices: std::collections::HashSet<u16> = rejected
53            .get(block_ref)
54            .map(|v| v.iter().copied().collect())
55            .unwrap_or_default();
56        for (i, tx_bytes) in block.transactions_data().iter().enumerate() {
57            if let Ok(tx) = bcs::from_bytes::<ConsensusTransaction>(tx_bytes) {
58                let key = format!("{:?}", tx.key());
59                if rejected_indices.contains(&(i as u16)) {
60                    tx_keys.push(format!("{key} [rejected]"));
61                } else {
62                    tx_keys.push(key);
63                }
64            }
65        }
66    }
67
68    Ok(Some(ConsensusCommitSummary {
69        commit,
70        tx_keys,
71        missing_blocks,
72    }))
73}