Skip to main content

fedimint_client/
error.rs

1//! Error types of the client.
2//!
3//! The types the client's *modules* also need are defined in
4//! [`fedimint_client_module::error`] and re-exported here, so this module is
5//! the single place to look.
6
7use std::sync::Arc;
8
9use fedimint_api_client::api::{ClientConfigDownloadError, FederationError};
10pub use fedimint_client_module::error::*;
11use fedimint_core::core::{ModuleInstanceId, ModuleKind};
12use fedimint_core::db::{DatabaseError, DbMigrationError};
13use fedimint_core::encoding::DecodeError;
14pub use fedimint_eventlog::EventHandlerError;
15use thiserror::Error;
16
17/// A failure to read or write the client's stored root secret.
18#[derive(Debug, Error)]
19#[non_exhaustive]
20pub enum ClientSecretError {
21    /// The database already holds a secret, which is never overwritten.
22    #[error("An encoded client secret already exists and cannot be overwritten")]
23    AlreadyExists,
24
25    /// The database holds no secret.
26    #[error("No encoded client secret is present in the database")]
27    NotPresent,
28
29    /// The stored secret is not a valid encoding of the requested type.
30    #[error("The stored client secret could not be decoded")]
31    Decode(#[from] DecodeError),
32}
33
34/// A failure to open, join or recover a client.
35#[derive(Debug, Error)]
36#[non_exhaustive]
37pub enum ClientBuildError {
38    /// The database was never joined to a federation.
39    #[error("The client database is not initialized")]
40    DatabaseNotInitialized,
41
42    /// The database already belongs to a federation and cannot be joined
43    /// again.
44    #[error("The client database is already initialized")]
45    DatabaseAlreadyInitialized,
46
47    /// The secret does not match the one the database was created with.
48    #[error("The secret does not match the one this database was created with")]
49    SecretMismatch,
50
51    /// The federation's client config could not be downloaded.
52    #[error("Failed to download the client config")]
53    ConfigDownload(#[source] Box<ClientConfigDownloadError>),
54
55    /// The stored client config could not be decoded with the modules this
56    /// client was built with.
57    #[error("Failed to decode the client config")]
58    ConfigDecode(#[from] DecodeError),
59
60    /// A database migration failed.
61    #[error("Failed to migrate the client database")]
62    Migration(#[from] DbMigrationError),
63
64    /// A module could not prepare its recovery.
65    #[error("Module {instance_id} ({kind}) failed to prepare its recovery")]
66    ModuleRecoveryPrepare {
67        /// The kind of the module that failed.
68        kind: ModuleKind,
69        /// The instance of the module that failed.
70        instance_id: ModuleInstanceId,
71        /// The failure the module reported.
72        #[source]
73        source: ClientModuleError,
74    },
75
76    /// A module could not be initialized.
77    #[error("Module {instance_id} ({kind}) failed to initialize")]
78    ModuleInit {
79        /// The kind of the module that failed.
80        kind: ModuleKind,
81        /// The instance of the module that failed.
82        instance_id: ModuleInstanceId,
83        /// The failure the module reported.
84        #[source]
85        source: ClientModuleError,
86    },
87
88    /// The database write failed.
89    #[error("Database error")]
90    Database(#[from] DatabaseError),
91
92    /// The client handle was already shut down and cannot be restarted.
93    #[error("The client is already stopped")]
94    AlreadyStopped,
95}
96
97impl From<ClientConfigDownloadError> for ClientBuildError {
98    fn from(source: ClientConfigDownloadError) -> Self {
99        Self::ConfigDownload(Box::new(source))
100    }
101}
102
103/// A failure of a request to [`crate::Client::handle_global_rpc`].
104///
105/// Every variant but [`Self::UnknownMethod`] carries the error of the
106/// operation the request ran, whole: its `Display` and `source()` are that
107/// error's.
108#[derive(Debug, Error)]
109#[non_exhaustive]
110pub enum GlobalRpcError {
111    /// The request's parameters do not fit the method, or its response could
112    /// not be serialized.
113    #[error(transparent)]
114    Json(#[from] serde_json::Error),
115
116    /// The federation could not report its session count.
117    #[error(transparent)]
118    Federation(#[from] FederationError),
119
120    /// A module's recovery failed, or the client stopped, while the request
121    /// waited for all recoveries.
122    #[error(transparent)]
123    Recovery(#[from] RecoveryError),
124
125    /// The backup could not be made or uploaded.
126    #[error(transparent)]
127    Backup(#[from] BackupError),
128
129    /// The client has no method of this name.
130    #[error("Unknown method: {method}")]
131    UnknownMethod {
132        /// The method the request named.
133        method: String,
134    },
135}
136
137/// A failure to create, encrypt, upload or read back a client backup.
138#[derive(Debug, Error)]
139#[non_exhaustive]
140pub enum BackupError {
141    /// A module is still recovering, so its state is not backed up yet.
142    #[error("Cannot back up while a module recovery is still running")]
143    PendingRecoveries,
144
145    /// The federation could not be reached.
146    #[error("The federation could not be reached")]
147    Federation(#[source] Box<FederationError>),
148
149    /// A module failed to produce its part of the backup.
150    #[error("Module {instance_id} failed to produce its backup")]
151    Module {
152        /// The module that failed.
153        instance_id: ModuleInstanceId,
154        /// The failure the module reported.
155        #[source]
156        source: ClientModuleError,
157    },
158
159    /// The encrypted backup is larger than the federation stores.
160    #[error("The backup payload is {size} bytes, over the limit of {max}")]
161    TooLarge {
162        /// The size of the encrypted backup.
163        size: usize,
164        /// The largest payload the federation stores.
165        max: usize,
166    },
167
168    /// The backup could not be encrypted or decrypted.
169    #[error("The backup could not be encrypted or decrypted")]
170    Encryption(#[source] Box<dyn std::error::Error + Send + Sync>),
171
172    /// A downloaded backup could not be decoded.
173    #[error("The backup could not be decoded")]
174    Decode(#[from] DecodeError),
175}
176
177impl From<FederationError> for BackupError {
178    fn from(source: FederationError) -> Self {
179        Self::Federation(Box::new(source))
180    }
181}
182
183/// A failure to wait for a module recovery to finish.
184#[derive(Debug, Error)]
185#[non_exhaustive]
186pub enum RecoveryError {
187    /// A module's recovery gave up.
188    ///
189    /// The failure is in-memory only and is never persisted: reopening the
190    /// client retries the recovery from its last persisted progress.
191    #[error("Recovery of module {module_instance_id} failed")]
192    Failed {
193        /// The module whose recovery failed.
194        module_instance_id: ModuleInstanceId,
195        /// The failure the module reported, shared by everyone waiting on the
196        /// recovery.
197        #[source]
198        source: Arc<ClientModuleError>,
199    },
200
201    /// The client shut down before the recovery reached an outcome.
202    #[error("The client shut down before the recovery finished")]
203    ClientStopped,
204}