Adds symmetric tests for all cases of un-/re-/deployment inside the same transaction (#29725)

Unifies test_program_sbf_upgrade_and_invoke_in_same_tx() and test_program_sbf_upgrade_self_via_cpi() into test_program_sbf_invoke_in_same_tx_as_redeployment().
Adds test_program_sbf_invoke_in_same_tx_as_deployment() and test_program_sbf_invoke_in_same_tx_as_undeployment().
This commit is contained in:
Alexander Meißner 2023-01-17 09:44:27 +01:00 committed by GitHub
parent 156454c980
commit c5380d4699
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 247 additions and 123 deletions

View File

@ -1745,7 +1745,7 @@ fn test_program_sbf_upgrade() {
#[test]
#[cfg(feature = "sbf_rust")]
fn test_program_sbf_upgrade_and_invoke_in_same_tx() {
fn test_program_sbf_invoke_in_same_tx_as_deployment() {
solana_logger::setup();
let GenesisConfigInfo {
@ -1759,7 +1759,99 @@ fn test_program_sbf_upgrade_and_invoke_in_same_tx() {
let bank = Arc::new(bank);
let bank_client = BankClient::new_shared(&bank);
// Deploy upgrade program
// Deploy upgradeable program
let buffer_keypair = Keypair::new();
let program_keypair = Keypair::new();
let program_id = program_keypair.pubkey();
let authority_keypair = Keypair::new();
// Deploy indirect invocation program
let indirect_program_keypair = Keypair::new();
load_upgradeable_program(
&bank_client,
&mint_keypair,
&buffer_keypair,
&indirect_program_keypair,
&authority_keypair,
"solana_sbf_rust_invoke_and_return",
);
let invoke_instruction =
Instruction::new_with_bytes(program_id, &[0], vec![AccountMeta::new(clock::id(), false)]);
let indirect_invoke_instruction = Instruction::new_with_bytes(
indirect_program_keypair.pubkey(),
&[0],
vec![
AccountMeta::new_readonly(program_id, false),
AccountMeta::new_readonly(clock::id(), false),
],
);
// Prepare deployment
let program = load_upgradeable_buffer(
&bank_client,
&mint_keypair,
&buffer_keypair,
&authority_keypair,
"solana_sbf_rust_noop",
);
let deployment_instructions = bpf_loader_upgradeable::deploy_with_max_program_len(
&mint_keypair.pubkey(),
&program_keypair.pubkey(),
&buffer_keypair.pubkey(),
&authority_keypair.pubkey(),
1.max(
bank_client
.get_minimum_balance_for_rent_exemption(
bpf_loader_upgradeable::UpgradeableLoaderState::size_of_program(),
)
.unwrap(),
),
program.len() * 2,
)
.unwrap();
// Deployment is invisible to top-level-instructions but visible to CPI instructions
for (invoke_instruction, expected) in [
(
invoke_instruction,
Err(TransactionError::ProgramAccountNotFound),
),
(indirect_invoke_instruction, Ok(())),
] {
let mut instructions = deployment_instructions.clone();
instructions.push(invoke_instruction);
let tx = Transaction::new(
&[&mint_keypair, &program_keypair, &authority_keypair],
Message::new(&instructions, Some(&mint_keypair.pubkey())),
bank.last_blockhash(),
);
let results = execute_transactions(&bank, vec![tx]);
if let Err(err) = expected {
assert_eq!(results[0].as_ref().unwrap_err(), &err);
} else {
assert!(results[0].is_ok());
}
}
}
#[test]
#[cfg(feature = "sbf_rust")]
fn test_program_sbf_invoke_in_same_tx_as_redeployment() {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new_for_tests(&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 upgradeable program
let buffer_keypair = Keypair::new();
let program_keypair = Keypair::new();
let program_id = program_keypair.pubkey();
@ -1773,16 +1865,29 @@ fn test_program_sbf_upgrade_and_invoke_in_same_tx() {
"solana_sbf_rust_noop",
);
// Deploy indirect invocation program
let indirect_program_keypair = Keypair::new();
load_upgradeable_program(
&bank_client,
&mint_keypair,
&buffer_keypair,
&indirect_program_keypair,
&authority_keypair,
"solana_sbf_rust_invoke_and_return",
);
let invoke_instruction =
Instruction::new_with_bytes(program_id, &[0], vec![AccountMeta::new(clock::id(), false)]);
let indirect_invoke_instruction = Instruction::new_with_bytes(
indirect_program_keypair.pubkey(),
&[0],
vec![
AccountMeta::new_readonly(program_id, false),
AccountMeta::new_readonly(clock::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();
// Prepare redeployment
load_upgradeable_buffer(
&bank_client,
&mint_keypair,
@ -1790,31 +1895,142 @@ fn test_program_sbf_upgrade_and_invoke_in_same_tx() {
&authority_keypair,
"solana_sbf_rust_panic",
);
let redeployment_instruction = bpf_loader_upgradeable::upgrade(
&program_id,
&buffer_keypair.pubkey(),
&authority_keypair.pubkey(),
&mint_keypair.pubkey(),
);
// 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,
// Redeployment is visible to both top-level-instructions and CPI instructions
for invoke_instruction in [invoke_instruction, indirect_invoke_instruction] {
// Call upgradeable program
let result =
bank_client.send_and_confirm_instruction(&mint_keypair, invoke_instruction.clone());
assert!(result.is_ok());
// Upgrade the program and invoke in same tx
let message = Message::new(
&[redeployment_instruction.clone(), 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(1, InstructionError::ProgramFailedToComplete),
);
}
}
#[test]
#[cfg(feature = "sbf_rust")]
fn test_program_sbf_invoke_in_same_tx_as_undeployment() {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new_for_tests(&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 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_program(
&bank_client,
&mint_keypair,
&buffer_keypair,
&program_keypair,
&authority_keypair,
"solana_sbf_rust_noop",
);
// Deploy indirect invocation program
let indirect_program_keypair = Keypair::new();
load_upgradeable_program(
&bank_client,
&mint_keypair,
&buffer_keypair,
&indirect_program_keypair,
&authority_keypair,
"solana_sbf_rust_invoke_and_return",
);
// Deploy panic program
let panic_program_keypair = Keypair::new();
load_upgradeable_program(
&bank_client,
&mint_keypair,
&buffer_keypair,
&panic_program_keypair,
&authority_keypair,
"solana_sbf_rust_panic",
);
let invoke_instruction =
Instruction::new_with_bytes(program_id, &[0], vec![AccountMeta::new(clock::id(), false)]);
let indirect_invoke_instruction = Instruction::new_with_bytes(
indirect_program_keypair.pubkey(),
&[0],
vec![
AccountMeta::new_readonly(program_id, false),
AccountMeta::new_readonly(clock::id(), false),
],
Some(&mint_keypair.pubkey()),
);
let tx = Transaction::new(
&[&mint_keypair, &authority_keypair],
message.clone(),
bank.last_blockhash(),
let panic_instruction =
Instruction::new_with_bytes(panic_program_keypair.pubkey(), &[], vec![]);
// Prepare undeployment
let (programdata_address, _) = Pubkey::find_program_address(
&[program_keypair.pubkey().as_ref()],
&bpf_loader_upgradeable::id(),
);
let (result, _, _) = process_transaction_and_record_inner(&bank, tx);
assert_eq!(
result.unwrap_err(),
TransactionError::InstructionError(2, InstructionError::ProgramFailedToComplete)
let undeployment_instruction = bpf_loader_upgradeable::close_any(
&programdata_address,
&mint_keypair.pubkey(),
Some(&authority_keypair.pubkey()),
Some(&program_id),
);
// Undeployment is invisible to both top-level-instructions and CPI instructions
for invoke_instruction in [invoke_instruction, indirect_invoke_instruction] {
// Call upgradeable program
let result =
bank_client.send_and_confirm_instruction(&mint_keypair, invoke_instruction.clone());
assert!(result.is_ok());
// Upgrade the program and invoke in same tx
let message = Message::new(
&[
undeployment_instruction.clone(),
invoke_instruction,
panic_instruction.clone(), // Make sure the TX fails, so we don't have to deploy another program
],
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),
);
}
}
#[test]
@ -2099,95 +2315,6 @@ fn test_program_sbf_upgrade_via_cpi() {
assert_ne!(programdata, original_programdata);
}
#[test]
#[cfg(feature = "sbf_rust")]
fn test_program_sbf_upgrade_self_via_cpi() {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config(50);
let mut bank = Bank::new_for_tests(&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_program(
&bank_client,
&bpf_loader::id(),
&mint_keypair,
"solana_sbf_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_program(
&bank_client,
&mint_keypair,
&buffer_keypair,
&program_keypair,
&authority_keypair,
"solana_sbf_rust_invoke_and_return",
);
let mut invoke_instruction = Instruction::new_with_bytes(
program_id,
&[0],
vec![
AccountMeta::new_readonly(noop_program_id, false),
AccountMeta::new_readonly(clock::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_sbf_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)
);
}
#[test]
#[cfg(feature = "sbf_rust")]
fn test_program_sbf_set_upgrade_authority_via_cpi() {

View File

@ -167,9 +167,6 @@ pub fn load_upgradeable_program(
authority_keypair: &Keypair,
name: &str,
) {
let program_pubkey = executable_keypair.pubkey();
let authority_pubkey = authority_keypair.pubkey();
let program = load_upgradeable_buffer(
bank_client,
from_keypair,
@ -181,9 +178,9 @@ pub fn load_upgradeable_program(
let message = Message::new(
&bpf_loader_upgradeable::deploy_with_max_program_len(
&from_keypair.pubkey(),
&program_pubkey,
&executable_keypair.pubkey(),
&buffer_keypair.pubkey(),
&authority_pubkey,
&authority_keypair.pubkey(),
1.max(
bank_client
.get_minimum_balance_for_rent_exemption(