Skip to main content

truthlinked_core/
pq_execution.rs

1//! Transaction and execution intent types for TruthLinked.
2//!
3//! This module owns the canonical transaction wire shape. Any change to these
4//! structures can affect transaction hashes, signing payloads, storage indexing,
5//! and client compatibility.
6
7use crate::cells::StorageKeySpec;
8use serde::{Deserialize, Serialize};
9
10pub type AccountId = [u8; 32];
11
12pub fn system_authority_id() -> AccountId {
13    use sha2::{Digest, Sha256};
14    let mut hasher = Sha256::new();
15    hasher.update(b"truthlinked-system-authority-v1");
16    let hash = hasher.finalize();
17    let mut account_id = [0u8; 32];
18    account_id.copy_from_slice(&hash);
19    account_id
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct BatchTransferEntry {
24    pub recipient: AccountId,
25    /// Omit for accounts already known on-chain (saves 1,952 bytes per recipient).
26    pub recipient_pubkey: Option<Vec<u8>>,
27    pub amount: u128,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct NameTransferEntry {
32    pub name: String,
33    pub amount: u128,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct CellCall {
38    pub cell_id: AccountId,
39    pub calldata: Vec<u8>,
40    pub value: u128,
41    pub use_result_from: Option<usize>,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct Transaction {
46    pub sender: AccountId,
47    pub intent: TransactionIntent,
48    pub signature: Vec<u8>,
49    #[serde(default)]
50    pub nonce: u64,
51    pub timestamp: u64,
52    pub genesis_fingerprint: [u8; 32],
53    pub expiration_height: u64,
54}
55
56impl Transaction {
57    /// Serialized payload size used for byte-weighted fees and mempool limits.
58    pub fn byte_weight(&self) -> Result<usize, postcard::Error> {
59        postcard::to_allocvec(self).map(|bytes| bytes.len())
60    }
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64#[allow(clippy::large_enum_variant)]
65pub enum TransactionIntent {
66    Transfer {
67        recipient: AccountId,
68        /// Omit for accounts already known on-chain (saves 1,952 bytes).
69        recipient_pubkey: Option<Vec<u8>>,
70        amount: u128,
71    },
72    TransferToName {
73        name: String,
74        amount: u128,
75    },
76    BatchTransfer {
77        transfers: Vec<BatchTransferEntry>,
78    },
79    BatchTransferToName {
80        transfers: Vec<NameTransferEntry>,
81    },
82    Claim {
83        recipient: AccountId,
84        /// Omit for accounts already known on-chain (saves 1,952 bytes).
85        recipient_pubkey: Option<Vec<u8>>,
86        amount: u128,
87    },
88    RotateKey {
89        new_pubkey: Vec<u8>,
90    },
91    DepositCompute {
92        amount: u128,
93    },
94    WithdrawCompute {
95        amount: u128,
96    },
97    /// Wrap TRTH into wTRTH (1:1). Deducts TRTH, mints wTRTH token balance.
98    WrapTRTH {
99        amount: u128,
100    },
101    /// Unwrap wTRTH back to TRTH (1:1). Burns wTRTH token balance, credits TRTH.
102    UnwrapTRTH {
103        amount: u128,
104    },
105    Stake {
106        amount: u128,
107    },
108    Unstake {
109        amount: u128,
110    },
111    WithdrawStake,
112    Unjail,
113    MintNFT {
114        nft_id: [u8; 32],
115        name: String,
116        metadata_uri: String,
117        collection: Option<[u8; 32]>,
118        royalty_bps: u16,
119        royalty_recipient: Option<AccountId>,
120    },
121    TransferNFT {
122        nft_id: [u8; 32],
123        recipient: AccountId,
124        /// Omit for accounts already known on-chain (saves 1,952 bytes).
125        recipient_pubkey: Option<Vec<u8>>,
126        sale_price: Option<u128>,
127    },
128    BurnNFT {
129        nft_id: [u8; 32],
130    },
131    ApproveNFT {
132        nft_id: [u8; 32],
133        approved: Option<AccountId>,
134    },
135    DeployCell {
136        cell_id: AccountId,
137        bytecode: Vec<u8>,
138        initial_balance: u128,
139        declared_reads: Vec<[u8; 32]>,
140        declared_writes: Vec<[u8; 32]>,
141        commutative_keys: Vec<[u8; 32]>,
142        storage_key_specs: Vec<StorageKeySpec>,
143        oracle_schema_ids: Vec<[u8; 32]>,
144    },
145    DeployToken {
146        cell_id: AccountId,
147        name: String,
148        symbol: String,
149        decimals: u8,
150        total_supply: u128,
151        transfer_fee_bps: u16,
152        transfer_fee_recipient: Option<AccountId>,
153        non_transferable: bool,
154    },
155    CallCell {
156        cell_id: AccountId,
157        calldata: Vec<u8>,
158        value: u128,
159        gas_limit: u64,
160    },
161    CallCellChain {
162        calls: Vec<CellCall>,
163        gas_limit: u64,
164    },
165    UpgradeCell {
166        cell_id: AccountId,
167        new_bytecode: Vec<u8>,
168        new_declared_reads: Vec<[u8; 32]>,
169        new_declared_writes: Vec<[u8; 32]>,
170        new_commutative_keys: Vec<[u8; 32]>,
171        new_storage_key_specs: Vec<StorageKeySpec>,
172        new_oracle_schema_ids: Vec<[u8; 32]>,
173    },
174    TransferOwnership {
175        cell_id: AccountId,
176        new_owner: AccountId,
177    },
178    AcceptOwnership {
179        cell_id: AccountId,
180    },
181    MakeImmutable {
182        cell_id: AccountId,
183    },
184    CloseCell {
185        cell_id: AccountId,
186    },
187    ProposeCellUpgrade {
188        cell_id: AccountId,
189        new_bytecode: Vec<u8>,
190        new_declared_reads: Vec<[u8; 32]>,
191        new_declared_writes: Vec<[u8; 32]>,
192        new_commutative_keys: Vec<[u8; 32]>,
193        new_storage_key_specs: Vec<StorageKeySpec>,
194        new_oracle_schema_ids: Vec<[u8; 32]>,
195        timelock_blocks: u64,
196    },
197    ProposeCellOwnershipTransfer {
198        cell_id: AccountId,
199        new_owner: AccountId,
200        timelock_blocks: u64,
201    },
202    ProposeCellMakeImmutable {
203        cell_id: AccountId,
204        timelock_blocks: u64,
205    },
206    VoteCellProposal {
207        cell_id: AccountId,
208        approve: bool,
209    },
210    ExecuteCellProposal {
211        cell_id: AccountId,
212    },
213    TokenTransfer {
214        token_cell: AccountId,
215        recipient: AccountId,
216        amount: u128,
217    },
218    TokenMint {
219        token_cell: AccountId,
220        recipient: AccountId,
221        amount: u128,
222    },
223    TokenBurn {
224        token_cell: AccountId,
225        amount: u128,
226    },
227    TokenFreeze {
228        token_cell: AccountId,
229        account: AccountId,
230    },
231    TokenThaw {
232        token_cell: AccountId,
233        account: AccountId,
234    },
235    ProposeTokenAuthority {
236        token_cell: AccountId,
237        set_mint_authority: bool,
238        new_mint_authority: AccountId,
239        set_freeze_authority: bool,
240        new_freeze_authority: AccountId,
241        voting_period_blocks: u64,
242    },
243    VoteTokenAuthority {
244        token_cell: AccountId,
245        approve: bool,
246    },
247    CallSystem {
248        controller: AccountId,
249        calldata: Vec<u8>,
250    },
251    ProposeUrl {
252        url_pattern: String,
253        bond_amount: u128,
254        voting_period_blocks: u64,
255    },
256    VoteUrl {
257        url_pattern: String,
258        approve: bool,
259    },
260    ReportMaliciousUrl {
261        url_pattern: String,
262        evidence: String,
263    },
264    SetCellVisibility {
265        cell_id: AccountId,
266        visibility: u8,
267    },
268    // MCP intents
269    RegisterMcpTool {
270        tool_id: AccountId,
271        bytecode: Vec<u8>,
272        name: String,
273        input_schema_json: Vec<u8>,
274        category: u8,
275        declared_reads: Vec<[u8; 32]>,
276        declared_writes: Vec<[u8; 32]>,
277        commutative_keys: Vec<[u8; 32]>,
278        oracle_schema_ids: Vec<[u8; 32]>,
279        registry_id: AccountId,
280    },
281    RegisterMcpResource {
282        resource_id: AccountId,
283        bytecode: Vec<u8>,
284        name: String,
285        uri_scheme: String,
286        mime_type: String,
287        initial_data: Vec<(Vec<u8>, Vec<u8>)>,
288        declared_reads: Vec<[u8; 32]>,
289        declared_writes: Vec<[u8; 32]>,
290        oracle_schema_ids: Vec<[u8; 32]>,
291        registry_id: AccountId,
292    },
293    RegisterMcpPrompt {
294        prompt_id: AccountId,
295        name: String,
296        template_bytes: Vec<u8>,
297        arguments: Vec<(String, String, bool)>,
298        registry_id: AccountId,
299    },
300    RegisterAgent {
301        agent_id: AccountId,
302        policy_cell_id: AccountId,
303        agent_registry_id: AccountId,
304    },
305    SuspendAgent {
306        agent_id: AccountId,
307        agent_registry_id: AccountId,
308        reason: String,
309    },
310    ReinstateAgent {
311        agent_id: AccountId,
312        agent_registry_id: AccountId,
313    },
314    McpToolCall {
315        agent_id: AccountId,
316        tool_id: AccountId,
317        tool_calldata: Vec<u8>,
318        value: u128,
319        gas_limit: u64,
320        policy_cell_id: AccountId,
321        action_log_id: Option<AccountId>,
322        timestamp: u64,
323    },
324    PrivateBalanceInit {
325        cell_id: AccountId,
326        agent_id: AccountId,
327        encrypted_balance: Vec<u8>,
328        commitment: [u8; 32],
329        commit_nonce: [u8; 16],
330    },
331    PrivateBalanceDeposit {
332        cell_id: AccountId,
333        agent_id: AccountId,
334        amount: u128,
335        new_encrypted_balance: Vec<u8>,
336        new_commitment: [u8; 32],
337        new_commit_nonce: [u8; 16],
338        old_commitment: [u8; 32],
339    },
340    PrivateBalanceWithdraw {
341        cell_id: AccountId,
342        agent_id: AccountId,
343        amount: u128,
344        recipient: AccountId,
345        new_encrypted_balance: Vec<u8>,
346        new_commitment: [u8; 32],
347        new_commit_nonce: [u8; 16],
348        old_commitment: [u8; 32],
349    },
350    PrivateBalanceConfidentialTransfer {
351        sender_cell_id: AccountId,
352        sender_agent_id: AccountId,
353        recipient_cell_id: AccountId,
354        amount_commitment: [u8; 32],
355        stark_proof: Vec<u8>,
356        sender_new_encrypted: Vec<u8>,
357        sender_new_commitment: [u8; 32],
358        sender_new_commit_nonce: [u8; 16],
359        sender_old_commitment: [u8; 32],
360        recipient_new_encrypted: Vec<u8>,
361        recipient_new_commitment: [u8; 32],
362        recipient_new_commit_nonce: [u8; 16],
363        recipient_old_commitment: [u8; 32],
364    },
365    // Oracle commit/reveal
366    SubmitOracleCommit {
367        request_id: [u8; 32],
368        commit_hash: [u8; 32],
369    },
370    SubmitOracleReveal {
371        request_id: [u8; 32],
372        response_body: Vec<u8>,
373        response_status: u16,
374    },
375}
376
377pub fn system_cell_id(label: &str) -> AccountId {
378    use sha2::{Digest, Sha256};
379    let mut hasher = Sha256::new();
380    hasher.update(label.as_bytes());
381    hasher.finalize().into()
382}
383
384pub fn staking_system_cell_id() -> AccountId {
385    system_cell_id("truthlinked-staking-v1")
386}
387
388pub fn treasury_system_cell_id() -> AccountId {
389    system_cell_id("truthlinked-treasury-v1")
390}
391
392pub fn governance_system_cell_id() -> AccountId {
393    system_cell_id("truthlinked-governance-v1")
394}
395
396pub fn name_registry_system_cell_id() -> AccountId {
397    system_cell_id("truthlinked-name-registry-v1")
398}
399
400pub fn token_governance_system_cell_id() -> AccountId {
401    system_cell_id("truthlinked-token-governance-v1")
402}
403
404pub fn oracle_governance_system_cell_id() -> AccountId {
405    system_cell_id("truthlinked-oracle-governance-v1")
406}
407
408pub fn usdc_system_cell_id() -> AccountId {
409    system_cell_id("truthlinked-usdc-controller-v1")
410}
411
412pub fn wtrth_system_cell_id() -> AccountId {
413    system_cell_id("truthlinked-wtrth-v1")
414}
415
416pub fn usdt_system_cell_id() -> AccountId {
417    system_cell_id("truthlinked-usdt-controller-v1")
418}
419
420impl Transaction {
421    pub fn current_timestamp() -> u64 {
422        std::time::SystemTime::now()
423            .duration_since(std::time::UNIX_EPOCH)
424            .unwrap()
425            .as_secs()
426    }
427}
428
429impl TransactionIntent {
430    pub fn cell_id_option(&self) -> Option<AccountId> {
431        match self {
432            TransactionIntent::DeployCell { cell_id, .. }
433            | TransactionIntent::DeployToken { cell_id, .. }
434            | TransactionIntent::CallCell { cell_id, .. }
435            | TransactionIntent::UpgradeCell { cell_id, .. }
436            | TransactionIntent::TransferOwnership { cell_id, .. }
437            | TransactionIntent::AcceptOwnership { cell_id }
438            | TransactionIntent::MakeImmutable { cell_id }
439            | TransactionIntent::CloseCell { cell_id }
440            | TransactionIntent::ProposeCellUpgrade { cell_id, .. }
441            | TransactionIntent::ProposeCellOwnershipTransfer { cell_id, .. }
442            | TransactionIntent::ProposeCellMakeImmutable { cell_id, .. }
443            | TransactionIntent::VoteCellProposal { cell_id, .. }
444            | TransactionIntent::ExecuteCellProposal { cell_id, .. }
445            | TransactionIntent::TokenTransfer {
446                token_cell: cell_id,
447                ..
448            }
449            | TransactionIntent::TokenMint {
450                token_cell: cell_id,
451                ..
452            }
453            | TransactionIntent::TokenBurn {
454                token_cell: cell_id,
455                ..
456            } => Some(*cell_id),
457            TransactionIntent::CallCellChain { calls, .. } => calls.first().map(|c| c.cell_id),
458            _ => None,
459        }
460    }
461}