feat(rpc): add EthSubscriptionIdProvider (#1721)

This commit is contained in:
Matthias Seitz
2023-03-13 03:36:19 +01:00
committed by GitHub
parent ff34c0ec2a
commit 29a2e1ab3a
5 changed files with 111 additions and 6 deletions

View File

@ -379,6 +379,7 @@ pub struct Builder<B = Identity, L = ()> {
settings: Settings,
resources: Resources,
logger: L,
/// Subscription ID provider.
id_provider: Arc<dyn IdProvider>,
service_builder: tower::ServiceBuilder<B>,
}

View File

@ -58,13 +58,15 @@ use jsonrpsee::{
server::{host_filtering::AllowHosts, rpc_module::Methods},
Error as RpcError,
},
server::{Server, ServerHandle},
server::{IdProvider, Server, ServerHandle},
RpcModule,
};
use reth_ipc::server::IpcServer;
use reth_network_api::{NetworkInfo, Peers};
use reth_provider::{BlockProvider, EvmEnvProvider, HeaderProvider, StateProviderFactory};
use reth_rpc::{AdminApi, DebugApi, EthApi, EthFilter, NetApi, TraceApi, Web3Api};
use reth_rpc::{
AdminApi, DebugApi, EthApi, EthFilter, EthSubscriptionIdProvider, NetApi, TraceApi, Web3Api,
};
use reth_rpc_api::servers::*;
use reth_transaction_pool::TransactionPool;
use serde::{Deserialize, Serialize, Serializer};
@ -670,11 +672,17 @@ impl RpcServerConfig {
pub fn ipc(config: IpcServerBuilder) -> Self {
Self::default().with_ipc(config)
}
/// Configures the http server
///
/// Note: this always configures an [EthSubscriptionIdProvider] [IdProvider] for convenience.
/// To set a custom [IdProvider], please use [Self::with_id_provider].
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().set_id_provider(EthSubscriptionIdProvider::default()));
self
}
/// Configure the corsdomains
pub fn with_cors(mut self, cors_domain: String) -> Self {
self.http_cors_domains = Some(cors_domain);
@ -682,8 +690,12 @@ impl RpcServerConfig {
}
/// Configures the ws server
///
/// Note: this always configures an [EthSubscriptionIdProvider] [IdProvider] for convenience.
/// To set a custom [IdProvider], please use [Self::with_id_provider].
pub fn with_ws(mut self, config: ServerBuilder) -> Self {
self.ws_server_config = Some(config.ws_only());
self.ws_server_config =
Some(config.ws_only().set_id_provider(EthSubscriptionIdProvider::default()));
self
}
@ -704,8 +716,31 @@ impl RpcServerConfig {
}
/// Configures the ipc server
///
/// Note: this always configures an [EthSubscriptionIdProvider] [IdProvider] for convenience.
/// To set a custom [IdProvider], please use [Self::with_id_provider].
pub fn with_ipc(mut self, mut config: IpcServerBuilder) -> Self {
self.ipc_server_config = Some(config);
self.ipc_server_config = Some(config.set_id_provider(EthSubscriptionIdProvider::default()));
self
}
/// Sets a custom [IdProvider] for all configured transports.
///
/// By default all transports use [EthSubscriptionIdProvider]
pub fn with_id_provider<I>(mut self, id_provider: I) -> Self
where
I: IdProvider + Clone + 'static,
{
if let Some(http) = self.http_server_config {
self.http_server_config = Some(http.set_id_provider(id_provider.clone()));
}
if let Some(ws) = self.ws_server_config {
self.ws_server_config = Some(ws.set_id_provider(id_provider.clone()));
}
if let Some(ipc) = self.ipc_server_config {
self.ipc_server_config = Some(ipc.set_id_provider(id_provider));
}
self
}

View File

@ -0,0 +1,67 @@
use jsonrpsee::types::SubscriptionId;
use std::fmt::Write;
/// An [IdProvider](jsonrpsee::core::traits::IdProvider) for ethereum subscription ids.
///
/// Returns new hex-string [QUANTITY](https://ethereum.org/en/developers/docs/apis/json-rpc/#quantities-encoding) ids
#[derive(Debug, Clone, Copy, Default)]
#[non_exhaustive]
pub struct EthSubscriptionIdProvider;
impl jsonrpsee::core::traits::IdProvider for EthSubscriptionIdProvider {
fn next_id(&self) -> SubscriptionId<'static> {
to_quantity(rand::random::<u128>())
}
}
/// Returns a hex quantity string for the given value
///
/// Strips all leading zeros, `0` is returned as `0x0`
#[inline(always)]
fn to_quantity(val: u128) -> SubscriptionId<'static> {
let bytes = val.to_be_bytes();
let b = bytes.as_slice();
let non_zero = b.iter().take_while(|b| **b == 0).count();
let b = &b[non_zero..];
if b.is_empty() {
return SubscriptionId::Str("0x0".into())
}
let mut id = String::with_capacity(2 * b.len() + 2);
id.push_str("0x");
let first_byte = b[0];
write!(id, "{first_byte:x}").unwrap();
for byte in &b[1..] {
write!(id, "{byte:02x}").unwrap();
}
id.into()
}
#[cfg(test)]
mod tests {
use super::*;
use reth_primitives::U128;
#[test]
fn test_id_provider_quantity() {
let id = to_quantity(0);
assert_eq!(id, SubscriptionId::Str("0x0".into()));
let id = to_quantity(1);
assert_eq!(id, SubscriptionId::Str("0x1".into()));
for _ in 0..1000 {
let val = rand::random::<u128>();
let id = to_quantity(val);
match id {
SubscriptionId::Str(id) => {
let from_hex: U128 = id.parse().unwrap();
assert_eq!(from_hex, U128::from(val));
}
SubscriptionId::Num(_) => {
unreachable!()
}
}
}
}
}

View File

@ -4,9 +4,11 @@ mod api;
pub mod cache;
pub(crate) mod error;
mod filter;
mod id_provider;
mod pubsub;
mod signer;
pub use api::{EthApi, EthApiSpec};
pub use filter::EthFilter;
pub use id_provider::EthSubscriptionIdProvider;
pub use pubsub::EthPubSub;

View File

@ -23,7 +23,7 @@ mod web3;
pub use admin::AdminApi;
pub use debug::DebugApi;
pub use engine::EngineApi;
pub use eth::{EthApi, EthApiSpec, EthFilter, EthPubSub};
pub use eth::{EthApi, EthApiSpec, EthFilter, EthPubSub, EthSubscriptionIdProvider};
pub use layers::{AuthLayer, AuthValidator, JwtAuthValidator, JwtError, JwtSecret};
pub use net::NetApi;
pub use trace::TraceApi;