瀏覽代碼

ledger-tool: Remove duplicate slot output code (#1255)

This makes the slot subcommand use the same output method as bigtable
block to output blocks (-vv). Doing so creates consistency between the
two commands as well as removing a duplicate implementation in the
ledger-tool code. The shared type also supports json output which the
ledger-tool implementation did not.
steviez 1 年之前
父節點
當前提交
bd09ba69b1

+ 1 - 1
ledger-tool/Cargo.toml

@@ -36,7 +36,7 @@ solana-cost-model = { workspace = true }
 solana-entry = { workspace = true }
 solana-geyser-plugin-manager = { workspace = true }
 solana-gossip = { workspace = true }
-solana-ledger = { workspace = true }
+solana-ledger = { workspace = true, features = ["dev-context-only-utils"] }
 solana-logger = { workspace = true }
 solana-measure = { workspace = true }
 solana-program-runtime = { workspace = true }

+ 6 - 20
ledger-tool/src/bigtable.rs

@@ -2,7 +2,10 @@
 use {
     crate::{
         ledger_path::canonicalize_ledger_path,
-        output::{CliBlockWithEntries, CliEntries, EncodedConfirmedBlockWithEntries},
+        output::{
+            encode_confirmed_block, CliBlockWithEntries, CliEntries,
+            EncodedConfirmedBlockWithEntries,
+        },
     },
     clap::{
         value_t, value_t_or_exit, values_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand,
@@ -25,10 +28,7 @@ use {
     },
     solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature},
     solana_storage_bigtable::CredentialType,
-    solana_transaction_status::{
-        BlockEncodingOptions, ConfirmedBlock, EncodeError, EncodedConfirmedBlock,
-        TransactionDetails, UiTransactionEncoding, VersionedConfirmedBlock,
-    },
+    solana_transaction_status::{ConfirmedBlock, UiTransactionEncoding, VersionedConfirmedBlock},
     std::{
         cmp::min,
         collections::HashSet,
@@ -124,21 +124,7 @@ async fn block(
         .map_err(|err| format!("Failed to connect to storage: {err:?}"))?;
 
     let confirmed_block = bigtable.get_confirmed_block(slot).await?;
-    let encoded_block = confirmed_block
-        .encode_with_options(
-            UiTransactionEncoding::Base64,
-            BlockEncodingOptions {
-                transaction_details: TransactionDetails::Full,
-                show_rewards: true,
-                max_supported_transaction_version: Some(0),
-            },
-        )
-        .map_err(|err| match err {
-            EncodeError::UnsupportedTransactionVersion(version) => {
-                format!("Failed to process unsupported transaction version ({version}) in block")
-            }
-        })?;
-    let encoded_block: EncodedConfirmedBlock = encoded_block.into();
+    let encoded_block = encode_confirmed_block(confirmed_block)?;
 
     if show_entries {
         let entries = bigtable.get_entries(slot).await?;

+ 6 - 6
ledger-tool/src/blockstore.rs

@@ -848,7 +848,7 @@ fn do_blockstore_process_command(ledger_path: &Path, matches: &ArgMatches<'_>) -
                 num_slots,
                 verbose_level,
                 only_rooted,
-            );
+            )?;
         }
         ("print-file-metadata", Some(arg_matches)) => {
             let blockstore =
@@ -1019,20 +1019,20 @@ fn do_blockstore_process_command(ledger_path: &Path, matches: &ArgMatches<'_>) -
         ("slot", Some(arg_matches)) => {
             let slots = values_t_or_exit!(arg_matches, "slots", Slot);
             let allow_dead_slots = arg_matches.is_present("allow_dead_slots");
+            let output_format = OutputFormat::from_matches(arg_matches, "output_format", false);
+
             let blockstore =
                 crate::open_blockstore(&ledger_path, arg_matches, AccessType::Secondary);
             for slot in slots {
                 println!("Slot {slot}");
-                if let Err(err) = output_slot(
+                output_slot(
                     &blockstore,
                     slot,
                     allow_dead_slots,
-                    &OutputFormat::Display,
+                    &output_format,
                     verbose_level,
                     &mut HashMap::new(),
-                ) {
-                    eprintln!("{err}");
-                }
+                )?;
             }
         }
         _ => unreachable!(),

+ 6 - 0
ledger-tool/src/error.rs

@@ -10,9 +10,15 @@ pub enum LedgerToolError {
     #[error("{0}")]
     SerdeJson(#[from] serde_json::Error),
 
+    #[error("{0}")]
+    TransactionEncode(#[from] solana_transaction_status::EncodeError),
+
     #[error("{0}")]
     Io(#[from] std::io::Error),
 
+    #[error("{0}")]
+    Generic(String),
+
     #[error("{0}")]
     BadArgument(String),
 }

+ 112 - 158
ledger-tool/src/output.rs

@@ -1,5 +1,8 @@
 use {
-    crate::ledger_utils::get_program_ids,
+    crate::{
+        error::{LedgerToolError, Result},
+        ledger_utils::get_program_ids,
+    },
     chrono::{Local, TimeZone},
     serde::ser::{Impossible, SerializeSeq, SerializeStruct, Serializer},
     serde_derive::{Deserialize, Serialize},
@@ -9,8 +12,7 @@ use {
         display::writeln_transaction, CliAccount, CliAccountNewConfig, OutputFormat, QuietDisplay,
         VerboseDisplay,
     },
-    solana_entry::entry::Entry,
-    solana_ledger::blockstore::Blockstore,
+    solana_ledger::blockstore::{Blockstore, BlockstoreError},
     solana_runtime::bank::{Bank, TotalAccountsStats},
     solana_sdk::{
         account::{AccountSharedData, ReadableAccount},
@@ -20,7 +22,10 @@ use {
         pubkey::Pubkey,
     },
     solana_transaction_status::{
-        EncodedConfirmedBlock, EncodedTransactionWithStatusMeta, EntrySummary, Rewards,
+        BlockEncodingOptions, ConfirmedBlock, EncodeError, EncodedConfirmedBlock,
+        EncodedTransactionWithStatusMeta, EntrySummary, Rewards, TransactionDetails,
+        UiTransactionEncoding, VersionedConfirmedBlockWithEntries,
+        VersionedTransactionWithStatusMeta,
     },
     std::{
         cell::RefCell,
@@ -28,7 +33,6 @@ use {
         fmt::{self, Display, Formatter},
         io::{stdout, Write},
         rc::Rc,
-        result::Result,
         sync::Arc,
     },
 };
@@ -291,20 +295,20 @@ pub struct EncodedConfirmedBlockWithEntries {
 impl EncodedConfirmedBlockWithEntries {
     pub fn try_from(
         block: EncodedConfirmedBlock,
-        entries_iterator: impl Iterator<Item = EntrySummary>,
-    ) -> Result<Self, String> {
+        entries_iterator: impl IntoIterator<Item = EntrySummary>,
+    ) -> Result<Self> {
         let mut entries = vec![];
-        for (i, entry) in entries_iterator.enumerate() {
+        for (i, entry) in entries_iterator.into_iter().enumerate() {
             let ending_transaction_index = entry
                 .starting_transaction_index
                 .saturating_add(entry.num_transactions as usize);
             let transactions = block
                 .transactions
                 .get(entry.starting_transaction_index..ending_transaction_index)
-                .ok_or(format!(
+                .ok_or(LedgerToolError::Generic(format!(
                     "Mismatched entry data and transactions: entry {:?}",
                     i
-                ))?;
+                )))?;
             entries.push(CliPopulatedEntry {
                 num_hashes: entry.num_hashes,
                 hash: entry.hash.to_string(),
@@ -325,162 +329,116 @@ impl EncodedConfirmedBlockWithEntries {
     }
 }
 
-pub fn output_slot_rewards(blockstore: &Blockstore, slot: Slot, method: &OutputFormat) {
-    // Note: rewards are not output in JSON yet
-    if *method == OutputFormat::Display {
-        if let Ok(Some(rewards)) = blockstore.read_rewards(slot) {
-            if !rewards.is_empty() {
-                println!("  Rewards:");
-                println!(
-                    "    {:<44}  {:^15}  {:<15}  {:<20}  {:>10}",
-                    "Address", "Type", "Amount", "New Balance", "Commission",
-                );
-
-                for reward in rewards {
-                    let sign = if reward.lamports < 0 { "-" } else { "" };
-                    println!(
-                        "    {:<44}  {:^15}  {}◎{:<14.9}  ◎{:<18.9}   {}",
-                        reward.pubkey,
-                        if let Some(reward_type) = reward.reward_type {
-                            format!("{reward_type}")
-                        } else {
-                            "-".to_string()
-                        },
-                        sign,
-                        lamports_to_sol(reward.lamports.unsigned_abs()),
-                        lamports_to_sol(reward.post_balance),
-                        reward
-                            .commission
-                            .map(|commission| format!("{commission:>9}%"))
-                            .unwrap_or_else(|| "    -".to_string())
-                    );
-                }
-            }
-        }
-    }
-}
-
-pub fn output_entry(
-    blockstore: &Blockstore,
-    method: &OutputFormat,
-    slot: Slot,
-    entry_index: usize,
-    entry: Entry,
-) {
-    match method {
-        OutputFormat::Display => {
-            println!(
-                "  Entry {} - num_hashes: {}, hash: {}, transactions: {}",
-                entry_index,
-                entry.num_hashes,
-                entry.hash,
-                entry.transactions.len()
-            );
-            for (transactions_index, transaction) in entry.transactions.into_iter().enumerate() {
-                println!("    Transaction {transactions_index}");
-                let tx_signature = transaction.signatures[0];
-                let tx_status_meta = blockstore
-                    .read_transaction_status((tx_signature, slot))
-                    .unwrap_or_else(|err| {
-                        eprintln!(
-                            "Failed to read transaction status for {} at slot {}: {}",
-                            transaction.signatures[0], slot, err
-                        );
-                        None
-                    })
-                    .map(|meta| meta.into());
-
-                solana_cli_output::display::println_transaction(
-                    &transaction,
-                    tx_status_meta.as_ref(),
-                    "      ",
-                    None,
-                    None,
-                );
-            }
-        }
-        OutputFormat::Json => {
-            // Note: transaction status is not output in JSON yet
-            serde_json::to_writer(stdout(), &entry).expect("serialize entry");
-            stdout().write_all(b",\n").expect("newline");
-        }
-        _ => unreachable!(),
-    }
+pub(crate) fn encode_confirmed_block(
+    confirmed_block: ConfirmedBlock,
+) -> Result<EncodedConfirmedBlock> {
+    let encoded_block = confirmed_block
+        .encode_with_options(
+            UiTransactionEncoding::Base64,
+            BlockEncodingOptions {
+                transaction_details: TransactionDetails::Full,
+                show_rewards: true,
+                max_supported_transaction_version: Some(0),
+            },
+        )
+        .map_err(|err| match err {
+            EncodeError::UnsupportedTransactionVersion(version) => LedgerToolError::Generic(
+                format!("Failed to process unsupported transaction version ({version}) in block"),
+            ),
+        })?;
+    let encoded_block: EncodedConfirmedBlock = encoded_block.into();
+    Ok(encoded_block)
 }
 
 pub fn output_slot(
     blockstore: &Blockstore,
     slot: Slot,
     allow_dead_slots: bool,
-    method: &OutputFormat,
+    output_format: &OutputFormat,
     verbose_level: u64,
     all_program_ids: &mut HashMap<Pubkey, u64>,
-) -> Result<(), String> {
+) -> Result<()> {
     if blockstore.is_dead(slot) {
         if allow_dead_slots {
-            if *method == OutputFormat::Display {
+            if *output_format == OutputFormat::Display {
                 println!(" Slot is dead");
             }
         } else {
-            return Err("Dead slot".to_string());
+            return Err(LedgerToolError::from(BlockstoreError::DeadSlot));
         }
     }
 
-    let (entries, num_shreds, is_full) = blockstore
-        .get_slot_entries_with_shred_info(slot, 0, allow_dead_slots)
-        .map_err(|err| format!("Failed to load entries for slot {slot}: {err:?}"))?;
+    let Some(meta) = blockstore.meta(slot)? else {
+        return Ok(());
+    };
+    let VersionedConfirmedBlockWithEntries { block, entries } = blockstore
+        .get_complete_block_with_entries(
+            slot,
+            /*require_previous_blockhash:*/ false,
+            /*populate_entries:*/ true,
+            allow_dead_slots,
+        )?;
 
-    if *method == OutputFormat::Display {
-        if let Ok(Some(meta)) = blockstore.meta(slot) {
-            if verbose_level >= 1 {
-                println!("  {meta:?} is_full: {is_full}");
-            } else {
-                println!(
-                    "  num_shreds: {}, parent_slot: {:?}, next_slots: {:?}, num_entries: {}, \
-                     is_full: {}",
-                    num_shreds,
-                    meta.parent_slot,
-                    meta.next_slots,
-                    entries.len(),
-                    is_full,
-                );
-            }
+    if verbose_level == 0 {
+        if *output_format == OutputFormat::Display {
+            // Given that Blockstore::get_complete_block_with_entries() returned Ok(_), we know
+            // that we have a full block so meta.consumed is the number of shreds in the block
+            println!(
+                "  num_shreds: {}, parent_slot: {:?}, next_slots: {:?}, num_entries: {}, \
+                 is_full: {}",
+                meta.consumed,
+                meta.parent_slot,
+                meta.next_slots,
+                entries.len(),
+                meta.is_full(),
+            );
         }
-    }
+    } else if verbose_level == 1 {
+        if *output_format == OutputFormat::Display {
+            println!("  {meta:?} is_full: {}", meta.is_full());
 
-    if verbose_level >= 2 {
-        for (entry_index, entry) in entries.into_iter().enumerate() {
-            output_entry(blockstore, method, slot, entry_index, entry);
-        }
+            let mut num_hashes = 0;
+            for entry in entries.iter() {
+                num_hashes += entry.num_hashes;
+            }
 
-        output_slot_rewards(blockstore, slot, method);
-    } else if verbose_level >= 1 {
-        let mut transactions = 0;
-        let mut num_hashes = 0;
-        let mut program_ids = HashMap::new();
-        let blockhash = if let Some(entry) = entries.last() {
-            entry.hash
-        } else {
-            Hash::default()
-        };
+            let blockhash = if let Some(entry) = entries.last() {
+                entry.hash
+            } else {
+                Hash::default()
+            };
 
-        for entry in entries {
-            transactions += entry.transactions.len();
-            num_hashes += entry.num_hashes;
-            for transaction in entry.transactions {
-                for program_id in get_program_ids(&transaction) {
+            let transactions = block.transactions.len();
+            let mut program_ids = HashMap::new();
+            for VersionedTransactionWithStatusMeta { transaction, .. } in block.transactions.iter()
+            {
+                for program_id in get_program_ids(transaction) {
                     *program_ids.entry(*program_id).or_insert(0) += 1;
                 }
             }
-        }
 
-        println!("  Transactions: {transactions}, hashes: {num_hashes}, block_hash: {blockhash}",);
-        for (pubkey, count) in program_ids.iter() {
-            *all_program_ids.entry(*pubkey).or_insert(0) += count;
+            println!(
+                "  Transactions: {transactions}, hashes: {num_hashes}, block_hash: {blockhash}",
+            );
+            for (pubkey, count) in program_ids.iter() {
+                *all_program_ids.entry(*pubkey).or_insert(0) += count;
+            }
+            println!("  Programs:");
+            output_sorted_program_ids(program_ids);
         }
-        println!("  Programs:");
-        output_sorted_program_ids(program_ids);
+    } else {
+        let encoded_block = encode_confirmed_block(ConfirmedBlock::from(block))?;
+        let cli_block = CliBlockWithEntries {
+            encoded_confirmed_block: EncodedConfirmedBlockWithEntries::try_from(
+                encoded_block,
+                entries,
+            )?,
+            slot,
+        };
+
+        println!("{}", output_format.formatted_string(&cli_block));
     }
+
     Ok(())
 }
 
@@ -489,20 +447,15 @@ pub fn output_ledger(
     starting_slot: Slot,
     ending_slot: Slot,
     allow_dead_slots: bool,
-    method: OutputFormat,
+    output_format: OutputFormat,
     num_slots: Option<Slot>,
     verbose_level: u64,
     only_rooted: bool,
-) {
-    let slot_iterator = blockstore
-        .slot_meta_iterator(starting_slot)
-        .unwrap_or_else(|err| {
-            eprintln!("Failed to load entries starting from slot {starting_slot}: {err:?}");
-            std::process::exit(1);
-        });
-
-    if method == OutputFormat::Json {
-        stdout().write_all(b"{\"ledger\":[\n").expect("open array");
+) -> Result<()> {
+    let slot_iterator = blockstore.slot_meta_iterator(starting_slot)?;
+
+    if output_format == OutputFormat::Json {
+        stdout().write_all(b"{\"ledger\":[\n")?;
     }
 
     let num_slots = num_slots.unwrap_or(Slot::MAX);
@@ -516,13 +469,13 @@ pub fn output_ledger(
             break;
         }
 
-        match method {
+        match output_format {
             OutputFormat::Display => {
                 println!("Slot {} root?: {}", slot, blockstore.is_root(slot))
             }
             OutputFormat::Json => {
-                serde_json::to_writer(stdout(), &slot_meta).expect("serialize slot_meta");
-                stdout().write_all(b",\n").expect("newline");
+                serde_json::to_writer(stdout(), &slot_meta)?;
+                stdout().write_all(b",\n")?;
             }
             _ => unreachable!(),
         }
@@ -531,7 +484,7 @@ pub fn output_ledger(
             &blockstore,
             slot,
             allow_dead_slots,
-            &method,
+            &output_format,
             verbose_level,
             &mut all_program_ids,
         ) {
@@ -543,12 +496,13 @@ pub fn output_ledger(
         }
     }
 
-    if method == OutputFormat::Json {
-        stdout().write_all(b"\n]}\n").expect("close array");
+    if output_format == OutputFormat::Json {
+        stdout().write_all(b"\n]}\n")?;
     } else {
         println!("Summary of Programs:");
         output_sorted_program_ids(all_program_ids);
     }
+    Ok(())
 }
 
 pub fn output_sorted_program_ids(program_ids: HashMap<Pubkey, u64>) {
@@ -600,7 +554,7 @@ impl AccountsOutputStreamer {
         }
     }
 
-    pub fn output(&self) -> Result<(), String> {
+    pub fn output(&self) -> std::result::Result<(), String> {
         match self.output_format {
             OutputFormat::Json | OutputFormat::JsonCompact => {
                 let mut serializer = serde_json::Serializer::new(stdout());
@@ -741,7 +695,7 @@ impl AccountsScanner {
 }
 
 impl serde::Serialize for AccountsScanner {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
     where
         S: Serializer,
     {

+ 18 - 41
ledger-tool/tests/basic.rs

@@ -1,12 +1,10 @@
 use {
     assert_cmd::prelude::*,
-    solana_entry::entry,
     solana_ledger::{
         blockstore, blockstore::Blockstore, blockstore_options::ShredStorageType,
-        create_new_tmp_ledger, create_new_tmp_ledger_fifo, genesis_utils::create_genesis_config,
-        get_tmp_ledger_path_auto_delete,
+        create_new_tmp_ledger_auto_delete, create_new_tmp_ledger_fifo_auto_delete,
+        genesis_utils::create_genesis_config, get_tmp_ledger_path_auto_delete,
     },
-    solana_sdk::hash::Hash,
     std::{
         fs,
         path::Path,
@@ -22,10 +20,6 @@ fn run_ledger_tool(args: &[&str]) -> Output {
         .unwrap()
 }
 
-fn count_newlines(chars: &[u8]) -> usize {
-    bytecount::count(chars, b'\n')
-}
-
 #[test]
 fn bad_arguments() {
     // At least a ledger path is required
@@ -37,66 +31,49 @@ fn bad_arguments() {
         .success());
 }
 
-fn nominal_test_helper(ledger_path: &str, ticks: usize) {
-    let meta_lines = 2;
-    let summary_lines = 1;
-
+fn nominal_test_helper(ledger_path: &str) {
     let output = run_ledger_tool(&["-l", ledger_path, "verify"]);
     assert!(output.status.success());
 
-    // Print everything
-    let output = run_ledger_tool(&["-l", ledger_path, "print", "-vvv"]);
+    let output = run_ledger_tool(&["-l", ledger_path, "print", "-vv"]);
     assert!(output.status.success());
-    assert_eq!(
-        count_newlines(&output.stdout)
-            .saturating_sub(meta_lines)
-            .saturating_sub(summary_lines),
-        ticks
-    );
 }
 
 #[test]
 fn nominal_default() {
     let genesis_config = create_genesis_config(100).genesis_config;
-    let (ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config);
-    nominal_test_helper(
-        ledger_path.to_str().unwrap(),
-        genesis_config.ticks_per_slot as usize,
-    );
+    let (ledger_path, _blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
+    nominal_test_helper(ledger_path.path().to_str().unwrap());
 }
 
 #[test]
 fn nominal_fifo() {
     let genesis_config = create_genesis_config(100).genesis_config;
-    let (ledger_path, _blockhash) = create_new_tmp_ledger_fifo!(&genesis_config);
-    nominal_test_helper(
-        ledger_path.to_str().unwrap(),
-        genesis_config.ticks_per_slot as usize,
-    );
+    let (ledger_path, _blockhash) = create_new_tmp_ledger_fifo_auto_delete!(&genesis_config);
+    nominal_test_helper(ledger_path.path().to_str().unwrap());
 }
 
 fn insert_test_shreds(ledger_path: &Path, ending_slot: u64) {
     let blockstore = Blockstore::open(ledger_path).unwrap();
-    for i in 1..ending_slot {
-        let entries = entry::create_ticks(1, 0, Hash::default());
-        let shreds = blockstore::entries_to_test_shreds(
-            &entries, i, 0, false, 0, /*merkle_variant:*/ true,
-        );
-        blockstore.insert_shreds(shreds, None, false).unwrap();
-    }
+    let (shreds, _) = blockstore::make_many_slot_entries(
+        /*start_slot:*/ 0,
+        ending_slot,
+        /*entries_per_slot:*/ 10,
+    );
+    blockstore.insert_shreds(shreds, None, false).unwrap();
 }
 
 fn ledger_tool_copy_test(src_shred_compaction: &str, dst_shred_compaction: &str) {
     let genesis_config = create_genesis_config(100).genesis_config;
 
     let (ledger_path, _blockhash) = match src_shred_compaction {
-        "fifo" => create_new_tmp_ledger_fifo!(&genesis_config),
-        _ => create_new_tmp_ledger!(&genesis_config),
+        "fifo" => create_new_tmp_ledger_fifo_auto_delete!(&genesis_config),
+        _ => create_new_tmp_ledger_auto_delete!(&genesis_config),
     };
     const LEDGER_TOOL_COPY_TEST_SHRED_COUNT: u64 = 25;
     const LEDGER_TOOL_COPY_TEST_ENDING_SLOT: u64 = LEDGER_TOOL_COPY_TEST_SHRED_COUNT + 1;
-    insert_test_shreds(&ledger_path, LEDGER_TOOL_COPY_TEST_ENDING_SLOT);
-    let ledger_path = ledger_path.to_str().unwrap();
+    insert_test_shreds(ledger_path.path(), LEDGER_TOOL_COPY_TEST_ENDING_SLOT);
+    let ledger_path = ledger_path.path().to_str().unwrap();
 
     let target_ledger_path = get_tmp_ledger_path_auto_delete!();
     if dst_shred_compaction == "fifo" {

+ 45 - 8
ledger/src/blockstore.rs

@@ -2559,8 +2559,13 @@ impl Blockstore {
         slot: Slot,
         require_previous_blockhash: bool,
     ) -> Result<VersionedConfirmedBlock> {
-        self.get_complete_block_with_entries(slot, require_previous_blockhash, false)
-            .map(|result| result.block)
+        self.do_get_complete_block_with_entries(
+            slot,
+            require_previous_blockhash,
+            false,
+            /*allow_dead_slots:*/ false,
+        )
+        .map(|result| result.block)
     }
 
     pub fn get_rooted_block_with_entries(
@@ -2574,23 +2579,49 @@ impl Blockstore {
         let _lock = self.check_lowest_cleanup_slot(slot)?;
 
         if self.is_root(slot) {
-            return self.get_complete_block_with_entries(slot, require_previous_blockhash, true);
+            return self.do_get_complete_block_with_entries(
+                slot,
+                require_previous_blockhash,
+                true,
+                /*allow_dead_slots:*/ false,
+            );
         }
         Err(BlockstoreError::SlotNotRooted)
     }
 
-    fn get_complete_block_with_entries(
+    #[cfg(feature = "dev-context-only-utils")]
+    pub fn get_complete_block_with_entries(
         &self,
         slot: Slot,
         require_previous_blockhash: bool,
         populate_entries: bool,
+        allow_dead_slots: bool,
+    ) -> Result<VersionedConfirmedBlockWithEntries> {
+        self.do_get_complete_block_with_entries(
+            slot,
+            require_previous_blockhash,
+            populate_entries,
+            allow_dead_slots,
+        )
+    }
+
+    fn do_get_complete_block_with_entries(
+        &self,
+        slot: Slot,
+        require_previous_blockhash: bool,
+        populate_entries: bool,
+        allow_dead_slots: bool,
     ) -> Result<VersionedConfirmedBlockWithEntries> {
         let Some(slot_meta) = self.meta_cf.get(slot)? else {
-            info!("SlotMeta not found for slot {}", slot);
+            trace!("do_get_complete_block_with_entries() failed for {slot} (missing SlotMeta)");
             return Err(BlockstoreError::SlotUnavailable);
         };
         if slot_meta.is_full() {
-            let slot_entries = self.get_slot_entries(slot, 0)?;
+            let (slot_entries, _, _) = self.get_slot_entries_with_shred_info(
+                slot,
+                /*shred_start_index:*/ 0,
+                allow_dead_slots,
+            )?;
             if !slot_entries.is_empty() {
                 let blockhash = slot_entries
                     .last()
@@ -2630,8 +2661,13 @@ impl Blockstore {
                 let parent_slot_entries = slot_meta
                     .parent_slot
                     .and_then(|parent_slot| {
-                        self.get_slot_entries(parent_slot, /*shred_start_index:*/ 0)
-                            .ok()
+                        self.get_slot_entries_with_shred_info(
+                            parent_slot,
+                            /*shred_start_index:*/ 0,
+                            allow_dead_slots,
+                        )
+                        .ok()
+                        .map(|(entries, _, _)| entries)
                     })
                     .unwrap_or_default();
                 if parent_slot_entries.is_empty() && require_previous_blockhash {
@@ -2670,6 +2706,7 @@ impl Blockstore {
                 return Ok(VersionedConfirmedBlockWithEntries { block, entries });
             }
         }
+        trace!("do_get_complete_block_with_entries() failed for {slot} (slot not full)");
         Err(BlockstoreError::SlotUnavailable)
     }