From 073771eb47f7ac0c63c20287da54d7b6755e3eb8 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Fri, 26 Dec 2025 11:34:48 +0800 Subject: [PATCH 01/29] add log for LogsFilterCapsule --- .../org/tron/common/logsfilter/capsule/LogsFilterCapsule.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java b/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java index 8a8e122d9a0..2a882e7d60d 100644 --- a/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java +++ b/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java @@ -45,6 +45,9 @@ public LogsFilterCapsule(long blockNumber, String blockHash, Bloom bloom, @Override public void processFilterTrigger() { + long t1 = System.currentTimeMillis(); handleLogsFilter(this); + long t2 = System.currentTimeMillis(); + logger.info("processFilterTrigger cost {}", t2 -t1); } } \ No newline at end of file From 2c9b59dde6dfcd124b33041f1aac9ac3430a1137 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Fri, 26 Dec 2025 12:57:33 +0800 Subject: [PATCH 02/29] use parallelStream for handleLogsFilter --- .../logsfilter/capsule/LogsFilterCapsule.java | 2 +- .../services/jsonrpc/TronJsonRpcImpl.java | 34 ++++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java b/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java index 2a882e7d60d..fbef7c5ee45 100644 --- a/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java +++ b/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java @@ -48,6 +48,6 @@ public void processFilterTrigger() { long t1 = System.currentTimeMillis(); handleLogsFilter(this); long t2 = System.currentTimeMillis(); - logger.info("processFilterTrigger cost {}", t2 -t1); + logger.info("processFilterTrigger cost {}", t2 - t1); } } \ No newline at end of file diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index 72fc579aa56..9aa118ea9c6 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -219,41 +219,42 @@ public static void handleBLockFilter(BlockFilterCapsule blockFilterCapsule) { * append LogsFilterCapsule's LogFilterElement list to each filter if matched */ public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { - Iterator> it; + Map eventFilterMap; if (logsFilterCapsule.isSolidified()) { - it = getEventFilter2ResultSolidity().entrySet().iterator(); + eventFilterMap = getEventFilter2ResultSolidity(); } else { - it = getEventFilter2ResultFull().entrySet().iterator(); + eventFilterMap = getEventFilter2ResultFull(); } - while (it.hasNext()) { - Entry entry = it.next(); - if (entry.getValue().isExpire()) { - it.remove(); - continue; - } + eventFilterMap.entrySet().parallelStream().forEach(entry -> { LogFilterAndResult logFilterAndResult = entry.getValue(); + if (logFilterAndResult.isExpire()) { + eventFilterMap.remove(entry.getKey()); + return; + } + + long blockNumber = logsFilterCapsule.getBlockNumber(); long fromBlock = logFilterAndResult.getLogFilterWrapper().getFromBlock(); long toBlock = logFilterAndResult.getLogFilterWrapper().getToBlock(); - if (!(fromBlock <= logsFilterCapsule.getBlockNumber() - && logsFilterCapsule.getBlockNumber() <= toBlock)) { - continue; + if (!(fromBlock <= blockNumber && blockNumber <= toBlock)) { + return; } if (logsFilterCapsule.getBloom() != null && !logFilterAndResult.getLogFilterWrapper().getLogFilter() .matchBloom(logsFilterCapsule.getBloom())) { - continue; + return; } LogFilter logFilter = logFilterAndResult.getLogFilterWrapper().getLogFilter(); List elements = - LogMatch.matchBlock(logFilter, logsFilterCapsule.getBlockNumber(), + LogMatch.matchBlock(logFilter, blockNumber, logsFilterCapsule.getBlockHash(), logsFilterCapsule.getTxInfoList(), logsFilterCapsule.isRemoved()); + List localResults = new ArrayList<>(elements.size()); for (LogFilterElement element : elements) { LogFilterElement cachedElement; try { @@ -263,9 +264,10 @@ public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { logger.error("Getting/loading LogFilterElement from cache fails", e); // never happen cachedElement = element; } - logFilterAndResult.getResult().add(cachedElement); + localResults.add(cachedElement); } - } + logFilterAndResult.getResult().addAll(localResults); + }); } @Override From 440582b71fafed878386aa2c4d8d8ef9adece6f1 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Fri, 26 Dec 2025 18:37:12 +0800 Subject: [PATCH 03/29] add number of log filter limit --- .../java/org/tron/core/services/jsonrpc/TronJsonRpc.java | 3 ++- .../org/tron/core/services/jsonrpc/TronJsonRpcImpl.java | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java index 8e7c8615da4..434bafd171b 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpc.java @@ -291,9 +291,10 @@ CompilationResult ethSubmitHashrate(String hashrate, String id) @JsonRpcErrors({ @JsonRpcError(exception = JsonRpcMethodNotFoundException.class, code = -32601, data = "{}"), @JsonRpcError(exception = JsonRpcInvalidParamsException.class, code = -32602, data = "{}"), + @JsonRpcError(exception = JsonRpcExceedLimitException.class, code = -32005, data = "{}"), }) String newFilter(FilterRequest fr) throws JsonRpcInvalidParamsException, - JsonRpcMethodNotFoundException; + JsonRpcMethodNotFoundException, JsonRpcExceedLimitException; @JsonRpcMethod("eth_newBlockFilter") @JsonRpcErrors({ diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index 9aa118ea9c6..d24ef1a2fd8 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -1394,7 +1394,7 @@ public CompilationResult ethSubmitHashrate(String hashrate, String id) @Override public String newFilter(FilterRequest fr) throws JsonRpcInvalidParamsException, - JsonRpcMethodNotFoundException { + JsonRpcMethodNotFoundException, JsonRpcExceedLimitException { disableInPBFT("eth_newFilter"); // not supports finalized as block parameter @@ -1409,7 +1409,10 @@ public String newFilter(FilterRequest fr) throws JsonRpcInvalidParamsException, } else { eventFilter2Result = eventFilter2ResultSolidity; } - + if (eventFilter2Result.size() > maxBlockFilterNum) { + throw new JsonRpcExceedLimitException( + "exceed max log filters: " + maxBlockFilterNum + ", try again later"); + } long currentMaxFullNum = wallet.getNowBlock().getBlockHeader().getRawData().getNumber(); LogFilterAndResult logFilterAndResult = new LogFilterAndResult(fr, currentMaxFullNum, wallet); String filterID = generateFilterId(); From 0016f13fe8c1c02733c9a2aaf20cf9ade660d203 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Fri, 26 Dec 2025 19:04:52 +0800 Subject: [PATCH 04/29] add map size --- .../org/tron/common/logsfilter/capsule/LogsFilterCapsule.java | 3 --- .../java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java b/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java index fbef7c5ee45..8a8e122d9a0 100644 --- a/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java +++ b/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java @@ -45,9 +45,6 @@ public LogsFilterCapsule(long blockNumber, String blockHash, Bloom bloom, @Override public void processFilterTrigger() { - long t1 = System.currentTimeMillis(); handleLogsFilter(this); - long t2 = System.currentTimeMillis(); - logger.info("processFilterTrigger cost {}", t2 - t1); } } \ No newline at end of file diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index d24ef1a2fd8..a94f907480e 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -219,6 +219,7 @@ public static void handleBLockFilter(BlockFilterCapsule blockFilterCapsule) { * append LogsFilterCapsule's LogFilterElement list to each filter if matched */ public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { + long t1 = System.currentTimeMillis(); Map eventFilterMap; if (logsFilterCapsule.isSolidified()) { @@ -268,6 +269,8 @@ public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { } logFilterAndResult.getResult().addAll(localResults); }); + long t2 = System.currentTimeMillis(); + logger.info("handleLogsFilter cost {}, filter size {}", t2 - t1, eventFilterMap.size()); } @Override From 0b8086150d3a488d17cb9d8678223197e4266984 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Fri, 26 Dec 2025 19:31:39 +0800 Subject: [PATCH 05/29] use serial map --- .../services/jsonrpc/TronJsonRpcImpl.java | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index a94f907480e..fc64c8dea37 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -220,42 +220,44 @@ public static void handleBLockFilter(BlockFilterCapsule blockFilterCapsule) { */ public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { long t1 = System.currentTimeMillis(); - Map eventFilterMap; + long filterSize; + Iterator> it; if (logsFilterCapsule.isSolidified()) { - eventFilterMap = getEventFilter2ResultSolidity(); + it = getEventFilter2ResultSolidity().entrySet().iterator(); + filterSize = getEventFilter2ResultSolidity().size(); } else { - eventFilterMap = getEventFilter2ResultFull(); + it = getEventFilter2ResultFull().entrySet().iterator(); + filterSize = getEventFilter2ResultFull().size(); } - eventFilterMap.entrySet().parallelStream().forEach(entry -> { - - LogFilterAndResult logFilterAndResult = entry.getValue(); - if (logFilterAndResult.isExpire()) { - eventFilterMap.remove(entry.getKey()); - return; + while (it.hasNext()) { + Entry entry = it.next(); + if (entry.getValue().isExpire()) { + it.remove(); + continue; } - long blockNumber = logsFilterCapsule.getBlockNumber(); + LogFilterAndResult logFilterAndResult = entry.getValue(); long fromBlock = logFilterAndResult.getLogFilterWrapper().getFromBlock(); long toBlock = logFilterAndResult.getLogFilterWrapper().getToBlock(); - if (!(fromBlock <= blockNumber && blockNumber <= toBlock)) { - return; + if (!(fromBlock <= logsFilterCapsule.getBlockNumber() + && logsFilterCapsule.getBlockNumber() <= toBlock)) { + continue; } if (logsFilterCapsule.getBloom() != null && !logFilterAndResult.getLogFilterWrapper().getLogFilter() .matchBloom(logsFilterCapsule.getBloom())) { - return; + continue; } LogFilter logFilter = logFilterAndResult.getLogFilterWrapper().getLogFilter(); List elements = - LogMatch.matchBlock(logFilter, blockNumber, + LogMatch.matchBlock(logFilter, logsFilterCapsule.getBlockNumber(), logsFilterCapsule.getBlockHash(), logsFilterCapsule.getTxInfoList(), logsFilterCapsule.isRemoved()); - List localResults = new ArrayList<>(elements.size()); for (LogFilterElement element : elements) { LogFilterElement cachedElement; try { @@ -265,12 +267,11 @@ public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { logger.error("Getting/loading LogFilterElement from cache fails", e); // never happen cachedElement = element; } - localResults.add(cachedElement); + logFilterAndResult.getResult().add(cachedElement); } - logFilterAndResult.getResult().addAll(localResults); - }); + } long t2 = System.currentTimeMillis(); - logger.info("handleLogsFilter cost {}, filter size {}", t2 - t1, eventFilterMap.size()); + logger.info("handleLogsFilter cost {}, filter size {}", t2 - t1, filterSize); } @Override From 6620708373fbb96c785825280bbe365c21deb42b Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Fri, 26 Dec 2025 19:44:41 +0800 Subject: [PATCH 06/29] use parallel --- .../services/jsonrpc/TronJsonRpcImpl.java | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index fc64c8dea37..a94f907480e 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -220,44 +220,42 @@ public static void handleBLockFilter(BlockFilterCapsule blockFilterCapsule) { */ public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { long t1 = System.currentTimeMillis(); - long filterSize; - Iterator> it; + Map eventFilterMap; if (logsFilterCapsule.isSolidified()) { - it = getEventFilter2ResultSolidity().entrySet().iterator(); - filterSize = getEventFilter2ResultSolidity().size(); + eventFilterMap = getEventFilter2ResultSolidity(); } else { - it = getEventFilter2ResultFull().entrySet().iterator(); - filterSize = getEventFilter2ResultFull().size(); + eventFilterMap = getEventFilter2ResultFull(); } - while (it.hasNext()) { - Entry entry = it.next(); - if (entry.getValue().isExpire()) { - it.remove(); - continue; - } + eventFilterMap.entrySet().parallelStream().forEach(entry -> { LogFilterAndResult logFilterAndResult = entry.getValue(); + if (logFilterAndResult.isExpire()) { + eventFilterMap.remove(entry.getKey()); + return; + } + + long blockNumber = logsFilterCapsule.getBlockNumber(); long fromBlock = logFilterAndResult.getLogFilterWrapper().getFromBlock(); long toBlock = logFilterAndResult.getLogFilterWrapper().getToBlock(); - if (!(fromBlock <= logsFilterCapsule.getBlockNumber() - && logsFilterCapsule.getBlockNumber() <= toBlock)) { - continue; + if (!(fromBlock <= blockNumber && blockNumber <= toBlock)) { + return; } if (logsFilterCapsule.getBloom() != null && !logFilterAndResult.getLogFilterWrapper().getLogFilter() .matchBloom(logsFilterCapsule.getBloom())) { - continue; + return; } LogFilter logFilter = logFilterAndResult.getLogFilterWrapper().getLogFilter(); List elements = - LogMatch.matchBlock(logFilter, logsFilterCapsule.getBlockNumber(), + LogMatch.matchBlock(logFilter, blockNumber, logsFilterCapsule.getBlockHash(), logsFilterCapsule.getTxInfoList(), logsFilterCapsule.isRemoved()); + List localResults = new ArrayList<>(elements.size()); for (LogFilterElement element : elements) { LogFilterElement cachedElement; try { @@ -267,11 +265,12 @@ public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { logger.error("Getting/loading LogFilterElement from cache fails", e); // never happen cachedElement = element; } - logFilterAndResult.getResult().add(cachedElement); + localResults.add(cachedElement); } - } + logFilterAndResult.getResult().addAll(localResults); + }); long t2 = System.currentTimeMillis(); - logger.info("handleLogsFilter cost {}, filter size {}", t2 - t1, filterSize); + logger.info("handleLogsFilter cost {}, filter size {}", t2 - t1, eventFilterMap.size()); } @Override From 5f03ddbf11f42ae425c1ac743ee318d66e93fad3 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Mon, 29 Dec 2025 10:08:30 +0800 Subject: [PATCH 07/29] set parallelism as 3 --- .../services/jsonrpc/TronJsonRpcImpl.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index a94f907480e..44d1a634b38 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -30,6 +30,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; +import java.util.concurrent.ForkJoinPool; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import lombok.Getter; @@ -228,7 +229,8 @@ public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { eventFilterMap = getEventFilter2ResultFull(); } - eventFilterMap.entrySet().parallelStream().forEach(entry -> { + ForkJoinPool pool = new ForkJoinPool(2); //parallelStream default num(CPU) -1 + pool.submit(() -> eventFilterMap.entrySet().parallelStream().forEach(entry -> { LogFilterAndResult logFilterAndResult = entry.getValue(); if (logFilterAndResult.isExpire()) { @@ -243,17 +245,15 @@ public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { return; } - if (logsFilterCapsule.getBloom() != null - && !logFilterAndResult.getLogFilterWrapper().getLogFilter() - .matchBloom(logsFilterCapsule.getBloom())) { + if (logsFilterCapsule.getBloom() != null && !logFilterAndResult.getLogFilterWrapper() + .getLogFilter().matchBloom(logsFilterCapsule.getBloom())) { return; } LogFilter logFilter = logFilterAndResult.getLogFilterWrapper().getLogFilter(); List elements = - LogMatch.matchBlock(logFilter, blockNumber, - logsFilterCapsule.getBlockHash(), logsFilterCapsule.getTxInfoList(), - logsFilterCapsule.isRemoved()); + LogMatch.matchBlock(logFilter, blockNumber, logsFilterCapsule.getBlockHash(), + logsFilterCapsule.getTxInfoList(), logsFilterCapsule.isRemoved()); List localResults = new ArrayList<>(elements.size()); for (LogFilterElement element : elements) { @@ -268,9 +268,10 @@ public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { localResults.add(cachedElement); } logFilterAndResult.getResult().addAll(localResults); - }); + })).join(); long t2 = System.currentTimeMillis(); - logger.info("handleLogsFilter cost {}, filter size {}", t2 - t1, eventFilterMap.size()); + logger.info("handleLogsFilter {} cost {}, filter size {}", + logsFilterCapsule.isSolidified() ? "Solidity" : "Full", t2 - t1, eventFilterMap.size()); } @Override From 1b1a081910d3c4838fe8f208e4fa23c6956ebf3f Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Mon, 29 Dec 2025 10:19:49 +0800 Subject: [PATCH 08/29] add config item node.jsonrpc.maxLogFilterNum --- .../main/java/org/tron/common/parameter/CommonParameter.java | 3 +++ .../src/main/java/org/tron/core/config/args/NodeConfig.java | 1 + common/src/main/resources/reference.conf | 3 +++ framework/src/main/java/org/tron/core/config/args/Args.java | 1 + .../java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java | 5 +++-- framework/src/main/resources/config.conf | 2 ++ framework/src/test/resources/config-localtest.conf | 1 + framework/src/test/resources/config-test-mainnet.conf | 1 + framework/src/test/resources/config-test.conf | 1 + 9 files changed, 16 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index a73158a718a..2638c3e8c90 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -459,6 +459,9 @@ public class CommonParameter { @Getter @Setter public int jsonRpcMaxBlockFilterNum = 50000; + @Getter + @Setter + public int jsonRpcMaxLogFilterNum = 20000; @Getter @Setter diff --git a/common/src/main/java/org/tron/core/config/args/NodeConfig.java b/common/src/main/java/org/tron/core/config/args/NodeConfig.java index c3305e976de..4544a64926f 100644 --- a/common/src/main/java/org/tron/core/config/args/NodeConfig.java +++ b/common/src/main/java/org/tron/core/config/args/NodeConfig.java @@ -302,6 +302,7 @@ public void setHttpPBFTPort(int v) { private int maxBlockRange = 5000; private int maxSubTopics = 1000; private int maxBlockFilterNum = 50000; + private int maxLogFilterNum = 20000; } @Getter diff --git a/common/src/main/resources/reference.conf b/common/src/main/resources/reference.conf index 11970a0a673..addaa870eea 100644 --- a/common/src/main/resources/reference.conf +++ b/common/src/main/resources/reference.conf @@ -400,6 +400,9 @@ node { # Maximum number for blockFilter maxBlockFilterNum = 50000 + + # Maximum number of log entries returned by eth_getLogs / eth_newFilter + maxLogFilterNum = 20000 } # Disabled API list (works for http, rpc and pbft, not jsonrpc). Case insensitive. diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index f91c6a437ac..d4712896673 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -585,6 +585,7 @@ private static void applyNodeConfig(NodeConfig nc) { PARAMETER.jsonRpcMaxBlockRange = jsonrpc.getMaxBlockRange(); PARAMETER.jsonRpcMaxSubTopics = jsonrpc.getMaxSubTopics(); PARAMETER.jsonRpcMaxBlockFilterNum = jsonrpc.getMaxBlockFilterNum(); + PARAMETER.jsonRpcMaxLogFilterNum = jsonrpc.getMaxLogFilterNum(); // ---- P2P sub-bean ---- PARAMETER.nodeP2pVersion = nc.getP2p().getVersion(); diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index 44d1a634b38..28e013cf9b2 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -119,6 +119,7 @@ public enum RequestSource { private static final String FILTER_NOT_FOUND = "filter not found"; public static final int EXPIRE_SECONDS = 5 * 60; private static final int maxBlockFilterNum = Args.getInstance().getJsonRpcMaxBlockFilterNum(); + private static final int maxLogFilterNum = Args.getInstance().getJsonRpcMaxLogFilterNum(); private static final Cache logElementCache = CacheBuilder.newBuilder() .maximumSize(300_000L) // 300s * tps(1000) * 1 log/tx ≈ 300_000 @@ -1413,9 +1414,9 @@ public String newFilter(FilterRequest fr) throws JsonRpcInvalidParamsException, } else { eventFilter2Result = eventFilter2ResultSolidity; } - if (eventFilter2Result.size() > maxBlockFilterNum) { + if (eventFilter2Result.size() > maxLogFilterNum) { throw new JsonRpcExceedLimitException( - "exceed max log filters: " + maxBlockFilterNum + ", try again later"); + "exceed max log filters: " + maxLogFilterNum + ", try again later"); } long currentMaxFullNum = wallet.getNowBlock().getBlockHeader().getRawData().getNumber(); LogFilterAndResult logFilterAndResult = new LogFilterAndResult(fr, currentMaxFullNum, wallet); diff --git a/framework/src/main/resources/config.conf b/framework/src/main/resources/config.conf index 369924074bc..2e2418cbe47 100644 --- a/framework/src/main/resources/config.conf +++ b/framework/src/main/resources/config.conf @@ -375,6 +375,8 @@ node { maxSubTopics = 1000 # Allowed maximum number for blockFilter maxBlockFilterNum = 50000 + # Allowed maximum number for newFilter + maxLogFilterNum = 20000 } # Disabled api list, it will work for http, rpc and pbft, both FullNode and SolidityNode, diff --git a/framework/src/test/resources/config-localtest.conf b/framework/src/test/resources/config-localtest.conf index 53a78d3e4c6..d31705f39bd 100644 --- a/framework/src/test/resources/config-localtest.conf +++ b/framework/src/test/resources/config-localtest.conf @@ -168,6 +168,7 @@ node { # maxBlockRange = 5000 # maxSubTopics = 1000 # maxBlockFilterNum = 30000 + # maxLogFilterNum = 20000 } } diff --git a/framework/src/test/resources/config-test-mainnet.conf b/framework/src/test/resources/config-test-mainnet.conf index d39f432ac36..9f968c5628d 100644 --- a/framework/src/test/resources/config-test-mainnet.conf +++ b/framework/src/test/resources/config-test-mainnet.conf @@ -95,6 +95,7 @@ node { # maxBlockRange = 5000 # maxSubTopics = 1000 # maxBlockFilterNum = 50000 + # maxLogFilterNum = 20000 } rpc { diff --git a/framework/src/test/resources/config-test.conf b/framework/src/test/resources/config-test.conf index 71e93f84db5..21cebbfeef4 100644 --- a/framework/src/test/resources/config-test.conf +++ b/framework/src/test/resources/config-test.conf @@ -119,6 +119,7 @@ node { # maxBlockRange = 5000 # maxSubTopics = 1000 # maxBlockFilterNum = 30000 + # maxLogFilterNum = 20000 } # use your ipv6 address for node discovery and tcp connection, default false From bf1f1000e1b6dd0882b4deeb1da6e1f49e2e389a Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Fri, 17 Apr 2026 19:16:48 +0800 Subject: [PATCH 09/29] add missing EventBloomException for switchFork; throw exception before addAll matchedLog instead after --- framework/src/main/java/org/tron/core/db/Manager.java | 3 ++- .../tron/core/services/jsonrpc/filters/LogMatch.java | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java index 6ccd024091d..0875e0364dc 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -1155,7 +1155,8 @@ private void switchFork(BlockCapsule newHead) | ValidateScheduleException | VMIllegalException | ZksnarkException - | BadBlockException e) { + | BadBlockException + | EventBloomException e) { logger.warn(e.getMessage(), e); exception = e; throw e; diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.java b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.java index 67d229b2948..04994969fc4 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/filters/LogMatch.java @@ -94,14 +94,14 @@ public LogFilterElement[] matchBlockOneByOne() String blockHash = manager.getChainBaseManager().getBlockIdByNum(blockNum).toString(); List matchedLog = matchBlock(logFilterWrapper.getLogFilter(), blockNum, blockHash, transactionInfoList, false); + if (!matchedLog.isEmpty()) { + if (logFilterElementList.size() + matchedLog.size() > LogBlockQuery.MAX_RESULT) { + throw new JsonRpcTooManyResultException( + "query returned more than " + LogBlockQuery.MAX_RESULT + " results"); + } logFilterElementList.addAll(matchedLog); } - - if (logFilterElementList.size() > LogBlockQuery.MAX_RESULT) { - throw new JsonRpcTooManyResultException( - "query returned more than " + LogBlockQuery.MAX_RESULT + " results"); - } } return logFilterElementList.toArray(new LogFilterElement[0]); From 6dd2d44ba8881a51f62acf9cfaf5f7b231081655 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Fri, 17 Apr 2026 19:57:33 +0800 Subject: [PATCH 10/29] use ForkJoinPool as necessary --- .../services/jsonrpc/TronJsonRpcImpl.java | 90 +++++++++++-------- 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index 28e013cf9b2..df5e3f45247 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -168,6 +168,8 @@ public enum RequestSource { private static final String NO_BLOCK_HEADER_BY_HASH = "header for hash not found"; 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(2); /** * thread pool of query section bloom store */ @@ -230,49 +232,59 @@ public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { eventFilterMap = getEventFilter2ResultFull(); } - ForkJoinPool pool = new ForkJoinPool(2); //parallelStream default num(CPU) -1 - pool.submit(() -> eventFilterMap.entrySet().parallelStream().forEach(entry -> { - - LogFilterAndResult logFilterAndResult = entry.getValue(); - if (logFilterAndResult.isExpire()) { - eventFilterMap.remove(entry.getKey()); - return; - } + if (eventFilterMap.size() <= FILTER_PARALLEL_THRESHOLD) { + eventFilterMap.entrySet().forEach( + entry -> processLogFilterEntry(entry, eventFilterMap, logsFilterCapsule)); + } else { + LOGS_FILTER_POOL.submit(() -> eventFilterMap.entrySet().parallelStream() + .forEach(entry -> processLogFilterEntry(entry, eventFilterMap, logsFilterCapsule)) + ).join(); + } + long t2 = System.currentTimeMillis(); + logger.info("handleLogsFilter {} cost {}, filter size {}", + logsFilterCapsule.isSolidified() ? "Solidity" : "Full", t2 - t1, eventFilterMap.size()); + } - long blockNumber = logsFilterCapsule.getBlockNumber(); - long fromBlock = logFilterAndResult.getLogFilterWrapper().getFromBlock(); - long toBlock = logFilterAndResult.getLogFilterWrapper().getToBlock(); - if (!(fromBlock <= blockNumber && blockNumber <= toBlock)) { - return; - } + private static void processLogFilterEntry( + Map.Entry entry, + Map eventFilterMap, + LogsFilterCapsule logsFilterCapsule) { + LogFilterAndResult logFilterAndResult = entry.getValue(); + if (logFilterAndResult.isExpire()) { + eventFilterMap.remove(entry.getKey()); + return; + } - if (logsFilterCapsule.getBloom() != null && !logFilterAndResult.getLogFilterWrapper() - .getLogFilter().matchBloom(logsFilterCapsule.getBloom())) { - return; - } + long blockNumber = logsFilterCapsule.getBlockNumber(); + long fromBlock = logFilterAndResult.getLogFilterWrapper().getFromBlock(); + long toBlock = logFilterAndResult.getLogFilterWrapper().getToBlock(); + if (!(fromBlock <= blockNumber && blockNumber <= toBlock)) { + return; + } - LogFilter logFilter = logFilterAndResult.getLogFilterWrapper().getLogFilter(); - List elements = - LogMatch.matchBlock(logFilter, blockNumber, logsFilterCapsule.getBlockHash(), - logsFilterCapsule.getTxInfoList(), logsFilterCapsule.isRemoved()); + if (logsFilterCapsule.getBloom() != null && !logFilterAndResult.getLogFilterWrapper() + .getLogFilter().matchBloom(logsFilterCapsule.getBloom())) { + return; + } - List localResults = new ArrayList<>(elements.size()); - for (LogFilterElement element : elements) { - LogFilterElement cachedElement; - try { - // compare with hashcode() first, then with equals(). If not exist, put it. - cachedElement = logElementCache.get(element, () -> element); - } catch (ExecutionException e) { - logger.error("Getting/loading LogFilterElement from cache fails", e); // never happen - cachedElement = element; - } - localResults.add(cachedElement); + LogFilter logFilter = logFilterAndResult.getLogFilterWrapper().getLogFilter(); + List elements = + LogMatch.matchBlock(logFilter, blockNumber, logsFilterCapsule.getBlockHash(), + logsFilterCapsule.getTxInfoList(), logsFilterCapsule.isRemoved()); + + List localResults = new ArrayList<>(elements.size()); + for (LogFilterElement element : elements) { + LogFilterElement cachedElement; + try { + // compare with hashcode() first, then with equals(). If not exist, put it. + cachedElement = logElementCache.get(element, () -> element); + } catch (ExecutionException e) { + logger.error("Getting/loading LogFilterElement from cache fails", e); // never happen + cachedElement = element; } - logFilterAndResult.getResult().addAll(localResults); - })).join(); - long t2 = System.currentTimeMillis(); - logger.info("handleLogsFilter {} cost {}, filter size {}", - logsFilterCapsule.isSolidified() ? "Solidity" : "Full", t2 - t1, eventFilterMap.size()); + localResults.add(cachedElement); + } + logFilterAndResult.getResult().addAll(localResults); } @Override @@ -1414,7 +1426,7 @@ public String newFilter(FilterRequest fr) throws JsonRpcInvalidParamsException, } else { eventFilter2Result = eventFilter2ResultSolidity; } - if (eventFilter2Result.size() > maxLogFilterNum) { + if (eventFilter2Result.size() >= maxLogFilterNum) { throw new JsonRpcExceedLimitException( "exceed max log filters: " + maxLogFilterNum + ", try again later"); } From 9259d2b788c83ec206f35a90670b158934d97c2a Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Thu, 23 Apr 2026 17:09:48 +0800 Subject: [PATCH 11/29] set handleLogsFilter to debug --- .../java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index df5e3f45247..2fcb8c12c65 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -169,7 +169,7 @@ public enum RequestSource { 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(2); + private static final ForkJoinPool LOGS_FILTER_POOL = new ForkJoinPool(); /** * thread pool of query section bloom store */ @@ -241,7 +241,7 @@ public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { ).join(); } long t2 = System.currentTimeMillis(); - logger.info("handleLogsFilter {} cost {}, filter size {}", + logger.debug("handleLogsFilter {} cost {}, filter size {}", logsFilterCapsule.isSolidified() ? "Solidity" : "Full", t2 - t1, eventFilterMap.size()); } @@ -1581,6 +1581,7 @@ public static Object[] getFilterResult(String filterId, Map Date: Fri, 24 Apr 2026 22:37:58 +0800 Subject: [PATCH 12/29] give name to LOGS_FILTER_POOL --- .../java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java | 4 ++-- .../src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index 2fcb8c12c65..b71487c60bb 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -169,7 +169,7 @@ public enum RequestSource { 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(); + private static final ForkJoinPool LOGS_FILTER_POOL = new ForkJoinPool(2); /** * thread pool of query section bloom store */ @@ -1581,7 +1581,7 @@ public static Object[] getFilterResult(String filterId, Map Date: Fri, 24 Apr 2026 23:04:13 +0800 Subject: [PATCH 13/29] remove EventBloomException --- framework/src/main/java/org/tron/core/db/Manager.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java index 0875e0364dc..6ccd024091d 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -1155,8 +1155,7 @@ private void switchFork(BlockCapsule newHead) | ValidateScheduleException | VMIllegalException | ZksnarkException - | BadBlockException - | EventBloomException e) { + | BadBlockException e) { logger.warn(e.getMessage(), e); exception = e; throw e; From 938533a145c6cc0eeaad1068a25e3ad3e655852a Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Tue, 28 Apr 2026 23:58:33 +0800 Subject: [PATCH 14/29] extract SecureRandom --- .../java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java | 3 ++- .../java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java b/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java index 08c1068e3a2..45be8a3a14e 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java @@ -60,6 +60,8 @@ public class JsonRpcApiUtil { public static final String TAG_SAFE_SUPPORT_ERROR = "TAG safe not supported"; public static final String BLOCK_NUM_ERROR = "invalid block number"; + static SecureRandom random = new SecureRandom(); + public static byte[] convertToTronAddress(byte[] address) { byte[] newAddress = new byte[21]; byte[] temp = new byte[] {Wallet.getAddressPreFixByte()}; @@ -567,7 +569,6 @@ public static long parseBlockNumber(String blockNumOrTag, Wallet wallet) } public static String generateFilterId() { - SecureRandom random = new SecureRandom(); byte[] uid = new byte[16]; // 128 bits are converted to 16 bytes random.nextBytes(uid); return ByteArray.toHexString(uid); diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index b71487c60bb..87c9b5ac5cb 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -1426,7 +1426,7 @@ public String newFilter(FilterRequest fr) throws JsonRpcInvalidParamsException, } else { eventFilter2Result = eventFilter2ResultSolidity; } - if (eventFilter2Result.size() >= maxLogFilterNum) { + if (maxLogFilterNum >0 && eventFilter2Result.size() >= maxLogFilterNum) { throw new JsonRpcExceedLimitException( "exceed max log filters: " + maxLogFilterNum + ", try again later"); } @@ -1448,7 +1448,7 @@ public String newBlockFilter() throws JsonRpcMethodNotFoundException, } else { blockFilter2Result = blockFilter2ResultSolidity; } - if (blockFilter2Result.size() >= maxBlockFilterNum) { + if (maxBlockFilterNum > 0 && blockFilter2Result.size() >= maxBlockFilterNum) { throw new JsonRpcExceedLimitException( "exceed max block filters: " + maxBlockFilterNum + ", try again later"); } From 5d15ea2f711e3b0b90a90034ad66a92cb70cf9d4 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Wed, 29 Apr 2026 00:01:45 +0800 Subject: [PATCH 15/29] format code --- .../java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index 87c9b5ac5cb..a4bb83641fb 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -1426,7 +1426,7 @@ public String newFilter(FilterRequest fr) throws JsonRpcInvalidParamsException, } else { eventFilter2Result = eventFilter2ResultSolidity; } - if (maxLogFilterNum >0 && eventFilter2Result.size() >= maxLogFilterNum) { + if (maxLogFilterNum > 0 && eventFilter2Result.size() >= maxLogFilterNum) { throw new JsonRpcExceedLimitException( "exceed max log filters: " + maxLogFilterNum + ", try again later"); } From 9493ecf6a597b07dc52b7403cadd9087606070ec Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Wed, 29 Apr 2026 12:56:37 +0800 Subject: [PATCH 16/29] add testcase HandleLogsFilterTest,LogMatchOverLimitTest --- .../core/jsonrpc/HandleLogsFilterTest.java | 194 ++++++++++++++++++ .../core/jsonrpc/LogMatchOverLimitTest.java | 156 ++++++++++++++ .../tron/core/jsonrpc/WalletCursorTest.java | 41 ++++ 3 files changed, 391 insertions(+) create mode 100644 framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java create mode 100644 framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java diff --git a/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java b/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java new file mode 100644 index 00000000000..478c7f6f1ad --- /dev/null +++ b/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java @@ -0,0 +1,194 @@ +package org.tron.core.jsonrpc; + +import static org.tron.core.services.jsonrpc.TronJsonRpcImpl.handleLogsFilter; + +import com.google.protobuf.ByteString; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.tron.common.logsfilter.capsule.LogsFilterCapsule; +import org.tron.common.runtime.vm.DataWord; +import org.tron.common.runtime.vm.LogInfo; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; +import org.tron.core.services.jsonrpc.TronJsonRpc.FilterRequest; +import org.tron.core.services.jsonrpc.TronJsonRpcImpl; +import org.tron.core.services.jsonrpc.filters.FilterResult; +import org.tron.core.services.jsonrpc.filters.LogFilterAndResult; +import org.tron.protos.Protocol.TransactionInfo; + +public class HandleLogsFilterTest { + + private static final String FILTER_ID_1 = "handle-logs-test-001"; + private static final String FILTER_ID_2 = "handle-logs-test-002"; + + @Before + public void setUp() { + cleanMaps(); + } + + @After + public void tearDown() { + cleanMaps(); + } + + private void cleanMaps() { + TronJsonRpcImpl.getEventFilter2ResultFull().remove(FILTER_ID_1); + TronJsonRpcImpl.getEventFilter2ResultFull().remove(FILTER_ID_2); + TronJsonRpcImpl.getEventFilter2ResultSolidity().remove(FILTER_ID_1); + TronJsonRpcImpl.getEventFilter2ResultSolidity().remove(FILTER_ID_2); + } + + private TransactionInfo buildTxInfoWithLog(byte[] address) { + LogInfo logInfo = new LogInfo(address, + Collections.singletonList(new DataWord(new byte[32])), new byte[0]); + return TransactionInfo.newBuilder().addLog(LogInfo.buildLog(logInfo)).build(); + } + + private TransactionInfo buildTxInfoWithLogAddress(String hexAddress) { + byte[] addr = new byte[20]; + byte[] src = ByteString.copyFromUtf8(hexAddress).toByteArray(); + System.arraycopy(src, 0, addr, 0, Math.min(src.length, 20)); + return buildTxInfoWithLog(addr); + } + + /** Events dispatched to a matching filter in the serial (<=10000 entries) path. */ + @Test + public void testMatchingFilter_receivesLogElements() throws JsonRpcInvalidParamsException { + FilterRequest fr = new FilterRequest(); + LogFilterAndResult filterAndResult = new LogFilterAndResult(fr, 100L, null); + TronJsonRpcImpl.getEventFilter2ResultFull().put(FILTER_ID_1, filterAndResult); + + List txInfoList = + Collections.singletonList(buildTxInfoWithLog(new byte[20])); + LogsFilterCapsule capsule = + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); + + handleLogsFilter(capsule); + + Assert.assertEquals(1, filterAndResult.getResult().size()); + } + + /** Filter with fromBlock=100 does not receive a capsule whose blockNumber is 50. */ + @Test + public void testBlockNumberBelowRange_noResult() throws JsonRpcInvalidParamsException { + FilterRequest fr = new FilterRequest(); + // currentMaxBlockNum=100 → fromBlock=100, toBlock=MAX_VALUE + LogFilterAndResult filterAndResult = new LogFilterAndResult(fr, 100L, null); + TronJsonRpcImpl.getEventFilter2ResultFull().put(FILTER_ID_1, filterAndResult); + + List txInfoList = + Collections.singletonList(buildTxInfoWithLog(new byte[20])); + LogsFilterCapsule capsule = + new LogsFilterCapsule(50L, "0xabcdef", null, txInfoList, false, false); + + handleLogsFilter(capsule); + + Assert.assertTrue(filterAndResult.getResult().isEmpty()); + } + + /** An expired filter is removed from the map during handleLogsFilter. */ + @Test + public void testExpiredFilter_removedFromMap() throws Exception { + FilterRequest fr = new FilterRequest(); + LogFilterAndResult filterAndResult = new LogFilterAndResult(fr, 100L, null); + + Field expireField = FilterResult.class.getDeclaredField("expireTimeStamp"); + expireField.setAccessible(true); + expireField.setLong(filterAndResult, 0L); + + Map map = TronJsonRpcImpl.getEventFilter2ResultFull(); + map.put(FILTER_ID_1, filterAndResult); + Assert.assertTrue(map.containsKey(FILTER_ID_1)); + + List txInfoList = + Collections.singletonList(buildTxInfoWithLog(new byte[20])); + LogsFilterCapsule capsule = + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); + + handleLogsFilter(capsule); + + Assert.assertFalse("expired filter should be removed", map.containsKey(FILTER_ID_1)); + } + + /** A solidified capsule is routed only to the solidity map; the full-node map is untouched. */ + @Test + public void testSolidifiedCapsule_routedToSolidityMap() throws JsonRpcInvalidParamsException { + FilterRequest fr = new FilterRequest(); + LogFilterAndResult solidityFilter = new LogFilterAndResult(fr, 100L, null); + TronJsonRpcImpl.getEventFilter2ResultSolidity().put(FILTER_ID_1, solidityFilter); + + LogFilterAndResult fullFilter = new LogFilterAndResult(fr, 100L, null); + TronJsonRpcImpl.getEventFilter2ResultFull().put(FILTER_ID_2, fullFilter); + + List txInfoList = + Collections.singletonList(buildTxInfoWithLog(new byte[20])); + LogsFilterCapsule capsule = + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, true, false); + + handleLogsFilter(capsule); + + Assert.assertEquals(1, solidityFilter.getResult().size()); + Assert.assertTrue("full-node filter must not be touched", fullFilter.getResult().isEmpty()); + } + + /** A non-solidified capsule is routed only to the full-node map. */ + @Test + public void testNonSolidifiedCapsule_routedToFullMap() throws JsonRpcInvalidParamsException { + FilterRequest fr = new FilterRequest(); + LogFilterAndResult solidityFilter = new LogFilterAndResult(fr, 100L, null); + TronJsonRpcImpl.getEventFilter2ResultSolidity().put(FILTER_ID_1, solidityFilter); + + LogFilterAndResult fullFilter = new LogFilterAndResult(fr, 100L, null); + TronJsonRpcImpl.getEventFilter2ResultFull().put(FILTER_ID_2, fullFilter); + + List txInfoList = + Collections.singletonList(buildTxInfoWithLog(new byte[20])); + LogsFilterCapsule capsule = + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); + + handleLogsFilter(capsule); + + Assert.assertEquals(1, fullFilter.getResult().size()); + Assert.assertTrue("solidity filter must not be touched", solidityFilter.getResult().isEmpty()); + } + + /** Both filters in the map receive events when both match. */ + @Test + public void testMultipleMatchingFilters_bothReceiveEvents() throws JsonRpcInvalidParamsException { + FilterRequest fr = new FilterRequest(); + LogFilterAndResult filter1 = new LogFilterAndResult(fr, 100L, null); + LogFilterAndResult filter2 = new LogFilterAndResult(fr, 100L, null); + TronJsonRpcImpl.getEventFilter2ResultFull().put(FILTER_ID_1, filter1); + TronJsonRpcImpl.getEventFilter2ResultFull().put(FILTER_ID_2, filter2); + + List txInfoList = + Collections.singletonList(buildTxInfoWithLog(new byte[20])); + LogsFilterCapsule capsule = + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); + + handleLogsFilter(capsule); + + Assert.assertEquals(1, filter1.getResult().size()); + Assert.assertEquals(1, filter2.getResult().size()); + } + + /** An empty txInfoList produces no results. */ + @Test + public void testEmptyTxInfoList_noResult() throws JsonRpcInvalidParamsException { + FilterRequest fr = new FilterRequest(); + LogFilterAndResult filterAndResult = new LogFilterAndResult(fr, 100L, null); + TronJsonRpcImpl.getEventFilter2ResultFull().put(FILTER_ID_1, filterAndResult); + + LogsFilterCapsule capsule = + new LogsFilterCapsule(150L, "0xabcdef", null, Collections.emptyList(), false, false); + + handleLogsFilter(capsule); + + Assert.assertTrue(filterAndResult.getResult().isEmpty()); + } +} diff --git a/framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java b/framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java new file mode 100644 index 00000000000..61f49c36359 --- /dev/null +++ b/framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java @@ -0,0 +1,156 @@ +package org.tron.core.jsonrpc; + +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.protobuf.ByteString; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.junit.Assert; +import org.junit.Test; +import org.tron.api.GrpcAPI.TransactionInfoList; +import org.tron.common.utils.Sha256Hash; +import org.tron.core.ChainBaseManager; +import org.tron.core.capsule.BlockCapsule; +import org.tron.core.db.Manager; +import org.tron.core.exception.BadItemException; +import org.tron.core.exception.ItemNotFoundException; +import org.tron.core.exception.jsonrpc.JsonRpcInvalidParamsException; +import org.tron.core.exception.jsonrpc.JsonRpcTooManyResultException; +import org.tron.core.services.jsonrpc.TronJsonRpc.FilterRequest; +import org.tron.core.services.jsonrpc.TronJsonRpc.LogFilterElement; +import org.tron.core.services.jsonrpc.filters.LogBlockQuery; +import org.tron.core.services.jsonrpc.filters.LogFilterWrapper; +import org.tron.core.services.jsonrpc.filters.LogMatch; +import org.tron.protos.Protocol.TransactionInfo; +import org.tron.protos.Protocol.TransactionInfo.Log; + +/** + * Verifies the over-limit check in {@link LogMatch#matchBlockOneByOne()} introduced in PR #71. + * The fix ensures the exception is thrown BEFORE {@code addAll}, so the result list never + * silently exceeds {@link LogBlockQuery#MAX_RESULT}. + */ +public class LogMatchOverLimitTest { + + private static final int MAX_RESULT = LogBlockQuery.MAX_RESULT; // 10000 + + /** Builds a TransactionInfoList containing one TransactionInfo with {@code logCount} logs. */ + private TransactionInfoList buildTxList(int logCount) { + TransactionInfo.Builder txBuilder = TransactionInfo.newBuilder(); + for (int i = 0; i < logCount; i++) { + txBuilder.addLog(Log.newBuilder() + .setAddress(ByteString.copyFrom(new byte[20])) + .build()); + } + return TransactionInfoList.newBuilder() + .addTransactionInfo(txBuilder.build()) + .build(); + } + + private Manager buildMockManager(long blockNum, TransactionInfoList txList) + throws ItemNotFoundException { + Manager manager = mock(Manager.class); + ChainBaseManager chainBaseManager = mock(ChainBaseManager.class); + BlockCapsule.BlockId blockId = new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, blockNum); + + when(manager.getChainBaseManager()).thenReturn(chainBaseManager); + when(chainBaseManager.getBlockIdByNum(anyLong())).thenReturn(blockId); + when(manager.getTransactionInfoByBlockNum(blockNum)).thenReturn(txList); + return manager; + } + + private Manager buildMockManager(long block1, TransactionInfoList txList1, + long block2, TransactionInfoList txList2) throws ItemNotFoundException { + Manager manager = mock(Manager.class); + ChainBaseManager chainBaseManager = mock(ChainBaseManager.class); + BlockCapsule.BlockId blockId = new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, 0); + + when(manager.getChainBaseManager()).thenReturn(chainBaseManager); + when(chainBaseManager.getBlockIdByNum(anyLong())).thenReturn(blockId); + when(manager.getTransactionInfoByBlockNum(block1)).thenReturn(txList1); + when(manager.getTransactionInfoByBlockNum(block2)).thenReturn(txList2); + return manager; + } + + private LogMatch buildLogMatch(List blockNums, Manager manager) + throws JsonRpcInvalidParamsException { + FilterRequest fr = new FilterRequest(); // match-all filter + LogFilterWrapper wrapper = new LogFilterWrapper(fr, 0L, null, false); + return new LogMatch(wrapper, blockNums, manager); + } + + /** Under the limit: all logs returned without exception. */ + @Test + public void testUnderLimit_returnsAllResults() + throws BadItemException, ItemNotFoundException, JsonRpcTooManyResultException, + JsonRpcInvalidParamsException { + int logCount = MAX_RESULT / 2; // 5000, well under limit + Manager manager = buildMockManager(100L, buildTxList(logCount)); + LogMatch logMatch = buildLogMatch(Collections.singletonList(100L), manager); + + LogFilterElement[] results = logMatch.matchBlockOneByOne(); + Assert.assertEquals(logCount, results.length); + } + + /** + * The cumulative log count from two blocks equals exactly MAX_RESULT. + * This should succeed (boundary: equal is still OK). + */ + @Test + public void testAtExactLimit_succeeds() + throws BadItemException, ItemNotFoundException, JsonRpcTooManyResultException, + JsonRpcInvalidParamsException { + // block 1: MAX_RESULT - 1 logs, block 2: 1 log → total == MAX_RESULT + Manager manager = buildMockManager( + 1L, buildTxList(MAX_RESULT - 1), + 2L, buildTxList(1)); + LogMatch logMatch = buildLogMatch(Arrays.asList(1L, 2L), manager); + + LogFilterElement[] results = logMatch.matchBlockOneByOne(); + Assert.assertEquals(MAX_RESULT, results.length); + } + + /** + * Verifies the fix: when the second block would push the total over MAX_RESULT, + * {@link JsonRpcTooManyResultException} is thrown BEFORE {@code addAll}. + * The result list must contain only the logs from block 1 (not the partial block-2 logs). + */ + @Test + public void testExceedsLimit_throwsBeforeAddAll() + throws BadItemException, ItemNotFoundException, JsonRpcInvalidParamsException { + // block 1: MAX_RESULT - 1 logs, block 2: 2 logs → 9999 + 2 = 10001 > MAX_RESULT + Manager manager = buildMockManager( + 1L, buildTxList(MAX_RESULT - 1), + 2L, buildTxList(2)); + LogMatch logMatch = buildLogMatch(Arrays.asList(1L, 2L), manager); + + try { + logMatch.matchBlockOneByOne(); + Assert.fail("Expected JsonRpcTooManyResultException"); + } catch (JsonRpcTooManyResultException e) { + Assert.assertTrue(e.getMessage().contains(String.valueOf(MAX_RESULT))); + } + } + + /** A block with no matching logs is skipped without incrementing the result count. */ + @Test + public void testEmptyBlockSkipped() + throws BadItemException, ItemNotFoundException, JsonRpcTooManyResultException, + JsonRpcInvalidParamsException { + // block 1: no logs (empty txInfoList → skipped), block 2: 3 logs + Manager manager = mock(Manager.class); + ChainBaseManager chainBaseManager = mock(ChainBaseManager.class); + BlockCapsule.BlockId blockId = new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, 0); + when(manager.getChainBaseManager()).thenReturn(chainBaseManager); + when(chainBaseManager.getBlockIdByNum(anyLong())).thenReturn(blockId); + when(manager.getTransactionInfoByBlockNum(1L)) + .thenReturn(TransactionInfoList.newBuilder().build()); // empty + when(manager.getTransactionInfoByBlockNum(2L)).thenReturn(buildTxList(3)); + + LogMatch logMatch = buildLogMatch(Arrays.asList(1L, 2L), manager); + LogFilterElement[] results = logMatch.matchBlockOneByOne(); + Assert.assertEquals(3, results.length); + } +} diff --git a/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java b/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java index b1d5c40e835..16901920383 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java @@ -1,6 +1,9 @@ package org.tron.core.jsonrpc; import com.google.protobuf.ByteString; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; @@ -13,9 +16,12 @@ import org.tron.core.capsule.AccountCapsule; import org.tron.core.config.args.Args; import org.tron.core.db2.core.Chainbase.Cursor; +import org.tron.core.exception.jsonrpc.JsonRpcExceedLimitException; import org.tron.core.services.NodeInfoService; +import org.tron.core.services.jsonrpc.TronJsonRpc.FilterRequest; import org.tron.core.services.jsonrpc.TronJsonRpcImpl; import org.tron.core.services.jsonrpc.TronJsonRpcImpl.RequestSource; +import org.tron.core.services.jsonrpc.filters.LogFilterAndResult; import org.tron.core.services.jsonrpc.types.BuildArguments; import org.tron.protos.Protocol; @@ -143,4 +149,39 @@ public void testEnableInFullNode() { } } + /** + * When the active filter count reaches the configured cap (node.jsonrpc.maxLogFilterNum), + * eth_newFilter must throw JsonRpcExceedLimitException instead of growing without bound. + */ + @Test + public void testNewFilter_exceedsCapThrowsException() throws Exception { + int cap = Args.getInstance().getJsonRpcMaxLogFilterNum(); + FilterRequest fr = new FilterRequest(); + Map map = TronJsonRpcImpl.getEventFilter2ResultFull(); + List addedKeys = new ArrayList<>(); + + try { + for (int i = 0; i < cap; i++) { + String key = "walletcursor-cap-test-" + i; + map.put(key, new LogFilterAndResult(fr, 0L, null)); + addedKeys.add(key); + } + Assert.assertEquals(cap, addedKeys.size()); + + TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, dbManager); + try { + tronJsonRpc.newFilter(fr); + Assert.fail("Expected JsonRpcExceedLimitException when filter count reaches cap"); + } catch (JsonRpcExceedLimitException e) { + Assert.assertTrue(e.getMessage().contains(String.valueOf(cap))); + } finally { + tronJsonRpc.close(); + } + } finally { + for (String key : addedKeys) { + map.remove(key); + } + } + } + } \ No newline at end of file From e99045da7c3fb5ee38c468e13a5b3da5926c8276 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Wed, 29 Apr 2026 13:05:48 +0800 Subject: [PATCH 17/29] add comment of config file --- framework/src/main/resources/config.conf | 2 +- .../java/org/tron/core/jsonrpc/HandleLogsFilterTest.java | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/framework/src/main/resources/config.conf b/framework/src/main/resources/config.conf index 2e2418cbe47..79794be57ab 100644 --- a/framework/src/main/resources/config.conf +++ b/framework/src/main/resources/config.conf @@ -375,7 +375,7 @@ node { maxSubTopics = 1000 # Allowed maximum number for blockFilter maxBlockFilterNum = 50000 - # Allowed maximum number for newFilter + # Allowed maximum number for newFilter, >0 otherwise no limit maxLogFilterNum = 20000 } diff --git a/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java b/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java index 478c7f6f1ad..26e5c76fb36 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java @@ -2,7 +2,6 @@ import static org.tron.core.services.jsonrpc.TronJsonRpcImpl.handleLogsFilter; -import com.google.protobuf.ByteString; import java.lang.reflect.Field; import java.util.Collections; import java.util.List; @@ -49,13 +48,6 @@ private TransactionInfo buildTxInfoWithLog(byte[] address) { return TransactionInfo.newBuilder().addLog(LogInfo.buildLog(logInfo)).build(); } - private TransactionInfo buildTxInfoWithLogAddress(String hexAddress) { - byte[] addr = new byte[20]; - byte[] src = ByteString.copyFromUtf8(hexAddress).toByteArray(); - System.arraycopy(src, 0, addr, 0, Math.min(src.length, 20)); - return buildTxInfoWithLog(addr); - } - /** Events dispatched to a matching filter in the serial (<=10000 entries) path. */ @Test public void testMatchingFilter_receivesLogElements() throws JsonRpcInvalidParamsException { From ad0d6888bf852aff91e13755c08f278f9490a33f Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Wed, 29 Apr 2026 16:47:00 +0800 Subject: [PATCH 18/29] fix the comment of maxLogFilterNum in reference.conf --- common/src/main/resources/reference.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/resources/reference.conf b/common/src/main/resources/reference.conf index addaa870eea..1bf7af82408 100644 --- a/common/src/main/resources/reference.conf +++ b/common/src/main/resources/reference.conf @@ -401,7 +401,7 @@ node { # Maximum number for blockFilter maxBlockFilterNum = 50000 - # Maximum number of log entries returned by eth_getLogs / eth_newFilter + # Maximum number of concurrent eth_newFilter registrations, >0 otherwise no limit maxLogFilterNum = 20000 } From a48b3b2778ce4883afabd189f1018b60e57eee9d Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Thu, 30 Apr 2026 13:33:34 +0800 Subject: [PATCH 19/29] drop static modifier of some varibles in TronJsonRpcImpl --- .../capsule/BlockFilterCapsule.java | 15 +- .../logsfilter/capsule/LogsFilterCapsule.java | 16 +- .../main/java/org/tron/core/db/Manager.java | 11 +- .../services/jsonrpc/TronJsonRpcImpl.java | 32 ++-- .../capsule/BlockFilterCapsuleTest.java | 7 +- .../core/jsonrpc/ConcurrentHashMapTest.java | 13 +- .../core/jsonrpc/HandleLogsFilterTest.java | 157 ++++++++++++++---- .../core/jsonrpc/LogMatchOverLimitTest.java | 3 +- .../tron/core/jsonrpc/WalletCursorTest.java | 10 +- 9 files changed, 190 insertions(+), 74 deletions(-) diff --git a/framework/src/main/java/org/tron/common/logsfilter/capsule/BlockFilterCapsule.java b/framework/src/main/java/org/tron/common/logsfilter/capsule/BlockFilterCapsule.java index 9cf3c0c690e..e802b0a8f3f 100644 --- a/framework/src/main/java/org/tron/common/logsfilter/capsule/BlockFilterCapsule.java +++ b/framework/src/main/java/org/tron/common/logsfilter/capsule/BlockFilterCapsule.java @@ -1,12 +1,11 @@ package org.tron.common.logsfilter.capsule; -import static org.tron.core.services.jsonrpc.TronJsonRpcImpl.handleBLockFilter; - import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.tron.core.capsule.BlockCapsule; +import org.tron.core.services.jsonrpc.TronJsonRpcImpl; @Slf4j(topic = "API") @ToString @@ -19,19 +18,21 @@ public class BlockFilterCapsule extends FilterTriggerCapsule { @Setter private boolean solidified; - public BlockFilterCapsule(BlockCapsule block, boolean solidified) { - blockHash = block.getBlockId().toString(); - this.solidified = solidified; + private final TronJsonRpcImpl jsonRpc; + + public BlockFilterCapsule(BlockCapsule block, boolean solidified, TronJsonRpcImpl jsonRpc) { + this(block.getBlockId().toString(), solidified, jsonRpc); } - public BlockFilterCapsule(String blockHash, boolean solidified) { + public BlockFilterCapsule(String blockHash, boolean solidified, TronJsonRpcImpl jsonRpc) { this.blockHash = blockHash; this.solidified = solidified; + this.jsonRpc = jsonRpc; } @Override public void processFilterTrigger() { - handleBLockFilter(this); + jsonRpc.handleBLockFilter(this); } } diff --git a/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java b/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java index 8a8e122d9a0..e0cc797417b 100644 --- a/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java +++ b/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java @@ -1,13 +1,12 @@ package org.tron.common.logsfilter.capsule; -import static org.tron.core.services.jsonrpc.TronJsonRpcImpl.handleLogsFilter; - import java.util.List; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.tron.common.bloom.Bloom; +import org.tron.core.services.jsonrpc.TronJsonRpcImpl; import org.tron.protos.Protocol.TransactionInfo; @Slf4j(topic = "API") @@ -33,18 +32,29 @@ public class LogsFilterCapsule extends FilterTriggerCapsule { @Setter private boolean removed; + private final TronJsonRpcImpl jsonRpc; + public LogsFilterCapsule(long blockNumber, String blockHash, Bloom bloom, List txInfoList, boolean solidified, boolean removed) { + this(blockNumber, blockHash, bloom, txInfoList, solidified, removed, null); + } + + public LogsFilterCapsule(long blockNumber, String blockHash, Bloom bloom, + List txInfoList, boolean solidified, boolean removed, + TronJsonRpcImpl jsonRpc) { this.blockNumber = blockNumber; this.blockHash = blockHash; this.bloom = bloom; this.txInfoList = txInfoList; this.solidified = solidified; this.removed = removed; + this.jsonRpc = jsonRpc; } @Override public void processFilterTrigger() { - handleLogsFilter(this); + if (jsonRpc != null) { + jsonRpc.handleLogsFilter(this); + } } } \ No newline at end of file diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java index 6ccd024091d..d7541a90476 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -48,6 +48,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.bouncycastle.util.encoders.Hex; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.tron.api.GrpcAPI; import org.tron.api.GrpcAPI.TransactionInfoList; @@ -141,6 +142,7 @@ import org.tron.core.service.MortgageService; import org.tron.core.service.RewardViCalService; import org.tron.core.services.event.exception.EventException; +import org.tron.core.services.jsonrpc.TronJsonRpcImpl; import org.tron.core.store.AccountAssetStore; import org.tron.core.store.AccountIdIndexStore; import org.tron.core.store.AccountIndexStore; @@ -276,6 +278,10 @@ public class Manager { @Autowired private RewardViCalService rewardViCalService; + @Lazy + @Autowired + private TronJsonRpcImpl tronJsonRpcImpl; + /** * Cycle thread to rePush Transactions */ @@ -2264,7 +2270,8 @@ private void reOrgLogsFilter() { } private void postBlockFilter(final BlockCapsule blockCapsule, boolean solidified) { - BlockFilterCapsule blockFilterCapsule = new BlockFilterCapsule(blockCapsule, solidified); + BlockFilterCapsule blockFilterCapsule = + new BlockFilterCapsule(blockCapsule, solidified, tronJsonRpcImpl); if (!filterCapsuleQueue.offer(blockFilterCapsule)) { logger.info("Too many filters, block filter lost: {}.", blockCapsule.getBlockId()); } @@ -2278,7 +2285,7 @@ private void postLogsFilter(final BlockCapsule blockCapsule, boolean solidified, = getTransactionInfoByBlockNum(blockNumber).getTransactionInfoList(); LogsFilterCapsule logsFilterCapsule = new LogsFilterCapsule(blockNumber, blockCapsule.getBlockId().toString(), blockCapsule.getBloom(), transactionInfoList, - solidified, removed); + solidified, removed, tronJsonRpcImpl); if (!filterCapsuleQueue.offer(logsFilterCapsule)) { logger.info("Too many filters, logs filter lost: {}.", blockNumber); diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index a4bb83641fb..d5820c84c91 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -118,14 +118,14 @@ public enum RequestSource { private static final String FILTER_NOT_FOUND = "filter not found"; public static final int EXPIRE_SECONDS = 5 * 60; - private static final int maxBlockFilterNum = Args.getInstance().getJsonRpcMaxBlockFilterNum(); - private static final int maxLogFilterNum = Args.getInstance().getJsonRpcMaxLogFilterNum(); - private static final Cache logElementCache = + private final int maxBlockFilterNum = Args.getInstance().getJsonRpcMaxBlockFilterNum(); + private final int maxLogFilterNum = Args.getInstance().getJsonRpcMaxLogFilterNum(); + private final Cache logElementCache = CacheBuilder.newBuilder() .maximumSize(300_000L) // 300s * tps(1000) * 1 log/tx ≈ 300_000 .expireAfterWrite(EXPIRE_SECONDS, TimeUnit.SECONDS) .recordStats().build(); //LRU cache - private static final Cache blockHashCache = + private final Cache blockHashCache = CacheBuilder.newBuilder() .maximumSize(60_000L) // 300s * 200 block/s when syncing .expireAfterWrite(EXPIRE_SECONDS, TimeUnit.SECONDS) @@ -134,25 +134,25 @@ public enum RequestSource { * for log filter in Full Json-RPC */ @Getter - private static final Map eventFilter2ResultFull = + private final Map eventFilter2ResultFull = new ConcurrentHashMap<>(); /** * for block in Full Json-RPC */ @Getter - private static final Map blockFilter2ResultFull = + private final Map blockFilter2ResultFull = new ConcurrentHashMap<>(); /** * for log filter in solidity Json-RPC */ @Getter - private static final Map eventFilter2ResultSolidity = + private final Map eventFilter2ResultSolidity = new ConcurrentHashMap<>(); /** * for block in solidity Json-RPC */ @Getter - private static final Map blockFilter2ResultSolidity = + private final Map blockFilter2ResultSolidity = new ConcurrentHashMap<>(); public static final String HASH_REGEX = "(0x)?[a-zA-Z0-9]{64}$"; @@ -168,8 +168,8 @@ public enum RequestSource { private static final String NO_BLOCK_HEADER_BY_HASH = "header for hash not found"; 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(2); + private final int filterParallelThreshold = 10000; + private final ForkJoinPool logsFilterPool = new ForkJoinPool(2); /** * thread pool of query section bloom store */ @@ -188,7 +188,7 @@ public TronJsonRpcImpl(@Autowired NodeInfoService nodeInfoService, @Autowired Wa this.sectionExecutor = ExecutorServiceManager.newFixedThreadPool(esName, 5); } - public static void handleBLockFilter(BlockFilterCapsule blockFilterCapsule) { + public void handleBLockFilter(BlockFilterCapsule blockFilterCapsule) { Iterator> it; if (blockFilterCapsule.isSolidified()) { @@ -222,7 +222,7 @@ public static void handleBLockFilter(BlockFilterCapsule blockFilterCapsule) { /** * append LogsFilterCapsule's LogFilterElement list to each filter if matched */ - public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { + public void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { long t1 = System.currentTimeMillis(); Map eventFilterMap; @@ -232,11 +232,11 @@ public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { eventFilterMap = getEventFilter2ResultFull(); } - if (eventFilterMap.size() <= FILTER_PARALLEL_THRESHOLD) { + if (eventFilterMap.size() <= filterParallelThreshold) { eventFilterMap.entrySet().forEach( entry -> processLogFilterEntry(entry, eventFilterMap, logsFilterCapsule)); } else { - LOGS_FILTER_POOL.submit(() -> eventFilterMap.entrySet().parallelStream() + logsFilterPool.submit(() -> eventFilterMap.entrySet().parallelStream() .forEach(entry -> processLogFilterEntry(entry, eventFilterMap, logsFilterCapsule)) ).join(); } @@ -245,7 +245,7 @@ public static void handleLogsFilter(LogsFilterCapsule logsFilterCapsule) { logsFilterCapsule.isSolidified() ? "Solidity" : "Full", t2 - t1, eventFilterMap.size()); } - private static void processLogFilterEntry( + private void processLogFilterEntry( Map.Entry entry, Map eventFilterMap, LogsFilterCapsule logsFilterCapsule) { @@ -1581,7 +1581,7 @@ public static Object[] getFilterResult(String filterId, Map conMap = TronJsonRpcImpl.getBlockFilter2ResultFull(); + Map conMap = jsonRpc.getBlockFilter2ResultFull(); Map> resultMap1 = new ConcurrentHashMap<>(); // used to check result Map> resultMap2 = new ConcurrentHashMap<>(); // used to check result Map> resultMap3 = new ConcurrentHashMap<>(); // used to check result @@ -70,8 +71,8 @@ public void testHandleBlockHash() { for (int j = 1 + (i - 1) * eachCount; j <= i * eachCount; j++) { BlockFilterCapsule blockFilterCapsule = - new BlockFilterCapsule(String.valueOf(j), false); - TronJsonRpcImpl.handleBLockFilter(blockFilterCapsule); + new BlockFilterCapsule(String.valueOf(j), false, jsonRpc); + jsonRpc.handleBLockFilter(blockFilterCapsule); } try { Thread.sleep(randomInt(50, 100)); @@ -97,7 +98,7 @@ public void testHandleBlockHash() { for (int k = 0; k < 5; k++) { try { Object[] blockHashList = TronJsonRpcImpl.getFilterResult(String.valueOf(k), conMap, - TronJsonRpcImpl.getEventFilter2ResultFull()); + jsonRpc.getEventFilter2ResultFull()); for (Object str : blockHashList) { resultMap1.get(String.valueOf(k)).add(str.toString()); @@ -125,7 +126,7 @@ public void testHandleBlockHash() { for (int k = 0; k < 5; k++) { try { Object[] blockHashList = TronJsonRpcImpl.getFilterResult(String.valueOf(k), conMap, - TronJsonRpcImpl.getEventFilter2ResultFull()); + jsonRpc.getEventFilter2ResultFull()); // if (blockHashList.length == 0) { // continue; @@ -157,7 +158,7 @@ public void testHandleBlockHash() { for (int k = 0; k < 5; k++) { try { Object[] blockHashList = TronJsonRpcImpl.getFilterResult(String.valueOf(k), conMap, - TronJsonRpcImpl.getEventFilter2ResultFull()); + jsonRpc.getEventFilter2ResultFull()); for (Object str : blockHashList) { try { diff --git a/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java b/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java index 26e5c76fb36..f353068aa47 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java @@ -1,7 +1,5 @@ package org.tron.core.jsonrpc; -import static org.tron.core.services.jsonrpc.TronJsonRpcImpl.handleLogsFilter; - import java.lang.reflect.Field; import java.util.Collections; import java.util.List; @@ -25,21 +23,16 @@ public class HandleLogsFilterTest { private static final String FILTER_ID_1 = "handle-logs-test-001"; private static final String FILTER_ID_2 = "handle-logs-test-002"; + private TronJsonRpcImpl jsonRpc; + @Before public void setUp() { - cleanMaps(); + jsonRpc = new TronJsonRpcImpl(null, null, null); } @After - public void tearDown() { - cleanMaps(); - } - - private void cleanMaps() { - TronJsonRpcImpl.getEventFilter2ResultFull().remove(FILTER_ID_1); - TronJsonRpcImpl.getEventFilter2ResultFull().remove(FILTER_ID_2); - TronJsonRpcImpl.getEventFilter2ResultSolidity().remove(FILTER_ID_1); - TronJsonRpcImpl.getEventFilter2ResultSolidity().remove(FILTER_ID_2); + public void tearDown() throws Exception { + jsonRpc.close(); } private TransactionInfo buildTxInfoWithLog(byte[] address) { @@ -53,14 +46,14 @@ private TransactionInfo buildTxInfoWithLog(byte[] address) { public void testMatchingFilter_receivesLogElements() throws JsonRpcInvalidParamsException { FilterRequest fr = new FilterRequest(); LogFilterAndResult filterAndResult = new LogFilterAndResult(fr, 100L, null); - TronJsonRpcImpl.getEventFilter2ResultFull().put(FILTER_ID_1, filterAndResult); + jsonRpc.getEventFilter2ResultFull().put(FILTER_ID_1, filterAndResult); List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); - handleLogsFilter(capsule); + jsonRpc.handleLogsFilter(capsule); Assert.assertEquals(1, filterAndResult.getResult().size()); } @@ -71,14 +64,14 @@ public void testBlockNumberBelowRange_noResult() throws JsonRpcInvalidParamsExce FilterRequest fr = new FilterRequest(); // currentMaxBlockNum=100 → fromBlock=100, toBlock=MAX_VALUE LogFilterAndResult filterAndResult = new LogFilterAndResult(fr, 100L, null); - TronJsonRpcImpl.getEventFilter2ResultFull().put(FILTER_ID_1, filterAndResult); + jsonRpc.getEventFilter2ResultFull().put(FILTER_ID_1, filterAndResult); List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = new LogsFilterCapsule(50L, "0xabcdef", null, txInfoList, false, false); - handleLogsFilter(capsule); + jsonRpc.handleLogsFilter(capsule); Assert.assertTrue(filterAndResult.getResult().isEmpty()); } @@ -93,7 +86,7 @@ public void testExpiredFilter_removedFromMap() throws Exception { expireField.setAccessible(true); expireField.setLong(filterAndResult, 0L); - Map map = TronJsonRpcImpl.getEventFilter2ResultFull(); + Map map = jsonRpc.getEventFilter2ResultFull(); map.put(FILTER_ID_1, filterAndResult); Assert.assertTrue(map.containsKey(FILTER_ID_1)); @@ -102,7 +95,7 @@ public void testExpiredFilter_removedFromMap() throws Exception { LogsFilterCapsule capsule = new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); - handleLogsFilter(capsule); + jsonRpc.handleLogsFilter(capsule); Assert.assertFalse("expired filter should be removed", map.containsKey(FILTER_ID_1)); } @@ -112,17 +105,17 @@ public void testExpiredFilter_removedFromMap() throws Exception { public void testSolidifiedCapsule_routedToSolidityMap() throws JsonRpcInvalidParamsException { FilterRequest fr = new FilterRequest(); LogFilterAndResult solidityFilter = new LogFilterAndResult(fr, 100L, null); - TronJsonRpcImpl.getEventFilter2ResultSolidity().put(FILTER_ID_1, solidityFilter); + jsonRpc.getEventFilter2ResultSolidity().put(FILTER_ID_1, solidityFilter); LogFilterAndResult fullFilter = new LogFilterAndResult(fr, 100L, null); - TronJsonRpcImpl.getEventFilter2ResultFull().put(FILTER_ID_2, fullFilter); + jsonRpc.getEventFilter2ResultFull().put(FILTER_ID_2, fullFilter); List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, true, false); - handleLogsFilter(capsule); + jsonRpc.handleLogsFilter(capsule); Assert.assertEquals(1, solidityFilter.getResult().size()); Assert.assertTrue("full-node filter must not be touched", fullFilter.getResult().isEmpty()); @@ -133,17 +126,17 @@ public void testSolidifiedCapsule_routedToSolidityMap() throws JsonRpcInvalidPar public void testNonSolidifiedCapsule_routedToFullMap() throws JsonRpcInvalidParamsException { FilterRequest fr = new FilterRequest(); LogFilterAndResult solidityFilter = new LogFilterAndResult(fr, 100L, null); - TronJsonRpcImpl.getEventFilter2ResultSolidity().put(FILTER_ID_1, solidityFilter); + jsonRpc.getEventFilter2ResultSolidity().put(FILTER_ID_1, solidityFilter); LogFilterAndResult fullFilter = new LogFilterAndResult(fr, 100L, null); - TronJsonRpcImpl.getEventFilter2ResultFull().put(FILTER_ID_2, fullFilter); + jsonRpc.getEventFilter2ResultFull().put(FILTER_ID_2, fullFilter); List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); - handleLogsFilter(capsule); + jsonRpc.handleLogsFilter(capsule); Assert.assertEquals(1, fullFilter.getResult().size()); Assert.assertTrue("solidity filter must not be touched", solidityFilter.getResult().isEmpty()); @@ -155,15 +148,15 @@ public void testMultipleMatchingFilters_bothReceiveEvents() throws JsonRpcInvali FilterRequest fr = new FilterRequest(); LogFilterAndResult filter1 = new LogFilterAndResult(fr, 100L, null); LogFilterAndResult filter2 = new LogFilterAndResult(fr, 100L, null); - TronJsonRpcImpl.getEventFilter2ResultFull().put(FILTER_ID_1, filter1); - TronJsonRpcImpl.getEventFilter2ResultFull().put(FILTER_ID_2, filter2); + jsonRpc.getEventFilter2ResultFull().put(FILTER_ID_1, filter1); + jsonRpc.getEventFilter2ResultFull().put(FILTER_ID_2, filter2); List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); - handleLogsFilter(capsule); + jsonRpc.handleLogsFilter(capsule); Assert.assertEquals(1, filter1.getResult().size()); Assert.assertEquals(1, filter2.getResult().size()); @@ -174,13 +167,119 @@ public void testMultipleMatchingFilters_bothReceiveEvents() throws JsonRpcInvali public void testEmptyTxInfoList_noResult() throws JsonRpcInvalidParamsException { FilterRequest fr = new FilterRequest(); LogFilterAndResult filterAndResult = new LogFilterAndResult(fr, 100L, null); - TronJsonRpcImpl.getEventFilter2ResultFull().put(FILTER_ID_1, filterAndResult); + jsonRpc.getEventFilter2ResultFull().put(FILTER_ID_1, filterAndResult); LogsFilterCapsule capsule = new LogsFilterCapsule(150L, "0xabcdef", null, Collections.emptyList(), false, false); - handleLogsFilter(capsule); + jsonRpc.handleLogsFilter(capsule); Assert.assertTrue(filterAndResult.getResult().isEmpty()); } + + // ------------------------------------------------------------------------- + // Parallel path tests (filterParallelThreshold lowered to 2 via reflection) + // ------------------------------------------------------------------------- + + private void setParallelThreshold(int value) throws Exception { + Field f = TronJsonRpcImpl.class.getDeclaredField("filterParallelThreshold"); + f.setAccessible(true); + f.setInt(jsonRpc, value); + } + + /** + * Parallel path: every matching filter receives exactly one event — no events dropped or + * double-counted under concurrent dispatch. + */ + @Test(timeout = 10000) + public void testParallelPath_allMatchingFilters_receiveEvents() throws Exception { + setParallelThreshold(2); + int count = 5; + FilterRequest fr = new FilterRequest(); + List txInfoList = + Collections.singletonList(buildTxInfoWithLog(new byte[20])); + Map map = jsonRpc.getEventFilter2ResultFull(); + String prefix = "parallel-match-"; + for (int i = 0; i < count; i++) { + map.put(prefix + i, new LogFilterAndResult(fr, 0L, null)); + } + + LogsFilterCapsule capsule = + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); + jsonRpc.handleLogsFilter(capsule); + + for (int i = 0; i < count; i++) { + Assert.assertEquals("filter " + i + " must receive exactly one event", + 1, map.get(prefix + i).getResult().size()); + } + } + + /** + * Parallel path: expired filters are evicted and all valid filters still receive their events. + */ + @Test(timeout = 10000) + public void testParallelPath_expiredFiltersRemoved() throws Exception { + setParallelThreshold(2); + int expiredCount = 2; + int validCount = 3; + FilterRequest fr = new FilterRequest(); + Field expireField = FilterResult.class.getDeclaredField("expireTimeStamp"); + expireField.setAccessible(true); + Map map = jsonRpc.getEventFilter2ResultFull(); + String prefix = "parallel-expire-"; + for (int i = 0; i < expiredCount + validCount; i++) { + LogFilterAndResult filter = new LogFilterAndResult(fr, 0L, null); + if (i < expiredCount) { + expireField.setLong(filter, 0L); + } + map.put(prefix + i, filter); + } + + List txInfoList = + Collections.singletonList(buildTxInfoWithLog(new byte[20])); + LogsFilterCapsule capsule = + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); + jsonRpc.handleLogsFilter(capsule); + + for (int i = 0; i < expiredCount; i++) { + Assert.assertFalse("expired filter " + i + " should be removed", + map.containsKey(prefix + i)); + } + for (int i = expiredCount; i < expiredCount + validCount; i++) { + Assert.assertEquals("valid filter " + i + " must receive one event", + 1, map.get(prefix + i).getResult().size()); + } + } + + /** + * Parallel path: a solidified capsule dispatches only to the solidity map; the full-node map + * is untouched even though it holds entries. + */ + @Test(timeout = 10000) + public void testParallelPath_solidifiedCapsule_routedToSolidityMap() throws Exception { + setParallelThreshold(2); + int count = 5; + FilterRequest fr = new FilterRequest(); + List txInfoList = + Collections.singletonList(buildTxInfoWithLog(new byte[20])); + Map solidityMap = jsonRpc.getEventFilter2ResultSolidity(); + Map fullMap = jsonRpc.getEventFilter2ResultFull(); + String solidityPrefix = "parallel-solid-"; + for (int i = 0; i < count; i++) { + solidityMap.put(solidityPrefix + i, new LogFilterAndResult(fr, 0L, null)); + } + LogFilterAndResult fullFilter = new LogFilterAndResult(fr, 0L, null); + fullMap.put("parallel-solid-full-0", fullFilter); + + LogsFilterCapsule capsule = + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, true, false); + jsonRpc.handleLogsFilter(capsule); + + for (int i = 0; i < count; i++) { + Assert.assertEquals("solidity filter " + i + " must receive one event", + 1, solidityMap.get(solidityPrefix + i).getResult().size()); + } + Assert.assertTrue("full-map filter must not receive events", + fullFilter.getResult().isEmpty()); + } } diff --git a/framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java b/framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java index 61f49c36359..75eda4268b0 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java @@ -28,7 +28,7 @@ import org.tron.protos.Protocol.TransactionInfo.Log; /** - * Verifies the over-limit check in {@link LogMatch#matchBlockOneByOne()} introduced in PR #71. + * Verifies the over-limit check in {@link LogMatch#matchBlockOneByOne()} * The fix ensures the exception is thrown BEFORE {@code addAll}, so the result list never * silently exceeds {@link LogBlockQuery#MAX_RESULT}. */ @@ -115,7 +115,6 @@ public void testAtExactLimit_succeeds() /** * Verifies the fix: when the second block would push the total over MAX_RESULT, * {@link JsonRpcTooManyResultException} is thrown BEFORE {@code addAll}. - * The result list must contain only the logs from block 1 (not the partial block-2 logs). */ @Test public void testExceedsLimit_throwsBeforeAddAll() diff --git a/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java b/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java index 16901920383..fdddcefa157 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java @@ -157,7 +157,8 @@ public void testEnableInFullNode() { public void testNewFilter_exceedsCapThrowsException() throws Exception { int cap = Args.getInstance().getJsonRpcMaxLogFilterNum(); FilterRequest fr = new FilterRequest(); - Map map = TronJsonRpcImpl.getEventFilter2ResultFull(); + TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, dbManager); + Map map = tronJsonRpc.getEventFilter2ResultFull(); List addedKeys = new ArrayList<>(); try { @@ -168,19 +169,14 @@ public void testNewFilter_exceedsCapThrowsException() throws Exception { } Assert.assertEquals(cap, addedKeys.size()); - TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, dbManager); try { tronJsonRpc.newFilter(fr); Assert.fail("Expected JsonRpcExceedLimitException when filter count reaches cap"); } catch (JsonRpcExceedLimitException e) { Assert.assertTrue(e.getMessage().contains(String.valueOf(cap))); - } finally { - tronJsonRpc.close(); } } finally { - for (String key : addedKeys) { - map.remove(key); - } + tronJsonRpc.close(); } } From 0f48bdaeeaa8aba0c7df60f792f000e1e668ef04 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Thu, 30 Apr 2026 13:39:26 +0800 Subject: [PATCH 20/29] optimize testcase --- .../logsfilter/capsule/LogsFilterCapsule.java | 9 +-------- .../capsule/LogsFilterCapsuleTest.java | 5 ++++- .../core/jsonrpc/HandleLogsFilterTest.java | 20 +++++++++---------- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java b/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java index e0cc797417b..0c387abd8ff 100644 --- a/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java +++ b/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java @@ -34,11 +34,6 @@ public class LogsFilterCapsule extends FilterTriggerCapsule { private final TronJsonRpcImpl jsonRpc; - public LogsFilterCapsule(long blockNumber, String blockHash, Bloom bloom, - List txInfoList, boolean solidified, boolean removed) { - this(blockNumber, blockHash, bloom, txInfoList, solidified, removed, null); - } - public LogsFilterCapsule(long blockNumber, String blockHash, Bloom bloom, List txInfoList, boolean solidified, boolean removed, TronJsonRpcImpl jsonRpc) { @@ -53,8 +48,6 @@ public LogsFilterCapsule(long blockNumber, String blockHash, Bloom bloom, @Override public void processFilterTrigger() { - if (jsonRpc != null) { - jsonRpc.handleLogsFilter(this); - } + jsonRpc.handleLogsFilter(this); } } \ No newline at end of file diff --git a/framework/src/test/java/org/tron/common/logsfilter/capsule/LogsFilterCapsuleTest.java b/framework/src/test/java/org/tron/common/logsfilter/capsule/LogsFilterCapsuleTest.java index 691a3106b49..ff1f0d04a34 100644 --- a/framework/src/test/java/org/tron/common/logsfilter/capsule/LogsFilterCapsuleTest.java +++ b/framework/src/test/java/org/tron/common/logsfilter/capsule/LogsFilterCapsuleTest.java @@ -6,16 +6,19 @@ import org.junit.Before; import org.junit.Test; import org.tron.common.bloom.Bloom; +import org.tron.core.services.jsonrpc.TronJsonRpcImpl; public class LogsFilterCapsuleTest { private LogsFilterCapsule capsule; + private TronJsonRpcImpl jsonRpc; @Before public void setUp() { + jsonRpc = new TronJsonRpcImpl(null, null, null); capsule = new LogsFilterCapsule(0, "e58f33f9baf9305dc6f82b9f1934ea8f0ade2defb951258d50167028c780351f", - new Bloom(), new ArrayList<>(), true, false); + new Bloom(), new ArrayList<>(), true, false, jsonRpc); } @Test diff --git a/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java b/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java index f353068aa47..ecf4f86e494 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java @@ -51,7 +51,7 @@ public void testMatchingFilter_receivesLogElements() throws JsonRpcInvalidParams List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false, jsonRpc); jsonRpc.handleLogsFilter(capsule); @@ -69,7 +69,7 @@ public void testBlockNumberBelowRange_noResult() throws JsonRpcInvalidParamsExce List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = - new LogsFilterCapsule(50L, "0xabcdef", null, txInfoList, false, false); + new LogsFilterCapsule(50L, "0xabcdef", null, txInfoList, false, false, jsonRpc); jsonRpc.handleLogsFilter(capsule); @@ -93,7 +93,7 @@ public void testExpiredFilter_removedFromMap() throws Exception { List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false, jsonRpc); jsonRpc.handleLogsFilter(capsule); @@ -113,7 +113,7 @@ public void testSolidifiedCapsule_routedToSolidityMap() throws JsonRpcInvalidPar List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, true, false); + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, true, false, jsonRpc); jsonRpc.handleLogsFilter(capsule); @@ -134,7 +134,7 @@ public void testNonSolidifiedCapsule_routedToFullMap() throws JsonRpcInvalidPara List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false, jsonRpc); jsonRpc.handleLogsFilter(capsule); @@ -154,7 +154,7 @@ public void testMultipleMatchingFilters_bothReceiveEvents() throws JsonRpcInvali List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false, jsonRpc); jsonRpc.handleLogsFilter(capsule); @@ -170,7 +170,7 @@ public void testEmptyTxInfoList_noResult() throws JsonRpcInvalidParamsException jsonRpc.getEventFilter2ResultFull().put(FILTER_ID_1, filterAndResult); LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, Collections.emptyList(), false, false); + new LogsFilterCapsule(150L, "0xabcdef", null, Collections.emptyList(), false, false, jsonRpc); jsonRpc.handleLogsFilter(capsule); @@ -205,7 +205,7 @@ public void testParallelPath_allMatchingFilters_receiveEvents() throws Exception } LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false, jsonRpc); jsonRpc.handleLogsFilter(capsule); for (int i = 0; i < count; i++) { @@ -238,7 +238,7 @@ public void testParallelPath_expiredFiltersRemoved() throws Exception { List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false, jsonRpc); jsonRpc.handleLogsFilter(capsule); for (int i = 0; i < expiredCount; i++) { @@ -272,7 +272,7 @@ public void testParallelPath_solidifiedCapsule_routedToSolidityMap() throws Exce fullMap.put("parallel-solid-full-0", fullFilter); LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, true, false); + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, true, false, jsonRpc); jsonRpc.handleLogsFilter(capsule); for (int i = 0; i < count; i++) { From 2666f981247f1afef9641cacd02ae5a8081b5b1f Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Thu, 30 Apr 2026 13:46:30 +0800 Subject: [PATCH 21/29] fix checkstyle --- .../test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java b/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java index ecf4f86e494..f63c9df8e57 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java @@ -169,8 +169,8 @@ public void testEmptyTxInfoList_noResult() throws JsonRpcInvalidParamsException LogFilterAndResult filterAndResult = new LogFilterAndResult(fr, 100L, null); jsonRpc.getEventFilter2ResultFull().put(FILTER_ID_1, filterAndResult); - LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, Collections.emptyList(), false, false, jsonRpc); + LogsFilterCapsule capsule = new LogsFilterCapsule(150L, "0xabcdef", null, + Collections.emptyList(), false, false, jsonRpc); jsonRpc.handleLogsFilter(capsule); From 2f2e11add55b239a8a774c0b098d1d6b4d9f6b92 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Thu, 30 Apr 2026 16:47:03 +0800 Subject: [PATCH 22/29] add the reason why choose 3 threads for ForkJoinPool --- .../org/tron/core/services/jsonrpc/TronJsonRpcImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index d5820c84c91..23bcddebc7f 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -169,7 +169,12 @@ public enum RequestSource { private static final String ERROR_SELECTOR = "08c379a0"; // Function selector for Error(string) private final int filterParallelThreshold = 10000; - private final ForkJoinPool logsFilterPool = new ForkJoinPool(2); + /** + * Using the default maxLogFilterNum of 20,000, a 3-thread pool can keep up with log event + * processing for each block within the 3-second BLOCK_PRODUCED_INTERVAL. Increasing the thread + * pool size too much may affect the performance of the main block processing thread. + */ + private final ForkJoinPool logsFilterPool = new ForkJoinPool(3); /** * thread pool of query section bloom store */ From 44266d63f48474d49b1fee4f87df16c1e2a98561 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Thu, 30 Apr 2026 17:12:22 +0800 Subject: [PATCH 23/29] optimze config file --- common/src/main/resources/reference.conf | 2 +- framework/src/main/resources/config.conf | 2 +- .../org/tron/core/jsonrpc/LogMatchOverLimitTest.java | 10 +++------- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/common/src/main/resources/reference.conf b/common/src/main/resources/reference.conf index 1bf7af82408..43cbf6679f3 100644 --- a/common/src/main/resources/reference.conf +++ b/common/src/main/resources/reference.conf @@ -398,7 +398,7 @@ node { # Maximum topics within a topic criteria, >0 otherwise no limit maxSubTopics = 1000 - # Maximum number for blockFilter + # Maximum number for blockFilter. >0 otherwise no limit maxBlockFilterNum = 50000 # Maximum number of concurrent eth_newFilter registrations, >0 otherwise no limit diff --git a/framework/src/main/resources/config.conf b/framework/src/main/resources/config.conf index 79794be57ab..c2f7b72317f 100644 --- a/framework/src/main/resources/config.conf +++ b/framework/src/main/resources/config.conf @@ -373,7 +373,7 @@ node { # The maximum number of allowed topics within a topic criteria, default value is 1000, # should be > 0, otherwise means no limit. maxSubTopics = 1000 - # Allowed maximum number for blockFilter + # Allowed maximum number for blockFilter, >0 otherwise no limit maxBlockFilterNum = 50000 # Allowed maximum number for newFilter, >0 otherwise no limit maxLogFilterNum = 20000 diff --git a/framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java b/framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java index 75eda4268b0..dd6a8d0dc8c 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java @@ -1,5 +1,6 @@ package org.tron.core.jsonrpc; +import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -118,19 +119,14 @@ public void testAtExactLimit_succeeds() */ @Test public void testExceedsLimit_throwsBeforeAddAll() - throws BadItemException, ItemNotFoundException, JsonRpcInvalidParamsException { + throws ItemNotFoundException, JsonRpcInvalidParamsException { // block 1: MAX_RESULT - 1 logs, block 2: 2 logs → 9999 + 2 = 10001 > MAX_RESULT Manager manager = buildMockManager( 1L, buildTxList(MAX_RESULT - 1), 2L, buildTxList(2)); LogMatch logMatch = buildLogMatch(Arrays.asList(1L, 2L), manager); - try { - logMatch.matchBlockOneByOne(); - Assert.fail("Expected JsonRpcTooManyResultException"); - } catch (JsonRpcTooManyResultException e) { - Assert.assertTrue(e.getMessage().contains(String.valueOf(MAX_RESULT))); - } + assertThrows(JsonRpcTooManyResultException.class, logMatch::matchBlockOneByOne); } /** A block with no matching logs is skipped without incrementing the result count. */ From 481130dac404fbd5b4bacbb5b5b7dc7e54916162 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Wed, 6 May 2026 20:10:28 +0800 Subject: [PATCH 24/29] remove Lazy tronJsonRpcImpl in Manager --- .../common/es/ExecutorServiceManager.java | 14 ++++++++++++ .../capsule/BlockFilterCapsule.java | 16 +++----------- .../logsfilter/capsule/LogsFilterCapsule.java | 13 ++--------- .../main/java/org/tron/core/db/Manager.java | 12 +++++----- .../core/services/jsonrpc/JsonRpcApiUtil.java | 2 +- .../services/jsonrpc/TronJsonRpcImpl.java | 16 ++++++++------ .../capsule/BlockFilterCapsuleTest.java | 8 ++----- .../capsule/LogsFilterCapsuleTest.java | 6 +---- .../tron/common/runtime/vm/Create2Test.java | 3 ++- .../core/jsonrpc/ConcurrentHashMapTest.java | 4 ++-- .../core/jsonrpc/HandleLogsFilterTest.java | 22 +++++++++---------- .../tron/core/jsonrpc/JsonrpcServiceTest.java | 3 ++- .../tron/core/jsonrpc/WalletCursorTest.java | 15 ++++++++----- 13 files changed, 65 insertions(+), 69 deletions(-) diff --git a/common/src/main/java/org/tron/common/es/ExecutorServiceManager.java b/common/src/main/java/org/tron/common/es/ExecutorServiceManager.java index 779a8edf75d..e7cc1434afc 100644 --- a/common/src/main/java/org/tron/common/es/ExecutorServiceManager.java +++ b/common/src/main/java/org/tron/common/es/ExecutorServiceManager.java @@ -4,11 +4,14 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import lombok.extern.slf4j.Slf4j; import org.tron.common.exit.ExitManager; @@ -35,6 +38,17 @@ public static ScheduledExecutorService newSingleThreadScheduledExecutor(String n new ThreadFactoryBuilder().setNameFormat(name).setDaemon(isDaemon).build()); } + public static ForkJoinPool newForkJoinPool(String name, int parallelism) { + AtomicInteger counter = new AtomicInteger(0); + ForkJoinPool.ForkJoinWorkerThreadFactory factory = pool -> { + ForkJoinWorkerThread thread = + ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); + thread.setName(name + "-" + counter.getAndIncrement()); + return thread; + }; + return new ForkJoinPool(parallelism, factory, null, false); + } + public static ExecutorService newFixedThreadPool(String name, int fixThreads) { return newFixedThreadPool(name, fixThreads, false); } diff --git a/framework/src/main/java/org/tron/common/logsfilter/capsule/BlockFilterCapsule.java b/framework/src/main/java/org/tron/common/logsfilter/capsule/BlockFilterCapsule.java index e802b0a8f3f..e0cfb6d4433 100644 --- a/framework/src/main/java/org/tron/common/logsfilter/capsule/BlockFilterCapsule.java +++ b/framework/src/main/java/org/tron/common/logsfilter/capsule/BlockFilterCapsule.java @@ -5,7 +5,6 @@ import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.tron.core.capsule.BlockCapsule; -import org.tron.core.services.jsonrpc.TronJsonRpcImpl; @Slf4j(topic = "API") @ToString @@ -18,22 +17,13 @@ public class BlockFilterCapsule extends FilterTriggerCapsule { @Setter private boolean solidified; - private final TronJsonRpcImpl jsonRpc; - - public BlockFilterCapsule(BlockCapsule block, boolean solidified, TronJsonRpcImpl jsonRpc) { - this(block.getBlockId().toString(), solidified, jsonRpc); + public BlockFilterCapsule(BlockCapsule block, boolean solidified) { + this(block.getBlockId().toString(), solidified); } - public BlockFilterCapsule(String blockHash, boolean solidified, TronJsonRpcImpl jsonRpc) { + public BlockFilterCapsule(String blockHash, boolean solidified) { this.blockHash = blockHash; this.solidified = solidified; - this.jsonRpc = jsonRpc; - } - - @Override - public void processFilterTrigger() { - jsonRpc.handleBLockFilter(this); } } - diff --git a/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java b/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java index 0c387abd8ff..c6f35e736a3 100644 --- a/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java +++ b/framework/src/main/java/org/tron/common/logsfilter/capsule/LogsFilterCapsule.java @@ -6,7 +6,6 @@ import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.tron.common.bloom.Bloom; -import org.tron.core.services.jsonrpc.TronJsonRpcImpl; import org.tron.protos.Protocol.TransactionInfo; @Slf4j(topic = "API") @@ -32,22 +31,14 @@ public class LogsFilterCapsule extends FilterTriggerCapsule { @Setter private boolean removed; - private final TronJsonRpcImpl jsonRpc; - public LogsFilterCapsule(long blockNumber, String blockHash, Bloom bloom, - List txInfoList, boolean solidified, boolean removed, - TronJsonRpcImpl jsonRpc) { + List txInfoList, boolean solidified, boolean removed) { this.blockNumber = blockNumber; this.blockHash = blockHash; this.bloom = bloom; this.txInfoList = txInfoList; this.solidified = solidified; this.removed = removed; - this.jsonRpc = jsonRpc; } - @Override - public void processFilterTrigger() { - jsonRpc.handleLogsFilter(this); - } -} \ No newline at end of file +} diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java index d7541a90476..f19c1641e16 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -48,7 +48,6 @@ import org.apache.commons.collections4.CollectionUtils; import org.bouncycastle.util.encoders.Hex; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.tron.api.GrpcAPI; import org.tron.api.GrpcAPI.TransactionInfoList; @@ -278,7 +277,6 @@ public class Manager { @Autowired private RewardViCalService rewardViCalService; - @Lazy @Autowired private TronJsonRpcImpl tronJsonRpcImpl; @@ -338,8 +336,10 @@ public class Manager { while (isRunFilterProcessThread) { try { FilterTriggerCapsule filterCapsule = filterCapsuleQueue.poll(1, TimeUnit.SECONDS); - if (filterCapsule != null) { - filterCapsule.processFilterTrigger(); + if (filterCapsule instanceof LogsFilterCapsule) { + tronJsonRpcImpl.handleLogsFilter((LogsFilterCapsule) filterCapsule); + } else if (filterCapsule instanceof BlockFilterCapsule) { + tronJsonRpcImpl.handleBLockFilter((BlockFilterCapsule) filterCapsule); } } catch (InterruptedException e) { logger.error("FilterProcessLoop get InterruptedException, error is {}.", @@ -2271,7 +2271,7 @@ private void reOrgLogsFilter() { private void postBlockFilter(final BlockCapsule blockCapsule, boolean solidified) { BlockFilterCapsule blockFilterCapsule = - new BlockFilterCapsule(blockCapsule, solidified, tronJsonRpcImpl); + new BlockFilterCapsule(blockCapsule, solidified); if (!filterCapsuleQueue.offer(blockFilterCapsule)) { logger.info("Too many filters, block filter lost: {}.", blockCapsule.getBlockId()); } @@ -2285,7 +2285,7 @@ private void postLogsFilter(final BlockCapsule blockCapsule, boolean solidified, = getTransactionInfoByBlockNum(blockNumber).getTransactionInfoList(); LogsFilterCapsule logsFilterCapsule = new LogsFilterCapsule(blockNumber, blockCapsule.getBlockId().toString(), blockCapsule.getBloom(), transactionInfoList, - solidified, removed, tronJsonRpcImpl); + solidified, removed); if (!filterCapsuleQueue.offer(logsFilterCapsule)) { logger.info("Too many filters, logs filter lost: {}.", blockNumber); diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java b/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java index 45be8a3a14e..73e020756c3 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/JsonRpcApiUtil.java @@ -60,7 +60,7 @@ public class JsonRpcApiUtil { public static final String TAG_SAFE_SUPPORT_ERROR = "TAG safe not supported"; public static final String BLOCK_NUM_ERROR = "invalid block number"; - static SecureRandom random = new SecureRandom(); + private static final SecureRandom random = new SecureRandom(); public static byte[] convertToTronAddress(byte[] address) { byte[] newAddress = new byte[21]; diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index 23bcddebc7f..ab1b7ecb704 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -34,6 +34,7 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.bouncycastle.util.encoders.Hex; @@ -120,12 +121,12 @@ public enum RequestSource { public static final int EXPIRE_SECONDS = 5 * 60; private final int maxBlockFilterNum = Args.getInstance().getJsonRpcMaxBlockFilterNum(); private final int maxLogFilterNum = Args.getInstance().getJsonRpcMaxLogFilterNum(); - private final Cache logElementCache = + private static final Cache logElementCache = CacheBuilder.newBuilder() .maximumSize(300_000L) // 300s * tps(1000) * 1 log/tx ≈ 300_000 .expireAfterWrite(EXPIRE_SECONDS, TimeUnit.SECONDS) .recordStats().build(); //LRU cache - private final Cache blockHashCache = + private static final Cache blockHashCache = CacheBuilder.newBuilder() .maximumSize(60_000L) // 300s * 200 block/s when syncing .expireAfterWrite(EXPIRE_SECONDS, TimeUnit.SECONDS) @@ -174,22 +175,23 @@ public enum RequestSource { * processing for each block within the 3-second BLOCK_PRODUCED_INTERVAL. Increasing the thread * pool size too much may affect the performance of the main block processing thread. */ - private final ForkJoinPool logsFilterPool = new ForkJoinPool(3); + private final ForkJoinPool logsFilterPool = + ExecutorServiceManager.newForkJoinPool("logs-filter-pool", 3); /** * thread pool of query section bloom store */ private final ExecutorService sectionExecutor; private final NodeInfoService nodeInfoService; private final Wallet wallet; - private final Manager manager; + @Setter + @Autowired + private Manager manager; private final String esName = "query-section"; @Autowired - public TronJsonRpcImpl(@Autowired NodeInfoService nodeInfoService, @Autowired Wallet wallet, - @Autowired Manager manager) { + public TronJsonRpcImpl(@Autowired NodeInfoService nodeInfoService, @Autowired Wallet wallet) { this.nodeInfoService = nodeInfoService; this.wallet = wallet; - this.manager = manager; this.sectionExecutor = ExecutorServiceManager.newFixedThreadPool(esName, 5); } diff --git a/framework/src/test/java/org/tron/common/logsfilter/capsule/BlockFilterCapsuleTest.java b/framework/src/test/java/org/tron/common/logsfilter/capsule/BlockFilterCapsuleTest.java index 8aec694f481..aac42facf96 100644 --- a/framework/src/test/java/org/tron/common/logsfilter/capsule/BlockFilterCapsuleTest.java +++ b/framework/src/test/java/org/tron/common/logsfilter/capsule/BlockFilterCapsuleTest.java @@ -6,19 +6,16 @@ import org.junit.Test; import org.tron.common.utils.Sha256Hash; import org.tron.core.capsule.BlockCapsule; -import org.tron.core.services.jsonrpc.TronJsonRpcImpl; public class BlockFilterCapsuleTest { private BlockFilterCapsule blockFilterCapsule; - private TronJsonRpcImpl jsonRpc; @Before public void setUp() { - jsonRpc = new TronJsonRpcImpl(null, null, null); BlockCapsule blockCapsule = new BlockCapsule(1, Sha256Hash.ZERO_HASH, System.currentTimeMillis(), ByteString.EMPTY); - blockFilterCapsule = new BlockFilterCapsule(blockCapsule, false, jsonRpc); + blockFilterCapsule = new BlockFilterCapsule(blockCapsule, false); } @Test @@ -32,9 +29,8 @@ public void testSetAndGetBlockHash() { @Test public void testSetAndIsSolidified() { blockFilterCapsule = new BlockFilterCapsule( - "e58f33f9baf9305dc6f82b9f1934ea8f0ade2defb951258d50167028c780351f", false, jsonRpc); + "e58f33f9baf9305dc6f82b9f1934ea8f0ade2defb951258d50167028c780351f", false); blockFilterCapsule.setSolidified(true); - blockFilterCapsule.processFilterTrigger(); Assert.assertTrue(blockFilterCapsule.isSolidified()); } } diff --git a/framework/src/test/java/org/tron/common/logsfilter/capsule/LogsFilterCapsuleTest.java b/framework/src/test/java/org/tron/common/logsfilter/capsule/LogsFilterCapsuleTest.java index ff1f0d04a34..f23c446c23d 100644 --- a/framework/src/test/java/org/tron/common/logsfilter/capsule/LogsFilterCapsuleTest.java +++ b/framework/src/test/java/org/tron/common/logsfilter/capsule/LogsFilterCapsuleTest.java @@ -6,19 +6,16 @@ import org.junit.Before; import org.junit.Test; import org.tron.common.bloom.Bloom; -import org.tron.core.services.jsonrpc.TronJsonRpcImpl; public class LogsFilterCapsuleTest { private LogsFilterCapsule capsule; - private TronJsonRpcImpl jsonRpc; @Before public void setUp() { - jsonRpc = new TronJsonRpcImpl(null, null, null); capsule = new LogsFilterCapsule(0, "e58f33f9baf9305dc6f82b9f1934ea8f0ade2defb951258d50167028c780351f", - new Bloom(), new ArrayList<>(), true, false, jsonRpc); + new Bloom(), new ArrayList<>(), true, false); } @Test @@ -30,7 +27,6 @@ public void testSetAndGetLogsFilterCapsule() { capsule.setRemoved(capsule.isRemoved()); capsule.setTxInfoList(capsule.getTxInfoList()); assertNotNull(capsule.toString()); - capsule.processFilterTrigger(); } } diff --git a/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java b/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java index 6fa2801c51f..5a58407f887 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java @@ -209,7 +209,8 @@ private void testJsonRpc(byte[] actualContract, long loop) { NodeInfoService nodeInfoService; nodeInfoService = context.getBean(NodeInfoService.class); Wallet wallet = context.getBean(Wallet.class); - tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, manager); + tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet); + tronJsonRpc.setManager(manager); try { String res = tronJsonRpc.getStorageAt(ByteArray.toHexString(actualContract), "0", "latest"); diff --git a/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java b/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java index f9c431b61fd..fe0c31a5397 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java @@ -23,7 +23,7 @@ @Slf4j public class ConcurrentHashMapTest { private static final String EXECUTOR_NAME = "jsonrpc-concurrent-map-test"; - private final TronJsonRpcImpl jsonRpc = new TronJsonRpcImpl(null, null, null); + private final TronJsonRpcImpl jsonRpc = new TronJsonRpcImpl(null, null); private static int randomInt(int minInt, int maxInt) { return (int) round(random(true) * (maxInt - minInt) + minInt, true); @@ -71,7 +71,7 @@ public void testHandleBlockHash() { for (int j = 1 + (i - 1) * eachCount; j <= i * eachCount; j++) { BlockFilterCapsule blockFilterCapsule = - new BlockFilterCapsule(String.valueOf(j), false, jsonRpc); + new BlockFilterCapsule(String.valueOf(j), false); jsonRpc.handleBLockFilter(blockFilterCapsule); } try { diff --git a/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java b/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java index f63c9df8e57..51b7d02d9d8 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java @@ -27,7 +27,7 @@ public class HandleLogsFilterTest { @Before public void setUp() { - jsonRpc = new TronJsonRpcImpl(null, null, null); + jsonRpc = new TronJsonRpcImpl(null, null); } @After @@ -51,7 +51,7 @@ public void testMatchingFilter_receivesLogElements() throws JsonRpcInvalidParams List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false, jsonRpc); + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); jsonRpc.handleLogsFilter(capsule); @@ -69,7 +69,7 @@ public void testBlockNumberBelowRange_noResult() throws JsonRpcInvalidParamsExce List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = - new LogsFilterCapsule(50L, "0xabcdef", null, txInfoList, false, false, jsonRpc); + new LogsFilterCapsule(50L, "0xabcdef", null, txInfoList, false, false); jsonRpc.handleLogsFilter(capsule); @@ -93,7 +93,7 @@ public void testExpiredFilter_removedFromMap() throws Exception { List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false, jsonRpc); + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); jsonRpc.handleLogsFilter(capsule); @@ -113,7 +113,7 @@ public void testSolidifiedCapsule_routedToSolidityMap() throws JsonRpcInvalidPar List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, true, false, jsonRpc); + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, true, false); jsonRpc.handleLogsFilter(capsule); @@ -134,7 +134,7 @@ public void testNonSolidifiedCapsule_routedToFullMap() throws JsonRpcInvalidPara List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false, jsonRpc); + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); jsonRpc.handleLogsFilter(capsule); @@ -154,7 +154,7 @@ public void testMultipleMatchingFilters_bothReceiveEvents() throws JsonRpcInvali List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false, jsonRpc); + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); jsonRpc.handleLogsFilter(capsule); @@ -170,7 +170,7 @@ public void testEmptyTxInfoList_noResult() throws JsonRpcInvalidParamsException jsonRpc.getEventFilter2ResultFull().put(FILTER_ID_1, filterAndResult); LogsFilterCapsule capsule = new LogsFilterCapsule(150L, "0xabcdef", null, - Collections.emptyList(), false, false, jsonRpc); + Collections.emptyList(), false, false); jsonRpc.handleLogsFilter(capsule); @@ -205,7 +205,7 @@ public void testParallelPath_allMatchingFilters_receiveEvents() throws Exception } LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false, jsonRpc); + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); jsonRpc.handleLogsFilter(capsule); for (int i = 0; i < count; i++) { @@ -238,7 +238,7 @@ public void testParallelPath_expiredFiltersRemoved() throws Exception { List txInfoList = Collections.singletonList(buildTxInfoWithLog(new byte[20])); LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false, jsonRpc); + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, false, false); jsonRpc.handleLogsFilter(capsule); for (int i = 0; i < expiredCount; i++) { @@ -272,7 +272,7 @@ public void testParallelPath_solidifiedCapsule_routedToSolidityMap() throws Exce fullMap.put("parallel-solid-full-0", fullFilter); LogsFilterCapsule capsule = - new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, true, false, jsonRpc); + new LogsFilterCapsule(150L, "0xabcdef", null, txInfoList, true, false); jsonRpc.handleLogsFilter(capsule); for (int i = 0; i < count; i++) { diff --git a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java index 7de494f1783..0122a98a81c 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java @@ -208,7 +208,8 @@ public void init() { dbManager.getTransactionRetStore() .put(ByteArray.fromLong(blockCapsule2.getNum()), transactionRetCapsule2); - tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, dbManager); + tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet); + tronJsonRpc.setManager(dbManager); } @Test diff --git a/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java b/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java index fdddcefa157..a20c0726f2d 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java @@ -59,7 +59,8 @@ public void init() { @Test public void testSource() { - TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, dbManager); + TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet); + tronJsonRpc.setManager(dbManager); Assert.assertEquals(Cursor.HEAD, wallet.getCursor()); Assert.assertEquals(RequestSource.FULLNODE, tronJsonRpc.getSource()); @@ -90,7 +91,8 @@ public void testDisableInSolidity() { dbManager.setCursor(Cursor.SOLIDITY); - TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, dbManager); + TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet); + tronJsonRpc.setManager(dbManager); try { tronJsonRpc.buildTransaction(buildArguments); tronJsonRpc.close(); @@ -112,7 +114,8 @@ public void testDisableInPBFT() { dbManager.setCursor(Cursor.PBFT); - TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, dbManager); + TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet); + tronJsonRpc.setManager(dbManager); try { tronJsonRpc.buildTransaction(buildArguments); } catch (Exception e) { @@ -139,7 +142,8 @@ public void testEnableInFullNode() { buildArguments.setTo("0x548794500882809695a8a687866e76d4271a1abc"); buildArguments.setValue("0x1f4"); - TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, dbManager); + TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet); + tronJsonRpc.setManager(dbManager); try { tronJsonRpc.buildTransaction(buildArguments); @@ -157,7 +161,8 @@ public void testEnableInFullNode() { public void testNewFilter_exceedsCapThrowsException() throws Exception { int cap = Args.getInstance().getJsonRpcMaxLogFilterNum(); FilterRequest fr = new FilterRequest(); - TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet, dbManager); + TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet); + tronJsonRpc.setManager(dbManager); Map map = tronJsonRpc.getEventFilter2ResultFull(); List addedKeys = new ArrayList<>(); From 7c4b94135750e4c587c4ded51284e4ab5aa82de9 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Wed, 6 May 2026 20:48:41 +0800 Subject: [PATCH 25/29] optimize some testcases --- .../services/jsonrpc/TronJsonRpcImpl.java | 3 +- .../core/jsonrpc/HandleLogsFilterTest.java | 38 +++++++++++-------- .../tron/core/jsonrpc/WalletCursorTest.java | 3 +- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index ab1b7ecb704..e869fe1653f 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -169,7 +169,8 @@ public enum RequestSource { private static final String NO_BLOCK_HEADER_BY_HASH = "header for hash not found"; private static final String ERROR_SELECTOR = "08c379a0"; // Function selector for Error(string) - private final int filterParallelThreshold = 10000; + @Setter + private int filterParallelThreshold = 10000; /** * Using the default maxLogFilterNum of 20,000, a 3-thread pool can keep up with log event * processing for each block within the 3-second BLOCK_PRODUCED_INTERVAL. Increasing the thread diff --git a/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java b/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java index 51b7d02d9d8..33835c482fe 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/HandleLogsFilterTest.java @@ -41,7 +41,9 @@ private TransactionInfo buildTxInfoWithLog(byte[] address) { return TransactionInfo.newBuilder().addLog(LogInfo.buildLog(logInfo)).build(); } - /** Events dispatched to a matching filter in the serial (<=10000 entries) path. */ + /** + * Events dispatched to a matching filter in the serial (<=10000 entries) path. + */ @Test public void testMatchingFilter_receivesLogElements() throws JsonRpcInvalidParamsException { FilterRequest fr = new FilterRequest(); @@ -58,7 +60,9 @@ public void testMatchingFilter_receivesLogElements() throws JsonRpcInvalidParams Assert.assertEquals(1, filterAndResult.getResult().size()); } - /** Filter with fromBlock=100 does not receive a capsule whose blockNumber is 50. */ + /** + * Filter with fromBlock=100 does not receive a capsule whose blockNumber is 50. + */ @Test public void testBlockNumberBelowRange_noResult() throws JsonRpcInvalidParamsException { FilterRequest fr = new FilterRequest(); @@ -76,7 +80,9 @@ public void testBlockNumberBelowRange_noResult() throws JsonRpcInvalidParamsExce Assert.assertTrue(filterAndResult.getResult().isEmpty()); } - /** An expired filter is removed from the map during handleLogsFilter. */ + /** + * An expired filter is removed from the map during handleLogsFilter. + */ @Test public void testExpiredFilter_removedFromMap() throws Exception { FilterRequest fr = new FilterRequest(); @@ -100,7 +106,9 @@ public void testExpiredFilter_removedFromMap() throws Exception { Assert.assertFalse("expired filter should be removed", map.containsKey(FILTER_ID_1)); } - /** A solidified capsule is routed only to the solidity map; the full-node map is untouched. */ + /** + * A solidified capsule is routed only to the solidity map; the full-node map is untouched. + */ @Test public void testSolidifiedCapsule_routedToSolidityMap() throws JsonRpcInvalidParamsException { FilterRequest fr = new FilterRequest(); @@ -121,7 +129,9 @@ public void testSolidifiedCapsule_routedToSolidityMap() throws JsonRpcInvalidPar Assert.assertTrue("full-node filter must not be touched", fullFilter.getResult().isEmpty()); } - /** A non-solidified capsule is routed only to the full-node map. */ + /** + * A non-solidified capsule is routed only to the full-node map. + */ @Test public void testNonSolidifiedCapsule_routedToFullMap() throws JsonRpcInvalidParamsException { FilterRequest fr = new FilterRequest(); @@ -142,7 +152,9 @@ public void testNonSolidifiedCapsule_routedToFullMap() throws JsonRpcInvalidPara Assert.assertTrue("solidity filter must not be touched", solidityFilter.getResult().isEmpty()); } - /** Both filters in the map receive events when both match. */ + /** + * Both filters in the map receive events when both match. + */ @Test public void testMultipleMatchingFilters_bothReceiveEvents() throws JsonRpcInvalidParamsException { FilterRequest fr = new FilterRequest(); @@ -162,7 +174,9 @@ public void testMultipleMatchingFilters_bothReceiveEvents() throws JsonRpcInvali Assert.assertEquals(1, filter2.getResult().size()); } - /** An empty txInfoList produces no results. */ + /** + * An empty txInfoList produces no results. + */ @Test public void testEmptyTxInfoList_noResult() throws JsonRpcInvalidParamsException { FilterRequest fr = new FilterRequest(); @@ -177,14 +191,8 @@ public void testEmptyTxInfoList_noResult() throws JsonRpcInvalidParamsException Assert.assertTrue(filterAndResult.getResult().isEmpty()); } - // ------------------------------------------------------------------------- - // Parallel path tests (filterParallelThreshold lowered to 2 via reflection) - // ------------------------------------------------------------------------- - - private void setParallelThreshold(int value) throws Exception { - Field f = TronJsonRpcImpl.class.getDeclaredField("filterParallelThreshold"); - f.setAccessible(true); - f.setInt(jsonRpc, value); + private void setParallelThreshold(int value) { + jsonRpc.setFilterParallelThreshold(value); } /** diff --git a/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java b/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java index a20c0726f2d..f1b636e9014 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java @@ -159,7 +159,8 @@ public void testEnableInFullNode() { */ @Test public void testNewFilter_exceedsCapThrowsException() throws Exception { - int cap = Args.getInstance().getJsonRpcMaxLogFilterNum(); + int cap = 5; + Args.getInstance().setJsonRpcMaxLogFilterNum(cap); FilterRequest fr = new FilterRequest(); TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet); tronJsonRpc.setManager(dbManager); From a209e451f55abaf8306c2fb4af916d1476ac7463 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Thu, 7 May 2026 11:11:10 +0800 Subject: [PATCH 26/29] remove modifier static of getFilterResult --- .../org/tron/core/services/jsonrpc/TronJsonRpcImpl.java | 2 +- .../java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index e869fe1653f..bb6b73a8650 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -1565,7 +1565,7 @@ private LogFilterElement[] getLogsByLogFilterWrapper(LogFilterWrapper logFilterW return logMatch.matchBlockOneByOne(); } - public static Object[] getFilterResult(String filterId, Map + public Object[] getFilterResult(String filterId, Map blockFilter2Result, Map eventFilter2Result) throws ItemNotFoundException { Object[] result; diff --git a/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java b/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java index fe0c31a5397..2fcb624002e 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java @@ -97,7 +97,7 @@ public void testHandleBlockHash() { for (int k = 0; k < 5; k++) { try { - Object[] blockHashList = TronJsonRpcImpl.getFilterResult(String.valueOf(k), conMap, + Object[] blockHashList = jsonRpc.getFilterResult(String.valueOf(k), conMap, jsonRpc.getEventFilter2ResultFull()); for (Object str : blockHashList) { @@ -125,7 +125,7 @@ public void testHandleBlockHash() { for (int k = 0; k < 5; k++) { try { - Object[] blockHashList = TronJsonRpcImpl.getFilterResult(String.valueOf(k), conMap, + Object[] blockHashList = jsonRpc.getFilterResult(String.valueOf(k), conMap, jsonRpc.getEventFilter2ResultFull()); // if (blockHashList.length == 0) { @@ -157,7 +157,7 @@ public void testHandleBlockHash() { for (int k = 0; k < 5; k++) { try { - Object[] blockHashList = TronJsonRpcImpl.getFilterResult(String.valueOf(k), conMap, + Object[] blockHashList = jsonRpc.getFilterResult(String.valueOf(k), conMap, jsonRpc.getEventFilter2ResultFull()); for (Object str : blockHashList) { From 4bf108aba2b42f8c6348d630ef75f0ff75c87f90 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Thu, 7 May 2026 12:41:34 +0800 Subject: [PATCH 27/29] add @Lazy for tronJsonRpcImpl in Manager --- framework/src/main/java/org/tron/core/db/Manager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java index f19c1641e16..e6204c16aec 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -48,6 +48,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.bouncycastle.util.encoders.Hex; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.tron.api.GrpcAPI; import org.tron.api.GrpcAPI.TransactionInfoList; @@ -277,6 +278,7 @@ public class Manager { @Autowired private RewardViCalService rewardViCalService; + @Lazy @Autowired private TronJsonRpcImpl tronJsonRpcImpl; From 5ba90534e66ea27d1d2a356d7732a3fae41790e5 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Thu, 7 May 2026 18:59:21 +0800 Subject: [PATCH 28/29] optimize FilterTriggerCapsule --- .../tron/common/es/ExecutorServiceManager.java | 5 +++++ .../logsfilter/capsule/FilterTriggerCapsule.java | 5 +---- .../core/services/jsonrpc/TronJsonRpcImpl.java | 15 ++++++++++++--- .../tron/common/logsfilter/FilterQueryTest.java | 8 -------- .../tron/core/jsonrpc/LogMatchOverLimitTest.java | 2 +- .../org/tron/core/jsonrpc/WalletCursorTest.java | 5 ++++- 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/common/src/main/java/org/tron/common/es/ExecutorServiceManager.java b/common/src/main/java/org/tron/common/es/ExecutorServiceManager.java index e7cc1434afc..fb845ec211c 100644 --- a/common/src/main/java/org/tron/common/es/ExecutorServiceManager.java +++ b/common/src/main/java/org/tron/common/es/ExecutorServiceManager.java @@ -39,11 +39,16 @@ public static ScheduledExecutorService newSingleThreadScheduledExecutor(String n } public static ForkJoinPool newForkJoinPool(String name, int parallelism) { + return newForkJoinPool(name, parallelism, false); + } + + public static ForkJoinPool newForkJoinPool(String name, int parallelism, boolean isDaemon) { AtomicInteger counter = new AtomicInteger(0); ForkJoinPool.ForkJoinWorkerThreadFactory factory = pool -> { ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); thread.setName(name + "-" + counter.getAndIncrement()); + thread.setDaemon(isDaemon); return thread; }; return new ForkJoinPool(parallelism, factory, null, false); diff --git a/framework/src/main/java/org/tron/common/logsfilter/capsule/FilterTriggerCapsule.java b/framework/src/main/java/org/tron/common/logsfilter/capsule/FilterTriggerCapsule.java index 0280f0c96a7..5d495a5c98c 100644 --- a/framework/src/main/java/org/tron/common/logsfilter/capsule/FilterTriggerCapsule.java +++ b/framework/src/main/java/org/tron/common/logsfilter/capsule/FilterTriggerCapsule.java @@ -1,8 +1,5 @@ package org.tron.common.logsfilter.capsule; -public class FilterTriggerCapsule extends TriggerCapsule { +public class FilterTriggerCapsule { - public void processFilterTrigger() { - throw new UnsupportedOperationException(); - } } \ No newline at end of file diff --git a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java index bb6b73a8650..b3bfd8f96f3 100644 --- a/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java +++ b/framework/src/main/java/org/tron/core/services/jsonrpc/TronJsonRpcImpl.java @@ -14,6 +14,7 @@ import static org.tron.core.services.jsonrpc.JsonRpcApiUtil.triggerCallContract; import com.alibaba.fastjson.JSON; +import com.google.common.annotations.VisibleForTesting; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.protobuf.ByteString; @@ -34,7 +35,6 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import lombok.Getter; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.bouncycastle.util.encoders.Hex; @@ -169,7 +169,6 @@ public enum RequestSource { private static final String NO_BLOCK_HEADER_BY_HASH = "header for hash not found"; private static final String ERROR_SELECTOR = "08c379a0"; // Function selector for Error(string) - @Setter private int filterParallelThreshold = 10000; /** * Using the default maxLogFilterNum of 20,000, a 3-thread pool can keep up with log event @@ -184,7 +183,6 @@ public enum RequestSource { private final ExecutorService sectionExecutor; private final NodeInfoService nodeInfoService; private final Wallet wallet; - @Setter @Autowired private Manager manager; private final String esName = "query-section"; @@ -196,6 +194,16 @@ public TronJsonRpcImpl(@Autowired NodeInfoService nodeInfoService, @Autowired Wa this.sectionExecutor = ExecutorServiceManager.newFixedThreadPool(esName, 5); } + @VisibleForTesting + public void setManager(Manager manager) { + this.manager = manager; + } + + @VisibleForTesting + public void setFilterParallelThreshold(int filterParallelThreshold) { + this.filterParallelThreshold = filterParallelThreshold; + } + public void handleBLockFilter(BlockFilterCapsule blockFilterCapsule) { Iterator> it; @@ -1434,6 +1442,7 @@ public String newFilter(FilterRequest fr) throws JsonRpcInvalidParamsException, } else { eventFilter2Result = eventFilter2ResultSolidity; } + // Due to concurrent access, the threshold may occasionally be exceeded. if (maxLogFilterNum > 0 && eventFilter2Result.size() >= maxLogFilterNum) { throw new JsonRpcExceedLimitException( "exceed max log filters: " + maxLogFilterNum + ", try again later"); diff --git a/framework/src/test/java/org/tron/common/logsfilter/FilterQueryTest.java b/framework/src/test/java/org/tron/common/logsfilter/FilterQueryTest.java index c87d8e1136e..b57b3a92fcd 100644 --- a/framework/src/test/java/org/tron/common/logsfilter/FilterQueryTest.java +++ b/framework/src/test/java/org/tron/common/logsfilter/FilterQueryTest.java @@ -21,7 +21,6 @@ import org.junit.Assert; import org.junit.Test; import org.tron.common.logsfilter.capsule.ContractEventTriggerCapsule; -import org.tron.common.logsfilter.capsule.FilterTriggerCapsule; import org.tron.common.logsfilter.capsule.TriggerCapsule; import org.tron.common.runtime.LogEventWrapper; import org.tron.protos.contract.SmartContractOuterClass.SmartContract.ABI.Entry; @@ -103,13 +102,6 @@ public synchronized void testMatchFilter() { assertNotNull(filterQuery.toString()); } - FilterTriggerCapsule filterTriggerCapsule = new FilterTriggerCapsule(); - try { - filterTriggerCapsule.processFilterTrigger(); - } catch (Exception e) { - logger.info(e.getMessage()); - } - TriggerCapsule triggerCapsule = new TriggerCapsule(); try { triggerCapsule.processTrigger(); diff --git a/framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java b/framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java index dd6a8d0dc8c..77f869fd5a8 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/LogMatchOverLimitTest.java @@ -118,7 +118,7 @@ public void testAtExactLimit_succeeds() * {@link JsonRpcTooManyResultException} is thrown BEFORE {@code addAll}. */ @Test - public void testExceedsLimit_throwsBeforeAddAll() + public void testExceedsLimit_throws() throws ItemNotFoundException, JsonRpcInvalidParamsException { // block 1: MAX_RESULT - 1 logs, block 2: 2 logs → 9999 + 2 = 10001 > MAX_RESULT Manager manager = buildMockManager( diff --git a/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java b/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java index f1b636e9014..24ca71a74bc 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java @@ -27,6 +27,7 @@ @Slf4j public class WalletCursorTest extends BaseTest { + private static final String OWNER_ADDRESS; private static final String OWNER_ADDRESS_ACCOUNT_NAME = "first"; @Resource @@ -36,7 +37,7 @@ public class WalletCursorTest extends BaseTest { private static boolean init; static { - Args.setParam(new String[]{"--output-directory", dbPath()}, TestConstants.TEST_CONF); + Args.setParam(new String[] {"--output-directory", dbPath()}, TestConstants.TEST_CONF); OWNER_ADDRESS = Wallet.getAddressPreFixString() + "abd4b9367799eaa3197fecb144eb71de1e049abc"; @@ -160,6 +161,7 @@ public void testEnableInFullNode() { @Test public void testNewFilter_exceedsCapThrowsException() throws Exception { int cap = 5; + int saved = Args.getInstance().getJsonRpcMaxLogFilterNum(); Args.getInstance().setJsonRpcMaxLogFilterNum(cap); FilterRequest fr = new FilterRequest(); TronJsonRpcImpl tronJsonRpc = new TronJsonRpcImpl(nodeInfoService, wallet); @@ -183,6 +185,7 @@ public void testNewFilter_exceedsCapThrowsException() throws Exception { } } finally { tronJsonRpc.close(); + Args.getInstance().setJsonRpcMaxLogFilterNum(saved); } } From 0cce0537080bd763a223e18aa5a4ff368042ca87 Mon Sep 17 00:00:00 2001 From: jiangyuanshu <317787106@qq.com> Date: Fri, 8 May 2026 22:00:51 +0800 Subject: [PATCH 29/29] fix the bug of JsonRpcCallAndEstimateGasTest --- .../core/jsonrpc/JsonRpcCallAndEstimateGasTest.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/framework/src/test/java/org/tron/core/jsonrpc/JsonRpcCallAndEstimateGasTest.java b/framework/src/test/java/org/tron/core/jsonrpc/JsonRpcCallAndEstimateGasTest.java index 65defdab2ed..2ab455fa580 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/JsonRpcCallAndEstimateGasTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/JsonRpcCallAndEstimateGasTest.java @@ -211,7 +211,9 @@ private static TronJsonRpcImpl newRpcWithMockedFailedCall(byte[] resData, Estima }); } - return new TronJsonRpcImpl(mockNodeInfo, mockWallet, mockManager); + TronJsonRpcImpl rpc = new TronJsonRpcImpl(mockNodeInfo, mockWallet); + rpc.setManager(mockManager); + return rpc; } private static TronJsonRpcImpl newRpcWithMockedSuccessfulCall(byte[]... constantResults) @@ -237,7 +239,9 @@ private static TronJsonRpcImpl newRpcWithMockedSuccessfulCall(byte[]... constant .build(); }); - return new TronJsonRpcImpl(mockNodeInfo, mockWallet, mockManager); + TronJsonRpcImpl rpc = new TronJsonRpcImpl(mockNodeInfo, mockWallet); + rpc.setManager(mockManager); + return rpc; } private static TronJsonRpcImpl newRpcWithMockedEstimateGasSuccessfulCall(long energyValue, @@ -272,6 +276,8 @@ private static TronJsonRpcImpl newRpcWithMockedEstimateGasSuccessfulCall(long en }); } - return new TronJsonRpcImpl(mockNodeInfo, mockWallet, mockManager); + TronJsonRpcImpl rpc = new TronJsonRpcImpl(mockNodeInfo, mockWallet); + rpc.setManager(mockManager); + return rpc; } }