solana/sdk/src/account.rs

1168 lines
38 KiB
Rust
Raw Normal View History

//! The Solana [`Account`] type.
#[cfg(feature = "dev-context-only-utils")]
use qualifier_attr::qualifiers;
use {
crate::{
clock::{Epoch, INITIAL_RENT_EPOCH},
lamports::LamportsError,
pubkey::Pubkey,
},
serde::{
ser::{Serialize, Serializer},
Deserialize,
},
solana_program::{account_info::AccountInfo, debug_account_data::*, sysvar::Sysvar},
std::{
cell::{Ref, RefCell},
Account data direct mapping (#28053) * AccountSharedData: make data_mut() private This ensures that the inner Vec is never handed out. This is in preparation of enforcing that the capacity of the inner vec never shrinks, which is required for direct mapping. * Adds the feature bpf_account_data_direct_mapping. * Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified. * WIP: Memory regions for each instruction account in create_vm(). * Fix serialization benches, run both copy and !copy variants * rbpf-cli: fix build * BorrowedAccount: ensure that account capacity is never reduced Accounts can be directly mapped in address space. Their capacity can't be reduced mid transaction as that would create holes in vm address space that point to invalid host memory. * bpf_load: run serialization tests for both copy and !copy account data * bpf_loader: add Serializer::write_account * fix lints * BorrowedAccount: make_data_mut is host only * Fix unused import warning * Fix lints * cpi: add explicit direct_mapping arg to update_(callee|caller)_account * cpi: rename account_data_or_only_realloc_padding to serialized_data * cpi: add CallerAccount::original_data_len comment * cpi: add update_callee_account direct_mapping test * cpi: add test_update_caller_account_data_direct_mapping and fix bug We used to have a bug in zeroing data when shrinking account, where we zeroed the spare account capacity but not the realloc padding. * cpi: add tests for mutated readonly accounts * cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on * cpi: update_caller_account: ensure that account capacity is always enough Introduce a better way to ensure that account capacity never goes below what might be mapped in memory regions. * cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut() Before we were using BorrowedAccount::get_data_mut() to get the base pointer to the account data, then we were slicing the spare capacity from it. Calling get_data_mut() doesn't work if an account has been closed tho, since the current program doesn't own the account anymore and therefore get_data_mut() errors out. * bpf_loader: fix same lint for the umpteenth time * bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations * programs/sbf: realloc: add test for large write after realloc Add a test that after a realloc does a large write that spans the original account length and the realloc area. This ensures that memory mapping works correctly across the boundary. * programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off By default test banks test with all features on. This ensures we keep testing the existing code until the new feature is enabled. * bpf_loader: tweak memcmp syscall Split the actual memcmp code in a separate function. Remove check indexing the slices since the slices are guaranteed to have the correct length by construction. * bpf_loader: tweak the memset syscall Use slice::fill, which is effectively memset. * bpf_loader: syscalls: update mem syscalls to work with non contiguous memory With direct mapping enabled, accounts can now span multiple memory regions. * fix lint, rebase mem_ops * Implement CoW for writable accounts * Fix CI * Move CoW to the MemoryMapping level * Update after rbpf API change * Fix merge screwup * Add create_vm macro. Fix benches. * cpi: simplify update_caller_account Simplify the logic to update a caller's memory region when a callee causes an account data pointer to change (eg during CoW) * benches/bpf_loader: move serialization out of create_vm bench * benches/bpf_loader: don't copy accounts when direct mapping is on * Fix review nits * bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new When starting at u64::MAX, the chunk iterator would always return the empty sequence (None on the first next()) call, instead of returning a memory access violation. Use checked instead of saturating arithmetic to detect the condition and error out. This commit also adds more tests around boundary conditions. * Fix loader-v3 tests: data_mut => data_as_mut_slice * Fix CI * bpf_loader: fix tuner bench: account must be writable With direct mapping on, invalid writes are caught early meaning the tuner would fail on the first store and not consume the whole budget like the benchmark expects. --------- Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
fmt,
mem::MaybeUninit,
ptr,
rc::Rc,
sync::Arc,
},
};
/// An Account with data that is stored on chain
#[repr(C)]
#[frozen_abi(digest = "HawRVHh7t4d3H3bitWHFt25WhhoDmbJMCfWdESQQoYEy")]
2022-03-17 07:29:49 -07:00
#[derive(Deserialize, PartialEq, Eq, Clone, Default, AbiExample)]
#[serde(rename_all = "camelCase")]
pub struct Account {
2019-03-05 16:28:14 -08:00
/// lamports in the account
pub lamports: u64,
2018-11-12 09:55:28 -08:00
/// data held in this account
#[serde(with = "serde_bytes")]
pub data: Vec<u8>,
/// the program that owns this account. If executable, the program that loads this account.
pub owner: Pubkey,
/// this account's data contains a loaded program (and is now read-only)
pub executable: bool,
/// the epoch at which this account will next owe rent
pub rent_epoch: Epoch,
}
2022-03-17 07:29:49 -07:00
// mod because we need 'Account' below to have the name 'Account' to match expected serialization
mod account_serialize {
use {
crate::{account::ReadableAccount, clock::Epoch, pubkey::Pubkey},
serde::{ser::Serializer, Serialize},
};
#[repr(C)]
#[frozen_abi(digest = "HawRVHh7t4d3H3bitWHFt25WhhoDmbJMCfWdESQQoYEy")]
#[derive(Serialize, AbiExample)]
#[serde(rename_all = "camelCase")]
struct Account<'a> {
lamports: u64,
#[serde(with = "serde_bytes")]
// a slice so we don't have to make a copy just to serialize this
data: &'a [u8],
2022-03-17 07:29:49 -07:00
// can't be &pubkey because abi example doesn't support it
owner: Pubkey,
executable: bool,
rent_epoch: Epoch,
}
2022-03-17 12:23:08 -07:00
/// allows us to implement serialize on AccountSharedData that is equivalent to Account::serialize without making a copy of the Vec<u8>
2022-03-17 07:29:49 -07:00
pub fn serialize_account<S>(
account: &(impl ReadableAccount + Serialize),
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let temp = Account {
lamports: account.lamports(),
data: account.data(),
2022-03-17 07:29:49 -07:00
owner: *account.owner(),
executable: account.executable(),
rent_epoch: account.rent_epoch(),
};
temp.serialize(serializer)
}
}
impl Serialize for Account {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
crate::account::account_serialize::serialize_account(self, serializer)
2022-03-17 07:29:49 -07:00
}
}
impl Serialize for AccountSharedData {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
crate::account::account_serialize::serialize_account(self, serializer)
2022-03-17 07:29:49 -07:00
}
}
2021-03-09 13:06:07 -08:00
/// An Account with data that is stored on chain
/// This will be the in-memory representation of the 'Account' struct data.
2021-03-09 13:06:07 -08:00
/// The existing 'Account' structure cannot easily change due to downstream projects.
#[derive(PartialEq, Eq, Clone, Default, AbiExample, Deserialize)]
#[serde(from = "Account")]
2021-03-09 13:06:07 -08:00
pub struct AccountSharedData {
/// lamports in the account
lamports: u64,
2021-03-09 13:06:07 -08:00
/// data held in this account
data: Arc<Vec<u8>>,
2021-03-09 13:06:07 -08:00
/// the program that owns this account. If executable, the program that loads this account.
owner: Pubkey,
2021-03-09 13:06:07 -08:00
/// this account's data contains a loaded program (and is now read-only)
executable: bool,
2021-03-09 13:06:07 -08:00
/// the epoch at which this account will next owe rent
2021-04-30 10:16:44 -07:00
rent_epoch: Epoch,
2021-03-09 13:06:07 -08:00
}
/// Compares two ReadableAccounts
///
/// Returns true if accounts are essentially equivalent as in all fields are equivalent.
pub fn accounts_equal<T: ReadableAccount, U: ReadableAccount>(me: &T, other: &U) -> bool {
me.lamports() == other.lamports()
&& me.executable() == other.executable()
&& me.rent_epoch() == other.rent_epoch()
&& me.owner() == other.owner()
&& me.data() == other.data()
2021-03-09 13:06:07 -08:00
}
impl From<AccountSharedData> for Account {
fn from(mut other: AccountSharedData) -> Self {
let account_data = Arc::make_mut(&mut other.data);
2021-03-09 13:06:07 -08:00
Self {
lamports: other.lamports,
data: std::mem::take(account_data),
2021-03-09 13:06:07 -08:00
owner: other.owner,
executable: other.executable,
rent_epoch: other.rent_epoch,
}
}
}
impl From<Account> for AccountSharedData {
fn from(other: Account) -> Self {
Self {
lamports: other.lamports,
data: Arc::new(other.data),
2021-03-09 13:06:07 -08:00
owner: other.owner,
executable: other.executable,
rent_epoch: other.rent_epoch,
}
}
}
pub trait WritableAccount: ReadableAccount {
fn set_lamports(&mut self, lamports: u64);
fn checked_add_lamports(&mut self, lamports: u64) -> Result<(), LamportsError> {
self.set_lamports(
self.lamports()
.checked_add(lamports)
.ok_or(LamportsError::ArithmeticOverflow)?,
);
Ok(())
}
fn checked_sub_lamports(&mut self, lamports: u64) -> Result<(), LamportsError> {
self.set_lamports(
self.lamports()
.checked_sub(lamports)
.ok_or(LamportsError::ArithmeticUnderflow)?,
);
Ok(())
}
fn saturating_add_lamports(&mut self, lamports: u64) {
self.set_lamports(self.lamports().saturating_add(lamports))
}
fn saturating_sub_lamports(&mut self, lamports: u64) {
self.set_lamports(self.lamports().saturating_sub(lamports))
}
2021-03-09 13:06:07 -08:00
fn data_as_mut_slice(&mut self) -> &mut [u8];
fn set_owner(&mut self, owner: Pubkey);
fn copy_into_owner_from_slice(&mut self, source: &[u8]);
2021-03-09 13:06:07 -08:00
fn set_executable(&mut self, executable: bool);
fn set_rent_epoch(&mut self, epoch: Epoch);
fn create(
lamports: u64,
data: Vec<u8>,
owner: Pubkey,
executable: bool,
rent_epoch: Epoch,
) -> Self;
}
pub trait ReadableAccount: Sized {
fn lamports(&self) -> u64;
fn data(&self) -> &[u8];
2021-03-09 13:06:07 -08:00
fn owner(&self) -> &Pubkey;
fn executable(&self) -> bool;
fn rent_epoch(&self) -> Epoch;
fn to_account_shared_data(&self) -> AccountSharedData {
AccountSharedData::create(
self.lamports(),
self.data().to_vec(),
*self.owner(),
self.executable(),
self.rent_epoch(),
)
}
2021-03-09 13:06:07 -08:00
}
impl ReadableAccount for Account {
fn lamports(&self) -> u64 {
self.lamports
}
fn data(&self) -> &[u8] {
2021-03-09 13:06:07 -08:00
&self.data
}
fn owner(&self) -> &Pubkey {
&self.owner
}
fn executable(&self) -> bool {
self.executable
}
fn rent_epoch(&self) -> Epoch {
self.rent_epoch
}
}
impl WritableAccount for Account {
fn set_lamports(&mut self, lamports: u64) {
self.lamports = lamports;
}
fn data_as_mut_slice(&mut self) -> &mut [u8] {
&mut self.data
}
fn set_owner(&mut self, owner: Pubkey) {
self.owner = owner;
}
fn copy_into_owner_from_slice(&mut self, source: &[u8]) {
self.owner.as_mut().copy_from_slice(source);
}
2021-03-09 13:06:07 -08:00
fn set_executable(&mut self, executable: bool) {
self.executable = executable;
}
fn set_rent_epoch(&mut self, epoch: Epoch) {
self.rent_epoch = epoch;
}
fn create(
lamports: u64,
data: Vec<u8>,
owner: Pubkey,
executable: bool,
rent_epoch: Epoch,
) -> Self {
Account {
lamports,
data,
owner,
executable,
rent_epoch,
}
}
}
impl WritableAccount for AccountSharedData {
fn set_lamports(&mut self, lamports: u64) {
self.lamports = lamports;
}
fn data_as_mut_slice(&mut self) -> &mut [u8] {
&mut self.data_mut()[..]
2021-03-09 13:06:07 -08:00
}
fn set_owner(&mut self, owner: Pubkey) {
self.owner = owner;
}
fn copy_into_owner_from_slice(&mut self, source: &[u8]) {
self.owner.as_mut().copy_from_slice(source);
}
2021-03-09 13:06:07 -08:00
fn set_executable(&mut self, executable: bool) {
self.executable = executable;
}
fn set_rent_epoch(&mut self, epoch: Epoch) {
self.rent_epoch = epoch;
}
fn create(
lamports: u64,
data: Vec<u8>,
owner: Pubkey,
executable: bool,
rent_epoch: Epoch,
) -> Self {
AccountSharedData {
lamports,
data: Arc::new(data),
2021-03-09 13:06:07 -08:00
owner,
executable,
rent_epoch,
}
}
}
impl ReadableAccount for AccountSharedData {
fn lamports(&self) -> u64 {
self.lamports
}
fn data(&self) -> &[u8] {
2021-03-09 13:06:07 -08:00
&self.data
}
fn owner(&self) -> &Pubkey {
&self.owner
}
fn executable(&self) -> bool {
self.executable
}
fn rent_epoch(&self) -> Epoch {
self.rent_epoch
}
fn to_account_shared_data(&self) -> AccountSharedData {
// avoid data copy here
self.clone()
}
2021-03-09 13:06:07 -08:00
}
impl ReadableAccount for Ref<'_, AccountSharedData> {
fn lamports(&self) -> u64 {
self.lamports
}
fn data(&self) -> &[u8] {
2021-03-09 13:06:07 -08:00
&self.data
}
fn owner(&self) -> &Pubkey {
&self.owner
}
fn executable(&self) -> bool {
self.executable
}
fn rent_epoch(&self) -> Epoch {
self.rent_epoch
}
fn to_account_shared_data(&self) -> AccountSharedData {
AccountSharedData {
lamports: self.lamports(),
// avoid data copy here
data: Arc::clone(&self.data),
owner: *self.owner(),
executable: self.executable(),
rent_epoch: self.rent_epoch(),
}
}
2021-03-09 13:06:07 -08:00
}
impl ReadableAccount for Ref<'_, Account> {
fn lamports(&self) -> u64 {
self.lamports
}
fn data(&self) -> &[u8] {
2021-03-09 13:06:07 -08:00
&self.data
}
fn owner(&self) -> &Pubkey {
&self.owner
}
fn executable(&self) -> bool {
self.executable
}
fn rent_epoch(&self) -> Epoch {
self.rent_epoch
}
}
fn debug_fmt<T: ReadableAccount>(item: &T, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("Account");
f.field("lamports", &item.lamports())
.field("data.len", &item.data().len())
.field("owner", &item.owner())
.field("executable", &item.executable())
.field("rent_epoch", &item.rent_epoch());
debug_account_data(item.data(), &mut f);
f.finish()
2021-03-09 13:06:07 -08:00
}
impl fmt::Debug for Account {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2021-03-09 13:06:07 -08:00
debug_fmt(self, f)
}
}
2021-03-09 13:06:07 -08:00
impl fmt::Debug for AccountSharedData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
debug_fmt(self, f)
}
}
fn shared_new<T: WritableAccount>(lamports: u64, space: usize, owner: &Pubkey) -> T {
T::create(
lamports,
vec![0u8; space],
*owner,
bool::default(),
Epoch::default(),
)
}
2022-02-24 17:49:33 -08:00
fn shared_new_rent_epoch<T: WritableAccount>(
lamports: u64,
space: usize,
owner: &Pubkey,
rent_epoch: Epoch,
) -> T {
T::create(
lamports,
vec![0u8; space],
*owner,
bool::default(),
rent_epoch,
)
}
2021-03-09 13:06:07 -08:00
fn shared_new_ref<T: WritableAccount>(
lamports: u64,
space: usize,
owner: &Pubkey,
) -> Rc<RefCell<T>> {
Rc::new(RefCell::new(shared_new::<T>(lamports, space, owner)))
}
fn shared_new_data<T: serde::Serialize, U: WritableAccount>(
lamports: u64,
state: &T,
owner: &Pubkey,
) -> Result<U, bincode::Error> {
let data = bincode::serialize(state)?;
Ok(U::create(
lamports,
data,
*owner,
bool::default(),
Epoch::default(),
))
}
fn shared_new_ref_data<T: serde::Serialize, U: WritableAccount>(
lamports: u64,
state: &T,
owner: &Pubkey,
) -> Result<RefCell<U>, bincode::Error> {
Ok(RefCell::new(shared_new_data::<T, U>(
lamports, state, owner,
)?))
}
fn shared_new_data_with_space<T: serde::Serialize, U: WritableAccount>(
lamports: u64,
state: &T,
space: usize,
owner: &Pubkey,
) -> Result<U, bincode::Error> {
let mut account = shared_new::<U>(lamports, space, owner);
shared_serialize_data(&mut account, state)?;
Ok(account)
}
fn shared_new_ref_data_with_space<T: serde::Serialize, U: WritableAccount>(
lamports: u64,
state: &T,
space: usize,
owner: &Pubkey,
) -> Result<RefCell<U>, bincode::Error> {
Ok(RefCell::new(shared_new_data_with_space::<T, U>(
lamports, state, space, owner,
)?))
}
fn shared_deserialize_data<T: serde::de::DeserializeOwned, U: ReadableAccount>(
account: &U,
) -> Result<T, bincode::Error> {
bincode::deserialize(account.data())
}
fn shared_serialize_data<T: serde::Serialize, U: WritableAccount>(
account: &mut U,
state: &T,
) -> Result<(), bincode::Error> {
if bincode::serialized_size(state)? > account.data().len() as u64 {
return Err(Box::new(bincode::ErrorKind::SizeLimit));
}
2022-11-09 11:39:38 -08:00
bincode::serialize_into(account.data_as_mut_slice(), state)
2021-03-09 13:06:07 -08:00
}
impl Account {
2020-01-20 15:27:36 -08:00
pub fn new(lamports: u64, space: usize, owner: &Pubkey) -> Self {
2021-03-09 13:06:07 -08:00
shared_new(lamports, space, owner)
}
pub fn new_ref(lamports: u64, space: usize, owner: &Pubkey) -> Rc<RefCell<Self>> {
2021-03-09 13:06:07 -08:00
shared_new_ref(lamports, space, owner)
}
pub fn new_data<T: serde::Serialize>(
lamports: u64,
state: &T,
owner: &Pubkey,
2020-01-20 15:27:36 -08:00
) -> Result<Self, bincode::Error> {
2021-03-09 13:06:07 -08:00
shared_new_data(lamports, state, owner)
}
pub fn new_ref_data<T: serde::Serialize>(
lamports: u64,
state: &T,
owner: &Pubkey,
) -> Result<RefCell<Self>, bincode::Error> {
2021-03-09 13:06:07 -08:00
shared_new_ref_data(lamports, state, owner)
}
pub fn new_data_with_space<T: serde::Serialize>(
lamports: u64,
state: &T,
space: usize,
owner: &Pubkey,
2020-01-20 15:27:36 -08:00
) -> Result<Self, bincode::Error> {
2021-03-09 13:06:07 -08:00
shared_new_data_with_space(lamports, state, space, owner)
}
pub fn new_ref_data_with_space<T: serde::Serialize>(
lamports: u64,
state: &T,
space: usize,
owner: &Pubkey,
) -> Result<RefCell<Self>, bincode::Error> {
2021-03-09 13:06:07 -08:00
shared_new_ref_data_with_space(lamports, state, space, owner)
}
2022-02-24 17:49:33 -08:00
pub fn new_rent_epoch(lamports: u64, space: usize, owner: &Pubkey, rent_epoch: Epoch) -> Self {
shared_new_rent_epoch(lamports, space, owner, rent_epoch)
}
pub fn deserialize_data<T: serde::de::DeserializeOwned>(&self) -> Result<T, bincode::Error> {
2021-03-09 13:06:07 -08:00
shared_deserialize_data(self)
}
2021-03-09 13:06:07 -08:00
pub fn serialize_data<T: serde::Serialize>(&mut self, state: &T) -> Result<(), bincode::Error> {
shared_serialize_data(self, state)
}
}
2021-03-09 13:06:07 -08:00
impl AccountSharedData {
Account data direct mapping (#28053) * AccountSharedData: make data_mut() private This ensures that the inner Vec is never handed out. This is in preparation of enforcing that the capacity of the inner vec never shrinks, which is required for direct mapping. * Adds the feature bpf_account_data_direct_mapping. * Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified. * WIP: Memory regions for each instruction account in create_vm(). * Fix serialization benches, run both copy and !copy variants * rbpf-cli: fix build * BorrowedAccount: ensure that account capacity is never reduced Accounts can be directly mapped in address space. Their capacity can't be reduced mid transaction as that would create holes in vm address space that point to invalid host memory. * bpf_load: run serialization tests for both copy and !copy account data * bpf_loader: add Serializer::write_account * fix lints * BorrowedAccount: make_data_mut is host only * Fix unused import warning * Fix lints * cpi: add explicit direct_mapping arg to update_(callee|caller)_account * cpi: rename account_data_or_only_realloc_padding to serialized_data * cpi: add CallerAccount::original_data_len comment * cpi: add update_callee_account direct_mapping test * cpi: add test_update_caller_account_data_direct_mapping and fix bug We used to have a bug in zeroing data when shrinking account, where we zeroed the spare account capacity but not the realloc padding. * cpi: add tests for mutated readonly accounts * cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on * cpi: update_caller_account: ensure that account capacity is always enough Introduce a better way to ensure that account capacity never goes below what might be mapped in memory regions. * cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut() Before we were using BorrowedAccount::get_data_mut() to get the base pointer to the account data, then we were slicing the spare capacity from it. Calling get_data_mut() doesn't work if an account has been closed tho, since the current program doesn't own the account anymore and therefore get_data_mut() errors out. * bpf_loader: fix same lint for the umpteenth time * bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations * programs/sbf: realloc: add test for large write after realloc Add a test that after a realloc does a large write that spans the original account length and the realloc area. This ensures that memory mapping works correctly across the boundary. * programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off By default test banks test with all features on. This ensures we keep testing the existing code until the new feature is enabled. * bpf_loader: tweak memcmp syscall Split the actual memcmp code in a separate function. Remove check indexing the slices since the slices are guaranteed to have the correct length by construction. * bpf_loader: tweak the memset syscall Use slice::fill, which is effectively memset. * bpf_loader: syscalls: update mem syscalls to work with non contiguous memory With direct mapping enabled, accounts can now span multiple memory regions. * fix lint, rebase mem_ops * Implement CoW for writable accounts * Fix CI * Move CoW to the MemoryMapping level * Update after rbpf API change * Fix merge screwup * Add create_vm macro. Fix benches. * cpi: simplify update_caller_account Simplify the logic to update a caller's memory region when a callee causes an account data pointer to change (eg during CoW) * benches/bpf_loader: move serialization out of create_vm bench * benches/bpf_loader: don't copy accounts when direct mapping is on * Fix review nits * bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new When starting at u64::MAX, the chunk iterator would always return the empty sequence (None on the first next()) call, instead of returning a memory access violation. Use checked instead of saturating arithmetic to detect the condition and error out. This commit also adds more tests around boundary conditions. * Fix loader-v3 tests: data_mut => data_as_mut_slice * Fix CI * bpf_loader: fix tuner bench: account must be writable With direct mapping on, invalid writes are caught early meaning the tuner would fail on the first store and not consume the whole budget like the benchmark expects. --------- Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
pub fn is_shared(&self) -> bool {
Arc::strong_count(&self.data) > 1
}
pub fn reserve(&mut self, additional: usize) {
self.data_mut().reserve(additional)
}
pub fn capacity(&self) -> usize {
self.data.capacity()
}
fn data_mut(&mut self) -> &mut Vec<u8> {
Arc::make_mut(&mut self.data)
}
pub fn resize(&mut self, new_len: usize, value: u8) {
self.data_mut().resize(new_len, value)
}
pub fn extend_from_slice(&mut self, data: &[u8]) {
self.data_mut().extend_from_slice(data)
}
pub fn set_data_from_slice(&mut self, new_data: &[u8]) {
// If the buffer isn't shared, we're going to memcpy in place.
let Some(data) = Arc::get_mut(&mut self.data) else {
// If the buffer is shared, the cheapest thing to do is to clone the
// incoming slice and replace the buffer.
return self.set_data(new_data.to_vec());
};
let new_len = new_data.len();
// Reserve additional capacity if needed. Here we make the assumption
// that growing the current buffer is cheaper than doing a whole new
// allocation to make `new_data` owned.
//
// This assumption holds true during CPI, especially when the account
// size doesn't change but the account is only changed in place. And
// it's also true when the account is grown by a small margin (the
// realloc limit is quite low), in which case the allocator can just
// update the allocation metadata without moving.
//
// Shrinking and copying in place is always faster than making
// `new_data` owned, since shrinking boils down to updating the Vec's
// length.
data.reserve(new_len.saturating_sub(data.len()));
// Safety:
// We just reserved enough capacity. We set data::len to 0 to avoid
// possible UB on panic (dropping uninitialized elements), do the copy,
// finally set the new length once everything is initialized.
#[allow(clippy::uninit_vec)]
// this is a false positive, the lint doesn't currently special case set_len(0)
unsafe {
data.set_len(0);
ptr::copy_nonoverlapping(new_data.as_ptr(), data.as_mut_ptr(), new_len);
data.set_len(new_len);
};
}
#[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
fn set_data(&mut self, data: Vec<u8>) {
self.data = Arc::new(data);
}
Account data direct mapping (#28053) * AccountSharedData: make data_mut() private This ensures that the inner Vec is never handed out. This is in preparation of enforcing that the capacity of the inner vec never shrinks, which is required for direct mapping. * Adds the feature bpf_account_data_direct_mapping. * Remaps EbpfError::AccessViolation into InstructionError::ReadonlyDataModified. * WIP: Memory regions for each instruction account in create_vm(). * Fix serialization benches, run both copy and !copy variants * rbpf-cli: fix build * BorrowedAccount: ensure that account capacity is never reduced Accounts can be directly mapped in address space. Their capacity can't be reduced mid transaction as that would create holes in vm address space that point to invalid host memory. * bpf_load: run serialization tests for both copy and !copy account data * bpf_loader: add Serializer::write_account * fix lints * BorrowedAccount: make_data_mut is host only * Fix unused import warning * Fix lints * cpi: add explicit direct_mapping arg to update_(callee|caller)_account * cpi: rename account_data_or_only_realloc_padding to serialized_data * cpi: add CallerAccount::original_data_len comment * cpi: add update_callee_account direct_mapping test * cpi: add test_update_caller_account_data_direct_mapping and fix bug We used to have a bug in zeroing data when shrinking account, where we zeroed the spare account capacity but not the realloc padding. * cpi: add tests for mutated readonly accounts * cpi: update_caller_account doesn't need to change .serialized_data when direct_mapping is on * cpi: update_caller_account: ensure that account capacity is always enough Introduce a better way to ensure that account capacity never goes below what might be mapped in memory regions. * cpi: zero account capacity using the newly introduced BorrowedAccount::spare_data_capacity_mut() Before we were using BorrowedAccount::get_data_mut() to get the base pointer to the account data, then we were slicing the spare capacity from it. Calling get_data_mut() doesn't work if an account has been closed tho, since the current program doesn't own the account anymore and therefore get_data_mut() errors out. * bpf_loader: fix same lint for the umpteenth time * bpf_loader: map AccessViolation to ReadonlyDataModified only for account region violations * programs/sbf: realloc: add test for large write after realloc Add a test that after a realloc does a large write that spans the original account length and the realloc area. This ensures that memory mapping works correctly across the boundary. * programs/sbf: run test_program_sbf_realloc with both direct_mapping on and off By default test banks test with all features on. This ensures we keep testing the existing code until the new feature is enabled. * bpf_loader: tweak memcmp syscall Split the actual memcmp code in a separate function. Remove check indexing the slices since the slices are guaranteed to have the correct length by construction. * bpf_loader: tweak the memset syscall Use slice::fill, which is effectively memset. * bpf_loader: syscalls: update mem syscalls to work with non contiguous memory With direct mapping enabled, accounts can now span multiple memory regions. * fix lint, rebase mem_ops * Implement CoW for writable accounts * Fix CI * Move CoW to the MemoryMapping level * Update after rbpf API change * Fix merge screwup * Add create_vm macro. Fix benches. * cpi: simplify update_caller_account Simplify the logic to update a caller's memory region when a callee causes an account data pointer to change (eg during CoW) * benches/bpf_loader: move serialization out of create_vm bench * benches/bpf_loader: don't copy accounts when direct mapping is on * Fix review nits * bpf_loader: mem_ops: handle u64 overflow in MemoryChunkIterator::new When starting at u64::MAX, the chunk iterator would always return the empty sequence (None on the first next()) call, instead of returning a memory access violation. Use checked instead of saturating arithmetic to detect the condition and error out. This commit also adds more tests around boundary conditions. * Fix loader-v3 tests: data_mut => data_as_mut_slice * Fix CI * bpf_loader: fix tuner bench: account must be writable With direct mapping on, invalid writes are caught early meaning the tuner would fail on the first store and not consume the whole budget like the benchmark expects. --------- Co-authored-by: Alexander Meißner <AlexanderMeissner@gmx.net>
2023-04-28 13:54:39 -07:00
pub fn spare_data_capacity_mut(&mut self) -> &mut [MaybeUninit<u8>] {
self.data_mut().spare_capacity_mut()
}
2021-03-09 13:06:07 -08:00
pub fn new(lamports: u64, space: usize, owner: &Pubkey) -> Self {
shared_new(lamports, space, owner)
}
pub fn new_ref(lamports: u64, space: usize, owner: &Pubkey) -> Rc<RefCell<Self>> {
shared_new_ref(lamports, space, owner)
}
pub fn new_data<T: serde::Serialize>(
lamports: u64,
state: &T,
owner: &Pubkey,
) -> Result<Self, bincode::Error> {
shared_new_data(lamports, state, owner)
}
pub fn new_ref_data<T: serde::Serialize>(
lamports: u64,
state: &T,
owner: &Pubkey,
) -> Result<RefCell<Self>, bincode::Error> {
shared_new_ref_data(lamports, state, owner)
}
pub fn new_data_with_space<T: serde::Serialize>(
lamports: u64,
state: &T,
space: usize,
owner: &Pubkey,
) -> Result<Self, bincode::Error> {
shared_new_data_with_space(lamports, state, space, owner)
}
pub fn new_ref_data_with_space<T: serde::Serialize>(
lamports: u64,
state: &T,
space: usize,
owner: &Pubkey,
) -> Result<RefCell<Self>, bincode::Error> {
shared_new_ref_data_with_space(lamports, state, space, owner)
}
2022-02-24 17:49:33 -08:00
pub fn new_rent_epoch(lamports: u64, space: usize, owner: &Pubkey, rent_epoch: Epoch) -> Self {
shared_new_rent_epoch(lamports, space, owner, rent_epoch)
}
2021-03-09 13:06:07 -08:00
pub fn deserialize_data<T: serde::de::DeserializeOwned>(&self) -> Result<T, bincode::Error> {
shared_deserialize_data(self)
}
pub fn serialize_data<T: serde::Serialize>(&mut self, state: &T) -> Result<(), bincode::Error> {
2021-03-09 13:06:07 -08:00
shared_serialize_data(self, state)
}
}
2020-10-28 22:01:07 -07:00
pub type InheritableAccountFields = (u64, Epoch);
pub const DUMMY_INHERITABLE_ACCOUNT_FIELDS: InheritableAccountFields = (1, INITIAL_RENT_EPOCH);
2020-10-28 22:01:07 -07:00
/// Create an `Account` from a `Sysvar`.
#[deprecated(
since = "1.5.17",
note = "Please use `create_account_for_test` instead"
)]
2020-10-28 22:01:07 -07:00
pub fn create_account<S: Sysvar>(sysvar: &S, lamports: u64) -> Account {
create_account_with_fields(sysvar, (lamports, INITIAL_RENT_EPOCH))
}
pub fn create_account_with_fields<S: Sysvar>(
sysvar: &S,
(lamports, rent_epoch): InheritableAccountFields,
) -> Account {
2020-10-28 22:01:07 -07:00
let data_len = S::size_of().max(bincode::serialized_size(sysvar).unwrap() as usize);
let mut account = Account::new(lamports, data_len, &solana_program::sysvar::id());
2021-03-09 13:06:07 -08:00
to_account::<S, Account>(sysvar, &mut account).unwrap();
account.rent_epoch = rent_epoch;
2020-10-28 22:01:07 -07:00
account
}
pub fn create_account_for_test<S: Sysvar>(sysvar: &S) -> Account {
create_account_with_fields(sysvar, DUMMY_INHERITABLE_ACCOUNT_FIELDS)
}
2021-03-09 13:06:07 -08:00
/// Create an `Account` from a `Sysvar`.
#[deprecated(
since = "1.5.17",
note = "Please use `create_account_shared_data_for_test` instead"
)]
2021-03-09 13:06:07 -08:00
pub fn create_account_shared_data<S: Sysvar>(sysvar: &S, lamports: u64) -> AccountSharedData {
AccountSharedData::from(create_account_with_fields(
sysvar,
(lamports, INITIAL_RENT_EPOCH),
))
}
pub fn create_account_shared_data_with_fields<S: Sysvar>(
sysvar: &S,
fields: InheritableAccountFields,
) -> AccountSharedData {
AccountSharedData::from(create_account_with_fields(sysvar, fields))
}
pub fn create_account_shared_data_for_test<S: Sysvar>(sysvar: &S) -> AccountSharedData {
AccountSharedData::from(create_account_with_fields(
sysvar,
DUMMY_INHERITABLE_ACCOUNT_FIELDS,
))
2021-03-09 13:06:07 -08:00
}
2020-10-28 22:01:07 -07:00
/// Create a `Sysvar` from an `Account`'s data.
2021-03-09 13:06:07 -08:00
pub fn from_account<S: Sysvar, T: ReadableAccount>(account: &T) -> Option<S> {
bincode::deserialize(account.data()).ok()
2020-10-28 22:01:07 -07:00
}
/// Serialize a `Sysvar` into an `Account`'s data.
2021-03-09 13:06:07 -08:00
pub fn to_account<S: Sysvar, T: WritableAccount>(sysvar: &S, account: &mut T) -> Option<()> {
bincode::serialize_into(account.data_as_mut_slice(), sysvar).ok()
}
2020-10-28 22:01:07 -07:00
/// Return the information required to construct an `AccountInfo`. Used by the
/// `AccountInfo` conversion implementations.
impl solana_program::account_info::Account for Account {
fn get(&mut self) -> (&mut u64, &mut [u8], &Pubkey, bool, Epoch) {
(
&mut self.lamports,
&mut self.data,
&self.owner,
self.executable,
self.rent_epoch,
)
}
}
/// Create `AccountInfo`s
pub fn create_is_signer_account_infos<'a>(
accounts: &'a mut [(&'a Pubkey, bool, &'a mut Account)],
) -> Vec<AccountInfo<'a>> {
accounts
.iter_mut()
.map(|(key, is_signer, account)| {
AccountInfo::new(
key,
*is_signer,
false,
&mut account.lamports,
&mut account.data,
&account.owner,
account.executable,
account.rent_epoch,
)
})
.collect()
}
2021-03-09 13:06:07 -08:00
#[cfg(test)]
pub mod tests {
use super::*;
fn make_two_accounts(key: &Pubkey) -> (Account, AccountSharedData) {
let mut account1 = Account::new(1, 2, key);
2021-03-09 13:06:07 -08:00
account1.executable = true;
account1.rent_epoch = 4;
let mut account2 = AccountSharedData::new(1, 2, key);
account2.executable = true;
account2.rent_epoch = 4;
assert!(accounts_equal(&account1, &account2));
(account1, account2)
}
#[test]
fn test_account_data_copy_as_slice() {
let key = Pubkey::new_unique();
let key2 = Pubkey::new_unique();
let (mut account1, mut account2) = make_two_accounts(&key);
account1.copy_into_owner_from_slice(key2.as_ref());
account2.copy_into_owner_from_slice(key2.as_ref());
assert!(accounts_equal(&account1, &account2));
assert_eq!(account1.owner(), &key2);
}
#[test]
fn test_account_set_data_from_slice() {
let key = Pubkey::new_unique();
let (_, mut account) = make_two_accounts(&key);
assert_eq!(account.data(), &vec![0, 0]);
account.set_data_from_slice(&[1, 2]);
assert_eq!(account.data(), &vec![1, 2]);
account.set_data_from_slice(&[1, 2, 3]);
assert_eq!(account.data(), &vec![1, 2, 3]);
account.set_data_from_slice(&[4, 5, 6]);
assert_eq!(account.data(), &vec![4, 5, 6]);
account.set_data_from_slice(&[4, 5, 6, 0]);
assert_eq!(account.data(), &vec![4, 5, 6, 0]);
account.set_data_from_slice(&[]);
assert_eq!(account.data().len(), 0);
account.set_data_from_slice(&[44]);
assert_eq!(account.data(), &vec![44]);
account.set_data_from_slice(&[44]);
assert_eq!(account.data(), &vec![44]);
}
#[test]
fn test_account_data_set_data() {
let key = Pubkey::new_unique();
let (_, mut account) = make_two_accounts(&key);
assert_eq!(account.data(), &vec![0, 0]);
account.set_data(vec![1, 2]);
assert_eq!(account.data(), &vec![1, 2]);
account.set_data(vec![]);
assert_eq!(account.data().len(), 0);
}
2021-03-09 13:06:07 -08:00
#[test]
#[should_panic(
expected = "called `Result::unwrap()` on an `Err` value: Io(Kind(UnexpectedEof))"
)]
fn test_account_deserialize() {
let key = Pubkey::new_unique();
let (account1, _account2) = make_two_accounts(&key);
account1.deserialize_data::<String>().unwrap();
}
#[test]
#[should_panic(expected = "called `Result::unwrap()` on an `Err` value: SizeLimit")]
fn test_account_serialize() {
let key = Pubkey::new_unique();
let (mut account1, _account2) = make_two_accounts(&key);
account1.serialize_data(&"hello world").unwrap();
}
#[test]
#[should_panic(
expected = "called `Result::unwrap()` on an `Err` value: Io(Kind(UnexpectedEof))"
)]
fn test_account_shared_data_deserialize() {
let key = Pubkey::new_unique();
let (_account1, account2) = make_two_accounts(&key);
account2.deserialize_data::<String>().unwrap();
}
#[test]
#[should_panic(expected = "called `Result::unwrap()` on an `Err` value: SizeLimit")]
fn test_account_shared_data_serialize() {
let key = Pubkey::new_unique();
let (_account1, mut account2) = make_two_accounts(&key);
account2.serialize_data(&"hello world").unwrap();
}
#[test]
fn test_to_account_shared_data() {
let key = Pubkey::new_unique();
let (account1, account2) = make_two_accounts(&key);
assert!(accounts_equal(&account1, &account2));
let account3 = account1.to_account_shared_data();
let account4 = account2.to_account_shared_data();
assert!(accounts_equal(&account1, &account3));
assert!(accounts_equal(&account1, &account4));
}
2021-03-09 13:06:07 -08:00
#[test]
fn test_account_shared_data() {
let key = Pubkey::new_unique();
let (account1, account2) = make_two_accounts(&key);
assert!(accounts_equal(&account1, &account2));
let account = account1;
assert_eq!(account.lamports, 1);
assert_eq!(account.lamports(), 1);
assert_eq!(account.data.len(), 2);
assert_eq!(account.data().len(), 2);
assert_eq!(account.owner, key);
assert_eq!(account.owner(), &key);
assert!(account.executable);
assert!(account.executable());
2021-03-09 13:06:07 -08:00
assert_eq!(account.rent_epoch, 4);
assert_eq!(account.rent_epoch(), 4);
let account = account2;
assert_eq!(account.lamports, 1);
assert_eq!(account.lamports(), 1);
assert_eq!(account.data.len(), 2);
assert_eq!(account.data().len(), 2);
assert_eq!(account.owner, key);
assert_eq!(account.owner(), &key);
assert!(account.executable);
assert!(account.executable());
2021-03-09 13:06:07 -08:00
assert_eq!(account.rent_epoch, 4);
assert_eq!(account.rent_epoch(), 4);
}
// test clone and from for both types against expected
fn test_equal(
should_be_equal: bool,
account1: &Account,
account2: &AccountSharedData,
account_expected: &Account,
) {
assert_eq!(should_be_equal, accounts_equal(account1, account2));
if should_be_equal {
assert!(accounts_equal(account_expected, account2));
}
assert_eq!(
accounts_equal(account_expected, account1),
accounts_equal(account_expected, &account1.clone())
);
assert_eq!(
accounts_equal(account_expected, account2),
accounts_equal(account_expected, &account2.clone())
);
assert_eq!(
accounts_equal(account_expected, account1),
accounts_equal(account_expected, &AccountSharedData::from(account1.clone()))
);
assert_eq!(
accounts_equal(account_expected, account2),
accounts_equal(account_expected, &Account::from(account2.clone()))
);
}
#[test]
fn test_account_add_sub_lamports() {
let key = Pubkey::new_unique();
let (mut account1, mut account2) = make_two_accounts(&key);
assert!(accounts_equal(&account1, &account2));
account1.checked_add_lamports(1).unwrap();
account2.checked_add_lamports(1).unwrap();
assert!(accounts_equal(&account1, &account2));
assert_eq!(account1.lamports(), 2);
account1.checked_sub_lamports(2).unwrap();
account2.checked_sub_lamports(2).unwrap();
assert!(accounts_equal(&account1, &account2));
assert_eq!(account1.lamports(), 0);
}
#[test]
#[should_panic(expected = "Overflow")]
fn test_account_checked_add_lamports_overflow() {
let key = Pubkey::new_unique();
let (mut account1, _account2) = make_two_accounts(&key);
account1.checked_add_lamports(u64::MAX).unwrap();
}
#[test]
#[should_panic(expected = "Underflow")]
fn test_account_checked_sub_lamports_underflow() {
let key = Pubkey::new_unique();
let (mut account1, _account2) = make_two_accounts(&key);
account1.checked_sub_lamports(u64::MAX).unwrap();
}
#[test]
#[should_panic(expected = "Overflow")]
fn test_account_checked_add_lamports_overflow2() {
let key = Pubkey::new_unique();
let (_account1, mut account2) = make_two_accounts(&key);
account2.checked_add_lamports(u64::MAX).unwrap();
}
#[test]
#[should_panic(expected = "Underflow")]
fn test_account_checked_sub_lamports_underflow2() {
let key = Pubkey::new_unique();
let (_account1, mut account2) = make_two_accounts(&key);
account2.checked_sub_lamports(u64::MAX).unwrap();
}
#[test]
fn test_account_saturating_add_lamports() {
let key = Pubkey::new_unique();
let (mut account, _) = make_two_accounts(&key);
let remaining = 22;
account.set_lamports(u64::MAX - remaining);
account.saturating_add_lamports(remaining * 2);
assert_eq!(account.lamports(), u64::MAX);
}
#[test]
fn test_account_saturating_sub_lamports() {
let key = Pubkey::new_unique();
let (mut account, _) = make_two_accounts(&key);
let remaining = 33;
account.set_lamports(remaining);
account.saturating_sub_lamports(remaining * 2);
assert_eq!(account.lamports(), 0);
}
2021-03-09 13:06:07 -08:00
#[test]
#[allow(clippy::redundant_clone)]
fn test_account_shared_data_all_fields() {
let key = Pubkey::new_unique();
let key2 = Pubkey::new_unique();
let key3 = Pubkey::new_unique();
let (mut account1, mut account2) = make_two_accounts(&key);
assert!(accounts_equal(&account1, &account2));
let mut account_expected = account1.clone();
assert!(accounts_equal(&account1, &account_expected));
assert!(accounts_equal(&account1, &account2.clone())); // test the clone here
for field_index in 0..5 {
for pass in 0..4 {
if field_index == 0 {
if pass == 0 {
account1.checked_add_lamports(1).unwrap();
2021-03-09 13:06:07 -08:00
} else if pass == 1 {
account_expected.checked_add_lamports(1).unwrap();
2021-03-09 13:06:07 -08:00
account2.set_lamports(account2.lamports + 1);
} else if pass == 2 {
account1.set_lamports(account1.lamports + 1);
} else if pass == 3 {
account_expected.checked_add_lamports(1).unwrap();
account2.checked_add_lamports(1).unwrap();
2021-03-09 13:06:07 -08:00
}
} else if field_index == 1 {
if pass == 0 {
account1.data[0] += 1;
} else if pass == 1 {
account_expected.data[0] += 1;
account2.data_as_mut_slice()[0] = account2.data[0] + 1;
} else if pass == 2 {
account1.data_as_mut_slice()[0] = account1.data[0] + 1;
} else if pass == 3 {
account_expected.data[0] += 1;
account2.data_as_mut_slice()[0] += 1;
2021-03-09 13:06:07 -08:00
}
} else if field_index == 2 {
if pass == 0 {
account1.owner = key2;
} else if pass == 1 {
account_expected.owner = key2;
account2.set_owner(key2);
} else if pass == 2 {
account1.set_owner(key3);
} else if pass == 3 {
account_expected.owner = key3;
account2.owner = key3;
}
} else if field_index == 3 {
if pass == 0 {
account1.executable = !account1.executable;
} else if pass == 1 {
account_expected.executable = !account_expected.executable;
account2.set_executable(!account2.executable);
} else if pass == 2 {
account1.set_executable(!account1.executable);
} else if pass == 3 {
account_expected.executable = !account_expected.executable;
account2.executable = !account2.executable;
}
} else if field_index == 4 {
if pass == 0 {
account1.rent_epoch += 1;
} else if pass == 1 {
account_expected.rent_epoch += 1;
account2.set_rent_epoch(account2.rent_epoch + 1);
} else if pass == 2 {
account1.set_rent_epoch(account1.rent_epoch + 1);
} else if pass == 3 {
account_expected.rent_epoch += 1;
account2.rent_epoch += 1;
}
}
let should_be_equal = pass == 1 || pass == 3;
test_equal(should_be_equal, &account1, &account2, &account_expected);
// test new_ref
if should_be_equal {
assert!(accounts_equal(
&Account::new_ref(
account_expected.lamports(),
account_expected.data().len(),
account_expected.owner()
)
.borrow(),
&AccountSharedData::new_ref(
account_expected.lamports(),
account_expected.data().len(),
account_expected.owner()
)
.borrow()
));
{
// test new_data
let account1_with_data = Account::new_data(
account_expected.lamports(),
&account_expected.data()[0],
account_expected.owner(),
)
.unwrap();
let account2_with_data = AccountSharedData::new_data(
account_expected.lamports(),
&account_expected.data()[0],
account_expected.owner(),
)
.unwrap();
assert!(accounts_equal(&account1_with_data, &account2_with_data));
assert_eq!(
account1_with_data.deserialize_data::<u8>().unwrap(),
account2_with_data.deserialize_data::<u8>().unwrap()
);
}
// test new_data_with_space
assert!(accounts_equal(
&Account::new_data_with_space(
account_expected.lamports(),
&account_expected.data()[0],
1,
account_expected.owner()
)
.unwrap(),
&AccountSharedData::new_data_with_space(
account_expected.lamports(),
&account_expected.data()[0],
1,
account_expected.owner()
)
.unwrap()
));
// test new_ref_data
assert!(accounts_equal(
&Account::new_ref_data(
account_expected.lamports(),
&account_expected.data()[0],
account_expected.owner()
)
.unwrap()
.borrow(),
&AccountSharedData::new_ref_data(
account_expected.lamports(),
&account_expected.data()[0],
account_expected.owner()
)
.unwrap()
.borrow()
));
//new_ref_data_with_space
assert!(accounts_equal(
&Account::new_ref_data_with_space(
account_expected.lamports(),
&account_expected.data()[0],
1,
account_expected.owner()
)
.unwrap()
.borrow(),
&AccountSharedData::new_ref_data_with_space(
account_expected.lamports(),
&account_expected.data()[0],
1,
account_expected.owner()
)
.unwrap()
.borrow()
));
}
}
}
}
}