let the frontend handle their own cookies
This commit is contained in:
parent
8035ee5a0c
commit
8459dcd1f1
101
Cargo.lock
generated
101
Cargo.lock
generated
@ -27,15 +27,6 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aead"
|
|
||||||
version = "0.4.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array 0.14.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aes"
|
name = "aes"
|
||||||
version = "0.7.5"
|
version = "0.7.5"
|
||||||
@ -48,20 +39,6 @@ dependencies = [
|
|||||||
"opaque-debug 0.3.0",
|
"opaque-debug 0.3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aes-gcm"
|
|
||||||
version = "0.9.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6"
|
|
||||||
dependencies = [
|
|
||||||
"aead",
|
|
||||||
"aes",
|
|
||||||
"cipher",
|
|
||||||
"ctr",
|
|
||||||
"ghash",
|
|
||||||
"subtle",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
@ -1060,24 +1037,6 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8"
|
checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cookie"
|
|
||||||
version = "0.16.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05"
|
|
||||||
dependencies = [
|
|
||||||
"aes-gcm",
|
|
||||||
"base64 0.13.0",
|
|
||||||
"hkdf",
|
|
||||||
"hmac",
|
|
||||||
"percent-encoding",
|
|
||||||
"rand 0.8.5",
|
|
||||||
"sha2 0.10.2",
|
|
||||||
"subtle",
|
|
||||||
"time 0.3.14",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
@ -2180,16 +2139,6 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ghash"
|
|
||||||
version = "0.4.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
|
|
||||||
dependencies = [
|
|
||||||
"opaque-debug 0.3.0",
|
|
||||||
"polyval",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.26.1"
|
version = "0.26.1"
|
||||||
@ -2358,15 +2307,6 @@ version = "0.4.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hkdf"
|
|
||||||
version = "0.12.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
|
|
||||||
dependencies = [
|
|
||||||
"hmac",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hmac"
|
name = "hmac"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
@ -3471,18 +3411,6 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "polyval"
|
|
||||||
version = "0.5.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cpufeatures",
|
|
||||||
"opaque-debug 0.3.0",
|
|
||||||
"universal-hash",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "postgres-protocol"
|
name = "postgres-protocol"
|
||||||
version = "0.6.4"
|
version = "0.6.4"
|
||||||
@ -5143,23 +5071,6 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tower-cookies"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "19833e336396f3953e5ab1513d72b5e5ea51d5ad39b78d306766a05740b48b97"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"axum-core",
|
|
||||||
"cookie",
|
|
||||||
"futures-util",
|
|
||||||
"http",
|
|
||||||
"parking_lot 0.12.1",
|
|
||||||
"pin-project-lite",
|
|
||||||
"tower-layer",
|
|
||||||
"tower-service",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-http"
|
name = "tower-http"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
@ -5410,16 +5321,6 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "universal-hash"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array 0.14.5",
|
|
||||||
"subtle",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@ -5625,6 +5526,7 @@ dependencies = [
|
|||||||
"axum",
|
"axum",
|
||||||
"axum-client-ip",
|
"axum-client-ip",
|
||||||
"axum-macros",
|
"axum-macros",
|
||||||
|
"chrono",
|
||||||
"counter",
|
"counter",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"deferred-rate-limiter",
|
"deferred-rate-limiter",
|
||||||
@ -5662,7 +5564,6 @@ dependencies = [
|
|||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"toml",
|
"toml",
|
||||||
"tower",
|
"tower",
|
||||||
"tower-cookies",
|
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tower-request-id",
|
"tower-request-id",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
20
TODO.md
20
TODO.md
@ -157,19 +157,19 @@ These are roughly in order of completition
|
|||||||
- [x] change user creation script to have a "unlimited requests per minute" flag that sets it to u64::MAX (18446744073709551615)
|
- [x] change user creation script to have a "unlimited requests per minute" flag that sets it to u64::MAX (18446744073709551615)
|
||||||
- [x] in /status, block hashes has a lower count than block numbers. how is that possible?
|
- [x] in /status, block hashes has a lower count than block numbers. how is that possible?
|
||||||
- we weren't calling sync. now we are
|
- we weren't calling sync. now we are
|
||||||
- [-] opt-in debug mode that inspects responses for reverts and saves the request to the database for the user.
|
- [x] opt-in debug mode that inspects responses for reverts and saves the request to the database for the user.
|
||||||
- [-] let them choose a % to log (or maybe x/second). someone like curve logging all reverts will be a BIG database very quickly
|
|
||||||
- this must be opt-in or spawned since it will slow things down and will make their calls less private
|
|
||||||
- [x] Api keys need option to lock to IP, cors header, referer, user agent, etc
|
- [x] Api keys need option to lock to IP, cors header, referer, user agent, etc
|
||||||
- [ ] endpoint for creating/modifying api keys and their advanced security features
|
- [x] /user/logout to clear bearer token and jwt
|
||||||
|
- [x] bearer tokens should expire
|
||||||
|
- [-] user login should return the bearer token, the user keys, and a jwt (jsonwebtoken rust crate should make it easy)
|
||||||
|
- [-] let users choose a % to log (or maybe x/second). someone like curve logging all reverts will be a BIG database very quickly
|
||||||
|
- this must be opt-in or spawned since it will slow things down and will make their calls less private
|
||||||
|
- [ ] we currently default to 0.0 and don't expose a way to edit it. we have a database row, but we don't use it
|
||||||
|
- [-] add configurable size limits to all the Caches
|
||||||
- [ ] active requests per second per api key
|
- [ ] active requests per second per api key
|
||||||
- [ ] distribution of methods per api key (eth_call, eth_getLogs, etc.)
|
- [ ] distribution of methods per api key (eth_call, eth_getLogs, etc.)
|
||||||
- [-] add configurable size limits to all the Caches
|
- [ ] endpoint for creating/modifying api keys and their advanced security features
|
||||||
- [ ] /user/logout to clear bearer token and jwt
|
|
||||||
- [ ] BUG: i think if all backend servers stop, the server doesn't properly reconnect. It appears to stop listening on 8854, but not shut down.
|
- [ ] BUG: i think if all backend servers stop, the server doesn't properly reconnect. It appears to stop listening on 8854, but not shut down.
|
||||||
- [ ] bearer tokens should expire
|
|
||||||
- [-] signed cookie jar
|
|
||||||
- [ ] user login should return both the bearer token and a jwt (jsonwebtoken rust crate should make it easy)
|
|
||||||
- [ ] revert logs should have a maximum age and a maximum count to keep the database from being huge
|
- [ ] revert logs should have a maximum age and a maximum count to keep the database from being huge
|
||||||
- [ ] Ulid instead of Uuid for user keys
|
- [ ] Ulid instead of Uuid for user keys
|
||||||
- <https://discord.com/channels/873880840487206962/900758376164757555/1012942974608474142>
|
- <https://discord.com/channels/873880840487206962/900758376164757555/1012942974608474142>
|
||||||
@ -177,6 +177,7 @@ These are roughly in order of completition
|
|||||||
- [ ] Ulid instead of Uuid for database ids
|
- [ ] Ulid instead of Uuid for database ids
|
||||||
- might have to use Uuid in sea-orm and then convert to Ulid on display
|
- might have to use Uuid in sea-orm and then convert to Ulid on display
|
||||||
- [ ] option to rotate api key
|
- [ ] option to rotate api key
|
||||||
|
- [ ] read the cookie key from a file. easy to re-use and no giant blob of hex in our app config
|
||||||
|
|
||||||
## V1
|
## V1
|
||||||
|
|
||||||
@ -380,3 +381,4 @@ in another repo: event subscriber
|
|||||||
- if we get a connection refused, we should remove the server's block info so it is taken out of rotation
|
- if we get a connection refused, we should remove the server's block info so it is taken out of rotation
|
||||||
- [ ] web3_proxy_cli command should read database settings from config
|
- [ ] web3_proxy_cli command should read database settings from config
|
||||||
- [ ] how should we handle reverting transactions? they won't confirm for a while after we send them
|
- [ ] how should we handle reverting transactions? they won't confirm for a while after we send them
|
||||||
|
- [ ] allow configuration of the expiration time of bearer tokens
|
@ -17,7 +17,7 @@ pub struct Model {
|
|||||||
pub active: bool,
|
pub active: bool,
|
||||||
pub requests_per_minute: Option<u64>,
|
pub requests_per_minute: Option<u64>,
|
||||||
#[sea_orm(column_type = "Decimal(Some((5, 4)))")]
|
#[sea_orm(column_type = "Decimal(Some((5, 4)))")]
|
||||||
pub log_reverts: Decimal,
|
pub log_revert_chance: Decimal,
|
||||||
#[sea_orm(column_type = "Text", nullable)]
|
#[sea_orm(column_type = "Text", nullable)]
|
||||||
pub allowed_ips: Option<String>,
|
pub allowed_ips: Option<String>,
|
||||||
#[sea_orm(column_type = "Text", nullable)]
|
#[sea_orm(column_type = "Text", nullable)]
|
||||||
|
@ -20,7 +20,7 @@ impl MigrationTrait for Migration {
|
|||||||
)
|
)
|
||||||
// add a column for logging reverts in the RevertLogs table
|
// add a column for logging reverts in the RevertLogs table
|
||||||
.add_column(
|
.add_column(
|
||||||
ColumnDef::new(UserKeys::LogReverts)
|
ColumnDef::new(UserKeys::LogRevertChance)
|
||||||
.decimal_len(5, 4)
|
.decimal_len(5, 4)
|
||||||
.not_null()
|
.not_null()
|
||||||
.default("0.0"),
|
.default("0.0"),
|
||||||
@ -90,7 +90,7 @@ impl MigrationTrait for Migration {
|
|||||||
.unsigned()
|
.unsigned()
|
||||||
.not_null(),
|
.not_null(),
|
||||||
)
|
)
|
||||||
.drop_column(UserKeys::LogReverts)
|
.drop_column(UserKeys::LogRevertChance)
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@ -109,7 +109,7 @@ pub enum UserKeys {
|
|||||||
// PrivateTxs,
|
// PrivateTxs,
|
||||||
// Active,
|
// Active,
|
||||||
RequestsPerMinute,
|
RequestsPerMinute,
|
||||||
LogReverts,
|
LogRevertChance,
|
||||||
AllowedIps,
|
AllowedIps,
|
||||||
AllowedOrigins,
|
AllowedOrigins,
|
||||||
AllowedReferers,
|
AllowedReferers,
|
||||||
|
@ -25,6 +25,8 @@ argh = "0.1.9"
|
|||||||
axum = { version = "0.5.16", features = ["headers", "serde_json", "tokio-tungstenite", "ws"] }
|
axum = { version = "0.5.16", features = ["headers", "serde_json", "tokio-tungstenite", "ws"] }
|
||||||
axum-client-ip = "0.2.0"
|
axum-client-ip = "0.2.0"
|
||||||
axum-macros = "0.2.3"
|
axum-macros = "0.2.3"
|
||||||
|
# TODO: import this from ethorm so we always have the same version
|
||||||
|
chrono = "0.4.22"
|
||||||
counter = "0.5.6"
|
counter = "0.5.6"
|
||||||
dashmap = "5.4.0"
|
dashmap = "5.4.0"
|
||||||
derive_more = "0.99.17"
|
derive_more = "0.99.17"
|
||||||
@ -59,7 +61,6 @@ time = "0.3.14"
|
|||||||
tokio = { version = "1.21.1", features = ["full", "tracing"] }
|
tokio = { version = "1.21.1", features = ["full", "tracing"] }
|
||||||
# TODO: make sure this uuid version matches sea-orm. PR to put this in their prelude
|
# TODO: make sure this uuid version matches sea-orm. PR to put this in their prelude
|
||||||
tokio-stream = { version = "0.1.10", features = ["sync"] }
|
tokio-stream = { version = "0.1.10", features = ["sync"] }
|
||||||
tower-cookies = { version = "0.7.0", features = ["private"] }
|
|
||||||
toml = "0.5.9"
|
toml = "0.5.9"
|
||||||
tower = "0.4.13"
|
tower = "0.4.13"
|
||||||
tower-request-id = "0.2.0"
|
tower-request-id = "0.2.0"
|
||||||
|
@ -29,6 +29,7 @@ use metered::{metered, ErrorCount, HitCount, ResponseTime, Throughput};
|
|||||||
use migration::{Migrator, MigratorTrait};
|
use migration::{Migrator, MigratorTrait};
|
||||||
use moka::future::Cache;
|
use moka::future::Cache;
|
||||||
use redis_rate_limiter::{DeadpoolRuntime, RedisConfig, RedisPool, RedisRateLimiter};
|
use redis_rate_limiter::{DeadpoolRuntime, RedisConfig, RedisPool, RedisRateLimiter};
|
||||||
|
use sea_orm::prelude::Decimal;
|
||||||
use sea_orm::DatabaseConnection;
|
use sea_orm::DatabaseConnection;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
@ -44,7 +45,6 @@ use tokio::sync::{broadcast, watch};
|
|||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tokio::time::timeout;
|
use tokio::time::timeout;
|
||||||
use tokio_stream::wrappers::{BroadcastStream, WatchStream};
|
use tokio_stream::wrappers::{BroadcastStream, WatchStream};
|
||||||
use tower_cookies::Key;
|
|
||||||
use tracing::{error, info, trace, warn};
|
use tracing::{error, info, trace, warn};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ type ResponseCache =
|
|||||||
|
|
||||||
pub type AnyhowJoinHandle<T> = JoinHandle<anyhow::Result<T>>;
|
pub type AnyhowJoinHandle<T> = JoinHandle<anyhow::Result<T>>;
|
||||||
|
|
||||||
#[derive(Clone, Debug, From)]
|
#[derive(Clone, Debug, Default, From)]
|
||||||
/// TODO: rename this?
|
/// TODO: rename this?
|
||||||
pub struct UserKeyData {
|
pub struct UserKeyData {
|
||||||
pub user_key_id: u64,
|
pub user_key_id: u64,
|
||||||
@ -78,6 +78,8 @@ pub struct UserKeyData {
|
|||||||
pub allowed_user_agents: Option<Vec<UserAgent>>,
|
pub allowed_user_agents: Option<Vec<UserAgent>>,
|
||||||
/// if None, allow any IP Address
|
/// if None, allow any IP Address
|
||||||
pub allowed_ips: Option<Vec<IpNet>>,
|
pub allowed_ips: Option<Vec<IpNet>>,
|
||||||
|
/// Chance to save reverting eth_call, eth_estimateGas, and eth_sendRawTransaction to the database.
|
||||||
|
pub log_revert_chance: Decimal,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The application
|
/// The application
|
||||||
@ -88,8 +90,6 @@ pub struct Web3ProxyApp {
|
|||||||
pub balanced_rpcs: Arc<Web3Connections>,
|
pub balanced_rpcs: Arc<Web3Connections>,
|
||||||
/// Send private requests (like eth_sendRawTransaction) to all these servers
|
/// Send private requests (like eth_sendRawTransaction) to all these servers
|
||||||
pub private_rpcs: Option<Arc<Web3Connections>>,
|
pub private_rpcs: Option<Arc<Web3Connections>>,
|
||||||
// TODO: this lifetime is definitely wrong
|
|
||||||
pub cookie_key: Key,
|
|
||||||
response_cache: ResponseCache,
|
response_cache: ResponseCache,
|
||||||
// don't drop this or the sender will stop working
|
// don't drop this or the sender will stop working
|
||||||
// TODO: broadcast channel instead?
|
// TODO: broadcast channel instead?
|
||||||
@ -371,12 +371,8 @@ impl Web3ProxyApp {
|
|||||||
.time_to_live(Duration::from_secs(60))
|
.time_to_live(Duration::from_secs(60))
|
||||||
.build_with_hasher(hashbrown::hash_map::DefaultHashBuilder::new());
|
.build_with_hasher(hashbrown::hash_map::DefaultHashBuilder::new());
|
||||||
|
|
||||||
// TODO: get this from the app's config
|
|
||||||
let cookie_key = Key::from(&[0; 64]);
|
|
||||||
|
|
||||||
let app = Self {
|
let app = Self {
|
||||||
config: top_config.app,
|
config: top_config.app,
|
||||||
cookie_key,
|
|
||||||
balanced_rpcs,
|
balanced_rpcs,
|
||||||
private_rpcs,
|
private_rpcs,
|
||||||
response_cache,
|
response_cache,
|
||||||
|
@ -204,6 +204,7 @@ mod tests {
|
|||||||
prometheus_port: 0,
|
prometheus_port: 0,
|
||||||
workers: 4,
|
workers: 4,
|
||||||
config: "./does/not/exist/test.toml".to_string(),
|
config: "./does/not/exist/test.toml".to_string(),
|
||||||
|
cookie_key_filename: "./does/not/exist/development_cookie_key".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// make a test AppConfig
|
// make a test AppConfig
|
||||||
|
@ -36,6 +36,7 @@ impl CreateUserSubCommand {
|
|||||||
|
|
||||||
// TODO: would be nice to use the fixed array instead of a Vec in the entities
|
// TODO: would be nice to use the fixed array instead of a Vec in the entities
|
||||||
// TODO: how can we use custom types with
|
// TODO: how can we use custom types with
|
||||||
|
// TODO: take a simple String. If it starts with 0x, parse as address. otherwise convert ascii to hex
|
||||||
let address = self.address.to_fixed_bytes().into();
|
let address = self.address.to_fixed_bytes().into();
|
||||||
|
|
||||||
let u = user::ActiveModel {
|
let u = user::ActiveModel {
|
||||||
|
@ -32,6 +32,10 @@ pub struct CliConfig {
|
|||||||
/// number of worker threads. Defaults to the number of logical processors
|
/// number of worker threads. Defaults to the number of logical processors
|
||||||
#[argh(option, default = "0")]
|
#[argh(option, default = "0")]
|
||||||
pub workers: usize,
|
pub workers: usize,
|
||||||
|
|
||||||
|
/// path to a binary file used to encrypt cookies. Should be at least 64 bytes.
|
||||||
|
#[argh(option, default = "\"./data/development_cookie_key\".to_string()")]
|
||||||
|
pub cookie_key_filename: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
@ -46,6 +50,8 @@ pub struct TopConfig {
|
|||||||
pub struct AppConfig {
|
pub struct AppConfig {
|
||||||
// TODO: better type for chain_id? max of `u64::MAX / 2 - 36` https://github.com/ethereum/EIPs/issues/2294
|
// TODO: better type for chain_id? max of `u64::MAX / 2 - 36` https://github.com/ethereum/EIPs/issues/2294
|
||||||
pub chain_id: u64,
|
pub chain_id: u64,
|
||||||
|
pub cookie_domain: Option<String>,
|
||||||
|
pub cookie_secure: Option<bool>,
|
||||||
pub db_url: Option<String>,
|
pub db_url: Option<String>,
|
||||||
/// minimum size of the connection pool for the database
|
/// minimum size of the connection pool for the database
|
||||||
/// If none, the number of workers are used
|
/// If none, the number of workers are used
|
||||||
|
@ -5,7 +5,7 @@ use axum::headers::{Origin, Referer, UserAgent};
|
|||||||
use deferred_rate_limiter::DeferredRateLimitResult;
|
use deferred_rate_limiter::DeferredRateLimitResult;
|
||||||
use entities::user_keys;
|
use entities::user_keys;
|
||||||
use ipnet::IpNet;
|
use ipnet::IpNet;
|
||||||
use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter};
|
use sea_orm::{prelude::Decimal, ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::{net::IpAddr, sync::Arc};
|
use std::{net::IpAddr, sync::Arc};
|
||||||
use tokio::time::Instant;
|
use tokio::time::Instant;
|
||||||
@ -28,9 +28,10 @@ pub enum RateLimitResult {
|
|||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct AuthorizedKey {
|
pub struct AuthorizedKey {
|
||||||
ip: IpAddr,
|
pub ip: IpAddr,
|
||||||
origin: Option<String>,
|
pub origin: Option<String>,
|
||||||
user_key_id: u64,
|
pub user_key_id: u64,
|
||||||
|
pub log_revert_chance: Decimal,
|
||||||
// TODO: what else?
|
// TODO: what else?
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +97,7 @@ impl AuthorizedKey {
|
|||||||
ip,
|
ip,
|
||||||
origin,
|
origin,
|
||||||
user_key_id: user_data.user_key_id,
|
user_key_id: user_data.user_key_id,
|
||||||
|
log_revert_chance: user_data.log_revert_chance,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -269,16 +271,10 @@ impl Web3ProxyApp {
|
|||||||
allowed_origins,
|
allowed_origins,
|
||||||
allowed_referers,
|
allowed_referers,
|
||||||
allowed_user_agents,
|
allowed_user_agents,
|
||||||
|
log_revert_chance: user_key_model.log_revert_chance,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
None => Ok(UserKeyData {
|
None => Ok(UserKeyData::default()),
|
||||||
user_key_id: 0,
|
|
||||||
user_max_requests_per_period: Some(0),
|
|
||||||
allowed_ips: None,
|
|
||||||
allowed_origins: None,
|
|
||||||
allowed_referers: None,
|
|
||||||
allowed_user_agents: None,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
@ -81,7 +81,7 @@ impl IntoResponse for FrontendErrorResponse {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Self::RateLimitedIp(ip, retry_at) => {
|
Self::RateLimitedIp(ip, _retry_at) => {
|
||||||
// TODO: emit a stat
|
// TODO: emit a stat
|
||||||
// TODO: include retry_at in the error
|
// TODO: include retry_at in the error
|
||||||
// TODO: if retry_at is None, give an unauthorized status code?
|
// TODO: if retry_at is None, give an unauthorized status code?
|
||||||
@ -95,7 +95,7 @@ impl IntoResponse for FrontendErrorResponse {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
// TODO: this should actually by the id of the key. multiple users might control one key
|
// TODO: this should actually by the id of the key. multiple users might control one key
|
||||||
Self::RateLimitedUser(user_data, retry_at) => {
|
Self::RateLimitedUser(user_data, _retry_at) => {
|
||||||
// TODO: emit a stat
|
// TODO: emit a stat
|
||||||
// TODO: include retry_at in the error
|
// TODO: include retry_at in the error
|
||||||
(
|
(
|
||||||
|
@ -15,7 +15,6 @@ use axum::{
|
|||||||
};
|
};
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tower_cookies::CookieManagerLayer;
|
|
||||||
use tower_http::trace::TraceLayer;
|
use tower_http::trace::TraceLayer;
|
||||||
use tower_request_id::{RequestId, RequestIdLayer};
|
use tower_request_id::{RequestId, RequestIdLayer};
|
||||||
use tracing::{error_span, info};
|
use tracing::{error_span, info};
|
||||||
@ -70,8 +69,6 @@ pub async fn serve(port: u16, proxy_app: Arc<Web3ProxyApp>) -> anyhow::Result<()
|
|||||||
.layer(request_tracing_layer)
|
.layer(request_tracing_layer)
|
||||||
// create a unique id for each request
|
// create a unique id for each request
|
||||||
.layer(RequestIdLayer)
|
.layer(RequestIdLayer)
|
||||||
// signed cookies
|
|
||||||
.layer(CookieManagerLayer::new())
|
|
||||||
// 404 for any unknown routes
|
// 404 for any unknown routes
|
||||||
.fallback(errors::handler_404.into_service());
|
.fallback(errors::handler_404.into_service());
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@ use siwe::Message;
|
|||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use time::{Duration, OffsetDateTime};
|
use time::{Duration, OffsetDateTime};
|
||||||
use tower_cookies::Cookies;
|
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@ -130,15 +129,16 @@ pub struct PostLogin {
|
|||||||
// signer: String,
|
// signer: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO: what information should we return?
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct PostLoginResponse {
|
pub struct PostLoginResponse {
|
||||||
bearer_token: Ulid,
|
bearer_token: Ulid,
|
||||||
// TODO: change this Ulid
|
api_keys: Vec<Uuid>,
|
||||||
api_key: Uuid,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[debug_handler]
|
|
||||||
/// Post to the user endpoint to register or login.
|
/// Post to the user endpoint to register or login.
|
||||||
|
/// It is recommended to save the returned bearer this in a cookie and send bac
|
||||||
|
#[debug_handler]
|
||||||
pub async fn post_login(
|
pub async fn post_login(
|
||||||
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
||||||
ClientIp(ip): ClientIp,
|
ClientIp(ip): ClientIp,
|
||||||
@ -183,7 +183,7 @@ pub async fn post_login(
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (u, uk, response) = match u {
|
let (u, _uks, response) = match u {
|
||||||
None => {
|
None => {
|
||||||
let txn = db.begin().await?;
|
let txn = db.begin().await?;
|
||||||
|
|
||||||
@ -209,71 +209,65 @@ pub async fn post_login(
|
|||||||
.await
|
.await
|
||||||
.context("Failed saving new user key")?;
|
.context("Failed saving new user key")?;
|
||||||
|
|
||||||
|
let uks = vec![uk];
|
||||||
|
|
||||||
txn.commit().await?;
|
txn.commit().await?;
|
||||||
|
|
||||||
let response_json = PostLoginResponse {
|
let response_json = PostLoginResponse {
|
||||||
bearer_token,
|
bearer_token,
|
||||||
api_key: uk.api_key,
|
api_keys: uks.iter().map(|uk| uk.api_key).collect(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = (StatusCode::CREATED, Json(response_json)).into_response();
|
let response = (StatusCode::CREATED, Json(response_json)).into_response();
|
||||||
|
|
||||||
(u, uk, response)
|
(u, uks, response)
|
||||||
}
|
}
|
||||||
Some(u) => {
|
Some(u) => {
|
||||||
// the user is already registered
|
// the user is already registered
|
||||||
// TODO: what if the user has multiple keys?
|
let uks = user_keys::Entity::find()
|
||||||
let uk = user_keys::Entity::find()
|
|
||||||
.filter(user_keys::Column::UserId.eq(u.id))
|
.filter(user_keys::Column::UserId.eq(u.id))
|
||||||
.one(db)
|
.all(db)
|
||||||
.await
|
.await
|
||||||
.context("failed loading user's key")?
|
.context("failed loading user's key")?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let response_json = PostLoginResponse {
|
let response_json = PostLoginResponse {
|
||||||
bearer_token,
|
bearer_token,
|
||||||
api_key: uk.api_key,
|
api_keys: uks.iter().map(|uk| uk.api_key).collect(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = (StatusCode::OK, Json(response_json)).into_response();
|
let response = (StatusCode::OK, Json(response_json)).into_response();
|
||||||
|
|
||||||
(u, uk, response)
|
(u, uks, response)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: set a session cookie with the bearer token?
|
// add bearer to redis
|
||||||
|
|
||||||
// save the bearer token in redis with a long (7 or 30 day?) expiry. or in database?
|
|
||||||
let mut redis_conn = app.redis_conn().await?;
|
let mut redis_conn = app.redis_conn().await?;
|
||||||
|
|
||||||
// TODO: move this into a struct so this is less fragile
|
let bearer_redis_key = format!("bearer:{}", bearer_token);
|
||||||
let bearer_key = format!("bearer:{}", bearer_token);
|
|
||||||
|
|
||||||
redis_conn.set(bearer_key, u.id.to_string()).await?;
|
// expire in 4 weeks
|
||||||
|
// TODO: get expiration time from app config
|
||||||
// TODO: save user_data. we already have uk, so this could be more efficient. it works for now
|
// TODO: do we use this?
|
||||||
|
redis_conn
|
||||||
|
.set_ex(bearer_redis_key, u.id.to_string(), 2_419_200)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Log out the user connected to the given Authentication header.
|
||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
pub async fn get_logout(
|
pub async fn get_logout(
|
||||||
cookies: Cookies,
|
|
||||||
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
||||||
|
TypedHeader(Authorization(bearer)): TypedHeader<Authorization<Bearer>>,
|
||||||
) -> FrontendResult {
|
) -> FrontendResult {
|
||||||
// delete the cookie if it exists
|
// TODO: i don't like this. move this to a helper function so it is less fragile
|
||||||
let private_cookies = cookies.private(&app.cookie_key);
|
let bearer_cache_key = format!("bearer:{}", bearer.token());
|
||||||
|
|
||||||
if let Some(c) = private_cookies.get("bearer") {
|
let mut redis_conn = app.redis_conn().await?;
|
||||||
let bearer_cache_key = format!("bearer:{}", c.value());
|
|
||||||
|
|
||||||
// TODO: should deleting the cookie be last? redis being down shouldn't block the user
|
redis_conn.del(bearer_cache_key).await?;
|
||||||
private_cookies.remove(c);
|
|
||||||
|
|
||||||
let mut redis_conn = app.redis_conn().await?;
|
|
||||||
|
|
||||||
redis_conn.del(bearer_cache_key).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: what should the response be? probably json something
|
// TODO: what should the response be? probably json something
|
||||||
Ok("goodbye".into_response())
|
Ok("goodbye".into_response())
|
||||||
@ -348,8 +342,6 @@ impl ProtectedAction {
|
|||||||
// TODO: is this type correct?
|
// TODO: is this type correct?
|
||||||
let u_id: Option<u64> = redis_conn.get(bearer_cache_key).await?;
|
let u_id: Option<u64> = redis_conn.get(bearer_cache_key).await?;
|
||||||
|
|
||||||
// TODO: if not in redis, check the db?
|
|
||||||
|
|
||||||
// TODO: if auth_address == primary_address, allow
|
// TODO: if auth_address == primary_address, allow
|
||||||
// TODO: if auth_address != primary_address, only allow if they are a secondary user with the correct role
|
// TODO: if auth_address != primary_address, only allow if they are a secondary user with the correct role
|
||||||
todo!("verify token for the given user");
|
todo!("verify token for the given user");
|
||||||
|
@ -108,7 +108,11 @@ impl Web3Connections {
|
|||||||
Some(rpc) => {
|
Some(rpc) => {
|
||||||
rpc.wait_for_request_handle(authorization, Duration::from_secs(30))
|
rpc.wait_for_request_handle(authorization, Duration::from_secs(30))
|
||||||
.await?
|
.await?
|
||||||
.request("eth_getBlockByHash", &get_block_params, Level::ERROR.into())
|
.request(
|
||||||
|
"eth_getBlockByHash",
|
||||||
|
&json!(get_block_params),
|
||||||
|
Level::ERROR.into(),
|
||||||
|
)
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
@ -15,6 +15,7 @@ use redis_rate_limiter::{RedisPool, RedisRateLimitResult, RedisRateLimiter};
|
|||||||
use sea_orm::DatabaseConnection;
|
use sea_orm::DatabaseConnection;
|
||||||
use serde::ser::{SerializeStruct, Serializer};
|
use serde::ser::{SerializeStruct, Serializer};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use serde_json::json;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
@ -121,7 +122,11 @@ impl Web3Connection {
|
|||||||
let found_chain_id: Result<U64, _> = new_connection
|
let found_chain_id: Result<U64, _> = new_connection
|
||||||
.wait_for_request_handle(None, Duration::from_secs(30))
|
.wait_for_request_handle(None, Duration::from_secs(30))
|
||||||
.await?
|
.await?
|
||||||
.request("eth_chainId", &Option::None::<()>, Level::ERROR.into())
|
.request(
|
||||||
|
"eth_chainId",
|
||||||
|
&json!(Option::None::<()>),
|
||||||
|
Level::ERROR.into(),
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
match found_chain_id {
|
match found_chain_id {
|
||||||
@ -209,10 +214,10 @@ impl Web3Connection {
|
|||||||
.await?
|
.await?
|
||||||
.request(
|
.request(
|
||||||
"eth_getCode",
|
"eth_getCode",
|
||||||
&(
|
&json!((
|
||||||
"0xdead00000000000000000000000000000000beef",
|
"0xdead00000000000000000000000000000000beef",
|
||||||
maybe_archive_block,
|
maybe_archive_block,
|
||||||
),
|
)),
|
||||||
// error here are expected, so keep the level low
|
// error here are expected, so keep the level low
|
||||||
tracing::Level::DEBUG.into(),
|
tracing::Level::DEBUG.into(),
|
||||||
)
|
)
|
||||||
@ -543,7 +548,7 @@ impl Web3Connection {
|
|||||||
let block: Result<Block<TxHash>, _> = active_request_handle
|
let block: Result<Block<TxHash>, _> = active_request_handle
|
||||||
.request(
|
.request(
|
||||||
"eth_getBlockByNumber",
|
"eth_getBlockByNumber",
|
||||||
&("latest", false),
|
&json!(("latest", false)),
|
||||||
tracing::Level::ERROR.into(),
|
tracing::Level::ERROR.into(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@ -619,7 +624,7 @@ impl Web3Connection {
|
|||||||
.await?
|
.await?
|
||||||
.request(
|
.request(
|
||||||
"eth_getBlockByNumber",
|
"eth_getBlockByNumber",
|
||||||
&("latest", false),
|
&json!(("latest", false)),
|
||||||
tracing::Level::ERROR.into(),
|
tracing::Level::ERROR.into(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -23,6 +23,7 @@ use petgraph::graphmap::DiGraphMap;
|
|||||||
use sea_orm::DatabaseConnection;
|
use sea_orm::DatabaseConnection;
|
||||||
use serde::ser::{SerializeStruct, Serializer};
|
use serde::ser::{SerializeStruct, Serializer};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use serde_json::json;
|
||||||
use serde_json::value::RawValue;
|
use serde_json::value::RawValue;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::cmp::Reverse;
|
use std::cmp::Reverse;
|
||||||
@ -320,7 +321,11 @@ impl Web3Connections {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|active_request_handle| async move {
|
.map(|active_request_handle| async move {
|
||||||
let result: Result<Box<RawValue>, _> = active_request_handle
|
let result: Result<Box<RawValue>, _> = active_request_handle
|
||||||
.request(method, ¶ms.cloned(), tracing::Level::ERROR.into())
|
.request(
|
||||||
|
method,
|
||||||
|
&json!(params.cloned()),
|
||||||
|
tracing::Level::ERROR.into(),
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
result
|
result
|
||||||
})
|
})
|
||||||
@ -517,12 +522,12 @@ impl Web3Connections {
|
|||||||
// save the rpc in case we get an error and want to retry on another server
|
// save the rpc in case we get an error and want to retry on another server
|
||||||
skip_rpcs.push(active_request_handle.clone_connection());
|
skip_rpcs.push(active_request_handle.clone_connection());
|
||||||
|
|
||||||
// TODO: get the log percent from the user data?
|
// TODO: get the log percent from the user data
|
||||||
let response_result = active_request_handle
|
let response_result = active_request_handle
|
||||||
.request(
|
.request(
|
||||||
&request.method,
|
&request.method,
|
||||||
&request.params,
|
&json!(request.params),
|
||||||
RequestErrorHandler::SaveReverts(100.0),
|
RequestErrorHandler::SaveReverts(0.0),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
@ -3,12 +3,17 @@ use super::provider::Web3Provider;
|
|||||||
use crate::frontend::authorization::AuthorizedRequest;
|
use crate::frontend::authorization::AuthorizedRequest;
|
||||||
use crate::metered::{JsonRpcErrorCount, ProviderErrorCount};
|
use crate::metered::{JsonRpcErrorCount, ProviderErrorCount};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use chrono::Utc;
|
||||||
|
use entities::revert_logs;
|
||||||
|
use entities::sea_orm_active_enums::Method;
|
||||||
use ethers::providers::{HttpClientError, ProviderError, WsClientError};
|
use ethers::providers::{HttpClientError, ProviderError, WsClientError};
|
||||||
use metered::metered;
|
use metered::metered;
|
||||||
use metered::HitCount;
|
use metered::HitCount;
|
||||||
use metered::ResponseTime;
|
use metered::ResponseTime;
|
||||||
use metered::Throughput;
|
use metered::Throughput;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
use sea_orm::ActiveModelTrait;
|
||||||
|
use serde_json::json;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::atomic::{self, AtomicBool, Ordering};
|
use std::sync::atomic::{self, AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -47,6 +52,15 @@ pub enum RequestErrorHandler {
|
|||||||
WarnLevel,
|
WarnLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize, serde::Serialize)]
|
||||||
|
struct EthCallParams {
|
||||||
|
method: Method,
|
||||||
|
// TODO: do this as Address instead
|
||||||
|
to: Vec<u8>,
|
||||||
|
// TODO: do this as a Bytes instead
|
||||||
|
data: String,
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Level> for RequestErrorHandler {
|
impl From<Level> for RequestErrorHandler {
|
||||||
fn from(level: Level) -> Self {
|
fn from(level: Level) -> Self {
|
||||||
match level {
|
match level {
|
||||||
@ -60,13 +74,31 @@ impl From<Level> for RequestErrorHandler {
|
|||||||
|
|
||||||
impl AuthorizedRequest {
|
impl AuthorizedRequest {
|
||||||
/// Save a RPC call that return "execution reverted" to the database.
|
/// Save a RPC call that return "execution reverted" to the database.
|
||||||
async fn save_revert<T>(self: Arc<Self>, method: String, params: T) -> anyhow::Result<()>
|
async fn save_revert(self: Arc<Self>, params: EthCallParams) -> anyhow::Result<()> {
|
||||||
where
|
if let Self::User(Some(db_conn), authorized_request) = &*self {
|
||||||
T: Clone + fmt::Debug + serde::Serialize + Send + Sync + 'static,
|
// TODO: do this on the database side?
|
||||||
{
|
let timestamp = Utc::now();
|
||||||
let db_conn = self.db_conn().context("db_conn needed to save reverts")?;
|
|
||||||
|
|
||||||
todo!("save the revert to the database");
|
let rl = revert_logs::ActiveModel {
|
||||||
|
user_key_id: sea_orm::Set(authorized_request.user_key_id),
|
||||||
|
method: sea_orm::Set(params.method),
|
||||||
|
to: sea_orm::Set(params.to),
|
||||||
|
call_data: sea_orm::Set(params.data),
|
||||||
|
timestamp: sea_orm::Set(timestamp),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let rl = rl
|
||||||
|
.save(db_conn)
|
||||||
|
.await
|
||||||
|
.context("Failed saving new revert log")?;
|
||||||
|
|
||||||
|
// TODO: what log level?
|
||||||
|
trace!(?rl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: return something useful
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,15 +142,15 @@ impl OpenRequestHandle {
|
|||||||
/// TODO: we no longer take self because metered doesn't like that
|
/// TODO: we no longer take self because metered doesn't like that
|
||||||
/// TODO: ErrorCount includes too many types of errors, such as transaction reverts
|
/// TODO: ErrorCount includes too many types of errors, such as transaction reverts
|
||||||
#[measure([JsonRpcErrorCount, HitCount, ProviderErrorCount, ResponseTime, Throughput])]
|
#[measure([JsonRpcErrorCount, HitCount, ProviderErrorCount, ResponseTime, Throughput])]
|
||||||
pub async fn request<T, R>(
|
pub async fn request<P, R>(
|
||||||
&self,
|
&self,
|
||||||
method: &str,
|
method: &str,
|
||||||
params: &T,
|
params: &P,
|
||||||
error_handler: RequestErrorHandler,
|
error_handler: RequestErrorHandler,
|
||||||
) -> Result<R, ProviderError>
|
) -> Result<R, ProviderError>
|
||||||
where
|
where
|
||||||
// TODO: not sure about this type. would be better to not need clones, but measure and spawns combine to need it
|
// TODO: not sure about this type. would be better to not need clones, but measure and spawns combine to need it
|
||||||
T: Clone + fmt::Debug + serde::Serialize + Send + Sync + 'static,
|
P: Clone + fmt::Debug + serde::Serialize + Send + Sync + 'static,
|
||||||
R: serde::Serialize + serde::de::DeserializeOwned + fmt::Debug,
|
R: serde::Serialize + serde::de::DeserializeOwned + fmt::Debug,
|
||||||
{
|
{
|
||||||
// ensure this function only runs once
|
// ensure this function only runs once
|
||||||
@ -212,11 +244,12 @@ impl OpenRequestHandle {
|
|||||||
|
|
||||||
if let Some(msg) = msg {
|
if let Some(msg) = msg {
|
||||||
if msg.starts_with("execution reverted") {
|
if msg.starts_with("execution reverted") {
|
||||||
|
// TODO: is there a more efficient way to do this?
|
||||||
|
let params: EthCallParams = serde_json::from_value(json!(params))
|
||||||
|
.expect("parsing eth_call");
|
||||||
|
|
||||||
// spawn saving to the database so we don't slow down the request (or error if no db)
|
// spawn saving to the database so we don't slow down the request (or error if no db)
|
||||||
let f = self
|
let f = self.authorization.clone().save_revert(params);
|
||||||
.authorization
|
|
||||||
.clone()
|
|
||||||
.save_revert(method.to_string(), params.clone());
|
|
||||||
|
|
||||||
tokio::spawn(async move { f.await });
|
tokio::spawn(async move { f.await });
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user