Skip to content

Commit

Permalink
Add test for executor cache runtime feature upgrades. (#30382)
Browse files Browse the repository at this point in the history
Co-authored-by: K-anon <[email protected]>
  • Loading branch information
IntokuSatori and IntokuSatori authored Feb 21, 2023
1 parent 568a4b1 commit c5a24e1
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 1 deletion.
Binary file not shown.
Binary file not shown.
92 changes: 91 additions & 1 deletion runtime/src/bank/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use {
rand::Rng,
rayon::ThreadPoolBuilder,
serde::{Deserialize, Serialize},
solana_bpf_loader_program::solana_bpf_loader_program,
solana_logger,
solana_program_runtime::{
compute_budget::{self, ComputeBudget, MAX_COMPUTE_UNIT_LIMIT},
Expand All @@ -56,7 +57,7 @@ use {
entrypoint::MAX_PERMITTED_DATA_INCREASE,
epoch_schedule::{EpochSchedule, MINIMUM_SLOTS_PER_EPOCH},
feature::{self, Feature},
feature_set::{self, FeatureSet},
feature_set::{self, reject_callx_r10, FeatureSet},
fee::FeeStructure,
fee_calculator::FeeRateGovernor,
genesis_config::{create_genesis_config, ClusterType, GenesisConfig},
Expand Down Expand Up @@ -12690,3 +12691,92 @@ fn test_calculate_fee_with_request_heap_frame_flag() {
signature_fee
);
}

#[test]
fn test_runtime_feature_enable_with_executor_cache() {
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 mut root_bank = Bank::new_for_tests(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
root_bank.add_builtin(&name, &id, entrypoint);

// Test a basic transfer
let amount = genesis_config.rent.minimum_balance(0);
let pubkey = solana_sdk::pubkey::new_rand();
root_bank.transfer(amount, &mint_keypair, &pubkey).unwrap();
assert_eq!(root_bank.get_balance(&pubkey), amount);

// Program Setup
let program_keypair = Keypair::new();
let program_data =
include_bytes!("../../../programs/bpf_loader/test_elfs/out/callx-r10-sbfv1.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 instruction using the desired program
let instruction1 = Instruction::new_with_bytes(program_keypair.pubkey(), &[], Vec::new());
let message1 = Message::new(&[instruction1], Some(&mint_keypair.pubkey()));
let binding1 = mint_keypair.insecure_clone();
let signers1 = vec![&binding1];
let transaction1 = Transaction::new(&signers1, message1, root_bank.last_blockhash());

// Advance the bank so the next transaction can be submitted.
goto_end_of_slot(&mut root_bank);
let mut bank = new_from_parent(&Arc::new(root_bank));

// Compose second instruction using the same program with a different block hash
let instruction2 = Instruction::new_with_bytes(program_keypair.pubkey(), &[], Vec::new());
let message2 = Message::new(&[instruction2], Some(&mint_keypair.pubkey()));
let binding2 = mint_keypair.insecure_clone();
let signers2 = vec![&binding2];
let transaction2 = Transaction::new(&signers2, message2, bank.last_blockhash());

// Execute before feature is enabled to get program into the cache.
let result_without_feature_enabled = bank.process_transaction(&transaction1);
// Should fail when executing here
assert_eq!(
result_without_feature_enabled,
Err(TransactionError::InstructionError(
0,
InstructionError::ProgramFailedToComplete
))
);

// Activate feature
bank.activate_feature(&reject_callx_r10::id());

// Execute after feature is enabled
let result_with_feature_enabled = bank.process_transaction(&transaction2);
// Feature should have been activated thus causing the TX to fail at
// verification. It should fail here with new Executor Cache because feature
// activations should force the program to recompile with the new feature,
// and in this case the feature should cause the TX to fail at verification.
// Note: `ProgramFailedToComplete` error appearing again means the account
// was not recompiled by the cache upon feature activation and thus fails in
// the same way.
match &result_with_feature_enabled {
Err(x) => {
if *x
== TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete)
{
println!("ERROR: Program was not recompiled after runtime feature was enabled.");
}
}
Ok(_) => println!("ERROR: Program should fail during execution."),
}
// assert_eq!(
// result_with_feature_enabled,
// Err(TransactionError::InstructionError(0, InstructionError::InvalidAccountData))
// );
}

0 comments on commit c5a24e1

Please sign in to comment.