solana/programs/bpf/tests/programs.rs
Dmitri Makarov 2fe211c5e0
Remove O2 option for compiling BPF programs (#17865)
rustc default compiler optimization level is O3. This change removes
the option that overrides the default optimization level, because it
is safe to do so.  The code generation is incorrect in some cases
because of link-time optimizations, which remain disabled for
compiling BPF programs.  In addition, this commit updates the expected
instruction counts for assert_instruction_count test.
2021-06-10 05:04:17 +00:00

2548 lines
85 KiB
Rust

#![cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
#[macro_use]
extern crate solana_bpf_loader_program;
use itertools::izip;
use log::{log_enabled, trace, Level::Trace};
use solana_account_decoder::parse_bpf_loader::{
parse_bpf_upgradeable_loader, BpfUpgradeableLoaderAccountType,
};
use solana_bpf_loader_program::{
create_vm,
serialization::{deserialize_parameters, serialize_parameters},
syscalls::register_syscalls,
BpfError, ThisInstructionMeter,
};
use solana_cli_output::display::println_transaction;
use solana_rbpf::{
static_analysis::Analysis,
vm::{Config, Executable, Tracer},
};
use solana_runtime::{
bank::{Bank, ExecuteTimings, NonceRollbackInfo, TransactionBalancesSet, TransactionResults},
bank_client::BankClient,
genesis_utils::{create_genesis_config, GenesisConfigInfo},
loader_utils::{
load_buffer_account, load_program, load_upgradeable_program, set_upgrade_authority,
upgrade_program,
},
};
use solana_sdk::{
account::{AccountSharedData, ReadableAccount},
account_utils::StateMut,
bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable,
client::SyncClient,
clock::MAX_PROCESSING_AGE,
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
keyed_account::KeyedAccount,
message::Message,
process_instruction::{InvokeContext, MockInvokeContext},
pubkey::Pubkey,
signature::{keypair_from_seed, Keypair, Signer},
system_instruction,
sysvar::{clock, fees, rent},
transaction::{Transaction, TransactionError},
};
use solana_transaction_status::{
token_balances::collect_token_balances, ConfirmedTransaction, InnerInstructions,
TransactionStatusMeta, TransactionWithStatusMeta, UiTransactionEncoding,
};
use std::{
cell::RefCell, collections::HashMap, env, fs::File, io::Read, path::PathBuf, str::FromStr,
sync::Arc,
};
/// BPF program file extension
const PLATFORM_FILE_EXTENSION_BPF: &str = "so";
/// Create a BPF program file name
fn create_bpf_path(name: &str) -> PathBuf {
let mut pathbuf = {
let current_exe = env::current_exe().unwrap();
PathBuf::from(current_exe.parent().unwrap().parent().unwrap())
};
pathbuf.push("bpf/");
pathbuf.push(name);
pathbuf.set_extension(PLATFORM_FILE_EXTENSION_BPF);
pathbuf
}
fn load_bpf_program(
bank_client: &BankClient,
loader_id: &Pubkey,
payer_keypair: &Keypair,
name: &str,
) -> Pubkey {
let elf = read_bpf_program(name);
load_program(bank_client, payer_keypair, loader_id, elf)
}
fn read_bpf_program(name: &str) -> Vec<u8> {
let path = create_bpf_path(name);
let mut file = File::open(&path).unwrap_or_else(|err| {
panic!("Failed to open {}: {}", path.display(), err);
});
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
elf
}
#[cfg(feature = "bpf_rust")]
fn write_bpf_program(
bank_client: &BankClient,
loader_id: &Pubkey,
payer_keypair: &Keypair,
program_keypair: &Keypair,
elf: &[u8],
) {
use solana_sdk::loader_instruction;
let chunk_size = 256; // Size of chunk just needs to fit into tx
let mut offset = 0;
for chunk in elf.chunks(chunk_size) {
let instruction =
loader_instruction::write(&program_keypair.pubkey(), loader_id, offset, chunk.to_vec());
let message = Message::new(&[instruction], Some(&payer_keypair.pubkey()));
bank_client
.send_and_confirm_message(&[payer_keypair, &program_keypair], message)
.unwrap();
offset += chunk_size as u32;
}
}
fn load_upgradeable_bpf_program(
bank_client: &BankClient,
payer_keypair: &Keypair,
buffer_keypair: &Keypair,
executable_keypair: &Keypair,
authority_keypair: &Keypair,
name: &str,
) {
let path = create_bpf_path(name);
let mut file = File::open(&path).unwrap_or_else(|err| {
panic!("Failed to open {}: {}", path.display(), err);
});
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
load_upgradeable_program(
bank_client,
payer_keypair,
buffer_keypair,
executable_keypair,
authority_keypair,
elf,
);
}
fn load_upgradeable_buffer(
bank_client: &BankClient,
payer_keypair: &Keypair,
buffer_keypair: &Keypair,
buffer_authority_keypair: &Keypair,
name: &str,
) {
let path = create_bpf_path(name);
let mut file = File::open(&path).unwrap_or_else(|err| {
panic!("Failed to open {}: {}", path.display(), err);
});
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
load_buffer_account(
bank_client,
payer_keypair,
&buffer_keypair,
buffer_authority_keypair,
&elf,
);
}
fn upgrade_bpf_program(
bank_client: &BankClient,
payer_keypair: &Keypair,
buffer_keypair: &Keypair,
executable_pubkey: &Pubkey,
authority_keypair: &Keypair,
name: &str,
) {
load_upgradeable_buffer(
bank_client,
payer_keypair,
buffer_keypair,
authority_keypair,
name,
);
upgrade_program(
bank_client,
payer_keypair,
executable_pubkey,
&buffer_keypair.pubkey(),
&authority_keypair,
&payer_keypair.pubkey(),
);
}
fn run_program(
name: &str,
program_id: &Pubkey,
parameter_accounts: Vec<KeyedAccount>,
instruction_data: &[u8],
) -> Result<u64, InstructionError> {
let path = create_bpf_path(name);
let mut file = File::open(path).unwrap();
let mut data = vec![];
file.read_to_end(&mut data).unwrap();
let loader_id = bpf_loader::id();
let parameter_bytes = serialize_parameters(
&bpf_loader::id(),
program_id,
&parameter_accounts,
&instruction_data,
)
.unwrap();
let mut invoke_context = MockInvokeContext::new(parameter_accounts);
let compute_meter = invoke_context.get_compute_meter();
let mut instruction_meter = ThisInstructionMeter { compute_meter };
let config = Config {
max_call_depth: 20,
stack_frame_size: 4096,
enable_instruction_meter: true,
enable_instruction_tracing: true,
};
let mut executable =
<dyn Executable<BpfError, ThisInstructionMeter>>::from_elf(&data, None, config).unwrap();
executable.set_syscall_registry(register_syscalls(&mut invoke_context).unwrap());
executable.jit_compile().unwrap();
let mut instruction_count = 0;
let mut tracer = None;
for i in 0..2 {
let mut parameter_bytes = parameter_bytes.clone();
{
let mut vm = create_vm(
&loader_id,
executable.as_ref(),
parameter_bytes.as_slice_mut(),
&mut invoke_context,
)
.unwrap();
let result = if i == 0 {
vm.execute_program_interpreted(&mut instruction_meter)
} else {
vm.execute_program_jit(&mut instruction_meter)
};
assert_eq!(SUCCESS, result.unwrap());
if i == 1 {
assert_eq!(instruction_count, vm.get_total_instruction_count());
}
instruction_count = vm.get_total_instruction_count();
if config.enable_instruction_tracing {
if i == 1 {
if !Tracer::compare(tracer.as_ref().unwrap(), vm.get_tracer()) {
let analysis = Analysis::from_executable(executable.as_ref());
let stdout = std::io::stdout();
println!("TRACE (interpreted):");
tracer
.as_ref()
.unwrap()
.write(&mut stdout.lock(), &analysis)
.unwrap();
println!("TRACE (jit):");
vm.get_tracer()
.write(&mut stdout.lock(), &analysis)
.unwrap();
assert!(false);
} else if log_enabled!(Trace) {
let analysis = Analysis::from_executable(executable.as_ref());
let mut trace_buffer = Vec::<u8>::new();
tracer
.as_ref()
.unwrap()
.write(&mut trace_buffer, &analysis)
.unwrap();
let trace_string = String::from_utf8(trace_buffer).unwrap();
trace!("BPF Program Instruction Trace:\n{}", trace_string);
}
}
tracer = Some(vm.get_tracer().clone());
}
}
let parameter_accounts = invoke_context.get_keyed_accounts().unwrap();
deserialize_parameters(
&bpf_loader::id(),
parameter_accounts,
parameter_bytes.as_slice(),
)
.unwrap();
}
Ok(instruction_count)
}
fn process_transaction_and_record_inner(
bank: &Bank,
tx: Transaction,
) -> (Result<(), TransactionError>, Vec<Vec<CompiledInstruction>>) {
let signature = tx.signatures.get(0).unwrap().clone();
let txs = vec![tx];
let tx_batch = bank.prepare_batch(txs.iter());
let (mut results, _, mut inner, _transaction_logs) = bank.load_execute_and_commit_transactions(
&tx_batch,
MAX_PROCESSING_AGE,
false,
true,
false,
&mut ExecuteTimings::default(),
);
let inner_instructions = if inner.is_empty() {
Some(vec![vec![]])
} else {
inner.swap_remove(0)
};
let result = results
.fee_collection_results
.swap_remove(0)
.and_then(|_| bank.get_signature_status(&signature).unwrap());
(
result,
inner_instructions.expect("cpi recording should be enabled"),
)
}
fn execute_transactions(bank: &Bank, txs: &[Transaction]) -> Vec<ConfirmedTransaction> {
let batch = bank.prepare_batch(txs.iter());
let mut timings = ExecuteTimings::default();
let mut mint_decimals = HashMap::new();
let tx_pre_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals);
let (
TransactionResults {
execution_results, ..
},
TransactionBalancesSet {
pre_balances,
post_balances,
..
},
mut inner_instructions,
mut transaction_logs,
) = bank.load_execute_and_commit_transactions(
&batch,
std::usize::MAX,
true,
true,
true,
&mut timings,
);
let tx_post_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals);
for _ in 0..(txs.len() - transaction_logs.len()) {
transaction_logs.push(vec![]);
}
for _ in 0..(txs.len() - inner_instructions.len()) {
inner_instructions.push(None);
}
izip!(
txs.iter(),
execution_results.into_iter(),
inner_instructions.into_iter(),
pre_balances.into_iter(),
post_balances.into_iter(),
tx_pre_token_balances.into_iter(),
tx_post_token_balances.into_iter(),
transaction_logs.into_iter(),
)
.map(
|(
tx,
(execute_result, nonce_rollback),
inner_instructions,
pre_balances,
post_balances,
pre_token_balances,
post_token_balances,
log_messages,
)| {
let fee_calculator = nonce_rollback
.map(|nonce_rollback| nonce_rollback.fee_calculator())
.unwrap_or_else(|| bank.get_fee_calculator(&tx.message().recent_blockhash))
.expect("FeeCalculator must exist");
let fee = fee_calculator.calculate_fee(tx.message());
let inner_instructions = inner_instructions.map(|inner_instructions| {
inner_instructions
.into_iter()
.enumerate()
.map(|(index, instructions)| InnerInstructions {
index: index as u8,
instructions,
})
.filter(|i| !i.instructions.is_empty())
.collect()
});
let tx_status_meta = TransactionStatusMeta {
status: execute_result,
fee,
pre_balances,
post_balances,
pre_token_balances: Some(pre_token_balances),
post_token_balances: Some(post_token_balances),
inner_instructions,
log_messages: Some(log_messages),
rewards: None,
};
ConfirmedTransaction {
slot: bank.slot(),
transaction: TransactionWithStatusMeta {
transaction: tx.clone(),
meta: Some(tx_status_meta),
},
block_time: None,
}
},
)
.collect()
}
fn print_confirmed_tx(name: &str, confirmed_tx: ConfirmedTransaction) {
let block_time = confirmed_tx.block_time;
let tx = confirmed_tx.transaction.transaction.clone();
let encoded = confirmed_tx.encode(UiTransactionEncoding::JsonParsed);
println!("EXECUTE {} (slot {})", name, encoded.slot);
println_transaction(&tx, &encoded.transaction.meta, " ", None, block_time);
}
#[test]
#[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
fn test_program_bpf_sanity() {
solana_logger::setup();
let mut programs = Vec::new();
#[cfg(feature = "bpf_c")]
{
programs.extend_from_slice(&[
("alloc", true),
("bpf_to_bpf", true),
("float", true),
("multiple_static", true),
("noop", true),
("noop++", true),
("panic", false),
("relative_call", true),
("sanity", true),
("sanity++", true),
("sha", true),
("struct_pass", true),
("struct_ret", true),
]);
}
#[cfg(feature = "bpf_rust")]
{
programs.extend_from_slice(&[
("solana_bpf_rust_128bit", true),
("solana_bpf_rust_alloc", true),
("solana_bpf_rust_custom_heap", true),
("solana_bpf_rust_dep_crate", true),
("solana_bpf_rust_external_spend", false),
("solana_bpf_rust_iter", true),
("solana_bpf_rust_many_args", true),
("solana_bpf_rust_membuiltins", true),
("solana_bpf_rust_noop", true),
("solana_bpf_rust_panic", false),
("solana_bpf_rust_param_passing", true),
("solana_bpf_rust_rand", true),
("solana_bpf_rust_sanity", true),
("solana_bpf_rust_sha", true),
]);
}
for program in programs.iter() {
println!("Test program: {:?}", program.0);
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let bank_client = BankClient::new(bank);
// Call user program
let program_id =
load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program.0);
let account_metas = vec![
AccountMeta::new(mint_keypair.pubkey(), true),
AccountMeta::new(Keypair::new().pubkey(), false),
];
let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas);
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
if program.1 {
assert!(result.is_ok());
} else {
assert!(result.is_err());
}
}
}
#[test]
#[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
fn test_program_bpf_loader_deprecated() {
solana_logger::setup();
let mut programs = Vec::new();
#[cfg(feature = "bpf_c")]
{
programs.extend_from_slice(&[("deprecated_loader")]);
}
#[cfg(feature = "bpf_rust")]
{
programs.extend_from_slice(&[("solana_bpf_rust_deprecated_loader")]);
}
for program in programs.iter() {
println!("Test program: {:?}", program);
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_deprecated_program!();
bank.add_builtin(&name, id, entrypoint);
let bank_client = BankClient::new(bank);
let program_id = load_bpf_program(
&bank_client,
&bpf_loader_deprecated::id(),
&mint_keypair,
program,
);
let account_metas = vec![AccountMeta::new(mint_keypair.pubkey(), true)];
let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas);
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert!(result.is_ok());
}
}
#[test]
fn test_program_bpf_duplicate_accounts() {
solana_logger::setup();
let mut programs = Vec::new();
#[cfg(feature = "bpf_c")]
{
programs.extend_from_slice(&[("dup_accounts")]);
}
#[cfg(feature = "bpf_rust")]
{
programs.extend_from_slice(&[("solana_bpf_rust_dup_accounts")]);
}
for program in programs.iter() {
println!("Test program: {:?}", program);
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let bank = Arc::new(bank);
let bank_client = BankClient::new_shared(&bank);
let program_id = load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program);
let payee_account = AccountSharedData::new(10, 1, &program_id);
let payee_pubkey = solana_sdk::pubkey::new_rand();
bank.store_account(&payee_pubkey, &payee_account);
let account = AccountSharedData::new(10, 1, &program_id);
let pubkey = solana_sdk::pubkey::new_rand();
let account_metas = vec![
AccountMeta::new(mint_keypair.pubkey(), true),
AccountMeta::new(payee_pubkey, false),
AccountMeta::new(pubkey, false),
AccountMeta::new(pubkey, false),
];
bank.store_account(&pubkey, &account);
let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
assert!(result.is_ok());
assert_eq!(data[0], 1);
bank.store_account(&pubkey, &account);
let instruction = Instruction::new_with_bytes(program_id, &[2], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
assert!(result.is_ok());
assert_eq!(data[0], 2);
bank.store_account(&pubkey, &account);
let instruction = Instruction::new_with_bytes(program_id, &[3], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
let data = bank_client.get_account_data(&pubkey).unwrap().unwrap();
assert!(result.is_ok());
assert_eq!(data[0], 3);
bank.store_account(&pubkey, &account);
let instruction = Instruction::new_with_bytes(program_id, &[4], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
let lamports = bank_client.get_balance(&pubkey).unwrap();
assert!(result.is_ok());
assert_eq!(lamports, 11);
bank.store_account(&pubkey, &account);
let instruction = Instruction::new_with_bytes(program_id, &[5], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
let lamports = bank_client.get_balance(&pubkey).unwrap();
assert!(result.is_ok());
assert_eq!(lamports, 12);
bank.store_account(&pubkey, &account);
let instruction = Instruction::new_with_bytes(program_id, &[6], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
let lamports = bank_client.get_balance(&pubkey).unwrap();
assert!(result.is_ok());
assert_eq!(lamports, 13);
let keypair = Keypair::new();
let pubkey = keypair.pubkey();
let account_metas = vec![
AccountMeta::new(mint_keypair.pubkey(), true),
AccountMeta::new(payee_pubkey, false),
AccountMeta::new(pubkey, false),
AccountMeta::new_readonly(pubkey, true),
AccountMeta::new_readonly(program_id, false),
];
bank.store_account(&pubkey, &account);
let instruction = Instruction::new_with_bytes(program_id, &[7], account_metas.clone());
let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
let result = bank_client.send_and_confirm_message(&[&mint_keypair, &keypair], message);
assert!(result.is_ok());
}
}
#[test]
fn test_program_bpf_error_handling() {
solana_logger::setup();
let mut programs = Vec::new();
#[cfg(feature = "bpf_c")]
{
programs.extend_from_slice(&[("error_handling")]);
}
#[cfg(feature = "bpf_rust")]
{
programs.extend_from_slice(&[("solana_bpf_rust_error_handling")]);
}
for program in programs.iter() {
println!("Test program: {:?}", program);
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let bank_client = BankClient::new(bank);
let program_id = load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program);
let account_metas = vec![AccountMeta::new(mint_keypair.pubkey(), true)];
let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert!(result.is_ok());
let instruction = Instruction::new_with_bytes(program_id, &[2], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::InvalidAccountData)
);
let instruction = Instruction::new_with_bytes(program_id, &[3], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::Custom(0))
);
let instruction = Instruction::new_with_bytes(program_id, &[4], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::Custom(42))
);
let instruction = Instruction::new_with_bytes(program_id, &[5], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
let result = result.unwrap_err().unwrap();
if TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) != result
{
assert_eq!(
result,
TransactionError::InstructionError(0, InstructionError::InvalidError)
);
}
let instruction = Instruction::new_with_bytes(program_id, &[6], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
let result = result.unwrap_err().unwrap();
if TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) != result
{
assert_eq!(
result,
TransactionError::InstructionError(0, InstructionError::InvalidError)
);
}
let instruction = Instruction::new_with_bytes(program_id, &[7], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
let result = result.unwrap_err().unwrap();
if TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) != result
{
assert_eq!(
result,
TransactionError::InstructionError(0, InstructionError::AccountBorrowFailed)
);
}
let instruction = Instruction::new_with_bytes(program_id, &[8], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::InvalidInstructionData)
);
let instruction = Instruction::new_with_bytes(program_id, &[9], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::MaxSeedLengthExceeded)
);
}
}
#[test]
fn test_program_bpf_invoke_sanity() {
solana_logger::setup();
const TEST_SUCCESS: u8 = 1;
const TEST_PRIVILEGE_ESCALATION_SIGNER: u8 = 2;
const TEST_PRIVILEGE_ESCALATION_WRITABLE: u8 = 3;
const TEST_PPROGRAM_NOT_EXECUTABLE: u8 = 4;
const TEST_EMPTY_ACCOUNTS_SLICE: u8 = 5;
const TEST_CAP_SEEDS: u8 = 6;
const TEST_CAP_SIGNERS: u8 = 7;
const TEST_ALLOC_ACCESS_VIOLATION: u8 = 8;
const TEST_INSTRUCTION_DATA_TOO_LARGE: u8 = 9;
const TEST_INSTRUCTION_META_TOO_LARGE: u8 = 10;
const TEST_RETURN_ERROR: u8 = 11;
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER: u8 = 12;
const TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE: u8 = 13;
const TEST_WRITABLE_DEESCALATION_WRITABLE: u8 = 14;
const TEST_NESTED_INVOKE_TOO_DEEP: u8 = 15;
#[allow(dead_code)]
#[derive(Debug)]
enum Languages {
C,
Rust,
}
let mut programs = Vec::new();
#[cfg(feature = "bpf_c")]
{
programs.push((Languages::C, "invoke", "invoked", "noop"));
}
#[cfg(feature = "bpf_rust")]
{
programs.push((
Languages::Rust,
"solana_bpf_rust_invoke",
"solana_bpf_rust_invoked",
"solana_bpf_rust_noop",
));
}
for program in programs.iter() {
println!("Test program: {:?}", program);
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let bank = Arc::new(bank);
let bank_client = BankClient::new_shared(&bank);
let invoke_program_id =
load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program.1);
let invoked_program_id =
load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program.2);
let noop_program_id =
load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program.3);
let argument_keypair = Keypair::new();
let account = AccountSharedData::new(42, 100, &invoke_program_id);
bank.store_account(&argument_keypair.pubkey(), &account);
let invoked_argument_keypair = Keypair::new();
let account = AccountSharedData::new(10, 10, &invoked_program_id);
bank.store_account(&invoked_argument_keypair.pubkey(), &account);
let from_keypair = Keypair::new();
let account = AccountSharedData::new(84, 0, &solana_sdk::system_program::id());
bank.store_account(&from_keypair.pubkey(), &account);
let (derived_key1, bump_seed1) =
Pubkey::find_program_address(&[b"You pass butter"], &invoke_program_id);
let (derived_key2, bump_seed2) =
Pubkey::find_program_address(&[b"Lil'", b"Bits"], &invoked_program_id);
let (derived_key3, bump_seed3) =
Pubkey::find_program_address(&[derived_key2.as_ref()], &invoked_program_id);
let mint_pubkey = mint_keypair.pubkey();
let account_metas = vec![
AccountMeta::new(mint_pubkey, true),
AccountMeta::new(argument_keypair.pubkey(), true),
AccountMeta::new_readonly(invoked_program_id, false),
AccountMeta::new(invoked_argument_keypair.pubkey(), true),
AccountMeta::new_readonly(invoked_program_id, false),
AccountMeta::new(argument_keypair.pubkey(), true),
AccountMeta::new(derived_key1, false),
AccountMeta::new(derived_key2, false),
AccountMeta::new_readonly(derived_key3, false),
AccountMeta::new_readonly(solana_sdk::system_program::id(), false),
AccountMeta::new(from_keypair.pubkey(), true),
];
// success cases
let instruction = Instruction::new_with_bytes(
invoke_program_id,
&[TEST_SUCCESS, bump_seed1, bump_seed2, bump_seed3],
account_metas.clone(),
);
let noop_instruction = Instruction::new_with_bytes(noop_program_id, &[], vec![]);
let message = Message::new(&[instruction, noop_instruction], Some(&mint_pubkey));
let tx = Transaction::new(
&[
&mint_keypair,
&argument_keypair,
&invoked_argument_keypair,
&from_keypair,
],
message.clone(),
bank.last_blockhash(),
);
let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
assert!(result.is_ok());
let invoked_programs: Vec<Pubkey> = inner_instructions[0]
.iter()
.map(|ix| message.account_keys[ix.program_id_index as usize].clone())
.collect();
let expected_invoked_programs = match program.0 {
Languages::C => vec![
solana_sdk::system_program::id(),
solana_sdk::system_program::id(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
],
Languages::Rust => vec![
solana_sdk::system_program::id(),
solana_sdk::system_program::id(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
solana_sdk::system_program::id(),
],
};
assert_eq!(invoked_programs.len(), expected_invoked_programs.len());
assert_eq!(invoked_programs, expected_invoked_programs);
let no_invoked_programs: Vec<Pubkey> = inner_instructions[1]
.iter()
.map(|ix| message.account_keys[ix.program_id_index as usize].clone())
.collect();
assert_eq!(no_invoked_programs.len(), 0);
// failure cases
let do_invoke_failure_test_local =
|test: u8, expected_error: TransactionError, expected_invoked_programs: &[Pubkey]| {
println!("Running failure test #{:?}", test);
let instruction_data = &[test, bump_seed1, bump_seed2, bump_seed3];
let signers = vec![
&mint_keypair,
&argument_keypair,
&invoked_argument_keypair,
&from_keypair,
];
let instruction = Instruction::new_with_bytes(
invoke_program_id,
instruction_data,
account_metas.clone(),
);
let message = Message::new(&[instruction], Some(&mint_pubkey));
let tx = Transaction::new(&signers, message.clone(), bank.last_blockhash());
let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
let invoked_programs: Vec<Pubkey> = inner_instructions[0]
.iter()
.map(|ix| message.account_keys[ix.program_id_index as usize].clone())
.collect();
assert_eq!(result.unwrap_err(), expected_error);
assert_eq!(invoked_programs, expected_invoked_programs);
};
do_invoke_failure_test_local(
TEST_PRIVILEGE_ESCALATION_SIGNER,
TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation),
&[invoked_program_id.clone()],
);
do_invoke_failure_test_local(
TEST_PRIVILEGE_ESCALATION_WRITABLE,
TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation),
&[invoked_program_id.clone()],
);
do_invoke_failure_test_local(
TEST_PPROGRAM_NOT_EXECUTABLE,
TransactionError::InstructionError(0, InstructionError::AccountNotExecutable),
&[],
);
do_invoke_failure_test_local(
TEST_EMPTY_ACCOUNTS_SLICE,
TransactionError::InstructionError(0, InstructionError::MissingAccount),
&[],
);
do_invoke_failure_test_local(
TEST_CAP_SEEDS,
TransactionError::InstructionError(0, InstructionError::MaxSeedLengthExceeded),
&[],
);
do_invoke_failure_test_local(
TEST_CAP_SIGNERS,
TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete),
&[],
);
do_invoke_failure_test_local(
TEST_INSTRUCTION_DATA_TOO_LARGE,
TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete),
&[],
);
do_invoke_failure_test_local(
TEST_INSTRUCTION_META_TOO_LARGE,
TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete),
&[],
);
do_invoke_failure_test_local(
TEST_RETURN_ERROR,
TransactionError::InstructionError(0, InstructionError::Custom(42)),
&[invoked_program_id.clone()],
);
do_invoke_failure_test_local(
TEST_PRIVILEGE_DEESCALATION_ESCALATION_SIGNER,
TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation),
&[invoked_program_id.clone()],
);
do_invoke_failure_test_local(
TEST_PRIVILEGE_DEESCALATION_ESCALATION_WRITABLE,
TransactionError::InstructionError(0, InstructionError::PrivilegeEscalation),
&[invoked_program_id.clone()],
);
do_invoke_failure_test_local(
TEST_WRITABLE_DEESCALATION_WRITABLE,
TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified),
&[invoked_program_id.clone()],
);
do_invoke_failure_test_local(
TEST_NESTED_INVOKE_TOO_DEEP,
TransactionError::InstructionError(0, InstructionError::CallDepth),
&[
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
invoked_program_id.clone(),
],
);
// Check resulting state
assert_eq!(43, bank.get_balance(&derived_key1));
let account = bank.get_account(&derived_key1).unwrap();
assert_eq!(&invoke_program_id, account.owner());
assert_eq!(
MAX_PERMITTED_DATA_INCREASE,
bank.get_account(&derived_key1).unwrap().data().len()
);
for i in 0..20 {
assert_eq!(i as u8, account.data()[i]);
}
// Attempt to realloc into unauthorized address space
let account = AccountSharedData::new(84, 0, &solana_sdk::system_program::id());
bank.store_account(&from_keypair.pubkey(), &account);
bank.store_account(&derived_key1, &AccountSharedData::default());
let instruction = Instruction::new_with_bytes(
invoke_program_id,
&[
TEST_ALLOC_ACCESS_VIOLATION,
bump_seed1,
bump_seed2,
bump_seed3,
],
account_metas.clone(),
);
let message = Message::new(&[instruction], Some(&mint_pubkey));
let tx = Transaction::new(
&[
&mint_keypair,
&argument_keypair,
&invoked_argument_keypair,
&from_keypair,
],
message.clone(),
bank.last_blockhash(),
);
let (result, inner_instructions) = process_transaction_and_record_inner(&bank, tx);
let invoked_programs: Vec<Pubkey> = inner_instructions[0]
.iter()
.map(|ix| message.account_keys[ix.program_id_index as usize].clone())
.collect();
assert_eq!(invoked_programs, vec![solana_sdk::system_program::id()]);
assert_eq!(
result.unwrap_err(),
TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete)
);
}
}
#[cfg(feature = "bpf_rust")]
#[test]
fn test_program_bpf_program_id_spoofing() {
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let bank = Arc::new(bank);
let bank_client = BankClient::new_shared(&bank);
let malicious_swap_pubkey = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_spoof1",
);
let malicious_system_pubkey = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_spoof1_system",
);
let from_pubkey = Pubkey::new_unique();
let account = AccountSharedData::new(10, 0, &solana_sdk::system_program::id());
bank.store_account(&from_pubkey, &account);
let to_pubkey = Pubkey::new_unique();
let account = AccountSharedData::new(0, 0, &solana_sdk::system_program::id());
bank.store_account(&to_pubkey, &account);
let account_metas = vec![
AccountMeta::new_readonly(solana_sdk::system_program::id(), false),
AccountMeta::new_readonly(malicious_system_pubkey, false),
AccountMeta::new(from_pubkey, false),
AccountMeta::new(to_pubkey, false),
];
let instruction =
Instruction::new_with_bytes(malicious_swap_pubkey, &[], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::MissingRequiredSignature)
);
assert_eq!(10, bank.get_balance(&from_pubkey));
assert_eq!(0, bank.get_balance(&to_pubkey));
}
#[cfg(feature = "bpf_rust")]
#[test]
fn test_program_bpf_caller_has_access_to_cpi_program() {
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let bank = Arc::new(bank);
let bank_client = BankClient::new_shared(&bank);
let caller_pubkey = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_caller_access",
);
let caller2_pubkey = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_caller_access",
);
let account_metas = vec![
AccountMeta::new_readonly(caller_pubkey, false),
AccountMeta::new_readonly(caller2_pubkey, false),
];
let instruction = Instruction::new_with_bytes(caller_pubkey, &[1], account_metas.clone());
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::MissingAccount)
);
}
#[cfg(feature = "bpf_rust")]
#[test]
fn test_program_bpf_ro_modify() {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let bank = Arc::new(bank);
let bank_client = BankClient::new_shared(&bank);
let program_pubkey = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_ro_modify",
);
let test_keypair = Keypair::new();
let account = AccountSharedData::new(10, 0, &solana_sdk::system_program::id());
bank.store_account(&test_keypair.pubkey(), &account);
let account_metas = vec![
AccountMeta::new_readonly(solana_sdk::system_program::id(), false),
AccountMeta::new(test_keypair.pubkey(), true),
];
let instruction = Instruction::new_with_bytes(program_pubkey, &[1], account_metas.clone());
let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
let result = bank_client.send_and_confirm_message(&[&mint_keypair, &test_keypair], message);
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete)
);
let instruction = Instruction::new_with_bytes(program_pubkey, &[3], account_metas.clone());
let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
let result = bank_client.send_and_confirm_message(&[&mint_keypair, &test_keypair], message);
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete)
);
let instruction = Instruction::new_with_bytes(program_pubkey, &[4], account_metas.clone());
let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
let result = bank_client.send_and_confirm_message(&[&mint_keypair, &test_keypair], message);
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete)
);
}
#[cfg(feature = "bpf_rust")]
#[test]
fn test_program_bpf_call_depth() {
use solana_sdk::process_instruction::BpfComputeBudget;
solana_logger::setup();
println!("Test program: solana_bpf_rust_call_depth");
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let bank_client = BankClient::new(bank);
let program_id = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_call_depth",
);
let instruction = Instruction::new_with_bincode(
program_id,
&(BpfComputeBudget::default().max_call_depth - 1),
vec![],
);
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert!(result.is_ok());
let instruction = Instruction::new_with_bincode(
program_id,
&BpfComputeBudget::default().max_call_depth,
vec![],
);
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert!(result.is_err());
}
#[test]
fn assert_instruction_count() {
solana_logger::setup();
let mut programs = Vec::new();
#[cfg(feature = "bpf_c")]
{
programs.extend_from_slice(&[
("alloc", 1137),
("bpf_to_bpf", 13),
("multiple_static", 8),
("noop", 5),
("noop++", 5),
("relative_call", 10),
("sanity", 169),
("sanity++", 168),
("sha", 1040),
("struct_pass", 8),
("struct_ret", 22),
]);
}
#[cfg(feature = "bpf_rust")]
{
programs.extend_from_slice(&[
("solana_bpf_rust_128bit", 584),
("solana_bpf_rust_alloc", 7082),
("solana_bpf_rust_custom_heap", 522),
("solana_bpf_rust_dep_crate", 2),
("solana_bpf_rust_external_spend", 504),
("solana_bpf_rust_iter", 724),
("solana_bpf_rust_many_args", 233),
("solana_bpf_rust_mem", 3096),
("solana_bpf_rust_membuiltins", 4065),
("solana_bpf_rust_noop", 478),
("solana_bpf_rust_param_passing", 46),
("solana_bpf_rust_rand", 481),
("solana_bpf_rust_sanity", 873),
("solana_bpf_rust_sha", 32295),
]);
}
let mut passed = true;
println!("\n {:30} expected actual diff", "BPF program");
for program in programs.iter() {
let program_id = solana_sdk::pubkey::new_rand();
let key = solana_sdk::pubkey::new_rand();
let mut account = RefCell::new(AccountSharedData::default());
let parameter_accounts = vec![KeyedAccount::new(&key, false, &mut account)];
let count = run_program(program.0, &program_id, parameter_accounts, &[]).unwrap();
let diff: i64 = count as i64 - program.1 as i64;
println!(
" {:30} {:8} {:6} {:+5} ({:+3.0}%)",
program.0,
program.1,
count,
diff,
100.0_f64 * count as f64 / program.1 as f64 - 100.0_f64,
);
if count > program.1 {
passed = false;
}
}
assert!(passed);
}
#[cfg(any(feature = "bpf_rust"))]
#[test]
fn test_program_bpf_instruction_introspection() {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50_000);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let bank = Arc::new(bank);
let bank_client = BankClient::new_shared(&bank);
let program_id = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_instruction_introspection",
);
// Passing transaction
let account_metas = vec![AccountMeta::new_readonly(
solana_sdk::sysvar::instructions::id(),
false,
)];
let instruction0 = Instruction::new_with_bytes(program_id, &[0u8, 0u8], account_metas.clone());
let instruction1 = Instruction::new_with_bytes(program_id, &[0u8, 1u8], account_metas.clone());
let instruction2 = Instruction::new_with_bytes(program_id, &[0u8, 2u8], account_metas);
let message = Message::new(
&[instruction0, instruction1, instruction2],
Some(&mint_keypair.pubkey()),
);
let result = bank_client.send_and_confirm_message(&[&mint_keypair], message);
assert!(result.is_ok());
// writable special instructions11111 key, should not be allowed
let account_metas = vec![AccountMeta::new(
solana_sdk::sysvar::instructions::id(),
false,
)];
let instruction = Instruction::new_with_bytes(program_id, &[0], account_metas);
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert_eq!(
result.unwrap_err().unwrap(),
// sysvar write locks are demoted to read only. So this will no longer
// cause InvalidAccountIndex error.
TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete),
);
// No accounts, should error
let instruction = Instruction::new_with_bytes(program_id, &[0], vec![]);
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert!(result.is_err());
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(
0,
solana_sdk::instruction::InstructionError::NotEnoughAccountKeys
)
);
assert!(bank
.get_account(&solana_sdk::sysvar::instructions::id())
.is_none());
}
#[cfg(feature = "bpf_rust")]
#[test]
fn test_program_bpf_test_use_latest_executor() {
use solana_sdk::{loader_instruction, system_instruction};
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let bank_client = BankClient::new(bank);
let panic_id = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_panic",
);
let program_keypair = Keypair::new();
// Write the panic program into the program account
let elf = read_bpf_program("solana_bpf_rust_panic");
let message = Message::new(
&[system_instruction::create_account(
&mint_keypair.pubkey(),
&program_keypair.pubkey(),
1,
elf.len() as u64 * 2,
&bpf_loader::id(),
)],
Some(&mint_keypair.pubkey()),
);
assert!(bank_client
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
.is_ok());
write_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
&program_keypair,
&elf,
);
// Finalize the panic program, but fail the tx
let message = Message::new(
&[
loader_instruction::finalize(&program_keypair.pubkey(), &bpf_loader::id()),
Instruction::new_with_bytes(panic_id, &[0], vec![]),
],
Some(&mint_keypair.pubkey()),
);
assert!(bank_client
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
.is_err());
// Write the noop program into the same program account
let elf = read_bpf_program("solana_bpf_rust_noop");
write_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
&program_keypair,
&elf,
);
// Finalize the noop program
let message = Message::new(
&[loader_instruction::finalize(
&program_keypair.pubkey(),
&bpf_loader::id(),
)],
Some(&mint_keypair.pubkey()),
);
assert!(bank_client
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
.is_ok());
// Call the noop program, should get noop not panic
let message = Message::new(
&[Instruction::new_with_bytes(
program_keypair.pubkey(),
&[0],
vec![],
)],
Some(&mint_keypair.pubkey()),
);
assert!(bank_client
.send_and_confirm_message(&[&mint_keypair], message)
.is_ok());
}
#[ignore] // Invoking BPF loaders from CPI not allowed
#[cfg(feature = "bpf_rust")]
#[test]
fn test_program_bpf_test_use_latest_executor2() {
use solana_sdk::{loader_instruction, system_instruction};
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let bank_client = BankClient::new(bank);
let invoke_and_error = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_invoke_and_error",
);
let invoke_and_ok = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_invoke_and_ok",
);
let program_keypair = Keypair::new();
// Write the panic program into the program account
let elf = read_bpf_program("solana_bpf_rust_panic");
let message = Message::new(
&[system_instruction::create_account(
&mint_keypair.pubkey(),
&program_keypair.pubkey(),
1,
elf.len() as u64 * 2,
&bpf_loader::id(),
)],
Some(&mint_keypair.pubkey()),
);
assert!(bank_client
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
.is_ok());
write_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
&program_keypair,
&elf,
);
// - invoke finalize and return error, swallow error
let mut instruction =
loader_instruction::finalize(&program_keypair.pubkey(), &bpf_loader::id());
instruction.accounts.insert(
0,
AccountMeta {
is_signer: false,
is_writable: false,
pubkey: instruction.program_id,
},
);
instruction.program_id = invoke_and_ok;
instruction.accounts.insert(
0,
AccountMeta {
is_signer: false,
is_writable: false,
pubkey: invoke_and_error,
},
);
let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
assert!(bank_client
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
.is_ok());
// invoke program, verify not found
let message = Message::new(
&[Instruction::new_with_bytes(
program_keypair.pubkey(),
&[0],
vec![],
)],
Some(&mint_keypair.pubkey()),
);
assert_eq!(
bank_client
.send_and_confirm_message(&[&mint_keypair], message)
.unwrap_err()
.unwrap(),
TransactionError::InvalidProgramForExecution
);
// Write the noop program into the same program account
let elf = read_bpf_program("solana_bpf_rust_noop");
write_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
&program_keypair,
&elf,
);
// Finalize the noop program
let message = Message::new(
&[loader_instruction::finalize(
&program_keypair.pubkey(),
&bpf_loader::id(),
)],
Some(&mint_keypair.pubkey()),
);
assert!(bank_client
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
.is_ok());
// Call the program, should get noop, not panic
let message = Message::new(
&[Instruction::new_with_bytes(
program_keypair.pubkey(),
&[0],
vec![],
)],
Some(&mint_keypair.pubkey()),
);
assert!(bank_client
.send_and_confirm_message(&[&mint_keypair], message)
.is_ok());
}
#[cfg(feature = "bpf_rust")]
#[test]
fn test_program_bpf_upgrade() {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!();
bank.add_builtin(&name, id, entrypoint);
let bank_client = BankClient::new(bank);
// Deploy upgrade program
let buffer_keypair = Keypair::new();
let program_keypair = Keypair::new();
let program_id = program_keypair.pubkey();
let authority_keypair = Keypair::new();
load_upgradeable_bpf_program(
&bank_client,
&mint_keypair,
&buffer_keypair,
&program_keypair,
&authority_keypair,
"solana_bpf_rust_upgradeable",
);
let mut instruction = Instruction::new_with_bytes(
program_id,
&[0],
vec![
AccountMeta::new(program_id.clone(), false),
AccountMeta::new(clock::id(), false),
AccountMeta::new(fees::id(), false),
],
);
// Call upgrade program
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction.clone());
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::Custom(42))
);
// Upgrade program
let buffer_keypair = Keypair::new();
upgrade_bpf_program(
&bank_client,
&mint_keypair,
&buffer_keypair,
&program_id,
&authority_keypair,
"solana_bpf_rust_upgraded",
);
// Call upgraded program
instruction.data[0] += 1;
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction.clone());
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::Custom(43))
);
// Set a new authority
let new_authority_keypair = Keypair::new();
set_upgrade_authority(
&bank_client,
&mint_keypair,
&program_id,
&authority_keypair,
Some(&new_authority_keypair.pubkey()),
);
// Upgrade back to the original program
let buffer_keypair = Keypair::new();
upgrade_bpf_program(
&bank_client,
&mint_keypair,
&buffer_keypair,
&program_id,
&new_authority_keypair,
"solana_bpf_rust_upgradeable",
);
// Call original program
instruction.data[0] += 1;
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::Custom(42))
);
}
#[cfg(feature = "bpf_rust")]
#[test]
fn test_program_bpf_upgrade_and_invoke_in_same_tx() {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!();
bank.add_builtin(&name, id, entrypoint);
let bank = Arc::new(bank);
let bank_client = BankClient::new_shared(&bank);
// Deploy upgrade program
let buffer_keypair = Keypair::new();
let program_keypair = Keypair::new();
let program_id = program_keypair.pubkey();
let authority_keypair = Keypair::new();
load_upgradeable_bpf_program(
&bank_client,
&mint_keypair,
&buffer_keypair,
&program_keypair,
&authority_keypair,
"solana_bpf_rust_noop",
);
let invoke_instruction = Instruction::new_with_bytes(
program_id,
&[0],
vec![
AccountMeta::new(program_id.clone(), false),
AccountMeta::new(clock::id(), false),
AccountMeta::new(fees::id(), false),
],
);
// Call upgradeable program
let result =
bank_client.send_and_confirm_instruction(&mint_keypair, invoke_instruction.clone());
assert!(result.is_ok());
// Prepare for upgrade
let buffer_keypair = Keypair::new();
load_upgradeable_buffer(
&bank_client,
&mint_keypair,
&buffer_keypair,
&authority_keypair,
"solana_bpf_rust_panic",
);
// Invoke, then upgrade the program, and then invoke again in same tx
let message = Message::new(
&[
invoke_instruction.clone(),
bpf_loader_upgradeable::upgrade(
&program_id,
&buffer_keypair.pubkey(),
&authority_keypair.pubkey(),
&mint_keypair.pubkey(),
),
invoke_instruction,
],
Some(&mint_keypair.pubkey()),
);
let tx = Transaction::new(
&[&mint_keypair, &authority_keypair],
message.clone(),
bank.last_blockhash(),
);
let (result, _) = process_transaction_and_record_inner(&bank, tx);
assert_eq!(
result.unwrap_err(),
TransactionError::InstructionError(2, InstructionError::ProgramFailedToComplete)
);
}
#[cfg(feature = "bpf_rust")]
#[test]
fn test_program_bpf_invoke_upgradeable_via_cpi() {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!();
bank.add_builtin(&name, id, entrypoint);
let bank_client = BankClient::new(bank);
let invoke_and_return = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_invoke_and_return",
);
// Deploy upgradeable program
let buffer_keypair = Keypair::new();
let program_keypair = Keypair::new();
let program_id = program_keypair.pubkey();
let authority_keypair = Keypair::new();
load_upgradeable_bpf_program(
&bank_client,
&mint_keypair,
&buffer_keypair,
&program_keypair,
&authority_keypair,
"solana_bpf_rust_upgradeable",
);
let mut instruction = Instruction::new_with_bytes(
invoke_and_return,
&[0],
vec![
AccountMeta::new(program_id, false),
AccountMeta::new(program_id, false),
AccountMeta::new(clock::id(), false),
AccountMeta::new(fees::id(), false),
],
);
// Call invoker program to invoke the upgradeable program
instruction.data[0] += 1;
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction.clone());
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::Custom(42))
);
// Upgrade program
let buffer_keypair = Keypair::new();
upgrade_bpf_program(
&bank_client,
&mint_keypair,
&buffer_keypair,
&program_id,
&authority_keypair,
"solana_bpf_rust_upgraded",
);
// Call the upgraded program
instruction.data[0] += 1;
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction.clone());
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::Custom(43))
);
// Set a new authority
let new_authority_keypair = Keypair::new();
set_upgrade_authority(
&bank_client,
&mint_keypair,
&program_id,
&authority_keypair,
Some(&new_authority_keypair.pubkey()),
);
// Upgrade back to the original program
let buffer_keypair = Keypair::new();
upgrade_bpf_program(
&bank_client,
&mint_keypair,
&buffer_keypair,
&program_id,
&new_authority_keypair,
"solana_bpf_rust_upgradeable",
);
// Call original program
instruction.data[0] += 1;
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction.clone());
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::Custom(42))
);
}
#[test]
#[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
fn test_program_bpf_disguised_as_bpf_loader() {
solana_logger::setup();
let mut programs = Vec::new();
#[cfg(feature = "bpf_c")]
{
programs.extend_from_slice(&[("noop")]);
}
#[cfg(feature = "bpf_rust")]
{
programs.extend_from_slice(&[("solana_bpf_rust_noop")]);
}
for program in programs.iter() {
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_deprecated_program!();
bank.add_builtin(&name, id, entrypoint);
let bank_client = BankClient::new(bank);
let program_id = load_bpf_program(
&bank_client,
&bpf_loader_deprecated::id(),
&mint_keypair,
program,
);
let account_metas = vec![AccountMeta::new_readonly(program_id, false)];
let instruction =
Instruction::new_with_bytes(bpf_loader_deprecated::id(), &[1], account_metas);
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::IncorrectProgramId)
);
}
}
#[test]
#[cfg(feature = "bpf_c")]
fn test_program_bpf_c_dup() {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let account_address = Pubkey::new_unique();
let account =
AccountSharedData::new_data(42, &[1_u8, 2, 3], &solana_sdk::system_program::id()).unwrap();
bank.store_account(&account_address, &account);
let bank_client = BankClient::new(bank);
let program_id = load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, "ser");
let account_metas = vec![
AccountMeta::new_readonly(account_address, false),
AccountMeta::new_readonly(account_address, false),
];
let instruction = Instruction::new_with_bytes(program_id, &[4, 5, 6, 7], account_metas);
bank_client
.send_and_confirm_instruction(&mint_keypair, instruction)
.unwrap();
}
#[cfg(feature = "bpf_rust")]
#[test]
fn test_program_bpf_upgrade_via_cpi() {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!();
bank.add_builtin(&name, id, entrypoint);
let bank_client = BankClient::new(bank);
let invoke_and_return = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_invoke_and_return",
);
// Deploy upgradeable program
let buffer_keypair = Keypair::new();
let program_keypair = Keypair::new();
let program_id = program_keypair.pubkey();
let authority_keypair = Keypair::new();
load_upgradeable_bpf_program(
&bank_client,
&mint_keypair,
&buffer_keypair,
&program_keypair,
&authority_keypair,
"solana_bpf_rust_upgradeable",
);
let program_account = bank_client.get_account(&program_id).unwrap().unwrap();
let programdata_address = match program_account.state() {
Ok(bpf_loader_upgradeable::UpgradeableLoaderState::Program {
programdata_address,
}) => programdata_address,
_ => unreachable!(),
};
let original_programdata = bank_client
.get_account_data(&programdata_address)
.unwrap()
.unwrap();
let mut instruction = Instruction::new_with_bytes(
invoke_and_return,
&[0],
vec![
AccountMeta::new(program_id, false),
AccountMeta::new(program_id, false),
AccountMeta::new(clock::id(), false),
AccountMeta::new(fees::id(), false),
],
);
// Call the upgradable program
instruction.data[0] += 1;
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction.clone());
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::Custom(42))
);
// Load the buffer account
let path = create_bpf_path("solana_bpf_rust_upgraded");
let mut file = File::open(&path).unwrap_or_else(|err| {
panic!("Failed to open {}: {}", path.display(), err);
});
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
let buffer_keypair = Keypair::new();
load_buffer_account(
&bank_client,
&mint_keypair,
&buffer_keypair,
&authority_keypair,
&elf,
);
// Upgrade program via CPI
let mut upgrade_instruction = bpf_loader_upgradeable::upgrade(
&program_id,
&buffer_keypair.pubkey(),
&authority_keypair.pubkey(),
&mint_keypair.pubkey(),
);
upgrade_instruction.program_id = invoke_and_return;
upgrade_instruction
.accounts
.insert(0, AccountMeta::new(bpf_loader_upgradeable::id(), false));
let message = Message::new(&[upgrade_instruction], Some(&mint_keypair.pubkey()));
bank_client
.send_and_confirm_message(&[&mint_keypair, &authority_keypair], message)
.unwrap();
// Call the upgraded program
instruction.data[0] += 1;
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction.clone());
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::Custom(43))
);
// Validate that the programdata was actually overwritten
let programdata = bank_client
.get_account_data(&programdata_address)
.unwrap()
.unwrap();
assert_ne!(programdata, original_programdata);
}
#[cfg(feature = "bpf_rust")]
#[test]
fn test_program_bpf_upgrade_self_via_cpi() {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!();
bank.add_builtin(&name, id, entrypoint);
let bank = Arc::new(bank);
let bank_client = BankClient::new_shared(&bank);
let noop_program_id = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_noop",
);
// Deploy upgradeable program
let buffer_keypair = Keypair::new();
let program_keypair = Keypair::new();
let program_id = program_keypair.pubkey();
let authority_keypair = Keypair::new();
load_upgradeable_bpf_program(
&bank_client,
&mint_keypair,
&buffer_keypair,
&program_keypair,
&authority_keypair,
"solana_bpf_rust_invoke_and_return",
);
let mut invoke_instruction = Instruction::new_with_bytes(
program_id,
&[0],
vec![
AccountMeta::new(noop_program_id, false),
AccountMeta::new(noop_program_id, false),
AccountMeta::new(clock::id(), false),
AccountMeta::new(fees::id(), false),
],
);
// Call the upgraded program
invoke_instruction.data[0] += 1;
let result =
bank_client.send_and_confirm_instruction(&mint_keypair, invoke_instruction.clone());
assert!(result.is_ok());
// Prepare for upgrade
let buffer_keypair = Keypair::new();
load_upgradeable_buffer(
&bank_client,
&mint_keypair,
&buffer_keypair,
&authority_keypair,
"solana_bpf_rust_panic",
);
// Invoke, then upgrade the program, and then invoke again in same tx
let message = Message::new(
&[
invoke_instruction.clone(),
bpf_loader_upgradeable::upgrade(
&program_id,
&buffer_keypair.pubkey(),
&authority_keypair.pubkey(),
&mint_keypair.pubkey(),
),
invoke_instruction,
],
Some(&mint_keypair.pubkey()),
);
let tx = Transaction::new(
&[&mint_keypair, &authority_keypair],
message.clone(),
bank.last_blockhash(),
);
let (result, _) = process_transaction_and_record_inner(&bank, tx);
assert_eq!(
result.unwrap_err(),
TransactionError::InstructionError(2, InstructionError::ProgramFailedToComplete)
);
}
#[cfg(feature = "bpf_rust")]
#[test]
fn test_program_bpf_set_upgrade_authority_via_cpi() {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!();
bank.add_builtin(&name, id, entrypoint);
let bank_client = BankClient::new(bank);
// Deploy CPI invoker program
let invoke_and_return = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_invoke_and_return",
);
// Deploy upgradeable program
let buffer_keypair = Keypair::new();
let program_keypair = Keypair::new();
let program_id = program_keypair.pubkey();
let authority_keypair = Keypair::new();
load_upgradeable_bpf_program(
&bank_client,
&mint_keypair,
&buffer_keypair,
&program_keypair,
&authority_keypair,
"solana_bpf_rust_upgradeable",
);
// Set program upgrade authority instruction to invoke via CPI
let new_upgrade_authority_key = Keypair::new().pubkey();
let mut set_upgrade_authority_instruction = bpf_loader_upgradeable::set_upgrade_authority(
&program_id,
&authority_keypair.pubkey(),
Some(&new_upgrade_authority_key),
);
// Invoke set_upgrade_authority via CPI invoker program
set_upgrade_authority_instruction.program_id = invoke_and_return;
set_upgrade_authority_instruction
.accounts
.insert(0, AccountMeta::new(bpf_loader_upgradeable::id(), false));
let message = Message::new(
&[set_upgrade_authority_instruction],
Some(&mint_keypair.pubkey()),
);
bank_client
.send_and_confirm_message(&[&mint_keypair, &authority_keypair], message)
.unwrap();
// Assert upgrade authority was changed
let program_account_data = bank_client.get_account_data(&program_id).unwrap().unwrap();
let program_account = parse_bpf_upgradeable_loader(&program_account_data).unwrap();
let upgrade_authority_key = match program_account {
BpfUpgradeableLoaderAccountType::Program(ui_program) => {
let program_data_account_key = Pubkey::from_str(&ui_program.program_data).unwrap();
let program_data_account_data = bank_client
.get_account_data(&program_data_account_key)
.unwrap()
.unwrap();
let program_data_account =
parse_bpf_upgradeable_loader(&program_data_account_data).unwrap();
match program_data_account {
BpfUpgradeableLoaderAccountType::ProgramData(ui_program_data) => ui_program_data
.authority
.map(|a| Pubkey::from_str(&a).unwrap()),
_ => None,
}
}
_ => None,
};
assert_eq!(Some(new_upgrade_authority_key), upgrade_authority_key);
}
#[cfg(feature = "bpf_rust")]
#[test]
fn test_program_upgradeable_locks() {
fn setup_program_upgradeable_locks(
payer_keypair: &Keypair,
buffer_keypair: &Keypair,
program_keypair: &Keypair,
) -> (Arc<Bank>, Transaction, Transaction) {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(2_000_000_000);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!();
bank.add_builtin(&name, id, entrypoint);
let bank = Arc::new(bank);
let bank_client = BankClient::new_shared(&bank);
load_upgradeable_bpf_program(
&bank_client,
&mint_keypair,
buffer_keypair,
program_keypair,
payer_keypair,
"solana_bpf_rust_panic",
);
// Load the buffer account
let path = create_bpf_path("solana_bpf_rust_noop");
let mut file = File::open(&path).unwrap_or_else(|err| {
panic!("Failed to open {}: {}", path.display(), err);
});
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
load_buffer_account(
&bank_client,
&mint_keypair,
buffer_keypair,
&payer_keypair,
&elf,
);
bank_client
.send_and_confirm_instruction(
&mint_keypair,
system_instruction::transfer(
&mint_keypair.pubkey(),
&payer_keypair.pubkey(),
1_000_000_000,
),
)
.unwrap();
let invoke_tx = Transaction::new(
&[payer_keypair],
Message::new(
&[Instruction::new_with_bytes(
program_keypair.pubkey(),
&[0; 0],
vec![],
)],
Some(&payer_keypair.pubkey()),
),
bank.last_blockhash(),
);
let upgrade_tx = Transaction::new(
&[payer_keypair],
Message::new(
&[bpf_loader_upgradeable::upgrade(
&program_keypair.pubkey(),
&buffer_keypair.pubkey(),
&payer_keypair.pubkey(),
&payer_keypair.pubkey(),
)],
Some(&payer_keypair.pubkey()),
),
bank.last_blockhash(),
);
(bank, invoke_tx, upgrade_tx)
}
let payer_keypair = keypair_from_seed(&[56u8; 32]).unwrap();
let buffer_keypair = keypair_from_seed(&[11; 32]).unwrap();
let program_keypair = keypair_from_seed(&[77u8; 32]).unwrap();
let results1 = {
let (bank, invoke_tx, upgrade_tx) =
setup_program_upgradeable_locks(&payer_keypair, &buffer_keypair, &program_keypair);
execute_transactions(&bank, &[upgrade_tx, invoke_tx])
};
let results2 = {
let (bank, invoke_tx, upgrade_tx) =
setup_program_upgradeable_locks(&payer_keypair, &buffer_keypair, &program_keypair);
execute_transactions(&bank, &[invoke_tx, upgrade_tx])
};
if false {
println!("upgrade and invoke");
for result in &results1 {
print_confirmed_tx("result", result.clone());
}
println!("invoke and upgrade");
for result in &results2 {
print_confirmed_tx("result", result.clone());
}
}
if let Some(ref meta) = results1[0].transaction.meta {
assert_eq!(meta.status, Ok(()));
} else {
panic!("no meta");
}
if let Some(ref meta) = results1[1].transaction.meta {
assert_eq!(meta.status, Err(TransactionError::AccountInUse));
} else {
panic!("no meta");
}
if let Some(ref meta) = results2[0].transaction.meta {
assert_eq!(
meta.status,
Err(TransactionError::InstructionError(
0,
InstructionError::ProgramFailedToComplete
))
);
} else {
panic!("no meta");
}
if let Some(ref meta) = results2[1].transaction.meta {
assert_eq!(meta.status, Err(TransactionError::AccountInUse));
} else {
panic!("no meta");
}
}
#[cfg(feature = "bpf_rust")]
#[test]
fn test_program_bpf_finalize() {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let bank = Arc::new(bank);
let bank_client = BankClient::new_shared(&bank);
let program_pubkey = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_finalize",
);
let noop_keypair = Keypair::new();
// Write the noop program into the same program account
let elf = read_bpf_program("solana_bpf_rust_noop");
let message = Message::new(
&[system_instruction::create_account(
&mint_keypair.pubkey(),
&noop_keypair.pubkey(),
1,
elf.len() as u64 * 2,
&bpf_loader::id(),
)],
Some(&mint_keypair.pubkey()),
);
assert!(bank_client
.send_and_confirm_message(&[&mint_keypair, &noop_keypair], message)
.is_ok());
write_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
&noop_keypair,
&elf,
);
let account_metas = vec![
AccountMeta::new(noop_keypair.pubkey(), true),
AccountMeta::new_readonly(bpf_loader::id(), false),
AccountMeta::new(rent::id(), false),
];
let instruction = Instruction::new_with_bytes(program_pubkey, &[], account_metas.clone());
let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
let result = bank_client.send_and_confirm_message(&[&mint_keypair, &noop_keypair], message);
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete)
);
}
#[cfg(feature = "bpf_rust")]
#[test]
fn test_program_bpf_ro_account_modify() {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new(&genesis_config);
let (name, id, entrypoint) = solana_bpf_loader_program!();
bank.add_builtin(&name, id, entrypoint);
let bank = Arc::new(bank);
let bank_client = BankClient::new_shared(&bank);
let program_id = load_bpf_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_bpf_rust_ro_account_modify",
);
let argument_keypair = Keypair::new();
let account = AccountSharedData::new(42, 100, &program_id);
bank.store_account(&argument_keypair.pubkey(), &account);
let from_keypair = Keypair::new();
let account = AccountSharedData::new(84, 0, &solana_sdk::system_program::id());
bank.store_account(&from_keypair.pubkey(), &account);
let mint_pubkey = mint_keypair.pubkey();
let account_metas = vec![
AccountMeta::new_readonly(argument_keypair.pubkey(), false),
AccountMeta::new_readonly(program_id, false),
];
let instruction = Instruction::new_with_bytes(program_id, &[0], account_metas.clone());
let message = Message::new(&[instruction], Some(&mint_pubkey));
let result = bank_client.send_and_confirm_message(&[&mint_keypair], message);
println!("result: {:?}", result);
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified)
);
let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas.clone());
let message = Message::new(&[instruction], Some(&mint_pubkey));
let result = bank_client.send_and_confirm_message(&[&mint_keypair], message);
println!("result: {:?}", result);
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified)
);
let instruction = Instruction::new_with_bytes(program_id, &[2], account_metas.clone());
let message = Message::new(&[instruction], Some(&mint_pubkey));
let result = bank_client.send_and_confirm_message(&[&mint_keypair], message);
println!("result: {:?}", result);
assert_eq!(
result.unwrap_err().unwrap(),
TransactionError::InstructionError(0, InstructionError::ReadonlyDataModified)
);
}