1use std::fmt::Debug;
8use std::sync::Arc;
9
10use fedimint_core::module::audit::Audit;
11use fedimint_core::{apply, async_trait_maybe_send, OutPoint, PeerId};
12
13use super::ModuleKind;
14use crate::core::{
15 Any, Decoder, DynInput, DynInputError, DynModuleConsensusItem, DynOutput, DynOutputError,
16 DynOutputOutcome,
17};
18use crate::db::DatabaseTransaction;
19use crate::dyn_newtype_define;
20use crate::module::registry::ModuleInstanceId;
21use crate::module::{
22 ApiEndpoint, ApiEndpointContext, ApiRequestErased, InputMeta, ModuleCommon, ServerModule,
23 TransactionItemAmount,
24};
25
26#[apply(async_trait_maybe_send!)]
30pub trait IServerModule: Debug {
31 fn as_any(&self) -> &dyn Any;
32
33 fn decoder(&self) -> Decoder;
35
36 fn module_kind(&self) -> ModuleKind;
37
38 async fn consensus_proposal(
40 &self,
41 dbtx: &mut DatabaseTransaction<'_>,
42 module_instance_id: ModuleInstanceId,
43 ) -> Vec<DynModuleConsensusItem>;
44
45 async fn process_consensus_item<'a, 'b>(
49 &self,
50 dbtx: &mut DatabaseTransaction<'a>,
51 consensus_item: &'b DynModuleConsensusItem,
52 peer_id: PeerId,
53 ) -> anyhow::Result<()>;
54
55 fn verify_input(&self, input: &DynInput) -> Result<(), DynInputError>;
59
60 async fn process_input<'a, 'b, 'c>(
65 &'a self,
66 dbtx: &mut DatabaseTransaction<'c>,
67 input: &'b DynInput,
68 ) -> Result<InputMeta, DynInputError>;
69
70 async fn process_output<'a>(
79 &self,
80 dbtx: &mut DatabaseTransaction<'a>,
81 output: &DynOutput,
82 out_point: OutPoint,
83 ) -> Result<TransactionItemAmount, DynOutputError>;
84
85 async fn output_status(
90 &self,
91 dbtx: &mut DatabaseTransaction<'_>,
92 out_point: OutPoint,
93 module_instance_id: ModuleInstanceId,
94 ) -> Option<DynOutputOutcome>;
95
96 async fn audit(
102 &self,
103 dbtx: &mut DatabaseTransaction<'_>,
104 audit: &mut Audit,
105 module_instance_id: ModuleInstanceId,
106 );
107
108 fn api_endpoints(&self) -> Vec<ApiEndpoint<DynServerModule>>;
113}
114
115dyn_newtype_define!(
116 #[derive(Clone)]
117 pub DynServerModule(Arc<IServerModule>)
118);
119
120#[apply(async_trait_maybe_send!)]
121impl<T> IServerModule for T
122where
123 T: ServerModule + 'static + Sync,
124{
125 fn decoder(&self) -> Decoder {
126 <T::Common as ModuleCommon>::decoder_builder().build()
127 }
128
129 fn as_any(&self) -> &dyn Any {
130 self
131 }
132
133 fn module_kind(&self) -> ModuleKind {
134 <Self as ServerModule>::module_kind()
135 }
136
137 async fn consensus_proposal(
139 &self,
140 dbtx: &mut DatabaseTransaction<'_>,
141 module_instance_id: ModuleInstanceId,
142 ) -> Vec<DynModuleConsensusItem> {
143 <Self as ServerModule>::consensus_proposal(self, dbtx)
144 .await
145 .into_iter()
146 .map(|v| DynModuleConsensusItem::from_typed(module_instance_id, v))
147 .collect()
148 }
149
150 async fn process_consensus_item<'a, 'b>(
154 &self,
155 dbtx: &mut DatabaseTransaction<'a>,
156 consensus_item: &'b DynModuleConsensusItem,
157 peer_id: PeerId,
158 ) -> anyhow::Result<()> {
159 <Self as ServerModule>::process_consensus_item(
160 self,
161 dbtx,
162 Clone::clone(
163 consensus_item.as_any()
164 .downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::ConsensusItem>()
165 .expect("incorrect consensus item type passed to module plugin"),
166 ),
167 peer_id
168 )
169 .await
170 }
171
172 fn verify_input(&self, input: &DynInput) -> Result<(), DynInputError> {
176 <Self as ServerModule>::verify_input(
177 self,
178 input
179 .as_any()
180 .downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::Input>()
181 .expect("incorrect input type passed to module plugin"),
182 )
183 .map_err(|v| DynInputError::from_typed(input.module_instance_id(), v))
184 }
185
186 async fn process_input<'a, 'b, 'c>(
191 &'a self,
192 dbtx: &mut DatabaseTransaction<'c>,
193 input: &'b DynInput,
194 ) -> Result<InputMeta, DynInputError> {
195 <Self as ServerModule>::process_input(
196 self,
197 dbtx,
198 input
199 .as_any()
200 .downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::Input>()
201 .expect("incorrect input type passed to module plugin"),
202 )
203 .await
204 .map(Into::into)
205 .map_err(|v| DynInputError::from_typed(input.module_instance_id(), v))
206 }
207
208 async fn process_output<'a>(
217 &self,
218 dbtx: &mut DatabaseTransaction<'a>,
219 output: &DynOutput,
220 out_point: OutPoint,
221 ) -> Result<TransactionItemAmount, DynOutputError> {
222 <Self as ServerModule>::process_output(
223 self,
224 dbtx,
225 output
226 .as_any()
227 .downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::Output>()
228 .expect("incorrect output type passed to module plugin"),
229 out_point,
230 )
231 .await
232 .map_err(|v| DynOutputError::from_typed(output.module_instance_id(), v))
233 }
234
235 async fn output_status(
240 &self,
241 dbtx: &mut DatabaseTransaction<'_>,
242 out_point: OutPoint,
243 module_instance_id: ModuleInstanceId,
244 ) -> Option<DynOutputOutcome> {
245 <Self as ServerModule>::output_status(self, dbtx, out_point)
246 .await
247 .map(|v| DynOutputOutcome::from_typed(module_instance_id, v))
248 }
249
250 async fn audit(
256 &self,
257 dbtx: &mut DatabaseTransaction<'_>,
258 audit: &mut Audit,
259 module_instance_id: ModuleInstanceId,
260 ) {
261 <Self as ServerModule>::audit(self, dbtx, audit, module_instance_id).await;
262 }
263
264 fn api_endpoints(&self) -> Vec<ApiEndpoint<DynServerModule>> {
265 <Self as ServerModule>::api_endpoints(self)
266 .into_iter()
267 .map(|ApiEndpoint { path, handler }| ApiEndpoint {
268 path,
269 handler: Box::new(
270 move |module: &DynServerModule,
271 context: ApiEndpointContext<'_>,
272 value: ApiRequestErased| {
273 let typed_module = module
274 .as_any()
275 .downcast_ref::<T>()
276 .expect("the dispatcher should always call with the right module");
277 Box::pin(handler(typed_module, context, value))
278 },
279 ),
280 })
281 .collect()
282 }
283}