Skip to main content

truthlinked_runtime/
compiler_aware.rs

1//! Compiler-aware conflict domain extraction.
2//!
3//! Extracts conflict domains from cell metadata at compile time.
4
5use std::collections::HashSet;
6
7use truthlinked_core::pq_execution::{AccountId, Transaction, TransactionIntent};
8
9use crate::cells::CellAccount;
10
11#[derive(Debug, Clone)]
12pub struct ConcreteConflictDomain {
13    pub reads: HashSet<StorageKey>,
14    pub writes: HashSet<StorageKey>,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18pub enum StorageKey {
19    Account(AccountId),
20    CellStorage(AccountId, [u8; 32]),
21    Global(String),
22    Unknown([u8; 32]),
23}
24
25impl ConcreteConflictDomain {
26    pub fn conflicts_with(&self, other: &Self) -> bool {
27        if self.writes.iter().any(|w| other.writes.contains(w)) {
28            return true;
29        }
30        if self.reads.iter().any(|r| other.writes.contains(r)) {
31            return true;
32        }
33        if self.writes.iter().any(|w| other.reads.contains(w)) {
34            return true;
35        }
36        false
37    }
38
39    pub fn from_transaction(
40        tx: &Transaction,
41        cell: Option<&CellAccount>,
42        cells: &std::collections::HashMap<AccountId, CellAccount>,
43    ) -> Self {
44        let mut reads = HashSet::new();
45        let mut writes = HashSet::new();
46
47        match &tx.intent {
48            TransactionIntent::Transfer { recipient, .. }
49            | TransactionIntent::Claim { recipient, .. } => {
50                writes.insert(StorageKey::Account(tx.sender));
51                writes.insert(StorageKey::Account(*recipient));
52            }
53            TransactionIntent::BatchTransfer { transfers } => {
54                writes.insert(StorageKey::Account(tx.sender));
55                for t in transfers {
56                    writes.insert(StorageKey::Account(t.recipient));
57                }
58            }
59            TransactionIntent::TransferToName { .. }
60            | TransactionIntent::BatchTransferToName { .. } => {
61                // Name resolution is runtime - conservatively mark sender + global
62                writes.insert(StorageKey::Account(tx.sender));
63                writes.insert(StorageKey::Global("name_registry".to_string()));
64            }
65            TransactionIntent::DepositCompute { .. }
66            | TransactionIntent::WithdrawCompute { .. }
67            | TransactionIntent::WrapTRTH { .. }
68            | TransactionIntent::UnwrapTRTH { .. } => {
69                writes.insert(StorageKey::Account(tx.sender));
70            }
71
72            TransactionIntent::TokenTransfer { token_cell, .. } => {
73                writes.insert(StorageKey::CellStorage(*token_cell, tx.sender));
74            }
75            TransactionIntent::CloseCell { cell_id } => {
76                writes.insert(StorageKey::CellStorage(*cell_id, *cell_id));
77            }
78            TransactionIntent::ProposeCellUpgrade { cell_id, .. }
79            | TransactionIntent::ProposeCellOwnershipTransfer { cell_id, .. }
80            | TransactionIntent::ProposeCellMakeImmutable { cell_id, .. }
81            | TransactionIntent::VoteCellProposal { cell_id, .. }
82            | TransactionIntent::ExecuteCellProposal { cell_id, .. } => {
83                writes.insert(StorageKey::CellStorage(*cell_id, *cell_id));
84            }
85            TransactionIntent::ProposeTokenAuthority { .. }
86            | TransactionIntent::VoteTokenAuthority { .. }
87            | TransactionIntent::CallSystem { .. }
88            | TransactionIntent::ProposeUrl { .. }
89            | TransactionIntent::VoteUrl { .. }
90            | TransactionIntent::ReportMaliciousUrl { .. } => {
91                writes.insert(StorageKey::Global("system".to_string()));
92            }
93
94            TransactionIntent::CallCell {
95                cell_id, calldata, ..
96            } => {
97                if let Some(cell) = cell {
98                    for read_key in &cell.declared_reads {
99                        reads.insert(StorageKey::CellStorage(*cell_id, *read_key));
100                    }
101                    for write_key in &cell.declared_writes {
102                        if !cell.commutative_keys.contains(write_key) {
103                            writes.insert(StorageKey::CellStorage(*cell_id, *write_key));
104                        }
105                    }
106
107                    if !cell.storage_key_specs.is_empty() {
108                        for spec in &cell.storage_key_specs {
109                            let end = match spec.offset.checked_add(spec.len) {
110                                Some(v) => v,
111                                None => continue,
112                            };
113                            if spec.len == 32 && calldata.len() >= end {
114                                let mut user_key = [0u8; 32];
115                                user_key.copy_from_slice(&calldata[spec.offset..end]);
116                                if !cell.commutative_keys.contains(&user_key) {
117                                    writes.insert(StorageKey::CellStorage(*cell_id, user_key));
118                                }
119                            }
120                        }
121                    }
122                } else {
123                    writes.insert(StorageKey::Global("global".to_string()));
124                }
125            }
126
127            TransactionIntent::CallCellChain { calls, .. } => {
128                for call in calls {
129                    if let Some(c) = cells.get(&call.cell_id) {
130                        for read_key in &c.declared_reads {
131                            reads.insert(StorageKey::CellStorage(call.cell_id, *read_key));
132                        }
133                        for write_key in &c.declared_writes {
134                            if !c.commutative_keys.contains(write_key) {
135                                writes.insert(StorageKey::CellStorage(call.cell_id, *write_key));
136                            }
137                        }
138                        if !c.storage_key_specs.is_empty() {
139                            for spec in &c.storage_key_specs {
140                                let end = match spec.offset.checked_add(spec.len) {
141                                    Some(v) => v,
142                                    None => continue,
143                                };
144                                if spec.len == 32 && call.calldata.len() >= end {
145                                    let mut user_key = [0u8; 32];
146                                    user_key.copy_from_slice(&call.calldata[spec.offset..end]);
147                                    if !c.commutative_keys.contains(&user_key) {
148                                        writes.insert(StorageKey::CellStorage(
149                                            call.cell_id,
150                                            user_key,
151                                        ));
152                                    }
153                                }
154                            }
155                        }
156                    } else {
157                        // Unknown cell - conservatively block the whole batch slot
158                        writes.insert(StorageKey::Global("global".to_string()));
159                    }
160                }
161            }
162
163            // MCP intents and any unknowns serialize to a global conflict domain for safety.
164            TransactionIntent::RegisterMcpTool { .. }
165            | TransactionIntent::RegisterMcpResource { .. }
166            | TransactionIntent::RegisterMcpPrompt { .. }
167            | TransactionIntent::RegisterAgent { .. }
168            | TransactionIntent::SuspendAgent { .. }
169            | TransactionIntent::ReinstateAgent { .. }
170            | TransactionIntent::McpToolCall { .. }
171            | TransactionIntent::PrivateBalanceInit { .. }
172            | TransactionIntent::PrivateBalanceDeposit { .. }
173            | TransactionIntent::PrivateBalanceWithdraw { .. }
174            | TransactionIntent::PrivateBalanceConfidentialTransfer { .. }
175            | TransactionIntent::SubmitOracleCommit { .. }
176            | TransactionIntent::SubmitOracleReveal { .. }
177            | TransactionIntent::SetCellVisibility { .. }
178            | TransactionIntent::MintNFT { .. }
179            | TransactionIntent::TransferNFT { .. }
180            | TransactionIntent::BurnNFT { .. }
181            | TransactionIntent::ApproveNFT { .. }
182            | TransactionIntent::DeployCell { .. }
183            | TransactionIntent::DeployToken { .. }
184            | TransactionIntent::UpgradeCell { .. }
185            | TransactionIntent::TransferOwnership { .. }
186            | TransactionIntent::AcceptOwnership { .. }
187            | TransactionIntent::MakeImmutable { .. }
188            | TransactionIntent::TokenMint { .. }
189            | TransactionIntent::TokenBurn { .. }
190            | TransactionIntent::TokenFreeze { .. }
191            | TransactionIntent::TokenThaw { .. }
192            | TransactionIntent::Stake { .. }
193            | TransactionIntent::Unstake { .. }
194            | TransactionIntent::WithdrawStake
195            | TransactionIntent::Unjail
196            | TransactionIntent::RotateKey { .. } => {
197                writes.insert(StorageKey::Global("global".to_string()));
198            }
199        }
200
201        ConcreteConflictDomain { reads, writes }
202    }
203}
204
205pub fn partition_by_compiler_domains(
206    batch: &[Transaction],
207    cells: &std::collections::HashMap<AccountId, CellAccount>,
208) -> Vec<Vec<usize>> {
209    let mut partitions: Vec<Vec<usize>> = Vec::new();
210    let mut domains: Vec<ConcreteConflictDomain> = Vec::new();
211
212    for (tx_idx, tx) in batch.iter().enumerate() {
213        let cell = match &tx.intent {
214            TransactionIntent::CallCell { cell_id, .. } => cells.get(cell_id),
215            TransactionIntent::CallCellChain { calls, .. } => {
216                calls.get(0).and_then(|c| cells.get(&c.cell_id))
217            }
218            _ => None,
219        };
220        let domain = ConcreteConflictDomain::from_transaction(tx, cell, cells);
221
222        let mut placed = false;
223        for (part_idx, part_domain) in domains.iter_mut().enumerate() {
224            if !domain.conflicts_with(part_domain) {
225                part_domain.reads.extend(domain.reads.iter().cloned());
226                part_domain.writes.extend(domain.writes.iter().cloned());
227                partitions[part_idx].push(tx_idx);
228                placed = true;
229                break;
230            }
231        }
232        if !placed {
233            partitions.push(vec![tx_idx]);
234            domains.push(domain);
235        }
236    }
237
238    partitions
239}