Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit 08eb469

Browse files
mergify[bot]steviez
andauthored
v1.16: Add ability to output Bank hash details (backport of #32632) (#34257)
* Add ability to output components that go into Bank hash (#32632) When a consensus divergance occurs, the current workflow involves a handful of manual steps to hone in on the offending slot and transaction. This process isn't overly difficult to execute; however, it is tedious and currently involves creating and parsing logs. This change introduces functionality to output a debug file that contains the components go into the bank hash. The file can be generated in two ways: - Via solana-validator when the node realizes it has diverged - Via solana-ledger-tool verify by passing a flag When a divergance occurs now, the steps to debug would be: - Grab the file from the node that diverged - Generate a file for the same slot with ledger-tool with a known good version - Diff the files, they are pretty-printed json (cherry picked from commit 6bbf514) # Conflicts: # Cargo.lock # ledger-tool/src/args.rs # ledger-tool/src/main.rs # programs/sbf/Cargo.lock # runtime/Cargo.toml # runtime/src/accounts_db.rs # validator/src/main.rs * Merge conflict * Reorder base_wotking with accounts_hash to match other branches --------- Co-authored-by: steviez <[email protected]>
1 parent d63fc61 commit 08eb469

File tree

10 files changed

+390
-14
lines changed

10 files changed

+390
-14
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/src/replay_stage.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ use {
5959
solana_rpc_client_api::response::SlotUpdate,
6060
solana_runtime::{
6161
accounts_background_service::AbsRequestSender,
62-
bank::{Bank, NewBankOptions},
62+
bank::{bank_hash_details, Bank, NewBankOptions},
6363
bank_forks::{BankForks, MAX_ROOT_DISTANCE_FOR_VOTE_ONLY},
6464
commitment::BlockCommitmentCache,
6565
prioritization_fee_cache::PrioritizationFeeCache,
@@ -1502,6 +1502,7 @@ impl ReplayStage {
15021502
let bank = w_bank_forks
15031503
.remove(*slot)
15041504
.expect("BankForks should not have been purged yet");
1505+
let _ = bank_hash_details::write_bank_hash_details_file(&bank);
15051506
((*slot, bank.bank_id()), bank)
15061507
})
15071508
.unzip()

ledger-tool/src/args.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ pub fn get_accounts_db_config(
5757

5858
AccountsDbConfig {
5959
index: Some(accounts_index_config),
60+
base_working_path: Some(ledger_path.to_path_buf()),
6061
accounts_hash_cache_path: Some(
6162
ledger_path.join(AccountsDb::DEFAULT_ACCOUNTS_HASH_CACHE_DIR),
6263
),

ledger-tool/src/main.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use {
4747
accounts::Accounts,
4848
accounts_db::CalcAccountsHashDataSource,
4949
accounts_index::ScanConfig,
50-
bank::{Bank, RewardCalculationEvent, TotalAccountsStats},
50+
bank::{bank_hash_details, Bank, RewardCalculationEvent, TotalAccountsStats},
5151
bank_forks::BankForks,
5252
cost_model::CostModel,
5353
cost_tracker::CostTracker,
@@ -1629,6 +1629,14 @@ fn main() {
16291629
.takes_value(false)
16301630
.help("After verifying the ledger, print some information about the account stores"),
16311631
)
1632+
.arg(
1633+
Arg::with_name("write_bank_file")
1634+
.long("write-bank-file")
1635+
.takes_value(false)
1636+
.help("After verifying the ledger, write a file that contains the information \
1637+
that went into computing the completed bank's bank hash. The file will be \
1638+
written within <LEDGER_DIR>/bank_hash_details/"),
1639+
)
16321640
).subcommand(
16331641
SubCommand::with_name("graph")
16341642
.about("Create a Graphviz rendering of the ledger")
@@ -2592,6 +2600,7 @@ fn main() {
25922600
..ProcessOptions::default()
25932601
};
25942602
let print_accounts_stats = arg_matches.is_present("print_accounts_stats");
2603+
let write_bank_file = arg_matches.is_present("write_bank_file");
25952604
let genesis_config = open_genesis_config_by(&ledger_path, arg_matches);
25962605
info!("genesis hash: {}", genesis_config.hash());
25972606

@@ -2617,6 +2626,10 @@ fn main() {
26172626
let working_bank = bank_forks.read().unwrap().working_bank();
26182627
working_bank.print_accounts_stats();
26192628
}
2629+
if write_bank_file {
2630+
let working_bank = bank_forks.read().unwrap().working_bank();
2631+
let _ = bank_hash_details::write_bank_hash_details_file(&working_bank);
2632+
}
26202633
exit_signal.store(true, Ordering::Relaxed);
26212634
system_monitor_service.join().unwrap();
26222635
}

programs/sbf/Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

runtime/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ edition = { workspace = true }
1111

1212
[dependencies]
1313
arrayref = { workspace = true }
14+
base64 = { workspace = true }
1415
bincode = { workspace = true }
1516
blake3 = { workspace = true }
1617
bv = { workspace = true, features = ["serde"] }
@@ -43,6 +44,7 @@ rayon = { workspace = true }
4344
regex = { workspace = true }
4445
serde = { workspace = true, features = ["rc"] }
4546
serde_derive = { workspace = true }
47+
serde_json = { workspace = true }
4648
solana-address-lookup-table-program = { workspace = true }
4749
solana-bpf-loader-program = { workspace = true }
4850
solana-bucket-map = { workspace = true }
@@ -59,6 +61,7 @@ solana-rayon-threadlimit = { workspace = true }
5961
solana-sdk = { workspace = true }
6062
solana-stake-program = { workspace = true }
6163
solana-system-program = { workspace = true }
64+
solana-version = { workspace = true }
6265
solana-vote-program = { workspace = true }
6366
solana-zk-token-proof-program = { workspace = true }
6467
solana-zk-token-sdk = { workspace = true }

runtime/src/accounts_db.rs

Lines changed: 85 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,7 @@ pub(crate) struct ShrinkCollect<'a, T: ShrinkCollectRefs<'a>> {
469469

470470
pub const ACCOUNTS_DB_CONFIG_FOR_TESTING: AccountsDbConfig = AccountsDbConfig {
471471
index: Some(ACCOUNTS_INDEX_CONFIG_FOR_TESTING),
472+
base_working_path: None,
472473
accounts_hash_cache_path: None,
473474
filler_accounts_config: FillerAccountsConfig::const_default(),
474475
write_cache_limit_bytes: None,
@@ -480,6 +481,7 @@ pub const ACCOUNTS_DB_CONFIG_FOR_TESTING: AccountsDbConfig = AccountsDbConfig {
480481
};
481482
pub const ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS: AccountsDbConfig = AccountsDbConfig {
482483
index: Some(ACCOUNTS_INDEX_CONFIG_FOR_BENCHMARKS),
484+
base_working_path: None,
483485
accounts_hash_cache_path: None,
484486
filler_accounts_config: FillerAccountsConfig::const_default(),
485487
write_cache_limit_bytes: None,
@@ -539,6 +541,8 @@ const ANCIENT_APPEND_VEC_DEFAULT_OFFSET: Option<i64> = Some(-10_000);
539541
#[derive(Debug, Default, Clone)]
540542
pub struct AccountsDbConfig {
541543
pub index: Option<AccountsIndexConfig>,
544+
/// Base directory for various necessary files
545+
pub base_working_path: Option<PathBuf>,
542546
pub accounts_hash_cache_path: Option<PathBuf>,
543547
pub filler_accounts_config: FillerAccountsConfig,
544548
pub write_cache_limit_bytes: Option<u64>,
@@ -1395,12 +1399,14 @@ pub struct AccountsDb {
13951399
/// Set of storage paths to pick from
13961400
pub(crate) paths: Vec<PathBuf>,
13971401

1398-
accounts_hash_cache_path: PathBuf,
1399-
1402+
/// Base directory for various necessary files
1403+
base_working_path: PathBuf,
14001404
// used by tests
14011405
// holds this until we are dropped
14021406
#[allow(dead_code)]
1403-
temp_accounts_hash_cache_path: Option<TempDir>,
1407+
base_working_temp_dir: Option<TempDir>,
1408+
/// Directory for account hash calculations, within base_working_path
1409+
accounts_hash_cache_path: PathBuf,
14041410

14051411
pub shrink_paths: RwLock<Option<Vec<PathBuf>>>,
14061412

@@ -2347,29 +2353,47 @@ impl<'a> AppendVecScan for ScanState<'a> {
23472353
}
23482354
}
23492355

2356+
#[derive(Clone, Debug, Eq, PartialEq)]
2357+
pub struct PubkeyHashAccount {
2358+
pub pubkey: Pubkey,
2359+
pub hash: Hash,
2360+
pub account: AccountSharedData,
2361+
}
2362+
23502363
impl AccountsDb {
23512364
pub const DEFAULT_ACCOUNTS_HASH_CACHE_DIR: &str = "accounts_hash_cache";
23522365

23532366
pub fn default_for_tests() -> Self {
2354-
Self::default_with_accounts_index(AccountInfoAccountsIndex::default_for_tests(), None)
2367+
Self::default_with_accounts_index(AccountInfoAccountsIndex::default_for_tests(), None, None)
23552368
}
23562369

23572370
fn default_with_accounts_index(
23582371
accounts_index: AccountInfoAccountsIndex,
2372+
base_working_path: Option<PathBuf>,
23592373
accounts_hash_cache_path: Option<PathBuf>,
23602374
) -> Self {
23612375
let num_threads = get_thread_count();
23622376
const MAX_READ_ONLY_CACHE_DATA_SIZE: usize = 400_000_000; // 400M bytes
23632377

2364-
let (accounts_hash_cache_path, temp_accounts_hash_cache_path) =
2365-
if let Some(accounts_hash_cache_path) = accounts_hash_cache_path {
2366-
(accounts_hash_cache_path, None)
2378+
let (base_working_path, base_working_temp_dir) =
2379+
if let Some(base_working_path) = base_working_path {
2380+
(base_working_path, None)
23672381
} else {
2368-
let temp_dir = TempDir::new().expect("new tempdir");
2369-
let cache_path = temp_dir.path().to_path_buf();
2370-
(cache_path, Some(temp_dir))
2382+
let temp_base_working_dir = TempDir::new().unwrap();
2383+
let base_working_path = temp_base_working_dir.path().to_path_buf();
2384+
(base_working_path, Some(temp_base_working_dir))
23712385
};
23722386

2387+
let accounts_hash_cache_path = accounts_hash_cache_path.unwrap_or_else(|| {
2388+
let accounts_hash_cache_path =
2389+
base_working_path.join(Self::DEFAULT_ACCOUNTS_HASH_CACHE_DIR);
2390+
if !accounts_hash_cache_path.exists() {
2391+
std::fs::create_dir(&accounts_hash_cache_path)
2392+
.expect("create accounts hash cache dir");
2393+
}
2394+
accounts_hash_cache_path
2395+
});
2396+
23732397
let mut bank_hash_stats = HashMap::new();
23742398
bank_hash_stats.insert(0, BankHashStats::default());
23752399

@@ -2398,8 +2422,9 @@ impl AccountsDb {
23982422
write_cache_limit_bytes: None,
23992423
write_version: AtomicU64::new(0),
24002424
paths: vec![],
2425+
base_working_path,
2426+
base_working_temp_dir,
24012427
accounts_hash_cache_path,
2402-
temp_accounts_hash_cache_path,
24032428
shrink_paths: RwLock::new(None),
24042429
temp_paths: None,
24052430
file_size: DEFAULT_FILE_SIZE,
@@ -2477,6 +2502,9 @@ impl AccountsDb {
24772502
accounts_db_config.as_mut().and_then(|x| x.index.take()),
24782503
exit,
24792504
);
2505+
let base_working_path = accounts_db_config
2506+
.as_ref()
2507+
.and_then(|config| config.base_working_path.clone());
24802508
let accounts_hash_cache_path = accounts_db_config
24812509
.as_ref()
24822510
.and_then(|config| config.accounts_hash_cache_path.clone());
@@ -2534,7 +2562,11 @@ impl AccountsDb {
25342562
.and_then(|x| x.write_cache_limit_bytes),
25352563
partitioned_epoch_rewards_config,
25362564
exhaustively_verify_refcounts,
2537-
..Self::default_with_accounts_index(accounts_index, accounts_hash_cache_path)
2565+
..Self::default_with_accounts_index(
2566+
accounts_index,
2567+
base_working_path,
2568+
accounts_hash_cache_path,
2569+
)
25382570
};
25392571
if paths_is_empty {
25402572
// Create a temporary set of accounts directories, used primarily
@@ -2581,6 +2613,11 @@ impl AccountsDb {
25812613
self.file_size
25822614
}
25832615

2616+
/// Get the base working directory
2617+
pub fn get_base_working_path(&self) -> PathBuf {
2618+
self.base_working_path.clone()
2619+
}
2620+
25842621
pub fn new_single_for_tests() -> Self {
25852622
AccountsDb::new_for_tests(Vec::new(), &ClusterType::Development)
25862623
}
@@ -7775,6 +7812,42 @@ impl AccountsDb {
77757812
(hashes, scan.as_us(), accumulate)
77767813
}
77777814

7815+
/// Return all of the accounts for a given slot
7816+
pub fn get_pubkey_hash_account_for_slot(&self, slot: Slot) -> Vec<PubkeyHashAccount> {
7817+
type ScanResult =
7818+
ScanStorageResult<PubkeyHashAccount, DashMap<Pubkey, (Hash, AccountSharedData)>>;
7819+
let scan_result: ScanResult = self.scan_account_storage(
7820+
slot,
7821+
|loaded_account: LoadedAccount| {
7822+
// Cache only has one version per key, don't need to worry about versioning
7823+
Some(PubkeyHashAccount {
7824+
pubkey: *loaded_account.pubkey(),
7825+
hash: loaded_account.loaded_hash(),
7826+
account: loaded_account.take_account(),
7827+
})
7828+
},
7829+
|accum: &DashMap<Pubkey, (Hash, AccountSharedData)>, loaded_account: LoadedAccount| {
7830+
// Storage may have duplicates so only keep the latest version for each key
7831+
accum.insert(
7832+
*loaded_account.pubkey(),
7833+
(loaded_account.loaded_hash(), loaded_account.take_account()),
7834+
);
7835+
},
7836+
);
7837+
7838+
match scan_result {
7839+
ScanStorageResult::Cached(cached_result) => cached_result,
7840+
ScanStorageResult::Stored(stored_result) => stored_result
7841+
.into_iter()
7842+
.map(|(pubkey, (hash, account))| PubkeyHashAccount {
7843+
pubkey,
7844+
hash,
7845+
account,
7846+
})
7847+
.collect(),
7848+
}
7849+
}
7850+
77787851
/// Calculate accounts delta hash for `slot`
77797852
///
77807853
/// As part of calculating the accounts delta hash, get a list of accounts modified this slot

runtime/src/bank.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ struct VerifyAccountsHashConfig {
200200
}
201201

202202
mod address_lookup_table;
203+
pub mod bank_hash_details;
203204
mod builtin_programs;
204205
mod metrics;
205206
mod sysvar_cache;

0 commit comments

Comments
 (0)