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