2022-09-17 05:17:20 +03:00
|
|
|
use crate::rpcs::blockchain::BlockHashesCache;
|
2022-08-26 20:26:17 +03:00
|
|
|
use crate::rpcs::connection::Web3Connection;
|
2022-09-09 06:53:16 +03:00
|
|
|
use crate::rpcs::request::OpenRequestHandleMetrics;
|
2022-08-30 23:01:42 +03:00
|
|
|
use crate::{app::AnyhowJoinHandle, rpcs::blockchain::ArcBlock};
|
2022-05-06 00:25:40 +03:00
|
|
|
use argh::FromArgs;
|
2022-07-23 02:26:04 +03:00
|
|
|
use derive_more::Constructor;
|
2022-08-30 23:01:42 +03:00
|
|
|
use ethers::prelude::TxHash;
|
2022-07-23 02:26:04 +03:00
|
|
|
use hashbrown::HashMap;
|
2022-05-05 22:07:09 +03:00
|
|
|
use serde::Deserialize;
|
|
|
|
use std::sync::Arc;
|
2022-06-29 22:15:05 +03:00
|
|
|
use tokio::sync::broadcast;
|
2022-05-05 22:07:09 +03:00
|
|
|
|
2022-09-06 15:29:37 +03:00
|
|
|
pub type BlockAndRpc = (Option<ArcBlock>, Arc<Web3Connection>);
|
2022-08-30 23:01:42 +03:00
|
|
|
pub type TxHashAndRpc = (TxHash, Arc<Web3Connection>);
|
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
|
|
|
}
|
|
|
|
|
2022-05-22 02:34:05 +03:00
|
|
|
#[derive(Debug, Deserialize)]
|
2022-10-19 02:27:33 +03:00
|
|
|
#[serde(deny_unknown_fields)]
|
2022-08-12 22:07:14 +03:00
|
|
|
pub struct TopConfig {
|
|
|
|
pub app: AppConfig,
|
2022-05-13 23:50:11 +03:00
|
|
|
pub balanced_rpcs: HashMap<String, Web3ConnectionConfig>,
|
2022-05-06 01:21:27 +03:00
|
|
|
pub private_rpcs: Option<HashMap<String, Web3ConnectionConfig>>,
|
2022-05-05 22:07:09 +03:00
|
|
|
}
|
|
|
|
|
2022-05-12 21:49:57 +03:00
|
|
|
/// shared configuration between Web3Connections
|
2022-10-03 21:08:01 +03:00
|
|
|
// TODO: no String, only &str
|
2022-09-02 23:16:20 +03:00
|
|
|
#[derive(Debug, Default, Deserialize)]
|
2022-10-19 02:27:33 +03:00
|
|
|
#[serde(deny_unknown_fields)]
|
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.
|
2022-10-25 00:07:29 +03:00
|
|
|
pub allowed_origin_requests_per_minute: HashMap<String, u64>,
|
|
|
|
|
2022-10-07 05:15:53 +03:00
|
|
|
/// EVM chain id. 1 for ETH
|
|
|
|
/// TODO: better type for chain_id? max of `u64::MAX / 2 - 36` https://github.com/ethereum/EIPs/issues/2294
|
2022-07-19 07:21:32 +03:00
|
|
|
pub chain_id: u64,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
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-10-07 05:15:53 +03:00
|
|
|
/// Default request limit for registered users.
|
|
|
|
/// 0 = block all requests
|
|
|
|
/// None = allow all requests
|
2022-10-19 02:27:33 +03:00
|
|
|
pub default_user_requests_per_minute: Option<u64>,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
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>,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
2022-10-25 21:26:58 +03:00
|
|
|
/// Rate limit for bearer token authenticated entrypoints.
|
|
|
|
/// This is separate from the rpc limits.
|
|
|
|
#[serde(default = "default_bearer_token_max_concurrent_requests")]
|
|
|
|
pub bearer_token_max_concurrent_requests: u64,
|
|
|
|
|
2022-10-25 07:34:24 +03:00
|
|
|
/// Rate limit for the login entrypoint.
|
|
|
|
/// This is separate from the rpc limits.
|
|
|
|
#[serde(default = "default_login_rate_limit_per_minute")]
|
|
|
|
pub login_rate_limit_per_minute: u64,
|
|
|
|
|
2022-10-07 05:15:53 +03:00
|
|
|
/// The soft limit prevents thundering herds as new blocks are seen.
|
2022-08-27 06:11:58 +03:00
|
|
|
#[serde(default = "default_min_sum_soft_limit")]
|
|
|
|
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.
|
2022-08-27 03:33:45 +03:00
|
|
|
#[serde(default = "default_min_synced_rpcs")]
|
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
|
|
|
|
#[serde(default = "default_public_max_concurrent_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
|
|
|
|
#[serde(default = "default_public_requests_per_minute")]
|
|
|
|
pub public_requests_per_minute: Option<u64>,
|
2022-10-25 00:07:29 +03:00
|
|
|
|
2022-10-07 05:15:53 +03:00
|
|
|
/// RPC responses are cached locally
|
2022-07-22 22:30:39 +03:00
|
|
|
#[serde(default = "default_response_cache_max_bytes")]
|
|
|
|
pub response_cache_max_bytes: usize,
|
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-10-18 00:47:58 +03:00
|
|
|
/// the stats page url for a logged in user. if set, must contain "{user_id}"
|
|
|
|
pub redirect_user_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>
|
2022-10-25 00:07:29 +03:00
|
|
|
pub sentry_url: Option<String>,
|
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
|
|
|
|
/// If none, the minimum * 2 is used
|
|
|
|
pub volatile_redis_max_connections: Option<usize>,
|
2022-07-22 22:30:39 +03:00
|
|
|
}
|
|
|
|
|
2022-10-07 05:15:53 +03:00
|
|
|
/// This might cause a thundering herd!
|
2022-08-27 06:11:58 +03:00
|
|
|
fn default_min_sum_soft_limit() -> u32 {
|
2022-08-27 03:33:45 +03:00
|
|
|
1
|
|
|
|
}
|
|
|
|
|
2022-10-07 05:15:53 +03:00
|
|
|
/// Only require 1 server. This might cause a thundering herd!
|
2022-09-02 08:40:56 +03:00
|
|
|
fn default_min_synced_rpcs() -> usize {
|
2022-08-27 06:11:58 +03:00
|
|
|
1
|
|
|
|
}
|
|
|
|
|
2022-10-25 07:01:41 +03:00
|
|
|
/// 0 blocks anonymous requests.
|
|
|
|
/// None allows unlimited concurrent requests
|
|
|
|
// TODO: what is a reasonable default?
|
|
|
|
fn default_public_max_concurrent_requests() -> Option<usize> {
|
|
|
|
Some(5)
|
|
|
|
}
|
|
|
|
|
2022-09-24 06:59:21 +03:00
|
|
|
/// 0 blocks anonymous requests by default.
|
2022-10-19 02:27:33 +03:00
|
|
|
fn default_public_requests_per_minute() -> Option<u64> {
|
|
|
|
Some(0)
|
2022-07-22 22:30:39 +03:00
|
|
|
}
|
|
|
|
|
2022-10-25 21:26:58 +03:00
|
|
|
/// Having a low amount of concurrent requests for bearer tokens keeps us from hammering the database.
|
|
|
|
fn default_bearer_token_max_concurrent_requests() -> u64 {
|
|
|
|
2
|
|
|
|
}
|
|
|
|
|
2022-09-24 06:59:21 +03:00
|
|
|
/// Having a low amount of requests per minute for login is safest.
|
|
|
|
fn default_login_rate_limit_per_minute() -> u64 {
|
|
|
|
10
|
|
|
|
}
|
|
|
|
|
2022-07-22 22:30:39 +03:00
|
|
|
fn default_response_cache_max_bytes() -> usize {
|
|
|
|
// TODO: default to some percentage of the system?
|
|
|
|
// 100 megabytes
|
|
|
|
10_usize.pow(8)
|
2022-05-12 21:49:57 +03:00
|
|
|
}
|
|
|
|
|
2022-10-19 02:27:33 +03:00
|
|
|
/// Configuration for a backend web3 RPC server
|
2022-07-23 02:26:04 +03:00
|
|
|
#[derive(Debug, Deserialize, Constructor)]
|
2022-10-19 02:27:33 +03:00
|
|
|
#[serde(deny_unknown_fields)]
|
2022-05-05 22:07:09 +03:00
|
|
|
pub struct Web3ConnectionConfig {
|
2022-10-19 02:27:33 +03:00
|
|
|
/// simple way to disable a connection without deleting the row
|
|
|
|
#[serde(default)]
|
|
|
|
pub disabled: bool,
|
2022-10-07 05:15:53 +03:00
|
|
|
/// websocket (or http if no websocket)
|
2022-10-19 02:27:33 +03:00
|
|
|
pub url: String,
|
2022-10-07 05:15:53 +03:00
|
|
|
/// the requests per second at which the server starts slowing down
|
2022-10-19 02:27:33 +03:00
|
|
|
pub soft_limit: u32,
|
2022-10-07 05:15:53 +03:00
|
|
|
/// the requests per second at which the server throws errors (rate limit or otherwise)
|
2022-10-19 02:27:33 +03:00
|
|
|
pub hard_limit: Option<u64>,
|
2022-10-07 05:15:53 +03:00
|
|
|
/// All else equal, a server with a lower weight receives requests
|
2022-10-19 02:27:33 +03:00
|
|
|
pub weight: u32,
|
2022-10-07 05:15:53 +03:00
|
|
|
/// Subscribe to the firehose of pending transactions
|
|
|
|
/// Don't do this with free rpcs
|
2022-10-19 02:27:33 +03:00
|
|
|
#[serde(default)]
|
|
|
|
pub subscribe_txs: Option<bool>,
|
2022-05-05 22:07:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Web3ConnectionConfig {
|
2022-05-12 08:54:27 +03:00
|
|
|
/// Create a Web3Connection from config
|
2022-08-30 23:01:42 +03:00
|
|
|
/// TODO: move this into Web3Connection (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>,
|
2022-07-19 07:21:32 +03:00
|
|
|
chain_id: u64,
|
|
|
|
http_client: Option<reqwest::Client>,
|
2022-06-29 22:15:05 +03:00
|
|
|
http_interval_sender: Option<Arc<broadcast::Sender<()>>>,
|
2022-09-17 05:17:20 +03:00
|
|
|
block_map: BlockHashesCache,
|
2022-07-22 08:11:26 +03:00
|
|
|
block_sender: Option<flume::Sender<BlockAndRpc>>,
|
2022-08-30 23:01:42 +03:00
|
|
|
tx_id_sender: Option<flume::Sender<TxHashAndRpc>>,
|
2022-09-09 06:53:16 +03:00
|
|
|
open_request_handle_metrics: Arc<OpenRequestHandleMetrics>,
|
2022-06-14 08:43:28 +03:00
|
|
|
) -> anyhow::Result<(Arc<Web3Connection>, AnyhowJoinHandle<()>)> {
|
2022-08-16 01:50:56 +03:00
|
|
|
let hard_limit = match (self.hard_limit, redis_pool) {
|
2022-07-25 22:03:19 +03:00
|
|
|
(None, None) => None,
|
|
|
|
(Some(hard_limit), Some(redis_client_pool)) => Some((hard_limit, redis_client_pool)),
|
|
|
|
(None, Some(_)) => None,
|
2022-07-26 07:53:38 +03:00
|
|
|
(Some(_hard_limit), None) => {
|
2022-07-25 22:03:19 +03:00
|
|
|
return Err(anyhow::anyhow!(
|
|
|
|
"no redis client pool! needed for hard limit"
|
|
|
|
))
|
|
|
|
}
|
|
|
|
};
|
2022-05-22 02:34:05 +03:00
|
|
|
|
2022-09-02 23:16:20 +03:00
|
|
|
let tx_id_sender = if self.subscribe_txs.unwrap_or(false) {
|
|
|
|
tx_id_sender
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2022-06-14 07:04:14 +03:00
|
|
|
Web3Connection::spawn(
|
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
|
|
|
self.url,
|
|
|
|
http_client,
|
2022-06-29 22:15:05 +03:00
|
|
|
http_interval_sender,
|
2022-07-25 22:03:19 +03:00
|
|
|
hard_limit,
|
2022-05-05 22:07:09 +03:00
|
|
|
self.soft_limit,
|
2022-08-26 20:26:17 +03:00
|
|
|
block_map,
|
2022-06-14 08:43:28 +03:00
|
|
|
block_sender,
|
|
|
|
tx_id_sender,
|
2022-06-29 22:15:05 +03:00
|
|
|
true,
|
2022-08-08 22:57:54 +03:00
|
|
|
self.weight,
|
2022-09-09 06:53:16 +03:00
|
|
|
open_request_handle_metrics,
|
2022-05-05 22:07:09 +03:00
|
|
|
)
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
}
|