Skip to content

Commit

Permalink
Only enable plugin rpc api when enabled on --rpc-http-api or --rpc-ws…
Browse files Browse the repository at this point in the history
…-apis

Signed-off-by: Antony Denyer <[email protected]>
  • Loading branch information
antonydenyer committed Sep 15, 2021
1 parent 6464221 commit 908bfac
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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();
}
Expand All @@ -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");
Expand Down
7 changes: 6 additions & 1 deletion besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -863,7 +863,7 @@ private Map<String, JsonRpcMethod> jsonRpcMethods(
dataDir,
besuController.getProtocolManager().ethContext().getEthPeers());
methods.putAll(besuController.getAdditionalJsonRpcMethods(jsonRpcApis));
methods.putAll(rpcEndpointServiceImpl.getPluginMethods());
methods.putAll(rpcEndpointServiceImpl.getPluginMethods(jsonRpcConfiguration.getRpcApis()));
return methods;
}

Expand Down Expand Up @@ -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,
Expand Down
13 changes: 9 additions & 4 deletions besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<String> 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'");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -42,9 +43,21 @@ public <T> void registerRPCEndpoint(
rpcMethods.put(namespace + "_" + functionName, function);
}

public Map<String, ? extends JsonRpcMethod> getPluginMethods() {
public Map<String, ? extends JsonRpcMethod> getPluginMethods(
final Collection<String> 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()));
}
}
20 changes: 20 additions & 0 deletions besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;

Expand Down Expand Up @@ -1926,6 +1928,24 @@ public void rpcApisPropertyWithInvalidEntryMustDisplayError() {
.contains("Invalid value for option '--rpc-http-apis'");
}

@Test
public void rpcApisPropertyWithPluginNamespaceAreValid() {

rpcEndpointServiceImpl.registerRPCEndpoint(
"bob", "method", (Function<PluginRpcRequest, Object>) 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() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ public abstract class CommandTestAbstract {
private final List<TestBesuCommand> besuCommands = new ArrayList<>();
private KeyPair keyPair;

protected static final RpcEndpointServiceImpl rpcEndpointServiceImpl =
new RpcEndpointServiceImpl();

@Mock protected RunnerBuilder mockRunnerBuilder;
@Mock protected Runner mockRunner;

Expand Down Expand Up @@ -402,7 +405,7 @@ public static class TestBesuCommand extends BesuCommand {
new PermissioningServiceImpl(),
new PrivacyPluginServiceImpl(),
pkiBlockCreationConfigProvider,
new RpcEndpointServiceImpl());
rpcEndpointServiceImpl);
this.mockNodeKey = mockNodeKey;
this.keyPair = keyPair;
}
Expand Down

0 comments on commit 908bfac

Please sign in to comment.