From a7ab1773e427c1a3dd001c1940227c8158f420fd Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Wed, 31 Jul 2024 18:46:16 +0200 Subject: [PATCH] In process RPC service (#7395) Signed-off-by: Fabio Di Fabio --- CHANGELOG.md | 3 +- .../tests/acceptance/dsl/node/BesuNode.java | 8 ++ .../dsl/node/ThreadBesuNodeRunner.java | 9 ++- .../configuration/BesuNodeConfiguration.java | 8 ++ .../BesuNodeConfigurationBuilder.java | 11 +++ .../node/configuration/BesuNodeFactory.java | 17 +++- .../NodeConfigurationFactory.java | 12 +++ .../acceptance/dsl/privacy/PrivacyNode.java | 1 + .../miner/MinerGetMinGasPriceTransaction.java | 42 ++++++++++ .../miner/MinerRequestFactory.java | 5 ++ .../transaction/miner/MinerTransactions.java | 4 + .../TestInProcessRpcServicePlugin.java | 81 +++++++++++++++++++ .../InProcessRpcServicePluginTest.java | 51 ++++++++++++ .../java/org/hyperledger/besu/Runner.java | 15 ++++ .../org/hyperledger/besu/RunnerBuilder.java | 46 +++++++++++ .../org/hyperledger/besu/cli/BesuCommand.java | 12 +++ .../options/unstable/InProcessRpcOptions.java | 73 +++++++++++++++++ .../besu/services/RpcEndpointServiceImpl.java | 61 ++++++++++++++ .../hyperledger/besu/RunnerBuilderTest.java | 7 ++ .../java/org/hyperledger/besu/RunnerTest.java | 2 + .../besu/cli/CommandTestAbstract.java | 1 + .../jsonrpc/InProcessRpcConfiguration.java | 36 +++++++++ .../internal/response/JsonRpcResponse.java | 6 +- plugin-api/build.gradle | 2 +- .../plugin/services/RpcEndpointService.java | 10 +++ .../plugin/services/rpc/PluginRpcRequest.java | 2 +- .../services/rpc/PluginRpcResponse.java | 27 +++++++ .../besu/plugin/services/rpc/RpcResponse.java | 26 ++++++ 28 files changed, 568 insertions(+), 10 deletions(-) create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/miner/MinerGetMinGasPriceTransaction.java create mode 100644 acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/tests/acceptance/plugins/TestInProcessRpcServicePlugin.java create mode 100644 acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/InProcessRpcServicePluginTest.java create mode 100644 besu/src/main/java/org/hyperledger/besu/cli/options/unstable/InProcessRpcOptions.java create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/InProcessRpcConfiguration.java create mode 100644 plugin-api/src/main/java/org/hyperledger/besu/plugin/services/rpc/PluginRpcResponse.java create mode 100644 plugin-api/src/main/java/org/hyperledger/besu/plugin/services/rpc/RpcResponse.java diff --git a/CHANGELOG.md b/CHANGELOG.md index c5d782ba257..0db9e9df3f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ ### Additions and Improvements - Expose set finalized/safe block in plugin api BlockchainService. These method can be used by plugins to set finalized/safe block for a PoA network (such as QBFT, IBFT and Clique).[#7382](https://github.com/hyperledger/besu/pull/7382) +- In process RPC service [#7395](https://github.com/hyperledger/besu/pull/7395) ### Bug fixes @@ -44,7 +45,7 @@ - Force bonsai-limit-trie-logs-enabled=false when sync-mode=FULL instead of startup error [#7357](https://github.com/hyperledger/besu/pull/7357) - `--Xbonsai-parallel-tx-processing-enabled` option enables executing transactions in parallel during block processing for Bonsai nodes - Reduce default trie log pruning window size from 30,000 to 5,000 [#7365](https://github.com/hyperledger/besu/pull/7365) -- Add option `--poa-discovery-retry-bootnodes` for PoA networks to always use bootnodes during peer refresh, not just on first start [#7314](https://github.com/hyperledger/besu/pull/7314) +- Add option `--poa-discovery-retry-bootnodes` for PoA networks to always use bootnodes during peer refresh, not just on first start [#7314](https://github.com/hyperledger/besu/pull/7314) ### Bug fixes - Fix `eth_call` deserialization to correctly ignore unknown fields in the transaction object. [#7323](https://github.com/hyperledger/besu/pull/7323) 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 5f30941f232..1ae27d62614 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 @@ -23,6 +23,7 @@ import org.hyperledger.besu.crypto.KeyPairUtil; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.ethereum.api.ApiConfiguration; +import org.hyperledger.besu.ethereum.api.jsonrpc.InProcessRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.ipc.JsonRpcIpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration; @@ -109,6 +110,7 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable private final Optional engineRpcConfiguration; private final WebSocketConfiguration webSocketConfiguration; private final JsonRpcIpcConfiguration jsonRpcIpcConfiguration; + private final InProcessRpcConfiguration inProcessRpcConfiguration; private final MetricsConfiguration metricsConfiguration; private final DataStorageConfiguration dataStorageConfiguration; private Optional permissioningConfiguration; @@ -143,6 +145,7 @@ public BesuNode( final Optional engineRpcConfiguration, final WebSocketConfiguration webSocketConfiguration, final JsonRpcIpcConfiguration jsonRpcIpcConfiguration, + final InProcessRpcConfiguration inProcessRpcConfiguration, final MetricsConfiguration metricsConfiguration, final Optional permissioningConfiguration, final ApiConfiguration apiConfiguration, @@ -193,6 +196,7 @@ public BesuNode( this.engineRpcConfiguration = engineRpcConfiguration; this.webSocketConfiguration = webSocketConfiguration; this.jsonRpcIpcConfiguration = jsonRpcIpcConfiguration; + this.inProcessRpcConfiguration = inProcessRpcConfiguration; this.metricsConfiguration = metricsConfiguration; this.permissioningConfiguration = permissioningConfiguration; this.apiConfiguration = apiConfiguration; @@ -624,6 +628,10 @@ JsonRpcIpcConfiguration jsonRpcIpcConfiguration() { return jsonRpcIpcConfiguration; } + InProcessRpcConfiguration inProcessRpcConfiguration() { + return inProcessRpcConfiguration; + } + Optional wsRpcListenHost() { return Optional.of(webSocketConfiguration().getHost()); } 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 0448d324e64..459fe49efb3 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 @@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.GasLimitCalculator; import org.hyperledger.besu.ethereum.api.ApiConfiguration; import org.hyperledger.besu.ethereum.api.graphql.GraphQLConfiguration; +import org.hyperledger.besu.ethereum.api.jsonrpc.InProcessRpcConfiguration; import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters; import org.hyperledger.besu.ethereum.core.plugins.PluginConfiguration; import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; @@ -236,6 +237,8 @@ public void startNode(final BesuNode node) { .transactionPoolValidatorService(transactionPoolValidatorServiceImpl) .build(); + final InProcessRpcConfiguration inProcessRpcConfiguration = node.inProcessRpcConfiguration(); + final int maxPeers = 25; builder @@ -297,7 +300,8 @@ public void startNode(final BesuNode node) { .besuPluginContext(besuPluginContext) .autoLogBloomCaching(false) .storageProvider(storageProvider) - .rpcEndpointService(rpcEndpointServiceImpl); + .rpcEndpointService(rpcEndpointServiceImpl) + .inProcessRpcConfiguration(inProcessRpcConfiguration); node.engineRpcConfiguration().ifPresent(runnerBuilder::engineJsonRpcConfiguration); besuPluginContext.beforeExternalServices(); @@ -313,6 +317,9 @@ public void startNode(final BesuNode node) { besuController.getTransactionPool(), besuController.getSyncState(), besuController.getProtocolContext().getBadBlockManager())); + + rpcEndpointServiceImpl.init(runner.getInProcessRpcMethods()); + besuPluginContext.startPlugins(); runner.startEthereumMainLoop(); diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java index 7bffe9d7750..c77a6c8ac7b 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java @@ -17,6 +17,7 @@ import org.hyperledger.besu.cli.config.NetworkName; import org.hyperledger.besu.crypto.KeyPair; import org.hyperledger.besu.ethereum.api.ApiConfiguration; +import org.hyperledger.besu.ethereum.api.jsonrpc.InProcessRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.ipc.JsonRpcIpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration; @@ -45,6 +46,7 @@ public class BesuNodeConfiguration { private final Optional engineRpcConfiguration; private final WebSocketConfiguration webSocketConfiguration; private final JsonRpcIpcConfiguration jsonRpcIpcConfiguration; + private final InProcessRpcConfiguration inProcessRpcConfiguration; private final MetricsConfiguration metricsConfiguration; private final Optional permissioningConfiguration; private final ApiConfiguration apiConfiguration; @@ -81,6 +83,7 @@ public class BesuNodeConfiguration { final Optional engineRpcConfiguration, final WebSocketConfiguration webSocketConfiguration, final JsonRpcIpcConfiguration jsonRpcIpcConfiguration, + final InProcessRpcConfiguration inProcessRpcConfiguration, final MetricsConfiguration metricsConfiguration, final Optional permissioningConfiguration, final ApiConfiguration apiConfiguration, @@ -114,6 +117,7 @@ public class BesuNodeConfiguration { this.engineRpcConfiguration = engineRpcConfiguration; this.webSocketConfiguration = webSocketConfiguration; this.jsonRpcIpcConfiguration = jsonRpcIpcConfiguration; + this.inProcessRpcConfiguration = inProcessRpcConfiguration; this.metricsConfiguration = metricsConfiguration; this.permissioningConfiguration = permissioningConfiguration; this.apiConfiguration = apiConfiguration; @@ -171,6 +175,10 @@ public JsonRpcIpcConfiguration getJsonRpcIpcConfiguration() { return jsonRpcIpcConfiguration; } + public InProcessRpcConfiguration getInProcessRpcConfiguration() { + return inProcessRpcConfiguration; + } + public MetricsConfiguration getMetricsConfiguration() { return metricsConfiguration; } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java index 86fb8ab5caf..d8b5e0f9040 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java @@ -24,6 +24,8 @@ import org.hyperledger.besu.crypto.KeyPair; import org.hyperledger.besu.ethereum.api.ApiConfiguration; import org.hyperledger.besu.ethereum.api.ImmutableApiConfiguration; +import org.hyperledger.besu.ethereum.api.jsonrpc.ImmutableInProcessRpcConfiguration; +import org.hyperledger.besu.ethereum.api.jsonrpc.InProcessRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis; import org.hyperledger.besu.ethereum.api.jsonrpc.authentication.JwtAlgorithm; @@ -70,6 +72,8 @@ public class BesuNodeConfigurationBuilder { private JsonRpcConfiguration engineRpcConfiguration = JsonRpcConfiguration.createEngineDefault(); private WebSocketConfiguration webSocketConfiguration = WebSocketConfiguration.createDefault(); private JsonRpcIpcConfiguration jsonRpcIpcConfiguration = new JsonRpcIpcConfiguration(); + private InProcessRpcConfiguration inProcessRpcConfiguration = + ImmutableInProcessRpcConfiguration.builder().build(); private MetricsConfiguration metricsConfiguration = MetricsConfiguration.builder().build(); private Optional permissioningConfiguration = Optional.empty(); private ApiConfiguration apiConfiguration = ImmutableApiConfiguration.builder().build(); @@ -258,6 +262,12 @@ public BesuNodeConfigurationBuilder jsonRpcIpcConfiguration( return this; } + public BesuNodeConfigurationBuilder inProcessRpcConfiguration( + final InProcessRpcConfiguration inProcessRpcConfiguration) { + this.inProcessRpcConfiguration = inProcessRpcConfiguration; + return this; + } + public BesuNodeConfigurationBuilder metricsConfiguration( final MetricsConfiguration metricsConfiguration) { this.metricsConfiguration = metricsConfiguration; @@ -516,6 +526,7 @@ public BesuNodeConfiguration build() { Optional.of(engineRpcConfiguration), webSocketConfiguration, jsonRpcIpcConfiguration, + inProcessRpcConfiguration, metricsConfiguration, permissioningConfiguration, apiConfiguration, 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 1ea29388bd5..ed4a28422dd 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 @@ -16,6 +16,8 @@ import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; +import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.ADMIN; +import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.IBFT; import org.hyperledger.besu.crypto.KeyPair; import org.hyperledger.besu.datatypes.Wei; @@ -43,6 +45,7 @@ import java.net.URISyntaxException; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.Set; @@ -64,6 +67,7 @@ public BesuNode create(final BesuNodeConfiguration config) throws IOException { config.getEngineRpcConfiguration(), config.getWebSocketConfiguration(), config.getJsonRpcIpcConfiguration(), + config.getInProcessRpcConfiguration(), config.getMetricsConfiguration(), config.getPermissioningConfiguration(), config.getApiConfiguration(), @@ -330,12 +334,20 @@ public BesuNode createArchiveNodeWithRpcDisabled(final String name) throws IOExc } public BesuNode createPluginsNode( - final String name, final List plugins, final List extraCLIOptions) + final String name, + final List plugins, + final List extraCLIOptions, + final String... extraRpcApis) throws IOException { + + final List enableRpcApis = new ArrayList<>(Arrays.asList(extraRpcApis)); + enableRpcApis.addAll(List.of(IBFT.name(), ADMIN.name())); + return create( new BesuNodeConfigurationBuilder() .name(name) - .jsonRpcConfiguration(node.createJsonRpcWithIbft2AdminEnabledConfig()) + .jsonRpcConfiguration( + node.createJsonRpcWithRpcApiEnabledConfig(enableRpcApis.toArray(String[]::new))) .webSocketConfiguration(node.createWebSocketEnabledConfig()) .plugins(plugins) .extraCLIOptions(extraCLIOptions) @@ -394,6 +406,7 @@ public BesuNode createCliqueNodeWithExtraCliOptionsAndRpcApis( .miningEnabled() .jsonRpcConfiguration(node.createJsonRpcWithCliqueEnabledConfig(extraRpcApis)) .webSocketConfiguration(node.createWebSocketEnabledConfig()) + .inProcessRpcConfiguration(node.createInProcessRpcConfiguration(extraRpcApis)) .devMode(false) .jsonRpcTxPool() .genesisConfigProvider( diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/NodeConfigurationFactory.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/NodeConfigurationFactory.java index 219d15d1adf..c2e82c0f835 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/NodeConfigurationFactory.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/NodeConfigurationFactory.java @@ -22,6 +22,8 @@ import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.MINER; import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.QBFT; +import org.hyperledger.besu.ethereum.api.jsonrpc.ImmutableInProcessRpcConfiguration; +import org.hyperledger.besu.ethereum.api.jsonrpc.InProcessRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration; import org.hyperledger.besu.tests.acceptance.dsl.node.RunnableNode; @@ -94,4 +96,14 @@ public JsonRpcConfiguration createJsonRpcWithRpcApiEnabledConfig(final String... jsonRpcConfig.setRpcApis(rpcApis); return jsonRpcConfig; } + + public InProcessRpcConfiguration createInProcessRpcConfiguration(final Set extraRpcApis) { + final Set rpcApis = + new HashSet<>(ImmutableInProcessRpcConfiguration.DEFAULT_IN_PROCESS_RPC_APIS); + rpcApis.addAll(extraRpcApis); + return ImmutableInProcessRpcConfiguration.builder() + .inProcessRpcApis(rpcApis) + .isEnabled(true) + .build(); + } } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyNode.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyNode.java index 32a5a0fbf4a..520cd7d5741 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyNode.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyNode.java @@ -106,6 +106,7 @@ public PrivacyNode( besuConfig.getEngineRpcConfiguration(), besuConfig.getWebSocketConfiguration(), besuConfig.getJsonRpcIpcConfiguration(), + besuConfig.getInProcessRpcConfiguration(), besuConfig.getMetricsConfiguration(), besuConfig.getPermissioningConfiguration(), besuConfig.getApiConfiguration(), diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/miner/MinerGetMinGasPriceTransaction.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/miner/MinerGetMinGasPriceTransaction.java new file mode 100644 index 00000000000..991e9f7bc4e --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/miner/MinerGetMinGasPriceTransaction.java @@ -0,0 +1,42 @@ +/* + * 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.tests.acceptance.dsl.transaction.miner; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; + +import java.io.IOException; +import java.math.BigInteger; + +import org.web3j.protocol.core.methods.response.EthGasPrice; + +public class MinerGetMinGasPriceTransaction implements Transaction { + + @Override + public BigInteger execute(final NodeRequests node) { + try { + final EthGasPrice result = node.miner().minerGetMinGasPrice().send(); + assertThat(result).isNotNull(); + assertThat(result.hasError()).isFalse(); + + return result.getGasPrice(); + + } catch (final IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/miner/MinerRequestFactory.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/miner/MinerRequestFactory.java index 26be9d3a968..5c8b5ef6c10 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/miner/MinerRequestFactory.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/miner/MinerRequestFactory.java @@ -16,6 +16,7 @@ import org.web3j.protocol.Web3jService; import org.web3j.protocol.core.Request; +import org.web3j.protocol.core.methods.response.EthGasPrice; public class MinerRequestFactory { @@ -40,4 +41,8 @@ Request minerStop() { web3jService, org.web3j.protocol.core.methods.response.VoidResponse.class); } + + Request minerGetMinGasPrice() { + return new Request<>("miner_getMinGasPrice", null, web3jService, EthGasPrice.class); + } } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/miner/MinerTransactions.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/miner/MinerTransactions.java index e5cacd4e7a6..a8d24515a98 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/miner/MinerTransactions.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/miner/MinerTransactions.java @@ -23,4 +23,8 @@ public MinerStartTransaction minerStart() { public MinerStopTransaction minerStop() { return new MinerStopTransaction(); } + + public MinerGetMinGasPriceTransaction minerGetMinGasPrice() { + return new MinerGetMinGasPriceTransaction(); + } } diff --git a/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/tests/acceptance/plugins/TestInProcessRpcServicePlugin.java b/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/tests/acceptance/plugins/TestInProcessRpcServicePlugin.java new file mode 100644 index 00000000000..3ebced0df91 --- /dev/null +++ b/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/tests/acceptance/plugins/TestInProcessRpcServicePlugin.java @@ -0,0 +1,81 @@ +/* + * 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.tests.acceptance.plugins; + +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.plugin.BesuContext; +import org.hyperledger.besu.plugin.BesuPlugin; +import org.hyperledger.besu.plugin.services.PicoCLIOptions; +import org.hyperledger.besu.plugin.services.RpcEndpointService; +import org.hyperledger.besu.plugin.services.rpc.RpcResponseType; + +import com.google.auto.service.AutoService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import picocli.CommandLine; + +@AutoService(BesuPlugin.class) +public class TestInProcessRpcServicePlugin implements BesuPlugin { + private static final Logger LOG = LoggerFactory.getLogger(TestInProcessRpcServicePlugin.class); + + private RpcEndpointService rpcEndpointService; + + @CommandLine.Option(names = {"--plugin-test-set-min-gas-price"}) + long minGasPrice = -1; + + @Override + public void register(final BesuContext context) { + final PicoCLIOptions cmdlineOptions = + context + .getService(PicoCLIOptions.class) + .orElseThrow( + () -> + new IllegalStateException( + "Failed to obtain PicoCLI options from the BesuContext")); + + cmdlineOptions.addPicoCLIOptions("test", this); + + rpcEndpointService = + context + .getService(RpcEndpointService.class) + .orElseThrow( + () -> + new RuntimeException( + "Failed to obtain RpcEndpointService from the BesuContext.")); + } + + @Override + public void start() { + LOG.info("TestInProcessRpcServicePlugin minGasPrice option: {}", minGasPrice); + if (minGasPrice >= 0) { + callSetMinGasPrice(minGasPrice); + } + } + + @Override + public void stop() {} + + private void callSetMinGasPrice(final long minGasPrice) { + LOG.info("Setting minGasPrice via in-process RPC service"); + final var minGasPriceWei = Wei.of(minGasPrice); + final var resp = + rpcEndpointService.call( + "miner_setMinGasPrice", new Object[] {minGasPriceWei.toShortHexString()}); + LOG.info("miner_setMinGasPrice response: {}", resp); + if (!resp.getType().equals(RpcResponseType.SUCCESS)) { + throw new RuntimeException("Internal setMinGasPrice method failed: " + resp); + } + } +} diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/InProcessRpcServicePluginTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/InProcessRpcServicePluginTest.java new file mode 100644 index 00000000000..4dbf8fd5d18 --- /dev/null +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/InProcessRpcServicePluginTest.java @@ -0,0 +1,51 @@ +/* + * 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.tests.acceptance.plugins; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; +import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; + +import java.math.BigInteger; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class InProcessRpcServicePluginTest extends AcceptanceTestBase { + private static final long MIN_GAS_PRICE = 123456; + private BesuNode node; + + @BeforeEach + public void setUp() throws Exception { + node = + besu.createPluginsNode( + "node1", + List.of("testPlugins"), + List.of( + "--Xin-process-rpc-enabled=true", + "--Xin-process-rpc-apis=MINER", + "--plugin-test-set-min-gas-price=" + MIN_GAS_PRICE), + "MINER"); + cluster.start(node); + } + + @Test + public void smokeTest() { + final var currMinGasPrice = node.execute(minerTransactions.minerGetMinGasPrice()); + assertThat(currMinGasPrice).isEqualTo(BigInteger.valueOf(MIN_GAS_PRICE)); + } +} diff --git a/besu/src/main/java/org/hyperledger/besu/Runner.java b/besu/src/main/java/org/hyperledger/besu/Runner.java index eed35a03a94..609dab117ff 100644 --- a/besu/src/main/java/org/hyperledger/besu/Runner.java +++ b/besu/src/main/java/org/hyperledger/besu/Runner.java @@ -18,6 +18,7 @@ import org.hyperledger.besu.ethereum.api.graphql.GraphQLHttpService; import org.hyperledger.besu.ethereum.api.jsonrpc.EngineJsonRpcService; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcHttpService; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.ipc.JsonRpcIpcService; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketService; import org.hyperledger.besu.ethereum.api.query.cache.AutoTransactionLogBloomCachingService; @@ -39,6 +40,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.util.Map; import java.util.Optional; import java.util.Properties; import java.util.concurrent.CompletableFuture; @@ -69,6 +71,7 @@ public class Runner implements AutoCloseable { private final Optional engineJsonRpc; private final Optional metrics; private final Optional ipcJsonRpc; + private final Map inProcessRpcMethods; private final Optional pidPath; private final Optional webSocketRpc; private final TransactionPoolEvictionService transactionPoolEvictionService; @@ -90,6 +93,7 @@ public class Runner implements AutoCloseable { * @param graphQLHttp the graph ql http * @param webSocketRpc the web socket rpc * @param ipcJsonRpc the ipc json rpc + * @param inProcessRpcMethods the in-process rpc methods * @param stratumServer the stratum server * @param metrics the metrics * @param ethStatsService the eth stats service @@ -108,6 +112,7 @@ public class Runner implements AutoCloseable { final Optional graphQLHttp, final Optional webSocketRpc, final Optional ipcJsonRpc, + final Map inProcessRpcMethods, final Optional stratumServer, final Optional metrics, final Optional ethStatsService, @@ -125,6 +130,7 @@ public class Runner implements AutoCloseable { this.engineJsonRpc = engineJsonRpc; this.webSocketRpc = webSocketRpc; this.ipcJsonRpc = ipcJsonRpc; + this.inProcessRpcMethods = inProcessRpcMethods; this.metrics = metrics; this.ethStatsService = ethStatsService; this.besuController = besuController; @@ -413,6 +419,15 @@ public Optional getMetricsPort() { } } + /** + * Get the RPC methods that can be called in-process + * + * @return RPC methods by name + */ + public Map getInProcessRpcMethods() { + return inProcessRpcMethods; + } + /** * Gets local enode. * diff --git a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java index d0c434fc819..e34c0115fea 100644 --- a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java @@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.api.graphql.GraphQLHttpService; import org.hyperledger.besu.ethereum.api.graphql.GraphQLProvider; import org.hyperledger.besu.ethereum.api.jsonrpc.EngineJsonRpcService; +import org.hyperledger.besu.ethereum.api.jsonrpc.InProcessRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcHttpService; import org.hyperledger.besu.ethereum.api.jsonrpc.authentication.AuthenticationService; @@ -178,6 +179,7 @@ public class RunnerBuilder { private Optional engineJsonRpcConfiguration = Optional.empty(); private GraphQLConfiguration graphQLConfiguration; private WebSocketConfiguration webSocketConfiguration; + private InProcessRpcConfiguration inProcessRpcConfiguration; private ApiConfiguration apiConfiguration; private Path dataDir; private Optional pidPath = Optional.empty(); @@ -414,6 +416,18 @@ public RunnerBuilder webSocketConfiguration(final WebSocketConfiguration webSock return this; } + /** + * Add In-Process RPC configuration. + * + * @param inProcessRpcConfiguration the in-process RPC configuration + * @return the runner builder + */ + public RunnerBuilder inProcessRpcConfiguration( + final InProcessRpcConfiguration inProcessRpcConfiguration) { + this.inProcessRpcConfiguration = inProcessRpcConfiguration; + return this; + } + /** * Add Api configuration. * @@ -1082,6 +1096,37 @@ public Runner build() { jsonRpcIpcService = Optional.empty(); } + final Map inProcessRpcMethods; + if (inProcessRpcConfiguration.isEnabled()) { + inProcessRpcMethods = + jsonRpcMethods( + protocolSchedule, + context, + besuController, + peerNetwork, + blockchainQueries, + synchronizer, + transactionPool, + miningParameters, + miningCoordinator, + metricsSystem, + supportedCapabilities, + inProcessRpcConfiguration.getInProcessRpcApis(), + filterManager, + accountLocalConfigPermissioningController, + nodeLocalConfigPermissioningController, + privacyParameters, + jsonRpcConfiguration, + webSocketConfiguration, + metricsConfiguration, + natService, + besuPluginContext.getNamedPlugins(), + dataDir, + rpcEndpointServiceImpl); + } else { + inProcessRpcMethods = Map.of(); + } + return new Runner( vertx, networkRunner, @@ -1091,6 +1136,7 @@ public Runner build() { graphQLHttpService, webSocketService, jsonRpcIpcService, + inProcessRpcMethods, stratumServer, metricsService, ethStatsService, 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 d300fbea457..9c2b014ec01 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -63,6 +63,7 @@ import org.hyperledger.besu.cli.options.unstable.DnsOptions; import org.hyperledger.besu.cli.options.unstable.EthProtocolOptions; import org.hyperledger.besu.cli.options.unstable.EvmOptions; +import org.hyperledger.besu.cli.options.unstable.InProcessRpcOptions; import org.hyperledger.besu.cli.options.unstable.IpcOptions; import org.hyperledger.besu.cli.options.unstable.MetricsCLIOptions; import org.hyperledger.besu.cli.options.unstable.NatOptions; @@ -107,6 +108,7 @@ import org.hyperledger.besu.ethereum.GasLimitCalculator; import org.hyperledger.besu.ethereum.api.ApiConfiguration; import org.hyperledger.besu.ethereum.api.graphql.GraphQLConfiguration; +import org.hyperledger.besu.ethereum.api.jsonrpc.InProcessRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis; import org.hyperledger.besu.ethereum.api.jsonrpc.authentication.JwtAlgorithm; @@ -660,6 +662,10 @@ static class EngineRPCOptionGroup { @CommandLine.ArgGroup(validate = false, heading = "@|bold JSON-RPC Websocket Options|@%n") RpcWebsocketOptions rpcWebsocketOptions = new RpcWebsocketOptions(); + // In-Process RPC Options + @CommandLine.ArgGroup(validate = false, heading = "@|bold In-Process RPC Options|@%n") + InProcessRpcOptions inProcessRpcOptions = InProcessRpcOptions.create(); + // Privacy Options Group @CommandLine.ArgGroup(validate = false, heading = "@|bold Privacy Options|@%n") PrivacyOptionGroup privacyOptionGroup = new PrivacyOptionGroup(); @@ -926,6 +932,7 @@ static class MetricsOptionGroup { private GraphQLConfiguration graphQLConfiguration; private WebSocketConfiguration webSocketConfiguration; private JsonRpcIpcConfiguration jsonRpcIpcConfiguration; + private InProcessRpcConfiguration inProcessRpcConfiguration; private ApiConfiguration apiConfiguration; private MetricsConfiguration metricsConfiguration; private Optional permissioningConfiguration; @@ -1353,6 +1360,7 @@ private Runner buildRunner() { engineJsonRpcConfiguration, webSocketConfiguration, jsonRpcIpcConfiguration, + inProcessRpcConfiguration, apiConfiguration, metricsConfiguration, permissioningConfiguration, @@ -1370,6 +1378,7 @@ private void startPlugins(final Runner runner) { besuController.getProtocolContext().getWorldStateArchive(), besuController.getProtocolSchedule(), apiConfiguration.getGasCap())); + rpcEndpointServiceImpl.init(runner.getInProcessRpcMethods()); besuPluginContext.addService( BesuEvents.class, @@ -1810,6 +1819,7 @@ private void configure() throws Exception { unstableIpcOptions.isEnabled(), unstableIpcOptions.getIpcPath(), unstableIpcOptions.getRpcIpcApis()); + inProcessRpcConfiguration = inProcessRpcOptions.toDomainObject(); apiConfiguration = apiConfigurationOptions.apiConfiguration(); dataStorageConfiguration = getDataStorageConfiguration(); // hostsWhitelist is a hidden option. If it is specified, add the list to hostAllowlist @@ -2321,6 +2331,7 @@ private Runner synchronize( final JsonRpcConfiguration engineJsonRpcConfiguration, final WebSocketConfiguration webSocketConfiguration, final JsonRpcIpcConfiguration jsonRpcIpcConfiguration, + final InProcessRpcConfiguration inProcessRpcConfiguration, final ApiConfiguration apiConfiguration, final MetricsConfiguration metricsConfiguration, final Optional permissioningConfiguration, @@ -2353,6 +2364,7 @@ private Runner synchronize( .engineJsonRpcConfiguration(engineJsonRpcConfiguration) .webSocketConfiguration(webSocketConfiguration) .jsonRpcIpcConfiguration(jsonRpcIpcConfiguration) + .inProcessRpcConfiguration(inProcessRpcConfiguration) .apiConfiguration(apiConfiguration) .pidPath(pidPath) .dataDir(dataDir()) diff --git a/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/InProcessRpcOptions.java b/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/InProcessRpcOptions.java new file mode 100644 index 00000000000..9adeb37db7d --- /dev/null +++ b/besu/src/main/java/org/hyperledger/besu/cli/options/unstable/InProcessRpcOptions.java @@ -0,0 +1,73 @@ +/* + * 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.cli.options.unstable; + +import static org.hyperledger.besu.ethereum.api.jsonrpc.InProcessRpcConfiguration.DEFAULT_IN_PROCESS_RPC_APIS; +import static org.hyperledger.besu.ethereum.api.jsonrpc.InProcessRpcConfiguration.DEFAULT_IN_PROCESS_RPC_ENABLED; + +import org.hyperledger.besu.cli.options.CLIOptions; +import org.hyperledger.besu.cli.util.CommandLineUtils; +import org.hyperledger.besu.ethereum.api.jsonrpc.ImmutableInProcessRpcConfiguration; +import org.hyperledger.besu.ethereum.api.jsonrpc.InProcessRpcConfiguration; + +import java.util.List; +import java.util.Set; + +import picocli.CommandLine; + +/** The in process RPC options. */ +public class InProcessRpcOptions implements CLIOptions { + + /** Default constructor. */ + InProcessRpcOptions() {} + + /** + * Create ipc options. + * + * @return the ipc options + */ + public static InProcessRpcOptions create() { + return new InProcessRpcOptions(); + } + + @CommandLine.Option( + names = {"--Xin-process-rpc-enabled"}, + hidden = true, + description = "Set to enalbe in-process RPC method call service (default: ${DEFAULT-VALUE})") + private final Boolean enabled = DEFAULT_IN_PROCESS_RPC_ENABLED; + + @CommandLine.Option( + names = {"--Xin-process-rpc-api", "--Xin-process-rpc-apis"}, + hidden = true, + paramLabel = "", + split = " {0,1}, {0,1}", + arity = "1..*", + description = + "Comma separated list of APIs to enable on in-process RPC method call service (default: ${DEFAULT-VALUE})") + private final Set inProcessRpcApis = DEFAULT_IN_PROCESS_RPC_APIS; + + @Override + public InProcessRpcConfiguration toDomainObject() { + return ImmutableInProcessRpcConfiguration.builder() + .isEnabled(enabled) + .inProcessRpcApis(inProcessRpcApis) + .build(); + } + + @Override + public List getCLIOptions() { + return CommandLineUtils.getCLIOptions(this, new InProcessRpcOptions()); + } +} diff --git a/besu/src/main/java/org/hyperledger/besu/services/RpcEndpointServiceImpl.java b/besu/src/main/java/org/hyperledger/besu/services/RpcEndpointServiceImpl.java index f31323b91b8..dcfe29146e7 100644 --- a/besu/src/main/java/org/hyperledger/besu/services/RpcEndpointServiceImpl.java +++ b/besu/src/main/java/org/hyperledger/besu/services/RpcEndpointServiceImpl.java @@ -17,25 +17,48 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.PluginJsonRpcMethod; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; import org.hyperledger.besu.plugin.services.RpcEndpointService; import org.hyperledger.besu.plugin.services.rpc.PluginRpcRequest; +import org.hyperledger.besu.plugin.services.rpc.PluginRpcResponse; +import org.hyperledger.besu.plugin.services.rpc.RpcResponseType; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.NoSuchElementException; import java.util.function.Function; import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** The RPC endpoint service implementation. */ public class RpcEndpointServiceImpl implements RpcEndpointService { + private static final Logger LOG = LoggerFactory.getLogger(RpcEndpointServiceImpl.class); + private final Map> rpcMethods = new HashMap<>(); + private Map inProcessRpcMethods; /** Default Constructor. */ public RpcEndpointServiceImpl() {} + /** + * Init the service + * + * @param inProcessRpcMethods set of RPC methods that can be called + */ + public void init(final Map inProcessRpcMethods) { + this.inProcessRpcMethods = inProcessRpcMethods; + } + @Override public void registerRPCEndpoint( final String namespace, @@ -48,6 +71,44 @@ public void registerRPCEndpoint( rpcMethods.put(namespace + "_" + functionName, function); } + @Override + public PluginRpcResponse call(final String methodName, final Object[] params) { + checkNotNull( + inProcessRpcMethods, + "Service not initialized yet, this method must be called after plugin 'beforeExternalServices' call completes"); + + LOG.atTrace() + .setMessage("Calling method:{} with params:{}") + .addArgument(methodName) + .addArgument(() -> Arrays.toString(params)) + .log(); + + final var method = inProcessRpcMethods.get(methodName); + + if (method == null) { + throw new NoSuchElementException("Unknown or not enabled method: " + methodName); + } + + final var requestContext = + new JsonRpcRequestContext(new JsonRpcRequest("2.0", methodName, params)); + final var response = method.response(requestContext); + return new PluginRpcResponse() { + @Override + public Object getResult() { + return switch (response.getType()) { + case NONE, UNAUTHORIZED -> null; + case SUCCESS -> ((JsonRpcSuccessResponse) response).getResult(); + case ERROR -> ((JsonRpcErrorResponse) response).getError(); + }; + } + + @Override + public RpcResponseType getType() { + return response.getType(); + } + }; + } + /** * Gets plugin methods. * diff --git a/besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java b/besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java index c1770640801..c7180b958c1 100644 --- a/besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java +++ b/besu/src/test/java/org/hyperledger/besu/RunnerBuilderTest.java @@ -38,6 +38,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.api.ImmutableApiConfiguration; import org.hyperledger.besu.ethereum.api.graphql.GraphQLConfiguration; +import org.hyperledger.besu.ethereum.api.jsonrpc.InProcessRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.ipc.JsonRpcIpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration; @@ -163,6 +164,7 @@ public void enodeUrlShouldHaveAdvertisedHostWhenDiscoveryDisabled() { .graphQLConfiguration(mock(GraphQLConfiguration.class)) .webSocketConfiguration(mock(WebSocketConfiguration.class)) .jsonRpcIpcConfiguration(mock(JsonRpcIpcConfiguration.class)) + .inProcessRpcConfiguration(mock(InProcessRpcConfiguration.class)) .metricsConfiguration(mock(MetricsConfiguration.class)) .vertx(vertx) .dataDir(dataDir.getRoot()) @@ -208,6 +210,7 @@ public void movingAcrossProtocolSpecsUpdatesNodeRecord() { .graphQLConfiguration(mock(GraphQLConfiguration.class)) .webSocketConfiguration(mock(WebSocketConfiguration.class)) .jsonRpcIpcConfiguration(mock(JsonRpcIpcConfiguration.class)) + .inProcessRpcConfiguration(mock(InProcessRpcConfiguration.class)) .metricsConfiguration(mock(MetricsConfiguration.class)) .vertx(Vertx.vertx()) .dataDir(dataDir.getRoot()) @@ -267,6 +270,7 @@ public void whenEngineApiAddedListensOnDefaultPort() { .graphQLConfiguration(mock(GraphQLConfiguration.class)) .webSocketConfiguration(mock(WebSocketConfiguration.class)) .jsonRpcIpcConfiguration(mock(JsonRpcIpcConfiguration.class)) + .inProcessRpcConfiguration(mock(InProcessRpcConfiguration.class)) .metricsConfiguration(mock(MetricsConfiguration.class)) .vertx(Vertx.vertx()) .dataDir(dataDir.getRoot()) @@ -309,6 +313,7 @@ public void whenEngineApiAddedWebSocketReadyOnSamePort() { .engineJsonRpcConfiguration(engineConf) .webSocketConfiguration(wsRpc) .jsonRpcIpcConfiguration(mock(JsonRpcIpcConfiguration.class)) + .inProcessRpcConfiguration(mock(InProcessRpcConfiguration.class)) .graphQLConfiguration(mock(GraphQLConfiguration.class)) .metricsConfiguration(mock(MetricsConfiguration.class)) .vertx(Vertx.vertx()) @@ -351,6 +356,7 @@ public void whenEngineApiAddedEthSubscribeAvailable() { .engineJsonRpcConfiguration(engineConf) .webSocketConfiguration(wsRpc) .jsonRpcIpcConfiguration(mock(JsonRpcIpcConfiguration.class)) + .inProcessRpcConfiguration(mock(InProcessRpcConfiguration.class)) .graphQLConfiguration(mock(GraphQLConfiguration.class)) .metricsConfiguration(mock(MetricsConfiguration.class)) .vertx(Vertx.vertx()) @@ -395,6 +401,7 @@ public void noEngineApiNoServiceForMethods() { .graphQLConfiguration(mock(GraphQLConfiguration.class)) .webSocketConfiguration(defaultWebSockConfig) .jsonRpcIpcConfiguration(mock(JsonRpcIpcConfiguration.class)) + .inProcessRpcConfiguration(mock(InProcessRpcConfiguration.class)) .metricsConfiguration(mock(MetricsConfiguration.class)) .vertx(Vertx.vertx()) .dataDir(dataDir.getRoot()) diff --git a/besu/src/test/java/org/hyperledger/besu/RunnerTest.java b/besu/src/test/java/org/hyperledger/besu/RunnerTest.java index e10d5475818..5b2a56078f5 100644 --- a/besu/src/test/java/org/hyperledger/besu/RunnerTest.java +++ b/besu/src/test/java/org/hyperledger/besu/RunnerTest.java @@ -36,6 +36,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.api.ImmutableApiConfiguration; import org.hyperledger.besu.ethereum.api.graphql.GraphQLConfiguration; +import org.hyperledger.besu.ethereum.api.jsonrpc.ImmutableInProcessRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.ipc.JsonRpcIpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration; @@ -211,6 +212,7 @@ private void syncFromGenesis(final SyncMode mode, final GenesisConfigFile genesi .graphQLConfiguration(graphQLConfiguration()) .webSocketConfiguration(wsRpcConfiguration()) .jsonRpcIpcConfiguration(new JsonRpcIpcConfiguration()) + .inProcessRpcConfiguration(ImmutableInProcessRpcConfiguration.builder().build()) .metricsConfiguration(metricsConfiguration()) .dataDir(dbAhead) .pidPath(pidPath) 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 c77ced5c6fe..f6fa2e47984 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java @@ -332,6 +332,7 @@ public void initMocks() throws Exception { when(mockRunnerBuilder.graphQLConfiguration(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.webSocketConfiguration(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.jsonRpcIpcConfiguration(any())).thenReturn(mockRunnerBuilder); + when(mockRunnerBuilder.inProcessRpcConfiguration(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.apiConfiguration(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.dataDir(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.bannedNodeIds(any())).thenReturn(mockRunnerBuilder); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/InProcessRpcConfiguration.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/InProcessRpcConfiguration.java new file mode 100644 index 00000000000..e59d06a21fb --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/InProcessRpcConfiguration.java @@ -0,0 +1,36 @@ +/* + * 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.jsonrpc; + +import java.util.HashSet; +import java.util.Set; + +import org.immutables.value.Value; + +@Value.Immutable +public interface InProcessRpcConfiguration { + boolean DEFAULT_IN_PROCESS_RPC_ENABLED = false; + Set DEFAULT_IN_PROCESS_RPC_APIS = new HashSet<>(RpcApis.DEFAULT_RPC_APIS); + + @Value.Default + default boolean isEnabled() { + return DEFAULT_IN_PROCESS_RPC_ENABLED; + } + + @Value.Default + default Set getInProcessRpcApis() { + return DEFAULT_IN_PROCESS_RPC_APIS; + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcResponse.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcResponse.java index 6817d2cfbbd..5fb82187b69 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcResponse.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcResponse.java @@ -14,16 +14,14 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.response; -import org.hyperledger.besu.plugin.services.rpc.RpcResponseType; +import org.hyperledger.besu.plugin.services.rpc.RpcResponse; import com.fasterxml.jackson.annotation.JsonGetter; -public interface JsonRpcResponse { +public interface JsonRpcResponse extends RpcResponse { @JsonGetter("jsonrpc") default String getVersion() { return "2.0"; } - - RpcResponseType getType(); } diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index cc4b3237b52..e49297ab7b3 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -70,7 +70,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 = 'tXFd8EcMJtD+ZSLJxWJLYRZD0d3njRz+3Ubey2zFM2A=' + knownHash = 'I851CCOs00yYpW10qIGIak1bKbYhKFQkV2wyCYELHKY=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/RpcEndpointService.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/RpcEndpointService.java index a1b69a7c7fe..512d9877c78 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/RpcEndpointService.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/RpcEndpointService.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.plugin.services; import org.hyperledger.besu.plugin.services.rpc.PluginRpcRequest; +import org.hyperledger.besu.plugin.services.rpc.PluginRpcResponse; import java.util.function.Function; @@ -54,4 +55,13 @@ public interface RpcEndpointService extends BesuService { */ void registerRPCEndpoint( String namespace, String functionName, Function function); + + /** + * Allow to call any of the enabled in-process RPC methods + * + * @param methodName the method to invoke + * @param params the list of parameters accepted by the method + * @return the result of the method + */ + PluginRpcResponse call(String methodName, Object[] params); } diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/rpc/PluginRpcRequest.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/rpc/PluginRpcRequest.java index c360f35edc1..eab35b4b043 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/rpc/PluginRpcRequest.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/rpc/PluginRpcRequest.java @@ -1,5 +1,5 @@ /* - * Copyright ConsenSys AG. + * 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 diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/rpc/PluginRpcResponse.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/rpc/PluginRpcResponse.java new file mode 100644 index 00000000000..7096a8a4227 --- /dev/null +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/rpc/PluginRpcResponse.java @@ -0,0 +1,27 @@ +/* + * 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.plugin.services.rpc; + +/** The interface Plugin rpc response. */ +public interface PluginRpcResponse extends RpcResponse { + + /** + * Get the result, unfortunately there is no typing yet, so call must know how to interact with + * the response + * + * @return the result + */ + Object getResult(); +} diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/rpc/RpcResponse.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/rpc/RpcResponse.java new file mode 100644 index 00000000000..3fafbb1d108 --- /dev/null +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/services/rpc/RpcResponse.java @@ -0,0 +1,26 @@ +/* + * 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.plugin.services.rpc; + +/** Represent a Json RPC response */ +public interface RpcResponse { + + /** + * Get the response type + * + * @return the response type + */ + RpcResponseType getType(); +}