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",
|
"jsonrpsee",
|
||||||
"reth-ipc",
|
"reth-ipc",
|
||||||
"reth-network-api",
|
"reth-network-api",
|
||||||
|
"reth-primitives",
|
||||||
"reth-provider",
|
"reth-provider",
|
||||||
"reth-rpc",
|
"reth-rpc",
|
||||||
"reth-rpc-api",
|
"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_primitives::NodeRecord;
|
||||||
use reth_rpc_types::NodeInfo;
|
use reth_rpc_types::NodeInfo;
|
||||||
|
|
||||||
/// Admin namespace rpc interface that gives access to several non-standard RPC methods.
|
/// 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]
|
#[async_trait::async_trait]
|
||||||
pub trait AdminApi {
|
pub trait AdminApi {
|
||||||
/// Adds the given node record to the peerset.
|
/// Adds the given node record to the peerset.
|
||||||
#[method(name = "admin_addPeer")]
|
#[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.
|
/// Disconnects from a remote node if the connection exists.
|
||||||
///
|
///
|
||||||
/// Returns true if the peer was successfully removed.
|
/// Returns true if the peer was successfully removed.
|
||||||
#[method(name = "admin_removePeer")]
|
#[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.
|
/// Adds the given node record to the trusted peerset.
|
||||||
#[method(name = "admin_addTrustedPeer")]
|
#[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
|
/// Removes a remote node from the trusted peer set, but it does not disconnect it
|
||||||
/// automatically.
|
/// automatically.
|
||||||
///
|
///
|
||||||
/// Returns true if the peer was successfully removed.
|
/// Returns true if the peer was successfully removed.
|
||||||
#[method(name = "admin_removeTrustedPeer")]
|
#[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.
|
/// Creates an RPC subscription which serves events received from the network.
|
||||||
#[subscription(
|
#[subscription(
|
||||||
@ -33,9 +34,9 @@ pub trait AdminApi {
|
|||||||
unsubscribe = "admin_peerEvents_unsubscribe",
|
unsubscribe = "admin_peerEvents_unsubscribe",
|
||||||
item = String
|
item = String
|
||||||
)]
|
)]
|
||||||
fn subscribe(&self);
|
fn subscribe_peer_events(&self);
|
||||||
|
|
||||||
/// Returns the ENR of the node.
|
/// Returns the ENR of the node.
|
||||||
#[method(name = "admin_nodeInfo")]
|
#[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,
|
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]
|
[dev-dependencies]
|
||||||
reth-tracing = { path = "../../tracing" }
|
reth-tracing = { path = "../../tracing" }
|
||||||
|
reth-primitives = { path = "../../primitives" }
|
||||||
reth-rpc-api = { path = "../rpc-api", features = ["client"] }
|
reth-rpc-api = { path = "../rpc-api", features = ["client"] }
|
||||||
reth-transaction-pool = { path = "../../transaction-pool", features = ["test-utils"] }
|
reth-transaction-pool = { path = "../../transaction-pool", features = ["test-utils"] }
|
||||||
reth-provider = { path = "../../storage/provider", 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 ===
|
||||||
|
|
||||||
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
|
/// Configures the http server
|
||||||
pub fn with_http(mut self, config: ServerBuilder) -> Self {
|
pub fn with_http(mut self, config: ServerBuilder) -> Self {
|
||||||
self.http_server_config = Some(config.http_only());
|
self.http_server_config = Some(config.http_only());
|
||||||
@ -478,15 +493,23 @@ impl RpcServerConfig {
|
|||||||
.http_ws_addr
|
.http_ws_addr
|
||||||
.unwrap_or(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, DEFAULT_RPC_PORT)));
|
.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 http_server = if let Some(builder) = self.http_server_config {
|
||||||
let server = builder.build(socket_addr).await?;
|
let server = builder.build(socket_addr).await?;
|
||||||
|
if local_addr.is_none() {
|
||||||
|
local_addr = server.local_addr().ok();
|
||||||
|
}
|
||||||
Some(server)
|
Some(server)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let ws_server = if let Some(builder) = self.ws_server_config {
|
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)
|
Some(server)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -502,7 +525,7 @@ impl RpcServerConfig {
|
|||||||
None
|
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 ===
|
||||||
|
|
||||||
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.
|
/// Sets the [RpcModuleConfig] for the http transport.
|
||||||
pub fn with_http(mut self, http: impl Into<RpcModuleConfig>) -> Self {
|
pub fn with_http(mut self, http: impl Into<RpcModuleConfig>) -> Self {
|
||||||
self.http = Some(http.into());
|
self.http = Some(http.into());
|
||||||
@ -576,6 +614,8 @@ impl TransportRpcModules<()> {
|
|||||||
|
|
||||||
/// Container type for each transport ie. http, ws, and ipc server
|
/// Container type for each transport ie. http, ws, and ipc server
|
||||||
pub struct RpcServer {
|
pub struct RpcServer {
|
||||||
|
/// The address of the http/ws server
|
||||||
|
local_addr: Option<SocketAddr>,
|
||||||
/// http server
|
/// http server
|
||||||
http: Option<Server>,
|
http: Option<Server>,
|
||||||
/// ws server
|
/// ws server
|
||||||
@ -587,6 +627,11 @@ pub struct RpcServer {
|
|||||||
// === impl RpcServer ===
|
// === impl 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.
|
/// 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
|
/// This returns an [RpcServerHandle] that's connected to the server task(s) until the server is
|
||||||
@ -596,7 +641,8 @@ impl RpcServer {
|
|||||||
modules: TransportRpcModules<()>,
|
modules: TransportRpcModules<()>,
|
||||||
) -> Result<RpcServerHandle, RpcError> {
|
) -> Result<RpcServerHandle, RpcError> {
|
||||||
let TransportRpcModules { http, ws, ipc } = modules;
|
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
|
// Start all servers
|
||||||
if let Some((server, module)) =
|
if let Some((server, module)) =
|
||||||
@ -636,6 +682,8 @@ impl std::fmt::Debug for RpcServer {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
#[must_use = "Server stop if dropped"]
|
#[must_use = "Server stop if dropped"]
|
||||||
pub struct RpcServerHandle {
|
pub struct RpcServerHandle {
|
||||||
|
/// The address of the http/ws server
|
||||||
|
local_addr: Option<SocketAddr>,
|
||||||
http: Option<ServerHandle>,
|
http: Option<ServerHandle>,
|
||||||
ws: Option<ServerHandle>,
|
ws: Option<ServerHandle>,
|
||||||
ipc: Option<ServerHandle>,
|
ipc: Option<ServerHandle>,
|
||||||
@ -644,6 +692,11 @@ pub struct RpcServerHandle {
|
|||||||
// === impl RpcServerHandle ===
|
// === impl 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.
|
/// Tell the server to stop without waiting for the server to stop.
|
||||||
pub fn stop(self) -> Result<(), RpcError> {
|
pub fn stop(self) -> Result<(), RpcError> {
|
||||||
if let Some(handle) = self.http {
|
if let Some(handle) = self.http {
|
||||||
@ -660,6 +713,35 @@ impl RpcServerHandle {
|
|||||||
|
|
||||||
Ok(())
|
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 {
|
impl std::fmt::Debug for RpcServerHandle {
|
||||||
|
|||||||
@ -1,8 +1,48 @@
|
|||||||
//! Standalone http tests
|
//! 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")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_launch_http() {
|
async fn test_call_admin_functions_http() {
|
||||||
let _builder = test_rpc_builder();
|
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_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 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.
|
/// Returns an [RpcModuleBuilder] with testing components.
|
||||||
pub fn test_rpc_builder() -> RpcModuleBuilder<NoopProvider, TestPool, ()> {
|
pub fn test_rpc_builder() -> RpcModuleBuilder<NoopProvider, TestPool, NoopNetwork> {
|
||||||
RpcModuleBuilder::default().with_client(NoopProvider::default()).with_pool(testing_pool())
|
RpcModuleBuilder::default()
|
||||||
|
.with_client(NoopProvider::default())
|
||||||
|
.with_pool(testing_pool())
|
||||||
|
.with_network(NoopNetwork::default())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,7 +46,7 @@ where
|
|||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscribe(
|
fn subscribe_peer_events(
|
||||||
&self,
|
&self,
|
||||||
_subscription_sink: jsonrpsee::SubscriptionSink,
|
_subscription_sink: jsonrpsee::SubscriptionSink,
|
||||||
) -> jsonrpsee::types::SubscriptionResult {
|
) -> jsonrpsee::types::SubscriptionResult {
|
||||||
|
|||||||
Reference in New Issue
Block a user