mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
test: basic rpc testing support (#1222)
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -4639,6 +4639,7 @@ dependencies = [
|
||||
"jsonrpsee",
|
||||
"reth-ipc",
|
||||
"reth-network-api",
|
||||
"reth-primitives",
|
||||
"reth-provider",
|
||||
"reth-rpc",
|
||||
"reth-rpc-api",
|
||||
|
||||
@ -1,31 +1,32 @@
|
||||
use jsonrpsee::{core::RpcResult as Result, proc_macros::rpc};
|
||||
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
|
||||
use reth_primitives::NodeRecord;
|
||||
use reth_rpc_types::NodeInfo;
|
||||
|
||||
/// Admin namespace rpc interface that gives access to several non-standard RPC methods.
|
||||
#[rpc(server)]
|
||||
#[cfg_attr(not(feature = "client"), rpc(server))]
|
||||
#[cfg_attr(feature = "client", rpc(server, client))]
|
||||
#[async_trait::async_trait]
|
||||
pub trait AdminApi {
|
||||
/// Adds the given node record to the peerset.
|
||||
#[method(name = "admin_addPeer")]
|
||||
fn add_peer(&self, record: NodeRecord) -> Result<bool>;
|
||||
fn add_peer(&self, record: NodeRecord) -> RpcResult<bool>;
|
||||
|
||||
/// Disconnects from a remote node if the connection exists.
|
||||
///
|
||||
/// Returns true if the peer was successfully removed.
|
||||
#[method(name = "admin_removePeer")]
|
||||
fn remove_peer(&self, record: NodeRecord) -> Result<bool>;
|
||||
fn remove_peer(&self, record: NodeRecord) -> RpcResult<bool>;
|
||||
|
||||
/// Adds the given node record to the trusted peerset.
|
||||
#[method(name = "admin_addTrustedPeer")]
|
||||
fn add_trusted_peer(&self, record: NodeRecord) -> Result<bool>;
|
||||
fn add_trusted_peer(&self, record: NodeRecord) -> RpcResult<bool>;
|
||||
|
||||
/// Removes a remote node from the trusted peer set, but it does not disconnect it
|
||||
/// automatically.
|
||||
///
|
||||
/// Returns true if the peer was successfully removed.
|
||||
#[method(name = "admin_removeTrustedPeer")]
|
||||
fn remove_trusted_peer(&self, record: NodeRecord) -> Result<bool>;
|
||||
fn remove_trusted_peer(&self, record: NodeRecord) -> RpcResult<bool>;
|
||||
|
||||
/// Creates an RPC subscription which serves events received from the network.
|
||||
#[subscription(
|
||||
@ -33,9 +34,9 @@ pub trait AdminApi {
|
||||
unsubscribe = "admin_peerEvents_unsubscribe",
|
||||
item = String
|
||||
)]
|
||||
fn subscribe(&self);
|
||||
fn subscribe_peer_events(&self);
|
||||
|
||||
/// Returns the ENR of the node.
|
||||
#[method(name = "admin_nodeInfo")]
|
||||
async fn node_info(&self) -> Result<NodeInfo>;
|
||||
async fn node_info(&self) -> RpcResult<NodeInfo>;
|
||||
}
|
||||
|
||||
@ -30,3 +30,16 @@ pub mod servers {
|
||||
trace::TraceApiServer, web3::Web3ApiServer,
|
||||
};
|
||||
}
|
||||
|
||||
/// re-export of all client traits
|
||||
#[cfg(feature = "client")]
|
||||
pub use clients::*;
|
||||
|
||||
/// Aggregates all client traits.
|
||||
#[cfg(feature = "client")]
|
||||
pub mod clients {
|
||||
pub use crate::{
|
||||
admin::AdminApiClient, debug::DebugApiClient, engine::EngineApiClient, eth::EthApiClient,
|
||||
net::NetApiClient, trace::TraceApiClient, web3::Web3ApiClient,
|
||||
};
|
||||
}
|
||||
|
||||
@ -22,8 +22,10 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
reth-tracing = { path = "../../tracing" }
|
||||
reth-primitives = { path = "../../primitives" }
|
||||
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"] }
|
||||
reth-network-api = { path = "../../net/network-api", features = ["test-utils"] }
|
||||
|
||||
tokio = { version = "1", features = ["rt", "rt-multi-thread"] }
|
||||
tokio = { version = "1", features = ["rt", "rt-multi-thread"] }
|
||||
@ -426,6 +426,21 @@ pub struct RpcServerConfig {
|
||||
/// === impl RpcServerConfig ===
|
||||
|
||||
impl RpcServerConfig {
|
||||
/// Creates a new config with only http set
|
||||
pub fn http(config: ServerBuilder) -> Self {
|
||||
Self::default().with_http(config)
|
||||
}
|
||||
|
||||
/// Creates a new config with only ws set
|
||||
pub fn ws(config: ServerBuilder) -> Self {
|
||||
Self::default().with_ws(config)
|
||||
}
|
||||
|
||||
/// Creates a new config with only ipc set
|
||||
pub fn ipc(config: IpcServerBuilder) -> Self {
|
||||
Self::default().with_ipc(config)
|
||||
}
|
||||
|
||||
/// Configures the http server
|
||||
pub fn with_http(mut self, config: ServerBuilder) -> Self {
|
||||
self.http_server_config = Some(config.http_only());
|
||||
@ -478,15 +493,23 @@ impl RpcServerConfig {
|
||||
.http_ws_addr
|
||||
.unwrap_or(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, DEFAULT_RPC_PORT)));
|
||||
|
||||
let mut local_addr = None;
|
||||
|
||||
let http_server = if let Some(builder) = self.http_server_config {
|
||||
let server = builder.build(socket_addr).await?;
|
||||
if local_addr.is_none() {
|
||||
local_addr = server.local_addr().ok();
|
||||
}
|
||||
Some(server)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let ws_server = if let Some(builder) = self.ws_server_config {
|
||||
let server = builder.build(socket_addr).await?;
|
||||
let server = builder.build(socket_addr).await.unwrap();
|
||||
if local_addr.is_none() {
|
||||
local_addr = server.local_addr().ok();
|
||||
}
|
||||
Some(server)
|
||||
} else {
|
||||
None
|
||||
@ -502,7 +525,7 @@ impl RpcServerConfig {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(RpcServer { http: http_server, ws: ws_server, ipc: ipc_server })
|
||||
Ok(RpcServer { local_addr, http: http_server, ws: ws_server, ipc: ipc_server })
|
||||
}
|
||||
}
|
||||
|
||||
@ -530,6 +553,21 @@ pub struct TransportRpcModuleConfig {
|
||||
// === impl TransportRpcModuleConfig ===
|
||||
|
||||
impl TransportRpcModuleConfig {
|
||||
/// Creates a new config with only http set
|
||||
pub fn http(http: impl Into<RpcModuleConfig>) -> Self {
|
||||
Self::default().with_http(http)
|
||||
}
|
||||
|
||||
/// Creates a new config with only ws set
|
||||
pub fn ws(ws: impl Into<RpcModuleConfig>) -> Self {
|
||||
Self::default().with_ws(ws)
|
||||
}
|
||||
|
||||
/// Creates a new config with only ipc set
|
||||
pub fn ipc(ipc: impl Into<RpcModuleConfig>) -> Self {
|
||||
Self::default().with_ipc(ipc)
|
||||
}
|
||||
|
||||
/// Sets the [RpcModuleConfig] for the http transport.
|
||||
pub fn with_http(mut self, http: impl Into<RpcModuleConfig>) -> Self {
|
||||
self.http = Some(http.into());
|
||||
@ -576,6 +614,8 @@ impl TransportRpcModules<()> {
|
||||
|
||||
/// Container type for each transport ie. http, ws, and ipc server
|
||||
pub struct RpcServer {
|
||||
/// The address of the http/ws server
|
||||
local_addr: Option<SocketAddr>,
|
||||
/// http server
|
||||
http: Option<Server>,
|
||||
/// ws server
|
||||
@ -587,6 +627,11 @@ pub struct RpcServer {
|
||||
// === impl RpcServer ===
|
||||
|
||||
impl RpcServer {
|
||||
/// Returns the [`SocketAddr`] of the http/ws server if started.
|
||||
fn local_addr(&self) -> Option<SocketAddr> {
|
||||
self.local_addr
|
||||
}
|
||||
|
||||
/// Starts the configured server by spawning the servers on the tokio runtime.
|
||||
///
|
||||
/// This returns an [RpcServerHandle] that's connected to the server task(s) until the server is
|
||||
@ -596,7 +641,8 @@ impl RpcServer {
|
||||
modules: TransportRpcModules<()>,
|
||||
) -> Result<RpcServerHandle, RpcError> {
|
||||
let TransportRpcModules { http, ws, ipc } = modules;
|
||||
let mut handle = RpcServerHandle { http: None, ws: None, ipc: None };
|
||||
let mut handle =
|
||||
RpcServerHandle { local_addr: self.local_addr, http: None, ws: None, ipc: None };
|
||||
|
||||
// Start all servers
|
||||
if let Some((server, module)) =
|
||||
@ -636,6 +682,8 @@ impl std::fmt::Debug for RpcServer {
|
||||
#[derive(Clone)]
|
||||
#[must_use = "Server stop if dropped"]
|
||||
pub struct RpcServerHandle {
|
||||
/// The address of the http/ws server
|
||||
local_addr: Option<SocketAddr>,
|
||||
http: Option<ServerHandle>,
|
||||
ws: Option<ServerHandle>,
|
||||
ipc: Option<ServerHandle>,
|
||||
@ -644,6 +692,11 @@ pub struct RpcServerHandle {
|
||||
// === impl RpcServerHandle ===
|
||||
|
||||
impl RpcServerHandle {
|
||||
/// Returns the [`SocketAddr`] of the http/ws server if started.
|
||||
fn local_addr(&self) -> Option<SocketAddr> {
|
||||
self.local_addr
|
||||
}
|
||||
|
||||
/// Tell the server to stop without waiting for the server to stop.
|
||||
pub fn stop(self) -> Result<(), RpcError> {
|
||||
if let Some(handle) = self.http {
|
||||
@ -660,6 +713,35 @@ impl RpcServerHandle {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the url to the http server
|
||||
pub fn http_url(&self) -> Option<String> {
|
||||
self.local_addr.map(|addr| format!("http://{addr}"))
|
||||
}
|
||||
|
||||
/// Returns the url to the ws server
|
||||
pub fn ws_url(&self) -> Option<String> {
|
||||
self.local_addr.map(|addr| format!("ws://{addr}"))
|
||||
}
|
||||
|
||||
/// Returns a http client connected to the server.
|
||||
pub fn http_client(&self) -> Option<jsonrpsee::http_client::HttpClient> {
|
||||
let url = self.http_url()?;
|
||||
let client = jsonrpsee::http_client::HttpClientBuilder::default()
|
||||
.build(url)
|
||||
.expect("Failed to create http client");
|
||||
Some(client)
|
||||
}
|
||||
|
||||
/// Returns a ws client connected to the server.
|
||||
pub async fn ws_client(&self) -> Option<jsonrpsee::ws_client::WsClient> {
|
||||
let url = self.ws_url()?;
|
||||
let client = jsonrpsee::ws_client::WsClientBuilder::default()
|
||||
.build(url)
|
||||
.await
|
||||
.expect("Failed to create ws client");
|
||||
Some(client)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for RpcServerHandle {
|
||||
|
||||
@ -1,8 +1,48 @@
|
||||
//! Standalone http tests
|
||||
|
||||
use crate::utils::test_rpc_builder;
|
||||
use crate::utils::{launch_http, launch_http_ws, launch_ws};
|
||||
use jsonrpsee::core::client::{ClientT, SubscriptionClientT};
|
||||
use reth_primitives::NodeRecord;
|
||||
use reth_rpc_api::clients::AdminApiClient;
|
||||
use reth_rpc_builder::RethRpcModule;
|
||||
|
||||
async fn test_basic_admin_calls<C>(client: &C)
|
||||
where
|
||||
C: ClientT + SubscriptionClientT + Sync,
|
||||
{
|
||||
let url = "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@10.3.58.6:30303?discport=30301";
|
||||
let node: NodeRecord = url.parse().unwrap();
|
||||
|
||||
AdminApiClient::add_peer(client, node).await.unwrap();
|
||||
AdminApiClient::remove_peer(client, node).await.unwrap();
|
||||
AdminApiClient::add_trusted_peer(client, node).await.unwrap();
|
||||
AdminApiClient::remove_trusted_peer(client, node).await.unwrap();
|
||||
AdminApiClient::node_info(client).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_launch_http() {
|
||||
let _builder = test_rpc_builder();
|
||||
async fn test_call_admin_functions_http() {
|
||||
reth_tracing::init_test_tracing();
|
||||
|
||||
let handle = launch_http(vec![RethRpcModule::Admin]).await;
|
||||
let client = handle.http_client().unwrap();
|
||||
test_basic_admin_calls(&client).await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_call_admin_functions_ws() {
|
||||
reth_tracing::init_test_tracing();
|
||||
|
||||
let handle = launch_ws(vec![RethRpcModule::Admin]).await;
|
||||
let client = handle.ws_client().await.unwrap();
|
||||
test_basic_admin_calls(&client).await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_call_admin_functions_http_and_ws() {
|
||||
reth_tracing::init_test_tracing();
|
||||
|
||||
let handle = launch_http_ws(vec![RethRpcModule::Admin]).await;
|
||||
let client = handle.http_client().unwrap();
|
||||
test_basic_admin_calls(&client).await;
|
||||
}
|
||||
|
||||
@ -1,8 +1,55 @@
|
||||
use reth_network_api::test_utils::NoopNetwork;
|
||||
use reth_provider::test_utils::NoopProvider;
|
||||
use reth_rpc_builder::RpcModuleBuilder;
|
||||
use reth_rpc_builder::{
|
||||
RpcModuleBuilder, RpcModuleConfig, RpcServerConfig, RpcServerHandle, TransportRpcModuleConfig,
|
||||
};
|
||||
use reth_transaction_pool::test_utils::{testing_pool, TestPool};
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
|
||||
/// Localhost with port 0 so a free port is used.
|
||||
pub fn test_address() -> SocketAddr {
|
||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0))
|
||||
}
|
||||
|
||||
/// Launches a new server with http only with the given modules
|
||||
pub async fn launch_http(modules: impl Into<RpcModuleConfig>) -> RpcServerHandle {
|
||||
let builder = test_rpc_builder();
|
||||
let server = builder.build(TransportRpcModuleConfig::http(modules));
|
||||
server
|
||||
.start_server(RpcServerConfig::http(Default::default()).with_address(test_address()))
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Launches a new server with ws only with the given modules
|
||||
pub async fn launch_ws(modules: impl Into<RpcModuleConfig>) -> RpcServerHandle {
|
||||
let builder = test_rpc_builder();
|
||||
let server = builder.build(TransportRpcModuleConfig::ws(modules));
|
||||
server
|
||||
.start_server(RpcServerConfig::ws(Default::default()).with_address(test_address()))
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Launches a new server with http and ws and with the given modules
|
||||
pub async fn launch_http_ws(modules: impl Into<RpcModuleConfig>) -> RpcServerHandle {
|
||||
let builder = test_rpc_builder();
|
||||
let modules = modules.into();
|
||||
let server = builder.build(TransportRpcModuleConfig::ws(modules.clone()).with_http(modules));
|
||||
server
|
||||
.start_server(
|
||||
RpcServerConfig::ws(Default::default())
|
||||
.with_http(Default::default())
|
||||
.with_address(test_address()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Returns an [RpcModuleBuilder] with testing components.
|
||||
pub fn test_rpc_builder() -> RpcModuleBuilder<NoopProvider, TestPool, ()> {
|
||||
RpcModuleBuilder::default().with_client(NoopProvider::default()).with_pool(testing_pool())
|
||||
pub fn test_rpc_builder() -> RpcModuleBuilder<NoopProvider, TestPool, NoopNetwork> {
|
||||
RpcModuleBuilder::default()
|
||||
.with_client(NoopProvider::default())
|
||||
.with_pool(testing_pool())
|
||||
.with_network(NoopNetwork::default())
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ where
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn subscribe(
|
||||
fn subscribe_peer_events(
|
||||
&self,
|
||||
_subscription_sink: jsonrpsee::SubscriptionSink,
|
||||
) -> jsonrpsee::types::SubscriptionResult {
|
||||
|
||||
Reference in New Issue
Block a user