@@ -15,8 +15,7 @@ use alloy_eips::{BlockId, BlockNumberOrTag};
1515use alloy_json_rpc:: RpcObject ;
1616use alloy_primitives:: { B256 , U256 } ;
1717use alloy_rpc_types:: {
18- BlockTransactions , Filter , FilterChanges , FilterId , Log , PendingTransactionFilterKind ,
19- TransactionInfo ,
18+ BlockTransactions , Filter , FilterChanges , FilterId , Log , TransactionInfo ,
2019 pubsub:: { Params , SubscriptionKind } ,
2120} ;
2221use jsonrpsee:: { PendingSubscriptionSink , proc_macros:: rpc} ;
@@ -27,8 +26,8 @@ use reth_primitives_traits::SignedTransaction;
2726use reth_provider:: { BlockIdReader , BlockReader , BlockReaderIdExt , ReceiptProvider } ;
2827use reth_rpc:: { EthFilter , EthPubSub } ;
2928use 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} ;
3332use reth_rpc_eth_types:: EthApiError ;
3433use serde:: { Deserialize , Serialize } ;
@@ -261,88 +260,132 @@ where
261260pub struct HlNodeFilterHttp < Eth : EthWrapper > {
262261 filter : Arc < EthFilter < Eth > > ,
263262 provider : Arc < Eth :: Provider > ,
263+ default_compliant : bool ,
264264}
265265
266266impl < 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
319341pub 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
325348impl < 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 >
337377where
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