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 fedimint_api_client::api::{ClientConfigDownloadError, FederationError};
8pub use fedimint_client_module::error::*;
9use fedimint_core::core::{ModuleInstanceId, ModuleKind};
10use fedimint_core::db::{DatabaseError, DbMigrationError};
11use fedimint_core::encoding::DecodeError;
12pub use fedimint_eventlog::EventHandlerError;
13use thiserror::Error;
14
15/// A failure to read or write the client's stored root secret.
16#[derive(Debug, Error)]
17#[non_exhaustive]
18pub enum ClientSecretError {
19    /// The database already holds a secret, which is never overwritten.
20    #[error("An encoded client secret already exists and cannot be overwritten")]
21    AlreadyExists,
22
23    /// The database holds no secret.
24    #[error("No encoded client secret is present in the database")]
25    NotPresent,
26
27    /// The stored secret is not a valid encoding of the requested type.
28    #[error("The stored client secret could not be decoded")]
29    Decode(#[from] DecodeError),
30}
31
32/// A failure to open, join or recover a client.
33#[derive(Debug, Error)]
34#[non_exhaustive]
35pub enum ClientBuildError {
36    /// The database was never joined to a federation.
37    #[error("The client database is not initialized")]
38    DatabaseNotInitialized,
39
40    /// The database already belongs to a federation and cannot be joined
41    /// again.
42    #[error("The client database is already initialized")]
43    DatabaseAlreadyInitialized,
44
45    /// The secret does not match the one the database was created with.
46    #[error("The secret does not match the one this database was created with")]
47    SecretMismatch,
48
49    /// The federation's client config could not be downloaded.
50    #[error("Failed to download the client config")]
51    ConfigDownload(#[source] Box<ClientConfigDownloadError>),
52
53    /// The stored client config could not be decoded with the modules this
54    /// client was built with.
55    #[error("Failed to decode the client config")]
56    ConfigDecode(#[from] DecodeError),
57
58    /// A database migration failed.
59    #[error("Failed to migrate the client database")]
60    Migration(#[from] DbMigrationError),
61
62    /// A module could not prepare its recovery.
63    // The boxed cause narrows to `ClientModuleError` in #8821 part E.
64    #[error("Module {instance_id} ({kind}) failed to prepare its recovery")]
65    ModuleRecoveryPrepare {
66        /// The kind of the module that failed.
67        kind: ModuleKind,
68        /// The instance of the module that failed.
69        instance_id: ModuleInstanceId,
70        /// The failure the module reported.
71        #[source]
72        source: Box<dyn std::error::Error + Send + Sync>,
73    },
74
75    /// A module could not be initialized.
76    // The boxed cause narrows to `ClientModuleError` in #8821 part E.
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: Box<dyn std::error::Error + Send + Sync>,
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 to create, encrypt, upload or read back a client backup.
104#[derive(Debug, Error)]
105#[non_exhaustive]
106pub enum BackupError {
107    /// A module is still recovering, so its state is not backed up yet.
108    #[error("Cannot back up while a module recovery is still running")]
109    PendingRecoveries,
110
111    /// The federation could not be reached.
112    #[error("The federation could not be reached")]
113    Federation(#[source] Box<FederationError>),
114
115    /// A module failed to produce its part of the backup.
116    // The boxed cause narrows to `ClientModuleError` in #8821 part E.
117    #[error("Module {instance_id} failed to produce its backup")]
118    Module {
119        /// The module that failed.
120        instance_id: ModuleInstanceId,
121        /// The failure the module reported.
122        #[source]
123        source: Box<dyn std::error::Error + Send + Sync>,
124    },
125
126    /// The encrypted backup is larger than the federation stores.
127    #[error("The backup payload is {size} bytes, over the limit of {max}")]
128    TooLarge {
129        /// The size of the encrypted backup.
130        size: usize,
131        /// The largest payload the federation stores.
132        max: usize,
133    },
134
135    /// The backup could not be encrypted or decrypted.
136    #[error("The backup could not be encrypted or decrypted")]
137    Encryption(#[source] Box<dyn std::error::Error + Send + Sync>),
138
139    /// A downloaded backup could not be decoded.
140    #[error("The backup could not be decoded")]
141    Decode(#[from] DecodeError),
142}
143
144impl From<FederationError> for BackupError {
145    fn from(source: FederationError) -> Self {
146        Self::Federation(Box::new(source))
147    }
148}
149
150/// A failure to wait for a module recovery to finish.
151#[derive(Debug, Error)]
152#[non_exhaustive]
153pub enum RecoveryError {
154    /// A module's recovery gave up.
155    ///
156    /// The failure is in-memory only and is never persisted: reopening the
157    /// client retries the recovery from its last persisted progress.
158    // `error` is the module's already-stringified failure; it becomes a typed
159    // `ClientModuleError` in #8821 part E.
160    #[error("Recovery of module {module_instance_id} failed: {error}")]
161    Failed {
162        /// The module whose recovery failed.
163        module_instance_id: ModuleInstanceId,
164        /// What the module reported.
165        error: String,
166    },
167
168    /// The client shut down before the recovery reached an outcome.
169    #[error("The client shut down before the recovery finished")]
170    ClientStopped,
171}
172
173#[cfg(feature = "uniffi")]
174impl From<RecoveryError> for fedimint_core::util::ffi::UniffiError {
175    fn from(e: RecoveryError) -> Self {
176        Self::General(e.to_string())
177    }
178}