2023-05-24 00:40:34 +03:00
|
|
|
use crate::app::Web3ProxyJoinHandle;
|
2023-08-03 21:54:50 +03:00
|
|
|
use crate::compute_units::default_usd_per_cu;
|
2023-02-26 10:52:33 +03:00
|
|
|
use crate::rpcs::blockchain::{BlocksByHashCache, Web3ProxyBlock};
|
2023-02-06 20:55:27 +03:00
|
|
|
use crate::rpcs::one::Web3Rpc;
|
2022-05-06 00:25:40 +03:00
|
|
|
use argh::FromArgs;
|
2023-06-25 05:36:21 +03:00
|
|
|
use ethers::prelude::{Address, TxHash};
|
2023-02-14 23:14:50 +03:00
|
|
|
use ethers::types::{U256, U64};
|
2022-07-23 02:26:04 +03:00
|
|
|
use hashbrown::HashMap;
|
2023-07-12 10:35:07 +03:00
|
|
|
use migration::sea_orm::prelude::Decimal;
|
2023-06-24 02:28:45 +03:00
|
|
|
use sentry::types::Dsn;
|
2023-09-13 00:59:13 +03:00
|
|
|
use serde::{de, Deserialize, Deserializer};
|
2023-07-06 13:51:39 +03:00
|
|
|
use serde_inline_default::serde_inline_default;
|
2023-09-13 00:59:13 +03:00
|
|
|
use std::fmt;
|
2023-09-12 23:35:51 +03:00
|
|
|
use std::sync::atomic::AtomicU64;
|
2022-05-05 22:07:09 +03:00
|
|
|
use std::sync::Arc;
|
2023-05-24 00:40:34 +03:00
|
|
|
use std::time::Duration;
|
2023-07-11 09:08:06 +03:00
|
|
|
use tokio::sync::mpsc;
|
2023-06-24 02:28:45 +03:00
|
|
|
use tracing::warn;
|
2022-05-05 22:07:09 +03:00
|
|
|
|
2023-02-14 23:14:50 +03:00
|
|
|
pub type BlockAndRpc = (Option<Web3ProxyBlock>, Arc<Web3Rpc>);
|
2023-02-06 20:55:27 +03:00
|
|
|
pub type TxHashAndRpc = (TxHash, Arc<Web3Rpc>);
|
2022-07-22 08:11:26 +03:00
|
|
|
|
2022-05-22 02:34:05 +03:00
|
|
|
#[derive(Debug, FromArgs)]
|
2022-08-06 08:46:33 +03:00
|
|
|
/// Web3_proxy is a fast caching and load balancing proxy for web3 (Ethereum or similar) JsonRPC servers.
|
2022-05-06 00:25:40 +03:00
|
|
|
pub struct CliConfig {
|
2022-08-13 00:00:26 +03:00
|
|
|
/// path to a toml of rpc servers
|
|
|
|
#[argh(option, default = "\"./config/development.toml\".to_string()")]
|
|
|
|
pub config: String,
|
|
|
|
|
2022-05-06 00:25:40 +03:00
|
|
|
/// what port the proxy should listen on
|
2022-05-06 09:33:29 +03:00
|
|
|
#[argh(option, default = "8544")]
|
2022-05-18 23:28:00 +03:00
|
|
|
pub port: u16,
|
2022-05-06 00:25:40 +03:00
|
|
|
|
2022-08-13 00:00:26 +03:00
|
|
|
/// what port the proxy should expose prometheus stats on
|
|
|
|
#[argh(option, default = "8543")]
|
|
|
|
pub prometheus_port: u16,
|
|
|
|
|
2022-06-06 01:39:44 +03:00
|
|
|
/// number of worker threads. Defaults to the number of logical processors
|
2022-05-18 19:35:06 +03:00
|
|
|
#[argh(option, default = "0")]
|
2022-05-18 23:28:00 +03:00
|
|
|
pub workers: usize,
|
2022-09-24 05:47:44 +03:00
|
|
|
|
|
|
|
/// 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,
|
2022-05-06 00:25:40 +03:00
|
|
|
}
|
|
|
|
|
2023-02-28 00:13:18 +03:00
|
|
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
2022-08-12 22:07:14 +03:00
|
|
|
pub struct TopConfig {
|
|
|
|
pub app: AppConfig,
|
2023-02-06 20:55:27 +03:00
|
|
|
pub balanced_rpcs: HashMap<String, Web3RpcConfig>,
|
|
|
|
pub private_rpcs: Option<HashMap<String, Web3RpcConfig>>,
|
2023-04-14 10:04:35 +03:00
|
|
|
pub bundler_4337_rpcs: Option<HashMap<String, Web3RpcConfig>>,
|
2022-12-28 19:36:22 +03:00
|
|
|
/// unknown config options get put here
|
2022-12-29 00:50:34 +03:00
|
|
|
#[serde(flatten, default = "HashMap::default")]
|
2022-12-28 19:36:22 +03:00
|
|
|
pub extra: HashMap<String, serde_json::Value>,
|
2022-05-05 22:07:09 +03:00
|
|
|
}
|
|
|
|
|
2023-08-03 21:54:50 +03:00
|
|
|
impl TopConfig {
|
|
|
|
/// TODO: this should probably be part of Deserialize
|
|
|
|
pub fn clean(&mut self) {
|
|
|
|
if !self.extra.is_empty() {
|
|
|
|
warn!(
|
|
|
|
extra=?self.extra.keys(),
|
|
|
|
"unknown TopConfig fields!",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.app.clean();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-06 20:55:27 +03:00
|
|
|
/// shared configuration between Web3Rpcs
|
2022-10-03 21:08:01 +03:00
|
|
|
// TODO: no String, only &str
|
2023-07-06 13:51:39 +03:00
|
|
|
#[serde_inline_default]
|
|
|
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
2022-08-12 22:07:14 +03:00
|
|
|
pub struct AppConfig {
|
2022-10-25 00:07:29 +03:00
|
|
|
/// Request limit for allowed origins for anonymous users.
|
2022-10-25 07:34:24 +03:00
|
|
|
/// These requests get rate limited by IP.
|
2023-07-06 13:51:39 +03:00
|
|
|
#[serde(default = "Default::default")]
|
2022-11-01 22:12:57 +03:00
|
|
|
pub allowed_origin_requests_per_period: HashMap<String, u64>,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
2023-02-06 04:58:03 +03:00
|
|
|
/// erigon defaults to pruning beyond 90,000 blocks
|
2023-07-06 13:51:39 +03:00
|
|
|
#[serde_inline_default(90_000u64)]
|
2023-02-06 04:58:03 +03:00
|
|
|
pub archive_depth: u64,
|
|
|
|
|
2022-10-07 05:15:53 +03:00
|
|
|
/// EVM chain id. 1 for ETH
|
2022-11-08 01:30:02 +03:00
|
|
|
/// TODO: better type for chain_id? max of `u64::MAX / 2 - 36` <https://github.com/ethereum/EIPs/issues/2294>
|
2023-07-06 23:50:20 +03:00
|
|
|
#[serde_inline_default(1u64)]
|
2022-07-19 07:21:32 +03:00
|
|
|
pub chain_id: u64,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
2023-07-10 05:23:32 +03:00
|
|
|
/// Cost per computational unit
|
|
|
|
// pub cost_per_cu: Decimal,
|
|
|
|
|
2022-10-07 05:15:53 +03:00
|
|
|
/// Database is used for user data.
|
|
|
|
/// Currently supports mysql or compatible backend.
|
2022-07-26 07:53:38 +03:00
|
|
|
pub db_url: Option<String>,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
2022-10-07 05:15:53 +03:00
|
|
|
/// minimum size of the connection pool for the database.
|
|
|
|
/// If none, the number of workers are used.
|
2022-09-06 23:50:37 +03:00
|
|
|
pub db_min_connections: Option<u32>,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
2022-10-07 05:15:53 +03:00
|
|
|
/// maximum size of the connection pool for the database.
|
|
|
|
/// If none, the minimum * 2 is used.
|
2022-09-02 23:16:20 +03:00
|
|
|
pub db_max_connections: Option<u32>,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
2022-12-16 11:48:24 +03:00
|
|
|
/// Read-only replica of db_url.
|
|
|
|
pub db_replica_url: Option<String>,
|
|
|
|
|
|
|
|
/// minimum size of the connection pool for the database replica.
|
|
|
|
/// If none, db_min_connections is used.
|
|
|
|
pub db_replica_min_connections: Option<u32>,
|
|
|
|
|
|
|
|
/// maximum size of the connection pool for the database replica.
|
|
|
|
/// If none, db_max_connections is used.
|
|
|
|
pub db_replica_max_connections: Option<u32>,
|
|
|
|
|
2022-10-07 05:15:53 +03:00
|
|
|
/// Default request limit for registered users.
|
|
|
|
/// 0 = block all requests
|
|
|
|
/// None = allow all requests
|
2022-11-01 22:12:57 +03:00
|
|
|
pub default_user_max_requests_per_period: Option<u64>,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
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
|
|
|
/// Default ERC address for out deposit contract
|
|
|
|
pub deposit_factory_contract: Option<Address>,
|
|
|
|
|
2023-09-25 20:38:55 +03:00
|
|
|
/// True if anonymous users should be able to eth_subscribe
|
|
|
|
/// newHeads is always allowed because that is cheap to send
|
|
|
|
#[serde_inline_default(false)]
|
|
|
|
pub free_subscriptions: bool,
|
|
|
|
|
2023-01-20 05:08:53 +03:00
|
|
|
/// minimum amount to increase eth_estimateGas results
|
|
|
|
pub gas_increase_min: Option<U256>,
|
|
|
|
|
|
|
|
/// percentage to increase eth_estimateGas results. 100 == 100%
|
|
|
|
pub gas_increase_percent: Option<U256>,
|
|
|
|
|
2022-10-07 05:15:53 +03:00
|
|
|
/// Restrict user registration.
|
|
|
|
/// None = no code needed
|
2022-09-02 23:16:20 +03:00
|
|
|
pub invite_code: Option<String>,
|
2023-03-03 04:39:50 +03:00
|
|
|
|
|
|
|
/// Optional kafka brokers
|
|
|
|
/// Used by /debug/:rpc_key urls for logging requests and responses. No other endpoints log request/response data.
|
|
|
|
pub kafka_urls: Option<String>,
|
|
|
|
|
2023-07-06 13:51:39 +03:00
|
|
|
#[serde_inline_default("ssl".to_string())]
|
2023-04-20 01:22:27 +03:00
|
|
|
pub kafka_protocol: String,
|
|
|
|
|
2023-03-03 04:39:50 +03:00
|
|
|
/// domain in sign-in-with-ethereum messages
|
2022-11-28 22:59:42 +03:00
|
|
|
pub login_domain: Option<String>,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
2023-02-14 23:14:50 +03:00
|
|
|
/// do not serve any requests if the best known block is behind the best known block by more than this many blocks.
|
2023-06-17 20:11:48 +03:00
|
|
|
pub max_head_block_lag: Option<U64>,
|
2023-02-14 23:14:50 +03:00
|
|
|
|
2022-10-25 07:34:24 +03:00
|
|
|
/// Rate limit for the login entrypoint.
|
|
|
|
/// This is separate from the rpc limits.
|
2023-07-06 13:51:39 +03:00
|
|
|
#[serde_inline_default(10u64)]
|
2022-11-01 22:12:57 +03:00
|
|
|
pub login_rate_limit_per_period: u64,
|
2022-10-25 07:34:24 +03:00
|
|
|
|
2022-10-07 05:15:53 +03:00
|
|
|
/// The soft limit prevents thundering herds as new blocks are seen.
|
2023-07-06 13:51:39 +03:00
|
|
|
#[serde_inline_default(1u32)]
|
2022-08-27 06:11:58 +03:00
|
|
|
pub min_sum_soft_limit: u32,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
2022-10-07 05:15:53 +03:00
|
|
|
/// Another knob for preventing thundering herds as new blocks are seen.
|
2023-07-06 13:51:39 +03:00
|
|
|
#[serde_inline_default(1usize)]
|
2022-09-02 08:40:56 +03:00
|
|
|
pub min_synced_rpcs: usize,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
2022-10-25 07:01:41 +03:00
|
|
|
/// Concurrent request limit for anonymous users.
|
|
|
|
/// Some(0) = block all requests
|
|
|
|
/// None = allow all requests
|
|
|
|
pub public_max_concurrent_requests: Option<usize>,
|
|
|
|
|
2022-10-25 07:34:24 +03:00
|
|
|
/// Request limit for anonymous users.
|
|
|
|
/// Some(0) = block all requests
|
|
|
|
/// None = allow all requests
|
2022-11-01 22:12:57 +03:00
|
|
|
pub public_requests_per_period: Option<u64>,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
2023-01-26 08:24:09 +03:00
|
|
|
/// Salt for hashing recent ips. Not a perfect way to introduce privacy, but better than nothing
|
2022-12-28 09:11:18 +03:00
|
|
|
pub public_recent_ips_salt: Option<String>,
|
|
|
|
|
2022-10-07 05:15:53 +03:00
|
|
|
/// RPC responses are cached locally
|
2023-07-06 13:51:39 +03:00
|
|
|
#[serde_inline_default(10u64.pow(8))]
|
2023-02-09 22:55:39 +03:00
|
|
|
pub response_cache_max_bytes: u64,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
2022-08-12 22:07:14 +03:00
|
|
|
/// the stats page url for an anonymous user.
|
2022-10-18 00:47:58 +03:00
|
|
|
pub redirect_public_url: Option<String>,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
2022-11-11 01:17:22 +03:00
|
|
|
/// the stats page url for a logged in user. if set, must contain "{rpc_key_id}"
|
2022-11-08 22:58:11 +03:00
|
|
|
pub redirect_rpc_key_url: Option<String>,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
2022-10-25 07:12:24 +03:00
|
|
|
/// Optionally send errors to <https://sentry.io>
|
2023-06-24 02:28:45 +03:00
|
|
|
pub sentry_url: Option<Dsn>,
|
2022-10-25 07:34:24 +03:00
|
|
|
|
2023-07-05 21:23:16 +03:00
|
|
|
/// Stripe api key for checking validity of webhooks
|
|
|
|
pub stripe_whsec_key: Option<String>,
|
2023-06-30 00:32:05 +03:00
|
|
|
|
2023-07-12 10:35:07 +03:00
|
|
|
pub usd_per_cu: Option<Decimal>,
|
|
|
|
|
2022-10-25 07:34:24 +03:00
|
|
|
/// Track rate limits in a redis (or compatible backend)
|
|
|
|
/// It is okay if this data is lost.
|
|
|
|
pub volatile_redis_url: Option<String>,
|
|
|
|
|
|
|
|
/// maximum size of the connection pool for the cache
|
2023-07-17 23:31:41 +03:00
|
|
|
/// If none, workers * 2 is used
|
2022-10-25 07:34:24 +03:00
|
|
|
pub volatile_redis_max_connections: Option<usize>,
|
2022-12-28 19:36:22 +03:00
|
|
|
|
2023-01-26 08:24:09 +03:00
|
|
|
/// influxdb host for stats
|
|
|
|
pub influxdb_host: Option<String>,
|
|
|
|
|
|
|
|
/// influxdb org for stats
|
|
|
|
pub influxdb_org: Option<String>,
|
|
|
|
|
|
|
|
/// influxdb token for stats
|
|
|
|
pub influxdb_token: Option<String>,
|
|
|
|
|
2023-03-10 22:26:15 +03:00
|
|
|
/// influxdb bucket to use for stats
|
|
|
|
pub influxdb_bucket: Option<String>,
|
|
|
|
|
2023-08-03 21:54:50 +03:00
|
|
|
/// unique_id keeps stats from different servers being seen as duplicates of each other.
|
2023-07-26 07:44:48 +03:00
|
|
|
/// this int is used as part of the "nanoseconds" part of the influx timestamp.
|
2023-08-03 21:54:50 +03:00
|
|
|
/// it can also be used by the rate limiter.
|
2023-07-26 07:44:48 +03:00
|
|
|
///
|
|
|
|
/// This **MUST** be set to a unique value for each running server.
|
2023-08-03 21:54:50 +03:00
|
|
|
/// If not set, severs will overwrite eachother's stats!
|
2023-07-26 07:44:48 +03:00
|
|
|
///
|
|
|
|
/// <https://docs.influxdata.com/influxdb/v2.0/write-data/best-practices/duplicate-points/#increment-the-timestamp>
|
|
|
|
#[serde_inline_default(0i64)]
|
2023-08-03 21:54:50 +03:00
|
|
|
pub unique_id: i64,
|
2023-07-22 10:26:02 +03:00
|
|
|
|
2022-12-28 19:36:22 +03:00
|
|
|
/// unknown config options get put here
|
2022-12-29 00:50:34 +03:00
|
|
|
#[serde(flatten, default = "HashMap::default")]
|
2022-12-28 19:36:22 +03:00
|
|
|
pub extra: HashMap<String, serde_json::Value>,
|
2022-07-22 22:30:39 +03:00
|
|
|
}
|
|
|
|
|
2023-07-06 14:02:30 +03:00
|
|
|
impl Default for AppConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
serde_json::from_str("{}").unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-03 21:54:50 +03:00
|
|
|
impl AppConfig {
|
|
|
|
/// TODO: this should probably be part of Deserialize
|
|
|
|
fn clean(&mut self) {
|
|
|
|
if self.usd_per_cu.is_none() {
|
|
|
|
self.usd_per_cu = Some(default_usd_per_cu(self.chain_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(influxdb_id) = self.extra.get("influxdb_id") {
|
|
|
|
self.unique_id = influxdb_id.as_i64().unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
if !self.extra.is_empty() {
|
|
|
|
warn!(
|
|
|
|
extra=?self.extra.keys(),
|
|
|
|
"unknown Web3ProxyAppConfig fields!",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-17 20:11:48 +03:00
|
|
|
/// TODO: we can't query a provider because we need this to create a provider
|
2023-09-19 01:21:02 +03:00
|
|
|
/// TODO: cache this
|
2023-06-17 20:11:48 +03:00
|
|
|
pub fn average_block_interval(chain_id: u64) -> Duration {
|
|
|
|
match chain_id {
|
|
|
|
// ethereum
|
|
|
|
1 => Duration::from_secs(12),
|
|
|
|
// ethereum-goerli
|
|
|
|
5 => Duration::from_secs(12),
|
2023-09-18 20:54:55 +03:00
|
|
|
// optimism
|
|
|
|
10 => Duration::from_secs(2),
|
2023-06-17 20:11:48 +03:00
|
|
|
// binance
|
|
|
|
56 => Duration::from_secs(3),
|
|
|
|
// polygon
|
|
|
|
137 => Duration::from_secs(2),
|
|
|
|
// fantom
|
|
|
|
250 => Duration::from_secs(1),
|
2023-09-19 01:21:02 +03:00
|
|
|
// zkevm polygon
|
|
|
|
1101 => Duration::from_secs(7),
|
2023-09-18 20:54:55 +03:00
|
|
|
// base
|
|
|
|
8453 => Duration::from_secs(2),
|
2023-06-17 20:11:48 +03:00
|
|
|
// arbitrum
|
|
|
|
42161 => Duration::from_millis(500),
|
|
|
|
// anything else
|
|
|
|
_ => {
|
|
|
|
let default = 10;
|
|
|
|
warn!(
|
|
|
|
"unknown chain_id ({}). defaulting average_block_interval to {} seconds",
|
|
|
|
chain_id, default
|
|
|
|
);
|
|
|
|
Duration::from_secs(default)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-13 00:59:13 +03:00
|
|
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
2023-09-12 23:35:51 +03:00
|
|
|
pub enum BlockDataLimit {
|
|
|
|
/// archive nodes can return all data
|
|
|
|
Archive,
|
|
|
|
/// prune nodes don't have all the data
|
|
|
|
/// some devs will argue about what "prune" means but we use it to mean that any of the data is gone.
|
2023-09-13 00:59:13 +03:00
|
|
|
/// TODO: this is too simple. erigon can prune the different types of data differently
|
|
|
|
Set(u64),
|
2023-09-12 23:35:51 +03:00
|
|
|
/// Automatically detect the limit
|
2023-09-13 00:59:13 +03:00
|
|
|
#[default]
|
2023-09-12 23:35:51 +03:00
|
|
|
Unknown,
|
|
|
|
}
|
|
|
|
|
2023-09-13 00:59:13 +03:00
|
|
|
impl<'de> Deserialize<'de> for BlockDataLimit {
|
|
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
|
|
where
|
|
|
|
D: Deserializer<'de>,
|
|
|
|
{
|
|
|
|
struct BlockDataLimitVisitor;
|
|
|
|
|
|
|
|
impl<'de> de::Visitor<'de> for BlockDataLimitVisitor {
|
|
|
|
type Value = BlockDataLimit;
|
|
|
|
|
|
|
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
formatter.write_str("a string 'archive', 'unknown' or an positive signed 64-bit integer. 0 means automatically detect")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
|
|
|
|
match value.to_ascii_lowercase().as_str() {
|
|
|
|
"archive" => Ok(BlockDataLimit::Archive),
|
|
|
|
"unknown" => Ok(BlockDataLimit::Unknown),
|
|
|
|
_ => Err(de::Error::custom(format!("Unexpected value {}", value))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_i64<E: de::Error>(self, v: i64) -> Result<Self::Value, E> {
|
|
|
|
if v < 0 {
|
|
|
|
Err(de::Error::custom("Negative values are not allowed"))
|
|
|
|
} else {
|
|
|
|
Ok(BlockDataLimit::Set(v as u64))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
deserializer.deserialize_any(BlockDataLimitVisitor)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-12 23:35:51 +03:00
|
|
|
impl From<BlockDataLimit> for AtomicU64 {
|
|
|
|
fn from(value: BlockDataLimit) -> Self {
|
|
|
|
match value {
|
|
|
|
BlockDataLimit::Archive => AtomicU64::new(u64::MAX),
|
2023-09-13 00:59:13 +03:00
|
|
|
BlockDataLimit::Set(limit) => AtomicU64::new(limit),
|
2023-09-12 23:35:51 +03:00
|
|
|
BlockDataLimit::Unknown => AtomicU64::new(0),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-19 02:27:33 +03:00
|
|
|
/// Configuration for a backend web3 RPC server
|
2023-07-06 13:51:39 +03:00
|
|
|
#[serde_inline_default]
|
|
|
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
2023-02-06 20:55:27 +03:00
|
|
|
pub struct Web3RpcConfig {
|
2022-10-19 02:27:33 +03:00
|
|
|
/// simple way to disable a connection without deleting the row
|
2023-07-06 13:51:39 +03:00
|
|
|
#[serde(default = "Default::default")]
|
2022-10-19 02:27:33 +03:00
|
|
|
pub disabled: bool,
|
2022-11-14 00:05:37 +03:00
|
|
|
/// a name used in /status and other user facing messages
|
|
|
|
pub display_name: Option<String>,
|
2023-02-12 12:22:53 +03:00
|
|
|
/// while not absolutely required, a ws:// or wss:// connection will be able to subscribe to head blocks
|
|
|
|
pub ws_url: Option<String>,
|
|
|
|
/// while not absolutely required, a http:// or https:// connection will allow erigon to stream JSON
|
|
|
|
pub http_url: Option<String>,
|
2022-11-25 03:45:13 +03:00
|
|
|
/// block data limit. If None, will be queried
|
2023-09-12 23:35:51 +03:00
|
|
|
#[serde(default = "Default::default")]
|
|
|
|
pub block_data_limit: BlockDataLimit,
|
2022-10-07 05:15:53 +03:00
|
|
|
/// the requests per second at which the server starts slowing down
|
2023-07-06 13:51:39 +03:00
|
|
|
#[serde_inline_default(1u32)]
|
2022-10-19 02:27:33 +03:00
|
|
|
pub soft_limit: u32,
|
2023-08-03 21:54:50 +03:00
|
|
|
/// the requests per period at which the server throws errors (rate limit or otherwise)
|
2022-10-19 02:27:33 +03:00
|
|
|
pub hard_limit: Option<u64>,
|
2023-08-03 21:54:50 +03:00
|
|
|
/// the number of seconds in a rate limiting period
|
|
|
|
/// some providers allow burst limits and rolling windows, but coding that is a lot more complicated
|
|
|
|
#[serde_inline_default(1u32)]
|
|
|
|
pub hard_limit_period: u32,
|
|
|
|
/// if hard limits are applied per server or per endpoint. default is per server
|
|
|
|
#[serde(default = "Default::default")]
|
|
|
|
pub hard_limit_per_endpoint: bool,
|
2023-01-19 13:13:00 +03:00
|
|
|
/// only use this rpc if everything else is lagging too far. this allows us to ignore fast but very low limit rpcs
|
2023-07-06 13:51:39 +03:00
|
|
|
#[serde(default = "Default::default")]
|
2023-02-12 12:22:53 +03:00
|
|
|
pub backup: bool,
|
2022-10-07 05:15:53 +03:00
|
|
|
/// Subscribe to the firehose of pending transactions
|
|
|
|
/// Don't do this with free rpcs
|
2023-07-06 13:51:39 +03:00
|
|
|
#[serde(default = "Default::default")]
|
2023-02-12 12:22:53 +03:00
|
|
|
pub subscribe_txs: bool,
|
2022-12-28 19:36:22 +03:00
|
|
|
/// unknown config options get put here
|
2022-12-29 00:50:34 +03:00
|
|
|
#[serde(flatten, default = "HashMap::default")]
|
2022-12-28 19:36:22 +03:00
|
|
|
pub extra: HashMap<String, serde_json::Value>,
|
2022-05-05 22:07:09 +03:00
|
|
|
}
|
|
|
|
|
2023-07-06 14:02:30 +03:00
|
|
|
impl Default for Web3RpcConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
serde_json::from_str("{}").unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-06 20:55:27 +03:00
|
|
|
impl Web3RpcConfig {
|
|
|
|
/// Create a Web3Rpc from config
|
|
|
|
/// TODO: move this into Web3Rpc? (just need to make things pub(crate))
|
2022-08-10 08:56:09 +03:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2022-06-14 08:43:28 +03:00
|
|
|
pub async fn spawn(
|
2022-05-05 22:07:09 +03:00
|
|
|
self,
|
2022-08-10 08:56:09 +03:00
|
|
|
name: String,
|
2022-09-15 20:57:24 +03:00
|
|
|
redis_pool: Option<redis_rate_limiter::RedisPool>,
|
2023-08-03 21:54:50 +03:00
|
|
|
server_id: i64,
|
2022-07-19 07:21:32 +03:00
|
|
|
chain_id: u64,
|
2023-06-17 20:11:48 +03:00
|
|
|
block_interval: Duration,
|
2022-07-19 07:21:32 +03:00
|
|
|
http_client: Option<reqwest::Client>,
|
2023-02-26 10:52:33 +03:00
|
|
|
blocks_by_hash_cache: BlocksByHashCache,
|
2023-08-16 23:20:22 +03:00
|
|
|
block_and_rpc_sender: Option<mpsc::UnboundedSender<BlockAndRpc>>,
|
2023-09-13 22:05:47 +03:00
|
|
|
pending_txid_firehouse_sender: Option<mpsc::Sender<TxHash>>,
|
2023-07-11 08:17:15 +03:00
|
|
|
max_head_block_age: Duration,
|
2023-05-24 00:40:34 +03:00
|
|
|
) -> anyhow::Result<(Arc<Web3Rpc>, Web3ProxyJoinHandle<()>)> {
|
2022-12-28 19:36:22 +03:00
|
|
|
if !self.extra.is_empty() {
|
2023-08-03 21:54:50 +03:00
|
|
|
// TODO: move this to a `clean` function
|
2023-06-29 07:30:00 +03:00
|
|
|
warn!(extra=?self.extra.keys(), "unknown Web3RpcConfig fields!");
|
2022-12-28 19:36:22 +03:00
|
|
|
}
|
|
|
|
|
2023-02-06 20:55:27 +03:00
|
|
|
Web3Rpc::spawn(
|
2023-02-12 12:22:53 +03:00
|
|
|
self,
|
2022-08-10 08:56:09 +03:00
|
|
|
name,
|
2022-05-12 21:49:57 +03:00
|
|
|
chain_id,
|
2022-05-05 22:07:09 +03:00
|
|
|
http_client,
|
2023-02-12 12:22:53 +03:00
|
|
|
redis_pool,
|
2023-08-03 21:54:50 +03:00
|
|
|
server_id,
|
2023-05-24 00:40:34 +03:00
|
|
|
block_interval,
|
2023-02-26 10:52:33 +03:00
|
|
|
blocks_by_hash_cache,
|
2023-08-16 23:20:22 +03:00
|
|
|
block_and_rpc_sender,
|
2023-09-13 22:05:47 +03:00
|
|
|
pending_txid_firehouse_sender,
|
2023-07-11 08:17:15 +03:00
|
|
|
max_head_block_age,
|
2022-05-05 22:07:09 +03:00
|
|
|
)
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
}
|
2023-07-06 13:51:39 +03:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2023-07-06 23:50:20 +03:00
|
|
|
use super::{AppConfig, Web3RpcConfig};
|
|
|
|
use serde_json::json;
|
2023-07-06 13:51:39 +03:00
|
|
|
|
|
|
|
#[test]
|
2023-07-06 14:04:12 +03:00
|
|
|
fn expected_app_defaults() {
|
2023-07-22 11:41:07 +03:00
|
|
|
// a is from serde
|
2023-07-06 23:50:20 +03:00
|
|
|
let a: AppConfig = serde_json::from_value(json!({
|
|
|
|
"chain_id": 1,
|
2023-07-10 06:13:03 +03:00
|
|
|
}))
|
|
|
|
.unwrap();
|
2023-07-06 23:50:20 +03:00
|
|
|
|
|
|
|
assert_eq!(a.min_synced_rpcs, 1);
|
2023-07-06 14:04:12 +03:00
|
|
|
|
2023-07-22 11:41:07 +03:00
|
|
|
// b is from Default
|
2023-07-26 07:44:48 +03:00
|
|
|
let b = AppConfig::default();
|
2023-07-06 14:04:12 +03:00
|
|
|
|
2023-07-06 23:50:20 +03:00
|
|
|
assert_eq!(b.min_synced_rpcs, 1);
|
|
|
|
|
2023-07-06 14:04:12 +03:00
|
|
|
assert_eq!(a, b);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn expected_rpc_defaults() {
|
2023-07-06 13:51:39 +03:00
|
|
|
let a: Web3RpcConfig = serde_json::from_str("{}").unwrap();
|
|
|
|
|
|
|
|
assert_eq!(a.soft_limit, 1);
|
2023-07-06 14:02:30 +03:00
|
|
|
|
|
|
|
let b: Web3RpcConfig = Default::default();
|
|
|
|
|
|
|
|
assert_eq!(b.soft_limit, 1);
|
|
|
|
|
|
|
|
assert_eq!(a, b);
|
2023-07-06 13:51:39 +03:00
|
|
|
}
|
|
|
|
}
|