arb_stylus/
error.rs

1//! Error types raised by the Stylus runtime.
2//!
3//! All public APIs return [`StylusError`]. Host-function failures and
4//! module-lifecycle failures share one enum because both flow through the
5//! same surfaces (host functions trap; lifecycle errors revert the EVM
6//! frame) and callers benefit from a single match site.
7
8use thiserror::Error;
9use wasmer::MemoryAccessError;
10
11/// All failure modes raised by the Stylus runtime.
12#[derive(Error, Debug)]
13pub enum StylusError {
14    /// WASM linear-memory access (read/write) failed, typically because a
15    /// host function received an out-of-bounds pointer from the program.
16    #[error("memory access failed: {0}")]
17    Memory(#[from] MemoryAccessError),
18
19    /// A host function detected an unrecoverable internal condition (e.g.
20    /// the underlying journal returned an error during sload/sstore).
21    /// Surfaces from the WASM trap as `UserOutcome::Failure`.
22    #[error("internal stylus error: {0}")]
23    Internal(String),
24
25    /// The backing store failed while reading program/fragment data. This is an
26    /// infrastructure failure that must abort the block, not revert the call.
27    #[error("backing store failure: {0}")]
28    Backend(String),
29
30    /// A host function rejected its inputs on semantic grounds (e.g.
31    /// `emit_log` called in a static context, malformed topic data).
32    /// Surfaces as `UserOutcome::Failure`.
33    #[error("stylus logical error: {0}")]
34    Logical(String),
35
36    /// The program exhausted its ink budget mid-host-call. Surfaces as
37    /// `UserOutcome::OutOfInk`.
38    #[error("out of ink")]
39    OutOfInk,
40
41    /// The program returned voluntarily via `exit_early(status)`. A
42    /// non-zero status is reported as `UserOutcome::Revert`.
43    #[error("program exited with status {0}")]
44    Exit(u32),
45
46    /// Wasmer module compilation or serialization failed. Carries the
47    /// rendered wasmer error since the underlying type varies across
48    /// compilation backends.
49    #[error("wasm compile failed: {0}")]
50    Compile(String),
51
52    /// Wasmer instance creation failed (import binding, memory export, or
53    /// start function).
54    #[error("wasm instantiation failed: {0}")]
55    Instantiation(String),
56
57    /// `activate_program` failed at the prover-machine level. Wraps the
58    /// rendered error from the activation pipeline.
59    #[error("program activation failed: {0}")]
60    Activation(String),
61
62    /// The contract bytecode does not satisfy Stylus structural
63    /// requirements (e.g. missing discriminant or unsupported dictionary).
64    #[error("invalid stylus program: {0}")]
65    InvalidProgram(&'static str),
66
67    /// Brotli decompression of the Stylus payload failed.
68    #[error("brotli decompression failed: {0}")]
69    Decompression(String),
70
71    /// `TypedFunction::call` returned a non-trap error that could not be
72    /// downcast to [`StylusError`]. The original message is preserved.
73    #[error("wasm execution failed: {0}")]
74    Run(String),
75
76    /// A required wasmer global export was missing or had the wrong type.
77    #[error("missing or invalid wasm global: {0}")]
78    MissingGlobal(String),
79
80    /// The compile/runtime version requested for a Stylus program is not
81    /// supported by this build. The version is read from contract storage
82    /// and is therefore considered attacker-influenced — bad values must
83    /// surface as a typed error rather than panic.
84    #[error("unsupported Stylus dictionary version: {0}")]
85    UnsupportedDictionaryVersion(u16),
86}
87
88impl StylusError {
89    /// Construct an [`Internal`](Self::Internal) failure in an `Err(_)`.
90    pub fn internal<T>(error: impl Into<String>) -> Result<T, StylusError> {
91        Err(Self::Internal(error.into()))
92    }
93
94    /// Construct a [`Logical`](Self::Logical) failure in an `Err(_)`.
95    pub fn logical<T>(error: impl Into<String>) -> Result<T, StylusError> {
96        Err(Self::Logical(error.into()))
97    }
98
99    /// Construct an [`OutOfInk`](Self::OutOfInk) failure in an `Err(_)`.
100    pub fn out_of_ink<T>() -> Result<T, StylusError> {
101        Err(Self::OutOfInk)
102    }
103}
104
105impl From<std::io::Error> for StylusError {
106    fn from(err: std::io::Error) -> Self {
107        Self::Internal(err.to_string())
108    }
109}
110
111/// Result alias used by host functions whose only meaningful return is
112/// "success or trap".
113pub type MaybeEscape = Result<(), StylusError>;