Allow programs to realloc their accounts within limits (#19475)

This commit is contained in:
Jack May
2021-09-28 01:13:03 -07:00
committed by GitHub
parent 578efdd59f
commit 4e27543415
21 changed files with 1536 additions and 78 deletions

View File

@ -1,4 +1,6 @@
use crate::{clock::Epoch, program_error::ProgramError, pubkey::Pubkey};
use crate::{
clock::Epoch, program_error::ProgramError, program_memory::sol_memset, pubkey::Pubkey,
};
use std::{
cell::{Ref, RefCell, RefMut},
cmp, fmt,
@ -114,6 +116,53 @@ impl<'a> AccountInfo<'a> {
.map_err(|_| ProgramError::AccountBorrowFailed)
}
/// Realloc the account's data and optionally zero-initialize the new
/// memory.
///
/// Note: Account data can be increased within a single call by up to
/// `solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE` bytes.
///
/// Note: Memory used to grow is already zero-initialized upon program
/// entrypoint and re-zeroing it wastes compute units. If within the same
/// call a program reallocs from larger to smaller and back to larger again
/// the new space could contain stale data. Pass `true` for `zero_init` in
/// this case, otherwise compute units will be wasted re-zero-initializing.
pub fn realloc(&self, new_len: usize, zero_init: bool) -> Result<(), ProgramError> {
let orig_len = self.data_len();
// realloc
unsafe {
// First set new length in the serialized data
let ptr = self.try_borrow_mut_data()?.as_mut_ptr().offset(-8) as *mut u64;
*ptr = new_len as u64;
// Then set the new length in the local slice
let ptr = &mut *(((self.data.as_ptr() as *const u64).offset(1) as u64) as *mut u64);
*ptr = new_len as u64;
}
// zero-init if requested
if zero_init && new_len > orig_len {
sol_memset(
&mut self.try_borrow_mut_data()?[orig_len..],
0,
new_len.saturating_sub(orig_len),
);
}
Ok(())
}
pub fn assign(&self, new_owner: &Pubkey) {
// Set the non-mut owner field
unsafe {
std::ptr::write_volatile(
self.owner as *const Pubkey as *mut [u8; 32],
new_owner.to_bytes(),
);
}
}
pub fn new(
key: &'a Pubkey,
is_signer: bool,

View File

@ -66,8 +66,8 @@ pub enum InstructionError {
#[error("sum of account balances before and after instruction do not match")]
UnbalancedInstruction,
/// Program modified an account's program id
#[error("instruction modified the program id of an account")]
/// Program illegally modified an account's program id
#[error("instruction illegally modified the program id of an account")]
ModifiedProgramId,
/// Program spent the lamports of an account that doesn't belong to it
@ -103,8 +103,8 @@ pub enum InstructionError {
#[error("insufficient account keys for instruction")]
NotEnoughAccountKeys,
/// A non-system program changed the size of the account data
#[error("non-system instruction changed account size")]
/// Program other than the account's owner changed the size of the account data
#[error("program other than the account's owner changed the size of the account data")]
AccountDataSizeChanged,
/// The instruction expected an executable account