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>;