2021-02-16 13:48:20 -08:00
#![ allow(clippy::integer_arithmetic) ]
2021-12-02 16:23:51 -08:00
use {
2023-05-10 12:25:38 -07:00
crate ::{ args ::* , bigtable ::* , ledger_path ::* , ledger_utils ::* , output ::* , program ::* } ,
2022-05-24 12:03:28 -07:00
chrono ::{ DateTime , Utc } ,
2021-12-02 16:23:51 -08:00
clap ::{
crate_description , crate_name , value_t , value_t_or_exit , values_t_or_exit , App ,
AppSettings , Arg , ArgMatches , SubCommand ,
2020-12-04 22:46:32 -08:00
} ,
2021-12-02 16:23:51 -08:00
dashmap ::DashMap ,
itertools ::Itertools ,
log ::* ,
regex ::Regex ,
2022-10-18 13:03:13 -07:00
serde ::{
ser ::{ SerializeSeq , Serializer } ,
Serialize ,
} ,
2021-12-02 16:23:51 -08:00
serde_json ::json ,
2022-09-18 21:52:53 -07:00
solana_account_decoder ::{ UiAccount , UiAccountData , UiAccountEncoding } ,
2021-12-02 16:23:51 -08:00
solana_clap_utils ::{
2023-03-22 01:59:17 -07:00
hidden_unless_forced ,
2021-12-02 16:23:51 -08:00
input_parsers ::{ cluster_type_of , pubkey_of , pubkeys_of } ,
input_validators ::{
is_parsable , is_pow2 , is_pubkey , is_pubkey_or_keypair , is_slot , is_valid_percentage ,
2023-03-30 08:16:36 -07:00
validate_maximum_full_snapshot_archives_to_retain ,
validate_maximum_incremental_snapshot_archives_to_retain ,
2021-12-02 16:23:51 -08:00
} ,
2021-07-22 12:40:37 -07:00
} ,
2022-10-18 13:03:13 -07:00
solana_cli_output ::{ CliAccount , CliAccountNewConfig , OutputFormat } ,
2023-03-22 20:57:28 -07:00
solana_core ::{
system_monitor_service ::{ SystemMonitorService , SystemMonitorStatsReportConfig } ,
validator ::BlockVerificationMethod ,
} ,
2021-12-02 16:23:51 -08:00
solana_entry ::entry ::Entry ,
solana_ledger ::{
ancestor_iterator ::AncestorIterator ,
2023-04-27 11:11:04 -07:00
blockstore ::{ create_new_ledger , Blockstore , PurgeType } ,
2022-09-06 21:46:35 -07:00
blockstore_db ::{ self , columns as cf , Column , ColumnName , Database } ,
2022-05-26 16:59:26 -07:00
blockstore_options ::{
2023-04-27 11:11:04 -07:00
AccessType , BlockstoreRecoveryMode , LedgerColumnOptions ,
BLOCKSTORE_DIRECTORY_ROCKS_FIFO ,
2023-02-09 12:34:20 -08:00
} ,
2023-04-27 11:11:04 -07:00
blockstore_processor ::ProcessOptions ,
2021-12-02 16:23:51 -08:00
shred ::Shred ,
} ,
2022-06-22 10:17:43 -07:00
solana_measure ::{ measure , measure ::Measure } ,
2021-12-02 16:23:51 -08:00
solana_runtime ::{
2022-10-18 13:03:13 -07:00
accounts ::Accounts ,
2023-04-27 11:11:04 -07:00
accounts_db ::CalcAccountsHashDataSource ,
accounts_index ::ScanConfig ,
2022-10-18 13:03:13 -07:00
bank ::{ Bank , RewardCalculationEvent , TotalAccountsStats } ,
2021-12-02 16:23:51 -08:00
bank_forks ::BankForks ,
cost_model ::CostModel ,
cost_tracker ::CostTracker ,
2023-04-27 11:11:04 -07:00
hardened_unpack ::MAX_GENESIS_ARCHIVE_UNPACKED_SIZE ,
2022-04-11 17:28:10 -07:00
runtime_config ::RuntimeConfig ,
2021-12-02 16:23:51 -08:00
snapshot_archive_info ::SnapshotArchiveInfoGetter ,
2022-06-22 10:17:43 -07:00
snapshot_minimizer ::SnapshotMinimizer ,
2021-12-02 16:23:51 -08:00
snapshot_utils ::{
2023-04-27 11:11:04 -07:00
self , ArchiveFormat , SnapshotVersion , DEFAULT_ARCHIVE_COMPRESSION ,
SUPPORTED_ARCHIVE_COMPRESSION ,
2021-12-02 16:23:51 -08:00
} ,
} ,
solana_sdk ::{
account ::{ AccountSharedData , ReadableAccount , WritableAccount } ,
account_utils ::StateMut ,
clock ::{ Epoch , Slot } ,
2022-04-26 14:49:35 -07:00
feature ::{ self , Feature } ,
2022-11-22 09:55:56 -08:00
feature_set ::{ self , FeatureSet } ,
2023-04-27 11:11:04 -07:00
genesis_config ::ClusterType ,
2021-12-02 16:23:51 -08:00
hash ::Hash ,
inflation ::Inflation ,
native_token ::{ lamports_to_sol , sol_to_lamports , Sol } ,
pubkey ::Pubkey ,
rent ::Rent ,
shred_version ::compute_shred_version ,
stake ::{ self , state ::StakeState } ,
system_program ,
2023-03-03 12:18:57 -08:00
transaction ::{
MessageHash , SanitizedTransaction , SimpleAddressLoader , VersionedTransaction ,
} ,
2021-12-02 16:23:51 -08:00
} ,
solana_stake_program ::stake_state ::{ self , PointValue } ,
solana_vote_program ::{
self ,
vote_state ::{ self , VoteState } ,
} ,
std ::{
collections ::{ BTreeMap , BTreeSet , HashMap , HashSet } ,
ffi ::OsStr ,
fs ::File ,
io ::{ self , stdout , BufRead , BufReader , Write } ,
2023-03-30 08:16:36 -07:00
num ::NonZeroUsize ,
2021-12-02 16:23:51 -08:00
path ::{ Path , PathBuf } ,
2023-03-16 18:30:10 -07:00
process ::{ exit , Command , Stdio } ,
2021-12-02 16:23:51 -08:00
str ::FromStr ,
sync ::{
atomic ::{ AtomicBool , Ordering } ,
Arc , RwLock ,
} ,
2022-05-24 12:03:28 -07:00
time ::{ Duration , UNIX_EPOCH } ,
2021-11-15 07:29:30 -08:00
} ,
2019-11-04 18:10:06 -08:00
} ;
2018-08-04 14:31:12 -07:00
2023-04-27 11:11:04 -07:00
mod args ;
2020-07-20 21:06:13 -07:00
mod bigtable ;
2021-12-02 16:23:51 -08:00
mod ledger_path ;
2023-04-27 11:11:04 -07:00
mod ledger_utils ;
2023-01-12 21:21:04 -08:00
mod output ;
2023-05-10 12:25:38 -07:00
mod program ;
2020-06-02 21:32:44 -07:00
2022-05-22 18:00:42 -07:00
#[ derive(PartialEq, Eq) ]
2019-07-11 20:33:36 -07:00
enum LedgerOutputMethod {
Print ,
Json ,
}
2019-08-09 15:57:31 -07:00
2023-03-03 12:18:57 -08:00
fn get_program_ids ( tx : & VersionedTransaction ) -> impl Iterator < Item = & Pubkey > + '_ {
let message = & tx . message ;
let account_keys = message . static_account_keys ( ) ;
message
. instructions ( )
. iter ( )
. map ( | ix | ix . program_id ( account_keys ) )
}
2022-11-21 10:15:14 -08:00
fn parse_encoding_format ( matches : & ArgMatches < '_ > ) -> UiAccountEncoding {
match matches . value_of ( " encoding " ) {
Some ( " jsonParsed " ) = > UiAccountEncoding ::JsonParsed ,
Some ( " base64 " ) = > UiAccountEncoding ::Base64 ,
Some ( " base64+zstd " ) = > UiAccountEncoding ::Base64Zstd ,
_ = > UiAccountEncoding ::Base64 ,
}
}
2020-12-13 17:26:34 -08:00
fn output_slot_rewards ( blockstore : & Blockstore , slot : Slot , method : & LedgerOutputMethod ) {
2020-03-24 05:23:29 -07:00
// Note: rewards are not output in JSON yet
if * method = = LedgerOutputMethod ::Print {
2020-12-13 17:26:34 -08:00
if let Ok ( Some ( rewards ) ) = blockstore . read_rewards ( slot ) {
if ! rewards . is_empty ( ) {
println! ( " Rewards: " ) ;
2021-05-26 14:43:15 -07:00
println! (
2021-07-10 23:18:42 -07:00
" {:<44} {:^15} {:<15} {:<20} {:>10} " ,
" Address " , " Type " , " Amount " , " New Balance " , " Commission " ,
2021-05-26 14:43:15 -07:00
) ;
2020-12-13 17:26:34 -08:00
for reward in rewards {
2021-05-26 14:43:15 -07:00
let sign = if reward . lamports < 0 { " - " } else { " " } ;
2020-12-13 17:26:34 -08:00
println! (
2021-10-22 21:25:54 -07:00
" {:<44} {:^15} {}◎{:<14.9} ◎{:<18.9} {} " ,
2020-12-13 17:26:34 -08:00
reward . pubkey ,
2021-05-26 14:43:15 -07:00
if let Some ( reward_type ) = reward . reward_type {
2022-12-06 06:30:06 -08:00
format! ( " {reward_type} " )
2021-05-26 14:43:15 -07:00
} else {
" - " . to_string ( )
} ,
2021-10-22 21:25:54 -07:00
sign ,
2022-04-20 10:01:33 -07:00
lamports_to_sol ( reward . lamports . unsigned_abs ( ) ) ,
2021-10-22 21:25:54 -07:00
lamports_to_sol ( reward . post_balance ) ,
2021-07-10 23:18:42 -07:00
reward
. commission
2022-12-06 06:30:06 -08:00
. map ( | commission | format! ( " {commission:>9} % " ) )
2021-07-10 23:18:42 -07:00
. unwrap_or_else ( | | " - " . to_string ( ) )
2020-12-13 17:26:34 -08:00
) ;
2020-03-24 05:23:29 -07:00
}
}
}
}
}
2020-06-27 10:47:02 -07:00
fn output_entry (
blockstore : & Blockstore ,
method : & LedgerOutputMethod ,
slot : Slot ,
entry_index : usize ,
2021-08-17 15:17:56 -07:00
entry : Entry ,
2020-06-27 10:47:02 -07:00
) {
match method {
LedgerOutputMethod ::Print = > {
println! (
2021-12-07 13:14:39 -08:00
" Entry {} - num_hashes: {}, hash: {}, transactions: {} " ,
2020-06-27 10:47:02 -07:00
entry_index ,
entry . num_hashes ,
entry . hash ,
entry . transactions . len ( )
) ;
2021-08-17 15:17:56 -07:00
for ( transactions_index , transaction ) in entry . transactions . into_iter ( ) . enumerate ( ) {
2022-12-06 06:30:06 -08:00
println! ( " Transaction {transactions_index} " ) ;
2021-08-17 15:17:56 -07:00
let tx_signature = transaction . signatures [ 0 ] ;
2022-05-20 19:56:06 -07:00
let tx_status_meta = blockstore
2021-08-17 15:17:56 -07:00
. read_transaction_status ( ( tx_signature , slot ) )
2020-06-27 10:47:02 -07:00
. unwrap_or_else ( | err | {
eprintln! (
" Failed to read transaction status for {} at slot {}: {} " ,
transaction . signatures [ 0 ] , slot , err
) ;
None
} )
2022-05-20 19:56:06 -07:00
. map ( | meta | meta . into ( ) ) ;
2020-06-27 10:47:02 -07:00
2022-05-20 19:56:06 -07:00
solana_cli_output ::display ::println_transaction (
& transaction ,
tx_status_meta . as_ref ( ) ,
" " ,
None ,
None ,
) ;
2020-06-27 10:47:02 -07:00
}
}
LedgerOutputMethod ::Json = > {
// Note: transaction status is not output in JSON yet
serde_json ::to_writer ( stdout ( ) , & entry ) . expect ( " serialize entry " ) ;
stdout ( ) . write_all ( b " , \n " ) . expect ( " newline " ) ;
}
}
}
2020-03-21 21:43:33 -07:00
fn output_slot (
blockstore : & Blockstore ,
slot : Slot ,
2020-04-09 20:10:51 -07:00
allow_dead_slots : bool ,
2020-03-21 21:43:33 -07:00
method : & LedgerOutputMethod ,
2020-06-27 10:47:02 -07:00
verbose_level : u64 ,
2022-05-19 14:26:29 -07:00
all_program_ids : & mut HashMap < Pubkey , u64 > ,
2020-03-21 21:43:33 -07:00
) -> Result < ( ) , String > {
2020-04-09 20:10:51 -07:00
if blockstore . is_dead ( slot ) {
if allow_dead_slots {
if * method = = LedgerOutputMethod ::Print {
2020-06-27 10:47:02 -07:00
println! ( " Slot is dead " ) ;
2020-04-09 20:10:51 -07:00
}
} else {
return Err ( " Dead slot " . to_string ( ) ) ;
}
}
2021-10-13 03:50:18 -07:00
let ( entries , num_shreds , is_full ) = blockstore
2020-04-09 20:10:51 -07:00
. get_slot_entries_with_shred_info ( slot , 0 , allow_dead_slots )
2022-12-06 06:30:06 -08:00
. map_err ( | err | format! ( " Failed to load entries for slot {slot} : {err:?} " ) ) ? ;
2019-08-09 15:57:31 -07:00
2020-04-09 20:10:51 -07:00
if * method = = LedgerOutputMethod ::Print {
2020-06-27 10:47:02 -07:00
if let Ok ( Some ( meta ) ) = blockstore . meta ( slot ) {
2022-10-03 21:25:44 -07:00
if verbose_level > = 1 {
2022-12-06 06:30:06 -08:00
println! ( " {meta:?} is_full: {is_full} " ) ;
2020-06-27 10:47:02 -07:00
} else {
2019-11-11 22:22:20 -08:00
println! (
2022-10-03 21:25:44 -07:00
" num_shreds: {}, parent_slot: {:?}, next_slots: {:?}, num_entries: {}, is_full: {} " ,
2020-06-27 10:47:02 -07:00
num_shreds ,
meta . parent_slot ,
2022-10-03 21:25:44 -07:00
meta . next_slots ,
2021-10-13 03:50:18 -07:00
entries . len ( ) ,
is_full ,
2019-11-11 22:22:20 -08:00
) ;
2020-06-27 10:47:02 -07:00
}
}
}
2019-11-11 22:22:20 -08:00
2020-06-27 10:47:02 -07:00
if verbose_level > = 2 {
2021-08-17 15:17:56 -07:00
for ( entry_index , entry ) in entries . into_iter ( ) . enumerate ( ) {
2020-06-27 10:47:02 -07:00
output_entry ( blockstore , method , slot , entry_index , entry ) ;
}
2020-12-13 17:26:34 -08:00
output_slot_rewards ( blockstore , slot , method ) ;
2020-06-27 10:47:02 -07:00
} else if verbose_level > = 1 {
let mut transactions = 0 ;
2021-12-07 13:14:39 -08:00
let mut num_hashes = 0 ;
2020-06-27 10:47:02 -07:00
let mut program_ids = HashMap ::new ( ) ;
2021-08-17 15:17:56 -07:00
let blockhash = if let Some ( entry ) = entries . last ( ) {
entry . hash
} else {
Hash ::default ( )
} ;
for entry in entries {
2020-06-27 10:47:02 -07:00
transactions + = entry . transactions . len ( ) ;
2021-12-07 13:14:39 -08:00
num_hashes + = entry . num_hashes ;
2021-08-17 15:17:56 -07:00
for transaction in entry . transactions {
2023-03-03 12:18:57 -08:00
for program_id in get_program_ids ( & transaction ) {
* program_ids . entry ( * program_id ) . or_insert ( 0 ) + = 1 ;
2019-11-11 22:22:20 -08:00
}
}
2019-08-09 15:57:31 -07:00
}
2020-03-23 12:49:21 -07:00
2022-12-06 06:30:06 -08:00
println! ( " Transactions: {transactions} , hashes: {num_hashes} , block_hash: {blockhash} " , ) ;
2022-05-19 14:26:29 -07:00
for ( pubkey , count ) in program_ids . iter ( ) {
* all_program_ids . entry ( * pubkey ) . or_insert ( 0 ) + = count ;
}
println! ( " Programs: " ) ;
output_sorted_program_ids ( program_ids ) ;
2020-06-27 10:47:02 -07:00
}
Ok ( ( ) )
2019-08-09 15:57:31 -07:00
}
2020-04-09 20:10:51 -07:00
fn output_ledger (
blockstore : Blockstore ,
starting_slot : Slot ,
2021-02-06 17:26:42 -08:00
ending_slot : Slot ,
2020-04-09 20:10:51 -07:00
allow_dead_slots : bool ,
method : LedgerOutputMethod ,
2020-06-27 10:47:02 -07:00
num_slots : Option < Slot > ,
verbose_level : u64 ,
only_rooted : bool ,
2020-04-09 20:10:51 -07:00
) {
2020-06-27 10:47:02 -07:00
let slot_iterator = blockstore
. slot_meta_iterator ( starting_slot )
. unwrap_or_else ( | err | {
2022-12-06 06:30:06 -08:00
eprintln! ( " Failed to load entries starting from slot {starting_slot} : {err:?} " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2019-07-11 20:33:36 -07:00
} ) ;
if method = = LedgerOutputMethod ::Json {
stdout ( ) . write_all ( b " { \" ledger \" :[ \n " ) . expect ( " open array " ) ;
}
2021-02-06 17:26:42 -08:00
let num_slots = num_slots . unwrap_or ( Slot ::MAX ) ;
2020-06-27 10:47:02 -07:00
let mut num_printed = 0 ;
2022-05-19 14:26:29 -07:00
let mut all_program_ids = HashMap ::new ( ) ;
2020-06-27 10:47:02 -07:00
for ( slot , slot_meta ) in slot_iterator {
if only_rooted & & ! blockstore . is_root ( slot ) {
continue ;
}
2021-02-06 17:26:42 -08:00
if slot > ending_slot {
break ;
}
2020-06-27 10:47:02 -07:00
2019-07-11 20:33:36 -07:00
match method {
2020-06-27 10:47:02 -07:00
LedgerOutputMethod ::Print = > {
println! ( " Slot {} root?: {} " , slot , blockstore . is_root ( slot ) )
}
2019-07-11 20:33:36 -07:00
LedgerOutputMethod ::Json = > {
serde_json ::to_writer ( stdout ( ) , & slot_meta ) . expect ( " serialize slot_meta " ) ;
stdout ( ) . write_all ( b " , \n " ) . expect ( " newline " ) ;
}
}
2022-05-19 14:26:29 -07:00
if let Err ( err ) = output_slot (
& blockstore ,
slot ,
allow_dead_slots ,
& method ,
verbose_level ,
& mut all_program_ids ,
) {
2022-12-06 06:30:06 -08:00
eprintln! ( " {err} " ) ;
2020-03-21 21:43:33 -07:00
}
2020-06-27 10:47:02 -07:00
num_printed + = 1 ;
if num_printed > = num_slots as usize {
break ;
}
2019-07-11 20:33:36 -07:00
}
if method = = LedgerOutputMethod ::Json {
stdout ( ) . write_all ( b " \n ]} \n " ) . expect ( " close array " ) ;
2022-05-19 14:26:29 -07:00
} else {
println! ( " Summary of Programs: " ) ;
output_sorted_program_ids ( all_program_ids ) ;
}
}
fn output_sorted_program_ids ( program_ids : HashMap < Pubkey , u64 > ) {
let mut program_ids_array : Vec < _ > = program_ids . into_iter ( ) . collect ( ) ;
// Sort descending by count of program id
program_ids_array . sort_by ( | a , b | b . 1. cmp ( & a . 1 ) ) ;
for ( program_id , count ) in program_ids_array . iter ( ) {
println! ( " {:<44} : {} " , program_id . to_string ( ) , count ) ;
2019-07-11 20:33:36 -07:00
}
}
2022-03-24 12:26:08 -07:00
fn output_account (
pubkey : & Pubkey ,
account : & AccountSharedData ,
modified_slot : Option < Slot > ,
print_account_data : bool ,
2022-09-18 21:52:53 -07:00
encoding : UiAccountEncoding ,
2022-03-24 12:26:08 -07:00
) {
2022-12-06 06:30:06 -08:00
println! ( " {pubkey} : " ) ;
2022-03-24 12:26:08 -07:00
println! ( " balance: {} SOL " , lamports_to_sol ( account . lamports ( ) ) ) ;
println! ( " owner: ' {} ' " , account . owner ( ) ) ;
println! ( " executable: {} " , account . executable ( ) ) ;
if let Some ( slot ) = modified_slot {
2022-12-06 06:30:06 -08:00
println! ( " slot: {slot} " ) ;
2022-03-24 12:26:08 -07:00
}
println! ( " rent_epoch: {} " , account . rent_epoch ( ) ) ;
println! ( " data_len: {} " , account . data ( ) . len ( ) ) ;
if print_account_data {
2022-10-19 13:43:41 -07:00
let account_data = UiAccount ::encode ( pubkey , account , encoding , None , None ) . data ;
match account_data {
UiAccountData ::Binary ( data , data_encoding ) = > {
2022-12-06 06:30:06 -08:00
println! ( " data: ' {data} ' " ) ;
2022-10-19 13:43:41 -07:00
println! (
" encoding: {} " ,
serde_json ::to_string ( & data_encoding ) . unwrap ( )
) ;
}
UiAccountData ::Json ( account_data ) = > {
println! (
" data: '{}' " ,
serde_json ::to_string ( & account_data ) . unwrap ( )
) ;
println! ( " encoding: \" jsonParsed \" " ) ;
}
UiAccountData ::LegacyBinary ( _ ) = > { }
} ;
2022-03-24 12:26:08 -07:00
}
}
2019-11-12 19:27:15 -08:00
fn render_dot ( dot : String , output_file : & str , output_format : & str ) -> io ::Result < ( ) > {
let mut child = Command ::new ( " dot " )
2022-12-06 06:30:06 -08:00
. arg ( format! ( " -T {output_format} " ) )
. arg ( format! ( " -o {output_file} " ) )
2019-11-12 19:27:15 -08:00
. stdin ( Stdio ::piped ( ) )
. spawn ( )
. map_err ( | err | {
2022-12-06 06:30:06 -08:00
eprintln! ( " Failed to spawn dot: {err:?} " ) ;
2019-11-12 19:27:15 -08:00
err
} ) ? ;
let stdin = child . stdin . as_mut ( ) . unwrap ( ) ;
stdin . write_all ( & dot . into_bytes ( ) ) ? ;
let status = child . wait_with_output ( ) ? . status ;
if ! status . success ( ) {
return Err ( io ::Error ::new (
io ::ErrorKind ::Other ,
format! ( " dot failed with error {} " , status . code ( ) . unwrap_or ( - 1 ) ) ,
) ) ;
}
Ok ( ( ) )
}
2022-07-18 21:46:01 -07:00
#[ derive(Clone, Copy, Debug) ]
enum GraphVoteAccountMode {
Disabled ,
LastOnly ,
WithHistory ,
}
impl GraphVoteAccountMode {
const DISABLED : & 'static str = " disabled " ;
const LAST_ONLY : & 'static str = " last-only " ;
const WITH_HISTORY : & 'static str = " with-history " ;
const ALL_MODE_STRINGS : & 'static [ & 'static str ] =
& [ Self ::DISABLED , Self ::LAST_ONLY , Self ::WITH_HISTORY ] ;
fn is_enabled ( & self ) -> bool {
! matches! ( self , Self ::Disabled )
}
}
impl AsRef < str > for GraphVoteAccountMode {
fn as_ref ( & self ) -> & str {
match self {
Self ::Disabled = > Self ::DISABLED ,
Self ::LastOnly = > Self ::LAST_ONLY ,
Self ::WithHistory = > Self ::WITH_HISTORY ,
}
}
}
impl Default for GraphVoteAccountMode {
fn default ( ) -> Self {
Self ::Disabled
}
}
struct GraphVoteAccountModeError ( String ) ;
impl FromStr for GraphVoteAccountMode {
type Err = GraphVoteAccountModeError ;
fn from_str ( s : & str ) -> Result < Self , Self ::Err > {
match s {
Self ::DISABLED = > Ok ( Self ::Disabled ) ,
Self ::LAST_ONLY = > Ok ( Self ::LastOnly ) ,
Self ::WITH_HISTORY = > Ok ( Self ::WithHistory ) ,
_ = > Err ( GraphVoteAccountModeError ( s . to_string ( ) ) ) ,
}
}
}
2022-07-18 15:31:23 -07:00
struct GraphConfig {
include_all_votes : bool ,
2022-07-18 21:46:01 -07:00
vote_account_mode : GraphVoteAccountMode ,
2022-07-18 15:31:23 -07:00
}
2019-11-12 09:13:16 -08:00
#[ allow(clippy::cognitive_complexity) ]
2022-07-18 15:31:23 -07:00
fn graph_forks ( bank_forks : & BankForks , config : & GraphConfig ) -> String {
2020-05-06 08:24:59 -07:00
let frozen_banks = bank_forks . frozen_banks ( ) ;
let mut fork_slots : HashSet < _ > = frozen_banks . keys ( ) . cloned ( ) . collect ( ) ;
for ( _ , bank ) in frozen_banks {
for parent in bank . parents ( ) {
fork_slots . remove ( & parent . slot ( ) ) ;
}
}
2019-11-04 22:18:30 -08:00
// Search all forks and collect the last vote made by each validator
let mut last_votes = HashMap ::new ( ) ;
2020-11-30 09:18:33 -08:00
let default_vote_state = VoteState ::default ( ) ;
2020-05-06 08:24:59 -07:00
for fork_slot in & fork_slots {
let bank = & bank_forks [ * fork_slot ] ;
2019-11-04 22:18:30 -08:00
let total_stake = bank
. vote_accounts ( )
. iter ( )
2020-05-19 18:13:41 -07:00
. map ( | ( _ , ( stake , _ ) ) | stake )
. sum ( ) ;
2021-08-30 08:54:01 -07:00
for ( stake , vote_account ) in bank . vote_accounts ( ) . values ( ) {
2020-11-30 09:18:33 -08:00
let vote_state = vote_account . vote_state ( ) ;
2023-04-18 13:48:52 -07:00
let vote_state = vote_state . unwrap_or ( & default_vote_state ) ;
2019-11-04 22:18:30 -08:00
if let Some ( last_vote ) = vote_state . votes . iter ( ) . last ( ) {
2019-11-05 18:40:00 -08:00
let entry = last_votes . entry ( vote_state . node_pubkey ) . or_insert ( (
2023-01-18 18:28:28 -08:00
last_vote . slot ( ) ,
2019-11-05 18:40:00 -08:00
vote_state . clone ( ) ,
2021-08-30 08:54:01 -07:00
* stake ,
2019-11-05 18:40:00 -08:00
total_stake ,
) ) ;
2023-01-18 18:28:28 -08:00
if entry . 0 < last_vote . slot ( ) {
* entry = ( last_vote . slot ( ) , vote_state . clone ( ) , * stake , total_stake ) ;
2019-11-04 22:18:30 -08:00
}
}
}
}
// Figure the stake distribution at all the nodes containing the last vote from each
// validator
let mut slot_stake_and_vote_count = HashMap ::new ( ) ;
for ( last_vote_slot , _ , stake , total_stake ) in last_votes . values ( ) {
let entry = slot_stake_and_vote_count
. entry ( last_vote_slot )
. or_insert ( ( 0 , 0 , * total_stake ) ) ;
entry . 0 + = 1 ;
entry . 1 + = stake ;
assert_eq! ( entry . 2 , * total_stake )
}
let mut dot = vec! [ " digraph { " . to_string ( ) ] ;
// Build a subgraph consisting of all banks and links to their parent banks
dot . push ( " subgraph cluster_banks { " . to_string ( ) ) ;
dot . push ( " style=invis " . to_string ( ) ) ;
let mut styled_slots = HashSet ::new ( ) ;
2019-11-12 09:13:16 -08:00
let mut all_votes : HashMap < Pubkey , HashMap < Slot , VoteState > > = HashMap ::new ( ) ;
2020-05-06 08:24:59 -07:00
for fork_slot in & fork_slots {
let mut bank = bank_forks [ * fork_slot ] . clone ( ) ;
2019-11-04 22:18:30 -08:00
let mut first = true ;
loop {
2021-08-30 08:54:01 -07:00
for ( _ , vote_account ) in bank . vote_accounts ( ) . values ( ) {
2020-11-30 09:18:33 -08:00
let vote_state = vote_account . vote_state ( ) ;
2023-04-18 13:48:52 -07:00
let vote_state = vote_state . unwrap_or ( & default_vote_state ) ;
2019-11-12 09:13:16 -08:00
if let Some ( last_vote ) = vote_state . votes . iter ( ) . last ( ) {
let validator_votes = all_votes . entry ( vote_state . node_pubkey ) . or_default ( ) ;
validator_votes
2023-01-18 18:28:28 -08:00
. entry ( last_vote . slot ( ) )
2019-11-12 09:13:16 -08:00
. or_insert_with ( | | vote_state . clone ( ) ) ;
}
}
2019-11-04 22:18:30 -08:00
if ! styled_slots . contains ( & bank . slot ( ) ) {
dot . push ( format! (
2019-11-12 09:13:16 -08:00
r # " "{}"[label="{} (epoch {})\nleader: {}{}{}",style="{}{}"];"# ,
2019-11-04 22:18:30 -08:00
bank . slot ( ) ,
bank . slot ( ) ,
bank . epoch ( ) ,
bank . collector_id ( ) ,
2019-11-12 09:13:16 -08:00
if let Some ( parent ) = bank . parent ( ) {
format! (
" \n transactions: {} " ,
bank . transaction_count ( ) - parent . transaction_count ( ) ,
)
} else {
" " . to_string ( )
} ,
2019-11-04 22:18:30 -08:00
if let Some ( ( votes , stake , total_stake ) ) =
slot_stake_and_vote_count . get ( & bank . slot ( ) )
{
format! (
" \n votes: {}, stake: {:.1} SOL ({:.1}%) " ,
votes ,
lamports_to_sol ( * stake ) ,
* stake as f64 / * total_stake as f64 * 100. ,
)
} else {
" " . to_string ( )
} ,
if first { " filled, " } else { " " } ,
2019-11-06 01:02:26 -08:00
" "
2019-11-04 22:18:30 -08:00
) ) ;
styled_slots . insert ( bank . slot ( ) ) ;
}
first = false ;
match bank . parent ( ) {
None = > {
if bank . slot ( ) > 0 {
2019-11-05 18:40:00 -08:00
dot . push ( format! ( r # " "{}" -> "..." [dir=back]"# , bank . slot ( ) , ) ) ;
2019-11-04 22:18:30 -08:00
}
break ;
}
Some ( parent ) = > {
let slot_distance = bank . slot ( ) - parent . slot ( ) ;
let penwidth = if bank . epoch ( ) > parent . epoch ( ) {
" 5 "
} else {
" 1 "
} ;
let link_label = if slot_distance > 1 {
2022-07-18 15:17:59 -07:00
format! ( " label= \" {} slots \" ,color=red " , slot_distance - 1 )
2019-11-04 22:18:30 -08:00
} else {
" color=blue " . to_string ( )
} ;
dot . push ( format! (
2019-11-05 18:40:00 -08:00
r # " "{}" -> "{}"[{},dir=back,penwidth={}];"# ,
2019-11-04 22:18:30 -08:00
bank . slot ( ) ,
parent . slot ( ) ,
link_label ,
penwidth
) ) ;
bank = parent . clone ( ) ;
}
}
}
}
dot . push ( " } " . to_string ( ) ) ;
// Strafe the banks with links from validators to the bank they last voted on,
// while collecting information about the absent votes and stakes
let mut absent_stake = 0 ;
let mut absent_votes = 0 ;
let mut lowest_last_vote_slot = std ::u64 ::MAX ;
let mut lowest_total_stake = 0 ;
2019-11-05 18:40:00 -08:00
for ( node_pubkey , ( last_vote_slot , vote_state , stake , total_stake ) ) in & last_votes {
2019-11-12 09:13:16 -08:00
all_votes . entry ( * node_pubkey ) . and_modify ( | validator_votes | {
2021-06-18 06:34:46 -07:00
validator_votes . remove ( last_vote_slot ) ;
2019-11-12 09:13:16 -08:00
} ) ;
2022-07-18 21:46:01 -07:00
let maybe_styled_last_vote_slot = styled_slots . get ( last_vote_slot ) ;
if maybe_styled_last_vote_slot . is_none ( ) {
if * last_vote_slot < lowest_last_vote_slot {
lowest_last_vote_slot = * last_vote_slot ;
lowest_total_stake = * total_stake ;
}
absent_votes + = 1 ;
absent_stake + = stake ;
} ;
2019-11-05 18:40:00 -08:00
2022-07-18 21:46:01 -07:00
if config . vote_account_mode . is_enabled ( ) {
let vote_history =
if matches! ( config . vote_account_mode , GraphVoteAccountMode ::WithHistory ) {
format! (
" vote history: \n {} " ,
vote_state
. votes
. iter ( )
. map ( | vote | format! (
" slot {} (conf={}) " ,
2023-01-18 18:28:28 -08:00
vote . slot ( ) ,
vote . confirmation_count ( )
2022-07-18 21:46:01 -07:00
) )
. collect ::< Vec < _ > > ( )
. join ( " \n " )
)
} else {
format! (
" last vote slot: {} " ,
vote_state
. votes
. back ( )
2023-01-18 18:28:28 -08:00
. map ( | vote | vote . slot ( ) . to_string ( ) )
2022-07-18 21:46:01 -07:00
. unwrap_or_else ( | | " none " . to_string ( ) )
)
} ;
dot . push ( format! (
r # " "last vote {}"[shape=box,label="Latest validator vote: {}\nstake: {} SOL\nroot slot: {}\n{}"];"# ,
node_pubkey ,
node_pubkey ,
lamports_to_sol ( * stake ) ,
vote_state . root_slot . unwrap_or ( 0 ) ,
vote_history ,
) ) ;
dot . push ( format! (
r # " "last vote {}" -> "{}" [style=dashed,label="latest vote"];"# ,
node_pubkey ,
if let Some ( styled_last_vote_slot ) = maybe_styled_last_vote_slot {
styled_last_vote_slot . to_string ( )
} else {
" ... " . to_string ( )
} ,
) ) ;
}
2019-11-04 22:18:30 -08:00
}
// Annotate the final "..." node with absent vote and stake information
if absent_votes > 0 {
dot . push ( format! (
r # " "..."[label="...\nvotes: {}, stake: {:.1} SOL {:.1}%"];"# ,
absent_votes ,
lamports_to_sol ( absent_stake ) ,
absent_stake as f64 / lowest_total_stake as f64 * 100. ,
) ) ;
}
2019-11-12 09:13:16 -08:00
// Add for vote information from all banks.
2022-07-18 15:31:23 -07:00
if config . include_all_votes {
2019-11-12 09:13:16 -08:00
for ( node_pubkey , validator_votes ) in & all_votes {
for ( vote_slot , vote_state ) in validator_votes {
dot . push ( format! (
r # " "{} vote {}"[shape=box,style=dotted,label="validator vote: {}\nroot slot: {}\nvote history:\n{}"];"# ,
node_pubkey ,
vote_slot ,
node_pubkey ,
vote_state . root_slot . unwrap_or ( 0 ) ,
vote_state
. votes
. iter ( )
2023-01-18 18:28:28 -08:00
. map ( | vote | format! ( " slot {} (conf= {} ) " , vote . slot ( ) , vote . confirmation_count ( ) ) )
2019-11-12 09:13:16 -08:00
. collect ::< Vec < _ > > ( )
. join ( " \n " )
) ) ;
2019-11-04 22:18:30 -08:00
2019-11-12 09:13:16 -08:00
dot . push ( format! (
r # " "{} vote {}" -> "{}" [style=dotted,label="vote"];"# ,
node_pubkey ,
vote_slot ,
2021-06-18 06:34:46 -07:00
if styled_slots . contains ( vote_slot ) {
2019-11-12 09:13:16 -08:00
vote_slot . to_string ( )
} else {
" ... " . to_string ( )
} ,
) ) ;
}
}
}
dot . push ( " } " . to_string ( ) ) ;
dot . join ( " \n " )
2019-11-04 22:18:30 -08:00
}
2020-01-13 17:21:39 -08:00
fn analyze_column <
2022-01-06 21:40:02 -08:00
C : solana_ledger ::blockstore_db ::Column + solana_ledger ::blockstore_db ::ColumnName ,
2020-01-13 17:21:39 -08:00
> (
2019-12-12 15:54:50 -08:00
db : & Database ,
name : & str ,
2020-12-13 17:26:34 -08:00
) {
2019-12-12 15:54:50 -08:00
let mut key_tot : u64 = 0 ;
let mut val_hist = histogram ::Histogram ::new ( ) ;
let mut val_tot : u64 = 0 ;
let mut row_hist = histogram ::Histogram ::new ( ) ;
2022-01-06 21:40:02 -08:00
let a = C ::key_size ( ) as u64 ;
for ( _x , y ) in db . iter ::< C > ( blockstore_db ::IteratorMode ::Start ) . unwrap ( ) {
2019-12-12 15:54:50 -08:00
let b = y . len ( ) as u64 ;
key_tot + = a ;
val_hist . increment ( b ) . unwrap ( ) ;
val_tot + = b ;
row_hist . increment ( a + b ) . unwrap ( ) ;
}
let json_result = if val_hist . entries ( ) > 0 {
json! ( {
" column " :name ,
" entries " :val_hist . entries ( ) ,
" key_stats " :{
" max " :a ,
" total_bytes " :key_tot ,
} ,
" val_stats " :{
" p50 " :val_hist . percentile ( 50.0 ) . unwrap ( ) ,
" p90 " :val_hist . percentile ( 90.0 ) . unwrap ( ) ,
" p99 " :val_hist . percentile ( 99.0 ) . unwrap ( ) ,
" p999 " :val_hist . percentile ( 99.9 ) . unwrap ( ) ,
" min " :val_hist . minimum ( ) . unwrap ( ) ,
" max " :val_hist . maximum ( ) . unwrap ( ) ,
" stddev " :val_hist . stddev ( ) . unwrap ( ) ,
" total_bytes " :val_tot ,
} ,
" row_stats " :{
" p50 " :row_hist . percentile ( 50.0 ) . unwrap ( ) ,
" p90 " :row_hist . percentile ( 90.0 ) . unwrap ( ) ,
" p99 " :row_hist . percentile ( 99.0 ) . unwrap ( ) ,
" p999 " :row_hist . percentile ( 99.9 ) . unwrap ( ) ,
" min " :row_hist . minimum ( ) . unwrap ( ) ,
" max " :row_hist . maximum ( ) . unwrap ( ) ,
" stddev " :row_hist . stddev ( ) . unwrap ( ) ,
" total_bytes " :key_tot + val_tot ,
} ,
} )
} else {
json! ( {
" column " :name ,
" entries " :val_hist . entries ( ) ,
" key_stats " :{
" max " :a ,
" total_bytes " :0 ,
} ,
" val_stats " :{
" total_bytes " :0 ,
} ,
" row_stats " :{
" total_bytes " :0 ,
} ,
} )
} ;
println! ( " {} " , serde_json ::to_string_pretty ( & json_result ) . unwrap ( ) ) ;
}
2020-12-13 17:26:34 -08:00
fn analyze_storage ( database : & Database ) {
2020-01-13 13:13:52 -08:00
use blockstore_db ::columns ::* ;
2022-01-06 21:40:02 -08:00
analyze_column ::< SlotMeta > ( database , " SlotMeta " ) ;
analyze_column ::< Orphans > ( database , " Orphans " ) ;
analyze_column ::< DeadSlots > ( database , " DeadSlots " ) ;
analyze_column ::< DuplicateSlots > ( database , " DuplicateSlots " ) ;
analyze_column ::< ErasureMeta > ( database , " ErasureMeta " ) ;
analyze_column ::< BankHash > ( database , " BankHash " ) ;
analyze_column ::< Root > ( database , " Root " ) ;
analyze_column ::< Index > ( database , " Index " ) ;
analyze_column ::< ShredData > ( database , " ShredData " ) ;
analyze_column ::< ShredCode > ( database , " ShredCode " ) ;
analyze_column ::< TransactionStatus > ( database , " TransactionStatus " ) ;
analyze_column ::< AddressSignatures > ( database , " AddressSignatures " ) ;
analyze_column ::< TransactionMemos > ( database , " TransactionMemos " ) ;
analyze_column ::< TransactionStatusIndex > ( database , " TransactionStatusIndex " ) ;
analyze_column ::< Rewards > ( database , " Rewards " ) ;
analyze_column ::< Blocktime > ( database , " Blocktime " ) ;
analyze_column ::< PerfSamples > ( database , " PerfSamples " ) ;
analyze_column ::< BlockHeight > ( database , " BlockHeight " ) ;
analyze_column ::< ProgramCosts > ( database , " ProgramCosts " ) ;
2022-05-24 12:03:28 -07:00
analyze_column ::< OptimisticSlots > ( database , " OptimisticSlots " ) ;
2019-12-12 15:54:50 -08:00
}
2022-09-06 21:46:35 -07:00
fn raw_key_to_slot ( key : & [ u8 ] , column_name : & str ) -> Option < Slot > {
match column_name {
cf ::SlotMeta ::NAME = > Some ( cf ::SlotMeta ::slot ( cf ::SlotMeta ::index ( key ) ) ) ,
cf ::Orphans ::NAME = > Some ( cf ::Orphans ::slot ( cf ::Orphans ::index ( key ) ) ) ,
cf ::DeadSlots ::NAME = > Some ( cf ::SlotMeta ::slot ( cf ::SlotMeta ::index ( key ) ) ) ,
cf ::DuplicateSlots ::NAME = > Some ( cf ::SlotMeta ::slot ( cf ::SlotMeta ::index ( key ) ) ) ,
cf ::ErasureMeta ::NAME = > Some ( cf ::ErasureMeta ::slot ( cf ::ErasureMeta ::index ( key ) ) ) ,
cf ::BankHash ::NAME = > Some ( cf ::BankHash ::slot ( cf ::BankHash ::index ( key ) ) ) ,
cf ::Root ::NAME = > Some ( cf ::Root ::slot ( cf ::Root ::index ( key ) ) ) ,
cf ::Index ::NAME = > Some ( cf ::Index ::slot ( cf ::Index ::index ( key ) ) ) ,
cf ::ShredData ::NAME = > Some ( cf ::ShredData ::slot ( cf ::ShredData ::index ( key ) ) ) ,
cf ::ShredCode ::NAME = > Some ( cf ::ShredCode ::slot ( cf ::ShredCode ::index ( key ) ) ) ,
cf ::TransactionStatus ::NAME = > Some ( cf ::TransactionStatus ::slot (
cf ::TransactionStatus ::index ( key ) ,
) ) ,
cf ::AddressSignatures ::NAME = > Some ( cf ::AddressSignatures ::slot (
cf ::AddressSignatures ::index ( key ) ,
) ) ,
cf ::TransactionMemos ::NAME = > None , // does not implement slot()
cf ::TransactionStatusIndex ::NAME = > None , // does not implement slot()
cf ::Rewards ::NAME = > Some ( cf ::Rewards ::slot ( cf ::Rewards ::index ( key ) ) ) ,
cf ::Blocktime ::NAME = > Some ( cf ::Blocktime ::slot ( cf ::Blocktime ::index ( key ) ) ) ,
cf ::PerfSamples ::NAME = > Some ( cf ::PerfSamples ::slot ( cf ::PerfSamples ::index ( key ) ) ) ,
cf ::BlockHeight ::NAME = > Some ( cf ::BlockHeight ::slot ( cf ::BlockHeight ::index ( key ) ) ) ,
cf ::ProgramCosts ::NAME = > None , // does not implement slot()
cf ::OptimisticSlots ::NAME = > {
Some ( cf ::OptimisticSlots ::slot ( cf ::OptimisticSlots ::index ( key ) ) )
}
& _ = > None ,
}
}
fn print_blockstore_file_metadata (
blockstore : & Blockstore ,
file_name : & Option < & str > ,
) -> Result < ( ) , String > {
let live_files = blockstore
. live_files_metadata ( )
2022-12-06 06:30:06 -08:00
. map_err ( | err | format! ( " {err:?} " ) ) ? ;
2022-09-06 21:46:35 -07:00
// All files under live_files_metadata are prefixed with "/".
2022-12-06 06:30:06 -08:00
let sst_file_name = file_name . as_ref ( ) . map ( | name | format! ( " / {name} " ) ) ;
2022-09-06 21:46:35 -07:00
for file in live_files {
if sst_file_name . is_none ( ) | | file . name . eq ( sst_file_name . as_ref ( ) . unwrap ( ) ) {
println! (
" [{}] cf_name: {}, level: {}, start_slot: {:?}, end_slot: {:?}, size: {}, num_entries: {} " ,
file . name ,
file . column_family_name ,
file . level ,
raw_key_to_slot ( & file . start_key . unwrap ( ) , & file . column_family_name ) ,
raw_key_to_slot ( & file . end_key . unwrap ( ) , & file . column_family_name ) ,
file . size ,
file . num_entries ,
) ;
if sst_file_name . is_some ( ) {
return Ok ( ( ) ) ;
}
}
}
if sst_file_name . is_some ( ) {
return Err ( format! (
2022-12-06 06:30:06 -08:00
" Failed to find or load the metadata of the specified file {file_name:?} "
2022-09-06 21:46:35 -07:00
) ) ;
}
Ok ( ( ) )
}
2021-06-02 09:29:02 -07:00
fn compute_slot_cost ( blockstore : & Blockstore , slot : Slot ) -> Result < ( ) , String > {
if blockstore . is_dead ( slot ) {
return Err ( " Dead slot " . to_string ( ) ) ;
}
let ( entries , _num_shreds , _is_full ) = blockstore
. get_slot_entries_with_shred_info ( slot , 0 , false )
2022-12-06 06:30:06 -08:00
. map_err ( | err | format! ( " Slot: {slot} , Failed to load entries, err {err:?} " ) ) ? ;
2021-06-02 09:29:02 -07:00
2021-08-17 15:17:56 -07:00
let num_entries = entries . len ( ) ;
let mut num_transactions = 0 ;
let mut num_programs = 0 ;
2021-06-02 09:29:02 -07:00
let mut program_ids = HashMap ::new ( ) ;
2021-10-19 12:37:33 -07:00
let mut cost_tracker = CostTracker ::default ( ) ;
2021-06-02 09:29:02 -07:00
2021-08-17 15:17:56 -07:00
for entry in entries {
num_transactions + = entry . transactions . len ( ) ;
entry
. transactions
. into_iter ( )
. filter_map ( | transaction | {
2022-02-03 03:00:12 -08:00
SanitizedTransaction ::try_create (
transaction ,
2022-03-14 21:02:22 -07:00
MessageHash ::Compute ,
2022-02-03 03:00:12 -08:00
None ,
2022-03-14 21:02:22 -07:00
SimpleAddressLoader ::Disabled ,
2022-02-03 03:00:12 -08:00
)
2021-08-17 15:17:56 -07:00
. map_err ( | err | {
warn! ( " Failed to compute cost of transaction: {:?} " , err ) ;
} )
. ok ( )
} )
. for_each ( | transaction | {
num_programs + = transaction . message ( ) . instructions ( ) . len ( ) ;
2022-11-22 09:55:56 -08:00
let tx_cost = CostModel ::calculate_cost ( & transaction , & FeatureSet ::all_enabled ( ) ) ;
2022-04-08 10:34:11 -07:00
let result = cost_tracker . try_add ( & tx_cost ) ;
2021-10-24 20:19:23 -07:00
if result . is_err ( ) {
2021-08-17 15:17:56 -07:00
println! (
2022-12-06 06:30:06 -08:00
" Slot: {slot}, CostModel rejected transaction {transaction:?}, reason {result:?} " ,
2021-08-17 15:17:56 -07:00
) ;
}
for ( program_id , _instruction ) in transaction . message ( ) . program_instructions_iter ( )
2021-07-15 20:51:27 -07:00
{
2021-08-17 15:17:56 -07:00
* program_ids . entry ( * program_id ) . or_insert ( 0 ) + = 1 ;
}
} ) ;
2021-06-02 09:29:02 -07:00
}
println! (
2022-12-06 06:30:06 -08:00
" Slot: {slot}, Entries: {num_entries}, Transactions: {num_transactions}, Programs {num_programs} " ,
2021-06-02 09:29:02 -07:00
) ;
2022-12-06 06:30:06 -08:00
println! ( " Programs: {program_ids:?} " ) ;
2021-06-02 09:29:02 -07:00
Ok ( ( ) )
}
2022-06-22 10:17:43 -07:00
/// Finds the accounts needed to replay slots `snapshot_slot` to `ending_slot`.
/// Removes all other accounts from accounts_db, and updates the accounts hash
/// and capitalization. This is used by the --minimize option in create-snapshot
fn minimize_bank_for_snapshot (
blockstore : & Blockstore ,
bank : & Bank ,
snapshot_slot : Slot ,
ending_slot : Slot ,
) {
let ( transaction_account_set , transaction_accounts_measure ) = measure! (
2023-02-09 21:46:02 -08:00
blockstore . get_accounts_used_in_range ( bank , snapshot_slot , ending_slot ) ,
2022-06-22 10:17:43 -07:00
" get transaction accounts "
) ;
let total_accounts_len = transaction_account_set . len ( ) ;
info! ( " Added {total_accounts_len} accounts from transactions. {transaction_accounts_measure} " ) ;
SnapshotMinimizer ::minimize ( bank , snapshot_slot , ending_slot , transaction_account_set ) ;
}
2020-07-20 03:09:38 -07:00
fn assert_capitalization ( bank : & Bank ) {
2021-06-14 06:53:07 -07:00
let debug_verify = true ;
assert! ( bank . calculate_and_verify_capitalization ( debug_verify ) ) ;
2020-07-20 03:09:38 -07:00
}
2023-06-06 15:32:24 -07:00
/// Get the AccessType required, based on `process_options`
fn get_access_type ( process_options : & ProcessOptions ) -> AccessType {
if process_options . boot_from_local_state {
AccessType ::PrimaryForMaintenance
} else {
AccessType ::Secondary
}
}
2021-11-19 07:48:07 -08:00
#[ cfg(not(target_env = " msvc " )) ]
use jemallocator ::Jemalloc ;
#[ cfg(not(target_env = " msvc " )) ]
#[ global_allocator ]
static GLOBAL : Jemalloc = Jemalloc ;
2020-07-20 03:09:38 -07:00
2019-11-14 11:27:01 -08:00
#[ allow(clippy::cognitive_complexity) ]
2018-08-04 14:31:12 -07:00
fn main ( ) {
2020-06-21 11:08:17 -07:00
// Ignore SIGUSR1 to prevent long-running calls being killed by logrotate
// in warehouse deployments
#[ cfg(unix) ]
{
// `register()` is unsafe because the action is called in a signal handler
// with the usual caveats. So long as this action body stays empty, we'll
// be fine
2021-10-29 23:21:54 -07:00
unsafe { signal_hook ::low_level ::register ( signal_hook ::consts ::SIGUSR1 , | | { } ) } . unwrap ( ) ;
2020-06-21 11:08:17 -07:00
}
2019-07-12 16:58:13 -07:00
const DEFAULT_ROOT_COUNT : & str = " 1 " ;
2022-05-24 12:03:28 -07:00
const DEFAULT_LATEST_OPTIMISTIC_SLOTS_COUNT : & str = " 1 " ;
2021-05-06 13:12:01 -07:00
const DEFAULT_MAX_SLOTS_ROOT_REPAIR : & str = " 2000 " ;
2023-03-16 18:25:58 -07:00
// Use std::usize::MAX for DEFAULT_MAX_*_SNAPSHOTS_TO_RETAIN such that
// ledger-tool commands won't accidentally remove any snapshots by default
const DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN : usize = std ::usize ::MAX ;
const DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN : usize = std ::usize ::MAX ;
2020-01-08 09:19:12 -08:00
solana_logger ::setup_with_default ( " solana=info " ) ;
2019-08-09 15:57:31 -07:00
let starting_slot_arg = Arg ::with_name ( " starting_slot " )
. long ( " starting-slot " )
2022-04-08 13:45:09 -07:00
. value_name ( " SLOT " )
2019-08-09 15:57:31 -07:00
. takes_value ( true )
. default_value ( " 0 " )
. help ( " Start at this slot " ) ;
2020-12-08 09:31:24 -08:00
let ending_slot_arg = Arg ::with_name ( " ending_slot " )
. long ( " ending-slot " )
. value_name ( " SLOT " )
. takes_value ( true )
. help ( " The last slot to iterate to " ) ;
2020-01-23 15:09:36 -08:00
let no_snapshot_arg = Arg ::with_name ( " no_snapshot " )
. long ( " no-snapshot " )
. takes_value ( false )
. help ( " Do not start from a local snapshot if present " ) ;
2021-08-11 09:45:25 -07:00
let accounts_index_bins = Arg ::with_name ( " accounts_index_bins " )
. long ( " accounts-index-bins " )
. value_name ( " BINS " )
2021-10-25 07:45:46 -07:00
. validator ( is_pow2 )
2021-08-11 09:45:25 -07:00
. takes_value ( true )
. help ( " Number of bins to divide the accounts index into " ) ;
2021-09-19 16:00:15 -07:00
let accounts_index_limit = Arg ::with_name ( " accounts_index_memory_limit_mb " )
. long ( " accounts-index-memory-limit-mb " )
. value_name ( " MEGABYTES " )
. validator ( is_parsable ::< usize > )
. takes_value ( true )
2022-07-05 12:49:05 -07:00
. help ( " How much memory the accounts index can consume. If this is exceeded, some account index entries will be stored on disk. " ) ;
2022-02-23 16:07:24 -08:00
let disable_disk_index = Arg ::with_name ( " disable_accounts_disk_index " )
. long ( " disable-accounts-disk-index " )
2022-07-05 12:49:05 -07:00
. help ( " Disable the disk-based accounts index. It is enabled by default. The entire accounts index will be kept in memory. " )
2022-02-23 16:07:24 -08:00
. conflicts_with ( " accounts_index_memory_limit_mb " ) ;
2021-11-15 12:42:56 -08:00
let accountsdb_skip_shrink = Arg ::with_name ( " accounts_db_skip_shrink " )
. long ( " accounts-db-skip-shrink " )
. help (
" Enables faster starting of ledger-tool by skipping shrink. \
This option is for use during testing . " ,
) ;
2022-09-06 09:39:39 -07:00
let accountsdb_verify_refcounts = Arg ::with_name ( " accounts_db_verify_refcounts " )
. long ( " accounts-db-verify-refcounts " )
. help (
2023-01-03 11:34:06 -08:00
" Debug option to scan all AppendVecs and verify account index refcounts prior to clean " ,
2022-09-06 09:39:39 -07:00
)
2023-03-22 01:59:17 -07:00
. hidden ( hidden_unless_forced ( ) ) ;
2021-10-11 10:46:27 -07:00
let accounts_filler_count = Arg ::with_name ( " accounts_filler_count " )
. long ( " accounts-filler-count " )
. value_name ( " COUNT " )
. validator ( is_parsable ::< usize > )
. takes_value ( true )
2022-04-11 11:10:09 -07:00
. default_value ( " 0 " )
2021-10-11 10:46:27 -07:00
. help ( " How many accounts to add to stress the system. Accounts are ignored in operations related to correctness. " ) ;
2022-04-11 11:10:09 -07:00
let accounts_filler_size = Arg ::with_name ( " accounts_filler_size " )
. long ( " accounts-filler-size " )
. value_name ( " BYTES " )
. validator ( is_parsable ::< usize > )
. takes_value ( true )
. default_value ( " 0 " )
. requires ( " accounts_filler_count " )
. help ( " Size per filler account in bytes. " ) ;
2020-01-23 15:09:36 -08:00
let account_paths_arg = Arg ::with_name ( " account_paths " )
. long ( " accounts " )
. value_name ( " PATHS " )
. takes_value ( true )
. help ( " Comma separated persistent accounts location " ) ;
2021-09-28 09:07:47 -07:00
let accounts_index_path_arg = Arg ::with_name ( " accounts_index_path " )
. long ( " accounts-index-path " )
. value_name ( " PATH " )
. takes_value ( true )
. multiple ( true )
. help (
" Persistent accounts-index location. \
May be specified multiple times . \
[ default : [ ledger ] / accounts_index ] " ,
) ;
2021-05-24 16:15:57 -07:00
let accounts_db_test_hash_calculation_arg = Arg ::with_name ( " accounts_db_test_hash_calculation " )
. long ( " accounts-db-test-hash-calculation " )
. help ( " Enable hash calculation test " ) ;
2020-01-23 15:09:36 -08:00
let halt_at_slot_arg = Arg ::with_name ( " halt_at_slot " )
. long ( " halt-at-slot " )
. value_name ( " SLOT " )
2020-03-02 10:47:58 -08:00
. validator ( is_slot )
2020-01-23 15:09:36 -08:00
. takes_value ( true )
. help ( " Halt processing at the given slot " ) ;
2022-05-03 09:23:30 -07:00
let no_os_memory_stats_reporting_arg = Arg ::with_name ( " no_os_memory_stats_reporting " )
. long ( " no-os-memory-stats-reporting " )
. help ( " Disable reporting of OS memory statistics. " ) ;
2022-05-20 08:27:00 -07:00
let accounts_db_skip_initial_hash_calc_arg =
Arg ::with_name ( " accounts_db_skip_initial_hash_calculation " )
. long ( " accounts-db-skip-initial-hash-calculation " )
. help ( " Do not verify accounts hash at startup. " )
2023-03-22 01:59:17 -07:00
. hidden ( hidden_unless_forced ( ) ) ;
2022-05-11 06:47:07 -07:00
let ancient_append_vecs = Arg ::with_name ( " accounts_db_ancient_append_vecs " )
. long ( " accounts-db-ancient-append-vecs " )
2022-11-11 11:30:05 -08:00
. value_name ( " SLOT-OFFSET " )
2023-01-10 05:33:43 -08:00
. validator ( is_parsable ::< i64 > )
2022-11-11 11:30:05 -08:00
. takes_value ( true )
. help (
" AppendVecs that are older than (slots_per_epoch - SLOT-OFFSET) are squashed together. " ,
)
2023-03-22 01:59:17 -07:00
. hidden ( hidden_unless_forced ( ) ) ;
2022-07-29 13:54:56 -07:00
let halt_at_slot_store_hash_raw_data = Arg ::with_name ( " halt_at_slot_store_hash_raw_data " )
. long ( " halt-at-slot-store-hash-raw-data " )
. help ( " After halting at slot, run an accounts hash calculation and store the raw hash data for debugging. " )
2023-03-22 01:59:17 -07:00
. hidden ( hidden_unless_forced ( ) ) ;
2021-07-13 09:06:18 -07:00
let verify_index_arg = Arg ::with_name ( " verify_accounts_index " )
. long ( " verify-accounts-index " )
. takes_value ( false )
. help ( " For debugging and tests on accounts index. " ) ;
2021-05-26 08:36:12 -07:00
let limit_load_slot_count_from_snapshot_arg = Arg ::with_name ( " limit_load_slot_count_from_snapshot " )
. long ( " limit-load-slot-count-from-snapshot " )
. value_name ( " SLOT " )
. validator ( is_slot )
. takes_value ( true )
. help ( " For debugging and profiling with large snapshots, artificially limit how many slots are loaded from a snapshot. " ) ;
2020-01-24 17:27:04 -08:00
let hard_forks_arg = Arg ::with_name ( " hard_forks " )
. long ( " hard-fork " )
. value_name ( " SLOT " )
2020-03-02 10:47:58 -08:00
. validator ( is_slot )
2020-01-24 17:27:04 -08:00
. multiple ( true )
. takes_value ( true )
. help ( " Add a hard fork at this slot " ) ;
2020-04-09 20:10:51 -07:00
let allow_dead_slots_arg = Arg ::with_name ( " allow_dead_slots " )
. long ( " allow-dead-slots " )
. takes_value ( false )
. help ( " Output dead slots as well " ) ;
2020-04-29 18:53:34 -07:00
let default_genesis_archive_unpacked_size = MAX_GENESIS_ARCHIVE_UNPACKED_SIZE . to_string ( ) ;
let max_genesis_archive_unpacked_size_arg = Arg ::with_name ( " max_genesis_archive_unpacked_size " )
. long ( " max-genesis-archive-unpacked-size " )
. value_name ( " NUMBER " )
. takes_value ( true )
. default_value ( & default_genesis_archive_unpacked_size )
. help ( " maximum total uncompressed size of unpacked genesis archive " ) ;
2020-08-25 22:27:31 -07:00
let hashes_per_tick = Arg ::with_name ( " hashes_per_tick " )
. long ( " hashes-per-tick " )
. value_name ( " NUM_HASHES| \" sleep \" " )
. takes_value ( true )
. help (
" How many PoH hashes to roll before emitting the next tick. \
If \ " sleep \" , for development \
sleep for the target tick duration instead of hashing " ,
) ;
2020-06-18 22:38:37 -07:00
let snapshot_version_arg = Arg ::with_name ( " snapshot_version " )
. long ( " snapshot-version " )
. value_name ( " SNAPSHOT_VERSION " )
. validator ( is_parsable ::< SnapshotVersion > )
. takes_value ( true )
. default_value ( SnapshotVersion ::default ( ) . into ( ) )
. help ( " Output snapshot version " ) ;
2022-05-24 17:56:35 -07:00
let debug_key_arg = Arg ::with_name ( " debug_key " )
. long ( " debug-key " )
. validator ( is_pubkey )
. value_name ( " ADDRESS " )
. multiple ( true )
. takes_value ( true )
. help ( " Log when transactions are processed that reference the given key(s). " ) ;
2023-06-06 15:32:24 -07:00
let boot_from_local_state = Arg ::with_name ( " boot_from_local_state " )
. long ( " boot-from-local-state " )
. takes_value ( false )
. hidden ( hidden_unless_forced ( ) )
. help ( " Boot from state already on disk " )
. long_help (
" Boot from state already on disk, instead of \
extracting it from a snapshot archive . \
This requires primary access , so another instance of \
solana - ledger - tool or solana - validator cannot \
simultaneously use the same ledger / accounts . \
Note , this will use the latest state available , \
which may be newer than the latest snapshot archive . " ,
)
. conflicts_with ( " no_snapshot " ) ;
2020-08-25 22:27:31 -07:00
2023-03-16 18:25:58 -07:00
let default_max_full_snapshot_archives_to_retain =
& DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN . to_string ( ) ;
2021-09-16 08:56:18 -07:00
let maximum_full_snapshot_archives_to_retain = Arg ::with_name (
" maximum_full_snapshots_to_retain " ,
)
. long ( " maximum-full-snapshots-to-retain " )
. alias ( " maximum-snapshots-to-retain " )
. value_name ( " NUMBER " )
. takes_value ( true )
. default_value ( default_max_full_snapshot_archives_to_retain )
2023-03-30 08:16:36 -07:00
. validator ( validate_maximum_full_snapshot_archives_to_retain )
2021-09-16 08:56:18 -07:00
. help (
" The maximum number of full snapshot archives to hold on to when purging older snapshots. " ,
) ;
2023-03-16 18:25:58 -07:00
let default_max_incremental_snapshot_archives_to_retain =
& DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN . to_string ( ) ;
2021-09-16 08:56:18 -07:00
let maximum_incremental_snapshot_archives_to_retain = Arg ::with_name (
" maximum_incremental_snapshots_to_retain " ,
)
. long ( " maximum-incremental-snapshots-to-retain " )
. value_name ( " NUMBER " )
. takes_value ( true )
. default_value ( default_max_incremental_snapshot_archives_to_retain )
2023-03-30 08:16:36 -07:00
. validator ( validate_maximum_incremental_snapshot_archives_to_retain )
2021-09-16 08:56:18 -07:00
. help ( " The maximum number of incremental snapshot archives to hold on to when purging older snapshots. " ) ;
2021-05-12 10:32:27 -07:00
2022-08-09 08:44:57 -07:00
let geyser_plugin_args = Arg ::with_name ( " geyser_plugin_config " )
. long ( " geyser-plugin-config " )
. value_name ( " FILE " )
. takes_value ( true )
. multiple ( true )
. help ( " Specify the configuration file for the Geyser plugin. " ) ;
2022-10-27 00:35:27 -07:00
let accounts_data_encoding_arg = Arg ::with_name ( " encoding " )
. long ( " encoding " )
. takes_value ( true )
. possible_values ( & [ " base64 " , " base64+zstd " , " jsonParsed " ] )
. default_value ( " base64 " )
. help ( " Print account data in specified format when printing account contents. " ) ;
2020-08-25 22:27:31 -07:00
let rent = Rent ::default ( ) ;
let default_bootstrap_validator_lamports = & sol_to_lamports ( 500.0 )
. max ( VoteState ::get_rent_exempt_reserve ( & rent ) )
. to_string ( ) ;
let default_bootstrap_validator_stake_lamports = & sol_to_lamports ( 0.5 )
2022-04-25 15:14:50 -07:00
. max ( rent . minimum_balance ( StakeState ::size_of ( ) ) )
2020-08-25 22:27:31 -07:00
. to_string ( ) ;
2022-07-18 21:46:01 -07:00
let default_graph_vote_account_mode = GraphVoteAccountMode ::default ( ) ;
2020-08-25 22:27:31 -07:00
2022-07-05 07:23:06 -07:00
let mut measure_total_execution_time = Measure ::start ( " ledger tool " ) ;
2022-09-13 10:27:58 -07:00
2019-07-11 20:33:36 -07:00
let matches = App ::new ( crate_name! ( ) )
. about ( crate_description! ( ) )
2020-05-11 15:02:01 -07:00
. version ( solana_version ::version! ( ) )
2021-03-11 11:14:54 -08:00
. setting ( AppSettings ::InferSubcommands )
. setting ( AppSettings ::SubcommandRequiredElseHelp )
. setting ( AppSettings ::VersionlessSubcommands )
2018-08-04 14:31:12 -07:00
. arg (
2020-01-23 09:03:23 -08:00
Arg ::with_name ( " ledger_path " )
2018-08-04 14:31:12 -07:00
. short ( " l " )
. long ( " ledger " )
. value_name ( " DIR " )
. takes_value ( true )
2019-08-09 15:57:31 -07:00
. global ( true )
2021-03-11 11:14:54 -08:00
. default_value ( " ledger " )
. help ( " Use DIR as ledger location " ) ,
2018-08-04 14:31:12 -07:00
)
2020-07-06 12:43:45 -07:00
. arg (
Arg ::with_name ( " wal_recovery_mode " )
. long ( " wal-recovery-mode " )
. value_name ( " MODE " )
. takes_value ( true )
. global ( true )
. possible_values ( & [
" tolerate_corrupted_tail_records " ,
" absolute_consistency " ,
" point_in_time " ,
" skip_any_corrupted_record " ] )
. help (
2021-02-25 17:58:19 -08:00
" Mode to recovery the ledger db write ahead log "
2020-07-06 12:43:45 -07:00
) ,
)
2022-08-14 03:20:13 -07:00
. arg (
Arg ::with_name ( " force_update_to_open " )
. long ( " force-update-to-open " )
. takes_value ( false )
. global ( true )
. help ( " Allow commands that would otherwise not alter the \
blockstore to make necessary updates in order to open it " ),
)
2020-07-08 09:32:11 -07:00
. arg (
Arg ::with_name ( " snapshot_archive_path " )
. long ( " snapshot-archive-path " )
. value_name ( " DIR " )
. takes_value ( true )
. global ( true )
2022-05-10 13:37:41 -07:00
. help ( " Use DIR for snapshot location " ) ,
)
. arg (
Arg ::with_name ( " incremental_snapshot_archive_path " )
. long ( " incremental-snapshot-archive-path " )
. value_name ( " DIR " )
. takes_value ( true )
. global ( true )
. help ( " Use DIR for separate incremental snapshot location " ) ,
2020-07-08 09:32:11 -07:00
)
2023-03-22 20:57:28 -07:00
. arg (
Arg ::with_name ( " block_verification_method " )
. long ( " block-verification-method " )
. value_name ( " METHOD " )
. takes_value ( true )
. possible_values ( BlockVerificationMethod ::cli_names ( ) )
. global ( true )
. hidden ( hidden_unless_forced ( ) )
. help ( BlockVerificationMethod ::cli_message ( ) ) ,
)
2021-02-24 15:14:34 -08:00
. arg (
Arg ::with_name ( " output_format " )
. long ( " output " )
. value_name ( " FORMAT " )
. global ( true )
. takes_value ( true )
. possible_values ( & [ " json " , " json-compact " ] )
2021-02-25 17:58:19 -08:00
. help ( " Return information in specified output format, \
2023-05-10 12:25:38 -07:00
currently only available for bigtable and program subcommands " ),
2021-02-24 15:14:34 -08:00
)
2021-02-25 13:15:52 -08:00
. arg (
Arg ::with_name ( " verbose " )
. short ( " v " )
. long ( " verbose " )
. global ( true )
. multiple ( true )
. takes_value ( false )
. help ( " Show additional information where supported " ) ,
)
2020-07-20 21:06:13 -07:00
. bigtable_subcommand ( )
2019-11-04 22:18:30 -08:00
. subcommand (
SubCommand ::with_name ( " print " )
. about ( " Print the ledger " )
. arg ( & starting_slot_arg )
2020-04-09 20:10:51 -07:00
. arg ( & allow_dead_slots_arg )
2021-02-06 17:26:42 -08:00
. arg ( & ending_slot_arg )
2020-06-27 10:47:02 -07:00
. arg (
Arg ::with_name ( " num_slots " )
. long ( " num-slots " )
. value_name ( " SLOT " )
. validator ( is_slot )
. takes_value ( true )
. help ( " Number of slots to print " ) ,
)
. arg (
Arg ::with_name ( " only_rooted " )
. long ( " only-rooted " )
. takes_value ( false )
. help ( " Only print root slots " ) ,
)
2019-11-04 22:18:30 -08:00
)
2020-06-25 11:56:36 -07:00
. subcommand (
SubCommand ::with_name ( " copy " )
. about ( " Copy the ledger " )
. arg ( & starting_slot_arg )
2021-02-06 17:26:42 -08:00
. arg ( & ending_slot_arg )
2020-06-25 11:56:36 -07:00
. arg (
Arg ::with_name ( " target_db " )
. long ( " target-db " )
2022-04-08 13:45:09 -07:00
. value_name ( " DIR " )
2020-06-25 11:56:36 -07:00
. takes_value ( true )
. help ( " Target db " ) ,
)
)
2019-11-04 22:18:30 -08:00
. subcommand (
2020-02-24 09:20:31 -08:00
SubCommand ::with_name ( " slot " )
2019-11-11 22:22:20 -08:00
. about ( " Print the contents of one or more slots " )
2019-11-04 22:18:30 -08:00
. arg (
2019-11-11 22:22:20 -08:00
Arg ::with_name ( " slots " )
2019-11-04 22:18:30 -08:00
. index ( 1 )
2019-11-11 22:22:20 -08:00
. value_name ( " SLOTS " )
2020-03-02 10:47:58 -08:00
. validator ( is_slot )
2019-11-04 22:18:30 -08:00
. takes_value ( true )
2019-11-11 22:22:20 -08:00
. multiple ( true )
2019-11-04 22:18:30 -08:00
. required ( true )
2020-03-21 21:43:33 -07:00
. help ( " Slots to print " ) ,
)
2020-04-09 20:10:51 -07:00
. arg ( & allow_dead_slots_arg )
2020-03-21 21:43:33 -07:00
)
2020-10-21 10:45:21 -07:00
. subcommand (
SubCommand ::with_name ( " dead-slots " )
. arg ( & starting_slot_arg )
2021-02-25 17:58:19 -08:00
. about ( " Print all the dead slots in the ledger " )
2020-10-21 10:45:21 -07:00
)
2021-04-02 21:48:44 -07:00
. subcommand (
SubCommand ::with_name ( " duplicate-slots " )
. arg ( & starting_slot_arg )
. about ( " Print all the duplicate slots in the ledger " )
)
2020-03-21 21:43:33 -07:00
. subcommand (
SubCommand ::with_name ( " set-dead-slot " )
. about ( " Mark one or more slots dead " )
. arg (
Arg ::with_name ( " slots " )
. index ( 1 )
. value_name ( " SLOTS " )
. validator ( is_slot )
. takes_value ( true )
. multiple ( true )
. required ( true )
. help ( " Slots to mark dead " ) ,
2019-11-04 22:18:30 -08:00
)
)
2021-10-19 20:23:16 -07:00
. subcommand (
SubCommand ::with_name ( " remove-dead-slot " )
. about ( " Remove the dead flag for a slot " )
. arg (
Arg ::with_name ( " slots " )
. index ( 1 )
. value_name ( " SLOTS " )
. validator ( is_slot )
. takes_value ( true )
. multiple ( true )
. required ( true )
. help ( " Slots to mark as not dead " ) ,
)
)
2020-02-24 09:19:29 -08:00
. subcommand (
SubCommand ::with_name ( " genesis " )
. about ( " Prints the ledger's genesis config " )
2020-04-29 18:53:34 -07:00
. arg ( & max_genesis_archive_unpacked_size_arg )
2022-03-24 12:26:08 -07:00
. arg (
Arg ::with_name ( " accounts " )
. long ( " accounts " )
. takes_value ( false )
. help ( " Print the ledger's genesis accounts " ) ,
)
. arg (
Arg ::with_name ( " no_account_data " )
. long ( " no-account-data " )
. takes_value ( false )
. requires ( " accounts " )
. help ( " Do not print account data when printing account contents. " ) ,
)
2022-10-27 00:35:27 -07:00
. arg ( & accounts_data_encoding_arg . clone ( ) . requires ( " accounts " ) )
2022-03-24 12:26:08 -07:00
)
. subcommand (
SubCommand ::with_name ( " genesis-hash " )
. about ( " Prints the ledger's genesis hash " )
. arg ( & max_genesis_archive_unpacked_size_arg )
2020-02-24 09:19:29 -08:00
)
2020-06-30 02:20:54 -07:00
. subcommand (
SubCommand ::with_name ( " parse_full_frozen " )
2021-02-25 17:58:19 -08:00
. about ( " Parses log for information about critical events about \
ancestors of the given ` ending_slot ` " )
2020-06-30 02:20:54 -07:00
. arg ( & starting_slot_arg )
2020-12-08 09:31:24 -08:00
. arg ( & ending_slot_arg )
2020-06-30 02:20:54 -07:00
. arg (
Arg ::with_name ( " log_path " )
. long ( " log-path " )
. value_name ( " PATH " )
. takes_value ( true )
. help ( " path to log file to parse " ) ,
)
)
2020-08-25 22:27:31 -07:00
. subcommand (
SubCommand ::with_name ( " modify-genesis " )
. about ( " Modifies genesis parameters " )
. arg ( & max_genesis_archive_unpacked_size_arg )
. arg ( & hashes_per_tick )
. arg (
2020-09-08 07:55:09 -07:00
Arg ::with_name ( " cluster_type " )
. long ( " cluster-type " )
. possible_values ( & ClusterType ::STRINGS )
2020-08-25 22:27:31 -07:00
. takes_value ( true )
. help (
" Selects the features that will be enabled for the cluster "
) ,
)
2020-08-28 18:16:01 -07:00
. arg (
Arg ::with_name ( " output_directory " )
. index ( 1 )
. value_name ( " DIR " )
. takes_value ( true )
. help ( " Output directory for the modified genesis config " ) ,
)
2020-08-25 22:27:31 -07:00
)
2020-02-07 08:57:54 -08:00
. subcommand (
SubCommand ::with_name ( " shred-version " )
. about ( " Prints the ledger's shred hash " )
. arg ( & hard_forks_arg )
2020-04-29 18:53:34 -07:00
. arg ( & max_genesis_archive_unpacked_size_arg )
2023-03-24 10:46:09 -07:00
. arg ( & accounts_index_bins )
. arg ( & accounts_index_limit )
. arg ( & disable_disk_index )
. arg ( & accountsdb_verify_refcounts )
. arg ( & accounts_db_skip_initial_hash_calc_arg )
2020-02-07 08:57:54 -08:00
)
2020-12-08 09:31:24 -08:00
. subcommand (
SubCommand ::with_name ( " shred-meta " )
. about ( " Prints raw shred metadata " )
. arg ( & starting_slot_arg )
. arg ( & ending_slot_arg )
)
2020-08-30 09:04:14 -07:00
. subcommand (
SubCommand ::with_name ( " bank-hash " )
. about ( " Prints the hash of the working bank after reading the ledger " )
. arg ( & max_genesis_archive_unpacked_size_arg )
2023-01-24 14:15:07 -08:00
. arg ( & halt_at_slot_arg )
2023-03-24 10:46:09 -07:00
. arg ( & accounts_index_bins )
. arg ( & accounts_index_limit )
. arg ( & disable_disk_index )
. arg ( & accountsdb_verify_refcounts )
. arg ( & accounts_db_skip_initial_hash_calc_arg )
2020-08-30 09:04:14 -07:00
)
2019-11-04 22:18:30 -08:00
. subcommand (
SubCommand ::with_name ( " bounds " )
2023-01-12 21:21:04 -08:00
. about (
" Print lowest and highest non-empty slots. \
Note that there may be empty slots within the bounds " ,
)
2019-12-20 19:43:53 -08:00
. arg (
Arg ::with_name ( " all " )
. long ( " all " )
. takes_value ( false )
. required ( false )
. help ( " Additionally print all the non-empty slots within the bounds " ) ,
)
2023-01-12 21:21:04 -08:00
)
. subcommand (
2019-11-04 22:18:30 -08:00
SubCommand ::with_name ( " json " )
. about ( " Print the ledger in JSON format " )
. arg ( & starting_slot_arg )
2020-04-09 20:10:51 -07:00
. arg ( & allow_dead_slots_arg )
2019-11-04 22:18:30 -08:00
)
2019-11-04 21:14:55 -08:00
. subcommand (
SubCommand ::with_name ( " verify " )
2019-11-04 22:18:30 -08:00
. about ( " Verify the ledger " )
2020-01-23 15:09:36 -08:00
. arg ( & no_snapshot_arg )
. arg ( & account_paths_arg )
2021-09-28 09:07:47 -07:00
. arg ( & accounts_index_path_arg )
2020-01-23 15:09:36 -08:00
. arg ( & halt_at_slot_arg )
2021-05-26 08:36:12 -07:00
. arg ( & limit_load_slot_count_from_snapshot_arg )
2021-08-11 09:45:25 -07:00
. arg ( & accounts_index_bins )
2021-09-19 16:00:15 -07:00
. arg ( & accounts_index_limit )
2022-02-23 16:07:24 -08:00
. arg ( & disable_disk_index )
2021-11-15 12:42:56 -08:00
. arg ( & accountsdb_skip_shrink )
2022-09-06 09:39:39 -07:00
. arg ( & accountsdb_verify_refcounts )
2021-10-11 10:46:27 -07:00
. arg ( & accounts_filler_count )
2022-04-11 11:10:09 -07:00
. arg ( & accounts_filler_size )
2021-07-13 09:06:18 -07:00
. arg ( & verify_index_arg )
2022-05-20 08:27:00 -07:00
. arg ( & accounts_db_skip_initial_hash_calc_arg )
2022-05-11 06:47:07 -07:00
. arg ( & ancient_append_vecs )
2022-07-29 13:54:56 -07:00
. arg ( & halt_at_slot_store_hash_raw_data )
2020-01-24 17:27:04 -08:00
. arg ( & hard_forks_arg )
2021-05-24 16:15:57 -07:00
. arg ( & accounts_db_test_hash_calculation_arg )
2022-05-03 09:23:30 -07:00
. arg ( & no_os_memory_stats_reporting_arg )
2021-02-06 17:26:42 -08:00
. arg ( & allow_dead_slots_arg )
2020-04-29 18:53:34 -07:00
. arg ( & max_genesis_archive_unpacked_size_arg )
2022-05-24 17:56:35 -07:00
. arg ( & debug_key_arg )
2022-08-09 08:44:57 -07:00
. arg ( & geyser_plugin_args )
2023-06-06 15:32:24 -07:00
. arg ( & boot_from_local_state )
2019-11-04 21:14:55 -08:00
. arg (
2020-01-23 15:09:36 -08:00
Arg ::with_name ( " skip_poh_verify " )
. long ( " skip-poh-verify " )
2019-11-04 21:14:55 -08:00
. takes_value ( false )
2023-03-22 11:03:30 -07:00
. help (
" Deprecated, please use --skip-verification. \n \
Skip ledger PoH and transaction verification . "
) ,
)
. arg (
Arg ::with_name ( " skip_verification " )
. long ( " skip-verification " )
. takes_value ( false )
. help ( " Skip ledger PoH and transaction verification. " ) ,
2019-11-04 21:14:55 -08:00
)
2023-04-12 07:30:45 -07:00
. arg (
Arg ::with_name ( " run_final_hash_calc " )
. long ( " run-final-accounts-hash-calculation " )
. takes_value ( false )
. help ( " After 'verify' completes, run a final accounts hash calculation. Final hash calculation could race with accounts background service tasks and assert. " ) ,
)
2023-05-24 10:54:09 -07:00
. arg (
2023-05-25 07:47:00 -07:00
Arg ::with_name ( " partitioned_epoch_rewards_compare_calculation " )
. long ( " partitioned-epoch-rewards-compare-calculation " )
2023-05-24 10:54:09 -07:00
. takes_value ( false )
. help ( " Do normal epoch rewards distribution, but also calculate rewards using the partitioned rewards code path and compare the resulting vote and stake accounts " )
. hidden ( hidden_unless_forced ( ) )
)
. arg (
2023-05-25 07:47:00 -07:00
Arg ::with_name ( " partitioned_epoch_rewards_force_enable_single_slot " )
. long ( " partitioned-epoch-rewards-force-enable-single-slot " )
2023-05-24 10:54:09 -07:00
. takes_value ( false )
. help ( " Force the partitioned rewards distribution, but distribute all rewards in the first slot in the epoch. This should match consensus with the normal rewards distribution. " )
. conflicts_with ( " partitioned_epoch_rewards_compare_calculation " )
. hidden ( hidden_unless_forced ( ) )
)
2020-07-08 09:32:11 -07:00
. arg (
Arg ::with_name ( " print_accounts_stats " )
. long ( " print-accounts-stats " )
. takes_value ( false )
2021-02-25 17:58:19 -08:00
. help ( " After verifying the ledger, print some information about the account stores " ) ,
2020-07-08 09:32:11 -07:00
)
2020-01-23 15:09:36 -08:00
) . subcommand (
SubCommand ::with_name ( " graph " )
. about ( " Create a Graphviz rendering of the ledger " )
. arg ( & no_snapshot_arg )
. arg ( & account_paths_arg )
2023-03-24 10:46:09 -07:00
. arg ( & accounts_index_bins )
. arg ( & accounts_index_limit )
. arg ( & disable_disk_index )
. arg ( & accountsdb_verify_refcounts )
. arg ( & accounts_db_skip_initial_hash_calc_arg )
2020-01-23 15:09:36 -08:00
. arg ( & halt_at_slot_arg )
2020-01-24 17:27:04 -08:00
. arg ( & hard_forks_arg )
2020-04-29 18:53:34 -07:00
. arg ( & max_genesis_archive_unpacked_size_arg )
2023-06-06 15:32:24 -07:00
. arg ( & boot_from_local_state )
2019-11-04 21:14:55 -08:00
. arg (
2020-01-23 15:09:36 -08:00
Arg ::with_name ( " include_all_votes " )
. long ( " include-all-votes " )
. help ( " Include all votes in the graph " ) ,
2019-11-04 21:14:55 -08:00
)
2019-11-04 22:18:30 -08:00
. arg (
2020-01-23 15:09:36 -08:00
Arg ::with_name ( " graph_filename " )
. index ( 1 )
. value_name ( " FILENAME " )
2019-11-04 22:18:30 -08:00
. takes_value ( true )
2020-01-23 15:09:36 -08:00
. help ( " Output file " ) ,
2019-11-04 22:18:30 -08:00
)
2022-07-18 21:46:01 -07:00
. arg (
Arg ::with_name ( " vote_account_mode " )
. long ( " vote-account-mode " )
. takes_value ( true )
. value_name ( " MODE " )
. default_value ( default_graph_vote_account_mode . as_ref ( ) )
. possible_values ( GraphVoteAccountMode ::ALL_MODE_STRINGS )
. help ( " Specify if and how to graph vote accounts. Enabling will incur significant rendering overhead, especially `with-history` " )
)
2020-01-23 15:09:36 -08:00
) . subcommand (
SubCommand ::with_name ( " create-snapshot " )
. about ( " Create a new ledger snapshot " )
. arg ( & no_snapshot_arg )
. arg ( & account_paths_arg )
2023-03-24 10:46:09 -07:00
. arg ( & accounts_index_bins )
. arg ( & accounts_index_limit )
. arg ( & disable_disk_index )
. arg ( & accountsdb_verify_refcounts )
2022-06-21 11:07:05 -07:00
. arg ( & accounts_db_skip_initial_hash_calc_arg )
2023-02-09 21:46:02 -08:00
. arg ( & accountsdb_skip_shrink )
2022-06-21 11:07:05 -07:00
. arg ( & ancient_append_vecs )
2020-01-24 17:27:04 -08:00
. arg ( & hard_forks_arg )
2020-04-29 18:53:34 -07:00
. arg ( & max_genesis_archive_unpacked_size_arg )
2020-06-18 22:38:37 -07:00
. arg ( & snapshot_version_arg )
2021-09-16 08:56:18 -07:00
. arg ( & maximum_full_snapshot_archives_to_retain )
. arg ( & maximum_incremental_snapshot_archives_to_retain )
2022-08-09 08:44:57 -07:00
. arg ( & geyser_plugin_args )
2023-06-16 09:58:57 -07:00
. arg ( & boot_from_local_state )
2019-11-04 22:18:30 -08:00
. arg (
2020-01-23 15:09:36 -08:00
Arg ::with_name ( " snapshot_slot " )
. index ( 1 )
. value_name ( " SLOT " )
2021-02-26 13:44:38 -08:00
. validator ( | value | {
if value . parse ::< Slot > ( ) . is_ok ( )
| | value = = " ROOT "
{
Ok ( ( ) )
} else {
Err ( format! (
2022-12-06 06:30:06 -08:00
" Unable to parse as a number or the keyword ROOT, provided: {value} "
2021-02-26 13:44:38 -08:00
) )
}
} )
2019-11-04 22:18:30 -08:00
. takes_value ( true )
2021-02-26 13:44:38 -08:00
. help ( " Slot at which to create the snapshot; accepts keyword ROOT for the highest root " ) ,
2019-11-04 22:18:30 -08:00
)
2019-11-12 09:13:16 -08:00
. arg (
2020-01-23 15:09:36 -08:00
Arg ::with_name ( " output_directory " )
. index ( 2 )
. value_name ( " DIR " )
. takes_value ( true )
2022-05-02 10:00:15 -07:00
. help ( " Output directory for the snapshot [default: --snapshot-archive-path if present else --ledger directory] " ) ,
2019-11-12 09:13:16 -08:00
)
2020-06-08 18:01:44 -07:00
. arg (
Arg ::with_name ( " warp_slot " )
. required ( false )
. long ( " warp-slot " )
. takes_value ( true )
. value_name ( " WARP_SLOT " )
. validator ( is_slot )
. help ( " After loading the snapshot slot warp the ledger to WARP_SLOT, \
which could be a slot in a galaxy far far away " ),
)
2020-08-25 22:27:31 -07:00
. arg (
Arg ::with_name ( " faucet_lamports " )
. short ( " t " )
. long ( " faucet-lamports " )
. value_name ( " LAMPORTS " )
. takes_value ( true )
. requires ( " faucet_pubkey " )
. help ( " Number of lamports to assign to the faucet " ) ,
)
. arg (
Arg ::with_name ( " faucet_pubkey " )
. short ( " m " )
. long ( " faucet-pubkey " )
. value_name ( " PUBKEY " )
. takes_value ( true )
. validator ( is_pubkey_or_keypair )
. requires ( " faucet_lamports " )
. help ( " Path to file containing the faucet's pubkey " ) ,
)
. arg (
Arg ::with_name ( " bootstrap_validator " )
. short ( " b " )
. long ( " bootstrap-validator " )
. value_name ( " IDENTITY_PUBKEY VOTE_PUBKEY STAKE_PUBKEY " )
. takes_value ( true )
. validator ( is_pubkey_or_keypair )
. number_of_values ( 3 )
. multiple ( true )
. help ( " The bootstrap validator's identity, vote and stake pubkeys " ) ,
)
. arg (
Arg ::with_name ( " bootstrap_stake_authorized_pubkey " )
. long ( " bootstrap-stake-authorized-pubkey " )
. value_name ( " BOOTSTRAP STAKE AUTHORIZED PUBKEY " )
. takes_value ( true )
. validator ( is_pubkey_or_keypair )
. help (
" Path to file containing the pubkey authorized to manage the bootstrap \
validator ' s stake [ default : - - bootstrap - validator IDENTITY_PUBKEY ] " ,
) ,
)
. arg (
Arg ::with_name ( " bootstrap_validator_lamports " )
. long ( " bootstrap-validator-lamports " )
. value_name ( " LAMPORTS " )
. takes_value ( true )
. default_value ( default_bootstrap_validator_lamports )
. help ( " Number of lamports to assign to the bootstrap validator " ) ,
)
. arg (
Arg ::with_name ( " bootstrap_validator_stake_lamports " )
. long ( " bootstrap-validator-stake-lamports " )
. value_name ( " LAMPORTS " )
. takes_value ( true )
. default_value ( default_bootstrap_validator_stake_lamports )
. help ( " Number of lamports to assign to the bootstrap validator's stake account " ) ,
)
. arg (
Arg ::with_name ( " rent_burn_percentage " )
. long ( " rent-burn-percentage " )
. value_name ( " NUMBER " )
. takes_value ( true )
. help ( " Adjust percentage of collected rent to burn " )
. validator ( is_valid_percentage ) ,
)
. arg ( & hashes_per_tick )
2020-12-04 22:46:32 -08:00
. arg (
Arg ::with_name ( " accounts_to_remove " )
. required ( false )
. long ( " remove-account " )
. takes_value ( true )
. value_name ( " PUBKEY " )
. validator ( is_pubkey )
. multiple ( true )
2021-01-31 08:22:01 -08:00
. help ( " List of accounts to remove while creating the snapshot " ) ,
2020-12-04 22:46:32 -08:00
)
2023-03-16 14:54:05 -07:00
. arg (
Arg ::with_name ( " feature_gates_to_deactivate " )
. required ( false )
. long ( " deactivate-feature-gate " )
. takes_value ( true )
. value_name ( " PUBKEY " )
. validator ( is_pubkey )
. multiple ( true )
. help ( " List of feature gates to deactivate while creating the snapshot " )
)
2021-09-11 14:44:37 -07:00
. arg (
Arg ::with_name ( " vote_accounts_to_destake " )
. required ( false )
. long ( " destake-vote-account " )
. takes_value ( true )
. value_name ( " PUBKEY " )
. validator ( is_pubkey )
. multiple ( true )
. help ( " List of validator vote accounts to destake " )
)
2020-08-25 22:27:31 -07:00
. arg (
Arg ::with_name ( " remove_stake_accounts " )
. required ( false )
. long ( " remove-stake-accounts " )
. takes_value ( false )
2021-02-25 17:58:19 -08:00
. help ( " Remove all existing stake accounts from the new snapshot " )
2020-08-25 22:27:31 -07:00
)
2021-10-21 19:02:12 -07:00
. arg (
Arg ::with_name ( " incremental " )
. long ( " incremental " )
. takes_value ( false )
. help ( " Create an incremental snapshot instead of a full snapshot. This requires \
that the ledger is loaded from a full snapshot , which will be used as the \
base for the incremental snapshot . " )
. conflicts_with ( " no_snapshot " )
)
2022-06-22 10:17:43 -07:00
. arg (
Arg ::with_name ( " minimized " )
. long ( " minimized " )
. takes_value ( false )
. help ( " Create a minimized snapshot instead of a full snapshot. This snapshot \
will only include information needed to replay the ledger from the \
snapshot slot to the ending slot . " )
. conflicts_with ( " incremental " )
. requires ( " ending_slot " )
)
. arg (
Arg ::with_name ( " ending_slot " )
. long ( " ending-slot " )
. takes_value ( true )
. value_name ( " ENDING_SLOT " )
. help ( " Ending slot for minimized snapshot creation " )
)
2022-05-16 10:44:15 -07:00
. arg (
Arg ::with_name ( " snapshot_archive_format " )
. long ( " snapshot-archive-format " )
. possible_values ( SUPPORTED_ARCHIVE_COMPRESSION )
. default_value ( DEFAULT_ARCHIVE_COMPRESSION )
. value_name ( " ARCHIVE_TYPE " )
. takes_value ( true )
. help ( " Snapshot archive format to use. " )
2022-06-22 10:17:43 -07:00
. conflicts_with ( " no_snapshot " )
2022-05-16 10:44:15 -07:00
)
2022-06-22 10:17:43 -07:00
) . subcommand (
2020-02-24 09:20:31 -08:00
SubCommand ::with_name ( " accounts " )
2021-11-08 12:45:30 -08:00
. about ( " Print account stats and contents after processing the ledger " )
2020-02-14 12:24:16 -08:00
. arg ( & no_snapshot_arg )
. arg ( & account_paths_arg )
2023-03-24 10:46:09 -07:00
. arg ( & accounts_index_bins )
. arg ( & accounts_index_limit )
. arg ( & disable_disk_index )
. arg ( & accountsdb_verify_refcounts )
. arg ( & accounts_db_skip_initial_hash_calc_arg )
2020-02-14 12:24:16 -08:00
. arg ( & halt_at_slot_arg )
. arg ( & hard_forks_arg )
2022-08-09 08:44:57 -07:00
. arg ( & geyser_plugin_args )
2022-10-27 00:35:27 -07:00
. arg ( & accounts_data_encoding_arg )
2023-06-06 15:32:24 -07:00
. arg ( & boot_from_local_state )
2020-04-08 10:25:46 -07:00
. arg (
Arg ::with_name ( " include_sysvars " )
. long ( " include-sysvars " )
. takes_value ( false )
. help ( " Include sysvars too " ) ,
)
2020-08-26 20:28:40 -07:00
. arg (
2021-11-05 06:59:12 -07:00
Arg ::with_name ( " no_account_contents " )
. long ( " no-account-contents " )
2020-08-26 20:28:40 -07:00
. takes_value ( false )
2021-11-05 06:59:12 -07:00
. help ( " Do not print contents of each account, which is very slow with lots of accounts. " ) ,
)
2022-03-24 12:26:08 -07:00
. arg ( Arg ::with_name ( " no_account_data " )
. long ( " no-account-data " )
. takes_value ( false )
. help ( " Do not print account data when printing account contents. " ) ,
2020-08-26 20:28:40 -07:00
)
2020-04-29 18:53:34 -07:00
. arg ( & max_genesis_archive_unpacked_size_arg )
2020-05-08 10:42:32 -07:00
) . subcommand (
SubCommand ::with_name ( " capitalization " )
2021-02-25 17:58:19 -08:00
. about ( " Print capitalization (aka, total supply) while checksumming it " )
2020-05-08 10:42:32 -07:00
. arg ( & no_snapshot_arg )
. arg ( & account_paths_arg )
2023-03-24 10:46:09 -07:00
. arg ( & accounts_index_bins )
. arg ( & accounts_index_limit )
. arg ( & disable_disk_index )
. arg ( & accountsdb_verify_refcounts )
. arg ( & accounts_db_skip_initial_hash_calc_arg )
2020-05-08 10:42:32 -07:00
. arg ( & halt_at_slot_arg )
. arg ( & hard_forks_arg )
. arg ( & max_genesis_archive_unpacked_size_arg )
2022-08-09 08:44:57 -07:00
. arg ( & geyser_plugin_args )
2023-06-06 15:32:24 -07:00
. arg ( & boot_from_local_state )
2020-07-20 03:09:38 -07:00
. arg (
Arg ::with_name ( " warp_epoch " )
. required ( false )
. long ( " warp-epoch " )
. takes_value ( true )
. value_name ( " WARP_EPOCH " )
. help ( " After loading the snapshot warp the ledger to WARP_EPOCH, \
which could be an epoch in a galaxy far far away " ),
)
. arg (
2020-12-07 08:21:16 -08:00
Arg ::with_name ( " inflation " )
2020-07-20 03:09:38 -07:00
. required ( false )
2020-12-07 08:21:16 -08:00
. long ( " inflation " )
. takes_value ( true )
. possible_values ( & [ " pico " , " full " , " none " ] )
. help ( " Overwrite inflation when warping " ) ,
2020-07-20 03:09:38 -07:00
)
2022-04-26 14:49:35 -07:00
. arg (
Arg ::with_name ( " enable_credits_auto_rewind " )
. required ( false )
. long ( " enable-credits-auto-rewind " )
. takes_value ( false )
. help ( " Enable credits auto rewind " ) ,
)
2020-07-20 03:09:38 -07:00
. arg (
Arg ::with_name ( " recalculate_capitalization " )
. required ( false )
. long ( " recalculate-capitalization " )
. takes_value ( false )
. help ( " Recalculate capitalization before warping; circumvents \
bank ' s out - of - sync capitalization " ),
)
2020-11-07 23:43:50 -08:00
. arg (
Arg ::with_name ( " csv_filename " )
. long ( " csv-filename " )
. value_name ( " FILENAME " )
. takes_value ( true )
. help ( " Output file in the csv format " ) ,
)
2020-03-08 12:40:56 -07:00
) . subcommand (
SubCommand ::with_name ( " purge " )
2021-02-25 17:58:19 -08:00
. about ( " Delete a range of slots from the ledger " )
2020-03-08 12:40:56 -07:00
. arg (
Arg ::with_name ( " start_slot " )
. index ( 1 )
. value_name ( " SLOT " )
. takes_value ( true )
. required ( true )
2020-05-24 21:41:54 -07:00
. help ( " Start slot to purge from (inclusive) " ) ,
2020-03-08 12:40:56 -07:00
)
. arg (
Arg ::with_name ( " end_slot " )
. index ( 2 )
. value_name ( " SLOT " )
2021-02-25 17:58:19 -08:00
. help ( " Ending slot to stop purging (inclusive) \
[ default : the highest slot in the ledger ] " ),
2020-03-08 12:40:56 -07:00
)
2020-10-21 10:45:21 -07:00
. arg (
Arg ::with_name ( " batch_size " )
. long ( " batch-size " )
. value_name ( " NUM " )
. takes_value ( true )
. default_value ( " 1000 " )
. help ( " Removes at most BATCH_SIZE slots while purging in loop " ) ,
)
2020-07-14 09:41:41 -07:00
. arg (
Arg ::with_name ( " no_compaction " )
. long ( " no-compaction " )
. required ( false )
. takes_value ( false )
2023-03-29 08:34:00 -07:00
. help ( " --no-compaction is deprecated, ledger compaction \
after purge is disabled by default " )
. conflicts_with ( " enable_compaction " )
. hidden ( hidden_unless_forced ( ) )
)
. arg (
Arg ::with_name ( " enable_compaction " )
. long ( " enable-compaction " )
. required ( false )
. takes_value ( false )
. help ( " Perform ledger compaction after purge. Compaction \
will optimize storage space , but may take a long \
time to complete . " )
. conflicts_with ( " no_compaction " )
2020-07-14 09:41:41 -07:00
)
2020-10-21 10:45:21 -07:00
. arg (
Arg ::with_name ( " dead_slots_only " )
. long ( " dead-slots-only " )
. required ( false )
. takes_value ( false )
2021-02-08 12:37:33 -08:00
. help ( " Limit purging to dead slots only " )
2020-10-21 10:45:21 -07:00
)
2019-11-04 21:14:55 -08:00
)
2019-08-09 15:57:31 -07:00
. subcommand (
SubCommand ::with_name ( " list-roots " )
2021-02-25 17:58:19 -08:00
. about ( " Output up to last <num-roots> root hashes and their \
heights starting at the given block height " )
2019-08-09 15:57:31 -07:00
. arg (
Arg ::with_name ( " max_height " )
. long ( " max-height " )
. value_name ( " NUM " )
. takes_value ( true )
2019-11-04 22:18:30 -08:00
. help ( " Maximum block height " )
)
2021-02-25 15:52:16 -08:00
. arg (
Arg ::with_name ( " start_root " )
. long ( " start-root " )
. value_name ( " NUM " )
. takes_value ( true )
. help ( " First root to start searching from " )
)
2019-11-04 22:18:30 -08:00
. arg (
Arg ::with_name ( " slot_list " )
. long ( " slot-list " )
. value_name ( " FILENAME " )
. required ( false )
. takes_value ( true )
2021-02-25 17:58:19 -08:00
. help ( " The location of the output YAML file. A list of \
rollback slot heights and hashes will be written to the file " )
2019-11-04 22:18:30 -08:00
)
. arg (
Arg ::with_name ( " num_roots " )
. long ( " num-roots " )
. value_name ( " NUM " )
. takes_value ( true )
. default_value ( DEFAULT_ROOT_COUNT )
. required ( false )
. help ( " Number of roots in the output " ) ,
)
)
2022-05-24 12:03:28 -07:00
. subcommand (
SubCommand ::with_name ( " latest-optimistic-slots " )
. about ( " Output up to the most recent <num-slots> optimistic \
slots with their hashes and timestamps . " )
. arg (
Arg ::with_name ( " num_slots " )
. long ( " num-slots " )
. value_name ( " NUM " )
. takes_value ( true )
. default_value ( DEFAULT_LATEST_OPTIMISTIC_SLOTS_COUNT )
. required ( false )
. help ( " Number of slots in the output " ) ,
)
2023-03-23 14:13:41 -07:00
. arg (
Arg ::with_name ( " exclude_vote_only_slots " )
. long ( " exclude-vote-only-slots " )
. required ( false )
. help ( " Exclude slots that contain only votes from output " ) ,
)
2022-05-24 12:03:28 -07:00
)
2021-05-06 13:12:01 -07:00
. subcommand (
SubCommand ::with_name ( " repair-roots " )
. about ( " Traverses the AncestorIterator backward from a last known root \
to restore missing roots to the Root column " )
. arg (
Arg ::with_name ( " start_root " )
. long ( " before " )
. value_name ( " NUM " )
. takes_value ( true )
2022-06-16 09:03:27 -07:00
. help ( " Recent root after the range to repair " )
2021-05-06 13:12:01 -07:00
)
. arg (
Arg ::with_name ( " end_root " )
. long ( " until " )
. value_name ( " NUM " )
. takes_value ( true )
2022-06-16 09:03:27 -07:00
. help ( " Earliest slot to check for root repair " )
2021-05-06 13:12:01 -07:00
)
. arg (
Arg ::with_name ( " max_slots " )
. long ( " repair-limit " )
. value_name ( " NUM " )
. takes_value ( true )
. default_value ( DEFAULT_MAX_SLOTS_ROOT_REPAIR )
. required ( true )
. help ( " Override the maximum number of slots to check for root repair " )
)
)
2019-12-12 15:54:50 -08:00
. subcommand (
SubCommand ::with_name ( " analyze-storage " )
2021-02-25 17:58:19 -08:00
. about ( " Output statistics in JSON format about \
all column families in the ledger rocksdb " )
2019-12-12 15:54:50 -08:00
)
2021-06-02 09:29:02 -07:00
. subcommand (
SubCommand ::with_name ( " compute-slot-cost " )
. about ( " runs cost_model over the block at the given slots, \
computes how expensive a block was based on cost_model " )
. arg (
Arg ::with_name ( " slots " )
. index ( 1 )
. value_name ( " SLOTS " )
. validator ( is_slot )
. multiple ( true )
. takes_value ( true )
. help ( " Slots that their blocks are computed for cost, default to all slots in ledger " ) ,
)
)
2022-09-06 21:46:35 -07:00
. subcommand (
SubCommand ::with_name ( " print-file-metadata " )
. about ( " Print the metadata of the specified ledger-store file. \
If no file name is specified , it will print the metadata of all ledger files . " )
. arg (
Arg ::with_name ( " file_name " )
. long ( " file-name " )
. takes_value ( true )
. value_name ( " SST_FILE_NAME " )
. help ( " The ledger file name (e.g. 011080.sst.) \
If no file name is specified , it will print the metadata of all ledger files . " )
)
)
2023-05-10 12:25:38 -07:00
. program_subcommand ( )
2018-08-04 14:31:12 -07:00
. get_matches ( ) ;
2020-10-03 08:30:26 -07:00
info! ( " {} {} " , crate_name! ( ) , solana_version ::version! ( ) ) ;
2023-02-01 11:01:58 -08:00
let ledger_path = PathBuf ::from ( value_t_or_exit! ( matches , " ledger_path " , String ) ) ;
2020-07-08 09:32:11 -07:00
let snapshot_archive_path = value_t! ( matches , " snapshot_archive_path " , String )
. ok ( )
. map ( PathBuf ::from ) ;
2022-05-10 13:37:41 -07:00
let incremental_snapshot_archive_path =
value_t! ( matches , " incremental_snapshot_archive_path " , String )
. ok ( )
. map ( PathBuf ::from ) ;
2020-07-08 09:32:11 -07:00
2020-07-06 12:43:45 -07:00
let wal_recovery_mode = matches
. value_of ( " wal_recovery_mode " )
. map ( BlockstoreRecoveryMode ::from ) ;
2022-08-14 03:20:13 -07:00
let force_update_to_open = matches . is_present ( " force_update_to_open " ) ;
2021-09-11 14:44:37 -07:00
let verbose_level = matches . occurrences_of ( " verbose " ) ;
2022-07-18 10:19:17 -07:00
2021-12-02 16:23:51 -08:00
if let ( " bigtable " , Some ( arg_matches ) ) = matches . subcommand ( ) {
2023-02-02 14:55:18 -08:00
bigtable_process_command ( & ledger_path , arg_matches )
2021-12-02 16:23:51 -08:00
} else {
let ledger_path = canonicalize_ledger_path ( & ledger_path ) ;
match matches . subcommand ( ) {
( " print " , Some ( arg_matches ) ) = > {
let starting_slot = value_t_or_exit! ( arg_matches , " starting_slot " , Slot ) ;
let ending_slot = value_t! ( arg_matches , " ending_slot " , Slot ) . unwrap_or ( Slot ::MAX ) ;
let num_slots = value_t! ( arg_matches , " num_slots " , Slot ) . ok ( ) ;
let allow_dead_slots = arg_matches . is_present ( " allow_dead_slots " ) ;
let only_rooted = arg_matches . is_present ( " only_rooted " ) ;
output_ledger (
2022-07-18 10:19:17 -07:00
open_blockstore (
& ledger_path ,
AccessType ::Secondary ,
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ,
2021-12-02 16:23:51 -08:00
starting_slot ,
ending_slot ,
allow_dead_slots ,
LedgerOutputMethod ::Print ,
num_slots ,
verbose_level ,
only_rooted ,
) ;
}
( " copy " , Some ( arg_matches ) ) = > {
let starting_slot = value_t_or_exit! ( arg_matches , " starting_slot " , Slot ) ;
let ending_slot = value_t_or_exit! ( arg_matches , " ending_slot " , Slot ) ;
let target_db = PathBuf ::from ( value_t_or_exit! ( arg_matches , " target_db " , String ) ) ;
2022-09-13 10:27:58 -07:00
2022-07-18 10:19:17 -07:00
let source = open_blockstore (
& ledger_path ,
AccessType ::Secondary ,
None ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
) ;
2023-02-02 14:55:18 -08:00
// Check if shred storage type can be inferred; if not, a new
// ledger is being created. open_blockstore() will attempt to
// to infer shred storage type as well, but this check provides
// extra insight to user on how to create a FIFO ledger.
let _ = get_shred_storage_type (
2022-08-14 03:20:13 -07:00
& target_db ,
2023-02-02 14:55:18 -08:00
& format! (
" No --target-db ledger at {:?} was detected, default \
compaction ( RocksLevel ) will be used . Fifo compaction \
can be enabled for a new ledger by manually creating \
{ BLOCKSTORE_DIRECTORY_ROCKS_FIFO } directory within \
the specified - - target_db directory . " ,
& target_db
) ,
2022-07-18 10:19:17 -07:00
) ;
2023-02-02 14:55:18 -08:00
let target =
open_blockstore ( & target_db , AccessType ::Primary , None , force_update_to_open ) ;
2021-12-02 16:23:51 -08:00
for ( slot , _meta ) in source . slot_meta_iterator ( starting_slot ) . unwrap ( ) {
if slot > ending_slot {
break ;
}
if let Ok ( shreds ) = source . get_data_shreds_for_slot ( slot , 0 ) {
if target . insert_shreds ( shreds , None , true ) . is_err ( ) {
warn! ( " error inserting shreds for slot {} " , slot ) ;
}
2020-06-25 11:56:36 -07:00
}
}
}
2021-12-02 16:23:51 -08:00
( " genesis " , Some ( arg_matches ) ) = > {
2022-03-24 12:26:08 -07:00
let genesis_config = open_genesis_config_by ( & ledger_path , arg_matches ) ;
2022-09-18 21:52:53 -07:00
let print_accounts = arg_matches . is_present ( " accounts " ) ;
if print_accounts {
2022-03-24 12:26:08 -07:00
let print_account_data = ! arg_matches . is_present ( " no_account_data " ) ;
2022-11-21 10:15:14 -08:00
let print_encoding_format = parse_encoding_format ( arg_matches ) ;
2022-03-24 12:26:08 -07:00
for ( pubkey , account ) in genesis_config . accounts {
output_account (
& pubkey ,
& AccountSharedData ::from ( account ) ,
None ,
print_account_data ,
2022-09-18 21:52:53 -07:00
print_encoding_format ,
2022-03-24 12:26:08 -07:00
) ;
}
} else {
2022-12-06 06:30:06 -08:00
println! ( " {genesis_config} " ) ;
2022-03-24 12:26:08 -07:00
}
2020-08-25 22:27:31 -07:00
}
2021-12-02 16:23:51 -08:00
( " genesis-hash " , Some ( arg_matches ) ) = > {
println! (
" {} " ,
open_genesis_config_by ( & ledger_path , arg_matches ) . hash ( )
) ;
2020-08-25 22:27:31 -07:00
}
2021-12-02 16:23:51 -08:00
( " modify-genesis " , Some ( arg_matches ) ) = > {
let mut genesis_config = open_genesis_config_by ( & ledger_path , arg_matches ) ;
let output_directory =
PathBuf ::from ( arg_matches . value_of ( " output_directory " ) . unwrap ( ) ) ;
2020-08-28 18:16:01 -07:00
2021-12-02 16:23:51 -08:00
if let Some ( cluster_type ) = cluster_type_of ( arg_matches , " cluster_type " ) {
genesis_config . cluster_type = cluster_type ;
}
2020-08-28 18:16:01 -07:00
2021-12-02 16:23:51 -08:00
if let Some ( hashes_per_tick ) = arg_matches . value_of ( " hashes_per_tick " ) {
genesis_config . poh_config . hashes_per_tick = match hashes_per_tick {
// Note: Unlike `solana-genesis`, "auto" is not supported here.
" sleep " = > None ,
_ = > Some ( value_t_or_exit! ( arg_matches , " hashes_per_tick " , u64 ) ) ,
}
2020-02-07 08:57:54 -08:00
}
2021-12-02 16:23:51 -08:00
create_new_ledger (
& output_directory ,
& genesis_config ,
solana_runtime ::hardened_unpack ::MAX_GENESIS_ARCHIVE_UNPACKED_SIZE ,
2022-03-18 11:13:35 -07:00
LedgerColumnOptions ::default ( ) ,
2021-12-02 16:23:51 -08:00
)
. unwrap_or_else ( | err | {
2022-12-06 06:30:06 -08:00
eprintln! ( " Failed to write genesis config: {err:?} " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
} ) ;
println! ( " {} " , open_genesis_config_by ( & output_directory , arg_matches ) ) ;
2021-01-23 11:55:15 -08:00
}
2021-12-02 16:23:51 -08:00
( " shred-version " , Some ( arg_matches ) ) = > {
let process_options = ProcessOptions {
new_hard_forks : hardforks_of ( arg_matches , " hard_forks " ) ,
2022-04-19 15:06:30 -07:00
halt_at_slot : Some ( 0 ) ,
2023-03-22 11:03:30 -07:00
run_verification : false ,
2023-03-24 10:46:09 -07:00
accounts_db_config : Some ( get_accounts_db_config ( & ledger_path , arg_matches ) ) ,
2021-12-02 16:23:51 -08:00
.. ProcessOptions ::default ( )
} ;
let genesis_config = open_genesis_config_by ( & ledger_path , arg_matches ) ;
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
2023-06-06 15:32:24 -07:00
get_access_type ( & process_options ) ,
2022-07-18 10:19:17 -07:00
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2023-05-31 19:12:49 -07:00
match load_and_process_ledger (
2021-12-02 16:23:51 -08:00
arg_matches ,
& genesis_config ,
2023-02-09 12:34:20 -08:00
Arc ::new ( blockstore ) ,
2021-12-02 16:23:51 -08:00
process_options ,
snapshot_archive_path ,
2022-05-10 13:37:41 -07:00
incremental_snapshot_archive_path ,
2021-12-02 16:23:51 -08:00
) {
Ok ( ( bank_forks , .. ) ) = > {
2020-12-08 09:31:24 -08:00
println! (
2021-12-02 16:23:51 -08:00
" {} " ,
compute_shred_version (
& genesis_config . hash ( ) ,
2022-03-04 01:52:22 -08:00
Some (
& bank_forks
. read ( )
. unwrap ( )
. working_bank ( )
. hard_forks ( )
. read ( )
. unwrap ( )
)
2021-12-02 16:23:51 -08:00
)
2020-12-08 09:31:24 -08:00
) ;
}
2021-12-02 16:23:51 -08:00
Err ( err ) = > {
2022-12-06 06:30:06 -08:00
eprintln! ( " Failed to load ledger: {err:?} " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
}
2020-12-08 09:31:24 -08:00
}
}
2021-12-02 16:23:51 -08:00
( " shred-meta " , Some ( arg_matches ) ) = > {
#[ derive(Debug) ]
#[ allow(dead_code) ]
struct ShredMeta < ' a > {
slot : Slot ,
full_slot : bool ,
shred_index : usize ,
data : bool ,
code : bool ,
last_in_slot : bool ,
data_complete : bool ,
shred : & ' a Shred ,
2020-08-30 09:04:14 -07:00
}
2021-12-02 16:23:51 -08:00
let starting_slot = value_t_or_exit! ( arg_matches , " starting_slot " , Slot ) ;
let ending_slot = value_t! ( arg_matches , " ending_slot " , Slot ) . unwrap_or ( Slot ::MAX ) ;
2022-07-18 10:19:17 -07:00
let ledger = open_blockstore (
& ledger_path ,
AccessType ::Secondary ,
None ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2021-12-02 16:23:51 -08:00
for ( slot , _meta ) in ledger
. slot_meta_iterator ( starting_slot )
. unwrap ( )
. take_while ( | ( slot , _ ) | * slot < = ending_slot )
{
let full_slot = ledger . is_full ( slot ) ;
if let Ok ( shreds ) = ledger . get_data_shreds_for_slot ( slot , 0 ) {
for ( shred_index , shred ) in shreds . iter ( ) . enumerate ( ) {
println! (
" {:#?} " ,
ShredMeta {
slot ,
full_slot ,
shred_index ,
data : shred . is_data ( ) ,
code : shred . is_code ( ) ,
data_complete : shred . data_complete ( ) ,
last_in_slot : shred . last_in_slot ( ) ,
shred ,
}
) ;
}
}
2020-08-30 09:04:14 -07:00
}
}
2021-12-02 16:23:51 -08:00
( " bank-hash " , Some ( arg_matches ) ) = > {
let process_options = ProcessOptions {
new_hard_forks : hardforks_of ( arg_matches , " hard_forks " ) ,
2023-01-24 14:15:07 -08:00
halt_at_slot : value_t ! ( arg_matches , " halt_at_slot " , Slot ) . ok ( ) ,
2023-03-22 11:03:30 -07:00
run_verification : false ,
2023-03-24 10:46:09 -07:00
accounts_db_config : Some ( get_accounts_db_config ( & ledger_path , arg_matches ) ) ,
2021-12-02 16:23:51 -08:00
.. ProcessOptions ::default ( )
} ;
let genesis_config = open_genesis_config_by ( & ledger_path , arg_matches ) ;
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
2023-06-06 15:32:24 -07:00
get_access_type ( & process_options ) ,
2022-07-18 10:19:17 -07:00
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2023-05-31 19:12:49 -07:00
match load_and_process_ledger (
2021-12-02 16:23:51 -08:00
arg_matches ,
& genesis_config ,
2023-02-09 12:34:20 -08:00
Arc ::new ( blockstore ) ,
2021-12-02 16:23:51 -08:00
process_options ,
snapshot_archive_path ,
2022-05-10 13:37:41 -07:00
incremental_snapshot_archive_path ,
2020-04-09 20:10:51 -07:00
) {
2021-12-02 16:23:51 -08:00
Ok ( ( bank_forks , .. ) ) = > {
2022-03-04 01:52:22 -08:00
println! ( " {} " , & bank_forks . read ( ) . unwrap ( ) . working_bank ( ) . hash ( ) ) ;
2021-12-02 16:23:51 -08:00
}
Err ( err ) = > {
2022-12-06 06:30:06 -08:00
eprintln! ( " Failed to load ledger: {err:?} " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
}
2020-03-21 21:43:33 -07:00
}
2019-11-11 22:22:20 -08:00
}
2021-12-02 16:23:51 -08:00
( " slot " , Some ( arg_matches ) ) = > {
let slots = values_t_or_exit! ( arg_matches , " slots " , Slot ) ;
let allow_dead_slots = arg_matches . is_present ( " allow_dead_slots " ) ;
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
AccessType ::Secondary ,
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2021-12-02 16:23:51 -08:00
for slot in slots {
2022-12-06 06:30:06 -08:00
println! ( " Slot {slot} " ) ;
2021-12-02 16:23:51 -08:00
if let Err ( err ) = output_slot (
& blockstore ,
slot ,
allow_dead_slots ,
& LedgerOutputMethod ::Print ,
2021-12-07 13:14:39 -08:00
verbose_level ,
2022-05-19 14:26:29 -07:00
& mut HashMap ::new ( ) ,
2021-12-02 16:23:51 -08:00
) {
2022-12-06 06:30:06 -08:00
eprintln! ( " {err} " ) ;
2021-12-02 16:23:51 -08:00
}
}
2020-10-21 10:45:21 -07:00
}
2021-12-02 16:23:51 -08:00
( " json " , Some ( arg_matches ) ) = > {
let starting_slot = value_t_or_exit! ( arg_matches , " starting_slot " , Slot ) ;
let allow_dead_slots = arg_matches . is_present ( " allow_dead_slots " ) ;
output_ledger (
2022-07-18 10:19:17 -07:00
open_blockstore (
& ledger_path ,
AccessType ::Secondary ,
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ,
2021-12-02 16:23:51 -08:00
starting_slot ,
Slot ::MAX ,
allow_dead_slots ,
LedgerOutputMethod ::Json ,
None ,
std ::u64 ::MAX ,
true ,
) ;
2021-04-02 21:48:44 -07:00
}
2021-12-02 16:23:51 -08:00
( " dead-slots " , Some ( arg_matches ) ) = > {
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
AccessType ::Secondary ,
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2021-12-02 16:23:51 -08:00
let starting_slot = value_t_or_exit! ( arg_matches , " starting_slot " , Slot ) ;
for slot in blockstore . dead_slots_iterator ( starting_slot ) . unwrap ( ) {
2022-12-06 06:30:06 -08:00
println! ( " {slot} " ) ;
2021-10-19 20:23:16 -07:00
}
}
2021-12-02 16:23:51 -08:00
( " duplicate-slots " , Some ( arg_matches ) ) = > {
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
AccessType ::Secondary ,
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2021-12-02 16:23:51 -08:00
let starting_slot = value_t_or_exit! ( arg_matches , " starting_slot " , Slot ) ;
for slot in blockstore . duplicate_slots_iterator ( starting_slot ) . unwrap ( ) {
2022-12-06 06:30:06 -08:00
println! ( " {slot} " ) ;
2021-12-02 16:23:51 -08:00
}
}
( " set-dead-slot " , Some ( arg_matches ) ) = > {
let slots = values_t_or_exit! ( arg_matches , " slots " , Slot ) ;
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
AccessType ::Primary ,
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2021-12-02 16:23:51 -08:00
for slot in slots {
match blockstore . set_dead_slot ( slot ) {
2022-12-06 06:30:06 -08:00
Ok ( _ ) = > println! ( " Slot {slot} dead " ) ,
Err ( err ) = > eprintln! ( " Failed to set slot {slot} dead slot: {err:?} " ) ,
2021-10-19 20:23:16 -07:00
}
2020-03-21 21:43:33 -07:00
}
}
2021-12-02 16:23:51 -08:00
( " remove-dead-slot " , Some ( arg_matches ) ) = > {
let slots = values_t_or_exit! ( arg_matches , " slots " , Slot ) ;
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
AccessType ::Primary ,
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2021-12-02 16:23:51 -08:00
for slot in slots {
match blockstore . remove_dead_slot ( slot ) {
2022-12-06 06:30:06 -08:00
Ok ( _ ) = > println! ( " Slot {slot} not longer marked dead " ) ,
2021-12-02 16:23:51 -08:00
Err ( err ) = > {
2022-12-06 06:30:06 -08:00
eprintln! ( " Failed to remove dead flag for slot {slot} , {err:?} " )
2021-12-02 16:23:51 -08:00
}
}
2020-06-30 02:20:54 -07:00
}
}
2021-12-02 16:23:51 -08:00
( " parse_full_frozen " , Some ( arg_matches ) ) = > {
let starting_slot = value_t_or_exit! ( arg_matches , " starting_slot " , Slot ) ;
let ending_slot = value_t_or_exit! ( arg_matches , " ending_slot " , Slot ) ;
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
AccessType ::Secondary ,
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2021-12-02 16:23:51 -08:00
let mut ancestors = BTreeSet ::new ( ) ;
assert! (
blockstore . meta ( ending_slot ) . unwrap ( ) . is_some ( ) ,
" Ending slot doesn't exist "
) ;
for a in AncestorIterator ::new ( ending_slot , & blockstore ) {
ancestors . insert ( a ) ;
if a < = starting_slot {
break ;
2021-04-18 10:27:36 -07:00
}
2021-12-02 16:23:51 -08:00
}
println! ( " ancestors: {:?} " , ancestors . iter ( ) ) ;
let mut frozen = BTreeMap ::new ( ) ;
let mut full = BTreeMap ::new ( ) ;
let frozen_regex = Regex ::new ( r "bank frozen: (\d*)" ) . unwrap ( ) ;
let full_regex = Regex ::new ( r "slot (\d*) is full" ) . unwrap ( ) ;
let log_file = PathBuf ::from ( value_t_or_exit! ( arg_matches , " log_path " , String ) ) ;
let f = BufReader ::new ( File ::open ( log_file ) . unwrap ( ) ) ;
println! ( " Reading log file " ) ;
for line in f . lines ( ) . flatten ( ) {
let parse_results = {
if let Some ( slot_string ) = frozen_regex . captures_iter ( & line ) . next ( ) {
Some ( ( slot_string , & mut frozen ) )
} else {
full_regex
. captures_iter ( & line )
. next ( )
. map ( | slot_string | ( slot_string , & mut full ) )
}
} ;
2021-04-18 10:27:36 -07:00
2021-12-02 16:23:51 -08:00
if let Some ( ( slot_string , map ) ) = parse_results {
let slot = slot_string
. get ( 1 )
. expect ( " Only one match group " )
. as_str ( )
. parse ::< u64 > ( )
. unwrap ( ) ;
if ancestors . contains ( & slot ) & & ! map . contains_key ( & slot ) {
map . insert ( slot , line ) ;
}
if slot = = ending_slot
& & frozen . contains_key ( & slot )
& & full . contains_key ( & slot )
{
break ;
}
2020-06-30 02:20:54 -07:00
}
}
2021-12-02 16:23:51 -08:00
for ( ( slot1 , frozen_log ) , ( slot2 , full_log ) ) in frozen . iter ( ) . zip ( full . iter ( ) ) {
assert_eq! ( slot1 , slot2 ) ;
2022-12-06 06:30:06 -08:00
println! ( " Slot: {slot1} \n , full: {full_log} \n , frozen: {frozen_log} " ) ;
2021-12-02 16:23:51 -08:00
}
2021-09-17 11:12:06 -07:00
}
2021-12-02 16:23:51 -08:00
( " verify " , Some ( arg_matches ) ) = > {
let exit_signal = Arc ::new ( AtomicBool ::new ( false ) ) ;
2022-05-03 09:23:30 -07:00
let no_os_memory_stats_reporting =
arg_matches . is_present ( " no_os_memory_stats_reporting " ) ;
let system_monitor_service = SystemMonitorService ::new (
Arc ::clone ( & exit_signal ) ,
2022-12-09 05:43:03 -08:00
SystemMonitorStatsReportConfig {
report_os_memory_stats : ! no_os_memory_stats_reporting ,
report_os_network_stats : false ,
report_os_cpu_stats : false ,
report_os_disk_stats : false ,
} ,
2022-05-03 09:23:30 -07:00
) ;
2021-11-15 07:29:30 -08:00
2022-05-24 17:56:35 -07:00
let debug_keys = pubkeys_of ( arg_matches , " debug_key " )
. map ( | pubkeys | Arc ::new ( pubkeys . into_iter ( ) . collect ::< HashSet < _ > > ( ) ) ) ;
2023-03-22 11:03:30 -07:00
if arg_matches . is_present ( " skip_poh_verify " ) {
eprintln! (
" --skip-poh-verify is deprecated. Replace with --skip-verification. "
) ;
}
2021-12-02 16:23:51 -08:00
let process_options = ProcessOptions {
new_hard_forks : hardforks_of ( arg_matches , " hard_forks " ) ,
2023-03-22 11:03:30 -07:00
run_verification : ! ( arg_matches . is_present ( " skip_poh_verify " )
| | arg_matches . is_present ( " skip_verification " ) ) ,
2022-07-29 13:54:56 -07:00
on_halt_store_hash_raw_data_for_debug : arg_matches
. is_present ( " halt_at_slot_store_hash_raw_data " ) ,
2023-04-12 07:30:45 -07:00
run_final_accounts_hash_calc : arg_matches . is_present ( " run_final_hash_calc " ) ,
2022-04-19 15:06:30 -07:00
halt_at_slot : value_t ! ( arg_matches , " halt_at_slot " , Slot ) . ok ( ) ,
2022-05-24 17:56:35 -07:00
debug_keys ,
2021-12-02 16:23:51 -08:00
limit_load_slot_count_from_snapshot : value_t ! (
arg_matches ,
" limit_load_slot_count_from_snapshot " ,
usize
)
. ok ( ) ,
2023-01-30 12:59:35 -08:00
accounts_db_config : Some ( get_accounts_db_config ( & ledger_path , arg_matches ) ) ,
2021-12-02 16:23:51 -08:00
verify_index : arg_matches . is_present ( " verify_accounts_index " ) ,
allow_dead_slots : arg_matches . is_present ( " allow_dead_slots " ) ,
accounts_db_test_hash_calculation : arg_matches
. is_present ( " accounts_db_test_hash_calculation " ) ,
accounts_db_skip_shrink : arg_matches . is_present ( " accounts_db_skip_shrink " ) ,
2023-04-25 10:04:11 -07:00
runtime_config : RuntimeConfig ::default ( ) ,
2023-06-06 15:32:24 -07:00
boot_from_local_state : arg_matches . is_present ( " boot_from_local_state " ) ,
2021-12-02 16:23:51 -08:00
.. ProcessOptions ::default ( )
} ;
let print_accounts_stats = arg_matches . is_present ( " print_accounts_stats " ) ;
2023-01-26 23:20:42 -08:00
let genesis_config = open_genesis_config_by ( & ledger_path , arg_matches ) ;
info! ( " genesis hash: {} " , genesis_config . hash ( ) ) ;
2021-09-07 21:30:38 -07:00
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
2023-06-06 15:32:24 -07:00
get_access_type ( & process_options ) ,
2022-07-18 10:19:17 -07:00
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2023-05-31 19:12:49 -07:00
let ( bank_forks , .. ) = load_and_process_ledger (
2021-05-26 08:36:12 -07:00
arg_matches ,
2023-01-26 23:20:42 -08:00
& genesis_config ,
2023-02-09 12:34:20 -08:00
Arc ::new ( blockstore ) ,
2021-12-02 16:23:51 -08:00
process_options ,
snapshot_archive_path ,
2022-05-10 13:37:41 -07:00
incremental_snapshot_archive_path ,
2021-05-26 08:36:12 -07:00
)
2021-12-02 16:23:51 -08:00
. unwrap_or_else ( | err | {
2022-12-06 06:30:06 -08:00
eprintln! ( " Ledger verification failed: {err:?} " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
} ) ;
if print_accounts_stats {
2022-03-04 01:52:22 -08:00
let working_bank = bank_forks . read ( ) . unwrap ( ) . working_bank ( ) ;
2021-12-02 16:23:51 -08:00
working_bank . print_accounts_stats ( ) ;
}
exit_signal . store ( true , Ordering ::Relaxed ) ;
system_monitor_service . join ( ) . unwrap ( ) ;
2020-07-08 09:32:11 -07:00
}
2021-12-02 16:23:51 -08:00
( " graph " , Some ( arg_matches ) ) = > {
let output_file = value_t_or_exit! ( arg_matches , " graph_filename " , String ) ;
2022-07-18 15:31:23 -07:00
let graph_config = GraphConfig {
include_all_votes : arg_matches . is_present ( " include_all_votes " ) ,
2022-07-18 21:46:01 -07:00
vote_account_mode : value_t_or_exit ! (
arg_matches ,
" vote_account_mode " ,
GraphVoteAccountMode
) ,
2022-07-18 15:31:23 -07:00
} ;
2021-12-02 16:23:51 -08:00
let process_options = ProcessOptions {
new_hard_forks : hardforks_of ( arg_matches , " hard_forks " ) ,
2022-04-19 15:06:30 -07:00
halt_at_slot : value_t ! ( arg_matches , " halt_at_slot " , Slot ) . ok ( ) ,
2023-03-22 11:03:30 -07:00
run_verification : false ,
2023-03-24 10:46:09 -07:00
accounts_db_config : Some ( get_accounts_db_config ( & ledger_path , arg_matches ) ) ,
2023-06-06 15:32:24 -07:00
boot_from_local_state : arg_matches . is_present ( " boot_from_local_state " ) ,
2021-12-02 16:23:51 -08:00
.. ProcessOptions ::default ( )
} ;
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
2023-06-06 15:32:24 -07:00
get_access_type ( & process_options ) ,
2022-07-18 10:19:17 -07:00
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2023-05-31 19:12:49 -07:00
match load_and_process_ledger (
2021-12-02 16:23:51 -08:00
arg_matches ,
& open_genesis_config_by ( & ledger_path , arg_matches ) ,
2023-02-09 12:34:20 -08:00
Arc ::new ( blockstore ) ,
2021-12-02 16:23:51 -08:00
process_options ,
snapshot_archive_path ,
2022-05-10 13:37:41 -07:00
incremental_snapshot_archive_path ,
2021-12-02 16:23:51 -08:00
) {
Ok ( ( bank_forks , .. ) ) = > {
2022-07-18 15:31:23 -07:00
let dot = graph_forks ( & bank_forks . read ( ) . unwrap ( ) , & graph_config ) ;
2021-12-02 16:23:51 -08:00
let extension = Path ::new ( & output_file ) . extension ( ) ;
let result = if extension = = Some ( OsStr ::new ( " pdf " ) ) {
render_dot ( dot , & output_file , " pdf " )
} else if extension = = Some ( OsStr ::new ( " png " ) ) {
render_dot ( dot , & output_file , " png " )
} else {
File ::create ( & output_file )
. and_then ( | mut file | file . write_all ( & dot . into_bytes ( ) ) )
} ;
2019-11-12 09:13:16 -08:00
2021-12-02 16:23:51 -08:00
match result {
2022-12-06 06:30:06 -08:00
Ok ( _ ) = > println! ( " Wrote {output_file} " ) ,
Err ( err ) = > eprintln! ( " Unable to write {output_file} : {err} " ) ,
2021-12-02 16:23:51 -08:00
}
}
Err ( err ) = > {
2022-12-06 06:30:06 -08:00
eprintln! ( " Failed to load ledger: {err:?} " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2019-11-04 22:18:30 -08:00
}
2020-01-23 15:09:36 -08:00
}
}
2021-12-02 16:23:51 -08:00
( " create-snapshot " , Some ( arg_matches ) ) = > {
2022-05-10 13:37:41 -07:00
let is_incremental = arg_matches . is_present ( " incremental " ) ;
2022-06-22 10:17:43 -07:00
let is_minimized = arg_matches . is_present ( " minimized " ) ;
2021-12-02 16:23:51 -08:00
let output_directory = value_t! ( arg_matches , " output_directory " , PathBuf )
2022-05-10 13:37:41 -07:00
. unwrap_or_else ( | _ | {
match (
is_incremental ,
& snapshot_archive_path ,
& incremental_snapshot_archive_path ,
) {
( true , _ , Some ( incremental_snapshot_archive_path ) ) = > {
incremental_snapshot_archive_path . clone ( )
}
( _ , Some ( snapshot_archive_path ) , _ ) = > snapshot_archive_path . clone ( ) ,
( _ , _ , _ ) = > ledger_path . clone ( ) ,
}
2022-05-02 10:00:15 -07:00
} ) ;
2021-12-02 16:23:51 -08:00
let mut warp_slot = value_t! ( arg_matches , " warp_slot " , Slot ) . ok ( ) ;
let remove_stake_accounts = arg_matches . is_present ( " remove_stake_accounts " ) ;
let new_hard_forks = hardforks_of ( arg_matches , " hard_forks " ) ;
let faucet_pubkey = pubkey_of ( arg_matches , " faucet_pubkey " ) ;
let faucet_lamports = value_t! ( arg_matches , " faucet_lamports " , u64 ) . unwrap_or ( 0 ) ;
let rent_burn_percentage = value_t! ( arg_matches , " rent_burn_percentage " , u8 ) ;
let hashes_per_tick = arg_matches . value_of ( " hashes_per_tick " ) ;
let bootstrap_stake_authorized_pubkey =
pubkey_of ( arg_matches , " bootstrap_stake_authorized_pubkey " ) ;
let bootstrap_validator_lamports =
value_t_or_exit! ( arg_matches , " bootstrap_validator_lamports " , u64 ) ;
let bootstrap_validator_stake_lamports =
value_t_or_exit! ( arg_matches , " bootstrap_validator_stake_lamports " , u64 ) ;
2022-04-25 15:14:50 -07:00
let minimum_stake_lamports = rent . minimum_balance ( StakeState ::size_of ( ) ) ;
2021-12-02 16:23:51 -08:00
if bootstrap_validator_stake_lamports < minimum_stake_lamports {
eprintln! (
" Error: insufficient --bootstrap-validator-stake-lamports. \
2022-12-06 06:30:06 -08:00
Minimum amount is { minimum_stake_lamports } "
2021-12-02 16:23:51 -08:00
) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
}
let bootstrap_validator_pubkeys = pubkeys_of ( arg_matches , " bootstrap_validator " ) ;
let accounts_to_remove =
pubkeys_of ( arg_matches , " accounts_to_remove " ) . unwrap_or_default ( ) ;
2023-03-16 14:54:05 -07:00
let feature_gates_to_deactivate =
pubkeys_of ( arg_matches , " feature_gates_to_deactivate " ) . unwrap_or_default ( ) ;
2021-12-02 16:23:51 -08:00
let vote_accounts_to_destake : HashSet < _ > =
pubkeys_of ( arg_matches , " vote_accounts_to_destake " )
. unwrap_or_default ( )
. into_iter ( )
. collect ( ) ;
let snapshot_version = arg_matches . value_of ( " snapshot_version " ) . map_or (
SnapshotVersion ::default ( ) ,
| s | {
2020-06-18 22:38:37 -07:00
s . parse ::< SnapshotVersion > ( ) . unwrap_or_else ( | e | {
2022-12-06 06:30:06 -08:00
eprintln! ( " Error: {e} " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 )
2020-06-18 22:38:37 -07:00
} )
2021-12-02 16:23:51 -08:00
} ,
) ;
2021-02-26 13:44:38 -08:00
2022-05-16 10:44:15 -07:00
let snapshot_archive_format = {
let archive_format_str =
2022-05-17 14:46:31 -07:00
value_t_or_exit! ( arg_matches , " snapshot_archive_format " , String ) ;
2022-05-16 10:44:15 -07:00
ArchiveFormat ::from_cli_arg ( & archive_format_str ) . unwrap_or_else ( | | {
2022-12-06 06:30:06 -08:00
panic! ( " Archive format not recognized: {archive_format_str} " )
2022-05-16 10:44:15 -07:00
} )
} ;
2023-03-30 08:16:36 -07:00
let maximum_full_snapshot_archives_to_retain = value_t_or_exit! (
arg_matches ,
" maximum_full_snapshots_to_retain " ,
NonZeroUsize
) ;
2021-12-02 16:23:51 -08:00
let maximum_incremental_snapshot_archives_to_retain = value_t_or_exit! (
arg_matches ,
" maximum_incremental_snapshots_to_retain " ,
2023-03-30 08:16:36 -07:00
NonZeroUsize
2021-12-02 16:23:51 -08:00
) ;
let genesis_config = open_genesis_config_by ( & ledger_path , arg_matches ) ;
2023-06-16 09:58:57 -07:00
let mut process_options = ProcessOptions {
new_hard_forks ,
run_verification : false ,
accounts_db_config : Some ( get_accounts_db_config ( & ledger_path , arg_matches ) ) ,
accounts_db_skip_shrink : arg_matches . is_present ( " accounts_db_skip_shrink " ) ,
boot_from_local_state : arg_matches . is_present ( " boot_from_local_state " ) ,
.. ProcessOptions ::default ( )
} ;
2023-02-09 12:34:20 -08:00
let blockstore = Arc ::new ( open_blockstore (
2022-07-18 10:19:17 -07:00
& ledger_path ,
2023-06-16 09:58:57 -07:00
get_access_type ( & process_options ) ,
2022-07-18 10:19:17 -07:00
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2023-02-09 12:34:20 -08:00
) ) ;
2020-08-25 22:27:31 -07:00
2021-12-02 16:23:51 -08:00
let snapshot_slot = if Some ( " ROOT " ) = = arg_matches . value_of ( " snapshot_slot " ) {
blockstore
. rooted_slot_iterator ( 0 )
. expect ( " Failed to get rooted slot iterator " )
. last ( )
. expect ( " Failed to get root " )
} else {
value_t_or_exit! ( arg_matches , " snapshot_slot " , Slot )
} ;
2020-08-25 22:27:31 -07:00
2022-11-22 08:02:44 -08:00
if blockstore
. meta ( snapshot_slot )
. unwrap ( )
. filter ( | m | m . is_full ( ) )
. is_none ( )
{
eprintln! (
2022-12-06 06:30:06 -08:00
" Error: snapshot slot {snapshot_slot} does not exist in blockstore or is not full. " ,
2022-11-22 08:02:44 -08:00
) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2022-11-22 08:02:44 -08:00
}
2023-06-16 09:58:57 -07:00
process_options . halt_at_slot = Some ( snapshot_slot ) ;
2022-08-19 07:34:35 -07:00
2022-06-22 10:17:43 -07:00
let ending_slot = if is_minimized {
let ending_slot = value_t_or_exit! ( arg_matches , " ending_slot " , Slot ) ;
if ending_slot < = snapshot_slot {
eprintln! (
2022-12-06 06:30:06 -08:00
" Error: ending_slot ({ending_slot}) must be greater than snapshot_slot ({snapshot_slot}) "
2022-06-22 10:17:43 -07:00
) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2022-06-22 10:17:43 -07:00
}
Some ( ending_slot )
} else {
None
} ;
let snapshot_type_str = if is_incremental {
" incremental "
} else if is_minimized {
" minimized "
} else {
" "
} ;
2021-12-02 16:23:51 -08:00
info! (
" Creating {}snapshot of slot {} in {} " ,
2022-06-22 10:17:43 -07:00
snapshot_type_str ,
2021-12-02 16:23:51 -08:00
snapshot_slot ,
output_directory . display ( )
) ;
2020-08-25 22:27:31 -07:00
2023-05-31 19:12:49 -07:00
match load_and_process_ledger (
2021-12-02 16:23:51 -08:00
arg_matches ,
& genesis_config ,
2023-02-09 12:34:20 -08:00
blockstore . clone ( ) ,
2023-06-16 09:58:57 -07:00
process_options ,
2021-12-02 16:23:51 -08:00
snapshot_archive_path ,
2022-05-10 13:37:41 -07:00
incremental_snapshot_archive_path ,
2021-12-02 16:23:51 -08:00
) {
2022-03-07 02:23:22 -08:00
Ok ( ( bank_forks , starting_snapshot_hashes ) ) = > {
2021-12-02 16:23:51 -08:00
let mut bank = bank_forks
2022-03-04 01:52:22 -08:00
. read ( )
. unwrap ( )
2021-12-02 16:23:51 -08:00
. get ( snapshot_slot )
. unwrap_or_else ( | | {
2022-12-06 06:30:06 -08:00
eprintln! ( " Error: Slot {snapshot_slot} is not available " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2022-04-28 11:51:00 -07:00
} ) ;
2021-12-02 16:23:51 -08:00
let child_bank_required = rent_burn_percentage . is_ok ( )
| | hashes_per_tick . is_some ( )
| | remove_stake_accounts
| | ! accounts_to_remove . is_empty ( )
2023-03-16 14:54:05 -07:00
| | ! feature_gates_to_deactivate . is_empty ( )
2021-12-02 16:23:51 -08:00
| | ! vote_accounts_to_destake . is_empty ( )
| | faucet_pubkey . is_some ( )
| | bootstrap_validator_pubkeys . is_some ( ) ;
if child_bank_required {
let mut child_bank =
Bank ::new_from_parent ( & bank , bank . collector_id ( ) , bank . slot ( ) + 1 ) ;
if let Ok ( rent_burn_percentage ) = rent_burn_percentage {
child_bank . set_rent_burn_percentage ( rent_burn_percentage ) ;
}
if let Some ( hashes_per_tick ) = hashes_per_tick {
child_bank . set_hashes_per_tick ( match hashes_per_tick {
// Note: Unlike `solana-genesis`, "auto" is not supported here.
" sleep " = > None ,
_ = > {
Some ( value_t_or_exit! ( arg_matches , " hashes_per_tick " , u64 ) )
}
} ) ;
}
bank = Arc ::new ( child_bank ) ;
2020-08-25 22:27:31 -07:00
}
2021-12-02 16:23:51 -08:00
if let Some ( faucet_pubkey ) = faucet_pubkey {
bank . store_account (
& faucet_pubkey ,
& AccountSharedData ::new ( faucet_lamports , 0 , & system_program ::id ( ) ) ,
2021-09-11 14:44:37 -07:00
) ;
2021-12-02 16:23:51 -08:00
}
2021-09-11 14:44:37 -07:00
2021-12-02 16:23:51 -08:00
if remove_stake_accounts {
for ( address , mut account ) in bank
. get_program_accounts ( & stake ::program ::id ( ) , & ScanConfig ::default ( ) )
. unwrap ( )
. into_iter ( )
{
account . set_lamports ( 0 ) ;
bank . store_account ( & address , & account ) ;
2021-09-11 14:44:37 -07:00
}
2020-12-04 22:46:32 -08:00
}
2020-08-25 22:27:31 -07:00
2021-12-02 16:23:51 -08:00
for address in accounts_to_remove {
let mut account = bank . get_account ( & address ) . unwrap_or_else ( | | {
2020-08-25 22:27:31 -07:00
eprintln! (
2022-12-06 06:30:06 -08:00
" Error: Account does not exist, unable to remove it: {address} "
2020-08-25 22:27:31 -07:00
) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
} ) ;
2020-08-25 22:27:31 -07:00
2021-04-22 11:56:47 -07:00
account . set_lamports ( 0 ) ;
2020-08-25 22:27:31 -07:00
bank . store_account ( & address , & account ) ;
2023-03-16 14:54:05 -07:00
debug! ( " Account removed: {address} " ) ;
}
for address in feature_gates_to_deactivate {
let mut account = bank . get_account ( & address ) . unwrap_or_else ( | | {
eprintln! (
" Error: Feature-gate account does not exist, unable to deactivate it: {address} "
) ;
exit ( 1 ) ;
} ) ;
match feature ::from_account ( & account ) {
Some ( feature ) = > {
if feature . activated_at . is_none ( ) {
warn! ( " Feature gate is not yet activated: {address} " ) ;
}
}
None = > {
eprintln! ( " Error: Account is not a `Feature`: {address} " ) ;
exit ( 1 ) ;
}
}
account . set_lamports ( 0 ) ;
bank . store_account ( & address , & account ) ;
debug! ( " Feature gate deactivated: {address} " ) ;
2020-08-25 22:27:31 -07:00
}
2021-12-02 16:23:51 -08:00
if ! vote_accounts_to_destake . is_empty ( ) {
for ( address , mut account ) in bank
. get_program_accounts ( & stake ::program ::id ( ) , & ScanConfig ::default ( ) )
. unwrap ( )
. into_iter ( )
{
if let Ok ( StakeState ::Stake ( meta , stake ) ) = account . state ( ) {
if vote_accounts_to_destake
. contains ( & stake . delegation . voter_pubkey )
{
if verbose_level > 0 {
warn! (
" Undelegating stake account {} from {} " ,
address , stake . delegation . voter_pubkey ,
) ;
}
account . set_state ( & StakeState ::Initialized ( meta ) ) . unwrap ( ) ;
bank . store_account ( & address , & account ) ;
}
}
}
}
2020-08-25 22:27:31 -07:00
2021-12-02 16:23:51 -08:00
if let Some ( bootstrap_validator_pubkeys ) = bootstrap_validator_pubkeys {
assert_eq! ( bootstrap_validator_pubkeys . len ( ) % 3 , 0 ) ;
// Ensure there are no duplicated pubkeys in the --bootstrap-validator list
{
let mut v = bootstrap_validator_pubkeys . clone ( ) ;
v . sort ( ) ;
v . dedup ( ) ;
if v . len ( ) ! = bootstrap_validator_pubkeys . len ( ) {
eprintln! (
" Error: --bootstrap-validator pubkeys cannot be duplicated "
) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
}
}
2020-08-25 22:27:31 -07:00
2021-12-02 16:23:51 -08:00
// Delete existing vote accounts
for ( address , mut account ) in bank
. get_program_accounts (
& solana_vote_program ::id ( ) ,
& ScanConfig ::default ( ) ,
)
. unwrap ( )
. into_iter ( )
{
account . set_lamports ( 0 ) ;
bank . store_account ( & address , & account ) ;
}
2020-08-25 22:27:31 -07:00
2021-12-02 16:23:51 -08:00
// Add a new identity/vote/stake account for each of the provided bootstrap
// validators
let mut bootstrap_validator_pubkeys_iter =
bootstrap_validator_pubkeys . iter ( ) ;
loop {
let identity_pubkey = match bootstrap_validator_pubkeys_iter . next ( )
{
None = > break ,
Some ( identity_pubkey ) = > identity_pubkey ,
} ;
let vote_pubkey = bootstrap_validator_pubkeys_iter . next ( ) . unwrap ( ) ;
let stake_pubkey = bootstrap_validator_pubkeys_iter . next ( ) . unwrap ( ) ;
bank . store_account (
identity_pubkey ,
& AccountSharedData ::new (
bootstrap_validator_lamports ,
0 ,
& system_program ::id ( ) ,
) ,
) ;
2020-08-25 22:27:31 -07:00
2021-12-02 16:23:51 -08:00
let vote_account = vote_state ::create_account_with_authorized (
identity_pubkey ,
identity_pubkey ,
identity_pubkey ,
100 ,
VoteState ::get_rent_exempt_reserve ( & rent ) . max ( 1 ) ,
) ;
2020-08-25 22:27:31 -07:00
2021-12-02 16:23:51 -08:00
bank . store_account (
stake_pubkey ,
& stake_state ::create_account (
bootstrap_stake_authorized_pubkey
. as_ref ( )
. unwrap_or ( identity_pubkey ) ,
vote_pubkey ,
& vote_account ,
& rent ,
bootstrap_validator_stake_lamports ,
) ,
2020-08-25 22:27:31 -07:00
) ;
2021-12-02 16:23:51 -08:00
bank . store_account ( vote_pubkey , & vote_account ) ;
}
// Warp ahead at least two epochs to ensure that the leader schedule will be
// updated to reflect the new bootstrap validator(s)
let minimum_warp_slot =
genesis_config . epoch_schedule . get_first_slot_in_epoch (
genesis_config . epoch_schedule . get_epoch ( snapshot_slot ) + 2 ,
) ;
if let Some ( warp_slot ) = warp_slot {
if warp_slot < minimum_warp_slot {
eprintln! (
2022-12-06 06:30:06 -08:00
" Error: --warp-slot too close. Must be >= {minimum_warp_slot} "
2021-12-02 16:23:51 -08:00
) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
}
} else {
warn! ( " Warping to slot {} " , minimum_warp_slot ) ;
warp_slot = Some ( minimum_warp_slot ) ;
2020-08-25 22:27:31 -07:00
}
}
2021-12-02 16:23:51 -08:00
if child_bank_required {
while ! bank . is_complete ( ) {
bank . register_tick ( & Hash ::new_unique ( ) ) ;
}
2021-02-01 12:44:35 -08:00
}
2021-12-02 16:23:51 -08:00
bank . set_capitalization ( ) ;
2020-08-25 22:27:31 -07:00
2021-12-02 16:23:51 -08:00
let bank = if let Some ( warp_slot ) = warp_slot {
2022-12-12 08:22:15 -08:00
// need to flush the write cache in order to use Storages to calculate
// the accounts hash, and need to root `bank` before flushing the cache
bank . rc . accounts . accounts_db . add_root ( bank . slot ( ) ) ;
bank . force_flush_accounts_cache ( ) ;
2021-12-02 16:23:51 -08:00
Arc ::new ( Bank ::warp_from_parent (
& bank ,
bank . collector_id ( ) ,
warp_slot ,
2022-12-12 08:22:15 -08:00
CalcAccountsHashDataSource ::Storages ,
2021-12-02 16:23:51 -08:00
) )
} else {
bank
} ;
2020-01-23 15:09:36 -08:00
2022-06-22 10:17:43 -07:00
if is_minimized {
minimize_bank_for_snapshot (
& blockstore ,
& bank ,
snapshot_slot ,
ending_slot . unwrap ( ) ,
) ;
}
2021-12-02 16:23:51 -08:00
println! (
" Creating a version {} {}snapshot of slot {} " ,
snapshot_version ,
2022-06-22 10:17:43 -07:00
snapshot_type_str ,
2021-12-02 16:23:51 -08:00
bank . slot ( ) ,
) ;
2020-01-23 15:09:36 -08:00
2021-12-02 16:23:51 -08:00
if is_incremental {
if starting_snapshot_hashes . is_none ( ) {
eprintln! ( " Unable to create incremental snapshot without a base full snapshot " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
}
2023-04-13 15:01:27 -07:00
let full_snapshot_slot = starting_snapshot_hashes . unwrap ( ) . full . 0 . 0 ;
2021-12-02 16:23:51 -08:00
if bank . slot ( ) < = full_snapshot_slot {
2022-03-07 02:23:22 -08:00
eprintln! (
" Unable to create incremental snapshot: Slot must be greater than full snapshot slot. slot: {}, full snapshot slot: {} " ,
bank . slot ( ) ,
full_snapshot_slot ,
) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
}
2021-10-21 19:02:12 -07:00
2021-12-02 16:23:51 -08:00
let incremental_snapshot_archive_info =
snapshot_utils ::bank_to_incremental_snapshot_archive (
ledger_path ,
& bank ,
full_snapshot_slot ,
Some ( snapshot_version ) ,
2022-05-10 13:37:41 -07:00
output_directory . clone ( ) ,
2021-12-02 16:23:51 -08:00
output_directory ,
2022-05-16 10:44:15 -07:00
snapshot_archive_format ,
2021-12-02 16:23:51 -08:00
maximum_full_snapshot_archives_to_retain ,
maximum_incremental_snapshot_archives_to_retain ,
)
. unwrap_or_else ( | err | {
2022-12-06 06:30:06 -08:00
eprintln! ( " Unable to create incremental snapshot: {err} " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
} ) ;
println! (
2022-03-07 02:23:22 -08:00
" Successfully created incremental snapshot for slot {}, hash {}, base slot: {}: {} " ,
bank . slot ( ) ,
bank . hash ( ) ,
full_snapshot_slot ,
incremental_snapshot_archive_info . path ( ) . display ( ) ,
) ;
2021-12-02 16:23:51 -08:00
} else {
let full_snapshot_archive_info =
snapshot_utils ::bank_to_full_snapshot_archive (
ledger_path ,
& bank ,
Some ( snapshot_version ) ,
2022-05-10 13:37:41 -07:00
output_directory . clone ( ) ,
2021-12-02 16:23:51 -08:00
output_directory ,
2022-05-16 10:44:15 -07:00
snapshot_archive_format ,
2021-12-02 16:23:51 -08:00
maximum_full_snapshot_archives_to_retain ,
maximum_incremental_snapshot_archives_to_retain ,
)
. unwrap_or_else ( | err | {
2022-12-06 06:30:06 -08:00
eprintln! ( " Unable to create snapshot: {err} " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
} ) ;
println! (
" Successfully created snapshot for slot {}, hash {}: {} " ,
bank . slot ( ) ,
bank . hash ( ) ,
full_snapshot_archive_info . path ( ) . display ( ) ,
) ;
2022-06-22 10:17:43 -07:00
if is_minimized {
let starting_epoch = bank . epoch_schedule ( ) . get_epoch ( snapshot_slot ) ;
let ending_epoch =
bank . epoch_schedule ( ) . get_epoch ( ending_slot . unwrap ( ) ) ;
if starting_epoch ! = ending_epoch {
warn! ( " Minimized snapshot range crosses epoch boundary ({} to {}). Bank hashes after {} will not match replays from a full snapshot " ,
starting_epoch , ending_epoch , bank . epoch_schedule ( ) . get_last_slot_in_epoch ( starting_epoch ) ) ;
}
}
2021-12-02 16:23:51 -08:00
}
2021-10-21 19:02:12 -07:00
println! (
2021-12-02 16:23:51 -08:00
" Shred version: {} " ,
compute_shred_version (
& genesis_config . hash ( ) ,
Some ( & bank . hard_forks ( ) . read ( ) . unwrap ( ) )
)
2021-10-21 19:02:12 -07:00
) ;
}
2021-12-02 16:23:51 -08:00
Err ( err ) = > {
2022-12-06 06:30:06 -08:00
eprintln! ( " Failed to load ledger: {err:?} " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
}
2020-01-23 15:09:36 -08:00
}
2021-12-02 16:23:51 -08:00
}
( " accounts " , Some ( arg_matches ) ) = > {
2022-04-19 15:06:30 -07:00
let halt_at_slot = value_t! ( arg_matches , " halt_at_slot " , Slot ) . ok ( ) ;
2021-12-02 16:23:51 -08:00
let process_options = ProcessOptions {
new_hard_forks : hardforks_of ( arg_matches , " hard_forks " ) ,
2022-04-19 15:06:30 -07:00
halt_at_slot ,
2023-03-22 11:03:30 -07:00
run_verification : false ,
2023-03-24 10:46:09 -07:00
accounts_db_config : Some ( get_accounts_db_config ( & ledger_path , arg_matches ) ) ,
2023-06-06 15:32:24 -07:00
boot_from_local_state : arg_matches . is_present ( " boot_from_local_state " ) ,
2021-12-02 16:23:51 -08:00
.. ProcessOptions ::default ( )
} ;
let genesis_config = open_genesis_config_by ( & ledger_path , arg_matches ) ;
let include_sysvars = arg_matches . is_present ( " include_sysvars " ) ;
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
2023-06-06 15:32:24 -07:00
get_access_type ( & process_options ) ,
2022-07-18 10:19:17 -07:00
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2023-05-31 19:12:49 -07:00
let ( bank_forks , .. ) = load_and_process_ledger (
2021-12-02 16:23:51 -08:00
arg_matches ,
& genesis_config ,
2023-02-09 12:34:20 -08:00
Arc ::new ( blockstore ) ,
2021-12-02 16:23:51 -08:00
process_options ,
snapshot_archive_path ,
2022-05-10 13:37:41 -07:00
incremental_snapshot_archive_path ,
2021-12-02 16:23:51 -08:00
)
. unwrap_or_else ( | err | {
2022-12-06 06:30:06 -08:00
eprintln! ( " Failed to load ledger: {err:?} " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
} ) ;
2020-02-14 12:24:16 -08:00
2022-03-04 01:52:22 -08:00
let bank = bank_forks . read ( ) . unwrap ( ) . working_bank ( ) ;
2022-10-18 13:03:13 -07:00
let mut serializer = serde_json ::Serializer ::new ( stdout ( ) ) ;
let ( summarize , mut json_serializer ) =
match OutputFormat ::from_matches ( arg_matches , " output_format " , false ) {
OutputFormat ::Json | OutputFormat ::JsonCompact = > {
( false , Some ( serializer . serialize_seq ( None ) . unwrap ( ) ) )
}
_ = > ( true , None ) ,
} ;
let mut total_accounts_stats = TotalAccountsStats ::default ( ) ;
let rent_collector = bank . rent_collector ( ) ;
let print_account_contents = ! arg_matches . is_present ( " no_account_contents " ) ;
let print_account_data = ! arg_matches . is_present ( " no_account_data " ) ;
2022-11-21 10:15:14 -08:00
let data_encoding = parse_encoding_format ( arg_matches ) ;
2022-10-18 13:03:13 -07:00
let cli_account_new_config = CliAccountNewConfig {
data_encoding ,
.. CliAccountNewConfig ::default ( )
} ;
let scan_func = | some_account_tuple : Option < ( & Pubkey , AccountSharedData , Slot ) > | {
if let Some ( ( pubkey , account , slot ) ) = some_account_tuple
. filter ( | ( _ , account , _ ) | Accounts ::is_loadable ( account . lamports ( ) ) )
{
if ! include_sysvars & & solana_sdk ::sysvar ::is_sysvar_id ( pubkey ) {
return ;
}
2021-11-08 12:45:30 -08:00
2022-10-18 13:03:13 -07:00
total_accounts_stats . accumulate_account ( pubkey , & account , rent_collector ) ;
2020-05-08 10:42:32 -07:00
2022-10-18 13:03:13 -07:00
if print_account_contents {
if let Some ( json_serializer ) = json_serializer . as_mut ( ) {
let cli_account = CliAccount ::new_with_config (
pubkey ,
& account ,
& cli_account_new_config ,
) ;
json_serializer . serialize_element ( & cli_account ) . unwrap ( ) ;
} else {
output_account (
pubkey ,
& account ,
Some ( slot ) ,
print_account_data ,
data_encoding ,
) ;
}
}
2020-05-08 10:42:32 -07:00
}
2022-10-18 13:03:13 -07:00
} ;
let mut measure = Measure ::start ( " scanning accounts " ) ;
bank . scan_all_accounts_with_modified_slots ( scan_func )
. unwrap ( ) ;
measure . stop ( ) ;
info! ( " {} " , measure ) ;
if let Some ( json_serializer ) = json_serializer {
json_serializer . end ( ) . unwrap ( ) ;
}
if summarize {
2022-12-06 06:30:06 -08:00
println! ( " \n {total_accounts_stats:#?} " ) ;
2021-12-02 16:23:51 -08:00
}
}
( " capitalization " , Some ( arg_matches ) ) = > {
2022-04-19 15:06:30 -07:00
let halt_at_slot = value_t! ( arg_matches , " halt_at_slot " , Slot ) . ok ( ) ;
2021-12-02 16:23:51 -08:00
let process_options = ProcessOptions {
new_hard_forks : hardforks_of ( arg_matches , " hard_forks " ) ,
2022-04-19 15:06:30 -07:00
halt_at_slot ,
2023-03-22 11:03:30 -07:00
run_verification : false ,
2023-03-24 10:46:09 -07:00
accounts_db_config : Some ( get_accounts_db_config ( & ledger_path , arg_matches ) ) ,
2023-06-06 15:32:24 -07:00
boot_from_local_state : arg_matches . is_present ( " boot_from_local_state " ) ,
2021-12-02 16:23:51 -08:00
.. ProcessOptions ::default ( )
} ;
let genesis_config = open_genesis_config_by ( & ledger_path , arg_matches ) ;
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
2023-06-06 15:32:24 -07:00
get_access_type ( & process_options ) ,
2022-07-18 10:19:17 -07:00
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2023-05-31 19:12:49 -07:00
match load_and_process_ledger (
2021-12-02 16:23:51 -08:00
arg_matches ,
& genesis_config ,
2023-02-09 12:34:20 -08:00
Arc ::new ( blockstore ) ,
2021-12-02 16:23:51 -08:00
process_options ,
snapshot_archive_path ,
2022-05-10 13:37:41 -07:00
incremental_snapshot_archive_path ,
2021-12-02 16:23:51 -08:00
) {
Ok ( ( bank_forks , .. ) ) = > {
2022-03-04 01:52:22 -08:00
let bank_forks = bank_forks . read ( ) . unwrap ( ) ;
2021-12-02 16:23:51 -08:00
let slot = bank_forks . working_bank ( ) . slot ( ) ;
let bank = bank_forks . get ( slot ) . unwrap_or_else ( | | {
2022-12-06 06:30:06 -08:00
eprintln! ( " Error: Slot {slot} is not available " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
} ) ;
if arg_matches . is_present ( " recalculate_capitalization " ) {
println! ( " Recalculating capitalization " ) ;
let old_capitalization = bank . set_capitalization ( ) ;
if old_capitalization = = bank . capitalization ( ) {
eprintln! (
" Capitalization was identical: {} " ,
Sol ( old_capitalization )
) ;
}
2020-07-20 03:09:38 -07:00
}
2020-05-08 10:42:32 -07:00
2021-12-02 16:23:51 -08:00
if arg_matches . is_present ( " warp_epoch " ) {
let base_bank = bank ;
let raw_warp_epoch =
value_t! ( arg_matches , " warp_epoch " , String ) . unwrap ( ) ;
let warp_epoch = if raw_warp_epoch . starts_with ( '+' ) {
base_bank . epoch ( )
+ value_t! ( arg_matches , " warp_epoch " , Epoch ) . unwrap ( )
} else {
value_t! ( arg_matches , " warp_epoch " , Epoch ) . unwrap ( )
2020-12-07 08:21:16 -08:00
} ;
2021-12-02 16:23:51 -08:00
if warp_epoch < base_bank . epoch ( ) {
eprintln! (
" Error: can't warp epoch backwards: {} => {} " ,
base_bank . epoch ( ) ,
warp_epoch
) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
}
2020-07-20 03:09:38 -07:00
2021-12-02 16:23:51 -08:00
if let Ok ( raw_inflation ) = value_t! ( arg_matches , " inflation " , String ) {
let inflation = match raw_inflation . as_str ( ) {
" pico " = > Inflation ::pico ( ) ,
" full " = > Inflation ::full ( ) ,
" none " = > Inflation ::new_disabled ( ) ,
_ = > unreachable! ( ) ,
} ;
println! (
" Forcing to: {:?} (was: {:?}) " ,
inflation ,
base_bank . inflation ( )
) ;
base_bank . set_inflation ( inflation ) ;
}
2020-11-26 20:20:47 -08:00
2021-12-02 16:23:51 -08:00
let next_epoch = base_bank
. epoch_schedule ( )
. get_first_slot_in_epoch ( warp_epoch ) ;
// disable eager rent collection because this creates many unrelated
// rent collection account updates
base_bank
. lazy_rent_collection
. store ( true , std ::sync ::atomic ::Ordering ::Relaxed ) ;
2022-04-26 14:49:35 -07:00
let feature_account_balance = std ::cmp ::max (
genesis_config . rent . minimum_balance ( Feature ::size_of ( ) ) ,
1 ,
) ;
if arg_matches . is_present ( " enable_credits_auto_rewind " ) {
base_bank . unfreeze_for_ledger_tool ( ) ;
let mut force_enabled_count = 0 ;
if base_bank
. get_account ( & feature_set ::credits_auto_rewind ::id ( ) )
. is_none ( )
{
base_bank . store_account (
& feature_set ::credits_auto_rewind ::id ( ) ,
& feature ::create_account (
& Feature { activated_at : None } ,
feature_account_balance ,
) ,
) ;
force_enabled_count + = 1 ;
}
if force_enabled_count = = 0 {
warn! (
" Already credits_auto_rewind is activated (or scheduled) "
) ;
}
let mut store_failed_count = 0 ;
if force_enabled_count > = 1 {
if base_bank
. get_account ( & feature_set ::deprecate_rewards_sysvar ::id ( ) )
. is_some ( )
{
// steal some lamports from the pretty old feature not to affect
// capitalizaion, which doesn't affect inflation behavior!
base_bank . store_account (
& feature_set ::deprecate_rewards_sysvar ::id ( ) ,
& AccountSharedData ::default ( ) ,
) ;
force_enabled_count - = 1 ;
} else {
store_failed_count + = 1 ;
}
}
assert_eq! ( force_enabled_count , store_failed_count ) ;
if store_failed_count > = 1 {
// we have no choice; maybe locally created blank cluster with
// not-Development cluster type.
let old_cap = base_bank . set_capitalization ( ) ;
let new_cap = base_bank . capitalization ( ) ;
warn! (
" Skewing capitalization a bit to enable credits_auto_rewind as \
requested : increasing { } from { } to { } " ,
feature_account_balance , old_cap , new_cap ,
) ;
assert_eq! (
old_cap + feature_account_balance * store_failed_count ,
new_cap
) ;
}
}
2021-12-02 16:23:51 -08:00
#[ derive(Default, Debug) ]
struct PointDetail {
epoch : Epoch ,
points : u128 ,
stake : u128 ,
credits : u128 ,
}
#[ derive(Default, Debug) ]
struct CalculationDetail {
epochs : usize ,
voter : Pubkey ,
voter_owner : Pubkey ,
current_effective_stake : u64 ,
total_stake : u64 ,
rent_exempt_reserve : u64 ,
points : Vec < PointDetail > ,
base_rewards : u64 ,
commission : u8 ,
vote_rewards : u64 ,
stake_rewards : u64 ,
activation_epoch : Epoch ,
deactivation_epoch : Option < Epoch > ,
point_value : Option < PointValue > ,
old_credits_observed : Option < u64 > ,
new_credits_observed : Option < u64 > ,
skipped_reasons : String ,
}
use solana_stake_program ::stake_state ::InflationPointCalculationEvent ;
let stake_calculation_details : DashMap < Pubkey , CalculationDetail > =
DashMap ::new ( ) ;
let last_point_value = Arc ::new ( RwLock ::new ( None ) ) ;
let tracer = | event : & RewardCalculationEvent | {
// Currently RewardCalculationEvent enum has only Staking variant
// because only staking tracing is supported!
#[ allow(irrefutable_let_patterns) ]
if let RewardCalculationEvent ::Staking ( pubkey , event ) = event {
let mut detail =
stake_calculation_details . entry ( * * pubkey ) . or_default ( ) ;
match event {
2020-11-07 23:43:50 -08:00
InflationPointCalculationEvent ::CalculatedPoints (
2020-11-26 20:20:47 -08:00
epoch ,
2020-11-07 23:43:50 -08:00
stake ,
credits ,
2020-11-26 20:20:47 -08:00
points ,
2020-11-07 23:43:50 -08:00
) = > {
2020-11-26 20:20:47 -08:00
if * points > 0 {
2020-11-07 23:43:50 -08:00
detail . epochs + = 1 ;
2020-11-26 20:20:47 -08:00
detail . points . push ( PointDetail { epoch : * epoch , points : * points , stake : * stake , credits : * credits } ) ;
2020-11-07 23:43:50 -08:00
}
}
InflationPointCalculationEvent ::SplitRewards (
all ,
voter ,
staker ,
point_value ,
) = > {
detail . base_rewards = * all ;
detail . vote_rewards = * voter ;
detail . stake_rewards = * staker ;
detail . point_value = Some ( point_value . clone ( ) ) ;
// we have duplicate copies of `PointValue`s for possible
// miscalculation; do some minimum sanity check
2021-10-05 18:30:08 -07:00
let mut last_point_value = last_point_value . write ( ) . unwrap ( ) ;
if let Some ( last_point_value ) = last_point_value . as_ref ( ) {
assert_eq! ( last_point_value , point_value ) ;
} else {
* last_point_value = Some ( point_value . clone ( ) ) ;
2020-11-07 23:43:50 -08:00
}
}
2020-11-26 20:20:47 -08:00
InflationPointCalculationEvent ::EffectiveStakeAtRewardedEpoch ( stake ) = > {
detail . current_effective_stake = * stake ;
}
2020-11-07 23:43:50 -08:00
InflationPointCalculationEvent ::Commission ( commission ) = > {
detail . commission = * commission ;
}
InflationPointCalculationEvent ::RentExemptReserve ( reserve ) = > {
detail . rent_exempt_reserve = * reserve ;
}
2020-11-21 08:13:07 -08:00
InflationPointCalculationEvent ::CreditsObserved (
2020-11-26 20:20:47 -08:00
old_credits_observed ,
new_credits_observed ,
2020-11-21 08:13:07 -08:00
) = > {
2020-11-26 20:20:47 -08:00
detail . old_credits_observed = Some ( * old_credits_observed ) ;
2021-02-11 22:24:23 -08:00
detail . new_credits_observed = * new_credits_observed ;
2020-11-21 08:13:07 -08:00
}
2020-11-15 11:38:46 -08:00
InflationPointCalculationEvent ::Delegation (
delegation ,
owner ,
) = > {
2020-11-07 23:43:50 -08:00
detail . voter = delegation . voter_pubkey ;
2020-11-15 11:38:46 -08:00
detail . voter_owner = * owner ;
2020-11-07 23:43:50 -08:00
detail . total_stake = delegation . stake ;
detail . activation_epoch = delegation . activation_epoch ;
if delegation . deactivation_epoch < Epoch ::max_value ( ) {
detail . deactivation_epoch =
Some ( delegation . deactivation_epoch ) ;
}
}
2021-02-11 22:24:23 -08:00
InflationPointCalculationEvent ::Skipped ( skipped_reason ) = > {
if detail . skipped_reasons . is_empty ( ) {
2022-12-06 06:30:06 -08:00
detail . skipped_reasons = format! ( " {skipped_reason:?} " ) ;
2021-02-11 22:24:23 -08:00
} else {
2022-05-22 19:10:48 -07:00
use std ::fmt ::Write ;
2022-12-06 06:30:06 -08:00
let _ = write! ( & mut detail . skipped_reasons , " /{skipped_reason:?} " ) ;
2021-02-11 22:24:23 -08:00
}
}
2020-11-07 23:43:50 -08:00
}
2021-12-02 16:23:51 -08:00
}
} ;
let warped_bank = Bank ::new_from_parent_with_tracer (
2022-04-28 11:51:00 -07:00
& base_bank ,
2021-12-02 16:23:51 -08:00
base_bank . collector_id ( ) ,
next_epoch ,
tracer ,
) ;
warped_bank . freeze ( ) ;
let mut csv_writer = if arg_matches . is_present ( " csv_filename " ) {
let csv_filename =
value_t_or_exit! ( arg_matches , " csv_filename " , String ) ;
2022-11-09 11:39:38 -08:00
let file = File ::create ( csv_filename ) . unwrap ( ) ;
2021-12-02 16:23:51 -08:00
Some ( csv ::WriterBuilder ::new ( ) . from_writer ( file ) )
} else {
None
} ;
2020-07-20 03:09:38 -07:00
2021-12-02 16:23:51 -08:00
println! ( " Slot: {} => {} " , base_bank . slot ( ) , warped_bank . slot ( ) ) ;
println! ( " Epoch: {} => {} " , base_bank . epoch ( ) , warped_bank . epoch ( ) ) ;
2022-04-28 11:51:00 -07:00
assert_capitalization ( & base_bank ) ;
2021-12-02 16:23:51 -08:00
assert_capitalization ( & warped_bank ) ;
let interest_per_epoch = ( ( warped_bank . capitalization ( ) as f64 )
/ ( base_bank . capitalization ( ) as f64 )
* 100_ f64 )
- 100_ f64 ;
let interest_per_year = interest_per_epoch
/ warped_bank . epoch_duration_in_years ( base_bank . epoch ( ) ) ;
println! (
" Capitalization: {} => {} (+{} {}%; annualized {}%) " ,
Sol ( base_bank . capitalization ( ) ) ,
Sol ( warped_bank . capitalization ( ) ) ,
Sol ( warped_bank . capitalization ( ) - base_bank . capitalization ( ) ) ,
interest_per_epoch ,
interest_per_year ,
) ;
2020-07-20 03:09:38 -07:00
2021-12-02 16:23:51 -08:00
let mut overall_delta = 0 ;
let modified_accounts =
warped_bank . get_all_accounts_modified_since_parent ( ) ;
let mut rewarded_accounts = modified_accounts
. iter ( )
. map ( | ( pubkey , account ) | {
(
pubkey ,
account ,
base_bank
. get_account ( pubkey )
. map ( | a | a . lamports ( ) )
. unwrap_or_default ( ) ,
)
} )
. collect ::< Vec < _ > > ( ) ;
rewarded_accounts . sort_unstable_by_key (
| ( pubkey , account , base_lamports ) | {
(
* account . owner ( ) ,
* base_lamports ,
account . lamports ( ) - base_lamports ,
* pubkey ,
)
} ,
) ;
2020-11-07 23:43:50 -08:00
2021-12-02 16:23:51 -08:00
let mut unchanged_accounts = stake_calculation_details
. iter ( )
. map ( | entry | * entry . key ( ) )
. collect ::< HashSet < _ > > ( )
. difference (
& rewarded_accounts
. iter ( )
. map ( | ( pubkey , .. ) | * * pubkey )
. collect ( ) ,
)
. map ( | pubkey | ( * pubkey , warped_bank . get_account ( pubkey ) . unwrap ( ) ) )
. collect ::< Vec < _ > > ( ) ;
unchanged_accounts . sort_unstable_by_key ( | ( pubkey , account ) | {
( * account . owner ( ) , account . lamports ( ) , * pubkey )
} ) ;
let unchanged_accounts = unchanged_accounts . into_iter ( ) ;
let rewarded_accounts = rewarded_accounts
. into_iter ( )
. map ( | ( pubkey , account , .. ) | ( * pubkey , account . clone ( ) ) ) ;
let all_accounts = unchanged_accounts . chain ( rewarded_accounts ) ;
for ( pubkey , warped_account ) in all_accounts {
// Don't output sysvars; it's always updated but not related to
// inflation.
if solana_sdk ::sysvar ::is_sysvar_id ( & pubkey ) {
continue ;
}
2020-11-07 23:43:50 -08:00
2021-12-02 16:23:51 -08:00
if let Some ( base_account ) = base_bank . get_account ( & pubkey ) {
let delta = warped_account . lamports ( ) - base_account . lamports ( ) ;
let detail_ref = stake_calculation_details . get ( & pubkey ) ;
let detail : Option < & CalculationDetail > =
detail_ref . as_ref ( ) . map ( | detail_ref | detail_ref . value ( ) ) ;
println! (
" {:<45}({}): {} => {} (+{} {:>4.9}%) {:?} " ,
2022-12-06 06:30:06 -08:00
format! ( " {pubkey} " ) , // format! is needed to pad/justify correctly.
2021-12-02 16:23:51 -08:00
base_account . owner ( ) ,
Sol ( base_account . lamports ( ) ) ,
Sol ( warped_account . lamports ( ) ) ,
Sol ( delta ) ,
( ( warped_account . lamports ( ) as f64 )
/ ( base_account . lamports ( ) as f64 )
* 100_ f64 )
- 100_ f64 ,
detail ,
) ;
if let Some ( ref mut csv_writer ) = csv_writer {
#[ derive(Serialize) ]
struct InflationRecord {
cluster_type : String ,
rewarded_epoch : Epoch ,
account : String ,
owner : String ,
old_balance : u64 ,
new_balance : u64 ,
data_size : usize ,
delegation : String ,
delegation_owner : String ,
effective_stake : String ,
delegated_stake : String ,
rent_exempt_reserve : String ,
activation_epoch : String ,
deactivation_epoch : String ,
earned_epochs : String ,
epoch : String ,
epoch_credits : String ,
epoch_points : String ,
epoch_stake : String ,
old_credits_observed : String ,
new_credits_observed : String ,
base_rewards : String ,
stake_rewards : String ,
vote_rewards : String ,
commission : String ,
cluster_rewards : String ,
cluster_points : String ,
old_capitalization : u64 ,
new_capitalization : u64 ,
}
fn format_or_na < T : std ::fmt ::Display > (
data : Option < T > ,
) -> String {
2022-12-06 06:30:06 -08:00
data . map ( | data | format! ( " {data} " ) )
2021-12-02 16:23:51 -08:00
. unwrap_or_else ( | | " N/A " . to_owned ( ) )
}
let mut point_details = detail
. map ( | d | d . points . iter ( ) . map ( Some ) . collect ::< Vec < _ > > ( ) )
. unwrap_or_default ( ) ;
2020-11-26 20:20:47 -08:00
2021-12-02 16:23:51 -08:00
// ensure to print even if there is no calculation/point detail
if point_details . is_empty ( ) {
point_details . push ( None ) ;
}
2020-11-26 20:20:47 -08:00
2021-12-02 16:23:51 -08:00
for point_detail in point_details {
2023-01-06 11:30:07 -08:00
let ( cluster_rewards , cluster_points ) =
last_point_value
. read ( )
. unwrap ( )
. clone ( )
. map_or ( ( None , None ) , | pv | {
( Some ( pv . rewards ) , Some ( pv . points ) )
} ) ;
2021-12-02 16:23:51 -08:00
let record = InflationRecord {
cluster_type : format ! (
" {:?} " ,
base_bank . cluster_type ( )
) ,
rewarded_epoch : base_bank . epoch ( ) ,
2022-12-06 06:30:06 -08:00
account : format ! ( " {pubkey} " ) ,
2021-12-02 16:23:51 -08:00
owner : format ! ( " {} " , base_account . owner ( ) ) ,
old_balance : base_account . lamports ( ) ,
new_balance : warped_account . lamports ( ) ,
data_size : base_account . data ( ) . len ( ) ,
delegation : format_or_na ( detail . map ( | d | d . voter ) ) ,
delegation_owner : format_or_na (
detail . map ( | d | d . voter_owner ) ,
) ,
effective_stake : format_or_na (
detail . map ( | d | d . current_effective_stake ) ,
) ,
delegated_stake : format_or_na (
detail . map ( | d | d . total_stake ) ,
) ,
rent_exempt_reserve : format_or_na (
detail . map ( | d | d . rent_exempt_reserve ) ,
) ,
activation_epoch : format_or_na ( detail . map ( | d | {
if d . activation_epoch < Epoch ::max_value ( ) {
d . activation_epoch
} else {
// bootstraped
0
}
} ) ) ,
deactivation_epoch : format_or_na (
detail . and_then ( | d | d . deactivation_epoch ) ,
) ,
earned_epochs : format_or_na (
detail . map ( | d | d . epochs ) ,
) ,
epoch : format_or_na ( point_detail . map ( | d | d . epoch ) ) ,
epoch_credits : format_or_na (
point_detail . map ( | d | d . credits ) ,
) ,
epoch_points : format_or_na (
point_detail . map ( | d | d . points ) ,
) ,
epoch_stake : format_or_na (
point_detail . map ( | d | d . stake ) ,
) ,
old_credits_observed : format_or_na (
detail . and_then ( | d | d . old_credits_observed ) ,
) ,
new_credits_observed : format_or_na (
detail . and_then ( | d | d . new_credits_observed ) ,
) ,
base_rewards : format_or_na (
detail . map ( | d | d . base_rewards ) ,
) ,
stake_rewards : format_or_na (
detail . map ( | d | d . stake_rewards ) ,
) ,
vote_rewards : format_or_na (
detail . map ( | d | d . vote_rewards ) ,
) ,
commission : format_or_na (
detail . map ( | d | d . commission ) ,
) ,
2023-01-06 11:30:07 -08:00
cluster_rewards : format_or_na ( cluster_rewards ) ,
cluster_points : format_or_na ( cluster_points ) ,
2021-12-02 16:23:51 -08:00
old_capitalization : base_bank . capitalization ( ) ,
new_capitalization : warped_bank . capitalization ( ) ,
} ;
csv_writer . serialize ( & record ) . unwrap ( ) ;
}
2020-11-26 20:20:47 -08:00
}
2021-12-02 16:23:51 -08:00
overall_delta + = delta ;
} else {
error! ( " new account!?: {} " , pubkey ) ;
2020-07-20 03:09:38 -07:00
}
}
2021-12-02 16:23:51 -08:00
if overall_delta > 0 {
println! ( " Sum of lamports changes: {} " , Sol ( overall_delta ) ) ;
}
} else {
if arg_matches . is_present ( " recalculate_capitalization " ) {
eprintln! (
" Capitalization isn't verified because it's recalculated "
) ;
}
if arg_matches . is_present ( " inflation " ) {
eprintln! (
" Forcing inflation isn't meaningful because bank isn't warping "
) ;
}
2020-08-18 18:09:04 -07:00
2022-04-28 11:51:00 -07:00
assert_capitalization ( & bank ) ;
2021-12-02 16:23:51 -08:00
println! ( " Inflation: {:?} " , bank . inflation ( ) ) ;
println! ( " RentCollector: {:?} " , bank . rent_collector ( ) ) ;
println! ( " Capitalization: {} " , Sol ( bank . capitalization ( ) ) ) ;
2020-08-18 18:09:04 -07:00
}
}
Err ( err ) = > {
2022-12-06 06:30:06 -08:00
eprintln! ( " Failed to load ledger: {err:?} " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2020-08-18 18:09:04 -07:00
}
2021-12-02 16:23:51 -08:00
}
2020-08-18 18:09:04 -07:00
}
2021-12-02 16:23:51 -08:00
( " purge " , Some ( arg_matches ) ) = > {
let start_slot = value_t_or_exit! ( arg_matches , " start_slot " , Slot ) ;
let end_slot = value_t! ( arg_matches , " end_slot " , Slot ) . ok ( ) ;
2023-03-29 08:34:00 -07:00
let perform_compaction = arg_matches . is_present ( " enable_compaction " ) ;
2023-04-14 22:18:03 -07:00
if arg_matches . is_present ( " no_compaction " ) {
warn! ( " --no-compaction is deprecated and is now the default behavior. " ) ;
}
2021-12-02 16:23:51 -08:00
let dead_slots_only = arg_matches . is_present ( " dead_slots_only " ) ;
let batch_size = value_t_or_exit! ( arg_matches , " batch_size " , usize ) ;
2023-03-29 08:34:00 -07:00
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
2023-03-29 08:34:00 -07:00
AccessType ::PrimaryForMaintenance ,
2022-07-18 10:19:17 -07:00
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2021-12-02 16:23:51 -08:00
let end_slot = match end_slot {
Some ( end_slot ) = > end_slot ,
None = > match blockstore . slot_meta_iterator ( start_slot ) {
Ok ( metas ) = > {
let slots : Vec < _ > = metas . map ( | ( slot , _ ) | slot ) . collect ( ) ;
if slots . is_empty ( ) {
eprintln! ( " Purge range is empty " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
}
* slots . last ( ) . unwrap ( )
}
Err ( err ) = > {
2022-12-06 06:30:06 -08:00
eprintln! ( " Unable to read the Ledger: {err:?} " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
}
} ,
} ;
if end_slot < start_slot {
2022-12-06 06:30:06 -08:00
eprintln! ( " end slot {end_slot} is less than start slot {start_slot} " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
}
info! (
2023-03-29 08:34:00 -07:00
" Purging data from slots {} to {} ({} slots) (do compaction: {}) (dead slot only: {}) " ,
2020-10-21 10:45:21 -07:00
start_slot ,
end_slot ,
end_slot - start_slot ,
2023-03-29 08:34:00 -07:00
perform_compaction ,
2020-10-21 10:45:21 -07:00
dead_slots_only ,
) ;
2021-12-02 16:23:51 -08:00
let purge_from_blockstore = | start_slot , end_slot | {
blockstore . purge_from_next_slots ( start_slot , end_slot ) ;
2023-03-29 08:34:00 -07:00
if perform_compaction {
2021-12-02 16:23:51 -08:00
blockstore . purge_and_compact_slots ( start_slot , end_slot ) ;
2023-03-29 08:34:00 -07:00
} else {
blockstore . purge_slots ( start_slot , end_slot , PurgeType ::Exact ) ;
2021-12-02 16:23:51 -08:00
}
} ;
if ! dead_slots_only {
let slots_iter = & ( start_slot ..= end_slot ) . chunks ( batch_size ) ;
for slots in slots_iter {
let slots = slots . collect ::< Vec < _ > > ( ) ;
assert! ( ! slots . is_empty ( ) ) ;
let start_slot = * slots . first ( ) . unwrap ( ) ;
let end_slot = * slots . last ( ) . unwrap ( ) ;
info! (
" Purging chunked slots from {} to {} ({} slots) " ,
start_slot ,
end_slot ,
end_slot - start_slot
) ;
purge_from_blockstore ( start_slot , end_slot ) ;
}
2020-10-21 10:45:21 -07:00
} else {
2021-12-02 16:23:51 -08:00
let dead_slots_iter = blockstore
. dead_slots_iterator ( start_slot )
. unwrap ( )
. take_while ( | s | * s < = end_slot ) ;
for dead_slot in dead_slots_iter {
info! ( " Purging dead slot {} " , dead_slot ) ;
purge_from_blockstore ( dead_slot , dead_slot ) ;
}
2020-10-21 10:45:21 -07:00
}
2020-07-14 09:41:41 -07:00
}
2021-12-02 16:23:51 -08:00
( " list-roots " , Some ( arg_matches ) ) = > {
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
AccessType ::Secondary ,
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2021-12-02 16:23:51 -08:00
let max_height = if let Some ( height ) = arg_matches . value_of ( " max_height " ) {
usize ::from_str ( height ) . expect ( " Maximum height must be a number " )
} else {
usize ::MAX
} ;
let start_root = if let Some ( height ) = arg_matches . value_of ( " start_root " ) {
Slot ::from_str ( height ) . expect ( " Starting root must be a number " )
} else {
0
} ;
let num_roots = if let Some ( roots ) = arg_matches . value_of ( " num_roots " ) {
usize ::from_str ( roots ) . expect ( " Number of roots must be a number " )
} else {
usize ::from_str ( DEFAULT_ROOT_COUNT ) . unwrap ( )
} ;
2019-07-12 16:58:13 -07:00
2021-12-02 16:23:51 -08:00
let iter = blockstore
. rooted_slot_iterator ( start_root )
. expect ( " Failed to get rooted slot " ) ;
2021-02-06 17:26:42 -08:00
2022-12-15 20:33:57 -08:00
let mut output : Box < dyn Write > =
2021-12-02 16:23:51 -08:00
if let Some ( path ) = arg_matches . value_of ( " slot_list " ) {
match File ::create ( path ) {
Ok ( file ) = > Box ::new ( file ) ,
_ = > Box ::new ( stdout ( ) ) ,
}
} else {
Box ::new ( stdout ( ) )
} ;
2022-12-15 20:33:57 -08:00
iter . take ( num_roots )
. take_while ( | slot | * slot < = max_height as u64 )
. collect ::< Vec < _ > > ( )
2021-12-02 16:23:51 -08:00
. into_iter ( )
. rev ( )
2022-12-15 20:33:57 -08:00
. for_each ( | slot | {
let blockhash = blockstore
. get_slot_entries ( slot , 0 )
. unwrap ( )
. last ( )
. unwrap ( )
. hash ;
2022-12-19 14:26:38 -08:00
writeln! ( output , " {slot}: {blockhash:?} " ) . expect ( " failed to write " ) ;
2021-12-02 16:23:51 -08:00
} ) ;
}
2022-05-24 12:03:28 -07:00
( " latest-optimistic-slots " , Some ( arg_matches ) ) = > {
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
AccessType ::Secondary ,
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2022-05-24 12:03:28 -07:00
let num_slots = value_t_or_exit! ( arg_matches , " num_slots " , usize ) ;
2023-03-23 14:13:41 -07:00
let exclude_vote_only_slots = arg_matches . is_present ( " exclude_vote_only_slots " ) ;
let slots_iter = blockstore
. reversed_optimistic_slots_iterator ( )
. expect ( " Failed to get reversed optimistic slots iterator " )
. map ( | ( slot , hash , timestamp ) | {
let ( entries , _ , _ ) = blockstore
. get_slot_entries_with_shred_info ( slot , 0 , false )
. expect ( " Failed to get slot entries " ) ;
let contains_nonvote = entries
. iter ( )
. flat_map ( | entry | entry . transactions . iter ( ) )
. flat_map ( get_program_ids )
. any ( | program_id | * program_id ! = solana_vote_program ::id ( ) ) ;
( slot , hash , timestamp , contains_nonvote )
} ) ;
let slots : Vec < _ > = if exclude_vote_only_slots {
slots_iter
. filter ( | ( _ , _ , _ , contains_nonvote ) | * contains_nonvote )
. take ( num_slots )
. collect ( )
} else {
slots_iter . take ( num_slots ) . collect ( )
} ;
println! (
" {:>20} {:>44} {:>32} {:>13} " ,
" Slot " , " Hash " , " Timestamp " , " Vote Only? "
) ;
for ( slot , hash , timestamp , contains_nonvote ) in slots . iter ( ) {
2022-05-24 12:03:28 -07:00
let time_str = {
let secs : u64 = ( timestamp / 1_000 ) as u64 ;
let nanos : u32 = ( ( timestamp % 1_000 ) * 1_000_000 ) as u32 ;
let t = UNIX_EPOCH + Duration ::new ( secs , nanos ) ;
let datetime : DateTime < Utc > = t . into ( ) ;
datetime . to_rfc3339 ( )
} ;
2022-12-06 06:30:06 -08:00
let hash_str = format! ( " {hash} " ) ;
2023-03-23 14:13:41 -07:00
println! (
" {:>20} {:>44} {:>32} {:>13} " ,
slot , & hash_str , & time_str , ! contains_nonvote
) ;
2022-05-24 12:03:28 -07:00
}
}
2021-12-02 16:23:51 -08:00
( " repair-roots " , Some ( arg_matches ) ) = > {
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
AccessType ::Primary ,
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2021-12-02 16:23:51 -08:00
let start_root = if let Some ( root ) = arg_matches . value_of ( " start_root " ) {
Slot ::from_str ( root ) . expect ( " Before root must be a number " )
2019-08-15 13:00:09 -07:00
} else {
2021-12-02 16:23:51 -08:00
blockstore . max_root ( )
2019-08-15 13:00:09 -07:00
} ;
2021-12-02 16:23:51 -08:00
let max_slots = value_t_or_exit! ( arg_matches , " max_slots " , u64 ) ;
let end_root = if let Some ( root ) = arg_matches . value_of ( " end_root " ) {
Slot ::from_str ( root ) . expect ( " Until root must be a number " )
} else {
start_root . saturating_sub ( max_slots )
} ;
assert! ( start_root > end_root ) ;
assert! ( blockstore . is_root ( start_root ) ) ;
let num_slots = start_root - end_root - 1 ; // Adjust by one since start_root need not be checked
if arg_matches . is_present ( " end_root " ) & & num_slots > max_slots {
eprintln! (
2022-12-06 06:30:06 -08:00
" Requested range {num_slots} too large, max {max_slots}. \
2021-05-06 13:12:01 -07:00
Either adjust ` - - until ` value , or pass a larger ` - - repair - limit ` \
to override the limit " ,
2021-12-02 16:23:51 -08:00
) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
}
let ancestor_iterator = AncestorIterator ::new ( start_root , & blockstore )
. take_while ( | & slot | slot > = end_root ) ;
let roots_to_fix : Vec < _ > = ancestor_iterator
. filter ( | slot | ! blockstore . is_root ( * slot ) )
. collect ( ) ;
if ! roots_to_fix . is_empty ( ) {
eprintln! ( " {} slots to be rooted " , roots_to_fix . len ( ) ) ;
for chunk in roots_to_fix . chunks ( 100 ) {
2022-12-06 06:30:06 -08:00
eprintln! ( " {chunk:?} " ) ;
2021-12-02 16:23:51 -08:00
blockstore
. set_roots ( roots_to_fix . iter ( ) )
. unwrap_or_else ( | err | {
2022-12-06 06:30:06 -08:00
eprintln! ( " Unable to set roots {roots_to_fix:?} : {err} " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
} ) ;
}
} else {
2022-12-06 06:30:06 -08:00
println! ( " No missing roots found in range {end_root} to {start_root} " ) ;
2021-05-06 13:12:01 -07:00
}
}
2021-12-02 16:23:51 -08:00
( " bounds " , Some ( arg_matches ) ) = > {
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
AccessType ::Secondary ,
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2023-01-12 21:21:04 -08:00
2021-12-02 16:23:51 -08:00
match blockstore . slot_meta_iterator ( 0 ) {
Ok ( metas ) = > {
2023-01-12 21:21:04 -08:00
let output_format =
OutputFormat ::from_matches ( arg_matches , " output_format " , false ) ;
2021-12-02 16:23:51 -08:00
let all = arg_matches . is_present ( " all " ) ;
2019-12-20 19:43:53 -08:00
2021-12-02 16:23:51 -08:00
let slots : Vec < _ > = metas . map ( | ( slot , _ ) | slot ) . collect ( ) ;
2023-01-12 21:21:04 -08:00
let slot_bounds = if slots . is_empty ( ) {
SlotBounds ::default ( )
2019-12-20 19:43:53 -08:00
} else {
2023-01-12 21:21:04 -08:00
// Collect info about slot bounds
let mut bounds = SlotBounds {
slots : SlotInfo {
total : slots . len ( ) ,
first : Some ( * slots . first ( ) . unwrap ( ) ) ,
last : Some ( * slots . last ( ) . unwrap ( ) ) ,
.. SlotInfo ::default ( )
} ,
.. SlotBounds ::default ( )
} ;
if all {
bounds . all_slots = Some ( & slots ) ;
2021-12-02 16:23:51 -08:00
}
2023-01-12 21:21:04 -08:00
// Consider also rooted slots, if present
if let Ok ( rooted ) = blockstore . rooted_slot_iterator ( 0 ) {
let mut first_rooted = None ;
let mut last_rooted = None ;
let mut total_rooted = 0 ;
for ( i , slot ) in rooted . into_iter ( ) . enumerate ( ) {
if i = = 0 {
first_rooted = Some ( slot ) ;
}
last_rooted = Some ( slot ) ;
total_rooted + = 1 ;
2021-12-02 16:23:51 -08:00
}
2023-01-12 21:21:04 -08:00
let last_root_for_comparison = last_rooted . unwrap_or_default ( ) ;
let count_past_root = slots
. iter ( )
. rev ( )
. take_while ( | slot | * slot > & last_root_for_comparison )
. count ( ) ;
bounds . roots = SlotInfo {
total : total_rooted ,
first : first_rooted ,
last : last_rooted ,
num_after_last_root : Some ( count_past_root ) ,
} ;
2021-02-06 17:26:42 -08:00
}
2023-01-12 21:21:04 -08:00
bounds
} ;
// Print collected data
println! ( " {} " , output_format . formatted_string ( & slot_bounds ) ) ;
2021-02-06 17:26:42 -08:00
}
2021-12-02 16:23:51 -08:00
Err ( err ) = > {
2022-12-06 06:30:06 -08:00
eprintln! ( " Unable to read the Ledger: {err:?} " ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
}
} ;
}
( " analyze-storage " , _ ) = > {
2022-01-06 21:40:02 -08:00
analyze_storage (
2022-07-18 10:19:17 -07:00
& open_blockstore (
& ledger_path ,
AccessType ::Secondary ,
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
)
. db ( ) ,
2022-01-06 21:40:02 -08:00
) ;
2021-12-02 16:23:51 -08:00
}
( " compute-slot-cost " , Some ( arg_matches ) ) = > {
2022-07-18 10:19:17 -07:00
let blockstore = open_blockstore (
& ledger_path ,
AccessType ::Secondary ,
wal_recovery_mode ,
2022-08-14 03:20:13 -07:00
force_update_to_open ,
2022-07-18 10:19:17 -07:00
) ;
2021-06-02 09:29:02 -07:00
2021-12-02 16:23:51 -08:00
let mut slots : Vec < u64 > = vec! [ ] ;
if ! arg_matches . is_present ( " slots " ) {
if let Ok ( metas ) = blockstore . slot_meta_iterator ( 0 ) {
slots = metas . map ( | ( slot , _ ) | slot ) . collect ( ) ;
}
} else {
slots = values_t_or_exit! ( arg_matches , " slots " , Slot ) ;
2021-06-02 09:29:02 -07:00
}
2021-12-02 16:23:51 -08:00
for slot in slots {
if let Err ( err ) = compute_slot_cost ( & blockstore , slot ) {
2022-12-06 06:30:06 -08:00
eprintln! ( " {err} " ) ;
2021-12-02 16:23:51 -08:00
}
2021-06-02 09:29:02 -07:00
}
}
2022-09-06 21:46:35 -07:00
( " print-file-metadata " , Some ( arg_matches ) ) = > {
let blockstore = open_blockstore (
& ledger_path ,
AccessType ::Secondary ,
wal_recovery_mode ,
false ,
) ;
let sst_file_name = arg_matches . value_of ( " file_name " ) ;
if let Err ( err ) = print_blockstore_file_metadata ( & blockstore , & sst_file_name ) {
2022-12-06 06:30:06 -08:00
eprintln! ( " {err} " ) ;
2022-09-06 21:46:35 -07:00
}
}
2023-05-10 12:25:38 -07:00
( " program " , Some ( arg_matches ) ) = > {
program ( & ledger_path , arg_matches ) ;
2023-05-05 02:14:41 -07:00
}
2021-12-02 16:23:51 -08:00
( " " , _ ) = > {
eprintln! ( " {} " , matches . usage ( ) ) ;
2023-03-16 18:30:10 -07:00
exit ( 1 ) ;
2021-12-02 16:23:51 -08:00
}
_ = > unreachable! ( ) ,
} ;
2022-07-05 07:23:06 -07:00
measure_total_execution_time . stop ( ) ;
info! ( " {} " , measure_total_execution_time ) ;
2021-12-02 16:23:51 -08:00
}
2018-08-04 14:31:12 -07:00
}