polkavm/
config.rs

1use crate::error::{bail, Error};
2
3#[derive(Copy, Clone, PartialEq, Eq, Debug)]
4pub enum BackendKind {
5    Compiler,
6    Interpreter,
7}
8
9impl core::fmt::Display for BackendKind {
10    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
11        let name = match self {
12            BackendKind::Compiler => "compiler",
13            BackendKind::Interpreter => "interpreter",
14        };
15
16        fmt.write_str(name)
17    }
18}
19
20impl BackendKind {
21    fn from_os_str(s: &std::ffi::OsStr) -> Result<Option<BackendKind>, Error> {
22        if s == "auto" {
23            Ok(None)
24        } else if s == "interpreter" {
25            Ok(Some(BackendKind::Interpreter))
26        } else if s == "compiler" {
27            Ok(Some(BackendKind::Compiler))
28        } else {
29            Err(Error::from_static_str(
30                "invalid value of POLKAVM_BACKEND; supported values are: 'interpreter', 'compiler'",
31            ))
32        }
33    }
34}
35
36impl BackendKind {
37    pub fn is_supported(self) -> bool {
38        match self {
39            BackendKind::Interpreter => true,
40            BackendKind::Compiler => if_compiler_is_supported! {
41                { true } else { false }
42            },
43        }
44    }
45}
46
47#[derive(Copy, Clone, PartialEq, Eq, Debug)]
48pub enum SandboxKind {
49    Linux,
50    Generic,
51}
52
53impl core::fmt::Display for SandboxKind {
54    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
55        let name = match self {
56            SandboxKind::Linux => "linux",
57            SandboxKind::Generic => "generic",
58        };
59
60        fmt.write_str(name)
61    }
62}
63
64impl SandboxKind {
65    fn from_os_str(s: &std::ffi::OsStr) -> Result<Option<SandboxKind>, Error> {
66        if s == "auto" {
67            Ok(None)
68        } else if s == "linux" {
69            Ok(Some(SandboxKind::Linux))
70        } else if s == "generic" {
71            Ok(Some(SandboxKind::Generic))
72        } else {
73            Err(Error::from_static_str(
74                "invalid value of POLKAVM_SANDBOX; supported values are: 'linux', 'generic'",
75            ))
76        }
77    }
78}
79
80impl SandboxKind {
81    pub fn is_supported(self) -> bool {
82        if_compiler_is_supported! {
83            {
84                match self {
85                    SandboxKind::Linux => cfg!(target_os = "linux"),
86                    SandboxKind::Generic => true
87                }
88            } else {
89                false
90            }
91        }
92    }
93}
94
95#[derive(Clone)]
96pub struct Config {
97    pub(crate) backend: Option<BackendKind>,
98    pub(crate) sandbox: Option<SandboxKind>,
99    pub(crate) trace_execution: bool,
100    pub(crate) allow_insecure: bool,
101    pub(crate) worker_count: usize,
102}
103
104impl Default for Config {
105    fn default() -> Self {
106        Self::new()
107    }
108}
109
110fn env_bool(name: &str) -> Result<Option<bool>, Error> {
111    if let Some(value) = std::env::var_os(name) {
112        if value == "1" || value == "true" {
113            Ok(Some(true))
114        } else if value == "0" || value == "false" {
115            Ok(Some(false))
116        } else {
117            bail!("invalid value of {name}; must be either '1' or '0'")
118        }
119    } else {
120        Ok(None)
121    }
122}
123
124impl Config {
125    /// Creates a new default configuration.
126    pub fn new() -> Self {
127        Config {
128            backend: None,
129            sandbox: None,
130            trace_execution: false,
131            allow_insecure: false,
132            worker_count: 2,
133        }
134    }
135
136    /// Creates a new default configuration and seeds it from the environment variables.
137    pub fn from_env() -> Result<Self, Error> {
138        let mut config = Self::new();
139        if let Some(value) = std::env::var_os("POLKAVM_BACKEND") {
140            config.backend = BackendKind::from_os_str(&value)?;
141        }
142
143        if let Some(value) = std::env::var_os("POLKAVM_SANDBOX") {
144            config.sandbox = SandboxKind::from_os_str(&value)?;
145        }
146
147        if let Some(value) = env_bool("POLKAVM_TRACE_EXECUTION")? {
148            config.trace_execution = value;
149        }
150
151        if let Some(value) = env_bool("POLKAVM_ALLOW_INSECURE")? {
152            config.allow_insecure = value;
153        }
154
155        Ok(config)
156    }
157
158    /// Forces the use of a given backend.
159    ///
160    /// Default: `None` (automatically pick the best available backend)
161    ///
162    /// Corresponding environment variable: `POLKAVM_BACKEND` (`auto`, `compiler`, `interpreter`)
163    pub fn set_backend(&mut self, backend: Option<BackendKind>) -> &mut Self {
164        self.backend = backend;
165        self
166    }
167
168    /// Gets the currently set backend, if any.
169    pub fn backend(&self) -> Option<BackendKind> {
170        self.backend
171    }
172
173    /// Forces the use of a given sandbox.
174    ///
175    /// Default: `None` (automatically pick the best available sandbox)
176    ///
177    /// Corresponding environment variable: `POLKAVM_SANDBOX` (`auto`, `linux`, `generic`)
178    pub fn set_sandbox(&mut self, sandbox: Option<SandboxKind>) -> &mut Self {
179        self.sandbox = sandbox;
180        self
181    }
182
183    /// Gets the currently set sandbox, if any.
184    pub fn sandbox(&self) -> Option<SandboxKind> {
185        self.sandbox
186    }
187
188    /// Enables execution tracing.
189    ///
190    /// **Requires `set_allow_insecure` to be `true`.**
191    ///
192    /// Default: `false`
193    ///
194    /// Corresponding environment variable: `POLKAVM_TRACE_EXECUTION` (`true`, `false`)
195    pub fn set_trace_execution(&mut self, value: bool) -> &mut Self {
196        self.trace_execution = value;
197        self
198    }
199
200    /// Returns whether the execution tracing is enabled.
201    pub fn trace_execution(&self) -> bool {
202        self.trace_execution
203    }
204
205    /// Enabling this makes it possible to enable other settings
206    /// which can introduce unsafety or break determinism.
207    ///
208    /// Should only be used for debugging purposes and *never* enabled by default in production.
209    ///
210    /// Default: `false`
211    ///
212    /// Corresponding environment variable: `POLKAVM_ALLOW_INSECURE` (`true`, `false`)
213    pub fn set_allow_insecure(&mut self, value: bool) -> &mut Self {
214        self.allow_insecure = value;
215        self
216    }
217
218    /// Sets the number of worker sandboxes that will be permanently kept alive by the engine.
219    ///
220    /// This doesn't limit the number of instances that can be instantiated at the same time;
221    /// it will just tell the engine how many sandboxes should be cached between instantiations.
222    ///
223    /// For the Linux sandbox this will decide how many worker processes are kept alive.
224    ///
225    /// This only has an effect when using a recompiler. For the interpreter this setting will be ignored.
226    ///
227    /// Default: `2`
228    pub fn set_worker_count(&mut self, value: usize) -> &mut Self {
229        self.worker_count = value;
230        self
231    }
232}
233
234/// The type of gas metering.
235#[derive(Copy, Clone, PartialEq, Eq, Debug)]
236pub enum GasMeteringKind {
237    /// Synchronous gas metering. This will immediately abort the execution if we run out of gas.
238    Sync,
239    /// Asynchronous gas metering. Has a lower performance overhead compared to synchronous gas metering,
240    /// but will only periodically and asynchronously check whether we still have gas remaining while
241    /// the program is running.
242    ///
243    /// With asynchronous gas metering the program can run slightly longer than it would otherwise,
244    /// and the exact point *when* it is interrupted is not deterministic, but whether the computation
245    /// as a whole finishes under a given gas limit will still be strictly enforced and deterministic.
246    ///
247    /// This is only a hint, and the VM might still fall back to using synchronous gas metering
248    /// if asynchronous metering is not available.
249    Async,
250}
251
252/// The configuration for a module.
253#[derive(Clone)]
254pub struct ModuleConfig {
255    pub(crate) page_size: u32,
256    pub(crate) gas_metering: Option<GasMeteringKind>,
257}
258
259impl Default for ModuleConfig {
260    fn default() -> Self {
261        Self::new()
262    }
263}
264
265impl ModuleConfig {
266    /// Creates a new default module configuration.
267    pub fn new() -> Self {
268        ModuleConfig {
269            page_size: 0x4000,
270            gas_metering: None,
271        }
272    }
273
274    /// Sets the page size used for the module.
275    ///
276    /// Default: `16384` (16k)
277    pub fn set_page_size(&mut self, page_size: u32) -> &mut Self {
278        self.page_size = page_size;
279        self
280    }
281
282    /// Sets the type of gas metering to enable for this module.
283    ///
284    /// Default: `None`
285    pub fn set_gas_metering(&mut self, kind: Option<GasMeteringKind>) -> &mut Self {
286        self.gas_metering = kind;
287        self
288    }
289}