ledger-tool: Add additional modes for accounts subcommand (#34925)

- Add mode to output individual pubkeys
- Add mode to output program accounts
This commit is contained in:
steviez 2024-02-08 20:43:11 -06:00 committed by GitHub
parent 245d1c4087
commit 41f97d7d09
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 128 additions and 21 deletions

View File

@ -6,7 +6,9 @@ use {
blockstore::*, blockstore::*,
ledger_path::*, ledger_path::*,
ledger_utils::*, ledger_utils::*,
output::{output_account, AccountsOutputConfig, AccountsOutputStreamer}, output::{
output_account, AccountsOutputConfig, AccountsOutputMode, AccountsOutputStreamer,
},
program::*, program::*,
}, },
clap::{ clap::{
@ -1312,6 +1314,7 @@ fn main() {
.arg(&geyser_plugin_args) .arg(&geyser_plugin_args)
.arg(&accounts_data_encoding_arg) .arg(&accounts_data_encoding_arg)
.arg(&use_snapshot_archives_at_startup) .arg(&use_snapshot_archives_at_startup)
.arg(&max_genesis_archive_unpacked_size_arg)
.arg( .arg(
Arg::with_name("include_sysvars") Arg::with_name("include_sysvars")
.long("include-sysvars") .long("include-sysvars")
@ -1333,7 +1336,27 @@ fn main() {
.takes_value(false) .takes_value(false)
.help("Do not print account data when printing account contents."), .help("Do not print account data when printing account contents."),
) )
.arg(&max_genesis_archive_unpacked_size_arg), .arg(
Arg::with_name("account")
.long("account")
.takes_value(true)
.value_name("PUBKEY")
.validator(is_pubkey)
.multiple(true)
.help(
"Limit output to accounts corresponding to the specified pubkey(s), \
may be specified multiple times",
),
)
.arg(
Arg::with_name("program_accounts")
.long("program-accounts")
.takes_value(true)
.value_name("PUBKEY")
.validator(is_pubkey)
.conflicts_with("account")
.help("Limit output to accounts owned by the provided program pubkey"),
),
) )
.subcommand( .subcommand(
SubCommand::with_name("capitalization") SubCommand::with_name("capitalization")
@ -2179,7 +2202,18 @@ fn main() {
let include_account_contents = !arg_matches.is_present("no_account_contents"); let include_account_contents = !arg_matches.is_present("no_account_contents");
let include_account_data = !arg_matches.is_present("no_account_data"); let include_account_data = !arg_matches.is_present("no_account_data");
let account_data_encoding = parse_encoding_format(arg_matches); let account_data_encoding = parse_encoding_format(arg_matches);
let mode = if let Some(pubkeys) = pubkeys_of(arg_matches, "account") {
info!("Scanning individual accounts: {pubkeys:?}");
AccountsOutputMode::Individual(pubkeys)
} else if let Some(pubkey) = pubkey_of(arg_matches, "program_accounts") {
info!("Scanning program accounts for {pubkey}");
AccountsOutputMode::Program(pubkey)
} else {
info!("Scanning all accounts");
AccountsOutputMode::All
};
let config = AccountsOutputConfig { let config = AccountsOutputConfig {
mode,
include_sysvars, include_sysvars,
include_account_contents, include_account_contents,
include_account_data, include_account_data,

View File

@ -6,6 +6,7 @@ use {
Deserialize, Serialize, Deserialize, Serialize,
}, },
solana_account_decoder::{UiAccount, UiAccountData, UiAccountEncoding}, solana_account_decoder::{UiAccount, UiAccountData, UiAccountEncoding},
solana_accounts_db::accounts_index::ScanConfig,
solana_cli_output::{ solana_cli_output::{
display::writeln_transaction, CliAccount, CliAccountNewConfig, OutputFormat, QuietDisplay, display::writeln_transaction, CliAccount, CliAccountNewConfig, OutputFormat, QuietDisplay,
VerboseDisplay, VerboseDisplay,
@ -572,7 +573,14 @@ pub struct AccountsOutputStreamer {
output_format: OutputFormat, output_format: OutputFormat,
} }
pub enum AccountsOutputMode {
All,
Individual(Vec<Pubkey>),
Program(Pubkey),
}
pub struct AccountsOutputConfig { pub struct AccountsOutputConfig {
pub mode: AccountsOutputMode,
pub include_sysvars: bool, pub include_sysvars: bool,
pub include_account_contents: bool, pub include_account_contents: bool,
pub include_account_data: bool, pub include_account_data: bool,
@ -608,7 +616,10 @@ impl AccountsOutputStreamer {
.serialize_field("summary", &*self.total_accounts_stats.borrow()) .serialize_field("summary", &*self.total_accounts_stats.borrow())
.map_err(|err| format!("unable to serialize accounts summary: {err}"))?; .map_err(|err| format!("unable to serialize accounts summary: {err}"))?;
SerializeStruct::end(struct_serializer) SerializeStruct::end(struct_serializer)
.map_err(|err| format!("unable to end serialization: {err}")) .map_err(|err| format!("unable to end serialization: {err}"))?;
// The serializer doesn't give us a trailing newline so do it ourselves
println!();
Ok(())
} }
_ => { _ => {
// The compiler needs a placeholder type to satisfy the generic // The compiler needs a placeholder type to satisfy the generic
@ -637,6 +648,33 @@ impl AccountsScanner {
&& (self.config.include_sysvars || !solana_sdk::sysvar::is_sysvar_id(pubkey)) && (self.config.include_sysvars || !solana_sdk::sysvar::is_sysvar_id(pubkey))
} }
fn maybe_output_account<S>(
&self,
seq_serializer: &mut Option<S>,
pubkey: &Pubkey,
account: &AccountSharedData,
slot: Option<Slot>,
cli_account_new_config: &CliAccountNewConfig,
) where
S: SerializeSeq,
{
if self.config.include_account_contents {
if let Some(serializer) = seq_serializer {
let cli_account =
CliAccount::new_with_config(pubkey, account, cli_account_new_config);
serializer.serialize_element(&cli_account).unwrap();
} else {
output_account(
pubkey,
account,
slot,
self.config.include_account_data,
self.config.account_data_encoding,
);
}
}
}
pub fn output<S>(&self, seq_serializer: &mut Option<S>) pub fn output<S>(&self, seq_serializer: &mut Option<S>)
where where
S: SerializeSeq, S: SerializeSeq,
@ -654,26 +692,53 @@ impl AccountsScanner {
.filter(|(pubkey, account, _)| self.should_process_account(account, pubkey)) .filter(|(pubkey, account, _)| self.should_process_account(account, pubkey))
{ {
total_accounts_stats.accumulate_account(pubkey, &account, rent_collector); total_accounts_stats.accumulate_account(pubkey, &account, rent_collector);
self.maybe_output_account(
if self.config.include_account_contents { seq_serializer,
if let Some(serializer) = seq_serializer { pubkey,
let cli_account = &account,
CliAccount::new_with_config(pubkey, &account, &cli_account_new_config); Some(slot),
serializer.serialize_element(&cli_account).unwrap(); &cli_account_new_config,
} else { );
output_account(
pubkey,
&account,
Some(slot),
self.config.include_account_data,
self.config.account_data_encoding,
);
}
}
} }
}; };
self.bank.scan_all_accounts(scan_func).unwrap(); match &self.config.mode {
AccountsOutputMode::All => {
self.bank.scan_all_accounts(scan_func).unwrap();
}
AccountsOutputMode::Individual(pubkeys) => pubkeys.iter().for_each(|pubkey| {
if let Some((account, slot)) = self
.bank
.get_account_modified_slot_with_fixed_root(pubkey)
.filter(|(account, _)| self.should_process_account(account, pubkey))
{
total_accounts_stats.accumulate_account(pubkey, &account, rent_collector);
self.maybe_output_account(
seq_serializer,
pubkey,
&account,
Some(slot),
&cli_account_new_config,
);
}
}),
AccountsOutputMode::Program(program_pubkey) => self
.bank
.get_program_accounts(program_pubkey, &ScanConfig::default())
.unwrap()
.iter()
.filter(|(pubkey, account)| self.should_process_account(account, pubkey))
.for_each(|(pubkey, account)| {
total_accounts_stats.accumulate_account(pubkey, account, rent_collector);
self.maybe_output_account(
seq_serializer,
pubkey,
account,
None,
&cli_account_new_config,
);
}),
}
} }
} }

View File

@ -6039,10 +6039,18 @@ impl Bank {
// pro: safer assertion can be enabled inside AccountsDb // pro: safer assertion can be enabled inside AccountsDb
// con: panics!() if called from off-chain processing // con: panics!() if called from off-chain processing
pub fn get_account_with_fixed_root(&self, pubkey: &Pubkey) -> Option<AccountSharedData> { pub fn get_account_with_fixed_root(&self, pubkey: &Pubkey) -> Option<AccountSharedData> {
self.load_slow_with_fixed_root(&self.ancestors, pubkey) self.get_account_modified_slot_with_fixed_root(pubkey)
.map(|(acc, _slot)| acc) .map(|(acc, _slot)| acc)
} }
// See note above get_account_with_fixed_root() about when to prefer this function
pub fn get_account_modified_slot_with_fixed_root(
&self,
pubkey: &Pubkey,
) -> Option<(AccountSharedData, Slot)> {
self.load_slow_with_fixed_root(&self.ancestors, pubkey)
}
pub fn get_account_modified_slot(&self, pubkey: &Pubkey) -> Option<(AccountSharedData, Slot)> { pub fn get_account_modified_slot(&self, pubkey: &Pubkey) -> Option<(AccountSharedData, Slot)> {
self.load_slow(&self.ancestors, pubkey) self.load_slow(&self.ancestors, pubkey)
} }