diff --git a/ledger-tool/src/bigtable.rs b/ledger-tool/src/bigtable.rs index c4d5c77f30..6de86f4e0b 100644 --- a/ledger-tool/src/bigtable.rs +++ b/ledger-tool/src/bigtable.rs @@ -1,6 +1,6 @@ //! The `bigtable` subcommand use { - crate::ledger_path::canonicalize_ledger_path, + crate::{ledger_path::canonicalize_ledger_path, output::CliEntries}, clap::{ value_t, value_t_or_exit, values_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand, }, @@ -143,6 +143,24 @@ async fn block( Ok(()) } +async fn entries( + slot: Slot, + output_format: OutputFormat, + config: solana_storage_bigtable::LedgerStorageConfig, +) -> Result<(), Box> { + let bigtable = solana_storage_bigtable::LedgerStorage::new_with_config(config) + .await + .map_err(|err| format!("Failed to connect to storage: {err:?}"))?; + + let entries = bigtable.get_entries(slot).await?; + let cli_entries = CliEntries { + entries: entries.map(Into::into).collect(), + slot, + }; + println!("{}", output_format.formatted_string(&cli_entries)); + Ok(()) +} + async fn blocks( starting_slot: Slot, limit: usize, @@ -769,6 +787,19 @@ impl BigTableSubCommand for App<'_, '_> { .required(true), ), ) + .subcommand( + SubCommand::with_name("entries") + .about("Get the entry data for a block") + .arg( + Arg::with_name("slot") + .long("slot") + .validator(is_slot) + .value_name("SLOT") + .takes_value(true) + .index(1) + .required(true), + ) + ) .subcommand( SubCommand::with_name("confirm") .about("Confirm transaction by signature") @@ -1061,6 +1092,16 @@ pub fn bigtable_process_command(ledger_path: &Path, matches: &ArgMatches<'_>) { }; runtime.block_on(block(slot, output_format, config)) } + ("entries", Some(arg_matches)) => { + let slot = value_t_or_exit!(arg_matches, "slot", Slot); + let config = solana_storage_bigtable::LedgerStorageConfig { + read_only: true, + instance_name, + app_profile_id, + ..solana_storage_bigtable::LedgerStorageConfig::default() + }; + runtime.block_on(entries(slot, output_format, config)) + } ("blocks", Some(arg_matches)) => { let starting_slot = value_t_or_exit!(arg_matches, "starting_slot", Slot); let limit = value_t_or_exit!(arg_matches, "limit", usize); diff --git a/ledger-tool/src/output.rs b/ledger-tool/src/output.rs index 46c2a62f1b..2c0db44372 100644 --- a/ledger-tool/src/output.rs +++ b/ledger-tool/src/output.rs @@ -1,7 +1,9 @@ use { - serde::Serialize, + serde::{Deserialize, Serialize}, solana_cli_output::{QuietDisplay, VerboseDisplay}, - std::fmt::{Display, Formatter, Result}, + solana_sdk::clock::Slot, + solana_transaction_status::EntrySummary, + std::fmt::{self, Display, Formatter, Result}, }; #[derive(Serialize, Debug, Default)] @@ -67,3 +69,52 @@ impl Display for SlotBounds<'_> { Ok(()) } } + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CliEntries { + pub entries: Vec, + #[serde(skip_serializing)] + pub slot: Slot, +} + +impl QuietDisplay for CliEntries {} +impl VerboseDisplay for CliEntries {} + +impl fmt::Display for CliEntries { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "Slot {}", self.slot)?; + for (i, entry) in self.entries.iter().enumerate() { + writeln!( + f, + " Entry {} - num_hashes: {}, hash: {}, transactions: {}, starting_transaction_index: {}", + i, + entry.num_hashes, + entry.hash, + entry.num_transactions, + entry.starting_transaction_index, + )?; + } + Ok(()) + } +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CliEntry { + num_hashes: u64, + hash: String, + num_transactions: u64, + starting_transaction_index: usize, +} + +impl From for CliEntry { + fn from(entry_summary: EntrySummary) -> Self { + Self { + num_hashes: entry_summary.num_hashes, + hash: entry_summary.hash.to_string(), + num_transactions: entry_summary.num_transactions, + starting_transaction_index: entry_summary.starting_transaction_index, + } + } +} diff --git a/storage-bigtable/src/bigtable.rs b/storage-bigtable/src/bigtable.rs index f9454a91b8..3eeee6f6eb 100644 --- a/storage-bigtable/src/bigtable.rs +++ b/storage-bigtable/src/bigtable.rs @@ -746,6 +746,14 @@ impl) -> InterceptedRequestResult> BigTable { .collect()) } + pub async fn get_protobuf_cell

(&mut self, table: &str, key: RowKey) -> Result

+ where + P: prost::Message + Default, + { + let row_data = self.get_single_row_data(table, key.clone()).await?; + deserialize_protobuf_cell_data(&row_data, table, key.to_string()) + } + pub async fn get_protobuf_or_bincode_cell( &mut self, table: &str, diff --git a/storage-bigtable/src/lib.rs b/storage-bigtable/src/lib.rs index 0e7972fb68..1feba4d93f 100644 --- a/storage-bigtable/src/lib.rs +++ b/storage-bigtable/src/lib.rs @@ -18,7 +18,7 @@ use { solana_storage_proto::convert::{entries, generated, tx_by_addr}, solana_transaction_status::{ extract_and_fmt_memos, ConfirmedBlock, ConfirmedTransactionStatusWithSignature, - ConfirmedTransactionWithStatusMeta, Reward, TransactionByAddrInfo, + ConfirmedTransactionWithStatusMeta, EntrySummary, Reward, TransactionByAddrInfo, TransactionConfirmationStatus, TransactionStatus, TransactionStatusMeta, TransactionWithStatusMeta, VersionedConfirmedBlock, VersionedConfirmedBlockWithEntries, VersionedTransactionWithStatusMeta, @@ -611,6 +611,25 @@ impl LedgerStorage { Ok(block_exists) } + /// Fetches a vector of block entries via a multirow fetch + pub async fn get_entries(&self, slot: Slot) -> Result> { + trace!( + "LedgerStorage::get_block_entries request received: {:?}", + slot + ); + self.stats.increment_num_queries(); + let mut bigtable = self.connection.client(); + let entry_cell_data = bigtable + .get_protobuf_cell::("entries", slot_to_entries_key(slot)) + .await + .map_err(|err| match err { + bigtable::Error::RowNotFound => Error::BlockNotFound(slot), + _ => err.into(), + })?; + let entries = entry_cell_data.entries.into_iter().map(Into::into); + Ok(entries) + } + pub async fn get_signature_status(&self, signature: &Signature) -> Result { trace!( "LedgerStorage::get_signature_status request received: {:?}", diff --git a/storage-proto/src/convert.rs b/storage-proto/src/convert.rs index abb1e84885..e907095194 100644 --- a/storage-proto/src/convert.rs +++ b/storage-proto/src/convert.rs @@ -1206,6 +1206,17 @@ impl From<(usize, EntrySummary)> for entries::Entry { } } +impl From for EntrySummary { + fn from(entry: entries::Entry) -> Self { + EntrySummary { + num_hashes: entry.num_hashes, + hash: Hash::new(&entry.hash), + num_transactions: entry.num_transactions, + starting_transaction_index: entry.starting_transaction_index as usize, + } + } +} + #[cfg(test)] mod test { use {super::*, enum_iterator::all};