From fd8bba4bdd2061d05c20d3024f4c5d35c05c36c0 Mon Sep 17 00:00:00 2001 From: Bryan Stitt Date: Tue, 30 May 2023 21:26:11 -0700 Subject: [PATCH] most of these are for the whole app, not just the frontend --- TODO.md | 2 +- web3_proxy/src/admin_queries.rs | 2 +- web3_proxy/src/app/mod.rs | 2 +- web3_proxy/src/app/ws.rs | 2 +- web3_proxy/src/block_number.rs | 2 +- web3_proxy/src/errors.rs | 988 +++++++++++++++++ web3_proxy/src/frontend/admin.rs | 4 +- web3_proxy/src/frontend/authorization.rs | 2 +- web3_proxy/src/frontend/errors.rs | 990 +----------------- web3_proxy/src/frontend/mod.rs | 5 +- web3_proxy/src/frontend/rpc_proxy_http.rs | 2 +- web3_proxy/src/frontend/rpc_proxy_ws.rs | 4 +- .../src/frontend/users/authentication.rs | 2 +- web3_proxy/src/frontend/users/mod.rs | 2 +- web3_proxy/src/frontend/users/payment.rs | 2 +- web3_proxy/src/frontend/users/referral.rs | 2 +- web3_proxy/src/frontend/users/rpc_keys.rs | 2 +- web3_proxy/src/frontend/users/stats.rs | 2 +- web3_proxy/src/frontend/users/subuser.rs | 2 +- web3_proxy/src/http_params.rs | 2 +- web3_proxy/src/ipc.rs | 16 + web3_proxy/src/jsonrpc.rs | 2 +- web3_proxy/src/jsonrpc_client.rs | 28 - web3_proxy/src/lib.rs | 3 +- web3_proxy/src/prometheus.rs | 2 +- web3_proxy/src/response_cache.rs | 4 +- web3_proxy/src/rpcs/blockchain.rs | 2 +- web3_proxy/src/rpcs/consensus.rs | 2 +- web3_proxy/src/rpcs/many.rs | 2 +- web3_proxy/src/rpcs/one.rs | 2 +- web3_proxy/src/rpcs/request.rs | 2 +- web3_proxy/src/rpcs/transactions.rs | 3 +- web3_proxy/src/stats/db_queries.rs | 2 +- web3_proxy/src/stats/influxdb_queries.rs | 4 +- web3_proxy/src/stats/mod.rs | 2 +- web3_proxy/src/stats/stat_buffer.rs | 2 +- 36 files changed, 1044 insertions(+), 1055 deletions(-) create mode 100644 web3_proxy/src/errors.rs create mode 100644 web3_proxy/src/ipc.rs delete mode 100644 web3_proxy/src/jsonrpc_client.rs diff --git a/TODO.md b/TODO.md index 5bab04ae..631b9c2a 100644 --- a/TODO.md +++ b/TODO.md @@ -519,7 +519,7 @@ These are not yet ordered. There might be duplicates. We might not actually need - [ ] if user-specific caches have evictions that aren't from timeouts, log a warning - [ ] make sure the email address is valid. probably have a "verified" column in the database - [ ] if invalid user id given, we give a 500. should be a different error code instead - - WARN http_request: web3_proxy::frontend::errors: anyhow err=UserKey was not a ULID or UUID id=01GER4VBTS0FDHEBR96D1JRDZF method=POST + - WARN http_request: web3_proxy::errors: anyhow err=UserKey was not a ULID or UUID id=01GER4VBTS0FDHEBR96D1JRDZF method=POST - [ ] admin-only endpoint for seeing a user's stats for support requests - [ ] from what i thought, /status should show hashes > numbers! - but block numbers count is maxed out (10k) diff --git a/web3_proxy/src/admin_queries.rs b/web3_proxy/src/admin_queries.rs index 06500213..08d6e111 100644 --- a/web3_proxy/src/admin_queries.rs +++ b/web3_proxy/src/admin_queries.rs @@ -1,5 +1,5 @@ use crate::app::Web3ProxyApp; -use crate::frontend::errors::{Web3ProxyError, Web3ProxyResponse}; +use crate::errors::{Web3ProxyError, Web3ProxyResponse}; use crate::http_params::get_user_id_from_params; use anyhow::Context; use axum::response::IntoResponse; diff --git a/web3_proxy/src/app/mod.rs b/web3_proxy/src/app/mod.rs index 92d58691..36be936c 100644 --- a/web3_proxy/src/app/mod.rs +++ b/web3_proxy/src/app/mod.rs @@ -2,10 +2,10 @@ mod ws; use crate::block_number::{block_needed, BlockNeeded}; use crate::config::{AppConfig, TopConfig}; +use crate::errors::{Web3ProxyError, Web3ProxyErrorContext, Web3ProxyResult}; use crate::frontend::authorization::{ Authorization, RequestMetadata, RequestOrMethod, ResponseOrBytes, RpcSecretKey, }; -use crate::frontend::errors::{Web3ProxyError, Web3ProxyErrorContext, Web3ProxyResult}; use crate::frontend::rpc_proxy_ws::ProxyMode; use crate::jsonrpc::{ JsonRpcErrorData, JsonRpcForwardedResponse, JsonRpcForwardedResponseEnum, JsonRpcId, diff --git a/web3_proxy/src/app/ws.rs b/web3_proxy/src/app/ws.rs index bb162f52..cc13b44f 100644 --- a/web3_proxy/src/app/ws.rs +++ b/web3_proxy/src/app/ws.rs @@ -1,8 +1,8 @@ //! Websocket-specific functions for the Web3ProxyApp use super::Web3ProxyApp; +use crate::errors::{Web3ProxyError, Web3ProxyResult}; use crate::frontend::authorization::{Authorization, RequestMetadata, RequestOrMethod}; -use crate::frontend::errors::{Web3ProxyError, Web3ProxyResult}; use crate::jsonrpc::JsonRpcForwardedResponse; use crate::jsonrpc::JsonRpcRequest; use crate::response_cache::JsonRpcResponseEnum; diff --git a/web3_proxy/src/block_number.rs b/web3_proxy/src/block_number.rs index 6afb7d9c..f93a904b 100644 --- a/web3_proxy/src/block_number.rs +++ b/web3_proxy/src/block_number.rs @@ -1,5 +1,5 @@ //! Helper functions for turning ether's BlockNumber into numbers and updating incoming queries to match. -use crate::frontend::errors::{Web3ProxyError, Web3ProxyResult}; +use crate::errors::{Web3ProxyError, Web3ProxyResult}; use anyhow::Context; use ethers::{ prelude::{BlockNumber, U64}, diff --git a/web3_proxy/src/errors.rs b/web3_proxy/src/errors.rs new file mode 100644 index 00000000..8b6fdca2 --- /dev/null +++ b/web3_proxy/src/errors.rs @@ -0,0 +1,988 @@ +//! Utlities for logging errors for admins and displaying errors to users. + +use crate::frontend::authorization::Authorization; +use crate::jsonrpc::{JsonRpcErrorData, JsonRpcForwardedResponse}; +use crate::response_cache::JsonRpcResponseEnum; + +use std::error::Error; +use std::{borrow::Cow, net::IpAddr}; + +use axum::{ + headers, + http::StatusCode, + response::{IntoResponse, Response}, + Json, +}; +use derive_more::{Display, Error, From}; +use http::header::InvalidHeaderValue; +use ipnet::AddrParseError; +use log::{debug, error, info, trace, warn}; +use migration::sea_orm::DbErr; +use redis_rate_limiter::redis::RedisError; +use reqwest::header::ToStrError; +use serde::Serialize; +use serde_json::value::RawValue; +use tokio::{sync::AcquireError, task::JoinError, time::Instant}; + +pub type Web3ProxyResult = Result; +// TODO: take "IntoResponse" instead of Response? +pub type Web3ProxyResponse = Web3ProxyResult; + +impl From for Web3ProxyResult<()> { + fn from(value: Web3ProxyError) -> Self { + Err(value) + } +} + +#[derive(Debug, Display, Error, From)] +pub enum Web3ProxyError { + AccessDenied, + #[error(ignore)] + Anyhow(anyhow::Error), + #[error(ignore)] + #[from(ignore)] + BadRequest(String), + #[error(ignore)] + #[from(ignore)] + BadResponse(String), + BadRouting, + Database(DbErr), + #[display(fmt = "{:#?}, {:#?}", _0, _1)] + EipVerificationFailed(Box, Box), + EthersHttpClient(ethers::prelude::HttpClientError), + EthersProvider(ethers::prelude::ProviderError), + EthersWsClient(ethers::prelude::WsClientError), + FlumeRecv(flume::RecvError), + GasEstimateNotU256, + Headers(headers::Error), + HeaderToString(ToStrError), + Hyper(hyper::Error), + InfluxDb2Request(influxdb2::RequestError), + #[display(fmt = "{} > {}", min, max)] + #[from(ignore)] + InvalidBlockBounds { + min: u64, + max: u64, + }, + InvalidHeaderValue(InvalidHeaderValue), + InvalidEip, + InvalidInviteCode, + Io(std::io::Error), + UnknownReferralCode, + InvalidReferer, + InvalidSignatureLength, + InvalidUserAgent, + InvalidUserKey, + IpAddrParse(AddrParseError), + #[error(ignore)] + #[from(ignore)] + IpNotAllowed(IpAddr), + JoinError(JoinError), + #[display(fmt = "{:?}", _0)] + #[error(ignore)] + JsonRpcErrorData(JsonRpcErrorData), + #[display(fmt = "{:?}", _0)] + #[error(ignore)] + MsgPackEncode(rmp_serde::encode::Error), + NoBlockNumberOrHash, + NoBlocksKnown, + NoConsensusHeadBlock, + NoHandleReady, + NoServersSynced, + #[display(fmt = "{}/{}", num_known, min_head_rpcs)] + #[from(ignore)] + NotEnoughRpcs { + num_known: usize, + min_head_rpcs: usize, + }, + #[display(fmt = "{}/{}", available, needed)] + #[from(ignore)] + NotEnoughSoftLimit { + available: u32, + needed: u32, + }, + NotFound, + NotImplemented, + OriginRequired, + #[error(ignore)] + #[from(ignore)] + OriginNotAllowed(headers::Origin), + #[display(fmt = "{:?}", _0)] + #[error(ignore)] + ParseBytesError(Option), + ParseMsgError(siwe::ParseError), + ParseAddressError, + #[display(fmt = "{:?}, {:?}", _0, _1)] + RateLimited(Authorization, Option), + Redis(RedisError), + RefererRequired, + #[display(fmt = "{:?}", _0)] + #[error(ignore)] + #[from(ignore)] + RefererNotAllowed(headers::Referer), + SemaphoreAcquireError(AcquireError), + SendAppStatError(flume::SendError), + SerdeJson(serde_json::Error), + /// simple way to return an error message to the user and an anyhow to our logs + #[display(fmt = "{}, {}, {:?}", _0, _1, _2)] + StatusCode(StatusCode, String, Option), + /// TODO: what should be attached to the timout? + #[display(fmt = "{:?}", _0)] + #[error(ignore)] + Timeout(Option), + UlidDecode(ulid::DecodeError), + UnknownBlockNumber, + UnknownKey, + UserAgentRequired, + #[error(ignore)] + UserAgentNotAllowed(headers::UserAgent), + UserIdZero, + PaymentRequired, + VerificationError(siwe::VerificationError), + WatchRecvError(tokio::sync::watch::error::RecvError), + WatchSendError, + WebsocketOnly, + #[display(fmt = "{:?}, {}", _0, _1)] + #[error(ignore)] + WithContext(Option>, String), +} + +impl Web3ProxyError { + pub fn into_response_parts(self) -> (StatusCode, JsonRpcResponseEnum) { + // TODO: include a unique request id in the data + let (code, err): (StatusCode, JsonRpcErrorData) = match self { + Self::AccessDenied => { + // TODO: attach something to this trace. probably don't include much in the message though. don't want to leak creds by accident + trace!("access denied"); + ( + StatusCode::FORBIDDEN, + JsonRpcErrorData { + message: Cow::Borrowed("FORBIDDEN"), + code: StatusCode::FORBIDDEN.as_u16().into(), + data: None, + }, + ) + } + Self::Anyhow(err) => { + warn!("anyhow. err={:?}", err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + // TODO: is it safe to expose all of our anyhow strings? + message: Cow::Owned(err.to_string()), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::BadRequest(err) => { + debug!("BAD_REQUEST: {}", err); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Owned(format!("bad request: {}", err)), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + 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); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: Cow::Owned(format!("bad response: {}", err)), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::BadRouting => { + error!("BadRouting"); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: Cow::Borrowed("bad routing"), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::Database(err) => { + error!("database err={:?}", err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: Cow::Borrowed("database error!"), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::EipVerificationFailed(err_1, err_191) => { + info!( + "EipVerificationFailed err_1={:#?} err2={:#?}", + err_1, err_191 + ); + ( + StatusCode::UNAUTHORIZED, + JsonRpcErrorData { + message: Cow::Owned(format!( + "both the primary and eip191 verification failed: {:#?}; {:#?}", + err_1, err_191 + )), + code: StatusCode::UNAUTHORIZED.as_u16().into(), + data: None, + }, + ) + } + Self::EthersHttpClient(err) => { + warn!("EthersHttpClientError err={:?}", err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: Cow::Borrowed("ether http client error"), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::EthersProvider(err) => { + warn!("EthersProviderError err={:?}", err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: Cow::Borrowed("ether provider error"), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::EthersWsClient(err) => { + warn!("EthersWsClientError err={:?}", err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: Cow::Borrowed("ether ws client error"), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::FlumeRecv(err) => { + warn!("FlumeRecvError err={:#?}", err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: Cow::Borrowed("flume recv error!"), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + // Self::JsonRpcForwardedError(x) => (StatusCode::OK, x), + Self::GasEstimateNotU256 => { + warn!("GasEstimateNotU256"); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: Cow::Borrowed("gas estimate result is not an U256"), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::Headers(err) => { + warn!("HeadersError {:?}", err); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Owned(format!("{}", err)), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::Hyper(err) => { + warn!("hyper err={:?}", err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + // TODO: is it safe to expose these error strings? + message: Cow::Owned(err.to_string()), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::InfluxDb2Request(err) => { + // TODO: attach a request id to the message and to this error so that if people report problems, we can dig in sentry to find out more + error!("influxdb2 err={:?}", err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: Cow::Borrowed("influxdb2 error!"), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::InvalidBlockBounds { min, max } => { + debug!("InvalidBlockBounds min={} max={}", min, max); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Owned(format!( + "Invalid blocks bounds requested. min ({}) > max ({})", + min, max + )), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::IpAddrParse(err) => { + debug!("IpAddrParse err={:?}", err); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Owned(err.to_string()), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::IpNotAllowed(ip) => { + debug!("IpNotAllowed ip={})", ip); + ( + StatusCode::FORBIDDEN, + JsonRpcErrorData { + message: Cow::Owned(format!("IP ({}) is not allowed!", ip)), + code: StatusCode::FORBIDDEN.as_u16().into(), + data: None, + }, + ) + } + Self::InvalidHeaderValue(err) => { + debug!("InvalidHeaderValue err={:?}", err); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Owned(format!("{}", err)), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::InvalidEip => { + debug!("InvalidEip"); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Borrowed("invalid message eip given"), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::InvalidInviteCode => { + debug!("InvalidInviteCode"); + ( + StatusCode::UNAUTHORIZED, + JsonRpcErrorData { + message: Cow::Borrowed("invalid invite code"), + code: StatusCode::UNAUTHORIZED.as_u16().into(), + data: None, + }, + ) + } + Self::Io(err) => { + warn!("std io err={:?}", err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + // TODO: is it safe to expose our io error strings? + message: Cow::Owned(err.to_string()), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::UnknownReferralCode => { + debug!("UnknownReferralCode"); + ( + StatusCode::UNAUTHORIZED, + JsonRpcErrorData { + message: Cow::Borrowed("invalid referral code"), + code: StatusCode::UNAUTHORIZED.as_u16().into(), + data: None, + }, + ) + } + Self::InvalidReferer => { + debug!("InvalidReferer"); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Borrowed("invalid referer!"), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::InvalidSignatureLength => { + debug!("InvalidSignatureLength"); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Borrowed("invalid signature length"), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::InvalidUserAgent => { + debug!("InvalidUserAgent"); + ( + StatusCode::FORBIDDEN, + JsonRpcErrorData { + message: Cow::Borrowed("invalid user agent!"), + code: StatusCode::FORBIDDEN.as_u16().into(), + data: None, + }, + ) + } + Self::InvalidUserKey => { + warn!("InvalidUserKey"); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Borrowed("UserKey was not a ULID or UUID"), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::JoinError(err) => { + let code = if err.is_cancelled() { + trace!("JoinError. likely shutting down. err={:?}", err); + StatusCode::BAD_GATEWAY + } else { + warn!("JoinError. err={:?}", err); + StatusCode::INTERNAL_SERVER_ERROR + }; + + ( + code, + JsonRpcErrorData { + // TODO: different messages of cancelled or not? + message: Cow::Borrowed("Unable to complete request"), + code: code.as_u16().into(), + data: None, + }, + ) + } + Self::JsonRpcErrorData(jsonrpc_error_data) => (StatusCode::OK, jsonrpc_error_data), + Self::MsgPackEncode(err) => { + warn!("MsgPackEncode Error: {}", err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: Cow::Owned(format!("msgpack encode error: {}", err)), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::NoBlockNumberOrHash => { + warn!("NoBlockNumberOrHash"); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: Cow::Borrowed("Blocks here must have a number or hash"), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::NoBlocksKnown => { + error!("NoBlocksKnown"); + ( + StatusCode::BAD_GATEWAY, + JsonRpcErrorData { + message: Cow::Borrowed("no blocks known"), + code: StatusCode::BAD_GATEWAY.as_u16().into(), + data: None, + }, + ) + } + Self::NoConsensusHeadBlock => { + error!("NoConsensusHeadBlock"); + ( + StatusCode::BAD_GATEWAY, + JsonRpcErrorData { + message: Cow::Borrowed("no consensus head block"), + code: StatusCode::BAD_GATEWAY.as_u16().into(), + data: None, + }, + ) + } + Self::NoHandleReady => { + error!("NoHandleReady"); + ( + StatusCode::BAD_GATEWAY, + JsonRpcErrorData { + message: Cow::Borrowed("unable to retry for request handle"), + code: StatusCode::BAD_GATEWAY.as_u16().into(), + data: None, + }, + ) + } + Self::NoServersSynced => { + warn!("NoServersSynced"); + ( + StatusCode::BAD_GATEWAY, + JsonRpcErrorData { + message: Cow::Borrowed("no servers synced"), + code: StatusCode::BAD_GATEWAY.as_u16().into(), + data: None, + }, + ) + } + Self::NotEnoughRpcs { + num_known, + min_head_rpcs, + } => { + error!("NotEnoughRpcs {}/{}", num_known, min_head_rpcs); + ( + StatusCode::BAD_GATEWAY, + JsonRpcErrorData { + message: Cow::Owned(format!( + "not enough rpcs connected {}/{}", + num_known, min_head_rpcs + )), + code: StatusCode::BAD_GATEWAY.as_u16().into(), + data: None, + }, + ) + } + Self::NotEnoughSoftLimit { available, needed } => { + error!("NotEnoughSoftLimit {}/{}", available, needed); + ( + StatusCode::BAD_GATEWAY, + JsonRpcErrorData { + message: Cow::Owned(format!( + "not enough soft limit available {}/{}", + available, needed + )), + code: StatusCode::BAD_GATEWAY.as_u16().into(), + data: None, + }, + ) + } + Self::NotFound => { + // TODO: emit a stat? + // TODO: instead of an error, show a normal html page for 404? + ( + StatusCode::NOT_FOUND, + JsonRpcErrorData { + message: Cow::Borrowed("not found!"), + code: StatusCode::NOT_FOUND.as_u16().into(), + data: None, + }, + ) + } + Self::NotImplemented => { + trace!("NotImplemented"); + ( + StatusCode::NOT_IMPLEMENTED, + JsonRpcErrorData { + message: Cow::Borrowed("work in progress"), + code: StatusCode::NOT_IMPLEMENTED.as_u16().into(), + data: None, + }, + ) + } + Self::OriginRequired => { + trace!("OriginRequired"); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Borrowed("Origin required"), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::OriginNotAllowed(origin) => { + trace!("OriginNotAllowed origin={}", origin); + ( + StatusCode::FORBIDDEN, + JsonRpcErrorData { + message: Cow::Owned(format!("Origin ({}) is not allowed!", origin)), + code: StatusCode::FORBIDDEN.as_u16().into(), + data: None, + }, + ) + } + Self::ParseBytesError(err) => { + trace!("ParseBytesError err={:?}", err); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Borrowed("parse bytes error!"), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::ParseMsgError(err) => { + trace!("ParseMsgError err={:?}", err); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Borrowed("parse message error!"), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::ParseAddressError => { + trace!("ParseAddressError"); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Borrowed("unable to parse address"), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::PaymentRequired => { + trace!("PaymentRequiredError"); + ( + StatusCode::PAYMENT_REQUIRED, + JsonRpcErrorData { + message: Cow::Borrowed("Payment is required and user is not premium"), + code: StatusCode::PAYMENT_REQUIRED.as_u16().into(), + data: None, + }, + ) + } + // TODO: this should actually by the id of the key. multiple users might control one key + Self::RateLimited(authorization, retry_at) => { + // TODO: emit a stat + + let retry_msg = if let Some(retry_at) = retry_at { + let retry_in = retry_at.duration_since(Instant::now()).as_secs(); + + format!(" Retry in {} seconds", retry_in) + } else { + "".to_string() + }; + + // create a string with either the IP or the rpc_key_id + let msg = if authorization.checks.rpc_secret_key_id.is_none() { + format!("too many requests from {}.{}", authorization.ip, retry_msg) + } else { + format!( + "too many requests from rpc key #{}.{}", + authorization.checks.rpc_secret_key_id.unwrap(), + retry_msg, + ) + }; + + ( + StatusCode::TOO_MANY_REQUESTS, + JsonRpcErrorData { + message: Cow::Owned(msg), + code: StatusCode::TOO_MANY_REQUESTS.as_u16().into(), + data: None, + }, + ) + } + Self::Redis(err) => { + warn!("redis err={:?}", err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: Cow::Borrowed("redis error!"), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::RefererRequired => { + warn!("referer required"); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Borrowed("Referer required"), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::RefererNotAllowed(referer) => { + warn!("referer not allowed referer={:?}", referer); + ( + StatusCode::FORBIDDEN, + JsonRpcErrorData { + message: Cow::Owned(format!("Referer ({:?}) is not allowed", referer)), + code: StatusCode::FORBIDDEN.as_u16().into(), + data: None, + }, + ) + } + Self::SemaphoreAcquireError(err) => { + warn!("semaphore acquire err={:?}", err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + // TODO: is it safe to expose all of our anyhow strings? + message: Cow::Borrowed("semaphore acquire error"), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::SendAppStatError(err) => { + error!("SendAppStatError err={:?}", err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: Cow::Borrowed("error stat_sender sending response_stat"), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::SerdeJson(err) => { + warn!("serde json err={:?} source={:?}", err, err.source()); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Owned(format!("de/serialization error! {}", err)), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::StatusCode(status_code, err_msg, err) => { + // different status codes should get different error levels. 500s should warn. 400s should stat + let code = status_code.as_u16(); + if (500..600).contains(&code) { + warn!("server error {} {:?}: {:?}", code, err_msg, err); + } else { + trace!("user error {} {:?}: {:?}", code, err_msg, err); + } + + ( + status_code, + JsonRpcErrorData { + message: Cow::Owned(err_msg), + code: code.into(), + data: None, + }, + ) + } + Self::Timeout(x) => ( + StatusCode::REQUEST_TIMEOUT, + JsonRpcErrorData { + message: Cow::Owned(format!("request timed out: {:?}", x)), + code: StatusCode::REQUEST_TIMEOUT.as_u16().into(), + // TODO: include the actual id! + data: None, + }, + ), + Self::HeaderToString(err) => { + // trace!(?err, "HeaderToString"); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Owned(err.to_string()), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::UlidDecode(err) => { + // trace!(?err, "UlidDecodeError"); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Owned(format!("{}", err)), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::UnknownBlockNumber => { + error!("UnknownBlockNumber"); + ( + StatusCode::BAD_GATEWAY, + JsonRpcErrorData { + message: Cow::Borrowed("no servers synced. unknown eth_blockNumber"), + code: StatusCode::BAD_GATEWAY.as_u16().into(), + data: None, + }, + ) + } + // TODO: stat? + Self::UnknownKey => ( + StatusCode::UNAUTHORIZED, + JsonRpcErrorData { + message: Cow::Borrowed("unknown api key!"), + code: StatusCode::UNAUTHORIZED.as_u16().into(), + data: None, + }, + ), + Self::UserAgentRequired => { + warn!("UserAgentRequired"); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Borrowed("User agent required"), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::UserAgentNotAllowed(ua) => { + warn!("UserAgentNotAllowed ua={}", ua); + ( + StatusCode::FORBIDDEN, + JsonRpcErrorData { + message: Cow::Owned(format!("User agent ({}) is not allowed!", ua)), + code: StatusCode::FORBIDDEN.as_u16().into(), + data: None, + }, + ) + } + Self::UserIdZero => { + warn!("UserIdZero"); + // TODO: this might actually be an application error and not a BAD_REQUEST + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Borrowed("user ids should always be non-zero"), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::VerificationError(err) => { + trace!("VerificationError err={:?}", err); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Borrowed("verification error!"), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::WatchRecvError(err) => { + error!("WatchRecvError err={:?}", err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: Cow::Borrowed("watch recv error!"), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::WatchSendError => { + error!("WatchSendError"); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: Cow::Borrowed("watch send error!"), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + Self::WebsocketOnly => { + trace!("WebsocketOnly"); + ( + StatusCode::BAD_REQUEST, + JsonRpcErrorData { + message: Cow::Borrowed( + "redirect_public_url not set. only websockets work here", + ), + code: StatusCode::BAD_REQUEST.as_u16().into(), + data: None, + }, + ) + } + Self::WithContext(err, msg) => match err { + Some(err) => { + warn!("{:#?} w/ context {}", err, msg); + return err.into_response_parts(); + } + None => { + warn!("error w/ context {}", msg); + ( + StatusCode::INTERNAL_SERVER_ERROR, + JsonRpcErrorData { + message: Cow::Owned(msg), + code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), + data: None, + }, + ) + } + }, + }; + + (code, JsonRpcResponseEnum::from(err)) + } + + pub fn into_response_with_id(self, id: Box) -> Response { + let (status_code, response_data) = self.into_response_parts(); + + let response = JsonRpcForwardedResponse::from_response_data(response_data, id); + + (status_code, Json(response)).into_response() + } +} + +impl From for Web3ProxyError { + fn from(err: ethers::types::ParseBytesError) -> Self { + Self::ParseBytesError(Some(err)) + } +} + +impl From for Web3ProxyError { + fn from(err: tokio::time::error::Elapsed) -> Self { + Self::Timeout(Some(err)) + } +} + +impl IntoResponse for Web3ProxyError { + #[inline] + fn into_response(self) -> Response { + self.into_response_with_id(Default::default()) + } +} + +pub trait Web3ProxyErrorContext { + fn web3_context>(self, msg: S) -> Result; +} + +impl Web3ProxyErrorContext for Option { + fn web3_context>(self, msg: S) -> Result { + self.ok_or(Web3ProxyError::WithContext(None, msg.into())) + } +} + +impl Web3ProxyErrorContext for Result +where + E: Into, +{ + fn web3_context>(self, msg: S) -> Result { + self.map_err(|err| Web3ProxyError::WithContext(Some(Box::new(err.into())), msg.into())) + } +} diff --git a/web3_proxy/src/frontend/admin.rs b/web3_proxy/src/frontend/admin.rs index 1b2a5123..bb2b82d7 100644 --- a/web3_proxy/src/frontend/admin.rs +++ b/web3_proxy/src/frontend/admin.rs @@ -1,10 +1,10 @@ //! Handle admin helper logic use super::authorization::login_is_authorized; -use super::errors::Web3ProxyResponse; use crate::admin_queries::query_admin_modify_usertier; use crate::app::Web3ProxyApp; -use crate::frontend::errors::{Web3ProxyError, Web3ProxyErrorContext}; +use crate::errors::Web3ProxyResponse; +use crate::errors::{Web3ProxyError, Web3ProxyErrorContext}; use crate::user_token::UserBearerToken; use crate::PostLogin; use anyhow::Context; diff --git a/web3_proxy/src/frontend/authorization.rs b/web3_proxy/src/frontend/authorization.rs index ad6adb9d..0a5a0ea4 100644 --- a/web3_proxy/src/frontend/authorization.rs +++ b/web3_proxy/src/frontend/authorization.rs @@ -1,8 +1,8 @@ //! Utilities for authorization of logged in and anonymous users. -use super::errors::{Web3ProxyError, Web3ProxyErrorContext, Web3ProxyResult}; use super::rpc_proxy_ws::ProxyMode; use crate::app::{AuthorizationChecks, Web3ProxyApp, APP_USER_AGENT}; +use crate::errors::{Web3ProxyError, Web3ProxyErrorContext, Web3ProxyResult}; use crate::jsonrpc::{JsonRpcForwardedResponse, JsonRpcRequest}; use crate::rpcs::one::Web3Rpc; use crate::stats::{AppStat, BackendRequests, RpcQueryStats}; diff --git a/web3_proxy/src/frontend/errors.rs b/web3_proxy/src/frontend/errors.rs index 567a80b1..a17b4c34 100644 --- a/web3_proxy/src/frontend/errors.rs +++ b/web3_proxy/src/frontend/errors.rs @@ -1,993 +1,7 @@ -//! Utlities for logging errors for admins and displaying errors to users. - -use super::authorization::Authorization; -use crate::jsonrpc::{JsonRpcErrorData, JsonRpcForwardedResponse}; -use crate::response_cache::JsonRpcResponseEnum; - -use std::error::Error; -use std::{borrow::Cow, net::IpAddr}; - -use axum::{ - headers, - http::StatusCode, - response::{IntoResponse, Response}, - Json, -}; -use derive_more::{Display, Error, From}; -use http::header::InvalidHeaderValue; -use ipnet::AddrParseError; -use log::{debug, error, info, trace, warn}; -use migration::sea_orm::DbErr; -use redis_rate_limiter::redis::RedisError; -use reqwest::header::ToStrError; -use serde::Serialize; -use serde_json::value::RawValue; -use tokio::{sync::AcquireError, task::JoinError, time::Instant}; - -pub type Web3ProxyResult = Result; -// TODO: take "IntoResponse" instead of Response? -pub type Web3ProxyResponse = Web3ProxyResult; - -impl From for Web3ProxyResult<()> { - fn from(value: Web3ProxyError) -> Self { - Err(value) - } -} - -#[derive(Debug, Display, Error, From)] -pub enum Web3ProxyError { - AccessDenied, - #[error(ignore)] - Anyhow(anyhow::Error), - #[error(ignore)] - #[from(ignore)] - BadRequest(String), - #[error(ignore)] - #[from(ignore)] - BadResponse(String), - BadRouting, - Database(DbErr), - #[display(fmt = "{:#?}, {:#?}", _0, _1)] - EipVerificationFailed(Box, Box), - EthersHttpClient(ethers::prelude::HttpClientError), - EthersProvider(ethers::prelude::ProviderError), - EthersWsClient(ethers::prelude::WsClientError), - FlumeRecv(flume::RecvError), - GasEstimateNotU256, - Headers(headers::Error), - HeaderToString(ToStrError), - Hyper(hyper::Error), - InfluxDb2Request(influxdb2::RequestError), - #[display(fmt = "{} > {}", min, max)] - #[from(ignore)] - InvalidBlockBounds { - min: u64, - max: u64, - }, - InvalidHeaderValue(InvalidHeaderValue), - InvalidEip, - InvalidInviteCode, - Io(std::io::Error), - UnknownReferralCode, - InvalidReferer, - InvalidSignatureLength, - InvalidUserAgent, - InvalidUserKey, - IpAddrParse(AddrParseError), - #[error(ignore)] - #[from(ignore)] - IpNotAllowed(IpAddr), - JoinError(JoinError), - #[display(fmt = "{:?}", _0)] - #[error(ignore)] - JsonRpcErrorData(JsonRpcErrorData), - #[display(fmt = "{:?}", _0)] - #[error(ignore)] - MsgPackEncode(rmp_serde::encode::Error), - NoBlockNumberOrHash, - NoBlocksKnown, - NoConsensusHeadBlock, - NoHandleReady, - NoServersSynced, - #[display(fmt = "{}/{}", num_known, min_head_rpcs)] - #[from(ignore)] - NotEnoughRpcs { - num_known: usize, - min_head_rpcs: usize, - }, - #[display(fmt = "{}/{}", available, needed)] - #[from(ignore)] - NotEnoughSoftLimit { - available: u32, - needed: u32, - }, - NotFound, - NotImplemented, - OriginRequired, - #[error(ignore)] - #[from(ignore)] - OriginNotAllowed(headers::Origin), - #[display(fmt = "{:?}", _0)] - #[error(ignore)] - ParseBytesError(Option), - ParseMsgError(siwe::ParseError), - ParseAddressError, - #[display(fmt = "{:?}, {:?}", _0, _1)] - RateLimited(Authorization, Option), - Redis(RedisError), - RefererRequired, - #[display(fmt = "{:?}", _0)] - #[error(ignore)] - #[from(ignore)] - RefererNotAllowed(headers::Referer), - SemaphoreAcquireError(AcquireError), - SendAppStatError(flume::SendError), - SerdeJson(serde_json::Error), - /// simple way to return an error message to the user and an anyhow to our logs - #[display(fmt = "{}, {}, {:?}", _0, _1, _2)] - StatusCode(StatusCode, String, Option), - /// TODO: what should be attached to the timout? - #[display(fmt = "{:?}", _0)] - #[error(ignore)] - Timeout(Option), - UlidDecode(ulid::DecodeError), - UnknownBlockNumber, - UnknownKey, - UserAgentRequired, - #[error(ignore)] - UserAgentNotAllowed(headers::UserAgent), - UserIdZero, - PaymentRequired, - VerificationError(siwe::VerificationError), - WatchRecvError(tokio::sync::watch::error::RecvError), - WatchSendError, - WebsocketOnly, - #[display(fmt = "{:?}, {}", _0, _1)] - #[error(ignore)] - WithContext(Option>, String), -} - -impl Web3ProxyError { - pub fn into_response_parts(self) -> (StatusCode, JsonRpcResponseEnum) { - // TODO: include a unique request id in the data - let (code, err): (StatusCode, JsonRpcErrorData) = match self { - Self::AccessDenied => { - // TODO: attach something to this trace. probably don't include much in the message though. don't want to leak creds by accident - trace!("access denied"); - ( - StatusCode::FORBIDDEN, - JsonRpcErrorData { - message: Cow::Borrowed("FORBIDDEN"), - code: StatusCode::FORBIDDEN.as_u16().into(), - data: None, - }, - ) - } - Self::Anyhow(err) => { - warn!("anyhow. err={:?}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - // TODO: is it safe to expose all of our anyhow strings? - message: Cow::Owned(err.to_string()), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::BadRequest(err) => { - debug!("BAD_REQUEST: {}", err); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Owned(format!("bad request: {}", err)), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - 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); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: Cow::Owned(format!("bad response: {}", err)), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::BadRouting => { - error!("BadRouting"); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: Cow::Borrowed("bad routing"), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::Database(err) => { - error!("database err={:?}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: Cow::Borrowed("database error!"), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::EipVerificationFailed(err_1, err_191) => { - info!( - "EipVerificationFailed err_1={:#?} err2={:#?}", - err_1, err_191 - ); - ( - StatusCode::UNAUTHORIZED, - JsonRpcErrorData { - message: Cow::Owned(format!( - "both the primary and eip191 verification failed: {:#?}; {:#?}", - err_1, err_191 - )), - code: StatusCode::UNAUTHORIZED.as_u16().into(), - data: None, - }, - ) - } - Self::EthersHttpClient(err) => { - warn!("EthersHttpClientError err={:?}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: Cow::Borrowed("ether http client error"), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::EthersProvider(err) => { - warn!("EthersProviderError err={:?}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: Cow::Borrowed("ether provider error"), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::EthersWsClient(err) => { - warn!("EthersWsClientError err={:?}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: Cow::Borrowed("ether ws client error"), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::FlumeRecv(err) => { - warn!("FlumeRecvError err={:#?}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: Cow::Borrowed("flume recv error!"), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - // Self::JsonRpcForwardedError(x) => (StatusCode::OK, x), - Self::GasEstimateNotU256 => { - warn!("GasEstimateNotU256"); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: Cow::Borrowed("gas estimate result is not an U256"), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::Headers(err) => { - warn!("HeadersError {:?}", err); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Owned(format!("{}", err)), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::Hyper(err) => { - warn!("hyper err={:?}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - // TODO: is it safe to expose these error strings? - message: Cow::Owned(err.to_string()), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::InfluxDb2Request(err) => { - // TODO: attach a request id to the message and to this error so that if people report problems, we can dig in sentry to find out more - error!("influxdb2 err={:?}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: Cow::Borrowed("influxdb2 error!"), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::InvalidBlockBounds { min, max } => { - debug!("InvalidBlockBounds min={} max={}", min, max); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Owned(format!( - "Invalid blocks bounds requested. min ({}) > max ({})", - min, max - )), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::IpAddrParse(err) => { - debug!("IpAddrParse err={:?}", err); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Owned(err.to_string()), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::IpNotAllowed(ip) => { - debug!("IpNotAllowed ip={})", ip); - ( - StatusCode::FORBIDDEN, - JsonRpcErrorData { - message: Cow::Owned(format!("IP ({}) is not allowed!", ip)), - code: StatusCode::FORBIDDEN.as_u16().into(), - data: None, - }, - ) - } - Self::InvalidHeaderValue(err) => { - debug!("InvalidHeaderValue err={:?}", err); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Owned(format!("{}", err)), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::InvalidEip => { - debug!("InvalidEip"); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Borrowed("invalid message eip given"), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::InvalidInviteCode => { - debug!("InvalidInviteCode"); - ( - StatusCode::UNAUTHORIZED, - JsonRpcErrorData { - message: Cow::Borrowed("invalid invite code"), - code: StatusCode::UNAUTHORIZED.as_u16().into(), - data: None, - }, - ) - } - Self::Io(err) => { - warn!("std io err={:?}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - // TODO: is it safe to expose our io error strings? - message: Cow::Owned(err.to_string()), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::UnknownReferralCode => { - debug!("UnknownReferralCode"); - ( - StatusCode::UNAUTHORIZED, - JsonRpcErrorData { - message: Cow::Borrowed("invalid referral code"), - code: StatusCode::UNAUTHORIZED.as_u16().into(), - data: None, - }, - ) - } - Self::InvalidReferer => { - debug!("InvalidReferer"); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Borrowed("invalid referer!"), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::InvalidSignatureLength => { - debug!("InvalidSignatureLength"); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Borrowed("invalid signature length"), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::InvalidUserAgent => { - debug!("InvalidUserAgent"); - ( - StatusCode::FORBIDDEN, - JsonRpcErrorData { - message: Cow::Borrowed("invalid user agent!"), - code: StatusCode::FORBIDDEN.as_u16().into(), - data: None, - }, - ) - } - Self::InvalidUserKey => { - warn!("InvalidUserKey"); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Borrowed("UserKey was not a ULID or UUID"), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::JoinError(err) => { - let code = if err.is_cancelled() { - trace!("JoinError. likely shutting down. err={:?}", err); - StatusCode::BAD_GATEWAY - } else { - warn!("JoinError. err={:?}", err); - StatusCode::INTERNAL_SERVER_ERROR - }; - - ( - code, - JsonRpcErrorData { - // TODO: different messages of cancelled or not? - message: Cow::Borrowed("Unable to complete request"), - code: code.as_u16().into(), - data: None, - }, - ) - } - Self::JsonRpcErrorData(jsonrpc_error_data) => (StatusCode::OK, jsonrpc_error_data), - Self::MsgPackEncode(err) => { - warn!("MsgPackEncode Error: {}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: Cow::Owned(format!("msgpack encode error: {}", err)), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::NoBlockNumberOrHash => { - warn!("NoBlockNumberOrHash"); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: Cow::Borrowed("Blocks here must have a number or hash"), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::NoBlocksKnown => { - error!("NoBlocksKnown"); - ( - StatusCode::BAD_GATEWAY, - JsonRpcErrorData { - message: Cow::Borrowed("no blocks known"), - code: StatusCode::BAD_GATEWAY.as_u16().into(), - data: None, - }, - ) - } - Self::NoConsensusHeadBlock => { - error!("NoConsensusHeadBlock"); - ( - StatusCode::BAD_GATEWAY, - JsonRpcErrorData { - message: Cow::Borrowed("no consensus head block"), - code: StatusCode::BAD_GATEWAY.as_u16().into(), - data: None, - }, - ) - } - Self::NoHandleReady => { - error!("NoHandleReady"); - ( - StatusCode::BAD_GATEWAY, - JsonRpcErrorData { - message: Cow::Borrowed("unable to retry for request handle"), - code: StatusCode::BAD_GATEWAY.as_u16().into(), - data: None, - }, - ) - } - Self::NoServersSynced => { - warn!("NoServersSynced"); - ( - StatusCode::BAD_GATEWAY, - JsonRpcErrorData { - message: Cow::Borrowed("no servers synced"), - code: StatusCode::BAD_GATEWAY.as_u16().into(), - data: None, - }, - ) - } - Self::NotEnoughRpcs { - num_known, - min_head_rpcs, - } => { - error!("NotEnoughRpcs {}/{}", num_known, min_head_rpcs); - ( - StatusCode::BAD_GATEWAY, - JsonRpcErrorData { - message: Cow::Owned(format!( - "not enough rpcs connected {}/{}", - num_known, min_head_rpcs - )), - code: StatusCode::BAD_GATEWAY.as_u16().into(), - data: None, - }, - ) - } - Self::NotEnoughSoftLimit { available, needed } => { - error!("NotEnoughSoftLimit {}/{}", available, needed); - ( - StatusCode::BAD_GATEWAY, - JsonRpcErrorData { - message: Cow::Owned(format!( - "not enough soft limit available {}/{}", - available, needed - )), - code: StatusCode::BAD_GATEWAY.as_u16().into(), - data: None, - }, - ) - } - Self::NotFound => { - // TODO: emit a stat? - // TODO: instead of an error, show a normal html page for 404? - ( - StatusCode::NOT_FOUND, - JsonRpcErrorData { - message: Cow::Borrowed("not found!"), - code: StatusCode::NOT_FOUND.as_u16().into(), - data: None, - }, - ) - } - Self::NotImplemented => { - trace!("NotImplemented"); - ( - StatusCode::NOT_IMPLEMENTED, - JsonRpcErrorData { - message: Cow::Borrowed("work in progress"), - code: StatusCode::NOT_IMPLEMENTED.as_u16().into(), - data: None, - }, - ) - } - Self::OriginRequired => { - trace!("OriginRequired"); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Borrowed("Origin required"), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::OriginNotAllowed(origin) => { - trace!("OriginNotAllowed origin={}", origin); - ( - StatusCode::FORBIDDEN, - JsonRpcErrorData { - message: Cow::Owned(format!("Origin ({}) is not allowed!", origin)), - code: StatusCode::FORBIDDEN.as_u16().into(), - data: None, - }, - ) - } - Self::ParseBytesError(err) => { - trace!("ParseBytesError err={:?}", err); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Borrowed("parse bytes error!"), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::ParseMsgError(err) => { - trace!("ParseMsgError err={:?}", err); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Borrowed("parse message error!"), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::ParseAddressError => { - trace!("ParseAddressError"); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Borrowed("unable to parse address"), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::PaymentRequired => { - trace!("PaymentRequiredError"); - ( - StatusCode::PAYMENT_REQUIRED, - JsonRpcErrorData { - message: Cow::Borrowed("Payment is required and user is not premium"), - code: StatusCode::PAYMENT_REQUIRED.as_u16().into(), - data: None, - }, - ) - } - // TODO: this should actually by the id of the key. multiple users might control one key - Self::RateLimited(authorization, retry_at) => { - // TODO: emit a stat - - let retry_msg = if let Some(retry_at) = retry_at { - let retry_in = retry_at.duration_since(Instant::now()).as_secs(); - - format!(" Retry in {} seconds", retry_in) - } else { - "".to_string() - }; - - // create a string with either the IP or the rpc_key_id - let msg = if authorization.checks.rpc_secret_key_id.is_none() { - format!("too many requests from {}.{}", authorization.ip, retry_msg) - } else { - format!( - "too many requests from rpc key #{}.{}", - authorization.checks.rpc_secret_key_id.unwrap(), - retry_msg, - ) - }; - - ( - StatusCode::TOO_MANY_REQUESTS, - JsonRpcErrorData { - message: Cow::Owned(msg), - code: StatusCode::TOO_MANY_REQUESTS.as_u16().into(), - data: None, - }, - ) - } - Self::Redis(err) => { - warn!("redis err={:?}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: Cow::Borrowed("redis error!"), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::RefererRequired => { - warn!("referer required"); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Borrowed("Referer required"), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::RefererNotAllowed(referer) => { - warn!("referer not allowed referer={:?}", referer); - ( - StatusCode::FORBIDDEN, - JsonRpcErrorData { - message: Cow::Owned(format!("Referer ({:?}) is not allowed", referer)), - code: StatusCode::FORBIDDEN.as_u16().into(), - data: None, - }, - ) - } - Self::SemaphoreAcquireError(err) => { - warn!("semaphore acquire err={:?}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - // TODO: is it safe to expose all of our anyhow strings? - message: Cow::Borrowed("semaphore acquire error"), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::SendAppStatError(err) => { - error!("SendAppStatError err={:?}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: Cow::Borrowed("error stat_sender sending response_stat"), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::SerdeJson(err) => { - warn!("serde json err={:?} source={:?}", err, err.source()); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Owned(format!("de/serialization error! {}", err)), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::StatusCode(status_code, err_msg, err) => { - // different status codes should get different error levels. 500s should warn. 400s should stat - let code = status_code.as_u16(); - if (500..600).contains(&code) { - warn!("server error {} {:?}: {:?}", code, err_msg, err); - } else { - trace!("user error {} {:?}: {:?}", code, err_msg, err); - } - - ( - status_code, - JsonRpcErrorData { - message: Cow::Owned(err_msg), - code: code.into(), - data: None, - }, - ) - } - Self::Timeout(x) => ( - StatusCode::REQUEST_TIMEOUT, - JsonRpcErrorData { - message: Cow::Owned(format!("request timed out: {:?}", x)), - code: StatusCode::REQUEST_TIMEOUT.as_u16().into(), - // TODO: include the actual id! - data: None, - }, - ), - Self::HeaderToString(err) => { - // trace!(?err, "HeaderToString"); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Owned(err.to_string()), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::UlidDecode(err) => { - // trace!(?err, "UlidDecodeError"); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Owned(format!("{}", err)), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::UnknownBlockNumber => { - error!("UnknownBlockNumber"); - ( - StatusCode::BAD_GATEWAY, - JsonRpcErrorData { - message: Cow::Borrowed("no servers synced. unknown eth_blockNumber"), - code: StatusCode::BAD_GATEWAY.as_u16().into(), - data: None, - }, - ) - } - // TODO: stat? - Self::UnknownKey => ( - StatusCode::UNAUTHORIZED, - JsonRpcErrorData { - message: Cow::Borrowed("unknown api key!"), - code: StatusCode::UNAUTHORIZED.as_u16().into(), - data: None, - }, - ), - Self::UserAgentRequired => { - warn!("UserAgentRequired"); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Borrowed("User agent required"), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::UserAgentNotAllowed(ua) => { - warn!("UserAgentNotAllowed ua={}", ua); - ( - StatusCode::FORBIDDEN, - JsonRpcErrorData { - message: Cow::Owned(format!("User agent ({}) is not allowed!", ua)), - code: StatusCode::FORBIDDEN.as_u16().into(), - data: None, - }, - ) - } - Self::UserIdZero => { - warn!("UserIdZero"); - // TODO: this might actually be an application error and not a BAD_REQUEST - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Borrowed("user ids should always be non-zero"), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::VerificationError(err) => { - trace!("VerificationError err={:?}", err); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Borrowed("verification error!"), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::WatchRecvError(err) => { - error!("WatchRecvError err={:?}", err); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: Cow::Borrowed("watch recv error!"), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::WatchSendError => { - error!("WatchSendError"); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: Cow::Borrowed("watch send error!"), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - Self::WebsocketOnly => { - trace!("WebsocketOnly"); - ( - StatusCode::BAD_REQUEST, - JsonRpcErrorData { - message: Cow::Borrowed( - "redirect_public_url not set. only websockets work here", - ), - code: StatusCode::BAD_REQUEST.as_u16().into(), - data: None, - }, - ) - } - Self::WithContext(err, msg) => match err { - Some(err) => { - warn!("{:#?} w/ context {}", err, msg); - return err.into_response_parts(); - } - None => { - warn!("error w/ context {}", msg); - ( - StatusCode::INTERNAL_SERVER_ERROR, - JsonRpcErrorData { - message: Cow::Owned(msg), - code: StatusCode::INTERNAL_SERVER_ERROR.as_u16().into(), - data: None, - }, - ) - } - }, - }; - - (code, JsonRpcResponseEnum::from(err)) - } - - pub fn into_response_with_id(self, id: Box) -> Response { - let (status_code, response_data) = self.into_response_parts(); - - let response = JsonRpcForwardedResponse::from_response_data(response_data, id); - - (status_code, Json(response)).into_response() - } -} - -impl From for Web3ProxyError { - fn from(err: ethers::types::ParseBytesError) -> Self { - Self::ParseBytesError(Some(err)) - } -} - -impl From for Web3ProxyError { - fn from(err: tokio::time::error::Elapsed) -> Self { - Self::Timeout(Some(err)) - } -} - -impl IntoResponse for Web3ProxyError { - #[inline] - fn into_response(self) -> Response { - self.into_response_with_id(Default::default()) - } -} +use crate::errors::Web3ProxyError; +use axum::response::{IntoResponse, Response}; #[inline] pub async fn handler_404() -> Response { Web3ProxyError::NotFound.into_response() } - -pub trait Web3ProxyErrorContext { - fn web3_context>(self, msg: S) -> Result; -} - -impl Web3ProxyErrorContext for Option { - fn web3_context>(self, msg: S) -> Result { - self.ok_or(Web3ProxyError::WithContext(None, msg.into())) - } -} - -impl Web3ProxyErrorContext for Result -where - E: Into, -{ - fn web3_context>(self, msg: S) -> Result { - self.map_err(|err| Web3ProxyError::WithContext(Some(Box::new(err.into())), msg.into())) - } -} diff --git a/web3_proxy/src/frontend/mod.rs b/web3_proxy/src/frontend/mod.rs index b3ef44e6..111b14c3 100644 --- a/web3_proxy/src/frontend/mod.rs +++ b/web3_proxy/src/frontend/mod.rs @@ -1,11 +1,10 @@ //! `frontend` contains HTTP and websocket endpoints for use by a website or web3 wallet. //! //! Important reading about axum extractors: - +// TODO: these are only public so docs are generated. What's a better way to do this? pub mod admin; pub mod authorization; pub mod errors; -// TODO: these are only public so docs are generated. What's a better way to do this? pub mod rpc_proxy_http; pub mod rpc_proxy_ws; pub mod status; @@ -28,7 +27,7 @@ use tokio::sync::broadcast; use tower_http::cors::CorsLayer; use tower_http::sensitive_headers::SetSensitiveRequestHeadersLayer; -use self::errors::Web3ProxyResult; +use crate::errors::Web3ProxyResult; /// simple keys for caching responses #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, EnumCount, EnumIter)] diff --git a/web3_proxy/src/frontend/rpc_proxy_http.rs b/web3_proxy/src/frontend/rpc_proxy_http.rs index d01b72e5..6c6a8516 100644 --- a/web3_proxy/src/frontend/rpc_proxy_http.rs +++ b/web3_proxy/src/frontend/rpc_proxy_http.rs @@ -1,8 +1,8 @@ //! Take a user's HTTP JSON-RPC requests and either respond from local data or proxy the request to a backend rpc server. use super::authorization::{ip_is_authorized, key_is_authorized}; -use super::errors::Web3ProxyError; use super::rpc_proxy_ws::ProxyMode; +use crate::errors::Web3ProxyError; use crate::{app::Web3ProxyApp, jsonrpc::JsonRpcRequestEnum}; use axum::extract::Path; use axum::headers::{Origin, Referer, UserAgent}; diff --git a/web3_proxy/src/frontend/rpc_proxy_ws.rs b/web3_proxy/src/frontend/rpc_proxy_ws.rs index 933a1a56..0880102d 100644 --- a/web3_proxy/src/frontend/rpc_proxy_ws.rs +++ b/web3_proxy/src/frontend/rpc_proxy_ws.rs @@ -3,11 +3,11 @@ //! WebSockets are the preferred method of receiving requests, but not all clients have good support. use super::authorization::{ip_is_authorized, key_is_authorized, Authorization, RequestMetadata}; -use super::errors::{Web3ProxyError, Web3ProxyResponse}; +use crate::errors::{Web3ProxyError, Web3ProxyResponse}; use crate::jsonrpc::JsonRpcId; use crate::{ app::Web3ProxyApp, - frontend::errors::Web3ProxyResult, + errors::Web3ProxyResult, jsonrpc::{JsonRpcForwardedResponse, JsonRpcForwardedResponseEnum, JsonRpcRequest}, }; use anyhow::Context; diff --git a/web3_proxy/src/frontend/users/authentication.rs b/web3_proxy/src/frontend/users/authentication.rs index 2771fe41..805bec0c 100644 --- a/web3_proxy/src/frontend/users/authentication.rs +++ b/web3_proxy/src/frontend/users/authentication.rs @@ -1,7 +1,7 @@ //! Handle registration, logins, and managing account data. use crate::app::Web3ProxyApp; +use crate::errors::{Web3ProxyError, Web3ProxyErrorContext, Web3ProxyResponse}; use crate::frontend::authorization::{login_is_authorized, RpcSecretKey}; -use crate::frontend::errors::{Web3ProxyError, Web3ProxyErrorContext, Web3ProxyResponse}; use crate::user_token::UserBearerToken; use crate::{PostLogin, PostLoginQuery}; use axum::{ diff --git a/web3_proxy/src/frontend/users/mod.rs b/web3_proxy/src/frontend/users/mod.rs index 06269c7b..b3de0a3d 100644 --- a/web3_proxy/src/frontend/users/mod.rs +++ b/web3_proxy/src/frontend/users/mod.rs @@ -6,8 +6,8 @@ pub mod rpc_keys; pub mod stats; pub mod subuser; -use super::errors::{Web3ProxyErrorContext, Web3ProxyResponse}; use crate::app::Web3ProxyApp; +use crate::errors::{Web3ProxyErrorContext, Web3ProxyResponse}; use axum::{ headers::{authorization::Bearer, Authorization}, diff --git a/web3_proxy/src/frontend/users/payment.rs b/web3_proxy/src/frontend/users/payment.rs index e079a51a..98621abc 100644 --- a/web3_proxy/src/frontend/users/payment.rs +++ b/web3_proxy/src/frontend/users/payment.rs @@ -1,5 +1,5 @@ use crate::app::Web3ProxyApp; -use crate::frontend::errors::{Web3ProxyError, Web3ProxyResponse}; +use crate::errors::{Web3ProxyError, Web3ProxyResponse}; use anyhow::{anyhow, Context}; use axum::{ extract::Path, diff --git a/web3_proxy/src/frontend/users/referral.rs b/web3_proxy/src/frontend/users/referral.rs index 4feb1d4f..8f6b97ce 100644 --- a/web3_proxy/src/frontend/users/referral.rs +++ b/web3_proxy/src/frontend/users/referral.rs @@ -1,6 +1,6 @@ //! Handle registration, logins, and managing account data. use crate::app::Web3ProxyApp; -use crate::frontend::errors::Web3ProxyResponse; +use crate::errors::Web3ProxyResponse; use crate::referral_code::ReferralCode; use anyhow::Context; use axum::{ diff --git a/web3_proxy/src/frontend/users/rpc_keys.rs b/web3_proxy/src/frontend/users/rpc_keys.rs index 2230d09d..254275df 100644 --- a/web3_proxy/src/frontend/users/rpc_keys.rs +++ b/web3_proxy/src/frontend/users/rpc_keys.rs @@ -1,7 +1,7 @@ //! Handle registration, logins, and managing account data. use super::super::authorization::RpcSecretKey; -use super::super::errors::{Web3ProxyError, Web3ProxyErrorContext, Web3ProxyResponse}; use crate::app::Web3ProxyApp; +use crate::errors::{Web3ProxyError, Web3ProxyErrorContext, Web3ProxyResponse}; use axum::headers::{Header, Origin, Referer, UserAgent}; use axum::{ headers::{authorization::Bearer, Authorization}, diff --git a/web3_proxy/src/frontend/users/stats.rs b/web3_proxy/src/frontend/users/stats.rs index 25fa9160..8bf5559d 100644 --- a/web3_proxy/src/frontend/users/stats.rs +++ b/web3_proxy/src/frontend/users/stats.rs @@ -1,6 +1,6 @@ //! Handle registration, logins, and managing account data. use crate::app::Web3ProxyApp; -use crate::frontend::errors::{Web3ProxyErrorContext, Web3ProxyResponse}; +use crate::errors::{Web3ProxyErrorContext, Web3ProxyResponse}; use crate::http_params::{ get_chain_id_from_params, get_page_from_params, get_query_start_from_params, }; diff --git a/web3_proxy/src/frontend/users/subuser.rs b/web3_proxy/src/frontend/users/subuser.rs index fe98a21f..56c6ac42 100644 --- a/web3_proxy/src/frontend/users/subuser.rs +++ b/web3_proxy/src/frontend/users/subuser.rs @@ -1,7 +1,7 @@ //! Handle subusers, viewing subusers, and viewing accessible rpc-keys use crate::app::Web3ProxyApp; +use crate::errors::{Web3ProxyError, Web3ProxyErrorContext, Web3ProxyResponse}; use crate::frontend::authorization::RpcSecretKey; -use crate::frontend::errors::{Web3ProxyError, Web3ProxyErrorContext, Web3ProxyResponse}; use anyhow::Context; use axum::{ extract::Query, diff --git a/web3_proxy/src/http_params.rs b/web3_proxy/src/http_params.rs index e965739e..aa0a245e 100644 --- a/web3_proxy/src/http_params.rs +++ b/web3_proxy/src/http_params.rs @@ -1,4 +1,4 @@ -use crate::frontend::errors::{Web3ProxyError, Web3ProxyResult}; +use crate::errors::{Web3ProxyError, Web3ProxyResult}; use crate::relational_db::{DatabaseConnection, DatabaseReplica}; use crate::{app::Web3ProxyApp, user_token::UserBearerToken}; use anyhow::Context; diff --git a/web3_proxy/src/ipc.rs b/web3_proxy/src/ipc.rs new file mode 100644 index 00000000..c898d77c --- /dev/null +++ b/web3_proxy/src/ipc.rs @@ -0,0 +1,16 @@ +use std::path::PathBuf; +use std::sync::Arc; +use tokio::sync::broadcast; + +use crate::app::Web3ProxyApp; +use crate::errors::Web3ProxyResult; + +/// Start an ipc server that has no rate limits +pub async fn serve( + socket_path: PathBuf, + proxy_app: Arc, + mut shutdown_receiver: broadcast::Receiver<()>, + shutdown_complete_sender: broadcast::Sender<()>, +) -> Web3ProxyResult<()> { + todo!(); +} diff --git a/web3_proxy/src/jsonrpc.rs b/web3_proxy/src/jsonrpc.rs index 8f24738a..1f67de1c 100644 --- a/web3_proxy/src/jsonrpc.rs +++ b/web3_proxy/src/jsonrpc.rs @@ -1,4 +1,4 @@ -use crate::frontend::errors::{Web3ProxyError, Web3ProxyResult}; +use crate::errors::{Web3ProxyError, Web3ProxyResult}; use crate::response_cache::JsonRpcResponseEnum; use derive_more::From; use ethers::prelude::ProviderError; diff --git a/web3_proxy/src/jsonrpc_client.rs b/web3_proxy/src/jsonrpc_client.rs deleted file mode 100644 index bab28d20..00000000 --- a/web3_proxy/src/jsonrpc_client.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::app::Web3ProxyApp; -// use crate::frontend::errors::Web3ProxyError; -use ethers::providers::{JsonRpcClient, ProviderError}; -use serde::{de::DeserializeOwned, Serialize}; -use std::fmt::Debug; -use std::sync::Arc; - -/// use the app as an ether's JsonRpcClient -#[derive(Debug)] -struct Web3ProxyAppClient(Arc); - -#[async_trait::async_trait] -impl JsonRpcClient for Web3ProxyAppClient { - type Error = ProviderError; - - async fn request(&self, method: &str, params: T) -> Result - where - T: Debug + Serialize + Send + Sync, - R: DeserializeOwned + Send, - { - todo!("figure out traits"); - // match self.0.internal_request(method, ¶ms).await { - // Ok(x) => Ok(x), - // Err(Web3ProxyError::EthersProvider(err)) => Err(err), - // Err(err) => Err(ProviderError::CustomError(format!("{}", err))), - // } - } -} diff --git a/web3_proxy/src/lib.rs b/web3_proxy/src/lib.rs index a022cccf..13ec65f2 100644 --- a/web3_proxy/src/lib.rs +++ b/web3_proxy/src/lib.rs @@ -4,10 +4,11 @@ pub mod admin_queries; pub mod app; pub mod block_number; pub mod config; +pub mod errors; pub mod frontend; pub mod http_params; +pub mod ipc; pub mod jsonrpc; -pub mod jsonrpc_client; pub mod pagerduty; pub mod prometheus; pub mod referral_code; diff --git a/web3_proxy/src/prometheus.rs b/web3_proxy/src/prometheus.rs index c17b8ecd..a0679e00 100644 --- a/web3_proxy/src/prometheus.rs +++ b/web3_proxy/src/prometheus.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use tokio::sync::broadcast; use crate::app::Web3ProxyApp; -use crate::frontend::errors::Web3ProxyResult; +use crate::errors::Web3ProxyResult; /// Run a prometheus metrics server on the given port. pub async fn serve( diff --git a/web3_proxy/src/response_cache.rs b/web3_proxy/src/response_cache.rs index 0261e590..78fb6f2c 100644 --- a/web3_proxy/src/response_cache.rs +++ b/web3_proxy/src/response_cache.rs @@ -1,6 +1,4 @@ -use crate::{ - frontend::errors::Web3ProxyError, jsonrpc::JsonRpcErrorData, rpcs::blockchain::ArcBlock, -}; +use crate::{errors::Web3ProxyError, jsonrpc::JsonRpcErrorData, rpcs::blockchain::ArcBlock}; use derive_more::From; use ethers::{providers::ProviderError, types::U64}; use hashbrown::hash_map::DefaultHashBuilder; diff --git a/web3_proxy/src/rpcs/blockchain.rs b/web3_proxy/src/rpcs/blockchain.rs index 059cc65f..4d4375db 100644 --- a/web3_proxy/src/rpcs/blockchain.rs +++ b/web3_proxy/src/rpcs/blockchain.rs @@ -4,8 +4,8 @@ use super::many::Web3Rpcs; use super::one::Web3Rpc; use super::transactions::TxStatus; use crate::config::BlockAndRpc; +use crate::errors::{Web3ProxyError, Web3ProxyErrorContext, Web3ProxyResult}; use crate::frontend::authorization::Authorization; -use crate::frontend::errors::{Web3ProxyError, Web3ProxyErrorContext, Web3ProxyResult}; use derive_more::From; use ethers::prelude::{Block, TxHash, H256, U64}; use log::{debug, trace, warn}; diff --git a/web3_proxy/src/rpcs/consensus.rs b/web3_proxy/src/rpcs/consensus.rs index 258d9c19..a9f8397c 100644 --- a/web3_proxy/src/rpcs/consensus.rs +++ b/web3_proxy/src/rpcs/consensus.rs @@ -1,8 +1,8 @@ use super::blockchain::Web3ProxyBlock; use super::many::Web3Rpcs; use super::one::Web3Rpc; +use crate::errors::{Web3ProxyErrorContext, Web3ProxyResult}; use crate::frontend::authorization::Authorization; -use crate::frontend::errors::{Web3ProxyErrorContext, Web3ProxyResult}; use derive_more::Constructor; use ethers::prelude::{H256, U64}; use hashbrown::{HashMap, HashSet}; diff --git a/web3_proxy/src/rpcs/many.rs b/web3_proxy/src/rpcs/many.rs index f39d0d05..c89c99eb 100644 --- a/web3_proxy/src/rpcs/many.rs +++ b/web3_proxy/src/rpcs/many.rs @@ -5,8 +5,8 @@ use super::one::Web3Rpc; use super::request::{OpenRequestHandle, OpenRequestResult, RequestErrorHandler}; use crate::app::{flatten_handle, Web3ProxyApp, Web3ProxyJoinHandle}; use crate::config::{BlockAndRpc, TxHashAndRpc, Web3RpcConfig}; +use crate::errors::{Web3ProxyError, Web3ProxyResult}; use crate::frontend::authorization::{Authorization, RequestMetadata}; -use crate::frontend::errors::{Web3ProxyError, Web3ProxyResult}; use crate::frontend::rpc_proxy_ws::ProxyMode; use crate::jsonrpc::{JsonRpcErrorData, JsonRpcParams, JsonRpcResultData}; use crate::rpcs::transactions::TxStatus; diff --git a/web3_proxy/src/rpcs/one.rs b/web3_proxy/src/rpcs/one.rs index 1c0c6e69..909c35a4 100644 --- a/web3_proxy/src/rpcs/one.rs +++ b/web3_proxy/src/rpcs/one.rs @@ -4,8 +4,8 @@ use super::provider::{connect_http, connect_ws, EthersHttpProvider, EthersWsProv use super::request::{OpenRequestHandle, OpenRequestResult}; use crate::app::{flatten_handle, Web3ProxyJoinHandle}; use crate::config::{BlockAndRpc, Web3RpcConfig}; +use crate::errors::{Web3ProxyError, Web3ProxyResult}; use crate::frontend::authorization::Authorization; -use crate::frontend::errors::{Web3ProxyError, Web3ProxyResult}; use crate::jsonrpc::{JsonRpcParams, JsonRpcResultData}; use crate::rpcs::request::RequestErrorHandler; use anyhow::{anyhow, Context}; diff --git a/web3_proxy/src/rpcs/request.rs b/web3_proxy/src/rpcs/request.rs index c005ecd6..40daecf1 100644 --- a/web3_proxy/src/rpcs/request.rs +++ b/web3_proxy/src/rpcs/request.rs @@ -1,6 +1,6 @@ use super::one::Web3Rpc; +use crate::errors::Web3ProxyResult; use crate::frontend::authorization::Authorization; -use crate::frontend::errors::Web3ProxyResult; use crate::jsonrpc::{JsonRpcParams, JsonRpcResultData}; use anyhow::Context; use chrono::Utc; diff --git a/web3_proxy/src/rpcs/transactions.rs b/web3_proxy/src/rpcs/transactions.rs index 6d080222..7bc160db 100644 --- a/web3_proxy/src/rpcs/transactions.rs +++ b/web3_proxy/src/rpcs/transactions.rs @@ -2,7 +2,8 @@ use super::many::Web3Rpcs; use super::one::Web3Rpc; use super::request::OpenRequestResult; -use crate::frontend::{authorization::Authorization, errors::Web3ProxyResult}; +use crate::errors::Web3ProxyResult; +use crate::frontend::authorization::Authorization; use ethers::prelude::{ProviderError, Transaction, TxHash}; use log::{debug, trace, Level}; use std::sync::Arc; diff --git a/web3_proxy/src/stats/db_queries.rs b/web3_proxy/src/stats/db_queries.rs index 3e373d81..be9dc511 100644 --- a/web3_proxy/src/stats/db_queries.rs +++ b/web3_proxy/src/stats/db_queries.rs @@ -1,6 +1,6 @@ use super::StatType; use crate::app::Web3ProxyApp; -use crate::frontend::errors::{Web3ProxyError, Web3ProxyResponse, Web3ProxyResult}; +use crate::errors::{Web3ProxyError, Web3ProxyResponse, Web3ProxyResult}; use crate::http_params::{ get_chain_id_from_params, get_page_from_params, get_query_start_from_params, get_query_window_seconds_from_params, get_user_id_from_params, diff --git a/web3_proxy/src/stats/influxdb_queries.rs b/web3_proxy/src/stats/influxdb_queries.rs index 541847af..cace3d29 100644 --- a/web3_proxy/src/stats/influxdb_queries.rs +++ b/web3_proxy/src/stats/influxdb_queries.rs @@ -1,8 +1,8 @@ use super::StatType; -use crate::frontend::errors::Web3ProxyErrorContext; +use crate::errors::Web3ProxyErrorContext; use crate::{ app::Web3ProxyApp, - frontend::errors::{Web3ProxyError, Web3ProxyResponse}, + errors::{Web3ProxyError, Web3ProxyResponse}, http_params::{ get_chain_id_from_params, get_query_start_from_params, get_query_stop_from_params, get_query_window_seconds_from_params, diff --git a/web3_proxy/src/stats/mod.rs b/web3_proxy/src/stats/mod.rs index 0aedb115..15cd1918 100644 --- a/web3_proxy/src/stats/mod.rs +++ b/web3_proxy/src/stats/mod.rs @@ -7,8 +7,8 @@ mod stat_buffer; pub use stat_buffer::{SpawnedStatBuffer, StatBuffer}; use crate::app::RpcSecretKeyCache; +use crate::errors::{Web3ProxyError, Web3ProxyResult}; use crate::frontend::authorization::{Authorization, RequestMetadata}; -use crate::frontend::errors::{Web3ProxyError, Web3ProxyResult}; use crate::rpcs::one::Web3Rpc; use anyhow::{anyhow, Context}; use axum::headers::Origin; diff --git a/web3_proxy/src/stats/stat_buffer.rs b/web3_proxy/src/stats/stat_buffer.rs index c7028204..9d59f0d5 100644 --- a/web3_proxy/src/stats/stat_buffer.rs +++ b/web3_proxy/src/stats/stat_buffer.rs @@ -1,6 +1,6 @@ use super::{AppStat, RpcQueryKey}; use crate::app::{RpcSecretKeyCache, Web3ProxyJoinHandle}; -use crate::frontend::errors::Web3ProxyResult; +use crate::errors::Web3ProxyResult; use derive_more::From; use futures::stream; use hashbrown::HashMap;