From 2722aca1af21046401c05f61be98c211468aa188 Mon Sep 17 00:00:00 2001 From: Lucas Saldanha Date: Thu, 20 Dec 2018 15:19:03 +1300 Subject: [PATCH] Nodes whitelist acceptance test (#472) * Refactoring PantheonNodeFactory and adding PermissioningConfiguration * Created ClusterConfiguration * Acceptance test for nodes whitelist * Fixing constructor --- acceptance-tests/build.gradle | 1 + .../acceptance/dsl/AcceptanceTestBase.java | 4 +- .../acceptance/dsl/node/PantheonNode.java | 8 + .../dsl/node/ProcessPantheonNodeRunner.java | 8 + .../dsl/node/ThreadPantheonNodeRunner.java | 1 + .../dsl/node/{ => cluster}/Cluster.java | 18 +- .../node/cluster/ClusterConfiguration.java | 26 +++ .../cluster/ClusterConfigurationBuilder.java | 26 +++ .../factory/PantheonFactoryConfiguration.java | 75 +++++++ .../PantheonFactoryConfigurationBuilder.java | 112 ++++++++++ .../{ => factory}/PantheonNodeFactory.java | 204 ++++++++---------- .../NodesWhitelistAcceptanceTest.java | 65 ++++++ 12 files changed, 425 insertions(+), 123 deletions(-) rename acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/{ => cluster}/Cluster.java (80%) create mode 100644 acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/cluster/ClusterConfiguration.java create mode 100644 acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/cluster/ClusterConfigurationBuilder.java create mode 100644 acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfiguration.java create mode 100644 acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfigurationBuilder.java rename acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/{ => factory}/PantheonNodeFactory.java (56%) create mode 100644 acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/permissioning/NodesWhitelistAcceptanceTest.java diff --git a/acceptance-tests/build.gradle b/acceptance-tests/build.gradle index 8d589c48cf..20bca6a1cc 100644 --- a/acceptance-tests/build.gradle +++ b/acceptance-tests/build.gradle @@ -23,6 +23,7 @@ dependencies { testImplementation project(':ethereum:core') testImplementation project(':ethereum:blockcreation') testImplementation project(':ethereum:jsonrpc') + testImplementation project(':ethereum:p2p') testImplementation project(':metrics') testImplementation project(':pantheon') testImplementation project(':util') diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/AcceptanceTestBase.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/AcceptanceTestBase.java index 516346fded..46751edfb3 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/AcceptanceTestBase.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/AcceptanceTestBase.java @@ -19,8 +19,8 @@ import tech.pegasys.pantheon.tests.acceptance.dsl.contract.ContractVerifier; import tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc.Eth; import tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc.Net; import tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc.Web3; -import tech.pegasys.pantheon.tests.acceptance.dsl.node.Cluster; -import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNodeFactory; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.cluster.Cluster; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.factory.PantheonNodeFactory; import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transactions; import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.clique.CliqueTransactions; import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.eth.EthTransactions; diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNode.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNode.java index 8fd16d7d3c..ded13b8c2e 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNode.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNode.java @@ -22,6 +22,7 @@ import tech.pegasys.pantheon.ethereum.core.MiningParameters; import tech.pegasys.pantheon.ethereum.core.Util; import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition; import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.PantheonWeb3j; import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction; @@ -68,6 +69,7 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto private final MiningParameters miningParameters; private final JsonRpcConfiguration jsonRpcConfiguration; private final WebSocketConfiguration webSocketConfiguration; + private final PermissioningConfiguration permissioningConfiguration; private final GenesisConfigProvider genesisConfigProvider; private final boolean devMode; @@ -80,6 +82,7 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto final MiningParameters miningParameters, final JsonRpcConfiguration jsonRpcConfiguration, final WebSocketConfiguration webSocketConfiguration, + final PermissioningConfiguration permissioningConfiguration, final boolean devMode, final GenesisConfigProvider genesisConfigProvider, final int p2pPort) @@ -91,6 +94,7 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto this.miningParameters = miningParameters; this.jsonRpcConfiguration = jsonRpcConfiguration; this.webSocketConfiguration = webSocketConfiguration; + this.permissioningConfiguration = permissioningConfiguration; this.genesisConfigProvider = genesisConfigProvider; this.devMode = devMode; LOG.info("Created PantheonNode {}", this.toString()); @@ -314,6 +318,10 @@ public class PantheonNode implements Node, NodeConfiguration, RunnableNode, Auto return devMode; } + PermissioningConfiguration getPermissioningConfiguration() { + return permissioningConfiguration; + } + @Override public String toString() { return MoreObjects.toStringHelper(this) diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ProcessPantheonNodeRunner.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ProcessPantheonNodeRunner.java index 3f0ffa60bc..00f50b2588 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ProcessPantheonNodeRunner.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ProcessPantheonNodeRunner.java @@ -17,6 +17,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import tech.pegasys.pantheon.cli.EthNetworkConfig; import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApi; import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; import java.io.File; import java.io.IOException; @@ -69,6 +70,13 @@ public class ProcessPantheonNodeRunner implements PantheonNodeRunner { params.add("--bootnodes"); params.add(String.join(",", node.bootnodes().toString())); + final PermissioningConfiguration permissioningConfiguration = + node.getPermissioningConfiguration(); + if (permissioningConfiguration.isNodeWhitelistSet()) { + params.add("--nodes-whitelist"); + params.add(String.join(",", permissioningConfiguration.getNodeWhitelist().toString())); + } + if (node.jsonRpcEnabled()) { params.add("--rpc-enabled"); params.add("--rpc-listen"); diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java index 81d66cbd32..e164c6264b 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java @@ -86,6 +86,7 @@ public class ThreadPantheonNodeRunner implements PantheonNodeRunner { .dataDir(node.homeDirectory()) .bannedNodeIds(Collections.emptySet()) .metricsSystem(noOpMetricsSystem) + .permissioningConfiguration(node.getPermissioningConfiguration()) .build(); nodeExecutor.submit(runner::execute); diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/Cluster.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/cluster/Cluster.java similarity index 80% rename from acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/Cluster.java rename to acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/cluster/Cluster.java index 26e3db8a62..d25eb3aa9f 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/Cluster.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/cluster/Cluster.java @@ -10,13 +10,17 @@ * 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.tests.acceptance.dsl.node; +package tech.pegasys.pantheon.tests.acceptance.dsl.node.cluster; import static org.assertj.core.api.Assertions.assertThat; import tech.pegasys.pantheon.cli.EthNetworkConfig; import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition; import tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc.Net; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNodeRunner; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.RunnableNode; import tech.pegasys.pantheon.tests.acceptance.dsl.waitcondition.WaitCondition; import java.util.ArrayList; @@ -33,8 +37,14 @@ public class Cluster implements AutoCloseable { private final Map nodes = new HashMap<>(); private final PantheonNodeRunner pantheonNodeRunner = PantheonNodeRunner.instance(); private final Net net; + private final ClusterConfiguration clusterConfiguration; public Cluster(final Net net) { + this(new ClusterConfigurationBuilder().build(), net); + } + + public Cluster(final ClusterConfiguration clusterConfiguration, final Net net) { + this.clusterConfiguration = clusterConfiguration; this.net = net; } @@ -70,8 +80,10 @@ public class Cluster implements AutoCloseable { node.start(pantheonNodeRunner); } - for (final RunnableNode node : nodes) { - node.awaitPeerDiscovery(net.awaitPeerCount(nodes.size() - 1)); + if (clusterConfiguration.isAwaitPeerDiscovery()) { + for (final RunnableNode node : nodes) { + node.awaitPeerDiscovery(net.awaitPeerCount(nodes.size() - 1)); + } } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/cluster/ClusterConfiguration.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/cluster/ClusterConfiguration.java new file mode 100644 index 0000000000..ad5f19c788 --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/cluster/ClusterConfiguration.java @@ -0,0 +1,26 @@ +/* + * 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.tests.acceptance.dsl.node.cluster; + +public class ClusterConfiguration { + + private final boolean awaitPeerDiscovery; + + ClusterConfiguration(final boolean awaitPeerDiscovery) { + this.awaitPeerDiscovery = awaitPeerDiscovery; + } + + public boolean isAwaitPeerDiscovery() { + return awaitPeerDiscovery; + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/cluster/ClusterConfigurationBuilder.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/cluster/ClusterConfigurationBuilder.java new file mode 100644 index 0000000000..829b56d842 --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/cluster/ClusterConfigurationBuilder.java @@ -0,0 +1,26 @@ +/* + * 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.tests.acceptance.dsl.node.cluster; + +public class ClusterConfigurationBuilder { + private boolean awaitPeerDiscovery = true; + + public ClusterConfigurationBuilder setAwaitPeerDiscovery(final boolean awaitPeerDiscovery) { + this.awaitPeerDiscovery = awaitPeerDiscovery; + return this; + } + + public ClusterConfiguration build() { + return new ClusterConfiguration(awaitPeerDiscovery); + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfiguration.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfiguration.java new file mode 100644 index 0000000000..cd2dde68eb --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfiguration.java @@ -0,0 +1,75 @@ +/* + * 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.tests.acceptance.dsl.node.factory; + +import tech.pegasys.pantheon.ethereum.core.MiningParameters; +import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration; +import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.GenesisConfigProvider; + +class PantheonFactoryConfiguration { + + private final String name; + private final MiningParameters miningParameters; + private final JsonRpcConfiguration jsonRpcConfiguration; + private final WebSocketConfiguration webSocketConfiguration; + private final PermissioningConfiguration permissioningConfiguration; + private final boolean devMode; + private final GenesisConfigProvider genesisConfigProvider; + + PantheonFactoryConfiguration( + final String name, + final MiningParameters miningParameters, + final JsonRpcConfiguration jsonRpcConfiguration, + final WebSocketConfiguration webSocketConfiguration, + final PermissioningConfiguration permissioningConfiguration, + final boolean devMode, + final GenesisConfigProvider genesisConfigProvider) { + this.name = name; + this.miningParameters = miningParameters; + this.jsonRpcConfiguration = jsonRpcConfiguration; + this.webSocketConfiguration = webSocketConfiguration; + this.permissioningConfiguration = permissioningConfiguration; + this.devMode = devMode; + this.genesisConfigProvider = genesisConfigProvider; + } + + public String getName() { + return name; + } + + public MiningParameters getMiningParameters() { + return miningParameters; + } + + public JsonRpcConfiguration getJsonRpcConfiguration() { + return jsonRpcConfiguration; + } + + public WebSocketConfiguration getWebSocketConfiguration() { + return webSocketConfiguration; + } + + public PermissioningConfiguration getPermissioningConfiguration() { + return permissioningConfiguration; + } + + public boolean isDevMode() { + return devMode; + } + + public GenesisConfigProvider getGenesisConfigProvider() { + return genesisConfigProvider; + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfigurationBuilder.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfigurationBuilder.java new file mode 100644 index 0000000000..a7ff09bf2c --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfigurationBuilder.java @@ -0,0 +1,112 @@ +/* + * 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.tests.acceptance.dsl.node.factory; + +import static java.util.Collections.singletonList; + +import tech.pegasys.pantheon.ethereum.core.MiningParameters; +import tech.pegasys.pantheon.ethereum.core.MiningParametersTestBuilder; +import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration; +import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.GenesisConfigProvider; + +import java.util.Optional; + +public class PantheonFactoryConfigurationBuilder { + + private String name; + private MiningParameters miningParameters = + new MiningParametersTestBuilder().enabled(false).build(); + private JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault(); + private WebSocketConfiguration webSocketConfiguration = WebSocketConfiguration.createDefault(); + private PermissioningConfiguration permissioningConfiguration = + PermissioningConfiguration.createDefault(); + private boolean devMode = true; + private GenesisConfigProvider genesisConfigProvider = ignore -> Optional.empty(); + + public PantheonFactoryConfigurationBuilder setName(final String name) { + this.name = name; + return this; + } + + public PantheonFactoryConfigurationBuilder setMiningParameters( + final MiningParameters miningParameters) { + this.miningParameters = miningParameters; + return this; + } + + public PantheonFactoryConfigurationBuilder miningEnabled() { + this.miningParameters = new MiningParametersTestBuilder().enabled(true).build(); + return this; + } + + public PantheonFactoryConfigurationBuilder setJsonRpcConfiguration( + final JsonRpcConfiguration jsonRpcConfiguration) { + this.jsonRpcConfiguration = jsonRpcConfiguration; + return this; + } + + public PantheonFactoryConfigurationBuilder jsonRpcEnabled() { + final JsonRpcConfiguration config = JsonRpcConfiguration.createDefault(); + config.setEnabled(true); + config.setPort(0); + config.setHostsWhitelist(singletonList("*")); + + this.jsonRpcConfiguration = config; + return this; + } + + public PantheonFactoryConfigurationBuilder setWebSocketConfiguration( + final WebSocketConfiguration webSocketConfiguration) { + this.webSocketConfiguration = webSocketConfiguration; + return this; + } + + public PantheonFactoryConfigurationBuilder webSocketEnabled() { + final WebSocketConfiguration config = WebSocketConfiguration.createDefault(); + config.setEnabled(true); + config.setPort(0); + + this.webSocketConfiguration = config; + return this; + } + + public PantheonFactoryConfigurationBuilder setPermissioningConfiguration( + final PermissioningConfiguration permissioningConfiguration) { + this.permissioningConfiguration = permissioningConfiguration; + return this; + } + + public PantheonFactoryConfigurationBuilder setDevMode(final boolean devMode) { + this.devMode = devMode; + return this; + } + + public PantheonFactoryConfigurationBuilder setGenesisConfigProvider( + final GenesisConfigProvider genesisConfigProvider) { + this.genesisConfigProvider = genesisConfigProvider; + return this; + } + + public PantheonFactoryConfiguration build() { + return new PantheonFactoryConfiguration( + name, + miningParameters, + jsonRpcConfiguration, + webSocketConfiguration, + permissioningConfiguration, + devMode, + genesisConfigProvider); + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNodeFactory.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java similarity index 56% rename from acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNodeFactory.java rename to acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java index 0e842b0aba..7878faff57 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNodeFactory.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java @@ -10,7 +10,7 @@ * 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.tests.acceptance.dsl.node; +package tech.pegasys.pantheon.tests.acceptance.dsl.node.factory; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; @@ -19,11 +19,12 @@ import static tech.pegasys.pantheon.consensus.clique.jsonrpc.CliqueRpcApis.CLIQU import tech.pegasys.pantheon.consensus.clique.CliqueExtraData; import tech.pegasys.pantheon.ethereum.core.Address; -import tech.pegasys.pantheon.ethereum.core.MiningParameters; -import tech.pegasys.pantheon.ethereum.core.MiningParametersTestBuilder; import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration; import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApi; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.RunnableNode; import java.io.IOException; import java.net.ServerSocket; @@ -47,6 +48,7 @@ public class PantheonNodeFactory { config.getMiningParameters(), config.getJsonRpcConfiguration(), config.getWebSocketConfiguration(), + config.getPermissioningConfiguration(), config.isDevMode(), config.getGenesisConfigProvider(), serverSocket.getLocalPort()); @@ -57,76 +59,94 @@ public class PantheonNodeFactory { public PantheonNode createMinerNode(final String name) throws IOException { return create( - new PantheonFactoryConfiguration( - name, createMiningParameters(true), createJsonRpcConfig(), createWebSocketConfig())); + new PantheonFactoryConfigurationBuilder() + .setName(name) + .miningEnabled() + .jsonRpcEnabled() + .webSocketEnabled() + .build()); } public PantheonNode createMinerNodeWithCustomRefreshDelay( final String name, final Long refreshDelay) throws IOException { - WebSocketConfiguration wsConfig = createWebSocketConfig(); + + WebSocketConfiguration wsConfig = createWebSocketEnabledConfig(); wsConfig.setRefreshDelay(refreshDelay); + return create( - new PantheonFactoryConfiguration( - name, createMiningParameters(true), createJsonRpcConfig(), wsConfig)); + new PantheonFactoryConfigurationBuilder() + .setName(name) + .miningEnabled() + .setJsonRpcConfiguration(createJsonRpcEnabledConfig()) + .setWebSocketConfiguration(wsConfig) + .build()); } public PantheonNode createArchiveNode(final String name) throws IOException { return create( - new PantheonFactoryConfiguration( - name, createMiningParameters(false), createJsonRpcConfig(), createWebSocketConfig())); + new PantheonFactoryConfigurationBuilder() + .setName(name) + .jsonRpcEnabled() + .webSocketEnabled() + .build()); } public PantheonNode createArchiveNodeWithCustomRefreshDelay( final String name, final Long refreshDelay) throws IOException { - WebSocketConfiguration wsConfig = createWebSocketConfig(); + WebSocketConfiguration wsConfig = createWebSocketEnabledConfig(); wsConfig.setRefreshDelay(refreshDelay); + return create( - new PantheonFactoryConfiguration( - name, createMiningParameters(false), createJsonRpcConfig(), wsConfig)); + new PantheonFactoryConfigurationBuilder() + .setName(name) + .setJsonRpcConfiguration(createJsonRpcEnabledConfig()) + .setWebSocketConfiguration(wsConfig) + .build()); } public PantheonNode createArchiveNodeWithRpcDisabled(final String name) throws IOException { - return create( - new PantheonFactoryConfiguration( - name, - createMiningParameters(false), - JsonRpcConfiguration.createDefault(), - WebSocketConfiguration.createDefault())); + return create(new PantheonFactoryConfigurationBuilder().setName(name).build()); } public PantheonNode createArchiveNodeWithRpcApis( final String name, final RpcApi... enabledRpcApis) throws IOException { - final JsonRpcConfiguration jsonRpcConfig = createJsonRpcConfig(); + final JsonRpcConfiguration jsonRpcConfig = createJsonRpcEnabledConfig(); jsonRpcConfig.setRpcApis(asList(enabledRpcApis)); - final WebSocketConfiguration webSocketConfig = createWebSocketConfig(); + final WebSocketConfiguration webSocketConfig = createWebSocketEnabledConfig(); webSocketConfig.setRpcApis(asList(enabledRpcApis)); return create( - new PantheonFactoryConfiguration( - name, createMiningParameters(false), jsonRpcConfig, webSocketConfig)); + new PantheonFactoryConfigurationBuilder() + .setName(name) + .setJsonRpcConfiguration(jsonRpcConfig) + .setWebSocketConfiguration(webSocketConfig) + .build()); } - public PantheonNode createCliqueNode(final String name) throws IOException { + public PantheonNode createNodeWithNodesWhitelist( + final String name, final List nodesWhitelist) throws IOException { + PermissioningConfiguration permissioningConfiguration = + PermissioningConfiguration.createDefault(); + permissioningConfiguration.setNodeWhitelist(nodesWhitelist); + return create( - new PantheonFactoryConfiguration( - name, - createMiningParameters(true), - jsonRpcConfigWithClique(), - createWebSocketConfig(), - false, - this::createCliqueGenesisConfig)); + new PantheonFactoryConfigurationBuilder() + .setName(name) + .jsonRpcEnabled() + .setPermissioningConfiguration(permissioningConfiguration) + .build()); } - public PantheonNode createCliqueNodeWithValidators(final String name, final String... validators) - throws IOException { + public PantheonNode createCliqueNode(final String name) throws IOException { return create( - new PantheonFactoryConfiguration( - name, - createMiningParameters(true), - jsonRpcConfigWithClique(), - createWebSocketConfig(), - false, - nodes -> createCliqueGenesisConfigForValidators(asList(validators), nodes))); + new PantheonFactoryConfigurationBuilder() + .setName(name) + .miningEnabled() + .setJsonRpcConfiguration(jsonRpcConfigWithClique()) + .setWebSocketConfiguration(createWebSocketEnabledConfig()) + .setDevMode(false) + .setGenesisConfigProvider(this::createCliqueGenesisConfig) + .build()); } private Optional createCliqueGenesisConfig(final Collection validators) { @@ -136,11 +156,9 @@ public class PantheonNodeFactory { return Optional.of(genesis); } - private Optional createCliqueGenesisConfigForValidators( - final Collection validators, final Collection pantheonNodes) { - List collect = - pantheonNodes.stream().filter(n -> validators.contains(n.getName())).collect(toList()); - return createCliqueGenesisConfig(collect); + private String encodeCliqueExtraData(final Collection nodes) { + final List
addresses = nodes.stream().map(RunnableNode::getAddress).collect(toList()); + return CliqueExtraData.createGenesisExtraDataString(addresses); } private String cliqueGenesisTemplateConfig() { @@ -152,24 +170,37 @@ public class PantheonNodeFactory { } } - private String encodeCliqueExtraData(final Collection nodes) { - final List
addresses = nodes.stream().map(RunnableNode::getAddress).collect(toList()); - return CliqueExtraData.createGenesisExtraDataString(addresses); + public PantheonNode createCliqueNodeWithValidators(final String name, final String... validators) + throws IOException { + + return create( + new PantheonFactoryConfigurationBuilder() + .setName(name) + .miningEnabled() + .setJsonRpcConfiguration(jsonRpcConfigWithClique()) + .setWebSocketConfiguration(createWebSocketEnabledConfig()) + .setDevMode(false) + .setGenesisConfigProvider( + nodes -> createCliqueGenesisConfigForValidators(asList(validators), nodes)) + .build()); + } + + private Optional createCliqueGenesisConfigForValidators( + final Collection validators, final Collection pantheonNodes) { + List collect = + pantheonNodes.stream().filter(n -> validators.contains(n.getName())).collect(toList()); + return createCliqueGenesisConfig(collect); } private JsonRpcConfiguration jsonRpcConfigWithClique() { - final JsonRpcConfiguration jsonRpcConfig = createJsonRpcConfig(); + final JsonRpcConfiguration jsonRpcConfig = createJsonRpcEnabledConfig(); final List rpcApis = new ArrayList<>(jsonRpcConfig.getRpcApis()); rpcApis.add(CLIQUE); jsonRpcConfig.setRpcApis(rpcApis); return jsonRpcConfig; } - private MiningParameters createMiningParameters(final boolean miner) { - return new MiningParametersTestBuilder().enabled(miner).build(); - } - - private JsonRpcConfiguration createJsonRpcConfig() { + private JsonRpcConfiguration createJsonRpcEnabledConfig() { final JsonRpcConfiguration config = JsonRpcConfiguration.createDefault(); config.setEnabled(true); config.setPort(0); @@ -177,73 +208,10 @@ public class PantheonNodeFactory { return config; } - private WebSocketConfiguration createWebSocketConfig() { + private WebSocketConfiguration createWebSocketEnabledConfig() { final WebSocketConfiguration config = WebSocketConfiguration.createDefault(); config.setEnabled(true); config.setPort(0); return config; } - - static class PantheonFactoryConfiguration { - - private final String name; - private final MiningParameters miningParameters; - private final JsonRpcConfiguration jsonRpcConfiguration; - private final WebSocketConfiguration webSocketConfiguration; - private final boolean devMode; - private final GenesisConfigProvider genesisConfigProvider; - - public PantheonFactoryConfiguration( - final String name, - final MiningParameters miningParameters, - final JsonRpcConfiguration jsonRpcConfiguration, - final WebSocketConfiguration webSocketConfiguration) { - this( - name, - miningParameters, - jsonRpcConfiguration, - webSocketConfiguration, - true, - ignore -> Optional.empty()); - } - - public PantheonFactoryConfiguration( - final String name, - final MiningParameters miningParameters, - final JsonRpcConfiguration jsonRpcConfiguration, - final WebSocketConfiguration webSocketConfiguration, - final boolean devMode, - final GenesisConfigProvider genesisConfigProvider) { - this.name = name; - this.miningParameters = miningParameters; - this.jsonRpcConfiguration = jsonRpcConfiguration; - this.webSocketConfiguration = webSocketConfiguration; - this.devMode = devMode; - this.genesisConfigProvider = genesisConfigProvider; - } - - public String getName() { - return name; - } - - public MiningParameters getMiningParameters() { - return miningParameters; - } - - public JsonRpcConfiguration getJsonRpcConfiguration() { - return jsonRpcConfiguration; - } - - public WebSocketConfiguration getWebSocketConfiguration() { - return webSocketConfiguration; - } - - public boolean isDevMode() { - return devMode; - } - - public GenesisConfigProvider getGenesisConfigProvider() { - return genesisConfigProvider; - } - } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/permissioning/NodesWhitelistAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/permissioning/NodesWhitelistAcceptanceTest.java new file mode 100644 index 0000000000..de347cdce2 --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/permissioning/NodesWhitelistAcceptanceTest.java @@ -0,0 +1,65 @@ +/* + * 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.tests.acceptance.permissioning; + +import tech.pegasys.pantheon.tests.acceptance.dsl.AcceptanceTestBase; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.cluster.Cluster; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.cluster.ClusterConfiguration; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.cluster.ClusterConfigurationBuilder; + +import java.net.URI; +import java.util.Collections; + +import org.junit.Before; +import org.junit.Test; + +public class NodesWhitelistAcceptanceTest extends AcceptanceTestBase { + + private Cluster permissionedCluster; + private Node forbiddenNode; + private Node allowedNode; + private Node permissionedNode; + + @Before + public void setUp() throws Exception { + final ClusterConfiguration clusterConfiguration = + new ClusterConfigurationBuilder().setAwaitPeerDiscovery(false).build(); + + permissionedCluster = new Cluster(clusterConfiguration, net); + forbiddenNode = pantheon.createArchiveNode("forbidden-node"); + allowedNode = pantheon.createArchiveNode("allowed-node"); + permissionedNode = + pantheon.createNodeWithNodesWhitelist( + "permissioned-node", Collections.singletonList(getEnodeURI(allowedNode))); + permissionedCluster.start(allowedNode, forbiddenNode, permissionedNode); + } + + @Test + public void permissionedNodeShouldDiscoverOnlyAllowedNode() { + allowedNode.verify(net.awaitPeerCount(2)); + forbiddenNode.verify(net.awaitPeerCount(1)); + permissionedNode.verify(net.awaitPeerCount(1)); + } + + private URI getEnodeURI(final Node node) { + return URI.create(((PantheonNode) node).getConfiguration().enodeUrl()); + } + + @Override + public void tearDownAcceptanceTestBase() { + permissionedCluster.stop(); + super.tearDownAcceptanceTestBase(); + } +}