From 39af6c092d375f7d602dfe2a92c9ad1bff5a5548 Mon Sep 17 00:00:00 2001 From: Jon C Date: Thu, 8 Aug 2024 23:25:52 +0200 Subject: [PATCH] test-validator: Add flag to clone feature set from a cluster (#2480) * test-validator: Add flag to clone feature set from a cluster #### Problem Program devs run into issues when testing, where everything works great with a solana-test-validator, and then fails once they deploy to a real cluster. Most times, this happens because the test validator enables all features by default, and their program depended on a new feature without their knowledge. #### Summary of changes To make local development much easier, add a `--clone-feature-set` flag to solana-test-validator to easily mimic the functionality on the cluster targeted with `--url`. * Add changelog entry --- CHANGELOG.md | 1 + test-validator/src/lib.rs | 27 ++++++++++++++++++++++ validator/src/bin/solana-test-validator.rs | 13 +++++++++++ validator/src/cli.rs | 11 +++++++++ 4 files changed, 52 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5ab97ef8658dd..0cd6e2b22698d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Release channels have their own copy of this changelog: * Changes * SDK: removed the `respan` macro. This was marked as "internal use only" and was no longer used internally. * `agave-validator`: Update PoH speed check to compare against current hash rate from a Bank (#2447) + * `solana-test-validator`: Add `--clone-feature-set` flag to mimic features from a target cluster (#2480) ## [2.0.0] * Breaking diff --git a/test-validator/src/lib.rs b/test-validator/src/lib.rs index 39616c1fdd4936..218cf9e4141f18 100644 --- a/test-validator/src/lib.rs +++ b/test-validator/src/lib.rs @@ -43,6 +43,7 @@ use { commitment_config::CommitmentConfig, epoch_schedule::EpochSchedule, exit::Exit, + feature, feature_set::FEATURE_NAMES, fee_calculator::FeeRateGovernor, instruction::{AccountMeta, Instruction}, @@ -428,6 +429,32 @@ impl TestValidatorGenesis { Ok(self) } + pub fn clone_feature_set(&mut self, rpc_client: &RpcClient) -> Result<&mut Self, String> { + for feature_ids in FEATURE_NAMES + .keys() + .cloned() + .collect::>() + .chunks(MAX_MULTIPLE_ACCOUNTS) + { + rpc_client + .get_multiple_accounts(feature_ids) + .map_err(|err| format!("Failed to fetch: {err}"))? + .into_iter() + .zip(feature_ids) + .for_each(|(maybe_account, feature_id)| { + if maybe_account + .as_ref() + .and_then(feature::from_account) + .and_then(|feature| feature.activated_at) + .is_none() + { + self.deactivate_feature_set.insert(*feature_id); + } + }); + } + Ok(self) + } + pub fn add_accounts_from_json_files( &mut self, accounts: &[AccountInfo], diff --git a/validator/src/bin/solana-test-validator.rs b/validator/src/bin/solana-test-validator.rs index 7f7865d8ac18e8..bba5a359093370 100644 --- a/validator/src/bin/solana-test-validator.rs +++ b/validator/src/bin/solana-test-validator.rs @@ -281,6 +281,8 @@ fn main() { .map(|v| v.into_iter().collect()) .unwrap_or_default(); + let clone_feature_set = matches.is_present("clone_feature_set"); + let warp_slot = if matches.is_present("warp_slot") { Some(match matches.value_of("warp_slot") { Some(_) => value_t_or_exit!(matches, "warp_slot", Slot), @@ -511,6 +513,17 @@ fn main() { } } + if clone_feature_set { + if let Err(e) = genesis.clone_feature_set( + cluster_rpc_client + .as_ref() + .expect("bug: --url argument missing?"), + ) { + println!("Error: clone_feature_set failed: {e}"); + exit(1); + } + } + if let Some(warp_slot) = warp_slot { genesis.warp_slot(warp_slot); } diff --git a/validator/src/cli.rs b/validator/src/cli.rs index e1665383e777e3..9f4276d8d67ee0 100644 --- a/validator/src/cli.rs +++ b/validator/src/cli.rs @@ -2811,6 +2811,17 @@ pub fn test_app<'a>(version: &'a str, default_args: &'a DefaultTestArgs) -> App< .validator(is_parsable::) .takes_value(true) .help("Override the runtime's account lock limit per transaction"), + ) + .arg( + Arg::with_name("clone_feature_set") + .long("clone-feature-set") + .takes_value(false) + .requires("json_rpc_url") + .help( + "Copy a feature set from the cluster referenced by the --url \ + argument in the genesis configuration. If the ledger \ + already exists then this parameter is silently ignored", + ), ); }