mango-v4/programs/mango-v4/src/accounts_zerocopy.rs

241 lines
7.2 KiB
Rust

use anchor_lang::prelude::*;
use anchor_lang::ZeroCopy;
use arrayref::array_ref;
use std::cell::RefMut;
use std::{cell::Ref, mem};
/// Functions should prefer to work with AccountReader where possible, to abstract over
/// AccountInfo and AccountSharedData. That way the functions become usable in the program
/// and in client code.
// NOTE: would love to use solana's ReadableAccount, but that's in solana_sdk -- unavailable for programs
pub trait AccountReader {
fn owner(&self) -> &Pubkey;
fn data(&self) -> &[u8];
}
/// Like AccountReader, but can also get the account pubkey
pub trait KeyedAccountReader: AccountReader {
fn key(&self) -> &Pubkey;
}
/// A Ref to an AccountInfo - makes AccountInfo compatible with AccountReader
pub struct AccountInfoRef<'a, 'info: 'a> {
pub key: &'info Pubkey,
pub owner: &'info Pubkey,
pub data: Ref<'a, &'info mut [u8]>,
}
impl<'a, 'info: 'a> AccountInfoRef<'a, 'info> {
pub fn borrow(account_info: &'a AccountInfo<'info>) -> Result<Self> {
Ok(Self {
key: account_info.key,
owner: account_info.owner,
data: account_info
.data
.try_borrow()
.map_err(|_| ProgramError::AccountBorrowFailed)?,
// Why is the following not acceptable?
//data: account_info.try_borrow_data()?,
})
}
}
pub struct AccountInfoRefMut<'a, 'info: 'a> {
pub key: &'info Pubkey,
pub owner: &'info Pubkey,
pub data: RefMut<'a, &'info mut [u8]>,
}
impl<'a, 'info: 'a> AccountInfoRefMut<'a, 'info> {
pub fn borrow(account_info: &'a AccountInfo<'info>) -> Result<Self> {
Ok(Self {
key: account_info.key,
owner: account_info.owner,
data: account_info
.data
.try_borrow_mut()
.map_err(|_| ProgramError::AccountBorrowFailed)?,
})
}
}
impl<'info, 'a> AccountReader for AccountInfoRef<'info, 'a> {
fn owner(&self) -> &Pubkey {
self.owner
}
fn data(&self) -> &[u8] {
&self.data
}
}
impl<'info, 'a> AccountReader for AccountInfoRefMut<'info, 'a> {
fn owner(&self) -> &Pubkey {
self.owner
}
fn data(&self) -> &[u8] {
&self.data
}
}
impl<'info, 'a> KeyedAccountReader for AccountInfoRef<'info, 'a> {
fn key(&self) -> &Pubkey {
self.key
}
}
impl<'info, 'a> KeyedAccountReader for AccountInfoRefMut<'info, 'a> {
fn key(&self) -> &Pubkey {
self.key
}
}
#[cfg(feature = "solana-sdk")]
impl<T: solana_sdk::account::ReadableAccount> AccountReader for T {
fn owner(&self) -> &Pubkey {
self.owner()
}
fn data(&self) -> &[u8] {
self.data()
}
}
//
// Common traits for loading from account data.
//
pub trait LoadZeroCopy {
/// Using AccountLoader forces a AccountInfo.clone() and then binds the loaded
/// lifetime to the AccountLoader's lifetime. This function avoids both.
/// It checks the account owner and discriminator, then casts the data.
fn load<T: ZeroCopy + Owner>(&self) -> Result<&T>;
/// Same as load(), but doesn't check the discriminator or owner.
fn load_fully_unchecked<T: ZeroCopy + Owner>(&self) -> Result<&T>;
}
pub trait LoadMutZeroCopy {
/// Same as load(), but mut
fn load_mut<T: ZeroCopy + Owner>(&mut self) -> Result<&mut T>;
/// Same as load_fully_unchecked(), but mut
fn load_mut_fully_unchecked<T: ZeroCopy + Owner>(&mut self) -> Result<&mut T>;
}
pub trait LoadZeroCopyRef {
/// Using AccountLoader forces a AccountInfo.clone() and then binds the loaded
/// lifetime to the AccountLoader's lifetime. This function avoids both.
/// It checks the account owner and discriminator, then casts the data.
fn load<T: ZeroCopy + Owner>(&self) -> Result<Ref<T>>;
/// Same as load(), but doesn't check the discriminator or owner.
fn load_fully_unchecked<T: ZeroCopy + Owner>(&self) -> Result<Ref<T>>;
}
pub trait LoadMutZeroCopyRef {
/// Same as load(), but mut
fn load_mut<T: ZeroCopy + Owner>(&self) -> Result<RefMut<T>>;
/// Same as load_fully_unchecked(), but mut
fn load_mut_fully_unchecked<T: ZeroCopy + Owner>(&self) -> Result<RefMut<T>>;
}
impl<A: AccountReader> LoadZeroCopy for A {
fn load<T: ZeroCopy + Owner>(&self) -> Result<&T> {
if self.owner() != &T::owner() {
return Err(ErrorCode::AccountOwnedByWrongProgram.into());
}
let data = self.data();
let disc_bytes = array_ref![data, 0, 8];
if disc_bytes != &T::discriminator() {
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
}
Ok(bytemuck::from_bytes(&data[8..mem::size_of::<T>() + 8]))
}
fn load_fully_unchecked<T: ZeroCopy + Owner>(&self) -> Result<&T> {
Ok(bytemuck::from_bytes(
&self.data()[8..mem::size_of::<T>() + 8],
))
}
}
impl<'info, 'a> LoadMutZeroCopy for AccountInfoRefMut<'info, 'a> {
fn load_mut<T: ZeroCopy + Owner>(&mut self) -> Result<&mut T> {
if self.owner != &T::owner() {
return Err(ErrorCode::AccountOwnedByWrongProgram.into());
}
let disc_bytes = array_ref![self.data, 0, 8];
if disc_bytes != &T::discriminator() {
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
}
Ok(bytemuck::from_bytes_mut(
&mut self.data[8..mem::size_of::<T>() + 8],
))
}
fn load_mut_fully_unchecked<T: ZeroCopy + Owner>(&mut self) -> Result<&mut T> {
Ok(bytemuck::from_bytes_mut(
&mut self.data[8..mem::size_of::<T>() + 8],
))
}
}
impl<'info> LoadZeroCopyRef for AccountInfo<'info> {
fn load<T: ZeroCopy + Owner>(&self) -> Result<Ref<T>> {
if self.owner != &T::owner() {
return Err(ErrorCode::AccountOwnedByWrongProgram.into());
}
let data = self.try_borrow_data()?;
let disc_bytes = array_ref![data, 0, 8];
if disc_bytes != &T::discriminator() {
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
}
Ok(Ref::map(data, |data| {
bytemuck::from_bytes(&data[8..mem::size_of::<T>() + 8])
}))
}
fn load_fully_unchecked<T: ZeroCopy + Owner>(&self) -> Result<Ref<T>> {
let data = self.try_borrow_data()?;
Ok(Ref::map(data, |data| {
bytemuck::from_bytes(&data[8..mem::size_of::<T>() + 8])
}))
}
}
impl<'info> LoadMutZeroCopyRef for AccountInfo<'info> {
fn load_mut<T: ZeroCopy + Owner>(&self) -> Result<RefMut<T>> {
if self.owner != &T::owner() {
return Err(ErrorCode::AccountOwnedByWrongProgram.into());
}
let data = self.try_borrow_mut_data()?;
let disc_bytes = array_ref![data, 0, 8];
if disc_bytes != &T::discriminator() {
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
}
Ok(RefMut::map(data, |data| {
bytemuck::from_bytes_mut(&mut data[8..mem::size_of::<T>() + 8])
}))
}
fn load_mut_fully_unchecked<T: ZeroCopy + Owner>(&self) -> Result<RefMut<T>> {
let data = self.try_borrow_mut_data()?;
Ok(RefMut::map(data, |data| {
bytemuck::from_bytes_mut(&mut data[8..mem::size_of::<T>() + 8])
}))
}
}