From c8e1c07d7c6474a49afd72ee70938322e74b7e8f Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Thu, 6 Dec 2018 11:59:53 +1000 Subject: [PATCH] NC-1936 discovery wiring for --node-whitelist (#365) * use Peer for enode not String * moved NodeWhitelistController and PermissionConfig to p2p/permissioning * reject messages if not from a whitelisted peer --- .../ethereum/eth/transactions/TestNode.java | 5 +- .../config}/PermissioningConfiguration.java | 2 +- .../p2p/discovery/PeerDiscoveryAgent.java | 7 +- .../internal/PeerDiscoveryController.java | 17 ++- .../ethereum/p2p/netty/NettyP2PNetwork.java | 12 +- .../NodeWhitelistController.java | 36 ++--- .../ethereum/p2p/NettyP2PNetworkTest.java | 38 +++-- .../p2p/NetworkingServiceLifecycleTest.java | 35 +++-- .../PermissioningConfigurationTest.java | 2 +- .../discovery/AbstractPeerDiscoveryTest.java | 10 +- .../p2p/discovery/PeerDiscoveryAgentTest.java | 8 +- .../discovery/PeerDiscoveryObserversTest.java | 8 +- .../PeerDiscoveryTimestampsTest.java | 5 +- .../internal/PeerDiscoveryControllerTest.java | 139 ++++++++++++++++-- .../PeerDiscoveryTableRefreshTest.java | 5 +- .../java/tech/pegasys/pantheon/Runner.java | 7 +- .../tech/pegasys/pantheon/RunnerBuilder.java | 21 +-- .../pegasys/pantheon/cli/PantheonCommand.java | 2 +- .../tech/pegasys/pantheon/RunnerTest.java | 2 +- .../pantheon/cli/CommandTestAbstract.java | 2 +- 20 files changed, 275 insertions(+), 88 deletions(-) rename ethereum/{core/src/main/java/tech/pegasys/pantheon/ethereum/permissioning => p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/config}/PermissioningConfiguration.java (96%) rename {pantheon/src/main/java/tech/pegasys/pantheon/controller => ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/permissioning}/NodeWhitelistController.java (50%) rename ethereum/{core/src/test/java/tech/pegasys/pantheon/ethereum/permissioning => p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/config}/PermissioningConfigurationTest.java (97%) diff --git a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/transactions/TestNode.java b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/transactions/TestNode.java index 9f424e570f..64c3f0bace 100644 --- a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/transactions/TestNode.java +++ b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/transactions/TestNode.java @@ -37,12 +37,14 @@ import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; import tech.pegasys.pantheon.ethereum.p2p.api.PeerConnection; import tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration; import tech.pegasys.pantheon.ethereum.p2p.config.NetworkingConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.p2p.config.RlpxConfiguration; import tech.pegasys.pantheon.ethereum.p2p.netty.NettyP2PNetwork; import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer; import tech.pegasys.pantheon.ethereum.p2p.peers.Endpoint; import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; +import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController; import tech.pegasys.pantheon.ethereum.p2p.wire.messages.DisconnectMessage.DisconnectReason; import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; import tech.pegasys.pantheon.util.bytes.BytesValue; @@ -115,7 +117,8 @@ public class TestNode implements Closeable { capabilities, ethProtocolManager, new PeerBlacklist(), - new NoOpMetricsSystem())) + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault()))) .build(); network = networkRunner.getNetwork(); this.port = network.getSelf().getPort(); diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/PermissioningConfiguration.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/config/PermissioningConfiguration.java similarity index 96% rename from ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/PermissioningConfiguration.java rename to ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/config/PermissioningConfiguration.java index 8a53e63a5a..8020cf27ee 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/PermissioningConfiguration.java +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/config/PermissioningConfiguration.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.ethereum.permissioning; +package tech.pegasys.pantheon.ethereum.p2p.config; import java.util.ArrayList; import java.util.Collection; diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryAgent.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryAgent.java index df01fca322..17dea91dc1 100644 --- a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryAgent.java +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryAgent.java @@ -33,6 +33,7 @@ import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.PeerTable; import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.PingPacketData; import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeerId; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; +import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController; import tech.pegasys.pantheon.ethereum.p2p.wire.messages.DisconnectMessage; import tech.pegasys.pantheon.util.NetworkUtility; import tech.pegasys.pantheon.util.bytes.BytesValue; @@ -118,7 +119,8 @@ public class PeerDiscoveryAgent implements DisconnectCallback { final SECP256K1.KeyPair keyPair, final DiscoveryConfiguration config, final PeerRequirement peerRequirement, - final PeerBlacklist peerBlacklist) { + final PeerBlacklist peerBlacklist, + final NodeWhitelistController nodeWhitelistController) { checkArgument(vertx != null, "vertx instance cannot be null"); checkArgument(keyPair != null, "keypair cannot be null"); checkArgument(config != null, "provided configuration cannot be null"); @@ -140,7 +142,8 @@ public class PeerDiscoveryAgent implements DisconnectCallback { bootstrapPeers, PEER_REFRESH_INTERVAL_MS, peerRequirement, - peerBlacklist); + peerBlacklist, + nodeWhitelistController); } public CompletableFuture start(final int tcpPort) { diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/PeerDiscoveryController.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/PeerDiscoveryController.java index f24574d46c..49c6491c17 100644 --- a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/PeerDiscoveryController.java +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/PeerDiscoveryController.java @@ -26,6 +26,7 @@ import tech.pegasys.pantheon.ethereum.p2p.discovery.PeerDiscoveryEvent.PeerDropp import tech.pegasys.pantheon.ethereum.p2p.discovery.PeerDiscoveryStatus; import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; +import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController; import tech.pegasys.pantheon.util.Subscribers; import tech.pegasys.pantheon.util.bytes.BytesValue; @@ -65,7 +66,7 @@ import org.apache.logging.log4j.Logger; * | +---+----------------+ | * +------------+ +-----------+ +-----+----+ | +-----v-----+ * | | | | | <----------+ | | - * | KNOWN +---------> BONDING +---------> BONDED | | DROPPED | + * | KNOWN +---------> BONDING +---------> BONDED | | DROPPED | * | | | | | ^ | | * +------------+ +-----------+ +----------+ +-----------+ * @@ -106,6 +107,7 @@ public class PeerDiscoveryController { private final PeerDiscoveryAgent agent; private final PeerBlacklist peerBlacklist; + private final NodeWhitelistController nodeWhitelist; private RetryDelayFunction retryDelayFunction = RetryDelayFunction.linear(1.5, 2000, 60000); @@ -130,7 +132,8 @@ public class PeerDiscoveryController { final Collection bootstrapNodes, final long tableRefreshIntervalMs, final PeerRequirement peerRequirement, - final PeerBlacklist peerBlacklist) { + final PeerBlacklist peerBlacklist, + final NodeWhitelistController nodeWhitelist) { this.vertx = vertx; this.agent = agent; this.bootstrapNodes = bootstrapNodes; @@ -138,6 +141,7 @@ public class PeerDiscoveryController { this.tableRefreshIntervalMs = tableRefreshIntervalMs; this.peerRequirement = peerRequirement; this.peerBlacklist = peerBlacklist; + this.nodeWhitelist = nodeWhitelist; } public CompletableFuture start() { @@ -148,6 +152,7 @@ public class PeerDiscoveryController { bootstrapNodes .stream() .filter(node -> peerTable.tryAdd(node).getOutcome() == Outcome.ADDED) + .filter(node -> nodeWhitelist.contains(node)) .forEach(node -> bond(node, true)); final long timerId = @@ -195,6 +200,10 @@ public class PeerDiscoveryController { return; } + if (!nodeWhitelist.contains(sender)) { + return; + } + // Load the peer from the table, or use the instance that comes in. final Optional maybeKnownPeer = peerTable.get(sender); final DiscoveryPeer peer = maybeKnownPeer.orElse(sender); @@ -238,7 +247,9 @@ public class PeerDiscoveryController { .orElse(emptyList()); for (final DiscoveryPeer neighbor : neighbors) { - if (peerBlacklist.contains(neighbor) || peerTable.get(neighbor).isPresent()) { + if (!nodeWhitelist.contains(neighbor) + || peerBlacklist.contains(neighbor) + || peerTable.get(neighbor).isPresent()) { continue; } bond(neighbor, false); diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/netty/NettyP2PNetwork.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/netty/NettyP2PNetwork.java index d669416d1e..5ee78d2d83 100644 --- a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/netty/NettyP2PNetwork.java +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/netty/NettyP2PNetwork.java @@ -26,6 +26,7 @@ import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.PeerRequirement; import tech.pegasys.pantheon.ethereum.p2p.peers.Endpoint; import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; +import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.p2p.wire.PeerInfo; import tech.pegasys.pantheon.ethereum.p2p.wire.SubProtocol; @@ -150,6 +151,7 @@ public final class NettyP2PNetwork implements P2PNetwork { * @param peerBlacklist The peers with which this node will not connect * @param peerRequirement Queried to determine if enough peers are currently connected. * @param metricsSystem The metrics system to capture metrics with. + * @param nodeWhitelistController Controls the whitelist of nodes to which this node will connect. */ public NettyP2PNetwork( final Vertx vertx, @@ -158,13 +160,19 @@ public final class NettyP2PNetwork implements P2PNetwork { final List supportedCapabilities, final PeerRequirement peerRequirement, final PeerBlacklist peerBlacklist, - final MetricsSystem metricsSystem) { + final MetricsSystem metricsSystem, + final NodeWhitelistController nodeWhitelistController) { connections = new PeerConnectionRegistry(metricsSystem); this.peerBlacklist = peerBlacklist; peerDiscoveryAgent = new PeerDiscoveryAgent( - vertx, keyPair, config.getDiscovery(), peerRequirement, peerBlacklist); + vertx, + keyPair, + config.getDiscovery(), + peerRequirement, + peerBlacklist, + nodeWhitelistController); subscribeDisconnect(peerDiscoveryAgent); subscribeDisconnect(peerBlacklist); subscribeDisconnect(connections); diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/NodeWhitelistController.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/permissioning/NodeWhitelistController.java similarity index 50% rename from pantheon/src/main/java/tech/pegasys/pantheon/controller/NodeWhitelistController.java rename to ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/permissioning/NodeWhitelistController.java index 6ffb666c8d..74178bcbc3 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/NodeWhitelistController.java +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/permissioning/NodeWhitelistController.java @@ -10,40 +10,42 @@ * 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.controller; +package tech.pegasys.pantheon.ethereum.p2p.permissioning; -import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer; +import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import java.util.ArrayList; import java.util.List; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - public class NodeWhitelistController { - private static final Logger LOG = LogManager.getLogger(); - - private static List nodeWhitelist; - private static boolean nodeWhitelistSet = false; + private final List nodeWhitelist; + private boolean nodeWhitelistSet = false; public NodeWhitelistController(final PermissioningConfiguration configuration) { nodeWhitelist = new ArrayList<>(); if (configuration != null && configuration.getNodeWhitelist() != null) { - nodeWhitelist.addAll(configuration.getNodeWhitelist()); - nodeWhitelistSet = true; + for (String urlString : configuration.getNodeWhitelist()) { + nodeWhitelist.add(DefaultPeer.fromURI(urlString)); + } + if (configuration.isNodeWhitelistSet()) { + nodeWhitelistSet = true; + } } } - public boolean addNode(final String nodeId) { - return nodeWhitelist.add(nodeId); + public boolean addNode(final Peer node) { + nodeWhitelistSet = true; + return nodeWhitelist.add(node); } - public boolean removeNode(final String nodeId) { - return nodeWhitelist.remove(nodeId); + public boolean removeNode(final Peer node) { + return nodeWhitelist.remove(node); } - public static boolean isNodeWhitelistSet() { - return nodeWhitelistSet; + public boolean contains(final Peer node) { + return (!nodeWhitelistSet || (nodeWhitelistSet && nodeWhitelist.contains(node))); } } diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NettyP2PNetworkTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NettyP2PNetworkTest.java index bb7e2c7eb4..8e5efe41be 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NettyP2PNetworkTest.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NettyP2PNetworkTest.java @@ -25,6 +25,7 @@ import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; import tech.pegasys.pantheon.ethereum.p2p.api.PeerConnection; import tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration; import tech.pegasys.pantheon.ethereum.p2p.config.NetworkingConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.p2p.config.RlpxConfiguration; import tech.pegasys.pantheon.ethereum.p2p.netty.NettyP2PNetwork; import tech.pegasys.pantheon.ethereum.p2p.netty.exceptions.IncompatiblePeerException; @@ -32,6 +33,7 @@ import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer; import tech.pegasys.pantheon.ethereum.p2p.peers.Endpoint; import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; +import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.p2p.wire.PeerInfo; import tech.pegasys.pantheon.ethereum.p2p.wire.SubProtocol; @@ -75,7 +77,8 @@ public final class NettyP2PNetworkTest { singletonList(cap), () -> false, new PeerBlacklist(), - new NoOpMetricsSystem()); + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault())); final P2PNetwork connector = new NettyP2PNetwork( vertx, @@ -87,7 +90,8 @@ public final class NettyP2PNetworkTest { singletonList(cap), () -> false, new PeerBlacklist(), - new NoOpMetricsSystem())) { + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault()))) { final int listenPort = listener.getSelf().getPort(); listener.run(); @@ -127,7 +131,8 @@ public final class NettyP2PNetworkTest { capabilities, () -> true, new PeerBlacklist(), - new NoOpMetricsSystem()); + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault())); final P2PNetwork connector = new NettyP2PNetwork( vertx, @@ -139,7 +144,8 @@ public final class NettyP2PNetworkTest { capabilities, () -> true, new PeerBlacklist(), - new NoOpMetricsSystem())) { + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault()))) { final int listenPort = listener.getSelf().getPort(); listener.run(); connector.run(); @@ -194,7 +200,8 @@ public final class NettyP2PNetworkTest { cap, () -> true, new PeerBlacklist(), - new NoOpMetricsSystem()); + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault())); final P2PNetwork connector1 = new NettyP2PNetwork( vertx, @@ -206,7 +213,8 @@ public final class NettyP2PNetworkTest { cap, () -> true, new PeerBlacklist(), - new NoOpMetricsSystem()); + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault())); final P2PNetwork connector2 = new NettyP2PNetwork( vertx, @@ -218,7 +226,8 @@ public final class NettyP2PNetworkTest { cap, () -> true, new PeerBlacklist(), - new NoOpMetricsSystem())) { + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault()))) { final int listenPort = listener.getSelf().getPort(); // Setup listener and first connection @@ -272,7 +281,8 @@ public final class NettyP2PNetworkTest { singletonList(cap1), () -> false, new PeerBlacklist(), - new NoOpMetricsSystem()); + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault())); final P2PNetwork connector = new NettyP2PNetwork( vertx, @@ -284,7 +294,8 @@ public final class NettyP2PNetworkTest { singletonList(cap2), () -> false, new PeerBlacklist(), - new NoOpMetricsSystem())) { + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault()))) { final int listenPort = listener.getSelf().getPort(); listener.run(); connector.run(); @@ -325,7 +336,8 @@ public final class NettyP2PNetworkTest { singletonList(cap), () -> false, localBlacklist, - new NoOpMetricsSystem()); + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault())); final P2PNetwork remoteNetwork = new NettyP2PNetwork( vertx, @@ -337,7 +349,8 @@ public final class NettyP2PNetworkTest { singletonList(cap), () -> false, remoteBlacklist, - new NoOpMetricsSystem())) { + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault()))) { final int localListenPort = localNetwork.getSelf().getPort(); final int remoteListenPort = remoteNetwork.getSelf().getPort(); final Peer localPeer = @@ -457,7 +470,8 @@ public final class NettyP2PNetworkTest { singletonList(cap), () -> false, new PeerBlacklist(), - new NoOpMetricsSystem()); + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault())); } private Peer mockPeer() { diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NetworkingServiceLifecycleTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NetworkingServiceLifecycleTest.java index 6cda77f8cb..a78546cf1d 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NetworkingServiceLifecycleTest.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NetworkingServiceLifecycleTest.java @@ -22,9 +22,11 @@ import tech.pegasys.pantheon.crypto.SECP256K1; 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.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.p2p.discovery.PeerDiscoveryServiceException; import tech.pegasys.pantheon.ethereum.p2p.netty.NettyP2PNetwork; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; +import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController; import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; import tech.pegasys.pantheon.util.NetworkUtility; @@ -55,7 +57,8 @@ public class NetworkingServiceLifecycleTest { emptyList(), () -> true, new PeerBlacklist(), - new NoOpMetricsSystem())) { + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault()))) { service.run(); final int port = service.getDiscoverySocketAddress().getPort(); @@ -80,7 +83,8 @@ public class NetworkingServiceLifecycleTest { emptyList(), () -> true, new PeerBlacklist(), - new NoOpMetricsSystem())) { + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault()))) { Assertions.fail("Expected Exception"); } } @@ -99,7 +103,8 @@ public class NetworkingServiceLifecycleTest { emptyList(), () -> true, new PeerBlacklist(), - new NoOpMetricsSystem())) { + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault()))) { Assertions.fail("Expected Exception"); } } @@ -118,7 +123,8 @@ public class NetworkingServiceLifecycleTest { emptyList(), () -> true, new PeerBlacklist(), - new NoOpMetricsSystem())) { + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault()))) { Assertions.fail("Expected Exception"); } } @@ -133,7 +139,8 @@ public class NetworkingServiceLifecycleTest { emptyList(), () -> true, new PeerBlacklist(), - new NoOpMetricsSystem())) { + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault()))) { Assertions.fail("Expected Exception"); } } @@ -149,7 +156,8 @@ public class NetworkingServiceLifecycleTest { emptyList(), () -> true, new PeerBlacklist(), - new NoOpMetricsSystem())) { + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault()))) { service.run(); service.stop(); service.run(); @@ -167,7 +175,8 @@ public class NetworkingServiceLifecycleTest { emptyList(), () -> true, new PeerBlacklist(), - new NoOpMetricsSystem()); + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault())); final NettyP2PNetwork service2 = new NettyP2PNetwork( vertx, @@ -176,7 +185,8 @@ public class NetworkingServiceLifecycleTest { emptyList(), () -> true, new PeerBlacklist(), - new NoOpMetricsSystem())) { + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault()))) { service1.run(); service1.stop(); service2.run(); @@ -195,7 +205,8 @@ public class NetworkingServiceLifecycleTest { emptyList(), () -> true, new PeerBlacklist(), - new NoOpMetricsSystem())) { + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault()))) { service1.run(); final NetworkingConfiguration config = configWithRandomPorts(); config.getDiscovery().setBindPort(service1.getDiscoverySocketAddress().getPort()); @@ -207,7 +218,8 @@ public class NetworkingServiceLifecycleTest { emptyList(), () -> true, new PeerBlacklist(), - new NoOpMetricsSystem())) { + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault()))) { try { service2.run(); } catch (final Exception e) { @@ -236,7 +248,8 @@ public class NetworkingServiceLifecycleTest { emptyList(), () -> true, new PeerBlacklist(), - new NoOpMetricsSystem())) { + new NoOpMetricsSystem(), + new NodeWhitelistController(PermissioningConfiguration.createDefault()))) { assertTrue(agent.getDiscoveryPeers().isEmpty()); assertEquals(0, agent.getPeers().size()); } diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/permissioning/PermissioningConfigurationTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/config/PermissioningConfigurationTest.java similarity index 97% rename from ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/permissioning/PermissioningConfigurationTest.java rename to ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/config/PermissioningConfigurationTest.java index fa388287fa..68bc1417a4 100644 --- a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/permissioning/PermissioningConfigurationTest.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/config/PermissioningConfigurationTest.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.ethereum.permissioning; +package tech.pegasys.pantheon.ethereum.p2p.config; import static org.assertj.core.api.Assertions.assertThat; diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/AbstractPeerDiscoveryTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/AbstractPeerDiscoveryTest.java index c9d930a46b..3051e404d7 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/AbstractPeerDiscoveryTest.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/AbstractPeerDiscoveryTest.java @@ -16,11 +16,13 @@ import static io.vertx.core.Vertx.vertx; import tech.pegasys.pantheon.crypto.SECP256K1; import tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.Packet; import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.PacketType; import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.PingPacketData; import tech.pegasys.pantheon.ethereum.p2p.peers.Endpoint; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; +import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController; import tech.pegasys.pantheon.util.bytes.BytesValue; import java.util.List; @@ -127,7 +129,13 @@ public abstract class AbstractPeerDiscoveryTest { protected PeerDiscoveryAgent startDiscoveryAgent( final DiscoveryConfiguration config, final PeerBlacklist blacklist) { final PeerDiscoveryAgent agent = - new PeerDiscoveryAgent(vertx, SECP256K1.KeyPair.generate(), config, () -> true, blacklist); + new PeerDiscoveryAgent( + vertx, + SECP256K1.KeyPair.generate(), + config, + () -> true, + blacklist, + new NodeWhitelistController(PermissioningConfiguration.createDefault())); try { agent.start(BROADCAST_TCP_PORT).get(5, TimeUnit.SECONDS); } catch (final Exception ex) { diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryAgentTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryAgentTest.java index 5ce28f38fb..aac2b15583 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryAgentTest.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryAgentTest.java @@ -19,6 +19,7 @@ import tech.pegasys.pantheon.crypto.SECP256K1; import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; import tech.pegasys.pantheon.ethereum.p2p.api.PeerConnection; import tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.FindNeighborsPacketData; import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.NeighborsPacketData; import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.Packet; @@ -26,6 +27,7 @@ import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.PacketType; import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.PingPacketData; import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; +import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.p2p.wire.PeerInfo; import tech.pegasys.pantheon.ethereum.p2p.wire.messages.DisconnectMessage.DisconnectReason; @@ -137,7 +139,8 @@ public class PeerDiscoveryAgentTest extends AbstractPeerDiscoveryTest { keyPair1, DiscoveryConfiguration.create().setBindHost("127.0.0.1").setBindPort(0), () -> true, - new PeerBlacklist()); + new PeerBlacklist(), + new NodeWhitelistController(PermissioningConfiguration.createDefault())); peerDiscoveryAgent1.start(0).join(); final DefaultPeer peer = peerDiscoveryAgent1.getAdvertisedPeer(); @@ -151,7 +154,8 @@ public class PeerDiscoveryAgentTest extends AbstractPeerDiscoveryTest { .setBindPort(0) .setBootstrapPeers(Lists.newArrayList(peer)), () -> true, - new PeerBlacklist()); + new PeerBlacklist(), + new NodeWhitelistController(PermissioningConfiguration.createDefault())); peerDiscoveryAgent2.start(0).join(); assertThat(peerDiscoveryAgent2.getPeers().size()).isEqualTo(1); diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryObserversTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryObserversTest.java index b783f977ff..1505c60b5f 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryObserversTest.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryObserversTest.java @@ -19,9 +19,11 @@ import static org.awaitility.Awaitility.await; import static tech.pegasys.pantheon.ethereum.p2p.NetworkingTestHelper.configWithRandomPorts; import tech.pegasys.pantheon.crypto.SECP256K1; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.p2p.discovery.PeerDiscoveryEvent.PeerBondedEvent; import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; +import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController; import java.util.ArrayList; import java.util.Collections; @@ -108,7 +110,8 @@ public class PeerDiscoveryObserversTest extends AbstractPeerDiscoveryTest { SECP256K1.KeyPair.generate(), configWithRandomPorts().getDiscovery().setBootstrapPeers(peers2), () -> true, - new PeerBlacklist()); + new PeerBlacklist(), + new NodeWhitelistController(PermissioningConfiguration.createDefault())); // A queue for storing peer bonded events. final ArrayBlockingQueue queue = new ArrayBlockingQueue<>(10); @@ -159,7 +162,8 @@ public class PeerDiscoveryObserversTest extends AbstractPeerDiscoveryTest { .getDiscovery() .setBootstrapPeers(Collections.singletonList(peer)), () -> true, - new PeerBlacklist()); + new PeerBlacklist(), + new NodeWhitelistController(PermissioningConfiguration.createDefault())); // Create 5 queues and subscribe them to peer bonded events. final List> queues = diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryTimestampsTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryTimestampsTest.java index 11414353aa..c284064f2a 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryTimestampsTest.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryTimestampsTest.java @@ -18,12 +18,14 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import tech.pegasys.pantheon.crypto.SECP256K1; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.Packet; import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.PacketType; import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.PeerDiscoveryController; import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.PeerTable; import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.PingPacketData; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; +import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController; import java.util.Collections; import java.util.concurrent.TimeUnit; @@ -51,7 +53,8 @@ public class PeerDiscoveryTimestampsTest extends AbstractPeerDiscoveryTest { Collections.emptyList(), TimeUnit.HOURS.toMillis(1), () -> true, - new PeerBlacklist()); + new PeerBlacklist(), + new NodeWhitelistController(PermissioningConfiguration.createDefault())); controller.start(); final PingPacketData ping = diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/PeerDiscoveryControllerTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/PeerDiscoveryControllerTest.java index 0fe101e73c..5fb85c6bbc 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/PeerDiscoveryControllerTest.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/PeerDiscoveryControllerTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import tech.pegasys.pantheon.crypto.SECP256K1; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.p2p.discovery.DiscoveryPeer; import tech.pegasys.pantheon.ethereum.p2p.discovery.PeerDiscoveryAgent; import tech.pegasys.pantheon.ethereum.p2p.discovery.PeerDiscoveryStatus; @@ -35,12 +36,14 @@ import tech.pegasys.pantheon.ethereum.p2p.discovery.PeerDiscoveryTestHelper; import tech.pegasys.pantheon.ethereum.p2p.peers.Endpoint; import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; +import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController; import tech.pegasys.pantheon.util.bytes.Bytes32; import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.bytes.MutableBytesValue; import tech.pegasys.pantheon.util.uint.UInt256; import tech.pegasys.pantheon.util.uint.UInt256Value; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Optional; @@ -67,6 +70,7 @@ public class PeerDiscoveryControllerTest { private PeerDiscoveryController controller; private DiscoveryPeer peer; private PeerTable peerTable; + private NodeWhitelistController defaultNodeWhitelistController; @Before public void initializeMocks() { @@ -76,6 +80,9 @@ public class PeerDiscoveryControllerTest { agent = mock(PeerDiscoveryAgent.class); when(agent.getAdvertisedPeer()).thenReturn(peer); peerTable = new PeerTable(peer.getId()); + + defaultNodeWhitelistController = + new NodeWhitelistController(PermissioningConfiguration.createDefault()); } @After @@ -270,7 +277,8 @@ public class PeerDiscoveryControllerTest { Collections.singletonList(peers[0]), TABLE_REFRESH_INTERVAL_MS, PEER_REQUIREMENT, - new PeerBlacklist()); + new PeerBlacklist(), + defaultNodeWhitelistController); controller.setRetryDelayFunction((prev) -> 999999999L); controller.start(); @@ -331,7 +339,8 @@ public class PeerDiscoveryControllerTest { Arrays.asList(peers[0], peers[1]), TABLE_REFRESH_INTERVAL_MS, PEER_REQUIREMENT, - new PeerBlacklist()); + new PeerBlacklist(), + defaultNodeWhitelistController); controller.setRetryDelayFunction((prev) -> 999999999L); controller.start(); @@ -478,7 +487,8 @@ public class PeerDiscoveryControllerTest { Collections.singletonList(discoPeer), TABLE_REFRESH_INTERVAL_MS, PEER_REQUIREMENT, - blacklist); + blacklist, + defaultNodeWhitelistController); final Endpoint agentEndpoint = agent.getAdvertisedPeer().getEndpoint(); @@ -563,7 +573,8 @@ public class PeerDiscoveryControllerTest { Collections.singletonList(discoPeer), TABLE_REFRESH_INTERVAL_MS, PEER_REQUIREMENT, - blacklist)); + blacklist, + defaultNodeWhitelistController)); final Endpoint agentEndpoint = agent.getAdvertisedPeer().getEndpoint(); @@ -632,7 +643,8 @@ public class PeerDiscoveryControllerTest { Collections.singletonList(discoPeer), TABLE_REFRESH_INTERVAL_MS, PEER_REQUIREMENT, - blacklist)); + blacklist, + defaultNodeWhitelistController)); final Endpoint agentEndpoint = agent.getAdvertisedPeer().getEndpoint(); @@ -686,7 +698,8 @@ public class PeerDiscoveryControllerTest { Collections.singletonList(discoPeer), TABLE_REFRESH_INTERVAL_MS, PEER_REQUIREMENT, - blacklist)); + blacklist, + defaultNodeWhitelistController)); final Endpoint agentEndpoint = agent.getAdvertisedPeer().getEndpoint(); @@ -739,7 +752,8 @@ public class PeerDiscoveryControllerTest { Collections.singletonList(discoPeer), TABLE_REFRESH_INTERVAL_MS, PEER_REQUIREMENT, - blacklist)); + blacklist, + defaultNodeWhitelistController)); final Endpoint agentEndpoint = agent.getAdvertisedPeer().getEndpoint(); @@ -796,7 +810,8 @@ public class PeerDiscoveryControllerTest { Arrays.asList(peers[0]), TABLE_REFRESH_INTERVAL_MS, PEER_REQUIREMENT, - new PeerBlacklist()); + new PeerBlacklist(), + defaultNodeWhitelistController); controller.setRetryDelayFunction((prev) -> 999999999L); controller.start(); @@ -882,6 +897,111 @@ public class PeerDiscoveryControllerTest { assertThat(controller.getPeers()).doesNotContain(peers[1]); } + @Test + public void shouldNotBondWithNonWhitelistedPeer() + throws InterruptedException, ExecutionException, TimeoutException { + final DiscoveryPeer[] peers = createPeersInLastBucket(peer, 3); + final DiscoveryPeer discoPeer = peers[0]; + final DiscoveryPeer otherPeer = peers[1]; + final DiscoveryPeer otherPeer2 = peers[2]; + + final PeerBlacklist blacklist = new PeerBlacklist(); + final PermissioningConfiguration config = new PermissioningConfiguration(); + NodeWhitelistController nodeWhitelistController = new NodeWhitelistController(config); + + // Whitelist peers + nodeWhitelistController.addNode(discoPeer); + nodeWhitelistController.addNode(otherPeer2); + + controller = + spy( + new PeerDiscoveryController( + vertx, + agent, + peerTable, + Collections.singletonList(discoPeer), + TABLE_REFRESH_INTERVAL_MS, + PEER_REQUIREMENT, + blacklist, + nodeWhitelistController)); + + final Endpoint agentEndpoint = agent.getAdvertisedPeer().getEndpoint(); + + // Setup ping to be sent to discoPeer + SECP256K1.KeyPair[] keyPairs = PeerDiscoveryTestHelper.generateKeyPairs(1); + PingPacketData pingPacketData = PingPacketData.create(agentEndpoint, discoPeer.getEndpoint()); + final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, keyPairs[0]); + doReturn(discoPeerPing).when(agent).sendPacket(eq(discoPeer), eq(PacketType.PING), any()); + + controller.start(); + await() + .atMost(5, TimeUnit.SECONDS) + .untilAsserted( + () -> { + verify(agent, atLeast(1)).sendPacket(any(), eq(PacketType.PING), any()); + }); + + final Packet pongFromDiscoPeer = + MockPacketDataFactory.mockPongPacket(discoPeer, discoPeerPing.getHash()); + controller.onMessage(pongFromDiscoPeer, discoPeer); + + await() + .atMost(1, TimeUnit.SECONDS) + .untilAsserted( + () -> { + verify(agent, atLeast(1)) + .sendPacket(eq(discoPeer), eq(PacketType.FIND_NEIGHBORS), any()); + }); + + // Setup ping to be sent to otherPeer after neighbors packet is received + keyPairs = PeerDiscoveryTestHelper.generateKeyPairs(1); + pingPacketData = PingPacketData.create(agentEndpoint, otherPeer.getEndpoint()); + final Packet pingPacket = Packet.create(PacketType.PING, pingPacketData, keyPairs[0]); + doReturn(pingPacket).when(agent).sendPacket(eq(otherPeer), eq(PacketType.PING), any()); + + // Setup ping to be sent to otherPeer2 after neighbors packet is received + keyPairs = PeerDiscoveryTestHelper.generateKeyPairs(1); + pingPacketData = PingPacketData.create(agentEndpoint, otherPeer2.getEndpoint()); + final Packet pingPacket2 = Packet.create(PacketType.PING, pingPacketData, keyPairs[0]); + doReturn(pingPacket2).when(agent).sendPacket(eq(otherPeer2), eq(PacketType.PING), any()); + + final Packet neighborsPacket = + MockPacketDataFactory.mockNeighborsPacket(discoPeer, otherPeer, otherPeer2); + controller.onMessage(neighborsPacket, discoPeer); + + verify(controller, times(0)).bond(otherPeer, false); + verify(controller, times(1)).bond(otherPeer2, false); + } + + @Test + public void shouldNotRespondToPingFromNonWhitelistedDiscoveryPeer() { + final DiscoveryPeer[] peers = createPeersInLastBucket(peer, 3); + final DiscoveryPeer discoPeer = peers[0]; + + final PeerBlacklist blacklist = new PeerBlacklist(); + + // don't add disco peer to whitelist + PermissioningConfiguration config = PermissioningConfiguration.createDefault(); + config.setNodeWhitelist(new ArrayList<>()); + NodeWhitelistController nodeWhitelistController = new NodeWhitelistController(config); + + controller = + spy( + new PeerDiscoveryController( + vertx, + agent, + peerTable, + Collections.singletonList(discoPeer), + TABLE_REFRESH_INTERVAL_MS, + PEER_REQUIREMENT, + blacklist, + nodeWhitelistController)); + + final Packet pingPacket = mockPingPacket(peers[0], peer); + controller.onMessage(pingPacket, peers[0]); + assertThat(controller.getPeers()).doesNotContain(peers[0]); + } + private static Packet mockPingPacket(final Peer from, final Peer to) { final Packet packet = mock(Packet.class); @@ -937,7 +1057,8 @@ public class PeerDiscoveryControllerTest { Arrays.asList(bootstrapPeers), TABLE_REFRESH_INTERVAL_MS, PEER_REQUIREMENT, - new PeerBlacklist()); + new PeerBlacklist(), + defaultNodeWhitelistController); controller.setRetryDelayFunction(retryDelayFunction); controller.start(); } diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/PeerDiscoveryTableRefreshTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/PeerDiscoveryTableRefreshTest.java index 46a95fafd5..9698b771ee 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/PeerDiscoveryTableRefreshTest.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/PeerDiscoveryTableRefreshTest.java @@ -23,10 +23,12 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import tech.pegasys.pantheon.crypto.SECP256K1; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.p2p.discovery.DiscoveryPeer; import tech.pegasys.pantheon.ethereum.p2p.discovery.PeerDiscoveryAgent; import tech.pegasys.pantheon.ethereum.p2p.discovery.PeerDiscoveryTestHelper; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; +import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController; import tech.pegasys.pantheon.util.bytes.BytesValue; import java.util.ArrayList; @@ -59,7 +61,8 @@ public class PeerDiscoveryTableRefreshTest { emptyList(), 100, () -> true, - new PeerBlacklist()); + new PeerBlacklist(), + new NodeWhitelistController(PermissioningConfiguration.createDefault())); controller.start(); // Send a PING, so as to add a Peer in the controller. diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/Runner.java b/pantheon/src/main/java/tech/pegasys/pantheon/Runner.java index fab716ff9a..662aee5868 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/Runner.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/Runner.java @@ -12,7 +12,6 @@ */ package tech.pegasys.pantheon; -import tech.pegasys.pantheon.controller.NodeWhitelistController; import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcHttpService; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketService; @@ -47,23 +46,19 @@ public class Runner implements AutoCloseable { private final PantheonController pantheonController; private final Path dataDir; - private final NodeWhitelistController nodeWhitelistController; - Runner( final Vertx vertx, final NetworkRunner networkRunner, final Optional jsonRpc, final Optional websocketRpc, final PantheonController pantheonController, - final Path dataDir, - final NodeWhitelistController nodeWhitelistController) { + final Path dataDir) { this.vertx = vertx; this.networkRunner = networkRunner; this.jsonRpc = jsonRpc; this.websocketRpc = websocketRpc; this.pantheonController = pantheonController; this.dataDir = dataDir; - this.nodeWhitelistController = nodeWhitelistController; } public void execute() { diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java index 977eba9c68..a908fdbeff 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java @@ -12,7 +12,6 @@ */ package tech.pegasys.pantheon; -import tech.pegasys.pantheon.controller.NodeWhitelistController; import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.ethereum.ProtocolContext; @@ -44,14 +43,15 @@ import tech.pegasys.pantheon.ethereum.p2p.NetworkRunner; import tech.pegasys.pantheon.ethereum.p2p.api.ProtocolManager; import tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration; import tech.pegasys.pantheon.ethereum.p2p.config.NetworkingConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.p2p.config.RlpxConfiguration; import tech.pegasys.pantheon.ethereum.p2p.config.SubProtocolConfiguration; import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.PeerRequirement; import tech.pegasys.pantheon.ethereum.p2p.netty.NettyP2PNetwork; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; +import tech.pegasys.pantheon.ethereum.p2p.permissioning.NodeWhitelistController; import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; import tech.pegasys.pantheon.ethereum.p2p.wire.SubProtocol; -import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.metrics.MetricsSystem; import tech.pegasys.pantheon.util.bytes.BytesValue; @@ -196,6 +196,9 @@ public class RunnerBuilder { new PeerBlacklist( bannedNodeIds.stream().map(BytesValue::fromHexString).collect(Collectors.toSet())); + NodeWhitelistController nodeWhitelistController = + new NodeWhitelistController(permissioningConfiguration); + final NetworkRunner networkRunner = NetworkRunner.builder() .protocolManagers(protocolManagers) @@ -209,7 +212,8 @@ public class RunnerBuilder { caps, PeerRequirement.aggregateOf(protocolManagers), peerBlacklist, - metricsSystem)) + metricsSystem, + nodeWhitelistController)) .build(); final Synchronizer synchronizer = pantheonController.getSynchronizer(); @@ -272,17 +276,8 @@ public class RunnerBuilder { vertx, webSocketConfiguration, subscriptionManager, webSocketsJsonRpcMethods)); } - NodeWhitelistController nodeWhitelistController = - new NodeWhitelistController(permissioningConfiguration); - return new Runner( - vertx, - networkRunner, - jsonRpcHttpService, - webSocketService, - pantheonController, - dataDir, - nodeWhitelistController); + vertx, networkRunner, jsonRpcHttpService, webSocketService, pantheonController, dataDir); } private FilterManager createFilterManager( diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index 8347b5961c..c9f8daf892 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -33,8 +33,8 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration; import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApi; import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer; -import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.util.InvalidConfigurationException; import tech.pegasys.pantheon.metrics.MetricsSystem; import tech.pegasys.pantheon.metrics.prometheus.PrometheusMetricsSystem; diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java index add7efdb05..9e9bf7d542 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java @@ -33,8 +33,8 @@ import tech.pegasys.pantheon.ethereum.mainnet.HeaderValidationMode; import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer; -import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.storage.StorageProvider; import tech.pegasys.pantheon.ethereum.storage.keyvalue.RocksDbStorageProvider; import tech.pegasys.pantheon.metrics.MetricsSystem; diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java index d477ce1029..30b1921218 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java @@ -22,7 +22,7 @@ import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.ethereum.eth.sync.SynchronizerConfiguration; import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; -import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; +import tech.pegasys.pantheon.ethereum.p2p.config.PermissioningConfiguration; import tech.pegasys.pantheon.util.BlockImporter; import java.io.ByteArrayOutputStream;