1use diesel::result::DatabaseErrorKind;
2use thiserror::Error;
3use xmtp_common::ErrorCode;
4
5use crate::group_intent::GroupIntentError;
6
7use super::sql_key_store::{self, SqlKeyStoreError};
8use xmtp_common::{BoxDynError, RetryableError, retryable};
9use xmtp_proto::types::{Cursor, GroupId, InstallationId};
10
11pub struct Mls;
12
13#[derive(Debug, Error, ErrorCode)]
14pub enum StorageError {
15 #[error(
17 "This database predates the backend transition. Delete the database file and create a new client."
18 )]
19 PreTransitionDatabase,
20 #[error(
22 "This database uses an older self-hosted format. Keep a backup and create a new client database."
23 )]
24 OldStreamDatabase,
25 #[error(transparent)]
27 #[error_code(inherit)]
28 Stream(#[from] crate::stream_storage::StreamStorageError),
29 #[error(transparent)]
33 DieselConnect(#[from] diesel::ConnectionError),
34 #[error(transparent)]
38 DieselResult(#[from] diesel::result::Error),
39 #[error("Error migrating database {0}")]
43 MigrationError(#[from] BoxDynError),
44 #[error(transparent)]
48 NotFound(#[from] NotFound),
49 #[error(transparent)]
53 Duplicate(DuplicateItem),
54 #[error(transparent)]
58 OpenMlsStorage(#[from] SqlKeyStoreError),
59 #[error("failed to deserialize from db")]
63 DbDeserialize,
64 #[error("failed to serialize for db")]
68 DbSerialize,
69 #[error("required fields missing from stored db type {0}")]
73 Builder(#[from] derive_builder::UninitializedFieldError),
74 #[error(transparent)]
78 Platform(#[from] crate::database::PlatformStorageError),
79 #[error("decoding from database failed {}", _0)]
83 Prost(#[from] prost::DecodeError),
84 #[error(transparent)]
88 Conversion(#[from] xmtp_proto::ConversionError),
89 #[error(transparent)]
93 Connection(#[from] crate::ConnectionError),
94 #[error("HMAC key must be 42 bytes")]
98 InvalidHmacLength,
99 #[error(transparent)]
103 GroupIntent(#[from] GroupIntentError),
104}
105
106impl From<std::convert::Infallible> for StorageError {
107 fn from(_: std::convert::Infallible) -> StorageError {
108 unreachable!("Infallible conversion should never fail.")
110 }
111}
112
113impl StorageError {
114 #[cfg(target_arch = "wasm32")]
115 pub fn db_needs_connection(&self) -> bool {
116 use crate::PlatformStorageError::Disconnected;
117 matches!(
118 self,
119 Self::Platform(Disconnected)
120 | Self::Connection(crate::ConnectionError::Platform(Disconnected))
121 )
122 }
123
124 #[cfg(not(target_arch = "wasm32"))]
125 pub fn db_needs_connection(&self) -> bool {
126 use crate::PlatformStorageError::{Pool, PoolNeedsConnection};
127 use StorageError::*;
128 matches!(
131 self,
132 Platform(PoolNeedsConnection | Pool(_))
133 | Connection(crate::ConnectionError::Platform(
134 PoolNeedsConnection | Pool(_),
135 ))
136 )
137 }
138}
139
140#[derive(Error, Debug, ErrorCode)]
141pub enum NotFound {
143 #[error("Key package history row {0} not found")]
145 KeyPackageHistory(i32),
146 #[error("group with welcome id {0} not found")]
150 GroupByWelcome(Cursor),
151 #[error("group with id {0} not found")]
155 GroupById(GroupId),
156 #[error("installation time for group {0}")]
160 InstallationTimeForGroup(GroupId),
161 #[error("inbox id for address {0} not found")]
165 InboxIdForAddress(String),
166 #[error("message id {id} not found", id = hex::encode(_0))]
170 MessageById(Vec<u8>),
171 #[error("dm by dm_target_inbox_id {0} not found")]
175 DmByInbox(String),
176 #[error("intent with id {0} for state Published from ToPublish not found")]
180 IntentForToPublish(i32),
181 #[error("intent with id {0} for state ToPublish from Published not found")]
185 IntentForPublish(i32),
186 #[error("intent with id {0} for state Committed from Published not found")]
190 IntentForCommitted(i32),
191 #[error("Intent with id {0} not found")]
195 IntentById(i32),
196 #[error("Cipher salt for db at [`{0}`] not found")]
200 CipherSalt(String),
201 #[error("Sync Group for installation {0} not found")]
205 SyncGroup(InstallationId),
206 #[error("Key Package Reference {handle} not found", handle = hex::encode(_0))]
210 KeyPackageReference(Vec<u8>),
211 #[error("MLS Group {0} Not Found")]
215 MlsGroup(GroupId),
216 #[error("Post Quantum Key Pair not found")]
220 PostQuantumPrivateKey,
221 #[error("Key Package {kp} not found", kp = hex::encode(_0))]
225 KeyPackage(Vec<u8>),
226}
227
228#[derive(Error, Debug, ErrorCode)]
229#[error_code(internal)]
230pub enum DuplicateItem {
231 #[error("the welcome id {0:?} already exists")]
235 WelcomeId(Option<Cursor>),
236 #[error("the commit log public key for group id {id} already exists", id = hex::encode(_0))]
240 CommitLogPublicKey(Vec<u8>),
241}
242
243impl RetryableError for DuplicateItem {
244 fn is_retryable(&self) -> bool {
245 use DuplicateItem::*;
246 match self {
247 WelcomeId(_) => false,
248 CommitLogPublicKey(_) => false,
249 }
250 }
251}
252
253impl RetryableError<Mls> for diesel::result::Error {
254 fn is_retryable(&self) -> bool {
255 use DatabaseErrorKind::*;
256 use diesel::result::Error::*;
257
258 match self {
259 DatabaseError(UniqueViolation, _) => false,
260 DatabaseError(CheckViolation, _) => false,
261 DatabaseError(NotNullViolation, _) => false,
262 _ => true,
267 }
268 }
269}
270
271impl RetryableError for StorageError {
272 fn is_retryable(&self) -> bool {
273 match self {
274 Self::DieselConnect(_) => true,
275 Self::DieselResult(result) => retryable!(result),
276 Self::Duplicate(d) => retryable!(d),
277 Self::OpenMlsStorage(storage) => retryable!(storage),
278 Self::Platform(p) => retryable!(p),
279 Self::Connection(e) => retryable!(e),
280 Self::GroupIntent(e) => retryable!(e),
281 Self::Stream(e) => retryable!(e),
282 Self::PreTransitionDatabase
283 | Self::OldStreamDatabase
284 | Self::MigrationError(_)
285 | Self::Conversion(_)
286 | Self::NotFound(_)
287 | Self::DbDeserialize
288 | Self::DbSerialize
289 | Self::Builder(_)
290 | Self::InvalidHmacLength
291 | Self::Prost(_) => false,
292 }
293 }
294}
295
296impl RetryableError for NotFound {
297 fn is_retryable(&self) -> bool {
298 true
299 }
300}
301
302impl RetryableError<Mls> for openmls::group::AddMembersError<sql_key_store::SqlKeyStoreError> {
304 fn is_retryable(&self) -> bool {
305 match self {
306 Self::CreateCommitError(commit) => retryable!(commit),
307 Self::CommitBuilderStageError(commit_builder_stage) => retryable!(commit_builder_stage),
308 Self::StorageError(storage) => retryable!(storage),
309 Self::GroupStateError(group_state) => retryable!(group_state),
310 _ => false,
311 }
312 }
313}
314
315impl RetryableError<Mls> for openmls::group::CreateCommitError {
316 fn is_retryable(&self) -> bool {
317 false
318 }
319}
320
321impl RetryableError<Mls>
322 for openmls::treesync::LeafNodeUpdateError<sql_key_store::SqlKeyStoreError>
323{
324 fn is_retryable(&self) -> bool {
325 match self {
326 Self::Storage(storage) => retryable!(storage),
327 _ => false,
328 }
329 }
330}
331
332impl RetryableError<Mls> for openmls::key_packages::errors::KeyPackageNewError {
333 fn is_retryable(&self) -> bool {
334 matches!(self, Self::StorageError)
335 }
336}
337
338impl RetryableError<Mls> for openmls::group::RemoveMembersError<sql_key_store::SqlKeyStoreError> {
339 fn is_retryable(&self) -> bool {
340 match self {
341 Self::CreateCommitError(commit) => retryable!(commit),
342 Self::CommitBuilderStageError(commit_builder_stage) => retryable!(commit_builder_stage),
343 Self::GroupStateError(group_state) => retryable!(group_state),
344 Self::StorageError(storage) => retryable!(storage),
345 _ => false,
346 }
347 }
348}
349
350impl RetryableError<Mls> for openmls::group::NewGroupError<sql_key_store::SqlKeyStoreError> {
351 fn is_retryable(&self) -> bool {
352 match self {
353 Self::StorageError(storage) => retryable!(storage),
354 _ => false,
355 }
356 }
357}
358
359impl RetryableError<Mls>
360 for openmls::group::UpdateGroupMembershipError<sql_key_store::SqlKeyStoreError>
361{
362 fn is_retryable(&self) -> bool {
363 match self {
364 Self::CreateCommitError(create_commit) => retryable!(create_commit),
365 Self::GroupStateError(group_state) => retryable!(group_state),
366 Self::StorageError(storage) => retryable!(storage),
367 Self::CommitBuilderError(commit_builder) => retryable!(commit_builder),
368 }
369 }
370}
371
372impl RetryableError<Mls> for openmls::prelude::MlsGroupStateError {
373 fn is_retryable(&self) -> bool {
374 false
375 }
376}
377
378impl RetryableError<Mls>
379 for openmls::prelude::CreateGroupContextExtProposalError<sql_key_store::SqlKeyStoreError>
380{
381 fn is_retryable(&self) -> bool {
382 match self {
383 Self::CreateCommitError(create_commit) => retryable!(create_commit),
384 Self::StorageError(storage) => retryable!(storage),
385 _ => false,
386 }
387 }
388}
389
390impl RetryableError<Mls> for openmls::group::SelfUpdateError<sql_key_store::SqlKeyStoreError> {
391 fn is_retryable(&self) -> bool {
392 match self {
393 Self::CreateCommitError(commit) => retryable!(commit),
394 Self::CommitBuilderStageError(commit_builder_stage) => retryable!(commit_builder_stage),
395 Self::GroupStateError(group_state) => retryable!(group_state),
396 Self::StorageError(storage) => retryable!(storage),
397 _ => false,
398 }
399 }
400}
401
402impl RetryableError<Mls>
403 for openmls::group::CommitBuilderStageError<sql_key_store::SqlKeyStoreError>
404{
405 fn is_retryable(&self) -> bool {
406 match self {
407 Self::KeyStoreError(storage) => retryable!(storage),
408 Self::LibraryError(_) => false,
409 }
410 }
411}
412
413impl RetryableError<Mls>
414 for openmls::prelude::CreationFromExternalError<sql_key_store::SqlKeyStoreError>
415{
416 fn is_retryable(&self) -> bool {
417 match self {
418 Self::WriteToStorageError(storage) => retryable!(storage),
419 _ => false,
420 }
421 }
422}
423
424impl RetryableError<Mls>
425 for openmls::group::ProposeAddMemberError<sql_key_store::SqlKeyStoreError>
426{
427 fn is_retryable(&self) -> bool {
428 match self {
429 Self::GroupStateError(group_state) => retryable!(group_state),
430 Self::StorageError(storage) => retryable!(storage),
431 _ => false,
432 }
433 }
434}
435
436impl RetryableError<Mls>
437 for openmls::group::ProposeRemoveMemberError<sql_key_store::SqlKeyStoreError>
438{
439 fn is_retryable(&self) -> bool {
440 match self {
441 Self::GroupStateError(group_state) => retryable!(group_state),
442 Self::StorageError(storage) => retryable!(storage),
443 _ => false,
444 }
445 }
446}
447
448impl RetryableError<Mls>
449 for openmls::group::ProposeSelfUpdateError<sql_key_store::SqlKeyStoreError>
450{
451 fn is_retryable(&self) -> bool {
452 match self {
453 Self::LeafNodeUpdateError(leaf_node_update) => retryable!(leaf_node_update),
454 Self::StorageError(storage) => retryable!(storage),
455 _ => false,
456 }
457 }
458}
459
460impl RetryableError<Mls> for openmls::group::ProposalError<sql_key_store::SqlKeyStoreError> {
461 fn is_retryable(&self) -> bool {
462 match self {
463 Self::ProposeAddMemberError(e) => retryable!(e),
464 Self::ProposeSelfUpdateError(e) => retryable!(e),
465 Self::ProposeRemoveMemberError(e) => retryable!(e),
466 Self::CreateGroupContextExtProposalError(e) => retryable!(e),
467 Self::GroupStateError(group_state) => retryable!(group_state),
468 Self::StorageError(storage) => retryable!(storage),
469 _ => false,
470 }
471 }
472}
473
474impl RetryableError<Mls>
475 for openmls::group::CommitToPendingProposalsError<sql_key_store::SqlKeyStoreError>
476{
477 fn is_retryable(&self) -> bool {
478 match self {
479 Self::CreateCommitError(commit) => retryable!(commit),
480 Self::CommitBuilderStageError(commit_builder_stage) => {
481 retryable!(commit_builder_stage)
482 }
483 Self::GroupStateError(group_state) => retryable!(group_state),
484 Self::StorageError(storage) => retryable!(storage),
485 _ => false,
486 }
487 }
488}
489
490impl RetryableError<Mls> for openmls::prelude::WelcomeError<sql_key_store::SqlKeyStoreError> {
491 fn is_retryable(&self) -> bool {
492 match self {
493 Self::PublicGroupError(creation_err) => retryable!(creation_err),
494 Self::StorageError(storage) => retryable!(storage),
495 _ => false,
496 }
497 }
498}
499
500impl RetryableError<Mls> for openmls::group::MergeCommitError<sql_key_store::SqlKeyStoreError> {
501 fn is_retryable(&self) -> bool {
502 match self {
503 Self::StorageError(storage) => retryable!(storage),
504 Self::LibraryError(_) => false,
505 }
506 }
507}
508
509impl RetryableError<Mls>
510 for openmls::group::MergePendingCommitError<sql_key_store::SqlKeyStoreError>
511{
512 fn is_retryable(&self) -> bool {
513 match self {
514 Self::MlsGroupStateError(err) => retryable!(err),
515 Self::MergeCommitError(err) => retryable!(err),
516 }
517 }
518}
519
520impl RetryableError<Mls>
521 for openmls::prelude::ProcessMessageError<sql_key_store::SqlKeyStoreError>
522{
523 fn is_retryable(&self) -> bool {
524 match self {
525 Self::GroupStateError(err) => retryable!(err),
526 _ => false,
527 }
528 }
529}