web3-proxy/web3_proxy/src/frontend/errors.rs

934 lines
34 KiB
Rust
Raw Normal View History

2022-10-18 00:47:58 +03:00
//! Utlities for logging errors for admins and displaying errors to users.
use super::authorization::Authorization;
use crate::jsonrpc::JsonRpcForwardedResponse;
use std::net::IpAddr;
use std::sync::Arc;
2022-08-10 05:37:34 +03:00
use axum::{
2022-10-27 00:39:26 +03:00
headers,
2022-08-10 05:37:34 +03:00
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
use derive_more::{Display, Error, From};
2022-10-27 00:39:26 +03:00
use http::header::InvalidHeaderValue;
use ipnet::AddrParseError;
use log::{debug, error, info, trace, warn};
2022-11-14 21:24:52 +03:00
use migration::sea_orm::DbErr;
2022-09-15 20:57:24 +03:00
use redis_rate_limiter::redis::RedisError;
2022-10-27 00:39:26 +03:00
use reqwest::header::ToStrError;
2022-12-14 05:13:23 +03:00
use tokio::{sync::AcquireError, task::JoinError, time::Instant};
2022-06-05 22:58:47 +03:00
pub type Web3ProxyResult<T> = Result<T, Web3ProxyError>;
2022-10-20 23:26:14 +03:00
// TODO: take "IntoResponse" instead of Response?
pub type Web3ProxyResponse = Web3ProxyResult<Response>;
2022-08-17 00:43:39 +03:00
impl From<Web3ProxyError> for Web3ProxyResult<()> {
fn from(value: Web3ProxyError) -> Self {
Err(value)
}
}
2022-10-20 23:26:14 +03:00
// TODO:
#[derive(Debug, Display, Error, From)]
pub enum Web3ProxyError {
2022-12-06 00:13:36 +03:00
AccessDenied,
#[error(ignore)]
2022-08-17 00:43:39 +03:00
Anyhow(anyhow::Error),
Arc(Arc<Web3ProxyError>),
#[error(ignore)]
#[from(ignore)]
2023-02-03 21:56:05 +03:00
BadRequest(String),
#[error(ignore)]
#[from(ignore)]
BadResponse(String),
BadRouting,
2022-08-27 08:42:25 +03:00
Database(DbErr),
#[display(fmt = "{:#?}, {:#?}", _0, _1)]
EipVerificationFailed(Box<Web3ProxyError>, Box<Web3ProxyError>),
EthersHttpClientError(ethers::prelude::HttpClientError),
EthersProviderError(ethers::prelude::ProviderError),
EthersWsClientError(ethers::prelude::WsClientError),
2023-03-20 23:45:21 +03:00
FlumeRecvError(flume::RecvError),
GasEstimateNotU256,
2023-03-03 04:39:50 +03:00
Headers(headers::Error),
2022-10-27 00:39:26 +03:00
HeaderToString(ToStrError),
InfluxDb2RequestError(influxdb2::RequestError),
#[display(fmt = "{} > {}", min, max)]
2023-03-20 23:45:21 +03:00
#[from(ignore)]
InvalidBlockBounds {
min: u64,
max: u64,
},
2022-10-27 00:39:26 +03:00
InvalidHeaderValue(InvalidHeaderValue),
InvalidEip,
InvalidInviteCode,
User Balance + Referral Logic (#44) * will implement balance topup endpoint * will quickly fix other PR reviews * merging from master * will finish up godmoe * will finish up login * added logic to top up balance (first iteration) * should implement additional columns soon (currency, amount, tx-hash), as well as a new table for spend * updated migrations, will account for spend next * get back to this later * will merge PR from stats-v2 * stats v2 rebased all my commits and squashed them down to one * cargo upgrade * added migrtation for spend in accounting table. will run test-deposit next * trying to get request from polygon * first iteration /user/balance/:tx_hash works, needs to add accepted tokens next * creating the referral code seems to work * will now check if spending enough credits will lead to both parties receiving credits * rpcstats takes care of accounting for spend data * removed track spend from table * Revert "removed track spend from table" This reverts commit a50802d6ae75f786864c5ec42d0ceb2cb27124ed. * Revert "rpcstats takes care of accounting for spend data" This reverts commit 1cec728bf241e4cfd24351134637ed81c1a5a10b. * removed rpc request table entity * updated referral code to use ulid s * credits used are aggregated * added a bunch of fields to referrer * added database logic whenever an aggregate stats is added. will have to iterate over this a couple times i think. go to (1) detecting accepted stables next, (2) fix influxdb bug and (3) start to write test * removed track spend as this will occur in the database * will first work on "balance", then referral. these should really be treated as two separate PRs (although already convoluted) * balance logic initial commit * breaking WIP, changing the RPC call logic functions * will start testing next * got rid of warnings & lint * will proceed with subtracting / adding to balance * added decimal points, balance tracking seems to work * will beautify code a bit * removed deprecated dependency, and added topic + deposit contract to app.yaml * brownie test suite does not rely on local contract files it pulls all from polygonscan * will continue with referral * should perhaps (in a future revision) recordhow much the referees got for free. marking referrals seems to work rn * user is upgraded to premium if they deposit more than 10$. we dont accept more than $10M in a single tx * will start PR, referral seems to be fine so far, perhaps up to some numbers that still may need tweaking * will start PR * removed rogue comments, cleaned up payments a bit * changes before PR * apply stats * added unique constraint * some refactoring such that the user file is not too bloated * compiling * progress with subusers, creating a table entry seems to work * good response type is there as well now, will work on getters from primary user and secondary user next * subuser logic also seems fine now * downgrade logic * fixed bug influxdb does not support different types in same query (which makes sense) * WIP temporary commit * merging with PR * Delete daemon.rs there are multiple daemons now, so this was moved to `proxyd` * will remove request clone to &mut * multiple request handles for payment * making requests still seem fine * removed redundant commented out bits * added deposit endpoint, added deposit amount and deposit user, untested yet * small bug with downgrade tier id * will add authorization so balance can be received for users * balance history should be set now too * will check balance over time again * subususer can see rpc key balance if admin or owner * stats also seems to work fine now with historical balance * things seem to be building and working * removed clone from OpenRequestHandle * removed influxdb from workspace members * changed config files * reran sea-orm generate entities, added a foreign key, should be proper now * removed contract from commit * made deposit contract optional * added topic in polygon dev * changed deposit contract to deposit factory contract * added selfrelation on user_tier * added payment required * changed chain id to u64 * add wss in polygon llamarpc * removed origin and method from the table * added onchain transactions naming (and forgot to add a migration before) * changed foreign key to be the referrer (id), not the code itself * forgot to add id as the target foreign key * WIP adding cache to update role * fixed merge conflicts --------- Co-authored-by: Bryan Stitt <bryan@llamanodes.com> Co-authored-by: Bryan Stitt <bryan@stitthappens.com>
2023-05-12 19:45:15 +03:00
InvalidReferralCode,
InvalidReferer,
InvalidSignatureLength,
InvalidUserAgent,
InvalidUserKey,
2022-10-27 00:39:26 +03:00
IpAddrParse(AddrParseError),
#[error(ignore)]
#[from(ignore)]
IpNotAllowed(IpAddr),
2022-11-03 02:14:16 +03:00
JoinError(JoinError),
#[display(fmt = "{:?}", _0)]
#[error(ignore)]
JsonRpcForwardedError(JsonRpcForwardedResponse),
#[display(fmt = "{:?}", _0)]
#[error(ignore)]
2023-03-03 04:39:50 +03:00
MsgPackEncode(rmp_serde::encode::Error),
2023-03-20 23:45:21 +03:00
NoBlockNumberOrHash,
NoBlocksKnown,
NoConsensusHeadBlock,
NoHandleReady,
2023-03-20 23:45:21 +03:00
NoServersSynced,
#[display(fmt = "{}/{}", num_known, min_head_rpcs)]
#[from(ignore)]
NotEnoughRpcs {
num_known: usize,
min_head_rpcs: usize,
},
2022-10-27 00:39:26 +03:00
NotFound,
NotImplemented,
OriginRequired,
#[error(ignore)]
#[from(ignore)]
OriginNotAllowed(headers::Origin),
#[display(fmt = "{:?}", _0)]
#[error(ignore)]
ParseBytesError(Option<ethers::types::ParseBytesError>),
ParseMsgError(siwe::ParseError),
ParseAddressError,
#[display(fmt = "{:?}, {:?}", _0, _1)]
RateLimited(Authorization, Option<Instant>),
2022-10-27 00:39:26 +03:00
Redis(RedisError),
RefererRequired,
#[display(fmt = "{:?}", _0)]
#[error(ignore)]
#[from(ignore)]
RefererNotAllowed(headers::Referer),
SemaphoreAcquireError(AcquireError),
SendAppStatError(flume::SendError<crate::stats::AppStat>),
SerdeJson(serde_json::Error),
2022-10-20 23:26:14 +03:00
/// 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<anyhow::Error>),
/// TODO: what should be attached to the timout?
#[display(fmt = "{:?}", _0)]
#[error(ignore)]
Timeout(Option<tokio::time::error::Elapsed>),
2023-03-03 04:39:50 +03:00
UlidDecode(ulid::DecodeError),
UnknownBlockNumber,
2022-09-12 17:33:55 +03:00
UnknownKey,
UserAgentRequired,
#[error(ignore)]
UserAgentNotAllowed(headers::UserAgent),
UserIdZero,
User Balance + Referral Logic (#44) * will implement balance topup endpoint * will quickly fix other PR reviews * merging from master * will finish up godmoe * will finish up login * added logic to top up balance (first iteration) * should implement additional columns soon (currency, amount, tx-hash), as well as a new table for spend * updated migrations, will account for spend next * get back to this later * will merge PR from stats-v2 * stats v2 rebased all my commits and squashed them down to one * cargo upgrade * added migrtation for spend in accounting table. will run test-deposit next * trying to get request from polygon * first iteration /user/balance/:tx_hash works, needs to add accepted tokens next * creating the referral code seems to work * will now check if spending enough credits will lead to both parties receiving credits * rpcstats takes care of accounting for spend data * removed track spend from table * Revert "removed track spend from table" This reverts commit a50802d6ae75f786864c5ec42d0ceb2cb27124ed. * Revert "rpcstats takes care of accounting for spend data" This reverts commit 1cec728bf241e4cfd24351134637ed81c1a5a10b. * removed rpc request table entity * updated referral code to use ulid s * credits used are aggregated * added a bunch of fields to referrer * added database logic whenever an aggregate stats is added. will have to iterate over this a couple times i think. go to (1) detecting accepted stables next, (2) fix influxdb bug and (3) start to write test * removed track spend as this will occur in the database * will first work on "balance", then referral. these should really be treated as two separate PRs (although already convoluted) * balance logic initial commit * breaking WIP, changing the RPC call logic functions * will start testing next * got rid of warnings & lint * will proceed with subtracting / adding to balance * added decimal points, balance tracking seems to work * will beautify code a bit * removed deprecated dependency, and added topic + deposit contract to app.yaml * brownie test suite does not rely on local contract files it pulls all from polygonscan * will continue with referral * should perhaps (in a future revision) recordhow much the referees got for free. marking referrals seems to work rn * user is upgraded to premium if they deposit more than 10$. we dont accept more than $10M in a single tx * will start PR, referral seems to be fine so far, perhaps up to some numbers that still may need tweaking * will start PR * removed rogue comments, cleaned up payments a bit * changes before PR * apply stats * added unique constraint * some refactoring such that the user file is not too bloated * compiling * progress with subusers, creating a table entry seems to work * good response type is there as well now, will work on getters from primary user and secondary user next * subuser logic also seems fine now * downgrade logic * fixed bug influxdb does not support different types in same query (which makes sense) * WIP temporary commit * merging with PR * Delete daemon.rs there are multiple daemons now, so this was moved to `proxyd` * will remove request clone to &mut * multiple request handles for payment * making requests still seem fine * removed redundant commented out bits * added deposit endpoint, added deposit amount and deposit user, untested yet * small bug with downgrade tier id * will add authorization so balance can be received for users * balance history should be set now too * will check balance over time again * subususer can see rpc key balance if admin or owner * stats also seems to work fine now with historical balance * things seem to be building and working * removed clone from OpenRequestHandle * removed influxdb from workspace members * changed config files * reran sea-orm generate entities, added a foreign key, should be proper now * removed contract from commit * made deposit contract optional * added topic in polygon dev * changed deposit contract to deposit factory contract * added selfrelation on user_tier * added payment required * changed chain id to u64 * add wss in polygon llamarpc * removed origin and method from the table * added onchain transactions naming (and forgot to add a migration before) * changed foreign key to be the referrer (id), not the code itself * forgot to add id as the target foreign key * WIP adding cache to update role * fixed merge conflicts --------- Co-authored-by: Bryan Stitt <bryan@llamanodes.com> Co-authored-by: Bryan Stitt <bryan@stitthappens.com>
2023-05-12 19:45:15 +03:00
PaymentRequired,
VerificationError(siwe::VerificationError),
WatchRecvError(tokio::sync::watch::error::RecvError),
2023-03-20 23:45:21 +03:00
WatchSendError,
2023-03-20 05:14:46 +03:00
WebsocketOnly,
#[display(fmt = "{:?}, {}", _0, _1)]
#[error(ignore)]
WithContext(Option<Box<Web3ProxyError>>, String),
2022-08-16 22:29:00 +03:00
}
impl Web3ProxyError {
2023-01-19 03:17:43 +03:00
pub fn into_response_parts(self) -> (StatusCode, JsonRpcForwardedResponse) {
match self {
2022-12-06 00:13:36 +03:00
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,
JsonRpcForwardedResponse::from_string(
// TODO: is it safe to expose all of our anyhow strings?
"FORBIDDEN".to_string(),
Some(StatusCode::FORBIDDEN.as_u16().into()),
None,
),
)
}
Self::Anyhow(err) => {
2022-11-12 11:24:32 +03:00
warn!("anyhow. err={:?}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
2022-09-10 03:58:33 +03:00
JsonRpcForwardedResponse::from_string(
// TODO: is it safe to expose all of our anyhow strings?
2022-09-10 03:58:33 +03:00
err.to_string(),
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
2023-02-03 21:56:05 +03:00
Self::BadRequest(err) => {
debug!("BAD_REQUEST: {}", err);
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
&format!("bad request: {}", err),
Some(StatusCode::BAD_REQUEST.as_u16().into()),
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,
JsonRpcForwardedResponse::from_str(
&format!("bad response: {}", err),
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
Self::BadRouting => {
error!("BadRouting");
(
StatusCode::INTERNAL_SERVER_ERROR,
JsonRpcForwardedResponse::from_str(
"bad routing",
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
2022-10-27 00:39:26 +03:00
Self::Database(err) => {
error!("database err={:?}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
JsonRpcForwardedResponse::from_str(
2022-10-27 00:39:26 +03:00
"database error!",
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
Self::EipVerificationFailed(err_1, err_191) => {
info!(
"EipVerificationFailed err_1={:#?} err2={:#?}",
err_1, err_191
);
(
StatusCode::UNAUTHORIZED,
JsonRpcForwardedResponse::from_string(
format!(
"both the primary and eip191 verification failed: {:#?}; {:#?}",
err_1, err_191
),
Some(StatusCode::UNAUTHORIZED.as_u16().into()),
None,
),
)
}
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,
),
)
}
2023-03-20 23:45:21 +03:00
Self::FlumeRecvError(err) => {
warn!("FlumeRecvError err={:#?}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
JsonRpcForwardedResponse::from_str(
"flume recv error!",
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
Self::JsonRpcForwardedError(x) => (StatusCode::OK, x),
Self::GasEstimateNotU256 => {
warn!("GasEstimateNotU256");
(
StatusCode::INTERNAL_SERVER_ERROR,
JsonRpcForwardedResponse::from_str(
"gas estimate result is not an U256",
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
2023-03-03 04:39:50 +03:00
Self::Headers(err) => {
2022-11-12 11:24:32 +03:00
warn!("HeadersError {:?}", err);
2022-10-27 00:39:26 +03:00
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
&format!("{}", err),
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
2022-08-21 12:39:38 +03:00
}
Self::InfluxDb2RequestError(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,
JsonRpcForwardedResponse::from_str(
"influxdb2 error!",
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
Self::InvalidBlockBounds { min, max } => {
debug!("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,
),
)
}
2022-10-27 00:39:26 +03:00
Self::IpAddrParse(err) => {
2022-11-12 11:24:32 +03:00
warn!("IpAddrParse err={:?}", err);
2022-10-20 23:26:14 +03:00
(
2022-10-27 00:39:26 +03:00
StatusCode::BAD_REQUEST,
2022-10-20 23:26:14 +03:00
JsonRpcForwardedResponse::from_str(
2022-10-27 00:39:26 +03:00
&format!("{}", err),
Some(StatusCode::BAD_REQUEST.as_u16().into()),
2022-10-20 23:26:14 +03:00
None,
),
)
}
Self::IpNotAllowed(ip) => {
warn!("IpNotAllowed ip={})", ip);
(
StatusCode::FORBIDDEN,
JsonRpcForwardedResponse::from_string(
format!("IP ({}) is not allowed!", ip),
Some(StatusCode::FORBIDDEN.as_u16().into()),
None,
),
)
}
2022-10-27 00:39:26 +03:00
Self::InvalidHeaderValue(err) => {
2022-11-12 11:24:32 +03:00
warn!("InvalidHeaderValue err={:?}", err);
(
2022-10-27 00:39:26 +03:00
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
2022-10-27 00:39:26 +03:00
&format!("{}", err),
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
Self::InvalidEip => {
warn!("InvalidEip");
(
StatusCode::UNAUTHORIZED,
JsonRpcForwardedResponse::from_str(
"invalid message eip given",
Some(StatusCode::UNAUTHORIZED.as_u16().into()),
None,
),
)
}
Self::InvalidInviteCode => {
warn!("InvalidInviteCode");
(
StatusCode::UNAUTHORIZED,
JsonRpcForwardedResponse::from_str(
"invalid invite code",
Some(StatusCode::UNAUTHORIZED.as_u16().into()),
None,
),
)
}
User Balance + Referral Logic (#44) * will implement balance topup endpoint * will quickly fix other PR reviews * merging from master * will finish up godmoe * will finish up login * added logic to top up balance (first iteration) * should implement additional columns soon (currency, amount, tx-hash), as well as a new table for spend * updated migrations, will account for spend next * get back to this later * will merge PR from stats-v2 * stats v2 rebased all my commits and squashed them down to one * cargo upgrade * added migrtation for spend in accounting table. will run test-deposit next * trying to get request from polygon * first iteration /user/balance/:tx_hash works, needs to add accepted tokens next * creating the referral code seems to work * will now check if spending enough credits will lead to both parties receiving credits * rpcstats takes care of accounting for spend data * removed track spend from table * Revert "removed track spend from table" This reverts commit a50802d6ae75f786864c5ec42d0ceb2cb27124ed. * Revert "rpcstats takes care of accounting for spend data" This reverts commit 1cec728bf241e4cfd24351134637ed81c1a5a10b. * removed rpc request table entity * updated referral code to use ulid s * credits used are aggregated * added a bunch of fields to referrer * added database logic whenever an aggregate stats is added. will have to iterate over this a couple times i think. go to (1) detecting accepted stables next, (2) fix influxdb bug and (3) start to write test * removed track spend as this will occur in the database * will first work on "balance", then referral. these should really be treated as two separate PRs (although already convoluted) * balance logic initial commit * breaking WIP, changing the RPC call logic functions * will start testing next * got rid of warnings & lint * will proceed with subtracting / adding to balance * added decimal points, balance tracking seems to work * will beautify code a bit * removed deprecated dependency, and added topic + deposit contract to app.yaml * brownie test suite does not rely on local contract files it pulls all from polygonscan * will continue with referral * should perhaps (in a future revision) recordhow much the referees got for free. marking referrals seems to work rn * user is upgraded to premium if they deposit more than 10$. we dont accept more than $10M in a single tx * will start PR, referral seems to be fine so far, perhaps up to some numbers that still may need tweaking * will start PR * removed rogue comments, cleaned up payments a bit * changes before PR * apply stats * added unique constraint * some refactoring such that the user file is not too bloated * compiling * progress with subusers, creating a table entry seems to work * good response type is there as well now, will work on getters from primary user and secondary user next * subuser logic also seems fine now * downgrade logic * fixed bug influxdb does not support different types in same query (which makes sense) * WIP temporary commit * merging with PR * Delete daemon.rs there are multiple daemons now, so this was moved to `proxyd` * will remove request clone to &mut * multiple request handles for payment * making requests still seem fine * removed redundant commented out bits * added deposit endpoint, added deposit amount and deposit user, untested yet * small bug with downgrade tier id * will add authorization so balance can be received for users * balance history should be set now too * will check balance over time again * subususer can see rpc key balance if admin or owner * stats also seems to work fine now with historical balance * things seem to be building and working * removed clone from OpenRequestHandle * removed influxdb from workspace members * changed config files * reran sea-orm generate entities, added a foreign key, should be proper now * removed contract from commit * made deposit contract optional * added topic in polygon dev * changed deposit contract to deposit factory contract * added selfrelation on user_tier * added payment required * changed chain id to u64 * add wss in polygon llamarpc * removed origin and method from the table * added onchain transactions naming (and forgot to add a migration before) * changed foreign key to be the referrer (id), not the code itself * forgot to add id as the target foreign key * WIP adding cache to update role * fixed merge conflicts --------- Co-authored-by: Bryan Stitt <bryan@llamanodes.com> Co-authored-by: Bryan Stitt <bryan@stitthappens.com>
2023-05-12 19:45:15 +03:00
Self::InvalidReferralCode => {
warn!("InvalidReferralCode");
(
StatusCode::UNAUTHORIZED,
JsonRpcForwardedResponse::from_str(
"invalid referral code",
Some(StatusCode::UNAUTHORIZED.as_u16().into()),
None,
),
)
}
Self::InvalidReferer => {
warn!("InvalidReferer");
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
"invalid referer!",
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
Self::InvalidSignatureLength => {
warn!("InvalidSignatureLength");
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
"invalid signature length",
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
Self::InvalidUserAgent => {
warn!("InvalidUserAgent");
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
"invalid user agent!",
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
Self::InvalidUserKey => {
warn!("InvalidUserKey");
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
"UserKey was not a ULID or UUID",
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
2022-11-03 02:14:16 +03:00
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
};
2022-11-03 02:14:16 +03:00
(
code,
2022-11-03 02:14:16 +03:00
JsonRpcForwardedResponse::from_str(
// TODO: different messages, too?
2022-11-03 02:14:16 +03:00
"Unable to complete request",
Some(code.as_u16().into()),
2022-11-03 02:14:16 +03:00
None,
),
)
}
2023-03-03 04:39:50 +03:00
Self::MsgPackEncode(err) => {
warn!("MsgPackEncode Error: {}", err);
2023-03-03 04:39:50 +03:00
(
StatusCode::INTERNAL_SERVER_ERROR,
2023-03-03 04:39:50 +03:00
JsonRpcForwardedResponse::from_str(
&format!("msgpack encode error: {}", err),
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
2023-03-03 04:39:50 +03:00
None,
),
)
}
2023-03-20 23:45:21 +03:00
Self::NoBlockNumberOrHash => {
warn!("NoBlockNumberOrHash");
(
StatusCode::INTERNAL_SERVER_ERROR,
2023-03-20 23:45:21 +03:00
JsonRpcForwardedResponse::from_str(
"Blocks here must have a number or hash",
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
2023-03-20 23:45:21 +03:00
None,
),
)
}
Self::NoBlocksKnown => {
error!("NoBlocksKnown");
(
StatusCode::BAD_GATEWAY,
JsonRpcForwardedResponse::from_str(
2023-03-20 23:45:21 +03:00
"no blocks known",
Some(StatusCode::BAD_GATEWAY.as_u16().into()),
None,
),
)
}
Self::NoConsensusHeadBlock => {
error!("NoConsensusHeadBlock");
(
StatusCode::BAD_GATEWAY,
JsonRpcForwardedResponse::from_str(
"no consensus head block",
Some(StatusCode::BAD_GATEWAY.as_u16().into()),
None,
),
)
}
Self::NoHandleReady => {
error!("NoHandleReady");
(
StatusCode::BAD_GATEWAY,
JsonRpcForwardedResponse::from_str(
"unable to retry for request handle",
Some(StatusCode::BAD_GATEWAY.as_u16().into()),
None,
),
)
}
2023-03-20 23:45:21 +03:00
Self::NoServersSynced => {
warn!("NoServersSynced");
(
StatusCode::BAD_GATEWAY,
2023-03-20 23:45:21 +03:00
JsonRpcForwardedResponse::from_str(
"no servers synced",
Some(StatusCode::BAD_GATEWAY.as_u16().into()),
2023-03-20 23:45:21 +03:00
None,
),
)
}
Self::NotEnoughRpcs {
num_known,
min_head_rpcs,
} => {
error!("NotEnoughRpcs {}/{}", num_known, min_head_rpcs);
(
StatusCode::BAD_GATEWAY,
2023-03-20 23:45:21 +03:00
JsonRpcForwardedResponse::from_string(
format!("not enough rpcs connected {}/{}", num_known, min_head_rpcs),
Some(StatusCode::BAD_GATEWAY.as_u16().into()),
2023-03-20 23:45:21 +03:00
None,
),
)
}
2022-10-27 00:39:26 +03:00
Self::NotFound => {
// TODO: emit a stat?
// TODO: instead of an error, show a normal html page for 404?
2022-10-27 00:39:26 +03:00
(
StatusCode::NOT_FOUND,
JsonRpcForwardedResponse::from_str(
"not found!",
Some(StatusCode::NOT_FOUND.as_u16().into()),
None,
),
)
}
Self::NotImplemented => {
trace!("NotImplemented");
(
StatusCode::NOT_IMPLEMENTED,
JsonRpcForwardedResponse::from_str(
"work in progress",
Some(StatusCode::NOT_IMPLEMENTED.as_u16().into()),
None,
),
)
}
Self::OriginRequired => {
trace!("OriginRequired");
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
"Origin required",
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
Self::OriginNotAllowed(origin) => {
trace!("OriginNotAllowed origin={}", origin);
(
StatusCode::FORBIDDEN,
JsonRpcForwardedResponse::from_string(
format!("Origin ({}) is not allowed!", origin),
Some(StatusCode::FORBIDDEN.as_u16().into()),
None,
),
)
}
Self::ParseBytesError(err) => {
trace!("ParseBytesError err={:?}", err);
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
"parse bytes error!",
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
Self::ParseMsgError(err) => {
trace!("ParseMsgError err={:?}", err);
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
"parse message error!",
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
Self::ParseAddressError => {
trace!("ParseAddressError");
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
"unable to parse address",
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
User Balance + Referral Logic (#44) * will implement balance topup endpoint * will quickly fix other PR reviews * merging from master * will finish up godmoe * will finish up login * added logic to top up balance (first iteration) * should implement additional columns soon (currency, amount, tx-hash), as well as a new table for spend * updated migrations, will account for spend next * get back to this later * will merge PR from stats-v2 * stats v2 rebased all my commits and squashed them down to one * cargo upgrade * added migrtation for spend in accounting table. will run test-deposit next * trying to get request from polygon * first iteration /user/balance/:tx_hash works, needs to add accepted tokens next * creating the referral code seems to work * will now check if spending enough credits will lead to both parties receiving credits * rpcstats takes care of accounting for spend data * removed track spend from table * Revert "removed track spend from table" This reverts commit a50802d6ae75f786864c5ec42d0ceb2cb27124ed. * Revert "rpcstats takes care of accounting for spend data" This reverts commit 1cec728bf241e4cfd24351134637ed81c1a5a10b. * removed rpc request table entity * updated referral code to use ulid s * credits used are aggregated * added a bunch of fields to referrer * added database logic whenever an aggregate stats is added. will have to iterate over this a couple times i think. go to (1) detecting accepted stables next, (2) fix influxdb bug and (3) start to write test * removed track spend as this will occur in the database * will first work on "balance", then referral. these should really be treated as two separate PRs (although already convoluted) * balance logic initial commit * breaking WIP, changing the RPC call logic functions * will start testing next * got rid of warnings & lint * will proceed with subtracting / adding to balance * added decimal points, balance tracking seems to work * will beautify code a bit * removed deprecated dependency, and added topic + deposit contract to app.yaml * brownie test suite does not rely on local contract files it pulls all from polygonscan * will continue with referral * should perhaps (in a future revision) recordhow much the referees got for free. marking referrals seems to work rn * user is upgraded to premium if they deposit more than 10$. we dont accept more than $10M in a single tx * will start PR, referral seems to be fine so far, perhaps up to some numbers that still may need tweaking * will start PR * removed rogue comments, cleaned up payments a bit * changes before PR * apply stats * added unique constraint * some refactoring such that the user file is not too bloated * compiling * progress with subusers, creating a table entry seems to work * good response type is there as well now, will work on getters from primary user and secondary user next * subuser logic also seems fine now * downgrade logic * fixed bug influxdb does not support different types in same query (which makes sense) * WIP temporary commit * merging with PR * Delete daemon.rs there are multiple daemons now, so this was moved to `proxyd` * will remove request clone to &mut * multiple request handles for payment * making requests still seem fine * removed redundant commented out bits * added deposit endpoint, added deposit amount and deposit user, untested yet * small bug with downgrade tier id * will add authorization so balance can be received for users * balance history should be set now too * will check balance over time again * subususer can see rpc key balance if admin or owner * stats also seems to work fine now with historical balance * things seem to be building and working * removed clone from OpenRequestHandle * removed influxdb from workspace members * changed config files * reran sea-orm generate entities, added a foreign key, should be proper now * removed contract from commit * made deposit contract optional * added topic in polygon dev * changed deposit contract to deposit factory contract * added selfrelation on user_tier * added payment required * changed chain id to u64 * add wss in polygon llamarpc * removed origin and method from the table * added onchain transactions naming (and forgot to add a migration before) * changed foreign key to be the referrer (id), not the code itself * forgot to add id as the target foreign key * WIP adding cache to update role * fixed merge conflicts --------- Co-authored-by: Bryan Stitt <bryan@llamanodes.com> Co-authored-by: Bryan Stitt <bryan@stitthappens.com>
2023-05-12 19:45:15 +03:00
Self::PaymentRequired => {
trace!("PaymentRequiredError");
(
StatusCode::PAYMENT_REQUIRED,
JsonRpcForwardedResponse::from_str(
"Payment is required and user is not premium.",
Some(StatusCode::PAYMENT_REQUIRED.as_u16().into()),
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
2023-01-19 03:17:43 +03:00
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 #{}.{}",
2023-01-19 03:17:43 +03:00
authorization.checks.rpc_secret_key_id.unwrap(),
retry_msg
)
};
(
StatusCode::TOO_MANY_REQUESTS,
JsonRpcForwardedResponse::from_string(
msg,
Some(StatusCode::TOO_MANY_REQUESTS.as_u16().into()),
None,
),
)
}
2022-10-27 00:39:26 +03:00
Self::Redis(err) => {
2022-11-12 11:24:32 +03:00
warn!("redis err={:?}", err);
2022-10-27 00:39:26 +03:00
(
StatusCode::INTERNAL_SERVER_ERROR,
JsonRpcForwardedResponse::from_str(
"redis error!",
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
Self::RefererRequired => {
warn!("referer required");
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
"Referer required",
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
Self::RefererNotAllowed(referer) => {
warn!("referer not allowed referer={:?}", referer);
(
StatusCode::FORBIDDEN,
JsonRpcForwardedResponse::from_string(
format!("Referer ({:?}) is not allowed", referer),
Some(StatusCode::FORBIDDEN.as_u16().into()),
None,
),
)
}
Self::Arc(err) => match Arc::try_unwrap(err) {
Ok(err) => err,
Err(err) => Self::Anyhow(anyhow::anyhow!("{}", err)),
}
.into_response_parts(),
2022-12-14 05:13:23 +03:00
Self::SemaphoreAcquireError(err) => {
warn!("semaphore acquire err={:?}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
JsonRpcForwardedResponse::from_string(
// TODO: is it safe to expose all of our anyhow strings?
"semaphore acquire error".to_string(),
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
Self::SendAppStatError(err) => {
error!("SendAppStatError err={:?}", err);
(
StatusCode::INTERNAL_SERVER_ERROR,
JsonRpcForwardedResponse::from_str(
"error stat_sender sending response_stat",
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
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,
),
)
}
2022-10-27 00:39:26 +03:00
Self::StatusCode(status_code, err_msg, err) => {
2022-11-16 10:19:56 +03:00
// 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) {
2022-12-20 08:39:17 +03:00
warn!("server error {} {:?}: {:?}", code, err_msg, err);
2022-11-16 10:19:56 +03:00
} else {
trace!("user error {} {:?}: {:?}", code, err_msg, err);
}
2022-10-27 00:39:26 +03:00
(
status_code,
2022-11-16 10:19:56 +03:00
JsonRpcForwardedResponse::from_str(&err_msg, Some(code.into()), None),
2022-10-27 00:39:26 +03:00
)
}
Self::Timeout(x) => (
StatusCode::REQUEST_TIMEOUT,
JsonRpcForwardedResponse::from_str(
&format!("request timed out: {:?}", x),
Some(StatusCode::REQUEST_TIMEOUT.as_u16().into()),
// TODO: include the actual id!
None,
),
),
2022-10-27 00:39:26 +03:00
Self::HeaderToString(err) => {
// trace!(?err, "HeaderToString");
2022-10-27 00:39:26 +03:00
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
&format!("{}", err),
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
2023-03-03 04:39:50 +03:00
Self::UlidDecode(err) => {
// trace!(?err, "UlidDecodeError");
2022-10-31 23:05:58 +03:00
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
&format!("{}", err),
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
Self::UnknownBlockNumber => {
error!("UnknownBlockNumber");
(
StatusCode::BAD_GATEWAY,
JsonRpcForwardedResponse::from_str(
"no servers synced. unknown eth_blockNumber",
Some(StatusCode::BAD_GATEWAY.as_u16().into()),
None,
),
)
}
2022-10-31 23:05:58 +03:00
// TODO: stat?
2022-09-12 17:33:55 +03:00
Self::UnknownKey => (
StatusCode::UNAUTHORIZED,
JsonRpcForwardedResponse::from_str(
"unknown api key!",
Some(StatusCode::UNAUTHORIZED.as_u16().into()),
None,
),
),
Self::UserAgentRequired => {
warn!("UserAgentRequired");
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
"User agent required",
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
Self::UserAgentNotAllowed(ua) => {
warn!("UserAgentNotAllowed ua={}", ua);
(
StatusCode::FORBIDDEN,
JsonRpcForwardedResponse::from_string(
format!("User agent ({}) is not allowed!", ua),
Some(StatusCode::FORBIDDEN.as_u16().into()),
None,
),
)
}
Self::UserIdZero => {
warn!("UserIdZero");
// TODO: this might actually be an application error and not a BAD_REQUEST
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
"user ids should always be non-zero",
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
Self::VerificationError(err) => {
trace!("VerificationError err={:?}", err);
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
"verification error!",
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
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,
),
)
}
2023-03-20 23:45:21 +03:00
Self::WatchSendError => {
error!("WatchSendError");
(
StatusCode::INTERNAL_SERVER_ERROR,
JsonRpcForwardedResponse::from_str(
"watch send error!",
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
2023-03-20 05:14:46 +03:00
Self::WebsocketOnly => {
trace!("WebsocketOnly");
2023-03-20 05:14:46 +03:00
(
StatusCode::BAD_REQUEST,
JsonRpcForwardedResponse::from_str(
"redirect_public_url not set. only websockets work here",
Some(StatusCode::BAD_REQUEST.as_u16().into()),
None,
),
)
}
Self::WithContext(err, msg) => match err {
Some(err) => {
warn!("{:#?} w/ context {}", err, msg);
err.into_response_parts()
}
None => {
warn!("error w/ context {}", msg);
(
StatusCode::INTERNAL_SERVER_ERROR,
JsonRpcForwardedResponse::from_string(
msg,
Some(StatusCode::INTERNAL_SERVER_ERROR.as_u16().into()),
None,
),
)
}
},
2023-01-19 03:17:43 +03:00
}
}
}
impl From<ethers::types::ParseBytesError> for Web3ProxyError {
fn from(err: ethers::types::ParseBytesError) -> Self {
Self::ParseBytesError(Some(err))
}
}
impl From<tokio::time::error::Elapsed> for Web3ProxyError {
fn from(err: tokio::time::error::Elapsed) -> Self {
Self::Timeout(Some(err))
}
}
impl IntoResponse for Web3ProxyError {
2023-01-19 03:17:43 +03:00
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: status code is in the jsonrpc response and is also the first item in the tuple. DRY
let (status_code, response) = self.into_response_parts();
(status_code, Json(response)).into_response()
2022-08-16 22:29:00 +03:00
}
}
2022-06-16 05:53:37 +03:00
2022-08-10 05:37:34 +03:00
pub async fn handler_404() -> Response {
Web3ProxyError::NotFound.into_response()
2022-06-05 22:58:47 +03:00
}
pub trait Web3ProxyErrorContext<T> {
fn web3_context<S: Into<String>>(self, msg: S) -> Result<T, Web3ProxyError>;
}
impl<T> Web3ProxyErrorContext<T> for Option<T> {
fn web3_context<S: Into<String>>(self, msg: S) -> Result<T, Web3ProxyError> {
self.ok_or(Web3ProxyError::WithContext(None, msg.into()))
}
}
impl<T, E> Web3ProxyErrorContext<T> for Result<T, E>
where
E: Into<Web3ProxyError>,
{
fn web3_context<S: Into<String>>(self, msg: S) -> Result<T, Web3ProxyError> {
self.map_err(|err| Web3ProxyError::WithContext(Some(Box::new(err.into())), msg.into()))
}
}