diff --git a/Cargo.lock b/Cargo.lock index 1f553562..311bac37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1457,6 +1457,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -6919,6 +6930,7 @@ dependencies = [ "console-subscriber", "counter", "deferred-rate-limiter", + "derivative", "derive_more", "entities", "ethbloom", diff --git a/README.md b/README.md index 1eb8db43..e51c050e 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,12 @@ Start the server with the defaults (listen on `http://localhost:8544` and use `. cargo run --release -- proxyd ``` +Quickly run tests: + +``` +RUST_LOG=web3_proxy=trace,info cargo nextest run +``` + ## Common commands Create a user: diff --git a/web3_proxy/Cargo.toml b/web3_proxy/Cargo.toml index 2205ee0a..46ad911d 100644 --- a/web3_proxy/Cargo.toml +++ b/web3_proxy/Cargo.toml @@ -103,6 +103,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } ulid = { version = "1.0.0", features = ["rand", "uuid", "serde"] } url = { version = "2.4.0", features = ["serde"] } uuid = { version = "1.3.4", default-features = false, features = ["fast-rng", "serde", "v4", "zerocopy"] } +derivative = "2.2.0" [dev-dependencies] test-log = { version = "0.2.12", default-features = false, features = ["trace"] } diff --git a/web3_proxy/src/bin/web3_proxy_cli/proxyd.rs b/web3_proxy/src/bin/web3_proxy_cli/proxyd.rs index bed5e04c..21bec4f9 100644 --- a/web3_proxy/src/bin/web3_proxy_cli/proxyd.rs +++ b/web3_proxy/src/bin/web3_proxy_cli/proxyd.rs @@ -258,7 +258,7 @@ mod tests { use ethers::{ prelude::{Http, Provider, U256}, types::Address, - utils::Anvil, + utils::{Anvil, AnvilInstance}, }; use hashbrown::HashMap; use parking_lot::Mutex; @@ -269,7 +269,7 @@ mod tests { }; use tokio::{ sync::broadcast::error::SendError, - task::JoinHandle, + task::{yield_now, JoinHandle}, time::{sleep, Instant}, }; use web3_proxy::{ @@ -280,6 +280,7 @@ mod tests { // TODO: put it in a thread? struct TestApp { handle: Mutex>>>, + anvil: AnvilInstance, anvil_provider: Provider, proxy_provider: Provider, shutdown_sender: broadcast::Sender<()>, @@ -301,14 +302,11 @@ mod tests { let anvil_provider = Provider::::try_from(anvil.endpoint()).unwrap(); - // mine a block to test the provider - let _: U256 = anvil_provider.request("evm_mine", ()).await.unwrap(); - // make a test TopConfig // TODO: load TopConfig from a file? CliConfig could have `cli_config.load_top_config`. would need to inject our endpoint ports let top_config = TopConfig { app: AppConfig { - chain_id: 137, + chain_id: 31337, default_user_max_requests_per_period: Some(6_000_000), deposit_factory_contract: Address::from_str( "4e3BC2054788De923A04936C6ADdB99A05B0Ea36", @@ -368,6 +366,7 @@ mod tests { Self { handle: Mutex::new(Some(handle)), + anvil, anvil_provider, proxy_provider, shutdown_sender, @@ -383,6 +382,8 @@ mod tests { let handle = self.handle.lock().take(); if let Some(handle) = handle { + let _ = self.stop(); + info!("waiting for the app to stop..."); handle.await.unwrap().unwrap(); } @@ -418,11 +419,13 @@ mod tests { let first_block_num = anvil_result.number.unwrap(); + // mine a block let _: U256 = anvil_provider .request("evm_mine", None::<()>) .await .unwrap(); + // make sure the block advanced let anvil_result = anvil_provider .request::<_, Option>("eth_getBlockByNumber", ("latest", false)) .await @@ -440,7 +443,6 @@ mod tests { assert_eq!(first_block_num, second_block_num - 1); - // x.stop(); - // x.wait().await; + x.wait().await; } } diff --git a/web3_proxy/src/config.rs b/web3_proxy/src/config.rs index d05cbcbc..c7b97bf8 100644 --- a/web3_proxy/src/config.rs +++ b/web3_proxy/src/config.rs @@ -2,6 +2,7 @@ use crate::app::Web3ProxyJoinHandle; use crate::rpcs::blockchain::{BlocksByHashCache, Web3ProxyBlock}; use crate::rpcs::one::Web3Rpc; use argh::FromArgs; +use derivative::Derivative; use ethers::prelude::{Address, TxHash}; use ethers::types::{U256, U64}; use hashbrown::HashMap; @@ -256,15 +257,14 @@ pub fn average_block_interval(chain_id: u64) -> Duration { } /// Configuration for a backend web3 RPC server -#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq)] +#[derive(Clone, Debug, Derivative, Deserialize, PartialEq, Eq)] +#[derivative(Default)] pub struct Web3RpcConfig { /// simple way to disable a connection without deleting the row #[serde(default)] pub disabled: bool, /// a name used in /status and other user facing messages pub display_name: Option, - /// (deprecated) rpc url - pub url: Option, /// while not absolutely required, a ws:// or wss:// connection will be able to subscribe to head blocks pub ws_url: Option, /// while not absolutely required, a http:// or https:// connection will allow erigon to stream JSON @@ -272,7 +272,8 @@ pub struct Web3RpcConfig { /// block data limit. If None, will be queried pub block_data_limit: Option, /// the requests per second at which the server starts slowing down - #[serde(default = "default_soft_limit")] + #[serde(default)] + #[derivative(Default(value = "1"))] pub soft_limit: u32, /// the requests per second at which the server throws errors (rate limit or otherwise) pub hard_limit: Option, @@ -288,10 +289,6 @@ pub struct Web3RpcConfig { pub extra: HashMap, } -fn default_soft_limit() -> u32 { - 10 -} - impl Web3RpcConfig { /// Create a Web3Rpc from config /// TODO: move this into Web3Rpc? (just need to make things pub(crate)) diff --git a/web3_proxy/src/rpcs/consensus.rs b/web3_proxy/src/rpcs/consensus.rs index c3fd1215..60809493 100644 --- a/web3_proxy/src/rpcs/consensus.rs +++ b/web3_proxy/src/rpcs/consensus.rs @@ -817,8 +817,9 @@ impl ConsensusFinder { // we used to specify rpc on this, but it shouldn't be necessary let parent_hash = block_to_check.parent_hash(); + // TODO: i flip flop on passing rpc to this or not match web3_rpcs - .block(authorization, parent_hash, None, None) + .block(authorization, parent_hash, Some(rpc), None) .await { Ok(parent_block) => block_to_check = parent_block, diff --git a/web3_proxy/src/rpcs/one.rs b/web3_proxy/src/rpcs/one.rs index fb64c28b..98294a23 100644 --- a/web3_proxy/src/rpcs/one.rs +++ b/web3_proxy/src/rpcs/one.rs @@ -89,7 +89,7 @@ impl Web3Rpc { // TODO: have this take a builder (which will have channels attached). or maybe just take the config and give the config public fields #[allow(clippy::too_many_arguments)] pub async fn spawn( - mut config: Web3RpcConfig, + config: Web3RpcConfig, name: String, chain_id: u64, db_conn: Option, @@ -142,19 +142,9 @@ impl Web3Rpc { let (hard_limit_until, _) = watch::channel(Instant::now()); if config.ws_url.is_none() && config.http_url.is_none() { - if let Some(url) = config.url { - if url.starts_with("ws") { - config.ws_url = Some(url); - } else if url.starts_with("http") { - config.http_url = Some(url); - } else { - return Err(anyhow!("only ws or http urls are supported")); - } - } else { - return Err(anyhow!( - "either ws_url or http_url are required. it is best to set both" - )); - } + return Err(anyhow!( + "either ws_url or http_url are required. it is best to set both. they must both point to the same server!" + )); } let (head_block, _) = watch::channel(None);