From a1a5b20983aeab3648904da56e3d849ca43310cb Mon Sep 17 00:00:00 2001 From: Alyosha Karamazov Date: Thu, 25 Jan 2024 00:58:25 +0000 Subject: [PATCH] Enable limit on range Of JSON-RPC API trace_filter method (#5971) (#6446) * Enable limit on range of JSON-RPC API trace_filter method (#5971) Enable a limit on the range of blocks that can be supplied to the JSON-RPC trace_filter method. The limit has a default value and can be overridden with a command line option at start up. Signed-off-by: alyokaz Signed-off-by: Sally MacFarlane --------- Signed-off-by: alyokaz Signed-off-by: Sally MacFarlane Co-authored-by: Sally MacFarlane --- CHANGELOG.md | 1 + .../org/hyperledger/besu/cli/BesuCommand.java | 9 ++- .../src/test/resources/everything_config.toml | 3 +- .../besu/ethereum/api/ApiConfiguration.java | 5 ++ .../jsonrpc/internal/methods/TraceFilter.java | 18 ++++- .../jsonrpc/methods/TraceJsonRpcMethods.java | 6 +- .../internal/methods/TraceFilterTest.java | 79 +++++++++++++++++++ 7 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceFilterTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 9730af0fd5d..f966114ed92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 24.1.2-SNAPSHOT ### Breaking Changes +- The `trace-filter` method in JSON-RPC API now has a default block range limit of 1000, adjustable with `--rpc-max-trace-filter-range` [#6446](https://github.com/hyperledger/besu/pull/6446) ### Deprecations diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index aeb518a2f6e..9ee0e313166 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -1267,6 +1267,12 @@ static class PermissionsOptionGroup { description = "Specifies the number of last blocks to cache (default: ${DEFAULT-VALUE})") private final Integer numberOfblocksToCache = 0; + @Option( + names = {"--rpc-max-trace-filter-range"}, + description = + "Specifies the maximum number of blocks for the trace_filter method. Must be >=0. 0 specifies no limit (default: $DEFAULT-VALUE)") + private final Long maxTraceFilterRange = 1000L; + @Mixin private P2PTLSConfigOptions p2pTLSConfigOptions; @Mixin private PkiBlockCreationOptions pkiBlockCreationOptions; @@ -2534,7 +2540,8 @@ private ApiConfiguration apiConfiguration() { .gasPriceMax(apiGasPriceMax) .maxLogsRange(rpcMaxLogsRange) .gasCap(rpcGasCap) - .isGasAndPriorityFeeLimitingEnabled(apiGasAndPriorityFeeLimitingEnabled); + .isGasAndPriorityFeeLimitingEnabled(apiGasAndPriorityFeeLimitingEnabled) + .maxTraceFilterRange(maxTraceFilterRange); if (apiGasAndPriorityFeeLimitingEnabled) { if (apiGasAndPriorityFeeLowerBoundCoefficient > apiGasAndPriorityFeeUpperBoundCoefficient) { throw new ParameterException( diff --git a/besu/src/test/resources/everything_config.toml b/besu/src/test/resources/everything_config.toml index b205fa6692e..3c6792b7b95 100644 --- a/besu/src/test/resources/everything_config.toml +++ b/besu/src/test/resources/everything_config.toml @@ -90,6 +90,7 @@ rpc-max-logs-range=100 json-pretty-print-enabled=false cache-last-blocks=512 rpc-gas-cap = 50000000 +rpc-max-trace-filter-range=100 # PRIVACY TLS privacy-tls-enabled=false @@ -232,4 +233,4 @@ Xp2p-tls-crl-file="none.file" Xp2p-tls-clienthello-sni=false #contracts -Xevm-jumpdest-cache-weight-kb=32000 \ No newline at end of file +Xevm-jumpdest-cache-weight-kb=32000 diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/ApiConfiguration.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/ApiConfiguration.java index e9f30fca336..42a7f8f62e0 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/ApiConfiguration.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/ApiConfiguration.java @@ -77,4 +77,9 @@ public Long getLowerBoundGasAndPriorityFeeCoefficient() { public Long getUpperBoundGasAndPriorityFeeCoefficient() { return DEFAULT_UPPER_BOUND_GAS_AND_PRIORITY_FEE_COEFFICIENT; } + + @Value.Default + public Long getMaxTraceFilterRange() { + return 1000L; + } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceFilter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceFilter.java index 8c94f785065..ff65feb8469 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceFilter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceFilter.java @@ -24,8 +24,10 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionTrace; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTrace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.RewardTraceGenerator; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; @@ -66,12 +68,15 @@ public class TraceFilter extends TraceBlock { private static final Logger LOG = LoggerFactory.getLogger(TraceFilter.class); + private final Long maxRange; public TraceFilter( final Supplier blockTracerSupplier, final ProtocolSchedule protocolSchedule, - final BlockchainQueries blockchainQueries) { + final BlockchainQueries blockchainQueries, + final Long maxRange) { super(protocolSchedule, blockchainQueries); + this.maxRange = maxRange; } @Override @@ -88,6 +93,17 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { final long toBlock = resolveBlockNumber(filterParameter.getToBlock()); LOG.trace("Received RPC rpcName={} fromBlock={} toBlock={}", getName(), fromBlock, toBlock); + if (maxRange > 0 && toBlock - fromBlock > maxRange) { + LOG.atDebug() + .setMessage("trace_filter request {} failed:") + .addArgument(requestContext.getRequest()) + .setCause( + new IllegalArgumentException(RpcErrorType.EXCEEDS_RPC_MAX_BLOCK_RANGE.getMessage())) + .log(); + return new JsonRpcErrorResponse( + requestContext.getRequest().getId(), RpcErrorType.EXCEEDS_RPC_MAX_BLOCK_RANGE); + } + final ObjectMapper mapper = new ObjectMapper(); final ArrayNodeWrapper resultArrayNode = new ArrayNodeWrapper( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/TraceJsonRpcMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/TraceJsonRpcMethods.java index 96faae9fd29..2333409b089 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/TraceJsonRpcMethods.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/TraceJsonRpcMethods.java @@ -60,7 +60,11 @@ protected Map create() { new BlockReplay(protocolSchedule, blockchainQueries.getBlockchain()); return mapOf( new TraceReplayBlockTransactions(protocolSchedule, blockchainQueries), - new TraceFilter(() -> new BlockTracer(blockReplay), protocolSchedule, blockchainQueries), + new TraceFilter( + () -> new BlockTracer(blockReplay), + protocolSchedule, + blockchainQueries, + apiConfiguration.getMaxTraceFilterRange()), new TraceGet(() -> new BlockTracer(blockReplay), blockchainQueries, protocolSchedule), new TraceTransaction( () -> new BlockTracer(blockReplay), protocolSchedule, blockchainQueries), diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceFilterTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceFilterTest.java new file mode 100644 index 00000000000..99db8851141 --- /dev/null +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceFilterTest.java @@ -0,0 +1,79 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameter; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.FilterParameter; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTracer; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; +import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; + +import java.util.function.Supplier; + +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class TraceFilterTest { + + private TraceFilter method; + + @Mock Supplier blockTracerSupplier; + @Mock ProtocolSchedule protocolSchedule; + @Mock BlockchainQueries blockchainQueries; + + @ParameterizedTest + @CsvSource({ + "0, 1001, 1000", "0, 5000, 1000", "1, 1002, 1000", "1, 6002, 1000", "1000, 3000, 1000", + "0, 501, 500", "0, 5000, 500", "1, 502, 500", "1, 6002, 500", "1000, 3000, 500" + }) + public void shouldFailIfParamsExceedMaxRange( + final long fromBlock, final long toBlock, final long maxFilterRange) { + final FilterParameter filterParameter = + new FilterParameter( + new BlockParameter(fromBlock), + new BlockParameter(toBlock), + null, + null, + null, + null, + null, + null, + null); + + JsonRpcRequestContext request = + new JsonRpcRequestContext( + new JsonRpcRequest("2.0", "trace_filter", new Object[] {filterParameter})); + + method = + new TraceFilter(blockTracerSupplier, protocolSchedule, blockchainQueries, maxFilterRange); + + final JsonRpcResponse response = method.response(request); + assertThat(response).isInstanceOf(JsonRpcErrorResponse.class); + + final JsonRpcErrorResponse errorResponse = (JsonRpcErrorResponse) response; + assertThat(errorResponse.getErrorType()).isEqualTo(RpcErrorType.EXCEEDS_RPC_MAX_BLOCK_RANGE); + } +}