use ethrex_common::U256;
use ethrex_storage::error::StoreError;
use ethrex_vm::EvmError;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::{authentication::AuthenticationError, clients::EthClientError};
use ethrex_blockchain::error::MempoolError;
#[derive(Debug, thiserror::Error)]
pub enum RpcErr {
#[error("Method not found: {0}")]
MethodNotFound(String),
#[error("Wrong parameter: {0}")]
WrongParam(String),
#[error("Invalid params: {0}")]
BadParams(String),
#[error("Missing parameter: {0}")]
MissingParam(String),
#[error("Too large request")]
TooLargeRequest,
#[error("Bad hex format: {0}")]
BadHexFormat(u64),
#[error("Unsupported fork: {0}")]
UnsupportedFork(String),
#[error("Internal Error: {0}")]
Internal(String),
#[error("Vm execution error: {0}")]
Vm(String),
#[error("execution reverted: data={data}")]
Revert { data: String },
#[error("execution halted: reason={reason}, gas_used={gas_used}")]
Halt { reason: String, gas_used: u64 },
#[error("Authentication error: {0:?}")]
AuthenticationError(AuthenticationError),
#[error("Invalid Request: {0}")]
InvalidRequest(String),
#[error("Invalid forkchoice state: {0}")]
InvalidForkChoiceState(String),
#[error("Invalid payload attributes: {0}")]
InvalidPayloadAttributes(String),
#[error("Too deep reorg: {0}")]
TooDeepReorg(String),
#[error("Unknown payload: {0}")]
UnknownPayload(String),
#[error("Invalid proof format: {0}")]
InvalidProofFormat(String),
#[error("Invalid header format: {0}")]
InvalidHeaderFormat(String),
#[error("Invalid payload: {0}")]
InvalidPayload(String),
#[error("Proof generation unavailable: {0}")]
ProofGenerationUnavailable(String),
}
impl From<RpcErr> for RpcErrorMetadata {
fn from(value: RpcErr) -> Self {
match value {
RpcErr::MethodNotFound(bad_method) => RpcErrorMetadata {
code: -32601,
data: None,
message: format!("Method not found: {bad_method}"),
},
RpcErr::WrongParam(field) => RpcErrorMetadata {
code: -32602,
data: None,
message: format!("Field '{field}' is incorrect or has an unknown format"),
},
RpcErr::BadParams(context) => RpcErrorMetadata {
code: -32000,
data: None,
message: format!("Invalid params: {context}"),
},
RpcErr::InvalidRequest(context) => RpcErrorMetadata {
code: -32600,
data: None,
message: format!("Invalid Request: {context}"),
},
RpcErr::MissingParam(parameter_name) => RpcErrorMetadata {
code: -32000,
data: None,
message: format!("Expected parameter: {parameter_name} is missing"),
},
RpcErr::TooLargeRequest => RpcErrorMetadata {
code: -38004,
data: None,
message: "Too large request".to_string(),
},
RpcErr::UnsupportedFork(context) => RpcErrorMetadata {
code: -38005,
data: None,
message: format!("Unsupported fork: {context}"),
},
RpcErr::BadHexFormat(arg_number) => RpcErrorMetadata {
code: -32602,
data: None,
message: format!("invalid argument {arg_number} : hex string without 0x prefix"),
},
RpcErr::Internal(context) => RpcErrorMetadata {
code: -32603,
data: None,
message: format!("Internal Error: {context}"),
},
RpcErr::Vm(context) => RpcErrorMetadata {
code: -32015,
data: None,
message: format!("Vm execution error: {context}"),
},
RpcErr::Revert { data } => RpcErrorMetadata {
code: 3,
data: Some(data.clone()),
message: format!(
"execution reverted: {}",
get_message_from_revert_data(&data).unwrap_or_else(|err| format!(
"tried to decode error from abi but failed: {err}"
))
),
},
RpcErr::Halt { reason, gas_used } => RpcErrorMetadata {
code: 3,
data: None,
message: format!("execution halted: reason={reason}, gas_used={gas_used}"),
},
RpcErr::AuthenticationError(auth_error) => match auth_error {
AuthenticationError::InvalidIssuedAtClaim => RpcErrorMetadata {
code: -32000,
data: None,
message: "Auth failed: Invalid iat claim".to_string(),
},
AuthenticationError::TokenDecodingError => RpcErrorMetadata {
code: -32000,
data: None,
message: "Auth failed: Invalid or missing token".to_string(),
},
AuthenticationError::MissingAuthentication => RpcErrorMetadata {
code: -32000,
data: None,
message: "Auth failed: Missing authentication header".to_string(),
},
},
RpcErr::InvalidForkChoiceState(data) => RpcErrorMetadata {
code: -38002,
data: Some(data),
message: "Invalid forkchoice state".to_string(),
},
RpcErr::InvalidPayloadAttributes(data) => RpcErrorMetadata {
code: -38003,
data: Some(data),
message: "Invalid payload attributes".to_string(),
},
RpcErr::TooDeepReorg(data) => RpcErrorMetadata {
code: -38006,
data: Some(data),
message: "Too deep reorg".to_string(),
},
RpcErr::UnknownPayload(context) => RpcErrorMetadata {
code: -38001,
data: None,
message: format!("Unknown payload: {context}"),
},
RpcErr::InvalidProofFormat(context) => RpcErrorMetadata {
code: -39001,
data: None,
message: format!("Invalid proof format: {context}"),
},
RpcErr::InvalidHeaderFormat(context) => RpcErrorMetadata {
code: -39002,
data: None,
message: format!("Invalid header format: {context}"),
},
RpcErr::InvalidPayload(context) => RpcErrorMetadata {
code: -39003,
data: None,
message: format!("Invalid payload: {context}"),
},
RpcErr::ProofGenerationUnavailable(context) => RpcErrorMetadata {
code: -39004,
data: None,
message: format!("Proof generation unavailable: {context}"),
},
}
}
}
impl From<serde_json::Error> for RpcErr {
fn from(error: serde_json::Error) -> Self {
Self::BadParams(error.to_string())
}
}
impl From<MempoolError> for RpcErr {
fn from(err: MempoolError) -> Self {
match err {
MempoolError::StoreError(err) => Self::Internal(err.to_string()),
other_err => Self::BadParams(other_err.to_string()),
}
}
}
impl From<ethrex_crypto::CryptoError> for RpcErr {
fn from(err: ethrex_crypto::CryptoError) -> Self {
Self::Internal(format!("Cryptography error: {err}"))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RpcNamespace {
Engine,
Eth,
Admin,
Debug,
Web3,
Net,
Mempool,
}
impl RpcNamespace {
pub fn from_prefix(s: &str) -> Option<Self> {
match s {
"engine" => Some(RpcNamespace::Engine),
"eth" => Some(RpcNamespace::Eth),
"admin" => Some(RpcNamespace::Admin),
"debug" => Some(RpcNamespace::Debug),
"web3" => Some(RpcNamespace::Web3),
"net" => Some(RpcNamespace::Net),
"txpool" => Some(RpcNamespace::Mempool),
_ => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RpcRequestId {
Number(u64),
String(String),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RpcRequest {
pub id: RpcRequestId,
pub jsonrpc: String,
pub method: String,
pub params: Option<Vec<Value>>,
}
impl RpcRequest {
pub fn namespace(&self) -> Result<RpcNamespace, RpcErr> {
let mut parts = self.method.split('_');
let Some(namespace) = parts.next() else {
return Err(RpcErr::MethodNotFound(self.method.clone()));
};
resolve_namespace(namespace, self.method.clone())
}
pub fn new(method: &str, params: Option<Vec<Value>>) -> Self {
RpcRequest {
id: RpcRequestId::Number(1),
jsonrpc: "2.0".to_string(),
method: method.to_string(),
params,
}
}
}
pub fn resolve_namespace(maybe_namespace: &str, method: String) -> Result<RpcNamespace, RpcErr> {
RpcNamespace::from_prefix(maybe_namespace).ok_or(RpcErr::MethodNotFound(method))
}
impl Default for RpcRequest {
fn default() -> Self {
RpcRequest {
id: RpcRequestId::Number(1),
jsonrpc: "2.0".to_string(),
method: "".to_string(),
params: None,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RpcErrorMetadata {
pub code: i32,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<String>,
pub message: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct RpcSuccessResponse {
pub id: RpcRequestId,
pub jsonrpc: String,
pub result: Value,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct RpcErrorResponse {
pub id: RpcRequestId,
pub jsonrpc: String,
pub error: RpcErrorMetadata,
}
#[derive(Deserialize, Debug)]
#[serde(untagged)]
pub enum RpcResponse {
Success(RpcSuccessResponse),
Error(RpcErrorResponse),
}
impl From<StoreError> for RpcErr {
fn from(value: StoreError) -> Self {
RpcErr::Internal(value.to_string())
}
}
impl From<EvmError> for RpcErr {
fn from(value: EvmError) -> Self {
RpcErr::Vm(value.to_string())
}
}
pub fn get_message_from_revert_data(data: &str) -> Result<String, EthClientError> {
if data == "0x" {
Ok("Execution reverted without a reason string.".to_owned())
} else if data.len() == 10 {
Ok(data.to_owned())
} else {
let abi_decoded_error_data =
hex::decode(data.strip_prefix("0x").ok_or(EthClientError::Custom(
"Failed to strip_prefix when getting message from revert data".to_owned(),
))?)
.map_err(|_| {
EthClientError::Custom(
"Failed to hex::decode when getting message from revert data".to_owned(),
)
})?;
let string_length = U256::from_big_endian(abi_decoded_error_data.get(36..68).ok_or(
EthClientError::Custom(
"Failed to slice index abi_decoded_error_data when getting message from revert data".to_owned(),
),
)?);
let string_len = usize::try_from(string_length).map_err(|_| {
EthClientError::Custom(
"Failed to convert string_length to usize when getting message from revert data"
.to_owned(),
)
})?;
let string_data = abi_decoded_error_data
.get(68..68 + string_len)
.ok_or(EthClientError::Custom(
"Failed to slice index abi_decoded_error_data when getting message from revert data"
.to_owned(),
))?;
String::from_utf8(string_data.to_vec()).map_err(|_| {
EthClientError::Custom(
"Failed to String::from_utf8 when getting message from revert data".to_owned(),
)
})
}
}
pub fn parse_json_hex(hex: &serde_json::Value) -> Result<u64, String> {
if let Value::String(maybe_hex) = hex {
let trimmed = maybe_hex.trim_start_matches("0x");
let maybe_parsed = u64::from_str_radix(trimmed, 16);
maybe_parsed.map_err(|_| format!("Could not parse given hex {maybe_hex}"))
} else {
Err(format!("Could not parse given hex {hex}"))
}
}