Skip to content

fix(jsonrpc): enforce log filter cap and improve match efficiency#71

Closed
317787106 wants to merge 23 commits into
developfrom
hotfix/fix_newFilter
Closed

fix(jsonrpc): enforce log filter cap and improve match efficiency#71
317787106 wants to merge 23 commits into
developfrom
hotfix/fix_newFilter

Conversation

@317787106

@317787106 317787106 commented Apr 16, 2026

Copy link
Copy Markdown
Owner

Problem

Three issues exist in the JSON-RPC log filter subsystem (eth_newFilter / eth_getLogs), reported in tronprotocol#6510:

  1. Unbounded memory growtheth_newFilter imposes no limit on the number of active filters held in memory. A client can register filters indefinitely and eventually exhaust heap on the node.

  2. Correctness bugLogMatch.matchBlockOneByOne evaluated the MAX_RESULT guard after addAll, so the result list could transiently contain more than MAX_RESULT entries before the exception was thrown.

  3. Performance bottleneck under high filter counthandleLogsFilter iterated the filter map with an Iterator and called result.add() once per element, which is slow and contention-prone when thousands of filters are active.


Changes

1. Enforce a configurable cap on active log filters

Adds node.jsonrpc.maxLogFilterNum (default: 20000). When the cap is reached, eth_newFilter immediately returns JsonRpcExceedLimitException (JSON-RPC code -32005) instead of growing without bound.

node {
  jsonrpc {
    # Maximum number of concurrent eth_newFilter registrations (0 = unlimited)
    maxLogFilterNum = 20000
  }
}

2. Fix the MAX_RESULT boundary check in LogMatch.matchBlockOneByOne (correctness)

Moves the size + matchedLog.size() > MAX_RESULT guard before addAll. The result list now never exceeds MAX_RESULT entries regardless of how many logs a single block contributes.

3. Optimize handleLogsFilter for large filter maps

Condition Before After
filter count ≤ 10,000 (common case) Iterator loop, result.add() per element ConcurrentHashMap.forEach with local list + single addAll
filter count > 10,000 same slow path bounded ForkJoinPool(2) + parallelStream

Key improvements:

  • Reduced lock contention: elements matched per filter are collected into a local list first, then inserted with a single addAll on the shared LinkedBlockingQueue.
  • Bounded parallelism: the ForkJoinPool is created once and reused (named logs-filter-pool for thread-dump visibility), avoiding unbounded thread creation under spike load.
  • Observability: a logger.debug timing line records dispatch cost and filter-map size.

Testing

  • Unit tests (new):
    • HandleLogsFilterTest — 7 cases covering event dispatch, block-range filtering, expired-filter removal, and solidified/non-solidified routing
    • LogMatchOverLimitTest — 4 cases covering under-limit, exact-limit, exceed-limit (verifies exception is thrown before addAll), and empty-block skip
    • WalletCursorTest.testNewFilter_exceedsCapThrowsException — verifies the cap throws the correct exception with the limit value in the message
  • Manual testing

Closes tronprotocol#6510

Summary by CodeRabbit

  • New Features

    • eth_newFilter now enforces a configurable limit on concurrent log filters (default: 20,000); returns JSON-RPC error -32005 when exceeded.
  • Bug Fixes

    • Fixed result count validation in log matching to prevent exceeding limits.
  • Performance

    • Optimized log filter processing for improved throughput.

@coderabbitai

coderabbitai Bot commented Apr 16, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a configurable limit for JSON‑RPC log filters (default 20000), enforces it in eth_newFilter, parallelizes log-filter matching using a small ForkJoinPool, tightens result-size enforcement during matching, and updates configs and tests to exercise and clean up the new behavior.

Changes

Cohort / File(s) Summary
Configuration & Parameters
common/src/main/java/org/tron/common/parameter/CommonParameter.java, common/src/main/java/org/tron/core/config/args/NodeConfig.java, common/src/main/resources/reference.conf, framework/src/main/resources/config.conf, framework/src/main/java/org/tron/core/config/args/Args.java
Introduce jsonRpcMaxLogFilterNum / maxLogFilterNum (default 20000); Args.applyNodeConfig copies JSON‑RPC limit into runtime parameter.
JSON‑RPC API Surface
framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java
eth_newFilter now declares JsonRpcExceedLimitException and documents error code -32005.
JSON‑RPC Implementation & Matching Logic
framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java, framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.java, framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java
handleLogsFilter refactored to optionally run partitions in parallel via a dedicated ForkJoinPool(2); per-entry processing extracted; newFilter enforces max filter count and throws JsonRpcExceedLimitException; matchBlockOneByOne verifies result limit before appending; generateFilterId() reuses a shared SecureRandom; LOGS_FILTER_POOL is shutdown in close().
Tests — new & updated
framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java, framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java, framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java
Added tests exercising parallel/sequential handling, expiry removal, result-limit behavior; WalletCursor tests now close TronJsonRpcImpl; new test verifies eth_newFilter rejects when cap exceeded.
Test Configs
framework/src/test/resources/config-localtest.conf, framework/src/test/resources/config-test-mainnet.conf, framework/src/test/resources/config-test.conf
Added commented maxLogFilterNum = 20000 entries to test config files.

Sequence Diagram(s)

sequenceDiagram
    participant Block as BlockProcessor
    participant FilterSvc as TronJsonRpcImpl
    participant Pool as ForkJoinPool(2)
    participant Matcher as LogMatch
    participant Cache as FilterResultCache

    Block->>FilterSvc: handleLogsFilter(filterCapsule)
    FilterSvc->>FilterSvc: collect active filters
    alt many filters (>= threshold)
        FilterSvc->>Pool: submit partitions of filters
        Pool->>Matcher: process partition A (expiry, bloom, match)
        Pool->>Matcher: process partition B (expiry, bloom, match)
        Matcher->>Cache: append matched results (bulk)
        Pool-->>FilterSvc: tasks complete
    else few filters
        FilterSvc->>Matcher: sequentially process filters
        Matcher->>Cache: append matched results
    end
    FilterSvc->>FilterSvc: log elapsed time & active filter count
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hopped through filters, two threads at play,
Caps set at twenty‑thousand to keep chaos away,
Bloom checks and caches, results kept neat,
Parallel paws pounding — matching can't be beat!
🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(jsonrpc): enforce log filter cap and improve match efficiency' directly and concisely summarizes the two main changes: enforcing a log filter cap and improving matching efficiency through parallelization.
Linked Issues check ✅ Passed The PR successfully implements all primary coding objectives from issue #6510: adds configurable maxLogFilterNum cap with JsonRpcExceedLimitException (prevents unbounded growth), optimizes handleLogsFilter with controlled parallelism using bounded ForkJoinPool (2 threads), fixes LogMatch boundary check, and includes comprehensive unit tests.
Out of Scope Changes check ✅ Passed All changes are within scope of issue #6510. The JsonRpcApiUtil optimization (shared SecureRandom) is a minor performance improvement supporting the parallelization work and does not introduce unrelated functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch hotfix/fix_newFilter

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 10 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java">

<violation number="1" location="framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java:236">
P0: The `ForkJoinPool` is created on every `handleLogsFilter` call but never shut down. This leaks worker threads on each invocation. Either shut down the pool in a `finally` block after `join()`, or make it a static/instance-level field that is reused across calls.</violation>

<violation number="2" location="framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java:1431">
P2: Inconsistent boundary check: block filter uses `>=` but log filter uses `>`, allowing one extra filter beyond `maxLogFilterNum`. Use `>=` to match the block filter behavior.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java Outdated
Comment thread framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java Outdated
@317787106 317787106 changed the base branch from release_v4.8.1 to develop April 23, 2026 08:31
@317787106 317787106 force-pushed the hotfix/fix_newFilter branch from 20f0cc1 to 9897ea8 Compare April 23, 2026 08:31

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java (1)

251-291: Add a clarifying comment for ConcurrentHashMap iteration and removal.

Line 257's eventFilterMap.remove(entry.getKey()) during iteration is safe for ConcurrentHashMap (weakly consistent iterator), but consider adding a brief inline comment explaining this is intentional and safe—preventing future contributors from mistakenly porting this pattern to a non-concurrent collection.

The concurrent mutation concern at line 290 is mitigated: LogFilterAndResult.result is a LinkedBlockingQueue, so concurrent addAll() calls from ForkJoinPool workers and drainTo() calls from RPC reader threads are both thread-safe.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java`
around lines 251 - 291, The code in processLogFilterEntry calls
eventFilterMap.remove(entry.getKey()) while iterating a ConcurrentHashMap; add a
concise inline comment next to that remove explaining this is intentional and
safe because eventFilterMap is a ConcurrentHashMap with a weakly-consistent
iterator (so removals during iteration are allowed), and warn contributors not
to copy this pattern to non-concurrent maps; also add a short comment by the
getResult().addAll(...) call noting that LogFilterAndResult.getResult() is a
LinkedBlockingQueue so concurrent addAll() from ForkJoinPool workers and
drainTo() from RPC reader threads is thread-safe.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@framework/src/main/java/org/tron/core/config/args/Args.java`:
- Around line 268-271: When reading ConfigKey.NODE_JSONRPC_MAX_LOG_FILTER_NUM in
Args (setting PARAMETER.jsonRpcMaxLogFilterNum), validate the value is a
positive integer (>0) and reject non-positive values; if invalid, log an error
and fail fast (throw an IllegalArgumentException or exit) so the node won't
start with zero/negative maxLogFilterNum which breaks TronJsonRpcImpl's
eth_newFilter logic (see TronJsonRpcImpl.java:1443). Locate the assignment in
Args and add the check immediately after config.getInt(...), referencing
PARAMETER.jsonRpcMaxLogFilterNum for the value and ensuring a clear error
message that includes the invalid value.

In `@framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java`:
- Around line 228-249: The current per-block timing log in handleLogsFilter uses
logger.info which floods INFO logs; change it to logger.debug (or wrap with
logger.isDebugEnabled()) so timing is only emitted at DEBUG level, or
alternatively emit at INFO only when (t2 - t1) exceeds a chosen threshold;
update the call that currently uses logger.info(...) to use logger.debug(...) or
add the conditional check and keep the same message, referencing
handleLogsFilter, logger.info, logger.debug, and the timing variables t1/t2 and
eventFilterMap.size().
- Around line 174-175: The static LOGS_FILTER_POOL is created with new
ForkJoinPool(2) while the PR text said parallelism 3 and it is never shut down,
causing thread leaks; reconcile the intended parallelism (use 3 or make it
configurable using available processors), replace the hardcoded new
ForkJoinPool(2) by the chosen value (or a supplier/config param) and ensure the
pool is properly shutdown in the class close() method alongside sectionExecutor
(call LOGS_FILTER_POOL.shutdown() and await termination or forceShutdown as
appropriate); reference FILTER_PARALLEL_THRESHOLD, LOGS_FILTER_POOL, close(),
and sectionExecutor when making these changes.
- Around line 1443-1446: The size() check on eventFilter2Result and the
subsequent put() can race under concurrent eth_newFilter calls; change the
insertion to an atomic compute/computeIfAbsent or use eventFilter2Result.compute
to both test and insert in one atomic operation (referencing eventFilter2Result,
maxLogFilterNum, and the eth_newFilter insertion logic) so that you only
create/accept a new filter if the total stays <= maxLogFilterNum; alternatively,
perform the put and then immediately re-check the map size and remove/rollback
the newly inserted entry if the cap is exceeded to enforce the limit atomically.

---

Nitpick comments:
In `@framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java`:
- Around line 251-291: The code in processLogFilterEntry calls
eventFilterMap.remove(entry.getKey()) while iterating a ConcurrentHashMap; add a
concise inline comment next to that remove explaining this is intentional and
safe because eventFilterMap is a ConcurrentHashMap with a weakly-consistent
iterator (so removals during iteration are allowed), and warn contributors not
to copy this pattern to non-concurrent maps; also add a short comment by the
getResult().addAll(...) call noting that LogFilterAndResult.getResult() is a
LinkedBlockingQueue so concurrent addAll() from ForkJoinPool workers and
drainTo() from RPC reader threads is thread-safe.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2f546df6-e48d-4a43-8ae7-6129380594c1

📥 Commits

Reviewing files that changed from the base of the PR and between f0a8f0f and 9897ea8.

📒 Files selected for processing (11)
  • common/src/main/java/org/tron/common/parameter/CommonParameter.java
  • framework/src/main/java/org/tron/core/config/args/Args.java
  • framework/src/main/java/org/tron/core/config/args/ConfigKey.java
  • framework/src/main/java/org/tron/core/db/Manager.java
  • framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java
  • framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java
  • framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.java
  • framework/src/main/resources/config.conf
  • framework/src/test/resources/config-localtest.conf
  • framework/src/test/resources/config-test-mainnet.conf
  • framework/src/test/resources/config-test.conf

Comment on lines +268 to +271
if (config.hasPath(ConfigKey.NODE_JSONRPC_MAX_LOG_FILTER_NUM)) {
PARAMETER.jsonRpcMaxLogFilterNum =
config.getInt(ConfigKey.NODE_JSONRPC_MAX_LOG_FILTER_NUM);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect all uses of the new JSON-RPC log filter cap and related tests.
# Expectation: Enforcement assumes a strictly positive cap, and tests cover zero/negative config values if allowed.

rg -n -C4 '\bjsonRpcMaxLogFilterNum\b|getJsonRpcMaxLogFilterNum|NODE_JSONRPC_MAX_LOG_FILTER_NUM|eth_newFilter|newFilter\s*\(' --glob '*.java'

Repository: 317787106/java-tron

Length of output: 17055


🏁 Script executed:

#!/bin/bash
# Find where maxLogFilterNum is compared/used in enforcement logic
rg -n -B2 -A2 'maxLogFilterNum\s*[<>]=?\s*|<\s*maxLogFilterNum|>\s*maxLogFilterNum' --glob '*.java'

Repository: 317787106/java-tron

Length of output: 45


🏁 Script executed:

#!/bin/bash
# Broader search for all usages of maxLogFilterNum variable
rg -n 'maxLogFilterNum' --glob '*.java' -A3 -B1

Repository: 317787106/java-tron

Length of output: 2332


🏁 Script executed:

#!/bin/bash
# Verify the exact context around lines 268-271 in Args.java
sed -n '260,280p' framework/src/main/java/org/tron/core/config/args/Args.java

Repository: 317787106/java-tron

Length of output: 789


Add validation for maxLogFilterNum to reject non-positive values.

Line 270 accepts zero or negative values, which would break all eth_newFilter calls due to the check at TronJsonRpcImpl.java:1443 (if (eventFilter2Result.size() >= maxLogFilterNum)). Validate that the parameter is positive when loading config to fail fast.

Proposed validation
     if (config.hasPath(ConfigKey.NODE_JSONRPC_MAX_LOG_FILTER_NUM)) {
-      PARAMETER.jsonRpcMaxLogFilterNum =
-          config.getInt(ConfigKey.NODE_JSONRPC_MAX_LOG_FILTER_NUM);
+      int maxLogFilterNum = config.getInt(ConfigKey.NODE_JSONRPC_MAX_LOG_FILTER_NUM);
+      if (maxLogFilterNum <= 0) {
+        throw new IllegalArgumentException(
+            ConfigKey.NODE_JSONRPC_MAX_LOG_FILTER_NUM + " must be greater than 0");
+      }
+      PARAMETER.jsonRpcMaxLogFilterNum = maxLogFilterNum;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/src/main/java/org/tron/core/config/args/Args.java` around lines 268
- 271, When reading ConfigKey.NODE_JSONRPC_MAX_LOG_FILTER_NUM in Args (setting
PARAMETER.jsonRpcMaxLogFilterNum), validate the value is a positive integer (>0)
and reject non-positive values; if invalid, log an error and fail fast (throw an
IllegalArgumentException or exit) so the node won't start with zero/negative
maxLogFilterNum which breaks TronJsonRpcImpl's eth_newFilter logic (see
TronJsonRpcImpl.java:1443). Locate the assignment in Args and add the check
immediately after config.getInt(...), referencing
PARAMETER.jsonRpcMaxLogFilterNum for the value and ensuring a clear error
message that includes the invalid value.

Comment thread framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java Outdated
Comment thread framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java Outdated
Comment on lines +1443 to +1446
if (eventFilter2Result.size() >= maxLogFilterNum) {
throw new JsonRpcExceedLimitException(
"exceed max log filters: " + maxLogFilterNum + ", try again later");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

TOCTOU race: size check is not atomic with insert.

size() check on line 1443 and put() on line 1450 are not performed atomically on the ConcurrentHashMap. Under concurrent eth_newFilter requests, multiple callers can observe size() < maxLogFilterNum simultaneously and all proceed to insert, exceeding the configured cap. Given the default cap is 20,000 this is a soft limit abuse concern rather than a correctness bug, but it defeats the purpose of the cap under burst load.

Consider using compute/computeIfAbsent pattern, or re-check size after put and rollback if exceeded:

🛡️ Example hardening
-    if (eventFilter2Result.size() >= maxLogFilterNum) {
-      throw new JsonRpcExceedLimitException(
-          "exceed max log filters: " + maxLogFilterNum + ", try again later");
-    }
     long currentMaxFullNum = wallet.getNowBlock().getBlockHeader().getRawData().getNumber();
     LogFilterAndResult logFilterAndResult = new LogFilterAndResult(fr, currentMaxFullNum, wallet);
     String filterID = generateFilterId();
-    eventFilter2Result.put(filterID, logFilterAndResult);
+    if (eventFilter2Result.size() >= maxLogFilterNum) {
+      throw new JsonRpcExceedLimitException(
+          "exceed max log filters: " + maxLogFilterNum + ", try again later");
+    }
+    eventFilter2Result.put(filterID, logFilterAndResult);
+    if (eventFilter2Result.size() > maxLogFilterNum) {
+      eventFilter2Result.remove(filterID);
+      throw new JsonRpcExceedLimitException(
+          "exceed max log filters: " + maxLogFilterNum + ", try again later");
+    }
     return ByteArray.toJsonHex(filterID);

The same concern applies to newBlockFilter (lines 1465-1468), which is preexisting and out of scope but worth noting for a follow-up.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java`
around lines 1443 - 1446, The size() check on eventFilter2Result and the
subsequent put() can race under concurrent eth_newFilter calls; change the
insertion to an atomic compute/computeIfAbsent or use eventFilter2Result.compute
to both test and insert in one atomic operation (referencing eventFilter2Result,
maxLogFilterNum, and the eth_newFilter insertion logic) so that you only
create/accept a new filter if the total stays <= maxLogFilterNum; alternatively,
perform the put and then immediately re-check the map size and remove/rollback
the newly inserted entry if the cap is exceeded to enforce the limit atomically.

@317787106 317787106 changed the title hotfix(jsonrpc): restrict the newFilter size fix(jsonrpc): restrict the newFilter size Apr 23, 2026
@317787106 317787106 changed the title fix(jsonrpc): restrict the newFilter size fix(jsonrpc): enforce active log filter cap and fix EventBloomException Apr 23, 2026
Comment thread framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java Outdated

private static final String ERROR_SELECTOR = "08c379a0"; // Function selector for Error(string)
private static final int FILTER_PARALLEL_THRESHOLD = 10000;
private static final ForkJoinPool LOGS_FILTER_POOL = new ForkJoinPool();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[SHOULD] Static LOGS_FILTER_POOL shut down by instance close() breaks reuse

LOGS_FILTER_POOL is private static final, but close() (line 1598) calls shutdownAndAwaitTermination on it. If any instance of TronJsonRpcImpl is closed (Spring @DirtiesContext integration tests, or any future lifecycle that recreates the bean), subsequent handleLogsFilter calls that hit the parallel branch will throw RejectedExecutionException — the class-level pool is permanently dead after the first close. Separately, shutdownAndAwaitTermination(LOGS_FILTER_POOL, "") passes an empty name, so the log line becomes Pool shutdown... (two spaces), which is useless for operators.

Suggestion: Either make the pool an instance field (constructed in the constructor, torn down in close()), or keep it static and stop shutting it down from close() — use daemon worker threads so the JVM can exit cleanly. Pass a meaningful name (e.g. "jsonrpc-logs-filter-pool") either way.

Comment thread framework/src/main/java/org/tron/core/db/Manager.java Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java (1)

88-94: ⚠️ Potential issue | 🟡 Minor

close() is unreachable on the happy path of this test; move to finally.

This test asserts that buildTransaction throws because the cursor is SOLIDITY (see Line 92-93). When the test passes as intended, line 90 is never executed, so the TronJsonRpcImpl instance — and the static LOGS_FILTER_POOL it owns — is never cleaned up. If the test ever regresses to a non-throwing path, close() will run and (as noted on TronJsonRpcImpl.java Line 175) shut down the static pool for the rest of the JVM.

Move the cleanup into a finally block so it’s unconditional, and co-locate it with the instance lifecycle:

🧹 Proposed fix
     TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, dbManager);
     try {
       tronJsonRpc.buildTransaction(buildArguments);
-      tronJsonRpc.close();
     } catch (Exception e) {
       Assert.assertEquals("the method buildTransaction does not exist/is not available in "
           + "SOLIDITY", e.getMessage());
+    } finally {
+      try {
+        tronJsonRpc.close();
+      } catch (IOException ioe) {
+        // ignore on teardown
+      }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java` around
lines 88 - 94, The test currently calls tronJsonRpc.close() only in the try path
so the TronJsonRpcImpl instance and its static LOGS_FILTER_POOL may not be
cleaned up when buildTransaction throws; move the close() call into a finally
block and ensure the tronJsonRpc variable is declared/initialized outside the
try so it can be closed unconditionally (i.e., surround the try/catch with try {
... } catch(...) { ... } finally { tronJsonRpc.close(); }) referencing
buildTransaction and close on the TronJsonRpcImpl instance used in
WalletCursorTest.
♻️ Duplicate comments (1)
framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java (1)

175-175: ⚠️ Potential issue | 🟠 Major

Static LOGS_FILTER_POOL permanently dies after first close().

LOGS_FILTER_POOL is private static final (Line 175) but close() shuts it down on every instance tear-down (Line 1598). After the first instance is closed, the class-scoped pool is terminated for the lifetime of the JVM, and any subsequent TronJsonRpcImpl that hits the parallel branch in handleLogsFilter will throw RejectedExecutionException at LOGS_FILTER_POOL.submit(...).

This is now actively triggerable: WalletCursorTest.testEnableInFullNode and testDisableInSolidity have been updated in this PR to call tronJsonRpc.close(), so test suites that construct multiple instances in the same JVM (Spring @DirtiesContext, multiple @Test methods, or any lifecycle that recreates the bean) will leave the class in a broken state.

Pick one of the two ownership models:

Option A — keep pool static; stop shutting it down from instance close()
-  private static final ForkJoinPool LOGS_FILTER_POOL = new ForkJoinPool(2);
+  private static final ForkJoinPool LOGS_FILTER_POOL = new ForkJoinPool(
+      2,
+      pool -> {
+        ForkJoinWorkerThread w = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
+        w.setName("jsonrpc-logs-filter-" + w.getPoolIndex());
+        w.setDaemon(true);
+        return w;
+      },
+      null,
+      false);
   public void close() throws IOException {
-    ExecutorServiceManager.shutdownAndAwaitTermination(LOGS_FILTER_POOL, "logs-filter-pool");
     logElementCache.invalidateAll();
     blockHashCache.invalidateAll();
     ExecutorServiceManager.shutdownAndAwaitTermination(sectionExecutor, esName);
   }
Option B — make the pool an instance field tied to the bean lifecycle
-  private static final ForkJoinPool LOGS_FILTER_POOL = new ForkJoinPool(2);
+  private final ForkJoinPool logsFilterPool = new ForkJoinPool(2);

Then update handleLogsFilter / processLogFilterEntry to be instance methods (or pass the pool in), and keep the existing shutdown in close(). Note this also requires rethinking the static callers in Manager/event dispatch.

Option A is the minimal fix and matches the intent that handleLogsFilter is a static class-level service.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java`
at line 175, The static LOGS_FILTER_POOL is being shut down in the instance
close() causing RejectedExecutionException for subsequent instances; to fix,
adopt Option A: treat LOGS_FILTER_POOL as a static shared resource and remove
(or guard against) any shutdown/close logic from TronJsonRpcImpl.close() that
calls LOGS_FILTER_POOL.shutdown()/shutdownNow()/close; ensure handleLogsFilter
and processLogFilterEntry continue to submit to LOGS_FILTER_POOL as a static
pool and verify no other code paths attempt to terminate that static pool.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java`:
- Around line 88-94: The test currently calls tronJsonRpc.close() only in the
try path so the TronJsonRpcImpl instance and its static LOGS_FILTER_POOL may not
be cleaned up when buildTransaction throws; move the close() call into a finally
block and ensure the tronJsonRpc variable is declared/initialized outside the
try so it can be closed unconditionally (i.e., surround the try/catch with try {
... } catch(...) { ... } finally { tronJsonRpc.close(); }) referencing
buildTransaction and close on the TronJsonRpcImpl instance used in
WalletCursorTest.

---

Duplicate comments:
In `@framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java`:
- Line 175: The static LOGS_FILTER_POOL is being shut down in the instance
close() causing RejectedExecutionException for subsequent instances; to fix,
adopt Option A: treat LOGS_FILTER_POOL as a static shared resource and remove
(or guard against) any shutdown/close logic from TronJsonRpcImpl.close() that
calls LOGS_FILTER_POOL.shutdown()/shutdownNow()/close; ensure handleLogsFilter
and processLogFilterEntry continue to submit to LOGS_FILTER_POOL as a static
pool and verify no other code paths attempt to terminate that static pool.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f848f185-10c5-4ee0-9a9f-05c3d8e47094

📥 Commits

Reviewing files that changed from the base of the PR and between 9897ea8 and 61704aa.

📒 Files selected for processing (2)
  • framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java
  • framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java

@317787106 317787106 changed the title fix(jsonrpc): enforce active log filter cap and fix EventBloomException fix(jsonrpc): enforce active log filter cap and improve match efficiency Apr 24, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="framework/src/main/java/org/tron/core/db/Manager.java">

<violation number="1">
P1: EventBloomException is no longer included in the fork-apply catch, so EventBloom failures skip the switch-back rollback path and can leave fork switching in a partially applied state.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@317787106 317787106 changed the title fix(jsonrpc): enforce active log filter cap and improve match efficiency fix(jsonrpc): enforce log filter cap and improve match efficiency Apr 25, 2026
@317787106 317787106 force-pushed the hotfix/fix_newFilter branch from 26c89bf to 2551c5a Compare April 28, 2026 15:48

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java (1)

88-94: ⚠️ Potential issue | 🟡 Minor

close() unreachable when exception is expected.

In testDisableInSolidity, the test expects buildTransaction() to throw an exception because the cursor is set to SOLIDITY. The close() call on line 90 is inside the try block after the throwing call, so it will never execute. Consider using try-with-resources or a finally block.

Additionally, testSource() and testDisableInPBFT() also create TronJsonRpcImpl instances but never call close().

🛠️ Suggested pattern
     TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, dbManager);
     try {
       tronJsonRpc.buildTransaction(buildArguments);
-      tronJsonRpc.close();
+      Assert.fail("Expected exception was not thrown");
     } catch (Exception e) {
       Assert.assertEquals("the method buildTransaction does not exist/is not available in "
           + "SOLIDITY", e.getMessage());
+    } finally {
+      tronJsonRpc.close();
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java` around
lines 88 - 94, The tests (testDisableInSolidity, testSource, testDisableInPBFT)
create TronJsonRpcImpl instances but may skip calling close() because
tronJsonRpc.buildTransaction() is expected to throw, making the close() call
unreachable; update these tests to ensure TronJsonRpcImpl.close() always
runs—either instantiate TronJsonRpcImpl in a try-with-resources (implementing
AutoCloseable) or use a try/finally around the code that calls
tronJsonRpc.buildTransaction() so tronJsonRpc.close() is invoked in the finally
block; specifically modify testDisableInSolidity to move tronJsonRpc.close()
into a finally (or use try-with-resources) and apply the same pattern to
testSource and testDisableInPBFT to guarantee resources are closed.
♻️ Duplicate comments (1)
framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java (1)

174-175: ⚠️ Potential issue | 🟠 Major

Static ForkJoinPool shutdown via instance close() breaks reuse.

LOGS_FILTER_POOL is private static final, but close() at line 1598 calls shutdownAndAwaitTermination on it. If any TronJsonRpcImpl instance is closed (e.g., in tests with @DirtiesContext, or future lifecycle changes), subsequent handleLogsFilter calls hitting the parallel branch will throw RejectedExecutionException — the class-level pool is permanently dead after the first close.

Additionally, context snippets show JsonrpcServiceTest and Create2Test create instances without calling close(), which isn't ideal, but calling close() would be worse since it kills the shared pool.

Consider making the pool an instance field (constructed in constructor, shutdown in close()), or keep it static and use daemon worker threads so the JVM can exit cleanly without explicit shutdown.

🛠️ Option 1: Make pool instance-scoped
-  private static final int FILTER_PARALLEL_THRESHOLD = 10000;
-  private static final ForkJoinPool LOGS_FILTER_POOL = new ForkJoinPool(2);
+  private static final int FILTER_PARALLEL_THRESHOLD = 10000;
+  private final ForkJoinPool logsFilterPool;

   `@Autowired`
   public TronJsonRpcImpl(...) {
     ...
+    this.logsFilterPool = new ForkJoinPool(2);
   }

And update handleLogsFilter to accept the pool as a parameter or make it non-static.

🛠️ Option 2: Keep static, use daemon threads, don't shutdown
-  private static final ForkJoinPool LOGS_FILTER_POOL = new ForkJoinPool(2);
+  private static final ForkJoinPool LOGS_FILTER_POOL = new ForkJoinPool(
+      2,
+      pool -> {
+        ForkJoinWorkerThread t = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
+        t.setDaemon(true);
+        t.setName("jsonrpc-logs-filter-" + t.getPoolIndex());
+        return t;
+      },
+      null, false);

   `@Override`
   public void close() throws IOException {
-    ExecutorServiceManager.shutdownAndAwaitTermination(LOGS_FILTER_POOL, "logs-filter-pool");
     logElementCache.invalidateAll();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java`
around lines 174 - 175, LOGS_FILTER_POOL is a static shared ForkJoinPool that is
shutdown in close(), causing RejectedExecutionException for any other
TronJsonRpcImpl instances; change it to an instance field (remove static) and
initialize it in the TronJsonRpcImpl constructor (e.g., new ForkJoinPool(2)),
update handleLogsFilter (make non-static or pass the instance pool) to use this
instance pool, and then call shutdownAndAwaitTermination on that instance pool
in close() so each instance manages its own pool lifecycle.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@common/src/main/resources/reference.conf`:
- Around line 404-405: The comment above the config key maxLogFilterNum is
misleading: change it to state that maxLogFilterNum limits the number of active
log filters that can be registered via eth_newFilter (not the number of log
entries returned by eth_getLogs), and add a note pointing to
LogBlockQuery.MAX_RESULT as the place that controls per-query log entry limits;
reference the config key name maxLogFilterNum, the RPC methods eth_newFilter /
eth_getLogs, and LogBlockQuery.MAX_RESULT so reviewers can find the related
code.

---

Outside diff comments:
In `@framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java`:
- Around line 88-94: The tests (testDisableInSolidity, testSource,
testDisableInPBFT) create TronJsonRpcImpl instances but may skip calling close()
because tronJsonRpc.buildTransaction() is expected to throw, making the close()
call unreachable; update these tests to ensure TronJsonRpcImpl.close() always
runs—either instantiate TronJsonRpcImpl in a try-with-resources (implementing
AutoCloseable) or use a try/finally around the code that calls
tronJsonRpc.buildTransaction() so tronJsonRpc.close() is invoked in the finally
block; specifically modify testDisableInSolidity to move tronJsonRpc.close()
into a finally (or use try-with-resources) and apply the same pattern to
testSource and testDisableInPBFT to guarantee resources are closed.

---

Duplicate comments:
In `@framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java`:
- Around line 174-175: LOGS_FILTER_POOL is a static shared ForkJoinPool that is
shutdown in close(), causing RejectedExecutionException for any other
TronJsonRpcImpl instances; change it to an instance field (remove static) and
initialize it in the TronJsonRpcImpl constructor (e.g., new ForkJoinPool(2)),
update handleLogsFilter (make non-static or pass the instance pool) to use this
instance pool, and then call shutdownAndAwaitTermination on that instance pool
in close() so each instance manages its own pool lifecycle.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f3c347f2-bc76-4474-9882-3898bcf6c561

📥 Commits

Reviewing files that changed from the base of the PR and between 61704aa and 2551c5a.

📒 Files selected for processing (12)
  • common/src/main/java/org/tron/common/parameter/CommonParameter.java
  • common/src/main/java/org/tron/core/config/args/NodeConfig.java
  • common/src/main/resources/reference.conf
  • framework/src/main/java/org/tron/core/config/args/Args.java
  • framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java
  • framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java
  • framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.java
  • framework/src/main/resources/config.conf
  • framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java
  • framework/src/test/resources/config-localtest.conf
  • framework/src/test/resources/config-test-mainnet.conf
  • framework/src/test/resources/config-test.conf
✅ Files skipped from review due to trivial changes (5)
  • framework/src/test/resources/config-test-mainnet.conf
  • framework/src/test/resources/config-test.conf
  • framework/src/test/resources/config-localtest.conf
  • framework/src/main/java/org/tron/core/config/args/Args.java
  • framework/src/main/resources/config.conf

Comment thread common/src/main/resources/reference.conf Outdated
@github-actions

Copy link
Copy Markdown

❌ Math Usage Detection Results

Found forbidden usage of java.lang.Math in the following files:

./framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java

Please review if this usage is intended.

Caution

Note: You should use org.tron.common.math.StrictMathWrapper.
If you need to use java.lang.Math, please provide a justification.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java (2)

1443-1450: ⚠️ Potential issue | 🟠 Major

The filter-cap check is still racy.

Line 1443/Line 1465 checks size() before Line 1450/Line 1472 inserts into the ConcurrentHashMap. Concurrent eth_newFilter / eth_newBlockFilter calls can all pass the pre-check and overshoot the configured caps, which undermines the resource-exhaustion guard this PR is adding.

Possible hardening
-    if (maxLogFilterNum > 0 && eventFilter2Result.size() >= maxLogFilterNum) {
-      throw new JsonRpcExceedLimitException(
-          "exceed max log filters: " + maxLogFilterNum + ", try again later");
-    }
     long currentMaxFullNum = wallet.getNowBlock().getBlockHeader().getRawData().getNumber();
     LogFilterAndResult logFilterAndResult = new LogFilterAndResult(fr, currentMaxFullNum, wallet);
     String filterID = generateFilterId();
     eventFilter2Result.put(filterID, logFilterAndResult);
+    if (maxLogFilterNum > 0 && eventFilter2Result.size() > maxLogFilterNum) {
+      eventFilter2Result.remove(filterID, logFilterAndResult);
+      throw new JsonRpcExceedLimitException(
+          "exceed max log filters: " + maxLogFilterNum + ", try again later");
+    }

Also applies to: 1465-1472

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java`
around lines 1443 - 1450, The pre-check against maxLogFilterNum is racy because
multiple threads can pass size() then concurrently insert into
eventFilter2Result; change to an atomic check-and-insert: maintain an
AtomicInteger (e.g., filterCount) tracking current filters or use
eventFilter2Result.compute(...) / putIfAbsent inside a synchronized block so you
only insert when the incremented count is still <= maxLogFilterNum, otherwise
reject with JsonRpcExceedLimitException; update places that create filters (the
code paths using generateFilterId(), new LogFilterAndResult(...), and
eventFilter2Result.put(...)) to increment the counter before creating/putting
and decrement on failure/removal so the cap is never exceeded concurrently.

174-175: ⚠️ Potential issue | 🟠 Major

Don't shut down a class-level pool from an instance close().

Line 175 creates a static LOGS_FILTER_POOL, but Line 1598 tears it down every time a TronJsonRpcImpl instance is closed. After the first close, later handleLogsFilter() calls that hit the parallel branch will submit into a dead pool and fail.

Also applies to: 1597-1601

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java`
around lines 174 - 175, The static LOGS_FILTER_POOL is being shutdown from the
instance close() of TronJsonRpcImpl which leaves the class-level ForkJoinPool
unusable for subsequent handleLogsFilter() calls that rely on
FILTER_PARALLEL_THRESHOLD; to fix, remove the shutdown of LOGS_FILTER_POOL from
TronJsonRpcImpl.close() and either (a) move the pool to an instance field and
shutdown it in close(), or (b) keep it static and provide a single
application-lifecycle shutdown hook (or rely on JVM exit) so handleLogsFilter()
continues to submit tasks safely; update references to LOGS_FILTER_POOL and
close() accordingly and ensure no other instance methods call shutdown() on the
static pool.
🧹 Nitpick comments (2)
framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java (1)

59-193: Add one test that crosses the parallel threshold.

All cases here keep the filter map at 1-2 entries, so the new > FILTER_PARALLEL_THRESHOLD branch in TronJsonRpcImpl.handleLogsFilter() never runs. A single test with FILTER_PARALLEL_THRESHOLD + 1 matching filters would give this PR coverage for the actual ForkJoinPool path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java`
around lines 59 - 193, Add a test that triggers the parallel branch in
TronJsonRpcImpl.handleLogsFilter() by populating
TronJsonRpcImpl.getEventFilter2ResultFull() with FILTER_PARALLEL_THRESHOLD + 1
matching LogFilterAndResult entries (use LogFilterAndResult and the same
FilterRequest used in other tests), then create a LogsFilterCapsule that matches
and call handleLogsFilter(capsule); assert that all inserted filters receive
results (e.g., each LogFilterAndResult.getResult() has the expected entry) to
cover the ForkJoinPool path. Ensure you use the same FILTER_ID naming scheme
(e.g., generate unique IDs) so entries are distinct and the test cleans up the
map before/after to avoid interference with other tests.
framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java (1)

60-60: Make the shared RNG private static final.

Line 60 exposes a mutable package-scoped SecureRandom even though callers only need generateFilterId(). Locking this down avoids accidental reassignment of the filter-id generator from package peers.

Suggested change
-  static SecureRandom random = new SecureRandom();
+  private static final SecureRandom random = new SecureRandom();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java`
at line 60, The shared SecureRandom field "random" in JsonRpcApiUtil is
package-visible and mutable; change its declaration to "private static final
SecureRandom random = new SecureRandom();" inside class JsonRpcApiUtil so it
cannot be reassigned by package peers, and keep existing usages (e.g.,
generateFilterId()) unchanged to preserve behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java`:
- Around line 157-184: The test relies on the shared static map from
TronJsonRpcImpl.getEventFilter2ResultFull(), so make it independent by
snapshotting and restoring the map: at the start of
testNewFilter_exceedsCapThrowsException copy the current map contents (e.g., new
HashMap<>(map)), then clear the shared map before inserting exactly cap
synthetic entries into map and running tronJsonRpc.newFilter(fr); in the outer
finally, restore the original contents by clearing the map and
putAll(originalSnapshot) so other tests are unaffected. Ensure you still assert
cap behavior using Args.getInstance().getJsonRpcMaxLogFilterNum() and clean up
with tronJsonRpc.close() in the inner finally.

---

Duplicate comments:
In `@framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java`:
- Around line 1443-1450: The pre-check against maxLogFilterNum is racy because
multiple threads can pass size() then concurrently insert into
eventFilter2Result; change to an atomic check-and-insert: maintain an
AtomicInteger (e.g., filterCount) tracking current filters or use
eventFilter2Result.compute(...) / putIfAbsent inside a synchronized block so you
only insert when the incremented count is still <= maxLogFilterNum, otherwise
reject with JsonRpcExceedLimitException; update places that create filters (the
code paths using generateFilterId(), new LogFilterAndResult(...), and
eventFilter2Result.put(...)) to increment the counter before creating/putting
and decrement on failure/removal so the cap is never exceeded concurrently.
- Around line 174-175: The static LOGS_FILTER_POOL is being shutdown from the
instance close() of TronJsonRpcImpl which leaves the class-level ForkJoinPool
unusable for subsequent handleLogsFilter() calls that rely on
FILTER_PARALLEL_THRESHOLD; to fix, remove the shutdown of LOGS_FILTER_POOL from
TronJsonRpcImpl.close() and either (a) move the pool to an instance field and
shutdown it in close(), or (b) keep it static and provide a single
application-lifecycle shutdown hook (or rely on JVM exit) so handleLogsFilter()
continues to submit tasks safely; update references to LOGS_FILTER_POOL and
close() accordingly and ensure no other instance methods call shutdown() on the
static pool.

---

Nitpick comments:
In `@framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java`:
- Line 60: The shared SecureRandom field "random" in JsonRpcApiUtil is
package-visible and mutable; change its declaration to "private static final
SecureRandom random = new SecureRandom();" inside class JsonRpcApiUtil so it
cannot be reassigned by package peers, and keep existing usages (e.g.,
generateFilterId()) unchanged to preserve behavior.

In `@framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java`:
- Around line 59-193: Add a test that triggers the parallel branch in
TronJsonRpcImpl.handleLogsFilter() by populating
TronJsonRpcImpl.getEventFilter2ResultFull() with FILTER_PARALLEL_THRESHOLD + 1
matching LogFilterAndResult entries (use LogFilterAndResult and the same
FilterRequest used in other tests), then create a LogsFilterCapsule that matches
and call handleLogsFilter(capsule); assert that all inserted filters receive
results (e.g., each LogFilterAndResult.getResult() has the expected entry) to
cover the ForkJoinPool path. Ensure you use the same FILTER_ID naming scheme
(e.g., generate unique IDs) so entries are distinct and the test cleans up the
map before/after to avoid interference with other tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c9931960-0cc8-42ee-b49f-c2abc988d81c

📥 Commits

Reviewing files that changed from the base of the PR and between 2551c5a and f1d0e9a.

📒 Files selected for processing (5)
  • framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java
  • framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java
  • framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java
  • framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java
  • framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java

Comment thread framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
framework/src/main/resources/config.conf (1)

378-379: Optional: align wording with reference.conf for clarity.

Consider using eth_newFilter and explicitly saying “concurrent/active registrations” to avoid ambiguity with eth_getLogs limits.

Suggested wording tweak
-    # Allowed maximum number for newFilter, >0 otherwise no limit
+    # Maximum number of concurrent eth_newFilter registrations, >0 otherwise no limit
     maxLogFilterNum = 20000
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/src/main/resources/config.conf` around lines 378 - 379, Update the
config key description for maxLogFilterNum to match reference.conf wording by
explicitly referencing eth_newFilter and clarifying it limits concurrent/active
registrations (not eth_getLogs retrievals); locate the maxLogFilterNum setting
and adjust its comment text to read something like "Allowed maximum number of
concurrent/active eth_newFilter registrations; >0 means no limit" so the intent
is unambiguous.
framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java (1)

152-170: Ordering guarantee is not asserted.

The objective requires preserving per-subscription event order, but Line 168–169 only checks counts. Add a case with multiple matching logs and assert ordered indices in each filter result.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java`
around lines 152 - 170, Update the testMultipleMatchingFilters_bothReceiveEvents
test to verify ordering: instead of a single log, create multiple matching
TransactionInfo entries (e.g., two or three) and send them via handleLogsFilter
using the LogsFilterCapsule; after invoking handleLogsFilter, assert not only
that filter1.getResult().size() and filter2.getResult().size() equal the
expected count but also that the sequence of events in each filter's result
preserves input order (check the per-result entries' indices or timestamps in
the returned objects to match the creation order). Ensure you reference the
existing filter instances (filter1, filter2), the handleLogsFilter call, and
LogsFilterCapsule when adding and asserting ordered entries.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java`:
- Around line 51-185: Add a unit test that triggers the >10,000-entry parallel
branch in handleLogsFilter by populating the event-filter map with more than
10_000 LogFilterAndResult entries (use
TronJsonRpcImpl.getEventFilter2ResultFull()), then create a LogsFilterCapsule
with matching TransactionInfo and call handleLogsFilter(capsule); assert that at
least the intended filter(s) receive results (e.g., check
filter.getResult().size()) and that the test exercises the parallel path rather
than the serial one. Ensure you reference and use LogFilterAndResult,
FilterRequest, LogsFilterCapsule and handleLogsFilter to build the test, and
keep the filter expiration/state consistent with other tests so it is not
removed prematurely.

---

Nitpick comments:
In `@framework/src/main/resources/config.conf`:
- Around line 378-379: Update the config key description for maxLogFilterNum to
match reference.conf wording by explicitly referencing eth_newFilter and
clarifying it limits concurrent/active registrations (not eth_getLogs
retrievals); locate the maxLogFilterNum setting and adjust its comment text to
read something like "Allowed maximum number of concurrent/active eth_newFilter
registrations; >0 means no limit" so the intent is unambiguous.

In `@framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java`:
- Around line 152-170: Update the testMultipleMatchingFilters_bothReceiveEvents
test to verify ordering: instead of a single log, create multiple matching
TransactionInfo entries (e.g., two or three) and send them via handleLogsFilter
using the LogsFilterCapsule; after invoking handleLogsFilter, assert not only
that filter1.getResult().size() and filter2.getResult().size() equal the
expected count but also that the sequence of events in each filter's result
preserves input order (check the per-result entries' indices or timestamps in
the returned objects to match the creation order). Ensure you reference the
existing filter instances (filter1, filter2), the handleLogsFilter call, and
LogsFilterCapsule when adding and asserting ordered entries.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0a5ff1d3-86d5-4315-895f-d446dd1301d0

📥 Commits

Reviewing files that changed from the base of the PR and between f1d0e9a and e6354d0.

📒 Files selected for processing (3)
  • common/src/main/resources/reference.conf
  • framework/src/main/resources/config.conf
  • framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java

@317787106 317787106 force-pushed the hotfix/fix_newFilter branch from e6354d0 to ad0d688 Compare April 30, 2026 04:24
@cubic-dev-ai

cubic-dev-ai Bot commented Apr 30, 2026

Copy link
Copy Markdown

You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment @cubic-dev-ai review.

@317787106 317787106 closed this May 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parallelizing eth_newFilter event matching

2 participants