From 917dfc914fecaa6b719e5282461d3a227b743cae Mon Sep 17 00:00:00 2001 From: Bryan Stitt Date: Mon, 26 Jun 2023 22:40:00 -0700 Subject: [PATCH] handle more jsonrpc errors as HTTP 200 --- web3_proxy/src/errors.rs | 87 ++++++++++++++++++-------------- web3_proxy/src/response_cache.rs | 72 ++++++++++++++++---------- web3_proxy/src/rpcs/many.rs | 17 ++++++- 3 files changed, 109 insertions(+), 67 deletions(-) diff --git a/web3_proxy/src/errors.rs b/web3_proxy/src/errors.rs index a950752f..937ea310 100644 --- a/web3_proxy/src/errors.rs +++ b/web3_proxy/src/errors.rs @@ -167,7 +167,7 @@ impl Web3ProxyError { // TODO: include a unique request id in the data let (code, err): (StatusCode, JsonRpcErrorData) = match self { Self::Abi(err) => { - warn!("abi error={:?}", err); + warn!(?err, "abi error"); ( StatusCode::INTERNAL_SERVER_ERROR, JsonRpcErrorData { @@ -201,12 +201,12 @@ impl Web3ProxyError { ) } Self::Anyhow(err) => { - warn!("anyhow. err={:?}", err); + warn!(?err, "anyhow"); ( StatusCode::INTERNAL_SERVER_ERROR, JsonRpcErrorData { // TODO: is it safe to expose all of our anyhow strings? - message: err.to_string().into(), + message: "INTERNAL SERVER ERROR".into(), code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), data: None, }, @@ -214,10 +214,10 @@ impl Web3ProxyError { } Self::Arc(err) => { // recurse - return err.as_response_parts::(); + return err.as_response_parts(); } Self::BadRequest(err) => { - trace!("BAD_REQUEST: {}", err); + trace!(?err, "BAD_REQUEST"); ( StatusCode::BAD_REQUEST, JsonRpcErrorData { @@ -229,7 +229,7 @@ impl Web3ProxyError { } Self::BadResponse(err) => { // TODO: think about this one more. ankr gives us this because ethers fails to parse responses without an id - debug!("BAD_RESPONSE: {:?}", err); + debug!(?err, "BAD_RESPONSE"); ( StatusCode::INTERNAL_SERVER_ERROR, JsonRpcErrorData { @@ -284,43 +284,52 @@ impl Web3ProxyError { ) } Self::EthersHttpClient(err) => { - todo!("how should we handle this error? needs to try into jsonrpcerrordata"); - - warn!("EthersHttpClientError err={:#?}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: "ether http client error".into(), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) + if let Ok(err) = JsonRpcErrorData::try_from(err) { + trace!(?err, "EthersHttpClient jsonrpc error"); + (StatusCode::OK, err) + } else { + warn!(?err, "EthersHttpClient"); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: "ethers http client error".into(), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } } Self::EthersProvider(err) => { - todo!("how should we handle this error? needs to try into jsonrpcerrordata"); - - warn!("EthersProviderError err={:#?}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: "ether provider error".into(), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) + if let Ok(err) = JsonRpcErrorData::try_from(err) { + trace!(?err, "EthersProvider jsonrpc error"); + (StatusCode::OK, err) + } else { + warn!(?err, "EthersProvider"); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: "ethers provider error".into(), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } } Self::EthersWsClient(err) => { - todo!("how should we handle this error? needs to try into jsonrpcerrordata"); - - warn!("EthersWsClientError err={:#?}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: "ether ws client error".into(), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) + if let Ok(err) = JsonRpcErrorData::try_from(err) { + trace!(?err, "EthersWsClient jsonrpc error"); + (StatusCode::OK, err) + } else { + warn!(?err, "EthersWsClient"); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: "ethers ws client error".into(), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } } Self::FlumeRecv(err) => { warn!("FlumeRecvError err={:#?}", err); diff --git a/web3_proxy/src/response_cache.rs b/web3_proxy/src/response_cache.rs index 8bca377e..a4f7150d 100644 --- a/web3_proxy/src/response_cache.rs +++ b/web3_proxy/src/response_cache.rs @@ -1,6 +1,9 @@ use crate::{errors::Web3ProxyError, jsonrpc::JsonRpcErrorData, rpcs::blockchain::ArcBlock}; use derive_more::From; -use ethers::{providers::ProviderError, types::U64}; +use ethers::{ + providers::{HttpClientError, JsonRpcError, ProviderError, WsClientError}, + types::U64, +}; use hashbrown::hash_map::DefaultHashBuilder; use moka::future::Cache; use serde_json::value::RawValue; @@ -139,14 +142,15 @@ impl TryFrom for JsonRpcResponseEnum { type Error = Web3ProxyError; fn try_from(value: Web3ProxyError) -> Result { - match value { - Web3ProxyError::EthersProvider(provider_err) => { - let err = JsonRpcErrorData::try_from(provider_err)?; + if let Web3ProxyError::EthersProvider(ref err) = value { + if let Ok(x) = JsonRpcErrorData::try_from(err) { + let x = x.into(); - Ok(err.into()) + return Ok(x); } - err => Err(err), } + + Err(value) } } @@ -193,36 +197,52 @@ impl From for JsonRpcResponseEnum { } } -impl TryFrom for JsonRpcErrorData { - type Error = Web3ProxyError; +impl<'a> From<&'a JsonRpcError> for JsonRpcErrorData { + fn from(value: &'a JsonRpcError) -> Self { + Self { + code: value.code, + message: value.message.clone().into(), + data: value.data.clone(), + } + } +} - fn try_from(e: ProviderError) -> Result { - // TODO: move turning ClientError into json to a helper function? - let code; - let message: String; - let data; +impl<'a> TryFrom<&'a ProviderError> for JsonRpcErrorData { + type Error = &'a ProviderError; + fn try_from(e: &'a ProviderError) -> Result { match e { ProviderError::JsonRpcClientError(err) => { if let Some(err) = err.as_error_response() { - code = err.code; - message = err.message.clone(); - data = err.data.clone(); - } else if let Some(err) = err.as_serde_error() { - // this is not an rpc error. keep it as an error - return Err(Web3ProxyError::BadResponse(err.to_string().into())); + Ok(err.into()) } else { - return Err(anyhow::anyhow!("unexpected ethers error! {:?}", err).into()); + Err(e) } } - e => return Err(e.into()), + e => Err(e), } + } +} - Ok(JsonRpcErrorData { - code, - message: message.into(), - data, - }) +impl<'a> TryFrom<&'a HttpClientError> for JsonRpcErrorData { + type Error = &'a HttpClientError; + + fn try_from(e: &'a HttpClientError) -> Result { + match e { + HttpClientError::JsonRpcError(err) => Ok(err.into()), + e => Err(e), + } + } +} + +impl<'a> TryFrom<&'a WsClientError> for JsonRpcErrorData { + type Error = &'a WsClientError; + + fn try_from(e: &'a WsClientError) -> Result { + match e { + WsClientError::JsonRpcError(err) => Ok(err.into()), + e => Err(e), + } } } diff --git a/web3_proxy/src/rpcs/many.rs b/web3_proxy/src/rpcs/many.rs index 7481ae97..efb12821 100644 --- a/web3_proxy/src/rpcs/many.rs +++ b/web3_proxy/src/rpcs/many.rs @@ -946,19 +946,32 @@ impl Web3Rpcs { .store(is_backup_response, Ordering::Release); } + if let Some(request_metadata) = request_metadata { + request_metadata + .error_response + .store(false, Ordering::Release); + } + return Ok(response); } Err(error) => { // trace!(?response, "rpc error"); - // TODO: separate jsonrpc error and web3 proxy error! + // TODO: separate tracking for jsonrpc error and web3 proxy error! if let Some(request_metadata) = request_metadata { request_metadata .error_response .store(true, Ordering::Release); } - let error: JsonRpcErrorData = error.try_into()?; + // TODO: if this is an error, do NOT return. continue to try on another server + let error = match JsonRpcErrorData::try_from(&error) { + Ok(x) => x, + Err(err) => { + warn!(?err, "error from {}", rpc); + continue; + } + }; // some errors should be retried on other nodes let error_msg = error.message.as_ref();