Allow SetUpgradeAuthority instruction in CPI calls (#16676)

* feat: allow SetAuthority in CLI calls

* chore: clippy match_like_matches_macro

* chore: clippy match_like_matches_macro

* chore: rename CLI to CPI

* chore: move check for cpi authorised instruction to syscalls

* chore: add set_upgrade_authority cpi test

* chore: assert upgrade authority was changed

* feat: gate set_upgrade_authority via cpi with a feature

* chore: move feature to the end of the list

* chore: remove white spaces

* chore: remove white spaces

* chore: update comment to rerun build
This commit is contained in:
Sebastian Bor
2021-04-22 00:06:59 +01:00
committed by GitHub
parent 91b6888e15
commit 1a658c7f31
6 changed files with 187 additions and 40 deletions

View File

@ -2736,6 +2736,7 @@ dependencies = [
"itertools 0.10.0",
"miow 0.2.2",
"net2",
"solana-account-decoder",
"solana-bpf-loader-program",
"solana-cli-output",
"solana-logger 1.7.0",

View File

@ -33,6 +33,7 @@ solana_rbpf = "=0.2.8"
solana-runtime = { path = "../../runtime", version = "=1.7.0" }
solana-sdk = { path = "../../sdk", version = "=1.7.0" }
solana-transaction-status = { path = "../../transaction-status", version = "=1.7.0" }
solana-account-decoder = { path = "../../account-decoder", version = "=1.7.0" }
[[bench]]

View File

@ -4,6 +4,9 @@
extern crate solana_bpf_loader_program;
use itertools::izip;
use solana_account_decoder::parse_bpf_loader::{
parse_bpf_upgradeable_loader, BpfUpgradeableLoaderAccountType,
};
use solana_bpf_loader_program::{
create_vm,
serialization::{deserialize_parameters, serialize_parameters},
@ -42,7 +45,10 @@ 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, sync::Arc};
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";
@ -2085,6 +2091,94 @@ fn test_program_bpf_upgrade_self_via_cpi() {
);
}
#[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() {

View File

@ -21,7 +21,8 @@ use solana_sdk::{
epoch_schedule::EpochSchedule,
feature_set::{
cpi_data_cost, cpi_share_ro_and_exec_accounts, demote_sysvar_write_locks,
enforce_aligned_host_addrs, ristretto_mul_syscall_enabled, sysvar_via_syscall,
enforce_aligned_host_addrs, ristretto_mul_syscall_enabled,
set_upgrade_authority_via_cpi_enabled, sysvar_via_syscall,
},
hash::{Hasher, HASH_BYTES},
ic_msg,
@ -1874,12 +1875,16 @@ fn check_account_infos(
fn check_authorized_program(
program_id: &Pubkey,
instruction_data: &[u8],
invoke_context: &Ref<&mut dyn InvokeContext>,
) -> 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::is_upgrade_instruction(instruction_data))
&& !(bpf_loader_upgradeable::is_upgrade_instruction(instruction_data)
|| (bpf_loader_upgradeable::is_set_authority_instruction(instruction_data)
&& invoke_context
.is_feature_active(&set_upgrade_authority_via_cpi_enabled::id()))))
{
return Err(SyscallError::ProgramNotSupported(*program_id).into());
}
@ -1994,7 +1999,7 @@ fn call<'a>(
}
})
.collect::<Vec<bool>>();
check_authorized_program(&callee_program_id, &instruction.data)?;
check_authorized_program(&callee_program_id, &instruction.data, &invoke_context)?;
let (accounts, account_refs) = syscall.translate_accounts(
&message.account_keys,
&caller_write_privileges,