Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.rs linguist-language=Rust
Cargo.toml linguist-language=Rust
Cargo.lock linguist-language=Rust
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
Cargo.lock
AGENTS.md
CLAUDE.md
39 changes: 39 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[workspace]
members = ["solana/account"]
resolver = "3"

[workspace.package]
Expand All @@ -10,6 +11,44 @@ repository = "https://github.com/magicblock-labs/engine"
rust-version = "1.94.1"
version = "0.1.0"

[workspace.dependencies]
magic-root-interface = { path = "programs/magic-root-interface" }
magic-root-program = { path = "programs/magic-root-program" }
solana-account = { path = "solana/account" }

ahash = "0.8.12"
arc-swap = "1.9.1"
bincode = "1.3.3"
bitflags = "2.11.1"
blake3 = "1.8.5"
qualifier_attr = "0.2.2"
rand = "0.8.5"
rustix = { version = "1.1.4" }
serde = "1.0.228"
serde_bytes = "0.11.19"
snedfile = "0.1"
tar = "0.4.45"
tempfile = "3"
thiserror = "2.0.17"
tracing-subscriber = { version = "0.3.23", features = ["env-filter", "fmt"] }
wincode = "0.5.1"
zstd = { version = "0.13.3", default-features = false }

agave-feature-set = { version = "3.1.14", features = ["agave-unstable-api"] }
agave-transaction-view = { version = "3.1.13", features = ["agave-unstable-api"] }
solana-account-info = "3.1.1"
solana-clock = "3.1.0"
solana-compute-budget-instruction = "4.1.1"
solana-cpi = "3.1.0"
solana-hash = "4.3.0"
solana-instruction-error = "2.3.0"
solana-program-error = "3.0.1"
solana-pubkey = "4.2.0"
solana-sdk-ids = "3.1.0"
solana-sysvar = "4.0.0"
solana-transaction-error = "3.2.0"


[workspace.lints.rust]
missing_docs = "deny"
rust_2018_idioms = { level = "warn", priority = -1 }
Expand Down
37 changes: 37 additions & 0 deletions solana/account/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[package]
name = "solana-account"

authors = { workspace = true }
description = "Solana Account type"
edition = { workspace = true }
homepage = { workspace = true }
license = { workspace = true }
readme = "README.md"
repository = { workspace = true }
version = "4.3.1"

[features]
bincode = ["dep:bincode", "dep:solana-sysvar", "serde"]
serde = ["dep:serde", "dep:serde_bytes", "serde/rc", "solana-pubkey/serde"]
testkit = ["bincode"]
wincode = ["bincode", "dep:wincode", "solana-pubkey/wincode"]

[dependencies]
bincode = { workspace = true, optional = true }
bitflags = { workspace = true, features = ["serde"] }
serde = { workspace = true, optional = true }
serde_bytes = { workspace = true, optional = true }
solana-account-info = { workspace = true }
solana-clock = { workspace = true }
solana-instruction-error = { workspace = true }
solana-pubkey = { workspace = true }
solana-sdk-ids = { workspace = true }
solana-sysvar = { workspace = true, features = ["bincode"], optional = true }
wincode = { workspace = true, features = ["alloc"], optional = true }

[dev-dependencies]
solana-account = { path = ".", features = ["testkit"] }
solana-pubkey = { workspace = true, features = ["std"] }

[lints]
workspace = true
29 changes: 29 additions & 0 deletions solana/account/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# `solana-account`

This fork defines the engine's account representation. `Account` is the
fully-owned compatibility form. `AccountSharedData` uses either a heap-owned
`Arc<Vec<u8>>` or a borrowed view into aligned external storage and records
field-level dirty markers.

Equality compares core state and data bytes, ignoring storage form and dirty
markers.

Higher layers decide whether an account belongs in persisted or volatile storage
through `AccountSharedData::mutable()`.

## Borrowed layout

| Part | Position | Contents |
| --- | --- | --- |
| header | start | sequence and image size |
| pubkey | after header | shared account pubkey |
| image A | after pubkey | core state and data |
| image B | after image A | core state and data |

Borrowed buffers must be 8-byte aligned, match this layout, remain live, and have
unique mutable access for the duration of the borrow. The source may be an mmap,
arena, or test buffer.

The sequence counter selects the active image. Mutation translates active state
into the shadow image; commit advances the sequence to publish it. Writes that
exceed borrowed capacity promote the account to owned storage.
260 changes: 260 additions & 0 deletions solana/account/src/account.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
use {
crate::{AccountSharedData, ReadableAccount, traits::debug_fmt},
solana_account_info::AccountInfo,
solana_clock::Epoch,
solana_pubkey::Pubkey,
solana_sdk_ids::{
bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, loader_v4, native_loader,
},
std::{cell::RefCell, fmt, rc::Rc},
};

/// An on-chain account with owned data and an explicit rent epoch.
#[repr(C)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize), serde(rename_all = "camelCase"))]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
#[derive(PartialEq, Eq, Clone, Default)]
pub struct Account {
/// Lamports in the account.
pub lamports: u64,
/// Data held in the account.
#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
pub data: Vec<u8>,
/// The program that owns this account.
pub owner: Pubkey,
/// Whether the account contains executable program data.
pub executable: bool,
/// The epoch at which this account next owes rent.
pub rent_epoch: Epoch,
}

#[cfg(feature = "serde")]
mod account_serialize {
use {
crate::ReadableAccount,
serde::{Serialize, ser::Serializer},
solana_clock::Epoch,
solana_pubkey::Pubkey,
};

#[repr(C)]
#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
/// Serialization shape shared by `Account` and `AccountSharedData`.
struct Account<'a> {
lamports: u64,
#[serde(with = "serde_bytes")]
data: &'a [u8],
owner: &'a Pubkey,
executable: bool,
rent_epoch: Epoch,
}

/// Serializes any readable account using the canonical `Account` layout.
pub(crate) fn serialize_account<S>(
account: &impl ReadableAccount,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let account = Account {
lamports: account.lamports(),
data: account.data(),
owner: account.owner(),
executable: account.executable(),
rent_epoch: account.rent_epoch(),
};
account.serialize(serializer)
}
}

#[cfg(feature = "serde")]
impl serde::ser::Serialize for Account {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
account_serialize::serialize_account(self, serializer)
}
}

#[cfg(feature = "serde")]
impl serde::ser::Serialize for AccountSharedData {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
account_serialize::serialize_account(self, serializer)
}
}

impl From<AccountSharedData> for Account {
fn from(other: AccountSharedData) -> Self {
Self {
lamports: other.lamports(),
data: other.data().to_vec(),
owner: *other.owner(),
executable: other.executable(),
rent_epoch: other.rent_epoch(),
}
}
}

impl fmt::Debug for Account {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
debug_fmt(self, f, |_| {})
}
}

impl fmt::Debug for AccountSharedData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
debug_fmt(self, f, |f| {
f.field("slot", &self.slot())
.field("mode", &self.mode)
.field("flags", self.flags())
.field("dirty", self.markers());
})
}
}

impl Account {
/// Builds an account from its exact field set.
///
/// Used by the constructors to keep the owned layout in one place.
fn from_parts(
lamports: u64,
data: Vec<u8>,
owner: Pubkey,
executable: bool,
rent_epoch: Epoch,
) -> Self {
Self {
lamports,
data,
owner,
executable,
rent_epoch,
}
}

/// Creates a new account with zero-filled data.
pub fn new(lamports: u64, space: usize, owner: &Pubkey) -> Self {
Self::new_rent_epoch(lamports, space, owner, Epoch::default())
}

/// Creates a new account wrapped in a `RefCell`.
pub fn new_ref(lamports: u64, space: usize, owner: &Pubkey) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self::new(lamports, space, owner)))
}

/// Creates a new account whose data is the serialized state.
#[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::from_parts(
lamports,
data,
*owner,
false,
Epoch::default(),
))
}

/// Creates a new serialized account wrapped in a `RefCell`.
#[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)
}

/// Creates a new account with fixed space and serialized state.
#[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)
}

/// Creates a new fixed-size serialized account wrapped in a `RefCell`.
#[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)
}

/// Creates a new account with an explicit rent epoch.
pub fn new_rent_epoch(lamports: u64, space: usize, owner: &Pubkey, rent_epoch: Epoch) -> Self {
Self::from_parts(lamports, vec![0; space], *owner, false, rent_epoch)
}

/// Deserializes the account data as `T`.
#[cfg(feature = "bincode")]
pub fn deserialize_data<T: serde::de::DeserializeOwned>(&self) -> Result<T, bincode::Error> {
crate::codec::deserialize_data(self)
}

/// Serializes `state` into the existing account data buffer.
#[cfg(feature = "bincode")]
pub fn serialize_data<T: serde::Serialize>(&mut self, state: &T) -> Result<(), bincode::Error> {
crate::codec::serialize_data(self, state)
}
}

impl solana_account_info::Account for Account {
fn get(&mut self) -> (&mut u64, &mut [u8], &Pubkey, bool) {
(
&mut self.lamports,
&mut self.data,
&self.owner,
self.executable,
)
}
}

/// Builds `AccountInfo` values for accounts and signer bits.
///
/// The returned infos borrow the provided accounts directly.
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,
)
})
.collect()
}

/// Owners that imply the account contains a loaded program.
pub const PROGRAM_OWNERS: &[Pubkey] = &[
native_loader::id(),
bpf_loader_upgradeable::id(),
bpf_loader::id(),
bpf_loader_deprecated::id(),
loader_v4::id(),
];
Loading