fix directory structure
This commit is contained in:
parent
7d632fe501
commit
e295307afc
26
Cargo.lock
generated
26
Cargo.lock
generated
@ -1875,6 +1875,28 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fstrings"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7845a0f15da505ac36baad0486612dab57f8b8d34e19c5470a265bbcdd572ae6"
|
||||
dependencies = [
|
||||
"fstrings-proc-macro",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fstrings-proc-macro"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63b58c0e7581dc33478a32299182cbe5ae3b8c028be26728a47fb0a113c92d9d"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-zircon"
|
||||
version = "0.3.3"
|
||||
@ -5176,7 +5198,6 @@ dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"argh",
|
||||
"async-std",
|
||||
"axum",
|
||||
"axum-client-ip",
|
||||
"counter",
|
||||
@ -5187,6 +5208,7 @@ dependencies = [
|
||||
"ethers",
|
||||
"fdlimit",
|
||||
"flume",
|
||||
"fstrings",
|
||||
"futures",
|
||||
"hashbrown",
|
||||
"indexmap",
|
||||
@ -5197,6 +5219,7 @@ dependencies = [
|
||||
"parking_lot 0.12.1",
|
||||
"petgraph",
|
||||
"proctitle",
|
||||
"rand",
|
||||
"redis-cell-client",
|
||||
"regex",
|
||||
"reqwest",
|
||||
@ -5212,6 +5235,7 @@ dependencies = [
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
"uuid 1.1.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4,7 +4,7 @@ members = [
|
||||
"migration",
|
||||
"linkedhashmap",
|
||||
"redis-cell-client",
|
||||
"web3-proxy",
|
||||
"web3_proxy",
|
||||
]
|
||||
|
||||
# TODO: enable lto (and maybe other things proven with benchmarks) once rapid development is done
|
||||
|
@ -107,7 +107,7 @@ impl MigrationTrait for Migration {
|
||||
.col(ColumnDef::new(UserKeys::UserUuid).uuid().not_null())
|
||||
.col(
|
||||
ColumnDef::new(UserKeys::ApiKey)
|
||||
.string_len(32)
|
||||
.uuid()
|
||||
.not_null()
|
||||
.unique_key(),
|
||||
)
|
||||
|
@ -1,7 +0,0 @@
|
||||
//! Manage users.
|
||||
//!
|
||||
//! While most user management will (and should) happen through the web api,
|
||||
|
||||
fn main() {
|
||||
println!("hello, world");
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
name = "web3-proxy"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
default-run = "web3-proxy"
|
||||
default-run = "web3_proxy"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -29,6 +29,7 @@ ethers = { version = "0.17.0", features = ["rustls", "ws"] }
|
||||
fdlimit = "0.2.1"
|
||||
flume = "0.10.14"
|
||||
futures = { version = "0.3.21", features = ["thread-pool"] }
|
||||
fstrings = "0.2.3"
|
||||
hashbrown = { version = "0.12.3", features = ["serde"] }
|
||||
indexmap = "1.9.1"
|
||||
linkedhashmap = { path = "../linkedhashmap", features = ["inline-more"] }
|
||||
@ -37,6 +38,7 @@ num = "0.4.0"
|
||||
parking_lot = { version = "0.12.1", features = ["arc_lock"] }
|
||||
petgraph = "0.6.2"
|
||||
proctitle = "0.1.1"
|
||||
rand = "0.8.5"
|
||||
# TODO: regex has several "perf" features that we might want to use
|
||||
regex = "1.6.0"
|
||||
reqwest = { version = "0.11.11", default-features = false, features = ["json", "tokio-rustls"] }
|
||||
@ -46,7 +48,7 @@ sea-orm = { version = "0.9.1", features = ["macros"] }
|
||||
serde = { version = "1.0.142", features = [] }
|
||||
serde_json = { version = "1.0.83", default-features = false, features = ["alloc", "raw_value"] }
|
||||
tokio = { version = "1.20.1", features = ["full", "tracing"] }
|
||||
async-std = { version = "1.12.0", features = ["attributes", "tokio1"] }
|
||||
uuid = "1.1.2"
|
||||
toml = "0.5.9"
|
||||
tracing = "0.1.36"
|
||||
# TODO: tracing-subscriber has serde and serde_json features that we might want to use
|
@ -8,15 +8,6 @@
|
||||
//#![warn(missing_docs)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
pub mod app;
|
||||
pub mod bb8_helpers;
|
||||
pub mod config;
|
||||
pub mod connection;
|
||||
pub mod connections;
|
||||
pub mod firewall;
|
||||
pub mod frontend;
|
||||
pub mod jsonrpc;
|
||||
|
||||
use parking_lot::deadlock;
|
||||
use std::fs;
|
||||
use std::sync::atomic::{self, AtomicUsize};
|
||||
@ -25,9 +16,9 @@ use std::time::Duration;
|
||||
use tokio::runtime;
|
||||
use tracing::{debug, info};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use crate::app::{flatten_handle, Web3ProxyApp};
|
||||
use crate::config::{AppConfig, CliConfig};
|
||||
use web3_proxy::app::{flatten_handle, Web3ProxyApp};
|
||||
use web3_proxy::config::{AppConfig, CliConfig};
|
||||
use web3_proxy::frontend;
|
||||
|
||||
fn run(
|
||||
shutdown_receiver: flume::Receiver<()>,
|
||||
@ -156,7 +147,7 @@ mod tests {
|
||||
use hashbrown::HashMap;
|
||||
use std::env;
|
||||
|
||||
use crate::config::{RpcSharedConfig, Web3ConnectionConfig};
|
||||
use web3_proxy::config::{RpcSharedConfig, Web3ConnectionConfig};
|
||||
|
||||
use super::*;
|
||||
|
106
web3_proxy/src/bin/web3_proxy_cli.rs
Normal file
106
web3_proxy/src/bin/web3_proxy_cli.rs
Normal file
@ -0,0 +1,106 @@
|
||||
use argh::FromArgs;
|
||||
use entities::{user, user_keys};
|
||||
use ethers::prelude::Bytes;
|
||||
use fstrings::{format_args_f, println_f};
|
||||
use rand::prelude::*;
|
||||
use sea_orm::{prelude::Uuid, ActiveModelTrait};
|
||||
use web3_proxy::users::new_api_key;
|
||||
|
||||
#[derive(Debug, FromArgs)]
|
||||
/// Command line interface for admins to interact with web3-proxy
|
||||
pub struct TopConfig {
|
||||
/// what host the client should connect to
|
||||
#[argh(
|
||||
option,
|
||||
default = "\"mysql://root:dev_web3_proxy@127.0.0.1:3306/dev_web3_proxy\".to_string()"
|
||||
)]
|
||||
pub db_url: String,
|
||||
|
||||
/// this one cli can do multiple things
|
||||
#[argh(subcommand)]
|
||||
sub_command: SubCommand,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand)]
|
||||
enum SubCommand {
|
||||
CreateUser(CreateUserSubCommand),
|
||||
Two(SubCommandTwo),
|
||||
// TODO: sub command to downgrade migrations?
|
||||
// TODO: sub command to add new api keys to an existing user?
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
/// First subcommand.
|
||||
#[argh(subcommand, name = "create_user")]
|
||||
struct CreateUserSubCommand {
|
||||
#[argh(option)]
|
||||
/// the user's ethereum address
|
||||
address: String,
|
||||
|
||||
#[argh(option)]
|
||||
/// the user's optional email
|
||||
email: Option<String>,
|
||||
}
|
||||
|
||||
impl CreateUserSubCommand {
|
||||
async fn main(self, db: &sea_orm::DatabaseConnection) -> anyhow::Result<()> {
|
||||
let u = user::ActiveModel {
|
||||
address: sea_orm::Set(self.address),
|
||||
email: sea_orm::Set(self.email),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// TODO: proper error message
|
||||
let u = u.insert(db).await?;
|
||||
|
||||
println_f!("user: {u:?}");
|
||||
|
||||
// TODO: use chacha20?
|
||||
let api_key = new_api_key();
|
||||
|
||||
// TODO: create a key, too
|
||||
// TODO: why are active and private_txs ints instead of bools?
|
||||
let uk = user_keys::ActiveModel {
|
||||
user_uuid: sea_orm::Set(u.uuid),
|
||||
// api_key: api_key,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
println_f!("user key: {uk:?}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
/// Second subcommand.
|
||||
#[argh(subcommand, name = "two")]
|
||||
struct SubCommandTwo {
|
||||
#[argh(switch)]
|
||||
/// whether to fooey
|
||||
fooey: bool,
|
||||
}
|
||||
|
||||
impl SubCommandTwo {
|
||||
async fn main(self) -> anyhow::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let cli_config: TopConfig = argh::from_env();
|
||||
|
||||
println!("hello, {}", cli_config.db_url);
|
||||
|
||||
match cli_config.sub_command {
|
||||
SubCommand::CreateUser(x) => {
|
||||
// TODO: more advanced settings
|
||||
let db_conn = sea_orm::Database::connect(cli_config.db_url).await?;
|
||||
|
||||
x.main(&db_conn).await
|
||||
}
|
||||
SubCommand::Two(x) => x.main().await,
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ pub async fn health(Extension(app): Extension<Arc<Web3ProxyApp>>) -> impl IntoRe
|
||||
}
|
||||
|
||||
/// Very basic status page
|
||||
/// TODO: replace this with proper stats and monitoring
|
||||
pub async fn status(Extension(app): Extension<Arc<Web3ProxyApp>>) -> impl IntoResponse {
|
||||
// TODO: what else should we include? uptime? prometheus?
|
||||
let balanced_rpcs = app.balanced_rpcs();
|
@ -6,7 +6,7 @@ use super::errors::handle_anyhow_error;
|
||||
use super::{rate_limit_by_ip, rate_limit_by_key};
|
||||
use crate::{app::Web3ProxyApp, jsonrpc::JsonRpcRequestEnum};
|
||||
|
||||
pub async fn proxy_web3_rpc(
|
||||
pub async fn public_proxy_web3_rpc(
|
||||
Json(payload): Json<JsonRpcRequestEnum>,
|
||||
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
||||
ClientIp(ip): ClientIp,
|
@ -11,7 +11,9 @@ use axum::{
|
||||
routing::{get, post},
|
||||
Extension, Router,
|
||||
};
|
||||
use entities::user_keys;
|
||||
use reqwest::StatusCode;
|
||||
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QuerySelect};
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
use tracing::debug;
|
||||
@ -26,13 +28,48 @@ pub async fn rate_limit_by_ip(app: &Web3ProxyApp, ip: &IpAddr) -> Result<(), imp
|
||||
rate_limit_by_key(app, &rate_limiter_key).await
|
||||
}
|
||||
|
||||
/// if Ok(()), rate limits are acceptable
|
||||
/// if Err(response), rate limits exceeded
|
||||
pub async fn rate_limit_by_key(
|
||||
app: &Web3ProxyApp,
|
||||
user_key: &str,
|
||||
) -> Result<(), impl IntoResponse> {
|
||||
let db = app.db_conn();
|
||||
|
||||
// TODO: query the db to make sure this key is active
|
||||
// query the db to make sure this key is active
|
||||
// TODO: probably want a cache on this
|
||||
match user_keys::Entity::find()
|
||||
.select_only()
|
||||
.column(user_keys::Column::UserUuid)
|
||||
.filter(user_keys::Column::ApiKey.eq(user_key))
|
||||
.filter(user_keys::Column::Active.eq(true))
|
||||
.one(db)
|
||||
.await
|
||||
{
|
||||
Ok(Some(_)) => {
|
||||
// user key is valid
|
||||
}
|
||||
Ok(None) => {
|
||||
// invalid user key
|
||||
// TODO: rate limit by ip here, too? maybe tarpit?
|
||||
return Err(handle_anyhow_error(
|
||||
Some(StatusCode::FORBIDDEN),
|
||||
None,
|
||||
anyhow::anyhow!("unknown api key"),
|
||||
)
|
||||
.await
|
||||
.into_response());
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(handle_anyhow_error(
|
||||
Some(StatusCode::INTERNAL_SERVER_ERROR),
|
||||
None,
|
||||
e.into(),
|
||||
)
|
||||
.await
|
||||
.into_response());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(rate_limiter) = app.rate_limiter() {
|
||||
if rate_limiter.throttle_key(user_key).await.is_err() {
|
||||
@ -55,23 +92,15 @@ pub async fn rate_limit_by_key(
|
||||
}
|
||||
|
||||
pub async fn run(port: u16, proxy_app: Arc<Web3ProxyApp>) -> anyhow::Result<()> {
|
||||
// TODO: check auth (from authp?) here
|
||||
// build our application with a route
|
||||
// order most to least common
|
||||
let app = Router::new()
|
||||
// `POST /` goes to `proxy_web3_rpc`
|
||||
.route("/", post(http_proxy::proxy_web3_rpc))
|
||||
// `websocket /` goes to `proxy_web3_ws`
|
||||
.route("/", get(ws_proxy::websocket_handler))
|
||||
// `POST /rpc/:key` goes to `proxy_web3_rpc`
|
||||
.route("/rpc/:key", post(http_proxy::user_proxy_web3_rpc))
|
||||
// `websocket /` goes to `proxy_web3_ws`
|
||||
.route("/rpc/:key", get(ws_proxy::user_websocket_handler))
|
||||
// `GET /health` goes to `health`
|
||||
.route("/", post(http_proxy::public_proxy_web3_rpc))
|
||||
.route("/", get(ws_proxy::public_websocket_handler))
|
||||
.route("/u/:key", post(http_proxy::user_proxy_web3_rpc))
|
||||
.route("/u/:key", get(ws_proxy::user_websocket_handler))
|
||||
.route("/health", get(http::health))
|
||||
// `GET /status` goes to `status`
|
||||
.route("/status", get(http::status))
|
||||
// `POST /users` goes to `create_user`
|
||||
.route("/users", post(users::create_user))
|
||||
.layer(Extension(proxy_app));
|
||||
|
||||
@ -80,6 +109,7 @@ pub async fn run(port: u16, proxy_app: Arc<Web3ProxyApp>) -> anyhow::Result<()>
|
||||
|
||||
// run our app with hyper
|
||||
// `axum::Server` is a re-export of `hyper::Server`
|
||||
// TODO: allow only listening on localhost?
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], port));
|
||||
debug!("listening on port {}", port);
|
||||
// TODO: into_make_service is enough if we always run behind a proxy. make into_make_service_with_connect_info optional?
|
@ -22,7 +22,7 @@ use crate::{
|
||||
|
||||
use super::{rate_limit_by_ip, rate_limit_by_key};
|
||||
|
||||
pub async fn websocket_handler(
|
||||
pub async fn public_websocket_handler(
|
||||
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
||||
ClientIp(ip): ClientIp,
|
||||
ws: WebSocketUpgrade,
|
9
web3_proxy/src/lib.rs
Normal file
9
web3_proxy/src/lib.rs
Normal file
@ -0,0 +1,9 @@
|
||||
pub mod app;
|
||||
pub mod bb8_helpers;
|
||||
pub mod config;
|
||||
pub mod connection;
|
||||
pub mod connections;
|
||||
pub mod firewall;
|
||||
pub mod frontend;
|
||||
pub mod jsonrpc;
|
||||
pub mod users;
|
11
web3_proxy/src/users.rs
Normal file
11
web3_proxy/src/users.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use rand::prelude::*;
|
||||
use uuid::{Builder, Uuid};
|
||||
|
||||
pub fn new_api_key() -> Uuid {
|
||||
// TODO: chacha20?
|
||||
let mut rng = thread_rng();
|
||||
|
||||
let random_bytes = rng.gen();
|
||||
|
||||
Builder::from_random_bytes(random_bytes).into_uuid()
|
||||
}
|
Loading…
Reference in New Issue
Block a user