better error handling for proxy_web3_rpc()

This commit is contained in:
Rory Neithinger 2023-03-19 18:52:28 -07:00
parent c32d12b5e0
commit beac7ee017
10 changed files with 175 additions and 53 deletions

@ -18,6 +18,7 @@ use crate::stats::{AppStat, RpcQueryStats, StatBuffer};
use crate::user_token::UserBearerToken; use crate::user_token::UserBearerToken;
use anyhow::Context; use anyhow::Context;
use axum::headers::{Origin, Referer, UserAgent}; use axum::headers::{Origin, Referer, UserAgent};
use axum::http::StatusCode;
use chrono::Utc; use chrono::Utc;
use deferred_rate_limiter::DeferredRateLimiter; use deferred_rate_limiter::DeferredRateLimiter;
use derive_more::From; use derive_more::From;
@ -1157,7 +1158,7 @@ impl Web3ProxyApp {
) -> Web3ProxyResult<(JsonRpcForwardedResponse, Vec<Arc<Web3Rpc>>)> { ) -> Web3ProxyResult<(JsonRpcForwardedResponse, Vec<Arc<Web3Rpc>>)> {
// trace!("Received request: {:?}", request); // trace!("Received request: {:?}", request);
let request_metadata = Arc::new(RequestMetadata::new(request.num_bytes())?); let request_metadata = Arc::new(RequestMetadata::new(request.num_bytes()));
let mut kafka_stuff = None; let mut kafka_stuff = None;
@ -1338,10 +1339,7 @@ impl Web3ProxyApp {
} }
None => { None => {
// TODO: what does geth do if this happens? // TODO: what does geth do if this happens?
// TODO: i think we want a 502 so that haproxy retries on another server return Err(Web3ProxyError::UnknownBlockNumber);
return Err(
anyhow::anyhow!("no servers synced. unknown eth_blockNumber").into(),
);
} }
} }
} }
@ -1429,7 +1427,7 @@ impl Web3ProxyApp {
let head_block_num = head_block_num let head_block_num = head_block_num
.or(self.balanced_rpcs.head_block_num()) .or(self.balanced_rpcs.head_block_num())
.ok_or_else(|| anyhow::anyhow!("no servers synced"))?; .ok_or_else(|| Web3ProxyError::NoServersSynced)?;
// TODO: error/wait if no head block! // TODO: error/wait if no head block!
@ -1607,7 +1605,7 @@ impl Web3ProxyApp {
return Ok(( return Ok((
JsonRpcForwardedResponse::from_str( JsonRpcForwardedResponse::from_str(
"invalid request", "invalid request",
None, Some(StatusCode::BAD_REQUEST.as_u16().into()),
Some(request_id), Some(request_id),
), ),
vec![], vec![],
@ -1760,17 +1758,10 @@ impl Web3ProxyApp {
// TODO: only cache the inner response // TODO: only cache the inner response
// TODO: how are we going to stream this? // TODO: how are we going to stream this?
// TODO: check response size. if its very large, return it in a custom Error type that bypasses caching? or will moka do that for us? // TODO: check response size. if its very large, return it in a custom Error type that bypasses caching? or will moka do that for us?
Ok::<_, anyhow::Error>(response) Ok::<_, Web3ProxyError>(response)
}) })
.await // TODO: add context (error while caching and forwarding response {})
// TODO: what is the best way to handle an Arc here? .await?
.map_err(|err| {
// TODO: emit a stat for an error
anyhow::anyhow!(
"error while caching and forwarding response: {}",
err
)
})?
} else { } else {
self.balanced_rpcs self.balanced_rpcs
.try_proxy_connection( .try_proxy_connection(

@ -33,7 +33,7 @@ impl Web3ProxyApp {
.context("finding request size")? .context("finding request size")?
.len(); .len();
let request_metadata = Arc::new(RequestMetadata::new(request_bytes).unwrap()); let request_metadata = Arc::new(RequestMetadata::new(request_bytes));
let (subscription_abort_handle, subscription_registration) = AbortHandle::new_pair(); let (subscription_abort_handle, subscription_registration) = AbortHandle::new_pair();
@ -67,7 +67,7 @@ impl Web3ProxyApp {
}; };
// TODO: what should the payload for RequestMetadata be? // TODO: what should the payload for RequestMetadata be?
let request_metadata = Arc::new(RequestMetadata::new(0).unwrap()); let request_metadata = Arc::new(RequestMetadata::new(0));
// TODO: make a struct for this? using our JsonRpcForwardedResponse won't work because it needs an id // TODO: make a struct for this? using our JsonRpcForwardedResponse won't work because it needs an id
let response_json = json!({ let response_json = json!({
@ -133,7 +133,7 @@ impl Web3ProxyApp {
// TODO: do something with this handle? // TODO: do something with this handle?
tokio::spawn(async move { tokio::spawn(async move {
while let Some(Ok(new_tx_state)) = pending_tx_receiver.next().await { while let Some(Ok(new_tx_state)) = pending_tx_receiver.next().await {
let request_metadata = Arc::new(RequestMetadata::new(0).unwrap()); let request_metadata = Arc::new(RequestMetadata::new(0));
let new_tx = match new_tx_state { let new_tx = match new_tx_state {
TxStatus::Pending(tx) => tx, TxStatus::Pending(tx) => tx,
@ -208,7 +208,7 @@ impl Web3ProxyApp {
// TODO: do something with this handle? // TODO: do something with this handle?
tokio::spawn(async move { tokio::spawn(async move {
while let Some(Ok(new_tx_state)) = pending_tx_receiver.next().await { while let Some(Ok(new_tx_state)) = pending_tx_receiver.next().await {
let request_metadata = Arc::new(RequestMetadata::new(0).unwrap()); let request_metadata = Arc::new(RequestMetadata::new(0));
let new_tx = match new_tx_state { let new_tx = match new_tx_state {
TxStatus::Pending(tx) => tx, TxStatus::Pending(tx) => tx,
@ -284,7 +284,7 @@ impl Web3ProxyApp {
// TODO: do something with this handle? // TODO: do something with this handle?
tokio::spawn(async move { tokio::spawn(async move {
while let Some(Ok(new_tx_state)) = pending_tx_receiver.next().await { while let Some(Ok(new_tx_state)) = pending_tx_receiver.next().await {
let request_metadata = Arc::new(RequestMetadata::new(0).unwrap()); let request_metadata = Arc::new(RequestMetadata::new(0));
let new_tx = match new_tx_state { let new_tx = match new_tx_state {
TxStatus::Pending(tx) => tx, TxStatus::Pending(tx) => tx,

@ -1,4 +1,5 @@
//! Helper functions for turning ether's BlockNumber into numbers and updating incoming queries to match. //! Helper functions for turning ether's BlockNumber into numbers and updating incoming queries to match.
use crate::frontend::errors::{Web3ProxyError, Web3ProxyResult};
use anyhow::Context; use anyhow::Context;
use ethers::{ use ethers::{
prelude::{BlockNumber, U64}, prelude::{BlockNumber, U64},
@ -126,7 +127,7 @@ pub async fn block_needed(
params: Option<&mut serde_json::Value>, params: Option<&mut serde_json::Value>,
head_block_num: U64, head_block_num: U64,
rpcs: &Web3Rpcs, rpcs: &Web3Rpcs,
) -> anyhow::Result<BlockNeeded> { ) -> Web3ProxyResult<BlockNeeded> {
// some requests have potentially very large responses // some requests have potentially very large responses
// TODO: only skip caching if the response actually is large // TODO: only skip caching if the response actually is large
if method.starts_with("trace_") || method == "debug_traceTransaction" { if method.starts_with("trace_") || method == "debug_traceTransaction" {
@ -179,7 +180,7 @@ pub async fn block_needed(
// TODO: this shouldn't be a 500. this should be a 400. 500 will make haproxy retry a bunch // TODO: this shouldn't be a 500. this should be a 400. 500 will make haproxy retry a bunch
let obj = params[0] let obj = params[0]
.as_object_mut() .as_object_mut()
.ok_or_else(|| anyhow::anyhow!("invalid format"))?; .ok_or_else(|| Web3ProxyError::BadRequest("invalid format".to_string()))?;
if obj.contains_key("blockHash") { if obj.contains_key("blockHash") {
return Ok(BlockNeeded::CacheSuccessForever); return Ok(BlockNeeded::CacheSuccessForever);

@ -88,11 +88,11 @@ pub struct RequestMetadata {
} }
impl RequestMetadata { impl RequestMetadata {
pub fn new(request_bytes: usize) -> anyhow::Result<Self> { pub fn new(request_bytes: usize) -> Self {
// TODO: how can we do this without turning it into a string first. this is going to slow us down! // TODO: how can we do this without turning it into a string first. this is going to slow us down!
let request_bytes = request_bytes as u64; let request_bytes = request_bytes as u64;
let new = Self { Self {
start_instant: Instant::now(), start_instant: Instant::now(),
request_bytes, request_bytes,
archive_request: false.into(), archive_request: false.into(),
@ -102,9 +102,7 @@ impl RequestMetadata {
response_bytes: 0.into(), response_bytes: 0.into(),
response_millis: 0.into(), response_millis: 0.into(),
response_from_backup_rpc: false.into(), response_from_backup_rpc: false.into(),
}; }
Ok(new)
} }
} }

@ -4,6 +4,7 @@ use super::authorization::Authorization;
use crate::jsonrpc::JsonRpcForwardedResponse; use crate::jsonrpc::JsonRpcForwardedResponse;
use std::net::IpAddr; use std::net::IpAddr;
use std::sync::Arc;
use axum::{ use axum::{
headers, headers,
@ -33,11 +34,18 @@ pub enum Web3ProxyError {
#[error(ignore)] #[error(ignore)]
#[from(ignore)] #[from(ignore)]
BadRequest(String), BadRequest(String),
SemaphoreAcquireError(AcquireError),
Database(DbErr), Database(DbErr),
EthersHttpClientError(ethers::prelude::HttpClientError),
EthersProviderError(ethers::prelude::ProviderError),
EthersWsClientError(ethers::prelude::WsClientError),
Headers(headers::Error), Headers(headers::Error),
HeaderToString(ToStrError), HeaderToString(ToStrError),
InfluxDb2RequestError(influxdb2::RequestError), InfluxDb2RequestError(influxdb2::RequestError),
#[display(fmt = "{} > {}", min, max)]
InvalidBlockBounds {
min: u64,
max: u64,
},
InvalidHeaderValue(InvalidHeaderValue), InvalidHeaderValue(InvalidHeaderValue),
IpAddrParse(AddrParseError), IpAddrParse(AddrParseError),
#[error(ignore)] #[error(ignore)]
@ -45,6 +53,8 @@ pub enum Web3ProxyError {
IpNotAllowed(IpAddr), IpNotAllowed(IpAddr),
JoinError(JoinError), JoinError(JoinError),
MsgPackEncode(rmp_serde::encode::Error), MsgPackEncode(rmp_serde::encode::Error),
NoServersSynced,
NoHandleReady,
NotFound, NotFound,
OriginRequired, OriginRequired,
#[error(ignore)] #[error(ignore)]
@ -58,16 +68,23 @@ pub enum Web3ProxyError {
#[error(ignore)] #[error(ignore)]
#[from(ignore)] #[from(ignore)]
RefererNotAllowed(headers::Referer), RefererNotAllowed(headers::Referer),
SeaRc(Arc<Web3ProxyError>),
SemaphoreAcquireError(AcquireError),
SerdeJson(serde_json::Error),
/// simple way to return an error message to the user and an anyhow to our logs /// simple way to return an error message to the user and an anyhow to our logs
#[display(fmt = "{}, {}, {:?}", _0, _1, _2)] #[display(fmt = "{}, {}, {:?}", _0, _1, _2)]
StatusCode(StatusCode, String, Option<anyhow::Error>), StatusCode(StatusCode, String, Option<anyhow::Error>),
/// TODO: what should be attached to the timout? /// TODO: what should be attached to the timout?
Timeout(tokio::time::error::Elapsed), #[display(fmt = "{:?}", _0)]
#[error(ignore)]
Timeout(Option<tokio::time::error::Elapsed>),
UlidDecode(ulid::DecodeError), UlidDecode(ulid::DecodeError),
UnknownBlockNumber,
UnknownKey, UnknownKey,
UserAgentRequired, UserAgentRequired,
#[error(ignore)] #[error(ignore)]
UserAgentNotAllowed(headers::UserAgent), UserAgentNotAllowed(headers::UserAgent),
WatchRecvError(tokio::sync::watch::error::RecvError),
} }
impl Web3ProxyError { impl Web3ProxyError {
@ -120,6 +137,39 @@ impl Web3ProxyError {
), ),
) )
} }
Self::EthersHttpClientError(err) => {
warn!("EthersHttpClientError err={:?}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
JsonRpcForwardedResponse::from_str(
"ether http client error",
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
Self::EthersProviderError(err) => {
warn!("EthersProviderError err={:?}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
JsonRpcForwardedResponse::from_str(
"ether provider error",
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
Self::EthersWsClientError(err) => {
warn!("EthersWsClientError err={:?}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
JsonRpcForwardedResponse::from_str(
"ether ws client error",
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
Self::Headers(err) => { Self::Headers(err) => {
warn!("HeadersError {:?}", err); warn!("HeadersError {:?}", err);
( (
@ -143,6 +193,20 @@ impl Web3ProxyError {
), ),
) )
} }
Self::InvalidBlockBounds { min, max } => {
warn!("InvalidBlockBounds min={} max={}", min, max);
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_string(
format!(
"Invalid blocks bounds requested. min ({}) > max ({})",
min, max
),
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
Self::IpAddrParse(err) => { Self::IpAddrParse(err) => {
warn!("IpAddrParse err={:?}", err); warn!("IpAddrParse err={:?}", err);
( (
@ -206,6 +270,28 @@ impl Web3ProxyError {
), ),
) )
} }
Self::NoServersSynced => {
warn!("NoServersSynced");
(
StatusCode::INTERNAL_SERVER_ERROR,
JsonRpcForwardedResponse::from_str(
"no servers synced",
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
Self::NoHandleReady => {
error!("NoHandleReady");
(
StatusCode::INTERNAL_SERVER_ERROR,
JsonRpcForwardedResponse::from_str(
"unable to retry for request handle",
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
Self::NotFound => { Self::NotFound => {
// TODO: emit a stat? // TODO: emit a stat?
// TODO: instead of an error, show a normal html page for 404 // TODO: instead of an error, show a normal html page for 404
@ -305,6 +391,11 @@ impl Web3ProxyError {
), ),
) )
} }
Self::SeaRc(err) => match migration::SeaRc::try_unwrap(err) {
Ok(err) => err,
Err(err) => Self::Anyhow(anyhow::anyhow!("{}", err)),
}
.into_response_parts(),
Self::SemaphoreAcquireError(err) => { Self::SemaphoreAcquireError(err) => {
warn!("semaphore acquire err={:?}", err); warn!("semaphore acquire err={:?}", err);
( (
@ -317,6 +408,17 @@ impl Web3ProxyError {
), ),
) )
} }
Self::SerdeJson(err) => {
warn!("serde json err={:?}", err);
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
"de/serialization error!",
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
Self::StatusCode(status_code, err_msg, err) => { Self::StatusCode(status_code, err_msg, err) => {
// different status codes should get different error levels. 500s should warn. 400s should stat // different status codes should get different error levels. 500s should warn. 400s should stat
let code = status_code.as_u16(); let code = status_code.as_u16();
@ -362,6 +464,17 @@ impl Web3ProxyError {
), ),
) )
} }
Self::UnknownBlockNumber => {
error!("UnknownBlockNumber");
(
StatusCode::BAD_GATEWAY,
JsonRpcForwardedResponse::from_str(
"no servers synced. unknown eth_blockNumber",
Some(StatusCode::BAD_GATEWAY.as_u16().into()),
None,
),
)
}
// TODO: stat? // TODO: stat?
Self::UnknownKey => ( Self::UnknownKey => (
StatusCode::UNAUTHORIZED, StatusCode::UNAUTHORIZED,
@ -393,10 +506,27 @@ impl Web3ProxyError {
), ),
) )
} }
Self::WatchRecvError(err) => {
error!("WatchRecvError err={:?}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
JsonRpcForwardedResponse::from_str(
"watch recv error!",
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
} }
} }
} }
impl From<tokio::time::error::Elapsed> for Web3ProxyError {
fn from(err: tokio::time::error::Elapsed) -> Self {
Self::Timeout(Some(err))
}
}
impl IntoResponse for Web3ProxyError { impl IntoResponse for Web3ProxyError {
fn into_response(self) -> Response { fn into_response(self) -> Response {
// TODO: include the request id in these so that users can give us something that will point to logs // TODO: include the request id in these so that users can give us something that will point to logs

@ -378,7 +378,7 @@ async fn handle_socket_payload(
// TODO: move this logic into the app? // TODO: move this logic into the app?
let request_bytes = json_request.num_bytes(); let request_bytes = json_request.num_bytes();
let request_metadata = Arc::new(RequestMetadata::new(request_bytes).unwrap()); let request_metadata = Arc::new(RequestMetadata::new(request_bytes));
let subscription_id = json_request.params.unwrap().to_string(); let subscription_id = json_request.params.unwrap().to_string();

@ -1,3 +1,4 @@
use crate::frontend::errors::Web3ProxyResult;
use derive_more::From; use derive_more::From;
use ethers::prelude::{HttpClientError, ProviderError, WsClientError}; use ethers::prelude::{HttpClientError, ProviderError, WsClientError};
use serde::de::{self, Deserializer, MapAccess, SeqAccess, Visitor}; use serde::de::{self, Deserializer, MapAccess, SeqAccess, Visitor};
@ -240,7 +241,7 @@ impl JsonRpcForwardedResponse {
} }
} }
pub fn from_ethers_error(e: ProviderError, id: Box<RawValue>) -> anyhow::Result<Self> { pub fn from_ethers_error(e: ProviderError, id: Box<RawValue>) -> Web3ProxyResult<Self> {
// TODO: move turning ClientError into json to a helper function? // TODO: move turning ClientError into json to a helper function?
let code; let code;
let message: String; let message: String;
@ -302,7 +303,7 @@ impl JsonRpcForwardedResponse {
pub fn try_from_response_result( pub fn try_from_response_result(
result: Result<Box<RawValue>, ProviderError>, result: Result<Box<RawValue>, ProviderError>,
id: Box<RawValue>, id: Box<RawValue>,
) -> anyhow::Result<Self> { ) -> Web3ProxyResult<Self> {
match result { match result {
Ok(response) => Ok(Self::from_response(response, id)), Ok(response) => Ok(Self::from_response(response, id)),
Err(e) => Self::from_ethers_error(e, id), Err(e) => Self::from_ethers_error(e, id),

@ -4,6 +4,7 @@ use super::many::Web3Rpcs;
use super::one::Web3Rpc; use super::one::Web3Rpc;
use super::transactions::TxStatus; use super::transactions::TxStatus;
use crate::frontend::authorization::Authorization; use crate::frontend::authorization::Authorization;
use crate::frontend::errors::Web3ProxyResult;
use crate::{config::BlockAndRpc, jsonrpc::JsonRpcRequest}; use crate::{config::BlockAndRpc, jsonrpc::JsonRpcRequest};
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use derive_more::From; use derive_more::From;
@ -158,7 +159,7 @@ impl Web3Rpcs {
&self, &self,
block: Web3ProxyBlock, block: Web3ProxyBlock,
heaviest_chain: bool, heaviest_chain: bool,
) -> anyhow::Result<Web3ProxyBlock> { ) -> Web3ProxyResult<Web3ProxyBlock> {
// TODO: i think we can rearrange this function to make it faster on the hot path // TODO: i think we can rearrange this function to make it faster on the hot path
let block_hash = block.hash(); let block_hash = block.hash();
@ -196,7 +197,7 @@ impl Web3Rpcs {
authorization: &Arc<Authorization>, authorization: &Arc<Authorization>,
hash: &H256, hash: &H256,
rpc: Option<&Arc<Web3Rpc>>, rpc: Option<&Arc<Web3Rpc>>,
) -> anyhow::Result<Web3ProxyBlock> { ) -> Web3ProxyResult<Web3ProxyBlock> {
// first, try to get the hash from our cache // first, try to get the hash from our cache
// the cache is set last, so if its here, its everywhere // the cache is set last, so if its here, its everywhere
// TODO: use try_get_with // TODO: use try_get_with
@ -267,7 +268,7 @@ impl Web3Rpcs {
&self, &self,
authorization: &Arc<Authorization>, authorization: &Arc<Authorization>,
num: &U64, num: &U64,
) -> anyhow::Result<(H256, u64)> { ) -> Web3ProxyResult<(H256, u64)> {
let (block, block_depth) = self.cannonical_block(authorization, num).await?; let (block, block_depth) = self.cannonical_block(authorization, num).await?;
let hash = *block.hash(); let hash = *block.hash();
@ -281,7 +282,7 @@ impl Web3Rpcs {
&self, &self,
authorization: &Arc<Authorization>, authorization: &Arc<Authorization>,
num: &U64, num: &U64,
) -> anyhow::Result<(Web3ProxyBlock, u64)> { ) -> Web3ProxyResult<(Web3ProxyBlock, u64)> {
// we only have blocks by hash now // we only have blocks by hash now
// maybe save them during save_block in a blocks_by_number Cache<U64, Vec<ArcBlock>> // maybe save them during save_block in a blocks_by_number Cache<U64, Vec<ArcBlock>>
// if theres multiple, use petgraph to find the one on the main chain (and remove the others if they have enough confirmations) // if theres multiple, use petgraph to find the one on the main chain (and remove the others if they have enough confirmations)

@ -7,6 +7,7 @@ use crate::app::{flatten_handle, AnyhowJoinHandle, Web3ProxyApp};
///! Load balanced communication with a group of web3 providers ///! Load balanced communication with a group of web3 providers
use crate::config::{BlockAndRpc, TxHashAndRpc, Web3RpcConfig}; use crate::config::{BlockAndRpc, TxHashAndRpc, Web3RpcConfig};
use crate::frontend::authorization::{Authorization, RequestMetadata}; use crate::frontend::authorization::{Authorization, RequestMetadata};
use crate::frontend::errors::{Web3ProxyError, Web3ProxyResult};
use crate::frontend::rpc_proxy_ws::ProxyMode; use crate::frontend::rpc_proxy_ws::ProxyMode;
use crate::jsonrpc::{JsonRpcForwardedResponse, JsonRpcRequest}; use crate::jsonrpc::{JsonRpcForwardedResponse, JsonRpcRequest};
use crate::rpcs::transactions::TxStatus; use crate::rpcs::transactions::TxStatus;
@ -462,7 +463,7 @@ impl Web3Rpcs {
params: Option<&serde_json::Value>, params: Option<&serde_json::Value>,
error_level: Level, error_level: Level,
// TODO: remove this box once i figure out how to do the options // TODO: remove this box once i figure out how to do the options
) -> anyhow::Result<JsonRpcForwardedResponse> { ) -> Web3ProxyResult<JsonRpcForwardedResponse> {
// TODO: if only 1 active_request_handles, do self.try_send_request? // TODO: if only 1 active_request_handles, do self.try_send_request?
let responses = active_request_handles let responses = active_request_handles
@ -540,7 +541,7 @@ impl Web3Rpcs {
// TODO: if we are checking for the consensus head, i don' think we need min_block_needed/max_block_needed // TODO: if we are checking for the consensus head, i don' think we need min_block_needed/max_block_needed
min_block_needed: Option<&U64>, min_block_needed: Option<&U64>,
max_block_needed: Option<&U64>, max_block_needed: Option<&U64>,
) -> anyhow::Result<OpenRequestResult> { ) -> Web3ProxyResult<OpenRequestResult> {
let usable_rpcs_by_tier_and_head_number: BTreeMap<(u64, Option<U64>), Vec<Arc<Web3Rpc>>> = { let usable_rpcs_by_tier_and_head_number: BTreeMap<(u64, Option<U64>), Vec<Arc<Web3Rpc>>> = {
let synced_connections = self.watch_consensus_rpcs_sender.borrow().clone(); let synced_connections = self.watch_consensus_rpcs_sender.borrow().clone();
@ -569,11 +570,10 @@ impl Web3Rpcs {
cmp::Ordering::Greater => { cmp::Ordering::Greater => {
// TODO: force a debug log of the original request to see if our logic is wrong? // TODO: force a debug log of the original request to see if our logic is wrong?
// TODO: attach the rpc_key_id so we can find the user to ask if they need help // TODO: attach the rpc_key_id so we can find the user to ask if they need help
return Err(anyhow::anyhow!( return Err(Web3ProxyError::InvalidBlockBounds {
"Invalid blocks bounds requested. min ({}) > max ({})", min: min_block_needed.as_u64(),
min_block_needed, max: max_block_needed.as_u64(),
max_block_needed });
));
} }
} }
} }
@ -877,7 +877,7 @@ impl Web3Rpcs {
request_metadata: Option<&Arc<RequestMetadata>>, request_metadata: Option<&Arc<RequestMetadata>>,
min_block_needed: Option<&U64>, min_block_needed: Option<&U64>,
max_block_needed: Option<&U64>, max_block_needed: Option<&U64>,
) -> anyhow::Result<JsonRpcForwardedResponse> { ) -> Web3ProxyResult<JsonRpcForwardedResponse> {
let mut skip_rpcs = vec![]; let mut skip_rpcs = vec![];
let mut method_not_available_response = None; let mut method_not_available_response = None;
@ -1099,7 +1099,7 @@ impl Web3Rpcs {
error_level: Level, error_level: Level,
max_count: Option<usize>, max_count: Option<usize>,
always_include_backups: bool, always_include_backups: bool,
) -> anyhow::Result<JsonRpcForwardedResponse> { ) -> Web3ProxyResult<JsonRpcForwardedResponse> {
let mut watch_consensus_rpcs = self.watch_consensus_rpcs_sender.subscribe(); let mut watch_consensus_rpcs = self.watch_consensus_rpcs_sender.subscribe();
loop { loop {
@ -1205,7 +1205,7 @@ impl Web3Rpcs {
request_metadata: Option<&Arc<RequestMetadata>>, request_metadata: Option<&Arc<RequestMetadata>>,
min_block_needed: Option<&U64>, min_block_needed: Option<&U64>,
max_block_needed: Option<&U64>, max_block_needed: Option<&U64>,
) -> anyhow::Result<JsonRpcForwardedResponse> { ) -> Web3ProxyResult<JsonRpcForwardedResponse> {
match authorization.checks.proxy_mode { match authorization.checks.proxy_mode {
ProxyMode::Debug | ProxyMode::Best => { ProxyMode::Debug | ProxyMode::Best => {
self.try_send_best_consensus_head_connection( self.try_send_best_consensus_head_connection(

@ -5,6 +5,7 @@ use super::request::{OpenRequestHandle, OpenRequestResult};
use crate::app::{flatten_handle, AnyhowJoinHandle}; use crate::app::{flatten_handle, AnyhowJoinHandle};
use crate::config::{BlockAndRpc, Web3RpcConfig}; use crate::config::{BlockAndRpc, Web3RpcConfig};
use crate::frontend::authorization::Authorization; use crate::frontend::authorization::Authorization;
use crate::frontend::errors::{Web3ProxyError, Web3ProxyResult};
use crate::rpcs::request::RequestErrorHandler; use crate::rpcs::request::RequestErrorHandler;
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use ethers::prelude::{Bytes, Middleware, ProviderError, TxHash, H256, U64}; use ethers::prelude::{Bytes, Middleware, ProviderError, TxHash, H256, U64};
@ -1159,7 +1160,7 @@ impl Web3Rpc {
authorization: &'a Arc<Authorization>, authorization: &'a Arc<Authorization>,
max_wait: Option<Duration>, max_wait: Option<Duration>,
unlocked_provider: Option<Arc<Web3Provider>>, unlocked_provider: Option<Arc<Web3Provider>>,
) -> anyhow::Result<OpenRequestHandle> { ) -> Web3ProxyResult<OpenRequestHandle> {
let max_wait = max_wait.map(|x| Instant::now() + x); let max_wait = max_wait.map(|x| Instant::now() + x);
loop { loop {
@ -1181,8 +1182,7 @@ impl Web3Rpc {
if let Some(max_wait) = max_wait { if let Some(max_wait) = max_wait {
if retry_at > max_wait { if retry_at > max_wait {
// break now since we will wait past our maximum wait time // break now since we will wait past our maximum wait time
// TODO: don't use anyhow. use specific error type return Err(Web3ProxyError::Timeout(None));
return Err(anyhow::anyhow!("timeout waiting for request handle"));
} }
} }
@ -1196,7 +1196,7 @@ impl Web3Rpc {
let now = Instant::now(); let now = Instant::now();
if now > max_wait { if now > max_wait {
return Err(anyhow::anyhow!("unable to retry for request handle")); return Err(Web3ProxyError::NoHandleReady);
} }
} }
@ -1214,7 +1214,7 @@ impl Web3Rpc {
authorization: &Arc<Authorization>, authorization: &Arc<Authorization>,
// TODO: borrow on this instead of needing to clone the Arc? // TODO: borrow on this instead of needing to clone the Arc?
unlocked_provider: Option<Arc<Web3Provider>>, unlocked_provider: Option<Arc<Web3Provider>>,
) -> anyhow::Result<OpenRequestResult> { ) -> Web3ProxyResult<OpenRequestResult> {
// TODO: think more about this read block // TODO: think more about this read block
// TODO: this should *not* be new_head_client. this should be a separate object // TODO: this should *not* be new_head_client. this should be a separate object
if unlocked_provider.is_some() || self.provider.read().await.is_some() { if unlocked_provider.is_some() || self.provider.read().await.is_some() {