1use diesel::result::DatabaseErrorKind;
2use thiserror::Error;
3
4use crate::group_intent::GroupIntentError;
5
6use super::{
7 refresh_state::EntityKind,
8 sql_key_store::{self, SqlKeyStoreError},
9};
10use xmtp_common::{BoxDynError, RetryableError, retryable};
11use xmtp_proto::types::{Cursor, InstallationId};
12
13pub struct Mls;
14
15#[derive(Debug, Error)]
16pub enum StorageError {
17 #[error(transparent)]
18 DieselConnect(#[from] diesel::ConnectionError),
19 #[error(transparent)]
20 DieselResult(#[from] diesel::result::Error),
21 #[error("Error migrating database {0}")]
22 MigrationError(#[from] BoxDynError),
23 #[error(transparent)]
24 NotFound(#[from] NotFound),
25 #[error(transparent)]
26 Duplicate(DuplicateItem),
27 #[error(transparent)]
28 OpenMlsStorage(#[from] SqlKeyStoreError),
29 #[error("Transaction was intentionally rolled back")]
30 IntentionalRollback,
31 #[error("failed to deserialize from db")]
32 DbDeserialize,
33 #[error("failed to serialize for db")]
34 DbSerialize,
35 #[error("required fields missing from stored db type {0}")]
36 Builder(#[from] derive_builder::UninitializedFieldError),
37 #[error(transparent)]
38 Platform(#[from] crate::database::PlatformStorageError),
39 #[error("decoding from database failed {}", _0)]
40 Prost(#[from] prost::DecodeError),
41 #[error(transparent)]
42 Conversion(#[from] xmtp_proto::ConversionError),
43 #[error(transparent)]
44 Connection(#[from] crate::ConnectionError),
45 #[error("HMAC key must be 42 bytes")]
46 InvalidHmacLength,
47 #[error(transparent)]
48 GroupIntent(#[from] GroupIntentError),
49}
50
51impl From<std::convert::Infallible> for StorageError {
52 fn from(_: std::convert::Infallible) -> StorageError {
53 unreachable!("Infallible conversion should never fail.")
55 }
56}
57
58impl StorageError {
59 #[cfg(target_arch = "wasm32")]
61 pub fn db_needs_connection(&self) -> bool {
62 false
63 }
64
65 #[cfg(not(target_arch = "wasm32"))]
66 pub fn db_needs_connection(&self) -> bool {
67 use StorageError::*;
68 matches!(
69 self,
70 Platform(crate::PlatformStorageError::PoolNeedsConnection)
71 | Connection(crate::ConnectionError::Platform(
72 crate::PlatformStorageError::PoolNeedsConnection,
73 ))
74 )
75 }
76}
77
78#[derive(Error, Debug)]
79pub enum NotFound {
81 #[error("group with welcome id {0} not found")]
82 GroupByWelcome(Cursor),
83 #[error("group with id {id} not found", id = hex::encode(_0))]
84 GroupById(Vec<u8>),
85 #[error("installation time for group {id}", id = hex::encode(_0))]
86 InstallationTimeForGroup(Vec<u8>),
87 #[error("inbox id for address {0} not found")]
88 InboxIdForAddress(String),
89 #[error("message id {id} not found", id = hex::encode(_0))]
90 MessageById(Vec<u8>),
91 #[error("dm by dm_target_inbox_id {0} not found")]
92 DmByInbox(String),
93 #[error("intent with id {0} for state Publish from ToPublish not found")]
94 IntentForToPublish(i32),
95 #[error("intent with id {0} for state ToPublish from Published not found")]
96 IntentForPublish(i32),
97 #[error("intent with id {0} for state Committed from Published not found")]
98 IntentForCommitted(i32),
99 #[error("Intent with id {0} not found")]
100 IntentById(i32),
101 #[error("refresh state with id {id} of kind {1} originating from node {2} not found", id = hex::encode(_0))]
102 RefreshStateByIdKindAndOriginator(Vec<u8>, EntityKind, i32),
103 #[error("Cipher salt for db at [`{0}`] not found")]
104 CipherSalt(String),
105 #[error("Sync Group for installation {0} not found")]
106 SyncGroup(InstallationId),
107 #[error("Key Package Reference {handle} not found", handle = hex::encode(_0))]
108 KeyPackageReference(Vec<u8>),
109 #[error("MLS Group Not Found")]
110 MlsGroup,
111 #[error("Post Quantum Key Pair not found")]
112 PostQuantumPrivateKey,
113 #[error("Key Package {kp} not found", kp = hex::encode(_0))]
114 KeyPackage(Vec<u8>),
115}
116
117#[derive(Error, Debug)]
118pub enum DuplicateItem {
119 #[error("the welcome id {0:?} already exists")]
120 WelcomeId(Option<Cursor>),
121 #[error("the commit log public key for group id {id} already exists", id = hex::encode(_0))]
122 CommitLogPublicKey(Vec<u8>),
123}
124
125impl RetryableError for DuplicateItem {
126 fn is_retryable(&self) -> bool {
127 use DuplicateItem::*;
128 match self {
129 WelcomeId(_) => false,
130 CommitLogPublicKey(_) => false,
131 }
132 }
133}
134
135impl RetryableError<Mls> for diesel::result::Error {
136 fn is_retryable(&self) -> bool {
137 use DatabaseErrorKind::*;
138 use diesel::result::Error::*;
139
140 match self {
141 DatabaseError(UniqueViolation, _) => false,
142 DatabaseError(CheckViolation, _) => false,
143 DatabaseError(NotNullViolation, _) => false,
144 _ => true,
149 }
150 }
151}
152
153impl RetryableError for StorageError {
154 fn is_retryable(&self) -> bool {
155 match self {
156 Self::DieselConnect(_) => true,
157 Self::DieselResult(result) => retryable!(result),
158 Self::Duplicate(d) => retryable!(d),
159 Self::OpenMlsStorage(storage) => retryable!(storage),
160 Self::Platform(p) => retryable!(p),
161 Self::Connection(e) => retryable!(e),
162 Self::GroupIntent(e) => retryable!(e),
163 Self::MigrationError(_)
164 | Self::Conversion(_)
165 | Self::NotFound(_)
166 | Self::IntentionalRollback
167 | Self::DbDeserialize
168 | Self::DbSerialize
169 | Self::Builder(_)
170 | Self::InvalidHmacLength
171 | Self::Prost(_) => false,
172 }
173 }
174}
175
176impl RetryableError for NotFound {
177 fn is_retryable(&self) -> bool {
178 true
179 }
180}
181
182impl RetryableError<Mls> for openmls::group::AddMembersError<sql_key_store::SqlKeyStoreError> {
184 fn is_retryable(&self) -> bool {
185 match self {
186 Self::CreateCommitError(commit) => retryable!(commit),
187 Self::CommitBuilderStageError(commit_builder_stage) => retryable!(commit_builder_stage),
188 Self::StorageError(storage) => retryable!(storage),
189 Self::GroupStateError(group_state) => retryable!(group_state),
190 _ => false,
191 }
192 }
193}
194
195impl RetryableError<Mls> for openmls::group::CreateCommitError {
196 fn is_retryable(&self) -> bool {
197 false
198 }
199}
200
201impl RetryableError<Mls>
202 for openmls::treesync::LeafNodeUpdateError<sql_key_store::SqlKeyStoreError>
203{
204 fn is_retryable(&self) -> bool {
205 match self {
206 Self::Storage(storage) => retryable!(storage),
207 _ => false,
208 }
209 }
210}
211
212impl RetryableError<Mls> for openmls::key_packages::errors::KeyPackageNewError {
213 fn is_retryable(&self) -> bool {
214 matches!(self, Self::StorageError)
215 }
216}
217
218impl RetryableError<Mls> for openmls::group::RemoveMembersError<sql_key_store::SqlKeyStoreError> {
219 fn is_retryable(&self) -> bool {
220 match self {
221 Self::CreateCommitError(commit) => retryable!(commit),
222 Self::CommitBuilderStageError(commit_builder_stage) => retryable!(commit_builder_stage),
223 Self::GroupStateError(group_state) => retryable!(group_state),
224 Self::StorageError(storage) => retryable!(storage),
225 _ => false,
226 }
227 }
228}
229
230impl RetryableError<Mls> for openmls::group::NewGroupError<sql_key_store::SqlKeyStoreError> {
231 fn is_retryable(&self) -> bool {
232 match self {
233 Self::StorageError(storage) => retryable!(storage),
234 _ => false,
235 }
236 }
237}
238
239impl RetryableError<Mls>
240 for openmls::group::UpdateGroupMembershipError<sql_key_store::SqlKeyStoreError>
241{
242 fn is_retryable(&self) -> bool {
243 match self {
244 Self::CreateCommitError(create_commit) => retryable!(create_commit),
245 Self::GroupStateError(group_state) => retryable!(group_state),
246 Self::StorageError(storage) => retryable!(storage),
247 Self::CommitBuilderError(commit_builder) => retryable!(commit_builder),
248 }
249 }
250}
251
252impl RetryableError<Mls> for openmls::prelude::MlsGroupStateError {
253 fn is_retryable(&self) -> bool {
254 false
255 }
256}
257
258impl RetryableError<Mls>
259 for openmls::prelude::CreateGroupContextExtProposalError<sql_key_store::SqlKeyStoreError>
260{
261 fn is_retryable(&self) -> bool {
262 match self {
263 Self::CreateCommitError(create_commit) => retryable!(create_commit),
264 Self::StorageError(storage) => retryable!(storage),
265 _ => false,
266 }
267 }
268}
269
270impl RetryableError<Mls> for openmls::group::SelfUpdateError<sql_key_store::SqlKeyStoreError> {
271 fn is_retryable(&self) -> bool {
272 match self {
273 Self::CreateCommitError(commit) => retryable!(commit),
274 Self::CommitBuilderStageError(commit_builder_stage) => retryable!(commit_builder_stage),
275 Self::GroupStateError(group_state) => retryable!(group_state),
276 Self::StorageError(storage) => retryable!(storage),
277 _ => false,
278 }
279 }
280}
281
282impl RetryableError<Mls>
283 for openmls::group::CommitBuilderStageError<sql_key_store::SqlKeyStoreError>
284{
285 fn is_retryable(&self) -> bool {
286 match self {
287 Self::KeyStoreError(storage) => retryable!(storage),
288 Self::LibraryError(_) => false,
289 }
290 }
291}
292
293impl RetryableError<Mls>
294 for openmls::prelude::CreationFromExternalError<sql_key_store::SqlKeyStoreError>
295{
296 fn is_retryable(&self) -> bool {
297 match self {
298 Self::WriteToStorageError(storage) => retryable!(storage),
299 _ => false,
300 }
301 }
302}
303
304impl RetryableError<Mls> for openmls::prelude::WelcomeError<sql_key_store::SqlKeyStoreError> {
305 fn is_retryable(&self) -> bool {
306 match self {
307 Self::PublicGroupError(creation_err) => retryable!(creation_err),
308 Self::StorageError(storage) => retryable!(storage),
309 _ => false,
310 }
311 }
312}
313
314impl RetryableError<Mls> for openmls::group::MergeCommitError<sql_key_store::SqlKeyStoreError> {
315 fn is_retryable(&self) -> bool {
316 match self {
317 Self::StorageError(storage) => retryable!(storage),
318 Self::LibraryError(_) => false,
319 }
320 }
321}
322
323impl RetryableError<Mls>
324 for openmls::group::MergePendingCommitError<sql_key_store::SqlKeyStoreError>
325{
326 fn is_retryable(&self) -> bool {
327 match self {
328 Self::MlsGroupStateError(err) => retryable!(err),
329 Self::MergeCommitError(err) => retryable!(err),
330 }
331 }
332}
333
334impl RetryableError<Mls>
335 for openmls::prelude::ProcessMessageError<sql_key_store::SqlKeyStoreError>
336{
337 fn is_retryable(&self) -> bool {
338 match self {
339 Self::GroupStateError(err) => retryable!(err),
340 _ => false,
341 }
342 }
343}