2019-07-11 20:33:36 -07:00
use clap ::{ crate_description , crate_name , crate_version , value_t , App , Arg , SubCommand } ;
2019-02-07 20:52:39 -08:00
use solana ::blocktree ::Blocktree ;
2019-03-02 08:16:46 -08:00
use solana ::blocktree_processor ::process_blocktree ;
2019-02-18 22:26:22 -08:00
use solana_sdk ::genesis_block ::GenesisBlock ;
2019-07-12 16:58:13 -07:00
use std ::collections ::BTreeMap ;
use std ::fs ::File ;
2018-08-04 14:31:12 -07:00
use std ::io ::{ stdout , Write } ;
2018-08-06 16:03:08 -07:00
use std ::process ::exit ;
2019-07-12 16:58:13 -07:00
use std ::str ::FromStr ;
2018-08-04 14:31:12 -07:00
2019-07-11 20:33:36 -07:00
#[ derive(PartialEq) ]
enum LedgerOutputMethod {
Print ,
Json ,
}
fn output_ledger ( blocktree : Blocktree , starting_slot : u64 , method : LedgerOutputMethod ) {
let rooted_slot_iterator = blocktree
. rooted_slot_iterator ( starting_slot )
. unwrap_or_else ( | err | {
eprintln! (
" Failed to load entries starting from slot {}: {:?} " ,
starting_slot , err
) ;
exit ( 1 ) ;
} ) ;
if method = = LedgerOutputMethod ::Json {
stdout ( ) . write_all ( b " { \" ledger \" :[ \n " ) . expect ( " open array " ) ;
}
for ( slot , slot_meta ) in rooted_slot_iterator {
match method {
LedgerOutputMethod ::Print = > println! ( " Slot {} " , slot ) ,
LedgerOutputMethod ::Json = > {
serde_json ::to_writer ( stdout ( ) , & slot_meta ) . expect ( " serialize slot_meta " ) ;
stdout ( ) . write_all ( b " , \n " ) . expect ( " newline " ) ;
}
}
let entries = blocktree
. get_slot_entries ( slot , 0 , None )
. unwrap_or_else ( | err | {
eprintln! ( " Failed to load entries for slot {} : {:?} " , slot , err ) ;
exit ( 1 ) ;
} ) ;
for entry in entries {
match method {
LedgerOutputMethod ::Print = > println! ( " {:?} " , entry ) ,
LedgerOutputMethod ::Json = > {
serde_json ::to_writer ( stdout ( ) , & entry ) . expect ( " serialize entry " ) ;
stdout ( ) . write_all ( b " , \n " ) . expect ( " newline " ) ;
}
}
}
}
if method = = LedgerOutputMethod ::Json {
stdout ( ) . write_all ( b " \n ]} \n " ) . expect ( " close array " ) ;
}
}
2018-08-04 14:31:12 -07:00
fn main ( ) {
2019-07-12 16:58:13 -07:00
const DEFAULT_ROOT_COUNT : & str = " 1 " ;
2018-12-14 12:36:50 -08:00
solana_logger ::setup ( ) ;
2019-07-11 20:33:36 -07:00
let matches = App ::new ( crate_name! ( ) )
. about ( crate_description! ( ) )
2018-08-06 20:51:12 -07:00
. version ( crate_version! ( ) )
2018-08-04 14:31:12 -07:00
. arg (
Arg ::with_name ( " ledger " )
. short ( " l " )
. long ( " ledger " )
. value_name ( " DIR " )
. takes_value ( true )
. required ( true )
2018-09-14 15:32:57 -07:00
. help ( " Use directory for ledger location " ) ,
2018-08-04 14:31:12 -07:00
)
2018-08-09 22:14:04 -07:00
. arg (
2019-07-11 20:33:36 -07:00
Arg ::with_name ( " starting_slot " )
. long ( " starting-slot " )
2018-12-07 20:44:59 -08:00
. value_name ( " NUM " )
. takes_value ( true )
2019-07-11 20:33:36 -07:00
. default_value ( " 0 " )
. help ( " Start at this slot (only applies to print and json commands) " ) ,
2018-09-26 18:37:24 -07:00
)
2018-08-06 16:03:08 -07:00
. subcommand ( SubCommand ::with_name ( " print " ) . about ( " Print the ledger " ) )
2018-08-09 22:14:04 -07:00
. subcommand ( SubCommand ::with_name ( " json " ) . about ( " Print the ledger in JSON format " ) )
. subcommand ( SubCommand ::with_name ( " verify " ) . about ( " Verify the ledger's PoH " ) )
2019-07-12 16:58:13 -07:00
. subcommand ( SubCommand ::with_name ( " prune " ) . about ( " Prune the ledger at the block height " ) . arg (
Arg ::with_name ( " slot_list " )
. long ( " slot-list " )
. value_name ( " FILENAME " )
. takes_value ( true )
. help ( " The location of the YAML file with a list of rollback slot heights and hashes " ) ,
) )
. subcommand ( SubCommand ::with_name ( " list-roots " ) . about ( " Output upto last <num-roots> root hashes and their heights starting at the given block height " ) . arg (
Arg ::with_name ( " max_height " )
. long ( " max-height " )
. value_name ( " NUM " )
. takes_value ( true )
. required ( true )
. help ( " Maximum block height " ) ,
) . arg (
Arg ::with_name ( " slot_list " )
. long ( " slot-list " )
. value_name ( " FILENAME " )
. required ( false )
. takes_value ( true )
. help ( " The location of the output YAML file. A list of rollback slot heights and hashes will be written to the file. " ) ,
) . 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 " ) ,
) )
2018-08-04 14:31:12 -07:00
. get_matches ( ) ;
let ledger_path = matches . value_of ( " ledger " ) . unwrap ( ) ;
2018-08-10 18:41:26 -07:00
2019-01-24 12:04:04 -08:00
let genesis_block = GenesisBlock ::load ( ledger_path ) . unwrap_or_else ( | err | {
eprintln! (
" Failed to open ledger genesis_block at {}: {} " ,
ledger_path , err
) ;
exit ( 1 ) ;
} ) ;
2019-02-07 20:52:39 -08:00
let blocktree = match Blocktree ::open ( ledger_path ) {
Ok ( blocktree ) = > blocktree ,
2019-01-03 21:29:21 -08:00
Err ( err ) = > {
eprintln! ( " Failed to open ledger at {} : {} " , ledger_path , err ) ;
2018-08-10 18:41:26 -07:00
exit ( 1 ) ;
}
2019-01-03 21:29:21 -08:00
} ;
2018-09-26 18:37:24 -07:00
2019-07-11 20:33:36 -07:00
let starting_slot = value_t! ( matches , " starting_slot " , u64 ) . unwrap_or_else ( | e | e . exit ( ) ) ;
2018-12-07 20:44:59 -08:00
2018-08-06 16:03:08 -07:00
match matches . subcommand ( ) {
( " print " , _ ) = > {
2019-07-11 20:33:36 -07:00
output_ledger ( blocktree , starting_slot , LedgerOutputMethod ::Print ) ;
2018-08-09 22:14:04 -07:00
}
( " json " , _ ) = > {
2019-07-11 20:33:36 -07:00
output_ledger ( blocktree , starting_slot , LedgerOutputMethod ::Json ) ;
}
( " verify " , _ ) = > {
println! ( " Verifying ledger... " ) ;
2019-07-12 16:58:13 -07:00
match process_blocktree ( & genesis_block , & blocktree , None , true ) {
2019-07-11 20:33:36 -07:00
Ok ( ( _bank_forks , bank_forks_info , _ ) ) = > {
println! ( " {:?} " , bank_forks_info ) ;
2018-08-09 22:14:04 -07:00
}
2019-07-11 20:33:36 -07:00
Err ( err ) = > {
eprintln! ( " Ledger verification failed: {:?} " , err ) ;
exit ( 1 ) ;
2018-12-07 20:44:59 -08:00
}
2018-08-06 16:03:08 -07:00
}
}
2019-07-12 16:58:13 -07:00
( " prune " , Some ( args_matches ) ) = > {
if let Some ( prune_file_path ) = args_matches . value_of ( " slot_list " ) {
let prune_file = File ::open ( prune_file_path . to_string ( ) ) . unwrap ( ) ;
let slot_hashes : BTreeMap < u64 , String > =
serde_yaml ::from_reader ( prune_file ) . unwrap ( ) ;
let iter = blocktree
. rooted_slot_iterator ( 0 )
. expect ( " Failed to get rooted slot " ) ;
let potential_hashes : Vec < _ > = iter
. filter_map ( | ( slot , meta ) | {
let blockhash = blocktree
. get_slot_entries ( slot , meta . last_index , Some ( 1 ) )
. unwrap ( )
. first ( )
. unwrap ( )
. hash
. to_string ( ) ;
slot_hashes . get ( & slot ) . and_then ( | hash | {
if * hash = = blockhash {
Some ( ( slot , blockhash ) )
} else {
None
}
} )
} )
. collect ( ) ;
let ( target_slot , target_hash ) = potential_hashes
. last ( )
. expect ( " Failed to find a valid slot " ) ;
println! ( " Prune at slot {:?} hash {:?} " , target_slot , target_hash ) ;
// ToDo: Do the actual pruning of the database
}
}
( " list-roots " , Some ( args_matches ) ) = > {
let max_height = if let Some ( height ) = args_matches . value_of ( " max_height " ) {
usize ::from_str ( height ) . expect ( " Maximum height must be a number " )
} else {
panic! ( " Maximum height must be provided " ) ;
} ;
let num_roots = if let Some ( roots ) = args_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 ( )
} ;
let iter = blocktree
. rooted_slot_iterator ( 0 )
. expect ( " Failed to get rooted slot " ) ;
let slot_hash : Vec < _ > = iter
. filter_map ( | ( slot , meta ) | {
if slot < = max_height as u64 {
let blockhash = blocktree
. get_slot_entries ( slot , meta . last_index , Some ( 1 ) )
. unwrap ( )
. first ( )
. unwrap ( )
. hash ;
Some ( ( slot , blockhash ) )
} else {
None
}
} )
. collect ( ) ;
let mut output_file : Box < Write > = if let Some ( path ) = args_matches . value_of ( " slot_list " )
{
match File ::create ( path ) {
Ok ( file ) = > Box ::new ( file ) ,
_ = > Box ::new ( stdout ( ) ) ,
}
} else {
Box ::new ( stdout ( ) )
} ;
slot_hash
. into_iter ( )
. rev ( )
. enumerate ( )
. for_each ( | ( i , ( slot , hash ) ) | {
if i < num_roots {
output_file
. write_all ( format! ( " {:?} : {:?} \n " , slot , hash ) . as_bytes ( ) )
. expect ( " failed to write " ) ;
}
} ) ;
}
2018-08-06 16:03:08 -07:00
( " " , _ ) = > {
2018-08-09 22:14:04 -07:00
eprintln! ( " {} " , matches . usage ( ) ) ;
exit ( 1 ) ;
2018-08-06 16:03:08 -07:00
}
_ = > unreachable! ( ) ,
} ;
2018-08-04 14:31:12 -07:00
}