use std::fmt::Debug;
use std::sync::Arc;
use fedimint_core::module::audit::Audit;
use fedimint_core::{apply, async_trait_maybe_send, OutPoint, PeerId};
use super::ModuleKind;
use crate::core::{
Any, Decoder, DynInput, DynInputError, DynModuleConsensusItem, DynOutput, DynOutputError,
DynOutputOutcome,
};
use crate::db::DatabaseTransaction;
use crate::dyn_newtype_define;
use crate::module::registry::ModuleInstanceId;
use crate::module::{
ApiEndpoint, ApiEndpointContext, ApiRequestErased, InputMeta, ModuleCommon, ServerModule,
TransactionItemAmount,
};
#[apply(async_trait_maybe_send!)]
pub trait IServerModule: Debug {
fn as_any(&self) -> &dyn Any;
fn decoder(&self) -> Decoder;
fn module_kind(&self) -> ModuleKind;
async fn consensus_proposal(
&self,
dbtx: &mut DatabaseTransaction<'_>,
module_instance_id: ModuleInstanceId,
) -> Vec<DynModuleConsensusItem>;
async fn process_consensus_item<'a, 'b>(
&self,
dbtx: &mut DatabaseTransaction<'a>,
consensus_item: &'b DynModuleConsensusItem,
peer_id: PeerId,
) -> anyhow::Result<()>;
fn verify_input(&self, input: &DynInput) -> Result<(), DynInputError>;
async fn process_input<'a, 'b, 'c>(
&'a self,
dbtx: &mut DatabaseTransaction<'c>,
input: &'b DynInput,
) -> Result<InputMeta, DynInputError>;
async fn process_output<'a>(
&self,
dbtx: &mut DatabaseTransaction<'a>,
output: &DynOutput,
out_point: OutPoint,
) -> Result<TransactionItemAmount, DynOutputError>;
async fn output_status(
&self,
dbtx: &mut DatabaseTransaction<'_>,
out_point: OutPoint,
module_instance_id: ModuleInstanceId,
) -> Option<DynOutputOutcome>;
async fn audit(
&self,
dbtx: &mut DatabaseTransaction<'_>,
audit: &mut Audit,
module_instance_id: ModuleInstanceId,
);
fn api_endpoints(&self) -> Vec<ApiEndpoint<DynServerModule>>;
}
dyn_newtype_define!(
#[derive(Clone)]
pub DynServerModule(Arc<IServerModule>)
);
#[apply(async_trait_maybe_send!)]
impl<T> IServerModule for T
where
T: ServerModule + 'static + Sync,
{
fn decoder(&self) -> Decoder {
<T::Common as ModuleCommon>::decoder_builder().build()
}
fn as_any(&self) -> &dyn Any {
self
}
fn module_kind(&self) -> ModuleKind {
<Self as ServerModule>::module_kind()
}
async fn consensus_proposal(
&self,
dbtx: &mut DatabaseTransaction<'_>,
module_instance_id: ModuleInstanceId,
) -> Vec<DynModuleConsensusItem> {
<Self as ServerModule>::consensus_proposal(self, dbtx)
.await
.into_iter()
.map(|v| DynModuleConsensusItem::from_typed(module_instance_id, v))
.collect()
}
async fn process_consensus_item<'a, 'b>(
&self,
dbtx: &mut DatabaseTransaction<'a>,
consensus_item: &'b DynModuleConsensusItem,
peer_id: PeerId,
) -> anyhow::Result<()> {
<Self as ServerModule>::process_consensus_item(
self,
dbtx,
Clone::clone(
consensus_item.as_any()
.downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::ConsensusItem>()
.expect("incorrect consensus item type passed to module plugin"),
),
peer_id
)
.await
}
fn verify_input(&self, input: &DynInput) -> Result<(), DynInputError> {
<Self as ServerModule>::verify_input(
self,
input
.as_any()
.downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::Input>()
.expect("incorrect input type passed to module plugin"),
)
.map_err(|v| DynInputError::from_typed(input.module_instance_id(), v))
}
async fn process_input<'a, 'b, 'c>(
&'a self,
dbtx: &mut DatabaseTransaction<'c>,
input: &'b DynInput,
) -> Result<InputMeta, DynInputError> {
<Self as ServerModule>::process_input(
self,
dbtx,
input
.as_any()
.downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::Input>()
.expect("incorrect input type passed to module plugin"),
)
.await
.map(Into::into)
.map_err(|v| DynInputError::from_typed(input.module_instance_id(), v))
}
async fn process_output<'a>(
&self,
dbtx: &mut DatabaseTransaction<'a>,
output: &DynOutput,
out_point: OutPoint,
) -> Result<TransactionItemAmount, DynOutputError> {
<Self as ServerModule>::process_output(
self,
dbtx,
output
.as_any()
.downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::Output>()
.expect("incorrect output type passed to module plugin"),
out_point,
)
.await
.map_err(|v| DynOutputError::from_typed(output.module_instance_id(), v))
}
async fn output_status(
&self,
dbtx: &mut DatabaseTransaction<'_>,
out_point: OutPoint,
module_instance_id: ModuleInstanceId,
) -> Option<DynOutputOutcome> {
<Self as ServerModule>::output_status(self, dbtx, out_point)
.await
.map(|v| DynOutputOutcome::from_typed(module_instance_id, v))
}
async fn audit(
&self,
dbtx: &mut DatabaseTransaction<'_>,
audit: &mut Audit,
module_instance_id: ModuleInstanceId,
) {
<Self as ServerModule>::audit(self, dbtx, audit, module_instance_id).await;
}
fn api_endpoints(&self) -> Vec<ApiEndpoint<DynServerModule>> {
<Self as ServerModule>::api_endpoints(self)
.into_iter()
.map(|ApiEndpoint { path, handler }| ApiEndpoint {
path,
handler: Box::new(
move |module: &DynServerModule,
context: ApiEndpointContext<'_>,
value: ApiRequestErased| {
let typed_module = module
.as_any()
.downcast_ref::<T>()
.expect("the dispatcher should always call with the right module");
Box::pin(handler(typed_module, context, value))
},
),
})
.collect()
}
}