ledger-tool: read bigtable entries (#34266)

* Add helper to get a protobuf cell without bothering with bincode

* Add conversion back to EntrySummaries

* Add LedgerStorage::get_block_entries

* Add ledger-tool bigtable entries subcommand

* Move CliEntries into ledger_tool::output for now

* Fetch entries with read-only token
This commit is contained in:
Tyera 2023-11-30 14:33:04 -07:00 committed by GitHub
parent 71c1782c74
commit c5368a3d35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 134 additions and 4 deletions

View File

@ -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<dyn std::error::Error>> {
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);

View File

@ -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<CliEntry>,
#[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<EntrySummary> 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,
}
}
}

View File

@ -746,6 +746,14 @@ impl<F: FnMut(Request<()>) -> InterceptedRequestResult> BigTable<F> {
.collect())
}
pub async fn get_protobuf_cell<P>(&mut self, table: &str, key: RowKey) -> Result<P>
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<B, P>(
&mut self,
table: &str,

View File

@ -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<impl Iterator<Item = EntrySummary>> {
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::Entries>("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<TransactionStatus> {
trace!(
"LedgerStorage::get_signature_status request received: {:?}",

View File

@ -1206,6 +1206,17 @@ impl From<(usize, EntrySummary)> for entries::Entry {
}
}
impl From<entries::Entry> 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};