Enable program upgrades via CPI (#14449)
This commit is contained in:
parent
a89ba32b24
commit
5eacc5d08d
|
@ -21,7 +21,7 @@ use solana_runtime::{
|
|||
};
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
bpf_loader, bpf_loader_deprecated,
|
||||
bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable,
|
||||
client::SyncClient,
|
||||
clock::{DEFAULT_SLOTS_PER_EPOCH, MAX_PROCESSING_AGE},
|
||||
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
|
||||
|
@ -1632,7 +1632,7 @@ fn test_program_bpf_invoke_upgradeable_via_cpi() {
|
|||
"solana_bpf_rust_invoke_and_return",
|
||||
);
|
||||
|
||||
// Deploy upgrade program
|
||||
// Deploy upgradeable program
|
||||
let (program_id, authority_keypair) =
|
||||
load_upgradeable_bpf_program(&bank_client, &mint_keypair, "solana_bpf_rust_upgradeable");
|
||||
|
||||
|
@ -1741,3 +1741,83 @@ fn test_program_bpf_disguised_as_bpf_loader() {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[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 (program_id, authority_keypair) =
|
||||
load_upgradeable_bpf_program(&bank_client, &mint_keypair, "solana_bpf_rust_upgradeable");
|
||||
|
||||
let mut instruction = Instruction::new(
|
||||
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 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(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_pubkey = load_buffer_account(&bank_client, &mint_keypair, &elf);
|
||||
|
||||
// Upgrade program via CPI
|
||||
let mut upgrade_instruction = bpf_loader_upgradeable::upgrade(
|
||||
&program_id,
|
||||
&buffer_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))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1377,11 +1377,15 @@ fn check_instruction_size(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn check_authorized_program(program_id: &Pubkey) -> Result<(), EbpfError<BPFError>> {
|
||||
fn check_authorized_program(
|
||||
program_id: &Pubkey,
|
||||
instruction_data: &[u8],
|
||||
) -> Result<(), EbpfError<BPFError>> {
|
||||
if native_loader::check_id(program_id)
|
||||
|| bpf_loader::check_id(program_id)
|
||||
|| bpf_loader_deprecated::check_id(program_id)
|
||||
|| bpf_loader_upgradeable::check_id(program_id)
|
||||
|| (bpf_loader_upgradeable::check_id(program_id)
|
||||
&& !bpf_loader_upgradeable::is_upgrade_instruction(instruction_data))
|
||||
{
|
||||
return Err(SyscallError::InstructionError(InstructionError::UnsupportedProgramId).into());
|
||||
}
|
||||
|
@ -1432,7 +1436,7 @@ fn call<'a>(
|
|||
MessageProcessor::create_message(&instruction, &keyed_account_refs, &signers)
|
||||
.map_err(SyscallError::InstructionError)?;
|
||||
if invoke_context.is_feature_active(&limit_cpi_loader_invoke::id()) {
|
||||
check_authorized_program(&callee_program_id)?;
|
||||
check_authorized_program(&callee_program_id, &instruction.data)?;
|
||||
}
|
||||
let (mut accounts, mut account_refs) = syscall.translate_accounts(
|
||||
use_loaded_program_accounts,
|
||||
|
|
|
@ -172,6 +172,10 @@ pub fn upgrade(
|
|||
)
|
||||
}
|
||||
|
||||
pub fn is_upgrade_instruction(instruction_data: &[u8]) -> bool {
|
||||
3 == instruction_data[0]
|
||||
}
|
||||
|
||||
/// Returns the instructions required to set a program's authority.
|
||||
pub fn set_authority(
|
||||
program_address: &Pubkey,
|
||||
|
|
Loading…
Reference in New Issue