1#[cfg(test)]
2use crate::GroupCommitLock;
3use crate::builder::{DeviceSyncMode, ForkRecoveryOpts};
4#[cfg(any(test, target_arch = "wasm32"))]
5mod message_clock;
6use crate::client::DeviceSync;
7use crate::groups::change_callbacks::UnstableChangeCallbacks;
8use crate::server_configuration::ServerConfigurationHandle;
9use crate::subscriptions::internal::InternalEvent;
10use crate::utils::VersionInfo;
11use crate::worker::device_sync::worker::SyncMetric;
12use crate::worker::metrics::WorkerMetrics;
13use crate::worker::tasks::TaskWorkerChannels;
14use crate::worker::{DynMetrics, MetricsCasting, WorkerConfig, WorkerKind};
15use crate::{
16 identity::{Identity, IdentityError},
17 mutex_registry::MutexRegistry,
18};
19use parking_lot::Mutex;
20use std::collections::HashMap;
21use std::sync::Arc;
22use std::sync::atomic::{AtomicBool, Ordering};
23use tokio_util::sync::CancellationToken;
24use xmtp_api::{ApiClientWrapper, XmtpApi};
25use xmtp_common::{MaybeSend, MaybeSync};
26use xmtp_db::XmtpDb;
27use xmtp_db::XmtpMlsStorageProvider;
28use xmtp_db::xmtp_openmls_provider::XmtpOpenMlsProviderRef;
29use xmtp_events::EventBus;
30use xmtp_events::EventWriter;
31use xmtp_id::scw_verifier::SmartContractSignatureVerifier;
32use xmtp_id::{InboxIdRef, associations::builder::SignatureRequest};
33use xmtp_proto::types::InstallationId;
34
35#[cfg(test)]
36pub(crate) static FAIL_NEXT_DELIVERY_RELEASE: Mutex<Option<Vec<u8>>> = Mutex::new(None);
37
38#[cfg(any(test, feature = "test-utils"))]
39use crate::worker::device_sync::DeviceSyncClient;
40
41#[derive(Default)]
51pub struct ForegroundCalls {
52 state: Mutex<ForegroundCallState>,
53 idle: tokio::sync::Notify,
54}
55
56#[derive(Default)]
57struct ForegroundCallState {
58 closed: bool,
59 running: usize,
60}
61
62#[must_use = "the call leaves the gate when this guard drops"]
64pub struct ForegroundCall {
65 calls: Arc<ForegroundCalls>,
66}
67
68impl ForegroundCalls {
69 pub fn enter(self: &Arc<Self>) -> Option<ForegroundCall> {
71 let mut state = self.state.lock();
72 if state.closed {
73 return None;
74 }
75 state.running += 1;
76 Some(ForegroundCall {
77 calls: self.clone(),
78 })
79 }
80
81 pub async fn close_and_wait(&self) {
83 self.state.lock().closed = true;
84 loop {
85 let idle = self.idle.notified();
86 futures::pin_mut!(idle);
87 idle.as_mut().enable();
88 if self.state.lock().running == 0 {
89 return;
90 }
91 idle.await;
92 }
93 }
94}
95
96impl Drop for ForegroundCall {
97 fn drop(&mut self) {
98 let mut state = self.calls.state.lock();
99 state.running -= 1;
100 if state.running == 0 {
101 self.calls.idle.notify_waiters();
102 }
103 }
104}
105
106pub struct XmtpMlsLocalContext<ApiClient, Db, S> {
110 pub(crate) attachments: Arc<crate::attachments::AttachmentRuntime>,
111 pub(crate) identity: Identity,
113 pub(crate) api_client: ApiClientWrapper<ApiClient>,
115 pub(crate) store: Db,
117 pub(crate) mls_storage: S,
118 pub(crate) mutexes: MutexRegistry,
119 #[cfg(test)]
120 pub(crate) mls_commit_lock: Arc<GroupCommitLock>,
121 pub(crate) version_info: VersionInfo,
122 pub(crate) server_configuration: ServerConfigurationHandle,
125 pub(crate) events: EventBus<InternalEvent>,
126 pub(crate) public_event_writer: Arc<dyn EventWriter<()>>,
127 pub(crate) registration_event_pending: Arc<AtomicBool>,
129 pub(crate) delivery_owner: Arc<Mutex<Option<xmtp_db::delivery::DeliveryOwner>>>,
130 pub(crate) scw_verifier: Arc<Box<dyn SmartContractSignatureVerifier>>,
131 pub(crate) device_sync: DeviceSync,
132 pub(crate) fork_recovery_opts: ForkRecoveryOpts,
133 pub(crate) change_callbacks: UnstableChangeCallbacks,
136 pub(crate) incoming_runtime: Arc<crate::subscriptions::incoming::IncomingRuntime>,
137 pub(crate) identity_resolutions: Arc<crate::identity_updates::IdentityResolutionRegistry>,
138 pub(crate) worker_config: WorkerConfig,
139 pub(crate) worker_metrics: Arc<Mutex<HashMap<WorkerKind, DynMetrics>>>,
141 pub(crate) task_channels: TaskWorkerChannels,
142 pub(crate) cancellation_token: CancellationToken,
143 pub(crate) shutdown_complete: Arc<AtomicBool>,
149 pub(crate) foreground_calls: Arc<ForegroundCalls>,
150 #[cfg(any(test, target_arch = "wasm32"))]
151 pub(crate) message_clock: Arc<message_clock::MessageClock>,
152}
153
154impl<ApiClient, Db, S> XmtpMlsLocalContext<ApiClient, Db, S>
155where
156 Db: XmtpDb + 'static,
157 ApiClient: XmtpApi + 'static,
158 S: XmtpMlsStorageProvider + 'static,
159{
160 pub fn db(&self) -> Db::DbQuery {
163 self.store.db()
164 }
165
166 pub fn mls_provider(&'_ self) -> XmtpOpenMlsProviderRef<'_, S> {
168 XmtpOpenMlsProviderRef::new(&self.mls_storage)
169 }
170
171 pub fn store(&self) -> &Db {
172 &self.store
173 }
174
175 pub fn scw_verifier(&self) -> &Arc<Box<dyn SmartContractSignatureVerifier>> {
176 &self.scw_verifier
177 }
178
179 pub fn device_sync_worker_enabled(&self) -> bool {
180 !matches!(self.device_sync.mode, DeviceSyncMode::Disabled)
181 }
182
183 #[cfg(any(test, feature = "test-utils"))]
186 pub fn device_sync_client(
187 self: &Arc<XmtpMlsLocalContext<ApiClient, Db, S>>,
188 ) -> DeviceSyncClient<Arc<Self>> {
189 let metrics = self.sync_metrics();
190 DeviceSyncClient::new(
191 Arc::clone(self),
192 metrics.unwrap_or(Arc::new(WorkerMetrics::new(self.installation_id()))),
193 )
194 }
195}
196
197impl<ApiClient, Db, S> XmtpMlsLocalContext<ApiClient, Db, S> {
198 pub fn replace_mls_store<S2>(self, mls_store: S2) -> XmtpMlsLocalContext<ApiClient, Db, S2> {
199 XmtpMlsLocalContext::<ApiClient, Db, S2> {
200 attachments: self.attachments,
201 identity: self.identity,
202 api_client: self.api_client,
203 store: self.store,
204 mls_storage: mls_store,
205 mutexes: self.mutexes,
206 #[cfg(test)]
207 mls_commit_lock: self.mls_commit_lock,
208 version_info: self.version_info,
209 server_configuration: self.server_configuration,
210 events: self.events,
211 public_event_writer: self.public_event_writer,
212 registration_event_pending: self.registration_event_pending,
213 delivery_owner: self.delivery_owner,
214 scw_verifier: self.scw_verifier,
215 device_sync: self.device_sync,
216 fork_recovery_opts: self.fork_recovery_opts,
217 change_callbacks: self.change_callbacks,
218 incoming_runtime: self.incoming_runtime,
219 identity_resolutions: self.identity_resolutions,
220 worker_config: self.worker_config,
221 worker_metrics: self.worker_metrics,
222 task_channels: self.task_channels,
223 cancellation_token: self.cancellation_token,
224 shutdown_complete: self.shutdown_complete,
225 foreground_calls: self.foreground_calls,
226 #[cfg(any(test, target_arch = "wasm32"))]
227 message_clock: self.message_clock,
228 }
229 }
230}
231
232impl<ApiClient, Db, S> XmtpMlsLocalContext<ApiClient, Db, S> {
233 pub fn installation_public_key(&self) -> InstallationId {
235 (*self.identity.installation_keys.public_bytes()).into()
236 }
237
238 pub fn installation_id(&self) -> InstallationId {
240 self.identity.installation_id()
241 }
242
243 pub fn inbox_id(&self) -> InboxIdRef<'_> {
245 self.identity.inbox_id()
246 }
247
248 pub fn signature_request(&self) -> Option<SignatureRequest> {
251 self.identity.signature_request()
252 }
253
254 pub fn sign_with_public_context(
255 &self,
256 text: impl AsRef<str>,
257 ) -> Result<Vec<u8>, IdentityError> {
258 self.identity.sign_with_public_context(text)
259 }
260
261 #[cfg(test)]
262 pub fn mls_commit_lock(&self) -> &Arc<GroupCommitLock> {
263 &self.mls_commit_lock
264 }
265
266 pub fn sync_metrics(&self) -> Option<Arc<WorkerMetrics<SyncMetric>>> {
267 self.worker_metrics
268 .lock()
269 .get(&WorkerKind::DeviceSync)?
270 .as_sync_metrics()
271 }
272
273 pub fn cancellation_token(&self) -> &CancellationToken {
274 &self.cancellation_token
275 }
276
277 pub fn shutdown_complete(&self) -> bool {
278 self.shutdown_complete.load(Ordering::Acquire)
279 }
280
281 pub fn mark_shutdown_complete(&self) {
282 self.shutdown_complete.store(true, Ordering::Release);
283 }
284}
285
286pub trait XmtpSharedContext
287where
288 Self: MaybeSend + MaybeSync + Sized + Clone,
289{
290 type Db: XmtpDb;
291 type ApiClient: XmtpApi;
292 type MlsStorage: XmtpMlsStorageProvider;
293 type ContextReference: XmtpSharedContext<Db = Self::Db, ApiClient = Self::ApiClient, MlsStorage = Self::MlsStorage>
295 + 'static;
296
297 fn context_ref(&self) -> &Self::ContextReference;
299 fn db(&self) -> <Self::Db as XmtpDb>::DbQuery;
300 fn attachment_runtime(&self) -> &Arc<crate::attachments::AttachmentRuntime>;
301 fn api(&self) -> &ApiClientWrapper<Self::ApiClient>;
302 fn scw_verifier(&self) -> Arc<Box<dyn SmartContractSignatureVerifier>>;
303
304 fn device_sync(&self) -> &DeviceSync;
305
306 fn device_sync_worker_enabled(&self) -> bool {
307 !matches!(self.device_sync().mode, DeviceSyncMode::Disabled)
308 }
309
310 fn fork_recovery_opts(&self) -> &ForkRecoveryOpts;
311
312 fn worker_config(&self) -> &WorkerConfig;
313
314 fn worker_interval(
317 &self,
318 kind: WorkerKind,
319 const_default: std::time::Duration,
320 ) -> (std::time::Duration, std::time::Duration) {
321 self.worker_config().interval(kind, const_default)
322 }
323
324 fn mls_provider(&'_ self) -> XmtpOpenMlsProviderRef<'_, Self::MlsStorage> {
326 XmtpOpenMlsProviderRef::new(self.mls_storage())
327 }
328
329 fn mls_storage(&self) -> &Self::MlsStorage;
330 fn identity(&self) -> &Identity;
331
332 fn signature_request(&self) -> Option<SignatureRequest> {
333 self.identity().signature_request()
334 }
335
336 fn inbox_id(&self) -> InboxIdRef<'_> {
337 self.identity().inbox_id()
338 }
339
340 fn installation_id(&self) -> InstallationId {
341 (*self.identity().installation_keys.public_bytes()).into()
342 }
343
344 fn version_info(&self) -> &VersionInfo;
345 fn server_configuration(&self) -> &ServerConfigurationHandle;
347 fn events(&self) -> &EventBus<InternalEvent>;
348 fn registration_event_pending(&self) -> &AtomicBool;
349 fn public_event_writer(&self) -> &Arc<dyn EventWriter<()>>;
350 fn delivery_owner(&self) -> &Mutex<Option<xmtp_db::delivery::DeliveryOwner>>;
352
353 fn close_message_delivery(&self) -> Result<(), xmtp_db::StorageError> {
355 use xmtp_db::delivery::QueryDelivery;
356 #[cfg(test)]
357 {
358 let mut fail = FAIL_NEXT_DELIVERY_RELEASE.lock();
359 if fail.as_deref() == Some(self.installation_id().to_vec().as_slice()) {
360 *fail = None;
361 return Err(xmtp_db::StorageError::DbDeserialize);
362 }
363 }
364 let mut registered = self.delivery_owner().lock();
365 if let Some(owner) = *registered {
366 self.db().release_delivery_owner(owner)?;
367 *registered = None;
368 }
369 Ok(())
370 }
371 fn task_channels(&self) -> &TaskWorkerChannels;
372 fn change_callbacks(&self) -> &UnstableChangeCallbacks;
374 fn incoming_runtime(&self) -> &crate::subscriptions::incoming::IncomingRuntime;
376 fn identity_resolution_registry(&self) -> &crate::identity_updates::IdentityResolutionRegistry;
378 fn sync_metrics(&self) -> Option<Arc<WorkerMetrics<SyncMetric>>>;
379 #[cfg(test)]
380 fn mls_commit_lock(&self) -> &Arc<GroupCommitLock>;
381 fn mutexes(&self) -> &MutexRegistry;
382 fn cancellation_token(&self) -> &CancellationToken;
383
384 fn is_closed(&self) -> bool {
386 self.cancellation_token().is_cancelled()
387 }
388
389 fn shutdown_complete(&self) -> bool;
394
395 fn mark_shutdown_complete(&self);
396
397 fn foreground_calls(&self) -> &Arc<ForegroundCalls>;
399
400 fn message_timestamp_ns(&self) -> i64;
402}
403
404impl<XApiClient, XDb, XMls> XmtpSharedContext for Arc<XmtpMlsLocalContext<XApiClient, XDb, XMls>>
405where
406 XApiClient: XmtpApi + 'static,
407 XDb: XmtpDb + 'static,
408 XMls: XmtpMlsStorageProvider + 'static,
409{
410 type Db = XDb;
411 type ApiClient = XApiClient;
412 type MlsStorage = XMls;
413 type ContextReference = Self;
414
415 fn context_ref(&self) -> &Self::ContextReference {
416 self
417 }
418
419 fn db(&self) -> <Self::Db as XmtpDb>::DbQuery {
420 self.store.db()
421 }
422
423 fn attachment_runtime(&self) -> &Arc<crate::attachments::AttachmentRuntime> {
424 &self.attachments
425 }
426
427 fn api(&self) -> &ApiClientWrapper<Self::ApiClient> {
428 &self.api_client
429 }
430
431 fn scw_verifier(&self) -> Arc<Box<dyn SmartContractSignatureVerifier>> {
432 self.scw_verifier.clone()
433 }
434
435 fn device_sync(&self) -> &DeviceSync {
436 &self.device_sync
437 }
438
439 fn fork_recovery_opts(&self) -> &ForkRecoveryOpts {
440 &self.fork_recovery_opts
441 }
442
443 fn worker_config(&self) -> &WorkerConfig {
444 &self.worker_config
445 }
446
447 fn mls_storage(&self) -> &Self::MlsStorage {
450 &self.mls_storage
451 }
452
453 fn identity(&self) -> &Identity {
454 &self.identity
455 }
456
457 fn version_info(&self) -> &VersionInfo {
458 &self.version_info
459 }
460
461 fn server_configuration(&self) -> &ServerConfigurationHandle {
462 &self.server_configuration
463 }
464
465 fn events(&self) -> &EventBus<InternalEvent> {
466 &self.events
467 }
468
469 fn registration_event_pending(&self) -> &AtomicBool {
470 &self.registration_event_pending
471 }
472
473 fn public_event_writer(&self) -> &Arc<dyn EventWriter<()>> {
474 &self.public_event_writer
475 }
476
477 fn delivery_owner(&self) -> &Mutex<Option<xmtp_db::delivery::DeliveryOwner>> {
478 &self.delivery_owner
479 }
480
481 #[cfg(test)]
482 fn mls_commit_lock(&self) -> &Arc<GroupCommitLock> {
483 &self.mls_commit_lock
484 }
485
486 fn task_channels(&self) -> &TaskWorkerChannels {
487 &self.task_channels
488 }
489
490 fn change_callbacks(&self) -> &UnstableChangeCallbacks {
491 &self.change_callbacks
492 }
493
494 fn incoming_runtime(&self) -> &crate::subscriptions::incoming::IncomingRuntime {
495 &self.incoming_runtime
496 }
497
498 fn identity_resolution_registry(&self) -> &crate::identity_updates::IdentityResolutionRegistry {
499 &self.identity_resolutions
500 }
501
502 fn sync_metrics(&self) -> Option<Arc<WorkerMetrics<SyncMetric>>> {
503 self.worker_metrics
504 .lock()
505 .get(&WorkerKind::DeviceSync)?
506 .as_sync_metrics()
507 }
508
509 fn mutexes(&self) -> &MutexRegistry {
510 &self.mutexes
511 }
512
513 fn cancellation_token(&self) -> &CancellationToken {
514 &self.cancellation_token
515 }
516
517 fn shutdown_complete(&self) -> bool {
518 self.shutdown_complete.load(Ordering::Acquire)
519 }
520
521 fn mark_shutdown_complete(&self) {
522 self.shutdown_complete.store(true, Ordering::Release);
523 }
524
525 fn foreground_calls(&self) -> &Arc<ForegroundCalls> {
526 &self.foreground_calls
527 }
528
529 fn message_timestamp_ns(&self) -> i64 {
530 #[cfg(target_arch = "wasm32")]
531 {
532 self.message_clock.now_ns()
533 }
534
535 #[cfg(not(target_arch = "wasm32"))]
536 {
537 #[cfg(test)]
538 if self.message_clock.is_frozen() {
539 return self.message_clock.now_ns();
540 }
541 xmtp_common::time::now_ns()
542 }
543 }
544}
545
546impl<T> XmtpSharedContext for &T
547where
548 T: XmtpSharedContext,
549{
550 type Db = <T as XmtpSharedContext>::Db;
551 type ApiClient = <T as XmtpSharedContext>::ApiClient;
552 type MlsStorage = <T as XmtpSharedContext>::MlsStorage;
553 type ContextReference = <T as XmtpSharedContext>::ContextReference;
554
555 fn context_ref(&self) -> &Self::ContextReference {
556 <T as XmtpSharedContext>::context_ref(self)
557 }
558
559 fn db(&self) -> <Self::Db as XmtpDb>::DbQuery {
560 <T as XmtpSharedContext>::db(self)
561 }
562
563 fn attachment_runtime(&self) -> &Arc<crate::attachments::AttachmentRuntime> {
564 <T as XmtpSharedContext>::attachment_runtime(self)
565 }
566
567 fn api(&self) -> &ApiClientWrapper<Self::ApiClient> {
568 <T as XmtpSharedContext>::api(self)
569 }
570
571 fn scw_verifier(&self) -> Arc<Box<dyn SmartContractSignatureVerifier>> {
572 <T as XmtpSharedContext>::scw_verifier(self)
573 }
574
575 fn device_sync(&self) -> &DeviceSync {
576 <T as XmtpSharedContext>::device_sync(self)
577 }
578
579 fn device_sync_worker_enabled(&self) -> bool {
580 <T as XmtpSharedContext>::device_sync_worker_enabled(self)
581 }
582
583 fn fork_recovery_opts(&self) -> &ForkRecoveryOpts {
584 <T as XmtpSharedContext>::fork_recovery_opts(self)
585 }
586
587 fn worker_config(&self) -> &WorkerConfig {
588 <T as XmtpSharedContext>::worker_config(self)
589 }
590
591 fn mls_storage(&self) -> &Self::MlsStorage {
592 <T as XmtpSharedContext>::mls_storage(self)
593 }
594
595 fn identity(&self) -> &Identity {
596 <T as XmtpSharedContext>::identity(self)
597 }
598
599 fn version_info(&self) -> &VersionInfo {
600 <T as XmtpSharedContext>::version_info(self)
601 }
602
603 fn server_configuration(&self) -> &ServerConfigurationHandle {
604 <T as XmtpSharedContext>::server_configuration(self)
605 }
606
607 fn events(&self) -> &EventBus<InternalEvent> {
608 <T as XmtpSharedContext>::events(self)
609 }
610
611 fn registration_event_pending(&self) -> &AtomicBool {
612 <T as XmtpSharedContext>::registration_event_pending(self)
613 }
614
615 fn public_event_writer(&self) -> &Arc<dyn EventWriter<()>> {
616 <T as XmtpSharedContext>::public_event_writer(self)
617 }
618
619 fn delivery_owner(&self) -> &Mutex<Option<xmtp_db::delivery::DeliveryOwner>> {
620 <T as XmtpSharedContext>::delivery_owner(self)
621 }
622
623 #[cfg(test)]
624 fn mls_commit_lock(&self) -> &Arc<GroupCommitLock> {
625 <T as XmtpSharedContext>::mls_commit_lock(self)
626 }
627
628 fn task_channels(&self) -> &TaskWorkerChannels {
629 <T as XmtpSharedContext>::task_channels(self)
630 }
631
632 fn change_callbacks(&self) -> &UnstableChangeCallbacks {
633 <T as XmtpSharedContext>::change_callbacks(self)
634 }
635
636 fn incoming_runtime(&self) -> &crate::subscriptions::incoming::IncomingRuntime {
637 <T as XmtpSharedContext>::incoming_runtime(self)
638 }
639
640 fn identity_resolution_registry(&self) -> &crate::identity_updates::IdentityResolutionRegistry {
641 <T as XmtpSharedContext>::identity_resolution_registry(self)
642 }
643
644 fn sync_metrics(&self) -> Option<Arc<WorkerMetrics<SyncMetric>>> {
645 <T as XmtpSharedContext>::sync_metrics(self)
646 }
647
648 fn mutexes(&self) -> &MutexRegistry {
649 <T as XmtpSharedContext>::mutexes(self)
650 }
651
652 fn cancellation_token(&self) -> &CancellationToken {
653 <T as XmtpSharedContext>::cancellation_token(self)
654 }
655
656 fn shutdown_complete(&self) -> bool {
657 <T as XmtpSharedContext>::shutdown_complete(self)
658 }
659
660 fn mark_shutdown_complete(&self) {
661 <T as XmtpSharedContext>::mark_shutdown_complete(self)
662 }
663
664 fn foreground_calls(&self) -> &Arc<ForegroundCalls> {
665 <T as XmtpSharedContext>::foreground_calls(self)
666 }
667
668 fn message_timestamp_ns(&self) -> i64 {
669 <T as XmtpSharedContext>::message_timestamp_ns(self)
670 }
671}