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 8ceba60b15..1a1df67164 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 @@ -23,7 +23,7 @@ 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.permissioning.LocalPermissioningConfiguration; +import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition; import tech.pegasys.pantheon.tests.acceptance.dsl.httptransaction.HttpRequestFactory; @@ -83,7 +83,7 @@ public class PantheonNode implements NodeConfiguration, RunnableNode, AutoClosea private final JsonRpcConfiguration jsonRpcConfiguration; private final WebSocketConfiguration webSocketConfiguration; private final MetricsConfiguration metricsConfiguration; - private final Optional permissioningConfiguration; + private final Optional permissioningConfiguration; private final GenesisConfigProvider genesisConfigProvider; private final boolean devMode; private final boolean discoveryEnabled; @@ -103,7 +103,7 @@ public PantheonNode( final JsonRpcConfiguration jsonRpcConfiguration, final WebSocketConfiguration webSocketConfiguration, final MetricsConfiguration metricsConfiguration, - final Optional permissioningConfiguration, + final Optional permissioningConfiguration, final boolean devMode, final GenesisConfigProvider genesisConfigProvider, final boolean p2pEnabled, @@ -472,7 +472,7 @@ public boolean isDiscoveryEnabled() { return discoveryEnabled; } - Optional getPermissioningConfiguration() { + Optional getPermissioningConfiguration() { return permissioningConfiguration; } 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 7958836e07..1770e2f502 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 @@ -16,6 +16,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApi; import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis; +import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import java.io.BufferedReader; import java.io.File; @@ -145,6 +146,7 @@ public void startNode(final PantheonNode node) { } node.getPermissioningConfiguration() + .flatMap(PermissioningConfiguration::getLocalConfig) .ifPresent( permissioningConfiguration -> { if (permissioningConfiguration.isNodeWhitelistEnabled()) { 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 index 0c693e8983..0bd5366bb2 100644 --- 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 @@ -16,7 +16,7 @@ import tech.pegasys.pantheon.ethereum.core.PrivacyParameters; import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; -import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; +import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import tech.pegasys.pantheon.tests.acceptance.dsl.node.GenesisConfigProvider; @@ -30,7 +30,7 @@ class PantheonFactoryConfiguration { private final JsonRpcConfiguration jsonRpcConfiguration; private final WebSocketConfiguration webSocketConfiguration; private final MetricsConfiguration metricsConfiguration; - private final Optional permissioningConfiguration; + private final Optional permissioningConfiguration; private final boolean devMode; private final GenesisConfigProvider genesisConfigProvider; private final boolean p2pEnabled; @@ -44,7 +44,7 @@ class PantheonFactoryConfiguration { final JsonRpcConfiguration jsonRpcConfiguration, final WebSocketConfiguration webSocketConfiguration, final MetricsConfiguration metricsConfiguration, - final Optional permissioningConfiguration, + final Optional permissioningConfiguration, final boolean devMode, final GenesisConfigProvider genesisConfigProvider, final boolean p2pEnabled, @@ -88,7 +88,7 @@ public MetricsConfiguration getMetricsConfiguration() { return metricsConfiguration; } - public Optional getPermissioningConfiguration() { + public Optional getPermissioningConfiguration() { return permissioningConfiguration; } 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 index 4ce1c83bfd..2955af4126 100644 --- 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 @@ -20,7 +20,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.JsonRpcConfiguration; import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; -import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; +import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import tech.pegasys.pantheon.tests.acceptance.dsl.node.GenesisConfigProvider; @@ -38,7 +38,7 @@ public class PantheonFactoryConfigurationBuilder { private JsonRpcConfiguration jsonRpcConfiguration = JsonRpcConfiguration.createDefault(); private WebSocketConfiguration webSocketConfiguration = WebSocketConfiguration.createDefault(); private MetricsConfiguration metricsConfiguration = MetricsConfiguration.createDefault(); - private Optional permissioningConfiguration = Optional.empty(); + private Optional permissioningConfiguration = Optional.empty(); private boolean devMode = true; private GenesisConfigProvider genesisConfigProvider = ignore -> Optional.empty(); private Boolean p2pEnabled = true; @@ -137,7 +137,7 @@ public PantheonFactoryConfigurationBuilder webSocketAuthenticationEnabled() } public PantheonFactoryConfigurationBuilder setPermissioningConfiguration( - final LocalPermissioningConfiguration permissioningConfiguration) { + final PermissioningConfiguration permissioningConfiguration) { this.permissioningConfiguration = Optional.of(permissioningConfiguration); return this; } diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java index a3f50f93d4..506cae280f 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java @@ -27,6 +27,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; +import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.WhitelistPersistor; import tech.pegasys.pantheon.ethereum.permissioning.WhitelistPersistor.WHITELIST_TYPE; import tech.pegasys.pantheon.tests.acceptance.dsl.node.GenesisConfigProvider; @@ -183,11 +184,15 @@ public PantheonNode createNodeWithWhitelistsEnabled( final List accountsWhitelist, final String tempFilePath) throws IOException { - final LocalPermissioningConfiguration permissioningConfiguration = + final LocalPermissioningConfiguration localPermissioningConfiguration = LocalPermissioningConfiguration.createDefault(); - permissioningConfiguration.setNodeWhitelist(nodesWhitelist); - permissioningConfiguration.setAccountWhitelist(accountsWhitelist); - permissioningConfiguration.setConfigurationFilePath(tempFilePath); + localPermissioningConfiguration.setNodeWhitelist(nodesWhitelist); + localPermissioningConfiguration.setAccountWhitelist(accountsWhitelist); + localPermissioningConfiguration.setConfigurationFilePath(tempFilePath); + + final PermissioningConfiguration permissioningConfiguration = + new PermissioningConfiguration( + Optional.of(localPermissioningConfiguration), Optional.empty()); return create( new PantheonFactoryConfigurationBuilder() @@ -204,18 +209,22 @@ public PantheonNode createNodeWithNodesWhitelist( public PantheonNode createNodeWithNodesWhitelist( final String name, final List nodesWhitelist) throws IOException { - final LocalPermissioningConfiguration permissioningConfiguration = + final LocalPermissioningConfiguration localPermissioningConfiguration = LocalPermissioningConfiguration.createDefault(); - permissioningConfiguration.setNodeWhitelist(nodesWhitelist); + localPermissioningConfiguration.setNodeWhitelist(nodesWhitelist); final List whitelistAsStrings = nodesWhitelist.parallelStream().map(URI::toString).collect(toList()); final File tempFile = createTempPermissioningConfigurationFile(); tempFile.deleteOnExit(); - permissioningConfiguration.setConfigurationFilePath(tempFile.getPath()); + localPermissioningConfiguration.setConfigurationFilePath(tempFile.getPath()); initPermissioningConfigurationFile( WhitelistPersistor.WHITELIST_TYPE.NODES, whitelistAsStrings, tempFile.toPath()); + final PermissioningConfiguration permissioningConfiguration = + new PermissioningConfiguration( + Optional.of(localPermissioningConfiguration), Optional.empty()); + return create( new PantheonFactoryConfigurationBuilder() .setName(name) @@ -241,18 +250,22 @@ private void initPermissioningConfigurationFile( public PantheonNode createNodeWithAccountsWhitelist( final String name, final List accountsWhitelist) throws IOException { - final LocalPermissioningConfiguration permissioningConfiguration = + final LocalPermissioningConfiguration localPermissioningConfiguration = LocalPermissioningConfiguration.createDefault(); - permissioningConfiguration.setAccountWhitelist(accountsWhitelist); - permissioningConfiguration.setConfigurationFilePath( + localPermissioningConfiguration.setAccountWhitelist(accountsWhitelist); + localPermissioningConfiguration.setConfigurationFilePath( createTempPermissioningConfigurationFile().getPath()); final File tempFile = createTempPermissioningConfigurationFile(); tempFile.deleteOnExit(); - permissioningConfiguration.setConfigurationFilePath(tempFile.getPath()); + localPermissioningConfiguration.setConfigurationFilePath(tempFile.getPath()); initPermissioningConfigurationFile( WHITELIST_TYPE.ACCOUNTS, accountsWhitelist, tempFile.toPath()); + final PermissioningConfiguration permissioningConfiguration = + new PermissioningConfiguration( + Optional.of(localPermissioningConfiguration), Optional.empty()); + return create( new PantheonFactoryConfigurationBuilder() .setName(name) diff --git a/build.gradle b/build.gradle index 4586e6b0fd..889dd550d0 100644 --- a/build.gradle +++ b/build.gradle @@ -14,11 +14,11 @@ import net.ltgt.gradle.errorprone.CheckSeverity plugins { - id 'com.diffplug.gradle.spotless' version '3.17.0' + id 'com.diffplug.gradle.spotless' version '3.19.0' id 'com.jfrog.bintray' version '1.8.4' - id 'com.github.ben-manes.versions' version '0.20.0' + id 'com.github.ben-manes.versions' version '0.21.0' id 'com.github.hierynomus.license' version '0.15.0' - id 'io.spring.dependency-management' version '1.0.6.RELEASE' + id 'io.spring.dependency-management' version '1.0.7.RELEASE' id 'me.champeau.gradle.jmh' version '0.4.8' apply false id 'net.ltgt.errorprone' version '0.6.1' id 'net.researchgate.release' version '2.7.0' diff --git a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloaderTest.java b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloaderTest.java index 6b6b324201..c6dc4ae033 100644 --- a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloaderTest.java +++ b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloaderTest.java @@ -86,10 +86,14 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.junit.After; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.Timeout; public class WorldStateDownloaderTest { + @Rule public Timeout globalTimeout = Timeout.seconds(60); // 1 minute max per test + private static final Hash EMPTY_TRIE_ROOT = Hash.wrap(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH); private final BlockDataGenerator dataGen = new BlockDataGenerator(1); 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 9a203b93bf..c86b3a3bda 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 @@ -123,6 +123,7 @@ public TestNode( () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) .metricsSystem(new NoOpMetricsSystem()) .build(); 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 a0af74bbb7..3277a5c7a8 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 @@ -35,6 +35,7 @@ import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; import tech.pegasys.pantheon.ethereum.p2p.wire.messages.DisconnectMessage; import tech.pegasys.pantheon.ethereum.permissioning.NodeLocalConfigPermissioningController; +import tech.pegasys.pantheon.ethereum.permissioning.node.NodePermissioningController; import tech.pegasys.pantheon.util.NetworkUtility; import tech.pegasys.pantheon.util.Subscribers; import tech.pegasys.pantheon.util.bytes.BytesValue; @@ -71,6 +72,7 @@ public abstract class PeerDiscoveryAgent implements DisconnectCallback { private final PeerRequirement peerRequirement; private final PeerBlacklist peerBlacklist; private final Optional nodeWhitelistController; + private final Optional nodePermissioningController; /* The peer controller, which takes care of the state machine of peers. */ protected Optional controller = Optional.empty(); @@ -93,7 +95,8 @@ public PeerDiscoveryAgent( final DiscoveryConfiguration config, final PeerRequirement peerRequirement, final PeerBlacklist peerBlacklist, - final Optional nodeWhitelistController) { + final Optional nodeWhitelistController, + final Optional nodePermissioningController) { checkArgument(keyPair != null, "keypair cannot be null"); checkArgument(config != null, "provided configuration cannot be null"); @@ -102,6 +105,7 @@ public PeerDiscoveryAgent( this.peerRequirement = peerRequirement; this.peerBlacklist = peerBlacklist; this.nodeWhitelistController = nodeWhitelistController; + this.nodePermissioningController = nodePermissioningController; this.bootstrapPeers = config.getBootstrapPeers().stream().map(DiscoveryPeer::new).collect(Collectors.toList()); @@ -164,6 +168,7 @@ private PeerDiscoveryController createController() { peerRequirement, peerBlacklist, nodeWhitelistController, + nodePermissioningController, peerBondedObservers, peerDroppedObservers); } diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/VertxPeerDiscoveryAgent.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/VertxPeerDiscoveryAgent.java index 76bd818a8d..9ba71cfadb 100644 --- a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/VertxPeerDiscoveryAgent.java +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/VertxPeerDiscoveryAgent.java @@ -25,6 +25,7 @@ import tech.pegasys.pantheon.ethereum.p2p.peers.Endpoint; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; import tech.pegasys.pantheon.ethereum.permissioning.NodeLocalConfigPermissioningController; +import tech.pegasys.pantheon.ethereum.permissioning.node.NodePermissioningController; import tech.pegasys.pantheon.util.NetworkUtility; import tech.pegasys.pantheon.util.Preconditions; @@ -58,8 +59,15 @@ public VertxPeerDiscoveryAgent( final DiscoveryConfiguration config, final PeerRequirement peerRequirement, final PeerBlacklist peerBlacklist, - final Optional nodeWhitelistController) { - super(keyPair, config, peerRequirement, peerBlacklist, nodeWhitelistController); + final Optional nodeWhitelistController, + final Optional nodePermissioningController) { + super( + keyPair, + config, + peerRequirement, + peerBlacklist, + nodeWhitelistController, + nodePermissioningController); checkArgument(vertx != null, "vertx instance cannot be null"); this.vertx = vertx; } 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 08bbf31112..a5ad162ff6 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 @@ -34,6 +34,7 @@ import tech.pegasys.pantheon.ethereum.permissioning.node.NodeWhitelistUpdatedEvent; import tech.pegasys.pantheon.util.Subscribers; import tech.pegasys.pantheon.util.bytes.BytesValue; +import tech.pegasys.pantheon.util.enode.EnodeURL; import java.util.Collection; import java.util.List; @@ -118,6 +119,7 @@ public class PeerDiscoveryController { private final OutboundMessageHandler outboundMessageHandler; private final PeerBlacklist peerBlacklist; private final Optional nodeWhitelistController; + private final Optional nodePermissioningController; private RetryDelayFunction retryDelayFunction = RetryDelayFunction.linear(1.5, 2000, 60000); @@ -136,9 +138,6 @@ public class PeerDiscoveryController { private RecursivePeerRefreshState recursivePeerRefreshState; - private final Optional nodePermissioningController = - Optional.empty(); - public PeerDiscoveryController( final KeyPair keypair, final DiscoveryPeer localPeer, @@ -151,6 +150,7 @@ public PeerDiscoveryController( final PeerRequirement peerRequirement, final PeerBlacklist peerBlacklist, final Optional nodeWhitelistController, + final Optional nodePermissioningController, final Subscribers> peerBondedObservers, final Subscribers> peerDroppedObservers) { this.timerUtil = timerUtil; @@ -163,6 +163,7 @@ public PeerDiscoveryController( this.peerRequirement = peerRequirement; this.peerBlacklist = peerBlacklist; this.nodeWhitelistController = nodeWhitelistController; + this.nodePermissioningController = nodePermissioningController; this.outboundMessageHandler = outboundMessageHandler; this.peerBondedObservers = peerBondedObservers; this.peerDroppedObservers = peerDroppedObservers; @@ -173,25 +174,23 @@ public void start() { throw new IllegalStateException("The peer table had already been started"); } - bootstrapNodes.stream() - .filter(this::whitelistIfPresentIsNodePermitted) - .forEach(peerTable::tryAdd); + bootstrapNodes.stream().filter(p -> isPeerPermitted(localPeer, p)).forEach(peerTable::tryAdd); recursivePeerRefreshState = new RecursivePeerRefreshState( peerBlacklist, - nodeWhitelistController, + nodePermissioningController, this::bond, this::findNodes, timerUtil, - localPeer.getId(), + localPeer, peerTable, PEER_REFRESH_ROUND_TIMEOUT_IN_SECONDS, 100); final List initialDiscoveryPeers = bootstrapNodes.stream() - .filter(this::whitelistIfPresentIsNodePermitted) + .filter(p -> isPeerPermitted(localPeer, p)) .collect(Collectors.toList()); if (nodePermissioningController.isPresent()) { @@ -225,9 +224,13 @@ public CompletableFuture stop() { return CompletableFuture.completedFuture(null); } - private boolean whitelistIfPresentIsNodePermitted(final DiscoveryPeer sender) { - return nodeWhitelistController - .map(nodeWhitelistController -> nodeWhitelistController.isPermitted(sender.getEnodeURI())) + private boolean isPeerPermitted(final Peer sourcePeer, final Peer destinationPeer) { + return nodePermissioningController + .map( + c -> + c.isPermitted( + new EnodeURL(sourcePeer.getEnodeURI()), + new EnodeURL(destinationPeer.getEnodeURI()))) .orElse(true); } @@ -250,7 +253,7 @@ public void onMessage(final Packet packet, final DiscoveryPeer sender) { return; } - if (!whitelistIfPresentIsNodePermitted(sender)) { + if (!isPeerPermitted(sender, localPeer)) { LOG.trace("Dropping packet from peer not in the whitelist ({})", sender.getEnodeURI()); return; } diff --git a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/RecursivePeerRefreshState.java b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/RecursivePeerRefreshState.java index 330908266a..f4b8a7b353 100644 --- a/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/RecursivePeerRefreshState.java +++ b/ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/RecursivePeerRefreshState.java @@ -17,8 +17,9 @@ import tech.pegasys.pantheon.ethereum.p2p.discovery.DiscoveryPeer; import tech.pegasys.pantheon.ethereum.p2p.discovery.PeerDiscoveryStatus; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; -import tech.pegasys.pantheon.ethereum.permissioning.NodeLocalConfigPermissioningController; +import tech.pegasys.pantheon.ethereum.permissioning.node.NodePermissioningController; import tech.pegasys.pantheon.util.bytes.BytesValue; +import tech.pegasys.pantheon.util.enode.EnodeURL; import java.util.List; import java.util.Map; @@ -39,9 +40,9 @@ public class RecursivePeerRefreshState { private static final int MAX_CONCURRENT_REQUESTS = 3; private BytesValue target; private final PeerBlacklist peerBlacklist; - private final Optional peerWhitelist; + private final Optional nodePermissioningController; private final PeerTable peerTable; - private final BytesValue localPeerId; + private final DiscoveryPeer localPeer; private final BondingAgent bondingAgent; private final FindNeighbourDispatcher findNeighbourDispatcher; @@ -59,20 +60,20 @@ public class RecursivePeerRefreshState { RecursivePeerRefreshState( final PeerBlacklist peerBlacklist, - final Optional peerWhitelist, + final Optional nodePermissioningController, final BondingAgent bondingAgent, final FindNeighbourDispatcher neighborFinder, final TimerUtil timerUtil, - final BytesValue localPeerId, + final DiscoveryPeer localPeer, final PeerTable peerTable, final int timeoutPeriodInSeconds, final int maxRounds) { this.peerBlacklist = peerBlacklist; - this.peerWhitelist = peerWhitelist; + this.nodePermissioningController = nodePermissioningController; this.bondingAgent = bondingAgent; this.findNeighbourDispatcher = neighborFinder; this.timerUtil = timerUtil; - this.localPeerId = localPeerId; + this.localPeer = localPeer; this.peerTable = peerTable; this.timeoutPeriodInSeconds = timeoutPeriodInSeconds; this.maxRounds = maxRounds; @@ -183,11 +184,18 @@ private void neighboursCancelOutstandingRequests() { private boolean satisfiesMapAdditionCriteria(final DiscoveryPeer discoPeer) { return !oneTrueMap.containsKey(discoPeer.getId()) && !peerBlacklist.contains(discoPeer) - && peerWhitelist - .map(whitelist -> whitelist.isPermitted(discoPeer.getEnodeURI())) - .orElse(true) + && isPeerPermitted(discoPeer) && (initialPeers.contains(discoPeer) || !peerTable.get(discoPeer).isPresent()) - && !discoPeer.getId().equals(localPeerId); + && !discoPeer.getId().equals(localPeer); + } + + private Boolean isPeerPermitted(final DiscoveryPeer discoPeer) { + return nodePermissioningController + .map( + controller -> + controller.isPermitted( + new EnodeURL(localPeer.getEnodeURI()), new EnodeURL(discoPeer.getEnodeURI()))) + .orElse(true); } void onNeighboursPacketReceived( 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 222c6bce02..dced8a61f8 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 @@ -161,8 +161,8 @@ public class NettyP2PNetwork implements P2PNetwork { private final LabelledMetric outboundMessagesCounter; - private final Optional nodePermissioningController; private final Optional nodeWhitelistController; + private final Optional nodePermissioningController; private final Optional blockchain; private OptionalLong blockAddedObserverId = OptionalLong.empty(); @@ -174,7 +174,8 @@ public NettyP2PNetwork( final PeerRequirement peerRequirement, final PeerBlacklist peerBlacklist, final MetricsSystem metricsSystem, - final Optional nodeWhitelistController) { + final Optional nodeWhitelistController, + final Optional nodePermissioningController) { this( vertx, keyPair, @@ -184,7 +185,7 @@ public NettyP2PNetwork( peerBlacklist, metricsSystem, nodeWhitelistController, - null, + nodePermissioningController, null); } @@ -215,12 +216,11 @@ public NettyP2PNetwork( final PeerBlacklist peerBlacklist, final MetricsSystem metricsSystem, final Optional nodeLocalConfigPermissioningController, - final NodePermissioningController nodePermissioningController, + final Optional nodePermissioningController, final Blockchain blockchain) { connections = new PeerConnectionRegistry(metricsSystem); this.peerBlacklist = peerBlacklist; - this.nodePermissioningController = Optional.ofNullable(nodePermissioningController); this.peerMaintainConnectionList = new HashSet<>(); peerDiscoveryAgent = new VertxPeerDiscoveryAgent( @@ -229,7 +229,8 @@ public NettyP2PNetwork( config.getDiscovery(), peerRequirement, peerBlacklist, - nodeLocalConfigPermissioningController); + nodeLocalConfigPermissioningController, + nodePermissioningController); outboundMessagesCounter = metricsSystem.createLabelledCounter( @@ -301,6 +302,7 @@ public NettyP2PNetwork( } this.nodeWhitelistController = nodeLocalConfigPermissioningController; + this.nodePermissioningController = nodePermissioningController; this.blockchain = Optional.ofNullable(blockchain); } 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 f69308d203..f532a296bf 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 @@ -120,6 +120,7 @@ public void handshaking() throws Exception { () -> false, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty()); final P2PNetwork connector = new NettyP2PNetwork( @@ -133,6 +134,7 @@ public void handshaking() throws Exception { () -> false, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) { final int listenPort = listener.getLocalPeerInfo().getPort(); @@ -174,6 +176,7 @@ public void preventMultipleConnections() throws Exception { () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty()); final P2PNetwork connector = new NettyP2PNetwork( @@ -187,6 +190,7 @@ public void preventMultipleConnections() throws Exception { () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) { final int listenPort = listener.getLocalPeerInfo().getPort(); listener.start(); @@ -243,6 +247,7 @@ public void limitMaxPeers() throws Exception { () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty()); final P2PNetwork connector1 = new NettyP2PNetwork( @@ -256,6 +261,7 @@ public void limitMaxPeers() throws Exception { () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty()); final P2PNetwork connector2 = new NettyP2PNetwork( @@ -269,6 +275,7 @@ public void limitMaxPeers() throws Exception { () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) { final int listenPort = listener.getLocalPeerInfo().getPort(); @@ -324,6 +331,7 @@ public void rejectPeerWithNoSharedCaps() throws Exception { () -> false, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty()); final P2PNetwork connector = new NettyP2PNetwork( @@ -337,6 +345,7 @@ public void rejectPeerWithNoSharedCaps() throws Exception { () -> false, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) { final int listenPort = listener.getLocalPeerInfo().getPort(); listener.start(); @@ -379,6 +388,7 @@ public void rejectIncomingConnectionFromBlacklistedPeer() throws Exception { () -> false, localBlacklist, new NoOpMetricsSystem(), + Optional.empty(), Optional.empty()); final P2PNetwork remoteNetwork = new NettyP2PNetwork( @@ -392,6 +402,7 @@ public void rejectIncomingConnectionFromBlacklistedPeer() throws Exception { () -> false, remoteBlacklist, new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) { final int localListenPort = localNetwork.getLocalPeerInfo().getPort(); final int remoteListenPort = remoteNetwork.getLocalPeerInfo().getPort(); @@ -469,7 +480,8 @@ public void rejectIncomingConnectionFromNonWhitelistedPeer() throws Exception { () -> false, localBlacklist, new NoOpMetricsSystem(), - Optional.of(localWhitelistController)); + Optional.of(localWhitelistController), + Optional.empty()); final P2PNetwork remoteNetwork = new NettyP2PNetwork( vertx, @@ -482,6 +494,7 @@ public void rejectIncomingConnectionFromNonWhitelistedPeer() throws Exception { () -> false, remoteBlacklist, new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) { final int localListenPort = localNetwork.getLocalPeerInfo().getPort(); final Peer localPeer = @@ -788,7 +801,7 @@ private NettyP2PNetwork mockNettyP2PNetwork() { new PeerBlacklist(), new NoOpMetricsSystem(), Optional.empty(), - nodePermissioningController, + Optional.of(nodePermissioningController), blockchain); } 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 784e4d5eb6..9635009d77 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 @@ -58,6 +58,7 @@ public void createPeerDiscoveryAgent() { () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) { service.start(); final int udpPort = service.getAdvertisedPeer().get().getEndpoint().getUdpPort(); @@ -88,6 +89,7 @@ public void createPeerDiscoveryAgent_NullHost() throws IOException { () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) { Assertions.fail("Expected Exception"); } @@ -108,6 +110,7 @@ public void createPeerDiscoveryAgent_InvalidHost() throws IOException { () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) { Assertions.fail("Expected Exception"); } @@ -128,6 +131,7 @@ public void createPeerDiscoveryAgent_InvalidPort() throws IOException { () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) { Assertions.fail("Expected Exception"); } @@ -144,6 +148,7 @@ public void createPeerDiscoveryAgent_NullKeyPair() throws IOException { () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) { Assertions.fail("Expected Exception"); } @@ -161,6 +166,7 @@ public void startStopPeerDiscoveryAgent() { () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) { service.start(); service.stop(); @@ -180,6 +186,7 @@ public void startDiscoveryAgentBackToBack() { () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty()); final NettyP2PNetwork service2 = new NettyP2PNetwork( @@ -190,6 +197,7 @@ public void startDiscoveryAgentBackToBack() { () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) { service1.start(); service1.stop(); @@ -210,6 +218,7 @@ public void startDiscoveryPortInUse() { () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) { service1.start(); final NetworkingConfiguration config = configWithRandomPorts(); @@ -225,6 +234,7 @@ public void startDiscoveryPortInUse() { () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) { try { service2.start(); @@ -255,6 +265,7 @@ public void createPeerDiscoveryAgent_NoActivePeers() { () -> true, new PeerBlacklist(), new NoOpMetricsSystem(), + Optional.empty(), Optional.empty())) { assertTrue(agent.getDiscoveryPeers().isEmpty()); assertEquals(0, agent.getPeers().size()); diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NodePermissioningControllerTestHelper.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NodePermissioningControllerTestHelper.java new file mode 100644 index 0000000000..92221c98af --- /dev/null +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NodePermissioningControllerTestHelper.java @@ -0,0 +1,93 @@ +/* + * 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.p2p; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; +import tech.pegasys.pantheon.ethereum.permissioning.node.NodePermissioningController; +import tech.pegasys.pantheon.util.enode.EnodeURL; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Optional; +import java.util.stream.Collectors; + +public class NodePermissioningControllerTestHelper { + + private final EnodeURL localNode; + private final Collection permittedNodes = new ArrayList<>(); + private final Collection notPermittedNodes = new ArrayList<>(); + private boolean allowAll = false; + private boolean denyAll = false; + + public NodePermissioningControllerTestHelper(final Peer localPeer) { + this.localNode = new EnodeURL(localPeer.getEnodeURI()); + } + + public NodePermissioningControllerTestHelper withPermittedPeers(final Peer... peers) { + this.permittedNodes.addAll( + Arrays.stream(peers).map(p -> new EnodeURL(p.getEnodeURI())).collect(Collectors.toList())); + return this; + } + + public NodePermissioningControllerTestHelper withForbiddenPeers(final Peer... peers) { + this.notPermittedNodes.addAll( + Arrays.stream(peers).map(p -> new EnodeURL(p.getEnodeURI())).collect(Collectors.toList())); + return this; + } + + public NodePermissioningControllerTestHelper allowAll() { + this.allowAll = true; + return this; + } + + public NodePermissioningControllerTestHelper denyAll() { + this.denyAll = true; + return this; + } + + public NodePermissioningController build() { + final NodePermissioningController nodePermissioningController = + spy(new NodePermissioningController(Optional.empty(), new ArrayList<>())); + + if (allowAll && denyAll) { + throw new IllegalArgumentException( + "Can't allow all nodes and deny all nodes in the same NodePermissioningController"); + } else if (allowAll) { + when(nodePermissioningController.isPermitted(any(), any())).thenReturn(true); + } else if (denyAll) { + when(nodePermissioningController.isPermitted(any(), any())).thenReturn(false); + } else { + permittedNodes.forEach( + node -> { + when(nodePermissioningController.isPermitted(eq(localNode), eq(node))).thenReturn(true); + when(nodePermissioningController.isPermitted(eq(node), eq(localNode))).thenReturn(true); + }); + + notPermittedNodes.forEach( + node -> { + when(nodePermissioningController.isPermitted(eq(localNode), eq(node))) + .thenReturn(false); + when(nodePermissioningController.isPermitted(eq(node), eq(localNode))) + .thenReturn(false); + }); + } + + return nodePermissioningController; + } +} diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryTestHelper.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryTestHelper.java index ca76f971c2..38892497a3 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryTestHelper.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryTestHelper.java @@ -24,6 +24,7 @@ import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; import tech.pegasys.pantheon.ethereum.permissioning.NodeLocalConfigPermissioningController; +import tech.pegasys.pantheon.ethereum.permissioning.node.NodePermissioningController; import tech.pegasys.pantheon.util.bytes.BytesValue; import java.net.URI; @@ -174,6 +175,7 @@ public static class AgentBuilder { private PeerBlacklist blacklist = new PeerBlacklist(); private Optional whitelist = Optional.empty(); + private Optional nodePermissioningController = Optional.empty(); private List bootstrapPeers = Collections.emptyList(); private boolean active = true; @@ -203,6 +205,11 @@ public AgentBuilder whiteList( return this; } + public AgentBuilder nodePermissioningController(final NodePermissioningController controller) { + this.nodePermissioningController = Optional.ofNullable(controller); + return this; + } + public AgentBuilder blacklist(final PeerBlacklist blacklist) { this.blacklist = blacklist; return this; @@ -220,7 +227,13 @@ public MockPeerDiscoveryAgent build() { config.setActive(active); return new MockPeerDiscoveryAgent( - SECP256K1.KeyPair.generate(), config, () -> true, blacklist, whitelist, agents); + SECP256K1.KeyPair.generate(), + config, + () -> true, + blacklist, + whitelist, + nodePermissioningController, + agents); } } } 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 889670067a..b813eec1ec 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 @@ -64,6 +64,7 @@ public void lastSeenAndFirstDiscoveredTimestampsUpdatedOnMessage() { () -> true, new PeerBlacklist(), Optional.empty(), + Optional.empty(), new Subscribers<>(), new Subscribers<>()); controller.start(); diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/MockPeerDiscoveryAgent.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/MockPeerDiscoveryAgent.java index a394be814d..f991167628 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/MockPeerDiscoveryAgent.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/MockPeerDiscoveryAgent.java @@ -19,6 +19,7 @@ import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.PeerDiscoveryController.AsyncExecutor; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; import tech.pegasys.pantheon.ethereum.permissioning.NodeLocalConfigPermissioningController; +import tech.pegasys.pantheon.ethereum.permissioning.node.NodePermissioningController; import tech.pegasys.pantheon.util.bytes.BytesValue; import java.net.InetSocketAddress; @@ -41,8 +42,15 @@ public MockPeerDiscoveryAgent( final PeerRequirement peerRequirement, final PeerBlacklist peerBlacklist, final Optional nodeWhitelistController, + final Optional nodePermissioningController, final Map agentNetwork) { - super(keyPair, config, peerRequirement, peerBlacklist, nodeWhitelistController); + super( + keyPair, + config, + peerRequirement, + peerBlacklist, + nodeWhitelistController, + nodePermissioningController); this.agentNetwork = agentNetwork; } 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 975bfe3f4f..26b807d893 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 @@ -31,6 +31,7 @@ import tech.pegasys.pantheon.crypto.SECP256K1; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; +import tech.pegasys.pantheon.ethereum.p2p.NodePermissioningControllerTestHelper; import tech.pegasys.pantheon.ethereum.p2p.discovery.DiscoveryPeer; import tech.pegasys.pantheon.ethereum.p2p.discovery.PeerDiscoveryEvent.PeerBondedEvent; import tech.pegasys.pantheon.ethereum.p2p.discovery.PeerDiscoveryEvent.PeerDroppedEvent; @@ -42,6 +43,7 @@ import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.NodeLocalConfigPermissioningController; +import tech.pegasys.pantheon.ethereum.permissioning.node.NodePermissioningController; import tech.pegasys.pantheon.util.Subscribers; import tech.pegasys.pantheon.util.bytes.Bytes32; import tech.pegasys.pantheon.util.bytes.BytesValue; @@ -986,87 +988,81 @@ public void shouldNotAddPeerInNeighborsPacketWithoutBonding() { } @Test - public void shouldNotBondWithNonWhitelistedPeer() throws IOException { + public void shouldNotBondWithNonPermittedPeer() { final List peers = createPeersInLastBucket(localPeer, 3); - final DiscoveryPeer discoPeer = peers.get(0); - final DiscoveryPeer otherPeer = peers.get(1); - final DiscoveryPeer otherPeer2 = peers.get(2); + final DiscoveryPeer discoveryPeer = peers.get(0); + final DiscoveryPeer notPermittedPeer = peers.get(1); + final DiscoveryPeer permittedPeer = peers.get(2); final PeerBlacklist blacklist = new PeerBlacklist(); - final LocalPermissioningConfiguration config = permissioningConfigurationWithTempFile(); - final NodeLocalConfigPermissioningController nodeLocalConfigPermissioningController = - new NodeLocalConfigPermissioningController(config, Collections.emptyList(), selfEnode); - // Whitelist peers - nodeLocalConfigPermissioningController.addNodes(Arrays.asList(discoPeer.getEnodeURI())); - nodeLocalConfigPermissioningController.addNodes(Arrays.asList(otherPeer2.getEnodeURI())); + final NodePermissioningController nodePermissioningController = + new NodePermissioningControllerTestHelper(localPeer) + .withPermittedPeers(discoveryPeer, permittedPeer) + .withForbiddenPeers(notPermittedPeer) + .build(); final OutboundMessageHandler outboundMessageHandler = mock(OutboundMessageHandler.class); controller = getControllerBuilder() - .peers(discoPeer) + .peers(discoveryPeer) .blacklist(blacklist) - .whitelist(nodeLocalConfigPermissioningController) + .nodePermissioningController(nodePermissioningController) .outboundMessageHandler(outboundMessageHandler) .build(); final Endpoint localEndpoint = localPeer.getEndpoint(); - // Setup ping to be sent to discoPeer + // Setup ping to be sent to discoveryPeer List keyPairs = PeerDiscoveryTestHelper.generateKeyPairs(1); - PingPacketData pingPacketData = PingPacketData.create(localEndpoint, discoPeer.getEndpoint()); + PingPacketData pingPacketData = + PingPacketData.create(localEndpoint, discoveryPeer.getEndpoint()); final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, keyPairs.get(0)); - mockPacketCreation(PacketType.PING, discoPeer, discoPeerPing); + mockPacketCreation(PacketType.PING, discoveryPeer, discoPeerPing); controller.start(); verify(outboundMessageHandler, times(1)).send(any(), matchPacketOfType(PacketType.PING)); final Packet pongFromDiscoPeer = - MockPacketDataFactory.mockPongPacket(discoPeer, discoPeerPing.getHash()); - controller.onMessage(pongFromDiscoPeer, discoPeer); + MockPacketDataFactory.mockPongPacket(discoveryPeer, discoPeerPing.getHash()); + controller.onMessage(pongFromDiscoPeer, discoveryPeer); verify(outboundMessageHandler, times(1)) - .send(eq(discoPeer), matchPacketOfType(PacketType.FIND_NEIGHBORS)); + .send(eq(discoveryPeer), matchPacketOfType(PacketType.FIND_NEIGHBORS)); // Setup ping to be sent to otherPeer after neighbors packet is received keyPairs = PeerDiscoveryTestHelper.generateKeyPairs(1); - pingPacketData = PingPacketData.create(localEndpoint, otherPeer.getEndpoint()); + pingPacketData = PingPacketData.create(localEndpoint, notPermittedPeer.getEndpoint()); final Packet pingPacket = Packet.create(PacketType.PING, pingPacketData, keyPairs.get(0)); - mockPacketCreation(PacketType.PING, otherPeer, pingPacket); + mockPacketCreation(PacketType.PING, notPermittedPeer, pingPacket); // Setup ping to be sent to otherPeer2 after neighbors packet is received keyPairs = PeerDiscoveryTestHelper.generateKeyPairs(1); - pingPacketData = PingPacketData.create(localEndpoint, otherPeer2.getEndpoint()); + pingPacketData = PingPacketData.create(localEndpoint, permittedPeer.getEndpoint()); final Packet pingPacket2 = Packet.create(PacketType.PING, pingPacketData, keyPairs.get(0)); - mockPacketCreation(PacketType.PING, otherPeer2, pingPacket2); + mockPacketCreation(PacketType.PING, permittedPeer, pingPacket2); final Packet neighborsPacket = - MockPacketDataFactory.mockNeighborsPacket(discoPeer, otherPeer, otherPeer2); - controller.onMessage(neighborsPacket, discoPeer); + MockPacketDataFactory.mockNeighborsPacket(discoveryPeer, notPermittedPeer, permittedPeer); + controller.onMessage(neighborsPacket, discoveryPeer); - verify(controller, times(0)).bond(otherPeer); - verify(controller, times(1)).bond(otherPeer2); + verify(controller, times(0)).bond(notPermittedPeer); + verify(controller, times(1)).bond(permittedPeer); } @Test - public void shouldNotRespondToPingFromNonWhitelistedDiscoveryPeer() throws IOException { + public void shouldNotRespondToPingFromNonWhitelistedDiscoveryPeer() { final List peers = createPeersInLastBucket(localPeer, 3); final DiscoveryPeer discoPeer = peers.get(0); - final PeerBlacklist blacklist = new PeerBlacklist(); - - // don't add disco peer to whitelist - LocalPermissioningConfiguration config = permissioningConfigurationWithTempFile(); - config.setNodeWhitelist(new ArrayList<>()); - NodeLocalConfigPermissioningController nodeLocalConfigPermissioningController = - new NodeLocalConfigPermissioningController(config, Collections.emptyList(), selfEnode); + final NodePermissioningController nodePermissioningController = + new NodePermissioningControllerTestHelper(localPeer).withForbiddenPeers(discoPeer).build(); controller = getControllerBuilder() .peers(discoPeer) - .blacklist(blacklist) - .whitelist(nodeLocalConfigPermissioningController) + .nodePermissioningController(nodePermissioningController) .build(); final Packet pingPacket = mockPingPacket(peers.get(0), localPeer); @@ -1229,6 +1225,7 @@ static class ControllerBuilder { private Collection discoPeers = Collections.emptyList(); private PeerBlacklist blacklist = new PeerBlacklist(); private Optional whitelist = Optional.empty(); + private Optional nodePermissioningController = Optional.empty(); private MockTimerUtil timerUtil = new MockTimerUtil(); private KeyPair keypair; private DiscoveryPeer localPeer; @@ -1262,6 +1259,11 @@ ControllerBuilder whitelist(final NodeLocalConfigPermissioningController whiteli return this; } + ControllerBuilder nodePermissioningController(final NodePermissioningController controller) { + this.nodePermissioningController = Optional.of(controller); + return this; + } + ControllerBuilder timerUtil(final MockTimerUtil timerUtil) { this.timerUtil = timerUtil; return this; @@ -1319,6 +1321,7 @@ PeerDiscoveryController build() { PEER_REQUIREMENT, blacklist, whitelist, + nodePermissioningController, peerBondedObservers, peerDroppedObservers)); } 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 06ab99c64e..8fbf72a646 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 @@ -65,6 +65,7 @@ public void tableRefreshSingleNode() { () -> true, new PeerBlacklist(), Optional.empty(), + Optional.empty(), new Subscribers<>(), new Subscribers<>())); controller.start(); diff --git a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/RecursivePeerRefreshStateTest.java b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/RecursivePeerRefreshStateTest.java index 12be9fcbe0..29d88a9025 100644 --- a/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/RecursivePeerRefreshStateTest.java +++ b/ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/RecursivePeerRefreshStateTest.java @@ -16,11 +16,13 @@ import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; import tech.pegasys.pantheon.ethereum.p2p.discovery.DiscoveryPeer; import tech.pegasys.pantheon.ethereum.p2p.discovery.PeerDiscoveryStatus; @@ -28,13 +30,12 @@ import tech.pegasys.pantheon.ethereum.p2p.discovery.internal.RecursivePeerRefreshState.FindNeighbourDispatcher; import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; -import tech.pegasys.pantheon.ethereum.permissioning.NodeLocalConfigPermissioningController; +import tech.pegasys.pantheon.ethereum.permissioning.node.NodePermissioningController; import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.enode.EnodeURL; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -51,6 +52,7 @@ public class RecursivePeerRefreshStateTest { "enode://5f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.10:1111"; private final EnodeURL selfEnode = new EnodeURL(selfEnodeString); + private final DiscoveryPeer localPeer = new DiscoveryPeer(createId(9), "127.0.0.9", 9, 9); private final DiscoveryPeer peer1 = new DiscoveryPeer(createId(1), "127.0.0.1", 1, 1); private final DiscoveryPeer peer2 = new DiscoveryPeer(createId(2), "127.0.0.2", 2, 2); private final DiscoveryPeer peer3 = new DiscoveryPeer(createId(3), "127.0.0.3", 3, 3); @@ -63,7 +65,7 @@ public class RecursivePeerRefreshStateTest { bondingAgent, neighborFinder, timerUtil, - createId(999), + localPeer, new PeerTable(createId(999), 16), 5, 100); @@ -180,7 +182,7 @@ public void shouldStopWhenMaximumNumberOfRoundsReached() { bondingAgent, neighborFinder, timerUtil, - createId(999), + localPeer, new PeerTable(createId(999), 16), 5, 1); @@ -462,7 +464,7 @@ public void shouldNotBondWithNodesOnBlacklist() { bondingAgent, neighborFinder, timerUtil, - createId(999), + localPeer, new PeerTable(createId(999), 16), 5, 100); @@ -481,7 +483,8 @@ public void shouldNotBondWithNodesOnBlacklist() { } @Test - public void shouldNotBondWithNodesRejectedByWhitelist() throws Exception { + public void shouldNotBondWithNodesNotPermitted() throws Exception { + final DiscoveryPeer localPeer = new DiscoveryPeer(createId(999), "127.0.0.9", 9, 9); final DiscoveryPeer peerA = new DiscoveryPeer(createId(1), "127.0.0.1", 1, 1); final DiscoveryPeer peerB = new DiscoveryPeer(createId(2), "127.0.0.2", 2, 2); @@ -491,19 +494,21 @@ public void shouldNotBondWithNodesRejectedByWhitelist() throws Exception { LocalPermissioningConfiguration.createDefault(); permissioningConfiguration.setConfigurationFilePath(tempFile.toAbsolutePath().toString()); - final NodeLocalConfigPermissioningController peerWhitelist = - new NodeLocalConfigPermissioningController( - permissioningConfiguration, Collections.emptyList(), selfEnode); - peerWhitelist.addNodes(Arrays.asList(peerA.getEnodeURI())); + final NodePermissioningController nodeWhitelistController = + mock(NodePermissioningController.class); + when(nodeWhitelistController.isPermitted(any(), eq(new EnodeURL(peerA.getEnodeURI())))) + .thenReturn(true); + when(nodeWhitelistController.isPermitted(any(), eq(new EnodeURL(peerB.getEnodeURI())))) + .thenReturn(false); recursivePeerRefreshState = new RecursivePeerRefreshState( peerBlacklist, - Optional.of(peerWhitelist), + Optional.of(nodeWhitelistController), bondingAgent, neighborFinder, timerUtil, - createId(999), + localPeer, new PeerTable(createId(999), 16), 5, 100); diff --git a/ethereum/permissioning/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/PermissioningConfiguration.java b/ethereum/permissioning/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/PermissioningConfiguration.java index 89abe7dc8c..34efcfde9c 100644 --- a/ethereum/permissioning/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/PermissioningConfiguration.java +++ b/ethereum/permissioning/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/PermissioningConfiguration.java @@ -32,4 +32,8 @@ public Optional getLocalConfig() { public Optional getSmartContractConfig() { return smartContractConfig; } + + public static PermissioningConfiguration createDefault() { + return new PermissioningConfiguration(Optional.empty(), Optional.empty()); + } } diff --git a/ethereum/permissioning/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/node/NodePermissioningController.java b/ethereum/permissioning/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/node/NodePermissioningController.java index ce1bf9b87e..97fdf5790a 100644 --- a/ethereum/permissioning/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/node/NodePermissioningController.java +++ b/ethereum/permissioning/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/node/NodePermissioningController.java @@ -27,7 +27,7 @@ public class NodePermissioningController { private static final Logger LOG = LogManager.getLogger(); private final Optional syncStatusNodePermissioningProvider; - private List providers; + private final List providers; public NodePermissioningController( final Optional syncStatusNodePermissioningProvider, @@ -53,8 +53,11 @@ public boolean isPermitted(final EnodeURL sourceEnode, final EnodeURL destinatio } public void startPeerDiscoveryCallback(final Runnable peerDiscoveryCallback) { - syncStatusNodePermissioningProvider.ifPresent( - (p) -> p.setHasReachedSyncCallback(peerDiscoveryCallback)); + if (syncStatusNodePermissioningProvider.isPresent()) { + syncStatusNodePermissioningProvider.get().setHasReachedSyncCallback(peerDiscoveryCallback); + } else { + peerDiscoveryCallback.run(); + } } @VisibleForTesting diff --git a/ethereum/permissioning/src/test/java/tech/pegasys/pantheon/ethereum/permissioning/node/NodePermissioningControllerTest.java b/ethereum/permissioning/src/test/java/tech/pegasys/pantheon/ethereum/permissioning/node/NodePermissioningControllerTest.java index 09b2546ab2..ac147ef012 100644 --- a/ethereum/permissioning/src/test/java/tech/pegasys/pantheon/ethereum/permissioning/node/NodePermissioningControllerTest.java +++ b/ethereum/permissioning/src/test/java/tech/pegasys/pantheon/ethereum/permissioning/node/NodePermissioningControllerTest.java @@ -15,6 +15,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -23,6 +24,7 @@ import tech.pegasys.pantheon.util.enode.EnodeURL; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -72,6 +74,16 @@ public void peerDiscoveryCallbackShouldBeDelegatedToSyncStatusNodePermissioningP verify(syncStatusNodePermissioningProvider).setHasReachedSyncCallback(any(Runnable.class)); } + @Test + public void peerDiscoveryCallbackShouldRunWhenSyncStatusProviderDoesNotExist() { + final Runnable callback = mock(Runnable.class); + + controller = new NodePermissioningController(Optional.empty(), Collections.emptyList()); + controller.startPeerDiscoveryCallback(callback); + + verify(callback).run(); + } + @Test public void whenNoSyncStatusProviderWeShouldDelegateToLocalConfigNodePermissioningProvider() { List providers = new ArrayList<>(); diff --git a/gradle/versions.gradle b/gradle/versions.gradle index 8e6da7bff0..3ca1ba3428 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -25,13 +25,13 @@ dependencyManagement { dependency 'com.google.errorprone:error_prone_annotation:2.3.2' dependency 'com.google.errorprone:error_prone_test_helpers:2.3.2' - dependency 'com.google.guava:guava:27.0.1-jre' + dependency 'com.google.guava:guava:27.1-jre' - dependency 'com.squareup.okhttp3:okhttp:3.12.1' + dependency 'com.squareup.okhttp3:okhttp:3.13.1' dependency 'commons-cli:commons-cli:1.4' - dependency 'info.picocli:picocli:3.9.2' + dependency 'info.picocli:picocli:3.9.5' dependency 'io.pkts:pkts-core:3.0.4' @@ -40,8 +40,9 @@ dependencyManagement { dependency "io.prometheus:simpleclient_hotspot:0.6.0" dependency "io.prometheus:simpleclient_pushgateway:0.6.0" - dependency 'io.reactivex.rxjava2:rxjava:2.2.6' + dependency 'io.reactivex.rxjava2:rxjava:2.2.7' + dependency 'io.vertx:vertx-auth-jwt:3.6.3' dependency 'io.vertx:vertx-codegen:3.6.3' dependency 'io.vertx:vertx-core:3.6.3' dependency 'io.vertx:vertx-unit:3.6.3' @@ -55,26 +56,26 @@ dependencyManagement { dependency 'org.apache.commons:commons-text:1.6' - dependency 'org.apache.logging.log4j:log4j-api:2.11.1' - dependency 'org.apache.logging.log4j:log4j-core:2.11.1' - dependency 'org.apache.logging.log4j:log4j-slf4j-impl:2.11.1' + dependency 'org.apache.logging.log4j:log4j-api:2.11.2' + dependency 'org.apache.logging.log4j:log4j-core:2.11.2' + dependency 'org.apache.logging.log4j:log4j-slf4j-impl:2.11.2' - dependency 'org.assertj:assertj-core:3.11.1' + dependency 'org.assertj:assertj-core:3.12.1' dependency 'org.awaitility:awaitility:3.1.6' - dependency 'org.bouncycastle:bcprov-jdk15on:1.60' + dependency 'org.bouncycastle:bcprov-jdk15on:1.61' - dependency 'org.java-websocket:Java-WebSocket:1.3.9' + dependency 'org.java-websocket:Java-WebSocket:1.4.0' - dependency 'org.mockito:mockito-core:2.23.4' + dependency 'org.mockito:mockito-core:2.25.0' dependency 'org.openjdk.jmh:jmh-core:1.21' dependency 'org.openjdk.jmh:jmh-generator-annprocess:1.21' dependency 'org.rocksdb:rocksdbjni:5.17.2' - dependency 'org.springframework.security:spring-security-crypto:5.1.3.RELEASE' + dependency 'org.springframework.security:spring-security-crypto:5.1.4.RELEASE' dependency 'org.web3j:abi:4.1.1' dependency 'org.web3j:core:4.1.1' diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index caf54fa280..7919ae6e6b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java index b48943a795..4d6b9218f2 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java @@ -55,6 +55,10 @@ import tech.pegasys.pantheon.ethereum.permissioning.AccountWhitelistController; import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.NodeLocalConfigPermissioningController; +import tech.pegasys.pantheon.ethereum.permissioning.NodePermissioningControllerFactory; +import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; +import tech.pegasys.pantheon.ethereum.permissioning.node.NodePermissioningController; +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; @@ -90,7 +94,7 @@ public class RunnerBuilder { private Collection bannedNodeIds; private MetricsConfiguration metricsConfiguration; private MetricsSystem metricsSystem; - private Optional permissioningConfiguration = Optional.empty(); + private Optional permissioningConfiguration = Optional.empty(); private EnodeURL getSelfEnode() { String nodeId = pantheonController.getLocalNodeKeyPair().getPublicKey().toString(); @@ -148,7 +152,7 @@ public RunnerBuilder webSocketConfiguration(final WebSocketConfiguration webSock } public RunnerBuilder permissioningConfiguration( - final LocalPermissioningConfiguration permissioningConfiguration) { + final PermissioningConfiguration permissioningConfiguration) { this.permissioningConfiguration = Optional.of(permissioningConfiguration); return this; } @@ -224,16 +228,25 @@ public Runner build() { discoveryConfiguration.getBootstrapPeers().stream() .map(p -> new EnodeURL(p.getEnodeURI())) .collect(Collectors.toList()); + + final Optional localPermissioningConfiguration = + permissioningConfiguration.flatMap(PermissioningConfiguration::getLocalConfig); + final Optional nodeWhitelistController = - permissioningConfiguration - .filter(LocalPermissioningConfiguration::isNodeWhitelistEnabled) - .map( - config -> - new NodeLocalConfigPermissioningController( - config, bootnodesAsEnodeURLs, getSelfEnode())); + localPermissioningConfiguration.map( + config -> + new NodeLocalConfigPermissioningController( + config, bootnodesAsEnodeURLs, getSelfEnode())); final Synchronizer synchronizer = pantheonController.getSynchronizer(); + final TransactionSimulator transactionSimulator = + new TransactionSimulator( + context.getBlockchain(), context.getWorldStateArchive(), protocolSchedule); + + final Optional nodePermissioningController = + buildNodePermissioningController(bootnodesAsEnodeURLs, synchronizer, transactionSimulator); + final NetworkRunner networkRunner = NetworkRunner.builder() .protocolManagers(protocolManagers) @@ -249,7 +262,12 @@ public Runner build() { synchronizer::hasSufficientPeers, peerBlacklist, metricsSystem, - nodeWhitelistController) + nodeWhitelistController, + nodePermissioningController, + // TODO this dependency on the Blockchain will be removed in PAN-2442 + nodePermissioningController.isPresent() + ? context.getBlockchain() + : null) : caps -> new NoopP2PNetwork()) .metricsSystem(metricsSystem) .build(); @@ -257,7 +275,7 @@ public Runner build() { final TransactionPool transactionPool = pantheonController.getTransactionPool(); final MiningCoordinator miningCoordinator = pantheonController.getMiningCoordinator(); final Optional accountWhitelistController = - permissioningConfiguration + localPermissioningConfiguration .filter(LocalPermissioningConfiguration::isAccountWhitelistEnabled) .map( configuration -> { @@ -343,6 +361,21 @@ public Runner build() { dataDir); } + private Optional buildNodePermissioningController( + final List bootnodesAsEnodeURLs, + final Synchronizer synchronizer, + final TransactionSimulator transactionSimulator) { + return permissioningConfiguration.map( + config -> + new NodePermissioningControllerFactory() + .create( + config, + synchronizer, + bootnodesAsEnodeURLs, + getSelfEnode(), + transactionSimulator)); + } + private FilterManager createFilterManager( final Vertx vertx, final ProtocolContext context, final TransactionPool transactionPool) { final FilterManager filterManager = 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 1ea10af439..e9b5810076 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -49,6 +49,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApis; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; +import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfigurationBuilder; import tech.pegasys.pantheon.metrics.MetricCategory; import tech.pegasys.pantheon.metrics.MetricsSystem; @@ -613,10 +614,12 @@ public void run() { try { final JsonRpcConfiguration jsonRpcConfiguration = jsonRpcConfiguration(); final WebSocketConfiguration webSocketConfiguration = webSocketConfiguration(); - final Optional permissioningConfiguration = + final Optional permissioningConfiguration = permissioningConfiguration(); - permissioningConfiguration.ifPresent( - p -> ensureAllBootnodesAreInWhitelist(ethNetworkConfig, p)); + + permissioningConfiguration + .flatMap(PermissioningConfiguration::getLocalConfig) + .ifPresent(p -> ensureAllBootnodesAreInWhitelist(ethNetworkConfig, p)); synchronize( buildController(), @@ -787,8 +790,7 @@ MetricsConfiguration metricsConfiguration() { return metricsConfiguration; } - private Optional permissioningConfiguration() throws Exception { - + private Optional permissioningConfiguration() throws Exception { if (!permissionsAccountsEnabled && !permissionsNodesEnabled) { if (rpcHttpApis.contains(RpcApis.PERM) || rpcWsApis.contains(RpcApis.PERM)) { logger.warn( @@ -797,9 +799,14 @@ private Optional permissioningConfiguration() t return Optional.empty(); } - final LocalPermissioningConfiguration permissioningConfiguration = + final LocalPermissioningConfiguration localPermissioningConfiguration = PermissioningConfigurationBuilder.permissioningConfiguration( getPermissionsConfigFile(), permissionsNodesEnabled, permissionsAccountsEnabled); + + final PermissioningConfiguration permissioningConfiguration = + new PermissioningConfiguration( + Optional.of(localPermissioningConfiguration), Optional.empty()); + return Optional.of(permissioningConfiguration); } @@ -847,7 +854,7 @@ private void synchronize( final JsonRpcConfiguration jsonRpcConfiguration, final WebSocketConfiguration webSocketConfiguration, final MetricsConfiguration metricsConfiguration, - final Optional permissioningConfiguration) { + final Optional permissioningConfiguration) { checkNotNull(runnerBuilder); diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonControllerBuilder.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonControllerBuilder.java index 02c7fe1122..8919c10a53 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonControllerBuilder.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonControllerBuilder.java @@ -19,6 +19,7 @@ import tech.pegasys.pantheon.controller.PantheonController; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.ethereum.core.MiningParameters; +import tech.pegasys.pantheon.ethereum.core.PendingTransactions; import tech.pegasys.pantheon.ethereum.core.PrivacyParameters; import tech.pegasys.pantheon.ethereum.eth.sync.SynchronizerConfiguration; import tech.pegasys.pantheon.ethereum.storage.StorageProvider; @@ -40,7 +41,7 @@ public class PantheonControllerBuilder { private File nodePrivateKeyFile; private MetricsSystem metricsSystem; private PrivacyParameters privacyParameters; - private Integer maxPendingTransactions; + private Integer maxPendingTransactions = PendingTransactions.MAX_PENDING_TRANSACTIONS; public PantheonControllerBuilder synchronizerConfiguration( final SynchronizerConfiguration synchronizerConfiguration) { diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java index 874c992444..a567d3e658 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java @@ -39,7 +39,6 @@ import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec; import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; -import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; import tech.pegasys.pantheon.ethereum.storage.StorageProvider; import tech.pegasys.pantheon.ethereum.storage.keyvalue.RocksDbStorageProvider; import tech.pegasys.pantheon.metrics.MetricsSystem; @@ -136,8 +135,6 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { final JsonRpcConfiguration aheadJsonRpcConfiguration = jsonRpcConfiguration(); final WebSocketConfiguration aheadWebSocketConfiguration = wsRpcConfiguration(); final MetricsConfiguration aheadMetricsConfiguration = metricsConfiguration(); - final LocalPermissioningConfiguration aheadPermissioningConfiguration = - permissioningConfiguration(); final RunnerBuilder runnerBuilder = new RunnerBuilder() .vertx(Vertx.vertx()) @@ -157,7 +154,6 @@ private void syncFromGenesis(final SyncMode mode) throws Exception { .webSocketConfiguration(aheadWebSocketConfiguration) .metricsConfiguration(aheadMetricsConfiguration) .dataDir(dbAhead) - .permissioningConfiguration(aheadPermissioningConfiguration) .build(); try { @@ -305,10 +301,6 @@ private MetricsConfiguration metricsConfiguration() { return configuration; } - private LocalPermissioningConfiguration permissioningConfiguration() { - return LocalPermissioningConfiguration.createDefault(); - } - private static void setupState( final int count, final ProtocolSchedule protocolSchedule, 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 a4fd7f3858..3ab957c85d 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.java @@ -26,7 +26,7 @@ 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.LocalPermissioningConfiguration; +import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import tech.pegasys.pantheon.util.BlockImporter; @@ -84,7 +84,7 @@ public abstract class CommandTestAbstract { @Captor ArgumentCaptor wsRpcConfigArgumentCaptor; @Captor ArgumentCaptor metricsConfigArgumentCaptor; - @Captor ArgumentCaptor permissioningConfigurationArgumentCaptor; + @Captor ArgumentCaptor permissioningConfigurationArgumentCaptor; @Rule public final TemporaryFolder temp = new TemporaryFolder(); diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java index 8c57011dcf..1f3aa9061b 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java @@ -45,6 +45,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.RpcApi; import tech.pegasys.pantheon.ethereum.jsonrpc.websocket.WebSocketConfiguration; import tech.pegasys.pantheon.ethereum.permissioning.LocalPermissioningConfiguration; +import tech.pegasys.pantheon.ethereum.permissioning.PermissioningConfiguration; import tech.pegasys.pantheon.metrics.MetricCategory; import tech.pegasys.pantheon.metrics.prometheus.MetricsConfiguration; import tech.pegasys.pantheon.util.bytes.BytesValue; @@ -359,18 +360,19 @@ public void permissionsTomlPathMustUseOption() throws IOException { parseCommand( "--permissions-accounts-enabled", "--permissions-config-file", permToml.toString()); - final LocalPermissioningConfiguration permissioningConfiguration = + final LocalPermissioningConfiguration localPermissioningConfiguration = LocalPermissioningConfiguration.createDefault(); - permissioningConfiguration.setConfigurationFilePath(permToml.toString()); - permissioningConfiguration.setAccountWhitelist( + localPermissioningConfiguration.setConfigurationFilePath(permToml.toString()); + localPermissioningConfiguration.setAccountWhitelist( Collections.singletonList("0x0000000000000000000000000000000000000009")); verify(mockRunnerBuilder) .permissioningConfiguration(permissioningConfigurationArgumentCaptor.capture()); verify(mockRunnerBuilder).build(); - assertThat(permissioningConfigurationArgumentCaptor.getValue()) - .isEqualToComparingFieldByField(permissioningConfiguration); + PermissioningConfiguration config = permissioningConfigurationArgumentCaptor.getValue(); + assertThat(config.getLocalConfig().get()) + .isEqualToComparingFieldByField(localPermissioningConfiguration); assertThat(commandErrorOutput.toString()).isEmpty(); assertThat(commandOutput.toString()).isEmpty();