1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9use truthlinked_core::pq_execution::AccountId;
10use truthlinked_governance::{
11 CellVisibility, PendingNameRegistration, SchemaProposal, TokenAuthorityProposal, UrlProposal,
12};
13use truthlinked_oracle::http_oracle::{OracleCommit, OracleRequest, OracleResult, OracleReveal};
14
15#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
16pub struct AccountRecord {
17 pub pubkey_bytes: Vec<u8>,
18 pub balance: u128,
19 #[serde(default)]
20 pub compute_escrow_trth: u128,
21 #[serde(default)]
22 pub nonce: u64,
23 pub nfts: Vec<[u8; 32]>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct NFTRecord {
28 pub nft_id: [u8; 32],
29 pub owner: AccountId,
30 pub name: String,
31 pub metadata_uri: String,
32 pub minted_at: u64,
33 pub collection: Option<[u8; 32]>,
34 pub royalty_bps: u16,
35 pub royalty_recipient: Option<AccountId>,
36 pub approved: Option<AccountId>,
37}
38
39#[derive(Debug, Clone)]
40pub enum StakingUpdate {
41 Stake {
42 validator: Vec<u8>,
43 amount: u64,
44 },
45 Unstake {
46 validator: Vec<u8>,
47 amount: u64,
48 },
49 Withdraw {
50 validator: Vec<u8>,
51 },
52 Slash {
53 validator: Vec<u8>,
54 reason: truthlinked_staking::SlashReason,
55 amount: u64,
56 redistribution: Vec<(Vec<u8>, u64)>,
57 },
58 Unjail {
59 validator: Vec<u8>,
60 },
61}
62
63#[derive(Debug, Clone, PartialEq)]
64pub enum DeltaOp {
65 Add(i128),
66 Max(u128),
67 Or(bool),
68 Append(Vec<u8>),
69}
70
71impl DeltaOp {
72 pub fn compose(&self, other: &DeltaOp) -> Result<DeltaOp, String> {
73 match (self, other) {
74 (DeltaOp::Add(a), DeltaOp::Add(b)) => Ok(DeltaOp::Add(a + b)),
75 (DeltaOp::Max(a), DeltaOp::Max(b)) => Ok(DeltaOp::Max(*a.max(b))),
76 (DeltaOp::Or(a), DeltaOp::Or(b)) => Ok(DeltaOp::Or(*a || *b)),
77 (DeltaOp::Append(a), DeltaOp::Append(b)) => {
78 let mut result = a.clone();
79 result.extend_from_slice(b);
80 Ok(DeltaOp::Append(result))
81 }
82 _ => Err("Cannot compose different delta types".to_string()),
83 }
84 }
85
86 pub fn apply(&self, base: &[u8]) -> Vec<u8> {
87 match self {
88 DeltaOp::Add(delta) => {
89 let current = if base.len() >= 16 {
90 i128::from_le_bytes(base[..16].try_into().unwrap_or([0u8; 16]))
91 } else {
92 0
93 };
94 let new_val = current.saturating_add(*delta);
95 new_val.to_le_bytes().to_vec()
96 }
97 DeltaOp::Max(value) => {
98 let current = if base.len() >= 16 {
99 u128::from_le_bytes(base[..16].try_into().unwrap_or([0u8; 16]))
100 } else {
101 0
102 };
103 let new_val = current.max(*value);
104 new_val.to_le_bytes().to_vec()
105 }
106 DeltaOp::Or(value) => {
107 let current = !base.is_empty() && base[0] != 0;
108 let new_val = current || *value;
109 vec![if new_val { 1 } else { 0 }]
110 }
111 DeltaOp::Append(data) => {
112 let mut result = base.to_vec();
113 result.extend_from_slice(data);
114 result
115 }
116 }
117 }
118}
119
120#[derive(Debug, Clone, Default)]
121pub struct StorageDelta {
122 pub deltas: HashMap<[u8; 32], DeltaOp>,
123}
124
125impl StorageDelta {
126 pub fn add_delta(&mut self, key: [u8; 32], delta: i128) {
127 let op = DeltaOp::Add(delta);
128 if let Some(existing) = self.deltas.get(&key) {
129 if let Ok(composed) = existing.compose(&op) {
130 self.deltas.insert(key, composed);
131 }
132 } else {
133 self.deltas.insert(key, op);
134 }
135 }
136
137 pub fn set_max(&mut self, key: [u8; 32], value: u128) {
138 let op = DeltaOp::Max(value);
139 if let Some(existing) = self.deltas.get(&key) {
140 if let Ok(composed) = existing.compose(&op) {
141 self.deltas.insert(key, composed);
142 }
143 } else {
144 self.deltas.insert(key, op);
145 }
146 }
147
148 pub fn set_flag(&mut self, key: [u8; 32], value: bool) {
149 let op = DeltaOp::Or(value);
150 if let Some(existing) = self.deltas.get(&key) {
151 if let Ok(composed) = existing.compose(&op) {
152 self.deltas.insert(key, composed);
153 }
154 } else {
155 self.deltas.insert(key, op);
156 }
157 }
158
159 pub fn append_log(&mut self, key: [u8; 32], data: Vec<u8>) {
160 let op = DeltaOp::Append(data);
161 if let Some(existing) = self.deltas.get(&key) {
162 if let Ok(composed) = existing.compose(&op) {
163 self.deltas.insert(key, composed);
164 }
165 } else {
166 self.deltas.insert(key, op);
167 }
168 }
169
170 pub fn compose(&mut self, other: &StorageDelta) {
171 for (key, delta) in &other.deltas {
172 if let Some(existing) = self.deltas.get(key) {
173 if let Ok(composed) = existing.compose(delta) {
174 self.deltas.insert(*key, composed);
175 }
176 } else {
177 self.deltas.insert(*key, delta.clone());
178 }
179 }
180 }
181}
182
183#[derive(Debug, Clone)]
184pub enum OracleUpdate {
185 QueueRequest(OracleRequest),
186 AddCommit {
187 request_id: [u8; 32],
188 commit: OracleCommit,
189 },
190 AddReveal {
191 request_id: [u8; 32],
192 reveal: OracleReveal,
193 },
194 FinalizeResult(OracleResult),
195 AddUrlProposal(UrlProposal),
196 VoteUrlProposal {
197 url_pattern: String,
198 voter_pk: Vec<u8>,
199 stake_for_delta: u64,
200 stake_against_delta: u64,
201 },
202 SlashUrlProposer {
203 url_pattern: String,
204 },
205 AddSchemaProposal(SchemaProposal),
206 VoteSchemaProposal {
207 schema_id: [u8; 32],
208 voter_pk: Vec<u8>,
209 stake_for_delta: u64,
210 stake_against_delta: u64,
211 },
212 SetVisibility {
213 cell_id: AccountId,
214 visibility: CellVisibility,
215 },
216}
217
218#[derive(Debug, Clone)]
219pub enum CellUpdate {
220 Deploy {
221 cell_id: AccountId,
222 cell: crate::cells::CellAccount,
223 },
224 StorageChange {
225 cell_id: AccountId,
226 storage_diff: HashMap<[u8; 32], Option<[u8; 32]>>,
227 },
228 BalanceChange {
229 cell_id: AccountId,
230 new_balance: u128,
231 },
232 Remove {
233 cell_id: AccountId,
234 },
235 Upgrade {
236 cell_id: AccountId,
237 new_bytecode: Vec<u8>,
238 new_declared_reads: Vec<[u8; 32]>,
239 new_declared_writes: Vec<[u8; 32]>,
240 new_commutative_keys: Vec<[u8; 32]>,
241 new_storage_key_specs: Vec<truthlinked_core::cells::StorageKeySpec>,
242 new_oracle_schema_ids: Vec<[u8; 32]>,
243 timestamp: u64,
244 },
245 TransferOwnership {
246 cell_id: AccountId,
247 new_owner: AccountId,
248 },
249 AcceptOwnership {
250 cell_id: AccountId,
251 caller: AccountId,
252 },
253 MakeImmutable {
254 cell_id: AccountId,
255 caller: AccountId,
256 },
257}
258
259#[derive(Debug, Clone, Default)]
260pub struct StateDiff {
261 pub account_updates: HashMap<AccountId, AccountRecord>,
262 pub staking_updates: Vec<StakingUpdate>,
263 pub nft_updates: HashMap<[u8; 32], Option<NFTRecord>>,
264 pub cell_updates: Vec<CellUpdate>,
265 pub gas_fee: u128,
266 pub name_fee: u128,
267 pub cu_fee: u128,
268 pub treasury_fee: u128,
269 pub compute_fee_trth: u128,
270 pub tx_hash: [u8; 32],
271 pub tx_hashes: std::collections::HashSet<[u8; 32]>,
272 pub pending_name_proposals: Vec<(String, PendingNameRegistration, AccountId, bool)>,
273 pub name_votes: Vec<(String, Vec<u8>, u64)>,
274 pub pending_token_authority_proposals: Vec<(AccountId, TokenAuthorityProposal)>,
275 pub token_authority_votes: Vec<(AccountId, Vec<u8>, u64)>,
276 pub name_renewals: Vec<(String, u64)>,
277 pub name_transfers: Vec<(String, AccountId)>,
278 pub native_transfers: Vec<(AccountId, u128)>,
279 pub native_debits: Vec<(AccountId, u128)>,
280 pub compute_escrow_credits: Vec<(AccountId, u128)>,
281 pub compute_escrow_debits: Vec<(AccountId, u128)>,
282 pub token_credits: Vec<(AccountId, AccountId, u128)>,
283 pub token_balance_updates: Vec<((AccountId, AccountId), u128)>,
284 pub staking_rewards: Vec<(AccountId, u128)>,
285 pub cell_credits: Vec<(AccountId, u128)>,
286 pub cell_debits: Vec<(AccountId, u128)>,
287 pub storage_deltas: HashMap<AccountId, StorageDelta>,
288 pub airdrop_claims: Vec<(AccountId, u64)>,
289 pub minted_amount: u128,
290 pub frozen_account_updates: Vec<((AccountId, AccountId), bool)>,
291 pub oracle_updates: Vec<OracleUpdate>,
292 pub param_updates: Vec<([u8; 32], [u8; 32])>,
293 pub nonce_updates: Vec<(AccountId, u64)>,
294 pub gas_fee_spent: u128,
295 pub name_fee_spent: u128,
296 pub compute_fee_spent: u128,
297 pub treasury_fee_spent: u128,
298 pub fee_burned: u128,
304 pub is_system: bool,
305 pub gas_breakdown: Vec<(String, u128)>,
306}