use crate::tn_public_address::tn_pubkey_to_address_string;
use core::mem::size_of;
pub const TN_ACCOUNT_DATA_SZ_MAX: usize = 16 * 1024 * 1024; pub const TN_ACCOUNT_PAGE_SZ: usize = 128; pub const TN_ACCOUNT_PAGE_CNT_MAX: usize = TN_ACCOUNT_DATA_SZ_MAX / TN_ACCOUNT_PAGE_SZ;
pub const TN_ACCOUNT_VERSION_V1: u8 = 0x01;
pub const TN_ACCOUNT_FLAG_PROGRAM: u8 = 0x01;
pub const TN_ACCOUNT_FLAG_PRIVILEGED: u8 = 0x02;
pub const TN_ACCOUNT_FLAG_UNCOMPRESSABLE: u8 = 0x04;
pub const TN_ACCOUNT_FLAG_EPHEMERAL: u8 = 0x08;
pub const TN_ACCOUNT_FLAG_DELETED: u8 = 0x10;
pub const TN_ACCOUNT_FLAG_NEW: u8 = 0x20;
pub const TN_ACCOUNT_FLAG_COMPRESSED: u8 = 0x40;
#[repr(C, packed)]
#[derive(Clone)]
pub struct TnAccountMeta {
pub magic: u16,
pub version: u8,
pub flags: u8,
pub data_sz: u32,
pub state_counter: u64,
pub owner: [u8; 32], pub balance: u64,
pub nonce: u64,
}
pub const TN_ACCOUNT_META_MAGIC: u16 = 0xC7A3;
pub const TN_ACCOUNT_META_FOOTPRINT: usize = size_of::<TnAccountMeta>();
#[repr(C, align(16))]
pub struct TnTxnAccountPageTable {
pub magic: u64, }
impl TnAccountMeta {
pub fn size(&self) -> usize {
TN_ACCOUNT_META_FOOTPRINT + self.data_sz as usize
}
pub fn to_wire(&self) -> Vec<u8> {
use core::mem::size_of;
let mut result = Vec::with_capacity(size_of::<TnAccountMeta>());
result.extend_from_slice(&self.magic.to_le_bytes());
result.push(self.version);
result.push(self.flags);
result.extend_from_slice(&self.data_sz.to_le_bytes());
result.extend_from_slice(&self.state_counter.to_le_bytes());
result.extend_from_slice(&self.owner);
result.extend_from_slice(&self.balance.to_le_bytes());
result.extend_from_slice(&self.nonce.to_le_bytes());
result
}
pub fn from_wire(bytes: &[u8]) -> Option<Self> {
use core::mem::size_of;
if bytes.len() < size_of::<TnAccountMeta>() {
return None;
}
let mut offset = 0;
let magic = u16::from_le_bytes([bytes[offset], bytes[offset + 1]]);
offset += 2;
let version = bytes[offset];
offset += 1;
let flags = bytes[offset];
offset += 1;
let data_sz = u32::from_le_bytes([
bytes[offset],
bytes[offset + 1],
bytes[offset + 2],
bytes[offset + 3],
]);
offset += 4;
let state_counter = u64::from_le_bytes([
bytes[offset],
bytes[offset + 1],
bytes[offset + 2],
bytes[offset + 3],
bytes[offset + 4],
bytes[offset + 5],
bytes[offset + 6],
bytes[offset + 7],
]);
offset += 8;
let mut owner = [0u8; 32];
owner.copy_from_slice(&bytes[offset..offset + 32]);
offset += 32;
let balance = u64::from_le_bytes([
bytes[offset],
bytes[offset + 1],
bytes[offset + 2],
bytes[offset + 3],
bytes[offset + 4],
bytes[offset + 5],
bytes[offset + 6],
bytes[offset + 7],
]);
offset += 8;
let nonce = u64::from_le_bytes([
bytes[offset],
bytes[offset + 1],
bytes[offset + 2],
bytes[offset + 3],
bytes[offset + 4],
bytes[offset + 5],
bytes[offset + 6],
bytes[offset + 7],
]);
Some(TnAccountMeta {
magic,
version,
flags,
data_sz,
state_counter,
owner,
balance,
nonce,
})
}
}
impl Default for TnAccountMeta {
fn default() -> Self {
Self {
magic: TN_ACCOUNT_META_MAGIC,
version: TN_ACCOUNT_VERSION_V1,
flags: 0,
data_sz: 0,
state_counter: 0,
owner: [0; 32],
balance: 0,
nonce: 0,
}
}
}
impl core::fmt::Debug for TnAccountMeta {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let magic = self.magic;
let version = self.version;
let flags = self.flags;
let owner = self.owner;
let data_sz = self.data_sz;
let balance = self.balance;
let nonce = self.nonce;
let state_counter = self.state_counter;
let owner_addr = tn_pubkey_to_address_string(&owner);
f.debug_struct("TnAccountMeta")
.field("magic", &magic)
.field("version", &version)
.field("flags", &flags)
.field("owner", &owner_addr)
.field("data_sz", &data_sz)
.field("balance", &balance)
.field("nonce", &nonce)
.field("state_counter", &state_counter)
.finish()
}
}
pub fn tn_account_is_active(meta: Option<&TnAccountMeta>) -> bool {
meta.is_some()
}
pub fn tn_account_is_deleted(meta: &TnAccountMeta) -> bool {
(meta.flags & TN_ACCOUNT_FLAG_DELETED) != 0
}
pub fn tn_account_exists(meta: Option<&TnAccountMeta>) -> bool {
tn_account_is_active(meta) && meta.map_or(false, |m| !tn_account_is_deleted(m))
}
pub fn tn_account_is_ephemeral(meta: &TnAccountMeta) -> bool {
(meta.flags & TN_ACCOUNT_FLAG_EPHEMERAL) != 0
}
pub fn tn_account_is_new(meta: &TnAccountMeta) -> bool {
(meta.flags & TN_ACCOUNT_FLAG_NEW) != 0
}
pub fn tn_account_is_program(meta: &TnAccountMeta) -> bool {
(meta.flags & TN_ACCOUNT_FLAG_PROGRAM) != 0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_account_meta_wire_serialization_basic() {
let meta = TnAccountMeta::default();
let wire_bytes = meta.to_wire();
assert_eq!(wire_bytes.len(), TN_ACCOUNT_META_FOOTPRINT);
let _deserialized = TnAccountMeta::from_wire(&wire_bytes).unwrap();
let second_wire_bytes = _deserialized.to_wire();
assert_eq!(wire_bytes, second_wire_bytes);
}
#[test]
fn test_account_meta_from_wire_invalid_size() {
let short_bytes = vec![0u8; 10];
assert!(TnAccountMeta::from_wire(&short_bytes).is_none());
let empty_bytes = vec![];
assert!(TnAccountMeta::from_wire(&empty_bytes).is_none());
}
#[test]
fn test_account_meta_size_calculation() {
let meta = TnAccountMeta::default();
assert_eq!(meta.size(), TN_ACCOUNT_META_FOOTPRINT);
}
}