1use bitcoin::secp256k1;
2use lightning_invoice::ParseOrSemanticError;
3use std::io;
4use thiserror::Error;
5use tonic::codegen::http::uri::InvalidUri;
6
7pub use self::crypto::CryptoError;
9pub use self::internal::InternalError;
10pub use self::io_error::IoError;
11pub use self::network::NetworkError;
12pub use self::transaction::TransactionError;
13pub use self::validation::ValidationError;
14pub use self::wallet::WalletError;
15
16#[derive(Debug, Error)]
18pub enum SparkSdkError {
19 #[error(transparent)]
20 Network(#[from] NetworkError),
21
22 #[error(transparent)]
23 Wallet(#[from] WalletError),
24
25 #[error(transparent)]
26 Crypto(#[from] CryptoError),
27
28 #[error(transparent)]
29 Validation(#[from] ValidationError),
30
31 #[error(transparent)]
32 Transaction(#[from] TransactionError),
33
34 #[error(transparent)]
35 Io(#[from] IoError),
36
37 #[error(transparent)]
38 Internal(#[from] InternalError),
39
40 #[error("General error: {0}")]
41 General(String),
42}
43
44pub mod network {
46 use super::*;
47
48 #[derive(Debug, Error)]
49 pub enum NetworkError {
50 #[error("Transport error: {0}")]
51 Transport(#[from] tonic::transport::Error),
52
53 #[error("HTTP error: {0}")]
54 Http(#[from] reqwest::Error),
55
56 #[error("Status error: {0}")]
57 Status(#[from] tonic::Status),
58
59 #[error("Invalid URI: {0}")]
60 InvalidUri(#[from] InvalidUri),
61
62 #[error("Invalid URL: {url}")]
63 InvalidUrl {
64 url: String,
65 details: Option<String>,
66 },
67
68 #[error("RPC connection error with endpoint: {uri}")]
69 RpcConnection {
70 uri: String,
71 details: Option<String>,
72 },
73
74 #[error("Bitcoin RPC error: {0}")]
75 BitcoinRpc(String),
76
77 #[error("Invalid GraphQL operation: {0}")]
78 InvalidGraphQLOperation(String),
79
80 #[error("GraphQL error with query: {0}")]
81 GraphQL(String),
82
83 #[error("GraphQL request failed: {status_code}")]
84 GraphQLRequestFailed { status_code: u16 },
85
86 #[error("Faucet request failed with status: {status_code}")]
87 FaucetRequestFailed { status_code: u16 },
88
89 #[error("Invalid response from server")]
90 InvalidResponse,
91 }
92}
93
94pub mod wallet {
96 use super::*;
97
98 #[derive(Debug, Error)]
99 pub enum WalletError {
100 #[error("No leaves found for wallet")]
101 NoLeavesFound,
102
103 #[error("No leaf with exact amount")]
104 NoLeafWithExactAmount,
105
106 #[error("Leaf selection failed: insufficient funds")]
107 LeafSelectionInsufficientFunds,
108
109 #[error("Leaf selection failed: no suitable leaves available")]
110 LeafSelectionNoSuitableLeaves,
111
112 #[error("Leaf not found with id: {0}")]
113 LeafNotFound(String),
114
115 #[error("Leaf is not Bitcoin type: {leaf_id}")]
116 LeafIsNotBitcoin { leaf_id: String },
117
118 #[error("Leaf already exists in wallet: {leaf_id}")]
119 LeafAlreadyExistsInWallet { leaf_id: String },
120
121 #[error("Leaf not available for use: {leaf_id}")]
122 LeafNotAvailableForUse { leaf_id: String },
123
124 #[error("Leaf not using expected lock. Expected: {expected}, Found: {actual}")]
125 LeafNotUsingExpectedLock { expected: String, actual: String },
126
127 #[error("Leaf not found in wallet: {leaf_id}")]
128 LeafNotFoundInWallet { leaf_id: String },
129
130 #[error("Invalid account: {account_id}")]
131 InvalidAccount { account_id: String },
132
133 #[error("Cooperative exit failed: {reason}")]
134 CooperativeExit { reason: String },
135 }
136}
137
138pub mod crypto {
140 use super::*;
141
142 #[derive(Debug, Error)]
143 pub enum CryptoError {
144 #[error("Signature error: {0}")]
145 Secp256k1(#[from] secp256k1::Error),
146
147 #[error("FROST signing failed with job id: {job_id}")]
148 FrostSigning { job_id: String },
149
150 #[error("FROST aggregation failed")]
151 FrostAggregation,
152
153 #[error("Invalid hash: {hash}")]
154 InvalidHash { hash: String },
155
156 #[error("Decryption failed")]
157 Decryption,
158
159 #[error("Secret key not found for public key: {public_key}")]
160 SecretKeyNotFound { public_key: String },
161
162 #[error("Missing keyshare information")]
163 MissingKeyshareInfo,
164
165 #[error("Could not find FROST signature with job id: {job_id}")]
166 FrostSignatureNotFound { job_id: String },
167
168 #[error("Invalid key type: {key_type}")]
169 InvalidKeyType { key_type: String },
170
171 #[error("Invalid seed")]
172 InvalidSeed,
173
174 #[error("Child key derivation failed with path: {derivation_path}")]
175 ChildKeyDerivationError { derivation_path: String },
176
177 #[error("Missing root node signature shares")]
178 MissingRootNodeSignatureShares,
179
180 #[error("Invalid crypto input: {field}")]
181 InvalidInput { field: String },
182 }
183}
184
185pub mod validation {
187 use super::*;
188
189 #[derive(Debug, Error)]
190 pub enum ValidationError {
191 #[error("Invalid configuration: {parameter}")]
192 Config { parameter: String },
193
194 #[error("Invalid input: {field}")]
195 InvalidInput { field: String },
196
197 #[error("Invalid argument: {argument}")]
198 InvalidArgument { argument: String },
199
200 #[error("Invalid address: {address}")]
201 InvalidAddress { address: String },
202
203 #[error("Deposit address validation failed: {address}")]
204 DepositAddressValidationFailed { address: String },
205
206 #[error("Deposit transaction not found in network: {txid}")]
207 DepositTransactionNotFoundInNetwork { txid: String },
208
209 #[error("Invalid keyshare config with id: {config_id}")]
210 InvalidKeyshareConfig { config_id: String },
211
212 #[error("Invalid sequence number: {sequence}")]
213 InvalidSequence { sequence: u64 },
214
215 #[error("Invalid UUID: {0}")]
216 InvalidUuid(#[from] uuid::Error),
217
218 #[error("Invalid Bolt11 invoice: {0}")]
219 InvalidBolt11Invoice(String),
220
221 #[cfg(feature = "integration-tests")]
222 #[error("Transfer claim did not update balance")]
223 TransferClaimDidNotUpdateBalance,
224 }
225}
226
227pub mod transaction {
229 use super::*;
230
231 #[derive(Debug, Error)]
232 pub enum TransactionError {
233 #[error("Transfer failed: {reason}")]
234 Transfer { reason: String },
235
236 #[error("Invalid bitcoin transaction: {0}")]
237 InvalidBitcoinTransaction(String),
238
239 #[error("Invalid token transaction: {0}")]
240 InvalidTokenTransaction(String),
241
242 #[error("Invalid deposit transaction: {0}")]
243 InvalidDepositTx(String),
244 }
245}
246
247pub mod io_error {
249
250 use super::*;
251
252 #[derive(Debug, Error)]
253 pub enum IoError {
254 #[error("IO error: {0}")]
255 Io(#[from] io::Error),
256
257 #[error("JSON serialization error: {0}")]
258 SerdeJson(#[from] serde_json::Error),
259
260 #[error("Hex decoding error: {0}")]
261 Decoding(#[from] hex::FromHexError),
262
263 #[error("Bolt11 invoice decoding error: {0}")]
264 Bolt11InvoiceDecoding(String),
265 }
266}
267
268pub mod internal {
270 use super::*;
271
272 #[derive(Debug, Error)]
273 pub enum InternalError {
274 #[error("Initialization failed: {component}")]
275 Initialization { component: String },
276
277 #[error("Tree creation failed: {reason}")]
278 TreeCreation { reason: String },
279
280 #[error("Test database query error: {query}")]
281 TestDatabaseQuery { query: String },
282
283 #[error("Async task failed: {task}")]
284 AsyncTask { task: String },
285 }
286}
287
288impl From<String> for SparkSdkError {
290 fn from(s: String) -> Self {
291 Self::General(s)
292 }
293}
294
295impl From<&str> for SparkSdkError {
296 fn from(s: &str) -> Self {
297 Self::General(s.to_string())
298 }
299}
300
301impl From<tonic::transport::Error> for SparkSdkError {
303 fn from(err: tonic::transport::Error) -> Self {
304 Self::Network(NetworkError::Transport(err))
305 }
306}
307
308impl From<secp256k1::Error> for SparkSdkError {
310 fn from(err: secp256k1::Error) -> Self {
311 Self::Crypto(CryptoError::Secp256k1(err))
312 }
313}
314
315impl From<ParseOrSemanticError> for IoError {
317 fn from(err: ParseOrSemanticError) -> Self {
318 Self::Bolt11InvoiceDecoding(err.to_string())
319 }
320}