use {
agave_feature_set as feature_set,
solana_account::{
Account, AccountSharedData, WritableAccount, state_traits::StateMutWincode as _,
},
solana_feature_gate_interface::{self as feature, Feature},
solana_instruction::Instruction,
solana_instruction_error::InstructionError,
solana_keypair::Keypair,
solana_leader_schedule::SlotLeader,
solana_loader_v3_interface::state::UpgradeableLoaderState,
solana_message::Message,
solana_native_token::LAMPORTS_PER_SOL,
solana_rent::Rent,
solana_runtime::{
bank::{Bank, test_utils::goto_end_of_slot},
loader_utils::{create_buffer_with_elf, create_program_with_elf},
program_cache_utils::{EntryType, ExpectedEntry, assert_cache_contents, current_env},
},
solana_sdk_ids::{bpf_loader, bpf_loader_upgradeable},
solana_signer::Signer,
solana_transaction::Transaction,
solana_transaction_error::TransactionError,
std::{fs::File, io::Read, sync::Arc},
};
#[test]
fn test_bank_load_program() {
agave_logger::setup();
let (genesis_config, mint_keypair) =
solana_genesis_config::create_genesis_config(1_000_000_000);
let bank = Bank::new_for_tests(&genesis_config);
let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests();
goto_end_of_slot(bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(&bank_forks, bank, SlotLeader::default(), 42);
let bank = Bank::new_from_parent_with_bank_forks(&bank_forks, bank, SlotLeader::default(), 50);
let program_key = solana_pubkey::new_rand();
let programdata_key = solana_pubkey::new_rand();
let mut file = File::open("../programs/bpf_loader/test_elfs/out/noop_aligned.so").unwrap();
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
let mut program_account = AccountSharedData::new_data(
40,
&UpgradeableLoaderState::Program {
programdata_address: programdata_key,
},
&bpf_loader_upgradeable::id(),
)
.unwrap();
program_account.set_executable(true);
program_account.set_rent_epoch(1);
let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata();
let mut programdata_account = AccountSharedData::new(
40,
programdata_data_offset + elf.len(),
&bpf_loader_upgradeable::id(),
);
programdata_account
.set_state(&UpgradeableLoaderState::ProgramData {
slot: 42,
upgrade_authority_address: None,
})
.unwrap();
programdata_account.data_as_mut_slice()[programdata_data_offset..].copy_from_slice(&elf);
programdata_account.set_rent_epoch(1);
bank.store_account(&program_key, &program_account);
bank.store_account(&programdata_key, &programdata_account);
let instruction = Instruction::new_with_bytes(program_key, &[], Vec::new());
let invocation_message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
let binding = mint_keypair.insecure_clone();
let transaction = Transaction::new(&[&binding], invocation_message, bank.last_blockhash());
assert!(bank.process_transaction(&transaction).is_ok());
assert_cache_contents(
&bank,
&program_key,
&[ExpectedEntry {
entry_type: EntryType::Loaded,
deployment_slot: 42,
environment: ¤t_env(&bank),
}],
);
}
#[test]
fn test_feature_activation_loaded_programs_cache_preparation_phase() {
agave_logger::setup();
let (mut genesis_config, mint_keypair) =
solana_genesis_config::create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
genesis_config
.accounts
.remove(&feature_set::disable_sbpf_v0_execution::id());
genesis_config
.accounts
.remove(&feature_set::reenable_sbpf_v0_execution::id());
let (root_bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
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);
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];
goto_end_of_slot(root_bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(
bank_forks.as_ref(),
root_bank.clone(),
SlotLeader::default(),
root_bank.slot() + 1,
);
let transaction = Transaction::new(&signers, message.clone(), bank.last_blockhash());
let result_without_feature_enabled = bank.process_transaction(&transaction);
assert_eq!(result_without_feature_enabled, Ok(()));
let feature_account_balance =
std::cmp::max(genesis_config.rent.minimum_balance(Feature::size_of()), 1);
bank.store_account(
&feature_set::disable_sbpf_v0_execution::id(),
&feature::create_account(&Feature { activated_at: None }, feature_account_balance),
);
goto_end_of_slot(bank.clone());
assert_cache_contents(
&bank,
&program_keypair.pubkey(),
&[ExpectedEntry {
entry_type: EntryType::Loaded,
deployment_slot: 0,
environment: ¤t_env(&bank),
}],
);
let bank = Bank::new_from_parent_with_bank_forks(&bank_forks, bank, SlotLeader::default(), 16);
let current_env = bank
.get_transaction_processor()
.program_runtime_environment_for_epoch(0);
let upcoming_env = bank
.get_transaction_processor()
.program_runtime_environment_for_epoch(1);
let ebpp_env = bank
.get_transaction_processor()
.epoch_boundary_preparation
.read()
.unwrap()
.upcoming_environment
.clone()
.unwrap();
assert!(*upcoming_env == *ebpp_env);
assert_eq!(upcoming_env, ebpp_env); assert_cache_contents(
&bank,
&program_keypair.pubkey(),
&[
ExpectedEntry {
entry_type: EntryType::Loaded,
deployment_slot: 0,
environment: ¤t_env,
},
ExpectedEntry {
entry_type: EntryType::FailedVerification,
deployment_slot: 0,
environment: &upcoming_env,
},
],
);
goto_end_of_slot(bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(&bank_forks, bank, SlotLeader::default(), 33);
let new_processor_env = bank
.get_transaction_processor()
.program_runtime_environment
.clone();
assert!(*upcoming_env == *new_processor_env);
assert_eq!(upcoming_env, new_processor_env);
let computed_env = bank.create_program_runtime_environment(&bank.feature_set);
assert!(*computed_env == *new_processor_env);
assert_ne!(computed_env, new_processor_env);
assert_ne!(computed_env, upcoming_env);
let transaction = Transaction::new(&signers, message, bank.last_blockhash());
let result_with_feature_enabled = bank.process_transaction(&transaction);
assert_eq!(
result_with_feature_enabled,
Err(TransactionError::InstructionError(
0,
InstructionError::UnsupportedProgramId
))
);
}
#[test]
fn test_feature_activation_loaded_programs_epoch_transition() {
agave_logger::setup();
let (mut genesis_config, mint_keypair) =
solana_genesis_config::create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
genesis_config
.accounts
.remove(&feature_set::disable_fees_sysvar::id());
genesis_config
.accounts
.remove(&feature_set::reenable_sbpf_v0_execution::id());
let (root_bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
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);
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];
goto_end_of_slot(root_bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(
bank_forks.as_ref(),
root_bank.clone(),
SlotLeader::default(),
root_bank.slot() + 1,
);
let transaction = Transaction::new(&signers, message.clone(), bank.last_blockhash());
assert!(bank.process_transaction(&transaction).is_ok());
let feature_account_balance =
std::cmp::max(genesis_config.rent.minimum_balance(Feature::size_of()), 1);
bank.store_account(
&feature_set::disable_fees_sysvar::id(),
&feature::create_account(&Feature { activated_at: None }, feature_account_balance),
);
goto_end_of_slot(bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(&bank_forks, bank, SlotLeader::default(), 31);
let current_env = bank
.get_transaction_processor()
.program_runtime_environment_for_epoch(0);
let upcoming_env = bank
.get_transaction_processor()
.program_runtime_environment_for_epoch(1);
let ebpp_env = bank
.get_transaction_processor()
.epoch_boundary_preparation
.read()
.unwrap()
.upcoming_environment
.clone()
.unwrap();
assert!(*current_env != *upcoming_env);
assert!(*upcoming_env == *ebpp_env);
assert_eq!(upcoming_env, ebpp_env);
let guard_env = bank
.get_transaction_processor()
.deployment_env_override()
.unwrap()
.clone();
assert!(*guard_env == *ebpp_env);
assert_eq!(guard_env, ebpp_env);
goto_end_of_slot(bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(&bank_forks, bank, SlotLeader::default(), 32);
let new_processor_env = bank
.get_transaction_processor()
.program_runtime_environment
.clone();
assert!(*upcoming_env == *new_processor_env);
assert_eq!(upcoming_env, new_processor_env);
let computed_env = bank.create_program_runtime_environment(&bank.feature_set);
assert!(*computed_env == *new_processor_env);
assert_ne!(computed_env, new_processor_env);
assert_ne!(computed_env, upcoming_env);
assert!(
bank.get_transaction_processor()
.deployment_env_override()
.is_none()
);
assert!(*bank.get_transaction_processor().program_runtime_environment == *guard_env);
let transaction = Transaction::new(&signers, message.clone(), bank.last_blockhash());
assert!(bank.process_transaction(&transaction).is_ok());
{
let upcoming_environment = bank
.get_transaction_processor()
.epoch_boundary_preparation
.write()
.unwrap()
.reroot(bank.epoch());
assert!(upcoming_environment.is_some());
let mut program_cache = bank
.get_transaction_processor()
.global_program_cache
.write()
.unwrap();
program_cache.prune(
bank.slot(),
upcoming_environment,
&bank_forks.read().unwrap(),
);
program_cache.sort_and_unload(0);
}
goto_end_of_slot(bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(
bank_forks.as_ref(),
bank.clone(),
SlotLeader::default(),
bank.slot() + 1,
);
let transaction = Transaction::new(&signers, message, bank.last_blockhash());
assert!(bank.process_transaction(&transaction).is_ok());
}
#[test]
fn test_feature_activation_loaded_programs_late_activation() {
agave_logger::setup();
let (mut genesis_config, mint_keypair) =
solana_genesis_config::create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
genesis_config
.accounts
.remove(&feature_set::disable_sbpf_v0_execution::id());
genesis_config
.accounts
.remove(&feature_set::reenable_sbpf_v0_execution::id());
let (root_bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
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);
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];
goto_end_of_slot(root_bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(
bank_forks.as_ref(),
root_bank.clone(),
SlotLeader::default(),
root_bank.slot() + 1,
);
let transaction = Transaction::new(&signers, message.clone(), bank.last_blockhash());
let result_without_feature_enabled = bank.process_transaction(&transaction);
assert_eq!(result_without_feature_enabled, Ok(()));
goto_end_of_slot(bank.clone());
assert_cache_contents(
&bank,
&program_keypair.pubkey(),
&[ExpectedEntry {
entry_type: EntryType::Loaded,
deployment_slot: 0,
environment: ¤t_env(&bank),
}],
);
let bank = Bank::new_from_parent_with_bank_forks(&bank_forks, bank, SlotLeader::default(), 16);
let current_env = bank
.get_transaction_processor()
.program_runtime_environment_for_epoch(0);
let upcoming_env = bank
.get_transaction_processor()
.program_runtime_environment_for_epoch(1);
let ebpp = bank
.get_transaction_processor()
.epoch_boundary_preparation
.read()
.unwrap();
assert!(*current_env == *upcoming_env);
assert_eq!(current_env, upcoming_env);
assert_cache_contents(
&bank,
&program_keypair.pubkey(),
&[ExpectedEntry {
entry_type: EntryType::Loaded,
deployment_slot: 0,
environment: ¤t_env,
}],
);
assert!(ebpp.upcoming_environment.is_none());
assert!(ebpp.programs_to_recompile.is_empty());
drop(ebpp);
goto_end_of_slot(bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(&bank_forks, bank, SlotLeader::default(), 24);
let feature_account_balance =
std::cmp::max(genesis_config.rent.minimum_balance(Feature::size_of()), 1);
bank.store_account(
&feature_set::disable_sbpf_v0_execution::id(),
&feature::create_account(&Feature { activated_at: None }, feature_account_balance),
);
goto_end_of_slot(bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(&bank_forks, bank, SlotLeader::default(), 30);
let current_env = bank
.get_transaction_processor()
.program_runtime_environment_for_epoch(0);
let upcoming_env = bank
.get_transaction_processor()
.program_runtime_environment_for_epoch(1);
let ebpp_env = bank
.get_transaction_processor()
.epoch_boundary_preparation
.read()
.unwrap()
.upcoming_environment
.clone()
.unwrap();
assert!(*current_env != *upcoming_env);
assert_ne!(current_env, upcoming_env);
assert!(*ebpp_env == *upcoming_env);
assert_eq!(ebpp_env, upcoming_env);
assert_cache_contents(
&bank,
&program_keypair.pubkey(),
&[
ExpectedEntry {
entry_type: EntryType::Loaded,
deployment_slot: 0,
environment: ¤t_env,
},
ExpectedEntry {
entry_type: EntryType::FailedVerification,
deployment_slot: 0,
environment: &upcoming_env,
},
],
);
goto_end_of_slot(bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(&bank_forks, bank, SlotLeader::default(), 33);
let computed_env = bank.create_program_runtime_environment(&bank.feature_set);
let new_processor_env = bank
.get_transaction_processor()
.program_runtime_environment
.clone();
assert!(*new_processor_env == *computed_env);
assert!(*new_processor_env == *upcoming_env);
assert_eq!(new_processor_env, upcoming_env);
assert_cache_contents(
&bank,
&program_keypair.pubkey(),
&[
ExpectedEntry {
entry_type: EntryType::Loaded,
deployment_slot: 0,
environment: ¤t_env,
},
ExpectedEntry {
entry_type: EntryType::FailedVerification,
deployment_slot: 0,
environment: &new_processor_env,
},
],
);
}
#[test]
fn test_feature_activation_loaded_programs_fork_without_activation() {
agave_logger::setup();
let (mut genesis_config, mint_keypair) =
solana_genesis_config::create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
genesis_config
.accounts
.remove(&feature_set::disable_sbpf_v0_execution::id());
genesis_config
.accounts
.remove(&feature_set::reenable_sbpf_v0_execution::id());
let (root_bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
let program_data = include_bytes!("../../programs/bpf_loader/test_elfs/out/noop_aligned.so");
let program_keypairs = [Keypair::new(), Keypair::new(), Keypair::new()];
for program_keypair in &program_keypairs {
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);
}
goto_end_of_slot(root_bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(
bank_forks.as_ref(),
root_bank.clone(),
SlotLeader::default(),
root_bank.slot() + 1,
);
for program_keypair in &program_keypairs {
let instruction = Instruction::new_with_bytes(program_keypair.pubkey(), &[], Vec::new());
let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
let transaction = Transaction::new(&[&mint_keypair], message, bank.last_blockhash());
assert_eq!(bank.process_transaction(&transaction), Ok(()));
}
let old_env = current_env(&bank);
goto_end_of_slot(bank.clone());
let fork_point =
Bank::new_from_parent_with_bank_forks(&bank_forks, bank, SlotLeader::default(), 10);
goto_end_of_slot(fork_point.clone());
let with_feature = Bank::new_from_parent_with_bank_forks(
&bank_forks,
fork_point.clone(),
SlotLeader::default(),
11,
);
let feature_account_balance =
std::cmp::max(genesis_config.rent.minimum_balance(Feature::size_of()), 1);
with_feature.store_account(
&feature_set::disable_sbpf_v0_execution::id(),
&feature::create_account(&Feature { activated_at: None }, feature_account_balance),
);
let without_feature =
Bank::new_from_parent_with_bank_forks(&bank_forks, fork_point, SlotLeader::default(), 16);
{
let current_env = without_feature
.get_transaction_processor()
.program_runtime_environment_for_epoch(0);
let upcoming_env = without_feature
.get_transaction_processor()
.program_runtime_environment_for_epoch(1);
let ebpp = without_feature
.get_transaction_processor()
.epoch_boundary_preparation
.read()
.unwrap();
assert!(*current_env == *upcoming_env);
assert_eq!(current_env, upcoming_env);
assert!(ebpp.upcoming_environment.is_none());
assert!(ebpp.programs_to_recompile.is_empty());
}
goto_end_of_slot(with_feature.clone());
let with_feature =
Bank::new_from_parent_with_bank_forks(&bank_forks, with_feature, SlotLeader::default(), 17);
let ebpp_latch_env = {
let current_env = without_feature
.get_transaction_processor()
.program_runtime_environment_for_epoch(0);
let upcoming_env = without_feature
.get_transaction_processor()
.program_runtime_environment_for_epoch(1);
let ebpp = without_feature
.get_transaction_processor()
.epoch_boundary_preparation
.read()
.unwrap();
let ebpp_env = ebpp.upcoming_environment.clone().unwrap();
assert!(*current_env != *upcoming_env);
assert_ne!(current_env, upcoming_env);
assert!(*ebpp_env == *upcoming_env);
assert_eq!(ebpp_env, upcoming_env);
assert_eq!(ebpp.programs_to_recompile.len(), program_keypairs.len() - 1);
ebpp_env
};
let without_feature = Bank::new_from_parent_with_bank_forks(
&bank_forks,
without_feature,
SlotLeader::default(),
18,
);
let non_recompiled_programs = {
let current_env = without_feature
.get_transaction_processor()
.program_runtime_environment_for_epoch(0);
let upcoming_env = without_feature
.get_transaction_processor()
.program_runtime_environment_for_epoch(1);
assert!(*current_env != *upcoming_env);
assert_ne!(current_env, upcoming_env);
let computed_env =
without_feature.create_program_runtime_environment(&without_feature.feature_set);
assert!(*current_env == *computed_env);
let ebpp = without_feature
.get_transaction_processor()
.epoch_boundary_preparation
.read()
.unwrap();
let ebpp_env = ebpp.upcoming_environment.clone().unwrap();
assert!(*ebpp_env == *upcoming_env);
assert_eq!(ebpp_env, upcoming_env);
assert_eq!(ebpp_env, ebpp_latch_env);
assert!(*ebpp_env != *computed_env);
assert_eq!(ebpp.programs_to_recompile.len(), program_keypairs.len() - 2);
ebpp.programs_to_recompile.clone()
};
goto_end_of_slot(with_feature.clone());
let with_feature =
Bank::new_from_parent_with_bank_forks(&bank_forks, with_feature, SlotLeader::default(), 33);
let computed_env = with_feature.create_program_runtime_environment(&with_feature.feature_set);
let new_processor_env = with_feature
.get_transaction_processor()
.program_runtime_environment
.clone();
assert!(*new_processor_env == *computed_env);
assert!(*new_processor_env == *ebpp_latch_env);
assert_eq!(new_processor_env, ebpp_latch_env);
for program_id in program_keypairs.iter().filter_map(|program_keypair| {
let program_id = program_keypair.pubkey();
non_recompiled_programs
.iter()
.all(|(key, _)| *key != program_id)
.then_some(program_id)
}) {
assert_cache_contents(
&with_feature,
&program_id,
&[
ExpectedEntry {
entry_type: EntryType::Loaded,
deployment_slot: 0,
environment: &old_env,
},
ExpectedEntry {
entry_type: EntryType::FailedVerification,
deployment_slot: 0,
environment: &new_processor_env,
},
],
);
}
}
#[test]
fn test_sbpf_v0_deploy_in_last_slot_before_feature_activation() {
let (mut genesis_config, mint_keypair) =
solana_genesis_config::create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
for feature_id in [
feature_set::disable_sbpf_v0_execution::id(),
feature_set::reenable_sbpf_v0_execution::id(),
feature_set::disable_sbpf_v0_v1_v2_deployment::id(),
] {
genesis_config.accounts.remove(&feature_id);
}
let (root_bank, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
let elf = include_bytes!("../../programs/bpf_loader/test_elfs/out/noop_aligned.so");
let authority_keypair = Keypair::new();
let payer = mint_keypair.insecure_clone();
let program_id = create_program_with_elf(
&root_bank,
&bpf_loader_upgradeable::id(),
&authority_keypair.pubkey(),
elf,
);
goto_end_of_slot(root_bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(
bank_forks.as_ref(),
root_bank.clone(),
SlotLeader::default(),
root_bank.slot() + 1,
);
let instruction = Instruction::new_with_bytes(program_id, &[], Vec::new());
let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
let transaction = Transaction::new(&[&mint_keypair], message, bank.last_blockhash());
assert_eq!(bank.process_transaction(&transaction), Ok(()));
let upgrade_with_sbpf_v0_elf = |bank: &Arc<Bank>| {
let buffer_address = create_buffer_with_elf(bank, &authority_keypair.pubkey(), elf);
let message = Message::new(
&[solana_loader_v3_interface::instruction::upgrade(
&program_id,
&buffer_address,
&authority_keypair.pubkey(),
&payer.pubkey(),
)],
Some(&payer.pubkey()),
);
bank.process_transaction(&Transaction::new(
&[&payer, &authority_keypair],
message,
bank.last_blockhash(),
))
};
goto_end_of_slot(bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(&bank_forks, bank, SlotLeader::default(), 12);
let feature_account_balance =
std::cmp::max(genesis_config.rent.minimum_balance(Feature::size_of()), 1);
bank.store_account(
&feature_set::disable_sbpf_v0_execution::id(),
&feature::create_account(&Feature { activated_at: None }, feature_account_balance),
);
goto_end_of_slot(bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(&bank_forks, bank, SlotLeader::default(), 24);
let current_env = bank
.get_transaction_processor()
.program_runtime_environment_for_epoch(0);
let upcoming_env = bank
.get_transaction_processor()
.program_runtime_environment_for_epoch(1);
let (ebpp_env, queued) = {
let epoch_boundary_preparation = bank
.get_transaction_processor()
.epoch_boundary_preparation
.read()
.unwrap();
(
epoch_boundary_preparation
.upcoming_environment
.clone()
.unwrap(),
epoch_boundary_preparation.programs_to_recompile.len(),
)
};
assert!(*current_env != *upcoming_env);
assert!(*upcoming_env == *ebpp_env);
assert_eq!(upcoming_env, ebpp_env);
assert_eq!(queued, 0);
let last_slot_in_epoch = bank.epoch_schedule().get_last_slot_in_epoch(bank.epoch());
goto_end_of_slot(bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(
&bank_forks,
bank,
SlotLeader::default(),
last_slot_in_epoch,
);
let deployment_env = bank
.get_transaction_processor()
.deployment_env_override()
.unwrap()
.clone();
assert!(*deployment_env != *current_env);
assert!(*deployment_env == *ebpp_env);
assert_eq!(deployment_env, ebpp_env);
assert_eq!(
upgrade_with_sbpf_v0_elf(&bank),
Err(TransactionError::InstructionError(
0,
InstructionError::InvalidAccountData
))
);
goto_end_of_slot(bank.clone());
let bank = Bank::new_from_parent_with_bank_forks(
bank_forks.as_ref(),
bank.clone(),
SlotLeader::default(),
bank.slot() + 1,
);
assert_eq!(bank.epoch(), 1);
assert!(
bank.get_transaction_processor()
.deployment_env_override()
.is_none()
);
assert!(*bank.get_transaction_processor().program_runtime_environment == *deployment_env);
assert_eq!(
upgrade_with_sbpf_v0_elf(&bank),
Err(TransactionError::InstructionError(
0,
InstructionError::InvalidAccountData
))
);
}