Implement OutputFormat for block in Cli and ledger-tool bigtable (#15524)
* Impl DisplayFormat for solana block * Use DisplayFormat in ledger-tool bigtable block
This commit is contained in:
parent
7cb44b1095
commit
d5f235d997
|
@ -2,10 +2,12 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
display::{
|
display::{
|
||||||
build_balance_message, build_balance_message_with_config, format_labeled_address,
|
build_balance_message, build_balance_message_with_config, format_labeled_address,
|
||||||
unix_timestamp_to_string, writeln_name_value, BuildBalanceMessageConfig,
|
unix_timestamp_to_string, writeln_name_value, writeln_transaction,
|
||||||
|
BuildBalanceMessageConfig,
|
||||||
},
|
},
|
||||||
QuietDisplay, VerboseDisplay,
|
QuietDisplay, VerboseDisplay,
|
||||||
},
|
},
|
||||||
|
chrono::{Local, TimeZone},
|
||||||
console::{style, Emoji},
|
console::{style, Emoji},
|
||||||
inflector::cases::titlecase::to_title_case,
|
inflector::cases::titlecase::to_title_case,
|
||||||
serde::{Deserialize, Serialize},
|
serde::{Deserialize, Serialize},
|
||||||
|
@ -27,6 +29,7 @@ use {
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
},
|
},
|
||||||
solana_stake_program::stake_state::{Authorized, Lockup},
|
solana_stake_program::stake_state::{Authorized, Lockup},
|
||||||
|
solana_transaction_status::EncodedConfirmedBlock,
|
||||||
solana_vote_program::{
|
solana_vote_program::{
|
||||||
authorized_voters::AuthorizedVoters,
|
authorized_voters::AuthorizedVoters,
|
||||||
vote_state::{BlockTimestamp, Lockout, MAX_EPOCH_CREDITS_HISTORY, MAX_LOCKOUT_HISTORY},
|
vote_state::{BlockTimestamp, Lockout, MAX_EPOCH_CREDITS_HISTORY, MAX_LOCKOUT_HISTORY},
|
||||||
|
@ -1739,6 +1742,100 @@ impl fmt::Display for CliSignatureVerificationStatus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct CliBlock {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub encoded_confirmed_block: EncodedConfirmedBlock,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
pub slot: Slot,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QuietDisplay for CliBlock {}
|
||||||
|
impl VerboseDisplay for CliBlock {}
|
||||||
|
|
||||||
|
impl fmt::Display for CliBlock {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
writeln!(f, "Slot: {}", self.slot)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"Parent Slot: {}",
|
||||||
|
self.encoded_confirmed_block.parent_slot
|
||||||
|
)?;
|
||||||
|
writeln!(f, "Blockhash: {}", self.encoded_confirmed_block.blockhash)?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"Previous Blockhash: {}",
|
||||||
|
self.encoded_confirmed_block.previous_blockhash
|
||||||
|
)?;
|
||||||
|
if let Some(block_time) = self.encoded_confirmed_block.block_time {
|
||||||
|
writeln!(f, "Block Time: {:?}", Local.timestamp(block_time, 0))?;
|
||||||
|
}
|
||||||
|
if !self.encoded_confirmed_block.rewards.is_empty() {
|
||||||
|
let mut rewards = self.encoded_confirmed_block.rewards.clone();
|
||||||
|
rewards.sort_by(|a, b| a.pubkey.cmp(&b.pubkey));
|
||||||
|
let mut total_rewards = 0;
|
||||||
|
writeln!(f, "Rewards:")?;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" {:<44} {:^15} {:<15} {:<20} {:>14}",
|
||||||
|
"Address", "Type", "Amount", "New Balance", "Percent Change"
|
||||||
|
)?;
|
||||||
|
for reward in rewards {
|
||||||
|
let sign = if reward.lamports < 0 { "-" } else { "" };
|
||||||
|
|
||||||
|
total_rewards += reward.lamports;
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
" {:<44} {:^15} {:>15} {}",
|
||||||
|
reward.pubkey,
|
||||||
|
if let Some(reward_type) = reward.reward_type {
|
||||||
|
format!("{}", reward_type)
|
||||||
|
} else {
|
||||||
|
"-".to_string()
|
||||||
|
},
|
||||||
|
format!(
|
||||||
|
"{}◎{:<14.9}",
|
||||||
|
sign,
|
||||||
|
lamports_to_sol(reward.lamports.abs() as u64)
|
||||||
|
),
|
||||||
|
if reward.post_balance == 0 {
|
||||||
|
" - -".to_string()
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"◎{:<19.9} {:>13.9}%",
|
||||||
|
lamports_to_sol(reward.post_balance),
|
||||||
|
(reward.lamports.abs() as f64
|
||||||
|
/ (reward.post_balance as f64 - reward.lamports as f64))
|
||||||
|
* 100.0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sign = if total_rewards < 0 { "-" } else { "" };
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"Total Rewards: {}◎{:<12.9}",
|
||||||
|
sign,
|
||||||
|
lamports_to_sol(total_rewards.abs() as u64)
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
for (index, transaction_with_meta) in
|
||||||
|
self.encoded_confirmed_block.transactions.iter().enumerate()
|
||||||
|
{
|
||||||
|
writeln!(f, "Transaction {}:", index)?;
|
||||||
|
writeln_transaction(
|
||||||
|
f,
|
||||||
|
&transaction_with_meta.transaction.decode().unwrap(),
|
||||||
|
&transaction_with_meta.meta,
|
||||||
|
" ",
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -294,6 +294,30 @@ pub fn println_transaction(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn writeln_transaction(
|
||||||
|
f: &mut dyn fmt::Write,
|
||||||
|
transaction: &Transaction,
|
||||||
|
transaction_status: &Option<UiTransactionStatusMeta>,
|
||||||
|
prefix: &str,
|
||||||
|
sigverify_status: Option<&[CliSignatureVerificationStatus]>,
|
||||||
|
) -> fmt::Result {
|
||||||
|
let mut w = Vec::new();
|
||||||
|
if write_transaction(
|
||||||
|
&mut w,
|
||||||
|
transaction,
|
||||||
|
transaction_status,
|
||||||
|
prefix,
|
||||||
|
sigverify_status,
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
if let Ok(s) = String::from_utf8(w) {
|
||||||
|
write!(f, "{}", s)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a new process bar for processing that will take an unknown amount of time
|
/// Creates a new process bar for processing that will take an unknown amount of time
|
||||||
pub fn new_spinner_progress_bar() -> ProgressBar {
|
pub fn new_spinner_progress_bar() -> ProgressBar {
|
||||||
let progress_bar = ProgressBar::new(42);
|
let progress_bar = ProgressBar::new(42);
|
||||||
|
|
|
@ -3,7 +3,6 @@ use crate::{
|
||||||
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
|
||||||
stake::is_stake_program_v2_enabled,
|
stake::is_stake_program_v2_enabled,
|
||||||
};
|
};
|
||||||
use chrono::{Local, TimeZone};
|
|
||||||
use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
|
use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||||
use console::{style, Emoji};
|
use console::{style, Emoji};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -896,7 +895,7 @@ pub fn process_leader_schedule(
|
||||||
|
|
||||||
pub fn process_get_block(
|
pub fn process_get_block(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
_config: &CliConfig,
|
config: &CliConfig,
|
||||||
slot: Option<Slot>,
|
slot: Option<Slot>,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let slot = if let Some(slot) = slot {
|
let slot = if let Some(slot) = slot {
|
||||||
|
@ -905,72 +904,13 @@ pub fn process_get_block(
|
||||||
rpc_client.get_slot_with_commitment(CommitmentConfig::finalized())?
|
rpc_client.get_slot_with_commitment(CommitmentConfig::finalized())?
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut block =
|
let encoded_confirmed_block =
|
||||||
rpc_client.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Base64)?;
|
rpc_client.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Base64)?;
|
||||||
|
let cli_block = CliBlock {
|
||||||
println!("Slot: {}", slot);
|
encoded_confirmed_block,
|
||||||
println!("Parent Slot: {}", block.parent_slot);
|
slot,
|
||||||
println!("Blockhash: {}", block.blockhash);
|
};
|
||||||
println!("Previous Blockhash: {}", block.previous_blockhash);
|
Ok(config.output_format.formatted_string(&cli_block))
|
||||||
if let Some(block_time) = block.block_time {
|
|
||||||
println!("Block Time: {:?}", Local.timestamp(block_time, 0));
|
|
||||||
}
|
|
||||||
if !block.rewards.is_empty() {
|
|
||||||
block.rewards.sort_by(|a, b| a.pubkey.cmp(&b.pubkey));
|
|
||||||
let mut total_rewards = 0;
|
|
||||||
println!("Rewards:",);
|
|
||||||
println!(
|
|
||||||
" {:<44} {:^15} {:<15} {:<20} {:>14}",
|
|
||||||
"Address", "Type", "Amount", "New Balance", "Percent Change"
|
|
||||||
);
|
|
||||||
for reward in block.rewards {
|
|
||||||
let sign = if reward.lamports < 0 { "-" } else { "" };
|
|
||||||
|
|
||||||
total_rewards += reward.lamports;
|
|
||||||
println!(
|
|
||||||
" {:<44} {:^15} {:>15} {}",
|
|
||||||
reward.pubkey,
|
|
||||||
if let Some(reward_type) = reward.reward_type {
|
|
||||||
format!("{}", reward_type)
|
|
||||||
} else {
|
|
||||||
"-".to_string()
|
|
||||||
},
|
|
||||||
format!(
|
|
||||||
"{}◎{:<14.9}",
|
|
||||||
sign,
|
|
||||||
lamports_to_sol(reward.lamports.abs() as u64)
|
|
||||||
),
|
|
||||||
if reward.post_balance == 0 {
|
|
||||||
" - -".to_string()
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"◎{:<19.9} {:>13.9}%",
|
|
||||||
lamports_to_sol(reward.post_balance),
|
|
||||||
(reward.lamports.abs() as f64
|
|
||||||
/ (reward.post_balance as f64 - reward.lamports as f64))
|
|
||||||
* 100.0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let sign = if total_rewards < 0 { "-" } else { "" };
|
|
||||||
println!(
|
|
||||||
"Total Rewards: {}◎{:<12.9}",
|
|
||||||
sign,
|
|
||||||
lamports_to_sol(total_rewards.abs() as u64)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
for (index, transaction_with_meta) in block.transactions.iter().enumerate() {
|
|
||||||
println!("Transaction {}:", index);
|
|
||||||
println_transaction(
|
|
||||||
&transaction_with_meta.transaction.decode().unwrap(),
|
|
||||||
&transaction_with_meta.meta,
|
|
||||||
" ",
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_get_block_time(
|
pub fn process_get_block_time(
|
||||||
|
|
|
@ -4,10 +4,10 @@ use solana_clap_utils::{
|
||||||
input_parsers::pubkey_of,
|
input_parsers::pubkey_of,
|
||||||
input_validators::{is_slot, is_valid_pubkey},
|
input_validators::{is_slot, is_valid_pubkey},
|
||||||
};
|
};
|
||||||
use solana_cli_output::display::println_transaction;
|
use solana_cli_output::{display::println_transaction, CliBlock, OutputFormat};
|
||||||
use solana_ledger::{blockstore::Blockstore, blockstore_db::AccessType};
|
use solana_ledger::{blockstore::Blockstore, blockstore_db::AccessType};
|
||||||
use solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature};
|
use solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature};
|
||||||
use solana_transaction_status::ConfirmedBlock;
|
use solana_transaction_status::{ConfirmedBlock, UiTransactionEncoding};
|
||||||
use std::{
|
use std::{
|
||||||
path::Path,
|
path::Path,
|
||||||
process::exit,
|
process::exit,
|
||||||
|
@ -48,32 +48,18 @@ async fn first_available_block() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn block(slot: Slot) -> Result<(), Box<dyn std::error::Error>> {
|
async fn block(slot: Slot, output_format: OutputFormat) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let bigtable = solana_storage_bigtable::LedgerStorage::new(false, None)
|
let bigtable = solana_storage_bigtable::LedgerStorage::new(false, None)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| format!("Failed to connect to storage: {:?}", err))?;
|
.map_err(|err| format!("Failed to connect to storage: {:?}", err))?;
|
||||||
|
|
||||||
let block = bigtable.get_confirmed_block(slot).await?;
|
let block = bigtable.get_confirmed_block(slot).await?;
|
||||||
|
|
||||||
println!("Slot: {}", slot);
|
let cli_block = CliBlock {
|
||||||
println!("Parent Slot: {}", block.parent_slot);
|
encoded_confirmed_block: block.encode(UiTransactionEncoding::Base64),
|
||||||
println!("Blockhash: {}", block.blockhash);
|
slot,
|
||||||
println!("Previous Blockhash: {}", block.previous_blockhash);
|
};
|
||||||
if block.block_time.is_some() {
|
println!("{}", output_format.formatted_string(&cli_block));
|
||||||
println!("Block Time: {:?}", block.block_time);
|
|
||||||
}
|
|
||||||
if !block.rewards.is_empty() {
|
|
||||||
println!("Rewards: {:?}", block.rewards);
|
|
||||||
}
|
|
||||||
for (index, transaction_with_meta) in block.transactions.into_iter().enumerate() {
|
|
||||||
println!("Transaction {}:", index);
|
|
||||||
println_transaction(
|
|
||||||
&transaction_with_meta.transaction,
|
|
||||||
&transaction_with_meta.meta.map(|meta| meta.into()),
|
|
||||||
" ",
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,6 +382,15 @@ impl BigTableSubCommand for App<'_, '_> {
|
||||||
pub fn bigtable_process_command(ledger_path: &Path, matches: &ArgMatches<'_>) {
|
pub fn bigtable_process_command(ledger_path: &Path, matches: &ArgMatches<'_>) {
|
||||||
let runtime = tokio::runtime::Runtime::new().unwrap();
|
let runtime = tokio::runtime::Runtime::new().unwrap();
|
||||||
|
|
||||||
|
let output_format = matches
|
||||||
|
.value_of("output_format")
|
||||||
|
.map(|value| match value {
|
||||||
|
"json" => OutputFormat::Json,
|
||||||
|
"json-compact" => OutputFormat::JsonCompact,
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
.unwrap_or(OutputFormat::Display);
|
||||||
|
|
||||||
let future = match matches.subcommand() {
|
let future = match matches.subcommand() {
|
||||||
("upload", Some(arg_matches)) => {
|
("upload", Some(arg_matches)) => {
|
||||||
let starting_slot = value_t!(arg_matches, "starting_slot", Slot).unwrap_or(0);
|
let starting_slot = value_t!(arg_matches, "starting_slot", Slot).unwrap_or(0);
|
||||||
|
@ -416,7 +411,7 @@ pub fn bigtable_process_command(ledger_path: &Path, matches: &ArgMatches<'_>) {
|
||||||
("first-available-block", Some(_arg_matches)) => runtime.block_on(first_available_block()),
|
("first-available-block", Some(_arg_matches)) => runtime.block_on(first_available_block()),
|
||||||
("block", Some(arg_matches)) => {
|
("block", Some(arg_matches)) => {
|
||||||
let slot = value_t_or_exit!(arg_matches, "slot", Slot);
|
let slot = value_t_or_exit!(arg_matches, "slot", Slot);
|
||||||
runtime.block_on(block(slot))
|
runtime.block_on(block(slot, output_format))
|
||||||
}
|
}
|
||||||
("blocks", Some(arg_matches)) => {
|
("blocks", Some(arg_matches)) => {
|
||||||
let starting_slot = value_t_or_exit!(arg_matches, "starting_slot", Slot);
|
let starting_slot = value_t_or_exit!(arg_matches, "starting_slot", Slot);
|
||||||
|
|
|
@ -843,6 +843,15 @@ fn main() {
|
||||||
.global(true)
|
.global(true)
|
||||||
.help("Use DIR for ledger location"),
|
.help("Use DIR for ledger location"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("output_format")
|
||||||
|
.long("output")
|
||||||
|
.value_name("FORMAT")
|
||||||
|
.global(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.possible_values(&["json", "json-compact"])
|
||||||
|
.help("Return information in specified output format, currently only available for bigtable subcommands"),
|
||||||
|
)
|
||||||
.bigtable_subcommand()
|
.bigtable_subcommand()
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("print")
|
SubCommand::with_name("print")
|
||||||
|
|
Loading…
Reference in New Issue