use fedimint_core::Amount;
use fedimint_core::config::{FederationId, ModuleConfigError};
use fedimint_core::core::{ModuleInstanceId, ModuleKind, OperationId};
use fedimint_core::db::DatabaseError;
use fedimint_core::module::AmountUnit;
use thiserror::Error;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Error)]
pub struct InsufficientBalanceError {
pub requested_amount: Amount,
pub total_amount: Amount,
}
impl std::fmt::Display for InsufficientBalanceError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Insufficient balance: requested {} but only {} available",
self.requested_amount, self.total_amount
)
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum AddStateMachinesError {
#[error("State already exists in database")]
StateAlreadyExists,
#[error("Unknown module instance {module_instance_id}")]
UnknownModule {
module_instance_id: ModuleInstanceId,
},
#[error("State is already terminal, adding it to the executor does not make sense")]
StateAlreadyTerminal,
#[error("Database error")]
Database(#[from] DatabaseError),
}
#[derive(Debug, Error)]
#[error("An operation with id {} already exists", .operation_id.fmt_short())]
pub struct OperationAlreadyExistsError {
pub operation_id: OperationId,
}
#[derive(Debug, Error)]
#[error("No operation with id {}", .operation_id.fmt_short())]
pub struct OperationNotFoundError {
pub operation_id: OperationId,
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum OperationLookupError {
#[error("The operation does not exist")]
NotFound(#[from] OperationNotFoundError),
#[error(
"Operation {} was started by module {found}, not {expected}",
.operation_id.fmt_short()
)]
WrongModuleKind {
operation_id: OperationId,
expected: ModuleKind,
found: String,
},
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum TransactionSubmitError {
#[error("The operation already exists")]
OperationAlreadyExists(#[from] OperationAlreadyExistsError),
#[error("The transaction is {size} bytes, over the limit of {max}")]
TransactionTooLarge {
size: usize,
max: usize,
},
#[error("No primary module for unit {unit}")]
NoPrimaryModule {
unit: AmountUnit,
},
#[error("The primary module failed")]
PrimaryModule(#[source] ClientModuleError),
#[error("Database error")]
Database(#[from] DatabaseError),
#[error("Failed to add the transaction's state machines")]
StateMachines(#[from] AddStateMachinesError),
#[error("Insufficient funds")]
InsufficientFunds(#[from] InsufficientBalanceError),
}
impl TransactionSubmitError {
pub fn is_insufficient_funds(&self) -> bool {
matches!(self, Self::InsufficientFunds(_))
}
}
impl From<ClientModuleError> for TransactionSubmitError {
fn from(error: ClientModuleError) -> Self {
match error {
ClientModuleError::InsufficientBalance(error) => Self::InsufficientFunds(error),
other => Self::PrimaryModule(other),
}
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ClientModuleError {
#[error("The primary module's balance cannot fund the transaction")]
InsufficientBalance(#[from] InsufficientBalanceError),
#[error("Module {kind} does not implement {operation}")]
Unsupported {
kind: ModuleKind,
operation: &'static str,
},
#[error(transparent)]
Other(Box<dyn std::error::Error + Send + Sync>),
}
impl ClientModuleError {
pub fn other<E>(error: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
Self::Other(error.into())
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ModuleLookupError {
#[error("No module of kind {kind} found")]
NoModuleOfKind {
kind: ModuleKind,
},
#[error("Unknown module instance {instance_id}")]
UnknownInstance {
instance_id: ModuleInstanceId,
},
#[error("Module instance {instance_id} is not of type {expected}")]
WrongModuleType {
instance_id: ModuleInstanceId,
expected: &'static str,
},
#[error("No primary module for unit {unit}")]
NoPrimaryModule {
unit: AmountUnit,
},
#[error("No primary {kind} module for unit {unit}")]
NoPrimaryModuleOfKind {
kind: ModuleKind,
unit: AmountUnit,
},
}
#[derive(Debug, Error)]
#[error("Could not find a common core API version")]
pub struct ApiVersionDiscoveryError;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum MetaFetchError {
#[error("Failed to read the meta override URL from the client config")]
Config(#[from] ModuleConfigError),
#[error("The meta override source could not be fetched")]
Http(#[from] reqwest::Error),
#[error("The meta override source answered with status {status}")]
Status {
status: reqwest::StatusCode,
},
#[error("The meta override source returned invalid JSON")]
Json(#[from] serde_json::Error),
#[error("The meta override source has no entry for federation {federation_id}")]
NoEntry {
federation_id: FederationId,
},
#[error("The meta source failed")]
Custom(#[source] Box<dyn std::error::Error + Send + Sync>),
}
#[cfg(test)]
mod tests;