load the redirected page from config
This commit is contained in:
parent
a04629641c
commit
c57c2249c0
65
Cargo.lock
generated
65
Cargo.lock
generated
@ -2162,6 +2162,20 @@ version = "1.8.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "handlebars"
|
||||||
|
version = "4.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "360d9740069b2f6cbb63ce2dbaa71a20d3185350cbb990d7bebeb9318415eb17"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"pest",
|
||||||
|
"pest_derive",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@ -3151,6 +3165,50 @@ version = "2.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest"
|
||||||
|
version = "2.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69486e2b8c2d2aeb9762db7b4e00b0331156393555cff467f4163ff06821eef8"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror",
|
||||||
|
"ucd-trie",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_derive"
|
||||||
|
version = "2.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b13570633aff33c6d22ce47dd566b10a3b9122c2fe9d8e7501895905be532b91"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
"pest_generator",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_generator"
|
||||||
|
version = "2.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3c567e5702efdc79fb18859ea74c3eb36e14c43da7b8c1f098a4ed6514ec7a0"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
"pest_meta",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_meta"
|
||||||
|
version = "2.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5eb32be5ee3bbdafa8c7a18b0a8a8d962b66cfa2ceee4037f49267a50ee821fe"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"pest",
|
||||||
|
"sha-1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "petgraph"
|
name = "petgraph"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
@ -4947,6 +5005,12 @@ version = "1.15.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ucd-trie"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uint"
|
name = "uint"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
@ -5220,6 +5284,7 @@ dependencies = [
|
|||||||
"flume",
|
"flume",
|
||||||
"fstrings",
|
"fstrings",
|
||||||
"futures",
|
"futures",
|
||||||
|
"handlebars",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"migration",
|
"migration",
|
||||||
|
@ -107,6 +107,6 @@ Test erigon (assuming it is on 8945):
|
|||||||
|
|
||||||
Note: Testing with `getLatestBlockByNumber.lua` is not great because the latest block changes and so one run is likely to be very different than another.
|
Note: Testing with `getLatestBlockByNumber.lua` is not great because the latest block changes and so one run is likely to be very different than another.
|
||||||
|
|
||||||
Run ethspam for a more realistic load test:
|
Run [ethspam](https://github.com/INFURA/versus) and [versus](https://github.com/shazow/ethspam) for a more realistic load test:
|
||||||
|
|
||||||
ethspam --rpc http://127.0.0.1:8544/u/someuserkey | versus --concurrency=100 --stop-after=10000 http://127.0.0.1:8544/u/someuserkey
|
ethspam --rpc http://127.0.0.1:8544/u/someuserkey | versus --concurrency=100 --stop-after=10000 http://127.0.0.1:8544/u/someuserkey
|
||||||
|
5
TODO.md
5
TODO.md
@ -70,9 +70,9 @@
|
|||||||
- [x] im seeing ethspam occasionally try to query a future block. something must be setting the head block too early
|
- [x] im seeing ethspam occasionally try to query a future block. something must be setting the head block too early
|
||||||
- [x] we were sorting best block the wrong direction. i flipped a.cmp(b) to b.cmp(a) so that the largest would be first, but then i used 'max_by' which looks at the end of the list
|
- [x] we were sorting best block the wrong direction. i flipped a.cmp(b) to b.cmp(a) so that the largest would be first, but then i used 'max_by' which looks at the end of the list
|
||||||
- [x] HTTP GET to the websocket endpoints should redirect instead of giving an ugly error
|
- [x] HTTP GET to the websocket endpoints should redirect instead of giving an ugly error
|
||||||
- [ ] load the redirected page from config
|
- [x] load the redirected page from config
|
||||||
- [ ] use config to decide if the app should use `into_make_service_with_connect_info` or `into_make_service`. with_connect_info is needed for getting the ip from docker
|
|
||||||
- [ ] basic request method stats
|
- [ ] basic request method stats
|
||||||
|
- [ ] prettier output for create_user command. need the key in hex
|
||||||
- [ ] use siwe messages and signatures for sign up and login
|
- [ ] use siwe messages and signatures for sign up and login
|
||||||
- [ ] fantom_1 | 2022-08-10T22:19:43.522465Z WARN web3_proxy::jsonrpc: forwarding error err=missing field `jsonrpc` at line 1 column 60
|
- [ ] fantom_1 | 2022-08-10T22:19:43.522465Z WARN web3_proxy::jsonrpc: forwarding error err=missing field `jsonrpc` at line 1 column 60
|
||||||
- [ ] i think the server isn't following the spec. we need a context attached to this error so we know which one
|
- [ ] i think the server isn't following the spec. we need a context attached to this error so we know which one
|
||||||
@ -247,3 +247,4 @@ in another repo: event subscriber
|
|||||||
eth_1 | 2022-08-10T23:26:10.195014Z WARN web3_proxy::connections: chain is forked! 262 possible heads. 1/2/5/5 rpcs have 0x0538…bfff
|
eth_1 | 2022-08-10T23:26:10.195014Z WARN web3_proxy::connections: chain is forked! 262 possible heads. 1/2/5/5 rpcs have 0x0538…bfff
|
||||||
eth_1 | 2022-08-10T23:26:10.195658Z WARN web3_proxy::connections: chain is forked! 262 possible heads. 2/3/5/5 rpcs have 0x0538…bfff
|
eth_1 | 2022-08-10T23:26:10.195658Z WARN web3_proxy::connections: chain is forked! 262 possible heads. 2/3/5/5 rpcs have 0x0538…bfff
|
||||||
- [ ] disable redis persistence in dev
|
- [ ] disable redis persistence in dev
|
||||||
|
- [ ] fix ip detection when running in dev
|
@ -1,4 +1,4 @@
|
|||||||
[shared]
|
[app]
|
||||||
chain_id = 1
|
chain_id = 1
|
||||||
public_rate_limit_per_minute = 60_000
|
public_rate_limit_per_minute = 60_000
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
chain_id = 1
|
chain_id = 1
|
||||||
db_url = "mysql://root:dev_web3_proxy@dev-db:3306/dev_web3_proxy"
|
db_url = "mysql://root:dev_web3_proxy@dev-db:3306/dev_web3_proxy"
|
||||||
redis_url = "redis://dev-redis:6379/"
|
redis_url = "redis://dev-redis:6379/"
|
||||||
|
redirect_public_url = "https://llamanodes.com/free-rpc-stats"
|
||||||
|
redirect_user_url = "https://llamanodes.com/user-rpc-stats/{{user_id}}"
|
||||||
public_rate_limit_per_minute = 0
|
public_rate_limit_per_minute = 0
|
||||||
# 1GB of cache
|
# 1GB of cache
|
||||||
response_cache_max_bytes = 10000000000
|
response_cache_max_bytes = 10000000000
|
||||||
|
@ -42,6 +42,7 @@ rand = "0.8.5"
|
|||||||
# TODO: regex has several "perf" features that we might want to use
|
# TODO: regex has several "perf" features that we might want to use
|
||||||
regex = "1.6.0"
|
regex = "1.6.0"
|
||||||
reqwest = { version = "0.11.11", default-features = false, features = ["json", "tokio-rustls"] }
|
reqwest = { version = "0.11.11", default-features = false, features = ["json", "tokio-rustls"] }
|
||||||
|
handlebars = "4.3.3"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
siwe = "0.4.1"
|
siwe = "0.4.1"
|
||||||
sea-orm = { version = "0.9.1", features = ["macros"] }
|
sea-orm = { version = "0.9.1", features = ["macros"] }
|
||||||
|
@ -34,7 +34,7 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use crate::bb8_helpers;
|
use crate::bb8_helpers;
|
||||||
use crate::block_helpers::block_needed;
|
use crate::block_helpers::block_needed;
|
||||||
use crate::config::AppConfig;
|
use crate::config::{AppConfig, TopConfig};
|
||||||
use crate::connections::Web3Connections;
|
use crate::connections::Web3Connections;
|
||||||
use crate::jsonrpc::JsonRpcForwardedResponse;
|
use crate::jsonrpc::JsonRpcForwardedResponse;
|
||||||
use crate::jsonrpc::JsonRpcForwardedResponseEnum;
|
use crate::jsonrpc::JsonRpcForwardedResponseEnum;
|
||||||
@ -139,6 +139,7 @@ pub struct Web3ProxyApp {
|
|||||||
// TODO: broadcast channel instead?
|
// TODO: broadcast channel instead?
|
||||||
head_block_receiver: watch::Receiver<Arc<Block<TxHash>>>,
|
head_block_receiver: watch::Receiver<Arc<Block<TxHash>>>,
|
||||||
pending_tx_sender: broadcast::Sender<TxState>,
|
pending_tx_sender: broadcast::Sender<TxState>,
|
||||||
|
pub config: AppConfig,
|
||||||
pub pending_transactions: Arc<DashMap<TxHash, TxState>>,
|
pub pending_transactions: Arc<DashMap<TxHash, TxState>>,
|
||||||
pub user_cache: RwLock<FifoCountMap<Uuid, UserCacheValue>>,
|
pub user_cache: RwLock<FifoCountMap<Uuid, UserCacheValue>>,
|
||||||
pub redis_pool: Option<RedisPool>,
|
pub redis_pool: Option<RedisPool>,
|
||||||
@ -156,17 +157,23 @@ impl fmt::Debug for Web3ProxyApp {
|
|||||||
impl Web3ProxyApp {
|
impl Web3ProxyApp {
|
||||||
// TODO: should we just take the rpc config as the only arg instead?
|
// TODO: should we just take the rpc config as the only arg instead?
|
||||||
pub async fn spawn(
|
pub async fn spawn(
|
||||||
app_config: AppConfig,
|
top_config: TopConfig,
|
||||||
num_workers: usize,
|
num_workers: usize,
|
||||||
) -> anyhow::Result<(
|
) -> anyhow::Result<(
|
||||||
Arc<Web3ProxyApp>,
|
Arc<Web3ProxyApp>,
|
||||||
Pin<Box<dyn Future<Output = anyhow::Result<()>>>>,
|
Pin<Box<dyn Future<Output = anyhow::Result<()>>>>,
|
||||||
)> {
|
)> {
|
||||||
|
// safety checks on the config
|
||||||
|
assert!(
|
||||||
|
top_config.app.redirect_user_url.contains("{{user_id}}"),
|
||||||
|
"redirect user url must contain \"{{user_id}}\""
|
||||||
|
);
|
||||||
|
|
||||||
// first, we connect to mysql and make sure the latest migrations have run
|
// first, we connect to mysql and make sure the latest migrations have run
|
||||||
let db_conn = if let Some(db_url) = app_config.shared.db_url {
|
let db_conn = if let Some(db_url) = &top_config.app.db_url {
|
||||||
let max_connections = num_workers.try_into()?;
|
let max_connections = num_workers.try_into()?;
|
||||||
|
|
||||||
let db = get_migrated_db(db_url, max_connections).await?;
|
let db = get_migrated_db(db_url.clone(), max_connections).await?;
|
||||||
|
|
||||||
Some(db)
|
Some(db)
|
||||||
} else {
|
} else {
|
||||||
@ -174,9 +181,9 @@ impl Web3ProxyApp {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let balanced_rpcs = app_config.balanced_rpcs;
|
let balanced_rpcs = top_config.balanced_rpcs;
|
||||||
|
|
||||||
let private_rpcs = if let Some(private_rpcs) = app_config.private_rpcs {
|
let private_rpcs = if let Some(private_rpcs) = top_config.private_rpcs {
|
||||||
private_rpcs
|
private_rpcs
|
||||||
} else {
|
} else {
|
||||||
Default::default()
|
Default::default()
|
||||||
@ -196,11 +203,11 @@ impl Web3ProxyApp {
|
|||||||
.build()?,
|
.build()?,
|
||||||
);
|
);
|
||||||
|
|
||||||
let redis_pool = match app_config.shared.redis_url {
|
let redis_pool = match top_config.app.redis_url.as_ref() {
|
||||||
Some(redis_url) => {
|
Some(redis_url) => {
|
||||||
info!("Connecting to redis on {}", redis_url);
|
info!("Connecting to redis on {}", redis_url);
|
||||||
|
|
||||||
let manager = RedisConnectionManager::new(redis_url)?;
|
let manager = RedisConnectionManager::new(redis_url.as_ref())?;
|
||||||
|
|
||||||
let min_size = num_workers as u32;
|
let min_size = num_workers as u32;
|
||||||
let max_size = min_size * 4;
|
let max_size = min_size * 4;
|
||||||
@ -236,9 +243,8 @@ impl Web3ProxyApp {
|
|||||||
// TODO: once a transaction is "Confirmed" we remove it from the map. this should prevent major memory leaks.
|
// TODO: once a transaction is "Confirmed" we remove it from the map. this should prevent major memory leaks.
|
||||||
// TODO: we should still have some sort of expiration or maximum size limit for the map
|
// TODO: we should still have some sort of expiration or maximum size limit for the map
|
||||||
|
|
||||||
// TODO: attach context to this error
|
|
||||||
let (balanced_rpcs, balanced_handle) = Web3Connections::spawn(
|
let (balanced_rpcs, balanced_handle) = Web3Connections::spawn(
|
||||||
app_config.shared.chain_id,
|
top_config.app.chain_id,
|
||||||
balanced_rpcs,
|
balanced_rpcs,
|
||||||
http_client.clone(),
|
http_client.clone(),
|
||||||
redis_pool.clone(),
|
redis_pool.clone(),
|
||||||
@ -257,7 +263,7 @@ impl Web3ProxyApp {
|
|||||||
} else {
|
} else {
|
||||||
// TODO: attach context to this error
|
// TODO: attach context to this error
|
||||||
let (private_rpcs, private_handle) = Web3Connections::spawn(
|
let (private_rpcs, private_handle) = Web3Connections::spawn(
|
||||||
app_config.shared.chain_id,
|
top_config.app.chain_id,
|
||||||
private_rpcs,
|
private_rpcs,
|
||||||
http_client.clone(),
|
http_client.clone(),
|
||||||
redis_pool.clone(),
|
redis_pool.clone(),
|
||||||
@ -276,7 +282,8 @@ impl Web3ProxyApp {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TODO: how much should we allow?
|
// TODO: how much should we allow?
|
||||||
let public_max_burst = app_config.shared.public_rate_limit_per_minute / 3;
|
// TODO: im seeing errors in redis. just use the redis FAQ on rate limiting. its really simple
|
||||||
|
let public_max_burst = top_config.app.public_rate_limit_per_minute / 3;
|
||||||
|
|
||||||
let frontend_rate_limiter = redis_pool.as_ref().map(|redis_pool| {
|
let frontend_rate_limiter = redis_pool.as_ref().map(|redis_pool| {
|
||||||
RedisCell::new(
|
RedisCell::new(
|
||||||
@ -284,20 +291,21 @@ impl Web3ProxyApp {
|
|||||||
"web3_proxy",
|
"web3_proxy",
|
||||||
"frontend",
|
"frontend",
|
||||||
public_max_burst,
|
public_max_burst,
|
||||||
app_config.shared.public_rate_limit_per_minute,
|
top_config.app.public_rate_limit_per_minute,
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// keep the borrow checker happy
|
||||||
|
let response_cache_max_bytes = top_config.app.response_cache_max_bytes;
|
||||||
|
|
||||||
let app = Self {
|
let app = Self {
|
||||||
|
config: top_config.app,
|
||||||
balanced_rpcs,
|
balanced_rpcs,
|
||||||
private_rpcs,
|
private_rpcs,
|
||||||
active_requests: Default::default(),
|
active_requests: Default::default(),
|
||||||
// TODO: make the share configurable
|
// TODO: make the share configurable
|
||||||
response_cache: RwLock::new(FifoSizedMap::new(
|
response_cache: RwLock::new(FifoSizedMap::new(response_cache_max_bytes, 100)),
|
||||||
app_config.shared.response_cache_max_bytes,
|
|
||||||
100,
|
|
||||||
)),
|
|
||||||
head_block_receiver,
|
head_block_receiver,
|
||||||
pending_tx_sender,
|
pending_tx_sender,
|
||||||
pending_transactions,
|
pending_transactions,
|
||||||
|
@ -17,15 +17,15 @@ use tokio::time::Duration;
|
|||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
use web3_proxy::app::{flatten_handle, Web3ProxyApp};
|
use web3_proxy::app::{flatten_handle, Web3ProxyApp};
|
||||||
use web3_proxy::config::{AppConfig, CliConfig};
|
use web3_proxy::config::{CliConfig, TopConfig};
|
||||||
use web3_proxy::frontend;
|
use web3_proxy::frontend;
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
shutdown_receiver: flume::Receiver<()>,
|
shutdown_receiver: flume::Receiver<()>,
|
||||||
cli_config: CliConfig,
|
cli_config: CliConfig,
|
||||||
app_config: AppConfig,
|
top_config: TopConfig,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
debug!(?cli_config, ?app_config);
|
debug!(?cli_config, ?top_config);
|
||||||
|
|
||||||
// spawn a thread for deadlock detection
|
// spawn a thread for deadlock detection
|
||||||
thread::spawn(move || loop {
|
thread::spawn(move || loop {
|
||||||
@ -48,7 +48,7 @@ fn run(
|
|||||||
// set up tokio's async runtime
|
// set up tokio's async runtime
|
||||||
let mut rt_builder = runtime::Builder::new_multi_thread();
|
let mut rt_builder = runtime::Builder::new_multi_thread();
|
||||||
|
|
||||||
let chain_id = app_config.shared.chain_id;
|
let chain_id = top_config.app.chain_id;
|
||||||
rt_builder.enable_all().thread_name_fn(move || {
|
rt_builder.enable_all().thread_name_fn(move || {
|
||||||
static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0);
|
static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0);
|
||||||
// TODO: what ordering? i think we want seqcst so that these all happen in order, but that might be stricter than we really need
|
// TODO: what ordering? i think we want seqcst so that these all happen in order, but that might be stricter than we really need
|
||||||
@ -70,7 +70,7 @@ fn run(
|
|||||||
debug!(?num_workers);
|
debug!(?num_workers);
|
||||||
|
|
||||||
rt.block_on(async {
|
rt.block_on(async {
|
||||||
let (app, app_handle) = Web3ProxyApp::spawn(app_config, num_workers).await?;
|
let (app, app_handle) = Web3ProxyApp::spawn(top_config, num_workers).await?;
|
||||||
|
|
||||||
let frontend_handle = tokio::spawn(frontend::serve(cli_config.port, app));
|
let frontend_handle = tokio::spawn(frontend::serve(cli_config.port, app));
|
||||||
|
|
||||||
@ -128,15 +128,17 @@ fn main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
// advanced configuration is on disk
|
// advanced configuration is on disk
|
||||||
info!("Loading rpc config @ {}", cli_config.config);
|
info!("Loading rpc config @ {}", cli_config.config);
|
||||||
let app_config: String = fs::read_to_string(cli_config.config.clone())?;
|
let top_config: String = fs::read_to_string(cli_config.config.clone())?;
|
||||||
let app_config: AppConfig = toml::from_str(&app_config)?;
|
let top_config: TopConfig = toml::from_str(&top_config)?;
|
||||||
|
|
||||||
// TODO: this doesn't seem to do anything
|
// TODO: this doesn't seem to do anything
|
||||||
proctitle::set_title(format!("web3_proxy-{}", app_config.shared.chain_id));
|
proctitle::set_title(format!("web3_proxy-{}", top_config.app.chain_id));
|
||||||
|
|
||||||
|
// tokio has code for catching ctrl+c so we use that
|
||||||
|
// this shutdown sender is currently only used in tests, but we might make a /shutdown endpoint or something
|
||||||
let (_shutdown_sender, shutdown_receiver) = flume::bounded(1);
|
let (_shutdown_sender, shutdown_receiver) = flume::bounded(1);
|
||||||
|
|
||||||
run(shutdown_receiver, cli_config, app_config)
|
run(shutdown_receiver, cli_config, top_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -148,7 +150,7 @@ mod tests {
|
|||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use web3_proxy::config::{RpcSharedConfig, Web3ConnectionConfig};
|
use web3_proxy::config::{AppConfig, Web3ConnectionConfig};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -190,13 +192,15 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// make a test AppConfig
|
// make a test AppConfig
|
||||||
let app_config = AppConfig {
|
let app_config = TopConfig {
|
||||||
shared: RpcSharedConfig {
|
app: AppConfig {
|
||||||
chain_id: 31337,
|
chain_id: 31337,
|
||||||
db_url: None,
|
db_url: None,
|
||||||
redis_url: None,
|
redis_url: None,
|
||||||
public_rate_limit_per_minute: 0,
|
public_rate_limit_per_minute: 0,
|
||||||
response_cache_max_bytes: 10_usize.pow(7),
|
response_cache_max_bytes: 10_usize.pow(7),
|
||||||
|
redirect_public_url: "example.com/".to_string(),
|
||||||
|
redirect_user_url: "example.com/users/{user_address}".to_string(),
|
||||||
},
|
},
|
||||||
balanced_rpcs: HashMap::from([
|
balanced_rpcs: HashMap::from([
|
||||||
(
|
(
|
||||||
|
@ -28,15 +28,15 @@ pub struct CliConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct AppConfig {
|
pub struct TopConfig {
|
||||||
pub shared: RpcSharedConfig,
|
pub app: AppConfig,
|
||||||
pub balanced_rpcs: HashMap<String, Web3ConnectionConfig>,
|
pub balanced_rpcs: HashMap<String, Web3ConnectionConfig>,
|
||||||
pub private_rpcs: Option<HashMap<String, Web3ConnectionConfig>>,
|
pub private_rpcs: Option<HashMap<String, Web3ConnectionConfig>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// shared configuration between Web3Connections
|
/// shared configuration between Web3Connections
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct RpcSharedConfig {
|
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 db_url: Option<String>,
|
pub db_url: Option<String>,
|
||||||
@ -45,6 +45,10 @@ pub struct RpcSharedConfig {
|
|||||||
pub public_rate_limit_per_minute: u32,
|
pub public_rate_limit_per_minute: u32,
|
||||||
#[serde(default = "default_response_cache_max_bytes")]
|
#[serde(default = "default_response_cache_max_bytes")]
|
||||||
pub response_cache_max_bytes: usize,
|
pub response_cache_max_bytes: usize,
|
||||||
|
/// the stats page url for an anonymous user.
|
||||||
|
pub redirect_public_url: String,
|
||||||
|
/// the stats page url for a logged in user. it must contain "{user_id}"
|
||||||
|
pub redirect_user_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_public_rate_limit_per_minute() -> u32 {
|
fn default_public_rate_limit_per_minute() -> u32 {
|
||||||
|
@ -182,6 +182,7 @@ impl Web3Connections {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// turn configs into connections (in parallel)
|
// turn configs into connections (in parallel)
|
||||||
|
// TODO: move this into a helper function. then we can use it when configs change (will need a remove function too)
|
||||||
let spawn_handles: Vec<_> = server_configs
|
let spawn_handles: Vec<_> = server_configs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(server_name, server_config)| {
|
.map(|(server_name, server_config)| {
|
||||||
|
@ -106,6 +106,7 @@ impl Web3ProxyApp {
|
|||||||
UserId,
|
UserId,
|
||||||
RequestsPerMinute,
|
RequestsPerMinute,
|
||||||
}
|
}
|
||||||
|
// TODO: join the user table to this to return the User? we don't always need it
|
||||||
let user_data = match user_keys::Entity::find()
|
let user_data = match user_keys::Entity::find()
|
||||||
.select_only()
|
.select_only()
|
||||||
.column_as(user_keys::Column::UserId, QueryAs::UserId)
|
.column_as(user_keys::Column::UserId, QueryAs::UserId)
|
||||||
|
@ -5,12 +5,12 @@ use axum::{
|
|||||||
Extension,
|
Extension,
|
||||||
};
|
};
|
||||||
use axum_client_ip::ClientIp;
|
use axum_client_ip::ClientIp;
|
||||||
use fstrings::{format_args_f, format_f};
|
|
||||||
use futures::SinkExt;
|
use futures::SinkExt;
|
||||||
use futures::{
|
use futures::{
|
||||||
future::AbortHandle,
|
future::AbortHandle,
|
||||||
stream::{SplitSink, SplitStream, StreamExt},
|
stream::{SplitSink, SplitStream, StreamExt},
|
||||||
};
|
};
|
||||||
|
use handlebars::Handlebars;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use serde_json::{json, value::RawValue};
|
use serde_json::{json, value::RawValue};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -30,7 +30,7 @@ pub async fn public_websocket_handler(
|
|||||||
ClientIp(ip): ClientIp,
|
ClientIp(ip): ClientIp,
|
||||||
ws_upgrade: Option<WebSocketUpgrade>,
|
ws_upgrade: Option<WebSocketUpgrade>,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
let ip = match app.rate_limit_by_ip(ip).await {
|
let _ip = match app.rate_limit_by_ip(ip).await {
|
||||||
Ok(x) => match x.try_into_response().await {
|
Ok(x) => match x.try_into_response().await {
|
||||||
Ok(RateLimitResult::AllowedIp(x)) => x,
|
Ok(RateLimitResult::AllowedIp(x)) => x,
|
||||||
Err(err_response) => return err_response,
|
Err(err_response) => return err_response,
|
||||||
@ -44,12 +44,8 @@ pub async fn public_websocket_handler(
|
|||||||
.on_upgrade(|socket| proxy_web3_socket(app, socket))
|
.on_upgrade(|socket| proxy_web3_socket(app, socket))
|
||||||
.into_response(),
|
.into_response(),
|
||||||
None => {
|
None => {
|
||||||
// this is not a websocket. give a friendly page. maybe redirect to the llama nodes home
|
// this is not a websocket. redirect to a friendly page
|
||||||
// TODO: redirect to a configurable url
|
Redirect::to(&app.config.redirect_public_url).into_response()
|
||||||
Redirect::to(&format_f!(
|
|
||||||
"https://llamanodes.com/free-rpc-stats#userip={ip}"
|
|
||||||
))
|
|
||||||
.into_response()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,13 +68,20 @@ pub async fn user_websocket_handler(
|
|||||||
match ws_upgrade {
|
match ws_upgrade {
|
||||||
Some(ws_upgrade) => ws_upgrade.on_upgrade(|socket| proxy_web3_socket(app, socket)),
|
Some(ws_upgrade) => ws_upgrade.on_upgrade(|socket| proxy_web3_socket(app, socket)),
|
||||||
None => {
|
None => {
|
||||||
// this is not a websocket. give a friendly page with stats for this user
|
// TODO: store this on the app and use register_template?
|
||||||
// TODO: redirect to a configurable url
|
let reg = Handlebars::new();
|
||||||
|
|
||||||
|
// TODO: show the user's address, not their id (remember to update the checks for {{user_id}} in app.rs)
|
||||||
// TODO: query to get the user's address. expose that instead of user_id
|
// TODO: query to get the user's address. expose that instead of user_id
|
||||||
Redirect::to(&format_f!(
|
let user_url = reg
|
||||||
"https://llamanodes.com/user-rpc-stats#user_id={user_id}"
|
.render_template(
|
||||||
))
|
&app.config.redirect_user_url,
|
||||||
.into_response()
|
&json!({ "user_id": user_id }),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// this is not a websocket. redirect to a page for this user
|
||||||
|
Redirect::to(&user_url).into_response()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user