diff --git a/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/TestRpcEndpointServicePlugin.java b/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/TestRpcEndpointServicePlugin.java index d605dda638c..b1d8c195d46 100644 --- a/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/TestRpcEndpointServicePlugin.java +++ b/acceptance-tests/test-plugins/src/main/java/org/hyperledger/besu/plugins/TestRpcEndpointServicePlugin.java @@ -54,12 +54,13 @@ public void register(final BesuContext context) { .getService(RpcEndpointService.class) .ifPresent( rpcEndpointService -> { - rpcEndpointService.registerRPCEndpoint("unitTests", "getValue", this::getValue); - rpcEndpointService.registerRPCEndpoint("unitTests", "setValue", this::setValue); + rpcEndpointService.registerRPCEndpoint("tests", "getValue", this::getValue); + rpcEndpointService.registerRPCEndpoint("tests", "setValue", this::setValue); rpcEndpointService.registerRPCEndpoint( - "unitTests", "replaceValueList", this::replaceValueList); + "tests", "replaceValueList", this::replaceValueList); rpcEndpointService.registerRPCEndpoint( - "unitTests", "throwException", this::throwException); + "tests", "throwException", this::throwException); + rpcEndpointService.registerRPCEndpoint("notEnabled", "getValue", this::getValue); }); } diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/RpcEndpointServicePluginTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/RpcEndpointServicePluginTest.java index 07c1d28f8eb..0653c0088a0 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/RpcEndpointServicePluginTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/RpcEndpointServicePluginTest.java @@ -21,7 +21,6 @@ import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; import java.io.IOException; -import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -43,9 +42,7 @@ public class RpcEndpointServicePluginTest extends AcceptanceTestBase { @Before public void setUp() throws Exception { - node = - besu.createPluginsNode( - "node1", Collections.singletonList("testPlugins"), Collections.emptyList()); + node = besu.createPluginsNode("node1", List.of("testPlugins"), List.of("--rpc-http-api=TESTS")); cluster.start(node); client = new OkHttpClient(); } @@ -54,34 +51,40 @@ public void setUp() throws Exception { public void canUseRpcToSetValue() throws IOException { String setValue = "secondCall"; - ObjectNode resultJson = callTestMethod("unitTests_getValue", List.of()); + ObjectNode resultJson = callTestMethod("tests_getValue", List.of()); assertThat(resultJson.get("result").asText()).isEqualTo("InitialValue"); - resultJson = callTestMethod("unitTests_setValue", List.of(setValue)); + resultJson = callTestMethod("tests_setValue", List.of(setValue)); assertThat(resultJson.get("result").asText()).isEqualTo(setValue); - resultJson = callTestMethod("unitTests_getValue", List.of("ignored")); + resultJson = callTestMethod("tests_getValue", List.of("ignored")); assertThat(resultJson.get("result").asText()).isEqualTo(setValue); } @Test public void canCheckArgumentInsideSetValue() throws IOException { - ObjectNode resultJson = callTestMethod("unitTests_setValue", List.of("one", "two")); + ObjectNode resultJson = callTestMethod("tests_setValue", List.of("one", "two")); assertThat(resultJson.get("error").get("message").asText()).isEqualTo("Internal error"); } @Test public void canThrowExceptions() throws IOException { - ObjectNode resultJson = callTestMethod("unitTests_throwException", List.of()); + ObjectNode resultJson = callTestMethod("tests_throwException", List.of()); assertThat(resultJson.get("error").get("message").asText()).isEqualTo("Internal error"); } + @Test + public void onlyEnabledMethodsReturn() throws IOException { + ObjectNode resultJson = callTestMethod("notEnabled_getValue", List.of()); + assertThat(resultJson.get("error").get("message").asText()).isEqualTo("Method not found"); + } + @Test public void mixedTypeArraysAreStringified() throws IOException { - ObjectNode resultJson = callTestMethod("unitTests_replaceValueList", List.of()); + ObjectNode resultJson = callTestMethod("tests_replaceValueList", List.of()); assertThat(resultJson.get("result")).isEmpty(); - resultJson = callTestMethod("unitTests_replaceValueList", List.of("One", 2, true)); + resultJson = callTestMethod("tests_replaceValueList", List.of("One", 2, true)); JsonNode result = resultJson.get("result"); assertThat(result.get(0).asText()).isEqualTo("One"); diff --git a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java index 5c3e49b1e02..385423b6456 100644 --- a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java @@ -863,7 +863,7 @@ private Map jsonRpcMethods( dataDir, besuController.getProtocolManager().ethContext().getEthPeers()); methods.putAll(besuController.getAdditionalJsonRpcMethods(jsonRpcApis)); - methods.putAll(rpcEndpointServiceImpl.getPluginMethods()); + methods.putAll(rpcEndpointServiceImpl.getPluginMethods(jsonRpcConfiguration.getRpcApis())); return methods; } @@ -965,6 +965,11 @@ private WebSocketService createWebsocketService( privateWebSocketMethodsFactory.methods().forEach(websocketMethodsFactory::addMethods); } + rpcEndpointServiceImpl + .getPluginMethods(configuration.getRpcApis()) + .values() + .forEach(websocketMethodsFactory::addMethods); + final WebSocketRequestHandler websocketRequestHandler = new WebSocketRequestHandler( vertx, 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 1d33548ece8..4b7f17a6e3d 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -196,6 +196,7 @@ import java.util.Set; import java.util.TreeMap; import java.util.function.Function; +import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -1524,13 +1525,17 @@ private void validateDnsOptionsParams() { } public void validateRpcOptionsParams() { - if (!rpcHttpApis.stream() - .allMatch(x -> Arrays.stream(RpcApis.values()).anyMatch(y -> x.equals(y.name())))) { + Predicate configuredApi = + apiName -> + Arrays.stream(RpcApis.values()) + .anyMatch(builtInApi -> apiName.equals(builtInApi.name())) + || rpcEndpointServiceImpl.hasNamespace(apiName); + + if (!rpcHttpApis.stream().allMatch(configuredApi)) { throw new ParameterException(this.commandLine, "Invalid value for option '--rpc-http-apis'"); } - if (!rpcWsApis.stream() - .allMatch(x -> Arrays.stream(RpcApis.values()).anyMatch(y -> x.equals(y.name())))) { + if (!rpcWsApis.stream().allMatch(configuredApi)) { throw new ParameterException(this.commandLine, "Invalid value for option '--rpc-ws-apis'"); } } 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 c7295b299a9..80c9002ba3e 100644 --- a/besu/src/main/java/org/hyperledger/besu/services/RpcEndpointServiceImpl.java +++ b/besu/src/main/java/org/hyperledger/besu/services/RpcEndpointServiceImpl.java @@ -22,6 +22,7 @@ import org.hyperledger.besu.plugin.services.RpcEndpointService; import org.hyperledger.besu.plugin.services.rpc.PluginRpcRequest; +import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.function.Function; @@ -42,9 +43,21 @@ public void registerRPCEndpoint( rpcMethods.put(namespace + "_" + functionName, function); } - public Map getPluginMethods() { + public Map getPluginMethods( + final Collection namespaces) { return rpcMethods.entrySet().stream() + .filter( + entry -> + namespaces.stream() + .anyMatch( + namespace -> + entry.getKey().toUpperCase().startsWith(namespace.toUpperCase()))) .map(entry -> new PluginJsonRpcMethod(entry.getKey(), entry.getValue())) .collect(Collectors.toMap(PluginJsonRpcMethod::getName, e -> e)); } + + public boolean hasNamespace(final String namespace) { + return rpcMethods.keySet().stream() + .anyMatch(key -> key.toUpperCase().startsWith(namespace.toUpperCase())); + } } 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 752046488be..d7967ba8e04 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -75,6 +75,7 @@ import org.hyperledger.besu.nat.NatMethod; import org.hyperledger.besu.pki.config.PkiKeyStoreConfiguration; import org.hyperledger.besu.plugin.data.EnodeURL; +import org.hyperledger.besu.plugin.services.rpc.PluginRpcRequest; import org.hyperledger.besu.util.number.Fraction; import org.hyperledger.besu.util.number.Percentage; @@ -92,6 +93,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -1926,6 +1928,24 @@ public void rpcApisPropertyWithInvalidEntryMustDisplayError() { .contains("Invalid value for option '--rpc-http-apis'"); } + @Test + public void rpcApisPropertyWithPluginNamespaceAreValid() { + + rpcEndpointServiceImpl.registerRPCEndpoint( + "bob", "method", (Function) request -> "nothing"); + + parseCommand("--rpc-http-api", "BOB"); + + verify(mockRunnerBuilder).jsonRpcConfiguration(jsonRpcConfigArgumentCaptor.capture()); + verify(mockRunnerBuilder).build(); + + assertThat(jsonRpcConfigArgumentCaptor.getValue().getRpcApis()) + .containsExactlyInAnyOrder("BOB"); + + assertThat(commandOutput.toString()).isEmpty(); + assertThat(commandErrorOutput.toString()).isEmpty(); + } + @Test public void rpcHttpHostAndPortOptionsMustBeUsed() { 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 4fa7307eaa6..ae1314c2478 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java @@ -130,6 +130,9 @@ public abstract class CommandTestAbstract { private final List besuCommands = new ArrayList<>(); private KeyPair keyPair; + protected static final RpcEndpointServiceImpl rpcEndpointServiceImpl = + new RpcEndpointServiceImpl(); + @Mock protected RunnerBuilder mockRunnerBuilder; @Mock protected Runner mockRunner; @@ -402,7 +405,7 @@ public static class TestBesuCommand extends BesuCommand { new PermissioningServiceImpl(), new PrivacyPluginServiceImpl(), pkiBlockCreationConfigProvider, - new RpcEndpointServiceImpl()); + rpcEndpointServiceImpl); this.mockNodeKey = mockNodeKey; this.keyPair = keyPair; }