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
Sally MacFarlane 6 years ago committed by GitHub
parent 17a4e88798
commit c8e1c07d7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/transactions/TestNode.java
  2. 2
      ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/config/PermissioningConfiguration.java
  3. 7
      ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryAgent.java
  4. 15
      ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/PeerDiscoveryController.java
  5. 12
      ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/netty/NettyP2PNetwork.java
  6. 34
      ethereum/p2p/src/main/java/tech/pegasys/pantheon/ethereum/p2p/permissioning/NodeWhitelistController.java
  7. 38
      ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NettyP2PNetworkTest.java
  8. 35
      ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/NetworkingServiceLifecycleTest.java
  9. 2
      ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/config/PermissioningConfigurationTest.java
  10. 10
      ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/AbstractPeerDiscoveryTest.java
  11. 8
      ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryAgentTest.java
  12. 8
      ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryObserversTest.java
  13. 5
      ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/PeerDiscoveryTimestampsTest.java
  14. 139
      ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/PeerDiscoveryControllerTest.java
  15. 5
      ethereum/p2p/src/test/java/tech/pegasys/pantheon/ethereum/p2p/discovery/internal/PeerDiscoveryTableRefreshTest.java
  16. 7
      pantheon/src/main/java/tech/pegasys/pantheon/Runner.java
  17. 21
      pantheon/src/main/java/tech/pegasys/pantheon/RunnerBuilder.java
  18. 2
      pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java
  19. 2
      pantheon/src/test/java/tech/pegasys/pantheon/RunnerTest.java
  20. 2
      pantheon/src/test/java/tech/pegasys/pantheon/cli/CommandTestAbstract.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();

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

@ -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) {

@ -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;
@ -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<DiscoveryPeer> 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<DiscoveryPeer> 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);

@ -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<Capability> 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);

@ -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<String> nodeWhitelist;
private static boolean nodeWhitelistSet = false;
private final List<Peer> nodeWhitelist;
private boolean nodeWhitelistSet = false;
public NodeWhitelistController(final PermissioningConfiguration configuration) {
nodeWhitelist = new ArrayList<>();
if (configuration != null && configuration.getNodeWhitelist() != null) {
nodeWhitelist.addAll(configuration.getNodeWhitelist());
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)));
}
}

@ -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() {

@ -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());
}

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

@ -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) {

@ -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);

@ -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<PeerBondedEvent> 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<ArrayBlockingQueue<PeerBondedEvent>> queues =

@ -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 =

@ -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();
}

@ -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.

@ -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<JsonRpcHttpService> jsonRpc,
final Optional<WebSocketService> 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() {

@ -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(

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

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

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

Loading…
Cancel
Save