Skip to main content

truthlinked_axiom/
host.rs

1//! Truthlinked Axiom Src Host
2//!
3//! Owns host interfaces exposed to Axiom programs.
4//! VM and bytecode changes are consensus-sensitive and must remain deterministic across platforms.
5
6use crate::error::AxiomError;
7use alloc::vec::Vec;
8
9/// Log entry emitted by a cell.
10#[derive(Debug, Clone)]
11pub struct CellLog {
12    pub topics: Vec<[u8; 32]>,
13    pub data: Vec<u8>,
14}
15
16/// Result of a cross-cell call.
17#[derive(Debug, Clone)]
18pub struct CrossCellResult {
19    pub success: bool,
20    pub return_data: Vec<u8>,
21    pub gas_used: u64,
22}
23
24/// Staking side-effect queued by a cell.
25#[derive(Debug, Clone)]
26pub enum StakingOp {
27    Stake { pubkey: Vec<u8>, amount: u64 },
28    Unstake { pubkey: Vec<u8>, amount: u64 },
29    Withdraw { pubkey: Vec<u8> },
30    Unjail { pubkey: Vec<u8> },
31}
32
33/// Name-registry side-effect queued by a cell.
34#[derive(Debug, Clone)]
35pub enum NameOp {
36    Propose {
37        name: Vec<u8>,
38        target: [u8; 32],
39        owner: [u8; 32],
40    },
41    Vote {
42        name: Vec<u8>,
43        approve: bool,
44    },
45    Renew {
46        name: Vec<u8>,
47    },
48    Transfer {
49        name: Vec<u8>,
50        new_owner: [u8; 32],
51    },
52}
53
54/// Everything the VM needs from the chain.
55pub trait Host {
56    // ── Storage ─────────────────────────────────────────────────────────────
57    fn storage_read(&self, key: &[u8; 32]) -> Result<[u8; 32], AxiomError>;
58    fn storage_write(&mut self, key: &[u8; 32], value: &[u8; 32]) -> Result<(), AxiomError>;
59    fn storage_delete(&mut self, key: &[u8; 32]) -> Result<(), AxiomError>;
60
61    // ── Context ──────────────────────────────────────────────────────────────
62    fn caller(&self) -> [u8; 32];
63    fn owner(&self) -> [u8; 32];
64    fn cell_id(&self) -> [u8; 32];
65    fn height(&self) -> u64;
66    fn timestamp(&self) -> u64;
67    fn value(&self) -> u128;
68    fn calldata(&self) -> &[u8];
69
70    // ── Output ───────────────────────────────────────────────────────────────
71    fn set_return_data(&mut self, data: Vec<u8>) -> Result<(), AxiomError>;
72    fn emit_log(&mut self, log: CellLog) -> Result<(), AxiomError>;
73
74    // ── Cross-cell ───────────────────────────────────────────────────────────
75    fn call_cell(
76        &mut self,
77        cell_id: &[u8; 32],
78        calldata: &[u8],
79        value: u128,
80        gas_limit: u64,
81    ) -> Result<CrossCellResult, AxiomError>;
82
83    // ── Crypto ───────────────────────────────────────────────────────────────
84    fn sha256(&self, input: &[u8]) -> [u8; 32];
85
86    // ── Privileged: staking (system cells only) ──────────────────────────────
87    fn staking_op(&mut self, _op: StakingOp) -> Result<(), AxiomError> {
88        Err(AxiomError::Unauthorized)
89    }
90    /// Returns active stake for a validator pubkey (u64, little-endian in low 8 bytes).
91    fn staking_validator_stake(&self, _pubkey: &[u8]) -> Result<u64, AxiomError> {
92        Err(AxiomError::Unauthorized)
93    }
94    /// Returns total active stake across all validators.
95    fn staking_total_stake(&self) -> Result<u64, AxiomError> {
96        Err(AxiomError::Unauthorized)
97    }
98
99    // ── Privileged: governance params (system cells only) ────────────────────
100    fn governance_set_param(
101        &mut self,
102        _key: &[u8; 32],
103        _value: &[u8; 32],
104    ) -> Result<(), AxiomError> {
105        Err(AxiomError::Unauthorized)
106    }
107
108    // ── Privileged: native transfer (system cells only) ──────────────────────
109    fn native_transfer(&mut self, _to: &[u8; 32], _amount: u128) -> Result<(), AxiomError> {
110        Err(AxiomError::Unauthorized)
111    }
112
113    // ── Privileged: name registry (system cells only) ────────────────────────
114    fn name_op(&mut self, _op: NameOp) -> Result<(), AxiomError> {
115        Err(AxiomError::Unauthorized)
116    }
117
118    // ── Accord ───────────────────────────────────────────────────────────────
119    /// Queue an accord request. Returns the request_id in dst.
120    fn accord_request(
121        &mut self,
122        _url: &[u8],
123        _method: &[u8],
124        _body: &[u8],
125    ) -> Result<[u8; 32], AxiomError> {
126        Err(AxiomError::NotImplemented(
127            crate::opcode::tag::ACCORD_REQUEST,
128        ))
129    }
130
131    /// Read a finalized accord result by request_id.
132    /// Returns Ok(Some(body_bytes)) if result is ready, Ok(None) if still pending.
133    fn accord_read(&self, _request_id: &[u8; 32]) -> Result<Option<Vec<u8>>, AxiomError> {
134        Err(AxiomError::NotImplemented(crate::opcode::tag::ACCORD_READ))
135    }
136
137    // ── Token ops (metered, sandboxed) ───────────────────────────────────────
138    fn token_balance(&self, _token: &[u8; 32], _account: &[u8; 32]) -> Result<u128, AxiomError> {
139        Err(AxiomError::Unauthorized)
140    }
141    fn token_transfer(
142        &mut self,
143        _token: &[u8; 32],
144        _from: &[u8; 32],
145        _to: &[u8; 32],
146        _amount: u128,
147    ) -> Result<(), AxiomError> {
148        Err(AxiomError::Unauthorized)
149    }
150    fn token_mint(
151        &mut self,
152        _token: &[u8; 32],
153        _recipient: &[u8; 32],
154        _amount: u128,
155    ) -> Result<(), AxiomError> {
156        Err(AxiomError::Unauthorized)
157    }
158    fn token_burn(
159        &mut self,
160        _token: &[u8; 32],
161        _owner: &[u8; 32],
162        _amount: u128,
163    ) -> Result<(), AxiomError> {
164        Err(AxiomError::Unauthorized)
165    }
166    fn token_freeze(&mut self, _token: &[u8; 32], _account: &[u8; 32]) -> Result<(), AxiomError> {
167        Err(AxiomError::Unauthorized)
168    }
169    fn token_thaw(&mut self, _token: &[u8; 32], _account: &[u8; 32]) -> Result<(), AxiomError> {
170        Err(AxiomError::Unauthorized)
171    }
172
173    // ── Limits ───────────────────────────────────────────────────────────────
174    fn max_return_data(&self) -> usize {
175        262_144
176    }
177    fn max_log_data(&self) -> usize {
178        65_536
179    }
180    fn max_log_topics(&self) -> usize {
181        8
182    }
183    /// Call depth limit. Each nested call costs 1,000 gas minimum, so depth 500
184    /// requires at least 500,000 gas - natural economic deterrent against abuse.
185    fn max_call_depth(&self) -> u32 {
186        500
187    }
188}