From b7f20876db951eaf840e4d3ea3c4e54b75147abf Mon Sep 17 00:00:00 2001 From: garyschulte Date: Fri, 19 May 2023 12:52:27 -0700 Subject: [PATCH 1/6] remove bonsai accumulator trielogfactory, rely on TrieLogManager instead (#5479) Signed-off-by: garyschulte --- .../bonsai/trielog/AbstractTrieLogManager.java | 2 +- .../worldview/BonsaiWorldStateUpdateAccumulator.java | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/trielog/AbstractTrieLogManager.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/trielog/AbstractTrieLogManager.java index a20e7b200ff..90aff386e83 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/trielog/AbstractTrieLogManager.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/trielog/AbstractTrieLogManager.java @@ -102,7 +102,7 @@ TrieLog prepareTrieLog( .setMessage("Adding layered world state for {}") .addArgument(blockHeader::toLogString) .log(); - final TrieLog trieLog = localUpdater.generateTrieLog(blockHeader); + final TrieLog trieLog = trieLogFactory.create(localUpdater, blockHeader); trieLog.freeze(); return trieLog; } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java index a70e3dfacb8..24023c8a7e4 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/worldview/BonsaiWorldStateUpdateAccumulator.java @@ -24,8 +24,6 @@ import org.hyperledger.besu.ethereum.bonsai.BonsaiAccount; import org.hyperledger.besu.ethereum.bonsai.BonsaiValue; import org.hyperledger.besu.ethereum.bonsai.storage.BonsaiWorldStateKeyValueStorage; -import org.hyperledger.besu.ethereum.bonsai.trielog.TrieLogFactoryImpl; -import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.trie.MerkleTrieException; import org.hyperledger.besu.evm.account.Account; @@ -35,7 +33,6 @@ import org.hyperledger.besu.evm.worldstate.WrappedEvmAccount; import org.hyperledger.besu.plugin.services.trielogs.TrieLog; import org.hyperledger.besu.plugin.services.trielogs.TrieLogAccumulator; -import org.hyperledger.besu.plugin.services.trielogs.TrieLogFactory; import java.util.Collection; import java.util.Collections; @@ -75,9 +72,6 @@ public class BonsaiWorldStateUpdateAccumulator private final Map>> storageToUpdate = new ConcurrentHashMap<>(); - // todo plumb me from plugin service: - TrieLogFactory trieLogFactory = new TrieLogFactoryImpl(); - private boolean isAccumulatorStateChanged; public BonsaiWorldStateUpdateAccumulator( @@ -500,10 +494,6 @@ public BonsaiWorldStateKeyValueStorage getWorldStateStorage() { return wrappedWorldView().getWorldStateStorage(); } - public TrieLog generateTrieLog(final BlockHeader blockHeader) { - return trieLogFactory.create(this, blockHeader); - } - public void rollForward(final TrieLog layer) { layer.getAccountChanges().entrySet().stream() .forEach( From e4a8e1da30ba90493085cf8e7c71c1cbdaef97ef Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Sun, 21 May 2023 10:13:31 +1000 Subject: [PATCH 2/6] JsonRpcHttpService - Add BodyHandler limit size (#5467) Signed-off-by: Gabriel-Trintinalia --- CHANGELOG.md | 1 + .../org/hyperledger/besu/cli/BesuCommand.java | 8 ++++++++ .../besu/cli/DefaultCommandValues.java | 2 ++ .../hyperledger/besu/cli/BesuCommandTest.java | 16 ++++++++++++++++ besu/src/test/resources/everything_config.toml | 1 + .../api/jsonrpc/JsonRpcConfiguration.java | 11 +++++++++++ .../ethereum/api/jsonrpc/JsonRpcHttpService.java | 1 + 7 files changed, 40 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c44ef733b8..891197a9101 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 23.4.1 ### Breaking Changes +- Add request content length limit for the JSON-RPC API (5MB) [#5467](https://github.com/hyperledger/besu/pull/5467) ### Additions and Improvements - Set the retention policy for RocksDB log files to maintain only the logs from the last week [#5428](https://github.com/hyperledger/besu/pull/5428) 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 95b38786c12..193ec5b2a81 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -767,6 +767,12 @@ static class JsonRPCHttpOptionGroup { description = "Specifies the maximum number of requests in a single RPC batch request via RPC. -1 specifies no limit (default: ${DEFAULT-VALUE})") private final Integer rpcHttpMaxBatchSize = DEFAULT_HTTP_MAX_BATCH_SIZE; + + @CommandLine.Option( + names = {"--rpc-http-max-request-content-length"}, + paramLabel = MANDATORY_LONG_FORMAT_HELP, + description = "Specifies the maximum request content length. (default: ${DEFAULT-VALUE})") + private final Long rpcHttpMaxRequestContentLength = DEFAULT_MAX_REQUEST_CONTENT_LENGTH; } // JSON-RPC Websocket Options @@ -2379,6 +2385,8 @@ && rpcHttpAuthenticationCredentialsFile() == null jsonRpcConfiguration.setTlsConfiguration(rpcHttpTlsConfiguration()); jsonRpcConfiguration.setHttpTimeoutSec(unstableRPCOptions.getHttpTimeoutSec()); jsonRpcConfiguration.setMaxBatchSize(jsonRPCHttpOptionGroup.rpcHttpMaxBatchSize); + jsonRpcConfiguration.setMaxRequestContentLength( + jsonRPCHttpOptionGroup.rpcHttpMaxRequestContentLength); return jsonRpcConfiguration; } diff --git a/besu/src/main/java/org/hyperledger/besu/cli/DefaultCommandValues.java b/besu/src/main/java/org/hyperledger/besu/cli/DefaultCommandValues.java index 615cb6628bc..887e6120419 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/DefaultCommandValues.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/DefaultCommandValues.java @@ -88,6 +88,8 @@ public interface DefaultCommandValues { int DEFAULT_HTTP_MAX_CONNECTIONS = 80; /** The constant DEFAULT_HTTP_MAX_BATCH_SIZE. */ int DEFAULT_HTTP_MAX_BATCH_SIZE = 1024; + /** The constant DEFAULT_MAX_REQUEST_CONTENT_LENGTH. */ + long DEFAULT_MAX_REQUEST_CONTENT_LENGTH = 5 * 1024 * 1024; // 5MB /** The constant DEFAULT_WS_MAX_CONNECTIONS. */ int DEFAULT_WS_MAX_CONNECTIONS = 80; /** The constant DEFAULT_WS_MAX_FRAME_SIZE. */ diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index 9b76e1f68e1..997af020a57 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -1634,6 +1634,22 @@ public void rpcHttpMaxBatchSizeOptionMustBeUsed() { assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); } + @Test + public void rpcHttpMaxRequestContentLengthOptionMustBeUsed() { + final int rpcHttpMaxRequestContentLength = 1; + parseCommand( + "--rpc-http-max-request-content-length", Long.toString(rpcHttpMaxRequestContentLength)); + + verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture()); + verify(mockRunnerBuilder).build(); + + assertThat(jsonRpcConfigArgumentCaptor.getValue().getMaxRequestContentLength()) + .isEqualTo(rpcHttpMaxRequestContentLength); + + assertThat(commandOutput.toString(UTF_8)).isEmpty(); + assertThat(commandErrorOutput.toString(UTF_8)).isEmpty(); + } + @Test public void maxpeersSet_p2pPeerLowerBoundSet() { diff --git a/besu/src/test/resources/everything_config.toml b/besu/src/test/resources/everything_config.toml index 82a9062a336..d0bb8c2cf76 100644 --- a/besu/src/test/resources/everything_config.toml +++ b/besu/src/test/resources/everything_config.toml @@ -85,6 +85,7 @@ rpc-ws-authentication-jwt-algorithm="RS256" rpc-http-tls-protocols=["TLSv1.2,TlSv1.1"] rpc-http-tls-cipher-suites=["TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384","TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"] rpc-http-max-batch-size=1 +rpc-http-max-request-content-length = 5242880 rpc-max-logs-range=100 # PRIVACY TLS diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcConfiguration.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcConfiguration.java index 66239260dd0..cb77d9ce818 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcConfiguration.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcConfiguration.java @@ -37,6 +37,7 @@ public class JsonRpcConfiguration { public static final int DEFAULT_ENGINE_JSON_RPC_PORT = 8551; public static final int DEFAULT_MAX_ACTIVE_CONNECTIONS = 80; public static final int DEFAULT_MAX_BATCH_SIZE = 1024; + public static final long DEFAULT_MAX_REQUEST_CONTENT_LENGTH = 5 * 1024 * 1024; // 5MB private boolean enabled; private int port; @@ -53,6 +54,7 @@ public class JsonRpcConfiguration { private long httpTimeoutSec = TimeoutOptions.defaultOptions().getTimeoutSeconds(); private int maxActiveConnections; private int maxBatchSize; + private long maxRequestContentLength; public static JsonRpcConfiguration createDefault() { final JsonRpcConfiguration config = new JsonRpcConfiguration(); @@ -63,6 +65,7 @@ public static JsonRpcConfiguration createDefault() { config.httpTimeoutSec = TimeoutOptions.defaultOptions().getTimeoutSeconds(); config.setMaxActiveConnections(DEFAULT_MAX_ACTIVE_CONNECTIONS); config.setMaxBatchSize(DEFAULT_MAX_BATCH_SIZE); + config.setMaxRequestContentLength(DEFAULT_MAX_REQUEST_CONTENT_LENGTH); return config; } @@ -263,4 +266,12 @@ public int getMaxBatchSize() { public void setMaxBatchSize(final int maxBatchSize) { this.maxBatchSize = maxBatchSize; } + + public long getMaxRequestContentLength() { + return maxRequestContentLength; + } + + public void setMaxRequestContentLength(final long maxRequestContentLength) { + this.maxRequestContentLength = maxRequestContentLength; + } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpService.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpService.java index 0cf5dc14049..12d623bdacb 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpService.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpService.java @@ -320,6 +320,7 @@ private Router buildRouter() { .route() .handler( BodyHandler.create() + .setBodyLimit(config.getMaxRequestContentLength()) .setUploadsDirectory(dataDir.resolve("uploads").toString()) .setDeleteUploadedFilesOnEnd(true)); router.route("/").method(HttpMethod.GET).handler(this::handleEmptyRequest); From 7053ee56564d9a73f0d5b543deb3457e4318da7b Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Mon, 22 May 2023 11:21:54 +1000 Subject: [PATCH 3/6] Implement debug_getRawReceipts (#5476) Co-authored-by: Gabriel-Trintinalia Signed-off-by: Adrian Sutton --- CHANGELOG.md | 1 + .../besu/ethereum/api/jsonrpc/RpcMethod.java | 1 + .../internal/methods/DebugGetRawReceipts.java | 61 +++++++++++++++++++ .../jsonrpc/methods/DebugJsonRpcMethods.java | 4 +- 4 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugGetRawReceipts.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 891197a9101..f129925dcf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - New optional feature to save the txpool content to file on shutdown and reloading it on startup [#5434](https://github.com/hyperledger/besu/pull/5434) - New option to send SNI header in TLS ClientHello message [#5439](https://github.com/hyperledger/besu/pull/5439) - Early access - layered transaction pool implementation [#5290](https://github.com/hyperledger/besu/pull/5290) +- New RPC method `debug_getRawReceipts` ### Bug Fixes - Fix eth_feeHistory response for the case in which blockCount is higher than highestBlock requested. [#5397](https://github.com/hyperledger/besu/pull/5397) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java index bdecee7c9e9..4530e22baab 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java @@ -48,6 +48,7 @@ public enum RpcMethod { DEBUG_GET_BAD_BLOCKS("debug_getBadBlocks"), DEBUG_GET_RAW_HEADER("debug_getRawHeader"), DEBUG_GET_RAW_BLOCK("debug_getRawBlock"), + DEBUG_GET_RAW_RECEIPTS("debug_getRawReceipts"), ENGINE_GET_PAYLOAD_V1("engine_getPayloadV1"), ENGINE_GET_PAYLOAD_V2("engine_getPayloadV2"), ENGINE_NEW_PAYLOAD_V1("engine_newPayloadV1"), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugGetRawReceipts.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugGetRawReceipts.java new file mode 100644 index 00000000000..9e879de5170 --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugGetRawReceipts.java @@ -0,0 +1,61 @@ +/* + * 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 org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameterOrBlockHash; +import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; +import org.hyperledger.besu.ethereum.core.TransactionReceipt; +import org.hyperledger.besu.ethereum.rlp.RLP; + +import java.util.List; + +import com.google.common.base.Suppliers; + +public class DebugGetRawReceipts extends AbstractBlockParameterOrBlockHashMethod { + + public DebugGetRawReceipts(final BlockchainQueries blockchain) { + super(Suppliers.ofInstance(blockchain)); + } + + @Override + public String getName() { + return RpcMethod.DEBUG_GET_RAW_RECEIPTS.getMethodName(); + } + + @Override + protected BlockParameterOrBlockHash blockParameterOrBlockHash( + final JsonRpcRequestContext request) { + return request.getRequiredParameter(0, BlockParameterOrBlockHash.class); + } + + @Override + protected Object resultByBlockHash(final JsonRpcRequestContext request, final Hash blockHash) { + return blockchainQueries + .get() + .getBlockchain() + .getTxReceipts(blockHash) + .map(this::toRLP) + .orElseGet(() -> new String[0]); + } + + private String[] toRLP(final List receipts) { + return receipts.stream() + .map(receipt -> RLP.encode(receipt::writeTo).toHexString()) + .toArray(String[]::new); + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java index cce9ada72a3..0dc681bf2ff 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/DebugJsonRpcMethods.java @@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugGetBadBlocks; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugGetRawBlock; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugGetRawHeader; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugGetRawReceipts; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugMetrics; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugResyncWorldstate; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.DebugSetHead; @@ -109,6 +110,7 @@ protected Map create() { () -> new TransactionTracer(blockReplay), blockchainQueries, protocolSchedule, dataDir), new DebugAccountAt(blockchainQueries, () -> new BlockTracer(blockReplay)), new DebugGetRawHeader(blockchainQueries), - new DebugGetRawBlock(blockchainQueries)); + new DebugGetRawBlock(blockchainQueries), + new DebugGetRawReceipts(blockchainQueries)); } } From 23e8f03eda6f61e7b5595b748343f5bcc4088e18 Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Tue, 23 May 2023 10:18:33 +1000 Subject: [PATCH 4/6] JSON array handling Improvements (#5458) Signed-off-by: Gabriel-Trintinalia --- .../api/handlers/AbstractJsonRpcExecutor.java | 138 +++++++++++ .../api/handlers/JsonRpcArrayExecutor.java | 134 +++++++++++ .../api/handlers/JsonRpcExecutorHandler.java | 222 ++---------------- .../api/handlers/JsonRpcObjectExecutor.java | 106 +++++++++ 4 files changed, 403 insertions(+), 197 deletions(-) create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/AbstractJsonRpcExecutor.java create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcArrayExecutor.java create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcObjectExecutor.java diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/AbstractJsonRpcExecutor.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/AbstractJsonRpcExecutor.java new file mode 100644 index 00000000000..3967c5250fe --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/AbstractJsonRpcExecutor.java @@ -0,0 +1,138 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * 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.handlers; + +import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_JSON; + +import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; +import org.hyperledger.besu.ethereum.api.jsonrpc.context.ContextKey; +import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; + +import java.io.IOException; +import java.util.Optional; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.json.Json; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.auth.User; +import io.vertx.ext.web.RoutingContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class AbstractJsonRpcExecutor { + private static final Logger LOG = LoggerFactory.getLogger(AbstractJsonRpcExecutor.class); + + private static final String SPAN_CONTEXT = "span_context"; + final JsonRpcExecutor jsonRpcExecutor; + final Tracer tracer; + final RoutingContext ctx; + final JsonRpcConfiguration jsonRpcConfiguration; + + private static final ObjectMapper jsonObjectMapper = + new ObjectMapper() + .registerModule(new Jdk8Module()); // Handle JDK8 Optionals (de)serialization + + /** + * Creates a new AbstractJsonRpcExecutor. + * + * @param jsonRpcExecutor The executor used to process the JSON RPC requests. + * @param tracer The tracer used for monitoring and debugging purposes. + * @param ctx The context of the routing, containing information about the HTTP request and + * response. + * @param jsonRpcConfiguration The configuration for JSON RPC operations + */ + public AbstractJsonRpcExecutor( + final JsonRpcExecutor jsonRpcExecutor, + final Tracer tracer, + final RoutingContext ctx, + final JsonRpcConfiguration jsonRpcConfiguration) { + this.jsonRpcExecutor = jsonRpcExecutor; + this.tracer = tracer; + this.ctx = ctx; + this.jsonRpcConfiguration = jsonRpcConfiguration; + } + + abstract void execute() throws IOException; + + abstract String getRpcMethodName(final RoutingContext ctx); + + protected static JsonRpcResponse executeRequest( + final JsonRpcExecutor jsonRpcExecutor, + final Tracer tracer, + final JsonObject jsonRequest, + final RoutingContext ctx) { + final Optional user = ContextKey.AUTHENTICATED_USER.extractFrom(ctx, Optional::empty); + final Context spanContext = ctx.get(SPAN_CONTEXT); + return jsonRpcExecutor.execute( + user, + tracer, + spanContext, + () -> !ctx.response().closed(), + jsonRequest, + req -> req.mapTo(JsonRpcRequest.class)); + } + + protected static void handleJsonRpcError( + final RoutingContext routingContext, final Object id, final JsonRpcError error) { + final HttpServerResponse response = routingContext.response(); + if (!response.closed()) { + response + .setStatusCode(statusCodeFromError(error).code()) + .end(Json.encode(new JsonRpcErrorResponse(id, error))); + } + } + + private static HttpResponseStatus statusCodeFromError(final JsonRpcError error) { + return switch (error) { + case INVALID_REQUEST, PARSE_ERROR -> HttpResponseStatus.BAD_REQUEST; + default -> HttpResponseStatus.OK; + }; + } + + protected HttpServerResponse prepareHttpResponse(final RoutingContext ctx) { + HttpServerResponse response = ctx.response(); + response = response.putHeader("Content-Type", APPLICATION_JSON); + return response; + } + + protected static ObjectMapper getJsonObjectMapper() { + return jsonObjectMapper; + } + + @FunctionalInterface + protected interface ExceptionThrowingSupplier { + T get() throws Exception; + } + + protected static void lazyTraceLogger( + final ExceptionThrowingSupplier logMessageSupplier) { + if (LOG.isTraceEnabled()) { + try { + LOG.trace(logMessageSupplier.get()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcArrayExecutor.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcArrayExecutor.java new file mode 100644 index 00000000000..a45ad2a3dc3 --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcArrayExecutor.java @@ -0,0 +1,134 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * 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.handlers; + +import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.INVALID_REQUEST; + +import org.hyperledger.besu.ethereum.api.jsonrpc.JsonResponseStreamer; +import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; +import org.hyperledger.besu.ethereum.api.jsonrpc.context.ContextKey; +import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; +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.JsonRpcResponseType; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import io.opentelemetry.api.trace.Tracer; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; + +public class JsonRpcArrayExecutor extends AbstractJsonRpcExecutor { + public JsonRpcArrayExecutor( + final JsonRpcExecutor jsonRpcExecutor, + final Tracer tracer, + final RoutingContext ctx, + final JsonRpcConfiguration jsonRpcConfiguration) { + super(jsonRpcExecutor, tracer, ctx, jsonRpcConfiguration); + } + + /** + * Executes the JSON-RPC request(s) associated with the current routing context. + * + * @throws IOException if there is an error writing the response to the client + */ + @Override + void execute() throws IOException { + HttpServerResponse response = prepareHttpResponse(ctx); + final JsonArray batchJsonRequest = getRequestBodyAsJsonArray(ctx); + if (isBatchSizeValid(batchJsonRequest)) { + try (final JsonResponseStreamer streamer = + new JsonResponseStreamer(response, ctx.request().remoteAddress())) { + executeRpcRequestBatch(batchJsonRequest, streamer); + } + } else { + handleJsonRpcError(ctx, null, JsonRpcError.EXCEEDS_RPC_MAX_BATCH_SIZE); + } + } + + /** + * Executes a batch of RPC requests. + * + * @param rpcRequestBatch the batch of RPC requests. + * @param streamer the JsonResponseStreamer to use. + */ + public void executeRpcRequestBatch( + final JsonArray rpcRequestBatch, final JsonResponseStreamer streamer) throws IOException { + try (JsonGenerator generator = getJsonObjectMapper().getFactory().createGenerator(streamer)) { + generator.writeStartArray(); + for (int i = 0; i < rpcRequestBatch.size(); i++) { + JsonRpcResponse response = processMaybeRequest(rpcRequestBatch.getValue(i)); + if (response.getType() != JsonRpcResponseType.NONE) { + generator.writeObject(response); + } + } + generator.writeEndArray(); + } + } + + /** + * Processes a single RPC request. + * + * @param maybeRequest the object that might be a request. + * @return the response from executing the request, or an error response if it wasn't a valid + * request. + */ + private JsonRpcResponse processMaybeRequest(final Object maybeRequest) { + if (maybeRequest instanceof JsonObject) { + return executeRequest((JsonObject) maybeRequest); + } else { + return createErrorResponse(); + } + } + + /** + * Executes a single RPC request. + * + * @param request the request to execute. + * @return the response from executing the request. + */ + private JsonRpcResponse executeRequest(final JsonObject request) { + return executeRequest(jsonRpcExecutor, tracer, request, ctx); + } + + /** + * Creates an error response for an invalid request. + * + * @return an error response. + */ + private JsonRpcResponse createErrorResponse() { + return new JsonRpcErrorResponse(null, INVALID_REQUEST); + } + + @Override + String getRpcMethodName(final RoutingContext ctx) { + return "JsonArray"; + } + + private boolean isBatchSizeValid(final JsonArray batchJsonRequest) { + return !(jsonRpcConfiguration.getMaxBatchSize() > 0 + && batchJsonRequest.size() > jsonRpcConfiguration.getMaxBatchSize()); + } + + private JsonArray getRequestBodyAsJsonArray(final RoutingContext ctx) { + final JsonArray batchJsonRequest = ctx.get(ContextKey.REQUEST_BODY_AS_JSON_ARRAY.name()); + lazyTraceLogger(batchJsonRequest::toString); + return batchJsonRequest; + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcExecutorHandler.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcExecutorHandler.java index 5f3874f9eb3..3d95f15245a 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcExecutorHandler.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcExecutorHandler.java @@ -14,50 +14,25 @@ */ package org.hyperledger.besu.ethereum.api.handlers; -import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.INVALID_REQUEST; +import static org.hyperledger.besu.ethereum.api.handlers.AbstractJsonRpcExecutor.handleJsonRpcError; -import org.hyperledger.besu.ethereum.api.jsonrpc.JsonResponseStreamer; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.context.ContextKey; import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; -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.JsonRpcResponseType; import java.io.IOException; -import java.security.InvalidParameterException; -import java.util.ArrayList; -import java.util.List; import java.util.Optional; -import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import io.netty.handler.codec.http.HttpResponseStatus; import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.context.Context; import io.vertx.core.Handler; -import io.vertx.core.http.HttpServerResponse; -import io.vertx.core.json.Json; -import io.vertx.core.json.JsonArray; -import io.vertx.core.json.JsonObject; -import io.vertx.ext.auth.User; import io.vertx.ext.web.RoutingContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class JsonRpcExecutorHandler { - private static final Logger LOG = LoggerFactory.getLogger(JsonRpcExecutorHandler.class); - private static final String SPAN_CONTEXT = "span_context"; - private static final String APPLICATION_JSON = "application/json"; - private static ObjectMapper jsonObjectMapper = - new ObjectMapper() - .registerModule(new Jdk8Module()); // Handle JDK8 Optionals (de)serialization - private static ObjectWriter jsonObjectWriter = createObjectWriter(jsonObjectMapper); private JsonRpcExecutorHandler() {} @@ -66,8 +41,6 @@ public static Handler handler( final JsonRpcExecutor jsonRpcExecutor, final Tracer tracer, final JsonRpcConfiguration jsonRpcConfiguration) { - JsonRpcExecutorHandler.jsonObjectMapper = jsonObjectMapper; - JsonRpcExecutorHandler.jsonObjectWriter = createObjectWriter(jsonObjectMapper); return handler(jsonRpcExecutor, tracer, jsonRpcConfiguration); } @@ -76,191 +49,46 @@ public static Handler handler( final Tracer tracer, final JsonRpcConfiguration jsonRpcConfiguration) { return ctx -> { - HttpServerResponse response = ctx.response(); try { - response = response.putHeader("Content-Type", APPLICATION_JSON); - if (isJsonObjectRequest(ctx)) { - final JsonRpcResponse jsonRpcResponse = - executeJsonObjectRequest(jsonRpcExecutor, tracer, ctx); - handleJsonObjectResponse(response, jsonRpcResponse, ctx); - } else if (isJsonArrayRequest(ctx)) { - final List jsonRpcBatchResponses; - try { - jsonRpcBatchResponses = - executeJsonArrayRequest(jsonRpcExecutor, tracer, ctx, jsonRpcConfiguration); - handleJsonArrayResponse(response, jsonRpcBatchResponses, ctx); - } catch (final InvalidParameterException e) { - handleJsonRpcError(ctx, null, JsonRpcError.EXCEEDS_RPC_MAX_BATCH_SIZE); - } catch (final RuntimeException e) { - response.setStatusCode(HttpResponseStatus.BAD_REQUEST.code()).end(); - } - } else { - handleJsonRpcError(ctx, null, JsonRpcError.PARSE_ERROR); - } - } catch (final IOException ex) { - final String method = getRpcMethodName(ctx); - LOG.error("{} - Error streaming JSON-RPC response", method, ex); + createExecutor(jsonRpcExecutor, tracer, ctx, jsonRpcConfiguration) + .ifPresentOrElse( + executor -> { + try { + executor.execute(); + } catch (IOException e) { + final String method = executor.getRpcMethodName(ctx); + LOG.error("{} - Error streaming JSON-RPC response", method, e); + throw new RuntimeException(e); + } + }, + () -> handleJsonRpcError(ctx, null, JsonRpcError.PARSE_ERROR)); } catch (final RuntimeException e) { handleJsonRpcError(ctx, null, JsonRpcError.INTERNAL_ERROR); } }; } - private static ObjectWriter createObjectWriter(final ObjectMapper jsonObjectMapper) { - return jsonObjectMapper - .writerWithDefaultPrettyPrinter() - .without(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM) - .with(JsonGenerator.Feature.AUTO_CLOSE_TARGET); - } - - private static boolean isJsonObjectRequest(final RoutingContext ctx) { - return ctx.data().containsKey(ContextKey.REQUEST_BODY_AS_JSON_OBJECT.name()); - } - - private static boolean isJsonArrayRequest(final RoutingContext ctx) { - return ctx.data().containsKey(ContextKey.REQUEST_BODY_AS_JSON_ARRAY.name()); - } - - private static JsonRpcResponse executeRequest( - final JsonRpcExecutor jsonRpcExecutor, - final Tracer tracer, - final JsonObject jsonRequest, - final RoutingContext ctx) { - final Optional user = ContextKey.AUTHENTICATED_USER.extractFrom(ctx, Optional::empty); - final Context spanContext = ctx.get(SPAN_CONTEXT); - return jsonRpcExecutor.execute( - user, - tracer, - spanContext, - () -> !ctx.response().closed(), - jsonRequest, - req -> req.mapTo(JsonRpcRequest.class)); - } - - private static JsonRpcResponse executeJsonObjectRequest( - final JsonRpcExecutor jsonRpcExecutor, final Tracer tracer, final RoutingContext ctx) { - final JsonObject jsonRequest = ctx.get(ContextKey.REQUEST_BODY_AS_JSON_OBJECT.name()); - lazyTraceLogger(jsonRequest::toString); - return executeRequest(jsonRpcExecutor, tracer, jsonRequest, ctx); - } - - private static List executeJsonArrayRequest( + private static Optional createExecutor( final JsonRpcExecutor jsonRpcExecutor, final Tracer tracer, final RoutingContext ctx, - final JsonRpcConfiguration jsonRpcConfiguration) - throws InvalidParameterException { - final JsonArray batchJsonRequest = ctx.get(ContextKey.REQUEST_BODY_AS_JSON_ARRAY.name()); - lazyTraceLogger(batchJsonRequest::toString); - final List jsonRpcBatchResponses = new ArrayList<>(); - - if (jsonRpcConfiguration.getMaxBatchSize() > 0 - && batchJsonRequest.size() > jsonRpcConfiguration.getMaxBatchSize()) { - throw new InvalidParameterException(); - } - - for (int i = 0; i < batchJsonRequest.size(); i++) { - final JsonObject jsonRequest; - try { - jsonRequest = batchJsonRequest.getJsonObject(i); - } catch (final ClassCastException e) { - jsonRpcBatchResponses.add(new JsonRpcErrorResponse(null, INVALID_REQUEST)); - continue; - } - jsonRpcBatchResponses.add(executeRequest(jsonRpcExecutor, tracer, jsonRequest, ctx)); - } - return jsonRpcBatchResponses; - } - - private static void handleJsonObjectResponse( - final HttpServerResponse response, - final JsonRpcResponse jsonRpcResponse, - final RoutingContext ctx) - throws IOException { - response.setStatusCode(status(jsonRpcResponse).code()); - if (jsonRpcResponse.getType() == JsonRpcResponseType.NONE) { - response.end(); - } else { - try (final JsonResponseStreamer streamer = - new JsonResponseStreamer(response, ctx.request().remoteAddress())) { - // underlying output stream lifecycle is managed by the json object writer - lazyTraceLogger(() -> jsonObjectMapper.writeValueAsString(jsonRpcResponse)); - jsonObjectWriter.writeValue(streamer, jsonRpcResponse); - } - } - } - - private static void handleJsonArrayResponse( - final HttpServerResponse response, - final List jsonRpcBatchResponses, - final RoutingContext ctx) - throws IOException { - final JsonRpcResponse[] completed = - jsonRpcBatchResponses.stream() - .filter(jsonRpcResponse -> jsonRpcResponse.getType() != JsonRpcResponseType.NONE) - .toArray(JsonRpcResponse[]::new); - try (final JsonResponseStreamer streamer = - new JsonResponseStreamer(response, ctx.request().remoteAddress())) { - // underlying output stream lifecycle is managed by the json object writer - lazyTraceLogger(() -> jsonObjectMapper.writeValueAsString(completed)); - jsonObjectWriter.writeValue(streamer, completed); - } - } - - private static String getRpcMethodName(final RoutingContext ctx) { + final JsonRpcConfiguration jsonRpcConfiguration) { if (isJsonObjectRequest(ctx)) { - final JsonObject jsonObject = ctx.get(ContextKey.REQUEST_BODY_AS_JSON_OBJECT.name()); - return jsonObject.getString("method"); - } else { - return ""; - } - } - - private static void handleJsonRpcError( - final RoutingContext routingContext, final Object id, final JsonRpcError error) { - final HttpServerResponse response = routingContext.response(); - if (!response.closed()) { - response - .setStatusCode(statusCodeFromError(error).code()) - .end(Json.encode(new JsonRpcErrorResponse(id, error))); + return Optional.of( + new JsonRpcObjectExecutor(jsonRpcExecutor, tracer, ctx, jsonRpcConfiguration)); } - } - - private static HttpResponseStatus status(final JsonRpcResponse response) { - switch (response.getType()) { - case UNAUTHORIZED: - return HttpResponseStatus.UNAUTHORIZED; - case ERROR: - return statusCodeFromError(((JsonRpcErrorResponse) response).getError()); - case SUCCESS: - case NONE: - default: - return HttpResponseStatus.OK; - } - } - - private static HttpResponseStatus statusCodeFromError(final JsonRpcError error) { - switch (error) { - case INVALID_REQUEST: - case PARSE_ERROR: - return HttpResponseStatus.BAD_REQUEST; - default: - return HttpResponseStatus.OK; + if (isJsonArrayRequest(ctx)) { + return Optional.of( + new JsonRpcArrayExecutor(jsonRpcExecutor, tracer, ctx, jsonRpcConfiguration)); } + return Optional.empty(); } - @FunctionalInterface - private interface ExceptionThrowingSupplier { - T get() throws Exception; + private static boolean isJsonObjectRequest(final RoutingContext ctx) { + return ctx.data().containsKey(ContextKey.REQUEST_BODY_AS_JSON_OBJECT.name()); } - private static void lazyTraceLogger(final ExceptionThrowingSupplier logMessageSupplier) { - if (LOG.isTraceEnabled()) { - try { - LOG.trace(logMessageSupplier.get()); - } catch (Exception e) { - throw new RuntimeException(e); - } - } + private static boolean isJsonArrayRequest(final RoutingContext ctx) { + return ctx.data().containsKey(ContextKey.REQUEST_BODY_AS_JSON_ARRAY.name()); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcObjectExecutor.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcObjectExecutor.java new file mode 100644 index 00000000000..2380dcca3bd --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/handlers/JsonRpcObjectExecutor.java @@ -0,0 +1,106 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * 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.handlers; + +import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_JSON; + +import org.hyperledger.besu.ethereum.api.jsonrpc.JsonResponseStreamer; +import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; +import org.hyperledger.besu.ethereum.api.jsonrpc.context.ContextKey; +import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; +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.JsonRpcResponseType; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.ObjectWriter; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.opentelemetry.api.trace.Tracer; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; + +public class JsonRpcObjectExecutor extends AbstractJsonRpcExecutor { + private static final ObjectWriter jsonObjectWriter = createObjectWriter(); + + public JsonRpcObjectExecutor( + final JsonRpcExecutor jsonRpcExecutor, + final Tracer tracer, + final RoutingContext ctx, + final JsonRpcConfiguration jsonRpcConfiguration) { + super(jsonRpcExecutor, tracer, ctx, jsonRpcConfiguration); + } + + @Override + void execute() throws IOException { + HttpServerResponse response = ctx.response(); + response = response.putHeader("Content-Type", APPLICATION_JSON); + + final JsonObject jsonRequest = ctx.get(ContextKey.REQUEST_BODY_AS_JSON_OBJECT.name()); + lazyTraceLogger(jsonRequest::toString); + final JsonRpcResponse jsonRpcResponse = + executeRequest(jsonRpcExecutor, tracer, jsonRequest, ctx); + handleJsonObjectResponse(response, jsonRpcResponse, ctx); + } + + @Override + String getRpcMethodName(final RoutingContext ctx) { + final JsonObject jsonObject = ctx.get(ContextKey.REQUEST_BODY_AS_JSON_OBJECT.name()); + return jsonObject.getString("method"); + } + + private static void handleJsonObjectResponse( + final HttpServerResponse response, + final JsonRpcResponse jsonRpcResponse, + final RoutingContext ctx) + throws IOException { + response.setStatusCode(status(jsonRpcResponse).code()); + if (jsonRpcResponse.getType() == JsonRpcResponseType.NONE) { + response.end(); + } else { + try (final JsonResponseStreamer streamer = + new JsonResponseStreamer(response, ctx.request().remoteAddress())) { + // underlying output stream lifecycle is managed by the json object writer + lazyTraceLogger(() -> getJsonObjectMapper().writeValueAsString(jsonRpcResponse)); + jsonObjectWriter.writeValue(streamer, jsonRpcResponse); + } + } + } + + private static HttpResponseStatus status(final JsonRpcResponse response) { + return switch (response.getType()) { + case UNAUTHORIZED -> HttpResponseStatus.UNAUTHORIZED; + case ERROR -> statusCodeFromError(((JsonRpcErrorResponse) response).getError()); + default -> HttpResponseStatus.OK; + }; + } + + private static ObjectWriter createObjectWriter() { + return getJsonObjectMapper() + .writerWithDefaultPrettyPrinter() + .without(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM) + .with(JsonGenerator.Feature.AUTO_CLOSE_TARGET); + } + + private static HttpResponseStatus statusCodeFromError(final JsonRpcError error) { + return switch (error) { + case INVALID_REQUEST, PARSE_ERROR -> HttpResponseStatus.BAD_REQUEST; + default -> HttpResponseStatus.OK; + }; + } +} From f121f12ead70fd26dcfe89a2235afa4ad6060ede Mon Sep 17 00:00:00 2001 From: Gabriel Fukushima Date: Tue, 23 May 2023 10:36:34 +1000 Subject: [PATCH 5/6] remove old ef bootnodes (#5474) Signed-off-by: Gabriel Fukushima --- .../p2p/config/DefaultDiscoveryConfiguration.java | 12 +----------- config/src/main/resources/mainnet.json | 8 +------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/besu/src/test/java/org/hyperledger/besu/ethereum/p2p/config/DefaultDiscoveryConfiguration.java b/besu/src/test/java/org/hyperledger/besu/ethereum/p2p/config/DefaultDiscoveryConfiguration.java index 4658c2e5554..28772492891 100644 --- a/besu/src/test/java/org/hyperledger/besu/ethereum/p2p/config/DefaultDiscoveryConfiguration.java +++ b/besu/src/test/java/org/hyperledger/besu/ethereum/p2p/config/DefaultDiscoveryConfiguration.java @@ -38,17 +38,7 @@ public class DefaultDiscoveryConfiguration { "enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@18.138.108.67:30303", // Singapore AWS "enode://22a8232c3abc76a16ae9d6c3b164f98775fe226f0917b0ca871128a74a8e9630b458460865bab457221f1d448dd9791d24c4e5d88786180ac185df813a68d4de@3.209.45.79:30303", // Virginia AWS "enode://2b252ab6a1d0f971d9722cb839a42cb81db019ba44c08754628ab4a823487071b5695317c8ccd085219c3a03af063495b2f1da8d18218da2d6a82981b45e6ffc@65.108.70.101:30303", // Helsinki Hetzner - "enode://4aeb4ab6c14b23e2c4cfdce879c04b0748a20d8e9b59e25ded2a08143e265c6c25936e74cbc8e641e3312ca288673d91f2f93f8e277de3cfa444ecdaaf982052@157.90.35.166:30303", // Falkenstein Hetzner - - // Old Ethereum Foundation Bootnodes - "enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", // IE - "enode://3f1d12044546b76342d59d4a05532c14b85aa669704bfe1f864fe079415aa2c02d743e03218e57a33fb94523adb54032871a6c51b2cc5514cb7c7e35b3ed0a99@13.93.211.84:30303", // US-WEST - "enode://78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d@191.235.84.50:30303", // BR - "enode://158f8aab45f6d19c6cbf4a089c2670541a8da11978a2f90dbf6a502a4a3bab80d288afdbeb7ec0ef6d92de563767f3b1ea9e8e334ca711e9f8e2df5a0385e8e6@13.75.154.138:30303", // AU - "enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303", // SG - - // Ethereum Foundation Aleth Bootnodes - "enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303" // DE + "enode://4aeb4ab6c14b23e2c4cfdce879c04b0748a20d8e9b59e25ded2a08143e265c6c25936e74cbc8e641e3312ca288673d91f2f93f8e277de3cfa444ecdaaf982052@157.90.35.166:30303" // Falkenstein Hetzner ) .map(EnodeURLImpl::fromString) .collect(toList())); diff --git a/config/src/main/resources/mainnet.json b/config/src/main/resources/mainnet.json index 00668a8cf0e..645ddb09d47 100644 --- a/config/src/main/resources/mainnet.json +++ b/config/src/main/resources/mainnet.json @@ -23,13 +23,7 @@ "enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@18.138.108.67:30303", "enode://22a8232c3abc76a16ae9d6c3b164f98775fe226f0917b0ca871128a74a8e9630b458460865bab457221f1d448dd9791d24c4e5d88786180ac185df813a68d4de@3.209.45.79:30303", "enode://2b252ab6a1d0f971d9722cb839a42cb81db019ba44c08754628ab4a823487071b5695317c8ccd085219c3a03af063495b2f1da8d18218da2d6a82981b45e6ffc@65.108.70.101:30303", - "enode://4aeb4ab6c14b23e2c4cfdce879c04b0748a20d8e9b59e25ded2a08143e265c6c25936e74cbc8e641e3312ca288673d91f2f93f8e277de3cfa444ecdaaf982052@157.90.35.166:30303", - "enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", - "enode://3f1d12044546b76342d59d4a05532c14b85aa669704bfe1f864fe079415aa2c02d743e03218e57a33fb94523adb54032871a6c51b2cc5514cb7c7e35b3ed0a99@13.93.211.84:30303", - "enode://78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d@191.235.84.50:30303", - "enode://158f8aab45f6d19c6cbf4a089c2670541a8da11978a2f90dbf6a502a4a3bab80d288afdbeb7ec0ef6d92de563767f3b1ea9e8e334ca711e9f8e2df5a0385e8e6@13.75.154.138:30303", - "enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303", - "enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303" + "enode://4aeb4ab6c14b23e2c4cfdce879c04b0748a20d8e9b59e25ded2a08143e265c6c25936e74cbc8e641e3312ca288673d91f2f93f8e277de3cfa444ecdaaf982052@157.90.35.166:30303" ] }, "checkpoint": { From 65bcc557e41cca2568588a21441aa5d8658a5e79 Mon Sep 17 00:00:00 2001 From: Stefan Pingel <16143240+pinges@users.noreply.github.com> Date: Tue, 23 May 2023 13:24:22 +1000 Subject: [PATCH 6/6] Add plugin API to select Transactions (#5396) This API fulfils the basic requirements, but will probably be extended in the near future. Signed-off-by: Stefan Signed-off-by: Stefan Pingel <16143240+pinges@users.noreply.github.com> Co-authored-by: Sally MacFarlane --- .../tests/acceptance/dsl/node/BesuNode.java | 2 +- .../dsl/node/ThreadBesuNodeRunner.java | 39 +++++++-- .../node/configuration/BesuNodeFactory.java | 23 +++++ .../org/hyperledger/besu/cli/BesuCommand.java | 29 ++++++- .../controller/BesuControllerBuilder.java | 30 ++++++- ...onsensusScheduleBesuControllerBuilder.java | 10 ++- .../TransitionBesuControllerBuilder.java | 10 ++- .../TransactionSelectionServiceImpl.java | 37 ++++++++ .../besu/cli/CommandTestAbstract.java | 5 +- .../CliqueDifficultyCalculatorTest.java | 3 +- .../clique/NodeCanProduceNextBlockTest.java | 15 ++-- .../blockcreation/CliqueBlockCreatorTest.java | 3 +- .../CliqueMinerExecutorTest.java | 2 +- .../CliqueDifficultyValidationRuleTest.java | 3 +- .../CliqueExtraDataValidationRuleTest.java | 3 +- .../common/MigratingProtocolContext.java | 18 +++- .../common/MigratingProtocolContextTest.java | 4 +- .../BftCoinbaseValidationRuleTest.java | 5 +- .../BftCommitSealsValidationRuleTest.java | 11 +-- .../BftValidatorsValidationRuleTest.java | 12 ++- .../BftVanityDataValidationRuleTest.java | 5 +- .../ibft/support/TestContextBuilder.java | 3 +- .../tests/round/IbftRoundIntegrationTest.java | 3 +- ...ockHeaderValidationRulesetFactoryTest.java | 5 +- .../ibft/IbftProtocolScheduleTest.java | 4 +- .../blockcreation/BftBlockCreatorTest.java | 3 +- .../IbftBlockHeightManagerTest.java | 3 +- .../ibft/statemachine/IbftRoundTest.java | 3 +- .../ibft/validation/MessageValidatorTest.java | 5 +- .../blockcreation/MergeCoordinatorTest.java | 3 +- .../merge/blockcreation/MergeReorgTest.java | 3 +- .../qbft/support/TestContextBuilder.java | 3 +- .../test/round/QbftRoundIntegrationTest.java | 4 +- ...ockHeaderValidationRulesetFactoryTest.java | 3 +- .../qbft/QbftProtocolScheduleTest.java | 3 +- .../QbftValidatorsValidationRuleTest.java | 16 +++- .../QbftBlockHeightManagerTest.java | 3 +- .../qbft/statemachine/QbftRoundTest.java | 3 +- .../ProposalPayloadValidatorTest.java | 7 +- .../validation/ProposalValidatorTest.java | 3 +- .../RoundChangeMessageValidatorTest.java | 3 +- .../jsonrpc/JsonRpcTestMethodsFactory.java | 2 +- ...ckByNumberLatestDesyncIntegrationTest.java | 2 +- .../AbstractEthGraphQLHttpServiceTest.java | 2 +- .../blockcreation/AbstractBlockCreator.java | 3 +- .../BlockTransactionSelector.java | 87 ++++++++++--------- .../AbstractBlockTransactionSelectorTest.java | 17 +++- .../blockcreation/BlockMinerTest.java | 4 +- .../besu/ethereum/ProtocolContext.java | 21 ++++- .../ethereum/core/BlockchainSetupUtil.java | 4 +- .../core/ExecutionContextTestFixture.java | 3 +- .../bonsai/AbstractIsolationTests.java | 2 +- .../eth/transactions/PendingTransactions.java | 7 +- .../AbstractPendingTransactionsSorter.java | 1 + .../AbstractBlockPropagationManagerTest.java | 4 +- .../fullsync/FullSyncTargetManagerTest.java | 3 +- ...neCommonAncestorTaskParameterizedTest.java | 3 +- .../DetermineCommonAncestorTaskTest.java | 4 +- .../ethereum/eth/transactions/TestNode.java | 2 +- .../LayeredPendingTransactionsTest.java | 6 +- .../AbstractPendingTransactionsTestBase.java | 7 +- .../BlockchainReferenceTestCaseSpec.java | 3 +- .../ethereum/retesteth/RetestethContext.java | 2 +- plugin-api/build.gradle | 2 +- .../data/TransactionSelectionResult.java | 26 ++++++ .../services/TransactionSelectionService.java | 40 +++++++++ .../txselection/TransactionSelector.java | 39 +++++++++ .../TransactionSelectorFactory.java | 30 +++++++ 68 files changed, 534 insertions(+), 144 deletions(-) create mode 100644 besu/src/main/java/org/hyperledger/besu/services/TransactionSelectionServiceImpl.java create mode 100644 plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java create mode 100644 plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSelectionService.java create mode 100644 plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelector.java create mode 100644 plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelectorFactory.java diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java index c336192b2b1..d822a21fb18 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java @@ -386,7 +386,7 @@ public String getHostName() { return LOCALHOST; } - private NodeRequests nodeRequests() { + public NodeRequests nodeRequests() { Optional websocketService = Optional.empty(); if (nodeRequests == null) { final Web3jService web3jService; diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java index bfa0ae2cbcf..cea5b323210 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java @@ -45,7 +45,9 @@ import org.hyperledger.besu.plugin.services.PicoCLIOptions; import org.hyperledger.besu.plugin.services.SecurityModuleService; import org.hyperledger.besu.plugin.services.StorageService; +import org.hyperledger.besu.plugin.services.TransactionSelectionService; import org.hyperledger.besu.plugin.services.storage.rocksdb.RocksDBPlugin; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; import org.hyperledger.besu.services.BesuConfigurationImpl; import org.hyperledger.besu.services.BesuEventsImpl; import org.hyperledger.besu.services.BesuPluginContextImpl; @@ -54,6 +56,7 @@ import org.hyperledger.besu.services.RpcEndpointServiceImpl; import org.hyperledger.besu.services.SecurityModuleServiceImpl; import org.hyperledger.besu.services.StorageServiceImpl; +import org.hyperledger.besu.services.TransactionSelectionServiceImpl; import java.io.File; import java.nio.file.Path; @@ -63,6 +66,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -91,14 +95,22 @@ private BesuPluginContextImpl buildPluginContext( besuPluginContext.addService(StorageService.class, storageService); besuPluginContext.addService(SecurityModuleService.class, securityModuleService); besuPluginContext.addService(PicoCLIOptions.class, new PicoCLIOptionsImpl(commandLine)); - - final Path pluginsPath = node.homeDirectory().resolve("plugins"); - final File pluginsDirFile = pluginsPath.toFile(); - if (!pluginsDirFile.isDirectory()) { - pluginsDirFile.mkdirs(); - pluginsDirFile.deleteOnExit(); + besuPluginContext.addService( + TransactionSelectionService.class, new TransactionSelectionServiceImpl()); + + final Path pluginsPath; + final String pluginDirEnv = System.getenv("besu.plugins.dir"); + if (pluginDirEnv == null || pluginDirEnv.isEmpty()) { + pluginsPath = node.homeDirectory().resolve("plugins"); + final File pluginsDirFile = pluginsPath.toFile(); + if (!pluginsDirFile.isDirectory()) { + pluginsDirFile.mkdirs(); + pluginsDirFile.deleteOnExit(); + } + } else { + pluginsPath = Path.of(pluginDirEnv); + System.setProperty("besu.plugins.dir", pluginsPath.toString()); } - System.setProperty("besu.plugins.dir", pluginsPath.toString()); besuPluginContext.registerPlugins(pluginsPath); commandLine.parseArgs(node.getConfiguration().getExtraCLIOptions().toArray(new String[0])); @@ -169,6 +181,9 @@ public void startNode(final BesuNode node) { final int maxPeers = 25; + final Optional transactionSelectorFactory = + getTransactionSelectorFactory(besuPluginContext); + builder .synchronizerConfiguration(new SynchronizerConfiguration.Builder().build()) .dataDirectory(node.homeDirectory()) @@ -190,7 +205,8 @@ public void startNode(final BesuNode node) { .lowerBoundPeers(maxPeers) .maxRemotelyInitiatedPeers(15) .networkConfiguration(node.getNetworkingConfiguration()) - .randomPeerPriority(false); + .randomPeerPriority(false) + .transactionSelectorFactory(transactionSelectorFactory); node.getGenesisConfig() .map(GenesisConfigFile::fromConfig) @@ -299,4 +315,11 @@ public void startConsoleCapture() { public String getConsoleContents() { throw new RuntimeException("Console contents can only be captured in process execution"); } + + private Optional getTransactionSelectorFactory( + final BesuPluginContextImpl besuPluginContext) { + final Optional txSelectionService = + besuPluginContext.getService(TransactionSelectionService.class); + return txSelectionService.isPresent() ? txSelectionService.get().get() : Optional.empty(); + } } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java index 6f3c601fa37..fb6ac5f1014 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java @@ -107,10 +107,33 @@ public BesuNode createMinerNode( return create(config); } + public BesuNode createMinerNodeWithExtraCliOptions( + final String name, + final UnaryOperator configModifier, + final List extraCliOptions) + throws IOException { + BesuNodeConfigurationBuilder builder = + new BesuNodeConfigurationBuilder() + .name(name) + .miningEnabled() + .jsonRpcEnabled() + .webSocketEnabled() + .extraCLIOptions(extraCliOptions); + builder = configModifier.apply(builder); + final BesuNodeConfiguration config = builder.build(); + + return create(config); + } + public BesuNode createMinerNode(final String name) throws IOException { return createMinerNode(name, UnaryOperator.identity()); } + public BesuNode createMinerNodeWithExtraCliOptions( + final String name, final List extraCliOptions) throws IOException { + return createMinerNodeWithExtraCliOptions(name, UnaryOperator.identity(), extraCliOptions); + } + public BesuNode createMinerNodeWithRevertReasonEnabled(final String name) throws IOException { return createMinerNode(name, BesuNodeConfigurationBuilder::revertReasonEnabled); } 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 193ec5b2a81..b842392f03d 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -167,12 +167,14 @@ import org.hyperledger.besu.plugin.services.SecurityModuleService; import org.hyperledger.besu.plugin.services.StorageService; import org.hyperledger.besu.plugin.services.TraceService; +import org.hyperledger.besu.plugin.services.TransactionSelectionService; import org.hyperledger.besu.plugin.services.exception.StorageException; import org.hyperledger.besu.plugin.services.metrics.MetricCategory; import org.hyperledger.besu.plugin.services.metrics.MetricCategoryRegistry; import org.hyperledger.besu.plugin.services.securitymodule.SecurityModule; import org.hyperledger.besu.plugin.services.storage.PrivacyKeyValueStorageFactory; import org.hyperledger.besu.plugin.services.storage.rocksdb.RocksDBPlugin; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; import org.hyperledger.besu.services.BesuEventsImpl; import org.hyperledger.besu.services.BesuPluginContextImpl; import org.hyperledger.besu.services.BlockchainServiceImpl; @@ -183,6 +185,7 @@ import org.hyperledger.besu.services.SecurityModuleServiceImpl; import org.hyperledger.besu.services.StorageServiceImpl; import org.hyperledger.besu.services.TraceServiceImpl; +import org.hyperledger.besu.services.TransactionSelectionServiceImpl; import org.hyperledger.besu.services.kvstore.InMemoryStoragePlugin; import org.hyperledger.besu.util.InvalidConfigurationException; import org.hyperledger.besu.util.LogConfigurator; @@ -234,6 +237,7 @@ import io.vertx.core.metrics.MetricsOptions; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import picocli.AutoComplete; import picocli.CommandLine; @@ -360,6 +364,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable { @CommandLine.ArgGroup(validate = false, heading = "@|bold P2P Discovery Options|@%n") P2PDiscoveryOptionGroup p2PDiscoveryOptionGroup = new P2PDiscoveryOptionGroup(); + private final TransactionSelectionServiceImpl transactionSelectionServiceImpl; + static class P2PDiscoveryOptionGroup { // Public IP stored to prevent having to research it each time we need it. @@ -1380,7 +1386,8 @@ public BesuCommand( new PermissioningServiceImpl(), new PrivacyPluginServiceImpl(), new PkiBlockCreationConfigurationProvider(), - new RpcEndpointServiceImpl()); + new RpcEndpointServiceImpl(), + new TransactionSelectionServiceImpl()); } /** @@ -1400,6 +1407,7 @@ public BesuCommand( * @param privacyPluginService instance of PrivacyPluginServiceImpl * @param pkiBlockCreationConfigProvider instance of PkiBlockCreationConfigurationProvider * @param rpcEndpointServiceImpl instance of RpcEndpointServiceImpl + * @param transactionSelectionServiceImpl instance of TransactionSelectionServiceImpl */ @VisibleForTesting protected BesuCommand( @@ -1416,7 +1424,8 @@ protected BesuCommand( final PermissioningServiceImpl permissioningService, final PrivacyPluginServiceImpl privacyPluginService, final PkiBlockCreationConfigurationProvider pkiBlockCreationConfigProvider, - final RpcEndpointServiceImpl rpcEndpointServiceImpl) { + final RpcEndpointServiceImpl rpcEndpointServiceImpl, + final TransactionSelectionServiceImpl transactionSelectionServiceImpl) { this.besuComponent = besuComponent; this.logger = besuComponent.getBesuCommandLogger(); this.rlpBlockImporter = rlpBlockImporter; @@ -1434,6 +1443,7 @@ protected BesuCommand( besuPluginContext.addService(BesuConfiguration.class, pluginCommonConfiguration); this.pkiBlockCreationConfigProvider = pkiBlockCreationConfigProvider; this.rpcEndpointServiceImpl = rpcEndpointServiceImpl; + this.transactionSelectionServiceImpl = transactionSelectionServiceImpl; } /** @@ -1614,6 +1624,8 @@ private void preparePlugins() { besuPluginContext.addService(PermissioningService.class, permissioningService); besuPluginContext.addService(PrivacyPluginService.class, privacyPluginService); besuPluginContext.addService(RpcEndpointService.class, rpcEndpointServiceImpl); + besuPluginContext.addService( + TransactionSelectionService.class, transactionSelectionServiceImpl); // register built-in plugins rocksDBPlugin = new RocksDBPlugin(); @@ -1715,6 +1727,9 @@ private void startPlugins() { besuController.getProtocolContext().getWorldStateArchive()), besuController.getProtocolSchedule())); + besuPluginContext.addService( + TransactionSelectionService.class, new TransactionSelectionServiceImpl()); + besuController.getAdditionalPluginServices().appendPluginServices(besuPluginContext); besuPluginContext.startPlugins(); } @@ -2241,12 +2256,15 @@ public BesuController buildController() { */ public BesuControllerBuilder getControllerBuilder() { final KeyValueStorageProvider storageProvider = keyValueStorageProvider(keyValueStorageName); + final Optional transactionSelectorFactory = + getTransactionSelectorFactory(); return controllerBuilderFactory .fromEthNetworkConfig( updateNetworkConfig(network), genesisConfigOverrides, getDefaultSyncModeIfNotSet()) .synchronizerConfiguration(buildSyncConfig()) .ethProtocolConfiguration(unstableEthProtocolOptions.toDomainObject()) .networkConfiguration(unstableNetworkingOptions.toDomainObject()) + .transactionSelectorFactory(transactionSelectorFactory) .dataDirectory(dataDir()) .miningParameters( new MiningParameters.Builder() @@ -2296,6 +2314,13 @@ public BesuControllerBuilder getControllerBuilder() { .chainPruningConfiguration(unstableChainPruningOptions.toDomainObject()); } + @NotNull + private Optional getTransactionSelectorFactory() { + final Optional txSelectionService = + besuPluginContext.getService(TransactionSelectionService.class); + return txSelectionService.isPresent() ? txSelectionService.get().get() : Optional.empty(); + } + private GraphQLConfiguration graphQLConfiguration() { CommandLineUtils.checkOptionDependencies( diff --git a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java index 7e66dda25d4..4b7bff0234f 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java @@ -94,6 +94,7 @@ import org.hyperledger.besu.metrics.ObservableMetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.permissioning.NodeMessagePermissioningProvider; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; import java.io.Closeable; import java.math.BigInteger; @@ -179,6 +180,7 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides private NetworkingConfiguration networkingConfiguration; private Boolean randomPeerPriority; + private Optional transactionSelectorFactory = Optional.empty(); /** the Dagger configured context that can provide dependencies */ protected Optional besuComponent = Optional.empty(); @@ -522,6 +524,18 @@ public BesuControllerBuilder randomPeerPriority(final Boolean randomPeerPriority return this; } + /** + * sets the transactionSelectorFactory in the builder + * + * @param transactionSelectorFactory the optional transaction selector factory + * @return the besu controller builder + */ + public BesuControllerBuilder transactionSelectorFactory( + final Optional transactionSelectorFactory) { + this.transactionSelectorFactory = transactionSelectorFactory; + return this; + } + /** * Build besu controller. * @@ -575,7 +589,11 @@ public BesuController build() { final ProtocolContext protocolContext = createProtocolContext( - blockchain, worldStateArchive, protocolSchedule, this::createConsensusContext); + blockchain, + worldStateArchive, + protocolSchedule, + this::createConsensusContext, + transactionSelectorFactory); validateContext(protocolContext); if (chainPrunerConfiguration.getChainPruningEnabled()) { @@ -991,15 +1009,21 @@ protected EthProtocolManager createEthProtocolManager( * @param worldStateArchive the world state archive * @param protocolSchedule the protocol schedule * @param consensusContextFactory the consensus context factory + * @param transactionSelectorFactory optional transaction selector factory * @return the protocol context */ protected ProtocolContext createProtocolContext( final MutableBlockchain blockchain, final WorldStateArchive worldStateArchive, final ProtocolSchedule protocolSchedule, - final ConsensusContextFactory consensusContextFactory) { + final ConsensusContextFactory consensusContextFactory, + final Optional transactionSelectorFactory) { return ProtocolContext.init( - blockchain, worldStateArchive, protocolSchedule, consensusContextFactory); + blockchain, + worldStateArchive, + protocolSchedule, + consensusContextFactory, + transactionSelectorFactory); } private Optional createSnapProtocolManager( diff --git a/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java index e4d5761dbf1..6d0d02b22f0 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java @@ -62,6 +62,7 @@ import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.metrics.ObservableMetricsSystem; import org.hyperledger.besu.plugin.services.permissioning.NodeMessagePermissioningProvider; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; import java.math.BigInteger; import java.nio.file.Path; @@ -174,9 +175,14 @@ protected ProtocolContext createProtocolContext( final MutableBlockchain blockchain, final WorldStateArchive worldStateArchive, final ProtocolSchedule protocolSchedule, - final ConsensusContextFactory consensusContextFactory) { + final ConsensusContextFactory consensusContextFactory, + final Optional transactionSelectorFactory) { return MigratingProtocolContext.init( - blockchain, worldStateArchive, protocolSchedule, consensusContextFactory); + blockchain, + worldStateArchive, + protocolSchedule, + consensusContextFactory, + transactionSelectorFactory); } @Override diff --git a/besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java index b159992e0bf..9323f3b81d6 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java @@ -60,6 +60,7 @@ import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.metrics.ObservableMetricsSystem; import org.hyperledger.besu.plugin.services.permissioning.NodeMessagePermissioningProvider; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; import java.math.BigInteger; import java.nio.file.Path; @@ -188,10 +189,15 @@ protected ProtocolContext createProtocolContext( final MutableBlockchain blockchain, final WorldStateArchive worldStateArchive, final ProtocolSchedule protocolSchedule, - final ConsensusContextFactory consensusContextFactory) { + final ConsensusContextFactory consensusContextFactory, + final Optional transactionSelectorFactory) { final ProtocolContext protocolContext = super.createProtocolContext( - blockchain, worldStateArchive, protocolSchedule, consensusContextFactory); + blockchain, + worldStateArchive, + protocolSchedule, + consensusContextFactory, + transactionSelectorFactory); transitionProtocolSchedule.setProtocolContext(protocolContext); return protocolContext; } diff --git a/besu/src/main/java/org/hyperledger/besu/services/TransactionSelectionServiceImpl.java b/besu/src/main/java/org/hyperledger/besu/services/TransactionSelectionServiceImpl.java new file mode 100644 index 00000000000..42b1ae88a76 --- /dev/null +++ b/besu/src/main/java/org/hyperledger/besu/services/TransactionSelectionServiceImpl.java @@ -0,0 +1,37 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * 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.services; + +import org.hyperledger.besu.plugin.services.TransactionSelectionService; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; + +import java.util.Optional; + +/** The Transaction Selection service implementation. */ +public class TransactionSelectionServiceImpl implements TransactionSelectionService { + + private Optional factory = Optional.empty(); + + @Override + public Optional get() { + return factory; + } + + @Override + public void registerTransactionSelectorFactory( + final TransactionSelectorFactory transactionSelectorFactory) { + factory = Optional.ofNullable(transactionSelectorFactory); + } +} diff --git a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java index fcce5353adc..b5c31a34dee 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java @@ -79,6 +79,7 @@ import org.hyperledger.besu.services.RpcEndpointServiceImpl; import org.hyperledger.besu.services.SecurityModuleServiceImpl; import org.hyperledger.besu.services.StorageServiceImpl; +import org.hyperledger.besu.services.TransactionSelectionServiceImpl; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import java.io.ByteArrayOutputStream; @@ -237,6 +238,7 @@ public void initMocks() throws Exception { when(mockControllerBuilder.lowerBoundPeers(anyInt())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.maxRemotelyInitiatedPeers(anyInt())) .thenReturn(mockControllerBuilder); + when(mockControllerBuilder.transactionSelectorFactory(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.besuComponent(any(BesuComponent.class))) .thenReturn(mockControllerBuilder); // doReturn used because of generic BesuController @@ -484,7 +486,8 @@ public static class TestBesuCommand extends BesuCommand { new PermissioningServiceImpl(), privacyPluginService, pkiBlockCreationConfigProvider, - rpcEndpointServiceImpl); + rpcEndpointServiceImpl, + new TransactionSelectionServiceImpl()); } @Override diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/CliqueDifficultyCalculatorTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/CliqueDifficultyCalculatorTest.java index 6297ec6b6eb..6f9c5c53e0e 100644 --- a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/CliqueDifficultyCalculatorTest.java +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/CliqueDifficultyCalculatorTest.java @@ -31,6 +31,7 @@ import java.math.BigInteger; import java.util.List; +import java.util.Optional; import com.google.common.collect.Lists; import org.junit.jupiter.api.BeforeEach; @@ -57,7 +58,7 @@ public void setup() { when(validatorProvider.getValidatorsAfterBlock(any())).thenReturn(validatorList); final CliqueContext cliqueContext = new CliqueContext(validatorProvider, null, blockInterface); - cliqueProtocolContext = new ProtocolContext(null, null, cliqueContext); + cliqueProtocolContext = new ProtocolContext(null, null, cliqueContext, Optional.empty()); blockHeaderBuilder = new BlockHeaderTestFixture(); } diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/NodeCanProduceNextBlockTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/NodeCanProduceNextBlockTest.java index 0018f3d9e17..486c1840fc9 100644 --- a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/NodeCanProduceNextBlockTest.java +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/NodeCanProduceNextBlockTest.java @@ -37,6 +37,7 @@ import org.hyperledger.besu.ethereum.core.Util; import java.util.List; +import java.util.Optional; import com.google.common.collect.Lists; import org.junit.jupiter.api.BeforeEach; @@ -79,7 +80,7 @@ public void networkWithOneValidatorIsAllowedToCreateConsecutiveBlocks() { final ValidatorProvider validatorProvider = mock(ValidatorProvider.class); when(validatorProvider.getValidatorsAfterBlock(any())).thenReturn(validatorList); final CliqueContext cliqueContext = new CliqueContext(validatorProvider, null, blockInterface); - cliqueProtocolContext = new ProtocolContext(blockChain, null, cliqueContext); + cliqueProtocolContext = new ProtocolContext(blockChain, null, cliqueContext, Optional.empty()); headerBuilder.number(1).parentHash(genesisBlock.getHash()); final Block block_1 = createEmptyBlock(proposerKeyPair); @@ -103,7 +104,7 @@ public void networkWithTwoValidatorsIsAllowedToProduceBlockIfNotPreviousBlockPro final ValidatorProvider validatorProvider = mock(ValidatorProvider.class); when(validatorProvider.getValidatorsAfterBlock(any())).thenReturn(validatorList); final CliqueContext cliqueContext = new CliqueContext(validatorProvider, null, blockInterface); - cliqueProtocolContext = new ProtocolContext(blockChain, null, cliqueContext); + cliqueProtocolContext = new ProtocolContext(blockChain, null, cliqueContext, Optional.empty()); headerBuilder.number(1).parentHash(genesisBlock.getHash()); final Block block_1 = createEmptyBlock(proposerKeyPair); @@ -136,7 +137,7 @@ public void networkWithTwoValidatorsIsNotAllowedToProduceBlockIfIsPreviousBlockP final ValidatorProvider validatorProvider = mock(ValidatorProvider.class); when(validatorProvider.getValidatorsAfterBlock(any())).thenReturn(validatorList); final CliqueContext cliqueContext = new CliqueContext(validatorProvider, null, blockInterface); - cliqueProtocolContext = new ProtocolContext(blockChain, null, cliqueContext); + cliqueProtocolContext = new ProtocolContext(blockChain, null, cliqueContext, Optional.empty()); headerBuilder.parentHash(genesisBlock.getHash()).number(1); final Block block_1 = createEmptyBlock(proposerKeyPair); @@ -165,7 +166,7 @@ public void withThreeValidatorsMustHaveOneBlockBetweenSignings() { final ValidatorProvider validatorProvider = mock(ValidatorProvider.class); when(validatorProvider.getValidatorsAfterBlock(any())).thenReturn(validatorList); final CliqueContext cliqueContext = new CliqueContext(validatorProvider, null, blockInterface); - cliqueProtocolContext = new ProtocolContext(blockChain, null, cliqueContext); + cliqueProtocolContext = new ProtocolContext(blockChain, null, cliqueContext, Optional.empty()); headerBuilder.parentHash(genesisBlock.getHash()).number(1); final Block block_1 = createEmptyBlock(proposerKeyPair); @@ -209,7 +210,7 @@ public void signerIsValidIfInsufficientBlocksExistInHistory() { final ValidatorProvider validatorProvider = mock(ValidatorProvider.class); when(validatorProvider.getValidatorsAfterBlock(any())).thenReturn(validatorList); final CliqueContext cliqueContext = new CliqueContext(validatorProvider, null, blockInterface); - cliqueProtocolContext = new ProtocolContext(blockChain, null, cliqueContext); + cliqueProtocolContext = new ProtocolContext(blockChain, null, cliqueContext, Optional.empty()); headerBuilder.parentHash(genesisBlock.getHash()).number(1); final Block block_1 = createEmptyBlock(otherNodeKeyPair); @@ -237,7 +238,7 @@ public void exceptionIsThrownIfOnAnOrphanedChain() { final ValidatorProvider validatorProvider = mock(ValidatorProvider.class); when(validatorProvider.getValidatorsAfterBlock(any())).thenReturn(validatorList); final CliqueContext cliqueContext = new CliqueContext(validatorProvider, null, blockInterface); - cliqueProtocolContext = new ProtocolContext(blockChain, null, cliqueContext); + cliqueProtocolContext = new ProtocolContext(blockChain, null, cliqueContext, Optional.empty()); headerBuilder.parentHash(Hash.ZERO).number(3); final BlockHeader parentHeader = @@ -260,7 +261,7 @@ public void nonValidatorIsNotAllowedToCreateABlock() { final ValidatorProvider validatorProvider = mock(ValidatorProvider.class); when(validatorProvider.getValidatorsAfterBlock(any())).thenReturn(validatorList); final CliqueContext cliqueContext = new CliqueContext(validatorProvider, null, blockInterface); - cliqueProtocolContext = new ProtocolContext(blockChain, null, cliqueContext); + cliqueProtocolContext = new ProtocolContext(blockChain, null, cliqueContext, Optional.empty()); headerBuilder.parentHash(Hash.ZERO).number(3); final BlockHeader parentHeader = headerBuilder.buildHeader(); diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java index 8f3709b1605..76741501dc2 100644 --- a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java @@ -105,7 +105,8 @@ public void setup() { final Block genesis = GenesisState.fromConfig(GenesisConfigFile.mainnet(), protocolSchedule).getBlock(); blockchain = createInMemoryBlockchain(genesis); - protocolContext = new ProtocolContext(blockchain, stateArchive, cliqueContext); + protocolContext = + new ProtocolContext(blockchain, stateArchive, cliqueContext, Optional.empty()); epochManager = new EpochManager(10); // Add a block above the genesis diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java index 76caf56f5d9..ab3e45abc59 100644 --- a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java @@ -81,7 +81,7 @@ public void setup() { when(validatorProvider.getValidatorsAfterBlock(any())).thenReturn(validatorList); final CliqueContext cliqueContext = new CliqueContext(validatorProvider, null, blockInterface); - cliqueProtocolContext = new ProtocolContext(null, null, cliqueContext); + cliqueProtocolContext = new ProtocolContext(null, null, cliqueContext, Optional.empty()); blockHeaderBuilder = new BlockHeaderTestFixture(); } diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueDifficultyValidationRuleTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueDifficultyValidationRuleTest.java index 56d511b4be1..7f7daf4eeab 100644 --- a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueDifficultyValidationRuleTest.java +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueDifficultyValidationRuleTest.java @@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.core.Util; import java.util.List; +import java.util.Optional; import com.google.common.collect.Lists; import org.junit.jupiter.api.BeforeEach; @@ -57,7 +58,7 @@ public void setup() { when(validatorProvider.getValidatorsAfterBlock(any())).thenReturn(validatorList); final CliqueContext cliqueContext = new CliqueContext(validatorProvider, null, blockInterface); - cliqueProtocolContext = new ProtocolContext(null, null, cliqueContext); + cliqueProtocolContext = new ProtocolContext(null, null, cliqueContext, Optional.empty()); blockHeaderBuilder = new BlockHeaderTestFixture(); } diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueExtraDataValidationRuleTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueExtraDataValidationRuleTest.java index 04657661776..72043c9cb9a 100644 --- a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueExtraDataValidationRuleTest.java +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueExtraDataValidationRuleTest.java @@ -35,6 +35,7 @@ import org.hyperledger.besu.ethereum.core.Util; import java.util.List; +import java.util.Optional; import com.google.common.collect.Lists; import org.apache.tuweni.bytes.Bytes; @@ -61,7 +62,7 @@ public void setup() { when(validatorProvider.getValidatorsAfterBlock(any())).thenReturn(validatorList); final CliqueContext cliqueContext = new CliqueContext(validatorProvider, null, blockInterface); - cliqueProtocolContext = new ProtocolContext(null, null, cliqueContext); + cliqueProtocolContext = new ProtocolContext(null, null, cliqueContext, Optional.empty()); } @Test diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/MigratingProtocolContext.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/MigratingProtocolContext.java index 95ac7a010ca..e4304a3dbd0 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/MigratingProtocolContext.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/MigratingProtocolContext.java @@ -20,6 +20,9 @@ import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; + +import java.util.Optional; /** The Migrating protocol context. */ public class MigratingProtocolContext extends ProtocolContext { @@ -32,12 +35,14 @@ public class MigratingProtocolContext extends ProtocolContext { * @param blockchain the blockchain * @param worldStateArchive the world state archive * @param consensusContextSchedule the consensus context schedule + * @param transactionSelectorFactory the optional transaction selector factory */ public MigratingProtocolContext( final MutableBlockchain blockchain, final WorldStateArchive worldStateArchive, - final ForksSchedule consensusContextSchedule) { - super(blockchain, worldStateArchive, null); + final ForksSchedule consensusContextSchedule, + final Optional transactionSelectorFactory) { + super(blockchain, worldStateArchive, null, transactionSelectorFactory); this.consensusContextSchedule = consensusContextSchedule; } @@ -48,18 +53,23 @@ public MigratingProtocolContext( * @param worldStateArchive the world state archive * @param protocolSchedule the protocol schedule * @param consensusContextFactory the consensus context factory + * @param transactionSelectorFactory the optional transaction selector factory * @return the protocol context */ public static ProtocolContext init( final MutableBlockchain blockchain, final WorldStateArchive worldStateArchive, final ProtocolSchedule protocolSchedule, - final ConsensusContextFactory consensusContextFactory) { + final ConsensusContextFactory consensusContextFactory, + final Optional transactionSelectorFactory) { final ConsensusContext consensusContext = consensusContextFactory.create(blockchain, worldStateArchive, protocolSchedule); final MigratingContext migratingContext = consensusContext.as(MigratingContext.class); return new MigratingProtocolContext( - blockchain, worldStateArchive, migratingContext.getConsensusContextSchedule()); + blockchain, + worldStateArchive, + migratingContext.getConsensusContextSchedule(), + transactionSelectorFactory); } @Override diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/MigratingProtocolContextTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/MigratingProtocolContextTest.java index 84fc82da67c..21ba7638c40 100644 --- a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/MigratingProtocolContextTest.java +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/MigratingProtocolContextTest.java @@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import java.util.List; +import java.util.Optional; import org.junit.Test; import org.mockito.Mockito; @@ -43,7 +44,8 @@ public void returnsContextForSpecificChainHeight() { final ForksSchedule contextSchedule = new ForksSchedule<>(List.of(new ForkSpec<>(0L, context1), new ForkSpec<>(10L, context2))); final MigratingProtocolContext migratingProtocolContext = - new MigratingProtocolContext(blockchain, worldStateArchive, contextSchedule); + new MigratingProtocolContext( + blockchain, worldStateArchive, contextSchedule, Optional.empty()); assertThat(migratingProtocolContext.getConsensusContext(ConsensusContext.class)) .isSameAs(context1); diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/headervalidationrules/BftCoinbaseValidationRuleTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/headervalidationrules/BftCoinbaseValidationRuleTest.java index d268c514467..fcbea4954d8 100644 --- a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/headervalidationrules/BftCoinbaseValidationRuleTest.java +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/headervalidationrules/BftCoinbaseValidationRuleTest.java @@ -27,6 +27,7 @@ import org.hyperledger.besu.ethereum.core.Util; import java.util.List; +import java.util.Optional; import com.google.common.collect.Lists; import org.junit.Test; @@ -50,7 +51,7 @@ public void proposerInValidatorListPassesValidation() { final List
validators = Lists.newArrayList(proposerAddress); final ProtocolContext context = - new ProtocolContext(null, null, setupContextWithValidators(validators)); + new ProtocolContext(null, null, setupContextWithValidators(validators), Optional.empty()); final BftCoinbaseValidationRule coinbaseValidationRule = new BftCoinbaseValidationRule(); @@ -70,7 +71,7 @@ public void proposerNotInValidatorListFailsValidation() { final List
validators = Lists.newArrayList(otherValidatorNodeAddress); final ProtocolContext context = - new ProtocolContext(null, null, setupContextWithValidators(validators)); + new ProtocolContext(null, null, setupContextWithValidators(validators), Optional.empty()); final BftCoinbaseValidationRule coinbaseValidationRule = new BftCoinbaseValidationRule(); diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/headervalidationrules/BftCommitSealsValidationRuleTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/headervalidationrules/BftCommitSealsValidationRuleTest.java index cae209bd662..453b28a601c 100644 --- a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/headervalidationrules/BftCommitSealsValidationRuleTest.java +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/headervalidationrules/BftCommitSealsValidationRuleTest.java @@ -33,6 +33,7 @@ import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -57,7 +58,7 @@ public void correctlyConstructedHeaderPassesValidation() { .collect(Collectors.toList()); final BftContext bftContext = setupContextWithValidators(committerAddresses); - final ProtocolContext context = new ProtocolContext(null, null, bftContext); + final ProtocolContext context = new ProtocolContext(null, null, bftContext, Optional.empty()); when(bftContext.getBlockInterface().getCommitters(any())).thenReturn(committerAddresses); assertThat(commitSealsValidationRule.validate(blockHeader, null, context)).isTrue(); @@ -71,7 +72,7 @@ public void insufficientCommitSealsFailsValidation() { final List
validators = singletonList(committerAddress); final BftContext bftContext = setupContextWithValidators(validators); - final ProtocolContext context = new ProtocolContext(null, null, bftContext); + final ProtocolContext context = new ProtocolContext(null, null, bftContext, Optional.empty()); when(bftContext.getBlockInterface().getCommitters(any())).thenReturn(emptyList()); assertThat(commitSealsValidationRule.validate(blockHeader, null, context)).isFalse(); @@ -88,7 +89,7 @@ public void committerNotInValidatorListFailsValidation() { final NodeKey nonValidatorNodeKey = NodeKeyUtils.generate(); final BftContext bftContext = setupContextWithValidators(validators); - final ProtocolContext context = new ProtocolContext(null, null, bftContext); + final ProtocolContext context = new ProtocolContext(null, null, bftContext, Optional.empty()); when(bftContext.getBlockInterface().getCommitters(any())) .thenReturn(singletonList(Util.publicKeyToAddress(nonValidatorNodeKey.getPublicKey()))); @@ -135,7 +136,7 @@ public void headerContainsDuplicateSealsFailsValidation() { final List
validators = singletonList(committerAddress); final BftContext bftContext = setupContextWithValidators(validators); - final ProtocolContext context = new ProtocolContext(null, null, bftContext); + final ProtocolContext context = new ProtocolContext(null, null, bftContext, Optional.empty()); when(bftContext.getBlockInterface().getCommitters(any())) .thenReturn(List.of(committerAddress, committerAddress)); @@ -154,7 +155,7 @@ private boolean subExecution(final int validatorCount, final int committerCount) Collections.sort(validators); final BftContext bftContext = setupContextWithValidators(validators); - final ProtocolContext context = new ProtocolContext(null, null, bftContext); + final ProtocolContext context = new ProtocolContext(null, null, bftContext, Optional.empty()); when(bftContext.getBlockInterface().getCommitters(any())) .thenReturn(validators.subList(0, committerCount)); diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/headervalidationrules/BftValidatorsValidationRuleTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/headervalidationrules/BftValidatorsValidationRuleTest.java index 048faa3ad88..00b994295ca 100644 --- a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/headervalidationrules/BftValidatorsValidationRuleTest.java +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/headervalidationrules/BftValidatorsValidationRuleTest.java @@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader; import java.util.List; +import java.util.Optional; import com.google.common.collect.Lists; import org.junit.Test; @@ -44,7 +45,8 @@ public void correctlyConstructedHeaderPassesValidation() { AddressHelpers.ofValue(1), AddressHelpers.ofValue(2), AddressHelpers.ofValue(3)); final ProtocolContext context = - new ProtocolContext(null, null, setupContextWithBftExtraData(validators, bftExtraData)); + new ProtocolContext( + null, null, setupContextWithBftExtraData(validators, bftExtraData), Optional.empty()); when(bftExtraData.getValidators()).thenReturn(validators); assertThat(validatorsValidationRule.validate(blockHeader, null, context)).isTrue(); @@ -58,7 +60,8 @@ public void validatorsInNonAscendingOrderFailValidation() { AddressHelpers.ofValue(1), AddressHelpers.ofValue(2), AddressHelpers.ofValue(3)); final ProtocolContext context = - new ProtocolContext(null, null, setupContextWithBftExtraData(validators, bftExtraData)); + new ProtocolContext( + null, null, setupContextWithBftExtraData(validators, bftExtraData), Optional.empty()); when(bftExtraData.getValidators()).thenReturn(Lists.reverse(validators)); assertThat(validatorsValidationRule.validate(blockHeader, null, context)).isFalse(); @@ -76,7 +79,10 @@ public void mismatchingReportedValidatorsVsLocallyStoredListFailsValidation() { final ProtocolContext context = new ProtocolContext( - null, null, setupContextWithBftExtraData(storedValidators, bftExtraData)); + null, + null, + setupContextWithBftExtraData(storedValidators, bftExtraData), + Optional.empty()); when(bftExtraData.getValidators()).thenReturn(Lists.reverse(reportedValidators)); assertThat(validatorsValidationRule.validate(blockHeader, null, context)).isFalse(); diff --git a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/headervalidationrules/BftVanityDataValidationRuleTest.java b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/headervalidationrules/BftVanityDataValidationRuleTest.java index 548084f81cb..cd148f8787a 100644 --- a/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/headervalidationrules/BftVanityDataValidationRuleTest.java +++ b/consensus/common/src/test/java/org/hyperledger/besu/consensus/common/bft/headervalidationrules/BftVanityDataValidationRuleTest.java @@ -24,6 +24,8 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.core.BlockHeader; +import java.util.Optional; + import org.apache.tuweni.bytes.Bytes; import org.junit.Test; @@ -44,7 +46,8 @@ public boolean headerWithVanityDataOfSize(final int extraDataSize) { new BftExtraData(Bytes.wrap(new byte[extraDataSize]), emptyList(), empty(), 0, emptyList()); final ProtocolContext context = - new ProtocolContext(null, null, setupContextWithBftExtraData(emptyList(), extraData)); + new ProtocolContext( + null, null, setupContextWithBftExtraData(emptyList(), extraData), Optional.empty()); return validationRule.validate(blockHeader, null, context); } } diff --git a/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java b/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java index c0b85a434bf..7d7778410cb 100644 --- a/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java +++ b/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java @@ -328,7 +328,8 @@ private static ControllerAndState createControllerAndFinalState( new ProtocolContext( blockChain, worldStateArchive, - new BftContext(validatorProvider, epochManager, blockInterface)); + new BftContext(validatorProvider, epochManager, blockInterface), + Optional.empty()); final GasPricePendingTransactionsSorter pendingTransactions = new GasPricePendingTransactionsSorter( diff --git a/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/tests/round/IbftRoundIntegrationTest.java b/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/tests/round/IbftRoundIntegrationTest.java index 88669b91504..ff9c47ab38c 100644 --- a/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/tests/round/IbftRoundIntegrationTest.java +++ b/consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/tests/round/IbftRoundIntegrationTest.java @@ -118,7 +118,8 @@ public void setup() { new ProtocolContext( blockChain, worldStateArchive, - setupContextWithBftExtraDataEncoder(emptyList(), bftExtraDataEncoder)); + setupContextWithBftExtraDataEncoder(emptyList(), bftExtraDataEncoder), + Optional.empty()); } @Test diff --git a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/IbftBlockHeaderValidationRulesetFactoryTest.java b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/IbftBlockHeaderValidationRulesetFactoryTest.java index 6c1bf8ed5db..40ad127eb5b 100644 --- a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/IbftBlockHeaderValidationRulesetFactoryTest.java +++ b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/IbftBlockHeaderValidationRulesetFactoryTest.java @@ -48,7 +48,10 @@ public class IbftBlockHeaderValidationRulesetFactoryTest { private ProtocolContext protocolContext(final Collection
validators) { return new ProtocolContext( - null, null, setupContextWithBftExtraDataEncoder(validators, new IbftExtraDataCodec())); + null, + null, + setupContextWithBftExtraDataEncoder(validators, new IbftExtraDataCodec()), + Optional.empty()); } @Test diff --git a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/IbftProtocolScheduleTest.java b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/IbftProtocolScheduleTest.java index 562d441d4d8..1ca3c0269a8 100644 --- a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/IbftProtocolScheduleTest.java +++ b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/IbftProtocolScheduleTest.java @@ -47,6 +47,7 @@ import java.math.BigInteger; import java.util.Collection; import java.util.List; +import java.util.Optional; import org.junit.Before; import org.junit.Test; @@ -119,6 +120,7 @@ private ProtocolContext protocolContext(final Collection
validators) { return new ProtocolContext( null, null, - setupContextWithBftExtraDataEncoder(BftContext.class, validators, bftExtraDataCodec)); + setupContextWithBftExtraDataEncoder(BftContext.class, validators, bftExtraDataCodec), + Optional.empty()); } } diff --git a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java index 7ebfda0294f..c83f747f701 100644 --- a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java +++ b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/BftBlockCreatorTest.java @@ -113,7 +113,8 @@ public BlockHeaderValidator.Builder createBlockHeaderRuleset( new ProtocolContext( blockchain, createInMemoryWorldStateArchive(), - setupContextWithBftExtraDataEncoder(initialValidatorList, bftExtraDataEncoder)); + setupContextWithBftExtraDataEncoder(initialValidatorList, bftExtraDataEncoder), + Optional.empty()); final GasPricePendingTransactionsSorter pendingTransactions = new GasPricePendingTransactionsSorter( diff --git a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftBlockHeightManagerTest.java b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftBlockHeightManagerTest.java index 1fd93d61d97..54142aa8b4b 100644 --- a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftBlockHeightManagerTest.java +++ b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftBlockHeightManagerTest.java @@ -151,7 +151,8 @@ public void setup() { .thenReturn(futureRoundProposalMessageValidator); when(messageValidatorFactory.createMessageValidator(any(), any())).thenReturn(messageValidator); - protocolContext = new ProtocolContext(null, null, setupContextWithValidators(validators)); + protocolContext = + new ProtocolContext(null, null, setupContextWithValidators(validators), Optional.empty()); // Ensure the created IbftRound has the valid ConsensusRoundIdentifier; when(roundFactory.createNewRound(any(), anyInt())) diff --git a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftRoundTest.java b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftRoundTest.java index 8023ff0a822..af5156e531e 100644 --- a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftRoundTest.java +++ b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/IbftRoundTest.java @@ -106,7 +106,8 @@ public void setup() { new ProtocolContext( blockChain, worldStateArchive, - setupContextWithBftExtraDataEncoder(emptyList(), new IbftExtraDataCodec())); + setupContextWithBftExtraDataEncoder(emptyList(), new IbftExtraDataCodec()), + Optional.empty()); when(messageValidator.validateProposal(any())).thenReturn(true); when(messageValidator.validatePrepare(any())).thenReturn(true); diff --git a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/validation/MessageValidatorTest.java b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/validation/MessageValidatorTest.java index 909d30b73cb..9296f0419c8 100644 --- a/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/validation/MessageValidatorTest.java +++ b/consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/validation/MessageValidatorTest.java @@ -95,7 +95,10 @@ public void setup() { protocolContext = new ProtocolContext( - mock(MutableBlockchain.class), mock(WorldStateArchive.class), mockBftCtx); + mock(MutableBlockchain.class), + mock(WorldStateArchive.class), + mockBftCtx, + Optional.empty()); when(blockValidator.validateAndProcessBlock(any(), any(), any(), any())) .thenReturn(new BlockProcessingResult(Optional.empty())); diff --git a/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinatorTest.java b/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinatorTest.java index 932b8130760..d246711a2af 100644 --- a/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinatorTest.java +++ b/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinatorTest.java @@ -183,7 +183,8 @@ public void setUp() { .when(protocolSchedule) .getByBlockHeader(any(BlockHeader.class)); - protocolContext = new ProtocolContext(blockchain, worldStateArchive, mergeContext); + protocolContext = + new ProtocolContext(blockchain, worldStateArchive, mergeContext, Optional.empty()); var mutable = worldStateArchive.getMutable(); genesisState.writeStateTo(mutable); mutable.persist(null); diff --git a/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeReorgTest.java b/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeReorgTest.java index de75bffd145..e4a1d7a4144 100644 --- a/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeReorgTest.java +++ b/consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeReorgTest.java @@ -45,6 +45,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import org.junit.Before; @@ -69,7 +70,7 @@ public class MergeReorgTest implements MergeGenesisConfigHelper { private final MutableBlockchain blockchain = createInMemoryBlockchain(genesisState.getBlock()); private final ProtocolContext protocolContext = - new ProtocolContext(blockchain, worldStateArchive, mergeContext); + new ProtocolContext(blockchain, worldStateArchive, mergeContext, Optional.empty()); private final Address coinbase = genesisAllocations(getPowGenesisConfigFile()).findFirst().get(); private final BlockHeaderTestFixture headerGenerator = new BlockHeaderTestFixture(); diff --git a/consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java b/consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java index 0a6337de71f..76c429d2124 100644 --- a/consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java +++ b/consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/support/TestContextBuilder.java @@ -456,7 +456,8 @@ private static ControllerAndState createControllerAndFinalState( new ProtocolContext( blockChain, worldStateArchive, - new QbftContext(validatorProvider, epochManager, blockInterface, Optional.empty())); + new QbftContext(validatorProvider, epochManager, blockInterface, Optional.empty()), + Optional.empty()); final GasPricePendingTransactionsSorter pendingTransactions = new GasPricePendingTransactionsSorter( diff --git a/consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/test/round/QbftRoundIntegrationTest.java b/consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/test/round/QbftRoundIntegrationTest.java index 549717c59dc..cd3af6ac15b 100644 --- a/consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/test/round/QbftRoundIntegrationTest.java +++ b/consensus/qbft/src/integration-test/java/org/hyperledger/besu/consensus/qbft/test/round/QbftRoundIntegrationTest.java @@ -55,6 +55,7 @@ import org.hyperledger.besu.util.Subscribers; import java.math.BigInteger; +import java.util.Optional; import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.BeforeEach; @@ -121,7 +122,8 @@ public void setup() { blockChain, worldStateArchive, setupContextWithBftExtraDataEncoder( - QbftContext.class, emptyList(), qbftExtraDataEncoder)); + QbftContext.class, emptyList(), qbftExtraDataEncoder), + Optional.empty()); } @Test diff --git a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftBlockHeaderValidationRulesetFactoryTest.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftBlockHeaderValidationRulesetFactoryTest.java index 8c310d9700d..d17dabddf74 100644 --- a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftBlockHeaderValidationRulesetFactoryTest.java +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftBlockHeaderValidationRulesetFactoryTest.java @@ -45,7 +45,8 @@ private ProtocolContext protocolContext(final Collection
validators) { null, null, setupContextWithBftExtraDataEncoder( - QbftContext.class, validators, new QbftExtraDataCodec())); + QbftContext.class, validators, new QbftExtraDataCodec()), + Optional.empty()); } @Test diff --git a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftProtocolScheduleTest.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftProtocolScheduleTest.java index e090b0e6501..704cc67f1fe 100644 --- a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftProtocolScheduleTest.java +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/QbftProtocolScheduleTest.java @@ -57,7 +57,8 @@ private ProtocolContext protocolContext(final Collection
validators) { null, null, setupContextWithBftExtraDataEncoder( - QbftContext.class, validators, new QbftExtraDataCodec())); + QbftContext.class, validators, new QbftExtraDataCodec()), + Optional.empty()); } @Test diff --git a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/QbftValidatorsValidationRuleTest.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/QbftValidatorsValidationRuleTest.java index 105e94af024..4e33d202c66 100644 --- a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/QbftValidatorsValidationRuleTest.java +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/headervalidationrules/QbftValidatorsValidationRuleTest.java @@ -46,7 +46,8 @@ public void validationPassesIfValidatorsAndVoteAreEmpty() { new ProtocolContext( null, null, - setupContextWithBftExtraData(QbftContext.class, Collections.emptyList(), bftExtraData)); + setupContextWithBftExtraData(QbftContext.class, Collections.emptyList(), bftExtraData), + Optional.empty()); when(bftExtraData.getValidators()).thenReturn(Collections.emptyList()); when(bftExtraData.getVote()).thenReturn(Optional.empty()); assertThat(qbftValidatorsValidationRule.validate(blockHeader, null, context)).isTrue(); @@ -62,7 +63,10 @@ public void validationIsDelegatedWhenConstructorFlagIsFalse() { final ProtocolContext context = new ProtocolContext( - null, null, setupContextWithBftExtraData(QbftContext.class, validators, bftExtraData)); + null, + null, + setupContextWithBftExtraData(QbftContext.class, validators, bftExtraData), + Optional.empty()); when(bftExtraData.getValidators()).thenReturn(validators); assertThat(qbftValidatorsValidationRule.validate(blockHeader, null, context)).isTrue(); } @@ -77,7 +81,10 @@ public void validationFailsIfValidatorsAreNotEmpty() { final ProtocolContext context = new ProtocolContext( - null, null, setupContextWithBftExtraData(QbftContext.class, validators, bftExtraData)); + null, + null, + setupContextWithBftExtraData(QbftContext.class, validators, bftExtraData), + Optional.empty()); when(bftExtraData.getValidators()).thenReturn(validators); assertThat(qbftValidatorsValidationRule.validate(blockHeader, null, context)).isFalse(); } @@ -90,7 +97,8 @@ public void validationFailsIfVoteIsPresent() { new ProtocolContext( null, null, - setupContextWithBftExtraData(QbftContext.class, Collections.emptyList(), bftExtraData)); + setupContextWithBftExtraData(QbftContext.class, Collections.emptyList(), bftExtraData), + Optional.empty()); when(bftExtraData.getValidators()).thenReturn(Collections.emptyList()); when(bftExtraData.getVote()).thenReturn(Optional.of(mock(Vote.class))); assertThat(qbftValidatorsValidationRule.validate(blockHeader, null, context)).isFalse(); diff --git a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftBlockHeightManagerTest.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftBlockHeightManagerTest.java index daf083c8fbe..12fb5cc486e 100644 --- a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftBlockHeightManagerTest.java +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftBlockHeightManagerTest.java @@ -157,7 +157,8 @@ public void setup() { null, null, setupContextWithBftExtraDataEncoder( - QbftContext.class, validators, new QbftExtraDataCodec())); + QbftContext.class, validators, new QbftExtraDataCodec()), + Optional.empty()); // Ensure the created QbftRound has the valid ConsensusRoundIdentifier; when(roundFactory.createNewRound(any(), anyInt())) diff --git a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftRoundTest.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftRoundTest.java index 3459e9110c9..3dec991276b 100644 --- a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftRoundTest.java +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/statemachine/QbftRoundTest.java @@ -114,7 +114,8 @@ public void setup() { blockChain, worldStateArchive, setupContextWithBftExtraDataEncoder( - QbftContext.class, emptyList(), new QbftExtraDataCodec())); + QbftContext.class, emptyList(), new QbftExtraDataCodec()), + Optional.empty()); when(messageValidator.validateProposal(any())).thenReturn(true); when(messageValidator.validatePrepare(any())).thenReturn(true); diff --git a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/validation/ProposalPayloadValidatorTest.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/validation/ProposalPayloadValidatorTest.java index 5e117cc6cf3..9185dc11f8d 100644 --- a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/validation/ProposalPayloadValidatorTest.java +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/validation/ProposalPayloadValidatorTest.java @@ -86,7 +86,8 @@ public void setup() { new ProtocolContext( blockChain, worldStateArchive, - setupContextWithBftExtraDataEncoder(QbftContext.class, emptyList(), bftExtraDataCodec)); + setupContextWithBftExtraDataEncoder(QbftContext.class, emptyList(), bftExtraDataCodec), + Optional.empty()); } @Test @@ -239,7 +240,7 @@ public void validationForCmsFailsWhenCmsFailsValidation() { setupContextWithBftExtraDataEncoder(QbftContext.class, emptyList(), pkiQbftExtraDataCodec); final Bytes cms = Bytes.fromHexStringLenient("0x1"); final ProtocolContext protocolContext = - new ProtocolContext(blockChain, worldStateArchive, qbftContext); + new ProtocolContext(blockChain, worldStateArchive, qbftContext, Optional.empty()); final ProposalPayloadValidator payloadValidator = new ProposalPayloadValidator( @@ -274,7 +275,7 @@ public void validationForCmsPassesWhenCmsIsValid() { setupContextWithBftExtraDataEncoder(QbftContext.class, emptyList(), pkiQbftExtraDataCodec); final Bytes cms = Bytes.fromHexStringLenient("0x1"); final ProtocolContext protocolContext = - new ProtocolContext(blockChain, worldStateArchive, qbftContext); + new ProtocolContext(blockChain, worldStateArchive, qbftContext, Optional.empty()); final ProposalPayloadValidator payloadValidator = new ProposalPayloadValidator( diff --git a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/validation/ProposalValidatorTest.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/validation/ProposalValidatorTest.java index e173208e7f2..f2f984473ff 100644 --- a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/validation/ProposalValidatorTest.java +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/validation/ProposalValidatorTest.java @@ -97,7 +97,8 @@ public void setup() { blockChain, worldStateArchive, setupContextWithBftExtraDataEncoder( - QbftContext.class, emptyList(), bftExtraDataEncoder)); + QbftContext.class, emptyList(), bftExtraDataEncoder), + Optional.empty()); // typically tests require the blockValidation to be successful when(blockValidator.validateAndProcessBlock( diff --git a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/validation/RoundChangeMessageValidatorTest.java b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/validation/RoundChangeMessageValidatorTest.java index fe000758a40..9f88c0972e4 100644 --- a/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/validation/RoundChangeMessageValidatorTest.java +++ b/consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/validation/RoundChangeMessageValidatorTest.java @@ -81,7 +81,8 @@ public void setup() { blockChain, worldStateArchive, setupContextWithBftExtraDataEncoder( - QbftContext.class, emptyList(), bftExtraDataEncoder)); + QbftContext.class, emptyList(), bftExtraDataEncoder), + Optional.empty()); } @Test diff --git a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java index 5df625a4a82..2a4021c7e96 100644 --- a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java +++ b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcTestMethodsFactory.java @@ -76,7 +76,7 @@ public JsonRpcTestMethodsFactory(final BlockchainImporter importer) { this.blockchain = createInMemoryBlockchain(importer.getGenesisBlock()); this.stateArchive = createInMemoryWorldStateArchive(); this.importer.getGenesisState().writeStateTo(stateArchive.getMutable()); - this.context = new ProtocolContext(blockchain, stateArchive, null); + this.context = new ProtocolContext(blockchain, stateArchive, null, Optional.empty()); final ProtocolSchedule protocolSchedule = importer.getProtocolSchedule(); this.synchronizer = mock(Synchronizer.class); diff --git a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthGetBlockByNumberLatestDesyncIntegrationTest.java b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthGetBlockByNumberLatestDesyncIntegrationTest.java index b9293d25de0..6d723844a2d 100644 --- a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthGetBlockByNumberLatestDesyncIntegrationTest.java +++ b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthGetBlockByNumberLatestDesyncIntegrationTest.java @@ -67,7 +67,7 @@ public static void setUpOnce() throws Exception { InMemoryKeyValueStorageProvider.createInMemoryBlockchain(importer.getGenesisBlock()); WorldStateArchive state = InMemoryKeyValueStorageProvider.createInMemoryWorldStateArchive(); importer.getGenesisState().writeStateTo(state.getMutable()); - ProtocolContext context = new ProtocolContext(chain, state, null); + ProtocolContext context = new ProtocolContext(chain, state, null, Optional.empty()); for (final Block block : importer.getBlocks()) { final ProtocolSchedule protocolSchedule = importer.getProtocolSchedule(); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/AbstractEthGraphQLHttpServiceTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/AbstractEthGraphQLHttpServiceTest.java index cb8c253ad3d..bd6f39695d8 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/AbstractEthGraphQLHttpServiceTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/AbstractEthGraphQLHttpServiceTest.java @@ -156,7 +156,7 @@ public void setupTest() throws Exception { final MutableBlockchain blockchain = InMemoryKeyValueStorageProvider.createInMemoryBlockchain(GENESIS_BLOCK); - context = new ProtocolContext(blockchain, stateArchive, null); + context = new ProtocolContext(blockchain, stateArchive, null, Optional.empty()); final BlockchainQueries blockchainQueries = new BlockchainQueries( context.getBlockchain(), diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java index d5e693220a9..0f818716e54 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java @@ -304,7 +304,8 @@ private BlockTransactionSelector.TransactionSelectionResults selectTransactions( dataGasPrice, protocolSpec.getFeeMarket(), protocolSpec.getGasCalculator(), - protocolSpec.getGasLimitCalculator()); + protocolSpec.getGasLimitCalculator(), + protocolContext.getTransactionSelectorFactory()); if (transactions.isPresent()) { return selector.evaluateTransactions(transactions.get()); diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockTransactionSelector.java index 9cc17b601d8..83dd16d5119 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockTransactionSelector.java @@ -23,7 +23,6 @@ import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions; -import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions.TransactionSelectionResult; import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor; import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor; import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; @@ -35,13 +34,17 @@ import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.worldstate.WorldUpdater; +import org.hyperledger.besu.plugin.data.TransactionSelectionResult; import org.hyperledger.besu.plugin.data.TransactionType; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelector; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; import java.util.ArrayList; import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.CancellationException; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -71,6 +74,7 @@ * not cleared between executions of buildTransactionListForBlock(). */ public class BlockTransactionSelector { + public static class TransactionValidationResult { private final Transaction transaction; private final ValidationResult validationResult; @@ -218,8 +222,9 @@ public String toTraceLog() { private final FeeMarket feeMarket; private final GasCalculator gasCalculator; private final GasLimitCalculator gasLimitCalculator; + private final TransactionSelector transactionSelector; - private final TransactionSelectionResults transactionSelectionResult = + private final TransactionSelectionResults transactionSelectionResults = new TransactionSelectionResults(); public BlockTransactionSelector( @@ -236,7 +241,8 @@ public BlockTransactionSelector( final Wei dataGasPrice, final FeeMarket feeMarket, final GasCalculator gasCalculator, - final GasLimitCalculator gasLimitCalculator) { + final GasLimitCalculator gasLimitCalculator, + final Optional transactionSelectorFactory) { this.transactionProcessor = transactionProcessor; this.blockchain = blockchain; this.worldState = worldState; @@ -251,6 +257,10 @@ public BlockTransactionSelector( this.feeMarket = feeMarket; this.gasCalculator = gasCalculator; this.gasLimitCalculator = gasLimitCalculator; + this.transactionSelector = + transactionSelectorFactory.isPresent() + ? transactionSelectorFactory.get().create() + : new TransactionSelector() {}; } /* @@ -268,9 +278,9 @@ public TransactionSelectionResults buildTransactionListForBlock() { pendingTransaction -> evaluateTransaction(pendingTransaction, false)); LOG.atTrace() .setMessage("Transaction selection result result {}") - .addArgument(transactionSelectionResult::toTraceLog) + .addArgument(transactionSelectionResults::toTraceLog) .log(); - return transactionSelectionResult; + return transactionSelectionResults; } /** @@ -281,7 +291,7 @@ public TransactionSelectionResults buildTransactionListForBlock() { */ public TransactionSelectionResults evaluateTransactions(final List transactions) { transactions.forEach(transaction -> evaluateTransaction(transaction, true)); - return transactionSelectionResult; + return transactionSelectionResults; } /* @@ -336,22 +346,43 @@ private TransactionSelectionResult evaluateTransaction( dataGasPrice); if (!effectiveResult.isInvalid()) { - worldStateUpdater.commit(); - LOG.atTrace() - .setMessage("Selected {} for block creation") - .addArgument(transaction::toTraceLog) - .log(); - updateTransactionResultTracking(transaction, effectiveResult); + + final long gasUsedByTransaction = + transaction.getGasLimit() - effectiveResult.getGasRemaining(); + + final long cumulativeGasUsed = + transactionSelectionResults.getCumulativeGasUsed() + gasUsedByTransaction; + + final TransactionReceipt receipt = + transactionReceiptFactory.create( + transaction.getType(), effectiveResult, worldState, cumulativeGasUsed); + + final TransactionSelectionResult transactionSelectionResult = + transactionSelector.selectTransaction(transaction, receipt); + + if (transactionSelectionResult == TransactionSelectionResult.CONTINUE) { + final long dataGasUsed = gasCalculator.dataGasCost(transaction.getBlobCount()); + + transactionSelectionResults.update(transaction, receipt, gasUsedByTransaction, dataGasUsed); + + worldStateUpdater.commit(); + LOG.atTrace() + .setMessage("Selected {} for block creation") + .addArgument(transaction::toTraceLog) + .log(); + } + + return transactionSelectionResult; } else { + final boolean isIncorrectNonce = isIncorrectNonce(effectiveResult.getValidationResult()); if (!isIncorrectNonce || reportFutureNonceTransactionsAsInvalid) { - transactionSelectionResult.updateWithInvalidTransaction( + transactionSelectionResults.updateWithInvalidTransaction( transaction, effectiveResult.getValidationResult()); } return transactionSelectionResultForInvalidResult( transaction, effectiveResult.getValidationResult()); } - return TransactionSelectionResult.CONTINUE; } private boolean transactionDataPriceBelowMin(final Transaction transaction) { @@ -418,28 +449,6 @@ private boolean isTransientValidationError(final TransactionInvalidReason invali || invalidReason.equals(TransactionInvalidReason.NONCE_TOO_HIGH); } - /* - Responsible for updating the state maintained between transaction validation (i.e. receipts, - cumulative gas, world state root hash.). - */ - private void updateTransactionResultTracking( - final Transaction transaction, final TransactionProcessingResult result) { - - final long gasUsedByTransaction = transaction.getGasLimit() - result.getGasRemaining(); - - final long cumulativeGasUsed = - transactionSelectionResult.getCumulativeGasUsed() + gasUsedByTransaction; - - final long dataGasUsed = gasCalculator.dataGasCost(transaction.getBlobCount()); - - transactionSelectionResult.update( - transaction, - transactionReceiptFactory.create( - transaction.getType(), result, worldState, cumulativeGasUsed), - gasUsedByTransaction, - dataGasUsed); - } - private boolean isIncorrectNonce(final ValidationResult result) { return result.getInvalidReason().equals(TransactionInvalidReason.NONCE_TOO_HIGH); } @@ -449,17 +458,17 @@ private boolean transactionTooLargeForBlock(final Transaction transaction) { if (dataGasUsed > gasLimitCalculator.currentDataGasLimit() - - transactionSelectionResult.getCumulativeDataGasUsed()) { + - transactionSelectionResults.getCumulativeDataGasUsed()) { return true; } return transaction.getGasLimit() + dataGasUsed - > processableBlockHeader.getGasLimit() - transactionSelectionResult.getCumulativeGasUsed(); + > processableBlockHeader.getGasLimit() - transactionSelectionResults.getCumulativeGasUsed(); } private boolean blockOccupancyAboveThreshold() { final double gasAvailable = processableBlockHeader.getGasLimit(); - final double gasUsed = transactionSelectionResult.getCumulativeGasUsed(); + final double gasUsed = transactionSelectionResults.getCumulativeGasUsed(); final double occupancyRatio = gasUsed / gasAvailable; LOG.trace( "Min block occupancy ratio {}, gas used {}, available {}, used/available {}", diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java index b52cdab9a8e..dbc4d1bb930 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java @@ -56,8 +56,10 @@ import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.worldstate.WorldState; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; +import org.hyperledger.besu.plugin.data.TransactionSelectionResult; import org.hyperledger.besu.plugin.data.TransactionType; import org.hyperledger.besu.plugin.services.MetricsSystem; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelector; import org.hyperledger.besu.testutil.TestClock; import java.math.BigInteger; @@ -80,6 +82,15 @@ public abstract class AbstractBlockTransactionSelectorTest { protected static final KeyPair keyPair = SignatureAlgorithmFactory.getInstance().generateKeyPair(); + public static final TransactionSelector DO_NOTHING_TRANSACTION_SELECTOR = + new TransactionSelector() { + @Override + public TransactionSelectionResult selectTransaction( + final org.hyperledger.besu.plugin.data.Transaction transaction, + final org.hyperledger.besu.plugin.data.TransactionReceipt receipt) { + return TransactionSelectionResult.CONTINUE; + } + }; protected final MetricsSystem metricsSystem = new NoOpMetricsSystem(); protected final Blockchain blockchain = new ReferenceTestBlockchain(); @@ -262,7 +273,8 @@ public void useSingleGasSpaceForAllTransactions() { Wei.ZERO, FeeMarket.london(0L), new LondonGasCalculator(), - GasLimitCalculator.constant()); + GasLimitCalculator.constant(), + Optional.empty()); // this should fill up all the block space final Transaction fillingLegacyTx = @@ -467,7 +479,8 @@ protected BlockTransactionSelector createBlockSelector( dataGasPrice, getFeeMarket(), new LondonGasCalculator(), - GasLimitCalculator.constant()); + GasLimitCalculator.constant(), + Optional.empty()); return selector; } diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/BlockMinerTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/BlockMinerTest.java index e687ab0b8b2..feb784f89e0 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/BlockMinerTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/BlockMinerTest.java @@ -54,7 +54,7 @@ public void blockCreatedIsAddedToBlockChain() throws InterruptedException { new Block( headerBuilder.buildHeader(), new BlockBody(Lists.newArrayList(), Lists.newArrayList())); - final ProtocolContext protocolContext = new ProtocolContext(null, null, null); + final ProtocolContext protocolContext = new ProtocolContext(null, null, null, Optional.empty()); final PoWBlockCreator blockCreator = mock(PoWBlockCreator.class); final Function blockCreatorSupplier = @@ -95,7 +95,7 @@ public void failureToImportDoesNotTriggerObservers() throws InterruptedException new Block( headerBuilder.buildHeader(), new BlockBody(Lists.newArrayList(), Lists.newArrayList())); - final ProtocolContext protocolContext = new ProtocolContext(null, null, null); + final ProtocolContext protocolContext = new ProtocolContext(null, null, null, Optional.empty()); final PoWBlockCreator blockCreator = mock(PoWBlockCreator.class); final Function blockCreatorSupplier = diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/ProtocolContext.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/ProtocolContext.java index 1a8a80a8e25..8c0fc460dc2 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/ProtocolContext.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/ProtocolContext.java @@ -18,6 +18,7 @@ import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; import java.util.Optional; @@ -30,6 +31,7 @@ public class ProtocolContext { private final MutableBlockchain blockchain; private final WorldStateArchive worldStateArchive; private final ConsensusContext consensusContext; + private final Optional transactionSelectorFactory; private Optional synchronizer; @@ -37,21 +39,32 @@ public ProtocolContext( final MutableBlockchain blockchain, final WorldStateArchive worldStateArchive, final ConsensusContext consensusContext) { + this(blockchain, worldStateArchive, consensusContext, Optional.empty()); + } + + public ProtocolContext( + final MutableBlockchain blockchain, + final WorldStateArchive worldStateArchive, + final ConsensusContext consensusContext, + final Optional transactionSelectorFactory) { this.blockchain = blockchain; this.worldStateArchive = worldStateArchive; this.consensusContext = consensusContext; this.synchronizer = Optional.empty(); + this.transactionSelectorFactory = transactionSelectorFactory; } public static ProtocolContext init( final MutableBlockchain blockchain, final WorldStateArchive worldStateArchive, final ProtocolSchedule protocolSchedule, - final ConsensusContextFactory consensusContextFactory) { + final ConsensusContextFactory consensusContextFactory, + final Optional transactionSelectorFactory) { return new ProtocolContext( blockchain, worldStateArchive, - consensusContextFactory.create(blockchain, worldStateArchive, protocolSchedule)); + consensusContextFactory.create(blockchain, worldStateArchive, protocolSchedule), + transactionSelectorFactory); } public Optional getSynchronizer() { @@ -79,4 +92,8 @@ public Optional safeConsensusContext(final Class .filter(c -> klass.isAssignableFrom(c.getClass())) .map(klass::cast); } + + public Optional getTransactionSelectorFactory() { + return transactionSelectorFactory; + } } diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockchainSetupUtil.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockchainSetupUtil.java index e2f40e81280..5b9fc1fb01a 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockchainSetupUtil.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockchainSetupUtil.java @@ -48,6 +48,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; import com.google.common.base.Charsets; import com.google.common.io.Resources; @@ -151,7 +152,8 @@ private static ProtocolContext mainnetProtocolContextProvider( public C as(final Class klass) { return null; } - }); + }, + Optional.empty()); } private static BlockchainSetupUtil create( diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java index c2903e92fef..e712139f29c 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java @@ -34,6 +34,7 @@ import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import java.math.BigInteger; +import java.util.Optional; import java.util.function.Function; public class ExecutionContextTestFixture { @@ -61,7 +62,7 @@ keyValueStorage, new MainnetBlockHeaderFunctions()), 0); this.stateArchive = createInMemoryWorldStateArchive(); this.protocolSchedule = protocolSchedule; - this.protocolContext = new ProtocolContext(blockchain, stateArchive, null); + this.protocolContext = new ProtocolContext(blockchain, stateArchive, null, Optional.empty()); genesisState.writeStateTo(stateArchive.getMutable()); } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/AbstractIsolationTests.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/AbstractIsolationTests.java index 9e6bef815d8..79c11099e52 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/AbstractIsolationTests.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/AbstractIsolationTests.java @@ -143,7 +143,7 @@ public void createStorage() { null); var ws = archive.getMutable(); genesisState.writeStateTo(ws); - protocolContext = new ProtocolContext(blockchain, archive, null); + protocolContext = new ProtocolContext(blockchain, archive, null, Optional.empty()); } // storage provider which uses a temporary directory based rocksdb diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransactions.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransactions.java index 1b00e82f64d..b8474d3d50b 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransactions.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransactions.java @@ -20,6 +20,7 @@ import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.evm.account.Account; +import org.hyperledger.besu.plugin.data.TransactionSelectionResult; import java.util.Collection; import java.util.List; @@ -85,12 +86,6 @@ default void signalInvalidAndRemoveDependentTransactions(final Transaction trans boolean isLocalSender(Address sender); - enum TransactionSelectionResult { - DELETE_TRANSACTION_AND_CONTINUE, - CONTINUE, - COMPLETE_OPERATION - } - @FunctionalInterface interface TransactionSelector { TransactionSelectionResult evaluateTransaction(Transaction transaction); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsSorter.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsSorter.java index 530175934ed..1b925086b5e 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsSorter.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsSorter.java @@ -36,6 +36,7 @@ import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.AccountState; import org.hyperledger.besu.metrics.BesuMetricCategory; +import org.hyperledger.besu.plugin.data.TransactionSelectionResult; import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.metrics.Counter; import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/AbstractBlockPropagationManagerTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/AbstractBlockPropagationManagerTest.java index a2cc12dc329..25ff8ab4b46 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/AbstractBlockPropagationManagerTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/AbstractBlockPropagationManagerTest.java @@ -65,6 +65,7 @@ import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; @@ -104,7 +105,8 @@ protected void setup(final DataStorageFormat dataStorageFormat) { new ProtocolContext( blockchain, tempProtocolContext.getWorldStateArchive(), - tempProtocolContext.getConsensusContext(ConsensusContext.class)); + tempProtocolContext.getConsensusContext(ConsensusContext.class), + Optional.empty()); ethProtocolManager = EthProtocolManagerTestUtil.create( protocolSchedule, diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManagerTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManagerTest.java index 495cbfd203d..29d9baca5e0 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManagerTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManagerTest.java @@ -40,6 +40,7 @@ import java.util.Arrays; import java.util.Collection; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import org.junit.After; @@ -80,7 +81,7 @@ public void setup() { final ProtocolSchedule protocolSchedule = ProtocolScheduleFixture.MAINNET; final ProtocolContext protocolContext = - new ProtocolContext(localBlockchain, localWorldState, null); + new ProtocolContext(localBlockchain, localWorldState, null, Optional.empty()); ethProtocolManager = EthProtocolManagerTestUtil.create( protocolSchedule, diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskParameterizedTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskParameterizedTest.java index b3abf90364a..74cddc5e569 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskParameterizedTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskParameterizedTest.java @@ -45,6 +45,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -160,7 +161,7 @@ public void searchesAgainstNetwork() { final EthContext ethContext = ethProtocolManager.ethContext(); final ProtocolContext protocolContext = - new ProtocolContext(localBlockchain, worldStateArchive, null); + new ProtocolContext(localBlockchain, worldStateArchive, null, Optional.empty()); final EthTask task = DetermineCommonAncestorTask.create( diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskTest.java index 2976facbe6c..8e2fd6cbae0 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskTest.java @@ -54,6 +54,7 @@ import org.hyperledger.besu.util.ExceptionUtils; import java.util.List; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; @@ -86,7 +87,8 @@ public void setup() { mock(TransactionPool.class), EthProtocolConfiguration.defaultConfig()); ethContext = ethProtocolManager.ethContext(); - protocolContext = new ProtocolContext(localBlockchain, worldStateArchive, null); + protocolContext = + new ProtocolContext(localBlockchain, worldStateArchive, null, Optional.empty()); } @Test diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java index 914536960e5..111fa072297 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java @@ -123,7 +123,7 @@ public TestNode( final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive(); genesisState.writeStateTo(worldStateArchive.getMutable()); final ProtocolContext protocolContext = - new ProtocolContext(blockchain, worldStateArchive, null); + new ProtocolContext(blockchain, worldStateArchive, null, Optional.empty()); final SyncState syncState = mock(SyncState.class); final SynchronizerConfiguration syncConfig = mock(SynchronizerConfiguration.class); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactionsTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactionsTest.java index f864eb1dc25..1fe331ef13b 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactionsTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactionsTest.java @@ -15,15 +15,15 @@ package org.hyperledger.besu.ethereum.eth.transactions.layered; import static org.assertj.core.api.Assertions.assertThat; -import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions.TransactionSelectionResult.COMPLETE_OPERATION; -import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions.TransactionSelectionResult.CONTINUE; -import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions.TransactionSelectionResult.DELETE_TRANSACTION_AND_CONTINUE; import static org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult.ADDED; import static org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult.ALREADY_KNOWN; import static org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult.NONCE_TOO_FAR_IN_FUTURE_FOR_SENDER; import static org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult.REJECTED_UNDERPRICED_REPLACEMENT; import static org.hyperledger.besu.ethereum.eth.transactions.layered.TransactionsLayer.RemovalReason.DROPPED; import static org.hyperledger.besu.ethereum.eth.transactions.layered.TransactionsLayer.RemovalReason.REPLACED; +import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.COMPLETE_OPERATION; +import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.CONTINUE; +import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.DELETE_TRANSACTION_AND_CONTINUE; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsTestBase.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsTestBase.java index c138779373d..602cccdb121 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsTestBase.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/sorter/AbstractPendingTransactionsTestBase.java @@ -15,11 +15,11 @@ package org.hyperledger.besu.ethereum.eth.transactions.sorter; import static org.assertj.core.api.Assertions.assertThat; -import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions.TransactionSelectionResult.CONTINUE; -import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions.TransactionSelectionResult.DELETE_TRANSACTION_AND_CONTINUE; import static org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult.ADDED; import static org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult.ALREADY_KNOWN; import static org.hyperledger.besu.ethereum.eth.transactions.TransactionAddedResult.REJECTED_UNDERPRICED_REPLACEMENT; +import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.CONTINUE; +import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.DELETE_TRANSACTION_AND_CONTINUE; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; @@ -43,6 +43,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.metrics.StubMetricsSystem; +import org.hyperledger.besu.plugin.data.TransactionSelectionResult; import org.hyperledger.besu.testutil.TestClock; import java.time.Clock; @@ -314,7 +315,7 @@ public void selectTransactionsUntilSelectorRequestsNoMore() { transactions.selectTransactions( transaction -> { parsedTransactions.add(transaction); - return PendingTransactions.TransactionSelectionResult.COMPLETE_OPERATION; + return TransactionSelectionResult.COMPLETE_OPERATION; }); assertThat(parsedTransactions.size()).isEqualTo(1); diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java index 55eec26fd2f..0cdb6854853 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/BlockchainReferenceTestCaseSpec.java @@ -106,7 +106,8 @@ public BlockchainReferenceTestCaseSpec( this.worldStateArchive = buildWorldStateArchive(accounts); this.blockchain = buildBlockchain(genesisBlockHeader); this.sealEngine = sealEngine; - this.protocolContext = new ProtocolContext(this.blockchain, this.worldStateArchive, null); + this.protocolContext = + new ProtocolContext(this.blockchain, this.worldStateArchive, null, Optional.empty()); } public String getNetwork() { diff --git a/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java b/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java index 62c8b031a2c..bbfb1fba786 100644 --- a/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java +++ b/ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java @@ -168,7 +168,7 @@ private boolean buildContext( genesisState.writeStateTo(worldState); blockchain = createInMemoryBlockchain(genesisState.getBlock()); - protocolContext = new ProtocolContext(blockchain, worldStateArchive, null); + protocolContext = new ProtocolContext(blockchain, worldStateArchive, null, Optional.empty()); blockchainQueries = new BlockchainQueries(blockchain, worldStateArchive, ethScheduler); diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index a465d64b0f6..8612d449d07 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -69,7 +69,7 @@ Calculated : ${currentHash} tasks.register('checkAPIChanges', FileStateChecker) { description = "Checks that the API for the Plugin-API project does not change without deliberate thought" files = sourceSets.main.allJava.files - knownHash = '3/qsZ9+jA10YbctpPtQ5Rn7cvYHwKRWBv6jXa+7WQMY=' + knownHash = 'p757auCgPIb5l/MEk8XPpJOkvEabVmkWUqEcq+NRtS4=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java new file mode 100644 index 00000000000..40e7b469151 --- /dev/null +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java @@ -0,0 +1,26 @@ +/* + * 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.plugin.data; + +/** Enum for the transaction selection result */ +public enum TransactionSelectionResult { + /** remove transaction from pool and continue block building */ + DELETE_TRANSACTION_AND_CONTINUE, + /** continue block building */ + CONTINUE, + /** stop block building */ + COMPLETE_OPERATION +} diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSelectionService.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSelectionService.java new file mode 100644 index 00000000000..1a46e71f306 --- /dev/null +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSelectionService.java @@ -0,0 +1,40 @@ +/* + * 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.plugin.services; + +import org.hyperledger.besu.plugin.Unstable; +import org.hyperledger.besu.plugin.services.txselection.TransactionSelectorFactory; + +import java.util.Optional; + +/** Transaction selection service interface */ +@Unstable +public interface TransactionSelectionService extends BesuService { + + /** + * Returns the (Optional) transaction selector factory + * + * @return the transaction selector factory + */ + Optional get(); + + /** + * Registers the transaction selector factory with the service + * + * @param transactionSelectorFactory transaction selector factory to be used + */ + void registerTransactionSelectorFactory(TransactionSelectorFactory transactionSelectorFactory); +} diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelector.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelector.java new file mode 100644 index 00000000000..eb416cf9aa8 --- /dev/null +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelector.java @@ -0,0 +1,39 @@ +/* + * 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.plugin.services.txselection; + +import org.hyperledger.besu.plugin.Unstable; +import org.hyperledger.besu.plugin.data.Transaction; +import org.hyperledger.besu.plugin.data.TransactionReceipt; +import org.hyperledger.besu.plugin.data.TransactionSelectionResult; + +/** Interface for the transaction selector */ +@Unstable +public interface TransactionSelector { + + /** + * Method called to decide whether a transaction is added to a block. The method can also indicate + * that no further transactions can be added to the block. + * + * @param transaction candidate transaction + * @param receipt receipt for the candidate transaction + * @return TransactionSelectionResult that indicates whether to include the transaction + */ + default TransactionSelectionResult selectTransaction( + final Transaction transaction, final TransactionReceipt receipt) { + return TransactionSelectionResult.CONTINUE; + } +} diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelectorFactory.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelectorFactory.java new file mode 100644 index 00000000000..1d4234acc9e --- /dev/null +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/txselection/TransactionSelectorFactory.java @@ -0,0 +1,30 @@ +/* + * 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.plugin.services.txselection; + +import org.hyperledger.besu.plugin.Unstable; + +/** Interface for a factory that creates transaction selectors */ +@Unstable +public interface TransactionSelectorFactory { + + /** + * Create a transaction selector + * + * @return the transaction selector + */ + TransactionSelector create(); +}