diff --git a/Cargo.lock b/Cargo.lock index 2ddad053f..f709f8bac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4858,6 +4858,7 @@ dependencies = [ "signal-hook", "solana-clap-utils", "solana-cli-output", + "solana-core", "solana-ledger", "solana-logger 1.8.0", "solana-measure", diff --git a/ledger-tool/Cargo.toml b/ledger-tool/Cargo.toml index 4b9ea5049..b6a02314f 100644 --- a/ledger-tool/Cargo.toml +++ b/ledger-tool/Cargo.toml @@ -25,6 +25,7 @@ serde_json = "1.0.56" serde_yaml = "0.8.13" solana-clap-utils = { path = "../clap-utils", version = "=1.8.0" } solana-cli-output = { path = "../cli-output", version = "=1.8.0" } +solana-core = { path = "../core", version = "=1.8.0" } solana-ledger = { path = "../ledger", version = "=1.8.0" } solana-logger = { path = "../logger", version = "=1.8.0" } solana-measure = { path = "../measure", version = "=1.8.0" } diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index 7f69d0fb7..1084e6904 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -14,6 +14,8 @@ use solana_clap_utils::{ is_parsable, is_pubkey, is_pubkey_or_keypair, is_slot, is_valid_percentage, }, }; +use solana_core::cost_model::{CostModel, ACCOUNT_MAX_COST, BLOCK_MAX_COST}; +use solana_core::cost_tracker::CostTracker; use solana_ledger::entry::Entry; use solana_ledger::{ ancestor_iterator::AncestorIterator, @@ -722,6 +724,58 @@ fn load_bank_forks( ) } +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) + .map_err(|err| format!(" Slot: {}, Failed to load entries, err {:?}", slot, err))?; + + let mut transactions = 0; + let mut programs = 0; + let mut program_ids = HashMap::new(); + let cost_model = CostModel::new(ACCOUNT_MAX_COST, BLOCK_MAX_COST); + let mut cost_tracker = CostTracker::new( + cost_model.get_account_cost_limit(), + cost_model.get_block_cost_limit(), + ); + + for entry in &entries { + transactions += entry.transactions.len(); + for transaction in &entry.transactions { + programs += transaction.message().instructions.len(); + let tx_cost = cost_model.calculate_cost(&transaction); + if cost_tracker.try_add(tx_cost).is_err() { + println!( + "Slot: {}, CostModel rejected transaction {:?}, stats {:?}!", + slot, + transaction, + cost_tracker.get_stats() + ); + } + for instruction in &transaction.message().instructions { + let program_id = + transaction.message().account_keys[instruction.program_id_index as usize]; + *program_ids.entry(program_id).or_insert(0) += 1; + } + } + } + + println!( + "Slot: {}, Entries: {}, Transactions: {}, Programs {}, {:?}", + slot, + entries.len(), + transactions, + programs, + cost_tracker.get_stats() + ); + println!(" Programs: {:?}", program_ids); + + Ok(()) +} + fn open_genesis_config_by(ledger_path: &Path, matches: &ArgMatches<'_>) -> GenesisConfig { let max_genesis_archive_unpacked_size = value_t_or_exit!(matches, "max_genesis_archive_unpacked_size", u64); @@ -1406,6 +1460,20 @@ fn main() { .about("Output statistics in JSON format about \ all column families in the ledger rocksdb") ) + .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"), + ) + ) .get_matches(); info!("{} {}", crate_name!(), solana_version::version!()); @@ -2995,6 +3063,28 @@ fn main() { )); println!("Ok."); } + ("compute-slot-cost", Some(arg_matches)) => { + let blockstore = open_blockstore( + &ledger_path, + AccessType::TryPrimaryThenSecondary, + wal_recovery_mode, + ); + + let mut slots: Vec = 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); + } + + for slot in slots { + if let Err(err) = compute_slot_cost(&blockstore, slot) { + eprintln!("{}", err); + } + } + } ("", _) => { eprintln!("{}", matches.usage()); exit(1);