From 1dd49fbbb471b526f5db0389f8d2045c68ac1904 Mon Sep 17 00:00:00 2001 From: Thorsten Lorenz Date: Fri, 14 Feb 2025 13:17:27 +0700 Subject: [PATCH 01/11] fix: prevent accounts files deletion during replay or tool access --- .../src/persist/accounts_persister.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/magicblock-accounts-db/src/persist/accounts_persister.rs b/magicblock-accounts-db/src/persist/accounts_persister.rs index f40a0b27..b938c8e4 100644 --- a/magicblock-accounts-db/src/persist/accounts_persister.rs +++ b/magicblock-accounts-db/src/persist/accounts_persister.rs @@ -316,6 +316,17 @@ impl AccountsPersister { return Ok(None); }; + // When we drop the AppendVec the underlying file is removed from the + // filesystem. There is no way to configure this via public methods. + // Thus we copy the file before using it for the AppendVec. This way + // we prevent account files being removed when we point a tool at the ledger + // or replay it. + let file = { + let copy = file.with_extension("copy"); + fs::copy(&file, ©)?; + copy + }; + // Create a AccountStorageEntry from the file let file_size = fs::metadata(&file)?.len() as usize; let (append_vec, num_accounts) = From 63b6f09bbe6be90d87c3e7a64a36b28d2e04664c Mon Sep 17 00:00:00 2001 From: Thorsten Lorenz Date: Fri, 14 Feb 2025 13:18:18 +0700 Subject: [PATCH 02/11] feat: genx supports large number of accounts --- Cargo.lock | 3 ++ test-integration/Cargo.lock | 10 ++-- tools/genx/Cargo.toml | 3 ++ tools/genx/src/test_validator.rs | 79 +++++++++++++++++++++++++++----- 4 files changed, 78 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 283b9138..fed991c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2121,9 +2121,12 @@ dependencies = [ name = "genx" version = "0.0.0" dependencies = [ + "base64 0.21.7", "clap 4.5.23", "ledger-stats", "magicblock-accounts-db", + "serde_json", + "solana-rpc-client", "solana-sdk", "tempfile", ] diff --git a/test-integration/Cargo.lock b/test-integration/Cargo.lock index 071ed607..1f067300 100644 --- a/test-integration/Cargo.lock +++ b/test-integration/Cargo.lock @@ -1596,9 +1596,9 @@ dependencies = [ [[package]] name = "ephemeral-rollups-sdk" -version = "0.2.3" +version = "0.2.1" dependencies = [ - "borsh 0.10.4", + "borsh 1.5.3", "ephemeral-rollups-sdk-attribute-commit", "ephemeral-rollups-sdk-attribute-delegate", "ephemeral-rollups-sdk-attribute-ephemeral", @@ -1608,7 +1608,7 @@ dependencies = [ [[package]] name = "ephemeral-rollups-sdk-attribute-commit" -version = "0.2.3" +version = "0.2.1" dependencies = [ "proc-macro2", "quote", @@ -1617,7 +1617,7 @@ dependencies = [ [[package]] name = "ephemeral-rollups-sdk-attribute-delegate" -version = "0.2.3" +version = "0.2.1" dependencies = [ "proc-macro2", "quote", @@ -1626,7 +1626,7 @@ dependencies = [ [[package]] name = "ephemeral-rollups-sdk-attribute-ephemeral" -version = "0.2.3" +version = "0.2.1" dependencies = [ "proc-macro2", "quote", diff --git a/tools/genx/Cargo.toml b/tools/genx/Cargo.toml index fec079c2..78e36eae 100644 --- a/tools/genx/Cargo.toml +++ b/tools/genx/Cargo.toml @@ -8,8 +8,11 @@ license.workspace = true edition.workspace = true [dependencies] +base64 = { workspace = true } clap = { version = "4.5.23", features = ["derive"] } ledger-stats = { workspace = true } magicblock-accounts-db = { workspace = true } +serde_json = { workspace = true } +solana-rpc-client = { workspace = true } solana-sdk = { workspace = true } tempfile = { workspace = true } diff --git a/tools/genx/src/test_validator.rs b/tools/genx/src/test_validator.rs index c8b48dcf..8b895ae4 100644 --- a/tools/genx/src/test_validator.rs +++ b/tools/genx/src/test_validator.rs @@ -1,8 +1,14 @@ -use std::{fs, os::unix::fs::PermissionsExt, path::PathBuf}; +use serde_json::{json, Value}; +use solana_rpc_client::rpc_client::RpcClient; +use std::{ + fs, + os::unix::fs::PermissionsExt, + path::{Path, PathBuf}, +}; use ledger_stats::{accounts_storage_from_ledger, open_ledger}; use magicblock_accounts_db::utils::all_accounts; -use solana_sdk::pubkey::Pubkey; +use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey}; use tempfile::tempdir; pub struct TestValidatorConfig { @@ -16,6 +22,9 @@ pub(crate) fn gen_test_validator_start_script( ) { let temp_dir = tempdir().expect("Failed to create temporary directory"); let temp_dir_path = temp_dir.into_path(); + let accounts_dir = temp_dir_path.join("accounts"); + fs::create_dir(&accounts_dir).expect("Failed to create accounts directory"); + let file_path = temp_dir_path.join("run-validator.sh"); let accounts: Vec = if let Some(ledger_path) = ledger_path { let ledger = open_ledger(ledger_path); @@ -39,16 +48,14 @@ pub(crate) fn gen_test_validator_start_script( "10000".to_string(), ]; - for pubkey in accounts { - // NOTE: we may need to treat executables differently if just cloning - // at startup is not sufficient even though we also will clone the - // executable data account. - // For now we don't run programs in the test validator, but just - // want to make sure they can be cloned by the ephemeral, so it is not - // yet important. - args.push("--maybe-clone".to_string()); - args.push(pubkey.to_string()); - } + download_accounts_into_from_url_into_dir( + &accounts, + config.url.clone(), + &accounts_dir, + ); + + args.push("--account-dir".into()); + args.push(accounts_dir.to_string_lossy().to_string()); args.push("--url".into()); args.push(config.url); @@ -71,3 +78,51 @@ solana-test-validator \\\n {}", file_path.display() ); } + +fn download_accounts_into_from_url_into_dir( + pubkeys: &[Pubkey], + url: String, + dir: &Path, +) { + // Derived from error from helius RPC: Failed to download accounts: Error { request: Some(GetMultipleAccounts), kind: RpcError(RpcResponseError { code: -32602, message: "Too many inputs provided; max 100", data: Empty }) } + const MAX_ACCOUNTS: usize = 100; + let rpc_client = + RpcClient::new_with_commitment(url, CommitmentConfig::confirmed()); + let total_len = pubkeys.len(); + for (idx, pubkeys) in pubkeys.chunks(MAX_ACCOUNTS).enumerate() { + let start = idx * pubkeys.len(); + let end = start + pubkeys.len(); + eprintln!("Downloading {}..{}/{} accounts", start, end, total_len); + match rpc_client.get_multiple_accounts(pubkeys) { + Ok(accs) => accs + .into_iter() + .zip(pubkeys) + .filter_map(|(acc, pubkey)| acc.map(|x| (x, pubkey))) + .for_each(|(acc, pubkey)| { + let path = dir.join(format!("{pubkey}.json")); + let pk = pubkey.to_string(); + let lamports = acc.lamports; + let data = + [base64::encode(&acc.data), "base64".to_string()]; + let owner = acc.owner.to_string(); + let executable = acc.executable; + let rent_epoch = acc.rent_epoch; + let space = acc.data.len(); + let json: Value = json! {{ + "pubkey": pk, + "account": { + "lamports": lamports, + "data": data, + "owner": owner, + "executable": executable, + "space": space, + "rentEpoch": rent_epoch + }, + }}; + fs::write(&path, format!("{:#}", json)) + .expect("Failed to write account"); + }), + Err(err) => eprintln!("Failed to download accounts: {:?}", err), + } + } +} From 8e5dcd0ed5d9346d4ce79b685a68d99b67fa753e Mon Sep 17 00:00:00 2001 From: Thorsten Lorenz Date: Fri, 14 Feb 2025 13:30:55 +0700 Subject: [PATCH 03/11] chore: minor fixes in genx tool --- tools/genx/src/test_validator.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/genx/src/test_validator.rs b/tools/genx/src/test_validator.rs index 8b895ae4..f2133df4 100644 --- a/tools/genx/src/test_validator.rs +++ b/tools/genx/src/test_validator.rs @@ -90,7 +90,7 @@ fn download_accounts_into_from_url_into_dir( RpcClient::new_with_commitment(url, CommitmentConfig::confirmed()); let total_len = pubkeys.len(); for (idx, pubkeys) in pubkeys.chunks(MAX_ACCOUNTS).enumerate() { - let start = idx * pubkeys.len(); + let start = idx * MAX_ACCOUNTS; let end = start + pubkeys.len(); eprintln!("Downloading {}..{}/{} accounts", start, end, total_len); match rpc_client.get_multiple_accounts(pubkeys) { @@ -102,8 +102,11 @@ fn download_accounts_into_from_url_into_dir( let path = dir.join(format!("{pubkey}.json")); let pk = pubkey.to_string(); let lamports = acc.lamports; - let data = - [base64::encode(&acc.data), "base64".to_string()]; + let data = [ + #[allow(deprecated)] // this is just a dev tool + base64::encode(&acc.data), + "base64".to_string(), + ]; let owner = acc.owner.to_string(); let executable = acc.executable; let rent_epoch = acc.rent_epoch; From 675e57de21f0e8b519922e54fc0f10b5c1f53968 Mon Sep 17 00:00:00 2001 From: Thorsten Lorenz Date: Fri, 14 Feb 2025 13:51:43 +0700 Subject: [PATCH 04/11] chore: adapt rust log helpers to new project structure --- sh/source/utils/source-log | 7 ------- 1 file changed, 7 deletions(-) diff --git a/sh/source/utils/source-log b/sh/source/utils/source-log index b9b2487d..e6b4e82f 100644 --- a/sh/source/utils/source-log +++ b/sh/source/utils/source-log @@ -5,8 +5,6 @@ TRACE_ARR=( "geyser_plugin=trace," "magicblock=trace," "rpc=trace," - "bank=trace," - "banking_stage=trace," "solana_geyser_plugin_manager=trace," "solana_svm=trace," "test_tools=trace," @@ -18,8 +16,6 @@ DEBUG_ARR=( "magicblock=debug," "rpc=debug," "bank=debug," - "banking_stage=debug," - "mutator=debug," "solana_geyser_plugin_manager=debug," "solana_svm=debug," "test_tools=debug," @@ -30,9 +26,6 @@ INFO_ARR=( "geyser_plugin=info," "magicblock=info," "rpc=info," - "bank=info," - "banking_stage=info," - "mutator=info," "solana_geyser_plugin_manager=info," "solana_svm=info," "test_tools=info," From 54577d3dc8645cc876d98be1071f75c0ebdec7da Mon Sep 17 00:00:00 2001 From: Thorsten Lorenz Date: Fri, 14 Feb 2025 14:00:16 +0700 Subject: [PATCH 05/11] chore: include transaction signature for block store error during ledger replay --- magicblock-ledger/src/blockstore_processor/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/magicblock-ledger/src/blockstore_processor/mod.rs b/magicblock-ledger/src/blockstore_processor/mod.rs index 9f9605ff..f4aed46a 100644 --- a/magicblock-ledger/src/blockstore_processor/mod.rs +++ b/magicblock-ledger/src/blockstore_processor/mod.rs @@ -206,6 +206,7 @@ pub fn process_ledger(ledger: &Ledger, bank: &Bank) -> LedgerResult { log_sanitized_transaction(&tx); let mut timings = ExecuteTimings::default(); + let signature = *tx.signature(); let batch = [tx]; let batch = bank.prepare_sanitized_batch(&batch); let (results, _) = bank @@ -237,8 +238,8 @@ pub fn process_ledger(ledger: &Ledger, bank: &Bank) -> LedgerResult { }; return Err(LedgerError::BlockStoreProcessor( format!( - "Transaction {:?} could not be executed: {:?}", - result, err + "Transaction '{}', {:?} could not be executed: {:?}", + signature, result, err ), )); } From 058a9fb1cca82ac06c459e17fba54835283cb165 Mon Sep 17 00:00:00 2001 From: Thorsten Lorenz Date: Mon, 17 Feb 2025 15:31:43 +0700 Subject: [PATCH 06/11] chore: num-format crate now used in entire workspace --- Cargo.lock | 1 + Cargo.toml | 1 + magicblock-ledger/Cargo.toml | 1 + tools/ledger-stats/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index fed991c2..c4b1ac37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3702,6 +3702,7 @@ dependencies = [ "magicblock-accounts-db", "magicblock-bank", "magicblock-core", + "num-format", "num_cpus", "prost 0.11.9", "rocksdb", diff --git a/Cargo.toml b/Cargo.toml index dfc97913..5b84f0dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,6 +94,7 @@ log = "0.4.20" min-max-heap = "1.3.0" num_cpus = "1.16.0" num-derive = "0.4" +num-format = "0.4.4" num-traits = "0.2" paste = "1.0" percentage = "0.1.0" diff --git a/magicblock-ledger/Cargo.toml b/magicblock-ledger/Cargo.toml index 1c6beb75..a3f0ba4d 100644 --- a/magicblock-ledger/Cargo.toml +++ b/magicblock-ledger/Cargo.toml @@ -14,6 +14,7 @@ byteorder = { workspace = true } fs_extra = { workspace = true } libc = { workspace = true } num_cpus = { workspace = true } +num-format = { workspace = true } prost = { workspace = true } serde = { workspace = true } magicblock-bank = { workspace = true } diff --git a/tools/ledger-stats/Cargo.toml b/tools/ledger-stats/Cargo.toml index 2d14ac7d..268633e8 100644 --- a/tools/ledger-stats/Cargo.toml +++ b/tools/ledger-stats/Cargo.toml @@ -13,7 +13,7 @@ path = "src/lib.rs" [dependencies] magicblock-accounts-db = { workspace = true } magicblock-ledger = { workspace = true } -num-format = "0.4.4" +num-format = { workspace = true } pretty-hex = "0.4.1" solana-sdk = { workspace = true } solana-transaction-status = { workspace = true } From c4699fcceb8d709a4c745975f37d3071023af7d9 Mon Sep 17 00:00:00 2001 From: Thorsten Lorenz Date: Mon, 17 Feb 2025 15:32:42 +0700 Subject: [PATCH 07/11] feat: log progress of replaying ledger on info --- .../src/blockstore_processor/mod.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/magicblock-ledger/src/blockstore_processor/mod.rs b/magicblock-ledger/src/blockstore_processor/mod.rs index f4aed46a..ab597149 100644 --- a/magicblock-ledger/src/blockstore_processor/mod.rs +++ b/magicblock-ledger/src/blockstore_processor/mod.rs @@ -1,5 +1,7 @@ use std::str::FromStr; +use num_format::{Locale, ToFormattedString}; + use log::{Level::Trace, *}; use magicblock_accounts_db::{ utils::{all_accounts, StoredAccountMeta}, @@ -54,10 +56,30 @@ fn iter_blocks( blockhashes_only_starting_slot, } = params; let mut slot: u64 = blockhashes_only_starting_slot; + + let max_slot = if log::log_enabled!(Level::Info) { + ledger + .get_max_blockhash()? + .0 + .to_formatted_string(&Locale::en) + } else { + "N/A".to_string() + }; + const PROGRESS_REPORT_INTERVAL: u64 = 100; loop { let Ok(Some(block)) = ledger.get_block(slot) else { break; }; + if log::log_enabled!(Level::Info) + && slot % PROGRESS_REPORT_INTERVAL == 0 + { + info!( + "Processing block: {}/{}", + slot.to_formatted_string(&Locale::en), + max_slot + ); + } + let VersionedConfirmedBlock { blockhash, previous_blockhash, @@ -116,6 +138,8 @@ fn iter_blocks( } fn hydrate_bank(bank: &Bank, max_slot: Slot) -> LedgerResult<(Slot, usize)> { + info!("Hydrating bank"); + let persister = AccountsPersister::new_with_paths(vec![bank.accounts_path.clone()]); let Some((storage, slot)) = persister.load_most_recent_store(max_slot)? @@ -134,6 +158,10 @@ fn hydrate_bank(bank: &Bank, max_slot: Slot) -> LedgerResult<(Slot, usize)> { (*acc_meta.pubkey(), AccountSharedData::from(acc)) }); let len = storable_accounts.len(); + info!( + "Storing {} accounts into bank", + len.to_formatted_string(&Locale::en) + ); bank.store_accounts(storable_accounts); Ok((slot, len)) } From 9d2d5ff05b205573080520fcd75740ae79a2f9bb Mon Sep 17 00:00:00 2001 From: Thorsten Lorenz Date: Mon, 17 Feb 2025 15:38:22 +0700 Subject: [PATCH 08/11] chore: consistent RUST_LOG var --- sh/source/utils/source-log | 1 - 1 file changed, 1 deletion(-) diff --git a/sh/source/utils/source-log b/sh/source/utils/source-log index e6b4e82f..a05eb191 100644 --- a/sh/source/utils/source-log +++ b/sh/source/utils/source-log @@ -15,7 +15,6 @@ DEBUG_ARR=( "geyser_plugin=debug," "magicblock=debug," "rpc=debug," - "bank=debug," "solana_geyser_plugin_manager=debug," "solana_svm=debug," "test_tools=debug," From 31d56499a38e3d575187754b857accee82cf4c81 Mon Sep 17 00:00:00 2001 From: Thorsten Lorenz Date: Mon, 17 Feb 2025 17:29:51 +0700 Subject: [PATCH 09/11] feat: add test-ledger command to genx --- tools/genx/src/ledger_replay_test.rs | 46 ++++++++++++++++++++++++++++ tools/genx/src/main.rs | 11 +++++++ 2 files changed, 57 insertions(+) create mode 100644 tools/genx/src/ledger_replay_test.rs diff --git a/tools/genx/src/ledger_replay_test.rs b/tools/genx/src/ledger_replay_test.rs new file mode 100644 index 00000000..9ba35184 --- /dev/null +++ b/tools/genx/src/ledger_replay_test.rs @@ -0,0 +1,46 @@ +use std::fs; +use std::path::PathBuf; + +use solana_sdk::signature::Keypair; +use solana_sdk::signer::{EncodableKey, Signer}; + +pub(crate) fn ledger_replay_test(ledger_path: &PathBuf) { + match fs::exists(ledger_path) { + Ok(x) if !x => { + panic!("Ledger path does not exist: {}", ledger_path.display()) + } + Err(err) => { + panic!( + "Error looking up ledger path: {} ({})", + ledger_path.display(), + err + ) + } + _ => {} + } + + let keypair_file = ledger_path.join("validator-keypair.json"); + + if let Ok(true) = fs::exists(&keypair_file) { + if let Ok(kp) = Keypair::read_from_file("validator-keypair.json") { + eprintln!("Overwriting existing keypair: {}", kp.pubkey()); + } + } + + let kp = Keypair::new(); + Keypair::write_to_file(&kp, &keypair_file) + .expect("Failed to write keypair"); + + eprintln!( + "Wrote test validator authority (pubkey: {}) to ledger", + kp.pubkey() + ); + let base58 = kp.to_base58_string(); + eprintln!( + "Add the keypair to your environment via 'export VALIDATOR_KEYPAIR={}'\n", + base58 + ); + + // Print the base58 keypair to stdout for easy copy/paste and output capture + println!("{}", base58); +} diff --git a/tools/genx/src/main.rs b/tools/genx/src/main.rs index 92a6c522..74a58b24 100644 --- a/tools/genx/src/main.rs +++ b/tools/genx/src/main.rs @@ -2,6 +2,7 @@ use std::path::PathBuf; use clap::{Parser, Subcommand}; use test_validator::TestValidatorConfig; +mod ledger_replay_test; mod test_validator; #[derive(Debug, Parser)] @@ -29,6 +30,13 @@ enum Commands { #[arg(long)] url: String, }, + /// Prepares the ledger for testing + #[command(name = "test-ledger")] + #[command( + about = "Prepares the ledger for testing", + long_about = "(Over)writes the validator keypair" + )] + LedgerReplayTest { ledger_path: PathBuf }, } fn main() { @@ -45,5 +53,8 @@ fn main() { config, ) } + Commands::LedgerReplayTest { ledger_path } => { + ledger_replay_test::ledger_replay_test(&ledger_path) + } } } From 10e9d4fc4fd5d675971cc584ed1e3db01d4b06c3 Mon Sep 17 00:00:00 2001 From: Thorsten Lorenz Date: Mon, 17 Feb 2025 17:30:11 +0700 Subject: [PATCH 10/11] doc: include more instructions for genx tool --- tools/genx/README.md | 48 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/tools/genx/README.md b/tools/genx/README.md index cd941f5c..dea6e8c8 100644 --- a/tools/genx/README.md +++ b/tools/genx/README.md @@ -14,5 +14,51 @@ if they exist on chain ```sh cargo run --release --bin genx test-validator \ --rpc-port 7799 --url 'https://rpc.magicblock.app/mainnet' \ - test-integration/ledgers/ledgers + ledger ``` + +Then you run the script to start a mainnet standin locally. + +Also make sure to update the config with which to start the ephemeral validator that will +replay the ledger to point to the port that it is running on. + +#### Config Example + +```toml +[accounts] +remote = ["http://127.0.0.1:7799", "ws://127.0.0.1:7800"] +lifecycle = "ephemeral" +commit = { frequency_millis = 50_000 } +payer = { init_sol = 1_000 } + +[rpc] +addr = "0.0.0.0" +port = 8899 + +[validator] +millis_per_slot = 50 + +[ledger] +reset = false +path = "./ledger" +``` + +### test-ledger + +Prepares a ledger for replay in a diagnostics/test scenario. At this point it ensures that the +original keypair of the validator operator is not used. Instead it is overwritten with a random +keypair. That keypair is then printed to the terminal so that the `VALIDATOR_KEYPAIR` env var +can be set to it when running the epehemeral validator to replay the ledger. + +One can auto-set that env var using the output of this command as follows: + +```sh +export VALIDATOR_KEYPAIR=$(cargo run --bin genx -- test-ledger ./ledger) +``` + +Then we can just do the following to replay the ledger (i.e. providing the above config): + +```sh +cargo run --release ledger-config.toml +``` + From e6777402ac6ca8f8cc8255173acc925085976f14 Mon Sep 17 00:00:00 2001 From: Thorsten Lorenz Date: Mon, 17 Feb 2025 18:32:11 +0700 Subject: [PATCH 11/11] chore: fix greptile issues --- tools/genx/README.md | 2 +- tools/genx/src/ledger_replay_test.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/genx/README.md b/tools/genx/README.md index dea6e8c8..1fd83f06 100644 --- a/tools/genx/README.md +++ b/tools/genx/README.md @@ -48,7 +48,7 @@ path = "./ledger" Prepares a ledger for replay in a diagnostics/test scenario. At this point it ensures that the original keypair of the validator operator is not used. Instead it is overwritten with a random keypair. That keypair is then printed to the terminal so that the `VALIDATOR_KEYPAIR` env var -can be set to it when running the epehemeral validator to replay the ledger. +can be set to it when running the ephemeral validator to replay the ledger. One can auto-set that env var using the output of this command as follows: diff --git a/tools/genx/src/ledger_replay_test.rs b/tools/genx/src/ledger_replay_test.rs index 9ba35184..4ef863ab 100644 --- a/tools/genx/src/ledger_replay_test.rs +++ b/tools/genx/src/ledger_replay_test.rs @@ -22,7 +22,7 @@ pub(crate) fn ledger_replay_test(ledger_path: &PathBuf) { let keypair_file = ledger_path.join("validator-keypair.json"); if let Ok(true) = fs::exists(&keypair_file) { - if let Ok(kp) = Keypair::read_from_file("validator-keypair.json") { + if let Ok(kp) = Keypair::read_from_file(&keypair_file) { eprintln!("Overwriting existing keypair: {}", kp.pubkey()); } }