2019-05-24 16:21:42 -07:00
|
|
|
pub mod alloc;
|
|
|
|
pub mod allocator_bump;
|
2018-10-16 16:33:31 -07:00
|
|
|
pub mod bpf_verifier;
|
2020-08-11 16:11:52 -07:00
|
|
|
pub mod deprecated;
|
|
|
|
pub mod serialization;
|
2020-04-30 01:43:11 -07:00
|
|
|
pub mod syscalls;
|
2020-12-14 15:35:10 -08:00
|
|
|
pub mod upgradeable;
|
|
|
|
pub mod upgradeable_with_jit;
|
2020-12-07 00:49:55 -08:00
|
|
|
pub mod with_jit;
|
2018-10-16 16:33:31 -07:00
|
|
|
|
2020-08-11 16:11:52 -07:00
|
|
|
use crate::{
|
|
|
|
bpf_verifier::VerifierError,
|
|
|
|
serialization::{deserialize_parameters, serialize_parameters},
|
|
|
|
syscalls::SyscallError,
|
|
|
|
};
|
2020-03-26 14:00:26 -07:00
|
|
|
use solana_rbpf::{
|
2020-11-04 09:46:26 -08:00
|
|
|
ebpf::MM_HEAP_START,
|
2020-09-14 17:42:37 -07:00
|
|
|
error::{EbpfError, UserDefinedError},
|
2020-03-26 14:00:26 -07:00
|
|
|
memory_region::MemoryRegion,
|
2020-10-09 13:07:09 -07:00
|
|
|
vm::{Config, EbpfVm, Executable, InstructionMeter},
|
2020-03-26 14:00:26 -07:00
|
|
|
};
|
2020-12-14 15:35:10 -08:00
|
|
|
use solana_runtime::message_processor::MessageProcessor;
|
2020-01-02 18:18:56 -08:00
|
|
|
use solana_sdk::{
|
2020-12-14 15:35:10 -08:00
|
|
|
account_utils::State,
|
2020-08-11 16:11:52 -07:00
|
|
|
bpf_loader, bpf_loader_deprecated,
|
2020-12-14 15:35:10 -08:00
|
|
|
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
|
|
|
clock::Clock,
|
2020-01-31 10:58:07 -08:00
|
|
|
entrypoint::SUCCESS,
|
2021-01-19 16:24:44 -08:00
|
|
|
feature_set::{bpf_compute_budget_balancing, prevent_upgrade_and_invoke},
|
2020-01-02 18:18:56 -08:00
|
|
|
instruction::InstructionError,
|
2020-12-14 15:35:10 -08:00
|
|
|
keyed_account::{from_keyed_account, next_keyed_account, KeyedAccount},
|
2020-01-02 18:18:56 -08:00
|
|
|
loader_instruction::LoaderInstruction,
|
2020-12-14 15:35:10 -08:00
|
|
|
loader_upgradeable_instruction::UpgradeableLoaderInstruction,
|
2020-11-12 12:44:37 -08:00
|
|
|
process_instruction::{stable_log, ComputeMeter, Executor, InvokeContext},
|
2020-06-17 10:39:14 -07:00
|
|
|
program_utils::limited_deserialize,
|
2020-01-02 18:18:56 -08:00
|
|
|
pubkey::Pubkey,
|
2020-12-14 15:35:10 -08:00
|
|
|
rent::Rent,
|
|
|
|
system_instruction,
|
2020-01-02 18:18:56 -08:00
|
|
|
};
|
2020-10-20 09:05:45 -07:00
|
|
|
use std::{cell::RefCell, fmt::Debug, rc::Rc, sync::Arc};
|
2020-02-14 13:59:03 -08:00
|
|
|
use thiserror::Error;
|
2019-05-24 16:21:42 -07:00
|
|
|
|
2020-08-14 12:32:45 -07:00
|
|
|
solana_sdk::declare_builtin!(
|
2019-12-03 17:55:18 -08:00
|
|
|
solana_sdk::bpf_loader::ID,
|
2019-11-20 16:32:19 -08:00
|
|
|
solana_bpf_loader_program,
|
2020-08-14 12:32:45 -07:00
|
|
|
solana_bpf_loader_program::process_instruction
|
2019-11-20 16:32:19 -08:00
|
|
|
);
|
|
|
|
|
2020-09-14 17:42:37 -07:00
|
|
|
/// Errors returned by functions the BPF Loader registers with the VM
|
2020-08-26 14:48:51 -07:00
|
|
|
#[derive(Debug, Error, PartialEq)]
|
2020-03-26 14:00:26 -07:00
|
|
|
pub enum BPFError {
|
|
|
|
#[error("{0}")]
|
|
|
|
VerifierError(#[from] VerifierError),
|
|
|
|
#[error("{0}")]
|
2020-04-30 01:43:11 -07:00
|
|
|
SyscallError(#[from] SyscallError),
|
2020-03-26 14:00:26 -07:00
|
|
|
}
|
|
|
|
impl UserDefinedError for BPFError {}
|
|
|
|
|
2020-09-14 17:42:37 -07:00
|
|
|
/// Point all log messages to the log collector
|
2020-11-12 12:44:37 -08:00
|
|
|
macro_rules! log {
|
2020-06-13 13:20:08 -07:00
|
|
|
($logger:ident, $message:expr) => {
|
2021-01-08 09:37:57 -08:00
|
|
|
if let Ok(logger) = $logger.try_borrow_mut() {
|
2020-06-13 13:20:08 -07:00
|
|
|
if logger.log_enabled() {
|
|
|
|
logger.log($message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
($logger:ident, $fmt:expr, $($arg:tt)*) => {
|
2021-01-08 09:37:57 -08:00
|
|
|
if let Ok(logger) = $logger.try_borrow_mut() {
|
2020-09-14 17:42:37 -07:00
|
|
|
if logger.log_enabled() {
|
|
|
|
logger.log(&format!($fmt, $($arg)*));
|
|
|
|
}
|
2020-06-13 13:20:08 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-09-29 14:36:30 -07:00
|
|
|
fn map_ebpf_error(
|
|
|
|
invoke_context: &mut dyn InvokeContext,
|
|
|
|
e: EbpfError<BPFError>,
|
|
|
|
) -> InstructionError {
|
|
|
|
let logger = invoke_context.get_logger();
|
|
|
|
log!(logger, "{}", e);
|
|
|
|
InstructionError::InvalidAccountData
|
|
|
|
}
|
|
|
|
|
2020-09-14 17:42:37 -07:00
|
|
|
pub fn create_and_cache_executor(
|
2020-12-14 15:35:10 -08:00
|
|
|
key: &Pubkey,
|
|
|
|
data: &[u8],
|
2020-09-14 17:42:37 -07:00
|
|
|
invoke_context: &mut dyn InvokeContext,
|
2020-12-07 00:49:55 -08:00
|
|
|
use_jit: bool,
|
2020-09-14 17:42:37 -07:00
|
|
|
) -> Result<Arc<BPFExecutor>, InstructionError> {
|
2020-12-15 09:54:07 -08:00
|
|
|
let logger = invoke_context.get_logger();
|
|
|
|
|
2020-11-24 09:00:19 -08:00
|
|
|
let bpf_compute_budget = invoke_context.get_bpf_compute_budget();
|
2020-12-14 15:35:10 -08:00
|
|
|
let mut program = Executable::<BPFError, ThisInstructionMeter>::from_elf(
|
|
|
|
data,
|
2020-11-24 09:00:19 -08:00
|
|
|
None,
|
|
|
|
Config {
|
|
|
|
max_call_depth: bpf_compute_budget.max_call_depth,
|
|
|
|
stack_frame_size: bpf_compute_budget.stack_frame_size,
|
|
|
|
enable_instruction_meter: true,
|
|
|
|
enable_instruction_tracing: false,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.map_err(|e| map_ebpf_error(invoke_context, e))?;
|
2020-12-14 15:35:10 -08:00
|
|
|
let (_, elf_bytes) = program
|
2020-09-29 14:36:30 -07:00
|
|
|
.get_text_bytes()
|
|
|
|
.map_err(|e| map_ebpf_error(invoke_context, e))?;
|
|
|
|
bpf_verifier::check(
|
|
|
|
elf_bytes,
|
2020-10-28 13:16:13 -07:00
|
|
|
!invoke_context.is_feature_active(&bpf_compute_budget_balancing::id()),
|
2020-09-14 17:42:37 -07:00
|
|
|
)
|
2020-09-29 14:36:30 -07:00
|
|
|
.map_err(|e| map_ebpf_error(invoke_context, EbpfError::UserError(e)))?;
|
2020-12-15 09:54:07 -08:00
|
|
|
let syscall_registry = syscalls::register_syscalls(invoke_context).map_err(|e| {
|
|
|
|
log!(logger, "Failed to register syscalls: {}", e);
|
|
|
|
InstructionError::ProgramEnvironmentSetupFailure
|
|
|
|
})?;
|
2020-12-14 15:35:10 -08:00
|
|
|
program.set_syscall_registry(syscall_registry);
|
2020-12-15 09:54:07 -08:00
|
|
|
if use_jit {
|
|
|
|
if let Err(err) = program.jit_compile() {
|
|
|
|
log!(logger, "Failed to compile program {:?}", err);
|
|
|
|
return Err(InstructionError::ProgramFailedToCompile);
|
|
|
|
}
|
2020-11-24 09:00:19 -08:00
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
let executor = Arc::new(BPFExecutor { program });
|
|
|
|
invoke_context.add_executor(key, executor.clone());
|
2020-09-14 17:42:37 -07:00
|
|
|
Ok(executor)
|
2020-08-21 15:31:19 -07:00
|
|
|
}
|
2020-09-14 17:42:37 -07:00
|
|
|
|
2020-12-14 15:35:10 -08:00
|
|
|
fn write_program_data(
|
2021-01-08 09:37:57 -08:00
|
|
|
data: &mut [u8],
|
2020-12-14 15:35:10 -08:00
|
|
|
offset: usize,
|
|
|
|
bytes: &[u8],
|
|
|
|
invoke_context: &mut dyn InvokeContext,
|
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
let logger = invoke_context.get_logger();
|
|
|
|
|
|
|
|
let len = bytes.len();
|
2021-01-08 09:37:57 -08:00
|
|
|
if data.len() < offset + len {
|
|
|
|
log!(logger, "Write overflow: {} < {}", data.len(), offset + len);
|
2020-12-14 15:35:10 -08:00
|
|
|
return Err(InstructionError::AccountDataTooSmall);
|
|
|
|
}
|
2021-01-08 09:37:57 -08:00
|
|
|
data[offset..offset + len].copy_from_slice(&bytes);
|
2020-12-14 15:35:10 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-12-23 19:04:48 -08:00
|
|
|
fn check_loader_id(id: &Pubkey) -> bool {
|
|
|
|
bpf_loader::check_id(id)
|
|
|
|
|| bpf_loader_deprecated::check_id(id)
|
|
|
|
|| bpf_loader_upgradeable::check_id(id)
|
|
|
|
}
|
|
|
|
|
2020-11-04 09:46:26 -08:00
|
|
|
/// Default program heap size, allocators
|
|
|
|
/// are expected to enforce this
|
|
|
|
const DEFAULT_HEAP_SIZE: usize = 32 * 1024;
|
|
|
|
|
2020-09-14 17:42:37 -07:00
|
|
|
/// Create the BPF virtual machine
|
|
|
|
pub fn create_vm<'a>(
|
|
|
|
loader_id: &'a Pubkey,
|
2020-12-14 15:35:10 -08:00
|
|
|
program: &'a dyn Executable<BPFError, ThisInstructionMeter>,
|
2020-11-24 09:00:19 -08:00
|
|
|
parameter_bytes: &mut [u8],
|
2020-09-14 17:42:37 -07:00
|
|
|
parameter_accounts: &'a [KeyedAccount<'a>],
|
|
|
|
invoke_context: &'a mut dyn InvokeContext,
|
2020-11-04 09:46:26 -08:00
|
|
|
) -> Result<EbpfVm<'a, BPFError, ThisInstructionMeter>, EbpfError<BPFError>> {
|
|
|
|
let heap = vec![0_u8; DEFAULT_HEAP_SIZE];
|
2020-11-24 09:00:19 -08:00
|
|
|
let heap_region = MemoryRegion::new_from_slice(&heap, MM_HEAP_START, 0, true);
|
2020-12-14 15:35:10 -08:00
|
|
|
let mut vm = EbpfVm::new(program, parameter_bytes, &[heap_region])?;
|
2020-11-24 09:00:19 -08:00
|
|
|
syscalls::bind_syscall_context_objects(
|
|
|
|
loader_id,
|
|
|
|
&mut vm,
|
|
|
|
parameter_accounts,
|
|
|
|
invoke_context,
|
|
|
|
heap,
|
2020-10-09 13:07:09 -07:00
|
|
|
)?;
|
2020-11-04 09:46:26 -08:00
|
|
|
Ok(vm)
|
2020-08-21 15:31:19 -07:00
|
|
|
}
|
|
|
|
|
2020-12-14 15:35:10 -08:00
|
|
|
pub fn process_instruction(
|
|
|
|
program_id: &Pubkey,
|
|
|
|
keyed_accounts: &[KeyedAccount],
|
|
|
|
instruction_data: &[u8],
|
|
|
|
invoke_context: &mut dyn InvokeContext,
|
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
process_instruction_common(
|
|
|
|
program_id,
|
|
|
|
keyed_accounts,
|
|
|
|
instruction_data,
|
|
|
|
invoke_context,
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn process_instruction_jit(
|
2018-11-17 17:02:14 -08:00
|
|
|
program_id: &Pubkey,
|
2020-01-22 17:54:06 -08:00
|
|
|
keyed_accounts: &[KeyedAccount],
|
2020-01-10 13:20:15 -08:00
|
|
|
instruction_data: &[u8],
|
2020-04-28 14:33:56 -07:00
|
|
|
invoke_context: &mut dyn InvokeContext,
|
2019-03-18 09:05:03 -07:00
|
|
|
) -> Result<(), InstructionError> {
|
2020-12-14 15:35:10 -08:00
|
|
|
process_instruction_common(
|
|
|
|
program_id,
|
|
|
|
keyed_accounts,
|
|
|
|
instruction_data,
|
|
|
|
invoke_context,
|
|
|
|
true,
|
|
|
|
)
|
|
|
|
}
|
2020-03-05 10:57:35 -08:00
|
|
|
|
2020-12-14 15:35:10 -08:00
|
|
|
fn process_instruction_common(
|
|
|
|
program_id: &Pubkey,
|
|
|
|
keyed_accounts: &[KeyedAccount],
|
|
|
|
instruction_data: &[u8],
|
|
|
|
invoke_context: &mut dyn InvokeContext,
|
|
|
|
use_jit: bool,
|
|
|
|
) -> Result<(), InstructionError> {
|
2020-06-13 13:20:08 -07:00
|
|
|
let logger = invoke_context.get_logger();
|
|
|
|
|
2020-12-14 15:35:10 -08:00
|
|
|
let account_iter = &mut keyed_accounts.iter();
|
|
|
|
let first_account = next_keyed_account(account_iter)?;
|
|
|
|
if first_account.executable()? {
|
2020-12-23 19:04:48 -08:00
|
|
|
if first_account.unsigned_key() != program_id {
|
|
|
|
log!(logger, "Program id mismatch");
|
|
|
|
return Err(InstructionError::IncorrectProgramId);
|
|
|
|
}
|
|
|
|
|
|
|
|
let (program, keyed_accounts, offset) =
|
|
|
|
if bpf_loader_upgradeable::check_id(&first_account.owner()?) {
|
|
|
|
if let UpgradeableLoaderState::Program {
|
|
|
|
programdata_address,
|
|
|
|
} = first_account.state()?
|
|
|
|
{
|
|
|
|
let programdata = next_keyed_account(account_iter)?;
|
|
|
|
if programdata_address != *programdata.unsigned_key() {
|
|
|
|
log!(logger, "Wrong ProgramData account for this Program account");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
(
|
|
|
|
programdata,
|
|
|
|
&keyed_accounts[1..],
|
|
|
|
UpgradeableLoaderState::programdata_data_offset()?,
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
log!(logger, "Invalid Program account");
|
|
|
|
return Err(InstructionError::InvalidAccountData);
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
} else {
|
2020-12-23 19:04:48 -08:00
|
|
|
(first_account, keyed_accounts, 0)
|
|
|
|
};
|
2020-12-14 15:35:10 -08:00
|
|
|
|
2020-12-30 12:02:29 -08:00
|
|
|
let loader_id = &program.owner()?;
|
|
|
|
|
|
|
|
if !check_loader_id(loader_id) {
|
2020-12-14 15:35:10 -08:00
|
|
|
log!(logger, "Executable account not owned by the BPF loader");
|
|
|
|
return Err(InstructionError::IncorrectProgramId);
|
|
|
|
}
|
|
|
|
|
2021-01-08 14:43:24 -08:00
|
|
|
let executor = match invoke_context.get_executor(program_id) {
|
2020-09-14 17:42:37 -07:00
|
|
|
Some(executor) => executor,
|
2020-12-14 15:35:10 -08:00
|
|
|
None => create_and_cache_executor(
|
2021-01-08 14:43:24 -08:00
|
|
|
program_id,
|
2020-12-14 15:35:10 -08:00
|
|
|
&program.try_account_ref()?.data[offset..],
|
|
|
|
invoke_context,
|
|
|
|
use_jit,
|
|
|
|
)?,
|
2020-09-14 17:42:37 -07:00
|
|
|
};
|
2020-12-07 00:49:55 -08:00
|
|
|
executor.execute(
|
2020-12-30 12:02:29 -08:00
|
|
|
loader_id,
|
2021-01-08 14:43:24 -08:00
|
|
|
program_id,
|
2020-12-07 00:49:55 -08:00
|
|
|
keyed_accounts,
|
|
|
|
instruction_data,
|
|
|
|
invoke_context,
|
|
|
|
use_jit,
|
|
|
|
)?
|
2020-12-14 15:35:10 -08:00
|
|
|
} else {
|
2020-12-23 19:04:48 -08:00
|
|
|
if !check_loader_id(program_id) {
|
|
|
|
log!(logger, "Invalid BPF loader id");
|
|
|
|
return Err(InstructionError::IncorrectProgramId);
|
|
|
|
}
|
|
|
|
|
|
|
|
if bpf_loader_upgradeable::check_id(program_id) {
|
|
|
|
process_loader_upgradeable_instruction(
|
|
|
|
program_id,
|
|
|
|
keyed_accounts,
|
|
|
|
instruction_data,
|
|
|
|
invoke_context,
|
|
|
|
use_jit,
|
|
|
|
)?;
|
|
|
|
} else {
|
|
|
|
process_loader_instruction(
|
|
|
|
program_id,
|
|
|
|
keyed_accounts,
|
|
|
|
instruction_data,
|
|
|
|
invoke_context,
|
|
|
|
use_jit,
|
|
|
|
)?;
|
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn process_loader_upgradeable_instruction(
|
|
|
|
program_id: &Pubkey,
|
|
|
|
keyed_accounts: &[KeyedAccount],
|
|
|
|
instruction_data: &[u8],
|
|
|
|
invoke_context: &mut dyn InvokeContext,
|
|
|
|
use_jit: bool,
|
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
let logger = invoke_context.get_logger();
|
|
|
|
let account_iter = &mut keyed_accounts.iter();
|
|
|
|
|
|
|
|
match limited_deserialize(instruction_data)? {
|
|
|
|
UpgradeableLoaderInstruction::InitializeBuffer => {
|
|
|
|
let buffer = next_keyed_account(account_iter)?;
|
2021-01-08 09:37:57 -08:00
|
|
|
let authority = next_keyed_account(account_iter)
|
|
|
|
.ok()
|
|
|
|
.map(|account| account.unsigned_key());
|
|
|
|
|
2020-12-14 15:35:10 -08:00
|
|
|
if UpgradeableLoaderState::Uninitialized != buffer.state()? {
|
|
|
|
log!(logger, "Buffer account already initialized");
|
|
|
|
return Err(InstructionError::AccountAlreadyInitialized);
|
|
|
|
}
|
2021-01-08 09:37:57 -08:00
|
|
|
buffer.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: authority.cloned(),
|
|
|
|
})?;
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
UpgradeableLoaderInstruction::Write { offset, bytes } => {
|
|
|
|
let buffer = next_keyed_account(account_iter)?;
|
2021-01-08 09:37:57 -08:00
|
|
|
let authority = next_keyed_account(account_iter)?;
|
|
|
|
|
|
|
|
if let UpgradeableLoaderState::Buffer { authority_address } = buffer.state()? {
|
|
|
|
if authority_address == None {
|
|
|
|
log!(logger, "Buffer is immutable");
|
|
|
|
return Err(InstructionError::Immutable); // TODO better error code
|
|
|
|
}
|
|
|
|
if authority_address != Some(*authority.unsigned_key()) {
|
|
|
|
log!(logger, "Incorrect buffer authority provided");
|
|
|
|
return Err(InstructionError::IncorrectAuthority);
|
|
|
|
}
|
|
|
|
if authority.signer_key().is_none() {
|
|
|
|
log!(logger, "Buffer authority did not sign");
|
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
|
|
|
} else {
|
2020-12-14 15:35:10 -08:00
|
|
|
log!(logger, "Invalid Buffer account");
|
|
|
|
return Err(InstructionError::InvalidAccountData);
|
|
|
|
}
|
|
|
|
write_program_data(
|
2021-01-08 09:37:57 -08:00
|
|
|
&mut buffer.try_account_ref_mut()?.data,
|
2020-12-14 15:35:10 -08:00
|
|
|
UpgradeableLoaderState::buffer_data_offset()? + offset as usize,
|
|
|
|
&bytes,
|
|
|
|
invoke_context,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len } => {
|
|
|
|
let payer = next_keyed_account(account_iter)?;
|
|
|
|
let programdata = next_keyed_account(account_iter)?;
|
|
|
|
let program = next_keyed_account(account_iter)?;
|
|
|
|
let buffer = next_keyed_account(account_iter)?;
|
|
|
|
let rent = from_keyed_account::<Rent>(next_keyed_account(account_iter)?)?;
|
|
|
|
let clock = from_keyed_account::<Clock>(next_keyed_account(account_iter)?)?;
|
|
|
|
let system = next_keyed_account(account_iter)?;
|
|
|
|
let authority = next_keyed_account(account_iter)
|
|
|
|
.ok()
|
|
|
|
.map(|account| account.unsigned_key());
|
|
|
|
|
|
|
|
// Verify Program account
|
|
|
|
|
|
|
|
if UpgradeableLoaderState::Uninitialized != program.state()? {
|
|
|
|
log!(logger, "Program account already initialized");
|
|
|
|
return Err(InstructionError::AccountAlreadyInitialized);
|
|
|
|
}
|
2020-12-17 01:02:31 -08:00
|
|
|
if program.data_len()? < UpgradeableLoaderState::program_len()? {
|
|
|
|
log!(logger, "Program account too small");
|
|
|
|
return Err(InstructionError::AccountDataTooSmall);
|
|
|
|
}
|
|
|
|
if program.lamports()? < rent.minimum_balance(program.data_len()?) {
|
2020-12-14 15:35:10 -08:00
|
|
|
log!(logger, "Program account not rent-exempt");
|
|
|
|
return Err(InstructionError::ExecutableAccountNotRentExempt);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify Buffer account
|
|
|
|
|
2021-01-08 09:37:57 -08:00
|
|
|
if let UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: _,
|
|
|
|
} = buffer.state()?
|
|
|
|
{
|
|
|
|
} else {
|
2020-12-14 15:35:10 -08:00
|
|
|
log!(logger, "Invalid Buffer account");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
|
|
|
|
let buffer_data_offset = UpgradeableLoaderState::buffer_data_offset()?;
|
|
|
|
let buffer_data_len = buffer.data_len()?.saturating_sub(buffer_data_offset);
|
|
|
|
let programdata_data_offset = UpgradeableLoaderState::programdata_data_offset()?;
|
|
|
|
|
|
|
|
if max_data_len < buffer_data_len {
|
|
|
|
log!(logger, "Max data length is too small to hold Buffer data");
|
|
|
|
return Err(InstructionError::AccountDataTooSmall);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create ProgramData account
|
|
|
|
|
|
|
|
let (derived_address, bump_seed) =
|
|
|
|
Pubkey::find_program_address(&[program.unsigned_key().as_ref()], &program_id);
|
|
|
|
if derived_address != *programdata.unsigned_key() {
|
|
|
|
log!(logger, "ProgramData address is not derived");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
|
|
|
|
MessageProcessor::native_invoke(
|
|
|
|
invoke_context,
|
|
|
|
system_instruction::create_account(
|
|
|
|
payer.unsigned_key(),
|
|
|
|
programdata.unsigned_key(),
|
|
|
|
1.max(
|
|
|
|
rent.minimum_balance(UpgradeableLoaderState::programdata_len(
|
|
|
|
max_data_len,
|
|
|
|
)?),
|
|
|
|
),
|
|
|
|
UpgradeableLoaderState::programdata_len(max_data_len)? as u64,
|
|
|
|
program_id,
|
|
|
|
),
|
|
|
|
&[payer, programdata, system],
|
|
|
|
&[&[program.unsigned_key().as_ref(), &[bump_seed]]],
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// Load and verify the program bits
|
|
|
|
let _ = create_and_cache_executor(
|
2021-01-08 14:43:24 -08:00
|
|
|
program_id,
|
2020-12-14 15:35:10 -08:00
|
|
|
&buffer.try_account_ref()?.data[buffer_data_offset..],
|
|
|
|
invoke_context,
|
|
|
|
use_jit,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// Update the ProgramData account and record the program bits
|
|
|
|
|
|
|
|
programdata.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot: clock.slot,
|
|
|
|
upgrade_authority_address: authority.cloned(),
|
|
|
|
})?;
|
|
|
|
programdata.try_account_ref_mut()?.data
|
|
|
|
[programdata_data_offset..programdata_data_offset + buffer_data_len]
|
|
|
|
.copy_from_slice(&buffer.try_account_ref()?.data[buffer_data_offset..]);
|
|
|
|
|
|
|
|
// Update the Program account
|
|
|
|
|
|
|
|
program.set_state(&UpgradeableLoaderState::Program {
|
|
|
|
programdata_address: *programdata.unsigned_key(),
|
|
|
|
})?;
|
|
|
|
program.try_account_ref_mut()?.executable = true;
|
|
|
|
|
|
|
|
// Drain the Buffer account back to the payer
|
|
|
|
|
|
|
|
payer.try_account_ref_mut()?.lamports += buffer.lamports()?;
|
|
|
|
buffer.try_account_ref_mut()?.lamports = 0;
|
|
|
|
|
|
|
|
log!(logger, "Deployed program {:?}", program.unsigned_key());
|
|
|
|
}
|
|
|
|
UpgradeableLoaderInstruction::Upgrade => {
|
|
|
|
let programdata = next_keyed_account(account_iter)?;
|
|
|
|
let program = next_keyed_account(account_iter)?;
|
|
|
|
let buffer = next_keyed_account(account_iter)?;
|
|
|
|
let spill = next_keyed_account(account_iter)?;
|
|
|
|
let rent = from_keyed_account::<Rent>(next_keyed_account(account_iter)?)?;
|
|
|
|
let clock = from_keyed_account::<Clock>(next_keyed_account(account_iter)?)?;
|
|
|
|
let authority = next_keyed_account(account_iter)?;
|
|
|
|
|
|
|
|
// Verify Program account
|
|
|
|
|
|
|
|
if !program.executable()? {
|
|
|
|
log!(logger, "Program account not executable");
|
|
|
|
return Err(InstructionError::AccountNotExecutable);
|
|
|
|
}
|
2021-01-19 16:24:44 -08:00
|
|
|
if !program.is_writable()
|
|
|
|
&& invoke_context.is_feature_active(&prevent_upgrade_and_invoke::id())
|
|
|
|
{
|
|
|
|
log!(logger, "Program account not writeable");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
if &program.owner()? != program_id {
|
|
|
|
log!(logger, "Program account not owned by loader");
|
|
|
|
return Err(InstructionError::IncorrectProgramId);
|
|
|
|
}
|
|
|
|
if let UpgradeableLoaderState::Program {
|
|
|
|
programdata_address,
|
|
|
|
} = program.state()?
|
|
|
|
{
|
|
|
|
if programdata_address != *programdata.unsigned_key() {
|
|
|
|
log!(logger, "Program and ProgramData account mismatch");
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log!(logger, "Invalid Program account");
|
|
|
|
return Err(InstructionError::InvalidAccountData);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify Buffer account
|
|
|
|
|
2021-01-08 09:37:57 -08:00
|
|
|
if let UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: _,
|
|
|
|
} = buffer.state()?
|
|
|
|
{
|
|
|
|
} else {
|
2020-12-14 15:35:10 -08:00
|
|
|
log!(logger, "Invalid Buffer account");
|
2021-01-08 09:37:57 -08:00
|
|
|
return Err(InstructionError::InvalidArgument);
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
let buffer_data_offset = UpgradeableLoaderState::buffer_data_offset()?;
|
|
|
|
let buffer_data_len = buffer.data_len()?.saturating_sub(buffer_data_offset);
|
|
|
|
let programdata_data_offset = UpgradeableLoaderState::programdata_data_offset()?;
|
|
|
|
let programdata_balance_required = 1.max(rent.minimum_balance(programdata.data_len()?));
|
|
|
|
|
|
|
|
// Verify ProgramData account
|
|
|
|
|
|
|
|
if programdata.data_len()? < UpgradeableLoaderState::programdata_len(buffer_data_len)? {
|
|
|
|
log!(logger, "ProgramData account not large enough");
|
|
|
|
return Err(InstructionError::AccountDataTooSmall);
|
|
|
|
}
|
|
|
|
if programdata.lamports()? + buffer.lamports()? < programdata_balance_required {
|
|
|
|
log!(logger, "Buffer account balance too low to fund upgrade");
|
|
|
|
return Err(InstructionError::InsufficientFunds);
|
|
|
|
}
|
|
|
|
if let UpgradeableLoaderState::ProgramData {
|
|
|
|
slot: _,
|
|
|
|
upgrade_authority_address,
|
|
|
|
} = programdata.state()?
|
|
|
|
{
|
|
|
|
if upgrade_authority_address == None {
|
|
|
|
log!(logger, "Program not upgradeable");
|
2021-01-08 09:37:57 -08:00
|
|
|
return Err(InstructionError::Immutable);
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
if upgrade_authority_address != Some(*authority.unsigned_key()) {
|
2021-01-08 09:37:57 -08:00
|
|
|
log!(logger, "Incorrect upgrade authority provided");
|
|
|
|
return Err(InstructionError::IncorrectAuthority);
|
2020-09-14 17:42:37 -07:00
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
if authority.signer_key().is_none() {
|
|
|
|
log!(logger, "Upgrade authority did not sign");
|
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
2020-09-14 17:42:37 -07:00
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
} else {
|
|
|
|
log!(logger, "Invalid ProgramData account");
|
|
|
|
return Err(InstructionError::InvalidAccountData);
|
2020-09-14 17:42:37 -07:00
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
|
|
|
|
// Load and verify the program bits
|
|
|
|
|
|
|
|
let _ = create_and_cache_executor(
|
2021-01-08 14:43:24 -08:00
|
|
|
program.unsigned_key(),
|
2020-12-14 15:35:10 -08:00
|
|
|
&buffer.try_account_ref()?.data[buffer_data_offset..],
|
|
|
|
invoke_context,
|
|
|
|
use_jit,
|
|
|
|
)?;
|
|
|
|
|
2020-12-17 01:02:31 -08:00
|
|
|
// Update the ProgramData account, record the upgraded data, and zero
|
|
|
|
// the rest
|
2020-12-14 15:35:10 -08:00
|
|
|
|
|
|
|
programdata.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot: clock.slot,
|
|
|
|
upgrade_authority_address: Some(*authority.unsigned_key()),
|
|
|
|
})?;
|
|
|
|
programdata.try_account_ref_mut()?.data
|
|
|
|
[programdata_data_offset..programdata_data_offset + buffer_data_len]
|
|
|
|
.copy_from_slice(&buffer.try_account_ref()?.data[buffer_data_offset..]);
|
2020-12-17 01:02:31 -08:00
|
|
|
for i in &mut programdata.try_account_ref_mut()?.data
|
|
|
|
[programdata_data_offset + buffer_data_len..]
|
|
|
|
{
|
|
|
|
*i = 0
|
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
|
|
|
|
// Fund ProgramData to rent-exemption, spill the rest
|
|
|
|
|
|
|
|
spill.try_account_ref_mut()?.lamports += (programdata.lamports()?
|
|
|
|
+ buffer.lamports()?)
|
|
|
|
.saturating_sub(programdata_balance_required);
|
|
|
|
buffer.try_account_ref_mut()?.lamports = 0;
|
|
|
|
programdata.try_account_ref_mut()?.lamports = programdata_balance_required;
|
|
|
|
|
|
|
|
log!(logger, "Upgraded program {:?}", program.unsigned_key());
|
|
|
|
}
|
|
|
|
UpgradeableLoaderInstruction::SetAuthority => {
|
2021-01-08 09:37:57 -08:00
|
|
|
let account = next_keyed_account(account_iter)?;
|
2020-12-14 15:35:10 -08:00
|
|
|
let present_authority = next_keyed_account(account_iter)?;
|
|
|
|
let new_authority = next_keyed_account(account_iter)
|
|
|
|
.ok()
|
|
|
|
.map(|account| account.unsigned_key());
|
|
|
|
|
2021-01-08 09:37:57 -08:00
|
|
|
match account.state()? {
|
|
|
|
UpgradeableLoaderState::Buffer { authority_address } => {
|
|
|
|
if authority_address == None {
|
|
|
|
log!(logger, "Buffer is immutable");
|
|
|
|
return Err(InstructionError::Immutable);
|
|
|
|
}
|
|
|
|
if authority_address != Some(*present_authority.unsigned_key()) {
|
|
|
|
log!(logger, "Incorrect buffer authority provided");
|
|
|
|
return Err(InstructionError::IncorrectAuthority);
|
|
|
|
}
|
|
|
|
if present_authority.signer_key().is_none() {
|
|
|
|
log!(logger, "Buffer authority did not sign");
|
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
|
|
|
account.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: new_authority.cloned(),
|
|
|
|
})?;
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
2021-01-08 09:37:57 -08:00
|
|
|
UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address,
|
|
|
|
} => {
|
|
|
|
if upgrade_authority_address == None {
|
|
|
|
log!(logger, "Program not upgradeable");
|
|
|
|
return Err(InstructionError::Immutable);
|
|
|
|
}
|
|
|
|
if upgrade_authority_address != Some(*present_authority.unsigned_key()) {
|
|
|
|
log!(logger, "Incorrect upgrade authority provided");
|
|
|
|
return Err(InstructionError::IncorrectAuthority);
|
|
|
|
}
|
|
|
|
if present_authority.signer_key().is_none() {
|
|
|
|
log!(logger, "Upgrade authority did not sign");
|
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
|
|
|
account.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: new_authority.cloned(),
|
|
|
|
})?;
|
2020-09-14 17:42:37 -07:00
|
|
|
}
|
2021-01-08 09:37:57 -08:00
|
|
|
_ => {
|
|
|
|
log!(logger, "Account does not support authorities");
|
|
|
|
return Err(InstructionError::InvalidAccountData);
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
2020-09-14 17:42:37 -07:00
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
|
|
|
|
log!(logger, "New authority {:?}", new_authority);
|
2020-09-14 17:42:37 -07:00
|
|
|
}
|
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
|
2020-09-14 17:42:37 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-12-14 15:35:10 -08:00
|
|
|
fn process_loader_instruction(
|
2020-12-07 00:49:55 -08:00
|
|
|
program_id: &Pubkey,
|
|
|
|
keyed_accounts: &[KeyedAccount],
|
|
|
|
instruction_data: &[u8],
|
|
|
|
invoke_context: &mut dyn InvokeContext,
|
2020-12-14 15:35:10 -08:00
|
|
|
use_jit: bool,
|
2020-12-07 00:49:55 -08:00
|
|
|
) -> Result<(), InstructionError> {
|
2020-12-14 15:35:10 -08:00
|
|
|
let logger = invoke_context.get_logger();
|
|
|
|
let account_iter = &mut keyed_accounts.iter();
|
2020-12-07 00:49:55 -08:00
|
|
|
|
2020-12-14 15:35:10 -08:00
|
|
|
let program = next_keyed_account(account_iter)?;
|
|
|
|
if program.owner()? != *program_id {
|
|
|
|
log!(logger, "Executable account not owned by the BPF loader");
|
|
|
|
return Err(InstructionError::IncorrectProgramId);
|
|
|
|
}
|
|
|
|
match limited_deserialize(instruction_data)? {
|
|
|
|
LoaderInstruction::Write { offset, bytes } => {
|
2021-01-08 09:37:57 -08:00
|
|
|
if program.signer_key().is_none() {
|
|
|
|
log!(logger, "Program account did not sign");
|
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
|
|
|
write_program_data(
|
|
|
|
&mut program.try_account_ref_mut()?.data,
|
|
|
|
offset as usize,
|
|
|
|
&bytes,
|
|
|
|
invoke_context,
|
|
|
|
)?;
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
LoaderInstruction::Finalize => {
|
|
|
|
if program.signer_key().is_none() {
|
|
|
|
log!(logger, "key[0] did not sign the transaction");
|
|
|
|
return Err(InstructionError::MissingRequiredSignature);
|
|
|
|
}
|
|
|
|
|
|
|
|
let _ = create_and_cache_executor(
|
|
|
|
program.unsigned_key(),
|
|
|
|
&program.try_account_ref()?.data,
|
|
|
|
invoke_context,
|
|
|
|
use_jit,
|
|
|
|
)?;
|
|
|
|
program.try_account_ref_mut()?.executable = true;
|
|
|
|
log!(logger, "Finalized account {:?}", program.unsigned_key());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2020-12-07 00:49:55 -08:00
|
|
|
}
|
|
|
|
|
2020-09-14 17:42:37 -07:00
|
|
|
/// Passed to the VM to enforce the compute budget
|
2020-11-04 09:46:26 -08:00
|
|
|
pub struct ThisInstructionMeter {
|
|
|
|
pub compute_meter: Rc<RefCell<dyn ComputeMeter>>,
|
2020-09-14 17:42:37 -07:00
|
|
|
}
|
2020-10-15 15:55:37 -07:00
|
|
|
impl ThisInstructionMeter {
|
|
|
|
fn new(compute_meter: Rc<RefCell<dyn ComputeMeter>>) -> Self {
|
|
|
|
Self { compute_meter }
|
|
|
|
}
|
|
|
|
}
|
2020-09-14 17:42:37 -07:00
|
|
|
impl InstructionMeter for ThisInstructionMeter {
|
|
|
|
fn consume(&mut self, amount: u64) {
|
|
|
|
// 1 to 1 instruction to compute unit mapping
|
|
|
|
// ignore error, Ebpf will bail if exceeded
|
|
|
|
let _ = self.compute_meter.borrow_mut().consume(amount);
|
|
|
|
}
|
|
|
|
fn get_remaining(&self) -> u64 {
|
|
|
|
self.compute_meter.borrow().get_remaining()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// BPF Loader's Executor implementation
|
|
|
|
pub struct BPFExecutor {
|
2020-12-14 15:35:10 -08:00
|
|
|
program: Box<dyn Executable<BPFError, ThisInstructionMeter>>,
|
2020-09-14 17:42:37 -07:00
|
|
|
}
|
2020-10-20 09:05:45 -07:00
|
|
|
|
|
|
|
// Well, implement Debug for solana_rbpf::vm::Executable in solana-rbpf...
|
|
|
|
impl Debug for BPFExecutor {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
write!(f, "BPFExecutor({:p})", self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-14 17:42:37 -07:00
|
|
|
impl Executor for BPFExecutor {
|
|
|
|
fn execute(
|
|
|
|
&self,
|
2020-12-30 12:02:29 -08:00
|
|
|
loader_id: &Pubkey,
|
2021-01-04 13:45:05 -08:00
|
|
|
program_id: &Pubkey,
|
2020-09-14 17:42:37 -07:00
|
|
|
keyed_accounts: &[KeyedAccount],
|
|
|
|
instruction_data: &[u8],
|
|
|
|
invoke_context: &mut dyn InvokeContext,
|
2020-12-07 00:49:55 -08:00
|
|
|
use_jit: bool,
|
2020-09-14 17:42:37 -07:00
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
let logger = invoke_context.get_logger();
|
2020-11-12 12:44:37 -08:00
|
|
|
let invoke_depth = invoke_context.invoke_depth();
|
2020-09-14 17:42:37 -07:00
|
|
|
|
2020-01-22 17:54:06 -08:00
|
|
|
let mut keyed_accounts_iter = keyed_accounts.iter();
|
2021-01-08 14:43:24 -08:00
|
|
|
let _ = next_keyed_account(&mut keyed_accounts_iter)?;
|
2020-01-22 17:54:06 -08:00
|
|
|
let parameter_accounts = keyed_accounts_iter.as_slice();
|
2021-01-04 13:45:05 -08:00
|
|
|
let mut parameter_bytes =
|
|
|
|
serialize_parameters(loader_id, program_id, parameter_accounts, &instruction_data)?;
|
2020-03-17 12:06:15 -07:00
|
|
|
{
|
2020-08-21 15:31:19 -07:00
|
|
|
let compute_meter = invoke_context.get_compute_meter();
|
2020-11-04 09:46:26 -08:00
|
|
|
let mut vm = match create_vm(
|
2020-12-30 12:02:29 -08:00
|
|
|
loader_id,
|
2020-12-14 15:35:10 -08:00
|
|
|
self.program.as_ref(),
|
2020-11-24 09:00:19 -08:00
|
|
|
&mut parameter_bytes,
|
2020-08-24 13:21:34 -07:00
|
|
|
¶meter_accounts,
|
|
|
|
invoke_context,
|
|
|
|
) {
|
|
|
|
Ok(info) => info,
|
|
|
|
Err(e) => {
|
|
|
|
log!(logger, "Failed to create BPF VM: {}", e);
|
2020-12-15 09:54:07 -08:00
|
|
|
return Err(InstructionError::ProgramEnvironmentSetupFailure);
|
2020-08-24 13:21:34 -07:00
|
|
|
}
|
|
|
|
};
|
2020-03-17 12:06:15 -07:00
|
|
|
|
2021-01-08 14:43:24 -08:00
|
|
|
stable_log::program_invoke(&logger, program_id, invoke_depth);
|
2020-11-04 09:46:26 -08:00
|
|
|
let mut instruction_meter = ThisInstructionMeter::new(compute_meter.clone());
|
2020-10-15 15:55:37 -07:00
|
|
|
let before = compute_meter.borrow().get_remaining();
|
2020-12-07 00:49:55 -08:00
|
|
|
let result = if use_jit {
|
2020-11-24 09:00:19 -08:00
|
|
|
vm.execute_program_jit(&mut instruction_meter)
|
2020-11-04 09:46:26 -08:00
|
|
|
} else {
|
|
|
|
vm.execute_program_interpreted(&mut instruction_meter)
|
|
|
|
};
|
2020-10-15 15:55:37 -07:00
|
|
|
let after = compute_meter.borrow().get_remaining();
|
|
|
|
log!(
|
|
|
|
logger,
|
2020-11-12 12:44:37 -08:00
|
|
|
"Program {} consumed {} of {} compute units",
|
2021-01-08 14:43:24 -08:00
|
|
|
program_id,
|
2020-10-15 15:55:37 -07:00
|
|
|
before - after,
|
|
|
|
before
|
|
|
|
);
|
|
|
|
match result {
|
2020-03-17 12:06:15 -07:00
|
|
|
Ok(status) => {
|
|
|
|
if status != SUCCESS {
|
|
|
|
let error: InstructionError = status.into();
|
2021-01-08 14:43:24 -08:00
|
|
|
stable_log::program_failure(&logger, program_id, &error);
|
2020-03-17 12:06:15 -07:00
|
|
|
return Err(error);
|
|
|
|
}
|
|
|
|
}
|
2020-03-26 14:00:26 -07:00
|
|
|
Err(error) => {
|
2021-01-08 14:43:24 -08:00
|
|
|
log!(logger, "Program {} BPF VM error: {}", program_id, error);
|
2020-11-12 12:44:37 -08:00
|
|
|
let error = match error {
|
2020-04-30 01:43:11 -07:00
|
|
|
EbpfError::UserError(BPFError::SyscallError(
|
|
|
|
SyscallError::InstructionError(error),
|
2020-11-12 12:44:37 -08:00
|
|
|
)) => error,
|
2020-12-15 09:54:07 -08:00
|
|
|
err => {
|
|
|
|
log!(logger, "Program failed to complete: {:?}", err);
|
|
|
|
InstructionError::ProgramFailedToComplete
|
|
|
|
}
|
2020-03-26 14:00:26 -07:00
|
|
|
};
|
2020-11-12 12:44:37 -08:00
|
|
|
|
2021-01-08 14:43:24 -08:00
|
|
|
stable_log::program_failure(&logger, program_id, &error);
|
2020-11-12 12:44:37 -08:00
|
|
|
return Err(error);
|
2020-01-10 13:20:15 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-30 12:02:29 -08:00
|
|
|
deserialize_parameters(loader_id, parameter_accounts, ¶meter_bytes)?;
|
2021-01-08 14:43:24 -08:00
|
|
|
stable_log::program_success(&logger, program_id);
|
2020-09-14 17:42:37 -07:00
|
|
|
Ok(())
|
2018-10-16 09:43:49 -07:00
|
|
|
}
|
|
|
|
}
|
2018-10-31 10:59:56 -07:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2020-05-08 12:37:04 -07:00
|
|
|
use rand::Rng;
|
2020-12-14 15:35:10 -08:00
|
|
|
use solana_runtime::{
|
|
|
|
bank::Bank,
|
|
|
|
bank_client::BankClient,
|
|
|
|
message_processor::{Executors, ThisInvokeContext},
|
|
|
|
};
|
2020-10-28 20:21:50 -07:00
|
|
|
use solana_sdk::{
|
2020-12-14 15:35:10 -08:00
|
|
|
account::{create_account, Account},
|
|
|
|
account_utils::StateMut,
|
|
|
|
client::SyncClient,
|
|
|
|
clock::Clock,
|
2020-09-29 14:36:30 -07:00
|
|
|
feature_set::FeatureSet,
|
2020-12-14 15:35:10 -08:00
|
|
|
genesis_config::create_genesis_config,
|
|
|
|
instruction::Instruction,
|
|
|
|
instruction::{AccountMeta, InstructionError},
|
|
|
|
message::Message,
|
2020-10-28 13:16:13 -07:00
|
|
|
process_instruction::{BpfComputeBudget, MockInvokeContext},
|
2020-11-04 09:46:26 -08:00
|
|
|
pubkey::Pubkey,
|
2020-10-28 20:21:50 -07:00
|
|
|
rent::Rent,
|
2020-12-14 15:35:10 -08:00
|
|
|
signature::{Keypair, Signer},
|
|
|
|
system_program, sysvar,
|
|
|
|
transaction::TransactionError,
|
2020-04-28 14:33:56 -07:00
|
|
|
};
|
2020-12-14 15:35:10 -08:00
|
|
|
use std::{cell::RefCell, fs::File, io::Read, ops::Range, rc::Rc, sync::Arc};
|
2020-04-28 14:33:56 -07:00
|
|
|
|
2020-08-21 15:31:19 -07:00
|
|
|
struct TestInstructionMeter {
|
|
|
|
remaining: u64,
|
2020-06-13 13:20:08 -07:00
|
|
|
}
|
2020-08-21 15:31:19 -07:00
|
|
|
impl InstructionMeter for TestInstructionMeter {
|
|
|
|
fn consume(&mut self, amount: u64) {
|
|
|
|
self.remaining = self.remaining.saturating_sub(amount);
|
2020-06-06 10:18:28 -07:00
|
|
|
}
|
2020-08-21 15:31:19 -07:00
|
|
|
fn get_remaining(&self) -> u64 {
|
|
|
|
self.remaining
|
2020-06-06 10:18:28 -07:00
|
|
|
}
|
2020-04-28 14:33:56 -07:00
|
|
|
}
|
2018-11-06 14:28:46 -08:00
|
|
|
|
2018-10-31 10:59:56 -07:00
|
|
|
#[test]
|
2020-11-04 09:46:26 -08:00
|
|
|
#[should_panic(expected = "ExceededMaxInstructions(31, 10)")]
|
2019-11-08 09:19:19 -08:00
|
|
|
fn test_bpf_loader_non_terminating_program() {
|
2018-10-31 10:59:56 -07:00
|
|
|
#[rustfmt::skip]
|
2019-11-08 09:19:19 -08:00
|
|
|
let program = &[
|
2019-02-22 16:27:19 -08:00
|
|
|
0x07, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // r6 + 1
|
|
|
|
0x05, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, // goto -2
|
2018-10-31 10:59:56 -07:00
|
|
|
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit
|
|
|
|
];
|
|
|
|
let input = &mut [0x00];
|
2018-11-06 14:28:46 -08:00
|
|
|
|
2020-12-14 15:35:10 -08:00
|
|
|
let program = Executable::<BPFError, TestInstructionMeter>::from_text_bytes(
|
2020-11-24 09:00:19 -08:00
|
|
|
program,
|
|
|
|
None,
|
2020-11-04 09:46:26 -08:00
|
|
|
Config::default(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
2020-11-24 09:00:19 -08:00
|
|
|
let mut vm =
|
2020-12-14 15:35:10 -08:00
|
|
|
EbpfVm::<BPFError, TestInstructionMeter>::new(program.as_ref(), input, &[]).unwrap();
|
2020-11-04 09:46:26 -08:00
|
|
|
let mut instruction_meter = TestInstructionMeter { remaining: 10 };
|
|
|
|
vm.execute_program_interpreted(&mut instruction_meter)
|
2020-08-21 15:31:19 -07:00
|
|
|
.unwrap();
|
2018-10-31 10:59:56 -07:00
|
|
|
}
|
2019-11-08 09:19:19 -08:00
|
|
|
|
2020-11-12 13:13:42 -08:00
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "VerifierError(LDDWCannotBeLast)")]
|
|
|
|
fn test_bpf_loader_check_load_dw() {
|
|
|
|
let prog = &[
|
|
|
|
0x18, 0x00, 0x00, 0x00, 0x88, 0x77, 0x66, 0x55, // first half of lddw
|
|
|
|
];
|
|
|
|
bpf_verifier::check(prog, true).unwrap();
|
|
|
|
}
|
|
|
|
|
2019-11-08 09:19:19 -08:00
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_write() {
|
2020-12-14 15:35:10 -08:00
|
|
|
let program_id = bpf_loader::id();
|
2020-10-19 12:12:08 -07:00
|
|
|
let program_key = solana_sdk::pubkey::new_rand();
|
2020-01-22 17:54:06 -08:00
|
|
|
let program_account = Account::new_ref(1, 0, &program_id);
|
|
|
|
let keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
2020-01-10 13:20:15 -08:00
|
|
|
let instruction_data = bincode::serialize(&LoaderInstruction::Write {
|
2019-11-08 09:19:19 -08:00
|
|
|
offset: 3,
|
|
|
|
bytes: vec![1, 2, 3],
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Case: Empty keyed accounts
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::NotEnoughAccountKeys),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
2020-05-15 09:35:43 -07:00
|
|
|
&[],
|
2020-04-28 14:33:56 -07:00
|
|
|
&instruction_data,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not signed
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
|
|
|
&instruction_data,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Write bytes to an offset
|
2020-07-10 13:02:55 -07:00
|
|
|
#[allow(unused_mut)]
|
2020-01-22 17:54:06 -08:00
|
|
|
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)];
|
2020-01-22 09:11:56 -08:00
|
|
|
keyed_accounts[0].account.borrow_mut().data = vec![0; 6];
|
2019-11-08 09:19:19 -08:00
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
|
|
|
&instruction_data,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
2020-01-22 09:11:56 -08:00
|
|
|
assert_eq!(
|
|
|
|
vec![0, 0, 0, 1, 2, 3],
|
|
|
|
keyed_accounts[0].account.borrow().data
|
|
|
|
);
|
2019-11-08 09:19:19 -08:00
|
|
|
|
|
|
|
// Case: Overflow
|
2020-07-10 13:02:55 -07:00
|
|
|
#[allow(unused_mut)]
|
2020-01-22 17:54:06 -08:00
|
|
|
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)];
|
2020-01-22 09:11:56 -08:00
|
|
|
keyed_accounts[0].account.borrow_mut().data = vec![0; 5];
|
2019-11-08 09:19:19 -08:00
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::AccountDataTooSmall),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
|
|
|
&instruction_data,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_finalize() {
|
2020-12-14 15:35:10 -08:00
|
|
|
let program_id = bpf_loader::id();
|
2020-10-19 12:12:08 -07:00
|
|
|
let program_key = solana_sdk::pubkey::new_rand();
|
2020-08-11 16:11:52 -07:00
|
|
|
let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed");
|
2019-12-04 12:03:29 -08:00
|
|
|
let mut elf = Vec::new();
|
2020-03-31 10:07:38 -07:00
|
|
|
let rent = Rent::default();
|
2019-12-04 12:03:29 -08:00
|
|
|
file.read_to_end(&mut elf).unwrap();
|
2020-01-22 17:54:06 -08:00
|
|
|
let program_account = Account::new_ref(rent.minimum_balance(elf.len()), 0, &program_id);
|
2020-01-22 09:11:56 -08:00
|
|
|
program_account.borrow_mut().data = elf;
|
2020-03-31 10:07:38 -07:00
|
|
|
let keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
2020-01-10 13:20:15 -08:00
|
|
|
let instruction_data = bincode::serialize(&LoaderInstruction::Finalize).unwrap();
|
2019-11-08 09:19:19 -08:00
|
|
|
|
|
|
|
// Case: Empty keyed accounts
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::NotEnoughAccountKeys),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
2020-05-15 09:35:43 -07:00
|
|
|
&[],
|
2020-04-28 14:33:56 -07:00
|
|
|
&instruction_data,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not signed
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
|
|
|
&instruction_data,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Finalize
|
2020-03-31 10:07:38 -07:00
|
|
|
let keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)];
|
2019-11-08 09:19:19 -08:00
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
|
|
|
&instruction_data,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
2020-01-22 09:11:56 -08:00
|
|
|
assert!(keyed_accounts[0].account.borrow().executable);
|
|
|
|
|
|
|
|
program_account.borrow_mut().executable = false; // Un-finalize the account
|
2019-12-04 12:03:29 -08:00
|
|
|
|
|
|
|
// Case: Finalize
|
2020-01-22 09:11:56 -08:00
|
|
|
program_account.borrow_mut().data[0] = 0; // bad elf
|
2020-03-31 10:07:38 -07:00
|
|
|
let keyed_accounts = vec![KeyedAccount::new(&program_key, true, &program_account)];
|
2019-12-04 12:03:29 -08:00
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::InvalidAccountData),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
|
|
|
&instruction_data,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2019-12-04 12:03:29 -08:00
|
|
|
);
|
2019-11-08 09:19:19 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_invoke_main() {
|
2020-12-14 15:35:10 -08:00
|
|
|
let program_id = bpf_loader::id();
|
2020-10-19 12:12:08 -07:00
|
|
|
let program_key = solana_sdk::pubkey::new_rand();
|
2019-11-08 09:19:19 -08:00
|
|
|
|
|
|
|
// Create program account
|
2020-08-11 16:11:52 -07:00
|
|
|
let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed");
|
2019-11-08 09:19:19 -08:00
|
|
|
let mut elf = Vec::new();
|
|
|
|
file.read_to_end(&mut elf).unwrap();
|
2020-01-22 17:54:06 -08:00
|
|
|
let program_account = Account::new_ref(1, 0, &program_id);
|
2020-01-22 09:11:56 -08:00
|
|
|
program_account.borrow_mut().data = elf;
|
|
|
|
program_account.borrow_mut().executable = true;
|
2019-11-08 09:19:19 -08:00
|
|
|
|
2020-01-22 17:54:06 -08:00
|
|
|
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
2019-11-08 09:19:19 -08:00
|
|
|
|
2020-09-14 17:42:37 -07:00
|
|
|
let mut invoke_context = MockInvokeContext::default();
|
|
|
|
|
2019-11-08 09:19:19 -08:00
|
|
|
// Case: Empty keyed accounts
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::NotEnoughAccountKeys),
|
2020-12-23 19:04:48 -08:00
|
|
|
process_instruction(&program_id, &[], &[], &mut invoke_context)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Only a program account
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
2020-12-23 19:04:48 -08:00
|
|
|
process_instruction(&program_key, &keyed_accounts, &[], &mut invoke_context)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
|
|
|
|
2020-12-23 19:04:48 -08:00
|
|
|
// Case: Account not a program
|
2020-01-22 09:11:56 -08:00
|
|
|
keyed_accounts[0].account.borrow_mut().executable = false;
|
2019-11-08 09:19:19 -08:00
|
|
|
assert_eq!(
|
2020-01-10 13:20:15 -08:00
|
|
|
Err(InstructionError::InvalidInstructionData),
|
2020-12-23 19:04:48 -08:00
|
|
|
process_instruction(&program_id, &keyed_accounts, &[], &mut invoke_context)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
2020-01-22 09:11:56 -08:00
|
|
|
keyed_accounts[0].account.borrow_mut().executable = true;
|
2019-11-08 09:19:19 -08:00
|
|
|
|
|
|
|
// Case: With program and parameter account
|
2020-01-22 17:54:06 -08:00
|
|
|
let parameter_account = Account::new_ref(1, 0, &program_id);
|
|
|
|
keyed_accounts.push(KeyedAccount::new(&program_key, false, ¶meter_account));
|
2019-11-08 09:19:19 -08:00
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
2020-12-23 19:04:48 -08:00
|
|
|
process_instruction(&program_key, &keyed_accounts, &[], &mut invoke_context)
|
2019-11-08 09:19:19 -08:00
|
|
|
);
|
2020-01-22 09:11:56 -08:00
|
|
|
|
2020-08-26 14:48:51 -07:00
|
|
|
// Case: limited budget
|
|
|
|
let program_id = Pubkey::default();
|
|
|
|
let mut invoke_context = ThisInvokeContext::new(
|
|
|
|
&program_id,
|
|
|
|
Rent::default(),
|
|
|
|
vec![],
|
2020-10-29 09:57:14 -07:00
|
|
|
&[],
|
2020-12-22 09:26:55 -08:00
|
|
|
&[],
|
2020-08-26 14:48:51 -07:00
|
|
|
None,
|
2020-10-28 13:16:13 -07:00
|
|
|
BpfComputeBudget {
|
2020-08-26 14:48:51 -07:00
|
|
|
max_units: 1,
|
|
|
|
log_units: 100,
|
|
|
|
log_64_units: 100,
|
|
|
|
create_program_address_units: 1500,
|
|
|
|
invoke_units: 1000,
|
|
|
|
max_invoke_depth: 2,
|
2020-09-29 23:29:20 -07:00
|
|
|
sha256_base_cost: 85,
|
|
|
|
sha256_byte_cost: 1,
|
2020-10-09 13:07:09 -07:00
|
|
|
max_call_depth: 20,
|
|
|
|
stack_frame_size: 4096,
|
2020-10-15 09:11:54 -07:00
|
|
|
log_pubkey_units: 100,
|
2020-12-28 17:14:17 -08:00
|
|
|
max_cpi_instruction_size: usize::MAX,
|
2020-08-26 14:48:51 -07:00
|
|
|
},
|
2020-09-14 17:42:37 -07:00
|
|
|
Rc::new(RefCell::new(Executors::default())),
|
2020-09-24 07:36:22 -07:00
|
|
|
None,
|
2020-09-29 14:36:30 -07:00
|
|
|
Arc::new(FeatureSet::default()),
|
2020-08-26 14:48:51 -07:00
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-12-15 09:54:07 -08:00
|
|
|
Err(InstructionError::ProgramFailedToComplete),
|
2020-12-23 19:04:48 -08:00
|
|
|
process_instruction(&program_key, &keyed_accounts, &[], &mut invoke_context)
|
2020-08-26 14:48:51 -07:00
|
|
|
);
|
|
|
|
|
2020-01-22 09:11:56 -08:00
|
|
|
// Case: With duplicate accounts
|
2020-10-19 12:12:08 -07:00
|
|
|
let duplicate_key = solana_sdk::pubkey::new_rand();
|
2020-01-22 09:11:56 -08:00
|
|
|
let parameter_account = Account::new_ref(1, 0, &program_id);
|
2020-02-11 10:03:28 -08:00
|
|
|
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
2020-01-22 09:11:56 -08:00
|
|
|
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account));
|
|
|
|
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account));
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
2020-04-28 14:33:56 -07:00
|
|
|
process_instruction(
|
2020-12-23 19:04:48 -08:00
|
|
|
&program_key,
|
2020-04-28 14:33:56 -07:00
|
|
|
&keyed_accounts,
|
2020-05-15 09:35:43 -07:00
|
|
|
&[],
|
2020-04-28 14:33:56 -07:00
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
2020-01-22 09:11:56 -08:00
|
|
|
);
|
2019-11-08 09:19:19 -08:00
|
|
|
}
|
2020-05-08 12:37:04 -07:00
|
|
|
|
2020-08-11 16:11:52 -07:00
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_serialize_unaligned() {
|
2020-12-14 15:35:10 -08:00
|
|
|
let program_id = bpf_loader_deprecated::id();
|
2020-10-19 12:12:08 -07:00
|
|
|
let program_key = solana_sdk::pubkey::new_rand();
|
2020-08-11 16:11:52 -07:00
|
|
|
|
|
|
|
// Create program account
|
|
|
|
let mut file = File::open("test_elfs/noop_unaligned.so").expect("file open failed");
|
|
|
|
let mut elf = Vec::new();
|
|
|
|
file.read_to_end(&mut elf).unwrap();
|
|
|
|
let program_account = Account::new_ref(1, 0, &program_id);
|
|
|
|
program_account.borrow_mut().data = elf;
|
|
|
|
program_account.borrow_mut().executable = true;
|
|
|
|
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
|
|
|
|
|
|
|
// Case: With program and parameter account
|
|
|
|
let parameter_account = Account::new_ref(1, 0, &program_id);
|
|
|
|
keyed_accounts.push(KeyedAccount::new(&program_key, false, ¶meter_account));
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
|
|
|
process_instruction(
|
2020-12-23 19:04:48 -08:00
|
|
|
&program_key,
|
2020-08-11 16:11:52 -07:00
|
|
|
&keyed_accounts,
|
|
|
|
&[],
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: With duplicate accounts
|
2020-10-19 12:12:08 -07:00
|
|
|
let duplicate_key = solana_sdk::pubkey::new_rand();
|
2020-08-11 16:11:52 -07:00
|
|
|
let parameter_account = Account::new_ref(1, 0, &program_id);
|
|
|
|
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
|
|
|
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account));
|
|
|
|
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account));
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
|
|
|
process_instruction(
|
2020-12-23 19:04:48 -08:00
|
|
|
&program_key,
|
2020-08-11 16:11:52 -07:00
|
|
|
&keyed_accounts,
|
|
|
|
&[],
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_serialize_aligned() {
|
2020-12-14 15:35:10 -08:00
|
|
|
let program_id = bpf_loader::id();
|
2020-10-19 12:12:08 -07:00
|
|
|
let program_key = solana_sdk::pubkey::new_rand();
|
2020-08-11 16:11:52 -07:00
|
|
|
|
|
|
|
// Create program account
|
|
|
|
let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed");
|
|
|
|
let mut elf = Vec::new();
|
|
|
|
file.read_to_end(&mut elf).unwrap();
|
|
|
|
let program_account = Account::new_ref(1, 0, &program_id);
|
|
|
|
program_account.borrow_mut().data = elf;
|
|
|
|
program_account.borrow_mut().executable = true;
|
|
|
|
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
|
|
|
|
|
|
|
// Case: With program and parameter account
|
|
|
|
let parameter_account = Account::new_ref(1, 0, &program_id);
|
|
|
|
keyed_accounts.push(KeyedAccount::new(&program_key, false, ¶meter_account));
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
|
|
|
process_instruction(
|
2020-12-23 19:04:48 -08:00
|
|
|
&program_key,
|
2020-08-11 16:11:52 -07:00
|
|
|
&keyed_accounts,
|
|
|
|
&[],
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: With duplicate accounts
|
2020-10-19 12:12:08 -07:00
|
|
|
let duplicate_key = solana_sdk::pubkey::new_rand();
|
2020-08-11 16:11:52 -07:00
|
|
|
let parameter_account = Account::new_ref(1, 0, &program_id);
|
|
|
|
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
|
|
|
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account));
|
|
|
|
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account));
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
|
|
|
process_instruction(
|
2020-12-23 19:04:48 -08:00
|
|
|
&program_key,
|
2020-08-11 16:11:52 -07:00
|
|
|
&keyed_accounts,
|
|
|
|
&[],
|
|
|
|
&mut MockInvokeContext::default()
|
2020-12-14 15:35:10 -08:00
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_upgradeable_initialize_buffer() {
|
|
|
|
let instruction =
|
|
|
|
bincode::serialize(&UpgradeableLoaderInstruction::InitializeBuffer).unwrap();
|
|
|
|
let buffer_address = Pubkey::new_unique();
|
|
|
|
let buffer_account = Account::new_ref(
|
|
|
|
1,
|
|
|
|
UpgradeableLoaderState::buffer_len(9).unwrap(),
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Success
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
2021-01-08 09:37:57 -08:00
|
|
|
&[KeyedAccount::new(&buffer_address, false, &buffer_account)],
|
2020-12-14 15:35:10 -08:00
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap();
|
2021-01-08 09:37:57 -08:00
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: None
|
|
|
|
}
|
|
|
|
);
|
2020-12-14 15:35:10 -08:00
|
|
|
|
|
|
|
// Case: Already initialized
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::AccountAlreadyInitialized),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
2021-01-08 09:37:57 -08:00
|
|
|
&[KeyedAccount::new(&buffer_address, false, &buffer_account)],
|
2020-12-14 15:35:10 -08:00
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap();
|
2021-01-08 09:37:57 -08:00
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: None
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: With authority
|
|
|
|
let buffer_account = Account::new_ref(
|
|
|
|
1,
|
|
|
|
UpgradeableLoaderState::buffer_len(9).unwrap(),
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
let authority_address = Pubkey::new_unique();
|
|
|
|
let authority_account = Account::new_ref(
|
|
|
|
1,
|
|
|
|
UpgradeableLoaderState::buffer_len(9).unwrap(),
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&authority_address, false, &authority_account)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(authority_address)
|
|
|
|
}
|
|
|
|
);
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_upgradeable_write() {
|
|
|
|
let buffer_address = Pubkey::new_unique();
|
|
|
|
let buffer_account = Account::new_ref(
|
|
|
|
1,
|
|
|
|
UpgradeableLoaderState::buffer_len(9).unwrap(),
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not initialized
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
|
|
|
|
offset: 0,
|
|
|
|
bytes: vec![42; 9],
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::InvalidAccountData),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
2021-01-08 09:37:57 -08:00
|
|
|
&[
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&buffer_address, true, &buffer_account)
|
|
|
|
],
|
2020-12-14 15:35:10 -08:00
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Write entire buffer
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
|
|
|
|
offset: 0,
|
|
|
|
bytes: vec![42; 9],
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
buffer_account
|
|
|
|
.borrow_mut()
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address),
|
|
|
|
})
|
2020-12-14 15:35:10 -08:00
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
2021-01-08 09:37:57 -08:00
|
|
|
&[
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&buffer_address, true, &buffer_account)
|
|
|
|
],
|
2020-12-14 15:35:10 -08:00
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap();
|
2021-01-08 09:37:57 -08:00
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address)
|
|
|
|
}
|
|
|
|
);
|
2020-12-14 15:35:10 -08:00
|
|
|
assert_eq!(
|
|
|
|
&buffer_account.borrow().data[UpgradeableLoaderState::buffer_data_offset().unwrap()..],
|
|
|
|
&[42; 9]
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Write portion of the buffer
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
|
|
|
|
offset: 3,
|
|
|
|
bytes: vec![42; 6],
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
let buffer_account = Account::new_ref(
|
|
|
|
1,
|
|
|
|
UpgradeableLoaderState::buffer_len(9).unwrap(),
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
buffer_account
|
|
|
|
.borrow_mut()
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address),
|
|
|
|
})
|
2020-12-14 15:35:10 -08:00
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
2021-01-08 09:37:57 -08:00
|
|
|
&[
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&buffer_address, true, &buffer_account)
|
|
|
|
],
|
2020-12-14 15:35:10 -08:00
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap();
|
2021-01-08 09:37:57 -08:00
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address)
|
|
|
|
}
|
|
|
|
);
|
2020-12-14 15:35:10 -08:00
|
|
|
assert_eq!(
|
|
|
|
&buffer_account.borrow().data[UpgradeableLoaderState::buffer_data_offset().unwrap()..],
|
|
|
|
&[0, 0, 0, 42, 42, 42, 42, 42, 42]
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not signed
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
|
|
|
|
offset: 0,
|
|
|
|
bytes: vec![42; 9],
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
buffer_account
|
|
|
|
.borrow_mut()
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address),
|
|
|
|
})
|
2020-12-14 15:35:10 -08:00
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
2021-01-08 09:37:57 -08:00
|
|
|
&[
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account)
|
|
|
|
],
|
2020-12-14 15:35:10 -08:00
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: overflow size
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
|
|
|
|
offset: 0,
|
|
|
|
bytes: vec![42; 10],
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
buffer_account
|
|
|
|
.borrow_mut()
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address),
|
|
|
|
})
|
2020-12-14 15:35:10 -08:00
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::AccountDataTooSmall),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
2021-01-08 09:37:57 -08:00
|
|
|
&[
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&buffer_address, true, &buffer_account)
|
|
|
|
],
|
2020-12-14 15:35:10 -08:00
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: overflow offset
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
|
|
|
|
offset: 1,
|
|
|
|
bytes: vec![42; 9],
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
buffer_account
|
|
|
|
.borrow_mut()
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address),
|
|
|
|
})
|
2020-12-14 15:35:10 -08:00
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::AccountDataTooSmall),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
2021-01-08 09:37:57 -08:00
|
|
|
&[
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&buffer_address, true, &buffer_account)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: wrong authority
|
|
|
|
let authority_address = Pubkey::new_unique();
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Write {
|
|
|
|
offset: 1,
|
|
|
|
bytes: vec![42; 9],
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
buffer_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::IncorrectAuthority),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&authority_address, true, &buffer_account)
|
|
|
|
],
|
2020-12-14 15:35:10 -08:00
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_upgradeable_deploy_with_max_len() {
|
|
|
|
let (genesis_config, mint_keypair) = create_genesis_config(1_000_000_000);
|
|
|
|
let mut bank = Bank::new(&genesis_config);
|
|
|
|
bank.add_builtin(
|
|
|
|
"solana_bpf_loader_upgradeable_program",
|
|
|
|
bpf_loader_upgradeable::id(),
|
|
|
|
process_instruction,
|
|
|
|
);
|
|
|
|
let bank = Arc::new(bank);
|
|
|
|
let bank_client = BankClient::new_shared(&bank);
|
|
|
|
|
|
|
|
// Setup initial accounts
|
|
|
|
let program_keypair = Keypair::new();
|
|
|
|
let (programdata_address, _) = Pubkey::find_program_address(
|
|
|
|
&[program_keypair.pubkey().as_ref()],
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
let upgrade_authority_address = Pubkey::new_unique();
|
|
|
|
let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed");
|
|
|
|
let mut elf = Vec::new();
|
|
|
|
file.read_to_end(&mut elf).unwrap();
|
|
|
|
let min_program_balance = bank
|
|
|
|
.get_minimum_balance_for_rent_exemption(UpgradeableLoaderState::program_len().unwrap());
|
|
|
|
let min_programdata_balance = bank.get_minimum_balance_for_rent_exemption(
|
|
|
|
UpgradeableLoaderState::programdata_len(elf.len()).unwrap(),
|
|
|
|
);
|
|
|
|
let buffer_address = Pubkey::new_unique();
|
|
|
|
let mut buffer_account = Account::new(
|
|
|
|
min_programdata_balance,
|
|
|
|
UpgradeableLoaderState::buffer_len(elf.len()).unwrap(),
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
buffer_account
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address),
|
|
|
|
})
|
2020-12-14 15:35:10 -08:00
|
|
|
.unwrap();
|
|
|
|
buffer_account.data[UpgradeableLoaderState::buffer_data_offset().unwrap()..]
|
|
|
|
.copy_from_slice(&elf);
|
|
|
|
|
|
|
|
// Test successful deploy
|
|
|
|
bank.clear_signatures();
|
|
|
|
bank.store_account(&buffer_address, &buffer_account);
|
|
|
|
bank.store_account(&program_keypair.pubkey(), &Account::default());
|
|
|
|
bank.store_account(&programdata_address, &Account::default());
|
|
|
|
let before = bank.get_balance(&mint_keypair.pubkey());
|
|
|
|
let message = Message::new(
|
|
|
|
&bpf_loader_upgradeable::deploy_with_max_program_len(
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
&program_keypair.pubkey(),
|
|
|
|
&buffer_address,
|
|
|
|
Some(&upgrade_authority_address),
|
|
|
|
min_program_balance,
|
|
|
|
elf.len(),
|
|
|
|
)
|
|
|
|
.unwrap(),
|
|
|
|
Some(&mint_keypair.pubkey()),
|
|
|
|
);
|
|
|
|
assert!(bank_client
|
|
|
|
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
|
|
|
|
.is_ok());
|
|
|
|
assert_eq!(
|
|
|
|
bank.get_balance(&mint_keypair.pubkey()),
|
|
|
|
before - min_program_balance
|
|
|
|
);
|
|
|
|
assert_eq!(bank.get_balance(&buffer_address), 0);
|
|
|
|
assert_eq!(None, bank.get_account(&buffer_address));
|
|
|
|
let post_program_account = bank.get_account(&program_keypair.pubkey()).unwrap();
|
|
|
|
assert_eq!(post_program_account.lamports, min_program_balance);
|
|
|
|
assert_eq!(post_program_account.owner, bpf_loader_upgradeable::id());
|
|
|
|
assert_eq!(
|
|
|
|
post_program_account.data.len(),
|
|
|
|
UpgradeableLoaderState::program_len().unwrap()
|
|
|
|
);
|
|
|
|
let state: UpgradeableLoaderState = post_program_account.state().unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::Program {
|
|
|
|
programdata_address
|
|
|
|
}
|
|
|
|
);
|
|
|
|
let post_programdata_account = bank.get_account(&programdata_address).unwrap();
|
|
|
|
assert_eq!(post_programdata_account.lamports, min_programdata_balance);
|
|
|
|
assert_eq!(post_programdata_account.owner, bpf_loader_upgradeable::id());
|
|
|
|
let state: UpgradeableLoaderState = post_programdata_account.state().unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::ProgramData {
|
|
|
|
slot: bank_client.get_slot().unwrap(),
|
|
|
|
upgrade_authority_address: Some(upgrade_authority_address)
|
|
|
|
}
|
|
|
|
);
|
|
|
|
for (i, byte) in post_programdata_account.data
|
|
|
|
[UpgradeableLoaderState::programdata_data_offset().unwrap()..]
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
{
|
|
|
|
assert_eq!(elf[i], *byte);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test initialized program account
|
|
|
|
bank.clear_signatures();
|
|
|
|
bank.store_account(&buffer_address, &buffer_account);
|
|
|
|
let message = Message::new(
|
|
|
|
&[Instruction::new(
|
|
|
|
bpf_loader_upgradeable::id(),
|
|
|
|
&UpgradeableLoaderInstruction::DeployWithMaxDataLen {
|
|
|
|
max_data_len: elf.len(),
|
|
|
|
},
|
|
|
|
vec![
|
|
|
|
AccountMeta::new(mint_keypair.pubkey(), true),
|
|
|
|
AccountMeta::new(programdata_address, false),
|
|
|
|
AccountMeta::new(program_keypair.pubkey(), false),
|
|
|
|
AccountMeta::new(buffer_address, false),
|
|
|
|
AccountMeta::new_readonly(sysvar::rent::id(), false),
|
|
|
|
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
|
|
|
AccountMeta::new_readonly(system_program::id(), false),
|
|
|
|
],
|
|
|
|
)],
|
|
|
|
Some(&mint_keypair.pubkey()),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
TransactionError::InstructionError(0, InstructionError::AccountAlreadyInitialized),
|
|
|
|
bank_client
|
|
|
|
.send_and_confirm_message(&[&mint_keypair], message)
|
|
|
|
.unwrap_err()
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
|
|
|
|
// Test successful deploy no authority
|
|
|
|
bank.clear_signatures();
|
|
|
|
bank.store_account(&buffer_address, &buffer_account);
|
|
|
|
bank.store_account(&program_keypair.pubkey(), &Account::default());
|
|
|
|
bank.store_account(&programdata_address, &Account::default());
|
|
|
|
let message = Message::new(
|
|
|
|
&bpf_loader_upgradeable::deploy_with_max_program_len(
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
&program_keypair.pubkey(),
|
|
|
|
&buffer_address,
|
|
|
|
None,
|
|
|
|
min_program_balance,
|
|
|
|
elf.len(),
|
|
|
|
)
|
|
|
|
.unwrap(),
|
|
|
|
Some(&mint_keypair.pubkey()),
|
|
|
|
);
|
|
|
|
assert!(bank_client
|
|
|
|
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
|
|
|
|
.is_ok());
|
|
|
|
assert_eq!(None, bank.get_account(&buffer_address));
|
|
|
|
let post_program_account = bank.get_account(&program_keypair.pubkey()).unwrap();
|
|
|
|
assert_eq!(post_program_account.lamports, min_program_balance);
|
|
|
|
assert_eq!(post_program_account.owner, bpf_loader_upgradeable::id());
|
|
|
|
assert_eq!(
|
|
|
|
post_program_account.data.len(),
|
|
|
|
UpgradeableLoaderState::program_len().unwrap()
|
|
|
|
);
|
|
|
|
let state: UpgradeableLoaderState = post_program_account.state().unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::Program {
|
|
|
|
programdata_address
|
|
|
|
}
|
|
|
|
);
|
|
|
|
let post_programdata_account = bank.get_account(&programdata_address).unwrap();
|
|
|
|
assert_eq!(post_programdata_account.lamports, min_programdata_balance);
|
|
|
|
assert_eq!(post_programdata_account.owner, bpf_loader_upgradeable::id());
|
|
|
|
let state: UpgradeableLoaderState = post_programdata_account.state().unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::ProgramData {
|
|
|
|
slot: bank_client.get_slot().unwrap(),
|
|
|
|
upgrade_authority_address: None
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Test initialized ProgramData account
|
|
|
|
bank.clear_signatures();
|
|
|
|
bank.store_account(&buffer_address, &buffer_account);
|
|
|
|
bank.store_account(&program_keypair.pubkey(), &Account::default());
|
|
|
|
let message = Message::new(
|
|
|
|
&bpf_loader_upgradeable::deploy_with_max_program_len(
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
&program_keypair.pubkey(),
|
|
|
|
&buffer_address,
|
|
|
|
None,
|
|
|
|
min_program_balance,
|
|
|
|
elf.len(),
|
|
|
|
)
|
|
|
|
.unwrap(),
|
|
|
|
Some(&mint_keypair.pubkey()),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
TransactionError::InstructionError(1, InstructionError::Custom(0)),
|
|
|
|
bank_client
|
|
|
|
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
|
|
|
|
.unwrap_err()
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
|
|
|
|
// Test invalid Buffer account state
|
|
|
|
bank.clear_signatures();
|
|
|
|
bank.store_account(&buffer_address, &Account::default());
|
|
|
|
bank.store_account(&program_keypair.pubkey(), &Account::default());
|
|
|
|
bank.store_account(&programdata_address, &Account::default());
|
|
|
|
let message = Message::new(
|
|
|
|
&bpf_loader_upgradeable::deploy_with_max_program_len(
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
&program_keypair.pubkey(),
|
|
|
|
&buffer_address,
|
|
|
|
None,
|
|
|
|
min_program_balance,
|
|
|
|
elf.len(),
|
|
|
|
)
|
|
|
|
.unwrap(),
|
|
|
|
Some(&mint_keypair.pubkey()),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
TransactionError::InstructionError(1, InstructionError::InvalidAccountData),
|
|
|
|
bank_client
|
|
|
|
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
|
|
|
|
.unwrap_err()
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
|
|
|
|
// Test program account not rent exempt
|
|
|
|
bank.clear_signatures();
|
|
|
|
bank.store_account(&buffer_address, &buffer_account);
|
|
|
|
bank.store_account(&program_keypair.pubkey(), &Account::default());
|
|
|
|
bank.store_account(&programdata_address, &Account::default());
|
|
|
|
let message = Message::new(
|
|
|
|
&bpf_loader_upgradeable::deploy_with_max_program_len(
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
&program_keypair.pubkey(),
|
|
|
|
&buffer_address,
|
|
|
|
None,
|
|
|
|
min_program_balance - 1,
|
|
|
|
elf.len(),
|
|
|
|
)
|
|
|
|
.unwrap(),
|
|
|
|
Some(&mint_keypair.pubkey()),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
TransactionError::InstructionError(1, InstructionError::ExecutableAccountNotRentExempt),
|
|
|
|
bank_client
|
|
|
|
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
|
|
|
|
.unwrap_err()
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
|
2020-12-17 01:02:31 -08:00
|
|
|
// Test program account not rent exempt because data is larger than needed
|
|
|
|
bank.clear_signatures();
|
|
|
|
bank.store_account(&buffer_address, &buffer_account);
|
|
|
|
bank.store_account(&program_keypair.pubkey(), &Account::default());
|
|
|
|
bank.store_account(&programdata_address, &Account::default());
|
|
|
|
let mut instructions = bpf_loader_upgradeable::deploy_with_max_program_len(
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
&program_keypair.pubkey(),
|
|
|
|
&buffer_address,
|
|
|
|
None,
|
|
|
|
min_program_balance,
|
|
|
|
elf.len(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
instructions[0] = system_instruction::create_account(
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
&program_keypair.pubkey(),
|
|
|
|
min_program_balance,
|
|
|
|
UpgradeableLoaderState::program_len().unwrap() as u64 + 1,
|
|
|
|
&id(),
|
|
|
|
);
|
|
|
|
let message = Message::new(&instructions, Some(&mint_keypair.pubkey()));
|
|
|
|
assert_eq!(
|
|
|
|
TransactionError::InstructionError(1, InstructionError::ExecutableAccountNotRentExempt),
|
|
|
|
bank_client
|
|
|
|
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
|
|
|
|
.unwrap_err()
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
|
|
|
|
// Test program account too small
|
|
|
|
bank.clear_signatures();
|
|
|
|
bank.store_account(&buffer_address, &buffer_account);
|
|
|
|
bank.store_account(&program_keypair.pubkey(), &Account::default());
|
|
|
|
bank.store_account(&programdata_address, &Account::default());
|
|
|
|
let mut instructions = bpf_loader_upgradeable::deploy_with_max_program_len(
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
&program_keypair.pubkey(),
|
|
|
|
&buffer_address,
|
|
|
|
None,
|
|
|
|
min_program_balance,
|
|
|
|
elf.len(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
instructions[0] = system_instruction::create_account(
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
&program_keypair.pubkey(),
|
|
|
|
min_program_balance,
|
|
|
|
UpgradeableLoaderState::program_len().unwrap() as u64 - 1,
|
|
|
|
&id(),
|
|
|
|
);
|
|
|
|
let message = Message::new(&instructions, Some(&mint_keypair.pubkey()));
|
|
|
|
assert_eq!(
|
|
|
|
TransactionError::InstructionError(1, InstructionError::AccountDataTooSmall),
|
|
|
|
bank_client
|
|
|
|
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
|
|
|
|
.unwrap_err()
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
|
2020-12-14 15:35:10 -08:00
|
|
|
// Test Insufficient payer funds
|
|
|
|
bank.clear_signatures();
|
|
|
|
bank.store_account(
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
&Account::new(min_program_balance, 0, &system_program::id()),
|
|
|
|
);
|
|
|
|
bank.store_account(&buffer_address, &buffer_account);
|
|
|
|
bank.store_account(&program_keypair.pubkey(), &Account::default());
|
|
|
|
bank.store_account(&programdata_address, &Account::default());
|
|
|
|
let message = Message::new(
|
|
|
|
&bpf_loader_upgradeable::deploy_with_max_program_len(
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
&program_keypair.pubkey(),
|
|
|
|
&buffer_address,
|
|
|
|
None,
|
|
|
|
min_program_balance,
|
|
|
|
elf.len(),
|
|
|
|
)
|
|
|
|
.unwrap(),
|
|
|
|
Some(&mint_keypair.pubkey()),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
TransactionError::InstructionError(1, InstructionError::Custom(1)),
|
|
|
|
bank_client
|
|
|
|
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
|
|
|
|
.unwrap_err()
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
bank.store_account(
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
&Account::new(1_000_000_000, 0, &system_program::id()),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Test max_data_len
|
|
|
|
bank.clear_signatures();
|
|
|
|
bank.store_account(&buffer_address, &buffer_account);
|
|
|
|
bank.store_account(&program_keypair.pubkey(), &Account::default());
|
|
|
|
bank.store_account(&programdata_address, &Account::default());
|
|
|
|
let message = Message::new(
|
|
|
|
&bpf_loader_upgradeable::deploy_with_max_program_len(
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
&program_keypair.pubkey(),
|
|
|
|
&buffer_address,
|
|
|
|
None,
|
|
|
|
min_program_balance,
|
|
|
|
elf.len() - 1,
|
|
|
|
)
|
|
|
|
.unwrap(),
|
|
|
|
Some(&mint_keypair.pubkey()),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
TransactionError::InstructionError(1, InstructionError::AccountDataTooSmall),
|
|
|
|
bank_client
|
|
|
|
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
|
|
|
|
.unwrap_err()
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
|
|
|
|
// Test not the system account
|
|
|
|
bank.clear_signatures();
|
|
|
|
bank.store_account(&buffer_address, &buffer_account);
|
|
|
|
bank.store_account(&program_keypair.pubkey(), &Account::default());
|
|
|
|
bank.store_account(&programdata_address, &Account::default());
|
|
|
|
let mut instructions = bpf_loader_upgradeable::deploy_with_max_program_len(
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
&program_keypair.pubkey(),
|
|
|
|
&buffer_address,
|
|
|
|
None,
|
|
|
|
min_program_balance,
|
|
|
|
elf.len(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
instructions[1].accounts[6] = AccountMeta::new_readonly(Pubkey::new_unique(), false);
|
|
|
|
let message = Message::new(&instructions, Some(&mint_keypair.pubkey()));
|
|
|
|
assert_eq!(
|
|
|
|
TransactionError::InstructionError(1, InstructionError::MissingAccount),
|
|
|
|
bank_client
|
|
|
|
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
|
|
|
|
.unwrap_err()
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
|
|
|
|
// Test Bad ELF data
|
|
|
|
bank.clear_signatures();
|
|
|
|
let mut modified_buffer_account = buffer_account;
|
|
|
|
modified_buffer_account
|
|
|
|
.data
|
|
|
|
.truncate(UpgradeableLoaderState::buffer_len(1).unwrap());
|
|
|
|
bank.store_account(&buffer_address, &modified_buffer_account);
|
|
|
|
bank.store_account(&program_keypair.pubkey(), &Account::default());
|
|
|
|
bank.store_account(&programdata_address, &Account::default());
|
|
|
|
let message = Message::new(
|
|
|
|
&bpf_loader_upgradeable::deploy_with_max_program_len(
|
|
|
|
&mint_keypair.pubkey(),
|
|
|
|
&program_keypair.pubkey(),
|
|
|
|
&buffer_address,
|
|
|
|
None,
|
|
|
|
min_program_balance,
|
|
|
|
elf.len(),
|
|
|
|
)
|
|
|
|
.unwrap(),
|
|
|
|
Some(&mint_keypair.pubkey()),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
TransactionError::InstructionError(1, InstructionError::InvalidAccountData),
|
|
|
|
bank_client
|
|
|
|
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
|
|
|
|
.unwrap_err()
|
|
|
|
.unwrap()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_upgradeable_upgrade() {
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap();
|
|
|
|
let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed");
|
|
|
|
let mut elf_orig = Vec::new();
|
|
|
|
file.read_to_end(&mut elf_orig).unwrap();
|
|
|
|
let mut file = File::open("test_elfs/noop_unaligned.so").expect("file open failed");
|
|
|
|
let mut elf_new = Vec::new();
|
|
|
|
file.read_to_end(&mut elf_new).unwrap();
|
|
|
|
assert_ne!(elf_orig.len(), elf_new.len());
|
|
|
|
let rent = Rent::default();
|
|
|
|
let rent_account = RefCell::new(create_account(&Rent::default(), 1));
|
|
|
|
let slot = 42;
|
|
|
|
let clock_account = RefCell::new(create_account(
|
|
|
|
&Clock {
|
|
|
|
slot,
|
|
|
|
..Clock::default()
|
|
|
|
},
|
|
|
|
1,
|
|
|
|
));
|
|
|
|
let min_program_balance =
|
|
|
|
1.max(rent.minimum_balance(UpgradeableLoaderState::program_len().unwrap()));
|
|
|
|
let min_programdata_balance = 1.max(rent.minimum_balance(
|
|
|
|
UpgradeableLoaderState::programdata_len(elf_orig.len().max(elf_new.len())).unwrap(),
|
|
|
|
));
|
|
|
|
let upgrade_authority_address = Pubkey::new_unique();
|
|
|
|
let buffer_address = Pubkey::new_unique();
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
|
|
let (programdata_address, _) =
|
|
|
|
Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
|
|
let spill_address = Pubkey::new_unique();
|
|
|
|
let upgrade_authority_account = Account::new_ref(1, 0, &Pubkey::new_unique());
|
|
|
|
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
|
|
fn get_accounts(
|
2021-01-08 09:37:57 -08:00
|
|
|
buffer_authority: &Pubkey,
|
2020-12-14 15:35:10 -08:00
|
|
|
programdata_address: &Pubkey,
|
|
|
|
upgrade_authority_address: &Pubkey,
|
|
|
|
slot: u64,
|
|
|
|
elf_orig: &[u8],
|
|
|
|
elf_new: &[u8],
|
|
|
|
min_program_balance: u64,
|
|
|
|
min_programdata_balance: u64,
|
|
|
|
) -> (
|
|
|
|
Rc<RefCell<Account>>,
|
|
|
|
Rc<RefCell<Account>>,
|
|
|
|
Rc<RefCell<Account>>,
|
|
|
|
Rc<RefCell<Account>>,
|
|
|
|
) {
|
|
|
|
let buffer_account = Account::new_ref(
|
|
|
|
1,
|
|
|
|
UpgradeableLoaderState::buffer_len(elf_new.len()).unwrap(),
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
buffer_account
|
|
|
|
.borrow_mut()
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(*buffer_authority),
|
|
|
|
})
|
2020-12-14 15:35:10 -08:00
|
|
|
.unwrap();
|
|
|
|
buffer_account.borrow_mut().data
|
|
|
|
[UpgradeableLoaderState::buffer_data_offset().unwrap()..]
|
|
|
|
.copy_from_slice(&elf_new);
|
|
|
|
let programdata_account = Account::new_ref(
|
|
|
|
min_programdata_balance,
|
|
|
|
UpgradeableLoaderState::programdata_len(elf_orig.len().max(elf_new.len())).unwrap(),
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
programdata_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: Some(*upgrade_authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
let program_account = Account::new_ref(
|
|
|
|
min_program_balance,
|
|
|
|
UpgradeableLoaderState::program_len().unwrap(),
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
program_account.borrow_mut().executable = true;
|
|
|
|
program_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::Program {
|
|
|
|
programdata_address: *programdata_address,
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
let spill_account = Account::new_ref(0, 0, &Pubkey::new_unique());
|
|
|
|
|
|
|
|
(
|
|
|
|
buffer_account,
|
|
|
|
program_account,
|
|
|
|
programdata_account,
|
|
|
|
spill_account,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Case: Success
|
|
|
|
let (buffer_account, program_account, programdata_account, spill_account) = get_accounts(
|
2021-01-08 09:37:57 -08:00
|
|
|
&buffer_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&programdata_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
slot,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
min_program_balance,
|
|
|
|
min_programdata_balance,
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
2021-01-19 16:24:44 -08:00
|
|
|
KeyedAccount::new(&program_address, false, &program_account),
|
2020-12-14 15:35:10 -08:00
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&upgrade_authority_address,
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
assert_eq!(0, buffer_account.borrow().lamports);
|
|
|
|
assert_eq!(
|
|
|
|
min_programdata_balance,
|
|
|
|
programdata_account.borrow().lamports
|
|
|
|
);
|
|
|
|
assert_eq!(1, spill_account.borrow().lamports);
|
|
|
|
let state: UpgradeableLoaderState = programdata_account.borrow().state().unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: Some(upgrade_authority_address)
|
|
|
|
}
|
|
|
|
);
|
|
|
|
for (i, byte) in programdata_account.borrow().data
|
|
|
|
[UpgradeableLoaderState::programdata_data_offset().unwrap()
|
|
|
|
..UpgradeableLoaderState::programdata_data_offset().unwrap() + elf_new.len()]
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
{
|
|
|
|
assert_eq!(elf_new[i], *byte);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Case: not upgradable
|
|
|
|
let (buffer_account, program_account, programdata_account, spill_account) = get_accounts(
|
2021-01-08 09:37:57 -08:00
|
|
|
&buffer_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&programdata_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
slot,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
min_program_balance,
|
|
|
|
min_programdata_balance,
|
|
|
|
);
|
|
|
|
programdata_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: None,
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
2021-01-08 09:37:57 -08:00
|
|
|
Err(InstructionError::Immutable),
|
2020-12-14 15:35:10 -08:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
2021-01-19 16:24:44 -08:00
|
|
|
KeyedAccount::new(&program_address, false, &program_account),
|
2020-12-14 15:35:10 -08:00
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&upgrade_authority_address,
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: wrong authority
|
|
|
|
let (buffer_account, program_account, programdata_account, spill_account) = get_accounts(
|
2021-01-08 09:37:57 -08:00
|
|
|
&buffer_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&programdata_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
slot,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
min_program_balance,
|
|
|
|
min_programdata_balance,
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2021-01-08 09:37:57 -08:00
|
|
|
Err(InstructionError::IncorrectAuthority),
|
2020-12-14 15:35:10 -08:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
2021-01-19 16:24:44 -08:00
|
|
|
KeyedAccount::new(&program_address, false, &program_account),
|
2020-12-14 15:35:10 -08:00
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&Pubkey::new_unique(),
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: authority did not sign
|
|
|
|
let (buffer_account, program_account, programdata_account, spill_account) = get_accounts(
|
2021-01-08 09:37:57 -08:00
|
|
|
&buffer_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&programdata_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
slot,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
min_program_balance,
|
|
|
|
min_programdata_balance,
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
2021-01-19 16:24:44 -08:00
|
|
|
KeyedAccount::new(&program_address, false, &program_account),
|
2020-12-14 15:35:10 -08:00
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&upgrade_authority_address,
|
|
|
|
false,
|
|
|
|
&upgrade_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Program account not executable
|
|
|
|
let (buffer_account, program_account, programdata_account, spill_account) = get_accounts(
|
2021-01-08 09:37:57 -08:00
|
|
|
&buffer_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&programdata_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
slot,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
min_program_balance,
|
|
|
|
min_programdata_balance,
|
|
|
|
);
|
|
|
|
program_account.borrow_mut().executable = false;
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::AccountNotExecutable),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
2021-01-19 16:24:44 -08:00
|
|
|
KeyedAccount::new(&program_address, false, &program_account),
|
2020-12-14 15:35:10 -08:00
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&upgrade_authority_address,
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Program account now owned by loader
|
|
|
|
let (buffer_account, program_account, programdata_account, spill_account) = get_accounts(
|
2021-01-08 09:37:57 -08:00
|
|
|
&buffer_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&programdata_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
slot,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
min_program_balance,
|
|
|
|
min_programdata_balance,
|
|
|
|
);
|
|
|
|
program_account.borrow_mut().owner = Pubkey::new_unique();
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::IncorrectProgramId),
|
2021-01-19 16:24:44 -08:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
|
|
|
KeyedAccount::new(&program_address, false, &program_account),
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&upgrade_authority_address,
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Program account not writable
|
|
|
|
let (buffer_account, program_account, programdata_account, spill_account) = get_accounts(
|
|
|
|
&buffer_address,
|
|
|
|
&programdata_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
slot,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
min_program_balance,
|
|
|
|
min_programdata_balance,
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::InvalidArgument),
|
2020-12-14 15:35:10 -08:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
|
|
|
KeyedAccount::new_readonly(&program_address, false, &program_account),
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&upgrade_authority_address,
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Program account not initialized
|
|
|
|
let (buffer_account, program_account, programdata_account, spill_account) = get_accounts(
|
2021-01-08 09:37:57 -08:00
|
|
|
&buffer_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&programdata_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
slot,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
min_program_balance,
|
|
|
|
min_programdata_balance,
|
|
|
|
);
|
|
|
|
program_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::Uninitialized)
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::InvalidAccountData),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
2021-01-19 16:24:44 -08:00
|
|
|
KeyedAccount::new(&program_address, false, &program_account),
|
2020-12-14 15:35:10 -08:00
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&upgrade_authority_address,
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: ProgramData account not initialized
|
|
|
|
let (buffer_account, program_account, programdata_account, spill_account) = get_accounts(
|
2021-01-08 09:37:57 -08:00
|
|
|
&buffer_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&programdata_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
slot,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
min_program_balance,
|
|
|
|
min_programdata_balance,
|
|
|
|
);
|
|
|
|
programdata_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::Uninitialized)
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::InvalidAccountData),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
2021-01-19 16:24:44 -08:00
|
|
|
KeyedAccount::new(&program_address, false, &program_account),
|
2020-12-14 15:35:10 -08:00
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&upgrade_authority_address,
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Program ProgramData account mismatch
|
|
|
|
let (buffer_account, program_account, programdata_account, spill_account) = get_accounts(
|
2021-01-08 09:37:57 -08:00
|
|
|
&buffer_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&programdata_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
slot,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
min_program_balance,
|
|
|
|
min_programdata_balance,
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::InvalidArgument),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&Pubkey::new_unique(), false, &programdata_account),
|
2021-01-19 16:24:44 -08:00
|
|
|
KeyedAccount::new(&program_address, false, &program_account),
|
2020-12-14 15:35:10 -08:00
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&upgrade_authority_address,
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Buffer account not initialized
|
|
|
|
let (buffer_account, program_account, programdata_account, spill_account) = get_accounts(
|
2021-01-08 09:37:57 -08:00
|
|
|
&buffer_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&programdata_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
slot,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
min_program_balance,
|
|
|
|
min_programdata_balance,
|
|
|
|
);
|
|
|
|
buffer_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::Uninitialized)
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
2021-01-08 09:37:57 -08:00
|
|
|
Err(InstructionError::InvalidArgument),
|
2020-12-14 15:35:10 -08:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
2021-01-19 16:24:44 -08:00
|
|
|
KeyedAccount::new(&program_address, false, &program_account),
|
2020-12-14 15:35:10 -08:00
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&upgrade_authority_address,
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Buffer account too big
|
|
|
|
let (_, program_account, programdata_account, spill_account) = get_accounts(
|
2021-01-08 09:37:57 -08:00
|
|
|
&buffer_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&programdata_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
slot,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
min_program_balance,
|
|
|
|
min_programdata_balance,
|
|
|
|
);
|
|
|
|
let buffer_account = Account::new_ref(
|
|
|
|
1,
|
|
|
|
UpgradeableLoaderState::buffer_len(elf_orig.len().max(elf_new.len()) + 1).unwrap(),
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
buffer_account
|
|
|
|
.borrow_mut()
|
2021-01-08 09:37:57 -08:00
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(buffer_address),
|
|
|
|
})
|
2020-12-14 15:35:10 -08:00
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::AccountDataTooSmall),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
2021-01-19 16:24:44 -08:00
|
|
|
KeyedAccount::new(&program_address, false, &program_account),
|
2020-12-14 15:35:10 -08:00
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&upgrade_authority_address,
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: bad elf data
|
|
|
|
let (buffer_account, program_account, programdata_account, spill_account) = get_accounts(
|
2021-01-08 09:37:57 -08:00
|
|
|
&buffer_address,
|
2020-12-14 15:35:10 -08:00
|
|
|
&programdata_address,
|
|
|
|
&upgrade_authority_address,
|
|
|
|
slot,
|
|
|
|
&elf_orig,
|
|
|
|
&elf_new,
|
|
|
|
min_program_balance,
|
|
|
|
min_programdata_balance,
|
|
|
|
);
|
|
|
|
buffer_account.borrow_mut().data[UpgradeableLoaderState::buffer_data_offset().unwrap()..]
|
|
|
|
.copy_from_slice(&vec![0; elf_new.len()]);
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::InvalidAccountData),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
2021-01-19 16:24:44 -08:00
|
|
|
KeyedAccount::new(&program_address, false, &program_account),
|
2020-12-14 15:35:10 -08:00
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
|
|
|
KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&upgrade_authority_address,
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_upgradeable_set_upgrade_authority() {
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap();
|
|
|
|
let slot = 0;
|
|
|
|
let upgrade_authority_address = Pubkey::new_unique();
|
|
|
|
let upgrade_authority_account = Account::new_ref(1, 0, &Pubkey::new_unique());
|
|
|
|
let new_upgrade_authority_address = Pubkey::new_unique();
|
|
|
|
let new_upgrade_authority_account = Account::new_ref(1, 0, &Pubkey::new_unique());
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
|
|
let (programdata_address, _) =
|
|
|
|
Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
|
|
let programdata_account = Account::new_ref(
|
|
|
|
1,
|
|
|
|
UpgradeableLoaderState::programdata_len(0).unwrap(),
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Set to new authority
|
|
|
|
programdata_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: Some(upgrade_authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&upgrade_authority_address,
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&new_upgrade_authority_address,
|
|
|
|
false,
|
|
|
|
&new_upgrade_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
let state: UpgradeableLoaderState = programdata_account.borrow().state().unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: Some(new_upgrade_authority_address),
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not upgradeable
|
|
|
|
programdata_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: Some(upgrade_authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&upgrade_authority_address,
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
let state: UpgradeableLoaderState = programdata_account.borrow().state().unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: None,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Authority did not sign
|
|
|
|
programdata_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: Some(upgrade_authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&upgrade_authority_address,
|
|
|
|
false,
|
|
|
|
&upgrade_authority_account
|
|
|
|
),
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: wrong authority
|
|
|
|
programdata_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: Some(upgrade_authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
2021-01-08 09:37:57 -08:00
|
|
|
Err(InstructionError::IncorrectAuthority),
|
2020-12-14 15:35:10 -08:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&Pubkey::new_unique(),
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&new_upgrade_authority_address,
|
|
|
|
false,
|
|
|
|
&new_upgrade_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: No authority
|
|
|
|
programdata_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::ProgramData {
|
|
|
|
slot,
|
|
|
|
upgrade_authority_address: None,
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
2021-01-08 09:37:57 -08:00
|
|
|
Err(InstructionError::Immutable),
|
2020-12-14 15:35:10 -08:00
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&Pubkey::new_unique(),
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
),
|
|
|
|
],
|
|
|
|
&bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(),
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not a ProgramData account
|
|
|
|
programdata_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::Program {
|
|
|
|
programdata_address: Pubkey::new_unique(),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::InvalidAccountData),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&Pubkey::new_unique(),
|
|
|
|
true,
|
|
|
|
&upgrade_authority_account
|
|
|
|
),
|
|
|
|
],
|
|
|
|
&bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(),
|
|
|
|
&mut MockInvokeContext::default()
|
2021-01-08 09:37:57 -08:00
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bpf_loader_upgradeable_set_buffer_authority() {
|
|
|
|
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap();
|
|
|
|
let authority_address = Pubkey::new_unique();
|
|
|
|
let authority_account = Account::new_ref(1, 0, &Pubkey::new_unique());
|
|
|
|
let new_authority_address = Pubkey::new_unique();
|
|
|
|
let new_authority_account = Account::new_ref(1, 0, &Pubkey::new_unique());
|
|
|
|
let buffer_address = Pubkey::new_unique();
|
|
|
|
let buffer_account = Account::new_ref(
|
|
|
|
1,
|
|
|
|
UpgradeableLoaderState::buffer_len(0).unwrap(),
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Set to new authority
|
|
|
|
buffer_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new_readonly(&authority_address, true, &authority_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&new_authority_address,
|
|
|
|
false,
|
|
|
|
&new_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(new_authority_address),
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not upgradeable
|
|
|
|
buffer_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Ok(()),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new_readonly(&authority_address, true, &authority_account)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
let state: UpgradeableLoaderState = buffer_account.borrow().state().unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
state,
|
|
|
|
UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: None,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Authority did not sign
|
|
|
|
buffer_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::MissingRequiredSignature),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new_readonly(&authority_address, false, &authority_account),
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: wrong authority
|
|
|
|
buffer_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: Some(authority_address),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::IncorrectAuthority),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new_readonly(&Pubkey::new_unique(), true, &authority_account),
|
|
|
|
KeyedAccount::new_readonly(
|
|
|
|
&new_authority_address,
|
|
|
|
false,
|
|
|
|
&new_authority_account
|
|
|
|
)
|
|
|
|
],
|
|
|
|
&instruction,
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: No authority
|
|
|
|
buffer_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::Buffer {
|
|
|
|
authority_address: None,
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::Immutable),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new_readonly(&Pubkey::new_unique(), true, &authority_account),
|
|
|
|
],
|
|
|
|
&bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(),
|
|
|
|
&mut MockInvokeContext::default()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Case: Not a Buffer account
|
|
|
|
buffer_account
|
|
|
|
.borrow_mut()
|
|
|
|
.set_state(&UpgradeableLoaderState::Program {
|
|
|
|
programdata_address: Pubkey::new_unique(),
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
Err(InstructionError::InvalidAccountData),
|
|
|
|
process_instruction(
|
|
|
|
&bpf_loader_upgradeable::id(),
|
|
|
|
&[
|
|
|
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
|
|
|
KeyedAccount::new_readonly(&Pubkey::new_unique(), true, &authority_account),
|
|
|
|
],
|
|
|
|
&bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(),
|
|
|
|
&mut MockInvokeContext::default()
|
2020-08-11 16:11:52 -07:00
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-05-08 12:37:04 -07:00
|
|
|
/// fuzzing utility function
|
|
|
|
fn fuzz<F>(
|
|
|
|
bytes: &[u8],
|
|
|
|
outer_iters: usize,
|
|
|
|
inner_iters: usize,
|
|
|
|
offset: Range<usize>,
|
|
|
|
value: Range<u8>,
|
|
|
|
work: F,
|
|
|
|
) where
|
|
|
|
F: Fn(&mut [u8]),
|
|
|
|
{
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
for _ in 0..outer_iters {
|
|
|
|
let mut mangled_bytes = bytes.to_vec();
|
|
|
|
for _ in 0..inner_iters {
|
|
|
|
let offset = rng.gen_range(offset.start, offset.end);
|
|
|
|
let value = rng.gen_range(value.start, value.end);
|
|
|
|
mangled_bytes[offset] = value;
|
|
|
|
work(&mut mangled_bytes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[ignore]
|
|
|
|
fn test_fuzz() {
|
2020-10-19 12:12:08 -07:00
|
|
|
let program_id = solana_sdk::pubkey::new_rand();
|
|
|
|
let program_key = solana_sdk::pubkey::new_rand();
|
2020-05-08 12:37:04 -07:00
|
|
|
|
|
|
|
// Create program account
|
2020-08-11 16:11:52 -07:00
|
|
|
let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed");
|
2020-05-08 12:37:04 -07:00
|
|
|
let mut elf = Vec::new();
|
|
|
|
file.read_to_end(&mut elf).unwrap();
|
|
|
|
|
2020-06-13 13:20:08 -07:00
|
|
|
// Mangle the whole file
|
2020-05-08 12:37:04 -07:00
|
|
|
fuzz(
|
|
|
|
&elf,
|
|
|
|
1_000_000_000,
|
|
|
|
100,
|
|
|
|
0..elf.len(),
|
|
|
|
0..255,
|
|
|
|
|bytes: &mut [u8]| {
|
|
|
|
let program_account = Account::new_ref(1, 0, &program_id);
|
|
|
|
program_account.borrow_mut().data = bytes.to_vec();
|
|
|
|
program_account.borrow_mut().executable = true;
|
|
|
|
|
|
|
|
let parameter_account = Account::new_ref(1, 0, &program_id);
|
|
|
|
let keyed_accounts = vec![
|
|
|
|
KeyedAccount::new(&program_key, false, &program_account),
|
|
|
|
KeyedAccount::new(&program_key, false, ¶meter_account),
|
|
|
|
];
|
|
|
|
|
|
|
|
let _result = process_instruction(
|
|
|
|
&bpf_loader::id(),
|
|
|
|
&keyed_accounts,
|
2020-05-15 09:35:43 -07:00
|
|
|
&[],
|
2020-05-08 12:37:04 -07:00
|
|
|
&mut MockInvokeContext::default(),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2018-10-31 10:59:56 -07:00
|
|
|
}
|