Skip to content

Commit 2ca2850

Browse files
authored
fix(rpc): return -32602 error for eth_getLogs with pending block tag
Reth resolves "pending" as head+1 for log queries and returns [] rather than coercing to "latest". Arc intentionally hides all pending-block state for public nodes, so surface this as an explicit -32602 Invalid params error instead of silently returning [], which makes callers think no logs exist at the current head. Changes: - Add ETH_GET_LOGS_METHOD and PENDING_LOGS_ERROR_CODE constants - Add pending_logs_error() that parses filter params for pending fromBlock/toBlock (positional and object-form; case-insensitive; decoupled from Alloy proc-macro names) - Wire into intercept_or_forward before the null-response path Fixes #426
1 parent 66ad2d5 commit 2ca2850

1 file changed

Lines changed: 47 additions & 0 deletions

File tree

‎crates/evm-node/src/rpc_middleware.rs‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ const UNPROTECTED_TX_ERROR_MSG: &str =
7070
"only replay-protected (EIP-155) transactions allowed over RPC";
7171
const RELAY_UNAVAILABLE_ERROR_CODE: i32 = -32010;
7272
const RELAY_UNAVAILABLE_ERROR_MSG: &str = "all transaction relay upstreams are unreachable";
73+
const ETH_GET_LOGS_METHOD: &str = "eth_getLogs";
74+
/// <https://eips.ethereum.org/EIPS/eip-1474> – Invalid params
75+
const PENDING_LOGS_ERROR_CODE: i32 = -32602;
7376

7477
/// Default maximum number of entries permitted in a JSON-RPC batch request.
7578
pub const ARC_RPC_MAX_BATCH_ENTRIES_DEFAULT: usize = 100;
@@ -315,6 +318,47 @@ where
315318
}
316319
}
317320

321+
/// Returns an error if the request is `eth_getLogs` with a `"pending"` block tag.
322+
///
323+
/// Reth resolves `"pending"` as `head + 1` for log queries and returns an empty
324+
/// result set rather than coercing to `"latest"`. Arc intentionally hides all
325+
/// pending-block state for public nodes, so surface this as an explicit
326+
/// `-32602 Invalid params` error instead of silently returning `[]`, which
327+
/// makes callers think no logs exist at the current head.
328+
fn pending_logs_error<'a>(req: &Request<'a>) -> Option<ErrorObject<'a>> {
329+
if req.method_name() != ETH_GET_LOGS_METHOD {
330+
return None;
331+
}
332+
// Parse the raw JSON rather than deserializing to alloy_rpc_types_eth::Filter
333+
// to stay decoupled from proc-macro field names that can change across Reth
334+
// versions. eth_getLogs accepts the filter either positionally ([{...}]) or
335+
// under the "filter" key in object-form params.
336+
let raw = req.params().as_str().unwrap_or("");
337+
let value: serde_json::Value = serde_json::from_str(raw).unwrap_or(serde_json::Value::Null);
338+
let filter = match &value {
339+
serde_json::Value::Array(arr) => arr.first().cloned(),
340+
serde_json::Value::Object(obj) => obj.get("filter").cloned(),
341+
_ => None,
342+
};
343+
let has_pending = |key_camel: &str, key_snake: &str| -> bool {
344+
filter
345+
.as_ref()
346+
.and_then(|f| f.get(key_camel).or_else(|| f.get(key_snake)))
347+
.and_then(|v| v.as_str())
348+
.map(|s| s.eq_ignore_ascii_case("pending"))
349+
.unwrap_or(false)
350+
};
351+
if has_pending("fromBlock", "from_block") || has_pending("toBlock", "to_block") {
352+
Some(ErrorObjectOwned::owned::<()>(
353+
PENDING_LOGS_ERROR_CODE,
354+
r#""pending" block tag is not supported for eth_getLogs on Arc; use "latest" instead"#,
355+
None,
356+
))
357+
} else {
358+
None
359+
}
360+
}
361+
318362
/// Intercepts pending-tx RPCs (error or null) or forwards to the inner service.
319363
async fn intercept_or_forward<'a, S>(service: &S, req: Request<'a>) -> MethodResponse
320364
where
@@ -323,6 +367,9 @@ where
323367
if let Err(err) = error_if_pending_tx_rpc(&req) {
324368
return MethodResponse::error(req.id(), err);
325369
}
370+
if let Some(err) = pending_logs_error(&req) {
371+
return MethodResponse::error(req.id(), err);
372+
}
326373
if is_pool_pending_tx_lookup(&req) || is_pending_block_query(&req) {
327374
return null_response(&req);
328375
}

0 commit comments

Comments
 (0)