#![allow(unsafe_op_in_unsafe_fn)]
mod borrowed;
mod owned;
pub use borrowed::BorrowedAccount;
pub use owned::{AccountBuilder, OwnedAccount};
use crate::{Account, ReadableAccount, WritableAccount, patch::AccountPatchError};
use solana_clock::{Epoch, Slot};
use solana_pubkey::Pubkey;
use std::{
cell::RefCell,
ops::{Deref, DerefMut},
rc::Rc,
sync::Arc,
};
use CoWAccount::*;
pub const ALIGNMENT: usize = 8;
pub const STORAGE_UNIT: usize = size_of::<StorageUnit>();
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct StorageUnit(pub u64);
#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(from = "Account"))]
#[derive(Clone, Default)]
pub struct AccountSharedData {
pub(crate) cow: CoWAccount,
pub(crate) dirty: DirtyMarkers,
}
#[repr(C)]
#[derive(Clone, Copy, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct AccountCore {
pub(crate) lamports: u64,
pub(crate) owner: Pubkey,
pub(crate) slot: Slot,
pub(crate) mode: AccountMode,
pub(crate) flags: StateFlags,
_padding: [u8; 6],
}
impl Deref for AccountSharedData {
type Target = AccountCore;
fn deref(&self) -> &Self::Target {
match &self.cow {
Borrowed(account) => {
unsafe { account.core.as_ref() }
}
Owned(account) => &account.core,
}
}
}
impl DerefMut for AccountSharedData {
fn deref_mut(&mut self) -> &mut Self::Target {
match &mut self.cow {
Borrowed(account) => {
unsafe { account.core.as_mut() }
}
Owned(account) => &mut account.core,
}
}
}
impl PartialEq for AccountSharedData {
fn eq(&self, other: &Self) -> bool {
self.deref() == other.deref() && self.cow.data() == other.cow.data()
}
}
impl Eq for AccountSharedData {}
impl PartialEq<OwnedAccount> for AccountSharedData {
fn eq(&self, other: &OwnedAccount) -> bool {
self.deref() == &other.core && self.cow.data() == other.data.as_slice()
}
}
impl AccountSharedData {
pub fn cow(&self) -> &CoWAccount {
&self.cow
}
pub fn cow_mut(&mut self) -> &mut CoWAccount {
&mut self.cow
}
pub fn slot(&self) -> Slot {
self.slot
}
pub fn translate(&mut self) {
if self.dirty() {
return;
}
if let Borrowed(ref mut acc) = self.cow {
unsafe { acc.translate() };
}
}
pub fn owned(&self) -> OwnedAccount {
match self.cow() {
Borrowed(a) => a.into(),
Owned(a) => a.clone(),
}
}
pub fn mutable(&self) -> bool {
self.mode.mutable()
|| matches!(self.mode, AccountMode::Transient | AccountMode::Closed)
&& self.dirty.contains(DirtyMarkers::MODE)
}
pub fn mode(&self) -> AccountMode {
self.mode
}
pub fn is(&self, mode: AccountMode) -> bool {
self.mode == mode
}
pub fn flags(&self) -> &StateFlags {
&self.flags
}
pub fn markers(&self) -> &DirtyMarkers {
&self.dirty
}
pub(crate) fn mark_data_dirty(&mut self) {
self.dirty.insert(DirtyMarkers::DATA);
}
pub fn is_shared(&self) -> bool {
self.cow.is_shared()
}
pub fn dirty(&self) -> bool {
self.dirty.intersects(DirtyMarkers::all())
}
pub fn capacity(&self) -> usize {
self.cow.capacity()
}
pub fn data_clone(&self) -> Arc<Vec<u8>> {
self.cow.data_clone()
}
pub fn resize(&mut self, len: usize, val: u8) {
self.translate();
self.mark_data_dirty();
self.cow.resize(len, val);
}
pub fn extend_from_slice(&mut self, data: &[u8]) {
self.translate();
self.mark_data_dirty();
self.cow.extend_from_slice(data);
}
pub fn set_data_from_slice(&mut self, data: &[u8]) {
self.translate();
self.mark_data_dirty();
self.cow.set_data_from_slice(data);
}
pub fn set_lifecycle(
&mut self,
mode: AccountMode,
slot: Slot,
) -> Result<(), AccountPatchError> {
self.mode.validate_transition(mode, self.slot, slot)?;
self.translate();
if self.mode != mode {
self.dirty.insert(DirtyMarkers::MODE);
self.mode = mode;
}
self.dirty.insert(DirtyMarkers::SLOT);
self.slot = slot;
Ok(())
}
pub(crate) fn set_data_at(&mut self, offset: usize, data: &[u8]) {
self.translate();
self.mark_data_dirty();
let len = self.data().len();
if offset > len {
self.resize(offset, 0);
}
let n = self.data().len().saturating_sub(offset).min(data.len());
self.data_as_mut_slice()[offset..offset + n].copy_from_slice(&data[..n]);
self.extend_from_slice(&data[n..]);
}
pub fn set_flags(&mut self, flags: StateFlags) {
if self.flags == flags {
return;
}
self.translate();
self.dirty.set(DirtyMarkers::FLAGS, true);
self.flags = flags;
}
pub fn new(lamports: u64, space: usize, owner: &Pubkey) -> Self {
AccountBuilder::default()
.lamports(lamports)
.data(vec![0; space])
.owner(*owner)
.build()
}
pub fn new_ref(lamports: u64, space: usize, owner: &Pubkey) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self::new(lamports, space, owner)))
}
#[cfg(feature = "bincode")]
pub fn new_data<T: serde::Serialize>(
lamports: u64,
state: &T,
owner: &Pubkey,
) -> Result<Self, bincode::Error> {
let data = bincode::serialize(state)?;
Ok(Self::create_from_existing_shared_data(
lamports,
Arc::new(data),
*owner,
false,
Epoch::default(),
))
}
#[cfg(feature = "bincode")]
pub fn new_ref_data<T: serde::Serialize>(
lamports: u64,
state: &T,
owner: &Pubkey,
) -> Result<RefCell<Self>, bincode::Error> {
Self::new_data(lamports, state, owner).map(RefCell::new)
}
#[cfg(feature = "bincode")]
pub fn new_data_with_space<T: serde::Serialize>(
lamports: u64,
state: &T,
space: usize,
owner: &Pubkey,
) -> Result<Self, bincode::Error> {
let mut account = Self::new(lamports, space, owner);
crate::codec::serialize_data(&mut account, state)?;
Ok(account)
}
#[cfg(feature = "bincode")]
pub fn new_ref_data_with_space<T: serde::Serialize>(
lamports: u64,
state: &T,
space: usize,
owner: &Pubkey,
) -> Result<RefCell<Self>, bincode::Error> {
Self::new_data_with_space(lamports, state, space, owner).map(RefCell::new)
}
pub fn new_rent_epoch(lamports: u64, space: usize, owner: &Pubkey, _: Epoch) -> Self {
Self::new(lamports, space, owner)
}
#[cfg(feature = "bincode")]
pub fn deserialize_data<T: serde::de::DeserializeOwned>(&self) -> Result<T, bincode::Error> {
crate::codec::deserialize_data(self)
}
#[cfg(feature = "bincode")]
pub fn serialize_data<T: serde::Serialize>(&mut self, state: &T) -> Result<(), bincode::Error> {
crate::codec::serialize_data(self, state)
}
pub fn create_from_existing_shared_data(
lamports: u64,
data: Arc<Vec<u8>>,
owner: Pubkey,
executable: bool,
_: Epoch,
) -> Self {
AccountBuilder::default()
.lamports(lamports)
.data(data)
.owner(owner)
.executable(executable)
.build()
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct StateFlags: u8 {
const EXECUTABLE = 1 << 0;
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct DirtyMarkers: u8 {
const OWNER = 1 << 0;
const LAMPORTS = 1 << 1;
const MODE = 1 << 2;
const FLAGS = 1 << 3;
const SLOT = 1 << 4;
const DATA = 1 << 5;
}
}
#[cfg(feature = "wincode")]
const _: () = {
use core::mem::MaybeUninit;
use wincode::{
ReadError, ReadResult, SchemaRead, SchemaWrite, TypeMeta, WriteError, WriteResult,
config::ConfigCore,
io::{Reader, Writer},
};
unsafe impl<C: ConfigCore> SchemaWrite<C> for StateFlags {
type Src = StateFlags;
const TYPE_META: TypeMeta = TypeMeta::Static { size: 1, zero_copy: false };
fn size_of(_: &Self::Src) -> WriteResult<usize> {
Ok(1)
}
fn write(mut writer: impl Writer, src: &Self::Src) -> WriteResult<()> {
let bytes = bincode::serialize(src).map_err(|_| WriteError::Custom("StateFlags"))?;
writer.write(&bytes)?;
Ok(())
}
}
unsafe impl<'de, C: ConfigCore> SchemaRead<'de, C> for StateFlags {
type Dst = StateFlags;
const TYPE_META: TypeMeta = TypeMeta::Static { size: 1, zero_copy: false };
fn read(mut reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
let bytes = reader.take_array::<1>()?;
dst.write(bincode::deserialize(&bytes).map_err(|_| ReadError::Custom("StateFlags"))?);
Ok(())
}
}
};
#[derive(PartialEq, Eq)]
pub enum CoWAccount {
Borrowed(BorrowedAccount),
Owned(OwnedAccount),
}
impl Clone for CoWAccount {
fn clone(&self) -> Self {
match self {
Borrowed(acc) => Self::Owned(acc.into()),
Owned(acc) => Self::Owned(acc.clone()),
}
}
}
impl CoWAccount {
pub(crate) fn promote(&mut self) {
let Self::Borrowed(account) = self else {
return;
};
*self = Self::Owned(account.deref().into());
}
pub(crate) fn data(&self) -> &[u8] {
match self {
Self::Borrowed(account) => &account.data,
Self::Owned(account) => &account.data,
}
}
pub(crate) fn is_shared(&self) -> bool {
match self {
Self::Borrowed(_) => false,
Self::Owned(account) => Arc::strong_count(&account.data) > 1,
}
}
pub(crate) fn capacity(&self) -> usize {
match self {
Self::Borrowed(account) => account.data.capacity(),
Self::Owned(account) => account.data.capacity(),
}
}
pub(crate) fn data_clone(&self) -> Arc<Vec<u8>> {
match self {
Self::Borrowed(account) => Arc::new(account.data.to_vec()),
Self::Owned(account) => Arc::clone(&account.data),
}
}
pub(crate) fn data_mut(&mut self) -> &mut [u8] {
match self {
Self::Borrowed(account) => &mut account.data,
Self::Owned(account) => Arc::<Vec<u8>>::make_mut(&mut account.data).as_mut_slice(),
}
}
pub fn reserve(&mut self, additional: usize) {
if let Self::Borrowed(a) = self
&& a.data.spare() >= additional
{
return;
}
self.promote();
if let Self::Owned(account) = self {
Arc::make_mut(&mut account.data).reserve(additional);
}
}
pub(crate) fn resize(&mut self, len: usize, val: u8) {
if let Self::Borrowed(a) = self
&& len <= a.data.capacity()
{
unsafe { a.data.resize(len, val) };
return;
}
self.promote();
if let Self::Owned(account) = self {
Arc::make_mut(&mut account.data).resize(len, val);
}
}
pub(crate) fn extend_from_slice(&mut self, data: &[u8]) {
self.reserve(data.len());
match self {
Self::Borrowed(account) => {
unsafe { account.data.extend(data) };
}
Self::Owned(account) => Arc::make_mut(&mut account.data).extend_from_slice(data),
}
}
pub(crate) fn set_data_from_slice(&mut self, data: &[u8]) {
let additional = data.len().saturating_sub(self.data().len());
self.reserve(additional);
match self {
Self::Borrowed(account) => {
unsafe { account.data.set(data) };
}
Self::Owned(account) => {
let data_buf = Arc::make_mut(&mut account.data);
data_buf.clear();
data_buf.extend_from_slice(data);
}
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
#[repr(u8)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
pub enum AccountMode {
#[default]
Placeholder = 0,
ReadOnly,
System,
Delegated,
Ephemeral,
Transient,
Closed = 255,
}
impl AccountMode {
pub fn allows_transition(self, to: Self, from_slot: Slot, to_slot: Slot) -> bool {
self.validate_transition(to, from_slot, to_slot).is_ok()
}
fn validate_transition(
self,
to: Self,
from_slot: Slot,
to_slot: Slot,
) -> Result<(), AccountPatchError> {
use AccountMode::*;
let valid_slot = match (self, to) {
(Placeholder, ReadOnly | System | Delegated | Ephemeral | Closed)
| (ReadOnly, Delegated | Ephemeral | Closed)
| (Delegated, Transient)
| (Transient, ReadOnly | Placeholder)
| (Ephemeral, Closed) => to_slot >= from_slot,
(Placeholder, Placeholder)
| (ReadOnly, ReadOnly | Placeholder)
| (System, System)
| (Transient, Delegated) => to_slot > from_slot,
_ => return Err(AccountPatchError::InvalidModeTransition { from: self, to }),
};
if !valid_slot {
return Err(AccountPatchError::InvalidSlotTransition { from: from_slot, to: to_slot });
}
Ok(())
}
pub fn mutable(&self) -> bool {
use AccountMode::*;
matches!(self, Delegated | Ephemeral)
}
pub fn authoritative(&self) -> bool {
use AccountMode::*;
matches!(self, Delegated | Ephemeral | Transient)
}
}
pub struct AccountSeqLock {
account: AccountSharedData,
sequence: Option<u32>,
}
impl AccountSeqLock {
pub fn new(account: AccountSharedData) -> Self {
let mut sequence = None;
if let Borrowed(ref acc) = account.cow {
sequence.replace(acc.version);
}
Self { account, sequence }
}
pub fn read<F, R>(&mut self, reader: F) -> R
where
F: Fn(&AccountSharedData) -> R,
{
loop {
let pre = self.sequence.unwrap_or_default();
let result = reader(&self.account);
match self.account.cow_mut() {
Borrowed(acc) => {
let post = acc.sequence();
if pre == post {
return result;
}
unsafe { acc.reset() };
self.sequence = Some(acc.version);
}
Owned(_) => return result,
}
}
}
}
impl Default for CoWAccount {
fn default() -> Self {
Self::Owned(OwnedAccount::default())
}
}
impl From<OwnedAccount> for AccountSharedData {
fn from(value: OwnedAccount) -> Self {
Self {
cow: Owned(value),
dirty: DirtyMarkers::default(),
}
}
}
impl From<BorrowedAccount> for AccountSharedData {
fn from(value: BorrowedAccount) -> Self {
Self {
cow: Borrowed(value),
dirty: DirtyMarkers::default(),
}
}
}
impl From<Account> for AccountSharedData {
fn from(value: Account) -> Self {
AccountBuilder::default()
.lamports(value.lamports)
.data(value.data)
.owner(value.owner)
.executable(value.executable)
.build()
}
}
unsafe impl Sync for AccountSharedData {}