Skip to main content

thru_base/
tn_account.rs

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; // Max account data size (excluding metadata)
5pub const TN_ACCOUNT_PAGE_SZ: usize = 128; // Size of pages
6pub 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], // fd_pubkey_t assumed to be 32 bytes
27    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, // == TN_TXN_ACCOUNT_PAGE_TABLE_MAGIC
37}
38
39// Inline/static functions with bodies
40impl TnAccountMeta {
41    pub fn size(&self) -> usize {
42        TN_ACCOUNT_META_FOOTPRINT + self.data_sz as usize
43    }
44
45    /// Serialize account metadata to wire format (bytes)
46    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        // Since the struct is #[repr(C, packed)], we can serialize it directly
51        // But we need to be careful about alignment and endianness
52        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    /// Deserialize account metadata from wire format (bytes)
65    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        // Parse magic (2 bytes)
75        let magic = u16::from_le_bytes([bytes[offset], bytes[offset + 1]]);
76        offset += 2;
77
78        // Parse version (1 byte)
79        let version = bytes[offset];
80        offset += 1;
81
82        // Parse flags (1 byte)
83        let flags = bytes[offset];
84        offset += 1;
85
86        // Parse data_sz (4 bytes)
87        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        // Parse state_counter (8 bytes)
96        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        // Parse owner (32 bytes)
109        let mut owner = [0u8; 32];
110        owner.copy_from_slice(&bytes[offset..offset + 32]);
111        offset += 32;
112
113        // Parse balance (8 bytes)
114        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        // Parse nonce (8 bytes)
127        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        // Copy packed fields to local variables to avoid unaligned references
169        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
191// Standalone functions
192pub fn tn_account_is_active(meta: Option<&TnAccountMeta>) -> bool {
193    // TODO: this will need to change to be correct.
194    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        // Test basic serialization with default values
224        let meta = TnAccountMeta::default();
225        let wire_bytes = meta.to_wire();
226
227        // Verify the wire format has the expected size
228        assert_eq!(wire_bytes.len(), TN_ACCOUNT_META_FOOTPRINT);
229
230        // Deserialize from wire format and verify it succeeds
231        let _deserialized = TnAccountMeta::from_wire(&wire_bytes).unwrap();
232
233        // Test that the round-trip works correctly by re-serializing
234        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        // Test with insufficient bytes
241        let short_bytes = vec![0u8; 10];
242        assert!(TnAccountMeta::from_wire(&short_bytes).is_none());
243
244        // Test with empty bytes
245        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        // Test size calculation without accessing packed fields directly
252        let meta = TnAccountMeta::default();
253        assert_eq!(meta.size(), TN_ACCOUNT_META_FOOTPRINT);
254    }
255}