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        proof_sender_old_balance: u64,
365        proof_sender_new_balance: u64,
366        proof_recipient_old_balance: u64,
367        proof_recipient_new_balance: u64,
368        proof_amount: u64,
369    },
370    // Oracle commit/reveal
371    SubmitOracleCommit {
372        request_id: [u8; 32],
373        commit_hash: [u8; 32],
374    },
375    SubmitOracleReveal {
376        request_id: [u8; 32],
377        response_body: Vec<u8>,
378        response_status: u16,
379    },
380}
381
382pub fn system_cell_id(label: &str) -> AccountId {
383    use sha2::{Digest, Sha256};
384    let mut hasher = Sha256::new();
385    hasher.update(label.as_bytes());
386    hasher.finalize().into()
387}
388
389pub fn staking_system_cell_id() -> AccountId {
390    system_cell_id("truthlinked-staking-v1")
391}
392
393pub fn treasury_system_cell_id() -> AccountId {
394    system_cell_id("truthlinked-treasury-v1")
395}
396
397pub fn governance_system_cell_id() -> AccountId {
398    system_cell_id("truthlinked-governance-v1")
399}
400
401pub fn name_registry_system_cell_id() -> AccountId {
402    system_cell_id("truthlinked-name-registry-v1")
403}
404
405pub fn token_governance_system_cell_id() -> AccountId {
406    system_cell_id("truthlinked-token-governance-v1")
407}
408
409pub fn oracle_governance_system_cell_id() -> AccountId {
410    system_cell_id("truthlinked-oracle-governance-v1")
411}
412
413pub fn usdc_system_cell_id() -> AccountId {
414    system_cell_id("truthlinked-usdc-controller-v1")
415}
416
417pub fn wtrth_system_cell_id() -> AccountId {
418    system_cell_id("truthlinked-wtrth-v1")
419}
420
421pub fn usdt_system_cell_id() -> AccountId {
422    system_cell_id("truthlinked-usdt-controller-v1")
423}
424
425impl Transaction {
426    pub fn current_timestamp() -> u64 {
427        std::time::SystemTime::now()
428            .duration_since(std::time::UNIX_EPOCH)
429            .unwrap()
430            .as_secs()
431    }
432}
433
434impl TransactionIntent {
435    pub fn cell_id_option(&self) -> Option<AccountId> {
436        match self {
437            TransactionIntent::DeployCell { cell_id, .. }
438            | TransactionIntent::DeployToken { cell_id, .. }
439            | TransactionIntent::CallCell { cell_id, .. }
440            | TransactionIntent::UpgradeCell { cell_id, .. }
441            | TransactionIntent::TransferOwnership { cell_id, .. }
442            | TransactionIntent::AcceptOwnership { cell_id }
443            | TransactionIntent::MakeImmutable { cell_id }
444            | TransactionIntent::CloseCell { cell_id }
445            | TransactionIntent::ProposeCellUpgrade { cell_id, .. }
446            | TransactionIntent::ProposeCellOwnershipTransfer { cell_id, .. }
447            | TransactionIntent::ProposeCellMakeImmutable { cell_id, .. }
448            | TransactionIntent::VoteCellProposal { cell_id, .. }
449            | TransactionIntent::ExecuteCellProposal { cell_id, .. }
450            | TransactionIntent::TokenTransfer {
451                token_cell: cell_id,
452                ..
453            }
454            | TransactionIntent::TokenMint {
455                token_cell: cell_id,
456                ..
457            }
458            | TransactionIntent::TokenBurn {
459                token_cell: cell_id,
460                ..
461            } => Some(*cell_id),
462            TransactionIntent::CallCellChain { calls, .. } => calls.first().map(|c| c.cell_id),
463            _ => None,
464        }
465    }
466}