Fix - program loading with effective slot at epoch boundary (#35283)
* Always limit effective slot to the begin of the current epoch. * Adds comments. * Optimizes to avoid having two entries if there is no relevant feature activation. * Adds test_feature_activation_loaded_programs_epoch_transition().
This commit is contained in:
parent
367f489f63
commit
2891ce886b
|
@ -11961,6 +11961,60 @@ fn test_feature_activation_loaded_programs_recompilation_phase() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_feature_activation_loaded_programs_epoch_transition() {
|
||||
solana_logger::setup();
|
||||
|
||||
// Bank Setup
|
||||
let (mut genesis_config, mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
|
||||
genesis_config
|
||||
.accounts
|
||||
.remove(&feature_set::reject_callx_r10::id());
|
||||
let (root_bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
|
||||
|
||||
// Program Setup
|
||||
let program_keypair = Keypair::new();
|
||||
let program_data = include_bytes!("../../../programs/bpf_loader/test_elfs/out/noop_aligned.so");
|
||||
let program_account = AccountSharedData::from(Account {
|
||||
lamports: Rent::default().minimum_balance(program_data.len()).min(1),
|
||||
data: program_data.to_vec(),
|
||||
owner: bpf_loader::id(),
|
||||
executable: true,
|
||||
rent_epoch: 0,
|
||||
});
|
||||
root_bank.store_account(&program_keypair.pubkey(), &program_account);
|
||||
|
||||
// Compose message using the desired program.
|
||||
let instruction = Instruction::new_with_bytes(program_keypair.pubkey(), &[], Vec::new());
|
||||
let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
|
||||
let binding = mint_keypair.insecure_clone();
|
||||
let signers = vec![&binding];
|
||||
|
||||
// Advance the bank so that the program becomes effective.
|
||||
goto_end_of_slot(root_bank.clone());
|
||||
let bank = new_from_parent_with_fork_next_slot(root_bank, bank_forks.as_ref());
|
||||
|
||||
// Load the program with the old environment.
|
||||
let transaction = Transaction::new(&signers, message.clone(), bank.last_blockhash());
|
||||
assert!(bank.process_transaction(&transaction).is_ok());
|
||||
|
||||
// Schedule feature activation to trigger a change of environment at the epoch boundary.
|
||||
let feature_account_balance =
|
||||
std::cmp::max(genesis_config.rent.minimum_balance(Feature::size_of()), 1);
|
||||
bank.store_account(
|
||||
&feature_set::reject_callx_r10::id(),
|
||||
&feature::create_account(&Feature { activated_at: None }, feature_account_balance),
|
||||
);
|
||||
|
||||
// Advance the bank to cross the epoch boundary and activate the feature.
|
||||
goto_end_of_slot(bank.clone());
|
||||
let bank = new_bank_from_parent_with_bank_forks(&bank_forks, bank, &Pubkey::default(), 33);
|
||||
|
||||
// Load the program with the new environment.
|
||||
let transaction = Transaction::new(&signers, message, bank.last_blockhash());
|
||||
assert!(bank.process_transaction(&transaction).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bank_verify_accounts_hash_with_base() {
|
||||
let GenesisConfigInfo {
|
||||
|
|
|
@ -739,10 +739,22 @@ impl<FG: ForkGraph> TransactionBatchProcessor<FG> {
|
|||
|
||||
let mut timings = ExecuteDetailsTimings::default();
|
||||
load_program_metrics.submit_datapoint(&mut timings);
|
||||
if let Some(recompile) = recompile {
|
||||
if !Arc::ptr_eq(
|
||||
&environments.program_runtime_v1,
|
||||
&loaded_programs_cache.environments.program_runtime_v1,
|
||||
) || !Arc::ptr_eq(
|
||||
&environments.program_runtime_v2,
|
||||
&loaded_programs_cache.environments.program_runtime_v2,
|
||||
) {
|
||||
// There can be two entries per program when the environment changes.
|
||||
// One for the old environment before the epoch boundary and one for the new environment after the epoch boundary.
|
||||
// These two entries have the same deployment slot, so they must differ in their effective slot instead.
|
||||
// This is done by setting the effective slot of the entry for the new environment to the epoch boundary.
|
||||
loaded_program.effective_slot = loaded_program
|
||||
.effective_slot
|
||||
.max(self.epoch_schedule.get_first_slot_in_epoch(effective_epoch));
|
||||
}
|
||||
if let Some(recompile) = recompile {
|
||||
loaded_program.tx_usage_counter =
|
||||
AtomicU64::new(recompile.tx_usage_counter.load(Ordering::Relaxed));
|
||||
loaded_program.ix_usage_counter =
|
||||
|
|
Loading…
Reference in New Issue