arb_stylus/
env.rs

1use std::marker::PhantomData;
2
3use arbos::programs::types::EvmData;
4use wasmer::{FunctionEnvMut, Memory, MemoryView, Pages, StoreMut};
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/// The WASM execution environment.
17///
18/// Contains all state needed during Stylus program execution,
19/// including the EVM API bridge, metering state, and I/O buffers.
20#[derive(Debug)]
21pub struct WasmEnv<E: EvmApi> {
22    /// The instance's arguments.
23    pub args: Vec<u8>,
24    /// The instance's return data.
25    pub outs: Vec<u8>,
26    /// WASM linear memory.
27    pub memory: Option<Memory>,
28    /// Ink metering state via WASM globals.
29    pub meter: Option<MeterData>,
30    /// Bridge to EVM state (storage, calls, etc.).
31    pub evm_api: E,
32    /// EVM context data (block info, sender, etc.).
33    pub evm_data: EvmData,
34    /// Cached length of the last call's return data.
35    pub evm_return_data_len: u32,
36    /// Compile-time configuration.
37    pub compile: CompileConfig,
38    /// Runtime configuration (set when running).
39    pub config: Option<StylusConfig>,
40    _phantom: PhantomData<E>,
41}
42
43impl<E: EvmApi> WasmEnv<E> {
44    pub fn new(
45        compile: CompileConfig,
46        config: Option<StylusConfig>,
47        evm_api: E,
48        evm_data: EvmData,
49    ) -> Self {
50        Self {
51            compile,
52            config,
53            evm_api,
54            evm_data,
55            args: vec![],
56            outs: vec![],
57            memory: None,
58            meter: None,
59            evm_return_data_len: 0,
60            _phantom: PhantomData,
61        }
62    }
63
64    /// Create a HostioInfo and charge the standard hostio cost plus `ink`.
65    pub fn start<'a>(
66        env: &'a mut WasmEnvMut<'_, E>,
67        ink: Ink,
68    ) -> Result<HostioInfo<'a, E>, Escape> {
69        let mut info = Self::program(env)?;
70        info.buy_ink(HOSTIO_INK.saturating_add(ink))?;
71        Ok(info)
72    }
73
74    /// Create a HostioInfo for accessing host functionality.
75    pub fn program<'a>(env: &'a mut WasmEnvMut<'_, E>) -> Result<HostioInfo<'a, E>, Escape> {
76        let (env, store) = env.data_and_store_mut();
77        let memory = env.memory.clone().expect("WASM memory not initialized");
78        let mut info = HostioInfo {
79            env,
80            memory,
81            store,
82            start_ink: Ink(0),
83        };
84        if info.env.evm_data.tracing {
85            info.start_ink = info.ink_ready()?;
86        }
87        Ok(info)
88    }
89
90    pub fn meter_mut(&mut self) -> &mut MeterData {
91        self.meter.as_mut().expect("not metered")
92    }
93
94    pub fn meter(&self) -> &MeterData {
95        self.meter.as_ref().expect("not metered")
96    }
97}
98
99/// Ink meter data stored as raw pointers to WASM globals.
100///
101/// These point into the wasmer Store's global storage and are used
102/// for fast ink reads/writes without going through the full wasmer API.
103#[derive(Clone, Copy, Debug)]
104pub struct MeterData {
105    ink_left: u64,
106    ink_status: u32,
107}
108
109impl MeterData {
110    pub fn new() -> Self {
111        Self {
112            ink_left: 0,
113            ink_status: 0,
114        }
115    }
116
117    pub fn ink(&self) -> Ink {
118        Ink(self.ink_left)
119    }
120
121    pub fn status(&self) -> u32 {
122        self.ink_status
123    }
124
125    pub fn set_ink(&mut self, ink: Ink) {
126        self.ink_left = ink.0;
127    }
128
129    pub fn set_status(&mut self, status: u32) {
130        self.ink_status = status;
131    }
132}
133
134unsafe impl Send for MeterData {}
135
136/// Wrapper providing access to host I/O operations during WASM execution.
137///
138/// Bundles the WasmEnv, Memory, and Store together for convenient access
139/// in host function implementations.
140pub struct HostioInfo<'a, E: EvmApi> {
141    pub env: &'a mut WasmEnv<E>,
142    pub memory: Memory,
143    pub store: StoreMut<'a>,
144    pub start_ink: Ink,
145}
146
147impl<E: EvmApi> HostioInfo<'_, E> {
148    pub fn config(&self) -> StylusConfig {
149        self.env.config.expect("no config")
150    }
151
152    pub fn pricing(&self) -> crate::config::PricingParams {
153        self.config().pricing
154    }
155
156    pub fn view(&self) -> MemoryView<'_> {
157        self.memory.view(&self.store)
158    }
159
160    pub fn memory_size(&self) -> Pages {
161        self.memory.ty(&self.store).minimum
162    }
163
164    pub fn read_fixed<const N: usize>(
165        &self,
166        ptr: u32,
167    ) -> Result<[u8; N], wasmer::MemoryAccessError> {
168        let mut data = [0u8; N];
169        self.view().read(ptr as u64, &mut data)?;
170        Ok(data)
171    }
172
173    pub fn read_slice(&self, ptr: u32, len: u32) -> Result<Vec<u8>, wasmer::MemoryAccessError> {
174        let mut data = vec![0u8; len as usize];
175        self.view().read(ptr as u64, &mut data)?;
176        Ok(data)
177    }
178
179    pub fn write_slice(&self, ptr: u32, data: &[u8]) -> Result<(), wasmer::MemoryAccessError> {
180        self.view().write(ptr as u64, data)
181    }
182
183    pub fn write_u32(&self, ptr: u32, value: u32) -> Result<(), wasmer::MemoryAccessError> {
184        self.view().write(ptr as u64, &value.to_le_bytes())
185    }
186}
187
188impl<E: EvmApi> MeteredMachine for HostioInfo<'_, E> {
189    fn ink_left(&self) -> MachineMeter {
190        let vm = self.env.meter();
191        match vm.status() {
192            0 => MachineMeter::Ready(vm.ink()),
193            _ => MachineMeter::Exhausted,
194        }
195    }
196
197    fn set_meter(&mut self, meter: MachineMeter) {
198        let vm = self.env.meter_mut();
199        vm.set_ink(meter.ink());
200        vm.set_status(meter.status());
201    }
202}
203
204impl<E: EvmApi> GasMeteredMachine for HostioInfo<'_, E> {
205    fn pricing(&self) -> crate::config::PricingParams {
206        self.config().pricing
207    }
208}
209
210impl<E: EvmApi> std::ops::Deref for HostioInfo<'_, E> {
211    type Target = WasmEnv<E>;
212    fn deref(&self) -> &Self::Target {
213        self.env
214    }
215}
216
217impl<E: EvmApi> std::ops::DerefMut for HostioInfo<'_, E> {
218    fn deref_mut(&mut self) -> &mut Self::Target {
219        self.env
220    }
221}