diff --git a/Cargo.lock b/Cargo.lock index a47052fbd3..ea7b16f91f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10486,6 +10486,7 @@ dependencies = [ "axum", "hyper 0.14.30", "infra_utils", + "jsonrpsee", "papyrus_config", "reqwest 0.11.27", "serde", diff --git a/crates/starknet_gateway_types/src/errors.rs b/crates/starknet_gateway_types/src/errors.rs index bca04f72cf..84112d4ea4 100644 --- a/crates/starknet_gateway_types/src/errors.rs +++ b/crates/starknet_gateway_types/src/errors.rs @@ -1,5 +1,3 @@ -use axum::http::StatusCode; -use axum::response::{IntoResponse, Response}; use enum_assoc::Assoc; use papyrus_network_types::network_types::BroadcastedMessageMetadata; use papyrus_rpc::error::{ @@ -59,25 +57,6 @@ pub enum GatewaySpecError { ValidationFailure { data: String }, } -impl IntoResponse for GatewaySpecError { - fn into_response(self) -> Response { - let as_rpc = self.into_rpc(); - // TODO(Arni): Fix the status code. The status code should be a HTTP status code - not a - // Json RPC error code. status code. - let status = - StatusCode::from_u16(u16::try_from(as_rpc.code).expect("Expecting a valid u16")) - .expect("Expecting a valid error code"); - - let resp = Response::builder() - .status(status) - .body((as_rpc.message, as_rpc.data)) - .expect("Expecting valid response"); - let status = resp.status(); - let body = serde_json::to_string(resp.body()).expect("Expecting valid body"); - (status, body).into_response() - } -} - impl std::fmt::Display for GatewaySpecError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let as_rpc = self.clone().into_rpc(); diff --git a/crates/starknet_http_server/Cargo.toml b/crates/starknet_http_server/Cargo.toml index 056eead0af..1ce2950056 100644 --- a/crates/starknet_http_server/Cargo.toml +++ b/crates/starknet_http_server/Cargo.toml @@ -15,9 +15,11 @@ workspace = true axum.workspace = true hyper.workspace = true infra_utils.workspace = true +jsonrpsee = { workspace = true, features = ["full"] } papyrus_config.workspace = true reqwest = { workspace = true, optional = true } serde.workspace = true +serde_json.workspace = true starknet_api.workspace = true starknet_gateway_types.workspace = true starknet_sequencer_infra.workspace = true diff --git a/crates/starknet_http_server/src/errors.rs b/crates/starknet_http_server/src/errors.rs index 4a0a5122bc..2ff9bafb74 100644 --- a/crates/starknet_http_server/src/errors.rs +++ b/crates/starknet_http_server/src/errors.rs @@ -1,4 +1,9 @@ +use axum::response::{IntoResponse, Response}; +use jsonrpsee::types::error::ErrorCode; +use starknet_gateway_types::communication::GatewayClientError; +use starknet_gateway_types::errors::GatewayError; use thiserror::Error; +use tracing::error; /// Errors originating from `[`HttpServer::run`]` command. #[derive(Debug, Error)] @@ -6,3 +11,44 @@ pub enum HttpServerRunError { #[error(transparent)] ServerStartupError(#[from] hyper::Error), } + +/// Errors that may occure during the runtime of the HTTP server. +#[derive(Error, Debug)] +pub enum HttpServerError { + #[error(transparent)] + GatewayClientError(#[from] GatewayClientError), +} + +impl IntoResponse for HttpServerError { + fn into_response(self) -> Response { + match self { + HttpServerError::GatewayClientError(e) => gw_client_err_into_response(e), + } + } +} + +fn gw_client_err_into_response(err: GatewayClientError) -> Response { + let general_rpc_error = match err { + GatewayClientError::ClientError(e) => { + error!("Got a gateway client: {}", e); + jsonrpsee::types::ErrorObject::owned( + ErrorCode::InternalError.code(), + "Internal error", + None::<()>, + ) + } + GatewayClientError::GatewayError(GatewayError::GatewaySpecError { + source, + p2p_message_metadata: _, + }) => { + let rpc_spec_error = source.into_rpc(); + jsonrpsee::types::ErrorObject::owned( + ErrorCode::ServerError(rpc_spec_error.code).code(), + rpc_spec_error.message, + rpc_spec_error.data, + ) + } + }; + + serde_json::to_vec(&general_rpc_error).expect("Expecting a serializable error.").into_response() +} diff --git a/crates/starknet_http_server/src/http_server.rs b/crates/starknet_http_server/src/http_server.rs index fe70577a1a..966a1c3b5f 100644 --- a/crates/starknet_http_server/src/http_server.rs +++ b/crates/starknet_http_server/src/http_server.rs @@ -8,20 +8,19 @@ use infra_utils::type_name::short_type_name; use starknet_api::rpc_transaction::RpcTransaction; use starknet_api::transaction::TransactionHash; use starknet_gateway_types::communication::SharedGatewayClient; -use starknet_gateway_types::errors::GatewaySpecError; use starknet_gateway_types::gateway_types::GatewayInput; use starknet_sequencer_infra::component_definitions::ComponentStarter; use starknet_sequencer_infra::errors::ComponentError; -use tracing::{error, info, instrument}; +use tracing::{debug, info, instrument}; use crate::config::HttpServerConfig; -use crate::errors::HttpServerRunError; +use crate::errors::{HttpServerError, HttpServerRunError}; #[cfg(test)] #[path = "http_server_test.rs"] pub mod http_server_test; -pub type HttpServerResult = Result; +pub type HttpServerResult = Result; pub struct HttpServer { pub config: HttpServerConfig, @@ -62,18 +61,18 @@ async fn add_tx( State(app_state): State, Json(tx): Json, ) -> HttpServerResult> { - let gateway_input: GatewayInput = GatewayInput { rpc_tx: tx.clone(), message_metadata: None }; + let gateway_input: GatewayInput = GatewayInput { rpc_tx: tx, message_metadata: None }; - let add_tx_result = app_state.gateway_client.add_tx(gateway_input).await.map_err(|join_err| { - error!("Failed to process tx: {}", join_err); - GatewaySpecError::UnexpectedError { data: "Internal server error".to_owned() } + let add_tx_result = app_state.gateway_client.add_tx(gateway_input).await.map_err(|e| { + debug!("Error while adding transaction: {}", e); + HttpServerError::from(e) }); add_tx_result_as_json(add_tx_result) } pub(crate) fn add_tx_result_as_json( - result: Result, + result: HttpServerResult, ) -> HttpServerResult> { let tx_hash = result?; Ok(Json(tx_hash))