explorerd/error: enhance error module with parameterized errors and conversions

Enhanced the `explorerd` error module by introducing parameterized errors, improving error propagation through conversions, renaming for clarity, and refining JSON-RPC error handling.

Updates include:
- Renamed `RpcError` to `ExplorerdError` for use across all application layers (rpc, service, store)
- Updated `ExplorerdError` to support parameterized error variants for more detailed and flexible error messages
- Added conversions from `ExplorerdError` to `RpcError` and `Error` to aid with error propagation from individual RPC handlers to the `handle_request` method
- Renamed `to_tuple` to `to_error_code_message` for improved readability and updated it to support parameterized errors
- Revised the `server_error` function to construct `JsonError` responses using `ExplorerdError`
- Updated Cargo.toml to include thiserror support
- Updated `mod/ping_darkfid` function to use the updated `ExplorerdError`, including error details for additional context
This commit is contained in:
kalm
2025-03-24 01:13:22 -07:00
parent 6ccf08e924
commit 83478ad50d
3 changed files with 62 additions and 28 deletions

View File

@@ -38,6 +38,7 @@ log = "0.4.26"
lazy_static = "1.5.0"
tar = "0.4.44"
toml = "0.8.20"
thiserror = "2.0.11"
# Testing
tempdir = "0.3.7"

View File

@@ -16,44 +16,77 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use std::{fmt, sync::Arc};
use log::error;
use darkfi::{
rpc::jsonrpc::{ErrorCode::ServerError, JsonError, JsonResult},
Error,
rpc::jsonrpc::{ErrorCode, JsonError},
Error, RpcError,
};
// Constant for the error code
pub const ERROR_CODE_PING_DARKFID_FAILED: i32 = -32300;
/// Custom RPC errors available for blockchain explorer.
/// Please sort them sensefully.
pub enum RpcError {
// Misc errors
PingFailed = -32300,
/// These represent specific RPC-related failures.
#[derive(Debug, thiserror::Error)]
pub enum ExplorerdError {
#[error("Ping darkfid failed: {0}")]
PingDarkfidFailed(String),
#[error("Invalid contract ID: {0}")]
InvalidContractId(String),
#[error("Invalid header hash: {0}")]
InvalidHeaderHash(String),
#[error("Invalid tx hash: {0}")]
InvalidTxHash(String),
}
fn to_tuple(e: RpcError) -> (i32, String) {
let msg = match e {
// Misc errors
RpcError::PingFailed => "Darkfid daemon ping error",
};
(e as i32, msg.to_string())
}
pub fn server_error(e: RpcError, id: u16, msg: Option<&str>) -> JsonResult {
let (code, default_msg) = to_tuple(e);
if let Some(message) = msg {
return JsonError::new(ServerError(code), Some(message.to_string()), id).into()
/// Provides a conversion from [`ExplorerdError`] to darkfi [`Error`] type.
impl From<ExplorerdError> for Error {
fn from(err: ExplorerdError) -> Self {
let error: RpcError = err.into();
error.into()
}
JsonError::new(ServerError(code), Some(default_msg), id).into()
}
/// Handles a database error by formatting the output, logging it with target-specific context,
/// and returning a [`DatabaseError`].
pub fn handle_database_error(target: &str, message: &str, error: impl std::fmt::Debug) -> Error {
/// Conversion from [`ExplorerdRpcError`] to [`RpcError`]
impl From<ExplorerdError> for RpcError {
fn from(err: ExplorerdError) -> Self {
RpcError::ServerError(Arc::new(err))
}
}
/// Helper function to convert `ExplorerdRpcError` into error code with corresponding error message.
pub fn to_error_code_message(e: &ExplorerdError) -> (i32, String) {
match e {
ExplorerdError::PingDarkfidFailed(_) => (ERROR_CODE_PING_DARKFID_FAILED, e.to_string()),
ExplorerdError::InvalidContractId(_) |
ExplorerdError::InvalidHeaderHash(_) |
ExplorerdError::InvalidTxHash(_) => (ErrorCode::InvalidParams.code(), e.to_string()),
}
}
/// Constructs a [`JsonError`] representing a server error using the provided
/// [`ExplorerdError`] , request ID, and optional custom message, returning a [`JsonError`]
/// with a corresponding server error code and message.
pub fn server_error(e: &ExplorerdError, id: u16, msg: Option<&str>) -> JsonError {
let (code, default_msg) = to_error_code_message(e);
// Use the provided custom message if available; otherwise, use the default.
let message = msg.unwrap_or(&default_msg).to_string();
JsonError::new(ErrorCode::ServerError(code), Some(message), id)
}
/// Logs and converts a database error into a [`DatabaseError`].
/// This function ensures the error is logged contextually before being returned.
pub fn handle_database_error(target: &str, message: &str, error: impl fmt::Debug) -> Error {
let error_message = format!("{}: {:?}", message, error);
let formatted_target = format!("explorerd:: {target}");
let formatted_target = format!("explorerd::{}", target);
error!(target: &formatted_target, "{}", error_message);
Error::DatabaseError(error_message)
}

View File

@@ -35,7 +35,7 @@ use darkfi::{
};
use crate::{
error::{server_error, RpcError},
error::{server_error, ExplorerdError},
Explorerd,
};
@@ -221,7 +221,7 @@ impl Explorerd {
debug!(target: "explorerd::rpc::ping_darkfid", "Pinging darkfid daemon...");
if let Err(e) = self.darkfid_client.ping().await {
error!(target: "explorerd::rpc::ping_darkfid", "Failed to ping darkfid daemon: {}", e);
return server_error(RpcError::PingFailed, id, None)
return server_error(&ExplorerdError::PingDarkfidFailed(e.to_string()), id, None).into()
}
JsonResponse::new(JsonValue::Boolean(true), id).into()
}