Allow programs to realloc their accounts within limits (#19475)
This commit is contained in:
@ -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,
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user