From 318c41b571ef0844b0e67cd356d9909f28128375 Mon Sep 17 00:00:00 2001 From: usmananwar Date: Wed, 17 Apr 2019 17:37:27 +0900 Subject: [PATCH] PAN-2485#Add net_services json rpc endpoint (#1295) Implemented a JSON RPC method "net_services" that takes no input parameter and returns an object containing a list of enabled services (jsonrpc, p2p, ws, metrics) with "host" and "port", as show below; { "id":1, "jsonrpc": "2.0", "result": { "jsonrpc": {"host": "127.0.0.1", "port":3000}, "p2p":{"host": "127.0.0.1", "port":3000}, "ws":{"host": "127.0.0.1", "port":3000}, "metrics":{"host": "127.0.0.1", "port":3000} } } In case a service is not enabled, that will not be included in the list; { "id":1, "jsonrpc": "2.0", "result": {} } Fixes PAN-2485 --- .../jsonrpc/JsonRpcTestMethodsFactory.java | 10 +- .../jsonrpc/JsonRpcMethodsFactory.java | 22 +- .../jsonrpc/internal/methods/NetServices.java | 82 +++++++ .../AbstractEthJsonRpcHttpServiceTest.java | 10 +- .../JsonRpcHttpServiceHostWhitelistTest.java | 7 +- .../jsonrpc/JsonRpcHttpServiceLoginTest.java | 7 +- .../JsonRpcHttpServiceRpcApisTest.java | 217 +++++++++++++++++- .../jsonrpc/JsonRpcHttpServiceTest.java | 7 +- .../tech/pegasys/pantheon/RunnerBuilder.java | 20 +- 9 files changed, 367 insertions(+), 15 deletions(-) create mode 100644 ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/NetServices.java diff --git a/ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java b/ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java index 9b122e6c95..674daa25ae 100644 --- a/ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java +++ b/ethereum/jsonrpc/src/integration-test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcTestMethodsFactory.java @@ -30,6 +30,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.filter.FilterRepository; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; +import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; import tech.pegasys.pantheon.ethereum.mainnet.HeaderValidationMode; import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; @@ -40,6 +41,7 @@ import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive; import tech.pegasys.pantheon.metrics.MetricsSystem; import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; +import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import java.util.HashSet; import java.util.Map; @@ -88,6 +90,9 @@ public Map methods() { final Optional nodeWhitelistController = Optional.of(mock(NodeLocalConfigPermissioningController.class)); final PrivacyParameters privacyParameters = mock(PrivacyParameters.class); + final JsonRpcConfiguration jsonRpcConfiguration = mock(JsonRpcConfiguration.class); + final WebSocketConfiguration webSocketConfiguration = mock(WebSocketConfiguration.class); + final MetricsConfiguration metricsConfiguration = mock(MetricsConfiguration.class); return new JsonRpcMethodsFactory() .methods( @@ -106,6 +111,9 @@ public Map methods() { accountWhitelistController, nodeWhitelistController, RpcApis.DEFAULT_JSON_RPC_APIS, - privacyParameters); + privacyParameters, + jsonRpcConfiguration, + webSocketConfiguration, + metricsConfiguration); } } diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java index 24f29068c5..4cd5d7e192 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcMethodsFactory.java @@ -70,6 +70,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.NetEnode; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.NetListening; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.NetPeerCount; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.NetServices; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.NetVersion; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.RpcModules; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.TxPoolPantheonTransactions; @@ -94,6 +95,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.processor.TransactionTracer; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.results.BlockResultFactory; +import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; import tech.pegasys.pantheon.ethereum.mainnet.ScheduleBasedBlockHashFunction; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; @@ -104,6 +106,7 @@ import tech.pegasys.pantheon.ethereum.transaction.TransactionSimulator; import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive; import tech.pegasys.pantheon.metrics.MetricsSystem; +import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import java.util.Collection; import java.util.HashMap; @@ -133,7 +136,10 @@ public Map methods( final FilterManager filterManager, final Optional accountsWhitelistController, final Optional nodeWhitelistController, - final PrivacyParameters privacyParameters) { + final PrivacyParameters privacyParameters, + final JsonRpcConfiguration jsonRpcConfiguration, + final WebSocketConfiguration webSocketConfiguration, + final MetricsConfiguration metricsConfiguration) { final BlockchainQueries blockchainQueries = new BlockchainQueries(blockchain, worldStateArchive); return methods( @@ -152,7 +158,10 @@ public Map methods( accountsWhitelistController, nodeWhitelistController, rpcApis, - privacyParameters); + privacyParameters, + jsonRpcConfiguration, + webSocketConfiguration, + metricsConfiguration); } public Map methods( @@ -171,7 +180,10 @@ public Map methods( final Optional accountsWhitelistController, final Optional nodeWhitelistController, final Collection rpcApis, - final PrivacyParameters privacyParameters) { + final PrivacyParameters privacyParameters, + final JsonRpcConfiguration jsonRpcConfiguration, + final WebSocketConfiguration webSocketConfiguration, + final MetricsConfiguration metricsConfiguration) { final Map enabledMethods = new HashMap<>(); if (!rpcApis.isEmpty()) { addMethods(enabledMethods, new RpcModules(rpcApis)); @@ -256,7 +268,9 @@ blockchainQueries, new TransactionTracer(blockReplay), parameter), new NetVersion(protocolSchedule.getChainId()), new NetListening(p2pNetwork), new NetPeerCount(p2pNetwork), - new NetEnode(p2pNetwork)); + new NetEnode(p2pNetwork), + new NetServices( + jsonRpcConfiguration, webSocketConfiguration, p2pNetwork, metricsConfiguration)); } if (rpcApis.contains(RpcApis.WEB3)) { addMethods(enabledMethods, new Web3ClientVersion(clientVersion), new Web3Sha3()); diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/NetServices.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/NetServices.java new file mode 100644 index 0000000000..cbcaabc455 --- /dev/null +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/NetServices.java @@ -0,0 +1,82 @@ +/* + * Copyright 2018 ConsenSys AG. + * + * 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. + */ +package tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods; + +import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse; +import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse; +import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; +import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; + +import com.google.common.collect.ImmutableMap; + +public class NetServices implements JsonRpcMethod { + + private final ImmutableMap> services; + + public NetServices( + final JsonRpcConfiguration jsonRpcConfiguration, + final WebSocketConfiguration webSocketConfiguration, + final P2PNetwork p2pNetwork, + final MetricsConfiguration metricsConfiguration) { + + ImmutableMap.Builder> servicesMapBuilder = + ImmutableMap.builder(); + + if (jsonRpcConfiguration.isEnabled()) { + servicesMapBuilder.put( + "jsonrpc", + createServiceDetailsMap(jsonRpcConfiguration.getHost(), jsonRpcConfiguration.getPort())); + } + if (webSocketConfiguration.isEnabled()) { + servicesMapBuilder.put( + "ws", + createServiceDetailsMap( + webSocketConfiguration.getHost(), webSocketConfiguration.getPort())); + } + if (p2pNetwork.isP2pEnabled()) { + if (p2pNetwork.isP2pEnabled()) { + p2pNetwork + .getLocalEnode() + .ifPresent( + enode -> { + servicesMapBuilder.put( + "p2p", createServiceDetailsMap(enode.getIp(), enode.getListeningPort())); + }); + } + } + if (metricsConfiguration.isEnabled()) { + servicesMapBuilder.put( + "metrics", + createServiceDetailsMap(metricsConfiguration.getHost(), metricsConfiguration.getPort())); + } + + services = servicesMapBuilder.build(); + } + + @Override + public String getName() { + return "net_services"; + } + + @Override + public JsonRpcResponse response(final JsonRpcRequest req) { + return new JsonRpcSuccessResponse(req.getId(), services); + } + + private ImmutableMap createServiceDetailsMap(final String host, final int port) { + return ImmutableMap.of("host", host, "port", String.valueOf(port)); + } +} diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java index e78b638e32..27751c15ae 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/AbstractEthJsonRpcHttpServiceTest.java @@ -38,6 +38,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.filter.FilterRepository; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; +import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; import tech.pegasys.pantheon.ethereum.mainnet.HeaderValidationMode; import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockHashFunction; import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; @@ -49,6 +50,7 @@ import tech.pegasys.pantheon.ethereum.util.RawBlockIterator; import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive; import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; +import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import java.net.URL; import java.nio.file.Paths; @@ -170,6 +172,7 @@ public void setupTest() throws Exception { supportedCapabilities.add(EthProtocol.ETH62); supportedCapabilities.add(EthProtocol.ETH63); + final JsonRpcConfiguration config = JsonRpcConfiguration.createDefault(); final Map methods = new JsonRpcMethodsFactory() .methods( @@ -188,8 +191,11 @@ public void setupTest() throws Exception { Optional.empty(), Optional.empty(), JSON_RPC_APIS, - privacyParameters); - final JsonRpcConfiguration config = JsonRpcConfiguration.createDefault(); + privacyParameters, + config, + mock(WebSocketConfiguration.class), + mock(MetricsConfiguration.class)); + config.setPort(0); service = new JsonRpcHttpService( diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java index 4135b02680..f0fb519a6d 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceHostWhitelistTest.java @@ -25,12 +25,14 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.filter.FilterManager; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; +import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; import tech.pegasys.pantheon.ethereum.permissioning.NodeLocalConfigPermissioningController; import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; +import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import java.io.IOException; import java.util.Arrays; @@ -102,7 +104,10 @@ public void initServerAndClient() throws Exception { Optional.of(mock(AccountWhitelistController.class)), Optional.of(mock(NodeLocalConfigPermissioningController.class)), JSON_RPC_APIS, - mock(PrivacyParameters.class))); + mock(PrivacyParameters.class), + mock(JsonRpcConfiguration.class), + mock(WebSocketConfiguration.class), + mock(MetricsConfiguration.class))); service = createJsonRpcHttpService(); service.start().join(); diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java index 4749fc213c..74dfbae340 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceLoginTest.java @@ -31,10 +31,12 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.Web3ClientVersion; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.Web3Sha3; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; +import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; +import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -132,7 +134,10 @@ public static void initServerAndClient() throws Exception { Optional.empty(), Optional.empty(), JSON_RPC_APIS, - mock(PrivacyParameters.class))); + mock(PrivacyParameters.class), + mock(JsonRpcConfiguration.class), + mock(WebSocketConfiguration.class), + mock(MetricsConfiguration.class))); service = createJsonRpcHttpService(); jwtAuth = service.authenticationService.get().getJwtAuthProvider(); service.start().join(); diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java index e567c50983..3eadb6f9a3 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceRpcApisTest.java @@ -12,12 +12,16 @@ */ package tech.pegasys.pantheon.ethereum.jsonrpc; +import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import tech.pegasys.pantheon.config.StubGenesisConfigOptions; +import tech.pegasys.pantheon.crypto.SECP256K1; import tech.pegasys.pantheon.ethereum.blockcreation.EthHashMiningCoordinator; import tech.pegasys.pantheon.ethereum.core.PrivacyParameters; import tech.pegasys.pantheon.ethereum.core.Synchronizer; @@ -27,14 +31,25 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError; +import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; +import tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.config.NetworkingConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.config.RlpxConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.netty.NettyP2PNetwork; +import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; import tech.pegasys.pantheon.ethereum.permissioning.NodeLocalConfigPermissioningController; import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; +import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -69,6 +84,8 @@ public class JsonRpcHttpServiceRpcApisTest { private static final String CLIENT_VERSION = "TestClientVersion/0.1.0"; private static final int NETWORK_ID = 123; private JsonRpcConfiguration configuration; + private static final List netServices = + new ArrayList<>(Arrays.asList("jsonrpc", "ws", "p2p", "metrics")); @Mock protected static BlockchainQueries blockchainQueries; @@ -186,7 +203,10 @@ private JsonRpcHttpService createJsonRpcHttpServiceWithRpcApis(final JsonRpcConf Optional.of(mock(AccountWhitelistController.class)), Optional.of(mock(NodeLocalConfigPermissioningController.class)), config.getRpcApis(), - mock(PrivacyParameters.class))); + mock(PrivacyParameters.class), + mock(JsonRpcConfiguration.class), + mock(WebSocketConfiguration.class), + mock(MetricsConfiguration.class))); final JsonRpcHttpService jsonRpcHttpService = new JsonRpcHttpService( vertx, folder.newFolder().toPath(), config, new NoOpMetricsSystem(), rpcMethods); @@ -199,4 +219,199 @@ private JsonRpcHttpService createJsonRpcHttpServiceWithRpcApis(final JsonRpcConf private Request buildRequest(final RequestBody body) { return new Request.Builder().post(body).url(baseUrl).build(); } + + private JsonRpcConfiguration createJsonRpcConfiguration() { + final JsonRpcConfiguration config = JsonRpcConfiguration.createDefault(); + config.setEnabled(true); + return config; + } + + private WebSocketConfiguration createWebSocketConfiguration() { + final WebSocketConfiguration config = WebSocketConfiguration.createDefault(); + config.setEnabled(true); + return config; + } + + private P2PNetwork createP2pNetwork() { + final SECP256K1.KeyPair keyPair = SECP256K1.KeyPair.generate(); + final NetworkingConfiguration config = + NetworkingConfiguration.create() + .setRlpx(RlpxConfiguration.create().setBindPort(0)) + .setDiscovery(DiscoveryConfiguration.create().setBindPort(0)); + + final NettyP2PNetwork p2pNetwork = + new NettyP2PNetwork( + vertx, + keyPair, + config, + emptyList(), + new PeerBlacklist(), + new NoOpMetricsSystem(), + Optional.empty(), + Optional.empty()); + p2pNetwork.start(); + return p2pNetwork; + } + + private MetricsConfiguration createMetricsConfiguration() { + final MetricsConfiguration config = MetricsConfiguration.createDefault(); + config.setEnabled(true); + return config; + } + + private JsonRpcHttpService createJsonRpcHttpService( + final JsonRpcConfiguration jsonRpcConfiguration, + final WebSocketConfiguration webSocketConfiguration, + final P2PNetwork p2pNetwork, + final MetricsConfiguration metricsConfiguration) + throws Exception { + final Set supportedCapabilities = new HashSet<>(); + supportedCapabilities.add(EthProtocol.ETH62); + supportedCapabilities.add(EthProtocol.ETH63); + + final Map rpcMethods = + spy( + new JsonRpcMethodsFactory() + .methods( + CLIENT_VERSION, + NETWORK_ID, + new StubGenesisConfigOptions(), + p2pNetwork, + blockchainQueries, + mock(Synchronizer.class), + MainnetProtocolSchedule.create(), + mock(FilterManager.class), + mock(TransactionPool.class), + mock(EthHashMiningCoordinator.class), + new NoOpMetricsSystem(), + supportedCapabilities, + Optional.of(mock(AccountWhitelistController.class)), + Optional.of(mock(NodeLocalConfigPermissioningController.class)), + jsonRpcConfiguration.getRpcApis(), + mock(PrivacyParameters.class), + jsonRpcConfiguration, + webSocketConfiguration, + metricsConfiguration)); + final JsonRpcHttpService jsonRpcHttpService = + new JsonRpcHttpService( + vertx, + folder.newFolder().toPath(), + jsonRpcConfiguration, + new NoOpMetricsSystem(), + rpcMethods); + jsonRpcHttpService.start().join(); + + baseUrl = jsonRpcHttpService.url(); + return jsonRpcHttpService; + } + + @Test + public void netServicesTestWhenJsonrpcWebsocketP2pNetworkAndMatricesIsEnabled() throws Exception { + boolean[] servicesStates = new boolean[netServices.size()]; + Arrays.fill(servicesStates, Boolean.TRUE); // All services are enabled + service = getJsonRpcHttpService(servicesStates); + + final RequestBody body = createNetServicesRequestBody(); + + try (final Response resp = client.newCall(buildRequest(body)).execute()) { + + final JsonObject responseBody = new JsonObject(resp.body().string()); + for (int j = 0; j < netServices.size(); j++) { + assertNetService(servicesStates, responseBody, netServices.get(j)); + } + } + } + + @Test + public void netServicesTestWhenOneIsEnabled() throws Exception { + + for (int i = 0; i < netServices.size(); i++) { + + boolean[] servicesStates = new boolean[netServices.size()]; + int enabledServiceIndex = i % netServices.size(); + servicesStates[enabledServiceIndex] = true; // enable only one service at a time + service = getJsonRpcHttpService(servicesStates); + + final RequestBody body = createNetServicesRequestBody(); + try (final Response resp = client.newCall(buildRequest(body)).execute()) { + final JsonObject responseBody = new JsonObject(resp.body().string()); + + for (int j = 0; j < netServices.size(); j++) { + assertNetService(servicesStates, responseBody, netServices.get(j)); + } + } + service.stop().join(); + } + } + + private void assertNetService( + final boolean[] servicesStates, final JsonObject jsonBody, final String serviceName) + throws IOException { + + boolean isAssertTrue = servicesStates[netServices.indexOf(serviceName)]; + + final JsonObject result = jsonBody.getJsonObject("result"); + final JsonObject serviceElement = result.getJsonObject(serviceName); + if (isAssertTrue) { + assertTrue( + serviceElement != null + && serviceElement.containsKey("host") + && serviceElement.containsKey("port")); + } else { + assertFalse( + serviceElement != null + && serviceElement.containsKey("host") + && serviceElement.containsKey("port")); + } + } + + public RequestBody createNetServicesRequestBody() { + String id = "123"; + return RequestBody.create( + JSON, "{\"jsonrpc\":\"2.0\",\"id\":" + Json.encode(id) + ",\"method\":\"net_services\"}"); + } + + public JsonRpcHttpService getJsonRpcHttpService(final boolean[] enabledNetServices) + throws Exception { + + JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault(); + WebSocketConfiguration webSocketConfiguration = WebSocketConfiguration.createDefault(); + P2PNetwork p2pNetwork = mock(P2PNetwork.class); + MetricsConfiguration metricsConfiguration = MetricsConfiguration.createDefault(); + + if (enabledNetServices[netServices.indexOf("jsonrpc")]) { + jsonRpcConfiguration = createJsonRpcConfiguration(); + } + + if (enabledNetServices[netServices.indexOf("ws")]) { + webSocketConfiguration = createWebSocketConfiguration(); + } + if (enabledNetServices[netServices.indexOf("p2p")]) { + p2pNetwork = createP2pNetwork(); + } + if (enabledNetServices[netServices.indexOf("metrics")]) { + metricsConfiguration = createMetricsConfiguration(); + } + + return createJsonRpcHttpService( + jsonRpcConfiguration, webSocketConfiguration, p2pNetwork, metricsConfiguration); + } + + @Test + public void netServicesTestWhenJsonrpcWebsocketP2pNetworkAndMatricesIsDisabled() + throws Exception { + service = + createJsonRpcHttpService( + JsonRpcConfiguration.createDefault(), + WebSocketConfiguration.createDefault(), + mock(P2PNetwork.class), + MetricsConfiguration.createDefault()); + final RequestBody body = createNetServicesRequestBody(); + + try (final Response resp = client.newCall(buildRequest(body)).execute()) { + final JsonObject json = new JsonObject(resp.body().string()); + final JsonObject result = json.getJsonObject("result"); + assertTrue(result.isEmpty()); + } + } } diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java index e4e870f6f8..732fb3183b 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/JsonRpcHttpServiceTest.java @@ -40,12 +40,14 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.BlockchainQueries; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.queries.TransactionWithMetadata; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcError; +import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; import tech.pegasys.pantheon.ethereum.permissioning.NodeLocalConfigPermissioningController; import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; +import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.bytes.BytesValues; import tech.pegasys.pantheon.util.uint.UInt256; @@ -130,7 +132,10 @@ public static void initServerAndClient() throws Exception { Optional.of(mock(AccountWhitelistController.class)), Optional.of(mock(NodeLocalConfigPermissioningController.class)), JSON_RPC_APIS, - mock(PrivacyParameters.class))); + mock(PrivacyParameters.class), + mock(JsonRpcConfiguration.class), + mock(WebSocketConfiguration.class), + mock(MetricsConfiguration.class))); service = createJsonRpcHttpService(); service.start().join(); diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java index 7f116e4875..bb567a8ff0 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java @@ -340,7 +340,10 @@ public Runner build() { filterManager, accountWhitelistController, nodeWhitelistController, - privacyParameters); + privacyParameters, + jsonRpcConfiguration, + webSocketConfiguration, + metricsConfiguration); jsonRpcHttpService = Optional.of( new JsonRpcHttpService( @@ -364,7 +367,10 @@ public Runner build() { filterManager, accountWhitelistController, nodeWhitelistController, - privacyParameters); + privacyParameters, + jsonRpcConfiguration, + webSocketConfiguration, + metricsConfiguration); final SubscriptionManager subscriptionManager = createSubscriptionManager(vertx, transactionPool); @@ -439,7 +445,10 @@ private Map jsonRpcMethods( final FilterManager filterManager, final Optional accountWhitelistController, final Optional nodeWhitelistController, - final PrivacyParameters privacyParameters) { + final PrivacyParameters privacyParameters, + final JsonRpcConfiguration jsonRpcConfiguration, + final WebSocketConfiguration webSocketConfiguration, + final MetricsConfiguration metricsConfiguration) { final Map methods = new JsonRpcMethodsFactory() .methods( @@ -459,7 +468,10 @@ private Map jsonRpcMethods( filterManager, accountWhitelistController, nodeWhitelistController, - privacyParameters); + privacyParameters, + jsonRpcConfiguration, + webSocketConfiguration, + metricsConfiguration); methods.putAll(pantheonController.getAdditionalJsonRpcMethods(jsonRpcApis)); return methods; }