Skip to main content

fedimint_client_module/module/
mod.rs

1use core::fmt;
2use std::any::Any;
3use std::collections::BTreeSet;
4use std::fmt::Debug;
5use std::pin::Pin;
6use std::sync::{Arc, Weak};
7use std::{ffi, marker, ops};
8
9use bitcoin::secp256k1::PublicKey;
10use fedimint_api_client::api::{DynGlobalApi, DynModuleApi};
11use fedimint_core::config::ClientConfig;
12use fedimint_core::core::{
13    Decoder, DynInput, DynOutput, IInput, IntoDynInstance, ModuleInstanceId, ModuleKind,
14    OperationId,
15};
16use fedimint_core::db::{Database, DatabaseTransaction, GlobalDBTxAccessToken, NonCommittable};
17use fedimint_core::invite_code::InviteCode;
18use fedimint_core::module::registry::{ModuleDecoderRegistry, ModuleRegistry};
19use fedimint_core::module::{AmountUnit, Amounts, CommonModuleInit, ModuleCommon, ModuleInit};
20use fedimint_core::task::{MaybeSend, MaybeSync};
21use fedimint_core::util::{BoxStream, FmtCompact as _};
22use fedimint_core::{
23    Amount, OutPoint, PeerId, apply, async_trait_maybe_send, dyn_newtype_define, maybe_add_send,
24    maybe_add_send_sync,
25};
26use fedimint_eventlog::{
27    DBTransactionEventLogExt, Event, EventKind, EventLogId, EventPersistence, PersistedLogEntry,
28};
29use fedimint_logging::LOG_CLIENT;
30use futures::{Stream, StreamExt};
31use serde::Serialize;
32use serde::de::DeserializeOwned;
33use tracing::warn;
34
35use self::init::ClientModuleInit;
36use crate::error::{
37    ClientModuleError, ModuleLookupError, OperationAlreadyExistsError, OperationLookupError,
38    OperationNotFoundError, TransactionSubmitError,
39};
40use crate::module::recovery::{DynModuleBackup, ModuleBackup};
41use crate::oplog::{IOperationLog, OperationLogEntry, UpdateStreamOrOutcome};
42use crate::sm::executor::{ActiveStateKey, IExecutor, InactiveStateKey};
43use crate::sm::{self, ActiveStateMeta, Context, DynContext, DynState, InactiveStateMeta, State};
44use crate::transaction::{
45    ClientInputBundle, ClientOutputBundle, FeeQuote, FeeQuoteRequest, TransactionBuilder,
46};
47use crate::{AddStateMachinesResult, InstancelessDynClientInputBundle, TransactionUpdates, oplog};
48
49pub mod init;
50pub mod recovery;
51
52pub type ClientModuleRegistry = ModuleRegistry<DynClientModule>;
53
54/// A fedimint-client interface exposed to client modules
55///
56/// To break the dependency of the client modules on the whole fedimint client
57/// and in particular the `fedimint-client` crate, the module gets access to an
58/// interface, that is implemented by the `Client`.
59///
60/// This allows lose coupling, less recompilation and better control and
61/// understanding of what functionality of the Client the modules get access to.
62#[apply(async_trait_maybe_send!)]
63pub trait ClientContextIface: MaybeSend + MaybeSync {
64    fn get_module(&self, instance: ModuleInstanceId) -> &maybe_add_send_sync!(dyn IClientModule);
65    fn api_clone(&self) -> DynGlobalApi;
66    fn decoders(&self) -> &ModuleDecoderRegistry;
67    async fn finalize_and_submit_transaction(
68        &self,
69        operation_id: OperationId,
70        operation_type: &str,
71        operation_meta_gen: Box<maybe_add_send_sync!(dyn Fn(OutPointRange) -> serde_json::Value)>,
72        tx_builder: TransactionBuilder,
73    ) -> Result<OutPointRange, TransactionSubmitError>;
74
75    async fn finalize_and_submit_transaction_dbtx(
76        &self,
77        dbtx: &mut DatabaseTransaction<'_>,
78        operation_id: OperationId,
79        operation_type: &str,
80        operation_meta_gen: Box<maybe_add_send_sync!(dyn Fn(OutPointRange) -> serde_json::Value)>,
81        tx_builder: TransactionBuilder,
82    ) -> Result<OutPointRange, TransactionSubmitError>;
83
84    // TODO: unify
85    async fn finalize_and_submit_transaction_inner(
86        &self,
87        dbtx: &mut DatabaseTransaction<'_>,
88        operation_id: OperationId,
89        tx_builder: TransactionBuilder,
90    ) -> Result<OutPointRange, TransactionSubmitError>;
91
92    /// Computes the fee finalizing and submitting a transaction with the
93    /// explicit items described by `request` would incur, without submitting
94    /// anything. See `Client::fee_quote`.
95    async fn fee_quote(
96        &self,
97        operation_id: OperationId,
98        request: FeeQuoteRequest,
99    ) -> Result<FeeQuote, TransactionSubmitError>;
100
101    /// The client's balance for `unit`, held by the primary module. See
102    /// `Client::get_balance_for_unit`.
103    async fn get_balance_for_unit(&self, unit: AmountUnit) -> Result<Amount, ModuleLookupError>;
104
105    async fn transaction_updates(&self, operation_id: OperationId) -> TransactionUpdates;
106
107    async fn await_primary_module_outputs(
108        &self,
109        operation_id: OperationId,
110        // TODO: make `impl Iterator<Item = ...>`
111        outputs: Vec<OutPoint>,
112    ) -> Result<(), TransactionSubmitError>;
113
114    fn operation_log(&self) -> &dyn IOperationLog;
115
116    async fn has_active_states(&self, operation_id: OperationId) -> bool;
117
118    /// Waits until no state machine of the operation is running any more.
119    /// See `Client::await_no_active_states`.
120    async fn await_no_active_states(&self, operation_id: OperationId);
121
122    async fn operation_exists(&self, operation_id: OperationId) -> bool;
123
124    async fn config(&self) -> ClientConfig;
125
126    fn db(&self) -> &Database;
127
128    fn executor(&self) -> &(maybe_add_send_sync!(dyn IExecutor + 'static));
129
130    async fn invite_code(&self, peer: PeerId) -> Option<InviteCode>;
131
132    fn get_internal_payment_markers(&self) -> Result<(PublicKey, u64), bitcoin::secp256k1::Error>;
133
134    #[allow(clippy::too_many_arguments)]
135    async fn log_event_json(
136        &self,
137        dbtx: &mut DatabaseTransaction<'_, NonCommittable>,
138        module_kind: Option<ModuleKind>,
139        module_id: ModuleInstanceId,
140        kind: EventKind,
141        payload: serde_json::Value,
142        persist: EventPersistence,
143    );
144
145    async fn read_operation_active_states<'dbtx>(
146        &self,
147        operation_id: OperationId,
148        module_id: ModuleInstanceId,
149        dbtx: &'dbtx mut DatabaseTransaction<'_>,
150    ) -> Pin<Box<maybe_add_send!(dyn Stream<Item = (ActiveStateKey, ActiveStateMeta)> + 'dbtx)>>;
151
152    async fn read_operation_inactive_states<'dbtx>(
153        &self,
154        operation_id: OperationId,
155        module_id: ModuleInstanceId,
156        dbtx: &'dbtx mut DatabaseTransaction<'_>,
157    ) -> Pin<Box<maybe_add_send!(dyn Stream<Item = (InactiveStateKey, InactiveStateMeta)> + 'dbtx)>>;
158}
159
160/// A final, fully initialized client
161///
162/// Client modules need to be able to access a `Client` they are a part
163/// of. To break the circular dependency, the final `Client` is passed
164/// after `Client` was built via a shared state.
165#[derive(Clone, Default)]
166pub struct FinalClientIface(Arc<std::sync::OnceLock<Weak<dyn ClientContextIface>>>);
167
168impl FinalClientIface {
169    /// Get a temporary strong reference to [`ClientContextIface`]
170    ///
171    /// Care must be taken to not let the user take ownership of this value,
172    /// and not store it elsewhere permanently either, as it could prevent
173    /// the cleanup of the Client.
174    pub(crate) fn get(&self) -> Arc<dyn ClientContextIface> {
175        self.0
176            .get()
177            .expect("client must be already set")
178            .upgrade()
179            .expect("client module context must not be use past client shutdown")
180    }
181
182    pub fn set(&self, client: Weak<dyn ClientContextIface>) {
183        self.0.set(client).expect("FinalLazyClient already set");
184    }
185}
186
187impl fmt::Debug for FinalClientIface {
188    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189        f.write_str("FinalClientIface")
190    }
191}
192/// A Client context for a [`ClientModule`] `M`
193///
194/// Client modules can interact with the whole
195/// client through this struct.
196pub struct ClientContext<M> {
197    client: FinalClientIface,
198    module_instance_id: ModuleInstanceId,
199    global_dbtx_access_token: GlobalDBTxAccessToken,
200    module_db: Database,
201    _marker: marker::PhantomData<M>,
202}
203
204impl<M> Clone for ClientContext<M> {
205    fn clone(&self) -> Self {
206        Self {
207            client: self.client.clone(),
208            module_db: self.module_db.clone(),
209            module_instance_id: self.module_instance_id,
210            _marker: marker::PhantomData,
211            global_dbtx_access_token: self.global_dbtx_access_token,
212        }
213    }
214}
215
216/// A reference back to itself that the module cacn get from the
217/// [`ClientContext`]
218pub struct ClientContextSelfRef<'s, M> {
219    // we are OK storing `ClientStrong` here, because of the `'s` preventing `Self` from being
220    // stored permanently somewhere
221    client: Arc<dyn ClientContextIface>,
222    module_instance_id: ModuleInstanceId,
223    _marker: marker::PhantomData<&'s M>,
224}
225
226impl<M> ops::Deref for ClientContextSelfRef<'_, M>
227where
228    M: ClientModule,
229{
230    type Target = M;
231
232    fn deref(&self) -> &Self::Target {
233        self.client
234            .get_module(self.module_instance_id)
235            .as_any()
236            .downcast_ref::<M>()
237            .unwrap_or_else(|| panic!("Module is not of type {}", std::any::type_name::<M>()))
238    }
239}
240
241impl<M> fmt::Debug for ClientContext<M> {
242    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243        f.write_str("ClientContext")
244    }
245}
246
247impl<M> ClientContext<M>
248where
249    M: ClientModule,
250{
251    pub fn new(
252        client: FinalClientIface,
253        module_instance_id: ModuleInstanceId,
254        global_dbtx_access_token: GlobalDBTxAccessToken,
255        module_db: Database,
256    ) -> Self {
257        Self {
258            client,
259            module_instance_id,
260            global_dbtx_access_token,
261            module_db,
262            _marker: marker::PhantomData,
263        }
264    }
265
266    /// Get a reference back to client module from the [`Self`]
267    ///
268    /// It's often necessary for a client module to "move self"
269    /// by-value, especially due to async lifetimes issues.
270    /// Clients usually work with `&mut self`, which can't really
271    /// work in such context.
272    ///
273    /// Fortunately [`ClientContext`] is `Clone` and `Send, and
274    /// can be used to recover the reference to the module at later
275    /// time.
276    #[allow(clippy::needless_lifetimes)] // just for explicitiness
277    pub fn self_ref(&self) -> ClientContextSelfRef<'_, M> {
278        ClientContextSelfRef {
279            client: self.client.get(),
280            module_instance_id: self.module_instance_id,
281            _marker: marker::PhantomData,
282        }
283    }
284
285    /// Get a reference to a global Api handle
286    pub fn global_api(&self) -> DynGlobalApi {
287        self.client.get().api_clone()
288    }
289
290    /// Get a reference to a module Api handle
291    pub fn module_api(&self) -> DynModuleApi {
292        self.global_api().with_module(self.module_instance_id)
293    }
294
295    /// A set of all decoders of all modules of the client
296    pub fn decoders(&self) -> ModuleDecoderRegistry {
297        Clone::clone(self.client.get().decoders())
298    }
299
300    pub fn input_from_dyn<'i>(
301        &self,
302        input: &'i DynInput,
303    ) -> Option<&'i <M::Common as ModuleCommon>::Input> {
304        (input.module_instance_id() == self.module_instance_id).then(|| {
305            input
306                .as_any()
307                .downcast_ref::<<M::Common as ModuleCommon>::Input>()
308                .unwrap_or_else(|| {
309                    panic!("instance_id {} just checked", input.module_instance_id())
310                })
311        })
312    }
313
314    pub fn output_from_dyn<'o>(
315        &self,
316        output: &'o DynOutput,
317    ) -> Option<&'o <M::Common as ModuleCommon>::Output> {
318        (output.module_instance_id() == self.module_instance_id).then(|| {
319            output
320                .as_any()
321                .downcast_ref::<<M::Common as ModuleCommon>::Output>()
322                .unwrap_or_else(|| {
323                    panic!("instance_id {} just checked", output.module_instance_id())
324                })
325        })
326    }
327
328    pub fn map_dyn<'s, 'i, 'o, I>(
329        &'s self,
330        typed: impl IntoIterator<Item = I> + 'i,
331    ) -> impl Iterator<Item = <I as IntoDynInstance>::DynType> + 'o
332    where
333        I: IntoDynInstance,
334        'i: 'o,
335        's: 'o,
336    {
337        typed.into_iter().map(|i| self.make_dyn(i))
338    }
339
340    /// Turn a typed output into a dyn version
341    pub fn make_dyn_output(&self, output: <M::Common as ModuleCommon>::Output) -> DynOutput {
342        self.make_dyn(output)
343    }
344
345    /// Turn a typed input into a dyn version
346    pub fn make_dyn_input(&self, input: <M::Common as ModuleCommon>::Input) -> DynInput {
347        self.make_dyn(input)
348    }
349
350    /// Turn a `typed` into a dyn version
351    pub fn make_dyn<I>(&self, typed: I) -> <I as IntoDynInstance>::DynType
352    where
353        I: IntoDynInstance,
354    {
355        typed.into_dyn(self.module_instance_id)
356    }
357
358    /// Turn a typed [`ClientOutputBundle`] into a dyn version
359    pub fn make_client_outputs<O, S>(&self, output: ClientOutputBundle<O, S>) -> ClientOutputBundle
360    where
361        O: IntoDynInstance<DynType = DynOutput> + 'static,
362        S: IntoDynInstance<DynType = DynState> + 'static,
363    {
364        self.make_dyn(output)
365    }
366
367    /// Turn a typed [`ClientInputBundle`] into a dyn version
368    pub fn make_client_inputs<I, S>(&self, inputs: ClientInputBundle<I, S>) -> ClientInputBundle
369    where
370        I: IntoDynInstance<DynType = DynInput> + 'static,
371        S: IntoDynInstance<DynType = DynState> + 'static,
372    {
373        self.make_dyn(inputs)
374    }
375
376    pub fn make_dyn_state<S>(&self, sm: S) -> DynState
377    where
378        S: sm::IState + 'static,
379    {
380        DynState::from_typed(self.module_instance_id, sm)
381    }
382
383    pub async fn finalize_and_submit_transaction<F, Meta>(
384        &self,
385        operation_id: OperationId,
386        operation_type: &str,
387        operation_meta_gen: F,
388        tx_builder: TransactionBuilder,
389    ) -> Result<OutPointRange, TransactionSubmitError>
390    where
391        F: Fn(OutPointRange) -> Meta + Clone + MaybeSend + MaybeSync + 'static,
392        Meta: serde::Serialize + MaybeSend,
393    {
394        self.client
395            .get()
396            .finalize_and_submit_transaction(
397                operation_id,
398                operation_type,
399                Box::new(move |out_point_range| {
400                    serde_json::to_value(operation_meta_gen(out_point_range)).expect("Can't fail")
401                }),
402                tx_builder,
403            )
404            .await
405    }
406
407    pub async fn finalize_and_submit_transaction_dbtx<F, Meta>(
408        &self,
409        dbtx: &mut DatabaseTransaction<'_>,
410        operation_id: OperationId,
411        operation_type: &str,
412        operation_meta_gen: F,
413        tx_builder: TransactionBuilder,
414    ) -> Result<OutPointRange, TransactionSubmitError>
415    where
416        F: Fn(OutPointRange) -> Meta + MaybeSend + MaybeSync + 'static,
417        Meta: serde::Serialize + MaybeSend,
418    {
419        self.client
420            .get()
421            .finalize_and_submit_transaction_dbtx(
422                &mut dbtx.global_dbtx(self.global_dbtx_access_token),
423                operation_id,
424                operation_type,
425                Box::new(move |out_point_range| {
426                    serde_json::to_value(operation_meta_gen(out_point_range)).expect("Can't fail")
427                }),
428                tx_builder,
429            )
430            .await
431    }
432
433    /// Computes the fee that finalizing and submitting a transaction with the
434    /// explicit items described by `request` would incur, as a dry-run over the
435    /// client's current funds, without submitting anything. See
436    /// `Client::fee_quote`.
437    ///
438    /// Summarize the explicit inputs/outputs the operation would submit (their
439    /// gross amounts and federation fees) in `request`; the returned breakdown
440    /// adds the change the primary module's balancing would produce.
441    pub async fn fee_quote(
442        &self,
443        operation_id: OperationId,
444        request: FeeQuoteRequest,
445    ) -> Result<FeeQuote, TransactionSubmitError> {
446        self.client.get().fee_quote(operation_id, request).await
447    }
448
449    /// The client's Bitcoin balance, held by the primary module. See
450    /// `Client::get_balance_for_btc`.
451    pub async fn get_balance_for_btc(&self) -> Result<Amount, ModuleLookupError> {
452        self.client
453            .get()
454            .get_balance_for_unit(AmountUnit::BITCOIN)
455            .await
456    }
457
458    pub async fn transaction_updates(&self, operation_id: OperationId) -> TransactionUpdates {
459        self.client.get().transaction_updates(operation_id).await
460    }
461
462    pub async fn await_primary_module_outputs(
463        &self,
464        operation_id: OperationId,
465        // TODO: make `impl Iterator<Item = ...>`
466        outputs: Vec<OutPoint>,
467    ) -> Result<(), TransactionSubmitError> {
468        self.client
469            .get()
470            .await_primary_module_outputs(operation_id, outputs)
471            .await
472    }
473
474    // TODO: unify with `Self::get_operation`
475    pub async fn get_operation(
476        &self,
477        operation_id: OperationId,
478    ) -> Result<oplog::OperationLogEntry, OperationLookupError> {
479        let operation = self
480            .client
481            .get()
482            .operation_log()
483            .get_operation(operation_id)
484            .await
485            .ok_or(OperationNotFoundError { operation_id })?;
486
487        if operation.operation_module_kind() != M::kind().as_str() {
488            return Err(OperationLookupError::WrongModuleKind {
489                operation_id,
490                expected: M::kind(),
491                found: operation.operation_module_kind().to_owned(),
492            });
493        }
494
495        Ok(operation)
496    }
497
498    /// Get global db.
499    ///
500    /// Only intended for internal use (private).
501    fn global_db(&self) -> fedimint_core::db::Database {
502        let db = Clone::clone(self.client.get().db());
503
504        db.ensure_global()
505            .expect("global_db must always return a global db");
506
507        db
508    }
509
510    pub fn module_db(&self) -> &Database {
511        self.module_db
512            .ensure_isolated()
513            .expect("module_db must always return isolated db");
514        &self.module_db
515    }
516
517    /// Read a portion of the client's event log, starting at `pos` (or the
518    /// beginning of the log if `None`) and returning up to `limit` entries.
519    pub async fn get_event_log(
520        &self,
521        pos: Option<EventLogId>,
522        limit: u64,
523    ) -> Vec<PersistedLogEntry> {
524        self.global_db()
525            .begin_transaction_nc()
526            .await
527            .get_event_log(pos, limit)
528            .await
529    }
530
531    pub async fn has_active_states(&self, op_id: OperationId) -> bool {
532        self.client.get().has_active_states(op_id).await
533    }
534
535    /// Waits until no state machine of the operation is running any more:
536    /// every transaction it submitted has been accepted or rejected, and
537    /// every input and output state machine those started, the primary
538    /// module's among them, has reached its final state.
539    ///
540    /// Unlike [`Self::await_primary_module_outputs`] this needs no outputs
541    /// named, and does not report whether they were issued. See
542    /// `Client::await_no_active_states`.
543    pub async fn await_no_active_states(&self, op_id: OperationId) {
544        self.client.get().await_no_active_states(op_id).await;
545    }
546
547    pub async fn operation_exists(&self, op_id: OperationId) -> bool {
548        self.client.get().operation_exists(op_id).await
549    }
550
551    pub async fn get_own_active_states(&self) -> Vec<(M::States, ActiveStateMeta)> {
552        self.client
553            .get()
554            .executor()
555            .get_active_states()
556            .await
557            .into_iter()
558            .filter(|s| s.0.module_instance_id() == self.module_instance_id)
559            .map(|s| {
560                (
561                    Clone::clone(
562                        s.0.as_any()
563                            .downcast_ref::<M::States>()
564                            .expect("incorrect output type passed to module plugin"),
565                    ),
566                    s.1,
567                )
568            })
569            .collect()
570    }
571
572    /// Returns this module's currently active state machines for the operation.
573    pub async fn get_own_operation_active_states(
574        &self,
575        operation_id: OperationId,
576    ) -> Vec<(M::States, ActiveStateMeta)> {
577        let db = self.global_db();
578        let mut dbtx = db.begin_transaction_nc().await;
579
580        self.client
581            .get()
582            .read_operation_active_states(operation_id, self.module_instance_id, &mut dbtx)
583            .await
584            .map(|(key, meta)| {
585                (
586                    Clone::clone(
587                        key.state
588                            .as_any()
589                            .downcast_ref::<M::States>()
590                            .expect("incorrect output type passed to module plugin"),
591                    ),
592                    meta,
593                )
594            })
595            .collect()
596            .await
597    }
598
599    /// Returns this module's previously active state machines for the
600    /// operation.
601    pub async fn get_own_operation_inactive_states(
602        &self,
603        operation_id: OperationId,
604    ) -> Vec<(M::States, InactiveStateMeta)> {
605        let db = self.global_db();
606        let mut dbtx = db.begin_transaction_nc().await;
607
608        self.client
609            .get()
610            .read_operation_inactive_states(operation_id, self.module_instance_id, &mut dbtx)
611            .await
612            .map(|(key, meta)| {
613                (
614                    Clone::clone(
615                        key.state
616                            .as_any()
617                            .downcast_ref::<M::States>()
618                            .expect("incorrect output type passed to module plugin"),
619                    ),
620                    meta,
621                )
622            })
623            .collect()
624            .await
625    }
626
627    pub async fn get_config(&self) -> ClientConfig {
628        self.client.get().config().await
629    }
630
631    /// Returns an invite code for the federation that points to an arbitrary
632    /// guardian server for fetching the config
633    pub async fn get_invite_code(&self) -> InviteCode {
634        let cfg = self.get_config().await.global;
635        self.client
636            .get()
637            .invite_code(
638                *cfg.api_endpoints
639                    .keys()
640                    .next()
641                    .expect("A federation always has at least one guardian"),
642            )
643            .await
644            .expect("The guardian we requested an invite code for exists")
645    }
646
647    pub fn get_internal_payment_markers(
648        &self,
649    ) -> Result<(PublicKey, u64), bitcoin::secp256k1::Error> {
650        self.client.get().get_internal_payment_markers()
651    }
652
653    /// This method starts n state machines with given operation id without a
654    /// corresponding transaction
655    pub async fn manual_operation_start(
656        &self,
657        operation_id: OperationId,
658        op_type: &str,
659        operation_meta: impl serde::Serialize + Debug,
660        sms: Vec<DynState>,
661    ) -> Result<(), TransactionSubmitError> {
662        let db = self.module_db();
663        let mut dbtx = db.begin_transaction().await;
664        {
665            let dbtx = &mut dbtx.global_dbtx(self.global_dbtx_access_token);
666
667            self.manual_operation_start_inner(
668                &mut dbtx.to_ref_nc(),
669                operation_id,
670                op_type,
671                operation_meta,
672                sms,
673            )
674            .await?;
675        }
676
677        dbtx.commit_tx_result().await?;
678
679        Ok(())
680    }
681
682    pub async fn manual_operation_start_dbtx(
683        &self,
684        dbtx: &mut DatabaseTransaction<'_>,
685        operation_id: OperationId,
686        op_type: &str,
687        operation_meta: impl serde::Serialize + Debug,
688        sms: Vec<DynState>,
689    ) -> Result<(), OperationAlreadyExistsError> {
690        self.manual_operation_start_inner(
691            &mut dbtx.global_dbtx(self.global_dbtx_access_token),
692            operation_id,
693            op_type,
694            operation_meta,
695            sms,
696        )
697        .await
698    }
699
700    /// See [`Self::manual_operation_start`], just inside a database
701    /// transaction.
702    async fn manual_operation_start_inner(
703        &self,
704        dbtx: &mut DatabaseTransaction<'_>,
705        operation_id: OperationId,
706        op_type: &str,
707        operation_meta: impl serde::Serialize + Debug,
708        sms: Vec<DynState>,
709    ) -> Result<(), OperationAlreadyExistsError> {
710        dbtx.ensure_global()
711            .expect("Must deal with global dbtx here");
712
713        if self
714            .client
715            .get()
716            .operation_log()
717            .get_operation_dbtx(&mut dbtx.to_ref_nc(), operation_id)
718            .await
719            .is_some()
720        {
721            return Err(OperationAlreadyExistsError { operation_id });
722        }
723
724        self.client
725            .get()
726            .operation_log()
727            .add_operation_log_entry_dbtx(
728                &mut dbtx.to_ref_nc(),
729                operation_id,
730                op_type,
731                serde_json::to_value(operation_meta).expect("Can't fail"),
732            )
733            .await;
734
735        self.client
736            .get()
737            .executor()
738            .add_state_machines_dbtx(&mut dbtx.to_ref_nc(), sms)
739            .await
740            .expect("State machine is valid");
741
742        Ok(())
743    }
744
745    /// Resolve an operation to its cached outcome, or to a caching update
746    /// stream built by `stream_gen`.
747    ///
748    /// `is_terminal` reports whether an update is a final state of the
749    /// operation; only terminal updates are cached as the durable outcome (a
750    /// stream that ends early on a non-terminal update caches nothing). The
751    /// same predicate also validates outcomes READ from the cache: a cached
752    /// outcome that fails to deserialize into `U` (e.g. one written by an
753    /// incompatible earlier version) or that is non-terminal (one frozen by a
754    /// previous version that cached whatever update a stream ended on) is
755    /// discarded with a warning and the state is rebuilt from the update
756    /// stream — whose terminal end then overwrites the stale cache — instead
757    /// of short-circuiting subscribers to it or panicking.
758    pub fn outcome_or_updates<U, S>(
759        &self,
760        operation: &OperationLogEntry,
761        operation_id: OperationId,
762        is_terminal: impl Fn(&U) -> bool + MaybeSend + MaybeSync + 'static,
763        stream_gen: impl FnOnce() -> S + MaybeSend + 'static,
764    ) -> UpdateStreamOrOutcome<U>
765    where
766        U: Clone + Serialize + DeserializeOwned + Debug + MaybeSend + MaybeSync + 'static,
767        S: Stream<Item = U> + MaybeSend + 'static,
768    {
769        use futures::StreamExt;
770        match operation.try_outcome::<U>() {
771            Ok(Some(outcome)) if is_terminal(&outcome) => {
772                return UpdateStreamOrOutcome::Outcome(outcome);
773            }
774            Ok(Some(_non_terminal)) => {
775                warn!(
776                    target: LOG_CLIENT,
777                    "Cached operation outcome is not a terminal update (cached by a previous \
778                     version); rebuilding it from the update stream"
779                );
780            }
781            Ok(None) => {}
782            Err(err) => {
783                warn!(
784                    target: LOG_CLIENT,
785                    err = %err.fmt_compact(),
786                    "Cached operation outcome failed to deserialize; rebuilding it from the update stream"
787                );
788            }
789        }
790        let stream = self
791            .client
792            .get()
793            .operation_log()
794            .caching_operation_update_stream(
795                operation_id,
796                Box::new(move || {
797                    let stream_gen = stream_gen();
798                    Box::pin(
799                        stream_gen.map(move |item| serde_json::to_value(item).expect("Can't fail")),
800                    )
801                }),
802                Box::new(move |update| {
803                    // The update was serialized from a `U` moments ago in this
804                    // process, so deserialization only fails if `U` is not
805                    // round-trip-safe; treat that conservatively as non-terminal
806                    // (skip caching) rather than panicking inside the stream.
807                    serde_json::from_value::<U>(update.clone())
808                        .map(|update| is_terminal(&update))
809                        .unwrap_or(false)
810                }),
811            );
812        UpdateStreamOrOutcome::UpdateStream(Box::pin(
813            stream.map(|u| serde_json::from_value::<U>(u).expect("Can't fail")),
814        ))
815    }
816
817    pub async fn claim_inputs<I, S>(
818        &self,
819        dbtx: &mut DatabaseTransaction<'_>,
820        inputs: ClientInputBundle<I, S>,
821        operation_id: OperationId,
822    ) -> Result<OutPointRange, TransactionSubmitError>
823    where
824        I: IInput + MaybeSend + MaybeSync + 'static,
825        S: sm::IState + MaybeSend + MaybeSync + 'static,
826    {
827        self.claim_inputs_dyn(dbtx, inputs.into_instanceless(), operation_id)
828            .await
829    }
830
831    async fn claim_inputs_dyn(
832        &self,
833        dbtx: &mut DatabaseTransaction<'_>,
834        inputs: InstancelessDynClientInputBundle,
835        operation_id: OperationId,
836    ) -> Result<OutPointRange, TransactionSubmitError> {
837        let tx_builder =
838            TransactionBuilder::new().with_inputs(inputs.into_dyn(self.module_instance_id));
839
840        self.client
841            .get()
842            .finalize_and_submit_transaction_inner(
843                &mut dbtx.global_dbtx(self.global_dbtx_access_token),
844                operation_id,
845                tx_builder,
846            )
847            .await
848    }
849
850    pub async fn add_state_machines_dbtx(
851        &self,
852        dbtx: &mut DatabaseTransaction<'_>,
853        states: Vec<DynState>,
854    ) -> AddStateMachinesResult {
855        self.client
856            .get()
857            .executor()
858            .add_state_machines_dbtx(&mut dbtx.global_dbtx(self.global_dbtx_access_token), states)
859            .await
860    }
861
862    /// Reads an operation log entry within an ongoing database transaction.
863    ///
864    /// Unlike [`Self::get_operation`] this observes writes made earlier in
865    /// `dbtx`, which is what a caller needs to decide atomically whether it is
866    /// about to create an operation that already exists.
867    pub async fn get_operation_dbtx(
868        &self,
869        dbtx: &mut DatabaseTransaction<'_>,
870        operation_id: OperationId,
871    ) -> Option<oplog::OperationLogEntry> {
872        self.client
873            .get()
874            .operation_log()
875            .get_operation_dbtx(
876                &mut dbtx.global_dbtx(self.global_dbtx_access_token),
877                operation_id,
878            )
879            .await
880    }
881
882    pub async fn add_operation_log_entry_dbtx(
883        &self,
884        dbtx: &mut DatabaseTransaction<'_>,
885        operation_id: OperationId,
886        operation_type: &str,
887        operation_meta: impl serde::Serialize,
888    ) {
889        self.client
890            .get()
891            .operation_log()
892            .add_operation_log_entry_dbtx(
893                &mut dbtx.global_dbtx(self.global_dbtx_access_token),
894                operation_id,
895                operation_type,
896                serde_json::to_value(operation_meta).expect("Can't fail"),
897            )
898            .await;
899    }
900
901    pub async fn log_event<E, Cap>(&self, dbtx: &mut DatabaseTransaction<'_, Cap>, event: E)
902    where
903        E: Event + Send,
904        Cap: Send,
905    {
906        if <E as Event>::MODULE != Some(<M as ClientModule>::kind()) {
907            warn!(
908                target: LOG_CLIENT,
909                module_kind = %<M as ClientModule>::kind(),
910                event_module = ?<E as Event>::MODULE,
911                "Client module logging events of different module than its own. This might become an error in the future."
912            );
913        }
914        self.client
915            .get()
916            .log_event_json(
917                &mut dbtx.global_dbtx(self.global_dbtx_access_token).to_ref_nc(),
918                <E as Event>::MODULE,
919                self.module_instance_id,
920                <E as Event>::KIND,
921                serde_json::to_value(event).expect("Can't fail"),
922                <E as Event>::PERSISTENCE,
923            )
924            .await;
925    }
926}
927
928/// Priority module priority (lower number is higher priority)
929#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
930pub struct PrimaryModulePriority(u64);
931
932impl PrimaryModulePriority {
933    pub const HIGH: Self = Self(100);
934    pub const LOW: Self = Self(10000);
935
936    pub fn custom(prio: u64) -> Self {
937        Self(prio)
938    }
939}
940/// Which amount units this module supports being primary for
941pub enum PrimaryModuleSupport {
942    /// Potentially any unit
943    Any { priority: PrimaryModulePriority },
944    /// Some units supported by the module
945    Selected {
946        priority: PrimaryModulePriority,
947        units: BTreeSet<AmountUnit>,
948    },
949    /// None
950    None,
951}
952
953impl PrimaryModuleSupport {
954    pub fn selected<const N: usize>(
955        priority: PrimaryModulePriority,
956        units: [AmountUnit; N],
957    ) -> Self {
958        Self::Selected {
959            priority,
960            units: BTreeSet::from(units),
961        }
962    }
963}
964
965/// Fedimint module client
966#[apply(async_trait_maybe_send!)]
967pub trait ClientModule: Debug + MaybeSend + MaybeSync + 'static {
968    type Init: ClientModuleInit;
969
970    /// Common module types shared between client and server
971    type Common: ModuleCommon;
972
973    /// Data stored in regular backups so that restoring doesn't have to start
974    /// from epoch 0
975    type Backup: ModuleBackup;
976
977    /// Data and API clients available to state machine transitions of this
978    /// module
979    type ModuleStateMachineContext: Context;
980
981    /// All possible states this client can submit to the executor
982    type States: State<ModuleContext = Self::ModuleStateMachineContext>
983        + IntoDynInstance<DynType = DynState>;
984
985    fn decoder() -> Decoder {
986        let mut decoder_builder = Self::Common::decoder_builder();
987        decoder_builder.with_decodable_type::<Self::States>();
988        decoder_builder.with_decodable_type::<Self::Backup>();
989        decoder_builder.build()
990    }
991
992    fn kind() -> ModuleKind {
993        <<<Self as ClientModule>::Init as ModuleInit>::Common as CommonModuleInit>::KIND
994    }
995
996    fn context(&self) -> Self::ModuleStateMachineContext;
997
998    /// Initialize client.
999    ///
1000    /// Called by the core client code on start, after [`ClientContext`] is
1001    /// fully initialized, so unlike during [`ClientModuleInit::init`],
1002    /// access to global client is allowed.
1003    async fn start(&self) {}
1004
1005    /// Runs a `fedimint-cli module` command, given as its arguments.
1006    ///
1007    /// # Errors
1008    ///
1009    /// The default body fails with [`ClientModuleError::Unsupported`], for a
1010    /// module that has no commands.
1011    async fn handle_cli_command(
1012        &self,
1013        _args: &[ffi::OsString],
1014    ) -> Result<serde_json::Value, ClientModuleError> {
1015        Err(ClientModuleError::Unsupported {
1016            kind: <Self as ClientModule>::kind(),
1017            operation: "handle_cli_command",
1018        })
1019    }
1020
1021    /// Serves a JSON request, such as one `fedimint-client-rpc` forwards.
1022    ///
1023    /// # Errors
1024    ///
1025    /// The stream yields the module's failures. The default body yields a
1026    /// single [`ClientModuleError::Unsupported`], for a module that serves no
1027    /// requests.
1028    async fn handle_rpc(
1029        &self,
1030        _method: String,
1031        _request: serde_json::Value,
1032    ) -> BoxStream<'_, Result<serde_json::Value, ClientModuleError>> {
1033        Box::pin(futures::stream::once(std::future::ready(Err(
1034            ClientModuleError::Unsupported {
1035                kind: <Self as ClientModule>::kind(),
1036                operation: "handle_rpc",
1037            },
1038        ))))
1039    }
1040
1041    /// Returns the fee the processing of this input requires.
1042    ///
1043    /// If the semantics of a given input aren't known this function returns
1044    /// `None`, this only happens if a future version of Fedimint introduces a
1045    /// new input variant. For clients this should only be the case when
1046    /// processing transactions created by other users, so the result of
1047    /// this function can be `unwrap`ped whenever dealing with inputs
1048    /// generated by ourselves.
1049    fn input_fee(
1050        &self,
1051        amount: &Amounts,
1052        input: &<Self::Common as ModuleCommon>::Input,
1053    ) -> Option<Amounts>;
1054
1055    /// Returns the fee the processing of this output requires.
1056    ///
1057    /// If the semantics of a given output aren't known this function returns
1058    /// `None`, this only happens if a future version of Fedimint introduces a
1059    /// new output variant. For clients this should only be the case when
1060    /// processing transactions created by other users, so the result of
1061    /// this function can be `unwrap`ped whenever dealing with inputs
1062    /// generated by ourselves.
1063    fn output_fee(
1064        &self,
1065        amount: &Amounts,
1066        output: &<Self::Common as ModuleCommon>::Output,
1067    ) -> Option<Amounts>;
1068
1069    fn supports_backup(&self) -> bool {
1070        false
1071    }
1072
1073    /// The module's part of a client backup.
1074    ///
1075    /// # Errors
1076    ///
1077    /// The default body fails with [`ClientModuleError::Unsupported`]; a
1078    /// module whose [`Self::supports_backup`] is `true` must override it.
1079    async fn backup(&self) -> Result<Self::Backup, ClientModuleError> {
1080        Err(ClientModuleError::Unsupported {
1081            kind: <Self as ClientModule>::kind(),
1082            operation: "backup",
1083        })
1084    }
1085
1086    /// Does this module support being a primary module
1087    ///
1088    /// If it does it must implement:
1089    ///
1090    /// * [`Self::create_final_inputs_and_outputs`]
1091    /// * [`Self::await_primary_module_output`]
1092    /// * [`Self::get_balance`]
1093    /// * [`Self::subscribe_balance_changes`]
1094    fn supports_being_primary(&self) -> PrimaryModuleSupport {
1095        PrimaryModuleSupport::None
1096    }
1097
1098    /// Creates all inputs and outputs necessary to balance the transaction.
1099    ///
1100    /// A returned input also contains:
1101    /// * A set of private keys belonging to the input for signing the
1102    ///   transaction
1103    /// * A closure that generates states belonging to the input. This closure
1104    ///   takes the transaction id of the transaction in which the input was
1105    ///   used and the input index as input since these cannot be known at time
1106    ///   of calling `create_funding_input` and have to be injected later.
1107    ///
1108    /// A returned output also contains:
1109    /// * A closure that generates states belonging to the output. This closure
1110    ///   takes the transaction id of the transaction in which the output was
1111    ///   used and the output index as input since these cannot be known at time
1112    ///   of calling `create_change_output` and have to be injected later.
1113    ///
1114    /// # Errors
1115    ///
1116    /// A module whose funds cannot cover the transaction must fail with
1117    /// [`ClientModuleError::InsufficientBalance`]: the client reports that as
1118    /// [`TransactionSubmitError::InsufficientFunds`], and any other failure as
1119    /// [`TransactionSubmitError::PrimaryModule`]. The default body fails with
1120    /// [`ClientModuleError::Unsupported`].
1121    async fn create_final_inputs_and_outputs(
1122        &self,
1123        _dbtx: &mut DatabaseTransaction<'_>,
1124        _operation_id: OperationId,
1125        _unit: AmountUnit,
1126        _input_amount: Amount,
1127        _output_amount: Amount,
1128    ) -> Result<
1129        (
1130            ClientInputBundle<<Self::Common as ModuleCommon>::Input, Self::States>,
1131            ClientOutputBundle<<Self::Common as ModuleCommon>::Output, Self::States>,
1132        ),
1133        ClientModuleError,
1134    > {
1135        Err(ClientModuleError::Unsupported {
1136            kind: <Self as ClientModule>::kind(),
1137            operation: "create_final_inputs_and_outputs",
1138        })
1139    }
1140
1141    /// Waits for the funds from an output created by
1142    /// [`Self::create_final_inputs_and_outputs`] to become available. This
1143    /// function returning typically implies a change in the output of
1144    /// [`Self::get_balance`].
1145    ///
1146    /// # Errors
1147    ///
1148    /// Fails if the output does not become available, for example because
1149    /// the federation rejected its transaction. The default body fails with
1150    /// [`ClientModuleError::Unsupported`].
1151    async fn await_primary_module_output(
1152        &self,
1153        _operation_id: OperationId,
1154        _out_point: OutPoint,
1155    ) -> Result<(), ClientModuleError> {
1156        Err(ClientModuleError::Unsupported {
1157            kind: <Self as ClientModule>::kind(),
1158            operation: "await_primary_module_output",
1159        })
1160    }
1161
1162    /// Returns the balance held by this module and available for funding
1163    /// transactions.
1164    async fn get_balance(&self, _dbtx: &mut DatabaseTransaction<'_>, _unit: AmountUnit) -> Amount {
1165        unimplemented!()
1166    }
1167
1168    /// Returns the balance held by this module and available for funding
1169    /// transactions.
1170    async fn get_balances(&self, _dbtx: &mut DatabaseTransaction<'_>) -> Amounts {
1171        unimplemented!()
1172    }
1173
1174    /// Returns a stream that will output the updated module balance each time
1175    /// it changes.
1176    async fn subscribe_balance_changes(&self) -> BoxStream<'static, ()> {
1177        unimplemented!()
1178    }
1179
1180    /// Leave the federation
1181    ///
1182    /// While technically there's nothing stopping the client from just
1183    /// abandoning Federation at any point by deleting all the related
1184    /// local data, it is useful to make sure it's safe beforehand.
1185    ///
1186    /// This call indicates the desire of the caller client code
1187    /// to orderly and safely leave the Federation by this module instance.
1188    /// The goal of the implementations is to fulfil that wish,
1189    /// giving prompt and informative feedback if it's not yet possible.
1190    ///
1191    /// The client module implementation should handle the request
1192    /// and return as fast as possible avoiding blocking for longer than
1193    /// necessary. This would usually involve some combination of:
1194    ///
1195    /// * recording the state of being in process of leaving the Federation to
1196    ///   prevent initiating new conditions that could delay its completion;
1197    /// * performing any fast to complete cleanup/exit logic;
1198    /// * initiating any time-consuming logic (e.g. canceling outstanding
1199    ///   contracts), as background jobs, tasks machines, etc.
1200    /// * checking for any conditions indicating it might not be safe to leave
1201    ///   at the moment.
1202    ///
1203    /// This function should return `Ok` only if from the perspective
1204    /// of this module instance, it is safe to delete client data and
1205    /// stop using it, with no further actions (like background jobs) required
1206    /// to complete.
1207    ///
1208    /// This function should return an error if it's not currently possible
1209    /// to safely (e.g. without losing funds) leave the Federation.
1210    /// It should avoid running indefinitely trying to complete any cleanup
1211    /// actions necessary to reach a clean state, preferring spawning new
1212    /// state machines and returning an informative error about cleanup
1213    /// still in progress.
1214    ///
1215    /// If any internal task needs to complete, any user action is required,
1216    /// or even external condition needs to be met this function
1217    /// should return a `Err`.
1218    ///
1219    /// Notably modules should not disable interaction that might be necessary
1220    /// for the user (possibly through other modules) to leave the Federation.
1221    /// In particular a Mint module should retain ability to create new notes,
1222    /// and LN module should retain ability to send funds out.
1223    ///
1224    /// Calling code must NOT assume that a module that once returned `Ok`,
1225    /// will not return `Err` at later point. E.g. a Mint module might have
1226    /// no outstanding balance at first, but other modules winding down
1227    /// might "cash-out" to Ecash.
1228    ///
1229    /// Before leaving the Federation and deleting any state the calling code
1230    /// must collect a full round of `Ok` from all the modules.
1231    ///
1232    /// Calling code should allow the user to override and ignore any
1233    /// outstanding errors, after sufficient amount of warnings. Ideally,
1234    /// this should be done on per-module basis, to avoid mistakes.
1235    ///
1236    /// # Errors
1237    ///
1238    /// The default body fails with [`ClientModuleError::Unsupported`], since a
1239    /// module that does not implement this cannot tell whether leaving is
1240    /// safe.
1241    async fn leave(&self, _dbtx: &mut DatabaseTransaction<'_>) -> Result<(), ClientModuleError> {
1242        Err(ClientModuleError::Unsupported {
1243            kind: <Self as ClientModule>::kind(),
1244            operation: "leave",
1245        })
1246    }
1247}
1248
1249/// Type-erased version of [`ClientModule`]
1250#[apply(async_trait_maybe_send!)]
1251pub trait IClientModule: Debug {
1252    fn as_any(&self) -> &(maybe_add_send_sync!(dyn std::any::Any));
1253
1254    fn decoder(&self) -> Decoder;
1255
1256    fn context(&self, instance: ModuleInstanceId) -> DynContext;
1257
1258    async fn start(&self);
1259
1260    async fn handle_cli_command(
1261        &self,
1262        args: &[ffi::OsString],
1263    ) -> Result<serde_json::Value, ClientModuleError>;
1264
1265    async fn handle_rpc(
1266        &self,
1267        method: String,
1268        request: serde_json::Value,
1269    ) -> BoxStream<'_, Result<serde_json::Value, ClientModuleError>>;
1270
1271    fn input_fee(&self, amount: &Amounts, input: &DynInput) -> Option<Amounts>;
1272
1273    fn output_fee(&self, amount: &Amounts, output: &DynOutput) -> Option<Amounts>;
1274
1275    fn supports_backup(&self) -> bool;
1276
1277    async fn backup(
1278        &self,
1279        module_instance_id: ModuleInstanceId,
1280    ) -> Result<DynModuleBackup, ClientModuleError>;
1281
1282    fn supports_being_primary(&self) -> PrimaryModuleSupport;
1283
1284    async fn create_final_inputs_and_outputs(
1285        &self,
1286        module_instance: ModuleInstanceId,
1287        dbtx: &mut DatabaseTransaction<'_>,
1288        operation_id: OperationId,
1289        unit: AmountUnit,
1290        input_amount: Amount,
1291        output_amount: Amount,
1292    ) -> Result<(ClientInputBundle, ClientOutputBundle), ClientModuleError>;
1293
1294    async fn await_primary_module_output(
1295        &self,
1296        operation_id: OperationId,
1297        out_point: OutPoint,
1298    ) -> Result<(), ClientModuleError>;
1299
1300    async fn get_balance(
1301        &self,
1302        module_instance: ModuleInstanceId,
1303        dbtx: &mut DatabaseTransaction<'_>,
1304        unit: AmountUnit,
1305    ) -> Amount;
1306
1307    async fn subscribe_balance_changes(&self) -> BoxStream<'static, ()>;
1308}
1309
1310#[apply(async_trait_maybe_send!)]
1311impl<T> IClientModule for T
1312where
1313    T: ClientModule,
1314{
1315    fn as_any(&self) -> &(maybe_add_send_sync!(dyn Any)) {
1316        self
1317    }
1318
1319    fn decoder(&self) -> Decoder {
1320        T::decoder()
1321    }
1322
1323    fn context(&self, instance: ModuleInstanceId) -> DynContext {
1324        DynContext::from_typed(instance, <T as ClientModule>::context(self))
1325    }
1326
1327    async fn start(&self) {
1328        <T as ClientModule>::start(self).await;
1329    }
1330
1331    async fn handle_cli_command(
1332        &self,
1333        args: &[ffi::OsString],
1334    ) -> Result<serde_json::Value, ClientModuleError> {
1335        <T as ClientModule>::handle_cli_command(self, args).await
1336    }
1337
1338    async fn handle_rpc(
1339        &self,
1340        method: String,
1341        request: serde_json::Value,
1342    ) -> BoxStream<'_, Result<serde_json::Value, ClientModuleError>> {
1343        <T as ClientModule>::handle_rpc(self, method, request).await
1344    }
1345
1346    fn input_fee(&self, amount: &Amounts, input: &DynInput) -> Option<Amounts> {
1347        <T as ClientModule>::input_fee(
1348            self,
1349            amount,
1350            input
1351                .as_any()
1352                .downcast_ref()
1353                .expect("Dispatched to correct module"),
1354        )
1355    }
1356
1357    fn output_fee(&self, amount: &Amounts, output: &DynOutput) -> Option<Amounts> {
1358        <T as ClientModule>::output_fee(
1359            self,
1360            amount,
1361            output
1362                .as_any()
1363                .downcast_ref()
1364                .expect("Dispatched to correct module"),
1365        )
1366    }
1367
1368    fn supports_backup(&self) -> bool {
1369        <T as ClientModule>::supports_backup(self)
1370    }
1371
1372    async fn backup(
1373        &self,
1374        module_instance_id: ModuleInstanceId,
1375    ) -> Result<DynModuleBackup, ClientModuleError> {
1376        Ok(DynModuleBackup::from_typed(
1377            module_instance_id,
1378            <T as ClientModule>::backup(self).await?,
1379        ))
1380    }
1381
1382    fn supports_being_primary(&self) -> PrimaryModuleSupport {
1383        <T as ClientModule>::supports_being_primary(self)
1384    }
1385
1386    async fn create_final_inputs_and_outputs(
1387        &self,
1388        module_instance: ModuleInstanceId,
1389        dbtx: &mut DatabaseTransaction<'_>,
1390        operation_id: OperationId,
1391        unit: AmountUnit,
1392        input_amount: Amount,
1393        output_amount: Amount,
1394    ) -> Result<(ClientInputBundle, ClientOutputBundle), ClientModuleError> {
1395        let (inputs, outputs) = <T as ClientModule>::create_final_inputs_and_outputs(
1396            self,
1397            &mut dbtx.to_ref_with_prefix_module_id(module_instance).0,
1398            operation_id,
1399            unit,
1400            input_amount,
1401            output_amount,
1402        )
1403        .await?;
1404
1405        let inputs = inputs.into_dyn(module_instance);
1406
1407        let outputs = outputs.into_dyn(module_instance);
1408
1409        Ok((inputs, outputs))
1410    }
1411
1412    async fn await_primary_module_output(
1413        &self,
1414        operation_id: OperationId,
1415        out_point: OutPoint,
1416    ) -> Result<(), ClientModuleError> {
1417        <T as ClientModule>::await_primary_module_output(self, operation_id, out_point).await
1418    }
1419
1420    async fn get_balance(
1421        &self,
1422        module_instance: ModuleInstanceId,
1423        dbtx: &mut DatabaseTransaction<'_>,
1424        unit: AmountUnit,
1425    ) -> Amount {
1426        <T as ClientModule>::get_balance(
1427            self,
1428            &mut dbtx.to_ref_with_prefix_module_id(module_instance).0,
1429            unit,
1430        )
1431        .await
1432    }
1433
1434    async fn subscribe_balance_changes(&self) -> BoxStream<'static, ()> {
1435        <T as ClientModule>::subscribe_balance_changes(self).await
1436    }
1437}
1438
1439dyn_newtype_define!(
1440    #[derive(Clone)]
1441    pub DynClientModule(Arc<IClientModule>)
1442);
1443
1444impl AsRef<maybe_add_send_sync!(dyn IClientModule + 'static)> for DynClientModule {
1445    fn as_ref(&self) -> &maybe_add_send_sync!(dyn IClientModule + 'static) {
1446        self.inner.as_ref()
1447    }
1448}
1449
1450// Re-export types from fedimint_core
1451pub use fedimint_core::{IdxRange, OutPointRange, OutPointRangeIter};
1452
1453pub type StateGenerator<S> = Arc<maybe_add_send_sync!(dyn Fn(OutPointRange) -> Vec<S> + 'static)>;