1use std::marker::PhantomData;
2
3use arb_chainspec::arbos_version::ARBOS_VERSION_59;
4use arbos::programs::{memory::MemoryModel, types::EvmData};
5use wasmer::{FunctionEnvMut, Global, Memory, MemoryView, Pages, StoreMut, Value};
6
7use crate::{
8 config::{CompileConfig, StylusConfig},
9 error::StylusError,
10 evm_api::EvmApi,
11 ink::Ink,
12 meter::{GasMeteredMachine, HOSTIO_INK, MachineMeter, MeteredMachine},
13};
14
15#[inline]
18pub fn page_limit_exceeded(arbos_version: u64, page_limit: u16, new_open: u16) -> bool {
19 arbos_version >= ARBOS_VERSION_59 && page_limit > 0 && new_open > page_limit
20}
21
22#[inline]
26pub fn pay_for_memory_grow_overflows(arbos_version: u64, pages: u32) -> bool {
27 pages > u32::from(u16::MAX) && arbos_version >= ARBOS_VERSION_59
28}
29
30pub type WasmEnvMut<'a, E> = FunctionEnvMut<'a, WasmEnv<E>>;
31
32#[derive(Debug)]
37pub struct WasmEnv<E: EvmApi> {
38 pub args: Vec<u8>,
40 pub outs: Vec<u8>,
42 pub memory: Option<Memory>,
44 pub meter: Option<MeterData>,
46 pub evm_api: E,
48 pub evm_data: EvmData,
50 pub evm_return_data_len: u32,
52 pub compile: CompileConfig,
54 pub config: Option<StylusConfig>,
56 pub ink_global: Option<Global>,
58 pub ink_status_global: Option<Global>,
60 pub pages_open: u16,
61 pub pages_ever: u16,
62 pub free_pages: u16,
63 pub page_gas: u16,
64 pub page_limit: u16,
65 pub arbos_version: u64,
66 _phantom: PhantomData<E>,
67}
68
69impl<E: EvmApi> WasmEnv<E> {
70 pub fn new(
71 compile: CompileConfig,
72 config: Option<StylusConfig>,
73 evm_api: E,
74 evm_data: EvmData,
75 ) -> Self {
76 Self {
77 compile,
78 config,
79 evm_api,
80 evm_data,
81 args: vec![],
82 outs: vec![],
83 memory: None,
84 meter: None,
85 ink_global: None,
86 ink_status_global: None,
87 evm_return_data_len: 0,
88 pages_open: 0,
89 pages_ever: 0,
90 free_pages: 0,
91 page_gas: 0,
92 page_limit: 0,
93 arbos_version: 0,
94 _phantom: PhantomData,
95 }
96 }
97
98 pub fn set_pages(
101 &mut self,
102 open: u16,
103 ever: u16,
104 free_pages: u16,
105 page_gas: u16,
106 page_limit: u16,
107 arbos_version: u64,
108 ) {
109 self.pages_open = open;
110 self.pages_ever = ever;
111 self.free_pages = free_pages;
112 self.page_gas = page_gas;
113 self.page_limit = page_limit;
114 self.arbos_version = arbos_version;
115 }
116
117 pub fn add_pages_charge(&mut self, new_pages: u16) -> u64 {
120 let model = MemoryModel::new(self.free_pages, self.page_gas);
121 let cost = model.gas_cost(new_pages, self.pages_open, self.pages_ever);
122 self.pages_open = self.pages_open.saturating_add(new_pages);
123 self.pages_ever = self.pages_ever.max(self.pages_open);
124 if page_limit_exceeded(self.arbos_version, self.page_limit, self.pages_open) {
125 return u64::MAX;
126 }
127 cost
128 }
129
130 pub fn start<'a>(
132 env: &'a mut WasmEnvMut<'_, E>,
133 ink: Ink,
134 ) -> Result<HostioInfo<'a, E>, StylusError> {
135 let mut info = Self::program(env)?;
136 info.buy_ink(HOSTIO_INK.saturating_add(ink))?;
137 Ok(info)
138 }
139
140 pub fn program<'a>(env: &'a mut WasmEnvMut<'_, E>) -> Result<HostioInfo<'a, E>, StylusError> {
142 let (env, store) = env.data_and_store_mut();
143 let memory = env.memory.clone().expect("WASM memory not initialized");
144 let mut info = HostioInfo {
145 env,
146 memory,
147 store,
148 start_ink: Ink(0),
149 };
150 if info.env.evm_data.tracing {
151 info.start_ink = info.ink_ready()?;
152 }
153 Ok(info)
154 }
155
156 pub fn meter_mut(&mut self) -> &mut MeterData {
157 self.meter.as_mut().expect("not metered")
158 }
159
160 pub fn meter(&self) -> &MeterData {
161 self.meter.as_ref().expect("not metered")
162 }
163}
164
165#[derive(Clone, Copy, Debug)]
170pub struct MeterData {
171 ink_left: u64,
172 ink_status: u32,
173}
174
175impl MeterData {
176 pub fn new() -> Self {
177 Self {
178 ink_left: 0,
179 ink_status: 0,
180 }
181 }
182
183 pub fn ink(&self) -> Ink {
184 Ink(self.ink_left)
185 }
186
187 pub fn status(&self) -> u32 {
188 self.ink_status
189 }
190
191 pub fn set_ink(&mut self, ink: Ink) {
192 self.ink_left = ink.0;
193 }
194
195 pub fn set_status(&mut self, status: u32) {
196 self.ink_status = status;
197 }
198}
199
200pub struct HostioInfo<'a, E: EvmApi> {
205 pub env: &'a mut WasmEnv<E>,
206 pub memory: Memory,
207 pub store: StoreMut<'a>,
208 pub start_ink: Ink,
209}
210
211impl<E: EvmApi> HostioInfo<'_, E> {
212 pub fn config(&self) -> StylusConfig {
213 self.env.config.expect("no config")
214 }
215
216 pub fn pricing(&self) -> crate::config::PricingParams {
217 self.config().pricing
218 }
219
220 pub fn view(&self) -> MemoryView<'_> {
221 self.memory.view(&self.store)
222 }
223
224 pub fn memory_size(&self) -> Pages {
225 self.memory.ty(&self.store).minimum
226 }
227
228 pub fn read_fixed<const N: usize>(
229 &self,
230 ptr: u32,
231 ) -> Result<[u8; N], wasmer::MemoryAccessError> {
232 let mut data = [0u8; N];
233 self.view().read(ptr as u64, &mut data)?;
234 Ok(data)
235 }
236
237 pub fn read_slice(&self, ptr: u32, len: u32) -> Result<Vec<u8>, wasmer::MemoryAccessError> {
238 let mut data = vec![0u8; len as usize];
239 self.view().read(ptr as u64, &mut data)?;
240 Ok(data)
241 }
242
243 pub fn write_slice(&self, ptr: u32, data: &[u8]) -> Result<(), wasmer::MemoryAccessError> {
244 self.view().write(ptr as u64, data)
245 }
246
247 pub fn write_u32(&self, ptr: u32, value: u32) -> Result<(), wasmer::MemoryAccessError> {
248 self.view().write(ptr as u64, &value.to_le_bytes())
249 }
250}
251
252impl<E: EvmApi> MeteredMachine for HostioInfo<'_, E> {
253 fn ink_left(&self) -> MachineMeter {
254 let vm = self.env.meter();
255 match vm.status() {
256 0 => MachineMeter::Ready(vm.ink()),
257 _ => MachineMeter::Exhausted,
258 }
259 }
260
261 fn set_meter(&mut self, meter: MachineMeter) {
262 if let Some(ref g) = self.env.ink_global {
264 let _ = g.set(&mut self.store, Value::I64(meter.ink().0 as i64));
265 }
266 if let Some(ref g) = self.env.ink_status_global {
267 let _ = g.set(&mut self.store, Value::I32(meter.status() as i32));
268 }
269 let vm = self.env.meter_mut();
271 vm.set_ink(meter.ink());
272 vm.set_status(meter.status());
273 }
274
275 fn buy_ink(&mut self, ink: Ink) -> Result<(), StylusError> {
278 let current = if let Some(ref g) = self.env.ink_global {
280 if let Value::I64(v) = g.get(&mut self.store) {
281 Ink(v as u64)
282 } else {
283 self.ink_ready()?
284 }
285 } else {
286 self.ink_ready()?
287 };
288 if current < ink {
289 self.set_meter(MachineMeter::Exhausted);
290 return StylusError::out_of_ink();
291 }
292 self.set_meter(MachineMeter::Ready(current - ink));
293 Ok(())
294 }
295
296 fn require_ink(&mut self, ink: Ink) -> Result<(), StylusError> {
297 let current = if let Some(ref g) = self.env.ink_global {
298 if let Value::I64(v) = g.get(&mut self.store) {
299 Ink(v as u64)
300 } else {
301 self.ink_ready()?
302 }
303 } else {
304 self.ink_ready()?
305 };
306 if current < ink {
307 return StylusError::out_of_ink();
308 }
309 Ok(())
310 }
311}
312
313impl<E: EvmApi> GasMeteredMachine for HostioInfo<'_, E> {
314 fn pricing(&self) -> crate::config::PricingParams {
315 self.config().pricing
316 }
317}
318
319impl<E: EvmApi> std::ops::Deref for HostioInfo<'_, E> {
320 type Target = WasmEnv<E>;
321 fn deref(&self) -> &Self::Target {
322 self.env
323 }
324}
325
326impl<E: EvmApi> std::ops::DerefMut for HostioInfo<'_, E> {
327 fn deref_mut(&mut self) -> &mut Self::Target {
328 self.env
329 }
330}