web3-proxy/web3_proxy/src/config.rs

339 lines
11 KiB
Rust
Raw Normal View History

2023-05-24 00:40:34 +03:00
use crate::app::Web3ProxyJoinHandle;
use crate::rpcs::blockchain::{BlocksByHashCache, Web3ProxyBlock};
use crate::rpcs::one::Web3Rpc;
2022-05-06 00:25:40 +03:00
use argh::FromArgs;
use ethers::prelude::{Address, TxHash};
use ethers::types::{U256, U64};
2022-07-23 02:26:04 +03:00
use hashbrown::HashMap;
More balance tests (#182) * fix popularity contest * more info in the Debug for Web3Rpc * add frontend_requests and cache_misses to the Balance query * add more to balance and stats flushing and improved test coverage * it compiles * deserializer for Ulid to Uuid I think a wrapper type on Ulid that implements sea_orm::Value is probably better * rename variable to match struct name * add deserializer for Address -> Vec<u8> * sql sum returns a Decimal. need to convert to u64 * assert more * one log and assert more * log more * use a helper to get the user's rpc provider * this should be 2 now that we have a public and authed call * this should be zero. the public has the cache miss * instrument cu calcs * trace the value we took, not the default that replaced it * move usd_per_chain into config * remove some extra logging * use Arc::into_inner to maybe avoid a race * off by 1 * pass paid credits used instead of returning it this lets us use it to write to our user balance cache first. importantly, this keeps us from holding a write lock while writing to mysql * no cache misses expected in this test * actually check the admin * put the balance checks back now that the rest of the test works * archive request is being set incorrectly * wow howd we manage flipping the greater than sign on archive depth * move latest_balance and premium_credits_used to before any stats are emitted * lint * and build undoes the linting. fun i didnt even want to lint them in the first place, so this is fine * missed incrementing total_spent when not incrementing total_spent_paid_credits * use the credits on self * use the credits on self (pt 2) * fix type for 10 cu query * convert the requestmetadata on the other side of the channel * logs * viewing stats is allowed even without a balance * move paid_credits_used to AuthorizationChecks * wip * test_sum_credits_used finally passes * UserBalanceCache::get_or_insert * re-enable rpc_secret_key_cache * move invalidate to a helper function and always call it **after** the db is commited * fix PartialEq and Eq on RpcSecretKey * cargo upgrade
2023-07-12 10:35:07 +03:00
use migration::sea_orm::prelude::Decimal;
use sentry::types::Dsn;
2022-05-05 22:07:09 +03:00
use serde::Deserialize;
use serde_inline_default::serde_inline_default;
2022-05-05 22:07:09 +03:00
use std::sync::Arc;
2023-05-24 00:40:34 +03:00
use std::time::Duration;
use tokio::sync::mpsc;
use tracing::warn;
2022-05-05 22:07:09 +03:00
pub type BlockAndRpc = (Option<Web3ProxyBlock>, Arc<Web3Rpc>);
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 {
/// 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
/// 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,
/// 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
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
2022-08-12 22:07:14 +03:00
pub struct TopConfig {
pub app: AppConfig,
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
#[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
}
/// shared configuration between Web3Rpcs
2022-10-03 21:08:01 +03:00
// TODO: no String, only &str
#[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.
#[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
#[serde_inline_default(90_000u64)]
2023-02-06 04:58:03 +03:00
pub archive_depth: u64,
/// 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)]
pub chain_id: u64,
2022-10-25 00:07:29 +03:00
/// Cost per computational unit
// pub cost_per_cu: Decimal,
/// 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
/// 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
/// 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
/// 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>,
/// 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-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>,
/// 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>,
#[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
/// 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>,
2022-10-25 07:34:24 +03:00
/// Rate limit for the login entrypoint.
/// This is separate from the rpc limits.
#[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
/// The soft limit prevents thundering herds as new blocks are seen.
#[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
/// Another knob for preventing thundering herds as new blocks are seen.
#[serde_inline_default(1usize)]
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
/// 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>,
/// RPC responses are cached locally
#[serde_inline_default(10u64.pow(8))]
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}"
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>
pub sentry_url: Option<Dsn>,
2022-10-25 07:34:24 +03:00
/// Stripe api key for checking validity of webhooks
pub stripe_whsec_key: Option<String>,
More balance tests (#182) * fix popularity contest * more info in the Debug for Web3Rpc * add frontend_requests and cache_misses to the Balance query * add more to balance and stats flushing and improved test coverage * it compiles * deserializer for Ulid to Uuid I think a wrapper type on Ulid that implements sea_orm::Value is probably better * rename variable to match struct name * add deserializer for Address -> Vec<u8> * sql sum returns a Decimal. need to convert to u64 * assert more * one log and assert more * log more * use a helper to get the user's rpc provider * this should be 2 now that we have a public and authed call * this should be zero. the public has the cache miss * instrument cu calcs * trace the value we took, not the default that replaced it * move usd_per_chain into config * remove some extra logging * use Arc::into_inner to maybe avoid a race * off by 1 * pass paid credits used instead of returning it this lets us use it to write to our user balance cache first. importantly, this keeps us from holding a write lock while writing to mysql * no cache misses expected in this test * actually check the admin * put the balance checks back now that the rest of the test works * archive request is being set incorrectly * wow howd we manage flipping the greater than sign on archive depth * move latest_balance and premium_credits_used to before any stats are emitted * lint * and build undoes the linting. fun i didnt even want to lint them in the first place, so this is fine * missed incrementing total_spent when not incrementing total_spent_paid_credits * use the credits on self * use the credits on self (pt 2) * fix type for 10 cu query * convert the requestmetadata on the other side of the channel * logs * viewing stats is allowed even without a balance * move paid_credits_used to AuthorizationChecks * wip * test_sum_credits_used finally passes * UserBalanceCache::get_or_insert * re-enable rpc_secret_key_cache * move invalidate to a helper function and always call it **after** the db is commited * fix PartialEq and Eq on RpcSecretKey * cargo upgrade
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
/// If none, the minimum * 2 is used
pub volatile_redis_max_connections: Option<usize>,
2022-12-28 19:36:22 +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>,
/// influxdb bucket to use for stats
pub influxdb_bucket: Option<String>,
2022-12-28 19:36:22 +03:00
/// unknown config options get put here
#[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-06-17 20:11:48 +03:00
/// TODO: we can't query a provider because we need this to create a provider
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),
// binance
56 => Duration::from_secs(3),
// polygon
137 => Duration::from_secs(2),
// fantom
250 => Duration::from_secs(1),
// 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)
}
}
}
2022-10-19 02:27:33 +03:00
/// Configuration for a backend web3 RPC server
#[serde_inline_default]
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
pub struct Web3RpcConfig {
2022-10-19 02:27:33 +03:00
/// simple way to disable a connection without deleting the row
#[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>,
/// 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>,
/// block data limit. If None, will be queried
pub block_data_limit: Option<u64>,
/// the requests per second at which the server starts slowing down
#[serde_inline_default(1u32)]
2022-10-19 02:27:33 +03:00
pub soft_limit: u32,
/// 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>,
/// only use this rpc if everything else is lagging too far. this allows us to ignore fast but very low limit rpcs
#[serde(default = "Default::default")]
pub backup: bool,
/// Subscribe to the firehose of pending transactions
/// Don't do this with free rpcs
#[serde(default = "Default::default")]
pub subscribe_txs: bool,
2022-12-28 19:36:22 +03:00
/// unknown config options get put here
#[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()
}
}
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>,
chain_id: u64,
2023-06-17 20:11:48 +03:00
block_interval: Duration,
http_client: Option<reqwest::Client>,
blocks_by_hash_cache: BlocksByHashCache,
block_sender: Option<mpsc::UnboundedSender<BlockAndRpc>>,
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-06-29 07:30:00 +03:00
warn!(extra=?self.extra.keys(), "unknown Web3RpcConfig fields!");
2022-12-28 19:36:22 +03:00
}
Web3Rpc::spawn(
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,
redis_pool,
2023-05-24 00:40:34 +03:00
block_interval,
blocks_by_hash_cache,
2022-06-14 08:43:28 +03:00
block_sender,
max_head_block_age,
2022-05-05 22:07:09 +03:00
)
.await
}
}
#[cfg(test)]
mod tests {
2023-07-06 23:50:20 +03:00
use super::{AppConfig, Web3RpcConfig};
use serde_json::json;
#[test]
2023-07-06 14:04:12 +03:00
fn expected_app_defaults() {
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
let b: AppConfig = Default::default();
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() {
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);
}
}