1use crate::tn_public_address::tn_pubkey_to_address_string;
2use core::mem::size_of;
3
4pub 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;
7
8pub const TN_ACCOUNT_VERSION_V1: u8 = 0x01;
9
10pub const TN_ACCOUNT_FLAG_PROGRAM: u8 = 0x01;
11pub const TN_ACCOUNT_FLAG_PRIVILEGED: u8 = 0x02;
12pub const TN_ACCOUNT_FLAG_UNCOMPRESSABLE: u8 = 0x04;
13pub const TN_ACCOUNT_FLAG_EPHEMERAL: u8 = 0x08;
14pub const TN_ACCOUNT_FLAG_DELETED: u8 = 0x10;
15pub const TN_ACCOUNT_FLAG_NEW: u8 = 0x20;
16pub const TN_ACCOUNT_FLAG_COMPRESSED: u8 = 0x40;
17
18#[repr(C, packed)]
19#[derive(Clone)]
20pub struct TnAccountMeta {
21 pub magic: u16,
22 pub version: u8,
23 pub flags: u8,
24 pub data_sz: u32,
25 pub state_counter: u64,
26 pub owner: [u8; 32], pub balance: u64,
28 pub nonce: u64,
29}
30
31pub const TN_ACCOUNT_META_MAGIC: u16 = 0xC7A3;
32pub const TN_ACCOUNT_META_FOOTPRINT: usize = size_of::<TnAccountMeta>();
33
34#[repr(C, align(16))]
35pub struct TnTxnAccountPageTable {
36 pub magic: u64, }
38
39impl TnAccountMeta {
41 pub fn size(&self) -> usize {
42 TN_ACCOUNT_META_FOOTPRINT + self.data_sz as usize
43 }
44
45 pub fn to_wire(&self) -> Vec<u8> {
47 use core::mem::size_of;
48 let mut result = Vec::with_capacity(size_of::<TnAccountMeta>());
49
50 result.extend_from_slice(&self.magic.to_le_bytes());
53 result.push(self.version);
54 result.push(self.flags);
55 result.extend_from_slice(&self.data_sz.to_le_bytes());
56 result.extend_from_slice(&self.state_counter.to_le_bytes());
57 result.extend_from_slice(&self.owner);
58 result.extend_from_slice(&self.balance.to_le_bytes());
59 result.extend_from_slice(&self.nonce.to_le_bytes());
60
61 result
62 }
63
64 pub fn from_wire(bytes: &[u8]) -> Option<Self> {
66 use core::mem::size_of;
67
68 if bytes.len() < size_of::<TnAccountMeta>() {
69 return None;
70 }
71
72 let mut offset = 0;
73
74 let magic = u16::from_le_bytes([bytes[offset], bytes[offset + 1]]);
76 offset += 2;
77
78 let version = bytes[offset];
80 offset += 1;
81
82 let flags = bytes[offset];
84 offset += 1;
85
86 let data_sz = u32::from_le_bytes([
88 bytes[offset],
89 bytes[offset + 1],
90 bytes[offset + 2],
91 bytes[offset + 3],
92 ]);
93 offset += 4;
94
95 let state_counter = u64::from_le_bytes([
97 bytes[offset],
98 bytes[offset + 1],
99 bytes[offset + 2],
100 bytes[offset + 3],
101 bytes[offset + 4],
102 bytes[offset + 5],
103 bytes[offset + 6],
104 bytes[offset + 7],
105 ]);
106 offset += 8;
107
108 let mut owner = [0u8; 32];
110 owner.copy_from_slice(&bytes[offset..offset + 32]);
111 offset += 32;
112
113 let balance = u64::from_le_bytes([
115 bytes[offset],
116 bytes[offset + 1],
117 bytes[offset + 2],
118 bytes[offset + 3],
119 bytes[offset + 4],
120 bytes[offset + 5],
121 bytes[offset + 6],
122 bytes[offset + 7],
123 ]);
124 offset += 8;
125
126 let nonce = u64::from_le_bytes([
128 bytes[offset],
129 bytes[offset + 1],
130 bytes[offset + 2],
131 bytes[offset + 3],
132 bytes[offset + 4],
133 bytes[offset + 5],
134 bytes[offset + 6],
135 bytes[offset + 7],
136 ]);
137
138 Some(TnAccountMeta {
139 magic,
140 version,
141 flags,
142 data_sz,
143 state_counter,
144 owner,
145 balance,
146 nonce,
147 })
148 }
149}
150
151impl Default for TnAccountMeta {
152 fn default() -> Self {
153 Self {
154 magic: TN_ACCOUNT_META_MAGIC,
155 version: TN_ACCOUNT_VERSION_V1,
156 flags: 0,
157 data_sz: 0,
158 state_counter: 0,
159 owner: [0; 32],
160 balance: 0,
161 nonce: 0,
162 }
163 }
164}
165
166impl core::fmt::Debug for TnAccountMeta {
167 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
168 let magic = self.magic;
170 let version = self.version;
171 let flags = self.flags;
172 let owner = self.owner;
173 let data_sz = self.data_sz;
174 let balance = self.balance;
175 let nonce = self.nonce;
176 let state_counter = self.state_counter;
177 let owner_addr = tn_pubkey_to_address_string(&owner);
178 f.debug_struct("TnAccountMeta")
179 .field("magic", &magic)
180 .field("version", &version)
181 .field("flags", &flags)
182 .field("owner", &owner_addr)
183 .field("data_sz", &data_sz)
184 .field("balance", &balance)
185 .field("nonce", &nonce)
186 .field("state_counter", &state_counter)
187 .finish()
188 }
189}
190
191pub fn tn_account_is_active(meta: Option<&TnAccountMeta>) -> bool {
193 meta.is_some()
195}
196
197pub fn tn_account_is_deleted(meta: &TnAccountMeta) -> bool {
198 (meta.flags & TN_ACCOUNT_FLAG_DELETED) != 0
199}
200
201pub fn tn_account_exists(meta: Option<&TnAccountMeta>) -> bool {
202 tn_account_is_active(meta) && meta.map_or(false, |m| !tn_account_is_deleted(m))
203}
204
205pub fn tn_account_is_ephemeral(meta: &TnAccountMeta) -> bool {
206 (meta.flags & TN_ACCOUNT_FLAG_EPHEMERAL) != 0
207}
208
209pub fn tn_account_is_new(meta: &TnAccountMeta) -> bool {
210 (meta.flags & TN_ACCOUNT_FLAG_NEW) != 0
211}
212
213pub fn tn_account_is_program(meta: &TnAccountMeta) -> bool {
214 (meta.flags & TN_ACCOUNT_FLAG_PROGRAM) != 0
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn test_account_meta_wire_serialization_basic() {
223 let meta = TnAccountMeta::default();
225 let wire_bytes = meta.to_wire();
226
227 assert_eq!(wire_bytes.len(), TN_ACCOUNT_META_FOOTPRINT);
229
230 let _deserialized = TnAccountMeta::from_wire(&wire_bytes).unwrap();
232
233 let second_wire_bytes = _deserialized.to_wire();
235 assert_eq!(wire_bytes, second_wire_bytes);
236 }
237
238 #[test]
239 fn test_account_meta_from_wire_invalid_size() {
240 let short_bytes = vec![0u8; 10];
242 assert!(TnAccountMeta::from_wire(&short_bytes).is_none());
243
244 let empty_bytes = vec![];
246 assert!(TnAccountMeta::from_wire(&empty_bytes).is_none());
247 }
248
249 #[test]
250 fn test_account_meta_size_calculation() {
251 let meta = TnAccountMeta::default();
253 assert_eq!(meta.size(), TN_ACCOUNT_META_FOOTPRINT);
254 }
255}