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:
parent
71c1782c74
commit
c5368a3d35
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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: {:?}",
|
||||
|
|
|
@ -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};
|
||||
|
|
Loading…
Reference in New Issue