From 10aa3d617a679fa562cc5d00b876911724ddb6e0 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 7 Feb 2023 20:03:05 +0100 Subject: [PATCH] feat(rpc): impl rpc-builder types (#1203) --- Cargo.lock | 3 + crates/rpc/rpc-api/src/lib.rs | 16 +- crates/rpc/rpc-builder/Cargo.toml | 9 + crates/rpc/rpc-builder/src/lib.rs | 522 ++++++++++++++++-- crates/rpc/rpc-builder/tests/it/http.rs | 8 + crates/rpc/rpc-builder/tests/it/main.rs | 4 + crates/rpc/rpc-builder/tests/it/utils.rs | 8 + crates/rpc/rpc/src/debug.rs | 12 + crates/rpc/rpc/src/eth/api/block.rs | 2 +- crates/rpc/rpc/src/eth/api/mod.rs | 21 +- crates/rpc/rpc/src/eth/api/server.rs | 2 +- crates/rpc/rpc/src/eth/api/state.rs | 2 +- crates/rpc/rpc/src/eth/api/transactions.rs | 2 +- crates/rpc/rpc/src/trace.rs | 11 + crates/transaction-pool/src/test_utils/mod.rs | 5 +- 15 files changed, 551 insertions(+), 76 deletions(-) create mode 100644 crates/rpc/rpc-builder/tests/it/http.rs create mode 100644 crates/rpc/rpc-builder/tests/it/main.rs create mode 100644 crates/rpc/rpc-builder/tests/it/utils.rs diff --git a/Cargo.lock b/Cargo.lock index b02e7eccbf..ded9c9c779 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4641,9 +4641,12 @@ dependencies = [ "reth-network-api", "reth-provider", "reth-rpc", + "reth-rpc-api", + "reth-tracing", "reth-transaction-pool", "serde", "strum", + "tokio", ] [[package]] diff --git a/crates/rpc/rpc-api/src/lib.rs b/crates/rpc/rpc-api/src/lib.rs index 2c6a24ce0a..ec573ede05 100644 --- a/crates/rpc/rpc-api/src/lib.rs +++ b/crates/rpc/rpc-api/src/lib.rs @@ -19,8 +19,14 @@ mod net; mod trace; mod web3; -pub use self::{ - admin::AdminApiServer, debug::DebugApiServer, engine::EngineApiServer, eth::EthApiServer, - eth_filter::EthFilterApiServer, eth_pubsub::EthPubSubApiServer, net::NetApiServer, - trace::TraceApiServer, web3::Web3ApiServer, -}; +/// re-export of all server traits +pub use servers::*; + +/// Aggregates all server traits. +pub mod servers { + pub use crate::{ + admin::AdminApiServer, debug::DebugApiServer, engine::EngineApiServer, eth::EthApiServer, + eth_filter::EthFilterApiServer, eth_pubsub::EthPubSubApiServer, net::NetApiServer, + trace::TraceApiServer, web3::Web3ApiServer, + }; +} diff --git a/crates/rpc/rpc-builder/Cargo.toml b/crates/rpc/rpc-builder/Cargo.toml index b3f2a035b1..983617a027 100644 --- a/crates/rpc/rpc-builder/Cargo.toml +++ b/crates/rpc/rpc-builder/Cargo.toml @@ -12,9 +12,18 @@ reth-ipc = { path = "../ipc" } reth-network-api = { path = "../../net/network-api" } reth-provider = { path = "../../storage/provider" } reth-rpc = { path = "../rpc" } +reth-rpc-api = { path = "../rpc-api" } reth-transaction-pool = { path = "../../transaction-pool" } jsonrpsee = { version = "0.16", features = ["server"] } strum = { version = "0.24", features = ["derive"] } serde = { version = "1.0", features = ["derive"] } + +[dev-dependencies] +reth-tracing = { path = "../../tracing" } +reth-rpc-api = { path = "../rpc-api", features = ["client"] } +reth-transaction-pool = { path = "../../transaction-pool", features = ["test-utils"] } +reth-provider = { path = "../../storage/provider", features = ["test-utils"] } + +tokio = { version = "1", features = ["rt", "rt-multi-thread"] } diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index b21c45137b..af4ae908fa 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -7,29 +7,106 @@ #![allow(unused)] //! Configure reth RPC +//! +//! This crate contains several builder and config types that allow to configure the selection of +//! [RethRpcModule] specific to transports (ws, http, ipc). +//! +//! The [RpcModuleBuilder] is the main entrypoint for configuring all reth modules. It takes +//! instances of components required to start the servers, such as provider impls, network and +//! transaction pool. [RpcModuleBuilder::build] returns a [TransportRpcModules] which contains the +//! transport specific config (what APIs are available via this transport). +//! +//! The [RpcServerConfig] is used to configure the [RpcServer] type which contains all transport +//! implementations (http server, ws server, ipc server). [RpcServer::start] requires the +//! [TransportRpcModules] so it can start the servers with the configured modules. +//! +//! # Examples +//! +//! Configure only a http server with a selection of [RethRpcModule]s +//! +//! ``` +//! use reth_network_api::{NetworkInfo, Peers}; +//! use reth_provider::{BlockProvider, StateProviderFactory}; +//! use reth_rpc_builder::{RethRpcModule, RpcModuleBuilder, RpcServerConfig, ServerBuilder, TransportRpcModuleConfig}; +//! use reth_transaction_pool::TransactionPool; +//! pub async fn launch(client: Client, pool: Pool, network: Network) +//! where +//! Client: BlockProvider + StateProviderFactory + Clone + 'static, +//! Pool: TransactionPool + Clone + 'static, +//! Network: NetworkInfo + Peers + Clone + 'static, +//! { +//! // configure the rpc module per transport +//! let transports = TransportRpcModuleConfig::default().with_http(vec![ +//! RethRpcModule::Admin, +//! RethRpcModule::Debug, +//! RethRpcModule::Eth, +//! RethRpcModule::Web3, +//! ]); +//! let transport_modules = RpcModuleBuilder::new(client, pool, network).build(transports); +//! let handle = RpcServerConfig::default() +//! .with_http(ServerBuilder::default()) +//! .start(transport_modules) +//! .await +//! .unwrap(); +//! } +//! ``` +pub use jsonrpsee::server::ServerBuilder; use jsonrpsee::{ core::{server::rpc_module::Methods, Error as RpcError}, - server::{Server, ServerBuilder, ServerHandle}, + server::{Server, ServerHandle}, RpcModule, }; -use reth_ipc::server::{Builder as IpcServerBuilder, Endpoint, IpcServer}; -use reth_network_api::{NetworkInfo, PeersInfo}; +use reth_ipc::server::IpcServer; +pub use reth_ipc::server::{Builder as IpcServerBuilder, Endpoint}; +use reth_network_api::{NetworkInfo, Peers}; use reth_provider::{BlockProvider, StateProviderFactory}; +use reth_rpc::{AdminApi, DebugApi, EthApi, NetApi, TraceApi, Web3Api}; +use reth_rpc_api::servers::*; use reth_transaction_pool::TransactionPool; use serde::{Deserialize, Serialize, Serializer}; -use std::{fmt, net::SocketAddr}; -use strum::{AsRefStr, EnumString, EnumVariantNames}; +use std::{ + collections::HashMap, + fmt, + net::{Ipv4Addr, SocketAddr, SocketAddrV4}, +}; +use strum::{AsRefStr, EnumString, EnumVariantNames, VariantNames}; + +/// The default port for the http/ws server +pub const DEFAULT_RPC_PORT: u16 = 8545; + +/// The default IPC endpoint +#[cfg(windows)] +pub const DEFAULT_IPC_ENDPOINT: &str = r"\\.\pipe\reth.ipc"; + +/// The default IPC endpoint +#[cfg(not(windows))] +pub const DEFAULT_IPC_ENDPOINT: &str = "/tmp/reth.ipc"; + +/// Convenience function for starting a server in one step. +pub async fn launch( + client: Client, + pool: Pool, + network: Network, + module_config: impl Into, + server_config: impl Into, +) -> Result +where + Client: BlockProvider + StateProviderFactory + Clone + 'static, + Pool: TransactionPool + Clone + 'static, + Network: NetworkInfo + Peers + Clone + 'static, +{ + let module_config = module_config.into(); + let server_config = server_config.into(); + RpcModuleBuilder::new(client, pool, network) + .build(module_config) + .start_server(server_config) + .await +} /// A builder type to configure the RPC module: See [RpcModule] /// /// This is the main entrypoint for up RPC servers. -/// -/// ``` -/// use reth_rpc_builder::{RethRpcModule, RpcModuleBuilder, RpcModuleConfig}; -/// let builder = RpcModuleBuilder::default() -/// .with_config(RpcModuleConfig::Selection(vec![RethRpcModule::Eth])); -/// ``` #[derive(Debug)] pub struct RpcModuleBuilder { /// The Client type to when creating all rpc handlers @@ -38,8 +115,6 @@ pub struct RpcModuleBuilder { pool: Pool, /// The Network type to when creating all rpc handlers network: Network, - /// What modules to configure - config: RpcModuleConfig, } // === impl RpcBuilder === @@ -47,13 +122,7 @@ pub struct RpcModuleBuilder { impl RpcModuleBuilder { /// Create a new instance of the builder pub fn new(client: Client, pool: Pool, network: Network) -> Self { - Self { client, pool, network, config: Default::default() } - } - - /// Configures what RPC modules should be installed - pub fn with_config(mut self, config: RpcModuleConfig) -> Self { - self.config = config; - self + Self { client, pool, network } } /// Configure the client instance. @@ -61,8 +130,8 @@ impl RpcModuleBuilder { where C: BlockProvider + StateProviderFactory + 'static, { - let Self { pool, config, network, .. } = self; - RpcModuleBuilder { client, config, network, pool } + let Self { pool, network, .. } = self; + RpcModuleBuilder { client, network, pool } } /// Configure the transaction pool instance. @@ -70,33 +139,45 @@ impl RpcModuleBuilder { where P: TransactionPool + 'static, { - let Self { client, config, network, .. } = self; - RpcModuleBuilder { client, config, network, pool } + let Self { client, network, .. } = self; + RpcModuleBuilder { client, network, pool } } /// Configure the network instance. pub fn with_network(self, network: N) -> RpcModuleBuilder where - N: NetworkInfo + PeersInfo + 'static, + N: NetworkInfo + Peers + 'static, { - let Self { client, config, pool, .. } = self; - RpcModuleBuilder { client, config, network, pool } + let Self { client, pool, .. } = self; + RpcModuleBuilder { client, network, pool } } } impl RpcModuleBuilder where - Client: BlockProvider + StateProviderFactory + 'static, - Pool: TransactionPool + 'static, - Network: NetworkInfo + PeersInfo + 'static, + Client: BlockProvider + StateProviderFactory + Clone + 'static, + Pool: TransactionPool + Clone + 'static, + Network: NetworkInfo + Peers + Clone + 'static, { - /// Configures the [RpcModule] which can be used to start the server(s). + /// Configures all [RpcModule]s specific to the given [TransportRpcModuleConfig] which can be + /// used to start the transport server(s). /// /// See also [RpcServer::start] - pub fn build(self, _servers: TransportRpcModuleConfig) -> TransportRpcModules<()> { - let Self { client: _, pool: _, network: _, config: _ } = self; + pub fn build(self, module_config: TransportRpcModuleConfig) -> TransportRpcModules<()> { + let mut modules = TransportRpcModules::default(); - todo!("merge all handlers") + let Self { client, pool, network } = self; + + let mut registry = RethModuleRegistry::new(client, pool, network); + + if !module_config.is_empty() { + let TransportRpcModuleConfig { http, ws, ipc } = module_config; + modules.http = registry.maybe_module(http.as_ref()); + modules.ws = registry.maybe_module(ws.as_ref()); + modules.ipc = registry.maybe_module(ipc.as_ref()); + } + + modules } } @@ -107,7 +188,16 @@ impl Default for RpcModuleBuilder<(), (), ()> { } /// Describes the modules that should be installed -#[derive(Debug, Default)] +/// +/// # Example +/// +/// Create a [RpcModuleConfig] from a selection. +/// +/// ``` +/// use reth_rpc_builder::{RethRpcModule, RpcModuleConfig}; +/// let config: RpcModuleConfig = vec![RethRpcModule::Eth].into(); +/// ``` +#[derive(Debug, Default, Clone, Eq, PartialEq)] pub enum RpcModuleConfig { /// Use all available modules. #[default] @@ -116,9 +206,88 @@ pub enum RpcModuleConfig { Selection(Vec), } +// === impl RpcModuleConfig === + +impl RpcModuleConfig { + /// Returns a selection of [RethRpcModule] with all [RethRpcModule::VARIANTS]. + pub fn all_modules() -> Vec { + RpcModuleConfig::try_from_selection(RethRpcModule::VARIANTS.iter().copied()) + .expect("valid selection") + .into_selection() + } + + /// Creates a new [RpcModuleConfig::Selection] from the given items. + /// + /// # Example + /// + /// Create a selection from the [RethRpcModule] string identifiers + /// + /// ``` + /// use reth_rpc_builder::{RethRpcModule, RpcModuleConfig}; + /// let selection = vec!["eth", "admin"]; + /// let config = RpcModuleConfig::try_from_selection(selection).unwrap(); + /// assert_eq!(config, RpcModuleConfig::Selection(vec![RethRpcModule::Eth, RethRpcModule::Admin])); + /// ``` + pub fn try_from_selection(selection: I) -> Result + where + I: IntoIterator, + T: TryInto, + { + let selection = + selection.into_iter().map(TryInto::try_into).collect::, _>>()?; + Ok(RpcModuleConfig::Selection(selection)) + } + + /// Creates a new [RpcModule] based on the configured reth modules. + /// + /// Note: This will always create new instance of the module handlers and is therefor only + /// recommended for launching standalone transports. If multiple transports need to be + /// configured it's recommended to use the [RpcModuleBuilder]. + pub fn standalone_module( + &self, + client: Client, + pool: Pool, + network: Network, + ) -> RpcModule<()> + where + Client: BlockProvider + StateProviderFactory + Clone + 'static, + Pool: TransactionPool + Clone + 'static, + Network: NetworkInfo + Peers + Clone + 'static, + { + let mut registry = RethModuleRegistry::new(client, pool, network); + registry.module(self) + } + + /// Returns an iterator over all configured [RethRpcModule] + pub fn iter_selection(&self) -> Box + '_> { + match self { + RpcModuleConfig::All => Box::new(Self::all_modules().into_iter()), + RpcModuleConfig::Selection(s) => Box::new(s.iter().copied()), + } + } + + /// Returns the list of configured [RethRpcModule] + pub fn into_selection(self) -> Vec { + match self { + RpcModuleConfig::All => Self::all_modules(), + RpcModuleConfig::Selection(s) => s, + } + } +} + +impl From for RpcModuleConfig +where + I: IntoIterator, + T: Into, +{ + fn from(value: I) -> Self { + RpcModuleConfig::Selection(value.into_iter().map(Into::into).collect()) + } +} + /// Represents RPC modules that are supported by reth #[derive( - Debug, Clone, Copy, Eq, PartialEq, AsRefStr, EnumVariantNames, EnumString, Deserialize, + Debug, Clone, Copy, Eq, PartialEq, Hash, AsRefStr, EnumVariantNames, EnumString, Deserialize, )] #[serde(rename_all = "snake_case")] #[strum(serialize_all = "kebab-case")] @@ -152,6 +321,83 @@ impl Serialize for RethRpcModule { } } +/// A Helper type the holds instances of the configured modules. +pub struct RethModuleRegistry { + client: Client, + pool: Pool, + network: Network, + /// Holds a clone of the actual [EthApi] namespace impl since this can be required by other + /// namespaces + eth_api: Option>, + /// Contains the [Methods] of a module + modules: HashMap, +} + +// === impl RethModuleRegistry === + +impl RethModuleRegistry +where + Client: BlockProvider + StateProviderFactory + Clone + 'static, + Pool: TransactionPool + Clone + 'static, + Network: NetworkInfo + Peers + Clone + 'static, +{ + /// Creates a new, empty instance + pub fn new(client: Client, pool: Pool, network: Network) -> Self { + Self { client, pool, network, eth_api: None, modules: Default::default() } + } + + /// Helper function to create a [RpcModule] if it's not `None` + fn maybe_module(&mut self, config: Option<&RpcModuleConfig>) -> Option> { + let config = config?; + let module = self.module(config); + Some(module) + } + + /// Populates a new [RpcModule] based on the selected [RethRpcModule]s in the given + /// [RpcModuleConfig] + pub fn module(&mut self, config: &RpcModuleConfig) -> RpcModule<()> { + let mut module = RpcModule::new(()); + for reth_module in config.iter_selection() { + let methods = self.reth_methods(reth_module); + module.merge(methods).expect("No conflicts"); + } + module + } + + /// Returns the [Methods] for the given [RethRpcModule] + /// + /// If this is the first time the namespace is requested, a new instance of API implementation + /// will be created. + pub fn reth_methods(&mut self, namespace: RethRpcModule) -> Methods { + if let Some(methods) = self.modules.get(&namespace).cloned() { + return methods + } + let methods: Methods = match namespace { + RethRpcModule::Admin => AdminApi::new(self.network.clone()).into_rpc().into(), + RethRpcModule::Debug => DebugApi::new().into_rpc().into(), + RethRpcModule::Eth => self.eth_api().into_rpc().into(), + RethRpcModule::Net => { + let eth_api = self.eth_api(); + NetApi::new(self.network.clone(), eth_api).into_rpc().into() + } + RethRpcModule::Trace => TraceApi::new().into_rpc().into(), + RethRpcModule::Web3 => Web3Api::new(self.network.clone()).into_rpc().into(), + }; + self.modules.insert(namespace, methods.clone()); + + methods + } + + /// Returns the configured [EthApi] or creates it if it does not exist yet + fn eth_api(&mut self) -> EthApi { + self.eth_api + .get_or_insert_with(|| { + EthApi::new(self.client.clone(), self.pool.clone(), self.network.clone()) + }) + .clone() + } +} + /// A builder type for configuring and launching the servers that will handle RPC requests. /// /// Supported server transports are: @@ -163,31 +409,74 @@ impl Serialize for RethRpcModule { /// /// Once the [RpcModule] is built via [RpcModuleBuilder] the servers can be started, See also /// [ServerBuilder::build] and [Server::start](jsonrpsee::server::Server::start). -pub struct RpcServerBuilder { +#[derive(Default)] +pub struct RpcServerConfig { /// Configs for JSON-RPC Http. - pub http_server_config: Option, + http_server_config: Option, /// Configs for WS server - pub ws_server_config: Option, + ws_server_config: Option, /// Address where to bind the http and ws server to - pub http_ws_addr: Option, + http_ws_addr: Option, /// Configs for JSON-RPC IPC server - pub ipc_server_config: Option, + ipc_server_config: Option, /// The Endpoint where to launch the ipc server - pub ipc_server_path: Option, + ipc_endpoint: Option, } -/// === impl RpcServerBuilder === +/// === impl RpcServerConfig === + +impl RpcServerConfig { + /// Configures the http server + pub fn with_http(mut self, config: ServerBuilder) -> Self { + self.http_server_config = Some(config.http_only()); + self + } + + /// Configures the ws server + pub fn with_ws(mut self, config: ServerBuilder) -> Self { + self.ws_server_config = Some(config.ws_only()); + self + } + + /// Configures the [SocketAddr] of the http/ws server + /// + /// Default is [Ipv4Addr::UNSPECIFIED] and [DEFAULT_RPC_PORT] + pub fn with_address(mut self, addr: SocketAddr) -> Self { + self.http_ws_addr = Some(addr); + self + } + + /// Configures the ipc server + pub fn with_ipc(mut self, mut config: IpcServerBuilder) -> Self { + self.ipc_server_config = Some(config); + self + } + + /// Configures the endpoint of the ipc server + /// + /// Default is [DEFAULT_IPC_ENDPOINT] + pub fn with_ipc_endpoint(mut self, path: impl Into) -> Self { + self.ipc_endpoint = Some(Endpoint::new(path.into())); + self + } + + /// Convenience function to do [RpcServerConfig::build] and [RpcServer::start] in one step + pub async fn start( + self, + modules: TransportRpcModules<()>, + ) -> Result { + self.build().await?.start(modules).await + } -impl RpcServerBuilder { /// Finalize the configuration of the server(s). /// /// This consumes the builder and returns a server. /// - /// Note: The server ist not started and does nothing unless polled, See also + /// Note: The server ist not started and does nothing unless polled, See also [RpcServer::start] pub async fn build(self) -> Result { - let socket_addr = self.http_ws_addr.unwrap_or("127.0.0.1:8545".parse().unwrap()); - /// Todo: default path for ipc - let ips_path = self.ipc_server_path.unwrap(); // default path undecided + let socket_addr = self + .http_ws_addr + .unwrap_or(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, DEFAULT_RPC_PORT))); let http_server = if let Some(builder) = self.http_server_config { let server = builder.build(socket_addr).await?; @@ -204,7 +493,10 @@ impl RpcServerBuilder { }; let ipc_server = if let Some(builder) = self.ipc_server_config { - let server = builder.build(ips_path.path())?; + let ipc_path = self + .ipc_endpoint + .unwrap_or_else(|| Endpoint::new(DEFAULT_IPC_ENDPOINT.to_string())); + let server = builder.build(ipc_path.path())?; Some(server) } else { None @@ -215,6 +507,17 @@ impl RpcServerBuilder { } /// Holds modules to be installed per transport type +/// +/// # Example +/// +/// Configure an http transport only +/// +/// ``` +/// use reth_rpc_builder::{RethRpcModule, TransportRpcModuleConfig}; +/// let config = TransportRpcModuleConfig::default() +/// .with_http([RethRpcModule::Eth, RethRpcModule::Admin]); +/// ``` +#[derive(Debug, Clone, Default, Eq, PartialEq)] pub struct TransportRpcModuleConfig { /// http module configuration http: Option, @@ -224,7 +527,35 @@ pub struct TransportRpcModuleConfig { ipc: Option, } -/// Holds installed modules per transport type +// === impl TransportRpcModuleConfig === + +impl TransportRpcModuleConfig { + /// Sets the [RpcModuleConfig] for the http transport. + pub fn with_http(mut self, http: impl Into) -> Self { + self.http = Some(http.into()); + self + } + + /// Sets the [RpcModuleConfig] for the ws transport. + pub fn with_ws(mut self, ws: impl Into) -> Self { + self.ws = Some(ws.into()); + self + } + + /// Sets the [RpcModuleConfig] for the http transport. + pub fn with_ipc(mut self, ipc: impl Into) -> Self { + self.ipc = Some(ipc.into()); + self + } + + /// Returns true if no transports are configured + pub fn is_empty(&self) -> bool { + self.http.is_none() && self.ws.is_none() && self.ipc.is_none() + } +} + +/// Holds installed modules per transport type. +#[derive(Debug, Default)] pub struct TransportRpcModules { /// rpcs module for http http: Option>, @@ -234,7 +565,16 @@ pub struct TransportRpcModules { ipc: Option>, } -/// Container type for each trandport ie. http, ws, and ipc server +// === impl TransportRpcModules === + +impl TransportRpcModules<()> { + /// Convenience function for starting a server + pub async fn start_server(self, builder: RpcServerConfig) -> Result { + builder.start(self).await + } +} + +/// Container type for each transport ie. http, ws, and ipc server pub struct RpcServer { /// http server http: Option, @@ -251,15 +591,50 @@ impl RpcServer { /// /// This returns an [RpcServerHandle] that's connected to the server task(s) until the server is /// stopped or the [RpcServerHandle] is dropped. - pub async fn start(self, _methods: impl Into) -> Result { - todo!() + pub async fn start( + self, + modules: TransportRpcModules<()>, + ) -> Result { + let TransportRpcModules { http, ws, ipc } = modules; + let mut handle = RpcServerHandle { http: None, ws: None, ipc: None }; + + // Start all servers + if let Some((server, module)) = + self.http.and_then(|server| http.map(|module| (server, module))) + { + handle.http = Some(server.start(module)?); + } + + if let Some((server, module)) = self.ws.and_then(|server| ws.map(|module| (server, module))) + { + handle.ws = Some(server.start(module)?); + } + + if let Some((server, module)) = + self.ipc.and_then(|server| ipc.map(|module| (server, module))) + { + handle.ipc = Some(server.start(module).await?); + } + + Ok(handle) + } +} + +impl std::fmt::Debug for RpcServer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RpcServer") + .field("http", &self.http.is_some()) + .field("ws", &self.ws.is_some()) + .field("ipc", &self.ipc.is_some()) + .finish() } } /// A handle to the spawned servers. /// -/// When stop has been called the server will be stopped. -#[derive(Debug, Clone)] +/// When this type is dropped or [RpcServerHandle::stop] has been called the server will be stopped. +#[derive(Clone)] +#[must_use = "Server stop if dropped"] pub struct RpcServerHandle { http: Option, ws: Option, @@ -287,6 +662,16 @@ impl RpcServerHandle { } } +impl std::fmt::Debug for RpcServerHandle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RpcServerHandle") + .field("http", &self.http.is_some()) + .field("ws", &self.ws.is_some()) + .field("ipc", &self.ipc.is_some()) + .finish() + } +} + #[cfg(test)] mod tests { use super::*; @@ -312,4 +697,31 @@ mod tests { "web3" => RethRpcModule::Web3, ); } + + #[test] + fn test_create_rpc_module_config() { + let selection = vec!["eth", "admin"]; + let config = RpcModuleConfig::try_from_selection(selection).unwrap(); + assert_eq!( + config, + RpcModuleConfig::Selection(vec![RethRpcModule::Eth, RethRpcModule::Admin]) + ); + } + + #[test] + fn test_configure_transport_config() { + let config = TransportRpcModuleConfig::default() + .with_http([RethRpcModule::Eth, RethRpcModule::Admin]); + assert_eq!( + config, + TransportRpcModuleConfig { + http: Some(RpcModuleConfig::Selection(vec![ + RethRpcModule::Eth, + RethRpcModule::Admin + ])), + ws: None, + ipc: None, + } + ) + } } diff --git a/crates/rpc/rpc-builder/tests/it/http.rs b/crates/rpc/rpc-builder/tests/it/http.rs new file mode 100644 index 0000000000..ccf0adc408 --- /dev/null +++ b/crates/rpc/rpc-builder/tests/it/http.rs @@ -0,0 +1,8 @@ +//! Standalone http tests + +use crate::utils::test_rpc_builder; + +#[tokio::test(flavor = "multi_thread")] +async fn test_launch_http() { + let _builder = test_rpc_builder(); +} diff --git a/crates/rpc/rpc-builder/tests/it/main.rs b/crates/rpc/rpc-builder/tests/it/main.rs new file mode 100644 index 0000000000..9a2b956c07 --- /dev/null +++ b/crates/rpc/rpc-builder/tests/it/main.rs @@ -0,0 +1,4 @@ +mod http; +pub mod utils; + +fn main() {} diff --git a/crates/rpc/rpc-builder/tests/it/utils.rs b/crates/rpc/rpc-builder/tests/it/utils.rs new file mode 100644 index 0000000000..8dc623c94c --- /dev/null +++ b/crates/rpc/rpc-builder/tests/it/utils.rs @@ -0,0 +1,8 @@ +use reth_provider::test_utils::NoopProvider; +use reth_rpc_builder::RpcModuleBuilder; +use reth_transaction_pool::test_utils::{testing_pool, TestPool}; + +/// Returns an [RpcModuleBuilder] with testing components. +pub fn test_rpc_builder() -> RpcModuleBuilder { + RpcModuleBuilder::default().with_client(NoopProvider::default()).with_pool(testing_pool()) +} diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 229d635935..98dae72d03 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -8,10 +8,22 @@ use reth_rpc_types::RichBlock; /// `debug` API implementation. /// /// This type provides the functionality for handling `debug` related requests. +#[non_exhaustive] pub struct DebugApi { // TODO: add proper handler when implementing functions } +// === impl DebugApi === + +impl DebugApi { + /// Create a new instance of the [DebugApi] + #[allow(clippy::new_without_default)] + // TODO add necessary types + pub fn new() -> Self { + Self {} + } +} + #[async_trait] impl DebugApiServer for DebugApi { async fn raw_header(&self, _block_id: BlockId) -> Result { diff --git a/crates/rpc/rpc/src/eth/api/block.rs b/crates/rpc/rpc/src/eth/api/block.rs index 47e1219915..db2f7ae64a 100644 --- a/crates/rpc/rpc/src/eth/api/block.rs +++ b/crates/rpc/rpc/src/eth/api/block.rs @@ -5,7 +5,7 @@ use reth_primitives::H256; use reth_provider::{BlockProvider, StateProviderFactory}; use reth_rpc_types::RichBlock; -impl EthApi +impl EthApi where Client: BlockProvider + StateProviderFactory + 'static, { diff --git a/crates/rpc/rpc/src/eth/api/mod.rs b/crates/rpc/rpc/src/eth/api/mod.rs index da200b611a..53e75c5510 100644 --- a/crates/rpc/rpc/src/eth/api/mod.rs +++ b/crates/rpc/rpc/src/eth/api/mod.rs @@ -12,7 +12,6 @@ use reth_primitives::{ Address, ChainInfo, H256, U64, }; use reth_provider::{BlockProvider, StateProviderFactory}; -use reth_rpc_types::Transaction; use reth_transaction_pool::TransactionPool; use std::sync::Arc; @@ -49,20 +48,20 @@ pub trait EthApiSpec: Send + Sync { /// or in other network handlers (for example ipc). #[derive(Clone)] #[allow(missing_debug_implementations)] -pub struct EthApi { +pub struct EthApi { /// All nested fields bundled together. - inner: Arc>, + inner: Arc>, } -impl EthApi { +impl EthApi { /// Creates a new, shareable instance. - pub fn new(client: Arc, pool: Pool, network: Network) -> Self { + pub fn new(client: Client, pool: Pool, network: Network) -> Self { let inner = EthApiInner { client, pool, network, signers: Default::default() }; Self { inner: Arc::new(inner) } } /// Returns the inner `Client` - pub(crate) fn client(&self) -> &Arc { + pub(crate) fn client(&self) -> &Client { &self.inner.client } @@ -79,7 +78,7 @@ impl EthApi { // === State access helpers === -impl EthApi +impl EthApi where Client: BlockProvider + StateProviderFactory + 'static, { @@ -130,9 +129,9 @@ where } #[async_trait] -impl EthApiSpec for EthApi +impl EthApiSpec for EthApi where - Pool: TransactionPool + Clone + 'static, + Pool: TransactionPool + Clone + 'static, Client: BlockProvider + StateProviderFactory + 'static, Network: NetworkInfo + 'static, { @@ -160,11 +159,11 @@ where } /// Container type `EthApi` -struct EthApiInner { +struct EthApiInner { /// The transaction pool. pool: Pool, /// The client that can interact with the chain. - client: Arc, + client: Client, /// An interface to interact with the network network: Network, /// All configured Signers diff --git a/crates/rpc/rpc/src/eth/api/server.rs b/crates/rpc/rpc/src/eth/api/server.rs index c91944b8ab..1324bbcaf4 100644 --- a/crates/rpc/rpc/src/eth/api/server.rs +++ b/crates/rpc/rpc/src/eth/api/server.rs @@ -22,7 +22,7 @@ use serde_json::Value; use super::EthApiSpec; #[async_trait::async_trait] -impl EthApiServer for EthApi +impl EthApiServer for EthApi where Self: EthApiSpec, Pool: TransactionPool + 'static, diff --git a/crates/rpc/rpc/src/eth/api/state.rs b/crates/rpc/rpc/src/eth/api/state.rs index 9eb2d4bb13..970dca1dc0 100644 --- a/crates/rpc/rpc/src/eth/api/state.rs +++ b/crates/rpc/rpc/src/eth/api/state.rs @@ -5,7 +5,7 @@ use reth_interfaces::Result; use reth_primitives::{rpc::BlockId, Address, H256, U256}; use reth_provider::{BlockProvider, StateProviderFactory}; -impl EthApi +impl EthApi where Client: BlockProvider + StateProviderFactory + 'static, { diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index 8908f3f7d4..a119cbab18 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -10,7 +10,7 @@ use reth_rlp::Decodable; use reth_rpc_types::TransactionRequest; use reth_transaction_pool::{TransactionOrigin, TransactionPool}; -impl EthApi +impl EthApi where Pool: TransactionPool + 'static, Client: BlockProvider + StateProviderFactory + 'static, diff --git a/crates/rpc/rpc/src/trace.rs b/crates/rpc/rpc/src/trace.rs index 51e37c00ab..7f567ac43e 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -15,6 +15,17 @@ use std::collections::HashSet; #[non_exhaustive] pub struct TraceApi {} +// === impl TraceApi === + +impl TraceApi { + /// Create a new instance of the [TraceApi] + #[allow(clippy::new_without_default)] + // TODO add necessary types + pub fn new() -> Self { + Self {} + } +} + #[async_trait] impl TraceApiServer for TraceApi { async fn call( diff --git a/crates/transaction-pool/src/test_utils/mod.rs b/crates/transaction-pool/src/test_utils/mod.rs index 233ed1bec2..7fd1da50bb 100644 --- a/crates/transaction-pool/src/test_utils/mod.rs +++ b/crates/transaction-pool/src/test_utils/mod.rs @@ -11,8 +11,11 @@ use async_trait::async_trait; pub use mock::*; use std::{marker::PhantomData, sync::Arc}; +/// A [Pool] used for testing +pub type TestPool = Pool, MockOrdering>; + /// Returns a new [Pool] used for testing purposes -pub fn testing_pool() -> Pool, MockOrdering> { +pub fn testing_pool() -> TestPool { Pool::new( Arc::new(NoopTransactionValidator::default()), Arc::new(MockOrdering::default()),