From 191cf4c91020c5f00bd790bb9f79a657a0b681d1 Mon Sep 17 00:00:00 2001 From: Joe C Date: Tue, 26 Nov 2024 21:54:12 +0900 Subject: [PATCH] test validator: make feature accounts additive (#3766) * test validator: make feature accounts additive * add test for feature override --- runtime/src/genesis_utils.rs | 34 ++++++++++- test-validator/src/lib.rs | 115 ++++++++++++++++++++++++++++++----- 2 files changed, 132 insertions(+), 17 deletions(-) diff --git a/runtime/src/genesis_utils.rs b/runtime/src/genesis_utils.rs index 0e91fe08616acd..2d163ddc53b120 100644 --- a/runtime/src/genesis_utils.rs +++ b/runtime/src/genesis_utils.rs @@ -231,7 +231,7 @@ pub fn activate_feature(genesis_config: &mut GenesisConfig, feature_id: Pubkey) } #[allow(clippy::too_many_arguments)] -pub fn create_genesis_config_with_leader_ex( +pub fn create_genesis_config_with_leader_ex_no_features( mint_lamports: u64, mint_pubkey: &Pubkey, validator_pubkey: &Pubkey, @@ -295,6 +295,38 @@ pub fn create_genesis_config_with_leader_ex( }; solana_stake_program::add_genesis_accounts(&mut genesis_config); + + genesis_config +} + +#[allow(clippy::too_many_arguments)] +pub fn create_genesis_config_with_leader_ex( + mint_lamports: u64, + mint_pubkey: &Pubkey, + validator_pubkey: &Pubkey, + validator_vote_account_pubkey: &Pubkey, + validator_stake_account_pubkey: &Pubkey, + validator_stake_lamports: u64, + validator_lamports: u64, + fee_rate_governor: FeeRateGovernor, + rent: Rent, + cluster_type: ClusterType, + initial_accounts: Vec<(Pubkey, AccountSharedData)>, +) -> GenesisConfig { + let mut genesis_config = create_genesis_config_with_leader_ex_no_features( + mint_lamports, + mint_pubkey, + validator_pubkey, + validator_vote_account_pubkey, + validator_stake_account_pubkey, + validator_stake_lamports, + validator_lamports, + fee_rate_governor, + rent, + cluster_type, + initial_accounts, + ); + if genesis_config.cluster_type == ClusterType::Development { activate_all_features(&mut genesis_config); } diff --git a/test-validator/src/lib.rs b/test-validator/src/lib.rs index f6044b3de5300b..0c77705e04acce 100644 --- a/test-validator/src/lib.rs +++ b/test-validator/src/lib.rs @@ -34,8 +34,10 @@ use { solana_rpc_client::{nonblocking, rpc_client::RpcClient}, solana_rpc_client_api::request::MAX_MULTIPLE_ACCOUNTS, solana_runtime::{ - bank_forks::BankForks, genesis_utils::create_genesis_config_with_leader_ex, - runtime_config::RuntimeConfig, snapshot_config::SnapshotConfig, + bank_forks::BankForks, + genesis_utils::{self, create_genesis_config_with_leader_ex_no_features}, + runtime_config::RuntimeConfig, + snapshot_config::SnapshotConfig, }, solana_sdk::{ account::{Account, AccountSharedData, ReadableAccount, WritableAccount}, @@ -44,6 +46,7 @@ use { commitment_config::CommitmentConfig, epoch_schedule::EpochSchedule, exit::Exit, + feature_set::FeatureSet, fee_calculator::FeeRateGovernor, instruction::{AccountMeta, Instruction}, message::Message, @@ -829,7 +832,7 @@ impl TestValidator { ); } - let mut genesis_config = create_genesis_config_with_leader_ex( + let mut genesis_config = create_genesis_config_with_leader_ex_no_features( mint_lamports, &mint_address, &validator_identity.pubkey(), @@ -852,23 +855,21 @@ impl TestValidator { genesis_config.ticks_per_slot = ticks_per_slot; } - // Remove features tagged to deactivate - for deactivate_feature_pk in &config.deactivate_feature_set { - if FEATURE_NAMES.contains_key(deactivate_feature_pk) { - match genesis_config.accounts.remove(deactivate_feature_pk) { - Some(_) => info!("Feature for {:?} deactivated", deactivate_feature_pk), - None => warn!( - "Feature {:?} set for deactivation not found in genesis_config account list, ignored.", - deactivate_feature_pk - ), - } + // Only activate features which are not explicitly deactivated. + let mut feature_set = FeatureSet::default().inactive; + for feature in &config.deactivate_feature_set { + if feature_set.remove(feature) { + info!("Feature for {:?} deactivated", feature) } else { warn!( "Feature {:?} set for deactivation is not a known Feature public key", - deactivate_feature_pk - ); + feature, + ) } } + for feature in feature_set { + genesis_utils::activate_feature(&mut genesis_config, feature); + } let ledger_path = match &config.ledger_path { None => create_new_tmp_ledger!(&genesis_config).0, @@ -1193,7 +1194,7 @@ impl Drop for TestValidator { #[cfg(test)] mod test { - use super::*; + use {super::*, solana_sdk::feature::Feature}; #[test] fn get_health() { @@ -1217,4 +1218,86 @@ mod test { // `start()` blows up when run within tokio let (_test_validator, _payer) = TestValidatorGenesis::default().start(); } + + #[tokio::test] + async fn test_deactivate_features() { + let mut control = FeatureSet::default().inactive; + let mut deactivate_features = Vec::new(); + [ + solana_sdk::feature_set::deprecate_rewards_sysvar::id(), + solana_sdk::feature_set::disable_fees_sysvar::id(), + ] + .into_iter() + .for_each(|feature| { + control.remove(&feature); + deactivate_features.push(feature); + }); + + // Convert to `Vec` so we can get a slice. + let control: Vec = control.into_iter().collect(); + + let (test_validator, _payer) = TestValidatorGenesis::default() + .deactivate_features(&deactivate_features) + .start_async() + .await; + + let rpc_client = test_validator.get_async_rpc_client(); + + // Our deactivated features should be inactive. + let inactive_feature_accounts = rpc_client + .get_multiple_accounts(&deactivate_features) + .await + .unwrap(); + for f in inactive_feature_accounts { + assert!(f.is_none()); + } + + // Everything else should be active. + for chunk in control.chunks(100) { + let active_feature_accounts = rpc_client.get_multiple_accounts(chunk).await.unwrap(); + for f in active_feature_accounts { + let account = f.unwrap(); // Should be `Some`. + let feature_state: Feature = bincode::deserialize(account.data()).unwrap(); + assert!(feature_state.activated_at.is_some()); + } + } + } + + #[tokio::test] + async fn test_override_feature_account() { + let with_deactivate_flag = solana_sdk::feature_set::deprecate_rewards_sysvar::id(); + let without_deactivate_flag = solana_sdk::feature_set::disable_fees_sysvar::id(); + + let owner = Pubkey::new_unique(); + let account = || AccountSharedData::new(100_000, 0, &owner); + + let (test_validator, _payer) = TestValidatorGenesis::default() + .deactivate_features(&[with_deactivate_flag]) // Just deactivate one feature. + .add_accounts([ + (with_deactivate_flag, account()), // But add both accounts. + (without_deactivate_flag, account()), + ]) + .start_async() + .await; + + let rpc_client = test_validator.get_async_rpc_client(); + + let our_accounts = rpc_client + .get_multiple_accounts(&[with_deactivate_flag, without_deactivate_flag]) + .await + .unwrap(); + + // The first one, where we provided `--deactivate-feature`, should be + // the account we provided. + let overriden_account = our_accounts[0].as_ref().unwrap(); + assert_eq!(overriden_account.lamports, 100_000); + assert_eq!(overriden_account.data.len(), 0); + assert_eq!(overriden_account.owner, owner); + + // The second one should be a feature account. + let feature_account = our_accounts[1].as_ref().unwrap(); + assert_eq!(feature_account.owner, solana_sdk::feature::id()); + let feature_state: Feature = bincode::deserialize(feature_account.data()).unwrap(); + assert!(feature_state.activated_at.is_some()); + } }