Allow programs to realloc their accounts within limits (#19475)
This commit is contained in:
18
programs/bpf/rust/realloc_invoke/src/instructions.rs
Normal file
18
programs/bpf/rust/realloc_invoke/src/instructions.rs
Normal file
@ -0,0 +1,18 @@
|
||||
//! @brief Example Rust-based BPF realloc test program
|
||||
|
||||
pub const INVOKE_REALLOC_ZERO_RO: u8 = 0;
|
||||
pub const INVOKE_REALLOC_ZERO: u8 = 1;
|
||||
pub const INVOKE_REALLOC_MAX_PLUS_ONE: u8 = 2;
|
||||
pub const INVOKE_REALLOC_EXTEND_MAX: u8 = 3;
|
||||
pub const INVOKE_REALLOC_AND_ASSIGN: u8 = 4;
|
||||
pub const INVOKE_REALLOC_AND_ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM: u8 = 5;
|
||||
pub const INVOKE_ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM_AND_REALLOC: u8 = 6;
|
||||
pub const INVOKE_REALLOC_INVOKE_CHECK: u8 = 7;
|
||||
pub const INVOKE_OVERFLOW: u8 = 8;
|
||||
pub const INVOKE_REALLOC_TO: u8 = 9;
|
||||
pub const INVOKE_REALLOC_RECURSIVE: u8 = 10;
|
||||
pub const INVOKE_CREATE_ACCOUNT_REALLOC_CHECK: u8 = 11;
|
||||
pub const INVOKE_DEALLOC_AND_ASSIGN: u8 = 12;
|
||||
pub const INVOKE_REALLOC_MAX_TWICE: u8 = 13;
|
||||
pub const INVOKE_REALLOC_MAX_INVOKE_MAX: u8 = 14;
|
||||
pub const INVOKE_INVOKE_MAX_TWICE: u8 = 15;
|
301
programs/bpf/rust/realloc_invoke/src/lib.rs
Normal file
301
programs/bpf/rust/realloc_invoke/src/lib.rs
Normal file
@ -0,0 +1,301 @@
|
||||
//! @brief Example Rust-based BPF realloc test program
|
||||
|
||||
pub mod instructions;
|
||||
|
||||
extern crate solana_program;
|
||||
use crate::instructions::*;
|
||||
use solana_bpf_rust_realloc::instructions::*;
|
||||
use solana_program::{
|
||||
account_info::AccountInfo,
|
||||
entrypoint,
|
||||
entrypoint::ProgramResult,
|
||||
entrypoint::MAX_PERMITTED_DATA_INCREASE,
|
||||
instruction::{AccountMeta, Instruction},
|
||||
msg,
|
||||
program::invoke,
|
||||
pubkey::Pubkey,
|
||||
system_instruction, system_program,
|
||||
};
|
||||
use std::convert::TryInto;
|
||||
|
||||
entrypoint!(process_instruction);
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn process_instruction(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
instruction_data: &[u8],
|
||||
) -> ProgramResult {
|
||||
let account = &accounts[0];
|
||||
let invoke_program_id = accounts[1].key;
|
||||
let pre_len = account.data_len();
|
||||
let mut bump = 0;
|
||||
|
||||
match instruction_data[0] {
|
||||
INVOKE_REALLOC_ZERO_RO => {
|
||||
msg!("invoke realloc to zero of ro account");
|
||||
// Realloc RO account
|
||||
let mut instruction = realloc(invoke_program_id, account.key, 0, &mut bump);
|
||||
instruction.accounts[0].is_writable = false;
|
||||
invoke(&instruction, accounts)?;
|
||||
}
|
||||
INVOKE_REALLOC_ZERO => {
|
||||
msg!("invoke realloc to zero");
|
||||
invoke(
|
||||
&realloc(invoke_program_id, account.key, 0, &mut bump),
|
||||
accounts,
|
||||
)?;
|
||||
assert_eq!(0, account.data_len());
|
||||
}
|
||||
INVOKE_REALLOC_MAX_PLUS_ONE => {
|
||||
msg!("invoke realloc max + 1");
|
||||
invoke(
|
||||
&realloc(
|
||||
invoke_program_id,
|
||||
account.key,
|
||||
MAX_PERMITTED_DATA_INCREASE + 1,
|
||||
&mut bump,
|
||||
),
|
||||
accounts,
|
||||
)?;
|
||||
}
|
||||
INVOKE_REALLOC_EXTEND_MAX => {
|
||||
msg!("invoke realloc max");
|
||||
invoke(
|
||||
&realloc_extend(
|
||||
invoke_program_id,
|
||||
account.key,
|
||||
MAX_PERMITTED_DATA_INCREASE,
|
||||
&mut bump,
|
||||
),
|
||||
accounts,
|
||||
)?;
|
||||
assert_eq!(pre_len + MAX_PERMITTED_DATA_INCREASE, account.data_len());
|
||||
}
|
||||
INVOKE_REALLOC_MAX_TWICE => {
|
||||
msg!("invoke realloc max twice");
|
||||
invoke(
|
||||
&realloc(
|
||||
invoke_program_id,
|
||||
account.key,
|
||||
MAX_PERMITTED_DATA_INCREASE,
|
||||
&mut bump,
|
||||
),
|
||||
accounts,
|
||||
)?;
|
||||
let new_len = pre_len + MAX_PERMITTED_DATA_INCREASE;
|
||||
assert_eq!(new_len, account.data_len());
|
||||
account.realloc(new_len + MAX_PERMITTED_DATA_INCREASE, false)?;
|
||||
assert_eq!(new_len + MAX_PERMITTED_DATA_INCREASE, account.data_len());
|
||||
}
|
||||
INVOKE_REALLOC_AND_ASSIGN => {
|
||||
msg!("invoke realloc and assign");
|
||||
invoke(
|
||||
&Instruction::new_with_bytes(
|
||||
*invoke_program_id,
|
||||
&[REALLOC_AND_ASSIGN],
|
||||
vec![AccountMeta::new(*account.key, false)],
|
||||
),
|
||||
accounts,
|
||||
)?;
|
||||
assert_eq!(pre_len + MAX_PERMITTED_DATA_INCREASE, account.data_len());
|
||||
assert_eq!(*account.owner, system_program::id());
|
||||
}
|
||||
INVOKE_REALLOC_AND_ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM => {
|
||||
msg!("invoke realloc and assign to self via system program");
|
||||
invoke(
|
||||
&Instruction::new_with_bytes(
|
||||
*accounts[1].key,
|
||||
&[REALLOC_AND_ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM],
|
||||
vec![
|
||||
AccountMeta::new(*account.key, true),
|
||||
AccountMeta::new_readonly(*accounts[2].key, false),
|
||||
],
|
||||
),
|
||||
accounts,
|
||||
)?;
|
||||
}
|
||||
INVOKE_ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM_AND_REALLOC => {
|
||||
msg!("invoke assign to self and realloc via system program");
|
||||
invoke(
|
||||
&Instruction::new_with_bytes(
|
||||
*accounts[1].key,
|
||||
&[ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM_AND_REALLOC],
|
||||
vec![
|
||||
AccountMeta::new(*account.key, true),
|
||||
AccountMeta::new_readonly(*accounts[2].key, false),
|
||||
],
|
||||
),
|
||||
accounts,
|
||||
)?;
|
||||
}
|
||||
INVOKE_REALLOC_INVOKE_CHECK => {
|
||||
msg!("realloc invoke check size");
|
||||
account.realloc(100, false)?;
|
||||
assert_eq!(100, account.data_len());
|
||||
account.try_borrow_mut_data()?[pre_len..].fill(2);
|
||||
invoke(
|
||||
&Instruction::new_with_bytes(
|
||||
*accounts[1].key,
|
||||
&[CHECK],
|
||||
vec![AccountMeta::new(*account.key, false)],
|
||||
),
|
||||
accounts,
|
||||
)?;
|
||||
}
|
||||
INVOKE_REALLOC_TO => {
|
||||
let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::<usize>());
|
||||
let new_len = usize::from_le_bytes(bytes.try_into().unwrap());
|
||||
msg!("realloc to {}", new_len);
|
||||
account.realloc(new_len, false)?;
|
||||
assert_eq!(new_len, account.data_len());
|
||||
if pre_len < new_len {
|
||||
account.try_borrow_mut_data()?[pre_len..].fill(instruction_data[1]);
|
||||
}
|
||||
}
|
||||
INVOKE_REALLOC_RECURSIVE => {
|
||||
msg!("realloc invoke recursive");
|
||||
let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::<usize>());
|
||||
let new_len = usize::from_le_bytes(bytes.try_into().unwrap());
|
||||
account.realloc(new_len, false)?;
|
||||
assert_eq!(new_len, account.data_len());
|
||||
account.try_borrow_mut_data()?[pre_len..].fill(instruction_data[1]);
|
||||
let final_len: usize = 200;
|
||||
let mut new_instruction_data = vec![];
|
||||
new_instruction_data.extend_from_slice(&[INVOKE_REALLOC_TO, 2]);
|
||||
new_instruction_data.extend_from_slice(&final_len.to_le_bytes());
|
||||
invoke(
|
||||
&Instruction::new_with_bytes(
|
||||
*program_id,
|
||||
&new_instruction_data,
|
||||
vec![
|
||||
AccountMeta::new(*account.key, false),
|
||||
AccountMeta::new_readonly(*accounts[1].key, false),
|
||||
],
|
||||
),
|
||||
accounts,
|
||||
)?;
|
||||
assert_eq!(final_len, account.data_len());
|
||||
let data = account.try_borrow_mut_data()?;
|
||||
for i in 0..new_len {
|
||||
assert_eq!(data[i], instruction_data[1]);
|
||||
}
|
||||
for i in new_len..final_len {
|
||||
assert_eq!(data[i], new_instruction_data[1]);
|
||||
}
|
||||
}
|
||||
INVOKE_CREATE_ACCOUNT_REALLOC_CHECK => {
|
||||
msg!("Create new account, realloc, and check");
|
||||
let pre_len: usize = 100;
|
||||
invoke(
|
||||
&system_instruction::create_account(
|
||||
accounts[0].key,
|
||||
accounts[1].key,
|
||||
1,
|
||||
pre_len as u64,
|
||||
program_id,
|
||||
),
|
||||
accounts,
|
||||
)?;
|
||||
assert_eq!(pre_len, accounts[1].data_len());
|
||||
accounts[1].realloc(pre_len + 1, false)?;
|
||||
assert_eq!(pre_len + 1, accounts[1].data_len());
|
||||
assert_eq!(accounts[1].owner, program_id);
|
||||
let final_len: usize = 200;
|
||||
let mut new_instruction_data = vec![];
|
||||
new_instruction_data.extend_from_slice(&[INVOKE_REALLOC_TO, 2]);
|
||||
new_instruction_data.extend_from_slice(&final_len.to_le_bytes());
|
||||
invoke(
|
||||
&Instruction::new_with_bytes(
|
||||
*program_id,
|
||||
&new_instruction_data,
|
||||
vec![
|
||||
AccountMeta::new(*accounts[1].key, false),
|
||||
AccountMeta::new_readonly(*accounts[3].key, false),
|
||||
],
|
||||
),
|
||||
accounts,
|
||||
)?;
|
||||
assert_eq!(final_len, accounts[1].data_len());
|
||||
}
|
||||
INVOKE_DEALLOC_AND_ASSIGN => {
|
||||
msg!("realloc zerod");
|
||||
let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::<usize>());
|
||||
let pre_len = usize::from_le_bytes(bytes.try_into().unwrap());
|
||||
let new_len = pre_len * 2;
|
||||
assert_eq!(pre_len, 100);
|
||||
{
|
||||
let data = account.try_borrow_mut_data()?;
|
||||
for i in 0..pre_len {
|
||||
assert_eq!(data[i], instruction_data[1]);
|
||||
}
|
||||
}
|
||||
|
||||
invoke(
|
||||
&Instruction::new_with_bytes(
|
||||
*accounts[2].key,
|
||||
&[DEALLOC_AND_ASSIGN_TO_CALLER],
|
||||
vec![
|
||||
AccountMeta::new(*account.key, false),
|
||||
AccountMeta::new_readonly(*accounts[1].key, false),
|
||||
],
|
||||
),
|
||||
accounts,
|
||||
)?;
|
||||
assert_eq!(account.owner, program_id);
|
||||
assert_eq!(account.data_len(), 0);
|
||||
account.realloc(new_len, false)?;
|
||||
assert_eq!(account.data_len(), new_len);
|
||||
{
|
||||
let data = account.try_borrow_mut_data()?;
|
||||
for i in 0..new_len {
|
||||
assert_eq!(data[i], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
INVOKE_REALLOC_MAX_INVOKE_MAX => {
|
||||
msg!("invoke realloc max invoke max");
|
||||
assert_eq!(0, account.data_len());
|
||||
account.realloc(MAX_PERMITTED_DATA_INCREASE, false)?;
|
||||
assert_eq!(MAX_PERMITTED_DATA_INCREASE, account.data_len());
|
||||
account.assign(invoke_program_id);
|
||||
assert_eq!(account.owner, invoke_program_id);
|
||||
invoke(
|
||||
&realloc_extend(
|
||||
invoke_program_id,
|
||||
account.key,
|
||||
MAX_PERMITTED_DATA_INCREASE,
|
||||
&mut bump,
|
||||
),
|
||||
accounts,
|
||||
)?;
|
||||
}
|
||||
INVOKE_INVOKE_MAX_TWICE => {
|
||||
msg!("invoke invoke max twice");
|
||||
assert_eq!(0, account.data_len());
|
||||
account.assign(accounts[2].key);
|
||||
assert_eq!(account.owner, accounts[2].key);
|
||||
invoke(
|
||||
&realloc(
|
||||
accounts[2].key,
|
||||
account.key,
|
||||
MAX_PERMITTED_DATA_INCREASE,
|
||||
&mut bump,
|
||||
),
|
||||
accounts,
|
||||
)?;
|
||||
invoke(
|
||||
&realloc_extend(
|
||||
accounts[2].key,
|
||||
account.key,
|
||||
MAX_PERMITTED_DATA_INCREASE,
|
||||
&mut bump,
|
||||
),
|
||||
accounts,
|
||||
)?;
|
||||
panic!("last invoke should fail");
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
Reference in New Issue
Block a user