Skip to main content

rings_node/
error.rs

1//! A bunch of wrap errors.
2use rings_core::dht::Did;
3
4use crate::onion::OnionProxyTargetError;
5use crate::onion::OnionRouteError;
6use crate::prelude::rings_core;
7
8/// Bounded onion/relay queue whose admission failed.
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub enum OnionQueueKind {
11    /// Terminal relay-control sends.
12    RelayControl,
13    /// Onion data-plane sends.
14    CircuitData,
15}
16
17impl OnionQueueKind {
18    /// Build the closed admission error for this queue kind.
19    pub(crate) const fn admission(self, peer: Did, reason: OnionQueueAdmissionReason) -> Error {
20        Error::OnionQueueAdmission {
21            queue: self,
22            peer,
23            reason,
24        }
25    }
26}
27
28/// Algebraic reason a bounded onion/relay queue rejected one item.
29#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30pub enum OnionQueueAdmissionReason {
31    /// The queue-wide bound was reached.
32    GlobalFull,
33    /// One peer reached its isolated share.
34    PeerFull,
35    /// A resource counter could not represent its successor.
36    CounterOverflow,
37}
38
39/// A wrap `Result` contains custom errors.
40pub type Result<T> = std::result::Result<T, Error>;
41
42/// Errors enum mapping global custom errors.
43/// The error type can be expressed in decimal, where the high decs represent
44/// the error category and the low decs represent the error type.
45#[derive(Debug, thiserror::Error)]
46#[non_exhaustive]
47#[repr(u32)]
48pub enum Error {
49    /// Connecting to a remote JSON-RPC server failed.
50    #[error("Connect remote rpc server failed: {0}.")]
51    RemoteRpcError(String) = 100,
52    /// A remote JSON-RPC error did not match a known local variant.
53    #[error("Unknown rpc error.")]
54    UnknownRpcError = 101,
55    /// The internal JSON-RPC dispatcher returned an error.
56    #[error("Internal rpc services error: {0}.")]
57    InternalRpcError(#[from] jsonrpc_core::Error) = 102,
58    /// UUID parsing or formatting failed.
59    #[error("Uuid error: {0}")]
60    UuidError(#[from] uuid::Error) = 103,
61    /// The requested connection does not exist in the local swarm.
62    #[error("Connection not found.")]
63    ConnectionNotFound = 203,
64    /// Opening a new connection through the core swarm failed.
65    #[error("Create connection error: {0}.")]
66    NewConnectionError(rings_core::error::Error) = 204,
67    /// Closing an existing connection through the core swarm failed.
68    #[error("Close connection error: {0}.")]
69    CloseConnectionError(rings_core::error::Error) = 205,
70    /// The supplied connection identifier is malformed or unknown.
71    #[error("Invalid connection id.")]
72    InvalidConnectionId = 206,
73    /// Creating WebRTC offer data failed.
74    #[error("Create offer info failed: {0}.")]
75    CreateOffer(rings_core::error::Error) = 207,
76    /// Creating WebRTC answer data failed.
77    #[error("Answer offer info failed: {0}.")]
78    AnswerOffer(rings_core::error::Error) = 208,
79    /// Accepting WebRTC answer data failed.
80    #[error("Accept answer info failed: {0}.")]
81    AcceptAnswer(rings_core::error::Error) = 209,
82    /// Decoding structured data failed.
83    #[error("Decode error.")]
84    DecodeError = 300,
85    /// Encoding structured data failed.
86    #[error("Encode error.")]
87    EncodeError = 301,
88    /// Compiling a WASM artifact failed.
89    #[error("WASM compile error: {0}")]
90    WasmCompileError(String) = 400,
91    /// Acquiring the WASM backend-message lock failed.
92    #[error("BackendMessage RwLock Error")]
93    WasmBackendMessageRwLockError = 401,
94    /// Instantiating a WASM module failed.
95    #[error("WASM instantiation error.")]
96    WasmInstantiationError = 402,
97    /// Resolving a required WASM export failed.
98    #[error("WASM export error.")]
99    WasmExportError = 403,
100    /// Executing WASM code returned a runtime error.
101    #[error("WASM runtime error: {0}")]
102    WasmRuntimeError(String) = 404,
103    /// Acquiring the WASM global memory lock failed.
104    #[error("WASM global memory mutex error.")]
105    WasmGlobalMemoryLockError = 405,
106    /// Loading a WASM input file failed.
107    #[error("WASM failed to load file.")]
108    WasmFailedToLoadFile = 406,
109    /// Parsing or validating a DID failed.
110    #[error("Invalid did: {0}")]
111    InvalidDid(String) = 500,
112    /// The requested JSON-RPC method is not supported.
113    #[error("Invalid method.")]
114    InvalidMethod = 501,
115    /// A core internal operation failed.
116    #[error("Internal error: {0}.")]
117    InternalError(rings_core::error::Error) = 502,
118    /// The caller is not authorized to perform the requested action.
119    #[error("No Permission")]
120    NoPermission = 504,
121    /// Connecting to a peer failed.
122    #[error("Connect error, {0}")]
123    ConnectError(rings_core::error::Error) = 600,
124    /// Sending a peer message failed.
125    #[error("Send message error: {0}")]
126    SendMessage(rings_core::error::Error) = 601,
127    /// Applying a DHT entry action failed.
128    #[error("entry action error: {0}")]
129    EntryError(rings_core::error::Error) = 603,
130    /// Registering a service descriptor failed.
131    #[error("service register action error: {0}")]
132    ServiceRegisterError(rings_core::error::Error) = 604,
133    /// JavaScript host code returned an error.
134    #[error("JsError: {0}")]
135    JsError(String) = 700,
136    /// A protocol message failed validation.
137    #[error("Invalid message")]
138    InvalidMessage = 800,
139    /// An HTTP proxy request failed validation.
140    #[error("Invalid http request: {0}")]
141    HttpRequestError(String) = 801,
142    /// Input data failed semantic validation.
143    #[error("Invalid data")]
144    InvalidData = 802,
145    /// A service name or service descriptor is invalid.
146    #[error("Invalid service")]
147    InvalidService = 803,
148    /// A network address is invalid.
149    #[error("Invalid address")]
150    InvalidAddress = 804,
151    /// Authentication payload data is invalid.
152    #[error("Invalid auth data")]
153    InvalidAuthData = 805,
154    /// Request headers are missing required values or contain invalid values.
155    #[error("invalid headers")]
156    InvalidHeaders = 806,
157    /// Persistent or cache storage returned an error.
158    #[error("Storage Error: {0}")]
159    Storage(rings_core::error::Error) = 807,
160    /// Swarm state management returned an error.
161    #[error("Swarm Error: {0}")]
162    Swarm(rings_core::error::Error) = 808,
163    /// The requested logging level is not supported.
164    #[error("Invalid logging level: {0}")]
165    InvalidLoggingLevel(String) = 809,
166    /// The configured WebRTC UDP port range is invalid.
167    #[error("Invalid WebRTC UDP port range: {0}")]
168    InvalidWebrtcUdpPortRange(#[from] rings_transport::webrtc_config::WebrtcUdpPortRangeError) =
169        810,
170    /// Only one side of the WebRTC UDP port range was configured.
171    #[error("Both webrtc_udp_port_min and webrtc_udp_port_max must be set together: min={min:?}, max={max:?}")]
172    IncompleteWebrtcUdpPortRange {
173        /// Configured lower UDP port bound.
174        min: Option<u16>,
175        /// Configured upper UDP port bound.
176        max: Option<u16>,
177    } = 811,
178    /// The node configuration is structurally invalid.
179    #[error("Invalid configuration: {0}")]
180    InvalidConfig(String) = 812,
181    /// Opening browser IndexedDB-backed provider storage failed.
182    #[error("Open browser storage \"{name}\" failed: {source}")]
183    BrowserStorageOpen {
184        /// IndexedDB database and object-store name requested by the browser provider.
185        name: String,
186        /// Storage backend error returned while opening the database.
187        source: rings_core::error::Error,
188    } = 814,
189    /// A periodic registration task observed a cooperative stop request.
190    #[error("registration task stopped")]
191    RegistrationStopped = 815,
192    /// Loading or explicitly flushing local peer measurements failed.
193    #[error("Measurement runtime error: {0}")]
194    MeasurementRuntime(#[from] crate::measure::MeasureRuntimeError) = 816,
195    /// Creating a file on disk failed.
196    #[error("Create File Error: {0}")]
197    CreateFileError(String) = 900,
198    /// Opening a file on disk failed.
199    #[error("Open File Error: {0}")]
200    OpenFileError(String) = 901,
201    /// Acquiring a synchronization lock failed.
202    #[error("Acquire lock failed")]
203    Lock = 902,
204    /// The process home directory could not be resolved.
205    #[error("Cannot find home directory")]
206    HomeDirError = 903,
207    /// The parent directory of a path could not be resolved.
208    #[error("Cannot find parent directory")]
209    ParentDirError = 904,
210    /// A filesystem path cannot be represented as UTF-8.
211    #[error("Path is not valid UTF-8: {0}")]
212    PathUtf8Error(String) = 905,
213    /// JSON serialization or deserialization failed.
214    #[error("Serde json error: {0}")]
215    SerdeJsonError(#[from] serde_json::Error) = 1000,
216    /// YAML serialization or deserialization failed.
217    #[error("Serde yaml error: {0}")]
218    SerdeYamlError(#[from] serde_yaml::Error) = 1001,
219    /// Cryptographic verification failed.
220    #[error("verify error: {0}")]
221    VerifyError(String) = 1002,
222    /// A rings-core operation returned an error.
223    #[error("Core error: {0}")]
224    CoreError(#[from] rings_core::error::Error) = 1102,
225    /// An external signer returned an error.
226    #[error("External singer error: {0}")]
227    ExternalError(String) = 1202,
228    /// An FFI string contained an interior nul byte.
229    #[error("An error indicating that an interior nul byte was found: {0}")]
230    FFINulError(#[from] std::ffi::NulError) = 1203,
231    /// Converting an FFI C string to UTF-8 failed.
232    #[error("Failed to convert CStr to String: {0}")]
233    FFICStrError(#[from] std::str::Utf8Error) = 1204,
234    /// An FFI pointer argument was null.
235    #[error("An error indicating that a ptr is null")]
236    FFINulPtrError = 1205,
237    /// Converting owned FFI bytes to UTF-8 failed.
238    #[error("Failed to convert bytes to String: {0}")]
239    FFIFromUtf8Error(#[from] std::string::FromUtf8Error) = 1206,
240    /// A protocol backend returned an error.
241    #[error("Extend Backend Error {0}")]
242    BackendError(String) = 1501,
243    /// An extension runtime returned an error.
244    #[error("Extension error: {0}")]
245    ExtensionError(String) = 1502,
246    /// An owned extension task ended before publishing its result.
247    #[error("Detached extension task closed before publishing its result")]
248    DetachedExtensionTaskClosed = 1503,
249    /// Onion route construction or validation failed.
250    #[error("Onion route error: {0}")]
251    OnionRouteError(OnionRouteError) = 1601,
252    /// Onion proxy I/O failed.
253    #[error("Onion proxy IO error: {0}")]
254    OnionProxyIoError(String) = 1602,
255    /// A local onion proxy request did not complete before its deadline.
256    #[error("Onion proxy request timed out")]
257    OnionProxyRequestTimedOut = 1603,
258    /// A bounded onion or relay send queue rejected one item.
259    #[error("{queue:?} queue rejected peer {peer}: {reason:?}")]
260    OnionQueueAdmission {
261        /// Queue whose bound was reached.
262        queue: OnionQueueKind,
263        /// Peer whose item was rejected.
264        peer: Did,
265        /// Exact admission relation that failed.
266        reason: OnionQueueAdmissionReason,
267    } = 1604,
268    /// An onion proxy authority failed closed parsing.
269    #[error("Invalid onion proxy target: {0}")]
270    OnionProxyTarget(#[from] OnionProxyTargetError) = 1605,
271    /// Runtime DNS resolution of an admitted onion target failed.
272    #[error("Failed to resolve onion target {authority:?}: {source}")]
273    OnionTargetResolve {
274        /// Canonical target authority.
275        authority: String,
276        /// Resolver I/O failure.
277        source: std::io::Error,
278    } = 1606,
279    /// Runtime DNS resolution returned no addresses.
280    #[error("Onion target {authority:?} resolved no addresses")]
281    OnionTargetResolvedEmpty {
282        /// Canonical target authority.
283        authority: String,
284    } = 1607,
285}
286
287impl Error {
288    fn discriminant(&self) -> u32 {
289        // SAFETY: Because `Self` is marked `repr(u32)`, its layout is a `repr(C)` `union`
290        // between `repr(C)` structs, each of which has the `u32` discriminant as its first
291        // field, so we can read the discriminant without offsetting the pointer.
292        // This code is copy from
293        // ref: https://doc.rust-lang.org/std/mem/fn.discriminant.html
294        // And we modify it from [u8] to [u32], this is work because
295        // repr(C) is equivalent to one of repr(u*) (see the next section) for
296        // fieldless enums.
297        // ref: https://doc.rust-lang.org/nomicon/other-reprs.html
298        unsafe { *<*const _>::from(self).cast::<u32>() }
299    }
300
301    /// Returns the stable numeric error code for JSON-RPC error conversion.
302    pub fn code(&self) -> u32 {
303        self.discriminant()
304    }
305}
306
307impl From<Error> for jsonrpc_core::Error {
308    fn from(e: Error) -> Self {
309        Self {
310            code: jsonrpc_core::ErrorCode::ServerError(e.code().into()),
311            message: e.to_string(),
312            data: None,
313        }
314    }
315}
316
317impl From<rings_rpc::error::Error> for Error {
318    fn from(e: rings_rpc::error::Error) -> Self {
319        match e {
320            rings_rpc::error::Error::InvalidMethod => Error::InvalidMethod,
321            rings_rpc::error::Error::RpcError(v) => Error::RemoteRpcError(v.to_string()),
322            rings_rpc::error::Error::InvalidSignature => Error::InvalidData,
323            rings_rpc::error::Error::InvalidHeaders => Error::InvalidHeaders,
324            _ => Error::UnknownRpcError,
325        }
326    }
327}
328
329#[cfg(all(feature = "browser", target_family = "wasm"))]
330impl From<Error> for wasm_bindgen::JsValue {
331    fn from(err: Error) -> Self {
332        wasm_bindgen::JsValue::from_str(&err.to_string())
333    }
334}
335
336#[cfg(test)]
337mod tests {
338    use super::*;
339    #[test]
340    fn test_error_code() {
341        let err = Error::RemoteRpcError("Test".to_string());
342        assert_eq!(err.code(), 100);
343    }
344}