Skip to content

Commit 0718e6b

Browse files
authored
Merge pull request #141 from hl-archive-node/fix/multiplex-compliance-logs
fix: honor per-request ?hl= in log filter and pubsub methods
2 parents 1e6194b + 6bfe827 commit 0718e6b

1 file changed

Lines changed: 113 additions & 62 deletions

File tree

  • src/addons/hl_node_compliance

src/addons/hl_node_compliance/rpc.rs

Lines changed: 113 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ use alloy_eips::{BlockId, BlockNumberOrTag};
1515
use alloy_json_rpc::RpcObject;
1616
use alloy_primitives::{B256, U256};
1717
use alloy_rpc_types::{
18-
BlockTransactions, Filter, FilterChanges, FilterId, Log, PendingTransactionFilterKind,
19-
TransactionInfo,
18+
BlockTransactions, Filter, FilterChanges, FilterId, Log, TransactionInfo,
2019
pubsub::{Params, SubscriptionKind},
2120
};
2221
use jsonrpsee::{PendingSubscriptionSink, proc_macros::rpc};
@@ -27,8 +26,8 @@ use reth_primitives_traits::SignedTransaction;
2726
use reth_provider::{BlockIdReader, BlockReader, BlockReaderIdExt, ReceiptProvider};
2827
use reth_rpc::{EthFilter, EthPubSub};
2928
use reth_rpc_eth_api::{
30-
EthApiTypes, EthFilterApiServer, EthPubSubApiServer, RpcBlock, RpcConvert, RpcReceipt,
31-
RpcTransaction, helpers::{EthBlocks, EthTransactions}, transaction::ConvertReceiptInput,
29+
EthApiTypes, EthFilterApiServer, RpcBlock, RpcConvert, RpcReceipt, RpcTransaction,
30+
helpers::{EthBlocks, EthTransactions}, transaction::ConvertReceiptInput,
3231
};
3332
use reth_rpc_eth_types::EthApiError;
3433
use serde::{Deserialize, Serialize};
@@ -261,88 +260,132 @@ where
261260
pub struct HlNodeFilterHttp<Eth: EthWrapper> {
262261
filter: Arc<EthFilter<Eth>>,
263262
provider: Arc<Eth::Provider>,
263+
default_compliant: bool,
264264
}
265265

266266
impl<Eth: EthWrapper> HlNodeFilterHttp<Eth> {
267-
pub fn new(filter: Arc<EthFilter<Eth>>, provider: Arc<Eth::Provider>) -> Self {
268-
Self { filter, provider }
267+
pub fn new(
268+
filter: Arc<EthFilter<Eth>>,
269+
provider: Arc<Eth::Provider>,
270+
default_compliant: bool,
271+
) -> Self {
272+
Self { filter, provider, default_compliant }
273+
}
274+
}
275+
276+
/// Per-request `?hl=`-aware overrides of the log-returning `eth_` filter methods. Other filter
277+
/// methods are left to the stock reth handler (`EthFilter` clones share state via `Arc`).
278+
#[rpc(server, namespace = "eth")]
279+
pub trait EthLogFilterApi<T: RpcObject> {
280+
#[method(name = "getLogs", with_extensions)]
281+
async fn logs(&self, filter: Filter) -> RpcResult<Vec<Log>>;
282+
283+
#[method(name = "getFilterLogs", with_extensions)]
284+
async fn filter_logs(&self, id: FilterId) -> RpcResult<Vec<Log>>;
285+
286+
#[method(name = "getFilterChanges", with_extensions)]
287+
async fn filter_changes(&self, id: FilterId) -> RpcResult<FilterChanges<T>>;
288+
}
289+
290+
fn adjust_filter_changes<Eth: EthWrapper>(
291+
changes: FilterChanges<RpcTransaction<Eth::NetworkTypes>>,
292+
provider: &Eth::Provider,
293+
) -> FilterChanges<RpcTransaction<Eth::NetworkTypes>> {
294+
match changes {
295+
FilterChanges::Logs(logs) => FilterChanges::Logs(
296+
logs.into_iter().filter_map(|log| adjust_log::<Eth>(log, provider)).collect(),
297+
),
298+
other => other,
269299
}
270300
}
271301

272302
#[async_trait]
273-
impl<Eth: EthWrapper> EthFilterApiServer<RpcTransaction<Eth::NetworkTypes>>
303+
impl<Eth: EthWrapper> EthLogFilterApiServer<RpcTransaction<Eth::NetworkTypes>>
274304
for HlNodeFilterHttp<Eth>
275305
{
276-
async fn new_filter(&self, filter: Filter) -> RpcResult<FilterId> {
277-
trace!(target: "rpc::eth", "Serving eth_newFilter");
278-
self.filter.new_filter(filter).await
279-
}
280-
281-
async fn new_block_filter(&self) -> RpcResult<FilterId> {
282-
trace!(target: "rpc::eth", "Serving eth_newBlockFilter");
283-
self.filter.new_block_filter().await
306+
async fn logs(&self, ext: &Extensions, filter: Filter) -> RpcResult<Vec<Log>> {
307+
trace!(target: "rpc::eth", "Serving eth_getLogs");
308+
let logs = EthFilterApiServer::logs(&*self.filter, filter).await?;
309+
if is_hl_compliant(ext, self.default_compliant) {
310+
Ok(logs.into_iter().filter_map(|log| adjust_log::<Eth>(log, &self.provider)).collect())
311+
} else {
312+
Ok(logs)
313+
}
284314
}
285315

286-
async fn new_pending_transaction_filter(
287-
&self,
288-
kind: Option<PendingTransactionFilterKind>,
289-
) -> RpcResult<FilterId> {
290-
trace!(target: "rpc::eth", "Serving eth_newPendingTransactionFilter");
291-
self.filter.new_pending_transaction_filter(kind).await
316+
async fn filter_logs(&self, ext: &Extensions, id: FilterId) -> RpcResult<Vec<Log>> {
317+
trace!(target: "rpc::eth", "Serving eth_getFilterLogs");
318+
let logs = self.filter.filter_logs(id).await.map_err(ErrorObject::from)?;
319+
if is_hl_compliant(ext, self.default_compliant) {
320+
Ok(logs.into_iter().filter_map(|log| adjust_log::<Eth>(log, &self.provider)).collect())
321+
} else {
322+
Ok(logs)
323+
}
292324
}
293325

294326
async fn filter_changes(
295327
&self,
328+
ext: &Extensions,
296329
id: FilterId,
297330
) -> RpcResult<FilterChanges<RpcTransaction<Eth::NetworkTypes>>> {
298331
trace!(target: "rpc::eth", "Serving eth_getFilterChanges");
299-
self.filter.filter_changes(id).await.map_err(ErrorObject::from)
300-
}
301-
302-
async fn filter_logs(&self, id: FilterId) -> RpcResult<Vec<Log>> {
303-
trace!(target: "rpc::eth", "Serving eth_getFilterLogs");
304-
self.filter.filter_logs(id).await.map_err(ErrorObject::from)
305-
}
306-
307-
async fn uninstall_filter(&self, id: FilterId) -> RpcResult<bool> {
308-
trace!(target: "rpc::eth", "Serving eth_uninstallFilter");
309-
self.filter.uninstall_filter(id).await
310-
}
311-
312-
async fn logs(&self, filter: Filter) -> RpcResult<Vec<Log>> {
313-
trace!(target: "rpc::eth", "Serving eth_getLogs");
314-
let logs = EthFilterApiServer::logs(&*self.filter, filter).await?;
315-
Ok(logs.into_iter().filter_map(|log| adjust_log::<Eth>(log, &self.provider)).collect())
332+
let changes = self.filter.filter_changes(id).await.map_err(ErrorObject::from)?;
333+
if is_hl_compliant(ext, self.default_compliant) {
334+
Ok(adjust_filter_changes::<Eth>(changes, &self.provider))
335+
} else {
336+
Ok(changes)
337+
}
316338
}
317339
}
318340

319341
pub struct HlNodeFilterWs<Eth: EthWrapper> {
320342
pubsub: Arc<EthPubSub<Eth>>,
321343
provider: Arc<Eth::Provider>,
322344
subscription_task_spawner: Box<dyn TaskSpawner + 'static>,
345+
default_compliant: bool,
323346
}
324347

325348
impl<Eth: EthWrapper> HlNodeFilterWs<Eth> {
326349
pub fn new(
327350
pubsub: Arc<EthPubSub<Eth>>,
328351
provider: Arc<Eth::Provider>,
329352
subscription_task_spawner: Box<dyn TaskSpawner + 'static>,
353+
default_compliant: bool,
330354
) -> Self {
331-
Self { pubsub, provider, subscription_task_spawner }
355+
Self { pubsub, provider, subscription_task_spawner, default_compliant }
332356
}
333357
}
334358

359+
/// Per-request `?hl=`-aware `eth_subscribe`; only `logs` subscriptions are filtered.
360+
#[rpc(server, namespace = "eth")]
361+
pub trait EthHlPubSubApi {
362+
#[subscription(
363+
name = "subscribe" => "subscription",
364+
unsubscribe = "unsubscribe",
365+
item = alloy_rpc_types::pubsub::SubscriptionResult,
366+
with_extensions
367+
)]
368+
async fn subscribe(
369+
&self,
370+
kind: SubscriptionKind,
371+
params: Option<Params>,
372+
) -> jsonrpsee::core::SubscriptionResult;
373+
}
374+
335375
#[async_trait]
336-
impl<Eth: EthWrapper> EthPubSubApiServer<RpcTransaction<Eth::NetworkTypes>> for HlNodeFilterWs<Eth>
376+
impl<Eth: EthWrapper> EthHlPubSubApiServer for HlNodeFilterWs<Eth>
337377
where
338378
jsonrpsee_types::error::ErrorObject<'static>: From<<Eth as EthApiTypes>::Error>,
339379
{
340380
async fn subscribe(
341381
&self,
342382
pending: PendingSubscriptionSink,
383+
ext: &Extensions,
343384
kind: SubscriptionKind,
344385
params: Option<Params>,
345386
) -> jsonrpsee::core::SubscriptionResult {
387+
// resolve before spawning; `ext` is borrowed
388+
let compliant = is_hl_compliant(ext, self.default_compliant);
346389
let sink = pending.accept().await?;
347390
let (pubsub, provider) = (self.pubsub.clone(), self.provider.clone());
348391
self.subscription_task_spawner.spawn(Box::pin(async move {
@@ -352,11 +395,17 @@ where
352395
Some(Params::Bool(_)) => return,
353396
_ => Default::default(),
354397
};
355-
let _ = pipe_from_stream(
356-
sink,
357-
pubsub.log_stream(filter).filter_map(|log| adjust_log::<Eth>(log, &provider)),
358-
)
359-
.await;
398+
if compliant {
399+
let _ = pipe_from_stream(
400+
sink,
401+
pubsub
402+
.log_stream(filter)
403+
.filter_map(|log| adjust_log::<Eth>(log, &provider)),
404+
)
405+
.await;
406+
} else {
407+
let _ = pipe_from_stream(sink, pubsub.log_stream(filter)).await;
408+
}
360409
} else if kind == SubscriptionKind::NewHeads {
361410
let _ = pipe_from_stream(sink, new_headers_stream::<Eth>(&provider)).await;
362411
} else {
@@ -767,23 +816,25 @@ where
767816
EthApi: EthWrapper,
768817
ErrorObject<'static>: From<EthApi::Error>,
769818
{
770-
if default_compliant {
771-
ctx.modules.replace_configured(
772-
HlNodeFilterHttp::new(
773-
Arc::new(ctx.registry.eth_handlers().filter.clone()),
774-
Arc::new(ctx.registry.eth_api().provider().clone()),
775-
)
776-
.into_rpc(),
777-
)?;
778-
ctx.modules.replace_configured(
779-
HlNodeFilterWs::new(
780-
Arc::new(ctx.registry.eth_handlers().pubsub.clone()),
781-
Arc::new(ctx.registry.eth_api().provider().clone()),
782-
Box::new(ctx.node().task_executor().clone()),
783-
)
784-
.into_rpc(),
785-
)?;
786-
}
819+
// Installed unconditionally so `?hl=` works in both directions; absent the extension,
820+
// `is_hl_compliant` falls back to `default_compliant`.
821+
ctx.modules.replace_configured(
822+
HlNodeFilterHttp::new(
823+
Arc::new(ctx.registry.eth_handlers().filter.clone()),
824+
Arc::new(ctx.registry.eth_api().provider().clone()),
825+
default_compliant,
826+
)
827+
.into_rpc(),
828+
)?;
829+
ctx.modules.replace_configured(
830+
HlNodeFilterWs::new(
831+
Arc::new(ctx.registry.eth_handlers().pubsub.clone()),
832+
Arc::new(ctx.registry.eth_api().provider().clone()),
833+
Box::new(ctx.node().task_executor().clone()),
834+
default_compliant,
835+
)
836+
.into_rpc(),
837+
)?;
787838

788839
ctx.modules.replace_configured(
789840
HlNodeBlockFilterHttp::new(Arc::new(ctx.registry.eth_api().clone()), default_compliant)

0 commit comments

Comments
 (0)