1use std::marker::PhantomData;
2
3use arbos::programs::types::EvmData;
4use wasmer::{FunctionEnvMut, Global, Memory, MemoryView, Pages, StoreMut, Value};
5
6use crate::{
7 config::{CompileConfig, StylusConfig},
8 error::Escape,
9 evm_api::EvmApi,
10 ink::Ink,
11 meter::{GasMeteredMachine, MachineMeter, MeteredMachine, HOSTIO_INK},
12};
13
14pub type WasmEnvMut<'a, E> = FunctionEnvMut<'a, WasmEnv<E>>;
15
16#[derive(Debug)]
21pub struct WasmEnv<E: EvmApi> {
22 pub args: Vec<u8>,
24 pub outs: Vec<u8>,
26 pub memory: Option<Memory>,
28 pub meter: Option<MeterData>,
30 pub evm_api: E,
32 pub evm_data: EvmData,
34 pub evm_return_data_len: u32,
36 pub compile: CompileConfig,
38 pub config: Option<StylusConfig>,
40 pub ink_global: Option<Global>,
42 pub ink_status_global: Option<Global>,
44 _phantom: PhantomData<E>,
45}
46
47impl<E: EvmApi> WasmEnv<E> {
48 pub fn new(
49 compile: CompileConfig,
50 config: Option<StylusConfig>,
51 evm_api: E,
52 evm_data: EvmData,
53 ) -> Self {
54 Self {
55 compile,
56 config,
57 evm_api,
58 evm_data,
59 args: vec![],
60 outs: vec![],
61 memory: None,
62 meter: None,
63 ink_global: None,
64 ink_status_global: None,
65 evm_return_data_len: 0,
66 _phantom: PhantomData,
67 }
68 }
69
70 pub fn start<'a>(
72 env: &'a mut WasmEnvMut<'_, E>,
73 ink: Ink,
74 ) -> Result<HostioInfo<'a, E>, Escape> {
75 let mut info = Self::program(env)?;
76 info.buy_ink(HOSTIO_INK.saturating_add(ink))?;
77 Ok(info)
78 }
79
80 pub fn program<'a>(env: &'a mut WasmEnvMut<'_, E>) -> Result<HostioInfo<'a, E>, Escape> {
82 let (env, store) = env.data_and_store_mut();
83 let memory = env.memory.clone().expect("WASM memory not initialized");
84 let mut info = HostioInfo {
85 env,
86 memory,
87 store,
88 start_ink: Ink(0),
89 };
90 if info.env.evm_data.tracing {
91 info.start_ink = info.ink_ready()?;
92 }
93 Ok(info)
94 }
95
96 pub fn meter_mut(&mut self) -> &mut MeterData {
97 self.meter.as_mut().expect("not metered")
98 }
99
100 pub fn meter(&self) -> &MeterData {
101 self.meter.as_ref().expect("not metered")
102 }
103}
104
105#[derive(Clone, Copy, Debug)]
110pub struct MeterData {
111 ink_left: u64,
112 ink_status: u32,
113}
114
115impl MeterData {
116 pub fn new() -> Self {
117 Self {
118 ink_left: 0,
119 ink_status: 0,
120 }
121 }
122
123 pub fn ink(&self) -> Ink {
124 Ink(self.ink_left)
125 }
126
127 pub fn status(&self) -> u32 {
128 self.ink_status
129 }
130
131 pub fn set_ink(&mut self, ink: Ink) {
132 self.ink_left = ink.0;
133 }
134
135 pub fn set_status(&mut self, status: u32) {
136 self.ink_status = status;
137 }
138}
139
140unsafe impl Send for MeterData {}
141
142pub struct HostioInfo<'a, E: EvmApi> {
147 pub env: &'a mut WasmEnv<E>,
148 pub memory: Memory,
149 pub store: StoreMut<'a>,
150 pub start_ink: Ink,
151}
152
153impl<E: EvmApi> HostioInfo<'_, E> {
154 pub fn config(&self) -> StylusConfig {
155 self.env.config.expect("no config")
156 }
157
158 pub fn pricing(&self) -> crate::config::PricingParams {
159 self.config().pricing
160 }
161
162 pub fn view(&self) -> MemoryView<'_> {
163 self.memory.view(&self.store)
164 }
165
166 pub fn memory_size(&self) -> Pages {
167 self.memory.ty(&self.store).minimum
168 }
169
170 pub fn read_fixed<const N: usize>(
171 &self,
172 ptr: u32,
173 ) -> Result<[u8; N], wasmer::MemoryAccessError> {
174 let mut data = [0u8; N];
175 self.view().read(ptr as u64, &mut data)?;
176 Ok(data)
177 }
178
179 pub fn read_slice(&self, ptr: u32, len: u32) -> Result<Vec<u8>, wasmer::MemoryAccessError> {
180 let mut data = vec![0u8; len as usize];
181 self.view().read(ptr as u64, &mut data)?;
182 Ok(data)
183 }
184
185 pub fn write_slice(&self, ptr: u32, data: &[u8]) -> Result<(), wasmer::MemoryAccessError> {
186 self.view().write(ptr as u64, data)
187 }
188
189 pub fn write_u32(&self, ptr: u32, value: u32) -> Result<(), wasmer::MemoryAccessError> {
190 self.view().write(ptr as u64, &value.to_le_bytes())
191 }
192}
193
194impl<E: EvmApi> MeteredMachine for HostioInfo<'_, E> {
195 fn ink_left(&self) -> MachineMeter {
196 let vm = self.env.meter();
197 match vm.status() {
198 0 => MachineMeter::Ready(vm.ink()),
199 _ => MachineMeter::Exhausted,
200 }
201 }
202
203 fn set_meter(&mut self, meter: MachineMeter) {
204 if let Some(ref g) = self.env.ink_global {
206 let _ = g.set(&mut self.store, Value::I64(meter.ink().0 as i64));
207 }
208 if let Some(ref g) = self.env.ink_status_global {
209 let _ = g.set(&mut self.store, Value::I32(meter.status() as i32));
210 }
211 let vm = self.env.meter_mut();
213 vm.set_ink(meter.ink());
214 vm.set_status(meter.status());
215 }
216
217 fn buy_ink(&mut self, ink: Ink) -> Result<(), Escape> {
220 let current = if let Some(ref g) = self.env.ink_global {
222 if let Value::I64(v) = g.get(&mut self.store) {
223 Ink(v as u64)
224 } else {
225 self.ink_ready()?
226 }
227 } else {
228 self.ink_ready()?
229 };
230 if current < ink {
231 self.set_meter(MachineMeter::Exhausted);
232 return Escape::out_of_ink();
233 }
234 self.set_meter(MachineMeter::Ready(current - ink));
235 Ok(())
236 }
237
238 fn require_ink(&mut self, ink: Ink) -> Result<(), Escape> {
239 let current = if let Some(ref g) = self.env.ink_global {
240 if let Value::I64(v) = g.get(&mut self.store) {
241 Ink(v as u64)
242 } else {
243 self.ink_ready()?
244 }
245 } else {
246 self.ink_ready()?
247 };
248 if current < ink {
249 return Escape::out_of_ink();
250 }
251 Ok(())
252 }
253}
254
255impl<E: EvmApi> GasMeteredMachine for HostioInfo<'_, E> {
256 fn pricing(&self) -> crate::config::PricingParams {
257 self.config().pricing
258 }
259}
260
261impl<E: EvmApi> std::ops::Deref for HostioInfo<'_, E> {
262 type Target = WasmEnv<E>;
263 fn deref(&self) -> &Self::Target {
264 self.env
265 }
266}
267
268impl<E: EvmApi> std::ops::DerefMut for HostioInfo<'_, E> {
269 fn deref_mut(&mut self) -> &mut Self::Target {
270 self.env
271 }
272}