Refactor: Remove KeyedAccount
from program runtime (#22226)
* Makes error handling in BorrowedAccount optional. Adds BorrowedAccount ::get_rent_epoch(). Exposes InstructionContext::get_index_in_transaction(). Turns accounts and account_keys into pinned boxed slices. * Introduces "unsafe" to InvokeContext::push(). * Turns &TransactionContext into &mut TransactionContext in InvokeContext. * Push and pop InstructionContext in InvokeContext. Makes test_process_cross_program and test_native_invoke symmetric. Removes the borrow check from test_invoke_context_verify. * Removes keyed_accounts from prepare_instruction() * Removes usage of invoke_stack. * Removes keyed_accounts from program-test. * Removes caller_write_privileges. * Removes keyed_accounts from BPF parameter (de-)serialization.
This commit is contained in:
committed by
GitHub
parent
672fed04cb
commit
73e6038986
@ -6,7 +6,10 @@ use crate::{
|
||||
lamports::LamportsError,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::{
|
||||
cell::{RefCell, RefMut},
|
||||
pin::Pin,
|
||||
};
|
||||
|
||||
pub type TransactionAccount = (Pubkey, AccountSharedData);
|
||||
|
||||
@ -23,8 +26,8 @@ pub struct InstructionAccount {
|
||||
/// This context is valid for the entire duration of a transaction being processed.
|
||||
#[derive(Debug)]
|
||||
pub struct TransactionContext {
|
||||
account_keys: Vec<Pubkey>,
|
||||
accounts: Vec<RefCell<AccountSharedData>>,
|
||||
account_keys: Pin<Box<[Pubkey]>>,
|
||||
accounts: Pin<Box<[RefCell<AccountSharedData>]>>,
|
||||
instruction_context_capacity: usize,
|
||||
instruction_context_stack: Vec<InstructionContext>,
|
||||
return_data: (Pubkey, Vec<u8>),
|
||||
@ -36,13 +39,14 @@ impl TransactionContext {
|
||||
transaction_accounts: Vec<TransactionAccount>,
|
||||
instruction_context_capacity: usize,
|
||||
) -> Self {
|
||||
let (account_keys, accounts) = transaction_accounts
|
||||
.into_iter()
|
||||
.map(|(key, account)| (key, RefCell::new(account)))
|
||||
.unzip();
|
||||
let (account_keys, accounts): (Vec<Pubkey>, Vec<RefCell<AccountSharedData>>) =
|
||||
transaction_accounts
|
||||
.into_iter()
|
||||
.map(|(key, account)| (key, RefCell::new(account)))
|
||||
.unzip();
|
||||
Self {
|
||||
account_keys,
|
||||
accounts,
|
||||
account_keys: Pin::new(account_keys.into_boxed_slice()),
|
||||
accounts: Pin::new(accounts.into_boxed_slice()),
|
||||
instruction_context_capacity,
|
||||
instruction_context_stack: Vec::with_capacity(instruction_context_capacity),
|
||||
return_data: (Pubkey::default(), Vec::new()),
|
||||
@ -51,10 +55,10 @@ impl TransactionContext {
|
||||
|
||||
/// Used by the bank in the runtime to write back the processed accounts
|
||||
pub fn deconstruct(self) -> Vec<TransactionAccount> {
|
||||
self.account_keys
|
||||
Vec::from(Pin::into_inner(self.account_keys))
|
||||
.into_iter()
|
||||
.zip(
|
||||
self.accounts
|
||||
Vec::from(Pin::into_inner(self.accounts))
|
||||
.into_iter()
|
||||
.map(|account| account.into_inner()),
|
||||
)
|
||||
@ -66,8 +70,7 @@ impl TransactionContext {
|
||||
if !self.instruction_context_stack.is_empty() {
|
||||
return Err(InstructionError::CallDepth);
|
||||
}
|
||||
Ok(self
|
||||
.accounts
|
||||
Ok(Vec::from(Pin::into_inner(self.accounts))
|
||||
.into_iter()
|
||||
.map(|account| account.into_inner())
|
||||
.collect())
|
||||
@ -243,21 +246,29 @@ impl InstructionContext {
|
||||
.map(|index| index.saturating_add(self.program_accounts.len()))
|
||||
}
|
||||
|
||||
/// Translates the given instruction wide index into a transaction wide index
|
||||
pub fn get_index_in_transaction(
|
||||
&self,
|
||||
index_in_instruction: usize,
|
||||
) -> Result<usize, InstructionError> {
|
||||
if index_in_instruction < self.program_accounts.len() {
|
||||
Ok(self.program_accounts[index_in_instruction])
|
||||
} else if index_in_instruction < self.get_number_of_accounts() {
|
||||
Ok(self.instruction_accounts
|
||||
[index_in_instruction.saturating_sub(self.program_accounts.len())]
|
||||
.index_in_transaction)
|
||||
} else {
|
||||
Err(InstructionError::NotEnoughAccountKeys)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to borrow an account from this Instruction
|
||||
pub fn try_borrow_account<'a, 'b: 'a>(
|
||||
&'a self,
|
||||
transaction_context: &'b TransactionContext,
|
||||
index_in_instruction: usize,
|
||||
) -> Result<BorrowedAccount<'a>, InstructionError> {
|
||||
let index_in_transaction = if index_in_instruction < self.program_accounts.len() {
|
||||
self.program_accounts[index_in_instruction]
|
||||
} else if index_in_instruction < self.get_number_of_accounts() {
|
||||
self.instruction_accounts
|
||||
[index_in_instruction.saturating_sub(self.program_accounts.len())]
|
||||
.index_in_transaction
|
||||
} else {
|
||||
return Err(InstructionError::NotEnoughAccountKeys);
|
||||
};
|
||||
let index_in_transaction = self.get_index_in_transaction(index_in_instruction)?;
|
||||
if index_in_transaction >= transaction_context.accounts.len() {
|
||||
return Err(InstructionError::MissingAccount);
|
||||
}
|
||||
@ -331,12 +342,13 @@ impl<'a> BorrowedAccount<'a> {
|
||||
}
|
||||
|
||||
/// Assignes the owner of this account (transaction wide)
|
||||
pub fn set_owner(&mut self, pubkey: Pubkey) -> Result<(), InstructionError> {
|
||||
if !self.is_writable() {
|
||||
return Err(InstructionError::Immutable);
|
||||
pub fn set_owner(&mut self, pubkey: &[u8]) -> Result<(), InstructionError> {
|
||||
self.account.copy_into_owner_from_slice(pubkey);
|
||||
if self.is_writable() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InstructionError::Immutable)
|
||||
}
|
||||
self.account.set_owner(pubkey);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the number of lamports of this account (transaction wide)
|
||||
@ -346,11 +358,12 @@ impl<'a> BorrowedAccount<'a> {
|
||||
|
||||
/// Overwrites the number of lamports of this account (transaction wide)
|
||||
pub fn set_lamports(&mut self, lamports: u64) -> Result<(), InstructionError> {
|
||||
if !self.is_writable() {
|
||||
return Err(InstructionError::Immutable);
|
||||
}
|
||||
self.account.set_lamports(lamports);
|
||||
Ok(())
|
||||
if self.is_writable() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InstructionError::Immutable)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds lamports to this account (transaction wide)
|
||||
@ -376,26 +389,23 @@ impl<'a> BorrowedAccount<'a> {
|
||||
self.account.data()
|
||||
}
|
||||
|
||||
/// Returns a writable slice of the account data (transaction wide)
|
||||
pub fn get_data_mut(&mut self) -> Result<&mut [u8], InstructionError> {
|
||||
if !self.is_writable() {
|
||||
return Err(InstructionError::Immutable);
|
||||
/// Overwrites the account data and size (transaction wide)
|
||||
pub fn set_data(&mut self, data: &[u8]) -> Result<(), InstructionError> {
|
||||
if data.len() == self.account.data().len() {
|
||||
self.account.data_as_mut_slice().copy_from_slice(data);
|
||||
} else {
|
||||
self.account.set_data_from_slice(data);
|
||||
}
|
||||
if self.is_writable() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InstructionError::Immutable)
|
||||
}
|
||||
Ok(self.account.data_as_mut_slice())
|
||||
}
|
||||
|
||||
/// Guarded alternative to `get_data_mut()?.copy_from_slice()` which checks if the account size matches
|
||||
pub fn copy_from_slice(&mut self, data: &[u8]) -> Result<(), InstructionError> {
|
||||
if !self.is_writable() {
|
||||
return Err(InstructionError::Immutable);
|
||||
}
|
||||
let account_data = self.account.data_as_mut_slice();
|
||||
if data.len() != account_data.len() {
|
||||
return Err(InstructionError::AccountDataSizeChanged);
|
||||
}
|
||||
account_data.copy_from_slice(data);
|
||||
Ok(())
|
||||
}
|
||||
/*pub fn realloc(&self, new_len: usize, zero_init: bool) {
|
||||
// TODO
|
||||
}*/
|
||||
|
||||
/// Deserializes the account data into a state
|
||||
pub fn get_state<T: serde::de::DeserializeOwned>(&self) -> Result<T, InstructionError> {
|
||||
@ -406,22 +416,20 @@ impl<'a> BorrowedAccount<'a> {
|
||||
|
||||
/// Serializes a state into the account data
|
||||
pub fn set_state<T: serde::Serialize>(&mut self, state: &T) -> Result<(), InstructionError> {
|
||||
if !self.is_writable() {
|
||||
return Err(InstructionError::Immutable);
|
||||
}
|
||||
let data = self.account.data_as_mut_slice();
|
||||
let serialized_size =
|
||||
bincode::serialized_size(state).map_err(|_| InstructionError::GenericError)?;
|
||||
if serialized_size > data.len() as u64 {
|
||||
return Err(InstructionError::AccountDataTooSmall);
|
||||
}
|
||||
bincode::serialize_into(&mut *data, state).map_err(|_| InstructionError::GenericError)
|
||||
bincode::serialize_into(&mut *data, state).map_err(|_| InstructionError::GenericError)?;
|
||||
if self.is_writable() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InstructionError::Immutable)
|
||||
}
|
||||
}
|
||||
|
||||
/*pub fn realloc(&self, new_len: usize, zero_init: bool) {
|
||||
// TODO
|
||||
}*/
|
||||
|
||||
/// Returns whether this account is executable (transaction wide)
|
||||
pub fn is_executable(&self) -> bool {
|
||||
self.account.executable()
|
||||
@ -429,11 +437,17 @@ impl<'a> BorrowedAccount<'a> {
|
||||
|
||||
/// Configures whether this account is executable (transaction wide)
|
||||
pub fn set_executable(&mut self, is_executable: bool) -> Result<(), InstructionError> {
|
||||
if !self.is_writable() {
|
||||
return Err(InstructionError::Immutable);
|
||||
}
|
||||
self.account.set_executable(is_executable);
|
||||
Ok(())
|
||||
if self.is_writable() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InstructionError::Immutable)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the rent epoch of this account (transaction wide)
|
||||
pub fn get_rent_epoch(&self) -> u64 {
|
||||
self.account.rent_epoch()
|
||||
}
|
||||
|
||||
/// Returns whether this account is a signer (instruction wide)
|
||||
|
Reference in New Issue
Block a user