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:
Tyera Eulberg 2021-02-24 16:14:34 -07:00 committed by GitHub
parent 7cb44b1095
commit d5f235d997
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 156 additions and 91 deletions

View File

@ -2,10 +2,12 @@ use {
crate::{
display::{
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,
},
chrono::{Local, TimeZone},
console::{style, Emoji},
inflector::cases::titlecase::to_title_case,
serde::{Deserialize, Serialize},
@ -27,6 +29,7 @@ use {
transaction::Transaction,
},
solana_stake_program::stake_state::{Authorized, Lockup},
solana_transaction_status::EncodedConfirmedBlock,
solana_vote_program::{
authorized_voters::AuthorizedVoters,
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)]
mod tests {
use super::*;

View File

@ -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
pub fn new_spinner_progress_bar() -> ProgressBar {
let progress_bar = ProgressBar::new(42);

View File

@ -3,7 +3,6 @@ use crate::{
spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
stake::is_stake_program_v2_enabled,
};
use chrono::{Local, TimeZone};
use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
use console::{style, Emoji};
use serde::{Deserialize, Serialize};
@ -896,7 +895,7 @@ pub fn process_leader_schedule(
pub fn process_get_block(
rpc_client: &RpcClient,
_config: &CliConfig,
config: &CliConfig,
slot: Option<Slot>,
) -> ProcessResult {
let slot = if let Some(slot) = slot {
@ -905,72 +904,13 @@ pub fn process_get_block(
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)?;
println!("Slot: {}", slot);
println!("Parent Slot: {}", block.parent_slot);
println!("Blockhash: {}", block.blockhash);
println!("Previous Blockhash: {}", block.previous_blockhash);
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())
let cli_block = CliBlock {
encoded_confirmed_block,
slot,
};
Ok(config.output_format.formatted_string(&cli_block))
}
pub fn process_get_block_time(

View File

@ -4,10 +4,10 @@ use solana_clap_utils::{
input_parsers::pubkey_of,
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_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature};
use solana_transaction_status::ConfirmedBlock;
use solana_transaction_status::{ConfirmedBlock, UiTransactionEncoding};
use std::{
path::Path,
process::exit,
@ -48,32 +48,18 @@ async fn first_available_block() -> Result<(), Box<dyn std::error::Error>> {
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)
.await
.map_err(|err| format!("Failed to connect to storage: {:?}", err))?;
let block = bigtable.get_confirmed_block(slot).await?;
println!("Slot: {}", slot);
println!("Parent Slot: {}", block.parent_slot);
println!("Blockhash: {}", block.blockhash);
println!("Previous Blockhash: {}", block.previous_blockhash);
if block.block_time.is_some() {
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,
);
}
let cli_block = CliBlock {
encoded_confirmed_block: block.encode(UiTransactionEncoding::Base64),
slot,
};
println!("{}", output_format.formatted_string(&cli_block));
Ok(())
}
@ -396,6 +382,15 @@ impl BigTableSubCommand for App<'_, '_> {
pub fn bigtable_process_command(ledger_path: &Path, matches: &ArgMatches<'_>) {
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() {
("upload", Some(arg_matches)) => {
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()),
("block", Some(arg_matches)) => {
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)) => {
let starting_slot = value_t_or_exit!(arg_matches, "starting_slot", Slot);

View File

@ -843,6 +843,15 @@ fn main() {
.global(true)
.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()
.subcommand(
SubCommand::with_name("print")